@qntx/nostr 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +25 -0
- package/dist/builder-BphJa-Y-.d.mts +67 -0
- package/dist/builder-q0VsvnYE.mjs +158 -0
- package/dist/builder-q0VsvnYE.mjs.map +1 -0
- package/dist/client-Bd8B1mYm.mjs +835 -0
- package/dist/client-Bd8B1mYm.mjs.map +1 -0
- package/dist/client.d.mts +2 -0
- package/dist/client.mjs +2 -0
- package/dist/core.d.mts +8 -0
- package/dist/core.mjs +9 -0
- package/dist/error-B49rbDzT.d.mts +7 -0
- package/dist/error-CwBxle6N.mjs +13 -0
- package/dist/error-CwBxle6N.mjs.map +1 -0
- package/dist/error-DZEs5OuZ.mjs +24 -0
- package/dist/error-DZEs5OuZ.mjs.map +1 -0
- package/dist/error-WEFS-R-4.d.mts +20 -0
- package/dist/event-B5D2jxt_.d.mts +108 -0
- package/dist/event-CzoQEFMF.mjs +188 -0
- package/dist/event-CzoQEFMF.mjs.map +1 -0
- package/dist/filter-BxwjEkZp.mjs +113 -0
- package/dist/filter-BxwjEkZp.mjs.map +1 -0
- package/dist/filter-DUEKP3q1.d.mts +40 -0
- package/dist/gossip-3xkRRCm_.mjs +220 -0
- package/dist/gossip-3xkRRCm_.mjs.map +1 -0
- package/dist/gossip-C5RVB1UB.d.mts +71 -0
- package/dist/gossip.d.mts +3 -0
- package/dist/gossip.mjs +2 -0
- package/dist/http-6vevbBlu.d.mts +22 -0
- package/dist/http-D2rhokZo.mjs +24 -0
- package/dist/http-D2rhokZo.mjs.map +1 -0
- package/dist/index-BYMnhRIC.d.mts +161 -0
- package/dist/index-C0C4e-Gn.d.mts +55 -0
- package/dist/index-CaxrMD1A.d.mts +1 -0
- package/dist/index-DaRyjo7O.d.mts +77 -0
- package/dist/index-DgOugrpF.d.mts +275 -0
- package/dist/index-EwL6fHLo.d.mts +267 -0
- package/dist/index-k2YhCgsh.d.mts +57 -0
- package/dist/index.d.mts +22 -0
- package/dist/index.mjs +23 -0
- package/dist/key-BiGxGLhg.d.mts +37 -0
- package/dist/key-Dn-aX4x4.mjs +130 -0
- package/dist/key-Dn-aX4x4.mjs.map +1 -0
- package/dist/kind-BLl4cLOc.mjs +59 -0
- package/dist/kind-BLl4cLOc.mjs.map +1 -0
- package/dist/loaders-BNQszDAn.mjs +767 -0
- package/dist/loaders-BNQszDAn.mjs.map +1 -0
- package/dist/loaders.d.mts +3 -0
- package/dist/loaders.mjs +2 -0
- package/dist/memory-BCmhZPek.mjs +461 -0
- package/dist/memory-BCmhZPek.mjs.map +1 -0
- package/dist/message-C_Uj-Y-c.mjs +179 -0
- package/dist/message-C_Uj-Y-c.mjs.map +1 -0
- package/dist/message-DJC-Jz6T.d.mts +32 -0
- package/dist/nip17-BWq-Kfq5.d.mts +38 -0
- package/dist/nip19-D7905oIv.d.mts +63 -0
- package/dist/nip46-C_DoxiS_.d.mts +51 -0
- package/dist/nip59-DljW_sYh.d.mts +76 -0
- package/dist/nip65-Di5hZRuV.d.mts +21 -0
- package/dist/nip77-ByxlgoQO.d.mts +55 -0
- package/dist/nips/blossom.d.mts +93 -0
- package/dist/nips/blossom.mjs +325 -0
- package/dist/nips/blossom.mjs.map +1 -0
- package/dist/nips/nip04.d.mts +8 -0
- package/dist/nips/nip04.mjs +41 -0
- package/dist/nips/nip04.mjs.map +1 -0
- package/dist/nips/nip05.d.mts +89 -0
- package/dist/nips/nip05.mjs +147 -0
- package/dist/nips/nip05.mjs.map +1 -0
- package/dist/nips/nip10.d.mts +43 -0
- package/dist/nips/nip10.mjs +194 -0
- package/dist/nips/nip10.mjs.map +1 -0
- package/dist/nips/nip11.d.mts +43 -0
- package/dist/nips/nip11.mjs +99 -0
- package/dist/nips/nip11.mjs.map +1 -0
- package/dist/nips/nip13.d.mts +23 -0
- package/dist/nips/nip13.mjs +88 -0
- package/dist/nips/nip13.mjs.map +1 -0
- package/dist/nips/nip17.d.mts +2 -0
- package/dist/nips/nip17.mjs +138 -0
- package/dist/nips/nip17.mjs.map +1 -0
- package/dist/nips/nip19.d.mts +2 -0
- package/dist/nips/nip19.mjs +174 -0
- package/dist/nips/nip19.mjs.map +1 -0
- package/dist/nips/nip21.d.mts +17 -0
- package/dist/nips/nip21.mjs +29 -0
- package/dist/nips/nip21.mjs.map +1 -0
- package/dist/nips/nip27.d.mts +42 -0
- package/dist/nips/nip27.mjs +175 -0
- package/dist/nips/nip27.mjs.map +1 -0
- package/dist/nips/nip42.d.mts +12 -0
- package/dist/nips/nip42.mjs +22 -0
- package/dist/nips/nip42.mjs.map +1 -0
- package/dist/nips/nip44.d.mts +12 -0
- package/dist/nips/nip44.mjs +125 -0
- package/dist/nips/nip44.mjs.map +1 -0
- package/dist/nips/nip46.d.mts +2 -0
- package/dist/nips/nip46.mjs +132 -0
- package/dist/nips/nip46.mjs.map +1 -0
- package/dist/nips/nip49.d.mts +12 -0
- package/dist/nips/nip49.mjs +80 -0
- package/dist/nips/nip49.mjs.map +1 -0
- package/dist/nips/nip51.d.mts +88 -0
- package/dist/nips/nip51.mjs +246 -0
- package/dist/nips/nip51.mjs.map +1 -0
- package/dist/nips/nip57.d.mts +43 -0
- package/dist/nips/nip57.mjs +230 -0
- package/dist/nips/nip57.mjs.map +1 -0
- package/dist/nips/nip59.d.mts +2 -0
- package/dist/nips/nip59.mjs +171 -0
- package/dist/nips/nip59.mjs.map +1 -0
- package/dist/nips/nip65.d.mts +2 -0
- package/dist/nips/nip65.mjs +76 -0
- package/dist/nips/nip65.mjs.map +1 -0
- package/dist/nips/nip77.d.mts +2 -0
- package/dist/nips/nip77.mjs +421 -0
- package/dist/nips/nip77.mjs.map +1 -0
- package/dist/nips/nip96.d.mts +31 -0
- package/dist/nips/nip96.mjs +95 -0
- package/dist/nips/nip96.mjs.map +1 -0
- package/dist/nips/nip98.d.mts +28 -0
- package/dist/nips/nip98.mjs +91 -0
- package/dist/nips/nip98.mjs.map +1 -0
- package/dist/nostr_crypto_wasm_bg-hi5aXi5P.mjs +4 -0
- package/dist/nostr_crypto_wasm_bg.wasm +0 -0
- package/dist/pool-C_j1OmBB.mjs +1881 -0
- package/dist/pool-C_j1OmBB.mjs.map +1 -0
- package/dist/pool-DhRi7l-b.d.mts +297 -0
- package/dist/relay.d.mts +4 -0
- package/dist/relay.mjs +2 -0
- package/dist/signer-CZ6K6FUc.mjs +473 -0
- package/dist/signer-CZ6K6FUc.mjs.map +1 -0
- package/dist/signer.d.mts +3 -0
- package/dist/signer.mjs +2 -0
- package/dist/storage-DOy0iLRE.mjs +870 -0
- package/dist/storage-DOy0iLRE.mjs.map +1 -0
- package/dist/storage.d.mts +4 -0
- package/dist/storage.mjs +4 -0
- package/dist/types-BzyADqnw.d.mts +19 -0
- package/dist/types-Dn24TPFs.d.mts +43 -0
- package/dist/util-6tfLQ9JF.mjs +73 -0
- package/dist/util-6tfLQ9JF.mjs.map +1 -0
- package/dist/wasm.d.mts +19 -0
- package/dist/wasm.mjs +237 -0
- package/dist/wasm.mjs.map +1 -0
- package/package.json +204 -0
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
import { a as isHex32, s as normalizeURL } from "./util-6tfLQ9JF.mjs";
|
|
2
|
+
import { a as NostrError } from "./error-DZEs5OuZ.mjs";
|
|
3
|
+
import { r as isAddressableKind, t as Kind } from "./kind-BLl4cLOc.mjs";
|
|
4
|
+
import { _ as getDTag, d as sortedEvents, i as isReplaceableWinner } from "./event-CzoQEFMF.mjs";
|
|
5
|
+
import { Nip19Error, decode, npubEncode } from "./nips/nip19.mjs";
|
|
6
|
+
import { parseRelayList } from "./nips/nip65.mjs";
|
|
7
|
+
import { parseDmRelayList } from "./nips/nip17.mjs";
|
|
8
|
+
import { n as toStorageError } from "./error-CwBxle6N.mjs";
|
|
9
|
+
//#region src/loaders/cache.ts
|
|
10
|
+
function keyOf(spec) {
|
|
11
|
+
return `${spec.kind}:${spec.pubkey.toLowerCase()}:${spec.dTag ?? ""}`;
|
|
12
|
+
}
|
|
13
|
+
/** In-memory replaceable-event cache used by loaders (no DOM / localStorage). */
|
|
14
|
+
var ReplaceableCache = class {
|
|
15
|
+
#events = /* @__PURE__ */ new Map();
|
|
16
|
+
get(spec) {
|
|
17
|
+
return this.#events.get(keyOf(spec));
|
|
18
|
+
}
|
|
19
|
+
set(spec, event, fetchedAt = Math.floor(Date.now() / 1e3)) {
|
|
20
|
+
this.#events.set(keyOf(spec), {
|
|
21
|
+
event,
|
|
22
|
+
fetchedAt
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/** Keep newer event by created_at. */
|
|
26
|
+
putIfNewer(event, dTag) {
|
|
27
|
+
const spec = {
|
|
28
|
+
kind: event.kind,
|
|
29
|
+
pubkey: event.pubkey,
|
|
30
|
+
dTag
|
|
31
|
+
};
|
|
32
|
+
const prev = this.get(spec);
|
|
33
|
+
if (!prev?.event || isReplaceableWinner(event, prev.event)) this.set(spec, event);
|
|
34
|
+
}
|
|
35
|
+
clear(spec) {
|
|
36
|
+
if (spec) this.#events.delete(keyOf(spec));
|
|
37
|
+
else this.#events.clear();
|
|
38
|
+
}
|
|
39
|
+
get size() {
|
|
40
|
+
return this.#events.size;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/loaders/context.ts
|
|
45
|
+
/**
|
|
46
|
+
* Explicit dependency bag for loaders — never a module-level singleton.
|
|
47
|
+
*/
|
|
48
|
+
var LoaderContext = class {
|
|
49
|
+
pool;
|
|
50
|
+
cache;
|
|
51
|
+
staleAfterSec;
|
|
52
|
+
fetchTimeoutMs;
|
|
53
|
+
#relays;
|
|
54
|
+
constructor(opts) {
|
|
55
|
+
this.pool = opts.pool;
|
|
56
|
+
this.#relays = [...opts.relays];
|
|
57
|
+
this.cache = opts.cache ?? new ReplaceableCache();
|
|
58
|
+
this.staleAfterSec = opts.staleAfterSec ?? 172800;
|
|
59
|
+
this.fetchTimeoutMs = opts.fetchTimeoutMs ?? 4400;
|
|
60
|
+
}
|
|
61
|
+
/** Snapshot of discovery/fallback relays. */
|
|
62
|
+
get relays() {
|
|
63
|
+
return this.#relays;
|
|
64
|
+
}
|
|
65
|
+
addRelay(url) {
|
|
66
|
+
if (!this.#relays.includes(url)) this.#relays.push(url);
|
|
67
|
+
}
|
|
68
|
+
removeRelay(url) {
|
|
69
|
+
this.#relays = this.#relays.filter((r) => r !== url);
|
|
70
|
+
}
|
|
71
|
+
setRelays(urls) {
|
|
72
|
+
this.#relays = [...urls];
|
|
73
|
+
}
|
|
74
|
+
isFresh(fetchedAt, now = Math.floor(Date.now() / 1e3)) {
|
|
75
|
+
return now - fetchedAt < this.staleAfterSec;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/loaders/event.ts
|
|
80
|
+
function dFilter(kind, identifier) {
|
|
81
|
+
if (isAddressableKind(kind)) return { "#d": [identifier] };
|
|
82
|
+
return {};
|
|
83
|
+
}
|
|
84
|
+
function parseRef(ref) {
|
|
85
|
+
if (typeof ref === "string") {
|
|
86
|
+
if (isHex32(ref)) return {
|
|
87
|
+
filter: { ids: [ref.toLowerCase()] },
|
|
88
|
+
hints: [],
|
|
89
|
+
cacheKey: `id:${ref.toLowerCase()}`
|
|
90
|
+
};
|
|
91
|
+
const decoded = decode(ref);
|
|
92
|
+
switch (decoded.type) {
|
|
93
|
+
case "note": return {
|
|
94
|
+
filter: { ids: [decoded.data] },
|
|
95
|
+
hints: [],
|
|
96
|
+
cacheKey: `id:${decoded.data}`
|
|
97
|
+
};
|
|
98
|
+
case "nevent": return {
|
|
99
|
+
filter: { ids: [decoded.data.id] },
|
|
100
|
+
hints: decoded.data.relays ?? [],
|
|
101
|
+
cacheKey: `id:${decoded.data.id}`
|
|
102
|
+
};
|
|
103
|
+
case "naddr": return {
|
|
104
|
+
filter: {
|
|
105
|
+
authors: [decoded.data.pubkey.toLowerCase()],
|
|
106
|
+
kinds: [decoded.data.kind],
|
|
107
|
+
...dFilter(decoded.data.kind, decoded.data.identifier)
|
|
108
|
+
},
|
|
109
|
+
hints: decoded.data.relays ?? [],
|
|
110
|
+
cacheKey: `addr:${decoded.data.kind}:${decoded.data.pubkey}:${decoded.data.identifier}`
|
|
111
|
+
};
|
|
112
|
+
default: throw new Nip19Error(`cannot load event from ${decoded.type}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if ("id" in ref) return {
|
|
116
|
+
filter: { ids: [ref.id.toLowerCase()] },
|
|
117
|
+
hints: ref.relays ?? [],
|
|
118
|
+
cacheKey: `id:${ref.id.toLowerCase()}`
|
|
119
|
+
};
|
|
120
|
+
return {
|
|
121
|
+
filter: {
|
|
122
|
+
authors: [ref.pubkey.toLowerCase()],
|
|
123
|
+
kinds: [ref.kind],
|
|
124
|
+
...dFilter(ref.kind, ref.identifier)
|
|
125
|
+
},
|
|
126
|
+
hints: ref.relays ?? [],
|
|
127
|
+
cacheKey: `addr:${ref.kind}:${ref.pubkey}:${ref.identifier}`
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function createEventLoader(ctx) {
|
|
131
|
+
const inflight = /* @__PURE__ */ new Map();
|
|
132
|
+
const cache = /* @__PURE__ */ new Map();
|
|
133
|
+
return {
|
|
134
|
+
load(ref) {
|
|
135
|
+
const parsed = parseRef(ref);
|
|
136
|
+
if (cache.has(parsed.cacheKey)) return Promise.resolve(cache.get(parsed.cacheKey));
|
|
137
|
+
const hit = inflight.get(parsed.cacheKey);
|
|
138
|
+
if (hit) return hit;
|
|
139
|
+
const p = (async () => {
|
|
140
|
+
const relays = [.../* @__PURE__ */ new Set([...parsed.hints, ...ctx.relays])];
|
|
141
|
+
if (relays.length === 0) return void 0;
|
|
142
|
+
const events = await ctx.pool.fetch(relays, [parsed.filter], { timeoutMs: ctx.fetchTimeoutMs });
|
|
143
|
+
let best;
|
|
144
|
+
for (const e of events) if (!best || isReplaceableWinner(e, best)) best = e;
|
|
145
|
+
cache.set(parsed.cacheKey, best);
|
|
146
|
+
return best;
|
|
147
|
+
})().finally(() => inflight.delete(parsed.cacheKey));
|
|
148
|
+
inflight.set(parsed.cacheKey, p);
|
|
149
|
+
return p;
|
|
150
|
+
},
|
|
151
|
+
clearAll() {
|
|
152
|
+
inflight.clear();
|
|
153
|
+
cache.clear();
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/loaders/dataloader.ts
|
|
159
|
+
var LoaderError = class extends NostrError {};
|
|
160
|
+
var DataLoader = class {
|
|
161
|
+
#batchLoadFn;
|
|
162
|
+
#cacheKeyFn;
|
|
163
|
+
#maxBatchSize;
|
|
164
|
+
#useCache;
|
|
165
|
+
#cache = /* @__PURE__ */ new Map();
|
|
166
|
+
#inflight = /* @__PURE__ */ new Map();
|
|
167
|
+
#queue = [];
|
|
168
|
+
#scheduled = false;
|
|
169
|
+
constructor(batchLoadFn, options = {}) {
|
|
170
|
+
this.#batchLoadFn = batchLoadFn;
|
|
171
|
+
this.#cacheKeyFn = options.cacheKeyFn ?? ((k) => k);
|
|
172
|
+
this.#maxBatchSize = options.maxBatchSize ?? Infinity;
|
|
173
|
+
this.#useCache = options.cache !== false;
|
|
174
|
+
}
|
|
175
|
+
load(key) {
|
|
176
|
+
const cacheKey = this.#cacheKeyFn(key);
|
|
177
|
+
if (this.#useCache) {
|
|
178
|
+
const cached = this.#cache.get(cacheKey);
|
|
179
|
+
if (cached) return cached;
|
|
180
|
+
}
|
|
181
|
+
const inflight = this.#inflight.get(cacheKey);
|
|
182
|
+
if (inflight) return inflight;
|
|
183
|
+
const promise = new Promise((resolve, reject) => {
|
|
184
|
+
this.#queue.push({
|
|
185
|
+
key,
|
|
186
|
+
resolve,
|
|
187
|
+
reject
|
|
188
|
+
});
|
|
189
|
+
if (this.#queue.length >= this.#maxBatchSize) this.#dispatch();
|
|
190
|
+
else if (!this.#scheduled) {
|
|
191
|
+
this.#scheduled = true;
|
|
192
|
+
queueMicrotask(() => this.#dispatch());
|
|
193
|
+
}
|
|
194
|
+
}).finally(() => {
|
|
195
|
+
this.#inflight.delete(cacheKey);
|
|
196
|
+
});
|
|
197
|
+
this.#inflight.set(cacheKey, promise);
|
|
198
|
+
if (this.#useCache) this.#cache.set(cacheKey, promise);
|
|
199
|
+
return promise;
|
|
200
|
+
}
|
|
201
|
+
clear(key) {
|
|
202
|
+
this.#cache.delete(this.#cacheKeyFn(key));
|
|
203
|
+
this.#inflight.delete(this.#cacheKeyFn(key));
|
|
204
|
+
}
|
|
205
|
+
clearAll() {
|
|
206
|
+
this.#cache.clear();
|
|
207
|
+
this.#inflight.clear();
|
|
208
|
+
}
|
|
209
|
+
#dispatch() {
|
|
210
|
+
this.#scheduled = false;
|
|
211
|
+
const batch = this.#queue.splice(0, this.#maxBatchSize);
|
|
212
|
+
if (batch.length === 0) return;
|
|
213
|
+
const keys = batch.map((b) => b.key);
|
|
214
|
+
this.#batchLoadFn(keys).then((values) => {
|
|
215
|
+
if (values.length !== keys.length) {
|
|
216
|
+
const err = new LoaderError(`DataLoader batch function must return array of length ${keys.length}, got ${values.length}`);
|
|
217
|
+
for (const item of batch) item.reject(err);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
for (let i = 0; i < batch.length; i++) {
|
|
221
|
+
const value = values[i];
|
|
222
|
+
if (value instanceof Error) batch[i].reject(value);
|
|
223
|
+
else batch[i].resolve(value);
|
|
224
|
+
}
|
|
225
|
+
}).catch((err) => {
|
|
226
|
+
for (const item of batch) item.reject(err);
|
|
227
|
+
});
|
|
228
|
+
if (this.#queue.length > 0) {
|
|
229
|
+
this.#scheduled = true;
|
|
230
|
+
queueMicrotask(() => this.#dispatch());
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/loaders/replaceable.ts
|
|
236
|
+
/**
|
|
237
|
+
* Batch-fetch replaceable events (kind + authors) via the pool, with cache.
|
|
238
|
+
* Durable cache is ReplaceableCache; DataLoader only coalesces in-flight requests.
|
|
239
|
+
*/
|
|
240
|
+
function createReplaceableLoader(ctx, kind) {
|
|
241
|
+
const loader = new DataLoader(async (keys) => {
|
|
242
|
+
const authors = [...new Set(keys.map((k) => k.pubkey))];
|
|
243
|
+
const hintRelays = [...new Set(keys.flatMap((k) => k.hints ?? []).concat(ctx.relays))];
|
|
244
|
+
const filter = {
|
|
245
|
+
kinds: [kind],
|
|
246
|
+
authors
|
|
247
|
+
};
|
|
248
|
+
const events = hintRelays.length > 0 ? await ctx.pool.fetch(hintRelays, [filter], { timeoutMs: ctx.fetchTimeoutMs }) : [];
|
|
249
|
+
const best = /* @__PURE__ */ new Map();
|
|
250
|
+
for (const event of events) {
|
|
251
|
+
if (event.kind !== kind) continue;
|
|
252
|
+
const prev = best.get(event.pubkey);
|
|
253
|
+
if (!prev || isReplaceableWinner(event, prev)) best.set(event.pubkey, event);
|
|
254
|
+
}
|
|
255
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
256
|
+
return keys.map((k) => {
|
|
257
|
+
const event = best.get(k.pubkey) ?? null;
|
|
258
|
+
ctx.cache.set({
|
|
259
|
+
kind,
|
|
260
|
+
pubkey: k.pubkey,
|
|
261
|
+
dTag: event ? getDTag(event.tags) ?? void 0 : void 0
|
|
262
|
+
}, event, now);
|
|
263
|
+
return event;
|
|
264
|
+
});
|
|
265
|
+
}, {
|
|
266
|
+
cache: false,
|
|
267
|
+
cacheKeyFn: (k) => `${kind}:${k.pubkey}:${(k.hints ?? []).join(",")}`,
|
|
268
|
+
maxBatchSize: 50
|
|
269
|
+
});
|
|
270
|
+
return {
|
|
271
|
+
async load(pubkey, opts) {
|
|
272
|
+
const pk = pubkey.toLowerCase();
|
|
273
|
+
const style = opts?.style ?? "default";
|
|
274
|
+
const cached = ctx.cache.get({
|
|
275
|
+
kind,
|
|
276
|
+
pubkey: pk
|
|
277
|
+
});
|
|
278
|
+
if (style === "cache-only") return {
|
|
279
|
+
event: cached?.event ?? null,
|
|
280
|
+
fresh: false
|
|
281
|
+
};
|
|
282
|
+
if (style === "default" && cached && ctx.isFresh(cached.fetchedAt)) return {
|
|
283
|
+
event: cached.event,
|
|
284
|
+
fresh: false
|
|
285
|
+
};
|
|
286
|
+
return {
|
|
287
|
+
event: await loader.load({
|
|
288
|
+
pubkey: pk,
|
|
289
|
+
hints: opts?.hints
|
|
290
|
+
}),
|
|
291
|
+
fresh: true
|
|
292
|
+
};
|
|
293
|
+
},
|
|
294
|
+
clear(pubkey) {
|
|
295
|
+
if (pubkey) {
|
|
296
|
+
const pk = pubkey.toLowerCase();
|
|
297
|
+
loader.clear({ pubkey: pk });
|
|
298
|
+
ctx.cache.clear({
|
|
299
|
+
kind,
|
|
300
|
+
pubkey: pk
|
|
301
|
+
});
|
|
302
|
+
} else loader.clearAll();
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
//#endregion
|
|
307
|
+
//#region src/loaders/lists.ts
|
|
308
|
+
function fromTags(event, map) {
|
|
309
|
+
if (!event) return [];
|
|
310
|
+
const out = [];
|
|
311
|
+
for (const tag of event.tags) {
|
|
312
|
+
const item = map(tag);
|
|
313
|
+
if (item !== void 0) out.push(item);
|
|
314
|
+
}
|
|
315
|
+
return out;
|
|
316
|
+
}
|
|
317
|
+
function createListLoaders(ctx) {
|
|
318
|
+
const followsLoader = createReplaceableLoader(ctx, Kind.Contacts);
|
|
319
|
+
const muteLoader = createReplaceableLoader(ctx, Kind.MuteList);
|
|
320
|
+
const relayListLoader = createReplaceableLoader(ctx, Kind.RelayList);
|
|
321
|
+
const dmRelayListLoader = createReplaceableLoader(ctx, Kind.DirectMessageRelaysList);
|
|
322
|
+
return {
|
|
323
|
+
async follows(pubkey, opts) {
|
|
324
|
+
const { event, fresh } = await followsLoader.load(pubkey, opts);
|
|
325
|
+
return {
|
|
326
|
+
event,
|
|
327
|
+
fresh,
|
|
328
|
+
items: fromTags(event, (tag) => tag[0] === "p" && tag[1] && isHex32(tag[1]) ? tag[1].toLowerCase() : void 0)
|
|
329
|
+
};
|
|
330
|
+
},
|
|
331
|
+
async muteList(pubkey, opts) {
|
|
332
|
+
const { event, fresh } = await muteLoader.load(pubkey, opts);
|
|
333
|
+
return {
|
|
334
|
+
event,
|
|
335
|
+
fresh,
|
|
336
|
+
items: fromTags(event, (tag) => {
|
|
337
|
+
if (!tag[1]) return void 0;
|
|
338
|
+
switch (tag[0]) {
|
|
339
|
+
case "p": return isHex32(tag[1]) ? {
|
|
340
|
+
label: "pubkey",
|
|
341
|
+
value: tag[1].toLowerCase()
|
|
342
|
+
} : void 0;
|
|
343
|
+
case "e": return isHex32(tag[1]) ? {
|
|
344
|
+
label: "thread",
|
|
345
|
+
value: tag[1].toLowerCase()
|
|
346
|
+
} : void 0;
|
|
347
|
+
case "t": return {
|
|
348
|
+
label: "hashtag",
|
|
349
|
+
value: tag[1]
|
|
350
|
+
};
|
|
351
|
+
case "word": return {
|
|
352
|
+
label: "word",
|
|
353
|
+
value: tag[1]
|
|
354
|
+
};
|
|
355
|
+
default: return;
|
|
356
|
+
}
|
|
357
|
+
})
|
|
358
|
+
};
|
|
359
|
+
},
|
|
360
|
+
async relayList(pubkey, opts) {
|
|
361
|
+
const { event, fresh } = await relayListLoader.load(pubkey, opts);
|
|
362
|
+
let items = [];
|
|
363
|
+
if (event) try {
|
|
364
|
+
items = parseRelayList(event);
|
|
365
|
+
} catch {
|
|
366
|
+
items = [];
|
|
367
|
+
}
|
|
368
|
+
items = items.map((i) => {
|
|
369
|
+
try {
|
|
370
|
+
return {
|
|
371
|
+
...i,
|
|
372
|
+
url: normalizeURL(i.url)
|
|
373
|
+
};
|
|
374
|
+
} catch {
|
|
375
|
+
return i;
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
return {
|
|
379
|
+
event,
|
|
380
|
+
fresh,
|
|
381
|
+
items
|
|
382
|
+
};
|
|
383
|
+
},
|
|
384
|
+
async dmRelayList(pubkey, opts) {
|
|
385
|
+
const { event, fresh } = await dmRelayListLoader.load(pubkey, opts);
|
|
386
|
+
let items = [];
|
|
387
|
+
if (event) try {
|
|
388
|
+
items = parseDmRelayList(event);
|
|
389
|
+
} catch {
|
|
390
|
+
items = [];
|
|
391
|
+
}
|
|
392
|
+
return {
|
|
393
|
+
event,
|
|
394
|
+
fresh,
|
|
395
|
+
items
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
//#endregion
|
|
401
|
+
//#region src/loaders/profile.ts
|
|
402
|
+
function bareNostrUser(pubkey) {
|
|
403
|
+
const pk = pubkey.toLowerCase();
|
|
404
|
+
let npub;
|
|
405
|
+
try {
|
|
406
|
+
npub = npubEncode(pk);
|
|
407
|
+
} catch {
|
|
408
|
+
npub = pk;
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
pubkey: pk,
|
|
412
|
+
npub,
|
|
413
|
+
shortName: npub.startsWith("npub1") ? `${npub.slice(0, 8)}…${npub.slice(-4)}` : pk.slice(0, 8),
|
|
414
|
+
metadata: {},
|
|
415
|
+
lastUpdated: 0,
|
|
416
|
+
event: null,
|
|
417
|
+
fresh: false
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function parseMetadata(content) {
|
|
421
|
+
try {
|
|
422
|
+
const obj = JSON.parse(content);
|
|
423
|
+
return typeof obj === "object" && obj !== null ? obj : {};
|
|
424
|
+
} catch {
|
|
425
|
+
return {};
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
function createProfileLoader(ctx) {
|
|
429
|
+
const loader = createReplaceableLoader(ctx, Kind.Metadata);
|
|
430
|
+
return { async load(pubkey, opts) {
|
|
431
|
+
const base = bareNostrUser(pubkey);
|
|
432
|
+
const { event, fresh } = await loader.load(pubkey, opts);
|
|
433
|
+
if (!event) return {
|
|
434
|
+
...base,
|
|
435
|
+
fresh
|
|
436
|
+
};
|
|
437
|
+
const metadata = parseMetadata(event.content);
|
|
438
|
+
const display = metadata.display_name || metadata.name;
|
|
439
|
+
return {
|
|
440
|
+
...base,
|
|
441
|
+
shortName: display || base.shortName,
|
|
442
|
+
image: metadata.picture,
|
|
443
|
+
metadata,
|
|
444
|
+
lastUpdated: event.created_at,
|
|
445
|
+
event,
|
|
446
|
+
fresh
|
|
447
|
+
};
|
|
448
|
+
} };
|
|
449
|
+
}
|
|
450
|
+
//#endregion
|
|
451
|
+
//#region src/loaders/outbox.ts
|
|
452
|
+
var OutboxError = class extends NostrError {};
|
|
453
|
+
function boundKey(pubkey, kind) {
|
|
454
|
+
return `${pubkey.toLowerCase()}:${kind}`;
|
|
455
|
+
}
|
|
456
|
+
/** Same skip/dedup as Gossip.setRoutes so prefer can match Client.relays. */
|
|
457
|
+
function canonicalRelayUrls(urls) {
|
|
458
|
+
const out = [];
|
|
459
|
+
for (const raw of urls) try {
|
|
460
|
+
const url = normalizeURL(raw);
|
|
461
|
+
if (!out.includes(url)) out.push(url);
|
|
462
|
+
} catch {}
|
|
463
|
+
return out;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Group authors by outbox relay (discovery fallback).
|
|
467
|
+
* `prefer` reorders existing candidates only; it never appends a URL
|
|
468
|
+
* that is not already in the author's outbox or discovery list.
|
|
469
|
+
* Returns Map<relayUrl, authors[]>.
|
|
470
|
+
*/
|
|
471
|
+
function groupAuthorsByOutboxRelay(authors, gossip, discoveryRelays, maxRelaysPerAuthor = 3, prefer = []) {
|
|
472
|
+
const leftover = new Set((gossip.route({ authors: authors.map((a) => a.toLowerCase()) }).remainder?.authors ?? []).map((a) => a.toLowerCase()));
|
|
473
|
+
const preferSet = new Set(canonicalRelayUrls(prefer));
|
|
474
|
+
const discovery = canonicalRelayUrls(discoveryRelays);
|
|
475
|
+
const map = /* @__PURE__ */ new Map();
|
|
476
|
+
for (const author of authors) {
|
|
477
|
+
const pk = author.toLowerCase();
|
|
478
|
+
const urls = leftover.has(pk) ? discovery : gossip.outboxRelays(pk);
|
|
479
|
+
const preferred = [];
|
|
480
|
+
const rest = [];
|
|
481
|
+
for (const url of urls) if (preferSet.has(url)) preferred.push(url);
|
|
482
|
+
else rest.push(url);
|
|
483
|
+
for (const url of preferred.concat(rest).slice(0, maxRelaysPerAuthor)) {
|
|
484
|
+
const list = map.get(url) ?? [];
|
|
485
|
+
if (!list.includes(pk)) list.push(pk);
|
|
486
|
+
map.set(url, list);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return map;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Outbox-model feed: history sync + live subscription for a set of authors,
|
|
493
|
+
* routed via NIP-65 write relays (with discovery fallback).
|
|
494
|
+
*/
|
|
495
|
+
var OutboxFeed = class {
|
|
496
|
+
#pool;
|
|
497
|
+
#gossip;
|
|
498
|
+
#storage;
|
|
499
|
+
#discovery;
|
|
500
|
+
#kinds;
|
|
501
|
+
#maxRelays;
|
|
502
|
+
#timeoutMs;
|
|
503
|
+
#onEvent;
|
|
504
|
+
#observe;
|
|
505
|
+
#applySync;
|
|
506
|
+
#hydrate;
|
|
507
|
+
#bounds = /* @__PURE__ */ new Map();
|
|
508
|
+
#authors;
|
|
509
|
+
#liveCloser;
|
|
510
|
+
#closed = false;
|
|
511
|
+
constructor(opts) {
|
|
512
|
+
this.#pool = opts.pool;
|
|
513
|
+
this.#gossip = opts.gossip;
|
|
514
|
+
this.#storage = opts.storage;
|
|
515
|
+
this.#discovery = [...opts.discoveryRelays];
|
|
516
|
+
this.#authors = [...new Set(opts.authors.map((a) => a.toLowerCase()))];
|
|
517
|
+
this.#kinds = [...opts.kinds ?? [Kind.TextNote]];
|
|
518
|
+
this.#maxRelays = opts.maxRelaysPerAuthor ?? 3;
|
|
519
|
+
this.#timeoutMs = opts.fetchTimeoutMs ?? 4400;
|
|
520
|
+
this.#onEvent = opts.onEvent;
|
|
521
|
+
this.#observe = opts.observe;
|
|
522
|
+
this.#applySync = opts.applySync;
|
|
523
|
+
this.#hydrate = opts.hydrate;
|
|
524
|
+
}
|
|
525
|
+
get authors() {
|
|
526
|
+
return this.#authors;
|
|
527
|
+
}
|
|
528
|
+
get kinds() {
|
|
529
|
+
return this.#kinds;
|
|
530
|
+
}
|
|
531
|
+
/** Replace the author set (does not auto-restart live). */
|
|
532
|
+
setAuthors(authors) {
|
|
533
|
+
this.#authors = [...new Set(authors.map((a) => a.toLowerCase()))];
|
|
534
|
+
}
|
|
535
|
+
getBound(pubkey, kind) {
|
|
536
|
+
return this.#bounds.get(boundKey(pubkey, kind));
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Load NIP-65 relay lists for authors that have no outbox routes yet.
|
|
540
|
+
*/
|
|
541
|
+
async hydrate() {
|
|
542
|
+
this.#assertOpen();
|
|
543
|
+
if (!this.#hydrate) return;
|
|
544
|
+
const missing = this.#authors.filter((pk) => this.#gossip.outboxRelays(pk).length === 0);
|
|
545
|
+
if (missing.length === 0) return;
|
|
546
|
+
await this.#hydrate(missing);
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* One-shot history pull. Persistence is `applySync`; bounds move only for events it returns.
|
|
550
|
+
*/
|
|
551
|
+
async sync(opts) {
|
|
552
|
+
this.#assertOpen();
|
|
553
|
+
if (!opts?.skipHydrate) await this.hydrate();
|
|
554
|
+
await this.#hydrateBoundsFromStore();
|
|
555
|
+
const byRelay = groupAuthorsByOutboxRelay(this.#authors, this.#gossip, this.#discovery, this.#maxRelays, this.#pool.connectedUrls());
|
|
556
|
+
if (byRelay.size === 0) return [];
|
|
557
|
+
const byId = /* @__PURE__ */ new Map();
|
|
558
|
+
await Promise.all([...byRelay.entries()].map(async ([url, authors]) => {
|
|
559
|
+
if (opts?.signal?.aborted) return;
|
|
560
|
+
const filters = this.#syncFilters(authors, opts);
|
|
561
|
+
try {
|
|
562
|
+
const batch = await this.#pool.fetch([url], filters, {
|
|
563
|
+
timeoutMs: this.#timeoutMs,
|
|
564
|
+
signal: opts?.signal
|
|
565
|
+
});
|
|
566
|
+
for (const event of batch) byId.set(event.id, event);
|
|
567
|
+
} catch {}
|
|
568
|
+
}));
|
|
569
|
+
const unique = [...byId.values()];
|
|
570
|
+
if (unique.length === 0) return [];
|
|
571
|
+
let applied = [];
|
|
572
|
+
if (this.#applySync) try {
|
|
573
|
+
applied = await this.#applySync(unique);
|
|
574
|
+
} catch (err) {
|
|
575
|
+
throw toStorageError(err);
|
|
576
|
+
}
|
|
577
|
+
const dirty = /* @__PURE__ */ new Set();
|
|
578
|
+
for (const event of applied) {
|
|
579
|
+
this.#updateBounds(event);
|
|
580
|
+
dirty.add(boundKey(event.pubkey, event.kind));
|
|
581
|
+
this.#onEvent?.(event);
|
|
582
|
+
}
|
|
583
|
+
await this.#persistBounds(dirty);
|
|
584
|
+
return sortedEvents(unique);
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Start a live subscription across outbox relays for the current authors.
|
|
588
|
+
* Closes any previous live subscription.
|
|
589
|
+
*/
|
|
590
|
+
startLive(opts) {
|
|
591
|
+
this.#assertOpen();
|
|
592
|
+
this.#liveCloser?.close("restart");
|
|
593
|
+
const byRelay = groupAuthorsByOutboxRelay(this.#authors, this.#gossip, this.#discovery, this.#maxRelays, this.#pool.connectedUrls());
|
|
594
|
+
const seen = /* @__PURE__ */ new Set();
|
|
595
|
+
const closers = [];
|
|
596
|
+
const since = opts?.since ?? Math.floor(Date.now() / 1e3) - 60;
|
|
597
|
+
for (const [url, authors] of byRelay) {
|
|
598
|
+
const filter = {
|
|
599
|
+
authors,
|
|
600
|
+
kinds: this.#kinds,
|
|
601
|
+
since
|
|
602
|
+
};
|
|
603
|
+
closers.push(this.#pool.subscribe([url], [filter], {
|
|
604
|
+
signal: opts?.signal,
|
|
605
|
+
onevent: (event) => {
|
|
606
|
+
if (seen.has(event.id)) return;
|
|
607
|
+
seen.add(event.id);
|
|
608
|
+
this.#noteEvent(event);
|
|
609
|
+
}
|
|
610
|
+
}));
|
|
611
|
+
}
|
|
612
|
+
const closer = { close: (reason) => {
|
|
613
|
+
for (const c of closers) c.close(reason);
|
|
614
|
+
if (this.#liveCloser === closer) this.#liveCloser = void 0;
|
|
615
|
+
} };
|
|
616
|
+
this.#liveCloser = closer;
|
|
617
|
+
return closer;
|
|
618
|
+
}
|
|
619
|
+
/** Convenience: hydrate + sync + startLive. */
|
|
620
|
+
async start(opts) {
|
|
621
|
+
const events = await this.sync({
|
|
622
|
+
limit: opts?.limit,
|
|
623
|
+
signal: opts?.signal
|
|
624
|
+
});
|
|
625
|
+
const live = this.startLive({ signal: opts?.signal });
|
|
626
|
+
return {
|
|
627
|
+
close: (r) => live.close(r),
|
|
628
|
+
events
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
close() {
|
|
632
|
+
this.#closed = true;
|
|
633
|
+
this.#liveCloser?.close("feed closed");
|
|
634
|
+
this.#liveCloser = void 0;
|
|
635
|
+
}
|
|
636
|
+
#assertOpen() {
|
|
637
|
+
if (this.#closed) throw new OutboxError("OutboxFeed is closed");
|
|
638
|
+
}
|
|
639
|
+
async #hydrateBoundsFromStore() {
|
|
640
|
+
await Promise.all(this.#authors.flatMap((pk) => this.#kinds.map(async (kind) => {
|
|
641
|
+
const key = boundKey(pk, kind);
|
|
642
|
+
if (this.#bounds.has(key)) return;
|
|
643
|
+
const bound = await this.#storage.getOutboxBound(pk, kind);
|
|
644
|
+
if (!bound) return;
|
|
645
|
+
await this.#storage.setOutboxBound(pk, kind, bound);
|
|
646
|
+
this.#bounds.set(key, {
|
|
647
|
+
oldest: bound.oldest,
|
|
648
|
+
newest: bound.newest
|
|
649
|
+
});
|
|
650
|
+
})));
|
|
651
|
+
}
|
|
652
|
+
#syncFilters(authors, opts) {
|
|
653
|
+
const base = {
|
|
654
|
+
kinds: this.#kinds,
|
|
655
|
+
limit: opts?.limit ?? 50,
|
|
656
|
+
...opts?.until !== void 0 ? { until: opts.until } : {}
|
|
657
|
+
};
|
|
658
|
+
if (opts?.since !== void 0) return [{
|
|
659
|
+
...base,
|
|
660
|
+
authors,
|
|
661
|
+
since: opts.since
|
|
662
|
+
}];
|
|
663
|
+
const bounded = [];
|
|
664
|
+
const unbounded = [];
|
|
665
|
+
let minBoundedNewest;
|
|
666
|
+
for (const pk of authors) {
|
|
667
|
+
let newest;
|
|
668
|
+
let complete = true;
|
|
669
|
+
for (const kind of this.#kinds) {
|
|
670
|
+
const b = this.#bounds.get(boundKey(pk, kind));
|
|
671
|
+
if (!b) {
|
|
672
|
+
complete = false;
|
|
673
|
+
break;
|
|
674
|
+
}
|
|
675
|
+
if (newest === void 0 || b.newest < newest) newest = b.newest;
|
|
676
|
+
}
|
|
677
|
+
if (complete && newest !== void 0) {
|
|
678
|
+
bounded.push(pk);
|
|
679
|
+
if (minBoundedNewest === void 0 || newest < minBoundedNewest) minBoundedNewest = newest;
|
|
680
|
+
} else unbounded.push(pk);
|
|
681
|
+
}
|
|
682
|
+
if (bounded.length === 0 || minBoundedNewest === void 0) return [{
|
|
683
|
+
...base,
|
|
684
|
+
authors
|
|
685
|
+
}];
|
|
686
|
+
if (unbounded.length === 0) return [{
|
|
687
|
+
...base,
|
|
688
|
+
authors,
|
|
689
|
+
since: Math.max(0, minBoundedNewest - 1)
|
|
690
|
+
}];
|
|
691
|
+
return [{
|
|
692
|
+
...base,
|
|
693
|
+
authors: bounded,
|
|
694
|
+
since: Math.max(0, minBoundedNewest - 1)
|
|
695
|
+
}, {
|
|
696
|
+
...base,
|
|
697
|
+
authors: unbounded
|
|
698
|
+
}];
|
|
699
|
+
}
|
|
700
|
+
#noteEvent(event) {
|
|
701
|
+
this.#updateBounds(event);
|
|
702
|
+
const bound = this.#bounds.get(boundKey(event.pubkey, event.kind));
|
|
703
|
+
if (bound) this.#storage.setOutboxBound(event.pubkey, event.kind, {
|
|
704
|
+
oldest: bound.oldest,
|
|
705
|
+
newest: bound.newest
|
|
706
|
+
}).catch(() => {});
|
|
707
|
+
this.#observe?.(event);
|
|
708
|
+
this.#onEvent?.(event);
|
|
709
|
+
}
|
|
710
|
+
async #persistBounds(keys) {
|
|
711
|
+
const writes = [];
|
|
712
|
+
for (const key of keys) {
|
|
713
|
+
const bound = this.#bounds.get(key);
|
|
714
|
+
if (!bound) continue;
|
|
715
|
+
const sep = key.lastIndexOf(":");
|
|
716
|
+
writes.push(this.#storage.setOutboxBound(key.slice(0, sep), Number(key.slice(sep + 1)), {
|
|
717
|
+
oldest: bound.oldest,
|
|
718
|
+
newest: bound.newest
|
|
719
|
+
}));
|
|
720
|
+
}
|
|
721
|
+
if (writes.length === 0) return;
|
|
722
|
+
try {
|
|
723
|
+
await Promise.all(writes);
|
|
724
|
+
} catch (err) {
|
|
725
|
+
throw toStorageError(err);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
#updateBounds(event) {
|
|
729
|
+
const key = boundKey(event.pubkey, event.kind);
|
|
730
|
+
const prev = this.#bounds.get(key);
|
|
731
|
+
if (!prev) {
|
|
732
|
+
this.#bounds.set(key, {
|
|
733
|
+
oldest: event.created_at,
|
|
734
|
+
newest: event.created_at
|
|
735
|
+
});
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
if (event.created_at < prev.oldest) prev.oldest = event.created_at;
|
|
739
|
+
if (event.created_at > prev.newest) prev.newest = event.created_at;
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
/** Factory matching other loaders helpers. */
|
|
743
|
+
function createOutboxFeed(opts) {
|
|
744
|
+
return new OutboxFeed(opts);
|
|
745
|
+
}
|
|
746
|
+
//#endregion
|
|
747
|
+
//#region src/loaders/index.ts
|
|
748
|
+
/** Build an instance-scoped loader suite (no module globals). */
|
|
749
|
+
function createLoaders(opts) {
|
|
750
|
+
const context = new LoaderContext(opts);
|
|
751
|
+
const lists = createListLoaders(context);
|
|
752
|
+
const profile = createProfileLoader(context);
|
|
753
|
+
const event = createEventLoader(context);
|
|
754
|
+
return {
|
|
755
|
+
context,
|
|
756
|
+
follows: (pubkey, o) => lists.follows(pubkey, o),
|
|
757
|
+
muteList: (pubkey, o) => lists.muteList(pubkey, o),
|
|
758
|
+
relayList: (pubkey, o) => lists.relayList(pubkey, o),
|
|
759
|
+
dmRelayList: (pubkey, o) => lists.dmRelayList(pubkey, o),
|
|
760
|
+
profile: (pubkey, o) => profile.load(pubkey, o),
|
|
761
|
+
event: (ref) => event.load(ref)
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
//#endregion
|
|
765
|
+
export { groupAuthorsByOutboxRelay as a, createListLoaders as c, LoaderError as d, createEventLoader as f, createOutboxFeed as i, createReplaceableLoader as l, ReplaceableCache as m, OutboxError as n, bareNostrUser as o, LoaderContext as p, OutboxFeed as r, createProfileLoader as s, createLoaders as t, DataLoader as u };
|
|
766
|
+
|
|
767
|
+
//# sourceMappingURL=loaders-BNQszDAn.mjs.map
|