@qntx/nostr 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/dist/{abort-Ba8tDrOI.mjs → abort-DBq2STPj.mjs} +13 -6
  2. package/dist/abort-DBq2STPj.mjs.map +1 -0
  3. package/dist/{builder-sSflu6nB.d.mts → builder-CTwkB2zM.d.mts} +14 -15
  4. package/dist/{builder-9tjI6WQ3.mjs → builder-aNU2gGFP.mjs} +11 -17
  5. package/dist/builder-aNU2gGFP.mjs.map +1 -0
  6. package/dist/{client-CadO5fAX.mjs → client-Cj2lUaVh.mjs} +141 -353
  7. package/dist/client-Cj2lUaVh.mjs.map +1 -0
  8. package/dist/client.d.mts +2 -2
  9. package/dist/client.mjs +2 -2
  10. package/dist/collections-DtwMc1NU.mjs +33 -0
  11. package/dist/collections-DtwMc1NU.mjs.map +1 -0
  12. package/dist/core.d.mts +9 -8
  13. package/dist/core.mjs +7 -7
  14. package/dist/emitter-DkCY6NkU.mjs +32 -0
  15. package/dist/emitter-DkCY6NkU.mjs.map +1 -0
  16. package/dist/{error-BW7re1UN.d.mts → error-BD_0QFKT.d.mts} +2 -2
  17. package/dist/{error-DrEIM7FO.mjs → error-BILMe3Gr.mjs} +2 -2
  18. package/dist/{error-DrEIM7FO.mjs.map → error-BILMe3Gr.mjs.map} +1 -1
  19. package/dist/{error-CR8Urc84.d.mts → error-BquXDmIN.d.mts} +4 -4
  20. package/dist/{error-B__OMBRY.mjs → error-DlhGW4mJ.mjs} +2 -2
  21. package/dist/{error-B__OMBRY.mjs.map → error-DlhGW4mJ.mjs.map} +1 -1
  22. package/dist/{event-D8Nfoh4K.mjs → event-CHf47dPM.mjs} +16 -10
  23. package/dist/event-CHf47dPM.mjs.map +1 -0
  24. package/dist/{event-fU3Y01PR.d.mts → event-DJeb_DIL.d.mts} +4 -2
  25. package/dist/{filter-C0IMByE3.d.mts → filter-jI_9htF7.d.mts} +2 -2
  26. package/dist/{gossip-oloDMWEj.mjs → gossip-DFxIBlsr.mjs} +23 -33
  27. package/dist/gossip-DFxIBlsr.mjs.map +1 -0
  28. package/dist/{gossip-DpIvkBuj.d.mts → gossip-OOeeFzG5.d.mts} +37 -28
  29. package/dist/gossip.d.mts +1 -1
  30. package/dist/gossip.mjs +1 -1
  31. package/dist/{index-D1bqFHKB.d.mts → index-BxCBX-8R.d.mts} +18 -18
  32. package/dist/{index-lvXeER5t.d.mts → index-CPVEyzNX.d.mts} +32 -50
  33. package/dist/{index-MxC28S4P.d.mts → index-C_w2WfVz.d.mts} +27 -29
  34. package/dist/{index-CNl2q67H.d.mts → index-Cl_1XdYP.d.mts} +111 -180
  35. package/dist/{index-CvkvqB16.d.mts → index-DU9tRFTZ.d.mts} +7 -20
  36. package/dist/{index-q4MDce_r.d.mts → index-DyKwQnbt.d.mts} +6 -8
  37. package/dist/index.d.mts +23 -21
  38. package/dist/index.mjs +21 -19
  39. package/dist/{instance-DQE8BoWt.mjs → instance-C62w3cEt.mjs} +26 -27
  40. package/dist/instance-C62w3cEt.mjs.map +1 -0
  41. package/dist/{instance-CIca-ynn.d.mts → instance-DjsL8h84.d.mts} +5 -5
  42. package/dist/{key-Cv4_pN3x.d.mts → key-D_5OgGT7.d.mts} +11 -7
  43. package/dist/{key-Bps-LqX7.mjs → key-DnVpchl6.mjs} +13 -9
  44. package/dist/key-DnVpchl6.mjs.map +1 -0
  45. package/dist/{loaders-o6YXB_Ri.mjs → loaders-BmPkQfzI.mjs} +73 -82
  46. package/dist/loaders-BmPkQfzI.mjs.map +1 -0
  47. package/dist/loaders.d.mts +3 -3
  48. package/dist/loaders.mjs +2 -2
  49. package/dist/{memory-C2EVx0_O.mjs → memory-CdJ07kub.mjs} +2 -2
  50. package/dist/memory-CdJ07kub.mjs.map +1 -0
  51. package/dist/{memory-index-Do1mz0WF.mjs → memory-index-Cbc83vOZ.mjs} +30 -247
  52. package/dist/memory-index-Cbc83vOZ.mjs.map +1 -0
  53. package/dist/message-oNr7t0SJ.mjs +194 -0
  54. package/dist/message-oNr7t0SJ.mjs.map +1 -0
  55. package/dist/{message-iGPIqnn0.d.mts → message-tclYCVNc.d.mts} +6 -6
  56. package/dist/{nip17-CKIufV98.d.mts → nip17-B5WgW4C4.d.mts} +6 -7
  57. package/dist/{nip19-BevCJZsp.d.mts → nip19-DpPN17bY.d.mts} +25 -31
  58. package/dist/{nip46-DDV4dC56.d.mts → nip46-BHaxC9TZ.d.mts} +19 -21
  59. package/dist/nip51-CR0Q8eym.d.mts +87 -0
  60. package/dist/{nip59-zQgdLcEr.d.mts → nip59-C-RWFQIv.d.mts} +11 -5
  61. package/dist/nip65-D1fbmhOg.d.mts +23 -0
  62. package/dist/{nip77-DH4RhS5r.d.mts → nip77-DzUwUxHq.d.mts} +8 -9
  63. package/dist/nips/blossom.d.mts +17 -13
  64. package/dist/nips/blossom.mjs +30 -9
  65. package/dist/nips/blossom.mjs.map +1 -1
  66. package/dist/nips/nip04.mjs +17 -7
  67. package/dist/nips/nip04.mjs.map +1 -1
  68. package/dist/nips/nip05.d.mts +9 -10
  69. package/dist/nips/nip05.mjs +1 -4
  70. package/dist/nips/nip05.mjs.map +1 -1
  71. package/dist/nips/nip10.d.mts +8 -8
  72. package/dist/nips/nip10.mjs +34 -32
  73. package/dist/nips/nip10.mjs.map +1 -1
  74. package/dist/nips/nip11.d.mts +28 -28
  75. package/dist/nips/nip11.mjs +4 -4
  76. package/dist/nips/nip11.mjs.map +1 -1
  77. package/dist/nips/nip13.d.mts +7 -7
  78. package/dist/nips/nip13.mjs +32 -10
  79. package/dist/nips/nip13.mjs.map +1 -1
  80. package/dist/nips/nip17.d.mts +1 -1
  81. package/dist/nips/nip17.mjs +16 -34
  82. package/dist/nips/nip17.mjs.map +1 -1
  83. package/dist/nips/nip19.d.mts +2 -2
  84. package/dist/nips/nip19.mjs +16 -28
  85. package/dist/nips/nip19.mjs.map +1 -1
  86. package/dist/nips/nip21.d.mts +9 -4
  87. package/dist/nips/nip21.mjs +15 -5
  88. package/dist/nips/nip21.mjs.map +1 -1
  89. package/dist/nips/nip27.d.mts +22 -22
  90. package/dist/nips/nip27.mjs +2 -2
  91. package/dist/nips/nip27.mjs.map +1 -1
  92. package/dist/nips/nip42.d.mts +1 -1
  93. package/dist/nips/nip42.mjs +2 -1
  94. package/dist/nips/nip42.mjs.map +1 -1
  95. package/dist/nips/nip44.mjs +2 -2
  96. package/dist/nips/nip44.mjs.map +1 -1
  97. package/dist/nips/nip46.d.mts +2 -2
  98. package/dist/nips/nip46.mjs +15 -19
  99. package/dist/nips/nip46.mjs.map +1 -1
  100. package/dist/nips/nip49.d.mts +9 -9
  101. package/dist/nips/nip49.mjs +4 -4
  102. package/dist/nips/nip49.mjs.map +1 -1
  103. package/dist/nips/nip51.d.mts +2 -86
  104. package/dist/nips/nip51.mjs +7 -23
  105. package/dist/nips/nip51.mjs.map +1 -1
  106. package/dist/nips/nip57.d.mts +25 -23
  107. package/dist/nips/nip57.mjs +12 -17
  108. package/dist/nips/nip57.mjs.map +1 -1
  109. package/dist/nips/nip59.d.mts +1 -1
  110. package/dist/nips/nip59.mjs +27 -19
  111. package/dist/nips/nip59.mjs.map +1 -1
  112. package/dist/nips/nip65.d.mts +2 -2
  113. package/dist/nips/nip65.mjs +14 -37
  114. package/dist/nips/nip65.mjs.map +1 -1
  115. package/dist/nips/nip77.d.mts +1 -1
  116. package/dist/nips/nip77.mjs +33 -32
  117. package/dist/nips/nip77.mjs.map +1 -1
  118. package/dist/nips/nip98.d.mts +5 -5
  119. package/dist/nips/nip98.mjs +8 -8
  120. package/dist/nips/nip98.mjs.map +1 -1
  121. package/dist/{pool-DBIYMtwy.d.mts → pool-CqQvUnsq.d.mts} +132 -108
  122. package/dist/pool-vlw74C4t.mjs +606 -0
  123. package/dist/pool-vlw74C4t.mjs.map +1 -0
  124. package/dist/put-w13ci3sC.mjs +187 -0
  125. package/dist/put-w13ci3sC.mjs.map +1 -0
  126. package/dist/{reactive-BfhPp9a3.mjs → reactive-Tz0otDIP.mjs} +125 -88
  127. package/dist/reactive-Tz0otDIP.mjs.map +1 -0
  128. package/dist/{reactive-7aqeip4A.d.mts → reactive-gGqUrzW8.d.mts} +20 -26
  129. package/dist/relay-quCs7MHD.mjs +1811 -0
  130. package/dist/relay-quCs7MHD.mjs.map +1 -0
  131. package/dist/relay.d.mts +4 -4
  132. package/dist/relay.mjs +3 -2
  133. package/dist/serial-Bzpw-xz3.mjs +21 -0
  134. package/dist/serial-Bzpw-xz3.mjs.map +1 -0
  135. package/dist/{signer-CYQutDzD.mjs → signer-poTGe6Er.mjs} +12 -14
  136. package/dist/signer-poTGe6Er.mjs.map +1 -0
  137. package/dist/signer.d.mts +2 -2
  138. package/dist/signer.mjs +2 -2
  139. package/dist/{storage-Dky3wuoH.mjs → storage-B5IXTVuS.mjs} +28 -51
  140. package/dist/storage-B5IXTVuS.mjs.map +1 -0
  141. package/dist/storage.d.mts +3 -3
  142. package/dist/storage.mjs +4 -4
  143. package/dist/store.d.mts +2 -2
  144. package/dist/store.mjs +1 -1
  145. package/dist/testing.d.mts +19 -19
  146. package/dist/testing.mjs +9 -9
  147. package/dist/testing.mjs.map +1 -1
  148. package/dist/{types-2pCUprRU.d.mts → types-BacwnDtU.d.mts} +10 -10
  149. package/dist/{types-CRQEk0Oi.d.mts → types-DNmiscfd.d.mts} +2 -2
  150. package/dist/{util-LfQirgDk.mjs → util-DMF9AIO0.mjs} +33 -7
  151. package/dist/util-DMF9AIO0.mjs.map +1 -0
  152. package/dist/{verifier-t5WoWbtY.mjs → verifier-B51Wkt5H.mjs} +3 -3
  153. package/dist/verifier-B51Wkt5H.mjs.map +1 -0
  154. package/dist/verifier-CgTFpgsC.d.mts +22 -0
  155. package/dist/wasm.browser.d.mts +3 -3
  156. package/dist/wasm.browser.mjs +3 -3
  157. package/dist/wasm.d.mts +3 -3
  158. package/dist/wasm.mjs +3 -3
  159. package/dist/wasm.mjs.map +1 -1
  160. package/package.json +5 -9
  161. package/dist/abort-Ba8tDrOI.mjs.map +0 -1
  162. package/dist/builder-9tjI6WQ3.mjs.map +0 -1
  163. package/dist/client-CadO5fAX.mjs.map +0 -1
  164. package/dist/event-D8Nfoh4K.mjs.map +0 -1
  165. package/dist/gossip-oloDMWEj.mjs.map +0 -1
  166. package/dist/instance-DQE8BoWt.mjs.map +0 -1
  167. package/dist/key-Bps-LqX7.mjs.map +0 -1
  168. package/dist/loaders-o6YXB_Ri.mjs.map +0 -1
  169. package/dist/memory-C2EVx0_O.mjs.map +0 -1
  170. package/dist/memory-index-Do1mz0WF.mjs.map +0 -1
  171. package/dist/message-DcY6Fi9o.mjs +0 -193
  172. package/dist/message-DcY6Fi9o.mjs.map +0 -1
  173. package/dist/nip65-CjgmdpPB.d.mts +0 -26
  174. package/dist/nips/nip96.d.mts +0 -40
  175. package/dist/nips/nip96.mjs +0 -118
  176. package/dist/nips/nip96.mjs.map +0 -1
  177. package/dist/pool-As20OGxB.mjs +0 -2166
  178. package/dist/pool-As20OGxB.mjs.map +0 -1
  179. package/dist/reactive-BfhPp9a3.mjs.map +0 -1
  180. package/dist/signer-CYQutDzD.mjs.map +0 -1
  181. package/dist/storage-Dky3wuoH.mjs.map +0 -1
  182. package/dist/util-LfQirgDk.mjs.map +0 -1
  183. package/dist/verifier-t5WoWbtY.mjs.map +0 -1
