@toon-protocol/sdk 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/identity.ts","../src/errors.ts","../src/handler-context.ts","../src/handler-registry.ts","../src/pricing-validator.ts","../src/verification-pipeline.ts","../src/payment-handler-bridge.ts","../src/event-storage-handler.ts","../src/create-node.ts","../src/skill-descriptor.ts"],"sourcesContent":["/**\n * Unified identity module for @toon-protocol/sdk.\n *\n * Derives both a Nostr pubkey (x-only Schnorr, BIP-340) and an EVM address\n * (Keccak-256) from a single secp256k1 private key, following the NIP-06\n * derivation standard.\n *\n * Both Nostr and EVM use the secp256k1 elliptic curve, so a single 12-word\n * seed phrase can recover the complete identity across both layers.\n */\n\nimport {\n generateMnemonic as _generateMnemonic,\n validateMnemonic,\n mnemonicToSeedSync,\n} from '@scure/bip39';\nimport { wordlist } from '@scure/bip39/wordlists/english.js';\nimport { HDKey } from '@scure/bip32';\nimport { getPublicKey } from 'nostr-tools/pure';\nimport { secp256k1 } from '@noble/curves/secp256k1.js';\nimport { keccak_256 } from '@noble/hashes/sha3.js';\nimport { bytesToHex } from '@noble/hashes/utils.js';\nimport { IdentityError } from './errors.js';\n\n/**\n * Represents a node's complete identity: secret key, Nostr pubkey, and EVM address.\n */\nexport interface NodeIdentity {\n /** The 32-byte secp256k1 secret key. */\n secretKey: Uint8Array;\n /** The x-only Schnorr public key (32 bytes, 64 lowercase hex characters). */\n pubkey: string;\n /** The EIP-55 checksummed EVM address (0x-prefixed, 42 characters). */\n evmAddress: string;\n}\n\n/**\n * Options for mnemonic-based key derivation.\n */\nexport interface FromMnemonicOptions {\n /** Key index in the NIP-06 derivation path. Defaults to 0. */\n accountIndex?: number;\n}\n\n/**\n * Generates a valid 12-word BIP-39 mnemonic using 128-bit entropy.\n *\n * @returns A space-separated string of 12 BIP-39 English words.\n */\nexport function generateMnemonic(): string {\n return _generateMnemonic(wordlist, 128);\n}\n\n/**\n * Derives a complete NodeIdentity from a BIP-39 mnemonic phrase.\n *\n * Uses the NIP-06 derivation path: m/44'/1237'/0'/0/{accountIndex}\n *\n * @param mnemonic - A valid BIP-39 mnemonic (12 or 24 words).\n * @param options - Optional derivation options (accountIndex defaults to 0).\n * @returns The derived NodeIdentity with secretKey, pubkey, and evmAddress.\n * @throws {IdentityError} If the mnemonic is invalid.\n */\nexport function fromMnemonic(\n mnemonic: string,\n options?: FromMnemonicOptions\n): NodeIdentity {\n if (!validateMnemonic(mnemonic, wordlist)) {\n throw new IdentityError(\n `Invalid BIP-39 mnemonic: the provided words do not form a valid mnemonic phrase`\n );\n }\n\n const accountIndex = options?.accountIndex ?? 0;\n\n if (\n !Number.isInteger(accountIndex) ||\n accountIndex < 0 ||\n accountIndex > MAX_BIP32_INDEX\n ) {\n throw new IdentityError(\n `Invalid accountIndex: expected a non-negative integer (0 to ${MAX_BIP32_INDEX}), got ${String(accountIndex)}`\n );\n }\n\n const path = `m/44'/1237'/0'/0/${accountIndex}`;\n\n let seed: Uint8Array | undefined;\n try {\n seed = mnemonicToSeedSync(mnemonic);\n const hdKey = HDKey.fromMasterSeed(seed).derive(path);\n\n if (!hdKey.privateKey) {\n throw new IdentityError(`Failed to derive private key at path ${path}`);\n }\n\n const secretKey = hdKey.privateKey;\n return deriveIdentity(secretKey);\n } catch (error: unknown) {\n if (error instanceof IdentityError) {\n throw error;\n }\n throw new IdentityError(\n `Key derivation failed at path ${path}: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n } finally {\n // Best-effort zeroing of the intermediate seed to reduce the window\n // during which sensitive material remains in memory. This is not a\n // guarantee (JS has no secure-erase primitive), but it limits exposure.\n if (seed) {\n seed.fill(0);\n }\n }\n}\n\n/**\n * Derives a complete NodeIdentity from an existing 32-byte secret key.\n *\n * @param secretKey - A 32-byte secp256k1 secret key.\n * @returns The derived NodeIdentity with secretKey, pubkey, and evmAddress.\n * @throws {IdentityError} If the secret key is not exactly 32 bytes.\n */\nexport function fromSecretKey(secretKey: Uint8Array): NodeIdentity {\n if (!(secretKey instanceof Uint8Array)) {\n throw new IdentityError(\n `Invalid secret key: expected Uint8Array, got ${secretKey === null ? 'null' : typeof secretKey}`\n );\n }\n\n if (secretKey.length !== 32) {\n throw new IdentityError(\n `Invalid secret key: expected 32 bytes, got ${secretKey.length} bytes`\n );\n }\n\n try {\n return deriveIdentity(secretKey);\n } catch (error: unknown) {\n if (error instanceof IdentityError) {\n throw error;\n }\n throw new IdentityError(\n `Invalid secret key: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n}\n\n/**\n * Maximum valid BIP-32 non-hardened child index (2^31 - 1).\n * Values at or above 2^31 are reserved for hardened derivation.\n */\nconst MAX_BIP32_INDEX = 0x7fffffff;\n\n/**\n * Shared helper that computes pubkey and evmAddress from a 32-byte secret key.\n * Returns a defensive copy of the secret key to prevent external mutation.\n */\nfunction deriveIdentity(secretKey: Uint8Array): NodeIdentity {\n // Compute x-only Schnorr pubkey (32 bytes hex) via nostr-tools\n const pubkey = getPublicKey(secretKey);\n\n // Compute EVM address from the uncompressed secp256k1 public key\n const evmAddress = computeEvmAddress(secretKey);\n\n // Return a defensive copy of the secret key to prevent external mutation\n // from affecting the identity or vice versa\n return { secretKey: new Uint8Array(secretKey), pubkey, evmAddress };\n}\n\n/**\n * Computes an EIP-55 checksummed EVM address from a secp256k1 private key.\n *\n * Steps:\n * 1. Compute the full uncompressed public key (65 bytes: 0x04 + 64 bytes X,Y)\n * 2. Strip the 0x04 prefix to get 64 bytes\n * 3. Hash with Keccak-256\n * 4. Take the last 20 bytes\n * 5. Format as 0x-prefixed hex with EIP-55 checksum\n */\nfunction computeEvmAddress(secretKey: Uint8Array): string {\n // Get the uncompressed public key (65 bytes: 0x04 prefix + 64 bytes X,Y)\n const uncompressedPubkey = secp256k1.getPublicKey(secretKey, false);\n\n // Strip the 0x04 prefix (first byte), leaving 64 bytes of X,Y coordinates\n const pubkeyWithoutPrefix = uncompressedPubkey.slice(1);\n\n // Hash with Keccak-256\n const hash = keccak_256(pubkeyWithoutPrefix);\n\n // Take last 20 bytes\n const addressBytes = hash.slice(-20);\n const addressHex = bytesToHex(addressBytes);\n\n // Apply EIP-55 mixed-case checksum\n return toChecksumAddress(addressHex);\n}\n\n/**\n * Applies EIP-55 mixed-case checksum encoding to an Ethereum address.\n *\n * EIP-55 rules:\n * - Hash the lowercase hex address (without 0x) with Keccak-256\n * - For each hex character in the address:\n * - If the corresponding nibble in the hash is >= 8, uppercase it\n * - Otherwise, lowercase it\n *\n * @param addressHex - The 40-character lowercase hex address (without 0x prefix).\n * @returns The checksummed address with 0x prefix.\n */\nfunction toChecksumAddress(addressHex: string): string {\n const lower = addressHex.toLowerCase();\n const hash = bytesToHex(keccak_256(new TextEncoder().encode(lower)));\n\n let checksummed = '0x';\n for (let i = 0; i < 40; i++) {\n const char = lower[i];\n const hashChar = hash[i];\n if (char === undefined || hashChar === undefined) {\n throw new IdentityError(\n `Unexpected undefined at index ${i} during checksum computation`\n );\n }\n const hashNibble = parseInt(hashChar, 16);\n checksummed += hashNibble >= 8 ? char.toUpperCase() : char;\n }\n\n return checksummed;\n}\n","/**\n * SDK-specific error classes for @toon-protocol/sdk.\n * All errors extend ToonError from @toon-protocol/core for a consistent error hierarchy.\n */\n\nimport { ToonError } from '@toon-protocol/core';\n\n/**\n * Error thrown when identity operations fail.\n * Used for invalid mnemonics, invalid secret keys, and key derivation failures.\n */\nexport class IdentityError extends ToonError {\n constructor(message: string, cause?: Error) {\n super(message, 'IDENTITY_ERROR', cause);\n this.name = 'IdentityError';\n }\n}\n\n/**\n * Error thrown when node lifecycle operations fail.\n * Used for start/stop failures, configuration errors, etc.\n */\nexport class NodeError extends ToonError {\n constructor(message: string, cause?: Error) {\n super(message, 'NODE_ERROR', cause);\n this.name = 'NodeError';\n }\n}\n\n/**\n * Error thrown when handler dispatch operations fail.\n * Used for handler registration conflicts, missing handlers, and dispatch errors.\n */\nexport class HandlerError extends ToonError {\n constructor(message: string, cause?: Error) {\n super(message, 'HANDLER_ERROR', cause);\n this.name = 'HandlerError';\n }\n}\n\n/**\n * Error thrown when Schnorr signature verification fails.\n * Used for invalid signatures, malformed events, and verification pipeline errors.\n */\nexport class VerificationError extends ToonError {\n constructor(message: string, cause?: Error) {\n super(message, 'VERIFICATION_ERROR', cause);\n this.name = 'VerificationError';\n }\n}\n\n/**\n * Error thrown when payment validation fails.\n * Used for pricing calculation errors, insufficient payment, and pricing policy violations.\n */\nexport class PricingError extends ToonError {\n constructor(message: string, cause?: Error) {\n super(message, 'PRICING_ERROR', cause);\n this.name = 'PricingError';\n }\n}\n","/**\n * Handler context for @toon-protocol/sdk.\n *\n * Provides a context object passed to kind-based handlers with methods\n * for accessing TOON data, shallow-parsed metadata, and accept/reject actions.\n */\n\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport type { ToonRoutingMeta } from '@toon-protocol/core/toon';\nimport type {\n HandlePacketAcceptResponse,\n HandlePacketRejectResponse,\n} from '@toon-protocol/core';\n\n// Re-export core response types so SDK consumers don't need to import from core\nexport type { HandlePacketAcceptResponse, HandlePacketRejectResponse };\n\n/**\n * The handler context passed to each kind-based handler.\n */\nexport interface HandlerContext {\n /** Raw TOON string (base64-encoded). */\n readonly toon: string;\n /** Event kind from shallow parse. */\n readonly kind: number;\n /** Event pubkey from shallow parse. */\n readonly pubkey: string;\n /** Payment amount in the ILP packet. */\n readonly amount: bigint;\n /** ILP destination address. */\n readonly destination: string;\n /** Lazy-decode the TOON payload into a full NostrEvent. */\n decode(): NostrEvent;\n /** Accept the packet with optional metadata. */\n accept(metadata?: Record<string, unknown>): HandlePacketAcceptResponse;\n /** Reject the packet with an ILP error code and message. */\n reject(code: string, message: string): HandlePacketRejectResponse;\n}\n\nexport interface CreateHandlerContextOptions {\n toon: string;\n meta: ToonRoutingMeta;\n amount: bigint;\n destination: string;\n toonDecoder: (toon: string) => NostrEvent;\n}\n\n/**\n * Creates a HandlerContext from the given options.\n */\nexport function createHandlerContext(\n options: CreateHandlerContextOptions\n): HandlerContext {\n let cachedEvent: NostrEvent | undefined;\n\n return {\n get toon() {\n return options.toon;\n },\n get kind() {\n return options.meta.kind;\n },\n get pubkey() {\n return options.meta.pubkey;\n },\n get amount() {\n return options.amount;\n },\n get destination() {\n return options.destination;\n },\n decode(): NostrEvent {\n if (!cachedEvent) {\n cachedEvent = options.toonDecoder(options.toon);\n }\n return cachedEvent;\n },\n accept(metadata?: Record<string, unknown>): HandlePacketAcceptResponse {\n // Placeholder fulfillment for SDK handler context. In production, the BLS\n // computes the real fulfillment as SHA-256(eventId). SDK users building\n // custom handlers should override this with a cryptographically valid value.\n return {\n accept: true,\n fulfillment: 'default-fulfillment',\n ...(metadata ? { metadata } : {}),\n };\n },\n reject(code: string, message: string): HandlePacketRejectResponse {\n return {\n accept: false,\n code,\n message,\n };\n },\n };\n}\n","/**\n * Handler registry for @toon-protocol/sdk.\n *\n * Maps event kinds to handler functions for dispatching incoming ILP packets.\n */\n\nimport { JOB_REQUEST_KIND_BASE } from '@toon-protocol/core';\nimport type {\n HandlerContext,\n HandlePacketAcceptResponse,\n HandlePacketRejectResponse,\n} from './handler-context.js';\n\nexport type HandlerResponse =\n | HandlePacketAcceptResponse\n | HandlePacketRejectResponse;\n\nexport type Handler = (ctx: HandlerContext) => Promise<HandlerResponse>;\n\n/**\n * Registry that maps Nostr event kinds to handler functions.\n */\nexport class HandlerRegistry {\n private handlers = new Map<number, Handler>();\n private defaultHandler: Handler | undefined;\n\n /**\n * Register a handler for a specific event kind.\n * Replaces any existing handler for that kind.\n */\n on(kind: number, handler: Handler): this {\n this.handlers.set(kind, handler);\n return this;\n }\n\n /**\n * Register a default handler for unrecognized kinds.\n */\n onDefault(handler: Handler): this {\n this.defaultHandler = handler;\n return this;\n }\n\n /**\n * Returns all registered kind numbers, sorted ascending.\n */\n getRegisteredKinds(): number[] {\n return [...this.handlers.keys()].sort((a, b) => a - b);\n }\n\n /**\n * Returns registered kinds in the DVM request range (5000-5999), sorted ascending.\n * Uses JOB_REQUEST_KIND_BASE (5000) as the range start.\n */\n getDvmKinds(): number[] {\n const dvmRangeStart = JOB_REQUEST_KIND_BASE;\n const dvmRangeEnd = JOB_REQUEST_KIND_BASE + 999;\n return this.getRegisteredKinds().filter(\n (k) => k >= dvmRangeStart && k <= dvmRangeEnd\n );\n }\n\n /**\n * Dispatch a context to the appropriate handler based on kind.\n */\n async dispatch(ctx: HandlerContext): Promise<HandlerResponse> {\n const handler = this.handlers.get(ctx.kind);\n if (handler) {\n return handler(ctx);\n }\n if (this.defaultHandler) {\n return this.defaultHandler(ctx);\n }\n return {\n accept: false,\n code: 'F00',\n message: `No handler registered for kind ${ctx.kind}`,\n };\n }\n}\n","/**\n * Pricing validator for @toon-protocol/sdk.\n *\n * Validates that incoming ILP payments meet the required price based on\n * TOON payload size and configured price per byte.\n */\n\nimport type { ToonRoutingMeta } from '@toon-protocol/core/toon';\n\nexport interface PricingValidatorConfig {\n basePricePerByte?: bigint;\n ownPubkey: string;\n kindPricing?: Record<number, bigint>;\n}\n\nexport interface PricingValidationResult {\n accepted: boolean;\n rejection?: {\n accept: false;\n code: string;\n message: string;\n metadata?: Record<string, string>;\n };\n}\n\n/**\n * Creates a pricing validator that checks payment amounts against TOON size.\n */\nexport function createPricingValidator(config: PricingValidatorConfig) {\n const basePricePerByte = config.basePricePerByte ?? 10n;\n\n return {\n validate(meta: ToonRoutingMeta, amount: bigint): PricingValidationResult {\n // Self-write bypass\n if (meta.pubkey === config.ownPubkey) {\n return { accepted: true };\n }\n\n // Kind-specific pricing override (use Object.hasOwn for prototype-safe lookup)\n const kindOverride =\n config.kindPricing && Object.hasOwn(config.kindPricing, meta.kind)\n ? config.kindPricing[meta.kind]\n : undefined;\n const pricePerByte = kindOverride ?? basePricePerByte;\n\n const requiredAmount = BigInt(meta.rawBytes.length) * pricePerByte;\n\n if (amount < requiredAmount) {\n return {\n accepted: false,\n rejection: {\n accept: false,\n code: 'F04',\n message: 'Insufficient payment',\n metadata: {\n required: requiredAmount.toString(),\n received: amount.toString(),\n },\n },\n };\n }\n\n return { accepted: true };\n },\n };\n}\n","/**\n * Verification pipeline for @toon-protocol/sdk.\n *\n * Verifies Schnorr signatures on incoming TOON payloads before dispatching\n * to handlers. In dev mode, verification is skipped.\n */\n\nimport type { ToonRoutingMeta } from '@toon-protocol/core/toon';\nimport { schnorr } from '@noble/curves/secp256k1.js';\nimport { hexToBytes } from '@noble/hashes/utils.js';\n\nexport interface VerificationResult {\n verified: boolean;\n rejection?: {\n accept: false;\n code: string;\n message: string;\n };\n}\n\nexport interface VerificationPipelineConfig {\n devMode: boolean;\n}\n\n/**\n * Creates a verification pipeline that checks Schnorr signatures.\n */\nexport function createVerificationPipeline(config: VerificationPipelineConfig) {\n return {\n async verify(\n meta: ToonRoutingMeta,\n _toonData: string\n ): Promise<VerificationResult> {\n if (config.devMode) {\n return { verified: true };\n }\n\n try {\n const sigBytes = hexToBytes(meta.sig);\n const msgBytes = hexToBytes(meta.id);\n const pubkeyBytes = hexToBytes(meta.pubkey);\n const valid = schnorr.verify(sigBytes, msgBytes, pubkeyBytes);\n if (!valid) {\n return {\n verified: false,\n rejection: {\n accept: false,\n code: 'F06',\n message: 'Invalid Schnorr signature',\n },\n };\n }\n return { verified: true };\n } catch {\n return {\n verified: false,\n rejection: {\n accept: false,\n code: 'F06',\n message: 'Signature verification failed',\n },\n };\n }\n },\n };\n}\n","/**\n * Payment handler bridge for @toon-protocol/sdk.\n *\n * Bridges between ILP packet handling and the handler registry,\n * using isTransit to distinguish fire-and-forget from await semantics.\n */\n\nimport type { HandlerRegistry } from './handler-registry.js';\nimport { createHandlerContext } from './handler-context.js';\nimport type { ToonRoutingMeta } from '@toon-protocol/core/toon';\n\nexport interface PaymentHandlerBridgeConfig {\n registry: HandlerRegistry;\n devMode: boolean;\n ownPubkey: string;\n basePricePerByte: bigint;\n}\n\nexport interface PaymentRequest {\n paymentId: string;\n destination: string;\n amount: string;\n data: string;\n isTransit: boolean;\n}\n\nexport interface PaymentResponse {\n accept: boolean;\n fulfillment?: string;\n code?: string;\n message?: string;\n data?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Creates a payment handler bridge that connects ILP packets to the handler registry.\n */\nexport function createPaymentHandlerBridge(config: PaymentHandlerBridgeConfig) {\n const { registry } = config;\n\n return {\n async handlePayment(request: PaymentRequest): Promise<PaymentResponse> {\n let amount: bigint;\n try {\n amount = BigInt(request.amount);\n } catch {\n return {\n accept: false,\n code: 'T00',\n message: 'Invalid payment amount',\n };\n }\n\n const ctx = createHandlerContext({\n toon: request.data,\n meta: {\n kind: 0,\n pubkey: '',\n id: '',\n sig: '',\n rawBytes: new Uint8Array(0),\n } as ToonRoutingMeta,\n amount,\n destination: request.destination,\n toonDecoder: () => {\n throw new Error('decode not available in bridge');\n },\n });\n\n if (request.isTransit) {\n void registry.dispatch(ctx).catch((err: unknown) => {\n // Log only the error message, not the full error object (which may contain payload data)\n const errMsg = err instanceof Error ? err.message : 'Unknown error';\n console.error('Transit handler error:', errMsg);\n });\n return { accept: true };\n }\n\n try {\n return await registry.dispatch(ctx);\n } catch (err: unknown) {\n // Log only the error message, not the full error object (which may contain payload data)\n const errMsg = err instanceof Error ? err.message : 'Unknown error';\n console.error('Handler error:', errMsg);\n return {\n accept: false,\n code: 'T00',\n message: 'Internal error',\n };\n }\n },\n };\n}\n","/**\n * Event storage handler stub for @toon-protocol/sdk.\n *\n * This is a stub that throws. The real implementation lives in\n * `@toon-protocol/town` -- see `createEventStorageHandler` from that package.\n *\n * The SDK is the framework; Town is the relay implementation. SDK consumers\n * building relay functionality should use `@toon-protocol/town` directly.\n */\n\n/**\n * Creates an event storage handler.\n *\n * **Stub** -- throws \"not yet implemented\". See `@toon-protocol/town` for the\n * real relay implementation of this handler.\n *\n * @see {@link https://github.com/ALLiDoizCode/toon/tree/main/packages/town | @toon-protocol/town}\n */\nexport function createEventStorageHandler(_config: unknown): unknown {\n throw new Error(\n 'createEventStorageHandler is not yet implemented in @toon-protocol/sdk. ' +\n 'Use @toon-protocol/town for the relay implementation.'\n );\n}\n","/**\n * createNode() composition for @toon-protocol/sdk.\n *\n * Wires the full ILP packet processing pipeline:\n * shallow TOON parse -> Schnorr signature verification -> pricing validation -> handler dispatch\n *\n * Provides start() / stop() lifecycle management by delegating to\n * the core ToonNode composition (embedded mode) or manual HTTP\n * composition (standalone mode).\n *\n * ## Deployment Modes\n *\n * - **Embedded** (`connector`): Pass an EmbeddableConnectorLike directly.\n * Zero-latency packet delivery via direct function calls.\n * - **Standalone** (`connectorUrl` + `handlerPort`): Connect to an external\n * connector via HTTP. The SDK starts an HTTP server to receive packets.\n */\n\nimport { createServer, type Server as HttpServer } from 'node:http';\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport type {\n EmbeddableConnectorLike,\n HandlePacketRequest,\n HandlePacketResponse,\n ConnectorChannelClient,\n} from '@toon-protocol/core';\nimport type {\n KnownPeer,\n BootstrapResult,\n BootstrapEventListener,\n} from '@toon-protocol/core';\nimport type { SettlementConfig } from '@toon-protocol/core';\nimport {\n createToonNode,\n BootstrapService,\n createDiscoveryTracker,\n createHttpIlpClient,\n createHttpConnectorAdmin,\n createHttpChannelClient,\n resolveChainConfig,\n buildIlpPrepare,\n buildJobFeedbackEvent,\n buildJobResultEvent,\n parseJobResult,\n} from '@toon-protocol/core';\nimport type { DvmJobStatus, IlpSendResult } from '@toon-protocol/core';\nimport type {\n IlpClient,\n ConnectorAdminClient,\n DiscoveryTracker,\n} from '@toon-protocol/core';\nimport {\n shallowParseToon,\n decodeEventFromToon,\n encodeEventToToon,\n} from '@toon-protocol/core/toon';\n\nimport type { SkillDescriptor } from '@toon-protocol/core';\nimport { fromSecretKey } from './identity.js';\nimport { HandlerRegistry, type Handler } from './handler-registry.js';\nimport { createHandlerContext } from './handler-context.js';\nimport { createVerificationPipeline } from './verification-pipeline.js';\nimport { createPricingValidator } from './pricing-validator.js';\nimport {\n buildSkillDescriptor,\n type BuildSkillDescriptorConfig,\n} from './skill-descriptor.js';\nimport { NodeError } from './errors.js';\n\n/**\n * Maximum base64-encoded payload size (in characters) accepted by the pipeline.\n * Defense-in-depth against DoS via oversized payloads. 1MB of base64 decodes\n * to ~750KB of raw TOON data, which is far beyond any legitimate Nostr event.\n * The pay-per-byte pricing model provides an additional economic disincentive.\n */\nconst MAX_PAYLOAD_BASE64_LENGTH = 1_048_576;\n\n/**\n * Configuration for creating a ServiceNode via createNode().\n *\n * Supports two deployment modes:\n * - **Embedded** (`connector`): Pass an EmbeddableConnectorLike directly for\n * zero-latency packet delivery.\n * - **Standalone** (`connectorUrl` + `handlerPort`): Connect to an external\n * connector via HTTP. The SDK starts an HTTP server to receive packets.\n *\n * Provide `connector` OR (`connectorUrl` + `handlerPort`), not both.\n */\nexport interface NodeConfig {\n /** 32-byte secp256k1 secret key */\n secretKey: Uint8Array;\n\n /** Chain preset name (default: 'anvil'). See resolveChainConfig(). */\n chain?: string;\n\n // --- Connector (exactly one mode required) ---\n\n /** Embedded connector instance for zero-latency mode. */\n connector?: EmbeddableConnectorLike;\n /**\n * External connector admin URL for standalone mode (e.g., \"http://localhost:8081\").\n * Must be provided with `handlerPort`.\n */\n connectorUrl?: string;\n /**\n * Port for the HTTP server that receives ILP packets from the external connector.\n * Must be provided with `connectorUrl`.\n */\n handlerPort?: number;\n\n // --- Network ---\n\n /** ILP address (default: 'g.toon.local') */\n ilpAddress?: string;\n /** BTP endpoint URL advertised in kind:10032 announcements */\n btpEndpoint?: string;\n /** Asset code (default: 'USD') */\n assetCode?: string;\n /** Asset scale (default: 6) */\n assetScale?: number;\n /**\n * Base price per byte for pricing validation (default: 10n).\n *\n * Amounts are in USDC micro-units (6 decimals) for production.\n * Default 10n = 10 micro-USDC per byte = $0.00001/byte.\n * A 1KB event costs 10,240 micro-USDC = ~$0.01.\n */\n basePricePerByte?: bigint;\n /** Dev mode skips signature verification (default: false) */\n devMode?: boolean;\n /** TOON encoder function */\n toonEncoder?: (event: NostrEvent) => Uint8Array;\n /** TOON decoder function */\n toonDecoder?: (bytes: Uint8Array) => NostrEvent;\n /** Initial known peers for bootstrap */\n knownPeers?: KnownPeer[];\n /** Relay WebSocket URL */\n relayUrl?: string;\n /** Settlement info for peer registration */\n settlementInfo?: SettlementConfig;\n /** Enable ArDrive peer lookup */\n ardriveEnabled?: boolean;\n /** Per-kind pricing overrides */\n kindPricing?: Record<number, bigint>;\n /** Config-based handler registration (alternative to post-creation .on()) */\n handlers?: Record<number, Handler>;\n /** Config-based default handler (alternative to post-creation .onDefault()) */\n defaultHandler?: Handler;\n /**\n * Optional skill descriptor configuration overrides.\n * When DVM handlers are registered, these values override the auto-derived\n * defaults in the skill descriptor. See buildSkillDescriptor().\n */\n skillConfig?: Omit<\n BuildSkillDescriptorConfig,\n 'basePricePerByte' | 'kindPricing'\n >;\n}\n\n/**\n * Result returned by ServiceNode.start().\n */\nexport interface StartResult {\n /** Number of peers successfully bootstrapped */\n peerCount: number;\n /** Number of payment channels opened */\n channelCount: number;\n /** Detailed results from the bootstrap phase */\n bootstrapResults: BootstrapResult[];\n}\n\n/**\n * Result returned by ServiceNode.publishEvent().\n */\nexport interface PublishEventResult {\n success: boolean;\n eventId: string;\n fulfillment?: string;\n code?: string;\n message?: string;\n}\n\n/**\n * A fully wired TOON node with lifecycle management.\n */\nexport interface ServiceNode {\n /** Nostr x-only public key (32 bytes, 64 hex chars) */\n readonly pubkey: string;\n /** EVM address derived from the same secp256k1 key */\n readonly evmAddress: string;\n /** Pass-through to the underlying connector (null in standalone mode) */\n readonly connector: EmbeddableConnectorLike | null;\n /** Channel client (null if connector lacks channel support or in standalone mode) */\n readonly channelClient: ConnectorChannelClient | null;\n /** Register a handler for a specific event kind (builder pattern) */\n on(kind: number, handler: Handler): ServiceNode;\n /** Register a lifecycle event listener */\n on(event: 'bootstrap', listener: BootstrapEventListener): ServiceNode;\n /** Register a default handler for unrecognized kinds (builder pattern) */\n onDefault(handler: Handler): ServiceNode;\n /** Start the node: wire packet handler, run bootstrap, start discovery */\n start(): Promise<StartResult>;\n /** Stop the node: clean up lifecycle state */\n stop(): Promise<void>;\n /** Initiate peering with a discovered peer (register + settlement) */\n peerWith(pubkey: string): Promise<void>;\n /**\n * Publish a Nostr event to a remote peer via the embedded connector.\n *\n * TOON-encodes the event, computes payment amount, and sends as an\n * ILP PREPARE packet via the runtime client.\n *\n * @param event - The Nostr event to publish\n * @param options - Must include destination ILP address\n * @returns Result with success/failure info and event ID\n */\n publishEvent(\n event: NostrEvent,\n options?: { destination: string }\n ): Promise<PublishEventResult>;\n\n /**\n * Publish a Kind 7000 DVM job feedback event via ILP PREPARE.\n *\n * Builds a signed Kind 7000 feedback event with NIP-90 tags (e, p, status)\n * and delegates to publishEvent() for TOON encoding and ILP delivery.\n * Standard relay write fee applies: basePricePerByte * toonData.length.\n *\n * @param requestEventId - 64-char hex event ID of the original Kind 5xxx request\n * @param customerPubkey - 64-char hex pubkey of the customer who posted the request\n * @param status - Job status value ('processing', 'error', 'success', 'partial')\n * @param content - Optional status details or error message\n * @param options - Must include destination ILP address\n * @returns Result with success/failure info and event ID\n */\n publishFeedback(\n requestEventId: string,\n customerPubkey: string,\n status: DvmJobStatus,\n content?: string,\n options?: { destination: string }\n ): Promise<PublishEventResult>;\n\n /**\n * Publish a Kind 6xxx DVM job result event via ILP PREPARE.\n *\n * Builds a signed Kind 6xxx result event with NIP-90 tags (e, p, amount)\n * and delegates to publishEvent() for TOON encoding and ILP delivery.\n * Standard relay write fee applies: basePricePerByte * toonData.length.\n *\n * @param requestEventId - 64-char hex event ID of the original Kind 5xxx request\n * @param customerPubkey - 64-char hex pubkey of the customer who posted the request\n * @param amount - Compute cost in USDC micro-units as string\n * @param content - Result data (text, URL, etc.)\n * @param options - Must include destination; optional kind (default: 6100)\n * @returns Result with success/failure info and event ID\n */\n publishResult(\n requestEventId: string,\n customerPubkey: string,\n amount: string,\n content: string,\n options?: { destination: string; kind?: number }\n ): Promise<PublishEventResult>;\n\n /**\n * Returns the computed skill descriptor for this node's DVM capabilities.\n * Returns `undefined` if no DVM handlers (kinds 5000-5999) are registered.\n * The descriptor is computed from the handler registry and node config.\n */\n getSkillDescriptor(): SkillDescriptor | undefined;\n\n /**\n * Send an ILP payment to a provider for compute settlement.\n *\n * Extracts the compute cost from the result event's `amount` tag via\n * parseJobResult(), optionally validates against the original bid amount\n * (E5-R005 bid validation), and sends a pure ILP value transfer\n * (empty data field) to the provider's ILP address.\n *\n * This is a payment-only operation -- no TOON encoding, no relay write.\n * The payment routes through the ILP mesh using the same infrastructure\n * as relay write fees.\n *\n * @param resultEvent - Kind 6xxx result event with amount tag\n * @param providerIlpAddress - Provider's ILP address from kind:10035\n * @param options - Optional originalBid for bid validation (E5-R005)\n * @returns ILP send result with accepted/rejected status\n */\n settleCompute(\n resultEvent: NostrEvent,\n providerIlpAddress: string,\n options?: { originalBid?: string }\n ): Promise<IlpSendResult>;\n}\n\n/**\n * Creates a fully wired ServiceNode from configuration.\n *\n * The returned node has the full ILP packet processing pipeline wired in the\n * correct order:\n * 1. Shallow TOON parse (extract routing metadata)\n * 2. Schnorr signature verification (reject with F06 if invalid)\n * 3. Pricing validation (reject with F04 if underpaid)\n * 4. Handler dispatch (route to kind-specific or default handler)\n *\n * Supports two deployment modes:\n * - **Embedded** (`connector`): Uses createToonNode for zero-latency\n * packet delivery via direct function calls.\n * - **Standalone** (`connectorUrl` + `handlerPort`): Starts an HTTP server\n * to receive packets and uses HTTP clients for the connector API.\n *\n * Handlers can be registered via config or post-creation .on()/.onDefault().\n */\nexport function createNode(config: NodeConfig): ServiceNode {\n // 0. Validate connector mode\n const hasConnector = config.connector !== undefined;\n const hasConnectorUrl = config.connectorUrl !== undefined;\n\n if (hasConnector && hasConnectorUrl) {\n throw new NodeError(\n 'NodeConfig: provide either connector or connectorUrl, not both'\n );\n }\n if (!hasConnector && !hasConnectorUrl) {\n throw new NodeError(\n 'NodeConfig: one of connector or connectorUrl is required'\n );\n }\n if (hasConnectorUrl && config.handlerPort === undefined) {\n throw new NodeError(\n 'NodeConfig: handlerPort is required when using connectorUrl (standalone mode)'\n );\n }\n const embeddedMode = hasConnector;\n\n // 0b. Resolve chain config and auto-populate settlementInfo if not set\n const chainConfig = resolveChainConfig(config.chain);\n let effectiveSettlementInfo = config.settlementInfo;\n if (!effectiveSettlementInfo) {\n const chainKey = `evm:base:${chainConfig.chainId}`;\n const supportedChains = [chainKey];\n const preferredTokens: Record<string, string> = {\n [chainKey]: chainConfig.usdcAddress,\n };\n const tokenNetworks: Record<string, string> | undefined =\n chainConfig.tokenNetworkAddress\n ? { [chainKey]: chainConfig.tokenNetworkAddress }\n : undefined;\n\n effectiveSettlementInfo = {\n supportedChains,\n preferredTokens,\n ...(tokenNetworks && { tokenNetworks }),\n };\n }\n\n // 1. Derive identity from secretKey\n let identity;\n try {\n identity = fromSecretKey(config.secretKey);\n } catch (error: unknown) {\n throw new NodeError(\n `Invalid secretKey: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n const { pubkey, evmAddress } = identity;\n\n // 2. Create handler registry\n const registry = new HandlerRegistry();\n\n // 3. Register config-based handlers (with kind validation matching node.on())\n if (config.handlers) {\n for (const [kind, handler] of Object.entries(config.handlers)) {\n const kindNum = Number(kind);\n if (!Number.isInteger(kindNum) || kindNum < 0) {\n throw new NodeError(\n `Invalid event kind in handlers config: expected a non-negative integer, got '${kind}'`\n );\n }\n registry.on(kindNum, handler);\n }\n }\n\n // 4. Register config-based default handler\n if (config.defaultHandler) {\n registry.onDefault(config.defaultHandler);\n }\n\n // 5. Create verification pipeline\n const verifier = createVerificationPipeline({\n devMode: config.devMode ?? false,\n });\n\n // 6. Create pricing validator\n const pricer = createPricingValidator({\n basePricePerByte: config.basePricePerByte ?? 10n,\n ownPubkey: pubkey,\n kindPricing: config.kindPricing,\n });\n\n // 7. Set up TOON codec with defaults\n const encoder = config.toonEncoder ?? encodeEventToToon;\n const decoder = config.toonDecoder ?? decodeEventFromToon;\n\n // Context decoder: converts base64 TOON string to NostrEvent\n const contextDecoder = (toon: string): NostrEvent => {\n const bytes = Buffer.from(toon, 'base64');\n return decoder(bytes);\n };\n\n // Mutable ref so the packet handler closure can access the tracker after it's created.\n const trackerRef: { current?: { processEvent(event: NostrEvent): void } } =\n {};\n\n // 8. Build the pipelined packet handler (Option A: directly on HandlePacketRequest)\n const pipelinedHandler = async (\n request: HandlePacketRequest\n ): Promise<HandlePacketResponse> => {\n // Step 0: Reject oversized payloads before allocating memory (DoS mitigation)\n if (request.data.length > MAX_PAYLOAD_BASE64_LENGTH) {\n return {\n accept: false,\n code: 'F06',\n message: `Payload too large: ${request.data.length} bytes exceeds maximum ${MAX_PAYLOAD_BASE64_LENGTH}`,\n };\n }\n\n // Step 1: Shallow TOON parse\n const toonBytes = Buffer.from(request.data, 'base64');\n let meta;\n try {\n meta = shallowParseToon(toonBytes);\n } catch {\n // Corrupted TOON data cannot be parsed -- reject as invalid payload\n return {\n accept: false,\n code: 'F06',\n message: 'Invalid TOON payload: failed to parse routing metadata',\n };\n }\n\n // Dev mode: log packet details\n // Sanitize user-controlled fields (amount, destination) to prevent log injection\n // via newlines or control characters. meta.kind (integer) and meta.pubkey (validated\n // hex) are safe; request.data preview is base64 (safe character set).\n if (config.devMode ?? false) {\n const toonPreview =\n request.data.length > 80\n ? request.data.substring(0, 80) + '...'\n : request.data;\n // eslint-disable-next-line no-control-regex\n const sanitize = (s: string) => s.replace(/[\\x00-\\x1f\\x7f]/g, '');\n console.log(\n '[toon:dev]',\n `kind=${meta.kind}`,\n `pubkey=${meta.pubkey.substring(0, 16)}...`,\n `amount=${sanitize(request.amount)}`,\n `dest=${sanitize(request.destination)}`,\n `toon=${toonPreview}`\n );\n }\n\n // Step 2: Verify signature\n const verifyResult = await verifier.verify(meta, request.data);\n if (!verifyResult.verified) {\n // VerificationResult.rejection is always set when verified=false per\n // createVerificationPipeline contract. Guard defensively anyway.\n if (verifyResult.rejection) {\n return verifyResult.rejection;\n }\n return { accept: false, code: 'F06', message: 'Verification failed' };\n }\n\n // Step 3: Validate pricing (skip in dev mode)\n let amount: bigint;\n try {\n amount = BigInt(request.amount);\n } catch {\n if (config.devMode ?? false) {\n amount = 0n;\n } else {\n return {\n accept: false,\n code: 'T00',\n message: 'Invalid payment amount',\n };\n }\n }\n if (!(config.devMode ?? false)) {\n const priceResult = pricer.validate(meta, amount);\n if (!priceResult.accepted) {\n // PricingValidationResult.rejection is always set when accepted=false per\n // createPricingValidator contract. Guard defensively anyway.\n if (priceResult.rejection) {\n return priceResult.rejection;\n }\n return {\n accept: false,\n code: 'F04',\n message: 'Pricing validation failed',\n };\n }\n }\n\n // Step 4: Build HandlerContext with real metadata\n const ctx = createHandlerContext({\n toon: request.data,\n meta,\n amount,\n destination: request.destination,\n toonDecoder: contextDecoder,\n });\n\n // Step 5: Dispatch to handler (T00 error boundary)\n try {\n const result = await registry.dispatch(ctx);\n\n // Feed accepted kind:10032 events to discovery tracker for peer discovery.\n // Uses late-binding reference since the tracker is created after this handler.\n if (result.accept && meta.kind === 10032 && trackerRef.current) {\n const decoded = ctx.decode();\n if (decoded) {\n trackerRef.current.processEvent(decoded);\n }\n }\n\n return result;\n } catch (err: unknown) {\n // Log only the error message, not the full error object (which may contain payload data)\n const errMsg = err instanceof Error ? err.message : 'Unknown error';\n console.error('Handler dispatch failed:', errMsg);\n return { accept: false, code: 'T00', message: 'Internal error' };\n }\n };\n\n // ILP info shared between both modes\n const ilpInfo = {\n ilpAddress: config.ilpAddress ?? 'g.toon.local',\n btpEndpoint: config.btpEndpoint ?? '',\n assetCode: config.assetCode ?? 'USD',\n assetScale: config.assetScale ?? 6,\n };\n\n // 9. Branch: embedded mode vs standalone mode\n let ilpClient: IlpClient;\n let adminClient: ConnectorAdminClient;\n let channelClient: ConnectorChannelClient | null = null;\n let bootstrapServiceInstance: BootstrapService;\n let discoveryTrackerInstance: DiscoveryTracker;\n let httpServer: HttpServer | null = null;\n\n // Lifecycle delegates (start/stop differ per mode)\n let doStart: () => Promise<{\n bootstrapResults: BootstrapResult[];\n peerCount: number;\n channelCount: number;\n }>;\n let doStop: () => Promise<void>;\n\n if (embeddedMode) {\n // --- EMBEDDED MODE: delegate to createToonNode ---\n const toonNode = createToonNode({\n connector: config.connector as NonNullable<typeof config.connector>,\n handlePacket: pipelinedHandler,\n secretKey: config.secretKey,\n ilpInfo,\n toonEncoder: encoder,\n toonDecoder: decoder,\n relayUrl: config.relayUrl,\n knownPeers: config.knownPeers,\n settlementInfo: effectiveSettlementInfo,\n basePricePerByte: config.basePricePerByte,\n ardriveEnabled: config.ardriveEnabled,\n });\n\n ilpClient = toonNode.ilpClient;\n channelClient = toonNode.channelClient;\n bootstrapServiceInstance = toonNode.bootstrapService;\n discoveryTrackerInstance = toonNode.discoveryTracker;\n adminClient = {\n addPeer: () => Promise.resolve(),\n removePeer: () => Promise.resolve(),\n };\n\n trackerRef.current = discoveryTrackerInstance;\n\n doStart = async () => toonNode.start();\n doStop = async () => toonNode.stop();\n } else {\n // --- STANDALONE MODE: manual composition with HTTP clients ---\n const connectorUrl = config.connectorUrl as string;\n const handlerPort = config.handlerPort as number;\n\n ilpClient = createHttpIlpClient(connectorUrl);\n adminClient = createHttpConnectorAdmin(connectorUrl, '');\n\n // Channel client via HTTP if settlement is configured\n if (effectiveSettlementInfo) {\n channelClient = createHttpChannelClient(connectorUrl);\n }\n\n // Create BootstrapService\n bootstrapServiceInstance = new BootstrapService(\n {\n knownPeers: config.knownPeers ?? [],\n ardriveEnabled: config.ardriveEnabled ?? false,\n defaultRelayUrl: config.relayUrl ?? '',\n settlementInfo: effectiveSettlementInfo,\n ownIlpAddress: ilpInfo.ilpAddress,\n toonEncoder: encoder,\n toonDecoder: decoder,\n basePricePerByte: config.basePricePerByte ?? 10n,\n },\n config.secretKey,\n ilpInfo\n );\n\n bootstrapServiceInstance.setIlpClient(ilpClient);\n bootstrapServiceInstance.setConnectorAdmin(adminClient);\n if (channelClient) {\n bootstrapServiceInstance.setChannelClient(channelClient);\n }\n\n // Create DiscoveryTracker\n discoveryTrackerInstance = createDiscoveryTracker({\n secretKey: config.secretKey,\n settlementInfo: effectiveSettlementInfo,\n });\n discoveryTrackerInstance.setConnectorAdmin(adminClient);\n if (channelClient) {\n discoveryTrackerInstance.setChannelClient(channelClient);\n }\n\n trackerRef.current = discoveryTrackerInstance;\n\n doStart = async () => {\n // Start HTTP server for receiving ILP packets\n httpServer = createServer(async (req, res) => {\n if (req.method === 'GET' && req.url === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'healthy', pubkey }));\n return;\n }\n\n if (req.method === 'POST' && req.url === '/handle-packet') {\n let body = '';\n req.on('data', (chunk: Buffer) => {\n body += chunk.toString();\n });\n req.on('end', async () => {\n try {\n const request = JSON.parse(body) as HandlePacketRequest;\n if (\n request.amount === undefined ||\n request.amount === null ||\n request.destination === undefined ||\n request.destination === null ||\n request.data === undefined ||\n request.data === null\n ) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n accept: false,\n code: 'F00',\n message: 'Missing required fields',\n })\n );\n return;\n }\n const result = await pipelinedHandler(request);\n res.writeHead(result.accept ? 200 : 400, {\n 'Content-Type': 'application/json',\n });\n res.end(JSON.stringify(result));\n } catch (err: unknown) {\n console.error('[SDK] handle-packet error:', err);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n accept: false,\n code: 'T00',\n message: 'Internal server error',\n })\n );\n }\n });\n return;\n }\n\n res.writeHead(404);\n res.end();\n });\n\n await new Promise<void>((resolve) => {\n httpServer?.listen(handlerPort, () => resolve());\n });\n\n // Run bootstrap\n const results = await bootstrapServiceInstance.bootstrap();\n const bootstrapPeerPubkeys = results.map((r) => r.knownPeer.pubkey);\n discoveryTrackerInstance.addExcludedPubkeys(bootstrapPeerPubkeys);\n\n return {\n bootstrapResults: results,\n peerCount: results.length,\n channelCount: results.filter((r) => r.channelId).length,\n };\n };\n\n doStop = async () => {\n const server = httpServer;\n if (server) {\n await new Promise<void>((resolve) => {\n server.close(() => resolve());\n });\n httpServer = null;\n }\n };\n }\n\n // 10. Track SDK-level lifecycle state\n let started = false;\n\n // 11. Build and return ServiceNode\n const node: ServiceNode = {\n get pubkey() {\n return pubkey;\n },\n get evmAddress() {\n return evmAddress;\n },\n get connector() {\n return config.connector ?? null;\n },\n get channelClient() {\n return channelClient;\n },\n\n getSkillDescriptor(): SkillDescriptor | undefined {\n return buildSkillDescriptor(registry, {\n basePricePerByte: config.basePricePerByte ?? 10n,\n kindPricing: config.kindPricing,\n ...config.skillConfig,\n });\n },\n\n on(\n kindOrEvent: number | string,\n handlerOrListener: Handler | BootstrapEventListener\n ): ServiceNode {\n if (typeof kindOrEvent === 'number') {\n // Handler registration (existing behavior)\n if (!Number.isInteger(kindOrEvent) || kindOrEvent < 0) {\n throw new NodeError(\n `Invalid event kind: expected a non-negative integer, got ${String(kindOrEvent)}`\n );\n }\n registry.on(kindOrEvent, handlerOrListener as Handler);\n } else if (kindOrEvent === 'bootstrap') {\n // Lifecycle event listener -- forward to bootstrapService AND discoveryTracker\n const listener = handlerOrListener as BootstrapEventListener;\n bootstrapServiceInstance.on(listener);\n discoveryTrackerInstance.on(listener);\n } else {\n // Sanitize event name to prevent log injection via control characters\n // eslint-disable-next-line no-control-regex\n const sanitized = String(kindOrEvent).replace(/[\\x00-\\x1f\\x7f]/g, '');\n throw new NodeError(\n `Unknown lifecycle event: '${sanitized}'. Supported: 'bootstrap'`\n );\n }\n return node;\n },\n\n onDefault(handler: Handler): ServiceNode {\n registry.onDefault(handler);\n return node;\n },\n\n async start(): Promise<StartResult> {\n if (started) {\n throw new NodeError('Node already started');\n }\n\n try {\n const result = await doStart();\n started = true;\n return {\n peerCount: result.peerCount,\n channelCount: result.channelCount,\n bootstrapResults: result.bootstrapResults,\n };\n } catch (error: unknown) {\n if (error instanceof NodeError) {\n throw error;\n }\n throw new NodeError(\n `Failed to start node: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n },\n\n async stop(): Promise<void> {\n if (!started) {\n return; // No-op if not started\n }\n\n await doStop();\n started = false;\n },\n\n async peerWith(targetPubkey: string): Promise<void> {\n if (!started) {\n throw new NodeError(\n 'Cannot peer: node not started. Call start() first.'\n );\n }\n // Defense-in-depth: validate pubkey format before delegating to core\n if (\n typeof targetPubkey !== 'string' ||\n targetPubkey.length !== 64 ||\n !/^[0-9a-f]{64}$/.test(targetPubkey)\n ) {\n throw new NodeError(\n 'Invalid pubkey: expected a 64-character lowercase hex string'\n );\n }\n return discoveryTrackerInstance.peerWith(targetPubkey);\n },\n\n async publishEvent(\n event: NostrEvent,\n options?: { destination: string }\n ): Promise<PublishEventResult> {\n // Guard: node must be started\n if (!started) {\n throw new NodeError(\n 'Cannot publish: node not started. Call start() first.'\n );\n }\n\n // Guard: destination is required\n if (!options?.destination) {\n throw new NodeError(\n \"Cannot publish: destination is required. Pass { destination: 'g.peer.address' }.\"\n );\n }\n\n try {\n // TOON-encode the event\n const toonData = encoder(event);\n\n // Compute amount: basePricePerByte * toonData.length\n const amount =\n (config.basePricePerByte ?? 10n) * BigInt(toonData.length);\n\n // Build ILP PREPARE packet using shared construction (packet equivalence\n // with x402 rail -- the destination relay cannot distinguish between\n // packets sent via publishEvent() and the x402 /publish endpoint).\n const packet = buildIlpPrepare({\n destination: options.destination,\n amount,\n data: toonData,\n });\n\n // Send via ILP client\n const result = await ilpClient.sendIlpPacket(packet);\n\n // Map IlpSendResult to PublishEventResult\n if (result.accepted) {\n return {\n success: true,\n eventId: event.id,\n fulfillment: result.fulfillment ?? '',\n };\n }\n\n return {\n success: false,\n eventId: event.id,\n code: result.code ?? 'T00',\n message: result.message ?? 'Unknown error',\n };\n } catch (error: unknown) {\n // Propagate NodeError directly\n if (error instanceof NodeError) {\n throw error;\n }\n throw new NodeError(\n `Failed to publish event: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n },\n\n async publishFeedback(\n requestEventId: string,\n customerPubkey: string,\n status: DvmJobStatus,\n content?: string,\n options?: { destination: string }\n ): Promise<PublishEventResult> {\n // Build Kind 7000 feedback event using provider's secretKey\n const feedbackEvent = buildJobFeedbackEvent(\n { requestEventId, customerPubkey, status, content },\n config.secretKey\n );\n\n // Delegate to publishEvent() for TOON encoding, pricing, and ILP delivery\n return node.publishEvent(feedbackEvent, options);\n },\n\n async publishResult(\n requestEventId: string,\n customerPubkey: string,\n amount: string,\n content: string,\n options?: { destination: string; kind?: number }\n ): Promise<PublishEventResult> {\n // Default result kind is 6100 (text generation result = 5100 + 1000)\n const resultKind = options?.kind ?? 6100;\n\n // Build Kind 6xxx result event using provider's secretKey\n const resultEvent = buildJobResultEvent(\n { kind: resultKind, requestEventId, customerPubkey, amount, content },\n config.secretKey\n );\n\n // Delegate to publishEvent() for TOON encoding, pricing, and ILP delivery\n return node.publishEvent(resultEvent, options);\n },\n\n async settleCompute(\n resultEvent: NostrEvent,\n providerIlpAddress: string,\n options?: { originalBid?: string }\n ): Promise<IlpSendResult> {\n // Guard: node must be started\n if (!started) {\n throw new NodeError(\n 'Cannot settle compute: node not started. Call start() first.'\n );\n }\n\n // Guard: providerIlpAddress must be a non-empty, non-whitespace string\n if (\n !providerIlpAddress ||\n typeof providerIlpAddress !== 'string' ||\n providerIlpAddress.trim() === ''\n ) {\n throw new NodeError(\n \"Cannot settle compute: providerIlpAddress is required. Resolve it from the provider's kind:10035 service discovery event.\"\n );\n }\n\n // Extract amount from result event via parseJobResult()\n const parsed = parseJobResult(resultEvent);\n if (!parsed) {\n throw new NodeError(\n 'Cannot settle compute: failed to parse result event. Ensure the event is a valid Kind 6xxx with an amount tag.'\n );\n }\n\n const computeAmount = parsed.amount;\n\n // Validate computeAmount is a valid non-negative numeric string before any\n // further processing. Without this, a non-numeric amount would propagate to\n // ilpClient.sendIlpPacket() and cause a BootstrapError instead of a clear\n // NodeError. Negative amounts would cause undefined behavior in the ILP layer.\n let amountBigInt: bigint;\n try {\n amountBigInt = BigInt(computeAmount);\n } catch {\n throw new NodeError(\n `Cannot settle compute: result event amount ('${computeAmount}') is not a valid numeric string.`\n );\n }\n if (amountBigInt < 0n) {\n throw new NodeError(\n `Cannot settle compute: result event amount ('${computeAmount}') must be non-negative.`\n );\n }\n\n // E5-R005 bid validation: if originalBid is provided, validate amount <= bid\n if (options?.originalBid !== undefined) {\n let bidBigInt: bigint;\n try {\n bidBigInt = BigInt(options.originalBid);\n } catch {\n throw new NodeError(\n `Cannot settle compute: originalBid ('${options.originalBid}') is not a valid numeric string.`\n );\n }\n if (amountBigInt > bidBigInt) {\n throw new NodeError(\n `Cannot settle compute: result amount (${computeAmount}) exceeds original bid (${options.originalBid}). Potential provider overcharge.`\n );\n }\n }\n\n // Send pure ILP value transfer (empty data = no event payload)\n // This is a payment-only operation -- no TOON encoding, no relay write.\n return ilpClient.sendIlpPacket({\n destination: providerIlpAddress,\n amount: computeAmount,\n data: '',\n });\n },\n };\n\n return node;\n}\n","/**\n * Skill descriptor builder for DVM service discovery.\n *\n * Computes a SkillDescriptor from the handler registry and node configuration.\n * The descriptor is embedded in kind:10035 events to advertise DVM capabilities.\n *\n * Returns `undefined` when no DVM handlers are registered (backward compatible\n * with pre-DVM kind:10035 events).\n */\n\nimport type { SkillDescriptor } from '@toon-protocol/core';\nimport type { HandlerRegistry } from './handler-registry.js';\n\n/** Configuration for building a skill descriptor. */\nexport interface BuildSkillDescriptorConfig {\n /** Base price per byte in USDC micro-units (default: 10n). */\n basePricePerByte?: bigint;\n /** Per-kind pricing overrides (kind number -> price in USDC micro-units). */\n kindPricing?: Record<number, bigint>;\n /** Service name override (default: 'toon-dvm'). */\n name?: string;\n /** Schema version override (default: '1.0'). */\n version?: string;\n /** Feature list override. */\n features?: string[];\n /** JSON Schema draft-07 object for job request parameters. */\n inputSchema?: Record<string, unknown>;\n /** Available AI models. */\n models?: string[];\n}\n\n/**\n * Builds a SkillDescriptor from the handler registry and configuration.\n *\n * Returns `undefined` when the registry has no DVM handlers (kinds 5000-5999).\n * When DVM kinds exist, auto-populates the descriptor:\n * - `kinds` from `registry.getDvmKinds()`\n * - `pricing` from `config.kindPricing` overrides or `config.basePricePerByte` fallback\n * - `name`, `version`, `features`, `inputSchema`, `models` from config or defaults\n *\n * @param registry - The handler registry to read DVM kinds from.\n * @param config - Node configuration for pricing and optional overrides.\n * @returns A SkillDescriptor, or undefined if no DVM handlers are registered.\n */\nexport function buildSkillDescriptor(\n registry: HandlerRegistry,\n config: BuildSkillDescriptorConfig = {}\n): SkillDescriptor | undefined {\n const dvmKinds = registry.getDvmKinds();\n if (dvmKinds.length === 0) {\n return undefined;\n }\n\n const basePricePerByte = config.basePricePerByte ?? 10n;\n const kindPricing = config.kindPricing ?? {};\n\n // Derive pricing: per-kind overrides or basePricePerByte fallback\n const pricing: Record<string, string> = {};\n for (const kind of dvmKinds) {\n if (Object.hasOwn(kindPricing, kind)) {\n pricing[String(kind)] = String(kindPricing[kind]);\n } else {\n pricing[String(kind)] = String(basePricePerByte);\n }\n }\n\n const descriptor: SkillDescriptor = {\n name: config.name ?? 'toon-dvm',\n version: config.version ?? '1.0',\n kinds: dvmKinds,\n features: config.features ?? [],\n inputSchema: config.inputSchema ?? { type: 'object', properties: {} },\n pricing,\n };\n\n if (config.models !== undefined) {\n descriptor.models = config.models;\n }\n\n return descriptor;\n}\n"],"mappings":";AAWA;AAAA,EACE,oBAAoB;AAAA,EACpB;AAAA,EACA;AAAA,OACK;AACP,SAAS,gBAAgB;AACzB,SAAS,aAAa;AACtB,SAAS,oBAAoB;AAC7B,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;;;AChB3B,SAAS,iBAAiB;AAMnB,IAAM,gBAAN,cAA4B,UAAU;AAAA,EAC3C,YAAY,SAAiB,OAAe;AAC1C,UAAM,SAAS,kBAAkB,KAAK;AACtC,SAAK,OAAO;AAAA,EACd;AACF;AAMO,IAAM,YAAN,cAAwB,UAAU;AAAA,EACvC,YAAY,SAAiB,OAAe;AAC1C,UAAM,SAAS,cAAc,KAAK;AAClC,SAAK,OAAO;AAAA,EACd;AACF;AAMO,IAAM,eAAN,cAA2B,UAAU;AAAA,EAC1C,YAAY,SAAiB,OAAe;AAC1C,UAAM,SAAS,iBAAiB,KAAK;AACrC,SAAK,OAAO;AAAA,EACd;AACF;AAMO,IAAM,oBAAN,cAAgC,UAAU;AAAA,EAC/C,YAAY,SAAiB,OAAe;AAC1C,UAAM,SAAS,sBAAsB,KAAK;AAC1C,SAAK,OAAO;AAAA,EACd;AACF;AAMO,IAAM,eAAN,cAA2B,UAAU;AAAA,EAC1C,YAAY,SAAiB,OAAe;AAC1C,UAAM,SAAS,iBAAiB,KAAK;AACrC,SAAK,OAAO;AAAA,EACd;AACF;;;ADXO,SAAS,mBAA2B;AACzC,SAAO,kBAAkB,UAAU,GAAG;AACxC;AAYO,SAAS,aACd,UACA,SACc;AACd,MAAI,CAAC,iBAAiB,UAAU,QAAQ,GAAG;AACzC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,eAAe,SAAS,gBAAgB;AAE9C,MACE,CAAC,OAAO,UAAU,YAAY,KAC9B,eAAe,KACf,eAAe,iBACf;AACA,UAAM,IAAI;AAAA,MACR,+DAA+D,eAAe,UAAU,OAAO,YAAY,CAAC;AAAA,IAC9G;AAAA,EACF;AAEA,QAAM,OAAO,oBAAoB,YAAY;AAE7C,MAAI;AACJ,MAAI;AACF,WAAO,mBAAmB,QAAQ;AAClC,UAAM,QAAQ,MAAM,eAAe,IAAI,EAAE,OAAO,IAAI;AAEpD,QAAI,CAAC,MAAM,YAAY;AACrB,YAAM,IAAI,cAAc,wCAAwC,IAAI,EAAE;AAAA,IACxE;AAEA,UAAM,YAAY,MAAM;AACxB,WAAO,eAAe,SAAS;AAAA,EACjC,SAAS,OAAgB;AACvB,QAAI,iBAAiB,eAAe;AAClC,YAAM;AAAA,IACR;AACA,UAAM,IAAI;AAAA,MACR,iCAAiC,IAAI,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MAChG,iBAAiB,QAAQ,QAAQ;AAAA,IACnC;AAAA,EACF,UAAE;AAIA,QAAI,MAAM;AACR,WAAK,KAAK,CAAC;AAAA,IACb;AAAA,EACF;AACF;AASO,SAAS,cAAc,WAAqC;AACjE,MAAI,EAAE,qBAAqB,aAAa;AACtC,UAAM,IAAI;AAAA,MACR,gDAAgD,cAAc,OAAO,SAAS,OAAO,SAAS;AAAA,IAChG;AAAA,EACF;AAEA,MAAI,UAAU,WAAW,IAAI;AAC3B,UAAM,IAAI;AAAA,MACR,8CAA8C,UAAU,MAAM;AAAA,IAChE;AAAA,EACF;AAEA,MAAI;AACF,WAAO,eAAe,SAAS;AAAA,EACjC,SAAS,OAAgB;AACvB,QAAI,iBAAiB,eAAe;AAClC,YAAM;AAAA,IACR;AACA,UAAM,IAAI;AAAA,MACR,uBAAuB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MAC7E,iBAAiB,QAAQ,QAAQ;AAAA,IACnC;AAAA,EACF;AACF;AAMA,IAAM,kBAAkB;AAMxB,SAAS,eAAe,WAAqC;AAE3D,QAAM,SAAS,aAAa,SAAS;AAGrC,QAAM,aAAa,kBAAkB,SAAS;AAI9C,SAAO,EAAE,WAAW,IAAI,WAAW,SAAS,GAAG,QAAQ,WAAW;AACpE;AAYA,SAAS,kBAAkB,WAA+B;AAExD,QAAM,qBAAqB,UAAU,aAAa,WAAW,KAAK;AAGlE,QAAM,sBAAsB,mBAAmB,MAAM,CAAC;AAGtD,QAAM,OAAO,WAAW,mBAAmB;AAG3C,QAAM,eAAe,KAAK,MAAM,GAAG;AACnC,QAAM,aAAa,WAAW,YAAY;AAG1C,SAAO,kBAAkB,UAAU;AACrC;AAcA,SAAS,kBAAkB,YAA4B;AACrD,QAAM,QAAQ,WAAW,YAAY;AACrC,QAAM,OAAO,WAAW,WAAW,IAAI,YAAY,EAAE,OAAO,KAAK,CAAC,CAAC;AAEnE,MAAI,cAAc;AAClB,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,UAAM,OAAO,MAAM,CAAC;AACpB,UAAM,WAAW,KAAK,CAAC;AACvB,QAAI,SAAS,UAAa,aAAa,QAAW;AAChD,YAAM,IAAI;AAAA,QACR,iCAAiC,CAAC;AAAA,MACpC;AAAA,IACF;AACA,UAAM,aAAa,SAAS,UAAU,EAAE;AACxC,mBAAe,cAAc,IAAI,KAAK,YAAY,IAAI;AAAA,EACxD;AAEA,SAAO;AACT;;;AEnLO,SAAS,qBACd,SACgB;AAChB,MAAI;AAEJ,SAAO;AAAA,IACL,IAAI,OAAO;AACT,aAAO,QAAQ;AAAA,IACjB;AAAA,IACA,IAAI,OAAO;AACT,aAAO,QAAQ,KAAK;AAAA,IACtB;AAAA,IACA,IAAI,SAAS;AACX,aAAO,QAAQ,KAAK;AAAA,IACtB;AAAA,IACA,IAAI,SAAS;AACX,aAAO,QAAQ;AAAA,IACjB;AAAA,IACA,IAAI,cAAc;AAChB,aAAO,QAAQ;AAAA,IACjB;AAAA,IACA,SAAqB;AACnB,UAAI,CAAC,aAAa;AAChB,sBAAc,QAAQ,YAAY,QAAQ,IAAI;AAAA,MAChD;AACA,aAAO;AAAA,IACT;AAAA,IACA,OAAO,UAAgE;AAIrE,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,MACjC;AAAA,IACF;AAAA,IACA,OAAO,MAAc,SAA6C;AAChE,aAAO;AAAA,QACL,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;ACzFA,SAAS,6BAA6B;AAgB/B,IAAM,kBAAN,MAAsB;AAAA,EACnB,WAAW,oBAAI,IAAqB;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMR,GAAG,MAAc,SAAwB;AACvC,SAAK,SAAS,IAAI,MAAM,OAAO;AAC/B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,SAAwB;AAChC,SAAK,iBAAiB;AACtB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,qBAA+B;AAC7B,WAAO,CAAC,GAAG,KAAK,SAAS,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,cAAwB;AACtB,UAAM,gBAAgB;AACtB,UAAM,cAAc,wBAAwB;AAC5C,WAAO,KAAK,mBAAmB,EAAE;AAAA,MAC/B,CAAC,MAAM,KAAK,iBAAiB,KAAK;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,KAA+C;AAC5D,UAAM,UAAU,KAAK,SAAS,IAAI,IAAI,IAAI;AAC1C,QAAI,SAAS;AACX,aAAO,QAAQ,GAAG;AAAA,IACpB;AACA,QAAI,KAAK,gBAAgB;AACvB,aAAO,KAAK,eAAe,GAAG;AAAA,IAChC;AACA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS,kCAAkC,IAAI,IAAI;AAAA,IACrD;AAAA,EACF;AACF;;;ACnDO,SAAS,uBAAuB,QAAgC;AACrE,QAAM,mBAAmB,OAAO,oBAAoB;AAEpD,SAAO;AAAA,IACL,SAAS,MAAuB,QAAyC;AAEvE,UAAI,KAAK,WAAW,OAAO,WAAW;AACpC,eAAO,EAAE,UAAU,KAAK;AAAA,MAC1B;AAGA,YAAM,eACJ,OAAO,eAAe,OAAO,OAAO,OAAO,aAAa,KAAK,IAAI,IAC7D,OAAO,YAAY,KAAK,IAAI,IAC5B;AACN,YAAM,eAAe,gBAAgB;AAErC,YAAM,iBAAiB,OAAO,KAAK,SAAS,MAAM,IAAI;AAEtD,UAAI,SAAS,gBAAgB;AAC3B,eAAO;AAAA,UACL,UAAU;AAAA,UACV,WAAW;AAAA,YACT,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UAAU;AAAA,cACR,UAAU,eAAe,SAAS;AAAA,cAClC,UAAU,OAAO,SAAS;AAAA,YAC5B;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,aAAO,EAAE,UAAU,KAAK;AAAA,IAC1B;AAAA,EACF;AACF;;;ACzDA,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAkBpB,SAAS,2BAA2B,QAAoC;AAC7E,SAAO;AAAA,IACL,MAAM,OACJ,MACA,WAC6B;AAC7B,UAAI,OAAO,SAAS;AAClB,eAAO,EAAE,UAAU,KAAK;AAAA,MAC1B;AAEA,UAAI;AACF,cAAM,WAAW,WAAW,KAAK,GAAG;AACpC,cAAM,WAAW,WAAW,KAAK,EAAE;AACnC,cAAM,cAAc,WAAW,KAAK,MAAM;AAC1C,cAAM,QAAQ,QAAQ,OAAO,UAAU,UAAU,WAAW;AAC5D,YAAI,CAAC,OAAO;AACV,iBAAO;AAAA,YACL,UAAU;AAAA,YACV,WAAW;AAAA,cACT,QAAQ;AAAA,cACR,MAAM;AAAA,cACN,SAAS;AAAA,YACX;AAAA,UACF;AAAA,QACF;AACA,eAAO,EAAE,UAAU,KAAK;AAAA,MAC1B,QAAQ;AACN,eAAO;AAAA,UACL,UAAU;AAAA,UACV,WAAW;AAAA,YACT,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,SAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AC3BO,SAAS,2BAA2B,QAAoC;AAC7E,QAAM,EAAE,SAAS,IAAI;AAErB,SAAO;AAAA,IACL,MAAM,cAAc,SAAmD;AACrE,UAAI;AACJ,UAAI;AACF,iBAAS,OAAO,QAAQ,MAAM;AAAA,MAChC,QAAQ;AACN,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,MACF;AAEA,YAAM,MAAM,qBAAqB;AAAA,QAC/B,MAAM,QAAQ;AAAA,QACd,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,IAAI;AAAA,UACJ,KAAK;AAAA,UACL,UAAU,IAAI,WAAW,CAAC;AAAA,QAC5B;AAAA,QACA;AAAA,QACA,aAAa,QAAQ;AAAA,QACrB,aAAa,MAAM;AACjB,gBAAM,IAAI,MAAM,gCAAgC;AAAA,QAClD;AAAA,MACF,CAAC;AAED,UAAI,QAAQ,WAAW;AACrB,aAAK,SAAS,SAAS,GAAG,EAAE,MAAM,CAAC,QAAiB;AAElD,gBAAM,SAAS,eAAe,QAAQ,IAAI,UAAU;AACpD,kBAAQ,MAAM,0BAA0B,MAAM;AAAA,QAChD,CAAC;AACD,eAAO,EAAE,QAAQ,KAAK;AAAA,MACxB;AAEA,UAAI;AACF,eAAO,MAAM,SAAS,SAAS,GAAG;AAAA,MACpC,SAAS,KAAc;AAErB,cAAM,SAAS,eAAe,QAAQ,IAAI,UAAU;AACpD,gBAAQ,MAAM,kBAAkB,MAAM;AACtC,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AC3EO,SAAS,0BAA0B,SAA2B;AACnE,QAAM,IAAI;AAAA,IACR;AAAA,EAEF;AACF;;;ACLA,SAAS,oBAA+C;AAcxD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAOP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACXA,SAAS,qBACd,UACA,SAAqC,CAAC,GACT;AAC7B,QAAM,WAAW,SAAS,YAAY;AACtC,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,QAAM,mBAAmB,OAAO,oBAAoB;AACpD,QAAM,cAAc,OAAO,eAAe,CAAC;AAG3C,QAAM,UAAkC,CAAC;AACzC,aAAW,QAAQ,UAAU;AAC3B,QAAI,OAAO,OAAO,aAAa,IAAI,GAAG;AACpC,cAAQ,OAAO,IAAI,CAAC,IAAI,OAAO,YAAY,IAAI,CAAC;AAAA,IAClD,OAAO;AACL,cAAQ,OAAO,IAAI,CAAC,IAAI,OAAO,gBAAgB;AAAA,IACjD;AAAA,EACF;AAEA,QAAM,aAA8B;AAAA,IAClC,MAAM,OAAO,QAAQ;AAAA,IACrB,SAAS,OAAO,WAAW;AAAA,IAC3B,OAAO;AAAA,IACP,UAAU,OAAO,YAAY,CAAC;AAAA,IAC9B,aAAa,OAAO,eAAe,EAAE,MAAM,UAAU,YAAY,CAAC,EAAE;AAAA,IACpE;AAAA,EACF;AAEA,MAAI,OAAO,WAAW,QAAW;AAC/B,eAAW,SAAS,OAAO;AAAA,EAC7B;AAEA,SAAO;AACT;;;ADLA,IAAM,4BAA4B;AA+O3B,SAAS,WAAW,QAAiC;AAE1D,QAAM,eAAe,OAAO,cAAc;AAC1C,QAAM,kBAAkB,OAAO,iBAAiB;AAEhD,MAAI,gBAAgB,iBAAiB;AACnC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,gBAAgB,CAAC,iBAAiB;AACrC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,mBAAmB,OAAO,gBAAgB,QAAW;AACvD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,eAAe;AAGrB,QAAM,cAAc,mBAAmB,OAAO,KAAK;AACnD,MAAI,0BAA0B,OAAO;AACrC,MAAI,CAAC,yBAAyB;AAC5B,UAAM,WAAW,YAAY,YAAY,OAAO;AAChD,UAAM,kBAAkB,CAAC,QAAQ;AACjC,UAAM,kBAA0C;AAAA,MAC9C,CAAC,QAAQ,GAAG,YAAY;AAAA,IAC1B;AACA,UAAM,gBACJ,YAAY,sBACR,EAAE,CAAC,QAAQ,GAAG,YAAY,oBAAoB,IAC9C;AAEN,8BAA0B;AAAA,MACxB;AAAA,MACA;AAAA,MACA,GAAI,iBAAiB,EAAE,cAAc;AAAA,IACvC;AAAA,EACF;AAGA,MAAI;AACJ,MAAI;AACF,eAAW,cAAc,OAAO,SAAS;AAAA,EAC3C,SAAS,OAAgB;AACvB,UAAM,IAAI;AAAA,MACR,sBAAsB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MAC5E,iBAAiB,QAAQ,QAAQ;AAAA,IACnC;AAAA,EACF;AACA,QAAM,EAAE,QAAQ,WAAW,IAAI;AAG/B,QAAM,WAAW,IAAI,gBAAgB;AAGrC,MAAI,OAAO,UAAU;AACnB,eAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,OAAO,QAAQ,GAAG;AAC7D,YAAM,UAAU,OAAO,IAAI;AAC3B,UAAI,CAAC,OAAO,UAAU,OAAO,KAAK,UAAU,GAAG;AAC7C,cAAM,IAAI;AAAA,UACR,gFAAgF,IAAI;AAAA,QACtF;AAAA,MACF;AACA,eAAS,GAAG,SAAS,OAAO;AAAA,IAC9B;AAAA,EACF;AAGA,MAAI,OAAO,gBAAgB;AACzB,aAAS,UAAU,OAAO,cAAc;AAAA,EAC1C;AAGA,QAAM,WAAW,2BAA2B;AAAA,IAC1C,SAAS,OAAO,WAAW;AAAA,EAC7B,CAAC;AAGD,QAAM,SAAS,uBAAuB;AAAA,IACpC,kBAAkB,OAAO,oBAAoB;AAAA,IAC7C,WAAW;AAAA,IACX,aAAa,OAAO;AAAA,EACtB,CAAC;AAGD,QAAM,UAAU,OAAO,eAAe;AACtC,QAAM,UAAU,OAAO,eAAe;AAGtC,QAAM,iBAAiB,CAAC,SAA6B;AACnD,UAAM,QAAQ,OAAO,KAAK,MAAM,QAAQ;AACxC,WAAO,QAAQ,KAAK;AAAA,EACtB;AAGA,QAAM,aACJ,CAAC;AAGH,QAAM,mBAAmB,OACvB,YACkC;AAElC,QAAI,QAAQ,KAAK,SAAS,2BAA2B;AACnD,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,SAAS,sBAAsB,QAAQ,KAAK,MAAM,0BAA0B,yBAAyB;AAAA,MACvG;AAAA,IACF;AAGA,UAAM,YAAY,OAAO,KAAK,QAAQ,MAAM,QAAQ;AACpD,QAAI;AACJ,QAAI;AACF,aAAO,iBAAiB,SAAS;AAAA,IACnC,QAAQ;AAEN,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,IACF;AAMA,QAAI,OAAO,WAAW,OAAO;AAC3B,YAAM,cACJ,QAAQ,KAAK,SAAS,KAClB,QAAQ,KAAK,UAAU,GAAG,EAAE,IAAI,QAChC,QAAQ;AAEd,YAAM,WAAW,CAAC,MAAc,EAAE,QAAQ,oBAAoB,EAAE;AAChE,cAAQ;AAAA,QACN;AAAA,QACA,QAAQ,KAAK,IAAI;AAAA,QACjB,UAAU,KAAK,OAAO,UAAU,GAAG,EAAE,CAAC;AAAA,QACtC,UAAU,SAAS,QAAQ,MAAM,CAAC;AAAA,QAClC,QAAQ,SAAS,QAAQ,WAAW,CAAC;AAAA,QACrC,QAAQ,WAAW;AAAA,MACrB;AAAA,IACF;AAGA,UAAM,eAAe,MAAM,SAAS,OAAO,MAAM,QAAQ,IAAI;AAC7D,QAAI,CAAC,aAAa,UAAU;AAG1B,UAAI,aAAa,WAAW;AAC1B,eAAO,aAAa;AAAA,MACtB;AACA,aAAO,EAAE,QAAQ,OAAO,MAAM,OAAO,SAAS,sBAAsB;AAAA,IACtE;AAGA,QAAI;AACJ,QAAI;AACF,eAAS,OAAO,QAAQ,MAAM;AAAA,IAChC,QAAQ;AACN,UAAI,OAAO,WAAW,OAAO;AAC3B,iBAAS;AAAA,MACX,OAAO;AACL,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AACA,QAAI,EAAE,OAAO,WAAW,QAAQ;AAC9B,YAAM,cAAc,OAAO,SAAS,MAAM,MAAM;AAChD,UAAI,CAAC,YAAY,UAAU;AAGzB,YAAI,YAAY,WAAW;AACzB,iBAAO,YAAY;AAAA,QACrB;AACA,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAGA,UAAM,MAAM,qBAAqB;AAAA,MAC/B,MAAM,QAAQ;AAAA,MACd;AAAA,MACA;AAAA,MACA,aAAa,QAAQ;AAAA,MACrB,aAAa;AAAA,IACf,CAAC;AAGD,QAAI;AACF,YAAM,SAAS,MAAM,SAAS,SAAS,GAAG;AAI1C,UAAI,OAAO,UAAU,KAAK,SAAS,SAAS,WAAW,SAAS;AAC9D,cAAM,UAAU,IAAI,OAAO;AAC3B,YAAI,SAAS;AACX,qBAAW,QAAQ,aAAa,OAAO;AAAA,QACzC;AAAA,MACF;AAEA,aAAO;AAAA,IACT,SAAS,KAAc;AAErB,YAAM,SAAS,eAAe,QAAQ,IAAI,UAAU;AACpD,cAAQ,MAAM,4BAA4B,MAAM;AAChD,aAAO,EAAE,QAAQ,OAAO,MAAM,OAAO,SAAS,iBAAiB;AAAA,IACjE;AAAA,EACF;AAGA,QAAM,UAAU;AAAA,IACd,YAAY,OAAO,cAAc;AAAA,IACjC,aAAa,OAAO,eAAe;AAAA,IACnC,WAAW,OAAO,aAAa;AAAA,IAC/B,YAAY,OAAO,cAAc;AAAA,EACnC;AAGA,MAAI;AACJ,MAAI;AACJ,MAAI,gBAA+C;AACnD,MAAI;AACJ,MAAI;AACJ,MAAI,aAAgC;AAGpC,MAAI;AAKJ,MAAI;AAEJ,MAAI,cAAc;AAEhB,UAAM,WAAW,eAAe;AAAA,MAC9B,WAAW,OAAO;AAAA,MAClB,cAAc;AAAA,MACd,WAAW,OAAO;AAAA,MAClB;AAAA,MACA,aAAa;AAAA,MACb,aAAa;AAAA,MACb,UAAU,OAAO;AAAA,MACjB,YAAY,OAAO;AAAA,MACnB,gBAAgB;AAAA,MAChB,kBAAkB,OAAO;AAAA,MACzB,gBAAgB,OAAO;AAAA,IACzB,CAAC;AAED,gBAAY,SAAS;AACrB,oBAAgB,SAAS;AACzB,+BAA2B,SAAS;AACpC,+BAA2B,SAAS;AACpC,kBAAc;AAAA,MACZ,SAAS,MAAM,QAAQ,QAAQ;AAAA,MAC/B,YAAY,MAAM,QAAQ,QAAQ;AAAA,IACpC;AAEA,eAAW,UAAU;AAErB,cAAU,YAAY,SAAS,MAAM;AACrC,aAAS,YAAY,SAAS,KAAK;AAAA,EACrC,OAAO;AAEL,UAAM,eAAe,OAAO;AAC5B,UAAM,cAAc,OAAO;AAE3B,gBAAY,oBAAoB,YAAY;AAC5C,kBAAc,yBAAyB,cAAc,EAAE;AAGvD,QAAI,yBAAyB;AAC3B,sBAAgB,wBAAwB,YAAY;AAAA,IACtD;AAGA,+BAA2B,IAAI;AAAA,MAC7B;AAAA,QACE,YAAY,OAAO,cAAc,CAAC;AAAA,QAClC,gBAAgB,OAAO,kBAAkB;AAAA,QACzC,iBAAiB,OAAO,YAAY;AAAA,QACpC,gBAAgB;AAAA,QAChB,eAAe,QAAQ;AAAA,QACvB,aAAa;AAAA,QACb,aAAa;AAAA,QACb,kBAAkB,OAAO,oBAAoB;AAAA,MAC/C;AAAA,MACA,OAAO;AAAA,MACP;AAAA,IACF;AAEA,6BAAyB,aAAa,SAAS;AAC/C,6BAAyB,kBAAkB,WAAW;AACtD,QAAI,eAAe;AACjB,+BAAyB,iBAAiB,aAAa;AAAA,IACzD;AAGA,+BAA2B,uBAAuB;AAAA,MAChD,WAAW,OAAO;AAAA,MAClB,gBAAgB;AAAA,IAClB,CAAC;AACD,6BAAyB,kBAAkB,WAAW;AACtD,QAAI,eAAe;AACjB,+BAAyB,iBAAiB,aAAa;AAAA,IACzD;AAEA,eAAW,UAAU;AAErB,cAAU,YAAY;AAEpB,mBAAa,aAAa,OAAO,KAAK,QAAQ;AAC5C,YAAI,IAAI,WAAW,SAAS,IAAI,QAAQ,WAAW;AACjD,cAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,cAAI,IAAI,KAAK,UAAU,EAAE,QAAQ,WAAW,OAAO,CAAC,CAAC;AACrD;AAAA,QACF;AAEA,YAAI,IAAI,WAAW,UAAU,IAAI,QAAQ,kBAAkB;AACzD,cAAI,OAAO;AACX,cAAI,GAAG,QAAQ,CAAC,UAAkB;AAChC,oBAAQ,MAAM,SAAS;AAAA,UACzB,CAAC;AACD,cAAI,GAAG,OAAO,YAAY;AACxB,gBAAI;AACF,oBAAM,UAAU,KAAK,MAAM,IAAI;AAC/B,kBACE,QAAQ,WAAW,UACnB,QAAQ,WAAW,QACnB,QAAQ,gBAAgB,UACxB,QAAQ,gBAAgB,QACxB,QAAQ,SAAS,UACjB,QAAQ,SAAS,MACjB;AACA,oBAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,oBAAI;AAAA,kBACF,KAAK,UAAU;AAAA,oBACb,QAAQ;AAAA,oBACR,MAAM;AAAA,oBACN,SAAS;AAAA,kBACX,CAAC;AAAA,gBACH;AACA;AAAA,cACF;AACA,oBAAM,SAAS,MAAM,iBAAiB,OAAO;AAC7C,kBAAI,UAAU,OAAO,SAAS,MAAM,KAAK;AAAA,gBACvC,gBAAgB;AAAA,cAClB,CAAC;AACD,kBAAI,IAAI,KAAK,UAAU,MAAM,CAAC;AAAA,YAChC,SAAS,KAAc;AACrB,sBAAQ,MAAM,8BAA8B,GAAG;AAC/C,kBAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,kBAAI;AAAA,gBACF,KAAK,UAAU;AAAA,kBACb,QAAQ;AAAA,kBACR,MAAM;AAAA,kBACN,SAAS;AAAA,gBACX,CAAC;AAAA,cACH;AAAA,YACF;AAAA,UACF,CAAC;AACD;AAAA,QACF;AAEA,YAAI,UAAU,GAAG;AACjB,YAAI,IAAI;AAAA,MACV,CAAC;AAED,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,oBAAY,OAAO,aAAa,MAAM,QAAQ,CAAC;AAAA,MACjD,CAAC;AAGD,YAAM,UAAU,MAAM,yBAAyB,UAAU;AACzD,YAAM,uBAAuB,QAAQ,IAAI,CAAC,MAAM,EAAE,UAAU,MAAM;AAClE,+BAAyB,mBAAmB,oBAAoB;AAEhE,aAAO;AAAA,QACL,kBAAkB;AAAA,QAClB,WAAW,QAAQ;AAAA,QACnB,cAAc,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE;AAAA,MACnD;AAAA,IACF;AAEA,aAAS,YAAY;AACnB,YAAM,SAAS;AACf,UAAI,QAAQ;AACV,cAAM,IAAI,QAAc,CAAC,YAAY;AACnC,iBAAO,MAAM,MAAM,QAAQ,CAAC;AAAA,QAC9B,CAAC;AACD,qBAAa;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAGA,MAAI,UAAU;AAGd,QAAM,OAAoB;AAAA,IACxB,IAAI,SAAS;AACX,aAAO;AAAA,IACT;AAAA,IACA,IAAI,aAAa;AACf,aAAO;AAAA,IACT;AAAA,IACA,IAAI,YAAY;AACd,aAAO,OAAO,aAAa;AAAA,IAC7B;AAAA,IACA,IAAI,gBAAgB;AAClB,aAAO;AAAA,IACT;AAAA,IAEA,qBAAkD;AAChD,aAAO,qBAAqB,UAAU;AAAA,QACpC,kBAAkB,OAAO,oBAAoB;AAAA,QAC7C,aAAa,OAAO;AAAA,QACpB,GAAG,OAAO;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,IAEA,GACE,aACA,mBACa;AACb,UAAI,OAAO,gBAAgB,UAAU;AAEnC,YAAI,CAAC,OAAO,UAAU,WAAW,KAAK,cAAc,GAAG;AACrD,gBAAM,IAAI;AAAA,YACR,4DAA4D,OAAO,WAAW,CAAC;AAAA,UACjF;AAAA,QACF;AACA,iBAAS,GAAG,aAAa,iBAA4B;AAAA,MACvD,WAAW,gBAAgB,aAAa;AAEtC,cAAM,WAAW;AACjB,iCAAyB,GAAG,QAAQ;AACpC,iCAAyB,GAAG,QAAQ;AAAA,MACtC,OAAO;AAGL,cAAM,YAAY,OAAO,WAAW,EAAE,QAAQ,oBAAoB,EAAE;AACpE,cAAM,IAAI;AAAA,UACR,6BAA6B,SAAS;AAAA,QACxC;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,IAEA,UAAU,SAA+B;AACvC,eAAS,UAAU,OAAO;AAC1B,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,QAA8B;AAClC,UAAI,SAAS;AACX,cAAM,IAAI,UAAU,sBAAsB;AAAA,MAC5C;AAEA,UAAI;AACF,cAAM,SAAS,MAAM,QAAQ;AAC7B,kBAAU;AACV,eAAO;AAAA,UACL,WAAW,OAAO;AAAA,UAClB,cAAc,OAAO;AAAA,UACrB,kBAAkB,OAAO;AAAA,QAC3B;AAAA,MACF,SAAS,OAAgB;AACvB,YAAI,iBAAiB,WAAW;AAC9B,gBAAM;AAAA,QACR;AACA,cAAM,IAAI;AAAA,UACR,yBAAyB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,UAC/E,iBAAiB,QAAQ,QAAQ;AAAA,QACnC;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,OAAsB;AAC1B,UAAI,CAAC,SAAS;AACZ;AAAA,MACF;AAEA,YAAM,OAAO;AACb,gBAAU;AAAA,IACZ;AAAA,IAEA,MAAM,SAAS,cAAqC;AAClD,UAAI,CAAC,SAAS;AACZ,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,UACE,OAAO,iBAAiB,YACxB,aAAa,WAAW,MACxB,CAAC,iBAAiB,KAAK,YAAY,GACnC;AACA,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,aAAO,yBAAyB,SAAS,YAAY;AAAA,IACvD;AAAA,IAEA,MAAM,aACJ,OACA,SAC6B;AAE7B,UAAI,CAAC,SAAS;AACZ,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAGA,UAAI,CAAC,SAAS,aAAa;AACzB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,UAAI;AAEF,cAAM,WAAW,QAAQ,KAAK;AAG9B,cAAM,UACH,OAAO,oBAAoB,OAAO,OAAO,SAAS,MAAM;AAK3D,cAAM,SAAS,gBAAgB;AAAA,UAC7B,aAAa,QAAQ;AAAA,UACrB;AAAA,UACA,MAAM;AAAA,QACR,CAAC;AAGD,cAAM,SAAS,MAAM,UAAU,cAAc,MAAM;AAGnD,YAAI,OAAO,UAAU;AACnB,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,MAAM;AAAA,YACf,aAAa,OAAO,eAAe;AAAA,UACrC;AAAA,QACF;AAEA,eAAO;AAAA,UACL,SAAS;AAAA,UACT,SAAS,MAAM;AAAA,UACf,MAAM,OAAO,QAAQ;AAAA,UACrB,SAAS,OAAO,WAAW;AAAA,QAC7B;AAAA,MACF,SAAS,OAAgB;AAEvB,YAAI,iBAAiB,WAAW;AAC9B,gBAAM;AAAA,QACR;AACA,cAAM,IAAI;AAAA,UACR,4BAA4B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,UAClF,iBAAiB,QAAQ,QAAQ;AAAA,QACnC;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,gBACJ,gBACA,gBACA,QACA,SACA,SAC6B;AAE7B,YAAM,gBAAgB;AAAA,QACpB,EAAE,gBAAgB,gBAAgB,QAAQ,QAAQ;AAAA,QAClD,OAAO;AAAA,MACT;AAGA,aAAO,KAAK,aAAa,eAAe,OAAO;AAAA,IACjD;AAAA,IAEA,MAAM,cACJ,gBACA,gBACA,QACA,SACA,SAC6B;AAE7B,YAAM,aAAa,SAAS,QAAQ;AAGpC,YAAM,cAAc;AAAA,QAClB,EAAE,MAAM,YAAY,gBAAgB,gBAAgB,QAAQ,QAAQ;AAAA,QACpE,OAAO;AAAA,MACT;AAGA,aAAO,KAAK,aAAa,aAAa,OAAO;AAAA,IAC/C;AAAA,IAEA,MAAM,cACJ,aACA,oBACA,SACwB;AAExB,UAAI,CAAC,SAAS;AACZ,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAGA,UACE,CAAC,sBACD,OAAO,uBAAuB,YAC9B,mBAAmB,KAAK,MAAM,IAC9B;AACA,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAGA,YAAM,SAAS,eAAe,WAAW;AACzC,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,YAAM,gBAAgB,OAAO;AAM7B,UAAI;AACJ,UAAI;AACF,uBAAe,OAAO,aAAa;AAAA,MACrC,QAAQ;AACN,cAAM,IAAI;AAAA,UACR,gDAAgD,aAAa;AAAA,QAC/D;AAAA,MACF;AACA,UAAI,eAAe,IAAI;AACrB,cAAM,IAAI;AAAA,UACR,gDAAgD,aAAa;AAAA,QAC/D;AAAA,MACF;AAGA,UAAI,SAAS,gBAAgB,QAAW;AACtC,YAAI;AACJ,YAAI;AACF,sBAAY,OAAO,QAAQ,WAAW;AAAA,QACxC,QAAQ;AACN,gBAAM,IAAI;AAAA,YACR,wCAAwC,QAAQ,WAAW;AAAA,UAC7D;AAAA,QACF;AACA,YAAI,eAAe,WAAW;AAC5B,gBAAM,IAAI;AAAA,YACR,yCAAyC,aAAa,2BAA2B,QAAQ,WAAW;AAAA,UACtG;AAAA,QACF;AAAA,MACF;AAIA,aAAO,UAAU,cAAc;AAAA,QAC7B,aAAa;AAAA,QACb,QAAQ;AAAA,QACR,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@toon-protocol/sdk",
3
+ "version": "0.1.4",
4
+ "description": "TOON SDK for building ILP-gated Nostr services",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "tsup --watch",
21
+ "test": "vitest run",
22
+ "test:integration": "vitest run --config vitest.integration.config.ts",
23
+ "test:e2e": "vitest run --config vitest.e2e.config.ts",
24
+ "test:e2e:docker": "vitest run --config vitest.e2e.config.ts -- tests/e2e/docker-publish-event-e2e.test.ts",
25
+ "test:watch": "vitest"
26
+ },
27
+ "keywords": [
28
+ "toon",
29
+ "nostr",
30
+ "ilp",
31
+ "interledger",
32
+ "sdk"
33
+ ],
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/toon-protocol/town.git",
38
+ "directory": "packages/sdk"
39
+ },
40
+ "author": "ALLiDoizCode",
41
+ "engines": {
42
+ "node": ">=20"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "dependencies": {
48
+ "@toon-protocol/core": "workspace:*",
49
+ "@noble/curves": "^2.0.0",
50
+ "@noble/hashes": "^2.0.0",
51
+ "@scure/bip32": "^2.0.0",
52
+ "@scure/bip39": "^2.0.0",
53
+ "nostr-tools": "^2.20.0"
54
+ },
55
+ "peerDependencies": {
56
+ "@toon-protocol/connector": ">=1.7.0"
57
+ },
58
+ "peerDependenciesMeta": {
59
+ "@toon-protocol/connector": {
60
+ "optional": true
61
+ }
62
+ },
63
+ "devDependencies": {
64
+ "@toon-protocol/connector": "^1.7.1",
65
+ "@toon-protocol/relay": "workspace:*",
66
+ "@types/node": "^20.0.0",
67
+ "@types/ws": "^8.0.0",
68
+ "tsup": "^8.0.0",
69
+ "typescript": "^5.3.0",
70
+ "viem": "^2.0.0",
71
+ "vitest": "^1.0.0",
72
+ "ws": "^8.0.0"
73
+ }
74
+ }