@tokenoftrust/storefront-runner 2.0.45 → 2.0.47
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.
|
@@ -15,7 +15,6 @@ import { readFile } from "node:fs/promises";
|
|
|
15
15
|
// normalized to match Vite's ctx.file (Vite always reports posix-style paths).
|
|
16
16
|
const REPO_ROOT = fileURLToPath(new URL("../../..", import.meta.url));
|
|
17
17
|
const TENANTS_DIR = join(REPO_ROOT, "tenants").replace(/\\/g, "/");
|
|
18
|
-
const PUBLIC_TENANTS_DEST = fileURLToPath(new URL("../public/tenants", import.meta.url));
|
|
19
18
|
|
|
20
19
|
/** Per-file finding signatures, so an unchanged warning is not reprinted on every
|
|
21
20
|
* keystroke-save. Lives for the dev server's lifetime. */
|
|
@@ -58,12 +57,13 @@ async function loadTsModule(server, url) {
|
|
|
58
57
|
* @param {string} file
|
|
59
58
|
* @returns {{ id: string, rest: string } | null}
|
|
60
59
|
*/
|
|
61
|
-
export function classifyTenantFile(file) {
|
|
60
|
+
export function classifyTenantFile(file, tenantsDir = TENANTS_DIR) {
|
|
62
61
|
const f = String(file).replace(/\\/g, "/");
|
|
63
|
-
|
|
62
|
+
const normalizedTenantsDir = String(tenantsDir).replace(/\\/g, "/");
|
|
63
|
+
if (!f.startsWith(normalizedTenantsDir + "/")) return null;
|
|
64
64
|
// "<id>/<...>" for a real tenant, "e2e/<id>/<...>" for a platform test tenant —
|
|
65
65
|
// consume the nesting segment so `id` is always the tenant's own id.
|
|
66
|
-
let rel = f.slice(
|
|
66
|
+
let rel = f.slice(normalizedTenantsDir.length + 1);
|
|
67
67
|
if (rel.startsWith(E2E_TENANT_DIR + "/")) rel = rel.slice(E2E_TENANT_DIR.length + 1);
|
|
68
68
|
const slash = rel.indexOf("/");
|
|
69
69
|
if (slash < 0) return null;
|
|
@@ -102,23 +102,31 @@ export function stripThemeMeta(parsed) {
|
|
|
102
102
|
* save mirroring into public/tenants/<id>/.
|
|
103
103
|
* @param {string} id
|
|
104
104
|
*/
|
|
105
|
-
export function isVersionedTenant(id) {
|
|
105
|
+
export function isVersionedTenant(id, root = REPO_ROOT) {
|
|
106
106
|
const envList = (process.env.TENANT_ASSETS_VERSIONED ?? "")
|
|
107
107
|
.split(",")
|
|
108
108
|
.map((s) => s.trim())
|
|
109
109
|
.filter(Boolean);
|
|
110
110
|
if (envList.includes(id)) return true;
|
|
111
|
-
return existsSync(join(
|
|
111
|
+
return existsSync(join(root, ...tenantDirSegments(id), ".tot", "assets-versioned"));
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
115
|
* Re-mirror one changed/added public asset into public/tenants/<id>/ (skip R2-versioned).
|
|
116
116
|
* @param {string} id @param {string} rest @param {string} sourceFile
|
|
117
117
|
*/
|
|
118
|
-
async function mirrorPublicAsset(
|
|
119
|
-
|
|
118
|
+
async function mirrorPublicAsset(
|
|
119
|
+
id,
|
|
120
|
+
rest,
|
|
121
|
+
sourceFile,
|
|
122
|
+
{
|
|
123
|
+
root = REPO_ROOT,
|
|
124
|
+
publicTenantsDest = join(root, "apps", "storefront", "public", "tenants"),
|
|
125
|
+
} = {},
|
|
126
|
+
) {
|
|
127
|
+
if (isVersionedTenant(id, root)) return;
|
|
120
128
|
const { cp, mkdir } = await import("node:fs/promises");
|
|
121
|
-
const target = join(
|
|
129
|
+
const target = join(publicTenantsDest, id, rest.slice("public/".length));
|
|
122
130
|
try {
|
|
123
131
|
await mkdir(dirname(target), { recursive: true });
|
|
124
132
|
await cp(sourceFile, target);
|
|
@@ -131,10 +139,17 @@ async function mirrorPublicAsset(id, rest, sourceFile) {
|
|
|
131
139
|
* Remove the stale mirrored copy under public/tenants/<id>/ when a public asset is deleted.
|
|
132
140
|
* @param {string} id @param {string} rest
|
|
133
141
|
*/
|
|
134
|
-
async function unmirrorPublicAsset(
|
|
135
|
-
|
|
142
|
+
async function unmirrorPublicAsset(
|
|
143
|
+
id,
|
|
144
|
+
rest,
|
|
145
|
+
{
|
|
146
|
+
root = REPO_ROOT,
|
|
147
|
+
publicTenantsDest = join(root, "apps", "storefront", "public", "tenants"),
|
|
148
|
+
} = {},
|
|
149
|
+
) {
|
|
150
|
+
if (isVersionedTenant(id, root)) return;
|
|
136
151
|
const { rm } = await import("node:fs/promises");
|
|
137
|
-
const target = join(
|
|
152
|
+
const target = join(publicTenantsDest, id, rest.slice("public/".length));
|
|
138
153
|
try {
|
|
139
154
|
await rm(target, { force: true });
|
|
140
155
|
} catch {
|
|
@@ -194,7 +209,11 @@ export function seedKnown(root, known) {
|
|
|
194
209
|
// the worker teardown, same as the change path). Public assets are NOT in any
|
|
195
210
|
// graph, so Vite ignores their add/unlink entirely — we mirror (add) / un-mirror
|
|
196
211
|
// (unlink) and reload ourselves. loopState.lastEvent then reflects add/unlink.
|
|
197
|
-
export function tenantHotReload(
|
|
212
|
+
export function tenantHotReload({
|
|
213
|
+
root = REPO_ROOT,
|
|
214
|
+
publicTenantsDest = join(root, "apps", "storefront", "public", "tenants"),
|
|
215
|
+
} = {}) {
|
|
216
|
+
const tenantsDir = join(root, "tenants").replace(/\\/g, "/");
|
|
198
217
|
// Typed `any`: `vite` isn't a direct dependency of this package (it's pulled in
|
|
199
218
|
// transitively via astro), so `import('vite').Plugin` can't be resolved here and
|
|
200
219
|
// importing from "vite" would even fail at runtime. `any` keeps the hook params
|
|
@@ -205,7 +224,7 @@ export function tenantHotReload() {
|
|
|
205
224
|
apply: "serve",
|
|
206
225
|
/** @param {any} server */
|
|
207
226
|
configureServer(server) {
|
|
208
|
-
server.watcher.add(
|
|
227
|
+
server.watcher.add(tenantsDir);
|
|
209
228
|
|
|
210
229
|
// Tell the hosted /dev panel the store is UP the moment the dev server is
|
|
211
230
|
// listening — completes Step 1 ("Get running") before any edit. Best-effort,
|
|
@@ -218,12 +237,12 @@ export function tenantHotReload() {
|
|
|
218
237
|
// really added/removed so the guard stays accurate for the session.
|
|
219
238
|
/** @type {Set<string>} */
|
|
220
239
|
const known = new Set();
|
|
221
|
-
seedKnown(
|
|
240
|
+
seedKnown(tenantsDir, known);
|
|
222
241
|
|
|
223
242
|
/** @param {"add"|"unlink"} event @param {string} rawPath */
|
|
224
243
|
const onAddUnlink = async (event, rawPath) => {
|
|
225
244
|
const file = String(rawPath).replace(/\\/g, "/");
|
|
226
|
-
const c = classifyTenantFile(file);
|
|
245
|
+
const c = classifyTenantFile(file, tenantsDir);
|
|
227
246
|
if (!c) return;
|
|
228
247
|
const { id, rest } = c;
|
|
229
248
|
|
|
@@ -244,8 +263,11 @@ export function tenantHotReload() {
|
|
|
244
263
|
// Public assets: Vite doesn't react (not in the module graph). Maintain the
|
|
245
264
|
// mirror ourselves, then reload (server.ws.send is timestamped by the Monitor).
|
|
246
265
|
if (rest.startsWith("public/")) {
|
|
247
|
-
if (event === "add")
|
|
248
|
-
|
|
266
|
+
if (event === "add") {
|
|
267
|
+
await mirrorPublicAsset(id, rest, file, { root, publicTenantsDest });
|
|
268
|
+
} else {
|
|
269
|
+
await unmirrorPublicAsset(id, rest, { root, publicTenantsDest });
|
|
270
|
+
}
|
|
249
271
|
server.ws.send({ type: "full-reload" });
|
|
250
272
|
return;
|
|
251
273
|
}
|
|
@@ -259,7 +281,7 @@ export function tenantHotReload() {
|
|
|
259
281
|
},
|
|
260
282
|
/** @param {any} ctx */
|
|
261
283
|
async handleHotUpdate(ctx) {
|
|
262
|
-
const c = classifyTenantFile(ctx.file);
|
|
284
|
+
const c = classifyTenantFile(ctx.file, tenantsDir);
|
|
263
285
|
if (!c) return undefined;
|
|
264
286
|
const { id, rest } = c;
|
|
265
287
|
|
|
@@ -276,7 +298,7 @@ export function tenantHotReload() {
|
|
|
276
298
|
// terminal a beat later. Putting this in the reload path would tax every
|
|
277
299
|
// save to report something that is almost always empty.
|
|
278
300
|
void validateSavedFile({
|
|
279
|
-
tenantDir: join(
|
|
301
|
+
tenantDir: join(root, tenantDirRelative(id)),
|
|
280
302
|
tenantId: id,
|
|
281
303
|
rest,
|
|
282
304
|
memo: savedFileFindings,
|
|
@@ -301,7 +323,10 @@ export function tenantHotReload() {
|
|
|
301
323
|
// Unlike content HTML this reloads no worker, so there's no teardown race to
|
|
302
324
|
// fire ahead of. Skip R2-versioned tenants, like copy-tenant-assets.mjs.
|
|
303
325
|
if (rest.startsWith("public/")) {
|
|
304
|
-
await mirrorPublicAsset(id, rest, ctx.file.replace(/\\/g, "/")
|
|
326
|
+
await mirrorPublicAsset(id, rest, ctx.file.replace(/\\/g, "/"), {
|
|
327
|
+
root,
|
|
328
|
+
publicTenantsDest,
|
|
329
|
+
});
|
|
305
330
|
ctx.server.ws.send({ type: "full-reload" });
|
|
306
331
|
return [];
|
|
307
332
|
}
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
* STATIC publish (materialize-tenant.mjs) sets this to the single tenant it renders,
|
|
20
20
|
* so a sibling tenant can never block it. Unset ⇒ framework-only Worker build.
|
|
21
21
|
*/
|
|
22
|
-
export function materializeGuard() {
|
|
22
|
+
export function materializeGuard({ root } = {}) {
|
|
23
23
|
return {
|
|
24
24
|
name: "materialize-guard",
|
|
25
25
|
hooks: {
|
|
@@ -40,7 +40,7 @@ export function materializeGuard() {
|
|
|
40
40
|
// set (not "all tenants", which `null` would mean), so a sibling
|
|
41
41
|
// tenant that is present-but-stale can never block this build.
|
|
42
42
|
const frameworkOnlyTenants = [];
|
|
43
|
-
const { problems } = checkPinned(
|
|
43
|
+
const { problems } = checkPinned(root, { expectRef, allowUnpinned, onlyTenants: frameworkOnlyTenants });
|
|
44
44
|
const absent = problems.filter((p) => p.reason === "absent");
|
|
45
45
|
const real = problems.filter((p) => p.reason !== "absent");
|
|
46
46
|
if (absent.length) {
|
|
@@ -49,11 +49,11 @@ export function materializeGuard() {
|
|
|
49
49
|
else console.log(msg);
|
|
50
50
|
}
|
|
51
51
|
if (real.length) {
|
|
52
|
-
assertPinnedMaterialized({ expectRef, allowUnpinned, onlyTenants: frameworkOnlyTenants });
|
|
52
|
+
assertPinnedMaterialized({ root, expectRef, allowUnpinned, onlyTenants: frameworkOnlyTenants });
|
|
53
53
|
}
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
-
assertPinnedMaterialized({ expectRef, allowUnpinned, onlyTenants });
|
|
56
|
+
assertPinnedMaterialized({ root, expectRef, allowUnpinned, onlyTenants });
|
|
57
57
|
},
|
|
58
58
|
},
|
|
59
59
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.47",
|
|
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",
|