@@ -1,12 +1,15 @@
1
- import { l as normalizeURL } from "./util-LfQirgDk.mjs";
2
- import { _ as formatEventAddress, b as parseEventAddress, g as eventAddress, o as itemCompare } from "./event-D8Nfoh4K.mjs";
1
+ import { u as normalizeURL } from "./util-DMF9AIO0.mjs";
2
+ import { t as Kind } from "./kind-Cxwhhkx8.mjs";
3
+ import { g as eventAddress, o as itemCompare, v as formatEventAddress, x as parseEventAddress } from "./event-CHf47dPM.mjs";
3
4
  import { o as matchFilters, r as filterFingerprint } from "./filter-BQl3GlHt.mjs";
4
5
  import { t as invokeSafely } from "./report-CHld4uWj.mjs";
5
- import { t as MemoryIndex } from "./memory-index-Do1mz0WF.mjs";
6
+ import { t as Emitter } from "./emitter-DkCY6NkU.mjs";
7
+ import { i as trimOldest, n as removeFromSetMap, t as addToSetMap } from "./collections-DtwMc1NU.mjs";
8
+ import { t as MemoryIndex } from "./memory-index-Cbc83vOZ.mjs";
6
9
  //#region src/store/reactive.ts
7
10
  const SEEN_ON_PER_ID = 16;
