@tokenoftrust/storefront-runner 2.2.126 → 2.3.1

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 (109) hide show
  1. package/apps/storefront/package.json +3 -1
  2. package/apps/storefront/public/shared/commerce-chrome.css +57 -15
  3. package/apps/storefront/public/shared/commerce-marketing.css +7 -7
  4. package/apps/storefront/src/components/Badge.astro +1 -1
  5. package/apps/storefront/src/components/Breadcrumbs.astro +8 -3
  6. package/apps/storefront/src/components/Section.astro +4 -1
  7. package/apps/storefront/src/components/Seo.astro +7 -5
  8. package/apps/storefront/src/components/blog/BlogIndexView.astro +22 -13
  9. package/apps/storefront/src/components/blog/ShareRow.astro +43 -0
  10. package/apps/storefront/src/components/chrome/AnnouncementBar.astro +1 -1
  11. package/apps/storefront/src/components/chrome/NavDropdown.astro +7 -7
  12. package/apps/storefront/src/components/chrome/SiteFooter.astro +17 -0
  13. package/apps/storefront/src/components/chrome/SiteHeader.astro +5 -5
  14. package/apps/storefront/src/components/commerce/TagFilterCloud.astro +1 -1
  15. package/apps/storefront/src/components/compliance/NicotineWarning.astro +12 -22
  16. package/apps/storefront/src/components/compliance/PactActNotice.astro +9 -10
  17. package/apps/storefront/src/components/content/ProseSections.astro +2 -2
  18. package/apps/storefront/src/components/home/Hero.astro +34 -12
  19. package/apps/storefront/src/components/islands/IsolatedAgeGate.tsx +39 -5
  20. package/apps/storefront/src/components/islands/VariantSelector.tsx +3 -3
  21. package/apps/storefront/src/components/marketing/CardGrid.astro +1 -1
  22. package/apps/storefront/src/components/marketing/CodeSample.astro +1 -1
  23. package/apps/storefront/src/components/marketing/CtaBand.astro +1 -1
  24. package/apps/storefront/src/components/marketing/Faq.astro +1 -1
  25. package/apps/storefront/src/components/marketing/Integrations.astro +1 -1
  26. package/apps/storefront/src/components/marketing/MarketingCtas.astro +1 -1
  27. package/apps/storefront/src/components/marketing/MarketingHero.astro +1 -1
  28. package/apps/storefront/src/components/marketing/ProofStrip.astro +1 -1
  29. package/apps/storefront/src/components/marketing/SplitCompare.astro +1 -1
  30. package/apps/storefront/src/components/marketing/Steps.astro +1 -1
  31. package/apps/storefront/src/components/marketing/Testimonials.astro +2 -2
  32. package/apps/storefront/src/components/marketing/TrustBar.astro +1 -1
  33. package/apps/storefront/src/components/membership/TierComparisonTable.astro +1 -1
  34. package/apps/storefront/src/components/plp/ActiveFilters.astro +1 -1
  35. package/apps/storefront/src/components/plp/FacetSidebar.astro +4 -4
  36. package/apps/storefront/src/components/plp/Pagination.astro +1 -1
  37. package/apps/storefront/src/components/plp/SortSelect.astro +1 -1
  38. package/apps/storefront/src/components/style-guide/StyleGuideNav.astro +18 -3
  39. package/apps/storefront/src/components/the-build/TheBuildIndexView.astro +9 -5
  40. package/apps/storefront/src/config/devTenantSeed.ts +23 -34
  41. package/apps/storefront/src/config/resolver.ts +0 -38
  42. package/apps/storefront/src/layouts/Layout.astro +8 -1
  43. package/apps/storefront/src/lib/assets/preview-version-cookie.ts +52 -5
  44. package/apps/storefront/src/lib/blog/pagination.ts +20 -10
  45. package/apps/storefront/src/lib/blog/presentation.ts +94 -0
  46. package/apps/storefront/src/lib/blog/rss.ts +1 -1
  47. package/apps/storefront/src/lib/blog/types.ts +24 -7
  48. package/apps/storefront/src/lib/breadcrumbs.ts +25 -0
  49. package/apps/storefront/src/lib/checkoutCommerce.ts +26 -3
  50. package/apps/storefront/src/lib/chrome/model.ts +4 -0
  51. package/apps/storefront/src/lib/cloudflare-workers.d.ts +53 -0
  52. package/apps/storefront/src/lib/compliance/rawComplianceNotices.ts +38 -18
  53. package/apps/storefront/src/lib/dashboard/delegateGate.ts +28 -0
  54. package/apps/storefront/src/lib/homeVisualParity.ts +8 -5
  55. package/apps/storefront/src/lib/jsonld.ts +5 -5
  56. package/apps/storefront/src/lib/pinnedRouteRewriteBoundary.ts +20 -2
  57. package/apps/storefront/src/lib/rawChrome.ts +74 -12
  58. package/apps/storefront/src/lib/runtimeEnv.ts +1 -1
  59. package/apps/storefront/src/lib/seo/documentTitle.ts +19 -0
  60. package/apps/storefront/src/lib/shared/addToRelease.ts +32 -0
  61. package/apps/storefront/src/lib/storyblok/content-model.ts +3 -0
  62. package/apps/storefront/src/lib/styleGuideThemes.ts +20 -22
  63. package/apps/storefront/src/pages/[...slug].astro +13 -8
  64. package/apps/storefront/src/pages/blog/[slug].astro +56 -24
  65. package/apps/storefront/src/pages/blog/author/[author].astro +17 -9
  66. package/apps/storefront/src/pages/blog/category/[category].astro +18 -10
  67. package/apps/storefront/src/pages/blog/index.astro +2 -0
  68. package/apps/storefront/src/pages/blog/page/[n].astro +2 -0
  69. package/apps/storefront/src/pages/blog/tag/[tag].astro +18 -10
  70. package/apps/storefront/src/pages/collections/[handle].astro +5 -4
  71. package/apps/storefront/src/pages/collections/index.astro +4 -3
  72. package/apps/storefront/src/pages/index.astro +34 -27
  73. package/apps/storefront/src/pages/products/[handle].astro +5 -4
  74. package/apps/storefront/src/pages/saved.astro +4 -3
  75. package/apps/storefront/src/pages/search.astro +4 -3
  76. package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +129 -118
  77. package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +19 -15
  78. package/apps/storefront/src/pages/style-guide/[tenant]/guide/[theme].astro +23 -27
  79. package/apps/storefront/src/pages/style-guide/[tenant]/index.astro +7 -14
  80. package/apps/storefront/src/pages/style-guide/index.astro +6 -5
  81. package/apps/storefront/src/pages/tenants/[id]/[...path].ts +32 -7
  82. package/apps/storefront/src/pages/the-build/[slug].astro +44 -20
  83. package/apps/storefront/src/pages/the-build/index.astro +2 -0
  84. package/apps/storefront/src/pages/the-build/page/[n].astro +2 -0
  85. package/apps/storefront/src/styles/admin.css +47 -0
  86. package/apps/storefront/src/styles/global.css +104 -56
  87. package/apps/storefront/src/themes/derivedTokens.ts +34 -0
  88. package/apps/storefront/src/themes/schema.ts +6 -0
  89. package/apps/storefront/test/pipeline/harness.ts +164 -0
  90. package/apps/storefront/test/pipeline/intake-worker.mjs +14 -0
  91. package/apps/storefront/test/pipeline/pipeline-host.ts +77 -0
  92. package/apps/storefront/test/pipeline/synthetic-job.ts +156 -0
  93. package/apps/storefront/vitest.workers.config.cts +67 -0
  94. package/docs/widget-library-guide.md +1496 -0
  95. package/package.json +1 -1
  96. package/packages/cli/src/declared-config.mjs +400 -5
  97. package/packages/public-runtime/package.json +1 -0
  98. package/packages/public-runtime/src/checkout.ts +13 -0
  99. package/packages/public-runtime/src/chrome.ts +93 -2
  100. package/packages/public-runtime/src/compliance-coherence.d.mts +17 -0
  101. package/packages/public-runtime/src/compliance-coherence.mjs +84 -0
  102. package/packages/public-runtime/src/declared-tenant-config.d.mts +1 -1
  103. package/packages/public-runtime/src/declared-tenant-config.mjs +6 -2
  104. package/packages/public-runtime/src/extension-contract-values.d.mts +4 -1
  105. package/packages/public-runtime/src/extension-contract-values.mjs +14 -1
  106. package/packages/public-runtime/src/extension-contract.ts +2 -0
  107. package/packages/public-runtime/src/tenant.ts +48 -4
  108. package/pnpm-lock.runner.yaml +469 -0
  109. package/scripts/tenant/validate.mjs +27 -1
