@kehto/services 0.14.1 → 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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ServiceHandler, Signer } from '@kehto/runtime';
1
+ import { ServiceHandler, Signer, RelayEventResult } from '@kehto/runtime';
2
2
  import { IdentityGetProfileMessage, IdentityGetProfileResultMessage, IdentityGetFollowsMessage, IdentityGetFollowsResultMessage, IdentityGetListMessage, IdentityGetListResultMessage, IdentityGetZapsMessage, IdentityGetZapsResultMessage, IdentityGetMutesMessage, IdentityGetMutesResultMessage, IdentityGetBlockedMessage, IdentityGetBlockedResultMessage, IdentityGetBadgesMessage, IdentityGetBadgesResultMessage } from '@napplet/nap/identity/types';
3
3
  import { NostrFilter, NostrEvent, EventTemplate, LinkOpenOptions, LinkOpenResult, ListSupport, ListRef, ListItem, ListOptions, ListMutationResult, SerialOpenRequest, SerialOpenResult, BleEvent, BleOpenRequest, BleOpenResult, BleService, BleAttribute, BleWriteOptions, WebrtcEvent, WebrtcOpenRequest, WebrtcOpenResult, CommonProfileTarget, CommonProfileResult, CommonFollowsResult, CommonActionResult, CommonReaction, CommonReportTarget, CommonReportReason } from '@napplet/core';
4
4
  import { MediaMetadata, MediaAction } from '@napplet/nap/media/types';
@@ -1635,7 +1635,7 @@ declare function createResourceService(options: ResourceServiceOptions): Resourc
1635
1635
  * Inbound: outbox.getEvent, outbox.query, outbox.subscribe, outbox.close,
1636
1636
  * outbox.publish, outbox.resolveRelays
1637
1637
  * Outbound: outbox.getEvent.result, outbox.query.result, outbox.event,
1638
- * outbox.eose, outbox.closed, outbox.publish.result,
1638
+ * outbox.closed, outbox.publish.result,
1639
1639
  * outbox.resolveRelays.result
1640
1640
  *
1641
1641
  * The shell owns relay discovery, routing, fallback, deduplication, signature
@@ -1720,10 +1720,8 @@ interface OutboxRelayPlan {
1720
1720
  }
1721
1721
  /** Outcome of a single-event outbox lookup. */
1722
1722
  interface OutboxEventResult {
1723
- /** The signature-validated event matching the requested event id, when found. */
1724
- event?: NostrEvent;
1725
- /** Relay URLs where the shell observed the event. */
1726
- relays: string[];
1723
+ /** The signature-validated event result matching the requested event id, when found. */
1724
+ result?: RelayEventResult;
1727
1725
  /** True when some relay lists or connections failed and the lookup was partial. */
1728
1726
  incomplete?: boolean;
1729
1727
  /** Error reason when the lookup could not complete or did not find the event. */
@@ -1731,10 +1729,8 @@ interface OutboxEventResult {
1731
1729
  }
1732
1730
  /** Outcome of an outbox query, as returned by the {@link OutboxRouter}. */
1733
1731
  interface OutboxResult {
1734
- /** Deduplicated, signature-validated events. */
1735
- events: NostrEvent[];
1736
- /** Map of event id -> relay URLs where the shell observed the event. */
1737
- relays: Record<string, string[]>;
1732
+ /** Deduplicated, signature-validated event results. */
1733
+ events: RelayEventResult[];
1738
1734
  /** True when some relay lists or connections failed and results are partial. */
1739
1735
  incomplete?: boolean;
1740
1736
  /** Error reason when the query could not complete. */
@@ -1755,10 +1751,8 @@ interface OutboxPublishResult {
1755
1751
  }
1756
1752
  /** Sink an {@link OutboxRouter} streams subscription lifecycle through. */
1757
1753
  interface OutboxSubscriptionSink {
1758
- /** Deliver a matching event; `relay` is the relay it was observed on, when known. */
1759
- event(event: NostrEvent, relay?: string): void;
1760
- /** Signal end-of-stored-events. */
1761
- eose(): void;
1754
+ /** Deliver a matching event result. */
1755
+ event(result: RelayEventResult): void;
1762
1756
  /** Signal that the subscription was closed upstream; `reason` is optional. */
1763
1757
  closed(reason?: string): void;
1764
1758
  }
package/dist/index.js CHANGED
@@ -426,6 +426,7 @@ function createIdentityService(options) {
426
426
  }
427
427
 
428
428
  // src/relay-pool-service.ts
429
+ import { createRelayEventResultWithHints } from "@kehto/runtime";
429
430
  var EOSE_FALLBACK_MS = 15e3;
430
431
  function createRelayPoolService(options) {
431
432
  const tracked = /* @__PURE__ */ new Map();
@@ -470,7 +471,7 @@ function createRelayPoolService(options) {
470
471
  }
471
472
  return;
472
473
  }
473
- send({ type: "relay.event", subId, event: item });
474
+ send({ type: "relay.event", subId, result: createRelayEventResultWithHints(item, relayUrls2) });
474
475
  }, relayUrls2);
