@kehto/services 0.5.0 → 0.6.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/cvm-nostr-transport.d.ts +1 -1
- package/dist/cvm-nostr-transport.js.map +1 -1
- package/dist/index.d.ts +266 -2
- package/dist/index.js +430 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1854,6 +1854,430 @@ function createResourceService(options) {
|
|
|
1854
1854
|
return handler;
|
|
1855
1855
|
}
|
|
1856
1856
|
|
|
1857
|
+
// src/outbox-service.ts
|
|
1858
|
+
var OUTBOX_SERVICE_VERSION = "1.0.0";
|
|
1859
|
+
var OUTBOX_DESCRIPTOR = {
|
|
1860
|
+
name: "outbox",
|
|
1861
|
+
version: OUTBOX_SERVICE_VERSION,
|
|
1862
|
+
description: "NAP-OUTBOX outbox-aware relay routing \u2014 query/subscribe/publish/resolveRelays"
|
|
1863
|
+
};
|
|
1864
|
+
function normalizeFilters(raw) {
|
|
1865
|
+
if (Array.isArray(raw)) {
|
|
1866
|
+
const filters = raw.filter((f) => typeof f === "object" && f !== null);
|
|
1867
|
+
return filters.length > 0 ? filters : null;
|
|
1868
|
+
}
|
|
1869
|
+
if (typeof raw === "object" && raw !== null) return [raw];
|
|
1870
|
+
return null;
|
|
1871
|
+
}
|
|
1872
|
+
function createOutboxService(options) {
|
|
1873
|
+
if (!options || typeof options.router !== "object" || options.router === null) {
|
|
1874
|
+
throw new Error("createOutboxService: options.router is required");
|
|
1875
|
+
}
|
|
1876
|
+
const { router } = options;
|
|
1877
|
+
const subscriptions = /* @__PURE__ */ new Map();
|
|
1878
|
+
function handleQuery(msg, send) {
|
|
1879
|
+
const m = msg;
|
|
1880
|
+
const id = m.id ?? "";
|
|
1881
|
+
const filters = normalizeFilters(m.filters);
|
|
1882
|
+
if (!filters) {
|
|
1883
|
+
send({ type: "outbox.query.result", id, events: [], relays: {}, error: "invalid filter" });
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1886
|
+
void router.query(filters, m.options).then(
|
|
1887
|
+
(result) => send({
|
|
1888
|
+
type: "outbox.query.result",
|
|
1889
|
+
id,
|
|
1890
|
+
events: result.events,
|
|
1891
|
+
relays: result.relays,
|
|
1892
|
+
...result.incomplete === void 0 ? {} : { incomplete: result.incomplete },
|
|
1893
|
+
...result.error === void 0 ? {} : { error: result.error }
|
|
1894
|
+
})
|
|
1895
|
+
).catch(
|
|
1896
|
+
(err) => send({ type: "outbox.query.result", id, events: [], relays: {}, error: toErrorMessage(err) })
|
|
1897
|
+
);
|
|
1898
|
+
}
|
|
1899
|
+
function handleSubscribe(windowId, msg, send) {
|
|
1900
|
+
const m = msg;
|
|
1901
|
+
const subId = m.subId;
|
|
1902
|
+
if (typeof subId !== "string" || subId.length === 0) return;
|
|
1903
|
+
const subKey = `${windowId}:${subId}`;
|
|
1904
|
+
subscriptions.get(subKey)?.close();
|
|
1905
|
+
subscriptions.delete(subKey);
|
|
1906
|
+
const filters = normalizeFilters(m.filters);
|
|
1907
|
+
if (!filters) {
|
|
1908
|
+
send({ type: "outbox.closed", subId, reason: "invalid filter" });
|
|
1909
|
+
return;
|
|
1910
|
+
}
|
|
1911
|
+
const sink = {
|
|
1912
|
+
event: (event, relay) => send({ type: "outbox.event", subId, event, ...relay === void 0 ? {} : { relay } }),
|
|
1913
|
+
eose: () => send({ type: "outbox.eose", subId }),
|
|
1914
|
+
closed: (reason) => {
|
|
1915
|
+
subscriptions.delete(subKey);
|
|
1916
|
+
send({ type: "outbox.closed", subId, ...reason === void 0 ? {} : { reason } });
|
|
1917
|
+
}
|
|
1918
|
+
};
|
|
1919
|
+
subscriptions.set(subKey, router.subscribe(filters, m.options, sink));
|
|
1920
|
+
}
|
|
1921
|
+
function handleClose(windowId, msg, send) {
|
|
1922
|
+
const m = msg;
|
|
1923
|
+
const subId = m.subId;
|
|
1924
|
+
if (typeof subId !== "string") return;
|
|
1925
|
+
const subKey = `${windowId}:${subId}`;
|
|
1926
|
+
subscriptions.get(subKey)?.close();
|
|
1927
|
+
subscriptions.delete(subKey);
|
|
1928
|
+
send({ type: "outbox.closed", subId });
|
|
1929
|
+
}
|
|
1930
|
+
function handlePublish(msg, send) {
|
|
1931
|
+
const m = msg;
|
|
1932
|
+
const id = m.id ?? "";
|
|
1933
|
+
if (!m.event || typeof m.event !== "object") {
|
|
1934
|
+
send({ type: "outbox.publish.result", id, ok: false, error: "invalid filter" });
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1937
|
+
void router.publish(m.event, m.options).then(
|
|
1938
|
+
(result) => send({
|
|
1939
|
+
type: "outbox.publish.result",
|
|
1940
|
+
id,
|
|
1941
|
+
ok: result.ok,
|
|
1942
|
+
...result.event === void 0 ? {} : { event: result.event },
|
|
1943
|
+
...result.eventId === void 0 ? {} : { eventId: result.eventId },
|
|
1944
|
+
...result.relays === void 0 ? {} : { relays: result.relays },
|
|
1945
|
+
...result.error === void 0 ? {} : { error: result.error }
|
|
1946
|
+
})
|
|
1947
|
+
).catch(
|
|
1948
|
+
(err) => send({ type: "outbox.publish.result", id, ok: false, error: toErrorMessage(err) })
|
|
1949
|
+
);
|
|
1950
|
+
}
|
|
1951
|
+
function handleResolveRelays(msg, send) {
|
|
1952
|
+
const m = msg;
|
|
1953
|
+
const id = m.id ?? "";
|
|
1954
|
+
if (!m.target || typeof m.target !== "object") {
|
|
1955
|
+
send({ type: "outbox.resolveRelays.result", id, error: "invalid filter" });
|
|
1956
|
+
return;
|
|
1957
|
+
}
|
|
1958
|
+
void router.resolveRelays(m.target).then((plan) => send({ type: "outbox.resolveRelays.result", id, plan })).catch(
|
|
1959
|
+
(err) => send({ type: "outbox.resolveRelays.result", id, error: toErrorMessage(err) })
|
|
1960
|
+
);
|
|
1961
|
+
}
|
|
1962
|
+
return {
|
|
1963
|
+
descriptor: OUTBOX_DESCRIPTOR,
|
|
1964
|
+
handleMessage(windowId, message, send) {
|
|
1965
|
+
switch (message.type) {
|
|
1966
|
+
case "outbox.query":
|
|
1967
|
+
handleQuery(message, send);
|
|
1968
|
+
return;
|
|
1969
|
+
case "outbox.subscribe":
|
|
1970
|
+
handleSubscribe(windowId, message, send);
|
|
1971
|
+
return;
|
|
1972
|
+
case "outbox.close":
|
|
1973
|
+
handleClose(windowId, message, send);
|
|
1974
|
+
return;
|
|
1975
|
+
case "outbox.publish":
|
|
1976
|
+
handlePublish(message, send);
|
|
1977
|
+
return;
|
|
1978
|
+
case "outbox.resolveRelays":
|
|
1979
|
+
handleResolveRelays(message, send);
|
|
1980
|
+
return;
|
|
1981
|
+
default:
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
},
|
|
1985
|
+
onWindowDestroyed(windowId) {
|
|
1986
|
+
const prefix = `${windowId}:`;
|
|
1987
|
+
for (const [key, sub] of subscriptions) {
|
|
1988
|
+
if (key.startsWith(prefix)) {
|
|
1989
|
+
sub.close();
|
|
1990
|
+
subscriptions.delete(key);
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
};
|
|
1995
|
+
}
|
|
1996
|
+
function toErrorMessage(err) {
|
|
1997
|
+
if (err instanceof Error) return err.message;
|
|
1998
|
+
if (typeof err === "string") return err;
|
|
1999
|
+
return "outbox request failed";
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
// src/relay-pool-outbox-router.ts
|
|
2003
|
+
var DEFAULT_QUERY_TIMEOUT_MS = 4e3;
|
|
2004
|
+
function defaultRelayAllowed(url) {
|
|
2005
|
+
return typeof url === "string" && (url.startsWith("wss://") || url.startsWith("ws://"));
|
|
2006
|
+
}
|
|
2007
|
+
function deriveAuthors(filters, optionAuthors) {
|
|
2008
|
+
const authors = /* @__PURE__ */ new Set();
|
|
2009
|
+
for (const filter of filters) {
|
|
2010
|
+
for (const author of filter.authors ?? []) authors.add(author);
|
|
2011
|
+
}
|
|
2012
|
+
for (const author of optionAuthors ?? []) authors.add(author);
|
|
2013
|
+
return [...authors];
|
|
2014
|
+
}
|
|
2015
|
+
function wantsWriteRelays(direction, strategy) {
|
|
2016
|
+
if (strategy === "outbox") return true;
|
|
2017
|
+
if (strategy === "inbox") return false;
|
|
2018
|
+
return direction === "read";
|
|
2019
|
+
}
|
|
2020
|
+
function allowed(ctx, urls) {
|
|
2021
|
+
const out = /* @__PURE__ */ new Set();
|
|
2022
|
+
for (const url of urls) {
|
|
2023
|
+
if (ctx.isRelayAllowed(url)) out.add(url);
|
|
2024
|
+
}
|
|
2025
|
+
return [...out];
|
|
2026
|
+
}
|
|
2027
|
+
async function resolvePlan(ctx, pubkeys, direction, strategy, relayHints) {
|
|
2028
|
+
const useWrite = wantsWriteRelays(direction, strategy);
|
|
2029
|
+
const collected = /* @__PURE__ */ new Set();
|
|
2030
|
+
const missingAuthors = [];
|
|
2031
|
+
let sawNip65 = false;
|
|
2032
|
+
if (pubkeys.length > 0) {
|
|
2033
|
+
const lists = await ctx.loadRelayLists(pubkeys);
|
|
2034
|
+
for (const pubkey of pubkeys) {
|
|
2035
|
+
const entry = lists.get(pubkey);
|
|
2036
|
+
const relays2 = entry ? useWrite ? entry.write : entry.read : void 0;
|
|
2037
|
+
if (relays2 && relays2.length > 0) {
|
|
2038
|
+
sawNip65 = true;
|
|
2039
|
+
for (const url of relays2) collected.add(url);
|
|
2040
|
+
} else {
|
|
2041
|
+
missingAuthors.push(pubkey);
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
for (const url of relayHints ?? []) collected.add(url);
|
|
2046
|
+
let relays = allowed(ctx, collected);
|
|
2047
|
+
let source;
|
|
2048
|
+
if (relays.length === 0) {
|
|
2049
|
+
relays = allowed(ctx, ctx.fallbackRelays);
|
|
2050
|
+
source = "fallback";
|
|
2051
|
+
} else {
|
|
2052
|
+
source = sawNip65 ? "nip65" : "policy";
|
|
2053
|
+
}
|
|
2054
|
+
const plan = { relays, source };
|
|
2055
|
+
if (missingAuthors.length > 0) plan.missingAuthors = missingAuthors;
|
|
2056
|
+
return plan;
|
|
2057
|
+
}
|
|
2058
|
+
function recordRelay(collector, id, relayUrl) {
|
|
2059
|
+
let set = collector.relayMap.get(id);
|
|
2060
|
+
if (!set) {
|
|
2061
|
+
set = /* @__PURE__ */ new Set();
|
|
2062
|
+
collector.relayMap.set(id, set);
|
|
2063
|
+
}
|
|
2064
|
+
set.add(relayUrl);
|
|
2065
|
+
}
|
|
2066
|
+
function admitEvent(ctx, collector, event) {
|
|
2067
|
+
if (collector.seen.has(event.id)) return;
|
|
2068
|
+
collector.verifications.push(
|
|
2069
|
+
ctx.verify(event).then((ok) => {
|
|
2070
|
+
if (ok && !collector.seen.has(event.id)) collector.seen.set(event.id, event);
|
|
2071
|
+
else if (!ok) collector.relayMap.delete(event.id);
|
|
2072
|
+
})
|
|
2073
|
+
);
|
|
2074
|
+
}
|
|
2075
|
+
function buildCollectResult(collector, timedOut) {
|
|
2076
|
+
const events = [...collector.seen.values()];
|
|
2077
|
+
const relayObj = {};
|
|
2078
|
+
for (const event of events) relayObj[event.id] = [...collector.relayMap.get(event.id) ?? []];
|
|
2079
|
+
return { events, relayMap: relayObj, incomplete: timedOut };
|
|
2080
|
+
}
|
|
2081
|
+
function collectFromRelays(ctx, filters, relayUrls, timeoutMs) {
|
|
2082
|
+
return new Promise((resolve) => {
|
|
2083
|
+
const collector = { seen: /* @__PURE__ */ new Map(), relayMap: /* @__PURE__ */ new Map(), verifications: [] };
|
|
2084
|
+
const handles = [];
|
|
2085
|
+
let eoseCount = 0;
|
|
2086
|
+
let finished = false;
|
|
2087
|
+
let timedOut = false;
|
|
2088
|
+
function finalize() {
|
|
2089
|
+
if (finished) return;
|
|
2090
|
+
finished = true;
|
|
2091
|
+
clearTimeout(timer);
|
|
2092
|
+
for (const handle of handles) {
|
|
2093
|
+
try {
|
|
2094
|
+
handle.unsubscribe();
|
|
2095
|
+
} catch {
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
void Promise.all(collector.verifications).then(() => resolve(buildCollectResult(collector, timedOut)));
|
|
2099
|
+
}
|
|
2100
|
+
const timer = setTimeout(() => {
|
|
2101
|
+
timedOut = true;
|
|
2102
|
+
finalize();
|
|
2103
|
+
}, timeoutMs);
|
|
2104
|
+
for (const relayUrl of relayUrls) {
|
|
2105
|
+
handles.push(relayPoolSubscribe(ctx, filters, relayUrl, (item) => {
|
|
2106
|
+
if (finished) return;
|
|
2107
|
+
if (item === "EOSE") {
|
|
2108
|
+
eoseCount += 1;
|
|
2109
|
+
if (eoseCount >= relayUrls.length) finalize();
|
|
2110
|
+
return;
|
|
2111
|
+
}
|
|
2112
|
+
recordRelay(collector, item.id, relayUrl);
|
|
2113
|
+
admitEvent(ctx, collector, item);
|
|
2114
|
+
}));
|
|
2115
|
+
}
|
|
2116
|
+
if (relayUrls.length === 0) finalize();
|
|
2117
|
+
});
|
|
2118
|
+
}
|
|
2119
|
+
function relayPoolSubscribe(ctx, filters, relayUrl, cb) {
|
|
2120
|
+
return ctx.relayPool.subscribe(filters, [relayUrl], cb);
|
|
2121
|
+
}
|
|
2122
|
+
async function queryImpl(ctx, filters, options) {
|
|
2123
|
+
if (!ctx.relayPool.isAvailable()) {
|
|
2124
|
+
return { events: [], relays: {}, incomplete: true, error: "relay list unavailable" };
|
|
2125
|
+
}
|
|
2126
|
+
const strategy = options?.strategy ?? "auto";
|
|
2127
|
+
const authors = deriveAuthors(filters, options?.authors);
|
|
2128
|
+
const plan = await resolvePlan(ctx, authors, "read", strategy, options?.relays);
|
|
2129
|
+
if (plan.relays.length === 0) {
|
|
2130
|
+
return { events: [], relays: {}, incomplete: true, error: "relay list unavailable" };
|
|
2131
|
+
}
|
|
2132
|
+
const timeoutMs = options?.timeoutMs ?? ctx.defaultTimeoutMs;
|
|
2133
|
+
const collected = await collectFromRelays(ctx, filters, plan.relays, timeoutMs);
|
|
2134
|
+
const incomplete = collected.incomplete || (plan.missingAuthors?.length ?? 0) > 0;
|
|
2135
|
+
let events = collected.events;
|
|
2136
|
+
if (options?.limit !== void 0 && events.length > options.limit) {
|
|
2137
|
+
events = [...events].sort((a, b) => b.created_at - a.created_at).slice(0, options.limit);
|
|
2138
|
+
}
|
|
2139
|
+
const result = { events, relays: collected.relayMap };
|
|
2140
|
+
if (incomplete) result.incomplete = true;
|
|
2141
|
+
return result;
|
|
2142
|
+
}
|
|
2143
|
+
function closeLiveSub(sub) {
|
|
2144
|
+
for (const handle of sub.handles) {
|
|
2145
|
+
try {
|
|
2146
|
+
handle.unsubscribe();
|
|
2147
|
+
} catch {
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
sub.handles.length = 0;
|
|
2151
|
+
}
|
|
2152
|
+
function attachLiveRelay(ctx, sub, filters, relayUrl, live, sink) {
|
|
2153
|
+
sub.handles.push(relayPoolSubscribe(ctx, filters, relayUrl, (item) => {
|
|
2154
|
+
if (sub.closed) return;
|
|
2155
|
+
if (item === "EOSE") {
|
|
2156
|
+
sub.eoseCount += 1;
|
|
2157
|
+
if (!sub.eoseSent && sub.eoseCount >= sub.relayCount) {
|
|
2158
|
+
sub.eoseSent = true;
|
|
2159
|
+
sink.eose();
|
|
2160
|
+
if (!live) {
|
|
2161
|
+
sub.closed = true;
|
|
2162
|
+
closeLiveSub(sub);
|
|
2163
|
+
sink.closed();
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
return;
|
|
2167
|
+
}
|
|
2168
|
+
if (sub.seen.has(item.id)) return;
|
|
2169
|
+
void ctx.verify(item).then((ok) => {
|
|
2170
|
+
if (!ok || sub.closed || sub.seen.has(item.id)) return;
|
|
2171
|
+
sub.seen.add(item.id);
|
|
2172
|
+
sink.event(item, relayUrl);
|
|
2173
|
+
});
|
|
2174
|
+
}));
|
|
2175
|
+
}
|
|
2176
|
+
function startSubscription(ctx, filters, options, sink) {
|
|
2177
|
+
const live = options?.live ?? true;
|
|
2178
|
+
const strategy = options?.strategy ?? "auto";
|
|
2179
|
+
const authors = deriveAuthors(filters, options?.authors);
|
|
2180
|
+
const sub = { handles: [], seen: /* @__PURE__ */ new Set(), closed: false, eoseCount: 0, relayCount: 0, eoseSent: false };
|
|
2181
|
+
void resolvePlan(ctx, authors, "read", strategy, options?.relays).then((plan) => {
|
|
2182
|
+
if (sub.closed) return;
|
|
2183
|
+
if (plan.relays.length === 0) {
|
|
2184
|
+
sink.closed("relay list unavailable");
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
2187
|
+
sub.relayCount = plan.relays.length;
|
|
2188
|
+
for (const relayUrl of plan.relays) attachLiveRelay(ctx, sub, filters, relayUrl, live, sink);
|
|
2189
|
+
}).catch((err) => {
|
|
2190
|
+
if (!sub.closed) sink.closed(err instanceof Error ? err.message : "subscribe failed");
|
|
2191
|
+
});
|
|
2192
|
+
return {
|
|
2193
|
+
close() {
|
|
2194
|
+
if (sub.closed) return;
|
|
2195
|
+
sub.closed = true;
|
|
2196
|
+
closeLiveSub(sub);
|
|
2197
|
+
}
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
async function resolvePublishTargets(ctx, signed, options) {
|
|
2201
|
+
const strategy = options?.strategy ?? "auto";
|
|
2202
|
+
const targets = /* @__PURE__ */ new Set();
|
|
2203
|
+
const authorPlan = await resolvePlan(ctx, [signed.pubkey], "read", strategy === "inbox" ? "auto" : "outbox");
|
|
2204
|
+
for (const url of authorPlan.relays) targets.add(url);
|
|
2205
|
+
if (options?.targetAuthors && options.targetAuthors.length > 0) {
|
|
2206
|
+
const inboxPlan = await resolvePlan(ctx, options.targetAuthors, "write", "inbox");
|
|
2207
|
+
for (const url of inboxPlan.relays) targets.add(url);
|
|
2208
|
+
}
|
|
2209
|
+
for (const url of options?.relays ?? []) targets.add(url);
|
|
2210
|
+
return allowed(ctx, targets);
|
|
2211
|
+
}
|
|
2212
|
+
async function publishImpl(ctx, template, options) {
|
|
2213
|
+
if (!ctx.signEvent) return { ok: false, error: "publish denied" };
|
|
2214
|
+
if (!ctx.relayPool.isAvailable()) return { ok: false, error: "relay list unavailable" };
|
|
2215
|
+
let signed;
|
|
2216
|
+
try {
|
|
2217
|
+
signed = await ctx.signEvent(template);
|
|
2218
|
+
} catch (err) {
|
|
2219
|
+
return { ok: false, error: err instanceof Error ? err.message : "sign failed" };
|
|
2220
|
+
}
|
|
2221
|
+
const relayUrls = await resolvePublishTargets(ctx, signed, options);
|
|
2222
|
+
if (relayUrls.length === 0) return { ok: false, event: signed, eventId: signed.id, error: "relay list unavailable" };
|
|
2223
|
+
let relays;
|
|
2224
|
+
try {
|
|
2225
|
+
relays = normalizePublishResult(await ctx.relayPool.publish(signed, relayUrls), relayUrls);
|
|
2226
|
+
} catch (err) {
|
|
2227
|
+
return { ok: false, event: signed, eventId: signed.id, error: err instanceof Error ? err.message : "publish failed" };
|
|
2228
|
+
}
|
|
2229
|
+
const ok = Object.values(relays).some(Boolean);
|
|
2230
|
+
const result = { ok, event: signed, eventId: signed.id, relays };
|
|
2231
|
+
if (!ok) result.error = "publish denied";
|
|
2232
|
+
return result;
|
|
2233
|
+
}
|
|
2234
|
+
function createRelayPoolOutboxRouter(options) {
|
|
2235
|
+
if (!options || typeof options.relayPool !== "object" || options.relayPool === null) {
|
|
2236
|
+
throw new Error("createRelayPoolOutboxRouter: options.relayPool is required");
|
|
2237
|
+
}
|
|
2238
|
+
if (typeof options.loadRelayLists !== "function") {
|
|
2239
|
+
throw new Error("createRelayPoolOutboxRouter: options.loadRelayLists is required");
|
|
2240
|
+
}
|
|
2241
|
+
if (!Array.isArray(options.fallbackRelays)) {
|
|
2242
|
+
throw new Error("createRelayPoolOutboxRouter: options.fallbackRelays is required");
|
|
2243
|
+
}
|
|
2244
|
+
const verifyEvent = options.verifyEvent;
|
|
2245
|
+
const ctx = {
|
|
2246
|
+
relayPool: options.relayPool,
|
|
2247
|
+
loadRelayLists: options.loadRelayLists,
|
|
2248
|
+
fallbackRelays: options.fallbackRelays,
|
|
2249
|
+
signEvent: options.signEvent,
|
|
2250
|
+
isRelayAllowed: options.isRelayAllowed ?? defaultRelayAllowed,
|
|
2251
|
+
defaultTimeoutMs: options.defaultTimeoutMs ?? DEFAULT_QUERY_TIMEOUT_MS,
|
|
2252
|
+
async verify(event) {
|
|
2253
|
+
if (!verifyEvent) return true;
|
|
2254
|
+
try {
|
|
2255
|
+
return await verifyEvent(event);
|
|
2256
|
+
} catch {
|
|
2257
|
+
return false;
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
};
|
|
2261
|
+
return {
|
|
2262
|
+
query: (filters, queryOptions) => queryImpl(ctx, filters, queryOptions),
|
|
2263
|
+
subscribe: (filters, subscribeOptions, sink) => startSubscription(ctx, filters, subscribeOptions, sink),
|
|
2264
|
+
publish: (template, publishOptions) => publishImpl(ctx, template, publishOptions),
|
|
2265
|
+
resolveRelays: (target) => {
|
|
2266
|
+
const pubkeys = target.authors ?? (target.pubkey ? [target.pubkey] : []);
|
|
2267
|
+
return resolvePlan(ctx, pubkeys, target.direction ?? "read", target.strategy ?? "auto");
|
|
2268
|
+
}
|
|
2269
|
+
};
|
|
2270
|
+
}
|
|
2271
|
+
function normalizePublishResult(res, relayUrls) {
|
|
2272
|
+
const out = {};
|
|
2273
|
+
if (res && typeof res === "object") {
|
|
2274
|
+
for (const url of relayUrls) out[url] = res[url] ?? false;
|
|
2275
|
+
} else {
|
|
2276
|
+
for (const url of relayUrls) out[url] = true;
|
|
2277
|
+
}
|
|
2278
|
+
return out;
|
|
2279
|
+
}
|
|
2280
|
+
|
|
1857
2281
|
// src/cvm-service.ts
|
|
1858
2282
|
var CVM_SERVICE_VERSION = "1.0.0";
|
|
1859
2283
|
var CVM_DESCRIPTOR = {
|
|
@@ -1896,7 +2320,7 @@ function createCvmService(options) {
|
|
|
1896
2320
|
const m = msg;
|
|
1897
2321
|
const id = m.id ?? "";
|
|
1898
2322
|
void transport.discover(m.query).then((servers) => send({ type: "cvm.discover.result", id, servers })).catch(
|
|
1899
|
-
(err) => send({ type: "cvm.discover.result", id, servers: [], error:
|
|
2323
|
+
(err) => send({ type: "cvm.discover.result", id, servers: [], error: toErrorMessage2(err) })
|
|
1900
2324
|
);
|
|
1901
2325
|
}
|
|
1902
2326
|
function handleRequest(windowId, msg, send) {
|
|
@@ -1912,7 +2336,7 @@ function createCvmService(options) {
|
|
|
1912
2336
|
}
|
|
1913
2337
|
openSession(windowId, m.server, send);
|
|
1914
2338
|
void transport.request(m.server, m.message, m.options).then((message) => send({ type: "cvm.request.result", id, message })).catch(
|
|
1915
|
-
(err) => send({ type: "cvm.request.result", id, error:
|
|
2339
|
+
(err) => send({ type: "cvm.request.result", id, error: toErrorMessage2(err) })
|
|
1916
2340
|
);
|
|
1917
2341
|
}
|
|
1918
2342
|
function handleClose(windowId, msg, send) {
|
|
@@ -1923,7 +2347,7 @@ function createCvmService(options) {
|
|
|
1923
2347
|
return;
|
|
1924
2348
|
}
|
|
1925
2349
|
closeSession(windowId, m.server.pubkey);
|
|
1926
|
-
void transport.close(m.server).then(() => send({ type: "cvm.close.result", id })).catch((err) => send({ type: "cvm.close.result", id, error:
|
|
2350
|
+
void transport.close(m.server).then(() => send({ type: "cvm.close.result", id })).catch((err) => send({ type: "cvm.close.result", id, error: toErrorMessage2(err) }));
|
|
1927
2351
|
}
|
|
1928
2352
|
return {
|
|
1929
2353
|
descriptor: CVM_DESCRIPTOR,
|
|
@@ -1954,7 +2378,7 @@ function createCvmService(options) {
|
|
|
1954
2378
|
}
|
|
1955
2379
|
};
|
|
1956
2380
|
}
|
|
1957
|
-
function
|
|
2381
|
+
function toErrorMessage2(err) {
|
|
1958
2382
|
if (err instanceof Error) return err.message;
|
|
1959
2383
|
if (typeof err === "string") return err;
|
|
1960
2384
|
return "cvm request failed";
|
|
@@ -1971,6 +2395,8 @@ export {
|
|
|
1971
2395
|
createMediaService,
|
|
1972
2396
|
createNotificationService,
|
|
1973
2397
|
createNotifyService,
|
|
2398
|
+
createOutboxService,
|
|
2399
|
+
createRelayPoolOutboxRouter,
|
|
1974
2400
|
createRelayPoolService,
|
|
1975
2401
|
createResourceService,
|
|
1976
2402
|
createThemeService
|