@tokenoftrust/storefront-runner 2.0.35 → 2.0.36
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": "2.0.
|
|
3
|
+
"version": "2.0.36",
|
|
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",
|
|
@@ -31,11 +31,12 @@
|
|
|
31
31
|
* reference files that don't exist. The Worker-plane build never sets this, so
|
|
32
32
|
* R2 shadowing semantics are unchanged there.
|
|
33
33
|
*/
|
|
34
|
-
import {
|
|
35
|
-
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
34
|
+
import { rm, rename } from "node:fs/promises";
|
|
35
|
+
import { existsSync, readdirSync, statSync, mkdirSync, copyFileSync } from "node:fs";
|
|
36
36
|
import { join, resolve, dirname } from "node:path";
|
|
37
37
|
import { fileURLToPath } from "node:url";
|
|
38
38
|
import { listTenantDirIds, tenantDirSegments } from "../lib/tenant-dirs.mjs";
|
|
39
|
+
import { copyTreeSkippingUnreadable, formatSkippedAssets } from "../lib/tenant-asset-copy.mjs";
|
|
39
40
|
|
|
40
41
|
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
41
42
|
const src = resolve(repoRoot, "tenants");
|
|
@@ -45,7 +46,14 @@ const dest = process.env.TENANT_ASSETS_DEST
|
|
|
45
46
|
|
|
46
47
|
if (!existsSync(src)) process.exit(0);
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
// Build into a staging dir and swap it in at the end, rather than wiping `dest` up
|
|
50
|
+
// front. The old order meant any throw mid-copy left the destination EMPTY: every
|
|
51
|
+
// tenant asset 404'd, pages/*.css included, while HTML kept returning 200 because
|
|
52
|
+
// pages render from content/ — an unstyled site with no signal
|
|
53
|
+
// (fb-1789008798249-sc4tgy). Staging means the previous good mirror survives a
|
|
54
|
+
// failure instead of being replaced by nothing.
|
|
55
|
+
const staging = `${dest}.staging-${process.pid}`;
|
|
56
|
+
await rm(staging, { recursive: true, force: true });
|
|
49
57
|
|
|
50
58
|
const versionedEnv = new Set(
|
|
51
59
|
(process.env.TENANT_ASSETS_VERSIONED ?? "")
|
|
@@ -65,8 +73,17 @@ const isVersioned = (id) =>
|
|
|
65
73
|
// is `/tenants/<id>/…` for every tenant, whichever tier its source sits in.
|
|
66
74
|
const ids = listTenantDirIds(src, readdirSync, (p) => statSync(p).isDirectory(), join);
|
|
67
75
|
|
|
76
|
+
const copyDeps = {
|
|
77
|
+
readdir: (dir) => readdirSync(dir, { withFileTypes: true }),
|
|
78
|
+
mkdir: (dir) => mkdirSync(dir, { recursive: true }),
|
|
79
|
+
copyFile: (from, to) => copyFileSync(from, to),
|
|
80
|
+
join,
|
|
81
|
+
};
|
|
82
|
+
|
|
68
83
|
let copied = 0;
|
|
69
84
|
let skipped = 0;
|
|
85
|
+
/** @type {{path: string, reason: string}[]} */
|
|
86
|
+
const unreadable = [];
|
|
70
87
|
for (const id of ids) {
|
|
71
88
|
const pub = join(tenantDir(id), "public");
|
|
72
89
|
if (!existsSync(pub)) continue;
|
|
@@ -75,12 +92,23 @@ for (const id of ids) {
|
|
|
75
92
|
skipped++;
|
|
76
93
|
continue;
|
|
77
94
|
}
|
|
78
|
-
|
|
79
|
-
|
|
95
|
+
// Per tenant AND per file: one tenant's unreadable entry must not cost another
|
|
96
|
+
// tenant its assets, and one missing image must not cost its own tenant the
|
|
97
|
+
// stylesheet beside it.
|
|
98
|
+
const result = copyTreeSkippingUnreadable(pub, join(staging, id), copyDeps);
|
|
99
|
+
for (const s of result.skipped) unreadable.push({ path: `${id}/${s.path}`, reason: s.reason });
|
|
80
100
|
copied++;
|
|
81
101
|
}
|
|
82
102
|
|
|
103
|
+
// Swap staging in only once the copy is done.
|
|
104
|
+
await rm(dest, { recursive: true, force: true });
|
|
105
|
+
await rename(staging, dest);
|
|
106
|
+
|
|
83
107
|
const skipNote = skipped ? ` (skipped ${skipped} versioned → R2)` : "";
|
|
84
108
|
console.log(
|
|
85
109
|
`copy-tenant-assets: mirrored ${copied} tenant public/ tree(s) → ${dest}${skipNote}`,
|
|
86
110
|
);
|
|
111
|
+
// Loud, and AFTER the summary so it is the last thing on screen. Exit stays 0: a
|
|
112
|
+
// missing blog image is not a reason to stop the dev loop, but it is a reason the
|
|
113
|
+
// operator must not have to infer from an unstyled page.
|
|
114
|
+
for (const line of formatSkippedAssets(unreadable)) console.warn(line);
|