@kehto/paja 0.9.0 → 0.10.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) {
@@ -4862,77 +4862,57 @@ function nappletNamespacePrelude(domains) {
4862
4862
  };
4863
4863
  }
4864
4864
  function makeIntent() {
4865
- const deliveryHandlers = /* @__PURE__ */ new Set();
4866
- const pendingDeliveries = [];
4867
4865
  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;
4866
+ const normalizeRequest = (value) => {
4867
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
4868
+ throw new TypeError("Intent request must be an object");
4871
4869
  }
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:");
4870
+ const supplied = value;
4871
+ if (hasOwn2(supplied, "sender")) {
4872
+ throw new TypeError("Intent callers cannot supply sender");
4893
4873
  }
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`);
4874
+ const allowed22 = ["archetype", "action", "convention", "payload", "handler", "behavior"];
4875
+ if (Object.keys(supplied).some((field) => !allowed22.includes(field))) {
4876
+ throw new TypeError("Intent request contains unsupported fields");
4900
4877
  }
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");
4878
+ if (typeof supplied.archetype !== "string" || supplied.archetype.length === 0) {
4879
+ throw new TypeError("Intent request requires an archetype");
4914
4880
  }
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
- }
4881
+ const action = supplied.action === void 0 ? "open" : supplied.action;
4882
+ if (typeof action !== "string" || action.length === 0) {
4883
+ throw new TypeError("Intent action must be a non-empty string");
4884
+ }
4885
+ if (supplied.convention !== void 0 && (typeof supplied.convention !== "string" || !/^napplet:[^/?#\s]+\/[^/?#\s]+$/.test(supplied.convention))) {
4886
+ throw new TypeError("Intent convention must be queryless");
4887
+ }
4888
+ if (supplied.handler !== void 0 && (typeof supplied.handler !== "string" || supplied.handler.length === 0)) {
4889
+ throw new TypeError("Intent handler must be a non-empty string");
4919
4890
  }
4920
4891
  const behaviorValue = supplied?.behavior;
4921
4892
  const behavior = behaviorValue && typeof behaviorValue === "object" ? behaviorValue : void 0;
4893
+ 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")) {
4894
+ throw new TypeError("Intent behavior contains unsupported fields");
4895
+ }
4922
4896
  const sanitizedBehavior = {
4923
4897
  ...typeof behavior?.focus === "boolean" ? { focus: behavior.focus } : {},
4898
+ ...typeof behavior?.newWindow === "boolean" ? { newWindow: behavior.newWindow } : {},
4924
4899
  ...typeof behavior?.reuse === "boolean" ? { reuse: behavior.reuse } : {}
4925
4900
  };
4926
- const handler = typeof supplied?.handler === "string" ? supplied.handler : void 0;
4901
+ return {
4902
+ archetype: supplied.archetype,
4903
+ action,
4904
+ ...typeof supplied.convention === "string" ? { convention: supplied.convention } : {},
4905
+ ...hasOwn2(supplied, "payload") ? { payload: supplied.payload } : {},
4906
+ ...typeof supplied.handler === "string" ? { handler: supplied.handler } : {},
4907
+ ...Object.keys(sanitizedBehavior).length === 0 ? {} : { behavior: sanitizedBehavior }
4908
+ };
4909
+ };
4910
+ const invoke = (value) => {
4911
+ const normalized = normalizeRequest(value);
4927
4912
  return request(
4928
4913
  {
4929
4914
  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
- }
4915
+ request: normalized
4936
4916
  },
4937
4917
  "intent.invoke.result",
4938
4918
  (msg) => fieldOrThrow(msg, "result", "intent.invoke.result missing result")
@@ -4940,7 +4920,12 @@ function nappletNamespacePrelude(domains) {
4940
4920
  };
4941
4921
  return {
4942
4922
  invoke,
4943
- open: (uri, opts) => invoke(uri, opts, "open"),
4923
+ open: (archetype, payload, opts) => invoke({
4924
+ archetype,
4925
+ action: "open",
4926
+ ...payload === void 0 ? {} : { payload },
4927
+ ...opts
4928
+ }),
4944
4929
  available: (archetype) => request(
4945
4930
  { type: "intent.available", archetype },
4946
4931
  "intent.available.result",
@@ -4955,14 +4940,7 @@ function nappletNamespacePrelude(domains) {
4955
4940
  "intent.changed",
4956
4941
  callback,
4957
4942
  (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
- }
4943
+ )
4966
4944
  };
4967
4945
  }
4968
4946
  function makeWebrtc() {
@@ -11721,14 +11699,17 @@ function hasOwn(value, key) {
11721
11699
  }
11722
11700
  function validateIntentRequest(value) {
11723
11701
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
11724
- return { error: "invalid convention" };
11702
+ return { error: "invalid request" };
11725
11703
  }
11726
11704
  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" };
11705
+ if (typeof request.archetype !== "string" || request.archetype.length === 0) {
11706
+ return { error: "invalid request" };
11707
+ }
11708
+ const action = request.action === void 0 ? "open" : request.action;
11709
+ if (typeof action !== "string" || action.length === 0) {
11710
+ return { error: "invalid request" };
11729
11711
  }
11730
- const match = /^napplet:([^/?#\s]+)\/([^/?#\s]+)$/.exec(request.convention);
11731
- if (!match || match[1] !== request.archetype || match[2] !== request.action) {
11712
+ if (request.convention !== void 0 && (typeof request.convention !== "string" || !/^napplet:[^/?#\s]+\/[^/?#\s]+$/.test(request.convention))) {
11732
11713
  return { error: "invalid convention" };
11733
11714
  }
11734
11715
  const allowedKeys = ["archetype", "action", "convention", "payload", "handler", "behavior"];
@@ -11744,19 +11725,20 @@ function validateIntentRequest(value) {
11744
11725
  return { error: "invoke rejected" };
11745
11726
  }
11746
11727
  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") {
11728
+ 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
11729
  return { error: "invoke rejected" };
11749
11730
  }
11750
11731
  behavior = {
11751
11732
  ...record.focus === void 0 ? {} : { focus: record.focus },
11733
+ ...record.newWindow === void 0 ? {} : { newWindow: record.newWindow },
11752
11734
  ...record.reuse === void 0 ? {} : { reuse: record.reuse }
11753
11735
  };
11754
11736
  }
11755
11737
  return {
11756
11738
  request: {
11757
11739
  archetype: request.archetype,
11758
- action: request.action,
11759
- convention: request.convention,
11740
+ action,
11741
+ ...typeof request.convention === "string" ? { convention: request.convention } : {},
11760
11742
  ...hasOwn(request, "payload") ? { payload: request.payload } : {},
11761
11743
  ...typeof request.handler === "string" ? { handler: request.handler } : {},
11762
11744
  ...behavior === void 0 ? {} : { behavior }
@@ -11773,11 +11755,20 @@ function settleResolverCall(call, send, resultType, id, onValue) {
11773
11755
  }
11774
11756
  pending.then((value) => send(onValue(value))).catch((error) => send({ type: resultType, id, error: toErrorMessage4(error) }));
11775
11757
  }
11776
- function rejectInvoke(send, id) {
11758
+ function rejectedResult(request, error) {
11759
+ return {
11760
+ ok: false,
11761
+ archetype: request.archetype,
11762
+ action: request.action ?? "open",
11763
+ handled: false,
11764
+ error
11765
+ };
11766
+ }
11767
+ function rejectInvoke(send, id, request, error = "invoke rejected") {
11777
11768
  send({
11778
11769
  type: "intent.invoke.result",
11779
11770
  id,
11780
- result: { ok: false, error: "invoke rejected" }
11771
+ result: rejectedResult(request, error)
11781
11772
  });
11782
11773
  }
11783
11774
  function createIntentService(options) {
@@ -11792,46 +11783,40 @@ function createIntentService(options) {
11792
11783
  const id = m.id ?? "";
11793
11784
  const validation = validateIntentRequest(m.request);
11794
11785
  if ("error" in validation) {
11795
- send({
11796
- type: "intent.invoke.result",
11786
+ const raw = typeof m.request === "object" && m.request !== null ? m.request : {};
11787
+ rejectInvoke(
11788
+ send,
11797
11789
  id,
11798
- result: { ok: false, error: validation.error }
11799
- });
11790
+ {
11791
+ archetype: typeof raw.archetype === "string" ? raw.archetype : "",
11792
+ action: typeof raw.action === "string" ? raw.action : "open"
11793
+ },
11794
+ validation.error
11795
+ );
11800
11796
  return;
11801
11797
  }
11802
11798
  const sender = runtimeContext?.resolveDTag(windowId);
11803
11799
  if (!sender) {
11804
- rejectInvoke(send, id);
11800
+ rejectInvoke(send, id, validation.request);
11805
11801
  return;
11806
11802
  }
11807
11803
  let pending;
11808
11804
  try {
11809
11805
  pending = Promise.resolve(resolver.invoke(validation.request, { sender }));
11810
11806
  } catch {
11811
- rejectInvoke(send, id);
11807
+ rejectInvoke(send, id, validation.request);
11812
11808
  return;
11813
11809
  }
11814
11810
  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
- }
11811
+ (result2) => {
11820
11812
  send({
11821
11813
  type: "intent.invoke.result",
11822
11814
  id,
11823
- result: outcome.result
11815
+ result: result2
11824
11816
  });
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
11817
  },
11833
11818
  () => {
11834
- rejectInvoke(send, id);
11819
+ rejectInvoke(send, id, validation.request);
11835
11820
  }
11836
11821
  ).catch(() => {
11837
11822
  });
@@ -11916,128 +11901,134 @@ function candidatesFor(catalog, archetype, defaultHandler) {
11916
11901
  ...entry.title === void 0 ? {} : { title: entry.title },
11917
11902
  actions: [...support.actions],
11918
11903
  conventions: [...support.conventions],
11919
- contracts: support.contracts.map((contract) => ({
11920
- convention: contract.convention,
11921
- ...contract.eventKinds === void 0 ? {} : { eventKinds: [...contract.eventKinds] }
11922
- })),
11923
11904
  ...entry.dTag === defaultHandler ? { isDefault: true } : {}
11924
11905
  });
11925
11906
  }
11926
11907
  return candidates;
11927
11908
  }
11928
- function reject(error) {
11929
- return { ok: false, error };
11909
+ function reject(request, error) {
11910
+ return {
11911
+ ok: false,
11912
+ archetype: request.archetype,
11913
+ action: request.action ?? "open",
11914
+ handled: false,
11915
+ error
11916
+ };
11917
+ }
11918
+ async function availabilityForCatalog(options, archetype) {
11919
+ const catalog = await options.loadCatalog();
11920
+ const defaultHandler = options.getDefaultHandler?.(archetype);
11921
+ const candidates = candidatesFor(catalog, archetype, defaultHandler);
11922
+ return {
11923
+ archetype,
11924
+ available: candidates.length > 0,
11925
+ candidates,
11926
+ hasDefault: defaultHandler !== void 0 && candidates.some((candidate) => candidate.dTag === defaultHandler)
11927
+ };
11928
+ }
11929
+ async function chooseCompatible(options, request, candidates, sender) {
11930
+ if (!options.chooseHandler) {
11931
+ return request.handler === "choose" ? { error: "user cancelled" } : { error: "invoke rejected" };
11932
+ }
11933
+ const picked = await options.chooseHandler(request.archetype, candidates, sender);
11934
+ if (picked === void 0) return { error: "user cancelled" };
11935
+ const candidate = candidates.find((item) => item.dTag === picked);
11936
+ return candidate ? { candidate } : { error: "invoke rejected" };
11937
+ }
11938
+ async function pickHandler(options, request, compatible, sender) {
11939
+ const preference = request.handler;
11940
+ if (typeof preference === "string" && preference !== "default" && preference !== "choose") {
11941
+ const candidate = compatible.find((item) => item.dTag === preference);
11942
+ if (!candidate || !options.authorizeExplicitHandler) return { error: "invoke rejected" };
11943
+ let authorized = false;
11944
+ try {
11945
+ authorized = await options.authorizeExplicitHandler(
11946
+ sender,
11947
+ preference,
11948
+ request,
11949
+ candidate
11950
+ );
11951
+ } catch {
11952
+ return { error: "invoke rejected" };
11953
+ }
11954
+ return authorized ? { candidate } : { error: "invoke rejected" };
11955
+ }
11956
+ if (preference === "choose") {
11957
+ return chooseCompatible(options, request, compatible, sender);
11958
+ }
11959
+ const defaultCandidate = compatible.find((candidate) => candidate.isDefault === true);
11960
+ if (preference === "default") {
11961
+ return defaultCandidate ? { candidate: defaultCandidate } : { error: "invoke rejected" };
11962
+ }
11963
+ if (defaultCandidate) return { candidate: defaultCandidate };
11964
+ if (compatible.length === 1) return { candidate: compatible[0] };
11965
+ return chooseCompatible(options, request, compatible, sender);
11966
+ }
11967
+ async function invokeFromCatalog(options, request, context) {
11968
+ const catalog = await options.loadCatalog();
11969
+ const defaultHandler = options.getDefaultHandler?.(request.archetype);
11970
+ const candidates = candidatesFor(catalog, request.archetype, defaultHandler);
11971
+ if (candidates.length === 0) return reject(request, "no handler");
11972
+ const action = request.action ?? "open";
11973
+ const actionCompatible = candidates.filter((candidate) => candidate.actions.includes(action));
11974
+ if (actionCompatible.length === 0) return reject(request, "unsupported action");
11975
+ const compatible = request.convention === void 0 ? actionCompatible : actionCompatible.filter((candidate) => candidate.conventions.includes(request.convention));
11976
+ if (compatible.length === 0) return reject(request, "unsupported convention");
11977
+ const sender = context.sender;
11978
+ if (typeof sender !== "string" || sender.length === 0) {
11979
+ return reject(request, "invoke rejected");
11980
+ }
11981
+ const selected = await pickHandler(options, request, compatible, sender);
11982
+ if ("error" in selected) return reject(request, selected.error);
11983
+ const convention = request.convention ?? selected.candidate.conventions.find((value) => {
11984
+ const match = /^napplet:[^/?#\s]+\/([^/?#\s]+)$/.exec(value);
11985
+ return match?.[1] === action;
11986
+ });
11987
+ if (!convention) return reject(request, "unsupported convention");
11988
+ const behavior = request.behavior === void 0 ? void 0 : Object.freeze({
11989
+ ...request.behavior.focus === void 0 ? {} : { focus: request.behavior.focus },
11990
+ ...request.behavior.newWindow === void 0 ? {} : { newWindow: request.behavior.newWindow },
11991
+ ...request.behavior.reuse === void 0 ? {} : { reuse: request.behavior.reuse }
11992
+ });
11993
+ const params = Object.freeze({
11994
+ handler: selected.candidate.dTag,
11995
+ sender,
11996
+ archetype: request.archetype,
11997
+ action,
11998
+ convention,
11999
+ ...request.payload === void 0 ? {} : { payload: request.payload },
12000
+ ...behavior === void 0 ? {} : { behavior }
12001
+ });
12002
+ let target;
12003
+ try {
12004
+ target = await options.targets.dispatch(params);
12005
+ } catch {
12006
+ return reject(request, "invoke failed");
12007
+ }
12008
+ if (!target || typeof target.windowId !== "string" || target.windowId.length === 0) {
12009
+ return reject(request, "invoke failed");
12010
+ }
12011
+ return {
12012
+ ok: true,
12013
+ archetype: request.archetype,
12014
+ action,
12015
+ handled: true,
12016
+ handler: selected.candidate.dTag,
12017
+ windowId: target.windowId,
12018
+ convention
12019
+ };
11930
12020
  }
11931
12021
  function createCatalogIntentResolver(options) {
11932
12022
  if (!options || typeof options.loadCatalog !== "function") {
11933
12023
  throw new Error("createCatalogIntentResolver: options.loadCatalog is required");
11934
12024
  }
11935
- if (!options.targets || typeof options.targets.retain !== "function") {
12025
+ if (!options.targets || typeof options.targets.dispatch !== "function") {
11936
12026
  throw new Error("createCatalogIntentResolver: options.targets is required");
11937
12027
  }
11938
- const {
11939
- loadCatalog,
11940
- targets,
11941
- getDefaultHandler,
11942
- chooseHandler,
11943
- authorizeExplicitHandler
11944
- } = options;
11945
12028
  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
- }
12029
+ const availabilityFor = (archetype) => availabilityForCatalog(options, archetype);
12039
12030
  async function handlers() {
12040
- const catalog = await loadCatalog();
12031
+ const catalog = await options.loadCatalog();
12041
12032
  const archetypes = /* @__PURE__ */ new Set();
12042
12033
  for (const entry of catalog) {
12043
12034
  for (const slug of Object.keys(entry.archetypes)) archetypes.add(slug);
@@ -12045,7 +12036,7 @@ function createCatalogIntentResolver(options) {
12045
12036
  return Promise.all([...archetypes].map((archetype) => availabilityFor(archetype)));
12046
12037
  }
12047
12038
  return {
12048
- invoke,
12039
+ invoke: (request, context) => invokeFromCatalog(options, request, context),
12049
12040
  available: availabilityFor,
12050
12041
  handlers,
12051
12042
  onChanged(listener) {
@@ -12072,21 +12063,12 @@ function actionFromConvention(slug, convention) {
12072
12063
  }
12073
12064
  function manifestToIntentCatalogEntry(manifest) {
12074
12065
  const archetypes = {};
12075
- for (const { slug, convention, eventKinds } of manifest.archetypes) {
12066
+ for (const { slug, convention } of manifest.archetypes) {
12076
12067
  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");
12079
- }
12080
12068
  const support = archetypes[slug] ??= {
12081
12069
  actions: [],
12082
- conventions: [],
12083
- contracts: []
12070
+ conventions: []
12084
12071
  };
12085
- const contract = {
12086
- convention,
12087
- ...eventKinds === void 0 || eventKinds.length === 0 ? {} : { eventKinds: [...eventKinds] }
12088
- };
12089
- support.contracts.push(contract);
12090
12072
  if (!support.actions.includes(action)) support.actions.push(action);
12091
12073
  if (!support.conventions.includes(convention)) support.conventions.push(convention);
12092
12074
  }
@@ -13095,25 +13077,13 @@ var BrowserIntentController = class {
13095
13077
  }
13096
13078
  options;
13097
13079
  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) {
13080
+ async dispatch(params) {
13081
+ const dispatch2 = freezeDispatch(params);
13112
13082
  let reason = "no-current-target";
13113
13083
  for (let attempt = 1; attempt <= this.maxAttempts; attempt += 1) {
13114
13084
  let generation;
13115
13085
  try {
13116
- generation = await this.options.openOrReuse(params, attempt);
13086
+ generation = await this.options.openOrReuse(dispatch2, attempt);
13117
13087
  } catch {
13118
13088
  reason = "open-failed";
13119
13089
  continue;
@@ -13132,17 +13102,18 @@ var BrowserIntentController = class {
13132
13102
  reason = "no-current-target";
13133
13103
  continue;
13134
13104
  }
13135
- if (isDelivered()) return;
13136
- markDelivered();
13137
13105
  try {
13138
- await this.options.send(generation, params.delivery);
13139
- return;
13106
+ await this.options.send(generation, dispatch2);
13107
+ const windowId = this.options.getWindowId(generation);
13108
+ if (!windowId) throw new Error("intent target window is unavailable");
13109
+ return { windowId };
13140
13110
  } catch {
13141
- this.options.onTerminal?.(params, "send-failed");
13142
- return;
13111
+ this.options.onTerminal?.(dispatch2, "send-failed");
13112
+ throw new Error("intent target send failed");
13143
13113
  }
13144
13114
  }
13145
- this.options.onTerminal?.(params, reason);
13115
+ this.options.onTerminal?.(dispatch2, reason);
13116
+ throw new Error(`intent target ${reason}`);
13146
13117
  }
13147
13118
  };
13148
13119
  function normalizeAttempts(value) {
@@ -13150,15 +13121,22 @@ function normalizeAttempts(value) {
13150
13121
  if (!Number.isFinite(value)) throw new TypeError("maxAttempts must be finite");
13151
13122
  return Math.min(MAX_INTENT_DELIVERY_ATTEMPTS, Math.max(1, Math.floor(value)));
13152
13123
  }
13153
- function freezeRetention(params) {
13124
+ function freezeDispatch(params) {
13154
13125
  if (!params || typeof params.handler !== "string" || params.handler.length === 0) {
13155
- throw new TypeError("Intent retention requires a handler");
13126
+ throw new TypeError("Intent dispatch requires a handler");
13127
+ }
13128
+ 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) {
13129
+ throw new TypeError("Intent dispatch requires canonical routing fields");
13156
13130
  }
13157
- const delivery = freezeValue({ ...params.delivery });
13131
+ const payload = freezeValue(params.payload);
13158
13132
  const behavior = params.behavior === void 0 ? void 0 : freezeValue({ ...params.behavior });
13159
13133
  return Object.freeze({
13160
13134
  handler: params.handler,
13161
- delivery,
13135
+ sender: params.sender,
13136
+ archetype: params.archetype,
13137
+ action: params.action,
13138
+ convention: params.convention,
13139
+ ...params.payload === void 0 ? {} : { payload },
13162
13140
  ...behavior === void 0 ? {} : { behavior }
13163
13141
  });
13164
13142
  }
@@ -13192,10 +13170,10 @@ var InstalledNappletCatalog = class {
13192
13170
  aggregateHash: resolved.aggregateHash,
13193
13171
  pointer: copyPointer(resolved.pointer),
13194
13172
  ...resolved.manifest.title === void 0 ? {} : { title: resolved.manifest.title },
13173
+ requires: [...resolved.manifest.requires],
13195
13174
  archetypes: resolved.manifest.archetypes.map((archetype) => ({
13196
13175
  slug: archetype.slug,
13197
- convention: archetype.convention,
13198
- ...archetype.eventKinds === void 0 ? {} : { eventKinds: [...archetype.eventKinds] }
13176
+ convention: archetype.convention
13199
13177
  }))
13200
13178
  });
13201
13179
  this.records.set(record.dTag, record);
@@ -13231,13 +13209,12 @@ var InstalledNappletCatalog = class {
13231
13209
  }
13232
13210
  /** Return exact handler candidates derived only from installed manifests. */
13233
13211
  intentCatalog() {
13234
- return this.installed().map((record) => manifestToIntentCatalogEntry({
13212
+ return this.installed().filter((record) => record.requires.includes("inc")).map((record) => manifestToIntentCatalogEntry({
13235
13213
  dTag: record.dTag,
13236
13214
  ...record.title === void 0 ? {} : { title: record.title },
13237
13215
  archetypes: record.archetypes.map((archetype) => ({
13238
13216
  slug: archetype.slug,
13239
- convention: archetype.convention,
13240
- ...archetype.eventKinds === void 0 ? {} : { eventKinds: [...archetype.eventKinds] }
13217
+ convention: archetype.convention
13241
13218
  }))
13242
13219
  }));
13243
13220
  }
@@ -13273,10 +13250,8 @@ function copyPointer(pointer) {
13273
13250
  function freezeRecord(record) {
13274
13251
  return Object.freeze({
13275
13252
  ...record,
13276
- archetypes: Object.freeze(record.archetypes.map((archetype) => Object.freeze({
13277
- ...archetype,
13278
- ...archetype.eventKinds === void 0 ? {} : { eventKinds: Object.freeze([...archetype.eventKinds]) }
13279
- })))
13253
+ requires: Object.freeze([...record.requires]),
13254
+ archetypes: Object.freeze(record.archetypes.map((archetype) => Object.freeze({ ...archetype })))
13280
13255
  });
13281
13256
  }
13282
13257
 
@@ -14898,6 +14873,7 @@ function createDefaultIntentHost() {
14898
14873
  openOrReuse: () => null,
14899
14874
  waitForReady: () => void 0,
14900
14875
  isCurrent: () => false,
14876
+ getWindowId: () => null,
14901
14877
  send: () => void 0
14902
14878
  })
14903
14879
  };
@@ -18925,7 +18901,7 @@ function aggregateTagValue(tags) {
18925
18901
  return void 0;
18926
18902
  }
18927
18903
 
18928
- // ../nip/dist/chunk-AGRSQG35.js
18904
+ // ../nip/dist/chunk-ZQSTY6HR.js
18929
18905
  var MIB = 1024 * 1024;
18930
18906
  var NAPPLET_ARTIFACT_CACHE_NAME = "kehto:napplet-artifacts:v1";
18931
18907
  var SCHEMA_VERSION = 1;
@@ -19390,28 +19366,13 @@ function archetypesFromTags(tags) {
19390
19366
  "archetype slug must match the convention archetype"
19391
19367
  );
19392
19368
  }
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
- });
19369
+ if (tag.length !== 3) {
19370
+ throw new NappletResolutionError(
19371
+ "invalid-manifest",
19372
+ "archetype tags must contain exactly slug and convention"
19373
+ );
19374
+ }
19375
+ out.push({ slug, convention });
19415
19376
  }
19416
19377
  return out;
19417
19378
  }
@@ -19773,7 +19734,7 @@ function createPajaIntentTargetOptions(getState, getContext, effects = {}) {
19773
19734
  const current = state.tabs.find(
19774
19735
  (tab2) => matchesInstalledNappletRecord(record, tab2.resolvedTarget) && isCurrentRuntimeTabGeneration(state, context, tab2)
19775
19736
  );
19776
- if (current && params.behavior?.reuse !== false) {
19737
+ if (current && params.behavior?.newWindow !== true && params.behavior?.reuse !== false) {
19777
19738
  return bindPajaIntentGeneration(current, record, context.runtime);
19778
19739
  }
19779
19740
  const resolved = await resolvePajaPointer(record.pointer.value, pajaPointerResolverOptions(context));
@@ -19810,7 +19771,12 @@ function createPajaIntentTargetOptions(getState, getContext, effects = {}) {
19810
19771
  }
19811
19772
  return true;
19812
19773
  },
19813
- send(generation, delivery) {
19774
+ getWindowId(generation) {
19775
+ const state = getState();
19776
+ const tab = state ? findRuntimeTabGeneration(state, generation) : null;
19777
+ return tab?.windowId ?? null;
19778
+ },
19779
+ send(generation, params) {
19814
19780
  const state = getState();
19815
19781
  const context = getContext();
19816
19782
  const tab = state && context ? findRuntimeTabGeneration(state, generation) : null;
@@ -19823,8 +19789,10 @@ function createPajaIntentTargetOptions(getState, getContext, effects = {}) {
19823
19789
  throw new Error("intent target source is no longer registered");
19824
19790
  }
19825
19791
  createPajaPostMessageProxy(source, state, tab.windowId).postMessage({
19826
- type: "intent.deliver",
19827
- delivery
19792
+ type: "inc.event",
19793
+ topic: params.convention,
19794
+ sender: params.sender,
19795
+ ...params.payload === void 0 ? {} : { payload: params.payload }
19828
19796
  }, "*");
19829
19797
  }
19830
19798
  };
@@ -19841,12 +19809,12 @@ function pajaPointerResolverOptions(context) {
19841
19809
  }
19842
19810
  function recordSupportsDelivery(record, params) {
19843
19811
  return record.archetypes.some(
19844
- (archetype) => archetype.slug === params.delivery.archetype && archetype.convention === params.delivery.convention
19812
+ (archetype) => archetype.slug === params.archetype && archetype.convention === params.convention
19845
19813
  );
19846
19814
  }
19847
19815
  function resolvedSupportsDelivery(resolved, params) {
19848
19816
  return resolved.manifest.archetypes.some(
19849
- (archetype) => archetype.slug === params.delivery.archetype && archetype.convention === params.delivery.convention
19817
+ (archetype) => archetype.slug === params.archetype && archetype.convention === params.convention
19850
19818
  );
19851
19819
  }
19852
19820
  function findRuntimeTabGeneration(state, generation) {