@qntx/nostr 0.0.1 → 0.2.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 (187) hide show
  1. package/dist/abort-Ba8tDrOI.mjs +38 -0
  2. package/dist/abort-Ba8tDrOI.mjs.map +1 -0
  3. package/dist/{builder-BphJa-Y-.d.mts → builder-BWn55Paf.d.mts} +20 -14
  4. package/dist/{builder-q0VsvnYE.mjs → builder-CyrGKRVP.mjs} +32 -16
  5. package/dist/builder-CyrGKRVP.mjs.map +1 -0
  6. package/dist/{client-Bd8B1mYm.mjs → client-BWsX9Cv1.mjs} +354 -195
  7. package/dist/client-BWsX9Cv1.mjs.map +1 -0
  8. package/dist/client.d.mts +1 -1
  9. package/dist/client.mjs +1 -1
  10. package/dist/core.d.mts +8 -8
  11. package/dist/core.mjs +8 -9
  12. package/dist/error-BW7re1UN.d.mts +9 -0
  13. package/dist/error-B__OMBRY.mjs +14 -0
  14. package/dist/error-B__OMBRY.mjs.map +1 -0
  15. package/dist/{error-WEFS-R-4.d.mts → error-CR8Urc84.d.mts} +20 -8
  16. package/dist/{error-CwBxle6N.mjs → error-DrEIM7FO.mjs} +5 -3
  17. package/dist/error-DrEIM7FO.mjs.map +1 -0
  18. package/dist/{event-CzoQEFMF.mjs → event-D8Nfoh4K.mjs} +40 -32
  19. package/dist/event-D8Nfoh4K.mjs.map +1 -0
  20. package/dist/{event-B5D2jxt_.d.mts → event-fU3Y01PR.d.mts} +23 -20
  21. package/dist/{filter-BxwjEkZp.mjs → filter-BQl3GlHt.mjs} +42 -35
  22. package/dist/filter-BQl3GlHt.mjs.map +1 -0
  23. package/dist/filter-C0IMByE3.d.mts +37 -0
  24. package/dist/{gossip-C5RVB1UB.d.mts → gossip-BRQwIM_q.d.mts} +25 -23
  25. package/dist/{gossip-3xkRRCm_.mjs → gossip-oloDMWEj.mjs} +52 -34
  26. package/dist/gossip-oloDMWEj.mjs.map +1 -0
  27. package/dist/gossip.d.mts +1 -1
  28. package/dist/gossip.mjs +1 -1
  29. package/dist/{http-6vevbBlu.d.mts → http-C4HwIu4B.d.mts} +7 -7
  30. package/dist/{http-D2rhokZo.mjs → http-CuazNRIP.mjs} +10 -6
  31. package/dist/http-CuazNRIP.mjs.map +1 -0
  32. package/dist/index-B425CAcQ.d.mts +328 -0
  33. package/dist/index-BVyfUBuG.d.mts +181 -0
  34. package/dist/{index-BYMnhRIC.d.mts → index-Ba7bJ7DD.d.mts} +68 -50
  35. package/dist/{index-DaRyjo7O.d.mts → index-Boe8FshM.d.mts} +13 -7
  36. package/dist/index-CUIvFQdE.d.mts +73 -0
  37. package/dist/index-JdaS_uce.d.mts +207 -0
  38. package/dist/index.d.mts +22 -20
  39. package/dist/index.mjs +19 -18
  40. package/dist/{key-Dn-aX4x4.mjs → key-Bbtys6o7.mjs} +22 -14
  41. package/dist/key-Bbtys6o7.mjs.map +1 -0
  42. package/dist/{key-BiGxGLhg.d.mts → key-BlsYhN1V.d.mts} +9 -6
  43. package/dist/{kind-BLl4cLOc.mjs → kind-Cxwhhkx8.mjs} +2 -1
  44. package/dist/kind-Cxwhhkx8.mjs.map +1 -0
  45. package/dist/{loaders-BNQszDAn.mjs → loaders-o6YXB_Ri.mjs} +329 -305
  46. package/dist/loaders-o6YXB_Ri.mjs.map +1 -0
  47. package/dist/loaders.d.mts +3 -3
  48. package/dist/loaders.mjs +2 -2
  49. package/dist/memory-C2EVx0_O.mjs +46 -0
  50. package/dist/memory-C2EVx0_O.mjs.map +1 -0
  51. package/dist/{memory-BCmhZPek.mjs → memory-index-Do1mz0WF.mjs} +212 -82
  52. package/dist/memory-index-Do1mz0WF.mjs.map +1 -0
  53. package/dist/{message-C_Uj-Y-c.mjs → message-DcY6Fi9o.mjs} +38 -24
  54. package/dist/message-DcY6Fi9o.mjs.map +1 -0
  55. package/dist/{message-DJC-Jz6T.d.mts → message-iGPIqnn0.d.mts} +15 -8
  56. package/dist/{nip17-BWq-Kfq5.d.mts → nip17-DNEkz8rM.d.mts} +18 -17
  57. package/dist/{nip19-D7905oIv.d.mts → nip19-BevCJZsp.d.mts} +23 -3
  58. package/dist/{nip46-C_DoxiS_.d.mts → nip46-DDV4dC56.d.mts} +13 -12
  59. package/dist/{nip59-DljW_sYh.d.mts → nip59-zQgdLcEr.d.mts} +28 -25
  60. package/dist/{nip65-Di5hZRuV.d.mts → nip65-DBfh2smt.d.mts} +8 -3
  61. package/dist/{nip77-ByxlgoQO.d.mts → nip77-DH4RhS5r.d.mts} +9 -8
  62. package/dist/nips/blossom.d.mts +37 -37
  63. package/dist/nips/blossom.mjs +67 -52
  64. package/dist/nips/blossom.mjs.map +1 -1
  65. package/dist/nips/nip04.d.mts +2 -3
  66. package/dist/nips/nip04.mjs +6 -10
  67. package/dist/nips/nip04.mjs.map +1 -1
  68. package/dist/nips/nip05.d.mts +37 -43
  69. package/dist/nips/nip05.mjs +57 -57
  70. package/dist/nips/nip05.mjs.map +1 -1
  71. package/dist/nips/nip10.d.mts +18 -19
  72. package/dist/nips/nip10.mjs +76 -61
  73. package/dist/nips/nip10.mjs.map +1 -1
  74. package/dist/nips/nip11.d.mts +9 -8
  75. package/dist/nips/nip11.mjs +14 -11
  76. package/dist/nips/nip11.mjs.map +1 -1
  77. package/dist/nips/nip13.d.mts +9 -9
  78. package/dist/nips/nip13.mjs +15 -17
  79. package/dist/nips/nip13.mjs.map +1 -1
  80. package/dist/nips/nip17.d.mts +1 -1
  81. package/dist/nips/nip17.mjs +17 -12
  82. package/dist/nips/nip17.mjs.map +1 -1
  83. package/dist/nips/nip19.d.mts +1 -1
  84. package/dist/nips/nip19.mjs +52 -29
  85. package/dist/nips/nip19.mjs.map +1 -1
  86. package/dist/nips/nip21.d.mts +5 -6
  87. package/dist/nips/nip21.mjs +8 -7
  88. package/dist/nips/nip21.mjs.map +1 -1
  89. package/dist/nips/nip27.d.mts +7 -8
  90. package/dist/nips/nip27.mjs +26 -25
  91. package/dist/nips/nip27.mjs.map +1 -1
  92. package/dist/nips/nip42.d.mts +5 -6
  93. package/dist/nips/nip42.mjs +3 -3
  94. package/dist/nips/nip42.mjs.map +1 -1
  95. package/dist/nips/nip44.d.mts +24 -7
  96. package/dist/nips/nip44.mjs +25 -15
  97. package/dist/nips/nip44.mjs.map +1 -1
  98. package/dist/nips/nip46.d.mts +1 -1
  99. package/dist/nips/nip46.mjs +52 -49
  100. package/dist/nips/nip46.mjs.map +1 -1
  101. package/dist/nips/nip49.d.mts +8 -7
  102. package/dist/nips/nip49.mjs +16 -12
  103. package/dist/nips/nip49.mjs.map +1 -1
  104. package/dist/nips/nip51.d.mts +25 -27
  105. package/dist/nips/nip51.mjs +47 -36
  106. package/dist/nips/nip51.mjs.map +1 -1
  107. package/dist/nips/nip57.d.mts +12 -13
  108. package/dist/nips/nip57.mjs +35 -38
  109. package/dist/nips/nip57.mjs.map +1 -1
  110. package/dist/nips/nip59.d.mts +1 -1
  111. package/dist/nips/nip59.mjs +14 -18
  112. package/dist/nips/nip59.mjs.map +1 -1
  113. package/dist/nips/nip65.d.mts +1 -1
  114. package/dist/nips/nip65.mjs +11 -8
  115. package/dist/nips/nip65.mjs.map +1 -1
  116. package/dist/nips/nip77.d.mts +1 -1
  117. package/dist/nips/nip77.mjs +56 -34
  118. package/dist/nips/nip77.mjs.map +1 -1
  119. package/dist/nips/nip96.d.mts +21 -12
  120. package/dist/nips/nip96.mjs +59 -36
  121. package/dist/nips/nip96.mjs.map +1 -1
  122. package/dist/nips/nip98.d.mts +12 -12
  123. package/dist/nips/nip98.mjs +18 -19
  124. package/dist/nips/nip98.mjs.map +1 -1
  125. package/dist/{nostr_crypto_wasm_bg-hi5aXi5P.mjs → nostr_crypto_wasm_bg-Czrdty7Q.mjs} +1 -1
  126. package/dist/nostr_crypto_wasm_bg.wasm +0 -0
  127. package/dist/{pool-C_j1OmBB.mjs → pool-XmAGaufS.mjs} +611 -487
  128. package/dist/pool-XmAGaufS.mjs.map +1 -0
  129. package/dist/pool-tEr7c5bG.d.mts +302 -0
  130. package/dist/reactive-DDlThDSZ.mjs +324 -0
  131. package/dist/reactive-DDlThDSZ.mjs.map +1 -0
  132. package/dist/reactive-DFpqtIf_.d.mts +66 -0
  133. package/dist/relay.d.mts +5 -4
  134. package/dist/relay.mjs +2 -2
  135. package/dist/report-CHld4uWj.mjs +32 -0
  136. package/dist/report-CHld4uWj.mjs.map +1 -0
  137. package/dist/{signer-CZ6K6FUc.mjs → signer-CKR9EHce.mjs} +107 -72
  138. package/dist/signer-CKR9EHce.mjs.map +1 -0
  139. package/dist/signer.d.mts +3 -3
  140. package/dist/signer.mjs +3 -2
  141. package/dist/storage-xXxLGCVb.mjs +1376 -0
  142. package/dist/storage-xXxLGCVb.mjs.map +1 -0
  143. package/dist/storage.d.mts +4 -4
  144. package/dist/storage.mjs +5 -4
  145. package/dist/store.d.mts +2 -0
  146. package/dist/store.mjs +2 -0
  147. package/dist/testing.d.mts +125 -0
  148. package/dist/testing.mjs +957 -0
  149. package/dist/testing.mjs.map +1 -0
  150. package/dist/types-2pCUprRU.d.mts +47 -0
  151. package/dist/types-CRQEk0Oi.d.mts +19 -0
  152. package/dist/util-LfQirgDk.mjs +113 -0
  153. package/dist/util-LfQirgDk.mjs.map +1 -0
  154. package/dist/wasm.d.mts +4 -4
  155. package/dist/wasm.mjs +55 -56
  156. package/dist/wasm.mjs.map +1 -1
  157. package/dist/websocket-CmeqvAkp.d.mts +27 -0
  158. package/package.json +31 -9
  159. package/dist/builder-q0VsvnYE.mjs.map +0 -1
  160. package/dist/client-Bd8B1mYm.mjs.map +0 -1
  161. package/dist/error-B49rbDzT.d.mts +0 -7
  162. package/dist/error-CwBxle6N.mjs.map +0 -1
  163. package/dist/error-DZEs5OuZ.mjs +0 -24
  164. package/dist/error-DZEs5OuZ.mjs.map +0 -1
  165. package/dist/event-CzoQEFMF.mjs.map +0 -1
  166. package/dist/filter-BxwjEkZp.mjs.map +0 -1
  167. package/dist/filter-DUEKP3q1.d.mts +0 -40
  168. package/dist/gossip-3xkRRCm_.mjs.map +0 -1
  169. package/dist/http-D2rhokZo.mjs.map +0 -1
  170. package/dist/index-C0C4e-Gn.d.mts +0 -55
  171. package/dist/index-DgOugrpF.d.mts +0 -275
  172. package/dist/index-EwL6fHLo.d.mts +0 -267
  173. package/dist/index-k2YhCgsh.d.mts +0 -57
  174. package/dist/key-Dn-aX4x4.mjs.map +0 -1
  175. package/dist/kind-BLl4cLOc.mjs.map +0 -1
  176. package/dist/loaders-BNQszDAn.mjs.map +0 -1
  177. package/dist/memory-BCmhZPek.mjs.map +0 -1
  178. package/dist/message-C_Uj-Y-c.mjs.map +0 -1
  179. package/dist/pool-C_j1OmBB.mjs.map +0 -1
  180. package/dist/pool-DhRi7l-b.d.mts +0 -297
  181. package/dist/signer-CZ6K6FUc.mjs.map +0 -1
  182. package/dist/storage-DOy0iLRE.mjs +0 -870
  183. package/dist/storage-DOy0iLRE.mjs.map +0 -1
  184. package/dist/types-BzyADqnw.d.mts +0 -19
  185. package/dist/types-Dn24TPFs.d.mts +0 -43
  186. package/dist/util-6tfLQ9JF.mjs +0 -73
  187. package/dist/util-6tfLQ9JF.mjs.map +0 -1
