@kehto/paja 0.4.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.
@@ -192,6 +192,9 @@ function relayMap(action) {
192
192
  if (action === "publishEncrypted") return { senderCap: "relay:write", recipientCap: null };
193
193
  return { senderCap: "relay:read", recipientCap: null };
194
194
  }
195
+ function countMap(_action) {
196
+ return { senderCap: "relay:read", recipientCap: null };
197
+ }
195
198
  function identityMap(action) {
196
199
  if (action === "getPublicKey" || action === "getRelays") {
197
200
  return { senderCap: null, recipientCap: null };
@@ -278,6 +281,8 @@ function resolveCapabilitiesNap(msg) {
278
281
  switch (domain) {
279
282
  case "relay":
280
283
  return relayMap(action);
284
+ case "count":
285
+ return countMap(action);
281
286
  case "identity":
282
287
  return identityMap(action);
283
288
  case "keys":
@@ -607,7 +612,7 @@ function deserialize2(json) {
607
612
  return defaultConfig();
608
613
  }
609
614
 
610
- // ../../node_modules/.pnpm/@napplet+core@0.24.0/node_modules/@napplet/core/dist/index.js
615
+ // ../../node_modules/.pnpm/@napplet+core@0.25.0/node_modules/@napplet/core/dist/index.js
611
616
  function createDispatch() {
612
617
  const handlers = /* @__PURE__ */ new Map();
613
618
  function registerNap2(domain, handler) {
@@ -781,11 +786,11 @@ function capToBit(cap) {
781
786
  return CAP_MAP[cap] ?? 0;
782
787
  }
783
788
  function bitsToCapabilities(bits) {
784
- const result = [];
789
+ const result2 = [];
785
790
  for (const [cap, bit] of Object.entries(CAP_MAP)) {
786
- if (bits & bit) result.push(cap);
791
+ if (bits & bit) result2.push(cap);
787
792
  }
788
- return result;
793
+ return result2;
789
794
  }
790
795
  function toIdentity(pubkey, dTag, hash) {
791
796
  return { pubkey, dTag, hash };
@@ -881,9 +886,9 @@ function createFirewallState(persistence) {
881
886
  let counters = createState2();
882
887
  return {
883
888
  evaluate(observation) {
884
- const result = evaluate(config, counters, observation);
885
- counters = result.newState;
886
- return result;
889
+ const result2 = evaluate(config, counters, observation);
890
+ counters = result2.newState;
891
+ return result2;
887
892
  },
888
893
  setPolicy(napplet, policy) {
889
894
  config = setPolicy(config, napplet, policy);
@@ -979,8 +984,8 @@ function createManifestCache(persistence) {
979
984
  getVerification(eventId) {
980
985
  return verificationCache.get(eventId);
981
986
  },
982
- setVerification(eventId, result) {
983
- verificationCache.set(eventId, result);
987
+ setVerification(eventId, result2) {
988
+ verificationCache.set(eventId, result2);
984
989
  },
985
990
  hasVerification(eventId) {
986
991
  return verificationCache.has(eventId);
@@ -1074,6 +1079,41 @@ function createEventBuffer(sendToNapplet, sessionRegistry2, enforce, subscriptio
1074
1079
  }
1075
1080
  };
1076
1081
  }
1082
+ function stringList(value) {
1083
+ if (!Array.isArray(value)) return void 0;
1084
+ const out = [...new Set(value.filter((item) => typeof item === "string" && item.length > 0))];
1085
+ return out.length > 0 ? out : void 0;
1086
+ }
1087
+ function sidecarFromUnknown(value) {
1088
+ if (!value || typeof value !== "object") return void 0;
1089
+ const input = value;
1090
+ const sidecar = {};
1091
+ if (Array.isArray(input.resources) && input.resources.length > 0) sidecar.resources = input.resources;
1092
+ const relayHints = stringList(input.relayHints);
1093
+ if (relayHints) sidecar.relayHints = relayHints;
1094
+ return sidecar.resources || sidecar.relayHints ? sidecar : void 0;
1095
+ }
1096
+ function createRelayEventResult(event, sidecar) {
1097
+ const normalized = sidecarFromUnknown(sidecar);
1098
+ return normalized ? { event, sidecar: normalized } : { event };
1099
+ }
1100
+ function createRelayEventResultWithHints(event, relayHints) {
1101
+ return createRelayEventResult(event, relayHints && relayHints.length > 0 ? { relayHints } : void 0);
1102
+ }
1103
+ function relayEventResultFromCarrier(carrier) {
1104
+ if (carrier.result && typeof carrier.result === "object") {
1105
+ const result2 = carrier.result;
1106
+ if (result2.event && typeof result2.event === "object") return createRelayEventResult(result2.event, result2.sidecar);
1107
+ }
1108
+ if (carrier.event && typeof carrier.event === "object") {
1109
+ const sidecar = sidecarFromUnknown(carrier.sidecar) ?? sidecarFromUnknown({ resources: carrier.resources });
1110
+ const relayHints = typeof carrier.relay === "string" && carrier.relay.length > 0 ? [carrier.relay] : void 0;
1111
+ const merged = sidecar ? { ...sidecar } : {};
1112
+ if (relayHints) merged.relayHints = relayHints;
1113
+ return createRelayEventResult(carrier.event, merged);
1114
+ }
1115
+ return null;
1116
+ }
1077
1117
  function notifyServiceWindowDestroyed(windowId, services) {
1078
1118
  for (const handler of Object.values(services)) {
1079
1119
  try {
@@ -1114,6 +1154,15 @@ function relayServiceFrom(context) {
1114
1154
  function isShellKindQuery(filters) {
1115
1155
  return filters.length > 0 && filters.every((filter) => filter.kinds?.every((kind) => kind >= 29e3 && kind < 3e4));
1116
1156
  }
1157
+ function sendRelayEvent(hooks, windowId, subId, result2) {
1158
+ hooks.sendToNapplet(windowId, { type: "relay.event", subId, result: result2 });
1159
+ }
1160
+ function normalizeRelayServiceResponse(resp) {
1161
+ const m = resp;
1162
+ if (m.type !== "relay.event") return resp;
1163
+ const result2 = relayEventResultFromCarrier(m);
1164
+ return result2 ? { type: "relay.event", subId: m.subId, result: result2 } : resp;
1165
+ }
1117
1166
  function handleRelaySubscribe(context, windowId, msg, m) {
1118
1167
  const { eventBuffer, hooks, serviceRegistry, subscriptions } = context;
1119
1168
  const subId = m.subId ?? "";
@@ -1122,11 +1171,11 @@ function handleRelaySubscribe(context, windowId, msg, m) {
1122
1171
  const subKey = `${windowId}:${subId}`;
1123
1172
  subscriptions.set(subKey, { windowId, filters });
1124
1173
  const seenIds = /* @__PURE__ */ new Set();
1125
- function deliver(event) {
1174
+ function deliver(event, relayHints) {
1126
1175
  if (seenIds.has(event.id)) return;
1127
1176
  seenIds.add(event.id);
1128
1177
  if (subscriptions.has(subKey)) {
1129
- hooks.sendToNapplet(windowId, { type: "relay.event", subId, event });
1178
+ sendRelayEvent(hooks, windowId, subId, createRelayEventResultWithHints(event, relayHints));
1130
1179
  }
1131
1180
  }
1132
1181
  for (const bufferedEvent of eventBuffer.getBufferedEvents()) {
@@ -1138,12 +1187,12 @@ function handleRelaySubscribe(context, windowId, msg, m) {
1138
1187
  if (!isShellKind && relayService) {
1139
1188
  relayService.handleMessage(windowId, msg, (resp) => {
1140
1189
  if (!subscriptions.has(subKey)) return;
1141
- hooks.sendToNapplet(windowId, resp);
1190
+ hooks.sendToNapplet(windowId, normalizeRelayServiceResponse(resp));
1142
1191
  });
1143
1192
  if (cacheService) {
1144
1193
  cacheService.handleMessage(windowId, msg, (resp) => {
1145
1194
  if (!subscriptions.has(subKey)) return;
1146
- hooks.sendToNapplet(windowId, resp);
1195
+ hooks.sendToNapplet(windowId, normalizeRelayServiceResponse(resp));
1147
1196
  });
1148
1197
  }
1149
1198
  return;
@@ -1183,10 +1232,11 @@ function deliverFromRuntimeBackends(context, windowId, subId, subKey, filters, i
1183
1232
  }
1184
1233
  return;
1185
1234
  }
1186
- deliver(item);
1235
+ const event = item;
1236
+ deliver(event, relayUrls);
1187
1237
  if (cache2?.isAvailable() && !isShellKind) {
1188
1238
  try {
1189
- cache2.store(item);
1239
+ cache2.store(event);
1190
1240
  } catch {
1191
1241
  return;
1192
1242
  }
@@ -1325,7 +1375,7 @@ function handleRelayQuery(context, windowId, m) {
1325
1375
  for (const event of eventBuffer.getBufferedEvents()) {
1326
1376
  if (matchesAnyFilter(event, filters) && !seenIds.has(event.id)) {
1327
1377
  seenIds.add(event.id);
1328
- events.push(event);
1378
+ events.push(createRelayEventResult(event));
1329
1379
  }
1330
1380
  }
1331
1381
  let settled = false;
@@ -1358,9 +1408,10 @@ function handleRelayQuery(context, windowId, m) {
1358
1408
  }, makeCollector2 = function() {
1359
1409
  return function collector(resp) {
1360
1410
  const r = resp;
1361
- if (r.type === "relay.event" && r.event && !seenIds.has(r.event.id)) {
1362
- seenIds.add(r.event.id);
1363
- events.push(r.event);
1411
+ const result2 = r.type === "relay.event" ? relayEventResultFromCarrier(r) : null;
1412
+ if (result2 && !seenIds.has(result2.event.id)) {
1413
+ seenIds.add(result2.event.id);
1414
+ events.push(result2);
1364
1415
  } else if (r.type === "relay.eose") {
1365
1416
  onBackendEose2();
1366
1417
  }
@@ -1402,7 +1453,7 @@ function handleRelayQuery(context, windowId, m) {
1402
1453
  for (const event of cachedEvents) {
1403
1454
  if (!seenIds.has(event.id)) {
1404
1455
  seenIds.add(event.id);
1405
- events.push(event);
1456
+ events.push(createRelayEventResult(event));
1406
1457
  }
1407
1458
  }
1408
1459
  }).catch(() => {
@@ -1429,7 +1480,7 @@ function handleRelayQuery(context, windowId, m) {
1429
1480
  const event = item;
1430
1481
  if (!seenIds.has(event.id)) {
1431
1482
  seenIds.add(event.id);
1432
- events.push(event);
1483
+ events.push(createRelayEventResultWithHints(event, relayUrls));
1433
1484
  }
1434
1485
  if (cache2?.isAvailable()) {
1435
1486
  try {
@@ -1498,6 +1549,65 @@ function createIdentityHandler(context) {
1498
1549
  }
1499
1550
  };
1500
1551
  }
1552
+ function createCountHandler(context) {
1553
+ return function handleCountMessage(windowId, msg) {
1554
+ if (msg.type !== "count.query") return;
1555
+ const message = msg;
1556
+ const id = typeof message.id === "string" ? message.id : "";
1557
+ const filters = message.filters;
1558
+ if (!isValidFilterArray(filters)) {
1559
+ sendCountError(context.hooks, windowId, id, "invalid-filter", "count.query requires a non-empty filters array");
1560
+ return;
1561
+ }
1562
+ if (message.options !== void 0 && !isPlainObject(message.options)) {
1563
+ sendCountError(context.hooks, windowId, id, "invalid-filter", "count.query options must be an object when present");
1564
+ return;
1565
+ }
1566
+ const service = context.serviceRegistry["count"];
1567
+ if (!service) {
1568
+ sendCountError(
1569
+ context.hooks,
1570
+ windowId,
1571
+ id,
1572
+ "count-unavailable",
1573
+ "count service unavailable; refusing to emulate counts by fetching event payloads"
1574
+ );
1575
+ return;
1576
+ }
1577
+ service.handleMessage(windowId, msg, (resp) => {
1578
+ context.hooks.sendToNapplet(windowId, normalizeCountResult(resp, id));
1579
+ });
1580
+ };
1581
+ }
1582
+ function normalizeCountResult(resp, id) {
1583
+ const value = resp;
1584
+ return {
1585
+ type: "count.query.result",
1586
+ id: typeof value.id === "string" ? value.id : id,
1587
+ ok: value.ok === true,
1588
+ ...typeof value.count === "number" && Number.isFinite(value.count) && value.count >= 0 ? { count: Math.floor(value.count) } : {},
1589
+ ...typeof value.approximate === "boolean" ? { approximate: value.approximate } : {},
1590
+ ...typeof value.hll === "string" ? { hll: value.hll } : {},
1591
+ ...Array.isArray(value.relays) ? { relays: value.relays.filter((relay) => typeof relay === "string") } : {},
1592
+ ...typeof value.error === "string" ? { error: value.error } : {},
1593
+ ...typeof value.reason === "string" ? { reason: value.reason } : {}
1594
+ };
1595
+ }
1596
+ function sendCountError(hooks, windowId, id, error, reason) {
1597
+ hooks.sendToNapplet(windowId, {
1598
+ type: "count.query.result",
1599
+ id,
1600
+ ok: false,
1601
+ error,
1602
+ reason
1603
+ });
1604
+ }
1605
+ function isValidFilterArray(value) {
1606
+ return Array.isArray(value) && value.length > 0 && value.every((filter) => isPlainObject(filter));
1607
+ }
1608
+ function isPlainObject(value) {
1609
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1610
+ }
1501
1611
  function createIncRuntime(hooks, sessionRegistry2) {
1502
1612
  const state = {
1503
1613
  subscriptions: /* @__PURE__ */ new Map(),
@@ -1747,14 +1857,14 @@ function handleStorageNap(windowId, msg, sendToNapplet, sessionRegistry2, aclSta
1747
1857
  break;
1748
1858
  }
1749
1859
  const newKey = scopedKey(dTag, aggregateHash, key);
1750
- let result = statePersistence.get(newKey);
1751
- if (result === null && pubkey) {
1752
- result = statePersistence.get(legacyScopedKey(pubkey, dTag, aggregateHash, key));
1860
+ let result2 = statePersistence.get(newKey);
1861
+ if (result2 === null && pubkey) {
1862
+ result2 = statePersistence.get(legacyScopedKey(pubkey, dTag, aggregateHash, key));
1753
1863
  }
1754
- if (result === null && pubkey) {
1755
- result = statePersistence.get(`napp-state:${pubkey}:${dTag}:${aggregateHash}:${key}`);
1864
+ if (result2 === null && pubkey) {
1865
+ result2 = statePersistence.get(`napp-state:${pubkey}:${dTag}:${aggregateHash}:${key}`);
1756
1866
  }
1757
- sendResult({ value: result });
1867
+ sendResult({ value: result2 });
1758
1868
  break;
1759
1869
  }
1760
1870
  case "set": {
@@ -1973,6 +2083,7 @@ function createNapEnvelopeDispatcher(handlers) {
1973
2083
  };
1974
2084
  napDispatch.registerNap("relay", adapt(handlers.relay));
1975
2085
  napDispatch.registerNap("identity", adapt(handlers.identity));
2086
+ napDispatch.registerNap("count", adapt(handlers.count));
1976
2087
  napDispatch.registerNap("keys", adapt(handlers.keys));
1977
2088
  napDispatch.registerNap("media", adapt(handlers.media));
1978
2089
  napDispatch.registerNap("notify", adapt(handlers.notify));
@@ -2040,8 +2151,8 @@ function createFirewallGate(config) {
2040
2151
  const { firewallState, sessionRegistry: sessionRegistry2, hooks, fireConsent } = config;
2041
2152
  return function firewallGate(windowId, envelope, senderCap) {
2042
2153
  const obs = buildObservation(envelope, windowId, senderCap, sessionRegistry2, hooks.getFocusContext);
2043
- const result = firewallState.evaluate(obs);
2044
- const { decision, action, ruleId, reason } = result;
2154
+ const result2 = firewallState.evaluate(obs);
2155
+ const { decision, action, ruleId, reason } = result2;
2045
2156
  const napplet = obs.napplet;
2046
2157
  const opClass = obs.opClass;
2047
2158
  if (decision === "reject" || decision === "prompt") {
@@ -2069,11 +2180,11 @@ function createMessageHandler(hooks, enforceNap, dispatchNapEnvelope, firewallGa
2069
2180
  if (dotIdx === -1) return;
2070
2181
  const caps = resolveCapabilitiesNap(envelope);
2071
2182
  if (caps.senderCap) {
2072
- const result = enforceNap(windowId, caps.senderCap, envelope);
2073
- if (!result.allowed) {
2183
+ const result2 = enforceNap(windowId, caps.senderCap, envelope);
2184
+ if (!result2.allowed) {
2074
2185
  const id = envelope.id ?? "";
2075
2186
  const isStorageEnvelope = envelope.type.startsWith("storage.");
2076
- const error = formatDenialReason(result.capability);
2187
+ const error = formatDenialReason(result2.capability);
2077
2188
  const type = isStorageEnvelope ? `${envelope.type}.result` : `${envelope.type}.error`;
2078
2189
  hooks.sendToNapplet(windowId, { type, id, error });
2079
2190
  return;
@@ -2225,6 +2336,7 @@ function createRuntime(hooks) {
2225
2336
  const dispatchNapEnvelope = createNapEnvelopeDispatcher({
2226
2337
  relay: createRelayHandler({ hooks, serviceRegistry, subscriptions, eventBuffer, replayDetector }),
2227
2338
  identity: createIdentityHandler({ hooks, serviceRegistry }),
2339
+ count: createCountHandler({ hooks, serviceRegistry }),
2228
2340
  inc: incRuntime.handleMessage,
2229
2341
  ...domainHandlers
2230
2342
  });
@@ -2437,12 +2549,12 @@ function createStatePersistence() {
2437
2549
  },
2438
2550
  keys(prefix) {
2439
2551
  try {
2440
- const result = [];
2552
+ const result2 = [];
2441
2553
  for (let i4 = 0; i4 < localStorage.length; i4++) {
2442
2554
  const key = localStorage.key(i4);
2443
- if (key?.startsWith(prefix)) result.push(key);
2555
+ if (key?.startsWith(prefix)) result2.push(key);
2444
2556
  }
2445
- return result;
2557
+ return result2;
2446
2558
  } catch {
2447
2559
  return [];
2448
2560
  }
@@ -3245,6 +3357,7 @@ function buildShellCapabilities(hooks) {
3245
3357
  const domains = hooks.relayPool ? ["relay", "outbox", ...NAP_DOMAINS] : [...NAP_DOMAINS];
3246
3358
  if (hooks.upload) domains.push("upload");
3247
3359
  if (hooks.intent?.isAvailable()) domains.push("intent");
3360
+ if (hooks.services?.count) domains.push("count");
3248
3361
  if (hooks.link?.isAvailable()) domains.push("link");
3249
3362
  if (hooks.common?.isAvailable()) domains.push("common");
3250
3363
  if (hooks.lists?.isAvailable()) domains.push("lists");
@@ -3262,6 +3375,7 @@ function buildShellCapabilities(hooks) {
3262
3375
  const naps = hooks.relayPool ? ["relay", "outbox", ...NAP_DOMAINS, ...NAP_INC_PROTOCOLS] : [...NAP_DOMAINS, ...NAP_INC_PROTOCOLS];
3263
3376
  if (hooks.upload) naps.push("upload");
3264
3377
  if (hooks.intent?.isAvailable()) naps.push("intent");
3378
+ if (hooks.services?.count) naps.push("count");
3265
3379
  if (hooks.link?.isAvailable()) naps.push("link");
3266
3380
  if (hooks.common?.isAvailable()) naps.push("common");
3267
3381
  if (hooks.lists?.isAvailable()) naps.push("lists");
@@ -3981,7 +4095,7 @@ function decode(code) {
3981
4095
  }
3982
4096
  }
3983
4097
  function parseTLV(data) {
3984
- let result = {};
4098
+ let result2 = {};
3985
4099
  let rest = data;
3986
4100
  while (rest.length > 0) {
3987
4101
  let t = rest[0];
@@ -3990,10 +4104,10 @@ function parseTLV(data) {
3990
4104
  rest = rest.slice(2 + l);
3991
4105
  if (v.length < l)
3992
4106
  throw new Error(`not enough data to read on TLV ${t}`);
3993
- result[t] = result[t] || [];
3994
- result[t].push(v);
4107
+ result2[t] = result2[t] || [];
4108
+ result2[t].push(v);
3995
4109
  }
3996
- return result;
4110
+ return result2;
3997
4111
  }
3998
4112
  function npubEncode(hex) {
3999
4113
  return encodeBytes("npub", hexToBytes2(hex));
@@ -4477,8 +4591,8 @@ var _8n = /* @__PURE__ */ BigInt(8);
4477
4591
  var _9n = /* @__PURE__ */ BigInt(9);
4478
4592
  var _16n = /* @__PURE__ */ BigInt(16);
4479
4593
  function mod(a, b) {
4480
- const result = a % b;
4481
- return result >= _0n2 ? result : b + result;
4594
+ const result2 = a % b;
4595
+ return result2 >= _0n2 ? result2 : b + result2;
4482
4596
  }
4483
4597
  function pow2(x, power, modulo) {
4484
4598
  let res = x;
@@ -7194,9 +7308,9 @@ function createNotificationService(options) {
7194
7308
  };
7195
7309
  }
7196
7310
  var IDENTITY_SERVICE_VERSION = "1.0.0";
7197
- function sendProviderError(send, result, fallback, err2) {
7311
+ function sendProviderError(send, result2, fallback, err2) {
7198
7312
  send({
7199
- ...result,
7313
+ ...result2,
7200
7314
  error: err2?.message ?? fallback
7201
7315
  });
7202
7316
  }
@@ -7214,7 +7328,7 @@ function sendOptionalProviderResult(options, send, fallbackResult, errorFallback
7214
7328
  send(fallbackResult);
7215
7329
  return;
7216
7330
  }
7217
- Promise.resolve(getCurrentPubkey(options)).then((pubkey) => buildResult(pubkey)).then((result) => send(result)).catch((err2) => sendProviderError(send, fallbackResult, errorFallback, err2));
7331
+ Promise.resolve(getCurrentPubkey(options)).then((pubkey) => buildResult(pubkey)).then((result2) => send(result2)).catch((err2) => sendProviderError(send, fallbackResult, errorFallback, err2));
7218
7332
  }
7219
7333
  function sendIdentityError(send, id, typeBase, error) {
7220
7334
  send({ type: `${typeBase}.error`, id, error });
@@ -7225,21 +7339,21 @@ function sendSignerError(send, id, typeBase, fallback, err2) {
7225
7339
  function handleGetPublicKey(options, id, send) {
7226
7340
  const signer = options.getSigner();
7227
7341
  if (!signer) {
7228
- const result = {
7342
+ const result2 = {
7229
7343
  type: "identity.getPublicKey.result",
7230
7344
  id,
7231
7345
  pubkey: ""
7232
7346
  };
7233
- send(result);
7347
+ send(result2);
7234
7348
  return;
7235
7349
  }
7236
7350
  Promise.resolve(signer.getPublicKey?.()).then((pubkey) => {
7237
- const result = {
7351
+ const result2 = {
7238
7352
  type: "identity.getPublicKey.result",
7239
7353
  id,
7240
7354
  pubkey: pubkey ?? ""
7241
7355
  };
7242
- send(result);
7356
+ send(result2);
7243
7357
  }).catch((err2) => sendSignerError(send, id, "identity.getPublicKey", "getPublicKey failed", err2));
7244
7358
  }
7245
7359
  function handleGetRelays(options, id, send) {
@@ -7249,12 +7363,12 @@ function handleGetRelays(options, id, send) {
7249
7363
  return;
7250
7364
  }
7251
7365
  Promise.resolve(signer.getRelays?.() ?? {}).then((relays) => {
7252
- const result = {
7366
+ const result2 = {
7253
7367
  type: "identity.getRelays.result",
7254
7368
  id,
7255
7369
  relays
7256
7370
  };
7257
- send(result);
7371
+ send(result2);
7258
7372
  }).catch((err2) => sendSignerError(send, id, "identity.getRelays", "getRelays failed", err2));
7259
7373
  }
7260
7374
  function handleReadProvider(options, id, message, send) {
@@ -7431,7 +7545,7 @@ function createRelayPoolService(options) {
7431
7545
  }
7432
7546
  return;
7433
7547
  }
7434
- send({ type: "relay.event", subId, event: item });
7548
+ send({ type: "relay.event", subId, result: createRelayEventResultWithHints(item, relayUrls2) });
7435
7549
  }, relayUrls2);
7436
7550
  tracked.set(subKey, { handle, eoseTimer });
7437
7551
  return;
@@ -7475,6 +7589,90 @@ function createRelayPoolService(options) {
7475
7589
  }
7476
7590
  };
7477
7591
  }
7592
+ var DEFAULT_MAX_FILTERS = 16;
7593
+ function createCountService(options) {
7594
+ return {
7595
+ descriptor: {
7596
+ name: "count",
7597
+ version: "1.0.0",
7598
+ description: "Runtime-mediated NIP-01 filter counts"
7599
+ },
7600
+ handleMessage(windowId, message, send) {
7601
+ if (message.type !== "count.query") return;
7602
+ const countMessage = message;
7603
+ const id = typeof countMessage.id === "string" ? countMessage.id : "";
7604
+ const filters = countMessage.filters;
7605
+ if (!isValidFilterArray2(filters)) {
7606
+ send(result(id, { ok: false, error: "invalid-filter", reason: "count.query requires a non-empty filters array" }));
7607
+ return;
7608
+ }
7609
+ const maxFilters = options.maxFilters ?? DEFAULT_MAX_FILTERS;
7610
+ if (filters.length > maxFilters) {
7611
+ send(result(id, { ok: false, error: "too-expensive", reason: `count.query accepts at most ${maxFilters} filters` }));
7612
+ return;
7613
+ }
7614
+ for (let i4 = 0; i4 < filters.length; i4++) {
7615
+ const supported = options.isFilterSupported?.(filters[i4], i4) ?? true;
7616
+ if (supported !== true) {
7617
+ send(result(id, {
7618
+ ok: false,
7619
+ error: "unsupported-filter",
7620
+ reason: typeof supported === "string" ? supported : `filter ${i4} is not supported by this count backend`
7621
+ }));
7622
+ return;
7623
+ }
7624
+ }
7625
+ const request = {
7626
+ filters,
7627
+ windowId,
7628
+ ...isPlainObject2(countMessage.options) ? { options: countMessage.options } : {}
7629
+ };
7630
+ Promise.resolve(options.count(request)).then((countResult) => {
7631
+ send(result(id, normalizeCountResult2(countResult)));
7632
+ }).catch((error) => {
7633
+ send(result(id, {
7634
+ ok: false,
7635
+ error: "count-unavailable",
7636
+ reason: error instanceof Error ? error.message : "count backend failed"
7637
+ }));
7638
+ });
7639
+ }
7640
+ };
7641
+ }
7642
+ function result(id, value) {
7643
+ return {
7644
+ type: "count.query.result",
7645
+ id,
7646
+ ok: value.ok,
7647
+ ...typeof value.count === "number" ? { count: value.count } : {},
7648
+ ...typeof value.approximate === "boolean" ? { approximate: value.approximate } : {},
7649
+ ...typeof value.hll === "string" ? { hll: value.hll } : {},
7650
+ ...Array.isArray(value.relays) ? { relays: value.relays.filter((relay) => typeof relay === "string") } : {},
7651
+ ...typeof value.error === "string" ? { error: value.error } : {},
7652
+ ...typeof value.reason === "string" ? { reason: value.reason } : {}
7653
+ };
7654
+ }
7655
+ function normalizeCountResult2(value) {
7656
+ if (typeof value === "number") {
7657
+ return Number.isFinite(value) && value >= 0 ? { ok: true, count: Math.floor(value), approximate: false } : { ok: false, error: "count-unavailable", reason: "count backend returned an invalid count" };
7658
+ }
7659
+ const count = typeof value.count === "number" && Number.isFinite(value.count) && value.count >= 0 ? Math.floor(value.count) : void 0;
7660
+ return {
7661
+ ok: value.ok === true,
7662
+ ...count !== void 0 ? { count } : {},
7663
+ ...typeof value.approximate === "boolean" ? { approximate: value.approximate } : {},
7664
+ ...typeof value.hll === "string" ? { hll: value.hll } : {},
7665
+ ...Array.isArray(value.relays) ? { relays: value.relays } : {},
7666
+ ...typeof value.error === "string" ? { error: value.error } : {},
7667
+ ...typeof value.reason === "string" ? { reason: value.reason } : {}
7668
+ };
7669
+ }
7670
+ function isValidFilterArray2(value) {
7671
+ return Array.isArray(value) && value.length > 0 && value.every((filter) => isPlainObject2(filter));
7672
+ }
7673
+ function isPlainObject2(value) {
7674
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7675
+ }
7478
7676
  var KEYS_SERVICE_VERSION = "1.2.0";
7479
7677
  var MODIFIER_ALIASES = {
7480
7678
  ctrl: "ctrl",
@@ -7588,13 +7786,13 @@ function createKeysService(options = {}) {
7588
7786
  return;
7589
7787
  }
7590
7788
  }
7591
- const result = {
7789
+ const result2 = {
7592
7790
  type: "keys.registerAction.result",
7593
7791
  id: m.id,
7594
7792
  actionId: m.action.id,
7595
7793
  ...m.action.defaultKey ? { binding: m.action.defaultKey } : {}
7596
7794
  };
7597
- send(result);
7795
+ send(result2);
7598
7796
  return;
7599
7797
  }
7600
7798
  case "keys.unregisterAction": {
@@ -7735,13 +7933,13 @@ function createKeysService(options = {}) {
7735
7933
  return;
7736
7934
  }
7737
7935
  }
7738
- const result = {
7936
+ const result2 = {
7739
7937
  type: "keys.registerAction.result",
7740
7938
  id: m.id,
7741
7939
  actionId: m.action.id,
7742
7940
  ...m.action.defaultKey ? { binding: m.action.defaultKey } : {}
7743
7941
  };
7744
- send(result);
7942
+ send(result2);
7745
7943
  return;
7746
7944
  }
7747
7945
  case "keys.unregisterAction": {
@@ -8166,12 +8364,12 @@ function createNotifyService(options = {}) {
8166
8364
  case "notify.send": {
8167
8365
  const m = message;
8168
8366
  options.onSend?.(windowId, m);
8169
- const result = {
8367
+ const result2 = {
8170
8368
  type: "notify.send.result",
8171
8369
  id: m.id,
8172
8370
  notificationId: gen()
8173
8371
  };
8174
- send(result);
8372
+ send(result2);
8175
8373
  return;
8176
8374
  }
8177
8375
  case "notify.dismiss":
@@ -8180,12 +8378,12 @@ function createNotifyService(options = {}) {
8180
8378
  return;
8181
8379
  case "notify.permission.request": {
8182
8380
  const m = message;
8183
- const result = {
8381
+ const result2 = {
8184
8382
  type: "notify.permission.result",
8185
8383
  id: m.id,
8186
8384
  granted: defaultGrant
8187
8385
  };
8188
- send(result);
8386
+ send(result2);
8189
8387
  return;
8190
8388
  }
8191
8389
  default: {
@@ -8224,12 +8422,12 @@ function createThemeService(options = {}) {
8224
8422
  handleMessage(_windowId, message, send) {
8225
8423
  const id = message.id ?? "";
8226
8424
  if (message.type === "theme.get") {
8227
- const result = {
8425
+ const result2 = {
8228
8426
  type: "theme.get.result",
8229
8427
  id,
8230
8428
  theme: currentTheme
8231
8429
  };
8232
- send(result);
8430
+ send(result2);
8233
8431
  return;
8234
8432
  }
8235
8433
  send({
@@ -8351,14 +8549,14 @@ function createConfigService(options) {
8351
8549
  case "config.registerSchema": {
8352
8550
  const m = message;
8353
8551
  const validation = options.registerSchema ? options.registerSchema(windowId, m.schema, m.version) : validateCoreSubset(m.schema);
8354
- const result = validation.ok ? { type: "config.registerSchema.result", id: m.id, ok: true } : {
8552
+ const result2 = validation.ok ? { type: "config.registerSchema.result", id: m.id, ok: true } : {
8355
8553
  type: "config.registerSchema.result",
8356
8554
  id: m.id,
8357
8555
  ok: false,
8358
8556
  code: validation.code,
8359
8557
  error: validation.error
8360
8558
  };
8361
- send(result);
8559
+ send(result2);
8362
8560
  return;
8363
8561
  }
8364
8562
  case "config.openSettings": {
@@ -8725,45 +8923,42 @@ function buildEventQuery(eventId, eventOptions) {
8725
8923
  if (eventOptions?.timeoutMs !== void 0) queryOptions.timeoutMs = eventOptions.timeoutMs;
8726
8924
  return { filter, queryOptions };
8727
8925
  }
8728
- function eventResultFromQuery(eventId, result) {
8729
- const event = result.events.find((candidate) => candidate.id === eventId);
8730
- const eventResult = {
8731
- relays: event ? result.relays[eventId] ?? [] : []
8732
- };
8733
- if (event) eventResult.event = event;
8734
- if (!event) eventResult.error = result.error ?? "not found";
8735
- else if (result.error !== void 0) eventResult.error = result.error;
8736
- if (result.incomplete !== void 0) eventResult.incomplete = result.incomplete;
8926
+ function eventResultFromQuery(eventId, result2) {
8927
+ const relayResult = result2.events.find((candidate) => candidate.event.id === eventId);
8928
+ const eventResult = {};
8929
+ if (relayResult) eventResult.result = relayResult;
8930
+ if (!relayResult) eventResult.error = result2.error ?? "not found";
8931
+ else if (result2.error !== void 0) eventResult.error = result2.error;
8932
+ if (result2.incomplete !== void 0) eventResult.incomplete = result2.incomplete;
8737
8933
  return eventResult;
8738
8934
  }
8739
8935
  function fallbackGetEvent(router, eventId, eventOptions) {
8740
8936
  const { filter, queryOptions } = buildEventQuery(eventId, eventOptions);
8741
- return router.query([filter], queryOptions).then((result) => eventResultFromQuery(eventId, result));
8937
+ return router.query([filter], queryOptions).then((result2) => eventResultFromQuery(eventId, result2));
8742
8938
  }
8743
- function sendEventResult(id, eventId, result, send) {
8744
- if (result.event && result.event.id !== eventId) {
8745
- send({ type: "outbox.getEvent.result", id, relays: [], error: "not found" });
8939
+ function sendEventResult(id, eventId, result2, send) {
8940
+ if (result2.result && result2.result.event.id !== eventId) {
8941
+ send({ type: "outbox.getEvent.result", id, error: "not found" });
8746
8942
  return;
8747
8943
  }
8748
8944
  send({
8749
8945
  type: "outbox.getEvent.result",
8750
8946
  id,
8751
- ...result.event === void 0 ? {} : { event: result.event },
8752
- relays: result.relays,
8753
- ...result.incomplete === void 0 ? {} : { incomplete: result.incomplete },
8754
- ...result.error === void 0 ? {} : { error: result.error }
8947
+ ...result2.result === void 0 ? {} : { result: result2.result },
8948
+ ...result2.incomplete === void 0 ? {} : { incomplete: result2.incomplete },
8949
+ ...result2.error === void 0 ? {} : { error: result2.error }
8755
8950
  });
8756
8951
  }
8757
8952
  function handleGetEvent(router, msg, send) {
8758
8953
  const m = msg;
8759
8954
  const id = m.id ?? "";
8760
8955
  if (typeof m.eventId !== "string" || m.eventId.length === 0) {
8761
- send({ type: "outbox.getEvent.result", id, relays: [], error: "invalid filter" });
8956
+ send({ type: "outbox.getEvent.result", id, error: "invalid filter" });
8762
8957
  return;
8763
8958
  }
8764
8959
  const getEvent = router.getEvent ?? ((eventId, eventOptions) => fallbackGetEvent(router, eventId, eventOptions));
8765
- void getEvent(m.eventId, m.options).then((result) => sendEventResult(id, m.eventId, result, send)).catch(
8766
- (err2) => send({ type: "outbox.getEvent.result", id, relays: [], error: toErrorMessage(err2) })
8960
+ void getEvent(m.eventId, m.options).then((result2) => sendEventResult(id, m.eventId, result2, send)).catch(
8961
+ (err2) => send({ type: "outbox.getEvent.result", id, error: toErrorMessage(err2) })
8767
8962
  );
8768
8963
  }
8769
8964
  function createOutboxService(options) {
@@ -8777,20 +8972,19 @@ function createOutboxService(options) {
8777
8972
  const id = m.id ?? "";
8778
8973
  const filters = normalizeFilters(m.filters);
8779
8974
  if (!filters) {
8780
- send({ type: "outbox.query.result", id, events: [], relays: {}, error: "invalid filter" });
8975
+ send({ type: "outbox.query.result", id, events: [], error: "invalid filter" });
8781
8976
  return;
8782
8977
  }
8783
8978
  void router.query(filters, m.options).then(
8784
- (result) => send({
8979
+ (result2) => send({
8785
8980
  type: "outbox.query.result",
8786
8981
  id,
8787
- events: result.events,
8788
- relays: result.relays,
8789
- ...result.incomplete === void 0 ? {} : { incomplete: result.incomplete },
8790
- ...result.error === void 0 ? {} : { error: result.error }
8982
+ events: result2.events,
8983
+ ...result2.incomplete === void 0 ? {} : { incomplete: result2.incomplete },
8984
+ ...result2.error === void 0 ? {} : { error: result2.error }
8791
8985
  })
8792
8986
  ).catch(
8793
- (err2) => send({ type: "outbox.query.result", id, events: [], relays: {}, error: toErrorMessage(err2) })
8987
+ (err2) => send({ type: "outbox.query.result", id, events: [], error: toErrorMessage(err2) })
8794
8988
  );
8795
8989
  }
8796
8990
  function handleSubscribe2(windowId, msg, send) {
@@ -8806,8 +9000,7 @@ function createOutboxService(options) {
8806
9000
  return;
8807
9001
  }
8808
9002
  const sink = {
8809
- event: (event, relay) => send({ type: "outbox.event", subId, event, ...relay === void 0 ? {} : { relay } }),
8810
- eose: () => send({ type: "outbox.eose", subId }),
9003
+ event: (result2) => send({ type: "outbox.event", subId, result: result2 }),
8811
9004
  closed: (reason) => {
8812
9005
  subscriptions.delete(subKey);
8813
9006
  send({ type: "outbox.closed", subId, ...reason === void 0 ? {} : { reason } });
@@ -8832,14 +9025,14 @@ function createOutboxService(options) {
8832
9025
  return;
8833
9026
  }
8834
9027
  void router.publish(m.event, m.options).then(
8835
- (result) => send({
9028
+ (result2) => send({
8836
9029
  type: "outbox.publish.result",
8837
9030
  id,
8838
- ok: result.ok,
8839
- ...result.event === void 0 ? {} : { event: result.event },
8840
- ...result.eventId === void 0 ? {} : { eventId: result.eventId },
8841
- ...result.relays === void 0 ? {} : { relays: result.relays },
8842
- ...result.error === void 0 ? {} : { error: result.error }
9031
+ ok: result2.ok,
9032
+ ...result2.event === void 0 ? {} : { event: result2.event },
9033
+ ...result2.eventId === void 0 ? {} : { eventId: result2.eventId },
9034
+ ...result2.relays === void 0 ? {} : { relays: result2.relays },
9035
+ ...result2.error === void 0 ? {} : { error: result2.error }
8843
9036
  })
8844
9037
  ).catch(
8845
9038
  (err2) => send({ type: "outbox.publish.result", id, ok: false, error: toErrorMessage(err2) })
@@ -8957,8 +9150,8 @@ function createUploadService(options) {
8957
9150
  send({ type: "upload.status.changed", status: stamped });
8958
9151
  }
8959
9152
  };
8960
- void uploader.upload(request, ctx).then((result) => {
8961
- const stamped = { ...result, uploadId };
9153
+ void uploader.upload(request, ctx).then((result2) => {
9154
+ const stamped = { ...result2, uploadId };
8962
9155
  const entry = entries.get(key);
8963
9156
  if (entry) entry.status = { ...stamped, updatedAt: now2() };
8964
9157
  send({ type: "upload.upload.result", id, result: stamped });
@@ -9070,7 +9263,7 @@ function createIntentService(options) {
9070
9263
  send,
9071
9264
  "intent.invoke.result",
9072
9265
  id,
9073
- (result) => ({ type: "intent.invoke.result", id, result })
9266
+ (result2) => ({ type: "intent.invoke.result", id, result: result2 })
9074
9267
  );
9075
9268
  }
9076
9269
  function handleAvailable(msg, send) {
@@ -9258,12 +9451,12 @@ async function handleOpen(options, windowId, message, send) {
9258
9451
  return;
9259
9452
  }
9260
9453
  try {
9261
- const result = await options.open({ windowId, url: parsed, options: message.options });
9454
+ const result2 = await options.open({ windowId, url: parsed, options: message.options });
9262
9455
  send({
9263
9456
  type: "link.open.result",
9264
9457
  id: message.id,
9265
- status: result.status,
9266
- ...result.status === "denied" ? { error: "blocked-by-policy" } : {}
9458
+ status: result2.status,
9459
+ ...result2.status === "denied" ? { error: "blocked-by-policy" } : {}
9267
9460
  });
9268
9461
  } catch {
9269
9462
  send(denied(message.id, "blocked-by-policy"));
@@ -9333,7 +9526,7 @@ function settleMutation(hook, message, context, send, resultType) {
9333
9526
  () => hook(message.list, message.items, message.options, context),
9334
9527
  send,
9335
9528
  (error) => ({ type: resultType, id: message.id, ok: false, error: "list-unavailable", reason: error }),
9336
- (result) => ({ type: resultType, id: message.id, ...result })
9529
+ (result2) => ({ type: resultType, id: message.id, ...result2 })
9337
9530
  );
9338
9531
  }
9339
9532
  function createListsService(options = {}) {
@@ -9417,7 +9610,7 @@ function createSerialService(options = {}) {
9417
9610
  () => options.open(serialMessage.request, context),
9418
9611
  send,
9419
9612
  (error) => ({ type: "serial.open.result", id, error }),
9420
- (result) => ({ type: "serial.open.result", id, session: result.session })
9613
+ (result2) => ({ type: "serial.open.result", id, session: result2.session })
9421
9614
  );
9422
9615
  return;
9423
9616
  }
@@ -9512,7 +9705,7 @@ function createBleService(options = {}) {
9512
9705
  () => options.open(bleMessage.request, context),
9513
9706
  send,
9514
9707
  (error) => ({ type: "ble.open.result", id, error }),
9515
- (result) => ({ type: "ble.open.result", id, session: result.session })
9708
+ (result2) => ({ type: "ble.open.result", id, session: result2.session })
9516
9709
  );
9517
9710
  return;
9518
9711
  }
@@ -9663,7 +9856,7 @@ function createWebrtcService(options = {}) {
9663
9856
  () => options.open(webrtcMessage.request, context),
9664
9857
  send,
9665
9858
  (error) => ({ type: "webrtc.open.result", id, error }),
9666
- (result) => ({ type: "webrtc.open.result", id, session: result.session })
9859
+ (result2) => ({ type: "webrtc.open.result", id, session: result2.session })
9667
9860
  );
9668
9861
  return;
9669
9862
  }
@@ -9803,7 +9996,7 @@ function decodeNrelay(value) {
9803
9996
  function convertBits(data, fromBits, toBits, pad2) {
9804
9997
  let acc = 0;
9805
9998
  let bits = 0;
9806
- const result = [];
9999
+ const result2 = [];
9807
10000
  const maxv = (1 << toBits) - 1;
9808
10001
  const maxAcc = (1 << fromBits + toBits - 1) - 1;
9809
10002
  for (const value of data) {
@@ -9812,15 +10005,15 @@ function convertBits(data, fromBits, toBits, pad2) {
9812
10005
  bits += fromBits;
9813
10006
  while (bits >= toBits) {
9814
10007
  bits -= toBits;
9815
- result.push(acc >> bits & maxv);
10008
+ result2.push(acc >> bits & maxv);
9816
10009
  }
9817
10010
  }
9818
10011
  if (pad2) {
9819
- if (bits > 0) result.push(acc << toBits - bits & maxv);
10012
+ if (bits > 0) result2.push(acc << toBits - bits & maxv);
9820
10013
  } else if (bits >= fromBits || (acc << toBits - bits & maxv) !== 0) {
9821
10014
  throw new Error("invalid bech32 padding");
9822
10015
  }
9823
- return result;
10016
+ return result2;
9824
10017
  }
9825
10018
  function settle5(call, send, okFalse, onValue) {
9826
10019
  let pending;
@@ -9840,14 +10033,14 @@ function createCommonService(options = {}) {
9840
10033
  const context = { windowId };
9841
10034
  if (message.type === "common.encodeNip19") {
9842
10035
  const input = message.input;
9843
- const result = input ? encodeNip19(input) : { ok: false, error: "invalid input" };
9844
- send({ type: "common.encodeNip19.result", id, ...result });
10036
+ const result2 = input ? encodeNip19(input) : { ok: false, error: "invalid input" };
10037
+ send({ type: "common.encodeNip19.result", id, ...result2 });
9845
10038
  return;
9846
10039
  }
9847
10040
  if (message.type === "common.decodeNip19") {
9848
10041
  const value = message.value;
9849
- const result = typeof value === "string" ? decodeNip19Value(value) : { ok: false, error: "invalid value" };
9850
- send({ type: "common.decodeNip19.result", id, ...result });
10042
+ const result2 = typeof value === "string" ? decodeNip19Value(value) : { ok: false, error: "invalid value" };
10043
+ send({ type: "common.decodeNip19.result", id, ...result2 });
9851
10044
  return;
9852
10045
  }
9853
10046
  if (message.type === "common.getProfile") {
@@ -9860,7 +10053,7 @@ function createCommonService(options = {}) {
9860
10053
  () => options.getProfile(target, context),
9861
10054
  send,
9862
10055
  (error) => ({ type: "common.getProfile.result", id, ok: false, pubkey: target ?? "", error }),
9863
- (result) => ({ type: "common.getProfile.result", id, ...result })
10056
+ (result2) => ({ type: "common.getProfile.result", id, ...result2 })
9864
10057
  );
9865
10058
  return;
9866
10059
  }
@@ -9873,7 +10066,7 @@ function createCommonService(options = {}) {
9873
10066
  () => options.follows(context),
9874
10067
  send,
9875
10068
  (error) => ({ type: "common.follows.result", id, ok: false, pubkeys: [], error }),
9876
- (result) => ({ type: "common.follows.result", id, ...result })
10069
+ (result2) => ({ type: "common.follows.result", id, ...result2 })
9877
10070
  );
9878
10071
  return;
9879
10072
  }
@@ -9897,7 +10090,7 @@ function createCommonService(options = {}) {
9897
10090
  () => options.react(m.targetEventId, m.reaction, m.customEmojiHref, context),
9898
10091
  send,
9899
10092
  (error) => ({ type: "common.react.result", id, ok: false, error }),
9900
- (result) => ({ type: "common.react.result", id, ...result })
10093
+ (result2) => ({ type: "common.react.result", id, ...result2 })
9901
10094
  );
9902
10095
  return;
9903
10096
  }
@@ -9911,7 +10104,7 @@ function createCommonService(options = {}) {
9911
10104
  () => options.report(m.target, m.reason, m.text, context),
9912
10105
  send,
9913
10106
  (error) => ({ type: "common.report.result", id, ok: false, error }),
9914
- (result) => ({ type: "common.report.result", id, ...result })
10107
+ (result2) => ({ type: "common.report.result", id, ...result2 })
9915
10108
  );
9916
10109
  return;
9917
10110
  }
@@ -9934,7 +10127,7 @@ function settleAction(action, pubkeys, context, send, resultType, id, unavailabl
9934
10127
  () => action(pubkeys, context),
9935
10128
  send,
9936
10129
  (error) => ({ type: resultType, id, ok: false, error }),
9937
- (result) => ({ type: resultType, id, ...result })
10130
+ (result2) => ({ type: resultType, id, ...result2 })
9938
10131
  );
9939
10132
  }
9940
10133
 
@@ -10183,6 +10376,13 @@ function createMemoryRelayPool(getSimulation, confirmRequest) {
10183
10376
  } };
10184
10377
  }
10185
10378
  };
10379
+ },
10380
+ count(_relayUrls, filters) {
10381
+ const seen = /* @__PURE__ */ new Set();
10382
+ for (const event of events) {
10383
+ if (matchesAnyFilter2(event, filters)) seen.add(event.id);
10384
+ }
10385
+ return seen.size;
10186
10386
  }
10187
10387
  };
10188
10388
  }
@@ -10241,7 +10441,7 @@ function createDevUploader(getSimulation) {
10241
10441
  throw new Error("upload simulation is disabled");
10242
10442
  }
10243
10443
  const size = request.data instanceof Blob ? request.data.size : request.data.byteLength;
10244
- const result = {
10444
+ const result2 = {
10245
10445
  ok: true,
10246
10446
  uploadId: ctx.uploadId,
10247
10447
  status: "complete",
@@ -10250,8 +10450,8 @@ function createDevUploader(getSimulation) {
10250
10450
  size,
10251
10451
  mimeType: request.mimeType ?? (request.data instanceof Blob ? request.data.type : void 0)
10252
10452
  };
10253
- ctx.onStatus({ ...result, updatedAt: Date.now() });
10254
- return result;
10453
+ ctx.onStatus({ ...result2, updatedAt: Date.now() });
10454
+ return result2;
10255
10455
  },
10256
10456
  async status(uploadId) {
10257
10457
  const simulation = getSimulation();
@@ -10319,13 +10519,13 @@ function createOutboxRouter(pool, getSimulation, confirmRequest, signerProvider)
10319
10519
  error: () => resolve(out)
10320
10520
  });
10321
10521
  });
10322
- return { events, relays: Object.fromEntries(events.map((event) => [event.id, relayUrls])) };
10522
+ return { events: events.map((event) => createRelayEventResultWithHints(event, relayUrls)) };
10323
10523
  },
10324
10524
  subscribe(filters, _options, sink) {
10325
10525
  const relayUrls = getRelayUrls(getSimulation());
10326
10526
  const sub = pool.subscription(relayUrls, filters).subscribe((item) => {
10327
- if (item === "EOSE") sink.eose();
10328
- else sink.event(item, relayUrls[0]);
10527
+ if (item === "EOSE") return;
10528
+ sink.event(createRelayEventResultWithHints(item, relayUrls[0] ? [relayUrls[0]] : relayUrls));
10329
10529
  });
10330
10530
  return { close: () => sub.unsubscribe() };
10331
10531
  },
@@ -10426,6 +10626,25 @@ function createDevServices(pool, getSimulation, onThemeService, confirmRequest,
10426
10626
  });
10427
10627
  services.outbox = createOutboxService({ router: createOutboxRouter(pool, getSimulation, confirmRequest, signerProvider) });
10428
10628
  }
10629
+ if (getSimulation().capabilities.domains.count && typeof pool.count === "function") {
10630
+ services.count = createCountService({
10631
+ count: async ({ filters }) => {
10632
+ const relays = getRelayUrls(getSimulation());
10633
+ return {
10634
+ ok: true,
10635
+ count: await pool.count(relays, filters),
10636
+ approximate: false,
10637
+ relays
10638
+ };
10639
+ },
10640
+ isFilterSupported: (filter) => {
10641
+ if (Object.keys(filter).length === 0) {
10642
+ return "broad empty filters are too expensive for the Paja count backend";
10643
+ }
10644
+ return true;
10645
+ }
10646
+ });
10647
+ }
10429
10648
  if (getSimulation().capabilities.domains.identity) {
10430
10649
  services.identity = createIdentityService({ getSigner: () => createRuntimeSigner(getSimulation, confirmRequest, signerProvider) });
10431
10650
  }
@@ -11020,8 +11239,8 @@ function binarySearch(arr, compare) {
11020
11239
  return [start, false];
11021
11240
  }
11022
11241
  function mergeReverseSortedLists(list1, list2) {
11023
- const result = new Array(list1.length + list2.length);
11024
- result.length = 0;
11242
+ const result2 = new Array(list1.length + list2.length);
11243
+ result2.length = 0;
11025
11244
  let i1 = 0;
11026
11245
  let i22 = 0;
11027
11246
  let sameTimestampIds = [];
@@ -11034,40 +11253,40 @@ function mergeReverseSortedLists(list1, list2) {
11034
11253
  next = list2[i22];
11035
11254
  i22++;
11036
11255
  }
11037
- if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {
11256
+ if (result2.length > 0 && result2[result2.length - 1].created_at === next.created_at) {
11038
11257
  if (sameTimestampIds.includes(next.id))
11039
11258
  continue;
11040
11259
  } else {
11041
11260
  sameTimestampIds.length = 0;
11042
11261
  }
11043
- result.push(next);
11262
+ result2.push(next);
11044
11263
  sameTimestampIds.push(next.id);
11045
11264
  }
11046
11265
  while (i1 < list1.length) {
11047
11266
  const next = list1[i1];
11048
11267
  i1++;
11049
- if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {
11268
+ if (result2.length > 0 && result2[result2.length - 1].created_at === next.created_at) {
11050
11269
  if (sameTimestampIds.includes(next.id))
11051
11270
  continue;
11052
11271
  } else {
11053
11272
  sameTimestampIds.length = 0;
11054
11273
  }
11055
- result.push(next);
11274
+ result2.push(next);
11056
11275
  sameTimestampIds.push(next.id);
11057
11276
  }
11058
11277
  while (i22 < list2.length) {
11059
11278
  const next = list2[i22];
11060
11279
  i22++;
11061
- if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {
11280
+ if (result2.length > 0 && result2[result2.length - 1].created_at === next.created_at) {
11062
11281
  if (sameTimestampIds.includes(next.id))
11063
11282
  continue;
11064
11283
  } else {
11065
11284
  sameTimestampIds.length = 0;
11066
11285
  }
11067
- result.push(next);
11286
+ result2.push(next);
11068
11287
  sameTimestampIds.push(next.id);
11069
11288
  }
11070
- return result;
11289
+ return result2;
11071
11290
  }
11072
11291
  var JS2 = class {
11073
11292
  generateSecretKey() {
@@ -11519,7 +11738,7 @@ function decode2(code) {
11519
11738
  }
11520
11739
  }
11521
11740
  function parseTLV2(data) {
11522
- let result = {};
11741
+ let result2 = {};
11523
11742
  let rest = data;
11524
11743
  while (rest.length > 0) {
11525
11744
  let t = rest[0];
@@ -11528,10 +11747,10 @@ function parseTLV2(data) {
11528
11747
  rest = rest.slice(2 + l);
11529
11748
  if (v.length < l)
11530
11749
  throw new Error(`not enough data to read on TLV ${t}`);
11531
- result[t] = result[t] || [];
11532
- result[t].push(v);
11750
+ result2[t] = result2[t] || [];
11751
+ result2[t].push(v);
11533
11752
  }
11534
- return result;
11753
+ return result2;
11535
11754
  }
11536
11755
  function nsecEncode(key) {
11537
11756
  return encodeBytes2("nsec", key);
@@ -11682,7 +11901,7 @@ __export(nip10_exports, {
11682
11901
  parse: () => parse
11683
11902
  });
11684
11903
  function parse(event) {
11685
- const result = {
11904
+ const result2 = {
11686
11905
  reply: void 0,
11687
11906
  root: void 0,
11688
11907
  mentions: [],
@@ -11701,15 +11920,15 @@ function parse(event) {
11701
11920
  author: eTagAuthor
11702
11921
  };
11703
11922
  if (eTagMarker === "root") {
11704
- result.root = eventPointer;
11923
+ result2.root = eventPointer;
11705
11924
  continue;
11706
11925
  }
11707
11926
  if (eTagMarker === "reply") {
11708
- result.reply = eventPointer;
11927
+ result2.reply = eventPointer;
11709
11928
  continue;
11710
11929
  }
11711
11930
  if (eTagMarker === "mention") {
11712
- result.mentions.push(eventPointer);
11931
+ result2.mentions.push(eventPointer);
11713
11932
  continue;
11714
11933
  }
11715
11934
  if (!maybeParent) {
@@ -11717,40 +11936,40 @@ function parse(event) {
11717
11936
  } else {
11718
11937
  maybeRoot = eventPointer;
11719
11938
  }
11720
- result.mentions.push(eventPointer);
11939
+ result2.mentions.push(eventPointer);
11721
11940
  continue;
11722
11941
  }
11723
11942
  if (tag[0] === "q" && tag[1]) {
11724
11943
  const [_, eTagEventId, eTagRelayUrl] = tag;
11725
- result.quotes.push({
11944
+ result2.quotes.push({
11726
11945
  id: eTagEventId,
11727
11946
  relays: eTagRelayUrl ? [eTagRelayUrl] : []
11728
11947
  });
11729
11948
  }
11730
11949
  if (tag[0] === "p" && tag[1]) {
11731
- result.profiles.push({
11950
+ result2.profiles.push({
11732
11951
  pubkey: tag[1],
11733
11952
  relays: tag[2] ? [tag[2]] : []
11734
11953
  });
11735
11954
  continue;
11736
11955
  }
11737
11956
  }
11738
- if (!result.root) {
11739
- result.root = maybeRoot || maybeParent || result.reply;
11957
+ if (!result2.root) {
11958
+ result2.root = maybeRoot || maybeParent || result2.reply;
11740
11959
  }
11741
- if (!result.reply) {
11742
- result.reply = maybeParent || result.root;
11960
+ if (!result2.reply) {
11961
+ result2.reply = maybeParent || result2.root;
11743
11962
  }
11744
11963
  ;
11745
- [result.reply, result.root].forEach((ref) => {
11964
+ [result2.reply, result2.root].forEach((ref) => {
11746
11965
  if (!ref)
11747
11966
  return;
11748
- let idx = result.mentions.indexOf(ref);
11967
+ let idx = result2.mentions.indexOf(ref);
11749
11968
  if (idx !== -1) {
11750
- result.mentions.splice(idx, 1);
11969
+ result2.mentions.splice(idx, 1);
11751
11970
  }
11752
11971
  if (ref.author) {
11753
- let author = result.profiles.find((p) => p.pubkey === ref.author);
11972
+ let author = result2.profiles.find((p) => p.pubkey === ref.author);
11754
11973
  if (author && author.relays) {
11755
11974
  if (!ref.relays) {
11756
11975
  ref.relays = [];
@@ -11763,9 +11982,9 @@ function parse(event) {
11763
11982
  }
11764
11983
  }
11765
11984
  });
11766
- result.mentions.forEach((ref) => {
11985
+ result2.mentions.forEach((ref) => {
11767
11986
  if (ref.author) {
11768
- let author = result.profiles.find((p) => p.pubkey === ref.author);
11987
+ let author = result2.profiles.find((p) => p.pubkey === ref.author);
11769
11988
  if (author && author.relays) {
11770
11989
  if (!ref.relays) {
11771
11990
  ref.relays = [];
@@ -11778,7 +11997,7 @@ function parse(event) {
11778
11997
  }
11779
11998
  }
11780
11999
  });
11781
- return result;
12000
+ return result2;
11782
12001
  }
11783
12002
  var nip11_exports = {};
11784
12003
  __export(nip11_exports, {
@@ -13447,11 +13666,11 @@ function createNipEncryptionMethods(state, prefix) {
13447
13666
  }
13448
13667
  async function connectNip46State(state, secret) {
13449
13668
  await openWebSocket(state);
13450
- const result = await sendRequestWithId(state, crypto.randomUUID(), "connect", [state.bunkerPubkey, secret ?? ""]);
13451
- if (typeof result === "string") {
13452
- state.authorizedPubkey = result;
13453
- } else if (result && typeof result === "object" && "pubkey" in result) {
13454
- state.authorizedPubkey = result.pubkey;
13669
+ const result2 = await sendRequestWithId(state, crypto.randomUUID(), "connect", [state.bunkerPubkey, secret ?? ""]);
13670
+ if (typeof result2 === "string") {
13671
+ state.authorizedPubkey = result2;
13672
+ } else if (result2 && typeof result2 === "object" && "pubkey" in result2) {
13673
+ state.authorizedPubkey = result2.pubkey;
13455
13674
  } else {
13456
13675
  const pkResult = await sendRequestWithId(state, crypto.randomUUID(), "get_public_key", []);
13457
13676
  state.authorizedPubkey = pkResult;
@@ -13465,11 +13684,11 @@ function createNip46Signer(state) {
13465
13684
  return sendRequestWithId(state, crypto.randomUUID(), "get_public_key", []);
13466
13685
  },
13467
13686
  signEvent: async (event) => {
13468
- const result = await sendRequestWithId(state, crypto.randomUUID(), "sign_event", [JSON.stringify(event)]);
13469
- if (typeof result === "string") {
13470
- return JSON.parse(result);
13687
+ const result2 = await sendRequestWithId(state, crypto.randomUUID(), "sign_event", [JSON.stringify(event)]);
13688
+ if (typeof result2 === "string") {
13689
+ return JSON.parse(result2);
13471
13690
  }
13472
- return result;
13691
+ return result2;
13473
13692
  },
13474
13693
  nip04: createNipEncryptionMethods(state, "nip04"),
13475
13694
  nip44: createNipEncryptionMethods(state, "nip44")
@@ -13617,6 +13836,7 @@ var PAJA_SIMULATION_DOMAINS = [
13617
13836
  "media",
13618
13837
  "upload",
13619
13838
  "intent",
13839
+ "count",
13620
13840
  "link",
13621
13841
  "common",
13622
13842
  "lists",
@@ -13729,8 +13949,8 @@ function installPajaOriginRegistryProxy(originRegistry2, stateRef) {
13729
13949
  };
13730
13950
  const originalGetWindowId = originRegistry2.getWindowId.bind(originRegistry2);
13731
13951
  originRegistry2.getWindowId = (win) => {
13732
- const result = originalGetWindowId(win);
13733
- if (result) return result;
13952
+ const result2 = originalGetWindowId(win);
13953
+ if (result2) return result2;
13734
13954
  const real = proxyToReal.get(win);
13735
13955
  return real ? originalGetWindowId(real) : void 0;
13736
13956
  };
@@ -14205,11 +14425,11 @@ var AbstractRelay = class {
14205
14425
  }
14206
14426
  async pingpong() {
14207
14427
  if (this.ws?.readyState === 1) {
14208
- const result = await Promise.any([
14428
+ const result2 = await Promise.any([
14209
14429
  this.ws && this.ws.ping && this.ws.once ? this.waitForPingPong() : this.waitForDummyReq(),
14210
14430
  new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
14211
14431
  ]);
14212
- if (!result) {
14432
+ if (!result2) {
14213
14433
  if (this.ws?.readyState === this._WebSocket.OPEN) {
14214
14434
  this.ws?.close();
14215
14435
  }