@@ -0,0 +1,156 @@
1
+ /**
2
+ * A synthetic 5-step job kind for the TenantPipeline fault harness. Test-only: the deployed
3
+ * registry (`src/lib/pipeline/job-kinds.ts`) never contains it.
4
+ *
5
+ * Steps s1..s5. s3 works in three chunks and checkpoints after each one. s2 records a commit
6
+ * fact, s4 evidence, s5 the live state, so retried fact writes are exercised too. Every step
7
+ * execution is logged to `synthetic_runs` (to prove resume-not-restart) and every effect to
8
+ * `synthetic_effects` with INSERT OR IGNORE (the step is idempotent, as the engine requires).
9
+ *
10
+ * The intent carries a fault plan: throw / hang past the lease / evict the object, at a step
11
+ * boundary (`before` the step's work, `after` it but before the engine records it) or `mid` s3
12
+ * (after its first chunk's checkpoint). A fault fires once unless `persistent`; its firing is
13
+ * persisted before it acts so it survives the eviction it causes.
14
+ */
15
+ import type { DurableObjectState } from "cloudflare:workers";
16
+ import {
17
+ InvalidJobInput,
18
+ JobKindRegistry,
19
+ StaleExecutionError,
20
+ type JobKindDefinition,
21
+ type StepContext,
22
+ type StepDefinition,
23
+ type StepOutcome,
24
+ } from "../../src/lib/pipeline/job-runner";
25
+
26
+ export type FaultMode = "throw" | "hang" | "evict";
27
+ export interface Fault {
28
+ at: "before" | "after" | "mid";
29
+ step: number;
30
+ mode: FaultMode;
31
+ persistent?: boolean;
32
+ }
33
+ export interface SyntheticInput {
34
+ faults: Fault[];
35
+ }
36
+
37
+ export const SYNTHETIC_KIND = "synthetic-5-step";
38
+ export const SYNTHETIC_SHORT_DEADLINE_KIND = "synthetic-5-step-short-deadline";
39
+ export const SYNTHETIC_LEASE_MS = 250;
40
+ export const SYNTHETIC_DEADLINE_MS = 30_000;
41
+ export const SHORT_DEADLINE_MS = 1_500;
42
+ export const S3_CHUNKS = 3;
43
+
44
+ /** Executions abandoned by a lease expiry, kept so a test can try writing through them. */
45
+ export const abandonedContexts: StepContext<SyntheticInput>[] = [];
46
+
47
+ function parseInput(raw: unknown): SyntheticInput {
48
+ const faults = (raw as { faults?: unknown } | null)?.faults ?? [];
49
+ if (!Array.isArray(faults)) throw new InvalidJobInput("faults must be an array");
50
+ for (const f of faults as Fault[]) {
51
+ if (!["before", "after", "mid"].includes(f.at)) throw new InvalidJobInput("fault.at is invalid");
52
+ if (!["throw", "hang", "evict"].includes(f.mode)) throw new InvalidJobInput("fault.mode is invalid");
53
+ if (!Number.isInteger(f.step) || f.step < 1 || f.step > 5) throw new InvalidJobInput("fault.step is invalid");
54
+ }
55
+ return { faults: faults as Fault[] };
56
+ }
57
+
58
+ function harnessTables(state: DurableObjectState): void {
59
+ const { sql } = state.storage;
60
+ sql.exec(`CREATE TABLE IF NOT EXISTS synthetic_runs (seq INTEGER PRIMARY KEY AUTOINCREMENT, job_id TEXT, step INTEGER, chunk INTEGER, attempt INTEGER)`);
61
+ sql.exec(`CREATE TABLE IF NOT EXISTS synthetic_effects (job_id TEXT, step INTEGER, chunk INTEGER, PRIMARY KEY (job_id, step, chunk))`);
62
+ sql.exec(`CREATE TABLE IF NOT EXISTS synthetic_faults_fired (job_id TEXT, fault INTEGER, n INTEGER, PRIMARY KEY (job_id, fault))`);
63
+ sql.exec(`CREATE TABLE IF NOT EXISTS synthetic_zombie_writes (job_id TEXT, outcome TEXT)`);
64
+ }
65
+
66
+ function syntheticKind(state: DurableObjectState, kind: string, deadlineMs: number): JobKindDefinition<SyntheticInput> {
67
+ const { sql } = state.storage;
68
+
69
+ async function maybeFault(ctx: StepContext<SyntheticInput>, at: Fault["at"], step: number): Promise<void> {
70
+ const index = ctx.input.faults.findIndex((f) => f.at === at && f.step === step);
71
+ if (index < 0) return;
72
+ const fault = ctx.input.faults[index]!;
73
+ const fired = sql
74
+ .exec<{ n: number }>(`SELECT n FROM synthetic_faults_fired WHERE job_id = ? AND fault = ?`, ctx.jobId, index)
75
+ .toArray()[0];
76
+ if (fired && !fault.persistent) return;
77
+ sql.exec(
78
+ `INSERT INTO synthetic_faults_fired (job_id, fault, n) VALUES (?, ?, 1)
79
+ ON CONFLICT(job_id, fault) DO UPDATE SET n = n + 1`,
80
+ ctx.jobId,
81
+ index,
82
+ );
83
+ await state.storage.sync();
84
+ if (fault.mode === "throw") throw new Error(`synthetic fault ${at} s${step}`);
85
+ if (fault.mode === "evict") {
86
+ state.abort(`synthetic eviction ${at} s${step}`);
87
+ throw new Error("abort() returned");
88
+ }
89
+ abandonedContexts.push(ctx);
90
+ await new Promise((resolve) => setTimeout(resolve, SYNTHETIC_LEASE_MS * 8));
91
+ try {
92
+ ctx.saveCheckpoint({ zombie: true });
93
+ sql.exec(`INSERT INTO synthetic_zombie_writes (job_id, outcome) VALUES (?, 'accepted')`, ctx.jobId);
94
+ } catch (err) {
95
+ sql.exec(
96
+ `INSERT INTO synthetic_zombie_writes (job_id, outcome) VALUES (?, ?)`,
97
+ ctx.jobId,
98
+ err instanceof StaleExecutionError ? "refused" : "error",
99
+ );
100
+ }
101
+ throw new Error("hung execution woke up");
102
+ }
103
+
104
+ function logRun(ctx: StepContext<SyntheticInput>, step: number, chunk: number): void {
105
+ sql.exec(`INSERT INTO synthetic_runs (job_id, step, chunk, attempt) VALUES (?, ?, ?, ?)`, ctx.jobId, step, chunk, ctx.attempt);
106
+ sql.exec(`INSERT OR IGNORE INTO synthetic_effects (job_id, step, chunk) VALUES (?, ?, ?)`, ctx.jobId, step, chunk);
107
+ }
108
+
109
+ const plainStep = (step: number, fact?: (ctx: StepContext<SyntheticInput>) => void): StepDefinition<SyntheticInput> => ({
110
+ name: `s${step}`,
111
+ async run(ctx): Promise<StepOutcome> {
112
+ await maybeFault(ctx, "before", step);
113
+ logRun(ctx, step, 0);
114
+ fact?.(ctx);
115
+ await maybeFault(ctx, "after", step);
116
+ return { kind: "done" };
117
+ },
118
+ });
119
+
120
+ const chunkedStep: StepDefinition<SyntheticInput> = {
121
+ name: "s3",
122
+ async run(ctx): Promise<StepOutcome> {
123
+ const next = (ctx.checkpoint as { next?: number } | null)?.next ?? 0;
124
+ if (next === 0) await maybeFault(ctx, "before", 3);
125
+ if (next === 1) await maybeFault(ctx, "mid", 3);
126
+ logRun(ctx, 3, next);
127
+ if (next < S3_CHUNKS - 1) return { kind: "continue", checkpoint: { next: next + 1 } };
128
+ await maybeFault(ctx, "after", 3);
129
+ return { kind: "done" };
130
+ },
131
+ };
132
+
133
+ return {
134
+ kind,
135
+ parseInput,
136
+ steps: [
137
+ plainStep(1),
138
+ plainStep(2, (ctx) => ctx.recordCommit(`sha-${ctx.jobId}`, [{ pr: 2, changeId: "c2", headSha: "h2" }])),
139
+ chunkedStep,
140
+ plainStep(4, (ctx) => ctx.recordEvidence(`sha-${ctx.jobId}`, `digest-${ctx.jobId}`, true)),
141
+ plainStep(5, (ctx) => ctx.recordLive("worker-r2-pointer", `sha-${ctx.jobId}`, `rcpt-${ctx.jobId}`, null)),
142
+ ],
143
+ leaseMs: SYNTHETIC_LEASE_MS,
144
+ deadlineMs,
145
+ maxAttemptsPerStep: kind === SYNTHETIC_SHORT_DEADLINE_KIND ? 1_000 : 5,
146
+ retryBaseMs: 10,
147
+ defaultExpectedMs: 2_000,
148
+ };
149
+ }
150
+
151
+ export function syntheticJobKinds(state: DurableObjectState): JobKindRegistry {
152
+ harnessTables(state);
153
+ return new JobKindRegistry()
154
+ .register(syntheticKind(state, SYNTHETIC_KIND, SYNTHETIC_DEADLINE_MS))
155
+ .register(syntheticKind(state, SYNTHETIC_SHORT_DEADLINE_KIND, SHORT_DEADLINE_MS));
156
+ }
@@ -0,0 +1,67 @@
1
+ // The pipeline harness: tests that run INSIDE workerd (@cloudflare/vitest-pool-workers), so the
2
+ // TenantPipeline Durable Object is exercised with real SQLite storage, real alarms and real
3
+ // eviction (`ctx.abort()`). The Node unit suite (vitest.config.cts) never picks these up.
4
+ //
5
+ // The object lives in its own Worker, `pipeline-host`, not in the test runner's isolate: an
6
+ // eviction must restart the object without touching its callers, as in production. Auxiliary
7
+ // Workers take plain modules, so the host is bundled here, in memory, with esbuild.
8
+ // .cts for the same reason as vitest.config.cts: compiled in memory, no sibling file written.
9
+ process.env.VITE_CJS_IGNORE_WARNING = "1";
10
+ const { defineWorkersConfig } = require("@cloudflare/vitest-pool-workers/config");
11
+ const esbuild = require("esbuild");
12
+ const path = require("node:path");
13
+
14
+ const compatibilityDate = "2024-11-01";
15
+ const compatibilityFlags = ["nodejs_compat"];
16
+ const HOST = "pipeline-host";
17
+
18
+ const hostModule = esbuild.buildSync({
19
+ entryPoints: [path.join(__dirname, "test/pipeline/pipeline-host.ts")],
20
+ bundle: true,
21
+ format: "esm",
22
+ platform: "neutral",
23
+ target: "es2022",
24
+ external: ["cloudflare:*"],
25
+ write: false,
26
+ }).outputFiles[0].text;
27
+
28
+ const pipelineBinding = { TENANT_PIPELINE: { className: "TenantPipeline", scriptName: HOST } };
29
+
30
+ module.exports = defineWorkersConfig({
31
+ test: {
32
+ name: "pipeline",
33
+ include: ["test/pipeline/**/*.test.ts"],
34
+ cache: false,
35
+ testTimeout: 60_000,
36
+ hookTimeout: 60_000,
37
+ poolOptions: {
38
+ workers: {
39
+ isolatedStorage: false,
40
+ singleWorker: true,
41
+ miniflare: {
42
+ compatibilityDate,
43
+ compatibilityFlags,
44
+ durableObjects: pipelineBinding,
45
+ serviceBindings: { PIPELINE_HOST: HOST, INTAKE_B: "pipeline-intake-b" },
46
+ workers: [
47
+ {
48
+ name: HOST,
49
+ modules: [{ type: "ESModule", path: "pipeline-host.mjs", contents: hostModule }],
50
+ compatibilityDate,
51
+ compatibilityFlags,
52
+ durableObjects: { TENANT_PIPELINE: { className: "TenantPipeline", useSQLite: true } },
53
+ },
54
+ {
55
+ name: "pipeline-intake-b",
56
+ modules: true,
57
+ scriptPath: path.join(__dirname, "test/pipeline/intake-worker.mjs"),
58
+ compatibilityDate,
59
+ compatibilityFlags,
60
+ durableObjects: pipelineBinding,
61
+ },
62
+ ],
63
+ },
64
+ },
65
+ },
66
+ },
67
+ });