@tokenoftrust/storefront-runner 3.6.1 → 3.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
|
|
6
6
|
"packageManager": "pnpm@11.9.0",
|
|
@@ -24,6 +24,14 @@ export const MAX_ASSET_REGISTRY_BYTES = 32 * 1024 * 1024;
|
|
|
24
24
|
export type VerificationMethod = "byte-read" | "create-readback" | "audit";
|
|
25
25
|
export interface VerifiedFacet { size: number; etag: string; at: string; how: VerificationMethod }
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* The response policy a stored pool image carries as object metadata (S3 only: CloudFront emits it;
|
|
29
|
+
* the Worker sets R2's itself). `csp` is the digest (`sha256:<hex>`) of the policy that was observed
|
|
30
|
+
* on the object, `etag` the object it was observed on. It describes that stored object, so a
|
|
31
|
+
* different ETag or a different policy digest makes it stale.
|
|
32
|
+
*/
|
|
33
|
+
export interface HeadersFacet { csp: string; etag: string; at: string }
|
|
34
|
+
|
|
27
35
|
export type AssetProvenanceKind = "capture" | "upload" | "repo" | "variant";
|
|
28
36
|
export interface AssetProvenance {
|
|
29
37
|
kind: AssetProvenanceKind;
|
|
@@ -74,6 +82,7 @@ export interface AssetRecord {
|
|
|
74
82
|
/** Entries (`<sha256><ext>`) derived from this one. */
|
|
75
83
|
renditions?: string[];
|
|
76
84
|
verified?: VerifiedFacet;
|
|
85
|
+
headers?: HeadersFacet;
|
|
77
86
|
retention?: RetentionFacet;
|
|
78
87
|
}
|
|
79
88
|
|
|
@@ -97,12 +106,14 @@ export interface AssetWrite {
|
|
|
97
106
|
recipe?: AssetRecipe;
|
|
98
107
|
rendition?: string;
|
|
99
108
|
verified?: VerifiedFacet;
|
|
109
|
+
headers?: HeadersFacet;
|
|
100
110
|
retention?: { to: RetentionClass; at: string; how: string; actor?: string };
|
|
101
111
|
}
|
|
102
112
|
|
|
103
113
|
const ENTRY_RE = /^([0-9a-f]{64})(\.[A-Za-z0-9]+)?$/;
|
|
104
114
|
const POOL_KEY_RE = /^assets\/([0-9a-f]{64})(\.[A-Za-z0-9]+)?$/;
|
|
105
115
|
const ETAG_RE = /^[A-Za-z0-9-]{1,128}$/;
|
|
116
|
+
const POLICY_DIGEST_RE = /^sha256:[0-9a-f]{64}$/;
|
|
106
117
|
const TENANT_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/;
|
|
107
118
|
const ACTOR_RE = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
108
119
|
const REVISION_RE = /^[A-Za-z0-9._-]{1,128}$/;
|
|
@@ -172,6 +183,12 @@ function normalizeVerified(raw: unknown): VerifiedFacet | null {
|
|
|
172
183
|
return { size: raw.size as number, etag: raw.etag, at: raw.at, how: raw.how as VerificationMethod };
|
|
173
184
|
}
|
|
174
185
|
|
|
186
|
+
function normalizeHeaders(raw: unknown): HeadersFacet | null {
|
|
187
|
+
if (!isObject(raw) || typeof raw.csp !== "string" || !POLICY_DIGEST_RE.test(raw.csp) ||
|
|
188
|
+
typeof raw.etag !== "string" || !ETAG_RE.test(raw.etag) || !validTime(raw.at)) return null;
|
|
189
|
+
return { csp: raw.csp, etag: raw.etag, at: raw.at };
|
|
190
|
+
}
|
|
191
|
+
|
|
175
192
|
function normalizeProvenance(raw: unknown): AssetProvenance | null {
|
|
176
193
|
if (!isObject(raw) || typeof raw.kind !== "string" || !PROVENANCE_KINDS.has(raw.kind) || !validTime(raw.at)) return null;
|
|
177
194
|
if (raw.locator !== undefined && !validLocator(raw.locator)) return null;
|
|
@@ -266,6 +283,8 @@ export function normalizeAssetRecord(entry: string, raw: unknown): AssetRecord |
|
|
|
266
283
|
}
|
|
267
284
|
const verified = normalizeVerified(raw.verified);
|
|
268
285
|
if (verified) record.verified = verified;
|
|
286
|
+
const headers = normalizeHeaders(raw.headers);
|
|
287
|
+
if (headers) record.headers = headers;
|
|
269
288
|
const retention = normalizeRetention(raw.retention);
|
|
270
289
|
if (retention) record.retention = retention;
|
|
271
290
|
return record;
|
|
@@ -305,7 +324,7 @@ export function isEmptyAssetRecord(record: AssetRecord): boolean {
|
|
|
305
324
|
* - `mediaType`, `size`, `provenance`, `owner`, `recipe`: the first writer's value stands. These
|
|
306
325
|
* describe immutable bytes, so a later writer has nothing to add.
|
|
307
326
|
* - `tenants`, `renditions`: a union.
|
|
308
|
-
* - `verified`: the latest
|
|
327
|
+
* - `verified`, `headers`: the latest observation replaces the earlier one.
|
|
309
328
|
* - `retention`: a transition is applied only when the current class allows it
|
|
310
329
|
* ({@link RETENTION_TRANSITIONS}); a write naming the current class is a no-op. `rejected` reports
|
|
311
330
|
* a transition that was not allowed.
|
|
@@ -341,6 +360,10 @@ export function applyAssetWrite(existing: AssetRecord | undefined, write: AssetW
|
|
|
341
360
|
const verified = normalizeVerified(write.verified);
|
|
342
361
|
if (verified) record.verified = verified;
|
|
343
362
|
}
|
|
363
|
+
if (write.headers) {
|
|
364
|
+
const headers = normalizeHeaders(write.headers);
|
|
365
|
+
if (headers) record.headers = headers;
|
|
366
|
+
}
|
|
344
367
|
let rejected: string | undefined;
|
|
345
368
|
if (write.retention) {
|
|
346
369
|
const from = record.retention?.class ?? "release";
|
|
@@ -59,6 +59,8 @@ export interface ReleaseImageFacts {
|
|
|
59
59
|
heldPoolBytes(entry: string): Uint8Array | null;
|
|
60
60
|
/** True when the release records `entry` as a sealed capture (`capturedOriginals`). */
|
|
61
61
|
captured(entry: string): boolean;
|
|
62
|
+
/** True when the release declares `entry` a rendition its build left in the pool, proven by recorded evidence. */
|
|
63
|
+
pooledRendition?(entry: string): boolean;
|
|
62
64
|
/**
|
|
63
65
|
* A bundle path (decoded, no leading slash): absent from the release, present but not pinned, or
|
|
64
66
|
* pinned to the pool entry its bytes hash to.
|
|
@@ -181,7 +183,7 @@ export function judgeReleaseImages(input: {
|
|
|
181
183
|
const pool = POOL_REF.exec(ref);
|
|
182
184
|
if (!pool) continue;
|
|
183
185
|
const entry = `${pool[1]}${pool[2] ?? ""}`;
|
|
184
|
-
if (!input.facts.heldPoolBytes(entry) && !input.facts.captured(entry)) add("unregistered", ref);
|
|
186
|
+
if (!input.facts.heldPoolBytes(entry) && !input.facts.captured(entry) && !input.facts.pooledRendition?.(entry)) add("unregistered", ref);
|
|
185
187
|
else if (!supportedPoolEntry(entry, input.facts, input.formats)) add("unsupported-format", ref);
|
|
186
188
|
else poolEntries.add(entry);
|
|
187
189
|
} else if (dependency.disposition === "tenant-hosted") {
|