@qntx/nostr 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{abort-Ba8tDrOI.mjs → abort-DBq2STPj.mjs} +13 -6
- package/dist/abort-DBq2STPj.mjs.map +1 -0
- package/dist/{builder-9tjI6WQ3.mjs → builder-CdLE4J6O.mjs} +11 -17
- package/dist/builder-CdLE4J6O.mjs.map +1 -0
- package/dist/{builder-sSflu6nB.d.mts → builder-DgkqDylK.d.mts} +5 -6
- package/dist/{client-BtIAtxrL.mjs → client-CILhdfsF.mjs} +104 -314
- package/dist/client-CILhdfsF.mjs.map +1 -0
- package/dist/client.d.mts +2 -2
- package/dist/client.mjs +2 -2
- package/dist/collections-DtwMc1NU.mjs +33 -0
- package/dist/collections-DtwMc1NU.mjs.map +1 -0
- package/dist/core.d.mts +9 -8
- package/dist/core.mjs +7 -7
- package/dist/{error-BW7re1UN.d.mts → error-BD_0QFKT.d.mts} +2 -2
- package/dist/{error-CR8Urc84.d.mts → error-BquXDmIN.d.mts} +4 -4
- package/dist/{error-DrEIM7FO.mjs → error-L_QdmSnV.mjs} +2 -2
- package/dist/{error-DrEIM7FO.mjs.map → error-L_QdmSnV.mjs.map} +1 -1
- package/dist/{error-B__OMBRY.mjs → error-gtJvV6YS.mjs} +2 -2
- package/dist/{error-B__OMBRY.mjs.map → error-gtJvV6YS.mjs.map} +1 -1
- package/dist/{event-fU3Y01PR.d.mts → event-DJeb_DIL.d.mts} +4 -2
- package/dist/{event-D8Nfoh4K.mjs → event-DinQMmJ1.mjs} +16 -10
- package/dist/event-DinQMmJ1.mjs.map +1 -0
- package/dist/{filter-C0IMByE3.d.mts → filter-jI_9htF7.d.mts} +2 -2
- package/dist/{gossip-DpIvkBuj.d.mts → gossip-C7HLW5kI.d.mts} +35 -26
- package/dist/{gossip-oloDMWEj.mjs → gossip-CcTp9VpU.mjs} +23 -33
- package/dist/gossip-CcTp9VpU.mjs.map +1 -0
- package/dist/gossip.d.mts +1 -1
- package/dist/gossip.mjs +1 -1
- package/dist/{index-CvkvqB16.d.mts → index-DU9tRFTZ.d.mts} +7 -20
- package/dist/{index-q4MDce_r.d.mts → index-DiXwNn2M.d.mts} +6 -8
- package/dist/{index-QxvrpMfF.d.mts → index-DocgD9K0.d.mts} +92 -167
- package/dist/{index-D1bqFHKB.d.mts → index-InQZpHyQ.d.mts} +18 -18
- package/dist/{index-MxC28S4P.d.mts → index-Tx15HGAD.d.mts} +26 -28
- package/dist/{index-Dmc8FJgO.d.mts → index-iLT8UzY3.d.mts} +19 -37
- package/dist/index.d.mts +23 -21
- package/dist/index.mjs +21 -19
- package/dist/instance-BcKzxHIP.mjs +220 -0
- package/dist/instance-BcKzxHIP.mjs.map +1 -0
- package/dist/instance-DjsL8h84.d.mts +15 -0
- package/dist/{key-Cv4_pN3x.d.mts → key-D_5OgGT7.d.mts} +11 -7
- package/dist/{key-Bps-LqX7.mjs → key-bshqfmZL.mjs} +13 -9
- package/dist/key-bshqfmZL.mjs.map +1 -0
- package/dist/{loaders-o6YXB_Ri.mjs → loaders-DD0XAGzw.mjs} +73 -82
- package/dist/loaders-DD0XAGzw.mjs.map +1 -0
- package/dist/loaders.d.mts +3 -3
- package/dist/loaders.mjs +2 -2
- package/dist/{memory-C2EVx0_O.mjs → memory-D_-5-T31.mjs} +2 -2
- package/dist/memory-D_-5-T31.mjs.map +1 -0
- package/dist/{memory-index-Do1mz0WF.mjs → memory-index-C6rujtoc.mjs} +30 -247
- package/dist/memory-index-C6rujtoc.mjs.map +1 -0
- package/dist/message-CWLcFTQ1.mjs +194 -0
- package/dist/message-CWLcFTQ1.mjs.map +1 -0
- package/dist/{message-iGPIqnn0.d.mts → message-CbBZOhq3.d.mts} +3 -3
- package/dist/{nip17-CKIufV98.d.mts → nip17-cTDFfFVt.d.mts} +5 -6
- package/dist/{nip19-BevCJZsp.d.mts → nip19-D1OnJ192.d.mts} +3 -9
- package/dist/{nip46-DDV4dC56.d.mts → nip46-DrsPI8EX.d.mts} +3 -5
- package/dist/nip51-DoPUvQ--.d.mts +87 -0
- package/dist/{nip59-zQgdLcEr.d.mts → nip59-BFIihLYY.d.mts} +3 -4
- package/dist/nip65-52HiwiyD.d.mts +23 -0
- package/dist/{nip77-DH4RhS5r.d.mts → nip77-C9QcZn7b.d.mts} +3 -4
- package/dist/nips/blossom.d.mts +12 -8
- package/dist/nips/blossom.mjs +30 -9
- package/dist/nips/blossom.mjs.map +1 -1
- package/dist/nips/nip04.mjs +17 -7
- package/dist/nips/nip04.mjs.map +1 -1
- package/dist/nips/nip05.d.mts +2 -3
- package/dist/nips/nip05.mjs +1 -4
- package/dist/nips/nip05.mjs.map +1 -1
- package/dist/nips/nip10.d.mts +3 -3
- package/dist/nips/nip10.mjs +3 -3
- package/dist/nips/nip11.d.mts +1 -1
- package/dist/nips/nip11.mjs +4 -4
- package/dist/nips/nip11.mjs.map +1 -1
- package/dist/nips/nip13.d.mts +7 -7
- package/dist/nips/nip13.mjs +32 -10
- package/dist/nips/nip13.mjs.map +1 -1
- package/dist/nips/nip17.d.mts +1 -1
- package/dist/nips/nip17.mjs +13 -33
- package/dist/nips/nip17.mjs.map +1 -1
- package/dist/nips/nip19.d.mts +2 -2
- package/dist/nips/nip19.mjs +16 -28
- package/dist/nips/nip19.mjs.map +1 -1
- package/dist/nips/nip21.d.mts +6 -1
- package/dist/nips/nip21.mjs +15 -5
- package/dist/nips/nip21.mjs.map +1 -1
- package/dist/nips/nip27.d.mts +4 -4
- package/dist/nips/nip27.mjs.map +1 -1
- package/dist/nips/nip42.d.mts +1 -1
- package/dist/nips/nip42.mjs +2 -1
- package/dist/nips/nip42.mjs.map +1 -1
- package/dist/nips/nip44.mjs +1 -1
- package/dist/nips/nip46.d.mts +2 -2
- package/dist/nips/nip46.mjs +15 -19
- package/dist/nips/nip46.mjs.map +1 -1
- package/dist/nips/nip49.d.mts +5 -5
- package/dist/nips/nip49.mjs +4 -4
- package/dist/nips/nip49.mjs.map +1 -1
- package/dist/nips/nip51.d.mts +2 -86
- package/dist/nips/nip51.mjs +7 -23
- package/dist/nips/nip51.mjs.map +1 -1
- package/dist/nips/nip57.d.mts +7 -5
- package/dist/nips/nip57.mjs +12 -17
- package/dist/nips/nip57.mjs.map +1 -1
- package/dist/nips/nip59.d.mts +1 -1
- package/dist/nips/nip59.mjs +16 -16
- package/dist/nips/nip59.mjs.map +1 -1
- package/dist/nips/nip65.d.mts +2 -2
- package/dist/nips/nip65.mjs +14 -37
- package/dist/nips/nip65.mjs.map +1 -1
- package/dist/nips/nip77.d.mts +1 -1
- package/dist/nips/nip77.mjs +33 -32
- package/dist/nips/nip77.mjs.map +1 -1
- package/dist/nips/nip98.d.mts +5 -5
- package/dist/nips/nip98.mjs +8 -8
- package/dist/nips/nip98.mjs.map +1 -1
- package/dist/nostr_crypto_wasm_bg.wasm +0 -0
- package/dist/{pool-DBIYMtwy.d.mts → pool-SzJKPIa3.d.mts} +96 -91
- package/dist/pool-YHRZgcQ3.mjs +596 -0
- package/dist/pool-YHRZgcQ3.mjs.map +1 -0
- package/dist/put-DS0yzVJx.mjs +187 -0
- package/dist/put-DS0yzVJx.mjs.map +1 -0
- package/dist/{reactive-DFpqtIf_.d.mts → reactive-BJ1ggn4d.d.mts} +9 -22
- package/dist/{reactive-DDlThDSZ.mjs → reactive-BJRoVHxU.mjs} +113 -64
- package/dist/reactive-BJRoVHxU.mjs.map +1 -0
- package/dist/{pool-As20OGxB.mjs → relay-CvxPiQ_m.mjs} +178 -721
- package/dist/relay-CvxPiQ_m.mjs.map +1 -0
- package/dist/relay.d.mts +4 -4
- package/dist/relay.mjs +3 -2
- package/dist/serial-Bzpw-xz3.mjs +21 -0
- package/dist/serial-Bzpw-xz3.mjs.map +1 -0
- package/dist/{signer-CYQutDzD.mjs → signer-yW3slDC2.mjs} +12 -14
- package/dist/signer-yW3slDC2.mjs.map +1 -0
- package/dist/signer.d.mts +2 -2
- package/dist/signer.mjs +2 -2
- package/dist/{storage-Dky3wuoH.mjs → storage-CZ52ifcQ.mjs} +17 -42
- package/dist/storage-CZ52ifcQ.mjs.map +1 -0
- package/dist/storage.d.mts +3 -3
- package/dist/storage.mjs +4 -4
- package/dist/store.d.mts +1 -1
- package/dist/store.mjs +1 -1
- package/dist/testing.d.mts +18 -18
- package/dist/testing.mjs +9 -9
- package/dist/testing.mjs.map +1 -1
- package/dist/{types-2pCUprRU.d.mts → types-Cm-XMgtH.d.mts} +6 -6
- package/dist/{types-CRQEk0Oi.d.mts → types-DNmiscfd.d.mts} +2 -2
- package/dist/{util-LfQirgDk.mjs → util-yFYzlbrm.mjs} +29 -7
- package/dist/util-yFYzlbrm.mjs.map +1 -0
- package/dist/{verifier-t5WoWbtY.mjs → verifier-BTLOpo1n.mjs} +3 -3
- package/dist/verifier-BTLOpo1n.mjs.map +1 -0
- package/dist/verifier-CgTFpgsC.d.mts +22 -0
- package/dist/wasm.browser.d.mts +8 -0
- package/dist/wasm.browser.mjs +19 -0
- package/dist/wasm.browser.mjs.map +1 -0
- package/dist/wasm.d.mts +3 -13
- package/dist/wasm.mjs +7 -208
- package/dist/wasm.mjs.map +1 -1
- package/package.json +13 -10
- package/dist/abort-Ba8tDrOI.mjs.map +0 -1
- package/dist/builder-9tjI6WQ3.mjs.map +0 -1
- package/dist/client-BtIAtxrL.mjs.map +0 -1
- package/dist/event-D8Nfoh4K.mjs.map +0 -1
- package/dist/gossip-oloDMWEj.mjs.map +0 -1
- package/dist/key-Bps-LqX7.mjs.map +0 -1
- package/dist/loaders-o6YXB_Ri.mjs.map +0 -1
- package/dist/memory-C2EVx0_O.mjs.map +0 -1
- package/dist/memory-index-Do1mz0WF.mjs.map +0 -1
- package/dist/message-DcY6Fi9o.mjs +0 -193
- package/dist/message-DcY6Fi9o.mjs.map +0 -1
- package/dist/nip65-CjgmdpPB.d.mts +0 -26
- package/dist/nips/nip96.d.mts +0 -40
- package/dist/nips/nip96.mjs +0 -118
- package/dist/nips/nip96.mjs.map +0 -1
- package/dist/pool-As20OGxB.mjs.map +0 -1
- package/dist/reactive-DDlThDSZ.mjs.map +0 -1
- package/dist/signer-CYQutDzD.mjs.map +0 -1
- package/dist/storage-Dky3wuoH.mjs.map +0 -1
- package/dist/util-LfQirgDk.mjs.map +0 -1
- package/dist/verifier-t5WoWbtY.mjs.map +0 -1
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { o as isHex32 } from "./util-yFYzlbrm.mjs";
|
|
2
|
+
import { i as isEphemeralKind, t as Kind } from "./kind-Cxwhhkx8.mjs";
|
|
3
|
+
import { a as isReplaceableWinner, g as eventAddress, m as validateSignedEvent, x as parseEventAddress } from "./event-DinQMmJ1.mjs";
|
|
4
|
+
//#region src/storage/deletion.ts
|
|
5
|
+
/**
|
|
6
|
+
* NIP-09 application plan for a kind 5 event.
|
|
7
|
+
*
|
|
8
|
+
* - `e` tags: delete only when the target exists, is not itself kind 5, and shares pubkey.
|
|
9
|
+
* - Unknown `e` ids are pending until the event arrives (client MUST validate pubkey).
|
|
10
|
+
* - `a` tags: tombstone that coordinate up to this deletion's `created_at` when pubkey matches.
|
|
11
|
+
* - Deleting a deletion request is a no-op.
|
|
12
|
+
*/
|
|
13
|
+
function planDeletion(deletion, getById) {
|
|
14
|
+
const { pubkey } = deletion;
|
|
15
|
+
const removeIds = [];
|
|
16
|
+
const pendingIds = [];
|
|
17
|
+
const coordinates = [];
|
|
18
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
19
|
+
const seenCoords = /* @__PURE__ */ new Set();
|
|
20
|
+
for (const tag of deletion.tags) {
|
|
21
|
+
if (tag[0] === "e" && tag[1] !== void 0 && isHex32(tag[1].toLowerCase())) {
|
|
22
|
+
const id = tag[1].toLowerCase();
|
|
23
|
+
if (seenIds.has(id)) continue;
|
|
24
|
+
seenIds.add(id);
|
|
25
|
+
const existing = getById(id);
|
|
26
|
+
if (!existing) {
|
|
27
|
+
pendingIds.push({
|
|
28
|
+
id,
|
|
29
|
+
pubkey
|
|
30
|
+
});
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (existing.kind === Kind.EventDeletion) continue;
|
|
34
|
+
if (existing.pubkey !== pubkey) continue;
|
|
35
|
+
removeIds.push(id);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (tag[0] === "a" && tag[1] !== void 0) {
|
|
39
|
+
const coord = parseEventAddress(tag[1]);
|
|
40
|
+
if (!coord || coord.pubkey !== pubkey) continue;
|
|
41
|
+
const key = `${coord.kind}:${coord.pubkey}:${coord.identifier}`;
|
|
42
|
+
if (seenCoords.has(key)) continue;
|
|
43
|
+
seenCoords.add(key);
|
|
44
|
+
coordinates.push({
|
|
45
|
+
key,
|
|
46
|
+
until: deletion.created_at
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
removeIds,
|
|
52
|
+
pendingIds,
|
|
53
|
+
coordinates
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** In-memory NIP-09 tombstones shared by MemoryEventStore and IndexedDbEventStore. */
|
|
57
|
+
var DeletionState = class {
|
|
58
|
+
ids = /* @__PURE__ */ new Set();
|
|
59
|
+
pending = /* @__PURE__ */ new Map();
|
|
60
|
+
coordinates = /* @__PURE__ */ new Map();
|
|
61
|
+
clear() {
|
|
62
|
+
this.ids.clear();
|
|
63
|
+
this.pending.clear();
|
|
64
|
+
this.coordinates.clear();
|
|
65
|
+
}
|
|
66
|
+
covers(event) {
|
|
67
|
+
if (this.ids.has(event.id)) return true;
|
|
68
|
+
if (event.kind === Kind.EventDeletion) return false;
|
|
69
|
+
const pendingPk = this.pending.get(event.id);
|
|
70
|
+
if (pendingPk !== void 0 && event.pubkey === pendingPk) return true;
|
|
71
|
+
const addr = eventAddress(event);
|
|
72
|
+
if (addr === void 0) return false;
|
|
73
|
+
const until = this.coordinates.get(addr);
|
|
74
|
+
return until !== void 0 && event.created_at <= until;
|
|
75
|
+
}
|
|
76
|
+
absorb(plan) {
|
|
77
|
+
for (const id of plan.removeIds) {
|
|
78
|
+
this.ids.add(id);
|
|
79
|
+
this.pending.delete(id);
|
|
80
|
+
}
|
|
81
|
+
for (const p of plan.pendingIds) if (!this.ids.has(p.id)) this.pending.set(p.id, p.pubkey);
|
|
82
|
+
for (const c of plan.coordinates) {
|
|
83
|
+
const prev = this.coordinates.get(c.key) ?? Number.NEGATIVE_INFINITY;
|
|
84
|
+
this.coordinates.delete(c.key);
|
|
85
|
+
this.coordinates.set(c.key, Math.max(prev, c.until));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
function coordinateRemovals(coordinates, getCurrent) {
|
|
90
|
+
const ids = [];
|
|
91
|
+
for (const c of coordinates) {
|
|
92
|
+
const current = getCurrent(c.key);
|
|
93
|
+
if (current && current.created_at <= c.until) ids.push(current.id);
|
|
94
|
+
}
|
|
95
|
+
return ids;
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/storage/put.ts
|
|
99
|
+
function outboxBoundKey(pubkey, kind) {
|
|
100
|
+
return `${pubkey.toLowerCase()}:${kind}`;
|
|
101
|
+
}
|
|
102
|
+
function decidePut(raw, lookup) {
|
|
103
|
+
if (!validateSignedEvent(raw)) return {
|
|
104
|
+
action: "skip",
|
|
105
|
+
result: "invalid",
|
|
106
|
+
event: raw
|
|
107
|
+
};
|
|
108
|
+
const event = raw;
|
|
109
|
+
if (lookup.deletion.ids.has(event.id) || lookup.getById(event.id)) return {
|
|
110
|
+
action: "skip",
|
|
111
|
+
result: "duplicate",
|
|
112
|
+
event
|
|
113
|
+
};
|
|
114
|
+
if (event.kind === Kind.EventDeletion) {
|
|
115
|
+
const plan = planDeletion(event, lookup.getById);
|
|
116
|
+
return {
|
|
117
|
+
action: "delete",
|
|
118
|
+
result: "deleted",
|
|
119
|
+
event,
|
|
120
|
+
plan,
|
|
121
|
+
coordIds: coordinateRemovals(plan.coordinates, lookup.getReplaceable)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (lookup.deletion.covers(event)) return {
|
|
125
|
+
action: "tombstone",
|
|
126
|
+
result: "duplicate",
|
|
127
|
+
event
|
|
128
|
+
};
|
|
129
|
+
if (isEphemeralKind(event.kind)) return {
|
|
130
|
+
action: "skip",
|
|
131
|
+
result: "ephemeral",
|
|
132
|
+
event
|
|
133
|
+
};
|
|
134
|
+
const address = eventAddress(event);
|
|
135
|
+
if (address !== void 0) {
|
|
136
|
+
const prev = lookup.getReplaceable(address);
|
|
137
|
+
if (prev && prev.id !== event.id && !isReplaceableWinner(event, prev)) return {
|
|
138
|
+
action: "skip",
|
|
139
|
+
result: "rejected",
|
|
140
|
+
event
|
|
141
|
+
};
|
|
142
|
+
if (prev !== void 0 && prev.evicted !== true) return {
|
|
143
|
+
action: "insert",
|
|
144
|
+
result: "replaced",
|
|
145
|
+
event,
|
|
146
|
+
address,
|
|
147
|
+
replaceId: prev.id
|
|
148
|
+
};
|
|
149
|
+
return {
|
|
150
|
+
action: "insert",
|
|
151
|
+
result: "accepted",
|
|
152
|
+
event,
|
|
153
|
+
address
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
action: "insert",
|
|
158
|
+
result: "accepted",
|
|
159
|
+
event
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function applyPutMemory(s, d) {
|
|
163
|
+
if (d.action === "skip") return d.result;
|
|
164
|
+
if (d.action === "tombstone") {
|
|
165
|
+
s.deletion.ids.add(d.event.id);
|
|
166
|
+
s.deletion.pending.delete(d.event.id);
|
|
167
|
+
return "duplicate";
|
|
168
|
+
}
|
|
169
|
+
if (d.action === "delete") {
|
|
170
|
+
s.deletion.pending.delete(d.event.id);
|
|
171
|
+
s.deletion.absorb(d.plan);
|
|
172
|
+
for (const id of d.plan.removeIds) s.indexRemove(id);
|
|
173
|
+
for (const id of d.coordIds) {
|
|
174
|
+
s.deletion.ids.add(id);
|
|
175
|
+
s.indexRemove(id);
|
|
176
|
+
}
|
|
177
|
+
s.indexInsert(d.event);
|
|
178
|
+
return "deleted";
|
|
179
|
+
}
|
|
180
|
+
if (d.replaceId !== void 0) s.indexRemove(d.replaceId);
|
|
181
|
+
s.indexInsert(d.event);
|
|
182
|
+
return d.result;
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
export { planDeletion as a, DeletionState as i, decidePut as n, outboxBoundKey as r, applyPutMemory as t };
|
|
186
|
+
|
|
187
|
+
//# sourceMappingURL=put-DS0yzVJx.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"put-DS0yzVJx.mjs","names":[],"sources":["../src/storage/deletion.ts","../src/storage/put.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"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as Event } from "./event-
|
|
2
|
-
import { t as Filter } from "./filter-
|
|
3
|
-
import { i as PutResult } from "./types-
|
|
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-Cm-XMgtH.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,11 @@ 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;
|
|
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;
|
|
17
|
+
maxTombstones?: number | undefined;
|
|
30
18
|
};
|
|
31
19
|
/**
|
|
32
20
|
* Synchronous reactive in-memory event store backed by {@link MemoryIndex}. The single read source
|
|
@@ -36,7 +24,6 @@ type WatchHandle = {
|
|
|
36
24
|
*/
|
|
37
25
|
declare class ReactiveEventStore {
|
|
38
26
|
#private;
|
|
39
|
-
_version: number;
|
|
40
27
|
constructor(opts?: ReactiveEventStoreOptions);
|
|
41
28
|
add(event: Event, relayUrl?: string): PutResult;
|
|
42
29
|
addMany(events: ReadonlyArray<Event>, relayUrl?: string): PutResult[];
|
|
@@ -58,9 +45,9 @@ declare class ReactiveEventStore {
|
|
|
58
45
|
watchQuery(filters: ReadonlyArray<Filter>): Watch<ReadonlyArray<Event>>;
|
|
59
46
|
/** Synchronous listener for every physical index insert (after update). */
|
|
60
47
|
onInsert(listener: (event: Event) => void): () => void;
|
|
61
|
-
|
|
62
|
-
|
|
48
|
+
/** Synchronous listener for every physical index remove (after watch invalidation). */
|
|
49
|
+
onRemove(listener: (event: Event) => void): () => void;
|
|
63
50
|
}
|
|
64
51
|
//#endregion
|
|
65
52
|
export { ReactiveEventStoreOptions as n, Watch as r, ReactiveEventStore as t };
|
|
66
|
-
//# sourceMappingURL=reactive-
|
|
53
|
+
//# sourceMappingURL=reactive-BJ1ggn4d.d.mts.map
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { u as normalizeURL } from "./util-yFYzlbrm.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-DinQMmJ1.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
|
|
6
|
+
import { i as trimOldest, n as removeFromSetMap, t as addToSetMap } from "./collections-DtwMc1NU.mjs";
|
|
7
|
+
import { t as MemoryIndex } from "./memory-index-C6rujtoc.mjs";
|
|
6
8
|
//#region src/store/reactive.ts
|
|
7
9
|
const SEEN_ON_PER_ID = 16;
|
|
8
10
|
var WatchImpl = class {
|
|
9
|
-
|
|
11
|
+
host;
|
|
10
12
|
kind;
|
|
11
13
|
key;
|
|
12
14
|
filters;
|
|
@@ -18,8 +20,8 @@ var WatchImpl = class {
|
|
|
18
20
|
#version = -1;
|
|
19
21
|
#dirty = false;
|
|
20
22
|
#pendingRemove = false;
|
|
21
|
-
constructor(
|
|
22
|
-
this.
|
|
23
|
+
constructor(host, kind, key, compute, equal, snapshotIds, filters) {
|
|
24
|
+
this.host = host;
|
|
23
25
|
this.kind = kind;
|
|
24
26
|
this.key = key;
|
|
25
27
|
this.filters = filters;
|
|
@@ -27,7 +29,7 @@ var WatchImpl = class {
|
|
|
27
29
|
this.equal = equal;
|
|
28
30
|
this.snapshotIds = snapshotIds;
|
|
29
31
|
this.#snapshot = compute();
|
|
30
|
-
this.#version =
|
|
32
|
+
this.#version = host.version();
|
|
31
33
|
}
|
|
32
34
|
get subscribed() {
|
|
33
35
|
return this.#subscribers.size > 0;
|
|
@@ -39,30 +41,33 @@ var WatchImpl = class {
|
|
|
39
41
|
const wasUnsubscribed = this.#subscribers.size === 0;
|
|
40
42
|
this.#subscribers.add(onChange);
|
|
41
43
|
this.#pendingRemove = false;
|
|
42
|
-
if (wasUnsubscribed && this.#version !== this.
|
|
43
|
-
this.
|
|
44
|
+
if (wasUnsubscribed && this.#version !== this.host.version()) this.#dirty = true;
|
|
45
|
+
this.host.register(this);
|
|
44
46
|
return () => {
|
|
45
47
|
this.#subscribers.delete(onChange);
|
|
46
48
|
if (this.#subscribers.size > 0 || this.#pendingRemove) return;
|
|
47
49
|
this.#pendingRemove = true;
|
|
48
50
|
queueMicrotask(() => {
|
|
49
|
-
if (this.#pendingRemove) this.
|
|
51
|
+
if (this.#pendingRemove) this.host.unregister(this);
|
|
50
52
|
});
|
|
51
53
|
};
|
|
52
54
|
}
|
|
53
55
|
getSnapshot() {
|
|
54
|
-
if (this.subscribed ? this.#dirty : this.#version !== this.
|
|
56
|
+
if (this.subscribed ? this.#dirty : this.#version !== this.host.version()) {
|
|
55
57
|
const next = this.compute();
|
|
56
|
-
if (!this.equal(next, this.#snapshot))
|
|
57
|
-
|
|
58
|
+
if (!this.equal(next, this.#snapshot)) {
|
|
59
|
+
this.#snapshot = next;
|
|
60
|
+
this.host.repin(this);
|
|
61
|
+
}
|
|
62
|
+
this.#version = this.host.version();
|
|
58
63
|
this.#dirty = false;
|
|
59
64
|
}
|
|
60
65
|
return this.#snapshot;
|
|
61
66
|
}
|
|
62
|
-
|
|
67
|
+
markDirty() {
|
|
63
68
|
this.#dirty = true;
|
|
64
69
|
}
|
|
65
|
-
|
|
70
|
+
notify() {
|
|
66
71
|
for (const onChange of this.#subscribers) invokeSafely(onChange);
|
|
67
72
|
}
|
|
68
73
|
};
|
|
@@ -86,16 +91,49 @@ var ReactiveEventStore = class {
|
|
|
86
91
|
#index;
|
|
87
92
|
#maxEvents;
|
|
88
93
|
#maxSeenOnEntries;
|
|
89
|
-
|
|
94
|
+
#version = 0;
|
|
90
95
|
#recency = /* @__PURE__ */ new Set();
|
|
91
96
|
#seenOn = /* @__PURE__ */ new Map();
|
|
92
97
|
#idWatches = /* @__PURE__ */ new Map();
|
|
93
98
|
#addressWatches = /* @__PURE__ */ new Map();
|
|
99
|
+
#eventCache = /* @__PURE__ */ new Map();
|
|
100
|
+
#addressCache = /* @__PURE__ */ new Map();
|
|
94
101
|
#queryCache = /* @__PURE__ */ new Map();
|
|
95
102
|
#queryRegistry = /* @__PURE__ */ new Set();
|
|
103
|
+
#pinned = /* @__PURE__ */ new Map();
|
|
104
|
+
#watchPins = /* @__PURE__ */ new Map();
|
|
96
105
|
#dirty = /* @__PURE__ */ new Set();
|
|
97
106
|
#flushScheduled = false;
|
|
98
107
|
#insertListeners = /* @__PURE__ */ new Set();
|
|
108
|
+
#removeListeners = /* @__PURE__ */ new Set();
|
|
109
|
+
#watchHost = {
|
|
110
|
+
version: () => this.#version,
|
|
111
|
+
register: (watch) => {
|
|
112
|
+
this.#registerWatch(watch);
|
|
113
|
+
if (!this.#watchPins.has(watch)) {
|
|
114
|
+
const ids = watch.pinnedIds;
|
|
115
|
+
this.#watchPins.set(watch, ids);
|
|
116
|
+
this.#pin(ids);
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
unregister: (watch) => {
|
|
120
|
+
if (watch.subscribed) return;
|
|
121
|
+
this.#unregisterWatch(watch);
|
|
122
|
+
const ids = this.#watchPins.get(watch);
|
|
123
|
+
if (ids !== void 0) {
|
|
124
|
+
this.#watchPins.delete(watch);
|
|
125
|
+
this.#unpin(ids);
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
repin: (watch) => {
|
|
129
|
+
const old = this.#watchPins.get(watch);
|
|
130
|
+
if (old === void 0) return;
|
|
131
|
+
const next = watch.pinnedIds;
|
|
132
|
+
this.#unpin(old);
|
|
133
|
+
this.#pin(next);
|
|
134
|
+
this.#watchPins.set(watch, next);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
99
137
|
constructor(opts) {
|
|
100
138
|
this.#maxEvents = opts?.maxEvents ?? 5e4;
|
|
101
139
|
this.#maxSeenOnEntries = opts?.maxSeenOnEntries ?? 2e4;
|
|
@@ -138,7 +176,7 @@ var ReactiveEventStore = class {
|
|
|
138
176
|
this.#index.clear();
|
|
139
177
|
this.#seenOn.clear();
|
|
140
178
|
this.#recency.clear();
|
|
141
|
-
this
|
|
179
|
+
this.#version += 1;
|
|
142
180
|
this.#invalidateAll();
|
|
143
181
|
}
|
|
144
182
|
get(id) {
|
|
@@ -170,18 +208,28 @@ var ReactiveEventStore = class {
|
|
|
170
208
|
}
|
|
171
209
|
watchEvent(id) {
|
|
172
210
|
const key = id.toLowerCase();
|
|
173
|
-
|
|
211
|
+
let watch = this.#eventCache.get(key);
|
|
212
|
+
if (!watch) {
|
|
213
|
+
watch = new WatchImpl(this.#watchHost, "event", key, () => this.get(key), sameRef, EVENT_IDS);
|
|
214
|
+
this.#eventCache.set(key, watch);
|
|
215
|
+
}
|
|
216
|
+
return watch;
|
|
174
217
|
}
|
|
175
218
|
watchReplaceable(kind, pubkey, d) {
|
|
176
219
|
const address = formatEventAddress(kind, pubkey, d ?? "");
|
|
177
|
-
|
|
220
|
+
let watch = this.#addressCache.get(address);
|
|
221
|
+
if (!watch) {
|
|
222
|
+
watch = new WatchImpl(this.#watchHost, "replaceable", address, () => this.getByAddress(address), sameRef, EVENT_IDS);
|
|
223
|
+
this.#addressCache.set(address, watch);
|
|
224
|
+
}
|
|
225
|
+
return watch;
|
|
178
226
|
}
|
|
179
227
|
watchQuery(filters) {
|
|
180
228
|
const key = filterFingerprint(filters);
|
|
181
229
|
let watch = this.#queryCache.get(key);
|
|
182
230
|
if (!watch) {
|
|
183
231
|
const list = [...filters];
|
|
184
|
-
watch = new WatchImpl(this, "query", key, () => this.query(list), sameList, LIST_IDS, list);
|
|
232
|
+
watch = new WatchImpl(this.#watchHost, "query", key, () => this.query(list), sameList, LIST_IDS, list);
|
|
185
233
|
this.#queryCache.set(key, watch);
|
|
186
234
|
}
|
|
187
235
|
return watch;
|
|
@@ -193,25 +241,33 @@ var ReactiveEventStore = class {
|
|
|
193
241
|
this.#insertListeners.delete(listener);
|
|
194
242
|
};
|
|
195
243
|
}
|
|
196
|
-
|
|
244
|
+
/** Synchronous listener for every physical index remove (after watch invalidation). */
|
|
245
|
+
onRemove(listener) {
|
|
246
|
+
this.#removeListeners.add(listener);
|
|
247
|
+
return () => {
|
|
248
|
+
this.#removeListeners.delete(listener);
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
#registerWatch(watch) {
|
|
197
252
|
switch (watch.kind) {
|
|
198
253
|
case "event":
|
|
199
|
-
|
|
254
|
+
addToSetMap(this.#idWatches, watch.key, watch);
|
|
200
255
|
return;
|
|
201
256
|
case "replaceable":
|
|
202
|
-
|
|
257
|
+
addToSetMap(this.#addressWatches, watch.key, watch);
|
|
203
258
|
return;
|
|
204
259
|
case "query": this.#queryRegistry.add(watch);
|
|
205
260
|
}
|
|
206
261
|
}
|
|
207
|
-
|
|
208
|
-
if (watch.subscribed) return;
|
|
262
|
+
#unregisterWatch(watch) {
|
|
209
263
|
switch (watch.kind) {
|
|
210
264
|
case "event":
|
|
211
|
-
|
|
265
|
+
removeFromSetMap(this.#idWatches, watch.key, watch);
|
|
266
|
+
if (this.#eventCache.get(watch.key) === watch) this.#eventCache.delete(watch.key);
|
|
212
267
|
return;
|
|
213
268
|
case "replaceable":
|
|
214
|
-
|
|
269
|
+
removeFromSetMap(this.#addressWatches, watch.key, watch);
|
|
270
|
+
if (this.#addressCache.get(watch.key) === watch) this.#addressCache.delete(watch.key);
|
|
215
271
|
return;
|
|
216
272
|
case "query":
|
|
217
273
|
this.#queryRegistry.delete(watch);
|
|
@@ -219,8 +275,8 @@ var ReactiveEventStore = class {
|
|
|
219
275
|
}
|
|
220
276
|
}
|
|
221
277
|
#touch(id) {
|
|
222
|
-
|
|
223
|
-
|
|
278
|
+
this.#recency.delete(id);
|
|
279
|
+
this.#recency.add(id);
|
|
224
280
|
}
|
|
225
281
|
#recordSeen(id, relayUrl) {
|
|
226
282
|
const url = normalizeURL(relayUrl);
|
|
@@ -230,22 +286,19 @@ var ReactiveEventStore = class {
|
|
|
230
286
|
this.#seenOn.set(id, urls);
|
|
231
287
|
}
|
|
232
288
|
if (!urls.includes(url) && urls.length < SEEN_ON_PER_ID) urls.push(url);
|
|
233
|
-
|
|
234
|
-
const oldest = this.#seenOn.keys().next().value;
|
|
235
|
-
if (oldest === void 0) break;
|
|
236
|
-
this.#seenOn.delete(oldest);
|
|
237
|
-
}
|
|
289
|
+
trimOldest(this.#seenOn, this.#maxSeenOnEntries);
|
|
238
290
|
}
|
|
239
291
|
#onInsert(event) {
|
|
240
|
-
this
|
|
292
|
+
this.#version += 1;
|
|
241
293
|
this.#touch(event.id);
|
|
242
294
|
this.#invalidateByEvent(event);
|
|
243
295
|
for (const listener of this.#insertListeners) invokeSafely(() => listener(event));
|
|
244
296
|
}
|
|
245
297
|
#onRemove(event) {
|
|
246
|
-
this
|
|
298
|
+
this.#version += 1;
|
|
247
299
|
this.#recency.delete(event.id);
|
|
248
300
|
this.#invalidateByEvent(event);
|
|
301
|
+
for (const listener of this.#removeListeners) invokeSafely(() => listener(event));
|
|
249
302
|
}
|
|
250
303
|
#invalidateByEvent(event) {
|
|
251
304
|
const byId = this.#idWatches.get(event.id);
|
|
@@ -255,6 +308,17 @@ var ReactiveEventStore = class {
|
|
|
255
308
|
const byAddress = this.#addressWatches.get(address);
|
|
256
309
|
if (byAddress) for (const watch of byAddress) this.#markDirty(watch);
|
|
257
310
|
}
|
|
311
|
+
if (event.kind === Kind.EventDeletion) {
|
|
312
|
+
for (const tag of event.tags) if (tag[0] === "e" && tag[1] !== void 0) {
|
|
313
|
+
const watches = this.#idWatches.get(tag[1].toLowerCase());
|
|
314
|
+
if (watches) for (const watch of watches) this.#markDirty(watch);
|
|
315
|
+
} else if (tag[0] === "a" && tag[1] !== void 0) {
|
|
316
|
+
const coord = parseEventAddress(tag[1]);
|
|
317
|
+
if (coord === void 0) continue;
|
|
318
|
+
const watches = this.#addressWatches.get(formatEventAddress(coord.kind, coord.pubkey, coord.identifier));
|
|
319
|
+
if (watches) for (const watch of watches) this.#markDirty(watch);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
258
322
|
for (const watch of this.#queryRegistry) if (watch.filters !== void 0 && matchFilters(watch.filters, event)) this.#markDirty(watch);
|
|
259
323
|
}
|
|
260
324
|
#invalidateAll() {
|
|
@@ -264,7 +328,7 @@ var ReactiveEventStore = class {
|
|
|
264
328
|
this.#scheduleFlush();
|
|
265
329
|
}
|
|
266
330
|
#markDirty(watch) {
|
|
267
|
-
watch.
|
|
331
|
+
watch.markDirty();
|
|
268
332
|
this.#dirty.add(watch);
|
|
269
333
|
this.#scheduleFlush();
|
|
270
334
|
}
|
|
@@ -275,28 +339,27 @@ var ReactiveEventStore = class {
|
|
|
275
339
|
while (this.#dirty.size > 0) {
|
|
276
340
|
const batch = [...this.#dirty];
|
|
277
341
|
this.#dirty.clear();
|
|
278
|
-
for (const watch of batch) watch.
|
|
342
|
+
for (const watch of batch) watch.notify();
|
|
279
343
|
}
|
|
280
344
|
this.#flushScheduled = false;
|
|
281
345
|
});
|
|
282
346
|
}
|
|
283
|
-
#
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
347
|
+
#pin(ids) {
|
|
348
|
+
for (const id of ids) this.#pinned.set(id, (this.#pinned.get(id) ?? 0) + 1);
|
|
349
|
+
}
|
|
350
|
+
#unpin(ids) {
|
|
351
|
+
for (const id of ids) {
|
|
352
|
+
const count = (this.#pinned.get(id) ?? 0) - 1;
|
|
353
|
+
if (count <= 0) this.#pinned.delete(id);
|
|
354
|
+
else this.#pinned.set(id, count);
|
|
355
|
+
}
|
|
292
356
|
}
|
|
293
357
|
#evictIfNeeded(protectedId) {
|
|
294
358
|
if (this.#index.size <= this.#maxEvents) return;
|
|
295
|
-
const pinned = this.#pinnedIds();
|
|
296
359
|
const evicting = [];
|
|
297
360
|
for (const id of this.#recency) {
|
|
298
361
|
if (this.#index.size - evicting.length <= this.#maxEvents) break;
|
|
299
|
-
if (pinned.has(id) || id === protectedId) continue;
|
|
362
|
+
if (this.#pinned.has(id) || id === protectedId) continue;
|
|
300
363
|
if (this.#index.get(id) === void 0) continue;
|
|
301
364
|
evicting.push(id);
|
|
302
365
|
}
|
|
@@ -304,21 +367,7 @@ var ReactiveEventStore = class {
|
|
|
304
367
|
for (const id of evicting) this.#recency.delete(id);
|
|
305
368
|
}
|
|
306
369
|
};
|
|
307
|
-
function addWatch(map, key, watch) {
|
|
308
|
-
let set = map.get(key);
|
|
309
|
-
if (!set) {
|
|
310
|
-
set = /* @__PURE__ */ new Set();
|
|
311
|
-
map.set(key, set);
|
|
312
|
-
}
|
|
313
|
-
set.add(watch);
|
|
314
|
-
}
|
|
315
|
-
function removeWatch(map, key, watch) {
|
|
316
|
-
const set = map.get(key);
|
|
317
|
-
if (!set) return;
|
|
318
|
-
set.delete(watch);
|
|
319
|
-
if (set.size === 0) map.delete(key);
|
|
320
|
-
}
|
|
321
370
|
//#endregion
|
|
322
371
|
export { ReactiveEventStore as t };
|
|
323
372
|
|
|
324
|
-
//# sourceMappingURL=reactive-
|
|
373
|
+
//# sourceMappingURL=reactive-BJRoVHxU.mjs.map
|