@kehto/shell 0.21.1 → 0.21.3

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.
package/README.md CHANGED
@@ -85,9 +85,10 @@ unsupported fields and caller-supplied sender data, and resolves only
85
85
  parent-originated correlated results.
86
86
 
87
87
  Successful results include `handled`, `handler`, `windowId`, and `convention`
88
- after target dispatch completes. There is no `intent.deliver` or `onDelivery`;
89
- selected targets consume their convention through the ordinary
90
- runtime-attested INC binding.
88
+ after target dispatch completes. Target delivery now follows NAP-INTENT at
89
+ `25b29ee49e5bff8ebfe031f4b76dce98705c8b7e`: one `intent.deliver` envelope
90
+ feeds the buffered `onDelivery` binding described below. Broader invocation and
91
+ result changes from that newer spec remain deferred.
91
92
 
92
93
  Phase 105 completed released `@napplet/*` package adoption and persistent live
93
94
  Paja/playground catalogs and target controllers. The Phase 104 Paja simulator
@@ -211,3 +212,27 @@ Generated API module: `docs/api/modules/_kehto_shell.html` (run `pnpm docs:api`)
211
212
  ## License
212
213
 
213
214
  MIT
215
+
216
+ ## NIP-5D event compatibility
217
+
218
+ Current manifests use a direct artifact `x` hash, plain-text `content`, independent
219
+ `z`/`i` routing declarations, and required `R` / optional `O` domains. Kehto also
220
+ accepts legacy aggregate events through an isolated compatibility adapter.
221
+ Existing `aggregateHash` host/cache/ACL fields carry the verified artifact hash
222
+ for current events; legacy identities keep their original aggregate. Both paths
223
+ verify signatures and bytes before runtime injection and `srcdoc` execution.
224
+ For the schema and removal boundary, see [event migration](https://kehto.github.io/web/docs/migrations/NIP-5D-EVENT-SCHEMA.html).
225
+
226
+ ### Intent delivery
227
+
228
+ The injected `window.napplet.intent.onDelivery(handler)` returns a closeable
229
+ subscription and drains buffered parent deliveries in order. Register it to
230
+ receive `{ sender, archetype, action, convention, payload? }`; INC is not required.
231
+ Its receiver is installed before `shell.ready` and survives namespace reassignment.
232
+ A throwing listener does not prevent delivery to other listeners.
233
+
234
+ When no canonical listener is registered, matching legacy INC listeners can
235
+ consume the delivery locally. This emits `KEHTO_COMPAT_INTENT_INC` once per
236
+ iframe; a delivery is never replayed to both APIs. See [compatibility tracking](https://github.com/kehto/web/blob/main/docs/compatibility.md).
237
+ This aligns delivery with NAP-INTENT at `25b29ee`; broader invocation/result
238
+ changes and the packaged SDK upgrade remain deferred.
package/dist/index.js CHANGED
@@ -389,7 +389,7 @@ var originRegistry = {
389
389
  */
390
390
  getIdentity(win) {
391
391
  const entry = registry.get(win);
392
- if (!entry?.dTag || !entry?.aggregateHash) return void 0;
392
+ if (typeof entry?.dTag !== "string" || !entry?.aggregateHash) return void 0;
393
393
  return { dTag: entry.dTag, aggregateHash: entry.aggregateHash };
394
394
  },
395
395
  /** Store the immutable environment captured for this exact frame registration. */
@@ -1691,7 +1691,13 @@ function nappletNamespacePrelude(domains) {
1691
1691
  fire({ type: "inc.subscribe", id: subscriptionId, topic: stableTopic });
1692
1692
  }
1693
1693
  state.handlers.add(callback);
1694
+ const offLegacy = intentDelivery?.onLegacy(stableTopic, (delivery) => callback({
1695
+ topic: delivery.convention,
1696
+ sender: delivery.sender,
1697
+ ...Object.prototype.hasOwnProperty.call(delivery, "payload") ? { payload: delivery.payload } : {}
1698
+ }));
1694
1699
  return subscriptionHandle(() => {
1700
+ offLegacy?.();
1695
1701
  const current = topicStates.get(stableTopic);
1696
1702
  if (!current) return;
1697
1703
  current.handlers.delete(callback);
@@ -2253,6 +2259,69 @@ function nappletNamespacePrelude(domains) {
2253
2259
  }
2254
2260
  };
2255
2261
  }
2262
+ function makeIntentDelivery() {
2263
+ const handlers = /* @__PURE__ */ new Set();
2264
+ const legacy = /* @__PURE__ */ new Map();
2265
+ const pending = [];
2266
+ let draining = false;
2267
+ let warned = false;
2268
+ function drain() {
2269
+ if (draining) return;
2270
+ draining = true;
2271
+ try {
2272
+ while (pending.length > 0) {
2273
+ const index = handlers.size > 0 ? 0 : pending.findIndex((delivery2) => [...legacy.values()].includes(delivery2.convention));
2274
+ if (index < 0) break;
2275
+ const [delivery] = pending.splice(index, 1);
2276
+ const canonical = handlers.size > 0;
2277
+ const recipients = canonical ? [...handlers] : [...legacy.entries()].filter(([, topic]) => topic === delivery.convention).map(([handler]) => handler);
2278
+ if (!canonical && !warned) {
2279
+ warned = true;
2280
+ console.warn("[KEHTO_COMPAT_INTENT_INC] Intent delivered to a legacy INC listener. Migrate to window.napplet.intent.onDelivery. See docs/compatibility.md.");
2281
+ }
2282
+ for (const handler of recipients) {
2283
+ try {
2284
+ handler(delivery);
2285
+ } catch (error) {
2286
+ console.error("Intent delivery handler failed", error);
2287
+ }
2288
+ }
2289
+ }
2290
+ } finally {
2291
+ draining = false;
2292
+ }
2293
+ }
2294
+ listen((event) => {
2295
+ if (!isParentMessage(event)) return;
2296
+ const message = event.data;
2297
+ if (!message || message.type !== "intent.deliver") return;
2298
+ const value = message.delivery;
2299
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
2300
+ const delivery = value;
2301
+ if (typeof delivery.sender !== "string" || typeof delivery.archetype !== "string" || typeof delivery.action !== "string" || typeof delivery.convention !== "string") return;
2302
+ pending.push(Object.freeze({
2303
+ sender: delivery.sender,
2304
+ archetype: delivery.archetype,
2305
+ action: delivery.action,
2306
+ convention: delivery.convention,
2307
+ ...Object.prototype.hasOwnProperty.call(delivery, "payload") ? { payload: delivery.payload } : {}
2308
+ }));
2309
+ drain();
2310
+ });
2311
+ return {
2312
+ onDelivery(handler) {
2313
+ if (typeof handler !== "function") throw new TypeError("Intent delivery handler must be a function");
2314
+ handlers.add(handler);
2315
+ drain();
2316
+ return subscriptionHandle(() => handlers.delete(handler));
2317
+ },
2318
+ onLegacy(topic, handler) {
2319
+ legacy.set(handler, topic);
2320
+ drain();
2321
+ return () => legacy.delete(handler);
2322
+ }
2323
+ };
2324
+ }
2256
2325
  function makeIntent() {
2257
2326
  const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
2258
2327
  const normalizeRequest = (value) => {
@@ -2311,6 +2380,7 @@ function nappletNamespacePrelude(domains) {
2311
2380
  );
2312
2381
  };
2313
2382
  return {
2383
+ onDelivery: intentDelivery.onDelivery,
2314
2384
  invoke,
2315
2385
  open: (archetype, payload, opts) => invoke({
2316
2386
  archetype,
@@ -2490,6 +2560,7 @@ function nappletNamespacePrelude(domains) {
2490
2560
  onChanged: (callback) => domainEventHandle("fs.changed", callback, (msg) => msg.change)
2491
2561
  };
2492
2562
  }
2563
+ const intentDelivery = allowed.has("intent") ? makeIntentDelivery() : void 0;
2493
2564
  const shell = makeShell();
2494
2565
  let inc;
2495
2566
  let identity;