@qntx/nostr 0.6.0 → 0.8.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.
Files changed (183) hide show
  1. package/dist/{abort-Ba8tDrOI.mjs → abort-DBq2STPj.mjs} +13 -6
  2. package/dist/abort-DBq2STPj.mjs.map +1 -0
  3. package/dist/{builder-sSflu6nB.d.mts → builder-CTwkB2zM.d.mts} +14 -15
  4. package/dist/{builder-9tjI6WQ3.mjs → builder-aNU2gGFP.mjs} +11 -17
  5. package/dist/builder-aNU2gGFP.mjs.map +1 -0
  6. package/dist/{client-CadO5fAX.mjs → client-Cj2lUaVh.mjs} +141 -353
  7. package/dist/client-Cj2lUaVh.mjs.map +1 -0
  8. package/dist/client.d.mts +2 -2
  9. package/dist/client.mjs +2 -2
  10. package/dist/collections-DtwMc1NU.mjs +33 -0
  11. package/dist/collections-DtwMc1NU.mjs.map +1 -0
  12. package/dist/core.d.mts +9 -8
  13. package/dist/core.mjs +7 -7
  14. package/dist/emitter-DkCY6NkU.mjs +32 -0
  15. package/dist/emitter-DkCY6NkU.mjs.map +1 -0
  16. package/dist/{error-BW7re1UN.d.mts → error-BD_0QFKT.d.mts} +2 -2
  17. package/dist/{error-DrEIM7FO.mjs → error-BILMe3Gr.mjs} +2 -2
  18. package/dist/{error-DrEIM7FO.mjs.map → error-BILMe3Gr.mjs.map} +1 -1
  19. package/dist/{error-CR8Urc84.d.mts → error-BquXDmIN.d.mts} +4 -4
  20. package/dist/{error-B__OMBRY.mjs → error-DlhGW4mJ.mjs} +2 -2
  21. package/dist/{error-B__OMBRY.mjs.map → error-DlhGW4mJ.mjs.map} +1 -1
  22. package/dist/{event-D8Nfoh4K.mjs → event-CHf47dPM.mjs} +16 -10
  23. package/dist/event-CHf47dPM.mjs.map +1 -0
  24. package/dist/{event-fU3Y01PR.d.mts → event-DJeb_DIL.d.mts} +4 -2
  25. package/dist/{filter-C0IMByE3.d.mts → filter-jI_9htF7.d.mts} +2 -2
  26. package/dist/{gossip-oloDMWEj.mjs → gossip-DFxIBlsr.mjs} +23 -33
  27. package/dist/gossip-DFxIBlsr.mjs.map +1 -0
  28. package/dist/{gossip-DpIvkBuj.d.mts → gossip-OOeeFzG5.d.mts} +37 -28
  29. package/dist/gossip.d.mts +1 -1
  30. package/dist/gossip.mjs +1 -1
  31. package/dist/{index-D1bqFHKB.d.mts → index-BxCBX-8R.d.mts} +18 -18
  32. package/dist/{index-lvXeER5t.d.mts → index-CPVEyzNX.d.mts} +32 -50
  33. package/dist/{index-MxC28S4P.d.mts → index-C_w2WfVz.d.mts} +27 -29
  34. package/dist/{index-CNl2q67H.d.mts → index-Cl_1XdYP.d.mts} +111 -180
  35. package/dist/{index-CvkvqB16.d.mts → index-DU9tRFTZ.d.mts} +7 -20
  36. package/dist/{index-q4MDce_r.d.mts → index-DyKwQnbt.d.mts} +6 -8
  37. package/dist/index.d.mts +23 -21
  38. package/dist/index.mjs +21 -19
  39. package/dist/{instance-DQE8BoWt.mjs → instance-C62w3cEt.mjs} +26 -27
  40. package/dist/instance-C62w3cEt.mjs.map +1 -0
  41. package/dist/{instance-CIca-ynn.d.mts → instance-DjsL8h84.d.mts} +5 -5
  42. package/dist/{key-Cv4_pN3x.d.mts → key-D_5OgGT7.d.mts} +11 -7
  43. package/dist/{key-Bps-LqX7.mjs → key-DnVpchl6.mjs} +13 -9
  44. package/dist/key-DnVpchl6.mjs.map +1 -0
  45. package/dist/{loaders-o6YXB_Ri.mjs → loaders-BmPkQfzI.mjs} +73 -82
  46. package/dist/loaders-BmPkQfzI.mjs.map +1 -0
  47. package/dist/loaders.d.mts +3 -3
  48. package/dist/loaders.mjs +2 -2
  49. package/dist/{memory-C2EVx0_O.mjs → memory-CdJ07kub.mjs} +2 -2
  50. package/dist/memory-CdJ07kub.mjs.map +1 -0
  51. package/dist/{memory-index-Do1mz0WF.mjs → memory-index-Cbc83vOZ.mjs} +30 -247
  52. package/dist/memory-index-Cbc83vOZ.mjs.map +1 -0
  53. package/dist/message-oNr7t0SJ.mjs +194 -0
  54. package/dist/message-oNr7t0SJ.mjs.map +1 -0
  55. package/dist/{message-iGPIqnn0.d.mts → message-tclYCVNc.d.mts} +6 -6
  56. package/dist/{nip17-CKIufV98.d.mts → nip17-B5WgW4C4.d.mts} +6 -7
  57. package/dist/{nip19-BevCJZsp.d.mts → nip19-DpPN17bY.d.mts} +25 -31
  58. package/dist/{nip46-DDV4dC56.d.mts → nip46-BHaxC9TZ.d.mts} +19 -21
  59. package/dist/nip51-CR0Q8eym.d.mts +87 -0
  60. package/dist/{nip59-zQgdLcEr.d.mts → nip59-C-RWFQIv.d.mts} +11 -5
  61. package/dist/nip65-D1fbmhOg.d.mts +23 -0
  62. package/dist/{nip77-DH4RhS5r.d.mts → nip77-DzUwUxHq.d.mts} +8 -9
  63. package/dist/nips/blossom.d.mts +17 -13
  64. package/dist/nips/blossom.mjs +30 -9
  65. package/dist/nips/blossom.mjs.map +1 -1
  66. package/dist/nips/nip04.mjs +17 -7
  67. package/dist/nips/nip04.mjs.map +1 -1
  68. package/dist/nips/nip05.d.mts +9 -10
  69. package/dist/nips/nip05.mjs +1 -4
  70. package/dist/nips/nip05.mjs.map +1 -1
  71. package/dist/nips/nip10.d.mts +8 -8
  72. package/dist/nips/nip10.mjs +34 -32
  73. package/dist/nips/nip10.mjs.map +1 -1
  74. package/dist/nips/nip11.d.mts +28 -28
  75. package/dist/nips/nip11.mjs +4 -4
  76. package/dist/nips/nip11.mjs.map +1 -1
  77. package/dist/nips/nip13.d.mts +7 -7
  78. package/dist/nips/nip13.mjs +32 -10
  79. package/dist/nips/nip13.mjs.map +1 -1
  80. package/dist/nips/nip17.d.mts +1 -1
  81. package/dist/nips/nip17.mjs +16 -34
  82. package/dist/nips/nip17.mjs.map +1 -1
  83. package/dist/nips/nip19.d.mts +2 -2
  84. package/dist/nips/nip19.mjs +16 -28
  85. package/dist/nips/nip19.mjs.map +1 -1
  86. package/dist/nips/nip21.d.mts +9 -4
  87. package/dist/nips/nip21.mjs +15 -5
  88. package/dist/nips/nip21.mjs.map +1 -1
  89. package/dist/nips/nip27.d.mts +22 -22
  90. package/dist/nips/nip27.mjs +2 -2
  91. package/dist/nips/nip27.mjs.map +1 -1
  92. package/dist/nips/nip42.d.mts +1 -1
  93. package/dist/nips/nip42.mjs +2 -1
  94. package/dist/nips/nip42.mjs.map +1 -1
  95. package/dist/nips/nip44.mjs +2 -2
  96. package/dist/nips/nip44.mjs.map +1 -1
  97. package/dist/nips/nip46.d.mts +2 -2
  98. package/dist/nips/nip46.mjs +15 -19
  99. package/dist/nips/nip46.mjs.map +1 -1
  100. package/dist/nips/nip49.d.mts +9 -9
  101. package/dist/nips/nip49.mjs +4 -4
  102. package/dist/nips/nip49.mjs.map +1 -1
  103. package/dist/nips/nip51.d.mts +2 -86
  104. package/dist/nips/nip51.mjs +7 -23
  105. package/dist/nips/nip51.mjs.map +1 -1
  106. package/dist/nips/nip57.d.mts +25 -23
  107. package/dist/nips/nip57.mjs +12 -17
  108. package/dist/nips/nip57.mjs.map +1 -1
  109. package/dist/nips/nip59.d.mts +1 -1
  110. package/dist/nips/nip59.mjs +27 -19
  111. package/dist/nips/nip59.mjs.map +1 -1
  112. package/dist/nips/nip65.d.mts +2 -2
  113. package/dist/nips/nip65.mjs +14 -37
  114. package/dist/nips/nip65.mjs.map +1 -1
  115. package/dist/nips/nip77.d.mts +1 -1
  116. package/dist/nips/nip77.mjs +33 -32
  117. package/dist/nips/nip77.mjs.map +1 -1
  118. package/dist/nips/nip98.d.mts +5 -5
  119. package/dist/nips/nip98.mjs +8 -8
  120. package/dist/nips/nip98.mjs.map +1 -1
  121. package/dist/{pool-DBIYMtwy.d.mts → pool-CqQvUnsq.d.mts} +132 -108
  122. package/dist/pool-vlw74C4t.mjs +606 -0
  123. package/dist/pool-vlw74C4t.mjs.map +1 -0
  124. package/dist/put-w13ci3sC.mjs +187 -0
  125. package/dist/put-w13ci3sC.mjs.map +1 -0
  126. package/dist/{reactive-BfhPp9a3.mjs → reactive-Tz0otDIP.mjs} +125 -88
  127. package/dist/reactive-Tz0otDIP.mjs.map +1 -0
  128. package/dist/{reactive-7aqeip4A.d.mts → reactive-gGqUrzW8.d.mts} +20 -26
  129. package/dist/relay-quCs7MHD.mjs +1811 -0
  130. package/dist/relay-quCs7MHD.mjs.map +1 -0
  131. package/dist/relay.d.mts +4 -4
  132. package/dist/relay.mjs +3 -2
  133. package/dist/serial-Bzpw-xz3.mjs +21 -0
  134. package/dist/serial-Bzpw-xz3.mjs.map +1 -0
  135. package/dist/{signer-CYQutDzD.mjs → signer-poTGe6Er.mjs} +12 -14
  136. package/dist/signer-poTGe6Er.mjs.map +1 -0
  137. package/dist/signer.d.mts +2 -2
  138. package/dist/signer.mjs +2 -2
  139. package/dist/{storage-Dky3wuoH.mjs → storage-B5IXTVuS.mjs} +28 -51
  140. package/dist/storage-B5IXTVuS.mjs.map +1 -0
  141. package/dist/storage.d.mts +3 -3
  142. package/dist/storage.mjs +4 -4
  143. package/dist/store.d.mts +2 -2
  144. package/dist/store.mjs +1 -1
  145. package/dist/testing.d.mts +19 -19
  146. package/dist/testing.mjs +9 -9
  147. package/dist/testing.mjs.map +1 -1
  148. package/dist/{types-2pCUprRU.d.mts → types-BacwnDtU.d.mts} +10 -10
  149. package/dist/{types-CRQEk0Oi.d.mts → types-DNmiscfd.d.mts} +2 -2
  150. package/dist/{util-LfQirgDk.mjs → util-DMF9AIO0.mjs} +33 -7
  151. package/dist/util-DMF9AIO0.mjs.map +1 -0
  152. package/dist/{verifier-t5WoWbtY.mjs → verifier-B51Wkt5H.mjs} +3 -3
  153. package/dist/verifier-B51Wkt5H.mjs.map +1 -0
  154. package/dist/verifier-CgTFpgsC.d.mts +22 -0
  155. package/dist/wasm.browser.d.mts +3 -3
  156. package/dist/wasm.browser.mjs +3 -3
  157. package/dist/wasm.d.mts +3 -3
  158. package/dist/wasm.mjs +3 -3
  159. package/dist/wasm.mjs.map +1 -1
  160. package/package.json +5 -9
  161. package/dist/abort-Ba8tDrOI.mjs.map +0 -1
  162. package/dist/builder-9tjI6WQ3.mjs.map +0 -1
  163. package/dist/client-CadO5fAX.mjs.map +0 -1
  164. package/dist/event-D8Nfoh4K.mjs.map +0 -1
  165. package/dist/gossip-oloDMWEj.mjs.map +0 -1
  166. package/dist/instance-DQE8BoWt.mjs.map +0 -1
  167. package/dist/key-Bps-LqX7.mjs.map +0 -1
  168. package/dist/loaders-o6YXB_Ri.mjs.map +0 -1
  169. package/dist/memory-C2EVx0_O.mjs.map +0 -1
  170. package/dist/memory-index-Do1mz0WF.mjs.map +0 -1
  171. package/dist/message-DcY6Fi9o.mjs +0 -193
  172. package/dist/message-DcY6Fi9o.mjs.map +0 -1
  173. package/dist/nip65-CjgmdpPB.d.mts +0 -26
  174. package/dist/nips/nip96.d.mts +0 -40
  175. package/dist/nips/nip96.mjs +0 -118
  176. package/dist/nips/nip96.mjs.map +0 -1
  177. package/dist/pool-As20OGxB.mjs +0 -2166
  178. package/dist/pool-As20OGxB.mjs.map +0 -1
  179. package/dist/reactive-BfhPp9a3.mjs.map +0 -1
  180. package/dist/signer-CYQutDzD.mjs.map +0 -1
  181. package/dist/storage-Dky3wuoH.mjs.map +0 -1
  182. package/dist/util-LfQirgDk.mjs.map +0 -1
  183. package/dist/verifier-t5WoWbtY.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loaders-BmPkQfzI.mjs","names":[],"sources":["../src/loaders/context.ts","../src/loaders/event.ts","../src/loaders/lists.ts","../src/loaders/profile.ts","../src/loaders/dataloader.ts","../src/loaders/replaceable.ts","../src/loaders/outbox.ts","../src/loaders/index.ts"],"sourcesContent":["import type { Event } from \"../core/event.ts\";\nimport { nowSeconds } from \"../core/util.ts\";\nimport type { Pool } from \"../relay/pool.ts\";\nimport { DEFAULT_REQUEST_TIMEOUT_MS } from \"../relay/relay.ts\";\nimport type { ReactiveEventStore } from \"../store/reactive.ts\";\n\nexport type LoaderContextOptions = {\n pool: Pool;\n /** Fallback / discovery relays when no per-user routing is known. */\n relays: ReadonlyArray<string>;\n /** The reactive index loaders read from and feed fetched events into. */\n index: ReactiveEventStore;\n /**\n * Inbound-event sink for fetched events. Defaults to `index.add`; Client supplies its single\n * ingest path so loader fetches get gossip meta and persistence like every other inbound event.\n */\n ingest?: ((event: Event, relayUrl: string) => void) | undefined;\n /** Max age (seconds) before a fetched replaceable is considered stale. Default 2 days. */\n staleAfterSec?: number | undefined;\n fetchTimeoutMs?: number | undefined;\n};\n\n/** Internal dependency bag for loaders — never a module-level singleton. */\nexport class LoaderContext {\n readonly pool: Pool;\n readonly index: ReactiveEventStore;\n readonly ingest: (event: Event, relayUrl: string) => void;\n readonly staleAfterSec: number;\n readonly fetchTimeoutMs: number;\n #relays: string[];\n\n constructor(opts: LoaderContextOptions) {\n this.pool = opts.pool;\n this.#relays = [...opts.relays];\n this.index = opts.index;\n this.ingest = opts.ingest ?? ((event, relayUrl) => this.index.add(event, relayUrl));\n this.staleAfterSec = opts.staleAfterSec ?? 60 * 60 * 24 * 2;\n this.fetchTimeoutMs = opts.fetchTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n }\n\n /** Snapshot of discovery/fallback relays. */\n get relays(): ReadonlyArray<string> {\n return this.#relays;\n }\n\n addRelay(url: string): void {\n if (!this.#relays.includes(url)) {\n this.#relays.push(url);\n }\n }\n\n removeRelay(url: string): void {\n this.#relays = this.#relays.filter((r) => r !== url);\n }\n\n setRelays(urls: ReadonlyArray<string>): void {\n this.#relays = [...urls];\n }\n\n isFresh(fetchedAt: number, now: number = nowSeconds()): boolean {\n return now - fetchedAt < this.staleAfterSec;\n }\n}\n","import type { Event } from \"../core/event.ts\";\nimport { isAddressableKind } from \"../core/kind.ts\";\nimport { formatEventAddress } from \"../core/tag.ts\";\nimport { isHex32 } from \"../core/util.ts\";\nimport { decode, Nip19Error } from \"../nips/nip19.ts\";\nimport type { AddressPointer, EventPointer } from \"../nips/nip19.ts\";\nimport type { LoaderContext } from \"./context.ts\";\n\nexport type EventRef = string | EventPointer | AddressPointer;\n\ntype ParsedRef = {\n filter: { ids?: string[]; authors?: string[]; kinds?: number[]; \"#d\"?: string[] };\n hints: ReadonlyArray<string>;\n /** Resolve the fetched event from the index after the network round. */\n lookup: (ctx: LoaderContext) => Event | undefined;\n cacheKey: string;\n};\n\nfunction dFilter(kind: number, identifier: string): { \"#d\"?: string[] } {\n if (isAddressableKind(kind)) {\n return { \"#d\": [identifier] };\n }\n return {};\n}\n\nfunction addressRef(kind: number, pubkey: string, identifier: string): string {\n return formatEventAddress(kind, pubkey.toLowerCase(), identifier);\n}\n\nfunction parseRef(ref: EventRef): ParsedRef {\n if (typeof ref === \"string\") {\n if (isHex32(ref.toLowerCase())) {\n const id = ref.toLowerCase();\n return {\n filter: { ids: [id] },\n hints: [],\n lookup: (ctx) => ctx.index.get(id),\n cacheKey: `id:${id}`,\n };\n }\n const decoded = decode(ref);\n switch (decoded.type) {\n case \"note\":\n return {\n filter: { ids: [decoded.data] },\n hints: [],\n lookup: (ctx) => ctx.index.get(decoded.data),\n cacheKey: `id:${decoded.data}`,\n };\n case \"nevent\":\n return {\n filter: { ids: [decoded.data.id] },\n hints: decoded.data.relays ?? [],\n lookup: (ctx) => ctx.index.get(decoded.data.id),\n cacheKey: `id:${decoded.data.id}`,\n };\n case \"naddr\": {\n const addr = addressRef(decoded.data.kind, decoded.data.pubkey, decoded.data.identifier);\n return {\n filter: {\n authors: [decoded.data.pubkey.toLowerCase()],\n kinds: [decoded.data.kind],\n ...dFilter(decoded.data.kind, decoded.data.identifier),\n },\n hints: decoded.data.relays ?? [],\n lookup: (ctx) => ctx.index.getByAddress(addr),\n cacheKey: `addr:${addr}`,\n };\n }\n default:\n throw new Nip19Error(`cannot load event from ${decoded.type}`);\n }\n }\n if (\"id\" in ref) {\n const id = ref.id.toLowerCase();\n return {\n filter: { ids: [id] },\n hints: ref.relays ?? [],\n lookup: (ctx) => ctx.index.get(id),\n cacheKey: `id:${id}`,\n };\n }\n const addr = addressRef(ref.kind, ref.pubkey, ref.identifier);\n return {\n filter: {\n authors: [ref.pubkey.toLowerCase()],\n kinds: [ref.kind],\n ...dFilter(ref.kind, ref.identifier),\n },\n hints: ref.relays ?? [],\n lookup: (ctx) => ctx.index.getByAddress(addr),\n cacheKey: `addr:${addr}`,\n };\n}\n\n/**\n * Resolve an event reference (hex id, note, nevent, naddr, or pointer object) through the reactive\n * index, fetching from relays on a miss. The index is the durable store — the loader keeps only\n * in-flight coalescing, so a miss never blocks a later retry.\n */\nexport type EventLoader = {\n load: (ref: EventRef) => Promise<Event | undefined>;\n};\n\nexport function createEventLoader(ctx: LoaderContext): EventLoader {\n const inflight = new Map<string, Promise<Event | undefined>>();\n return {\n // oxlint-disable-next-line typescript/promise-function-async -- parseRef must throw synchronously on malformed refs\n load(ref: EventRef): Promise<Event | undefined> {\n const parsed = parseRef(ref);\n const hit = parsed.lookup(ctx);\n if (hit !== undefined) {\n return Promise.resolve(hit);\n }\n const pending = inflight.get(parsed.cacheKey);\n if (pending !== undefined) {\n return pending;\n }\n const p = (async (): Promise<Event | undefined> => {\n try {\n const relays = [...new Set([...parsed.hints, ...ctx.relays])];\n if (relays.length === 0) {\n return undefined;\n }\n await ctx.pool.fetch(relays, [parsed.filter], {\n timeoutMs: ctx.fetchTimeoutMs,\n onevent: (event, relayUrl) => ctx.ingest(event, relayUrl),\n });\n return parsed.lookup(ctx);\n } finally {\n inflight.delete(parsed.cacheKey);\n }\n })();\n inflight.set(parsed.cacheKey, p);\n return p;\n },\n };\n}\n","import type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { isHex32 } from \"../core/util.ts\";\nimport { parseDmRelayList } from \"../nips/nip17.ts\";\nimport { parseMuteList } from \"../nips/nip51.ts\";\nimport type { MuteItem } from \"../nips/nip51.ts\";\nimport type { RelayListItem } from \"../nips/nip65.ts\";\nimport { parseRelayList } from \"../nips/nip65.ts\";\nimport type { LoadStyle, ReplaceableLoader } from \"./replaceable.ts\";\n\n/** Result of a list loader: the source event, its decoded items, and freshness. */\nexport type ListResult<T> = {\n readonly event: Event | undefined;\n readonly items: ReadonlyArray<T>;\n readonly fresh: boolean;\n};\n\nfunction fromTags<T>(\n event: Event | undefined,\n map: (tag: ReadonlyArray<string>) => T | undefined,\n): T[] {\n if (event === undefined) {\n return [];\n }\n const out: T[] = [];\n for (const tag of event.tags) {\n const item = map(tag);\n if (item !== undefined) {\n out.push(item);\n }\n }\n return out;\n}\n\ntype ListLoaderOpts = { hints?: string[] | undefined; style?: LoadStyle | undefined };\n\nexport type ListLoaders = {\n follows: (pubkey: string, opts?: ListLoaderOpts) => Promise<ListResult<string>>;\n muteList: (pubkey: string, opts?: ListLoaderOpts) => Promise<ListResult<MuteItem>>;\n relayList: (pubkey: string, opts?: ListLoaderOpts) => Promise<ListResult<RelayListItem>>;\n dmRelayList: (pubkey: string, opts?: ListLoaderOpts) => Promise<ListResult<string>>;\n};\n\nexport function createListLoaders(replaceable: (kind: number) => ReplaceableLoader): ListLoaders {\n const followsLoader = replaceable(Kind.Contacts);\n const muteLoader = replaceable(Kind.MuteList);\n const relayListLoader = replaceable(Kind.RelayList);\n const dmRelayListLoader = replaceable(Kind.DirectMessageRelaysList);\n\n return {\n async follows(pubkey: string, opts?: ListLoaderOpts): Promise<ListResult<string>> {\n const { event, fresh } = await followsLoader(pubkey, opts);\n return {\n event,\n fresh,\n items: fromTags(event, (tag) =>\n tag[0] === \"p\" && tag[1] !== undefined && isHex32(tag[1].toLowerCase())\n ? tag[1].toLowerCase()\n : undefined,\n ),\n };\n },\n\n async muteList(pubkey: string, opts?: ListLoaderOpts): Promise<ListResult<MuteItem>> {\n const { event, fresh } = await muteLoader(pubkey, opts);\n let items: MuteItem[] = [];\n if (event) {\n try {\n items = parseMuteList(event);\n } catch {\n items = [];\n }\n }\n return { event, fresh, items };\n },\n\n async relayList(pubkey: string, opts?: ListLoaderOpts): Promise<ListResult<RelayListItem>> {\n const { event, fresh } = await relayListLoader(pubkey, opts);\n let items: RelayListItem[] = [];\n if (event) {\n try {\n items = parseRelayList(event);\n } catch {\n items = [];\n }\n }\n return { event, fresh, items };\n },\n\n async dmRelayList(pubkey: string, opts?: ListLoaderOpts): Promise<ListResult<string>> {\n const { event, fresh } = await dmRelayListLoader(pubkey, opts);\n let items: string[] = [];\n if (event) {\n try {\n items = parseDmRelayList(event);\n } catch {\n items = [];\n }\n }\n return { event, fresh, items };\n },\n };\n}\n","import type { ProfileMetadata } from \"../core/builder.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { isRecord } from \"../core/util.ts\";\nimport { npubEncode } from \"../nips/nip19.ts\";\nimport type { LoadStyle, ReplaceableLoader } from \"./replaceable.ts\";\n\n/** A profile-card view of a pubkey: identity fields plus the kind:0 event. */\nexport type NostrUser = {\n readonly pubkey: string;\n readonly npub: string;\n readonly shortName: string;\n readonly image?: string | undefined;\n readonly metadata: ProfileMetadata;\n readonly lastUpdated: number;\n readonly event: Event | undefined;\n readonly fresh: boolean;\n};\n\n/** A {@link NostrUser} with no metadata — used when no kind:0 event exists. */\nexport function bareNostrUser(pubkey: string): NostrUser {\n const pk = pubkey.toLowerCase();\n let npub: string;\n try {\n npub = npubEncode(pk);\n } catch {\n npub = pk;\n }\n return {\n pubkey: pk,\n npub,\n shortName: npub.startsWith(\"npub1\") ? `${npub.slice(0, 8)}…${npub.slice(-4)}` : pk.slice(0, 8),\n metadata: {},\n lastUpdated: 0,\n event: undefined,\n fresh: false,\n };\n}\n\nfunction nonEmpty(value: unknown): string | undefined {\n return typeof value === \"string\" && value !== \"\" ? value : undefined;\n}\n\nconst PROFILE_METADATA_KEYS: ReadonlyArray<keyof ProfileMetadata> = [\n \"name\",\n \"display_name\",\n \"about\",\n \"picture\",\n \"banner\",\n \"website\",\n \"nip05\",\n \"lud06\",\n \"lud16\",\n];\n\nfunction parseMetadata(content: string): ProfileMetadata {\n try {\n const obj: unknown = JSON.parse(content);\n if (!isRecord(obj)) {\n return {};\n }\n const out: Record<string, string> = {};\n for (const key of PROFILE_METADATA_KEYS) {\n const value = obj[key];\n if (typeof value === \"string\") {\n out[key] = value;\n }\n }\n return out;\n } catch {\n return {};\n }\n}\n\nexport type ProfileLoader = {\n load: (\n pubkey: string,\n opts?: { hints?: string[] | undefined; style?: LoadStyle | undefined },\n ) => Promise<NostrUser>;\n};\n\nexport function createProfileLoader(\n replaceable: (kind: number) => ReplaceableLoader,\n): ProfileLoader {\n const loader = replaceable(Kind.Metadata);\n\n return {\n async load(\n pubkey: string,\n opts?: { hints?: string[] | undefined; style?: LoadStyle | undefined },\n ): Promise<NostrUser> {\n const base = bareNostrUser(pubkey);\n const { event, fresh } = await loader(pubkey, opts);\n if (!event) {\n return { ...base, fresh };\n }\n\n const metadata = parseMetadata(event.content);\n const display = nonEmpty(metadata.display_name) ?? nonEmpty(metadata.name);\n return {\n ...base,\n shortName: display ?? base.shortName,\n ...(metadata.picture === undefined ? {} : { image: metadata.picture }),\n metadata,\n lastUpdated: event.created_at,\n event,\n fresh,\n };\n },\n };\n}\n","import { NostrError } from \"../core/error.ts\";\n\nexport class LoaderError extends NostrError {\n override name = \"LoaderError\";\n}\n\n/** Minimal request coalescer. Batches keys within a microtask; no global state. */\nexport type BatchLoadFn<K, V> = (keys: ReadonlyArray<K>) => Promise<ReadonlyArray<V | Error>>;\n\nexport type DataLoaderOptions<K, C = K> = {\n cacheKeyFn?: (key: K) => C;\n maxBatchSize?: number;\n /** When false, only coalesces in-flight requests (no durable memoization). Default true. */\n cache?: boolean;\n};\n\nexport class DataLoader<K, V, C = K> {\n readonly #batchLoadFn: BatchLoadFn<K, V>;\n readonly #cacheKeyFn: (key: K) => C;\n readonly #maxBatchSize: number;\n readonly #useCache: boolean;\n readonly #cache = new Map<C, Promise<V>>();\n readonly #inflight = new Map<C, Promise<V>>();\n readonly #queue: Array<{\n key: K;\n resolve: (v: V) => void;\n reject: (e: unknown) => void;\n }> = [];\n #scheduled = false;\n\n constructor(batchLoadFn: BatchLoadFn<K, V>, options: DataLoaderOptions<K, C> = {}) {\n this.#batchLoadFn = batchLoadFn;\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the default C = K makes this the identity for callers that omit cacheKeyFn\n this.#cacheKeyFn = options.cacheKeyFn ?? ((k: K) => k as unknown as C);\n this.#maxBatchSize = options.maxBatchSize ?? Number.POSITIVE_INFINITY;\n this.#useCache = options.cache !== false;\n }\n\n async load(key: K): Promise<V> {\n const cacheKey = this.#cacheKeyFn(key);\n if (this.#useCache) {\n const cached = this.#cache.get(cacheKey);\n if (cached) {\n return cached;\n }\n }\n const inflight = this.#inflight.get(cacheKey);\n if (inflight) {\n return inflight;\n }\n\n const pending = new Promise<V>((resolve, reject) => {\n this.#queue.push({ key, resolve, reject });\n if (this.#queue.length >= this.#maxBatchSize) {\n this.#dispatch();\n } else if (!this.#scheduled) {\n this.#scheduled = true;\n queueMicrotask(() => this.#dispatch());\n }\n });\n const promise = (async (): Promise<V> => {\n try {\n return await pending;\n } finally {\n this.#inflight.delete(cacheKey);\n }\n })();\n\n this.#inflight.set(cacheKey, promise);\n if (this.#useCache) {\n this.#cache.set(cacheKey, promise);\n }\n return promise;\n }\n\n clear(key: K): void {\n this.#cache.delete(this.#cacheKeyFn(key));\n this.#inflight.delete(this.#cacheKeyFn(key));\n }\n\n clearAll(): void {\n this.#cache.clear();\n this.#inflight.clear();\n }\n\n #dispatch(): void {\n this.#scheduled = false;\n const batch = this.#queue.splice(0, this.#maxBatchSize);\n if (batch.length === 0) {\n return;\n }\n\n const keys = batch.map((b) => b.key);\n void (async () => {\n let values: ReadonlyArray<V | Error>;\n try {\n values = await this.#batchLoadFn(keys);\n } catch (error: unknown) {\n for (const item of batch) {\n item.reject(error);\n }\n return;\n }\n if (values.length !== keys.length) {\n const err = new LoaderError(\n `DataLoader batch function must return array of length ${keys.length}, got ${values.length}`,\n );\n for (const item of batch) {\n item.reject(err);\n }\n return;\n }\n for (const [i, value] of values.entries()) {\n const item = batch[i];\n if (item === undefined) {\n continue;\n }\n if (value instanceof Error) {\n item.reject(value);\n } else {\n item.resolve(value);\n }\n }\n })();\n\n if (this.#queue.length > 0) {\n this.#scheduled = true;\n queueMicrotask(() => this.#dispatch());\n }\n }\n}\n","import { touchKey, trimOldest } from \"../core/collections.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { formatEventAddress } from \"../core/tag.ts\";\nimport { nowSeconds } from \"../core/util.ts\";\nimport type { LoaderContext } from \"./context.ts\";\nimport { DataLoader } from \"./dataloader.ts\";\n\n/** Result of a replaceable load: the winning event (or none) and freshness. */\nexport type ReplaceableLoadResult = {\n readonly event: Event | undefined;\n readonly fresh: boolean;\n};\n\n/** `default`: fetch when stale or unfetched; `force`: always fetch; `cache-only`: index only. */\nexport type LoadStyle = \"default\" | \"force\" | \"cache-only\";\n\n/** Loads the winning replaceable/addressable event of one kind for a pubkey. */\nexport type ReplaceableLoader = (\n pubkey: string,\n opts?: { hints?: string[] | undefined; style?: LoadStyle | undefined },\n) => Promise<ReplaceableLoadResult>;\n\n/** FIFO cap on per-address fetch records (misses included). */\nconst MAX_FETCHED_AT = 10_000;\n\n/**\n * Address -> last network fetch. `hit` is true when the index held a winner right after that fetch;\n * a fresh `hit` record whose winner was evicted since must refetch instead of reporting a miss.\n */\ntype FetchedAt = Map<string, { at: number; hit: boolean }>;\n\n/**\n * Batch-fetch replaceable events (kind + authors) via the pool. Fetched events are written into the\n * reactive index (`onevent` records `seenOn`) and the winner is read back from it; the index — not\n * the loader — owns the stored value and winner selection. The loader only tracks per-address\n * `fetchedAt` timestamps so a recent fetch (including a miss) is not repeated within\n * `staleAfterSec`.\n */\nexport function createReplaceableLoader(ctx: LoaderContext, kind: number): ReplaceableLoader {\n type Key = { pubkey: string; hints?: string[] | undefined };\n\n const fetchedAt: FetchedAt = new Map();\n const markFetched = (addr: string, hit: boolean, now: number): void => {\n touchKey(fetchedAt, addr, { at: now, hit });\n trimOldest(fetchedAt, MAX_FETCHED_AT);\n };\n\n const loader = new DataLoader<Key, ReplaceableLoadResult, string>(\n async (keys) => {\n const authors = [...new Set(keys.map((k) => k.pubkey))];\n const relays = [...new Set([...keys.flatMap((k) => k.hints ?? []), ...ctx.relays])];\n if (relays.length > 0) {\n const filter: Filter = { kinds: [kind], authors };\n await ctx.pool.fetch(relays, [filter], {\n timeoutMs: ctx.fetchTimeoutMs,\n onevent: (event, relayUrl) => ctx.ingest(event, relayUrl),\n });\n }\n const now = nowSeconds();\n return keys.map((k) => {\n const addr = formatEventAddress(kind, k.pubkey, \"\");\n const event = ctx.index.getByAddress(addr);\n // Record the fetch even when nothing came back: a recent miss must\n // not be retried on every `default` load.\n markFetched(addr, event !== undefined, now);\n return { event, fresh: true };\n });\n },\n {\n cache: false,\n cacheKeyFn: (k) => `${kind}:${k.pubkey}:${(k.hints ?? []).join(\",\")}`,\n maxBatchSize: 50,\n },\n );\n\n return async (pubkey, opts) => {\n const pk = pubkey.toLowerCase();\n const address = formatEventAddress(kind, pk, \"\");\n const style = opts?.style ?? \"default\";\n const current = (): Event | undefined => ctx.index.getByAddress(address);\n\n if (style === \"cache-only\") {\n return { event: current(), fresh: false };\n }\n if (style === \"default\") {\n const rec = fetchedAt.get(address);\n const event = current();\n // A fresh record short-circuits the fetch, unless it was a hit whose\n // winner has since been evicted — that would report a stale miss.\n if (rec !== undefined && ctx.isFresh(rec.at) && (!rec.hit || event !== undefined)) {\n return { event, fresh: false };\n }\n }\n return loader.load({ pubkey: pk, hints: opts?.hints });\n };\n}\n","import { throwIfAborted } from \"../core/abort.ts\";\nimport { NostrError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { sortedEvents } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { invokeSafely } from \"../core/report.ts\";\nimport type { Mutable } from \"../core/util.ts\";\nimport { normalizeRelayUrls, nowSeconds } from \"../core/util.ts\";\nimport type { Gossip } from \"../gossip/gossip.ts\";\nimport type { Pool } from \"../relay/pool.ts\";\nimport { DEFAULT_REQUEST_TIMEOUT_MS } from \"../relay/relay.ts\";\nimport type { Closer } from \"../relay/subscription.ts\";\nimport { toStorageError } from \"../storage/error.ts\";\nimport { outboxBoundKey } from \"../storage/put.ts\";\nimport type { EventStore, OutboxBound } from \"../storage/types.ts\";\n\nexport type { OutboxBound } from \"../storage/types.ts\";\n\n/** Outbox feed lifecycle error (e.g. use after close). */\nexport class OutboxError extends NostrError {\n override name = \"OutboxError\";\n}\n\n/** Trailing-flush delay for live bound persistence: one write per dirty bound per window. */\nexport const BOUND_FLUSH_MS = 1000;\n\n/** Options for {@link OutboxFeed}. */\nexport type OutboxFeedOptions = {\n pool: Pool;\n gossip: Gossip;\n storage: EventStore;\n /** Fallback relays when an author has no known outbox routes. */\n discoveryRelays: ReadonlyArray<string>;\n authors: ReadonlyArray<string>;\n /** Event kinds to fetch. Default `[Kind.TextNote]`. */\n kinds?: ReadonlyArray<number> | undefined;\n /** Max write-relays to use per author. Default 3. */\n maxRelaysPerAuthor?: number | undefined;\n fetchTimeoutMs?: number | undefined;\n /** Called for live events after observe, and for sync events returned by applySync. */\n onEvent?: ((event: Event) => void) | undefined;\n /** Live ingest (e.g. `client.observe`) with the source relay URL. StartLive never putManys. */\n observe?: ((event: Event, relayUrl: string) => void) | undefined;\n /**\n * Every receipt from every relay — live (including duplicates the feed's own dedupe skips) and\n * sync fetch batches — for seenOn bookkeeping.\n */\n seen?: ((event: Event, relayUrl: string) => void) | undefined;\n /**\n * Sync path only. Awaited. Return events that should advance bounds. Throw → sync throws, bounds\n * unchanged.\n */\n applySync?: ((events: ReadonlyArray<Event>) => Promise<ReadonlyArray<Event>>) | undefined;\n /** Load kind:10002 for authors before sync when routes are missing. Requires a relay-list loader. */\n hydrate?: ((pubkeys: ReadonlyArray<string>) => Promise<void>) | undefined;\n};\n\n/**\n * Group authors by outbox relay (discovery fallback). `prefer` reorders existing candidates only;\n * it never appends a URL that is not already in the author's outbox or discovery list. Returns\n * Map<relayUrl, authors[]>.\n */\nexport function groupAuthorsByOutboxRelay(\n authors: ReadonlyArray<string>,\n gossip: Gossip,\n discoveryRelays: ReadonlyArray<string>,\n maxRelaysPerAuthor = 3,\n prefer: ReadonlyArray<string> = [],\n): Map<string, string[]> {\n const leftover = new Set(\n (gossip.route({ authors: authors.map((a) => a.toLowerCase()) }).remainder?.authors ?? []).map(\n (a) => a.toLowerCase(),\n ),\n );\n const preferSet = new Set(normalizeRelayUrls(prefer));\n const discovery = normalizeRelayUrls(discoveryRelays);\n const map = new Map<string, string[]>();\n\n for (const author of authors) {\n const pk = author.toLowerCase();\n const urls = leftover.has(pk) ? discovery : gossip.outboxRelays(pk);\n const preferred: string[] = [];\n const rest: string[] = [];\n for (const url of urls) {\n if (preferSet.has(url)) {\n preferred.push(url);\n } else {\n rest.push(url);\n }\n }\n for (const url of [...preferred, ...rest].slice(0, maxRelaysPerAuthor)) {\n const list = map.get(url) ?? [];\n if (!list.includes(pk)) {\n list.push(pk);\n }\n map.set(url, list);\n }\n }\n return map;\n}\n\n/**\n * Outbox-model feed: history sync + live subscription for a set of authors, routed via NIP-65 write\n * relays (with discovery fallback).\n */\nexport class OutboxFeed {\n readonly #pool: Pool;\n readonly #gossip: Gossip;\n readonly #storage: EventStore;\n readonly #discovery: string[];\n readonly #kinds: number[];\n readonly #maxRelays: number;\n readonly #timeoutMs: number;\n readonly #onEvent: ((event: Event) => void) | undefined;\n readonly #observe: ((event: Event, relayUrl: string) => void) | undefined;\n readonly #seen: ((event: Event, relayUrl: string) => void) | undefined;\n readonly #applySync:\n | ((events: ReadonlyArray<Event>) => Promise<ReadonlyArray<Event>>)\n | undefined;\n readonly #hydrate: ((pubkeys: ReadonlyArray<string>) => Promise<void>) | undefined;\n readonly #bounds = new Map<string, Mutable<OutboxBound>>();\n /** Bound keys dirtied by live events since the last flush. */\n readonly #dirtyBounds = new Set<string>();\n #boundTimer: ReturnType<typeof setTimeout> | undefined;\n #authors: string[];\n #liveCloser: Closer | undefined;\n #closed = false;\n\n constructor(opts: OutboxFeedOptions) {\n this.#pool = opts.pool;\n this.#gossip = opts.gossip;\n this.#storage = opts.storage;\n this.#discovery = [...opts.discoveryRelays];\n this.#authors = [...new Set(opts.authors.map((a) => a.toLowerCase()))];\n this.#kinds = [...(opts.kinds ?? [Kind.TextNote])];\n this.#maxRelays = opts.maxRelaysPerAuthor ?? 3;\n this.#timeoutMs = opts.fetchTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this.#onEvent = opts.onEvent;\n this.#observe = opts.observe;\n this.#seen = opts.seen;\n this.#applySync = opts.applySync;\n this.#hydrate = opts.hydrate;\n }\n\n get authors(): ReadonlyArray<string> {\n return this.#authors;\n }\n\n get kinds(): ReadonlyArray<number> {\n return this.#kinds;\n }\n\n /** Replace the author set (does not auto-restart live). */\n setAuthors(authors: ReadonlyArray<string>): void {\n this.#authors = [...new Set(authors.map((a) => a.toLowerCase()))];\n }\n\n getBound(pubkey: string, kind: number): OutboxBound | undefined {\n return this.#bounds.get(outboxBoundKey(pubkey, kind));\n }\n\n /** Load NIP-65 relay lists for authors that have no outbox routes yet. */\n async hydrate(): Promise<void> {\n this.#assertOpen();\n if (!this.#hydrate) {\n return;\n }\n const missing = this.#authors.filter((pk) => this.#gossip.outboxRelays(pk).length === 0);\n if (missing.length === 0) {\n return;\n }\n await this.#hydrate(missing);\n }\n\n /** One-shot history pull. Persistence is `applySync`; bounds move only for events it returns. */\n async sync(opts?: {\n limit?: number | undefined;\n since?: number | undefined;\n until?: number | undefined;\n signal?: AbortSignal | undefined;\n /** Skip hydrate() even if provided. */\n skipHydrate?: boolean | undefined;\n }): Promise<Event[]> {\n this.#assertOpen();\n if (opts?.skipHydrate !== true) {\n await this.hydrate();\n }\n await this.#hydrateBoundsFromStore();\n\n const byRelay = groupAuthorsByOutboxRelay(\n this.#authors,\n this.#gossip,\n this.#discovery,\n this.#maxRelays,\n this.#pool.connectedUrls(),\n );\n\n if (byRelay.size === 0) {\n return [];\n }\n\n const byId = new Map<string, Event>();\n await Promise.all(\n [...byRelay.entries()].map(async ([url, authors]) => {\n throwIfAborted(opts?.signal);\n\n const filters = this.#syncFilters(authors, opts);\n try {\n const batch = await this.#pool.fetch([url], filters, {\n timeoutMs: this.#timeoutMs,\n signal: opts?.signal,\n onevent: (event, relayUrl) => invokeSafely(() => this.#seen?.(event, relayUrl)),\n });\n for (const event of batch) {\n byId.set(event.id, event);\n }\n } catch (error) {\n // An abort rejects the whole sync; per-relay failures are skipped.\n if (opts?.signal?.aborted === true) {\n throw error;\n }\n }\n }),\n );\n\n const unique = [...byId.values()];\n if (unique.length === 0) {\n return [];\n }\n\n let applied: ReadonlyArray<Event> = [];\n if (this.#applySync) {\n try {\n applied = await this.#applySync(unique);\n } catch (error) {\n throw toStorageError(error);\n }\n }\n const dirty = new Set<string>();\n for (const event of applied) {\n this.#updateBounds(event);\n dirty.add(outboxBoundKey(event.pubkey, event.kind));\n invokeSafely(() => this.#onEvent?.(event));\n }\n await this.#persistBounds(dirty);\n return sortedEvents(unique);\n }\n\n /**\n * Start a live subscription across outbox relays for the current authors. Closes any previous\n * live subscription.\n */\n startLive(opts?: { signal?: AbortSignal | undefined; since?: number | undefined }): Closer {\n this.#assertOpen();\n this.#liveCloser?.close(\"restart\");\n\n const byRelay = groupAuthorsByOutboxRelay(\n this.#authors,\n this.#gossip,\n this.#discovery,\n this.#maxRelays,\n this.#pool.connectedUrls(),\n );\n\n const seen = new Set<string>();\n const closers: Closer[] = [];\n const since = opts?.since ?? nowSeconds() - 60;\n\n for (const [url, authors] of byRelay) {\n const filter: Filter = {\n authors,\n kinds: this.#kinds,\n since,\n };\n closers.push(\n this.#pool.subscribe([url], [filter], {\n signal: opts?.signal,\n onevent: (event, relayUrl) => {\n invokeSafely(() => this.#seen?.(event, relayUrl));\n if (seen.has(event.id)) {\n return;\n }\n seen.add(event.id);\n this.#noteEvent(event, relayUrl);\n },\n }),\n );\n }\n\n const closer = {\n close: (reason?: string) => {\n for (const c of closers) {\n c.close(reason);\n }\n if (this.#liveCloser === closer) {\n this.#liveCloser = undefined;\n }\n },\n };\n this.#liveCloser = closer;\n return closer;\n }\n\n /** Convenience: hydrate + sync + startLive. */\n async start(opts?: {\n limit?: number | undefined;\n signal?: AbortSignal | undefined;\n }): Promise<Closer & { events: Event[] }> {\n const events = await this.sync({ limit: opts?.limit, signal: opts?.signal });\n const live = this.startLive({ signal: opts?.signal });\n return { close: (r) => live.close(r), events };\n }\n\n close(): void {\n this.#closed = true;\n this.#liveCloser?.close(\"feed closed\");\n this.#liveCloser = undefined;\n if (this.#boundTimer !== undefined) {\n clearTimeout(this.#boundTimer);\n this.#boundTimer = undefined;\n }\n void this.#flushBounds();\n }\n\n #assertOpen(): void {\n if (this.#closed) {\n throw new OutboxError(\"OutboxFeed is closed\");\n }\n }\n\n async #hydrateBoundsFromStore(): Promise<void> {\n await Promise.all(\n this.#authors.flatMap((pk) =>\n this.#kinds.map(async (kind) => {\n const key = outboxBoundKey(pk, kind);\n if (this.#bounds.has(key)) {\n return;\n }\n const bound = await this.#storage.getOutboxBound(pk, kind);\n if (!bound) {\n return;\n }\n await this.#storage.setOutboxBound(pk, kind, bound);\n this.#bounds.set(key, { oldest: bound.oldest, newest: bound.newest });\n }),\n ),\n );\n }\n\n #syncFilters(\n authors: string[],\n opts?: { limit?: number | undefined; since?: number | undefined; until?: number | undefined },\n ): Filter[] {\n const base: Filter = {\n kinds: this.#kinds,\n limit: opts?.limit ?? 50,\n ...(opts?.until === undefined ? {} : { until: opts.until }),\n };\n\n if (opts?.since !== undefined) {\n return [{ ...base, authors, since: opts.since }];\n }\n\n const bounded: string[] = [];\n const unbounded: string[] = [];\n let minBoundedNewest: number | undefined;\n\n for (const pk of authors) {\n let newest: number | undefined;\n let complete = true;\n for (const kind of this.#kinds) {\n const b = this.#bounds.get(outboxBoundKey(pk, kind));\n if (!b) {\n complete = false;\n break;\n }\n newest = Math.min(newest ?? b.newest, b.newest);\n }\n if (complete && newest !== undefined) {\n bounded.push(pk);\n if (minBoundedNewest === undefined || newest < minBoundedNewest) {\n minBoundedNewest = newest;\n }\n } else {\n unbounded.push(pk);\n }\n }\n\n if (bounded.length === 0 || minBoundedNewest === undefined) {\n return [{ ...base, authors }];\n }\n if (unbounded.length === 0) {\n return [{ ...base, authors, since: Math.max(0, minBoundedNewest - 1) }];\n }\n return [\n { ...base, authors: bounded, since: Math.max(0, minBoundedNewest - 1) },\n { ...base, authors: unbounded },\n ];\n }\n\n #noteEvent(event: Event, relayUrl: string): void {\n this.#updateBounds(event);\n const key = outboxBoundKey(event.pubkey, event.kind);\n if (this.#bounds.has(key)) {\n this.#dirtyBounds.add(key);\n this.#boundTimer ??= setTimeout(() => {\n this.#boundTimer = undefined;\n void this.#flushBounds();\n }, BOUND_FLUSH_MS);\n }\n invokeSafely(() => this.#observe?.(event, relayUrl));\n invokeSafely(() => this.#onEvent?.(event));\n }\n\n /** Persist all bounds dirtied since the last flush. Best-effort: errors are swallowed. */\n async #flushBounds(): Promise<void> {\n const keys = [...this.#dirtyBounds];\n this.#dirtyBounds.clear();\n try {\n await this.#persistBounds(keys);\n } catch {\n // Bound persistence is best-effort.\n }\n }\n\n async #persistBounds(keys: Iterable<string>): Promise<void> {\n const writes: Array<Promise<void>> = [];\n for (const key of keys) {\n const bound = this.#bounds.get(key);\n if (!bound) {\n continue;\n }\n const sep = key.lastIndexOf(\":\");\n writes.push(\n this.#storage.setOutboxBound(key.slice(0, sep), Number(key.slice(sep + 1)), {\n oldest: bound.oldest,\n newest: bound.newest,\n }),\n );\n }\n if (writes.length === 0) {\n return;\n }\n try {\n await Promise.all(writes);\n } catch (error) {\n throw toStorageError(error);\n }\n }\n\n #updateBounds(event: Event): void {\n const key = outboxBoundKey(event.pubkey, event.kind);\n const prev = this.#bounds.get(key);\n if (!prev) {\n this.#bounds.set(key, { oldest: event.created_at, newest: event.created_at });\n return;\n }\n if (event.created_at < prev.oldest) {\n prev.oldest = event.created_at;\n }\n if (event.created_at > prev.newest) {\n prev.newest = event.created_at;\n }\n }\n}\n","import type { Event } from \"../core/event.ts\";\nimport type { Pool } from \"../relay/pool.ts\";\nimport type { ReactiveEventStore } from \"../store/reactive.ts\";\nimport { LoaderContext } from \"./context.ts\";\nimport { createEventLoader } from \"./event.ts\";\nimport type { EventLoader } from \"./event.ts\";\nimport { createListLoaders } from \"./lists.ts\";\nimport type { ListLoaders } from \"./lists.ts\";\nimport { createProfileLoader } from \"./profile.ts\";\nimport type { ProfileLoader } from \"./profile.ts\";\nimport { createReplaceableLoader } from \"./replaceable.ts\";\nimport type { ReplaceableLoader } from \"./replaceable.ts\";\n\nexport {\n type LoadStyle,\n type ReplaceableLoader,\n type ReplaceableLoadResult,\n} from \"./replaceable.ts\";\nexport { type ListResult, type ListLoaders } from \"./lists.ts\";\nexport { bareNostrUser, type NostrUser, type ProfileLoader } from \"./profile.ts\";\nexport { type EventLoader, type EventRef } from \"./event.ts\";\nexport {\n OutboxError,\n OutboxFeed,\n groupAuthorsByOutboxRelay,\n type OutboxBound,\n type OutboxFeedOptions,\n} from \"./outbox.ts\";\n\n/** The loader surface returned by {@link createLoaders}. */\nexport type Loaders = {\n follows: ListLoaders[\"follows\"];\n muteList: ListLoaders[\"muteList\"];\n relayList: ListLoaders[\"relayList\"];\n dmRelayList: ListLoaders[\"dmRelayList\"];\n profile: ProfileLoader[\"load\"];\n event: EventLoader[\"load\"];\n /** Generic replaceable loader for any kind; memoized per kind. */\n replaceable: (kind: number) => ReplaceableLoader;\n addRelay: (url: string) => void;\n removeRelay: (url: string) => void;\n};\n\n/** Options for {@link createLoaders}. */\nexport type CreateLoadersOptions = {\n pool: Pool;\n relays: ReadonlyArray<string>;\n /** The reactive index loaders read from and feed fetched events into. */\n index: ReactiveEventStore;\n /**\n * Inbound-event sink for fetched events; defaults to `index.add`. Client wires its single ingest\n * path so loader fetches get gossip meta and persistence like every other inbound event.\n */\n ingest?: ((event: Event, relayUrl: string) => void) | undefined;\n staleAfterSec?: number | undefined;\n fetchTimeoutMs?: number | undefined;\n};\n\n/** Build an instance-scoped loader suite (no module globals). */\nexport function createLoaders(opts: CreateLoadersOptions): Loaders {\n const context = new LoaderContext(opts);\n const byKind = new Map<number, ReplaceableLoader>();\n const replaceable = (kind: number): ReplaceableLoader => {\n let loader = byKind.get(kind);\n if (loader === undefined) {\n loader = createReplaceableLoader(context, kind);\n byKind.set(kind, loader);\n }\n return loader;\n };\n const lists = createListLoaders(replaceable);\n const profile = createProfileLoader(replaceable);\n const event = createEventLoader(context);\n return {\n follows: async (pubkey, o) => lists.follows(pubkey, o),\n muteList: async (pubkey, o) => lists.muteList(pubkey, o),\n relayList: async (pubkey, o) => lists.relayList(pubkey, o),\n dmRelayList: async (pubkey, o) => lists.dmRelayList(pubkey, o),\n profile: async (pubkey, o) => profile.load(pubkey, o),\n // oxlint-disable-next-line typescript/promise-function-async -- must pass through load's synchronous throw on malformed refs\n event: (ref) => event.load(ref),\n replaceable,\n addRelay: (url) => context.addRelay(url),\n removeRelay: (url) => context.removeRelay(url),\n };\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { r as OutboxBound } from "./types-2pCUprRU.mjs";
