@tokenoftrust/storefront-runner 2.0.3 → 2.0.5
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/apps/storefront/.astro/content.d.ts +159 -0
- package/apps/storefront/.astro/integrations/_astrojs_cloudflare/cloudflare.d.ts +1 -0
- package/apps/storefront/.astro/types.d.ts +3 -0
- package/apps/storefront/migrations-apps/0011_dt_tracking_tenant_databases.sql +23 -0
- package/apps/storefront/node_modules/.bin/acorn +43 -0
- package/apps/storefront/node_modules/.bin/astro +43 -0
- package/apps/storefront/node_modules/.bin/astro-check +43 -0
- package/apps/storefront/node_modules/.bin/browserslist +43 -0
- package/apps/storefront/node_modules/.bin/cf-wrangler +43 -0
- package/apps/storefront/node_modules/.bin/depcruise +43 -0
- package/apps/storefront/node_modules/.bin/depcruise-baseline +43 -0
- package/apps/storefront/node_modules/.bin/depcruise-fmt +43 -0
- package/apps/storefront/node_modules/.bin/depcruise-wrap-stream-in-html +43 -0
- package/apps/storefront/node_modules/.bin/dependency-cruise +43 -0
- package/apps/storefront/node_modules/.bin/dependency-cruiser +43 -0
- package/apps/storefront/node_modules/.bin/drizzle-kit +43 -0
- package/apps/storefront/node_modules/.bin/esbuild +43 -0
- package/apps/storefront/node_modules/.bin/jiti +43 -0
- package/apps/storefront/node_modules/.bin/playwright +43 -0
- package/apps/storefront/node_modules/.bin/playwright-core +43 -0
- package/apps/storefront/node_modules/.bin/prettier +43 -0
- package/apps/storefront/node_modules/.bin/rollup +43 -0
- package/apps/storefront/node_modules/.bin/tsc +43 -0
- package/apps/storefront/node_modules/.bin/tsserver +43 -0
- package/apps/storefront/node_modules/.bin/tsx +43 -0
- package/apps/storefront/node_modules/.bin/vite +43 -0
- package/apps/storefront/node_modules/.bin/vitest +43 -0
- package/apps/storefront/node_modules/.bin/workerd +43 -0
- package/apps/storefront/node_modules/.bin/wrangler +43 -0
- package/apps/storefront/node_modules/.bin/wrangler2 +43 -0
- package/apps/storefront/node_modules/.bin/yaml +43 -0
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_client__js.js +58 -0
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_client__js.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_n_@preact_signals.js +741 -0
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_n_@preact_signals.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_server__js.js +2549 -0
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_server__js.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/_metadata.json +150 -0
- package/apps/storefront/node_modules/.vite/deps/astro-BIHuWFxm.js +434 -0
- package/apps/storefront/node_modules/.vite/deps/astro-BIHuWFxm.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
- package/apps/storefront/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/astro_n_axobject-query.js +2705 -0
- package/apps/storefront/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
- package/apps/storefront/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
- package/apps/storefront/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/audit-DKDpcdEI.js +1535 -0
- package/apps/storefront/node_modules/.vite/deps/audit-DKDpcdEI.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/highlight-B9UoX1wa.js +48 -0
- package/apps/storefront/node_modules/.vite/deps/highlight-B9UoX1wa.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/icons-BWi2wI9n.js +42 -0
- package/apps/storefront/node_modules/.vite/deps/icons-BWi2wI9n.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/package.json +3 -0
- package/apps/storefront/node_modules/.vite/deps/preact.js +310 -0
- package/apps/storefront/node_modules/.vite/deps/preact.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/preact_compat.js +420 -0
- package/apps/storefront/node_modules/.vite/deps/preact_compat.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/preact_compat_jsx-runtime.js +4 -0
- package/apps/storefront/node_modules/.vite/deps/preact_debug.js +236 -0
- package/apps/storefront/node_modules/.vite/deps/preact_debug.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/preact_devtools.js +12 -0
- package/apps/storefront/node_modules/.vite/deps/preact_devtools.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/preact_hooks.js +192 -0
- package/apps/storefront/node_modules/.vite/deps/preact_hooks.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/preact_jsx-dev-runtime.js +3 -0
- package/apps/storefront/node_modules/.vite/deps/preact_jsx-runtime.js +92 -0
- package/apps/storefront/node_modules/.vite/deps/preact_jsx-runtime.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/preact_test-utils.js +59 -0
- package/apps/storefront/node_modules/.vite/deps/preact_test-utils.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/rolldown-runtime-BPOCksWG.js +24 -0
- package/apps/storefront/node_modules/.vite/deps/settings-BVDV0Roj.js +189 -0
- package/apps/storefront/node_modules/.vite/deps/settings-BVDV0Roj.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/static-html-DveYqwMo.js +13 -0
- package/apps/storefront/node_modules/.vite/deps/static-html-DveYqwMo.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/toolbar-B3VIHCXg.js +458 -0
- package/apps/storefront/node_modules/.vite/deps/toolbar-B3VIHCXg.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/ui-library-BK6ricUd.js +1016 -0
- package/apps/storefront/node_modules/.vite/deps/ui-library-BK6ricUd.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/window-BvxDenKN.js +32 -0
- package/apps/storefront/node_modules/.vite/deps/window-BvxDenKN.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/xray-YFiRUU0Z.js +113 -0
- package/apps/storefront/node_modules/.vite/deps/xray-YFiRUU0Z.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps_ssr/_metadata.json +8 -0
- package/apps/storefront/node_modules/.vite/deps_ssr/package.json +3 -0
- package/apps/storefront/src/components/chrome/MegaMenu.astro +11 -1
- package/apps/storefront/src/components/chrome/NavDropdown.astro +49 -7
- package/apps/storefront/src/components/chrome/SiteFooter.astro +29 -4
- package/apps/storefront/src/components/chrome/SiteHeader.astro +39 -6
- package/apps/storefront/src/components/chrome/UtilityNav.astro +13 -1
- package/apps/storefront/src/components/islands/MobileNav.tsx +42 -2
- package/apps/storefront/src/layouts/Layout.astro +65 -7
- package/apps/storefront/src/lib/apps/registryAccess.ts +45 -0
- package/apps/storefront/src/lib/auth/adminEntry.ts +4 -4
- package/apps/storefront/src/lib/chrome/ctaIdentity.ts +21 -0
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +22 -22
- package/apps/storefront/src/lib/chrome/trackingPlacements.ts +36 -0
- package/apps/storefront/src/lib/d1/schema-apps.ts +57 -0
- package/apps/storefront/src/lib/rawChrome.ts +73 -14
- package/apps/storefront/src/lib/rawMarketingHtml.ts +30 -0
- package/apps/storefront/src/lib/shared/capability.ts +68 -0
- package/apps/storefront/src/lib/shared/developerInvites.ts +1250 -0
- package/apps/storefront/src/lib/shared/invitePreflight.ts +123 -0
- package/apps/storefront/src/lib/storyblok/provider.ts +21 -5
- package/apps/storefront/src/lib/tracking/collectorAllowlist.ts +40 -0
- package/apps/storefront/src/lib/tracking/collectorIngest.ts +188 -0
- package/apps/storefront/src/lib/tracking/d1Control.ts +180 -0
- package/apps/storefront/src/lib/tracking/interactionCollector.ts +202 -0
- package/apps/storefront/src/lib/tracking/interactionHealth.ts +252 -0
- package/apps/storefront/src/lib/tracking/interactionSchema.ts +107 -0
- package/apps/storefront/src/lib/tracking/interactionStore.ts +0 -0
- package/apps/storefront/src/lib/tracking/pruneRollup.ts +260 -0
- package/apps/storefront/src/lib/tracking/publishSafetyNet.ts +162 -0
- package/apps/storefront/src/lib/tracking/tenantD1Registry.ts +214 -0
- package/apps/storefront/src/lib/tracking/tenantReadAccess.ts +138 -0
- package/apps/storefront/src/lib/tracking-writeback/rewriteTrackingIds.ts +175 -0
- package/apps/storefront/src/lib/tracking-writeback/writebackCandidate.ts +110 -0
- package/apps/storefront/src/lib/webhooks/deliveryEngine.ts +38 -4
- package/apps/storefront/src/lib/webhooks/emit.ts +1 -1
- package/apps/storefront/src/lib/webhooks/getDispatcher.ts +1 -1
- package/apps/storefront/src/pages/api/admin/interaction-insights.ts +101 -0
- package/apps/storefront/src/pages/api/auth/magic-exchange.ts +1 -1
- package/apps/storefront/src/pages/api/tracking/interaction.ts +123 -0
- package/apps/storefront/src/pages/index.astro +1 -1
- package/apps/storefront/src/pages/products/[handle].astro +1 -1
- package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +33 -14
- package/apps/storefront/src/workers/pruneRollupWorker.ts +120 -0
- package/package.json +1 -1
- package/packages/public-runtime/node_modules/.bin/tsc +43 -0
- package/packages/public-runtime/node_modules/.bin/tsserver +43 -0
- package/packages/public-runtime/node_modules/.bin/vitest +43 -0
- package/packages/public-runtime/src/chrome.ts +18 -7
- package/packages/public-runtime/src/index.ts +10 -0
- package/packages/public-runtime/src/integration/index.ts +1 -0
- package/packages/public-runtime/src/integration/interaction-tracking-gate.ts +142 -0
- package/packages/public-runtime/src/interaction-tracking.ts +364 -0
- package/packages/public-runtime/src/tracking-id-derivation.ts +1008 -0
- package/pnpm-workspace.yaml +3 -0
- package/scripts/tenant/validate.mjs +306 -1
- package/scripts/tot-dev.mjs +59 -3
- package/tenants/home/public/js/storefront.js +6 -87
- package/apps/storefront/src/lib/apps/adminService.ts +0 -116
- package/apps/storefront/src/lib/apps/adminSession.ts +0 -214
- package/apps/storefront/src/lib/apps/apiAuth.ts +0 -91
- package/apps/storefront/src/lib/apps/apiRoute.ts +0 -35
- package/apps/storefront/src/lib/apps/catalogMapper.ts +0 -39
- package/apps/storefront/src/lib/apps/consentConfigStore.ts +0 -228
- package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +0 -447
- package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +0 -367
- package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +0 -315
- package/apps/storefront/src/lib/apps/healthAggregate.ts +0 -66
- package/apps/storefront/src/lib/apps/integrationLifecycle.ts +0 -614
- package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +0 -125
- package/apps/storefront/src/lib/apps/integrationReadModel.ts +0 -563
- package/apps/storefront/src/lib/apps/integrationsTab.ts +0 -547
- package/apps/storefront/src/lib/apps/integrationsView.ts +0 -106
- package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +0 -599
- package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +0 -170
- package/apps/storefront/src/lib/apps/tokenIssuer.ts +0 -121
- package/apps/storefront/src/lib/apps/tokenVerifier.ts +0 -148
- package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +0 -16
- package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +0 -105
- package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +0 -177
- package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +0 -251
- package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +0 -241
- package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +0 -211
- package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +0 -409
- package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +0 -368
- package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +0 -188
- package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +0 -291
- package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +0 -95
- package/apps/storefront/src/lib/content-edit/applyDiff.ts +0 -91
- package/apps/storefront/src/lib/content-edit/client.ts +0 -286
- package/apps/storefront/src/lib/content-edit/index.ts +0 -14
- package/apps/storefront/src/lib/content-edit/route.ts +0 -25
- package/apps/storefront/src/lib/content-edit/types.ts +0 -19
- package/apps/storefront/src/pages/api/content-edit/ai-edit.ts +0 -164
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/tracking/interaction (dt-collector-v1) — the beacon sink for
|
|
3
|
+
* declarative interaction tracking.
|
|
4
|
+
*
|
|
5
|
+
* The browser collector (`@/lib/tracking/interactionCollector`) batches
|
|
6
|
+
* `TotInteraction`s captured off `installInteractionTracking` /
|
|
7
|
+
* `onInteraction` (`@tot/public-runtime`) — via `dt-consent-environment-gate`'s
|
|
8
|
+
* consent/environment wrapper in front of it — and POSTs them here with
|
|
9
|
+
* `navigator.sendBeacon`, same transport contract as the F14 RUM beacon
|
|
10
|
+
* (`pages/api/rum/vitals.ts`): unauthenticated (a beacon carries no
|
|
11
|
+
* credentials), tenant resolved from `context.locals.tenant.tenant_id`
|
|
12
|
+
* (never the body), 204 on every outcome so a beacon never errors, retries,
|
|
13
|
+
* or blocks page unload.
|
|
14
|
+
*
|
|
15
|
+
* VALIDATION — `collectorIngest.ts` re-derives every field from the raw body
|
|
16
|
+
* under a default-deny allowlist (`collectorAllowlist.ts`): an unlisted key is
|
|
17
|
+
* dropped and counted, a listed-but-wrong-typed field rejects that event. Never
|
|
18
|
+
* trust the client for `tenant_id` or `stream` — both are stamped here, always.
|
|
19
|
+
*
|
|
20
|
+
* STORAGE — two sinks, fanned out past the response (`interactionStore.ts`,
|
|
21
|
+
* same shape as `pages/api/activity.ts`'s `fanOutActivity`):
|
|
22
|
+
*
|
|
23
|
+
* • RAW ROWS → the tenant's OWN D1 database, resolved (and provisioned on
|
|
24
|
+
* first sight) by `tenantD1Registry.resolveTenantStore`. ONE DATABASE PER
|
|
25
|
+
* TENANT, so cross-tenant leakage is impossible by construction, not by a
|
|
26
|
+
* correct `WHERE tenant_id = ?`. The directory that maps tenant → database
|
|
27
|
+
* lives in the shared `STOREFRONT_APPS_DB`
|
|
28
|
+
* (`tracking_tenant_databases`); the rows never do.
|
|
29
|
+
* • AGGREGATES → the DEDICATED `INTERACTION_ANALYTICS` Analytics Engine
|
|
30
|
+
* dataset (`storefront_interactions`) — NOT the operational spine's
|
|
31
|
+
* `storefront_activity` dataset — rolled up to one point per dimension
|
|
32
|
+
* tuple, so no per-event identity reaches AE.
|
|
33
|
+
*
|
|
34
|
+
* Both sinks are best-effort: unconfigured or failing, the batch falls back to
|
|
35
|
+
* the Workers Logs firehose and the beacon still 204s. `stream` (always
|
|
36
|
+
* `"interaction"` today) is the discriminator column to route on if a second
|
|
37
|
+
* stream is ever added.
|
|
38
|
+
*/
|
|
39
|
+
import type { APIContext } from "astro";
|
|
40
|
+
import { readD1, readAnalytics, runAfterResponse, type CfExecutionContextLike } from "@/lib/env";
|
|
41
|
+
import { fromD1 } from "@/lib/d1/catalog";
|
|
42
|
+
import { ingestInteractions, type StampedInteractionEvent } from "@/lib/tracking/collectorIngest";
|
|
43
|
+
import { readD1Control } from "@/lib/tracking/d1Control";
|
|
44
|
+
import { resolveTenantStore, trackingNamePrefix } from "@/lib/tracking/tenantD1Registry";
|
|
45
|
+
import { persistInteractions } from "@/lib/tracking/interactionStore";
|
|
46
|
+
import type { AnalyticsEngineLike } from "@/lib/activity/store";
|
|
47
|
+
|
|
48
|
+
export const prerender = false;
|
|
49
|
+
|
|
50
|
+
/** Cap the batch so one beacon can't fan out unbounded off-path work. */
|
|
51
|
+
const MAX_BATCH = 50;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Resolve both sinks and write the batch. Runs PAST the response
|
|
55
|
+
* (`runAfterResponse`), so binding resolution + the D1 REST round-trip never
|
|
56
|
+
* hold the beacon open. Never throws — a telemetry beacon must not fail because
|
|
57
|
+
* storage is unconfigured or unreachable.
|
|
58
|
+
*/
|
|
59
|
+
async function storeInteractions(
|
|
60
|
+
tenantId: string,
|
|
61
|
+
events: StampedInteractionEvent[],
|
|
62
|
+
ingestedAt: string,
|
|
63
|
+
): Promise<void> {
|
|
64
|
+
try {
|
|
65
|
+
const [appsDb, analyticsBinding, control, namePrefix] = await Promise.all([
|
|
66
|
+
readD1("STOREFRONT_APPS_DB"),
|
|
67
|
+
readAnalytics("INTERACTION_ANALYTICS"),
|
|
68
|
+
readD1Control(),
|
|
69
|
+
trackingNamePrefix(),
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
// The raw sink needs BOTH the directory (shared apps D1) and the D1 REST
|
|
73
|
+
// credentials that address a per-tenant database; either absent ⇒ skip it.
|
|
74
|
+
const store =
|
|
75
|
+
appsDb && control
|
|
76
|
+
? await resolveTenantStore(tenantId, {
|
|
77
|
+
apps: fromD1(appsDb),
|
|
78
|
+
control,
|
|
79
|
+
namePrefix,
|
|
80
|
+
at: ingestedAt,
|
|
81
|
+
})
|
|
82
|
+
: undefined;
|
|
83
|
+
|
|
84
|
+
await persistInteractions(events, {
|
|
85
|
+
store,
|
|
86
|
+
analytics: (analyticsBinding as AnalyticsEngineLike | undefined) ?? undefined,
|
|
87
|
+
});
|
|
88
|
+
} catch {
|
|
89
|
+
/* never fail a telemetry beacon */
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export async function POST(context: APIContext): Promise<Response> {
|
|
94
|
+
try {
|
|
95
|
+
const tenantId = (context.locals as { tenant?: { tenant_id?: string } })?.tenant?.tenant_id;
|
|
96
|
+
// No resolved tenant (e.g. a request that never went through tenant
|
|
97
|
+
// resolution) ⇒ nothing to stamp or store; still 204, never error the beacon.
|
|
98
|
+
if (!tenantId) return new Response(null, { status: 204 });
|
|
99
|
+
|
|
100
|
+
const parsed: unknown = await context.request.json();
|
|
101
|
+
const events = Array.isArray(parsed)
|
|
102
|
+
? parsed
|
|
103
|
+
: parsed && typeof parsed === "object" && Array.isArray((parsed as { events?: unknown }).events)
|
|
104
|
+
? (parsed as { events: unknown[] }).events
|
|
105
|
+
: [];
|
|
106
|
+
if (events.length === 0) return new Response(null, { status: 204 });
|
|
107
|
+
|
|
108
|
+
const batch = events.slice(0, MAX_BATCH);
|
|
109
|
+
const ingestedAt = new Date().toISOString();
|
|
110
|
+
const { accepted } = ingestInteractions(batch, tenantId, ingestedAt);
|
|
111
|
+
|
|
112
|
+
if (accepted.length > 0) {
|
|
113
|
+
const cfContext = (context.locals as { cfContext?: CfExecutionContextLike })?.cfContext ?? null;
|
|
114
|
+
runAfterResponse(
|
|
115
|
+
storeInteractions(tenantId, accepted.map((a) => a.event), ingestedAt),
|
|
116
|
+
cfContext,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
} catch {
|
|
120
|
+
/* never fail a telemetry beacon */
|
|
121
|
+
}
|
|
122
|
+
return new Response(null, { status: 204 });
|
|
123
|
+
}
|
|
@@ -17,7 +17,7 @@ import TierCards from "@/components/membership/TierCards.astro";
|
|
|
17
17
|
import { withBase } from "@/lib/basePath";
|
|
18
18
|
import { rawHtmlPolicy, rawHtmlResponse } from "@/lib/rawMarketingHtml";
|
|
19
19
|
import AppWidgetFrame from "@/components/apps/AppWidgetFrame.astro";
|
|
20
|
-
import { getRegistryService } from "@/lib/apps/
|
|
20
|
+
import { getRegistryService } from "@/lib/apps/registryAccess.js";
|
|
21
21
|
import { renderWidgetSlot } from "@/lib/apps/widgets/renderSlot.js";
|
|
22
22
|
import type { CatalogCollection, CatalogProduct } from "@tot/public-runtime";
|
|
23
23
|
import {
|
|
@@ -42,7 +42,7 @@ import { productLd, breadcrumbLd, reviewLd } from "@/lib/jsonld";
|
|
|
42
42
|
import { isUsableImageUrl } from "@/lib/placeholder";
|
|
43
43
|
import { withBase } from "@/lib/basePath";
|
|
44
44
|
import AppWidgetFrame from "@/components/apps/AppWidgetFrame.astro";
|
|
45
|
-
import { getRegistryService } from "@/lib/apps/
|
|
45
|
+
import { getRegistryService } from "@/lib/apps/registryAccess.js";
|
|
46
46
|
import { renderWidgetSlot } from "@/lib/apps/widgets/renderSlot.js";
|
|
47
47
|
|
|
48
48
|
const { handle } = Astro.params;
|
|
@@ -43,6 +43,7 @@ import SiteHeader from "@/components/chrome/SiteHeader.astro";
|
|
|
43
43
|
import MegaMenu from "@/components/chrome/MegaMenu.astro";
|
|
44
44
|
import NavDropdown from "@/components/chrome/NavDropdown.astro";
|
|
45
45
|
import SiteFooter from "@/components/chrome/SiteFooter.astro";
|
|
46
|
+
import { CHROME_PLACEMENT } from "@/lib/chrome/trackingPlacements";
|
|
46
47
|
import NicotineWarning from "@/components/compliance/NicotineWarning.astro";
|
|
47
48
|
import PactActNotice from "@/components/compliance/PactActNotice.astro";
|
|
48
49
|
import AdultSignatureNotice from "@/components/compliance/AdultSignatureNotice.astro";
|
|
@@ -88,28 +89,33 @@ const { tenant, theme } = Astro.params;
|
|
|
88
89
|
const cspNonce = Astro.locals.cspNonce;
|
|
89
90
|
|
|
90
91
|
// --- Sample chrome data (preview-only; no live tenant content) ----------------
|
|
92
|
+
// Every actionable item declares its tracking intent (`action`) alongside its
|
|
93
|
+
// identity (`id`) — the two are different axes and neither is derived from the
|
|
94
|
+
// other. See docs/architecture/interaction-tracking-attribute-contract.md.
|
|
91
95
|
const sampleNav: ChromeNavItem[] = [
|
|
92
|
-
{ id: "nav-new", label: "New", href: "#", flag: true, badge: "NEW" },
|
|
96
|
+
{ id: "nav-new", action: "nav-new", label: "New", href: "#", flag: true, badge: "NEW" },
|
|
93
97
|
{
|
|
94
98
|
id: "nav-shop",
|
|
99
|
+
action: "nav-shop",
|
|
95
100
|
label: "Shop",
|
|
96
101
|
href: "#",
|
|
97
102
|
columns: [
|
|
98
|
-
{ title: "Category", links: [{ id: "nav-shop-disposables", label: "Disposables", href: "#" }, { id: "nav-shop-eliquid", label: "E-Liquid", href: "#" }, { id: "nav-shop-devices", label: "Devices", href: "#" }] },
|
|
99
|
-
{ title: "Featured", links: [{ id: "nav-shop-bestsellers", label: "Best Sellers", href: "#" }, { id: "nav-shop-onsale", label: "On Sale", href: "#", badge: "%" }] },
|
|
100
|
-
{ title: "Brands", links: [{ id: "nav-shop-northwind", label: "Northwind", href: "#" }, { id: "nav-shop-summit", label: "Summit Co.", href: "#" }] },
|
|
103
|
+
{ title: "Category", links: [{ id: "nav-shop-disposables", action: "nav-disposables", label: "Disposables", href: "#" }, { id: "nav-shop-eliquid", action: "nav-eliquid", label: "E-Liquid", href: "#" }, { id: "nav-shop-devices", action: "nav-devices", label: "Devices", href: "#" }] },
|
|
104
|
+
{ title: "Featured", links: [{ id: "nav-shop-bestsellers", action: "nav-bestsellers", label: "Best Sellers", href: "#" }, { id: "nav-shop-onsale", action: "nav-on-sale", label: "On Sale", href: "#", badge: "%" }] },
|
|
105
|
+
{ title: "Brands", links: [{ id: "nav-shop-northwind", action: "nav-brand-northwind", label: "Northwind", href: "#" }, { id: "nav-shop-summit", action: "nav-brand-summit", label: "Summit Co.", href: "#" }] },
|
|
101
106
|
],
|
|
102
107
|
},
|
|
103
|
-
{ id: "nav-coils", label: "Coils & Parts", href: "#", children: [{ id: "nav-coils-coils", label: "Coils", href: "#" }, { id: "nav-coils-pods", label: "Pods", href: "#" }, { id: "nav-coils-batteries", label: "Batteries", href: "#" }] },
|
|
104
|
-
{ id: "nav-sale", label: "On Sale", href: "#" },
|
|
108
|
+
{ id: "nav-coils", action: "nav-coils-and-parts", label: "Coils & Parts", href: "#", children: [{ id: "nav-coils-coils", action: "nav-coils", label: "Coils", href: "#" }, { id: "nav-coils-pods", action: "nav-pods", label: "Pods", href: "#" }, { id: "nav-coils-batteries", action: "nav-batteries", label: "Batteries", href: "#" }] },
|
|
109
|
+
{ id: "nav-sale", action: "nav-on-sale", label: "On Sale", href: "#" },
|
|
105
110
|
];
|
|
106
111
|
const sampleSocial = [
|
|
107
112
|
{ label: "X", href: "#", iconPath: "M18.9 2H22l-7.1 8.1L23 22h-6.6l-5-6.6L5.7 22H2.6l7.6-8.7L1.5 2h6.8l4.5 6 5.1-6z" },
|
|
108
113
|
];
|
|
109
114
|
const sampleFooterCols = [
|
|
110
|
-
|
|
111
|
-
{ title: "
|
|
112
|
-
{ title: "
|
|
115
|
+
// The footer repeats the header's intents: same `action`, different placement.
|
|
116
|
+
{ title: "Shop", links: [{ id: "ftr-shop-disposables", action: "nav-disposables", label: "Disposables", href: "#" }, { id: "ftr-shop-eliquid", action: "nav-eliquid", label: "E-Liquid", href: "#" }, { id: "ftr-shop-devices", action: "nav-devices", label: "Devices", href: "#" }] },
|
|
117
|
+
{ title: "Support", links: [{ id: "ftr-support-contact", action: "nav-contact", label: "Contact", href: "#" }, { id: "ftr-support-shipping", action: "nav-shipping", label: "Shipping", href: "#" }, { id: "ftr-support-returns", action: "nav-returns", label: "Returns", href: "#" }] },
|
|
118
|
+
{ title: "Company", links: [{ id: "ftr-company-about", action: "nav-about", label: "About", href: "#" }, { id: "ftr-company-blog", action: "nav-blog", label: "Blog", href: "#" }] },
|
|
113
119
|
];
|
|
114
120
|
// BrandGrid samples: text-wordmark tiles and logo-image tiles (Img renders
|
|
115
121
|
// deterministic placeholders offline so the image code path is exercised
|
|
@@ -321,12 +327,17 @@ const sampleManageEntries: SubscriptionManageEntry[] = [
|
|
|
321
327
|
// --- Sample islands data (preview-only) ----------------------------------
|
|
322
328
|
const sampleMobileNav = [
|
|
323
329
|
{
|
|
330
|
+
id: "mnav-shop",
|
|
331
|
+
action: "nav-shop",
|
|
324
332
|
label: "Shop",
|
|
325
333
|
href: "#",
|
|
326
|
-
children: [
|
|
334
|
+
children: [
|
|
335
|
+
{ id: "mnav-shop-disposables", action: "nav-disposables", label: "Disposables", href: "#" },
|
|
336
|
+
{ id: "mnav-shop-eliquid", action: "nav-eliquid", label: "E-Liquid", href: "#" },
|
|
337
|
+
],
|
|
327
338
|
},
|
|
328
|
-
{ label: "Coils & Parts", href: "#" },
|
|
329
|
-
{ label: "On Sale", href: "#" },
|
|
339
|
+
{ id: "mnav-coils", action: "nav-coils-and-parts", label: "Coils & Parts", href: "#" },
|
|
340
|
+
{ id: "mnav-sale", action: "nav-on-sale", label: "On Sale", href: "#" },
|
|
330
341
|
];
|
|
331
342
|
const sampleFromProduct = sampleProducts.find((p) => p.handle === "sample-from")!;
|
|
332
343
|
const sampleRecentlyViewed: RVItem[] = [
|
|
@@ -1149,7 +1160,7 @@ const componentVarsInline = Object.entries(componentVars)
|
|
|
1149
1160
|
<section class="sg-block">
|
|
1150
1161
|
<p class="sg-h">Chrome — UtilityNav</p>
|
|
1151
1162
|
<UtilityNav
|
|
1152
|
-
links={[{ id: "util-news", label: "News", href: "#" }, { id: "util-about", label: "About", href: "#" }, { id: "util-contact", label: "Contact", href: "#" }]}
|
|
1163
|
+
links={[{ id: "util-news", action: "nav-news", label: "News", href: "#" }, { id: "util-about", action: "nav-about", label: "About", href: "#" }, { id: "util-contact", action: "nav-contact", label: "Contact", href: "#" }]}
|
|
1153
1164
|
locale="English"
|
|
1154
1165
|
currency="United States (USD $)"
|
|
1155
1166
|
social={sampleSocial}
|
|
@@ -1181,7 +1192,15 @@ const componentVarsInline = Object.entries(componentVars)
|
|
|
1181
1192
|
<p class="sg-h">Chrome — NavDropdown accordion (mobile mega parity, zero-JS)</p>
|
|
1182
1193
|
<div style="max-width:18rem;background:var(--color-surface);border:1px solid var(--color-border);border-radius:8px;padding:8px 12px">
|
|
1183
1194
|
<ul class="grid">
|
|
1184
|
-
{sampleNav.map((item) =>
|
|
1195
|
+
{sampleNav.map((item, i) => (
|
|
1196
|
+
<NavDropdown
|
|
1197
|
+
item={item}
|
|
1198
|
+
mode="accordion"
|
|
1199
|
+
placement={CHROME_PLACEMENT.mobileMenu}
|
|
1200
|
+
menuPlacement={CHROME_PLACEMENT.mobileMenu}
|
|
1201
|
+
index={i}
|
|
1202
|
+
/>
|
|
1203
|
+
))}
|
|
1185
1204
|
</ul>
|
|
1186
1205
|
</div>
|
|
1187
1206
|
<p class="form-help" style="margin-top:8px;color:var(--color-muted)">Same nav items in the mobile menu: mega columns become inline <code><details></code> accordions (keyboard + touch operable). Toggle a caret item to expand.</p>
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scheduled Cloudflare Worker entry for `dt-prune-and-rollup`.
|
|
3
|
+
*
|
|
4
|
+
* A SEPARATE, DEDICATED Worker script from the Astro-adapted storefront
|
|
5
|
+
* Worker — deliberately, not a `scheduled` export bolted onto the Astro
|
|
6
|
+
* entry. The @astrojs/cloudflare adapter OWNS `dist/server/entry.mjs` and
|
|
7
|
+
* bakes its target environment in at BUILD time via `CLOUDFLARE_ENV`
|
|
8
|
+
* (`wrangler.toml`'s own "DEPLOY FOOTGUN" comment); the documented way to add
|
|
9
|
+
* a non-`fetch` handler there is a custom entrypoint that re-imports the
|
|
10
|
+
* adapter's generated `handle()`, which would make this cron trigger's
|
|
11
|
+
* correctness depend on the SAME footgun-prone build/deploy sequencing as
|
|
12
|
+
* the whole storefront. This module instead has nothing to do with Astro at
|
|
13
|
+
* all — it is a plain `ExportedHandler` deployed under its own Worker name
|
|
14
|
+
* (`wrangler.prune-rollup.toml`, `[triggers]` below), reusing the tracking
|
|
15
|
+
* library's already-built, framework-agnostic pieces
|
|
16
|
+
* (`lib/tracking/pruneRollup.ts`, `d1Control.ts`, `interactionStore.ts`).
|
|
17
|
+
* There is no other scheduled-Worker precedent in this repo to follow; this
|
|
18
|
+
* is the first one, chosen to keep the footgun-prone Astro deploy path
|
|
19
|
+
* completely untouched.
|
|
20
|
+
*
|
|
21
|
+
* BINDINGS (declared in `wrangler.prune-rollup.toml`, not this file):
|
|
22
|
+
* - `STOREFRONT_APPS_DB` — the SAME shared D1 database the main storefront
|
|
23
|
+
* Worker binds, for the `tracking_tenant_databases` directory.
|
|
24
|
+
* - `INTERACTION_ANALYTICS` — the SAME Analytics Engine dataset the main
|
|
25
|
+
* Worker writes rollups to.
|
|
26
|
+
* - `CF_ACCOUNT_ID` / `TRACKING_D1_API_TOKEN` (secret) — D1 REST
|
|
27
|
+
* credentials for reaching each tenant's OWN database (Worker bindings
|
|
28
|
+
* are static; a per-tenant database can only be addressed over the D1
|
|
29
|
+
* HTTP API, same reasoning as `d1Control.ts`'s module doc).
|
|
30
|
+
* - `TRACKING_D1_NAME_PREFIX` (optional) — unused by this worker directly,
|
|
31
|
+
* kept only for parity/documentation; the prune sweep addresses tenants
|
|
32
|
+
* by `database_uuid` from the directory, never by re-deriving the name.
|
|
33
|
+
*/
|
|
34
|
+
import type { Queryable } from "../lib/d1/catalog.js";
|
|
35
|
+
import type { D1Like } from "../lib/d1/catalog.js";
|
|
36
|
+
import { fromD1 } from "../lib/d1/catalog.js";
|
|
37
|
+
import type { AnalyticsEngineLike } from "../lib/activity/store.js";
|
|
38
|
+
import type { D1ControlConfig } from "../lib/tracking/d1Control.js";
|
|
39
|
+
import { runPruneSweep, type PruneSweepResult } from "../lib/tracking/pruneRollup.js";
|
|
40
|
+
|
|
41
|
+
/** The env bindings this Worker needs — a local, minimal slice (this repo
|
|
42
|
+
* does not take a `@cloudflare/workers-types` dependency; see `d1Control.ts`
|
|
43
|
+
* and `activity/store.ts` for the same convention). */
|
|
44
|
+
export interface PruneRollupEnv {
|
|
45
|
+
STOREFRONT_APPS_DB: D1Like;
|
|
46
|
+
INTERACTION_ANALYTICS?: AnalyticsEngineLike;
|
|
47
|
+
CF_ACCOUNT_ID?: string;
|
|
48
|
+
TRACKING_D1_API_TOKEN?: string;
|
|
49
|
+
/** Tenants processed per invocation; overridable for a slower/faster cron. */
|
|
50
|
+
PRUNE_BATCH_SIZE?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** The minimal slice of Cloudflare's `ScheduledController` this handler uses. */
|
|
54
|
+
export interface ScheduledControllerLike {
|
|
55
|
+
scheduledTime: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The minimal slice of Cloudflare's `ExecutionContext` this handler uses. */
|
|
59
|
+
export interface ExecutionContextLike {
|
|
60
|
+
waitUntil(promise: Promise<unknown>): void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Build the D1 REST control config from env, or undefined when unconfigured
|
|
64
|
+
* (mirrors `d1Control.ts`'s `readD1Control()` "skip, never fail" posture). */
|
|
65
|
+
function controlFromEnv(env: PruneRollupEnv): D1ControlConfig | undefined {
|
|
66
|
+
if (!env.CF_ACCOUNT_ID || !env.TRACKING_D1_API_TOKEN) return undefined;
|
|
67
|
+
return { accountId: env.CF_ACCOUNT_ID, apiToken: env.TRACKING_D1_API_TOKEN };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The pure orchestration the `scheduled` export below delegates to — kept
|
|
72
|
+
* separate so it is callable/testable without a real `ScheduledController`.
|
|
73
|
+
* Never throws: a misconfigured environment (no D1 REST credentials) logs
|
|
74
|
+
* and no-ops rather than failing the cron invocation.
|
|
75
|
+
*/
|
|
76
|
+
export async function handleScheduledPrune(
|
|
77
|
+
env: PruneRollupEnv,
|
|
78
|
+
now: string = new Date().toISOString(),
|
|
79
|
+
): Promise<PruneSweepResult | undefined> {
|
|
80
|
+
const control = controlFromEnv(env);
|
|
81
|
+
if (!control) {
|
|
82
|
+
console.error(
|
|
83
|
+
"[tracking:prune] skipped — CF_ACCOUNT_ID/TRACKING_D1_API_TOKEN not configured",
|
|
84
|
+
);
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const apps: Queryable = fromD1(env.STOREFRONT_APPS_DB);
|
|
89
|
+
const batchSize = env.PRUNE_BATCH_SIZE ? Number.parseInt(env.PRUNE_BATCH_SIZE, 10) : undefined;
|
|
90
|
+
|
|
91
|
+
const result = await runPruneSweep({
|
|
92
|
+
apps,
|
|
93
|
+
control,
|
|
94
|
+
analytics: env.INTERACTION_ANALYTICS,
|
|
95
|
+
now,
|
|
96
|
+
...(batchSize && Number.isFinite(batchSize) ? { batchSize } : {}),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
console.info(
|
|
100
|
+
`[tracking:prune] swept ${result.tenantsSwept} tenant(s), pruned ${result.totalPrunedRows} row(s), ` +
|
|
101
|
+
`wrote ${result.totalAggregatesWritten} aggregate(s)`,
|
|
102
|
+
);
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export default {
|
|
107
|
+
async scheduled(
|
|
108
|
+
controller: ScheduledControllerLike,
|
|
109
|
+
env: PruneRollupEnv,
|
|
110
|
+
ctx: ExecutionContextLike,
|
|
111
|
+
): Promise<void> {
|
|
112
|
+
ctx.waitUntil(
|
|
113
|
+
handleScheduledPrune(env, new Date(controller.scheduledTime).toISOString()).catch((err) => {
|
|
114
|
+
console.error(
|
|
115
|
+
`[tracking:prune] sweep threw: ${err instanceof Error ? err.message : String(err)}`,
|
|
116
|
+
);
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
},
|
|
120
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
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,43 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
+
basedir_win="$basedir"
|
|
4
|
+
exe=""
|
|
5
|
+
msys=""
|
|
6
|
+
|
|
7
|
+
case `uname -a` in
|
|
8
|
+
*CYGWIN*|*MINGW*|*MSYS*)
|
|
9
|
+
if command -v cygpath > /dev/null 2>&1; then
|
|
10
|
+
basedir_win=`cygpath -w "$basedir"`
|
|
11
|
+
fi
|
|
12
|
+
exe=".exe"
|
|
13
|
+
msys="true"
|
|
14
|
+
;;
|
|
15
|
+
*WSL2*)
|
|
16
|
+
if command -v wslpath > /dev/null 2>&1; then
|
|
17
|
+
basedir_win="$(wslpath -w "$basedir" 2> /dev/null)"
|
|
18
|
+
if [ $? -ne 0 ] || [ -z "$basedir_win" ]; then
|
|
19
|
+
basedir_win="$basedir"
|
|
20
|
+
else
|
|
21
|
+
exe=".exe"
|
|
22
|
+
fi
|
|
23
|
+
fi
|
|
24
|
+
;;
|
|
25
|
+
esac
|
|
26
|
+
|
|
27
|
+
if [ -z "$NODE_PATH" ]; then
|
|
28
|
+
export NODE_PATH="/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/typescript@5.9.3/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/node_modules"
|
|
29
|
+
else
|
|
30
|
+
export NODE_PATH="/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/typescript@5.9.3/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
31
|
+
fi
|
|
32
|
+
if [ -n "$exe" ] && [ -x "$basedir/node.exe" ]; then
|
|
33
|
+
exec "$basedir/node.exe" "$basedir_win/../typescript/bin/tsc" "$@"
|
|
34
|
+
elif [ -x "$basedir/node" ]; then
|
|
35
|
+
exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
|
|
36
|
+
elif command -v node >/dev/null 2>&1; then
|
|
37
|
+
exec node "$basedir/../typescript/bin/tsc" "$@"
|
|
38
|
+
elif [ -n "$exe" ] && command -v node.exe >/dev/null 2>&1; then
|
|
39
|
+
exec node.exe "$basedir_win/../typescript/bin/tsc" "$@"
|
|
40
|
+
else
|
|
41
|
+
exec node "$basedir/../typescript/bin/tsc" "$@"
|
|
42
|
+
fi
|
|
43
|
+
# cmd-shim-target=/home/runner/work/storefront/storefront/storefront-runner-dist/packages/public-runtime/node_modules/typescript/bin/tsc
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
+
basedir_win="$basedir"
|
|
4
|
+
exe=""
|
|
5
|
+
msys=""
|
|
6
|
+
|
|
7
|
+
case `uname -a` in
|
|
8
|
+
*CYGWIN*|*MINGW*|*MSYS*)
|
|
9
|
+
if command -v cygpath > /dev/null 2>&1; then
|
|
10
|
+
basedir_win=`cygpath -w "$basedir"`
|
|
11
|
+
fi
|
|
12
|
+
exe=".exe"
|
|
13
|
+
msys="true"
|
|
14
|
+
;;
|
|
15
|
+
*WSL2*)
|
|
16
|
+
if command -v wslpath > /dev/null 2>&1; then
|
|
17
|
+
basedir_win="$(wslpath -w "$basedir" 2> /dev/null)"
|
|
18
|
+
if [ $? -ne 0 ] || [ -z "$basedir_win" ]; then
|
|
19
|
+
basedir_win="$basedir"
|
|
20
|
+
else
|
|
21
|
+
exe=".exe"
|
|
22
|
+
fi
|
|
23
|
+
fi
|
|
24
|
+
;;
|
|
25
|
+
esac
|
|
26
|
+
|
|
27
|
+
if [ -z "$NODE_PATH" ]; then
|
|
28
|
+
export NODE_PATH="/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/typescript@5.9.3/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/node_modules"
|
|
29
|
+
else
|
|
30
|
+
export NODE_PATH="/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/typescript@5.9.3/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
31
|
+
fi
|
|
32
|
+
if [ -n "$exe" ] && [ -x "$basedir/node.exe" ]; then
|
|
33
|
+
exec "$basedir/node.exe" "$basedir_win/../typescript/bin/tsserver" "$@"
|
|
34
|
+
elif [ -x "$basedir/node" ]; then
|
|
35
|
+
exec "$basedir/node" "$basedir/../typescript/bin/tsserver" "$@"
|
|
36
|
+
elif command -v node >/dev/null 2>&1; then
|
|
37
|
+
exec node "$basedir/../typescript/bin/tsserver" "$@"
|
|
38
|
+
elif [ -n "$exe" ] && command -v node.exe >/dev/null 2>&1; then
|
|
39
|
+
exec node.exe "$basedir_win/../typescript/bin/tsserver" "$@"
|
|
40
|
+
else
|
|
41
|
+
exec node "$basedir/../typescript/bin/tsserver" "$@"
|
|
42
|
+
fi
|
|
43
|
+
# cmd-shim-target=/home/runner/work/storefront/storefront/storefront-runner-dist/packages/public-runtime/node_modules/typescript/bin/tsserver
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
+
basedir_win="$basedir"
|
|
4
|
+
exe=""
|
|
5
|
+
msys=""
|
|
6
|
+
|
|
7
|
+
case `uname -a` in
|
|
8
|
+
*CYGWIN*|*MINGW*|*MSYS*)
|
|
9
|
+
if command -v cygpath > /dev/null 2>&1; then
|
|
10
|
+
basedir_win=`cygpath -w "$basedir"`
|
|
11
|
+
fi
|
|
12
|
+
exe=".exe"
|
|
13
|
+
msys="true"
|
|
14
|
+
;;
|
|
15
|
+
*WSL2*)
|
|
16
|
+
if command -v wslpath > /dev/null 2>&1; then
|
|
17
|
+
basedir_win="$(wslpath -w "$basedir" 2> /dev/null)"
|
|
18
|
+
if [ $? -ne 0 ] || [ -z "$basedir_win" ]; then
|
|
19
|
+
basedir_win="$basedir"
|
|
20
|
+
else
|
|
21
|
+
exe=".exe"
|
|
22
|
+
fi
|
|
23
|
+
fi
|
|
24
|
+
;;
|
|
25
|
+
esac
|
|
26
|
+
|
|
27
|
+
if [ -z "$NODE_PATH" ]; then
|
|
28
|
+
export NODE_PATH="/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/vitest@2.1.8_@types+node@26.4.1_happy-dom@20.13.2_lightningcss@1.33.0/node_modules/vitest/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/vitest@2.1.8_@types+node@26.4.1_happy-dom@20.13.2_lightningcss@1.33.0/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/node_modules"
|
|
29
|
+
else
|
|
30
|
+
export NODE_PATH="/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/vitest@2.1.8_@types+node@26.4.1_happy-dom@20.13.2_lightningcss@1.33.0/node_modules/vitest/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/vitest@2.1.8_@types+node@26.4.1_happy-dom@20.13.2_lightningcss@1.33.0/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
31
|
+
fi
|
|
32
|
+
if [ -n "$exe" ] && [ -x "$basedir/node.exe" ]; then
|
|
33
|
+
exec "$basedir/node.exe" "$basedir_win/../vitest/vitest.mjs" "$@"
|
|
34
|
+
elif [ -x "$basedir/node" ]; then
|
|
35
|
+
exec "$basedir/node" "$basedir/../vitest/vitest.mjs" "$@"
|
|
36
|
+
elif command -v node >/dev/null 2>&1; then
|
|
37
|
+
exec node "$basedir/../vitest/vitest.mjs" "$@"
|
|
38
|
+
elif [ -n "$exe" ] && command -v node.exe >/dev/null 2>&1; then
|
|
39
|
+
exec node.exe "$basedir_win/../vitest/vitest.mjs" "$@"
|
|
40
|
+
else
|
|
41
|
+
exec node "$basedir/../vitest/vitest.mjs" "$@"
|
|
42
|
+
fi
|
|
43
|
+
# cmd-shim-target=/home/runner/work/storefront/storefront/storefront-runner-dist/packages/public-runtime/node_modules/vitest/vitest.mjs
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* `parseConfig.ts` re-export from here so every existing renderer import
|
|
29
29
|
* keeps resolving unchanged.
|
|
30
30
|
*/
|
|
31
|
+
import { validateTracking, type TrackingDeclaration } from "./interaction-tracking.js";
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* A navigation/footer/CTA link. `id` is a STABLE identifier, independent of
|
|
@@ -35,14 +36,20 @@
|
|
|
35
36
|
* registry (spec: no runtime registry, no DOM observer; just a schema-
|
|
36
37
|
* validated identifier + a real `href` fallback, always present on every
|
|
37
38
|
* actionable item). `external` opens off-site; `badge` shows e.g. "NEW".
|
|
39
|
+
*
|
|
40
|
+
* Every actionable item also carries the declarative-interaction-tracking
|
|
41
|
+
* declaration ({@link TrackingDeclaration}): a stable `action` naming the
|
|
42
|
+
* INTENT, or `tracking: "none"` to stamp nothing at all. `id` (identity) and
|
|
43
|
+
* `action` (intent) are different axes and are never derived from each other —
|
|
44
|
+
* see docs/architecture/interaction-tracking-attribute-contract.md.
|
|
38
45
|
*/
|
|
39
|
-
export
|
|
46
|
+
export type ChromeLink = {
|
|
40
47
|
id: string;
|
|
41
48
|
label: string;
|
|
42
49
|
href: string;
|
|
43
50
|
external?: boolean;
|
|
44
51
|
badge?: string;
|
|
45
|
-
}
|
|
52
|
+
} & TrackingDeclaration;
|
|
46
53
|
|
|
47
54
|
/** One column of a mega-menu panel. */
|
|
48
55
|
export interface MegaColumn {
|
|
@@ -56,7 +63,7 @@ export interface MegaColumn {
|
|
|
56
63
|
* marks it (e.g. "NEW"); `flag` gives it the emphasized/highlighted
|
|
57
64
|
* treatment. See {@link ChromeLink} for `id`.
|
|
58
65
|
*/
|
|
59
|
-
export
|
|
66
|
+
export type ChromeNavItem = {
|
|
60
67
|
id: string;
|
|
61
68
|
label: string;
|
|
62
69
|
href: string;
|
|
@@ -64,7 +71,7 @@ export interface ChromeNavItem {
|
|
|
64
71
|
flag?: boolean;
|
|
65
72
|
children?: ChromeLink[];
|
|
66
73
|
columns?: MegaColumn[];
|
|
67
|
-
}
|
|
74
|
+
} & TrackingDeclaration;
|
|
68
75
|
|
|
69
76
|
export interface SocialLink {
|
|
70
77
|
label: string;
|
|
@@ -81,16 +88,16 @@ export interface ChromeLogo {
|
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
/** A marketing conversion CTA (header-actions / footer, non-commerce tenants). */
|
|
84
|
-
export
|
|
91
|
+
export type ChromeCta = {
|
|
85
92
|
id: string;
|
|
86
93
|
label: string;
|
|
87
94
|
href: string;
|
|
88
95
|
variant?: "primary" | "secondary" | "ghost";
|
|
89
|
-
}
|
|
96
|
+
} & TrackingDeclaration;
|
|
90
97
|
|
|
91
98
|
/** A stable link-with-a-name-and-href, minus badge/external — used for the
|
|
92
99
|
* small number of single-purpose action links (e.g. a member-price cue). */
|
|
93
|
-
export type ChromeActionLink =
|
|
100
|
+
export type ChromeActionLink = { id: string; label: string; href: string } & TrackingDeclaration;
|
|
94
101
|
|
|
95
102
|
export interface FooterColumn {
|
|
96
103
|
title: string;
|
|
@@ -371,6 +378,7 @@ function validateLink(v: unknown, path: string, errors: string[]): v is ChromeLi
|
|
|
371
378
|
if (!isNonEmptyString(v.href)) errors.push(`${path}.href: required, non-empty string`);
|
|
372
379
|
checkOptionalBoolean(v.external, `${path}.external`, errors);
|
|
373
380
|
checkOptionalString(v.badge, `${path}.badge`, errors);
|
|
381
|
+
validateTracking(v, path, errors);
|
|
374
382
|
return errors.length === before;
|
|
375
383
|
}
|
|
376
384
|
|
|
@@ -384,6 +392,7 @@ function validateActionLink(v: unknown, path: string, errors: string[]): boolean
|
|
|
384
392
|
if (!isNonEmptyString(v.id)) errors.push(`${path}.id: required, non-empty string`);
|
|
385
393
|
if (!isNonEmptyString(v.label)) errors.push(`${path}.label: required, non-empty string`);
|
|
386
394
|
if (!isNonEmptyString(v.href)) errors.push(`${path}.href: required, non-empty string`);
|
|
395
|
+
validateTracking(v, path, errors);
|
|
387
396
|
return errors.length === before;
|
|
388
397
|
}
|
|
389
398
|
|
|
@@ -415,6 +424,7 @@ function validateNavItem(v: unknown, path: string, errors: string[]): v is Chrom
|
|
|
415
424
|
if (!isNonEmptyString(v.href)) errors.push(`${path}.href: required, non-empty string`);
|
|
416
425
|
checkOptionalString(v.badge, `${path}.badge`, errors);
|
|
417
426
|
checkOptionalBoolean(v.flag, `${path}.flag`, errors);
|
|
427
|
+
validateTracking(v, path, errors);
|
|
418
428
|
if (v.children !== undefined) {
|
|
419
429
|
if (!Array.isArray(v.children)) errors.push(`${path}.children: must be an array`);
|
|
420
430
|
else v.children.forEach((c, i) => validateLink(c, `${path}.children[${i}]`, errors));
|
|
@@ -438,6 +448,7 @@ function validateCta(v: unknown, path: string, errors: string[]): v is ChromeCta
|
|
|
438
448
|
if (v.variant !== undefined && !["primary", "secondary", "ghost"].includes(v.variant as string)) {
|
|
439
449
|
errors.push(`${path}.variant: must be one of primary, secondary, ghost`);
|
|
440
450
|
}
|
|
451
|
+
validateTracking(v, path, errors);
|
|
441
452
|
return errors.length === before;
|
|
442
453
|
}
|
|
443
454
|
|
|
@@ -33,6 +33,16 @@ export * from "./customization-versioning.js";
|
|
|
33
33
|
export * from "./customization-scripts.js";
|
|
34
34
|
export * from "./customization-reconcile.js";
|
|
35
35
|
export * from "./chrome.js";
|
|
36
|
+
// Declarative interaction tracking: the data-tot-el / -action / -placement
|
|
37
|
+
// attribute contract, its single emit path, and the ONE delegated click
|
|
38
|
+
// listener both document mounts install. Reads declared attributes only —
|
|
39
|
+
// never rendered content, never form values.
|
|
40
|
+
// See docs/architecture/interaction-tracking-attribute-contract.md.
|
|
41
|
+
export * from "./interaction-tracking.js";
|
|
42
|
+
// `data-tot-el` id derivation: the authoring-time engine that proposes a
|
|
43
|
+
// stable, human-readable IDENTITY from fragment STRUCTURE alone. A proposal
|
|
44
|
+
// engine only — never a runtime fallback, and it derives no `action`.
|
|
45
|
+
export * from "./tracking-id-derivation.js";
|
|
36
46
|
export * from "./candidate-index.js";
|
|
37
47
|
export * from "./extension-contract.js";
|
|
38
48
|
export * from "./integration-capabilities.js";
|
|
@@ -65,6 +65,7 @@ export * from "./consent-broker.js";
|
|
|
65
65
|
export * from "./consent-cmp-demand.js";
|
|
66
66
|
export * from "./consent-config.js";
|
|
67
67
|
export * from "./consent-preview.js";
|
|
68
|
+
export * from "./interaction-tracking-gate.js";
|
|
68
69
|
export * from "./candidate-evidence.js";
|
|
69
70
|
export * from "./lifecycle.js";
|
|
70
71
|
export * from "./lifecycle-runtime.js";
|