@qntx/nostr 0.14.0 → 0.15.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 (154) hide show
  1. package/dist/{builder-CGgM_QHT.mjs → builder-70CC2EBq.mjs} +4 -4
  2. package/dist/{builder-CGgM_QHT.mjs.map → builder-70CC2EBq.mjs.map} +1 -1
  3. package/dist/{builder-C72nJITG.d.mts → builder-Dk00TdNf.d.mts} +2 -2
  4. package/dist/{client-COkEihj_.mjs → client-BXmvZ7vU.mjs} +45 -30
  5. package/dist/client-BXmvZ7vU.mjs.map +1 -0
  6. package/dist/client.d.mts +1 -1
  7. package/dist/client.mjs +1 -2
  8. package/dist/core.d.mts +8 -7
  9. package/dist/core.mjs +8 -8
  10. package/dist/{error-Dyb89ahb.mjs → error-CoP2myne.mjs} +2 -2
  11. package/dist/{error-Dyb89ahb.mjs.map → error-CoP2myne.mjs.map} +1 -1
  12. package/dist/{error-BPqAU5RB.d.mts → error-DE1UCZ2J.d.mts} +6 -2
  13. package/dist/{error-BjSUsx6M.d.mts → error-IHMcLy8-.d.mts} +2 -2
  14. package/dist/{error-QyECUDIp.mjs → error-VDn7Nv-6.mjs} +2 -2
  15. package/dist/{error-QyECUDIp.mjs.map → error-VDn7Nv-6.mjs.map} +1 -1
  16. package/dist/{event-DvM1f03b.mjs → event-DHtcm0jM.mjs} +17 -6
  17. package/dist/event-DHtcm0jM.mjs.map +1 -0
  18. package/dist/{filter-3jOh4uLN.mjs → filter-BbShkmc4.mjs} +34 -28
  19. package/dist/filter-BbShkmc4.mjs.map +1 -0
  20. package/dist/{filter-oYG2zjJ3.d.mts → filter-w1R-mU63.d.mts} +8 -6
  21. package/dist/{gossip-BvCItMVZ.d.mts → gossip-C-7WhJ7v.d.mts} +3 -3
  22. package/dist/{gossip-CRyhK6E6.mjs → gossip-CejMm_QM.mjs} +13 -11
  23. package/dist/gossip-CejMm_QM.mjs.map +1 -0
  24. package/dist/gossip.d.mts +1 -1
  25. package/dist/gossip.mjs +1 -2
  26. package/dist/{index-B2JlP2Jf.d.mts → index-11znTLM0.d.mts} +4 -4
  27. package/dist/{index-DzHmhHqD.d.mts → index-B7nMjbyt.d.mts} +3 -3
  28. package/dist/{index-muydfuYD.d.mts → index-BuNI8JH_.d.mts} +13 -13
  29. package/dist/{index-Dpj_MUxb.d.mts → index-C5QomO2D.d.mts} +10 -3
  30. package/dist/{index-DCobP2Jj.d.mts → index-DDZjqV48.d.mts} +10 -10
  31. package/dist/{index-CcgmG3F6.d.mts → index-DYk24u9o.d.mts} +12 -7
  32. package/dist/index.d.mts +21 -20
  33. package/dist/index.mjs +19 -19
  34. package/dist/{key-D_oEutQ6.mjs → key-Bt0ow7mI.mjs} +33 -11
  35. package/dist/key-Bt0ow7mI.mjs.map +1 -0
  36. package/dist/{key-DojW2QZl.d.mts → key-E0AJKMRB.d.mts} +19 -11
  37. package/dist/{loaders-BRh2riV9.mjs → loaders-COGGrZY4.mjs} +7 -7
  38. package/dist/loaders-COGGrZY4.mjs.map +1 -0
  39. package/dist/loaders.d.mts +2 -2
  40. package/dist/loaders.mjs +1 -2
  41. package/dist/{memory-D_GdEQYG.mjs → memory-Dq4dg-U4.mjs} +2 -2
  42. package/dist/{memory-D_GdEQYG.mjs.map → memory-Dq4dg-U4.mjs.map} +1 -1
  43. package/dist/{memory-index-3GSrPIWj.mjs → memory-index-DhlmbqWx.mjs} +19 -18
  44. package/dist/memory-index-DhlmbqWx.mjs.map +1 -0
  45. package/dist/{message-BMfreCQc.mjs → message-D6k16gtg.mjs} +20 -20
  46. package/dist/message-D6k16gtg.mjs.map +1 -0
  47. package/dist/{verifier-DhAbqVJF.d.mts → message-u_7KOq6Q.d.mts} +3 -22
  48. package/dist/{nip17-BZvFQLcs.d.mts → nip17--YwFSuph.d.mts} +4 -4
  49. package/dist/{nip19-D4RKn6GC.d.mts → nip19-qtyc5sCl.d.mts} +2 -2
  50. package/dist/{nip46-i8L8_nIm.d.mts → nip46-C_ZFQprM.d.mts} +2 -2
  51. package/dist/{nip51-C_kTYcud.d.mts → nip51-yi9w8FsM.d.mts} +2 -2
  52. package/dist/{nip59-DfJ6Moeu.d.mts → nip59-B6Ey8L9z.d.mts} +12 -3
  53. package/dist/{nip65-CPNE0SV0.d.mts → nip65-XrBjV9WM.d.mts} +2 -2
  54. package/dist/{nip77-DhpoL7d8.d.mts → nip77-BAXijIt_.d.mts} +2 -2
  55. package/dist/nips/blossom.d.mts +3 -2
  56. package/dist/nips/blossom.mjs +11 -11
  57. package/dist/nips/blossom.mjs.map +1 -1
  58. package/dist/nips/nip04.d.mts +3 -2
  59. package/dist/nips/nip04.mjs +10 -12
  60. package/dist/nips/nip04.mjs.map +1 -1
  61. package/dist/nips/nip05.d.mts +2 -2
  62. package/dist/nips/nip05.mjs +9 -8
  63. package/dist/nips/nip05.mjs.map +1 -1
  64. package/dist/nips/nip10.d.mts +3 -2
  65. package/dist/nips/nip10.mjs +19 -19
  66. package/dist/nips/nip10.mjs.map +1 -1
  67. package/dist/nips/nip11.d.mts +2 -1
  68. package/dist/nips/nip11.mjs +6 -4
  69. package/dist/nips/nip11.mjs.map +1 -1
  70. package/dist/nips/nip13.d.mts +1 -1
  71. package/dist/nips/nip13.mjs +2 -2
  72. package/dist/nips/nip13.mjs.map +1 -1
  73. package/dist/nips/nip17.d.mts +1 -1
  74. package/dist/nips/nip17.mjs +6 -6
  75. package/dist/nips/nip17.mjs.map +1 -1
  76. package/dist/nips/nip19.d.mts +1 -1
  77. package/dist/nips/nip19.mjs +1 -1
  78. package/dist/nips/nip21.d.mts +2 -2
  79. package/dist/nips/nip21.mjs +3 -3
  80. package/dist/nips/nip21.mjs.map +1 -1
  81. package/dist/nips/nip27.d.mts +3 -3
  82. package/dist/nips/nip27.mjs +308 -85
  83. package/dist/nips/nip27.mjs.map +1 -1
  84. package/dist/nips/nip42.mjs +1 -1
  85. package/dist/nips/nip44.d.mts +2 -1
  86. package/dist/nips/nip44.mjs +4 -4
  87. package/dist/nips/nip44.mjs.map +1 -1
  88. package/dist/nips/nip46.d.mts +1 -1
  89. package/dist/nips/nip46.mjs +9 -8
  90. package/dist/nips/nip46.mjs.map +1 -1
  91. package/dist/nips/nip49.d.mts +1 -1
  92. package/dist/nips/nip49.mjs +1 -1
  93. package/dist/nips/nip51.d.mts +1 -1
  94. package/dist/nips/nip51.mjs +14 -14
  95. package/dist/nips/nip51.mjs.map +1 -1
  96. package/dist/nips/nip57.mjs +10 -10
  97. package/dist/nips/nip57.mjs.map +1 -1
  98. package/dist/nips/nip59.d.mts +1 -1
  99. package/dist/nips/nip59.mjs +14 -11
  100. package/dist/nips/nip59.mjs.map +1 -1
  101. package/dist/nips/nip65.d.mts +1 -1
  102. package/dist/nips/nip65.mjs +2 -2
  103. package/dist/nips/nip77.d.mts +1 -1
  104. package/dist/nips/nip77.mjs +2 -2
  105. package/dist/nips/nip77.mjs.map +1 -1
  106. package/dist/nips/nip98.d.mts +1 -1
  107. package/dist/nips/nip98.mjs +5 -5
  108. package/dist/nips/nip98.mjs.map +1 -1
  109. package/dist/{pool-MehgIIzm.d.mts → pool-BMfTQZjM.d.mts} +5 -4
  110. package/dist/{pool-lMT_mvzn.mjs → pool-D13eVCqc.mjs} +5 -5
  111. package/dist/{pool-lMT_mvzn.mjs.map → pool-D13eVCqc.mjs.map} +1 -1
  112. package/dist/{put-BEM5j8xK.mjs → put-DuymazkT.mjs} +5 -5
  113. package/dist/put-DuymazkT.mjs.map +1 -0
  114. package/dist/{reactive-B7lPFgTS.d.mts → reactive-BkzOsSwU.d.mts} +3 -3
  115. package/dist/{reactive-DFHY-f00.mjs → reactive-C0-rTqN9.mjs} +10 -9
  116. package/dist/reactive-C0-rTqN9.mjs.map +1 -0
  117. package/dist/{relay-DIR77xQ2.mjs → relay-CbgToreY.mjs} +6 -6
  118. package/dist/{relay-DIR77xQ2.mjs.map → relay-CbgToreY.mjs.map} +1 -1
  119. package/dist/relay.d.mts +3 -3
  120. package/dist/relay.mjs +2 -2
  121. package/dist/{signer-Bha9gQy8.mjs → signer-DQzjm3pt.mjs} +28 -14
  122. package/dist/signer-DQzjm3pt.mjs.map +1 -0
  123. package/dist/signer.d.mts +2 -2
  124. package/dist/signer.mjs +2 -2
  125. package/dist/{storage-n2BE1bdY.mjs → storage-DvDAuxzC.mjs} +26 -41
  126. package/dist/storage-DvDAuxzC.mjs.map +1 -0
  127. package/dist/storage.d.mts +3 -3
  128. package/dist/storage.mjs +4 -4
  129. package/dist/store.d.mts +1 -1
  130. package/dist/store.mjs +1 -2
  131. package/dist/testing.d.mts +2 -2
  132. package/dist/testing.mjs +7 -7
  133. package/dist/{types-DiVVyf1e.d.mts → types-COupAqtN.d.mts} +2 -2
  134. package/dist/{types-BJ4wZ7JJ.d.mts → types-DF8yT6EG.d.mts} +7 -1
  135. package/dist/{util-DfsyJ9nz.mjs → util-BcRBO6o-.mjs} +18 -3
  136. package/dist/util-BcRBO6o-.mjs.map +1 -0
  137. package/dist/{verifier-DEHUy4AD.mjs → verifier-DBSPHzGg.mjs} +6 -15
  138. package/dist/verifier-DBSPHzGg.mjs.map +1 -0
  139. package/dist/verifier-Or2nDvHb.d.mts +16 -0
  140. package/package.json +3 -3
  141. package/dist/client-COkEihj_.mjs.map +0 -1
  142. package/dist/event-DvM1f03b.mjs.map +0 -1
  143. package/dist/filter-3jOh4uLN.mjs.map +0 -1
  144. package/dist/gossip-CRyhK6E6.mjs.map +0 -1
  145. package/dist/key-D_oEutQ6.mjs.map +0 -1
  146. package/dist/loaders-BRh2riV9.mjs.map +0 -1
  147. package/dist/memory-index-3GSrPIWj.mjs.map +0 -1
  148. package/dist/message-BMfreCQc.mjs.map +0 -1
  149. package/dist/put-BEM5j8xK.mjs.map +0 -1
  150. package/dist/reactive-DFHY-f00.mjs.map +0 -1
  151. package/dist/signer-Bha9gQy8.mjs.map +0 -1
  152. package/dist/storage-n2BE1bdY.mjs.map +0 -1
  153. package/dist/util-DfsyJ9nz.mjs.map +0 -1
  154. package/dist/verifier-DEHUy4AD.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-index-DhlmbqWx.mjs","names":[],"sources":["../src/storage/sorted-events.ts","../src/storage/memory-index.ts"],"sourcesContent":["import type { Event } from \"../core/event.ts\";\n\n/**\n * Chunk bounds: a chunk splice moves at most `CHUNK_MAX` element refs and a chunk merge at\n * `CHUNK_MIN` keeps chunk count proportional to `size` under removals.\n */\nconst CHUNK_MAX = 1024;\nconst CHUNK_MIN = 128;\n\n/** Reverse of `compareEventsDesc`: `created_at` asc, `id` desc — the physical chunk order. */\nfunction compareAsc(\n a: { id: string; created_at: number },\n b: { id: string; created_at: number },\n): number {\n if (a.created_at !== b.created_at) {\n return a.created_at - b.created_at;\n }\n if (a.id < b.id) {\n return 1;\n }\n if (a.id > b.id) {\n return -1;\n }\n return 0;\n}\n\nfunction mid(lo: number, hi: number): number {\n return lo + Math.floor((hi - lo) / 2);\n}\n\n/** A read cursor over a `SortedEvents` range in canonical order; `value` is the current event. */\nexport class EventCursor {\n readonly #chunks: Event[][];\n #chunk: number;\n #index: number;\n readonly #endChunk: number;\n readonly #endIndex: number;\n\n constructor(chunks: Event[][], chunk: number, index: number, endChunk: number, endIndex: number) {\n this.#chunks = chunks;\n this.#chunk = chunk;\n this.#index = index;\n this.#endChunk = endChunk;\n this.#endIndex = endIndex;\n }\n\n get value(): Event | undefined {\n const chunk = this.#chunk;\n const index = this.#index;\n if (chunk < this.#endChunk || (chunk === this.#endChunk && index <= this.#endIndex)) {\n return undefined;\n }\n return this.#chunks[chunk]?.[index];\n }\n\n next(): void {\n this.#index -= 1;\n if (this.#index < 0) {\n this.#chunk -= 1;\n this.#index = (this.#chunks[this.#chunk]?.length ?? 1) - 1;\n }\n }\n}\n\n/**\n * Event refs sorted in canonical order (`compareEventsDesc`: `created_at` desc, `id` asc) stored as\n * chunks of ≤ `CHUNK_MAX` elements in ascending physical order so insertion and removal\n * binary-search a chunk and then move a bounded number of refs — appending newer events (live\n * ingest, ascending fills) pushes at the end, and appending older ones (relay backfill) moves at\n * most one chunk's worth of refs. Internal to the memory index; not exported.\n */\nexport class SortedEvents {\n #chunks: Event[][] = [];\n #size = 0;\n\n get size(): number {\n return this.#size;\n }\n\n clear(): void {\n this.#chunks = [];\n this.#size = 0;\n }\n\n /** Index of the chunk `event` belongs to: the last one whose first element is not newer. */\n #chunkOf(event: Event): number {\n const chunks = this.#chunks;\n let lo = 0;\n let hi = chunks.length;\n while (lo < hi) {\n const m = mid(lo, hi);\n const first = chunks[m]?.[0];\n if (first !== undefined && compareAsc(first, event) <= 0) {\n lo = m + 1;\n } else {\n hi = m;\n }\n }\n return lo - 1;\n }\n\n insert(event: Event): void {\n const chunks = this.#chunks;\n if (chunks.length === 0) {\n chunks.push([event]);\n this.#size = 1;\n return;\n }\n const chunkIndex = Math.max(this.#chunkOf(event), 0);\n const chunk = chunks[chunkIndex];\n if (chunk === undefined) {\n return;\n }\n // Upper bound inside the chunk: insert after all elements that sort before `event`.\n let a = 0;\n let b = chunk.length;\n while (a < b) {\n const m = mid(a, b);\n const element = chunk[m];\n if (element !== undefined && compareAsc(element, event) <= 0) {\n a = m + 1;\n } else {\n b = m;\n }\n }\n chunk.splice(a, 0, event);\n this.#size += 1;\n if (chunk.length > CHUNK_MAX) {\n const half = chunk.splice(Math.floor(CHUNK_MAX / 2));\n chunks.splice(chunkIndex + 1, 0, half);\n }\n }\n\n remove(event: Event): boolean {\n const chunks = this.#chunks;\n const chunkIndex = this.#chunkOf(event);\n if (chunkIndex < 0) {\n return false;\n }\n const chunk = chunks[chunkIndex];\n if (chunk === undefined) {\n return false;\n }\n let a = 0;\n let b = chunk.length;\n while (a < b) {\n const m = mid(a, b);\n const element = chunk[m];\n if (element !== undefined && compareAsc(element, event) < 0) {\n a = m + 1;\n } else {\n b = m;\n }\n }\n const found = chunk[a];\n if (found === undefined || compareAsc(found, event) !== 0) {\n return false;\n }\n chunk.splice(a, 1);\n this.#size -= 1;\n if (chunk.length === 0) {\n chunks.splice(chunkIndex, 1);\n } else if (chunk.length < CHUNK_MIN) {\n // Absorb a thin chunk into a neighbor when it fits; a small chunk left between\n // fuller ones is still sorted and correct.\n const next = chunks[chunkIndex + 1];\n const prev = chunks[chunkIndex - 1];\n if (next !== undefined && chunk.length + next.length <= CHUNK_MAX) {\n chunk.push(...next);\n chunks.splice(chunkIndex + 1, 1);\n } else if (prev !== undefined && prev.length + chunk.length <= CHUNK_MAX) {\n prev.push(...chunk);\n chunks.splice(chunkIndex, 1);\n }\n }\n return true;\n }\n\n /**\n * Cursor over the [since, until] `created_at` window in canonical (desc) order: iteration starts\n * at the newest element with `created_at <= until` and stops before the first with `created_at <\n * since`. Chunks and their elements are stored ascending; the cursor walks back.\n */\n cursor(since?: number, until?: number): EventCursor {\n const chunks = this.#chunks;\n // Start position: last element with created_at <= until.\n let chunk = chunks.length - 1;\n let index = -1;\n if (until === undefined) {\n index = (chunks[chunk]?.length ?? 1) - 1;\n } else {\n // Last chunk holding an element with created_at <= until.\n let lo = 0;\n let hi = chunks.length;\n while (lo < hi) {\n const m = mid(lo, hi);\n const first = chunks[m]?.[0];\n if (first !== undefined && first.created_at <= until) {\n lo = m + 1;\n } else {\n hi = m;\n }\n }\n chunk = lo - 1;\n if (chunk >= 0) {\n const bucket = chunks[chunk];\n let a = 0;\n let b = bucket?.length ?? 0;\n while (a < b) {\n const m = mid(a, b);\n const element = bucket?.[m];\n if (element !== undefined && element.created_at <= until) {\n a = m + 1;\n } else {\n b = m;\n }\n }\n index = a - 1;\n }\n }\n // End position: last element with created_at < since (walk stops once past it).\n let endChunk = -1;\n let endIndex = -1;\n if (since !== undefined) {\n // First chunk holding an element with created_at >= since; the position before the\n // first such element is the boundary.\n let lo = 0;\n let hi = chunks.length;\n while (lo < hi) {\n const m = mid(lo, hi);\n const bucket = chunks[m];\n const last = bucket?.at(-1);\n if (last !== undefined && last.created_at >= since) {\n hi = m;\n } else {\n lo = m + 1;\n }\n }\n if (lo === chunks.length) {\n // Everything is older than since: empty range.\n endChunk = Number.MAX_SAFE_INTEGER;\n } else {\n const bucket = chunks[lo];\n let a = 0;\n let b = bucket?.length ?? 0;\n while (a < b) {\n const m = mid(a, b);\n const element = bucket?.[m];\n if (element !== undefined && element.created_at >= since) {\n b = m;\n } else {\n a = m + 1;\n }\n }\n if (a === 0) {\n endChunk = lo - 1;\n endIndex = (chunks[endChunk]?.length ?? 1) - 1;\n } else {\n endChunk = lo;\n endIndex = a - 1;\n }\n }\n }\n return new EventCursor(chunks, chunk, index, endChunk, endIndex);\n }\n}\n","import { 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 { canonicalizeFilter, filterTagConditions, matchFilter } from \"../core/filter.ts\";\nimport { eventAddress, formatEventAddress, parseEventAddressInput } from \"../core/tag.ts\";\nimport { DeletionState } from \"./deletion.ts\";\nimport { applyPutMemory, decidePut, outboxBoundKey } from \"./put.ts\";\nimport type { PutLookup } from \"./put.ts\";\nimport { SortedEvents } from \"./sorted-events.ts\";\nimport type { EventCursor } from \"./sorted-events.ts\";\nimport type { NegentropyItem, OutboxBound, PutResult } from \"./types.ts\";\n\n/**\n * NIP-01 only defines single-letter tag conditions; `#e`/`#p` hold hex ids (filter side\n * lowercased).\n */\nconst SINGLE_ASCII_LETTER = /^[a-zA-Z]$/;\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 #all = new SortedEvents();\n readonly #byPubkey = new Map<string, SortedEvents>();\n readonly #byKind = new Map<number, SortedEvents>();\n readonly #byKindPubkey = new Map<string, SortedEvents>(); // `${kind}:${pubkey}` bucket\n readonly #byTag = new Map<string, SortedEvents>(); // `${name}:${value}` for single-letter tag names (event-side verbatim)\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 = parseEventAddressInput(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.map(canonicalizeFilter)) {\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.map(canonicalizeFilter)) {\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 const canonical = canonicalizeFilter(filter);\n if (canonical.limit === 0) {\n return [];\n }\n const items: NegentropyItem[] = [];\n const { limit } = canonical;\n if (canonical.ids) {\n const seen = new Set<string>();\n for (const raw of canonical.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 if (this.#deletion.covers(event) || !matchFilter(canonical, event)) {\n continue;\n }\n items.push({ id: event.id, created_at: event.created_at });\n }\n } else {\n const cursor = this.#candidates(canonical);\n while (cursor.value !== undefined) {\n const event = cursor.value;\n cursor.next();\n if (this.#deletion.covers(event) || !matchFilter(canonical, event)) {\n continue;\n }\n items.push({ id: event.id, created_at: event.created_at });\n if (limit !== undefined && items.length >= limit) {\n break;\n }\n }\n }\n if (canonical.limit !== undefined) {\n // The ids path collected matches in filter order; cursor results are already canonical.\n if (canonical.ids) {\n items.sort(compareEventsDesc);\n }\n if (items.length > canonical.limit) {\n items.length = canonical.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 = parseEventAddressInput(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.#all.clear();\n this.#byPubkey.clear();\n this.#byKind.clear();\n this.#byKindPubkey.clear();\n this.#byTag.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 this.#all.insert(event);\n insertBucket(this.#byPubkey, pubkey, event);\n insertBucket(this.#byKind, event.kind, event);\n insertBucket(this.#byKindPubkey, `${event.kind}:${pubkey}`, event);\n for (const key of tagKeys(event)) {\n insertBucket(this.#byTag, key, event);\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 this.#all.remove(event);\n removeBucket(this.#byPubkey, pubkey, event);\n removeBucket(this.#byKind, event.kind, event);\n removeBucket(this.#byKindPubkey, `${event.kind}:${pubkey}`, event);\n for (const key of tagKeys(event)) {\n removeBucket(this.#byTag, key, event);\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 { limit } = filter;\n const matched: Event[] = [];\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 if (this.#deletion.covers(event) || !matchFilter(filter, event)) {\n continue;\n }\n matched.push(event);\n }\n sortEvents(matched);\n return limit === undefined ? matched : matched.slice(0, limit);\n }\n const cursor = this.#candidates(filter);\n while (cursor.value !== undefined) {\n const event = cursor.value;\n cursor.next();\n if (this.#deletion.covers(event) || !matchFilter(filter, event)) {\n continue;\n }\n matched.push(event);\n if (limit !== undefined && matched.length >= limit) {\n break;\n }\n }\n return matched;\n }\n\n /**\n * Smallest-total bucket set for the filter's usable indexes: `authors`×`kinds` (both present),\n * `authors`, `kinds`, each single-letter `#x` condition (a bucket per value; `#e`/`#p` filter\n * values lowercased like `matchFilter`, others verbatim), or `all` as the fallback.\n */\n #plan(filter: Filter): SortedEvents[] {\n const { authors, kinds } = filter;\n const tagConds = filterTagConditions(filter);\n\n let best: SortedEvents[] | undefined;\n let bestTotal = Number.POSITIVE_INFINITY;\n const consider = (buckets: Array<SortedEvents | undefined>): void => {\n let total = 0;\n const present: SortedEvents[] = [];\n for (const bucket of buckets) {\n if (bucket !== undefined) {\n present.push(bucket);\n total += bucket.size;\n }\n }\n if (total < bestTotal) {\n bestTotal = total;\n best = present;\n }\n };\n\n if (authors !== undefined && kinds !== undefined) {\n const buckets: Array<SortedEvents | undefined> = [];\n for (const pk of authors) {\n const pubkey = pk.toLowerCase();\n for (const kind of kinds) {\n buckets.push(this.#byKindPubkey.get(`${kind}:${pubkey}`));\n }\n }\n consider(buckets);\n }\n if (authors !== undefined) {\n consider(authors.map((pk) => this.#byPubkey.get(pk.toLowerCase())));\n }\n if (kinds !== undefined) {\n consider(kinds.map((kind) => this.#byKind.get(kind)));\n }\n for (const { name, values } of tagConds) {\n consider(values.map((v) => this.#byTag.get(`${name}:${v}`)));\n }\n if (best === undefined || bestTotal > this.#all.size) {\n return [this.#all];\n }\n return best;\n }\n\n /** Canonical-order candidates from the planned buckets, each clipped to `since`/`until`. */\n #candidates(filter: Filter): CandidateCursor {\n const buckets = this.#plan(filter);\n const cursors: EventCursor[] = [];\n for (const bucket of buckets) {\n const cursor = bucket.cursor(filter.since, filter.until);\n if (cursor.value !== undefined) {\n cursors.push(cursor);\n }\n }\n if (cursors.length === 0) {\n return EMPTY_CURSOR;\n }\n if (cursors.length === 1) {\n return cursors[0] ?? EMPTY_CURSOR;\n }\n return new CursorHeap(cursors);\n }\n\n #deriveOutboxBound(pubkey: string, kind: number): OutboxBound | undefined {\n const bucket = this.#byKindPubkey.get(`${kind}:${pubkey.toLowerCase()}`);\n if (bucket === undefined) {\n return undefined;\n }\n // Canonical order: first non-deleted event is the newest, last the oldest.\n let oldest: number | undefined;\n let newest: number | undefined;\n const cursor = bucket.cursor();\n while (cursor.value !== undefined) {\n const event = cursor.value;\n cursor.next();\n if (this.#deletion.covers(event)) {\n continue;\n }\n newest ??= event.created_at;\n oldest = event.created_at;\n }\n if (oldest === undefined || newest === undefined) {\n return undefined;\n }\n return { oldest, newest };\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\ntype CandidateCursor = {\n readonly value: Event | undefined;\n next: () => void;\n};\n\nconst EMPTY_CURSOR: CandidateCursor = {\n value: undefined,\n next: () => undefined,\n};\n\n/**\n * Binary-heap k-way merge of sorted cursors. `next()` advances every cursor sitting on the emitted\n * event, which dedupes an event present in several buckets (multi-value `#x` filters); copies\n * compare equal so they always surface adjacent. Entries pair each cursor with its head event so\n * heap comparisons never read `undefined`.\n */\nclass CursorHeap implements CandidateCursor {\n #heap: Array<{ cursor: EventCursor; event: Event }>;\n\n constructor(cursors: EventCursor[]) {\n const heap: Array<{ cursor: EventCursor; event: Event }> = [];\n for (const cursor of cursors) {\n const event = cursor.value;\n if (event !== undefined) {\n heap.push({ cursor, event });\n }\n }\n this.#heap = heap;\n for (let i = Math.floor(heap.length / 2) - 1; i >= 0; i--) {\n this.#siftDown(i);\n }\n }\n\n get value(): Event | undefined {\n return this.#heap[0]?.event;\n }\n\n next(): void {\n const emitted = this.#heap[0]?.event;\n if (emitted === undefined) {\n return;\n }\n while (this.#heap[0]?.event === emitted) {\n const [root] = this.#heap;\n if (root === undefined) {\n return;\n }\n root.cursor.next();\n const nextEvent = root.cursor.value;\n if (nextEvent === undefined) {\n const last = this.#heap.pop();\n if (this.#heap.length > 0 && last !== undefined) {\n this.#heap[0] = last;\n this.#siftDown(0);\n }\n } else {\n root.event = nextEvent;\n this.#siftDown(0);\n }\n }\n }\n\n #less(a: number, b: number): boolean {\n const x = this.#heap[a];\n const y = this.#heap[b];\n return x !== undefined && y !== undefined && compareEventsDesc(x.event, y.event) < 0;\n }\n\n #siftDown(start: number): void {\n const heap = this.#heap;\n let i = start;\n for (;;) {\n let min = i;\n if (this.#less(2 * i + 1, min)) {\n min = 2 * i + 1;\n }\n if (this.#less(2 * i + 2, min)) {\n min = 2 * i + 2;\n }\n if (min === i) {\n return;\n }\n const tmp = heap[i];\n const swap = heap[min];\n if (tmp === undefined || swap === undefined) {\n return;\n }\n heap[i] = swap;\n heap[min] = tmp;\n i = min;\n }\n }\n}\n\n/** Distinct `${name}:${value}` bucket keys for every ASCII single-letter tag name on the event. */\nfunction tagKeys(event: Event): string[] {\n const keys = new Set<string>();\n for (const tag of event.tags) {\n const [name, value] = tag;\n if (name === undefined || value === undefined || !SINGLE_ASCII_LETTER.test(name)) {\n continue;\n }\n keys.add(`${name}:${value}`);\n }\n return [...keys];\n}\n\nfunction insertBucket<K>(map: Map<K, SortedEvents>, key: K, event: Event): void {\n let bucket = map.get(key);\n if (bucket === undefined) {\n bucket = new SortedEvents();\n map.set(key, bucket);\n }\n bucket.insert(event);\n}\n\nfunction removeBucket<K>(map: Map<K, SortedEvents>, key: K, event: Event): void {\n const bucket = map.get(key);\n if (bucket === undefined || !bucket.remove(event)) {\n return;\n }\n if (bucket.size === 0) {\n map.delete(key);\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { b as MessageError, i as bytesToHex, s as hexToBytes, u as isRecord } from "./util-DfsyJ9nz.mjs";
