@kehto/paja 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -262,7 +262,7 @@ function uploadMap(action) {
262
262
  return { senderCap: "upload:write", recipientCap: null };
263
263
  }
264
264
  function intentMap(action) {
265
- if (action === "changed" || action === "deliver" || action === "invoke.result" || action === "available.result" || action === "handlers.result") {
265
+ if (action === "changed" || action === "invoke.result" || action === "available.result" || action === "handlers.result") {
266
266
  return { senderCap: null, recipientCap: "intent:read" };
267
267
  }
268
268
  if (action === "invoke") return { senderCap: "intent:write", recipientCap: null };
@@ -623,7 +623,7 @@ function deserialize2(json) {
623
623
  return defaultConfig();
624
624
  }
625
625
 
626
- // ../../node_modules/.pnpm/@napplet+core@0.29.0/node_modules/@napplet/core/dist/index.js
626
+ // ../../node_modules/.pnpm/@napplet+core@0.31.0/node_modules/@napplet/core/dist/index.js
627
627
  function createDispatch() {
628
628
  const handlers = /* @__PURE__ */ new Map();
629
629
  function registerNap2(domain, handler) {
@@ -1599,17 +1599,29 @@ function isRuntimeOwnedIdentityOrThemeRequest(type) {
1599
1599
  function createRuntimeOwnedResultEnvelope(type, id) {
1600
1600
  return Object.assign({ type }, { id });
1601
1601
  }
1602
+ function intentRequestDetails(message) {
1603
+ const request = message.request;
1604
+ return {
1605
+ archetype: typeof request?.archetype === "string" ? request.archetype : "",
1606
+ action: typeof request?.action === "string" && request.action.length > 0 ? request.action : "open"
1607
+ };
1608
+ }
1609
+ function createIntentInvokeRejection(message, error) {
1610
+ const id = message.id ?? "";
1611
+ const { archetype, action } = intentRequestDetails(message);
1612
+ return {
1613
+ type: "intent.invoke.result",
1614
+ id,
1615
+ result: { ok: false, archetype, action, handled: false, error }
1616
+ };
1617
+ }
1602
1618
  function isIdentityOrThemeMessage(message) {
1603
1619
  return message.type.startsWith("identity.") || message.type.startsWith("theme.");
1604
1620
  }
1605
1621
  function createIntentPolicyDenial(message) {
1606
1622
  const id = message.id ?? "";
1607
1623
  if (message.type === "intent.invoke") {
1608
- return {
1609
- type: "intent.invoke.result",
1610
- id,
1611
- result: { ok: false, error: "invoke rejected" }
1612
- };
1624
+ return createIntentInvokeRejection(message, "invoke rejected");
1613
1625
  }
1614
1626
  if (message.type === "intent.available" || message.type === "intent.handlers") {
1615
1627
  return {
@@ -1620,6 +1632,29 @@ function createIntentPolicyDenial(message) {
1620
1632
  }
1621
1633
  return void 0;
1622
1634
  }
1635
+ function createIntentServiceUnavailableResult(message) {
1636
+ const id = message.id ?? "";
1637
+ if (message.type === "intent.invoke") {
1638
+ return createIntentInvokeRejection(message, "no handler");
1639
+ }
1640
+ if (message.type === "intent.available") {
1641
+ const archetype = message.archetype;
1642
+ return {
1643
+ type: "intent.available.result",
1644
+ id,
1645
+ availability: {
1646
+ archetype: typeof archetype === "string" ? archetype : "",
1647
+ available: false,
1648
+ candidates: [],
1649
+ hasDefault: false
1650
+ }
1651
+ };
1652
+ }
1653
+ if (message.type === "intent.handlers") {
1654
+ return { type: "intent.handlers.result", id, handlers: [] };
1655
+ }
1656
+ return void 0;
1657
+ }
1623
1658
  function createCanonicalDomainResult(message) {
1624
1659
  if (!isRuntimeOwnedIdentityOrThemeRequest(message.type)) return void 0;
1625
1660
  const id = message.id ?? "";
@@ -1766,6 +1801,7 @@ function createIncRuntime(hooks, sessionRegistry2, authorizeChannelOpen) {
1766
1801
  },
1767
1802
  revokeWindow(windowId) {
1768
1803
  revokeWindowChannels(state, hooks, windowId);
1804
+ removeWindowSubscriptions(state, windowId);
1769
1805
  },
1770
1806
  clear() {
1771
1807
  state.subscriptions.clear();
@@ -1851,7 +1887,12 @@ function handleEmit(state, hooks, sessionRegistry2, windowId, m) {
1851
1887
  if (!subscribers) return;
1852
1888
  for (const subscriberWindowId of subscribers) {
1853
1889
  if (subscriberWindowId !== windowId) {
1854
- hooks.sendToNapplet(subscriberWindowId, { type: "inc.event", topic, payload: m.payload, sender });
1890
+ hooks.sendToNapplet(subscriberWindowId, {
1891
+ type: "inc.event",
1892
+ topic,
1893
+ sender,
1894
+ ...m.payload === void 0 ? {} : { payload: m.payload }
1895
+ });
1855
1896
  }
1856
1897
  }
1857
1898
  }
@@ -1914,7 +1955,12 @@ function handleChannelEmit(state, hooks, sessionRegistry2, windowId, m) {
1914
1955
  const peer = peerOf(state, m.channelId ?? "", windowId);
1915
1956
  const sender = sessionRegistry2.getEntryByWindowId(windowId)?.dTag;
1916
1957
  if (peer && sender) {
1917
- hooks.sendToNapplet(peer, { type: "inc.channel.event", channelId: m.channelId ?? "", sender, payload: m.payload });
1958
+ hooks.sendToNapplet(peer, {
1959
+ type: "inc.channel.event",
1960
+ channelId: m.channelId ?? "",
1961
+ sender,
1962
+ ...m.payload === void 0 ? {} : { payload: m.payload }
1963
+ });
1918
1964
  }
1919
1965
  }
1920
1966
  function handleChannelBroadcast(state, hooks, sessionRegistry2, windowId, m) {
@@ -1924,7 +1970,12 @@ function handleChannelBroadcast(state, hooks, sessionRegistry2, windowId, m) {
1924
1970
  for (const channelId of channels) {
1925
1971
  const peer = peerOf(state, channelId, windowId);
1926
1972
  if (peer) {
1927
- hooks.sendToNapplet(peer, { type: "inc.channel.event", channelId, sender, payload: m.payload });
1973
+ hooks.sendToNapplet(peer, {
1974
+ type: "inc.channel.event",
1975
+ channelId,
1976
+ sender,
1977
+ ...m.payload === void 0 ? {} : { payload: m.payload }
1978
+ });
1928
1979
  }
1929
1980
  }
1930
1981
  }
@@ -2250,7 +2301,13 @@ function handleServiceOnlyMessage(context, name, windowId, msg) {
2250
2301
  return;
2251
2302
  }
2252
2303
  const service = context.serviceRegistry[name];
2253
- if (!service) return;
2304
+ if (!service) {
2305
+ if (name === "intent") {
2306
+ const result2 = createIntentServiceUnavailableResult(msg);
2307
+ if (result2) context.hooks.sendToNapplet(windowId, result2);
2308
+ }
2309
+ return;
2310
+ }
2254
2311
  service.handleMessage(windowId, msg, (resp) => context.hooks.sendToNapplet(windowId, resp));
2255
2312
  }
2256
2313
  function createRegisteredServices(serviceRegistry) {
@@ -2476,6 +2533,10 @@ function createRuntimeInstance(context) {
2476
2533
  sessionRegistry2.getAllEntries().map((entry) => entry.windowId)
2477
2534
  );
2478
2535
  },
2536
+ hasCapability(windowId, capability) {
2537
+ const entry = sessionRegistry2.getEntryByWindowId(windowId);
2538
+ return !!entry && aclState.check("", entry.dTag, entry.aggregateHash, capability);
2539
+ },
2479
2540
  sendToEligibleNapplet(windowId, message) {
2480
2541
  const entry = sessionRegistry2.getEntryByWindowId(windowId);
2481
2542
  if (!entry || typeof message.type !== "string") return false;
@@ -3979,9 +4040,14 @@ function nappletNamespacePrelude(domains) {
3979
4040
  function fire(message) {
3980
4041
  post(message);
3981
4042
  }
4043
+ function isConventionTopic(topic) {
4044
+ const pathEnd = topic.search(/[?#]/);
4045
+ const path = pathEnd === -1 ? topic : topic.slice(0, pathEnd);
4046
+ return /^napplet:[^/?#]+\/[^/?#]+$/.test(path);
4047
+ }
3982
4048
  function normalizeConventionUri(topic, hasExplicitPayload) {
3983
4049
  if (typeof topic !== "string") throw new TypeError("INC topic must be text");
3984
- if (!topic.startsWith("napplet:")) return { topic };
4050
+ if (!isConventionTopic(topic)) return { topic };
3985
4051
  if (topic.includes("#")) throw new TypeError("Convention topics cannot contain fragments");
3986
4052
  const queryStart = topic.indexOf("?");
3987
4053
  if (queryStart === -1) {
@@ -3990,7 +4056,7 @@ function nappletNamespacePrelude(domains) {
3990
4056
  if (hasExplicitPayload) {
3991
4057
  throw new TypeError("Convention queries cannot be combined with an explicit payload");
3992
4058
  }
3993
- const values = {};
4059
+ const entries = [];
3994
4060
  const names = /* @__PURE__ */ new Set();
3995
4061
  const query = topic.slice(queryStart + 1);
3996
4062
  if (query) {
@@ -4009,10 +4075,10 @@ function nappletNamespacePrelude(domains) {
4009
4075
  }
4010
4076
  if (names.has(name)) throw new TypeError("Convention query names must be unique");
4011
4077
  names.add(name);
4012
- values[name] = value;
4078
+ entries.push([name, value]);
4013
4079
  }
4014
4080
  }
4015
- return { topic: topic.slice(0, queryStart), payload: values };
4081
+ return { topic: topic.slice(0, queryStart), payload: Object.fromEntries(entries) };
4016
4082
  }
4017
4083
  function resultOrError(msg) {
4018
4084
  return msg;
@@ -4171,7 +4237,7 @@ function nappletNamespacePrelude(domains) {
4171
4237
  const unopenedClosed = /* @__PURE__ */ new Map();
4172
4238
  function stableSubscriptionTopic(topic) {
4173
4239
  if (typeof topic !== "string") throw new TypeError("INC topic must be text");
4174
- if (topic.includes("?") || topic.includes("#")) {
4240
+ if (isConventionTopic(topic) && (topic.includes("?") || topic.includes("#"))) {
4175
4241
  throw new TypeError("INC subscriptions require a queryless topic identity");
4176
4242
  }
4177
4243
  return topic;
@@ -4192,7 +4258,6 @@ function nappletNamespacePrelude(domains) {
4192
4258
  },
4193
4259
  on(handler) {
4194
4260
  if (typeof handler !== "function") throw new TypeError("INC channel event handler must be a function");
4195
- if (state.closed) return subscriptionHandle(() => void 0);
4196
4261
  state.eventHandlers.add(handler);
4197
4262
  const pending = state.pendingEvents.splice(0);
4198
4263
  for (const event of pending) handler(event);
@@ -4862,77 +4927,57 @@ function nappletNamespacePrelude(domains) {
4862
4927
  };
4863
4928
  }
4864
4929
  function makeIntent() {
4865
- const deliveryHandlers = /* @__PURE__ */ new Set();
4866
- const pendingDeliveries = [];
4867
4930
  const hasOwn2 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
4868
- const normalizeDelivery = (value) => {
4869
- if (!value || typeof value !== "object" || typeof value.sender !== "string" || typeof value.archetype !== "string" || typeof value.action !== "string" || typeof value.convention !== "string") {
4870
- return void 0;
4931
+ const normalizeRequest = (value) => {
4932
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
4933
+ throw new TypeError("Intent request must be an object");
4871
4934
  }
4872
- const delivery = value;
4873
- return {
4874
- sender: delivery.sender,
4875
- archetype: delivery.archetype,
4876
- action: delivery.action,
4877
- convention: delivery.convention,
4878
- ...hasOwn2(delivery, "payload") ? { payload: delivery.payload } : {}
4879
- };
4880
- };
4881
- listen((event) => {
4882
- if (!isParentMessage(event)) return;
4883
- const msg = event.data;
4884
- if (typeof msg !== "object" || msg === null || msg.type !== "intent.deliver") return;
4885
- const delivery = normalizeDelivery(msg.delivery);
4886
- if (!delivery) return;
4887
- if (deliveryHandlers.size === 0) pendingDeliveries.push(delivery);
4888
- else for (const handler of deliveryHandlers) handler(delivery);
4889
- });
4890
- function normalizeIntentUri(uri, explicitPayload, actionMustBe) {
4891
- if (typeof uri !== "string" || !uri.startsWith("napplet:")) {
4892
- throw new TypeError("Intent convention URI must start with napplet:");
4935
+ const supplied = value;
4936
+ if (hasOwn2(supplied, "sender")) {
4937
+ throw new TypeError("Intent callers cannot supply sender");
4893
4938
  }
4894
- if (uri.includes("#")) throw new TypeError("Intent convention URI cannot contain fragments");
4895
- const normalized = normalizeConventionUri(uri, explicitPayload);
4896
- const match = /^napplet:([^/?#]+)\/([^/?#]+)$/.exec(normalized.topic);
4897
- if (!match) throw new TypeError("Intent convention URI must be napplet:<archetype>/<intent>");
4898
- if (actionMustBe !== void 0 && match[2] !== actionMustBe) {
4899
- throw new TypeError(`Intent convention URI must use the ${actionMustBe} intent`);
4939
+ const allowed22 = ["archetype", "action", "convention", "payload", "handler", "behavior"];
4940
+ if (Object.keys(supplied).some((field) => !allowed22.includes(field))) {
4941
+ throw new TypeError("Intent request contains unsupported fields");
4900
4942
  }
4901
- return {
4902
- archetype: match[1],
4903
- action: match[2],
4904
- convention: normalized.topic,
4905
- ...normalized.payload === void 0 ? {} : { payload: normalized.payload }
4906
- };
4907
- }
4908
- const invoke = (uri, opts, actionMustBe) => {
4909
- const supplied = opts && typeof opts === "object" ? opts : void 0;
4910
- const explicitPayload = supplied?.payload !== void 0;
4911
- const normalized = normalizeIntentUri(uri, explicitPayload, actionMustBe);
4912
- if (supplied && hasOwn2(supplied, "sender")) {
4913
- throw new TypeError("Intent callers cannot supply sender");
4943
+ if (typeof supplied.archetype !== "string" || supplied.archetype.length === 0) {
4944
+ throw new TypeError("Intent request requires an archetype");
4914
4945
  }
4915
- for (const field of ["archetype", "action", "convention"]) {
4916
- if (supplied && hasOwn2(supplied, field) && supplied[field] !== normalized[field]) {
4917
- throw new TypeError(`Intent options cannot override URI-derived ${field}`);
4918
- }
4946
+ const action = supplied.action === void 0 ? "open" : supplied.action;
4947
+ if (typeof action !== "string" || action.length === 0) {
4948
+ throw new TypeError("Intent action must be a non-empty string");
4949
+ }
4950
+ if (supplied.convention !== void 0 && (typeof supplied.convention !== "string" || !/^napplet:[^/?#\s]+\/[^/?#\s]+$/.test(supplied.convention))) {
4951
+ throw new TypeError("Intent convention must be queryless");
4952
+ }
4953
+ if (supplied.handler !== void 0 && (typeof supplied.handler !== "string" || supplied.handler.length === 0)) {
4954
+ throw new TypeError("Intent handler must be a non-empty string");
4919
4955
  }
4920
4956
  const behaviorValue = supplied?.behavior;
4921
4957
  const behavior = behaviorValue && typeof behaviorValue === "object" ? behaviorValue : void 0;
4958
+ if (behaviorValue !== void 0 && (!behavior || Array.isArray(behaviorValue) || Object.keys(behavior).some((field) => field !== "focus" && field !== "newWindow" && field !== "reuse") || hasOwn2(behavior, "focus") && typeof behavior.focus !== "boolean" || hasOwn2(behavior, "newWindow") && typeof behavior.newWindow !== "boolean" || hasOwn2(behavior, "reuse") && typeof behavior.reuse !== "boolean")) {
4959
+ throw new TypeError("Intent behavior contains unsupported fields");
4960
+ }
4922
4961
  const sanitizedBehavior = {
4923
4962
  ...typeof behavior?.focus === "boolean" ? { focus: behavior.focus } : {},
4963
+ ...typeof behavior?.newWindow === "boolean" ? { newWindow: behavior.newWindow } : {},
4924
4964
  ...typeof behavior?.reuse === "boolean" ? { reuse: behavior.reuse } : {}
4925
4965
  };
4926
- const handler = typeof supplied?.handler === "string" ? supplied.handler : void 0;
4966
+ return {
4967
+ archetype: supplied.archetype,
4968
+ action,
4969
+ ...typeof supplied.convention === "string" ? { convention: supplied.convention } : {},
4970
+ ...hasOwn2(supplied, "payload") ? { payload: supplied.payload } : {},
4971
+ ...typeof supplied.handler === "string" ? { handler: supplied.handler } : {},
4972
+ ...Object.keys(sanitizedBehavior).length === 0 ? {} : { behavior: sanitizedBehavior }
4973
+ };
4974
+ };
4975
+ const invoke = (value) => {
4976
+ const normalized = normalizeRequest(value);
4927
4977
  return request(
4928
4978
  {
4929
4979
  type: "intent.invoke",
4930
- request: {
4931
- ...normalized,
4932
- ...explicitPayload ? { payload: supplied?.payload } : {},
4933
- ...handler === void 0 ? {} : { handler },
4934
- ...Object.keys(sanitizedBehavior).length === 0 ? {} : { behavior: sanitizedBehavior }
4935
- }
4980
+ request: normalized
4936
4981
  },
4937
4982
  "intent.invoke.result",
4938
4983
  (msg) => fieldOrThrow(msg, "result", "intent.invoke.result missing result")
@@ -4940,7 +4985,12 @@ function nappletNamespacePrelude(domains) {
4940
4985
  };
4941
4986
  return {
4942
4987
  invoke,
4943
- open: (uri, opts) => invoke(uri, opts, "open"),
4988
+ open: (archetype, payload, opts) => invoke({
4989
+ archetype,
4990
+ action: "open",
4991
+ ...payload === void 0 ? {} : { payload },
4992
+ ...opts
4993
+ }),
4944
4994
  available: (archetype) => request(
4945
4995
  { type: "intent.available", archetype },
4946
4996
  "intent.available.result",
@@ -4955,14 +5005,7 @@ function nappletNamespacePrelude(domains) {
4955
5005
  "intent.changed",
4956
5006
  callback,
4957
5007
  (msg) => msg.availability
4958
- ),
4959
- onDelivery: (callback) => {
4960
- if (typeof callback !== "function") throw new TypeError("Intent delivery handler must be a function");
4961
- deliveryHandlers.add(callback);
4962
- const pending = pendingDeliveries.splice(0);
4963
- for (const delivery of pending) callback(delivery);
4964
- return subscriptionHandle(() => deliveryHandlers.delete(callback));
4965
- }
5008
+ )
4966
5009
  };
4967
5010
  }
4968
5011
  function makeWebrtc() {
@@ -10741,9 +10784,10 @@ function createOutboxService(options) {
10741
10784
  if (!options || typeof options.router !== "object" || options.router === null) {
10742
10785
  throw new Error("createOutboxService: options.router is required");
10743
10786
  }
10744
- const { router } = options;
10787
+ const { router, getQueryRouter } = options;
10788
+ let runtimeContext;
10745
10789
  const subscriptions = /* @__PURE__ */ new Map();
10746
- function handleQuery(msg, send) {
10790
+ function handleQuery(windowId, msg, send) {
10747
10791
  const m = msg;
10748
10792
  const id = m.id ?? "";
10749
10793
  const filters = normalizeFilters(m.filters);
@@ -10751,7 +10795,8 @@ function createOutboxService(options) {
10751
10795
  send({ type: "outbox.query.result", id, events: [], error: "invalid filter" });
10752
10796
  return;
10753
10797
  }
10754
- void router.query(filters, sanitizeQueryOptions(m.options)).then(
10798
+ const queryRouter = getQueryRouter?.(windowId, runtimeContext) ?? router;
10799
+ void queryRouter.query(filters, sanitizeQueryOptions(m.options)).then(
10755
10800
  (result2) => send({
10756
10801
  type: "outbox.query.result",
10757
10802
  id,
@@ -10828,13 +10873,19 @@ function createOutboxService(options) {
10828
10873
  }
10829
10874
  return {
10830
10875
  descriptor: OUTBOX_DESCRIPTOR,
10876
+ onRegistered(context) {
10877
+ runtimeContext = context;
10878
+ },
10879
+ onUnregistered() {
10880
+ runtimeContext = void 0;
10881
+ },
10831
10882
  handleMessage(windowId, message, send) {
10832
10883
  switch (message.type) {
10833
10884
  case "outbox.getEvent":
10834
10885
  handleGetEvent(router, message, send);
10835
10886
  return;
10836
10887
  case "outbox.query":
10837
- handleQuery(message, send);
10888
+ handleQuery(windowId, message, send);
10838
10889
  return;
10839
10890
  case "outbox.subscribe":
10840
10891
  handleSubscribe2(windowId, message, send);
@@ -11721,14 +11772,17 @@ function hasOwn(value, key) {
11721
11772
  }
11722
11773
  function validateIntentRequest(value) {
11723
11774
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
11724
- return { error: "invalid convention" };
11775
+ return { error: "invalid request" };
11725
11776
  }
11726
11777
  const request = value;
11727
- if (typeof request.archetype !== "string" || request.archetype.length === 0 || typeof request.action !== "string" || request.action.length === 0 || typeof request.convention !== "string" || request.convention.length === 0) {
11728
- return { error: "invalid convention" };
11778
+ if (typeof request.archetype !== "string" || request.archetype.length === 0) {
11779
+ return { error: "invalid request" };
11780
+ }
11781
+ const action = request.action === void 0 ? "open" : request.action;
11782
+ if (typeof action !== "string" || action.length === 0) {
11783
+ return { error: "invalid request" };
11729
11784
  }
11730
- const match = /^napplet:([^/?#\s]+)\/([^/?#\s]+)$/.exec(request.convention);
11731
- if (!match || match[1] !== request.archetype || match[2] !== request.action) {
11785
+ if (request.convention !== void 0 && (typeof request.convention !== "string" || !/^napplet:[^/?#\s]+\/[^/?#\s]+$/.test(request.convention))) {
11732
11786
  return { error: "invalid convention" };
11733
11787
  }
11734
11788
  const allowedKeys = ["archetype", "action", "convention", "payload", "handler", "behavior"];
@@ -11744,19 +11798,20 @@ function validateIntentRequest(value) {
11744
11798
  return { error: "invoke rejected" };
11745
11799
  }
11746
11800
  const record = request.behavior;
11747
- if (Object.keys(record).some((key) => key !== "focus" && key !== "reuse") || hasOwn(record, "focus") && typeof record.focus !== "boolean" || hasOwn(record, "reuse") && typeof record.reuse !== "boolean") {
11801
+ if (Object.keys(record).some((key) => key !== "focus" && key !== "newWindow" && key !== "reuse") || hasOwn(record, "focus") && typeof record.focus !== "boolean" || hasOwn(record, "newWindow") && typeof record.newWindow !== "boolean" || hasOwn(record, "reuse") && typeof record.reuse !== "boolean") {
11748
11802
  return { error: "invoke rejected" };
11749
11803
  }
11750
11804
  behavior = {
11751
11805
  ...record.focus === void 0 ? {} : { focus: record.focus },
11806
+ ...record.newWindow === void 0 ? {} : { newWindow: record.newWindow },
11752
11807
  ...record.reuse === void 0 ? {} : { reuse: record.reuse }
11753
11808
  };
11754
11809
  }
11755
11810
  return {
11756
11811
  request: {
11757
11812
  archetype: request.archetype,
11758
- action: request.action,
11759
- convention: request.convention,
11813
+ action,
11814
+ ...typeof request.convention === "string" ? { convention: request.convention } : {},
11760
11815
  ...hasOwn(request, "payload") ? { payload: request.payload } : {},
11761
11816
  ...typeof request.handler === "string" ? { handler: request.handler } : {},
11762
11817
  ...behavior === void 0 ? {} : { behavior }
@@ -11773,11 +11828,20 @@ function settleResolverCall(call, send, resultType, id, onValue) {
11773
11828
  }
11774
11829
  pending.then((value) => send(onValue(value))).catch((error) => send({ type: resultType, id, error: toErrorMessage4(error) }));
11775
11830
  }
11776
- function rejectInvoke(send, id) {
11831
+ function rejectedResult(request, error) {
11832
+ return {
11833
+ ok: false,
11834
+ archetype: request.archetype,
11835
+ action: request.action ?? "open",
11836
+ handled: false,
11837
+ error
11838
+ };
11839
+ }
11840
+ function rejectInvoke(send, id, request, error = "invoke rejected") {
11777
11841
  send({
11778
11842
  type: "intent.invoke.result",
11779
11843
  id,
11780
- result: { ok: false, error: "invoke rejected" }
11844
+ result: rejectedResult(request, error)
11781
11845
  });
11782
11846
  }
11783
11847
  function createIntentService(options) {
@@ -11792,46 +11856,40 @@ function createIntentService(options) {
11792
11856
  const id = m.id ?? "";
11793
11857
  const validation = validateIntentRequest(m.request);
11794
11858
  if ("error" in validation) {
11795
- send({
11796
- type: "intent.invoke.result",
11859
+ const raw = typeof m.request === "object" && m.request !== null ? m.request : {};
11860
+ rejectInvoke(
11861
+ send,
11797
11862
  id,
11798
- result: { ok: false, error: validation.error }
11799
- });
11863
+ {
11864
+ archetype: typeof raw.archetype === "string" ? raw.archetype : "",
11865
+ action: typeof raw.action === "string" ? raw.action : "open"
11866
+ },
11867
+ validation.error
11868
+ );
11800
11869
  return;
11801
11870
  }
11802
11871
  const sender = runtimeContext?.resolveDTag(windowId);
11803
11872
  if (!sender) {
11804
- rejectInvoke(send, id);
11873
+ rejectInvoke(send, id, validation.request);
11805
11874
  return;
11806
11875
  }
11807
11876
  let pending;
11808
11877
  try {
11809
11878
  pending = Promise.resolve(resolver.invoke(validation.request, { sender }));
11810
11879
  } catch {
11811
- rejectInvoke(send, id);
11880
+ rejectInvoke(send, id, validation.request);
11812
11881
  return;
11813
11882
  }
11814
11883
  void pending.then(
11815
- (outcome) => {
11816
- if (outcome.result.ok && (!("retained" in outcome) || typeof outcome.retained.start !== "function")) {
11817
- rejectInvoke(send, id);
11818
- return;
11819
- }
11884
+ (result2) => {
11820
11885
  send({
11821
11886
  type: "intent.invoke.result",
11822
11887
  id,
11823
- result: outcome.result
11888
+ result: result2
11824
11889
  });
11825
- if (!outcome.result.ok || !("retained" in outcome)) return;
11826
- try {
11827
- const started = outcome.retained.start();
11828
- void Promise.resolve(started).catch(() => {
11829
- });
11830
- } catch {
11831
- }
11832
11890
  },
11833
11891
  () => {
11834
- rejectInvoke(send, id);
11892
+ rejectInvoke(send, id, validation.request);
11835
11893
  }
11836
11894
  ).catch(() => {
11837
11895
  });
@@ -11916,128 +11974,134 @@ function candidatesFor(catalog, archetype, defaultHandler) {
11916
11974
  ...entry.title === void 0 ? {} : { title: entry.title },
11917
11975
  actions: [...support.actions],
11918
11976
  conventions: [...support.conventions],
11919
- contracts: support.contracts.map((contract) => ({
11920
- convention: contract.convention,
11921
- ...contract.eventKinds === void 0 ? {} : { eventKinds: [...contract.eventKinds] }
11922
- })),
11923
11977
  ...entry.dTag === defaultHandler ? { isDefault: true } : {}
11924
11978
  });
11925
11979
  }
11926
11980
  return candidates;
11927
11981
  }
11928
- function reject(error) {
11929
- return { ok: false, error };
11982
+ function reject(request, error) {
11983
+ return {
11984
+ ok: false,
11985
+ archetype: request.archetype,
11986
+ action: request.action ?? "open",
11987
+ handled: false,
11988
+ error
11989
+ };
11990
+ }
11991
+ async function availabilityForCatalog(options, archetype) {
11992
+ const catalog = await options.loadCatalog();
11993
+ const defaultHandler = options.getDefaultHandler?.(archetype);
11994
+ const candidates = candidatesFor(catalog, archetype, defaultHandler);
11995
+ return {
11996
+ archetype,
11997
+ available: candidates.length > 0,
11998
+ candidates,
11999
+ hasDefault: defaultHandler !== void 0 && candidates.some((candidate) => candidate.dTag === defaultHandler)
12000
+ };
12001
+ }
12002
+ async function chooseCompatible(options, request, candidates, sender) {
12003
+ if (!options.chooseHandler) {
12004
+ return request.handler === "choose" ? { error: "user cancelled" } : { error: "invoke rejected" };
12005
+ }
12006
+ const picked = await options.chooseHandler(request.archetype, candidates, sender);
12007
+ if (picked === void 0) return { error: "user cancelled" };
12008
+ const candidate = candidates.find((item) => item.dTag === picked);
12009
+ return candidate ? { candidate } : { error: "invoke rejected" };
12010
+ }
12011
+ async function pickHandler(options, request, compatible, sender) {
12012
+ const preference = request.handler;
12013
+ if (typeof preference === "string" && preference !== "default" && preference !== "choose") {
12014
+ const candidate = compatible.find((item) => item.dTag === preference);
12015
+ if (!candidate || !options.authorizeExplicitHandler) return { error: "invoke rejected" };
12016
+ let authorized = false;
12017
+ try {
12018
+ authorized = await options.authorizeExplicitHandler(
12019
+ sender,
12020
+ preference,
12021
+ request,
12022
+ candidate
12023
+ );
12024
+ } catch {
12025
+ return { error: "invoke rejected" };
12026
+ }
12027
+ return authorized ? { candidate } : { error: "invoke rejected" };
12028
+ }
12029
+ if (preference === "choose") {
12030
+ return chooseCompatible(options, request, compatible, sender);
12031
+ }
12032
+ const defaultCandidate = compatible.find((candidate) => candidate.isDefault === true);
12033
+ if (preference === "default") {
12034
+ return defaultCandidate ? { candidate: defaultCandidate } : { error: "invoke rejected" };
12035
+ }
12036
+ if (defaultCandidate) return { candidate: defaultCandidate };
12037
+ if (compatible.length === 1) return { candidate: compatible[0] };
12038
+ return chooseCompatible(options, request, compatible, sender);
12039
+ }
12040
+ async function invokeFromCatalog(options, request, context) {
12041
+ const catalog = await options.loadCatalog();
12042
+ const defaultHandler = options.getDefaultHandler?.(request.archetype);
12043
+ const candidates = candidatesFor(catalog, request.archetype, defaultHandler);
12044
+ if (candidates.length === 0) return reject(request, "no handler");
12045
+ const action = request.action ?? "open";
12046
+ const actionCompatible = candidates.filter((candidate) => candidate.actions.includes(action));
12047
+ if (actionCompatible.length === 0) return reject(request, "unsupported action");
12048
+ const compatible = request.convention === void 0 ? actionCompatible : actionCompatible.filter((candidate) => candidate.conventions.includes(request.convention));
12049
+ if (compatible.length === 0) return reject(request, "unsupported convention");
12050
+ const sender = context.sender;
12051
+ if (typeof sender !== "string" || sender.length === 0) {
12052
+ return reject(request, "invoke rejected");
12053
+ }
12054
+ const selected = await pickHandler(options, request, compatible, sender);
12055
+ if ("error" in selected) return reject(request, selected.error);
12056
+ const convention = request.convention ?? selected.candidate.conventions.find((value) => {
12057
+ const match = /^napplet:[^/?#\s]+\/([^/?#\s]+)$/.exec(value);
12058
+ return match?.[1] === action;
12059
+ });
12060
+ if (!convention) return reject(request, "unsupported convention");
12061
+ const behavior = request.behavior === void 0 ? void 0 : Object.freeze({
12062
+ ...request.behavior.focus === void 0 ? {} : { focus: request.behavior.focus },
12063
+ ...request.behavior.newWindow === void 0 ? {} : { newWindow: request.behavior.newWindow },
12064
+ ...request.behavior.reuse === void 0 ? {} : { reuse: request.behavior.reuse }
12065
+ });
12066
+ const params = Object.freeze({
12067
+ handler: selected.candidate.dTag,
12068
+ sender,
12069
+ archetype: request.archetype,
12070
+ action,
12071
+ convention,
12072
+ ...request.payload === void 0 ? {} : { payload: request.payload },
12073
+ ...behavior === void 0 ? {} : { behavior }
12074
+ });
12075
+ let target;
12076
+ try {
12077
+ target = await options.targets.dispatch(params);
12078
+ } catch {
12079
+ return reject(request, "invoke failed");
12080
+ }
12081
+ if (!target || typeof target.windowId !== "string" || target.windowId.length === 0) {
12082
+ return reject(request, "invoke failed");
12083
+ }
12084
+ return {
12085
+ ok: true,
12086
+ archetype: request.archetype,
12087
+ action,
12088
+ handled: true,
12089
+ handler: selected.candidate.dTag,
12090
+ windowId: target.windowId,
12091
+ convention
12092
+ };
11930
12093
  }
11931
12094
  function createCatalogIntentResolver(options) {
11932
12095
  if (!options || typeof options.loadCatalog !== "function") {
11933
12096
  throw new Error("createCatalogIntentResolver: options.loadCatalog is required");
11934
12097
  }
11935
- if (!options.targets || typeof options.targets.retain !== "function") {
12098
+ if (!options.targets || typeof options.targets.dispatch !== "function") {
11936
12099
  throw new Error("createCatalogIntentResolver: options.targets is required");
11937
12100
  }
11938
- const {
11939
- loadCatalog,
11940
- targets,
11941
- getDefaultHandler,
11942
- chooseHandler,
11943
- authorizeExplicitHandler
11944
- } = options;
11945
12101
  const listeners = /* @__PURE__ */ new Set();
11946
- async function availabilityFor(archetype) {
11947
- const catalog = await loadCatalog();
11948
- const defaultHandler = getDefaultHandler?.(archetype);
11949
- const candidates = candidatesFor(catalog, archetype, defaultHandler);
11950
- return {
11951
- archetype,
11952
- available: candidates.length > 0,
11953
- candidates,
11954
- hasDefault: defaultHandler !== void 0 && candidates.some((candidate) => candidate.dTag === defaultHandler)
11955
- };
11956
- }
11957
- async function chooseCompatible(request, candidates, sender) {
11958
- if (!chooseHandler) {
11959
- return request.handler === "choose" ? { error: "user cancelled" } : { error: "invoke rejected" };
11960
- }
11961
- const picked = await chooseHandler(request.archetype, candidates, sender);
11962
- if (picked === void 0) return { error: "user cancelled" };
11963
- const candidate = candidates.find((item) => item.dTag === picked);
11964
- return candidate ? { candidate } : { error: "invoke rejected" };
11965
- }
11966
- async function pickHandler(request, compatible, sender) {
11967
- const preference = request.handler;
11968
- if (typeof preference === "string" && preference !== "default" && preference !== "choose") {
11969
- const candidate = compatible.find((item) => item.dTag === preference);
11970
- if (!candidate || !authorizeExplicitHandler) return { error: "invoke rejected" };
11971
- let authorized = false;
11972
- try {
11973
- authorized = await authorizeExplicitHandler(sender, preference, request, candidate);
11974
- } catch {
11975
- return { error: "invoke rejected" };
11976
- }
11977
- return authorized ? { candidate } : { error: "invoke rejected" };
11978
- }
11979
- if (preference === "choose") {
11980
- return chooseCompatible(request, compatible, sender);
11981
- }
11982
- const defaultCandidate = compatible.find((candidate) => candidate.isDefault === true);
11983
- if (preference === "default") {
11984
- return defaultCandidate ? { candidate: defaultCandidate } : { error: "invoke rejected" };
11985
- }
11986
- if (defaultCandidate) return { candidate: defaultCandidate };
11987
- if (compatible.length === 1) return { candidate: compatible[0] };
11988
- return chooseCompatible(request, compatible, sender);
11989
- }
11990
- async function invoke(request, context) {
11991
- const catalog = await loadCatalog();
11992
- const defaultHandler = getDefaultHandler?.(request.archetype);
11993
- const candidates = candidatesFor(catalog, request.archetype, defaultHandler);
11994
- if (candidates.length === 0) return { result: reject("no handler") };
11995
- const compatible = candidates.filter((candidate) => candidate.contracts.some((contract) => contract.convention === request.convention));
11996
- if (compatible.length === 0) return { result: reject("unsupported convention") };
11997
- const sender = context.sender;
11998
- if (typeof sender !== "string" || sender.length === 0) {
11999
- return { result: reject("invoke rejected") };
12000
- }
12001
- const selected = await pickHandler(request, compatible, sender);
12002
- if ("error" in selected) return { result: reject(selected.error) };
12003
- const delivery = Object.freeze({
12004
- sender,
12005
- archetype: request.archetype,
12006
- action: request.action,
12007
- convention: request.convention,
12008
- ...request.payload === void 0 ? {} : { payload: request.payload }
12009
- });
12010
- const behavior = request.behavior === void 0 ? void 0 : Object.freeze({
12011
- ...request.behavior.focus === void 0 ? {} : { focus: request.behavior.focus },
12012
- ...request.behavior.reuse === void 0 ? {} : { reuse: request.behavior.reuse }
12013
- });
12014
- const params = Object.freeze({
12015
- handler: selected.candidate.dTag,
12016
- delivery,
12017
- ...behavior === void 0 ? {} : { behavior }
12018
- });
12019
- let retained;
12020
- try {
12021
- retained = await targets.retain(params);
12022
- } catch {
12023
- return { result: reject("invoke rejected") };
12024
- }
12025
- if (!retained || typeof retained.start !== "function") {
12026
- return { result: reject("invoke rejected") };
12027
- }
12028
- return {
12029
- result: {
12030
- ok: true,
12031
- archetype: request.archetype,
12032
- action: request.action,
12033
- convention: request.convention,
12034
- handler: selected.candidate.dTag
12035
- },
12036
- retained
12037
- };
12038
- }
12102
+ const availabilityFor = (archetype) => availabilityForCatalog(options, archetype);
12039
12103
  async function handlers() {
12040
- const catalog = await loadCatalog();
12104
+ const catalog = await options.loadCatalog();
12041
12105
  const archetypes = /* @__PURE__ */ new Set();
12042
12106
  for (const entry of catalog) {
12043
12107
  for (const slug of Object.keys(entry.archetypes)) archetypes.add(slug);
@@ -12045,7 +12109,7 @@ function createCatalogIntentResolver(options) {
12045
12109
  return Promise.all([...archetypes].map((archetype) => availabilityFor(archetype)));
12046
12110
  }
12047
12111
  return {
12048
- invoke,
12112
+ invoke: (request, context) => invokeFromCatalog(options, request, context),
12049
12113
  available: availabilityFor,
12050
12114
  handlers,
12051
12115
  onChanged(listener) {
@@ -12060,33 +12124,24 @@ function createCatalogIntentResolver(options) {
12060
12124
  }
12061
12125
  };
12062
12126
  }
12063
- function actionFromConvention(slug, convention) {
12064
- if (!/^[a-z0-9][a-z0-9-]*$/.test(slug)) {
12065
- throw new TypeError("manifest archetype slug is invalid");
12066
- }
12127
+ function actionFromConvention(convention) {
12067
12128
  const match = /^napplet:([^/?#\s]+)\/([^/?#\s]+)$/.exec(convention);
12068
- if (!match || match[1] !== slug) {
12069
- throw new TypeError("manifest archetype convention is invalid or mismatched");
12129
+ if (!match) {
12130
+ throw new TypeError("manifest archetype convention is invalid");
12070
12131
  }
12071
12132
  return match[2];
12072
12133
  }
12073
12134
  function manifestToIntentCatalogEntry(manifest) {
12074
12135
  const archetypes = {};
12075
- for (const { slug, convention, eventKinds } of manifest.archetypes) {
12076
- const action = actionFromConvention(slug, convention);
12077
- if (eventKinds?.some((kind) => !Number.isSafeInteger(kind) || kind < 0)) {
12078
- throw new TypeError("manifest archetype event kinds must be unsigned safe integers");
12136
+ for (const { slug, convention } of manifest.archetypes) {
12137
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(slug)) {
12138
+ throw new TypeError("manifest archetype slug is invalid");
12079
12139
  }
12140
+ const action = actionFromConvention(convention);
12080
12141
  const support = archetypes[slug] ??= {
12081
12142
  actions: [],
12082
- conventions: [],
12083
- contracts: []
12143
+ conventions: []
12084
12144
  };
12085
- const contract = {
12086
- convention,
12087
- ...eventKinds === void 0 || eventKinds.length === 0 ? {} : { eventKinds: [...eventKinds] }
12088
- };
12089
- support.contracts.push(contract);
12090
12145
  if (!support.actions.includes(action)) support.actions.push(action);
12091
12146
  if (!support.conventions.includes(convention)) support.conventions.push(convention);
12092
12147
  }
@@ -13095,25 +13150,13 @@ var BrowserIntentController = class {
13095
13150
  }
13096
13151
  options;
13097
13152
  maxAttempts;
13098
- retain(params) {
13099
- const retained = freezeRetention(params);
13100
- let started;
13101
- let delivered = false;
13102
- return {
13103
- start: () => {
13104
- started ??= this.start(retained, () => delivered, () => {
13105
- delivered = true;
13106
- });
13107
- return started;
13108
- }
13109
- };
13110
- }
13111
- async start(params, isDelivered, markDelivered) {
13153
+ async dispatch(params) {
13154
+ const dispatch2 = freezeDispatch(params);
13112
13155
  let reason = "no-current-target";
13113
13156
  for (let attempt = 1; attempt <= this.maxAttempts; attempt += 1) {
13114
13157
  let generation;
13115
13158
  try {
13116
- generation = await this.options.openOrReuse(params, attempt);
13159
+ generation = await this.options.openOrReuse(dispatch2, attempt);
13117
13160
  } catch {
13118
13161
  reason = "open-failed";
13119
13162
  continue;
@@ -13132,17 +13175,18 @@ var BrowserIntentController = class {
13132
13175
  reason = "no-current-target";
13133
13176
  continue;
13134
13177
  }
13135
- if (isDelivered()) return;
13136
- markDelivered();
13137
13178
  try {
13138
- await this.options.send(generation, params.delivery);
13139
- return;
13179
+ await this.options.send(generation, dispatch2);
13180
+ const windowId = this.options.getWindowId(generation);
13181
+ if (!windowId) throw new Error("intent target window is unavailable");
13182
+ return { windowId };
13140
13183
  } catch {
13141
- this.options.onTerminal?.(params, "send-failed");
13142
- return;
13184
+ this.options.onTerminal?.(dispatch2, "send-failed");
13185
+ throw new Error("intent target send failed");
13143
13186
  }
13144
13187
  }
13145
- this.options.onTerminal?.(params, reason);
13188
+ this.options.onTerminal?.(dispatch2, reason);
13189
+ throw new Error(`intent target ${reason}`);
13146
13190
  }
13147
13191
  };
13148
13192
  function normalizeAttempts(value) {
@@ -13150,15 +13194,22 @@ function normalizeAttempts(value) {
13150
13194
  if (!Number.isFinite(value)) throw new TypeError("maxAttempts must be finite");
13151
13195
  return Math.min(MAX_INTENT_DELIVERY_ATTEMPTS, Math.max(1, Math.floor(value)));
13152
13196
  }
13153
- function freezeRetention(params) {
13197
+ function freezeDispatch(params) {
13154
13198
  if (!params || typeof params.handler !== "string" || params.handler.length === 0) {
13155
- throw new TypeError("Intent retention requires a handler");
13199
+ throw new TypeError("Intent dispatch requires a handler");
13156
13200
  }
13157
- const delivery = freezeValue({ ...params.delivery });
13201
+ if (typeof params.sender !== "string" || params.sender.length === 0 || typeof params.archetype !== "string" || params.archetype.length === 0 || typeof params.action !== "string" || params.action.length === 0 || typeof params.convention !== "string" || params.convention.length === 0) {
13202
+ throw new TypeError("Intent dispatch requires canonical routing fields");
13203
+ }
13204
+ const payload = freezeValue(params.payload);
13158
13205
  const behavior = params.behavior === void 0 ? void 0 : freezeValue({ ...params.behavior });
13159
13206
  return Object.freeze({
13160
13207
  handler: params.handler,
13161
- delivery,
13208
+ sender: params.sender,
13209
+ archetype: params.archetype,
13210
+ action: params.action,
13211
+ convention: params.convention,
13212
+ ...params.payload === void 0 ? {} : { payload },
13162
13213
  ...behavior === void 0 ? {} : { behavior }
13163
13214
  });
13164
13215
  }
@@ -13192,10 +13243,10 @@ var InstalledNappletCatalog = class {
13192
13243
  aggregateHash: resolved.aggregateHash,
13193
13244
  pointer: copyPointer(resolved.pointer),
13194
13245
  ...resolved.manifest.title === void 0 ? {} : { title: resolved.manifest.title },
13246
+ requires: [...resolved.manifest.requires],
13195
13247
  archetypes: resolved.manifest.archetypes.map((archetype) => ({
13196
13248
  slug: archetype.slug,
13197
- convention: archetype.convention,
13198
- ...archetype.eventKinds === void 0 ? {} : { eventKinds: [...archetype.eventKinds] }
13249
+ convention: archetype.convention
13199
13250
  }))
13200
13251
  });
13201
13252
  this.records.set(record.dTag, record);
@@ -13231,13 +13282,12 @@ var InstalledNappletCatalog = class {
13231
13282
  }
13232
13283
  /** Return exact handler candidates derived only from installed manifests. */
13233
13284
  intentCatalog() {
13234
- return this.installed().map((record) => manifestToIntentCatalogEntry({
13285
+ return this.installed().filter((record) => record.requires.includes("inc")).map((record) => manifestToIntentCatalogEntry({
13235
13286
  dTag: record.dTag,
13236
13287
  ...record.title === void 0 ? {} : { title: record.title },
13237
13288
  archetypes: record.archetypes.map((archetype) => ({
13238
13289
  slug: archetype.slug,
13239
- convention: archetype.convention,
13240
- ...archetype.eventKinds === void 0 ? {} : { eventKinds: [...archetype.eventKinds] }
13290
+ convention: archetype.convention
13241
13291
  }))
13242
13292
  }));
13243
13293
  }
@@ -13273,10 +13323,8 @@ function copyPointer(pointer) {
13273
13323
  function freezeRecord(record) {
13274
13324
  return Object.freeze({
13275
13325
  ...record,
13276
- archetypes: Object.freeze(record.archetypes.map((archetype) => Object.freeze({
13277
- ...archetype,
13278
- ...archetype.eventKinds === void 0 ? {} : { eventKinds: Object.freeze([...archetype.eventKinds]) }
13279
- })))
13326
+ requires: Object.freeze([...record.requires]),
13327
+ archetypes: Object.freeze(record.archetypes.map((archetype) => Object.freeze({ ...archetype })))
13280
13328
  });
13281
13329
  }
13282
13330
 
@@ -14528,7 +14576,9 @@ var SimplePool = class extends AbstractSimplePool {
14528
14576
  // src/browser-relay-runtime.ts
14529
14577
  var PAJA_NIP65_RELAY_LIST_KIND = 10002;
14530
14578
  var PAJA_CONTACT_LIST_KIND = 3;
14579
+ var PAJA_CONTACT_LIST_CANDIDATE_LIMIT = 64;
14531
14580
  var PAJA_LIVE_QUERY_WAIT_MS = 4e3;
14581
+ var contactListCandidateQueries = /* @__PURE__ */ new WeakMap();
14532
14582
  function matchesFilter2(event, filter) {
14533
14583
  const ids = filter.ids;
14534
14584
  if (ids && !ids.some((id) => event.id === id || event.id.startsWith(id))) return false;
@@ -14588,14 +14638,54 @@ function subscribeLive(pool, relayUrls, filters, next) {
14588
14638
  }
14589
14639
  };
14590
14640
  }
14591
- async function queryLive(pool, relayUrls, filters, maxWaitMs = PAJA_LIVE_QUERY_WAIT_MS) {
14641
+ function queryLiveBounded(pool, relayUrls, filter, maxWaitMs, limit, signal) {
14642
+ if (limit === 0 || signal?.aborted) return Promise.resolve([]);
14643
+ return new Promise((resolve) => {
14644
+ const events = [];
14645
+ let settled = false;
14646
+ let closeRequested = false;
14647
+ let closeReason = "paja query limit reached";
14648
+ let subscription;
14649
+ let abort;
14650
+ const finish = () => {
14651
+ if (settled) return;
14652
+ settled = true;
14653
+ if (abort) signal?.removeEventListener("abort", abort);
14654
+ resolve(events);
14655
+ };
14656
+ const close = (reason = closeReason) => {
14657
+ closeRequested = true;
14658
+ closeReason = reason;
14659
+ if (!subscription) return;
14660
+ void Promise.resolve(subscription.close(reason)).catch(() => {
14661
+ });
14662
+ finish();
14663
+ };
14664
+ abort = () => close("paja query aborted");
14665
+ signal?.addEventListener("abort", abort, { once: true });
14666
+ subscription = pool.subscribeEose(relayUrls, filter, {
14667
+ label: "kehto-paja-runtime",
14668
+ maxWait: maxWaitMs,
14669
+ onevent(event) {
14670
+ if (events.length >= limit) return;
14671
+ events.push(event);
14672
+ if (events.length === limit) close();
14673
+ },
14674
+ onclose: finish
14675
+ });
14676
+ if (closeRequested) close();
14677
+ });
14678
+ }
14679
+ async function queryLive(pool, relayUrls, filters, maxWaitMs = PAJA_LIVE_QUERY_WAIT_MS, collectUntilLimit = false, signal) {
14592
14680
  const activeFilters = filters.length > 0 ? filters : [{}];
14593
- const batches = await Promise.all(activeFilters.map(
14594
- (filter) => pool.querySync(relayUrls, filter, {
14681
+ const batches = await Promise.all(activeFilters.map((filter) => {
14682
+ const limit = typeof filter.limit === "number" && filter.limit >= 0 ? filter.limit : void 0;
14683
+ if (collectUntilLimit && limit !== void 0) return queryLiveBounded(pool, relayUrls, filter, maxWaitMs, limit, signal);
14684
+ return pool.querySync(relayUrls, filter, {
14595
14685
  label: "kehto-paja-runtime",
14596
14686
  maxWait: maxWaitMs
14597
- })
14598
- ));
14687
+ });
14688
+ }));
14599
14689
  const out = /* @__PURE__ */ new Map();
14600
14690
  for (const event of batches.flat()) out.set(event.id, event);
14601
14691
  return [...out.values()].sort((a, b) => b.created_at - a.created_at);
@@ -14616,6 +14706,14 @@ function createPajaRelayBackend(getSimulation, confirmRequest, livePool = new Si
14616
14706
  for (const event of [...memoryEvents, ...liveEvents]) out.set(event.id, event);
14617
14707
  return [...out.values()].sort((a, b) => b.created_at - a.created_at);
14618
14708
  };
14709
+ const queryContactListCandidates = async (relayUrls, filters, maxWaitMs, signal) => {
14710
+ const memoryEvents = collectMemoryEvents(events, filters);
14711
+ if (signal?.aborted || getSimulation().relay.mode !== "live" || relayUrls.length === 0) return memoryEvents;
14712
+ const liveEvents = await queryLive(livePool, relayUrls, filters, maxWaitMs, true, signal);
14713
+ const out = /* @__PURE__ */ new Map();
14714
+ for (const event of [...memoryEvents, ...liveEvents]) out.set(event.id, event);
14715
+ return [...out.values()].sort((a, b) => b.created_at - a.created_at);
14716
+ };
14619
14717
  function retainPublishedEvent(event) {
14620
14718
  events.push(event);
14621
14719
  for (const subscriber of subscribers) {
@@ -14691,6 +14789,7 @@ function createPajaRelayBackend(getSimulation, confirmRequest, livePool = new Si
14691
14789
  livePool.destroy();
14692
14790
  }
14693
14791
  };
14792
+ contactListCandidateQueries.set(backend, queryContactListCandidates);
14694
14793
  return backend;
14695
14794
  }
14696
14795
  function toNostrEvent(value) {
@@ -14730,19 +14829,25 @@ async function getBootstrapRelayUrls(getSimulation, signerProvider) {
14730
14829
  ...await getSignerRelayUrls(signerProvider, "read")
14731
14830
  ]);
14732
14831
  }
14832
+ function createPajaContactListLoader(backend, getSimulation, signerProvider) {
14833
+ return async (pubkey, signal) => {
14834
+ if (!/^[0-9a-fA-F]{64}$/.test(pubkey) || signal?.aborted) return [];
14835
+ const relayUrls = await getBootstrapRelayUrls(getSimulation, signerProvider);
14836
+ if (signal?.aborted) return [];
14837
+ const filters = [{
14838
+ kinds: [PAJA_CONTACT_LIST_KIND],
14839
+ authors: [pubkey],
14840
+ limit: PAJA_CONTACT_LIST_CANDIDATE_LIMIT
14841
+ }];
14842
+ const contactQuery = contactListCandidateQueries.get(backend);
14843
+ const candidates = contactQuery ? await contactQuery(relayUrls, filters, PAJA_LIVE_QUERY_WAIT_MS, signal) : await backend.query(relayUrls, filters, PAJA_LIVE_QUERY_WAIT_MS);
14844
+ if (signal?.aborted) return [];
14845
+ return candidates.filter((event) => event.kind === PAJA_CONTACT_LIST_KIND && event.pubkey.toLowerCase() === pubkey.toLowerCase()).sort((left, right) => right.created_at - left.created_at || left.id.localeCompare(right.id)).slice(0, PAJA_CONTACT_LIST_CANDIDATE_LIMIT);
14846
+ };
14847
+ }
14733
14848
  function latestEvent(events, kind, pubkey) {
14734
14849
  return events.filter((event) => event.kind === kind && event.pubkey === pubkey).sort((a, b) => b.created_at - a.created_at)[0];
14735
14850
  }
14736
- function contactPubkeys(event) {
14737
- if (!event) return [];
14738
- const out = /* @__PURE__ */ new Set();
14739
- for (const tag of event.tags) {
14740
- if (tag[0] === "p" && typeof tag[1] === "string" && /^[0-9a-fA-F]{64}$/.test(tag[1])) {
14741
- out.add(tag[1].toLowerCase());
14742
- }
14743
- }
14744
- return [...out];
14745
- }
14746
14851
  function createPajaRelayListLoader(backend, getSimulation, signerProvider) {
14747
14852
  const registry2 = createNip65Registry();
14748
14853
  return async (pubkeys) => {
@@ -14785,24 +14890,210 @@ function createPajaOutboxRelayPool(backend) {
14785
14890
  }
14786
14891
  };
14787
14892
  }
14788
- function createPajaIdentityProviders(backend, getSimulation, signerProvider) {
14789
- const followsCache = /* @__PURE__ */ new Map();
14893
+
14894
+ // src/browser-social-cache.ts
14895
+ var HEX_PUBKEY = /^[0-9a-f]{64}$/i;
14896
+ var FOLLOW_PARSE_YIELD_EVERY = 64;
14897
+ var PROFILE_WARM_YIELD_EVERY = 64;
14898
+ function createPajaSocialCache(options) {
14899
+ let generation = 0;
14900
+ let activeRefreshController;
14901
+ const snapshots = /* @__PURE__ */ new Map();
14902
+ async function loadFollows(capturedPubkey) {
14903
+ const normalizedPubkey = normalizePubkey(capturedPubkey);
14904
+ if (!normalizedPubkey) return [];
14905
+ const existing = snapshots.get(normalizedPubkey);
14906
+ if (existing) return [...existing.follows];
14907
+ const capturedGeneration = generation;
14908
+ const follows = await verifiedFollows(normalizedPubkey);
14909
+ const warmed = snapshots.get(normalizedPubkey);
14910
+ if (warmed) return [...warmed.follows];
14911
+ if (generation === capturedGeneration) {
14912
+ snapshots.set(normalizedPubkey, { follows, profiles: /* @__PURE__ */ new Map() });
14913
+ }
14914
+ return [...follows];
14915
+ }
14916
+ async function verifiedFollows(capturedPubkey, isStillCurrent, signal) {
14917
+ const candidates = await options.loadContactList(capturedPubkey, signal);
14918
+ const verified = [];
14919
+ for (const candidate of candidates) {
14920
+ if (isStillCurrent && !isStillCurrent()) return [];
14921
+ if (!isContactCandidate(candidate, capturedPubkey)) continue;
14922
+ try {
14923
+ if (await options.verifyEvent(candidate)) verified.push(candidate);
14924
+ } catch {
14925
+ }
14926
+ }
14927
+ return contactPubkeys(selectReplacement(verified), isStillCurrent);
14928
+ }
14929
+ async function refreshActiveIdentity() {
14930
+ activeRefreshController?.abort();
14931
+ activeRefreshController = void 0;
14932
+ const currentGeneration = ++generation;
14933
+ const capturedPubkey = normalizePubkey(options.getActivePubkey() ?? "");
14934
+ if (!capturedPubkey) return;
14935
+ const controller = new AbortController();
14936
+ activeRefreshController = controller;
14937
+ const isStillCurrent = () => !controller.signal.aborted && isCurrent(capturedPubkey, currentGeneration);
14938
+ try {
14939
+ const follows = await verifiedFollows(capturedPubkey, isStillCurrent, controller.signal);
14940
+ if (!isStillCurrent()) return;
14941
+ const snapshot = { follows, profiles: /* @__PURE__ */ new Map() };
14942
+ snapshots.set(capturedPubkey, snapshot);
14943
+ for (let index = 0; index < follows.length; index += 1) {
14944
+ if (!isStillCurrent()) return;
14945
+ const author = follows[index];
14946
+ let warmed;
14947
+ try {
14948
+ warmed = await options.baseRouter.query(
14949
+ [{ kinds: [0], authors: [author], limit: 1 }],
14950
+ { authors: [author], limit: 1 }
14951
+ );
14952
+ } catch {
14953
+ }
14954
+ if (!isStillCurrent()) return;
14955
+ if (warmed) {
14956
+ const profile = profileResult(warmed, author);
14957
+ if (profile) snapshot.profiles.set(author, profile);
14958
+ }
14959
+ if ((index + 1) % PROFILE_WARM_YIELD_EVERY === 0 && index + 1 < follows.length) {
14960
+ if (!isStillCurrent()) return;
14961
+ await yieldToEventLoop();
14962
+ if (!isStillCurrent()) return;
14963
+ }
14964
+ }
14965
+ } catch {
14966
+ } finally {
14967
+ if (activeRefreshController === controller) activeRefreshController = void 0;
14968
+ }
14969
+ }
14970
+ function isCurrent(capturedPubkey, currentGeneration) {
14971
+ return generation === currentGeneration && normalizePubkey(options.getActivePubkey() ?? "") === capturedPubkey;
14972
+ }
14973
+ function decorate(router, canReadIdentity) {
14974
+ return {
14975
+ ...router.getEvent ? { getEvent: router.getEvent.bind(router) } : {},
14976
+ async query(filters, queryOptions) {
14977
+ const canAugmentAtStart = canReadIdentity?.() ?? true;
14978
+ const activePubkey = normalizePubkey(options.getActivePubkey() ?? "");
14979
+ const snapshot = canAugmentAtStart && activePubkey ? snapshots.get(activePubkey) : void 0;
14980
+ const base = await router.query(filters, queryOptions);
14981
+ if (!canAugmentAtStart || !(canReadIdentity?.() ?? true)) return base;
14982
+ const cached = matchingCachedProfiles(snapshot?.profiles, filters);
14983
+ return mergeResult(base, cached, filters, queryOptions);
14984
+ },
14985
+ subscribe: router.subscribe.bind(router),
14986
+ publish: router.publish.bind(router),
14987
+ resolveRelays: router.resolveRelays.bind(router)
14988
+ };
14989
+ }
14990
+ const unsubscribe = options.subscribeSignerChange?.(() => {
14991
+ void refreshActiveIdentity();
14992
+ });
14790
14993
  return {
14791
- async getFollows(pubkey) {
14792
- if (!/^[0-9a-fA-F]{64}$/.test(pubkey)) return [];
14793
- const cached = followsCache.get(pubkey);
14794
- if (cached) return [...cached];
14795
- const events = await backend.query(await getBootstrapRelayUrls(getSimulation, signerProvider), [{
14796
- kinds: [PAJA_CONTACT_LIST_KIND],
14797
- authors: [pubkey],
14798
- limit: 1
14799
- }], PAJA_LIVE_QUERY_WAIT_MS);
14800
- const follows = contactPubkeys(latestEvent(events, PAJA_CONTACT_LIST_KIND, pubkey));
14801
- followsCache.set(pubkey, follows);
14802
- return [...follows];
14994
+ getFollows: loadFollows,
14995
+ refreshActiveIdentity,
14996
+ decorate,
14997
+ dispose: () => {
14998
+ activeRefreshController?.abort();
14999
+ unsubscribe?.();
14803
15000
  }
14804
15001
  };
14805
15002
  }
15003
+ function normalizePubkey(value) {
15004
+ return typeof value === "string" && HEX_PUBKEY.test(value) ? value.toLowerCase() : null;
15005
+ }
15006
+ function isContactCandidate(candidate, capturedPubkey) {
15007
+ if (typeof candidate !== "object" || candidate === null) return false;
15008
+ const event = candidate;
15009
+ return event.kind === 3 && normalizePubkey(event.pubkey) === capturedPubkey;
15010
+ }
15011
+ function selectReplacement(events) {
15012
+ return [...events].sort((left, right) => {
15013
+ const timestampOrder = right.created_at - left.created_at;
15014
+ if (timestampOrder !== 0) return timestampOrder;
15015
+ if (left.id === right.id) return 0;
15016
+ return left.id < right.id ? -1 : 1;
15017
+ })[0];
15018
+ }
15019
+ async function contactPubkeys(event, isStillCurrent) {
15020
+ if (!event || !Array.isArray(event.tags)) return [];
15021
+ const follows = /* @__PURE__ */ new Set();
15022
+ for (let index = 0; index < event.tags.length; index += 1) {
15023
+ if (isStillCurrent && !isStillCurrent()) return [];
15024
+ const tag = event.tags[index];
15025
+ if (Array.isArray(tag) && tag[0] === "p") {
15026
+ const pubkey = normalizePubkey(tag[1]);
15027
+ if (pubkey) follows.add(pubkey);
15028
+ }
15029
+ if ((index + 1) % FOLLOW_PARSE_YIELD_EVERY === 0 && index + 1 < event.tags.length) {
15030
+ await yieldToEventLoop();
15031
+ if (isStillCurrent && !isStillCurrent()) return [];
15032
+ }
15033
+ }
15034
+ return [...follows];
15035
+ }
15036
+ function profileResult(result2, author) {
15037
+ let newest;
15038
+ for (const entry of result2.events) {
15039
+ if (entry.event.kind !== 0 || normalizePubkey(entry.event.pubkey) !== author) continue;
15040
+ if (!newest || isNewerProfile(entry, newest)) newest = entry;
15041
+ }
15042
+ return newest;
15043
+ }
15044
+ function isNewerProfile(candidate, existing) {
15045
+ if (candidate.event.created_at !== existing.event.created_at) {
15046
+ return candidate.event.created_at > existing.event.created_at;
15047
+ }
15048
+ return candidate.event.id < existing.event.id;
15049
+ }
15050
+ function yieldToEventLoop() {
15051
+ return new Promise((resolve) => setTimeout(resolve, 0));
15052
+ }
15053
+ function matchingCachedProfiles(profiles, filters) {
15054
+ const readyProfiles = profiles ? [...profiles.values()] : [];
15055
+ if (filters.length === 0) return readyProfiles;
15056
+ const matched = /* @__PURE__ */ new Map();
15057
+ for (const filter of filters) {
15058
+ const limit = typeof filter.limit === "number" && filter.limit >= 0 ? filter.limit : void 0;
15059
+ let count = 0;
15060
+ for (const entry of readyProfiles) {
15061
+ if (!matchesAnyFilter2(entry.event, [filter])) continue;
15062
+ if (limit !== void 0 && count >= limit) continue;
15063
+ count += 1;
15064
+ if (!matched.has(entry.event.id)) matched.set(entry.event.id, entry);
15065
+ }
15066
+ }
15067
+ return [...matched.values()];
15068
+ }
15069
+ function mergeResult(base, cached, filters, queryOptions) {
15070
+ const events = /* @__PURE__ */ new Map();
15071
+ for (const entry of base.events) events.set(entry.event.id, entry);
15072
+ for (const entry of cached) {
15073
+ if (!events.has(entry.event.id)) events.set(entry.event.id, entry);
15074
+ }
15075
+ return {
15076
+ events: applyFinalLimits([...events.values()], filters, queryOptions),
15077
+ ...base.incomplete === void 0 ? {} : { incomplete: base.incomplete },
15078
+ ...base.error === void 0 ? {} : { error: base.error }
15079
+ };
15080
+ }
15081
+ function applyFinalLimits(entries, filters, queryOptions) {
15082
+ const aggregateLimit = typeof queryOptions?.limit === "number" && queryOptions.limit >= 0 ? queryOptions.limit : void 0;
15083
+ const aggregateCapped = aggregateLimit === void 0 ? entries : entries.slice(0, aggregateLimit);
15084
+ if (filters.length === 0) return aggregateCapped;
15085
+ const counts = filters.map(() => 0);
15086
+ return aggregateCapped.filter((entry) => {
15087
+ const matching = filters.flatMap((filter, index) => matchesAnyFilter2(entry.event, [filter]) ? [index] : []);
15088
+ const allowed2 = matching.some((index) => {
15089
+ const limit = filters[index]?.limit;
15090
+ return typeof limit !== "number" || limit < 0 || counts[index] < limit;
15091
+ });
15092
+ if (!allowed2) return false;
15093
+ for (const index of matching) counts[index] += 1;
15094
+ return true;
15095
+ });
15096
+ }
14806
15097
 
14807
15098
  // src/browser-adapter.ts
14808
15099
  var DEV_COMMON_PUBKEY = "1".repeat(64);
@@ -14898,6 +15189,7 @@ function createDefaultIntentHost() {
14898
15189
  openOrReuse: () => null,
14899
15190
  waitForReady: () => void 0,
14900
15191
  isCurrent: () => false,
15192
+ getWindowId: () => null,
14901
15193
  send: () => void 0
14902
15194
  })
14903
15195
  };
@@ -14975,7 +15267,7 @@ function createDevSigner(getSimulation, confirmRequest) {
14975
15267
  };
14976
15268
  }
14977
15269
  function getRuntimePubkey(getSimulation, signerProvider) {
14978
- return getSimulation().identity.pubkey || signerProvider?.getPubkey() || (signerProvider?.getMethod() === "dev" ? PAJA_DEV_SIGNER_PUBKEY : "");
15270
+ return signerProvider?.getPubkey() || (signerProvider?.getMethod() === "dev" ? PAJA_DEV_SIGNER_PUBKEY : "") || getSimulation().identity.pubkey;
14979
15271
  }
14980
15272
  function createRuntimeSigner(getSimulation, confirmRequest, signerProvider) {
14981
15273
  const signer = signerProvider?.getSigner();
@@ -15012,6 +15304,15 @@ function createDevServices(backend, getSimulation, onThemeService, onThemeBroadc
15012
15304
  }
15013
15305
  })
15014
15306
  });
15307
+ const baseOutboxRouter = createOutboxRouter(backend, getSimulation, confirmRequest, signerProvider);
15308
+ const socialCache = createPajaSocialCache({
15309
+ baseRouter: baseOutboxRouter,
15310
+ loadContactList: createPajaContactListLoader(backend, getSimulation, signerProvider),
15311
+ verifyEvent: (event) => verifyEvent(event),
15312
+ getActivePubkey: () => getRuntimePubkey(getSimulation, signerProvider),
15313
+ subscribeSignerChange: signerProvider?.subscribe?.bind(signerProvider)
15314
+ });
15315
+ void socialCache.refreshActiveIdentity();
15015
15316
  const services = {
15016
15317
  keys: createKeysService(),
15017
15318
  resource: createResourceService({
@@ -15032,7 +15333,13 @@ function createDevServices(backend, getSimulation, onThemeService, onThemeBroadc
15032
15333
  selectRelayTier: () => getPajaRelayUrls(getSimulation()),
15033
15334
  isAvailable: () => backend.isAvailable()
15034
15335
  });
15035
- services.outbox = createOutboxService({ router: createOutboxRouter(backend, getSimulation, confirmRequest, signerProvider) });
15336
+ services.outbox = createOutboxService({
15337
+ router: baseOutboxRouter,
15338
+ getQueryRouter: (windowId, context) => socialCache.decorate(
15339
+ baseOutboxRouter,
15340
+ () => context?.hasCapability(windowId, "identity:read") ?? false
15341
+ )
15342
+ });
15036
15343
  }
15037
15344
  if (getSimulation().capabilities.domains.count && typeof backend.count === "function") {
15038
15345
  services.count = createCountService({
@@ -15056,7 +15363,7 @@ function createDevServices(backend, getSimulation, onThemeService, onThemeBroadc
15056
15363
  if (getSimulation().capabilities.domains.identity) {
15057
15364
  services.identity = createIdentityService({
15058
15365
  getSigner: () => createRuntimeSigner(getSimulation, confirmRequest, signerProvider),
15059
- ...createPajaIdentityProviders(backend, getSimulation, signerProvider)
15366
+ getFollows: socialCache.getFollows
15060
15367
  });
15061
15368
  }
15062
15369
  if (getSimulation().notifications.enabled) {
@@ -18925,7 +19232,7 @@ function aggregateTagValue(tags) {
18925
19232
  return void 0;
18926
19233
  }
18927
19234
 
18928
- // ../nip/dist/chunk-AGRSQG35.js
19235
+ // ../nip/dist/chunk-YIG2IRN2.js
18929
19236
  var MIB = 1024 * 1024;
18930
19237
  var NAPPLET_ARTIFACT_CACHE_NAME = "kehto:napplet-artifacts:v1";
18931
19238
  var SCHEMA_VERSION = 1;
@@ -19390,28 +19697,13 @@ function archetypesFromTags(tags) {
19390
19697
  "archetype slug must match the convention archetype"
19391
19698
  );
19392
19699
  }
19393
- const eventKinds = tag.slice(3).map((value) => {
19394
- const match = /^kind:(\d+)$/.exec(value);
19395
- if (!match) {
19396
- throw new NappletResolutionError(
19397
- "invalid-manifest",
19398
- "archetype trailing fields must use kind:<unsigned-integer>"
19399
- );
19400
- }
19401
- const kind = Number(match[1]);
19402
- if (!Number.isSafeInteger(kind) || kind < 0) {
19403
- throw new NappletResolutionError(
19404
- "invalid-manifest",
19405
- "archetype event kinds must be unsigned safe integers"
19406
- );
19407
- }
19408
- return kind;
19409
- });
19410
- out.push({
19411
- slug,
19412
- convention,
19413
- ...eventKinds.length === 0 ? {} : { eventKinds }
19414
- });
19700
+ if (tag.length !== 3) {
19701
+ throw new NappletResolutionError(
19702
+ "invalid-manifest",
19703
+ "archetype tags must contain exactly slug and convention"
19704
+ );
19705
+ }
19706
+ out.push({ slug, convention });
19415
19707
  }
19416
19708
  return out;
19417
19709
  }
@@ -19427,10 +19719,14 @@ function parseNappletManifest(event) {
19427
19719
  if (!aggregateHash) {
19428
19720
  throw new NappletResolutionError("invalid-manifest", "manifest has no aggregate x tag");
19429
19721
  }
19722
+ const dTag = firstTagValue(event.tags, "d");
19723
+ if (event.kind !== NAPPLET_KIND_NAMED && dTag !== void 0) {
19724
+ throw new NappletResolutionError("invalid-manifest", "only named manifests may carry a d tag");
19725
+ }
19430
19726
  return {
19431
19727
  kind: event.kind,
19432
19728
  pubkey: event.pubkey,
19433
- dTag: firstTagValue(event.tags, "d") ?? "",
19729
+ dTag: dTag ?? "",
19434
19730
  paths,
19435
19731
  aggregateHash,
19436
19732
  servers: allTagValues(event.tags, "server"),
@@ -19773,7 +20069,7 @@ function createPajaIntentTargetOptions(getState, getContext, effects = {}) {
19773
20069
  const current = state.tabs.find(
19774
20070
  (tab2) => matchesInstalledNappletRecord(record, tab2.resolvedTarget) && isCurrentRuntimeTabGeneration(state, context, tab2)
19775
20071
  );
19776
- if (current && params.behavior?.reuse !== false) {
20072
+ if (current && params.behavior?.newWindow !== true && params.behavior?.reuse !== false) {
19777
20073
  return bindPajaIntentGeneration(current, record, context.runtime);
19778
20074
  }
19779
20075
  const resolved = await resolvePajaPointer(record.pointer.value, pajaPointerResolverOptions(context));
@@ -19810,7 +20106,12 @@ function createPajaIntentTargetOptions(getState, getContext, effects = {}) {
19810
20106
  }
19811
20107
  return true;
19812
20108
  },
19813
- send(generation, delivery) {
20109
+ getWindowId(generation) {
20110
+ const state = getState();
20111
+ const tab = state ? findRuntimeTabGeneration(state, generation) : null;
20112
+ return tab?.windowId ?? null;
20113
+ },
20114
+ send(generation, params) {
19814
20115
  const state = getState();
19815
20116
  const context = getContext();
19816
20117
  const tab = state && context ? findRuntimeTabGeneration(state, generation) : null;
@@ -19823,8 +20124,10 @@ function createPajaIntentTargetOptions(getState, getContext, effects = {}) {
19823
20124
  throw new Error("intent target source is no longer registered");
19824
20125
  }
19825
20126
  createPajaPostMessageProxy(source, state, tab.windowId).postMessage({
19826
- type: "intent.deliver",
19827
- delivery
20127
+ type: "inc.event",
20128
+ topic: params.convention,
20129
+ sender: params.sender,
20130
+ ...params.payload === void 0 ? {} : { payload: params.payload }
19828
20131
  }, "*");
19829
20132
  }
19830
20133
  };
@@ -19841,12 +20144,12 @@ function pajaPointerResolverOptions(context) {
19841
20144
  }
19842
20145
  function recordSupportsDelivery(record, params) {
19843
20146
  return record.archetypes.some(
19844
- (archetype) => archetype.slug === params.delivery.archetype && archetype.convention === params.delivery.convention
20147
+ (archetype) => archetype.slug === params.archetype && archetype.convention === params.convention
19845
20148
  );
19846
20149
  }
19847
20150
  function resolvedSupportsDelivery(resolved, params) {
19848
20151
  return resolved.manifest.archetypes.some(
19849
- (archetype) => archetype.slug === params.delivery.archetype && archetype.convention === params.delivery.convention
20152
+ (archetype) => archetype.slug === params.archetype && archetype.convention === params.convention
19850
20153
  );
19851
20154
  }
19852
20155
  function findRuntimeTabGeneration(state, generation) {