@unicitylabs/sphere-sdk 0.11.15-dev.2 → 0.11.15

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.
@@ -465,7 +465,7 @@ function checkCompatibility(input) {
465
465
  }
466
466
 
467
467
  // connect/version.ts
468
- var SDK_VERSION = "0.11.15-dev.2";
468
+ var SDK_VERSION = "0.11.15";
469
469
 
470
470
  // connect/permissions.ts
471
471
  var PERMISSION_SCOPES = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../core/logger.ts","../../core/errors.ts","../../connect/semver.ts","../../constants.ts","../../connect/protocol.ts","../../connect/compatibility.ts","../../connect/version.ts","../../connect/permissions.ts","../../connect/host/ConnectHost.ts","../../connect/client/ConnectClient.ts"],"sourcesContent":["/**\n * Centralized SDK Logger\n *\n * A lightweight singleton logger that works across all tsup bundles\n * by storing state on globalThis. Supports three log levels:\n * - debug: detailed messages (only shown when debug=true)\n * - warn: important warnings (ALWAYS shown regardless of debug flag)\n * - error: critical errors (ALWAYS shown regardless of debug flag)\n *\n * Global debug flag enables all logging. Per-tag overrides allow\n * granular control (e.g., only transport debug).\n *\n * @example\n * ```ts\n * import { logger } from '@unicitylabs/sphere-sdk';\n *\n * // Enable all debug logging\n * logger.configure({ debug: true });\n *\n * // Enable only specific tags\n * logger.setTagDebug('Nostr', true);\n *\n * // Usage in SDK classes\n * logger.debug('Payments', 'Transfer started', { amount, recipient });\n * logger.warn('Nostr', 'queryEvents timed out after 5s');\n * logger.error('Sphere', 'Critical failure', error);\n * ```\n */\n\nexport type LogLevel = 'debug' | 'warn' | 'error';\n\nexport type LogHandler = (level: LogLevel, tag: string, message: string, ...args: unknown[]) => void;\n\nexport interface LoggerConfig {\n /** Enable debug logging globally (default: false). When false, only warn and error messages are shown. */\n debug?: boolean;\n /** Custom log handler. If provided, replaces console output. Useful for tests or custom log sinks. */\n handler?: LogHandler | null;\n}\n\n// Use a unique symbol-like key on globalThis to share logger state across tsup bundles\nconst LOGGER_KEY = '__sphere_sdk_logger__';\n\ninterface LoggerState {\n debug: boolean;\n tags: Record<string, boolean>;\n handler: LogHandler | null;\n}\n\nfunction getState(): LoggerState {\n const g = globalThis as unknown as Record<string, unknown>;\n if (!g[LOGGER_KEY]) {\n g[LOGGER_KEY] = { debug: false, tags: {}, handler: null } satisfies LoggerState;\n }\n return g[LOGGER_KEY] as LoggerState;\n}\n\nfunction isEnabled(tag: string): boolean {\n const state = getState();\n // Per-tag override takes priority\n if (tag in state.tags) return state.tags[tag];\n // Fall back to global flag\n return state.debug;\n}\n\nexport const logger = {\n /**\n * Configure the logger. Can be called multiple times (last write wins).\n * Typically called by createBrowserProviders(), createNodeProviders(), or Sphere.init().\n */\n configure(config: LoggerConfig): void {\n const state = getState();\n if (config.debug !== undefined) state.debug = config.debug;\n if (config.handler !== undefined) state.handler = config.handler;\n },\n\n /**\n * Enable/disable debug logging for a specific tag.\n * Per-tag setting overrides the global debug flag.\n *\n * @example\n * ```ts\n * logger.setTagDebug('Nostr', true); // enable only Nostr logs\n * logger.setTagDebug('Nostr', false); // disable Nostr logs even if global debug=true\n * ```\n */\n setTagDebug(tag: string, enabled: boolean): void {\n getState().tags[tag] = enabled;\n },\n\n /**\n * Clear per-tag override, falling back to global debug flag.\n */\n clearTagDebug(tag: string): void {\n delete getState().tags[tag];\n },\n\n /** Returns true if debug mode is enabled for the given tag (or globally). */\n isDebugEnabled(tag?: string): boolean {\n if (tag) return isEnabled(tag);\n return getState().debug;\n },\n\n /**\n * Debug-level log. Only shown when debug is enabled (globally or for this tag).\n * Use for detailed operational information.\n */\n debug(tag: string, message: string, ...args: unknown[]): void {\n if (!isEnabled(tag)) return;\n const state = getState();\n if (state.handler) {\n state.handler('debug', tag, message, ...args);\n } else {\n console.log(`[${tag}]`, message, ...args);\n }\n },\n\n /**\n * Warning-level log. ALWAYS shown regardless of debug flag.\n * Use for important but non-critical issues (timeouts, retries, degraded state).\n */\n warn(tag: string, message: string, ...args: unknown[]): void {\n const state = getState();\n if (state.handler) {\n state.handler('warn', tag, message, ...args);\n } else {\n console.warn(`[${tag}]`, message, ...args);\n }\n },\n\n /**\n * Error-level log. ALWAYS shown regardless of debug flag.\n * Use for critical failures that should never be silenced.\n */\n error(tag: string, message: string, ...args: unknown[]): void {\n const state = getState();\n if (state.handler) {\n state.handler('error', tag, message, ...args);\n } else {\n console.error(`[${tag}]`, message, ...args);\n }\n },\n\n /** Reset all logger state (debug flag, tags, handler). Primarily for tests. */\n reset(): void {\n const g = globalThis as unknown as Record<string, unknown>;\n delete g[LOGGER_KEY];\n },\n};\n","/**\n * SDK Error Types\n *\n * Structured error codes for programmatic error handling in UI.\n * UI can switch on error.code to show appropriate user-facing messages.\n *\n * @example\n * ```ts\n * import { SphereError } from '@unicitylabs/sphere-sdk';\n *\n * try {\n * await sphere.payments.send({ ... });\n * } catch (err) {\n * if (err instanceof SphereError) {\n * switch (err.code) {\n * case 'INSUFFICIENT_BALANCE': showToast('Not enough funds'); break;\n * case 'INVALID_RECIPIENT': showToast('Recipient not found'); break;\n * case 'TRANSPORT_ERROR': showToast('Network connection issue'); break;\n * case 'TIMEOUT': showToast('Request timed out, try again'); break;\n * default: showToast(err.message);\n * }\n * }\n * }\n * ```\n */\n\nexport type SphereErrorCode =\n | 'NOT_INITIALIZED'\n | 'ALREADY_INITIALIZED'\n | 'INVALID_CONFIG'\n | 'INVALID_IDENTITY'\n | 'INSUFFICIENT_BALANCE'\n | 'INVALID_RECIPIENT'\n | 'TRANSFER_FAILED'\n // The source state was consumed by a DIFFERENT transaction (lost race, not a\n // resume) — raised as TransferConflictError (token-engine/errors.ts, Part E.2).\n | 'TRANSFER_CONFLICT'\n // The op's certification is INDETERMINATE (submit accepted / proof fetch\n // inconclusive) — raised as ProofUnconfirmedError; keep the intent OPEN (#631).\n | 'CERTIFICATION_UNCONFIRMED'\n // Split burn checkpoint (sdk-changes E.4, sphere-sdk#501) — all keep-open (token-engine/errors.ts):\n // burn certified but the checkpoint could not be persisted (no mint submitted);\n | 'CHECKPOINT_PERSIST_FAILED'\n // a certified mint leaf has no reproducing stored bytes, or a stored checkpoint fails byte-binding;\n | 'SPLIT_CHECKPOINT_LOST'\n // a byte-bound checkpoint proof no longer verifies against the current trust base (validator rotation).\n | 'CHECKPOINT_TRUSTBASE_MISMATCH'\n | 'STORAGE_ERROR'\n // #665: the on-chain spend committed but the post-commit wallet-api mirror\n // sync (inventory apply / blob upload / save) failed. NOT a lost payment —\n // the intent is kept open and resume converges the mirror (idempotent apply,\n // #664). Surfaced distinctly so a UI can reassure (\"sent — wallet catching\n // up\") instead of showing a hard send failure.\n | 'SEND_SYNC_PENDING'\n // #677: a multi-leg send lost a source to a concurrent transfer AFTER ≥1\n // earlier leg had already certified on-chain AND been journaled/delivered.\n // The delivered value has irreversibly left the wallet, so this is NOT a\n // plain, re-sendable failure: surfacing a bare TransferConflictError makes\n // the caller re-send the FULL amount and pay the delivered leg twice. Raised\n // as PartialSendConflictError; the caller MUST re-plan ONLY the remainder\n // under a NEW transferId, never the whole amount.\n | 'SEND_PARTIALLY_COMPLETED'\n | 'TRANSPORT_ERROR'\n | 'AGGREGATOR_ERROR'\n | 'VALIDATION_ERROR'\n | 'INVALID_AMOUNT'\n | 'NETWORK_ERROR'\n | 'TIMEOUT'\n | 'DECRYPTION_ERROR'\n | 'MODULE_NOT_AVAILABLE'\n | 'SIGNING_ERROR'\n // Token Spend Queue error codes\n | 'SEND_QUEUE_TIMEOUT'\n | 'SEND_INSUFFICIENT_BALANCE'\n | 'SEND_RESERVATION_CANCELLED'\n | 'SEND_QUEUE_FULL'\n | 'MODULE_DESTROYED'\n | 'REENTRANT_GATE'\n // Invoice / Accounting error codes\n | 'INVOICE_NO_TARGETS'\n | 'INVOICE_INVALID_ADDRESS'\n | 'INVOICE_NO_ASSETS'\n | 'INVOICE_INVALID_ASSET'\n | 'INVOICE_INVALID_AMOUNT'\n | 'INVOICE_INVALID_COIN'\n | 'INVOICE_INVALID_NFT'\n | 'INVOICE_PAST_DUE_DATE'\n | 'INVOICE_DUPLICATE_ADDRESS'\n | 'INVOICE_DUPLICATE_COIN'\n | 'INVOICE_DUPLICATE_NFT'\n | 'INVOICE_MINT_FAILED'\n | 'INVOICE_INVALID_PROOF'\n | 'INVOICE_WRONG_TOKEN_TYPE'\n | 'INVOICE_INVALID_DATA'\n | 'INVOICE_ALREADY_EXISTS'\n | 'INVOICE_NOT_FOUND'\n | 'INVOICE_NOT_TARGET'\n | 'INVOICE_ALREADY_CLOSED'\n | 'INVOICE_ALREADY_CANCELLED'\n | 'INVOICE_ORACLE_REQUIRED'\n | 'INVOICE_TERMINATED'\n | 'INVOICE_INVALID_TARGET'\n | 'INVOICE_INVALID_ASSET_INDEX'\n | 'INVOICE_RETURN_EXCEEDS_BALANCE'\n | 'INVOICE_INVALID_DELIVERY_METHOD'\n | 'INVOICE_INVALID_REFUND_ADDRESS'\n | 'INVOICE_INVALID_CONTACT'\n | 'INVOICE_INVALID_ID'\n | 'INVOICE_TOO_MANY_TARGETS'\n | 'INVOICE_TOO_MANY_ASSETS'\n | 'INVOICE_MEMO_TOO_LONG'\n | 'INVOICE_TERMS_TOO_LARGE'\n | 'INVOICE_NOT_TERMINATED'\n | 'INVOICE_NOT_CANCELLED'\n | 'INVOICE_STORAGE_FAILED'\n | 'RATE_LIMITED'\n | 'COMMUNICATIONS_UNAVAILABLE'\n // Swap error codes\n | 'SWAP_INVALID_DEAL'\n | 'SWAP_INVALID_MANIFEST'\n | 'SWAP_NOT_FOUND'\n | 'SWAP_WRONG_STATE'\n | 'SWAP_RESOLVE_FAILED'\n | 'SWAP_DM_SEND_FAILED'\n | 'SWAP_ESCROW_REJECTED'\n | 'SWAP_DEPOSIT_FAILED'\n | 'SWAP_PAYOUT_VERIFICATION_FAILED'\n | 'SWAP_ALREADY_EXISTS'\n | 'SWAP_ALREADY_COMPLETED'\n | 'SWAP_ALREADY_CANCELLED'\n | 'SWAP_TIMEOUT'\n | 'SWAP_LIMIT_EXCEEDED'\n | 'SWAP_ALREADY_INITIALIZED'\n | 'SWAP_MODULE_DESTROYED'\n | 'SWAP_NOT_INITIALIZED';\n\nexport class SphereError extends Error {\n readonly code: SphereErrorCode;\n readonly cause?: unknown;\n /**\n * #441 deferred-paid linkage: the transferId of the possibly-committed send,\n * stamped by `sendOnce` before the error leaves so a payment-request consumer\n * can durably journal request→transfer and resolve 'paid' only when that\n * transfer actually completes (never optimistically). Present ONLY on\n * possibly-committed outcomes ({@link isPossiblyCommittedSendOutcome}).\n */\n transferId?: string;\n\n constructor(message: string, code: SphereErrorCode, cause?: unknown) {\n super(message);\n this.name = 'SphereError';\n this.code = code;\n this.cause = cause;\n }\n}\n\n/**\n * #677: a send that PARTIALLY completed before losing a source to a concurrent\n * transfer. At least one earlier leg certified on-chain and was journaled for\n * delivery (its {@link committedTokenIds}) — that value has irreversibly left\n * the wallet — but a LATER leg raised `TransferConflictError` (a lost race), and\n * `send()` could NOT COMPLETE the {@link remainingAmount} from the remaining live\n * sources. This is the fallback after the internal remainder re-plan gave up —\n * either insufficient funds OR a transient/transport error during the re-plan\n * (the underlying reason is on `cause`), not necessarily an \"out of funds\" state.\n *\n * Distinct from `TransferConflictError` on purpose: a bare conflict makes the\n * caller re-send the FULL amount, paying the already-delivered leg a second\n * time. Catching THIS type (or `code === 'SEND_PARTIALLY_COMPLETED'`) tells the\n * caller/UI: the delivered legs are final, the conflicted intent has been\n * soft-aborted, and only the REMAINDER ({@link remainingAmount}) may be\n * re-planned — under a NEW transferId, never re-sending the whole amount.\n *\n * NOT a subclass of `TransferConflictError` by design: existing conflict\n * handlers that re-send in full must NOT treat this as an ordinary conflict.\n */\nexport class PartialSendConflictError extends SphereError {\n /**\n * The FIRST partial attempt's transferId — its intent was soft-aborted, and it\n * is the §6 handoff anchor. NOTE: `send()` may re-plan the remainder across\n * SEVERAL internal attempts before giving up, so {@link committedTokenIds} can\n * span MULTIPLE transferIds — they do NOT all map to this single id.\n */\n readonly transferId: string;\n /**\n * Source token ids whose spend already certified on-chain (and whose finished\n * output blob is journaled for delivery). Their value has already been\n * delivered; NEVER re-send these. May accumulate across several internal\n * remainder-re-plan attempts (so not all are journaled under {@link transferId}).\n */\n readonly committedTokenIds: readonly string[];\n /**\n * The still-undelivered portion (base units, decimal string) — `send()` could\n * not COMPLETE it from the remaining live sources (insufficient funds or a\n * transient error; see `cause`). Re-plan ONLY this amount, under a new\n * transferId; the delivered legs converge via the recipient's §6 claim.\n */\n readonly remainingAmount: string;\n\n constructor(\n message: string,\n transferId: string,\n committedTokenIds: readonly string[],\n remainingAmount: string,\n cause?: unknown,\n ) {\n super(message, 'SEND_PARTIALLY_COMPLETED', cause);\n this.name = 'PartialSendConflictError';\n this.transferId = transferId;\n this.committedTokenIds = [...committedTokenIds];\n this.remainingAmount = remainingAmount;\n }\n}\n\n/**\n * Error codes for a send outcome where money has (or may have) already left the\n * wallet on-chain, so the send is NOT a clean, re-sendable failure. A consumer\n * that persists a \"paid\" flag (e.g. a payment request) must keep the request\n * NON-payable on any of these — reverting to a re-payable state would double-pay\n * (#441). Covers the post-commit mirror-sync-pending / keep-open certification\n * states AND the partial-conflict outcome.\n */\nconst POSSIBLY_COMMITTED_SEND_CODES: ReadonlySet<SphereErrorCode> = new Set([\n 'SEND_SYNC_PENDING',\n 'CERTIFICATION_UNCONFIRMED',\n 'CHECKPOINT_PERSIST_FAILED',\n 'SPLIT_CHECKPOINT_LOST',\n 'CHECKPOINT_TRUSTBASE_MISMATCH',\n 'SEND_PARTIALLY_COMPLETED',\n]);\n\n/**\n * #441: true when a send failed with an outcome where the payment has (or may\n * have) irreversibly left the wallet — a post-commit sync-pending / keep-open\n * certification state, or a `PartialSendConflictError`. Such a send completes (or\n * has already partially completed) via resume/claim and MUST NOT be re-sent, so\n * a persisted \"paid\" state must stay non-payable. A `false` result is a clean\n * pre-commit failure — nothing left the wallet, safe to re-pay.\n */\nexport function isPossiblyCommittedSendOutcome(err: unknown): boolean {\n return isSphereError(err) && POSSIBLY_COMMITTED_SEND_CODES.has(err.code);\n}\n\n/**\n * Type guard to check if an error is a SphereError\n */\nexport function isSphereError(err: unknown): err is SphereError {\n return err instanceof SphereError;\n}\n","// connect/semver.ts\n// Tiny, dependency-free semver helpers for the Connect compatibility gate.\n\n/** MAJOR component of a semver string (e.g. '2.7.3' -> 2). NaN if unparseable. */\nexport function majorOf(v: string): number {\n return parseInt(String(v).split('.')[0], 10);\n}\n\n/**\n * Compare two semver strings, prerelease-aware. Returns -1 | 0 | 1.\n * A release outranks its own prerelease: compareSemver('1.2.3', '1.2.3-rc.1') === 1.\n */\nexport function compareSemver(a: string, b: string): number {\n const parse = (v: string) => {\n const [core, pre] = String(v).split('-', 2) as [string, string | undefined];\n const nums = core.split('.').map((n) => parseInt(n, 10) || 0);\n return { nums, pre };\n };\n const pa = parse(a);\n const pb = parse(b);\n for (let i = 0; i < 3; i++) {\n const d = (pa.nums[i] ?? 0) - (pb.nums[i] ?? 0);\n if (d !== 0) return d < 0 ? -1 : 1;\n }\n if (pa.pre === undefined && pb.pre === undefined) return 0;\n if (pa.pre === undefined) return 1; // release > prerelease\n if (pb.pre === undefined) return -1;\n const sa = pa.pre.split('.');\n const sb = pb.pre.split('.');\n for (let i = 0; i < Math.max(sa.length, sb.length); i++) {\n const x = sa[i];\n const y = sb[i];\n if (x === undefined) return -1;\n if (y === undefined) return 1;\n const nx = Number(x);\n const ny = Number(y);\n if (!Number.isNaN(nx) && !Number.isNaN(ny)) {\n if (nx !== ny) return nx < ny ? -1 : 1;\n } else if (x !== y) {\n return x < y ? -1 : 1;\n }\n }\n return 0;\n}\n","/**\n * SDK2 Constants\n * Default configuration values and storage keys\n */\n\n// =============================================================================\n// Storage Keys\n// =============================================================================\n\n/** Default prefix for all storage keys */\nexport const STORAGE_PREFIX = 'sphere_' as const;\n\n/**\n * Default encryption key for wallet data\n * WARNING: This is a placeholder. In production, use user-provided password.\n * This key is used when no password is provided to encrypt/decrypt mnemonic.\n */\nexport const DEFAULT_ENCRYPTION_KEY = 'sphere-default-key' as const;\n\n/**\n * Global storage keys (one per wallet, no address index)\n * Final key format: sphere_{key}\n */\nexport const STORAGE_KEYS_GLOBAL = {\n /** Encrypted BIP39 mnemonic */\n MNEMONIC: 'mnemonic',\n /** Encrypted master private key */\n MASTER_KEY: 'master_key',\n /** BIP32 chain code */\n CHAIN_CODE: 'chain_code',\n /** HD derivation path (full path like m/44'/0'/0'/0/0) */\n DERIVATION_PATH: 'derivation_path',\n /** Base derivation path (like m/44'/0'/0' without chain/index) */\n BASE_PATH: 'base_path',\n /** Derivation mode: bip32, wif_hmac, legacy_hmac */\n DERIVATION_MODE: 'derivation_mode',\n /** Wallet source: mnemonic, file, unknown */\n WALLET_SOURCE: 'wallet_source',\n /** Wallet existence flag */\n WALLET_EXISTS: 'wallet_exists',\n /** Current active address index */\n CURRENT_ADDRESS_INDEX: 'current_address_index',\n /** Nametag cache per address (separate from tracked addresses registry) */\n ADDRESS_NAMETAGS: 'address_nametags',\n /** Active addresses registry (JSON: TrackedAddressesStorage) */\n TRACKED_ADDRESSES: 'tracked_addresses',\n /** Last processed Nostr wallet event timestamp (unix seconds), keyed per pubkey */\n LAST_WALLET_EVENT_TS: 'last_wallet_event_ts',\n /** Last processed Nostr DM (gift-wrap) event timestamp (unix seconds), keyed per pubkey */\n LAST_DM_EVENT_TS: 'last_dm_event_ts',\n /** Group chat: last used relay URL (stale data detection) — global, same relay for all addresses */\n GROUP_CHAT_RELAY_URL: 'group_chat_relay_url',\n /** Cached token registry JSON (fetched from remote) */\n TOKEN_REGISTRY_CACHE: 'token_registry_cache',\n /** Timestamp of last token registry cache update (ms since epoch) */\n TOKEN_REGISTRY_CACHE_TS: 'token_registry_cache_ts',\n /** Cached price data JSON (from CoinGecko or other provider) */\n PRICE_CACHE: 'price_cache',\n /** Timestamp of last price cache update (ms since epoch) */\n PRICE_CACHE_TS: 'price_cache_ts',\n} as const;\n\n/**\n * Per-address storage keys (one per derived address)\n * Final key format: sphere_{DIRECT_xxx_yyy}_{key}\n * Example: sphere_DIRECT_abc123_xyz789_pending_transfers\n *\n * Note: Token data (tokens, tombstones, archived, forked) is stored via\n * TokenStorageProvider, not here. This avoids duplication.\n */\nexport const STORAGE_KEYS_ADDRESS = {\n /** Pending transfers for this address */\n PENDING_TRANSFERS: 'pending_transfers',\n /** Transfer outbox for this address */\n OUTBOX: 'outbox',\n /** Conversations for this address */\n CONVERSATIONS: 'conversations',\n /** Messages for this address */\n MESSAGES: 'messages',\n /** Transaction history for this address */\n TRANSACTION_HISTORY: 'transaction_history',\n /** Pending V5 finalization tokens (unconfirmed instant split tokens) */\n PENDING_V5_TOKENS: 'pending_v5_tokens',\n /**\n * FINISHED v2 token blobs awaiting transport delivery. Written the moment a\n * transfer/split output is certified on-chain (the source is already spent),\n * removed after successful delivery — survives transport failures + crashes\n * so the recipient's token is never lost with the process.\n */\n PENDING_V2_DELIVERIES: 'pending_v2_deliveries',\n /** Group chat: joined groups for this address */\n GROUP_CHAT_GROUPS: 'group_chat_groups',\n /** Group chat: messages for this address */\n GROUP_CHAT_MESSAGES: 'group_chat_messages',\n /** Group chat: members for this address */\n GROUP_CHAT_MEMBERS: 'group_chat_members',\n /** Group chat: processed event IDs for deduplication */\n GROUP_CHAT_PROCESSED_EVENTS: 'group_chat_processed_events',\n /** Processed V5 split group IDs for Nostr re-delivery dedup */\n PROCESSED_SPLIT_GROUP_IDS: 'processed_split_group_ids',\n /** Processed V6 combined transfer IDs for Nostr re-delivery dedup */\n PROCESSED_COMBINED_TRANSFER_IDS: 'processed_combined_transfer_ids',\n // Invoice / Accounting storage keys\n /** Set of cancelled invoice IDs (JSON string array) */\n CANCELLED_INVOICES: 'cancelled_invoices',\n /** Set of closed invoice IDs (JSON string array) */\n CLOSED_INVOICES: 'closed_invoices',\n /** Frozen balances for terminated invoices (JSON map: invoiceId → FrozenInvoiceBalances) */\n FROZEN_BALANCES: 'frozen_balances',\n /** Auto-return settings (JSON: AutoReturnSettings) */\n AUTO_RETURN: 'auto_return',\n /** Auto-return dedup ledger (JSON: AutoReturnLedger) */\n AUTO_RETURN_LEDGER: 'auto_return_ledger',\n /** Invoice-transfer index metadata (JSON: Record<invoiceId, { terminated, frozenAt? }>) */\n INV_LEDGER_INDEX: 'inv_ledger_index',\n /** Token scan state watermarks (JSON: Record<tokenId, txCount>) */\n TOKEN_SCAN_STATE: 'token_scan_state',\n // Swap storage keys\n /** Per-swap key: swap:{swapId} */\n SWAP_RECORD_PREFIX: 'swap:',\n /** Lightweight index array for listing */\n SWAP_INDEX: 'swap_index',\n} as const;\n\n/**\n * Per-address keys that are ALSO per-network: token/payment operational state. Mixing these\n * across networks is unsafe (e.g. a testnet2 auto-return ledger firing a real send on mainnet).\n * Chat/identity per-address keys (CONVERSATIONS/MESSAGES/GROUP_CHAT_*) are network-AGNOSTIC and\n * are deliberately NOT listed — they stay per-address only.\n */\nconst NETWORK_SCOPED_ADDRESS_KEYS: readonly string[] = [\n STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS,\n STORAGE_KEYS_ADDRESS.OUTBOX,\n STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY,\n STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS,\n STORAGE_KEYS_ADDRESS.PENDING_V2_DELIVERIES,\n STORAGE_KEYS_ADDRESS.PROCESSED_SPLIT_GROUP_IDS,\n STORAGE_KEYS_ADDRESS.PROCESSED_COMBINED_TRANSFER_IDS,\n STORAGE_KEYS_ADDRESS.CANCELLED_INVOICES,\n STORAGE_KEYS_ADDRESS.CLOSED_INVOICES,\n STORAGE_KEYS_ADDRESS.FROZEN_BALANCES,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN_LEDGER,\n STORAGE_KEYS_ADDRESS.INV_LEDGER_INDEX,\n STORAGE_KEYS_ADDRESS.TOKEN_SCAN_STATE,\n STORAGE_KEYS_ADDRESS.SWAP_INDEX,\n];\n\n/** Composite per-address key prefixes (modules store `{addressId}_{prefix}{id}`) — per-network. */\nconst NETWORK_SCOPED_ADDRESS_PREFIXES: readonly string[] = [\n STORAGE_KEYS_ADDRESS.SWAP_RECORD_PREFIX, // 'swap:'\n 'inv_ledger:', // AccountingModule INV_LEDGER_PREFIX\n];\n\n/**\n * True if a storage key is a per-NETWORK token/payment key (so the storage provider adds the\n * network segment). Handles the bare form ('auto_return_ledger'), the module-built addressId\n * form ('DIRECT_a_b_auto_return_ledger'), and composites ('{addressId}_swap:{id}'). Chat/identity\n * keys return false — they remain per-address, network-agnostic.\n */\nexport function isNetworkScopedAddressKey(key: string): boolean {\n for (const k of NETWORK_SCOPED_ADDRESS_KEYS) {\n if (key === k || key.endsWith(`_${k}`)) return true;\n }\n for (const p of NETWORK_SCOPED_ADDRESS_PREFIXES) {\n if (key.startsWith(p) || key.includes(`_${p}`)) return true;\n }\n return false;\n}\n\n/** @deprecated Use STORAGE_KEYS_GLOBAL and STORAGE_KEYS_ADDRESS instead */\nexport const STORAGE_KEYS = {\n ...STORAGE_KEYS_GLOBAL,\n ...STORAGE_KEYS_ADDRESS,\n} as const;\n\n/**\n * Build a per-address storage key using address identifier\n * @param addressId - Short identifier for the address (e.g., first 8 chars of pubkey hash, or direct address hash)\n * @param key - The key from STORAGE_KEYS_ADDRESS\n * @returns Key in format: \"{addressId}_{key}\" e.g., \"a1b2c3d4_tokens\"\n */\nexport function getAddressStorageKey(addressId: string, key: string): string {\n return `${addressId}_${key}`;\n}\n\n/**\n * Create a readable address identifier from directAddress or chainPubkey\n * Format: DIRECT_first6_last6 (sanitized for filesystem/storage)\n * @param directAddress - The L3 direct address (DIRECT:xxx) or chainPubkey\n * @returns Sanitized identifier like \"DIRECT_abc123_xyz789\"\n */\nexport function getAddressId(directAddress: string): string {\n // Remove DIRECT:// or DIRECT: prefix if present\n let hash = directAddress;\n if (hash.startsWith('DIRECT://')) {\n hash = hash.slice(9);\n } else if (hash.startsWith('DIRECT:')) {\n hash = hash.slice(7);\n }\n // Format: DIRECT_first6_last6 (sanitized)\n const first = hash.slice(0, 6).toLowerCase();\n const last = hash.slice(-6).toLowerCase();\n return `DIRECT_${first}_${last}`;\n}\n\n// =============================================================================\n// Nostr Defaults\n// =============================================================================\n\n/** Default Nostr relays */\nexport const DEFAULT_NOSTR_RELAYS = [\n 'wss://relay.unicity.network',\n 'wss://relay.damus.io',\n 'wss://nos.lol',\n 'wss://relay.nostr.band',\n] as const;\n\n/** Nostr event kinds used by SDK - must match @unicitylabs/nostr-js-sdk */\nexport const NOSTR_EVENT_KINDS = {\n /** NIP-04 encrypted direct message */\n DIRECT_MESSAGE: 4,\n /** Token transfer (Unicity custom - 31113) */\n TOKEN_TRANSFER: 31113,\n /** Payment request (Unicity custom - 31115) */\n PAYMENT_REQUEST: 31115,\n /** Payment request response (Unicity custom - 31116) */\n PAYMENT_REQUEST_RESPONSE: 31116,\n /** Nametag binding (NIP-78 app-specific data) */\n NAMETAG_BINDING: 30078,\n /** Public broadcast */\n BROADCAST: 1,\n} as const;\n\n/**\n * NIP-29 Event Kinds for relay-based group chat\n * https://github.com/nostr-protocol/nips/blob/master/29.md\n */\nexport const NIP29_KINDS = {\n /** Chat message sent to group */\n CHAT_MESSAGE: 9,\n /** Thread root message */\n THREAD_ROOT: 11,\n /** Thread reply message */\n THREAD_REPLY: 12,\n /** User join request */\n JOIN_REQUEST: 9021,\n /** User leave request */\n LEAVE_REQUEST: 9022,\n /** Admin: add/update user */\n PUT_USER: 9000,\n /** Admin: remove user */\n REMOVE_USER: 9001,\n /** Admin: edit group metadata */\n EDIT_METADATA: 9002,\n /** Admin: delete event */\n DELETE_EVENT: 9005,\n /** Admin: create group */\n CREATE_GROUP: 9007,\n /** Admin: delete group */\n DELETE_GROUP: 9008,\n /** Admin: create invite code */\n CREATE_INVITE: 9009,\n /** Relay-signed group metadata */\n GROUP_METADATA: 39000,\n /** Relay-signed group admins */\n GROUP_ADMINS: 39001,\n /** Relay-signed group members */\n GROUP_MEMBERS: 39002,\n /** Relay-signed group roles */\n GROUP_ROLES: 39003,\n} as const;\n\n// =============================================================================\n// Aggregator (Oracle) Defaults\n// =============================================================================\n\n/**\n * Default aggregator URL\n * Note: The aggregator is conceptually an oracle - a trusted service that provides\n * verifiable truth about token state through cryptographic inclusion proofs.\n */\nexport const DEFAULT_AGGREGATOR_URL = 'https://aggregator.unicity.network/rpc' as const;\n\n/** Dev aggregator URL */\nexport const DEV_AGGREGATOR_URL = 'https://dev-aggregator.dyndns.org/rpc' as const;\n\n/** Test aggregator URL (Goggregator) */\nexport const TEST_AGGREGATOR_URL = 'https://goggregator-test.unicity.network' as const;\n\n/** Default aggregator request timeout (ms) */\nexport const DEFAULT_AGGREGATOR_TIMEOUT = 30000;\n\n\n// =============================================================================\n// IPFS Defaults\n// =============================================================================\n\n/** Default IPFS gateways */\nexport const DEFAULT_IPFS_GATEWAYS = [\n 'https://unicity-ipfs1.dyndns.org',\n] as const;\n\n/** Unicity IPFS bootstrap peers */\nexport const DEFAULT_IPFS_BOOTSTRAP_PEERS = [\n '/dns4/unicity-ipfs2.dyndns.org/tcp/4001/p2p/12D3KooWLNi5NDPPHbrfJakAQqwBqymYTTwMQXQKEWuCrJNDdmfh',\n '/dns4/unicity-ipfs3.dyndns.org/tcp/4001/p2p/12D3KooWQ4aujVE4ShLjdusNZBdffq3TbzrwT2DuWZY9H1Gxhwn6',\n '/dns4/unicity-ipfs4.dyndns.org/tcp/4001/p2p/12D3KooWJ1ByPfUzUrpYvgxKU8NZrR8i6PU1tUgMEbQX9Hh2DEn1',\n '/dns4/unicity-ipfs5.dyndns.org/tcp/4001/p2p/12D3KooWB1MdZZGHN5B8TvWXntbycfe7Cjcz7n6eZ9eykZadvmDv',\n] as const;\n\n/** Unicity dedicated IPFS nodes (HTTP API access) */\nexport const UNICITY_IPFS_NODES = [\n {\n host: 'unicity-ipfs1.dyndns.org',\n peerId: '12D3KooWDKJqEMAhH4nsSSiKtK1VLcas5coUqSPZAfbWbZpxtL4u',\n httpPort: 9080,\n httpsPort: 443,\n },\n] as const;\n\n/**\n * Get IPFS gateway URLs for HTTP API access.\n * @param isSecure - Use HTTPS (default: true). Set false for development.\n */\nexport function getIpfsGatewayUrls(isSecure?: boolean): string[] {\n return UNICITY_IPFS_NODES.map((node) =>\n isSecure !== false\n ? `https://${node.host}`\n : `http://${node.host}:${node.httpPort}`,\n );\n}\n\n// =============================================================================\n// Wallet Defaults\n// =============================================================================\n\n/** Default BIP32 base path (without chain/index) */\nexport const DEFAULT_BASE_PATH = \"m/44'/0'/0'\" as const;\n\n/** Default BIP32 derivation path (full path with chain/index) */\nexport const DEFAULT_DERIVATION_PATH = `${DEFAULT_BASE_PATH}/0/0` as const;\n\n/** Coin types */\nexport const COIN_TYPES = {\n /** Test token */\n TEST: 'TEST',\n} as const;\n\n// =============================================================================\n// Token Registry Defaults\n// =============================================================================\n\n/** Remote token registry URL (GitHub raw) */\nexport const TOKEN_REGISTRY_URL =\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json' as const;\n\n/** Default token registry refresh interval (ms) — 1 hour */\nexport const TOKEN_REGISTRY_REFRESH_INTERVAL = 3_600_000;\n\n// =============================================================================\n// Network Defaults\n// =============================================================================\n\n/** Testnet Nostr relays */\nexport const TEST_NOSTR_RELAYS = [\n 'wss://nostr-relay.testnet.unicity.network',\n] as const;\n\n/** Default group chat relays (NIP-29 Zooid relay) */\nexport const DEFAULT_GROUP_RELAYS = [\n 'wss://sphere-relay.unicity.network',\n] as const;\n\n/**\n * Complete configuration for one network. Every field is required, so adding a\n * network (or a field) that is missing any of these is a COMPILE error at the\n * NETWORKS literal below (via `satisfies`) — a half-configured network can never\n * be defined silently.\n */\nexport interface NetworkConfig {\n readonly name: string;\n readonly aggregatorUrl: string;\n readonly nostrRelays: readonly string[];\n readonly ipfsGateways: readonly string[];\n readonly groupRelays: readonly string[];\n readonly tokenRegistryUrl: string;\n /** Canonical numeric network id (= RootTrustBase.networkId; testnet2 = 4).\n * Optional: only set for networks that have a live v2 trust base. */\n readonly networkId?: number;\n}\n\n/** Network configurations */\nexport const NETWORKS = {\n mainnet: {\n name: 'Mainnet',\n aggregatorUrl: DEFAULT_AGGREGATOR_URL,\n nostrRelays: DEFAULT_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl: TOKEN_REGISTRY_URL,\n },\n // v1 cutover: 'testnet' now POINTS AT TESTNET2 (the v2 gateway network). The\n // old goggregator testnet spoke the removed v1 protocol — a v2 engine cannot\n // run against it. 'testnet2' stays as an alias of the same configuration.\n testnet: {\n name: 'Testnet2',\n networkId: 4,\n // v2 state-transition gateway (networkId 4 comes from the trust base). apiKey is env-injected.\n aggregatorUrl: 'https://gateway.testnet2.unicity.network',\n nostrRelays: TEST_NOSTR_RELAYS, // reuse testnet infra (shared relays/ipfs)\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n },\n testnet2: {\n name: 'Testnet2',\n networkId: 4,\n // v2 state-transition gateway (networkId 4 comes from the trust base). apiKey is env-injected.\n aggregatorUrl: 'https://gateway.testnet2.unicity.network',\n nostrRelays: TEST_NOSTR_RELAYS, // reuse testnet infra (shared relays/ipfs)\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n },\n // NOTE: mainnet/dev still point at v1-era aggregators. The v2 engine cannot\n // operate against them until their gateways are cut over to the v2 protocol —\n // wallet operations on these networks fail loudly (AGGREGATOR_ERROR) until then.\n dev: {\n name: 'Development',\n aggregatorUrl: DEV_AGGREGATOR_URL,\n nostrRelays: TEST_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl: TOKEN_REGISTRY_URL,\n },\n} as const satisfies Record<string, NetworkConfig>;\n\nexport type NetworkType = keyof typeof NETWORKS;\n\n/**\n * A network descriptor — the canonical way to identify a Unicity network across\n * the SDK and the Connect protocol. `id` is the canonical key (= RootTrustBase\n * networkId, analogous to an EIP-155 chainId); `name` is human-readable metadata.\n * Richer fields (gateway/symbol/explorer/icon) and switch/add-network are deferred.\n */\nexport interface NetworkInfo {\n readonly id: number;\n readonly name?: string;\n}\n\n/**\n * Registry of known networks for dApps and wallets. Single-sourced from NETWORKS\n * so it cannot drift. Use as `network: SPHERE_NETWORKS.testnet2`. Custom networks\n * are the same shape: `network: { id, name }`. Only live v2 networks appear here;\n * the legacy `testnet` alias is intentionally not surfaced.\n */\nexport const SPHERE_NETWORKS = {\n testnet2: { id: NETWORKS.testnet2.networkId as number, name: 'testnet2' },\n} as const satisfies Record<string, NetworkInfo>;\n\n// =============================================================================\n// Timeouts & Limits\n// =============================================================================\n\n/** Default timeouts (ms) */\nexport const TIMEOUTS = {\n /** WebSocket connection timeout */\n WEBSOCKET_CONNECT: 10000,\n /** Nostr relay reconnect delay */\n NOSTR_RECONNECT_DELAY: 3000,\n /** Max reconnect attempts */\n MAX_RECONNECT_ATTEMPTS: 5,\n /** Proof polling interval */\n PROOF_POLL_INTERVAL: 1000,\n /** Sync interval */\n SYNC_INTERVAL: 60000,\n} as const;\n\n// =============================================================================\n// Sphere Connect\n// =============================================================================\n\n/** Signal sent by wallet popup to dApp when ConnectHost is ready */\nexport const HOST_READY_TYPE = 'sphere-connect:host-ready' as const;\n\n/** Default timeout (ms) for waiting for the host-ready signal */\nexport const HOST_READY_TIMEOUT = 30_000;\n\n/** Validation limits */\nexport const INVOICE_TOKEN_TYPE_HEX = '14676a280bda4275baf865b67cd4c611bcd58c9bf8226d508acaa10a8fcaccc6' as const;\n\nexport const LIMITS = {\n /** Min nametag length */\n NAMETAG_MIN_LENGTH: 3,\n /** Max nametag length */\n NAMETAG_MAX_LENGTH: 20,\n /** Max memo length */\n MEMO_MAX_LENGTH: 500,\n /** Max message length */\n MESSAGE_MAX_LENGTH: 10000,\n} as const;\n","/**\n * Sphere Connect Protocol\n * JSON-RPC-like message types for wallet ↔ dApp communication.\n */\n\nimport { majorOf } from './semver';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nexport const SPHERE_CONNECT_NAMESPACE = 'sphere-connect';\nexport const SPHERE_CONNECT_VERSION = '2.0'; // Connect protocol version (semver MAJOR.MINOR)\n\nexport { HOST_READY_TYPE, HOST_READY_TIMEOUT, SPHERE_NETWORKS } from '../constants';\n// Import for local use (e.g. SphereHandshake.network) AND re-export for connect consumers.\n// A bare `export type { NetworkInfo } from '../constants'` would re-export without bringing\n// the name into this module's scope, breaking the local references (TS2304).\nimport type { NetworkInfo } from '../constants';\nexport type { NetworkInfo };\n\n// =============================================================================\n// RPC Method Names (query — return data, no UI)\n// =============================================================================\n\nexport const RPC_METHODS = {\n GET_IDENTITY: 'sphere_getIdentity',\n GET_BALANCE: 'sphere_getBalance',\n GET_ASSETS: 'sphere_getAssets',\n GET_FIAT_BALANCE: 'sphere_getFiatBalance',\n GET_TOKENS: 'sphere_getTokens',\n GET_HISTORY: 'sphere_getHistory',\n RESOLVE: 'sphere_resolve',\n SUBSCRIBE: 'sphere_subscribe',\n UNSUBSCRIBE: 'sphere_unsubscribe',\n DISCONNECT: 'sphere_disconnect',\n GET_CONVERSATIONS: 'sphere_getConversations',\n GET_MESSAGES: 'sphere_getMessages',\n GET_DM_UNREAD_COUNT: 'sphere_getDMUnreadCount',\n MARK_AS_READ: 'sphere_markAsRead',\n GET_INVOICES: 'sphere_getInvoices',\n GET_INVOICE_STATUS: 'sphere_getInvoiceStatus',\n} as const;\n\nexport type RpcMethod = (typeof RPC_METHODS)[keyof typeof RPC_METHODS];\n\n// =============================================================================\n// Intent Action Names (open wallet UI, require user confirmation)\n// =============================================================================\n\nexport const INTENT_ACTIONS = {\n SEND: 'send',\n DM: 'dm',\n PAYMENT_REQUEST: 'payment_request',\n RECEIVE: 'receive',\n SIGN_MESSAGE: 'sign_message',\n CREATE_INVOICE: 'create_invoice',\n CLOSE_INVOICE: 'close_invoice',\n CANCEL_INVOICE: 'cancel_invoice',\n PAY_INVOICE: 'pay_invoice',\n RETURN_INVOICE_PAYMENT: 'return_invoice_payment',\n IMPORT_INVOICE: 'import_invoice',\n SEND_INVOICE_RECEIPTS: 'send_invoice_receipts',\n SEND_CANCELLATION_NOTICES: 'send_cancellation_notices',\n SET_AUTO_RETURN: 'set_auto_return',\n MINT: 'mint',\n} as const;\n\nexport type IntentAction = (typeof INTENT_ACTIONS)[keyof typeof INTENT_ACTIONS];\n\n// =============================================================================\n// Error Codes\n// =============================================================================\n\nexport const ERROR_CODES = {\n // Standard JSON-RPC\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n\n // Sphere Connect (4xxx)\n NOT_CONNECTED: 4001,\n PERMISSION_DENIED: 4002,\n USER_REJECTED: 4003,\n SESSION_EXPIRED: 4004,\n ORIGIN_BLOCKED: 4005,\n RATE_LIMITED: 4006,\n UNSUPPORTED_PROTOCOL_VERSION: 4007, // Connect MAJOR mismatch (incompatible era)\n INCOMPATIBLE_NETWORK: 4008, // dApp targets a different network than the wallet\n INSUFFICIENT_BALANCE: 4100,\n INVALID_RECIPIENT: 4101,\n TRANSFER_FAILED: 4102,\n INTENT_CANCELLED: 4200,\n} as const;\n\nexport type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];\n\n// =============================================================================\n// Message Types\n// =============================================================================\n\ninterface SphereMessageBase {\n readonly ns: typeof SPHERE_CONNECT_NAMESPACE;\n readonly v: typeof SPHERE_CONNECT_VERSION;\n}\n\n/** Query request: dApp → Wallet */\nexport interface SphereRpcRequest extends SphereMessageBase {\n readonly type: 'request';\n readonly id: string;\n readonly method: string;\n readonly params?: Record<string, unknown>;\n}\n\n/** Query response: Wallet → dApp */\nexport interface SphereRpcResponse extends SphereMessageBase {\n readonly type: 'response';\n readonly id: string;\n readonly result?: unknown;\n readonly error?: SphereRpcError;\n}\n\n/** Intent request: dApp → Wallet (opens wallet UI) */\nexport interface SphereIntentRequest extends SphereMessageBase {\n readonly type: 'intent';\n readonly id: string;\n readonly action: string;\n readonly params: Record<string, unknown>;\n}\n\n/** Intent result: Wallet → dApp (after user action) */\nexport interface SphereIntentResult extends SphereMessageBase {\n readonly type: 'intent_result';\n readonly id: string;\n readonly result?: unknown;\n readonly error?: SphereRpcError;\n}\n\n/** Event push: Wallet → dApp (unsolicited) */\nexport interface SphereEventMessage extends SphereMessageBase {\n readonly type: 'event';\n readonly event: string;\n readonly data: unknown;\n}\n\n/** Handshake: bidirectional */\nexport interface SphereHandshake extends SphereMessageBase {\n readonly type: 'handshake';\n readonly direction: 'request' | 'response';\n readonly permissions: string[];\n readonly dapp?: DAppMetadata;\n readonly sessionId?: string;\n readonly identity?: PublicIdentity;\n /** If true, wallet must NOT open any approval UI. Immediately reject if origin is not already approved. */\n readonly silent?: boolean;\n /** request: dApp target network; response: wallet active network */\n readonly network?: NetworkInfo;\n /** Informational: the dApp's npm SDK version. */\n readonly sdkVersion?: string;\n /** Response: structured rejection reason when the gate refuses the connection. */\n readonly error?: SphereRpcError;\n /** Response: non-fatal deprecation notice (does not block the connection). */\n readonly warning?: SphereRpcError;\n}\n\nexport interface SphereRpcError {\n readonly code: number;\n readonly message: string;\n readonly data?: unknown;\n}\n\nexport type SphereConnectMessage =\n | SphereRpcRequest\n | SphereRpcResponse\n | SphereIntentRequest\n | SphereIntentResult\n | SphereEventMessage\n | SphereHandshake;\n\n// =============================================================================\n// Shared Types\n// =============================================================================\n\nexport interface DAppMetadata {\n readonly name: string;\n readonly description?: string;\n readonly icon?: string;\n readonly url: string;\n}\n\nexport interface PublicIdentity {\n readonly chainPubkey: string;\n readonly directAddress?: string;\n readonly nametag?: string;\n}\n\n// =============================================================================\n// Wallet-initiated Events (pushed automatically by host, no subscription needed)\n// =============================================================================\n\n/**\n * Events that ConnectHost pushes proactively to connected dApps.\n * dApps can listen with client.on(WALLET_EVENTS.LOCKED, handler) etc.\n * No sphere_subscribe call needed — host sends these unconditionally.\n */\nexport const WALLET_EVENTS = {\n /** Wallet locked or user logged out. dApp shows locked state and waits for unlock.\n * Pushed automatically by ConnectHost — no sphere_subscribe needed. */\n LOCKED: 'wallet:locked',\n /** Active wallet address changed. dApp should update displayed identity.\n * Pushed automatically by ConnectHost — no sphere_subscribe needed. */\n IDENTITY_CHANGED: 'identity:changed',\n} as const;\n\nexport type WalletEvent = (typeof WALLET_EVENTS)[keyof typeof WALLET_EVENTS];\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/** Check if a message belongs to the Sphere Connect protocol.\n * Handshakes are accepted at ANY version (the version decision happens in the\n * handshake handler so an incompatible peer gets a clean typed error instead of a\n * silently-dropped message → timeout). All other (session) traffic must share the\n * same MAJOR (so MINOR versions interoperate, e.g. 2.0 ↔ 2.1). */\nexport function isSphereConnectMessage(msg: unknown): msg is SphereConnectMessage {\n if (!msg || typeof msg !== 'object') return false;\n const m = msg as Record<string, unknown>;\n if (m.ns !== SPHERE_CONNECT_NAMESPACE) return false;\n if (m.type === 'handshake') return true;\n if (typeof m.v !== 'string') return false;\n return majorOf(m.v) === majorOf(SPHERE_CONNECT_VERSION);\n}\n\n/** Create a unique request ID */\nexport function createRequestId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID\n return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;\n}\n","// connect/compatibility.ts — pure, dependency-free compatibility decision for the Connect gate.\nimport { ERROR_CODES } from './protocol';\nimport type { SphereRpcError, NetworkInfo } from './protocol';\nimport { majorOf, compareSemver } from './semver';\n\nexport type IncompatibleReason = 'protocol_incompatible' | 'network_incompatible';\n\nexport interface CompatInput {\n clientProtocol: string; // inbound msg.v\n walletProtocol: string; // SPHERE_CONNECT_VERSION\n clientNetwork: NetworkInfo | undefined; // inbound msg.network\n walletNetworkId: number; // wallet active network\n minMinor?: number; // optional MINOR floor within the MAJOR\n clientSdkVersion?: string; // optional secondary npm floor\n minSdkVersion?: string;\n}\n\nexport type CompatResult = { ok: true } | { ok: false; error: SphereRpcError };\n\nfunction fail(\n code: number,\n reason: IncompatibleReason,\n message: string,\n extra: Record<string, unknown>,\n): CompatResult {\n return { ok: false, error: { code, message, data: { reason, ...extra } } };\n}\n\n/**\n * Decide whether a connecting peer is compatible. Runs four ordered checks\n * (protocol MAJOR → optional MINOR floor → optional SDK floor → network) and\n * returns {ok:true} or {ok:false, error}. A malformed clientProtocol (NaN MAJOR)\n * is treated as protocol-incompatible.\n */\nexport function checkCompatibility(input: CompatInput): CompatResult {\n const { clientProtocol, walletProtocol, minMinor, clientSdkVersion, minSdkVersion } = input;\n\n // 1. Protocol MAJOR must match.\n if (majorOf(clientProtocol) !== majorOf(walletProtocol)) {\n return fail(ERROR_CODES.UNSUPPORTED_PROTOCOL_VERSION, 'protocol_incompatible',\n 'Incompatible Connect protocol version', { walletProtocol, clientProtocol });\n }\n\n const walletMajor = majorOf(walletProtocol);\n\n // 2. Optional MINOR floor within the MAJOR.\n if (minMinor !== undefined && compareSemver(clientProtocol, `${walletMajor}.${minMinor}`) < 0) {\n return fail(ERROR_CODES.UNSUPPORTED_PROTOCOL_VERSION, 'protocol_incompatible',\n 'Connect protocol below the required minimum', { walletProtocol, clientProtocol });\n }\n\n // 3. Optional secondary npm-SDK floor (rarely used).\n if (minSdkVersion !== undefined && (!clientSdkVersion || compareSemver(clientSdkVersion, minSdkVersion) < 0)) {\n return fail(ERROR_CODES.UNSUPPORTED_PROTOCOL_VERSION, 'protocol_incompatible',\n 'SDK version below the required minimum', { walletProtocol, clientProtocol, requiredSdk: minSdkVersion, actualSdk: clientSdkVersion ?? null });\n }\n\n // 4. Network must match (a missing network is treated as a mismatch).\n if (!input.clientNetwork || input.clientNetwork.id !== input.walletNetworkId) {\n return fail(ERROR_CODES.INCOMPATIBLE_NETWORK, 'network_incompatible',\n 'dApp targets a different network than the wallet',\n { walletNetwork: { id: input.walletNetworkId }, clientNetwork: input.clientNetwork ?? null });\n }\n\n return { ok: true };\n}\n\n","// AUTO-GENERATED by scripts/gen-version.mjs — do not edit.\nexport const SDK_VERSION = '0.11.15-dev.2';\n","/**\n * Sphere Connect Permission System\n * Defines scopes, maps methods/intents to required permissions.\n */\n\nimport { RPC_METHODS, INTENT_ACTIONS } from './protocol';\n\n// =============================================================================\n// Permission Scopes\n// =============================================================================\n\nexport const PERMISSION_SCOPES = {\n IDENTITY_READ: 'identity:read',\n BALANCE_READ: 'balance:read',\n TOKENS_READ: 'tokens:read',\n HISTORY_READ: 'history:read',\n EVENTS_SUBSCRIBE: 'events:subscribe',\n RESOLVE_PEER: 'resolve:peer',\n TRANSFER_REQUEST: 'transfer:request',\n DM_REQUEST: 'dm:request',\n DM_READ: 'dm:read',\n DM_MANAGE: 'dm:manage',\n PAYMENT_REQUEST: 'payment:request',\n SIGN_REQUEST: 'sign:request',\n MINT_REQUEST: 'mint:request',\n INVOICE_READ: 'invoice:read',\n INVOICE_WRITE: 'invoice:write',\n} as const;\n\nexport type PermissionScope = (typeof PERMISSION_SCOPES)[keyof typeof PERMISSION_SCOPES];\n\n/** All available permission scopes */\nexport const ALL_PERMISSIONS: readonly PermissionScope[] = Object.values(PERMISSION_SCOPES);\n\n/** Permissions always granted on connect */\nexport const DEFAULT_PERMISSIONS: readonly PermissionScope[] = [\n PERMISSION_SCOPES.IDENTITY_READ,\n];\n\n// =============================================================================\n// Method → Permission Mapping\n// =============================================================================\n\nexport const METHOD_PERMISSIONS: Record<string, PermissionScope> = {\n [RPC_METHODS.GET_IDENTITY]: PERMISSION_SCOPES.IDENTITY_READ,\n [RPC_METHODS.GET_BALANCE]: PERMISSION_SCOPES.BALANCE_READ,\n [RPC_METHODS.GET_ASSETS]: PERMISSION_SCOPES.BALANCE_READ,\n [RPC_METHODS.GET_FIAT_BALANCE]: PERMISSION_SCOPES.BALANCE_READ,\n [RPC_METHODS.GET_TOKENS]: PERMISSION_SCOPES.TOKENS_READ,\n [RPC_METHODS.GET_HISTORY]: PERMISSION_SCOPES.HISTORY_READ,\n [RPC_METHODS.RESOLVE]: PERMISSION_SCOPES.RESOLVE_PEER,\n [RPC_METHODS.SUBSCRIBE]: PERMISSION_SCOPES.EVENTS_SUBSCRIBE,\n [RPC_METHODS.UNSUBSCRIBE]: PERMISSION_SCOPES.EVENTS_SUBSCRIBE,\n [RPC_METHODS.GET_CONVERSATIONS]: PERMISSION_SCOPES.DM_READ,\n [RPC_METHODS.GET_MESSAGES]: PERMISSION_SCOPES.DM_READ,\n [RPC_METHODS.GET_DM_UNREAD_COUNT]: PERMISSION_SCOPES.DM_READ,\n [RPC_METHODS.MARK_AS_READ]: PERMISSION_SCOPES.DM_MANAGE,\n [RPC_METHODS.GET_INVOICES]: PERMISSION_SCOPES.INVOICE_READ,\n [RPC_METHODS.GET_INVOICE_STATUS]: PERMISSION_SCOPES.INVOICE_READ,\n};\n\n// =============================================================================\n// Intent → Permission Mapping\n// =============================================================================\n\nexport const INTENT_PERMISSIONS: Record<string, PermissionScope> = {\n [INTENT_ACTIONS.SEND]: PERMISSION_SCOPES.TRANSFER_REQUEST,\n [INTENT_ACTIONS.DM]: PERMISSION_SCOPES.DM_REQUEST,\n [INTENT_ACTIONS.PAYMENT_REQUEST]: PERMISSION_SCOPES.PAYMENT_REQUEST,\n [INTENT_ACTIONS.RECEIVE]: PERMISSION_SCOPES.IDENTITY_READ,\n [INTENT_ACTIONS.SIGN_MESSAGE]: PERMISSION_SCOPES.SIGN_REQUEST,\n [INTENT_ACTIONS.CREATE_INVOICE]: PERMISSION_SCOPES.INVOICE_WRITE,\n [INTENT_ACTIONS.CLOSE_INVOICE]: PERMISSION_SCOPES.INVOICE_WRITE,\n [INTENT_ACTIONS.CANCEL_INVOICE]: PERMISSION_SCOPES.INVOICE_WRITE,\n [INTENT_ACTIONS.PAY_INVOICE]: PERMISSION_SCOPES.TRANSFER_REQUEST,\n [INTENT_ACTIONS.RETURN_INVOICE_PAYMENT]: PERMISSION_SCOPES.TRANSFER_REQUEST,\n [INTENT_ACTIONS.IMPORT_INVOICE]: PERMISSION_SCOPES.INVOICE_WRITE,\n [INTENT_ACTIONS.SEND_INVOICE_RECEIPTS]: PERMISSION_SCOPES.INVOICE_WRITE,\n [INTENT_ACTIONS.SEND_CANCELLATION_NOTICES]: PERMISSION_SCOPES.INVOICE_WRITE,\n [INTENT_ACTIONS.SET_AUTO_RETURN]: PERMISSION_SCOPES.INVOICE_WRITE,\n [INTENT_ACTIONS.MINT]: PERMISSION_SCOPES.MINT_REQUEST,\n};\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/** Check if granted permissions allow calling a method */\nexport function hasMethodPermission(granted: ReadonlySet<string>, method: string): boolean {\n const required = METHOD_PERMISSIONS[method];\n if (!required) return false;\n return granted.has(required);\n}\n\n/** Check if granted permissions allow an intent action */\nexport function hasIntentPermission(granted: ReadonlySet<string>, action: string): boolean {\n const required = INTENT_PERMISSIONS[action];\n if (!required) return false;\n return granted.has(required);\n}\n\n/** Validate that all requested permissions are known scopes */\nexport function validatePermissions(permissions: string[]): permissions is PermissionScope[] {\n const validScopes = new Set<string>(ALL_PERMISSIONS);\n return permissions.every((p) => validScopes.has(p));\n}\n","/**\n * ConnectHost — Wallet side of Sphere Connect.\n *\n * Wraps a Sphere instance and exposes its API through a ConnectTransport.\n * Handles permission checking, rate limiting, session management,\n * and delegates intents to the wallet app via callbacks.\n */\n\nimport { logger } from '../../core/logger';\nimport { SphereError } from '../../core/errors';\nimport type { SphereEventType, SphereEventHandler } from '../../types';\nimport type { ConnectTransport, ConnectSession, ConnectHostConfig } from '../types';\nimport type {\n SphereConnectMessage,\n SphereRpcRequest,\n SphereIntentRequest,\n SphereHandshake,\n PublicIdentity,\n SphereRpcError,\n NetworkInfo,\n} from '../protocol';\nimport {\n SPHERE_CONNECT_NAMESPACE,\n SPHERE_CONNECT_VERSION,\n RPC_METHODS,\n ERROR_CODES,\n WALLET_EVENTS,\n createRequestId,\n} from '../protocol';\nimport { checkCompatibility } from '../compatibility';\nimport { SDK_VERSION } from '../version';\nimport {\n DEFAULT_PERMISSIONS,\n hasMethodPermission,\n hasIntentPermission,\n} from '../permissions';\nimport type { PermissionScope } from '../permissions';\n\n// Use a minimal interface for the Sphere dependency to avoid circular imports.\n// ConnectHost only needs these public methods from Sphere.\ninterface SphereInstance {\n readonly identity: { chainPubkey: string; directAddress?: string; nametag?: string } | null;\n readonly networkId?: number;\n readonly payments: {\n getBalance(coinId?: string): unknown[];\n getAssets(coinId?: string): Promise<unknown[]>;\n getFiatBalance(): Promise<number | null>;\n getTokens(filter?: { coinId?: string }): unknown[];\n getHistory(): unknown[];\n };\n signMessage(message: string): string;\n resolve(identifier: string): Promise<unknown>;\n on<T extends SphereEventType>(type: T, handler: SphereEventHandler<T>): () => void;\n readonly communications?: {\n getConversations(): Map<string, ConnectDirectMessage[]>;\n getConversationPage(\n peerPubkey: string,\n options?: { limit?: number; before?: number },\n ): { messages: ConnectDirectMessage[]; hasMore: boolean; oldestTimestamp: number | null };\n getUnreadCount(peerPubkey?: string): number;\n markAsRead(messageIds: string[]): Promise<void>;\n sendDM(recipient: string, content: string): Promise<ConnectDirectMessage>;\n resolvePeerNametag(peerPubkey: string): Promise<string | undefined>;\n };\n readonly accounting?: {\n getInvoices(options?: unknown): unknown[];\n getInvoiceStatus(invoiceId: string): unknown;\n } | null;\n}\n\n/** Minimal DM type to avoid circular imports with Sphere core types. */\ninterface ConnectDirectMessage {\n readonly id: string;\n readonly senderPubkey: string;\n readonly senderNametag?: string;\n readonly recipientPubkey: string;\n readonly recipientNametag?: string;\n readonly content: string;\n readonly timestamp: number;\n isRead: boolean;\n}\n\nconst DEFAULT_SESSION_TTL_MS = 86400000; // 24 hours\nconst DEFAULT_MAX_RPS = 20;\n\nexport class ConnectHost {\n private sphere: SphereInstance;\n private readonly transport: ConnectTransport;\n private readonly config: ConnectHostConfig;\n\n private session: ConnectSession | null = null;\n private grantedPermissions: Set<string> = new Set();\n\n // Event subscription management\n private eventSubscriptions: Map<string, () => void> = new Map(); // eventName → unsub\n\n // Intent auto-approve: action → handler that bypasses wallet UI\n private autoApprovedIntents = new Map<\n string,\n (action: string, params: Record<string, unknown>, session: ConnectSession) => Promise<{ result?: unknown; error?: { code: number; message: string } }>\n >();\n\n // Rate limiting\n private rateLimitCounter = 0;\n private rateLimitResetAt = 0;\n\n private unsubscribeTransport: (() => void) | null = null;\n\n constructor(config: ConnectHostConfig) {\n this.sphere = config.sphere as SphereInstance;\n this.transport = config.transport;\n this.config = config;\n\n this.unsubscribeTransport = this.transport.onMessage(this.handleMessage.bind(this));\n }\n\n /** Get current active session */\n getSession(): ConnectSession | null {\n return this.session;\n }\n\n /** Register an auto-approve handler for an intent action (session-scoped). */\n setIntentAutoApprove(\n action: string,\n handler: (\n action: string,\n params: Record<string, unknown>,\n session: ConnectSession,\n ) => Promise<{ result?: unknown; error?: { code: number; message: string } }>,\n ): void {\n this.autoApprovedIntents.set(action, handler);\n }\n\n /** Remove auto-approve for an intent action. */\n clearIntentAutoApprove(action: string): void {\n this.autoApprovedIntents.delete(action);\n }\n\n /**\n * Update the Sphere instance (e.g. user switched address — new Sphere created).\n * Re-subscribes auto-push events and notifies connected dApp of the new identity.\n */\n updateSphere(newSphere: unknown): void {\n this.sphere = newSphere as SphereInstance;\n\n // Re-subscribe identity:changed on the new Sphere instance\n const existing = this.eventSubscriptions.get(WALLET_EVENTS.IDENTITY_CHANGED);\n if (existing) {\n existing();\n this.eventSubscriptions.delete(WALLET_EVENTS.IDENTITY_CHANGED);\n }\n if (this.session?.active) {\n this.autoSubscribeIdentityChanged();\n // Push the new identity immediately so dApp doesn't have to wait for the next event\n const identity = this.getPublicIdentity();\n if (identity) {\n this.pushClientEvent(WALLET_EVENTS.IDENTITY_CHANGED, identity);\n }\n }\n }\n\n /** Revoke the current session */\n revokeSession(): void {\n if (this.session) {\n this.session.active = false;\n this.cleanupEventSubscriptions();\n this.autoApprovedIntents.clear();\n this.session = null;\n this.grantedPermissions.clear();\n }\n }\n\n /**\n * Notify connected dApp that wallet is locked/logged out, then revoke session.\n * Call this BEFORE destroy() when the wallet locks so the dApp gets a clean signal\n * instead of receiving NOT_CONNECTED errors on the next request.\n */\n notifyWalletLocked(): void {\n if (this.session?.active) {\n this.pushClientEvent(WALLET_EVENTS.LOCKED, {});\n }\n this.revokeSession();\n }\n\n /** Destroy the host, clean up all resources */\n destroy(): void {\n this.revokeSession();\n if (this.unsubscribeTransport) {\n this.unsubscribeTransport();\n this.unsubscribeTransport = null;\n }\n }\n\n // ===========================================================================\n // Message Handling\n // ===========================================================================\n\n private async handleMessage(msg: SphereConnectMessage): Promise<void> {\n try {\n if (msg.type === 'handshake' && msg.direction === 'request') {\n await this.handleHandshake(msg);\n return;\n }\n\n if (msg.type === 'request') {\n await this.handleRpcRequest(msg);\n return;\n }\n\n if (msg.type === 'intent') {\n await this.handleIntentRequest(msg);\n return;\n }\n } catch (error) {\n // Swallow errors from malformed messages\n logger.warn('ConnectHost', 'Error handling message:', error);\n }\n }\n\n // ===========================================================================\n // Handshake\n // ===========================================================================\n\n private async handleHandshake(msg: SphereHandshake): Promise<void> {\n const dapp = msg.dapp;\n // A handshake without dapp metadata is malformed — deny silently (no session).\n // The compatibility gate and onConnectionRejected are intentionally not consulted\n // here: there is no app to gate or to surface a rejection reason for.\n if (!dapp) {\n this.sendHandshakeResponse([], undefined, undefined);\n return;\n }\n\n // Compatibility gate — runs BEFORE resume and BEFORE onConnectionRequest, so an\n // incompatible/old client cannot slip through on a stale sessionId.\n const result = checkCompatibility({\n clientProtocol: msg.v,\n walletProtocol: SPHERE_CONNECT_VERSION,\n clientNetwork: msg.network,\n walletNetworkId: this.sphere.networkId ?? -1,\n minMinor: this.config.minMinorVersion,\n clientSdkVersion: msg.sdkVersion,\n minSdkVersion: this.config.minSdkVersion,\n });\n if (!result.ok) {\n logger.warn('ConnectHost', 'Rejected handshake', {\n dapp: dapp.name,\n reason: (result.error.data as { reason?: string } | undefined)?.reason,\n clientProtocol: msg.v,\n walletProtocol: SPHERE_CONNECT_VERSION,\n clientNetwork: msg.network ?? null,\n walletNetwork: this.sphere.networkId ?? null,\n });\n this.config.onConnectionRejected?.(dapp, result.error, !!msg.silent);\n this.sendHandshakeResponse([], undefined, undefined, result.error, msg.v);\n return;\n }\n\n const clientInfo = { protocolVersion: msg.v, network: msg.network, sdkVersion: msg.sdkVersion };\n\n // Session resumption: if the client presents a valid existing sessionId,\n // skip the approval popup and restore the session without user interaction.\n if (msg.sessionId && this.session?.active && this.session.id === msg.sessionId) {\n const identity = this.getPublicIdentity();\n this.sendHandshakeResponse([...this.grantedPermissions], this.session.id, identity);\n return;\n }\n\n const requestedPermissions = msg.permissions as PermissionScope[];\n\n const { approved, grantedPermissions } = await this.config.onConnectionRequest(\n dapp,\n requestedPermissions,\n msg.silent,\n clientInfo,\n );\n\n if (!approved) {\n this.sendHandshakeResponse([], undefined, undefined);\n return;\n }\n\n // Create session\n const sessionId = createRequestId();\n const allPermissions = [...new Set([...DEFAULT_PERMISSIONS, ...grantedPermissions])];\n const ttl = this.config.sessionTtlMs ?? DEFAULT_SESSION_TTL_MS;\n\n this.session = {\n id: sessionId,\n dapp,\n permissions: allPermissions,\n createdAt: Date.now(),\n expiresAt: ttl > 0 ? Date.now() + ttl : 0,\n active: true,\n };\n this.grantedPermissions = new Set(allPermissions);\n\n // Auto-push identity:changed to dApp whenever the wallet switches address.\n // MetaMask pattern: no sphere_subscribe needed — host pushes it unconditionally.\n this.autoSubscribeIdentityChanged();\n\n // Build public identity\n const identity = this.getPublicIdentity();\n\n this.sendHandshakeResponse(allPermissions, sessionId, identity);\n }\n\n // `warning` is a forward-compatible deprecation-notice slot (see SphereHandshake.warning);\n // no call site emits one yet — reserved for the deprecation-window policy.\n private sendHandshakeResponse(\n permissions: string[],\n sessionId: string | undefined,\n identity: PublicIdentity | undefined,\n error?: SphereRpcError,\n echoV?: string,\n warning?: SphereRpcError,\n ): void {\n const network: NetworkInfo | undefined =\n typeof this.sphere.networkId === 'number' ? { id: this.sphere.networkId } : undefined;\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: (error && echoV ? echoV : SPHERE_CONNECT_VERSION) as typeof SPHERE_CONNECT_VERSION,\n type: 'handshake',\n direction: 'response',\n permissions,\n sessionId,\n identity,\n network,\n sdkVersion: SDK_VERSION,\n error,\n warning,\n });\n }\n\n // ===========================================================================\n // RPC Requests (query)\n // ===========================================================================\n\n private async handleRpcRequest(msg: SphereRpcRequest): Promise<void> {\n // Session check\n if (!this.session?.active) {\n this.sendError(msg.id, ERROR_CODES.NOT_CONNECTED, 'Not connected');\n return;\n }\n\n // Session expiry\n if (this.session.expiresAt > 0 && Date.now() > this.session.expiresAt) {\n this.revokeSession();\n this.sendError(msg.id, ERROR_CODES.SESSION_EXPIRED, 'Session expired');\n return;\n }\n\n // Rate limit\n if (!this.checkRateLimit()) {\n this.sendError(msg.id, ERROR_CODES.RATE_LIMITED, 'Too many requests');\n return;\n }\n\n // Handle disconnect\n if (msg.method === RPC_METHODS.DISCONNECT) {\n const disconnectedSession = this.session;\n this.revokeSession();\n this.sendResult(msg.id, { disconnected: true });\n if (disconnectedSession && this.config.onDisconnect) {\n // Fire-and-forget: don't block the response\n Promise.resolve(this.config.onDisconnect(disconnectedSession)).catch((err) => logger.warn('Connect', 'onDisconnect handler error', err));\n }\n return;\n }\n\n // Permission check\n if (!hasMethodPermission(this.grantedPermissions, msg.method)) {\n this.sendError(msg.id, ERROR_CODES.PERMISSION_DENIED, `Permission denied for ${msg.method}`);\n return;\n }\n\n try {\n const result = await this.executeMethod(msg.method, msg.params ?? {});\n this.sendResult(msg.id, result);\n } catch (error) {\n this.sendError(msg.id, ERROR_CODES.INTERNAL_ERROR, (error as Error).message);\n }\n }\n\n // ===========================================================================\n // Intent Requests\n // ===========================================================================\n\n private async handleIntentRequest(msg: SphereIntentRequest): Promise<void> {\n // Session check\n if (!this.session?.active) {\n this.sendIntentError(msg.id, ERROR_CODES.NOT_CONNECTED, 'Not connected');\n return;\n }\n\n // Session expiry\n if (this.session.expiresAt > 0 && Date.now() > this.session.expiresAt) {\n this.revokeSession();\n this.sendIntentError(msg.id, ERROR_CODES.SESSION_EXPIRED, 'Session expired');\n return;\n }\n\n // Permission check\n if (!hasIntentPermission(this.grantedPermissions, msg.action)) {\n this.sendIntentError(msg.id, ERROR_CODES.PERMISSION_DENIED, `Permission denied for intent: ${msg.action}`);\n return;\n }\n\n // Check auto-approve before delegating to wallet UI\n const autoHandler = this.autoApprovedIntents.get(msg.action);\n if (autoHandler) {\n const autoResponse = await autoHandler(msg.action, msg.params, this.session);\n if (autoResponse.error) {\n this.sendIntentError(msg.id, autoResponse.error.code, autoResponse.error.message);\n } else {\n this.sendIntentResult(msg.id, autoResponse.result);\n }\n return;\n }\n\n // Delegate to wallet app\n const response = await this.config.onIntent(msg.action, msg.params, this.session);\n\n if (response.error) {\n this.sendIntentError(msg.id, response.error.code, response.error.message);\n } else {\n this.sendIntentResult(msg.id, response.result);\n }\n }\n\n // ===========================================================================\n // Method Router\n // ===========================================================================\n\n private async executeMethod(method: string, params: Record<string, unknown>): Promise<unknown> {\n switch (method) {\n case RPC_METHODS.GET_IDENTITY:\n return this.getPublicIdentity();\n\n case RPC_METHODS.GET_BALANCE:\n return this.sphere.payments.getBalance(params.coinId as string | undefined);\n\n case RPC_METHODS.GET_ASSETS:\n return this.sphere.payments.getAssets(params.coinId as string | undefined);\n\n case RPC_METHODS.GET_FIAT_BALANCE:\n return { fiatBalance: await this.sphere.payments.getFiatBalance() };\n\n case RPC_METHODS.GET_TOKENS:\n return this.stripTokenSdkData(\n this.sphere.payments.getTokens(\n params.coinId ? { coinId: params.coinId as string } : undefined,\n ),\n );\n\n case RPC_METHODS.GET_HISTORY:\n return this.sphere.payments.getHistory();\n\n case RPC_METHODS.RESOLVE:\n if (!params.identifier) {\n throw new SphereError('Missing required parameter: identifier', 'VALIDATION_ERROR');\n }\n return this.sphere.resolve(params.identifier as string);\n\n case RPC_METHODS.SUBSCRIBE:\n return this.handleSubscribe(params.event as string);\n\n case RPC_METHODS.UNSUBSCRIBE:\n return this.handleUnsubscribe(params.event as string);\n\n case RPC_METHODS.GET_CONVERSATIONS: {\n if (!this.sphere.communications) throw new SphereError('Communications module not available', 'MODULE_NOT_AVAILABLE');\n const convos = this.sphere.communications.getConversations();\n const result: Array<{\n peerPubkey: string;\n peerNametag?: string;\n lastMessage: ConnectDirectMessage;\n unreadCount: number;\n messageCount: number;\n }> = [];\n // Collect conversations and track which ones need nametag resolution\n const needsResolve: Array<{ index: number; peerPubkey: string }> = [];\n for (const [peer, messages] of convos) {\n if (messages.length === 0) continue;\n const last = messages[messages.length - 1];\n // Find peer nametag from any message in the conversation\n const peerNametag =\n messages.find(m => m.senderPubkey === peer && m.senderNametag)?.senderNametag\n ?? messages.find(m => m.recipientPubkey === peer && m.recipientNametag)?.recipientNametag;\n const idx = result.length;\n result.push({\n peerPubkey: peer,\n peerNametag,\n lastMessage: last,\n unreadCount: this.sphere.communications.getUnreadCount(peer),\n messageCount: messages.length,\n });\n if (!peerNametag) {\n needsResolve.push({ index: idx, peerPubkey: peer });\n }\n }\n // Resolve missing nametags via transport (parallel, best-effort)\n if (needsResolve.length > 0) {\n const resolved = await Promise.all(\n needsResolve.map(({ peerPubkey }) =>\n this.sphere.communications!.resolvePeerNametag(peerPubkey).catch((err) => { logger.debug('Connect', 'Peer Unicity ID resolution failed', err); return undefined; }),\n ),\n );\n for (let i = 0; i < needsResolve.length; i++) {\n if (resolved[i]) {\n result[needsResolve[i].index].peerNametag = resolved[i];\n }\n }\n }\n result.sort((a, b) => b.lastMessage.timestamp - a.lastMessage.timestamp);\n return result;\n }\n\n case RPC_METHODS.GET_MESSAGES: {\n if (!this.sphere.communications) throw new SphereError('Communications module not available', 'MODULE_NOT_AVAILABLE');\n if (!params.peerPubkey) throw new SphereError('Missing required parameter: peerPubkey', 'VALIDATION_ERROR');\n return this.sphere.communications.getConversationPage(\n params.peerPubkey as string,\n {\n limit: params.limit as number | undefined,\n before: params.before as number | undefined,\n },\n );\n }\n\n case RPC_METHODS.GET_DM_UNREAD_COUNT: {\n if (!this.sphere.communications) throw new SphereError('Communications module not available', 'MODULE_NOT_AVAILABLE');\n return {\n unreadCount: this.sphere.communications.getUnreadCount(\n params.peerPubkey as string | undefined,\n ),\n };\n }\n\n case RPC_METHODS.MARK_AS_READ: {\n if (!this.sphere.communications) throw new SphereError('Communications module not available', 'MODULE_NOT_AVAILABLE');\n if (!params.messageIds || !Array.isArray(params.messageIds)) {\n throw new SphereError('Missing required parameter: messageIds (string[])', 'VALIDATION_ERROR');\n }\n await this.sphere.communications.markAsRead(params.messageIds as string[]);\n return { marked: true, count: (params.messageIds as string[]).length };\n }\n\n case RPC_METHODS.GET_INVOICES: {\n if (!this.sphere.accounting) throw new SphereError('Accounting module not available', 'MODULE_NOT_AVAILABLE');\n // W23-R2 fix: Extract only known fields to prevent unsanitized dApp params\n // from reaching the module (defense-in-depth).\n const invoiceOpts: Record<string, unknown> = {};\n if (params.state !== undefined) invoiceOpts.state = params.state;\n if (params.limit !== undefined) invoiceOpts.limit = params.limit;\n if (params.offset !== undefined) invoiceOpts.offset = params.offset;\n if (params.sortBy !== undefined) invoiceOpts.sortBy = params.sortBy;\n if (params.sortOrder !== undefined) invoiceOpts.sortOrder = params.sortOrder;\n if (params.createdByMe !== undefined) invoiceOpts.createdByMe = params.createdByMe;\n if (params.targetingMe !== undefined) invoiceOpts.targetingMe = params.targetingMe;\n return this.sphere.accounting.getInvoices(invoiceOpts);\n }\n\n case RPC_METHODS.GET_INVOICE_STATUS: {\n if (!this.sphere.accounting) throw new SphereError('Accounting module not available', 'MODULE_NOT_AVAILABLE');\n if (!params.invoiceId || typeof params.invoiceId !== 'string') {\n throw new SphereError('Missing required parameter: invoiceId', 'VALIDATION_ERROR');\n }\n return this.sphere.accounting.getInvoiceStatus(params.invoiceId as string);\n }\n\n default:\n throw new SphereError(`Unknown method: ${method}`, 'VALIDATION_ERROR');\n }\n }\n\n // ===========================================================================\n // Event Subscriptions\n // ===========================================================================\n\n private autoSubscribeIdentityChanged(): void {\n if (this.eventSubscriptions.has(WALLET_EVENTS.IDENTITY_CHANGED)) return;\n const unsub = this.sphere.on('identity:changed' as SphereEventType, (data: unknown) => {\n this.pushClientEvent(WALLET_EVENTS.IDENTITY_CHANGED, data);\n });\n this.eventSubscriptions.set(WALLET_EVENTS.IDENTITY_CHANGED, unsub);\n }\n\n private handleSubscribe(eventName: string): { subscribed: boolean; event: string } {\n if (!eventName) throw new SphereError('Missing required parameter: event', 'VALIDATION_ERROR');\n\n if (this.eventSubscriptions.has(eventName)) {\n return { subscribed: true, event: eventName };\n }\n\n const unsub = this.sphere.on(eventName as SphereEventType, (data: unknown) => {\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'event',\n event: eventName,\n data,\n });\n });\n\n this.eventSubscriptions.set(eventName, unsub);\n return { subscribed: true, event: eventName };\n }\n\n private handleUnsubscribe(eventName: string): { unsubscribed: boolean; event: string } {\n if (!eventName) throw new SphereError('Missing required parameter: event', 'VALIDATION_ERROR');\n\n const unsub = this.eventSubscriptions.get(eventName);\n if (unsub) {\n unsub();\n this.eventSubscriptions.delete(eventName);\n }\n return { unsubscribed: true, event: eventName };\n }\n\n private cleanupEventSubscriptions(): void {\n for (const [, unsub] of this.eventSubscriptions) {\n unsub();\n }\n this.eventSubscriptions.clear();\n }\n\n // ===========================================================================\n // Helpers\n // ===========================================================================\n\n /** Push an event to the dApp without requiring a sphere_subscribe call. */\n private pushClientEvent(event: string, data: unknown): void {\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'event',\n event,\n data,\n });\n }\n\n private getPublicIdentity(): PublicIdentity | undefined {\n const id = this.sphere.identity;\n if (!id) return undefined;\n return {\n chainPubkey: id.chainPubkey,\n directAddress: id.directAddress,\n nametag: id.nametag,\n };\n }\n\n private stripTokenSdkData(tokens: unknown[]): unknown[] {\n return tokens.map((t) => {\n const token = t as Record<string, unknown>;\n // Return all fields except internal sdkData\n const { sdkData: _sdkData, ...publicFields } = token;\n return publicFields;\n });\n }\n\n private sendResult(id: string, result: unknown): void {\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'response',\n id,\n result,\n });\n }\n\n private sendError(id: string, code: number, message: string): void {\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'response',\n id,\n error: { code, message },\n });\n }\n\n private sendIntentResult(id: string, result: unknown): void {\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'intent_result',\n id,\n result,\n });\n }\n\n private sendIntentError(id: string, code: number, message: string): void {\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'intent_result',\n id,\n error: { code, message },\n });\n }\n\n private checkRateLimit(): boolean {\n const maxRps = this.config.maxRequestsPerSecond ?? DEFAULT_MAX_RPS;\n const now = Date.now();\n if (now > this.rateLimitResetAt) {\n this.rateLimitCounter = 0;\n this.rateLimitResetAt = now + 1000;\n }\n this.rateLimitCounter++;\n return this.rateLimitCounter <= maxRps;\n }\n}\n","/**\n * ConnectClient — dApp side of Sphere Connect.\n *\n * Lightweight client that communicates with a wallet's ConnectHost\n * through a ConnectTransport. Provides query and intent methods\n * that mirror the Sphere SDK API.\n *\n * Zero dependencies on the Sphere SDK core.\n */\n\nimport { logger } from '../../core/logger';\nimport { SphereError } from '../../core/errors';\nimport type { ConnectTransport, ConnectClientConfig, ConnectResult, ConnectEventHandler } from '../types';\nimport type {\n SphereConnectMessage,\n SphereHandshake,\n DAppMetadata,\n PublicIdentity,\n NetworkInfo,\n} from '../protocol';\nimport {\n SPHERE_CONNECT_NAMESPACE,\n SPHERE_CONNECT_VERSION,\n RPC_METHODS,\n createRequestId,\n} from '../protocol';\nimport { SDK_VERSION } from '../version';\nimport { ALL_PERMISSIONS } from '../permissions';\nimport type { PermissionScope } from '../permissions';\n\n/** Error thrown by ConnectClient carrying the host's structured rejection.\n * Discriminate on the numeric `.code` (bundle-safe), not `instanceof`. */\nexport class ConnectError extends Error {\n constructor(message: string, public readonly code: number, public readonly data?: unknown) {\n super(message);\n this.name = 'ConnectError';\n }\n}\n\nconst DEFAULT_TIMEOUT = 30000;\nconst DEFAULT_INTENT_TIMEOUT = 120000;\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\nexport class ConnectClient {\n private readonly transport: ConnectTransport;\n private readonly dapp: DAppMetadata;\n private readonly requestedPermissions: PermissionScope[];\n private readonly timeout: number;\n private readonly intentTimeout: number;\n\n private readonly resumeSessionId: string | null;\n private readonly silent: boolean;\n private readonly network: NetworkInfo | undefined;\n\n private sessionId: string | null = null;\n private grantedPermissions: PermissionScope[] = [];\n private identity: PublicIdentity | null = null;\n private walletNet: NetworkInfo | null = null;\n private connected = false;\n\n private pendingRequests: Map<string, PendingRequest> = new Map();\n private eventHandlers: Map<string, Set<ConnectEventHandler>> = new Map();\n private unsubscribeTransport: (() => void) | null = null;\n\n // Handshake resolver (one-shot)\n private handshakeResolver: {\n resolve: (value: ConnectResult) => void;\n reject: (error: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n } | null = null;\n\n constructor(config: ConnectClientConfig) {\n this.transport = config.transport;\n this.dapp = config.dapp;\n this.requestedPermissions = config.permissions ?? [...ALL_PERMISSIONS];\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT;\n this.intentTimeout = config.intentTimeout ?? DEFAULT_INTENT_TIMEOUT;\n this.resumeSessionId = config.resumeSessionId ?? null;\n this.silent = config.silent ?? false;\n this.network = config.network;\n }\n\n // ===========================================================================\n // Connection\n // ===========================================================================\n\n /** Connect to the wallet. Returns session info and public identity. */\n async connect(): Promise<ConnectResult> {\n // Start listening\n this.unsubscribeTransport = this.transport.onMessage(this.handleMessage.bind(this));\n\n return new Promise<ConnectResult>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.handshakeResolver = null;\n reject(new Error('Connection timeout'));\n }, this.timeout);\n\n this.handshakeResolver = { resolve, reject, timer };\n\n // Send handshake request\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'handshake',\n direction: 'request',\n permissions: this.requestedPermissions,\n dapp: this.dapp,\n sdkVersion: SDK_VERSION,\n ...(this.network ? { network: this.network } : {}),\n ...(this.resumeSessionId ? { sessionId: this.resumeSessionId } : {}),\n ...(this.silent ? { silent: true } : {}),\n });\n });\n }\n\n /** Disconnect from the wallet */\n async disconnect(): Promise<void> {\n if (this.connected) {\n try {\n await this.query(RPC_METHODS.DISCONNECT);\n } catch {\n // Ignore errors during disconnect\n }\n }\n this.cleanup();\n }\n\n /** Whether currently connected */\n get isConnected(): boolean {\n return this.connected;\n }\n\n /** Granted permission scopes */\n get permissions(): readonly PermissionScope[] {\n return this.grantedPermissions;\n }\n\n /** Current session ID */\n get session(): string | null {\n return this.sessionId;\n }\n\n /** Public identity received during handshake */\n get walletIdentity(): PublicIdentity | null {\n return this.identity;\n }\n\n /** Wallet's active network, received during handshake. */\n get walletNetwork(): NetworkInfo | null {\n return this.walletNet;\n }\n\n // ===========================================================================\n // Query (read data)\n // ===========================================================================\n\n /** Send a query request and return the result */\n async query<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T> {\n if (!this.connected) throw new SphereError('Not connected', 'NOT_INITIALIZED');\n\n const id = createRequestId();\n\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n reject(new Error(`Query timeout: ${method}`));\n }, this.timeout);\n\n this.pendingRequests.set(id, {\n resolve: resolve as (v: unknown) => void,\n reject,\n timer,\n });\n\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'request',\n id,\n method,\n params,\n });\n });\n }\n\n // ===========================================================================\n // Intent (trigger wallet UI)\n // ===========================================================================\n\n /** Send an intent request. The wallet will open its UI for user confirmation. */\n async intent<T = unknown>(action: string, params: Record<string, unknown>): Promise<T> {\n if (!this.connected) throw new SphereError('Not connected', 'NOT_INITIALIZED');\n\n const id = createRequestId();\n\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n reject(new Error(`Intent timeout: ${action}`));\n }, this.intentTimeout);\n\n this.pendingRequests.set(id, {\n resolve: resolve as (v: unknown) => void,\n reject,\n timer,\n });\n\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'intent',\n id,\n action,\n params,\n });\n });\n }\n\n // ===========================================================================\n // Events\n // ===========================================================================\n\n /** Subscribe to a wallet event. Returns unsubscribe function. */\n on(event: string, handler: ConnectEventHandler): () => void {\n if (!this.eventHandlers.has(event)) {\n this.eventHandlers.set(event, new Set());\n // Tell host to forward this event\n if (this.connected) {\n this.query(RPC_METHODS.SUBSCRIBE, { event }).catch((err) => logger.debug('Connect', 'Event subscription failed', err));\n }\n }\n this.eventHandlers.get(event)!.add(handler);\n\n return () => {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.delete(handler);\n if (handlers.size === 0) {\n this.eventHandlers.delete(event);\n if (this.connected) {\n this.query(RPC_METHODS.UNSUBSCRIBE, { event }).catch((err) => logger.debug('Connect', 'Event unsubscription failed', err));\n }\n }\n }\n };\n }\n\n // ===========================================================================\n // Message Handling\n // ===========================================================================\n\n private handleMessage(msg: SphereConnectMessage): void {\n // Handshake response\n if (msg.type === 'handshake' && msg.direction === 'response') {\n this.handleHandshakeResponse(msg);\n return;\n }\n\n // RPC response (query)\n if (msg.type === 'response') {\n this.handlePendingResponse(msg.id, msg.result, msg.error);\n return;\n }\n\n // Intent result\n if (msg.type === 'intent_result') {\n this.handlePendingResponse(msg.id, msg.result, msg.error);\n return;\n }\n\n // Event\n if (msg.type === 'event') {\n const handlers = this.eventHandlers.get(msg.event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(msg.data);\n } catch (err) {\n logger.debug('Connect', 'Event handler error', err);\n }\n }\n }\n }\n }\n\n private handleHandshakeResponse(msg: SphereConnectMessage & { type: 'handshake' }): void {\n if (!this.handshakeResolver) return;\n\n clearTimeout(this.handshakeResolver.timer);\n\n const m = msg as SphereHandshake;\n\n if (m.error) {\n this.handshakeResolver.reject(new ConnectError(m.error.message, m.error.code, m.error.data));\n this.handshakeResolver = null;\n return;\n }\n\n if (msg.sessionId && msg.identity) {\n this.sessionId = msg.sessionId;\n this.grantedPermissions = msg.permissions as PermissionScope[];\n this.identity = msg.identity;\n this.walletNet = m.network ?? null;\n this.connected = true;\n if (m.warning) logger.warn('Connect', 'Wallet deprecation notice', m.warning.message);\n this.handshakeResolver.resolve({\n sessionId: msg.sessionId,\n permissions: this.grantedPermissions,\n identity: msg.identity,\n });\n } else {\n this.handshakeResolver.reject(new Error('Connection rejected by wallet'));\n }\n\n this.handshakeResolver = null;\n }\n\n private handlePendingResponse(\n id: string,\n result: unknown,\n error?: { code: number; message: string; data?: unknown },\n ): void {\n const pending = this.pendingRequests.get(id);\n if (!pending) return;\n\n clearTimeout(pending.timer);\n this.pendingRequests.delete(id);\n\n if (error) {\n pending.reject(new ConnectError(error.message, error.code, error.data));\n } else {\n pending.resolve(result);\n }\n }\n\n // ===========================================================================\n // Cleanup\n // ===========================================================================\n\n private cleanup(): void {\n if (this.unsubscribeTransport) {\n this.unsubscribeTransport();\n this.unsubscribeTransport = null;\n }\n\n // Reject all pending requests\n for (const [, pending] of this.pendingRequests) {\n clearTimeout(pending.timer);\n pending.reject(new Error('Disconnected'));\n }\n this.pendingRequests.clear();\n this.eventHandlers.clear();\n\n this.connected = false;\n this.sessionId = null;\n this.grantedPermissions = [];\n this.identity = null;\n this.walletNet = null;\n }\n}\n"],"mappings":"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","names":["identity"]}