475
476
  tracked.set(subKey, { handle, eoseTimer });
476
477
  return;
@@ -516,6 +517,7 @@ function createRelayPoolService(options) {
516
517
  }
517
518
 
518
519
  // src/cache-service.ts
520
+ import { createRelayEventResult } from "@kehto/runtime";
519
521
  function createCacheService(options) {
520
522
  return {
521
523
  descriptor: {
@@ -535,7 +537,7 @@ function createCacheService(options) {
535
537
  }
536
538
  options.query(filters).then((events) => {
537
539
  for (const event of events) {
538
- send({ type: "relay.event", subId, event });
540
+ send({ type: "relay.event", subId, result: createRelayEventResult(event) });
539
541
  }
540
542
  send({ type: "relay.eose", subId });
541
543
  }).catch(() => {
@@ -647,6 +649,7 @@ function isPlainObject(value) {
647
649
  }
648
650
 
649
651
  // src/coordinated-relay.ts
652
+ import { createRelayEventResult as createRelayEventResult2, createRelayEventResultWithHints as createRelayEventResultWithHints2 } from "@kehto/runtime";
650
653
  var DEFAULT_EOSE_TIMEOUT_MS = 15e3;
651
654
  function createCoordinatedRelay(options) {
652
655
  const timeoutMs = options.eoseTimeoutMs ?? DEFAULT_EOSE_TIMEOUT_MS;
@@ -669,10 +672,13 @@ function createCoordinatedRelay(options) {
669
672
  handleMessage(windowId, message, send) {
670
673
  const relayMessage = message;
671
674
  if (message.type === "relay.subscribe") {
672
- let deliver2 = function(event) {
675
+ let deliver2 = function(event, relayHints) {
673
676
  if (tracked.seenIds.has(event.id)) return;
674
677
  tracked.seenIds.add(event.id);
675
- if (subs.has(subKey)) send({ type: "relay.event", subId, event });
678
+ if (subs.has(subKey)) {
679
+ const result2 = relayHints ? createRelayEventResultWithHints2(event, relayHints) : createRelayEventResult2(event);
680
+ send({ type: "relay.event", subId, result: result2 });
681
+ }
676
682
  };
677
683
  var deliver = deliver2;
678
684
  const subId = relayMessage.subId;
@@ -728,7 +734,7 @@ function createCoordinatedRelay(options) {
728
734
  maybeSendEose(subKey, subId, send);
729
735
  return;
730
736
  }
731
- deliver2(item);
737
+ deliver2(item, relayUrls2);
732
738
  if (cacheAvailable) {
733
739
  try {
734
740
  options.cache.store(item);
@@ -2045,12 +2051,10 @@ function buildEventQuery(eventId, eventOptions) {
2045
2051
  return { filter, queryOptions };
2046
2052
  }
2047
2053
  function eventResultFromQuery(eventId, result2) {
2048
- const event = result2.events.find((candidate) => candidate.id === eventId);
2049
- const eventResult = {
2050
- relays: event ? result2.relays[eventId] ?? [] : []
2051
- };
2052
- if (event) eventResult.event = event;
2053
- if (!event) eventResult.error = result2.error ?? "not found";
2054
+ const relayResult = result2.events.find((candidate) => candidate.event.id === eventId);
2055
+ const eventResult = {};
2056
+ if (relayResult) eventResult.result = relayResult;
2057
+ if (!relayResult) eventResult.error = result2.error ?? "not found";
2054
2058
  else if (result2.error !== void 0) eventResult.error = result2.error;
2055
2059
  if (result2.incomplete !== void 0) eventResult.incomplete = result2.incomplete;
2056
2060
  return eventResult;
@@ -2060,15 +2064,14 @@ function fallbackGetEvent(router, eventId, eventOptions) {
2060
2064
  return router.query([filter], queryOptions).then((result2) => eventResultFromQuery(eventId, result2));
2061
2065
  }
2062
2066
  function sendEventResult(id, eventId, result2, send) {
2063
- if (result2.event && result2.event.id !== eventId) {
2064
- send({ type: "outbox.getEvent.result", id, relays: [], error: "not found" });
2067
+ if (result2.result && result2.result.event.id !== eventId) {
2068
+ send({ type: "outbox.getEvent.result", id, error: "not found" });
2065
2069
  return;
2066
2070
  }
2067
2071
  send({
2068
2072
  type: "outbox.getEvent.result",
2069
2073
  id,
2070
- ...result2.event === void 0 ? {} : { event: result2.event },
2071
- relays: result2.relays,
2074
+ ...result2.result === void 0 ? {} : { result: result2.result },
2072
2075
  ...result2.incomplete === void 0 ? {} : { incomplete: result2.incomplete },
2073
2076
  ...result2.error === void 0 ? {} : { error: result2.error }
2074
2077
  });
@@ -2077,12 +2080,12 @@ function handleGetEvent(router, msg, send) {
2077
2080
  const m = msg;
2078
2081
  const id = m.id ?? "";
2079
2082
  if (typeof m.eventId !== "string" || m.eventId.length === 0) {
2080
- send({ type: "outbox.getEvent.result", id, relays: [], error: "invalid filter" });
2083
+ send({ type: "outbox.getEvent.result", id, error: "invalid filter" });
2081
2084
  return;
2082
2085
  }
2083
2086
  const getEvent = router.getEvent ?? ((eventId, eventOptions) => fallbackGetEvent(router, eventId, eventOptions));
2084
2087
  void getEvent(m.eventId, m.options).then((result2) => sendEventResult(id, m.eventId, result2, send)).catch(
2085
- (err2) => send({ type: "outbox.getEvent.result", id, relays: [], error: toErrorMessage(err2) })
2088
+ (err2) => send({ type: "outbox.getEvent.result", id, error: toErrorMessage(err2) })
2086
2089
  );
2087
2090
  }
2088
2091
  function createOutboxService(options) {
@@ -2096,7 +2099,7 @@ function createOutboxService(options) {
2096
2099
  const id = m.id ?? "";
2097
2100
  const filters = normalizeFilters(m.filters);
2098
2101
  if (!filters) {
2099
- send({ type: "outbox.query.result", id, events: [], relays: {}, error: "invalid filter" });
2102
+ send({ type: "outbox.query.result", id, events: [], error: "invalid filter" });
2100
2103
  return;
2101
2104
  }
2102
2105
  void router.query(filters, m.options).then(
@@ -2104,12 +2107,11 @@ function createOutboxService(options) {
2104
2107
  type: "outbox.query.result",
2105
2108
  id,
2106
2109
  events: result2.events,
2107
- relays: result2.relays,
2108
2110
  ...result2.incomplete === void 0 ? {} : { incomplete: result2.incomplete },
2109
2111
  ...result2.error === void 0 ? {} : { error: result2.error }
2110
2112
  })
2111
2113
  ).catch(
2112
- (err2) => send({ type: "outbox.query.result", id, events: [], relays: {}, error: toErrorMessage(err2) })
2114
+ (err2) => send({ type: "outbox.query.result", id, events: [], error: toErrorMessage(err2) })
2113
2115
  );
2114
2116
  }
2115
2117
  function handleSubscribe(windowId, msg, send) {
@@ -2125,8 +2127,7 @@ function createOutboxService(options) {
2125
2127
  return;
2126
2128
  }
2127
2129
  const sink = {
2128
- event: (event, relay) => send({ type: "outbox.event", subId, event, ...relay === void 0 ? {} : { relay } }),
2129
- eose: () => send({ type: "outbox.eose", subId }),
2130
+ event: (result2) => send({ type: "outbox.event", subId, result: result2 }),
2130
2131
  closed: (reason) => {
2131
2132
  subscriptions.delete(subKey);
2132
2133
  send({ type: "outbox.closed", subId, ...reason === void 0 ? {} : { reason } });
@@ -2219,6 +2220,7 @@ function toErrorMessage(err2) {
2219
2220
  }
2220
2221
 
2221
2222
  // src/relay-pool-outbox-router.ts
2223
+ import { createRelayEventResultWithHints as createRelayEventResultWithHints3 } from "@kehto/runtime";
2222
2224
  var DEFAULT_QUERY_TIMEOUT_MS = 4e3;
2223
2225
  function defaultRelayAllowed(url) {
2224
2226
  return typeof url === "string" && (url.startsWith("wss://") || url.startsWith("ws://"));
@@ -2292,10 +2294,10 @@ function admitEvent(ctx, collector, event) {
2292
2294
  );
2293
2295
  }
2294
2296
  function buildCollectResult(collector, timedOut) {
2295
- const events = [...collector.seen.values()];
2296
- const relayObj = {};
2297
- for (const event of events) relayObj[event.id] = [...collector.relayMap.get(event.id) ?? []];
2298
- return { events, relayMap: relayObj, incomplete: timedOut };
2297
+ const events = [...collector.seen.values()].map(
2298
+ (event) => createRelayEventResultWithHints3(event, [...collector.relayMap.get(event.id) ?? []])
2299
+ );
2300
+ return { events, incomplete: timedOut };
2299
2301
  }
2300
2302
  function collectFromRelays(ctx, filters, relayUrls2, timeoutMs) {
2301
2303
  return new Promise((resolve) => {
@@ -2340,28 +2342,28 @@ function relayPoolSubscribe(ctx, filters, relayUrl, cb) {
2340
2342
  }
2341
2343
  async function queryImpl(ctx, filters, options) {
2342
2344
  if (!ctx.relayPool.isAvailable()) {
2343
- return { events: [], relays: {}, incomplete: true, error: "relay list unavailable" };
2345
+ return { events: [], incomplete: true, error: "relay list unavailable" };
2344
2346
  }
2345
2347
  const strategy = options?.strategy ?? "auto";
2346
2348
  const authors = deriveAuthors(filters, options?.authors);
2347
2349
  const plan = await resolvePlan(ctx, authors, "read", strategy, options?.relays);
2348
2350
  if (plan.relays.length === 0) {
2349
- return { events: [], relays: {}, incomplete: true, error: "relay list unavailable" };
2351
+ return { events: [], incomplete: true, error: "relay list unavailable" };
2350
2352
  }
2351
2353
  const timeoutMs = options?.timeoutMs ?? ctx.defaultTimeoutMs;
2352
2354
  const collected = await collectFromRelays(ctx, filters, plan.relays, timeoutMs);
2353
2355
  const incomplete = collected.incomplete || (plan.missingAuthors?.length ?? 0) > 0;
2354
2356
  let events = collected.events;
2355
2357
  if (options?.limit !== void 0 && events.length > options.limit) {
2356
- events = [...events].sort((a, b) => b.created_at - a.created_at).slice(0, options.limit);
2358
+ events = [...events].sort((a, b) => b.event.created_at - a.event.created_at).slice(0, options.limit);
2357
2359
  }
2358
- const result2 = { events, relays: collected.relayMap };
2360
+ const result2 = { events };
2359
2361
  if (incomplete) result2.incomplete = true;
2360
2362
  return result2;
2361
2363
  }
2362
2364
  async function getEventImpl(ctx, eventId, options) {
2363
2365
  if (typeof eventId !== "string" || eventId.length === 0) {
2364
- return { relays: [], error: "invalid filter" };
2366
+ return { error: "invalid filter" };
2365
2367
  }
2366
2368
  const filter = { ids: [eventId] };
2367
2369
  const queryOptions = { limit: 1 };
@@ -2373,12 +2375,10 @@ async function getEventImpl(ctx, eventId, options) {
2373
2375
  if (options?.strategy !== void 0) queryOptions.strategy = options.strategy;
2374
2376
  if (options?.timeoutMs !== void 0) queryOptions.timeoutMs = options.timeoutMs;
2375
2377
  const queryResult = await queryImpl(ctx, [filter], queryOptions);
2376
- const event = queryResult.events.find((candidate) => candidate.id === eventId);
2377
- const result2 = {
2378
- relays: event ? queryResult.relays[eventId] ?? [] : []
2379
- };
2380
- if (event) result2.event = event;
2381
- if (!event) result2.error = queryResult.error ?? "not found";
2378
+ const eventResult = queryResult.events.find((candidate) => candidate.event.id === eventId);
2379
+ const result2 = {};
2380
+ if (eventResult) result2.result = eventResult;
2381
+ if (!eventResult) result2.error = queryResult.error ?? "not found";
2382
2382
  else if (queryResult.error !== void 0) result2.error = queryResult.error;
2383
2383
  if (queryResult.incomplete !== void 0) result2.incomplete = queryResult.incomplete;
2384
2384
  return result2;
@@ -2399,7 +2399,6 @@ function attachLiveRelay(ctx, sub, filters, relayUrl, live, sink) {
2399
2399
  sub.eoseCount += 1;
2400
2400
  if (!sub.eoseSent && sub.eoseCount >= sub.relayCount) {
2401
2401
  sub.eoseSent = true;
2402
- sink.eose();
2403
2402
  if (!live) {
2404
2403
  sub.closed = true;
2405
2404
  closeLiveSub(sub);
@@ -2412,7 +2411,7 @@ function attachLiveRelay(ctx, sub, filters, relayUrl, live, sink) {
2412
2411
  void ctx.verify(item).then((ok) => {
2413
2412
  if (!ok || sub.closed || sub.seen.has(item.id)) return;
2414
2413
  sub.seen.add(item.id);
2415
- sink.event(item, relayUrl);
2414
+ sink.event(createRelayEventResultWithHints3(item, [relayUrl]));
2416
2415
  });
2417
2416
  }));
2418
2417
  }