@tokenoftrust/storefront-runner 2.0.2 → 2.0.3

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.
Files changed (117) hide show
  1. package/package.json +1 -1
  2. package/scripts/lib/tenant-dirs.mjs +80 -0
  3. package/scripts/tenant/lint-embeds.mjs +180 -0
  4. package/scripts/tenant/materialize-tenant-content.mjs +533 -0
  5. package/scripts/tenant/validate.mjs +837 -0
  6. package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +0 -68
  7. package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +0 -111
  8. package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +0 -2
  9. package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +0 -247
  10. package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +0 -73
  11. package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +0 -79
  12. package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +0 -10982
  13. package/apps/storefront/scripts/import-v84-through-ws2.ts +0 -38
  14. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +0 -2
  15. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +0 -4
  16. package/apps/storefront/scripts/install-v84-native-through-ws1.ts +0 -185
  17. package/apps/storefront/scripts/prove-clone-isolation.ts +0 -506
  18. package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +0 -148
  19. package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +0 -35
  20. package/apps/storefront/scripts/replay-v84-clean-clone.ts +0 -214
  21. package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +0 -18
  22. package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +0 -18
  23. package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +0 -18
  24. package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +0 -17
  25. package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +0 -15
  26. package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +0 -21
  27. package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +0 -42
  28. package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +0 -145
  29. package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +0 -18
  30. package/apps/storefront/src/components/admin/AdminAIWorkflowsTab.astro +0 -531
  31. package/apps/storefront/src/components/admin/AdminBlogDetailRail.astro +0 -878
  32. package/apps/storefront/src/components/admin/AdminBlogTab.astro +0 -1435
  33. package/apps/storefront/src/components/admin/AdminCheckoutSettingsPanel.astro +0 -226
  34. package/apps/storefront/src/components/admin/AdminCustomersTab.astro +0 -593
  35. package/apps/storefront/src/components/admin/AdminFulfillmentTab.astro +0 -465
  36. package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +0 -105
  37. package/apps/storefront/src/components/admin/AdminProductsTab.astro +0 -901
  38. package/apps/storefront/src/components/admin/AdminPublishTab.astro +0 -2983
  39. package/apps/storefront/src/components/admin/AdminReportingTab.astro +0 -435
  40. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +0 -782
  41. package/apps/storefront/src/components/admin/AdminSubscriptionsTab.astro +0 -611
  42. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +0 -1223
  43. package/apps/storefront/src/lib/dev/cockpitStore.ts +0 -57
  44. package/apps/storefront/src/lib/publish/adminPublishTab.ts +0 -4625
  45. package/apps/storefront/src/lib/publish/apex-readiness.ts +0 -360
  46. package/apps/storefront/src/lib/publish/buildStream.ts +0 -269
  47. package/apps/storefront/src/lib/publish/collapsePersistence.ts +0 -72
  48. package/apps/storefront/src/lib/publish/dispatchHealth.ts +0 -267
  49. package/apps/storefront/src/lib/publish/domainState.ts +0 -360
  50. package/apps/storefront/src/lib/publish/lifecycleState.ts +0 -369
  51. package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +0 -29
  52. package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +0 -78
  53. package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +0 -62
  54. package/apps/storefront/src/lib/publish/mergeRowFindings.ts +0 -124
  55. package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +0 -75
  56. package/apps/storefront/src/lib/publish/panelPolling.ts +0 -78
  57. package/apps/storefront/src/lib/publish/publish-guard.ts +0 -88
  58. package/apps/storefront/src/lib/publish/publishFormat.ts +0 -165
  59. package/apps/storefront/src/lib/publish/publishHero.ts +0 -177
  60. package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +0 -66
  61. package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +0 -141
  62. package/apps/storefront/src/lib/publish/reviewRelease/index.ts +0 -41
  63. package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +0 -47
  64. package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +0 -42
  65. package/apps/storefront/src/lib/publish/reviewRelease/types.ts +0 -59
  66. package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +0 -440
  67. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +0 -161
  68. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +0 -325
  69. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +0 -241
  70. package/apps/storefront/src/lib/publish/shipWorkspace.ts +0 -439
  71. package/apps/storefront/src/lib/publish/sseRoute.ts +0 -119
  72. package/apps/storefront/src/lib/publish/stageBundleUpload.ts +0 -215
  73. package/apps/storefront/src/lib/publish/streamLogConsole.ts +0 -142
  74. package/apps/storefront/src/lib/publish/versionNumber.ts +0 -52
  75. package/apps/storefront/src/pages/admin/ops-timeline.astro +0 -420
  76. package/apps/storefront/src/pages/admin/review-release.astro +0 -265
  77. package/apps/storefront/src/pages/admin/select.astro +0 -96
  78. package/apps/storefront/src/pages/admin.astro +0 -570
  79. package/apps/storefront/src/pages/api/apps/admin/credentials/rotate.ts +0 -86
  80. package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +0 -174
  81. package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +0 -96
  82. package/apps/storefront/src/pages/api/apps/admin/health.ts +0 -44
  83. package/apps/storefront/src/pages/api/apps/admin/install.ts +0 -125
  84. package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +0 -118
  85. package/apps/storefront/src/pages/api/apps/admin/integrations.ts +0 -89
  86. package/apps/storefront/src/pages/api/apps/admin/list.ts +0 -26
  87. package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +0 -85
  88. package/apps/storefront/src/pages/api/apps/admin/resume.ts +0 -79
  89. package/apps/storefront/src/pages/api/apps/admin/suspend.ts +0 -80
  90. package/apps/storefront/src/pages/api/apps/admin/uninstall.ts +0 -98
  91. package/apps/storefront/src/pages/api/apps/admin/update.ts +0 -126
  92. package/apps/storefront/src/pages/api/apps/admin/webhooks/deliveries/[deliveryId]/replay.ts +0 -70
  93. package/apps/storefront/src/pages/api/apps/admin/webhooks/deliveries.ts +0 -51
  94. package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +0 -255
  95. package/apps/storefront/src/pages/api/apps/oauth/token.ts +0 -87
  96. package/apps/storefront/src/pages/api/apps/v1/attribution.ts +0 -162
  97. package/apps/storefront/src/pages/api/apps/v1/catalog/products/[handle].ts +0 -39
  98. package/apps/storefront/src/pages/api/apps/v1/catalog/products.ts +0 -49
  99. package/apps/storefront/src/pages/api/apps/v1/health.ts +0 -32
  100. package/apps/storefront/src/pages/api/apps/v1/inventory.ts +0 -59
  101. package/apps/storefront/src/pages/api/apps/v1/orders/[id].ts +0 -48
  102. package/apps/storefront/src/pages/api/apps/v1/orders.ts +0 -73
  103. package/apps/storefront/src/pages/api/apps/v1/reports.ts +0 -21
  104. package/apps/storefront/src/pages/api/apps/v1/webhooks/deliveries/[deliveryId]/replay.ts +0 -74
  105. package/apps/storefront/src/pages/api/apps/v1/webhooks/deliveries.ts +0 -47
  106. package/apps/storefront/src/pages/api/cache-purge.ts +0 -55
  107. package/apps/storefront/src/pages/api/compliance/tamper.ts +0 -88
  108. package/apps/storefront/src/pages/api/dashboard/enter-vendor.ts +0 -154
  109. package/apps/storefront/src/pages/api/dashboard/vendor-search.ts +0 -120
  110. package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +0 -108
  111. package/apps/storefront/src/pages/api/publish/stage-bundle.ts +0 -70
  112. package/apps/storefront/src/pages/api/test/dev-session.ts +0 -133
  113. package/apps/storefront/src/pages/cockpit.astro +0 -1548
  114. package/apps/storefront/src/pages/dashboard/[appDomain]/apps/index.astro +0 -119
  115. package/apps/storefront/src/pages/dashboard/[appDomain]/index.astro +0 -105
  116. package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +0 -177
  117. package/apps/storefront/src/pages/dashboard/index.astro +0 -160
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/storefront-runner",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
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,80 @@
1
+ /**
2
+ * Where a tenant's colocated directory lives — the NODE-side copy.
3
+ *
4
+ * A tenant IS a directory. Real tenants sit directly under `tenants/<id>/`;
5
+ * PLATFORM TEST tenants (every id ending `.e2e.test`) sit one level deeper,
6
+ * under `tenants/e2e/<id>/`.
7
+ *
8
+ * `@tot/public-runtime` owns this contract for the Worker/TS side
9
+ * (`packages/public-runtime/src/tenant.ts`), but it is TypeScript source with no
10
+ * build output, so the `.mjs` build/dev/tenant scripts cannot import it. This is
11
+ * that same total mapping, byte-for-byte in behavior, and
12
+ * `packages/public-runtime/tests/tenant-dirs-parity.test.ts` fails the build if
13
+ * the two ever disagree on any id.
14
+ */
15
+
16
+ /** The directory segment platform test tenants are nested under. */
17
+ export const E2E_TENANT_DIR = "e2e";
18
+
19
+ /** True for a PLATFORM TEST tenant id — the `<scenario>.e2e.test` class. */
20
+ export function isE2ETenantId(tenantId) {
21
+ return /\.e2e\.test$/.test(tenantId);
22
+ }
23
+
24
+ /** Repo-relative, POSIX-separated directory for a tenant id. */
25
+ export function tenantDirRelative(tenantId) {
26
+ return isE2ETenantId(tenantId)
27
+ ? `tenants/${E2E_TENANT_DIR}/${tenantId}`
28
+ : `tenants/${tenantId}`;
29
+ }
30
+
31
+ /** The `/tenants/…/<id>/` substring identifying one tenant inside a path. */
32
+ export function tenantDirSegment(tenantId) {
33
+ return `/${tenantDirRelative(tenantId)}/`;
34
+ }
35
+
36
+ /** The tenant id a `…/tenants/[e2e/]<id>/…` path encodes, or null. */
37
+ export function tenantIdFromPath(path) {
38
+ return /\/tenants\/(?:e2e\/)?([^/]+)\//.exec(path)?.[1] ?? null;
39
+ }
40
+
41
+ /**
42
+ * Narrow a set of tenant ids to what a generalized listing should show —
43
+ * platform test tenants are dropped unless explicitly requested.
44
+ */
45
+ export function listedTenantIds(ids, opts = {}) {
46
+ return opts.includeTestTenants ? [...ids] : ids.filter((id) => !isE2ETenantId(id));
47
+ }
48
+
49
+ /**
50
+ * Path segments (relative to the repo root) for a tenant's own directory —
51
+ * ready for `join(repoRoot, ...tenantDirSegments(id))`.
52
+ */
53
+ export function tenantDirSegments(tenantId) {
54
+ return tenantDirRelative(tenantId).split("/");
55
+ }
56
+
57
+ /**
58
+ * Every tenant id with a directory under `tenants/`, real and test, from one
59
+ * `readdir`-shaped view of the tree. `readdirSync` is passed in so the walk
60
+ * stays testable without a filesystem.
61
+ *
62
+ * @param tenantsDir absolute path to the repo's `tenants/` directory
63
+ * @param readdir `(dir) => string[]` listing a directory's entry names
64
+ * @param isDir `(path) => boolean`
65
+ * @param join path join
66
+ */
67
+ export function listTenantDirIds(tenantsDir, readdir, isDir, join) {
68
+ const ids = [];
69
+ for (const entry of readdir(tenantsDir)) {
70
+ if (!isDir(join(tenantsDir, entry))) continue;
71
+ if (entry === E2E_TENANT_DIR) {
72
+ for (const nested of readdir(join(tenantsDir, entry))) {
73
+ if (isDir(join(tenantsDir, entry, nested))) ids.push(nested);
74
+ }
75
+ continue;
76
+ }
77
+ ids.push(entry);
78
+ }
79
+ return ids;
80
+ }
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Dev-loop lint for third-party embeds (shift-left for the CSP allowlist).
3
+ *
4
+ * A strict CSP silently blocks any external <script>/<iframe> a tenant page
5
+ * embeds unless the origin is allowlisted (see packages/public-runtime/src/csp.ts
6
+ * — EMBED_PROVIDERS + embedCspAdditions). A blocked embed produces no build
7
+ * error and no page error — just a blank spot — so historically the only way it
8
+ * surfaced was a human noticing and escalating to the platform team.
9
+ *
10
+ * This lint closes that gap at author/preview time: it scans a tenant's HTML for
11
+ * external embed origins and tells the developer, right where they're working:
12
+ * - KNOWN provider present → make sure the tenant opts the slug into `embeds`
13
+ * (if we know the opt-in, hard-fail when it's missing).
14
+ * - UNKNOWN external origin → it will be blocked; a platform catalog entry is
15
+ * needed (EMBED_PROVIDERS), not a raw-origin config edit.
16
+ *
17
+ * Source of truth for the catalog is EMBED_PROVIDERS in TS; KNOWN_EMBEDS below
18
+ * mirrors it for dependency-free `node` execution and a vitest parity test
19
+ * (lint-embeds.test.mjs) fails if the two drift.
20
+ *
21
+ * Usage:
22
+ * node scripts/tenant/lint-embeds.mjs --dir tenants/<id>/content [--declared pipedrive,calendly]
23
+ * node scripts/tenant/lint-embeds.mjs --dir <tenant-repo>/content
24
+ * Exit non-zero when a blocking finding exists (undeclared known provider given
25
+ * a known opt-in, or an unknown external origin) so `tot preview` / CI can gate.
26
+ */
27
+ import { readdirSync, readFileSync, statSync } from "node:fs";
28
+ import { join, extname } from "node:path";
29
+ import { EMBED_PROVIDERS } from "../../packages/public-runtime/src/embed-catalog.mjs";
30
+
31
+ /**
32
+ * The embed catalog, flattened for the lint from the shared source of truth
33
+ * (packages/public-runtime/src/embed-catalog.mjs — imported directly, no mirror).
34
+ * `markers` = substrings meaning the embed is on a page; `origins` = the union
35
+ * of the provider's exact CSP hosts.
36
+ */
37
+ export const KNOWN_EMBEDS = Object.entries(EMBED_PROVIDERS).map(([slug, p]) => ({
38
+ slug,
39
+ label: p.label,
40
+ markers: p.markers,
41
+ origins: [...new Set([...(p.scriptSrc ?? []), ...(p.connectSrc ?? []), ...(p.frameSrc ?? [])])],
42
+ }));
43
+
44
+ /** Hosts the platform always allows (self is implicit); embeds to these are fine. */
45
+ const ALWAYS_ALLOWED_HOSTS = new Set([
46
+ "static.cloudflareinsights.com",
47
+ "cloudflareinsights.com",
48
+ ]);
49
+
50
+ // Only <script src> and <iframe src> load executable/frameable content the CSP
51
+ // gates (script-src / frame-src). We deliberately DON'T scan generic URLs
52
+ // (canonical links, JSON-LD @context, <a href>, og:url) — those aren't embeds
53
+ // and would be noisy false positives. Provider-specific embeds that mount via a
54
+ // data-attribute (e.g. Pipedrive's data-pd-webforms) are caught by their catalog
55
+ // marker instead, so they're covered without a generic URL sweep.
56
+ const SRC_RE = /<(?:script|iframe)\b[^>]*\bsrc\s*=\s*["']([^"']+)["']/gi;
57
+
58
+ /** Origin (host) of an absolute URL, or null for relative/self URLs. */
59
+ function originOf(u) {
60
+ try {
61
+ return new URL(u).origin;
62
+ } catch {
63
+ return null;
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Classify one HTML document's embeds.
69
+ * @returns {{ knownSlugs: string[], externalOrigins: string[], unknownOrigins: string[] }}
70
+ */
71
+ export function scanHtmlForEmbeds(html) {
72
+ const knownSlugs = new Set();
73
+ for (const { slug, markers } of KNOWN_EMBEDS) {
74
+ if (markers.some((m) => html.includes(m))) knownSlugs.add(slug);
75
+ }
76
+ const external = new Set();
77
+ SRC_RE.lastIndex = 0;
78
+ let m;
79
+ while ((m = SRC_RE.exec(html))) {
80
+ const origin = originOf(m[1]);
81
+ if (!origin) continue;
82
+ const host = new URL(origin).hostname;
83
+ if (ALWAYS_ALLOWED_HOSTS.has(host)) continue;
84
+ external.add(origin);
85
+ }
86
+ // An external origin is "unknown" if no catalog provider claims it.
87
+ const catalogOrigins = new Set(KNOWN_EMBEDS.flatMap((p) => p.origins));
88
+ const unknown = [...external].filter((o) => !catalogOrigins.has(o));
89
+ return {
90
+ knownSlugs: [...knownSlugs],
91
+ externalOrigins: [...external],
92
+ unknownOrigins: unknown,
93
+ };
94
+ }
95
+
96
+ function walkHtml(dir) {
97
+ /** @type {string[]} */
98
+ const out = [];
99
+ for (const entry of readdirSync(dir)) {
100
+ const p = join(dir, entry);
101
+ const st = statSync(p);
102
+ if (st.isDirectory()) out.push(...walkHtml(p));
103
+ else if (extname(p) === ".html") out.push(p);
104
+ }
105
+ return out;
106
+ }
107
+
108
+ /**
109
+ * Lint every HTML file under `dir`.
110
+ * @param {{ dir: string, declared?: string[] | null }} opts
111
+ * `declared` = the tenant's opted-in slugs if known (platform CI); when null,
112
+ * findings for known providers are advisory (we can't prove the opt-in here).
113
+ * @returns {{ findings: Array<{level:'error'|'warn'|'info', file:string, message:string}>, blocking: boolean }}
114
+ */
115
+ export function lintTenantEmbeds({ dir, declared = null }) {
116
+ /** @type {Array<{level:'error'|'warn'|'info', file:string, message:string}>} */
117
+ const findings = [];
118
+ const declaredSet = declared ? new Set(declared) : null;
119
+ for (const file of walkHtml(dir)) {
120
+ const html = readFileSync(file, "utf8");
121
+ const { knownSlugs, unknownOrigins } = scanHtmlForEmbeds(html);
122
+ for (const slug of knownSlugs) {
123
+ const label = KNOWN_EMBEDS.find((p) => p.slug === slug)?.label ?? slug;
124
+ if (declaredSet && !declaredSet.has(slug)) {
125
+ findings.push({
126
+ level: "error",
127
+ file,
128
+ message: `embeds ${label} but the tenant has not opted "${slug}" into its embeds allowlist — the CSP will block it. Add "${slug}" to the tenant's embeds config.`,
129
+ });
130
+ } else if (!declaredSet) {
131
+ findings.push({
132
+ level: "info",
133
+ file,
134
+ message: `embeds ${label}: ensure "${slug}" is in the tenant's embeds allowlist, or the CSP will block it.`,
135
+ });
136
+ }
137
+ }
138
+ for (const origin of unknownOrigins) {
139
+ findings.push({
140
+ level: "error",
141
+ file,
142
+ message: `embeds ${origin}, which is not a known embed provider — the CSP will block it. A platform catalog entry (EMBED_PROVIDERS) is required, or remove the embed.`,
143
+ });
144
+ }
145
+ }
146
+ return { findings, blocking: findings.some((f) => f.level === "error") };
147
+ }
148
+
149
+ function parseArgs(argv) {
150
+ const args = { dir: null, declared: null };
151
+ for (let i = 0; i < argv.length; i++) {
152
+ if (argv[i] === "--dir") args.dir = argv[++i];
153
+ else if (argv[i] === "--declared") args.declared = argv[++i].split(",").map((s) => s.trim()).filter(Boolean);
154
+ }
155
+ return args;
156
+ }
157
+
158
+ function main() {
159
+ const { dir, declared } = parseArgs(process.argv.slice(2));
160
+ if (!dir) {
161
+ console.error("usage: node scripts/tenant/lint-embeds.mjs --dir <content-dir> [--declared slug,slug]");
162
+ process.exit(2);
163
+ }
164
+ const { findings, blocking } = lintTenantEmbeds({ dir, declared });
165
+ if (findings.length === 0) {
166
+ console.log("embed lint: no external embeds found (or all allowlisted).");
167
+ return;
168
+ }
169
+ for (const f of findings) {
170
+ const tag = f.level === "error" ? "BLOCKED" : f.level === "warn" ? "WARN" : "note";
171
+ console.log(`[embed-lint:${tag}] ${f.file}\n ${f.message}`);
172
+ }
173
+ if (blocking) {
174
+ console.error(`\nembed lint: ${findings.filter((f) => f.level === "error").length} embed(s) will be blocked by CSP — see above.`);
175
+ process.exit(1);
176
+ }
177
+ }
178
+
179
+ // Only run main when invoked directly, not when imported by the parity test.
180
+ if (import.meta.url === `file://${process.argv[1]}`) main();