1
+ {"version":3,"sources":["../../core/logger.ts","../../core/errors.ts","../../connect/semver.ts","../../constants.ts","../../connect/protocol.ts","../../connect/compatibility.ts","../../connect/version.ts","../../connect/permissions.ts","../../connect/host/ConnectHost.ts","../../connect/client/ConnectClient.ts"],"sourcesContent":["/**\n * Centralized SDK Logger\n *\n * A lightweight singleton logger that works across all tsup bundles\n * by storing state on globalThis. Supports three log levels:\n * - debug: detailed messages (only shown when debug=true)\n * - warn: important warnings (ALWAYS shown regardless of debug flag)\n * - error: critical errors (ALWAYS shown regardless of debug flag)\n *\n * Global debug flag enables all logging. Per-tag overrides allow\n * granular control (e.g., only transport debug).\n *\n * @example\n * ```ts\n * import { logger } from '@unicitylabs/sphere-sdk';\n *\n * // Enable all debug logging\n * logger.configure({ debug: true });\n *\n * // Enable only specific tags\n * logger.setTagDebug('Nostr', true);\n *\n * // Usage in SDK classes\n * logger.debug('Payments', 'Transfer started', { amount, recipient });\n * logger.warn('Nostr', 'queryEvents timed out after 5s');\n * logger.error('Sphere', 'Critical failure', error);\n * ```\n */\n\nexport type LogLevel = 'debug' | 'warn' | 'error';\n\nexport type LogHandler = (level: LogLevel, tag: string, message: string, ...args: unknown[]) => void;\n\nexport interface LoggerConfig {\n /** Enable debug logging globally (default: false). When false, only warn and error messages are shown. */\n debug?: boolean;\n /** Custom log handler. If provided, replaces console output. Useful for tests or custom log sinks. */\n handler?: LogHandler | null;\n}\n\n// Use a unique symbol-like key on globalThis to share logger state across tsup bundles\nconst LOGGER_KEY = '__sphere_sdk_logger__';\n\ninterface LoggerState {\n debug: boolean;\n tags: Record<string, boolean>;\n handler: LogHandler | null;\n}\n\nfunction getState(): LoggerState {\n const g = globalThis as unknown as Record<string, unknown>;\n if (!g[LOGGER_KEY]) {\n g[LOGGER_KEY] = { debug: false, tags: {}, handler: null } satisfies LoggerState;\n }\n return g[LOGGER_KEY] as LoggerState;\n}\n\nfunction isEnabled(tag: string): boolean {\n const state = getState();\n // Per-tag override takes priority\n if (tag in state.tags) return state.tags[tag];\n // Fall back to global flag\n return state.debug;\n}\n\nexport const logger = {\n /**\n * Configure the logger. Can be called multiple times (last write wins).\n * Typically called by createBrowserProviders(), createNodeProviders(), or Sphere.init().\n */\n configure(config: LoggerConfig): void {\n const state = getState();\n if (config.debug !== undefined) state.debug = config.debug;\n if (config.handler !== undefined) state.handler = config.handler;\n },\n\n /**\n * Enable/disable debug logging for a specific tag.\n * Per-tag setting overrides the global debug flag.\n *\n * @example\n * ```ts\n * logger.setTagDebug('Nostr', true); // enable only Nostr logs\n * logger.setTagDebug('Nostr', false); // disable Nostr logs even if global debug=true\n * ```\n */\n setTagDebug(tag: string, enabled: boolean): void {\n getState().tags[tag] = enabled;\n },\n\n /**\n * Clear per-tag override, falling back to global debug flag.\n */\n clearTagDebug(tag: string): void {\n delete getState().tags[tag];\n },\n\n /** Returns true if debug mode is enabled for the given tag (or globally). */\n isDebugEnabled(tag?: string): boolean {\n if (tag) return isEnabled(tag);\n return getState().debug;\n },\n\n /**\n * Debug-level log. Only shown when debug is enabled (globally or for this tag).\n * Use for detailed operational information.\n */\n debug(tag: string, message: string, ...args: unknown[]): void {\n if (!isEnabled(tag)) return;\n const state = getState();\n if (state.handler) {\n state.handler('debug', tag, message, ...args);\n } else {\n console.log(`[${tag}]`, message, ...args);\n }\n },\n\n /**\n * Warning-level log. ALWAYS shown regardless of debug flag.\n * Use for important but non-critical issues (timeouts, retries, degraded state).\n */\n warn(tag: string, message: string, ...args: unknown[]): void {\n const state = getState();\n if (state.handler) {\n state.handler('warn', tag, message, ...args);\n } else {\n console.warn(`[${tag}]`, message, ...args);\n }\n },\n\n /**\n * Error-level log. ALWAYS shown regardless of debug flag.\n * Use for critical failures that should never be silenced.\n */\n error(tag: string, message: string, ...args: unknown[]): void {\n const state = getState();\n if (state.handler) {\n state.handler('error', tag, message, ...args);\n } else {\n console.error(`[${tag}]`, message, ...args);\n }\n },\n\n /** Reset all logger state (debug flag, tags, handler). Primarily for tests. */\n reset(): void {\n const g = globalThis as unknown as Record<string, unknown>;\n delete g[LOGGER_KEY];\n },\n};\n","/**\n * SDK Error Types\n *\n * Structured error codes for programmatic error handling in UI.\n * UI can switch on error.code to show appropriate user-facing messages.\n *\n * @example\n * ```ts\n * import { SphereError } from '@unicitylabs/sphere-sdk';\n *\n * try {\n * await sphere.payments.send({ ... });\n * } catch (err) {\n * if (err instanceof SphereError) {\n * switch (err.code) {\n * case 'INSUFFICIENT_BALANCE': showToast('Not enough funds'); break;\n * case 'INVALID_RECIPIENT': showToast('Recipient not found'); break;\n * case 'TRANSPORT_ERROR': showToast('Network connection issue'); break;\n * case 'TIMEOUT': showToast('Request timed out, try again'); break;\n * default: showToast(err.message);\n * }\n * }\n * }\n * ```\n */\n\nexport type SphereErrorCode =\n | 'NOT_INITIALIZED'\n | 'ALREADY_INITIALIZED'\n | 'INVALID_CONFIG'\n | 'INVALID_IDENTITY'\n | 'INSUFFICIENT_BALANCE'\n | 'INVALID_RECIPIENT'\n | 'TRANSFER_FAILED'\n // The source state was consumed by a DIFFERENT transaction (lost race, not a\n // resume) — raised as TransferConflictError (token-engine/errors.ts, Part E.2).\n | 'TRANSFER_CONFLICT'\n // The op's certification is INDETERMINATE (submit accepted / proof fetch\n // inconclusive) — raised as ProofUnconfirmedError; keep the intent OPEN (#631).\n | 'CERTIFICATION_UNCONFIRMED'\n // Split burn checkpoint (sdk-changes E.4, sphere-sdk#501) — all keep-open (token-engine/errors.ts):\n // burn certified but the checkpoint could not be persisted (no mint submitted);\n | 'CHECKPOINT_PERSIST_FAILED'\n // a certified mint leaf has no reproducing stored bytes, or a stored checkpoint fails byte-binding;\n | 'SPLIT_CHECKPOINT_LOST'\n // a byte-bound checkpoint proof no longer verifies against the current trust base (validator rotation).\n | 'CHECKPOINT_TRUSTBASE_MISMATCH'\n | 'STORAGE_ERROR'\n // #665: the on-chain spend committed but the post-commit wallet-api mirror\n // sync (inventory apply / blob upload / save) failed. NOT a lost payment —\n // the intent is kept open and resume converges the mirror (idempotent apply,\n // #664). Surfaced distinctly so a UI can reassure (\"sent — wallet catching\n // up\") instead of showing a hard send failure.\n | 'SEND_SYNC_PENDING'\n // #677: a multi-leg send lost a source to a concurrent transfer AFTER ≥1\n // earlier leg had already certified on-chain AND been journaled/delivered.\n // The delivered value has irreversibly left the wallet, so this is NOT a\n // plain, re-sendable failure: surfacing a bare TransferConflictError makes\n // the caller re-send the FULL amount and pay the delivered leg twice. Raised\n // as PartialSendConflictError; the caller MUST re-plan ONLY the remainder\n // under a NEW transferId, never the whole amount.\n | 'SEND_PARTIALLY_COMPLETED'\n | 'TRANSPORT_ERROR'\n | 'AGGREGATOR_ERROR'\n | 'VALIDATION_ERROR'\n | 'INVALID_AMOUNT'\n | 'NETWORK_ERROR'\n | 'TIMEOUT'\n | 'DECRYPTION_ERROR'\n | 'MODULE_NOT_AVAILABLE'\n | 'SIGNING_ERROR'\n // Token Spend Queue error codes\n | 'SEND_QUEUE_TIMEOUT'\n | 'SEND_INSUFFICIENT_BALANCE'\n | 'SEND_RESERVATION_CANCELLED'\n | 'SEND_QUEUE_FULL'\n | 'MODULE_DESTROYED'\n | 'REENTRANT_GATE'\n // Invoice / Accounting error codes\n | 'INVOICE_NO_TARGETS'\n | 'INVOICE_INVALID_ADDRESS'\n | 'INVOICE_NO_ASSETS'\n | 'INVOICE_INVALID_ASSET'\n | 'INVOICE_INVALID_AMOUNT'\n | 'INVOICE_INVALID_COIN'\n | 'INVOICE_INVALID_NFT'\n | 'INVOICE_PAST_DUE_DATE'\n | 'INVOICE_DUPLICATE_ADDRESS'\n | 'INVOICE_DUPLICATE_COIN'\n | 'INVOICE_DUPLICATE_NFT'\n | 'INVOICE_MINT_FAILED'\n | 'INVOICE_INVALID_PROOF'\n | 'INVOICE_WRONG_TOKEN_TYPE'\n | 'INVOICE_INVALID_DATA'\n | 'INVOICE_ALREADY_EXISTS'\n | 'INVOICE_NOT_FOUND'\n | 'INVOICE_NOT_TARGET'\n | 'INVOICE_ALREADY_CLOSED'\n | 'INVOICE_ALREADY_CANCELLED'\n | 'INVOICE_ORACLE_REQUIRED'\n | 'INVOICE_TERMINATED'\n | 'INVOICE_INVALID_TARGET'\n | 'INVOICE_INVALID_ASSET_INDEX'\n | 'INVOICE_RETURN_EXCEEDS_BALANCE'\n | 'INVOICE_INVALID_DELIVERY_METHOD'\n | 'INVOICE_INVALID_REFUND_ADDRESS'\n | 'INVOICE_INVALID_CONTACT'\n | 'INVOICE_INVALID_ID'\n | 'INVOICE_TOO_MANY_TARGETS'\n | 'INVOICE_TOO_MANY_ASSETS'\n | 'INVOICE_MEMO_TOO_LONG'\n | 'INVOICE_TERMS_TOO_LARGE'\n | 'INVOICE_NOT_TERMINATED'\n | 'INVOICE_NOT_CANCELLED'\n | 'INVOICE_STORAGE_FAILED'\n | 'RATE_LIMITED'\n | 'COMMUNICATIONS_UNAVAILABLE'\n // Swap error codes\n | 'SWAP_INVALID_DEAL'\n | 'SWAP_INVALID_MANIFEST'\n | 'SWAP_NOT_FOUND'\n | 'SWAP_WRONG_STATE'\n | 'SWAP_RESOLVE_FAILED'\n | 'SWAP_DM_SEND_FAILED'\n | 'SWAP_ESCROW_REJECTED'\n | 'SWAP_DEPOSIT_FAILED'\n | 'SWAP_PAYOUT_VERIFICATION_FAILED'\n | 'SWAP_ALREADY_EXISTS'\n | 'SWAP_ALREADY_COMPLETED'\n | 'SWAP_ALREADY_CANCELLED'\n | 'SWAP_TIMEOUT'\n | 'SWAP_LIMIT_EXCEEDED'\n | 'SWAP_ALREADY_INITIALIZED'\n | 'SWAP_MODULE_DESTROYED'\n | 'SWAP_NOT_INITIALIZED';\n\nexport class SphereError extends Error {\n readonly code: SphereErrorCode;\n readonly cause?: unknown;\n /**\n * #441 deferred-paid linkage: the transferId of the possibly-committed send,\n * stamped by `sendOnce` before the error leaves so a payment-request consumer\n * can durably journal request→transfer and resolve 'paid' only when that\n * transfer actually completes (never optimistically). Present ONLY on\n * possibly-committed outcomes ({@link isPossiblyCommittedSendOutcome}).\n */\n transferId?: string;\n\n constructor(message: string, code: SphereErrorCode, cause?: unknown) {\n super(message);\n this.name = 'SphereError';\n this.code = code;\n this.cause = cause;\n }\n}\n\n/**\n * #677: a send that PARTIALLY completed before losing a source to a concurrent\n * transfer. At least one earlier leg certified on-chain and was journaled for\n * delivery (its {@link committedTokenIds}) — that value has irreversibly left\n * the wallet — but a LATER leg raised `TransferConflictError` (a lost race), and\n * `send()` could NOT COMPLETE the {@link remainingAmount} from the remaining live\n * sources. This is the fallback after the internal remainder re-plan gave up —\n * either insufficient funds OR a transient/transport error during the re-plan\n * (the underlying reason is on `cause`), not necessarily an \"out of funds\" state.\n *\n * Distinct from `TransferConflictError` on purpose: a bare conflict makes the\n * caller re-send the FULL amount, paying the already-delivered leg a second\n * time. Catching THIS type (or `code === 'SEND_PARTIALLY_COMPLETED'`) tells the\n * caller/UI: the delivered legs are final, the conflicted intent has been\n * soft-aborted, and only the REMAINDER ({@link remainingAmount}) may be\n * re-planned — under a NEW transferId, never re-sending the whole amount.\n *\n * NOT a subclass of `TransferConflictError` by design: existing conflict\n * handlers that re-send in full must NOT treat this as an ordinary conflict.\n */\nexport class PartialSendConflictError extends SphereError {\n /**\n * The FIRST partial attempt's transferId — its intent was soft-aborted, and it\n * is the §6 handoff anchor. NOTE: `send()` may re-plan the remainder across\n * SEVERAL internal attempts before giving up, so {@link committedTokenIds} can\n * span MULTIPLE transferIds — they do NOT all map to this single id.\n */\n readonly transferId: string;\n /**\n * Source token ids whose spend already certified on-chain (and whose finished\n * output blob is journaled for delivery). Their value has already been\n * delivered; NEVER re-send these. May accumulate across several internal\n * remainder-re-plan attempts (so not all are journaled under {@link transferId}).\n */\n readonly committedTokenIds: readonly string[];\n /**\n * The still-undelivered portion (base units, decimal string) — `send()` could\n * not COMPLETE it from the remaining live sources (insufficient funds or a\n * transient error; see `cause`). Re-plan ONLY this amount, under a new\n * transferId; the delivered legs converge via the recipient's §6 claim.\n */\n readonly remainingAmount: string;\n\n constructor(\n message: string,\n transferId: string,\n committedTokenIds: readonly string[],\n remainingAmount: string,\n cause?: unknown,\n ) {\n super(message, 'SEND_PARTIALLY_COMPLETED', cause);\n this.name = 'PartialSendConflictError';\n this.transferId = transferId;\n this.committedTokenIds = [...committedTokenIds];\n this.remainingAmount = remainingAmount;\n }\n}\n\n/**\n * Error codes for a send outcome where money has (or may have) already left the\n * wallet on-chain, so the send is NOT a clean, re-sendable failure. A consumer\n * that persists a \"paid\" flag (e.g. a payment request) must keep the request\n * NON-payable on any of these — reverting to a re-payable state would double-pay\n * (#441). Covers the post-commit mirror-sync-pending / keep-open certification\n * states AND the partial-conflict outcome.\n */\nconst POSSIBLY_COMMITTED_SEND_CODES: ReadonlySet<SphereErrorCode> = new Set([\n 'SEND_SYNC_PENDING',\n 'CERTIFICATION_UNCONFIRMED',\n 'CHECKPOINT_PERSIST_FAILED',\n 'SPLIT_CHECKPOINT_LOST',\n 'CHECKPOINT_TRUSTBASE_MISMATCH',\n 'SEND_PARTIALLY_COMPLETED',\n]);\n\n/**\n * #441: true when a send failed with an outcome where the payment has (or may\n * have) irreversibly left the wallet — a post-commit sync-pending / keep-open\n * certification state, or a `PartialSendConflictError`. Such a send completes (or\n * has already partially completed) via resume/claim and MUST NOT be re-sent, so\n * a persisted \"paid\" state must stay non-payable. A `false` result is a clean\n * pre-commit failure — nothing left the wallet, safe to re-pay.\n */\nexport function isPossiblyCommittedSendOutcome(err: unknown): boolean {\n return isSphereError(err) && POSSIBLY_COMMITTED_SEND_CODES.has(err.code);\n}\n\n/**\n * Type guard to check if an error is a SphereError\n */\nexport function isSphereError(err: unknown): err is SphereError {\n return err instanceof SphereError;\n}\n","// connect/semver.ts\n// Tiny, dependency-free semver helpers for the Connect compatibility gate.\n\n/** MAJOR component of a semver string (e.g. '2.7.3' -> 2). NaN if unparseable. */\nexport function majorOf(v: string): number {\n return parseInt(String(v).split('.')[0], 10);\n}\n\n/**\n * Compare two semver strings, prerelease-aware. Returns -1 | 0 | 1.\n * A release outranks its own prerelease: compareSemver('1.2.3', '1.2.3-rc.1') === 1.\n */\nexport function compareSemver(a: string, b: string): number {\n const parse = (v: string) => {\n const [core, pre] = String(v).split('-', 2) as [string, string | undefined];\n const nums = core.split('.').map((n) => parseInt(n, 10) || 0);\n return { nums, pre };\n };\n const pa = parse(a);\n const pb = parse(b);\n for (let i = 0; i < 3; i++) {\n const d = (pa.nums[i] ?? 0) - (pb.nums[i] ?? 0);\n if (d !== 0) return d < 0 ? -1 : 1;\n }\n if (pa.pre === undefined && pb.pre === undefined) return 0;\n if (pa.pre === undefined) return 1; // release > prerelease\n if (pb.pre === undefined) return -1;\n const sa = pa.pre.split('.');\n const sb = pb.pre.split('.');\n for (let i = 0; i < Math.max(sa.length, sb.length); i++) {\n const x = sa[i];\n const y = sb[i];\n if (x === undefined) return -1;\n if (y === undefined) return 1;\n const nx = Number(x);\n const ny = Number(y);\n if (!Number.isNaN(nx) && !Number.isNaN(ny)) {\n if (nx !== ny) return nx < ny ? -1 : 1;\n } else if (x !== y) {\n return x < y ? -1 : 1;\n }\n }\n return 0;\n}\n","/**\n * SDK2 Constants\n * Default configuration values and storage keys\n */\n\n// =============================================================================\n// Storage Keys\n// =============================================================================\n\n/** Default prefix for all storage keys */\nexport const STORAGE_PREFIX = 'sphere_' as const;\n\n/**\n * Default encryption key for wallet data\n * WARNING: This is a placeholder. In production, use user-provided password.\n * This key is used when no password is provided to encrypt/decrypt mnemonic.\n */\nexport const DEFAULT_ENCRYPTION_KEY = 'sphere-default-key' as const;\n\n/**\n * Global storage keys (one per wallet, no address index)\n * Final key format: sphere_{key}\n */\nexport const STORAGE_KEYS_GLOBAL = {\n /** Encrypted BIP39 mnemonic */\n MNEMONIC: 'mnemonic',\n /** Encrypted master private key */\n MASTER_KEY: 'master_key',\n /** BIP32 chain code */\n CHAIN_CODE: 'chain_code',\n /** HD derivation path (full path like m/44'/0'/0'/0/0) */\n DERIVATION_PATH: 'derivation_path',\n /** Base derivation path (like m/44'/0'/0' without chain/index) */\n BASE_PATH: 'base_path',\n /** Derivation mode: bip32, wif_hmac, legacy_hmac */\n DERIVATION_MODE: 'derivation_mode',\n /** Wallet source: mnemonic, file, unknown */\n WALLET_SOURCE: 'wallet_source',\n /** Wallet existence flag */\n WALLET_EXISTS: 'wallet_exists',\n /** Current active address index */\n CURRENT_ADDRESS_INDEX: 'current_address_index',\n /** Nametag cache per address (separate from tracked addresses registry) */\n ADDRESS_NAMETAGS: 'address_nametags',\n /** Active addresses registry (JSON: TrackedAddressesStorage) */\n TRACKED_ADDRESSES: 'tracked_addresses',\n /** Last processed Nostr wallet event timestamp (unix seconds), keyed per pubkey */\n LAST_WALLET_EVENT_TS: 'last_wallet_event_ts',\n /** Last processed Nostr DM (gift-wrap) event timestamp (unix seconds), keyed per pubkey */\n LAST_DM_EVENT_TS: 'last_dm_event_ts',\n /** Group chat: last used relay URL (stale data detection) — global, same relay for all addresses */\n GROUP_CHAT_RELAY_URL: 'group_chat_relay_url',\n /** Cached token registry JSON (fetched from remote) */\n TOKEN_REGISTRY_CACHE: 'token_registry_cache',\n /** Timestamp of last token registry cache update (ms since epoch) */\n TOKEN_REGISTRY_CACHE_TS: 'token_registry_cache_ts',\n /** Cached price data JSON (from CoinGecko or other provider) */\n PRICE_CACHE: 'price_cache',\n /** Timestamp of last price cache update (ms since epoch) */\n PRICE_CACHE_TS: 'price_cache_ts',\n} as const;\n\n/**\n * Per-address storage keys (one per derived address)\n * Final key format: sphere_{DIRECT_xxx_yyy}_{key}\n * Example: sphere_DIRECT_abc123_xyz789_pending_transfers\n *\n * Note: Token data (tokens, tombstones, archived, forked) is stored via\n * TokenStorageProvider, not here. This avoids duplication.\n */\nexport const STORAGE_KEYS_ADDRESS = {\n /** Pending transfers for this address */\n PENDING_TRANSFERS: 'pending_transfers',\n /** Transfer outbox for this address */\n OUTBOX: 'outbox',\n /** Conversations for this address */\n CONVERSATIONS: 'conversations',\n /** Messages for this address */\n MESSAGES: 'messages',\n /** Transaction history for this address */\n TRANSACTION_HISTORY: 'transaction_history',\n /** Pending V5 finalization tokens (unconfirmed instant split tokens) */\n PENDING_V5_TOKENS: 'pending_v5_tokens',\n /**\n * FINISHED v2 token blobs awaiting transport delivery. Written the moment a\n * transfer/split output is certified on-chain (the source is already spent),\n * removed after successful delivery — survives transport failures + crashes\n * so the recipient's token is never lost with the process.\n */\n PENDING_V2_DELIVERIES: 'pending_v2_deliveries',\n /** Group chat: joined groups for this address */\n GROUP_CHAT_GROUPS: 'group_chat_groups',\n /** Group chat: messages for this address */\n GROUP_CHAT_MESSAGES: 'group_chat_messages',\n /** Group chat: members for this address */\n GROUP_CHAT_MEMBERS: 'group_chat_members',\n /** Group chat: processed event IDs for deduplication */\n GROUP_CHAT_PROCESSED_EVENTS: 'group_chat_processed_events',\n /** Processed V5 split group IDs for Nostr re-delivery dedup */\n PROCESSED_SPLIT_GROUP_IDS: 'processed_split_group_ids',\n /** Processed V6 combined transfer IDs for Nostr re-delivery dedup */\n PROCESSED_COMBINED_TRANSFER_IDS: 'processed_combined_transfer_ids',\n // Invoice / Accounting storage keys\n /** Set of cancelled invoice IDs (JSON string array) */\n CANCELLED_INVOICES: 'cancelled_invoices',\n /** Set of closed invoice IDs (JSON string array) */\n CLOSED_INVOICES: 'closed_invoices',\n /** Frozen balances for terminated invoices (JSON map: invoiceId → FrozenInvoiceBalances) */\n FROZEN_BALANCES: 'frozen_balances',\n /** Auto-return settings (JSON: AutoReturnSettings) */\n AUTO_RETURN: 'auto_return',\n /** Auto-return dedup ledger (JSON: AutoReturnLedger) */\n AUTO_RETURN_LEDGER: 'auto_return_ledger',\n /** Invoice-transfer index metadata (JSON: Record<invoiceId, { terminated, frozenAt? }>) */\n INV_LEDGER_INDEX: 'inv_ledger_index',\n /** Token scan state watermarks (JSON: Record<tokenId, txCount>) */\n TOKEN_SCAN_STATE: 'token_scan_state',\n // Swap storage keys\n /** Per-swap key: swap:{swapId} */\n SWAP_RECORD_PREFIX: 'swap:',\n /** Lightweight index array for listing */\n SWAP_INDEX: 'swap_index',\n} as const;\n\n/**\n * Per-address keys that are ALSO per-network: token/payment operational state. Mixing these\n * across networks is unsafe (e.g. a testnet2 auto-return ledger firing a real send on mainnet).\n * Chat/identity per-address keys (CONVERSATIONS/MESSAGES/GROUP_CHAT_*) are network-AGNOSTIC and\n * are deliberately NOT listed — they stay per-address only.\n */\nconst NETWORK_SCOPED_ADDRESS_KEYS: readonly string[] = [\n STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS,\n STORAGE_KEYS_ADDRESS.OUTBOX,\n STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY,\n STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS,\n STORAGE_KEYS_ADDRESS.PENDING_V2_DELIVERIES,\n STORAGE_KEYS_ADDRESS.PROCESSED_SPLIT_GROUP_IDS,\n STORAGE_KEYS_ADDRESS.PROCESSED_COMBINED_TRANSFER_IDS,\n STORAGE_KEYS_ADDRESS.CANCELLED_INVOICES,\n STORAGE_KEYS_ADDRESS.CLOSED_INVOICES,\n STORAGE_KEYS_ADDRESS.FROZEN_BALANCES,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN_LEDGER,\n STORAGE_KEYS_ADDRESS.INV_LEDGER_INDEX,\n STORAGE_KEYS_ADDRESS.TOKEN_SCAN_STATE,\n STORAGE_KEYS_ADDRESS.SWAP_INDEX,\n];\n\n/** Composite per-address key prefixes (modules store `{addressId}_{prefix}{id}`) — per-network. */\nconst NETWORK_SCOPED_ADDRESS_PREFIXES: readonly string[] = [\n STORAGE_KEYS_ADDRESS.SWAP_RECORD_PREFIX, // 'swap:'\n 'inv_ledger:', // AccountingModule INV_LEDGER_PREFIX\n];\n\n/**\n * True if a storage key is a per-NETWORK token/payment key (so the storage provider adds the\n * network segment). Handles the bare form ('auto_return_ledger'), the module-built addressId\n * form ('DIRECT_a_b_auto_return_ledger'), and composites ('{addressId}_swap:{id}'). Chat/identity\n * keys return false — they remain per-address, network-agnostic.\n */\nexport function isNetworkScopedAddressKey(key: string): boolean {\n for (const k of NETWORK_SCOPED_ADDRESS_KEYS) {\n if (key === k || key.endsWith(`_${k}`)) return true;\n }\n for (const p of NETWORK_SCOPED_ADDRESS_PREFIXES) {\n if (key.startsWith(p) || key.includes(`_${p}`)) return true;\n }\n return false;\n}\n\n/** @deprecated Use STORAGE_KEYS_GLOBAL and STORAGE_KEYS_ADDRESS instead */\nexport const STORAGE_KEYS = {\n ...STORAGE_KEYS_GLOBAL,\n ...STORAGE_KEYS_ADDRESS,\n} as const;\n\n/**\n * Build a per-address storage key using address identifier\n * @param addressId - Short identifier for the address (e.g., first 8 chars of pubkey hash, or direct address hash)\n * @param key - The key from STORAGE_KEYS_ADDRESS\n * @returns Key in format: \"{addressId}_{key}\" e.g., \"a1b2c3d4_tokens\"\n */\nexport function getAddressStorageKey(addressId: string, key: string): string {\n return `${addressId}_${key}`;\n}\n\n/**\n * Create a readable address identifier from directAddress or chainPubkey\n * Format: DIRECT_first6_last6 (sanitized for filesystem/storage)\n * @param directAddress - The L3 direct address (DIRECT:xxx) or chainPubkey\n * @returns Sanitized identifier like \"DIRECT_abc123_xyz789\"\n */\nexport function getAddressId(directAddress: string): string {\n // Remove DIRECT:// or DIRECT: prefix if present\n let hash = directAddress;\n if (hash.startsWith('DIRECT://')) {\n hash = hash.slice(9);\n } else if (hash.startsWith('DIRECT:')) {\n hash = hash.slice(7);\n }\n // Format: DIRECT_first6_last6 (sanitized)\n const first = hash.slice(0, 6).toLowerCase();\n const last = hash.slice(-6).toLowerCase();\n return `DIRECT_${first}_${last}`;\n}\n\n// =============================================================================\n// Nostr Defaults\n// =============================================================================\n\n/** Default Nostr relays */\nexport const DEFAULT_NOSTR_RELAYS = [\n 'wss://relay.unicity.network',\n 'wss://relay.damus.io',\n 'wss://nos.lol',\n 'wss://relay.nostr.band',\n] as const;\n\n/** Nostr event kinds used by SDK - must match @unicitylabs/nostr-js-sdk */\nexport const NOSTR_EVENT_KINDS = {\n /** NIP-04 encrypted direct message */\n DIRECT_MESSAGE: 4,\n /** Token transfer (Unicity custom - 31113) */\n TOKEN_TRANSFER: 31113,\n /** Payment request (Unicity custom - 31115) */\n PAYMENT_REQUEST: 31115,\n /** Payment request response (Unicity custom - 31116) */\n PAYMENT_REQUEST_RESPONSE: 31116,\n /** Nametag binding (NIP-78 app-specific data) */\n NAMETAG_BINDING: 30078,\n /** Public broadcast */\n BROADCAST: 1,\n} as const;\n\n/**\n * NIP-29 Event Kinds for relay-based group chat\n * https://github.com/nostr-protocol/nips/blob/master/29.md\n */\nexport const NIP29_KINDS = {\n /** Chat message sent to group */\n CHAT_MESSAGE: 9,\n /** Thread root message */\n THREAD_ROOT: 11,\n /** Thread reply message */\n THREAD_REPLY: 12,\n /** User join request */\n JOIN_REQUEST: 9021,\n /** User leave request */\n LEAVE_REQUEST: 9022,\n /** Admin: add/update user */\n PUT_USER: 9000,\n /** Admin: remove user */\n REMOVE_USER: 9001,\n /** Admin: edit group metadata */\n EDIT_METADATA: 9002,\n /** Admin: delete event */\n DELETE_EVENT: 9005,\n /** Admin: create group */\n CREATE_GROUP: 9007,\n /** Admin: delete group */\n DELETE_GROUP: 9008,\n /** Admin: create invite code */\n CREATE_INVITE: 9009,\n /** Relay-signed group metadata */\n GROUP_METADATA: 39000,\n /** Relay-signed group admins */\n GROUP_ADMINS: 39001,\n /** Relay-signed group members */\n GROUP_MEMBERS: 39002,\n /** Relay-signed group roles */\n GROUP_ROLES: 39003,\n} as const;\n\n// =============================================================================\n// Aggregator (Oracle) Defaults\n// =============================================================================\n\n/**\n * Default aggregator URL\n * Note: The aggregator is conceptually an oracle - a trusted service that provides\n * verifiable truth about token state through cryptographic inclusion proofs.\n */\nexport const DEFAULT_AGGREGATOR_URL = 'https://aggregator.unicity.network/rpc' as const;\n\n/** Dev aggregator URL */\nexport const DEV_AGGREGATOR_URL = 'https://dev-aggregator.dyndns.org/rpc' as const;\n\n/** Test aggregator URL (Goggregator) */\nexport const TEST_AGGREGATOR_URL = 'https://goggregator-test.unicity.network' as const;\n\n/** Default aggregator request timeout (ms) */\nexport const DEFAULT_AGGREGATOR_TIMEOUT = 30000;\n\n\n// =============================================================================\n// IPFS Defaults\n// =============================================================================\n\n/** Default IPFS gateways */\nexport const DEFAULT_IPFS_GATEWAYS = [\n 'https://unicity-ipfs1.dyndns.org',\n] as const;\n\n/** Unicity IPFS bootstrap peers */\nexport const DEFAULT_IPFS_BOOTSTRAP_PEERS = [\n '/dns4/unicity-ipfs2.dyndns.org/tcp/4001/p2p/12D3KooWLNi5NDPPHbrfJakAQqwBqymYTTwMQXQKEWuCrJNDdmfh',\n '/dns4/unicity-ipfs3.dyndns.org/tcp/4001/p2p/12D3KooWQ4aujVE4ShLjdusNZBdffq3TbzrwT2DuWZY9H1Gxhwn6',\n '/dns4/unicity-ipfs4.dyndns.org/tcp/4001/p2p/12D3KooWJ1ByPfUzUrpYvgxKU8NZrR8i6PU1tUgMEbQX9Hh2DEn1',\n '/dns4/unicity-ipfs5.dyndns.org/tcp/4001/p2p/12D3KooWB1MdZZGHN5B8TvWXntbycfe7Cjcz7n6eZ9eykZadvmDv',\n] as const;\n\n/** Unicity dedicated IPFS nodes (HTTP API access) */\nexport const UNICITY_IPFS_NODES = [\n {\n host: 'unicity-ipfs1.dyndns.org',\n peerId: '12D3KooWDKJqEMAhH4nsSSiKtK1VLcas5coUqSPZAfbWbZpxtL4u',\n httpPort: 9080,\n httpsPort: 443,\n },\n] as const;\n\n/**\n * Get IPFS gateway URLs for HTTP API access.\n * @param isSecure - Use HTTPS (default: true). Set false for development.\n */\nexport function getIpfsGatewayUrls(isSecure?: boolean): string[] {\n return UNICITY_IPFS_NODES.map((node) =>\n isSecure !== false\n ? `https://${node.host}`\n : `http://${node.host}:${node.httpPort}`,\n );\n}\n\n// =============================================================================\n// Wallet Defaults\n// =============================================================================\n\n/** Default BIP32 base path (without chain/index) */\nexport const DEFAULT_BASE_PATH = \"m/44'/0'/0'\" as const;\n\n/** Default BIP32 derivation path (full path with chain/index) */\nexport const DEFAULT_DERIVATION_PATH = `${DEFAULT_BASE_PATH}/0/0` as const;\n\n/** Coin types */\nexport const COIN_TYPES = {\n /** Test token */\n TEST: 'TEST',\n} as const;\n\n// =============================================================================\n// Token Registry Defaults\n// =============================================================================\n\n/** Remote token registry URL (GitHub raw) */\nexport const TOKEN_REGISTRY_URL =\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json' as const;\n\n/** Default token registry refresh interval (ms) — 1 hour */\nexport const TOKEN_REGISTRY_REFRESH_INTERVAL = 3_600_000;\n\n// =============================================================================\n// Network Defaults\n// =============================================================================\n\n/** Testnet Nostr relays */\nexport const TEST_NOSTR_RELAYS = [\n 'wss://nostr-relay.testnet.unicity.network',\n] as const;\n\n/** Default group chat relays (NIP-29 Zooid relay) */\nexport const DEFAULT_GROUP_RELAYS = [\n 'wss://sphere-relay.unicity.network',\n] as const;\n\n/**\n * Complete configuration for one network. Every field is required, so adding a\n * network (or a field) that is missing any of these is a COMPILE error at the\n * NETWORKS literal below (via `satisfies`) — a half-configured network can never\n * be defined silently.\n */\nexport interface NetworkConfig {\n readonly name: string;\n readonly aggregatorUrl: string;\n readonly nostrRelays: readonly string[];\n readonly ipfsGateways: readonly string[];\n readonly groupRelays: readonly string[];\n readonly tokenRegistryUrl: string;\n /** Canonical numeric network id (= RootTrustBase.networkId; testnet2 = 4).\n * Optional: only set for networks that have a live v2 trust base. */\n readonly networkId?: number;\n}\n\n/** Network configurations */\nexport const NETWORKS = {\n mainnet: {\n name: 'Mainnet',\n aggregatorUrl: DEFAULT_AGGREGATOR_URL,\n nostrRelays: DEFAULT_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl: TOKEN_REGISTRY_URL,\n },\n // v1 cutover: 'testnet' now POINTS AT TESTNET2 (the v2 gateway network). The\n // old goggregator testnet spoke the removed v1 protocol — a v2 engine cannot\n // run against it. 'testnet2' stays as an alias of the same configuration.\n testnet: {\n name: 'Testnet2',\n networkId: 4,\n // v2 state-transition gateway (networkId 4 comes from the trust base). apiKey is env-injected.\n aggregatorUrl: 'https://gateway.testnet2.unicity.network',\n nostrRelays: TEST_NOSTR_RELAYS, // reuse testnet infra (shared relays/ipfs)\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n },\n testnet2: {\n name: 'Testnet2',\n networkId: 4,\n // v2 state-transition gateway (networkId 4 comes from the trust base). apiKey is env-injected.\n aggregatorUrl: 'https://gateway.testnet2.unicity.network',\n nostrRelays: TEST_NOSTR_RELAYS, // reuse testnet infra (shared relays/ipfs)\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n },\n // NOTE: mainnet/dev still point at v1-era aggregators. The v2 engine cannot\n // operate against them until their gateways are cut over to the v2 protocol —\n // wallet operations on these networks fail loudly (AGGREGATOR_ERROR) until then.\n dev: {\n name: 'Development',\n aggregatorUrl: DEV_AGGREGATOR_URL,\n nostrRelays: TEST_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl: TOKEN_REGISTRY_URL,\n },\n} as const satisfies Record<string, NetworkConfig>;\n\nexport type NetworkType = keyof typeof NETWORKS;\n\n/**\n * A network descriptor — the canonical way to identify a Unicity network across\n * the SDK and the Connect protocol. `id` is the canonical key (= RootTrustBase\n * networkId, analogous to an EIP-155 chainId); `name` is human-readable metadata.\n * Richer fields (gateway/symbol/explorer/icon) and switch/add-network are deferred.\n */\nexport interface NetworkInfo {\n readonly id: number;\n readonly name?: string;\n}\n\n/**\n * Registry of known networks for dApps and wallets. Single-sourced from NETWORKS\n * so it cannot drift. Use as `network: SPHERE_NETWORKS.testnet2`. Custom networks\n * are the same shape: `network: { id, name }`. Only live v2 networks appear here;\n * the legacy `testnet` alias is intentionally not surfaced.\n */\nexport const SPHERE_NETWORKS = {\n testnet2: { id: NETWORKS.testnet2.networkId as number, name: 'testnet2' },\n} as const satisfies Record<string, NetworkInfo>;\n\n// =============================================================================\n// Timeouts & Limits\n// =============================================================================\n\n/** Default timeouts (ms) */\nexport const TIMEOUTS = {\n /** WebSocket connection timeout */\n WEBSOCKET_CONNECT: 10000,\n /** Nostr relay reconnect delay */\n NOSTR_RECONNECT_DELAY: 3000,\n /** Max reconnect attempts */\n MAX_RECONNECT_ATTEMPTS: 5,\n /** Proof polling interval */\n PROOF_POLL_INTERVAL: 1000,\n /** Sync interval */\n SYNC_INTERVAL: 60000,\n} as const;\n\n// =============================================================================\n// Sphere Connect\n// =============================================================================\n\n/** Signal sent by wallet popup to dApp when ConnectHost is ready */\nexport const HOST_READY_TYPE = 'sphere-connect:host-ready' as const;\n\n/** Default timeout (ms) for waiting for the host-ready signal */\nexport const HOST_READY_TIMEOUT = 30_000;\n\n/** Validation limits */\nexport const INVOICE_TOKEN_TYPE_HEX = '14676a280bda4275baf865b67cd4c611bcd58c9bf8226d508acaa10a8fcaccc6' as const;\n\nexport const LIMITS = {\n /** Min nametag length */\n NAMETAG_MIN_LENGTH: 3,\n /** Max nametag length */\n NAMETAG_MAX_LENGTH: 20,\n /** Max memo length */\n MEMO_MAX_LENGTH: 500,\n /** Max message length */\n MESSAGE_MAX_LENGTH: 10000,\n} as const;\n","/**\n * Sphere Connect Protocol\n * JSON-RPC-like message types for wallet ↔ dApp communication.\n */\n\nimport { majorOf } from './semver';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nexport const SPHERE_CONNECT_NAMESPACE = 'sphere-connect';\nexport const SPHERE_CONNECT_VERSION = '2.0'; // Connect protocol version (semver MAJOR.MINOR)\n\nexport { HOST_READY_TYPE, HOST_READY_TIMEOUT, SPHERE_NETWORKS } from '../constants';\n// Import for local use (e.g. SphereHandshake.network) AND re-export for connect consumers.\n// A bare `export type { NetworkInfo } from '../constants'` would re-export without bringing\n// the name into this module's scope, breaking the local references (TS2304).\nimport type { NetworkInfo } from '../constants';\nexport type { NetworkInfo };\n\n// =============================================================================\n// RPC Method Names (query — return data, no UI)\n// =============================================================================\n\nexport const RPC_METHODS = {\n GET_IDENTITY: 'sphere_getIdentity',\n GET_BALANCE: 'sphere_getBalance',\n GET_ASSETS: 'sphere_getAssets',\n GET_FIAT_BALANCE: 'sphere_getFiatBalance',\n GET_TOKENS: 'sphere_getTokens',\n GET_HISTORY: 'sphere_getHistory',\n RESOLVE: 'sphere_resolve',\n SUBSCRIBE: 'sphere_subscribe',\n UNSUBSCRIBE: 'sphere_unsubscribe',\n DISCONNECT: 'sphere_disconnect',\n GET_CONVERSATIONS: 'sphere_getConversations',\n GET_MESSAGES: 'sphere_getMessages',\n GET_DM_UNREAD_COUNT: 'sphere_getDMUnreadCount',\n MARK_AS_READ: 'sphere_markAsRead',\n GET_INVOICES: 'sphere_getInvoices',\n GET_INVOICE_STATUS: 'sphere_getInvoiceStatus',\n} as const;\n\nexport type RpcMethod = (typeof RPC_METHODS)[keyof typeof RPC_METHODS];\n\n// =============================================================================\n// Intent Action Names (open wallet UI, require user confirmation)\n// =============================================================================\n\nexport const INTENT_ACTIONS = {\n SEND: 'send',\n DM: 'dm',\n PAYMENT_REQUEST: 'payment_request',\n RECEIVE: 'receive',\n SIGN_MESSAGE: 'sign_message',\n CREATE_INVOICE: 'create_invoice',\n CLOSE_INVOICE: 'close_invoice',\n CANCEL_INVOICE: 'cancel_invoice',\n PAY_INVOICE: 'pay_invoice',\n RETURN_INVOICE_PAYMENT: 'return_invoice_payment',\n IMPORT_INVOICE: 'import_invoice',\n SEND_INVOICE_RECEIPTS: 'send_invoice_receipts',\n SEND_CANCELLATION_NOTICES: 'send_cancellation_notices',\n SET_AUTO_RETURN: 'set_auto_return',\n MINT: 'mint',\n} as const;\n\nexport type IntentAction = (typeof INTENT_ACTIONS)[keyof typeof INTENT_ACTIONS];\n\n// =============================================================================\n// Error Codes\n// =============================================================================\n\nexport const ERROR_CODES = {\n // Standard JSON-RPC\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n\n // Sphere Connect (4xxx)\n NOT_CONNECTED: 4001,\n PERMISSION_DENIED: 4002,\n USER_REJECTED: 4003,\n SESSION_EXPIRED: 4004,\n ORIGIN_BLOCKED: 4005,\n RATE_LIMITED: 4006,\n UNSUPPORTED_PROTOCOL_VERSION: 4007, // Connect MAJOR mismatch (incompatible era)\n INCOMPATIBLE_NETWORK: 4008, // dApp targets a different network than the wallet\n INSUFFICIENT_BALANCE: 4100,\n INVALID_RECIPIENT: 4101,\n TRANSFER_FAILED: 4102,\n INTENT_CANCELLED: 4200,\n} as const;\n\nexport type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];\n\n// =============================================================================\n// Message Types\n// =============================================================================\n\ninterface SphereMessageBase {\n readonly ns: typeof SPHERE_CONNECT_NAMESPACE;\n readonly v: typeof SPHERE_CONNECT_VERSION;\n}\n\n/** Query request: dApp → Wallet */\nexport interface SphereRpcRequest extends SphereMessageBase {\n readonly type: 'request';\n readonly id: string;\n readonly method: string;\n readonly params?: Record<string, unknown>;\n}\n\n/** Query response: Wallet → dApp */\nexport interface SphereRpcResponse extends SphereMessageBase {\n readonly type: 'response';\n readonly id: string;\n readonly result?: unknown;\n readonly error?: SphereRpcError;\n}\n\n/** Intent request: dApp → Wallet (opens wallet UI) */\nexport interface SphereIntentRequest extends SphereMessageBase {\n readonly type: 'intent';\n readonly id: string;\n readonly action: string;\n readonly params: Record<string, unknown>;\n}\n\n/** Intent result: Wallet → dApp (after user action) */\nexport interface SphereIntentResult extends SphereMessageBase {\n readonly type: 'intent_result';\n readonly id: string;\n readonly result?: unknown;\n readonly error?: SphereRpcError;\n}\n\n/** Event push: Wallet → dApp (unsolicited) */\nexport interface SphereEventMessage extends SphereMessageBase {\n readonly type: 'event';\n readonly event: string;\n readonly data: unknown;\n}\n\n/** Handshake: bidirectional */\nexport interface SphereHandshake extends SphereMessageBase {\n readonly type: 'handshake';\n readonly direction: 'request' | 'response';\n readonly permissions: string[];\n readonly dapp?: DAppMetadata;\n readonly sessionId?: string;\n readonly identity?: PublicIdentity;\n /** If true, wallet must NOT open any approval UI. Immediately reject if origin is not already approved. */\n readonly silent?: boolean;\n /** request: dApp target network; response: wallet active network */\n readonly network?: NetworkInfo;\n /** Informational: the dApp's npm SDK version. */\n readonly sdkVersion?: string;\n /** Response: structured rejection reason when the gate refuses the connection. */\n readonly error?: SphereRpcError;\n /** Response: non-fatal deprecation notice (does not block the connection). */\n readonly warning?: SphereRpcError;\n}\n\nexport interface SphereRpcError {\n readonly code: number;\n readonly message: string;\n readonly data?: unknown;\n}\n\nexport type SphereConnectMessage =\n | SphereRpcRequest\n | SphereRpcResponse\n | SphereIntentRequest\n | SphereIntentResult\n | SphereEventMessage\n | SphereHandshake;\n\n// =============================================================================\n// Shared Types\n// =============================================================================\n\nexport interface DAppMetadata {\n readonly name: string;\n readonly description?: string;\n readonly icon?: string;\n readonly url: string;\n}\n\nexport interface PublicIdentity {\n readonly chainPubkey: string;\n readonly directAddress?: string;\n readonly nametag?: string;\n}\n\n// =============================================================================\n// Wallet-initiated Events (pushed automatically by host, no subscription needed)\n// =============================================================================\n\n/**\n * Events that ConnectHost pushes proactively to connected dApps.\n * dApps can listen with client.on(WALLET_EVENTS.LOCKED, handler) etc.\n * No sphere_subscribe call needed — host sends these unconditionally.\n */\nexport const WALLET_EVENTS = {\n /** Wallet locked or user logged out. dApp shows locked state and waits for unlock.\n * Pushed automatically by ConnectHost — no sphere_subscribe needed. */\n LOCKED: 'wallet:locked',\n /** Active wallet address changed. dApp should update displayed identity.\n * Pushed automatically by ConnectHost — no sphere_subscribe needed. */\n IDENTITY_CHANGED: 'identity:changed',\n} as const;\n\nexport type WalletEvent = (typeof WALLET_EVENTS)[keyof typeof WALLET_EVENTS];\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/** Check if a message belongs to the Sphere Connect protocol.\n * Handshakes are accepted at ANY version (the version decision happens in the\n * handshake handler so an incompatible peer gets a clean typed error instead of a\n * silently-dropped message → timeout). All other (session) traffic must share the\n * same MAJOR (so MINOR versions interoperate, e.g. 2.0 ↔ 2.1). */\nexport function isSphereConnectMessage(msg: unknown): msg is SphereConnectMessage {\n if (!msg || typeof msg !== 'object') return false;\n const m = msg as Record<string, unknown>;\n if (m.ns !== SPHERE_CONNECT_NAMESPACE) return false;\n if (m.type === 'handshake') return true;\n if (typeof m.v !== 'string') return false;\n return majorOf(m.v) === majorOf(SPHERE_CONNECT_VERSION);\n}\n\n/** Create a unique request ID */\nexport function createRequestId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID\n return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;\n}\n","// connect/compatibility.ts — pure, dependency-free compatibility decision for the Connect gate.\nimport { ERROR_CODES } from './protocol';\nimport type { SphereRpcError, NetworkInfo } from './protocol';\nimport { majorOf, compareSemver } from './semver';\n\nexport type IncompatibleReason = 'protocol_incompatible' | 'network_incompatible';\n\nexport interface CompatInput {\n clientProtocol: string; // inbound msg.v\n walletProtocol: string; // SPHERE_CONNECT_VERSION\n clientNetwork: NetworkInfo | undefined; // inbound msg.network\n walletNetworkId: number; // wallet active network\n minMinor?: number; // optional MINOR floor within the MAJOR\n clientSdkVersion?: string; // optional secondary npm floor\n minSdkVersion?: string;\n}\n\nexport type CompatResult = { ok: true } | { ok: false; error: SphereRpcError };\n\nfunction fail(\n code: number,\n reason: IncompatibleReason,\n message: string,\n extra: Record<string, unknown>,\n): CompatResult {\n return { ok: false, error: { code, message, data: { reason, ...extra } } };\n}\n\n/**\n * Decide whether a connecting peer is compatible. Runs four ordered checks\n * (protocol MAJOR → optional MINOR floor → optional SDK floor → network) and\n * returns {ok:true} or {ok:false, error}. A malformed clientProtocol (NaN MAJOR)\n * is treated as protocol-incompatible.\n */\nexport function checkCompatibility(input: CompatInput): CompatResult {\n const { clientProtocol, walletProtocol, minMinor, clientSdkVersion, minSdkVersion } = input;\n\n // 1. Protocol MAJOR must match.\n if (majorOf(clientProtocol) !== majorOf(walletProtocol)) {\n return fail(ERROR_CODES.UNSUPPORTED_PROTOCOL_VERSION, 'protocol_incompatible',\n 'Incompatible Connect protocol version', { walletProtocol, clientProtocol });\n }\n\n const walletMajor = majorOf(walletProtocol);\n\n // 2. Optional MINOR floor within the MAJOR.\n if (minMinor !== undefined && compareSemver(clientProtocol, `${walletMajor}.${minMinor}`) < 0) {\n return fail(ERROR_CODES.UNSUPPORTED_PROTOCOL_VERSION, 'protocol_incompatible',\n 'Connect protocol below the required minimum', { walletProtocol, clientProtocol });\n }\n\n // 3. Optional secondary npm-SDK floor (rarely used).\n if (minSdkVersion !== undefined && (!clientSdkVersion || compareSemver(clientSdkVersion, minSdkVersion) < 0)) {\n return fail(ERROR_CODES.UNSUPPORTED_PROTOCOL_VERSION, 'protocol_incompatible',\n 'SDK version below the required minimum', { walletProtocol, clientProtocol, requiredSdk: minSdkVersion, actualSdk: clientSdkVersion ?? null });\n }\n\n // 4. Network must match (a missing network is treated as a mismatch).\n if (!input.clientNetwork || input.clientNetwork.id !== input.walletNetworkId) {\n return fail(ERROR_CODES.INCOMPATIBLE_NETWORK, 'network_incompatible',\n 'dApp targets a different network than the wallet',\n { walletNetwork: { id: input.walletNetworkId }, clientNetwork: input.clientNetwork ?? null });\n }\n\n return { ok: true };\n}\n\n","// AUTO-GENERATED by scripts/gen-version.mjs — do not edit.\nexport const SDK_VERSION = '0.11.15';\n","/**\n * Sphere Connect Permission System\n * Defines scopes, maps methods/intents to required permissions.\n */\n\nimport { RPC_METHODS, INTENT_ACTIONS } from './protocol';\n\n// =============================================================================\n// Permission Scopes\n// =============================================================================\n\nexport const PERMISSION_SCOPES = {\n IDENTITY_READ: 'identity:read',\n BALANCE_READ: 'balance:read',\n TOKENS_READ: 'tokens:read',\n HISTORY_READ: 'history:read',\n EVENTS_SUBSCRIBE: 'events:subscribe',\n RESOLVE_PEER: 'resolve:peer',\n TRANSFER_REQUEST: 'transfer:request',\n DM_REQUEST: 'dm:request',\n DM_READ: 'dm:read',\n DM_MANAGE: 'dm:manage',\n PAYMENT_REQUEST: 'payment:request',\n SIGN_REQUEST: 'sign:request',\n MINT_REQUEST: 'mint:request',\n INVOICE_READ: 'invoice:read',\n INVOICE_WRITE: 'invoice:write',\n} as const;\n\nexport type PermissionScope = (typeof PERMISSION_SCOPES)[keyof typeof PERMISSION_SCOPES];\n\n/** All available permission scopes */\nexport const ALL_PERMISSIONS: readonly PermissionScope[] = Object.values(PERMISSION_SCOPES);\n\n/** Permissions always granted on connect */\nexport const DEFAULT_PERMISSIONS: readonly PermissionScope[] = [\n PERMISSION_SCOPES.IDENTITY_READ,\n];\n\n// =============================================================================\n// Method → Permission Mapping\n// =============================================================================\n\nexport const METHOD_PERMISSIONS: Record<string, PermissionScope> = {\n [RPC_METHODS.GET_IDENTITY]: PERMISSION_SCOPES.IDENTITY_READ,\n [RPC_METHODS.GET_BALANCE]: PERMISSION_SCOPES.BALANCE_READ,\n [RPC_METHODS.GET_ASSETS]: PERMISSION_SCOPES.BALANCE_READ,\n [RPC_METHODS.GET_FIAT_BALANCE]: PERMISSION_SCOPES.BALANCE_READ,\n [RPC_METHODS.GET_TOKENS]: PERMISSION_SCOPES.TOKENS_READ,\n [RPC_METHODS.GET_HISTORY]: PERMISSION_SCOPES.HISTORY_READ,\n [RPC_METHODS.RESOLVE]: PERMISSION_SCOPES.RESOLVE_PEER,\n [RPC_METHODS.SUBSCRIBE]: PERMISSION_SCOPES.EVENTS_SUBSCRIBE,\n [RPC_METHODS.UNSUBSCRIBE]: PERMISSION_SCOPES.EVENTS_SUBSCRIBE,\n [RPC_METHODS.GET_CONVERSATIONS]: PERMISSION_SCOPES.DM_READ,\n [RPC_METHODS.GET_MESSAGES]: PERMISSION_SCOPES.DM_READ,\n [RPC_METHODS.GET_DM_UNREAD_COUNT]: PERMISSION_SCOPES.DM_READ,\n [RPC_METHODS.MARK_AS_READ]: PERMISSION_SCOPES.DM_MANAGE,\n [RPC_METHODS.GET_INVOICES]: PERMISSION_SCOPES.INVOICE_READ,\n [RPC_METHODS.GET_INVOICE_STATUS]: PERMISSION_SCOPES.INVOICE_READ,\n};\n\n// =============================================================================\n// Intent → Permission Mapping\n// =============================================================================\n\nexport const INTENT_PERMISSIONS: Record<string, PermissionScope> = {\n [INTENT_ACTIONS.SEND]: PERMISSION_SCOPES.TRANSFER_REQUEST,\n [INTENT_ACTIONS.DM]: PERMISSION_SCOPES.DM_REQUEST,\n [INTENT_ACTIONS.PAYMENT_REQUEST]: PERMISSION_SCOPES.PAYMENT_REQUEST,\n [INTENT_ACTIONS.RECEIVE]: PERMISSION_SCOPES.IDENTITY_READ,\n [INTENT_ACTIONS.SIGN_MESSAGE]: PERMISSION_SCOPES.SIGN_REQUEST,\n [INTENT_ACTIONS.CREATE_INVOICE]: PERMISSION_SCOPES.INVOICE_WRITE,\n [INTENT_ACTIONS.CLOSE_INVOICE]: PERMISSION_SCOPES.INVOICE_WRITE,\n [INTENT_ACTIONS.CANCEL_INVOICE]: PERMISSION_SCOPES.INVOICE_WRITE,\n [INTENT_ACTIONS.PAY_INVOICE]: PERMISSION_SCOPES.TRANSFER_REQUEST,\n [INTENT_ACTIONS.RETURN_INVOICE_PAYMENT]: PERMISSION_SCOPES.TRANSFER_REQUEST,\n [INTENT_ACTIONS.IMPORT_INVOICE]: PERMISSION_SCOPES.INVOICE_WRITE,\n [INTENT_ACTIONS.SEND_INVOICE_RECEIPTS]: PERMISSION_SCOPES.INVOICE_WRITE,\n [INTENT_ACTIONS.SEND_CANCELLATION_NOTICES]: PERMISSION_SCOPES.INVOICE_WRITE,\n [INTENT_ACTIONS.SET_AUTO_RETURN]: PERMISSION_SCOPES.INVOICE_WRITE,\n [INTENT_ACTIONS.MINT]: PERMISSION_SCOPES.MINT_REQUEST,\n};\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/** Check if granted permissions allow calling a method */\nexport function hasMethodPermission(granted: ReadonlySet<string>, method: string): boolean {\n const required = METHOD_PERMISSIONS[method];\n if (!required) return false;\n return granted.has(required);\n}\n\n/** Check if granted permissions allow an intent action */\nexport function hasIntentPermission(granted: ReadonlySet<string>, action: string): boolean {\n const required = INTENT_PERMISSIONS[action];\n if (!required) return false;\n return granted.has(required);\n}\n\n/** Validate that all requested permissions are known scopes */\nexport function validatePermissions(permissions: string[]): permissions is PermissionScope[] {\n const validScopes = new Set<string>(ALL_PERMISSIONS);\n return permissions.every((p) => validScopes.has(p));\n}\n","/**\n * ConnectHost — Wallet side of Sphere Connect.\n *\n * Wraps a Sphere instance and exposes its API through a ConnectTransport.\n * Handles permission checking, rate limiting, session management,\n * and delegates intents to the wallet app via callbacks.\n */\n\nimport { logger } from '../../core/logger';\nimport { SphereError } from '../../core/errors';\nimport type { SphereEventType, SphereEventHandler } from '../../types';\nimport type { ConnectTransport, ConnectSession, ConnectHostConfig } from '../types';\nimport type {\n SphereConnectMessage,\n SphereRpcRequest,\n SphereIntentRequest,\n SphereHandshake,\n PublicIdentity,\n SphereRpcError,\n NetworkInfo,\n} from '../protocol';\nimport {\n SPHERE_CONNECT_NAMESPACE,\n SPHERE_CONNECT_VERSION,\n RPC_METHODS,\n ERROR_CODES,\n WALLET_EVENTS,\n createRequestId,\n} from '../protocol';\nimport { checkCompatibility } from '../compatibility';\nimport { SDK_VERSION } from '../version';\nimport {\n DEFAULT_PERMISSIONS,\n hasMethodPermission,\n hasIntentPermission,\n} from '../permissions';\nimport type { PermissionScope } from '../permissions';\n\n// Use a minimal interface for the Sphere dependency to avoid circular imports.\n// ConnectHost only needs these public methods from Sphere.\ninterface SphereInstance {\n readonly identity: { chainPubkey: string; directAddress?: string; nametag?: string } | null;\n readonly networkId?: number;\n readonly payments: {\n getBalance(coinId?: string): unknown[];\n getAssets(coinId?: string): Promise<unknown[]>;\n getFiatBalance(): Promise<number | null>;\n getTokens(filter?: { coinId?: string }): unknown[];\n getHistory(): unknown[];\n };\n signMessage(message: string): string;\n resolve(identifier: string): Promise<unknown>;\n on<T extends SphereEventType>(type: T, handler: SphereEventHandler<T>): () => void;\n readonly communications?: {\n getConversations(): Map<string, ConnectDirectMessage[]>;\n getConversationPage(\n peerPubkey: string,\n options?: { limit?: number; before?: number },\n ): { messages: ConnectDirectMessage[]; hasMore: boolean; oldestTimestamp: number | null };\n getUnreadCount(peerPubkey?: string): number;\n markAsRead(messageIds: string[]): Promise<void>;\n sendDM(recipient: string, content: string): Promise<ConnectDirectMessage>;\n resolvePeerNametag(peerPubkey: string): Promise<string | undefined>;\n };\n readonly accounting?: {\n getInvoices(options?: unknown): unknown[];\n getInvoiceStatus(invoiceId: string): unknown;\n } | null;\n}\n\n/** Minimal DM type to avoid circular imports with Sphere core types. */\ninterface ConnectDirectMessage {\n readonly id: string;\n readonly senderPubkey: string;\n readonly senderNametag?: string;\n readonly recipientPubkey: string;\n readonly recipientNametag?: string;\n readonly content: string;\n readonly timestamp: number;\n isRead: boolean;\n}\n\nconst DEFAULT_SESSION_TTL_MS = 86400000; // 24 hours\nconst DEFAULT_MAX_RPS = 20;\n\nexport class ConnectHost {\n private sphere: SphereInstance;\n private readonly transport: ConnectTransport;\n private readonly config: ConnectHostConfig;\n\n private session: ConnectSession | null = null;\n private grantedPermissions: Set<string> = new Set();\n\n // Event subscription management\n private eventSubscriptions: Map<string, () => void> = new Map(); // eventName → unsub\n\n // Intent auto-approve: action → handler that bypasses wallet UI\n private autoApprovedIntents = new Map<\n string,\n (action: string, params: Record<string, unknown>, session: ConnectSession) => Promise<{ result?: unknown; error?: { code: number; message: string } }>\n >();\n\n // Rate limiting\n private rateLimitCounter = 0;\n private rateLimitResetAt = 0;\n\n private unsubscribeTransport: (() => void) | null = null;\n\n constructor(config: ConnectHostConfig) {\n this.sphere = config.sphere as SphereInstance;\n this.transport = config.transport;\n this.config = config;\n\n this.unsubscribeTransport = this.transport.onMessage(this.handleMessage.bind(this));\n }\n\n /** Get current active session */\n getSession(): ConnectSession | null {\n return this.session;\n }\n\n /** Register an auto-approve handler for an intent action (session-scoped). */\n setIntentAutoApprove(\n action: string,\n handler: (\n action: string,\n params: Record<string, unknown>,\n session: ConnectSession,\n ) => Promise<{ result?: unknown; error?: { code: number; message: string } }>,\n ): void {\n this.autoApprovedIntents.set(action, handler);\n }\n\n /** Remove auto-approve for an intent action. */\n clearIntentAutoApprove(action: string): void {\n this.autoApprovedIntents.delete(action);\n }\n\n /**\n * Update the Sphere instance (e.g. user switched address — new Sphere created).\n * Re-subscribes auto-push events and notifies connected dApp of the new identity.\n */\n updateSphere(newSphere: unknown): void {\n this.sphere = newSphere as SphereInstance;\n\n // Re-subscribe identity:changed on the new Sphere instance\n const existing = this.eventSubscriptions.get(WALLET_EVENTS.IDENTITY_CHANGED);\n if (existing) {\n existing();\n this.eventSubscriptions.delete(WALLET_EVENTS.IDENTITY_CHANGED);\n }\n if (this.session?.active) {\n this.autoSubscribeIdentityChanged();\n // Push the new identity immediately so dApp doesn't have to wait for the next event\n const identity = this.getPublicIdentity();\n if (identity) {\n this.pushClientEvent(WALLET_EVENTS.IDENTITY_CHANGED, identity);\n }\n }\n }\n\n /** Revoke the current session */\n revokeSession(): void {\n if (this.session) {\n this.session.active = false;\n this.cleanupEventSubscriptions();\n this.autoApprovedIntents.clear();\n this.session = null;\n this.grantedPermissions.clear();\n }\n }\n\n /**\n * Notify connected dApp that wallet is locked/logged out, then revoke session.\n * Call this BEFORE destroy() when the wallet locks so the dApp gets a clean signal\n * instead of receiving NOT_CONNECTED errors on the next request.\n */\n notifyWalletLocked(): void {\n if (this.session?.active) {\n this.pushClientEvent(WALLET_EVENTS.LOCKED, {});\n }\n this.revokeSession();\n }\n\n /** Destroy the host, clean up all resources */\n destroy(): void {\n this.revokeSession();\n if (this.unsubscribeTransport) {\n this.unsubscribeTransport();\n this.unsubscribeTransport = null;\n }\n }\n\n // ===========================================================================\n // Message Handling\n // ===========================================================================\n\n private async handleMessage(msg: SphereConnectMessage): Promise<void> {\n try {\n if (msg.type === 'handshake' && msg.direction === 'request') {\n await this.handleHandshake(msg);\n return;\n }\n\n if (msg.type === 'request') {\n await this.handleRpcRequest(msg);\n return;\n }\n\n if (msg.type === 'intent') {\n await this.handleIntentRequest(msg);\n return;\n }\n } catch (error) {\n // Swallow errors from malformed messages\n logger.warn('ConnectHost', 'Error handling message:', error);\n }\n }\n\n // ===========================================================================\n // Handshake\n // ===========================================================================\n\n private async handleHandshake(msg: SphereHandshake): Promise<void> {\n const dapp = msg.dapp;\n // A handshake without dapp metadata is malformed — deny silently (no session).\n // The compatibility gate and onConnectionRejected are intentionally not consulted\n // here: there is no app to gate or to surface a rejection reason for.\n if (!dapp) {\n this.sendHandshakeResponse([], undefined, undefined);\n return;\n }\n\n // Compatibility gate — runs BEFORE resume and BEFORE onConnectionRequest, so an\n // incompatible/old client cannot slip through on a stale sessionId.\n const result = checkCompatibility({\n clientProtocol: msg.v,\n walletProtocol: SPHERE_CONNECT_VERSION,\n clientNetwork: msg.network,\n walletNetworkId: this.sphere.networkId ?? -1,\n minMinor: this.config.minMinorVersion,\n clientSdkVersion: msg.sdkVersion,\n minSdkVersion: this.config.minSdkVersion,\n });\n if (!result.ok) {\n logger.warn('ConnectHost', 'Rejected handshake', {\n dapp: dapp.name,\n reason: (result.error.data as { reason?: string } | undefined)?.reason,\n clientProtocol: msg.v,\n walletProtocol: SPHERE_CONNECT_VERSION,\n clientNetwork: msg.network ?? null,\n walletNetwork: this.sphere.networkId ?? null,\n });\n this.config.onConnectionRejected?.(dapp, result.error, !!msg.silent);\n this.sendHandshakeResponse([], undefined, undefined, result.error, msg.v);\n return;\n }\n\n const clientInfo = { protocolVersion: msg.v, network: msg.network, sdkVersion: msg.sdkVersion };\n\n // Session resumption: if the client presents a valid existing sessionId,\n // skip the approval popup and restore the session without user interaction.\n if (msg.sessionId && this.session?.active && this.session.id === msg.sessionId) {\n const identity = this.getPublicIdentity();\n this.sendHandshakeResponse([...this.grantedPermissions], this.session.id, identity);\n return;\n }\n\n const requestedPermissions = msg.permissions as PermissionScope[];\n\n const { approved, grantedPermissions } = await this.config.onConnectionRequest(\n dapp,\n requestedPermissions,\n msg.silent,\n clientInfo,\n );\n\n if (!approved) {\n this.sendHandshakeResponse([], undefined, undefined);\n return;\n }\n\n // Create session\n const sessionId = createRequestId();\n const allPermissions = [...new Set([...DEFAULT_PERMISSIONS, ...grantedPermissions])];\n const ttl = this.config.sessionTtlMs ?? DEFAULT_SESSION_TTL_MS;\n\n this.session = {\n id: sessionId,\n dapp,\n permissions: allPermissions,\n createdAt: Date.now(),\n expiresAt: ttl > 0 ? Date.now() + ttl : 0,\n active: true,\n };\n this.grantedPermissions = new Set(allPermissions);\n\n // Auto-push identity:changed to dApp whenever the wallet switches address.\n // MetaMask pattern: no sphere_subscribe needed — host pushes it unconditionally.\n this.autoSubscribeIdentityChanged();\n\n // Build public identity\n const identity = this.getPublicIdentity();\n\n this.sendHandshakeResponse(allPermissions, sessionId, identity);\n }\n\n // `warning` is a forward-compatible deprecation-notice slot (see SphereHandshake.warning);\n // no call site emits one yet — reserved for the deprecation-window policy.\n private sendHandshakeResponse(\n permissions: string[],\n sessionId: string | undefined,\n identity: PublicIdentity | undefined,\n error?: SphereRpcError,\n echoV?: string,\n warning?: SphereRpcError,\n ): void {\n const network: NetworkInfo | undefined =\n typeof this.sphere.networkId === 'number' ? { id: this.sphere.networkId } : undefined;\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: (error && echoV ? echoV : SPHERE_CONNECT_VERSION) as typeof SPHERE_CONNECT_VERSION,\n type: 'handshake',\n direction: 'response',\n permissions,\n sessionId,\n identity,\n network,\n sdkVersion: SDK_VERSION,\n error,\n warning,\n });\n }\n\n // ===========================================================================\n // RPC Requests (query)\n // ===========================================================================\n\n private async handleRpcRequest(msg: SphereRpcRequest): Promise<void> {\n // Session check\n if (!this.session?.active) {\n this.sendError(msg.id, ERROR_CODES.NOT_CONNECTED, 'Not connected');\n return;\n }\n\n // Session expiry\n if (this.session.expiresAt > 0 && Date.now() > this.session.expiresAt) {\n this.revokeSession();\n this.sendError(msg.id, ERROR_CODES.SESSION_EXPIRED, 'Session expired');\n return;\n }\n\n // Rate limit\n if (!this.checkRateLimit()) {\n this.sendError(msg.id, ERROR_CODES.RATE_LIMITED, 'Too many requests');\n return;\n }\n\n // Handle disconnect\n if (msg.method === RPC_METHODS.DISCONNECT) {\n const disconnectedSession = this.session;\n this.revokeSession();\n this.sendResult(msg.id, { disconnected: true });\n if (disconnectedSession && this.config.onDisconnect) {\n // Fire-and-forget: don't block the response\n Promise.resolve(this.config.onDisconnect(disconnectedSession)).catch((err) => logger.warn('Connect', 'onDisconnect handler error', err));\n }\n return;\n }\n\n // Permission check\n if (!hasMethodPermission(this.grantedPermissions, msg.method)) {\n this.sendError(msg.id, ERROR_CODES.PERMISSION_DENIED, `Permission denied for ${msg.method}`);\n return;\n }\n\n try {\n const result = await this.executeMethod(msg.method, msg.params ?? {});\n this.sendResult(msg.id, result);\n } catch (error) {\n this.sendError(msg.id, ERROR_CODES.INTERNAL_ERROR, (error as Error).message);\n }\n }\n\n // ===========================================================================\n // Intent Requests\n // ===========================================================================\n\n private async handleIntentRequest(msg: SphereIntentRequest): Promise<void> {\n // Session check\n if (!this.session?.active) {\n this.sendIntentError(msg.id, ERROR_CODES.NOT_CONNECTED, 'Not connected');\n return;\n }\n\n // Session expiry\n if (this.session.expiresAt > 0 && Date.now() > this.session.expiresAt) {\n this.revokeSession();\n this.sendIntentError(msg.id, ERROR_CODES.SESSION_EXPIRED, 'Session expired');\n return;\n }\n\n // Permission check\n if (!hasIntentPermission(this.grantedPermissions, msg.action)) {\n this.sendIntentError(msg.id, ERROR_CODES.PERMISSION_DENIED, `Permission denied for intent: ${msg.action}`);\n return;\n }\n\n // Check auto-approve before delegating to wallet UI\n const autoHandler = this.autoApprovedIntents.get(msg.action);\n if (autoHandler) {\n const autoResponse = await autoHandler(msg.action, msg.params, this.session);\n if (autoResponse.error) {\n this.sendIntentError(msg.id, autoResponse.error.code, autoResponse.error.message);\n } else {\n this.sendIntentResult(msg.id, autoResponse.result);\n }\n return;\n }\n\n // Delegate to wallet app\n const response = await this.config.onIntent(msg.action, msg.params, this.session);\n\n if (response.error) {\n this.sendIntentError(msg.id, response.error.code, response.error.message);\n } else {\n this.sendIntentResult(msg.id, response.result);\n }\n }\n\n // ===========================================================================\n // Method Router\n // ===========================================================================\n\n private async executeMethod(method: string, params: Record<string, unknown>): Promise<unknown> {\n switch (method) {\n case RPC_METHODS.GET_IDENTITY:\n return this.getPublicIdentity();\n\n case RPC_METHODS.GET_BALANCE:\n return this.sphere.payments.getBalance(params.coinId as string | undefined);\n\n case RPC_METHODS.GET_ASSETS:\n return this.sphere.payments.getAssets(params.coinId as string | undefined);\n\n case RPC_METHODS.GET_FIAT_BALANCE:\n return { fiatBalance: await this.sphere.payments.getFiatBalance() };\n\n case RPC_METHODS.GET_TOKENS:\n return this.stripTokenSdkData(\n this.sphere.payments.getTokens(\n params.coinId ? { coinId: params.coinId as string } : undefined,\n ),\n );\n\n case RPC_METHODS.GET_HISTORY:\n return this.sphere.payments.getHistory();\n\n case RPC_METHODS.RESOLVE:\n if (!params.identifier) {\n throw new SphereError('Missing required parameter: identifier', 'VALIDATION_ERROR');\n }\n return this.sphere.resolve(params.identifier as string);\n\n case RPC_METHODS.SUBSCRIBE:\n return this.handleSubscribe(params.event as string);\n\n case RPC_METHODS.UNSUBSCRIBE:\n return this.handleUnsubscribe(params.event as string);\n\n case RPC_METHODS.GET_CONVERSATIONS: {\n if (!this.sphere.communications) throw new SphereError('Communications module not available', 'MODULE_NOT_AVAILABLE');\n const convos = this.sphere.communications.getConversations();\n const result: Array<{\n peerPubkey: string;\n peerNametag?: string;\n lastMessage: ConnectDirectMessage;\n unreadCount: number;\n messageCount: number;\n }> = [];\n // Collect conversations and track which ones need nametag resolution\n const needsResolve: Array<{ index: number; peerPubkey: string }> = [];\n for (const [peer, messages] of convos) {\n if (messages.length === 0) continue;\n const last = messages[messages.length - 1];\n // Find peer nametag from any message in the conversation\n const peerNametag =\n messages.find(m => m.senderPubkey === peer && m.senderNametag)?.senderNametag\n ?? messages.find(m => m.recipientPubkey === peer && m.recipientNametag)?.recipientNametag;\n const idx = result.length;\n result.push({\n peerPubkey: peer,\n peerNametag,\n lastMessage: last,\n unreadCount: this.sphere.communications.getUnreadCount(peer),\n messageCount: messages.length,\n });\n if (!peerNametag) {\n needsResolve.push({ index: idx, peerPubkey: peer });\n }\n }\n // Resolve missing nametags via transport (parallel, best-effort)\n if (needsResolve.length > 0) {\n const resolved = await Promise.all(\n needsResolve.map(({ peerPubkey }) =>\n this.sphere.communications!.resolvePeerNametag(peerPubkey).catch((err) => { logger.debug('Connect', 'Peer Unicity ID resolution failed', err); return undefined; }),\n ),\n );\n for (let i = 0; i < needsResolve.length; i++) {\n if (resolved[i]) {\n result[needsResolve[i].index].peerNametag = resolved[i];\n }\n }\n }\n result.sort((a, b) => b.lastMessage.timestamp - a.lastMessage.timestamp);\n return result;\n }\n\n case RPC_METHODS.GET_MESSAGES: {\n if (!this.sphere.communications) throw new SphereError('Communications module not available', 'MODULE_NOT_AVAILABLE');\n if (!params.peerPubkey) throw new SphereError('Missing required parameter: peerPubkey', 'VALIDATION_ERROR');\n return this.sphere.communications.getConversationPage(\n params.peerPubkey as string,\n {\n limit: params.limit as number | undefined,\n before: params.before as number | undefined,\n },\n );\n }\n\n case RPC_METHODS.GET_DM_UNREAD_COUNT: {\n if (!this.sphere.communications) throw new SphereError('Communications module not available', 'MODULE_NOT_AVAILABLE');\n return {\n unreadCount: this.sphere.communications.getUnreadCount(\n params.peerPubkey as string | undefined,\n ),\n };\n }\n\n case RPC_METHODS.MARK_AS_READ: {\n if (!this.sphere.communications) throw new SphereError('Communications module not available', 'MODULE_NOT_AVAILABLE');\n if (!params.messageIds || !Array.isArray(params.messageIds)) {\n throw new SphereError('Missing required parameter: messageIds (string[])', 'VALIDATION_ERROR');\n }\n await this.sphere.communications.markAsRead(params.messageIds as string[]);\n return { marked: true, count: (params.messageIds as string[]).length };\n }\n\n case RPC_METHODS.GET_INVOICES: {\n if (!this.sphere.accounting) throw new SphereError('Accounting module not available', 'MODULE_NOT_AVAILABLE');\n // W23-R2 fix: Extract only known fields to prevent unsanitized dApp params\n // from reaching the module (defense-in-depth).\n const invoiceOpts: Record<string, unknown> = {};\n if (params.state !== undefined) invoiceOpts.state = params.state;\n if (params.limit !== undefined) invoiceOpts.limit = params.limit;\n if (params.offset !== undefined) invoiceOpts.offset = params.offset;\n if (params.sortBy !== undefined) invoiceOpts.sortBy = params.sortBy;\n if (params.sortOrder !== undefined) invoiceOpts.sortOrder = params.sortOrder;\n if (params.createdByMe !== undefined) invoiceOpts.createdByMe = params.createdByMe;\n if (params.targetingMe !== undefined) invoiceOpts.targetingMe = params.targetingMe;\n return this.sphere.accounting.getInvoices(invoiceOpts);\n }\n\n case RPC_METHODS.GET_INVOICE_STATUS: {\n if (!this.sphere.accounting) throw new SphereError('Accounting module not available', 'MODULE_NOT_AVAILABLE');\n if (!params.invoiceId || typeof params.invoiceId !== 'string') {\n throw new SphereError('Missing required parameter: invoiceId', 'VALIDATION_ERROR');\n }\n return this.sphere.accounting.getInvoiceStatus(params.invoiceId as string);\n }\n\n default:\n throw new SphereError(`Unknown method: ${method}`, 'VALIDATION_ERROR');\n }\n }\n\n // ===========================================================================\n // Event Subscriptions\n // ===========================================================================\n\n private autoSubscribeIdentityChanged(): void {\n if (this.eventSubscriptions.has(WALLET_EVENTS.IDENTITY_CHANGED)) return;\n const unsub = this.sphere.on('identity:changed' as SphereEventType, (data: unknown) => {\n this.pushClientEvent(WALLET_EVENTS.IDENTITY_CHANGED, data);\n });\n this.eventSubscriptions.set(WALLET_EVENTS.IDENTITY_CHANGED, unsub);\n }\n\n private handleSubscribe(eventName: string): { subscribed: boolean; event: string } {\n if (!eventName) throw new SphereError('Missing required parameter: event', 'VALIDATION_ERROR');\n\n if (this.eventSubscriptions.has(eventName)) {\n return { subscribed: true, event: eventName };\n }\n\n const unsub = this.sphere.on(eventName as SphereEventType, (data: unknown) => {\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'event',\n event: eventName,\n data,\n });\n });\n\n this.eventSubscriptions.set(eventName, unsub);\n return { subscribed: true, event: eventName };\n }\n\n private handleUnsubscribe(eventName: string): { unsubscribed: boolean; event: string } {\n if (!eventName) throw new SphereError('Missing required parameter: event', 'VALIDATION_ERROR');\n\n const unsub = this.eventSubscriptions.get(eventName);\n if (unsub) {\n unsub();\n this.eventSubscriptions.delete(eventName);\n }\n return { unsubscribed: true, event: eventName };\n }\n\n private cleanupEventSubscriptions(): void {\n for (const [, unsub] of this.eventSubscriptions) {\n unsub();\n }\n this.eventSubscriptions.clear();\n }\n\n // ===========================================================================\n // Helpers\n // ===========================================================================\n\n /** Push an event to the dApp without requiring a sphere_subscribe call. */\n private pushClientEvent(event: string, data: unknown): void {\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'event',\n event,\n data,\n });\n }\n\n private getPublicIdentity(): PublicIdentity | undefined {\n const id = this.sphere.identity;\n if (!id) return undefined;\n return {\n chainPubkey: id.chainPubkey,\n directAddress: id.directAddress,\n nametag: id.nametag,\n };\n }\n\n private stripTokenSdkData(tokens: unknown[]): unknown[] {\n return tokens.map((t) => {\n const token = t as Record<string, unknown>;\n // Return all fields except internal sdkData\n const { sdkData: _sdkData, ...publicFields } = token;\n return publicFields;\n });\n }\n\n private sendResult(id: string, result: unknown): void {\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'response',\n id,\n result,\n });\n }\n\n private sendError(id: string, code: number, message: string): void {\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'response',\n id,\n error: { code, message },\n });\n }\n\n private sendIntentResult(id: string, result: unknown): void {\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'intent_result',\n id,\n result,\n });\n }\n\n private sendIntentError(id: string, code: number, message: string): void {\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'intent_result',\n id,\n error: { code, message },\n });\n }\n\n private checkRateLimit(): boolean {\n const maxRps = this.config.maxRequestsPerSecond ?? DEFAULT_MAX_RPS;\n const now = Date.now();\n if (now > this.rateLimitResetAt) {\n this.rateLimitCounter = 0;\n this.rateLimitResetAt = now + 1000;\n }\n this.rateLimitCounter++;\n return this.rateLimitCounter <= maxRps;\n }\n}\n","/**\n * ConnectClient — dApp side of Sphere Connect.\n *\n * Lightweight client that communicates with a wallet's ConnectHost\n * through a ConnectTransport. Provides query and intent methods\n * that mirror the Sphere SDK API.\n *\n * Zero dependencies on the Sphere SDK core.\n */\n\nimport { logger } from '../../core/logger';\nimport { SphereError } from '../../core/errors';\nimport type { ConnectTransport, ConnectClientConfig, ConnectResult, ConnectEventHandler } from '../types';\nimport type {\n SphereConnectMessage,\n SphereHandshake,\n DAppMetadata,\n PublicIdentity,\n NetworkInfo,\n} from '../protocol';\nimport {\n SPHERE_CONNECT_NAMESPACE,\n SPHERE_CONNECT_VERSION,\n RPC_METHODS,\n createRequestId,\n} from '../protocol';\nimport { SDK_VERSION } from '../version';\nimport { ALL_PERMISSIONS } from '../permissions';\nimport type { PermissionScope } from '../permissions';\n\n/** Error thrown by ConnectClient carrying the host's structured rejection.\n * Discriminate on the numeric `.code` (bundle-safe), not `instanceof`. */\nexport class ConnectError extends Error {\n constructor(message: string, public readonly code: number, public readonly data?: unknown) {\n super(message);\n this.name = 'ConnectError';\n }\n}\n\nconst DEFAULT_TIMEOUT = 30000;\nconst DEFAULT_INTENT_TIMEOUT = 120000;\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\nexport class ConnectClient {\n private readonly transport: ConnectTransport;\n private readonly dapp: DAppMetadata;\n private readonly requestedPermissions: PermissionScope[];\n private readonly timeout: number;\n private readonly intentTimeout: number;\n\n private readonly resumeSessionId: string | null;\n private readonly silent: boolean;\n private readonly network: NetworkInfo | undefined;\n\n private sessionId: string | null = null;\n private grantedPermissions: PermissionScope[] = [];\n private identity: PublicIdentity | null = null;\n private walletNet: NetworkInfo | null = null;\n private connected = false;\n\n private pendingRequests: Map<string, PendingRequest> = new Map();\n private eventHandlers: Map<string, Set<ConnectEventHandler>> = new Map();\n private unsubscribeTransport: (() => void) | null = null;\n\n // Handshake resolver (one-shot)\n private handshakeResolver: {\n resolve: (value: ConnectResult) => void;\n reject: (error: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n } | null = null;\n\n constructor(config: ConnectClientConfig) {\n this.transport = config.transport;\n this.dapp = config.dapp;\n this.requestedPermissions = config.permissions ?? [...ALL_PERMISSIONS];\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT;\n this.intentTimeout = config.intentTimeout ?? DEFAULT_INTENT_TIMEOUT;\n this.resumeSessionId = config.resumeSessionId ?? null;\n this.silent = config.silent ?? false;\n this.network = config.network;\n }\n\n // ===========================================================================\n // Connection\n // ===========================================================================\n\n /** Connect to the wallet. Returns session info and public identity. */\n async connect(): Promise<ConnectResult> {\n // Start listening\n this.unsubscribeTransport = this.transport.onMessage(this.handleMessage.bind(this));\n\n return new Promise<ConnectResult>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.handshakeResolver = null;\n reject(new Error('Connection timeout'));\n }, this.timeout);\n\n this.handshakeResolver = { resolve, reject, timer };\n\n // Send handshake request\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'handshake',\n direction: 'request',\n permissions: this.requestedPermissions,\n dapp: this.dapp,\n sdkVersion: SDK_VERSION,\n ...(this.network ? { network: this.network } : {}),\n ...(this.resumeSessionId ? { sessionId: this.resumeSessionId } : {}),\n ...(this.silent ? { silent: true } : {}),\n });\n });\n }\n\n /** Disconnect from the wallet */\n async disconnect(): Promise<void> {\n if (this.connected) {\n try {\n await this.query(RPC_METHODS.DISCONNECT);\n } catch {\n // Ignore errors during disconnect\n }\n }\n this.cleanup();\n }\n\n /** Whether currently connected */\n get isConnected(): boolean {\n return this.connected;\n }\n\n /** Granted permission scopes */\n get permissions(): readonly PermissionScope[] {\n return this.grantedPermissions;\n }\n\n /** Current session ID */\n get session(): string | null {\n return this.sessionId;\n }\n\n /** Public identity received during handshake */\n get walletIdentity(): PublicIdentity | null {\n return this.identity;\n }\n\n /** Wallet's active network, received during handshake. */\n get walletNetwork(): NetworkInfo | null {\n return this.walletNet;\n }\n\n // ===========================================================================\n // Query (read data)\n // ===========================================================================\n\n /** Send a query request and return the result */\n async query<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T> {\n if (!this.connected) throw new SphereError('Not connected', 'NOT_INITIALIZED');\n\n const id = createRequestId();\n\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n reject(new Error(`Query timeout: ${method}`));\n }, this.timeout);\n\n this.pendingRequests.set(id, {\n resolve: resolve as (v: unknown) => void,\n reject,\n timer,\n });\n\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'request',\n id,\n method,\n params,\n });\n });\n }\n\n // ===========================================================================\n // Intent (trigger wallet UI)\n // ===========================================================================\n\n /** Send an intent request. The wallet will open its UI for user confirmation. */\n async intent<T = unknown>(action: string, params: Record<string, unknown>): Promise<T> {\n if (!this.connected) throw new SphereError('Not connected', 'NOT_INITIALIZED');\n\n const id = createRequestId();\n\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n reject(new Error(`Intent timeout: ${action}`));\n }, this.intentTimeout);\n\n this.pendingRequests.set(id, {\n resolve: resolve as (v: unknown) => void,\n reject,\n timer,\n });\n\n this.transport.send({\n ns: SPHERE_CONNECT_NAMESPACE,\n v: SPHERE_CONNECT_VERSION,\n type: 'intent',\n id,\n action,\n params,\n });\n });\n }\n\n // ===========================================================================\n // Events\n // ===========================================================================\n\n /** Subscribe to a wallet event. Returns unsubscribe function. */\n on(event: string, handler: ConnectEventHandler): () => void {\n if (!this.eventHandlers.has(event)) {\n this.eventHandlers.set(event, new Set());\n // Tell host to forward this event\n if (this.connected) {\n this.query(RPC_METHODS.SUBSCRIBE, { event }).catch((err) => logger.debug('Connect', 'Event subscription failed', err));\n }\n }\n this.eventHandlers.get(event)!.add(handler);\n\n return () => {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.delete(handler);\n if (handlers.size === 0) {\n this.eventHandlers.delete(event);\n if (this.connected) {\n this.query(RPC_METHODS.UNSUBSCRIBE, { event }).catch((err) => logger.debug('Connect', 'Event unsubscription failed', err));\n }\n }\n }\n };\n }\n\n // ===========================================================================\n // Message Handling\n // ===========================================================================\n\n private handleMessage(msg: SphereConnectMessage): void {\n // Handshake response\n if (msg.type === 'handshake' && msg.direction === 'response') {\n this.handleHandshakeResponse(msg);\n return;\n }\n\n // RPC response (query)\n if (msg.type === 'response') {\n this.handlePendingResponse(msg.id, msg.result, msg.error);\n return;\n }\n\n // Intent result\n if (msg.type === 'intent_result') {\n this.handlePendingResponse(msg.id, msg.result, msg.error);\n return;\n }\n\n // Event\n if (msg.type === 'event') {\n const handlers = this.eventHandlers.get(msg.event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(msg.data);\n } catch (err) {\n logger.debug('Connect', 'Event handler error', err);\n }\n }\n }\n }\n }\n\n private handleHandshakeResponse(msg: SphereConnectMessage & { type: 'handshake' }): void {\n if (!this.handshakeResolver) return;\n\n clearTimeout(this.handshakeResolver.timer);\n\n const m = msg as SphereHandshake;\n\n if (m.error) {\n this.handshakeResolver.reject(new ConnectError(m.error.message, m.error.code, m.error.data));\n this.handshakeResolver = null;\n return;\n }\n\n if (msg.sessionId && msg.identity) {\n this.sessionId = msg.sessionId;\n this.grantedPermissions = msg.permissions as PermissionScope[];\n this.identity = msg.identity;\n this.walletNet = m.network ?? null;\n this.connected = true;\n if (m.warning) logger.warn('Connect', 'Wallet deprecation notice', m.warning.message);\n this.handshakeResolver.resolve({\n sessionId: msg.sessionId,\n permissions: this.grantedPermissions,\n identity: msg.identity,\n });\n } else {\n this.handshakeResolver.reject(new Error('Connection rejected by wallet'));\n }\n\n this.handshakeResolver = null;\n }\n\n private handlePendingResponse(\n id: string,\n result: unknown,\n error?: { code: number; message: string; data?: unknown },\n ): void {\n const pending = this.pendingRequests.get(id);\n if (!pending) return;\n\n clearTimeout(pending.timer);\n this.pendingRequests.delete(id);\n\n if (error) {\n pending.reject(new ConnectError(error.message, error.code, error.data));\n } else {\n pending.resolve(result);\n }\n }\n\n // ===========================================================================\n // Cleanup\n // ===========================================================================\n\n private cleanup(): void {\n if (this.unsubscribeTransport) {\n this.unsubscribeTransport();\n this.unsubscribeTransport = null;\n }\n\n // Reject all pending requests\n for (const [, pending] of this.pendingRequests) {\n clearTimeout(pending.timer);\n pending.reject(new Error('Disconnected'));\n }\n this.pendingRequests.clear();\n this.eventHandlers.clear();\n\n this.connected = false;\n this.sessionId = null;\n this.grantedPermissions = [];\n this.identity = null;\n this.walletNet = null;\n }\n}\n"],"mappings":";AAyCA,IAAM,aAAa;AAQnB,SAAS,WAAwB;AAC/B,QAAM,IAAI;AACV,MAAI,CAAC,EAAE,UAAU,GAAG;AAClB,MAAE,UAAU,IAAI,EAAE,OAAO,OAAO,MAAM,CAAC,GAAG,SAAS,KAAK;AAAA,EAC1D;AACA,SAAO,EAAE,UAAU;AACrB;AAEA,SAAS,UAAU,KAAsB;AACvC,QAAM,QAAQ,SAAS;AAEvB,MAAI,OAAO,MAAM,KAAM,QAAO,MAAM,KAAK,GAAG;AAE5C,SAAO,MAAM;AACf;AAEO,IAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAKpB,UAAU,QAA4B;AACpC,UAAM,QAAQ,SAAS;AACvB,QAAI,OAAO,UAAU,OAAW,OAAM,QAAQ,OAAO;AACrD,QAAI,OAAO,YAAY,OAAW,OAAM,UAAU,OAAO;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,YAAY,KAAa,SAAwB;AAC/C,aAAS,EAAE,KAAK,GAAG,IAAI;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,KAAmB;AAC/B,WAAO,SAAS,EAAE,KAAK,GAAG;AAAA,EAC5B;AAAA;AAAA,EAGA,eAAe,KAAuB;AACpC,QAAI,IAAK,QAAO,UAAU,GAAG;AAC7B,WAAO,SAAS,EAAE;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,KAAa,YAAoB,MAAuB;AAC5D,QAAI,CAAC,UAAU,GAAG,EAAG;AACrB,UAAM,QAAQ,SAAS;AACvB,QAAI,MAAM,SAAS;AACjB,YAAM,QAAQ,SAAS,KAAK,SAAS,GAAG,IAAI;AAAA,IAC9C,OAAO;AACL,cAAQ,IAAI,IAAI,GAAG,KAAK,SAAS,GAAG,IAAI;AAAA,IAC1C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAK,KAAa,YAAoB,MAAuB;AAC3D,UAAM,QAAQ,SAAS;AACvB,QAAI,MAAM,SAAS;AACjB,YAAM,QAAQ,QAAQ,KAAK,SAAS,GAAG,IAAI;AAAA,IAC7C,OAAO;AACL,cAAQ,KAAK,IAAI,GAAG,KAAK,SAAS,GAAG,IAAI;AAAA,IAC3C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,KAAa,YAAoB,MAAuB;AAC5D,UAAM,QAAQ,SAAS;AACvB,QAAI,MAAM,SAAS;AACjB,YAAM,QAAQ,SAAS,KAAK,SAAS,GAAG,IAAI;AAAA,IAC9C,OAAO;AACL,cAAQ,MAAM,IAAI,GAAG,KAAK,SAAS,GAAG,IAAI;AAAA,IAC5C;AAAA,EACF;AAAA;AAAA,EAGA,QAAc;AACZ,UAAM,IAAI;AACV,WAAO,EAAE,UAAU;AAAA,EACrB;AACF;;;ACZO,IAAM,cAAN,cAA0B,MAAM;AAAA,EAC5B;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQT;AAAA,EAEA,YAAY,SAAiB,MAAuB,OAAiB;AACnE,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,QAAQ;AAAA,EACf;AACF;;;ACtJO,SAAS,QAAQ,GAAmB;AACzC,SAAO,SAAS,OAAO,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE;AAC7C;AAMO,SAAS,cAAc,GAAW,GAAmB;AAC1D,QAAM,QAAQ,CAAC,MAAc;AAC3B,UAAM,CAAC,MAAM,GAAG,IAAI,OAAO,CAAC,EAAE,MAAM,KAAK,CAAC;AAC1C,UAAM,OAAO,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,SAAS,GAAG,EAAE,KAAK,CAAC;AAC5D,WAAO,EAAE,MAAM,IAAI;AAAA,EACrB;AACA,QAAM,KAAK,MAAM,CAAC;AAClB,QAAM,KAAK,MAAM,CAAC;AAClB,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,KAAK,GAAG,KAAK,CAAC,KAAK,MAAM,GAAG,KAAK,CAAC,KAAK;AAC7C,QAAI,MAAM,EAAG,QAAO,IAAI,IAAI,KAAK;AAAA,EACnC;AACA,MAAI,GAAG,QAAQ,UAAa,GAAG,QAAQ,OAAW,QAAO;AACzD,MAAI,GAAG,QAAQ,OAAW,QAAO;AACjC,MAAI,GAAG,QAAQ,OAAW,QAAO;AACjC,QAAM,KAAK,GAAG,IAAI,MAAM,GAAG;AAC3B,QAAM,KAAK,GAAG,IAAI,MAAM,GAAG;AAC3B,WAAS,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK;AACvD,UAAM,IAAI,GAAG,CAAC;AACd,UAAM,IAAI,GAAG,CAAC;AACd,QAAI,MAAM,OAAW,QAAO;AAC5B,QAAI,MAAM,OAAW,QAAO;AAC5B,UAAM,KAAK,OAAO,CAAC;AACnB,UAAM,KAAK,OAAO,CAAC;AACnB,QAAI,CAAC,OAAO,MAAM,EAAE,KAAK,CAAC,OAAO,MAAM,EAAE,GAAG;AAC1C,UAAI,OAAO,GAAI,QAAO,KAAK,KAAK,KAAK;AAAA,IACvC,WAAW,MAAM,GAAG;AAClB,aAAO,IAAI,IAAI,KAAK;AAAA,IACtB;AAAA,EACF;AACA,SAAO;AACT;;;ACpBO,IAAM,sBAAsB;AAAA;AAAA,EAEjC,UAAU;AAAA;AAAA,EAEV,YAAY;AAAA;AAAA,EAEZ,YAAY;AAAA;AAAA,EAEZ,iBAAiB;AAAA;AAAA,EAEjB,WAAW;AAAA;AAAA,EAEX,iBAAiB;AAAA;AAAA,EAEjB,eAAe;AAAA;AAAA,EAEf,eAAe;AAAA;AAAA,EAEf,uBAAuB;AAAA;AAAA,EAEvB,kBAAkB;AAAA;AAAA,EAElB,mBAAmB;AAAA;AAAA,EAEnB,sBAAsB;AAAA;AAAA,EAEtB,kBAAkB;AAAA;AAAA,EAElB,sBAAsB;AAAA;AAAA,EAEtB,sBAAsB;AAAA;AAAA,EAEtB,yBAAyB;AAAA;AAAA,EAEzB,aAAa;AAAA;AAAA,EAEb,gBAAgB;AAClB;AAUO,IAAM,uBAAuB;AAAA;AAAA,EAElC,mBAAmB;AAAA;AAAA,EAEnB,QAAQ;AAAA;AAAA,EAER,eAAe;AAAA;AAAA,EAEf,UAAU;AAAA;AAAA,EAEV,qBAAqB;AAAA;AAAA,EAErB,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOnB,uBAAuB;AAAA;AAAA,EAEvB,mBAAmB;AAAA;AAAA,EAEnB,qBAAqB;AAAA;AAAA,EAErB,oBAAoB;AAAA;AAAA,EAEpB,6BAA6B;AAAA;AAAA,EAE7B,2BAA2B;AAAA;AAAA,EAE3B,iCAAiC;AAAA;AAAA;AAAA,EAGjC,oBAAoB;AAAA;AAAA,EAEpB,iBAAiB;AAAA;AAAA,EAEjB,iBAAiB;AAAA;AAAA,EAEjB,aAAa;AAAA;AAAA,EAEb,oBAAoB;AAAA;AAAA,EAEpB,kBAAkB;AAAA;AAAA,EAElB,kBAAkB;AAAA;AAAA;AAAA,EAGlB,oBAAoB;AAAA;AAAA,EAEpB,YAAY;AACd;AAQA,IAAM,8BAAiD;AAAA,EACrD,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,qBAAqB;AACvB;AAGA,IAAM,kCAAqD;AAAA,EACzD,qBAAqB;AAAA;AAAA,EACrB;AAAA;AACF;AAmBO,IAAM,eAAe;AAAA,EAC1B,GAAG;AAAA,EACH,GAAG;AACL;AAqCO,IAAM,uBAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAkEO,IAAM,yBAAyB;AAG/B,IAAM,qBAAqB;AAc3B,IAAM,wBAAwB;AAAA,EACnC;AACF;AAqCO,IAAM,oBAAoB;AAG1B,IAAM,0BAA0B,GAAG,iBAAiB;AAapD,IAAM,qBACX;AAUK,IAAM,oBAAoB;AAAA,EAC/B;AACF;AAGO,IAAM,uBAAuB;AAAA,EAClC;AACF;AAqBO,IAAM,WAAW;AAAA,EACtB,SAAS;AAAA,IACP,MAAM;AAAA,IACN,eAAe;AAAA,IACf,aAAa;AAAA,IACb,cAAc;AAAA,IACd,aAAa;AAAA,IACb,kBAAkB;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAIA,SAAS;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA;AAAA,IAEX,eAAe;AAAA,IACf,aAAa;AAAA;AAAA,IACb,cAAc;AAAA,IACd,aAAa;AAAA,IACb,kBACE;AAAA,EACJ;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,IACN,WAAW;AAAA;AAAA,IAEX,eAAe;AAAA,IACf,aAAa;AAAA;AAAA,IACb,cAAc;AAAA,IACd,aAAa;AAAA,IACb,kBACE;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAIA,KAAK;AAAA,IACH,MAAM;AAAA,IACN,eAAe;AAAA,IACf,aAAa;AAAA,IACb,cAAc;AAAA,IACd,aAAa;AAAA,IACb,kBAAkB;AAAA,EACpB;AACF;AAqBO,IAAM,kBAAkB;AAAA,EAC7B,UAAU,EAAE,IAAI,SAAS,SAAS,WAAqB,MAAM,WAAW;AAC1E;AAyBO,IAAM,kBAAkB;AAGxB,IAAM,qBAAqB;;;AC9d3B,IAAM,2BAA2B;AACjC,IAAM,yBAAyB;AAa/B,IAAM,cAAc;AAAA,EACzB,cAAc;AAAA,EACd,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,SAAS;AAAA,EACT,WAAW;AAAA,EACX,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,cAAc;AAAA,EACd,cAAc;AAAA,EACd,oBAAoB;AACtB;AAQO,IAAM,iBAAiB;AAAA,EAC5B,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,iBAAiB;AAAA,EACjB,SAAS;AAAA,EACT,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,aAAa;AAAA,EACb,wBAAwB;AAAA,EACxB,gBAAgB;AAAA,EAChB,uBAAuB;AAAA,EACvB,2BAA2B;AAAA,EAC3B,iBAAiB;AAAA,EACjB,MAAM;AACR;AAQO,IAAM,cAAc;AAAA;AAAA,EAEzB,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,gBAAgB;AAAA;AAAA,EAGhB,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,8BAA8B;AAAA;AAAA,EAC9B,sBAA8B;AAAA;AAAA,EAC9B,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,kBAAkB;AACpB;AAgHO,IAAM,gBAAgB;AAAA;AAAA;AAAA,EAG3B,QAAQ;AAAA;AAAA;AAAA,EAGR,kBAAkB;AACpB;AAaO,SAAS,uBAAuB,KAA2C;AAChF,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,IAAI;AACV,MAAI,EAAE,OAAO,yBAA0B,QAAO;AAC9C,MAAI,EAAE,SAAS,YAAa,QAAO;AACnC,MAAI,OAAO,EAAE,MAAM,SAAU,QAAO;AACpC,SAAO,QAAQ,EAAE,CAAC,MAAM,QAAQ,sBAAsB;AACxD;AAGO,SAAS,kBAA0B;AACxC,MAAI,OAAO,WAAW,eAAe,OAAO,YAAY;AACtD,WAAO,OAAO,WAAW;AAAA,EAC3B;AAEA,SAAO,GAAG,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACjE;;;AChOA,SAAS,KACP,MACA,QACA,SACA,OACc;AACd,SAAO,EAAE,IAAI,OAAO,OAAO,EAAE,MAAM,SAAS,MAAM,EAAE,QAAQ,GAAG,MAAM,EAAE,EAAE;AAC3E;AAQO,SAAS,mBAAmB,OAAkC;AACnE,QAAM,EAAE,gBAAgB,gBAAgB,UAAU,kBAAkB,cAAc,IAAI;AAGtF,MAAI,QAAQ,cAAc,MAAM,QAAQ,cAAc,GAAG;AACvD,WAAO;AAAA,MAAK,YAAY;AAAA,MAA8B;AAAA,MACpD;AAAA,MAAyC,EAAE,gBAAgB,eAAe;AAAA,IAAC;AAAA,EAC/E;AAEA,QAAM,cAAc,QAAQ,cAAc;AAG1C,MAAI,aAAa,UAAa,cAAc,gBAAgB,GAAG,WAAW,IAAI,QAAQ,EAAE,IAAI,GAAG;AAC7F,WAAO;AAAA,MAAK,YAAY;AAAA,MAA8B;AAAA,MACpD;AAAA,MAA+C,EAAE,gBAAgB,eAAe;AAAA,IAAC;AAAA,EACrF;AAGA,MAAI,kBAAkB,WAAc,CAAC,oBAAoB,cAAc,kBAAkB,aAAa,IAAI,IAAI;AAC5G,WAAO;AAAA,MAAK,YAAY;AAAA,MAA8B;AAAA,MACpD;AAAA,MAA0C,EAAE,gBAAgB,gBAAgB,aAAa,eAAe,WAAW,oBAAoB,KAAK;AAAA,IAAC;AAAA,EACjJ;AAGA,MAAI,CAAC,MAAM,iBAAiB,MAAM,cAAc,OAAO,MAAM,iBAAiB;AAC5E,WAAO;AAAA,MAAK,YAAY;AAAA,MAAsB;AAAA,MAC5C;AAAA,MACA,EAAE,eAAe,EAAE,IAAI,MAAM,gBAAgB,GAAG,eAAe,MAAM,iBAAiB,KAAK;AAAA,IAAC;AAAA,EAChG;AAEA,SAAO,EAAE,IAAI,KAAK;AACpB;;;AChEO,IAAM,cAAc;;;ACUpB,IAAM,oBAAoB;AAAA,EAC/B,eAAe;AAAA,EACf,cAAc;AAAA,EACd,aAAa;AAAA,EACb,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,cAAc;AAAA,EACd,cAAc;AAAA,EACd,eAAe;AACjB;AAKO,IAAM,kBAA8C,OAAO,OAAO,iBAAiB;AAGnF,IAAM,sBAAkD;AAAA,EAC7D,kBAAkB;AACpB;AAMO,IAAM,qBAAsD;AAAA,EACjE,CAAC,YAAY,YAAY,GAAG,kBAAkB;AAAA,EAC9C,CAAC,YAAY,WAAW,GAAG,kBAAkB;AAAA,EAC7C,CAAC,YAAY,UAAU,GAAG,kBAAkB;AAAA,EAC5C,CAAC,YAAY,gBAAgB,GAAG,kBAAkB;AAAA,EAClD,CAAC,YAAY,UAAU,GAAG,kBAAkB;AAAA,EAC5C,CAAC,YAAY,WAAW,GAAG,kBAAkB;AAAA,EAC7C,CAAC,YAAY,OAAO,GAAG,kBAAkB;AAAA,EACzC,CAAC,YAAY,SAAS,GAAG,kBAAkB;AAAA,EAC3C,CAAC,YAAY,WAAW,GAAG,kBAAkB;AAAA,EAC7C,CAAC,YAAY,iBAAiB,GAAG,kBAAkB;AAAA,EACnD,CAAC,YAAY,YAAY,GAAG,kBAAkB;AAAA,EAC9C,CAAC,YAAY,mBAAmB,GAAG,kBAAkB;AAAA,EACrD,CAAC,YAAY,YAAY,GAAG,kBAAkB;AAAA,EAC9C,CAAC,YAAY,YAAY,GAAG,kBAAkB;AAAA,EAC9C,CAAC,YAAY,kBAAkB,GAAG,kBAAkB;AACtD;AAMO,IAAM,qBAAsD;AAAA,EACjE,CAAC,eAAe,IAAI,GAAG,kBAAkB;AAAA,EACzC,CAAC,eAAe,EAAE,GAAG,kBAAkB;AAAA,EACvC,CAAC,eAAe,eAAe,GAAG,kBAAkB;AAAA,EACpD,CAAC,eAAe,OAAO,GAAG,kBAAkB;AAAA,EAC5C,CAAC,eAAe,YAAY,GAAG,kBAAkB;AAAA,EACjD,CAAC,eAAe,cAAc,GAAG,kBAAkB;AAAA,EACnD,CAAC,eAAe,aAAa,GAAG,kBAAkB;AAAA,EAClD,CAAC,eAAe,cAAc,GAAG,kBAAkB;AAAA,EACnD,CAAC,eAAe,WAAW,GAAG,kBAAkB;AAAA,EAChD,CAAC,eAAe,sBAAsB,GAAG,kBAAkB;AAAA,EAC3D,CAAC,eAAe,cAAc,GAAG,kBAAkB;AAAA,EACnD,CAAC,eAAe,qBAAqB,GAAG,kBAAkB;AAAA,EAC1D,CAAC,eAAe,yBAAyB,GAAG,kBAAkB;AAAA,EAC9D,CAAC,eAAe,eAAe,GAAG,kBAAkB;AAAA,EACpD,CAAC,eAAe,IAAI,GAAG,kBAAkB;AAC3C;AAOO,SAAS,oBAAoB,SAA8B,QAAyB;AACzF,QAAM,WAAW,mBAAmB,MAAM;AAC1C,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,QAAQ,IAAI,QAAQ;AAC7B;AAGO,SAAS,oBAAoB,SAA8B,QAAyB;AACzF,QAAM,WAAW,mBAAmB,MAAM;AAC1C,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,QAAQ,IAAI,QAAQ;AAC7B;AAGO,SAAS,oBAAoB,aAAyD;AAC3F,QAAM,cAAc,IAAI,IAAY,eAAe;AACnD,SAAO,YAAY,MAAM,CAAC,MAAM,YAAY,IAAI,CAAC,CAAC;AACpD;;;ACvBA,IAAM,yBAAyB;AAC/B,IAAM,kBAAkB;AAEjB,IAAM,cAAN,MAAkB;AAAA,EACf;AAAA,EACS;AAAA,EACA;AAAA,EAET,UAAiC;AAAA,EACjC,qBAAkC,oBAAI,IAAI;AAAA;AAAA,EAG1C,qBAA8C,oBAAI,IAAI;AAAA;AAAA;AAAA,EAGtD,sBAAsB,oBAAI,IAGhC;AAAA;AAAA,EAGM,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EAEnB,uBAA4C;AAAA,EAEpD,YAAY,QAA2B;AACrC,SAAK,SAAS,OAAO;AACrB,SAAK,YAAY,OAAO;AACxB,SAAK,SAAS;AAEd,SAAK,uBAAuB,KAAK,UAAU,UAAU,KAAK,cAAc,KAAK,IAAI,CAAC;AAAA,EACpF;AAAA;AAAA,EAGA,aAAoC;AAClC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,qBACE,QACA,SAKM;AACN,SAAK,oBAAoB,IAAI,QAAQ,OAAO;AAAA,EAC9C;AAAA;AAAA,EAGA,uBAAuB,QAAsB;AAC3C,SAAK,oBAAoB,OAAO,MAAM;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,WAA0B;AACrC,SAAK,SAAS;AAGd,UAAM,WAAW,KAAK,mBAAmB,IAAI,cAAc,gBAAgB;AAC3E,QAAI,UAAU;AACZ,eAAS;AACT,WAAK,mBAAmB,OAAO,cAAc,gBAAgB;AAAA,IAC/D;AACA,QAAI,KAAK,SAAS,QAAQ;AACxB,WAAK,6BAA6B;AAElC,YAAM,WAAW,KAAK,kBAAkB;AACxC,UAAI,UAAU;AACZ,aAAK,gBAAgB,cAAc,kBAAkB,QAAQ;AAAA,MAC/D;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,gBAAsB;AACpB,QAAI,KAAK,SAAS;AAChB,WAAK,QAAQ,SAAS;AACtB,WAAK,0BAA0B;AAC/B,WAAK,oBAAoB,MAAM;AAC/B,WAAK,UAAU;AACf,WAAK,mBAAmB,MAAM;AAAA,IAChC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,qBAA2B;AACzB,QAAI,KAAK,SAAS,QAAQ;AACxB,WAAK,gBAAgB,cAAc,QAAQ,CAAC,CAAC;AAAA,IAC/C;AACA,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,cAAc;AACnB,QAAI,KAAK,sBAAsB;AAC7B,WAAK,qBAAqB;AAC1B,WAAK,uBAAuB;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,cAAc,KAA0C;AACpE,QAAI;AACF,UAAI,IAAI,SAAS,eAAe,IAAI,cAAc,WAAW;AAC3D,cAAM,KAAK,gBAAgB,GAAG;AAC9B;AAAA,MACF;AAEA,UAAI,IAAI,SAAS,WAAW;AAC1B,cAAM,KAAK,iBAAiB,GAAG;AAC/B;AAAA,MACF;AAEA,UAAI,IAAI,SAAS,UAAU;AACzB,cAAM,KAAK,oBAAoB,GAAG;AAClC;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AAEd,aAAO,KAAK,eAAe,2BAA2B,KAAK;AAAA,IAC7D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,gBAAgB,KAAqC;AACjE,UAAM,OAAO,IAAI;AAIjB,QAAI,CAAC,MAAM;AACT,WAAK,sBAAsB,CAAC,GAAG,QAAW,MAAS;AACnD;AAAA,IACF;AAIA,UAAM,SAAS,mBAAmB;AAAA,MAChC,gBAAgB,IAAI;AAAA,MACpB,gBAAgB;AAAA,MAChB,eAAe,IAAI;AAAA,MACnB,iBAAiB,KAAK,OAAO,aAAa;AAAA,MAC1C,UAAU,KAAK,OAAO;AAAA,MACtB,kBAAkB,IAAI;AAAA,MACtB,eAAe,KAAK,OAAO;AAAA,IAC7B,CAAC;AACD,QAAI,CAAC,OAAO,IAAI;AACd,aAAO,KAAK,eAAe,sBAAsB;AAAA,QAC/C,MAAM,KAAK;AAAA,QACX,QAAS,OAAO,MAAM,MAA0C;AAAA,QAChE,gBAAgB,IAAI;AAAA,QACpB,gBAAgB;AAAA,QAChB,eAAe,IAAI,WAAW;AAAA,QAC9B,eAAe,KAAK,OAAO,aAAa;AAAA,MAC1C,CAAC;AACD,WAAK,OAAO,uBAAuB,MAAM,OAAO,OAAO,CAAC,CAAC,IAAI,MAAM;AACnE,WAAK,sBAAsB,CAAC,GAAG,QAAW,QAAW,OAAO,OAAO,IAAI,CAAC;AACxE;AAAA,IACF;AAEA,UAAM,aAAa,EAAE,iBAAiB,IAAI,GAAG,SAAS,IAAI,SAAS,YAAY,IAAI,WAAW;AAI9F,QAAI,IAAI,aAAa,KAAK,SAAS,UAAU,KAAK,QAAQ,OAAO,IAAI,WAAW;AAC9E,YAAMA,YAAW,KAAK,kBAAkB;AACxC,WAAK,sBAAsB,CAAC,GAAG,KAAK,kBAAkB,GAAG,KAAK,QAAQ,IAAIA,SAAQ;AAClF;AAAA,IACF;AAEA,UAAM,uBAAuB,IAAI;AAEjC,UAAM,EAAE,UAAU,mBAAmB,IAAI,MAAM,KAAK,OAAO;AAAA,MACzD;AAAA,MACA;AAAA,MACA,IAAI;AAAA,MACJ;AAAA,IACF;AAEA,QAAI,CAAC,UAAU;AACb,WAAK,sBAAsB,CAAC,GAAG,QAAW,MAAS;AACnD;AAAA,IACF;AAGA,UAAM,YAAY,gBAAgB;AAClC,UAAM,iBAAiB,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,qBAAqB,GAAG,kBAAkB,CAAC,CAAC;AACnF,UAAM,MAAM,KAAK,OAAO,gBAAgB;AAExC,SAAK,UAAU;AAAA,MACb,IAAI;AAAA,MACJ;AAAA,MACA,aAAa;AAAA,MACb,WAAW,KAAK,IAAI;AAAA,MACpB,WAAW,MAAM,IAAI,KAAK,IAAI,IAAI,MAAM;AAAA,MACxC,QAAQ;AAAA,IACV;AACA,SAAK,qBAAqB,IAAI,IAAI,cAAc;AAIhD,SAAK,6BAA6B;AAGlC,UAAM,WAAW,KAAK,kBAAkB;AAExC,SAAK,sBAAsB,gBAAgB,WAAW,QAAQ;AAAA,EAChE;AAAA;AAAA;AAAA,EAIQ,sBACN,aACA,WACA,UACA,OACA,OACA,SACM;AACN,UAAM,UACJ,OAAO,KAAK,OAAO,cAAc,WAAW,EAAE,IAAI,KAAK,OAAO,UAAU,IAAI;AAC9E,SAAK,UAAU,KAAK;AAAA,MAClB,IAAI;AAAA,MACJ,GAAI,SAAS,QAAQ,QAAQ;AAAA,MAC7B,MAAM;AAAA,MACN,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,iBAAiB,KAAsC;AAEnE,QAAI,CAAC,KAAK,SAAS,QAAQ;AACzB,WAAK,UAAU,IAAI,IAAI,YAAY,eAAe,eAAe;AACjE;AAAA,IACF;AAGA,QAAI,KAAK,QAAQ,YAAY,KAAK,KAAK,IAAI,IAAI,KAAK,QAAQ,WAAW;AACrE,WAAK,cAAc;AACnB,WAAK,UAAU,IAAI,IAAI,YAAY,iBAAiB,iBAAiB;AACrE;AAAA,IACF;AAGA,QAAI,CAAC,KAAK,eAAe,GAAG;AAC1B,WAAK,UAAU,IAAI,IAAI,YAAY,cAAc,mBAAmB;AACpE;AAAA,IACF;AAGA,QAAI,IAAI,WAAW,YAAY,YAAY;AACzC,YAAM,sBAAsB,KAAK;AACjC,WAAK,cAAc;AACnB,WAAK,WAAW,IAAI,IAAI,EAAE,cAAc,KAAK,CAAC;AAC9C,UAAI,uBAAuB,KAAK,OAAO,cAAc;AAEnD,gBAAQ,QAAQ,KAAK,OAAO,aAAa,mBAAmB,CAAC,EAAE,MAAM,CAAC,QAAQ,OAAO,KAAK,WAAW,8BAA8B,GAAG,CAAC;AAAA,MACzI;AACA;AAAA,IACF;AAGA,QAAI,CAAC,oBAAoB,KAAK,oBAAoB,IAAI,MAAM,GAAG;AAC7D,WAAK,UAAU,IAAI,IAAI,YAAY,mBAAmB,yBAAyB,IAAI,MAAM,EAAE;AAC3F;AAAA,IACF;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,cAAc,IAAI,QAAQ,IAAI,UAAU,CAAC,CAAC;AACpE,WAAK,WAAW,IAAI,IAAI,MAAM;AAAA,IAChC,SAAS,OAAO;AACd,WAAK,UAAU,IAAI,IAAI,YAAY,gBAAiB,MAAgB,OAAO;AAAA,IAC7E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,oBAAoB,KAAyC;AAEzE,QAAI,CAAC,KAAK,SAAS,QAAQ;AACzB,WAAK,gBAAgB,IAAI,IAAI,YAAY,eAAe,eAAe;AACvE;AAAA,IACF;AAGA,QAAI,KAAK,QAAQ,YAAY,KAAK,KAAK,IAAI,IAAI,KAAK,QAAQ,WAAW;AACrE,WAAK,cAAc;AACnB,WAAK,gBAAgB,IAAI,IAAI,YAAY,iBAAiB,iBAAiB;AAC3E;AAAA,IACF;AAGA,QAAI,CAAC,oBAAoB,KAAK,oBAAoB,IAAI,MAAM,GAAG;AAC7D,WAAK,gBAAgB,IAAI,IAAI,YAAY,mBAAmB,iCAAiC,IAAI,MAAM,EAAE;AACzG;AAAA,IACF;AAGA,UAAM,cAAc,KAAK,oBAAoB,IAAI,IAAI,MAAM;AAC3D,QAAI,aAAa;AACf,YAAM,eAAe,MAAM,YAAY,IAAI,QAAQ,IAAI,QAAQ,KAAK,OAAO;AAC3E,UAAI,aAAa,OAAO;AACtB,aAAK,gBAAgB,IAAI,IAAI,aAAa,MAAM,MAAM,aAAa,MAAM,OAAO;AAAA,MAClF,OAAO;AACL,aAAK,iBAAiB,IAAI,IAAI,aAAa,MAAM;AAAA,MACnD;AACA;AAAA,IACF;AAGA,UAAM,WAAW,MAAM,KAAK,OAAO,SAAS,IAAI,QAAQ,IAAI,QAAQ,KAAK,OAAO;AAEhF,QAAI,SAAS,OAAO;AAClB,WAAK,gBAAgB,IAAI,IAAI,SAAS,MAAM,MAAM,SAAS,MAAM,OAAO;AAAA,IAC1E,OAAO;AACL,WAAK,iBAAiB,IAAI,IAAI,SAAS,MAAM;AAAA,IAC/C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,cAAc,QAAgB,QAAmD;AAC7F,YAAQ,QAAQ;AAAA,MACd,KAAK,YAAY;AACf,eAAO,KAAK,kBAAkB;AAAA,MAEhC,KAAK,YAAY;AACf,eAAO,KAAK,OAAO,SAAS,WAAW,OAAO,MAA4B;AAAA,MAE5E,KAAK,YAAY;AACf,eAAO,KAAK,OAAO,SAAS,UAAU,OAAO,MAA4B;AAAA,MAE3E,KAAK,YAAY;AACf,eAAO,EAAE,aAAa,MAAM,KAAK,OAAO,SAAS,eAAe,EAAE;AAAA,MAEpE,KAAK,YAAY;AACf,eAAO,KAAK;AAAA,UACV,KAAK,OAAO,SAAS;AAAA,YACnB,OAAO,SAAS,EAAE,QAAQ,OAAO,OAAiB,IAAI;AAAA,UACxD;AAAA,QACF;AAAA,MAEF,KAAK,YAAY;AACf,eAAO,KAAK,OAAO,SAAS,WAAW;AAAA,MAEzC,KAAK,YAAY;AACf,YAAI,CAAC,OAAO,YAAY;AACtB,gBAAM,IAAI,YAAY,0CAA0C,kBAAkB;AAAA,QACpF;AACA,eAAO,KAAK,OAAO,QAAQ,OAAO,UAAoB;AAAA,MAExD,KAAK,YAAY;AACf,eAAO,KAAK,gBAAgB,OAAO,KAAe;AAAA,MAEpD,KAAK,YAAY;AACf,eAAO,KAAK,kBAAkB,OAAO,KAAe;AAAA,MAEtD,KAAK,YAAY,mBAAmB;AAClC,YAAI,CAAC,KAAK,OAAO,eAAgB,OAAM,IAAI,YAAY,uCAAuC,sBAAsB;AACpH,cAAM,SAAS,KAAK,OAAO,eAAe,iBAAiB;AAC3D,cAAM,SAMD,CAAC;AAEN,cAAM,eAA6D,CAAC;AACpE,mBAAW,CAAC,MAAM,QAAQ,KAAK,QAAQ;AACrC,cAAI,SAAS,WAAW,EAAG;AAC3B,gBAAM,OAAO,SAAS,SAAS,SAAS,CAAC;AAEzC,gBAAM,cACJ,SAAS,KAAK,OAAK,EAAE,iBAAiB,QAAQ,EAAE,aAAa,GAAG,iBAC7D,SAAS,KAAK,OAAK,EAAE,oBAAoB,QAAQ,EAAE,gBAAgB,GAAG;AAC3E,gBAAM,MAAM,OAAO;AACnB,iBAAO,KAAK;AAAA,YACV,YAAY;AAAA,YACZ;AAAA,YACA,aAAa;AAAA,YACb,aAAa,KAAK,OAAO,eAAe,eAAe,IAAI;AAAA,YAC3D,cAAc,SAAS;AAAA,UACzB,CAAC;AACD,cAAI,CAAC,aAAa;AAChB,yBAAa,KAAK,EAAE,OAAO,KAAK,YAAY,KAAK,CAAC;AAAA,UACpD;AAAA,QACF;AAEA,YAAI,aAAa,SAAS,GAAG;AAC3B,gBAAM,WAAW,MAAM,QAAQ;AAAA,YAC7B,aAAa;AAAA,cAAI,CAAC,EAAE,WAAW,MAC7B,KAAK,OAAO,eAAgB,mBAAmB,UAAU,EAAE,MAAM,CAAC,QAAQ;AAAE,uBAAO,MAAM,WAAW,qCAAqC,GAAG;AAAG,uBAAO;AAAA,cAAW,CAAC;AAAA,YACpK;AAAA,UACF;AACA,mBAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;AAC5C,gBAAI,SAAS,CAAC,GAAG;AACf,qBAAO,aAAa,CAAC,EAAE,KAAK,EAAE,cAAc,SAAS,CAAC;AAAA,YACxD;AAAA,UACF;AAAA,QACF;AACA,eAAO,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,YAAY,EAAE,YAAY,SAAS;AACvE,eAAO;AAAA,MACT;AAAA,MAEA,KAAK,YAAY,cAAc;AAC7B,YAAI,CAAC,KAAK,OAAO,eAAgB,OAAM,IAAI,YAAY,uCAAuC,sBAAsB;AACpH,YAAI,CAAC,OAAO,WAAY,OAAM,IAAI,YAAY,0CAA0C,kBAAkB;AAC1G,eAAO,KAAK,OAAO,eAAe;AAAA,UAChC,OAAO;AAAA,UACP;AAAA,YACE,OAAO,OAAO;AAAA,YACd,QAAQ,OAAO;AAAA,UACjB;AAAA,QACF;AAAA,MACF;AAAA,MAEA,KAAK,YAAY,qBAAqB;AACpC,YAAI,CAAC,KAAK,OAAO,eAAgB,OAAM,IAAI,YAAY,uCAAuC,sBAAsB;AACpH,eAAO;AAAA,UACL,aAAa,KAAK,OAAO,eAAe;AAAA,YACtC,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,MAEA,KAAK,YAAY,cAAc;AAC7B,YAAI,CAAC,KAAK,OAAO,eAAgB,OAAM,IAAI,YAAY,uCAAuC,sBAAsB;AACpH,YAAI,CAAC,OAAO,cAAc,CAAC,MAAM,QAAQ,OAAO,UAAU,GAAG;AAC3D,gBAAM,IAAI,YAAY,qDAAqD,kBAAkB;AAAA,QAC/F;AACA,cAAM,KAAK,OAAO,eAAe,WAAW,OAAO,UAAsB;AACzE,eAAO,EAAE,QAAQ,MAAM,OAAQ,OAAO,WAAwB,OAAO;AAAA,MACvE;AAAA,MAEA,KAAK,YAAY,cAAc;AAC7B,YAAI,CAAC,KAAK,OAAO,WAAY,OAAM,IAAI,YAAY,mCAAmC,sBAAsB;AAG5G,cAAM,cAAuC,CAAC;AAC9C,YAAI,OAAO,UAAU,OAAW,aAAY,QAAQ,OAAO;AAC3D,YAAI,OAAO,UAAU,OAAW,aAAY,QAAQ,OAAO;AAC3D,YAAI,OAAO,WAAW,OAAW,aAAY,SAAS,OAAO;AAC7D,YAAI,OAAO,WAAW,OAAW,aAAY,SAAS,OAAO;AAC7D,YAAI,OAAO,cAAc,OAAW,aAAY,YAAY,OAAO;AACnE,YAAI,OAAO,gBAAgB,OAAW,aAAY,cAAc,OAAO;AACvE,YAAI,OAAO,gBAAgB,OAAW,aAAY,cAAc,OAAO;AACvE,eAAO,KAAK,OAAO,WAAW,YAAY,WAAW;AAAA,MACvD;AAAA,MAEA,KAAK,YAAY,oBAAoB;AACnC,YAAI,CAAC,KAAK,OAAO,WAAY,OAAM,IAAI,YAAY,mCAAmC,sBAAsB;AAC5G,YAAI,CAAC,OAAO,aAAa,OAAO,OAAO,cAAc,UAAU;AAC7D,gBAAM,IAAI,YAAY,yCAAyC,kBAAkB;AAAA,QACnF;AACA,eAAO,KAAK,OAAO,WAAW,iBAAiB,OAAO,SAAmB;AAAA,MAC3E;AAAA,MAEA;AACE,cAAM,IAAI,YAAY,mBAAmB,MAAM,IAAI,kBAAkB;AAAA,IACzE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,+BAAqC;AAC3C,QAAI,KAAK,mBAAmB,IAAI,cAAc,gBAAgB,EAAG;AACjE,UAAM,QAAQ,KAAK,OAAO,GAAG,oBAAuC,CAAC,SAAkB;AACrF,WAAK,gBAAgB,cAAc,kBAAkB,IAAI;AAAA,IAC3D,CAAC;AACD,SAAK,mBAAmB,IAAI,cAAc,kBAAkB,KAAK;AAAA,EACnE;AAAA,EAEQ,gBAAgB,WAA2D;AACjF,QAAI,CAAC,UAAW,OAAM,IAAI,YAAY,qCAAqC,kBAAkB;AAE7F,QAAI,KAAK,mBAAmB,IAAI,SAAS,GAAG;AAC1C,aAAO,EAAE,YAAY,MAAM,OAAO,UAAU;AAAA,IAC9C;AAEA,UAAM,QAAQ,KAAK,OAAO,GAAG,WAA8B,CAAC,SAAkB;AAC5E,WAAK,UAAU,KAAK;AAAA,QAClB,IAAI;AAAA,QACJ,GAAG;AAAA,QACH,MAAM;AAAA,QACN,OAAO;AAAA,QACP;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,SAAK,mBAAmB,IAAI,WAAW,KAAK;AAC5C,WAAO,EAAE,YAAY,MAAM,OAAO,UAAU;AAAA,EAC9C;AAAA,EAEQ,kBAAkB,WAA6D;AACrF,QAAI,CAAC,UAAW,OAAM,IAAI,YAAY,qCAAqC,kBAAkB;AAE7F,UAAM,QAAQ,KAAK,mBAAmB,IAAI,SAAS;AACnD,QAAI,OAAO;AACT,YAAM;AACN,WAAK,mBAAmB,OAAO,SAAS;AAAA,IAC1C;AACA,WAAO,EAAE,cAAc,MAAM,OAAO,UAAU;AAAA,EAChD;AAAA,EAEQ,4BAAkC;AACxC,eAAW,CAAC,EAAE,KAAK,KAAK,KAAK,oBAAoB;AAC/C,YAAM;AAAA,IACR;AACA,SAAK,mBAAmB,MAAM;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,gBAAgB,OAAe,MAAqB;AAC1D,SAAK,UAAU,KAAK;AAAA,MAClB,IAAI;AAAA,MACJ,GAAG;AAAA,MACH,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,oBAAgD;AACtD,UAAM,KAAK,KAAK,OAAO;AACvB,QAAI,CAAC,GAAI,QAAO;AAChB,WAAO;AAAA,MACL,aAAa,GAAG;AAAA,MAChB,eAAe,GAAG;AAAA,MAClB,SAAS,GAAG;AAAA,IACd;AAAA,EACF;AAAA,EAEQ,kBAAkB,QAA8B;AACtD,WAAO,OAAO,IAAI,CAAC,MAAM;AACvB,YAAM,QAAQ;AAEd,YAAM,EAAE,SAAS,UAAU,GAAG,aAAa,IAAI;AAC/C,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEQ,WAAW,IAAY,QAAuB;AACpD,SAAK,UAAU,KAAK;AAAA,MAClB,IAAI;AAAA,MACJ,GAAG;AAAA,MACH,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,UAAU,IAAY,MAAc,SAAuB;AACjE,SAAK,UAAU,KAAK;AAAA,MAClB,IAAI;AAAA,MACJ,GAAG;AAAA,MACH,MAAM;AAAA,MACN;AAAA,MACA,OAAO,EAAE,MAAM,QAAQ;AAAA,IACzB,CAAC;AAAA,EACH;AAAA,EAEQ,iBAAiB,IAAY,QAAuB;AAC1D,SAAK,UAAU,KAAK;AAAA,MAClB,IAAI;AAAA,MACJ,GAAG;AAAA,MACH,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,gBAAgB,IAAY,MAAc,SAAuB;AACvE,SAAK,UAAU,KAAK;AAAA,MAClB,IAAI;AAAA,MACJ,GAAG;AAAA,MACH,MAAM;AAAA,MACN;AAAA,MACA,OAAO,EAAE,MAAM,QAAQ;AAAA,IACzB,CAAC;AAAA,EACH;AAAA,EAEQ,iBAA0B;AAChC,UAAM,SAAS,KAAK,OAAO,wBAAwB;AACnD,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,MAAM,KAAK,kBAAkB;AAC/B,WAAK,mBAAmB;AACxB,WAAK,mBAAmB,MAAM;AAAA,IAChC;AACA,SAAK;AACL,WAAO,KAAK,oBAAoB;AAAA,EAClC;AACF;;;ACvqBO,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtC,YAAY,SAAiC,MAA8B,MAAgB;AACzF,UAAM,OAAO;AAD8B;AAA8B;AAEzE,SAAK,OAAO;AAAA,EACd;AACF;AAEA,IAAM,kBAAkB;AACxB,IAAM,yBAAyB;AAQxB,IAAM,gBAAN,MAAoB;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAA2B;AAAA,EAC3B,qBAAwC,CAAC;AAAA,EACzC,WAAkC;AAAA,EAClC,YAAgC;AAAA,EAChC,YAAY;AAAA,EAEZ,kBAA+C,oBAAI,IAAI;AAAA,EACvD,gBAAuD,oBAAI,IAAI;AAAA,EAC/D,uBAA4C;AAAA;AAAA,EAG5C,oBAIG;AAAA,EAEX,YAAY,QAA6B;AACvC,SAAK,YAAY,OAAO;AACxB,SAAK,OAAO,OAAO;AACnB,SAAK,uBAAuB,OAAO,eAAe,CAAC,GAAG,eAAe;AACrE,SAAK,UAAU,OAAO,WAAW;AACjC,SAAK,gBAAgB,OAAO,iBAAiB;AAC7C,SAAK,kBAAkB,OAAO,mBAAmB;AACjD,SAAK,SAAS,OAAO,UAAU;AAC/B,SAAK,UAAU,OAAO;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,UAAkC;AAEtC,SAAK,uBAAuB,KAAK,UAAU,UAAU,KAAK,cAAc,KAAK,IAAI,CAAC;AAElF,WAAO,IAAI,QAAuB,CAAC,SAAS,WAAW;AACrD,YAAM,QAAQ,WAAW,MAAM;AAC7B,aAAK,oBAAoB;AACzB,eAAO,IAAI,MAAM,oBAAoB,CAAC;AAAA,MACxC,GAAG,KAAK,OAAO;AAEf,WAAK,oBAAoB,EAAE,SAAS,QAAQ,MAAM;AAGlD,WAAK,UAAU,KAAK;AAAA,QAClB,IAAI;AAAA,QACJ,GAAG;AAAA,QACH,MAAM;AAAA,QACN,WAAW;AAAA,QACX,aAAa,KAAK;AAAA,QAClB,MAAM,KAAK;AAAA,QACX,YAAY;AAAA,QACZ,GAAI,KAAK,UAAU,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;AAAA,QAChD,GAAI,KAAK,kBAAkB,EAAE,WAAW,KAAK,gBAAgB,IAAI,CAAC;AAAA,QAClE,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC;AAAA,MACxC,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,aAA4B;AAChC,QAAI,KAAK,WAAW;AAClB,UAAI;AACF,cAAM,KAAK,MAAM,YAAY,UAAU;AAAA,MACzC,QAAQ;AAAA,MAER;AAAA,IACF;AACA,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA,EAGA,IAAI,cAAuB;AACzB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,cAA0C;AAC5C,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,UAAyB;AAC3B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,iBAAwC;AAC1C,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,gBAAoC;AACtC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,MAAmB,QAAgB,QAA8C;AACrF,QAAI,CAAC,KAAK,UAAW,OAAM,IAAI,YAAY,iBAAiB,iBAAiB;AAE7E,UAAM,KAAK,gBAAgB;AAE3B,WAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,YAAM,QAAQ,WAAW,MAAM;AAC7B,aAAK,gBAAgB,OAAO,EAAE;AAC9B,eAAO,IAAI,MAAM,kBAAkB,MAAM,EAAE,CAAC;AAAA,MAC9C,GAAG,KAAK,OAAO;AAEf,WAAK,gBAAgB,IAAI,IAAI;AAAA,QAC3B;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,WAAK,UAAU,KAAK;AAAA,QAClB,IAAI;AAAA,QACJ,GAAG;AAAA,QACH,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAoB,QAAgB,QAA6C;AACrF,QAAI,CAAC,KAAK,UAAW,OAAM,IAAI,YAAY,iBAAiB,iBAAiB;AAE7E,UAAM,KAAK,gBAAgB;AAE3B,WAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,YAAM,QAAQ,WAAW,MAAM;AAC7B,aAAK,gBAAgB,OAAO,EAAE;AAC9B,eAAO,IAAI,MAAM,mBAAmB,MAAM,EAAE,CAAC;AAAA,MAC/C,GAAG,KAAK,aAAa;AAErB,WAAK,gBAAgB,IAAI,IAAI;AAAA,QAC3B;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,WAAK,UAAU,KAAK;AAAA,QAClB,IAAI;AAAA,QACJ,GAAG;AAAA,QACH,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,GAAG,OAAe,SAA0C;AAC1D,QAAI,CAAC,KAAK,cAAc,IAAI,KAAK,GAAG;AAClC,WAAK,cAAc,IAAI,OAAO,oBAAI,IAAI,CAAC;AAEvC,UAAI,KAAK,WAAW;AAClB,aAAK,MAAM,YAAY,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,QAAQ,OAAO,MAAM,WAAW,6BAA6B,GAAG,CAAC;AAAA,MACvH;AAAA,IACF;AACA,SAAK,cAAc,IAAI,KAAK,EAAG,IAAI,OAAO;AAE1C,WAAO,MAAM;AACX,YAAM,WAAW,KAAK,cAAc,IAAI,KAAK;AAC7C,UAAI,UAAU;AACZ,iBAAS,OAAO,OAAO;AACvB,YAAI,SAAS,SAAS,GAAG;AACvB,eAAK,cAAc,OAAO,KAAK;AAC/B,cAAI,KAAK,WAAW;AAClB,iBAAK,MAAM,YAAY,aAAa,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,QAAQ,OAAO,MAAM,WAAW,+BAA+B,GAAG,CAAC;AAAA,UAC3H;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,cAAc,KAAiC;AAErD,QAAI,IAAI,SAAS,eAAe,IAAI,cAAc,YAAY;AAC5D,WAAK,wBAAwB,GAAG;AAChC;AAAA,IACF;AAGA,QAAI,IAAI,SAAS,YAAY;AAC3B,WAAK,sBAAsB,IAAI,IAAI,IAAI,QAAQ,IAAI,KAAK;AACxD;AAAA,IACF;AAGA,QAAI,IAAI,SAAS,iBAAiB;AAChC,WAAK,sBAAsB,IAAI,IAAI,IAAI,QAAQ,IAAI,KAAK;AACxD;AAAA,IACF;AAGA,QAAI,IAAI,SAAS,SAAS;AACxB,YAAM,WAAW,KAAK,cAAc,IAAI,IAAI,KAAK;AACjD,UAAI,UAAU;AACZ,mBAAW,WAAW,UAAU;AAC9B,cAAI;AACF,oBAAQ,IAAI,IAAI;AAAA,UAClB,SAAS,KAAK;AACZ,mBAAO,MAAM,WAAW,uBAAuB,GAAG;AAAA,UACpD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,wBAAwB,KAAyD;AACvF,QAAI,CAAC,KAAK,kBAAmB;AAE7B,iBAAa,KAAK,kBAAkB,KAAK;AAEzC,UAAM,IAAI;AAEV,QAAI,EAAE,OAAO;AACX,WAAK,kBAAkB,OAAO,IAAI,aAAa,EAAE,MAAM,SAAS,EAAE,MAAM,MAAM,EAAE,MAAM,IAAI,CAAC;AAC3F,WAAK,oBAAoB;AACzB;AAAA,IACF;AAEA,QAAI,IAAI,aAAa,IAAI,UAAU;AACjC,WAAK,YAAY,IAAI;AACrB,WAAK,qBAAqB,IAAI;AAC9B,WAAK,WAAW,IAAI;AACpB,WAAK,YAAY,EAAE,WAAW;AAC9B,WAAK,YAAY;AACjB,UAAI,EAAE,QAAS,QAAO,KAAK,WAAW,6BAA6B,EAAE,QAAQ,OAAO;AACpF,WAAK,kBAAkB,QAAQ;AAAA,QAC7B,WAAW,IAAI;AAAA,QACf,aAAa,KAAK;AAAA,QAClB,UAAU,IAAI;AAAA,MAChB,CAAC;AAAA,IACH,OAAO;AACL,WAAK,kBAAkB,OAAO,IAAI,MAAM,+BAA+B,CAAC;AAAA,IAC1E;AAEA,SAAK,oBAAoB;AAAA,EAC3B;AAAA,EAEQ,sBACN,IACA,QACA,OACM;AACN,UAAM,UAAU,KAAK,gBAAgB,IAAI,EAAE;AAC3C,QAAI,CAAC,QAAS;AAEd,iBAAa,QAAQ,KAAK;AAC1B,SAAK,gBAAgB,OAAO,EAAE;AAE9B,QAAI,OAAO;AACT,cAAQ,OAAO,IAAI,aAAa,MAAM,SAAS,MAAM,MAAM,MAAM,IAAI,CAAC;AAAA,IACxE,OAAO;AACL,cAAQ,QAAQ,MAAM;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,UAAgB;AACtB,QAAI,KAAK,sBAAsB;AAC7B,WAAK,qBAAqB;AAC1B,WAAK,uBAAuB;AAAA,IAC9B;AAGA,eAAW,CAAC,EAAE,OAAO,KAAK,KAAK,iBAAiB;AAC9C,mBAAa,QAAQ,KAAK;AAC1B,cAAQ,OAAO,IAAI,MAAM,cAAc,CAAC;AAAA,IAC1C;AACA,SAAK,gBAAgB,MAAM;AAC3B,SAAK,cAAc,MAAM;AAEzB,SAAK,YAAY;AACjB,SAAK,YAAY;AACjB,SAAK,qBAAqB,CAAC;AAC3B,SAAK,WAAW;AAChB,SAAK,YAAY;AAAA,EACnB;AACF;","names":["identity"]}