@tokenoftrust/storefront-runner 2.2.20 → 2.2.21

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.
@@ -21,8 +21,7 @@
21
21
  * passes — the client pass included — runs with `build.ssr === true`, so a Vite plugin
22
22
  * cannot tell them apart, while `config.build.client` says it outright.
23
23
  */
24
- import { createHash } from "node:crypto";
25
- import { mkdir, readFile, writeFile } from "node:fs/promises";
24
+ import { mkdir, writeFile } from "node:fs/promises";
26
25
  import { createRequire } from "node:module";
27
26
  import { dirname } from "node:path";
28
27
  import { fileURLToPath } from "node:url";
@@ -30,7 +29,6 @@ import {
30
29
  LIFECYCLE_HOST_ASSET_PATH,
31
30
  LIFECYCLE_HOST_COMPATIBILITY_ASSET_PATHS,
32
31
  } from "@tot/public-runtime/integration/lifecycle-host-asset";
33
- import { decideLifecycleHostLock, formatLifecycleHostLockReport } from "../../../scripts/lib/lifecycle-host-lock.mjs";
34
32
 
35
33
  const HOST_ASSET_PATHS = Object.freeze([
36
34
  ...LIFECYCLE_HOST_COMPATIBILITY_ASSET_PATHS,
@@ -44,11 +42,6 @@ const HOST_ENTRY = fileURLToPath(
44
42
  ),
45
43
  );
46
44
 
47
- const LOCK_URL = new URL(
48
- "../../../packages/public-runtime/src/integration/lifecycle-host-asset.lock.json",
49
- import.meta.url,
50
- );
51
-
52
45
  /**
53
46
  * Bundle the host to a single self-contained ES module.
54
47
  *
@@ -131,30 +124,6 @@ export function lifecycleHostAsset() {
131
124
  if (!clientDir) throw new Error("the lifecycle host has no resolved client output dir");
132
125
  const outputDir = clientDir;
133
126
  const code = await bundleLifecycleHost();
134
- const hash = createHash("sha256").update(code, "utf8").digest("hex");
135
-
136
- // Every HOST_ASSET_PATH is served under an `immutable, max-age=1yr` Cache-Control
137
- // (scripts/publish/lib/s3-publish.mjs PLATFORM_ASSET_PREFIX) — once published, a
138
- // path's bytes may never change, or visitors who already cached it are stuck on
139
- // stale tracking/consent behavior for up to a year with nothing to notice it. This
140
- // is the guard for that: refuse the build rather than silently rewrite a path that
141
- // already shipped different bytes.
142
- const lock = JSON.parse(await readFile(LOCK_URL, "utf8"));
143
- const decision = decideLifecycleHostLock(
144
- lock,
145
- HOST_ASSET_PATHS.map((assetPath) => ({ assetPath, hash })),
146
- );
147
- if (!decision.ok) {
148
- throw new Error(formatLifecycleHostLockReport(decision));
149
- }
150
- if (JSON.stringify(decision.nextLock) !== JSON.stringify(lock)) {
151
- await writeFile(LOCK_URL, `${JSON.stringify(decision.nextLock, null, 2)}\n`, "utf8");
152
- logger.warn(
153
- "lifecycle host lock recorded a new path — commit " +
154
- "packages/public-runtime/src/integration/lifecycle-host-asset.lock.json",
155
- );
156
- }
157
-
158
127
  await Promise.all(HOST_ASSET_PATHS.map(async (assetPath) => {
159
128
  const target = fileURLToPath(new URL(assetPath, outputDir));
160
129
  await mkdir(dirname(target), { recursive: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/storefront-runner",
3
- "version": "2.2.20",
3
+ "version": "2.2.21",
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",
@@ -0,0 +1,2 @@
1
+ /** Types for the shared (S3 publish + Cloudflare serve) immutable-prefix list. */
2
+ export declare const CONTENT_NAMED_ASSET_PREFIXES: readonly string[];
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Bundle-relative prefixes whose URL is derived from the file's own bytes, and therefore
3
+ * the ONLY paths that may be served `Cache-Control: immutable`:
4
+ * `_astro/` — Astro/Vite put a content hash in every filename.
5
+ * `_a/` — the content-addressed asset pool (`/_a/<sha256><ext>`).
6
+ *
7
+ * `immutable` tells a browser it will never need to revalidate, so it is only correct when a
8
+ * byte change necessarily changes the URL. A path-VERSIONED prefix such as `platform/` does
9
+ * not qualify: its `v{N}` names a contract, compatible changes rebuild it in place, and a
10
+ * returning visitor would keep the old bytes for a year.
11
+ *
12
+ * One list for both CDNs. The S3/CloudFront publish plane (`scripts/publish/lib/s3-publish.mjs`)
13
+ * and the Cloudflare serve plane (`src/static-bundle.ts`) both read it, so they cannot
14
+ * disagree about what is immutable.
15
+ */
16
+ export const CONTENT_NAMED_ASSET_PREFIXES = Object.freeze(["_astro/", "_a/"]);
@@ -8,18 +8,25 @@
8
8
  * disagree — a mismatch would leave every static tenant page with plan data and no host,
9
9
  * which fails silently in exactly the way it is not allowed to.
10
10
  *
11
- * The path is version-pinned (`/v3/`) like a platform adapter entry, so a future host
12
- * with a different boot contract ships alongside rather than replacing this one.
11
+ * The `/v4/` segment names the boot CONTRACT, not the bytes: a compatible runtime change
12
+ * rebuilds this same path, which is why `platform/` is served with a revalidating cache
13
+ * tier rather than `immutable`. A host with a different boot contract ships at the next
14
+ * `v{N}` alongside this one.
15
+ *
16
+ * The segment must also advance once whenever earlier bytes at a path were served under an
17
+ * `immutable` header, since nothing the server does reaches a browser that cached them that
18
+ * way. v3 was, which is why v4 exists.
13
19
  */
14
- export const LIFECYCLE_HOST_ASSET_PATH = "platform/lifecycle/v3/boot.mjs";
20
+ export const LIFECYCLE_HOST_ASSET_PATH = "platform/lifecycle/v4/boot.mjs";
15
21
 
16
22
  /**
17
- * Earlier immutable URLs that must keep serving the current host bytes during a
18
- * version transition. Cached HTML may still reference one of these paths after a
19
- * pointer flip, so dropping it would turn a cache refresh into a lifecycle 404.
23
+ * Earlier URLs that keep serving the current host bytes. Cached HTML may still reference
24
+ * one of these paths after a pointer flip, so dropping it would turn a cache refresh into a
25
+ * lifecycle 404.
20
26
  */
21
27
  export const LIFECYCLE_HOST_COMPATIBILITY_ASSET_PATHS = Object.freeze([
22
28
  "platform/lifecycle/v2/boot.mjs",
29
+ "platform/lifecycle/v3/boot.mjs",
23
30
  ]);
24
31
 
25
32
  /** The root-absolute, same-origin URL the served markup references. */
@@ -43,6 +43,7 @@
43
43
  import { assetContentType } from "./tenant-assets.js";
44
44
  import { formatHtmlCacheControl } from "./html-cache-policy.js";
45
45
  import { NATIVE_GA4_SANDBOX_ASSET_PATH } from "./integration/native-ga4-sandbox-asset.mjs";
46
+ import { CONTENT_NAMED_ASSET_PREFIXES } from "./content-named-asset-prefixes.mjs";
46
47
 
47
48
  // ---------------------------------------------------------------------------
48
49
  // R2 seam — same structural subset tenant-assets.ts uses (no workers-types dep)
@@ -302,8 +303,8 @@ const IMMUTABLE_CACHE_CONTROL = "public, max-age=31536000, immutable";
302
303
  */
303
304
  const STABLE_ASSET_CACHE_CONTROL = "public, max-age=86400, stale-while-revalidate=604800";
304
305
 
305
- /** Bundle prefixes whose stable paths are still immutable: hash-named build output and path-versioned platform code. */
306
- const IMMUTABLE_STABLE_PREFIXES = ["_astro/", "platform/"];
306
+ /** Bundle prefixes whose stable paths are immutable — shared with the S3 plane, see that module. */
307
+ const IMMUTABLE_STABLE_PREFIXES = CONTENT_NAMED_ASSET_PREFIXES;
307
308
 
308
309
  /**
309
310
  * URL prefix of a content-addressed asset (`/_a/<sha256><ext>`) — kept in lockstep with
@@ -1,4 +0,0 @@
1
- {
2
- "platform/lifecycle/v2/boot.mjs": "ed2c69b80fb2e1472d0b19631efec5ccb26a395a5f4820a54f55435deca6eb46",
3
- "platform/lifecycle/v3/boot.mjs": "ed2c69b80fb2e1472d0b19631efec5ccb26a395a5f4820a54f55435deca6eb46"
4
- }