@unicitylabs/sphere-sdk 0.17.6 → 0.18.0-dev.2

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.
Files changed (44) hide show
  1. package/dist/connect/chunks/{chunk-T4DZCXWS.js → chunk-FGQ6CKKK.js} +2 -2
  2. package/dist/connect/chunks/{chunk-T4DZCXWS.js.map → chunk-FGQ6CKKK.js.map} +1 -1
  3. package/dist/connect/chunks/{chunk-BRYRG2TL.js → chunk-JYBI36GK.js} +2 -2
  4. package/dist/connect/chunks/{chunk-MA32MTCD.js → chunk-OAAY2JEA.js} +2 -2
  5. package/dist/connect/index.cjs +1 -1
  6. package/dist/connect/index.cjs.map +1 -1
  7. package/dist/connect/index.js +3 -3
  8. package/dist/connect/internal/host.js +2 -2
  9. package/dist/core/index.cjs +304 -47
  10. package/dist/core/index.cjs.map +1 -1
  11. package/dist/core/index.d.cts +392 -320
  12. package/dist/core/index.d.ts +392 -320
  13. package/dist/core/index.js +304 -47
  14. package/dist/core/index.js.map +1 -1
  15. package/dist/impl/browser/connect/index.cjs +1 -1
  16. package/dist/impl/browser/connect/index.cjs.map +1 -1
  17. package/dist/impl/browser/connect/index.js +2 -2
  18. package/dist/impl/wallet-api-v2/index.cjs +2 -0
  19. package/dist/impl/wallet-api-v2/index.cjs.map +1 -1
  20. package/dist/impl/wallet-api-v2/index.d.cts +30 -30
  21. package/dist/impl/wallet-api-v2/index.d.ts +30 -30
  22. package/dist/impl/wallet-api-v2/index.js +2 -0
  23. package/dist/impl/wallet-api-v2/index.js.map +1 -1
  24. package/dist/index.cjs +304 -47
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +279 -154
  27. package/dist/index.d.ts +279 -154
  28. package/dist/index.js +304 -47
  29. package/dist/index.js.map +1 -1
  30. package/dist/modules/payments-v2/index.cjs +249 -28
  31. package/dist/modules/payments-v2/index.cjs.map +1 -1
  32. package/dist/modules/payments-v2/index.d.cts +252 -166
  33. package/dist/modules/payments-v2/index.d.ts +252 -166
  34. package/dist/modules/payments-v2/index.js +249 -28
  35. package/dist/modules/payments-v2/index.js.map +1 -1
  36. package/dist/token-engine/index.cjs +455 -427
  37. package/dist/token-engine/index.cjs.map +1 -1
  38. package/dist/token-engine/index.d.cts +26 -1
  39. package/dist/token-engine/index.d.ts +26 -1
  40. package/dist/token-engine/index.js +455 -427
  41. package/dist/token-engine/index.js.map +1 -1
  42. package/package.json +1 -1
  43. /package/dist/connect/chunks/{chunk-BRYRG2TL.js.map → chunk-JYBI36GK.js.map} +0 -0
  44. /package/dist/connect/chunks/{chunk-MA32MTCD.js.map → chunk-OAAY2JEA.js.map} +0 -0
@@ -575,7 +575,7 @@ var logger = {
575
575
  };
576
576
 
577
577
  // connect/version.ts
578
- var SDK_VERSION = "0.17.6";
578
+ var SDK_VERSION = "0.18.0-dev.2";
579
579
 
580
580
  // connect/permissions.ts