2
- import { _ as ReplaceableLoader, a as OutboxFeed, c as groupAuthorsByOutboxRelay, d as bareNostrUser, f as ListLoaders, g as ReplaceableLoadResult, h as LoadStyle, i as OutboxError, l as NostrUser, m as MutedEntity, n as Loaders, o as OutboxFeedOptions, p as ListResult, r as createLoaders, s as createOutboxFeed, t as CreateLoadersOptions, u as ProfileLoader, v as EventLoader, y as EventRef } from "./index-lvXeER5t.mjs";
3
- export { CreateLoadersOptions, type EventLoader, type EventRef, type ListLoaders, type ListResult, type LoadStyle, Loaders, type MutedEntity, type NostrUser, type OutboxBound, OutboxError, OutboxFeed, type OutboxFeedOptions, type ProfileLoader, type ReplaceableLoadResult, type ReplaceableLoader, bareNostrUser, createLoaders, createOutboxFeed, groupAuthorsByOutboxRelay };
1
+ import { r as OutboxBound } from "./types-BacwnDtU.mjs";
2
+ import { _ as EventRef, a as OutboxFeed, c as NostrUser, d as ListLoaders, f as ListResult, g as EventLoader, h as ReplaceableLoader, i as OutboxError, l as ProfileLoader, m as ReplaceableLoadResult, n as Loaders, o as OutboxFeedOptions, p as LoadStyle, r as createLoaders, s as groupAuthorsByOutboxRelay, t as CreateLoadersOptions, u as bareNostrUser } from "./index-CPVEyzNX.mjs";
3
+ export { CreateLoadersOptions, type EventLoader, type EventRef, type ListLoaders, type ListResult, type LoadStyle, Loaders, type NostrUser, type OutboxBound, OutboxError, OutboxFeed, type OutboxFeedOptions, type ProfileLoader, type ReplaceableLoadResult, type ReplaceableLoader, bareNostrUser, createLoaders, groupAuthorsByOutboxRelay };
package/dist/loaders.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as groupAuthorsByOutboxRelay, i as createOutboxFeed, n as OutboxError, o as bareNostrUser, r as OutboxFeed, t as createLoaders } from "./loaders-o6YXB_Ri.mjs";
2
- export { OutboxError, OutboxFeed, bareNostrUser, createLoaders, createOutboxFeed, groupAuthorsByOutboxRelay };
1
+ import { a as bareNostrUser, i as groupAuthorsByOutboxRelay, n as OutboxError, r as OutboxFeed, t as createLoaders } from "./loaders-BmPkQfzI.mjs";
2
+ export { OutboxError, OutboxFeed, bareNostrUser, createLoaders, groupAuthorsByOutboxRelay };
@@ -1,4 +1,4 @@
1
- import { t as MemoryIndex } from "./memory-index-Do1mz0WF.mjs";
1
+ import { t as MemoryIndex } from "./memory-index-Cbc83vOZ.mjs";
2
2
  //#region src/storage/memory.ts