8
11
  var WatchImpl = class {
9
- store;
12
+ host;
10
13
  kind;
11
14
  key;
12
15
  filters;
@@ -18,8 +21,8 @@ var WatchImpl = class {
18
21
  #version = -1;
19
22
  #dirty = false;
20
23
  #pendingRemove = false;
21
- constructor(store, kind, key, compute, equal, snapshotIds, filters) {
22
- this.store = store;
24
+ constructor(host, kind, key, compute, equal, snapshotIds, filters) {
25
+ this.host = host;
23
26
  this.kind = kind;
24
27
  this.key = key;
25
28
  this.filters = filters;
@@ -27,7 +30,7 @@ var WatchImpl = class {
27
30
  this.equal = equal;
28
31
  this.snapshotIds = snapshotIds;
29
32
  this.#snapshot = compute();
30
- this.#version = store._version;
33
+ this.#version = host.version();
31
34
  }
32
35
  get subscribed() {
33
36
  return this.#subscribers.size > 0;
@@ -39,30 +42,33 @@ var WatchImpl = class {
39
42
  const wasUnsubscribed = this.#subscribers.size === 0;
40
43
  this.#subscribers.add(onChange);
41
44
  this.#pendingRemove = false;
42
- if (wasUnsubscribed && this.#version !== this.store._version) this.#dirty = true;
43
- this.store._register(this);
45
+ if (wasUnsubscribed && this.#version !== this.host.version()) this.#dirty = true;
46
+ this.host.register(this);
44
47
  return () => {
45
48
  this.#subscribers.delete(onChange);
46
49
  if (this.#subscribers.size > 0 || this.#pendingRemove) return;
47
50
  this.#pendingRemove = true;
48
51
  queueMicrotask(() => {
49
- if (this.#pendingRemove) this.store._unregister(this);
52
+ if (this.#pendingRemove) this.host.unregister(this);
50
53
  });
51
54
  };
52
55
  }
53
56
  getSnapshot() {
54
- if (this.subscribed ? this.#dirty : this.#version !== this.store._version) {
57
+ if (this.subscribed ? this.#dirty : this.#version !== this.host.version()) {
55
58
  const next = this.compute();
56
- if (!this.equal(next, this.#snapshot)) this.#snapshot = next;
57
- this.#version = this.store._version;
59
+ if (!this.equal(next, this.#snapshot)) {
60
+ this.#snapshot = next;
61
+ this.host.repin(this);
62
+ }
63
+ this.#version = this.host.version();
58
64
  this.#dirty = false;
59
65
  }
60
66
  return this.#snapshot;
61
67
  }
62
- _markDirty() {
68
+ markDirty() {
63
69
  this.#dirty = true;
64
70
  }
65
- _notify() {
71
+ notify() {
66
72
  for (const onChange of this.#subscribers) invokeSafely(onChange);
67
73
  }
68
74
  };
@@ -86,7 +92,7 @@ var ReactiveEventStore = class {
86
92
  #index;
87
93
  #maxEvents;
88
94
  #maxSeenOnEntries;
89
- _version = 0;
95
+ #version = 0;
90
96
  #recency = /* @__PURE__ */ new Set();
91
97
  #seenOn = /* @__PURE__ */ new Map();
92
98
  #idWatches = /* @__PURE__ */ new Map();
@@ -94,11 +100,41 @@ var ReactiveEventStore = class {
94
100
  #eventCache = /* @__PURE__ */ new Map();
95
101
  #addressCache = /* @__PURE__ */ new Map();
96
102
  #queryCache = /* @__PURE__ */ new Map();
97
- #queryRegistry = /* @__PURE__ */ new Set();
103
+ #queryByKind = /* @__PURE__ */ new Map();
104
+ #queryAnyKind = /* @__PURE__ */ new Set();
105
+ #pinned = /* @__PURE__ */ new Map();
106
+ #watchPins = /* @__PURE__ */ new Map();
98
107
  #dirty = /* @__PURE__ */ new Set();
99
108
  #flushScheduled = false;
100
- #insertListeners = /* @__PURE__ */ new Set();
101
- #removeListeners = /* @__PURE__ */ new Set();
109
+ #events = new Emitter();
110
+ #watchHost = {
111
+ version: () => this.#version,
112
+ register: (watch) => {
113
+ this.#registerWatch(watch);
114
+ if (!this.#watchPins.has(watch)) {
115
+ const ids = watch.pinnedIds;
116
+ this.#watchPins.set(watch, ids);
117
+ this.#pin(ids);
118
+ }
119
+ },
120
+ unregister: (watch) => {
121
+ if (watch.subscribed) return;
122
+ this.#unregisterWatch(watch);
123
+ const ids = this.#watchPins.get(watch);
124
+ if (ids !== void 0) {
125
+ this.#watchPins.delete(watch);
126
+ this.#unpin(ids);
127
+ }
128
+ },
129
+ repin: (watch) => {
130
+ const old = this.#watchPins.get(watch);
131
+ if (old === void 0) return;
132
+ const next = watch.pinnedIds;
133
+ this.#unpin(old);
134
+ this.#pin(next);
135
+ this.#watchPins.set(watch, next);
136
+ }
137
+ };
102
138
  constructor(opts) {
103
139
  this.#maxEvents = opts?.maxEvents ?? 5e4;
104
140
  this.#maxSeenOnEntries = opts?.maxSeenOnEntries ?? 2e4;
@@ -141,7 +177,7 @@ var ReactiveEventStore = class {
141
177
  this.#index.clear();
142
178
  this.#seenOn.clear();
143
179
  this.#recency.clear();
144
- this._version += 1;
180
+ this.#version += 1;
145
181
  this.#invalidateAll();
146
182
  }
147
183
  get(id) {
@@ -175,7 +211,7 @@ var ReactiveEventStore = class {
175
211
  const key = id.toLowerCase();
176
212
  let watch = this.#eventCache.get(key);
177
213
  if (!watch) {
178
- watch = new WatchImpl(this, "event", key, () => this.get(key), sameRef, EVENT_IDS);
214
+ watch = new WatchImpl(this.#watchHost, "event", key, () => this.get(key), sameRef, EVENT_IDS);
179
215
  this.#eventCache.set(key, watch);
180
216
  }
181
217
  return watch;
@@ -184,7 +220,7 @@ var ReactiveEventStore = class {
184
220
  const address = formatEventAddress(kind, pubkey, d ?? "");
185
221
  let watch = this.#addressCache.get(address);
186
222
  if (!watch) {
187
- watch = new WatchImpl(this, "replaceable", address, () => this.getByAddress(address), sameRef, EVENT_IDS);
223
+ watch = new WatchImpl(this.#watchHost, "replaceable", address, () => this.getByAddress(address), sameRef, EVENT_IDS);
188
224
  this.#addressCache.set(address, watch);
189
225
  }
190
226
  return watch;
@@ -194,55 +230,70 @@ var ReactiveEventStore = class {
194
230
  let watch = this.#queryCache.get(key);
195
231
  if (!watch) {
196
232
  const list = [...filters];
197
- watch = new WatchImpl(this, "query", key, () => this.query(list), sameList, LIST_IDS, list);
233
+ watch = new WatchImpl(this.#watchHost, "query", key, () => this.query(list), sameList, LIST_IDS, list);
198
234
  this.#queryCache.set(key, watch);
199
235
  }
200
236
  return watch;
201
237
  }
202
- /** Synchronous listener for every physical index insert (after update). */
203
- onInsert(listener) {
204
- this.#insertListeners.add(listener);
205
- return () => {
206
- this.#insertListeners.delete(listener);
207
- };
238
+ /**
239
+ * Listen for a store event. Multiple listeners per type are allowed and fire in registration
240
+ * order; a throwing listener is reported and does not break the dispatch. Returns an unsubscribe
241
+ * function.
242
+ */
243
+ on(type, listener) {
244
+ return this.#events.on(type, listener);
208
245
  }
209
- /** Synchronous listener for every physical index remove (after watch invalidation). */
210
- onRemove(listener) {
211
- this.#removeListeners.add(listener);
212
- return () => {
213
- this.#removeListeners.delete(listener);
214
- };
215
- }
216
- _register(watch) {
246
+ #registerWatch(watch) {
217
247
  switch (watch.kind) {
218
248
  case "event":
219
- addWatch(this.#idWatches, watch.key, watch);
249
+ addToSetMap(this.#idWatches, watch.key, watch);
220
250
  return;
221
251
  case "replaceable":
222
- addWatch(this.#addressWatches, watch.key, watch);
252
+ addToSetMap(this.#addressWatches, watch.key, watch);
223
253
  return;
224
- case "query": this.#queryRegistry.add(watch);
254
+ case "query": {
255
+ const kinds = this.#queryKinds(watch);
256
+ if (kinds === void 0) {
257
+ this.#queryAnyKind.add(watch);
258
+ return;
259
+ }
260
+ for (const kind of kinds) addToSetMap(this.#queryByKind, kind, watch);
261
+ }
225
262
  }
226
263
  }
227
- _unregister(watch) {
228
- if (watch.subscribed) return;
264
+ #unregisterWatch(watch) {
229
265
  switch (watch.kind) {
230
266
  case "event":
231
- removeWatch(this.#idWatches, watch.key, watch);
267
+ removeFromSetMap(this.#idWatches, watch.key, watch);
232
268
  if (this.#eventCache.get(watch.key) === watch) this.#eventCache.delete(watch.key);
233
269
  return;
234
270
  case "replaceable":
235
- removeWatch(this.#addressWatches, watch.key, watch);
271
+ removeFromSetMap(this.#addressWatches, watch.key, watch);
236
272
  if (this.#addressCache.get(watch.key) === watch) this.#addressCache.delete(watch.key);
237
273
  return;
238
- case "query":
239
- this.#queryRegistry.delete(watch);
274
+ case "query": {
275
+ const kinds = this.#queryKinds(watch);
276
+ if (kinds === void 0) this.#queryAnyKind.delete(watch);
277
+ else for (const kind of kinds) removeFromSetMap(this.#queryByKind, kind, watch);
240
278
  if (this.#queryCache.get(watch.key) === watch) this.#queryCache.delete(watch.key);
279
+ }
241
280
  }
242
281
  }
282
+ /**
283
+ * Kind buckets a query watch belongs to (deduped across filters), or undefined when any filter
284
+ * lacks a non-empty `kinds` and so can match every kind.
285
+ */
286
+ #queryKinds(watch) {
287
+ const kinds = /* @__PURE__ */ new Set();
288
+ for (const filter of watch.filters ?? []) {
289
+ if (filter.kinds === void 0 || filter.kinds.length === 0) return;
290
+ for (const kind of filter.kinds) kinds.add(kind);
291
+ }
292
+ return kinds;
293
+ }
243
294
  #touch(id) {
244
- if (this.#recency.delete(id)) this.#recency.add(id);
245
- else this.#recency.add(id);
295
+ this.#recency.delete(id);
296
+ this.#recency.add(id);
246
297
  }
247
298
  #recordSeen(id, relayUrl) {
248
299
  const url = normalizeURL(relayUrl);
@@ -252,23 +303,19 @@ var ReactiveEventStore = class {
252
303
  this.#seenOn.set(id, urls);
253
304
  }
254
305
  if (!urls.includes(url) && urls.length < SEEN_ON_PER_ID) urls.push(url);
255
- while (this.#seenOn.size > this.#maxSeenOnEntries) {
256
- const oldest = this.#seenOn.keys().next().value;
257
- if (oldest === void 0) break;
258
- this.#seenOn.delete(oldest);
259
- }
306
+ trimOldest(this.#seenOn, this.#maxSeenOnEntries);
260
307
  }
261
308
  #onInsert(event) {
262
- this._version += 1;
309
+ this.#version += 1;
263
310
  this.#touch(event.id);
264
311
  this.#invalidateByEvent(event);
265
- for (const listener of this.#insertListeners) invokeSafely(() => listener(event));
312
+ this.#events.emit("insert", event);
266
313
  }
267
314
  #onRemove(event) {
268
- this._version += 1;
315
+ this.#version += 1;
269
316
  this.#recency.delete(event.id);
270
317
  this.#invalidateByEvent(event);
271
- for (const listener of this.#removeListeners) invokeSafely(() => listener(event));
318
+ this.#events.emit("remove", event);
272
319
  }
273
320
  #invalidateByEvent(event) {
274
321
  const byId = this.#idWatches.get(event.id);
@@ -278,7 +325,7 @@ var ReactiveEventStore = class {
278
325
  const byAddress = this.#addressWatches.get(address);
279
326
  if (byAddress) for (const watch of byAddress) this.#markDirty(watch);
280
327
  }
281
- if (event.kind === 5) {
328
+ if (event.kind === Kind.EventDeletion) {
282
329
  for (const tag of event.tags) if (tag[0] === "e" && tag[1] !== void 0) {
283
330
  const watches = this.#idWatches.get(tag[1].toLowerCase());
284
331
  if (watches) for (const watch of watches) this.#markDirty(watch);
@@ -289,16 +336,21 @@ var ReactiveEventStore = class {
289
336
  if (watches) for (const watch of watches) this.#markDirty(watch);
290
337
  }
291
338
  }
292
- for (const watch of this.#queryRegistry) if (watch.filters !== void 0 && matchFilters(watch.filters, event)) this.#markDirty(watch);
339
+ const byKind = this.#queryByKind.get(event.kind);
340
+ if (byKind) {
341
+ for (const watch of byKind) if (watch.filters !== void 0 && matchFilters(watch.filters, event)) this.#markDirty(watch);
342
+ }
343
+ for (const watch of this.#queryAnyKind) if (watch.filters !== void 0 && matchFilters(watch.filters, event)) this.#markDirty(watch);
293
344
  }
294
345
  #invalidateAll() {
295
346
  for (const watches of this.#idWatches.values()) for (const watch of watches) this.#markDirty(watch);
296
347
  for (const watches of this.#addressWatches.values()) for (const watch of watches) this.#markDirty(watch);
297
- for (const watch of this.#queryRegistry) this.#markDirty(watch);
348
+ for (const watches of this.#queryByKind.values()) for (const watch of watches) this.#markDirty(watch);
349
+ for (const watch of this.#queryAnyKind) this.#markDirty(watch);
298
350
  this.#scheduleFlush();
299
351
  }
300
352
  #markDirty(watch) {
301
- watch._markDirty();
353
+ watch.markDirty();
302
354
  this.#dirty.add(watch);
303
355
  this.#scheduleFlush();
304
356
  }
@@ -309,28 +361,27 @@ var ReactiveEventStore = class {
309
361
  while (this.#dirty.size > 0) {
310
362
  const batch = [...this.#dirty];
311
363
  this.#dirty.clear();
312
- for (const watch of batch) watch._notify();
364
+ for (const watch of batch) watch.notify();
313
365
  }
314
366
  this.#flushScheduled = false;
315
367
  });
316
368
  }
317
- #pinnedIds() {
318
- const pinned = /* @__PURE__ */ new Set();
319
- const collect = (watch) => {
320
- for (const id of watch.pinnedIds) pinned.add(id);
321
- };
322
- for (const watches of this.#idWatches.values()) for (const watch of watches) collect(watch);
323
- for (const watches of this.#addressWatches.values()) for (const watch of watches) collect(watch);
324
- for (const watch of this.#queryRegistry) collect(watch);
325
- return pinned;
369
+ #pin(ids) {
370
+ for (const id of ids) this.#pinned.set(id, (this.#pinned.get(id) ?? 0) + 1);
371
+ }
372
+ #unpin(ids) {
373
+ for (const id of ids) {
374
+ const count = (this.#pinned.get(id) ?? 0) - 1;
375
+ if (count <= 0) this.#pinned.delete(id);
376
+ else this.#pinned.set(id, count);
377
+ }
326
378
  }
327
379
  #evictIfNeeded(protectedId) {
328
380
  if (this.#index.size <= this.#maxEvents) return;
329
- const pinned = this.#pinnedIds();
330
381
  const evicting = [];
331
382
  for (const id of this.#recency) {
332
383
  if (this.#index.size - evicting.length <= this.#maxEvents) break;
333
- if (pinned.has(id) || id === protectedId) continue;
384
+ if (this.#pinned.has(id) || id === protectedId) continue;
334
385
  if (this.#index.get(id) === void 0) continue;
335
386
  evicting.push(id);
336
387
  }
@@ -338,21 +389,7 @@ var ReactiveEventStore = class {
338
389
  for (const id of evicting) this.#recency.delete(id);
339
390
  }
340
391
  };
341
- function addWatch(map, key, watch) {
342
- let set = map.get(key);
343
- if (!set) {
344
- set = /* @__PURE__ */ new Set();
345
- map.set(key, set);
346
- }
347
- set.add(watch);
348
- }
349
- function removeWatch(map, key, watch) {
350
- const set = map.get(key);
351
- if (!set) return;
352
- set.delete(watch);
353
- if (set.size === 0) map.delete(key);
354
- }
355
392
  //#endregion
356
393
  export { ReactiveEventStore as t };
357
394
 
358
- //# sourceMappingURL=reactive-BfhPp9a3.mjs.map
395
+ //# sourceMappingURL=reactive-Tz0otDIP.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reactive-Tz0otDIP.mjs","names":[],"sources":["../src/store/reactive.ts"],"sourcesContent":["import { addToSetMap, removeFromSetMap, trimOldest } from \"../core/collections.ts\";\nimport { Emitter } from \"../core/emitter.ts\";\nimport { itemCompare } from \"../core/event.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { filterFingerprint, matchFilters } from \"../core/filter.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { invokeSafely } from \"../core/report.ts\";\nimport { formatEventAddress, eventAddress, parseEventAddress } from \"../core/tag.ts\";\nimport { normalizeURL } from \"../core/util.ts\";\nimport { MemoryIndex } from \"../storage/memory-index.ts\";\nimport type { PutResult } from \"../storage/types.ts\";\n\n/** A reactive snapshot source compatible with `useSyncExternalStore`. */\nexport type Watch<T> = {\n subscribe: (onChange: () => void) => () => void;\n getSnapshot: () => T;\n};\n\n/** Capacity options for {@link ReactiveEventStore}. */\nexport type ReactiveEventStoreOptions = {\n /** Max stored events before LRU eviction. Default 50_000. */\n maxEvents?: number | undefined;\n /** Max ids tracked by `seenOn`. Default 20_000. */\n maxSeenOnEntries?: number | undefined;\n /** FIFO cap on tombstoned deletion ids. Default 100_000. */\n maxTombstones?: number | undefined;\n};\n\n/** Typed event payloads for {@link ReactiveEventStore.on}. */\nexport type ReactiveEventStoreEventMap = {\n /** Every physical index insert, fired synchronously after the update. */\n insert: Event;\n /** Every physical index remove, fired synchronously after watch invalidation. */\n remove: Event;\n};\n\nconst SEEN_ON_PER_ID = 16;\n\n/** Category of a {@link Watch} subscription. */\ntype WatchKind = \"event\" | \"replaceable\" | \"query\";\n\n/** Structural handle the store needs for invalidation; implemented by WatchImpl. */\ntype WatchHandle = {\n readonly kind: WatchKind;\n readonly key: string;\n readonly filters: ReadonlyArray<Filter> | undefined;\n readonly subscribed: boolean;\n readonly pinnedIds: ReadonlyArray<string>;\n markDirty: () => void;\n notify: () => void;\n};\n\n/** Store internals exposed to a {@link WatchImpl}: version clock and registry/pin bookkeeping. */\ntype WatchHost = {\n version: () => number;\n register: (watch: WatchHandle) => void;\n unregister: (watch: WatchHandle) => void;\n /** Refresh pinned ids after a registered watch's snapshot changed. No-op when unregistered. */\n repin: (watch: WatchHandle) => void;\n};\n\nclass WatchImpl<T> implements Watch<T>, WatchHandle {\n readonly host: WatchHost;\n readonly kind: WatchKind;\n readonly key: string;\n readonly filters: ReadonlyArray<Filter> | undefined;\n readonly compute: () => T;\n readonly equal: (a: T, b: T) => boolean;\n readonly snapshotIds: (snapshot: T) => ReadonlyArray<string>;\n readonly #subscribers = new Set<() => void>();\n #snapshot: T;\n #version = -1;\n #dirty = false;\n #pendingRemove = false;\n\n constructor(\n host: WatchHost,\n kind: WatchKind,\n key: string,\n compute: () => T,\n equal: (a: T, b: T) => boolean,\n snapshotIds: (snapshot: T) => ReadonlyArray<string>,\n filters?: ReadonlyArray<Filter>,\n ) {\n this.host = host;\n this.kind = kind;\n this.key = key;\n this.filters = filters;\n this.compute = compute;\n this.equal = equal;\n this.snapshotIds = snapshotIds;\n this.#snapshot = compute();\n this.#version = host.version();\n }\n\n get subscribed(): boolean {\n return this.#subscribers.size > 0;\n }\n\n get pinnedIds(): ReadonlyArray<string> {\n return this.snapshotIds(this.#snapshot);\n }\n\n subscribe(onChange: () => void): () => void {\n const wasUnsubscribed = this.#subscribers.size === 0;\n this.#subscribers.add(onChange);\n this.#pendingRemove = false;\n // Writes that landed while unregistered must not be lost.\n if (wasUnsubscribed && this.#version !== this.host.version()) {\n this.#dirty = true;\n }\n this.host.register(this);\n return () => {\n this.#subscribers.delete(onChange);\n if (this.#subscribers.size > 0 || this.#pendingRemove) {\n return;\n }\n this.#pendingRemove = true;\n queueMicrotask(() => {\n if (this.#pendingRemove) {\n this.host.unregister(this);\n }\n });\n };\n }\n\n getSnapshot(): T {\n if (this.subscribed ? this.#dirty : this.#version !== this.host.version()) {\n const next = this.compute();\n if (!this.equal(next, this.#snapshot)) {\n this.#snapshot = next;\n this.host.repin(this);\n }\n this.#version = this.host.version();\n this.#dirty = false;\n }\n return this.#snapshot;\n }\n\n markDirty(): void {\n this.#dirty = true;\n }\n\n notify(): void {\n // Do not sync #version here: subscribers will call getSnapshot, which\n // recomputes against the bumped store version and keeps the reference\n // when the result is element-wise identical. A throwing subscriber is\n // reported and never aborts the remaining notifications.\n for (const onChange of this.#subscribers) {\n invokeSafely(onChange);\n }\n }\n}\n\nconst EVENT_IDS = (e: Event | undefined): ReadonlyArray<string> => (e ? [e.id] : []);\nconst LIST_IDS = (events: ReadonlyArray<Event>): ReadonlyArray<string> => events.map((e) => e.id);\n\nfunction sameRef(a: Event | undefined, b: Event | undefined): boolean {\n return a === b;\n}\n\nfunction sameList(a: ReadonlyArray<Event>, b: ReadonlyArray<Event>): boolean {\n if (a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Synchronous reactive in-memory event store backed by {@link MemoryIndex}. The single read source\n * for Nostr events on the UI side: `Watch` snapshots keep referential stability for\n * `useSyncExternalStore` and are invalidated on matching writes, deletions and tombstones. Writes\n * batch notifications into one microtask flush.\n */\nexport class ReactiveEventStore {\n readonly #index: MemoryIndex;\n readonly #maxEvents: number;\n readonly #maxSeenOnEntries: number;\n\n #version = 0;\n\n // id -> recency-ordered access order (delete+add moves to the newest end)\n readonly #recency = new Set<string>();\n // id -> relay urls (≤ SEEN_ON_PER_ID); Map insertion order for eviction\n readonly #seenOn = new Map<string, string[]>();\n\n readonly #idWatches = new Map<string, Set<WatchHandle>>();\n readonly #addressWatches = new Map<string, Set<WatchHandle>>();\n readonly #eventCache = new Map<string, WatchImpl<Event | undefined>>();\n readonly #addressCache = new Map<string, WatchImpl<Event | undefined>>();\n readonly #queryCache = new Map<string, WatchImpl<ReadonlyArray<Event>>>();\n // Query watches indexed by filter kinds; a filter without kinds matches any kind.\n readonly #queryByKind = new Map<number, Set<WatchHandle>>();\n readonly #queryAnyKind = new Set<WatchHandle>();\n\n // Event-id → pin refcount across registered watch snapshots; eviction skips pinned ids.\n readonly #pinned = new Map<string, number>();\n // Registered watch → ids it currently pins (repin/unpin need the old set).\n readonly #watchPins = new Map<WatchHandle, ReadonlyArray<string>>();\n\n readonly #dirty = new Set<WatchHandle>();\n #flushScheduled = false;\n readonly #events = new Emitter<ReactiveEventStoreEventMap>();\n\n readonly #watchHost: WatchHost = {\n version: () => this.#version,\n register: (watch) => {\n this.#registerWatch(watch);\n if (!this.#watchPins.has(watch)) {\n const ids = watch.pinnedIds;\n this.#watchPins.set(watch, ids);\n this.#pin(ids);\n }\n },\n unregister: (watch) => {\n if (watch.subscribed) {\n return;\n }\n this.#unregisterWatch(watch);\n const ids = this.#watchPins.get(watch);\n if (ids !== undefined) {\n this.#watchPins.delete(watch);\n this.#unpin(ids);\n }\n },\n repin: (watch) => {\n const old = this.#watchPins.get(watch);\n if (old === undefined) {\n return;\n }\n const next = watch.pinnedIds;\n this.#unpin(old);\n this.#pin(next);\n this.#watchPins.set(watch, next);\n },\n };\n\n constructor(opts?: ReactiveEventStoreOptions) {\n this.#maxEvents = opts?.maxEvents ?? 50_000;\n this.#maxSeenOnEntries = opts?.maxSeenOnEntries ?? 20_000;\n this.#index = new MemoryIndex({\n maxTombstones: opts?.maxTombstones ?? 100_000,\n // Keep evicted replaceable winners rejectable while their body is gone.\n maxWatermarks: this.#maxEvents,\n onInsert: (event) => this.#onInsert(event),\n onRemove: (event) => this.#onRemove(event),\n });\n }\n\n add(event: Event, relayUrl?: string): PutResult {\n const result = this.#index.put(event);\n // Stored events are canonical; seenOn keys are their lowercase ids.\n const { id } = event;\n if (\n relayUrl !== undefined &&\n result !== \"ephemeral\" &&\n result !== \"rejected\" &&\n result !== \"invalid\"\n ) {\n this.#recordSeen(id, relayUrl);\n }\n // The just-inserted event must survive its own eviction pass even when\n // every older entry is pinned by a subscribed watch snapshot.\n this.#evictIfNeeded(id);\n return result;\n }\n\n addMany(events: ReadonlyArray<Event>, relayUrl?: string): PutResult[] {\n const results: PutResult[] = [];\n for (const event of events) {\n results.push(this.add(event, relayUrl));\n }\n return results;\n }\n\n /** Record a relay sighting for an id already in the index. No notification. */\n markSeen(id: string, relayUrl: string): boolean {\n const key = id.toLowerCase();\n if (this.#index.get(key) === undefined) {\n return false;\n }\n this.#recordSeen(key, relayUrl);\n return true;\n }\n\n /** Bulk load (initial hydration): no seenOn, one batched notification. */\n hydrate(events: ReadonlyArray<Event>): void {\n // Insert oldest-first so recency ends with the newest entries hottest.\n const sorted = [...events].sort(itemCompare);\n for (const event of sorted) {\n this.#index.put(event);\n }\n this.#evictIfNeeded();\n }\n\n remove(ids: ReadonlyArray<string>): number {\n return this.#index.remove(ids);\n }\n\n clear(): void {\n this.#index.clear();\n this.#seenOn.clear();\n this.#recency.clear();\n this.#version += 1;\n this.#invalidateAll();\n }\n\n get(id: string): Event | undefined {\n const event = this.#index.get(id);\n if (event) {\n this.#touch(event.id);\n }\n return event;\n }\n\n getReplaceable(kind: number, pubkey: string, d?: string): Event | undefined {\n return this.getByAddress(formatEventAddress(kind, pubkey, d ?? \"\"));\n }\n\n getByAddress(address: string): Event | undefined {\n const event = this.#index.getByAddress(address);\n if (event) {\n this.#touch(event.id);\n }\n return event;\n }\n\n query(filters: ReadonlyArray<Filter>): ReadonlyArray<Event> {\n const events = this.#index.query(filters);\n // Results come back newest-first; touch oldest→newest so the newest\n // entries end up hottest in the recency order.\n for (const event of [...events].reverse()) {\n this.#touch(event.id);\n }\n return events;\n }\n\n isDeleted(idOrAddress: string): boolean {\n return this.#index.isDeleted(idOrAddress);\n }\n\n seenOn(id: string): ReadonlyArray<string> {\n return this.#seenOn.get(id.toLowerCase()) ?? [];\n }\n\n get size(): number {\n return this.#index.size;\n }\n\n watchEvent(id: string): Watch<Event | undefined> {\n const key = id.toLowerCase();\n let watch = this.#eventCache.get(key);\n if (!watch) {\n watch = new WatchImpl(this.#watchHost, \"event\", key, () => this.get(key), sameRef, EVENT_IDS);\n this.#eventCache.set(key, watch);\n }\n return watch;\n }\n\n watchReplaceable(kind: number, pubkey: string, d?: string): Watch<Event | undefined> {\n const address = formatEventAddress(kind, pubkey, d ?? \"\");\n let watch = this.#addressCache.get(address);\n if (!watch) {\n watch = new WatchImpl(\n this.#watchHost,\n \"replaceable\",\n address,\n () => this.getByAddress(address),\n sameRef,\n EVENT_IDS,\n );\n this.#addressCache.set(address, watch);\n }\n return watch;\n }\n\n watchQuery(filters: ReadonlyArray<Filter>): Watch<ReadonlyArray<Event>> {\n const key = filterFingerprint(filters);\n let watch = this.#queryCache.get(key);\n if (!watch) {\n const list = [...filters];\n watch = new WatchImpl(\n this.#watchHost,\n \"query\",\n key,\n () => this.query(list),\n sameList,\n LIST_IDS,\n list,\n );\n this.#queryCache.set(key, watch);\n }\n return watch;\n }\n\n /**\n * Listen for a store event. Multiple listeners per type are allowed and fire in registration\n * order; a throwing listener is reported and does not break the dispatch. Returns an unsubscribe\n * function.\n */\n on<K extends keyof ReactiveEventStoreEventMap>(\n type: K,\n listener: (payload: ReactiveEventStoreEventMap[K]) => void,\n ): () => void {\n return this.#events.on(type, listener);\n }\n\n #registerWatch(watch: WatchHandle): void {\n switch (watch.kind) {\n case \"event\":\n addToSetMap(this.#idWatches, watch.key, watch);\n return;\n case \"replaceable\":\n addToSetMap(this.#addressWatches, watch.key, watch);\n return;\n case \"query\": {\n const kinds = this.#queryKinds(watch);\n if (kinds === undefined) {\n this.#queryAnyKind.add(watch);\n return;\n }\n for (const kind of kinds) {\n addToSetMap(this.#queryByKind, kind, watch);\n }\n }\n }\n }\n\n #unregisterWatch(watch: WatchHandle): void {\n switch (watch.kind) {\n case \"event\":\n removeFromSetMap(this.#idWatches, watch.key, watch);\n if (this.#eventCache.get(watch.key) === watch) {\n this.#eventCache.delete(watch.key);\n }\n return;\n case \"replaceable\":\n removeFromSetMap(this.#addressWatches, watch.key, watch);\n if (this.#addressCache.get(watch.key) === watch) {\n this.#addressCache.delete(watch.key);\n }\n return;\n case \"query\": {\n const kinds = this.#queryKinds(watch);\n if (kinds === undefined) {\n this.#queryAnyKind.delete(watch);\n } else {\n for (const kind of kinds) {\n removeFromSetMap(this.#queryByKind, kind, watch);\n }\n }\n if (this.#queryCache.get(watch.key) === watch) {\n this.#queryCache.delete(watch.key);\n }\n }\n }\n }\n\n /**\n * Kind buckets a query watch belongs to (deduped across filters), or undefined when any filter\n * lacks a non-empty `kinds` and so can match every kind.\n */\n #queryKinds(watch: WatchHandle): Set<number> | undefined {\n const kinds = new Set<number>();\n for (const filter of watch.filters ?? []) {\n if (filter.kinds === undefined || filter.kinds.length === 0) {\n return undefined;\n }\n for (const kind of filter.kinds) {\n kinds.add(kind);\n }\n }\n return kinds;\n }\n\n #touch(id: string): void {\n this.#recency.delete(id);\n this.#recency.add(id);\n }\n\n #recordSeen(id: string, relayUrl: string): void {\n const url = normalizeURL(relayUrl);\n let urls = this.#seenOn.get(id);\n if (!urls) {\n urls = [];\n this.#seenOn.set(id, urls);\n }\n if (!urls.includes(url) && urls.length < SEEN_ON_PER_ID) {\n urls.push(url);\n }\n trimOldest(this.#seenOn, this.#maxSeenOnEntries);\n }\n\n #onInsert(event: Event): void {\n this.#version += 1;\n this.#touch(event.id);\n this.#invalidateByEvent(event);\n this.#events.emit(\"insert\", event);\n }\n\n #onRemove(event: Event): void {\n this.#version += 1;\n this.#recency.delete(event.id);\n this.#invalidateByEvent(event);\n this.#events.emit(\"remove\", event);\n }\n\n #invalidateByEvent(event: Event): void {\n const byId = this.#idWatches.get(event.id);\n if (byId) {\n for (const watch of byId) {\n this.#markDirty(watch);\n }\n }\n const address = eventAddress(event);\n if (address !== undefined) {\n const byAddress = this.#addressWatches.get(address);\n if (byAddress) {\n for (const watch of byAddress) {\n this.#markDirty(watch);\n }\n }\n }\n // A kind-5 deletion marks its targets tombstoned even when they are not in\n // the index, so nothing else invalidates a watch keyed on the target.\n if (event.kind === Kind.EventDeletion) {\n for (const tag of event.tags) {\n if (tag[0] === \"e\" && tag[1] !== undefined) {\n const watches = this.#idWatches.get(tag[1].toLowerCase());\n if (watches) {\n for (const watch of watches) {\n this.#markDirty(watch);\n }\n }\n } else if (tag[0] === \"a\" && tag[1] !== undefined) {\n const coord = parseEventAddress(tag[1]);\n if (coord === undefined) {\n continue;\n }\n const watches = this.#addressWatches.get(\n formatEventAddress(coord.kind, coord.pubkey, coord.identifier),\n );\n if (watches) {\n for (const watch of watches) {\n this.#markDirty(watch);\n }\n }\n }\n }\n }\n // Candidates are the event's kind bucket plus any-kind watches — a watch sits\n // in at most one bucket per kind, so it is checked at most once per event.\n const byKind = this.#queryByKind.get(event.kind);\n if (byKind) {\n for (const watch of byKind) {\n if (watch.filters !== undefined && matchFilters(watch.filters, event)) {\n this.#markDirty(watch);\n }\n }\n }\n for (const watch of this.#queryAnyKind) {\n if (watch.filters !== undefined && matchFilters(watch.filters, event)) {\n this.#markDirty(watch);\n }\n }\n }\n\n #invalidateAll(): void {\n for (const watches of this.#idWatches.values()) {\n for (const watch of watches) {\n this.#markDirty(watch);\n }\n }\n for (const watches of this.#addressWatches.values()) {\n for (const watch of watches) {\n this.#markDirty(watch);\n }\n }\n for (const watches of this.#queryByKind.values()) {\n for (const watch of watches) {\n this.#markDirty(watch);\n }\n }\n for (const watch of this.#queryAnyKind) {\n this.#markDirty(watch);\n }\n this.#scheduleFlush();\n }\n\n #markDirty(watch: WatchHandle): void {\n watch.markDirty();\n this.#dirty.add(watch);\n this.#scheduleFlush();\n }\n\n #scheduleFlush(): void {\n if (this.#flushScheduled) {\n return;\n }\n this.#flushScheduled = true;\n queueMicrotask(() => {\n // Keep the flag set while flushing: writes re-entered from a\n // subscriber land in #dirty and are notified in the same pass.\n while (this.#dirty.size > 0) {\n const batch = [...this.#dirty];\n this.#dirty.clear();\n for (const watch of batch) {\n watch.notify();\n }\n }\n this.#flushScheduled = false;\n });\n }\n\n #pin(ids: ReadonlyArray<string>): void {\n for (const id of ids) {\n this.#pinned.set(id, (this.#pinned.get(id) ?? 0) + 1);\n }\n }\n\n #unpin(ids: ReadonlyArray<string>): void {\n for (const id of ids) {\n const count = (this.#pinned.get(id) ?? 0) - 1;\n if (count <= 0) {\n this.#pinned.delete(id);\n } else {\n this.#pinned.set(id, count);\n }\n }\n }\n\n #evictIfNeeded(protectedId?: string): void {\n if (this.#index.size <= this.#maxEvents) {\n return;\n }\n const evicting: string[] = [];\n for (const id of this.#recency) {\n if (this.#index.size - evicting.length <= this.#maxEvents) {\n break;\n }\n if (this.#pinned.has(id) || id === protectedId) {\n continue;\n }\n const event = this.#index.get(id);\n if (event === undefined) {\n continue;\n }\n // Replaceable/addressable winners evict like any other event; the index\n // records a watermark so stale versions stay rejected afterwards.\n evicting.push(id);\n }\n this.#index.evict(evicting);\n for (const id of evicting) {\n this.#recency.delete(id);\n }\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { t as Event } from "./event-fU3Y01PR.mjs";
2
- import { t as Filter } from "./filter-C0IMByE3.mjs";
3
- import { i as PutResult } from "./types-2pCUprRU.mjs";
1
+ import { t as Event } from "./event-DJeb_DIL.mjs";
2
+ import { t as Filter } from "./filter-jI_9htF7.mjs";
3
+ import { i as PutResult } from "./types-BacwnDtU.mjs";
4
4
  //#region src/store/reactive.d.ts
5
5
  /** A reactive snapshot source compatible with `useSyncExternalStore`. */
6
6
  type Watch<T> = {
@@ -10,23 +10,18 @@ type Watch<T> = {
10
10
  /** Capacity options for {@link ReactiveEventStore}. */
11
11
  type ReactiveEventStoreOptions = {
12
12
  /** Max stored events before LRU eviction. Default 50_000. */
13
- maxEvents?: number;
13
+ maxEvents?: number | undefined;
14
14
  /** Max ids tracked by `seenOn`. Default 20_000. */
15
- maxSeenOnEntries?: number;
15
+ maxSeenOnEntries?: number | undefined;
16
16
  /** FIFO cap on tombstoned deletion ids. Default 100_000. */
17
- maxTombstones?: number;
17
+ maxTombstones?: number | undefined;
18
18
  };
19
- /** Category of a {@link Watch} subscription. */
20
- type WatchKind = "event" | "replaceable" | "query";
21
- /** Structural handle the store needs for invalidation; implemented by WatchImpl. */
22
- type WatchHandle = {
23
- readonly kind: WatchKind;
24
- readonly key: string;
25
- readonly filters: ReadonlyArray<Filter> | undefined;
26
- readonly subscribed: boolean;
27
- readonly pinnedIds: ReadonlyArray<string>;
28
- _markDirty: () => void;
29
- _notify: () => void;
19
+ /** Typed event payloads for {@link ReactiveEventStore.on}. */
20
+ type ReactiveEventStoreEventMap = {
21
+ /** Every physical index insert, fired synchronously after the update. */
22
+ insert: Event;
23
+ /** Every physical index remove, fired synchronously after watch invalidation. */
24
+ remove: Event;
30
25
  };
31
26
  /**
32
27
  * Synchronous reactive in-memory event store backed by {@link MemoryIndex}. The single read source
@@ -36,7 +31,6 @@ type WatchHandle = {
36
31
  */
37
32
  declare class ReactiveEventStore {
38
33
  #private;
39
- _version: number;
40
34
  constructor(opts?: ReactiveEventStoreOptions);
41
35
  add(event: Event, relayUrl?: string): PutResult;
42
36
  addMany(events: ReadonlyArray<Event>, relayUrl?: string): PutResult[];
@@ -56,13 +50,13 @@ declare class ReactiveEventStore {
56
50
  watchEvent(id: string): Watch<Event | undefined>;
57
51
  watchReplaceable(kind: number, pubkey: string, d?: string): Watch<Event | undefined>;
58
52
  watchQuery(filters: ReadonlyArray<Filter>): Watch<ReadonlyArray<Event>>;
59
- /** Synchronous listener for every physical index insert (after update). */
60
- onInsert(listener: (event: Event) => void): () => void;
61
- /** Synchronous listener for every physical index remove (after watch invalidation). */
62
- onRemove(listener: (event: Event) => void): () => void;
63
- _register(watch: WatchHandle): void;
64
- _unregister(watch: WatchHandle): void;
53
+ /**
54
+ * Listen for a store event. Multiple listeners per type are allowed and fire in registration
55
+ * order; a throwing listener is reported and does not break the dispatch. Returns an unsubscribe
56
+ * function.
57
+ */
58
+ on<K extends keyof ReactiveEventStoreEventMap>(type: K, listener: (payload: ReactiveEventStoreEventMap[K]) => void): () => void;
65
59
  }
66
60
  //#endregion
67
- export { ReactiveEventStoreOptions as n, Watch as r, ReactiveEventStore as t };
68
- //# sourceMappingURL=reactive-7aqeip4A.d.mts.map
61
+ export { Watch as i, ReactiveEventStoreEventMap as n, ReactiveEventStoreOptions as r, ReactiveEventStore as t };
62
+ //# sourceMappingURL=reactive-gGqUrzW8.d.mts.map