2
- import { h as validateSignedEvent } from "./event-DvM1f03b.mjs";
3
- import { n as canonicalizeFilters, t as canonicalizeFilter } from "./filter-3jOh4uLN.mjs";
1
+ import { C as MessageError, i as bytesToHex, s as hexToBytes, u as isRecord } from "./util-BcRBO6o-.mjs";
2
+ import { h as validateSignedEvent } from "./event-DHtcm0jM.mjs";
3
+ import { n as canonicalizeFilters, t as canonicalizeFilter } from "./filter-BbShkmc4.mjs";
4
4
  //#region src/core/message.ts
5
5
  /** Validate a subscription id; throws {@link MessageError} unless 1..64 Unicode scalar values. */
6
6
  function assertSubscriptionId(id) {
@@ -18,7 +18,7 @@ function createSubscriptionId(id) {
18
18
  }
19
19
  const HLL_BYTES = 256;
20
20
  const HLL_HEX_LEN = HLL_BYTES * 2;
21
- const HEX_RE = /^[0-9a-fA-F]+$/;
21
+ const HEX_RE = /^[0-9a-f]+$/;
22
22
  /**
23
23
  * Register-wise max of NIP-45 HyperLogLog sketches. Output is always lowercase 512 hex. Empty input
24
24
  * is the zero sketch (identity).
@@ -26,7 +26,7 @@ const HEX_RE = /^[0-9a-fA-F]+$/;
26
26
  function mergeCountHll(hexes) {
27
27
  const merged = new Uint8Array(HLL_BYTES);
28
28
  for (const hex of hexes) {
29
- if (hex.length !== HLL_HEX_LEN || !HEX_RE.test(hex)) throw new MessageError("invalid NIP-45 HLL sketch: expected 512-char hex");
29
+ if (hex.length !== HLL_HEX_LEN || !/^[0-9a-f]+$/i.test(hex)) throw new MessageError("invalid NIP-45 HLL sketch: expected 512-char hex");
30
30
  const bytes = hexToBytes(hex);
31
31
  for (const [i, b] of bytes.entries()) {
32
32
  const m = merged[i];
@@ -102,14 +102,14 @@ function parseClientMessage(raw) {
102
102
  "NEG-OPEN",
103
103
  assertSubscriptionId(items[1]),
104
104
  parseWireFilter(items[2], "NEG-OPEN"),
105
- items[3].toLowerCase()
105
+ items[3]
106
106
  ];
107
107
  case "NEG-MSG":
108
108
  if (items.length !== 3 || typeof items[1] !== "string" || !isNegHex(items[2])) throw new MessageError("invalid NEG-MSG client message");
109
109
  return [
110
110
  "NEG-MSG",
111
111
  assertSubscriptionId(items[1]),
112
- items[2].toLowerCase()
112
+ items[2]
113
113
  ];
114
114
  case "NEG-CLOSE":
115
115
  if (items.length !== 2 || typeof items[1] !== "string") throw new MessageError("invalid NEG-CLOSE client message");
@@ -179,7 +179,7 @@ function parseRelayMessage(raw) {
179
179
  return [
180
180
  "NEG-MSG",
181
181
  assertSubscriptionId(items[1]),
182
- items[2].toLowerCase()
182
+ items[2]
183
183
  ];
184
184
  case "NEG-ERR":
185
185
  if (items.length !== 3 || typeof items[1] !== "string" || typeof items[2] !== "string") throw new MessageError("invalid NEG-ERR relay message");
@@ -193,17 +193,16 @@ function parseRelayMessage(raw) {
193
193
  }
194
194
  function parseCountHll(value) {
195
195
  if (typeof value !== "string" || value.length !== HLL_HEX_LEN || !HEX_RE.test(value)) return;
196
- return value.toLowerCase();
196
+ return value;
197
197
  }
198
- const HEX64_RE = /^[0-9a-fA-F]{64}$/;
199
198
  const HEX64_LOWER_RE = /^[0-9a-f]{64}$/;
200
199
  /**
201
200
  * Validates a wire filter per NIP-01: `ids`/`authors` are 64-hex arrays (normalized lowercase),
202
201
  * `kinds` are integers in 0..=65535, `since`/`until`/`limit` non-negative integers, `search` a
203
- * string, and `#<single letter>` arrays of strings (`#e`/`#p` lowercased). Multi-letter `#` keys
204
- * and unknown non-`#` keys are dropped; wrong types throw {@link MessageError}. Duplicate keys and
205
- * integer spellings (`1e3`, `1.0`, `-0`) need no handling: `JSON.parse` already applied last-wins
206
- * and normalized the numbers before this runs.
202
+ * string, and `#<single letter>` arrays of strings (`#e`/`#p` lowercased). No list may be empty.
203
+ * Multi-letter `#` keys and unknown non-`#` keys are dropped; wrong types throw
204
+ * {@link MessageError}. Duplicate keys and integer spellings (`1e3`, `1.0`, `-0`) need no handling:
205
+ * `JSON.parse` already applied last-wins and normalized the numbers before this runs.
207
206
  */
208
207
  function parseWireFilter(value, kind) {
209
208
  if (!isRecord(value)) throw new MessageError(`invalid ${kind} filter`);
@@ -211,13 +210,14 @@ function parseWireFilter(value, kind) {
211
210
  const out = {};
212
211
  for (const [key, v] of Object.entries(value)) if (key.startsWith("#")) {
213
212
  if (!/^#[a-zA-Z]$/.test(key)) continue;
214
- if (!Array.isArray(v) || !v.every((x) => typeof x === "string")) throw fail();
215
- out[`#${key.slice(1)}`] = key === "#e" || key === "#p" ? v.map((x) => x.toLowerCase()) : v;
213
+ if (!Array.isArray(v) || v.length === 0 || !v.every((x) => typeof x === "string")) throw fail();
214
+ if ((key === "#e" || key === "#p") && !v.every((x) => HEX64_LOWER_RE.test(x))) throw fail();
215
+ out[`#${key.slice(1)}`] = v;
216
216
  } else if (key === "ids" || key === "authors") {
217
- if (!Array.isArray(v) || !v.every((x) => typeof x === "string" && HEX64_RE.test(x))) throw fail();
218
- out[key] = v.map((x) => x.toLowerCase());
217
+ if (!Array.isArray(v) || v.length === 0 || !v.every((x) => typeof x === "string" && HEX64_LOWER_RE.test(x))) throw fail();
218
+ out[key] = v;
219
219
  } else if (key === "kinds") {
220
- if (!Array.isArray(v) || !v.every((x) => typeof x === "number" && Number.isSafeInteger(x) && x >= 0 && x <= 65535)) throw fail();
220
+ if (!Array.isArray(v) || v.length === 0 || !v.every((x) => typeof x === "number" && Number.isSafeInteger(x) && x >= 0 && x <= 65535)) throw fail();
221
221
  out.kinds = v;
222
222
  } else if (key === "since" || key === "until" || key === "limit") {
223
223
  if (typeof v !== "number" || !Number.isSafeInteger(v) || v < 0) throw fail();
@@ -237,4 +237,4 @@ function isNegHex(value) {
237
237
  //#endregion
238
238
  export { mergeCountHll as a, encodeRelayMessage as i, createSubscriptionId as n, parseClientMessage as o, encodeClientMessage as r, parseRelayMessage as s, assertSubscriptionId as t };
239
239
 
240
- //# sourceMappingURL=message-BMfreCQc.mjs.map
240
+ //# sourceMappingURL=message-D6k16gtg.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-D6k16gtg.mjs","names":[],"sources":["../src/core/message.ts"],"sourcesContent":["import { MessageError } from \"./error.ts\";\nimport type { Event } from \"./event.ts\";\nimport { validateSignedEvent } from \"./event.ts\";\nimport { canonicalizeFilter, canonicalizeFilters } from \"./filter.ts\";\nimport type { Filter } from \"./filter.ts\";\nimport { SUBSCRIPTION_ID_MAX_CHARS } from \"./limits.ts\";\nimport type { Mutable } from \"./util.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 Unicode scalar values. */\nexport function assertSubscriptionId(id: string): SubscriptionId {\n // Iterating a string yields code points, so astral chars count once (NIP-01's \"chars\").\n let length = 0;\n for (const _ of id) {\n length += 1;\n }\n if (length === 0 || 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 readonly count: number;\n readonly approximate?: boolean | undefined;\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 readonly hll?: string | undefined;\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-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 || !/^[0-9a-f]+$/i.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 // REQ/COUNT/NEG-OPEN filters are canonicalized so the emitted wire form is\n // the canonical filter `filterFingerprint` uses for subscription identity.\n switch (message[0]) {\n case \"REQ\":\n case \"COUNT\": {\n const [type, id, ...filters] = message;\n return JSON.stringify([type, id, ...canonicalizeFilters(filters)]);\n }\n case \"NEG-OPEN\":\n return JSON.stringify([\"NEG-OPEN\", message[1], canonicalizeFilter(message[2]), message[3]]);\n default:\n return JSON.stringify(message);\n }\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 parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new MessageError(\"client message is not valid JSON\");\n }\n if (!Array.isArray(parsed) || parsed.length === 0 || typeof parsed[0] !== \"string\") {\n throw new MessageError(\"client message must be a non-empty JSON array\");\n }\n const items: unknown[] = parsed;\n const [type] = items;\n switch (type) {\n case \"EVENT\":\n if (items.length !== 2 || !validateSignedEvent(items[1])) {\n throw new MessageError(\"invalid EVENT client message\");\n }\n return [\"EVENT\", items[1]];\n\n case \"REQ\":\n if (items.length < 3 || typeof items[1] !== \"string\") {\n throw new MessageError(\"invalid REQ client message\");\n }\n return [\"REQ\", assertSubscriptionId(items[1]), ...parseWireFilters(items.slice(2), \"REQ\")];\n\n case \"CLOSE\":\n if (items.length !== 2 || typeof items[1] !== \"string\") {\n throw new MessageError(\"invalid CLOSE client message\");\n }\n return [\"CLOSE\", assertSubscriptionId(items[1])];\n\n case \"AUTH\":\n if (items.length !== 2 || !validateSignedEvent(items[1])) {\n throw new MessageError(\"invalid AUTH client message\");\n }\n return [\"AUTH\", items[1]];\n\n case \"COUNT\":\n if (items.length < 3 || typeof items[1] !== \"string\") {\n throw new MessageError(\"invalid COUNT client message\");\n }\n return [\n \"COUNT\",\n assertSubscriptionId(items[1]),\n ...parseWireFilters(items.slice(2), \"COUNT\"),\n ];\n\n case \"NEG-OPEN\":\n if (items.length === 5) {\n throw new MessageError(\"obsolete 5-element NEG-OPEN; expected [NEG-OPEN, id, filter, hex]\");\n }\n if (\n items.length !== 4 ||\n typeof items[1] !== \"string\" ||\n !isRecord(items[2]) ||\n !isNegHex(items[3])\n ) {\n throw new MessageError(\"invalid NEG-OPEN client message\");\n }\n return [\n \"NEG-OPEN\",\n assertSubscriptionId(items[1]),\n parseWireFilter(items[2], \"NEG-OPEN\"),\n items[3],\n ];\n\n case \"NEG-MSG\":\n if (items.length !== 3 || typeof items[1] !== \"string\" || !isNegHex(items[2])) {\n throw new MessageError(\"invalid NEG-MSG client message\");\n }\n return [\"NEG-MSG\", assertSubscriptionId(items[1]), items[2]];\n\n case \"NEG-CLOSE\":\n if (items.length !== 2 || typeof items[1] !== \"string\") {\n throw new MessageError(\"invalid NEG-CLOSE client message\");\n }\n return [\"NEG-CLOSE\", assertSubscriptionId(items[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 parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new MessageError(\"relay message is not valid JSON\");\n }\n if (!Array.isArray(parsed) || parsed.length === 0 || typeof parsed[0] !== \"string\") {\n throw new MessageError(\"relay message must be a non-empty JSON array\");\n }\n const items: unknown[] = parsed;\n const [type] = items;\n switch (type) {\n case \"EVENT\":\n if (items.length !== 3 || typeof items[1] !== \"string\" || !validateSignedEvent(items[2])) {\n throw new MessageError(\"invalid EVENT relay message\");\n }\n return [\"EVENT\", assertSubscriptionId(items[1]), items[2]];\n\n case \"OK\":\n if (\n items.length !== 4 ||\n typeof items[1] !== \"string\" ||\n !HEX64_LOWER_RE.test(items[1]) ||\n typeof items[2] !== \"boolean\" ||\n typeof items[3] !== \"string\"\n ) {\n throw new MessageError(\"invalid OK relay message\");\n }\n return [\"OK\", items[1], items[2], items[3]];\n\n case \"EOSE\":\n if (items.length !== 2 || typeof items[1] !== \"string\") {\n throw new MessageError(\"invalid EOSE relay message\");\n }\n return [\"EOSE\", assertSubscriptionId(items[1])];\n\n case \"CLOSED\":\n if (items.length !== 3 || typeof items[1] !== \"string\" || typeof items[2] !== \"string\") {\n throw new MessageError(\"invalid CLOSED relay message\");\n }\n return [\"CLOSED\", assertSubscriptionId(items[1]), items[2]];\n\n case \"NOTICE\":\n if (items.length !== 2 || typeof items[1] !== \"string\") {\n throw new MessageError(\"invalid NOTICE relay message\");\n }\n return [\"NOTICE\", items[1]];\n\n case \"AUTH\":\n if (items.length !== 2 || typeof items[1] !== \"string\") {\n throw new MessageError(\"invalid AUTH relay message\");\n }\n return [\"AUTH\", items[1]];\n\n case \"COUNT\": {\n const payload = items.at(2);\n const count = isRecord(payload) ? payload[\"count\"] : undefined;\n if (\n items.length !== 3 ||\n typeof items[1] !== \"string\" ||\n !isRecord(payload) ||\n typeof count !== \"number\" ||\n !Number.isSafeInteger(count) ||\n count < 0\n ) {\n throw new MessageError(\"invalid COUNT relay message\");\n }\n const result: Mutable<CountResult> = { 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\", assertSubscriptionId(items[1]), result];\n }\n case \"NEG-MSG\":\n if (items.length !== 3 || typeof items[1] !== \"string\" || !isNegHex(items[2])) {\n throw new MessageError(\"invalid NEG-MSG relay message\");\n }\n return [\"NEG-MSG\", assertSubscriptionId(items[1]), items[2]];\n\n case \"NEG-ERR\":\n if (items.length !== 3 || typeof items[1] !== \"string\" || typeof items[2] !== \"string\") {\n throw new MessageError(\"invalid NEG-ERR relay message\");\n }\n return [\"NEG-ERR\", assertSubscriptionId(items[1]), items[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;\n}\n\nconst HEX64_LOWER_RE = /^[0-9a-f]{64}$/;\n\n/**\n * Validates a wire filter per NIP-01: `ids`/`authors` are 64-hex arrays (normalized lowercase),\n * `kinds` are integers in 0..=65535, `since`/`until`/`limit` non-negative integers, `search` a\n * string, and `#<single letter>` arrays of strings (`#e`/`#p` lowercased). No list may be empty.\n * Multi-letter `#` keys and unknown non-`#` keys are dropped; wrong types throw\n * {@link MessageError}. Duplicate keys and integer spellings (`1e3`, `1.0`, `-0`) need no handling:\n * `JSON.parse` already applied last-wins and normalized the numbers before this runs.\n */\nfunction parseWireFilter(value: unknown, kind: string): Filter {\n if (!isRecord(value)) {\n throw new MessageError(`invalid ${kind} filter`);\n }\n const fail = (): MessageError => new MessageError(`invalid ${kind} filter`);\n const out: Mutable<Filter> = {};\n for (const [key, v] of Object.entries(value)) {\n if (key.startsWith(\"#\")) {\n if (!/^#[a-zA-Z]$/.test(key)) {\n continue;\n }\n if (\n !Array.isArray(v) ||\n v.length === 0 ||\n !v.every((x): x is string => typeof x === \"string\")\n ) {\n throw fail();\n }\n if ((key === \"#e\" || key === \"#p\") && !v.every((x): x is string => HEX64_LOWER_RE.test(x))) {\n throw fail();\n }\n out[`#${key.slice(1)}`] = v;\n } else if (key === \"ids\" || key === \"authors\") {\n if (\n !Array.isArray(v) ||\n v.length === 0 ||\n !v.every((x): x is string => typeof x === \"string\" && HEX64_LOWER_RE.test(x))\n ) {\n throw fail();\n }\n out[key] = v;\n } else if (key === \"kinds\") {\n if (\n !Array.isArray(v) ||\n v.length === 0 ||\n !v.every(\n (x): x is number =>\n typeof x === \"number\" && Number.isSafeInteger(x) && x >= 0 && x <= 65535,\n )\n ) {\n throw fail();\n }\n out.kinds = v;\n } else if (key === \"since\" || key === \"until\" || key === \"limit\") {\n if (typeof v !== \"number\" || !Number.isSafeInteger(v) || v < 0) {\n throw fail();\n }\n out[key] = v;\n } else if (key === \"search\") {\n if (typeof v !== \"string\") {\n throw fail();\n }\n out.search = v;\n }\n }\n return out;\n}\n\nfunction parseWireFilters(items: unknown[], kind: string): Filter[] {\n return items.map((item) => parseWireFilter(item, kind));\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,5 +1,5 @@
1
1
  import { t as Event } from "./event-j3BwI5dF.mjs";
2
- import { t as Filter } from "./filter-oYG2zjJ3.mjs";
2
+ import { t as Filter } from "./filter-w1R-mU63.mjs";
3
3
  //#region src/core/message.d.ts
4
4
  /** NIP-01 subscription id: 1..64 chars. */
5
5
  type SubscriptionId = string;
@@ -35,24 +35,5 @@ declare function parseClientMessage(raw: string): ClientMessage;
35
35
  /** Parse a relay->client JSON message; throws {@link MessageError} on malformed input. */
36
36
  declare function parseRelayMessage(raw: string): RelayMessage;
37
37
  //#endregion
38
- //#region src/core/verifier.d.ts
39
- /**
40
- * Verification backend: return true iff `sha256(serializedUtf8)` equals `id` and `sig` is a valid
41
- * BIP-340 signature of `id` by the x-only `pubkey`. Inputs are raw bytes (`id`/`pubkey` 32, `sig`
42
- * 64). Exceptions propagate through the built verifier unmarked — a throwing backend is broken and
43
- * must surface.
44
- */
45
- type SerializedEventVerifier = (serializedUtf8: Uint8Array, id: Uint8Array, pubkey: Uint8Array, sig: Uint8Array) => boolean;
46
- /** Whole-event verifier: return true iff the event's id and BIP-340 signature verify. */
47
- type EventVerifier = (event: Event) => boolean;
48
- /**
49
- * Build an event verifier with exactly the semantics of {@link verifyEvent}, delegating the hash and
50
- * signature check to `backend`. Verified/failed results share `verifyEvent`'s WeakSet caches, so
51
- * verifiers built this way and `verifyEvent` agree on (and reuse) each other's results.
52
- */
53
- declare function createEventVerifier(backend: SerializedEventVerifier): EventVerifier;
54
- /** Verify event id and BIP-340 signature. Uses WeakSet cache (does not mutate the event). */
55
- declare const verifyEvent: EventVerifier;
56
- //#endregion
57
- export { ClientMessage as a, SubscriptionId as c, encodeClientMessage as d, encodeRelayMessage as f, parseRelayMessage as h, verifyEvent as i, assertSubscriptionId as l, parseClientMessage as m, SerializedEventVerifier as n, CountResult as o, mergeCountHll as p, createEventVerifier as r, RelayMessage as s, EventVerifier as t, createSubscriptionId as u };
58
- //# sourceMappingURL=verifier-DhAbqVJF.d.mts.map
38
+ export { assertSubscriptionId as a, encodeRelayMessage as c, parseRelayMessage as d, SubscriptionId as i, mergeCountHll as l, CountResult as n, createSubscriptionId as o, RelayMessage as r, encodeClientMessage as s, ClientMessage as t, parseClientMessage as u };
39
+ //# sourceMappingURL=message-u_7KOq6Q.d.mts.map
@@ -1,7 +1,7 @@
1
1
  import { h as Tag, t as Event } from "./event-j3BwI5dF.mjs";
2
- import { t as EventBuilder } from "./builder-C72nJITG.mjs";
3
- import { a as NostrError } from "./error-BPqAU5RB.mjs";
4
- import { a as Rumor, l as WrapOptions, r as Nip59Crypto } from "./nip59-DfJ6Moeu.mjs";
2
+ import { t as EventBuilder } from "./builder-Dk00TdNf.mjs";
3
+ import { o as NostrError } from "./error-DE1UCZ2J.mjs";
4
+ import { a as Rumor, l as WrapOptions, r as Nip59Crypto } from "./nip59-B6Ey8L9z.mjs";
5
5
  //#region src/nips/nip17.d.ts
6
6
  declare class Nip17Error extends NostrError {
7
7
  name: string;
@@ -35,4 +35,4 @@ declare function wrapDirectMessage(crypto: Nip59Crypto, recipients: ReadonlyArra
35
35
  declare function requireDmRelays(pubkey: string, relays: ReadonlyArray<string>): string[];
36
36
  //#endregion
37
37
  export { buildChatMessageRumor as a, normalizeRecipients as c, wrapDirectMessage as d, ReplyTo as i, parseDmRelayList as l, Nip17Error as n, dmRelayListEventBuilder as o, Recipient as r, dmRelayListToTags as s, ChatMessageOptions as t, requireDmRelays as u };
38
- //# sourceMappingURL=nip17-BZvFQLcs.d.mts.map
38
+ //# sourceMappingURL=nip17--YwFSuph.d.mts.map
@@ -1,4 +1,4 @@
1
- import { a as NostrError } from "./error-BPqAU5RB.mjs";
1
+ import { o as NostrError } from "./error-DE1UCZ2J.mjs";
2
2
  //#region src/nips/nip19.d.ts
3
3
  /** Bech32 `nprofile1…` string (TLV profile pointer). */
4
4
  type NProfile = `nprofile1${string}`;
@@ -74,4 +74,4 @@ declare function naddrEncode(addr: AddressPointer): NAddr;
74
74
  declare function decode(code: string): DecodedResult;
75
75
  //#endregion
76
76
  export { noteEncode as _, NAddr as a, nsecEncode as b, NPub as c, Note as d, ProfilePointer as f, neventEncode as g, naddrEncode as h, EventPointer as i, NSec as l, encodeBytes as m, Bech32MaxSize as n, NEvent as o, decode as p, DecodedResult as r, NProfile as s, AddressPointer as t, Nip19Error as u, nprofileEncode as v, npubEncode as y };
77
- //# sourceMappingURL=nip19-D4RKn6GC.d.mts.map
77
+ //# sourceMappingURL=nip19-qtyc5sCl.d.mts.map
@@ -1,4 +1,4 @@
1
- import { a as NostrError } from "./error-BPqAU5RB.mjs";
1
+ import { o as NostrError } from "./error-DE1UCZ2J.mjs";
2
2
  //#region src/nips/nip46.d.ts
3
3
  type BunkerPointer = {
4
4
  /** Remote signer / bunker public key (hex). */
@@ -47,4 +47,4 @@ declare function encodeNip46Response(res: Nip46Response): string;
47
47
  declare function decodeNip46Response(json: string): Nip46Response;
48
48
  //#endregion
49
49
  export { Nip46Response as a, decodeNip46Request as c, encodeNip46Response as d, parseBunkerURL as f, Nip46Request as i, decodeNip46Response as l, toBunkerURL as m, ClientMetadata as n, NostrConnectParams as o, parseNostrConnectURI as p, Nip46Error as r, createNostrConnectURI as s, BunkerPointer as t, encodeNip46Request as u };
50
- //# sourceMappingURL=nip46-i8L8_nIm.d.mts.map
50
+ //# sourceMappingURL=nip46-C_ZFQprM.d.mts.map
@@ -1,5 +1,5 @@
1
1
  import { t as Event } from "./event-j3BwI5dF.mjs";
2
- import { t as EventBuilder } from "./builder-C72nJITG.mjs";
2
+ import { t as EventBuilder } from "./builder-Dk00TdNf.mjs";
3
3
  //#region src/nips/nip51.d.ts
4
4
  /**
5
5
  * Structural crypto used by NIP-51 private tags. Satisfied by NostrSigner when
@@ -84,4 +84,4 @@ declare function parseFollowPack(event: Pick<Event, "kind" | "tags">): {
84
84
  };
85
85
  //#endregion
86
86
  export { encryptPrivateTags as a, parseEmojiSet as c, parseMuteList as d, parseMuteListPrivate as f, pinListEventBuilder as g, parseUserEmojiList as h, decryptPrivateTags as i, parseFavoriteRelays as l, parseRelaySet as m, Nip51Crypto as n, muteListEventBuilder as o, parsePinList as p, bookmarkListEventBuilder as r, parseBookmarkList as s, MuteItem as t, parseFollowPack as u };
87
- //# sourceMappingURL=nip51-C_kTYcud.d.mts.map
87
+ //# sourceMappingURL=nip51-yi9w8FsM.d.mts.map
@@ -1,5 +1,6 @@
1
1
  import { h as Tag, r as UnsignedEvent, t as Event } from "./event-j3BwI5dF.mjs";
2
- import { a as NostrError } from "./error-BPqAU5RB.mjs";
2
+ import { t as EventVerifier } from "./verifier-Or2nDvHb.mjs";
3
+ import { o as NostrError } from "./error-DE1UCZ2J.mjs";
3
4
  //#region src/nips/nip59.d.ts
4
5
  /** Unsigned event with a computed id. Never has `sig`. */
5
6
  type Rumor = UnsignedEvent & {
@@ -14,6 +15,11 @@ type Nip59Crypto = {
14
15
  signEvent: (unsigned: UnsignedEvent) => Promise<Event>;
15
16
  nip44Encrypt: (peer: string, plaintext: string) => Promise<string>;
16
17
  nip44Decrypt: (peer: string, payload: string) => Promise<string>;
18
+ /**
19
+ * Optional batch unwrap: one entry per input event, preserving order — a Rumor on success or an
20
+ * Error for that wrap's failure. A single bad wrap never fails the batch.
21
+ */
22
+ unwrapGiftWraps?: (events: ReadonlyArray<Event>) => Promise<ReadonlyArray<Rumor | Error>>;
17
23
  };
18
24
  type Nip44Decryptor = {
19
25
  nip44Decrypt: (peer: string, payload: string) => Promise<string>;
@@ -66,6 +72,7 @@ type Nip59CryptoInput = {
66
72
  signEvent: (unsigned: UnsignedEvent) => Promise<Event>;
67
73
  nip44Encrypt?: (peer: string, plaintext: string) => Promise<string>;
68
74
  nip44Decrypt?: (peer: string, payload: string) => Promise<string>;
75
+ unwrapGiftWraps?: (events: ReadonlyArray<Event>) => Promise<ReadonlyArray<Rumor | Error>>;
69
76
  };
70
77
  /** Narrow a signer-shaped object to {@link Nip59Crypto}, or throw. */
71
78
  declare function requireNip59Crypto(crypto: Nip59CryptoInput): Nip59Crypto;
@@ -87,7 +94,9 @@ declare function createRumor(pubkey: string, template: {
87
94
  declare function createSeal(crypto: Nip59Crypto, recipient: string, rumor: Rumor, opts?: SealOptions): Promise<Event>;
88
95
  declare function createGiftWrap(seal: Event, recipient: string, opts?: WrapOptions): Event;
89
96
  declare function wrap(crypto: Nip59Crypto, recipient: string, rumor: Rumor, opts?: WrapOptions): Promise<Event>;
90
- declare function unwrap(crypto: Nip44Decryptor, giftWrap: Event): Promise<Rumor>;
97
+ declare function unwrap(crypto: Nip44Decryptor, giftWrap: Event, options?: {
98
+ verify?: EventVerifier | undefined;
99
+ }): Promise<Rumor>;
91
100
  //#endregion
92
101
  export { requireNip59Crypto as _, Rumor as a, wrap as b, TimestampRandomize as c, createRumor as d, createSeal as f, requireNip44Decryptor as g, randomPastTimestamp as h, Nip59Error as i, WrapOptions as l, isGiftWrapKind as m, Nip44Decryptor as n, SealOptions as o, eventToJson as p, Nip59Crypto as r, TWO_DAYS_SECS as s, GiftWrapTimestamps as t, createGiftWrap as u, rumorToJson as v, unwrap as y };
93
- //# sourceMappingURL=nip59-DfJ6Moeu.d.mts.map
102
+ //# sourceMappingURL=nip59-B6Ey8L9z.d.mts.map
@@ -1,5 +1,5 @@
1
1
  import { h as Tag, t as Event } from "./event-j3BwI5dF.mjs";
2
- import { t as EventBuilder } from "./builder-C72nJITG.mjs";
2
+ import { t as EventBuilder } from "./builder-Dk00TdNf.mjs";
3
3
  //#region src/nips/nip65.d.ts
4
4
  /** NIP-65 `r` tag marker: read-only, write-only, or unmarked (both). */
5
5
  type RelayMarker = "read" | "write" | "both";
@@ -20,4 +20,4 @@ declare function readRelays(items: ReadonlyArray<RelayListItem>): string[];
20
20
  declare function writeRelays(items: ReadonlyArray<RelayListItem>): string[];
21
21
  //#endregion
22
22
  export { relayListEventBuilder as a, readRelays as i, RelayMarker as n, relayListToTags as o, parseRelayList as r, writeRelays as s, RelayListItem as t };
23
- //# sourceMappingURL=nip65-CPNE0SV0.d.mts.map
23
+ //# sourceMappingURL=nip65-XrBjV9WM.d.mts.map
@@ -1,5 +1,5 @@
1
1
  import { t as Event } from "./event-j3BwI5dF.mjs";
2
- import { a as NostrError } from "./error-BPqAU5RB.mjs";
2
+ import { o as NostrError } from "./error-DE1UCZ2J.mjs";
3
3
  //#region src/nips/nip77.d.ts
4
4
  declare const PROTOCOL_VERSION = 97;
5
5
  declare const DEFAULT_FRAME_SIZE_LIMIT = 60000;
@@ -52,4 +52,4 @@ declare function runNegSession(opts: {
52
52
  }>;
53
53
  //#endregion
54
54
  export { NegentropyStorageVector as a, ReconcileOutcome as c, storageFromItems as d, Negentropy as i, runNegSession as l, MAX_NEG_ROUNDS as n, Nip77Error as o, NegItem as r, PROTOCOL_VERSION as s, DEFAULT_FRAME_SIZE_LIMIT as t, storageFromEvents as u };
55
- //# sourceMappingURL=nip77-DhpoL7d8.d.mts.map
55
+ //# sourceMappingURL=nip77-BAXijIt_.d.mts.map
@@ -1,6 +1,6 @@
1
1
  import { n as EventTemplate, t as Event } from "../event-j3BwI5dF.mjs";
2
- import { t as EventBuilder } from "../builder-C72nJITG.mjs";
3
- import { a as NostrError } from "../error-BPqAU5RB.mjs";
2
+ import { t as EventBuilder } from "../builder-Dk00TdNf.mjs";
3
+ import { o as NostrError } from "../error-DE1UCZ2J.mjs";
4
4
  import { t as ManualFetch } from "../http-kwWG-V1X.mjs";
5
5
  //#region src/nips/blossom.d.ts
6
6
  export type BlobDescriptor = {
@@ -93,5 +93,6 @@ export declare function uploadToServers(servers: ReadonlyArray<string>, file: Bl
93
93
  }): Promise<BlobDescriptor>;
94
94
  export declare function blossomServerListEventBuilder(servers: ReadonlyArray<string>): EventBuilder;
95
95
  export declare function parseBlossomServerList(event: Pick<Event, "kind" | "tags">): string[];
96
+ export declare function parseBlobDescriptor(json: unknown): BlobDescriptor;
96
97
  //#endregion
97
98
  //# sourceMappingURL=blossom.d.mts.map
@@ -1,6 +1,6 @@
1
- import { g as utf8Encoder, i as bytesToHex, n as assertHex32, p as nowSeconds, u as isRecord, v as EventValidationError, x as NostrError } from "../util-DfsyJ9nz.mjs";
1
+ import { b as EventValidationError, c as isHex32, g as trimAscii, i as bytesToHex, m as nowSeconds, n as assertHex32, u as isRecord, v as utf8Encoder, w as NostrError } from "../util-BcRBO6o-.mjs";
2
2
  import { t as Kind } from "../kind-I2Tvbe82.mjs";
3
- import { t as EventBuilder } from "../builder-CGgM_QHT.mjs";
3
+ import { t as EventBuilder } from "../builder-70CC2EBq.mjs";
4
4
  import { n as requireGlobalFetch, r as sendManual, t as fetchManual } from "../http-CuazNRIP.mjs";
5
5
  import { sha256 } from "@noble/hashes/sha2.js";
6
6
  import { base64urlnopad } from "@scure/base";
@@ -38,9 +38,9 @@ function getHashFromURL(url) {
38
38
  } catch {
39
39
  return;
40
40
  }
41
- const matches = path.match(/[0-9a-f]{64}/gi);
41
+ const matches = path.match(/[0-9a-f]{64}/g);
42
42
  if (matches === null || matches.length === 0) return;
43
- return matches.at(-1)?.toLowerCase();
43
+ return matches.at(-1);
44
44
  }
45
45
  async function sha256Blob(data) {
46
46
  let bytes;
@@ -74,7 +74,7 @@ function serverHostname(raw) {
74
74
  try {
75
75
  const url = new URL(raw.includes("://") ? raw : `https://${raw}`);
76
76
  if (url.protocol !== "http:" && url.protocol !== "https:") return;
77
- const hostname = url.hostname.toLowerCase();
77
+ const { hostname } = url;
78
78
  return hostname === "" ? void 0 : hostname;
79
79
  } catch {
80
80
  return;
@@ -82,7 +82,7 @@ function serverHostname(raw) {
82
82
  }
83
83
  function createAuthTemplate(verb, opts) {
84
84
  if (!AUTH_VERBS.has(verb)) throw new BlossomError(`invalid blossom auth verb: ${verb}`);
85
- if (opts?.message !== void 0 && opts.message.trim() === "") throw new BlossomError("blossom auth message must not be empty");
85
+ if (opts?.message !== void 0 && trimAscii(opts.message) === "") throw new BlossomError("blossom auth message must not be empty");
86
86
  const expiration = opts?.expiration ?? nowSeconds() + AUTH_EXPIRATION_SECS;
87
87
  const tags = [["t", verb], ["expiration", String(expiration)]];
88
88
  if (opts?.sha256 !== void 0) tags.push(["x", assertHex32(opts.sha256, "blob sha256")]);
@@ -285,7 +285,7 @@ function extensionFromHashUrl(url, hash) {
285
285
  } catch {
286
286
  return "";
287
287
  }
288
- const i = pathname.toLowerCase().lastIndexOf(hash);
288
+ const i = pathname.lastIndexOf(hash);
289
289
  if (i === -1) return "";
290
290
  return /^\.[^/]+/.exec(pathname.slice(i + 64))?.[0] ?? "";
291
291
  }
@@ -299,7 +299,7 @@ function blossomHttpError(method, url, res) {
299
299
  } catch {
300
300
  reason = void 0;
301
301
  }
302
- const detail = reason?.trim();
302
+ const detail = reason === void 0 ? void 0 : trimAscii(reason);
303
303
  return new BlossomError(detail === void 0 || detail === "" ? `blossom ${method} ${url} failed (${res.status})` : detail, { status: res.status });
304
304
  }
305
305
  async function blossomFetch(fetchImpl, url, init) {
@@ -339,8 +339,8 @@ function parseBlobDescriptor(json) {
339
339
  if (!isRecord(json)) throw new BlossomError("invalid blob descriptor");
340
340
  const raw = json;
341
341
  if (typeof raw["url"] !== "string" || !raw["url"]) throw new BlossomError("blob descriptor missing url");
342
- if (typeof raw["sha256"] !== "string") throw new BlossomError("blob descriptor missing sha256");
343
- const hash = assertHex32(raw["sha256"], "blob sha256");
342
+ if (typeof raw["sha256"] !== "string" || !isHex32(raw["sha256"])) throw new BlossomError("blob descriptor missing sha256");
343
+ const hash = raw["sha256"];
344
344
  if (typeof raw["size"] !== "number" || !Number.isInteger(raw["size"]) || raw["size"] < 0) throw new BlossomError("blob descriptor missing size");
345
345
  const desc = {
346
346
  url: raw["url"],
@@ -356,6 +356,6 @@ function requireDescriptorHash(desc, expected) {
356
356
  return desc;
357
357
  }
358
358
  //#endregion
359
- export { BlossomError, blobExists, blossomServerListEventBuilder, checkUpload, createAuthTemplate, createUploadAuth, deleteBlob, encodeAuthorizationHeader, getBlob, getHashFromURL, healBlobUrl, listBlobs, mirrorBlob, parseBlossomServerList, sha256Blob, upload, uploadToServers, verifyBlob };
359
+ export { BlossomError, blobExists, blossomServerListEventBuilder, checkUpload, createAuthTemplate, createUploadAuth, deleteBlob, encodeAuthorizationHeader, getBlob, getHashFromURL, healBlobUrl, listBlobs, mirrorBlob, parseBlobDescriptor, parseBlossomServerList, sha256Blob, upload, uploadToServers, verifyBlob };
360
360
 
361
361
  //# sourceMappingURL=blossom.mjs.map