@kehto/shell 0.20.1 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,7 @@ pnpm add @kehto/shell
15
15
  ## Published Napplet Compatibility
16
16
 
17
17
  `@kehto/shell` publishes against `@napplet/core` and `@napplet/nap`
18
- `>=0.31.0 <0.32.0`. The installed core 0.31.1 / nap 0.31.2 contracts follow
18
+ `>=0.32.0 <0.33.0`. The installed core 0.32.0 / nap 0.32.0 contracts follow
19
19
  NAP-INTENT authority `5ac0490461ca6fec2f0d2e45b4835cf9bc08de24`, napplet/web#199
20
20
  source `3037200c932488f14f7f369b8583c39c9c16510a` / merge
21
21
  `b3f0007867eac109fa4917fac9c285d3b7cc6155`, and Version Packages #198 release
@@ -34,7 +34,8 @@ Current draft behaviors this package enforces:
34
34
  when the runtime can identify it. The shell forwards this optional context
35
35
  without imposing a consent policy; host runtimes may ignore it or use it to
36
36
  resolve their own identity-scoped signer decisions.
37
- - `injectNappletNamespacePrelude()` implements the NIP-5D injected-domain bootstrap and mandatory NAP-SHELL shim: hosts prepend it to `srcdoc` outside verified artifact bytes, install the parent-bound `shell.init` receiver, emit one `shell.ready`, and expose callable NAP interfaces before authored scripts run. Optional namespaces are filtered to the bare-domain allowlist; `shell` is always retained. Published core 0.31.1 and shim 0.29.2 omit generic mandatory shell, so Kehto retains this host-owned prelude under NAP-SHELL `5ac0490461ca6fec2f0d2e45b4835cf9bc08de24` until an upstream correction is reviewed.
37
+ - `injectNappletNamespacePrelude()` implements the NIP-5D injected-domain bootstrap and mandatory NAP-SHELL shim: hosts prepend it to `srcdoc` outside verified artifact bytes, install the parent-bound `shell.init` receiver, emit one `shell.ready`, and expose callable NAP interfaces before authored scripts run. Optional namespaces are filtered to the bare-domain allowlist; `shell` is always retained. Published core 0.32.0 and shim 0.30.0 omit generic mandatory shell, so Kehto retains this host-owned prelude under NAP-SHELL `5ac0490461ca6fec2f0d2e45b4835cf9bc08de24` until an upstream correction is reviewed.
38
+ - The injected resource projection follows draft NAP-RESOURCE `9511232f69313aa7953d110e35d32cc28d506f66`: `bytes(url, { servers? })` carries advisory Blossom locations and `bytesMany(requests)` keeps each `{ url, servers? }` entry intact.
38
39
  - `window.napplet.shell.supports(domain)` answers synchronously and locally from the cached first `shell.init` environment. It returns `false` before `shell.init`, for unknown values, and for domains that are not live and granted to that napplet; it never sends a support-query message.
39
40
  - Five optional per-domain proxies — `createIdentityProxy`, `createThemeProxy`, `createKeysProxy`, `createMediaProxy`, `createNotifyProxy` — can be composed between napplet and runtime to intercept request traffic per NAP. They are NOT wired by default. Identity/theme proxy `emit()` compatibility members fail closed; hosts must deliver automatic changes through `ShellBridge.publishIdentityChanged()` / `publishTheme()`, which enforce live session, granted domain, and current ACL.
40
41
  - `keys.forward` is napplet-to-shell only. Active napplets suppress locally-bound keys from `keys.bindings` before forwarding; shell-initiated action triggers use `keys.action`.
@@ -60,7 +61,7 @@ dispatch, and does not infer payload kinds. Runtime delivery supplies the
60
61
  **runtime-attested dTag**; no caller sender is accepted, topic source exclusion
61
62
  is runtime-owned, and IDs and payloads are opaque.
62
63
 
63
- Merged NAP-INC and released `@napplet/nap@0.31.2` both deliver one `IncEvent`
64
+ Merged NAP-INC and released `@napplet/nap@0.32.0` both deliver one `IncEvent`
64
65
  to `on(topic, callback)`.
65
66
 
66
67
  For channels, runtime ACL checks are open-only rather than per-message. The
