@qntx/nostr 0.6.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.
Files changed (177) 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-9tjI6WQ3.mjs → builder-CdLE4J6O.mjs} +11 -17
  4. package/dist/builder-CdLE4J6O.mjs.map +1 -0
  5. package/dist/{builder-sSflu6nB.d.mts → builder-DgkqDylK.d.mts} +5 -6
  6. package/dist/{client-CadO5fAX.mjs → client-CILhdfsF.mjs} +104 -314
  7. package/dist/client-CILhdfsF.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/{error-BW7re1UN.d.mts → error-BD_0QFKT.d.mts} +2 -2
  15. package/dist/{error-CR8Urc84.d.mts → error-BquXDmIN.d.mts} +4 -4
  16. package/dist/{error-DrEIM7FO.mjs → error-L_QdmSnV.mjs} +2 -2
  17. package/dist/{error-DrEIM7FO.mjs.map → error-L_QdmSnV.mjs.map} +1 -1
  18. package/dist/{error-B__OMBRY.mjs → error-gtJvV6YS.mjs} +2 -2
  19. package/dist/{error-B__OMBRY.mjs.map → error-gtJvV6YS.mjs.map} +1 -1
  20. package/dist/{event-fU3Y01PR.d.mts → event-DJeb_DIL.d.mts} +4 -2
  21. package/dist/{event-D8Nfoh4K.mjs → event-DinQMmJ1.mjs} +16 -10
  22. package/dist/event-DinQMmJ1.mjs.map +1 -0
  23. package/dist/{filter-C0IMByE3.d.mts → filter-jI_9htF7.d.mts} +2 -2
  24. package/dist/{gossip-DpIvkBuj.d.mts → gossip-C7HLW5kI.d.mts} +35 -26
  25. package/dist/{gossip-oloDMWEj.mjs → gossip-CcTp9VpU.mjs} +23 -33
  26. package/dist/gossip-CcTp9VpU.mjs.map +1 -0
  27. package/dist/gossip.d.mts +1 -1
  28. package/dist/gossip.mjs +1 -1
  29. package/dist/{index-CvkvqB16.d.mts → index-DU9tRFTZ.d.mts} +7 -20
  30. package/dist/{index-q4MDce_r.d.mts → index-DiXwNn2M.d.mts} +6 -8
  31. package/dist/{index-CNl2q67H.d.mts → index-DocgD9K0.d.mts} +92 -167
  32. package/dist/{index-D1bqFHKB.d.mts → index-InQZpHyQ.d.mts} +18 -18
  33. package/dist/{index-MxC28S4P.d.mts → index-Tx15HGAD.d.mts} +26 -28
  34. package/dist/{index-lvXeER5t.d.mts → index-iLT8UzY3.d.mts} +19 -37
  35. package/dist/index.d.mts +23 -21
  36. package/dist/index.mjs +21 -19
  37. package/dist/{instance-DQE8BoWt.mjs → instance-BcKzxHIP.mjs} +26 -27
  38. package/dist/instance-BcKzxHIP.mjs.map +1 -0
  39. package/dist/{instance-CIca-ynn.d.mts → instance-DjsL8h84.d.mts} +5 -5
  40. package/dist/{key-Cv4_pN3x.d.mts → key-D_5OgGT7.d.mts} +11 -7
  41. package/dist/{key-Bps-LqX7.mjs → key-bshqfmZL.mjs} +13 -9
  42. package/dist/key-bshqfmZL.mjs.map +1 -0
  43. package/dist/{loaders-o6YXB_Ri.mjs → loaders-DD0XAGzw.mjs} +73 -82
  44. package/dist/loaders-DD0XAGzw.mjs.map +1 -0
  45. package/dist/loaders.d.mts +3 -3
  46. package/dist/loaders.mjs +2 -2
  47. package/dist/{memory-C2EVx0_O.mjs → memory-D_-5-T31.mjs} +2 -2
  48. package/dist/memory-D_-5-T31.mjs.map +1 -0
  49. package/dist/{memory-index-Do1mz0WF.mjs → memory-index-C6rujtoc.mjs} +30 -247
  50. package/dist/memory-index-C6rujtoc.mjs.map +1 -0
  51. package/dist/message-CWLcFTQ1.mjs +194 -0
  52. package/dist/message-CWLcFTQ1.mjs.map +1 -0
  53. package/dist/{message-iGPIqnn0.d.mts → message-CbBZOhq3.d.mts} +3 -3
  54. package/dist/{nip17-CKIufV98.d.mts → nip17-cTDFfFVt.d.mts} +5 -6
  55. package/dist/{nip19-BevCJZsp.d.mts → nip19-D1OnJ192.d.mts} +3 -9
  56. package/dist/{nip46-DDV4dC56.d.mts → nip46-DrsPI8EX.d.mts} +3 -5
  57. package/dist/nip51-DoPUvQ--.d.mts +87 -0
  58. package/dist/{nip59-zQgdLcEr.d.mts → nip59-BFIihLYY.d.mts} +3 -4
  59. package/dist/nip65-52HiwiyD.d.mts +23 -0
  60. package/dist/{nip77-DH4RhS5r.d.mts → nip77-C9QcZn7b.d.mts} +3 -4
  61. package/dist/nips/blossom.d.mts +12 -8
  62. package/dist/nips/blossom.mjs +30 -9
  63. package/dist/nips/blossom.mjs.map +1 -1
  64. package/dist/nips/nip04.mjs +17 -7
  65. package/dist/nips/nip04.mjs.map +1 -1
  66. package/dist/nips/nip05.d.mts +2 -3
  67. package/dist/nips/nip05.mjs +1 -4
  68. package/dist/nips/nip05.mjs.map +1 -1
  69. package/dist/nips/nip10.d.mts +3 -3
  70. package/dist/nips/nip10.mjs +3 -3
  71. package/dist/nips/nip11.d.mts +1 -1
  72. package/dist/nips/nip11.mjs +4 -4
  73. package/dist/nips/nip11.mjs.map +1 -1
  74. package/dist/nips/nip13.d.mts +7 -7
  75. package/dist/nips/nip13.mjs +32 -10
  76. package/dist/nips/nip13.mjs.map +1 -1
  77. package/dist/nips/nip17.d.mts +1 -1
  78. package/dist/nips/nip17.mjs +13 -33
  79. package/dist/nips/nip17.mjs.map +1 -1
  80. package/dist/nips/nip19.d.mts +2 -2
  81. package/dist/nips/nip19.mjs +16 -28
  82. package/dist/nips/nip19.mjs.map +1 -1
  83. package/dist/nips/nip21.d.mts +6 -1
  84. package/dist/nips/nip21.mjs +15 -5
  85. package/dist/nips/nip21.mjs.map +1 -1
  86. package/dist/nips/nip27.d.mts +4 -4
  87. package/dist/nips/nip27.mjs.map +1 -1
  88. package/dist/nips/nip42.d.mts +1 -1
  89. package/dist/nips/nip42.mjs +2 -1
  90. package/dist/nips/nip42.mjs.map +1 -1
  91. package/dist/nips/nip44.mjs +1 -1
  92. package/dist/nips/nip46.d.mts +2 -2
  93. package/dist/nips/nip46.mjs +15 -19
  94. package/dist/nips/nip46.mjs.map +1 -1
  95. package/dist/nips/nip49.d.mts +5 -5
  96. package/dist/nips/nip49.mjs +4 -4
  97. package/dist/nips/nip49.mjs.map +1 -1
  98. package/dist/nips/nip51.d.mts +2 -86
  99. package/dist/nips/nip51.mjs +7 -23
  100. package/dist/nips/nip51.mjs.map +1 -1
  101. package/dist/nips/nip57.d.mts +7 -5
  102. package/dist/nips/nip57.mjs +12 -17
  103. package/dist/nips/nip57.mjs.map +1 -1
  104. package/dist/nips/nip59.d.mts +1 -1
  105. package/dist/nips/nip59.mjs +16 -16
  106. package/dist/nips/nip59.mjs.map +1 -1
  107. package/dist/nips/nip65.d.mts +2 -2
  108. package/dist/nips/nip65.mjs +14 -37
  109. package/dist/nips/nip65.mjs.map +1 -1
  110. package/dist/nips/nip77.d.mts +1 -1
  111. package/dist/nips/nip77.mjs +33 -32
  112. package/dist/nips/nip77.mjs.map +1 -1
  113. package/dist/nips/nip98.d.mts +5 -5
  114. package/dist/nips/nip98.mjs +8 -8
  115. package/dist/nips/nip98.mjs.map +1 -1
  116. package/dist/{pool-DBIYMtwy.d.mts → pool-SzJKPIa3.d.mts} +96 -91
  117. package/dist/pool-YHRZgcQ3.mjs +596 -0
  118. package/dist/pool-YHRZgcQ3.mjs.map +1 -0
  119. package/dist/put-DS0yzVJx.mjs +187 -0
  120. package/dist/put-DS0yzVJx.mjs.map +1 -0
  121. package/dist/{reactive-7aqeip4A.d.mts → reactive-BJ1ggn4d.d.mts} +7 -22
  122. package/dist/{reactive-BfhPp9a3.mjs → reactive-BJRoVHxU.mjs} +80 -65
  123. package/dist/reactive-BJRoVHxU.mjs.map +1 -0
  124. package/dist/{pool-As20OGxB.mjs → relay-CvxPiQ_m.mjs} +178 -721
  125. package/dist/relay-CvxPiQ_m.mjs.map +1 -0
  126. package/dist/relay.d.mts +4 -4
  127. package/dist/relay.mjs +3 -2
  128. package/dist/serial-Bzpw-xz3.mjs +21 -0
  129. package/dist/serial-Bzpw-xz3.mjs.map +1 -0
  130. package/dist/{signer-CYQutDzD.mjs → signer-yW3slDC2.mjs} +12 -14
  131. package/dist/signer-yW3slDC2.mjs.map +1 -0
  132. package/dist/signer.d.mts +2 -2
  133. package/dist/signer.mjs +2 -2
  134. package/dist/{storage-Dky3wuoH.mjs → storage-CZ52ifcQ.mjs} +17 -42
  135. package/dist/storage-CZ52ifcQ.mjs.map +1 -0
  136. package/dist/storage.d.mts +3 -3
  137. package/dist/storage.mjs +4 -4
  138. package/dist/store.d.mts +1 -1
  139. package/dist/store.mjs +1 -1
  140. package/dist/testing.d.mts +18 -18
  141. package/dist/testing.mjs +9 -9
  142. package/dist/testing.mjs.map +1 -1
  143. package/dist/{types-2pCUprRU.d.mts → types-Cm-XMgtH.d.mts} +6 -6
  144. package/dist/{types-CRQEk0Oi.d.mts → types-DNmiscfd.d.mts} +2 -2
  145. package/dist/{util-LfQirgDk.mjs → util-yFYzlbrm.mjs} +29 -7
  146. package/dist/util-yFYzlbrm.mjs.map +1 -0
  147. package/dist/{verifier-t5WoWbtY.mjs → verifier-BTLOpo1n.mjs} +3 -3
  148. package/dist/verifier-BTLOpo1n.mjs.map +1 -0
  149. package/dist/verifier-CgTFpgsC.d.mts +22 -0
  150. package/dist/wasm.browser.d.mts +3 -3
  151. package/dist/wasm.browser.mjs +3 -3
  152. package/dist/wasm.d.mts +3 -3
  153. package/dist/wasm.mjs +3 -3
  154. package/dist/wasm.mjs.map +1 -1
  155. package/package.json +5 -9
  156. package/dist/abort-Ba8tDrOI.mjs.map +0 -1
  157. package/dist/builder-9tjI6WQ3.mjs.map +0 -1
  158. package/dist/client-CadO5fAX.mjs.map +0 -1
  159. package/dist/event-D8Nfoh4K.mjs.map +0 -1
  160. package/dist/gossip-oloDMWEj.mjs.map +0 -1
  161. package/dist/instance-DQE8BoWt.mjs.map +0 -1
  162. package/dist/key-Bps-LqX7.mjs.map +0 -1
  163. package/dist/loaders-o6YXB_Ri.mjs.map +0 -1
  164. package/dist/memory-C2EVx0_O.mjs.map +0 -1
  165. package/dist/memory-index-Do1mz0WF.mjs.map +0 -1
  166. package/dist/message-DcY6Fi9o.mjs +0 -193
  167. package/dist/message-DcY6Fi9o.mjs.map +0 -1
  168. package/dist/nip65-CjgmdpPB.d.mts +0 -26
  169. package/dist/nips/nip96.d.mts +0 -40
  170. package/dist/nips/nip96.mjs +0 -118
  171. package/dist/nips/nip96.mjs.map +0 -1
  172. package/dist/pool-As20OGxB.mjs.map +0 -1
  173. package/dist/reactive-BfhPp9a3.mjs.map +0 -1
  174. package/dist/signer-CYQutDzD.mjs.map +0 -1
  175. package/dist/storage-Dky3wuoH.mjs.map +0 -1
  176. package/dist/util-LfQirgDk.mjs.map +0 -1
  177. package/dist/verifier-t5WoWbtY.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"memory-index-Do1mz0WF.mjs","names":[],"sources":["../src/storage/deletion.ts","../src/storage/put.ts","../src/storage/memory-index.ts"],"sourcesContent":["import type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { eventAddress, parseEventAddress } from \"../core/tag.ts\";\nimport { isHex32 } from \"../core/util.ts\";\n\nexport type DeletionPlan = {\n /** Stored events validated as same-pubkey (not kind 5) and to be removed now. */\n removeIds: string[];\n /** Referenced ids not in the store; remember deletion pubkey until the event arrives. */\n pendingIds: Array<{ id: string; pubkey: string }>;\n /** Replaceable/addressable coordinates tombstoned up to `until` (deletion created_at). */\n coordinates: Array<{ key: string; until: number }>;\n};\n\n/**\n * NIP-09 application plan for a kind 5 event.\n *\n * - `e` tags: delete only when the target exists, is not itself kind 5, and shares pubkey.\n * - Unknown `e` ids are pending until the event arrives (client MUST validate pubkey).\n * - `a` tags: tombstone that coordinate up to this deletion's `created_at` when pubkey matches.\n * - Deleting a deletion request is a no-op.\n */\nexport function planDeletion(\n deletion: Pick<Event, \"pubkey\" | \"created_at\" | \"tags\">,\n getById: (id: string) => Pick<Event, \"id\" | \"pubkey\" | \"kind\"> | undefined,\n): DeletionPlan {\n const { pubkey } = deletion;\n const removeIds: string[] = [];\n const pendingIds: Array<{ id: string; pubkey: string }> = [];\n const coordinates: Array<{ key: string; until: number }> = [];\n const seenIds = new Set<string>();\n const seenCoords = new Set<string>();\n\n for (const tag of deletion.tags) {\n if (tag[0] === \"e\" && tag[1] !== undefined && isHex32(tag[1].toLowerCase())) {\n const id = tag[1].toLowerCase();\n if (seenIds.has(id)) {\n continue;\n }\n seenIds.add(id);\n const existing = getById(id);\n if (!existing) {\n pendingIds.push({ id, pubkey });\n continue;\n }\n if (existing.kind === Kind.EventDeletion) {\n continue;\n }\n if (existing.pubkey !== pubkey) {\n continue;\n }\n removeIds.push(id);\n continue;\n }\n\n if (tag[0] === \"a\" && tag[1] !== undefined) {\n const coord = parseEventAddress(tag[1]);\n if (!coord || coord.pubkey !== pubkey) {\n continue;\n }\n const key = `${coord.kind}:${coord.pubkey}:${coord.identifier}`;\n if (seenCoords.has(key)) {\n continue;\n }\n seenCoords.add(key);\n coordinates.push({ key, until: deletion.created_at });\n }\n }\n\n return { removeIds, pendingIds, coordinates };\n}\n\n/** In-memory NIP-09 tombstones shared by MemoryEventStore and IndexedDbEventStore. */\nexport class DeletionState {\n readonly ids: Set<string> = new Set<string>();\n readonly pending: Map<string, string> = new Map<string, string>();\n readonly coordinates: Map<string, number> = new Map<string, number>();\n\n clear(): void {\n this.ids.clear();\n this.pending.clear();\n this.coordinates.clear();\n }\n\n covers(event: Pick<Event, \"id\" | \"pubkey\" | \"kind\" | \"created_at\" | \"tags\">): boolean {\n if (this.ids.has(event.id)) {\n return true;\n }\n if (event.kind === Kind.EventDeletion) {\n return false;\n }\n const pendingPk = this.pending.get(event.id);\n if (pendingPk !== undefined && event.pubkey === pendingPk) {\n return true;\n }\n const addr = eventAddress(event);\n if (addr === undefined) {\n return false;\n }\n const until = this.coordinates.get(addr);\n return until !== undefined && event.created_at <= until;\n }\n\n absorb(plan: DeletionPlan): void {\n for (const id of plan.removeIds) {\n this.ids.add(id);\n this.pending.delete(id);\n }\n for (const p of plan.pendingIds) {\n if (!this.ids.has(p.id)) {\n this.pending.set(p.id, p.pubkey);\n }\n }\n for (const c of plan.coordinates) {\n const prev = this.coordinates.get(c.key) ?? Number.NEGATIVE_INFINITY;\n // Re-absorbing a coordinate moves it to the newest end so FIFO\n // tombstone caps evict the oldest marker, not the freshest.\n this.coordinates.delete(c.key);\n this.coordinates.set(c.key, Math.max(prev, c.until));\n }\n }\n}\n\nexport function coordinateRemovals(\n coordinates: ReadonlyArray<{ key: string; until: number }>,\n getCurrent: (key: string) => Pick<Event, \"id\" | \"created_at\"> | undefined,\n): string[] {\n const ids: string[] = [];\n for (const c of coordinates) {\n const current = getCurrent(c.key);\n if (current && current.created_at <= c.until) {\n ids.push(current.id);\n }\n }\n return ids;\n}\n","import type { Event } from \"../core/event.ts\";\nimport { isReplaceableWinner, validateSignedEvent } from \"../core/event.ts\";\nimport { isEphemeralKind, Kind } from \"../core/kind.ts\";\nimport { eventAddress } from \"../core/tag.ts\";\nimport { coordinateRemovals, planDeletion } from \"./deletion.ts\";\nimport type { DeletionPlan, DeletionState } from \"./deletion.ts\";\nimport type { PutResult } from \"./types.ts\";\n\nexport function outboxBoundKey(pubkey: string, kind: number): string {\n return `${pubkey.toLowerCase()}:${kind}`;\n}\n\nexport type PutDecision =\n | { action: \"skip\"; result: \"duplicate\" | \"ephemeral\" | \"rejected\" | \"invalid\"; event: Event }\n | { action: \"tombstone\"; result: \"duplicate\"; event: Event }\n | { action: \"delete\"; result: \"deleted\"; event: Event; plan: DeletionPlan; coordIds: string[] }\n | {\n action: \"insert\";\n result: \"accepted\" | \"replaced\";\n event: Event;\n address?: string;\n replaceId?: string;\n };\n\nexport type PutLookup = {\n deletion: DeletionState;\n getById: (\n id: string,\n ) => Pick<Event, \"id\" | \"pubkey\" | \"kind\" | \"created_at\" | \"tags\"> | undefined;\n /**\n * Current replaceable winner for an address. `evicted: true` marks an eviction watermark: the\n * winning event body was dropped, but its id/created_at still reject older versions, and a re-put\n * of the watermarked id re-inserts it.\n */\n getReplaceable: (\n address: string,\n ) => (Pick<Event, \"id\" | \"created_at\"> & { evicted?: boolean }) | undefined;\n};\n\nexport function decidePut(raw: Event, lookup: PutLookup): PutDecision {\n // Canonical-input precondition: non-canonical events are never indexed.\n if (!validateSignedEvent(raw)) {\n return { action: \"skip\", result: \"invalid\", event: raw };\n }\n const event = raw;\n if (lookup.deletion.ids.has(event.id) || lookup.getById(event.id)) {\n return { action: \"skip\", result: \"duplicate\", event };\n }\n if (event.kind === Kind.EventDeletion) {\n const plan = planDeletion(event, lookup.getById);\n const coordIds = coordinateRemovals(plan.coordinates, lookup.getReplaceable);\n return { action: \"delete\", result: \"deleted\", event, plan, coordIds };\n }\n if (lookup.deletion.covers(event)) {\n return { action: \"tombstone\", result: \"duplicate\", event };\n }\n if (isEphemeralKind(event.kind)) {\n return { action: \"skip\", result: \"ephemeral\", event };\n }\n const address = eventAddress(event);\n if (address !== undefined) {\n const prev = lookup.getReplaceable(address);\n // A stored incumbent rejects stale versions; an eviction watermark does\n // the same, except a re-put of the watermarked id re-inserts it.\n if (prev && prev.id !== event.id && !isReplaceableWinner(event, prev)) {\n return { action: \"skip\", result: \"rejected\", event };\n }\n if (prev !== undefined && prev.evicted !== true) {\n return { action: \"insert\", result: \"replaced\", event, address, replaceId: prev.id };\n }\n return { action: \"insert\", result: \"accepted\", event, address };\n }\n return { action: \"insert\", result: \"accepted\", event };\n}\n\nexport function applyPutMemory(\n s: {\n deletion: DeletionState;\n indexInsert: (event: Event) => void;\n indexRemove: (id: string) => boolean;\n },\n d: PutDecision,\n): PutResult {\n if (d.action === \"skip\") {\n return d.result;\n }\n if (d.action === \"tombstone\") {\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 s.deletion.absorb(d.plan);\n for (const id of d.plan.removeIds) {\n s.indexRemove(id);\n }\n for (const id of d.coordIds) {\n s.deletion.ids.add(id);\n s.indexRemove(id);\n }\n s.indexInsert(d.event);\n return \"deleted\";\n }\n if (d.replaceId !== undefined) {\n s.indexRemove(d.replaceId);\n }\n s.indexInsert(d.event);\n return d.result;\n}\n","import 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;\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;\n /** Fires after every physical index insert (accept, replace, deletion event). */\n onInsert?: (event: Event) => void;\n /** Fires after every physical index remove (replace, delete, evict, remove, clear). */\n onRemove?: (event: Event) => void;\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 === undefined ? undefined : (event) => opts.onInsert?.(event);\n this.#onRemove = opts?.onRemove === undefined ? undefined : (event) => opts.onRemove?.(event);\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 const key = id.toLowerCase();\n if (this.#deletion.ids.has(key)) {\n return undefined;\n }\n return this.#byId.get(key);\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(queryItemOrder);\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 addToSet(this.#byPubkey, pubkey, event.id);\n addToSet(this.#byKind, event.kind, event.id);\n addToSet(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 addToSet(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 const key = id;\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(key);\n const event = this.#byId.get(key);\n if (!event) {\n return false;\n }\n this.#byId.delete(key);\n const { pubkey } = event;\n removeFromSet(this.#byPubkey, pubkey, key);\n removeFromSet(this.#byKind, event.kind, key);\n removeFromSet(this.#byKindPubkey, `${event.kind}:${pubkey}`, key);\n for (const tag of event.tags) {\n if ((tag[0] !== \"e\" && tag[0] !== \"p\") || tag[1] === undefined) {\n continue;\n }\n removeFromSet(this.#byEpTag, `${tag[0]}:${tag[1].toLowerCase()}`, key);\n }\n const addr = eventAddress(event);\n if (addr !== undefined && this.#replaceable.get(addr) === key) {\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 while (this.#watermarks.size > this.#maxWatermarks) {\n const oldest = this.#watermarks.keys().next().value;\n if (oldest === undefined) {\n break;\n }\n this.#dropWatermark(oldest);\n }\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 let excess = this.#deletion.ids.size - cap;\n for (const id of this.#deletion.ids) {\n if (excess <= 0) {\n break;\n }\n this.#deletion.ids.delete(id);\n this.#deletion.pending.delete(id);\n excess--;\n }\n excess = this.#deletion.pending.size - cap;\n for (const id of this.#deletion.pending.keys()) {\n if (excess <= 0) {\n break;\n }\n this.#deletion.pending.delete(id);\n excess--;\n }\n excess = this.#deletion.coordinates.size - cap;\n for (const key of this.#deletion.coordinates.keys()) {\n if (excess <= 0) {\n break;\n }\n this.#deletion.coordinates.delete(key);\n excess--;\n }\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\nfunction addToSet<K>(map: Map<K, Set<string>>, key: K, id: string): void {\n let set = map.get(key);\n if (!set) {\n set = new Set();\n map.set(key, set);\n }\n set.add(id);\n}\n\nfunction removeFromSet<K>(map: Map<K, Set<string>>, key: K, id: string): void {\n const set = map.get(key);\n if (!set) {\n return;\n }\n set.delete(id);\n if (set.size === 0) {\n map.delete(key);\n }\n}\n\nfunction queryItemOrder(a: NegentropyItem, b: NegentropyItem): number {\n return compareEventsDesc(a, b);\n}\n"],"mappings":"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"}
