@tokenoftrust/storefront-runner 2.0.32 → 2.0.34

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.
@@ -133,6 +133,14 @@ export default defineConfig({
133
133
  "@astrojs/cloudflare/entrypoints/preview.js",
134
134
  ],
135
135
  },
136
+ // The Cloudflare dev worker keeps its SSR module graph across Vite optimizer
137
+ // turns. Prebundle Preact's renderer before that graph is loaded so it never
138
+ // retains a URL for a superseded .vite/deps_ssr chunk.
139
+ ssr: {
140
+ optimizeDeps: {
141
+ include: ["@astrojs/preact/server.js"],
142
+ },
143
+ },
136
144
  // Dev-only: let the dev server accept tenant Host headers so host-based
137
145
  // tenant routing can be tested locally (e.g. curl -H 'Host: sample-store.example').
138
146
  // Prod runs on Cloudflare Workers, which has no such allowlist.
@@ -4,9 +4,17 @@
4
4
  * a framework, stays URL-addressable. The current query's other params ride
5
5
  * along as hidden inputs so sort doesn't drop active filters.
6
6
  *
7
- * Auto-submit is wired by a nonce'd `is:inline` <script> rather than an inline
8
- * `onchange=` handler, so a strict, ENFORCED CSP allows it without 'unsafe-inline'
9
- * / 'unsafe-hashes' (Epic S2). The <noscript> Apply button covers the no-JS case.
7
+ * Auto-submit is wired by a nonce'd `is:inline` script element rather than an
8
+ * inline `onchange=` handler, so a strict, ENFORCED CSP allows it without
9
+ * 'unsafe-inline' / 'unsafe-hashes' (Epic S2). The noscript Apply button covers
10
+ * the no-JS case.
11
+ *
12
+ * Never write a literal script tag (angle brackets) in this file's frontmatter
13
+ * comment: the .astro dependency scanner matches it as a real tag, pairs it with
14
+ * the closing tag of the actual block below, and parses the prose between them as
15
+ * JavaScript. That fails the whole scan, and Vite's response is to skip dependency
16
+ * pre-bundling for the entire app rather than to error — which silently disables
17
+ * the SSR prebundle that `astro.config.mjs` relies on.
10
18
  */
11
19
  import type { ListProductsQuery } from "@tot/public-runtime";
12
20
 
@@ -41,7 +41,7 @@ const marketing = !features.catalog;
41
41
 
42
42
  // Raw-HTML home (vendor-injected): serve the tenant's own self-contained
43
43
  // document VERBATIM, bypassing the block Layout entirely. Its inline
44
- // <style>/<script> get this request's CSP nonce so the strict policy still
44
+ // style/script elements get this request's CSP nonce so the strict policy still
45
45
  // allows them. This route stays fully inside Astro — middleware already applied
46
46
  // tenant resolution + canonical host, and on the way out applies security
47
47
  // headers, the CSP, and (in prod) the edge page cache. SEO title/meta/canonical/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/storefront-runner",
3
- "version": "2.0.32",
3
+ "version": "2.0.34",
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",
@@ -6,6 +6,7 @@
6
6
  * selectors supplied by a caller, event detail, or arbitrary DOM data.
7
7
  */
8
8
  import type { PlatformManagedActionActivation, PlatformManagedActionSource } from "./managed-analytics-bridge.js";
9
+ import { managedAnalyticsOpaqueRevision } from "./managed-analytics-value-safety.js";
9
10
 
10
11
  /** The only reviewed CTA identities that may raise this marketing action. */
