@unicitylabs/sphere-sdk 0.1.2 → 0.1.3
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.
- package/dist/core/index.cjs +12 -16
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.js +12 -16
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +121 -677
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +121 -677
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/browser/ipfs.cjs +673 -0
- package/dist/impl/browser/ipfs.cjs.map +1 -0
- package/dist/impl/browser/ipfs.js +640 -0
- package/dist/impl/browser/ipfs.js.map +1 -0
- package/dist/impl/nodejs/index.cjs +3 -0
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.js +3 -0
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +12 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -16
- package/dist/index.d.ts +12 -16
- package/dist/index.js +12 -16
- package/dist/index.js.map +1 -1
- package/package.json +11 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../constants.ts","../../../impl/browser/storage/IpfsStorageProvider.ts"],"sourcesContent":["/**\n * SDK2 Constants\n * Default configuration values and storage keys\n */\n\n// =============================================================================\n// Storage Keys\n// =============================================================================\n\n/** Default prefix for all storage keys */\nexport const STORAGE_PREFIX = 'sphere_' as const;\n\n/**\n * Default encryption key for wallet data\n * WARNING: This is a placeholder. In production, use user-provided password.\n * This key is used when no password is provided to encrypt/decrypt mnemonic.\n */\nexport const DEFAULT_ENCRYPTION_KEY = 'sphere-default-key' as const;\n\n/** Storage keys for wallet data */\nexport const STORAGE_KEYS = {\n /** Encrypted BIP39 mnemonic */\n MNEMONIC: `${STORAGE_PREFIX}mnemonic`,\n /** Encrypted master private key */\n MASTER_KEY: `${STORAGE_PREFIX}master_key`,\n /** BIP32 chain code */\n CHAIN_CODE: `${STORAGE_PREFIX}chain_code`,\n /** HD derivation path (full path like m/44'/0'/0'/0/0) */\n DERIVATION_PATH: `${STORAGE_PREFIX}derivation_path`,\n /** Base derivation path (like m/44'/0'/0' without chain/index) */\n BASE_PATH: `${STORAGE_PREFIX}base_path`,\n /** Derivation mode: bip32, wif_hmac, legacy_hmac */\n DERIVATION_MODE: `${STORAGE_PREFIX}derivation_mode`,\n /** Wallet source: mnemonic, file, unknown */\n WALLET_SOURCE: `${STORAGE_PREFIX}wallet_source`,\n /** Wallet existence flag */\n WALLET_EXISTS: `${STORAGE_PREFIX}wallet_exists`,\n /** Registered nametag (legacy - single address) */\n NAMETAG: `${STORAGE_PREFIX}nametag`,\n /** Current active address index */\n CURRENT_ADDRESS_INDEX: `${STORAGE_PREFIX}current_address_index`,\n /** Address nametags map (JSON: { \"0\": \"alice\", \"1\": \"bob\" }) */\n ADDRESS_NAMETAGS: `${STORAGE_PREFIX}address_nametags`,\n /** Token data */\n TOKENS: `${STORAGE_PREFIX}tokens`,\n /** Pending transfers */\n PENDING_TRANSFERS: `${STORAGE_PREFIX}pending_transfers`,\n /** Transfer outbox */\n OUTBOX: `${STORAGE_PREFIX}outbox`,\n /** Conversations */\n CONVERSATIONS: `${STORAGE_PREFIX}conversations`,\n /** Messages */\n MESSAGES: `${STORAGE_PREFIX}messages`,\n /** Transaction history */\n TRANSACTION_HISTORY: `${STORAGE_PREFIX}transaction_history`,\n /** Archived tokens (spent token history) */\n ARCHIVED_TOKENS: `${STORAGE_PREFIX}archived_tokens`,\n /** Tombstones (records of deleted/spent tokens) */\n TOMBSTONES: `${STORAGE_PREFIX}tombstones`,\n /** Forked tokens (alternative histories) */\n FORKED_TOKENS: `${STORAGE_PREFIX}forked_tokens`,\n} as const;\n\n// =============================================================================\n// Nostr Defaults\n// =============================================================================\n\n/** Default Nostr relays */\nexport const DEFAULT_NOSTR_RELAYS = [\n 'wss://relay.unicity.network',\n 'wss://relay.damus.io',\n 'wss://nos.lol',\n 'wss://relay.nostr.band',\n] as const;\n\n/** Nostr event kinds used by SDK - must match @unicitylabs/nostr-js-sdk */\nexport const NOSTR_EVENT_KINDS = {\n /** NIP-04 encrypted direct message */\n DIRECT_MESSAGE: 4,\n /** Token transfer (Unicity custom - 31113) */\n TOKEN_TRANSFER: 31113,\n /** Payment request (Unicity custom - 31115) */\n PAYMENT_REQUEST: 31115,\n /** Payment request response (Unicity custom - 31116) */\n PAYMENT_REQUEST_RESPONSE: 31116,\n /** Nametag binding (NIP-78 app-specific data) */\n NAMETAG_BINDING: 30078,\n /** Public broadcast */\n BROADCAST: 1,\n} as const;\n\n// =============================================================================\n// Aggregator (Oracle) Defaults\n// =============================================================================\n\n/**\n * Default aggregator URL\n * Note: The aggregator is conceptually an oracle - a trusted service that provides\n * verifiable truth about token state through cryptographic inclusion proofs.\n */\nexport const DEFAULT_AGGREGATOR_URL = 'https://aggregator.unicity.network/rpc' as const;\n\n/** Dev aggregator URL */\nexport const DEV_AGGREGATOR_URL = 'https://dev-aggregator.dyndns.org/rpc' as const;\n\n/** Test aggregator URL (Goggregator) */\nexport const TEST_AGGREGATOR_URL = 'https://goggregator-test.unicity.network' as const;\n\n/** Default aggregator request timeout (ms) */\nexport const DEFAULT_AGGREGATOR_TIMEOUT = 30000;\n\n// =============================================================================\n// IPFS Defaults\n// =============================================================================\n\n/** Default IPFS gateways */\nexport const DEFAULT_IPFS_GATEWAYS = [\n 'https://ipfs.unicity.network',\n 'https://dweb.link',\n 'https://ipfs.io',\n] as const;\n\n/** Unicity IPFS bootstrap peers */\nexport const DEFAULT_IPFS_BOOTSTRAP_PEERS = [\n '/dns4/unicity-ipfs2.dyndns.org/tcp/4001/p2p/12D3KooWLNi5NDPPHbrfJakAQqwBqymYTTwMQXQKEWuCrJNDdmfh',\n '/dns4/unicity-ipfs3.dyndns.org/tcp/4001/p2p/12D3KooWQ4aujVE4ShLjdusNZBdffq3TbzrwT2DuWZY9H1Gxhwn6',\n '/dns4/unicity-ipfs4.dyndns.org/tcp/4001/p2p/12D3KooWJ1ByPfUzUrpYvgxKU8NZrR8i6PU1tUgMEbQX9Hh2DEn1',\n '/dns4/unicity-ipfs5.dyndns.org/tcp/4001/p2p/12D3KooWB1MdZZGHN5B8TvWXntbycfe7Cjcz7n6eZ9eykZadvmDv',\n] as const;\n\n// =============================================================================\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 /** ALPHA token (L1 blockchain) */\n ALPHA: 'ALPHA',\n /** Test token */\n TEST: 'TEST',\n} as const;\n\n// =============================================================================\n// L1 (ALPHA Blockchain) Defaults\n// =============================================================================\n\n/** Default Fulcrum electrum server for mainnet */\nexport const DEFAULT_ELECTRUM_URL = 'wss://fulcrum.alpha.unicity.network:50004' as const;\n\n/** Testnet Fulcrum electrum server */\nexport const TEST_ELECTRUM_URL = 'wss://fulcrum.alpha.testnet.unicity.network:50004' as const;\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/** Network configurations */\nexport const NETWORKS = {\n mainnet: {\n name: 'Mainnet',\n aggregatorUrl: DEFAULT_AGGREGATOR_URL,\n nostrRelays: DEFAULT_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: DEFAULT_ELECTRUM_URL,\n },\n testnet: {\n name: 'Testnet',\n aggregatorUrl: TEST_AGGREGATOR_URL,\n nostrRelays: TEST_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: TEST_ELECTRUM_URL,\n },\n dev: {\n name: 'Development',\n aggregatorUrl: DEV_AGGREGATOR_URL,\n nostrRelays: TEST_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: TEST_ELECTRUM_URL,\n },\n} as const;\n\nexport type NetworkType = keyof typeof NETWORKS;\nexport type NetworkConfig = (typeof NETWORKS)[NetworkType];\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/** 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 * Browser IPFS Storage Provider\n * Implements TokenStorageProvider using IPFS/IPNS for decentralized storage\n *\n * Uses a hybrid approach:\n * - HTTP API to backend gateways for fast publishing/resolution\n * - Helia for browser-based DHT operations as backup\n */\n\nimport type { ProviderStatus, FullIdentity } from '../../../types';\nimport type {\n TokenStorageProvider,\n TxfStorageDataBase,\n TxfTombstone,\n SaveResult,\n LoadResult,\n SyncResult,\n StorageEvent,\n StorageEventCallback,\n} from '../../../storage';\nimport {\n DEFAULT_IPFS_GATEWAYS,\n DEFAULT_IPFS_BOOTSTRAP_PEERS,\n} from '../../../constants';\n\n// Helia and IPFS types (runtime imports are dynamic)\nimport type { Helia } from 'helia';\nimport type { JSON as HeliaJSON } from '@helia/json';\nimport type { PrivateKey } from '@libp2p/interface';\nimport { hkdf } from '@noble/hashes/hkdf.js';\nimport { sha256 } from '@noble/hashes/sha2.js';\n\n// Dynamic import cache\nlet heliaModule: typeof import('helia') | null = null;\nlet heliaJsonModule: typeof import('@helia/json') | null = null;\nlet libp2pBootstrapModule: typeof import('@libp2p/bootstrap') | null = null;\nlet libp2pCryptoModule: typeof import('@libp2p/crypto/keys') | null = null;\nlet libp2pPeerIdModule: typeof import('@libp2p/peer-id') | null = null;\nlet multiformatsCidModule: typeof import('multiformats/cid') | null = null;\n\nasync function loadHeliaModules() {\n if (!heliaModule) {\n [\n heliaModule,\n heliaJsonModule,\n libp2pBootstrapModule,\n libp2pCryptoModule,\n libp2pPeerIdModule,\n multiformatsCidModule,\n ] = await Promise.all([\n import('helia'),\n import('@helia/json'),\n import('@libp2p/bootstrap'),\n import('@libp2p/crypto/keys'),\n import('@libp2p/peer-id'),\n import('multiformats/cid'),\n ]);\n }\n return {\n createHelia: heliaModule!.createHelia,\n json: heliaJsonModule!.json,\n bootstrap: libp2pBootstrapModule!.bootstrap,\n generateKeyPairFromSeed: libp2pCryptoModule!.generateKeyPairFromSeed,\n peerIdFromPrivateKey: libp2pPeerIdModule!.peerIdFromPrivateKey,\n CID: multiformatsCidModule!.CID,\n };\n}\n\n/** HKDF info for IPNS key derivation */\nconst HKDF_INFO = new TextEncoder().encode('ipfs-storage-key');\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface IpfsStorageProviderConfig {\n /** IPFS gateway URLs for HTTP API */\n gateways?: string[];\n /** Bootstrap peers for DHT */\n bootstrapPeers?: string[];\n /** Enable IPNS for mutable addressing */\n enableIpns?: boolean;\n /** IPNS publish timeout (ms) */\n ipnsTimeout?: number;\n /** Content fetch timeout (ms) */\n fetchTimeout?: number;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class IpfsStorageProvider<TData extends TxfStorageDataBase = TxfStorageDataBase>\n implements TokenStorageProvider<TData>\n{\n readonly id = 'ipfs';\n readonly name = 'IPFS Storage';\n readonly type = 'p2p' as const;\n readonly description = 'Decentralized storage via IPFS/IPNS';\n\n private config: Required<IpfsStorageProviderConfig>;\n private identity: FullIdentity | null = null;\n private status: ProviderStatus = 'disconnected';\n private ipnsName: string | null = null;\n private lastCid: string | null = null;\n private eventCallbacks: Set<StorageEventCallback> = new Set();\n\n // Helia instance for browser-based IPFS\n private helia: Helia | null = null;\n private heliaJson: HeliaJSON | null = null;\n private ipnsKeyPair: PrivateKey | null = null;\n\n /** Get the last published CID */\n getLastCid(): string | null {\n return this.lastCid;\n }\n\n // Local cache for faster loads\n private localCache: TData | null = null;\n private cacheTimestamp = 0;\n\n constructor(config?: IpfsStorageProviderConfig) {\n this.config = {\n gateways: config?.gateways ?? [...DEFAULT_IPFS_GATEWAYS],\n bootstrapPeers: config?.bootstrapPeers ?? [...DEFAULT_IPFS_BOOTSTRAP_PEERS],\n enableIpns: config?.enableIpns ?? true,\n ipnsTimeout: config?.ipnsTimeout ?? 30000,\n fetchTimeout: config?.fetchTimeout ?? 15000,\n debug: config?.debug ?? false,\n };\n }\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected') return;\n\n this.status = 'connecting';\n\n try {\n // Test gateway connectivity first (fast path)\n await this.testGatewayConnectivity();\n\n // Initialize Helia for browser-based DHT\n await this.initializeHelia();\n\n this.status = 'connected';\n this.log('Connected to IPFS gateways and Helia initialized');\n } catch (error) {\n this.status = 'error';\n throw new Error(`IPFS connection failed: ${error}`);\n }\n }\n\n /**\n * Initialize Helia browser IPFS node\n */\n private async initializeHelia(): Promise<void> {\n if (this.helia) return;\n\n try {\n this.log('Initializing Helia with bootstrap peers...');\n\n const { createHelia, json, bootstrap } = await loadHeliaModules();\n\n this.helia = await createHelia({\n libp2p: {\n peerDiscovery: [\n bootstrap({ list: this.config.bootstrapPeers }),\n ],\n connectionManager: {\n maxConnections: 10,\n },\n },\n });\n\n this.heliaJson = json(this.helia);\n\n const peerId = this.helia.libp2p.peerId.toString();\n this.log('Helia initialized, browser peer ID:', peerId.slice(0, 20) + '...');\n\n // Log connections after a short delay\n setTimeout(() => {\n const connections = this.helia?.libp2p.getConnections() || [];\n this.log(`Active Helia connections: ${connections.length}`);\n }, 3000);\n } catch (error) {\n this.log('Helia initialization failed (will use HTTP only):', error);\n // Non-fatal - HTTP gateways still work\n }\n }\n\n async disconnect(): Promise<void> {\n // Stop Helia\n if (this.helia) {\n try {\n await this.helia.stop();\n } catch (error) {\n this.log('Error stopping Helia:', error);\n }\n this.helia = null;\n this.heliaJson = null;\n }\n\n this.status = 'disconnected';\n this.localCache = null;\n this.ipnsKeyPair = null;\n this.log('Disconnected from IPFS');\n }\n\n isConnected(): boolean {\n return this.status === 'connected';\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // TokenStorageProvider Implementation\n // ===========================================================================\n\n async setIdentity(identity: FullIdentity): Promise<void> {\n this.identity = identity;\n\n // Derive IPNS key pair from wallet private key using HKDF\n try {\n const { generateKeyPairFromSeed, peerIdFromPrivateKey } = await loadHeliaModules();\n const walletSecret = this.hexToBytes(identity.privateKey);\n const derivedKey = hkdf(sha256, walletSecret, undefined, HKDF_INFO, 32);\n\n // Generate libp2p Ed25519 key pair for IPNS\n this.ipnsKeyPair = await generateKeyPairFromSeed('Ed25519', derivedKey);\n const peerId = peerIdFromPrivateKey(this.ipnsKeyPair);\n this.ipnsName = peerId.toString();\n\n this.log('Identity set, IPNS name:', this.ipnsName);\n } catch {\n // Fallback to provided IPNS name or simple derivation\n this.ipnsName = identity.ipnsName ?? this.deriveIpnsNameSimple(identity.privateKey);\n this.log('Identity set with fallback IPNS name:', this.ipnsName);\n }\n }\n\n async initialize(): Promise<boolean> {\n if (!this.identity) {\n throw new Error('Identity must be set before initialization');\n }\n\n try {\n await this.connect();\n return true;\n } catch {\n return false;\n }\n }\n\n async shutdown(): Promise<void> {\n await this.disconnect();\n }\n\n async save(data: TData): Promise<SaveResult> {\n this.ensureReady();\n this.emitEvent({ type: 'storage:saving', timestamp: Date.now() });\n\n try {\n // Update metadata\n const dataToSave: TData = {\n ...data,\n _meta: {\n ...data._meta,\n updatedAt: Date.now(),\n ipnsName: this.ipnsName ?? undefined,\n },\n };\n\n // Publish to IPFS (parallel to all gateways)\n const cid = await this.publishToGateways(dataToSave);\n\n // Update IPNS if enabled\n if (this.config.enableIpns && this.ipnsName) {\n await this.publishIpns(cid);\n }\n\n // Update local cache\n this.localCache = dataToSave;\n this.cacheTimestamp = Date.now();\n this.lastCid = cid;\n\n this.emitEvent({ type: 'storage:saved', timestamp: Date.now(), data: { cid } });\n\n return {\n success: true,\n cid,\n timestamp: Date.now(),\n };\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n this.emitEvent({ type: 'storage:error', timestamp: Date.now(), error: errorMsg });\n\n return {\n success: false,\n error: errorMsg,\n timestamp: Date.now(),\n };\n }\n }\n\n async load(identifier?: string): Promise<LoadResult<TData>> {\n this.ensureReady();\n this.emitEvent({ type: 'storage:loading', timestamp: Date.now() });\n\n try {\n // Try local cache first (if recent)\n const cacheAge = Date.now() - this.cacheTimestamp;\n if (this.localCache && cacheAge < 60000) {\n this.log('Returning cached data');\n return {\n success: true,\n data: this.localCache,\n source: 'cache',\n timestamp: Date.now(),\n };\n }\n\n // Resolve IPNS or use direct CID\n let cid: string | null = identifier ?? null;\n\n if (!cid && this.config.enableIpns && this.ipnsName) {\n cid = await this.resolveIpns(this.ipnsName);\n }\n\n if (!cid) {\n // No remote data found\n return {\n success: true,\n data: undefined,\n source: 'remote',\n timestamp: Date.now(),\n };\n }\n\n // Fetch content\n const data = await this.fetchFromGateways<TData>(cid);\n\n // Update cache\n this.localCache = data;\n this.cacheTimestamp = Date.now();\n this.lastCid = cid;\n\n this.emitEvent({ type: 'storage:loaded', timestamp: Date.now() });\n\n return {\n success: true,\n data,\n source: 'remote',\n timestamp: Date.now(),\n };\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n this.emitEvent({ type: 'storage:error', timestamp: Date.now(), error: errorMsg });\n\n // Fallback to cache on error\n if (this.localCache) {\n return {\n success: true,\n data: this.localCache,\n source: 'cache',\n timestamp: Date.now(),\n };\n }\n\n return {\n success: false,\n error: errorMsg,\n source: 'remote',\n timestamp: Date.now(),\n };\n }\n }\n\n async sync(localData: TData): Promise<SyncResult<TData>> {\n this.ensureReady();\n this.emitEvent({ type: 'sync:started', timestamp: Date.now() });\n\n try {\n // Load remote data\n const remoteResult = await this.load();\n const remoteData = remoteResult.data;\n\n if (!remoteData) {\n // No remote data, just save local\n await this.save(localData);\n this.emitEvent({ type: 'sync:completed', timestamp: Date.now() });\n return {\n success: true,\n merged: localData,\n added: 0,\n removed: 0,\n conflicts: 0,\n };\n }\n\n // Merge data\n const mergeResult = this.mergeData(localData, remoteData);\n\n // Save merged result\n await this.save(mergeResult.merged);\n\n this.emitEvent({ type: 'sync:completed', timestamp: Date.now() });\n\n return {\n success: true,\n merged: mergeResult.merged,\n added: mergeResult.added,\n removed: mergeResult.removed,\n conflicts: mergeResult.conflicts,\n };\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n this.emitEvent({ type: 'sync:error', timestamp: Date.now(), error: errorMsg });\n\n return {\n success: false,\n added: 0,\n removed: 0,\n conflicts: 0,\n error: errorMsg,\n };\n }\n }\n\n async exists(): Promise<boolean> {\n if (!this.ipnsName) return false;\n\n try {\n const cid = await this.resolveIpns(this.ipnsName);\n return cid !== null;\n } catch {\n return false;\n }\n }\n\n async clear(): Promise<boolean> {\n // IPFS is immutable, we can only publish empty data\n const emptyData = {\n _meta: {\n version: 0,\n address: this.identity?.address ?? '',\n formatVersion: '2.0',\n updatedAt: Date.now(),\n },\n _tombstones: [],\n } as unknown as TData;\n\n const result = await this.save(emptyData);\n return result.success;\n }\n\n onEvent(callback: StorageEventCallback): () => void {\n this.eventCallbacks.add(callback);\n return () => this.eventCallbacks.delete(callback);\n }\n\n // ===========================================================================\n // Private: IPFS Operations\n // ===========================================================================\n\n private async testGatewayConnectivity(): Promise<void> {\n const gateway = this.config.gateways[0];\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 5000);\n\n try {\n const response = await fetch(`${gateway}/api/v0/version`, {\n method: 'POST',\n signal: controller.signal,\n });\n if (!response.ok) throw new Error('Gateway not responding');\n } finally {\n clearTimeout(timeout);\n }\n }\n\n private async publishToGateways(data: TData): Promise<string> {\n const content = JSON.stringify(data);\n const blob = new Blob([content], { type: 'application/json' });\n\n // Strategy: Try both HTTP and Helia in parallel, return first success\n const promises: Promise<string>[] = [];\n\n // HTTP gateway publishing (fast path)\n for (const gateway of this.config.gateways) {\n promises.push(this.publishToGateway(gateway, blob));\n }\n\n // Helia DHT publishing (backup path)\n if (this.heliaJson) {\n promises.push(this.publishToHelia(data));\n }\n\n try {\n const cid = await Promise.any(promises);\n this.log('Published to IPFS, CID:', cid);\n return cid;\n } catch {\n throw new Error('All publish attempts failed');\n }\n }\n\n /**\n * Publish data via Helia (browser DHT)\n */\n private async publishToHelia(data: TData): Promise<string> {\n if (!this.heliaJson) {\n throw new Error('Helia not initialized');\n }\n\n const cid = await this.heliaJson.add(data);\n this.log('Published via Helia, CID:', cid.toString());\n return cid.toString();\n }\n\n private async publishToGateway(gateway: string, blob: Blob): Promise<string> {\n const formData = new FormData();\n formData.append('file', blob);\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.fetchTimeout);\n\n try {\n const response = await fetch(`${gateway}/api/v0/add?pin=true`, {\n method: 'POST',\n body: formData,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`Gateway ${gateway} returned ${response.status}`);\n }\n\n const result = await response.json();\n return result.Hash;\n } finally {\n clearTimeout(timeout);\n }\n }\n\n private async publishIpns(cid: string): Promise<void> {\n if (!this.identity) return;\n\n // Publish to all gateways in parallel\n const promises = this.config.gateways.map((gateway) =>\n this.publishIpnsToGateway(gateway, cid).catch(() => null)\n );\n\n await Promise.allSettled(promises);\n this.log('Published IPNS:', this.ipnsName, '->', cid);\n }\n\n private async publishIpnsToGateway(gateway: string, cid: string): Promise<void> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.ipnsTimeout);\n\n try {\n const response = await fetch(\n `${gateway}/api/v0/name/publish?arg=${cid}&key=${this.ipnsName}`,\n {\n method: 'POST',\n signal: controller.signal,\n }\n );\n\n if (!response.ok) {\n throw new Error(`IPNS publish failed: ${response.status}`);\n }\n } finally {\n clearTimeout(timeout);\n }\n }\n\n private async resolveIpns(name: string): Promise<string | null> {\n // Try each gateway\n for (const gateway of this.config.gateways) {\n try {\n return await this.resolveIpnsFromGateway(gateway, name);\n } catch {\n continue;\n }\n }\n return null;\n }\n\n private async resolveIpnsFromGateway(gateway: string, name: string): Promise<string> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.fetchTimeout);\n\n try {\n const response = await fetch(`${gateway}/api/v0/name/resolve?arg=${name}`, {\n method: 'POST',\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`IPNS resolve failed: ${response.status}`);\n }\n\n const result = await response.json();\n // Path is like \"/ipfs/Qm...\"\n return result.Path.replace('/ipfs/', '');\n } finally {\n clearTimeout(timeout);\n }\n }\n\n private async fetchFromGateways<T>(cid: string): Promise<T> {\n // Strategy: Try both HTTP and Helia in parallel\n const promises: Promise<T>[] = [];\n\n // HTTP gateway fetching (fast path)\n for (const gateway of this.config.gateways) {\n promises.push(this.fetchFromGateway<T>(gateway, cid));\n }\n\n // Helia DHT fetching (backup path)\n if (this.heliaJson) {\n promises.push(this.fetchFromHelia<T>(cid));\n }\n\n return Promise.any(promises);\n }\n\n /**\n * Fetch content via Helia (browser DHT)\n */\n private async fetchFromHelia<T>(cidString: string): Promise<T> {\n if (!this.heliaJson) {\n throw new Error('Helia not initialized');\n }\n\n const { CID } = await loadHeliaModules();\n const cid = CID.parse(cidString);\n const data = await this.heliaJson.get(cid);\n this.log('Fetched via Helia, CID:', cidString);\n return data as T;\n }\n\n private async fetchFromGateway<T>(gateway: string, cid: string): Promise<T> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.fetchTimeout);\n\n try {\n const response = await fetch(`${gateway}/ipfs/${cid}`, {\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`Fetch failed: ${response.status}`);\n }\n\n return response.json();\n } finally {\n clearTimeout(timeout);\n }\n }\n\n // ===========================================================================\n // Private: Merge Logic\n // ===========================================================================\n\n private mergeData(\n local: TData,\n remote: TData\n ): { merged: TData; added: number; removed: number; conflicts: number } {\n const localVersion = local._meta?.version ?? 0;\n const remoteVersion = remote._meta?.version ?? 0;\n\n // Simple strategy: newer version wins for meta\n const baseMeta = remoteVersion > localVersion ? remote._meta : local._meta;\n\n // Merge tombstones (union)\n const tombstones = new Map<string, TxfTombstone>();\n for (const t of local._tombstones ?? []) {\n tombstones.set(t.tokenId, t);\n }\n for (const t of remote._tombstones ?? []) {\n const existing = tombstones.get(t.tokenId);\n if (!existing || t.timestamp > existing.timestamp) {\n tombstones.set(t.tokenId, t);\n }\n }\n\n // Merge token entries (newer wins, respect tombstones)\n const merged = {\n _meta: {\n ...baseMeta,\n version: Math.max(localVersion, remoteVersion) + 1,\n updatedAt: Date.now(),\n },\n _tombstones: Array.from(tombstones.values()),\n } as unknown as TData;\n\n let added = 0;\n let conflicts = 0;\n\n // Process all token entries from both sources\n const processedKeys = new Set<string>();\n\n for (const key of Object.keys(local)) {\n if (!key.startsWith('_') || key === '_meta' || key === '_tombstones') continue;\n processedKeys.add(key);\n\n const tokenId = key.slice(1); // Remove leading _\n if (tombstones.has(tokenId)) continue; // Deleted\n\n const localToken = local[key as keyof TData];\n const remoteToken = remote[key as keyof TData];\n\n if (remoteToken) {\n // Both have it - conflict resolution\n conflicts++;\n // Use local (could be smarter based on timestamps)\n (merged as Record<string, unknown>)[key] = localToken;\n } else {\n (merged as Record<string, unknown>)[key] = localToken;\n }\n }\n\n for (const key of Object.keys(remote)) {\n if (!key.startsWith('_') || key === '_meta' || key === '_tombstones') continue;\n if (processedKeys.has(key)) continue;\n\n const tokenId = key.slice(1);\n if (tombstones.has(tokenId)) continue;\n\n (merged as Record<string, unknown>)[key] = remote[key as keyof TData];\n added++;\n }\n\n return { merged, added, removed: 0, conflicts };\n }\n\n // ===========================================================================\n // Private: Helpers\n // ===========================================================================\n\n private ensureReady(): void {\n if (this.status !== 'connected') {\n throw new Error('IpfsStorageProvider not connected');\n }\n if (!this.identity) {\n throw new Error('Identity not set');\n }\n }\n\n /**\n * Simple IPNS name derivation (fallback when libp2p is unavailable)\n */\n private deriveIpnsNameSimple(privateKey: string): string {\n // Fallback: use truncated hash of private key\n return `12D3KooW${privateKey.slice(0, 40)}`;\n }\n\n /**\n * Convert hex string to Uint8Array\n */\n private hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(hex.substr(i * 2, 2), 16);\n }\n return bytes;\n }\n\n private emitEvent(event: StorageEvent): void {\n for (const callback of this.eventCallbacks) {\n try {\n callback(event);\n } catch (error) {\n console.error('[IpfsStorageProvider] Event callback error:', error);\n }\n }\n }\n\n private log(...args: unknown[]): void {\n if (this.config.debug) {\n console.log('[IpfsStorageProvider]', ...args);\n }\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\nexport function createIpfsStorageProvider<TData extends TxfStorageDataBase = TxfStorageDataBase>(\n config?: IpfsStorageProviderConfig\n): IpfsStorageProvider<TData> {\n return new IpfsStorageProvider<TData>(config);\n}\n"],"mappings":";AAUO,IAAM,iBAAiB;AAUvB,IAAM,eAAe;AAAA;AAAA,EAE1B,UAAU,GAAG,cAAc;AAAA;AAAA,EAE3B,YAAY,GAAG,cAAc;AAAA;AAAA,EAE7B,YAAY,GAAG,cAAc;AAAA;AAAA,EAE7B,iBAAiB,GAAG,cAAc;AAAA;AAAA,EAElC,WAAW,GAAG,cAAc;AAAA;AAAA,EAE5B,iBAAiB,GAAG,cAAc;AAAA;AAAA,EAElC,eAAe,GAAG,cAAc;AAAA;AAAA,EAEhC,eAAe,GAAG,cAAc;AAAA;AAAA,EAEhC,SAAS,GAAG,cAAc;AAAA;AAAA,EAE1B,uBAAuB,GAAG,cAAc;AAAA;AAAA,EAExC,kBAAkB,GAAG,cAAc;AAAA;AAAA,EAEnC,QAAQ,GAAG,cAAc;AAAA;AAAA,EAEzB,mBAAmB,GAAG,cAAc;AAAA;AAAA,EAEpC,QAAQ,GAAG,cAAc;AAAA;AAAA,EAEzB,eAAe,GAAG,cAAc;AAAA;AAAA,EAEhC,UAAU,GAAG,cAAc;AAAA;AAAA,EAE3B,qBAAqB,GAAG,cAAc;AAAA;AAAA,EAEtC,iBAAiB,GAAG,cAAc;AAAA;AAAA,EAElC,YAAY,GAAG,cAAc;AAAA;AAAA,EAE7B,eAAe,GAAG,cAAc;AAClC;AAuDO,IAAM,wBAAwB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AACF;AAGO,IAAM,+BAA+B;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,IAAM,oBAAoB;AAG1B,IAAM,0BAA0B,GAAG,iBAAiB;;;AC7G3D,SAAS,YAAY;AACrB,SAAS,cAAc;AAGvB,IAAI,cAA6C;AACjD,IAAI,kBAAuD;AAC3D,IAAI,wBAAmE;AACvE,IAAI,qBAAkE;AACtE,IAAI,qBAA8D;AAClE,IAAI,wBAAkE;AAEtE,eAAe,mBAAmB;AAChC,MAAI,CAAC,aAAa;AAChB;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,IAAI,MAAM,QAAQ,IAAI;AAAA,MACpB,OAAO,OAAO;AAAA,MACd,OAAO,aAAa;AAAA,MACpB,OAAO,mBAAmB;AAAA,MAC1B,OAAO,qBAAqB;AAAA,MAC5B,OAAO,iBAAiB;AAAA,MACxB,OAAO,kBAAkB;AAAA,IAC3B,CAAC;AAAA,EACH;AACA,SAAO;AAAA,IACL,aAAa,YAAa;AAAA,IAC1B,MAAM,gBAAiB;AAAA,IACvB,WAAW,sBAAuB;AAAA,IAClC,yBAAyB,mBAAoB;AAAA,IAC7C,sBAAsB,mBAAoB;AAAA,IAC1C,KAAK,sBAAuB;AAAA,EAC9B;AACF;AAGA,IAAM,YAAY,IAAI,YAAY,EAAE,OAAO,kBAAkB;AAyBtD,IAAM,sBAAN,MAEP;AAAA,EACW,KAAK;AAAA,EACL,OAAO;AAAA,EACP,OAAO;AAAA,EACP,cAAc;AAAA,EAEf;AAAA,EACA,WAAgC;AAAA,EAChC,SAAyB;AAAA,EACzB,WAA0B;AAAA,EAC1B,UAAyB;AAAA,EACzB,iBAA4C,oBAAI,IAAI;AAAA;AAAA,EAGpD,QAAsB;AAAA,EACtB,YAA8B;AAAA,EAC9B,cAAiC;AAAA;AAAA,EAGzC,aAA4B;AAC1B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGQ,aAA2B;AAAA,EAC3B,iBAAiB;AAAA,EAEzB,YAAY,QAAoC;AAC9C,SAAK,SAAS;AAAA,MACZ,UAAU,QAAQ,YAAY,CAAC,GAAG,qBAAqB;AAAA,MACvD,gBAAgB,QAAQ,kBAAkB,CAAC,GAAG,4BAA4B;AAAA,MAC1E,YAAY,QAAQ,cAAc;AAAA,MAClC,aAAa,QAAQ,eAAe;AAAA,MACpC,cAAc,QAAQ,gBAAgB;AAAA,MACtC,OAAO,QAAQ,SAAS;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAAyB;AAC7B,QAAI,KAAK,WAAW,YAAa;AAEjC,SAAK,SAAS;AAEd,QAAI;AAEF,YAAM,KAAK,wBAAwB;AAGnC,YAAM,KAAK,gBAAgB;AAE3B,WAAK,SAAS;AACd,WAAK,IAAI,kDAAkD;AAAA,IAC7D,SAAS,OAAO;AACd,WAAK,SAAS;AACd,YAAM,IAAI,MAAM,2BAA2B,KAAK,EAAE;AAAA,IACpD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,kBAAiC;AAC7C,QAAI,KAAK,MAAO;AAEhB,QAAI;AACF,WAAK,IAAI,4CAA4C;AAErD,YAAM,EAAE,aAAa,MAAM,UAAU,IAAI,MAAM,iBAAiB;AAEhE,WAAK,QAAQ,MAAM,YAAY;AAAA,QAC7B,QAAQ;AAAA,UACN,eAAe;AAAA,YACb,UAAU,EAAE,MAAM,KAAK,OAAO,eAAe,CAAC;AAAA,UAChD;AAAA,UACA,mBAAmB;AAAA,YACjB,gBAAgB;AAAA,UAClB;AAAA,QACF;AAAA,MACF,CAAC;AAED,WAAK,YAAY,KAAK,KAAK,KAAK;AAEhC,YAAM,SAAS,KAAK,MAAM,OAAO,OAAO,SAAS;AACjD,WAAK,IAAI,uCAAuC,OAAO,MAAM,GAAG,EAAE,IAAI,KAAK;AAG3E,iBAAW,MAAM;AACf,cAAM,cAAc,KAAK,OAAO,OAAO,eAAe,KAAK,CAAC;AAC5D,aAAK,IAAI,6BAA6B,YAAY,MAAM,EAAE;AAAA,MAC5D,GAAG,GAAI;AAAA,IACT,SAAS,OAAO;AACd,WAAK,IAAI,qDAAqD,KAAK;AAAA,IAErE;AAAA,EACF;AAAA,EAEA,MAAM,aAA4B;AAEhC,QAAI,KAAK,OAAO;AACd,UAAI;AACF,cAAM,KAAK,MAAM,KAAK;AAAA,MACxB,SAAS,OAAO;AACd,aAAK,IAAI,yBAAyB,KAAK;AAAA,MACzC;AACA,WAAK,QAAQ;AACb,WAAK,YAAY;AAAA,IACnB;AAEA,SAAK,SAAS;AACd,SAAK,aAAa;AAClB,SAAK,cAAc;AACnB,SAAK,IAAI,wBAAwB;AAAA,EACnC;AAAA,EAEA,cAAuB;AACrB,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA,EAEA,YAA4B;AAC1B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,YAAY,UAAuC;AACvD,SAAK,WAAW;AAGhB,QAAI;AACF,YAAM,EAAE,yBAAyB,qBAAqB,IAAI,MAAM,iBAAiB;AACjF,YAAM,eAAe,KAAK,WAAW,SAAS,UAAU;AACxD,YAAM,aAAa,KAAK,QAAQ,cAAc,QAAW,WAAW,EAAE;AAGtE,WAAK,cAAc,MAAM,wBAAwB,WAAW,UAAU;AACtE,YAAM,SAAS,qBAAqB,KAAK,WAAW;AACpD,WAAK,WAAW,OAAO,SAAS;AAEhC,WAAK,IAAI,4BAA4B,KAAK,QAAQ;AAAA,IACpD,QAAQ;AAEN,WAAK,WAAW,SAAS,YAAY,KAAK,qBAAqB,SAAS,UAAU;AAClF,WAAK,IAAI,yCAAyC,KAAK,QAAQ;AAAA,IACjE;AAAA,EACF;AAAA,EAEA,MAAM,aAA+B;AACnC,QAAI,CAAC,KAAK,UAAU;AAClB,YAAM,IAAI,MAAM,4CAA4C;AAAA,IAC9D;AAEA,QAAI;AACF,YAAM,KAAK,QAAQ;AACnB,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,WAA0B;AAC9B,UAAM,KAAK,WAAW;AAAA,EACxB;AAAA,EAEA,MAAM,KAAK,MAAkC;AAC3C,SAAK,YAAY;AACjB,SAAK,UAAU,EAAE,MAAM,kBAAkB,WAAW,KAAK,IAAI,EAAE,CAAC;AAEhE,QAAI;AAEF,YAAM,aAAoB;AAAA,QACxB,GAAG;AAAA,QACH,OAAO;AAAA,UACL,GAAG,KAAK;AAAA,UACR,WAAW,KAAK,IAAI;AAAA,UACpB,UAAU,KAAK,YAAY;AAAA,QAC7B;AAAA,MACF;AAGA,YAAM,MAAM,MAAM,KAAK,kBAAkB,UAAU;AAGnD,UAAI,KAAK,OAAO,cAAc,KAAK,UAAU;AAC3C,cAAM,KAAK,YAAY,GAAG;AAAA,MAC5B;AAGA,WAAK,aAAa;AAClB,WAAK,iBAAiB,KAAK,IAAI;AAC/B,WAAK,UAAU;AAEf,WAAK,UAAU,EAAE,MAAM,iBAAiB,WAAW,KAAK,IAAI,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC;AAE9E,aAAO;AAAA,QACL,SAAS;AAAA,QACT;AAAA,QACA,WAAW,KAAK,IAAI;AAAA,MACtB;AAAA,IACF,SAAS,OAAO;AACd,YAAM,WAAW,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACtE,WAAK,UAAU,EAAE,MAAM,iBAAiB,WAAW,KAAK,IAAI,GAAG,OAAO,SAAS,CAAC;AAEhF,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO;AAAA,QACP,WAAW,KAAK,IAAI;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,YAAiD;AAC1D,SAAK,YAAY;AACjB,SAAK,UAAU,EAAE,MAAM,mBAAmB,WAAW,KAAK,IAAI,EAAE,CAAC;AAEjE,QAAI;AAEF,YAAM,WAAW,KAAK,IAAI,IAAI,KAAK;AACnC,UAAI,KAAK,cAAc,WAAW,KAAO;AACvC,aAAK,IAAI,uBAAuB;AAChC,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM,KAAK;AAAA,UACX,QAAQ;AAAA,UACR,WAAW,KAAK,IAAI;AAAA,QACtB;AAAA,MACF;AAGA,UAAI,MAAqB,cAAc;AAEvC,UAAI,CAAC,OAAO,KAAK,OAAO,cAAc,KAAK,UAAU;AACnD,cAAM,MAAM,KAAK,YAAY,KAAK,QAAQ;AAAA,MAC5C;AAEA,UAAI,CAAC,KAAK;AAER,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,WAAW,KAAK,IAAI;AAAA,QACtB;AAAA,MACF;AAGA,YAAM,OAAO,MAAM,KAAK,kBAAyB,GAAG;AAGpD,WAAK,aAAa;AAClB,WAAK,iBAAiB,KAAK,IAAI;AAC/B,WAAK,UAAU;AAEf,WAAK,UAAU,EAAE,MAAM,kBAAkB,WAAW,KAAK,IAAI,EAAE,CAAC;AAEhE,aAAO;AAAA,QACL,SAAS;AAAA,QACT;AAAA,QACA,QAAQ;AAAA,QACR,WAAW,KAAK,IAAI;AAAA,MACtB;AAAA,IACF,SAAS,OAAO;AACd,YAAM,WAAW,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACtE,WAAK,UAAU,EAAE,MAAM,iBAAiB,WAAW,KAAK,IAAI,GAAG,OAAO,SAAS,CAAC;AAGhF,UAAI,KAAK,YAAY;AACnB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM,KAAK;AAAA,UACX,QAAQ;AAAA,UACR,WAAW,KAAK,IAAI;AAAA,QACtB;AAAA,MACF;AAEA,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,WAAW,KAAK,IAAI;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,WAA8C;AACvD,SAAK,YAAY;AACjB,SAAK,UAAU,EAAE,MAAM,gBAAgB,WAAW,KAAK,IAAI,EAAE,CAAC;AAE9D,QAAI;AAEF,YAAM,eAAe,MAAM,KAAK,KAAK;AACrC,YAAM,aAAa,aAAa;AAEhC,UAAI,CAAC,YAAY;AAEf,cAAM,KAAK,KAAK,SAAS;AACzB,aAAK,UAAU,EAAE,MAAM,kBAAkB,WAAW,KAAK,IAAI,EAAE,CAAC;AAChE,eAAO;AAAA,UACL,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,SAAS;AAAA,UACT,WAAW;AAAA,QACb;AAAA,MACF;AAGA,YAAM,cAAc,KAAK,UAAU,WAAW,UAAU;AAGxD,YAAM,KAAK,KAAK,YAAY,MAAM;AAElC,WAAK,UAAU,EAAE,MAAM,kBAAkB,WAAW,KAAK,IAAI,EAAE,CAAC;AAEhE,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ,YAAY;AAAA,QACpB,OAAO,YAAY;AAAA,QACnB,SAAS,YAAY;AAAA,QACrB,WAAW,YAAY;AAAA,MACzB;AAAA,IACF,SAAS,OAAO;AACd,YAAM,WAAW,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACtE,WAAK,UAAU,EAAE,MAAM,cAAc,WAAW,KAAK,IAAI,GAAG,OAAO,SAAS,CAAC;AAE7E,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO;AAAA,QACP,SAAS;AAAA,QACT,WAAW;AAAA,QACX,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,SAA2B;AAC/B,QAAI,CAAC,KAAK,SAAU,QAAO;AAE3B,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,YAAY,KAAK,QAAQ;AAChD,aAAO,QAAQ;AAAA,IACjB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,QAA0B;AAE9B,UAAM,YAAY;AAAA,MAChB,OAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,KAAK,UAAU,WAAW;AAAA,QACnC,eAAe;AAAA,QACf,WAAW,KAAK,IAAI;AAAA,MACtB;AAAA,MACA,aAAa,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,MAAM,KAAK,KAAK,SAAS;AACxC,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,QAAQ,UAA4C;AAClD,SAAK,eAAe,IAAI,QAAQ;AAChC,WAAO,MAAM,KAAK,eAAe,OAAO,QAAQ;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,0BAAyC;AACrD,UAAM,UAAU,KAAK,OAAO,SAAS,CAAC;AACtC,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,GAAI;AAEzD,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,OAAO,mBAAmB;AAAA,QACxD,QAAQ;AAAA,QACR,QAAQ,WAAW;AAAA,MACrB,CAAC;AACD,UAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,wBAAwB;AAAA,IAC5D,UAAE;AACA,mBAAa,OAAO;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAc,kBAAkB,MAA8B;AAC5D,UAAM,UAAU,KAAK,UAAU,IAAI;AACnC,UAAM,OAAO,IAAI,KAAK,CAAC,OAAO,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAG7D,UAAM,WAA8B,CAAC;AAGrC,eAAW,WAAW,KAAK,OAAO,UAAU;AAC1C,eAAS,KAAK,KAAK,iBAAiB,SAAS,IAAI,CAAC;AAAA,IACpD;AAGA,QAAI,KAAK,WAAW;AAClB,eAAS,KAAK,KAAK,eAAe,IAAI,CAAC;AAAA,IACzC;AAEA,QAAI;AACF,YAAM,MAAM,MAAM,QAAQ,IAAI,QAAQ;AACtC,WAAK,IAAI,2BAA2B,GAAG;AACvC,aAAO;AAAA,IACT,QAAQ;AACN,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,eAAe,MAA8B;AACzD,QAAI,CAAC,KAAK,WAAW;AACnB,YAAM,IAAI,MAAM,uBAAuB;AAAA,IACzC;AAEA,UAAM,MAAM,MAAM,KAAK,UAAU,IAAI,IAAI;AACzC,SAAK,IAAI,6BAA6B,IAAI,SAAS,CAAC;AACpD,WAAO,IAAI,SAAS;AAAA,EACtB;AAAA,EAEA,MAAc,iBAAiB,SAAiB,MAA6B;AAC3E,UAAM,WAAW,IAAI,SAAS;AAC9B,aAAS,OAAO,QAAQ,IAAI;AAE5B,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO,YAAY;AAE7E,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,OAAO,wBAAwB;AAAA,QAC7D,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,QAAQ,WAAW;AAAA,MACrB,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,WAAW,OAAO,aAAa,SAAS,MAAM,EAAE;AAAA,MAClE;AAEA,YAAM,SAAS,MAAM,SAAS,KAAK;AACnC,aAAO,OAAO;AAAA,IAChB,UAAE;AACA,mBAAa,OAAO;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAc,YAAY,KAA4B;AACpD,QAAI,CAAC,KAAK,SAAU;AAGpB,UAAM,WAAW,KAAK,OAAO,SAAS;AAAA,MAAI,CAAC,YACzC,KAAK,qBAAqB,SAAS,GAAG,EAAE,MAAM,MAAM,IAAI;AAAA,IAC1D;AAEA,UAAM,QAAQ,WAAW,QAAQ;AACjC,SAAK,IAAI,mBAAmB,KAAK,UAAU,MAAM,GAAG;AAAA,EACtD;AAAA,EAEA,MAAc,qBAAqB,SAAiB,KAA4B;AAC9E,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO,WAAW;AAE5E,QAAI;AACF,YAAM,WAAW,MAAM;AAAA,QACrB,GAAG,OAAO,4BAA4B,GAAG,QAAQ,KAAK,QAAQ;AAAA,QAC9D;AAAA,UACE,QAAQ;AAAA,UACR,QAAQ,WAAW;AAAA,QACrB;AAAA,MACF;AAEA,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,wBAAwB,SAAS,MAAM,EAAE;AAAA,MAC3D;AAAA,IACF,UAAE;AACA,mBAAa,OAAO;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAc,YAAY,MAAsC;AAE9D,eAAW,WAAW,KAAK,OAAO,UAAU;AAC1C,UAAI;AACF,eAAO,MAAM,KAAK,uBAAuB,SAAS,IAAI;AAAA,MACxD,QAAQ;AACN;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,uBAAuB,SAAiB,MAA+B;AACnF,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO,YAAY;AAE7E,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,OAAO,4BAA4B,IAAI,IAAI;AAAA,QACzE,QAAQ;AAAA,QACR,QAAQ,WAAW;AAAA,MACrB,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,wBAAwB,SAAS,MAAM,EAAE;AAAA,MAC3D;AAEA,YAAM,SAAS,MAAM,SAAS,KAAK;AAEnC,aAAO,OAAO,KAAK,QAAQ,UAAU,EAAE;AAAA,IACzC,UAAE;AACA,mBAAa,OAAO;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAc,kBAAqB,KAAyB;AAE1D,UAAM,WAAyB,CAAC;AAGhC,eAAW,WAAW,KAAK,OAAO,UAAU;AAC1C,eAAS,KAAK,KAAK,iBAAoB,SAAS,GAAG,CAAC;AAAA,IACtD;AAGA,QAAI,KAAK,WAAW;AAClB,eAAS,KAAK,KAAK,eAAkB,GAAG,CAAC;AAAA,IAC3C;AAEA,WAAO,QAAQ,IAAI,QAAQ;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,eAAkB,WAA+B;AAC7D,QAAI,CAAC,KAAK,WAAW;AACnB,YAAM,IAAI,MAAM,uBAAuB;AAAA,IACzC;AAEA,UAAM,EAAE,IAAI,IAAI,MAAM,iBAAiB;AACvC,UAAM,MAAM,IAAI,MAAM,SAAS;AAC/B,UAAM,OAAO,MAAM,KAAK,UAAU,IAAI,GAAG;AACzC,SAAK,IAAI,2BAA2B,SAAS;AAC7C,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,iBAAoB,SAAiB,KAAyB;AAC1E,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO,YAAY;AAE7E,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,OAAO,SAAS,GAAG,IAAI;AAAA,QACrD,QAAQ,WAAW;AAAA,MACrB,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,iBAAiB,SAAS,MAAM,EAAE;AAAA,MACpD;AAEA,aAAO,SAAS,KAAK;AAAA,IACvB,UAAE;AACA,mBAAa,OAAO;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,UACN,OACA,QACsE;AACtE,UAAM,eAAe,MAAM,OAAO,WAAW;AAC7C,UAAM,gBAAgB,OAAO,OAAO,WAAW;AAG/C,UAAM,WAAW,gBAAgB,eAAe,OAAO,QAAQ,MAAM;AAGrE,UAAM,aAAa,oBAAI,IAA0B;AACjD,eAAW,KAAK,MAAM,eAAe,CAAC,GAAG;AACvC,iBAAW,IAAI,EAAE,SAAS,CAAC;AAAA,IAC7B;AACA,eAAW,KAAK,OAAO,eAAe,CAAC,GAAG;AACxC,YAAM,WAAW,WAAW,IAAI,EAAE,OAAO;AACzC,UAAI,CAAC,YAAY,EAAE,YAAY,SAAS,WAAW;AACjD,mBAAW,IAAI,EAAE,SAAS,CAAC;AAAA,MAC7B;AAAA,IACF;AAGA,UAAM,SAAS;AAAA,MACb,OAAO;AAAA,QACL,GAAG;AAAA,QACH,SAAS,KAAK,IAAI,cAAc,aAAa,IAAI;AAAA,QACjD,WAAW,KAAK,IAAI;AAAA,MACtB;AAAA,MACA,aAAa,MAAM,KAAK,WAAW,OAAO,CAAC;AAAA,IAC7C;AAEA,QAAI,QAAQ;AACZ,QAAI,YAAY;AAGhB,UAAM,gBAAgB,oBAAI,IAAY;AAEtC,eAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AACpC,UAAI,CAAC,IAAI,WAAW,GAAG,KAAK,QAAQ,WAAW,QAAQ,cAAe;AACtE,oBAAc,IAAI,GAAG;AAErB,YAAM,UAAU,IAAI,MAAM,CAAC;AAC3B,UAAI,WAAW,IAAI,OAAO,EAAG;AAE7B,YAAM,aAAa,MAAM,GAAkB;AAC3C,YAAM,cAAc,OAAO,GAAkB;AAE7C,UAAI,aAAa;AAEf;AAEA,QAAC,OAAmC,GAAG,IAAI;AAAA,MAC7C,OAAO;AACL,QAAC,OAAmC,GAAG,IAAI;AAAA,MAC7C;AAAA,IACF;AAEA,eAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACrC,UAAI,CAAC,IAAI,WAAW,GAAG,KAAK,QAAQ,WAAW,QAAQ,cAAe;AACtE,UAAI,cAAc,IAAI,GAAG,EAAG;AAE5B,YAAM,UAAU,IAAI,MAAM,CAAC;AAC3B,UAAI,WAAW,IAAI,OAAO,EAAG;AAE7B,MAAC,OAAmC,GAAG,IAAI,OAAO,GAAkB;AACpE;AAAA,IACF;AAEA,WAAO,EAAE,QAAQ,OAAO,SAAS,GAAG,UAAU;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAMQ,cAAoB;AAC1B,QAAI,KAAK,WAAW,aAAa;AAC/B,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AACA,QAAI,CAAC,KAAK,UAAU;AAClB,YAAM,IAAI,MAAM,kBAAkB;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAAqB,YAA4B;AAEvD,WAAO,WAAW,WAAW,MAAM,GAAG,EAAE,CAAC;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAW,KAAyB;AAC1C,UAAM,QAAQ,IAAI,WAAW,IAAI,SAAS,CAAC;AAC3C,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAM,CAAC,IAAI,SAAS,IAAI,OAAO,IAAI,GAAG,CAAC,GAAG,EAAE;AAAA,IAC9C;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,OAA2B;AAC3C,eAAW,YAAY,KAAK,gBAAgB;AAC1C,UAAI;AACF,iBAAS,KAAK;AAAA,MAChB,SAAS,OAAO;AACd,gBAAQ,MAAM,+CAA+C,KAAK;AAAA,MACpE;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,OAAO,MAAuB;AACpC,QAAI,KAAK,OAAO,OAAO;AACrB,cAAQ,IAAI,yBAAyB,GAAG,IAAI;AAAA,IAC9C;AAAA,EACF;AACF;AAMO,SAAS,0BACd,QAC4B;AAC5B,SAAO,IAAI,oBAA2B,MAAM;AAC9C;","names":[]}
|