@kehto/nip 0.6.0 → 0.7.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.
package/README.md CHANGED
@@ -26,19 +26,21 @@ bytes, see the repo guide:
26
26
 
27
27
  ## NIP-5D archetype manifest contracts
28
28
 
29
- Kehto follows merged [NAP-INTENT at
30
- `5ac0490461ca6fec2f0d2e45b4835cf9bc08de24`](https://github.com/napplet/naps/blob/5ac0490461ca6fec2f0d2e45b4835cf9bc08de24/naps/NAP-INTENT.md).
31
- An accepted manifest tag has the exact form
32
- `["archetype", "<slug>", "napplet:<slug>/<action>"]`.
33
- The convention is stable and queryless; each tag becomes one ordered
34
- archetype/convention declaration. Trailing metadata and numbered NAP names are
35
- rejected.
36
-
37
- `resolveNapplet()` exposes these verified tags as
38
- `manifest.archetypes: Array<{ slug, convention }>`. The NIP package
39
- parses and verifies that metadata but does not select handlers, retain intent
40
- delivery, or start target windows. Those responsibilities live in
41
- `@kehto/services` and the host runtime.
29
+ Kehto follows [NAP-INTENT PR #106](https://github.com/napplet/naps/blob/fc121fc264615482143eda86125863d2e1f741a2/naps/NAP-INTENT.md) at `fc121fc264615482143eda86125863d2e1f741a2` and NIP-5D PR #2303 at `020cb8b33a9e4c6b8ca4b2f9d0ed0a67843b68f7`.
30
+ Current manifests declare roles with `z` and contracts with
31
+ `["i", "napplet:<role>/<action>", ...parameterNames]`. Only an intent whose
32
+ role is also declared by `z` is eligible. Invalid advertisements are ignored;
33
+ they do not invalidate otherwise valid artifact metadata.
34
+
35
+ `resolveNapplet()` exposes `manifest.archetypes` as ordered
36
+ `{ slug, convention, params }` entries. Parameter names describe text fields,
37
+ not types or required values. Legacy paired archetype tags retain empty params.
38
+ `manifest.catalogId`, also available through `getNappletCatalogId(event)`, is
39
+ publisher/kind-safe: named events use kind, publisher and literal d-tag; roots
40
+ use kind and publisher; snapshots use kind and event ID. Callers treat this ID
41
+ as opaque. The existing dTag/artifact hash remains separate artifact metadata.
42
+
43
+ NIP utilities verify these facts; services and hosts select and run handlers.
42
44
 
43
45
  ## Selection criteria
44
46
 
@@ -90,8 +92,8 @@ Every NIP module is:
90
92
 
91
93
  ## NIP-5D event compatibility
92
94
 
93
- Current manifests use a direct artifact `x` hash, plain-text `content`, independent
94
- `z`/`i` routing declarations, and required `R` / optional `O` domains. Kehto also
95
+ Current manifests use a direct artifact `x` hash, plain-text `content`, role-matched
96
+ `z`/`i` intent declarations, and required `R` / optional `O` domains. Kehto also
95
97
  accepts legacy aggregate events through an isolated compatibility adapter.
96
98
  Existing `aggregateHash` host/cache/ACL fields carry the verified artifact hash
97
99
  for current events; legacy identities keep their original aggregate. Both paths
@@ -1,6 +1,16 @@
1
1
  import { NostrEvent } from 'nostr-tools';
2
2
  import { PathEntry } from '../5a/index.js';
3
3
 
4
+ /** NIP-5D napplet manifest event kinds. */
5
+ /** Snapshot manifest — regular event, immutable point-in-time release. */
6
+ declare const NAPPLET_KIND_SNAPSHOT = 5129;
7
+ /** Root manifest — replaceable event, an author's latest unnamed napplet. */
8
+ declare const NAPPLET_KIND_ROOT = 15129;
9
+ /** Named manifest — addressable event (carries a `d` tag identifier). */
10
+ declare const NAPPLET_KIND_NAMED = 35129;
11
+ /** All three NIP-5D napplet manifest kinds. */
12
+ declare const NAPPLET_KINDS: readonly number[];
13
+
4
14
  /** Versioned Cache Storage namespace for verified napplet artifacts. */
5
15
  declare const NAPPLET_ARTIFACT_CACHE_NAME = "kehto:napplet-artifacts:v1";
6
16
  interface NappletCacheDiagnostic {
@@ -233,6 +243,26 @@ declare class CacheStorageNappletArtifactCache implements NappletArtifactCache {
233
243
  */
234
244
  declare function openNappletArtifactCache(options?: OpenNappletArtifactCacheOptions): Promise<CacheStorageNappletArtifactCache | undefined>;
235
245
 
246
+ /** Publisher-safe catalog identities for verified NIP-5D manifest events. */
247
+
248
+ /**
249
+ * Derive the runtime catalog identity for a verified NIP-5D manifest event.
250
+ *
251
+ * Named and root manifests retain a stable publisher-scoped identity while a
252
+ * snapshot is identified by its immutable signed event id. Call this only after
253
+ * the event has passed signature and manifest verification.
254
+ *
255
+ * @param event - A verified NIP-5D manifest event.
256
+ * @returns The opaque publisher/kind-safe catalog ID.
257
+ *
258
+ * @example
259
+ * ```ts
260
+ * getNappletCatalogId(namedManifest);
261
+ * // → 'nip5d:35129:<publisher>:<literal-d-tag>'
262
+ * ```
263
+ */
264
+ declare function getNappletCatalogId(event: Pick<NostrEvent, 'id' | 'kind' | 'pubkey' | 'tags'>): string;
265
+
236
266
  /** Error codes for every napplet resolution failure path. */
237
267
  type NappletResolutionErrorCode = 'invalid-signature' | 'invalid-manifest' | 'aggregate-mismatch' | 'blob-hash-mismatch' | 'blob-unavailable' | 'missing-index';
238
268
  /**
@@ -245,21 +275,6 @@ declare class NappletResolutionError extends Error {
245
275
  constructor(code: NappletResolutionErrorCode, message: string);
246
276
  }
247
277
 
248
- /**
249
- * `@kehto/nip/5d` — verify signed napplet manifests and their content bytes.
250
- * Current events use one artifact x hash. The isolated legacy adapter accepts
251
- * path tags plus an aggregate x marker. Both formats share signature checking,
252
- * blob verification, caching, and verified srcdoc output.
253
- * @module
254
- */
255
- /** Snapshot manifest — regular event, immutable point-in-time release. */
256
- declare const NAPPLET_KIND_SNAPSHOT = 5129;
257
- /** Root manifest — replaceable event, an author's latest unnamed napplet. */
258
- declare const NAPPLET_KIND_ROOT = 15129;
259
- /** Named manifest — addressable event (carries a `d` tag identifier). */
260
- declare const NAPPLET_KIND_NAMED = 35129;
261
- /** All three NIP-5D napplet manifest kinds. */
262
- declare const NAPPLET_KINDS: readonly number[];
263
278
  /**
264
279
  * Whether `kind` is one of the three NIP-5D napplet manifest kinds.
265
280
  *
@@ -301,6 +316,8 @@ interface NappletManifest {
301
316
  pubkey: string;
302
317
  /** Named-napplet `d` identifier, or `''` for root/snapshot manifests. */
303
318
  dTag: string;
319
+ /** Publisher/kind-safe opaque identity for the verified manifest catalog. */
320
+ catalogId: string;
304
321
  /** Normalized files: one /index.html for current events, legacy path entries otherwise. */
305
322
  paths: PathEntry[];
306
323
  /** Compatibility name for the signed content identity (current artifact or legacy aggregate). */
@@ -315,6 +332,7 @@ interface NappletManifest {
315
332
  archetypes: Array<{
316
333
  slug: string;
317
334
  convention: string;
335
+ params: string[];
318
336
  }>;
319
337
  /** Optional human title. */
320
338
  title?: string;
@@ -417,4 +435,4 @@ interface ResolveNappletOptions {
417
435
  */
418
436
  declare function resolveNapplet(options: ResolveNappletOptions): Promise<ResolvedNapplet>;
419
437
 
420
- export { CacheStorageNappletArtifactCache, type CacheStorageNappletArtifactCacheOptions, type CachedCoordinate, type CoordinateFreshnessOptions, NAPPLET_ARTIFACT_CACHE_NAME, NAPPLET_KINDS, NAPPLET_KIND_NAMED, NAPPLET_KIND_ROOT, NAPPLET_KIND_SNAPSHOT, type NappletAggregateIndexEntry, type NappletArtifactCache, type NappletArtifactCacheIndex, type NappletBlobIndexEntry, type NappletCacheDiagnostic, type NappletManifest, NappletResolutionError, type NappletResolutionErrorCode, type OpenNappletArtifactCacheOptions, type ResolveNappletOptions, type ResolvedNapplet, type WriteVerifiedResolutionInput, coordinateKey, fetchBlob, isCoordinateFresh, isNappletManifestKind, openNappletArtifactCache, parseNappletManifest, resolveNapplet, verifyBlobHash, verifyManifestSignature };
438
+ export { CacheStorageNappletArtifactCache, type CacheStorageNappletArtifactCacheOptions, type CachedCoordinate, type CoordinateFreshnessOptions, NAPPLET_ARTIFACT_CACHE_NAME, NAPPLET_KINDS, NAPPLET_KIND_NAMED, NAPPLET_KIND_ROOT, NAPPLET_KIND_SNAPSHOT, type NappletAggregateIndexEntry, type NappletArtifactCache, type NappletArtifactCacheIndex, type NappletBlobIndexEntry, type NappletCacheDiagnostic, type NappletManifest, NappletResolutionError, type NappletResolutionErrorCode, type OpenNappletArtifactCacheOptions, type ResolveNappletOptions, type ResolvedNapplet, type WriteVerifiedResolutionInput, coordinateKey, fetchBlob, getNappletCatalogId, isCoordinateFresh, isNappletManifestKind, openNappletArtifactCache, parseNappletManifest, resolveNapplet, verifyBlobHash, verifyManifestSignature };
package/dist/5d/index.js CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  NappletResolutionError,
9
9
  coordinateKey,
10
10
  fetchBlob,
11
+ getNappletCatalogId,
11
12
  isCoordinateFresh,
12
13
  isNappletManifestKind,
13
14
  openNappletArtifactCache,
@@ -15,7 +16,7 @@ import {
15
16
  resolveNapplet,
16
17
  verifyBlobHash,
17
18
  verifyManifestSignature
18
- } from "../chunk-3JJBAODT.js";
19
+ } from "../chunk-AKQIPGRC.js";
19
20
  import "../chunk-JT5WCCCX.js";
20
21
  export {
21
22
  CacheStorageNappletArtifactCache,
@@ -27,6 +28,7 @@ export {
27
28
  NappletResolutionError,
28
29
  coordinateKey,
29
30
  fetchBlob,
31
+ getNappletCatalogId,
30
32
  isCoordinateFresh,
31
33
  isNappletManifestKind,
32
34
  openNappletArtifactCache,
@@ -19,6 +19,35 @@ var NappletResolutionError = class extends Error {
19
19
  }
20
20
  };
21
21
 
22
+ // src/5d/kinds.ts
23
+ var NAPPLET_KIND_SNAPSHOT = 5129;
24
+ var NAPPLET_KIND_ROOT = 15129;
25
+ var NAPPLET_KIND_NAMED = 35129;
26
+ var NAPPLET_KINDS = [
27
+ NAPPLET_KIND_SNAPSHOT,
28
+ NAPPLET_KIND_ROOT,
29
+ NAPPLET_KIND_NAMED
30
+ ];
31
+
32
+ // src/5d/catalog-id.ts
33
+ var PUBKEY = /^[a-f0-9]{64}$/;
34
+ function getNappletCatalogId(event) {
35
+ if (!PUBKEY.test(event.pubkey)) throw new TypeError("napplet manifest pubkey is invalid");
36
+ if (event.kind === NAPPLET_KIND_SNAPSHOT) {
37
+ if (!/^[a-f0-9]{64}$/.test(event.id)) throw new TypeError("napplet snapshot id is invalid");
38
+ return `nip5d:${NAPPLET_KIND_SNAPSHOT}:${event.id}`;
39
+ }
40
+ if (event.kind === NAPPLET_KIND_ROOT) return `nip5d:${NAPPLET_KIND_ROOT}:${event.pubkey}`;
41
+ if (event.kind === NAPPLET_KIND_NAMED) {
42
+ const dTags = event.tags.filter((tag) => tag[0] === "d");
43
+ if (dTags.length !== 1 || dTags[0].length !== 2 || !dTags[0][1]) {
44
+ throw new TypeError("named napplet manifest must have one non-empty d tag");
45
+ }
46
+ return `nip5d:${NAPPLET_KIND_NAMED}:${event.pubkey}:${dTags[0][1]}`;
47
+ }
48
+ throw new TypeError(`unsupported napplet manifest kind: ${event.kind}`);
49
+ }
50
+
22
51
  // src/5d/legacy-manifest.ts
23
52
  function firstTagValue(tags, name) {
24
53
  for (const tag of tags) {
@@ -66,7 +95,7 @@ function archetypesFromTags(tags) {
66
95
  "archetype tags must contain exactly slug and convention"
67
96
  );
68
97
  }
69
- out.push({ slug, convention });
98
+ out.push({ slug, convention, params: [] });
70
99
  }
71
100
  return out;
72
101
  }
@@ -90,6 +119,7 @@ function parseLegacyManifest(event) {
90
119
  kind: event.kind,
91
120
  pubkey: event.pubkey,
92
121
  dTag: dTag ?? "",
122
+ catalogId: getNappletCatalogId(event),
93
123
  paths,
94
124
  aggregateHash,
95
125
  servers: allTagValues(event.tags, "server"),
@@ -120,7 +150,6 @@ function warnLegacyManifest() {
120
150
  var HASH = /^[a-f0-9]{64}$/;
121
151
  var DOMAIN = /^[a-z][a-z0-9-]*$/;
122
152
  var SLUG = /^[a-z0-9][a-z0-9-]*$/;
123
- var INTENT = /^napplet:[^/?#\s]+\/[^/?#\s]+$/;
124
153
  function invalid(message) {
125
154
  throw new NappletResolutionError("invalid-manifest", message);
126
155
  }
@@ -137,6 +166,19 @@ function declarations(tags, name, pattern) {
137
166
  return tag[1];
138
167
  }))];
139
168
  }
169
+ function intentDeclarations(tags, roles) {
170
+ const roleSet = new Set(roles);
171
+ const contracts = [];
172
+ for (const tag of tags) {
173
+ if (tag[0] !== "i") continue;
174
+ const [, identity, ...params] = tag;
175
+ const match = typeof identity === "string" ? /^napplet:([a-z0-9][a-z0-9-]*)\/[^/?#\s]+$/.exec(identity) : null;
176
+ if (!match || params.some((param) => !param || /\s/.test(param))) continue;
177
+ if (!roleSet.has(match[1])) continue;
178
+ contracts.push({ slug: match[1], convention: identity, params });
179
+ }
180
+ return contracts;
181
+ }
140
182
  function iconFromTags(tags) {
141
183
  const icons = tags.filter((tag) => tag[0] === "icon");
142
184
  if (icons.length !== 1) return void 0;
@@ -166,19 +208,15 @@ function parseCurrentManifest(event) {
166
208
  invalid("lineage must be a napplet address on a snapshot");
167
209
  }
168
210
  }
169
- const archetypeSlugs = declarations(tags, "z", SLUG);
170
- const intents = tags.filter((tag) => tag[0] === "i").map((tag) => {
171
- const [, identity, ...parameters] = tag;
172
- if (!identity || !INTENT.test(identity) || parameters.some((parameter) => !parameter || /\s/.test(parameter))) {
173
- invalid("invalid accepted intent or parameter name");
174
- }
175
- return { identity, parameters };
176
- });
211
+ const archetypeSlugs = [...new Set(tags.filter((tag) => tag[0] === "z" && tag.length === 2 && SLUG.test(tag[1])).map((tag) => tag[1]))];
212
+ const archetypes = intentDeclarations(tags, archetypeSlugs);
213
+ const intents = archetypes.map(({ convention: identity, params: parameters }) => ({ identity, parameters }));
177
214
  return {
178
215
  format: "current",
179
216
  kind: event.kind,
180
217
  pubkey: event.pubkey,
181
218
  dTag: dTag ?? "",
219
+ catalogId: getNappletCatalogId(event),
182
220
  artifactHash,
183
221
  aggregateHash: artifactHash,
184
222
  paths: [{ path: "/index.html", sha256: artifactHash }],
@@ -187,12 +225,7 @@ function parseCurrentManifest(event) {
187
225
  optional: declarations(tags, "O", DOMAIN),
188
226
  archetypeSlugs,
189
227
  intents,
190
- // NAP-INTENT keeps roles and conventions orthogonal. The current schema
191
- // advertises independent sets; legacy explicit pairings stay in its adapter.
192
- archetypes: archetypeSlugs.flatMap((slug) => intents.map(({ identity }) => ({
193
- slug,
194
- convention: identity
195
- }))),
228
+ archetypes,
196
229
  title: single(tags, "title"),
197
230
  description: event.content,
198
231
  source: single(tags, "source"),
@@ -603,14 +636,6 @@ async function openNappletArtifactCache(options = {}) {
603
636
  }
604
637
 
605
638
  // src/5d/index.ts
606
- var NAPPLET_KIND_SNAPSHOT = 5129;
607
- var NAPPLET_KIND_ROOT = 15129;
608
- var NAPPLET_KIND_NAMED = 35129;
609
- var NAPPLET_KINDS = [
610
- NAPPLET_KIND_SNAPSHOT,
611
- NAPPLET_KIND_ROOT,
612
- NAPPLET_KIND_NAMED
613
- ];
614
639
  function isNappletManifestKind(kind) {
615
640
  return NAPPLET_KINDS.includes(kind);
616
641
  }
@@ -694,15 +719,16 @@ async function resolveNapplet(options) {
694
719
 
695
720
  export {
696
721
  NappletResolutionError,
722
+ NAPPLET_KIND_SNAPSHOT,
723
+ NAPPLET_KIND_ROOT,
724
+ NAPPLET_KIND_NAMED,
725
+ NAPPLET_KINDS,
726
+ getNappletCatalogId,
697
727
  NAPPLET_ARTIFACT_CACHE_NAME,
698
728
  coordinateKey,
699
729
  isCoordinateFresh,
700
730
  CacheStorageNappletArtifactCache,
701
731
  openNappletArtifactCache,
702
- NAPPLET_KIND_SNAPSHOT,
703
- NAPPLET_KIND_ROOT,
704
- NAPPLET_KIND_NAMED,
705
- NAPPLET_KINDS,
706
732
  isNappletManifestKind,
707
733
  parseNappletManifest,
708
734
  verifyManifestSignature,
@@ -710,4 +736,4 @@ export {
710
736
  fetchBlob,
711
737
  resolveNapplet
712
738
  };
713
- //# sourceMappingURL=chunk-3JJBAODT.js.map
739
+ //# sourceMappingURL=chunk-AKQIPGRC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/5d/index.ts","../src/5d/errors.ts","../src/5d/kinds.ts","../src/5d/catalog-id.ts","../src/5d/legacy-manifest.ts","../src/5d/current-manifest.ts","../src/5d/artifact-cache.ts"],"sourcesContent":["import type { NostrEvent } from 'nostr-tools';\nimport { verifyEvent } from 'nostr-tools/pure';\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport { bytesToHex } from '@noble/hashes/utils.js';\nimport type { PathEntry } from '../5a/index.js';\nimport { NappletResolutionError } from './errors.js';\nimport { parseLegacyManifest, verifyLegacyAggregate, warnLegacyManifest } from './legacy-manifest.js';\nimport { parseCurrentManifest } from './current-manifest.js';\nimport {\n NAPPLET_KIND_NAMED,\n NAPPLET_KIND_ROOT,\n NAPPLET_KIND_SNAPSHOT,\n NAPPLET_KINDS,\n} from './kinds.js';\nimport type { NappletArtifactCache } from './artifact-cache.js';\nexport {\n CacheStorageNappletArtifactCache,\n NAPPLET_ARTIFACT_CACHE_NAME,\n coordinateKey,\n isCoordinateFresh,\n openNappletArtifactCache,\n type CachedCoordinate,\n type CacheStorageNappletArtifactCacheOptions,\n type CoordinateFreshnessOptions,\n type NappletAggregateIndexEntry,\n type NappletArtifactCacheIndex,\n type NappletArtifactCache,\n type NappletBlobIndexEntry,\n type NappletCacheDiagnostic,\n type OpenNappletArtifactCacheOptions,\n type WriteVerifiedResolutionInput,\n} from './artifact-cache.js';\n\n/**\n * `@kehto/nip/5d` — verify signed napplet manifests and their content bytes.\n * Current events use one artifact x hash. The isolated legacy adapter accepts\n * path tags plus an aggregate x marker. Both formats share signature checking,\n * blob verification, caching, and verified srcdoc output.\n * @module\n */\n\n/** Snapshot manifest — regular event, immutable point-in-time release. */\nexport { NAPPLET_KIND_SNAPSHOT, NAPPLET_KIND_ROOT, NAPPLET_KIND_NAMED, NAPPLET_KINDS };\n/** Root manifest — replaceable event, an author's latest unnamed napplet. */\n/** Named manifest — addressable event (carries a `d` tag identifier). */\n\n/** All three NIP-5D napplet manifest kinds. */\nexport { getNappletCatalogId } from './catalog-id.js';\n\n/**\n * Whether `kind` is one of the three NIP-5D napplet manifest kinds.\n *\n * @param kind - A Nostr event kind\n * @returns `true` for `5129` / `15129` / `35129`\n */\nexport function isNappletManifestKind(kind: number): boolean {\n return NAPPLET_KINDS.includes(kind);\n}\n\n/**\n * A parsed NIP-5D napplet manifest. All fields are derived from the manifest\n * event. Declarations are untrusted until {@link resolveNapplet} verifies both\n * the signature and the content bytes.\n */\nexport interface NappletManifest {\n /** Event format selected by the parser. Absent only in older host-created values. */\n format?: 'current' | 'legacy';\n /** Signed artifact hash for current events; legacy aggregate for older events. */\n artifactHash?: string;\n /** Optional domains; absence never prevents loading and presence grants nothing. */\n optional?: string[];\n /** Independent current z declarations, preserved even without accepted intents. */\n archetypeSlugs?: string[];\n /** Accepted queryless intents and advertised parameter names from i tags. */\n intents?: Array<{ identity: string; parameters: string[] }>;\n /** Valid supported icon declaration. Hosts may keep generic artwork. */\n icon?: { sha256: string; mimeType: 'image/png' | 'image/jpeg' | 'image/webp' };\n /** Snapshot provenance only; never resolved or trusted on its behalf. */\n parent?: string;\n /** Snapshot root provenance only. */\n root?: string;\n /** Manifest event kind (`5129` / `15129` / `35129`). */\n kind: number;\n /** Author hex public key. */\n pubkey: string;\n /** Named-napplet `d` identifier, or `''` for root/snapshot manifests. */\n dTag: string;\n /** Publisher/kind-safe opaque identity for the verified manifest catalog. */\n catalogId: string;\n /** Normalized files: one /index.html for current events, legacy path entries otherwise. */\n paths: PathEntry[];\n /** Compatibility name for the signed content identity (current artifact or legacy aggregate). */\n aggregateHash: string;\n /** Blossom server URL hints from `server` tags. */\n servers: string[];\n /** Required bare domains from current R tags or legacy requires tags. */\n requires: string[];\n /**\n * Routing projection: independent current z/i sets, or exact legacy archetype pairs.\n */\n archetypes: Array<{ slug: string; convention: string; params: string[] }>;\n /** Optional human title. */\n title?: string;\n /** Plain-text event content (legacy description tag for older events). */\n description?: string;\n /** Optional upstream source URL from the `source` tag. */\n source?: string;\n}\n\nexport { NappletResolutionError, type NappletResolutionErrorCode } from './errors.js';\n\n/**\n * Normalize a current or legacy NIP-5D event without verifying its signature.\n * @param event - Candidate manifest; use resolveNapplet before trusting it.\n * @returns Manifest with normalized identity, paths, and routing declarations.\n * @example\n * const manifest = parseNappletManifest(event);\n */\nexport function parseNappletManifest(event: NostrEvent): NappletManifest {\n if (!isNappletManifestKind(event.kind)) {\n throw new NappletResolutionError('invalid-manifest', `not a NIP-5D napplet kind: ${event.kind}`);\n }\n const hashes = event.tags.filter((tag) => tag[0] === 'x');\n if (hashes.length !== 1) {\n throw new NappletResolutionError('invalid-manifest', 'manifest must carry exactly one x tag');\n }\n // Explicit format selection, never retry a malformed current event as legacy.\n return hashes[0][2] === 'aggregate'\n ? parseLegacyManifest(event)\n : parseCurrentManifest(event);\n}\n\n/**\n * Verify a manifest event's Nostr signature (id + schnorr sig).\n *\n * @param event - A manifest event\n * @returns `true` if the event is internally consistent and validly signed\n */\nexport function verifyManifestSignature(event: NostrEvent): boolean {\n try {\n return verifyEvent(event);\n } catch {\n return false;\n }\n}\n\n/**\n * Whether `bytes` hash to the expected lowercase-hex SHA-256.\n *\n * @param bytes - The blob bytes\n * @param sha256Hex - Expected lowercase-hex SHA-256\n * @returns `true` only on an exact match\n */\nexport function verifyBlobHash(bytes: Uint8Array, sha256Hex: string): boolean {\n return bytesToHex(sha256(bytes)) === sha256Hex;\n}\n\nfunction stripTrailingSlash(url: string): string {\n return url.endsWith('/') ? url.slice(0, -1) : url;\n}\n\n/**\n * Fetch a blob by SHA-256 from a list of Blossom servers, returning the first\n * server's bytes whose hash matches. The returned bytes are always re-verified\n * against `sha256Hex` — servers (and gateways) are never trusted.\n *\n * @param servers - Candidate Blossom server base URLs, tried in order\n * @param sha256Hex - The blob's lowercase-hex SHA-256\n * @param fetchBytes - Fetches raw bytes for a URL (`<server>/<sha256>`)\n * @returns The verified blob bytes\n * @throws {@link NappletResolutionError} `blob-unavailable` if no server serves\n * a hash-matching blob\n */\nexport async function fetchBlob(\n servers: readonly string[],\n sha256Hex: string,\n fetchBytes: (url: string) => Promise<Uint8Array>,\n): Promise<Uint8Array> {\n for (const server of servers) {\n const url = `${stripTrailingSlash(server)}/${sha256Hex}`;\n try {\n const bytes = await fetchBytes(url);\n if (verifyBlobHash(bytes, sha256Hex)) return bytes;\n } catch {\n // try the next server\n }\n }\n throw new NappletResolutionError('blob-unavailable', `no server served blob ${sha256Hex}`);\n}\n\n/** A fully verified napplet, ready to inject via `iframe.srcdoc`. */\nexport interface ResolvedNapplet {\n /** Verified content identity; current artifact SHA-256 or legacy aggregate. */\n artifactHash: string;\n /** Computed `d` identifier (`''` for root/snapshot). */\n dTag: string;\n /** Compatibility alias for artifactHash; preserves existing host and ACL APIs. */\n aggregateHash: string;\n /** Verified file bytes keyed by manifest path. */\n files: Map<string, Uint8Array>;\n /** The verified `/index.html` decoded to text. */\n indexHtml: string;\n /** The parsed manifest. */\n manifest: NappletManifest;\n}\n\n/** Options for {@link resolveNapplet}. */\nexport interface ResolveNappletOptions {\n /** The candidate manifest event (resolved from relays by the caller). */\n event: NostrEvent;\n /**\n * Fetch raw blob bytes by SHA-256. Backed by Blossom (or a gateway) by the\n * caller. The bytes are re-verified against the hash here, so the fetcher is\n * untrusted.\n */\n fetchBlob: (sha256Hex: string, servers: readonly string[]) => Promise<Uint8Array>;\n /** Decode blob bytes to text for `indexHtml` (default UTF-8). */\n textDecode?: (bytes: Uint8Array) => string;\n /**\n * Optional verified artifact cache. Cache hits are still re-verified against\n * the manifest hash before use; cache writes happen only after the signature,\n * content identity, and every blob hash have been verified.\n */\n cache?: NappletArtifactCache;\n}\n\nconst INDEX_PATHS = ['/index.html', 'index.html', '/'];\n\nfunction defaultDecode(bytes: Uint8Array): string {\n return new TextDecoder().decode(bytes);\n}\n\n/**\n * Resolve a napplet end-to-end from a candidate manifest event: verify the\n * signature, parse its schema, fetch and hash its artifact, and return verified\n * `/index.html`. Legacy events additionally verify their NIP-5A aggregate.\n *\n * The returned `(dTag, aggregateHash)` is computed from the verified bytes and\n * is the napplet's identity. Any failure throws a {@link NappletResolutionError}\n * — the caller must fail closed (never render unverified bytes).\n *\n * @param options - {@link ResolveNappletOptions}\n * @returns The {@link ResolvedNapplet}\n * @throws {@link NappletResolutionError} on signature, manifest, aggregate,\n * blob-hash, blob-availability, or missing-index failures\n *\n * @example\n * ```ts\n * import { resolveNapplet } from '@kehto/nip/5d';\n * const napplet = await resolveNapplet({ event, fetchBlob });\n * iframe.srcdoc = napplet.indexHtml; // sandbox=\"allow-scripts\", opaque origin\n * ```\n */\nexport async function resolveNapplet(options: ResolveNappletOptions): Promise<ResolvedNapplet> {\n const { event, fetchBlob: fetchBlobBytes, textDecode = defaultDecode, cache } = options;\n\n if (!verifyManifestSignature(event)) {\n throw new NappletResolutionError('invalid-signature', 'manifest signature is invalid');\n }\n\n const manifest = parseNappletManifest(event);\n\n if (manifest.format === 'legacy') verifyLegacyAggregate(manifest);\n\n const files = new Map<string, Uint8Array>();\n for (const entry of manifest.paths) {\n let bytes = await cache?.readBlob(entry.sha256);\n if (bytes && !verifyBlobHash(bytes, entry.sha256)) {\n await cache?.deleteBlob(entry.sha256);\n bytes = undefined;\n }\n bytes ??= await fetchBlobBytes(entry.sha256, manifest.servers);\n if (!verifyBlobHash(bytes, entry.sha256)) {\n throw new NappletResolutionError(\n 'blob-hash-mismatch',\n `blob for ${entry.path} does not match hash ${entry.sha256}`,\n );\n }\n files.set(entry.path, bytes);\n }\n\n const indexEntry = manifest.paths.find((e) => INDEX_PATHS.includes(e.path));\n if (!indexEntry) {\n throw new NappletResolutionError('missing-index', 'manifest has no /index.html entry');\n }\n\n const indexHtml = textDecode(files.get(indexEntry.path)!);\n await cache?.writeVerifiedResolution({ event, manifest, files, indexHtml });\n if (manifest.format === 'legacy') warnLegacyManifest();\n\n return {\n artifactHash: manifest.aggregateHash,\n dTag: manifest.dTag,\n aggregateHash: manifest.aggregateHash,\n files,\n indexHtml,\n manifest,\n };\n}\n","/** Error codes for every napplet resolution failure path. */\nexport type NappletResolutionErrorCode =\n | 'invalid-signature'\n | 'invalid-manifest'\n | 'aggregate-mismatch'\n | 'blob-hash-mismatch'\n | 'blob-unavailable'\n | 'missing-index';\n\n/**\n * Thrown on any napplet resolution/verification failure. The `code` field\n * identifies which guard rejected, so callers can fail closed without parsing\n * the message.\n */\nexport class NappletResolutionError extends Error {\n readonly code: NappletResolutionErrorCode;\n constructor(code: NappletResolutionErrorCode, message: string) {\n super(message);\n this.name = 'NappletResolutionError';\n this.code = code;\n }\n}\n\n","/** NIP-5D napplet manifest event kinds. */\n\n/** Snapshot manifest — regular event, immutable point-in-time release. */\nexport const NAPPLET_KIND_SNAPSHOT = 5129;\n/** Root manifest — replaceable event, an author's latest unnamed napplet. */\nexport const NAPPLET_KIND_ROOT = 15129;\n/** Named manifest — addressable event (carries a `d` tag identifier). */\nexport const NAPPLET_KIND_NAMED = 35129;\n\n/** All three NIP-5D napplet manifest kinds. */\nexport const NAPPLET_KINDS: readonly number[] = [\n NAPPLET_KIND_SNAPSHOT,\n NAPPLET_KIND_ROOT,\n NAPPLET_KIND_NAMED,\n];\n","/** Publisher-safe catalog identities for verified NIP-5D manifest events. */\n\nimport type { NostrEvent } from 'nostr-tools';\nimport {\n NAPPLET_KIND_NAMED,\n NAPPLET_KIND_ROOT,\n NAPPLET_KIND_SNAPSHOT,\n} from './kinds.js';\n\nconst PUBKEY = /^[a-f0-9]{64}$/;\n\n/**\n * Derive the runtime catalog identity for a verified NIP-5D manifest event.\n *\n * Named and root manifests retain a stable publisher-scoped identity while a\n * snapshot is identified by its immutable signed event id. Call this only after\n * the event has passed signature and manifest verification.\n *\n * @param event - A verified NIP-5D manifest event.\n * @returns The opaque publisher/kind-safe catalog ID.\n *\n * @example\n * ```ts\n * getNappletCatalogId(namedManifest);\n * // → 'nip5d:35129:<publisher>:<literal-d-tag>'\n * ```\n */\nexport function getNappletCatalogId(event: Pick<NostrEvent, 'id' | 'kind' | 'pubkey' | 'tags'>): string {\n if (!PUBKEY.test(event.pubkey)) throw new TypeError('napplet manifest pubkey is invalid');\n if (event.kind === NAPPLET_KIND_SNAPSHOT) {\n if (!/^[a-f0-9]{64}$/.test(event.id)) throw new TypeError('napplet snapshot id is invalid');\n return `nip5d:${NAPPLET_KIND_SNAPSHOT}:${event.id}`;\n }\n if (event.kind === NAPPLET_KIND_ROOT) return `nip5d:${NAPPLET_KIND_ROOT}:${event.pubkey}`;\n if (event.kind === NAPPLET_KIND_NAMED) {\n const dTags = event.tags.filter((tag) => tag[0] === 'd');\n if (dTags.length !== 1 || dTags[0].length !== 2 || !dTags[0][1]) {\n throw new TypeError('named napplet manifest must have one non-empty d tag');\n }\n // `d` is opaque and case-sensitive: deliberately preserve it byte-for-byte.\n return `nip5d:${NAPPLET_KIND_NAMED}:${event.pubkey}:${dTags[0][1]}`;\n }\n throw new TypeError(`unsupported napplet manifest kind: ${event.kind}`);\n}\n","// Removal boundary for pre-0.15 @napplet/vite-plugin event compatibility.\nimport type { NostrEvent } from 'nostr-tools';\nimport { computeAggregateHash, pathEntriesFromTags, aggregateTagValue } from '../5a/index.js';\nimport type { NappletManifest } from './index.js';\nimport { NappletResolutionError } from './errors.js';\nimport { getNappletCatalogId } from './catalog-id.js';\n\nfunction firstTagValue(tags: readonly (readonly string[])[], name: string): string | undefined {\n for (const tag of tags) {\n if (tag[0] === name && typeof tag[1] === 'string' && tag[1].length > 0) return tag[1];\n }\n return undefined;\n}\n\nfunction allTagValues(tags: readonly (readonly string[])[], name: string): string[] {\n const out: string[] = [];\n for (const tag of tags) {\n if (tag[0] === name && typeof tag[1] === 'string' && tag[1].length > 0) out.push(tag[1]);\n }\n return out;\n}\n\nfunction archetypesFromTags(\n tags: readonly (readonly string[])[],\n): Array<{ slug: string; convention: string; params: string[] }> {\n const out: Array<{ slug: string; convention: string; params: string[] }> = [];\n for (const tag of tags) {\n if (tag[0] !== 'archetype') continue;\n const slug = tag[1];\n if (typeof slug !== 'string' || !/^[a-z0-9][a-z0-9-]*$/.test(slug)) {\n throw new NappletResolutionError(\n 'invalid-manifest',\n 'archetype slug must contain lowercase letters, numbers, and hyphens',\n );\n }\n\n const convention = tag[2];\n if (typeof convention !== 'string' || convention.length === 0) {\n throw new NappletResolutionError('invalid-manifest', 'archetype convention is required');\n }\n if (/^NAP-\\d+$/.test(convention)) {\n throw new NappletResolutionError(\n 'invalid-manifest',\n 'numbered NAP identifier is not an archetype convention',\n );\n }\n if (!/^napplet:[^/?#\\s]+\\/[^/?#\\s]+$/.test(convention)) {\n throw new NappletResolutionError(\n 'invalid-manifest',\n 'archetype convention must be a queryless napplet:<archetype>/<intent> identity',\n );\n }\n // NAP-INTENT keeps a routing archetype and a payload convention\n // orthogonal: one convention may serve multiple archetypes and vice versa.\n\n if (tag.length !== 3) {\n throw new NappletResolutionError(\n 'invalid-manifest',\n 'archetype tags must contain exactly slug and convention',\n );\n }\n out.push({ slug, convention, params: [] });\n }\n return out;\n}\n\nexport function parseLegacyManifest(event: NostrEvent): NappletManifest {\n const paths = pathEntriesFromTags(event.tags);\n if (paths.length === 0) {\n throw new NappletResolutionError('invalid-manifest', 'manifest has no path tags');\n }\n const aggregateHash = aggregateTagValue(event.tags);\n if (!aggregateHash) {\n throw new NappletResolutionError('invalid-manifest', 'manifest has no aggregate x tag');\n }\n const dTag = firstTagValue(event.tags, 'd');\n if (event.kind !== 35129 && dTag !== undefined) {\n throw new NappletResolutionError('invalid-manifest', 'only named manifests may carry a d tag');\n }\n return {\n format: 'legacy',\n artifactHash: aggregateHash,\n optional: [],\n kind: event.kind,\n pubkey: event.pubkey,\n dTag: dTag ?? '',\n catalogId: getNappletCatalogId(event),\n paths,\n aggregateHash,\n servers: allTagValues(event.tags, 'server'),\n requires: allTagValues(event.tags, 'requires'),\n archetypes: archetypesFromTags(event.tags),\n title: firstTagValue(event.tags, 'title'),\n description: firstTagValue(event.tags, 'description'),\n source: firstTagValue(event.tags, 'source'),\n };\n}\n\n\n/** Compatibility-only aggregate check; current manifests never enter this path. */\nexport function verifyLegacyAggregate(manifest: NappletManifest): void {\n const recomputed = computeAggregateHash(manifest.paths);\n if (recomputed !== manifest.aggregateHash) {\n throw new NappletResolutionError('aggregate-mismatch',\n `recomputed aggregate ${recomputed} != manifest ${manifest.aggregateHash}`);\n }\n}\n\nlet warnedLegacyManifest = false;\n\n/** Warn once per module lifetime, after a legacy artifact has passed verification. */\nexport function warnLegacyManifest(): void {\n if (warnedLegacyManifest) return;\n warnedLegacyManifest = true;\n console.warn('[KEHTO_COMPAT_LEGACY_MANIFEST] Legacy aggregate manifest resolved. Republish with @napplet/vite-plugin >=0.15 using the current single-artifact schema. See docs/compatibility.md.');\n}\n","import type { NostrEvent } from 'nostr-tools';\nimport type { NappletManifest } from './index.js';\nimport { NappletResolutionError } from './errors.js';\nimport { getNappletCatalogId } from './catalog-id.js';\n\nconst HASH = /^[a-f0-9]{64}$/;\nconst DOMAIN = /^[a-z][a-z0-9-]*$/;\nconst SLUG = /^[a-z0-9][a-z0-9-]*$/;\n\nfunction invalid(message: string): never {\n throw new NappletResolutionError('invalid-manifest', message);\n}\n\nfunction single(tags: string[][], name: string): string | undefined {\n const matches = tags.filter((tag) => tag[0] === name);\n if (matches.length > 1 || matches.some((tag) => tag.length !== 2 || !tag[1])) {\n invalid(`manifest ${name} tag must have one value and occur at most once`);\n }\n return matches[0]?.[1];\n}\n\nfunction declarations(tags: string[][], name: string, pattern: RegExp): string[] {\n return [...new Set(tags.filter((tag) => tag[0] === name).map((tag) => {\n if (tag.length !== 2 || !pattern.test(tag[1])) invalid(`invalid ${name} declaration`);\n return tag[1];\n }))];\n}\n\nfunction intentDeclarations(tags: string[][], roles: readonly string[]): Array<{\n slug: string;\n convention: string;\n params: string[];\n}> {\n const roleSet = new Set(roles);\n const contracts: Array<{ slug: string; convention: string; params: string[] }> = [];\n for (const tag of tags) {\n if (tag[0] !== 'i') continue;\n const [, identity, ...params] = tag;\n const match = typeof identity === 'string' ? /^napplet:([a-z0-9][a-z0-9-]*)\\/[^/?#\\s]+$/.exec(identity) : null;\n if (!match || params.some((param) => !param || /\\s/.test(param))) continue;\n // A current `i` advertisement is eligible only for the same declared role.\n if (!roleSet.has(match[1])) continue;\n contracts.push({ slug: match[1], convention: identity, params });\n }\n return contracts;\n}\n\nfunction iconFromTags(tags: string[][]): NappletManifest['icon'] {\n const icons = tags.filter((tag) => tag[0] === 'icon');\n if (icons.length !== 1) return undefined;\n const [, hash, mimeType] = icons[0];\n if (icons[0].length !== 3 || !HASH.test(hash)) return undefined;\n if (mimeType !== 'image/png' && mimeType !== 'image/jpeg' && mimeType !== 'image/webp') {\n return undefined;\n }\n // A declaration is not verified image data. Hosts retain generic artwork until\n // both its hash and decoded image format have been checked independently.\n return { sha256: hash, mimeType };\n}\n\n/** Parse the single-artifact schema from NIP-5D #2303, 020cb8b. */\nexport function parseCurrentManifest(event: NostrEvent): NappletManifest {\n const tags = event.tags;\n const artifactHash = single(tags, 'x');\n if (!artifactHash || !HASH.test(artifactHash)) invalid('invalid artifact x hash');\n if (typeof event.content !== 'string' || !event.content.trim()) {\n invalid('manifest content must be a non-empty plain-text description');\n }\n const dTag = single(tags, 'd');\n if (event.kind === 35129 ? dTag === undefined : dTag !== undefined) {\n invalid('exactly one d tag is required only on named manifests');\n }\n const parent = single(tags, 'a');\n const root = single(tags, 'A');\n for (const address of [parent, root]) {\n if (address === undefined) continue;\n if (event.kind !== 5129 || !/^(35129:[a-f0-9]{64}:.+|15129:[a-f0-9]{64}:)$/.test(address)) {\n invalid('lineage must be a napplet address on a snapshot');\n }\n }\n // NAP-INTENT advertisements are optional routing hints. A malformed z/i tag\n // contributes no contract and never invalidates an otherwise verified artifact.\n const archetypeSlugs = [...new Set(tags\n .filter((tag) => tag[0] === 'z' && tag.length === 2 && SLUG.test(tag[1]))\n .map((tag) => tag[1]))];\n const archetypes = intentDeclarations(tags, archetypeSlugs);\n const intents = archetypes.map(({ convention: identity, params: parameters }) => ({ identity, parameters }));\n return {\n format: 'current',\n kind: event.kind,\n pubkey: event.pubkey,\n dTag: dTag ?? '',\n catalogId: getNappletCatalogId(event),\n artifactHash,\n aggregateHash: artifactHash,\n paths: [{ path: '/index.html', sha256: artifactHash }],\n servers: tags.filter((tag) => tag[0] === 'server' && tag[1]).map((tag) => tag[1]),\n requires: declarations(tags, 'R', DOMAIN),\n optional: declarations(tags, 'O', DOMAIN),\n archetypeSlugs,\n intents,\n archetypes,\n title: single(tags, 'title'),\n description: event.content,\n source: single(tags, 'source'),\n icon: iconFromTags(tags),\n parent,\n root,\n };\n}\n","import type { NostrEvent } from 'nostr-tools';\nimport type { NappletManifest } from './index.js';\n\nconst MIB = 1024 * 1024;\n\n/** Versioned Cache Storage namespace for verified napplet artifacts. */\nexport const NAPPLET_ARTIFACT_CACHE_NAME = 'kehto:napplet-artifacts:v1';\nconst SCHEMA_VERSION = 1;\nconst DEFAULT_BASE_URL = 'https://kehto.invalid';\nconst DEFAULT_UNKNOWN_BUDGET_BYTES = 32 * MIB;\nconst DEFAULT_SOFT_CEILING_BYTES = 128 * MIB;\nconst DEFAULT_HARD_CEILING_BYTES = 256 * MIB;\nconst DEFAULT_FOREGROUND_TTL_MS = 15 * 60 * 1000;\nconst DEFAULT_BACKGROUND_TTL_MS = 60 * 60 * 1000;\n\nexport interface NappletCacheDiagnostic {\n /** Diagnostic category for cache open/read/write/delete/prune/storage-estimate failures. */\n type:\n | 'cache-open-failed'\n | 'cache-read-failed'\n | 'cache-write-failed'\n | 'cache-delete-failed'\n | 'cache-prune-failed'\n | 'storage-estimate-unavailable';\n /** Human-readable diagnostic detail suitable for logs. */\n message: string;\n /** Original thrown value, when one exists. */\n cause?: unknown;\n}\n\n/** Metadata tracked for one cached napplet artifact blob. */\nexport interface NappletBlobIndexEntry {\n /** Blob byte length. */\n size: number;\n /** Last read or write timestamp in milliseconds. */\n lastAccessed: number;\n /** Number of cached aggregate records currently referencing this blob. */\n refCount: number;\n}\n\n/** Metadata tracked for one verified aggregate cache record. */\nexport interface NappletAggregateIndexEntry {\n /** Manifest `d` tag. */\n dTag: string;\n /** Verified NIP-5A aggregate hash. */\n aggregateHash: string;\n /** Internal cache key derived from `dTag` and `aggregateHash`. */\n key: string;\n /** SHA-256 hashes of blobs referenced by this aggregate. */\n blobHashes: string[];\n /** Approximate stored aggregate-record byte size. */\n size: number;\n /** Last read or write timestamp in milliseconds. */\n lastAccessed: number;\n /** Coordinate cache key that currently points at this aggregate, if any. */\n coordinateKey?: string;\n /** Reserved pin marker for hosts that want to protect an aggregate. */\n pinned?: boolean;\n}\n\n/** Cached mapping from a NIP-5D coordinate to a verified aggregate. */\nexport interface CachedCoordinate {\n /** Internal coordinate key in the form `kind:pubkey:dTag`. */\n key: string;\n /** Napplet manifest event kind. */\n kind: number;\n /** Manifest author public key. */\n pubkey: string;\n /** Manifest `d` tag. */\n dTag: string;\n /** Verified aggregate hash for the coordinate. */\n aggregateHash: string;\n /** Timestamp when this coordinate last resolved successfully. */\n lastResolvedAt: number;\n /** Foreground refresh TTL in milliseconds. */\n foregroundTtlMs: number;\n /** Background refresh TTL in milliseconds. */\n backgroundTtlMs: number;\n}\n\n/** Serialized Cache Storage index for verified napplet artifacts. */\nexport interface NappletArtifactCacheIndex {\n /** Index schema version. */\n schemaVersion: number;\n /** Last index update timestamp in milliseconds. */\n updatedAt: number;\n /** Blob entries keyed by SHA-256 hash. */\n blobs: Record<string, NappletBlobIndexEntry>;\n /** Aggregate entries keyed by the internal aggregate key. */\n aggregates: Record<string, NappletAggregateIndexEntry>;\n /** Coordinate entries keyed by {@link coordinateKey}. */\n coordinates: Record<string, CachedCoordinate>;\n}\n\ninterface AggregateRecord {\n schemaVersion: number;\n dTag: string;\n aggregateHash: string;\n manifest: {\n kind: number;\n pubkey: string;\n dTag: string;\n paths: Array<{ path: string; sha256: string }>;\n requires: string[];\n title?: string;\n description?: string;\n };\n indexHtml: string;\n blobHashes: string[];\n writtenAt: number;\n}\n\nexport interface WriteVerifiedResolutionInput {\n /** Verified NIP-5D manifest event. */\n event: NostrEvent;\n /** Parsed manifest whose aggregate and paths have already been verified. */\n manifest: NappletManifest;\n /** File bytes keyed by manifest path. */\n files: ReadonlyMap<string, Uint8Array>;\n /** Verified `/index.html` bytes decoded as text. */\n indexHtml: string;\n /** Timestamp override for deterministic tests. */\n now?: number;\n}\n\n/** Freshness controls for reading a cached coordinate. */\nexport interface CoordinateFreshnessOptions {\n /** Timestamp override in milliseconds. */\n now?: number;\n /** Use the background TTL instead of the foreground TTL. */\n background?: boolean;\n}\n\n/** Cache interface for verified NIP-5D napplet artifacts. */\nexport interface NappletArtifactCache {\n /** Read one cached artifact blob by SHA-256 hash. */\n readBlob(sha256Hex: string): Promise<Uint8Array | undefined>;\n /** Delete one cached artifact blob by SHA-256 hash. */\n deleteBlob(sha256Hex: string): Promise<void>;\n /** Write a verified manifest resolution and all referenced blobs. */\n writeVerifiedResolution(input: WriteVerifiedResolutionInput): Promise<void>;\n /** Cache the latest aggregate for a NIP-5D coordinate. */\n writeCoordinate(input: {\n kind: number;\n pubkey: string;\n dTag: string;\n aggregateHash: string;\n lastResolvedAt?: number;\n foregroundTtlMs?: number;\n backgroundTtlMs?: number;\n }): Promise<void>;\n /** Read a cached coordinate with a computed freshness flag. */\n getCoordinate(input: {\n kind: number;\n pubkey: string;\n dTag: string;\n }, options?: CoordinateFreshnessOptions): Promise<(CachedCoordinate & { fresh: boolean }) | undefined>;\n /** Update aggregate/blob last-access timestamps. */\n touchAggregate(dTag: string, aggregateHash: string, now?: number): Promise<void>;\n /** Protect an aggregate from normal pruning while it is in active use. */\n markAggregateActive(dTag: string, aggregateHash: string): void;\n /** Release an aggregate previously marked active. */\n releaseAggregateActive(dTag: string, aggregateHash: string): void;\n /** Prune cache contents to the configured storage budget. */\n prune(now?: number): Promise<void>;\n}\n\n/** Constructor options for {@link CacheStorageNappletArtifactCache}. */\nexport interface CacheStorageNappletArtifactCacheOptions {\n /** Cache Storage implementation; defaults to `globalThis.caches`. */\n cacheStorage?: CacheStorage;\n /** Storage manager used for quota estimates; defaults to `navigator.storage`. */\n storage?: StorageManager;\n /** Cache Storage namespace. */\n cacheName?: string;\n /** Base URL used to construct synthetic cache keys. */\n baseUrl?: string;\n /** Clock used for timestamps. */\n now?: () => number;\n /** Diagnostic callback for non-fatal cache failures. */\n onDiagnostic?: (diagnostic: NappletCacheDiagnostic) => void;\n /** Budget used when storage estimates are unavailable. */\n unknownBudgetBytes?: number;\n /** Soft cache ceiling in bytes. */\n softCeilingBytes?: number;\n /** Hard cache ceiling in bytes. */\n hardCeilingBytes?: number;\n}\n\n/** Options for opening the browser Cache Storage artifact cache. */\nexport interface OpenNappletArtifactCacheOptions extends CacheStorageNappletArtifactCacheOptions {\n /** Return `undefined` when quota estimates are unavailable. */\n requireStorageEstimate?: boolean;\n}\n\nfunction emptyIndex(now: number): NappletArtifactCacheIndex {\n return {\n schemaVersion: SCHEMA_VERSION,\n updatedAt: now,\n blobs: {},\n aggregates: {},\n coordinates: {},\n };\n}\n\nfunction aggregateKey(dTag: string, aggregateHash: string): string {\n return `${encodeURIComponent(dTag)}:${aggregateHash}`;\n}\n\n/**\n * Build the stable index key for a NIP-5D coordinate.\n *\n * @param kind - Manifest event kind.\n * @param pubkey - Manifest author public key.\n * @param dTag - Manifest `d` tag.\n * @returns Cache key in the form `kind:pubkey:dTag`.\n */\nexport function coordinateKey(kind: number, pubkey: string, dTag: string): string {\n return `${kind}:${pubkey}:${encodeURIComponent(dTag)}`;\n}\n\n/**\n * Check whether a cached coordinate is still inside its foreground/background TTL.\n *\n * @param coordinate - Cached coordinate to inspect.\n * @param options - Optional clock and TTL mode overrides.\n * @returns `true` when the coordinate can be used without a network refresh.\n */\nexport function isCoordinateFresh(\n coordinate: CachedCoordinate,\n options: CoordinateFreshnessOptions = {},\n): boolean {\n const now = options.now ?? Date.now();\n const ttl = options.background ? coordinate.backgroundTtlMs : coordinate.foregroundTtlMs;\n return ttl === Number.POSITIVE_INFINITY || now - coordinate.lastResolvedAt <= ttl;\n}\n\nfunction byteLength(bytes: Uint8Array): number {\n return bytes.byteLength;\n}\n\nfunction encodeKeyPart(value: string): string {\n return encodeURIComponent(value).replaceAll('%2F', '%252F');\n}\n\nfunction cloneBytes(bytes: Uint8Array): Uint8Array {\n return new Uint8Array(bytes);\n}\n\nfunction responseBody(bytes: Uint8Array): ArrayBuffer {\n return cloneBytes(bytes).buffer as ArrayBuffer;\n}\n\nfunction normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;\n}\n\nfunction resolveBaseUrl(baseUrl?: string): string {\n if (baseUrl) return normalizeBaseUrl(baseUrl);\n const maybeLocation = globalThis as typeof globalThis & { location?: { origin?: string } };\n if (maybeLocation.location?.origin) return normalizeBaseUrl(maybeLocation.location.origin);\n return normalizeBaseUrl(DEFAULT_BASE_URL);\n}\n\nasync function responseBytes(response: Response): Promise<Uint8Array> {\n return new Uint8Array(await response.arrayBuffer());\n}\n\nasync function responseJson<T>(response: Response): Promise<T> {\n return response.json() as Promise<T>;\n}\n\nfunction refCounts(index: NappletArtifactCacheIndex): Record<string, number> {\n const counts: Record<string, number> = {};\n for (const aggregate of Object.values(index.aggregates)) {\n for (const hash of aggregate.blobHashes) counts[hash] = (counts[hash] ?? 0) + 1;\n }\n return counts;\n}\n\nfunction cachedSize(index: NappletArtifactCacheIndex): number {\n const blobBytes = Object.values(index.blobs).reduce((sum, entry) => sum + entry.size, 0);\n const aggregateBytes = Object.values(index.aggregates).reduce((sum, entry) => sum + entry.size, 0);\n return blobBytes + aggregateBytes;\n}\n\n/**\n * Browser Cache Storage implementation of {@link NappletArtifactCache}.\n *\n * It stores verified blobs and aggregate records under synthetic URLs, keeps a\n * small JSON index, and prunes according to quota-derived budgets.\n */\nexport class CacheStorageNappletArtifactCache implements NappletArtifactCache {\n private readonly cacheStorage: CacheStorage;\n private readonly storage?: StorageManager;\n private readonly cacheName: string;\n private readonly baseUrl: string;\n private readonly now: () => number;\n private readonly activeAggregates = new Set<string>();\n private readonly onDiagnostic?: (diagnostic: NappletCacheDiagnostic) => void;\n private readonly unknownBudgetBytes: number;\n private readonly softCeilingBytes: number;\n private readonly hardCeilingBytes: number;\n private cachePromise?: Promise<Cache>;\n\n constructor(options: CacheStorageNappletArtifactCacheOptions = {}) {\n const maybeCacheStorage = options.cacheStorage ?? (globalThis as typeof globalThis & { caches?: CacheStorage }).caches;\n if (!maybeCacheStorage) {\n throw new Error('Cache Storage is unavailable');\n }\n this.cacheStorage = maybeCacheStorage;\n this.storage = options.storage ?? (globalThis.navigator as Navigator | undefined)?.storage;\n this.cacheName = options.cacheName ?? NAPPLET_ARTIFACT_CACHE_NAME;\n this.baseUrl = resolveBaseUrl(options.baseUrl);\n this.now = options.now ?? Date.now;\n this.onDiagnostic = options.onDiagnostic;\n this.unknownBudgetBytes = options.unknownBudgetBytes ?? DEFAULT_UNKNOWN_BUDGET_BYTES;\n this.softCeilingBytes = options.softCeilingBytes ?? DEFAULT_SOFT_CEILING_BYTES;\n this.hardCeilingBytes = options.hardCeilingBytes ?? DEFAULT_HARD_CEILING_BYTES;\n }\n\n async readBlob(sha256Hex: string): Promise<Uint8Array | undefined> {\n try {\n const cache = await this.open();\n const response = await cache.match(this.blobUrl(sha256Hex));\n if (!response) return undefined;\n const bytes = await responseBytes(response);\n const index = await this.readIndex(cache);\n const entry = index.blobs[sha256Hex];\n if (entry) {\n entry.lastAccessed = this.now();\n index.updatedAt = entry.lastAccessed;\n await this.writeIndex(cache, index);\n }\n return bytes;\n } catch (cause) {\n this.diagnostic({ type: 'cache-read-failed', message: `failed to read blob ${sha256Hex}`, cause });\n return undefined;\n }\n }\n\n async deleteBlob(sha256Hex: string): Promise<void> {\n try {\n const cache = await this.open();\n await cache.delete(this.blobUrl(sha256Hex));\n const index = await this.readIndex(cache);\n delete index.blobs[sha256Hex];\n index.updatedAt = this.now();\n await this.writeIndex(cache, index);\n } catch (cause) {\n this.diagnostic({ type: 'cache-delete-failed', message: `failed to delete blob ${sha256Hex}`, cause });\n }\n }\n\n async writeVerifiedResolution(input: WriteVerifiedResolutionInput): Promise<void> {\n try {\n await this.writeVerifiedResolutionOnce(input);\n } catch (firstCause) {\n try {\n await this.prune(input.now ?? this.now());\n await this.writeVerifiedResolutionOnce(input);\n } catch (cause) {\n this.diagnostic({\n type: 'cache-write-failed',\n message: 'failed to write verified napplet artifacts after prune retry',\n cause: cause ?? firstCause,\n });\n }\n }\n }\n\n async writeCoordinate(input: {\n kind: number;\n pubkey: string;\n dTag: string;\n aggregateHash: string;\n lastResolvedAt?: number;\n foregroundTtlMs?: number;\n backgroundTtlMs?: number;\n }): Promise<void> {\n const cache = await this.open();\n const now = this.now();\n const index = await this.readIndex(cache);\n const key = coordinateKey(input.kind, input.pubkey, input.dTag);\n index.coordinates[key] = {\n key,\n kind: input.kind,\n pubkey: input.pubkey,\n dTag: input.dTag,\n aggregateHash: input.aggregateHash,\n lastResolvedAt: input.lastResolvedAt ?? now,\n foregroundTtlMs: input.foregroundTtlMs ?? DEFAULT_FOREGROUND_TTL_MS,\n backgroundTtlMs: input.backgroundTtlMs ?? DEFAULT_BACKGROUND_TTL_MS,\n };\n index.updatedAt = now;\n await this.writeIndex(cache, index);\n }\n\n async getCoordinate(\n input: { kind: number; pubkey: string; dTag: string },\n options: CoordinateFreshnessOptions = {},\n ): Promise<(CachedCoordinate & { fresh: boolean }) | undefined> {\n const cache = await this.open();\n const index = await this.readIndex(cache);\n const coordinate = index.coordinates[coordinateKey(input.kind, input.pubkey, input.dTag)];\n if (!coordinate) return undefined;\n return { ...coordinate, fresh: isCoordinateFresh(coordinate, options) };\n }\n\n async touchAggregate(dTag: string, aggregateHash: string, now: number = this.now()): Promise<void> {\n const cache = await this.open();\n const index = await this.readIndex(cache);\n const key = aggregateKey(dTag, aggregateHash);\n const aggregate = index.aggregates[key];\n if (!aggregate) return;\n aggregate.lastAccessed = now;\n for (const hash of aggregate.blobHashes) {\n if (index.blobs[hash]) index.blobs[hash].lastAccessed = now;\n }\n index.updatedAt = now;\n await this.writeIndex(cache, index);\n }\n\n markAggregateActive(dTag: string, aggregateHash: string): void {\n this.activeAggregates.add(aggregateKey(dTag, aggregateHash));\n }\n\n releaseAggregateActive(dTag: string, aggregateHash: string): void {\n this.activeAggregates.delete(aggregateKey(dTag, aggregateHash));\n }\n\n async prune(now: number = this.now()): Promise<void> {\n try {\n const cache = await this.open();\n const index = await this.readIndex(cache);\n let size = cachedSize(index);\n const budget = await this.budgetBytes();\n const hardBudget = await this.hardBudgetBytes();\n const shouldPrune = size > budget || await this.originPressureHigh();\n if (!shouldPrune) return;\n\n this.rewriteRefCounts(index);\n\n for (const [hash, blob] of Object.entries(index.blobs)) {\n if (size <= budget) break;\n if (blob.refCount > 0) continue;\n await cache.delete(this.blobUrl(hash));\n delete index.blobs[hash];\n size -= blob.size;\n }\n\n const currentAggregateHashes = new Set(\n Object.values(index.coordinates).map((coordinate) => coordinate.aggregateHash),\n );\n const aggregates = Object.values(index.aggregates)\n .filter((entry) => !this.activeAggregates.has(entry.key))\n .sort((a, b) => {\n const currentA = currentAggregateHashes.has(a.aggregateHash) ? 1 : 0;\n const currentB = currentAggregateHashes.has(b.aggregateHash) ? 1 : 0;\n if (currentA !== currentB) return currentA - currentB;\n return a.lastAccessed - b.lastAccessed;\n });\n\n for (const aggregate of aggregates) {\n if (size <= budget) break;\n await cache.delete(this.aggregateUrl(aggregate.key));\n delete index.aggregates[aggregate.key];\n size -= aggregate.size;\n this.rewriteRefCounts(index);\n }\n\n if (size > hardBudget) {\n const active = Object.values(index.aggregates)\n .filter((entry) => this.activeAggregates.has(entry.key))\n .sort((a, b) => a.lastAccessed - b.lastAccessed);\n for (const aggregate of active) {\n if (size <= hardBudget) break;\n await cache.delete(this.aggregateUrl(aggregate.key));\n delete index.aggregates[aggregate.key];\n size -= aggregate.size;\n }\n }\n\n this.rewriteRefCounts(index);\n for (const [hash, blob] of Object.entries(index.blobs)) {\n if (blob.refCount === 0) {\n await cache.delete(this.blobUrl(hash));\n delete index.blobs[hash];\n }\n }\n index.updatedAt = now;\n await this.writeIndex(cache, index);\n } catch (cause) {\n this.diagnostic({ type: 'cache-prune-failed', message: 'failed to prune napplet artifact cache', cause });\n }\n }\n\n async snapshotIndex(): Promise<NappletArtifactCacheIndex> {\n return this.readIndex(await this.open());\n }\n\n private async writeVerifiedResolutionOnce(input: WriteVerifiedResolutionInput): Promise<void> {\n const cache = await this.open();\n const now = input.now ?? this.now();\n const index = await this.readIndex(cache);\n const blobHashes: string[] = [];\n\n for (const pathEntry of input.manifest.paths) {\n const bytes = input.files.get(pathEntry.path);\n if (!bytes) continue;\n blobHashes.push(pathEntry.sha256);\n await cache.put(this.blobUrl(pathEntry.sha256), new Response(responseBody(bytes)));\n index.blobs[pathEntry.sha256] = {\n size: byteLength(bytes),\n lastAccessed: now,\n refCount: index.blobs[pathEntry.sha256]?.refCount ?? 0,\n };\n }\n\n const key = aggregateKey(input.manifest.dTag, input.manifest.aggregateHash);\n const record: AggregateRecord = {\n schemaVersion: SCHEMA_VERSION,\n dTag: input.manifest.dTag,\n aggregateHash: input.manifest.aggregateHash,\n manifest: {\n kind: input.manifest.kind,\n pubkey: input.manifest.pubkey,\n dTag: input.manifest.dTag,\n paths: input.manifest.paths.map((entry) => ({ path: entry.path, sha256: entry.sha256 })),\n requires: [...input.manifest.requires],\n ...(input.manifest.title === undefined ? {} : { title: input.manifest.title }),\n ...(input.manifest.description === undefined ? {} : { description: input.manifest.description }),\n },\n indexHtml: input.indexHtml,\n blobHashes,\n writtenAt: now,\n };\n const aggregateBytes = new TextEncoder().encode(JSON.stringify(record));\n await cache.put(this.aggregateUrl(key), new Response(responseBody(aggregateBytes), {\n headers: { 'content-type': 'application/json' },\n }));\n\n const coordKey = coordinateKey(input.manifest.kind, input.manifest.pubkey, input.manifest.dTag);\n index.aggregates[key] = {\n dTag: input.manifest.dTag,\n aggregateHash: input.manifest.aggregateHash,\n key,\n blobHashes,\n size: byteLength(aggregateBytes),\n lastAccessed: now,\n coordinateKey: coordKey,\n };\n index.coordinates[coordKey] = {\n key: coordKey,\n kind: input.manifest.kind,\n pubkey: input.manifest.pubkey,\n dTag: input.manifest.dTag,\n aggregateHash: input.manifest.aggregateHash,\n lastResolvedAt: now,\n foregroundTtlMs: DEFAULT_FOREGROUND_TTL_MS,\n backgroundTtlMs: DEFAULT_BACKGROUND_TTL_MS,\n };\n\n this.rewriteRefCounts(index);\n index.updatedAt = now;\n await this.writeIndex(cache, index);\n await this.prune(now);\n }\n\n private async open(): Promise<Cache> {\n this.cachePromise ??= this.cacheStorage.open(this.cacheName);\n return this.cachePromise;\n }\n\n private indexUrl(): string {\n return new URL('__kehto_napplet_cache/index.json', this.baseUrl).toString();\n }\n\n private blobUrl(hash: string): string {\n return new URL(`__kehto_napplet_cache/blob/${encodeKeyPart(hash)}`, this.baseUrl).toString();\n }\n\n private aggregateUrl(key: string): string {\n return new URL(`__kehto_napplet_cache/aggregate/${encodeKeyPart(key)}.json`, this.baseUrl).toString();\n }\n\n private async readIndex(cache: Cache): Promise<NappletArtifactCacheIndex> {\n const response = await cache.match(this.indexUrl());\n if (!response) return emptyIndex(this.now());\n try {\n const index = await responseJson<NappletArtifactCacheIndex>(response);\n if (index.schemaVersion !== SCHEMA_VERSION) return emptyIndex(this.now());\n return index;\n } catch {\n return emptyIndex(this.now());\n }\n }\n\n private async writeIndex(cache: Cache, index: NappletArtifactCacheIndex): Promise<void> {\n await cache.put(this.indexUrl(), new Response(JSON.stringify(index), {\n headers: { 'content-type': 'application/json' },\n }));\n }\n\n private rewriteRefCounts(index: NappletArtifactCacheIndex): void {\n const counts = refCounts(index);\n for (const [hash, blob] of Object.entries(index.blobs)) {\n blob.refCount = counts[hash] ?? 0;\n }\n }\n\n private async budgetBytes(): Promise<number> {\n const estimate = await this.estimateStorage();\n if (!estimate?.quota) return this.unknownBudgetBytes;\n return Math.min(this.softCeilingBytes, Math.floor(estimate.quota * 0.1));\n }\n\n private async hardBudgetBytes(): Promise<number> {\n const estimate = await this.estimateStorage();\n if (!estimate?.quota) return this.unknownBudgetBytes;\n return Math.min(this.hardCeilingBytes, Math.floor(estimate.quota * 0.2));\n }\n\n private async originPressureHigh(): Promise<boolean> {\n const estimate = await this.estimateStorage();\n if (!estimate?.quota || estimate.usage === undefined) return false;\n return estimate.usage / estimate.quota >= 0.8;\n }\n\n private async estimateStorage(): Promise<StorageEstimate | undefined> {\n if (!this.storage?.estimate) return undefined;\n try {\n return this.storage.estimate();\n } catch (cause) {\n this.diagnostic({\n type: 'storage-estimate-unavailable',\n message: 'storage estimate failed; using unknown quota cache budget',\n cause,\n });\n return undefined;\n }\n }\n\n private diagnostic(diagnostic: NappletCacheDiagnostic): void {\n this.onDiagnostic?.(diagnostic);\n }\n}\n\n/**\n * Open the browser Cache Storage artifact cache when the host supports it.\n *\n * @param options - Cache construction options and diagnostics callback.\n * @returns A cache instance, or `undefined` when Cache Storage is unavailable.\n */\nexport async function openNappletArtifactCache(\n options: OpenNappletArtifactCacheOptions = {},\n): Promise<CacheStorageNappletArtifactCache | undefined> {\n const cacheStorage = options.cacheStorage ?? (globalThis as typeof globalThis & { caches?: CacheStorage }).caches;\n if (!cacheStorage) return undefined;\n const storage = options.storage ?? (globalThis.navigator as Navigator | undefined)?.storage;\n if (options.requireStorageEstimate && !storage?.estimate) {\n options.onDiagnostic?.({\n type: 'storage-estimate-unavailable',\n message: 'storage estimates are unavailable; using network-only loading',\n });\n return undefined;\n }\n try {\n const cache = new CacheStorageNappletArtifactCache({ ...options, cacheStorage, storage });\n await cache.snapshotIndex();\n return cache;\n } catch (cause) {\n options.onDiagnostic?.({\n type: 'cache-open-failed',\n message: 'Cache Storage napplet artifact cache could not be opened',\n cause,\n });\n return undefined;\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { PathEntry, aggregateTagValue, computeAggregateHash, pathEntriesFromTags, verifyAggregate } from './5a/index.js';
2
- export { CacheStorageNappletArtifactCache, CacheStorageNappletArtifactCacheOptions, CachedCoordinate, CoordinateFreshnessOptions, NAPPLET_ARTIFACT_CACHE_NAME, NAPPLET_KINDS, NAPPLET_KIND_NAMED, NAPPLET_KIND_ROOT, NAPPLET_KIND_SNAPSHOT, NappletAggregateIndexEntry, NappletArtifactCache, NappletArtifactCacheIndex, NappletBlobIndexEntry, NappletCacheDiagnostic, NappletManifest, NappletResolutionError, NappletResolutionErrorCode, OpenNappletArtifactCacheOptions, ResolveNappletOptions, ResolvedNapplet, WriteVerifiedResolutionInput, coordinateKey, fetchBlob, isCoordinateFresh, isNappletManifestKind, openNappletArtifactCache, parseNappletManifest, resolveNapplet, verifyBlobHash, verifyManifestSignature } from './5d/index.js';
2
+ export { CacheStorageNappletArtifactCache, CacheStorageNappletArtifactCacheOptions, CachedCoordinate, CoordinateFreshnessOptions, NAPPLET_ARTIFACT_CACHE_NAME, NAPPLET_KINDS, NAPPLET_KIND_NAMED, NAPPLET_KIND_ROOT, NAPPLET_KIND_SNAPSHOT, NappletAggregateIndexEntry, NappletArtifactCache, NappletArtifactCacheIndex, NappletBlobIndexEntry, NappletCacheDiagnostic, NappletManifest, NappletResolutionError, NappletResolutionErrorCode, OpenNappletArtifactCacheOptions, ResolveNappletOptions, ResolvedNapplet, WriteVerifiedResolutionInput, coordinateKey, fetchBlob, getNappletCatalogId, isCoordinateFresh, isNappletManifestKind, openNappletArtifactCache, parseNappletManifest, resolveNapplet, verifyBlobHash, verifyManifestSignature } from './5d/index.js';
3
3
  export { ITEM_TAGS, LIST_KINDS, Nip44Decryptor, Nip51List, SET_KINDS, decryptPrivateItems, getTagValues, isListKind, isSetKind, listKindName, parseList } from './51/index.js';
4
4
  export { Nip65Registry, RelayEntry, createNip65Registry, parseNip65RelayList, selectReadRelays, selectWriteRelays } from './65/index.js';
5
5
  export { DEFAULT_RELAY_ATTRIBUTE_GROUPS, Nip66Aggregator, Nip66AggregatorOptions, Nip66Filter, Nip66RelayAttributes, Nip66RelayPool, RelayAttributeGroups, RelayAttributeMatcher, createNip66Aggregator } from './66/index.js';
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  NappletResolutionError,
20
20
  coordinateKey,
21
21
  fetchBlob,
22
+ getNappletCatalogId,
22
23
  isCoordinateFresh,
23
24
  isNappletManifestKind,
24
25
  openNappletArtifactCache,
@@ -26,7 +27,7 @@ import {
26
27
  resolveNapplet,
27
28
  verifyBlobHash,
28
29
  verifyManifestSignature
29
- } from "./chunk-3JJBAODT.js";
30
+ } from "./chunk-AKQIPGRC.js";
30
31
  import {
31
32
  aggregateTagValue,
32
33
  computeAggregateHash,
@@ -75,6 +76,7 @@ export {
75
76
  createNip66Aggregator,
76
77
  decryptPrivateItems,
77
78
  fetchBlob,
79
+ getNappletCatalogId,
78
80
  getTagValues,
79
81
  handlesKind,
80
82
  isCoordinateFresh,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kehto/nip",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Tree-shakable bundle of unique Nostr NIP utilities for napplet runtimes — each NIP at its own subpath (e.g. @kehto/nip/66)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/5d/index.ts","../src/5d/errors.ts","../src/5d/legacy-manifest.ts","../src/5d/current-manifest.ts","../src/5d/artifact-cache.ts"],"sourcesContent":["import type { NostrEvent } from 'nostr-tools';\nimport { verifyEvent } from 'nostr-tools/pure';\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport { bytesToHex } from '@noble/hashes/utils.js';\nimport type { PathEntry } from '../5a/index.js';\nimport { NappletResolutionError } from './errors.js';\nimport { parseLegacyManifest, verifyLegacyAggregate, warnLegacyManifest } from './legacy-manifest.js';\nimport { parseCurrentManifest } from './current-manifest.js';\nimport type { NappletArtifactCache } from './artifact-cache.js';\nexport {\n CacheStorageNappletArtifactCache,\n NAPPLET_ARTIFACT_CACHE_NAME,\n coordinateKey,\n isCoordinateFresh,\n openNappletArtifactCache,\n type CachedCoordinate,\n type CacheStorageNappletArtifactCacheOptions,\n type CoordinateFreshnessOptions,\n type NappletAggregateIndexEntry,\n type NappletArtifactCacheIndex,\n type NappletArtifactCache,\n type NappletBlobIndexEntry,\n type NappletCacheDiagnostic,\n type OpenNappletArtifactCacheOptions,\n type WriteVerifiedResolutionInput,\n} from './artifact-cache.js';\n\n/**\n * `@kehto/nip/5d` — verify signed napplet manifests and their content bytes.\n * Current events use one artifact x hash. The isolated legacy adapter accepts\n * path tags plus an aggregate x marker. Both formats share signature checking,\n * blob verification, caching, and verified srcdoc output.\n * @module\n */\n\n/** Snapshot manifest — regular event, immutable point-in-time release. */\nexport const NAPPLET_KIND_SNAPSHOT = 5129;\n/** Root manifest — replaceable event, an author's latest unnamed napplet. */\nexport const NAPPLET_KIND_ROOT = 15129;\n/** Named manifest — addressable event (carries a `d` tag identifier). */\nexport const NAPPLET_KIND_NAMED = 35129;\n\n/** All three NIP-5D napplet manifest kinds. */\nexport const NAPPLET_KINDS: readonly number[] = [\n NAPPLET_KIND_SNAPSHOT,\n NAPPLET_KIND_ROOT,\n NAPPLET_KIND_NAMED,\n];\n\n/**\n * Whether `kind` is one of the three NIP-5D napplet manifest kinds.\n *\n * @param kind - A Nostr event kind\n * @returns `true` for `5129` / `15129` / `35129`\n */\nexport function isNappletManifestKind(kind: number): boolean {\n return NAPPLET_KINDS.includes(kind);\n}\n\n/**\n * A parsed NIP-5D napplet manifest. All fields are derived from the manifest\n * event. Declarations are untrusted until {@link resolveNapplet} verifies both\n * the signature and the content bytes.\n */\nexport interface NappletManifest {\n /** Event format selected by the parser. Absent only in older host-created values. */\n format?: 'current' | 'legacy';\n /** Signed artifact hash for current events; legacy aggregate for older events. */\n artifactHash?: string;\n /** Optional domains; absence never prevents loading and presence grants nothing. */\n optional?: string[];\n /** Independent current z declarations, preserved even without accepted intents. */\n archetypeSlugs?: string[];\n /** Accepted queryless intents and advertised parameter names from i tags. */\n intents?: Array<{ identity: string; parameters: string[] }>;\n /** Valid supported icon declaration. Hosts may keep generic artwork. */\n icon?: { sha256: string; mimeType: 'image/png' | 'image/jpeg' | 'image/webp' };\n /** Snapshot provenance only; never resolved or trusted on its behalf. */\n parent?: string;\n /** Snapshot root provenance only. */\n root?: string;\n /** Manifest event kind (`5129` / `15129` / `35129`). */\n kind: number;\n /** Author hex public key. */\n pubkey: string;\n /** Named-napplet `d` identifier, or `''` for root/snapshot manifests. */\n dTag: string;\n /** Normalized files: one /index.html for current events, legacy path entries otherwise. */\n paths: PathEntry[];\n /** Compatibility name for the signed content identity (current artifact or legacy aggregate). */\n aggregateHash: string;\n /** Blossom server URL hints from `server` tags. */\n servers: string[];\n /** Required bare domains from current R tags or legacy requires tags. */\n requires: string[];\n /**\n * Routing projection: independent current z/i sets, or exact legacy archetype pairs.\n */\n archetypes: Array<{ slug: string; convention: string }>;\n /** Optional human title. */\n title?: string;\n /** Plain-text event content (legacy description tag for older events). */\n description?: string;\n /** Optional upstream source URL from the `source` tag. */\n source?: string;\n}\n\nexport { NappletResolutionError, type NappletResolutionErrorCode } from './errors.js';\n\n/**\n * Normalize a current or legacy NIP-5D event without verifying its signature.\n * @param event - Candidate manifest; use resolveNapplet before trusting it.\n * @returns Manifest with normalized identity, paths, and routing declarations.\n * @example\n * const manifest = parseNappletManifest(event);\n */\nexport function parseNappletManifest(event: NostrEvent): NappletManifest {\n if (!isNappletManifestKind(event.kind)) {\n throw new NappletResolutionError('invalid-manifest', `not a NIP-5D napplet kind: ${event.kind}`);\n }\n const hashes = event.tags.filter((tag) => tag[0] === 'x');\n if (hashes.length !== 1) {\n throw new NappletResolutionError('invalid-manifest', 'manifest must carry exactly one x tag');\n }\n // Explicit format selection, never retry a malformed current event as legacy.\n return hashes[0][2] === 'aggregate'\n ? parseLegacyManifest(event)\n : parseCurrentManifest(event);\n}\n\n/**\n * Verify a manifest event's Nostr signature (id + schnorr sig).\n *\n * @param event - A manifest event\n * @returns `true` if the event is internally consistent and validly signed\n */\nexport function verifyManifestSignature(event: NostrEvent): boolean {\n try {\n return verifyEvent(event);\n } catch {\n return false;\n }\n}\n\n/**\n * Whether `bytes` hash to the expected lowercase-hex SHA-256.\n *\n * @param bytes - The blob bytes\n * @param sha256Hex - Expected lowercase-hex SHA-256\n * @returns `true` only on an exact match\n */\nexport function verifyBlobHash(bytes: Uint8Array, sha256Hex: string): boolean {\n return bytesToHex(sha256(bytes)) === sha256Hex;\n}\n\nfunction stripTrailingSlash(url: string): string {\n return url.endsWith('/') ? url.slice(0, -1) : url;\n}\n\n/**\n * Fetch a blob by SHA-256 from a list of Blossom servers, returning the first\n * server's bytes whose hash matches. The returned bytes are always re-verified\n * against `sha256Hex` — servers (and gateways) are never trusted.\n *\n * @param servers - Candidate Blossom server base URLs, tried in order\n * @param sha256Hex - The blob's lowercase-hex SHA-256\n * @param fetchBytes - Fetches raw bytes for a URL (`<server>/<sha256>`)\n * @returns The verified blob bytes\n * @throws {@link NappletResolutionError} `blob-unavailable` if no server serves\n * a hash-matching blob\n */\nexport async function fetchBlob(\n servers: readonly string[],\n sha256Hex: string,\n fetchBytes: (url: string) => Promise<Uint8Array>,\n): Promise<Uint8Array> {\n for (const server of servers) {\n const url = `${stripTrailingSlash(server)}/${sha256Hex}`;\n try {\n const bytes = await fetchBytes(url);\n if (verifyBlobHash(bytes, sha256Hex)) return bytes;\n } catch {\n // try the next server\n }\n }\n throw new NappletResolutionError('blob-unavailable', `no server served blob ${sha256Hex}`);\n}\n\n/** A fully verified napplet, ready to inject via `iframe.srcdoc`. */\nexport interface ResolvedNapplet {\n /** Verified content identity; current artifact SHA-256 or legacy aggregate. */\n artifactHash: string;\n /** Computed `d` identifier (`''` for root/snapshot). */\n dTag: string;\n /** Compatibility alias for artifactHash; preserves existing host and ACL APIs. */\n aggregateHash: string;\n /** Verified file bytes keyed by manifest path. */\n files: Map<string, Uint8Array>;\n /** The verified `/index.html` decoded to text. */\n indexHtml: string;\n /** The parsed manifest. */\n manifest: NappletManifest;\n}\n\n/** Options for {@link resolveNapplet}. */\nexport interface ResolveNappletOptions {\n /** The candidate manifest event (resolved from relays by the caller). */\n event: NostrEvent;\n /**\n * Fetch raw blob bytes by SHA-256. Backed by Blossom (or a gateway) by the\n * caller. The bytes are re-verified against the hash here, so the fetcher is\n * untrusted.\n */\n fetchBlob: (sha256Hex: string, servers: readonly string[]) => Promise<Uint8Array>;\n /** Decode blob bytes to text for `indexHtml` (default UTF-8). */\n textDecode?: (bytes: Uint8Array) => string;\n /**\n * Optional verified artifact cache. Cache hits are still re-verified against\n * the manifest hash before use; cache writes happen only after the signature,\n * content identity, and every blob hash have been verified.\n */\n cache?: NappletArtifactCache;\n}\n\nconst INDEX_PATHS = ['/index.html', 'index.html', '/'];\n\nfunction defaultDecode(bytes: Uint8Array): string {\n return new TextDecoder().decode(bytes);\n}\n\n/**\n * Resolve a napplet end-to-end from a candidate manifest event: verify the\n * signature, parse its schema, fetch and hash its artifact, and return verified\n * `/index.html`. Legacy events additionally verify their NIP-5A aggregate.\n *\n * The returned `(dTag, aggregateHash)` is computed from the verified bytes and\n * is the napplet's identity. Any failure throws a {@link NappletResolutionError}\n * — the caller must fail closed (never render unverified bytes).\n *\n * @param options - {@link ResolveNappletOptions}\n * @returns The {@link ResolvedNapplet}\n * @throws {@link NappletResolutionError} on signature, manifest, aggregate,\n * blob-hash, blob-availability, or missing-index failures\n *\n * @example\n * ```ts\n * import { resolveNapplet } from '@kehto/nip/5d';\n * const napplet = await resolveNapplet({ event, fetchBlob });\n * iframe.srcdoc = napplet.indexHtml; // sandbox=\"allow-scripts\", opaque origin\n * ```\n */\nexport async function resolveNapplet(options: ResolveNappletOptions): Promise<ResolvedNapplet> {\n const { event, fetchBlob: fetchBlobBytes, textDecode = defaultDecode, cache } = options;\n\n if (!verifyManifestSignature(event)) {\n throw new NappletResolutionError('invalid-signature', 'manifest signature is invalid');\n }\n\n const manifest = parseNappletManifest(event);\n\n if (manifest.format === 'legacy') verifyLegacyAggregate(manifest);\n\n const files = new Map<string, Uint8Array>();\n for (const entry of manifest.paths) {\n let bytes = await cache?.readBlob(entry.sha256);\n if (bytes && !verifyBlobHash(bytes, entry.sha256)) {\n await cache?.deleteBlob(entry.sha256);\n bytes = undefined;\n }\n bytes ??= await fetchBlobBytes(entry.sha256, manifest.servers);\n if (!verifyBlobHash(bytes, entry.sha256)) {\n throw new NappletResolutionError(\n 'blob-hash-mismatch',\n `blob for ${entry.path} does not match hash ${entry.sha256}`,\n );\n }\n files.set(entry.path, bytes);\n }\n\n const indexEntry = manifest.paths.find((e) => INDEX_PATHS.includes(e.path));\n if (!indexEntry) {\n throw new NappletResolutionError('missing-index', 'manifest has no /index.html entry');\n }\n\n const indexHtml = textDecode(files.get(indexEntry.path)!);\n await cache?.writeVerifiedResolution({ event, manifest, files, indexHtml });\n if (manifest.format === 'legacy') warnLegacyManifest();\n\n return {\n artifactHash: manifest.aggregateHash,\n dTag: manifest.dTag,\n aggregateHash: manifest.aggregateHash,\n files,\n indexHtml,\n manifest,\n };\n}\n","/** Error codes for every napplet resolution failure path. */\nexport type NappletResolutionErrorCode =\n | 'invalid-signature'\n | 'invalid-manifest'\n | 'aggregate-mismatch'\n | 'blob-hash-mismatch'\n | 'blob-unavailable'\n | 'missing-index';\n\n/**\n * Thrown on any napplet resolution/verification failure. The `code` field\n * identifies which guard rejected, so callers can fail closed without parsing\n * the message.\n */\nexport class NappletResolutionError extends Error {\n readonly code: NappletResolutionErrorCode;\n constructor(code: NappletResolutionErrorCode, message: string) {\n super(message);\n this.name = 'NappletResolutionError';\n this.code = code;\n }\n}\n\n","// Removal boundary for pre-0.15 @napplet/vite-plugin event compatibility.\nimport type { NostrEvent } from 'nostr-tools';\nimport { computeAggregateHash, pathEntriesFromTags, aggregateTagValue } from '../5a/index.js';\nimport type { NappletManifest } from './index.js';\nimport { NappletResolutionError } from './errors.js';\n\nfunction firstTagValue(tags: readonly (readonly string[])[], name: string): string | undefined {\n for (const tag of tags) {\n if (tag[0] === name && typeof tag[1] === 'string' && tag[1].length > 0) return tag[1];\n }\n return undefined;\n}\n\nfunction allTagValues(tags: readonly (readonly string[])[], name: string): string[] {\n const out: string[] = [];\n for (const tag of tags) {\n if (tag[0] === name && typeof tag[1] === 'string' && tag[1].length > 0) out.push(tag[1]);\n }\n return out;\n}\n\nfunction archetypesFromTags(\n tags: readonly (readonly string[])[],\n): Array<{ slug: string; convention: string }> {\n const out: Array<{ slug: string; convention: string }> = [];\n for (const tag of tags) {\n if (tag[0] !== 'archetype') continue;\n const slug = tag[1];\n if (typeof slug !== 'string' || !/^[a-z0-9][a-z0-9-]*$/.test(slug)) {\n throw new NappletResolutionError(\n 'invalid-manifest',\n 'archetype slug must contain lowercase letters, numbers, and hyphens',\n );\n }\n\n const convention = tag[2];\n if (typeof convention !== 'string' || convention.length === 0) {\n throw new NappletResolutionError('invalid-manifest', 'archetype convention is required');\n }\n if (/^NAP-\\d+$/.test(convention)) {\n throw new NappletResolutionError(\n 'invalid-manifest',\n 'numbered NAP identifier is not an archetype convention',\n );\n }\n if (!/^napplet:[^/?#\\s]+\\/[^/?#\\s]+$/.test(convention)) {\n throw new NappletResolutionError(\n 'invalid-manifest',\n 'archetype convention must be a queryless napplet:<archetype>/<intent> identity',\n );\n }\n // NAP-INTENT keeps a routing archetype and a payload convention\n // orthogonal: one convention may serve multiple archetypes and vice versa.\n\n if (tag.length !== 3) {\n throw new NappletResolutionError(\n 'invalid-manifest',\n 'archetype tags must contain exactly slug and convention',\n );\n }\n out.push({ slug, convention });\n }\n return out;\n}\n\nexport function parseLegacyManifest(event: NostrEvent): NappletManifest {\n const paths = pathEntriesFromTags(event.tags);\n if (paths.length === 0) {\n throw new NappletResolutionError('invalid-manifest', 'manifest has no path tags');\n }\n const aggregateHash = aggregateTagValue(event.tags);\n if (!aggregateHash) {\n throw new NappletResolutionError('invalid-manifest', 'manifest has no aggregate x tag');\n }\n const dTag = firstTagValue(event.tags, 'd');\n if (event.kind !== 35129 && dTag !== undefined) {\n throw new NappletResolutionError('invalid-manifest', 'only named manifests may carry a d tag');\n }\n return {\n format: 'legacy',\n artifactHash: aggregateHash,\n optional: [],\n kind: event.kind,\n pubkey: event.pubkey,\n dTag: dTag ?? '',\n paths,\n aggregateHash,\n servers: allTagValues(event.tags, 'server'),\n requires: allTagValues(event.tags, 'requires'),\n archetypes: archetypesFromTags(event.tags),\n title: firstTagValue(event.tags, 'title'),\n description: firstTagValue(event.tags, 'description'),\n source: firstTagValue(event.tags, 'source'),\n };\n}\n\n\n/** Compatibility-only aggregate check; current manifests never enter this path. */\nexport function verifyLegacyAggregate(manifest: NappletManifest): void {\n const recomputed = computeAggregateHash(manifest.paths);\n if (recomputed !== manifest.aggregateHash) {\n throw new NappletResolutionError('aggregate-mismatch',\n `recomputed aggregate ${recomputed} != manifest ${manifest.aggregateHash}`);\n }\n}\n\nlet warnedLegacyManifest = false;\n\n/** Warn once per module lifetime, after a legacy artifact has passed verification. */\nexport function warnLegacyManifest(): void {\n if (warnedLegacyManifest) return;\n warnedLegacyManifest = true;\n console.warn('[KEHTO_COMPAT_LEGACY_MANIFEST] Legacy aggregate manifest resolved. Republish with @napplet/vite-plugin >=0.15 using the current single-artifact schema. See docs/compatibility.md.');\n}\n","import type { NostrEvent } from 'nostr-tools';\nimport type { NappletManifest } from './index.js';\nimport { NappletResolutionError } from './errors.js';\n\nconst HASH = /^[a-f0-9]{64}$/;\nconst DOMAIN = /^[a-z][a-z0-9-]*$/;\nconst SLUG = /^[a-z0-9][a-z0-9-]*$/;\nconst INTENT = /^napplet:[^/?#\\s]+\\/[^/?#\\s]+$/;\n\nfunction invalid(message: string): never {\n throw new NappletResolutionError('invalid-manifest', message);\n}\n\nfunction single(tags: string[][], name: string): string | undefined {\n const matches = tags.filter((tag) => tag[0] === name);\n if (matches.length > 1 || matches.some((tag) => tag.length !== 2 || !tag[1])) {\n invalid(`manifest ${name} tag must have one value and occur at most once`);\n }\n return matches[0]?.[1];\n}\n\nfunction declarations(tags: string[][], name: string, pattern: RegExp): string[] {\n return [...new Set(tags.filter((tag) => tag[0] === name).map((tag) => {\n if (tag.length !== 2 || !pattern.test(tag[1])) invalid(`invalid ${name} declaration`);\n return tag[1];\n }))];\n}\n\nfunction iconFromTags(tags: string[][]): NappletManifest['icon'] {\n const icons = tags.filter((tag) => tag[0] === 'icon');\n if (icons.length !== 1) return undefined;\n const [, hash, mimeType] = icons[0];\n if (icons[0].length !== 3 || !HASH.test(hash)) return undefined;\n if (mimeType !== 'image/png' && mimeType !== 'image/jpeg' && mimeType !== 'image/webp') {\n return undefined;\n }\n // A declaration is not verified image data. Hosts retain generic artwork until\n // both its hash and decoded image format have been checked independently.\n return { sha256: hash, mimeType };\n}\n\n/** Parse the single-artifact schema from NIP-5D #2303, 020cb8b. */\nexport function parseCurrentManifest(event: NostrEvent): NappletManifest {\n const tags = event.tags;\n const artifactHash = single(tags, 'x');\n if (!artifactHash || !HASH.test(artifactHash)) invalid('invalid artifact x hash');\n if (typeof event.content !== 'string' || !event.content.trim()) {\n invalid('manifest content must be a non-empty plain-text description');\n }\n const dTag = single(tags, 'd');\n if (event.kind === 35129 ? dTag === undefined : dTag !== undefined) {\n invalid('exactly one d tag is required only on named manifests');\n }\n const parent = single(tags, 'a');\n const root = single(tags, 'A');\n for (const address of [parent, root]) {\n if (address === undefined) continue;\n if (event.kind !== 5129 || !/^(35129:[a-f0-9]{64}:.+|15129:[a-f0-9]{64}:)$/.test(address)) {\n invalid('lineage must be a napplet address on a snapshot');\n }\n }\n const archetypeSlugs = declarations(tags, 'z', SLUG);\n const intents = tags.filter((tag) => tag[0] === 'i').map((tag) => {\n const [, identity, ...parameters] = tag;\n if (!identity || !INTENT.test(identity)\n || parameters.some((parameter) => !parameter || /\\s/.test(parameter))) {\n invalid('invalid accepted intent or parameter name');\n }\n return { identity, parameters };\n });\n return {\n format: 'current',\n kind: event.kind,\n pubkey: event.pubkey,\n dTag: dTag ?? '',\n artifactHash,\n aggregateHash: artifactHash,\n paths: [{ path: '/index.html', sha256: artifactHash }],\n servers: tags.filter((tag) => tag[0] === 'server' && tag[1]).map((tag) => tag[1]),\n requires: declarations(tags, 'R', DOMAIN),\n optional: declarations(tags, 'O', DOMAIN),\n archetypeSlugs,\n intents,\n // NAP-INTENT keeps roles and conventions orthogonal. The current schema\n // advertises independent sets; legacy explicit pairings stay in its adapter.\n archetypes: archetypeSlugs.flatMap((slug) => intents.map(({ identity }) => ({\n slug, convention: identity,\n }))),\n title: single(tags, 'title'),\n description: event.content,\n source: single(tags, 'source'),\n icon: iconFromTags(tags),\n parent,\n root,\n };\n}\n","import type { NostrEvent } from 'nostr-tools';\nimport type { NappletManifest } from './index.js';\n\nconst MIB = 1024 * 1024;\n\n/** Versioned Cache Storage namespace for verified napplet artifacts. */\nexport const NAPPLET_ARTIFACT_CACHE_NAME = 'kehto:napplet-artifacts:v1';\nconst SCHEMA_VERSION = 1;\nconst DEFAULT_BASE_URL = 'https://kehto.invalid';\nconst DEFAULT_UNKNOWN_BUDGET_BYTES = 32 * MIB;\nconst DEFAULT_SOFT_CEILING_BYTES = 128 * MIB;\nconst DEFAULT_HARD_CEILING_BYTES = 256 * MIB;\nconst DEFAULT_FOREGROUND_TTL_MS = 15 * 60 * 1000;\nconst DEFAULT_BACKGROUND_TTL_MS = 60 * 60 * 1000;\n\nexport interface NappletCacheDiagnostic {\n /** Diagnostic category for cache open/read/write/delete/prune/storage-estimate failures. */\n type:\n | 'cache-open-failed'\n | 'cache-read-failed'\n | 'cache-write-failed'\n | 'cache-delete-failed'\n | 'cache-prune-failed'\n | 'storage-estimate-unavailable';\n /** Human-readable diagnostic detail suitable for logs. */\n message: string;\n /** Original thrown value, when one exists. */\n cause?: unknown;\n}\n\n/** Metadata tracked for one cached napplet artifact blob. */\nexport interface NappletBlobIndexEntry {\n /** Blob byte length. */\n size: number;\n /** Last read or write timestamp in milliseconds. */\n lastAccessed: number;\n /** Number of cached aggregate records currently referencing this blob. */\n refCount: number;\n}\n\n/** Metadata tracked for one verified aggregate cache record. */\nexport interface NappletAggregateIndexEntry {\n /** Manifest `d` tag. */\n dTag: string;\n /** Verified NIP-5A aggregate hash. */\n aggregateHash: string;\n /** Internal cache key derived from `dTag` and `aggregateHash`. */\n key: string;\n /** SHA-256 hashes of blobs referenced by this aggregate. */\n blobHashes: string[];\n /** Approximate stored aggregate-record byte size. */\n size: number;\n /** Last read or write timestamp in milliseconds. */\n lastAccessed: number;\n /** Coordinate cache key that currently points at this aggregate, if any. */\n coordinateKey?: string;\n /** Reserved pin marker for hosts that want to protect an aggregate. */\n pinned?: boolean;\n}\n\n/** Cached mapping from a NIP-5D coordinate to a verified aggregate. */\nexport interface CachedCoordinate {\n /** Internal coordinate key in the form `kind:pubkey:dTag`. */\n key: string;\n /** Napplet manifest event kind. */\n kind: number;\n /** Manifest author public key. */\n pubkey: string;\n /** Manifest `d` tag. */\n dTag: string;\n /** Verified aggregate hash for the coordinate. */\n aggregateHash: string;\n /** Timestamp when this coordinate last resolved successfully. */\n lastResolvedAt: number;\n /** Foreground refresh TTL in milliseconds. */\n foregroundTtlMs: number;\n /** Background refresh TTL in milliseconds. */\n backgroundTtlMs: number;\n}\n\n/** Serialized Cache Storage index for verified napplet artifacts. */\nexport interface NappletArtifactCacheIndex {\n /** Index schema version. */\n schemaVersion: number;\n /** Last index update timestamp in milliseconds. */\n updatedAt: number;\n /** Blob entries keyed by SHA-256 hash. */\n blobs: Record<string, NappletBlobIndexEntry>;\n /** Aggregate entries keyed by the internal aggregate key. */\n aggregates: Record<string, NappletAggregateIndexEntry>;\n /** Coordinate entries keyed by {@link coordinateKey}. */\n coordinates: Record<string, CachedCoordinate>;\n}\n\ninterface AggregateRecord {\n schemaVersion: number;\n dTag: string;\n aggregateHash: string;\n manifest: {\n kind: number;\n pubkey: string;\n dTag: string;\n paths: Array<{ path: string; sha256: string }>;\n requires: string[];\n title?: string;\n description?: string;\n };\n indexHtml: string;\n blobHashes: string[];\n writtenAt: number;\n}\n\nexport interface WriteVerifiedResolutionInput {\n /** Verified NIP-5D manifest event. */\n event: NostrEvent;\n /** Parsed manifest whose aggregate and paths have already been verified. */\n manifest: NappletManifest;\n /** File bytes keyed by manifest path. */\n files: ReadonlyMap<string, Uint8Array>;\n /** Verified `/index.html` bytes decoded as text. */\n indexHtml: string;\n /** Timestamp override for deterministic tests. */\n now?: number;\n}\n\n/** Freshness controls for reading a cached coordinate. */\nexport interface CoordinateFreshnessOptions {\n /** Timestamp override in milliseconds. */\n now?: number;\n /** Use the background TTL instead of the foreground TTL. */\n background?: boolean;\n}\n\n/** Cache interface for verified NIP-5D napplet artifacts. */\nexport interface NappletArtifactCache {\n /** Read one cached artifact blob by SHA-256 hash. */\n readBlob(sha256Hex: string): Promise<Uint8Array | undefined>;\n /** Delete one cached artifact blob by SHA-256 hash. */\n deleteBlob(sha256Hex: string): Promise<void>;\n /** Write a verified manifest resolution and all referenced blobs. */\n writeVerifiedResolution(input: WriteVerifiedResolutionInput): Promise<void>;\n /** Cache the latest aggregate for a NIP-5D coordinate. */\n writeCoordinate(input: {\n kind: number;\n pubkey: string;\n dTag: string;\n aggregateHash: string;\n lastResolvedAt?: number;\n foregroundTtlMs?: number;\n backgroundTtlMs?: number;\n }): Promise<void>;\n /** Read a cached coordinate with a computed freshness flag. */\n getCoordinate(input: {\n kind: number;\n pubkey: string;\n dTag: string;\n }, options?: CoordinateFreshnessOptions): Promise<(CachedCoordinate & { fresh: boolean }) | undefined>;\n /** Update aggregate/blob last-access timestamps. */\n touchAggregate(dTag: string, aggregateHash: string, now?: number): Promise<void>;\n /** Protect an aggregate from normal pruning while it is in active use. */\n markAggregateActive(dTag: string, aggregateHash: string): void;\n /** Release an aggregate previously marked active. */\n releaseAggregateActive(dTag: string, aggregateHash: string): void;\n /** Prune cache contents to the configured storage budget. */\n prune(now?: number): Promise<void>;\n}\n\n/** Constructor options for {@link CacheStorageNappletArtifactCache}. */\nexport interface CacheStorageNappletArtifactCacheOptions {\n /** Cache Storage implementation; defaults to `globalThis.caches`. */\n cacheStorage?: CacheStorage;\n /** Storage manager used for quota estimates; defaults to `navigator.storage`. */\n storage?: StorageManager;\n /** Cache Storage namespace. */\n cacheName?: string;\n /** Base URL used to construct synthetic cache keys. */\n baseUrl?: string;\n /** Clock used for timestamps. */\n now?: () => number;\n /** Diagnostic callback for non-fatal cache failures. */\n onDiagnostic?: (diagnostic: NappletCacheDiagnostic) => void;\n /** Budget used when storage estimates are unavailable. */\n unknownBudgetBytes?: number;\n /** Soft cache ceiling in bytes. */\n softCeilingBytes?: number;\n /** Hard cache ceiling in bytes. */\n hardCeilingBytes?: number;\n}\n\n/** Options for opening the browser Cache Storage artifact cache. */\nexport interface OpenNappletArtifactCacheOptions extends CacheStorageNappletArtifactCacheOptions {\n /** Return `undefined` when quota estimates are unavailable. */\n requireStorageEstimate?: boolean;\n}\n\nfunction emptyIndex(now: number): NappletArtifactCacheIndex {\n return {\n schemaVersion: SCHEMA_VERSION,\n updatedAt: now,\n blobs: {},\n aggregates: {},\n coordinates: {},\n };\n}\n\nfunction aggregateKey(dTag: string, aggregateHash: string): string {\n return `${encodeURIComponent(dTag)}:${aggregateHash}`;\n}\n\n/**\n * Build the stable index key for a NIP-5D coordinate.\n *\n * @param kind - Manifest event kind.\n * @param pubkey - Manifest author public key.\n * @param dTag - Manifest `d` tag.\n * @returns Cache key in the form `kind:pubkey:dTag`.\n */\nexport function coordinateKey(kind: number, pubkey: string, dTag: string): string {\n return `${kind}:${pubkey}:${encodeURIComponent(dTag)}`;\n}\n\n/**\n * Check whether a cached coordinate is still inside its foreground/background TTL.\n *\n * @param coordinate - Cached coordinate to inspect.\n * @param options - Optional clock and TTL mode overrides.\n * @returns `true` when the coordinate can be used without a network refresh.\n */\nexport function isCoordinateFresh(\n coordinate: CachedCoordinate,\n options: CoordinateFreshnessOptions = {},\n): boolean {\n const now = options.now ?? Date.now();\n const ttl = options.background ? coordinate.backgroundTtlMs : coordinate.foregroundTtlMs;\n return ttl === Number.POSITIVE_INFINITY || now - coordinate.lastResolvedAt <= ttl;\n}\n\nfunction byteLength(bytes: Uint8Array): number {\n return bytes.byteLength;\n}\n\nfunction encodeKeyPart(value: string): string {\n return encodeURIComponent(value).replaceAll('%2F', '%252F');\n}\n\nfunction cloneBytes(bytes: Uint8Array): Uint8Array {\n return new Uint8Array(bytes);\n}\n\nfunction responseBody(bytes: Uint8Array): ArrayBuffer {\n return cloneBytes(bytes).buffer as ArrayBuffer;\n}\n\nfunction normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;\n}\n\nfunction resolveBaseUrl(baseUrl?: string): string {\n if (baseUrl) return normalizeBaseUrl(baseUrl);\n const maybeLocation = globalThis as typeof globalThis & { location?: { origin?: string } };\n if (maybeLocation.location?.origin) return normalizeBaseUrl(maybeLocation.location.origin);\n return normalizeBaseUrl(DEFAULT_BASE_URL);\n}\n\nasync function responseBytes(response: Response): Promise<Uint8Array> {\n return new Uint8Array(await response.arrayBuffer());\n}\n\nasync function responseJson<T>(response: Response): Promise<T> {\n return response.json() as Promise<T>;\n}\n\nfunction refCounts(index: NappletArtifactCacheIndex): Record<string, number> {\n const counts: Record<string, number> = {};\n for (const aggregate of Object.values(index.aggregates)) {\n for (const hash of aggregate.blobHashes) counts[hash] = (counts[hash] ?? 0) + 1;\n }\n return counts;\n}\n\nfunction cachedSize(index: NappletArtifactCacheIndex): number {\n const blobBytes = Object.values(index.blobs).reduce((sum, entry) => sum + entry.size, 0);\n const aggregateBytes = Object.values(index.aggregates).reduce((sum, entry) => sum + entry.size, 0);\n return blobBytes + aggregateBytes;\n}\n\n/**\n * Browser Cache Storage implementation of {@link NappletArtifactCache}.\n *\n * It stores verified blobs and aggregate records under synthetic URLs, keeps a\n * small JSON index, and prunes according to quota-derived budgets.\n */\nexport class CacheStorageNappletArtifactCache implements NappletArtifactCache {\n private readonly cacheStorage: CacheStorage;\n private readonly storage?: StorageManager;\n private readonly cacheName: string;\n private readonly baseUrl: string;\n private readonly now: () => number;\n private readonly activeAggregates = new Set<string>();\n private readonly onDiagnostic?: (diagnostic: NappletCacheDiagnostic) => void;\n private readonly unknownBudgetBytes: number;\n private readonly softCeilingBytes: number;\n private readonly hardCeilingBytes: number;\n private cachePromise?: Promise<Cache>;\n\n constructor(options: CacheStorageNappletArtifactCacheOptions = {}) {\n const maybeCacheStorage = options.cacheStorage ?? (globalThis as typeof globalThis & { caches?: CacheStorage }).caches;\n if (!maybeCacheStorage) {\n throw new Error('Cache Storage is unavailable');\n }\n this.cacheStorage = maybeCacheStorage;\n this.storage = options.storage ?? (globalThis.navigator as Navigator | undefined)?.storage;\n this.cacheName = options.cacheName ?? NAPPLET_ARTIFACT_CACHE_NAME;\n this.baseUrl = resolveBaseUrl(options.baseUrl);\n this.now = options.now ?? Date.now;\n this.onDiagnostic = options.onDiagnostic;\n this.unknownBudgetBytes = options.unknownBudgetBytes ?? DEFAULT_UNKNOWN_BUDGET_BYTES;\n this.softCeilingBytes = options.softCeilingBytes ?? DEFAULT_SOFT_CEILING_BYTES;\n this.hardCeilingBytes = options.hardCeilingBytes ?? DEFAULT_HARD_CEILING_BYTES;\n }\n\n async readBlob(sha256Hex: string): Promise<Uint8Array | undefined> {\n try {\n const cache = await this.open();\n const response = await cache.match(this.blobUrl(sha256Hex));\n if (!response) return undefined;\n const bytes = await responseBytes(response);\n const index = await this.readIndex(cache);\n const entry = index.blobs[sha256Hex];\n if (entry) {\n entry.lastAccessed = this.now();\n index.updatedAt = entry.lastAccessed;\n await this.writeIndex(cache, index);\n }\n return bytes;\n } catch (cause) {\n this.diagnostic({ type: 'cache-read-failed', message: `failed to read blob ${sha256Hex}`, cause });\n return undefined;\n }\n }\n\n async deleteBlob(sha256Hex: string): Promise<void> {\n try {\n const cache = await this.open();\n await cache.delete(this.blobUrl(sha256Hex));\n const index = await this.readIndex(cache);\n delete index.blobs[sha256Hex];\n index.updatedAt = this.now();\n await this.writeIndex(cache, index);\n } catch (cause) {\n this.diagnostic({ type: 'cache-delete-failed', message: `failed to delete blob ${sha256Hex}`, cause });\n }\n }\n\n async writeVerifiedResolution(input: WriteVerifiedResolutionInput): Promise<void> {\n try {\n await this.writeVerifiedResolutionOnce(input);\n } catch (firstCause) {\n try {\n await this.prune(input.now ?? this.now());\n await this.writeVerifiedResolutionOnce(input);\n } catch (cause) {\n this.diagnostic({\n type: 'cache-write-failed',\n message: 'failed to write verified napplet artifacts after prune retry',\n cause: cause ?? firstCause,\n });\n }\n }\n }\n\n async writeCoordinate(input: {\n kind: number;\n pubkey: string;\n dTag: string;\n aggregateHash: string;\n lastResolvedAt?: number;\n foregroundTtlMs?: number;\n backgroundTtlMs?: number;\n }): Promise<void> {\n const cache = await this.open();\n const now = this.now();\n const index = await this.readIndex(cache);\n const key = coordinateKey(input.kind, input.pubkey, input.dTag);\n index.coordinates[key] = {\n key,\n kind: input.kind,\n pubkey: input.pubkey,\n dTag: input.dTag,\n aggregateHash: input.aggregateHash,\n lastResolvedAt: input.lastResolvedAt ?? now,\n foregroundTtlMs: input.foregroundTtlMs ?? DEFAULT_FOREGROUND_TTL_MS,\n backgroundTtlMs: input.backgroundTtlMs ?? DEFAULT_BACKGROUND_TTL_MS,\n };\n index.updatedAt = now;\n await this.writeIndex(cache, index);\n }\n\n async getCoordinate(\n input: { kind: number; pubkey: string; dTag: string },\n options: CoordinateFreshnessOptions = {},\n ): Promise<(CachedCoordinate & { fresh: boolean }) | undefined> {\n const cache = await this.open();\n const index = await this.readIndex(cache);\n const coordinate = index.coordinates[coordinateKey(input.kind, input.pubkey, input.dTag)];\n if (!coordinate) return undefined;\n return { ...coordinate, fresh: isCoordinateFresh(coordinate, options) };\n }\n\n async touchAggregate(dTag: string, aggregateHash: string, now: number = this.now()): Promise<void> {\n const cache = await this.open();\n const index = await this.readIndex(cache);\n const key = aggregateKey(dTag, aggregateHash);\n const aggregate = index.aggregates[key];\n if (!aggregate) return;\n aggregate.lastAccessed = now;\n for (const hash of aggregate.blobHashes) {\n if (index.blobs[hash]) index.blobs[hash].lastAccessed = now;\n }\n index.updatedAt = now;\n await this.writeIndex(cache, index);\n }\n\n markAggregateActive(dTag: string, aggregateHash: string): void {\n this.activeAggregates.add(aggregateKey(dTag, aggregateHash));\n }\n\n releaseAggregateActive(dTag: string, aggregateHash: string): void {\n this.activeAggregates.delete(aggregateKey(dTag, aggregateHash));\n }\n\n async prune(now: number = this.now()): Promise<void> {\n try {\n const cache = await this.open();\n const index = await this.readIndex(cache);\n let size = cachedSize(index);\n const budget = await this.budgetBytes();\n const hardBudget = await this.hardBudgetBytes();\n const shouldPrune = size > budget || await this.originPressureHigh();\n if (!shouldPrune) return;\n\n this.rewriteRefCounts(index);\n\n for (const [hash, blob] of Object.entries(index.blobs)) {\n if (size <= budget) break;\n if (blob.refCount > 0) continue;\n await cache.delete(this.blobUrl(hash));\n delete index.blobs[hash];\n size -= blob.size;\n }\n\n const currentAggregateHashes = new Set(\n Object.values(index.coordinates).map((coordinate) => coordinate.aggregateHash),\n );\n const aggregates = Object.values(index.aggregates)\n .filter((entry) => !this.activeAggregates.has(entry.key))\n .sort((a, b) => {\n const currentA = currentAggregateHashes.has(a.aggregateHash) ? 1 : 0;\n const currentB = currentAggregateHashes.has(b.aggregateHash) ? 1 : 0;\n if (currentA !== currentB) return currentA - currentB;\n return a.lastAccessed - b.lastAccessed;\n });\n\n for (const aggregate of aggregates) {\n if (size <= budget) break;\n await cache.delete(this.aggregateUrl(aggregate.key));\n delete index.aggregates[aggregate.key];\n size -= aggregate.size;\n this.rewriteRefCounts(index);\n }\n\n if (size > hardBudget) {\n const active = Object.values(index.aggregates)\n .filter((entry) => this.activeAggregates.has(entry.key))\n .sort((a, b) => a.lastAccessed - b.lastAccessed);\n for (const aggregate of active) {\n if (size <= hardBudget) break;\n await cache.delete(this.aggregateUrl(aggregate.key));\n delete index.aggregates[aggregate.key];\n size -= aggregate.size;\n }\n }\n\n this.rewriteRefCounts(index);\n for (const [hash, blob] of Object.entries(index.blobs)) {\n if (blob.refCount === 0) {\n await cache.delete(this.blobUrl(hash));\n delete index.blobs[hash];\n }\n }\n index.updatedAt = now;\n await this.writeIndex(cache, index);\n } catch (cause) {\n this.diagnostic({ type: 'cache-prune-failed', message: 'failed to prune napplet artifact cache', cause });\n }\n }\n\n async snapshotIndex(): Promise<NappletArtifactCacheIndex> {\n return this.readIndex(await this.open());\n }\n\n private async writeVerifiedResolutionOnce(input: WriteVerifiedResolutionInput): Promise<void> {\n const cache = await this.open();\n const now = input.now ?? this.now();\n const index = await this.readIndex(cache);\n const blobHashes: string[] = [];\n\n for (const pathEntry of input.manifest.paths) {\n const bytes = input.files.get(pathEntry.path);\n if (!bytes) continue;\n blobHashes.push(pathEntry.sha256);\n await cache.put(this.blobUrl(pathEntry.sha256), new Response(responseBody(bytes)));\n index.blobs[pathEntry.sha256] = {\n size: byteLength(bytes),\n lastAccessed: now,\n refCount: index.blobs[pathEntry.sha256]?.refCount ?? 0,\n };\n }\n\n const key = aggregateKey(input.manifest.dTag, input.manifest.aggregateHash);\n const record: AggregateRecord = {\n schemaVersion: SCHEMA_VERSION,\n dTag: input.manifest.dTag,\n aggregateHash: input.manifest.aggregateHash,\n manifest: {\n kind: input.manifest.kind,\n pubkey: input.manifest.pubkey,\n dTag: input.manifest.dTag,\n paths: input.manifest.paths.map((entry) => ({ path: entry.path, sha256: entry.sha256 })),\n requires: [...input.manifest.requires],\n ...(input.manifest.title === undefined ? {} : { title: input.manifest.title }),\n ...(input.manifest.description === undefined ? {} : { description: input.manifest.description }),\n },\n indexHtml: input.indexHtml,\n blobHashes,\n writtenAt: now,\n };\n const aggregateBytes = new TextEncoder().encode(JSON.stringify(record));\n await cache.put(this.aggregateUrl(key), new Response(responseBody(aggregateBytes), {\n headers: { 'content-type': 'application/json' },\n }));\n\n const coordKey = coordinateKey(input.manifest.kind, input.manifest.pubkey, input.manifest.dTag);\n index.aggregates[key] = {\n dTag: input.manifest.dTag,\n aggregateHash: input.manifest.aggregateHash,\n key,\n blobHashes,\n size: byteLength(aggregateBytes),\n lastAccessed: now,\n coordinateKey: coordKey,\n };\n index.coordinates[coordKey] = {\n key: coordKey,\n kind: input.manifest.kind,\n pubkey: input.manifest.pubkey,\n dTag: input.manifest.dTag,\n aggregateHash: input.manifest.aggregateHash,\n lastResolvedAt: now,\n foregroundTtlMs: DEFAULT_FOREGROUND_TTL_MS,\n backgroundTtlMs: DEFAULT_BACKGROUND_TTL_MS,\n };\n\n this.rewriteRefCounts(index);\n index.updatedAt = now;\n await this.writeIndex(cache, index);\n await this.prune(now);\n }\n\n private async open(): Promise<Cache> {\n this.cachePromise ??= this.cacheStorage.open(this.cacheName);\n return this.cachePromise;\n }\n\n private indexUrl(): string {\n return new URL('__kehto_napplet_cache/index.json', this.baseUrl).toString();\n }\n\n private blobUrl(hash: string): string {\n return new URL(`__kehto_napplet_cache/blob/${encodeKeyPart(hash)}`, this.baseUrl).toString();\n }\n\n private aggregateUrl(key: string): string {\n return new URL(`__kehto_napplet_cache/aggregate/${encodeKeyPart(key)}.json`, this.baseUrl).toString();\n }\n\n private async readIndex(cache: Cache): Promise<NappletArtifactCacheIndex> {\n const response = await cache.match(this.indexUrl());\n if (!response) return emptyIndex(this.now());\n try {\n const index = await responseJson<NappletArtifactCacheIndex>(response);\n if (index.schemaVersion !== SCHEMA_VERSION) return emptyIndex(this.now());\n return index;\n } catch {\n return emptyIndex(this.now());\n }\n }\n\n private async writeIndex(cache: Cache, index: NappletArtifactCacheIndex): Promise<void> {\n await cache.put(this.indexUrl(), new Response(JSON.stringify(index), {\n headers: { 'content-type': 'application/json' },\n }));\n }\n\n private rewriteRefCounts(index: NappletArtifactCacheIndex): void {\n const counts = refCounts(index);\n for (const [hash, blob] of Object.entries(index.blobs)) {\n blob.refCount = counts[hash] ?? 0;\n }\n }\n\n private async budgetBytes(): Promise<number> {\n const estimate = await this.estimateStorage();\n if (!estimate?.quota) return this.unknownBudgetBytes;\n return Math.min(this.softCeilingBytes, Math.floor(estimate.quota * 0.1));\n }\n\n private async hardBudgetBytes(): Promise<number> {\n const estimate = await this.estimateStorage();\n if (!estimate?.quota) return this.unknownBudgetBytes;\n return Math.min(this.hardCeilingBytes, Math.floor(estimate.quota * 0.2));\n }\n\n private async originPressureHigh(): Promise<boolean> {\n const estimate = await this.estimateStorage();\n if (!estimate?.quota || estimate.usage === undefined) return false;\n return estimate.usage / estimate.quota >= 0.8;\n }\n\n private async estimateStorage(): Promise<StorageEstimate | undefined> {\n if (!this.storage?.estimate) return undefined;\n try {\n return this.storage.estimate();\n } catch (cause) {\n this.diagnostic({\n type: 'storage-estimate-unavailable',\n message: 'storage estimate failed; using unknown quota cache budget',\n cause,\n });\n return undefined;\n }\n }\n\n private diagnostic(diagnostic: NappletCacheDiagnostic): void {\n this.onDiagnostic?.(diagnostic);\n }\n}\n\n/**\n * Open the browser Cache Storage artifact cache when the host supports it.\n *\n * @param options - Cache construction options and diagnostics callback.\n * @returns A cache instance, or `undefined` when Cache Storage is unavailable.\n */\nexport async function openNappletArtifactCache(\n options: OpenNappletArtifactCacheOptions = {},\n): Promise<CacheStorageNappletArtifactCache | undefined> {\n const cacheStorage = options.cacheStorage ?? (globalThis as typeof globalThis & { caches?: CacheStorage }).caches;\n if (!cacheStorage) return undefined;\n const storage = options.storage ?? (globalThis.navigator as Navigator | undefined)?.storage;\n if (options.requireStorageEstimate && !storage?.estimate) {\n options.onDiagnostic?.({\n type: 'storage-estimate-unavailable',\n message: 'storage estimates are unavailable; using network-only loading',\n });\n return undefined;\n }\n try {\n const cache = new CacheStorageNappletArtifactCache({ ...options, cacheStorage, storage });\n await cache.snapshotIndex();\n return cache;\n } catch (cause) {\n options.onDiagnostic?.({\n type: 'cache-open-failed',\n message: 'Cache Storage napplet artifact cache could not be opened',\n cause,\n });\n return undefined;\n }\n}\n"],"mappings":"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","names":[]}