11
12
  export const CONTACT_SALES_ACTION_SOURCES = [
@@ -42,6 +43,7 @@ export function createContactSalesActionSource(
42
43
  ): PlatformManagedActionSource {
43
44
  const document = options.document;
44
45
  const now = options.now ?? Date.now;
46
+ const revisionId = managedAnalyticsOpaqueRevision(options.planRevision);
45
47
  let sequence = 0;
46
48
 
47
49
  return {
@@ -55,10 +57,10 @@ export function createContactSalesActionSource(
55
57
  sequence += 1;
56
58
  const at = now();
57
59
  const activation: PlatformManagedActionActivation = {
58
- logicalId: `${source.logicalId}-${options.planRevision}-${sequence}`,
60
+ logicalId: `${source.logicalId}-${revisionId}-${sequence}`,
59
61
  name: CONTACT_SALES_ACTION_NAME,
60
62
  correlation: {
61
- id: `contact-sales-${options.planRevision}`,
63
+ id: `contact-sales-${revisionId}`,
62
64
  expiresAt: new Date(at + CORRELATION_TTL_MS).toISOString(),
63
65
  },
64
66
  sequence,
@@ -49,6 +49,7 @@ import {
49
49
  } from "./native-ga4-sandbox.js";
50
50
  import { ManagedAnalyticsEventBrokerBridge } from "./managed-analytics-bridge.js";
51
51
  import { minimizePageAttribution, type ManagedPageViewInput } from "./managed-analytics.js";
52
+ import { managedAnalyticsOpaqueRevision } from "./managed-analytics-value-safety.js";
52
53
  import {
53
54
  createContactSalesActionSource,
54
55
  type ContactSalesActionDocument,
@@ -238,7 +239,7 @@ function documentPageView(
238
239
  referrerUrl: win.document?.referrer ?? null,
239
240
  routeUrl: win.location.href,
240
241
  });
241
- const id = `pageview-${descriptor.planRevision}`;
242
+ const id = `pageview-${managedAnalyticsOpaqueRevision(descriptor.planRevision)}`;
242
243
  return {
243
244
  logicalId: id,
244
245
  path: win.location.pathname,
@@ -1,9 +1,11 @@
1
1
  /** Shared PII-value canary for managed browser events and their diagnostics. */
2
2
  import { MANAGED_ANALYTICS_PII_VALUE_PATTERN } from "./managed-analytics-api-v1-safety.mjs";
3
+ import { fnv1a128Hex } from "./digest.js";
3
4
 
4
5
  const MANAGED_ANALYTICS_PII_VALUE = new RegExp(MANAGED_ANALYTICS_PII_VALUE_PATTERN);
5
6
  const MANAGED_ANALYTICS_PROTECTED_VALUE =
6
7
  /(?:@|\d{7,}|(?:^|[./_~-])(?:bearer|password|secret|token|authorization|preview)(?:$|[./_~-]))/i;
8
+ const OPAQUE_HEX_ALPHABET = "abcdefghijklmnop";
7
9
 
8
10
  /** True when a public string resembles an email, SSN, payment card, or phone number. */
9
11
  export function resemblesManagedAnalyticsPii(value: unknown): boolean {
@@ -17,6 +19,22 @@ export function resemblesManagedAnalyticsProtectedValue(value: unknown): boolean
17
19
  );
18
20
  }
19
21
 
22
+ /**
23
+ * Derive a stable letters-only token for managed-event ids from a public revision.
24
+ *
25
+ * Revisions are hexadecimal content identities and can therefore contain a run of
26
+ * seven digits by chance. Copying one directly into an event id trips the deliberate
27
+ * phone/card canary and silently rejects an otherwise safe event. Hashing once more
28
+ * and encoding each nibble as `a`-`p` retains deterministic revision scoping while
29
+ * making a numeric/credential-looking result impossible by construction.
30
+ */
31
+ export function managedAnalyticsOpaqueRevision(value: string): string {
32
+ return Array.from(
33
+ fnv1a128Hex(value),
34
+ (nibble) => OPAQUE_HEX_ALPHABET[Number.parseInt(nibble, 16)]!,
35
+ ).join("");
36
+ }
37
+
20
38
  /** Defense-in-depth for identifiers copied into structured broker observations. */
21
39
  export function safeManagedAnalyticsDiagnosticId(value: string): string {
22
40
  return resemblesManagedAnalyticsPii(value) ? "[redacted]" : value;