@qntx/nostr 0.5.0 → 0.7.0
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/{abort-Ba8tDrOI.mjs → abort-DBq2STPj.mjs} +13 -6
- package/dist/abort-DBq2STPj.mjs.map +1 -0
- package/dist/{builder-9tjI6WQ3.mjs → builder-CdLE4J6O.mjs} +11 -17
- package/dist/builder-CdLE4J6O.mjs.map +1 -0
- package/dist/{builder-sSflu6nB.d.mts → builder-DgkqDylK.d.mts} +5 -6
- package/dist/{client-BtIAtxrL.mjs → client-CILhdfsF.mjs} +104 -314
- package/dist/client-CILhdfsF.mjs.map +1 -0
- package/dist/client.d.mts +2 -2
- package/dist/client.mjs +2 -2
- package/dist/collections-DtwMc1NU.mjs +33 -0
- package/dist/collections-DtwMc1NU.mjs.map +1 -0
- package/dist/core.d.mts +9 -8
- package/dist/core.mjs +7 -7
- package/dist/{error-BW7re1UN.d.mts → error-BD_0QFKT.d.mts} +2 -2
- package/dist/{error-CR8Urc84.d.mts → error-BquXDmIN.d.mts} +4 -4
- package/dist/{error-DrEIM7FO.mjs → error-L_QdmSnV.mjs} +2 -2
- package/dist/{error-DrEIM7FO.mjs.map → error-L_QdmSnV.mjs.map} +1 -1
- package/dist/{error-B__OMBRY.mjs → error-gtJvV6YS.mjs} +2 -2
- package/dist/{error-B__OMBRY.mjs.map → error-gtJvV6YS.mjs.map} +1 -1
- package/dist/{event-fU3Y01PR.d.mts → event-DJeb_DIL.d.mts} +4 -2
- package/dist/{event-D8Nfoh4K.mjs → event-DinQMmJ1.mjs} +16 -10
- package/dist/event-DinQMmJ1.mjs.map +1 -0
- package/dist/{filter-C0IMByE3.d.mts → filter-jI_9htF7.d.mts} +2 -2
- package/dist/{gossip-DpIvkBuj.d.mts → gossip-C7HLW5kI.d.mts} +35 -26
- package/dist/{gossip-oloDMWEj.mjs → gossip-CcTp9VpU.mjs} +23 -33
- package/dist/gossip-CcTp9VpU.mjs.map +1 -0
- package/dist/gossip.d.mts +1 -1
- package/dist/gossip.mjs +1 -1
- package/dist/{index-CvkvqB16.d.mts → index-DU9tRFTZ.d.mts} +7 -20
- package/dist/{index-q4MDce_r.d.mts → index-DiXwNn2M.d.mts} +6 -8
- package/dist/{index-QxvrpMfF.d.mts → index-DocgD9K0.d.mts} +92 -167
- package/dist/{index-D1bqFHKB.d.mts → index-InQZpHyQ.d.mts} +18 -18
- package/dist/{index-MxC28S4P.d.mts → index-Tx15HGAD.d.mts} +26 -28
- package/dist/{index-Dmc8FJgO.d.mts → index-iLT8UzY3.d.mts} +19 -37
- package/dist/index.d.mts +23 -21
- package/dist/index.mjs +21 -19
- package/dist/instance-BcKzxHIP.mjs +220 -0
- package/dist/instance-BcKzxHIP.mjs.map +1 -0
- package/dist/instance-DjsL8h84.d.mts +15 -0
- package/dist/{key-Cv4_pN3x.d.mts → key-D_5OgGT7.d.mts} +11 -7
- package/dist/{key-Bps-LqX7.mjs → key-bshqfmZL.mjs} +13 -9
- package/dist/key-bshqfmZL.mjs.map +1 -0
- package/dist/{loaders-o6YXB_Ri.mjs → loaders-DD0XAGzw.mjs} +73 -82
- package/dist/loaders-DD0XAGzw.mjs.map +1 -0
- package/dist/loaders.d.mts +3 -3
- package/dist/loaders.mjs +2 -2
- package/dist/{memory-C2EVx0_O.mjs → memory-D_-5-T31.mjs} +2 -2
- package/dist/memory-D_-5-T31.mjs.map +1 -0
- package/dist/{memory-index-Do1mz0WF.mjs → memory-index-C6rujtoc.mjs} +30 -247
- package/dist/memory-index-C6rujtoc.mjs.map +1 -0
- package/dist/message-CWLcFTQ1.mjs +194 -0
- package/dist/message-CWLcFTQ1.mjs.map +1 -0
- package/dist/{message-iGPIqnn0.d.mts → message-CbBZOhq3.d.mts} +3 -3
- package/dist/{nip17-CKIufV98.d.mts → nip17-cTDFfFVt.d.mts} +5 -6
- package/dist/{nip19-BevCJZsp.d.mts → nip19-D1OnJ192.d.mts} +3 -9
- package/dist/{nip46-DDV4dC56.d.mts → nip46-DrsPI8EX.d.mts} +3 -5
- package/dist/nip51-DoPUvQ--.d.mts +87 -0
- package/dist/{nip59-zQgdLcEr.d.mts → nip59-BFIihLYY.d.mts} +3 -4
- package/dist/nip65-52HiwiyD.d.mts +23 -0
- package/dist/{nip77-DH4RhS5r.d.mts → nip77-C9QcZn7b.d.mts} +3 -4
- package/dist/nips/blossom.d.mts +12 -8
- package/dist/nips/blossom.mjs +30 -9
- package/dist/nips/blossom.mjs.map +1 -1
- package/dist/nips/nip04.mjs +17 -7
- package/dist/nips/nip04.mjs.map +1 -1
- package/dist/nips/nip05.d.mts +2 -3
- package/dist/nips/nip05.mjs +1 -4
- package/dist/nips/nip05.mjs.map +1 -1
- package/dist/nips/nip10.d.mts +3 -3
- package/dist/nips/nip10.mjs +3 -3
- package/dist/nips/nip11.d.mts +1 -1
- package/dist/nips/nip11.mjs +4 -4
- package/dist/nips/nip11.mjs.map +1 -1
- package/dist/nips/nip13.d.mts +7 -7
- package/dist/nips/nip13.mjs +32 -10
- package/dist/nips/nip13.mjs.map +1 -1
- package/dist/nips/nip17.d.mts +1 -1
- package/dist/nips/nip17.mjs +13 -33
- package/dist/nips/nip17.mjs.map +1 -1
- package/dist/nips/nip19.d.mts +2 -2
- package/dist/nips/nip19.mjs +16 -28
- package/dist/nips/nip19.mjs.map +1 -1
- package/dist/nips/nip21.d.mts +6 -1
- package/dist/nips/nip21.mjs +15 -5
- package/dist/nips/nip21.mjs.map +1 -1
- package/dist/nips/nip27.d.mts +4 -4
- package/dist/nips/nip27.mjs.map +1 -1
- package/dist/nips/nip42.d.mts +1 -1
- package/dist/nips/nip42.mjs +2 -1
- package/dist/nips/nip42.mjs.map +1 -1
- package/dist/nips/nip44.mjs +1 -1
- package/dist/nips/nip46.d.mts +2 -2
- package/dist/nips/nip46.mjs +15 -19
- package/dist/nips/nip46.mjs.map +1 -1
- package/dist/nips/nip49.d.mts +5 -5
- package/dist/nips/nip49.mjs +4 -4
- package/dist/nips/nip49.mjs.map +1 -1
- package/dist/nips/nip51.d.mts +2 -86
- package/dist/nips/nip51.mjs +7 -23
- package/dist/nips/nip51.mjs.map +1 -1
- package/dist/nips/nip57.d.mts +7 -5
- package/dist/nips/nip57.mjs +12 -17
- package/dist/nips/nip57.mjs.map +1 -1
- package/dist/nips/nip59.d.mts +1 -1
- package/dist/nips/nip59.mjs +16 -16
- package/dist/nips/nip59.mjs.map +1 -1
- package/dist/nips/nip65.d.mts +2 -2
- package/dist/nips/nip65.mjs +14 -37
- package/dist/nips/nip65.mjs.map +1 -1
- package/dist/nips/nip77.d.mts +1 -1
- package/dist/nips/nip77.mjs +33 -32
- package/dist/nips/nip77.mjs.map +1 -1
- package/dist/nips/nip98.d.mts +5 -5
- package/dist/nips/nip98.mjs +8 -8
- package/dist/nips/nip98.mjs.map +1 -1
- package/dist/nostr_crypto_wasm_bg.wasm +0 -0
- package/dist/{pool-DBIYMtwy.d.mts → pool-SzJKPIa3.d.mts} +96 -91
- package/dist/pool-YHRZgcQ3.mjs +596 -0
- package/dist/pool-YHRZgcQ3.mjs.map +1 -0
- package/dist/put-DS0yzVJx.mjs +187 -0
- package/dist/put-DS0yzVJx.mjs.map +1 -0
- package/dist/{reactive-DFpqtIf_.d.mts → reactive-BJ1ggn4d.d.mts} +9 -22
- package/dist/{reactive-DDlThDSZ.mjs → reactive-BJRoVHxU.mjs} +113 -64
- package/dist/reactive-BJRoVHxU.mjs.map +1 -0
- package/dist/{pool-As20OGxB.mjs → relay-CvxPiQ_m.mjs} +178 -721
- package/dist/relay-CvxPiQ_m.mjs.map +1 -0
- package/dist/relay.d.mts +4 -4
- package/dist/relay.mjs +3 -2
- package/dist/serial-Bzpw-xz3.mjs +21 -0
- package/dist/serial-Bzpw-xz3.mjs.map +1 -0
- package/dist/{signer-CYQutDzD.mjs → signer-yW3slDC2.mjs} +12 -14
- package/dist/signer-yW3slDC2.mjs.map +1 -0
- package/dist/signer.d.mts +2 -2
- package/dist/signer.mjs +2 -2
- package/dist/{storage-Dky3wuoH.mjs → storage-CZ52ifcQ.mjs} +17 -42
- package/dist/storage-CZ52ifcQ.mjs.map +1 -0
- package/dist/storage.d.mts +3 -3
- package/dist/storage.mjs +4 -4
- package/dist/store.d.mts +1 -1
- package/dist/store.mjs +1 -1
- package/dist/testing.d.mts +18 -18
- package/dist/testing.mjs +9 -9
- package/dist/testing.mjs.map +1 -1
- package/dist/{types-2pCUprRU.d.mts → types-Cm-XMgtH.d.mts} +6 -6
- package/dist/{types-CRQEk0Oi.d.mts → types-DNmiscfd.d.mts} +2 -2
- package/dist/{util-LfQirgDk.mjs → util-yFYzlbrm.mjs} +29 -7
- package/dist/util-yFYzlbrm.mjs.map +1 -0
- package/dist/{verifier-t5WoWbtY.mjs → verifier-BTLOpo1n.mjs} +3 -3
- package/dist/verifier-BTLOpo1n.mjs.map +1 -0
- package/dist/verifier-CgTFpgsC.d.mts +22 -0
- package/dist/wasm.browser.d.mts +8 -0
- package/dist/wasm.browser.mjs +19 -0
- package/dist/wasm.browser.mjs.map +1 -0
- package/dist/wasm.d.mts +3 -13
- package/dist/wasm.mjs +7 -208
- package/dist/wasm.mjs.map +1 -1
- package/package.json +13 -10
- package/dist/abort-Ba8tDrOI.mjs.map +0 -1
- package/dist/builder-9tjI6WQ3.mjs.map +0 -1
- package/dist/client-BtIAtxrL.mjs.map +0 -1
- package/dist/event-D8Nfoh4K.mjs.map +0 -1
- package/dist/gossip-oloDMWEj.mjs.map +0 -1
- package/dist/key-Bps-LqX7.mjs.map +0 -1
- package/dist/loaders-o6YXB_Ri.mjs.map +0 -1
- package/dist/memory-C2EVx0_O.mjs.map +0 -1
- package/dist/memory-index-Do1mz0WF.mjs.map +0 -1
- package/dist/message-DcY6Fi9o.mjs +0 -193
- package/dist/message-DcY6Fi9o.mjs.map +0 -1
- package/dist/nip65-CjgmdpPB.d.mts +0 -26
- package/dist/nips/nip96.d.mts +0 -40
- package/dist/nips/nip96.mjs +0 -118
- package/dist/nips/nip96.mjs.map +0 -1
- package/dist/pool-As20OGxB.mjs.map +0 -1
- package/dist/reactive-DDlThDSZ.mjs.map +0 -1
- package/dist/signer-CYQutDzD.mjs.map +0 -1
- package/dist/storage-Dky3wuoH.mjs.map +0 -1
- package/dist/util-LfQirgDk.mjs.map +0 -1
- package/dist/verifier-t5WoWbtY.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"signer-CYQutDzD.mjs","names":["nip04.encrypt","nip04.decrypt","nip44.getConversationKey","nip44.encrypt","nip44.decrypt","decrypt","bytesToHex","encrypt"],"sources":["../src/signer/keys.ts","../src/signer/nip07.ts","../src/signer/nip46.ts"],"sourcesContent":["import type { Event, UnsignedEvent } from \"../core/event.ts\";\nimport type { SecretKey } from \"../core/key.ts\";\nimport { Keys, signEvent } from \"../core/key.ts\";\nimport * as nip04 from \"../nips/nip04.ts\";\nimport * as nip44 from \"../nips/nip44.ts\";\nimport type { NostrSigner } from \"./types.ts\";\n\n/** Local secret-key signer with NIP-04 (legacy) and NIP-44 support. */\nexport class KeysSigner implements NostrSigner {\n readonly #keys: Keys;\n readonly #convKeys = new Map<string, Uint8Array>();\n\n constructor(secretKey: SecretKey | Uint8Array | string | Keys) {\n this.#keys = secretKey instanceof Keys ? secretKey : Keys.fromSecretKey(secretKey);\n }\n\n get keys(): Keys {\n return this.#keys;\n }\n\n // oxlint-disable-next-line typescript/require-await -- NostrSigner is async; KeysSigner signs synchronously\n async getPublicKey(): Promise<string> {\n return this.#keys.publicKey;\n }\n\n // oxlint-disable-next-line typescript/require-await -- NostrSigner is async; KeysSigner signs synchronously\n async signEvent(unsigned: UnsignedEvent): Promise<Event> {\n return signEvent(unsigned, this.#keys);\n }\n\n // oxlint-disable-next-line typescript/require-await -- NostrSigner is async; KeysSigner signs synchronously\n async nip04Encrypt(peer: string, plaintext: string): Promise<string> {\n return nip04.encrypt(this.#keys.secretKey.bytes, peer, plaintext);\n }\n\n // oxlint-disable-next-line typescript/require-await -- NostrSigner is async; KeysSigner signs synchronously\n async nip04Decrypt(peer: string, ciphertext: string): Promise<string> {\n return nip04.decrypt(this.#keys.secretKey.bytes, peer, ciphertext);\n }\n\n #conversationKey(peer: string): Uint8Array {\n const pk = peer.toLowerCase();\n let key = this.#convKeys.get(pk);\n if (!key) {\n key = nip44.getConversationKey(this.#keys.secretKey.bytes, pk);\n this.#convKeys.set(pk, key);\n }\n return key;\n }\n\n // oxlint-disable-next-line typescript/require-await -- NostrSigner is async; KeysSigner signs synchronously\n async nip44Encrypt(peer: string, plaintext: string): Promise<string> {\n return nip44.encrypt(plaintext, this.#conversationKey(peer));\n }\n\n // oxlint-disable-next-line typescript/require-await -- NostrSigner is async; KeysSigner signs synchronously\n async nip44Decrypt(peer: string, payload: string): Promise<string> {\n return nip44.decrypt(payload, this.#conversationKey(peer));\n }\n}\n","import { CryptoError } from \"../core/error.ts\";\nimport type { Event, EventTemplate, UnsignedEvent } from \"../core/event.ts\";\nimport { signedMatchesUnsigned, validateSignedEvent } from \"../core/event.ts\";\nimport { assertHex32 } from \"../core/util.ts\";\nimport { verifyEvent } from \"../core/verifier.ts\";\nimport type { NostrSigner } from \"./types.ts\";\n\n/**\n * Minimal NIP-07 provider surface (browser extension `window.nostr`).\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/07.md\n */\nexport type WindowNostr = {\n getPublicKey: () => Promise<string>;\n signEvent: (event: EventTemplate) => Promise<Event>;\n nip04?: {\n encrypt: (pubkey: string, plaintext: string) => Promise<string>;\n decrypt: (pubkey: string, ciphertext: string) => Promise<string>;\n };\n nip44?: {\n encrypt: (pubkey: string, plaintext: string) => Promise<string>;\n decrypt: (pubkey: string, ciphertext: string) => Promise<string>;\n };\n};\n\ntype GlobalWithNostr = typeof globalThis & {\n nostr?: WindowNostr;\n window?: { nostr?: WindowNostr };\n};\n\n/** Resolve `window.nostr` / `globalThis.nostr` when present. */\nexport function getWindowNostr(): WindowNostr | undefined {\n const g = globalThis as GlobalWithNostr;\n return g.nostr ?? g.window?.nostr;\n}\n\n/** True when a NIP-07 provider is injected in the current environment. */\nexport function isNip07Available(): boolean {\n const n = getWindowNostr();\n return Boolean(n && typeof n.getPublicKey === \"function\" && typeof n.signEvent === \"function\");\n}\n\n/**\n * Signer backed by a NIP-07 browser extension (or any injected provider). Never holds secret keys\n * in-process.\n */\nexport class Nip07Signer implements NostrSigner {\n readonly #provider: WindowNostr | undefined;\n\n constructor(provider?: WindowNostr) {\n this.#provider = provider;\n }\n\n #resolve(): WindowNostr {\n const provider = this.#provider ?? getWindowNostr();\n if (!provider) {\n throw new CryptoError(\"NIP-07 provider not available (window.nostr missing)\");\n }\n return provider;\n }\n\n async getPublicKey(): Promise<string> {\n const pk = await this.#resolve().getPublicKey();\n try {\n return assertHex32(pk, \"pubkey\");\n } catch (error) {\n throw new CryptoError(\"NIP-07 getPublicKey returned an invalid pubkey\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n }\n\n async signEvent(unsigned: UnsignedEvent): Promise<Event> {\n const provider = this.#resolve();\n const template: EventTemplate = {\n kind: unsigned.kind,\n tags: unsigned.tags.map((t) => {\n const [name, ...rest] = t;\n if (name === undefined) {\n throw new CryptoError(\"unsigned event has an empty tag\");\n }\n return [name, ...rest];\n }),\n content: unsigned.content,\n created_at: unsigned.created_at,\n };\n\n const signed = await provider.signEvent(template);\n if (!validateSignedEvent(signed)) {\n throw new CryptoError(\"NIP-07 signEvent returned an invalid event\");\n }\n if (!verifyEvent(signed)) {\n throw new CryptoError(\"NIP-07 signed event failed signature verification\");\n }\n if (!signedMatchesUnsigned(signed, unsigned)) {\n throw new CryptoError(\"NIP-07 signed event does not match unsigned template\");\n }\n return signed;\n }\n\n async nip04Encrypt(peer: string, plaintext: string): Promise<string> {\n const { nip04 } = this.#resolve();\n if (!nip04?.encrypt) {\n throw new CryptoError(\"NIP-07 provider does not support nip04.encrypt\");\n }\n return nip04.encrypt(peer, plaintext);\n }\n\n async nip04Decrypt(peer: string, ciphertext: string): Promise<string> {\n const { nip04 } = this.#resolve();\n if (!nip04?.decrypt) {\n throw new CryptoError(\"NIP-07 provider does not support nip04.decrypt\");\n }\n return nip04.decrypt(peer, ciphertext);\n }\n\n async nip44Encrypt(peer: string, plaintext: string): Promise<string> {\n const { nip44 } = this.#resolve();\n if (!nip44?.encrypt) {\n throw new CryptoError(\"NIP-07 provider does not support nip44.encrypt\");\n }\n return nip44.encrypt(peer, plaintext);\n }\n\n async nip44Decrypt(peer: string, payload: string): Promise<string> {\n const { nip44 } = this.#resolve();\n if (!nip44?.decrypt) {\n throw new CryptoError(\"NIP-07 provider does not support nip44.decrypt\");\n }\n return nip44.decrypt(peer, payload);\n }\n}\n","import { randomBytes } from \"@noble/hashes/utils.js\";\n\nimport type { Event, EventTemplate, UnsignedEvent } from \"../core/event.ts\";\nimport { signedMatchesUnsigned, validateSignedEvent } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { SecretKey, finalizeEvent, getPublicKey } from \"../core/key.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { invokeSafely } from \"../core/report.ts\";\nimport { Tag } from \"../core/tag.ts\";\nimport { bytesToHex, isHex32 } from \"../core/util.ts\";\nimport { verifyEvent } from \"../core/verifier.ts\";\nimport { decrypt, encrypt, getConversationKey } from \"../nips/nip44.ts\";\nimport {\n Nip46Error,\n decodeNip46Response,\n encodeNip46Request,\n parseBunkerURL,\n parseNostrConnectURI,\n} from \"../nips/nip46.ts\";\nimport type { BunkerPointer, ClientMetadata, Nip46Request } from \"../nips/nip46.ts\";\nimport type { NostrSigner } from \"./types.ts\";\n\n/** Subscribe options used by NIP-46 transport (structural subset of Pool). */\nexport type Nip46SubscribeOptions = {\n signal?: AbortSignal | undefined;\n onevent?: ((event: Event) => void) | undefined;\n onclose?: ((reason: string) => void) | undefined;\n};\n\n/**\n * Structural relay transport for NIP-46 RPC. Satisfied by {@link import(\"../relay/pool.ts\").Pool};\n * constructed above the signer layer so `signer` never imports `relay` (ADR-0001).\n */\nexport type Nip46Transport = {\n subscribe: (\n relays: string[],\n filters: Filter[],\n opts?: Nip46SubscribeOptions,\n ) => { close: (reason?: string) => void };\n publish: (\n relays: string[],\n event: Event,\n ) => Promise<ReadonlyArray<{ result?: { ok: boolean; message: string }; error?: string }>>;\n close: (urls?: string[]) => void;\n};\n\n/** Options for {@link Nip46Signer}: transport, timeouts, and relay hints. */\nexport type Nip46SignerOptions = {\n /** Shared transport. When set, the signer does not close it on {@link Nip46Signer.close}. */\n pool?: Nip46Transport;\n /**\n * Factory for a private transport when `pool` is omitted. Typical: `() => new Pool({\n * websocketImplementation, enableReconnect: true })`.\n */\n createPool?: () => Nip46Transport;\n /**\n * Relays used when the bunker pointer has none, and merged uniquely onto a nonempty pointer\n * (pointer first).\n */\n relays?: string[];\n /** Bunker connection secret when the pointer or bunker:// URL has none. */\n secret?: string | undefined;\n /** Local client key used to encrypt RPC (not the remote user key). */\n clientSecretKey?: SecretKey | Uint8Array | string;\n /** Called when bunker returns `auth_url` for a pending request. */\n onAuthUrl?: (url: string) => void;\n /** Per-request timeout in ms. Default 30s. */\n timeoutMs?: number;\n /**\n * Timeout in ms applied after the bunker replies `auth_url`: the request keeps waiting for the\n * real response until this elapses. Default 300s.\n */\n authTimeoutMs?: number;\n /** Requested permissions sent with `connect` (`method[:kind]` list). */\n perms?: string[];\n /** Client metadata sent with bunker-initiated `connect`. */\n metadata?: ClientMetadata;\n};\n\nfunction resolveClientSecret(key?: SecretKey | Uint8Array | string): SecretKey {\n if (key === undefined) {\n return SecretKey.generate();\n }\n if (key instanceof SecretKey) {\n return key;\n }\n if (typeof key === \"string\") {\n return SecretKey.fromHex(key);\n }\n return SecretKey.fromBytes(key);\n}\n\nfunction resolveTransport(opts: Nip46SignerOptions): { pool: Nip46Transport; ownsPool: boolean } {\n if (opts.pool) {\n return { pool: opts.pool, ownsPool: false };\n }\n if (opts.createPool) {\n return { pool: opts.createPool(), ownsPool: true };\n }\n throw new Nip46Error(\n \"Nip46Signer requires pool or createPool (inject Pool from @qntx/nostr/relay)\",\n );\n}\n\nfunction applyPointerOpts(pointer: BunkerPointer, opts: Nip46SignerOptions): BunkerPointer {\n let next: BunkerPointer = {\n pubkey: pointer.pubkey.toLowerCase(),\n relays: [...pointer.relays],\n secret: pointer.secret,\n };\n if (opts.secret !== undefined && next.secret === undefined) {\n next = { ...next, secret: opts.secret };\n }\n\n const relays = next.relays.length > 0 ? next.relays : [...(opts.relays ?? [])];\n if (relays.length === 0) {\n throw new Nip46Error(\"no relays for bunker connection\");\n }\n if (next.relays.length > 0 && opts.relays !== undefined && opts.relays.length > 0) {\n const merged = [...next.relays];\n for (const r of opts.relays) {\n if (!merged.includes(r)) {\n merged.push(r);\n }\n }\n return { ...next, relays: merged };\n }\n return { ...next, relays };\n}\n\nfunction resolveBunkerPointer(input: string | BunkerPointer): BunkerPointer {\n if (typeof input !== \"string\") {\n return {\n pubkey: input.pubkey.toLowerCase(),\n relays: [...input.relays],\n secret: input.secret,\n };\n }\n\n const bunker = parseBunkerURL(input);\n if (bunker) {\n return bunker;\n }\n\n throw new Nip46Error(\"invalid bunker input (expected bunker:// URL or BunkerPointer)\");\n}\n\ntype PendingRequest = {\n resolve: (v: string) => void;\n reject: (e: Error) => void;\n method: string;\n timer?: ReturnType<typeof setTimeout>;\n};\n\n/**\n * NIP-46 remote signer (bunker / nostrconnect). Implements {@link NostrSigner}; never holds the\n * remote user's secret key. Network I/O is injected via {@link Nip46Transport} (no relay import).\n */\nexport class Nip46Signer implements NostrSigner {\n readonly #pool: Nip46Transport;\n readonly #ownsPool: boolean;\n readonly #clientSecret: SecretKey;\n readonly #clientPubkey: string;\n readonly #pointer: BunkerPointer;\n readonly #conversationKey: Uint8Array;\n readonly #onAuthUrl: ((url: string) => void) | undefined;\n readonly #timeoutMs: number;\n readonly #authTimeoutMs: number;\n readonly #perms: string[] | undefined;\n readonly #metadata: ClientMetadata | undefined;\n #relays: string[];\n readonly #listeners = new Map<string, PendingRequest>();\n #sub: { close: (reason?: string) => void } | undefined;\n #open = false;\n #cachedRemotePubkey: string | undefined;\n\n private constructor(\n clientSecret: SecretKey,\n pointer: BunkerPointer,\n pool: Nip46Transport,\n ownsPool: boolean,\n opts: Nip46SignerOptions,\n ) {\n if (pointer.relays.length === 0) {\n throw new Nip46Error(\"bunker pointer has no relays\");\n }\n this.#clientSecret = clientSecret;\n this.#clientPubkey = getPublicKey(clientSecret);\n this.#pointer = {\n pubkey: pointer.pubkey.toLowerCase(),\n relays: [...pointer.relays],\n secret: pointer.secret,\n };\n this.#relays = [...pointer.relays];\n this.#pool = pool;\n this.#ownsPool = ownsPool;\n this.#conversationKey = getConversationKey(clientSecret.bytes, this.#pointer.pubkey);\n this.#onAuthUrl = opts.onAuthUrl;\n this.#timeoutMs = opts.timeoutMs ?? 30_000;\n this.#authTimeoutMs = opts.authTimeoutMs ?? 300_000;\n this.#perms = opts.perms;\n this.#metadata = opts.metadata;\n }\n\n get bunker(): Readonly<BunkerPointer> {\n return {\n pubkey: this.#pointer.pubkey,\n relays: [...this.#relays],\n secret: this.#pointer.secret,\n };\n }\n\n get clientPublicKey(): string {\n return this.#clientPubkey;\n }\n\n /**\n * Subscribe to an already-known bunker pointer. Does not send the `connect` RPC (reconnect\n * without a new handshake). Requires `clientSecretKey` — reconnect must reuse the original client\n * identity.\n */\n static fromBunker(\n pointer: BunkerPointer,\n opts: Nip46SignerOptions & { clientSecretKey: SecretKey | Uint8Array | string },\n ): Nip46Signer {\n if (opts.clientSecretKey === undefined) {\n throw new Nip46Error(\"fromBunker requires clientSecretKey\");\n }\n const resolved = applyPointerOpts(pointer, opts);\n const sk = resolveClientSecret(opts.clientSecretKey);\n const { pool, ownsPool } = resolveTransport(opts);\n const signer = new Nip46Signer(sk, resolved, pool, ownsPool, opts);\n signer.#startSubscription();\n return signer;\n }\n\n /**\n * Connect using a `bunker://` URL or pre-parsed pointer. Invalid strings throw. A NIP-05\n * identifier is not a bunker pointer. When both pointer and `opts.relays` are nonempty, unique\n * `opts.relays` are appended.\n */\n static async connect(\n input: string | BunkerPointer,\n opts: Nip46SignerOptions = {},\n ): Promise<Nip46Signer> {\n const pointer = resolveBunkerPointer(input);\n const signer = Nip46Signer.fromBunker(pointer, {\n ...opts,\n clientSecretKey: resolveClientSecret(opts.clientSecretKey),\n });\n try {\n await signer.connectRemote();\n try {\n await signer.switchRelays();\n } catch {\n // Bunker may not implement switch_relays.\n }\n await signer.getPublicKey();\n return signer;\n } catch (error) {\n await signer.close();\n throw error;\n }\n }\n\n /**\n * Wait for a bunker to complete a client-initiated `nostrconnect://` handshake. Requires\n * `clientSecretKey` whose pubkey matches the URI client pubkey (build the URI with\n * {@link createNostrConnectURI} after generating keys).\n */\n static async fromNostrConnectURI(\n connectionURI: string,\n opts: Nip46SignerOptions & {\n clientSecretKey: SecretKey | Uint8Array | string;\n signal?: AbortSignal;\n handshakeTimeoutMs?: number;\n },\n ): Promise<Nip46Signer> {\n const params = parseNostrConnectURI(connectionURI);\n const sk = resolveClientSecret(opts.clientSecretKey);\n const clientPubkey = getPublicKey(sk);\n\n if (clientPubkey !== params.clientPubkey) {\n throw new Nip46Error(\"client secret key does not match nostrconnect client pubkey\");\n }\n\n const { pool, ownsPool } = resolveTransport(opts);\n const handshakeTimeoutMs = opts.handshakeTimeoutMs ?? 300_000;\n\n return new Promise((resolve, reject) => {\n let settled = false;\n const finish = (err?: Error, signer?: Nip46Signer) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n sub.close(err ? \"failed\" : \"connected\");\n if (err) {\n if (ownsPool) {\n pool.close();\n }\n reject(err);\n } else if (signer) {\n resolve(signer);\n }\n };\n\n const timer = setTimeout(() => {\n finish(new Nip46Error(\"nostrconnect handshake timed out\"));\n }, handshakeTimeoutMs);\n\n const sub = pool.subscribe(\n params.relays,\n [{ kinds: [Kind.NostrConnect], \"#p\": [clientPubkey], limit: 0 }],\n {\n signal: opts.signal,\n onevent: (event) => {\n try {\n const convKey = getConversationKey(sk.bytes, event.pubkey);\n const payload = decrypt(event.content, convKey);\n const response = decodeNip46Response(payload);\n if (response.result !== params.secret) {\n return;\n }\n\n const pointer: BunkerPointer = {\n pubkey: event.pubkey.toLowerCase(),\n relays: params.relays,\n secret: params.secret,\n };\n const signer = new Nip46Signer(sk, pointer, pool, ownsPool, opts);\n signer.#startSubscription();\n void (async () => {\n try {\n await signer.switchRelays();\n } catch {\n // Relay switch at connect time is best-effort.\n }\n try {\n await signer.getPublicKey();\n finish(undefined, signer);\n } catch (error: unknown) {\n finish(error instanceof Error ? error : new Nip46Error(String(error)));\n }\n })();\n } catch {\n // ignore non-matching events\n }\n },\n onclose: (reason) => {\n if (reason !== \"connected\" && reason !== \"failed\") {\n finish(new Nip46Error(`nostrconnect subscription closed: ${reason}`));\n }\n },\n },\n );\n });\n }\n\n #startSubscription(): void {\n this.#sub?.close();\n this.#sub = this.#pool.subscribe(\n this.#relays,\n [\n {\n kinds: [Kind.NostrConnect],\n authors: [this.#pointer.pubkey],\n \"#p\": [this.#clientPubkey],\n limit: 0,\n },\n ],\n {\n onevent: (event) => {\n try {\n const payload = decrypt(event.content, this.#conversationKey);\n const { id, result, error } = decodeNip46Response(payload);\n\n if (result === \"auth_url\" && this.#listeners.has(id)) {\n const listener = this.#listeners.get(id);\n if (!listener) {\n return;\n }\n if (error !== undefined && error !== \"\") {\n invokeSafely(() => this.#onAuthUrl?.(error));\n }\n clearTimeout(listener.timer);\n listener.timer = this.#requestTimer(id, listener, this.#authTimeoutMs);\n return;\n }\n\n const listener = this.#dropRequest(id);\n if (!listener) {\n return;\n }\n if (error !== undefined && error !== \"\") {\n listener.reject(new Nip46Error(error));\n } else if (result === undefined) {\n listener.reject(new Nip46Error(\"empty NIP-46 response\"));\n } else {\n listener.resolve(result);\n }\n } catch {\n // ignore decrypt failures from unrelated events\n }\n },\n onclose: () => {\n this.#sub = undefined;\n },\n },\n );\n this.#open = true;\n }\n\n async connectRemote(overrides?: { metadata?: ClientMetadata; perms?: string[] }): Promise<void> {\n const metadata = overrides?.metadata ?? this.#metadata;\n const perms = overrides?.perms ?? this.#perms;\n const params = [this.#pointer.pubkey, this.#pointer.secret ?? \"\"];\n const hasPerms = perms !== undefined && perms.length > 0;\n if (hasPerms || metadata !== undefined) {\n params.push(hasPerms ? perms.join(\",\") : \"\");\n }\n if (metadata !== undefined) {\n params.push(JSON.stringify(metadata));\n }\n const result = await this.#sendRequest(\"connect\", params);\n if (result === \"ack\") {\n return;\n }\n const { secret } = this.#pointer;\n if (secret !== undefined && secret !== \"\" && result === secret) {\n return;\n }\n throw new Nip46Error(`connect result is not ack or secret: ${result}`);\n }\n\n /**\n * Ask the bunker for its preferred relay list and resubscribe when it changes. Spec result is a\n * JSON array of URLs, or `null` when nothing changes.\n */\n async switchRelays(): Promise<string[] | undefined> {\n const resp = await this.#sendRequest(\"switch_relays\", []);\n if (resp === \"null\") {\n return undefined;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(resp);\n } catch (error) {\n throw new Nip46Error(\"invalid switch_relays JSON\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n if (\n !Array.isArray(parsed) ||\n parsed.length === 0 ||\n !parsed.every((x) => typeof x === \"string\")\n ) {\n throw new Nip46Error(\"invalid switch_relays result\");\n }\n this.#relays = parsed;\n this.#startSubscription();\n return parsed;\n }\n\n /** Courtesy session teardown. Always closes the local signer afterwards. */\n async logout(): Promise<void> {\n try {\n const resp = await this.#sendRequest(\"logout\", []);\n if (resp !== \"ack\") {\n throw new Nip46Error(`logout result is not ack: ${resp}`);\n }\n } finally {\n await this.close();\n }\n }\n\n async ping(): Promise<void> {\n const resp = await this.#sendRequest(\"ping\", []);\n if (resp !== \"pong\") {\n throw new Nip46Error(`ping result is not pong: ${resp}`);\n }\n }\n\n async getPublicKey(): Promise<string> {\n if (this.#cachedRemotePubkey === undefined) {\n const pk = await this.#sendRequest(\"get_public_key\", []);\n if (!isHex32(pk.toLowerCase())) {\n throw new Nip46Error(\"bunker returned invalid pubkey\");\n }\n this.#cachedRemotePubkey = pk.toLowerCase();\n }\n return this.#cachedRemotePubkey;\n }\n\n async signEvent(unsigned: UnsignedEvent): Promise<Event> {\n const template: EventTemplate = {\n kind: unsigned.kind,\n tags: unsigned.tags,\n content: unsigned.content,\n created_at: unsigned.created_at,\n };\n const resp = await this.#sendRequest(\"sign_event\", [JSON.stringify(template)]);\n let signed: unknown;\n try {\n signed = JSON.parse(resp);\n } catch (error) {\n throw new Nip46Error(\"bunker returned invalid sign_event JSON\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n if (!validateSignedEvent(signed) || !verifyEvent(signed)) {\n throw new Nip46Error(\"bunker returned improperly signed event\");\n }\n if (!signedMatchesUnsigned(signed, unsigned)) {\n throw new Nip46Error(\"signed event does not match unsigned template\");\n }\n return signed;\n }\n\n async nip04Encrypt(peer: string, plaintext: string): Promise<string> {\n return this.#sendRequest(\"nip04_encrypt\", [peer, plaintext]);\n }\n\n async nip04Decrypt(peer: string, ciphertext: string): Promise<string> {\n return this.#sendRequest(\"nip04_decrypt\", [peer, ciphertext]);\n }\n\n async nip44Encrypt(peer: string, plaintext: string): Promise<string> {\n return this.#sendRequest(\"nip44_encrypt\", [peer, plaintext]);\n }\n\n async nip44Decrypt(peer: string, payload: string): Promise<string> {\n return this.#sendRequest(\"nip44_decrypt\", [peer, payload]);\n }\n\n // oxlint-disable-next-line typescript/require-await -- NostrSigner is async; teardown is synchronous\n async close(): Promise<void> {\n this.#open = false;\n for (const [, listener] of this.#listeners) {\n clearTimeout(listener.timer);\n listener.reject(new Nip46Error(\"signer closed\"));\n }\n this.#listeners.clear();\n this.#sub?.close(\"signer closed\");\n this.#sub = undefined;\n if (this.#ownsPool) {\n this.#pool.close();\n }\n }\n\n async #sendRequest(method: string, params: string[]): Promise<string> {\n if (!this.#open) {\n throw new Nip46Error(\"signer is closed\");\n }\n if (!this.#sub) {\n this.#startSubscription();\n }\n\n const id = bytesToHex(randomBytes(16));\n const req: Nip46Request = { id, method, params };\n const encrypted = encrypt(encodeNip46Request(req), this.#conversationKey);\n\n const event = finalizeEvent(\n {\n kind: Kind.NostrConnect,\n tags: [Tag.p(this.#pointer.pubkey)],\n content: encrypted,\n created_at: Math.floor(Date.now() / 1000),\n },\n this.#clientSecret,\n );\n\n const resultPromise = new Promise<string>((resolve, reject) => {\n const listener: PendingRequest = { resolve, reject, method };\n listener.timer = this.#requestTimer(id, listener, this.#timeoutMs);\n this.#listeners.set(id, listener);\n });\n\n let replies;\n try {\n replies = await this.#pool.publish(this.#relays, event);\n } catch (error) {\n this.#dropRequest(id);\n throw error;\n }\n if (!replies.some((reply) => reply.result?.ok === true)) {\n const listener = this.#dropRequest(id);\n const detail = replies\n .map((reply) => reply.error ?? reply.result?.message)\n .filter((message): message is string => Boolean(message))\n .join(\"; \");\n listener?.reject(new Nip46Error(`request not accepted by any relay: ${detail}`));\n }\n return resultPromise;\n }\n\n #dropRequest(id: string): PendingRequest | undefined {\n const listener = this.#listeners.get(id);\n if (!listener) {\n return undefined;\n }\n clearTimeout(listener.timer);\n this.#listeners.delete(id);\n return listener;\n }\n\n #requestTimer(id: string, listener: PendingRequest, ms: number): ReturnType<typeof setTimeout> {\n return setTimeout(() => {\n this.#dropRequest(id);\n listener.reject(new Nip46Error(`NIP-46 request timed out: ${listener.method}`));\n }, ms);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"storage-Dky3wuoH.mjs","names":[],"sources":["../src/storage/idb-types.ts","../src/storage/idb-helpers.ts","../src/storage/idb-query.ts","../src/storage/idb-schema.ts","../src/storage/indexeddb.ts","../src/storage/sqlite.ts"],"sourcesContent":["// oxlint-disable typescript/no-restricted-types -- the *Like types mirror IndexedDB, whose request/transaction fields and event handlers are null-valued\nexport const EVENTS = \"events\";\nexport const TAG_REFS = \"tag_refs\";\nexport const ADDRESSES = \"addresses\";\nexport const TOMBSTONES = \"tombstones\";\nexport const OUTBOX_BOUNDS = \"outbox_bounds\";\nexport const WRITE_STORES: string[] = [EVENTS, TAG_REFS, ADDRESSES, TOMBSTONES];\n\nexport type IDBCursorDirectionLike = \"next\" | \"prev\";\n\nexport type IDBKeyRangeLike = {\n lower: unknown;\n upper: unknown;\n lowerOpen: boolean;\n upperOpen: boolean;\n};\n\nexport type IDBCursorLike = {\n value?: unknown;\n key: unknown;\n primaryKey: unknown;\n continue: () => void;\n};\n\nexport type IDBIndexLike = {\n openCursor: (range?: IDBKeyRangeLike, direction?: IDBCursorDirectionLike) => IDBRequestLike;\n openKeyCursor: (range?: IDBKeyRangeLike, direction?: IDBCursorDirectionLike) => IDBRequestLike;\n};\n\nexport type IDBRequestLike = {\n result: unknown;\n error: Error | null;\n onsuccess: ((ev: unknown) => void) | null;\n onerror: ((ev: unknown) => void) | null;\n};\n\nexport type IDBObjectStoreLike = {\n put: (value: unknown) => unknown;\n get: (key: string) => IDBRequestLike;\n delete: (key: string) => unknown;\n clear: () => unknown;\n getAll: () => IDBRequestLike;\n createIndex: (name: string, keyPath: string | string[]) => IDBIndexLike;\n index: (name: string) => IDBIndexLike;\n openCursor: (range?: IDBKeyRangeLike, direction?: IDBCursorDirectionLike) => IDBRequestLike;\n};\n\nexport type IDBTransactionLike = {\n objectStore: (name: string) => IDBObjectStoreLike;\n oncomplete: ((ev: unknown) => void) | null;\n onerror: ((ev: unknown) => void) | null;\n onabort: ((ev: unknown) => void) | null;\n error: Error | null;\n abort: () => void;\n};\n\nexport type IDBDatabaseLike = {\n objectStoreNames: { contains: (name: string) => boolean };\n createObjectStore: (name: string, options?: { keyPath?: string }) => IDBObjectStoreLike;\n transaction: (\n storeNames: string | string[],\n mode?: \"readonly\" | \"readwrite\",\n ) => IDBTransactionLike;\n onversionchange: ((ev: unknown) => void) | null;\n close: () => void;\n};\n\nexport type IDBVersionChangeEventLike = {\n oldVersion: number;\n newVersion: number | null;\n target: { result: IDBDatabaseLike; transaction: IDBTransactionLike };\n};\n\nexport type IDBFactoryLike = {\n open: (name: string, version?: number) => IDBOpenRequestLike;\n};\n\nexport type IDBOpenRequestLike = IDBRequestLike & {\n onupgradeneeded: ((ev: IDBVersionChangeEventLike) => void) | null;\n onblocked: ((ev: unknown) => void) | null;\n result: IDBDatabaseLike;\n};\n\nexport type TagRef = {\n key: string;\n name: string;\n value: string;\n id: string;\n created_at: number;\n};\n\nexport type AddressRow = {\n address: string;\n id: string;\n created_at: number;\n};\n\nexport type Tombstone =\n | { key: `id:${string}`; type: \"id\" }\n | { key: `pending:${string}`; type: \"pending\"; pubkey: string }\n | { key: `coord:${string}`; type: \"coord\"; until: number };\n\nexport type OutboxBoundRow = { key: string; oldest: number; newest: number };\n","// oxlint-disable unicorn/prefer-add-event-listener -- the *Like driver interfaces model only the `on*` handler surface\nimport type { Event } from \"../core/event.ts\";\nimport { isRecord } from \"../core/util.ts\";\nimport type { DeletionPlan, DeletionState } from \"./deletion.ts\";\nimport { StorageError } from \"./error.ts\";\nimport { ADDRESSES, EVENTS, TAG_REFS, TOMBSTONES } from \"./idb-types.ts\";\nimport type {\n AddressRow,\n IDBCursorDirectionLike,\n IDBCursorLike,\n IDBKeyRangeLike,\n IDBObjectStoreLike,\n IDBRequestLike,\n IDBTransactionLike,\n TagRef,\n Tombstone,\n} from \"./idb-types.ts\";\nimport type { PutDecision } from \"./put.ts\";\nimport type { PutResult } from \"./types.ts\";\n\nexport async function reqOf<T>(req: IDBRequestLike): Promise<T> {\n return new Promise((resolve, reject) => {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- request results are trusted as T at each call site\n req.onsuccess = () => resolve(req.result as T);\n req.onerror = () => reject(req.error ?? new StorageError(\"IndexedDB request failed\"));\n });\n}\n\nexport async function txDone(tx: IDBTransactionLike): Promise<void> {\n return new Promise((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error ?? new StorageError(\"IndexedDB transaction failed\"));\n });\n}\n\nexport async function walkCursor(\n source: {\n openCursor: (range?: IDBKeyRangeLike, direction?: IDBCursorDirectionLike) => IDBRequestLike;\n },\n range: IDBKeyRangeLike | undefined,\n direction: IDBCursorDirectionLike,\n visit: (cursor: IDBCursorLike) => boolean,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const req = source.openCursor(range, direction);\n req.onerror = () => reject(req.error ?? new StorageError(\"IndexedDB cursor failed\"));\n req.onsuccess = () => {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- openCursor resolves with a cursor or null\n const cursor = (req.result ?? undefined) as IDBCursorLike | undefined;\n if (cursor === undefined) {\n resolve();\n return;\n }\n if (visit(cursor)) {\n resolve();\n return;\n }\n cursor.continue();\n };\n });\n}\n\nexport function tagRefKey(name: string, value: string, id: string): string {\n return `${name}:${value.toLowerCase()}:${id}`;\n}\n\nexport function writeTagRefs(store: IDBObjectStoreLike, event: Event): void {\n for (const tag of event.tags) {\n if ((tag[0] !== \"e\" && tag[0] !== \"p\") || tag[1] === undefined) {\n continue;\n }\n const value = tag[1].toLowerCase();\n const { id } = event;\n store.put({\n key: tagRefKey(tag[0], value, id),\n name: tag[0],\n value,\n id,\n created_at: event.created_at,\n } satisfies TagRef);\n }\n}\n\nexport function deleteStoredEvent(\n tx: IDBTransactionLike,\n event: Event,\n addressRow?: AddressRow,\n): void {\n const tagRefs = tx.objectStore(TAG_REFS);\n for (const tag of event.tags) {\n if ((tag[0] === \"e\" || tag[0] === \"p\") && tag[1] !== undefined) {\n tagRefs.delete(tagRefKey(tag[0], tag[1], event.id));\n }\n }\n if (addressRow?.id === event.id) {\n tx.objectStore(ADDRESSES).delete(addressRow.address);\n }\n tx.objectStore(EVENTS).delete(event.id);\n}\n\nexport function persistPlanTombstones(\n store: IDBObjectStoreLike,\n plan: DeletionPlan,\n coordIds: ReadonlyArray<string>,\n deletion: DeletionState,\n): void {\n for (const id of plan.removeIds) {\n store.put({ key: `id:${id}`, type: \"id\" } satisfies Tombstone);\n store.delete(`pending:${id}`);\n }\n for (const id of coordIds) {\n store.put({ key: `id:${id}`, type: \"id\" } satisfies Tombstone);\n store.delete(`pending:${id}`);\n }\n for (const p of plan.pendingIds) {\n if (deletion.ids.has(p.id)) {\n continue;\n }\n store.put({\n key: `pending:${p.id}`,\n type: \"pending\",\n pubkey: p.pubkey,\n } satisfies Tombstone);\n }\n for (const c of plan.coordinates) {\n const prev = deletion.coordinates.get(c.key) ?? Number.NEGATIVE_INFINITY;\n store.put({\n key: `coord:${c.key}`,\n type: \"coord\",\n until: Math.max(prev, c.until),\n } satisfies Tombstone);\n }\n}\n\nexport function tombstonesToPlan(rows: unknown[]): DeletionPlan {\n const plan: DeletionPlan = { removeIds: [], pendingIds: [], coordinates: [] };\n for (const row of rows) {\n if (!isRecord(row)) {\n continue;\n }\n const r = row;\n if (r[\"type\"] === \"id\" && typeof r[\"key\"] === \"string\" && r[\"key\"].startsWith(\"id:\")) {\n plan.removeIds.push(r[\"key\"].slice(3));\n continue;\n }\n if (\n r[\"type\"] === \"pending\" &&\n typeof r[\"key\"] === \"string\" &&\n r[\"key\"].startsWith(\"pending:\") &&\n typeof r[\"pubkey\"] === \"string\"\n ) {\n plan.pendingIds.push({ id: r[\"key\"].slice(8), pubkey: r[\"pubkey\"] });\n continue;\n }\n if (\n r[\"type\"] === \"coord\" &&\n typeof r[\"key\"] === \"string\" &&\n r[\"key\"].startsWith(\"coord:\") &&\n typeof r[\"until\"] === \"number\"\n ) {\n plan.coordinates.push({ key: r[\"key\"].slice(6), until: r[\"until\"] });\n }\n }\n return plan;\n}\n\nexport function applyPutIndexedDb(\n tx: IDBTransactionLike,\n s: {\n deletion: DeletionState;\n replaceable: Map<string, string>;\n },\n d: PutDecision,\n): PutResult {\n const events = tx.objectStore(EVENTS);\n const tagRefs = tx.objectStore(TAG_REFS);\n const addresses = tx.objectStore(ADDRESSES);\n const tombstones = tx.objectStore(TOMBSTONES);\n if (d.action === \"skip\") {\n return d.result;\n }\n if (d.action === \"tombstone\") {\n tombstones.put({ key: `id:${d.event.id}`, type: \"id\" } satisfies Tombstone);\n tombstones.delete(`pending:${d.event.id}`);\n s.deletion.ids.add(d.event.id);\n s.deletion.pending.delete(d.event.id);\n return \"duplicate\";\n }\n if (d.action === \"delete\") {\n s.deletion.pending.delete(d.event.id);\n persistPlanTombstones(tombstones, d.plan, d.coordIds, s.deletion);\n s.deletion.absorb(d.plan);\n for (const id of d.coordIds) {\n s.deletion.ids.add(id);\n }\n events.put(d.event);\n writeTagRefs(tagRefs, d.event);\n return \"deleted\";\n }\n events.put(d.event);\n writeTagRefs(tagRefs, d.event);\n if (d.address !== undefined) {\n addresses.put({\n address: d.address,\n id: d.event.id,\n created_at: d.event.created_at,\n });\n s.replaceable.set(d.address, d.event.id);\n }\n return d.result;\n}\n","// oxlint-disable unicorn/prefer-add-event-listener -- the *Like driver interfaces model only the `on*` handler surface\nimport type { Event } from \"../core/event.ts\";\nimport { compareEventsDesc, sortEvents } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { StorageError } from \"./error.ts\";\nimport { reqOf } from \"./idb-helpers.ts\";\nimport { EVENTS, TAG_REFS } from \"./idb-types.ts\";\nimport type {\n IDBCursorDirectionLike,\n IDBCursorLike,\n IDBIndexLike,\n IDBKeyRangeLike,\n IDBObjectStoreLike,\n IDBRequestLike,\n IDBTransactionLike,\n TagRef,\n} from \"./idb-types.ts\";\n\nexport function prefixRange(\n prefix: ReadonlyArray<string | number>,\n since?: number,\n until?: number,\n): IDBKeyRangeLike {\n return idbKeyRange().bound(\n [...prefix, since ?? 0],\n [...prefix, until ?? Number.MAX_SAFE_INTEGER],\n );\n}\n\nfunction createdAtRange(since?: number, until?: number): IDBKeyRangeLike {\n return idbKeyRange().bound(since ?? 0, until ?? Number.MAX_SAFE_INTEGER);\n}\n\nfunction idbKeyRange(): {\n bound: (\n lower: unknown,\n upper: unknown,\n lowerOpen?: boolean,\n upperOpen?: boolean,\n ) => IDBKeyRangeLike;\n} {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- globalThis lookup of the platform IDBKeyRange\n return (globalThis as unknown as { IDBKeyRange: ReturnType<typeof idbKeyRange> }).IDBKeyRange;\n}\n\nexport function epTagPrefixes(filter: Filter): Array<{ name: \"e\" | \"p\"; value: string }> {\n const out: Array<{ name: \"e\" | \"p\"; value: string }> = [];\n for (const name of [\"e\", \"p\"] as const) {\n const values = filter[`#${name}`];\n if (values === undefined) {\n continue;\n }\n for (const value of values) {\n out.push({ name, value: value.toLowerCase() });\n }\n }\n return out;\n}\n\ntype MergeOpener = {\n open: () => IDBRequestLike;\n read: (\n cursor: IDBCursorLike,\n ok: (event: Event | undefined) => void,\n err: (error: Error) => void,\n ) => void;\n};\n\nfunction eventCursor(\n source: {\n openCursor: (range?: IDBKeyRangeLike, direction?: IDBCursorDirectionLike) => IDBRequestLike;\n },\n range: IDBKeyRangeLike,\n): MergeOpener {\n return {\n open: () => source.openCursor(range, \"prev\"),\n read: (cursor, ok) => {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- index cursor values are stored Event rows\n ok(cursor.value as Event);\n },\n };\n}\n\nfunction tagCursor(\n index: IDBIndexLike,\n events: IDBObjectStoreLike,\n range: IDBKeyRangeLike,\n): MergeOpener {\n return {\n open: () => index.openCursor(range, \"prev\"),\n read: (cursor, ok, err) => {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- tag_refs cursor values are TagRef rows written by writeTagRefs\n const row = cursor.value as TagRef;\n const req = events.get(row.id);\n req.onerror = () => err(req.error ?? new StorageError(\"IndexedDB get failed\"));\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- get resolves with a stored Event row or undefined\n req.onsuccess = () => ok(req.result as Event | undefined);\n },\n };\n}\n\nexport async function scanIds(\n tx: IDBTransactionLike,\n filter: Filter,\n accept: (event: Event) => boolean,\n take: (event: Event) => boolean,\n): Promise<void> {\n const ids = filter.ids ?? [];\n const events = tx.objectStore(EVENTS);\n const reqs = ids.map((id) => events.get(id.toLowerCase()));\n const rows = await Promise.all(reqs.map(async (req) => reqOf<Event | undefined>(req)));\n const matched: Event[] = [];\n const seen = new Set<string>();\n for (const event of rows) {\n if (event === undefined || seen.has(event.id) || !accept(event)) {\n continue;\n }\n seen.add(event.id);\n matched.push(event);\n }\n sortEvents(matched);\n const out = filter.limit === undefined ? matched : matched.slice(0, filter.limit);\n for (const event of out) {\n if (take(event)) {\n break;\n }\n }\n}\n\n/** IDB auto-commits when onsuccess returns with no outstanding requests. */\nexport async function kWayMerge(\n openers: ReadonlyArray<MergeOpener>,\n accept: (event: Event) => boolean,\n take: (event: Event) => boolean,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n if (openers.length === 0) {\n resolve();\n return;\n }\n\n type Slot = { cursor: IDBCursorLike | undefined; head: Event | undefined };\n const slots: Slot[] = openers.map(() => ({ cursor: undefined, head: undefined }));\n const seen = new Set<string>();\n let inflight = 0;\n let phase: \"merge\" | \"drain\" | \"done\" = \"merge\";\n let drainT = 0;\n let drainBuf: Event[] = [];\n\n const finish = () => {\n if (phase === \"done\") {\n return;\n }\n phase = \"done\";\n resolve();\n };\n const fail = (error: Error) => {\n if (phase === \"done\") {\n return;\n }\n phase = \"done\";\n reject(error);\n };\n\n const stepCursor = (i: number) => {\n const { cursor } = slots.at(i) ?? { cursor: undefined };\n if (cursor === undefined) {\n return;\n }\n inflight++;\n cursor.continue();\n };\n\n const emitDrain = () => {\n sortEvents(drainBuf);\n for (const event of drainBuf) {\n if (seen.has(event.id) || !accept(event)) {\n continue;\n }\n seen.add(event.id);\n if (take(event)) {\n finish();\n return;\n }\n }\n drainBuf = [];\n phase = \"merge\";\n pump();\n };\n\n const pump = () => {\n if (phase === \"done\" || inflight > 0) {\n return;\n }\n if (phase === \"drain\") {\n emitDrain();\n return;\n }\n let best: Event | undefined;\n for (const slot of slots) {\n const event = slot.head;\n if (event === undefined) {\n continue;\n }\n if (best === undefined || compareEventsDesc(event, best) < 0) {\n best = event;\n }\n }\n if (best === undefined) {\n finish();\n return;\n }\n phase = \"drain\";\n drainT = best.created_at;\n drainBuf = [];\n for (const [i, slot] of slots.entries()) {\n const event = slot.head;\n if (event === undefined || event.created_at !== drainT) {\n continue;\n }\n drainBuf.push(event);\n slot.head = undefined;\n stepCursor(i);\n }\n if (inflight === 0) {\n pump();\n }\n };\n\n const onEvent = (i: number, event: Event | undefined) => {\n if (phase === \"done\") {\n return;\n }\n const slot = slots.at(i);\n if (slot === undefined) {\n return;\n }\n const { cursor } = slot;\n if (event === undefined) {\n if (cursor === undefined) {\n pump();\n } else {\n inflight++;\n cursor.continue();\n }\n return;\n }\n if (phase === \"drain\") {\n if (event.created_at === drainT) {\n drainBuf.push(event);\n if (cursor === undefined) {\n pump();\n } else {\n inflight++;\n cursor.continue();\n }\n return;\n }\n slot.head = event;\n pump();\n return;\n }\n slot.head = event;\n pump();\n };\n\n const openSlot = (i: number, opener: MergeOpener): void => {\n const req = opener.open();\n req.onerror = () => fail(req.error ?? new StorageError(\"IndexedDB cursor failed\"));\n inflight++;\n req.onsuccess = () => {\n inflight--;\n if (phase === \"done\") {\n return;\n }\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- openCursor resolves with a cursor or null\n const cursor = (req.result ?? undefined) as IDBCursorLike | undefined;\n const slot = slots.at(i);\n if (slot === undefined) {\n return;\n }\n if (cursor === undefined) {\n slot.cursor = undefined;\n slot.head = undefined;\n pump();\n return;\n }\n slot.cursor = cursor;\n inflight++;\n opener.read(\n cursor,\n (event) => {\n inflight--;\n onEvent(i, event);\n },\n (error) => {\n inflight--;\n fail(error);\n },\n );\n };\n };\n for (const [i, opener] of openers.entries()) {\n openSlot(i, opener);\n }\n });\n}\n\n/**\n * Cap on cursors opened per filter: beyond it a k-way merge degenerates into one request per match\n * candidate. Past the cap the planner opens fewer, wider cursors and lets `accept` (matchFilter)\n * enforce the remaining terms, so results and per-filter limits are unchanged.\n */\nexport const MAX_MERGE_CURSORS = 64;\n\nexport async function scanFilter(\n tx: IDBTransactionLike,\n filter: Filter,\n accept: (event: Event) => boolean,\n take: (event: Event) => boolean,\n): Promise<void> {\n if (filter.limit === 0) {\n return;\n }\n if (filter.since !== undefined && filter.until !== undefined && filter.since > filter.until) {\n return;\n }\n\n if (filter.ids) {\n return scanIds(tx, filter, accept, take);\n }\n\n const events = tx.objectStore(EVENTS);\n const fullScan: MergeOpener = eventCursor(\n events.index(\"created_at\"),\n createdAtRange(filter.since, filter.until),\n );\n const openers: MergeOpener[] = [];\n if (filter.authors && filter.kinds) {\n if (filter.authors.length * filter.kinds.length > MAX_MERGE_CURSORS) {\n // One cursor per kind keeps the bound tight when the kind list is\n // small; otherwise a single created_at scan still fits under the cap.\n if (filter.kinds.length <= MAX_MERGE_CURSORS) {\n const index = events.index(\"kind_created_at\");\n for (const kind of filter.kinds) {\n openers.push(eventCursor(index, prefixRange([kind], filter.since, filter.until)));\n }\n } else {\n openers.push(fullScan);\n }\n } else {\n const index = events.index(\"kind_pubkey_created_at\");\n for (const kind of filter.kinds) {\n for (const pk of filter.authors) {\n openers.push(\n eventCursor(index, prefixRange([kind, pk.toLowerCase()], filter.since, filter.until)),\n );\n }\n }\n }\n } else if (filter.authors) {\n if (filter.authors.length > MAX_MERGE_CURSORS) {\n openers.push(fullScan);\n } else {\n const index = events.index(\"pubkey_created_at\");\n for (const pk of filter.authors) {\n openers.push(\n eventCursor(index, prefixRange([pk.toLowerCase()], filter.since, filter.until)),\n );\n }\n }\n } else if (filter.kinds) {\n if (filter.kinds.length > MAX_MERGE_CURSORS) {\n openers.push(fullScan);\n } else {\n const index = events.index(\"kind_created_at\");\n for (const kind of filter.kinds) {\n openers.push(eventCursor(index, prefixRange([kind], filter.since, filter.until)));\n }\n }\n } else {\n const tags = epTagPrefixes(filter);\n if (tags.length > MAX_MERGE_CURSORS) {\n openers.push(fullScan);\n } else if (tags.length > 0) {\n const index = tx.objectStore(TAG_REFS).index(\"name_value_created\");\n for (const tag of tags) {\n openers.push(\n tagCursor(index, events, prefixRange([tag.name, tag.value], filter.since, filter.until)),\n );\n }\n } else {\n openers.push(fullScan);\n }\n }\n return kWayMerge(openers, accept, take);\n}\n","// oxlint-disable unicorn/prefer-add-event-listener -- the *Like driver interfaces model only the `on*` handler surface\nimport type { Event } from \"../core/event.ts\";\nimport { isReplaceableWinner, itemCompare, validateSignedEvent } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { eventAddress } from \"../core/tag.ts\";\nimport { DeletionState, planDeletion } from \"./deletion.ts\";\nimport { StorageError } from \"./error.ts\";\nimport { deleteStoredEvent, writeTagRefs } from \"./idb-helpers.ts\";\nimport { ADDRESSES, EVENTS, OUTBOX_BOUNDS, TAG_REFS, TOMBSTONES } from \"./idb-types.ts\";\nimport type {\n AddressRow,\n IDBDatabaseLike,\n IDBFactoryLike,\n IDBTransactionLike,\n Tombstone,\n} from \"./idb-types.ts\";\n\nexport const IDB_VERSION = 4;\n\nexport async function openDb(dbName: string): Promise<IDBDatabaseLike> {\n return new Promise((resolve, reject) => {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- reads the platform global, which has no ambient types here\n const factory = (globalThis as unknown as { indexedDB: IDBFactoryLike }).indexedDB;\n const req = factory.open(dbName, IDB_VERSION);\n req.onupgradeneeded = (ev) => {\n const db = req.result;\n const { oldVersion } = ev;\n const tx = ev.target.transaction;\n if (oldVersion < 1) {\n db.createObjectStore(EVENTS, { keyPath: \"id\" });\n }\n if (oldVersion < 2) {\n const events = tx.objectStore(EVENTS);\n events.createIndex(\"created_at\", \"created_at\");\n events.createIndex(\"kind_created_at\", [\"kind\", \"created_at\"]);\n events.createIndex(\"pubkey_created_at\", [\"pubkey\", \"created_at\"]);\n events.createIndex(\"kind_pubkey_created_at\", [\"kind\", \"pubkey\", \"created_at\"]);\n db.createObjectStore(TAG_REFS, { keyPath: \"key\" }).createIndex(\"name_value_created\", [\n \"name\",\n \"value\",\n \"created_at\",\n ]);\n db.createObjectStore(ADDRESSES, { keyPath: \"address\" });\n db.createObjectStore(TOMBSTONES, { keyPath: \"key\" });\n if (oldVersion >= 1) {\n const all = events.getAll();\n all.onsuccess = () => {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- getAll resolves with the stored legacy event rows\n migrateV1Events(tx, (all.result as unknown[]) ?? []);\n compactSupersededReplaceables(tx);\n };\n }\n } else if (oldVersion < 3) {\n compactSupersededReplaceables(tx);\n }\n if (oldVersion < 4) {\n db.createObjectStore(OUTBOX_BOUNDS, { keyPath: \"key\" });\n }\n };\n let settled = false;\n req.onsuccess = () => {\n const db = req.result;\n if (settled) {\n // The blocked rejection already fired; do not leak this late connection.\n db.close();\n return;\n }\n settled = true;\n // Yield to any newer-version open instead of blocking it forever.\n db.onversionchange = () => db.close();\n resolve(db);\n };\n req.onerror = () => {\n settled = true;\n reject(req.error ?? new StorageError(\"IndexedDB open failed\"));\n };\n req.onblocked = () => {\n settled = true;\n reject(new StorageError(\"IndexedDB open blocked by another connection\"));\n };\n });\n}\n\nexport function migrateV1Events(tx: IDBTransactionLike, events: ReadonlyArray<unknown>): void {\n const eventsStore = tx.objectStore(EVENTS);\n const tagRefs = tx.objectStore(TAG_REFS);\n const addresses = tx.objectStore(ADDRESSES);\n const tombstones = tx.objectStore(TOMBSTONES);\n\n // Canonical-input precondition: legacy rows failing validation are dropped.\n const valid: Event[] = [];\n for (const e of events) {\n if (validateSignedEvent(e)) {\n valid.push(e);\n continue;\n }\n if (typeof e === \"object\" && e !== null && \"id\" in e && typeof e.id === \"string\") {\n eventsStore.delete(e.id);\n }\n }\n\n const byId = new Map(valid.map((e) => [e.id, e]));\n const deletion = new DeletionState();\n const dels = valid.filter((e) => e.kind === Kind.EventDeletion).sort(itemCompare);\n for (const del of dels) {\n const plan = planDeletion(del, (id) => byId.get(id));\n deletion.absorb(plan);\n for (const c of plan.coordinates) {\n for (const ev of valid) {\n if (ev.kind === Kind.EventDeletion) {\n continue;\n }\n if (eventAddress(ev) === c.key && ev.created_at <= c.until) {\n deletion.ids.add(ev.id);\n }\n }\n }\n }\n\n for (const id of deletion.ids) {\n tombstones.put({ key: `id:${id}`, type: \"id\" } satisfies Tombstone);\n }\n for (const [id, pubkey] of deletion.pending) {\n tombstones.put({ key: `pending:${id}`, type: \"pending\", pubkey } satisfies Tombstone);\n }\n for (const [key, until] of deletion.coordinates) {\n tombstones.put({ key: `coord:${key}`, type: \"coord\", until } satisfies Tombstone);\n }\n\n const winners = new Map<string, Event>();\n for (const event of valid) {\n if (deletion.ids.has(event.id) || deletion.covers(event)) {\n eventsStore.delete(event.id);\n continue;\n }\n writeTagRefs(tagRefs, event);\n const addr = eventAddress(event);\n if (addr !== undefined) {\n const prev = winners.get(addr);\n if (!prev || isReplaceableWinner(event, prev)) {\n winners.set(addr, event);\n }\n }\n }\n for (const [address, event] of winners) {\n addresses.put({ address, id: event.id, created_at: event.created_at });\n }\n}\n\nexport function compactSupersededReplaceables(tx: IDBTransactionLike): void {\n const eventsStore = tx.objectStore(EVENTS);\n const addressesStore = tx.objectStore(ADDRESSES);\n const evReq = eventsStore.getAll();\n const addrReq = addressesStore.getAll();\n let events: Event[] | undefined;\n let addressRows: AddressRow[] | undefined;\n const run = () => {\n if (events === undefined || addressRows === undefined) {\n return;\n }\n const byAddr = new Map<string, AddressRow>();\n for (const row of addressRows) {\n byAddr.set(row.address, row);\n }\n const kept: Event[] = [];\n for (const event of events) {\n const addr = eventAddress(event);\n if (addr !== undefined) {\n const row = byAddr.get(addr);\n if (!row || row.id !== event.id) {\n deleteStoredEvent(tx, event, row);\n continue;\n }\n }\n kept.push(event);\n }\n addressesStore.clear();\n for (const event of kept) {\n const addr = eventAddress(event);\n if (addr === undefined) {\n continue;\n }\n addressesStore.put({ address: addr, id: event.id, created_at: event.created_at });\n }\n };\n evReq.onsuccess = () => {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- getAll resolves with the stored Event rows\n events = (evReq.result as Event[]) ?? [];\n run();\n };\n addrReq.onsuccess = () => {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- getAll resolves with the stored AddressRow rows\n addressRows = (addrReq.result as AddressRow[]) ?? [];\n run();\n };\n}\n","// oxlint-disable unicorn/prefer-add-event-listener -- the *Like driver interfaces model only the `on*` handler surface\n// oxlint-disable no-await-in-loop -- requests inside one IndexedDB transaction are issued sequentially; awaiting a no-request promise auto-commits the tx\nimport type { Event } from \"../core/event.ts\";\nimport { itemCompare, sortEvents, validateSignedEvent } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { matchFilter } from \"../core/filter.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { eventAddress, parseEventAddress } from \"../core/tag.ts\";\nimport { DeletionState } from \"./deletion.ts\";\nimport { StorageError, toStorageError } from \"./error.ts\";\nimport {\n applyPutIndexedDb,\n deleteStoredEvent,\n reqOf,\n tombstonesToPlan,\n txDone,\n walkCursor,\n} from \"./idb-helpers.ts\";\nimport { prefixRange, scanFilter } from \"./idb-query.ts\";\nimport { openDb } from \"./idb-schema.ts\";\nimport {\n ADDRESSES,\n EVENTS,\n OUTBOX_BOUNDS,\n TAG_REFS,\n TOMBSTONES,\n WRITE_STORES,\n} from \"./idb-types.ts\";\nimport type {\n AddressRow,\n IDBCursorDirectionLike,\n IDBDatabaseLike,\n IDBFactoryLike,\n IDBTransactionLike,\n OutboxBoundRow,\n Tombstone,\n} from \"./idb-types.ts\";\nimport { decidePut, outboxBoundKey } from \"./put.ts\";\nimport type { EventStore, NegentropyItem, OutboxBound, PutResult } from \"./types.ts\";\n\n/** Options for {@link IndexedDbEventStore}. */\nexport type IndexedDbEventStoreOptions = {\n /** IndexedDB database name. */\n dbName?: string;\n};\n\n/**\n * Browser IndexedDB event store with the same replaceable / deletion semantics as\n * {@link MemoryEventStore}. Requires a DOM IndexedDB implementation.\n *\n * `open` loads every tombstone and address row into memory. Fine at 10^4 events; tens of MB at 10^5\n * addressables.\n */\nexport class IndexedDbEventStore implements EventStore {\n readonly #dbName: string;\n #db: IDBDatabaseLike | undefined;\n /** Set when this connection closed to unblock a newer-version open; ops then fail fast. */\n #yielded = false;\n readonly #deletion = new DeletionState();\n #replaceable = new Map<string, string>();\n /** Serializes writes so abort restore cannot roll back a committed sibling tx. */\n #writeTail: Promise<void> = Promise.resolve();\n\n constructor(opts: IndexedDbEventStoreOptions = {}) {\n this.#dbName = opts.dbName ?? \"@qntx/nostr\";\n }\n\n static isAvailable(): boolean {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- reads an optional host global\n return (globalThis as { indexedDB?: IDBFactoryLike }).indexedDB !== undefined;\n }\n\n async open(): Promise<void> {\n if (this.#db) {\n return;\n }\n if (!IndexedDbEventStore.isAvailable()) {\n throw new StorageError(\"IndexedDB is not available in this environment\");\n }\n const db = await openDb(this.#dbName);\n const closeOnUpgrade = db.onversionchange;\n db.onversionchange = (ev) => {\n closeOnUpgrade?.(ev);\n this.#yielded = true;\n };\n this.#yielded = false;\n this.#db = db;\n await this.#loadCaches(db);\n }\n\n async #ensure(): Promise<IDBDatabaseLike> {\n if (this.#yielded) {\n throw new StorageError(\"IndexedDB connection closed by a newer version\");\n }\n await this.open();\n const db = this.#db;\n if (db === undefined) {\n throw new StorageError(\"IndexedDB is not available in this environment\");\n }\n return db;\n }\n\n /** Loads every tombstone and address row into RAM. Tens of MB at 10^5 addressables. */\n async #loadCaches(db: IDBDatabaseLike): Promise<void> {\n const tx = db.transaction([TOMBSTONES, ADDRESSES], \"readonly\");\n const done = txDone(tx);\n this.#deletion.clear();\n this.#replaceable.clear();\n const tombs = await reqOf<unknown[]>(tx.objectStore(TOMBSTONES).getAll());\n this.#deletion.absorb(tombstonesToPlan(tombs ?? []));\n await walkCursor(tx.objectStore(ADDRESSES), undefined, \"next\", (cursor) => {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- address cursor values are the stored AddressRow rows\n const row = cursor.value as AddressRow;\n this.#replaceable.set(row.address, row.id);\n return false;\n });\n await done;\n }\n\n async #enqueueWrite<T>(op: () => Promise<T>): Promise<T> {\n const prev = this.#writeTail;\n let release: (() => void) | undefined;\n this.#writeTail = new Promise<void>((resolve) => {\n release = resolve;\n });\n try {\n await prev;\n return await op();\n } finally {\n release?.();\n }\n }\n\n async put(event: Event): Promise<PutResult> {\n const [result] = await this.putMany([event]);\n if (result === undefined) {\n throw new StorageError(\"putMany returned no result\");\n }\n return result;\n }\n\n /** One readwrite transaction; abort rejects the whole batch with no partial persist. */\n async putMany(events: ReadonlyArray<Event>): Promise<PutResult[]> {\n if (events.length === 0) {\n return [];\n }\n return this.#enqueueWrite(async () => this.#putManyLocked(events));\n }\n\n async #putManyLocked(events: ReadonlyArray<Event>): Promise<PutResult[]> {\n let db: IDBDatabaseLike;\n try {\n db = await this.#ensure();\n } catch (error) {\n throw toStorageError(error);\n }\n const tx = db.transaction(WRITE_STORES, \"readwrite\");\n const txSettled = new Promise<void>((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error ?? new StorageError(\"IndexedDB transaction failed\"));\n tx.onabort = () => reject(tx.error ?? new StorageError(\"IndexedDB transaction aborted\"));\n });\n const txOutcome = (async (): Promise<unknown> => {\n try {\n await txSettled;\n return undefined;\n } catch (error: unknown) {\n return error;\n }\n })();\n const cache = this.#snapshotCaches();\n try {\n const results = await this.#putAllInTx(tx, events);\n const txErr = await txOutcome;\n if (txErr !== undefined) {\n throw toStorageError(txErr);\n }\n return results;\n } catch (error) {\n this.#restoreCaches(cache);\n try {\n tx.abort();\n } catch {\n // already aborted or complete\n }\n await txOutcome;\n throw toStorageError(error);\n }\n }\n\n /** Sequential awaits so the tx stays alive between events. */\n async #putAllInTx(tx: IDBTransactionLike, batch: ReadonlyArray<Event>): Promise<PutResult[]> {\n const eventsStore = tx.objectStore(EVENTS);\n const addressesStore = tx.objectStore(ADDRESSES);\n const results: PutResult[] = [];\n for (const raw of batch) {\n if (!validateSignedEvent(raw)) {\n results.push(\"invalid\");\n continue;\n }\n const event = raw;\n const byId = new Map<string, Pick<Event, \"id\" | \"pubkey\" | \"kind\" | \"created_at\" | \"tags\">>();\n const existing = await reqOf<Event | undefined>(eventsStore.get(event.id));\n if (existing) {\n byId.set(existing.id, existing);\n }\n if (event.kind === Kind.EventDeletion) {\n for (const tag of event.tags) {\n if (tag[0] !== \"e\" || tag[1] === undefined) {\n continue;\n }\n const got = await reqOf<Event | undefined>(eventsStore.get(tag[1].toLowerCase()));\n if (got) {\n byId.set(got.id, got);\n }\n }\n }\n const addrRows = new Map<string, { id: string; created_at: number }>();\n const ownAddr = eventAddress(event);\n if (ownAddr !== undefined) {\n const row = await reqOf<AddressRow | undefined>(addressesStore.get(ownAddr));\n if (row) {\n addrRows.set(ownAddr, { id: row.id, created_at: row.created_at });\n }\n }\n if (event.kind === Kind.EventDeletion) {\n for (const tag of event.tags) {\n if (tag[0] !== \"a\" || tag[1] === undefined) {\n continue;\n }\n const coord = parseEventAddress(tag[1]);\n if (!coord) {\n continue;\n }\n const key = `${coord.kind}:${coord.pubkey}:${coord.identifier}`;\n if (addrRows.has(key)) {\n continue;\n }\n const row = await reqOf<AddressRow | undefined>(addressesStore.get(key));\n if (row) {\n addrRows.set(key, { id: row.id, created_at: row.created_at });\n }\n }\n }\n const d = decidePut(event, {\n deletion: this.#deletion,\n getById: (id) => byId.get(id),\n getReplaceable: (address) => addrRows.get(address),\n });\n // Await row deletes in this loop so the next events.get runs in the IDB\n // request continuation. `await` of a no-request promise auto-commits the tx.\n if (d.action === \"delete\") {\n const remove = new Set([...d.plan.removeIds, ...d.coordIds]);\n for (const id of remove) {\n await this.#deleteEventRows(tx, id);\n }\n } else if (d.action === \"insert\" && d.replaceId !== undefined) {\n await this.#deleteEventRows(tx, d.replaceId);\n }\n results.push(\n applyPutIndexedDb(\n tx,\n {\n deletion: this.#deletion,\n replaceable: this.#replaceable,\n },\n d,\n ),\n );\n }\n return results;\n }\n\n #snapshotCaches(): {\n replaceable: Map<string, string>;\n ids: Set<string>;\n pending: Map<string, string>;\n coordinates: Map<string, number>;\n } {\n return {\n replaceable: new Map(this.#replaceable),\n ids: new Set(this.#deletion.ids),\n pending: new Map(this.#deletion.pending),\n coordinates: new Map(this.#deletion.coordinates),\n };\n }\n\n #restoreCaches(snap: {\n replaceable: Map<string, string>;\n ids: Set<string>;\n pending: Map<string, string>;\n coordinates: Map<string, number>;\n }): void {\n this.#replaceable = snap.replaceable;\n this.#deletion.ids.clear();\n for (const id of snap.ids) {\n this.#deletion.ids.add(id);\n }\n this.#deletion.pending.clear();\n for (const [k, v] of snap.pending) {\n this.#deletion.pending.set(k, v);\n }\n this.#deletion.coordinates.clear();\n for (const [k, v] of snap.coordinates) {\n this.#deletion.coordinates.set(k, v);\n }\n }\n\n async get(id: string): Promise<Event | undefined> {\n const key = id.toLowerCase();\n if (this.#deletion.ids.has(key)) {\n return undefined;\n }\n const db = await this.#ensure();\n const tx = db.transaction(EVENTS, \"readonly\");\n const done = txDone(tx);\n const event = await reqOf<Event | undefined>(tx.objectStore(EVENTS).get(key));\n await done;\n return event;\n }\n\n async query(filters: Filter[]): Promise<Event[]> {\n const db = await this.#ensure();\n const tx = db.transaction([EVENTS, TAG_REFS], \"readonly\");\n const done = txDone(tx);\n const seen = new Set<string>();\n const events: Event[] = [];\n for (const filter of filters) {\n const matched = await this.#queryOne(tx, filter);\n for (const event of matched) {\n if (seen.has(event.id)) {\n continue;\n }\n seen.add(event.id);\n events.push(event);\n }\n }\n await done;\n sortEvents(events);\n return events;\n }\n\n async count(filters: Filter[]): Promise<number> {\n const db = await this.#ensure();\n const tx = db.transaction([EVENTS, TAG_REFS], \"readonly\");\n const done = txDone(tx);\n const seen = new Set<string>();\n for (const filter of filters) {\n const local = new Set<string>();\n await this.#scan(tx, filter, (event) => {\n local.add(event.id);\n return filter.limit !== undefined && local.size >= filter.limit;\n });\n for (const id of local) {\n seen.add(id);\n }\n }\n await done;\n return seen.size;\n }\n\n async negentropyItems(filter: Filter): Promise<NegentropyItem[]> {\n const db = await this.#ensure();\n const tx = db.transaction([EVENTS, TAG_REFS], \"readonly\");\n const done = txDone(tx);\n const items: NegentropyItem[] = [];\n await this.#scan(tx, filter, (event) => {\n items.push({ id: event.id, created_at: event.created_at });\n return filter.limit !== undefined && items.length >= filter.limit;\n });\n await done;\n items.sort(itemCompare);\n return items;\n }\n\n #acceptHit(filter: Filter, event: Event): boolean {\n if (this.#deletion.ids.has(event.id)) {\n return false;\n }\n if (this.#deletion.covers(event)) {\n return false;\n }\n return matchFilter(filter, event);\n }\n\n async #queryOne(tx: IDBTransactionLike, filter: Filter): Promise<Event[]> {\n const matched: Event[] = [];\n await this.#scan(tx, filter, (event) => {\n matched.push(event);\n return filter.limit !== undefined && matched.length >= filter.limit;\n });\n sortEvents(matched);\n return matched;\n }\n\n async #scan(\n tx: IDBTransactionLike,\n filter: Filter,\n take: (event: Event) => boolean,\n ): Promise<void> {\n return scanFilter(tx, filter, (event) => this.#acceptHit(filter, event), take);\n }\n\n async getOutboxBound(pubkey: string, kind: number): Promise<OutboxBound | undefined> {\n try {\n const db = await this.#ensure();\n const tx = db.transaction(OUTBOX_BOUNDS, \"readonly\");\n const done = txDone(tx);\n const row = await reqOf<OutboxBoundRow | undefined>(\n tx.objectStore(OUTBOX_BOUNDS).get(outboxBoundKey(pubkey, kind)),\n );\n await done;\n if (row && typeof row.oldest === \"number\" && typeof row.newest === \"number\") {\n return { oldest: row.oldest, newest: row.newest };\n }\n return await this.#deriveOutboxBound(pubkey, kind);\n } catch (error) {\n throw toStorageError(error);\n }\n }\n\n async setOutboxBound(pubkey: string, kind: number, bound: OutboxBound): Promise<void> {\n return this.#enqueueWrite(async () => this.#setOutboxBoundLocked(pubkey, kind, bound));\n }\n\n async #setOutboxBoundLocked(pubkey: string, kind: number, bound: OutboxBound): Promise<void> {\n try {\n const db = await this.#ensure();\n const tx = db.transaction(OUTBOX_BOUNDS, \"readwrite\");\n const done = txDone(tx);\n tx.objectStore(OUTBOX_BOUNDS).put({\n key: outboxBoundKey(pubkey, kind),\n oldest: bound.oldest,\n newest: bound.newest,\n } satisfies OutboxBoundRow);\n await done;\n } catch (error) {\n throw toStorageError(error);\n }\n }\n\n async remove(ids: string[]): Promise<number> {\n return this.#enqueueWrite(async () => this.#removeLocked(ids));\n }\n\n async #removeLocked(ids: string[]): Promise<number> {\n const db = await this.#ensure();\n const tx = db.transaction(WRITE_STORES, \"readwrite\");\n const tombstones = tx.objectStore(TOMBSTONES);\n const done = txDone(tx);\n let n = 0;\n for (const raw of ids) {\n const id = raw.toLowerCase();\n if (await this.#deleteEventRows(tx, id)) {\n n += 1;\n }\n tombstones.put({ key: `id:${id}`, type: \"id\" } satisfies Tombstone);\n tombstones.delete(`pending:${id}`);\n this.#deletion.ids.add(id);\n this.#deletion.pending.delete(id);\n }\n await done;\n return n;\n }\n\n async clear(): Promise<void> {\n return this.#enqueueWrite(async () => this.#clearLocked());\n }\n\n async #clearLocked(): Promise<void> {\n const db = await this.#ensure();\n const stores = [...WRITE_STORES, OUTBOX_BOUNDS];\n const tx = db.transaction(stores, \"readwrite\");\n const done = txDone(tx);\n for (const name of stores) {\n tx.objectStore(name).clear();\n }\n this.#deletion.clear();\n this.#replaceable.clear();\n await done;\n }\n\n close(): void {\n this.#db?.close();\n this.#db = undefined;\n }\n\n async #deleteEventRows(tx: IDBTransactionLike, id: string): Promise<boolean> {\n const events = tx.objectStore(EVENTS);\n const event = await reqOf<Event | undefined>(events.get(id));\n if (!event) {\n return false;\n }\n const addr = eventAddress(event);\n let row: AddressRow | undefined;\n if (addr !== undefined) {\n row = await reqOf<AddressRow | undefined>(tx.objectStore(ADDRESSES).get(addr));\n if (this.#replaceable.get(addr) === event.id) {\n this.#replaceable.delete(addr);\n }\n }\n deleteStoredEvent(tx, event, row);\n return true;\n }\n\n async #deriveOutboxBound(pubkey: string, kind: number): Promise<OutboxBound | undefined> {\n const db = await this.#ensure();\n const pk = pubkey.toLowerCase();\n const tx = db.transaction(EVENTS, \"readonly\");\n const done = txDone(tx);\n const index = tx.objectStore(EVENTS).index(\"kind_pubkey_created_at\");\n const range = prefixRange([kind, pk]);\n const filter: Filter = { authors: [pk], kinds: [kind] };\n let oldest: number | undefined;\n let newest: number | undefined;\n const firstLive = async (\n direction: IDBCursorDirectionLike,\n assign: (createdAt: number) => void,\n ) =>\n walkCursor(index, range, direction, (cursor) => {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- index cursor values are stored Event rows\n const event = cursor.value as Event;\n if (!this.#acceptHit(filter, event)) {\n return false;\n }\n assign(event.created_at);\n return true;\n });\n await Promise.all([\n firstLive(\"next\", (createdAt) => {\n oldest = createdAt;\n }),\n firstLive(\"prev\", (createdAt) => {\n newest = createdAt;\n }),\n ]);\n await done;\n if (oldest === undefined || newest === undefined) {\n return undefined;\n }\n return { oldest, newest };\n }\n}\n","// oxlint-disable no-await-in-loop -- SqlDriver statements are issued sequentially; drivers are not required to support overlapping statements on one connection\nimport { compareEventsDesc, itemCompare, sortEvents } from \"../core/event.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { matchFilter } from \"../core/filter.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { eventAddress, formatEventAddress, parseEventAddress } from \"../core/tag.ts\";\nimport { DeletionState } from \"./deletion.ts\";\nimport type { DeletionPlan } from \"./deletion.ts\";\nimport { toStorageError } from \"./error.ts\";\nimport { decidePut } from \"./put.ts\";\nimport type { PutDecision, PutLookup } from \"./put.ts\";\nimport type { EventStore, NegentropyItem, OutboxBound, PutResult } from \"./types.ts\";\n\n/** Value bindable to a SQLite statement parameter. */\n// oxlint-disable-next-line typescript/no-restricted-types -- SQL NULL is a first-class bind value\nexport type SqlValue = string | number | null | Uint8Array;\n\n/**\n * Minimal async SQLite driver surface.\n *\n * `transaction` must serialize `fn` exclusively — no interleaved statements from other callers may\n * run while the callback transaction is active. It commits when `fn` resolves and rolls back when\n * `fn` rejects.\n *\n * The store always issues statements sequentially — a driver never sees a second `exec`/`run`/`all`\n * start before the previous one settles, inside or outside a transaction — so drivers need not\n * support overlapping statements on one connection.\n *\n * `expo-sqlite` maps onto this interface without changes to query code. Note that\n * `withExclusiveTransactionAsync` resolves `void` and statements inside the exclusive transaction\n * must run on the `txn` connection passed to its callback, not on `db` — the result is captured\n * out-of-band:\n *\n * ```ts\n * const toDriver = (db: SQLiteDatabase): SqlDriver => ({\n * exec: (sql) => db.execAsync(sql),\n * run: async (sql, params = []) => ({\n * changes: (await db.runAsync(sql, [...params])).changes,\n * }),\n * all: (sql, params = []) => db.getAllAsync(sql, [...params]),\n * transaction: async (fn) => {\n * let result!: Awaited<ReturnType<typeof fn>>;\n * await db.withExclusiveTransactionAsync(async (txn) => {\n * result = await fn(toDriver(txn));\n * });\n * return result;\n * },\n * });\n * ```\n */\nexport type SqlDriver = {\n /** Execute SQL without parameters (DDL, PRAGMA, multi-statement batches). */\n exec: (sql: string) => Promise<void>;\n /** Run a statement; resolves with the number of changed rows. */\n run: (sql: string, params?: ReadonlyArray<SqlValue>) => Promise<{ changes: number }>;\n /** Run a query; resolves with all result rows. */\n all: <Row>(sql: string, params?: ReadonlyArray<SqlValue>) => Promise<Row[]>;\n /** Run `fn` inside an exclusive transaction; commit on resolve, roll back on reject. */\n transaction: <T>(fn: (tx: SqlDriver) => Promise<T>) => Promise<T>;\n};\n\n/** Bound values per `IN (...)` list; stays below every host's variable limit. */\nconst IN_CHUNK = 500;\nconst SCHEMA_VERSION = 1;\n\nconst SCHEMA_SQL = `\nCREATE TABLE IF NOT EXISTS events (\n id TEXT PRIMARY KEY,\n pubkey TEXT NOT NULL,\n kind INTEGER NOT NULL,\n created_at INTEGER NOT NULL,\n content TEXT NOT NULL,\n tags TEXT NOT NULL,\n sig TEXT NOT NULL,\n address TEXT\n);\nCREATE UNIQUE INDEX IF NOT EXISTS events_address\n ON events(address) WHERE address IS NOT NULL;\nCREATE INDEX IF NOT EXISTS events_kind_created ON events(kind, created_at DESC, id);\nCREATE INDEX IF NOT EXISTS events_pubkey_kind_created\n ON events(pubkey, kind, created_at DESC, id);\nCREATE INDEX IF NOT EXISTS events_pubkey_created ON events(pubkey, created_at DESC, id);\nCREATE INDEX IF NOT EXISTS events_created ON events(created_at DESC, id);\nCREATE TABLE IF NOT EXISTS tags (\n event_id TEXT NOT NULL REFERENCES events(id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n value TEXT NOT NULL,\n created_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS tags_name_value_created\n ON tags(name, value, created_at DESC);\nCREATE TABLE IF NOT EXISTS tombstones (\n kind TEXT NOT NULL CHECK(kind IN ('id', 'pending', 'coord')),\n key TEXT NOT NULL,\n pubkey TEXT,\n until INTEGER,\n PRIMARY KEY(kind, key)\n);\nCREATE TABLE IF NOT EXISTS outbox_bounds (\n pubkey TEXT NOT NULL,\n kind INTEGER NOT NULL,\n oldest INTEGER NOT NULL,\n newest INTEGER NOT NULL,\n PRIMARY KEY(pubkey, kind)\n);\n`;\n\ntype EventRow = {\n id: string;\n pubkey: string;\n kind: number;\n created_at: number;\n content: string;\n tags: string;\n sig: string;\n};\n\ntype TombstoneKind = \"id\" | \"pending\" | \"coord\";\n\ntype TombstoneRow = {\n kind: TombstoneKind;\n key: string;\n // oxlint-disable-next-line typescript/no-restricted-types -- nullable SQL column\n pubkey: string | null;\n // oxlint-disable-next-line typescript/no-restricted-types -- nullable SQL column\n until: number | null;\n};\n\nfunction rowToEvent(row: EventRow): Event {\n // Key order mirrors finalizeEvent's canonical construction so a round-trip\n // through the store JSON-equals the originally signed object.\n return {\n kind: row.kind,\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the tags column is written by #insertEvent as JSON.stringify(event.tags)\n tags: JSON.parse(row.tags) as string[][],\n content: row.content,\n created_at: row.created_at,\n pubkey: row.pubkey,\n id: row.id,\n sig: row.sig,\n };\n}\n\nfunction chunkValues<T>(values: ReadonlyArray<T>): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < values.length; i += IN_CHUNK) {\n chunks.push(values.slice(i, i + IN_CHUNK));\n }\n return chunks;\n}\n\nfunction inClause(column: string, count: number): string {\n return `${column} IN (${Array.from({ length: count }, () => \"?\").join(\",\")})`;\n}\n\n/** One query variant of a filter: WHERE terms and bound params. */\ntype FilterPlan = {\n wheres: string[];\n params: SqlValue[];\n};\n\n/**\n * Compile one NIP-01 filter into query variants. `IN` lists are chunked so a variant never binds\n * more than {@link IN_CHUNK} values per list; variants are the cartesian product of chunks across\n * constrained fields. `deferred` means a `#<multi-char>` tag term cannot use the tags index (only\n * single-letter tag names are indexed) and must be checked with `matchFilter` before `limit`.\n * Returns `undefined` when an empty list field makes the filter match nothing.\n */\nfunction compileFilter(filter: Filter): { plans: FilterPlan[]; deferred: boolean } | undefined {\n const variants: FilterPlan[][] = [];\n const pushIn = (values: ReadonlyArray<SqlValue>, clause: (n: number) => string) => {\n variants.push(\n chunkValues(values).map((chunk) => ({\n wheres: [clause(chunk.length)],\n params: [...chunk],\n })),\n );\n };\n\n if (filter.ids) {\n if (filter.ids.length === 0) {\n return undefined;\n }\n pushIn(\n filter.ids.map((id) => id.toLowerCase()),\n (n) => inClause(\"id\", n),\n );\n }\n if (filter.authors) {\n if (filter.authors.length === 0) {\n return undefined;\n }\n pushIn(\n filter.authors.map((pk) => pk.toLowerCase()),\n (n) => inClause(\"pubkey\", n),\n );\n }\n if (filter.kinds) {\n if (filter.kinds.length === 0) {\n return undefined;\n }\n pushIn(filter.kinds, (n) => inClause(\"kind\", n));\n }\n if (filter.since !== undefined) {\n variants.push([{ wheres: [\"created_at >= ?\"], params: [filter.since] }]);\n }\n if (filter.until !== undefined) {\n variants.push([{ wheres: [\"created_at <= ?\"], params: [filter.until] }]);\n }\n\n let deferred = false;\n for (const key of Object.keys(filter)) {\n if (!key.startsWith(\"#\")) {\n continue;\n }\n const name = key.slice(1);\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- startsWith(\"#\") above guarantees the template key\n const values = filter[key as `#${string}`];\n if (!values) {\n continue;\n }\n if (name.length !== 1) {\n deferred = true;\n continue;\n }\n if (values.length === 0) {\n return undefined;\n }\n const normalized = name === \"e\" || name === \"p\" ? values.map((v) => v.toLowerCase()) : values;\n variants.push(\n chunkValues(normalized).map((chunk) => ({\n wheres: [\n `EXISTS (SELECT 1 FROM tags t WHERE t.event_id = events.id ` +\n `AND t.name = ? AND ${inClause(\"t.value\", chunk.length)})`,\n ],\n params: [name, ...chunk],\n })),\n );\n }\n\n const plans: FilterPlan[] = [{ wheres: [], params: [] }];\n for (const field of variants) {\n const next: FilterPlan[] = [];\n for (const plan of plans) {\n for (const variant of field) {\n next.push({\n wheres: [...plan.wheres, ...variant.wheres],\n params: [...plan.params, ...variant.params],\n });\n }\n }\n plans.length = 0;\n plans.push(...next);\n }\n return { plans, deferred };\n}\n\n/**\n * SQLite-backed {@link EventStore} for React Native (`expo-sqlite`, `op-sqlite`) and desktop\n * runtimes. Same semantics as {@link MemoryEventStore} and {@link IndexedDbEventStore}: identical\n * `decidePut` insertion policy, NIP-09 tombstones, and filter handling, with the database — not\n * in-memory caches — as the source of truth.\n *\n * Construct via {@link SqliteEventStore.open}; writes are serialized and run inside an exclusive\n * driver transaction.\n */\nexport class SqliteEventStore implements EventStore {\n readonly #driver: SqlDriver;\n #writeTail: Promise<void> = Promise.resolve();\n\n private constructor(driver: SqlDriver) {\n this.#driver = driver;\n }\n\n /** Create the schema on first use and return a ready store. */\n static async open(driver: SqlDriver): Promise<SqliteEventStore> {\n const store = new SqliteEventStore(driver);\n await store.#migrate();\n return store;\n }\n\n async #migrate(): Promise<void> {\n try {\n await this.#driver.exec(\"PRAGMA foreign_keys = ON\");\n const rows = await this.#driver.all<{ user_version: number }>(\"PRAGMA user_version\");\n const version = rows[0]?.user_version ?? 0;\n if (version === 0) {\n await this.#driver.exec(SCHEMA_SQL);\n await this.#driver.exec(`PRAGMA user_version = ${SCHEMA_VERSION}`);\n } else if (version !== SCHEMA_VERSION) {\n throw toStorageError(new Error(`unsupported sqlite event store schema version ${version}`));\n }\n } catch (error) {\n throw toStorageError(error);\n }\n }\n\n async #enqueueWrite<T>(op: () => Promise<T>): Promise<T> {\n const tail = this.#writeTail;\n const result = (async (): Promise<T> => {\n await tail;\n return op();\n })();\n this.#writeTail = (async (): Promise<void> => {\n try {\n await result;\n } catch {\n // a failed write must not wedge later writes\n }\n })();\n return result;\n }\n\n async put(event: Event): Promise<PutResult> {\n const results = await this.putMany([event]);\n const result = results.at(0);\n if (result === undefined) {\n throw toStorageError(new Error(\"putMany returned no result\"));\n }\n return result;\n }\n\n async putMany(events: ReadonlyArray<Event>): Promise<PutResult[]> {\n if (events.length === 0) {\n return [];\n }\n return this.#enqueueWrite(async () => {\n try {\n return await this.#driver.transaction(async (tx) => this.#putAllInTx(tx, events));\n } catch (error) {\n throw toStorageError(error);\n }\n });\n }\n\n async #putAllInTx(tx: SqlDriver, batch: ReadonlyArray<Event>): Promise<PutResult[]> {\n const results: PutResult[] = [];\n for (const event of batch) {\n const lookup = await this.#buildLookup(tx, event);\n const decision = decidePut(event, lookup);\n await this.#applyDecision(tx, decision);\n results.push(decision.result);\n }\n return results;\n }\n\n async #buildLookup(tx: SqlDriver, event: Event): Promise<PutLookup> {\n const byId = new Map<string, Event>();\n const idList = [event.id];\n if (event.kind === Kind.EventDeletion) {\n for (const tag of event.tags) {\n if (tag[0] === \"e\" && tag[1] !== undefined && tag[1] !== \"\") {\n idList.push(tag[1].toLowerCase());\n }\n }\n }\n for (const row of await this.#eventRowsByIds(tx, idList)) {\n byId.set(row.id, row);\n }\n\n const byAddress = new Map<string, { id: string; created_at: number }>();\n const addressList: string[] = [];\n const ownAddress = eventAddress(event);\n if (ownAddress !== undefined) {\n addressList.push(ownAddress);\n }\n if (event.kind === Kind.EventDeletion) {\n for (const tag of event.tags) {\n if (tag[0] !== \"a\" || tag[1] === undefined || tag[1] === \"\") {\n continue;\n }\n const coord = parseEventAddress(tag[1]);\n if (coord === undefined) {\n continue;\n }\n const key = formatEventAddress(coord.kind, coord.pubkey, coord.identifier);\n if (!addressList.includes(key)) {\n addressList.push(key);\n }\n }\n }\n const addressRows: Array<{ address: string; id: string; created_at: number }> = [];\n for (const chunk of chunkValues(addressList)) {\n addressRows.push(\n ...(await tx.all<{ address: string; id: string; created_at: number }>(\n `SELECT address, id, created_at FROM events\n WHERE ${inClause(\"address\", chunk.length)}`,\n chunk,\n )),\n );\n }\n for (const row of addressRows.flat()) {\n byAddress.set(row.address, { id: row.id, created_at: row.created_at });\n }\n\n const deletion = new DeletionState();\n const tombstoneRows = await tx.all<TombstoneRow>(\n ownAddress === undefined\n ? `SELECT kind, key, pubkey, until FROM tombstones\n WHERE kind IN ('id', 'pending') AND key = ?`\n : `SELECT kind, key, pubkey, until FROM tombstones\n WHERE (kind IN ('id', 'pending') AND key = ?)\n OR (kind = 'coord' AND key = ?)`,\n ownAddress === undefined ? [event.id] : [event.id, ownAddress],\n );\n for (const row of tombstoneRows) {\n if (row.kind === \"id\") {\n deletion.ids.add(row.key);\n } else if (row.kind === \"pending\" && row.pubkey !== null) {\n deletion.pending.set(row.key, row.pubkey);\n } else if (row.kind === \"coord\" && row.until !== null) {\n deletion.coordinates.set(row.key, row.until);\n }\n }\n\n return {\n deletion,\n getById: (id) => byId.get(id),\n getReplaceable: (address) => byAddress.get(address),\n };\n }\n\n async #eventRowsByIds(tx: SqlDriver, ids: ReadonlyArray<string>): Promise<Event[]> {\n const events: Event[] = [];\n for (const chunk of chunkValues(ids)) {\n const rows = await tx.all<EventRow>(\n `SELECT id, pubkey, kind, created_at, content, tags, sig FROM events\n WHERE ${inClause(\"id\", chunk.length)}`,\n chunk,\n );\n for (const row of rows) {\n events.push(rowToEvent(row));\n }\n }\n return events;\n }\n\n async #applyDecision(tx: SqlDriver, d: PutDecision): Promise<void> {\n switch (d.action) {\n case \"skip\":\n return;\n case \"tombstone\":\n await this.#putTombstone(tx, \"id\", d.event.id);\n await this.#deleteTombstone(tx, \"pending\", d.event.id);\n return;\n case \"delete\":\n await this.#persistPlan(tx, d.event.id, d.plan, d.coordIds);\n for (const id of [...d.plan.removeIds, ...d.coordIds]) {\n await this.#deleteEvent(tx, id);\n }\n await this.#insertEvent(tx, d.event);\n return;\n case \"insert\":\n if (d.replaceId !== undefined) {\n await this.#deleteEvent(tx, d.replaceId);\n }\n await this.#insertEvent(tx, d.event);\n }\n }\n\n /** Persist a deletion plan's tombstones, mirroring `DeletionState.absorb`. */\n async #persistPlan(\n tx: SqlDriver,\n deletionId: string,\n plan: DeletionPlan,\n coordIds: ReadonlyArray<string>,\n ): Promise<void> {\n await this.#deleteTombstone(tx, \"pending\", deletionId);\n for (const id of [...plan.removeIds, ...coordIds]) {\n await this.#putTombstone(tx, \"id\", id);\n await this.#deleteTombstone(tx, \"pending\", id);\n }\n for (const p of plan.pendingIds) {\n const covered = await tx.all<{ found: number }>(\n `SELECT 1 AS found FROM tombstones WHERE kind = 'id' AND key = ?`,\n [p.id],\n );\n if (covered.length === 0) {\n await tx.run(\n `INSERT OR REPLACE INTO tombstones (kind, key, pubkey) VALUES ('pending', ?, ?)`,\n [p.id, p.pubkey],\n );\n }\n }\n for (const c of plan.coordinates) {\n await tx.run(\n `INSERT INTO tombstones (kind, key, until) VALUES ('coord', ?, ?)\n ON CONFLICT(kind, key)\n DO UPDATE SET until = MAX(tombstones.until, excluded.until)`,\n [c.key, c.until],\n );\n }\n }\n\n async #putTombstone(tx: SqlDriver, kind: TombstoneKind, key: string): Promise<void> {\n await tx.run(`INSERT OR REPLACE INTO tombstones (kind, key) VALUES (?, ?)`, [kind, key]);\n }\n\n async #deleteTombstone(tx: SqlDriver, kind: TombstoneKind, key: string): Promise<void> {\n await tx.run(`DELETE FROM tombstones WHERE kind = ? AND key = ?`, [kind, key]);\n }\n\n async #insertEvent(tx: SqlDriver, event: Event): Promise<void> {\n await tx.run(\n `INSERT INTO events (id, pubkey, kind, created_at, content, tags, sig, address)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n event.id,\n event.pubkey,\n event.kind,\n event.created_at,\n event.content,\n JSON.stringify(event.tags),\n event.sig,\n eventAddress(event) ?? null,\n ],\n );\n for (const tag of event.tags) {\n if (tag[0] === undefined || tag[1] === undefined || tag[0].length !== 1) {\n continue;\n }\n await tx.run(`INSERT INTO tags (event_id, name, value, created_at) VALUES (?, ?, ?, ?)`, [\n event.id,\n tag[0],\n tag[0] === \"e\" || tag[0] === \"p\" ? tag[1].toLowerCase() : tag[1],\n event.created_at,\n ]);\n }\n }\n\n async #deleteEvent(tx: SqlDriver, id: string): Promise<number> {\n const result = await tx.run(`DELETE FROM events WHERE id = ?`, [id]);\n return result.changes;\n }\n\n async get(id: string): Promise<Event | undefined> {\n try {\n const rows = await this.#driver.all<EventRow>(\n `SELECT id, pubkey, kind, created_at, content, tags, sig FROM events\n WHERE id = ?`,\n [id.toLowerCase()],\n );\n const row = rows.at(0);\n return row === undefined ? undefined : rowToEvent(row);\n } catch (error) {\n throw toStorageError(error);\n }\n }\n\n async query(filters: Filter[]): Promise<Event[]> {\n try {\n const perFilter: Event[][] = [];\n for (const filter of filters) {\n perFilter.push(await this.#filterRows(filter, \"*\"));\n }\n const seen = new Set<string>();\n const events: Event[] = [];\n for (const rows of perFilter) {\n for (const event of rows) {\n if (seen.has(event.id)) {\n continue;\n }\n seen.add(event.id);\n events.push(event);\n }\n }\n return sortEvents(events);\n } catch (error) {\n throw toStorageError(error);\n }\n }\n\n async count(filters: Filter[]): Promise<number> {\n try {\n const perFilter: NegentropyItem[][] = [];\n for (const filter of filters) {\n perFilter.push(await this.#filterRows(filter, \"id, created_at\"));\n }\n const seen = new Set<string>();\n for (const rows of perFilter) {\n for (const row of rows) {\n seen.add(row.id);\n }\n }\n return seen.size;\n } catch (error) {\n throw toStorageError(error);\n }\n }\n\n async negentropyItems(filter: Filter): Promise<NegentropyItem[]> {\n try {\n const items = await this.#filterRows(filter, \"id, created_at\");\n return [...items].sort(itemCompare);\n } catch (error) {\n throw toStorageError(error);\n }\n }\n\n /**\n * Rows matching one filter, deduped, newest-first, with `limit` applied. `select` is a trusted\n * column list for the projection. A filter with a non-indexed (multi-char) `#` tag term falls\n * back to a `matchFilter` pass and cannot push `limit` down.\n */\n async #filterRows(filter: Filter, select: \"*\"): Promise<Event[]>;\n async #filterRows(filter: Filter, select: \"id, created_at\"): Promise<NegentropyItem[]>;\n async #filterRows(\n filter: Filter,\n select: \"*\" | \"id, created_at\",\n ): Promise<Event[] | NegentropyItem[]> {\n if (filter.limit === 0) {\n return [];\n }\n const compiled = compileFilter(filter);\n if (compiled === undefined) {\n return [];\n }\n const { plans, deferred } = compiled;\n\n if (deferred) {\n // A multi-char #tag term bypasses the tags index — matchFilter decides and\n // `limit` cannot be pushed down.\n const merged = await this.#planRows<EventRow>(plans, \"*\", undefined);\n const matched = merged.map(rowToEvent).filter((event) => matchFilter(filter, event));\n const limited = filter.limit === undefined ? matched : matched.slice(0, filter.limit);\n return select === \"*\"\n ? limited\n : limited.map((e) => ({ id: e.id, created_at: e.created_at }));\n }\n\n if (select === \"*\") {\n const merged = await this.#planRows<EventRow>(plans, \"*\", filter.limit);\n return merged.map(rowToEvent);\n }\n return this.#planRows<NegentropyItem>(plans, \"id, created_at\", filter.limit);\n }\n\n /** Rows for the compiled plans of one filter, deduped newest-first, `limit` applied. */\n async #planRows<Row extends { id: string; created_at: number }>(\n plans: ReadonlyArray<FilterPlan>,\n select: string,\n limit: number | undefined,\n ): Promise<Row[]> {\n const tail = ` ORDER BY created_at DESC, id ASC${limit === undefined ? \"\" : \" LIMIT ?\"}`;\n const perPlan: Row[][] = [];\n for (const plan of plans) {\n const where = plan.wheres.length > 0 ? ` WHERE ${plan.wheres.join(\" AND \")}` : \"\";\n const params = limit === undefined ? plan.params : [...plan.params, limit];\n perPlan.push(\n await this.#driver.all<Row>(`SELECT ${select} FROM events${where}${tail}`, params),\n );\n }\n const sorted = perPlan.flat().sort(compareEventsDesc);\n const seen = new Set<string>();\n const merged: Row[] = [];\n for (const row of sorted) {\n if (seen.has(row.id)) {\n continue;\n }\n seen.add(row.id);\n merged.push(row);\n }\n return limit === undefined ? merged : merged.slice(0, limit);\n }\n\n async getOutboxBound(pubkey: string, kind: number): Promise<OutboxBound | undefined> {\n const pk = pubkey.toLowerCase();\n try {\n const rows = await this.#driver.all<{ oldest: number; newest: number }>(\n `SELECT oldest, newest FROM outbox_bounds\n WHERE pubkey = ? AND kind = ?`,\n [pk, kind],\n );\n const row = rows.at(0);\n if (row !== undefined) {\n return { oldest: row.oldest, newest: row.newest };\n }\n const derived = await this.#driver.all<{\n // oxlint-disable-next-line typescript/no-restricted-types -- MIN() of an empty set is SQL NULL\n oldest: number | null;\n // oxlint-disable-next-line typescript/no-restricted-types -- MAX() of an empty set is SQL NULL\n newest: number | null;\n }>(\n `SELECT MIN(created_at) AS oldest, MAX(created_at) AS newest\n FROM events WHERE pubkey = ? AND kind = ?`,\n [pk, kind],\n );\n const d = derived.at(0);\n if (d === undefined || d.oldest === null || d.newest === null) {\n return undefined;\n }\n return { oldest: d.oldest, newest: d.newest };\n } catch (error) {\n throw toStorageError(error);\n }\n }\n\n async setOutboxBound(pubkey: string, kind: number, bound: OutboxBound): Promise<void> {\n const pk = pubkey.toLowerCase();\n await this.#enqueueWrite(async () => {\n try {\n await this.#driver.transaction(async (tx) => {\n await tx.run(\n `INSERT OR REPLACE INTO outbox_bounds (pubkey, kind, oldest, newest)\n VALUES (?, ?, ?, ?)`,\n [pk, kind, bound.oldest, bound.newest],\n );\n });\n } catch (error) {\n throw toStorageError(error);\n }\n });\n }\n\n async remove(ids: string[]): Promise<number> {\n const lowered = ids.map((id) => id.toLowerCase());\n return this.#enqueueWrite(async () => {\n try {\n return await this.#driver.transaction(async (tx) => {\n let removed = 0;\n for (const id of lowered) {\n removed += await this.#deleteEvent(tx, id);\n await this.#putTombstone(tx, \"id\", id);\n await this.#deleteTombstone(tx, \"pending\", id);\n }\n return removed;\n });\n } catch (error) {\n throw toStorageError(error);\n }\n });\n }\n\n async clear(): Promise<void> {\n await this.#enqueueWrite(async () => {\n try {\n await this.#driver.transaction(async (tx) => {\n await tx.run(\"DELETE FROM tags\");\n await tx.run(\"DELETE FROM events\");\n await tx.run(\"DELETE FROM tombstones\");\n await tx.run(\"DELETE FROM outbox_bounds\");\n });\n } catch (error) {\n throw toStorageError(error);\n }\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"util-LfQirgDk.mjs","names":["bytesToHex","nobleBytesToHex","hexToBytes","nobleHexToBytes"],"sources":["../src/core/limits.ts","../src/core/error.ts","../src/core/util.ts"],"sourcesContent":["/** NIP-01 event id length in bytes. */\nexport const EVENT_ID_BYTES = 32;\n\n/** BIP-340 signature length in bytes. */\nexport const SIGNATURE_BYTES = 64;\n\n/** BIP-340 public key length in bytes (x-only). */\nexport const PUBLIC_KEY_BYTES = 32;\n\n/** BIP-340 secret key length in bytes. */\nexport const SECRET_KEY_BYTES = 32;\n\n/** NIP-01 subscription id max character length. */\nexport const SUBSCRIPTION_ID_MAX_CHARS = 64;\n","/** Base error for all @qntx/nostr failures. */\nexport class NostrError extends Error {\n override name = \"NostrError\";\n}\n\n/** Invalid hex encoding or length. */\nexport class HexError extends NostrError {\n override name = \"HexError\";\n}\n\n/** Invalid URL / relay URL. */\nexport class UrlError extends NostrError {\n override name = \"UrlError\";\n}\n\n/** Event shape / wire validation failure. */\nexport class EventValidationError extends NostrError {\n override name = \"EventValidationError\";\n}\n\n/** Cryptographic operation failure (keys, signatures). */\nexport class CryptoError extends NostrError {\n override name = \"CryptoError\";\n}\n\n/** Message parse / encode failure. */\nexport class MessageError extends NostrError {\n override name = \"MessageError\";\n}\n\n/** Wasm verify instance aborted; later verifies on that instance fail. */\nexport class WasmVerifyPoisonedError extends NostrError {\n override name = \"WasmVerifyPoisonedError\";\n}\n","import {\n bytesToHex as nobleBytesToHex,\n hexToBytes as nobleHexToBytes,\n} from \"@noble/hashes/utils.js\";\n\nimport { HexError, UrlError } from \"./error.ts\";\nimport { SECRET_KEY_BYTES } from \"./limits.ts\";\n\nexport const utf8Encoder: TextEncoder = new TextEncoder();\nexport const utf8Decoder: TextDecoder = new TextDecoder();\n\nconst HEX32_RE = /^[0-9a-f]{64}$/;\nconst HEX64_RE = /^[0-9a-f]{128}$/;\n\n/** Lowercase hex encode. */\nexport function bytesToHex(bytes: Uint8Array): string {\n return nobleBytesToHex(bytes);\n}\n\n/** Decode lowercase or mixed-case hex to bytes. */\nexport function hexToBytes(hex: string): Uint8Array {\n try {\n return nobleHexToBytes(hex);\n } catch (error) {\n throw new HexError(`invalid hex string of length ${hex.length}`, {\n cause: error instanceof Error ? error : undefined,\n });\n }\n}\n\n/** True when value is canonical NIP-01 lowercase hex of 32 bytes (64 chars). */\nexport function isHex32(value: string): boolean {\n return HEX32_RE.test(value);\n}\n\n/** True when value is canonical NIP-01 lowercase hex of 64 bytes (128 chars). */\nexport function isHex64(value: string): boolean {\n return HEX64_RE.test(value);\n}\n\n/** Caller input of any case: lowercases first, then requires canonical hex shape. */\nexport function assertHex32(value: string, label: string): string {\n const normalized = value.toLowerCase();\n if (!isHex32(normalized)) {\n throw new HexError(`invalid ${label}: expected 64-char hex`);\n }\n return normalized;\n}\n\nexport function assertByteLength(bytes: Uint8Array, expected: number, label: string): void {\n if (bytes.length !== expected) {\n throw new HexError(`invalid ${label} length: expected ${expected}, got ${bytes.length}`);\n }\n}\n\nexport function assertSecretKeyBytes(bytes: Uint8Array): void {\n assertByteLength(bytes, SECRET_KEY_BYTES, \"secret key\");\n}\n\n/** Structural guard: a plain record (not `null`, not an array). */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Normalize a relay URL to a stable form: `http:`/`https:` are rewritten to `ws:`/`wss:` (a bare\n * host gets `wss://`), any other scheme throws UrlError. The result has a lowercased host, the\n * default port removed, duplicate path slashes collapsed, a sorted query, and no fragment. `URL`\n * serialization keeps a trailing `/` on the root path (`wss://a.example/`).\n */\nexport function normalizeURL(url: string): string {\n try {\n let input = url;\n if (!input.includes(\"://\")) {\n input = `wss://${input}`;\n }\n const p = new URL(input);\n if (p.protocol === \"http:\") {\n p.protocol = \"ws:\";\n } else if (p.protocol === \"https:\") {\n p.protocol = \"wss:\";\n }\n if (p.protocol !== \"ws:\" && p.protocol !== \"wss:\") {\n throw new UrlError(`unsupported relay URL scheme: ${p.protocol}`);\n }\n p.pathname = p.pathname.replaceAll(/\\/+/g, \"/\");\n if (p.pathname.endsWith(\"/\") && p.pathname.length > 1) {\n p.pathname = p.pathname.slice(0, -1);\n }\n if ((p.port === \"80\" && p.protocol === \"ws:\") || (p.port === \"443\" && p.protocol === \"wss:\")) {\n p.port = \"\";\n }\n p.searchParams.sort();\n p.hash = \"\";\n return p.toString();\n } catch (error) {\n if (error instanceof UrlError) {\n throw error;\n }\n throw new UrlError(`invalid URL: ${url}`, {\n cause: error instanceof Error ? error : undefined,\n });\n }\n}\n"],"mappings":";;;AACA,MAAa,iBAAiB;;AAG9B,MAAa,kBAAkB;;AAG/B,MAAa,mBAAmB;;AAGhC,MAAa,mBAAmB;;AAGhC,MAAa,4BAA4B;;;;ACZzC,IAAa,aAAb,cAAgC,MAAM;CACpC,OAAgB;AAClB;;AAGA,IAAa,WAAb,cAA8B,WAAW;CACvC,OAAgB;AAClB;;AAGA,IAAa,WAAb,cAA8B,WAAW;CACvC,OAAgB;AAClB;;AAGA,IAAa,uBAAb,cAA0C,WAAW;CACnD,OAAgB;AAClB;;AAGA,IAAa,cAAb,cAAiC,WAAW;CAC1C,OAAgB;AAClB;;AAGA,IAAa,eAAb,cAAkC,WAAW;CAC3C,OAAgB;AAClB;;AAGA,IAAa,0BAAb,cAA6C,WAAW;CACtD,OAAgB;AAClB;;;ACzBA,MAAa,cAA2B,IAAI,YAAY;AACxD,MAAa,cAA2B,IAAI,YAAY;AAExD,MAAM,WAAW;AACjB,MAAM,WAAW;;AAGjB,SAAgBA,aAAW,OAA2B;CACpD,OAAOC,WAAgB,KAAK;AAC9B;;AAGA,SAAgBC,aAAW,KAAyB;CAClD,IAAI;EACF,OAAOC,WAAgB,GAAG;CAC5B,SAAS,OAAO;EACd,MAAM,IAAI,SAAS,gCAAgC,IAAI,UAAU,EAC/D,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;AACF;;AAGA,SAAgB,QAAQ,OAAwB;CAC9C,OAAO,SAAS,KAAK,KAAK;AAC5B;;AAGA,SAAgB,QAAQ,OAAwB;CAC9C,OAAO,SAAS,KAAK,KAAK;AAC5B;;AAGA,SAAgB,YAAY,OAAe,OAAuB;CAChE,MAAM,aAAa,MAAM,YAAY;CACrC,IAAI,CAAC,QAAQ,UAAU,GACrB,MAAM,IAAI,SAAS,WAAW,MAAM,uBAAuB;CAE7D,OAAO;AACT;AAEA,SAAgB,iBAAiB,OAAmB,UAAkB,OAAqB;CACzF,IAAI,MAAM,WAAW,UACnB,MAAM,IAAI,SAAS,WAAW,MAAM,oBAAoB,SAAS,QAAQ,MAAM,QAAQ;AAE3F;AAEA,SAAgB,qBAAqB,OAAyB;CAC5D,iBAAiB,OAAA,IAAyB,YAAY;AACxD;;AAGA,SAAgB,SAAS,OAAkD;CACzE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;;;;;;;AAQA,SAAgB,aAAa,KAAqB;CAChD,IAAI;EACF,IAAI,QAAQ;EACZ,IAAI,CAAC,MAAM,SAAS,KAAK,GACvB,QAAQ,SAAS;EAEnB,MAAM,IAAI,IAAI,IAAI,KAAK;EACvB,IAAI,EAAE,aAAa,SACjB,EAAE,WAAW;OACR,IAAI,EAAE,aAAa,UACxB,EAAE,WAAW;EAEf,IAAI,EAAE,aAAa,SAAS,EAAE,aAAa,QACzC,MAAM,IAAI,SAAS,iCAAiC,EAAE,UAAU;EAElE,EAAE,WAAW,EAAE,SAAS,WAAW,QAAQ,GAAG;EAC9C,IAAI,EAAE,SAAS,SAAS,GAAG,KAAK,EAAE,SAAS,SAAS,GAClD,EAAE,WAAW,EAAE,SAAS,MAAM,GAAG,EAAE;EAErC,IAAK,EAAE,SAAS,QAAQ,EAAE,aAAa,SAAW,EAAE,SAAS,SAAS,EAAE,aAAa,QACnF,EAAE,OAAO;EAEX,EAAE,aAAa,KAAK;EACpB,EAAE,OAAO;EACT,OAAO,EAAE,SAAS;CACpB,SAAS,OAAO;EACd,IAAI,iBAAiB,UACnB,MAAM;EAER,MAAM,IAAI,SAAS,gBAAgB,OAAO,EACxC,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;AACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"verifier-t5WoWbtY.mjs","names":[],"sources":["../src/core/verifier.ts"],"sourcesContent":["import { schnorr } from \"@noble/curves/secp256k1.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\n\nimport {\n isMarkedFailed,\n isMarkedVerified,\n markUnverified,\n markVerified,\n serializeEvent,\n validateSignedEvent,\n} from \"./event.ts\";\nimport type { Event } from \"./event.ts\";\nimport { hexToBytes, utf8Encoder } from \"./util.ts\";\n\n/**\n * Verification backend: return true iff `sha256(serializedUtf8)` equals `id` and `sig` is a valid\n * BIP-340 signature of `id` by the x-only `pubkey`. Inputs are raw bytes (`id`/`pubkey` 32, `sig`\n * 64). Exceptions propagate through the built verifier unmarked — a throwing backend is broken and\n * must surface.\n */\nexport type SerializedEventVerifier = (\n serializedUtf8: Uint8Array,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n) => boolean;\n\n/**\n * Build an event verifier with exactly the semantics of {@link verifyEvent}, delegating the hash and\n * signature check to `backend`. Verified/failed results share `verifyEvent`'s WeakSet caches, so\n * verifiers built this way and `verifyEvent` agree on (and reuse) each other's results.\n */\nexport function createEventVerifier(backend: SerializedEventVerifier): (event: Event) => boolean {\n return (event) => {\n if (isMarkedVerified(event)) {\n return true;\n }\n if (isMarkedFailed(event)) {\n return false;\n }\n if (!validateSignedEvent(event)) {\n markUnverified(event);\n return false;\n }\n const ok = backend(\n utf8Encoder.encode(serializeEvent(event)),\n hexToBytes(event.id),\n hexToBytes(event.pubkey),\n hexToBytes(event.sig),\n );\n if (ok) {\n markVerified(event);\n } else {\n markUnverified(event);\n }\n return ok;\n };\n}\n\nconst nobleSerializedVerify: SerializedEventVerifier = (serializedUtf8, id, pubkey, sig) => {\n try {\n const digest = sha256(serializedUtf8);\n if (!digest.every((byte, i) => byte === id[i])) {\n return false;\n }\n return schnorr.verify(sig, id, pubkey);\n } catch {\n return false;\n }\n};\n\n/** Verify event id and BIP-340 signature. Uses WeakSet cache (does not mutate the event). */\nexport const verifyEvent: (event: Event) => boolean = createEventVerifier(nobleSerializedVerify);\n"],"mappings":";;;;;;;;;;AAgCA,SAAgB,oBAAoB,SAA6D;CAC/F,QAAQ,UAAU;EAChB,IAAI,iBAAiB,KAAK,GACxB,OAAO;EAET,IAAI,eAAe,KAAK,GACtB,OAAO;EAET,IAAI,CAAC,oBAAoB,KAAK,GAAG;GAC/B,eAAe,KAAK;GACpB,OAAO;EACT;EACA,MAAM,KAAK,QACT,YAAY,OAAO,eAAe,KAAK,CAAC,GACxC,WAAW,MAAM,EAAE,GACnB,WAAW,MAAM,MAAM,GACvB,WAAW,MAAM,GAAG,CACtB;EACA,IAAI,IACF,aAAa,KAAK;OAElB,eAAe,KAAK;EAEtB,OAAO;CACT;AACF;AAEA,MAAM,yBAAkD,gBAAgB,IAAI,QAAQ,QAAQ;CAC1F,IAAI;EAEF,IAAI,CADW,OAAO,cACZ,CAAC,CAAC,OAAO,MAAM,MAAM,SAAS,GAAG,EAAE,GAC3C,OAAO;EAET,OAAO,QAAQ,OAAO,KAAK,IAAI,MAAM;CACvC,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,MAAa,cAAyC,oBAAoB,qBAAqB"}
|