3
3
  /** In-memory event store: async {@link EventStore} facade over the synchronous {@link MemoryIndex}. */
4
4
  var MemoryEventStore = class {
@@ -43,4 +43,4 @@ var MemoryEventStore = class {
43
43
  //#endregion
44
44
  export { MemoryEventStore as t };
45
45
 
46
- //# sourceMappingURL=memory-C2EVx0_O.mjs.map
46
+ //# sourceMappingURL=memory-CdJ07kub.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-CdJ07kub.mjs","names":[],"sources":["../src/storage/memory.ts"],"sourcesContent":["import type { Event } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { MemoryIndex } from \"./memory-index.ts\";\nimport type { EventStore, NegentropyItem, OutboxBound, PutResult } from \"./types.ts\";\n\n/** In-memory event store: async {@link EventStore} facade over the synchronous {@link MemoryIndex}. */\nexport class MemoryEventStore implements EventStore {\n readonly #index = new MemoryIndex();\n\n async put(raw: Event): Promise<PutResult> {\n return this.#index.put(raw);\n }\n\n /** Sequential `put` in input order. No transaction: a throw leaves earlier events applied. */\n async putMany(events: ReadonlyArray<Event>): Promise<PutResult[]> {\n const results: PutResult[] = [];\n for (const event of events) {\n // oxlint-disable-next-line no-await-in-loop -- putMany applies puts sequentially in input order\n results.push(await this.put(event));\n }\n return results;\n }\n\n async get(id: string): Promise<Event | undefined> {\n return this.#index.get(id);\n }\n\n async query(filters: ReadonlyArray<Filter>): Promise<Event[]> {\n return this.#index.query(filters);\n }\n\n async count(filters: ReadonlyArray<Filter>): Promise<number> {\n return this.#index.count(filters);\n }\n\n async negentropyItems(filter: Filter): Promise<NegentropyItem[]> {\n return this.#index.negentropyItems(filter);\n }\n\n async remove(ids: ReadonlyArray<string>): Promise<number> {\n return this.#index.remove(ids);\n }\n\n async getOutboxBound(pubkey: string, kind: number): Promise<OutboxBound | undefined> {\n return this.#index.getOutboxBound(pubkey, kind);\n }\n\n async setOutboxBound(pubkey: string, kind: number, bound: OutboxBound): Promise<void> {\n this.#index.setOutboxBound(pubkey, kind, bound);\n }\n\n async clear(): Promise<void> {\n this.#index.clear();\n }\n\n get size(): number {\n return this.#index.size;\n }\n}\n"],"mappings":";;;AAMA,IAAa,mBAAb,MAAoD;CAClD,SAAkB,IAAI,YAAY;CAElC,MAAM,IAAI,KAAgC;EACxC,OAAO,KAAK,OAAO,IAAI,GAAG;CAC5B;;CAGA,MAAM,QAAQ,QAAoD;EAChE,MAAM,UAAuB,CAAC;EAC9B,KAAK,MAAM,SAAS,QAElB,QAAQ,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC;EAEpC,OAAO;CACT;CAEA,MAAM,IAAI,IAAwC;EAChD,OAAO,KAAK,OAAO,IAAI,EAAE;CAC3B;CAEA,MAAM,MAAM,SAAkD;EAC5D,OAAO,KAAK,OAAO,MAAM,OAAO;CAClC;CAEA,MAAM,MAAM,SAAiD;EAC3D,OAAO,KAAK,OAAO,MAAM,OAAO;CAClC;CAEA,MAAM,gBAAgB,QAA2C;EAC/D,OAAO,KAAK,OAAO,gBAAgB,MAAM;CAC3C;CAEA,MAAM,OAAO,KAA6C;EACxD,OAAO,KAAK,OAAO,OAAO,GAAG;CAC/B;CAEA,MAAM,eAAe,QAAgB,MAAgD;EACnF,OAAO,KAAK,OAAO,eAAe,QAAQ,IAAI;CAChD;CAEA,MAAM,eAAe,QAAgB,MAAc,OAAmC;EACpF,KAAK,OAAO,eAAe,QAAQ,MAAM,KAAK;CAChD;CAEA,MAAM,QAAuB;EAC3B,KAAK,OAAO,MAAM;CACpB;CAEA,IAAI,OAAe;EACjB,OAAO,KAAK,OAAO;CACrB;AACF"}
@@ -1,188 +1,7 @@
1
- import { o as isHex32 } from "./util-LfQirgDk.mjs";
2
- import { i as isEphemeralKind, t as Kind } from "./kind-Cxwhhkx8.mjs";
3
- import { _ as formatEventAddress, a as isReplaceableWinner, b as parseEventAddress, d as sortEvents, g as eventAddress, m as validateSignedEvent, o as itemCompare, t as compareEventsDesc } from "./event-D8Nfoh4K.mjs";
1
+ import { d as sortEvents, g as eventAddress, o as itemCompare, t as compareEventsDesc, v as formatEventAddress, x as parseEventAddress } from "./event-CHf47dPM.mjs";
4
2
  import { a as matchFilter } from "./filter-BQl3GlHt.mjs";
5
- //#region src/storage/deletion.ts
6
- /**
7
- * NIP-09 application plan for a kind 5 event.
8
- *
9
- * - `e` tags: delete only when the target exists, is not itself kind 5, and shares pubkey.
10
- * - Unknown `e` ids are pending until the event arrives (client MUST validate pubkey).
11
- * - `a` tags: tombstone that coordinate up to this deletion's `created_at` when pubkey matches.
12
- * - Deleting a deletion request is a no-op.
13
- */
14
- function planDeletion(deletion, getById) {
15
- const { pubkey } = deletion;
16
- const removeIds = [];
17
- const pendingIds = [];
18
- const coordinates = [];
19
- const seenIds = /* @__PURE__ */ new Set();
20
- const seenCoords = /* @__PURE__ */ new Set();
21
- for (const tag of deletion.tags) {
22
- if (tag[0] === "e" && tag[1] !== void 0 && isHex32(tag[1].toLowerCase())) {
23
- const id = tag[1].toLowerCase();
24
- if (seenIds.has(id)) continue;
25
- seenIds.add(id);
26
- const existing = getById(id);
27
- if (!existing) {
28
- pendingIds.push({
29
- id,
30
- pubkey
31
- });
32
- continue;
33
- }
34
- if (existing.kind === Kind.EventDeletion) continue;
35
- if (existing.pubkey !== pubkey) continue;
36
- removeIds.push(id);
37
- continue;
38
- }
39
- if (tag[0] === "a" && tag[1] !== void 0) {
40
- const coord = parseEventAddress(tag[1]);
41
- if (!coord || coord.pubkey !== pubkey) continue;
42
- const key = `${coord.kind}:${coord.pubkey}:${coord.identifier}`;
43
- if (seenCoords.has(key)) continue;
44
- seenCoords.add(key);
45
- coordinates.push({
46
- key,
47
- until: deletion.created_at
48
- });
49
- }
50
- }
51
- return {
52
- removeIds,
53
- pendingIds,
54
- coordinates
55
- };
56
- }
57
- /** In-memory NIP-09 tombstones shared by MemoryEventStore and IndexedDbEventStore. */
58
- var DeletionState = class {
59
- ids = /* @__PURE__ */ new Set();
60
- pending = /* @__PURE__ */ new Map();
61
- coordinates = /* @__PURE__ */ new Map();
62
- clear() {
63
- this.ids.clear();
64
- this.pending.clear();
65
- this.coordinates.clear();
66
- }
67
- covers(event) {
68
- if (this.ids.has(event.id)) return true;
69
- if (event.kind === Kind.EventDeletion) return false;
70
- const pendingPk = this.pending.get(event.id);
71
- if (pendingPk !== void 0 && event.pubkey === pendingPk) return true;
72
- const addr = eventAddress(event);
73
- if (addr === void 0) return false;
74
- const until = this.coordinates.get(addr);
75
- return until !== void 0 && event.created_at <= until;
76
- }
77
- absorb(plan) {
78
- for (const id of plan.removeIds) {
79
- this.ids.add(id);
80
- this.pending.delete(id);
81
- }
82
- for (const p of plan.pendingIds) if (!this.ids.has(p.id)) this.pending.set(p.id, p.pubkey);
83
- for (const c of plan.coordinates) {
84
- const prev = this.coordinates.get(c.key) ?? Number.NEGATIVE_INFINITY;
85
- this.coordinates.delete(c.key);
86
- this.coordinates.set(c.key, Math.max(prev, c.until));
87
- }
88
- }
89
- };
90
- function coordinateRemovals(coordinates, getCurrent) {
91
- const ids = [];
92
- for (const c of coordinates) {
93
- const current = getCurrent(c.key);
94
- if (current && current.created_at <= c.until) ids.push(current.id);
95
- }
96
- return ids;
97
- }
98
- //#endregion
99
- //#region src/storage/put.ts
100
- function outboxBoundKey(pubkey, kind) {
101
- return `${pubkey.toLowerCase()}:${kind}`;
102
- }
103
- function decidePut(raw, lookup) {
104
- if (!validateSignedEvent(raw)) return {
105
- action: "skip",
106
- result: "invalid",
107
- event: raw
108
- };
109
- const event = raw;
110
- if (lookup.deletion.ids.has(event.id) || lookup.getById(event.id)) return {
111
- action: "skip",
112
- result: "duplicate",
113
- event
114
- };
115
- if (event.kind === Kind.EventDeletion) {
116
- const plan = planDeletion(event, lookup.getById);
117
- return {
118
- action: "delete",
119
- result: "deleted",
120
- event,
121
- plan,
122
- coordIds: coordinateRemovals(plan.coordinates, lookup.getReplaceable)
123
- };
124
- }
125
- if (lookup.deletion.covers(event)) return {
126
- action: "tombstone",
127
- result: "duplicate",
128
- event
129
- };
130
- if (isEphemeralKind(event.kind)) return {
131
- action: "skip",
132
- result: "ephemeral",
133
- event
134
- };
135
- const address = eventAddress(event);
136
- if (address !== void 0) {
137
- const prev = lookup.getReplaceable(address);
138
- if (prev && prev.id !== event.id && !isReplaceableWinner(event, prev)) return {
139
- action: "skip",
140
- result: "rejected",
141
- event
142
- };
143
- if (prev !== void 0 && prev.evicted !== true) return {
144
- action: "insert",
145
- result: "replaced",
146
- event,
147
- address,
148
- replaceId: prev.id
149
- };
150
- return {
151
- action: "insert",
152
- result: "accepted",
153
- event,
154
- address
155
- };
156
- }
157
- return {
158
- action: "insert",
159
- result: "accepted",
160
- event
161
- };
162
- }
163
- function applyPutMemory(s, d) {
164
- if (d.action === "skip") return d.result;
165
- if (d.action === "tombstone") {
166
- s.deletion.ids.add(d.event.id);
167
- s.deletion.pending.delete(d.event.id);
168
- return "duplicate";
169
- }
170
- if (d.action === "delete") {
171
- s.deletion.pending.delete(d.event.id);
172
- s.deletion.absorb(d.plan);
173
- for (const id of d.plan.removeIds) s.indexRemove(id);
174
- for (const id of d.coordIds) {
175
- s.deletion.ids.add(id);
176
- s.indexRemove(id);
177
- }
178
- s.indexInsert(d.event);
179
- return "deleted";
180
- }
181
- if (d.replaceId !== void 0) s.indexRemove(d.replaceId);
182
- s.indexInsert(d.event);
183
- return d.result;
184
- }
185
- //#endregion
3
+ import { i as trimOldest, n as removeFromSetMap, t as addToSetMap } from "./collections-DtwMc1NU.mjs";
4
+ import { i as DeletionState, n as decidePut, r as outboxBoundKey, t as applyPutMemory } from "./put-w13ci3sC.mjs";
186
5
  //#region src/storage/memory-index.ts
187
6
  /**
188
7
  * Synchronous in-memory event index with NIP-01 replaceable / addressable / ephemeral handling and
@@ -207,11 +26,11 @@ var MemoryIndex = class {
207
26
  constructor(opts) {
208
27
  this.#maxTombstones = opts?.maxTombstones;
209
28
  this.#maxWatermarks = opts?.maxWatermarks ?? 0;
210
- this.#onInsert = opts?.onInsert === void 0 ? void 0 : (event) => opts.onInsert?.(event);
211
- this.#onRemove = opts?.onRemove === void 0 ? void 0 : (event) => opts.onRemove?.(event);
29
+ this.#onInsert = opts?.onInsert;
30
+ this.#onRemove = opts?.onRemove;
212
31
  }
213
32
  put(raw) {
214
- const decision = decidePut(raw, {
33
+ const lookup = {
215
34
  deletion: this.#deletion,
216
35
  getById: (id) => this.#byId.get(id),
217
36
  getReplaceable: (addr) => {
@@ -227,7 +46,8 @@ var MemoryIndex = class {
227
46
  evicted: true
228
47
  };
229
48
  }
230
- });
49
+ };
50
+ const decision = decidePut(raw, lookup);
231
51
  if (decision.action === "delete") {
232
52
  for (const p of decision.plan.pendingIds) this.#dropWatermarkId(p.id);
233
53
  for (const c of decision.plan.coordinates) this.#dropWatermark(c.key);
@@ -257,9 +77,8 @@ var MemoryIndex = class {
257
77
  if (!coord) return;
258
78
  const id = this.#replaceable.get(formatEventAddress(coord.kind, coord.pubkey, coord.identifier));
259
79
  if (id === void 0) return;
260
- const key = id.toLowerCase();
261
- if (this.#deletion.ids.has(key)) return;
262
- return this.#byId.get(key);
80
+ if (this.#deletion.ids.has(id)) return;
81
+ return this.#byId.get(id);
263
82
  }
264
83
  query(filters) {
265
84
  const seen = /* @__PURE__ */ new Set();
@@ -289,7 +108,7 @@ var MemoryIndex = class {
289
108
  });
290
109
  });
