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