@@ -1,7 +1,7 @@
1
- import { a as isHex32 } from "./util-6tfLQ9JF.mjs";
2
- import { i as isEphemeralKind, t as Kind } from "./kind-BLl4cLOc.mjs";
3
- import { a as itemCompare, h as eventAddress, i as isReplaceableWinner, u as sortEvents, y as parseEventAddress } from "./event-CzoQEFMF.mjs";
4
- import { o as matchFilter } from "./filter-BxwjEkZp.mjs";
1
+ import { o as isHex32 } from "./util-LfQirgDk.mjs";
2
+ import { i as isEphemeralKind, t as Kind } from "./kind-Cxwhhkx8.mjs";
3
+ import { _ as formatEventAddress, a as isReplaceableWinner, b as parseEventAddress, d as sortEvents, g as eventAddress, m as validateSignedEvent, o as itemCompare, t as compareEventsDesc } from "./event-D8Nfoh4K.mjs";
4
+ import { a as matchFilter } from "./filter-BQl3GlHt.mjs";
5
5
  //#region src/storage/deletion.ts
6
6
  /**
7
7
  * NIP-09 application plan for a kind 5 event.
@@ -12,14 +12,14 @@ import { o as matchFilter } from "./filter-BxwjEkZp.mjs";
12
12
  * - Deleting a deletion request is a no-op.
13
13
  */
