@qntx/nostr 0.1.0 → 0.2.1
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.
- package/README.md +16 -0
- package/dist/abort-Ba8tDrOI.mjs +38 -0
- package/dist/abort-Ba8tDrOI.mjs.map +1 -0
- package/dist/{builder-BphJa-Y-.d.mts → builder-BWn55Paf.d.mts} +20 -14
- package/dist/{builder-q0VsvnYE.mjs → builder-CyrGKRVP.mjs} +32 -16
- package/dist/builder-CyrGKRVP.mjs.map +1 -0
- package/dist/{client-Bd8B1mYm.mjs → client-5DhgYV7P.mjs} +372 -195
- package/dist/client-5DhgYV7P.mjs.map +1 -0
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs +1 -1
- package/dist/core.d.mts +8 -8
- package/dist/core.mjs +8 -9
- package/dist/error-BW7re1UN.d.mts +9 -0
- package/dist/error-B__OMBRY.mjs +14 -0
- package/dist/error-B__OMBRY.mjs.map +1 -0
- package/dist/{error-WEFS-R-4.d.mts → error-CR8Urc84.d.mts} +20 -8
- package/dist/{error-CwBxle6N.mjs → error-DrEIM7FO.mjs} +5 -3
- package/dist/error-DrEIM7FO.mjs.map +1 -0
- package/dist/{event-CzoQEFMF.mjs → event-D8Nfoh4K.mjs} +40 -32
- package/dist/event-D8Nfoh4K.mjs.map +1 -0
- package/dist/{event-B5D2jxt_.d.mts → event-fU3Y01PR.d.mts} +23 -20
- package/dist/{filter-BxwjEkZp.mjs → filter-BQl3GlHt.mjs} +42 -35
- package/dist/filter-BQl3GlHt.mjs.map +1 -0
- package/dist/filter-C0IMByE3.d.mts +37 -0
- package/dist/{gossip-C5RVB1UB.d.mts → gossip-BRQwIM_q.d.mts} +25 -23
- package/dist/{gossip-3xkRRCm_.mjs → gossip-oloDMWEj.mjs} +52 -34
- package/dist/gossip-oloDMWEj.mjs.map +1 -0
- package/dist/gossip.d.mts +1 -1
- package/dist/gossip.mjs +1 -1
- package/dist/{http-6vevbBlu.d.mts → http-C4HwIu4B.d.mts} +7 -7
- package/dist/{http-D2rhokZo.mjs → http-CuazNRIP.mjs} +10 -6
- package/dist/http-CuazNRIP.mjs.map +1 -0
- package/dist/index-B-CGNDPS.d.mts +207 -0
- package/dist/index-BIhSADwK.d.mts +339 -0
- package/dist/{index-BYMnhRIC.d.mts → index-Ba7bJ7DD.d.mts} +68 -50
- package/dist/{index-DaRyjo7O.d.mts → index-Boe8FshM.d.mts} +13 -7
- package/dist/index-D-TK_KJG.d.mts +84 -0
- package/dist/index-D1bqFHKB.d.mts +188 -0
- package/dist/index.d.mts +22 -20
- package/dist/index.mjs +19 -18
- package/dist/{key-Dn-aX4x4.mjs → key-Bbtys6o7.mjs} +22 -14
- package/dist/key-Bbtys6o7.mjs.map +1 -0
- package/dist/{key-BiGxGLhg.d.mts → key-BlsYhN1V.d.mts} +9 -6
- package/dist/{kind-BLl4cLOc.mjs → kind-Cxwhhkx8.mjs} +2 -1
- package/dist/kind-Cxwhhkx8.mjs.map +1 -0
- package/dist/{loaders-BNQszDAn.mjs → loaders-o6YXB_Ri.mjs} +329 -305
- package/dist/loaders-o6YXB_Ri.mjs.map +1 -0
- package/dist/loaders.d.mts +3 -3
- package/dist/loaders.mjs +2 -2
- package/dist/memory-C2EVx0_O.mjs +46 -0
- package/dist/memory-C2EVx0_O.mjs.map +1 -0
- package/dist/{memory-BCmhZPek.mjs → memory-index-Do1mz0WF.mjs} +212 -82
- package/dist/memory-index-Do1mz0WF.mjs.map +1 -0
- package/dist/{message-C_Uj-Y-c.mjs → message-DcY6Fi9o.mjs} +38 -24
- package/dist/message-DcY6Fi9o.mjs.map +1 -0
- package/dist/{message-DJC-Jz6T.d.mts → message-iGPIqnn0.d.mts} +15 -8
- package/dist/{nip17-BWq-Kfq5.d.mts → nip17-DNEkz8rM.d.mts} +18 -17
- package/dist/{nip19-D7905oIv.d.mts → nip19-BevCJZsp.d.mts} +23 -3
- package/dist/{nip46-C_DoxiS_.d.mts → nip46-DDV4dC56.d.mts} +13 -12
- package/dist/{nip59-DljW_sYh.d.mts → nip59-zQgdLcEr.d.mts} +28 -25
- package/dist/{nip65-Di5hZRuV.d.mts → nip65-DBfh2smt.d.mts} +8 -3
- package/dist/{nip77-ByxlgoQO.d.mts → nip77-DH4RhS5r.d.mts} +9 -8
- package/dist/nips/blossom.d.mts +37 -37
- package/dist/nips/blossom.mjs +67 -52
- package/dist/nips/blossom.mjs.map +1 -1
- package/dist/nips/nip04.d.mts +2 -3
- package/dist/nips/nip04.mjs +6 -10
- package/dist/nips/nip04.mjs.map +1 -1
- package/dist/nips/nip05.d.mts +37 -43
- package/dist/nips/nip05.mjs +57 -57
- package/dist/nips/nip05.mjs.map +1 -1
- package/dist/nips/nip10.d.mts +18 -19
- package/dist/nips/nip10.mjs +76 -61
- package/dist/nips/nip10.mjs.map +1 -1
- package/dist/nips/nip11.d.mts +9 -8
- package/dist/nips/nip11.mjs +14 -11
- package/dist/nips/nip11.mjs.map +1 -1
- package/dist/nips/nip13.d.mts +9 -9
- package/dist/nips/nip13.mjs +15 -17
- package/dist/nips/nip13.mjs.map +1 -1
- package/dist/nips/nip17.d.mts +1 -1
- package/dist/nips/nip17.mjs +17 -12
- package/dist/nips/nip17.mjs.map +1 -1
- package/dist/nips/nip19.d.mts +1 -1
- package/dist/nips/nip19.mjs +52 -29
- package/dist/nips/nip19.mjs.map +1 -1
- package/dist/nips/nip21.d.mts +5 -6
- package/dist/nips/nip21.mjs +8 -7
- package/dist/nips/nip21.mjs.map +1 -1
- package/dist/nips/nip27.d.mts +7 -8
- package/dist/nips/nip27.mjs +26 -25
- package/dist/nips/nip27.mjs.map +1 -1
- package/dist/nips/nip42.d.mts +5 -6
- package/dist/nips/nip42.mjs +3 -3
- package/dist/nips/nip42.mjs.map +1 -1
- package/dist/nips/nip44.d.mts +24 -7
- package/dist/nips/nip44.mjs +25 -15
- package/dist/nips/nip44.mjs.map +1 -1
- package/dist/nips/nip46.d.mts +1 -1
- package/dist/nips/nip46.mjs +52 -49
- package/dist/nips/nip46.mjs.map +1 -1
- package/dist/nips/nip49.d.mts +8 -7
- package/dist/nips/nip49.mjs +16 -12
- package/dist/nips/nip49.mjs.map +1 -1
- package/dist/nips/nip51.d.mts +25 -27
- package/dist/nips/nip51.mjs +47 -36
- package/dist/nips/nip51.mjs.map +1 -1
- package/dist/nips/nip57.d.mts +12 -13
- package/dist/nips/nip57.mjs +35 -38
- package/dist/nips/nip57.mjs.map +1 -1
- package/dist/nips/nip59.d.mts +1 -1
- package/dist/nips/nip59.mjs +14 -18
- package/dist/nips/nip59.mjs.map +1 -1
- package/dist/nips/nip65.d.mts +1 -1
- package/dist/nips/nip65.mjs +11 -8
- package/dist/nips/nip65.mjs.map +1 -1
- package/dist/nips/nip77.d.mts +1 -1
- package/dist/nips/nip77.mjs +56 -34
- package/dist/nips/nip77.mjs.map +1 -1
- package/dist/nips/nip96.d.mts +21 -12
- package/dist/nips/nip96.mjs +59 -36
- package/dist/nips/nip96.mjs.map +1 -1
- package/dist/nips/nip98.d.mts +12 -12
- package/dist/nips/nip98.mjs +18 -19
- package/dist/nips/nip98.mjs.map +1 -1
- package/dist/pool-B7p_reAm.d.mts +333 -0
- package/dist/{pool-C_j1OmBB.mjs → pool-C2xOcsun.mjs} +792 -568
- package/dist/pool-C2xOcsun.mjs.map +1 -0
- package/dist/reactive-DDlThDSZ.mjs +324 -0
- package/dist/reactive-DDlThDSZ.mjs.map +1 -0
- package/dist/reactive-DFpqtIf_.d.mts +66 -0
- package/dist/relay.d.mts +5 -4
- package/dist/relay.mjs +2 -2
- package/dist/report-CHld4uWj.mjs +32 -0
- package/dist/report-CHld4uWj.mjs.map +1 -0
- package/dist/{signer-CZ6K6FUc.mjs → signer-CKR9EHce.mjs} +107 -72
- package/dist/signer-CKR9EHce.mjs.map +1 -0
- package/dist/signer.d.mts +3 -3
- package/dist/signer.mjs +3 -2
- package/dist/storage-xXxLGCVb.mjs +1376 -0
- package/dist/storage-xXxLGCVb.mjs.map +1 -0
- package/dist/storage.d.mts +4 -4
- package/dist/storage.mjs +5 -4
- package/dist/store.d.mts +2 -0
- package/dist/store.mjs +2 -0
- package/dist/testing.d.mts +135 -0
- package/dist/testing.mjs +963 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/types-2pCUprRU.d.mts +47 -0
- package/dist/types-CRQEk0Oi.d.mts +19 -0
- package/dist/util-LfQirgDk.mjs +113 -0
- package/dist/util-LfQirgDk.mjs.map +1 -0
- package/dist/wasm.d.mts +4 -4
- package/dist/wasm.mjs +54 -55
- package/dist/wasm.mjs.map +1 -1
- package/dist/websocket-CmeqvAkp.d.mts +27 -0
- package/package.json +93 -39
- package/dist/builder-q0VsvnYE.mjs.map +0 -1
- package/dist/client-Bd8B1mYm.mjs.map +0 -1
- package/dist/error-B49rbDzT.d.mts +0 -7
- package/dist/error-CwBxle6N.mjs.map +0 -1
- package/dist/error-DZEs5OuZ.mjs +0 -24
- package/dist/error-DZEs5OuZ.mjs.map +0 -1
- package/dist/event-CzoQEFMF.mjs.map +0 -1
- package/dist/filter-BxwjEkZp.mjs.map +0 -1
- package/dist/filter-DUEKP3q1.d.mts +0 -40
- package/dist/gossip-3xkRRCm_.mjs.map +0 -1
- package/dist/http-D2rhokZo.mjs.map +0 -1
- package/dist/index-C0C4e-Gn.d.mts +0 -55
- package/dist/index-DgOugrpF.d.mts +0 -275
- package/dist/index-EwL6fHLo.d.mts +0 -267
- package/dist/index-k2YhCgsh.d.mts +0 -57
- package/dist/key-Dn-aX4x4.mjs.map +0 -1
- package/dist/kind-BLl4cLOc.mjs.map +0 -1
- package/dist/loaders-BNQszDAn.mjs.map +0 -1
- package/dist/memory-BCmhZPek.mjs.map +0 -1
- package/dist/message-C_Uj-Y-c.mjs.map +0 -1
- package/dist/pool-C_j1OmBB.mjs.map +0 -1
- package/dist/pool-DhRi7l-b.d.mts +0 -297
- package/dist/signer-CZ6K6FUc.mjs.map +0 -1
- package/dist/storage-DOy0iLRE.mjs +0 -870
- package/dist/storage-DOy0iLRE.mjs.map +0 -1
- package/dist/types-BzyADqnw.d.mts +0 -19
- package/dist/types-Dn24TPFs.d.mts +0 -43
- package/dist/util-6tfLQ9JF.mjs +0 -73
- package/dist/util-6tfLQ9JF.mjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as isEphemeralKind, t as Kind } from "./kind-
|
|
3
|
-
import { a as
|
|
4
|
-
import {
|
|
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
|
|
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
|
|
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
|
|
71
|
+
if (pendingPk !== void 0 && event.pubkey === pendingPk) return true;
|
|
72
72
|
const addr = eventAddress(event);
|
|
73
|
-
if (
|
|
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
|
-
|
|
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:
|
|
145
|
+
result: "replaced",
|
|
150
146
|
event,
|
|
151
147
|
address,
|
|
152
|
-
replaceId: prev
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
*
|
|
188
|
-
*
|
|
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
|
|
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
|
-
|
|
200
|
-
|
|
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)
|
|
206
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
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
|
-
},
|
|
239
|
+
}, decision);
|
|
240
|
+
this.#trimTombstones();
|
|
241
|
+
return result;
|
|
217
242
|
}
|
|
218
|
-
/** Sequential `put` in input order.
|
|
219
|
-
|
|
243
|
+
/** Sequential `put` in input order. */
|
|
244
|
+
putMany(events) {
|
|
220
245
|
const results = [];
|
|
221
|
-
for (const event of events) results.push(
|
|
246
|
+
for (const event of events) results.push(this.put(event));
|
|
222
247
|
return results;
|
|
223
248
|
}
|
|
224
|
-
|
|
249
|
+
get(id) {
|
|
225
250
|
const key = id.toLowerCase();
|
|
226
|
-
if (this.#deletion.ids.has(key)) return
|
|
251
|
+
if (this.#deletion.ids.has(key)) return;
|
|
227
252
|
return this.#byId.get(key);
|
|
228
253
|
}
|
|
229
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
456
|
-
return a.id.localeCompare(b.id);
|
|
586
|
+
return compareEventsDesc(a, b);
|
|
457
587
|
}
|
|
458
588
|
//#endregion
|
|
459
|
-
export {
|
|
589
|
+
export { planDeletion as a, DeletionState as i, decidePut as n, outboxBoundKey as r, MemoryIndex as t };
|
|
460
590
|
|
|
461
|
-
//# sourceMappingURL=memory-
|
|
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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"}
|