package/dist/index.d.ts CHANGED
@@ -3,6 +3,8 @@ export { ALL_CAPABILITIES, AclChecker, Capability, ConsentRequest, EnforceConfig
3
3
  import { NappletMessage, NostrEvent, NostrFilter } from '@napplet/core';
4
4
  export { NappletMessage, NostrEvent, NostrFilter, TOPICS, TopicKey, TopicValue } from '@napplet/core';
5
5
  import { Theme } from '@napplet/nap/theme/types';
6
+ import { ResourceBytesItem, ResourceBytesRequest as ResourceBytesRequest$1, ResourceInfo } from '@napplet/nap/resource/types';
7
+ export { ResourceInfo } from '@napplet/nap/resource/types';
6
8
 
7
9
  /** NIP-5D identity metadata associated with a registered iframe window. */
8
10
  interface OriginIdentity {
@@ -1303,15 +1305,11 @@ declare function createNotifyProxy(deps: NotifyProxyDeps): NotifyProxy;
1303
1305
  * @file internal-resource.ts
1304
1306
  *
1305
1307
  * Kehto-internal shell-side resource wire types. Per PROJECT.md Decision #31,
1306
- * this is NOT a staging-ground duplicate of upstream `@napplet/nap/resource`:
1307
- * Phase 44 audit confirmed the two surfaces diverge substantively — different
1308
- * field names (kehto's legacy `requestId` vs upstream `id`; kehto's legacy
1309
- * `bodyBase64` vs upstream `blob` + `mime`), different message-type names
1310
- * (`ResourceBytesRequest` vs `ResourceBytesMessage`), and disjoint error
1311
- * vocabularies (kehto: 4 codes `{denied, canceled, network-error, invalid-url}`;
1312
- * upstream: 8 codes `{not-found, blocked-by-policy,
1313
- * timeout, too-large, unsupported-scheme, decode-failed, network-error,
1314
- * quota-exceeded}`).
1308
+ * this is NOT a staging-ground duplicate of upstream `@napplet/nap/resource`.
1309
+ * Canonical resource info, per-resource request, and result-item shapes are
1310
+ * imported from the published package. The remaining local envelopes retain
1311
+ * Kehto's legacy compatibility fields (`requestId`, `bodyBase64`, status, and
1312
+ * headers) alongside the current `id`, `blob`, and `mime` fields.
1315
1313
  *
1316
1314
  * `resource-service.ts` now emits both legacy single-fetch compatibility
1317
1315
  * fields and the current upstream-compatible fields.
@@ -1322,20 +1320,12 @@ declare function createNotifyProxy(deps: NotifyProxyDeps): NotifyProxy;
1322
1320
  * resource.bytes.result, resource.bytes.error,
1323
1321
  * resource.bytesMany.result, resource.bytesMany.error
1324
1322
  */
1323
+
1325
1324
  /**
1326
1325
  * Unique id for correlating a `resource.bytes` request to its later result /
1327
1326
  * error / cancel envelope.
1328
1327
  */
1329
1328
  type ResourceRequestId = string;
1330
- /** Advisory resource capability and policy limits disclosed by the runtime. */
1331
- interface ResourceInfo {
1332
- schemes: readonly {
1333
- scheme: string;
1334
- enabled: boolean;
1335
- }[];
1336
- maxBytes?: number;
1337
- maxUrls?: number;
1338
- }
1339
1329
  /** Inbound: napplet asks for advisory resource policy and scheme support. */
1340
1330
  interface ResourceInfoRequest {
1341
1331
  type: 'resource.info';
@@ -1353,6 +1343,8 @@ interface ResourceBytesRequest {
1353
1343
  /** Legacy Kehto correlation ID. */
1354
1344
  requestId?: ResourceRequestId;
1355
1345
  url: string;
1346
+ /** Advisory Blossom server locations. Ignored for other schemes. */
1347
+ servers?: readonly string[];
1356
1348
  /** Optional subset of fetch init (method, headers). Body bytes are shell-proxy-internal. */
1357
1349
  init?: {
1358
1350
  method?: string;
@@ -1377,7 +1369,7 @@ interface ResourceCancelRequest {
1377
1369
  interface ResourceBytesManyRequest {
1378
1370
  type: 'resource.bytesMany';
1379
1371
  id: ResourceRequestId;
1380
- urls: readonly string[];
1372
+ requests: readonly ResourceBytesRequest$1[];
1381
1373
  }
1382
1374
  /**
1383
1375
  * Outbound: successful fetch result.
@@ -1420,17 +1412,7 @@ interface ResourceBytesError {
1420
1412
  /**
1421
1413
  * Current NAP-RESOURCE bulk result item.
1422
1414
  */
1423
- type ResourceBytesManyItem = {
1424
- url: string;
1425
- ok: true;
1426
- blob: Blob;
1427
- mime: string;
1428
- } | {
1429
- url: string;
1430
- ok: false;
1431
- error: string;
1432
- message?: string;
1433
- };
1415
+ type ResourceBytesManyItem = ResourceBytesItem;
1434
1416
  /**
1435
1417
  * Outbound: ordered bulk fetch result.
1436
1418
  */
@@ -1470,4 +1452,4 @@ type ResourceInbound = ResourceInfoRequest | ResourceBytesRequest | ResourceByte
1470
1452
  */
1471
1453
  type ResourceOutbound = ResourceInfoResult | ResourceInfoError | ResourceBytesResult | ResourceBytesError | ResourceBytesManyResult | ResourceBytesManyError;
1472
1454
 
1473
- export { type AclCheckEvent, type AclEntry, type AclStore, type AudioManager, type AudioSource, type AuthHooks, type BleHooks, type BrowserDeps, type CapabilityHooks, type CommonHooks, type ConfigHooks, type CryptoHooks, type DmHooks, type HotkeyHooks, type IdentityProxy, type IdentityProxyDeps, type IntentHooks, type KeysProxy, type KeysProxyDeps, type LinkHooks, type ListsHooks, type ManifestCache, type ManifestCacheEntry, type MediaProxy, type MediaProxyDeps, type NappletNamespacePreludeOptions, type NotifyProxy, type NotifyProxyDeps, type OriginIdentity, type OriginRegistry, type ProxyOriginRegistry, type RelayConfigHooks, type RelayPoolHooks, type RelayPoolLike, type ResourceBytesError, type ResourceBytesManyError, type ResourceBytesManyItem, type ResourceBytesManyRequest, type ResourceBytesManyResult, type ResourceBytesRequest, type ResourceBytesResult, type ResourceCancelRequest, type ResourceErrorCode, type ResourceInbound, type ResourceInfo, type ResourceInfoError, type ResourceInfoRequest, type ResourceInfoResult, type ResourceOutbound, type ResourceRequestId, type SerialHooks, type SessionRegistry, type ShellAdapter, type ShellBridge, type ShellCapabilities, type ShellEnvironment, type ThemeProxy, type ThemeProxyDeps, type UnroutedMessageInfo, type UploadBackendLike, type UploadHooks, type WebrtcHooks, type WindowManagerHooks, type WorkerRelayHooks, type WorkerRelayLike, adaptHooks, audioManager, buildShellCapabilities, createIdentityProxy, createKeysProxy, createMediaProxy, createNotifyProxy, createShellBridge, createThemeProxy, injectNappletNamespacePrelude, manifestCache, nappKeyRegistry, originRegistry, renderNappletNamespacePrelude, resolveShellEnvironment, sessionRegistry };
1455
+ export { type AclCheckEvent, type AclEntry, type AclStore, type AudioManager, type AudioSource, type AuthHooks, type BleHooks, type BrowserDeps, type CapabilityHooks, type CommonHooks, type ConfigHooks, type CryptoHooks, type DmHooks, type HotkeyHooks, type IdentityProxy, type IdentityProxyDeps, type IntentHooks, type KeysProxy, type KeysProxyDeps, type LinkHooks, type ListsHooks, type ManifestCache, type ManifestCacheEntry, type MediaProxy, type MediaProxyDeps, type NappletNamespacePreludeOptions, type NotifyProxy, type NotifyProxyDeps, type OriginIdentity, type OriginRegistry, type ProxyOriginRegistry, type RelayConfigHooks, type RelayPoolHooks, type RelayPoolLike, type ResourceBytesError, type ResourceBytesManyError, type ResourceBytesManyItem, type ResourceBytesManyRequest, type ResourceBytesManyResult, type ResourceBytesRequest, type ResourceBytesResult, type ResourceCancelRequest, type ResourceErrorCode, type ResourceInbound, type ResourceInfoError, type ResourceInfoRequest, type ResourceInfoResult, type ResourceOutbound, type ResourceRequestId, type SerialHooks, type SessionRegistry, type ShellAdapter, type ShellBridge, type ShellCapabilities, type ShellEnvironment, type ThemeProxy, type ThemeProxyDeps, type UnroutedMessageInfo, type UploadBackendLike, type UploadHooks, type WebrtcHooks, type WindowManagerHooks, type WorkerRelayHooks, type WorkerRelayLike, adaptHooks, audioManager, buildShellCapabilities, createIdentityProxy, createKeysProxy, createMediaProxy, createNotifyProxy, createShellBridge, createThemeProxy, injectNappletNamespacePrelude, manifestCache, nappKeyRegistry, originRegistry, renderNappletNamespacePrelude, resolveShellEnvironment, sessionRegistry };
package/dist/index.js CHANGED
@@ -1302,16 +1302,29 @@ function nappletNamespacePrelude(domains) {
1302
1302
  function request(message, resultType, map, options = {}) {
1303
1303
  const requestId = message.id ?? id();
1304
1304
  const outbound = { ...message, id: requestId };
1305
+ const timeoutMs = options.timeoutMs === void 0 ? requestTimeoutMs : options.timeoutMs;
1306
+ if (options.signal?.aborted) return Promise.reject(new DOMException("Aborted", "AbortError"));
1305
1307
  return new Promise((resolve, reject) => {
1306
- const off = listen((event) => {
1308
+ let timer;
1309
+ let off;
1310
+ const cleanup = () => {
1311
+ off?.();
1312
+ if (timer !== void 0) clearTimeout(timer);
1313
+ options.signal?.removeEventListener("abort", abort);
1314
+ };
1315
+ const abort = () => {
1316
+ cleanup();
1317
+ if (options.cancelOnAbort) post({ type: "resource.cancel", id: requestId });
1318
+ reject(new DOMException("Aborted", "AbortError"));
1319
+ };
1320
+ off = listen((event) => {
1307
1321
  if (!isParentMessage(event)) return;
1308
1322
  const incoming = event.data;
1309
1323
  if (typeof incoming !== "object" || incoming === null) return;
1310
1324
  const msg = incoming;
1311
1325
  if (msg.type !== resultType && msg.type !== `${message.type}.error`) return;
1312
1326
  if (msg.id !== requestId) return;
1313
- off();
1314
- clearTimeout(timer);
1327
+ cleanup();
1315
1328
  const error = typeof msg.error === "string" ? msg.error : "";
1316
1329
  if (error && options.rejectOnError !== false) {
1317
1330
  reject(new Error(error));
@@ -1323,10 +1336,13 @@ function nappletNamespacePrelude(domains) {
1323
1336
  reject(err);
1324
1337
  }
1325
1338
  });
1326
- const timer = setTimeout(() => {
1327
- off();
1328
- reject(new Error(`${message.type} timed out`));
1329
- }, requestTimeoutMs);
1339
+ if (timeoutMs !== null) {
1340
+ timer = setTimeout(() => {
1341
+ cleanup();
1342
+ reject(new Error(`${message.type} timed out`));
1343
+ }, timeoutMs);
1344
+ }
1345
+ options.signal?.addEventListener("abort", abort, { once: true });
1330
1346
  post(outbound);
1331
1347
  });
1332
1348
  }
@@ -2071,11 +2087,29 @@ function nappletNamespacePrelude(domains) {
2071
2087
  }
2072
2088
  function makeResource() {
2073
2089
  const objectUrls = /* @__PURE__ */ new Set();
2074
- const bytes = (url) => request({ type: "resource.bytes", url }, "resource.bytes.result", (msg) => msg.blob);
2090
+ const bytes = (url, options) => request({
2091
+ type: "resource.bytes",
2092
+ url,
2093
+ ...Array.isArray(options?.servers) ? { servers: Array.from(options.servers) } : {}
2094
+ }, "resource.bytes.result", (msg) => msg.blob, {
2095
+ timeoutMs: null,
2096
+ signal: options?.signal,
2097
+ cancelOnAbort: true
2098
+ });
2075
2099
  return {
2076
2100
  info: () => request({ type: "resource.info" }, "resource.info.result", (msg) => msg.info),
2077
2101
  bytes,
2078
- bytesMany: (urls) => request({ type: "resource.bytesMany", urls }, "resource.bytesMany.result", (msg) => Array.isArray(msg.items) ? msg.items : []),
2102
+ bytesMany: (requests, options) => request({
2103
+ type: "resource.bytesMany",
2104
+ requests: Array.from(requests || [], (entry) => ({
2105
+ url: entry.url,
2106
+ ...Array.isArray(entry.servers) ? { servers: Array.from(entry.servers) } : {}
2107
+ }))
2108
+ }, "resource.bytesMany.result", (msg) => Array.isArray(msg.items) ? msg.items : [], {
2109
+ timeoutMs: null,
2110
+ signal: options?.signal,
2111
+ cancelOnAbort: true
2112
+ }),
2079
2113
  bytesAsObjectURL(url) {
2080
2114
  let objectUrl = "";
2081
2115
  const handle = {