@@ -1,193 +0,0 @@
1
- import { a as hexToBytes, c as isRecord, h as MessageError, i as bytesToHex } from "./util-LfQirgDk.mjs";
2
- import { m as validateSignedEvent } from "./event-D8Nfoh4K.mjs";
3
- //#region src/core/message.ts
4
- /** Validate a subscription id; throws {@link MessageError} unless 1..64 chars. */
5
- function assertSubscriptionId(id) {
6
- if (id.length === 0 || id.length > 64) throw new MessageError(`subscription id length must be 1..64`);
7
- return id;
8
- }
9
- /** Validate the given id, or mint a random 16-hex-char subscription id. */
10
- function createSubscriptionId(id) {
11
- if (id !== void 0) return assertSubscriptionId(id);
12
- const bytes = /* @__PURE__ */ new Uint8Array(8);
13
- crypto.getRandomValues(bytes);
14
- return bytesToHex(bytes);
15
- }
16
- const HLL_BYTES = 256;
17
- const HLL_HEX_LEN = HLL_BYTES * 2;
18
- const HEX_RE = /^[0-9a-fA-F]+$/;
19
- /**
20
- * Register-wise max of NIP-45 HyperLogLog sketches. Output is always lowercase 512 hex. Empty input
21
- * is the zero sketch (identity).
22
- */
23
- function mergeCountHll(hexes) {
24
- const merged = new Uint8Array(HLL_BYTES);
25
- for (const hex of hexes) {
26
- if (hex.length !== HLL_HEX_LEN || !HEX_RE.test(hex)) throw new MessageError("invalid NIP-45 HLL sketch: expected 512-char hex");
27
- const bytes = hexToBytes(hex);
28
- for (const [i, b] of bytes.entries()) {
29
- const m = merged[i];
30
- if (m !== void 0 && b > m) merged[i] = b;
31
- }
32
- }
33
- return bytesToHex(merged);
34
- }
35
- /** Serialize a client->relay message to its NIP-01 JSON wire form. */
36
- function encodeClientMessage(message) {
37
- return JSON.stringify(message);
38
- }
39
- /** Serialize a relay->client message to its NIP-01 JSON wire form. */
40
- function encodeRelayMessage(message) {
41
- return JSON.stringify(message);
42
- }
43
- /** Parse a client->relay JSON message; throws {@link MessageError} on malformed input. */
44
- function parseClientMessage(raw) {
45
- let data;
46
- try {
47
- data = JSON.parse(raw);
48
- } catch {
49
- throw new MessageError("client message is not valid JSON");
50
- }
51
- if (!Array.isArray(data) || data.length === 0 || typeof data[0] !== "string") throw new MessageError("client message must be a non-empty JSON array");
52
- const [type] = data;
53
- switch (type) {
54
- case "EVENT":
55
- if (data.length !== 2 || !validateSignedEvent(data[1])) throw new MessageError("invalid EVENT client message");
56
- return ["EVENT", data[1]];
57
- case "REQ":
58
- if (data.length < 3 || typeof data[1] !== "string") throw new MessageError("invalid REQ client message");
59
- return [
60
- "REQ",
61
- data[1],
62
- ...parseWireFilters(data.slice(2), "REQ")
63
- ];
64
- case "CLOSE":
65
- if (data.length !== 2 || typeof data[1] !== "string") throw new MessageError("invalid CLOSE client message");
66
- return ["CLOSE", data[1]];
67
- case "AUTH":
68
- if (data.length !== 2 || !validateSignedEvent(data[1])) throw new MessageError("invalid AUTH client message");
69
- return ["AUTH", data[1]];
70
- case "COUNT":
71
- if (data.length < 3 || typeof data[1] !== "string") throw new MessageError("invalid COUNT client message");
72
- return [
73
- "COUNT",
74
- data[1],
75
- ...parseWireFilters(data.slice(2), "COUNT")
76
- ];
77
- case "NEG-OPEN":
78
- if (data.length === 5) throw new MessageError("obsolete 5-element NEG-OPEN; expected [NEG-OPEN, id, filter, hex]");
79
- if (data.length !== 4 || typeof data[1] !== "string" || !isFilterObject(data[2]) || !isNegHex(data[3])) throw new MessageError("invalid NEG-OPEN client message");
80
- return [
81
- "NEG-OPEN",
82
- data[1],
83
- data[2],
84
- data[3].toLowerCase()
85
- ];
86
- case "NEG-MSG":
87
- if (data.length !== 3 || typeof data[1] !== "string" || !isNegHex(data[2])) throw new MessageError("invalid NEG-MSG client message");
88
- return [
89
- "NEG-MSG",
90
- data[1],
91
- data[2].toLowerCase()
92
- ];
93
- case "NEG-CLOSE":
94
- if (data.length !== 2 || typeof data[1] !== "string") throw new MessageError("invalid NEG-CLOSE client message");
95
- return ["NEG-CLOSE", data[1]];
96
- default: throw new MessageError(`unknown client message type: ${String(type)}`);
97
- }
98
- }
99
- /** Parse a relay->client JSON message; throws {@link MessageError} on malformed input. */
100
- function parseRelayMessage(raw) {
101
- let data;
102
- try {
103
- data = JSON.parse(raw);
104
- } catch {
105
- throw new MessageError("relay message is not valid JSON");
106
- }
107
- if (!Array.isArray(data) || data.length === 0 || typeof data[0] !== "string") throw new MessageError("relay message must be a non-empty JSON array");
108
- const items = data;
109
- const [type] = items;
110
- switch (type) {
111
- case "EVENT":
112
- if (data.length !== 3 || typeof data[1] !== "string" || !validateSignedEvent(data[2])) throw new MessageError("invalid EVENT relay message");
113
- return [
114
- "EVENT",
115
- data[1],
116
- data[2]
117
- ];
118
- case "OK":
119
- if (data.length !== 4 || typeof data[1] !== "string" || typeof data[2] !== "boolean" || typeof data[3] !== "string") throw new MessageError("invalid OK relay message");
120
- return [
121
- "OK",
122
- data[1],
123
- data[2],
124
- data[3]
125
- ];
126
- case "EOSE":
127
- if (data.length !== 2 || typeof data[1] !== "string") throw new MessageError("invalid EOSE relay message");
128
- return ["EOSE", data[1]];
129
- case "CLOSED":
130
- if (data.length !== 3 || typeof data[1] !== "string" || typeof data[2] !== "string") throw new MessageError("invalid CLOSED relay message");
131
- return [
132
- "CLOSED",
133
- data[1],
134
- data[2]
135
- ];
136
- case "NOTICE":
137
- if (data.length !== 2 || typeof data[1] !== "string") throw new MessageError("invalid NOTICE relay message");
138
- return ["NOTICE", data[1]];
139
- case "AUTH":
140
- if (data.length !== 2 || typeof data[1] !== "string") throw new MessageError("invalid AUTH relay message");
141
- return ["AUTH", data[1]];
142
- case "COUNT": {
143
- const payload = items.at(2);
144
- if (data.length !== 3 || typeof data[1] !== "string" || !isRecord(payload) || typeof payload["count"] !== "number") throw new MessageError("invalid COUNT relay message");
145
- const result = { count: payload["count"] };
146
- if (typeof payload["approximate"] === "boolean") result.approximate = payload["approximate"];
147
- const hll = parseCountHll(payload["hll"]);
148
- if (hll !== void 0) result.hll = hll;
149
- return [
150
- "COUNT",
151
- data[1],
152
- result
153
- ];
154
- }
155
- case "NEG-MSG":
156
- if (data.length !== 3 || typeof data[1] !== "string" || !isNegHex(data[2])) throw new MessageError("invalid NEG-MSG relay message");
157
- return [
158
- "NEG-MSG",
159
- data[1],
160
- data[2].toLowerCase()
161
- ];
162
- case "NEG-ERR":
163
- if (data.length !== 3 && data.length !== 4 || typeof data[1] !== "string" || typeof data[2] !== "string") throw new MessageError("invalid NEG-ERR relay message");
164
- return [
165
- "NEG-ERR",
166
- data[1],
167
- data[2]
168
- ];
169
- default: throw new MessageError(`unknown relay message type: ${String(type)}`);
170
- }
171
- }
172
- function parseCountHll(value) {
173
- if (typeof value !== "string" || value.length !== HLL_HEX_LEN || !HEX_RE.test(value)) return;
174
- return value.toLowerCase();
175
- }
176
- function parseWireFilters(items, kind) {
177
- const filters = [];
178
- for (const item of items) {
179
- if (!isFilterObject(item)) throw new MessageError(`invalid ${kind} filter`);
180
- filters.push(item);
181
- }
182
- return filters;
183
- }
184
- function isFilterObject(value) {
185
- return isRecord(value);
186
- }
187
- function isNegHex(value) {
188
- return typeof value === "string" && value.length > 0 && value.length % 2 === 0 && HEX_RE.test(value);
189
- }
190
- //#endregion
191
- export { mergeCountHll as a, encodeRelayMessage as i, createSubscriptionId as n, parseClientMessage as o, encodeClientMessage as r, parseRelayMessage as s, assertSubscriptionId as t };
192
-
193
- //# sourceMappingURL=message-DcY6Fi9o.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"message-DcY6Fi9o.mjs","names":["items"],"sources":["../src/core/message.ts"],"sourcesContent":["import { MessageError } from \"./error.ts\";\nimport type { Event } from \"./event.ts\";\nimport { validateSignedEvent } from \"./event.ts\";\nimport type { Filter } from \"./filter.ts\";\nimport { SUBSCRIPTION_ID_MAX_CHARS } from \"./limits.ts\";\nimport { bytesToHex, hexToBytes, isRecord } from \"./util.ts\";\n\n/** NIP-01 subscription id: 1..64 chars. */\nexport type SubscriptionId = string;\n\n/** Validate a subscription id; throws {@link MessageError} unless 1..64 chars. */\nexport function assertSubscriptionId(id: string): SubscriptionId {\n if (id.length === 0 || id.length > SUBSCRIPTION_ID_MAX_CHARS) {\n throw new MessageError(`subscription id length must be 1..${SUBSCRIPTION_ID_MAX_CHARS}`);\n }\n return id;\n}\n\n/** Validate the given id, or mint a random 16-hex-char subscription id. */\nexport function createSubscriptionId(id?: string): SubscriptionId {\n if (id !== undefined) {\n return assertSubscriptionId(id);\n }\n const bytes = new Uint8Array(8);\n crypto.getRandomValues(bytes);\n return bytesToHex(bytes);\n}\n\n/** Client → relay messages (NIP-01 + NIP-42 AUTH + NIP-45 COUNT + NIP-77). */\nexport type ClientMessage =\n | [\"EVENT\", Event]\n | [\"REQ\", SubscriptionId, ...Filter[]]\n | [\"CLOSE\", SubscriptionId]\n | [\"AUTH\", Event]\n | [\"COUNT\", SubscriptionId, ...Filter[]]\n | [\"NEG-OPEN\", SubscriptionId, Filter, string]\n | [\"NEG-MSG\", SubscriptionId, string]\n | [\"NEG-CLOSE\", SubscriptionId];\n\n/** Relay → client messages. */\nexport type RelayMessage =\n | [\"EVENT\", SubscriptionId, Event]\n | [\"OK\", string, boolean, string]\n | [\"EOSE\", SubscriptionId]\n | [\"CLOSED\", SubscriptionId, string]\n | [\"NOTICE\", string]\n | [\"AUTH\", string]\n | [\"COUNT\", SubscriptionId, CountResult]\n | [\"NEG-MSG\", SubscriptionId, string]\n | [\"NEG-ERR\", SubscriptionId, string];\n\n/** NIP-45 COUNT reply payload. */\nexport type CountResult = {\n count: number;\n approximate?: boolean;\n /**\n * Optional 512-char hex HyperLogLog sketch (256 registers). Merge sketches with `mergeCountHll`.\n * Estimation is unspecified by NIP-45 and not provided.\n */\n hll?: string;\n};\n\nconst HLL_BYTES = 256; // 512 hex chars (NIP-45; hex only, not base64)\nconst HLL_HEX_LEN = HLL_BYTES * 2;\nconst HEX_RE = /^[0-9a-fA-F]+$/;\n\n/**\n * Register-wise max of NIP-45 HyperLogLog sketches. Output is always lowercase 512 hex. Empty input\n * is the zero sketch (identity).\n */\nexport function mergeCountHll(hexes: ReadonlyArray<string>): string {\n const merged = new Uint8Array(HLL_BYTES);\n for (const hex of hexes) {\n if (hex.length !== HLL_HEX_LEN || !HEX_RE.test(hex)) {\n throw new MessageError(\"invalid NIP-45 HLL sketch: expected 512-char hex\");\n }\n const bytes = hexToBytes(hex);\n for (const [i, b] of bytes.entries()) {\n const m = merged[i];\n if (m !== undefined && b > m) {\n merged[i] = b;\n }\n }\n }\n return bytesToHex(merged);\n}\n\n/** Serialize a client->relay message to its NIP-01 JSON wire form. */\nexport function encodeClientMessage(message: ClientMessage): string {\n return JSON.stringify(message);\n}\n\n/** Serialize a relay->client message to its NIP-01 JSON wire form. */\nexport function encodeRelayMessage(message: RelayMessage): string {\n return JSON.stringify(message);\n}\n\n/** Parse a client->relay JSON message; throws {@link MessageError} on malformed input. */\nexport function parseClientMessage(raw: string): ClientMessage {\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch {\n throw new MessageError(\"client message is not valid JSON\");\n }\n if (!Array.isArray(data) || data.length === 0 || typeof data[0] !== \"string\") {\n throw new MessageError(\"client message must be a non-empty JSON array\");\n }\n const items: unknown[] = data;\n const [type] = items;\n switch (type) {\n case \"EVENT\":\n if (data.length !== 2 || !validateSignedEvent(data[1])) {\n throw new MessageError(\"invalid EVENT client message\");\n }\n return [\"EVENT\", data[1]];\n\n case \"REQ\":\n if (data.length < 3 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid REQ client message\");\n }\n return [\"REQ\", data[1], ...parseWireFilters(data.slice(2), \"REQ\")];\n\n case \"CLOSE\":\n if (data.length !== 2 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid CLOSE client message\");\n }\n return [\"CLOSE\", data[1]];\n\n case \"AUTH\":\n if (data.length !== 2 || !validateSignedEvent(data[1])) {\n throw new MessageError(\"invalid AUTH client message\");\n }\n return [\"AUTH\", data[1]];\n\n case \"COUNT\":\n if (data.length < 3 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid COUNT client message\");\n }\n return [\"COUNT\", data[1], ...parseWireFilters(data.slice(2), \"COUNT\")];\n\n case \"NEG-OPEN\":\n if (data.length === 5) {\n throw new MessageError(\"obsolete 5-element NEG-OPEN; expected [NEG-OPEN, id, filter, hex]\");\n }\n if (\n data.length !== 4 ||\n typeof data[1] !== \"string\" ||\n !isFilterObject(data[2]) ||\n !isNegHex(data[3])\n ) {\n throw new MessageError(\"invalid NEG-OPEN client message\");\n }\n return [\"NEG-OPEN\", data[1], data[2], data[3].toLowerCase()];\n\n case \"NEG-MSG\":\n if (data.length !== 3 || typeof data[1] !== \"string\" || !isNegHex(data[2])) {\n throw new MessageError(\"invalid NEG-MSG client message\");\n }\n return [\"NEG-MSG\", data[1], data[2].toLowerCase()];\n\n case \"NEG-CLOSE\":\n if (data.length !== 2 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid NEG-CLOSE client message\");\n }\n return [\"NEG-CLOSE\", data[1]];\n\n default:\n throw new MessageError(`unknown client message type: ${String(type)}`);\n }\n}\n\n/** Parse a relay->client JSON message; throws {@link MessageError} on malformed input. */\nexport function parseRelayMessage(raw: string): RelayMessage {\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch {\n throw new MessageError(\"relay message is not valid JSON\");\n }\n if (!Array.isArray(data) || data.length === 0 || typeof data[0] !== \"string\") {\n throw new MessageError(\"relay message must be a non-empty JSON array\");\n }\n const items: unknown[] = data;\n const [type] = items;\n switch (type) {\n case \"EVENT\":\n if (data.length !== 3 || typeof data[1] !== \"string\" || !validateSignedEvent(data[2])) {\n throw new MessageError(\"invalid EVENT relay message\");\n }\n return [\"EVENT\", data[1], data[2]];\n\n case \"OK\":\n if (\n data.length !== 4 ||\n typeof data[1] !== \"string\" ||\n typeof data[2] !== \"boolean\" ||\n typeof data[3] !== \"string\"\n ) {\n throw new MessageError(\"invalid OK relay message\");\n }\n return [\"OK\", data[1], data[2], data[3]];\n\n case \"EOSE\":\n if (data.length !== 2 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid EOSE relay message\");\n }\n return [\"EOSE\", data[1]];\n\n case \"CLOSED\":\n if (data.length !== 3 || typeof data[1] !== \"string\" || typeof data[2] !== \"string\") {\n throw new MessageError(\"invalid CLOSED relay message\");\n }\n return [\"CLOSED\", data[1], data[2]];\n\n case \"NOTICE\":\n if (data.length !== 2 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid NOTICE relay message\");\n }\n return [\"NOTICE\", data[1]];\n\n case \"AUTH\":\n if (data.length !== 2 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid AUTH relay message\");\n }\n return [\"AUTH\", data[1]];\n\n case \"COUNT\": {\n const payload = items.at(2);\n if (\n data.length !== 3 ||\n typeof data[1] !== \"string\" ||\n !isRecord(payload) ||\n typeof payload[\"count\"] !== \"number\"\n ) {\n throw new MessageError(\"invalid COUNT relay message\");\n }\n const result: CountResult = { count: payload[\"count\"] };\n if (typeof payload[\"approximate\"] === \"boolean\") {\n result.approximate = payload[\"approximate\"];\n }\n const hll = parseCountHll(payload[\"hll\"]);\n if (hll !== undefined) {\n result.hll = hll;\n }\n return [\"COUNT\", data[1], result];\n }\n case \"NEG-MSG\":\n if (data.length !== 3 || typeof data[1] !== \"string\" || !isNegHex(data[2])) {\n throw new MessageError(\"invalid NEG-MSG relay message\");\n }\n return [\"NEG-MSG\", data[1], data[2].toLowerCase()];\n\n case \"NEG-ERR\":\n if (\n (data.length !== 3 && data.length !== 4) ||\n typeof data[1] !== \"string\" ||\n typeof data[2] !== \"string\"\n ) {\n throw new MessageError(\"invalid NEG-ERR relay message\");\n }\n return [\"NEG-ERR\", data[1], data[2]];\n\n default:\n throw new MessageError(`unknown relay message type: ${String(type)}`);\n }\n}\n\nfunction parseCountHll(value: unknown): string | undefined {\n if (typeof value !== \"string\" || value.length !== HLL_HEX_LEN || !HEX_RE.test(value)) {\n return undefined;\n }\n return value.toLowerCase();\n}\n\nfunction parseWireFilters(items: unknown[], kind: string): Filter[] {\n const filters: Filter[] = [];\n for (const item of items) {\n if (!isFilterObject(item)) {\n throw new MessageError(`invalid ${kind} filter`);\n }\n filters.push(item);\n }\n return filters;\n}\n\nfunction isFilterObject(value: unknown): value is Filter {\n return isRecord(value);\n}\n\nfunction isNegHex(value: unknown): value is string {\n return (\n typeof value === \"string\" && value.length > 0 && value.length % 2 === 0 && HEX_RE.test(value)\n );\n}\n"],"mappings":"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"}
@@ -1,26 +0,0 @@
1
- import { _ as Tag, t as Event } from "./event-fU3Y01PR.mjs";
2
- import { t as EventBuilder } from "./builder-sSflu6nB.mjs";
3
- //#region src/nips/nip65.d.ts
4
- /** NIP-65 `r` tag marker: read-only, write-only, or unmarked (both). */
5
- type RelayMarker = "read" | "write" | "readwrite";
6
- /** One NIP-65 relay-list entry: normalized URL plus read/write flags. */
7
- type RelayListItem = {
8
- url: string;
9
- read: boolean;
10
- write: boolean;
11
- };
12
- /** The NIP-65 marker an item's read/write flags map to. */
13
- declare function markerOf(item: RelayListItem): RelayMarker;
14
- /** Parse a kind:10002 NIP-65 event into relay list entries. */
15
- declare function parseRelayList(event: Event): RelayListItem[];
16
- /** Encode relay list items as NIP-65 `r` tags. */
17
- declare function relayListToTags(items: RelayListItem[]): Tag[];
18
- /** Build an unsigned kind:10002 EventBuilder from relay list items. */
19
- declare function relayListEventBuilder(items: RelayListItem[]): EventBuilder;
20
- /** URLs of the read-enabled items. */
21
- declare function readRelays(items: RelayListItem[]): string[];
22
- /** URLs of the write-enabled items. */
23
- declare function writeRelays(items: RelayListItem[]): string[];
24
- //#endregion
25
- export { readRelays as a, writeRelays as c, parseRelayList as i, RelayMarker as n, relayListEventBuilder as o, markerOf as r, relayListToTags as s, RelayListItem as t };
26
- //# sourceMappingURL=nip65-CjgmdpPB.d.mts.map
@@ -1,40 +0,0 @@
1
- import { a as NostrError } from "../error-CR8Urc84.mjs";
2
- import { t as ManualFetch } from "../http-C4HwIu4B.mjs";
3
- //#region src/nips/nip96.d.ts
4
- export type Nip96Fetch = ManualFetch;
5
- export type Nip96ServerInfo = {
6
- api_url: string;
7
- download_url?: string;
8
- delegated_to_url?: string;
9
- content_types?: string[];
10
- };
11
- export type Nip96UploadResult = {
12
- status: "success";
13
- url: string;
14
- tags: string[][];
15
- } | {
16
- status: "processing";
17
- processingUrl: string;
18
- tags: string[][];
19
- };
20
- export declare class Nip96Error extends NostrError {
21
- name: string;
22
- }
23
- /**
24
- * Parse `{ status, processing_url, nip94_event.tags }`. A `url` tag is required unless the response
25
- * reports delayed processing (`status: "processing"` or HTTP 202 with `processing_url`).
26
- */
27
- export declare function parseNip96UploadResponse(json: unknown, httpStatus?: number): Nip96UploadResult;
28
- /** GET `${service}/.well-known/nostr/nip96.json`; follows `delegated_to_url` one hop. */
29
- export declare function fetchNip96Info(serviceUrl: string, opts?: {
30
- fetch?: Nip96Fetch;
31
- signal?: AbortSignal;
32
- }): Promise<Nip96ServerInfo>;
33
- /** POST `apiUrl` as multipart `file`. `authorization` is a prebuilt NIP-98 header. */
34
- export declare function uploadNip96(apiUrl: string, file: Blob, authorization: string, opts?: {
35
- fetch?: Nip96Fetch;
36
- signal?: AbortSignal;
37
- extraFields?: Record<string, string>;
38
- }): Promise<Nip96UploadResult>;
39
- //#endregion
40
- //# sourceMappingURL=nip96.d.mts.map
@@ -1,118 +0,0 @@
1
- import { c as isRecord, g as NostrError } from "../util-LfQirgDk.mjs";
2
- import { n as requireGlobalFetch, t as fetchManual } from "../http-CuazNRIP.mjs";
3
- //#region src/nips/nip96.ts
4
- /**
5
- * NIP-96: HTTP file storage (unrecommended; NIP-B7 preferred). Authorization is a prebuilt NIP-98
6
- * header — this module does not import nip98.
7
- *
8
- * @see https://github.com/nostr-protocol/nips/blob/master/96.md
9
- */
10
- const WELL_KNOWN_PATH = "/.well-known/nostr/nip96.json";
11
- var Nip96Error = class extends NostrError {
12
- name = "Nip96Error";
13
- };
14
- function serverInfoUrl(serviceUrl) {
15
- return `${serviceUrl.replace(/\/+$/, "")}${WELL_KNOWN_PATH}`;
16
- }
17
- function errorMessageFromBody(json) {
18
- if (!isRecord(json)) return;
19
- const { message } = json;
20
- if (typeof message !== "string") return;
21
- return message.trim() || void 0;
22
- }
23
- /** Non-OK: prefer NIP-96 JSON `message`; never require a `url` tag. */
24
- async function throwHttpError(prefix, res) {
25
- let detail;
26
- try {
27
- detail = errorMessageFromBody(await res.json());
28
- } catch {}
29
- throw new Nip96Error(detail === void 0 || detail === "" ? `${prefix} HTTP ${res.status}` : `${prefix} HTTP ${res.status}: ${detail}`);
30
- }
31
- function parseNip96ServerInfo(json) {
32
- if (!isRecord(json)) throw new Nip96Error("NIP-96 server info must be a JSON object");
33
- const raw = json;
34
- if (typeof raw["api_url"] !== "string") throw new Nip96Error("missing api_url");
35
- const info = { api_url: raw["api_url"] };
36
- if (typeof raw["download_url"] === "string") info.download_url = raw["download_url"];
37
- if (typeof raw["delegated_to_url"] === "string") info.delegated_to_url = raw["delegated_to_url"];
38
- if (Array.isArray(raw["content_types"]) && raw["content_types"].every((t) => typeof t === "string")) info.content_types = [...raw["content_types"]];
39
- return info;
40
- }
41
- function parseStatus(value) {
42
- return value === "success" || value === "error" || value === "processing" ? value : void 0;
43
- }
44
- /**
45
- * Parse `{ status, processing_url, nip94_event.tags }`. A `url` tag is required unless the response
46
- * reports delayed processing (`status: "processing"` or HTTP 202 with `processing_url`).
47
- */
48
- function parseNip96UploadResponse(json, httpStatus = 200) {
49
- if (!isRecord(json)) throw new Nip96Error("NIP-96 upload response must be a JSON object");
50
- const raw = json;
51
- const status = parseStatus(raw["status"]);
52
- const rawProcessingUrl = raw["processing_url"];
53
- const processingUrl = typeof rawProcessingUrl === "string" && rawProcessingUrl !== "" ? rawProcessingUrl : void 0;
54
- const event = raw["nip94_event"];
55
- const rawTags = isRecord(event) ? event["tags"] : void 0;
56
- const tags = [];
57
- if (Array.isArray(rawTags)) for (const tag of rawTags) {
58
- if (!Array.isArray(tag) || !tag.every((item) => typeof item === "string")) throw new Nip96Error("invalid nip94_event.tags");
59
- tags.push([...tag]);
60
- }
61
- const url = tags.find((tag) => tag[0] === "url")?.at(1);
62
- if (url !== void 0 && url !== "") return {
63
- status: "success",
64
- url,
65
- tags
66
- };
67
- if ((httpStatus === 202 || status === "processing") && processingUrl !== void 0) return {
68
- status: "processing",
69
- processingUrl,
70
- tags
71
- };
72
- throw new Nip96Error("upload response without url");
73
- }
74
- async function fetchServerInfo(fetchImpl, url, signal) {
75
- const res = await fetchManual(fetchImpl, url, { signal }, (cause) => new Nip96Error(`NIP-96 server info request failed: ${url}`, { cause: cause instanceof Error ? cause : void 0 }));
76
- if (!res.ok) await throwHttpError("NIP-96 server info", res);
77
- let json;
78
- try {
79
- json = await res.json();
80
- } catch (error) {
81
- throw new Nip96Error("invalid NIP-96 server info JSON", { cause: error instanceof Error ? error : void 0 });
82
- }
83
- return parseNip96ServerInfo(json);
84
- }
85
- /** GET `${service}/.well-known/nostr/nip96.json`; follows `delegated_to_url` one hop. */
86
- async function fetchNip96Info(serviceUrl, opts) {
87
- const fetchImpl = opts?.fetch ?? requireGlobalFetch(() => new Nip96Error("no fetch implementation available; pass opts.fetch"));
88
- const info = await fetchServerInfo(fetchImpl, serverInfoUrl(serviceUrl), opts?.signal);
89
- if (info.delegated_to_url === void 0 || info.delegated_to_url === "") return info;
90
- const delegated = await fetchServerInfo(fetchImpl, serverInfoUrl(info.delegated_to_url), opts?.signal);
91
- if (delegated.delegated_to_url !== void 0 && delegated.delegated_to_url !== "") throw new Nip96Error("NIP-96 delegation exceeded one hop");
92
- return delegated;
93
- }
94
- /** POST `apiUrl` as multipart `file`. `authorization` is a prebuilt NIP-98 header. */
95
- async function uploadNip96(apiUrl, file, authorization, opts) {
96
- const fetchImpl = opts?.fetch ?? requireGlobalFetch(() => new Nip96Error("no fetch implementation available; pass opts.fetch"));
97
- const body = new FormData();
98
- body.append("file", file);
99
- if (opts?.extraFields) for (const [name, value] of Object.entries(opts.extraFields)) body.append(name, value);
100
- const res = await fetchManual(fetchImpl, apiUrl, {
101
- method: "POST",
102
- headers: { Authorization: authorization },
103
- body,
104
- signal: opts?.signal
105
- }, (cause) => new Nip96Error(`NIP-96 upload request failed: ${apiUrl}`, { cause: cause instanceof Error ? cause : void 0 }));
106
- if (!res.ok) await throwHttpError("NIP-96 upload", res);
107
- let json;
108
- try {
109
- json = await res.json();
110
- } catch (error) {
111
- throw new Nip96Error("invalid NIP-96 upload response JSON", { cause: error instanceof Error ? error : void 0 });
112
- }
113
- return parseNip96UploadResponse(json, res.status);
114
- }
115
- //#endregion
116
- export { Nip96Error, fetchNip96Info, parseNip96UploadResponse, uploadNip96 };
117
-
118
- //# sourceMappingURL=nip96.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"nip96.mjs","names":[],"sources":["../../src/nips/nip96.ts"],"sourcesContent":["/**\n * NIP-96: HTTP file storage (unrecommended; NIP-B7 preferred). Authorization is a prebuilt NIP-98\n * header — this module does not import nip98.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/96.md\n */\nimport { NostrError } from \"../core/error.ts\";\nimport { isRecord } from \"../core/util.ts\";\nimport { fetchManual, requireGlobalFetch } from \"./http.ts\";\nimport type { ManualFetch } from \"./http.ts\";\n\nconst WELL_KNOWN_PATH = \"/.well-known/nostr/nip96.json\";\n\nexport type Nip96Fetch = ManualFetch;\n\nexport type Nip96ServerInfo = {\n api_url: string;\n download_url?: string;\n delegated_to_url?: string;\n content_types?: string[];\n};\n\nexport type Nip96UploadResult =\n | { status: \"success\"; url: string; tags: string[][] }\n | { status: \"processing\"; processingUrl: string; tags: string[][] };\n\nexport class Nip96Error extends NostrError {\n override name = \"Nip96Error\";\n}\n\nfunction serverInfoUrl(serviceUrl: string): string {\n return `${serviceUrl.replace(/\\/+$/, \"\")}${WELL_KNOWN_PATH}`;\n}\n\nfunction errorMessageFromBody(json: unknown): string | undefined {\n if (!isRecord(json)) {\n return undefined;\n }\n const { message } = json;\n if (typeof message !== \"string\") {\n return undefined;\n }\n const text = message.trim();\n return text || undefined;\n}\n\n/** Non-OK: prefer NIP-96 JSON `message`; never require a `url` tag. */\nasync function throwHttpError(\n prefix: string,\n res: Awaited<ReturnType<Nip96Fetch>>,\n): Promise<never> {\n let detail: string | undefined;\n try {\n detail = errorMessageFromBody(await res.json());\n } catch {\n // body is optional on error\n }\n throw new Nip96Error(\n detail === undefined || detail === \"\"\n ? `${prefix} HTTP ${res.status}`\n : `${prefix} HTTP ${res.status}: ${detail}`,\n );\n}\n\nfunction parseNip96ServerInfo(json: unknown): Nip96ServerInfo {\n if (!isRecord(json)) {\n throw new Nip96Error(\"NIP-96 server info must be a JSON object\");\n }\n const raw = json;\n if (typeof raw[\"api_url\"] !== \"string\") {\n throw new Nip96Error(\"missing api_url\");\n }\n\n const info: Nip96ServerInfo = { api_url: raw[\"api_url\"] };\n if (typeof raw[\"download_url\"] === \"string\") {\n info.download_url = raw[\"download_url\"];\n }\n if (typeof raw[\"delegated_to_url\"] === \"string\") {\n info.delegated_to_url = raw[\"delegated_to_url\"];\n }\n if (\n Array.isArray(raw[\"content_types\"]) &&\n raw[\"content_types\"].every((t) => typeof t === \"string\")\n ) {\n info.content_types = [...raw[\"content_types\"]];\n }\n return info;\n}\n\nfunction parseStatus(value: unknown): \"success\" | \"error\" | \"processing\" | undefined {\n return value === \"success\" || value === \"error\" || value === \"processing\" ? value : undefined;\n}\n\n/**\n * Parse `{ status, processing_url, nip94_event.tags }`. A `url` tag is required unless the response\n * reports delayed processing (`status: \"processing\"` or HTTP 202 with `processing_url`).\n */\nexport function parseNip96UploadResponse(json: unknown, httpStatus = 200): Nip96UploadResult {\n if (!isRecord(json)) {\n throw new Nip96Error(\"NIP-96 upload response must be a JSON object\");\n }\n const raw = json;\n const status = parseStatus(raw[\"status\"]);\n const rawProcessingUrl = raw[\"processing_url\"];\n const processingUrl =\n typeof rawProcessingUrl === \"string\" && rawProcessingUrl !== \"\" ? rawProcessingUrl : undefined;\n const event = raw[\"nip94_event\"];\n const rawTags: unknown = isRecord(event) ? event[\"tags\"] : undefined;\n\n const tags: string[][] = [];\n if (Array.isArray(rawTags)) {\n for (const tag of rawTags) {\n if (!Array.isArray(tag) || !tag.every((item): item is string => typeof item === \"string\")) {\n throw new Nip96Error(\"invalid nip94_event.tags\");\n }\n tags.push([...tag]);\n }\n }\n\n const url = tags.find((tag) => tag[0] === \"url\")?.at(1);\n if (url !== undefined && url !== \"\") {\n return { status: \"success\", url, tags };\n }\n if ((httpStatus === 202 || status === \"processing\") && processingUrl !== undefined) {\n return { status: \"processing\", processingUrl, tags };\n }\n throw new Nip96Error(\"upload response without url\");\n}\n\nasync function fetchServerInfo(\n fetchImpl: Nip96Fetch,\n url: string,\n signal?: AbortSignal,\n): Promise<Nip96ServerInfo> {\n const res = await fetchManual(\n fetchImpl,\n url,\n { signal },\n (cause) =>\n new Nip96Error(`NIP-96 server info request failed: ${url}`, {\n cause: cause instanceof Error ? cause : undefined,\n }),\n );\n if (!res.ok) {\n await throwHttpError(\"NIP-96 server info\", res);\n }\n\n let json: unknown;\n try {\n json = await res.json();\n } catch (error) {\n throw new Nip96Error(\"invalid NIP-96 server info JSON\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n return parseNip96ServerInfo(json);\n}\n\n/** GET `${service}/.well-known/nostr/nip96.json`; follows `delegated_to_url` one hop. */\nexport async function fetchNip96Info(\n serviceUrl: string,\n opts?: { fetch?: Nip96Fetch; signal?: AbortSignal },\n): Promise<Nip96ServerInfo> {\n const fetchImpl =\n opts?.fetch ??\n requireGlobalFetch(() => new Nip96Error(\"no fetch implementation available; pass opts.fetch\"));\n\n const info = await fetchServerInfo(fetchImpl, serverInfoUrl(serviceUrl), opts?.signal);\n if (info.delegated_to_url === undefined || info.delegated_to_url === \"\") {\n return info;\n }\n\n const delegated = await fetchServerInfo(\n fetchImpl,\n serverInfoUrl(info.delegated_to_url),\n opts?.signal,\n );\n if (delegated.delegated_to_url !== undefined && delegated.delegated_to_url !== \"\") {\n throw new Nip96Error(\"NIP-96 delegation exceeded one hop\");\n }\n return delegated;\n}\n\n/** POST `apiUrl` as multipart `file`. `authorization` is a prebuilt NIP-98 header. */\nexport async function uploadNip96(\n apiUrl: string,\n file: Blob,\n authorization: string,\n opts?: { fetch?: Nip96Fetch; signal?: AbortSignal; extraFields?: Record<string, string> },\n): Promise<Nip96UploadResult> {\n const fetchImpl =\n opts?.fetch ??\n requireGlobalFetch(() => new Nip96Error(\"no fetch implementation available; pass opts.fetch\"));\n const body = new FormData();\n body.append(\"file\", file);\n if (opts?.extraFields) {\n for (const [name, value] of Object.entries(opts.extraFields)) {\n body.append(name, value);\n }\n }\n\n const res = await fetchManual(\n fetchImpl,\n apiUrl,\n {\n method: \"POST\",\n headers: { Authorization: authorization },\n body,\n signal: opts?.signal,\n },\n (cause) =>\n new Nip96Error(`NIP-96 upload request failed: ${apiUrl}`, {\n cause: cause instanceof Error ? cause : undefined,\n }),\n );\n if (!res.ok) {\n await throwHttpError(\"NIP-96 upload\", res);\n }\n\n let json: unknown;\n try {\n json = await res.json();\n } catch (error) {\n throw new Nip96Error(\"invalid NIP-96 upload response JSON\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n return parseNip96UploadResponse(json, res.status);\n}\n"],"mappings":"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"}