@kehto/paja 0.3.7 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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);
@@ -1498,6 +1503,65 @@ function createIdentityHandler(context) {
1498
1503
  }
1499
1504
  };
1500
1505
  }
1506
+ function createCountHandler(context) {
1507
+ return function handleCountMessage(windowId, msg) {
1508
+ if (msg.type !== "count.query") return;
1509
+ const message = msg;
1510
+ const id = typeof message.id === "string" ? message.id : "";
1511
+ const filters = message.filters;
1512
+ if (!isValidFilterArray(filters)) {
1513
+ sendCountError(context.hooks, windowId, id, "invalid-filter", "count.query requires a non-empty filters array");
1514
+ return;
1515
+ }
1516
+ if (message.options !== void 0 && !isPlainObject(message.options)) {
1517
+ sendCountError(context.hooks, windowId, id, "invalid-filter", "count.query options must be an object when present");
1518
+ return;
1519
+ }
1520
+ const service = context.serviceRegistry["count"];
1521
+ if (!service) {
1522
+ sendCountError(
1523
+ context.hooks,
1524
+ windowId,
1525
+ id,
1526
+ "count-unavailable",
1527
+ "count service unavailable; refusing to emulate counts by fetching event payloads"
1528
+ );
1529
+ return;
1530
+ }
1531
+ service.handleMessage(windowId, msg, (resp) => {
1532
+ context.hooks.sendToNapplet(windowId, normalizeCountResult(resp, id));
1533
+ });
1534
+ };
1535
+ }
1536
+ function normalizeCountResult(resp, id) {
1537
+ const value = resp;
1538
+ return {
1539
+ type: "count.query.result",
1540
+ id: typeof value.id === "string" ? value.id : id,
1541
+ ok: value.ok === true,
1542
+ ...typeof value.count === "number" && Number.isFinite(value.count) && value.count >= 0 ? { count: Math.floor(value.count) } : {},
1543
+ ...typeof value.approximate === "boolean" ? { approximate: value.approximate } : {},
1544
+ ...typeof value.hll === "string" ? { hll: value.hll } : {},
1545
+ ...Array.isArray(value.relays) ? { relays: value.relays.filter((relay) => typeof relay === "string") } : {},
1546
+ ...typeof value.error === "string" ? { error: value.error } : {},
1547
+ ...typeof value.reason === "string" ? { reason: value.reason } : {}
1548
+ };
1549
+ }
1550
+ function sendCountError(hooks, windowId, id, error, reason) {
1551
+ hooks.sendToNapplet(windowId, {
1552
+ type: "count.query.result",
1553
+ id,
1554
+ ok: false,
1555
+ error,
1556
+ reason
1557
+ });
1558
+ }
1559
+ function isValidFilterArray(value) {
1560
+ return Array.isArray(value) && value.length > 0 && value.every((filter) => isPlainObject(filter));
1561
+ }
1562
+ function isPlainObject(value) {
1563
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1564
+ }
1501
1565
  function createIncRuntime(hooks, sessionRegistry2) {
1502
1566
  const state = {
1503
1567
  subscriptions: /* @__PURE__ */ new Map(),
@@ -1697,13 +1761,13 @@ function legacyScopedKey(pubkey, dTag, aggregateHash, userKey) {
1697
1761
  }
1698
1762
  function byteLength(str) {
1699
1763
  let bytes = 0;
1700
- for (let i3 = 0; i3 < str.length; i3++) {
1701
- const c = str.charCodeAt(i3);
1764
+ for (let i4 = 0; i4 < str.length; i4++) {
1765
+ const c = str.charCodeAt(i4);
1702
1766
  if (c < 128) bytes += 1;
1703
1767
  else if (c < 2048) bytes += 2;
1704
1768
  else if (c < 55296 || c >= 57344) bytes += 3;
1705
1769
  else {
1706
- i3++;
1770
+ i4++;
1707
1771
  bytes += 4;
1708
1772
  }
1709
1773
  }
@@ -1747,14 +1811,14 @@ function handleStorageNap(windowId, msg, sendToNapplet, sessionRegistry2, aclSta
1747
1811
  break;
1748
1812
  }
1749
1813
  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));
1814
+ let result2 = statePersistence.get(newKey);
1815
+ if (result2 === null && pubkey) {
1816
+ result2 = statePersistence.get(legacyScopedKey(pubkey, dTag, aggregateHash, key));
1753
1817
  }
1754
- if (result === null && pubkey) {
1755
- result = statePersistence.get(`napp-state:${pubkey}:${dTag}:${aggregateHash}:${key}`);
1818
+ if (result2 === null && pubkey) {
1819
+ result2 = statePersistence.get(`napp-state:${pubkey}:${dTag}:${aggregateHash}:${key}`);
1756
1820
  }
1757
- sendResult({ value: result });
1821
+ sendResult({ value: result2 });
1758
1822
  break;
1759
1823
  }
1760
1824
  case "set": {
@@ -1973,6 +2037,7 @@ function createNapEnvelopeDispatcher(handlers) {
1973
2037
  };
1974
2038
  napDispatch.registerNap("relay", adapt(handlers.relay));
1975
2039
  napDispatch.registerNap("identity", adapt(handlers.identity));
2040
+ napDispatch.registerNap("count", adapt(handlers.count));
1976
2041
  napDispatch.registerNap("keys", adapt(handlers.keys));
1977
2042
  napDispatch.registerNap("media", adapt(handlers.media));
1978
2043
  napDispatch.registerNap("notify", adapt(handlers.notify));
@@ -2003,13 +2068,13 @@ function createNapEnvelopeDispatcher(handlers) {
2003
2068
  }
2004
2069
  function utf8ByteLength(str) {
2005
2070
  let bytes = 0;
2006
- for (let i3 = 0; i3 < str.length; i3++) {
2007
- const c = str.charCodeAt(i3);
2071
+ for (let i4 = 0; i4 < str.length; i4++) {
2072
+ const c = str.charCodeAt(i4);
2008
2073
  if (c < 128) bytes += 1;
2009
2074
  else if (c < 2048) bytes += 2;
2010
2075
  else if (c < 55296 || c >= 57344) bytes += 3;
2011
2076
  else {
2012
- i3++;
2077
+ i4++;
2013
2078
  bytes += 4;
2014
2079
  }
2015
2080
  }
@@ -2040,8 +2105,8 @@ function createFirewallGate(config) {
2040
2105
  const { firewallState, sessionRegistry: sessionRegistry2, hooks, fireConsent } = config;
2041
2106
  return function firewallGate(windowId, envelope, senderCap) {
2042
2107
  const obs = buildObservation(envelope, windowId, senderCap, sessionRegistry2, hooks.getFocusContext);
2043
- const result = firewallState.evaluate(obs);
2044
- const { decision, action, ruleId, reason } = result;
2108
+ const result2 = firewallState.evaluate(obs);
2109
+ const { decision, action, ruleId, reason } = result2;
2045
2110
  const napplet = obs.napplet;
2046
2111
  const opClass = obs.opClass;
2047
2112
  if (decision === "reject" || decision === "prompt") {
@@ -2069,11 +2134,11 @@ function createMessageHandler(hooks, enforceNap, dispatchNapEnvelope, firewallGa
2069
2134
  if (dotIdx === -1) return;
2070
2135
  const caps = resolveCapabilitiesNap(envelope);
2071
2136
  if (caps.senderCap) {
2072
- const result = enforceNap(windowId, caps.senderCap, envelope);
2073
- if (!result.allowed) {
2137
+ const result2 = enforceNap(windowId, caps.senderCap, envelope);
2138
+ if (!result2.allowed) {
2074
2139
  const id = envelope.id ?? "";
2075
2140
  const isStorageEnvelope = envelope.type.startsWith("storage.");
2076
- const error = formatDenialReason(result.capability);
2141
+ const error = formatDenialReason(result2.capability);
2077
2142
  const type = isStorageEnvelope ? `${envelope.type}.result` : `${envelope.type}.error`;
2078
2143
  hooks.sendToNapplet(windowId, { type, id, error });
2079
2144
  return;
@@ -2225,6 +2290,7 @@ function createRuntime(hooks) {
2225
2290
  const dispatchNapEnvelope = createNapEnvelopeDispatcher({
2226
2291
  relay: createRelayHandler({ hooks, serviceRegistry, subscriptions, eventBuffer, replayDetector }),
2227
2292
  identity: createIdentityHandler({ hooks, serviceRegistry }),
2293
+ count: createCountHandler({ hooks, serviceRegistry }),
2228
2294
  inc: incRuntime.handleMessage,
2229
2295
  ...domainHandlers
2230
2296
  });
@@ -2252,8 +2318,8 @@ function bytesToHex(bytes) {
2252
2318
  }
2253
2319
  function hexToBytes(hex) {
2254
2320
  const bytes = new Uint8Array(hex.length / 2);
2255
- for (let i3 = 0; i3 < hex.length; i3 += 2) {
2256
- bytes[i3 / 2] = parseInt(hex.substring(i3, i3 + 2), 16);
2321
+ for (let i4 = 0; i4 < hex.length; i4 += 2) {
2322
+ bytes[i4 / 2] = parseInt(hex.substring(i4, i4 + 2), 16);
2257
2323
  }
2258
2324
  return bytes;
2259
2325
  }
@@ -2427,8 +2493,8 @@ function createStatePersistence() {
2427
2493
  clear(prefix) {
2428
2494
  try {
2429
2495
  const keysToRemove = [];
2430
- for (let i3 = 0; i3 < localStorage.length; i3++) {
2431
- const key = localStorage.key(i3);
2496
+ for (let i4 = 0; i4 < localStorage.length; i4++) {
2497
+ const key = localStorage.key(i4);
2432
2498
  if (key?.startsWith(prefix)) keysToRemove.push(key);
2433
2499
  }
2434
2500
  for (const key of keysToRemove) localStorage.removeItem(key);
@@ -2437,12 +2503,12 @@ function createStatePersistence() {
2437
2503
  },
2438
2504
  keys(prefix) {
2439
2505
  try {
2440
- const result = [];
2441
- for (let i3 = 0; i3 < localStorage.length; i3++) {
2442
- const key = localStorage.key(i3);
2443
- if (key?.startsWith(prefix)) result.push(key);
2506
+ const result2 = [];
2507
+ for (let i4 = 0; i4 < localStorage.length; i4++) {
2508
+ const key = localStorage.key(i4);
2509
+ if (key?.startsWith(prefix)) result2.push(key);
2444
2510
  }
2445
- return result;
2511
+ return result2;
2446
2512
  } catch {
2447
2513
  return [];
2448
2514
  }
@@ -2450,8 +2516,8 @@ function createStatePersistence() {
2450
2516
  calculateBytes(prefix, excludeKey) {
2451
2517
  try {
2452
2518
  let total = 0;
2453
- for (let i3 = 0; i3 < localStorage.length; i3++) {
2454
- const key = localStorage.key(i3);
2519
+ for (let i4 = 0; i4 < localStorage.length; i4++) {
2520
+ const key = localStorage.key(i4);
2455
2521
  if (!key?.startsWith(prefix)) continue;
2456
2522
  if (excludeKey && key === excludeKey) continue;
2457
2523
  const value = localStorage.getItem(key) ?? "";
@@ -3245,6 +3311,7 @@ function buildShellCapabilities(hooks) {
3245
3311
  const domains = hooks.relayPool ? ["relay", "outbox", ...NAP_DOMAINS] : [...NAP_DOMAINS];
3246
3312
  if (hooks.upload) domains.push("upload");
3247
3313
  if (hooks.intent?.isAvailable()) domains.push("intent");
3314
+ if (hooks.services?.count) domains.push("count");
3248
3315
  if (hooks.link?.isAvailable()) domains.push("link");
3249
3316
  if (hooks.common?.isAvailable()) domains.push("common");
3250
3317
  if (hooks.lists?.isAvailable()) domains.push("lists");
@@ -3262,6 +3329,7 @@ function buildShellCapabilities(hooks) {
3262
3329
  const naps = hooks.relayPool ? ["relay", "outbox", ...NAP_DOMAINS, ...NAP_INC_PROTOCOLS] : [...NAP_DOMAINS, ...NAP_INC_PROTOCOLS];
3263
3330
  if (hooks.upload) naps.push("upload");
3264
3331
  if (hooks.intent?.isAvailable()) naps.push("intent");
3332
+ if (hooks.services?.count) naps.push("count");
3265
3333
  if (hooks.link?.isAvailable()) naps.push("link");
3266
3334
  if (hooks.common?.isAvailable()) naps.push("common");
3267
3335
  if (hooks.lists?.isAvailable()) naps.push("lists");
@@ -3503,8 +3571,8 @@ function aoutput(out, instance) {
3503
3571
  }
3504
3572
  }
3505
3573
  function clean(...arrays) {
3506
- for (let i3 = 0; i3 < arrays.length; i3++) {
3507
- arrays[i3].fill(0);
3574
+ for (let i4 = 0; i4 < arrays.length; i4++) {
3575
+ arrays[i4].fill(0);
3508
3576
  }
3509
3577
  }
3510
3578
  function createView(arr) {
@@ -3517,14 +3585,14 @@ var hasHexBuiltin = /* @__PURE__ */ (() => (
3517
3585
  // @ts-ignore
3518
3586
  typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function"
3519
3587
  ))();
3520
- var hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i3) => i3.toString(16).padStart(2, "0"));
3588
+ var hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i4) => i4.toString(16).padStart(2, "0"));
3521
3589
  function bytesToHex2(bytes) {
3522
3590
  abytes(bytes);
3523
3591
  if (hasHexBuiltin)
3524
3592
  return bytes.toHex();
3525
3593
  let hex = "";
3526
- for (let i3 = 0; i3 < bytes.length; i3++) {
3527
- hex += hexes[bytes[i3]];
3594
+ for (let i4 = 0; i4 < bytes.length; i4++) {
3595
+ hex += hexes[bytes[i4]];
3528
3596
  }
3529
3597
  return hex;
3530
3598
  }
@@ -3561,14 +3629,14 @@ function hexToBytes2(hex) {
3561
3629
  }
3562
3630
  function concatBytes(...arrays) {
3563
3631
  let sum = 0;
3564
- for (let i3 = 0; i3 < arrays.length; i3++) {
3565
- const a = arrays[i3];
3632
+ for (let i4 = 0; i4 < arrays.length; i4++) {
3633
+ const a = arrays[i4];
3566
3634
  abytes(a);
3567
3635
  sum += a.length;
3568
3636
  }
3569
3637
  const res = new Uint8Array(sum);
3570
- for (let i3 = 0, pad2 = 0; i3 < arrays.length; i3++) {
3571
- const a = arrays[i3];
3638
+ for (let i4 = 0, pad2 = 0; i4 < arrays.length; i4++) {
3639
+ const a = arrays[i4];
3572
3640
  res.set(a, pad2);
3573
3641
  pad2 += a.length;
3574
3642
  }
@@ -3651,24 +3719,24 @@ function alphabet(letters) {
3651
3719
  const lettersA = typeof letters === "string" ? letters.split("") : letters;
3652
3720
  const len = lettersA.length;
3653
3721
  astrArr("alphabet", lettersA);
3654
- const indexes = new Map(lettersA.map((l, i3) => [l, i3]));
3722
+ const indexes = new Map(lettersA.map((l, i4) => [l, i4]));
3655
3723
  return {
3656
3724
  encode: (digits) => {
3657
3725
  aArr(digits);
3658
- return digits.map((i3) => {
3659
- if (!Number.isSafeInteger(i3) || i3 < 0 || i3 >= len)
3660
- throw new Error(`alphabet.encode: digit index outside alphabet "${i3}". Allowed: ${letters}`);
3661
- return lettersA[i3];
3726
+ return digits.map((i4) => {
3727
+ if (!Number.isSafeInteger(i4) || i4 < 0 || i4 >= len)
3728
+ throw new Error(`alphabet.encode: digit index outside alphabet "${i4}". Allowed: ${letters}`);
3729
+ return lettersA[i4];
3662
3730
  });
3663
3731
  },
3664
3732
  decode: (input) => {
3665
3733
  aArr(input);
3666
3734
  return input.map((letter) => {
3667
3735
  astr("alphabet.decode", letter);
3668
- const i3 = indexes.get(letter);
3669
- if (i3 === void 0)
3736
+ const i4 = indexes.get(letter);
3737
+ if (i4 === void 0)
3670
3738
  throw new Error(`Unknown letter: "${letter}". Allowed: ${letters}`);
3671
- return i3;
3739
+ return i4;
3672
3740
  });
3673
3741
  }
3674
3742
  };
@@ -3717,8 +3785,8 @@ var gcd = (a, b) => b === 0 ? a : gcd(b, a % b);
3717
3785
  var radix2carry = /* @__NO_SIDE_EFFECTS__ */ (from, to) => from + (to - gcd(from, to));
3718
3786
  var powers = /* @__PURE__ */ (() => {
3719
3787
  let res = [];
3720
- for (let i3 = 0; i3 < 40; i3++)
3721
- res.push(2 ** i3);
3788
+ for (let i4 = 0; i4 < 40; i4++)
3789
+ res.push(2 ** i4);
3722
3790
  return res;
3723
3791
  })();
3724
3792
  function convertRadix2(data, from, to, padding2) {
@@ -3810,27 +3878,27 @@ var POLYMOD_GENERATORS = [996825010, 642813549, 513874426, 1027748829, 705979059
3810
3878
  function bech32Polymod(pre) {
3811
3879
  const b = pre >> 25;
3812
3880
  let chk = (pre & 33554431) << 5;
3813
- for (let i3 = 0; i3 < POLYMOD_GENERATORS.length; i3++) {
3814
- if ((b >> i3 & 1) === 1)
3815
- chk ^= POLYMOD_GENERATORS[i3];
3881
+ for (let i4 = 0; i4 < POLYMOD_GENERATORS.length; i4++) {
3882
+ if ((b >> i4 & 1) === 1)
3883
+ chk ^= POLYMOD_GENERATORS[i4];
3816
3884
  }
3817
3885
  return chk;
3818
3886
  }
3819
3887
  function bechChecksum(prefix, words, encodingConst = 1) {
3820
3888
  const len = prefix.length;
3821
3889
  let chk = 1;
3822
- for (let i3 = 0; i3 < len; i3++) {
3823
- const c = prefix.charCodeAt(i3);
3890
+ for (let i4 = 0; i4 < len; i4++) {
3891
+ const c = prefix.charCodeAt(i4);
3824
3892
  if (c < 33 || c > 126)
3825
3893
  throw new Error(`Invalid prefix (${prefix})`);
3826
3894
  chk = bech32Polymod(chk) ^ c >> 5;
3827
3895
  }
3828
3896
  chk = bech32Polymod(chk);
3829
- for (let i3 = 0; i3 < len; i3++)
3830
- chk = bech32Polymod(chk) ^ prefix.charCodeAt(i3) & 31;
3897
+ for (let i4 = 0; i4 < len; i4++)
3898
+ chk = bech32Polymod(chk) ^ prefix.charCodeAt(i4) & 31;
3831
3899
  for (let v of words)
3832
3900
  chk = bech32Polymod(chk) ^ v;
3833
- for (let i3 = 0; i3 < 6; i3++)
3901
+ for (let i4 = 0; i4 < 6; i4++)
3834
3902
  chk = bech32Polymod(chk);
3835
3903
  chk ^= encodingConst;
3836
3904
  return BECH_ALPHABET.encode(convertRadix2([chk % powers[30]], 30, 5, false));
@@ -3981,7 +4049,7 @@ function decode(code) {
3981
4049
  }
3982
4050
  }
3983
4051
  function parseTLV(data) {
3984
- let result = {};
4052
+ let result2 = {};
3985
4053
  let rest = data;
3986
4054
  while (rest.length > 0) {
3987
4055
  let t = rest[0];
@@ -3990,10 +4058,10 @@ function parseTLV(data) {
3990
4058
  rest = rest.slice(2 + l);
3991
4059
  if (v.length < l)
3992
4060
  throw new Error(`not enough data to read on TLV ${t}`);
3993
- result[t] = result[t] || [];
3994
- result[t].push(v);
4061
+ result2[t] = result2[t] || [];
4062
+ result2[t].push(v);
3995
4063
  }
3996
- return result;
4064
+ return result2;
3997
4065
  }
3998
4066
  function npubEncode(hex) {
3999
4067
  return encodeBytes("npub", hexToBytes2(hex));
@@ -4117,8 +4185,8 @@ var HashMD = class {
4117
4185
  this.process(view, 0);
4118
4186
  pos = 0;
4119
4187
  }
4120
- for (let i3 = pos; i3 < blockLen; i3++)
4121
- buffer[i3] = 0;
4188
+ for (let i4 = pos; i4 < blockLen; i4++)
4189
+ buffer[i4] = 0;
4122
4190
  view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE2);
4123
4191
  this.process(view, 0);
4124
4192
  const oview = createView(out);
@@ -4129,8 +4197,8 @@ var HashMD = class {
4129
4197
  const state = this.get();
4130
4198
  if (outLen > state.length)
4131
4199
  throw new Error("_sha2: outputLen bigger than state");
4132
- for (let i3 = 0; i3 < outLen; i3++)
4133
- oview.setUint32(4 * i3, state[i3], isLE2);
4200
+ for (let i4 = 0; i4 < outLen; i4++)
4201
+ oview.setUint32(4 * i4, state[i4], isLE2);
4134
4202
  }
4135
4203
  digest() {
4136
4204
  const { buffer, outputLen } = this;
@@ -4254,19 +4322,19 @@ var SHA2_32B = class extends HashMD {
4254
4322
  this.H = H | 0;
4255
4323
  }
4256
4324
  process(view, offset) {
4257
- for (let i3 = 0; i3 < 16; i3++, offset += 4)
4258
- SHA256_W[i3] = view.getUint32(offset, false);
4259
- for (let i3 = 16; i3 < 64; i3++) {
4260
- const W15 = SHA256_W[i3 - 15];
4261
- const W2 = SHA256_W[i3 - 2];
4325
+ for (let i4 = 0; i4 < 16; i4++, offset += 4)
4326
+ SHA256_W[i4] = view.getUint32(offset, false);
4327
+ for (let i4 = 16; i4 < 64; i4++) {
4328
+ const W15 = SHA256_W[i4 - 15];
4329
+ const W2 = SHA256_W[i4 - 2];
4262
4330
  const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
4263
4331
  const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
4264
- SHA256_W[i3] = s1 + SHA256_W[i3 - 7] + s0 + SHA256_W[i3 - 16] | 0;
4332
+ SHA256_W[i4] = s1 + SHA256_W[i4 - 7] + s0 + SHA256_W[i4 - 16] | 0;
4265
4333
  }
4266
4334
  let { A, B, C, D, E, F, G, H } = this;
4267
- for (let i3 = 0; i3 < 64; i3++) {
4335
+ for (let i4 = 0; i4 < 64; i4++) {
4268
4336
  const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
4269
- const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i3] + SHA256_W[i3] | 0;
4337
+ const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i4] + SHA256_W[i4] | 0;
4270
4338
  const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
4271
4339
  const T2 = sigma0 + Maj(A, B, C) | 0;
4272
4340
  H = G;
@@ -4364,10 +4432,10 @@ function copyBytes(bytes) {
4364
4432
  return Uint8Array.from(bytes);
4365
4433
  }
4366
4434
  function asciiToBytes(ascii) {
4367
- return Uint8Array.from(ascii, (c, i3) => {
4435
+ return Uint8Array.from(ascii, (c, i4) => {
4368
4436
  const charCode = c.charCodeAt(0);
4369
4437
  if (c.length !== 1 || charCode > 127) {
4370
- throw new Error(`string contains non-ASCII character "${ascii[i3]}" with code ${charCode} at position ${i3}`);
4438
+ throw new Error(`string contains non-ASCII character "${ascii[i4]}" with code ${charCode} at position ${i4}`);
4371
4439
  }
4372
4440
  return charCode;
4373
4441
  });
@@ -4399,11 +4467,11 @@ function createHmacDrbg(hashLen, qByteLen, hmacFn) {
4399
4467
  const _maxDrbgIters = 1e3;
4400
4468
  let v = u8n(hashLen);
4401
4469
  let k = u8n(hashLen);
4402
- let i3 = 0;
4470
+ let i4 = 0;
4403
4471
  const reset = () => {
4404
4472
  v.fill(1);
4405
4473
  k.fill(0);
4406
- i3 = 0;
4474
+ i4 = 0;
4407
4475
  };
4408
4476
  const h = (...msgs) => hmacFn(k, concatBytes(v, ...msgs));
4409
4477
  const reseed = (seed = NULL) => {
@@ -4415,7 +4483,7 @@ function createHmacDrbg(hashLen, qByteLen, hmacFn) {
4415
4483
  v = h();
4416
4484
  };
4417
4485
  const gen = () => {
4418
- if (i3++ >= _maxDrbgIters)
4486
+ if (i4++ >= _maxDrbgIters)
4419
4487
  throw new Error("drbg: tried max amount of iterations");
4420
4488
  let len = 0;
4421
4489
  const out = [];
@@ -4477,8 +4545,8 @@ var _8n = /* @__PURE__ */ BigInt(8);
4477
4545
  var _9n = /* @__PURE__ */ BigInt(9);
4478
4546
  var _16n = /* @__PURE__ */ BigInt(16);
4479
4547
  function mod(a, b) {
4480
- const result = a % b;
4481
- return result >= _0n2 ? result : b + result;
4548
+ const result2 = a % b;
4549
+ return result2 >= _0n2 ? result2 : b + result2;
4482
4550
  }
4483
4551
  function pow2(x, power, modulo) {
4484
4552
  let res = x;
@@ -4523,8 +4591,8 @@ function sqrt5mod8(Fp, n) {
4523
4591
  const n2 = Fp.mul(n, _2n);
4524
4592
  const v = Fp.pow(n2, p5div8);
4525
4593
  const nv = Fp.mul(n, v);
4526
- const i3 = Fp.mul(Fp.mul(nv, _2n), v);
4527
- const root = Fp.mul(nv, Fp.sub(i3, Fp.ONE));
4594
+ const i4 = Fp.mul(Fp.mul(nv, _2n), v);
4595
+ const root = Fp.mul(nv, Fp.sub(i4, Fp.ONE));
4528
4596
  assertIsSquare(Fp, root, n);
4529
4597
  return root;
4530
4598
  }
@@ -4581,17 +4649,17 @@ function tonelliShanks(P) {
4581
4649
  while (!Fp.eql(t, Fp.ONE)) {
4582
4650
  if (Fp.is0(t))
4583
4651
  return Fp.ZERO;
4584
- let i3 = 1;
4652
+ let i4 = 1;
4585
4653
  let t_tmp = Fp.sqr(t);
4586
4654
  while (!Fp.eql(t_tmp, Fp.ONE)) {
4587
- i3++;
4655
+ i4++;
4588
4656
  t_tmp = Fp.sqr(t_tmp);
4589
- if (i3 === M)
4657
+ if (i4 === M)
4590
4658
  throw new Error("Cannot find square root");
4591
4659
  }
4592
- const exponent = _1n2 << BigInt(M - i3 - 1);
4660
+ const exponent = _1n2 << BigInt(M - i4 - 1);
4593
4661
  const b = Fp.pow(c, exponent);
4594
- M = i3;
4662
+ M = i4;
4595
4663
  c = Fp.sqr(b);
4596
4664
  t = Fp.mul(t, c);
4597
4665
  R = Fp.mul(R, b);
@@ -4659,17 +4727,17 @@ function FpPow(Fp, num2, power) {
4659
4727
  }
4660
4728
  function FpInvertBatch(Fp, nums, passZero = false) {
4661
4729
  const inverted = new Array(nums.length).fill(passZero ? Fp.ZERO : void 0);
4662
- const multipliedAcc = nums.reduce((acc, num2, i3) => {
4730
+ const multipliedAcc = nums.reduce((acc, num2, i4) => {
4663
4731
  if (Fp.is0(num2))
4664
4732
  return acc;
4665
- inverted[i3] = acc;
4733
+ inverted[i4] = acc;
4666
4734
  return Fp.mul(acc, num2);
4667
4735
  }, Fp.ONE);
4668
4736
  const invertedAcc = Fp.inv(multipliedAcc);
4669
- nums.reduceRight((acc, num2, i3) => {
4737
+ nums.reduceRight((acc, num2, i4) => {
4670
4738
  if (Fp.is0(num2))
4671
4739
  return acc;
4672
- inverted[i3] = Fp.mul(acc, inverted[i3]);
4740
+ inverted[i4] = Fp.mul(acc, inverted[i4]);
4673
4741
  return Fp.mul(acc, num2);
4674
4742
  }, invertedAcc);
4675
4743
  return inverted;
@@ -4860,7 +4928,7 @@ function negateCt(condition, item) {
4860
4928
  }
4861
4929
  function normalizeZ(c, points) {
4862
4930
  const invertedZs = FpInvertBatch(c.Fp, points.map((p) => p.Z));
4863
- return points.map((p, i3) => c.fromAffine(p.toAffine(invertedZs[i3])));
4931
+ return points.map((p, i4) => c.fromAffine(p.toAffine(invertedZs[i4])));
4864
4932
  }
4865
4933
  function validateW(W, bits) {
4866
4934
  if (!Number.isSafeInteger(W) || W <= 0 || W > bits)
@@ -4943,7 +5011,7 @@ var wNAF = class {
4943
5011
  for (let window2 = 0; window2 < windows; window2++) {
4944
5012
  base = p;
4945
5013
  points.push(base);
4946
- for (let i3 = 1; i3 < windowSize; i3++) {
5014
+ for (let i4 = 1; i4 < windowSize; i4++) {
4947
5015
  base = base.add(p);
4948
5016
  points.push(base);
4949
5017
  }
@@ -5077,10 +5145,10 @@ function createCurveFields(type, CURVE, curveOpts = {}, FpFnLE) {
5077
5145
  CURVE = Object.freeze(Object.assign({}, CURVE));
5078
5146
  return { CURVE, Fp, Fn };
5079
5147
  }
5080
- function createKeygen(randomSecretKey, getPublicKey3) {
5148
+ function createKeygen(randomSecretKey, getPublicKey4) {
5081
5149
  return function keygen(seed) {
5082
5150
  const secretKey = randomSecretKey(seed);
5083
- return { secretKey, publicKey: getPublicKey3(secretKey) };
5151
+ return { secretKey, publicKey: getPublicKey4(secretKey) };
5084
5152
  };
5085
5153
  }
5086
5154
 
@@ -5103,12 +5171,12 @@ var _HMAC = class {
5103
5171
  const blockLen = this.blockLen;
5104
5172
  const pad2 = new Uint8Array(blockLen);
5105
5173
  pad2.set(key.length > blockLen ? hash.create().update(key).digest() : key);
5106
- for (let i3 = 0; i3 < pad2.length; i3++)
5107
- pad2[i3] ^= 54;
5174
+ for (let i4 = 0; i4 < pad2.length; i4++)
5175
+ pad2[i4] ^= 54;
5108
5176
  this.iHash.update(pad2);
5109
5177
  this.oHash = hash.create();
5110
- for (let i3 = 0; i3 < pad2.length; i3++)
5111
- pad2[i3] ^= 54 ^ 92;
5178
+ for (let i4 = 0; i4 < pad2.length; i4++)
5179
+ pad2[i4] ^= 54 ^ 92;
5112
5180
  this.oHash.update(pad2);
5113
5181
  clean(pad2);
5114
5182
  }
@@ -5765,7 +5833,7 @@ function ecdh(Point, ecdhOpts = {}) {
5765
5833
  function randomSecretKey(seed = randomBytes_(lengths.seed)) {
5766
5834
  return mapHashToField(abytes(seed, lengths.seed, "seed"), Fn.ORDER);
5767
5835
  }
5768
- function getPublicKey3(secretKey, isCompressed = true) {
5836
+ function getPublicKey4(secretKey, isCompressed = true) {
5769
5837
  return Point.BASE.multiply(Fn.fromBytes(secretKey)).toBytes(isCompressed);
5770
5838
  }
5771
5839
  function isProbPub(item) {
@@ -5791,8 +5859,8 @@ function ecdh(Point, ecdhOpts = {}) {
5791
5859
  isValidPublicKey,
5792
5860
  randomSecretKey
5793
5861
  };
5794
- const keygen = createKeygen(randomSecretKey, getPublicKey3);
5795
- return Object.freeze({ getPublicKey: getPublicKey3, getSharedSecret, keygen, Point, utils, lengths });
5862
+ const keygen = createKeygen(randomSecretKey, getPublicKey4);
5863
+ return Object.freeze({ getPublicKey: getPublicKey4, getSharedSecret, keygen, Point, utils, lengths });
5796
5864
  }
5797
5865
  function ecdsa(Point, hash, ecdsaOpts = {}) {
5798
5866
  ahash(hash);
@@ -5808,7 +5876,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
5808
5876
  const hmac2 = ecdsaOpts.hmac || ((key, msg) => hmac(hash, key, msg));
5809
5877
  const { Fp, Fn } = Point;
5810
5878
  const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
5811
- const { keygen, getPublicKey: getPublicKey3, getSharedSecret, utils, lengths } = ecdh(Point, ecdsaOpts);
5879
+ const { keygen, getPublicKey: getPublicKey4, getSharedSecret, utils, lengths } = ecdh(Point, ecdsaOpts);
5812
5880
  const defaultSigOpts = {
5813
5881
  prehash: true,
5814
5882
  lowS: typeof ecdsaOpts.lowS === "boolean" ? ecdsaOpts.lowS : true,
@@ -6014,7 +6082,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
6014
6082
  }
6015
6083
  return Object.freeze({
6016
6084
  keygen,
6017
- getPublicKey: getPublicKey3,
6085
+ getPublicKey: getPublicKey4,
6018
6086
  getSharedSecret,
6019
6087
  utils,
6020
6088
  lengths,
@@ -6300,8 +6368,8 @@ function u32(arr) {
6300
6368
  return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
6301
6369
  }
6302
6370
  function clean2(...arrays) {
6303
- for (let i3 = 0; i3 < arrays.length; i3++) {
6304
- arrays[i3].fill(0);
6371
+ for (let i4 = 0; i4 < arrays.length; i4++) {
6372
+ arrays[i4].fill(0);
6305
6373
  }
6306
6374
  }
6307
6375
  function createView2(arr) {
@@ -6327,8 +6395,8 @@ function equalBytes(a, b) {
6327
6395
  if (a.length !== b.length)
6328
6396
  return false;
6329
6397
  let diff = 0;
6330
- for (let i3 = 0; i3 < a.length; i3++)
6331
- diff |= a[i3] ^ b[i3];
6398
+ for (let i4 = 0; i4 < a.length; i4++)
6399
+ diff |= a[i4] ^ b[i4];
6332
6400
  return diff === 0;
6333
6401
  }
6334
6402
  var wrapCipher = /* @__NO_SIDE_EFFECTS__ */ (params, constructor) => {
@@ -6508,8 +6576,8 @@ function createCipher(core, opts) {
6508
6576
  }
6509
6577
 
6510
6578
  // ../../node_modules/.pnpm/@noble+ciphers@2.1.1/node_modules/@noble/ciphers/_poly1305.js
6511
- function u8to16(a, i3) {
6512
- return a[i3++] & 255 | (a[i3++] & 255) << 8;
6579
+ function u8to16(a, i4) {
6580
+ return a[i4++] & 255 | (a[i4++] & 255) << 8;
6513
6581
  }
6514
6582
  var Poly1305 = class {
6515
6583
  blockLen = 16;
@@ -6542,8 +6610,8 @@ var Poly1305 = class {
6542
6610
  this.r[7] = (t5 >>> 11 | t6 << 5) & 8065;
6543
6611
  this.r[8] = (t6 >>> 8 | t7 << 8) & 8191;
6544
6612
  this.r[9] = t7 >>> 5 & 127;
6545
- for (let i3 = 0; i3 < 8; i3++)
6546
- this.pad[i3] = u8to16(key, 16 + 2 * i3);
6613
+ for (let i4 = 0; i4 < 8; i4++)
6614
+ this.pad[i4] = u8to16(key, 16 + 2 * i4);
6547
6615
  }
6548
6616
  process(data, offset, isLast = false) {
6549
6617
  const hibit = isLast ? 0 : 1 << 11;
@@ -6658,10 +6726,10 @@ var Poly1305 = class {
6658
6726
  const g = new Uint16Array(10);
6659
6727
  let c = h[1] >>> 13;
6660
6728
  h[1] &= 8191;
6661
- for (let i3 = 2; i3 < 10; i3++) {
6662
- h[i3] += c;
6663
- c = h[i3] >>> 13;
6664
- h[i3] &= 8191;
6729
+ for (let i4 = 2; i4 < 10; i4++) {
6730
+ h[i4] += c;
6731
+ c = h[i4] >>> 13;
6732
+ h[i4] &= 8191;
6665
6733
  }
6666
6734
  h[0] += c * 5;
6667
6735
  c = h[0] >>> 13;
@@ -6673,18 +6741,18 @@ var Poly1305 = class {
6673
6741
  g[0] = h[0] + 5;
6674
6742
  c = g[0] >>> 13;
6675
6743
  g[0] &= 8191;
6676
- for (let i3 = 1; i3 < 10; i3++) {
6677
- g[i3] = h[i3] + c;
6678
- c = g[i3] >>> 13;
6679
- g[i3] &= 8191;
6744
+ for (let i4 = 1; i4 < 10; i4++) {
6745
+ g[i4] = h[i4] + c;
6746
+ c = g[i4] >>> 13;
6747
+ g[i4] &= 8191;
6680
6748
  }
6681
6749
  g[9] -= 1 << 13;
6682
6750
  let mask = (c ^ 1) - 1;
6683
- for (let i3 = 0; i3 < 10; i3++)
6684
- g[i3] &= mask;
6751
+ for (let i4 = 0; i4 < 10; i4++)
6752
+ g[i4] &= mask;
6685
6753
  mask = ~mask;
6686
- for (let i3 = 0; i3 < 10; i3++)
6687
- h[i3] = h[i3] & mask | g[i3];
6754
+ for (let i4 = 0; i4 < 10; i4++)
6755
+ h[i4] = h[i4] & mask | g[i4];
6688
6756
  h[0] = (h[0] | h[1] << 13) & 65535;
6689
6757
  h[1] = (h[1] >>> 3 | h[2] << 10) & 65535;
6690
6758
  h[2] = (h[2] >>> 6 | h[3] << 7) & 65535;
@@ -6695,9 +6763,9 @@ var Poly1305 = class {
6695
6763
  h[7] = (h[8] >>> 8 | h[9] << 5) & 65535;
6696
6764
  let f = h[0] + pad2[0];
6697
6765
  h[0] = f & 65535;
6698
- for (let i3 = 1; i3 < 8; i3++) {
6699
- f = (h[i3] + pad2[i3] | 0) + (f >>> 16) | 0;
6700
- h[i3] = f & 65535;
6766
+ for (let i4 = 1; i4 < 8; i4++) {
6767
+ f = (h[i4] + pad2[i4] | 0) + (f >>> 16) | 0;
6768
+ h[i4] = f & 65535;
6701
6769
  }
6702
6770
  clean2(g);
6703
6771
  }
@@ -6741,9 +6809,9 @@ var Poly1305 = class {
6741
6809
  }
6742
6810
  this.finalize();
6743
6811
  let opos = 0;
6744
- for (let i3 = 0; i3 < 8; i3++) {
6745
- out[opos++] = h[i3] >>> 0;
6746
- out[opos++] = h[i3] >>> 8;
6812
+ for (let i4 = 0; i4 < 8; i4++) {
6813
+ out[opos++] = h[i4] >>> 0;
6814
+ out[opos++] = h[i4] >>> 8;
6747
6815
  }
6748
6816
  return out;
6749
6817
  }
@@ -6853,8 +6921,8 @@ function chachaCore(s, k, n, out, cnt, rounds = 20) {
6853
6921
  out[oi++] = y14 + x14 | 0;
6854
6922
  out[oi++] = y15 + x15 | 0;
6855
6923
  }
6856
- function hchacha(s, k, i3, out) {
6857
- let x00 = s[0], x01 = s[1], x02 = s[2], x03 = s[3], x04 = k[0], x05 = k[1], x06 = k[2], x07 = k[3], x08 = k[4], x09 = k[5], x10 = k[6], x11 = k[7], x12 = i3[0], x13 = i3[1], x14 = i3[2], x15 = i3[3];
6924
+ function hchacha(s, k, i4, out) {
6925
+ let x00 = s[0], x01 = s[1], x02 = s[2], x03 = s[3], x04 = k[0], x05 = k[1], x06 = k[2], x07 = k[3], x08 = k[4], x09 = k[5], x10 = k[6], x11 = k[7], x12 = i4[0], x13 = i4[1], x14 = i4[2], x15 = i4[3];
6858
6926
  for (let r = 0; r < 20; r += 2) {
6859
6927
  x00 = x00 + x04 | 0;
6860
6928
  x12 = rotl(x12 ^ x00, 16);
@@ -7194,9 +7262,9 @@ function createNotificationService(options) {
7194
7262
  };
7195
7263
  }
7196
7264
  var IDENTITY_SERVICE_VERSION = "1.0.0";
7197
- function sendProviderError(send, result, fallback, err2) {
7265
+ function sendProviderError(send, result2, fallback, err2) {
7198
7266
  send({
7199
- ...result,
7267
+ ...result2,
7200
7268
  error: err2?.message ?? fallback
7201
7269
  });
7202
7270
  }
@@ -7214,7 +7282,7 @@ function sendOptionalProviderResult(options, send, fallbackResult, errorFallback
7214
7282
  send(fallbackResult);
7215
7283
  return;
7216
7284
  }
7217
- Promise.resolve(getCurrentPubkey(options)).then((pubkey) => buildResult(pubkey)).then((result) => send(result)).catch((err2) => sendProviderError(send, fallbackResult, errorFallback, err2));
7285
+ Promise.resolve(getCurrentPubkey(options)).then((pubkey) => buildResult(pubkey)).then((result2) => send(result2)).catch((err2) => sendProviderError(send, fallbackResult, errorFallback, err2));
7218
7286
  }
7219
7287
  function sendIdentityError(send, id, typeBase, error) {
7220
7288
  send({ type: `${typeBase}.error`, id, error });
@@ -7225,21 +7293,21 @@ function sendSignerError(send, id, typeBase, fallback, err2) {
7225
7293
  function handleGetPublicKey(options, id, send) {
7226
7294
  const signer = options.getSigner();
7227
7295
  if (!signer) {
7228
- const result = {
7296
+ const result2 = {
7229
7297
  type: "identity.getPublicKey.result",
7230
7298
  id,
7231
7299
  pubkey: ""
7232
7300
  };
7233
- send(result);
7301
+ send(result2);
7234
7302
  return;
7235
7303
  }
7236
7304
  Promise.resolve(signer.getPublicKey?.()).then((pubkey) => {
7237
- const result = {
7305
+ const result2 = {
7238
7306
  type: "identity.getPublicKey.result",
7239
7307
  id,
7240
7308
  pubkey: pubkey ?? ""
7241
7309
  };
7242
- send(result);
7310
+ send(result2);
7243
7311
  }).catch((err2) => sendSignerError(send, id, "identity.getPublicKey", "getPublicKey failed", err2));
7244
7312
  }
7245
7313
  function handleGetRelays(options, id, send) {
@@ -7249,12 +7317,12 @@ function handleGetRelays(options, id, send) {
7249
7317
  return;
7250
7318
  }
7251
7319
  Promise.resolve(signer.getRelays?.() ?? {}).then((relays) => {
7252
- const result = {
7320
+ const result2 = {
7253
7321
  type: "identity.getRelays.result",
7254
7322
  id,
7255
7323
  relays
7256
7324
  };
7257
- send(result);
7325
+ send(result2);
7258
7326
  }).catch((err2) => sendSignerError(send, id, "identity.getRelays", "getRelays failed", err2));
7259
7327
  }
7260
7328
  function handleReadProvider(options, id, message, send) {
@@ -7475,6 +7543,90 @@ function createRelayPoolService(options) {
7475
7543
  }
7476
7544
  };
7477
7545
  }
7546
+ var DEFAULT_MAX_FILTERS = 16;
7547
+ function createCountService(options) {
7548
+ return {
7549
+ descriptor: {
7550
+ name: "count",
7551
+ version: "1.0.0",
7552
+ description: "Runtime-mediated NIP-01 filter counts"
7553
+ },
7554
+ handleMessage(windowId, message, send) {
7555
+ if (message.type !== "count.query") return;
7556
+ const countMessage = message;
7557
+ const id = typeof countMessage.id === "string" ? countMessage.id : "";
7558
+ const filters = countMessage.filters;
7559
+ if (!isValidFilterArray2(filters)) {
7560
+ send(result(id, { ok: false, error: "invalid-filter", reason: "count.query requires a non-empty filters array" }));
7561
+ return;
7562
+ }
7563
+ const maxFilters = options.maxFilters ?? DEFAULT_MAX_FILTERS;
7564
+ if (filters.length > maxFilters) {
7565
+ send(result(id, { ok: false, error: "too-expensive", reason: `count.query accepts at most ${maxFilters} filters` }));
7566
+ return;
7567
+ }
7568
+ for (let i4 = 0; i4 < filters.length; i4++) {
7569
+ const supported = options.isFilterSupported?.(filters[i4], i4) ?? true;
7570
+ if (supported !== true) {
7571
+ send(result(id, {
7572
+ ok: false,
7573
+ error: "unsupported-filter",
7574
+ reason: typeof supported === "string" ? supported : `filter ${i4} is not supported by this count backend`
7575
+ }));
7576
+ return;
7577
+ }
7578
+ }
7579
+ const request = {
7580
+ filters,
7581
+ windowId,
7582
+ ...isPlainObject2(countMessage.options) ? { options: countMessage.options } : {}
7583
+ };
7584
+ Promise.resolve(options.count(request)).then((countResult) => {
7585
+ send(result(id, normalizeCountResult2(countResult)));
7586
+ }).catch((error) => {
7587
+ send(result(id, {
7588
+ ok: false,
7589
+ error: "count-unavailable",
7590
+ reason: error instanceof Error ? error.message : "count backend failed"
7591
+ }));
7592
+ });
7593
+ }
7594
+ };
7595
+ }
7596
+ function result(id, value) {
7597
+ return {
7598
+ type: "count.query.result",
7599
+ id,
7600
+ ok: value.ok,
7601
+ ...typeof value.count === "number" ? { count: value.count } : {},
7602
+ ...typeof value.approximate === "boolean" ? { approximate: value.approximate } : {},
7603
+ ...typeof value.hll === "string" ? { hll: value.hll } : {},
7604
+ ...Array.isArray(value.relays) ? { relays: value.relays.filter((relay) => typeof relay === "string") } : {},
7605
+ ...typeof value.error === "string" ? { error: value.error } : {},
7606
+ ...typeof value.reason === "string" ? { reason: value.reason } : {}
7607
+ };
7608
+ }
7609
+ function normalizeCountResult2(value) {
7610
+ if (typeof value === "number") {
7611
+ 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" };
7612
+ }
7613
+ const count = typeof value.count === "number" && Number.isFinite(value.count) && value.count >= 0 ? Math.floor(value.count) : void 0;
7614
+ return {
7615
+ ok: value.ok === true,
7616
+ ...count !== void 0 ? { count } : {},
7617
+ ...typeof value.approximate === "boolean" ? { approximate: value.approximate } : {},
7618
+ ...typeof value.hll === "string" ? { hll: value.hll } : {},
7619
+ ...Array.isArray(value.relays) ? { relays: value.relays } : {},
7620
+ ...typeof value.error === "string" ? { error: value.error } : {},
7621
+ ...typeof value.reason === "string" ? { reason: value.reason } : {}
7622
+ };
7623
+ }
7624
+ function isValidFilterArray2(value) {
7625
+ return Array.isArray(value) && value.length > 0 && value.every((filter) => isPlainObject2(filter));
7626
+ }
7627
+ function isPlainObject2(value) {
7628
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7629
+ }
7478
7630
  var KEYS_SERVICE_VERSION = "1.2.0";
7479
7631
  var MODIFIER_ALIASES = {
7480
7632
  ctrl: "ctrl",
@@ -7492,11 +7644,11 @@ function parseChord(chord) {
7492
7644
  if (chord.length === 0) throw new Error("empty chord");
7493
7645
  const parts = chord.split("+");
7494
7646
  const out = { ctrl: false, alt: false, shift: false, meta: false, key: "" };
7495
- for (let i3 = 0; i3 < parts.length - 1; i3++) {
7496
- const tok = parts[i3].trim().toLowerCase();
7647
+ for (let i4 = 0; i4 < parts.length - 1; i4++) {
7648
+ const tok = parts[i4].trim().toLowerCase();
7497
7649
  if (tok.length === 0) continue;
7498
7650
  const slot = MODIFIER_ALIASES[tok];
7499
- if (!slot) throw new Error(`unknown modifier: ${parts[i3]}`);
7651
+ if (!slot) throw new Error(`unknown modifier: ${parts[i4]}`);
7500
7652
  out[slot] = true;
7501
7653
  }
7502
7654
  const keyTok = parts[parts.length - 1].trim();
@@ -7588,13 +7740,13 @@ function createKeysService(options = {}) {
7588
7740
  return;
7589
7741
  }
7590
7742
  }
7591
- const result = {
7743
+ const result2 = {
7592
7744
  type: "keys.registerAction.result",
7593
7745
  id: m.id,
7594
7746
  actionId: m.action.id,
7595
7747
  ...m.action.defaultKey ? { binding: m.action.defaultKey } : {}
7596
7748
  };
7597
- send(result);
7749
+ send(result2);
7598
7750
  return;
7599
7751
  }
7600
7752
  case "keys.unregisterAction": {
@@ -7735,13 +7887,13 @@ function createKeysService(options = {}) {
7735
7887
  return;
7736
7888
  }
7737
7889
  }
7738
- const result = {
7890
+ const result2 = {
7739
7891
  type: "keys.registerAction.result",
7740
7892
  id: m.id,
7741
7893
  actionId: m.action.id,
7742
7894
  ...m.action.defaultKey ? { binding: m.action.defaultKey } : {}
7743
7895
  };
7744
- send(result);
7896
+ send(result2);
7745
7897
  return;
7746
7898
  }
7747
7899
  case "keys.unregisterAction": {
@@ -8166,12 +8318,12 @@ function createNotifyService(options = {}) {
8166
8318
  case "notify.send": {
8167
8319
  const m = message;
8168
8320
  options.onSend?.(windowId, m);
8169
- const result = {
8321
+ const result2 = {
8170
8322
  type: "notify.send.result",
8171
8323
  id: m.id,
8172
8324
  notificationId: gen()
8173
8325
  };
8174
- send(result);
8326
+ send(result2);
8175
8327
  return;
8176
8328
  }
8177
8329
  case "notify.dismiss":
@@ -8180,12 +8332,12 @@ function createNotifyService(options = {}) {
8180
8332
  return;
8181
8333
  case "notify.permission.request": {
8182
8334
  const m = message;
8183
- const result = {
8335
+ const result2 = {
8184
8336
  type: "notify.permission.result",
8185
8337
  id: m.id,
8186
8338
  granted: defaultGrant
8187
8339
  };
8188
- send(result);
8340
+ send(result2);
8189
8341
  return;
8190
8342
  }
8191
8343
  default: {
@@ -8224,12 +8376,12 @@ function createThemeService(options = {}) {
8224
8376
  handleMessage(_windowId, message, send) {
8225
8377
  const id = message.id ?? "";
8226
8378
  if (message.type === "theme.get") {
8227
- const result = {
8379
+ const result2 = {
8228
8380
  type: "theme.get.result",
8229
8381
  id,
8230
8382
  theme: currentTheme
8231
8383
  };
8232
- send(result);
8384
+ send(result2);
8233
8385
  return;
8234
8386
  }
8235
8387
  send({
@@ -8351,14 +8503,14 @@ function createConfigService(options) {
8351
8503
  case "config.registerSchema": {
8352
8504
  const m = message;
8353
8505
  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 } : {
8506
+ const result2 = validation.ok ? { type: "config.registerSchema.result", id: m.id, ok: true } : {
8355
8507
  type: "config.registerSchema.result",
8356
8508
  id: m.id,
8357
8509
  ok: false,
8358
8510
  code: validation.code,
8359
8511
  error: validation.error
8360
8512
  };
8361
- send(result);
8513
+ send(result2);
8362
8514
  return;
8363
8515
  }
8364
8516
  case "config.openSettings": {
@@ -8393,8 +8545,8 @@ function arrayBufferToBase64(buf) {
8393
8545
  const bytes = new Uint8Array(buf);
8394
8546
  const CHUNK = 32768;
8395
8547
  let binary = "";
8396
- for (let i3 = 0; i3 < bytes.length; i3 += CHUNK) {
8397
- binary += String.fromCharCode(...bytes.subarray(i3, i3 + CHUNK));
8548
+ for (let i4 = 0; i4 < bytes.length; i4 += CHUNK) {
8549
+ binary += String.fromCharCode(...bytes.subarray(i4, i4 + CHUNK));
8398
8550
  }
8399
8551
  return btoa(binary);
8400
8552
  }
@@ -8725,33 +8877,33 @@ function buildEventQuery(eventId, eventOptions) {
8725
8877
  if (eventOptions?.timeoutMs !== void 0) queryOptions.timeoutMs = eventOptions.timeoutMs;
8726
8878
  return { filter, queryOptions };
8727
8879
  }
8728
- function eventResultFromQuery(eventId, result) {
8729
- const event = result.events.find((candidate) => candidate.id === eventId);
8880
+ function eventResultFromQuery(eventId, result2) {
8881
+ const event = result2.events.find((candidate) => candidate.id === eventId);
8730
8882
  const eventResult = {
8731
- relays: event ? result.relays[eventId] ?? [] : []
8883
+ relays: event ? result2.relays[eventId] ?? [] : []
8732
8884
  };
8733
8885
  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;
8886
+ if (!event) eventResult.error = result2.error ?? "not found";
8887
+ else if (result2.error !== void 0) eventResult.error = result2.error;
8888
+ if (result2.incomplete !== void 0) eventResult.incomplete = result2.incomplete;
8737
8889
  return eventResult;
8738
8890
  }
8739
8891
  function fallbackGetEvent(router, eventId, eventOptions) {
8740
8892
  const { filter, queryOptions } = buildEventQuery(eventId, eventOptions);
8741
- return router.query([filter], queryOptions).then((result) => eventResultFromQuery(eventId, result));
8893
+ return router.query([filter], queryOptions).then((result2) => eventResultFromQuery(eventId, result2));
8742
8894
  }
8743
- function sendEventResult(id, eventId, result, send) {
8744
- if (result.event && result.event.id !== eventId) {
8895
+ function sendEventResult(id, eventId, result2, send) {
8896
+ if (result2.event && result2.event.id !== eventId) {
8745
8897
  send({ type: "outbox.getEvent.result", id, relays: [], error: "not found" });
8746
8898
  return;
8747
8899
  }
8748
8900
  send({
8749
8901
  type: "outbox.getEvent.result",
8750
8902
  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 }
8903
+ ...result2.event === void 0 ? {} : { event: result2.event },
8904
+ relays: result2.relays,
8905
+ ...result2.incomplete === void 0 ? {} : { incomplete: result2.incomplete },
8906
+ ...result2.error === void 0 ? {} : { error: result2.error }
8755
8907
  });
8756
8908
  }
8757
8909
  function handleGetEvent(router, msg, send) {
@@ -8762,7 +8914,7 @@ function handleGetEvent(router, msg, send) {
8762
8914
  return;
8763
8915
  }
8764
8916
  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(
8917
+ void getEvent(m.eventId, m.options).then((result2) => sendEventResult(id, m.eventId, result2, send)).catch(
8766
8918
  (err2) => send({ type: "outbox.getEvent.result", id, relays: [], error: toErrorMessage(err2) })
8767
8919
  );
8768
8920
  }
@@ -8781,13 +8933,13 @@ function createOutboxService(options) {
8781
8933
  return;
8782
8934
  }
8783
8935
  void router.query(filters, m.options).then(
8784
- (result) => send({
8936
+ (result2) => send({
8785
8937
  type: "outbox.query.result",
8786
8938
  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 }
8939
+ events: result2.events,
8940
+ relays: result2.relays,
8941
+ ...result2.incomplete === void 0 ? {} : { incomplete: result2.incomplete },
8942
+ ...result2.error === void 0 ? {} : { error: result2.error }
8791
8943
  })
8792
8944
  ).catch(
8793
8945
  (err2) => send({ type: "outbox.query.result", id, events: [], relays: {}, error: toErrorMessage(err2) })
@@ -8832,14 +8984,14 @@ function createOutboxService(options) {
8832
8984
  return;
8833
8985
  }
8834
8986
  void router.publish(m.event, m.options).then(
8835
- (result) => send({
8987
+ (result2) => send({
8836
8988
  type: "outbox.publish.result",
8837
8989
  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 }
8990
+ ok: result2.ok,
8991
+ ...result2.event === void 0 ? {} : { event: result2.event },
8992
+ ...result2.eventId === void 0 ? {} : { eventId: result2.eventId },
8993
+ ...result2.relays === void 0 ? {} : { relays: result2.relays },
8994
+ ...result2.error === void 0 ? {} : { error: result2.error }
8843
8995
  })
8844
8996
  ).catch(
8845
8997
  (err2) => send({ type: "outbox.publish.result", id, ok: false, error: toErrorMessage(err2) })
@@ -8957,8 +9109,8 @@ function createUploadService(options) {
8957
9109
  send({ type: "upload.status.changed", status: stamped });
8958
9110
  }
8959
9111
  };
8960
- void uploader.upload(request, ctx).then((result) => {
8961
- const stamped = { ...result, uploadId };
9112
+ void uploader.upload(request, ctx).then((result2) => {
9113
+ const stamped = { ...result2, uploadId };
8962
9114
  const entry = entries.get(key);
8963
9115
  if (entry) entry.status = { ...stamped, updatedAt: now2() };
8964
9116
  send({ type: "upload.upload.result", id, result: stamped });
@@ -9070,7 +9222,7 @@ function createIntentService(options) {
9070
9222
  send,
9071
9223
  "intent.invoke.result",
9072
9224
  id,
9073
- (result) => ({ type: "intent.invoke.result", id, result })
9225
+ (result2) => ({ type: "intent.invoke.result", id, result: result2 })
9074
9226
  );
9075
9227
  }
9076
9228
  function handleAvailable(msg, send) {
@@ -9258,12 +9410,12 @@ async function handleOpen(options, windowId, message, send) {
9258
9410
  return;
9259
9411
  }
9260
9412
  try {
9261
- const result = await options.open({ windowId, url: parsed, options: message.options });
9413
+ const result2 = await options.open({ windowId, url: parsed, options: message.options });
9262
9414
  send({
9263
9415
  type: "link.open.result",
9264
9416
  id: message.id,
9265
- status: result.status,
9266
- ...result.status === "denied" ? { error: "blocked-by-policy" } : {}
9417
+ status: result2.status,
9418
+ ...result2.status === "denied" ? { error: "blocked-by-policy" } : {}
9267
9419
  });
9268
9420
  } catch {
9269
9421
  send(denied(message.id, "blocked-by-policy"));
@@ -9333,7 +9485,7 @@ function settleMutation(hook, message, context, send, resultType) {
9333
9485
  () => hook(message.list, message.items, message.options, context),
9334
9486
  send,
9335
9487
  (error) => ({ type: resultType, id: message.id, ok: false, error: "list-unavailable", reason: error }),
9336
- (result) => ({ type: resultType, id: message.id, ...result })
9488
+ (result2) => ({ type: resultType, id: message.id, ...result2 })
9337
9489
  );
9338
9490
  }
9339
9491
  function createListsService(options = {}) {
@@ -9417,7 +9569,7 @@ function createSerialService(options = {}) {
9417
9569
  () => options.open(serialMessage.request, context),
9418
9570
  send,
9419
9571
  (error) => ({ type: "serial.open.result", id, error }),
9420
- (result) => ({ type: "serial.open.result", id, session: result.session })
9572
+ (result2) => ({ type: "serial.open.result", id, session: result2.session })
9421
9573
  );
9422
9574
  return;
9423
9575
  }
@@ -9512,7 +9664,7 @@ function createBleService(options = {}) {
9512
9664
  () => options.open(bleMessage.request, context),
9513
9665
  send,
9514
9666
  (error) => ({ type: "ble.open.result", id, error }),
9515
- (result) => ({ type: "ble.open.result", id, session: result.session })
9667
+ (result2) => ({ type: "ble.open.result", id, session: result2.session })
9516
9668
  );
9517
9669
  return;
9518
9670
  }
@@ -9663,7 +9815,7 @@ function createWebrtcService(options = {}) {
9663
9815
  () => options.open(webrtcMessage.request, context),
9664
9816
  send,
9665
9817
  (error) => ({ type: "webrtc.open.result", id, error }),
9666
- (result) => ({ type: "webrtc.open.result", id, session: result.session })
9818
+ (result2) => ({ type: "webrtc.open.result", id, session: result2.session })
9667
9819
  );
9668
9820
  return;
9669
9821
  }
@@ -9803,7 +9955,7 @@ function decodeNrelay(value) {
9803
9955
  function convertBits(data, fromBits, toBits, pad2) {
9804
9956
  let acc = 0;
9805
9957
  let bits = 0;
9806
- const result = [];
9958
+ const result2 = [];
9807
9959
  const maxv = (1 << toBits) - 1;
9808
9960
  const maxAcc = (1 << fromBits + toBits - 1) - 1;
9809
9961
  for (const value of data) {
@@ -9812,15 +9964,15 @@ function convertBits(data, fromBits, toBits, pad2) {
9812
9964
  bits += fromBits;
9813
9965
  while (bits >= toBits) {
9814
9966
  bits -= toBits;
9815
- result.push(acc >> bits & maxv);
9967
+ result2.push(acc >> bits & maxv);
9816
9968
  }
9817
9969
  }
9818
9970
  if (pad2) {
9819
- if (bits > 0) result.push(acc << toBits - bits & maxv);
9971
+ if (bits > 0) result2.push(acc << toBits - bits & maxv);
9820
9972
  } else if (bits >= fromBits || (acc << toBits - bits & maxv) !== 0) {
9821
9973
  throw new Error("invalid bech32 padding");
9822
9974
  }
9823
- return result;
9975
+ return result2;
9824
9976
  }
9825
9977
  function settle5(call, send, okFalse, onValue) {
9826
9978
  let pending;
@@ -9840,14 +9992,14 @@ function createCommonService(options = {}) {
9840
9992
  const context = { windowId };
9841
9993
  if (message.type === "common.encodeNip19") {
9842
9994
  const input = message.input;
9843
- const result = input ? encodeNip19(input) : { ok: false, error: "invalid input" };
9844
- send({ type: "common.encodeNip19.result", id, ...result });
9995
+ const result2 = input ? encodeNip19(input) : { ok: false, error: "invalid input" };
9996
+ send({ type: "common.encodeNip19.result", id, ...result2 });
9845
9997
  return;
9846
9998
  }
9847
9999
  if (message.type === "common.decodeNip19") {
9848
10000
  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 });
10001
+ const result2 = typeof value === "string" ? decodeNip19Value(value) : { ok: false, error: "invalid value" };
10002
+ send({ type: "common.decodeNip19.result", id, ...result2 });
9851
10003
  return;
9852
10004
  }
9853
10005
  if (message.type === "common.getProfile") {
@@ -9860,7 +10012,7 @@ function createCommonService(options = {}) {
9860
10012
  () => options.getProfile(target, context),
9861
10013
  send,
9862
10014
  (error) => ({ type: "common.getProfile.result", id, ok: false, pubkey: target ?? "", error }),
9863
- (result) => ({ type: "common.getProfile.result", id, ...result })
10015
+ (result2) => ({ type: "common.getProfile.result", id, ...result2 })
9864
10016
  );
9865
10017
  return;
9866
10018
  }
@@ -9873,7 +10025,7 @@ function createCommonService(options = {}) {
9873
10025
  () => options.follows(context),
9874
10026
  send,
9875
10027
  (error) => ({ type: "common.follows.result", id, ok: false, pubkeys: [], error }),
9876
- (result) => ({ type: "common.follows.result", id, ...result })
10028
+ (result2) => ({ type: "common.follows.result", id, ...result2 })
9877
10029
  );
9878
10030
  return;
9879
10031
  }
@@ -9897,7 +10049,7 @@ function createCommonService(options = {}) {
9897
10049
  () => options.react(m.targetEventId, m.reaction, m.customEmojiHref, context),
9898
10050
  send,
9899
10051
  (error) => ({ type: "common.react.result", id, ok: false, error }),
9900
- (result) => ({ type: "common.react.result", id, ...result })
10052
+ (result2) => ({ type: "common.react.result", id, ...result2 })
9901
10053
  );
9902
10054
  return;
9903
10055
  }
@@ -9911,7 +10063,7 @@ function createCommonService(options = {}) {
9911
10063
  () => options.report(m.target, m.reason, m.text, context),
9912
10064
  send,
9913
10065
  (error) => ({ type: "common.report.result", id, ok: false, error }),
9914
- (result) => ({ type: "common.report.result", id, ...result })
10066
+ (result2) => ({ type: "common.report.result", id, ...result2 })
9915
10067
  );
9916
10068
  return;
9917
10069
  }
@@ -9934,7 +10086,7 @@ function settleAction(action, pubkeys, context, send, resultType, id, unavailabl
9934
10086
  () => action(pubkeys, context),
9935
10087
  send,
9936
10088
  (error) => ({ type: resultType, id, ok: false, error }),
9937
- (result) => ({ type: resultType, id, ...result })
10089
+ (result2) => ({ type: resultType, id, ...result2 })
9938
10090
  );
9939
10091
  }
9940
10092
 
@@ -10183,6 +10335,13 @@ function createMemoryRelayPool(getSimulation, confirmRequest) {
10183
10335
  } };
10184
10336
  }
10185
10337
  };
10338
+ },
10339
+ count(_relayUrls, filters) {
10340
+ const seen = /* @__PURE__ */ new Set();
10341
+ for (const event of events) {
10342
+ if (matchesAnyFilter2(event, filters)) seen.add(event.id);
10343
+ }
10344
+ return seen.size;
10186
10345
  }
10187
10346
  };
10188
10347
  }
@@ -10241,7 +10400,7 @@ function createDevUploader(getSimulation) {
10241
10400
  throw new Error("upload simulation is disabled");
10242
10401
  }
10243
10402
  const size = request.data instanceof Blob ? request.data.size : request.data.byteLength;
10244
- const result = {
10403
+ const result2 = {
10245
10404
  ok: true,
10246
10405
  uploadId: ctx.uploadId,
10247
10406
  status: "complete",
@@ -10250,8 +10409,8 @@ function createDevUploader(getSimulation) {
10250
10409
  size,
10251
10410
  mimeType: request.mimeType ?? (request.data instanceof Blob ? request.data.type : void 0)
10252
10411
  };
10253
- ctx.onStatus({ ...result, updatedAt: Date.now() });
10254
- return result;
10412
+ ctx.onStatus({ ...result2, updatedAt: Date.now() });
10413
+ return result2;
10255
10414
  },
10256
10415
  async status(uploadId) {
10257
10416
  const simulation = getSimulation();
@@ -10426,6 +10585,25 @@ function createDevServices(pool, getSimulation, onThemeService, confirmRequest,
10426
10585
  });
10427
10586
  services.outbox = createOutboxService({ router: createOutboxRouter(pool, getSimulation, confirmRequest, signerProvider) });
10428
10587
  }
10588
+ if (getSimulation().capabilities.domains.count && typeof pool.count === "function") {
10589
+ services.count = createCountService({
10590
+ count: async ({ filters }) => {
10591
+ const relays = getRelayUrls(getSimulation());
10592
+ return {
10593
+ ok: true,
10594
+ count: await pool.count(relays, filters),
10595
+ approximate: false,
10596
+ relays
10597
+ };
10598
+ },
10599
+ isFilterSupported: (filter) => {
10600
+ if (Object.keys(filter).length === 0) {
10601
+ return "broad empty filters are too expensive for the Paja count backend";
10602
+ }
10603
+ return true;
10604
+ }
10605
+ });
10606
+ }
10429
10607
  if (getSimulation().capabilities.domains.identity) {
10430
10608
  services.identity = createIdentityService({ getSigner: () => createRuntimeSigner(getSimulation, confirmRequest, signerProvider) });
10431
10609
  }
@@ -10500,7 +10678,7 @@ function createDevServices(pool, getSimulation, onThemeService, confirmRequest,
10500
10678
  }
10501
10679
  return services;
10502
10680
  }
10503
- function createPajaAdapter(config, getSimulation, onThemeService, confirmRequest, signerProvider) {
10681
+ function createPajaAdapter(config, getSimulation, onThemeService, confirmRequest, signerProvider, getIdentity) {
10504
10682
  const pool = createMemoryRelayPool(getSimulation, confirmRequest);
10505
10683
  const workerRelayEvents = [];
10506
10684
  return {
@@ -10540,10 +10718,13 @@ function createPajaAdapter(config, getSimulation, onThemeService, confirmRequest
10540
10718
  crypto: {
10541
10719
  verifyEvent: async () => true
10542
10720
  },
10543
- onNip5dIframeCreate: () => ({
10544
- dTag: config.window.dTag,
10545
- aggregateHash: config.window.aggregateHash
10546
- })
10721
+ onNip5dIframeCreate: () => {
10722
+ const identity = getIdentity?.();
10723
+ return {
10724
+ dTag: identity?.dTag ?? config.window.dTag,
10725
+ aggregateHash: identity?.aggregateHash ?? config.window.aggregateHash
10726
+ };
10727
+ }
10547
10728
  };
10548
10729
  }
10549
10730
 
@@ -10567,14 +10748,14 @@ function mul(a, b) {
10567
10748
  }
10568
10749
  var sbox = /* @__PURE__ */ (() => {
10569
10750
  const t = new Uint8Array(256);
10570
- for (let i3 = 0, x = 1; i3 < 256; i3++, x ^= mul2(x))
10571
- t[i3] = x;
10751
+ for (let i4 = 0, x = 1; i4 < 256; i4++, x ^= mul2(x))
10752
+ t[i4] = x;
10572
10753
  const box = new Uint8Array(256);
10573
10754
  box[0] = 99;
10574
- for (let i3 = 0; i3 < 255; i3++) {
10575
- let x = t[255 - i3];
10755
+ for (let i4 = 0; i4 < 255; i4++) {
10756
+ let x = t[255 - i4];
10576
10757
  x |= x << 8;
10577
- box[t[i3]] = (x ^ x >> 4 ^ x >> 5 ^ x >> 6 ^ x >> 7 ^ 99) & 255;
10758
+ box[t[i4]] = (x ^ x >> 4 ^ x >> 5 ^ x >> 6 ^ x >> 7 ^ 99) & 255;
10578
10759
  }
10579
10760
  clean2(t);
10580
10761
  return box;
@@ -10592,12 +10773,12 @@ function genTtable(sbox2, fn) {
10592
10773
  const T01 = new Uint32Array(256 * 256);
10593
10774
  const T23 = new Uint32Array(256 * 256);
10594
10775
  const sbox22 = new Uint16Array(256 * 256);
10595
- for (let i3 = 0; i3 < 256; i3++) {
10776
+ for (let i4 = 0; i4 < 256; i4++) {
10596
10777
  for (let j = 0; j < 256; j++) {
10597
- const idx = i3 * 256 + j;
10598
- T01[idx] = T0[i3] ^ T1[j];
10599
- T23[idx] = T2[i3] ^ T3[j];
10600
- sbox22[idx] = sbox2[i3] << 8 | sbox2[j];
10778
+ const idx = i4 * 256 + j;
10779
+ T01[idx] = T0[i4] ^ T1[j];
10780
+ T23[idx] = T2[i4] ^ T3[j];
10781
+ sbox22[idx] = sbox2[i4] << 8 | sbox2[j];
10601
10782
  }
10602
10783
  }
10603
10784
  return { sbox: sbox2, sbox2: sbox22, T0, T1, T2, T3, T01, T23 };
@@ -10606,8 +10787,8 @@ var tableEncoding = /* @__PURE__ */ genTtable(sbox, (s) => mul(s, 3) << 24 | s <
10606
10787
  var tableDecoding = /* @__PURE__ */ genTtable(invSbox, (s) => mul(s, 11) << 24 | mul(s, 13) << 16 | mul(s, 9) << 8 | mul(s, 14));
10607
10788
  var xPowers = /* @__PURE__ */ (() => {
10608
10789
  const p = new Uint8Array(16);
10609
- for (let i3 = 0, x = 1; i3 < 16; i3++, x = mul2(x))
10610
- p[i3] = x;
10790
+ for (let i4 = 0, x = 1; i4 < 16; i4++, x = mul2(x))
10791
+ p[i4] = x;
10611
10792
  return p;
10612
10793
  })();
10613
10794
  function expandKeyLE(key) {
@@ -10623,13 +10804,13 @@ function expandKeyLE(key) {
10623
10804
  const subByte = (n) => applySbox(sbox2, n, n, n, n);
10624
10805
  const xk = new Uint32Array(len + 28);
10625
10806
  xk.set(k32);
10626
- for (let i3 = Nk; i3 < xk.length; i3++) {
10627
- let t = xk[i3 - 1];
10628
- if (i3 % Nk === 0)
10629
- t = subByte(rotr32_8(t)) ^ xPowers[i3 / Nk - 1];
10630
- else if (Nk > 6 && i3 % Nk === 4)
10807
+ for (let i4 = Nk; i4 < xk.length; i4++) {
10808
+ let t = xk[i4 - 1];
10809
+ if (i4 % Nk === 0)
10810
+ t = subByte(rotr32_8(t)) ^ xPowers[i4 / Nk - 1];
10811
+ else if (Nk > 6 && i4 % Nk === 4)
10631
10812
  t = subByte(t);
10632
- xk[i3] = xk[i3 - Nk] ^ t;
10813
+ xk[i4] = xk[i4 - Nk] ^ t;
10633
10814
  }
10634
10815
  clean2(...toClean);
10635
10816
  return xk;
@@ -10640,15 +10821,15 @@ function expandKeyDecLE(key) {
10640
10821
  const Nk = encKey.length;
10641
10822
  const { sbox2 } = tableEncoding;
10642
10823
  const { T0, T1, T2, T3 } = tableDecoding;
10643
- for (let i3 = 0; i3 < Nk; i3 += 4) {
10824
+ for (let i4 = 0; i4 < Nk; i4 += 4) {
10644
10825
  for (let j = 0; j < 4; j++)
10645
- xk[i3 + j] = encKey[Nk - i3 - 4 + j];
10826
+ xk[i4 + j] = encKey[Nk - i4 - 4 + j];
10646
10827
  }
10647
10828
  clean2(encKey);
10648
- for (let i3 = 4; i3 < Nk - 4; i3++) {
10649
- const x = xk[i3];
10829
+ for (let i4 = 4; i4 < Nk - 4; i4++) {
10830
+ const x = xk[i4];
10650
10831
  const w = applySbox(sbox2, x, x, x, x);
10651
- xk[i3] = T0[w & 255] ^ T1[w >>> 8 & 255] ^ T2[w >>> 16 & 255] ^ T3[w >>> 24];
10832
+ xk[i4] = T0[w & 255] ^ T1[w >>> 8 & 255] ^ T2[w >>> 16 & 255] ^ T3[w >>> 24];
10652
10833
  }
10653
10834
  return xk;
10654
10835
  }
@@ -10663,7 +10844,7 @@ function encrypt(xk, s0, s1, s2, s3) {
10663
10844
  let k = 0;
10664
10845
  s0 ^= xk[k++], s1 ^= xk[k++], s2 ^= xk[k++], s3 ^= xk[k++];
10665
10846
  const rounds = xk.length / 4 - 2;
10666
- for (let i3 = 0; i3 < rounds; i3++) {
10847
+ for (let i4 = 0; i4 < rounds; i4++) {
10667
10848
  const t02 = xk[k++] ^ apply0123(T01, T23, s0, s1, s2, s3);
10668
10849
  const t12 = xk[k++] ^ apply0123(T01, T23, s1, s2, s3, s0);
10669
10850
  const t22 = xk[k++] ^ apply0123(T01, T23, s2, s3, s0, s1);
@@ -10681,7 +10862,7 @@ function decrypt(xk, s0, s1, s2, s3) {
10681
10862
  let k = 0;
10682
10863
  s0 ^= xk[k++], s1 ^= xk[k++], s2 ^= xk[k++], s3 ^= xk[k++];
10683
10864
  const rounds = xk.length / 4 - 2;
10684
- for (let i3 = 0; i3 < rounds; i3++) {
10865
+ for (let i4 = 0; i4 < rounds; i4++) {
10685
10866
  const t02 = xk[k++] ^ apply0123(T01, T23, s0, s3, s2, s1);
10686
10867
  const t12 = xk[k++] ^ apply0123(T01, T23, s1, s0, s3, s2);
10687
10868
  const t22 = xk[k++] ^ apply0123(T01, T23, s2, s1, s0, s3);
@@ -10730,8 +10911,8 @@ function validatePCKS(data, pcks5) {
10730
10911
  if (lastByte <= 0 || lastByte > 16)
10731
10912
  throw new Error("aes/pcks5: wrong padding");
10732
10913
  const out = data.subarray(0, -lastByte);
10733
- for (let i3 = 0; i3 < lastByte; i3++)
10734
- if (data[len - i3 - 1] !== lastByte)
10914
+ for (let i4 = 0; i4 < lastByte; i4++)
10915
+ if (data[len - i4 - 1] !== lastByte)
10735
10916
  throw new Error("aes/pcks5: wrong padding");
10736
10917
  return out;
10737
10918
  }
@@ -10740,8 +10921,8 @@ function padPCKS(left) {
10740
10921
  const tmp32 = u32(tmp);
10741
10922
  tmp.set(left);
10742
10923
  const paddingByte = BLOCK_SIZE - left.length;
10743
- for (let i3 = BLOCK_SIZE - paddingByte; i3 < BLOCK_SIZE; i3++)
10744
- tmp[i3] = paddingByte;
10924
+ for (let i4 = BLOCK_SIZE - paddingByte; i4 < BLOCK_SIZE; i4++)
10925
+ tmp[i4] = paddingByte;
10745
10926
  return tmp32;
10746
10927
  }
10747
10928
  var cbc = /* @__PURE__ */ wrapCipher({ blockSize: 16, nonceLength: 16 }, function aescbc(key, iv, opts = {}) {
@@ -10756,17 +10937,17 @@ var cbc = /* @__PURE__ */ wrapCipher({ blockSize: 16, nonceLength: 16 }, functio
10756
10937
  toClean.push(_iv = copyBytes2(_iv));
10757
10938
  const n32 = u32(_iv);
10758
10939
  let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3];
10759
- let i3 = 0;
10760
- for (; i3 + 4 <= b.length; ) {
10761
- s0 ^= b[i3 + 0], s1 ^= b[i3 + 1], s2 ^= b[i3 + 2], s3 ^= b[i3 + 3];
10940
+ let i4 = 0;
10941
+ for (; i4 + 4 <= b.length; ) {
10942
+ s0 ^= b[i4 + 0], s1 ^= b[i4 + 1], s2 ^= b[i4 + 2], s3 ^= b[i4 + 3];
10762
10943
  ({ s0, s1, s2, s3 } = encrypt(xk, s0, s1, s2, s3));
10763
- o[i3++] = s0, o[i3++] = s1, o[i3++] = s2, o[i3++] = s3;
10944
+ o[i4++] = s0, o[i4++] = s1, o[i4++] = s2, o[i4++] = s3;
10764
10945
  }
10765
10946
  if (pcks5) {
10766
- const tmp32 = padPCKS(plaintext.subarray(i3 * 4));
10947
+ const tmp32 = padPCKS(plaintext.subarray(i4 * 4));
10767
10948
  s0 ^= tmp32[0], s1 ^= tmp32[1], s2 ^= tmp32[2], s3 ^= tmp32[3];
10768
10949
  ({ s0, s1, s2, s3 } = encrypt(xk, s0, s1, s2, s3));
10769
- o[i3++] = s0, o[i3++] = s1, o[i3++] = s2, o[i3++] = s3;
10950
+ o[i4++] = s0, o[i4++] = s1, o[i4++] = s2, o[i4++] = s3;
10770
10951
  }
10771
10952
  clean2(...toClean);
10772
10953
  return _out;
@@ -10786,11 +10967,11 @@ var cbc = /* @__PURE__ */ wrapCipher({ blockSize: 16, nonceLength: 16 }, functio
10786
10967
  const b = u32(ciphertext);
10787
10968
  const o = u32(dst);
10788
10969
  let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3];
10789
- for (let i3 = 0; i3 + 4 <= b.length; ) {
10970
+ for (let i4 = 0; i4 + 4 <= b.length; ) {
10790
10971
  const ps0 = s0, ps1 = s1, ps2 = s2, ps3 = s3;
10791
- s0 = b[i3 + 0], s1 = b[i3 + 1], s2 = b[i3 + 2], s3 = b[i3 + 3];
10972
+ s0 = b[i4 + 0], s1 = b[i4 + 1], s2 = b[i4 + 2], s3 = b[i4 + 3];
10792
10973
  const { s0: o0, s1: o1, s2: o2, s3: o3 } = decrypt(xk, s0, s1, s2, s3);
10793
- o[i3++] = o0 ^ ps0, o[i3++] = o1 ^ ps1, o[i3++] = o2 ^ ps2, o[i3++] = o3 ^ ps3;
10974
+ o[i4++] = o0 ^ ps0, o[i4++] = o1 ^ ps1, o[i4++] = o2 ^ ps2, o[i4++] = o3 ^ ps3;
10794
10975
  }
10795
10976
  clean2(...toClean);
10796
10977
  return validatePCKS(dst, pcks5);
@@ -10811,9 +10992,9 @@ function encryptBlock(xk, block2) {
10811
10992
  }
10812
10993
  function dbl(block2) {
10813
10994
  let carry = 0;
10814
- for (let i3 = BLOCK_SIZE - 1; i3 >= 0; i3--) {
10815
- const newCarry = (block2[i3] & 128) >>> 7;
10816
- block2[i3] = block2[i3] << 1 | carry;
10995
+ for (let i4 = BLOCK_SIZE - 1; i4 >= 0; i4--) {
10996
+ const newCarry = (block2[i4] & 128) >>> 7;
10997
+ block2[i4] = block2[i4] << 1 | carry;
10817
10998
  carry = newCarry;
10818
10999
  }
10819
11000
  if (carry) {
@@ -10824,8 +11005,8 @@ function dbl(block2) {
10824
11005
  function xorBlock(a, b) {
10825
11006
  if (a.length !== b.length)
10826
11007
  throw new Error("xorBlock: blocks must have same length");
10827
- for (let i3 = 0; i3 < a.length; i3++) {
10828
- a[i3] = a[i3] ^ b[i3];
11008
+ for (let i4 = 0; i4 < a.length; i4++) {
11009
+ a[i4] = a[i4] ^ b[i4];
10829
11010
  }
10830
11011
  return a;
10831
11012
  }
@@ -10883,8 +11064,8 @@ var _CMAC = class {
10883
11064
  m_last = xorBlock(padded, this.k2);
10884
11065
  }
10885
11066
  let x = new Uint8Array(BLOCK_SIZE);
10886
- for (let i3 = 0; i3 < n - 1; i3++) {
10887
- const m_i = buffer.subarray(i3 * BLOCK_SIZE, (i3 + 1) * BLOCK_SIZE);
11067
+ for (let i4 = 0; i4 < n - 1; i4++) {
11068
+ const m_i = buffer.subarray(i4 * BLOCK_SIZE, (i4 + 1) * BLOCK_SIZE);
10888
11069
  xorBlock(x, m_i);
10889
11070
  encryptBlock(this.xk, x);
10890
11071
  }
@@ -11017,8 +11198,8 @@ function binarySearch(arr, compare) {
11017
11198
  return [start, false];
11018
11199
  }
11019
11200
  function mergeReverseSortedLists(list1, list2) {
11020
- const result = new Array(list1.length + list2.length);
11021
- result.length = 0;
11201
+ const result2 = new Array(list1.length + list2.length);
11202
+ result2.length = 0;
11022
11203
  let i1 = 0;
11023
11204
  let i22 = 0;
11024
11205
  let sameTimestampIds = [];
@@ -11031,40 +11212,40 @@ function mergeReverseSortedLists(list1, list2) {
11031
11212
  next = list2[i22];
11032
11213
  i22++;
11033
11214
  }
11034
- if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {
11215
+ if (result2.length > 0 && result2[result2.length - 1].created_at === next.created_at) {
11035
11216
  if (sameTimestampIds.includes(next.id))
11036
11217
  continue;
11037
11218
  } else {
11038
11219
  sameTimestampIds.length = 0;
11039
11220
  }
11040
- result.push(next);
11221
+ result2.push(next);
11041
11222
  sameTimestampIds.push(next.id);
11042
11223
  }
11043
11224
  while (i1 < list1.length) {
11044
11225
  const next = list1[i1];
11045
11226
  i1++;
11046
- if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {
11227
+ if (result2.length > 0 && result2[result2.length - 1].created_at === next.created_at) {
11047
11228
  if (sameTimestampIds.includes(next.id))
11048
11229
  continue;
11049
11230
  } else {
11050
11231
  sameTimestampIds.length = 0;
11051
11232
  }
11052
- result.push(next);
11233
+ result2.push(next);
11053
11234
  sameTimestampIds.push(next.id);
11054
11235
  }
11055
11236
  while (i22 < list2.length) {
11056
11237
  const next = list2[i22];
11057
11238
  i22++;
11058
- if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {
11239
+ if (result2.length > 0 && result2[result2.length - 1].created_at === next.created_at) {
11059
11240
  if (sameTimestampIds.includes(next.id))
11060
11241
  continue;
11061
11242
  } else {
11062
11243
  sameTimestampIds.length = 0;
11063
11244
  }
11064
- result.push(next);
11245
+ result2.push(next);
11065
11246
  sameTimestampIds.push(next.id);
11066
11247
  }
11067
- return result;
11248
+ return result2;
11068
11249
  }
11069
11250
  var JS2 = class {
11070
11251
  generateSecretKey() {
@@ -11516,7 +11697,7 @@ function decode2(code) {
11516
11697
  }
11517
11698
  }
11518
11699
  function parseTLV2(data) {
11519
- let result = {};
11700
+ let result2 = {};
11520
11701
  let rest = data;
11521
11702
  while (rest.length > 0) {
11522
11703
  let t = rest[0];
@@ -11525,10 +11706,10 @@ function parseTLV2(data) {
11525
11706
  rest = rest.slice(2 + l);
11526
11707
  if (v.length < l)
11527
11708
  throw new Error(`not enough data to read on TLV ${t}`);
11528
- result[t] = result[t] || [];
11529
- result[t].push(v);
11709
+ result2[t] = result2[t] || [];
11710
+ result2[t].push(v);
11530
11711
  }
11531
- return result;
11712
+ return result2;
11532
11713
  }
11533
11714
  function nsecEncode(key) {
11534
11715
  return encodeBytes2("nsec", key);
@@ -11679,7 +11860,7 @@ __export(nip10_exports, {
11679
11860
  parse: () => parse
11680
11861
  });
11681
11862
  function parse(event) {
11682
- const result = {
11863
+ const result2 = {
11683
11864
  reply: void 0,
11684
11865
  root: void 0,
11685
11866
  mentions: [],
@@ -11698,15 +11879,15 @@ function parse(event) {
11698
11879
  author: eTagAuthor
11699
11880
  };
11700
11881
  if (eTagMarker === "root") {
11701
- result.root = eventPointer;
11882
+ result2.root = eventPointer;
11702
11883
  continue;
11703
11884
  }
11704
11885
  if (eTagMarker === "reply") {
11705
- result.reply = eventPointer;
11886
+ result2.reply = eventPointer;
11706
11887
  continue;
11707
11888
  }
11708
11889
  if (eTagMarker === "mention") {
11709
- result.mentions.push(eventPointer);
11890
+ result2.mentions.push(eventPointer);
11710
11891
  continue;
11711
11892
  }
11712
11893
  if (!maybeParent) {
@@ -11714,40 +11895,40 @@ function parse(event) {
11714
11895
  } else {
11715
11896
  maybeRoot = eventPointer;
11716
11897
  }
11717
- result.mentions.push(eventPointer);
11898
+ result2.mentions.push(eventPointer);
11718
11899
  continue;
11719
11900
  }
11720
11901
  if (tag[0] === "q" && tag[1]) {
11721
11902
  const [_, eTagEventId, eTagRelayUrl] = tag;
11722
- result.quotes.push({
11903
+ result2.quotes.push({
11723
11904
  id: eTagEventId,
11724
11905
  relays: eTagRelayUrl ? [eTagRelayUrl] : []
11725
11906
  });
11726
11907
  }
11727
11908
  if (tag[0] === "p" && tag[1]) {
11728
- result.profiles.push({
11909
+ result2.profiles.push({
11729
11910
  pubkey: tag[1],
11730
11911
  relays: tag[2] ? [tag[2]] : []
11731
11912
  });
11732
11913
  continue;
11733
11914
  }
11734
11915
  }
11735
- if (!result.root) {
11736
- result.root = maybeRoot || maybeParent || result.reply;
11916
+ if (!result2.root) {
11917
+ result2.root = maybeRoot || maybeParent || result2.reply;
11737
11918
  }
11738
- if (!result.reply) {
11739
- result.reply = maybeParent || result.root;
11919
+ if (!result2.reply) {
11920
+ result2.reply = maybeParent || result2.root;
11740
11921
  }
11741
11922
  ;
11742
- [result.reply, result.root].forEach((ref) => {
11923
+ [result2.reply, result2.root].forEach((ref) => {
11743
11924
  if (!ref)
11744
11925
  return;
11745
- let idx = result.mentions.indexOf(ref);
11926
+ let idx = result2.mentions.indexOf(ref);
11746
11927
  if (idx !== -1) {
11747
- result.mentions.splice(idx, 1);
11928
+ result2.mentions.splice(idx, 1);
11748
11929
  }
11749
11930
  if (ref.author) {
11750
- let author = result.profiles.find((p) => p.pubkey === ref.author);
11931
+ let author = result2.profiles.find((p) => p.pubkey === ref.author);
11751
11932
  if (author && author.relays) {
11752
11933
  if (!ref.relays) {
11753
11934
  ref.relays = [];
@@ -11760,9 +11941,9 @@ function parse(event) {
11760
11941
  }
11761
11942
  }
11762
11943
  });
11763
- result.mentions.forEach((ref) => {
11944
+ result2.mentions.forEach((ref) => {
11764
11945
  if (ref.author) {
11765
- let author = result.profiles.find((p) => p.pubkey === ref.author);
11946
+ let author = result2.profiles.find((p) => p.pubkey === ref.author);
11766
11947
  if (author && author.relays) {
11767
11948
  if (!ref.relays) {
11768
11949
  ref.relays = [];
@@ -11775,7 +11956,7 @@ function parse(event) {
11775
11956
  }
11776
11957
  }
11777
11958
  });
11778
- return result;
11959
+ return result2;
11779
11960
  }
11780
11961
  var nip11_exports = {};
11781
11962
  __export(nip11_exports, {
@@ -13444,11 +13625,11 @@ function createNipEncryptionMethods(state, prefix) {
13444
13625
  }
13445
13626
  async function connectNip46State(state, secret) {
13446
13627
  await openWebSocket(state);
13447
- const result = await sendRequestWithId(state, crypto.randomUUID(), "connect", [state.bunkerPubkey, secret ?? ""]);
13448
- if (typeof result === "string") {
13449
- state.authorizedPubkey = result;
13450
- } else if (result && typeof result === "object" && "pubkey" in result) {
13451
- state.authorizedPubkey = result.pubkey;
13628
+ const result2 = await sendRequestWithId(state, crypto.randomUUID(), "connect", [state.bunkerPubkey, secret ?? ""]);
13629
+ if (typeof result2 === "string") {
13630
+ state.authorizedPubkey = result2;
13631
+ } else if (result2 && typeof result2 === "object" && "pubkey" in result2) {
13632
+ state.authorizedPubkey = result2.pubkey;
13452
13633
  } else {
13453
13634
  const pkResult = await sendRequestWithId(state, crypto.randomUUID(), "get_public_key", []);
13454
13635
  state.authorizedPubkey = pkResult;
@@ -13462,11 +13643,11 @@ function createNip46Signer(state) {
13462
13643
  return sendRequestWithId(state, crypto.randomUUID(), "get_public_key", []);
13463
13644
  },
13464
13645
  signEvent: async (event) => {
13465
- const result = await sendRequestWithId(state, crypto.randomUUID(), "sign_event", [JSON.stringify(event)]);
13466
- if (typeof result === "string") {
13467
- return JSON.parse(result);
13646
+ const result2 = await sendRequestWithId(state, crypto.randomUUID(), "sign_event", [JSON.stringify(event)]);
13647
+ if (typeof result2 === "string") {
13648
+ return JSON.parse(result2);
13468
13649
  }
13469
- return result;
13650
+ return result2;
13470
13651
  },
13471
13652
  nip04: createNipEncryptionMethods(state, "nip04"),
13472
13653
  nip44: createNipEncryptionMethods(state, "nip44")
@@ -13614,6 +13795,7 @@ var PAJA_SIMULATION_DOMAINS = [
13614
13795
  "media",
13615
13796
  "upload",
13616
13797
  "intent",
13798
+ "count",
13617
13799
  "link",
13618
13800
  "common",
13619
13801
  "lists",
@@ -13726,8 +13908,8 @@ function installPajaOriginRegistryProxy(originRegistry2, stateRef) {
13726
13908
  };
13727
13909
  const originalGetWindowId = originRegistry2.getWindowId.bind(originRegistry2);
13728
13910
  originRegistry2.getWindowId = (win) => {
13729
- const result = originalGetWindowId(win);
13730
- if (result) return result;
13911
+ const result2 = originalGetWindowId(win);
13912
+ if (result2) return result2;
13731
13913
  const real = proxyToReal.get(win);
13732
13914
  return real ? originalGetWindowId(real) : void 0;
13733
13915
  };
@@ -13848,117 +14030,1800 @@ function renderSignerStatus(state, signerPubkey) {
13848
14030
  controls.replaceChildren(devButton, nip07Button, bunkerInput, bunkerButton);
13849
14031
  }
13850
14032
 
13851
- // src/browser-host.ts
13852
- var bridgeRef = null;
13853
- function readConfig() {
13854
- const script = document.getElementById("kehto-paja-config");
13855
- if (!script?.textContent) {
13856
- throw new Error("Missing Kehto Paja config.");
14033
+ // ../../node_modules/.pnpm/nostr-tools@2.23.3_typescript@5.9.3/node_modules/nostr-tools/lib/esm/pool.js
14034
+ var verifiedSymbol3 = /* @__PURE__ */ Symbol("verified");
14035
+ var isRecord3 = (obj) => obj instanceof Object;
14036
+ function validateEvent3(event) {
14037
+ if (!isRecord3(event))
14038
+ return false;
14039
+ if (typeof event.kind !== "number")
14040
+ return false;
14041
+ if (typeof event.content !== "string")
14042
+ return false;
14043
+ if (typeof event.created_at !== "number")
14044
+ return false;
14045
+ if (typeof event.pubkey !== "string")
14046
+ return false;
14047
+ if (!event.pubkey.match(/^[a-f0-9]{64}$/))
14048
+ return false;
14049
+ if (!Array.isArray(event.tags))
14050
+ return false;
14051
+ for (let i22 = 0; i22 < event.tags.length; i22++) {
14052
+ let tag = event.tags[i22];
14053
+ if (!Array.isArray(tag))
14054
+ return false;
14055
+ for (let j = 0; j < tag.length; j++) {
14056
+ if (typeof tag[j] !== "string")
14057
+ return false;
14058
+ }
13857
14059
  }
13858
- return JSON.parse(script.textContent);
14060
+ return true;
13859
14061
  }
13860
- async function readLatestConfig(fallback) {
14062
+ var utf8Decoder4 = new TextDecoder("utf-8");
14063
+ var utf8Encoder4 = new TextEncoder();
14064
+ function normalizeURL2(url) {
13861
14065
  try {
13862
- const response = await fetch("/__kehto/config.json", { cache: "no-store" });
13863
- if (!response.ok) return fallback;
13864
- return await response.json();
13865
- } catch (error) {
13866
- console.warn("[paja] config refresh failed; using embedded config", error);
13867
- return fallback;
14066
+ if (url.indexOf("://") === -1)
14067
+ url = "wss://" + url;
14068
+ let p = new URL(url);
14069
+ if (p.protocol === "http:")
14070
+ p.protocol = "ws:";
14071
+ else if (p.protocol === "https:")
14072
+ p.protocol = "wss:";
14073
+ p.pathname = p.pathname.replace(/\/+/g, "/");
14074
+ if (p.pathname.endsWith("/"))
14075
+ p.pathname = p.pathname.slice(0, -1);
14076
+ if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:")
14077
+ p.port = "";
14078
+ p.searchParams.sort();
14079
+ p.hash = "";
14080
+ return p.toString();
14081
+ } catch (e) {
14082
+ throw new Error(`Invalid URL: ${url}`);
13868
14083
  }
13869
14084
  }
13870
- function setTargetUrlDisplay(config, frame) {
13871
- const targetEl = document.querySelector(".target");
13872
- if (targetEl) {
13873
- targetEl.textContent = config.target.url;
13874
- targetEl.setAttribute("title", config.target.url);
14085
+ var JS3 = class {
14086
+ generateSecretKey() {
14087
+ return schnorr.utils.randomSecretKey();
14088
+ }
14089
+ getPublicKey(secretKey) {
14090
+ return bytesToHex2(schnorr.getPublicKey(secretKey));
14091
+ }
14092
+ finalizeEvent(t, secretKey) {
14093
+ const event = t;
14094
+ event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
14095
+ event.id = getEventHash3(event);
14096
+ event.sig = bytesToHex2(schnorr.sign(hexToBytes2(getEventHash3(event)), secretKey));
14097
+ event[verifiedSymbol3] = true;
14098
+ return event;
14099
+ }
14100
+ verifyEvent(event) {
14101
+ if (typeof event[verifiedSymbol3] === "boolean")
14102
+ return event[verifiedSymbol3];
14103
+ try {
14104
+ const hash = getEventHash3(event);
14105
+ if (hash !== event.id) {
14106
+ event[verifiedSymbol3] = false;
14107
+ return false;
14108
+ }
14109
+ const valid = schnorr.verify(hexToBytes2(event.sig), hexToBytes2(hash), hexToBytes2(event.pubkey));
14110
+ event[verifiedSymbol3] = valid;
14111
+ return valid;
14112
+ } catch (err) {
14113
+ event[verifiedSymbol3] = false;
14114
+ return false;
14115
+ }
13875
14116
  }
13876
- frame.dataset.targetUrl = config.target.url;
14117
+ };
14118
+ function serializeEvent3(evt) {
14119
+ if (!validateEvent3(evt))
14120
+ throw new Error("can't serialize event with wrong or missing properties");
14121
+ return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
13877
14122
  }
13878
- function setStatus(state, status) {
13879
- state.status = status;
13880
- const statusEl = document.getElementById("lifecycle-status");
13881
- if (statusEl) statusEl.textContent = status;
14123
+ function getEventHash3(event) {
14124
+ let eventHash = sha256(utf8Encoder4.encode(serializeEvent3(event)));
14125
+ return bytesToHex2(eventHash);
13882
14126
  }
13883
- function setSimulationStatus(state) {
13884
- const statusEl = document.getElementById("simulation-status");
13885
- if (statusEl) statusEl.textContent = summarizePajaSimulation(state.simulation);
13886
- const themeSelect = document.getElementById("simulation-theme");
13887
- if (themeSelect instanceof HTMLSelectElement) themeSelect.value = state.simulation.theme.mode;
13888
- renderPajaDevtools(state, { bridge: bridgeRef, devSignerPubkey: PAJA_DEV_SIGNER_PUBKEY });
14127
+ var i3 = new JS3();
14128
+ var generateSecretKey3 = i3.generateSecretKey;
14129
+ var getPublicKey3 = i3.getPublicKey;
14130
+ var finalizeEvent3 = i3.finalizeEvent;
14131
+ var verifyEvent3 = i3.verifyEvent;
14132
+ var ClientAuth2 = 22242;
14133
+ function matchFilter(filter, event) {
14134
+ if (filter.ids && filter.ids.indexOf(event.id) === -1) {
14135
+ return false;
14136
+ }
14137
+ if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {
14138
+ return false;
14139
+ }
14140
+ if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {
14141
+ return false;
14142
+ }
14143
+ for (let f in filter) {
14144
+ if (f[0] === "#") {
14145
+ let tagName = f.slice(1);
14146
+ let values = filter[`#${tagName}`];
14147
+ if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values.indexOf(v) !== -1))
14148
+ return false;
14149
+ }
14150
+ }
14151
+ if (filter.since && event.created_at < filter.since)
14152
+ return false;
14153
+ if (filter.until && event.created_at > filter.until)
14154
+ return false;
14155
+ return true;
13889
14156
  }
13890
- function getFrame() {
13891
- const frame = document.getElementById("napplet-frame");
13892
- if (!(frame instanceof HTMLIFrameElement)) {
13893
- throw new Error("Missing Kehto Paja iframe.");
14157
+ function matchFilters(filters, event) {
14158
+ for (let i22 = 0; i22 < filters.length; i22++) {
14159
+ if (matchFilter(filters[i22], event)) {
14160
+ return true;
14161
+ }
13894
14162
  }
13895
- frame.sandbox.add("allow-scripts");
13896
- frame.sandbox.remove("allow-same-origin");
13897
- return frame;
14163
+ return false;
13898
14164
  }
13899
- function confirmPajaRequest(state, request) {
13900
- const event = request.event;
13901
- const kind = typeof event.kind === "number" ? event.kind : "unknown";
13902
- const content = typeof event.content === "string" && event.content.length > 0 ? `
13903
-
13904
- ${event.content.slice(0, 240)}` : "";
13905
- const allowed = window.confirm(`Paja ${request.action} request
13906
- kind: ${kind}${content}`);
13907
- appendPajaMessageLog(state, "paja", {
13908
- type: `paja.${request.action}.${allowed ? "confirmed" : "denied"}`,
13909
- kind
13910
- });
13911
- return allowed;
14165
+ function getHex642(json, field) {
14166
+ let len = field.length + 3;
14167
+ let idx = json.indexOf(`"${field}":`) + len;
14168
+ let s = json.slice(idx).indexOf(`"`) + idx + 1;
14169
+ return json.slice(s, s + 64);
14170
+ }
14171
+ function getSubscriptionId2(json) {
14172
+ let idx = json.slice(0, 22).indexOf(`"EVENT"`);
14173
+ if (idx === -1)
14174
+ return null;
14175
+ let pstart = json.slice(idx + 7 + 1).indexOf(`"`);
14176
+ if (pstart === -1)
14177
+ return null;
14178
+ let start = idx + 7 + 1 + pstart;
14179
+ let pend = json.slice(start + 1, 80).indexOf(`"`);
14180
+ if (pend === -1)
14181
+ return null;
14182
+ let end = start + 1 + pend;
14183
+ return json.slice(start + 1, end);
13912
14184
  }
13913
- function getTargetIdentity2(config) {
14185
+ function makeAuthEvent2(relayURL, challenge2) {
13914
14186
  return {
13915
- pubkey: "",
13916
- dTag: config.window.dTag,
13917
- aggregateHash: config.window.aggregateHash
14187
+ kind: ClientAuth2,
14188
+ created_at: Math.floor(Date.now() / 1e3),
14189
+ tags: [
14190
+ ["relay", relayURL],
14191
+ ["challenge", challenge2]
14192
+ ],
14193
+ content: ""
13918
14194
  };
13919
14195
  }
13920
- function registerFrameForGeneration(bridge, frame, config, generation) {
13921
- const win = frame.contentWindow;
13922
- if (!win) return null;
13923
- const windowId = `${config.window.id}:${generation}`;
13924
- originRegistry.register(win, windowId, {
13925
- dTag: config.window.dTag,
13926
- aggregateHash: config.window.aggregateHash
13927
- });
13928
- bridge.runtime.sessionRegistry.register(windowId, {
13929
- ...getTargetIdentity2(config),
13930
- windowId,
13931
- origin: "null",
13932
- type: "napplet",
13933
- registeredAt: Date.now(),
13934
- instanceId: `${windowId}:${Date.now()}`,
13935
- provenance: "nip-5d"
13936
- });
13937
- return windowId;
13938
- }
13939
- function navigateFrame(bridge, frame, config, generation) {
13940
- const windowId = registerFrameForGeneration(bridge, frame, config, generation);
13941
- frame.src = config.target.url;
13942
- return windowId;
13943
- }
13944
- function createHostSignerController(getState) {
13945
- return createPajaSignerController({
13946
- confirmRequest: (request) => confirmPajaRequest(getState(), request),
13947
- onChange(signer) {
13948
- const state = getState();
13949
- if (!state) return;
13950
- state.signer = signer;
13951
- appendPajaMessageLog(state, "paja", {
13952
- type: `paja.signer.${signer.method}.${signer.status}`,
13953
- pubkey: signer.pubkey,
13954
- relay: signer.relay,
13955
- error: signer.error
13956
- });
13957
- setSimulationStatus(state);
13958
- if (signer.status === "connected") state.reload();
13959
- }
13960
- });
13961
- }
14196
+ var SendingOnClosedConnection = class extends Error {
14197
+ constructor(message, relay) {
14198
+ super(`Tried to send message '${message} on a closed connection to ${relay}.`);
14199
+ this.name = "SendingOnClosedConnection";
14200
+ }
14201
+ };
14202
+ var AbstractRelay = class {
14203
+ url;
14204
+ _connected = false;
14205
+ onclose = null;
14206
+ onnotice = (msg) => console.debug(`NOTICE from ${this.url}: ${msg}`);
14207
+ onauth;
14208
+ baseEoseTimeout = 4400;
14209
+ publishTimeout = 4400;
14210
+ pingFrequency = 29e3;
14211
+ pingTimeout = 2e4;
14212
+ resubscribeBackoff = [1e4, 1e4, 1e4, 2e4, 2e4, 3e4, 6e4];
14213
+ openSubs = /* @__PURE__ */ new Map();
14214
+ enablePing;
14215
+ enableReconnect;
14216
+ idleSince = Date.now();
14217
+ ongoingOperations = 0;
14218
+ reconnectTimeoutHandle;
14219
+ pingIntervalHandle;
14220
+ reconnectAttempts = 0;
14221
+ skipReconnection = false;
14222
+ connectionPromise;
14223
+ openCountRequests = /* @__PURE__ */ new Map();
14224
+ openEventPublishes = /* @__PURE__ */ new Map();
14225
+ ws;
14226
+ challenge;
14227
+ authPromise;
14228
+ serial = 0;
14229
+ verifyEvent;
14230
+ _WebSocket;
14231
+ constructor(url, opts) {
14232
+ this.url = normalizeURL2(url);
14233
+ this.verifyEvent = opts.verifyEvent;
14234
+ this._WebSocket = opts.websocketImplementation || WebSocket;
14235
+ this.enablePing = opts.enablePing;
14236
+ this.enableReconnect = opts.enableReconnect || false;
14237
+ }
14238
+ static async connect(url, opts) {
14239
+ const relay = new AbstractRelay(url, opts);
14240
+ await relay.connect(opts);
14241
+ return relay;
14242
+ }
14243
+ closeAllSubscriptions(reason) {
14244
+ for (let [_, sub] of this.openSubs) {
14245
+ sub.close(reason);
14246
+ }
14247
+ this.openSubs.clear();
14248
+ for (let [_, ep] of this.openEventPublishes) {
14249
+ ep.reject(new Error(reason));
14250
+ }
14251
+ this.openEventPublishes.clear();
14252
+ for (let [_, cr] of this.openCountRequests) {
14253
+ cr.reject(new Error(reason));
14254
+ }
14255
+ this.openCountRequests.clear();
14256
+ }
14257
+ get connected() {
14258
+ return this._connected;
14259
+ }
14260
+ async reconnect() {
14261
+ const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)];
14262
+ this.reconnectAttempts++;
14263
+ this.reconnectTimeoutHandle = setTimeout(async () => {
14264
+ try {
14265
+ await this.connect();
14266
+ } catch (err) {
14267
+ }
14268
+ }, backoff);
14269
+ }
14270
+ handleHardClose(reason) {
14271
+ if (this.pingIntervalHandle) {
14272
+ clearInterval(this.pingIntervalHandle);
14273
+ this.pingIntervalHandle = void 0;
14274
+ }
14275
+ this._connected = false;
14276
+ this.connectionPromise = void 0;
14277
+ this.idleSince = void 0;
14278
+ if (this.enableReconnect && !this.skipReconnection) {
14279
+ this.reconnect();
14280
+ } else {
14281
+ this.onclose?.();
14282
+ this.closeAllSubscriptions(reason);
14283
+ }
14284
+ }
14285
+ async connect(opts) {
14286
+ let connectionTimeoutHandle;
14287
+ if (this.connectionPromise)
14288
+ return this.connectionPromise;
14289
+ this.challenge = void 0;
14290
+ this.authPromise = void 0;
14291
+ this.skipReconnection = false;
14292
+ this.connectionPromise = new Promise((resolve, reject) => {
14293
+ if (opts?.timeout) {
14294
+ connectionTimeoutHandle = setTimeout(() => {
14295
+ reject("connection timed out");
14296
+ this.connectionPromise = void 0;
14297
+ this.skipReconnection = true;
14298
+ this.onclose?.();
14299
+ this.handleHardClose("relay connection timed out");
14300
+ }, opts.timeout);
14301
+ }
14302
+ if (opts?.abort) {
14303
+ opts.abort.onabort = reject;
14304
+ }
14305
+ try {
14306
+ this.ws = new this._WebSocket(this.url);
14307
+ } catch (err) {
14308
+ clearTimeout(connectionTimeoutHandle);
14309
+ reject(err);
14310
+ return;
14311
+ }
14312
+ this.ws.onopen = () => {
14313
+ if (this.reconnectTimeoutHandle) {
14314
+ clearTimeout(this.reconnectTimeoutHandle);
14315
+ this.reconnectTimeoutHandle = void 0;
14316
+ }
14317
+ clearTimeout(connectionTimeoutHandle);
14318
+ this._connected = true;
14319
+ const isReconnection = this.reconnectAttempts > 0;
14320
+ this.reconnectAttempts = 0;
14321
+ for (const sub of this.openSubs.values()) {
14322
+ sub.eosed = false;
14323
+ if (isReconnection) {
14324
+ for (let f = 0; f < sub.filters.length; f++) {
14325
+ if (sub.lastEmitted) {
14326
+ sub.filters[f].since = sub.lastEmitted + 1;
14327
+ }
14328
+ }
14329
+ }
14330
+ sub.fire();
14331
+ }
14332
+ if (this.enablePing) {
14333
+ this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency);
14334
+ }
14335
+ resolve();
14336
+ };
14337
+ this.ws.onerror = () => {
14338
+ clearTimeout(connectionTimeoutHandle);
14339
+ reject("connection failed");
14340
+ this.connectionPromise = void 0;
14341
+ this.skipReconnection = true;
14342
+ this.onclose?.();
14343
+ this.handleHardClose("relay connection failed");
14344
+ };
14345
+ this.ws.onclose = (ev) => {
14346
+ clearTimeout(connectionTimeoutHandle);
14347
+ reject(ev.message || "websocket closed");
14348
+ this.handleHardClose("relay connection closed");
14349
+ };
14350
+ this.ws.onmessage = this._onmessage.bind(this);
14351
+ });
14352
+ return this.connectionPromise;
14353
+ }
14354
+ waitForPingPong() {
14355
+ return new Promise((resolve) => {
14356
+ ;
14357
+ this.ws.once("pong", () => resolve(true));
14358
+ this.ws.ping();
14359
+ });
14360
+ }
14361
+ waitForDummyReq() {
14362
+ return new Promise((resolve, reject) => {
14363
+ if (!this.connectionPromise)
14364
+ return reject(new Error(`no connection to ${this.url}, can't ping`));
14365
+ try {
14366
+ const sub = this.subscribe(
14367
+ [{ ids: ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], limit: 0 }],
14368
+ {
14369
+ label: "<forced-ping>",
14370
+ oneose: () => {
14371
+ resolve(true);
14372
+ sub.close();
14373
+ },
14374
+ onclose() {
14375
+ resolve(true);
14376
+ },
14377
+ eoseTimeout: this.pingTimeout + 1e3
14378
+ }
14379
+ );
14380
+ } catch (err) {
14381
+ reject(err);
14382
+ }
14383
+ });
14384
+ }
14385
+ async pingpong() {
14386
+ if (this.ws?.readyState === 1) {
14387
+ const result2 = await Promise.any([
14388
+ this.ws && this.ws.ping && this.ws.once ? this.waitForPingPong() : this.waitForDummyReq(),
14389
+ new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
14390
+ ]);
14391
+ if (!result2) {
14392
+ if (this.ws?.readyState === this._WebSocket.OPEN) {
14393
+ this.ws?.close();
14394
+ }
14395
+ }
14396
+ }
14397
+ }
14398
+ async send(message) {
14399
+ if (!this.connectionPromise)
14400
+ throw new SendingOnClosedConnection(message, this.url);
14401
+ this.connectionPromise.then(() => {
14402
+ this.ws?.send(message);
14403
+ });
14404
+ }
14405
+ async auth(signAuthEvent) {
14406
+ const challenge2 = this.challenge;
14407
+ if (!challenge2)
14408
+ throw new Error("can't perform auth, no challenge was received");
14409
+ if (this.authPromise)
14410
+ return this.authPromise;
14411
+ this.authPromise = new Promise(async (resolve, reject) => {
14412
+ try {
14413
+ let evt = await signAuthEvent(makeAuthEvent2(this.url, challenge2));
14414
+ let timeout = setTimeout(() => {
14415
+ let ep = this.openEventPublishes.get(evt.id);
14416
+ if (ep) {
14417
+ ep.reject(new Error("auth timed out"));
14418
+ this.openEventPublishes.delete(evt.id);
14419
+ }
14420
+ }, this.publishTimeout);
14421
+ this.openEventPublishes.set(evt.id, { resolve, reject, timeout });
14422
+ this.send('["AUTH",' + JSON.stringify(evt) + "]");
14423
+ } catch (err) {
14424
+ console.warn("subscribe auth function failed:", err);
14425
+ }
14426
+ });
14427
+ return this.authPromise;
14428
+ }
14429
+ async publish(event) {
14430
+ this.idleSince = void 0;
14431
+ this.ongoingOperations++;
14432
+ const ret = new Promise((resolve, reject) => {
14433
+ const timeout = setTimeout(() => {
14434
+ const ep = this.openEventPublishes.get(event.id);
14435
+ if (ep) {
14436
+ ep.reject(new Error("publish timed out"));
14437
+ this.openEventPublishes.delete(event.id);
14438
+ }
14439
+ }, this.publishTimeout);
14440
+ this.openEventPublishes.set(event.id, { resolve, reject, timeout });
14441
+ });
14442
+ this.send('["EVENT",' + JSON.stringify(event) + "]");
14443
+ this.ongoingOperations--;
14444
+ if (this.ongoingOperations === 0)
14445
+ this.idleSince = Date.now();
14446
+ return ret;
14447
+ }
14448
+ async count(filters, params) {
14449
+ this.serial++;
14450
+ const id = params?.id || "count:" + this.serial;
14451
+ const ret = new Promise((resolve, reject) => {
14452
+ this.openCountRequests.set(id, { resolve, reject });
14453
+ });
14454
+ this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1));
14455
+ return ret;
14456
+ }
14457
+ subscribe(filters, params) {
14458
+ if (params.label !== "<forced-ping>") {
14459
+ this.idleSince = void 0;
14460
+ this.ongoingOperations++;
14461
+ }
14462
+ const sub = this.prepareSubscription(filters, params);
14463
+ sub.fire();
14464
+ if (params.abort) {
14465
+ params.abort.onabort = () => sub.close(String(params.abort.reason || "<aborted>"));
14466
+ }
14467
+ return sub;
14468
+ }
14469
+ prepareSubscription(filters, params) {
14470
+ this.serial++;
14471
+ const id = params.id || (params.label ? params.label + ":" : "sub:") + this.serial;
14472
+ const sub = new Subscription(this, id, filters, params);
14473
+ this.openSubs.set(id, sub);
14474
+ return sub;
14475
+ }
14476
+ close() {
14477
+ this.skipReconnection = true;
14478
+ if (this.reconnectTimeoutHandle) {
14479
+ clearTimeout(this.reconnectTimeoutHandle);
14480
+ this.reconnectTimeoutHandle = void 0;
14481
+ }
14482
+ if (this.pingIntervalHandle) {
14483
+ clearInterval(this.pingIntervalHandle);
14484
+ this.pingIntervalHandle = void 0;
14485
+ }
14486
+ this.closeAllSubscriptions("relay connection closed by us");
14487
+ this._connected = false;
14488
+ this.idleSince = void 0;
14489
+ this.onclose?.();
14490
+ if (this.ws?.readyState === this._WebSocket.OPEN) {
14491
+ this.ws?.close();
14492
+ }
14493
+ }
14494
+ _onmessage(ev) {
14495
+ const json = ev.data;
14496
+ if (!json) {
14497
+ return;
14498
+ }
14499
+ const subid = getSubscriptionId2(json);
14500
+ if (subid) {
14501
+ const so = this.openSubs.get(subid);
14502
+ if (!so) {
14503
+ return;
14504
+ }
14505
+ const id = getHex642(json, "id");
14506
+ const alreadyHave = so.alreadyHaveEvent?.(id);
14507
+ so.receivedEvent?.(this, id);
14508
+ if (alreadyHave) {
14509
+ return;
14510
+ }
14511
+ }
14512
+ try {
14513
+ let data = JSON.parse(json);
14514
+ switch (data[0]) {
14515
+ case "EVENT": {
14516
+ const so = this.openSubs.get(data[1]);
14517
+ const event = data[2];
14518
+ if (this.verifyEvent(event) && matchFilters(so.filters, event)) {
14519
+ so.onevent(event);
14520
+ } else {
14521
+ so.oninvalidevent?.(event);
14522
+ }
14523
+ if (!so.lastEmitted || so.lastEmitted < event.created_at)
14524
+ so.lastEmitted = event.created_at;
14525
+ return;
14526
+ }
14527
+ case "COUNT": {
14528
+ const id = data[1];
14529
+ const payload = data[2];
14530
+ const cr = this.openCountRequests.get(id);
14531
+ if (cr) {
14532
+ cr.resolve(payload.count);
14533
+ this.openCountRequests.delete(id);
14534
+ }
14535
+ return;
14536
+ }
14537
+ case "EOSE": {
14538
+ const so = this.openSubs.get(data[1]);
14539
+ if (!so)
14540
+ return;
14541
+ so.receivedEose();
14542
+ return;
14543
+ }
14544
+ case "OK": {
14545
+ const id = data[1];
14546
+ const ok = data[2];
14547
+ const reason = data[3];
14548
+ const ep = this.openEventPublishes.get(id);
14549
+ if (ep) {
14550
+ clearTimeout(ep.timeout);
14551
+ if (ok)
14552
+ ep.resolve(reason);
14553
+ else
14554
+ ep.reject(new Error(reason));
14555
+ this.openEventPublishes.delete(id);
14556
+ }
14557
+ return;
14558
+ }
14559
+ case "CLOSED": {
14560
+ const id = data[1];
14561
+ const so = this.openSubs.get(id);
14562
+ if (!so)
14563
+ return;
14564
+ so.closed = true;
14565
+ so.close(data[2]);
14566
+ return;
14567
+ }
14568
+ case "NOTICE": {
14569
+ this.onnotice(data[1]);
14570
+ return;
14571
+ }
14572
+ case "AUTH": {
14573
+ this.challenge = data[1];
14574
+ if (this.onauth) {
14575
+ this.auth(this.onauth);
14576
+ }
14577
+ return;
14578
+ }
14579
+ default: {
14580
+ const so = this.openSubs.get(data[1]);
14581
+ so?.oncustom?.(data);
14582
+ return;
14583
+ }
14584
+ }
14585
+ } catch (err) {
14586
+ try {
14587
+ const [_, __, event] = JSON.parse(json);
14588
+ console.warn(`[nostr] relay ${this.url} error processing message:`, err, event);
14589
+ } catch (_) {
14590
+ console.warn(`[nostr] relay ${this.url} error processing message:`, err);
14591
+ }
14592
+ return;
14593
+ }
14594
+ }
14595
+ };
14596
+ var Subscription = class {
14597
+ relay;
14598
+ id;
14599
+ lastEmitted;
14600
+ closed = false;
14601
+ eosed = false;
14602
+ filters;
14603
+ alreadyHaveEvent;
14604
+ receivedEvent;
14605
+ onevent;
14606
+ oninvalidevent;
14607
+ oneose;
14608
+ onclose;
14609
+ oncustom;
14610
+ eoseTimeout;
14611
+ eoseTimeoutHandle;
14612
+ constructor(relay, id, filters, params) {
14613
+ if (filters.length === 0)
14614
+ throw new Error("subscription can't be created with zero filters");
14615
+ this.relay = relay;
14616
+ this.filters = filters;
14617
+ this.id = id;
14618
+ this.alreadyHaveEvent = params.alreadyHaveEvent;
14619
+ this.receivedEvent = params.receivedEvent;
14620
+ this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout;
14621
+ this.oneose = params.oneose;
14622
+ this.onclose = params.onclose;
14623
+ this.oninvalidevent = params.oninvalidevent;
14624
+ this.onevent = params.onevent || ((event) => {
14625
+ console.warn(
14626
+ `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,
14627
+ event
14628
+ );
14629
+ });
14630
+ }
14631
+ fire() {
14632
+ this.relay.send('["REQ","' + this.id + '",' + JSON.stringify(this.filters).substring(1));
14633
+ this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout);
14634
+ }
14635
+ receivedEose() {
14636
+ if (this.eosed)
14637
+ return;
14638
+ clearTimeout(this.eoseTimeoutHandle);
14639
+ this.eosed = true;
14640
+ this.oneose?.();
14641
+ }
14642
+ close(reason = "closed by caller") {
14643
+ if (!this.closed && this.relay.connected) {
14644
+ try {
14645
+ this.relay.send('["CLOSE",' + JSON.stringify(this.id) + "]");
14646
+ } catch (err) {
14647
+ if (err instanceof SendingOnClosedConnection) {
14648
+ } else {
14649
+ throw err;
14650
+ }
14651
+ }
14652
+ this.closed = true;
14653
+ }
14654
+ this.relay.openSubs.delete(this.id);
14655
+ this.relay.ongoingOperations--;
14656
+ if (this.relay.ongoingOperations === 0)
14657
+ this.relay.idleSince = Date.now();
14658
+ this.onclose?.(reason);
14659
+ }
14660
+ };
14661
+ var alwaysTrue = (t) => {
14662
+ t[verifiedSymbol3] = true;
14663
+ return true;
14664
+ };
14665
+ var AbstractSimplePool = class {
14666
+ relays = /* @__PURE__ */ new Map();
14667
+ seenOn = /* @__PURE__ */ new Map();
14668
+ trackRelays = false;
14669
+ verifyEvent;
14670
+ enablePing;
14671
+ enableReconnect;
14672
+ automaticallyAuth;
14673
+ trustedRelayURLs = /* @__PURE__ */ new Set();
14674
+ onRelayConnectionFailure;
14675
+ onRelayConnectionSuccess;
14676
+ allowConnectingToRelay;
14677
+ maxWaitForConnection;
14678
+ _WebSocket;
14679
+ constructor(opts) {
14680
+ this.verifyEvent = opts.verifyEvent;
14681
+ this._WebSocket = opts.websocketImplementation;
14682
+ this.enablePing = opts.enablePing;
14683
+ this.enableReconnect = opts.enableReconnect || false;
14684
+ this.automaticallyAuth = opts.automaticallyAuth;
14685
+ this.onRelayConnectionFailure = opts.onRelayConnectionFailure;
14686
+ this.onRelayConnectionSuccess = opts.onRelayConnectionSuccess;
14687
+ this.allowConnectingToRelay = opts.allowConnectingToRelay;
14688
+ this.maxWaitForConnection = opts.maxWaitForConnection || 3e3;
14689
+ }
14690
+ async ensureRelay(url, params) {
14691
+ url = normalizeURL2(url);
14692
+ let relay = this.relays.get(url);
14693
+ if (!relay) {
14694
+ relay = new AbstractRelay(url, {
14695
+ verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,
14696
+ websocketImplementation: this._WebSocket,
14697
+ enablePing: this.enablePing,
14698
+ enableReconnect: this.enableReconnect
14699
+ });
14700
+ relay.onclose = () => {
14701
+ this.relays.delete(url);
14702
+ };
14703
+ this.relays.set(url, relay);
14704
+ }
14705
+ if (this.automaticallyAuth) {
14706
+ const authSignerFn = this.automaticallyAuth(url);
14707
+ if (authSignerFn) {
14708
+ relay.onauth = authSignerFn;
14709
+ }
14710
+ }
14711
+ try {
14712
+ await relay.connect({
14713
+ timeout: params?.connectionTimeout,
14714
+ abort: params?.abort
14715
+ });
14716
+ } catch (err) {
14717
+ this.relays.delete(url);
14718
+ throw err;
14719
+ }
14720
+ return relay;
14721
+ }
14722
+ close(relays) {
14723
+ relays.map(normalizeURL2).forEach((url) => {
14724
+ this.relays.get(url)?.close();
14725
+ this.relays.delete(url);
14726
+ });
14727
+ }
14728
+ subscribe(relays, filter, params) {
14729
+ const request = [];
14730
+ const uniqUrls = [];
14731
+ for (let i22 = 0; i22 < relays.length; i22++) {
14732
+ const url = normalizeURL2(relays[i22]);
14733
+ if (!request.find((r) => r.url === url)) {
14734
+ if (uniqUrls.indexOf(url) === -1) {
14735
+ uniqUrls.push(url);
14736
+ request.push({ url, filter });
14737
+ }
14738
+ }
14739
+ }
14740
+ return this.subscribeMap(request, params);
14741
+ }
14742
+ subscribeMany(relays, filter, params) {
14743
+ return this.subscribe(relays, filter, params);
14744
+ }
14745
+ subscribeMap(requests, params) {
14746
+ const grouped = /* @__PURE__ */ new Map();
14747
+ for (const req of requests) {
14748
+ const { url, filter } = req;
14749
+ if (!grouped.has(url))
14750
+ grouped.set(url, []);
14751
+ grouped.get(url).push(filter);
14752
+ }
14753
+ const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }));
14754
+ if (this.trackRelays) {
14755
+ params.receivedEvent = (relay, id) => {
14756
+ let set = this.seenOn.get(id);
14757
+ if (!set) {
14758
+ set = /* @__PURE__ */ new Set();
14759
+ this.seenOn.set(id, set);
14760
+ }
14761
+ set.add(relay);
14762
+ };
14763
+ }
14764
+ const _knownIds = /* @__PURE__ */ new Set();
14765
+ const subs = [];
14766
+ const eosesReceived = [];
14767
+ let handleEose = (i22) => {
14768
+ if (eosesReceived[i22])
14769
+ return;
14770
+ eosesReceived[i22] = true;
14771
+ if (eosesReceived.filter((a) => a).length === groupedRequests.length) {
14772
+ params.oneose?.();
14773
+ handleEose = () => {
14774
+ };
14775
+ }
14776
+ };
14777
+ const closesReceived = [];
14778
+ let handleClose = (i22, reason) => {
14779
+ if (closesReceived[i22])
14780
+ return;
14781
+ handleEose(i22);
14782
+ closesReceived[i22] = reason;
14783
+ if (closesReceived.filter((a) => a).length === groupedRequests.length) {
14784
+ params.onclose?.(closesReceived);
14785
+ handleClose = () => {
14786
+ };
14787
+ }
14788
+ };
14789
+ const localAlreadyHaveEventHandler = (id) => {
14790
+ if (params.alreadyHaveEvent?.(id)) {
14791
+ return true;
14792
+ }
14793
+ const have = _knownIds.has(id);
14794
+ _knownIds.add(id);
14795
+ return have;
14796
+ };
14797
+ const allOpened = Promise.all(
14798
+ groupedRequests.map(async ({ url, filters }, i22) => {
14799
+ if (this.allowConnectingToRelay?.(url, ["read", filters]) === false) {
14800
+ handleClose(i22, "connection skipped by allowConnectingToRelay");
14801
+ return;
14802
+ }
14803
+ let relay;
14804
+ try {
14805
+ relay = await this.ensureRelay(url, {
14806
+ connectionTimeout: this.maxWaitForConnection < (params.maxWait || 0) ? Math.max(params.maxWait * 0.8, params.maxWait - 1e3) : this.maxWaitForConnection,
14807
+ abort: params.abort
14808
+ });
14809
+ } catch (err) {
14810
+ this.onRelayConnectionFailure?.(url);
14811
+ handleClose(i22, err?.message || String(err));
14812
+ return;
14813
+ }
14814
+ this.onRelayConnectionSuccess?.(url);
14815
+ let subscription = relay.subscribe(filters, {
14816
+ ...params,
14817
+ oneose: () => handleEose(i22),
14818
+ onclose: (reason) => {
14819
+ if (reason.startsWith("auth-required: ") && params.onauth) {
14820
+ relay.auth(params.onauth).then(() => {
14821
+ relay.subscribe(filters, {
14822
+ ...params,
14823
+ oneose: () => handleEose(i22),
14824
+ onclose: (reason2) => {
14825
+ handleClose(i22, reason2);
14826
+ },
14827
+ alreadyHaveEvent: localAlreadyHaveEventHandler,
14828
+ eoseTimeout: params.maxWait,
14829
+ abort: params.abort
14830
+ });
14831
+ }).catch((err) => {
14832
+ handleClose(i22, `auth was required and attempted, but failed with: ${err}`);
14833
+ });
14834
+ } else {
14835
+ handleClose(i22, reason);
14836
+ }
14837
+ },
14838
+ alreadyHaveEvent: localAlreadyHaveEventHandler,
14839
+ eoseTimeout: params.maxWait,
14840
+ abort: params.abort
14841
+ });
14842
+ subs.push(subscription);
14843
+ })
14844
+ );
14845
+ return {
14846
+ async close(reason) {
14847
+ await allOpened;
14848
+ subs.forEach((sub) => {
14849
+ sub.close(reason);
14850
+ });
14851
+ }
14852
+ };
14853
+ }
14854
+ subscribeEose(relays, filter, params) {
14855
+ let subcloser;
14856
+ subcloser = this.subscribe(relays, filter, {
14857
+ ...params,
14858
+ oneose() {
14859
+ const reason = "closed automatically on eose";
14860
+ if (subcloser)
14861
+ subcloser.close(reason);
14862
+ else
14863
+ params.onclose?.(relays.map((_) => reason));
14864
+ }
14865
+ });
14866
+ return subcloser;
14867
+ }
14868
+ subscribeManyEose(relays, filter, params) {
14869
+ return this.subscribeEose(relays, filter, params);
14870
+ }
14871
+ async querySync(relays, filter, params) {
14872
+ return new Promise(async (resolve) => {
14873
+ const events = [];
14874
+ this.subscribeEose(relays, filter, {
14875
+ ...params,
14876
+ onevent(event) {
14877
+ events.push(event);
14878
+ },
14879
+ onclose(_) {
14880
+ resolve(events);
14881
+ }
14882
+ });
14883
+ });
14884
+ }
14885
+ async get(relays, filter, params) {
14886
+ filter.limit = 1;
14887
+ const events = await this.querySync(relays, filter, params);
14888
+ events.sort((a, b) => b.created_at - a.created_at);
14889
+ return events[0] || null;
14890
+ }
14891
+ publish(relays, event, params) {
14892
+ return relays.map(normalizeURL2).map(async (url, i22, arr) => {
14893
+ if (arr.indexOf(url) !== i22) {
14894
+ return Promise.reject("duplicate url");
14895
+ }
14896
+ if (this.allowConnectingToRelay?.(url, ["write", event]) === false) {
14897
+ return Promise.reject("connection skipped by allowConnectingToRelay");
14898
+ }
14899
+ let r;
14900
+ try {
14901
+ r = await this.ensureRelay(url, {
14902
+ connectionTimeout: this.maxWaitForConnection < (params?.maxWait || 0) ? Math.max(params.maxWait * 0.8, params.maxWait - 1e3) : this.maxWaitForConnection,
14903
+ abort: params?.abort
14904
+ });
14905
+ } catch (err) {
14906
+ this.onRelayConnectionFailure?.(url);
14907
+ return String("connection failure: " + String(err));
14908
+ }
14909
+ return r.publish(event).catch(async (err) => {
14910
+ if (err instanceof Error && err.message.startsWith("auth-required: ") && params?.onauth) {
14911
+ await r.auth(params.onauth);
14912
+ return r.publish(event);
14913
+ }
14914
+ throw err;
14915
+ }).then((reason) => {
14916
+ if (this.trackRelays) {
14917
+ let set = this.seenOn.get(event.id);
14918
+ if (!set) {
14919
+ set = /* @__PURE__ */ new Set();
14920
+ this.seenOn.set(event.id, set);
14921
+ }
14922
+ set.add(r);
14923
+ }
14924
+ return reason;
14925
+ });
14926
+ });
14927
+ }
14928
+ listConnectionStatus() {
14929
+ const map = /* @__PURE__ */ new Map();
14930
+ this.relays.forEach((relay, url) => map.set(url, relay.connected));
14931
+ return map;
14932
+ }
14933
+ destroy() {
14934
+ this.relays.forEach((conn) => conn.close());
14935
+ this.relays = /* @__PURE__ */ new Map();
14936
+ }
14937
+ pruneIdleRelays(idleThresholdMs = 1e4) {
14938
+ const prunedUrls = [];
14939
+ for (const [url, relay] of this.relays) {
14940
+ if (relay.idleSince && Date.now() - relay.idleSince >= idleThresholdMs) {
14941
+ this.relays.delete(url);
14942
+ prunedUrls.push(url);
14943
+ relay.close();
14944
+ }
14945
+ }
14946
+ return prunedUrls;
14947
+ }
14948
+ };
14949
+ var _WebSocket3;
14950
+ try {
14951
+ _WebSocket3 = WebSocket;
14952
+ } catch {
14953
+ }
14954
+ var SimplePool = class extends AbstractSimplePool {
14955
+ constructor(options) {
14956
+ super({ verifyEvent: verifyEvent3, websocketImplementation: _WebSocket3, maxWaitForConnection: 3e3, ...options });
14957
+ }
14958
+ };
14959
+
14960
+ // ../nip/dist/chunk-2J3CSGKL.js
14961
+ function computeAggregateHash(entries) {
14962
+ const lines = entries.map((entry) => `${entry.sha256} ${entry.path}
14963
+ `);
14964
+ lines.sort();
14965
+ return bytesToHex2(sha256(new TextEncoder().encode(lines.join(""))));
14966
+ }
14967
+ function pathEntriesFromTags(tags) {
14968
+ const entries = [];
14969
+ for (const tag of tags) {
14970
+ if (tag[0] === "path" && typeof tag[1] === "string" && typeof tag[2] === "string" && tag[1].length > 0 && tag[2].length > 0) {
14971
+ entries.push({ path: tag[1], sha256: tag[2] });
14972
+ }
14973
+ }
14974
+ return entries;
14975
+ }
14976
+ function aggregateTagValue(tags) {
14977
+ for (const tag of tags) {
14978
+ if (tag[0] === "x" && tag[2] === "aggregate" && typeof tag[1] === "string" && tag[1].length > 0) {
14979
+ return tag[1];
14980
+ }
14981
+ }
14982
+ return void 0;
14983
+ }
14984
+
14985
+ // ../nip/dist/chunk-CDBUCFLP.js
14986
+ var MIB = 1024 * 1024;
14987
+ var NAPPLET_ARTIFACT_CACHE_NAME = "kehto:napplet-artifacts:v1";
14988
+ var SCHEMA_VERSION = 1;
14989
+ var DEFAULT_BASE_URL = "https://kehto.invalid";
14990
+ var DEFAULT_UNKNOWN_BUDGET_BYTES = 32 * MIB;
14991
+ var DEFAULT_SOFT_CEILING_BYTES = 128 * MIB;
14992
+ var DEFAULT_HARD_CEILING_BYTES = 256 * MIB;
14993
+ var DEFAULT_FOREGROUND_TTL_MS = 15 * 60 * 1e3;
14994
+ var DEFAULT_BACKGROUND_TTL_MS = 60 * 60 * 1e3;
14995
+ function emptyIndex(now2) {
14996
+ return {
14997
+ schemaVersion: SCHEMA_VERSION,
14998
+ updatedAt: now2,
14999
+ blobs: {},
15000
+ aggregates: {},
15001
+ coordinates: {}
15002
+ };
15003
+ }
15004
+ function aggregateKey(dTag, aggregateHash) {
15005
+ return `${encodeURIComponent(dTag)}:${aggregateHash}`;
15006
+ }
15007
+ function coordinateKey(kind, pubkey, dTag) {
15008
+ return `${kind}:${pubkey}:${encodeURIComponent(dTag)}`;
15009
+ }
15010
+ function isCoordinateFresh(coordinate, options = {}) {
15011
+ const now2 = options.now ?? Date.now();
15012
+ const ttl = options.background ? coordinate.backgroundTtlMs : coordinate.foregroundTtlMs;
15013
+ return ttl === Number.POSITIVE_INFINITY || now2 - coordinate.lastResolvedAt <= ttl;
15014
+ }
15015
+ function byteLength2(bytes) {
15016
+ return bytes.byteLength;
15017
+ }
15018
+ function encodeKeyPart(value) {
15019
+ return encodeURIComponent(value).replaceAll("%2F", "%252F");
15020
+ }
15021
+ function cloneBytes(bytes) {
15022
+ return new Uint8Array(bytes);
15023
+ }
15024
+ function responseBody(bytes) {
15025
+ return cloneBytes(bytes).buffer;
15026
+ }
15027
+ function normalizeBaseUrl(baseUrl) {
15028
+ return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
15029
+ }
15030
+ function resolveBaseUrl(baseUrl) {
15031
+ if (baseUrl) return normalizeBaseUrl(baseUrl);
15032
+ const maybeLocation = globalThis;
15033
+ if (maybeLocation.location?.origin) return normalizeBaseUrl(maybeLocation.location.origin);
15034
+ return normalizeBaseUrl(DEFAULT_BASE_URL);
15035
+ }
15036
+ async function responseBytes(response) {
15037
+ return new Uint8Array(await response.arrayBuffer());
15038
+ }
15039
+ async function responseJson(response) {
15040
+ return response.json();
15041
+ }
15042
+ function refCounts(index) {
15043
+ const counts = {};
15044
+ for (const aggregate of Object.values(index.aggregates)) {
15045
+ for (const hash of aggregate.blobHashes) counts[hash] = (counts[hash] ?? 0) + 1;
15046
+ }
15047
+ return counts;
15048
+ }
15049
+ function cachedSize(index) {
15050
+ const blobBytes = Object.values(index.blobs).reduce((sum, entry) => sum + entry.size, 0);
15051
+ const aggregateBytes = Object.values(index.aggregates).reduce((sum, entry) => sum + entry.size, 0);
15052
+ return blobBytes + aggregateBytes;
15053
+ }
15054
+ var CacheStorageNappletArtifactCache = class {
15055
+ cacheStorage;
15056
+ storage;
15057
+ cacheName;
15058
+ baseUrl;
15059
+ now;
15060
+ activeAggregates = /* @__PURE__ */ new Set();
15061
+ onDiagnostic;
15062
+ unknownBudgetBytes;
15063
+ softCeilingBytes;
15064
+ hardCeilingBytes;
15065
+ cachePromise;
15066
+ constructor(options = {}) {
15067
+ const maybeCacheStorage = options.cacheStorage ?? globalThis.caches;
15068
+ if (!maybeCacheStorage) {
15069
+ throw new Error("Cache Storage is unavailable");
15070
+ }
15071
+ this.cacheStorage = maybeCacheStorage;
15072
+ this.storage = options.storage ?? globalThis.navigator?.storage;
15073
+ this.cacheName = options.cacheName ?? NAPPLET_ARTIFACT_CACHE_NAME;
15074
+ this.baseUrl = resolveBaseUrl(options.baseUrl);
15075
+ this.now = options.now ?? Date.now;
15076
+ this.onDiagnostic = options.onDiagnostic;
15077
+ this.unknownBudgetBytes = options.unknownBudgetBytes ?? DEFAULT_UNKNOWN_BUDGET_BYTES;
15078
+ this.softCeilingBytes = options.softCeilingBytes ?? DEFAULT_SOFT_CEILING_BYTES;
15079
+ this.hardCeilingBytes = options.hardCeilingBytes ?? DEFAULT_HARD_CEILING_BYTES;
15080
+ }
15081
+ async readBlob(sha256Hex) {
15082
+ try {
15083
+ const cache2 = await this.open();
15084
+ const response = await cache2.match(this.blobUrl(sha256Hex));
15085
+ if (!response) return void 0;
15086
+ const bytes = await responseBytes(response);
15087
+ const index = await this.readIndex(cache2);
15088
+ const entry = index.blobs[sha256Hex];
15089
+ if (entry) {
15090
+ entry.lastAccessed = this.now();
15091
+ index.updatedAt = entry.lastAccessed;
15092
+ await this.writeIndex(cache2, index);
15093
+ }
15094
+ return bytes;
15095
+ } catch (cause) {
15096
+ this.diagnostic({ type: "cache-read-failed", message: `failed to read blob ${sha256Hex}`, cause });
15097
+ return void 0;
15098
+ }
15099
+ }
15100
+ async deleteBlob(sha256Hex) {
15101
+ try {
15102
+ const cache2 = await this.open();
15103
+ await cache2.delete(this.blobUrl(sha256Hex));
15104
+ const index = await this.readIndex(cache2);
15105
+ delete index.blobs[sha256Hex];
15106
+ index.updatedAt = this.now();
15107
+ await this.writeIndex(cache2, index);
15108
+ } catch (cause) {
15109
+ this.diagnostic({ type: "cache-delete-failed", message: `failed to delete blob ${sha256Hex}`, cause });
15110
+ }
15111
+ }
15112
+ async writeVerifiedResolution(input) {
15113
+ try {
15114
+ await this.writeVerifiedResolutionOnce(input);
15115
+ } catch (firstCause) {
15116
+ try {
15117
+ await this.prune(input.now ?? this.now());
15118
+ await this.writeVerifiedResolutionOnce(input);
15119
+ } catch (cause) {
15120
+ this.diagnostic({
15121
+ type: "cache-write-failed",
15122
+ message: "failed to write verified napplet artifacts after prune retry",
15123
+ cause: cause ?? firstCause
15124
+ });
15125
+ }
15126
+ }
15127
+ }
15128
+ async writeCoordinate(input) {
15129
+ const cache2 = await this.open();
15130
+ const now2 = this.now();
15131
+ const index = await this.readIndex(cache2);
15132
+ const key = coordinateKey(input.kind, input.pubkey, input.dTag);
15133
+ index.coordinates[key] = {
15134
+ key,
15135
+ kind: input.kind,
15136
+ pubkey: input.pubkey,
15137
+ dTag: input.dTag,
15138
+ aggregateHash: input.aggregateHash,
15139
+ lastResolvedAt: input.lastResolvedAt ?? now2,
15140
+ foregroundTtlMs: input.foregroundTtlMs ?? DEFAULT_FOREGROUND_TTL_MS,
15141
+ backgroundTtlMs: input.backgroundTtlMs ?? DEFAULT_BACKGROUND_TTL_MS
15142
+ };
15143
+ index.updatedAt = now2;
15144
+ await this.writeIndex(cache2, index);
15145
+ }
15146
+ async getCoordinate(input, options = {}) {
15147
+ const cache2 = await this.open();
15148
+ const index = await this.readIndex(cache2);
15149
+ const coordinate = index.coordinates[coordinateKey(input.kind, input.pubkey, input.dTag)];
15150
+ if (!coordinate) return void 0;
15151
+ return { ...coordinate, fresh: isCoordinateFresh(coordinate, options) };
15152
+ }
15153
+ async touchAggregate(dTag, aggregateHash, now2 = this.now()) {
15154
+ const cache2 = await this.open();
15155
+ const index = await this.readIndex(cache2);
15156
+ const key = aggregateKey(dTag, aggregateHash);
15157
+ const aggregate = index.aggregates[key];
15158
+ if (!aggregate) return;
15159
+ aggregate.lastAccessed = now2;
15160
+ for (const hash of aggregate.blobHashes) {
15161
+ if (index.blobs[hash]) index.blobs[hash].lastAccessed = now2;
15162
+ }
15163
+ index.updatedAt = now2;
15164
+ await this.writeIndex(cache2, index);
15165
+ }
15166
+ markAggregateActive(dTag, aggregateHash) {
15167
+ this.activeAggregates.add(aggregateKey(dTag, aggregateHash));
15168
+ }
15169
+ releaseAggregateActive(dTag, aggregateHash) {
15170
+ this.activeAggregates.delete(aggregateKey(dTag, aggregateHash));
15171
+ }
15172
+ async prune(now2 = this.now()) {
15173
+ try {
15174
+ const cache2 = await this.open();
15175
+ const index = await this.readIndex(cache2);
15176
+ let size = cachedSize(index);
15177
+ const budget = await this.budgetBytes();
15178
+ const hardBudget = await this.hardBudgetBytes();
15179
+ const shouldPrune = size > budget || await this.originPressureHigh();
15180
+ if (!shouldPrune) return;
15181
+ this.rewriteRefCounts(index);
15182
+ for (const [hash, blob] of Object.entries(index.blobs)) {
15183
+ if (size <= budget) break;
15184
+ if (blob.refCount > 0) continue;
15185
+ await cache2.delete(this.blobUrl(hash));
15186
+ delete index.blobs[hash];
15187
+ size -= blob.size;
15188
+ }
15189
+ const currentAggregateHashes = new Set(
15190
+ Object.values(index.coordinates).map((coordinate) => coordinate.aggregateHash)
15191
+ );
15192
+ const aggregates = Object.values(index.aggregates).filter((entry) => !this.activeAggregates.has(entry.key)).sort((a, b) => {
15193
+ const currentA = currentAggregateHashes.has(a.aggregateHash) ? 1 : 0;
15194
+ const currentB = currentAggregateHashes.has(b.aggregateHash) ? 1 : 0;
15195
+ if (currentA !== currentB) return currentA - currentB;
15196
+ return a.lastAccessed - b.lastAccessed;
15197
+ });
15198
+ for (const aggregate of aggregates) {
15199
+ if (size <= budget) break;
15200
+ await cache2.delete(this.aggregateUrl(aggregate.key));
15201
+ delete index.aggregates[aggregate.key];
15202
+ size -= aggregate.size;
15203
+ this.rewriteRefCounts(index);
15204
+ }
15205
+ if (size > hardBudget) {
15206
+ const active = Object.values(index.aggregates).filter((entry) => this.activeAggregates.has(entry.key)).sort((a, b) => a.lastAccessed - b.lastAccessed);
15207
+ for (const aggregate of active) {
15208
+ if (size <= hardBudget) break;
15209
+ await cache2.delete(this.aggregateUrl(aggregate.key));
15210
+ delete index.aggregates[aggregate.key];
15211
+ size -= aggregate.size;
15212
+ }
15213
+ }
15214
+ this.rewriteRefCounts(index);
15215
+ for (const [hash, blob] of Object.entries(index.blobs)) {
15216
+ if (blob.refCount === 0) {
15217
+ await cache2.delete(this.blobUrl(hash));
15218
+ delete index.blobs[hash];
15219
+ }
15220
+ }
15221
+ index.updatedAt = now2;
15222
+ await this.writeIndex(cache2, index);
15223
+ } catch (cause) {
15224
+ this.diagnostic({ type: "cache-prune-failed", message: "failed to prune napplet artifact cache", cause });
15225
+ }
15226
+ }
15227
+ async snapshotIndex() {
15228
+ return this.readIndex(await this.open());
15229
+ }
15230
+ async writeVerifiedResolutionOnce(input) {
15231
+ const cache2 = await this.open();
15232
+ const now2 = input.now ?? this.now();
15233
+ const index = await this.readIndex(cache2);
15234
+ const blobHashes = [];
15235
+ for (const pathEntry of input.manifest.paths) {
15236
+ const bytes = input.files.get(pathEntry.path);
15237
+ if (!bytes) continue;
15238
+ blobHashes.push(pathEntry.sha256);
15239
+ await cache2.put(this.blobUrl(pathEntry.sha256), new Response(responseBody(bytes)));
15240
+ index.blobs[pathEntry.sha256] = {
15241
+ size: byteLength2(bytes),
15242
+ lastAccessed: now2,
15243
+ refCount: index.blobs[pathEntry.sha256]?.refCount ?? 0
15244
+ };
15245
+ }
15246
+ const key = aggregateKey(input.manifest.dTag, input.manifest.aggregateHash);
15247
+ const record = {
15248
+ schemaVersion: SCHEMA_VERSION,
15249
+ dTag: input.manifest.dTag,
15250
+ aggregateHash: input.manifest.aggregateHash,
15251
+ manifest: {
15252
+ kind: input.manifest.kind,
15253
+ pubkey: input.manifest.pubkey,
15254
+ dTag: input.manifest.dTag,
15255
+ paths: input.manifest.paths.map((entry) => ({ path: entry.path, sha256: entry.sha256 })),
15256
+ requires: [...input.manifest.requires],
15257
+ ...input.manifest.title === void 0 ? {} : { title: input.manifest.title },
15258
+ ...input.manifest.description === void 0 ? {} : { description: input.manifest.description }
15259
+ },
15260
+ indexHtml: input.indexHtml,
15261
+ blobHashes,
15262
+ writtenAt: now2
15263
+ };
15264
+ const aggregateBytes = new TextEncoder().encode(JSON.stringify(record));
15265
+ await cache2.put(this.aggregateUrl(key), new Response(responseBody(aggregateBytes), {
15266
+ headers: { "content-type": "application/json" }
15267
+ }));
15268
+ const coordKey = coordinateKey(input.manifest.kind, input.manifest.pubkey, input.manifest.dTag);
15269
+ index.aggregates[key] = {
15270
+ dTag: input.manifest.dTag,
15271
+ aggregateHash: input.manifest.aggregateHash,
15272
+ key,
15273
+ blobHashes,
15274
+ size: byteLength2(aggregateBytes),
15275
+ lastAccessed: now2,
15276
+ coordinateKey: coordKey
15277
+ };
15278
+ index.coordinates[coordKey] = {
15279
+ key: coordKey,
15280
+ kind: input.manifest.kind,
15281
+ pubkey: input.manifest.pubkey,
15282
+ dTag: input.manifest.dTag,
15283
+ aggregateHash: input.manifest.aggregateHash,
15284
+ lastResolvedAt: now2,
15285
+ foregroundTtlMs: DEFAULT_FOREGROUND_TTL_MS,
15286
+ backgroundTtlMs: DEFAULT_BACKGROUND_TTL_MS
15287
+ };
15288
+ this.rewriteRefCounts(index);
15289
+ index.updatedAt = now2;
15290
+ await this.writeIndex(cache2, index);
15291
+ await this.prune(now2);
15292
+ }
15293
+ async open() {
15294
+ this.cachePromise ??= this.cacheStorage.open(this.cacheName);
15295
+ return this.cachePromise;
15296
+ }
15297
+ indexUrl() {
15298
+ return new URL("__kehto_napplet_cache/index.json", this.baseUrl).toString();
15299
+ }
15300
+ blobUrl(hash) {
15301
+ return new URL(`__kehto_napplet_cache/blob/${encodeKeyPart(hash)}`, this.baseUrl).toString();
15302
+ }
15303
+ aggregateUrl(key) {
15304
+ return new URL(`__kehto_napplet_cache/aggregate/${encodeKeyPart(key)}.json`, this.baseUrl).toString();
15305
+ }
15306
+ async readIndex(cache2) {
15307
+ const response = await cache2.match(this.indexUrl());
15308
+ if (!response) return emptyIndex(this.now());
15309
+ try {
15310
+ const index = await responseJson(response);
15311
+ if (index.schemaVersion !== SCHEMA_VERSION) return emptyIndex(this.now());
15312
+ return index;
15313
+ } catch {
15314
+ return emptyIndex(this.now());
15315
+ }
15316
+ }
15317
+ async writeIndex(cache2, index) {
15318
+ await cache2.put(this.indexUrl(), new Response(JSON.stringify(index), {
15319
+ headers: { "content-type": "application/json" }
15320
+ }));
15321
+ }
15322
+ rewriteRefCounts(index) {
15323
+ const counts = refCounts(index);
15324
+ for (const [hash, blob] of Object.entries(index.blobs)) {
15325
+ blob.refCount = counts[hash] ?? 0;
15326
+ }
15327
+ }
15328
+ async budgetBytes() {
15329
+ const estimate = await this.estimateStorage();
15330
+ if (!estimate?.quota) return this.unknownBudgetBytes;
15331
+ return Math.min(this.softCeilingBytes, Math.floor(estimate.quota * 0.1));
15332
+ }
15333
+ async hardBudgetBytes() {
15334
+ const estimate = await this.estimateStorage();
15335
+ if (!estimate?.quota) return this.unknownBudgetBytes;
15336
+ return Math.min(this.hardCeilingBytes, Math.floor(estimate.quota * 0.2));
15337
+ }
15338
+ async originPressureHigh() {
15339
+ const estimate = await this.estimateStorage();
15340
+ if (!estimate?.quota || estimate.usage === void 0) return false;
15341
+ return estimate.usage / estimate.quota >= 0.8;
15342
+ }
15343
+ async estimateStorage() {
15344
+ if (!this.storage?.estimate) return void 0;
15345
+ try {
15346
+ return this.storage.estimate();
15347
+ } catch (cause) {
15348
+ this.diagnostic({
15349
+ type: "storage-estimate-unavailable",
15350
+ message: "storage estimate failed; using unknown quota cache budget",
15351
+ cause
15352
+ });
15353
+ return void 0;
15354
+ }
15355
+ }
15356
+ diagnostic(diagnostic) {
15357
+ this.onDiagnostic?.(diagnostic);
15358
+ }
15359
+ };
15360
+ async function openNappletArtifactCache(options = {}) {
15361
+ const cacheStorage = options.cacheStorage ?? globalThis.caches;
15362
+ if (!cacheStorage) return void 0;
15363
+ const storage = options.storage ?? globalThis.navigator?.storage;
15364
+ if (options.requireStorageEstimate && !storage?.estimate) {
15365
+ options.onDiagnostic?.({
15366
+ type: "storage-estimate-unavailable",
15367
+ message: "storage estimates are unavailable; using network-only loading"
15368
+ });
15369
+ return void 0;
15370
+ }
15371
+ try {
15372
+ const cache2 = new CacheStorageNappletArtifactCache({ ...options, cacheStorage, storage });
15373
+ await cache2.snapshotIndex();
15374
+ return cache2;
15375
+ } catch (cause) {
15376
+ options.onDiagnostic?.({
15377
+ type: "cache-open-failed",
15378
+ message: "Cache Storage napplet artifact cache could not be opened",
15379
+ cause
15380
+ });
15381
+ return void 0;
15382
+ }
15383
+ }
15384
+ var NAPPLET_KIND_SNAPSHOT = 5129;
15385
+ var NAPPLET_KIND_ROOT = 15129;
15386
+ var NAPPLET_KIND_NAMED = 35129;
15387
+ var NAPPLET_KINDS = [
15388
+ NAPPLET_KIND_SNAPSHOT,
15389
+ NAPPLET_KIND_ROOT,
15390
+ NAPPLET_KIND_NAMED
15391
+ ];
15392
+ function isNappletManifestKind(kind) {
15393
+ return NAPPLET_KINDS.includes(kind);
15394
+ }
15395
+ var NappletResolutionError = class extends Error {
15396
+ code;
15397
+ constructor(code, message) {
15398
+ super(message);
15399
+ this.name = "NappletResolutionError";
15400
+ this.code = code;
15401
+ }
15402
+ };
15403
+ function firstTagValue(tags, name) {
15404
+ for (const tag of tags) {
15405
+ if (tag[0] === name && typeof tag[1] === "string" && tag[1].length > 0) return tag[1];
15406
+ }
15407
+ return void 0;
15408
+ }
15409
+ function allTagValues(tags, name) {
15410
+ const out = [];
15411
+ for (const tag of tags) {
15412
+ if (tag[0] === name && typeof tag[1] === "string" && tag[1].length > 0) out.push(tag[1]);
15413
+ }
15414
+ return out;
15415
+ }
15416
+ function archetypesFromTags(tags) {
15417
+ const out = [];
15418
+ for (const tag of tags) {
15419
+ if (tag[0] !== "archetype") continue;
15420
+ if (typeof tag[1] !== "string" || tag[1].length === 0) continue;
15421
+ out.push(
15422
+ typeof tag[2] === "string" && tag[2].length > 0 ? { slug: tag[1], nap: tag[2] } : { slug: tag[1] }
15423
+ );
15424
+ }
15425
+ return out;
15426
+ }
15427
+ function parseNappletManifest(event) {
15428
+ if (!isNappletManifestKind(event.kind)) {
15429
+ throw new NappletResolutionError("invalid-manifest", `not a NIP-5D napplet kind: ${event.kind}`);
15430
+ }
15431
+ const paths = pathEntriesFromTags(event.tags);
15432
+ if (paths.length === 0) {
15433
+ throw new NappletResolutionError("invalid-manifest", "manifest has no path tags");
15434
+ }
15435
+ const aggregateHash = aggregateTagValue(event.tags);
15436
+ if (!aggregateHash) {
15437
+ throw new NappletResolutionError("invalid-manifest", "manifest has no aggregate x tag");
15438
+ }
15439
+ return {
15440
+ kind: event.kind,
15441
+ pubkey: event.pubkey,
15442
+ dTag: firstTagValue(event.tags, "d") ?? "",
15443
+ paths,
15444
+ aggregateHash,
15445
+ servers: allTagValues(event.tags, "server"),
15446
+ requires: allTagValues(event.tags, "requires"),
15447
+ archetypes: archetypesFromTags(event.tags),
15448
+ title: firstTagValue(event.tags, "title"),
15449
+ description: firstTagValue(event.tags, "description"),
15450
+ source: firstTagValue(event.tags, "source")
15451
+ };
15452
+ }
15453
+ function verifyManifestSignature(event) {
15454
+ try {
15455
+ return verifyEvent(event);
15456
+ } catch {
15457
+ return false;
15458
+ }
15459
+ }
15460
+ function verifyBlobHash(bytes, sha256Hex) {
15461
+ return bytesToHex2(sha256(bytes)) === sha256Hex;
15462
+ }
15463
+ function stripTrailingSlash(url) {
15464
+ return url.endsWith("/") ? url.slice(0, -1) : url;
15465
+ }
15466
+ async function fetchBlob(servers, sha256Hex, fetchBytes) {
15467
+ for (const server of servers) {
15468
+ const url = `${stripTrailingSlash(server)}/${sha256Hex}`;
15469
+ try {
15470
+ const bytes = await fetchBytes(url);
15471
+ if (verifyBlobHash(bytes, sha256Hex)) return bytes;
15472
+ } catch {
15473
+ }
15474
+ }
15475
+ throw new NappletResolutionError("blob-unavailable", `no server served blob ${sha256Hex}`);
15476
+ }
15477
+ var INDEX_PATHS = ["/index.html", "index.html", "/"];
15478
+ function defaultDecode(bytes) {
15479
+ return new TextDecoder().decode(bytes);
15480
+ }
15481
+ async function resolveNapplet(options) {
15482
+ const { event, fetchBlob: fetchBlobBytes, textDecode = defaultDecode, cache: cache2 } = options;
15483
+ if (!verifyManifestSignature(event)) {
15484
+ throw new NappletResolutionError("invalid-signature", "manifest signature is invalid");
15485
+ }
15486
+ const manifest = parseNappletManifest(event);
15487
+ const recomputed = computeAggregateHash(manifest.paths);
15488
+ if (recomputed !== manifest.aggregateHash) {
15489
+ throw new NappletResolutionError(
15490
+ "aggregate-mismatch",
15491
+ `recomputed aggregate ${recomputed} != manifest ${manifest.aggregateHash}`
15492
+ );
15493
+ }
15494
+ const files = /* @__PURE__ */ new Map();
15495
+ for (const entry of manifest.paths) {
15496
+ let bytes = await cache2?.readBlob(entry.sha256);
15497
+ if (bytes && !verifyBlobHash(bytes, entry.sha256)) {
15498
+ await cache2?.deleteBlob(entry.sha256);
15499
+ bytes = void 0;
15500
+ }
15501
+ bytes ??= await fetchBlobBytes(entry.sha256, manifest.servers);
15502
+ if (!verifyBlobHash(bytes, entry.sha256)) {
15503
+ throw new NappletResolutionError(
15504
+ "blob-hash-mismatch",
15505
+ `blob for ${entry.path} does not match hash ${entry.sha256}`
15506
+ );
15507
+ }
15508
+ files.set(entry.path, bytes);
15509
+ }
15510
+ const indexEntry = manifest.paths.find((e) => INDEX_PATHS.includes(e.path));
15511
+ if (!indexEntry) {
15512
+ throw new NappletResolutionError("missing-index", "manifest has no /index.html entry");
15513
+ }
15514
+ const indexHtml = textDecode(files.get(indexEntry.path));
15515
+ await cache2?.writeVerifiedResolution({ event, manifest, files, indexHtml });
15516
+ return {
15517
+ dTag: manifest.dTag,
15518
+ aggregateHash: manifest.aggregateHash,
15519
+ files,
15520
+ indexHtml,
15521
+ manifest
15522
+ };
15523
+ }
15524
+
15525
+ // src/runtime-resolver.ts
15526
+ var PAJA_NAPPLET_MANIFEST_KIND = 35129;
15527
+ function decodePajaPointer(value) {
15528
+ const trimmed = value.trim();
15529
+ const decoded = decode(trimmed);
15530
+ if (decoded.type === "naddr") {
15531
+ return {
15532
+ type: "naddr",
15533
+ value: trimmed,
15534
+ identifier: decoded.data.identifier,
15535
+ pubkey: decoded.data.pubkey,
15536
+ kind: decoded.data.kind,
15537
+ relays: [...decoded.data.relays ?? []]
15538
+ };
15539
+ }
15540
+ if (decoded.type === "nevent") {
15541
+ return {
15542
+ type: "nevent",
15543
+ value: trimmed,
15544
+ id: decoded.data.id,
15545
+ ...decoded.data.author ? { author: decoded.data.author } : {},
15546
+ ...decoded.data.kind ? { kind: decoded.data.kind } : {},
15547
+ relays: [...decoded.data.relays ?? []]
15548
+ };
15549
+ }
15550
+ throw new Error(`Expected naddr or nevent pointer, got ${decoded.type}.`);
15551
+ }
15552
+ async function resolvePajaPointer(value, options = {}) {
15553
+ const pointer = decodePajaPointer(value);
15554
+ const relays = uniqueRelayUrls([...pointer.relays, ...options.relays ?? []]);
15555
+ if (relays.length === 0) {
15556
+ throw new Error("Pointer does not include relay hints; add a relay-bearing naddr or nevent.");
15557
+ }
15558
+ const pool = options.pool ?? new SimplePool();
15559
+ const ownsPool = options.pool === void 0;
15560
+ try {
15561
+ const event = await resolvePointerEvent(pointer, pool, relays, options.maxWaitMs ?? 5e3);
15562
+ const blossomServers = [...options.blossomServers ?? []];
15563
+ const resolved = await resolveNapplet({
15564
+ event,
15565
+ cache: await openNappletArtifactCache({ requireStorageEstimate: false }),
15566
+ fetchBlob: (sha256Hex, servers) => fetchBlob([...servers, ...blossomServers], sha256Hex, async (url) => {
15567
+ const response = await (options.fetcher ?? fetch)(url);
15568
+ if (!response.ok) throw new Error(`[paja-runtime] blob ${url}: ${response.status}`);
15569
+ return new Uint8Array(await response.arrayBuffer());
15570
+ })
15571
+ });
15572
+ return {
15573
+ pointer,
15574
+ event,
15575
+ relays,
15576
+ blossomServers,
15577
+ dTag: resolved.dTag,
15578
+ aggregateHash: resolved.aggregateHash,
15579
+ indexHtml: resolved.indexHtml,
15580
+ manifest: resolved.manifest
15581
+ };
15582
+ } finally {
15583
+ if (ownsPool) {
15584
+ pool.close?.(relays);
15585
+ pool.destroy?.();
15586
+ }
15587
+ }
15588
+ }
15589
+ function injectPajaRuntimeCsp(html, origins) {
15590
+ const value = origins.length > 0 ? `connect-src ${[...new Set(origins)].sort().join(" ")}` : "connect-src 'none'";
15591
+ const meta = `<meta http-equiv="Content-Security-Policy" content="${escapeAttribute(value)}">`;
15592
+ if (/<head[^>]*>/i.test(html)) {
15593
+ return html.replace(/<head[^>]*>/i, (open) => `${open}${meta}`);
15594
+ }
15595
+ if (/<html[^>]*>/i.test(html)) {
15596
+ return html.replace(/<html[^>]*>/i, (open) => `${open}<head>${meta}</head>`);
15597
+ }
15598
+ return `${meta}${html}`;
15599
+ }
15600
+ async function resolvePointerEvent(pointer, pool, relays, maxWaitMs) {
15601
+ const filter = pointer.type === "naddr" ? addressableFilter(pointer) : eventFilter(pointer);
15602
+ const events = await pool.querySync([...relays], filter, {
15603
+ maxWait: maxWaitMs,
15604
+ label: "kehto-paja-runtime"
15605
+ });
15606
+ const event = events.filter((candidate) => matchesPointer(pointer, candidate)).sort((a, b) => b.created_at - a.created_at)[0];
15607
+ if (!event) {
15608
+ throw new Error(`No napplet manifest event found for ${pointer.type} pointer.`);
15609
+ }
15610
+ if (event.kind !== PAJA_NAPPLET_MANIFEST_KIND) {
15611
+ throw new Error(`Pointer resolved kind ${event.kind}; expected ${PAJA_NAPPLET_MANIFEST_KIND}.`);
15612
+ }
15613
+ return event;
15614
+ }
15615
+ function addressableFilter(pointer) {
15616
+ if (pointer.kind !== PAJA_NAPPLET_MANIFEST_KIND) {
15617
+ throw new Error(`naddr kind ${pointer.kind} is not a napplet manifest kind.`);
15618
+ }
15619
+ return {
15620
+ kinds: [PAJA_NAPPLET_MANIFEST_KIND],
15621
+ authors: [pointer.pubkey],
15622
+ "#d": [pointer.identifier],
15623
+ limit: 1
15624
+ };
15625
+ }
15626
+ function eventFilter(pointer) {
15627
+ return {
15628
+ ids: [pointer.id],
15629
+ ...pointer.kind ? { kinds: [pointer.kind] } : {},
15630
+ ...pointer.author ? { authors: [pointer.author] } : {},
15631
+ limit: 1
15632
+ };
15633
+ }
15634
+ function matchesPointer(pointer, event) {
15635
+ if (pointer.type === "nevent") {
15636
+ if (event.id !== pointer.id) return false;
15637
+ if (pointer.author && event.pubkey !== pointer.author) return false;
15638
+ if (pointer.kind && event.kind !== pointer.kind) return false;
15639
+ return true;
15640
+ }
15641
+ return event.kind === pointer.kind && event.pubkey === pointer.pubkey && event.tags.some((tag) => tag[0] === "d" && tag[1] === pointer.identifier);
15642
+ }
15643
+ function uniqueRelayUrls(values) {
15644
+ const out = [];
15645
+ const seen = /* @__PURE__ */ new Set();
15646
+ for (const value of values) {
15647
+ const url = normalizeRelayUrl(value);
15648
+ if (!url || seen.has(url)) continue;
15649
+ seen.add(url);
15650
+ out.push(url);
15651
+ }
15652
+ return out;
15653
+ }
15654
+ function normalizeRelayUrl(value) {
15655
+ const raw = value.trim();
15656
+ if (!raw) return void 0;
15657
+ try {
15658
+ const url = new URL(raw);
15659
+ if (url.protocol !== "wss:" && url.protocol !== "ws:") return void 0;
15660
+ url.hash = "";
15661
+ url.search = "";
15662
+ return url.href.replace(/\/$/, "");
15663
+ } catch {
15664
+ return void 0;
15665
+ }
15666
+ }
15667
+ function escapeAttribute(value) {
15668
+ return value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
15669
+ }
15670
+
15671
+ // src/browser-host.ts
15672
+ var bridgeRef = null;
15673
+ function readConfig() {
15674
+ const script = document.getElementById("kehto-paja-config");
15675
+ if (!script?.textContent) {
15676
+ throw new Error("Missing Kehto Paja config.");
15677
+ }
15678
+ return JSON.parse(script.textContent);
15679
+ }
15680
+ async function readLatestConfig(fallback) {
15681
+ try {
15682
+ const response = await fetch(new URL("./__kehto/config.json", window.location.href), { cache: "no-store" });
15683
+ if (!response.ok) return fallback;
15684
+ return await response.json();
15685
+ } catch (error) {
15686
+ console.warn("[paja] config refresh failed; using embedded config", error);
15687
+ return fallback;
15688
+ }
15689
+ }
15690
+ function setTargetUrlDisplay(config, frame) {
15691
+ const label = getTargetLabel(config);
15692
+ const targetEl = document.querySelector(".target");
15693
+ if (targetEl) {
15694
+ targetEl.textContent = label;
15695
+ targetEl.setAttribute("title", label);
15696
+ }
15697
+ frame.dataset.targetUrl = label;
15698
+ }
15699
+ function getTargetLabel(config) {
15700
+ if (config.target.mode === "runtime-pointer") return config.target.pointer?.value ?? "runtime pointer";
15701
+ return config.target.url;
15702
+ }
15703
+ function readInitialPointerValue(config) {
15704
+ if (config.target.mode !== "runtime-pointer") return "";
15705
+ const params = new URLSearchParams(window.location.search);
15706
+ return params.get("naddr") ?? params.get("nevent") ?? params.get("pointer") ?? config.target.pointer?.value ?? "";
15707
+ }
15708
+ function connectOrigins(urls) {
15709
+ const out = /* @__PURE__ */ new Set();
15710
+ for (const value of urls) {
15711
+ try {
15712
+ const url = new URL(value);
15713
+ if (url.protocol === "wss:" || url.protocol === "ws:") {
15714
+ out.add(value.replace(/\/$/, ""));
15715
+ } else {
15716
+ out.add(url.origin);
15717
+ }
15718
+ } catch {
15719
+ }
15720
+ }
15721
+ return [...out];
15722
+ }
15723
+ function setStatus(state, status) {
15724
+ state.status = status;
15725
+ const statusEl = document.getElementById("lifecycle-status");
15726
+ if (statusEl) statusEl.textContent = status;
15727
+ }
15728
+ function setSimulationStatus(state) {
15729
+ const statusEl = document.getElementById("simulation-status");
15730
+ if (statusEl) statusEl.textContent = summarizePajaSimulation(state.simulation);
15731
+ const themeSelect = document.getElementById("simulation-theme");
15732
+ if (themeSelect instanceof HTMLSelectElement) themeSelect.value = state.simulation.theme.mode;
15733
+ renderPajaDevtools(state, { bridge: bridgeRef, devSignerPubkey: PAJA_DEV_SIGNER_PUBKEY });
15734
+ }
15735
+ function setPointerStatus(state, message) {
15736
+ state.pointerStatus = message;
15737
+ const statusEl = document.getElementById("runtime-pointer-status");
15738
+ if (statusEl) statusEl.textContent = message;
15739
+ }
15740
+ function getFrame() {
15741
+ const frame = document.getElementById("napplet-frame");
15742
+ if (!(frame instanceof HTMLIFrameElement)) {
15743
+ throw new Error("Missing Kehto Paja iframe.");
15744
+ }
15745
+ frame.sandbox.add("allow-scripts");
15746
+ frame.sandbox.remove("allow-same-origin");
15747
+ return frame;
15748
+ }
15749
+ function confirmPajaRequest(state, request) {
15750
+ const event = request.event;
15751
+ const kind = typeof event.kind === "number" ? event.kind : "unknown";
15752
+ const content = typeof event.content === "string" && event.content.length > 0 ? `
15753
+
15754
+ ${event.content.slice(0, 240)}` : "";
15755
+ const allowed = window.confirm(`Paja ${request.action} request
15756
+ kind: ${kind}${content}`);
15757
+ appendPajaMessageLog(state, "paja", {
15758
+ type: `paja.${request.action}.${allowed ? "confirmed" : "denied"}`,
15759
+ kind
15760
+ });
15761
+ return allowed;
15762
+ }
15763
+ function getTargetIdentity2(config, resolvedTarget) {
15764
+ return {
15765
+ pubkey: "",
15766
+ dTag: resolvedTarget?.dTag ?? config.window.dTag,
15767
+ aggregateHash: resolvedTarget?.aggregateHash ?? config.window.aggregateHash
15768
+ };
15769
+ }
15770
+ function registerFrameForGeneration(bridge, frame, config, generation, resolvedTarget) {
15771
+ const win = frame.contentWindow;
15772
+ if (!win) return null;
15773
+ const windowId = `${config.window.id}:${generation}`;
15774
+ const identity = getTargetIdentity2(config, resolvedTarget);
15775
+ originRegistry.register(win, windowId, {
15776
+ dTag: identity.dTag,
15777
+ aggregateHash: identity.aggregateHash
15778
+ });
15779
+ bridge.runtime.sessionRegistry.register(windowId, {
15780
+ ...identity,
15781
+ windowId,
15782
+ origin: "null",
15783
+ type: "napplet",
15784
+ registeredAt: Date.now(),
15785
+ instanceId: `${windowId}:${Date.now()}`,
15786
+ provenance: "nip-5d"
15787
+ });
15788
+ return windowId;
15789
+ }
15790
+ function navigateFrame(bridge, frame, config, generation, resolvedTarget) {
15791
+ if (config.target.mode === "runtime-pointer") {
15792
+ if (!resolvedTarget) {
15793
+ frame.removeAttribute("src");
15794
+ frame.srcdoc = "<!doctype html><html><body></body></html>";
15795
+ return null;
15796
+ }
15797
+ const windowId2 = registerFrameForGeneration(bridge, frame, config, generation, resolvedTarget);
15798
+ frame.removeAttribute("src");
15799
+ frame.srcdoc = injectPajaRuntimeCsp(
15800
+ resolvedTarget.indexHtml,
15801
+ connectOrigins([...resolvedTarget.relays, ...resolvedTarget.blossomServers])
15802
+ );
15803
+ return windowId2;
15804
+ }
15805
+ const windowId = registerFrameForGeneration(bridge, frame, config, generation, resolvedTarget);
15806
+ frame.src = config.target.url;
15807
+ return windowId;
15808
+ }
15809
+ function createHostSignerController(getState) {
15810
+ return createPajaSignerController({
15811
+ confirmRequest: (request) => confirmPajaRequest(getState(), request),
15812
+ onChange(signer) {
15813
+ const state = getState();
15814
+ if (!state) return;
15815
+ state.signer = signer;
15816
+ appendPajaMessageLog(state, "paja", {
15817
+ type: `paja.signer.${signer.method}.${signer.status}`,
15818
+ pubkey: signer.pubkey,
15819
+ relay: signer.relay,
15820
+ error: signer.error
15821
+ });
15822
+ setSimulationStatus(state);
15823
+ if (signer.status === "connected") state.reload();
15824
+ }
15825
+ });
15826
+ }
13962
15827
  function installPajaControlListeners(state) {
13963
15828
  document.getElementById("reload-target")?.addEventListener("click", () => {
13964
15829
  state.reload();
@@ -13979,80 +15844,72 @@ function installPajaControlListeners(state) {
13979
15844
  document.getElementById("clear-log")?.addEventListener("click", () => {
13980
15845
  state.clearLog();
13981
15846
  });
15847
+ document.getElementById("runtime-pointer-form")?.addEventListener("submit", (event) => {
15848
+ event.preventDefault();
15849
+ const input = document.getElementById("runtime-pointer-input");
15850
+ if (!(input instanceof HTMLInputElement)) return;
15851
+ void state.loadPointer(input.value);
15852
+ });
13982
15853
  }
13983
- async function installPajaHost() {
13984
- const config = await readLatestConfig(readConfig());
13985
- const frame = getFrame();
13986
- setTargetUrlDisplay(config, frame);
13987
- let currentSimulation = config.simulation;
13988
- const getSimulation = () => currentSimulation;
13989
- let themeService = null;
13990
- let stateRef = null;
13991
- const signerController = createHostSignerController(() => stateRef);
13992
- const adapter = createPajaAdapter(config, getSimulation, (theme) => {
13993
- themeService = theme;
13994
- }, (request) => confirmPajaRequest(stateRef, request), signerController);
13995
- const bridge = createShellBridge(adapter);
13996
- bridgeRef = bridge;
13997
- installPajaOriginRegistryProxy(originRegistry, () => stateRef);
13998
- const capabilities = buildShellCapabilities(adapter);
13999
- const services = Object.keys(adapter.services ?? {}).sort();
14000
- let currentWindowId = null;
14001
- let initReceivedGeneration = -1;
14002
- const state = {
15854
+ function createPajaBrowserState(context) {
15855
+ const { config, frame, bridge, adapter, signerController, capabilities, runtime } = context;
15856
+ return {
14003
15857
  config,
14004
15858
  capabilities,
14005
- services,
14006
- simulation: currentSimulation,
15859
+ services: Object.keys(adapter.services ?? {}).sort(),
15860
+ simulation: runtime.currentSimulation,
14007
15861
  signer: signerController.getState(),
15862
+ resolvedTarget: null,
15863
+ pointerValue: readInitialPointerValue(config),
15864
+ pointerStatus: config.target.mode === "runtime-pointer" ? "idle" : "",
14008
15865
  generation: 0,
14009
15866
  status: "booting",
14010
15867
  messageFilter: "",
14011
15868
  messageLog: [],
14012
15869
  reload() {
14013
- if (currentWindowId) {
14014
- bridge.runtime.destroyWindow(currentWindowId);
14015
- bridge.runtime.sessionRegistry.unregister(currentWindowId);
14016
- originRegistry.unregister(currentWindowId);
15870
+ if (runtime.currentWindowId) {
15871
+ bridge.runtime.destroyWindow(runtime.currentWindowId);
15872
+ bridge.runtime.sessionRegistry.unregister(runtime.currentWindowId);
15873
+ originRegistry.unregister(runtime.currentWindowId);
14017
15874
  }
14018
15875
  this.generation += 1;
14019
- initReceivedGeneration = -1;
15876
+ runtime.initReceivedGeneration = -1;
14020
15877
  setStatus(this, "reloading");
14021
- currentWindowId = navigateFrame(bridge, frame, config, this.generation);
15878
+ runtime.currentWindowId = navigateFrame(bridge, frame, config, this.generation, this.resolvedTarget);
14022
15879
  },
14023
15880
  setThemeMode(mode) {
14024
- currentSimulation = {
14025
- ...currentSimulation,
15881
+ runtime.currentSimulation = {
15882
+ ...runtime.currentSimulation,
14026
15883
  theme: {
14027
- ...currentSimulation.theme,
15884
+ ...runtime.currentSimulation.theme,
14028
15885
  mode
14029
15886
  }
14030
15887
  };
14031
- this.simulation = currentSimulation;
14032
- themeService?.publishTheme(createDevTheme(currentSimulation.theme.mode, currentSimulation.theme.values));
15888
+ this.simulation = runtime.currentSimulation;
15889
+ runtime.themeService?.publishTheme(createDevTheme(runtime.currentSimulation.theme.mode, runtime.currentSimulation.theme.values));
14033
15890
  setSimulationStatus(this);
14034
15891
  },
14035
15892
  setDomainEnabled(domain, enabled) {
14036
- currentSimulation = {
14037
- ...currentSimulation,
15893
+ runtime.currentSimulation = {
15894
+ ...runtime.currentSimulation,
14038
15895
  capabilities: {
14039
15896
  domains: {
14040
- ...currentSimulation.capabilities.domains,
15897
+ ...runtime.currentSimulation.capabilities.domains,
14041
15898
  [domain]: enabled
14042
15899
  },
14043
15900
  disabledDomains: PAJA_SIMULATION_DOMAINS.filter(
14044
- (entry) => entry === domain ? !enabled : !currentSimulation.capabilities.domains[entry]
15901
+ (entry) => entry === domain ? !enabled : !runtime.currentSimulation.capabilities.domains[entry]
14045
15902
  )
14046
15903
  }
14047
15904
  };
14048
- this.simulation = currentSimulation;
14049
- this.services = Object.keys(adapter.services ?? {}).filter((name) => currentSimulation.capabilities.domains[name] !== false).sort();
15905
+ this.simulation = runtime.currentSimulation;
15906
+ this.services = Object.keys(adapter.services ?? {}).filter((name) => runtime.currentSimulation.capabilities.domains[name] !== false).sort();
14050
15907
  setSimulationStatus(this);
14051
15908
  appendPajaMessageLog(this, "paja", { type: `paja.interface.${enabled ? "enabled" : "disabled"}`, domain });
14052
15909
  this.reload();
14053
15910
  },
14054
15911
  setAclCapability(capability, enabled) {
14055
- const identity = getTargetIdentity2(config);
15912
+ const identity = getTargetIdentity2(config, this.resolvedTarget);
14056
15913
  if (enabled) bridge.runtime.aclState.grant(identity.pubkey, identity.dTag, identity.aggregateHash, capability);
14057
15914
  else bridge.runtime.aclState.revoke(identity.pubkey, identity.dTag, identity.aggregateHash, capability);
14058
15915
  bridge.runtime.aclState.persist();
@@ -14068,6 +15925,40 @@ async function installPajaHost() {
14068
15925
  connectBunker(uri) {
14069
15926
  return signerController.connectBunker(uri);
14070
15927
  },
15928
+ async loadPointer(value) {
15929
+ if (config.target.mode !== "runtime-pointer") return;
15930
+ const pointer = value.trim();
15931
+ const input = document.getElementById("runtime-pointer-input");
15932
+ if (input instanceof HTMLInputElement) input.value = pointer;
15933
+ this.pointerValue = pointer;
15934
+ if (!pointer) {
15935
+ setPointerStatus(this, "idle");
15936
+ return;
15937
+ }
15938
+ setPointerStatus(this, "resolving");
15939
+ setStatus(this, "booting");
15940
+ appendPajaMessageLog(this, "paja", { type: "paja.pointer.resolve", pointer });
15941
+ try {
15942
+ this.resolvedTarget = await resolvePajaPointer(pointer, {
15943
+ relays: config.target.pointer?.relays ?? [],
15944
+ blossomServers: config.target.pointer?.blossomServers ?? [],
15945
+ maxWaitMs: config.target.pointer?.maxWaitMs
15946
+ });
15947
+ setPointerStatus(this, `${this.resolvedTarget.dTag}:${this.resolvedTarget.aggregateHash.slice(0, 12)}`);
15948
+ appendPajaMessageLog(this, "paja", {
15949
+ type: "paja.pointer.resolved",
15950
+ dTag: this.resolvedTarget.dTag,
15951
+ aggregateHash: this.resolvedTarget.aggregateHash
15952
+ });
15953
+ this.reload();
15954
+ } catch (error) {
15955
+ const message = error instanceof Error ? error.message : String(error);
15956
+ this.resolvedTarget = null;
15957
+ setPointerStatus(this, message);
15958
+ setStatus(this, "error");
15959
+ appendPajaMessageLog(this, "paja", { type: "paja.pointer.error", error: message });
15960
+ }
15961
+ },
14071
15962
  clearLog() {
14072
15963
  this.messageLog.length = 0;
14073
15964
  renderPajaMessageLog(this);
@@ -14077,20 +15968,52 @@ async function installPajaHost() {
14077
15968
  generation: this.generation,
14078
15969
  status: this.status,
14079
15970
  iframeCount: document.querySelectorAll("iframe").length,
14080
- initSent: initReceivedGeneration === this.generation,
15971
+ initSent: runtime.initReceivedGeneration === this.generation,
14081
15972
  services: this.services,
14082
- simulation: currentSimulation,
15973
+ simulation: runtime.currentSimulation,
14083
15974
  signer: this.signer,
15975
+ resolvedTarget: this.resolvedTarget,
15976
+ pointerStatus: this.pointerStatus,
14084
15977
  messageLog: [...this.messageLog]
14085
15978
  };
14086
15979
  }
14087
15980
  };
15981
+ }
15982
+ async function installPajaHost() {
15983
+ const config = await readLatestConfig(readConfig());
15984
+ const frame = getFrame();
15985
+ setTargetUrlDisplay(config, frame);
15986
+ const runtime = {
15987
+ currentSimulation: config.simulation,
15988
+ themeService: null,
15989
+ currentWindowId: null,
15990
+ initReceivedGeneration: -1
15991
+ };
15992
+ const getSimulation = () => runtime.currentSimulation;
15993
+ let stateRef = null;
15994
+ const signerController = createHostSignerController(() => stateRef);
15995
+ const adapter = createPajaAdapter(config, getSimulation, (theme) => {
15996
+ runtime.themeService = theme;
15997
+ }, (request) => confirmPajaRequest(stateRef, request), signerController, () => getTargetIdentity2(config, stateRef?.resolvedTarget));
15998
+ const bridge = createShellBridge(adapter);
15999
+ bridgeRef = bridge;
16000
+ installPajaOriginRegistryProxy(originRegistry, () => stateRef);
16001
+ const capabilities = buildShellCapabilities(adapter);
16002
+ const state = createPajaBrowserState({
16003
+ config,
16004
+ frame,
16005
+ bridge,
16006
+ adapter,
16007
+ signerController,
16008
+ capabilities,
16009
+ runtime
16010
+ });
14088
16011
  stateRef = state;
14089
16012
  window.__KEHTO_PAJA__ = state;
14090
16013
  window.addEventListener("message", (event) => {
14091
16014
  if (event.source !== frame.contentWindow) return;
14092
- appendPajaMessageLog(state, "napplet->shell", event.data, currentWindowId ?? void 0);
14093
- const proxiedSource = createPajaPostMessageProxy(event.source, state, currentWindowId ?? void 0);
16015
+ appendPajaMessageLog(state, "napplet->shell", event.data, runtime.currentWindowId ?? void 0);
16016
+ const proxiedSource = createPajaPostMessageProxy(event.source, state, runtime.currentWindowId ?? void 0);
14094
16017
  const syntheticEvent = new Proxy(event, {
14095
16018
  get(target, prop) {
14096
16019
  if (prop === "source") return proxiedSource;
@@ -14101,13 +16024,13 @@ async function installPajaHost() {
14101
16024
  bridge.handleMessage(syntheticEvent);
14102
16025
  const data = event.data;
14103
16026
  if (data && typeof data === "object" && data.type === "shell.ready") {
14104
- initReceivedGeneration = state.generation;
16027
+ runtime.initReceivedGeneration = state.generation;
14105
16028
  setStatus(state, "ready");
14106
16029
  }
14107
16030
  });
14108
16031
  frame.addEventListener("load", () => {
14109
16032
  if (state.status === "booting" || state.status === "reloading") {
14110
- registerFrameForGeneration(bridge, frame, config, state.generation);
16033
+ runtime.currentWindowId = registerFrameForGeneration(bridge, frame, config, state.generation, state.resolvedTarget);
14111
16034
  }
14112
16035
  });
14113
16036
  frame.addEventListener("error", () => {
@@ -14116,7 +16039,15 @@ async function installPajaHost() {
14116
16039
  installPajaControlListeners(state);
14117
16040
  setStatus(state, "booting");
14118
16041
  setSimulationStatus(state);
14119
- currentWindowId = navigateFrame(bridge, frame, config, state.generation);
16042
+ setPointerStatus(state, state.pointerStatus);
16043
+ if (config.target.mode === "runtime-pointer") {
16044
+ const input = document.getElementById("runtime-pointer-input");
16045
+ if (input instanceof HTMLInputElement) input.value = state.pointerValue;
16046
+ if (state.pointerValue) void state.loadPointer(state.pointerValue);
16047
+ else runtime.currentWindowId = navigateFrame(bridge, frame, config, state.generation, state.resolvedTarget);
16048
+ } else {
16049
+ runtime.currentWindowId = navigateFrame(bridge, frame, config, state.generation, state.resolvedTarget);
16050
+ }
14120
16051
  }
14121
16052
  try {
14122
16053
  void installPajaHost().catch((error) => {