From 3e68057d168e0ca12230c40e190bdb3045ebbcbf Mon Sep 17 00:00:00 2001 From: Nazarii-4chain <150656925+Nazarii-4chain@users.noreply.github.com> Date: Tue, 1 Oct 2024 10:26:43 +0300 Subject: [PATCH] SPV-1010/WebhookView (#1033) Co-authored-by: chris-4chain <152964795+chris-4chain@users.noreply.github.com> --- package.json | 2 +- .../AccessKeysTabContent.tsx | 8 +- .../ContactsTabContent/ContactsTabContent.tsx | 20 +++-- src/components/DataTable/DataTable.tsx | 31 ++++---- .../DestinationsTabContent.tsx | 8 +- .../PaymailsTabContent/PaymailsTabContent.tsx | 10 ++- .../TransactionsTabContent.tsx | 9 ++- .../UnsubscribeWebhook/UnsubscribeWebhook.tsx | 73 +++++++++++++++++++ src/components/UnsubscribeWebhook/index.ts | 1 + src/components/ViewDialog/ViewDialog.tsx | 4 +- .../WebhooksColumns/WebhooksColumns.tsx | 51 +++++++++++++ .../WebhooksTabContent/WebhooksTabContent.tsx | 34 +++++++++ src/components/WebhooksTabContent/index.ts | 1 + .../XpubsTabContent/XpubsTabContent.tsx | 13 +++- src/components/index.ts | 3 + src/interfaces/webhook.ts | 5 ++ src/routes/admin/_admin.tsx | 19 ++++- src/routes/admin/_admin.webhooks.tsx | 50 +++++++++++++ src/routes/routeTree.autogenerated.ts | 47 +++++++----- src/utils/index.ts | 1 + src/utils/mapElements.ts | 4 +- src/utils/webhooksQueryOptions.tsx | 15 ++++ yarn.lock | 8 +- 23 files changed, 356 insertions(+), 61 deletions(-) create mode 100644 src/components/UnsubscribeWebhook/UnsubscribeWebhook.tsx create mode 100644 src/components/UnsubscribeWebhook/index.ts create mode 100644 src/components/WebhooksColumns/WebhooksColumns.tsx create mode 100644 src/components/WebhooksTabContent/WebhooksTabContent.tsx create mode 100644 src/components/WebhooksTabContent/index.ts create mode 100644 src/interfaces/webhook.ts create mode 100644 src/routes/admin/_admin.webhooks.tsx create mode 100644 src/utils/webhooksQueryOptions.tsx diff --git a/package.json b/package.json index 6003d5e5..65ff8080 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "dependencies": { "@4chain-ag/react-configuration": "^1.0.4", "@bsv/sdk": "^1.0.37", - "@bsv/spv-wallet-js-client": "^1.0.0-beta.20", + "@bsv/spv-wallet-js-client": "^1.0.0-beta.21", "@estruyf/github-actions-reporter": "^1.9.2", "@heroicons/react": "^2.1.3", "@hookform/resolvers": "^3.9.0", diff --git a/src/components/AccessKeysTabContent/AccessKeysTabContent.tsx b/src/components/AccessKeysTabContent/AccessKeysTabContent.tsx index 534d1873..4a2eacd2 100644 --- a/src/components/AccessKeysTabContent/AccessKeysTabContent.tsx +++ b/src/components/AccessKeysTabContent/AccessKeysTabContent.tsx @@ -7,6 +7,7 @@ import { DataTable, NoRecordsText, RevokeKeyDialog, + ViewDialog, } from '@/components'; import { AccessKeyExtended } from '@/interfaces'; @@ -26,7 +27,12 @@ export const AccessKeysTabContent = ({ accessKeys, hasRevokeKeyDialog }: AccessK hasRevokeKeyDialog && } + renderItem={(row) => ( + <> + + {hasRevokeKeyDialog && } + + )} /> ) : ( diff --git a/src/components/ContactsTabContent/ContactsTabContent.tsx b/src/components/ContactsTabContent/ContactsTabContent.tsx index e339bfd1..811a08fa 100644 --- a/src/components/ContactsTabContent/ContactsTabContent.tsx +++ b/src/components/ContactsTabContent/ContactsTabContent.tsx @@ -11,6 +11,7 @@ import { ContactStatus, DataTable, NoRecordsText, + ViewDialog, } from '@/components'; import { ContactExtended } from '@/interfaces/contacts.ts'; @@ -29,16 +30,19 @@ export const ContactsTabContent = ({ contacts }: ContactsTabContentProps) => { - row.getValue('status') === ContactStatus.Awaiting ? ( -
- - -
- ) : null - } + renderInlineItem={(row) => ( + <> + {row.getValue('status') === ContactStatus.Awaiting ? ( +
+ + +
+ ) : null} + + )} renderItem={(row) => ( <> + diff --git a/src/components/DataTable/DataTable.tsx b/src/components/DataTable/DataTable.tsx index a1382bfd..1642c24b 100644 --- a/src/components/DataTable/DataTable.tsx +++ b/src/components/DataTable/DataTable.tsx @@ -11,7 +11,6 @@ import { TableHead, TableHeader, TableRow, - ViewDialog, } from '@/components'; import { AccessKey, Contact, Destination, PaymailAddress, Tx, XPub } from '@bsv/spv-wallet-js-client'; import { @@ -79,20 +78,22 @@ export function DataTable({ {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} {renderInlineItem ? renderInlineItem(row) : null} - - - - - - - Actions - } /> - {renderItem ? renderItem(row) : null} - - - + + {renderItem && ( + + + + + + + Actions + {renderItem ? renderItem(row) : null} + + + + )} )) ) : ( diff --git a/src/components/DestinationsTabContent/DestinationsTabContent.tsx b/src/components/DestinationsTabContent/DestinationsTabContent.tsx index 9eb22dc9..6c50712e 100644 --- a/src/components/DestinationsTabContent/DestinationsTabContent.tsx +++ b/src/components/DestinationsTabContent/DestinationsTabContent.tsx @@ -7,6 +7,7 @@ import { DestinationEditDialog, destinationsColumns, NoRecordsText, + ViewDialog, } from '@/components'; import { DestinationExtended } from '@/interfaces/destination.ts'; @@ -26,7 +27,12 @@ export const DestinationsTabContent = ({ destinations, hasDestinationEditDialog hasDestinationEditDialog && } + renderItem={(row) => ( + <> + + {hasDestinationEditDialog && } + + )} /> ) : ( diff --git a/src/components/PaymailsTabContent/PaymailsTabContent.tsx b/src/components/PaymailsTabContent/PaymailsTabContent.tsx index 79dcf622..33ef4293 100644 --- a/src/components/PaymailsTabContent/PaymailsTabContent.tsx +++ b/src/components/PaymailsTabContent/PaymailsTabContent.tsx @@ -7,6 +7,7 @@ import { NoRecordsText, paymailColumns, PaymailDeleteDialog, + ViewDialog, } from '@/components'; import { PaymailExtended } from '@/interfaces/paymail.ts'; @@ -26,9 +27,12 @@ export const PaymailsTabContent = ({ paymails, hasPaymailDeleteDialog }: Paymail - hasPaymailDeleteDialog && row.original.status !== 'deleted' && - } + renderItem={(row) => ( + <> + + {hasPaymailDeleteDialog && row.original.status !== 'deleted' && } + + )} /> ) : ( diff --git a/src/components/TransactionsTabContent/TransactionsTabContent.tsx b/src/components/TransactionsTabContent/TransactionsTabContent.tsx index 2ac68a4a..124100f2 100644 --- a/src/components/TransactionsTabContent/TransactionsTabContent.tsx +++ b/src/components/TransactionsTabContent/TransactionsTabContent.tsx @@ -1,4 +1,4 @@ -import { Card, CardContent, CardHeader, CardTitle, DataTable, TransactionEditDialog } from '@/components'; +import { Card, CardContent, CardHeader, CardTitle, DataTable, TransactionEditDialog, ViewDialog } from '@/components'; import { columns } from '@/components/TransactionsColumns/columns.tsx'; import { Tx } from '@bsv/spv-wallet-js-client'; import React from 'react'; @@ -26,7 +26,12 @@ export const TransactionsTabContent = ({ hasTransactionEditDialog && } + renderItem={(row) => ( + <> + + {hasTransactionEditDialog && } + + )} /> ) : (
diff --git a/src/components/UnsubscribeWebhook/UnsubscribeWebhook.tsx b/src/components/UnsubscribeWebhook/UnsubscribeWebhook.tsx new file mode 100644 index 00000000..5cdd55fa --- /dev/null +++ b/src/components/UnsubscribeWebhook/UnsubscribeWebhook.tsx @@ -0,0 +1,73 @@ +import { + Button, + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components'; +import { useSpvWalletClient } from '@/contexts'; +import { errorWrapper } from '@/utils'; +import { Webhook } from '@bsv/spv-wallet-js-client'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { Row } from '@tanstack/react-table'; +import { CircleMinus } from 'lucide-react'; +import { useState } from 'react'; +import { toast } from 'sonner'; + +interface UnsubscribeWebhookProps { + row: Row; +} + +export const UnsubscribeWebhook = ({ row }: UnsubscribeWebhookProps) => { + const { spvWalletClient } = useSpvWalletClient(); + const queryClient = useQueryClient(); + const [isOpen, setIsOpen] = useState(false); + + const handleIsOpenToggle = () => { + setIsOpen((prev) => !prev); + }; + + const mutation = useMutation({ + mutationFn: async (url: string) => { + // At this point, spvWalletClient is defined; using non-null assertion. + return await spvWalletClient!.AdminDeleteWebhook(url); + }, + onSuccess: () => queryClient.invalidateQueries(), + }); + const onRemove = () => { + mutation.mutate(row.original.url, { + onSuccess: () => { + toast.success('Webhook unsubscribed'); + }, + onError: (error) => { + toast.error('Failed to unsubscribe webhook'); + errorWrapper(error); + }, + }); + }; + return ( + <> + + + + + + + Unsubscribe webhook + + Are you sure you want to unsubscribe a webhook ? +
+ + +
+
+
+ + ); +}; diff --git a/src/components/UnsubscribeWebhook/index.ts b/src/components/UnsubscribeWebhook/index.ts new file mode 100644 index 00000000..75177a44 --- /dev/null +++ b/src/components/UnsubscribeWebhook/index.ts @@ -0,0 +1 @@ +export * from './UnsubscribeWebhook.tsx'; diff --git a/src/components/ViewDialog/ViewDialog.tsx b/src/components/ViewDialog/ViewDialog.tsx index 186f31ad..745cc400 100644 --- a/src/components/ViewDialog/ViewDialog.tsx +++ b/src/components/ViewDialog/ViewDialog.tsx @@ -30,10 +30,10 @@ export const ViewDialog = ({ row }: ViewDialogProps) => {
); } - return (
- {field}: {value as React.ReactNode} + {field}:{' '} + {typeof value === 'boolean' ? value.toString() : (value as React.ReactNode)}
); }); diff --git a/src/components/WebhooksColumns/WebhooksColumns.tsx b/src/components/WebhooksColumns/WebhooksColumns.tsx new file mode 100644 index 00000000..1d66b9a1 --- /dev/null +++ b/src/components/WebhooksColumns/WebhooksColumns.tsx @@ -0,0 +1,51 @@ +import { Badge, Button } from '@/components/ui'; +import { getSortDirection } from '@/utils'; +import { Webhook } from '@bsv/spv-wallet-js-client'; +import { Link } from '@tanstack/react-router'; +import { ColumnDef } from '@tanstack/react-table'; + +import { ArrowUpDown } from 'lucide-react'; + +export interface WebhooksColumns extends Webhook { + status: string; +} + +export const webhookColumns: ColumnDef[] = [ + { + accessorKey: 'url', + header: ({ column }) => { + return ( + ({ + ...prev, + order_by_field: 'url', + sort_direction: getSortDirection(column), + })} + > + + + ); + }, + }, + { + accessorKey: 'status', + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + return row.getValue('status') === 'banned' ? ( + Banned + ) : ( + Active + ); + }, + }, +]; diff --git a/src/components/WebhooksTabContent/WebhooksTabContent.tsx b/src/components/WebhooksTabContent/WebhooksTabContent.tsx new file mode 100644 index 00000000..4142120c --- /dev/null +++ b/src/components/WebhooksTabContent/WebhooksTabContent.tsx @@ -0,0 +1,34 @@ +import { Card, CardContent, CardHeader, CardTitle, DataTable, webhookColumns } from '@/components'; +import { UnsubscribeWebhook } from '@/components/UnsubscribeWebhook/UnsubscribeWebhook.tsx'; +import { WebhookExtended } from '@/interfaces/webhook.ts'; + +export interface WebhooksTabContentProps { + webhooks: WebhookExtended[]; +} + +export const WebhooksTabContent = ({ webhooks }: WebhooksTabContentProps) => { + return ( + + + Webhooks + + + {webhooks.length > 0 ? ( + ( + <> + + + )} + /> + ) : ( +
+

You have no webhooks

+
+ )} +
+
+ ); +}; diff --git a/src/components/WebhooksTabContent/index.ts b/src/components/WebhooksTabContent/index.ts new file mode 100644 index 00000000..da95636a --- /dev/null +++ b/src/components/WebhooksTabContent/index.ts @@ -0,0 +1 @@ +export * from './WebhooksTabContent.tsx'; diff --git a/src/components/XpubsTabContent/XpubsTabContent.tsx b/src/components/XpubsTabContent/XpubsTabContent.tsx index e86e3664..bbb7d246 100644 --- a/src/components/XpubsTabContent/XpubsTabContent.tsx +++ b/src/components/XpubsTabContent/XpubsTabContent.tsx @@ -1,4 +1,13 @@ -import { AddXpubDialog, Card, CardContent, CardHeader, CardTitle, DataTable, xPubsColumns } from '@/components'; +import { + AddXpubDialog, + Card, + CardContent, + CardHeader, + CardTitle, + DataTable, + ViewDialog, + xPubsColumns, +} from '@/components'; import { XpubExtended } from '@/interfaces'; export interface XpubsTabContentProps { @@ -13,7 +22,7 @@ export const XpubsTabContent = ({ xpubs }: XpubsTabContentProps) => { {xpubs.length > 0 ? ( - + } /> ) : (

You have no xPubs

diff --git a/src/components/index.ts b/src/components/index.ts index 73e4f070..8268cd56 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -34,3 +34,6 @@ export * from './XpubsSkeleton'; export * from './XpubsTabContent'; export * from './CustomErrorComponent'; export * from './LoadingSpinner'; +export * from './WebhooksColumns/WebhooksColumns.tsx'; +export * from './WebhooksTabContent'; +export * from './UnsubscribeWebhook'; diff --git a/src/interfaces/webhook.ts b/src/interfaces/webhook.ts new file mode 100644 index 00000000..d058a671 --- /dev/null +++ b/src/interfaces/webhook.ts @@ -0,0 +1,5 @@ +import { Webhook } from '@bsv/spv-wallet-js-client'; + +export interface WebhookExtended extends Webhook { + status: string; +} diff --git a/src/routes/admin/_admin.tsx b/src/routes/admin/_admin.tsx index e75e579d..6804d757 100644 --- a/src/routes/admin/_admin.tsx +++ b/src/routes/admin/_admin.tsx @@ -1,11 +1,10 @@ -import { Outlet, createFileRoute, Link, useLocation, redirect } from '@tanstack/react-router'; +import { Logo, ModeToggle, Profile, Sheet, Tooltip, TooltipContent, TooltipTrigger } from '@/components'; +import { createFileRoute, Link, Outlet, redirect, useLocation } from '@tanstack/react-router'; -import { KeyRound, KeySquare, Route as RouteIcon, Mail, ArrowLeftRight, UsersRound } from 'lucide-react'; +import { ArrowLeftRight, KeyRound, KeySquare, Mail, Route as RouteIcon, UsersRound, Webhook } from 'lucide-react'; import { useEffect, useState } from 'react'; -import { Logo, ModeToggle, Profile, Sheet, Tooltip, TooltipContent, TooltipTrigger } from '@/components'; - export const Route = createFileRoute('/admin/_admin')({ beforeLoad: ({ context, location }) => { if (!context.auth.isAdmin) { @@ -111,6 +110,18 @@ function LayoutComponent() { Contacts + + + + + Webhooks + + + Webhooks +
diff --git a/src/routes/admin/_admin.webhooks.tsx b/src/routes/admin/_admin.webhooks.tsx new file mode 100644 index 00000000..7bbe8463 --- /dev/null +++ b/src/routes/admin/_admin.webhooks.tsx @@ -0,0 +1,50 @@ +import { + CustomErrorComponent, + Tabs, + TabsContent, + TabsList, + TabsTrigger, + Toaster, + WebhooksTabContent, +} from '@/components'; +import { useSpvWalletClient } from '@/contexts'; + +import { addStatusField, webhooksQueryOptions } from '@/utils'; +import { useSuspenseQuery } from '@tanstack/react-query'; +import { createFileRoute } from '@tanstack/react-router'; +import { useState } from 'react'; + +export const Route = createFileRoute('/admin/_admin/webhooks')({ + component: Webhooks, + errorComponent: ({ error }) => , +}); + +export function Webhooks() { + const { spvWalletClient } = useSpvWalletClient(); + const [tab, setTab] = useState('all'); + + const { data: webhooks } = useSuspenseQuery( + // At this point, spvWalletClient is defined; using non-null assertion. + webhooksQueryOptions({ + spvWalletClient: spvWalletClient!, + }), + ); + + const mappedWebhooks = addStatusField(webhooks); + + return ( + <> + +
+ + All + +
+ + + +
+ + + ); +} diff --git a/src/routes/routeTree.autogenerated.ts b/src/routes/routeTree.autogenerated.ts index e3a91b4f..14e2f2c9 100644 --- a/src/routes/routeTree.autogenerated.ts +++ b/src/routes/routeTree.autogenerated.ts @@ -8,25 +8,25 @@ // This file is auto-generated by TanStack Router -import { createFileRoute } from '@tanstack/react-router' +import { createFileRoute } from '@tanstack/react-router'; // Import Routes - -import { Route as rootRoute } from './__root' -import { Route as LoginImport } from './login' -import { Route as IndexImport } from './index' -import { Route as UserUserImport } from './user/_user' -import { Route as AdminAdminImport } from './admin/_admin' -import { Route as UserUserXpubImport } from './user/_user.xpub' -import { Route as UserUserTransactionsImport } from './user/_user.transactions' -import { Route as UserUserDestinationsImport } from './user/_user.destinations' -import { Route as UserUserAccessKeysImport } from './user/_user.access-keys' -import { Route as AdminAdminXpubImport } from './admin/_admin.xpub' -import { Route as AdminAdminTransactionsImport } from './admin/_admin.transactions' -import { Route as AdminAdminPaymailsImport } from './admin/_admin.paymails' -import { Route as AdminAdminDestinationsImport } from './admin/_admin.destinations' -import { Route as AdminAdminContactsImport } from './admin/_admin.contacts' -import { Route as AdminAdminAccessKeysImport } from './admin/_admin.access-keys' +import { Route as rootRoute } from './__root'; +import { Route as AdminAdminImport } from './admin/_admin'; +import { Route as AdminAdminAccessKeysImport } from './admin/_admin.access-keys'; +import { Route as AdminAdminContactsImport } from './admin/_admin.contacts'; +import { Route as AdminAdminDestinationsImport } from './admin/_admin.destinations'; +import { Route as AdminAdminPaymailsImport } from './admin/_admin.paymails'; +import { Route as AdminAdminTransactionsImport } from './admin/_admin.transactions'; +import { Route as AdminAdminWebhooksImport } from './admin/_admin.webhooks'; +import { Route as AdminAdminXpubImport } from './admin/_admin.xpub'; +import { Route as IndexImport } from './index'; +import { Route as LoginImport } from './login'; +import { Route as UserUserImport } from './user/_user'; +import { Route as UserUserAccessKeysImport } from './user/_user.access-keys'; +import { Route as UserUserDestinationsImport } from './user/_user.destinations'; +import { Route as UserUserTransactionsImport } from './user/_user.transactions'; +import { Route as UserUserXpubImport } from './user/_user.xpub'; // Create Virtual Routes @@ -96,6 +96,11 @@ const AdminAdminXpubRoute = AdminAdminXpubImport.update({ getParentRoute: () => AdminAdminRoute, } as any) +const AdminAdminWebhooksRoute = AdminAdminWebhooksImport.update({ + path: '/webhooks', + getParentRoute: () => AdminAdminRoute, +} as any) + const AdminAdminTransactionsRoute = AdminAdminTransactionsImport.update({ path: '/transactions', getParentRoute: () => AdminAdminRoute, @@ -209,6 +214,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AdminAdminTransactionsImport parentRoute: typeof AdminAdminImport } + '/admin/_admin/webhooks': { + id: '/admin/_admin/webhooks' + path: '/webhooks' + fullPath: '/admin/webhooks' + preLoaderRoute: typeof AdminAdminWebhooksImport + parentRoute: typeof AdminAdminImport + } '/admin/_admin/xpub': { id: '/admin/_admin/xpub' path: '/xpub' @@ -260,6 +272,7 @@ export const routeTree = rootRoute.addChildren({ AdminAdminDestinationsRoute, AdminAdminPaymailsRoute, AdminAdminTransactionsRoute, + AdminAdminWebhooksRoute, AdminAdminXpubRoute, }), }), diff --git a/src/utils/index.ts b/src/utils/index.ts index ebc2d9af..e8acebe5 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -15,3 +15,4 @@ export * from './getContactId.ts'; export * from './getContactPaymail.ts'; export * from './accessKeysAdminQueryOptions.tsx'; export * from './destinationsAdminQueryOptions.tsx'; +export * from './webhooksQueryOptions.tsx'; diff --git a/src/utils/mapElements.ts b/src/utils/mapElements.ts index a87409d1..c83aa7e7 100644 --- a/src/utils/mapElements.ts +++ b/src/utils/mapElements.ts @@ -3,7 +3,7 @@ type ExtractElementType = T extends (infer U)[] ? U : never; export const addStatusField = (data: T): Array & { status: string }> => { return data.map((el) => { const element = el as ExtractElementType; - const { revoked_at, deleted_at } = el as { revoked_at?: string; deleted_at?: string }; + const { revoked_at, deleted_at, banned } = el as { revoked_at?: string; deleted_at?: string; banned?: boolean }; let status; @@ -11,6 +11,8 @@ export const addStatusField = (data: T): Array { + const { spvWalletClient } = opts; + + return queryOptions({ + queryKey: ['webhooks', opts], + queryFn: () => spvWalletClient.AdminGetWebhooks(), + }); +}; diff --git a/yarn.lock b/yarn.lock index 2a314b38..8aea20f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -370,10 +370,10 @@ resolved "https://registry.yarnpkg.com/@bsv/sdk/-/sdk-1.1.13.tgz#3e681e1db4357dcbad92cdea6f79facf7681abbd" integrity sha512-AxDDx7QHURl2CggGmTejdRHH1RfsrwBeUHT6hosLICetiTAORIqK/4cEbf5jQBikcLd5Dj/w7kESXy7KaQKq+w== -"@bsv/spv-wallet-js-client@^1.0.0-beta.20": - version "1.0.0-beta.20" - resolved "https://registry.yarnpkg.com/@bsv/spv-wallet-js-client/-/spv-wallet-js-client-1.0.0-beta.20.tgz#6d19916563c21a65fe1d1367827437f4585c1182" - integrity sha512-7DDQyLlOnQl2dYAuMKpAxjByH+SqI0PjUZwOib3bAz52XmUPg1Y5l1xwUa+HOCPbN6oBwe7XeUr8spaWrb+eAg== +"@bsv/spv-wallet-js-client@^1.0.0-beta.21": + version "1.0.0-beta.21" + resolved "https://registry.yarnpkg.com/@bsv/spv-wallet-js-client/-/spv-wallet-js-client-1.0.0-beta.21.tgz#c3bd4770a47c923a11577b40179072e22519b5d3" + integrity sha512-VQ1q22/VQ1xETgRetkyQ/oqlXROlUGxfiC3EUQXYoIKwrzVpXumifv59e4tBxjjIiF7XBh4qX2tfJ2r77QlSDg== dependencies: "@bsv/sdk" "^1.1.13" "@types/rollup-plugin-node-builtins" "^2.1.5"