14
14
  function planDeletion(deletion, getById) {
15
- const pubkey = deletion.pubkey.toLowerCase();
15
+ const { pubkey } = deletion;
16
16
  const removeIds = [];
17
17
  const pendingIds = [];
18
18
  const coordinates = [];
19
19
  const seenIds = /* @__PURE__ */ new Set();
20
20
  const seenCoords = /* @__PURE__ */ new Set();
21
21
  for (const tag of deletion.tags) {
22
- if (tag[0] === "e" && tag[1] && isHex32(tag[1])) {
22
+ if (tag[0] === "e" && tag[1] !== void 0 && isHex32(tag[1].toLowerCase())) {
23
23
  const id = tag[1].toLowerCase();
24
24
  if (seenIds.has(id)) continue;
25
25
  seenIds.add(id);
@@ -32,11 +32,11 @@ function planDeletion(deletion, getById) {
32
32
  continue;
33
33
  }
34
34
  if (existing.kind === Kind.EventDeletion) continue;
35
- if (existing.pubkey.toLowerCase() !== pubkey) continue;
35
+ if (existing.pubkey !== pubkey) continue;
36
36
  removeIds.push(id);
37
37
  continue;
38
38
  }
39
- if (tag[0] === "a" && tag[1]) {
39
+ if (tag[0] === "a" && tag[1] !== void 0) {
40
40
  const coord = parseEventAddress(tag[1]);
41
41
  if (!coord || coord.pubkey !== pubkey) continue;
42
42
  const key = `${coord.kind}:${coord.pubkey}:${coord.identifier}`;
@@ -68,9 +68,9 @@ var DeletionState = class {
68
68
  if (this.ids.has(event.id)) return true;
69
69
  if (event.kind === Kind.EventDeletion) return false;
70
70
  const pendingPk = this.pending.get(event.id);
71
- if (pendingPk && event.pubkey.toLowerCase() === pendingPk) return true;
71
+ if (pendingPk !== void 0 && event.pubkey === pendingPk) return true;
72
72
  const addr = eventAddress(event);
73
- if (!addr) return false;
73
+ if (addr === void 0) return false;
74
74
  const until = this.coordinates.get(addr);
75
75
  return until !== void 0 && event.created_at <= until;
76
76
  }
@@ -82,6 +82,7 @@ var DeletionState = class {
82
82
  for (const p of plan.pendingIds) if (!this.ids.has(p.id)) this.pending.set(p.id, p.pubkey);
83
83
  for (const c of plan.coordinates) {
84
84
  const prev = this.coordinates.get(c.key) ?? Number.NEGATIVE_INFINITY;
85
+ this.coordinates.delete(c.key);
85
86
  this.coordinates.set(c.key, Math.max(prev, c.until));
86
87
  }
87
88
  }
@@ -96,21 +97,16 @@ function coordinateRemovals(coordinates, getCurrent) {
96
97
  }
97
98
  //#endregion
98
99
  //#region src/storage/put.ts
99
- function normalizeEvent(event) {
100
- const id = event.id.toLowerCase();
101
- const pubkey = event.pubkey.toLowerCase();
102
- if (id === event.id && pubkey === event.pubkey) return event;
103
- return {
104
- ...event,
105
- id,
106
- pubkey
107
- };
108
- }
109
100
  function outboxBoundKey(pubkey, kind) {
110
101
  return `${pubkey.toLowerCase()}:${kind}`;
111
102
  }
112
103
  function decidePut(raw, lookup) {
113
- const event = normalizeEvent(raw);
104
+ if (!validateSignedEvent(raw)) return {
105
+ action: "skip",
106
+ result: "invalid",
107
+ event: raw
108
+ };
109
+ const event = raw;
114
110
  if (lookup.deletion.ids.has(event.id) || lookup.getById(event.id)) return {
115
111
  action: "skip",
116
112
  result: "duplicate",
@@ -137,19 +133,25 @@ function decidePut(raw, lookup) {
137
133
  event
138
134
  };
139
135
  const address = eventAddress(event);
140
- if (address) {
136
+ if (address !== void 0) {
141
137
  const prev = lookup.getReplaceable(address);
142
- if (prev && !isReplaceableWinner(event, prev)) return {
138
+ if (prev && prev.id !== event.id && !isReplaceableWinner(event, prev)) return {
143
139
  action: "skip",
144
140
  result: "rejected",
145
141
  event
146
142
  };
147
- return {
143
+ if (prev !== void 0 && prev.evicted !== true) return {
148
144
  action: "insert",
149
- result: prev ? "replaced" : "accepted",
145
+ result: "replaced",
150
146
  event,
151
147
  address,
152
- replaceId: prev?.id
148
+ replaceId: prev.id
149
+ };
150
+ return {
151
+ action: "insert",
152
+ result: "accepted",
153
+ event,
154
+ address
153
155
  };
154
156
  }
155
157
  return {
@@ -159,74 +161,107 @@ function decidePut(raw, lookup) {
159
161
  };
160
162
  }
161
163
  function applyPutMemory(s, d) {
162
- switch (d.action) {
163
- case "skip": return d.result;
164
- case "tombstone":
165
- s.deletion.ids.add(d.event.id);
166
- s.deletion.pending.delete(d.event.id);
167
- return "duplicate";
168
- case "delete":
169
- s.deletion.pending.delete(d.event.id);
170
- s.deletion.absorb(d.plan);
171
- for (const id of d.plan.removeIds) s.indexRemove(id);
172
- for (const id of d.coordIds) {
173
- s.deletion.ids.add(id);
174
- s.indexRemove(id);
175
- }
176
- s.indexInsert(d.event);
177
- return "deleted";
178
- case "insert":
179
- if (d.replaceId) s.indexRemove(d.replaceId);
180
- s.indexInsert(d.event);
181
- return d.result;
164
+ if (d.action === "skip") return d.result;
165
+ if (d.action === "tombstone") {
166
+ s.deletion.ids.add(d.event.id);
167
+ s.deletion.pending.delete(d.event.id);
168
+ return "duplicate";
182
169
  }
170
+ if (d.action === "delete") {
171
+ s.deletion.pending.delete(d.event.id);
172
+ s.deletion.absorb(d.plan);
173
+ for (const id of d.plan.removeIds) s.indexRemove(id);
174
+ for (const id of d.coordIds) {
175
+ s.deletion.ids.add(id);
176
+ s.indexRemove(id);
177
+ }
178
+ s.indexInsert(d.event);
179
+ return "deleted";
180
+ }
181
+ if (d.replaceId !== void 0) s.indexRemove(d.replaceId);
182
+ s.indexInsert(d.event);
183
+ return d.result;
183
184
  }
184
185
  //#endregion
185
- //#region src/storage/memory.ts
186
+ //#region src/storage/memory-index.ts
186
187
  /**
187
- * In-memory event store with NIP-01 replaceable / addressable / ephemeral
188
- * handling and NIP-09 deletion (kind 5) application.
188
+ * Synchronous in-memory event index with NIP-01 replaceable / addressable / ephemeral handling and
189
+ * NIP-09 deletion (kind 5) application. Shared by {@link MemoryEventStore} (async facade) and
190
+ * `ReactiveEventStore`.
189
191
  */
190
- var MemoryEventStore = class {
192
+ var MemoryIndex = class {
191
193
  #byId = /* @__PURE__ */ new Map();
192
194
  #byPubkey = /* @__PURE__ */ new Map();
193
195
  #byKind = /* @__PURE__ */ new Map();
194
196
  #byKindPubkey = /* @__PURE__ */ new Map();
195
197
  #byEpTag = /* @__PURE__ */ new Map();
196
198
  #replaceable = /* @__PURE__ */ new Map();
199
+ #watermarks = /* @__PURE__ */ new Map();
200
+ #watermarkIds = /* @__PURE__ */ new Map();
197
201
  #deletion = new DeletionState();
198
202
  #outboxBounds = /* @__PURE__ */ new Map();
199
- async put(raw) {
200
- const lookup = {
203
+ #maxTombstones;
204
+ #maxWatermarks;
205
+ #onInsert;
206
+ #onRemove;
207
+ constructor(opts) {
208
+ this.#maxTombstones = opts?.maxTombstones;
209
+ this.#maxWatermarks = opts?.maxWatermarks ?? 0;
210
+ this.#onInsert = opts?.onInsert === void 0 ? void 0 : (event) => opts.onInsert?.(event);
211
+ this.#onRemove = opts?.onRemove === void 0 ? void 0 : (event) => opts.onRemove?.(event);
212
+ }
213
+ put(raw) {
214
+ const decision = decidePut(raw, {
201
215
  deletion: this.#deletion,
202
216
  getById: (id) => this.#byId.get(id),
203
217
  getReplaceable: (addr) => {
204
218
  const id = this.#replaceable.get(addr);
205
- const ev = id ? this.#byId.get(id) : void 0;
206
- return ev ? {
219
+ const ev = id === void 0 ? void 0 : this.#byId.get(id);
220
+ if (ev) return {
207
221
  id: ev.id,
208
222
  created_at: ev.created_at
209
- } : void 0;
223
+ };
224
+ const watermark = this.#watermarks.get(addr);
225
+ return watermark === void 0 ? void 0 : {
226
+ ...watermark,
227
+ evicted: true
228
+ };
210
229
  }
211
- };
212
- return applyPutMemory({
230
+ });
231
+ if (decision.action === "delete") {
232
+ for (const p of decision.plan.pendingIds) this.#dropWatermarkId(p.id);
233
+ for (const c of decision.plan.coordinates) this.#dropWatermark(c.key);
234
+ }
235
+ const result = applyPutMemory({
213
236
  deletion: this.#deletion,
214
237
  indexInsert: (e) => this.#indexInsert(e),
215
238
  indexRemove: (id) => this.#indexRemove(id)
216
- }, decidePut(raw, lookup));
239
+ }, decision);
240
+ this.#trimTombstones();
241
+ return result;
217
242
  }
218
- /** Sequential `put` in input order. No transaction: a throw leaves earlier events applied. */
219
- async putMany(events) {
243
+ /** Sequential `put` in input order. */
244
+ putMany(events) {
220
245
  const results = [];
221
- for (const event of events) results.push(await this.put(event));
246
+ for (const event of events) results.push(this.put(event));
222
247
  return results;
223
248
  }
224
- async get(id) {
249
+ get(id) {
225
250
  const key = id.toLowerCase();
226
- if (this.#deletion.ids.has(key)) return void 0;
251
+ if (this.#deletion.ids.has(key)) return;
227
252
  return this.#byId.get(key);
228
253
  }
229
- async query(filters) {
254
+ /** Current event at a `kind:pubkey:d` coordinate (replaceable or addressable). */
255
+ getByAddress(address) {
256
+ const coord = parseEventAddress(address);
257
+ if (!coord) return;
258
+ const id = this.#replaceable.get(formatEventAddress(coord.kind, coord.pubkey, coord.identifier));
259
+ if (id === void 0) return;
260
+ const key = id.toLowerCase();
261
+ if (this.#deletion.ids.has(key)) return;
262
+ return this.#byId.get(key);
263
+ }
264
+ query(filters) {
230
265
  const seen = /* @__PURE__ */ new Set();
231
266
  const events = [];
232
267
  for (const filter of filters) for (const event of this.#matchedEvents(filter)) {
@@ -237,12 +272,12 @@ var MemoryEventStore = class {
237
272
  sortEvents(events);
238
273
  return events;
239
274
  }
240
- async count(filters) {
275
+ count(filters) {
241
276
  const seen = /* @__PURE__ */ new Set();
242
277
  for (const filter of filters) for (const event of this.#matchedEvents(filter)) seen.add(event.id);
243
278
  return seen.size;
244
279
  }
245
- async negentropyItems(filter) {
280
+ negentropyItems(filter) {
246
281
  if (filter.limit === 0) return [];
247
282
  const items = [];
248
283
  this.#eachCandidate(filter, (event) => {
@@ -260,7 +295,8 @@ var MemoryEventStore = class {
260
295
  items.sort(itemCompare);
261
296
  return items;
262
297
  }
263
- async remove(ids) {
298
+ /** Remove by id and tombstone (like a local NIP-09). Returns removed count. */
299
+ remove(ids) {
264
300
  let n = 0;
265
301
  for (const raw of ids) {
266
302
  const id = raw.toLowerCase();
@@ -268,9 +304,49 @@ var MemoryEventStore = class {
268
304
  this.#deletion.ids.add(id);
269
305
  this.#deletion.pending.delete(id);
270
306
  }
307
+ this.#trimTombstones();
308
+ return n;
309
+ }
310
+ /**
311
+ * Remove by id without writing a tombstone (LRU eviction). When the evicted event is the current
312
+ * winner of a replaceable/addressable address, a `{ id, created_at }` watermark is recorded so
313
+ * stale versions stay rejected while the body is gone; the watermarks are FIFO-bounded by
314
+ * `maxWatermarks`.
315
+ */
316
+ evict(ids) {
317
+ let n = 0;
318
+ for (const raw of ids) {
319
+ const id = raw.toLowerCase();
320
+ const event = this.#byId.get(id);
321
+ if (event === void 0) continue;
322
+ const addr = eventAddress(event);
323
+ const winner = addr !== void 0 && this.#replaceable.get(addr) === id;
324
+ if (this.#indexRemove(id)) n += 1;
325
+ if (winner) this.#setWatermark(addr, {
326
+ id,
327
+ created_at: event.created_at
328
+ });
329
+ }
271
330
  return n;
272
331
  }
273
- async getOutboxBound(pubkey, kind) {
332
+ /**
333
+ * True when the id is tombstoned, or the coordinate has a deletion marker that still covers the
334
+ * current event at that address: a replacement published after the marker's `until` clears the
335
+ * deletion. Coordinates are matched on the canonical lowercase pubkey.
336
+ */
337
+ isDeleted(idOrAddress) {
338
+ if (this.#deletion.ids.has(idOrAddress.toLowerCase())) return true;
339
+ const coord = parseEventAddress(idOrAddress);
340
+ if (!coord) return false;
341
+ const address = formatEventAddress(coord.kind, coord.pubkey, coord.identifier);
342
+ const until = this.#deletion.coordinates.get(address);
343
+ if (until === void 0) return false;
344
+ const id = this.#replaceable.get(address);
345
+ if (id === void 0) return true;
346
+ const event = this.#byId.get(id);
347
+ return event === void 0 || event.created_at <= until;
348
+ }
349
+ getOutboxBound(pubkey, kind) {
274
350
  const persisted = this.#outboxBounds.get(outboxBoundKey(pubkey, kind));
275
351
  if (persisted) return {
276
352
  oldest: persisted.oldest,
@@ -278,19 +354,22 @@ var MemoryEventStore = class {
278
354
  };
279
355
  return this.#deriveOutboxBound(pubkey, kind);
280
356
  }
281
- async setOutboxBound(pubkey, kind, bound) {
357
+ setOutboxBound(pubkey, kind, bound) {
282
358
  this.#outboxBounds.set(outboxBoundKey(pubkey, kind), {
283
359
  oldest: bound.oldest,
284
360
  newest: bound.newest
285
361
  });
286
362
  }
287
- async clear() {
363
+ clear() {
364
+ if (this.#onRemove) for (const event of this.#byId.values()) this.#onRemove(event);
288
365
  this.#byId.clear();
289
366
  this.#byPubkey.clear();
290
367
  this.#byKind.clear();
291
368
  this.#byKindPubkey.clear();
292
369
  this.#byEpTag.clear();
293
370
  this.#replaceable.clear();
371
+ this.#watermarks.clear();
372
+ this.#watermarkIds.clear();
294
373
  this.#deletion.clear();
295
374
  this.#outboxBounds.clear();
296
375
  }
@@ -299,7 +378,7 @@ var MemoryEventStore = class {
299
378
  }
300
379
  #indexInsert(event) {
301
380
  this.#byId.set(event.id, event);
302
- const pubkey = event.pubkey.toLowerCase();
381
+ const { pubkey } = event;
303
382
  addToSet(this.#byPubkey, pubkey, event.id);
304
383
  addToSet(this.#byKind, event.kind, event.id);
305
384
  addToSet(this.#byKindPubkey, `${event.kind}:${pubkey}`, event.id);
@@ -308,14 +387,19 @@ var MemoryEventStore = class {
308
387
  addToSet(this.#byEpTag, `${tag[0]}:${tag[1].toLowerCase()}`, event.id);
309
388
  }
310
389
  const addr = eventAddress(event);
311
- if (addr) this.#replaceable.set(addr, event.id);
390
+ if (addr !== void 0) {
391
+ this.#replaceable.set(addr, event.id);
392
+ this.#dropWatermark(addr);
393
+ }
394
+ this.#onInsert?.(event);
312
395
  }
313
396
  #indexRemove(id) {
314
- const key = id.toLowerCase();
397
+ const key = id;
398
+ this.#dropWatermarkId(key);
315
399
  const event = this.#byId.get(key);
316
400
  if (!event) return false;
317
401
  this.#byId.delete(key);
318
- const pubkey = event.pubkey.toLowerCase();
402
+ const { pubkey } = event;
319
403
  removeFromSet(this.#byPubkey, pubkey, key);
320
404
  removeFromSet(this.#byKind, event.kind, key);
321
405
  removeFromSet(this.#byKindPubkey, `${event.kind}:${pubkey}`, key);
@@ -324,7 +408,8 @@ var MemoryEventStore = class {
324
408
  removeFromSet(this.#byEpTag, `${tag[0]}:${tag[1].toLowerCase()}`, key);
325
409
  }
326
410
  const addr = eventAddress(event);
327
- if (addr && this.#replaceable.get(addr) === key) this.#replaceable.delete(addr);
411
+ if (addr !== void 0 && this.#replaceable.get(addr) === key) this.#replaceable.delete(addr);
412
+ this.#onRemove?.(event);
328
413
  return true;
329
414
  }
330
415
  #matchedEvents(filter) {
@@ -336,12 +421,12 @@ var MemoryEventStore = class {
336
421
  matched.push(event);
337
422
  });
338
423
  sortEvents(matched);
339
- return filter.limit !== void 0 ? matched.slice(0, filter.limit) : matched;
424
+ return filter.limit === void 0 ? matched : matched.slice(0, filter.limit);
340
425
  }
341
426
  #deriveOutboxBound(pubkey, kind) {
342
427
  const byPk = this.#byPubkey.get(pubkey.toLowerCase());
343
428
  const byKind = this.#byKind.get(kind);
344
- if (!byPk || !byKind) return void 0;
429
+ if (!byPk || !byKind) return;
345
430
  let oldest;
346
431
  let newest;
347
432
  for (const id of byPk) {
@@ -351,7 +436,7 @@ var MemoryEventStore = class {
351
436
  if (oldest === void 0 || event.created_at < oldest) oldest = event.created_at;
352
437
  if (newest === void 0 || event.created_at > newest) newest = event.created_at;
353
438
  }
354
- if (oldest === void 0 || newest === void 0) return void 0;
439
+ if (oldest === void 0 || newest === void 0) return;
355
440
  return {
356
441
  oldest,
357
442
  newest
@@ -423,6 +508,52 @@ var MemoryEventStore = class {
423
508
  }
424
509
  for (const event of this.#byId.values()) visit(event);
425
510
  }
511
+ #setWatermark(addr, watermark) {
512
+ if (this.#maxWatermarks === 0) return;
513
+ this.#dropWatermark(addr);
514
+ this.#watermarks.set(addr, watermark);
515
+ this.#watermarkIds.set(watermark.id, addr);
516
+ while (this.#watermarks.size > this.#maxWatermarks) {
517
+ const oldest = this.#watermarks.keys().next().value;
518
+ if (oldest === void 0) break;
519
+ this.#dropWatermark(oldest);
520
+ }
521
+ }
522
+ #dropWatermark(addr) {
523
+ const watermark = this.#watermarks.get(addr);
524
+ if (watermark === void 0) return;
525
+ this.#watermarks.delete(addr);
526
+ this.#watermarkIds.delete(watermark.id);
527
+ }
528
+ #dropWatermarkId(id) {
529
+ const addr = this.#watermarkIds.get(id);
530
+ if (addr === void 0) return;
531
+ this.#watermarkIds.delete(id);
532
+ this.#watermarks.delete(addr);
533
+ }
534
+ #trimTombstones() {
535
+ const cap = this.#maxTombstones;
536
+ if (cap === void 0) return;
537
+ let excess = this.#deletion.ids.size - cap;
538
+ for (const id of this.#deletion.ids) {
539
+ if (excess <= 0) break;
540
+ this.#deletion.ids.delete(id);
541
+ this.#deletion.pending.delete(id);
542
+ excess--;
543
+ }
544
+ excess = this.#deletion.pending.size - cap;
545
+ for (const id of this.#deletion.pending.keys()) {
546
+ if (excess <= 0) break;
547
+ this.#deletion.pending.delete(id);
548
+ excess--;
549
+ }
550
+ excess = this.#deletion.coordinates.size - cap;
551
+ for (const key of this.#deletion.coordinates.keys()) {
552
+ if (excess <= 0) break;
553
+ this.#deletion.coordinates.delete(key);
554
+ excess--;
555
+ }
556
+ }
426
557
  };
427
558
  function visitEpTagIds(byEpTag, byId, name, values, seen, visit) {
428
559
  if (values === void 0) return;
@@ -452,10 +583,9 @@ function removeFromSet(map, key, id) {
452
583
  if (set.size === 0) map.delete(key);
453
584
  }
454
585
  function queryItemOrder(a, b) {
455
- if (a.created_at !== b.created_at) return b.created_at - a.created_at;
456
- return a.id.localeCompare(b.id);
586
+ return compareEventsDesc(a, b);
457
587
  }
458
588
  //#endregion
459
- export { DeletionState as a, outboxBoundKey as i, decidePut as n, planDeletion as o, normalizeEvent as r, MemoryEventStore as t };
589
+ export { planDeletion as a, DeletionState as i, decidePut as n, outboxBoundKey as r, MemoryIndex as t };
460
590
 
461
- //# sourceMappingURL=memory-BCmhZPek.mjs.map
591
+ //# sourceMappingURL=memory-index-Do1mz0WF.mjs.map
@@ -0,0 +1 @@
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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"}