291
110
  if (filter.limit !== void 0) {
292
- items.sort(queryItemOrder);
111
+ items.sort(compareEventsDesc);
293
112
  if (items.length > filter.limit) items.length = filter.limit;
294
113
  }
295
114
  items.sort(itemCompare);
@@ -379,12 +198,12 @@ var MemoryIndex = class {
379
198
  #indexInsert(event) {
380
199
  this.#byId.set(event.id, event);
381
200
  const { pubkey } = event;
382
- addToSet(this.#byPubkey, pubkey, event.id);
383
- addToSet(this.#byKind, event.kind, event.id);
384
- addToSet(this.#byKindPubkey, `${event.kind}:${pubkey}`, event.id);
201
+ addToSetMap(this.#byPubkey, pubkey, event.id);
202
+ addToSetMap(this.#byKind, event.kind, event.id);
203
+ addToSetMap(this.#byKindPubkey, `${event.kind}:${pubkey}`, event.id);
385
204
  for (const tag of event.tags) {
386
205
  if (tag[0] !== "e" && tag[0] !== "p" || tag[1] === void 0) continue;
387
- addToSet(this.#byEpTag, `${tag[0]}:${tag[1].toLowerCase()}`, event.id);
206
+ addToSetMap(this.#byEpTag, `${tag[0]}:${tag[1].toLowerCase()}`, event.id);
388
207
  }
389
208
  const addr = eventAddress(event);
390
209
  if (addr !== void 0) {
@@ -394,21 +213,20 @@ var MemoryIndex = class {
394
213
  this.#onInsert?.(event);
395
214
  }
396
215
  #indexRemove(id) {
397
- const key = id;
398
- this.#dropWatermarkId(key);
399
- const event = this.#byId.get(key);
216
+ this.#dropWatermarkId(id);
217
+ const event = this.#byId.get(id);
400
218
  if (!event) return false;
401
- this.#byId.delete(key);
219
+ this.#byId.delete(id);
402
220
  const { pubkey } = event;
403
- removeFromSet(this.#byPubkey, pubkey, key);
404
- removeFromSet(this.#byKind, event.kind, key);
405
- removeFromSet(this.#byKindPubkey, `${event.kind}:${pubkey}`, key);
221
+ removeFromSetMap(this.#byPubkey, pubkey, id);
222
+ removeFromSetMap(this.#byKind, event.kind, id);
223
+ removeFromSetMap(this.#byKindPubkey, `${event.kind}:${pubkey}`, id);
406
224
  for (const tag of event.tags) {
407
225
  if (tag[0] !== "e" && tag[0] !== "p" || tag[1] === void 0) continue;
408
- removeFromSet(this.#byEpTag, `${tag[0]}:${tag[1].toLowerCase()}`, key);
226
+ removeFromSetMap(this.#byEpTag, `${tag[0]}:${tag[1].toLowerCase()}`, id);
409
227
  }
410
228
  const addr = eventAddress(event);
411
- if (addr !== void 0 && this.#replaceable.get(addr) === key) this.#replaceable.delete(addr);
229
+ if (addr !== void 0 && this.#replaceable.get(addr) === id) this.#replaceable.delete(addr);
412
230
  this.#onRemove?.(event);
413
231
  return true;
414
232
  }
@@ -513,11 +331,7 @@ var MemoryIndex = class {
513
331
  this.#dropWatermark(addr);
514
332
  this.#watermarks.set(addr, watermark);
515
333
  this.#watermarkIds.set(watermark.id, addr);
516
- while (this.#watermarks.size > this.#maxWatermarks) {
517
- const oldest = this.#watermarks.keys().next().value;
518
- if (oldest === void 0) break;
519
- this.#dropWatermark(oldest);
520
- }
334
+ trimOldest(this.#watermarks, this.#maxWatermarks, (a) => this.#dropWatermark(a));
521
335
  }
522
336
  #dropWatermark(addr) {
523
337
  const watermark = this.#watermarks.get(addr);
@@ -534,25 +348,11 @@ var MemoryIndex = class {
534
348
  #trimTombstones() {
535
349
  const cap = this.#maxTombstones;
536
350
  if (cap === void 0) return;
537
- let excess = this.#deletion.ids.size - cap;
538
- for (const id of this.#deletion.ids) {
539
- if (excess <= 0) break;
540
- this.#deletion.ids.delete(id);
351
+ trimOldest(this.#deletion.ids, cap, (id) => {
541
352
  this.#deletion.pending.delete(id);
542
- excess--;
543
- }
544
- excess = this.#deletion.pending.size - cap;
545
- for (const id of this.#deletion.pending.keys()) {
546
- if (excess <= 0) break;
547
- this.#deletion.pending.delete(id);
548
- excess--;
549
- }
550
- excess = this.#deletion.coordinates.size - cap;
551
- for (const key of this.#deletion.coordinates.keys()) {
552
- if (excess <= 0) break;
553
- this.#deletion.coordinates.delete(key);
554
- excess--;
555
- }
353
+ });
354
+ trimOldest(this.#deletion.pending, cap);
355
+ trimOldest(this.#deletion.coordinates, cap);
556
356
  }
557
357
  };
558
358
  function visitEpTagIds(byEpTag, byId, name, values, seen, visit) {
@@ -568,24 +368,7 @@ function visitEpTagIds(byEpTag, byId, name, values, seen, visit) {
568
368
  }
569
369
  }
570
370
  }
571
- function addToSet(map, key, id) {
572
- let set = map.get(key);
573
- if (!set) {
574
- set = /* @__PURE__ */ new Set();
575
- map.set(key, set);
576
- }
577
- set.add(id);
578
- }
579
- function removeFromSet(map, key, id) {
580
- const set = map.get(key);
581
- if (!set) return;
582
- set.delete(id);
583
- if (set.size === 0) map.delete(key);
584
- }
585
- function queryItemOrder(a, b) {
586
- return compareEventsDesc(a, b);
587
- }
588
371
  //#endregion
589
- export { planDeletion as a, DeletionState as i, decidePut as n, outboxBoundKey as r, MemoryIndex as t };
372
+ export { MemoryIndex as t };
590
373
 
591
- //# sourceMappingURL=memory-index-Do1mz0WF.mjs.map
374
+ //# sourceMappingURL=memory-index-Cbc83vOZ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-index-Cbc83vOZ.mjs","names":[],"sources":["../src/storage/memory-index.ts"],"sourcesContent":["import { addToSetMap, removeFromSetMap, trimOldest } from \"../core/collections.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { compareEventsDesc, itemCompare, sortEvents } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { matchFilter } from \"../core/filter.ts\";\nimport { eventAddress, formatEventAddress, parseEventAddress } from \"../core/tag.ts\";\nimport { DeletionState } from \"./deletion.ts\";\nimport { applyPutMemory, decidePut, outboxBoundKey } from \"./put.ts\";\nimport type { PutLookup } from \"./put.ts\";\nimport type { NegentropyItem, OutboxBound, PutResult } from \"./types.ts\";\n\nexport type MemoryIndexOptions = {\n /**\n * FIFO cap applied independently to each deletion-state collection: tombstoned event ids, pending\n * e-tag ids, and coordinate tombstones. Default unbounded. Trade-off: once an entry is trimmed, a\n * re-arriving old deleted event or replaceable version can be accepted again.\n */\n maxTombstones?: number | undefined;\n /**\n * FIFO cap on replaceable/addressable winner watermarks recorded by {@link evict}. A watermark\n * keeps only `{ id, created_at }` per address so stale versions stay rejected after the winner's\n * body is evicted, and a re-put of the watermarked id re-inserts it. Default 0 (no watermark\n * retention); callers that evict should pass a bound. Once a watermark is trimmed, an older\n * evicted version can be accepted again — the same trade-off as deletion tombstones.\n */\n maxWatermarks?: number | undefined;\n /** Fires after every physical index insert (accept, replace, deletion event). */\n onInsert?: ((event: Event) => void) | undefined;\n /** Fires after every physical index remove (replace, delete, evict, remove, clear). */\n onRemove?: ((event: Event) => void) | undefined;\n};\n\n/**\n * Synchronous in-memory event index with NIP-01 replaceable / addressable / ephemeral handling and\n * NIP-09 deletion (kind 5) application. Shared by {@link MemoryEventStore} (async facade) and\n * `ReactiveEventStore`.\n */\nexport class MemoryIndex {\n readonly #byId = new Map<string, Event>();\n readonly #byPubkey = new Map<string, Set<string>>();\n readonly #byKind = new Map<number, Set<string>>();\n readonly #byKindPubkey = new Map<string, Set<string>>(); // `${kind}:${pubkey}` → ids\n readonly #byEpTag = new Map<string, Set<string>>(); // `${\"e\"|\"p\"}:${value.toLowerCase()}` → ids\n readonly #replaceable = new Map<string, string>(); // address -> event id\n // address -> last evicted winner; insertion order is the FIFO trim order\n readonly #watermarks = new Map<string, { id: string; created_at: number }>();\n // winner id -> address reverse index for O(1) watermark drops on deletion\n readonly #watermarkIds = new Map<string, string>();\n readonly #deletion = new DeletionState();\n readonly #outboxBounds = new Map<string, OutboxBound>();\n readonly #maxTombstones: number | undefined;\n readonly #maxWatermarks: number;\n readonly #onInsert: ((event: Event) => void) | undefined;\n readonly #onRemove: ((event: Event) => void) | undefined;\n\n constructor(opts?: MemoryIndexOptions) {\n this.#maxTombstones = opts?.maxTombstones;\n this.#maxWatermarks = opts?.maxWatermarks ?? 0;\n this.#onInsert = opts?.onInsert;\n this.#onRemove = opts?.onRemove;\n }\n\n put(raw: Event): PutResult {\n const lookup: PutLookup = {\n deletion: this.#deletion,\n getById: (id) => this.#byId.get(id),\n getReplaceable: (addr) => {\n const id = this.#replaceable.get(addr);\n const ev = id === undefined ? undefined : this.#byId.get(id);\n if (ev) {\n return { id: ev.id, created_at: ev.created_at };\n }\n const watermark = this.#watermarks.get(addr);\n return watermark === undefined ? undefined : { ...watermark, evicted: true };\n },\n };\n const decision = decidePut(raw, lookup);\n if (decision.action === \"delete\") {\n // Pending e-tag targets and coordinate tombstones never reach\n // indexRemove — drop their watermarks here.\n for (const p of decision.plan.pendingIds) {\n this.#dropWatermarkId(p.id);\n }\n for (const c of decision.plan.coordinates) {\n this.#dropWatermark(c.key);\n }\n }\n const result = applyPutMemory(\n {\n deletion: this.#deletion,\n indexInsert: (e) => this.#indexInsert(e),\n indexRemove: (id) => this.#indexRemove(id),\n },\n decision,\n );\n this.#trimTombstones();\n return result;\n }\n\n /** Sequential `put` in input order. */\n putMany(events: ReadonlyArray<Event>): PutResult[] {\n const results: PutResult[] = [];\n for (const event of events) {\n results.push(this.put(event));\n }\n return results;\n }\n\n get(id: string): Event | undefined {\n const key = id.toLowerCase();\n if (this.#deletion.ids.has(key)) {\n return undefined;\n }\n return this.#byId.get(key);\n }\n\n /** Current event at a `kind:pubkey:d` coordinate (replaceable or addressable). */\n getByAddress(address: string): Event | undefined {\n const coord = parseEventAddress(address);\n if (!coord) {\n return undefined;\n }\n const id = this.#replaceable.get(\n formatEventAddress(coord.kind, coord.pubkey, coord.identifier),\n );\n if (id === undefined) {\n return undefined;\n }\n if (this.#deletion.ids.has(id)) {\n return undefined;\n }\n return this.#byId.get(id);\n }\n\n query(filters: ReadonlyArray<Filter>): Event[] {\n const seen = new Set<string>();\n const events: Event[] = [];\n for (const filter of filters) {\n for (const event of this.#matchedEvents(filter)) {\n if (seen.has(event.id)) {\n continue;\n }\n seen.add(event.id);\n events.push(event);\n }\n }\n sortEvents(events);\n return events;\n }\n\n count(filters: ReadonlyArray<Filter>): number {\n const seen = new Set<string>();\n for (const filter of filters) {\n for (const event of this.#matchedEvents(filter)) {\n seen.add(event.id);\n }\n }\n return seen.size;\n }\n\n negentropyItems(filter: Filter): NegentropyItem[] {\n if (filter.limit === 0) {\n return [];\n }\n const items: NegentropyItem[] = [];\n this.#eachCandidate(filter, (event) => {\n if (this.#deletion.covers(event)) {\n return;\n }\n if (!matchFilter(filter, event)) {\n return;\n }\n items.push({ id: event.id, created_at: event.created_at });\n });\n if (filter.limit !== undefined) {\n items.sort(compareEventsDesc);\n if (items.length > filter.limit) {\n items.length = filter.limit;\n }\n }\n items.sort(itemCompare);\n return items;\n }\n\n /** Remove by id and tombstone (like a local NIP-09). Returns removed count. */\n remove(ids: ReadonlyArray<string>): number {\n let n = 0;\n for (const raw of ids) {\n const id = raw.toLowerCase();\n if (this.#indexRemove(id)) {\n n += 1;\n }\n this.#deletion.ids.add(id);\n this.#deletion.pending.delete(id);\n }\n this.#trimTombstones();\n return n;\n }\n\n /**\n * Remove by id without writing a tombstone (LRU eviction). When the evicted event is the current\n * winner of a replaceable/addressable address, a `{ id, created_at }` watermark is recorded so\n * stale versions stay rejected while the body is gone; the watermarks are FIFO-bounded by\n * `maxWatermarks`.\n */\n evict(ids: ReadonlyArray<string>): number {\n let n = 0;\n for (const raw of ids) {\n const id = raw.toLowerCase();\n const event = this.#byId.get(id);\n if (event === undefined) {\n continue;\n }\n const addr = eventAddress(event);\n const winner = addr !== undefined && this.#replaceable.get(addr) === id;\n if (this.#indexRemove(id)) {\n n += 1;\n }\n if (winner) {\n this.#setWatermark(addr, { id, created_at: event.created_at });\n }\n }\n return n;\n }\n\n /**\n * True when the id is tombstoned, or the coordinate has a deletion marker that still covers the\n * current event at that address: a replacement published after the marker's `until` clears the\n * deletion. Coordinates are matched on the canonical lowercase pubkey.\n */\n isDeleted(idOrAddress: string): boolean {\n if (this.#deletion.ids.has(idOrAddress.toLowerCase())) {\n return true;\n }\n const coord = parseEventAddress(idOrAddress);\n if (!coord) {\n return false;\n }\n const address = formatEventAddress(coord.kind, coord.pubkey, coord.identifier);\n const until = this.#deletion.coordinates.get(address);\n if (until === undefined) {\n return false;\n }\n const id = this.#replaceable.get(address);\n if (id === undefined) {\n return true;\n }\n const event = this.#byId.get(id);\n return event === undefined || event.created_at <= until;\n }\n\n getOutboxBound(pubkey: string, kind: number): OutboxBound | undefined {\n const persisted = this.#outboxBounds.get(outboxBoundKey(pubkey, kind));\n if (persisted) {\n return { oldest: persisted.oldest, newest: persisted.newest };\n }\n return this.#deriveOutboxBound(pubkey, kind);\n }\n\n setOutboxBound(pubkey: string, kind: number, bound: OutboxBound): void {\n this.#outboxBounds.set(outboxBoundKey(pubkey, kind), {\n oldest: bound.oldest,\n newest: bound.newest,\n });\n }\n\n clear(): void {\n if (this.#onRemove) {\n for (const event of this.#byId.values()) {\n this.#onRemove(event);\n }\n }\n this.#byId.clear();\n this.#byPubkey.clear();\n this.#byKind.clear();\n this.#byKindPubkey.clear();\n this.#byEpTag.clear();\n this.#replaceable.clear();\n this.#watermarks.clear();\n this.#watermarkIds.clear();\n this.#deletion.clear();\n this.#outboxBounds.clear();\n }\n\n get size(): number {\n return this.#byId.size;\n }\n\n #indexInsert(event: Event): void {\n this.#byId.set(event.id, event);\n const { pubkey } = event;\n addToSetMap(this.#byPubkey, pubkey, event.id);\n addToSetMap(this.#byKind, event.kind, event.id);\n addToSetMap(this.#byKindPubkey, `${event.kind}:${pubkey}`, event.id);\n for (const tag of event.tags) {\n if ((tag[0] !== \"e\" && tag[0] !== \"p\") || tag[1] === undefined) {\n continue;\n }\n addToSetMap(this.#byEpTag, `${tag[0]}:${tag[1].toLowerCase()}`, event.id);\n }\n const addr = eventAddress(event);\n if (addr !== undefined) {\n this.#replaceable.set(addr, event.id);\n this.#dropWatermark(addr);\n }\n this.#onInsert?.(event);\n }\n\n #indexRemove(id: string): boolean {\n // A removed/deleted id must not keep a stale watermark alive; eviction\n // re-records its winner watermark right after this call.\n this.#dropWatermarkId(id);\n const event = this.#byId.get(id);\n if (!event) {\n return false;\n }\n this.#byId.delete(id);\n const { pubkey } = event;\n removeFromSetMap(this.#byPubkey, pubkey, id);\n removeFromSetMap(this.#byKind, event.kind, id);\n removeFromSetMap(this.#byKindPubkey, `${event.kind}:${pubkey}`, id);\n for (const tag of event.tags) {\n if ((tag[0] !== \"e\" && tag[0] !== \"p\") || tag[1] === undefined) {\n continue;\n }\n removeFromSetMap(this.#byEpTag, `${tag[0]}:${tag[1].toLowerCase()}`, id);\n }\n const addr = eventAddress(event);\n if (addr !== undefined && this.#replaceable.get(addr) === id) {\n this.#replaceable.delete(addr);\n }\n this.#onRemove?.(event);\n return true;\n }\n\n #matchedEvents(filter: Filter): Event[] {\n if (filter.limit === 0) {\n return [];\n }\n const matched: Event[] = [];\n this.#eachCandidate(filter, (event) => {\n if (this.#deletion.covers(event)) {\n return;\n }\n if (!matchFilter(filter, event)) {\n return;\n }\n matched.push(event);\n });\n sortEvents(matched);\n return filter.limit === undefined ? matched : matched.slice(0, filter.limit);\n }\n\n #deriveOutboxBound(pubkey: string, kind: number): OutboxBound | undefined {\n const byPk = this.#byPubkey.get(pubkey.toLowerCase());\n const byKind = this.#byKind.get(kind);\n if (!byPk || !byKind) {\n return undefined;\n }\n let oldest: number | undefined;\n let newest: number | undefined;\n for (const id of byPk) {\n if (!byKind.has(id) || this.#deletion.ids.has(id)) {\n continue;\n }\n const event = this.#byId.get(id);\n if (!event || this.#deletion.covers(event)) {\n continue;\n }\n if (oldest === undefined || event.created_at < oldest) {\n oldest = event.created_at;\n }\n if (newest === undefined || event.created_at > newest) {\n newest = event.created_at;\n }\n }\n if (oldest === undefined || newest === undefined) {\n return undefined;\n }\n return { oldest, newest };\n }\n\n #eachCandidate(filter: Filter, visit: (event: Event) => void): void {\n if (filter.ids) {\n const seen = new Set<string>();\n for (const raw of filter.ids) {\n const event = this.#byId.get(raw.toLowerCase());\n if (!event || seen.has(event.id)) {\n continue;\n }\n seen.add(event.id);\n visit(event);\n }\n return;\n }\n\n if (filter.authors && filter.kinds) {\n const seen = new Set<string>();\n for (const pk of filter.authors) {\n const pubkey = pk.toLowerCase();\n for (const kind of filter.kinds) {\n const ids = this.#byKindPubkey.get(`${kind}:${pubkey}`);\n if (!ids) {\n continue;\n }\n for (const id of ids) {\n if (seen.has(id)) {\n continue;\n }\n seen.add(id);\n const event = this.#byId.get(id);\n if (event) {\n visit(event);\n }\n }\n }\n }\n return;\n }\n\n if (filter.authors) {\n const seen = new Set<string>();\n for (const pk of filter.authors) {\n const byPk = this.#byPubkey.get(pk.toLowerCase());\n if (!byPk) {\n continue;\n }\n for (const id of byPk) {\n if (seen.has(id)) {\n continue;\n }\n seen.add(id);\n const event = this.#byId.get(id);\n if (event) {\n visit(event);\n }\n }\n }\n return;\n }\n\n if (filter.kinds) {\n const seen = new Set<string>();\n for (const kind of filter.kinds) {\n const byKind = this.#byKind.get(kind);\n if (!byKind) {\n continue;\n }\n for (const id of byKind) {\n if (seen.has(id)) {\n continue;\n }\n seen.add(id);\n const event = this.#byId.get(id);\n if (event) {\n visit(event);\n }\n }\n }\n return;\n }\n\n const eTags = filter[\"#e\"];\n const pTags = filter[\"#p\"];\n if (eTags !== undefined || pTags !== undefined) {\n const seen = new Set<string>();\n visitEpTagIds(this.#byEpTag, this.#byId, \"e\", eTags, seen, visit);\n visitEpTagIds(this.#byEpTag, this.#byId, \"p\", pTags, seen, visit);\n return;\n }\n\n // #t/#d and other non-e/p tags are not indexed (e/p only). A generic tag\n // store is extra put/remove amp; hashtag-only queries scan #byId.\n for (const event of this.#byId.values()) {\n visit(event);\n }\n }\n\n #setWatermark(addr: string, watermark: { id: string; created_at: number }): void {\n if (this.#maxWatermarks === 0) {\n return;\n }\n this.#dropWatermark(addr);\n this.#watermarks.set(addr, watermark);\n this.#watermarkIds.set(watermark.id, addr);\n trimOldest(this.#watermarks, this.#maxWatermarks, (a) => this.#dropWatermark(a));\n }\n\n #dropWatermark(addr: string): void {\n const watermark = this.#watermarks.get(addr);\n if (watermark === undefined) {\n return;\n }\n this.#watermarks.delete(addr);\n this.#watermarkIds.delete(watermark.id);\n }\n\n #dropWatermarkId(id: string): void {\n const addr = this.#watermarkIds.get(id);\n if (addr === undefined) {\n return;\n }\n this.#watermarkIds.delete(id);\n this.#watermarks.delete(addr);\n }\n\n #trimTombstones(): void {\n const cap = this.#maxTombstones;\n if (cap === undefined) {\n return;\n }\n trimOldest(this.#deletion.ids, cap, (id) => {\n this.#deletion.pending.delete(id);\n });\n trimOldest(this.#deletion.pending, cap);\n trimOldest(this.#deletion.coordinates, cap);\n }\n}\n\nfunction visitEpTagIds(\n byEpTag: Map<string, Set<string>>,\n byId: Map<string, Event>,\n name: \"e\" | \"p\",\n values: ReadonlyArray<string> | undefined,\n seen: Set<string>,\n visit: (event: Event) => void,\n): void {\n if (values === undefined) {\n return;\n }\n for (const value of values) {\n const ids = byEpTag.get(`${name}:${value.toLowerCase()}`);\n if (!ids) {\n continue;\n }\n for (const id of ids) {\n if (seen.has(id)) {\n continue;\n }\n seen.add(id);\n const event = byId.get(id);\n if (event) {\n visit(event);\n }\n }\n }\n}\n"],"mappings":"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"}