@kehto/paja 0.2.0 → 0.3.2

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.
@@ -1,4 +1,4 @@
1
- // ../acl/dist/chunk-5FE7VHM4.js
1
+ // ../acl/dist/chunk-6GH7LZSY.js
2
2
  var ALL_CAPABILITIES = [
3
3
  // v1.1 kept:
4
4
  "relay:read",
@@ -224,7 +224,7 @@ function configMap(action) {
224
224
  return { senderCap: "config:read", recipientCap: null };
225
225
  }
226
226
  function resourceMap(action) {
227
- if (action === "bytes.result" || action === "bytes.error") {
227
+ if (action === "bytes.result" || action === "bytes.error" || action === "bytesMany.result" || action === "bytesMany.error") {
228
228
  return { senderCap: null, recipientCap: "resource:fetch" };
229
229
  }
230
230
  return { senderCap: "resource:fetch", recipientCap: null };
@@ -593,7 +593,7 @@ function deserialize2(json) {
593
593
  return defaultConfig();
594
594
  }
595
595
 
596
- // ../../node_modules/.pnpm/@napplet+core@0.13.0/node_modules/@napplet/core/dist/index.js
596
+ // ../../node_modules/.pnpm/@napplet+core@0.21.0/node_modules/@napplet/core/dist/index.js
597
597
  function createDispatch() {
598
598
  const handlers = /* @__PURE__ */ new Map();
599
599
  function registerNap2(domain, handler) {
@@ -1299,13 +1299,128 @@ function publishSignedEncrypted(context, windowId, id, signed, replyPe) {
1299
1299
  }
1300
1300
  }
1301
1301
  function handleRelayQuery(context, windowId, m) {
1302
+ const { hooks, serviceRegistry, eventBuffer } = context;
1302
1303
  const id = m.id ?? "";
1303
1304
  const filters = m.filters ?? [];
1304
- let count = 0;
1305
- for (const event of context.eventBuffer.getBufferedEvents()) {
1306
- if (matchesAnyFilter(event, filters)) count++;
1305
+ const seenIds = /* @__PURE__ */ new Set();
1306
+ const events = [];
1307
+ for (const event of eventBuffer.getBufferedEvents()) {
1308
+ if (matchesAnyFilter(event, filters) && !seenIds.has(event.id)) {
1309
+ seenIds.add(event.id);
1310
+ events.push(event);
1311
+ }
1312
+ }
1313
+ let settled = false;
1314
+ let fallbackTimer;
1315
+ function settle6() {
1316
+ if (settled) return;
1317
+ settled = true;
1318
+ clearTimeout(fallbackTimer);
1319
+ hooks.sendToNapplet(windowId, { type: "relay.query.result", id, events });
1320
+ }
1321
+ if (isShellKindQuery(filters)) {
1322
+ settle6();
1323
+ return;
1324
+ }
1325
+ const relayService = relayServiceFrom(context);
1326
+ const cacheService = !serviceRegistry["relay"] ? serviceRegistry["cache"] : void 0;
1327
+ if (relayService) {
1328
+ let onBackendEose2 = function() {
1329
+ openBackends--;
1330
+ if (openBackends <= 0) {
1331
+ const closeMsg = { type: "relay.close", id, subId };
1332
+ svc.handleMessage(windowId, closeMsg, () => {
1333
+ });
1334
+ if (cacheService) {
1335
+ cacheService.handleMessage(windowId, closeMsg, () => {
1336
+ });
1337
+ }
1338
+ settle6();
1339
+ }
1340
+ }, makeCollector2 = function() {
1341
+ return function collector(resp) {
1342
+ const r = resp;
1343
+ if (r.type === "relay.event" && r.event && !seenIds.has(r.event.id)) {
1344
+ seenIds.add(r.event.id);
1345
+ events.push(r.event);
1346
+ } else if (r.type === "relay.eose") {
1347
+ onBackendEose2();
1348
+ }
1349
+ };
1350
+ };
1351
+ var onBackendEose = onBackendEose2, makeCollector = makeCollector2;
1352
+ const svc = relayService;
1353
+ const subId = "__query__:" + id;
1354
+ const subscribeMsg = {
1355
+ type: "relay.subscribe",
1356
+ id,
1357
+ subId,
1358
+ filters
1359
+ };
1360
+ if (typeof m.relay === "string" && m.relay.length > 0) {
1361
+ subscribeMsg.relay = m.relay;
1362
+ }
1363
+ let openBackends = cacheService ? 2 : 1;
1364
+ fallbackTimer = setTimeout(() => {
1365
+ const closeMsg = { type: "relay.close", id, subId };
1366
+ svc.handleMessage(windowId, closeMsg, () => {
1367
+ });
1368
+ if (cacheService) {
1369
+ cacheService.handleMessage(windowId, closeMsg, () => {
1370
+ });
1371
+ }
1372
+ settle6();
1373
+ }, 15e3);
1374
+ svc.handleMessage(windowId, subscribeMsg, makeCollector2());
1375
+ if (cacheService) {
1376
+ cacheService.handleMessage(windowId, subscribeMsg, makeCollector2());
1377
+ }
1378
+ return;
1379
+ }
1380
+ const cache2 = hooks.cache;
1381
+ const pool = hooks.relayPool;
1382
+ if (cache2?.isAvailable()) {
1383
+ cache2.query(filters).then((cachedEvents) => {
1384
+ for (const event of cachedEvents) {
1385
+ if (!seenIds.has(event.id)) {
1386
+ seenIds.add(event.id);
1387
+ events.push(event);
1388
+ }
1389
+ }
1390
+ }).catch(() => {
1391
+ });
1392
+ }
1393
+ if (!pool?.isAvailable()) {
1394
+ settle6();
1395
+ return;
1307
1396
  }
1308
- context.hooks.sendToNapplet(windowId, { type: "relay.query.result", id, count });
1397
+ const relayHint = typeof m.relay === "string" && m.relay.length > 0 ? m.relay : void 0;
1398
+ const relayUrls = relayHint ? [relayHint] : pool.selectRelayTier(filters);
1399
+ let poolSubscription;
1400
+ fallbackTimer = setTimeout(() => {
1401
+ poolSubscription?.unsubscribe();
1402
+ settle6();
1403
+ }, 15e3);
1404
+ poolSubscription = pool.subscribe(filters, (item) => {
1405
+ if (item === "EOSE") {
1406
+ clearTimeout(fallbackTimer);
1407
+ poolSubscription?.unsubscribe();
1408
+ settle6();
1409
+ return;
1410
+ }
1411
+ const event = item;
1412
+ if (!seenIds.has(event.id)) {
1413
+ seenIds.add(event.id);
1414
+ events.push(event);
1415
+ }
1416
+ if (cache2?.isAvailable()) {
1417
+ try {
1418
+ cache2.store(event);
1419
+ } catch {
1420
+ return;
1421
+ }
1422
+ }
1423
+ }, relayUrls);
1309
1424
  }
1310
1425
  function createIdentityHandler(context) {
1311
1426
  return function handleIdentityMessage(windowId, msg) {
@@ -1704,7 +1819,13 @@ function createRuntimeDomainHandlers(context) {
1704
1819
  cvm: (windowId, msg) => handleServiceOnlyMessage(context, "cvm", windowId, msg),
1705
1820
  outbox: (windowId, msg) => handleServiceOnlyMessage(context, "outbox", windowId, msg),
1706
1821
  upload: (windowId, msg) => handleServiceOnlyMessage(context, "upload", windowId, msg),
1707
- intent: (windowId, msg) => handleServiceOnlyMessage(context, "intent", windowId, msg)
1822
+ intent: (windowId, msg) => handleServiceOnlyMessage(context, "intent", windowId, msg),
1823
+ link: (windowId, msg) => handleServiceOnlyMessage(context, "link", windowId, msg),
1824
+ common: (windowId, msg) => handleServiceOnlyMessage(context, "common", windowId, msg),
1825
+ lists: (windowId, msg) => handleServiceOnlyMessage(context, "lists", windowId, msg),
1826
+ serial: (windowId, msg) => handleServiceOnlyMessage(context, "serial", windowId, msg),
1827
+ ble: (windowId, msg) => handleServiceOnlyMessage(context, "ble", windowId, msg),
1828
+ webrtc: (windowId, msg) => handleServiceOnlyMessage(context, "webrtc", windowId, msg)
1708
1829
  };
1709
1830
  }
1710
1831
  function handleStorageMessage(context, windowId, msg) {
@@ -1845,6 +1966,12 @@ function createNapEnvelopeDispatcher(handlers) {
1845
1966
  napDispatch.registerNap("outbox", adapt(handlers.outbox));
1846
1967
  napDispatch.registerNap("upload", adapt(handlers.upload));
1847
1968
  napDispatch.registerNap("intent", adapt(handlers.intent));
1969
+ napDispatch.registerNap("link", adapt(handlers.link));
1970
+ napDispatch.registerNap("common", adapt(handlers.common));
1971
+ napDispatch.registerNap("lists", adapt(handlers.lists));
1972
+ napDispatch.registerNap("serial", adapt(handlers.serial));
1973
+ napDispatch.registerNap("ble", adapt(handlers.ble));
1974
+ napDispatch.registerNap("webrtc", adapt(handlers.webrtc));
1848
1975
  return (windowId, envelope) => {
1849
1976
  currentWindowId = windowId;
1850
1977
  try {
@@ -3082,6 +3209,12 @@ function buildShellCapabilities(hooks) {
3082
3209
  const domains = hooks.relayPool ? ["relay", "outbox", ...NAP_DOMAINS] : [...NAP_DOMAINS];
3083
3210
  if (hooks.upload) domains.push("upload");
3084
3211
  if (hooks.intent?.isAvailable()) domains.push("intent");
3212
+ if (hooks.link?.isAvailable()) domains.push("link");
3213
+ if (hooks.common?.isAvailable()) domains.push("common");
3214
+ if (hooks.lists?.isAvailable()) domains.push("lists");
3215
+ if (hooks.serial?.isAvailable()) domains.push("serial");
3216
+ if (hooks.ble?.isAvailable()) domains.push("ble");
3217
+ if (hooks.webrtc?.isAvailable()) domains.push("webrtc");
3085
3218
  const sandbox = [];
3086
3219
  domains.push(...sandbox);
3087
3220
  const protocols = {};
@@ -3092,6 +3225,12 @@ function buildShellCapabilities(hooks) {
3092
3225
  const naps = hooks.relayPool ? ["relay", "outbox", ...NAP_DOMAINS, ...NAP_INC_PROTOCOLS] : [...NAP_DOMAINS, ...NAP_INC_PROTOCOLS];
3093
3226
  if (hooks.upload) naps.push("upload");
3094
3227
  if (hooks.intent?.isAvailable()) naps.push("intent");
3228
+ if (hooks.link?.isAvailable()) naps.push("link");
3229
+ if (hooks.common?.isAvailable()) naps.push("common");
3230
+ if (hooks.lists?.isAvailable()) naps.push("lists");
3231
+ if (hooks.serial?.isAvailable()) naps.push("serial");
3232
+ if (hooks.ble?.isAvailable()) naps.push("ble");
3233
+ if (hooks.webrtc?.isAvailable()) naps.push("webrtc");
3095
3234
  return applyCapabilityOverrides(
3096
3235
  { domains, protocols, naps, sandbox },
3097
3236
  hooks.capabilities?.disabledDomains ?? []
@@ -3269,6 +3408,496 @@ function createShellBridge(hooks) {
3269
3408
  };
3270
3409
  }
3271
3410
 
3411
+ // ../../node_modules/.pnpm/@noble+hashes@2.0.1/node_modules/@noble/hashes/utils.js
3412
+ function isBytes(a) {
3413
+ return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
3414
+ }
3415
+ function abytes(value, length, title = "") {
3416
+ const bytes = isBytes(value);
3417
+ const len = value?.length;
3418
+ const needsLen = length !== void 0;
3419
+ if (!bytes || needsLen && len !== length) {
3420
+ const prefix = title && `"${title}" `;
3421
+ const ofLen = needsLen ? ` of length ${length}` : "";
3422
+ const got = bytes ? `length=${len}` : `type=${typeof value}`;
3423
+ throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got);
3424
+ }
3425
+ return value;
3426
+ }
3427
+ var hasHexBuiltin = /* @__PURE__ */ (() => (
3428
+ // @ts-ignore
3429
+ typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function"
3430
+ ))();
3431
+ var hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
3432
+ function bytesToHex2(bytes) {
3433
+ abytes(bytes);
3434
+ if (hasHexBuiltin)
3435
+ return bytes.toHex();
3436
+ let hex = "";
3437
+ for (let i = 0; i < bytes.length; i++) {
3438
+ hex += hexes[bytes[i]];
3439
+ }
3440
+ return hex;
3441
+ }
3442
+ var asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };
3443
+ function asciiToBase16(ch) {
3444
+ if (ch >= asciis._0 && ch <= asciis._9)
3445
+ return ch - asciis._0;
3446
+ if (ch >= asciis.A && ch <= asciis.F)
3447
+ return ch - (asciis.A - 10);
3448
+ if (ch >= asciis.a && ch <= asciis.f)
3449
+ return ch - (asciis.a - 10);
3450
+ return;
3451
+ }
3452
+ function hexToBytes2(hex) {
3453
+ if (typeof hex !== "string")
3454
+ throw new Error("hex string expected, got " + typeof hex);
3455
+ if (hasHexBuiltin)
3456
+ return Uint8Array.fromHex(hex);
3457
+ const hl = hex.length;
3458
+ const al = hl / 2;
3459
+ if (hl % 2)
3460
+ throw new Error("hex string expected, got unpadded hex of length " + hl);
3461
+ const array = new Uint8Array(al);
3462
+ for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
3463
+ const n1 = asciiToBase16(hex.charCodeAt(hi));
3464
+ const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
3465
+ if (n1 === void 0 || n2 === void 0) {
3466
+ const char = hex[hi] + hex[hi + 1];
3467
+ throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
3468
+ }
3469
+ array[ai] = n1 * 16 + n2;
3470
+ }
3471
+ return array;
3472
+ }
3473
+ function concatBytes(...arrays) {
3474
+ let sum = 0;
3475
+ for (let i = 0; i < arrays.length; i++) {
3476
+ const a = arrays[i];
3477
+ abytes(a);
3478
+ sum += a.length;
3479
+ }
3480
+ const res = new Uint8Array(sum);
3481
+ for (let i = 0, pad = 0; i < arrays.length; i++) {
3482
+ const a = arrays[i];
3483
+ res.set(a, pad);
3484
+ pad += a.length;
3485
+ }
3486
+ return res;
3487
+ }
3488
+
3489
+ // ../../node_modules/.pnpm/@scure+base@2.0.0/node_modules/@scure/base/index.js
3490
+ function isBytes2(a) {
3491
+ return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
3492
+ }
3493
+ function isArrayOf(isString, arr) {
3494
+ if (!Array.isArray(arr))
3495
+ return false;
3496
+ if (arr.length === 0)
3497
+ return true;
3498
+ if (isString) {
3499
+ return arr.every((item) => typeof item === "string");
3500
+ } else {
3501
+ return arr.every((item) => Number.isSafeInteger(item));
3502
+ }
3503
+ }
3504
+ function afn(input) {
3505
+ if (typeof input !== "function")
3506
+ throw new Error("function expected");
3507
+ return true;
3508
+ }
3509
+ function astr(label, input) {
3510
+ if (typeof input !== "string")
3511
+ throw new Error(`${label}: string expected`);
3512
+ return true;
3513
+ }
3514
+ function anumber(n) {
3515
+ if (!Number.isSafeInteger(n))
3516
+ throw new Error(`invalid integer: ${n}`);
3517
+ }
3518
+ function aArr(input) {
3519
+ if (!Array.isArray(input))
3520
+ throw new Error("array expected");
3521
+ }
3522
+ function astrArr(label, input) {
3523
+ if (!isArrayOf(true, input))
3524
+ throw new Error(`${label}: array of strings expected`);
3525
+ }
3526
+ function anumArr(label, input) {
3527
+ if (!isArrayOf(false, input))
3528
+ throw new Error(`${label}: array of numbers expected`);
3529
+ }
3530
+ // @__NO_SIDE_EFFECTS__
3531
+ function chain(...args) {
3532
+ const id = (a) => a;
3533
+ const wrap = (a, b) => (c) => a(b(c));
3534
+ const encode = args.map((x) => x.encode).reduceRight(wrap, id);
3535
+ const decode2 = args.map((x) => x.decode).reduce(wrap, id);
3536
+ return { encode, decode: decode2 };
3537
+ }
3538
+ // @__NO_SIDE_EFFECTS__
3539
+ function alphabet(letters) {
3540
+ const lettersA = typeof letters === "string" ? letters.split("") : letters;
3541
+ const len = lettersA.length;
3542
+ astrArr("alphabet", lettersA);
3543
+ const indexes = new Map(lettersA.map((l, i) => [l, i]));
3544
+ return {
3545
+ encode: (digits) => {
3546
+ aArr(digits);
3547
+ return digits.map((i) => {
3548
+ if (!Number.isSafeInteger(i) || i < 0 || i >= len)
3549
+ throw new Error(`alphabet.encode: digit index outside alphabet "${i}". Allowed: ${letters}`);
3550
+ return lettersA[i];
3551
+ });
3552
+ },
3553
+ decode: (input) => {
3554
+ aArr(input);
3555
+ return input.map((letter) => {
3556
+ astr("alphabet.decode", letter);
3557
+ const i = indexes.get(letter);
3558
+ if (i === void 0)
3559
+ throw new Error(`Unknown letter: "${letter}". Allowed: ${letters}`);
3560
+ return i;
3561
+ });
3562
+ }
3563
+ };
3564
+ }
3565
+ // @__NO_SIDE_EFFECTS__
3566
+ function join(separator = "") {
3567
+ astr("join", separator);
3568
+ return {
3569
+ encode: (from) => {
3570
+ astrArr("join.decode", from);
3571
+ return from.join(separator);
3572
+ },
3573
+ decode: (to) => {
3574
+ astr("join.decode", to);
3575
+ return to.split(separator);
3576
+ }
3577
+ };
3578
+ }
3579
+ var gcd = (a, b) => b === 0 ? a : gcd(b, a % b);
3580
+ var radix2carry = /* @__NO_SIDE_EFFECTS__ */ (from, to) => from + (to - gcd(from, to));
3581
+ var powers = /* @__PURE__ */ (() => {
3582
+ let res = [];
3583
+ for (let i = 0; i < 40; i++)
3584
+ res.push(2 ** i);
3585
+ return res;
3586
+ })();
3587
+ function convertRadix2(data, from, to, padding) {
3588
+ aArr(data);
3589
+ if (from <= 0 || from > 32)
3590
+ throw new Error(`convertRadix2: wrong from=${from}`);
3591
+ if (to <= 0 || to > 32)
3592
+ throw new Error(`convertRadix2: wrong to=${to}`);
3593
+ if (/* @__PURE__ */ radix2carry(from, to) > 32) {
3594
+ throw new Error(`convertRadix2: carry overflow from=${from} to=${to} carryBits=${/* @__PURE__ */ radix2carry(from, to)}`);
3595
+ }
3596
+ let carry = 0;
3597
+ let pos = 0;
3598
+ const max = powers[from];
3599
+ const mask = powers[to] - 1;
3600
+ const res = [];
3601
+ for (const n of data) {
3602
+ anumber(n);
3603
+ if (n >= max)
3604
+ throw new Error(`convertRadix2: invalid data word=${n} from=${from}`);
3605
+ carry = carry << from | n;
3606
+ if (pos + from > 32)
3607
+ throw new Error(`convertRadix2: carry overflow pos=${pos} from=${from}`);
3608
+ pos += from;
3609
+ for (; pos >= to; pos -= to)
3610
+ res.push((carry >> pos - to & mask) >>> 0);
3611
+ const pow = powers[pos];
3612
+ if (pow === void 0)
3613
+ throw new Error("invalid carry");
3614
+ carry &= pow - 1;
3615
+ }
3616
+ carry = carry << to - pos & mask;
3617
+ if (!padding && pos >= from)
3618
+ throw new Error("Excess padding");
3619
+ if (!padding && carry > 0)
3620
+ throw new Error(`Non-zero padding: ${carry}`);
3621
+ if (padding && pos > 0)
3622
+ res.push(carry >>> 0);
3623
+ return res;
3624
+ }
3625
+ // @__NO_SIDE_EFFECTS__
3626
+ function radix2(bits, revPadding = false) {
3627
+ anumber(bits);
3628
+ if (bits <= 0 || bits > 32)
3629
+ throw new Error("radix2: bits should be in (0..32]");
3630
+ if (/* @__PURE__ */ radix2carry(8, bits) > 32 || /* @__PURE__ */ radix2carry(bits, 8) > 32)
3631
+ throw new Error("radix2: carry overflow");
3632
+ return {
3633
+ encode: (bytes) => {
3634
+ if (!isBytes2(bytes))
3635
+ throw new Error("radix2.encode input should be Uint8Array");
3636
+ return convertRadix2(Array.from(bytes), 8, bits, !revPadding);
3637
+ },
3638
+ decode: (digits) => {
3639
+ anumArr("radix2.decode", digits);
3640
+ return Uint8Array.from(convertRadix2(digits, bits, 8, revPadding));
3641
+ }
3642
+ };
3643
+ }
3644
+ function unsafeWrapper(fn) {
3645
+ afn(fn);
3646
+ return function(...args) {
3647
+ try {
3648
+ return fn.apply(null, args);
3649
+ } catch (e) {
3650
+ }
3651
+ };
3652
+ }
3653
+ var BECH_ALPHABET = /* @__PURE__ */ chain(/* @__PURE__ */ alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), /* @__PURE__ */ join(""));
3654
+ var POLYMOD_GENERATORS = [996825010, 642813549, 513874426, 1027748829, 705979059];
3655
+ function bech32Polymod(pre) {
3656
+ const b = pre >> 25;
3657
+ let chk = (pre & 33554431) << 5;
3658
+ for (let i = 0; i < POLYMOD_GENERATORS.length; i++) {
3659
+ if ((b >> i & 1) === 1)
3660
+ chk ^= POLYMOD_GENERATORS[i];
3661
+ }
3662
+ return chk;
3663
+ }
3664
+ function bechChecksum(prefix, words, encodingConst = 1) {
3665
+ const len = prefix.length;
3666
+ let chk = 1;
3667
+ for (let i = 0; i < len; i++) {
3668
+ const c = prefix.charCodeAt(i);
3669
+ if (c < 33 || c > 126)
3670
+ throw new Error(`Invalid prefix (${prefix})`);
3671
+ chk = bech32Polymod(chk) ^ c >> 5;
3672
+ }
3673
+ chk = bech32Polymod(chk);
3674
+ for (let i = 0; i < len; i++)
3675
+ chk = bech32Polymod(chk) ^ prefix.charCodeAt(i) & 31;
3676
+ for (let v of words)
3677
+ chk = bech32Polymod(chk) ^ v;
3678
+ for (let i = 0; i < 6; i++)
3679
+ chk = bech32Polymod(chk);
3680
+ chk ^= encodingConst;
3681
+ return BECH_ALPHABET.encode(convertRadix2([chk % powers[30]], 30, 5, false));
3682
+ }
3683
+ // @__NO_SIDE_EFFECTS__
3684
+ function genBech32(encoding) {
3685
+ const ENCODING_CONST = encoding === "bech32" ? 1 : 734539939;
3686
+ const _words = /* @__PURE__ */ radix2(5);
3687
+ const fromWords = _words.decode;
3688
+ const toWords = _words.encode;
3689
+ const fromWordsUnsafe = unsafeWrapper(fromWords);
3690
+ function encode(prefix, words, limit = 90) {
3691
+ astr("bech32.encode prefix", prefix);
3692
+ if (isBytes2(words))
3693
+ words = Array.from(words);
3694
+ anumArr("bech32.encode", words);
3695
+ const plen = prefix.length;
3696
+ if (plen === 0)
3697
+ throw new TypeError(`Invalid prefix length ${plen}`);
3698
+ const actualLength = plen + 7 + words.length;
3699
+ if (limit !== false && actualLength > limit)
3700
+ throw new TypeError(`Length ${actualLength} exceeds limit ${limit}`);
3701
+ const lowered = prefix.toLowerCase();
3702
+ const sum = bechChecksum(lowered, words, ENCODING_CONST);
3703
+ return `${lowered}1${BECH_ALPHABET.encode(words)}${sum}`;
3704
+ }
3705
+ function decode2(str, limit = 90) {
3706
+ astr("bech32.decode input", str);
3707
+ const slen = str.length;
3708
+ if (slen < 8 || limit !== false && slen > limit)
3709
+ throw new TypeError(`invalid string length: ${slen} (${str}). Expected (8..${limit})`);
3710
+ const lowered = str.toLowerCase();
3711
+ if (str !== lowered && str !== str.toUpperCase())
3712
+ throw new Error(`String must be lowercase or uppercase`);
3713
+ const sepIndex = lowered.lastIndexOf("1");
3714
+ if (sepIndex === 0 || sepIndex === -1)
3715
+ throw new Error(`Letter "1" must be present between prefix and data only`);
3716
+ const prefix = lowered.slice(0, sepIndex);
3717
+ const data = lowered.slice(sepIndex + 1);
3718
+ if (data.length < 6)
3719
+ throw new Error("Data must be at least 6 characters long");
3720
+ const words = BECH_ALPHABET.decode(data).slice(0, -6);
3721
+ const sum = bechChecksum(prefix, words, ENCODING_CONST);
3722
+ if (!data.endsWith(sum))
3723
+ throw new Error(`Invalid checksum in ${str}: expected "${sum}"`);
3724
+ return { prefix, words };
3725
+ }
3726
+ const decodeUnsafe = unsafeWrapper(decode2);
3727
+ function decodeToBytes(str) {
3728
+ const { prefix, words } = decode2(str, false);
3729
+ return { prefix, words, bytes: fromWords(words) };
3730
+ }
3731
+ function encodeFromBytes(prefix, bytes) {
3732
+ return encode(prefix, toWords(bytes));
3733
+ }
3734
+ return {
3735
+ encode,
3736
+ decode: decode2,
3737
+ encodeFromBytes,
3738
+ decodeToBytes,
3739
+ decodeUnsafe,
3740
+ fromWords,
3741
+ fromWordsUnsafe,
3742
+ toWords
3743
+ };
3744
+ }
3745
+ var bech32 = /* @__PURE__ */ genBech32("bech32");
3746
+
3747
+ // ../../node_modules/.pnpm/nostr-tools@2.23.3_typescript@5.9.3/node_modules/nostr-tools/lib/esm/nip19.js
3748
+ var utf8Decoder = new TextDecoder("utf-8");
3749
+ var utf8Encoder = new TextEncoder();
3750
+ var Bech32MaxSize = 5e3;
3751
+ function integerToUint8Array(number) {
3752
+ const uint8Array = new Uint8Array(4);
3753
+ uint8Array[0] = number >> 24 & 255;
3754
+ uint8Array[1] = number >> 16 & 255;
3755
+ uint8Array[2] = number >> 8 & 255;
3756
+ uint8Array[3] = number & 255;
3757
+ return uint8Array;
3758
+ }
3759
+ function decode(code) {
3760
+ let { prefix, words } = bech32.decode(code, Bech32MaxSize);
3761
+ let data = new Uint8Array(bech32.fromWords(words));
3762
+ switch (prefix) {
3763
+ case "nprofile": {
3764
+ let tlv = parseTLV(data);
3765
+ if (!tlv[0]?.[0])
3766
+ throw new Error("missing TLV 0 for nprofile");
3767
+ if (tlv[0][0].length !== 32)
3768
+ throw new Error("TLV 0 should be 32 bytes");
3769
+ return {
3770
+ type: "nprofile",
3771
+ data: {
3772
+ pubkey: bytesToHex2(tlv[0][0]),
3773
+ relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
3774
+ }
3775
+ };
3776
+ }
3777
+ case "nevent": {
3778
+ let tlv = parseTLV(data);
3779
+ if (!tlv[0]?.[0])
3780
+ throw new Error("missing TLV 0 for nevent");
3781
+ if (tlv[0][0].length !== 32)
3782
+ throw new Error("TLV 0 should be 32 bytes");
3783
+ if (tlv[2] && tlv[2][0].length !== 32)
3784
+ throw new Error("TLV 2 should be 32 bytes");
3785
+ if (tlv[3] && tlv[3][0].length !== 4)
3786
+ throw new Error("TLV 3 should be 4 bytes");
3787
+ return {
3788
+ type: "nevent",
3789
+ data: {
3790
+ id: bytesToHex2(tlv[0][0]),
3791
+ relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
3792
+ author: tlv[2]?.[0] ? bytesToHex2(tlv[2][0]) : void 0,
3793
+ kind: tlv[3]?.[0] ? parseInt(bytesToHex2(tlv[3][0]), 16) : void 0
3794
+ }
3795
+ };
3796
+ }
3797
+ case "naddr": {
3798
+ let tlv = parseTLV(data);
3799
+ if (!tlv[0]?.[0])
3800
+ throw new Error("missing TLV 0 for naddr");
3801
+ if (!tlv[2]?.[0])
3802
+ throw new Error("missing TLV 2 for naddr");
3803
+ if (tlv[2][0].length !== 32)
3804
+ throw new Error("TLV 2 should be 32 bytes");
3805
+ if (!tlv[3]?.[0])
3806
+ throw new Error("missing TLV 3 for naddr");
3807
+ if (tlv[3][0].length !== 4)
3808
+ throw new Error("TLV 3 should be 4 bytes");
3809
+ return {
3810
+ type: "naddr",
3811
+ data: {
3812
+ identifier: utf8Decoder.decode(tlv[0][0]),
3813
+ pubkey: bytesToHex2(tlv[2][0]),
3814
+ kind: parseInt(bytesToHex2(tlv[3][0]), 16),
3815
+ relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
3816
+ }
3817
+ };
3818
+ }
3819
+ case "nsec":
3820
+ return { type: prefix, data };
3821
+ case "npub":
3822
+ case "note":
3823
+ return { type: prefix, data: bytesToHex2(data) };
3824
+ default:
3825
+ throw new Error(`unknown prefix ${prefix}`);
3826
+ }
3827
+ }
3828
+ function parseTLV(data) {
3829
+ let result = {};
3830
+ let rest = data;
3831
+ while (rest.length > 0) {
3832
+ let t = rest[0];
3833
+ let l = rest[1];
3834
+ let v = rest.slice(2, 2 + l);
3835
+ rest = rest.slice(2 + l);
3836
+ if (v.length < l)
3837
+ throw new Error(`not enough data to read on TLV ${t}`);
3838
+ result[t] = result[t] || [];
3839
+ result[t].push(v);
3840
+ }
3841
+ return result;
3842
+ }
3843
+ function npubEncode(hex) {
3844
+ return encodeBytes("npub", hexToBytes2(hex));
3845
+ }
3846
+ function noteEncode(hex) {
3847
+ return encodeBytes("note", hexToBytes2(hex));
3848
+ }
3849
+ function encodeBech32(prefix, data) {
3850
+ let words = bech32.toWords(data);
3851
+ return bech32.encode(prefix, words, Bech32MaxSize);
3852
+ }
3853
+ function encodeBytes(prefix, bytes) {
3854
+ return encodeBech32(prefix, bytes);
3855
+ }
3856
+ function nprofileEncode(profile) {
3857
+ let data = encodeTLV({
3858
+ 0: [hexToBytes2(profile.pubkey)],
3859
+ 1: (profile.relays || []).map((url) => utf8Encoder.encode(url))
3860
+ });
3861
+ return encodeBech32("nprofile", data);
3862
+ }
3863
+ function neventEncode(event) {
3864
+ let kindArray;
3865
+ if (event.kind !== void 0) {
3866
+ kindArray = integerToUint8Array(event.kind);
3867
+ }
3868
+ let data = encodeTLV({
3869
+ 0: [hexToBytes2(event.id)],
3870
+ 1: (event.relays || []).map((url) => utf8Encoder.encode(url)),
3871
+ 2: event.author ? [hexToBytes2(event.author)] : [],
3872
+ 3: kindArray ? [new Uint8Array(kindArray)] : []
3873
+ });
3874
+ return encodeBech32("nevent", data);
3875
+ }
3876
+ function naddrEncode(addr) {
3877
+ let kind = new ArrayBuffer(4);
3878
+ new DataView(kind).setUint32(0, addr.kind, false);
3879
+ let data = encodeTLV({
3880
+ 0: [utf8Encoder.encode(addr.identifier)],
3881
+ 1: (addr.relays || []).map((url) => utf8Encoder.encode(url)),
3882
+ 2: [hexToBytes2(addr.pubkey)],
3883
+ 3: [new Uint8Array(kind)]
3884
+ });
3885
+ return encodeBech32("naddr", data);
3886
+ }
3887
+ function encodeTLV(tlv) {
3888
+ let entries = [];
3889
+ Object.entries(tlv).reverse().forEach(([t, vs]) => {
3890
+ vs.forEach((v) => {
3891
+ let entry = new Uint8Array(v.length + 2);
3892
+ entry.set([parseInt(t)], 0);
3893
+ entry.set([v.length], 1);
3894
+ entry.set(v, 2);
3895
+ entries.push(entry);
3896
+ });
3897
+ });
3898
+ return concatBytes(...entries);
3899
+ }
3900
+
3272
3901
  // ../services/dist/index.js
3273
3902
  var NOTIFICATION_SERVICE_VERSION = "1.0.0";
3274
3903
  var DEFAULT_MAX_PER_WINDOW = 100;
@@ -4657,19 +5286,35 @@ function untrackRequest(state, requestId) {
4657
5286
  state.perWindow.get(entry.windowId)?.delete(requestId);
4658
5287
  }
4659
5288
  }
4660
- function sendResourceError(send, requestId, code, message) {
5289
+ function sendResourceError(send, requestId, code, message, error = toResourceError(code), type = "resource.bytes.error") {
4661
5290
  send({
4662
- type: "resource.bytes.error",
5291
+ type,
5292
+ id: requestId,
4663
5293
  requestId,
5294
+ error,
4664
5295
  code,
4665
5296
  message
4666
5297
  });
4667
5298
  }
4668
- function parseResourceUrl(send, requestId, url) {
5299
+ function sendBytesManyError(send, requestId, code, message, error) {
5300
+ sendResourceError(send, requestId, code, message, error, "resource.bytesMany.error");
5301
+ }
5302
+ function toResourceError(code) {
5303
+ switch (code) {
5304
+ case "denied":
5305
+ return "blocked-by-policy";
5306
+ case "invalid-url":
5307
+ return "invalid-request";
5308
+ case "canceled":
5309
+ return "timeout";
5310
+ case "network-error":
5311
+ return "network-error";
5312
+ }
5313
+ }
5314
+ function parseResourceUrl(url) {
4669
5315
  try {
4670
5316
  return new URL(url);
4671
5317
  } catch {
4672
- sendResourceError(send, requestId, "invalid-url", `invalid URL: ${url}`);
4673
5318
  return null;
4674
5319
  }
4675
5320
  }
@@ -4680,45 +5325,138 @@ function collectResponseHeaders(response) {
4680
5325
  });
4681
5326
  return headers;
4682
5327
  }
4683
- async function handleBytes(options, state, windowId, msg, send) {
4684
- const { requestId, url, init } = msg;
4685
- const identity = options.resolveIdentity(windowId);
4686
- if (!identity) {
4687
- sendResourceError(send, requestId, "denied", "napplet identity not resolvable");
4688
- return;
4689
- }
4690
- const parsedUrl = parseResourceUrl(send, requestId, url);
4691
- if (!parsedUrl) return;
5328
+ function responseMime(response) {
5329
+ return response.headers.get("content-type") || "application/octet-stream";
5330
+ }
5331
+ function responseBlob(buffer, mime) {
5332
+ return new Blob([buffer], { type: mime });
5333
+ }
5334
+ function requestIdFromMessage(message) {
5335
+ if (typeof message.id === "string" && message.id.length > 0) return message.id;
5336
+ if (typeof message.requestId === "string" && message.requestId.length > 0) return message.requestId;
5337
+ return null;
5338
+ }
5339
+ function resourceInvalidRequest(url, message) {
5340
+ return {
5341
+ ok: false,
5342
+ url,
5343
+ error: "invalid-request",
5344
+ code: "invalid-url",
5345
+ message
5346
+ };
5347
+ }
5348
+ async function fetchResourceItem(options, identity, url, init, signal) {
5349
+ const parsedUrl = parseResourceUrl(url);
5350
+ if (!parsedUrl) return resourceInvalidRequest(url, `invalid URL: ${url}`);
4692
5351
  const origin = parsedUrl.origin;
4693
5352
  const grants = options.getConnectGrants(identity.dTag, identity.aggregateHash);
4694
5353
  if (!options.isOriginGranted(origin, grants)) {
4695
- sendResourceError(send, requestId, "denied", `origin ${origin} not granted`);
4696
- return;
5354
+ return {
5355
+ ok: false,
5356
+ url,
5357
+ error: "blocked-by-policy",
5358
+ code: "denied",
5359
+ message: `origin ${origin} not granted`
5360
+ };
4697
5361
  }
4698
- const controller = new AbortController();
4699
- trackRequest(state, requestId, windowId, controller);
4700
5362
  try {
4701
5363
  const response = await options.fetch(url, {
4702
5364
  method: init?.method,
4703
5365
  headers: init?.headers ? { ...init.headers } : void 0,
4704
- signal: controller.signal
5366
+ signal
4705
5367
  });
4706
5368
  const buffer = await response.arrayBuffer();
5369
+ const headers = collectResponseHeaders(response);
5370
+ const mime = responseMime(response);
5371
+ return {
5372
+ ok: true,
5373
+ url,
5374
+ blob: responseBlob(buffer, mime),
5375
+ mime,
5376
+ status: response.status,
5377
+ headers,
5378
+ bodyBase64: arrayBufferToBase64(buffer)
5379
+ };
5380
+ } catch (err) {
5381
+ const isAbort = signal.aborted || err instanceof Error && (err.name === "AbortError" || err.name === "DOMException");
5382
+ return {
5383
+ ok: false,
5384
+ url,
5385
+ error: isAbort ? "timeout" : "network-error",
5386
+ code: isAbort ? "canceled" : "network-error",
5387
+ message: err instanceof Error ? err.message : String(err)
5388
+ };
5389
+ }
5390
+ }
5391
+ async function handleBytes(options, state, windowId, msg, send) {
5392
+ const { requestId, url, init } = msg;
5393
+ const identity = options.resolveIdentity(windowId);
5394
+ if (!identity) {
5395
+ sendResourceError(send, requestId, "denied", "napplet identity not resolvable", "blocked-by-policy");
5396
+ return;
5397
+ }
5398
+ const controller = new AbortController();
5399
+ trackRequest(state, requestId, windowId, controller);
5400
+ try {
5401
+ const item = await fetchResourceItem(options, identity, url, init, controller.signal);
5402
+ if (!item.ok) {
5403
+ sendResourceError(send, requestId, item.code, item.message, item.error);
5404
+ return;
5405
+ }
4707
5406
  send({
4708
5407
  type: "resource.bytes.result",
5408
+ id: requestId,
4709
5409
  requestId,
4710
- status: response.status,
4711
- headers: collectResponseHeaders(response),
4712
- bodyBase64: arrayBufferToBase64(buffer)
5410
+ blob: item.blob,
5411
+ mime: item.mime,
5412
+ status: item.status,
5413
+ headers: item.headers,
5414
+ bodyBase64: item.bodyBase64
4713
5415
  });
4714
- } catch (err) {
4715
- const isAbort = controller.signal.aborted || err instanceof Error && (err.name === "AbortError" || err.name === "DOMException");
4716
- sendResourceError(
4717
- send,
5416
+ } finally {
5417
+ untrackRequest(state, requestId);
5418
+ }
5419
+ }
5420
+ async function handleBytesMany(options, state, windowId, msg, send) {
5421
+ const { requestId, urls, init } = msg;
5422
+ if (!Array.isArray(urls) || urls.length === 0 || urls.some((url) => typeof url !== "string")) {
5423
+ sendBytesManyError(send, requestId, "invalid-url", "resource.bytesMany requires a non-empty urls array", "invalid-request");
5424
+ return;
5425
+ }
5426
+ const identity = options.resolveIdentity(windowId);
5427
+ if (!identity) {
5428
+ sendBytesManyError(send, requestId, "denied", "napplet identity not resolvable", "blocked-by-policy");
5429
+ return;
5430
+ }
5431
+ const controller = new AbortController();
5432
+ trackRequest(state, requestId, windowId, controller);
5433
+ try {
5434
+ const items = [];
5435
+ for (const url of urls) {
5436
+ const item = await fetchResourceItem(options, identity, url, init, controller.signal);
5437
+ if (item.ok) {
5438
+ items.push({
5439
+ url: item.url,
5440
+ ok: true,
5441
+ blob: item.blob,
5442
+ mime: item.mime
5443
+ });
5444
+ } else {
5445
+ items.push({
5446
+ url: item.url,
5447
+ ok: false,
5448
+ error: item.error,
5449
+ code: item.code,
5450
+ message: item.message
5451
+ });
5452
+ }
5453
+ }
5454
+ send({
5455
+ type: "resource.bytesMany.result",
5456
+ id: requestId,
4718
5457
  requestId,
4719
- isAbort ? "canceled" : "network-error",
4720
- err instanceof Error ? err.message : String(err)
4721
- );
5458
+ items
5459
+ });
4722
5460
  } finally {
4723
5461
  untrackRequest(state, requestId);
4724
5462
  }
@@ -4758,13 +5496,24 @@ function createResourceService(options) {
4758
5496
  switch (message.type) {
4759
5497
  case "resource.bytes": {
4760
5498
  const m = message;
4761
- handleBytes(options, state, windowId, m, send).catch(() => {
5499
+ const requestId = requestIdFromMessage(m);
5500
+ if (!requestId || typeof m.url !== "string") return;
5501
+ handleBytes(options, state, windowId, { requestId, url: m.url, init: m.init }, send).catch(() => {
5502
+ });
5503
+ return;
5504
+ }
5505
+ case "resource.bytesMany": {
5506
+ const m = message;
5507
+ const requestId = requestIdFromMessage(m);
5508
+ if (!requestId) return;
5509
+ handleBytesMany(options, state, windowId, { requestId, urls: m.urls ?? [], init: m.init }, send).catch(() => {
4762
5510
  });
4763
5511
  return;
4764
5512
  }
4765
5513
  case "resource.cancel": {
4766
5514
  const m = message;
4767
- handleCancel(state, m.requestId);
5515
+ const requestId = requestIdFromMessage(m);
5516
+ if (requestId) handleCancel(state, requestId);
4768
5517
  return;
4769
5518
  }
4770
5519
  default:
@@ -5042,7 +5791,7 @@ function createIntentService(options) {
5042
5791
  send({ type: "intent.changed", availability });
5043
5792
  }
5044
5793
  });
5045
- function settle(call, send, resultType, id, onValue) {
5794
+ function settle6(call, send, resultType, id, onValue) {
5046
5795
  let pending;
5047
5796
  try {
5048
5797
  pending = Promise.resolve(call());
@@ -5060,7 +5809,7 @@ function createIntentService(options) {
5060
5809
  send({ type: "intent.invoke.result", id, error: "invalid request" });
5061
5810
  return;
5062
5811
  }
5063
- settle(
5812
+ settle6(
5064
5813
  () => resolver.invoke(request, { windowId }),
5065
5814
  send,
5066
5815
  "intent.invoke.result",
@@ -5076,7 +5825,7 @@ function createIntentService(options) {
5076
5825
  return;
5077
5826
  }
5078
5827
  const archetype = m.archetype;
5079
- settle(
5828
+ settle6(
5080
5829
  () => resolver.available(archetype),
5081
5830
  send,
5082
5831
  "intent.available.result",
@@ -5087,7 +5836,7 @@ function createIntentService(options) {
5087
5836
  function handleHandlers(msg, send) {
5088
5837
  const m = msg;
5089
5838
  const id = m.id ?? "";
5090
- settle(
5839
+ settle6(
5091
5840
  () => resolver.handlers(),
5092
5841
  send,
5093
5842
  "intent.handlers.result",
@@ -5222,67 +5971,954 @@ function toErrorMessage5(err) {
5222
5971
  if (typeof err === "string") return err;
5223
5972
  return "cvm request failed";
5224
5973
  }
5225
-
5226
- // src/simulation.ts
5227
- function summarizePajaSimulation(simulation) {
5228
- const relay = simulation.relay.mode === "memory" ? `relay:${simulation.relay.urls.length}` : "relay:off";
5229
- const identity = simulation.identity.mode === "fixed" ? "identity:fixed" : "identity:anon";
5230
- const storage = `storage:${simulation.storage.mode}`;
5231
- const theme = `theme:${simulation.theme.mode}`;
5232
- const disabled = simulation.capabilities.disabledDomains.length > 0 ? `off:${simulation.capabilities.disabledDomains.join(",")}` : "off:none";
5233
- return `${identity} ${relay} ${storage} ${theme} ${disabled}`;
5974
+ var LINK_SERVICE_VERSION = "1.0.0";
5975
+ var DEFAULT_ALLOWED_PROTOCOLS = ["http:", "https:"];
5976
+ function denied(id, error) {
5977
+ return { type: "link.open.result", id, status: "denied", error };
5234
5978
  }
5235
-
5236
- // src/browser-host.ts
5237
- var DEV_INTENT_ARCHETYPE = "paja-target";
5238
- var DEV_CVM_SERVER = {
5239
- pubkey: "0".repeat(64),
5240
- name: "Kehto Paja ContextVM",
5241
- description: "Deterministic development ContextVM adapter",
5242
- relays: ["wss://relay.kehto.dev"],
5243
- capabilities: ["echo"]
5244
- };
5245
- function readConfig() {
5246
- const script = document.getElementById("kehto-paja-config");
5247
- if (!script?.textContent) {
5248
- throw new Error("Missing Kehto Paja config.");
5979
+ function parseLinkUrl(id, rawUrl) {
5980
+ try {
5981
+ return new URL(rawUrl);
5982
+ } catch {
5983
+ return denied(id, "invalid-url");
5249
5984
  }
5250
- return JSON.parse(script.textContent);
5251
- }
5252
- function setStatus(state, status) {
5253
- state.status = status;
5254
- const statusEl = document.getElementById("lifecycle-status");
5255
- if (statusEl) statusEl.textContent = status;
5256
5985
  }
5257
- function setSimulationStatus(state) {
5258
- const statusEl = document.getElementById("simulation-status");
5259
- if (statusEl) statusEl.textContent = summarizePajaSimulation(state.simulation);
5260
- const themeSelect = document.getElementById("simulation-theme");
5261
- if (themeSelect instanceof HTMLSelectElement) themeSelect.value = state.simulation.theme.mode;
5986
+ function isProtocolAllowed(url, allowedProtocols) {
5987
+ return allowedProtocols.includes(url.protocol);
5262
5988
  }
5263
- function getFrame() {
5264
- const frame = document.getElementById("napplet-frame");
5265
- if (!(frame instanceof HTMLIFrameElement)) {
5266
- throw new Error("Missing Kehto Paja iframe.");
5989
+ async function handleOpen(options, windowId, message, send) {
5990
+ const parsed = parseLinkUrl(message.id, message.url);
5991
+ if ("type" in parsed) {
5992
+ send(parsed);
5993
+ return;
5994
+ }
5995
+ const allowedProtocols = options.allowedProtocols ?? DEFAULT_ALLOWED_PROTOCOLS;
5996
+ if (!isProtocolAllowed(parsed, allowedProtocols)) {
5997
+ send(denied(message.id, "unsupported-scheme"));
5998
+ return;
5999
+ }
6000
+ if (!options.open) {
6001
+ send(denied(message.id, "blocked-by-policy"));
6002
+ return;
6003
+ }
6004
+ try {
6005
+ const result = await options.open({ windowId, url: parsed, options: message.options });
6006
+ send({
6007
+ type: "link.open.result",
6008
+ id: message.id,
6009
+ status: result.status,
6010
+ ...result.status === "denied" ? { error: "blocked-by-policy" } : {}
6011
+ });
6012
+ } catch {
6013
+ send(denied(message.id, "blocked-by-policy"));
5267
6014
  }
5268
- frame.sandbox.add("allow-scripts");
5269
- frame.sandbox.remove("allow-same-origin");
5270
- return frame;
5271
- }
5272
- function matchesFilter2(event, filter) {
5273
- const ids = filter.ids;
5274
- if (ids && !ids.includes(event.id)) return false;
5275
- const authors = filter.authors;
5276
- if (authors && !authors.includes(event.pubkey)) return false;
5277
- const kinds = filter.kinds;
5278
- if (kinds && !kinds.includes(event.kind)) return false;
5279
- return true;
5280
- }
5281
- function matchesAnyFilter2(event, filters) {
5282
- return filters.length === 0 || filters.some((filter) => matchesFilter2(event, filter));
5283
6015
  }
5284
- function createMemoryRelayPool(getSimulation) {
5285
- const events = getSimulation().relay.fixtures.flatMap(toNostrEvent);
6016
+ function createLinkService(options = {}) {
6017
+ const descriptor = {
6018
+ name: "link",
6019
+ version: LINK_SERVICE_VERSION,
6020
+ description: "NAP-LINK reference handler for shell-mediated link opening"
6021
+ };
6022
+ return {
6023
+ descriptor,
6024
+ handleMessage(windowId, message, send) {
6025
+ if (message.type === "link.open") {
6026
+ void handleOpen(options, windowId, message, send);
6027
+ return;
6028
+ }
6029
+ const id = message.id ?? "";
6030
+ send({
6031
+ type: `${message.type}.error`,
6032
+ id,
6033
+ error: `Unknown link method: ${message.type}`
6034
+ });
6035
+ },
6036
+ onWindowDestroyed(_windowId) {
6037
+ }
6038
+ };
6039
+ }
6040
+ var LISTS_SERVICE_VERSION = "1.0.0";
6041
+ var LISTS_DESCRIPTOR = {
6042
+ name: "lists",
6043
+ version: LISTS_SERVICE_VERSION,
6044
+ description: "NAP-LISTS reference handler for shell-mediated NIP-51 list mutations"
6045
+ };
6046
+ function errorMessage(err, fallback) {
6047
+ if (err instanceof Error) return err.message;
6048
+ if (typeof err === "string") return err;
6049
+ return fallback;
6050
+ }
6051
+ function settle(call, send, okFalse, onValue) {
6052
+ let pending;
6053
+ try {
6054
+ pending = Promise.resolve(call());
6055
+ } catch (err) {
6056
+ send(okFalse(errorMessage(err, "lists request failed")));
6057
+ return;
6058
+ }
6059
+ pending.then((value) => send(onValue(value))).catch((err) => send(okFalse(errorMessage(err, "lists request failed"))));
6060
+ }
6061
+ function unsupportedMutation(resultType, id) {
6062
+ return {
6063
+ type: resultType,
6064
+ id,
6065
+ ok: false,
6066
+ error: "unsupported",
6067
+ reason: `${resultType.replace(".result", "")} unavailable`,
6068
+ supported: []
6069
+ };
6070
+ }
6071
+ function settleMutation(hook, message, context, send, resultType) {
6072
+ if (!hook) {
6073
+ send(unsupportedMutation(resultType, message.id));
6074
+ return;
6075
+ }
6076
+ settle(
6077
+ () => hook(message.list, message.items, message.options, context),
6078
+ send,
6079
+ (error) => ({ type: resultType, id: message.id, ok: false, error: "list-unavailable", reason: error }),
6080
+ (result) => ({ type: resultType, id: message.id, ...result })
6081
+ );
6082
+ }
6083
+ function createListsService(options = {}) {
6084
+ return {
6085
+ descriptor: LISTS_DESCRIPTOR,
6086
+ handleMessage(windowId, message, send) {
6087
+ const id = message.id ?? "";
6088
+ const context = { windowId };
6089
+ if (message.type === "lists.supported") {
6090
+ if (!options.supported) {
6091
+ send({ type: "lists.supported.result", id, lists: [] });
6092
+ return;
6093
+ }
6094
+ settle(
6095
+ () => options.supported(context),
6096
+ send,
6097
+ (error) => ({ type: "lists.supported.result", id, error }),
6098
+ (lists) => ({ type: "lists.supported.result", id, lists: [...lists] })
6099
+ );
6100
+ return;
6101
+ }
6102
+ if (message.type === "lists.add") {
6103
+ settleMutation(options.add, message, context, send, "lists.add.result");
6104
+ return;
6105
+ }
6106
+ if (message.type === "lists.remove") {
6107
+ settleMutation(options.remove, message, context, send, "lists.remove.result");
6108
+ return;
6109
+ }
6110
+ send({
6111
+ type: `${message.type}.error`,
6112
+ id,
6113
+ error: `Unknown lists method: ${message.type}`
6114
+ });
6115
+ },
6116
+ onWindowDestroyed(_windowId) {
6117
+ }
6118
+ };
6119
+ }
6120
+ var SERIAL_SERVICE_VERSION = "1.0.0";
6121
+ var SERIAL_DESCRIPTOR = {
6122
+ name: "serial",
6123
+ version: SERIAL_SERVICE_VERSION,
6124
+ description: "NAP-SERIAL reference handler for shell-mediated serial sessions"
6125
+ };
6126
+ function errorMessage2(err, fallback) {
6127
+ if (err instanceof Error) return err.message;
6128
+ if (typeof err === "string") return err;
6129
+ return fallback;
6130
+ }
6131
+ function settle2(call, send, okFalse, onValue) {
6132
+ let pending;
6133
+ try {
6134
+ pending = Promise.resolve(call());
6135
+ } catch (err) {
6136
+ send(okFalse(errorMessage2(err, "serial request failed")));
6137
+ return;
6138
+ }
6139
+ pending.then((value) => send(onValue(value))).catch((err) => send(okFalse(errorMessage2(err, "serial request failed"))));
6140
+ }
6141
+ function unsupported(resultType, id) {
6142
+ return {
6143
+ type: resultType,
6144
+ id,
6145
+ error: `${resultType.replace(".result", "")} unavailable`
6146
+ };
6147
+ }
6148
+ function createSerialService(options = {}) {
6149
+ return {
6150
+ descriptor: SERIAL_DESCRIPTOR,
6151
+ handleMessage(windowId, message, send) {
6152
+ const id = message.id ?? "";
6153
+ const context = { windowId };
6154
+ if (message.type === "serial.open") {
6155
+ if (!options.open) {
6156
+ send(unsupported("serial.open.result", id));
6157
+ return;
6158
+ }
6159
+ const serialMessage = message;
6160
+ settle2(
6161
+ () => options.open(serialMessage.request, context),
6162
+ send,
6163
+ (error) => ({ type: "serial.open.result", id, error }),
6164
+ (result) => ({ type: "serial.open.result", id, session: result.session })
6165
+ );
6166
+ return;
6167
+ }
6168
+ if (message.type === "serial.write") {
6169
+ if (!options.write) {
6170
+ send(unsupported("serial.write.result", id));
6171
+ return;
6172
+ }
6173
+ const serialMessage = message;
6174
+ settle2(
6175
+ () => options.write(serialMessage.sessionId, serialMessage.data, context),
6176
+ send,
6177
+ (error) => ({ type: "serial.write.result", id, error }),
6178
+ () => ({ type: "serial.write.result", id })
6179
+ );
6180
+ return;
6181
+ }
6182
+ if (message.type === "serial.close") {
6183
+ if (!options.close) {
6184
+ send(unsupported("serial.close.result", id));
6185
+ return;
6186
+ }
6187
+ const serialMessage = message;
6188
+ settle2(
6189
+ () => options.close(serialMessage.sessionId, serialMessage.reason, context),
6190
+ send,
6191
+ (error) => ({ type: "serial.close.result", id, error }),
6192
+ () => ({ type: "serial.close.result", id })
6193
+ );
6194
+ return;
6195
+ }
6196
+ send({
6197
+ type: `${message.type}.error`,
6198
+ id,
6199
+ error: `Unknown serial method: ${message.type}`
6200
+ });
6201
+ },
6202
+ onWindowDestroyed(windowId) {
6203
+ options.destroyWindow?.(windowId);
6204
+ }
6205
+ };
6206
+ }
6207
+ var BLE_SERVICE_VERSION = "1.0.0";
6208
+ var BLE_DESCRIPTOR = {
6209
+ name: "ble",
6210
+ version: BLE_SERVICE_VERSION,
6211
+ description: "NAP-BLE reference handler for shell-mediated BLE/GATT sessions"
6212
+ };
6213
+ function errorMessage3(err, fallback) {
6214
+ if (err instanceof Error) return err.message;
6215
+ if (typeof err === "string") return err;
6216
+ return fallback;
6217
+ }
6218
+ function settle3(call, send, okFalse, onValue) {
6219
+ let pending;
6220
+ try {
6221
+ pending = Promise.resolve(call());
6222
+ } catch (err) {
6223
+ send(okFalse(errorMessage3(err, "ble request failed")));
6224
+ return;
6225
+ }
6226
+ pending.then((value) => send(onValue(value))).catch((err) => send(okFalse(errorMessage3(err, "ble request failed"))));
6227
+ }
6228
+ function unsupported2(resultType, id) {
6229
+ return {
6230
+ type: resultType,
6231
+ id,
6232
+ error: `${resultType.replace(".result", "")} unavailable`
6233
+ };
6234
+ }
6235
+ function createContext(windowId, send) {
6236
+ return {
6237
+ windowId,
6238
+ emit(event) {
6239
+ send({ type: "ble.event", event });
6240
+ }
6241
+ };
6242
+ }
6243
+ function createBleService(options = {}) {
6244
+ return {
6245
+ descriptor: BLE_DESCRIPTOR,
6246
+ handleMessage(windowId, message, send) {
6247
+ const id = message.id ?? "";
6248
+ const context = createContext(windowId, send);
6249
+ if (message.type === "ble.open") {
6250
+ if (!options.open) {
6251
+ send(unsupported2("ble.open.result", id));
6252
+ return;
6253
+ }
6254
+ const bleMessage = message;
6255
+ settle3(
6256
+ () => options.open(bleMessage.request, context),
6257
+ send,
6258
+ (error) => ({ type: "ble.open.result", id, error }),
6259
+ (result) => ({ type: "ble.open.result", id, session: result.session })
6260
+ );
6261
+ return;
6262
+ }
6263
+ if (message.type === "ble.services") {
6264
+ if (!options.services) {
6265
+ send(unsupported2("ble.services.result", id));
6266
+ return;
6267
+ }
6268
+ const bleMessage = message;
6269
+ settle3(
6270
+ () => options.services(bleMessage.sessionId, context),
6271
+ send,
6272
+ (error) => ({ type: "ble.services.result", id, error }),
6273
+ (services) => ({ type: "ble.services.result", id, services })
6274
+ );
6275
+ return;
6276
+ }
6277
+ if (message.type === "ble.read") {
6278
+ if (!options.read) {
6279
+ send(unsupported2("ble.read.result", id));
6280
+ return;
6281
+ }
6282
+ const bleMessage = message;
6283
+ settle3(
6284
+ () => options.read(bleMessage.sessionId, bleMessage.target, context),
6285
+ send,
6286
+ (error) => ({ type: "ble.read.result", id, error }),
6287
+ (data) => ({ type: "ble.read.result", id, data })
6288
+ );
6289
+ return;
6290
+ }
6291
+ if (message.type === "ble.write") {
6292
+ if (!options.write) {
6293
+ send(unsupported2("ble.write.result", id));
6294
+ return;
6295
+ }
6296
+ const bleMessage = message;
6297
+ settle3(
6298
+ () => options.write(bleMessage.sessionId, bleMessage.target, bleMessage.data, bleMessage.options, context),
6299
+ send,
6300
+ (error) => ({ type: "ble.write.result", id, error }),
6301
+ () => ({ type: "ble.write.result", id })
6302
+ );
6303
+ return;
6304
+ }
6305
+ if (message.type === "ble.subscribe") {
6306
+ if (!options.subscribe) {
6307
+ send(unsupported2("ble.subscribe.result", id));
6308
+ return;
6309
+ }
6310
+ const bleMessage = message;
6311
+ settle3(
6312
+ () => options.subscribe(bleMessage.sessionId, bleMessage.target, context),
6313
+ send,
6314
+ (error) => ({ type: "ble.subscribe.result", id, error }),
6315
+ () => ({ type: "ble.subscribe.result", id })
6316
+ );
6317
+ return;
6318
+ }
6319
+ if (message.type === "ble.unsubscribe") {
6320
+ if (!options.unsubscribe) {
6321
+ send(unsupported2("ble.unsubscribe.result", id));
6322
+ return;
6323
+ }
6324
+ const bleMessage = message;
6325
+ settle3(
6326
+ () => options.unsubscribe(bleMessage.sessionId, bleMessage.target, context),
6327
+ send,
6328
+ (error) => ({ type: "ble.unsubscribe.result", id, error }),
6329
+ () => ({ type: "ble.unsubscribe.result", id })
6330
+ );
6331
+ return;
6332
+ }
6333
+ if (message.type === "ble.close") {
6334
+ if (!options.close) {
6335
+ send(unsupported2("ble.close.result", id));
6336
+ return;
6337
+ }
6338
+ const bleMessage = message;
6339
+ settle3(
6340
+ () => options.close(bleMessage.sessionId, bleMessage.reason, context),
6341
+ send,
6342
+ (error) => ({ type: "ble.close.result", id, error }),
6343
+ () => ({ type: "ble.close.result", id })
6344
+ );
6345
+ return;
6346
+ }
6347
+ send({
6348
+ type: `${message.type}.error`,
6349
+ id,
6350
+ error: `Unknown ble method: ${message.type}`
6351
+ });
6352
+ },
6353
+ onWindowDestroyed(windowId) {
6354
+ options.destroyWindow?.(windowId);
6355
+ }
6356
+ };
6357
+ }
6358
+ var WEBRTC_SERVICE_VERSION = "1.0.0";
6359
+ var WEBRTC_DESCRIPTOR = {
6360
+ name: "webrtc",
6361
+ version: WEBRTC_SERVICE_VERSION,
6362
+ description: "NAP-WEBRTC reference handler for shell-mediated WebRTC sessions"
6363
+ };
6364
+ function errorMessage4(err, fallback) {
6365
+ if (err instanceof Error) return err.message;
6366
+ if (typeof err === "string") return err;
6367
+ return fallback;
6368
+ }
6369
+ function settle4(call, send, okFalse, onValue) {
6370
+ let pending;
6371
+ try {
6372
+ pending = Promise.resolve(call());
6373
+ } catch (err) {
6374
+ send(okFalse(errorMessage4(err, "webrtc request failed")));
6375
+ return;
6376
+ }
6377
+ pending.then((value) => send(onValue(value))).catch((err) => send(okFalse(errorMessage4(err, "webrtc request failed"))));
6378
+ }
6379
+ function unsupported3(resultType, id) {
6380
+ return {
6381
+ type: resultType,
6382
+ id,
6383
+ error: `${resultType.replace(".result", "")} unavailable`
6384
+ };
6385
+ }
6386
+ function createContext2(windowId, send) {
6387
+ return {
6388
+ windowId,
6389
+ emit(event) {
6390
+ send({ type: "webrtc.event", event });
6391
+ }
6392
+ };
6393
+ }
6394
+ function createWebrtcService(options = {}) {
6395
+ return {
6396
+ descriptor: WEBRTC_DESCRIPTOR,
6397
+ handleMessage(windowId, message, send) {
6398
+ const id = message.id ?? "";
6399
+ const context = createContext2(windowId, send);
6400
+ if (message.type === "webrtc.open") {
6401
+ if (!options.open) {
6402
+ send(unsupported3("webrtc.open.result", id));
6403
+ return;
6404
+ }
6405
+ const webrtcMessage = message;
6406
+ settle4(
6407
+ () => options.open(webrtcMessage.request, context),
6408
+ send,
6409
+ (error) => ({ type: "webrtc.open.result", id, error }),
6410
+ (result) => ({ type: "webrtc.open.result", id, session: result.session })
6411
+ );
6412
+ return;
6413
+ }
6414
+ if (message.type === "webrtc.send") {
6415
+ if (!options.send) {
6416
+ send(unsupported3("webrtc.send.result", id));
6417
+ return;
6418
+ }
6419
+ const webrtcMessage = message;
6420
+ settle4(
6421
+ () => options.send(webrtcMessage.sessionId, webrtcMessage.payload, context),
6422
+ send,
6423
+ (error) => ({ type: "webrtc.send.result", id, error }),
6424
+ () => ({ type: "webrtc.send.result", id })
6425
+ );
6426
+ return;
6427
+ }
6428
+ if (message.type === "webrtc.close") {
6429
+ if (!options.close) {
6430
+ send(unsupported3("webrtc.close.result", id));
6431
+ return;
6432
+ }
6433
+ const webrtcMessage = message;
6434
+ settle4(
6435
+ () => options.close(webrtcMessage.sessionId, webrtcMessage.reason, context),
6436
+ send,
6437
+ (error) => ({ type: "webrtc.close.result", id, error }),
6438
+ () => ({ type: "webrtc.close.result", id })
6439
+ );
6440
+ return;
6441
+ }
6442
+ send({
6443
+ type: `${message.type}.error`,
6444
+ id,
6445
+ error: `Unknown webrtc method: ${message.type}`
6446
+ });
6447
+ },
6448
+ onWindowDestroyed(windowId) {
6449
+ options.destroyWindow?.(windowId);
6450
+ }
6451
+ };
6452
+ }
6453
+ var COMMON_SERVICE_VERSION = "1.0.0";
6454
+ var BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
6455
+ var COMMON_DESCRIPTOR = {
6456
+ name: "common",
6457
+ version: COMMON_SERVICE_VERSION,
6458
+ description: "NAP-COMMON reference handler for shell-mediated social helpers"
6459
+ };
6460
+ function errorMessage5(err, fallback) {
6461
+ if (err instanceof Error) return err.message;
6462
+ if (typeof err === "string") return err;
6463
+ return fallback;
6464
+ }
6465
+ function encodeNip19(input) {
6466
+ try {
6467
+ switch (input.type) {
6468
+ case "npub":
6469
+ return { ok: true, value: npubEncode(input.hex), nip19Type: input.type };
6470
+ case "note":
6471
+ return { ok: true, value: noteEncode(input.hex), nip19Type: input.type };
6472
+ case "nprofile":
6473
+ return { ok: true, value: nprofileEncode({ pubkey: input.pubkey, relays: input.relays }), nip19Type: input.type };
6474
+ case "nevent":
6475
+ return {
6476
+ ok: true,
6477
+ value: neventEncode({ id: input.eventId, relays: input.relays, author: input.author, kind: input.kind }),
6478
+ nip19Type: input.type
6479
+ };
6480
+ case "naddr":
6481
+ return {
6482
+ ok: true,
6483
+ value: naddrEncode({ identifier: input.identifier, pubkey: input.pubkey, kind: input.kind, relays: input.relays }),
6484
+ nip19Type: input.type
6485
+ };
6486
+ case "nrelay":
6487
+ return { ok: true, value: encodeBytes("nrelay", new TextEncoder().encode(input.relay)), nip19Type: input.type };
6488
+ default:
6489
+ return { ok: false, error: "unsupported NIP-19 type" };
6490
+ }
6491
+ } catch (err) {
6492
+ return { ok: false, error: errorMessage5(err, "NIP-19 encode failed") };
6493
+ }
6494
+ }
6495
+ function decodeNip19Value(value) {
6496
+ try {
6497
+ const decoded = decode(value);
6498
+ switch (decoded.type) {
6499
+ case "npub":
6500
+ case "note":
6501
+ return { ok: true, nip19Type: decoded.type, hex: decoded.data };
6502
+ case "nprofile":
6503
+ return { ok: true, nip19Type: decoded.type, pubkey: decoded.data.pubkey, relays: decoded.data.relays };
6504
+ case "nevent":
6505
+ return {
6506
+ ok: true,
6507
+ nip19Type: decoded.type,
6508
+ eventId: decoded.data.id,
6509
+ relays: decoded.data.relays,
6510
+ author: decoded.data.author,
6511
+ kind: decoded.data.kind
6512
+ };
6513
+ case "naddr":
6514
+ return {
6515
+ ok: true,
6516
+ nip19Type: decoded.type,
6517
+ identifier: decoded.data.identifier,
6518
+ pubkey: decoded.data.pubkey,
6519
+ kind: decoded.data.kind,
6520
+ relays: decoded.data.relays
6521
+ };
6522
+ default:
6523
+ return { ok: false, error: "unsupported NIP-19 type" };
6524
+ }
6525
+ } catch (err) {
6526
+ if (value.startsWith("nrelay1")) return decodeNrelay(value);
6527
+ return { ok: false, error: errorMessage5(err, "NIP-19 decode failed") };
6528
+ }
6529
+ }
6530
+ function decodeNrelay(value) {
6531
+ try {
6532
+ const separator = value.lastIndexOf("1");
6533
+ if (separator <= 0 || separator + 7 > value.length) return { ok: false, error: "invalid nrelay value" };
6534
+ const dataPart = value.slice(separator + 1);
6535
+ const words = [...dataPart].map((char) => {
6536
+ const index = BECH32_CHARSET.indexOf(char);
6537
+ if (index === -1) throw new Error("invalid nrelay character");
6538
+ return index;
6539
+ });
6540
+ const payloadWords = words.slice(0, -6);
6541
+ const bytes = convertBits(payloadWords, 5, 8, false);
6542
+ return { ok: true, nip19Type: "nrelay", relay: new TextDecoder().decode(Uint8Array.from(bytes)) };
6543
+ } catch (err) {
6544
+ return { ok: false, error: errorMessage5(err, "invalid nrelay value") };
6545
+ }
6546
+ }
6547
+ function convertBits(data, fromBits, toBits, pad) {
6548
+ let acc = 0;
6549
+ let bits = 0;
6550
+ const result = [];
6551
+ const maxv = (1 << toBits) - 1;
6552
+ const maxAcc = (1 << fromBits + toBits - 1) - 1;
6553
+ for (const value of data) {
6554
+ if (value < 0 || value >> fromBits !== 0) throw new Error("invalid bech32 data");
6555
+ acc = (acc << fromBits | value) & maxAcc;
6556
+ bits += fromBits;
6557
+ while (bits >= toBits) {
6558
+ bits -= toBits;
6559
+ result.push(acc >> bits & maxv);
6560
+ }
6561
+ }
6562
+ if (pad) {
6563
+ if (bits > 0) result.push(acc << toBits - bits & maxv);
6564
+ } else if (bits >= fromBits || (acc << toBits - bits & maxv) !== 0) {
6565
+ throw new Error("invalid bech32 padding");
6566
+ }
6567
+ return result;
6568
+ }
6569
+ function settle5(call, send, okFalse, onValue) {
6570
+ let pending;
6571
+ try {
6572
+ pending = Promise.resolve(call());
6573
+ } catch (err) {
6574
+ send(okFalse(errorMessage5(err, "common request failed")));
6575
+ return;
6576
+ }
6577
+ pending.then((value) => send(onValue(value))).catch((err) => send(okFalse(errorMessage5(err, "common request failed"))));
6578
+ }
6579
+ function createCommonService(options = {}) {
6580
+ return {
6581
+ descriptor: COMMON_DESCRIPTOR,
6582
+ handleMessage(windowId, message, send) {
6583
+ const id = message.id ?? "";
6584
+ const context = { windowId };
6585
+ if (message.type === "common.encodeNip19") {
6586
+ const input = message.input;
6587
+ const result = input ? encodeNip19(input) : { ok: false, error: "invalid input" };
6588
+ send({ type: "common.encodeNip19.result", id, ...result });
6589
+ return;
6590
+ }
6591
+ if (message.type === "common.decodeNip19") {
6592
+ const value = message.value;
6593
+ const result = typeof value === "string" ? decodeNip19Value(value) : { ok: false, error: "invalid value" };
6594
+ send({ type: "common.decodeNip19.result", id, ...result });
6595
+ return;
6596
+ }
6597
+ if (message.type === "common.getProfile") {
6598
+ const target = message.target;
6599
+ if (!options.getProfile) {
6600
+ send({ type: "common.getProfile.result", id, ok: false, pubkey: target ?? "", error: "profile lookup unavailable" });
6601
+ return;
6602
+ }
6603
+ settle5(
6604
+ () => options.getProfile(target, context),
6605
+ send,
6606
+ (error) => ({ type: "common.getProfile.result", id, ok: false, pubkey: target ?? "", error }),
6607
+ (result) => ({ type: "common.getProfile.result", id, ...result })
6608
+ );
6609
+ return;
6610
+ }
6611
+ if (message.type === "common.follows") {
6612
+ if (!options.follows) {
6613
+ send({ type: "common.follows.result", id, ok: false, pubkeys: [], error: "follows lookup unavailable" });
6614
+ return;
6615
+ }
6616
+ settle5(
6617
+ () => options.follows(context),
6618
+ send,
6619
+ (error) => ({ type: "common.follows.result", id, ok: false, pubkeys: [], error }),
6620
+ (result) => ({ type: "common.follows.result", id, ...result })
6621
+ );
6622
+ return;
6623
+ }
6624
+ if (message.type === "common.follow") {
6625
+ const pubkeys = message.pubkeys ?? [];
6626
+ settleAction(options.follow, pubkeys, context, send, "common.follow.result", id, "follow unavailable");
6627
+ return;
6628
+ }
6629
+ if (message.type === "common.unfollow") {
6630
+ const pubkeys = message.pubkeys ?? [];
6631
+ settleAction(options.unfollow, pubkeys, context, send, "common.unfollow.result", id, "unfollow unavailable");
6632
+ return;
6633
+ }
6634
+ if (message.type === "common.react") {
6635
+ const m = message;
6636
+ if (!options.react) {
6637
+ send({ type: "common.react.result", id, ok: false, error: "react unavailable" });
6638
+ return;
6639
+ }
6640
+ settle5(
6641
+ () => options.react(m.targetEventId, m.reaction, m.customEmojiHref, context),
6642
+ send,
6643
+ (error) => ({ type: "common.react.result", id, ok: false, error }),
6644
+ (result) => ({ type: "common.react.result", id, ...result })
6645
+ );
6646
+ return;
6647
+ }
6648
+ if (message.type === "common.report") {
6649
+ const m = message;
6650
+ if (!options.report) {
6651
+ send({ type: "common.report.result", id, ok: false, error: "report unavailable" });
6652
+ return;
6653
+ }
6654
+ settle5(
6655
+ () => options.report(m.target, m.reason, m.text, context),
6656
+ send,
6657
+ (error) => ({ type: "common.report.result", id, ok: false, error }),
6658
+ (result) => ({ type: "common.report.result", id, ...result })
6659
+ );
6660
+ return;
6661
+ }
6662
+ send({
6663
+ type: `${message.type}.error`,
6664
+ id,
6665
+ error: `Unknown common method: ${message.type}`
6666
+ });
6667
+ },
6668
+ onWindowDestroyed(_windowId) {
6669
+ }
6670
+ };
6671
+ }
6672
+ function settleAction(action, pubkeys, context, send, resultType, id, unavailable) {
6673
+ if (!action) {
6674
+ send({ type: resultType, id, ok: false, error: unavailable });
6675
+ return;
6676
+ }
6677
+ settle5(
6678
+ () => action(pubkeys, context),
6679
+ send,
6680
+ (error) => ({ type: resultType, id, ok: false, error }),
6681
+ (result) => ({ type: resultType, id, ...result })
6682
+ );
6683
+ }
6684
+
6685
+ // src/simulation.ts
6686
+ function summarizePajaSimulation(simulation) {
6687
+ const relay = simulation.relay.mode === "memory" ? `relay:${simulation.relay.urls.length}` : "relay:off";
6688
+ const identity = simulation.identity.mode === "fixed" ? "identity:fixed" : "identity:anon";
6689
+ const storage = `storage:${simulation.storage.mode}`;
6690
+ const theme = `theme:${simulation.theme.mode}`;
6691
+ const disabled = simulation.capabilities.disabledDomains.length > 0 ? `off:${simulation.capabilities.disabledDomains.join(",")}` : "off:none";
6692
+ return `${identity} ${relay} ${storage} ${theme} ${disabled}`;
6693
+ }
6694
+
6695
+ // src/development-services.ts
6696
+ var DEV_LISTS_EVENT_ID = "3".repeat(64);
6697
+ var DEV_SERIAL_LABEL = "Paja serial";
6698
+ var DEV_BLE_DEVICE_NAME = "Paja BLE";
6699
+ var DEV_BLE_SERVICE_UUID = "battery_service";
6700
+ var DEV_BLE_CHARACTERISTIC_UUID = "battery_level";
6701
+ var DEV_WEBRTC_PEER = "6".repeat(64);
6702
+ var DEV_LISTS_SUPPORT = {
6703
+ kind: 10003,
6704
+ type: "bookmarks",
6705
+ addressable: false,
6706
+ supportedItemTypes: ["event", "url"]
6707
+ };
6708
+ function destroyWindowSessions2(sessions, windowId) {
6709
+ for (const [sessionId, session] of sessions) {
6710
+ if (session.windowId === windowId) sessions.delete(sessionId);
6711
+ }
6712
+ }
6713
+ function createDevListStore() {
6714
+ const values = /* @__PURE__ */ new Set();
6715
+ const itemKey = (item) => `${item.itemType}:${item.value}`;
6716
+ const isSupported = (list) => "type" in list && list.type === DEV_LISTS_SUPPORT.type || "kind" in list && list.kind === DEV_LISTS_SUPPORT.kind;
6717
+ return {
6718
+ supported: () => [DEV_LISTS_SUPPORT],
6719
+ add(list, items) {
6720
+ if (!isSupported(list)) return { ok: false, error: "unsupported-list", reason: "unsupported list", supported: [DEV_LISTS_SUPPORT] };
6721
+ let added = 0;
6722
+ let skipped = 0;
6723
+ for (const item of items) {
6724
+ const key = itemKey(item);
6725
+ if (values.has(key)) skipped += 1;
6726
+ else {
6727
+ values.add(key);
6728
+ added += 1;
6729
+ }
6730
+ }
6731
+ return { ok: true, eventId: DEV_LISTS_EVENT_ID, added, skipped };
6732
+ },
6733
+ remove(list, items) {
6734
+ if (!isSupported(list)) return { ok: false, error: "unsupported-list", reason: "unsupported list", supported: [DEV_LISTS_SUPPORT] };
6735
+ let removed = 0;
6736
+ let skipped = 0;
6737
+ for (const item of items) {
6738
+ if (values.delete(itemKey(item))) removed += 1;
6739
+ else skipped += 1;
6740
+ }
6741
+ return { ok: true, eventId: DEV_LISTS_EVENT_ID, removed, skipped };
6742
+ }
6743
+ };
6744
+ }
6745
+ function createDevSerialController() {
6746
+ const sessions = /* @__PURE__ */ new Map();
6747
+ let nextSession = 1;
6748
+ return {
6749
+ open(_request, context) {
6750
+ const id = `paja-serial-${nextSession++}`;
6751
+ sessions.set(id, { windowId: context.windowId, writes: [] });
6752
+ return {
6753
+ session: {
6754
+ id,
6755
+ state: "open",
6756
+ info: { displayName: DEV_SERIAL_LABEL }
6757
+ }
6758
+ };
6759
+ },
6760
+ write(sessionId, data) {
6761
+ const session = sessions.get(sessionId);
6762
+ if (!session) throw new Error("serial session not found");
6763
+ session.writes.push([...data]);
6764
+ },
6765
+ close(sessionId) {
6766
+ if (!sessions.delete(sessionId)) throw new Error("serial session not found");
6767
+ },
6768
+ destroyWindow(windowId) {
6769
+ destroyWindowSessions2(sessions, windowId);
6770
+ }
6771
+ };
6772
+ }
6773
+ function createDevBleController() {
6774
+ const sessions = /* @__PURE__ */ new Map();
6775
+ let nextSession = 1;
6776
+ const service = {
6777
+ uuid: DEV_BLE_SERVICE_UUID,
6778
+ characteristics: [{
6779
+ uuid: DEV_BLE_CHARACTERISTIC_UUID,
6780
+ properties: { read: true, write: true, notify: true }
6781
+ }]
6782
+ };
6783
+ const targetKey = (target) => `${String(target.service)}:${String(target.characteristic)}:${String(target.descriptor ?? "")}`;
6784
+ const getSession = (sessionId) => {
6785
+ const session = sessions.get(sessionId);
6786
+ if (!session) throw new Error("ble session not found");
6787
+ return session;
6788
+ };
6789
+ return {
6790
+ open(_request, context) {
6791
+ const id = `paja-ble-${nextSession++}`;
6792
+ sessions.set(id, { windowId: context.windowId, writes: [], subscriptions: /* @__PURE__ */ new Set() });
6793
+ return {
6794
+ session: {
6795
+ id,
6796
+ state: "open",
6797
+ device: {
6798
+ id: "paja-ble-device",
6799
+ name: DEV_BLE_DEVICE_NAME,
6800
+ services: [DEV_BLE_SERVICE_UUID]
6801
+ }
6802
+ }
6803
+ };
6804
+ },
6805
+ services(sessionId) {
6806
+ getSession(sessionId);
6807
+ return [service];
6808
+ },
6809
+ read(sessionId, _target) {
6810
+ getSession(sessionId);
6811
+ return [87];
6812
+ },
6813
+ write(sessionId, _target, data, _options) {
6814
+ getSession(sessionId).writes.push([...data]);
6815
+ },
6816
+ subscribe(sessionId, target) {
6817
+ getSession(sessionId).subscriptions.add(targetKey(target));
6818
+ },
6819
+ unsubscribe(sessionId, target) {
6820
+ getSession(sessionId).subscriptions.delete(targetKey(target));
6821
+ },
6822
+ close(sessionId) {
6823
+ if (!sessions.delete(sessionId)) throw new Error("ble session not found");
6824
+ },
6825
+ destroyWindow(windowId) {
6826
+ destroyWindowSessions2(sessions, windowId);
6827
+ }
6828
+ };
6829
+ }
6830
+ function createDevWebrtcController() {
6831
+ const sessions = /* @__PURE__ */ new Map();
6832
+ let nextSession = 1;
6833
+ const getSession = (sessionId) => {
6834
+ const session = sessions.get(sessionId);
6835
+ if (!session) throw new Error("webrtc session not found");
6836
+ return session;
6837
+ };
6838
+ return {
6839
+ open(request, context) {
6840
+ const id = `paja-webrtc-${nextSession++}`;
6841
+ const channel = request.channel ?? "default";
6842
+ sessions.set(id, { windowId: context.windowId, payloads: [] });
6843
+ context.emit({ type: "state", sessionId: id, state: "open" });
6844
+ context.emit({ type: "peer", sessionId: id, pubkey: DEV_WEBRTC_PEER, state: "joined" });
6845
+ return {
6846
+ session: {
6847
+ id,
6848
+ scope: request.scope,
6849
+ channel,
6850
+ ...request.protocol ? { protocol: request.protocol } : {},
6851
+ state: "open"
6852
+ }
6853
+ };
6854
+ },
6855
+ send(sessionId, payload, context) {
6856
+ getSession(sessionId).payloads.push(payload);
6857
+ context.emit({ type: "message", sessionId, from: DEV_WEBRTC_PEER, payload });
6858
+ },
6859
+ close(sessionId, reason, context) {
6860
+ getSession(sessionId);
6861
+ sessions.delete(sessionId);
6862
+ context.emit({ type: "closed", sessionId, ...reason ? { reason } : {} });
6863
+ },
6864
+ destroyWindow(windowId) {
6865
+ destroyWindowSessions2(sessions, windowId);
6866
+ }
6867
+ };
6868
+ }
6869
+
6870
+ // src/browser-host.ts
6871
+ var DEV_INTENT_ARCHETYPE = "paja-target";
6872
+ var DEV_COMMON_PUBKEY = "1".repeat(64);
6873
+ var DEV_COMMON_EVENT_ID = "2".repeat(64);
6874
+ var DEV_CVM_SERVER = {
6875
+ pubkey: "0".repeat(64),
6876
+ name: "Kehto Paja ContextVM",
6877
+ description: "Deterministic development ContextVM adapter",
6878
+ relays: ["wss://relay.kehto.dev"],
6879
+ capabilities: ["echo"]
6880
+ };
6881
+ function readConfig() {
6882
+ const script = document.getElementById("kehto-paja-config");
6883
+ if (!script?.textContent) {
6884
+ throw new Error("Missing Kehto Paja config.");
6885
+ }
6886
+ return JSON.parse(script.textContent);
6887
+ }
6888
+ function setStatus(state, status) {
6889
+ state.status = status;
6890
+ const statusEl = document.getElementById("lifecycle-status");
6891
+ if (statusEl) statusEl.textContent = status;
6892
+ }
6893
+ function setSimulationStatus(state) {
6894
+ const statusEl = document.getElementById("simulation-status");
6895
+ if (statusEl) statusEl.textContent = summarizePajaSimulation(state.simulation);
6896
+ const themeSelect = document.getElementById("simulation-theme");
6897
+ if (themeSelect instanceof HTMLSelectElement) themeSelect.value = state.simulation.theme.mode;
6898
+ }
6899
+ function getFrame() {
6900
+ const frame = document.getElementById("napplet-frame");
6901
+ if (!(frame instanceof HTMLIFrameElement)) {
6902
+ throw new Error("Missing Kehto Paja iframe.");
6903
+ }
6904
+ frame.sandbox.add("allow-scripts");
6905
+ frame.sandbox.remove("allow-same-origin");
6906
+ return frame;
6907
+ }
6908
+ function matchesFilter2(event, filter) {
6909
+ const ids = filter.ids;
6910
+ if (ids && !ids.includes(event.id)) return false;
6911
+ const authors = filter.authors;
6912
+ if (authors && !authors.includes(event.pubkey)) return false;
6913
+ const kinds = filter.kinds;
6914
+ if (kinds && !kinds.includes(event.kind)) return false;
6915
+ return true;
6916
+ }
6917
+ function matchesAnyFilter2(event, filters) {
6918
+ return filters.length === 0 || filters.some((filter) => matchesFilter2(event, filter));
6919
+ }
6920
+ function createMemoryRelayPool(getSimulation) {
6921
+ const events = getSimulation().relay.fixtures.flatMap(toNostrEvent);
5286
6922
  const subscribers = /* @__PURE__ */ new Set();
5287
6923
  return {
5288
6924
  subscription(_relayUrls, filters) {
@@ -5587,6 +7223,43 @@ function createDevServices(pool, getSimulation, onThemeService) {
5587
7223
  }
5588
7224
  });
5589
7225
  }
7226
+ if (getSimulation().capabilities.domains.link) {
7227
+ services.link = createLinkService({
7228
+ open: ({ url }) => ({ status: url.protocol === "https:" || url.protocol === "http:" ? "opened" : "denied" })
7229
+ });
7230
+ }
7231
+ if (getSimulation().capabilities.domains.common) {
7232
+ services.common = createCommonService({
7233
+ getProfile: (target) => ({
7234
+ ok: true,
7235
+ pubkey: target || getSimulation().identity.pubkey || DEV_COMMON_PUBKEY,
7236
+ profile: { name: "paja", displayName: "Kehto Paja" },
7237
+ relays: getRelayUrls(getSimulation())
7238
+ }),
7239
+ follows: () => ({ ok: true, pubkeys: [getSimulation().identity.pubkey || DEV_COMMON_PUBKEY] }),
7240
+ follow: () => ({ ok: true, eventId: DEV_COMMON_EVENT_ID }),
7241
+ unfollow: () => ({ ok: true, eventId: DEV_COMMON_EVENT_ID }),
7242
+ react: () => ({ ok: true, eventId: DEV_COMMON_EVENT_ID }),
7243
+ report: () => ({ ok: true, eventId: DEV_COMMON_EVENT_ID })
7244
+ });
7245
+ }
7246
+ if (getSimulation().capabilities.domains.lists) {
7247
+ const listStore = createDevListStore();
7248
+ services.lists = createListsService({
7249
+ supported: listStore.supported,
7250
+ add: listStore.add,
7251
+ remove: listStore.remove
7252
+ });
7253
+ }
7254
+ if (getSimulation().capabilities.domains.serial) {
7255
+ services.serial = createSerialService(createDevSerialController());
7256
+ }
7257
+ if (getSimulation().capabilities.domains.ble) {
7258
+ services.ble = createBleService(createDevBleController());
7259
+ }
7260
+ if (getSimulation().capabilities.domains.webrtc) {
7261
+ services.webrtc = createWebrtcService(createDevWebrtcController());
7262
+ }
5590
7263
  return services;
5591
7264
  }
5592
7265
  function createPajaAdapter(config, getSimulation, onThemeService) {
@@ -5618,6 +7291,12 @@ function createPajaAdapter(config, getSimulation, onThemeService) {
5618
7291
  workerRelay: { getWorkerRelay: () => createWorkerRelay(workerRelayEvents) },
5619
7292
  upload: getSimulation().upload.mode === "memory" ? { getUploader: () => ({ rails: [getSimulation().upload.rail] }) } : void 0,
5620
7293
  intent: { isAvailable: () => getSimulation().intent.enabled },
7294
+ link: { isAvailable: () => getSimulation().capabilities.domains.link },
7295
+ common: { isAvailable: () => getSimulation().capabilities.domains.common },
7296
+ lists: { isAvailable: () => getSimulation().capabilities.domains.lists },
7297
+ serial: { isAvailable: () => getSimulation().capabilities.domains.serial },
7298
+ ble: { isAvailable: () => getSimulation().capabilities.domains.ble },
7299
+ webrtc: { isAvailable: () => getSimulation().capabilities.domains.webrtc },
5621
7300
  crypto: {
5622
7301
  verifyEvent: async () => true
5623
7302
  },
@@ -5639,11 +7318,7 @@ function registerFrameForGeneration(bridge, frame, config, generation) {
5639
7318
  }
5640
7319
  function navigateFrame(bridge, frame, config, generation) {
5641
7320
  const windowId = registerFrameForGeneration(bridge, frame, config, generation);
5642
- frame.src = "about:blank";
5643
- window.setTimeout(() => {
5644
- registerFrameForGeneration(bridge, frame, config, generation);
5645
- frame.src = config.target.url;
5646
- }, 0);
7321
+ frame.src = config.target.url;
5647
7322
  return windowId;
5648
7323
  }
5649
7324
  function installPajaHost() {
@@ -5740,4 +7415,12 @@ try {
5740
7415
  if (statusEl) statusEl.textContent = "error";
5741
7416
  console.error(error);
5742
7417
  }
7418
+ /*! Bundled license information:
7419
+
7420
+ @noble/hashes/utils.js:
7421
+ (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
7422
+
7423
+ @scure/base/index.js:
7424
+ (*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
7425
+ */
5743
7426
  //# sourceMappingURL=browser-host.js.map