581
581
  var PERMISSION_SCOPES = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../impl/browser/connect/index.ts","../../../../connect/semver.ts","../../../../constants.ts","../../../../connect/protocol.ts","../../../../impl/browser/connect/PostMessageTransport.ts","../../../../impl/browser/connect/ExtensionTransport.ts","../../../../core/logger.ts","../../../../connect/version.ts","../../../../connect/permissions.ts","../../../../connect/client/ConnectClient.ts","../../../../impl/browser/connect/autoConnect.ts"],"sourcesContent":["export { PostMessageTransport } from './PostMessageTransport';\nexport type { PostMessageHostOptions, PostMessageClientOptions } from './PostMessageTransport';\n\nexport { ExtensionTransport, EXT_MSG_TO_HOST, EXT_MSG_TO_CLIENT, isExtensionConnectEnvelope } from './ExtensionTransport';\nexport type { ExtensionConnectEnvelope, ChromeMessagingApi } from './ExtensionTransport';\n\nexport { autoConnect, detectTransport, isInIframe, hasExtension } from './autoConnect';\nexport type { AutoConnectConfig, AutoConnectResult, DetectedTransport } from './autoConnect';\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 * Legacy key: Sphere's private decrypt() tries it only as a fallback for wallets written by old\n * SDK versions when no password is given. Nothing is encrypted with it; without a password the\n * mnemonic or master key is stored as plaintext.\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 is server custody (wallet-api backend), not here; the\n * payments vertical's durable client state self-prefixes `pv2g2:{network}:{pubkey}:`.\n */\nexport const STORAGE_KEYS_ADDRESS = {\n /** Transfer outbox for this address (pre-flip key name; kept as the network-scoping witness) */\n OUTBOX: 'outbox',\n /** Conversations for this address */\n CONVERSATIONS: 'conversations',\n /** Messages for this address */\n MESSAGES: 'messages',\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 /** Auto-return settings (pre-flip key name; kept as the network-scoping witness) */\n AUTO_RETURN: 'auto_return',\n /** Auto-return dedup ledger (pre-flip key name; kept as the network-scoping witness) */\n AUTO_RETURN_LEDGER: 'auto_return_ledger',\n /** Per-swap key prefix (pre-flip key name; kept as the network-scoping witness) */\n SWAP_RECORD_PREFIX: 'swap:',\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. Chat/identity per-address keys (CONVERSATIONS/MESSAGES/\n * GROUP_CHAT_*) are network-AGNOSTIC and deliberately NOT listed. The payments vertical\n * self-prefixes `pv2g2:{network}:{pubkey}:` and never rides this mechanism; the entries left\n * here are the pre-flip key names the network-isolation tests pin the provider behavior\n * with (P11 stage-2 note: the full isNetworkScopedAddressKey cut needs an owner call on\n * those tests first).\n */\nconst NETWORK_SCOPED_ADDRESS_KEYS: readonly string[] = [\n STORAGE_KEYS_ADDRESS.OUTBOX,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN_LEDGER,\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/**\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/**\n * Fallback Nostr relays, used only when no relay list is configured\n * (NostrTransportProvider / MultiAddressTransportMux). `relay.unicity.network`\n * was the v1 relay and no longer resolves, so it is not listed here.\n */\nexport const DEFAULT_NOSTR_RELAYS = [\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 /** 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 request timeout (ms)\n * Note: The aggregator is conceptually an oracle - a trusted service that provides\n * verifiable truth about token state through cryptographic inclusion proofs.\n * There is no default aggregator URL: every live network names its own gateway.\n */\nexport const DEFAULT_AGGREGATOR_TIMEOUT = 30000;\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/** 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 groupRelays: readonly string[];\n readonly tokenRegistryUrl: string;\n /** The NFT vessel token type every NFT mints under (#785), 64 lowercase hex: the single\n * `non-fungible` entry of this network's unicity-ids registry. */\n readonly nftTokenType: 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 networkId: 1,\n // v3 state-transition gateway (networkId 1 comes from the trust base). apiKey is env-injected;\n // unlike testnet2's, a mainnet gateway key is a SECRET — never commit one.\n aggregatorUrl: 'https://gateway.mainnet.unicity.network',\n // Mainnet has no relay of its own yet — it shares the testnet relay until one is stood up.\n // Consequence while shared: nametag bindings for both networks live in ONE namespace, and\n // since bindings carry no network the cross-network recipient guard can only signal (#734).\n nostrRelays: TEST_NOSTR_RELAYS,\n groupRelays: DEFAULT_GROUP_RELAYS,\n // Published, but currently only the non-fungible base token type — no fungible\n // coins yet, so those still miss the registry (decimals 0, symbol falls back to\n // six hex chars). Presentation only: the money path treats coinId as opaque bytes.\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.mainnet.json',\n nftTokenType: '9f190eea6c8d7e1e564c35feb4c289add78be4bedb81bb77fe265e926e5493f4',\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: 'Testnet',\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 groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n nftTokenType: '971a26eef0e3aeb22bd3e7d44c47ce963400037e8df42b50d4d44e1589f83826',\n },\n testnet2: {\n name: 'Testnet',\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 groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n nftTokenType: '971a26eef0e3aeb22bd3e7d44c47ce963400037e8df42b50d4d44e1589f83826',\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 mainnet: { id: NETWORKS.mainnet.networkId as number, name: 'mainnet' },\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 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.3'; // Connect protocol version (semver MAJOR.MINOR)\n\n// Default npm-SDK floor a host enforces at the handshake (0.14.1 = the P11 flip:\n// the v1 payments era is gone; pre-flip ConnectClients expect a wallet that no\n// longer exists). '-0' admits every 0.14.1 prerelease (compareSemver: release >\n// prerelease). Override via ConnectHostConfig.minSdkVersion; the claim is\n// compatibility hygiene, not security — a hostile client can lie about it.\nexport const DEFAULT_MIN_CLIENT_SDK_VERSION = '0.14.1-0';\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} 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 MINT: 'mint',\n // #777: params { to, tokenId, memo? }. Named NFT rather than 'send_token':\n // coins are tokens too, so 'token' does not say which kind moves.\n SEND_NFT: 'send_nft',\n // Connect 2.3: params MintNftIntentParams, result MintNftIntentResult (connect/nft-wire.ts).\n MINT_NFT: 'mint_nft',\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 // Wallet locked; THE SESSION IS STILL ALIVE. A QUERY may be retried after wallet:unlocked.\n // An INTENT already delegated to the wallet is NEVER answered with this code — it gets\n // INTENT_OUTCOME_UNKNOWN (4201) instead, because a retry could double-spend.\n WALLET_LOCKED: 4009,\n INSUFFICIENT_BALANCE: 4100,\n INVALID_RECIPIENT: 4101,\n TRANSFER_FAILED: 4102,\n INTENT_CANCELLED: 4200,\n /**\n * The intent was DELEGATED to the wallet and the host lost track of the answer — a host\n * deadline fired, or the wallet locked / logged out mid-flight. **The outcome is UNKNOWN:\n * the money may or may not have moved.**\n *\n * A dApp MUST NOT retry on this code. Reconcile out of band (poll the recipient, the\n * aggregator, or your own backend) and only then decide.\n *\n * This code exists because every other answer would be a lie. `INTENT_CANCELLED` (4200)\n * asserts the user declined and nothing happened; `WALLET_LOCKED` (4009) invites a retry\n * after the unlock. Sending either for an intent the wallet had already submitted is how a\n * paid-but-not-credited order — and then a double spend on retry — happens.\n */\n INTENT_OUTCOME_UNKNOWN: 4201,\n} as const;\n\nexport type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];\n\n/** `data` carried by every WALLET_LOCKED (4009) refusal. */\nexport interface WalletLockedData {\n /** Discriminator. Always the literal 'locked' — reserved for future refusal reasons. */\n readonly reason: 'locked';\n /**\n * Whether the wallet's own unlock surface is currently on screen.\n * DECLARED in the fail-fast release and always absent there; POPULATED in the resume\n * release from ConnectHostConfig.unlockSurface(), evaluated at refusal time (visibility\n * changes over time, so a static value would lie). Feeds\n * ConnectClientConfig.onWalletAttention.\n */\n readonly unlockSurface?: 'visible' | 'background';\n}\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 /** Response only: the wallet is LOCKED and the session is ALIVE. The dApp is connected\n * and must not re-handshake; it will receive `wallet:unlocked` on the same session.\n * Additive and safe for old clients: handleHandshakeResponse reads only sessionId,\n * permissions, identity, network, warning and error and ignores unknown fields. */\n readonly locked?: boolean;\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 is LOCKED — the session is STILL ALIVE. Requests are answered\n * WALLET_LOCKED (4009) until `wallet:unlocked`. The dApp must NOT disconnect,\n * must NOT clear its sessionId, and must NOT re-handshake.\n * Payload: {@link WalletLockedPayload}. Pushed by ConnectHost.setLocked() and\n * immediately after a handshake response carrying `locked: true`. */\n LOCKED: 'wallet:locked',\n /** Wallet was unlocked — the SAME session continues: no re-handshake, no re-approval,\n * no re-subscribe (the host re-arms the dApp's subscriptions before pushing this).\n * Payload: {@link WalletUnlockedPayload} — carries the CURRENT identity, which may\n * differ from the one the dApp connected with. Pushed by ConnectHost.updateSphere()\n * on the locked -> live edge only. */\n UNLOCKED: 'wallet:unlocked',\n /** The session is GONE (logout, wallet deleted, dApp sphere_disconnect, expiry seen at\n * unlock, a different seed behind the lock screen, host destroy).\n * The dApp must clear its session and re-handshake to continue. Unlocking does not cure it.\n * Payload: {@link WalletDisconnectedPayload}. Pushed by ConnectHost.revokeSession(). */\n DISCONNECTED: 'wallet:disconnected',\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/** Payload of {@link WALLET_EVENTS.LOCKED}. Intentionally empty — matches today's push,\n * so an old dApp sees no shape change. */\nexport type WalletLockedPayload = Record<string, never>;\n\n/** Payload of {@link WALLET_EVENTS.DISCONNECTED}. Intentionally empty. */\nexport type WalletDisconnectedPayload = Record<string, never>;\n\n/** Payload of {@link WALLET_EVENTS.UNLOCKED}. */\nexport interface WalletUnlockedPayload {\n /** The wallet's public identity at unlock time. Absent when the rebound Sphere has no\n * identity yet (JSON transports drop undefined keys).\n * Unlock is NOT implicitly the same wallet: the lock screen's \"Forgot password ->\n * restore from recovery phrase\" installs a DIFFERENT seed while origin approvals are\n * keyed by origin alone. The host's own lock-edge guard is authoritative (it revokes\n * instead of pushing this event on a mismatch); this field is what lets a dApp render\n * honestly and what the client-side retry queue checks before draining. */\n readonly identity?: PublicIdentity;\n}\n\n/** Payload of {@link WALLET_EVENTS.IDENTITY_CHANGED} — unchanged: the host forwards\n * Sphere's own event data verbatim and pushes a PublicIdentity from updateSphere().\n * Typed as the union for honesty. */\nexport type WalletIdentityChangedPayload = PublicIdentity | unknown;\n\n/** Events the host pushes unconditionally. They must NEVER be routed through\n * `sphere_subscribe`: Sphere.on() accepts any string and would silently never emit,\n * so the subscribe would succeed and deliver nothing forever. */\nexport const AUTO_PUSHED_EVENTS: readonly WalletEvent[] = [\n WALLET_EVENTS.LOCKED,\n WALLET_EVENTS.UNLOCKED,\n WALLET_EVENTS.DISCONNECTED,\n WALLET_EVENTS.IDENTITY_CHANGED,\n];\n\n/** True for an event the host pushes unconditionally (see {@link AUTO_PUSHED_EVENTS}). */\nexport function isAutoPushedEvent(event: string): event is WalletEvent {\n return (AUTO_PUSHED_EVENTS as readonly string[]).includes(event);\n}\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","/**\n * PostMessageTransport — Browser transport for Sphere Connect.\n *\n * Two modes:\n * - iframe: wallet (parent) ↔ dApp (iframe child)\n * - popup: dApp (opener) ↔ wallet (popup window)\n */\n\nimport type { ConnectTransport } from '../../../connect/types';\nimport type { SphereConnectMessage } from '../../../connect/protocol';\nimport { isSphereConnectMessage } from '../../../connect/protocol';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface PostMessageHostOptions {\n /** Allowed origins for incoming messages. Use ['*'] only in development. */\n allowedOrigins: string[];\n}\n\nexport interface PostMessageClientOptions {\n /** Target window to send messages to. Defaults to window.parent (iframe mode). */\n target?: Window;\n /** Target origin for postMessage. Default: '*'. Should be set to wallet origin. */\n targetOrigin?: string;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nconst POPUP_CLOSE_CHECK_INTERVAL = 1000;\n\n/**\n * The origin of a caller-supplied target URL, as a single-entry allow-list. `null` (no filter)\n * for `'*'` and for anything that will not parse.\n */\nfunction originOf(targetOrigin: string): string[] | null {\n if (targetOrigin === '*') return null;\n try {\n return [new URL(targetOrigin).origin];\n } catch {\n return null;\n }\n}\n\nexport class PostMessageTransport implements ConnectTransport {\n private readonly targetWindow: Window;\n private readonly targetOrigin: string;\n private readonly allowedOrigins: Set<string> | null;\n private handlers: Set<(message: SphereConnectMessage) => void> = new Set();\n private listener: ((event: MessageEvent) => void) | null = null;\n private popupCheckInterval: ReturnType<typeof setInterval> | null = null;\n private onPopupClosed: (() => void) | null = null;\n\n private constructor(\n targetWindow: Window,\n targetOrigin: string,\n allowedOrigins: string[] | null,\n ) {\n this.targetWindow = targetWindow;\n this.targetOrigin = targetOrigin;\n this.allowedOrigins = allowedOrigins ? new Set(allowedOrigins) : null;\n\n // Listen for incoming messages\n this.listener = (event: MessageEvent) => {\n // WINDOW check, first and unconditional. `event.origin` alone cannot tell the wallet\n // window from any OTHER frame on the same origin — an injected same-origin iframe passes\n // an origin allow-list, and in iframe mode there is no allow-list at all because the\n // client defaults to `'*'`. Only the window we are actually talking to may speak to us.\n //\n // Guarded on `event.source` being present: some environments (and jsdom in tests) deliver\n // synthetic events without one, and dropping those would break every existing consumer.\n if (event.source && event.source !== this.targetWindow) {\n return;\n }\n\n // Origin check (host mode, and client mode when a concrete targetOrigin was given)\n if (this.allowedOrigins && !this.allowedOrigins.has('*') && !this.allowedOrigins.has(event.origin)) {\n return;\n }\n\n // Namespace filter\n if (!isSphereConnectMessage(event.data)) {\n return;\n }\n\n for (const handler of this.handlers) {\n try {\n handler(event.data);\n } catch {\n // Ignore handler errors\n }\n }\n };\n\n window.addEventListener('message', this.listener);\n }\n\n // ===========================================================================\n // Factory Methods\n // ===========================================================================\n\n /**\n * Create transport for the HOST side (wallet).\n *\n * iframe mode: target = iframe.contentWindow\n * popup mode: target = window.opener\n */\n static forHost(\n target: HTMLIFrameElement | Window,\n options: PostMessageHostOptions,\n ): PostMessageTransport {\n const targetWindow = target instanceof HTMLIFrameElement\n ? target.contentWindow!\n : target;\n const targetOrigin = options.allowedOrigins[0] === '*' ? '*' : options.allowedOrigins[0];\n return new PostMessageTransport(targetWindow, targetOrigin, options.allowedOrigins);\n }\n\n /**\n * Create transport for the CLIENT side (dApp).\n *\n * iframe mode: target defaults to window.parent\n * popup mode: target = popup window (from window.open())\n */\n static forClient(options?: PostMessageClientOptions): PostMessageTransport {\n const target = options?.target ?? window.parent;\n const targetOrigin = options?.targetOrigin ?? '*';\n // Accept frames only from the wallet we are talking TO, whenever we know who that is.\n //\n // The client used to pass `null` here, i.e. no filter at all: anything able to reach this\n // window could deliver a wallet event, and those frames now decide the client's\n // authoritative `identity`, `locked` and `connected` — the same identity the docs tell a\n // dApp to compare before resuming a spend.\n //\n // NORMALISED. `targetOrigin` is a caller-supplied URL, and callers pass whole wallet URLs:\n // `https://wallet.example/` and `https://wallet.example/connect` are both perfectly\n // reasonable values, and neither equals the `event.origin` the browser reports. Comparing\n // them raw silently dropped every inbound frame and killed popup mode. An unparseable value\n // falls back to no origin filter — the window check above is the real guard, and refusing to\n // parse must not brick the transport.\n const allowedOrigins = originOf(targetOrigin);\n const transport = new PostMessageTransport(target, targetOrigin, allowedOrigins);\n\n // If target is a popup window, detect when it closes\n if (options?.target && options.target !== window.parent) {\n transport.startPopupCloseDetection(options.target);\n }\n\n return transport;\n }\n\n // ===========================================================================\n // ConnectTransport Interface\n // ===========================================================================\n\n send(message: SphereConnectMessage): void {\n try {\n this.targetWindow.postMessage(message, this.targetOrigin);\n } catch {\n // Window may be closed\n }\n }\n\n onMessage(handler: (message: SphereConnectMessage) => void): () => void {\n this.handlers.add(handler);\n return () => {\n this.handlers.delete(handler);\n };\n }\n\n destroy(): void {\n if (this.listener) {\n window.removeEventListener('message', this.listener);\n this.listener = null;\n }\n if (this.popupCheckInterval) {\n clearInterval(this.popupCheckInterval);\n this.popupCheckInterval = null;\n }\n this.handlers.clear();\n }\n\n // ===========================================================================\n // Popup Close Detection\n // ===========================================================================\n\n /** Register a callback for when the popup window closes */\n onClose(callback: () => void): void {\n this.onPopupClosed = callback;\n }\n\n private startPopupCloseDetection(popup: Window): void {\n this.popupCheckInterval = setInterval(() => {\n if (popup.closed) {\n if (this.popupCheckInterval) {\n clearInterval(this.popupCheckInterval);\n this.popupCheckInterval = null;\n }\n if (this.onPopupClosed) {\n this.onPopupClosed();\n }\n }\n }, POPUP_CLOSE_CHECK_INTERVAL);\n }\n}\n","/**\n * ExtensionTransport — Chrome Extension transport for Sphere Connect.\n *\n * LEGACY: the Sphere browser extension is discontinued and no supported wallet answers this\n * transport. It still ships so existing builds keep compiling; new integrations use the hosted\n * wallet (PostMessageTransport / autoConnect) or, in Node.js, WebSocketTransport.\n *\n * Two modes:\n * - forClient(): dApp page sends messages via window.postMessage with namespace\n * 'sphere-connect-ext:tohost'. Content script relays to background via\n * chrome.runtime.sendMessage. Responses arrive via 'sphere-connect-ext:toclient'.\n *\n * - forHost(): Extension background listens via chrome.runtime.onMessage for\n * 'sphere-connect-ext:tohost' messages and sends responses back via\n * chrome.tabs.sendMessage to the originating tab.\n */\n\nimport type { ConnectTransport } from '../../../connect/types';\nimport type { SphereConnectMessage } from '../../../connect/protocol';\nimport { isSphereConnectMessage } from '../../../connect/protocol';\n\n// =============================================================================\n// Message namespaces\n// =============================================================================\n\nexport const EXT_MSG_TO_HOST = 'sphere-connect-ext:tohost';\nexport const EXT_MSG_TO_CLIENT = 'sphere-connect-ext:toclient';\n\n/** Shape of the wrapper sent via postMessage / chrome.runtime.sendMessage */\nexport interface ExtensionConnectEnvelope {\n type: typeof EXT_MSG_TO_HOST | typeof EXT_MSG_TO_CLIENT;\n payload: SphereConnectMessage;\n}\n\nexport function isExtensionConnectEnvelope(data: unknown): data is ExtensionConnectEnvelope {\n return (\n typeof data === 'object' &&\n data !== null &&\n 'type' in data &&\n ((data as ExtensionConnectEnvelope).type === EXT_MSG_TO_HOST ||\n (data as ExtensionConnectEnvelope).type === EXT_MSG_TO_CLIENT) &&\n 'payload' in data &&\n isSphereConnectMessage((data as ExtensionConnectEnvelope).payload)\n );\n}\n\n// =============================================================================\n// Client-side transport (runs in dApp page, injected script context)\n// =============================================================================\n\nclass ExtensionClientTransport implements ConnectTransport {\n private handlers: Set<(message: SphereConnectMessage) => void> = new Set();\n private listener: ((event: MessageEvent) => void) | null = null;\n\n constructor() {\n // Listen for responses relayed back from content script\n this.listener = (event: MessageEvent) => {\n if (!isExtensionConnectEnvelope(event.data)) return;\n if (event.data.type !== EXT_MSG_TO_CLIENT) return;\n\n for (const handler of this.handlers) {\n try {\n handler(event.data.payload);\n } catch {\n // Ignore handler errors\n }\n }\n };\n\n window.addEventListener('message', this.listener);\n }\n\n send(message: SphereConnectMessage): void {\n // Post to window — content script will pick this up and forward to background\n const envelope: ExtensionConnectEnvelope = {\n type: EXT_MSG_TO_HOST,\n payload: message,\n };\n window.postMessage(envelope, '*');\n }\n\n onMessage(handler: (message: SphereConnectMessage) => void): () => void {\n this.handlers.add(handler);\n return () => {\n this.handlers.delete(handler);\n };\n }\n\n destroy(): void {\n if (this.listener) {\n window.removeEventListener('message', this.listener);\n this.listener = null;\n }\n this.handlers.clear();\n }\n}\n\n// =============================================================================\n// Host-side transport (runs in extension background service worker)\n// =============================================================================\n\n/**\n * Chrome extension API subset used by ExtensionHostTransport.\n * Allows injecting a mock in tests without depending on chrome globals.\n */\nexport interface ChromeMessagingApi {\n onMessage: {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n addListener(listener: (message: unknown, sender: any, sendResponse: (r?: unknown) => void) => void): void;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n removeListener(listener: (message: unknown, sender: any, sendResponse: (r?: unknown) => void) => void): void;\n };\n tabs: {\n sendMessage(tabId: number, message: unknown): void;\n };\n}\n\nclass ExtensionHostTransport implements ConnectTransport {\n private handlers: Set<(message: SphereConnectMessage) => void> = new Set();\n // tabId of the currently connected dApp tab (used to send responses back)\n private activeTabId: number | null = null;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private chromeListener: ((message: unknown, sender: any, sendResponse: (r?: unknown) => void) => void) | null = null;\n private readonly chromeApi: ChromeMessagingApi;\n\n constructor(chromeApi: ChromeMessagingApi) {\n this.chromeApi = chromeApi;\n\n this.chromeListener = (message: unknown, sender: { tab?: { id?: number } }) => {\n if (!isExtensionConnectEnvelope(message)) return;\n if ((message as ExtensionConnectEnvelope).type !== EXT_MSG_TO_HOST) return;\n\n // Track which tab is talking to us\n if (sender.tab?.id !== undefined) {\n this.activeTabId = sender.tab.id;\n }\n\n const payload = (message as ExtensionConnectEnvelope).payload;\n for (const handler of this.handlers) {\n try {\n handler(payload);\n } catch {\n // Ignore handler errors\n }\n }\n };\n\n this.chromeApi.onMessage.addListener(this.chromeListener);\n }\n\n send(message: SphereConnectMessage): void {\n if (this.activeTabId === null) return;\n\n const envelope: ExtensionConnectEnvelope = {\n type: EXT_MSG_TO_CLIENT,\n payload: message,\n };\n\n try {\n this.chromeApi.tabs.sendMessage(this.activeTabId, envelope);\n } catch {\n // Tab may have been closed\n }\n }\n\n onMessage(handler: (message: SphereConnectMessage) => void): () => void {\n this.handlers.add(handler);\n return () => {\n this.handlers.delete(handler);\n };\n }\n\n destroy(): void {\n if (this.chromeListener) {\n this.chromeApi.onMessage.removeListener(this.chromeListener);\n this.chromeListener = null;\n }\n this.handlers.clear();\n this.activeTabId = null;\n }\n}\n\n// =============================================================================\n// Public API\n// =============================================================================\n\nexport const ExtensionTransport = {\n /**\n * Create transport for the CLIENT side (dApp page / inject script).\n * Sends via window.postMessage; receives via window.postMessage from content script.\n */\n forClient(): ConnectTransport {\n return new ExtensionClientTransport();\n },\n\n /**\n * Create transport for the HOST side (extension background service worker).\n * Receives via chrome.runtime.onMessage; sends via chrome.tabs.sendMessage.\n *\n * @param chromeApi - Pass `chrome` from the extension background context,\n * or a mock for unit tests.\n */\n forHost(chromeApi: ChromeMessagingApi): ConnectTransport {\n return new ExtensionHostTransport(chromeApi);\n },\n};\n","/**\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","// AUTO-GENERATED by scripts/gen-version.mjs — do not edit.\nexport const SDK_VERSION = '0.17.6';\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 /** #777: moving a coinless token (an NFT). Distinct from transfer:request. */\n NFT_TRANSFER: 'nft:transfer',\n /** Minting an NFT signs dApp-chosen content as the user; mint:request and nft:transfer do not imply it. */\n NFT_MINT: 'nft:mint',\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};\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.MINT]: PERMISSION_SCOPES.MINT_REQUEST,\n [INTENT_ACTIONS.SEND_NFT]: PERMISSION_SCOPES.NFT_TRANSFER,\n [INTENT_ACTIONS.MINT_NFT]: PERMISSION_SCOPES.NFT_MINT,\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 * 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 EXTERNAL dependencies: nothing from npm reaches a dApp through this file. It does use a\n * few SDK-internal modules (core/logger, and constants by way of connect/protocol's\n * SPHERE_NETWORKS), which are bundled into the Connect entry points.\n */\n\nimport { logger } from '../../core/logger';\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 ERROR_CODES,\n WALLET_EVENTS,\n createRequestId,\n isAutoPushedEvent,\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 * A QUERY may be failed with anything truthful — it is idempotent and safe to retry. An\n * INTENT may not: once it reached the wallet, the money may already be moving, so no local\n * teardown is allowed to tell the dApp it did not happen.\n */\n kind: 'query' | 'intent';\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 walletProto: string | null = null;\n private locked = false;\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 * The wallet's Connect protocol version, captured from the handshake response `v`.\n * Null before the first handshake response and after a disconnect.\n *\n * Feature-detect with it: compare against '2.1' to decide whether the wallet can be\n * trusted to send wallet:unlocked / wallet:disconnected. A Connect 2.0 wallet destroys\n * the session on lock and never emits either, so a dApp waiting for them against one\n * waits forever.\n *\n * CAVEAT: on an ERROR response the host echoes the dApp's own `v` back\n * (ConnectHost.sendHandshakeResponse), so after a refused connection this may be the\n * dApp's version rather than the wallet's. Only trust it after a successful handshake.\n */\n get walletProtocol(): string | null {\n return this.walletProto;\n }\n\n /**\n * Whether the wallet was locked at the last handshake or lifecycle event.\n * A locked client is still CONNECTED: `isConnected` stays true and `session` stays valid.\n * Requests answer WALLET_LOCKED (4009) until `wallet:unlocked` arrives on the SAME\n * session — do not disconnect, do not clear the session, do not re-handshake.\n */\n get walletLocked(): boolean {\n return this.locked;\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 ConnectError('Not connected', ERROR_CODES.NOT_CONNECTED);\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 kind: 'query',\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 ConnectError('Not connected', ERROR_CODES.NOT_CONNECTED);\n\n const id = createRequestId();\n\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n // TYPED, and specifically NOT a cancellation. We stopped waiting; the wallet may have\n // submitted the transfer. A dApp that retries here can pay twice, so the code says\n // \"outcome unknown\" and the docs forbid a retry (see ERROR_CODES.INTENT_OUTCOME_UNKNOWN).\n reject(\n new ConnectError(\n `Intent outcome unknown — do not retry; reconcile before acting: ${action}`,\n ERROR_CODES.INTENT_OUTCOME_UNKNOWN,\n ),\n );\n }, this.intentTimeout);\n\n this.pendingRequests.set(id, {\n resolve: resolve as (v: unknown) => void,\n reject,\n timer,\n kind: 'intent',\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. Auto-pushed wallet events are NEVER routed through\n // sphere_subscribe: the host pushes them unconditionally and answers that RPC with success\n // without attaching anything (Sphere.on() would accept the name and never emit).\n if (this.connected && !isAutoPushedEvent(event)) {\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 && !isAutoPushedEvent(event)) {\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 // 0. Only an ESTABLISHED session may move this client's authoritative state.\n // These frames now decide `identity`, `locked` and `connected` — and the docs tell a\n // dApp to gate a money replay on comparing that identity. A PostMessage client accepts\n // frames from anything that can reach its window, so an injected iframe could\n // otherwise rewrite `walletIdentity` to an address of its choosing before any\n // handshake had happened. A pre-connect wallet event is meaningless in any case:\n // there is no session for it to describe.\n if (!this.connected || !this.sessionId) {\n logger.warn('Connect', `Ignoring wallet event before a session exists: ${msg.event}`);\n return;\n }\n\n // 1. Unconditional NON-DESTRUCTIVE state update, BEFORE any handler lookup, so a\n // dApp that registered no handler still ends up with correct client state. The\n // client privileged no wallet event before 2.1, so an unhandled\n // wallet:disconnected was dropped entirely.\n if (msg.event === WALLET_EVENTS.LOCKED) {\n this.locked = true;\n } else if (msg.event === WALLET_EVENTS.UNLOCKED) {\n this.locked = false;\n const identity = (msg.data as { identity?: PublicIdentity } | undefined)?.identity;\n if (identity) this.identity = identity;\n } else if (msg.event === WALLET_EVENTS.DISCONNECTED) {\n // So a handler reading isConnected sees the truth.\n this.connected = false;\n } else if (msg.event === WALLET_EVENTS.IDENTITY_CHANGED) {\n const data = msg.data as PublicIdentity | undefined;\n if (data && typeof data.chainPubkey === 'string') this.identity = data;\n }\n\n // 2. Dispatch to registered handlers.\n this.dispatchEvent(msg.event, msg.data);\n\n // 3. Destructive step LAST — cleanup() clears eventHandlers, so running it after\n // dispatch is what keeps the dApp's own wallet:disconnected callback alive.\n if (msg.event === WALLET_EVENTS.DISCONNECTED) {\n this.cleanup();\n }\n }\n }\n\n private dispatchEvent(event: string, data: unknown): void {\n const handlers = this.eventHandlers.get(event);\n if (!handlers) return;\n for (const handler of handlers) {\n try {\n handler(data);\n } catch (err) {\n logger.debug('Connect', 'Event handler error', err);\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 // Captured even on a refusal, but see the getter's caveat: the host echoes the dApp's\n // own `v` on an error response.\n this.walletProto = (msg as { v?: string }).v ?? null;\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.locked = m.locked === true;\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 // A resume DURING a lock succeeds: the dApp is connected on the same session and\n // must not re-handshake. It will get wallet:unlocked when the user unlocks.\n ...(this.locked ? { locked: true } : {}),\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 // A handshake in flight has to be settled too. wallet:disconnected arriving mid-connect()\n // left it parked for the full client timeout and then rejected untyped, so a dApp could\n // not tell \"the wallet dropped us\" from \"the wallet is slow\".\n if (this.handshakeResolver) {\n clearTimeout(this.handshakeResolver.timer);\n this.handshakeResolver.reject(new ConnectError('Disconnected', ERROR_CODES.NOT_CONNECTED));\n this.handshakeResolver = null;\n }\n\n // Reject all pending requests with a TYPED error. new Error('Disconnected') carried no\n // .code, so a dApp could not tell it from a transport failure.\n //\n // A pending INTENT is rejected with INTENT_OUTCOME_UNKNOWN, never NOT_CONNECTED: the\n // wallet already had it and may have submitted the transfer, so \"not connected\" would read\n // as \"your spend never ran\" and invite a retry that pays twice.\n for (const [, pending] of this.pendingRequests) {\n clearTimeout(pending.timer);\n pending.reject(\n pending.kind === 'intent'\n ? new ConnectError(\n 'Intent outcome unknown — do not retry; reconcile before acting',\n ERROR_CODES.INTENT_OUTCOME_UNKNOWN,\n )\n : new ConnectError('Disconnected', ERROR_CODES.NOT_CONNECTED),\n );\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 this.walletProto = null;\n this.locked = false;\n }\n}\n","/**\n * autoConnect — Universal dApp connection to Sphere wallet.\n *\n * Auto-detects the best available transport and connects:\n * P1: iframe → PostMessageTransport to parent window — the live path: the hosted wallet\n * runs a dApp as a custom agent inside its own iframe\n * P2: extension → ExtensionTransport — LEGACY: the Sphere browser extension is discontinued\n * and no supported wallet answers this transport\n * P3: standalone → PostMessageTransport to popup window — the fallback when the dApp has its\n * own tab; not the route the hosted wallet is driven through, see\n * \"Running against the hosted wallet\" in docs/CONNECT.md\n *\n * Usage:\n * import { autoConnect } from '@unicitylabs/sphere-sdk/connect/browser';\n * import { SPHERE_NETWORKS } from '@unicitylabs/sphere-sdk/connect';\n *\n * const { client, connection, disconnect } = await autoConnect({\n * dapp: { name: 'My App', url: location.origin },\n * walletUrl: 'https://sphere.unicity.network',\n * network: SPHERE_NETWORKS.testnet2, // required at runtime (INCOMPATIBLE_NETWORK 4008)\n * });\n *\n * // Use the client — same API regardless of transport:\n * const balance = await client.query('sphere_getBalance');\n * await client.intent('send', {\n * to: '@bob',\n * amount: '1000000000000000000', // base units, as a string\n * coinId: '<lowercase 64-hex coin id>',\n * });\n * client.on('transfer:incoming', (data) => console.log(data));\n */\n\nimport { ConnectClient } from '../../../connect/client/ConnectClient';\nimport { HOST_READY_TYPE, HOST_READY_TIMEOUT } from '../../../connect/protocol';\nimport type { ConnectTransport, ConnectResult, ConnectClientConfig } from '../../../connect/types';\nimport type { DAppMetadata, NetworkInfo } from '../../../connect/protocol';\nimport type { PermissionScope } from '../../../connect/permissions';\nimport { PostMessageTransport } from './PostMessageTransport';\nimport { ExtensionTransport } from './ExtensionTransport';\n\n// =============================================================================\n// Environment detection\n// =============================================================================\n\n/** Returns true when the page is running inside an iframe. */\nexport function isInIframe(): boolean {\n try {\n return window.parent !== window && window.self !== window.top;\n } catch {\n // cross-origin access throws — we're in an iframe\n return true;\n }\n}\n\n/** Returns true when the Sphere browser extension is installed and active. */\nexport function hasExtension(): boolean {\n try {\n const sphere = (window as unknown as Record<string, unknown>).sphere;\n if (!sphere || typeof sphere !== 'object') return false;\n const isInstalled = (sphere as Record<string, unknown>).isInstalled;\n if (typeof isInstalled !== 'function') return false;\n return (isInstalled as () => boolean)() === true;\n } catch {\n return false;\n }\n}\n\n/** Detected transport type. */\nexport type DetectedTransport = 'iframe' | 'extension' | 'popup';\n\n/** Detect which transport to use based on the current environment. */\nexport function detectTransport(): DetectedTransport {\n if (isInIframe()) return 'iframe';\n if (hasExtension()) return 'extension';\n return 'popup';\n}\n\n// =============================================================================\n// autoConnect config\n// =============================================================================\n\nexport interface AutoConnectConfig {\n /** dApp metadata sent during handshake. */\n dapp: DAppMetadata;\n\n /**\n * Wallet URL for popup fallback (P3).\n * The URL will be opened with `/connect?origin=<current origin>` appended.\n * Required if the extension is not installed and the page is not in an iframe.\n */\n walletUrl?: string;\n\n /** Permissions to request. Defaults to all. */\n permissions?: PermissionScope[];\n\n /**\n * If true, silently fail if the wallet has not previously approved this origin.\n * No UI will be shown. Useful for auto-connect on page load.\n * Default: false.\n */\n silent?: boolean;\n\n /**\n * The network this dApp targets ({ id, name? }). Sent in the handshake; **required at\n * runtime** — optional in the type only for backward compatibility. The wallet's\n * compatibility gate rejects a missing or mismatched network with INCOMPATIBLE_NETWORK\n * (4008) before any UI appears. Use SPHERE_NETWORKS.mainnet / SPHERE_NETWORKS.testnet2\n * from '@unicitylabs/sphere-sdk/connect'; both networks are live.\n */\n network?: NetworkInfo;\n\n /**\n * Existing session ID to resume. The host keeps sessions in memory, so this works only while\n * the ConnectHost that issued it is still alive (in practice iframe mode). A popup is a fresh\n * wallet page with a new host, so there the handshake runs as a new connection.\n */\n resumeSessionId?: string;\n\n /**\n * Timeout in ms for query requests and for the connect handshake. Default: 30000. The\n * handshake answer waits for the user's approval, so a non-silent autoConnect() rejects with\n * 'Connection timeout' when the user takes longer than this.\n */\n timeout?: number;\n\n /** Timeout for intent requests in ms. Default: 120000. */\n intentTimeout?: number;\n\n /**\n * Popup window features (width, height, etc.).\n * Default: 'width=420,height=720,scrollbars=yes,resizable=yes'\n */\n popupFeatures?: string;\n\n /**\n * Force a specific transport instead of auto-detecting.\n * Useful for testing or explicit control.\n */\n forceTransport?: DetectedTransport;\n}\n\nexport interface AutoConnectResult {\n /** Connected client — use for queries, intents, and events. */\n client: ConnectClient;\n /** Connection result with session info and identity. */\n connection: ConnectResult;\n /** Which transport was selected. */\n transport: DetectedTransport;\n /**\n * Disconnect and clean up all resources.\n * For popup mode, also closes the popup window.\n */\n disconnect: () => Promise<void>;\n}\n\n// =============================================================================\n// autoConnect implementation\n// =============================================================================\n\nconst DEFAULT_POPUP_FEATURES = 'width=420,height=720,scrollbars=yes,resizable=yes';\n\n/**\n * Auto-detect the best transport and connect to the Sphere wallet.\n *\n * @throws Error if connection fails or is rejected by the wallet.\n */\nexport async function autoConnect(config: AutoConnectConfig): Promise<AutoConnectResult> {\n const transportType = config.forceTransport ?? detectTransport();\n\n switch (transportType) {\n case 'iframe':\n return connectViaIframe(config);\n case 'extension':\n return connectViaExtension(config);\n case 'popup':\n return connectViaPopup(config);\n }\n}\n\n// =============================================================================\n// P1: iframe\n// =============================================================================\n\nasync function connectViaIframe(config: AutoConnectConfig): Promise<AutoConnectResult> {\n const transport = PostMessageTransport.forClient();\n\n const { client, connection, cleanup } = await createAndConnect(transport, config);\n\n return {\n client,\n connection,\n transport: 'iframe',\n disconnect: async () => {\n await client.disconnect();\n cleanup();\n },\n };\n}\n\n// =============================================================================\n// P2: extension\n// =============================================================================\n\nasync function connectViaExtension(config: AutoConnectConfig): Promise<AutoConnectResult> {\n const transport = ExtensionTransport.forClient();\n\n const { client, connection, cleanup } = await createAndConnect(transport, config);\n\n return {\n client,\n connection,\n transport: 'extension',\n disconnect: async () => {\n await client.disconnect();\n cleanup();\n },\n };\n}\n\n// =============================================================================\n// P3: popup\n// =============================================================================\n\nasync function connectViaPopup(config: AutoConnectConfig): Promise<AutoConnectResult> {\n if (!config.walletUrl) {\n throw new Error('autoConnect: walletUrl is required when no extension or iframe is available');\n }\n\n const origin = encodeURIComponent(window.location.origin);\n const popupUrl = `${config.walletUrl}/connect?origin=${origin}`;\n const features = config.popupFeatures ?? DEFAULT_POPUP_FEATURES;\n\n const popup = window.open(popupUrl, 'sphere-wallet', features);\n if (!popup) {\n throw new Error('autoConnect: Failed to open wallet popup — check popup blocker settings');\n }\n\n // Wait for HOST_READY signal from the wallet popup\n await waitForHostReady(popup, config.walletUrl);\n\n const transport = PostMessageTransport.forClient({\n target: popup,\n targetOrigin: config.walletUrl,\n });\n\n const { client, connection, cleanup } = await createAndConnect(transport, config);\n\n // Monitor popup close → treat as disconnect\n const closeCheckInterval = setInterval(() => {\n if (popup.closed) {\n clearInterval(closeCheckInterval);\n cleanup();\n }\n }, 1000);\n\n return {\n client,\n connection,\n transport: 'popup',\n disconnect: async () => {\n clearInterval(closeCheckInterval);\n await client.disconnect();\n cleanup();\n if (!popup.closed) popup.close();\n },\n };\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction waitForHostReady(popup: Window, _walletOrigin: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n window.removeEventListener('message', listener);\n reject(new Error('autoConnect: Wallet popup did not respond in time'));\n }, HOST_READY_TIMEOUT);\n\n function listener(event: MessageEvent) {\n // Accept from the wallet origin or any origin (for local dev)\n if (event.data?.type === HOST_READY_TYPE) {\n clearTimeout(timer);\n window.removeEventListener('message', listener);\n resolve();\n }\n }\n\n window.addEventListener('message', listener);\n\n // Also detect if popup is closed before responding\n const closeCheck = setInterval(() => {\n if (popup.closed) {\n clearInterval(closeCheck);\n clearTimeout(timer);\n window.removeEventListener('message', listener);\n reject(new Error('autoConnect: Wallet popup was closed before connecting'));\n }\n }, 500);\n });\n}\n\nasync function createAndConnect(\n transport: ConnectTransport,\n config: AutoConnectConfig,\n): Promise<{\n client: ConnectClient;\n connection: ConnectResult;\n cleanup: () => void;\n}> {\n const clientConfig: ConnectClientConfig = {\n transport,\n dapp: config.dapp,\n permissions: config.permissions,\n timeout: config.timeout,\n intentTimeout: config.intentTimeout,\n resumeSessionId: config.resumeSessionId,\n silent: config.silent,\n network: config.network,\n };\n\n const client = new ConnectClient(clientConfig);\n\n try {\n const connection = await client.connect();\n return {\n client,\n connection,\n cleanup: () => transport.destroy(),\n };\n } catch (err) {\n transport.destroy();\n throw err;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../impl/browser/connect/index.ts","../../../../connect/semver.ts","../../../../constants.ts","../../../../connect/protocol.ts","../../../../impl/browser/connect/PostMessageTransport.ts","../../../../impl/browser/connect/ExtensionTransport.ts","../../../../core/logger.ts","../../../../connect/version.ts","../../../../connect/permissions.ts","../../../../connect/client/ConnectClient.ts","../../../../impl/browser/connect/autoConnect.ts"],"sourcesContent":["export { PostMessageTransport } from './PostMessageTransport';\nexport type { PostMessageHostOptions, PostMessageClientOptions } from './PostMessageTransport';\n\nexport { ExtensionTransport, EXT_MSG_TO_HOST, EXT_MSG_TO_CLIENT, isExtensionConnectEnvelope } from './ExtensionTransport';\nexport type { ExtensionConnectEnvelope, ChromeMessagingApi } from './ExtensionTransport';\n\nexport { autoConnect, detectTransport, isInIframe, hasExtension } from './autoConnect';\nexport type { AutoConnectConfig, AutoConnectResult, DetectedTransport } from './autoConnect';\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 * Legacy key: Sphere's private decrypt() tries it only as a fallback for wallets written by old\n * SDK versions when no password is given. Nothing is encrypted with it; without a password the\n * mnemonic or master key is stored as plaintext.\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 is server custody (wallet-api backend), not here; the\n * payments vertical's durable client state self-prefixes `pv2g2:{network}:{pubkey}:`.\n */\nexport const STORAGE_KEYS_ADDRESS = {\n /** Transfer outbox for this address (pre-flip key name; kept as the network-scoping witness) */\n OUTBOX: 'outbox',\n /** Conversations for this address */\n CONVERSATIONS: 'conversations',\n /** Messages for this address */\n MESSAGES: 'messages',\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 /** Auto-return settings (pre-flip key name; kept as the network-scoping witness) */\n AUTO_RETURN: 'auto_return',\n /** Auto-return dedup ledger (pre-flip key name; kept as the network-scoping witness) */\n AUTO_RETURN_LEDGER: 'auto_return_ledger',\n /** Per-swap key prefix (pre-flip key name; kept as the network-scoping witness) */\n SWAP_RECORD_PREFIX: 'swap:',\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. Chat/identity per-address keys (CONVERSATIONS/MESSAGES/\n * GROUP_CHAT_*) are network-AGNOSTIC and deliberately NOT listed. The payments vertical\n * self-prefixes `pv2g2:{network}:{pubkey}:` and never rides this mechanism; the entries left\n * here are the pre-flip key names the network-isolation tests pin the provider behavior\n * with (P11 stage-2 note: the full isNetworkScopedAddressKey cut needs an owner call on\n * those tests first).\n */\nconst NETWORK_SCOPED_ADDRESS_KEYS: readonly string[] = [\n STORAGE_KEYS_ADDRESS.OUTBOX,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN_LEDGER,\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/**\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/**\n * Fallback Nostr relays, used only when no relay list is configured\n * (NostrTransportProvider / MultiAddressTransportMux). `relay.unicity.network`\n * was the v1 relay and no longer resolves, so it is not listed here.\n */\nexport const DEFAULT_NOSTR_RELAYS = [\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 /** 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 request timeout (ms)\n * Note: The aggregator is conceptually an oracle - a trusted service that provides\n * verifiable truth about token state through cryptographic inclusion proofs.\n * There is no default aggregator URL: every live network names its own gateway.\n */\nexport const DEFAULT_AGGREGATOR_TIMEOUT = 30000;\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/** 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 groupRelays: readonly string[];\n readonly tokenRegistryUrl: string;\n /** The NFT vessel token type every NFT mints under (#785), 64 lowercase hex: the single\n * `non-fungible` entry of this network's unicity-ids registry. */\n readonly nftTokenType: 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 networkId: 1,\n // v3 state-transition gateway (networkId 1 comes from the trust base). apiKey is env-injected;\n // unlike testnet2's, a mainnet gateway key is a SECRET — never commit one.\n aggregatorUrl: 'https://gateway.mainnet.unicity.network',\n // Mainnet has no relay of its own yet — it shares the testnet relay until one is stood up.\n // Consequence while shared: nametag bindings for both networks live in ONE namespace, and\n // since bindings carry no network the cross-network recipient guard can only signal (#734).\n nostrRelays: TEST_NOSTR_RELAYS,\n groupRelays: DEFAULT_GROUP_RELAYS,\n // Published, but currently only the non-fungible base token type — no fungible\n // coins yet, so those still miss the registry (decimals 0, symbol falls back to\n // six hex chars). Presentation only: the money path treats coinId as opaque bytes.\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.mainnet.json',\n nftTokenType: '9f190eea6c8d7e1e564c35feb4c289add78be4bedb81bb77fe265e926e5493f4',\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: 'Testnet',\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 groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n nftTokenType: '971a26eef0e3aeb22bd3e7d44c47ce963400037e8df42b50d4d44e1589f83826',\n },\n testnet2: {\n name: 'Testnet',\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 groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n nftTokenType: '971a26eef0e3aeb22bd3e7d44c47ce963400037e8df42b50d4d44e1589f83826',\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 mainnet: { id: NETWORKS.mainnet.networkId as number, name: 'mainnet' },\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 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.3'; // Connect protocol version (semver MAJOR.MINOR)\n\n// Default npm-SDK floor a host enforces at the handshake (0.14.1 = the P11 flip:\n// the v1 payments era is gone; pre-flip ConnectClients expect a wallet that no\n// longer exists). '-0' admits every 0.14.1 prerelease (compareSemver: release >\n// prerelease). Override via ConnectHostConfig.minSdkVersion; the claim is\n// compatibility hygiene, not security — a hostile client can lie about it.\nexport const DEFAULT_MIN_CLIENT_SDK_VERSION = '0.14.1-0';\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} 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 MINT: 'mint',\n // #777: params { to, tokenId, memo? }. Named NFT rather than 'send_token':\n // coins are tokens too, so 'token' does not say which kind moves.\n SEND_NFT: 'send_nft',\n // Connect 2.3: params MintNftIntentParams, result MintNftIntentResult (connect/nft-wire.ts).\n MINT_NFT: 'mint_nft',\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 // Wallet locked; THE SESSION IS STILL ALIVE. A QUERY may be retried after wallet:unlocked.\n // An INTENT already delegated to the wallet is NEVER answered with this code — it gets\n // INTENT_OUTCOME_UNKNOWN (4201) instead, because a retry could double-spend.\n WALLET_LOCKED: 4009,\n INSUFFICIENT_BALANCE: 4100,\n INVALID_RECIPIENT: 4101,\n TRANSFER_FAILED: 4102,\n INTENT_CANCELLED: 4200,\n /**\n * The intent was DELEGATED to the wallet and the host lost track of the answer — a host\n * deadline fired, or the wallet locked / logged out mid-flight. **The outcome is UNKNOWN:\n * the money may or may not have moved.**\n *\n * A dApp MUST NOT retry on this code. Reconcile out of band (poll the recipient, the\n * aggregator, or your own backend) and only then decide.\n *\n * This code exists because every other answer would be a lie. `INTENT_CANCELLED` (4200)\n * asserts the user declined and nothing happened; `WALLET_LOCKED` (4009) invites a retry\n * after the unlock. Sending either for an intent the wallet had already submitted is how a\n * paid-but-not-credited order — and then a double spend on retry — happens.\n */\n INTENT_OUTCOME_UNKNOWN: 4201,\n} as const;\n\nexport type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];\n\n/** `data` carried by every WALLET_LOCKED (4009) refusal. */\nexport interface WalletLockedData {\n /** Discriminator. Always the literal 'locked' — reserved for future refusal reasons. */\n readonly reason: 'locked';\n /**\n * Whether the wallet's own unlock surface is currently on screen.\n * DECLARED in the fail-fast release and always absent there; POPULATED in the resume\n * release from ConnectHostConfig.unlockSurface(), evaluated at refusal time (visibility\n * changes over time, so a static value would lie). Feeds\n * ConnectClientConfig.onWalletAttention.\n */\n readonly unlockSurface?: 'visible' | 'background';\n}\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 /** Response only: the wallet is LOCKED and the session is ALIVE. The dApp is connected\n * and must not re-handshake; it will receive `wallet:unlocked` on the same session.\n * Additive and safe for old clients: handleHandshakeResponse reads only sessionId,\n * permissions, identity, network, warning and error and ignores unknown fields. */\n readonly locked?: boolean;\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 is LOCKED — the session is STILL ALIVE. Requests are answered\n * WALLET_LOCKED (4009) until `wallet:unlocked`. The dApp must NOT disconnect,\n * must NOT clear its sessionId, and must NOT re-handshake.\n * Payload: {@link WalletLockedPayload}. Pushed by ConnectHost.setLocked() and\n * immediately after a handshake response carrying `locked: true`. */\n LOCKED: 'wallet:locked',\n /** Wallet was unlocked — the SAME session continues: no re-handshake, no re-approval,\n * no re-subscribe (the host re-arms the dApp's subscriptions before pushing this).\n * Payload: {@link WalletUnlockedPayload} — carries the CURRENT identity, which may\n * differ from the one the dApp connected with. Pushed by ConnectHost.updateSphere()\n * on the locked -> live edge only. */\n UNLOCKED: 'wallet:unlocked',\n /** The session is GONE (logout, wallet deleted, dApp sphere_disconnect, expiry seen at\n * unlock, a different seed behind the lock screen, host destroy).\n * The dApp must clear its session and re-handshake to continue. Unlocking does not cure it.\n * Payload: {@link WalletDisconnectedPayload}. Pushed by ConnectHost.revokeSession(). */\n DISCONNECTED: 'wallet:disconnected',\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/** Payload of {@link WALLET_EVENTS.LOCKED}. Intentionally empty — matches today's push,\n * so an old dApp sees no shape change. */\nexport type WalletLockedPayload = Record<string, never>;\n\n/** Payload of {@link WALLET_EVENTS.DISCONNECTED}. Intentionally empty. */\nexport type WalletDisconnectedPayload = Record<string, never>;\n\n/** Payload of {@link WALLET_EVENTS.UNLOCKED}. */\nexport interface WalletUnlockedPayload {\n /** The wallet's public identity at unlock time. Absent when the rebound Sphere has no\n * identity yet (JSON transports drop undefined keys).\n * Unlock is NOT implicitly the same wallet: the lock screen's \"Forgot password ->\n * restore from recovery phrase\" installs a DIFFERENT seed while origin approvals are\n * keyed by origin alone. The host's own lock-edge guard is authoritative (it revokes\n * instead of pushing this event on a mismatch); this field is what lets a dApp render\n * honestly and what the client-side retry queue checks before draining. */\n readonly identity?: PublicIdentity;\n}\n\n/** Payload of {@link WALLET_EVENTS.IDENTITY_CHANGED} — unchanged: the host forwards\n * Sphere's own event data verbatim and pushes a PublicIdentity from updateSphere().\n * Typed as the union for honesty. */\nexport type WalletIdentityChangedPayload = PublicIdentity | unknown;\n\n/** Events the host pushes unconditionally. They must NEVER be routed through\n * `sphere_subscribe`: Sphere.on() accepts any string and would silently never emit,\n * so the subscribe would succeed and deliver nothing forever. */\nexport const AUTO_PUSHED_EVENTS: readonly WalletEvent[] = [\n WALLET_EVENTS.LOCKED,\n WALLET_EVENTS.UNLOCKED,\n WALLET_EVENTS.DISCONNECTED,\n WALLET_EVENTS.IDENTITY_CHANGED,\n];\n\n/** True for an event the host pushes unconditionally (see {@link AUTO_PUSHED_EVENTS}). */\nexport function isAutoPushedEvent(event: string): event is WalletEvent {\n return (AUTO_PUSHED_EVENTS as readonly string[]).includes(event);\n}\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","/**\n * PostMessageTransport — Browser transport for Sphere Connect.\n *\n * Two modes:\n * - iframe: wallet (parent) ↔ dApp (iframe child)\n * - popup: dApp (opener) ↔ wallet (popup window)\n */\n\nimport type { ConnectTransport } from '../../../connect/types';\nimport type { SphereConnectMessage } from '../../../connect/protocol';\nimport { isSphereConnectMessage } from '../../../connect/protocol';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface PostMessageHostOptions {\n /** Allowed origins for incoming messages. Use ['*'] only in development. */\n allowedOrigins: string[];\n}\n\nexport interface PostMessageClientOptions {\n /** Target window to send messages to. Defaults to window.parent (iframe mode). */\n target?: Window;\n /** Target origin for postMessage. Default: '*'. Should be set to wallet origin. */\n targetOrigin?: string;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nconst POPUP_CLOSE_CHECK_INTERVAL = 1000;\n\n/**\n * The origin of a caller-supplied target URL, as a single-entry allow-list. `null` (no filter)\n * for `'*'` and for anything that will not parse.\n */\nfunction originOf(targetOrigin: string): string[] | null {\n if (targetOrigin === '*') return null;\n try {\n return [new URL(targetOrigin).origin];\n } catch {\n return null;\n }\n}\n\nexport class PostMessageTransport implements ConnectTransport {\n private readonly targetWindow: Window;\n private readonly targetOrigin: string;\n private readonly allowedOrigins: Set<string> | null;\n private handlers: Set<(message: SphereConnectMessage) => void> = new Set();\n private listener: ((event: MessageEvent) => void) | null = null;\n private popupCheckInterval: ReturnType<typeof setInterval> | null = null;\n private onPopupClosed: (() => void) | null = null;\n\n private constructor(\n targetWindow: Window,\n targetOrigin: string,\n allowedOrigins: string[] | null,\n ) {\n this.targetWindow = targetWindow;\n this.targetOrigin = targetOrigin;\n this.allowedOrigins = allowedOrigins ? new Set(allowedOrigins) : null;\n\n // Listen for incoming messages\n this.listener = (event: MessageEvent) => {\n // WINDOW check, first and unconditional. `event.origin` alone cannot tell the wallet\n // window from any OTHER frame on the same origin — an injected same-origin iframe passes\n // an origin allow-list, and in iframe mode there is no allow-list at all because the\n // client defaults to `'*'`. Only the window we are actually talking to may speak to us.\n //\n // Guarded on `event.source` being present: some environments (and jsdom in tests) deliver\n // synthetic events without one, and dropping those would break every existing consumer.\n if (event.source && event.source !== this.targetWindow) {\n return;\n }\n\n // Origin check (host mode, and client mode when a concrete targetOrigin was given)\n if (this.allowedOrigins && !this.allowedOrigins.has('*') && !this.allowedOrigins.has(event.origin)) {\n return;\n }\n\n // Namespace filter\n if (!isSphereConnectMessage(event.data)) {\n return;\n }\n\n for (const handler of this.handlers) {\n try {\n handler(event.data);\n } catch {\n // Ignore handler errors\n }\n }\n };\n\n window.addEventListener('message', this.listener);\n }\n\n // ===========================================================================\n // Factory Methods\n // ===========================================================================\n\n /**\n * Create transport for the HOST side (wallet).\n *\n * iframe mode: target = iframe.contentWindow\n * popup mode: target = window.opener\n */\n static forHost(\n target: HTMLIFrameElement | Window,\n options: PostMessageHostOptions,\n ): PostMessageTransport {\n const targetWindow = target instanceof HTMLIFrameElement\n ? target.contentWindow!\n : target;\n const targetOrigin = options.allowedOrigins[0] === '*' ? '*' : options.allowedOrigins[0];\n return new PostMessageTransport(targetWindow, targetOrigin, options.allowedOrigins);\n }\n\n /**\n * Create transport for the CLIENT side (dApp).\n *\n * iframe mode: target defaults to window.parent\n * popup mode: target = popup window (from window.open())\n */\n static forClient(options?: PostMessageClientOptions): PostMessageTransport {\n const target = options?.target ?? window.parent;\n const targetOrigin = options?.targetOrigin ?? '*';\n // Accept frames only from the wallet we are talking TO, whenever we know who that is.\n //\n // The client used to pass `null` here, i.e. no filter at all: anything able to reach this\n // window could deliver a wallet event, and those frames now decide the client's\n // authoritative `identity`, `locked` and `connected` — the same identity the docs tell a\n // dApp to compare before resuming a spend.\n //\n // NORMALISED. `targetOrigin` is a caller-supplied URL, and callers pass whole wallet URLs:\n // `https://wallet.example/` and `https://wallet.example/connect` are both perfectly\n // reasonable values, and neither equals the `event.origin` the browser reports. Comparing\n // them raw silently dropped every inbound frame and killed popup mode. An unparseable value\n // falls back to no origin filter — the window check above is the real guard, and refusing to\n // parse must not brick the transport.\n const allowedOrigins = originOf(targetOrigin);\n const transport = new PostMessageTransport(target, targetOrigin, allowedOrigins);\n\n // If target is a popup window, detect when it closes\n if (options?.target && options.target !== window.parent) {\n transport.startPopupCloseDetection(options.target);\n }\n\n return transport;\n }\n\n // ===========================================================================\n // ConnectTransport Interface\n // ===========================================================================\n\n send(message: SphereConnectMessage): void {\n try {\n this.targetWindow.postMessage(message, this.targetOrigin);\n } catch {\n // Window may be closed\n }\n }\n\n onMessage(handler: (message: SphereConnectMessage) => void): () => void {\n this.handlers.add(handler);\n return () => {\n this.handlers.delete(handler);\n };\n }\n\n destroy(): void {\n if (this.listener) {\n window.removeEventListener('message', this.listener);\n this.listener = null;\n }\n if (this.popupCheckInterval) {\n clearInterval(this.popupCheckInterval);\n this.popupCheckInterval = null;\n }\n this.handlers.clear();\n }\n\n // ===========================================================================\n // Popup Close Detection\n // ===========================================================================\n\n /** Register a callback for when the popup window closes */\n onClose(callback: () => void): void {\n this.onPopupClosed = callback;\n }\n\n private startPopupCloseDetection(popup: Window): void {\n this.popupCheckInterval = setInterval(() => {\n if (popup.closed) {\n if (this.popupCheckInterval) {\n clearInterval(this.popupCheckInterval);\n this.popupCheckInterval = null;\n }\n if (this.onPopupClosed) {\n this.onPopupClosed();\n }\n }\n }, POPUP_CLOSE_CHECK_INTERVAL);\n }\n}\n","/**\n * ExtensionTransport — Chrome Extension transport for Sphere Connect.\n *\n * LEGACY: the Sphere browser extension is discontinued and no supported wallet answers this\n * transport. It still ships so existing builds keep compiling; new integrations use the hosted\n * wallet (PostMessageTransport / autoConnect) or, in Node.js, WebSocketTransport.\n *\n * Two modes:\n * - forClient(): dApp page sends messages via window.postMessage with namespace\n * 'sphere-connect-ext:tohost'. Content script relays to background via\n * chrome.runtime.sendMessage. Responses arrive via 'sphere-connect-ext:toclient'.\n *\n * - forHost(): Extension background listens via chrome.runtime.onMessage for\n * 'sphere-connect-ext:tohost' messages and sends responses back via\n * chrome.tabs.sendMessage to the originating tab.\n */\n\nimport type { ConnectTransport } from '../../../connect/types';\nimport type { SphereConnectMessage } from '../../../connect/protocol';\nimport { isSphereConnectMessage } from '../../../connect/protocol';\n\n// =============================================================================\n// Message namespaces\n// =============================================================================\n\nexport const EXT_MSG_TO_HOST = 'sphere-connect-ext:tohost';\nexport const EXT_MSG_TO_CLIENT = 'sphere-connect-ext:toclient';\n\n/** Shape of the wrapper sent via postMessage / chrome.runtime.sendMessage */\nexport interface ExtensionConnectEnvelope {\n type: typeof EXT_MSG_TO_HOST | typeof EXT_MSG_TO_CLIENT;\n payload: SphereConnectMessage;\n}\n\nexport function isExtensionConnectEnvelope(data: unknown): data is ExtensionConnectEnvelope {\n return (\n typeof data === 'object' &&\n data !== null &&\n 'type' in data &&\n ((data as ExtensionConnectEnvelope).type === EXT_MSG_TO_HOST ||\n (data as ExtensionConnectEnvelope).type === EXT_MSG_TO_CLIENT) &&\n 'payload' in data &&\n isSphereConnectMessage((data as ExtensionConnectEnvelope).payload)\n );\n}\n\n// =============================================================================\n// Client-side transport (runs in dApp page, injected script context)\n// =============================================================================\n\nclass ExtensionClientTransport implements ConnectTransport {\n private handlers: Set<(message: SphereConnectMessage) => void> = new Set();\n private listener: ((event: MessageEvent) => void) | null = null;\n\n constructor() {\n // Listen for responses relayed back from content script\n this.listener = (event: MessageEvent) => {\n if (!isExtensionConnectEnvelope(event.data)) return;\n if (event.data.type !== EXT_MSG_TO_CLIENT) return;\n\n for (const handler of this.handlers) {\n try {\n handler(event.data.payload);\n } catch {\n // Ignore handler errors\n }\n }\n };\n\n window.addEventListener('message', this.listener);\n }\n\n send(message: SphereConnectMessage): void {\n // Post to window — content script will pick this up and forward to background\n const envelope: ExtensionConnectEnvelope = {\n type: EXT_MSG_TO_HOST,\n payload: message,\n };\n window.postMessage(envelope, '*');\n }\n\n onMessage(handler: (message: SphereConnectMessage) => void): () => void {\n this.handlers.add(handler);\n return () => {\n this.handlers.delete(handler);\n };\n }\n\n destroy(): void {\n if (this.listener) {\n window.removeEventListener('message', this.listener);\n this.listener = null;\n }\n this.handlers.clear();\n }\n}\n\n// =============================================================================\n// Host-side transport (runs in extension background service worker)\n// =============================================================================\n\n/**\n * Chrome extension API subset used by ExtensionHostTransport.\n * Allows injecting a mock in tests without depending on chrome globals.\n */\nexport interface ChromeMessagingApi {\n onMessage: {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n addListener(listener: (message: unknown, sender: any, sendResponse: (r?: unknown) => void) => void): void;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n removeListener(listener: (message: unknown, sender: any, sendResponse: (r?: unknown) => void) => void): void;\n };\n tabs: {\n sendMessage(tabId: number, message: unknown): void;\n };\n}\n\nclass ExtensionHostTransport implements ConnectTransport {\n private handlers: Set<(message: SphereConnectMessage) => void> = new Set();\n // tabId of the currently connected dApp tab (used to send responses back)\n private activeTabId: number | null = null;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private chromeListener: ((message: unknown, sender: any, sendResponse: (r?: unknown) => void) => void) | null = null;\n private readonly chromeApi: ChromeMessagingApi;\n\n constructor(chromeApi: ChromeMessagingApi) {\n this.chromeApi = chromeApi;\n\n this.chromeListener = (message: unknown, sender: { tab?: { id?: number } }) => {\n if (!isExtensionConnectEnvelope(message)) return;\n if ((message as ExtensionConnectEnvelope).type !== EXT_MSG_TO_HOST) return;\n\n // Track which tab is talking to us\n if (sender.tab?.id !== undefined) {\n this.activeTabId = sender.tab.id;\n }\n\n const payload = (message as ExtensionConnectEnvelope).payload;\n for (const handler of this.handlers) {\n try {\n handler(payload);\n } catch {\n // Ignore handler errors\n }\n }\n };\n\n this.chromeApi.onMessage.addListener(this.chromeListener);\n }\n\n send(message: SphereConnectMessage): void {\n if (this.activeTabId === null) return;\n\n const envelope: ExtensionConnectEnvelope = {\n type: EXT_MSG_TO_CLIENT,\n payload: message,\n };\n\n try {\n this.chromeApi.tabs.sendMessage(this.activeTabId, envelope);\n } catch {\n // Tab may have been closed\n }\n }\n\n onMessage(handler: (message: SphereConnectMessage) => void): () => void {\n this.handlers.add(handler);\n return () => {\n this.handlers.delete(handler);\n };\n }\n\n destroy(): void {\n if (this.chromeListener) {\n this.chromeApi.onMessage.removeListener(this.chromeListener);\n this.chromeListener = null;\n }\n this.handlers.clear();\n this.activeTabId = null;\n }\n}\n\n// =============================================================================\n// Public API\n// =============================================================================\n\nexport const ExtensionTransport = {\n /**\n * Create transport for the CLIENT side (dApp page / inject script).\n * Sends via window.postMessage; receives via window.postMessage from content script.\n */\n forClient(): ConnectTransport {\n return new ExtensionClientTransport();\n },\n\n /**\n * Create transport for the HOST side (extension background service worker).\n * Receives via chrome.runtime.onMessage; sends via chrome.tabs.sendMessage.\n *\n * @param chromeApi - Pass `chrome` from the extension background context,\n * or a mock for unit tests.\n */\n forHost(chromeApi: ChromeMessagingApi): ConnectTransport {\n return new ExtensionHostTransport(chromeApi);\n },\n};\n","/**\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","// AUTO-GENERATED by scripts/gen-version.mjs — do not edit.\nexport const SDK_VERSION = '0.18.0-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 /** #777: moving a coinless token (an NFT). Distinct from transfer:request. */\n NFT_TRANSFER: 'nft:transfer',\n /** Minting an NFT signs dApp-chosen content as the user; mint:request and nft:transfer do not imply it. */\n NFT_MINT: 'nft:mint',\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};\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.MINT]: PERMISSION_SCOPES.MINT_REQUEST,\n [INTENT_ACTIONS.SEND_NFT]: PERMISSION_SCOPES.NFT_TRANSFER,\n [INTENT_ACTIONS.MINT_NFT]: PERMISSION_SCOPES.NFT_MINT,\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 * 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 EXTERNAL dependencies: nothing from npm reaches a dApp through this file. It does use a\n * few SDK-internal modules (core/logger, and constants by way of connect/protocol's\n * SPHERE_NETWORKS), which are bundled into the Connect entry points.\n */\n\nimport { logger } from '../../core/logger';\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 ERROR_CODES,\n WALLET_EVENTS,\n createRequestId,\n isAutoPushedEvent,\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 * A QUERY may be failed with anything truthful — it is idempotent and safe to retry. An\n * INTENT may not: once it reached the wallet, the money may already be moving, so no local\n * teardown is allowed to tell the dApp it did not happen.\n */\n kind: 'query' | 'intent';\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 walletProto: string | null = null;\n private locked = false;\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 * The wallet's Connect protocol version, captured from the handshake response `v`.\n * Null before the first handshake response and after a disconnect.\n *\n * Feature-detect with it: compare against '2.1' to decide whether the wallet can be\n * trusted to send wallet:unlocked / wallet:disconnected. A Connect 2.0 wallet destroys\n * the session on lock and never emits either, so a dApp waiting for them against one\n * waits forever.\n *\n * CAVEAT: on an ERROR response the host echoes the dApp's own `v` back\n * (ConnectHost.sendHandshakeResponse), so after a refused connection this may be the\n * dApp's version rather than the wallet's. Only trust it after a successful handshake.\n */\n get walletProtocol(): string | null {\n return this.walletProto;\n }\n\n /**\n * Whether the wallet was locked at the last handshake or lifecycle event.\n * A locked client is still CONNECTED: `isConnected` stays true and `session` stays valid.\n * Requests answer WALLET_LOCKED (4009) until `wallet:unlocked` arrives on the SAME\n * session — do not disconnect, do not clear the session, do not re-handshake.\n */\n get walletLocked(): boolean {\n return this.locked;\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 ConnectError('Not connected', ERROR_CODES.NOT_CONNECTED);\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 kind: 'query',\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 ConnectError('Not connected', ERROR_CODES.NOT_CONNECTED);\n\n const id = createRequestId();\n\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n // TYPED, and specifically NOT a cancellation. We stopped waiting; the wallet may have\n // submitted the transfer. A dApp that retries here can pay twice, so the code says\n // \"outcome unknown\" and the docs forbid a retry (see ERROR_CODES.INTENT_OUTCOME_UNKNOWN).\n reject(\n new ConnectError(\n `Intent outcome unknown — do not retry; reconcile before acting: ${action}`,\n ERROR_CODES.INTENT_OUTCOME_UNKNOWN,\n ),\n );\n }, this.intentTimeout);\n\n this.pendingRequests.set(id, {\n resolve: resolve as (v: unknown) => void,\n reject,\n timer,\n kind: 'intent',\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. Auto-pushed wallet events are NEVER routed through\n // sphere_subscribe: the host pushes them unconditionally and answers that RPC with success\n // without attaching anything (Sphere.on() would accept the name and never emit).\n if (this.connected && !isAutoPushedEvent(event)) {\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 && !isAutoPushedEvent(event)) {\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 // 0. Only an ESTABLISHED session may move this client's authoritative state.\n // These frames now decide `identity`, `locked` and `connected` — and the docs tell a\n // dApp to gate a money replay on comparing that identity. A PostMessage client accepts\n // frames from anything that can reach its window, so an injected iframe could\n // otherwise rewrite `walletIdentity` to an address of its choosing before any\n // handshake had happened. A pre-connect wallet event is meaningless in any case:\n // there is no session for it to describe.\n if (!this.connected || !this.sessionId) {\n logger.warn('Connect', `Ignoring wallet event before a session exists: ${msg.event}`);\n return;\n }\n\n // 1. Unconditional NON-DESTRUCTIVE state update, BEFORE any handler lookup, so a\n // dApp that registered no handler still ends up with correct client state. The\n // client privileged no wallet event before 2.1, so an unhandled\n // wallet:disconnected was dropped entirely.\n if (msg.event === WALLET_EVENTS.LOCKED) {\n this.locked = true;\n } else if (msg.event === WALLET_EVENTS.UNLOCKED) {\n this.locked = false;\n const identity = (msg.data as { identity?: PublicIdentity } | undefined)?.identity;\n if (identity) this.identity = identity;\n } else if (msg.event === WALLET_EVENTS.DISCONNECTED) {\n // So a handler reading isConnected sees the truth.\n this.connected = false;\n } else if (msg.event === WALLET_EVENTS.IDENTITY_CHANGED) {\n const data = msg.data as PublicIdentity | undefined;\n if (data && typeof data.chainPubkey === 'string') this.identity = data;\n }\n\n // 2. Dispatch to registered handlers.\n this.dispatchEvent(msg.event, msg.data);\n\n // 3. Destructive step LAST — cleanup() clears eventHandlers, so running it after\n // dispatch is what keeps the dApp's own wallet:disconnected callback alive.\n if (msg.event === WALLET_EVENTS.DISCONNECTED) {\n this.cleanup();\n }\n }\n }\n\n private dispatchEvent(event: string, data: unknown): void {\n const handlers = this.eventHandlers.get(event);\n if (!handlers) return;\n for (const handler of handlers) {\n try {\n handler(data);\n } catch (err) {\n logger.debug('Connect', 'Event handler error', err);\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 // Captured even on a refusal, but see the getter's caveat: the host echoes the dApp's\n // own `v` on an error response.\n this.walletProto = (msg as { v?: string }).v ?? null;\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.locked = m.locked === true;\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 // A resume DURING a lock succeeds: the dApp is connected on the same session and\n // must not re-handshake. It will get wallet:unlocked when the user unlocks.\n ...(this.locked ? { locked: true } : {}),\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 // A handshake in flight has to be settled too. wallet:disconnected arriving mid-connect()\n // left it parked for the full client timeout and then rejected untyped, so a dApp could\n // not tell \"the wallet dropped us\" from \"the wallet is slow\".\n if (this.handshakeResolver) {\n clearTimeout(this.handshakeResolver.timer);\n this.handshakeResolver.reject(new ConnectError('Disconnected', ERROR_CODES.NOT_CONNECTED));\n this.handshakeResolver = null;\n }\n\n // Reject all pending requests with a TYPED error. new Error('Disconnected') carried no\n // .code, so a dApp could not tell it from a transport failure.\n //\n // A pending INTENT is rejected with INTENT_OUTCOME_UNKNOWN, never NOT_CONNECTED: the\n // wallet already had it and may have submitted the transfer, so \"not connected\" would read\n // as \"your spend never ran\" and invite a retry that pays twice.\n for (const [, pending] of this.pendingRequests) {\n clearTimeout(pending.timer);\n pending.reject(\n pending.kind === 'intent'\n ? new ConnectError(\n 'Intent outcome unknown — do not retry; reconcile before acting',\n ERROR_CODES.INTENT_OUTCOME_UNKNOWN,\n )\n : new ConnectError('Disconnected', ERROR_CODES.NOT_CONNECTED),\n );\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 this.walletProto = null;\n this.locked = false;\n }\n}\n","/**\n * autoConnect — Universal dApp connection to Sphere wallet.\n *\n * Auto-detects the best available transport and connects:\n * P1: iframe → PostMessageTransport to parent window — the live path: the hosted wallet\n * runs a dApp as a custom agent inside its own iframe\n * P2: extension → ExtensionTransport — LEGACY: the Sphere browser extension is discontinued\n * and no supported wallet answers this transport\n * P3: standalone → PostMessageTransport to popup window — the fallback when the dApp has its\n * own tab; not the route the hosted wallet is driven through, see\n * \"Running against the hosted wallet\" in docs/CONNECT.md\n *\n * Usage:\n * import { autoConnect } from '@unicitylabs/sphere-sdk/connect/browser';\n * import { SPHERE_NETWORKS } from '@unicitylabs/sphere-sdk/connect';\n *\n * const { client, connection, disconnect } = await autoConnect({\n * dapp: { name: 'My App', url: location.origin },\n * walletUrl: 'https://sphere.unicity.network',\n * network: SPHERE_NETWORKS.testnet2, // required at runtime (INCOMPATIBLE_NETWORK 4008)\n * });\n *\n * // Use the client — same API regardless of transport:\n * const balance = await client.query('sphere_getBalance');\n * await client.intent('send', {\n * to: '@bob',\n * amount: '1000000000000000000', // base units, as a string\n * coinId: '<lowercase 64-hex coin id>',\n * });\n * client.on('transfer:incoming', (data) => console.log(data));\n */\n\nimport { ConnectClient } from '../../../connect/client/ConnectClient';\nimport { HOST_READY_TYPE, HOST_READY_TIMEOUT } from '../../../connect/protocol';\nimport type { ConnectTransport, ConnectResult, ConnectClientConfig } from '../../../connect/types';\nimport type { DAppMetadata, NetworkInfo } from '../../../connect/protocol';\nimport type { PermissionScope } from '../../../connect/permissions';\nimport { PostMessageTransport } from './PostMessageTransport';\nimport { ExtensionTransport } from './ExtensionTransport';\n\n// =============================================================================\n// Environment detection\n// =============================================================================\n\n/** Returns true when the page is running inside an iframe. */\nexport function isInIframe(): boolean {\n try {\n return window.parent !== window && window.self !== window.top;\n } catch {\n // cross-origin access throws — we're in an iframe\n return true;\n }\n}\n\n/** Returns true when the Sphere browser extension is installed and active. */\nexport function hasExtension(): boolean {\n try {\n const sphere = (window as unknown as Record<string, unknown>).sphere;\n if (!sphere || typeof sphere !== 'object') return false;\n const isInstalled = (sphere as Record<string, unknown>).isInstalled;\n if (typeof isInstalled !== 'function') return false;\n return (isInstalled as () => boolean)() === true;\n } catch {\n return false;\n }\n}\n\n/** Detected transport type. */\nexport type DetectedTransport = 'iframe' | 'extension' | 'popup';\n\n/** Detect which transport to use based on the current environment. */\nexport function detectTransport(): DetectedTransport {\n if (isInIframe()) return 'iframe';\n if (hasExtension()) return 'extension';\n return 'popup';\n}\n\n// =============================================================================\n// autoConnect config\n// =============================================================================\n\nexport interface AutoConnectConfig {\n /** dApp metadata sent during handshake. */\n dapp: DAppMetadata;\n\n /**\n * Wallet URL for popup fallback (P3).\n * The URL will be opened with `/connect?origin=<current origin>` appended.\n * Required if the extension is not installed and the page is not in an iframe.\n */\n walletUrl?: string;\n\n /** Permissions to request. Defaults to all. */\n permissions?: PermissionScope[];\n\n /**\n * If true, silently fail if the wallet has not previously approved this origin.\n * No UI will be shown. Useful for auto-connect on page load.\n * Default: false.\n */\n silent?: boolean;\n\n /**\n * The network this dApp targets ({ id, name? }). Sent in the handshake; **required at\n * runtime** — optional in the type only for backward compatibility. The wallet's\n * compatibility gate rejects a missing or mismatched network with INCOMPATIBLE_NETWORK\n * (4008) before any UI appears. Use SPHERE_NETWORKS.mainnet / SPHERE_NETWORKS.testnet2\n * from '@unicitylabs/sphere-sdk/connect'; both networks are live.\n */\n network?: NetworkInfo;\n\n /**\n * Existing session ID to resume. The host keeps sessions in memory, so this works only while\n * the ConnectHost that issued it is still alive (in practice iframe mode). A popup is a fresh\n * wallet page with a new host, so there the handshake runs as a new connection.\n */\n resumeSessionId?: string;\n\n /**\n * Timeout in ms for query requests and for the connect handshake. Default: 30000. The\n * handshake answer waits for the user's approval, so a non-silent autoConnect() rejects with\n * 'Connection timeout' when the user takes longer than this.\n */\n timeout?: number;\n\n /** Timeout for intent requests in ms. Default: 120000. */\n intentTimeout?: number;\n\n /**\n * Popup window features (width, height, etc.).\n * Default: 'width=420,height=720,scrollbars=yes,resizable=yes'\n */\n popupFeatures?: string;\n\n /**\n * Force a specific transport instead of auto-detecting.\n * Useful for testing or explicit control.\n */\n forceTransport?: DetectedTransport;\n}\n\nexport interface AutoConnectResult {\n /** Connected client — use for queries, intents, and events. */\n client: ConnectClient;\n /** Connection result with session info and identity. */\n connection: ConnectResult;\n /** Which transport was selected. */\n transport: DetectedTransport;\n /**\n * Disconnect and clean up all resources.\n * For popup mode, also closes the popup window.\n */\n disconnect: () => Promise<void>;\n}\n\n// =============================================================================\n// autoConnect implementation\n// =============================================================================\n\nconst DEFAULT_POPUP_FEATURES = 'width=420,height=720,scrollbars=yes,resizable=yes';\n\n/**\n * Auto-detect the best transport and connect to the Sphere wallet.\n *\n * @throws Error if connection fails or is rejected by the wallet.\n */\nexport async function autoConnect(config: AutoConnectConfig): Promise<AutoConnectResult> {\n const transportType = config.forceTransport ?? detectTransport();\n\n switch (transportType) {\n case 'iframe':\n return connectViaIframe(config);\n case 'extension':\n return connectViaExtension(config);\n case 'popup':\n return connectViaPopup(config);\n }\n}\n\n// =============================================================================\n// P1: iframe\n// =============================================================================\n\nasync function connectViaIframe(config: AutoConnectConfig): Promise<AutoConnectResult> {\n const transport = PostMessageTransport.forClient();\n\n const { client, connection, cleanup } = await createAndConnect(transport, config);\n\n return {\n client,\n connection,\n transport: 'iframe',\n disconnect: async () => {\n await client.disconnect();\n cleanup();\n },\n };\n}\n\n// =============================================================================\n// P2: extension\n// =============================================================================\n\nasync function connectViaExtension(config: AutoConnectConfig): Promise<AutoConnectResult> {\n const transport = ExtensionTransport.forClient();\n\n const { client, connection, cleanup } = await createAndConnect(transport, config);\n\n return {\n client,\n connection,\n transport: 'extension',\n disconnect: async () => {\n await client.disconnect();\n cleanup();\n },\n };\n}\n\n// =============================================================================\n// P3: popup\n// =============================================================================\n\nasync function connectViaPopup(config: AutoConnectConfig): Promise<AutoConnectResult> {\n if (!config.walletUrl) {\n throw new Error('autoConnect: walletUrl is required when no extension or iframe is available');\n }\n\n const origin = encodeURIComponent(window.location.origin);\n const popupUrl = `${config.walletUrl}/connect?origin=${origin}`;\n const features = config.popupFeatures ?? DEFAULT_POPUP_FEATURES;\n\n const popup = window.open(popupUrl, 'sphere-wallet', features);\n if (!popup) {\n throw new Error('autoConnect: Failed to open wallet popup — check popup blocker settings');\n }\n\n // Wait for HOST_READY signal from the wallet popup\n await waitForHostReady(popup, config.walletUrl);\n\n const transport = PostMessageTransport.forClient({\n target: popup,\n targetOrigin: config.walletUrl,\n });\n\n const { client, connection, cleanup } = await createAndConnect(transport, config);\n\n // Monitor popup close → treat as disconnect\n const closeCheckInterval = setInterval(() => {\n if (popup.closed) {\n clearInterval(closeCheckInterval);\n cleanup();\n }\n }, 1000);\n\n return {\n client,\n connection,\n transport: 'popup',\n disconnect: async () => {\n clearInterval(closeCheckInterval);\n await client.disconnect();\n cleanup();\n if (!popup.closed) popup.close();\n },\n };\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction waitForHostReady(popup: Window, _walletOrigin: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n window.removeEventListener('message', listener);\n reject(new Error('autoConnect: Wallet popup did not respond in time'));\n }, HOST_READY_TIMEOUT);\n\n function listener(event: MessageEvent) {\n // Accept from the wallet origin or any origin (for local dev)\n if (event.data?.type === HOST_READY_TYPE) {\n clearTimeout(timer);\n window.removeEventListener('message', listener);\n resolve();\n }\n }\n\n window.addEventListener('message', listener);\n\n // Also detect if popup is closed before responding\n const closeCheck = setInterval(() => {\n if (popup.closed) {\n clearInterval(closeCheck);\n clearTimeout(timer);\n window.removeEventListener('message', listener);\n reject(new Error('autoConnect: Wallet popup was closed before connecting'));\n }\n }, 500);\n });\n}\n\nasync function createAndConnect(\n transport: ConnectTransport,\n config: AutoConnectConfig,\n): Promise<{\n client: ConnectClient;\n connection: ConnectResult;\n cleanup: () => void;\n}> {\n const clientConfig: ConnectClientConfig = {\n transport,\n dapp: config.dapp,\n permissions: config.permissions,\n timeout: config.timeout,\n intentTimeout: config.intentTimeout,\n resumeSessionId: config.resumeSessionId,\n silent: config.silent,\n network: config.network,\n };\n\n const client = new ConnectClient(clientConfig);\n\n try {\n const connection = await client.connect();\n return {\n client,\n connection,\n cleanup: () => transport.destroy(),\n };\n } catch (err) {\n transport.destroy();\n throw err;\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ConnectClient
3
- } from "../../../connect/chunks/chunk-BRYRG2TL.js";
4
- import "../../../connect/chunks/chunk-T4DZCXWS.js";
3
+ } from "../../../connect/chunks/chunk-JYBI36GK.js";
4
+ import "../../../connect/chunks/chunk-FGQ6CKKK.js";
5
5
  import {
6
6
  HOST_READY_TIMEOUT,
7
7
  HOST_READY_TYPE,
@@ -836,6 +836,8 @@ var STORE_KEYS = {
836
836
  deliveryJournal: "delivery-journal",
837
837
  mintJournal: "mint-journal",
838
838
  nftMintJournal: "nft-mint-journal",
839
+ customMintJournal: "custom-mint-journal",
840
+ burnJournal: "burn-journal",
839
841
  shortfalls: "shortfalls",
840
842
  settlingLinks: "settling",
841
843
  streamCursor: (s) => `cursor:${s}`,