@fayz-ai/storefront 0.82.0 → 0.83.0

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.
@@ -0,0 +1,153 @@
1
+ // src/experiments/assign.ts
2
+ var CONTROL = "control";
3
+ var EXPERIMENT_HEADER = "x-fayz-experiments";
4
+ var VISITOR_COOKIE = "fz_vid";
5
+ var NO_ASSIGNMENTS = Object.freeze({ flags: {} });
6
+ var FLAG_KEY = /^[A-Za-z0-9_-]{1,100}$/;
7
+ function validateExperiments(experiments) {
8
+ const issues = [];
9
+ for (const [key, experiment] of Object.entries(experiments ?? {})) {
10
+ const at = `experiments.${key}`;
11
+ if (!FLAG_KEY.test(key)) issues.push(`${at}: the key must be a PostHog flag key (letters, digits, - and _)`);
12
+ const variants = experiment?.variants ?? [];
13
+ if (!variants.includes(CONTROL)) issues.push(`${at}: variants must include "${CONTROL}"`);
14
+ if (variants.length < 2) issues.push(`${at}: an experiment needs at least two variants`);
15
+ if (new Set(variants).size !== variants.length) issues.push(`${at}: variants repeat`);
16
+ for (const variant of variants) {
17
+ if (!FLAG_KEY.test(variant)) issues.push(`${at}: variant "${variant}" is not a valid variant key`);
18
+ }
19
+ const status = experiment?.status;
20
+ if (status !== void 0 && status !== "running" && status !== "off") {
21
+ issues.push(`${at}.status: must be "running" or "off"`);
22
+ }
23
+ if (experiment?.split) {
24
+ const keys = Object.keys(experiment.split);
25
+ const missing = variants.filter((v) => !keys.includes(v));
26
+ const extra = keys.filter((k) => !variants.includes(k));
27
+ if (missing.length || extra.length) {
28
+ issues.push(`${at}.split: keys must be exactly the variants (missing: ${missing.join(", ") || "\u2014"}; unknown: ${extra.join(", ") || "\u2014"})`);
29
+ }
30
+ const values = Object.values(experiment.split);
31
+ if (values.some((n) => typeof n !== "number" || !Number.isFinite(n) || n < 0 || n > 100)) {
32
+ issues.push(`${at}.split: each share is a percentage from 0 to 100`);
33
+ } else if (Math.abs(values.reduce((a, b) => a + b, 0) - 100) > 1e-9) {
34
+ issues.push(`${at}.split: shares must sum to 100`);
35
+ }
36
+ }
37
+ const rollout = experiment?.rollout;
38
+ if (rollout !== void 0 && (typeof rollout !== "number" || !Number.isFinite(rollout) || rollout < 0 || rollout > 100)) {
39
+ issues.push(`${at}.rollout: a percentage from 0 to 100`);
40
+ }
41
+ }
42
+ return issues;
43
+ }
44
+ var BOT_UA = /bot\b|crawler|spider|crawling|slurp|bingpreview|facebookexternalhit|facebot|meta-externalagent|whatsapp|telegrambot|twitterbot|linkedinbot|discordbot|slackbot|embedly|pinterest|google-inspectiontool|googleother|lighthouse|headlesschrome|pagespeed|uptime|pingdom|curl\/|wget\/|python-requests|node-fetch|axios\//i;
45
+ function isBotUserAgent(userAgent) {
46
+ if (!userAgent) return true;
47
+ return BOT_UA.test(userAgent);
48
+ }
49
+ function readCookie(header, name) {
50
+ if (!header) return void 0;
51
+ for (const part of header.split(";")) {
52
+ const eq = part.indexOf("=");
53
+ if (eq < 0) continue;
54
+ if (part.slice(0, eq).trim() === name) return part.slice(eq + 1).trim();
55
+ }
56
+ return void 0;
57
+ }
58
+ function readVisitorId(cookieHeader, posthogKey) {
59
+ const own = readPinnedVisitorId(cookieHeader);
60
+ if (own) return own;
61
+ const posthog = readCookie(cookieHeader, `ph_${posthogKey}_posthog`);
62
+ if (posthog) {
63
+ try {
64
+ const id = JSON.parse(decodeURIComponent(posthog)).distinct_id;
65
+ if (typeof id === "string" && id) return id;
66
+ } catch {
67
+ }
68
+ }
69
+ return void 0;
70
+ }
71
+ var PINNABLE_ID = /^[A-Za-z0-9-]{8,64}$/;
72
+ function readPinnedVisitorId(cookieHeader) {
73
+ const own = readCookie(cookieHeader, VISITOR_COOKIE);
74
+ return own && PINNABLE_ID.test(own) ? own : void 0;
75
+ }
76
+ function isPinnableVisitorId(id) {
77
+ return PINNABLE_ID.test(id);
78
+ }
79
+ function visitorCookie(id, secure = true) {
80
+ return `${VISITOR_COOKIE}=${id}; Path=/; Max-Age=31536000; SameSite=Lax${secure ? "; Secure" : ""}`;
81
+ }
82
+ var LONG_SCALE = 1152921504606847e3;
83
+ async function sha1Hex(text) {
84
+ const digest = await crypto.subtle.digest("SHA-1", new TextEncoder().encode(text));
85
+ return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join("");
86
+ }
87
+ async function posthogHash(key, distinctId, salt = "") {
88
+ const hex = await sha1Hex(`${key}.${distinctId}${salt}`);
89
+ return parseInt(hex.slice(0, 15), 16) / LONG_SCALE;
90
+ }
91
+ async function inPosthogRollout(key, distinctId, rolloutPercentage) {
92
+ return !(await posthogHash(key, distinctId) > rolloutPercentage / 100);
93
+ }
94
+ async function posthogVariant(key, distinctId, variants) {
95
+ const hash = await posthogHash(key, distinctId, "variant");
96
+ let min = 0;
97
+ for (const variant of variants) {
98
+ const max = min + variant.rollout_percentage / 100;
99
+ if (hash >= min && hash < max) return variant.key;
100
+ min = max;
101
+ }
102
+ return void 0;
103
+ }
104
+ function experimentVariantTable(experiment) {
105
+ const variants = experiment.variants;
106
+ if (experiment.split) return variants.map((key) => ({ key, rollout_percentage: experiment.split[key] ?? 0 }));
107
+ const share = Math.floor(100 / variants.length);
108
+ return variants.map((key, i) => ({ key, rollout_percentage: share + (i === 0 ? 100 - share * variants.length : 0) }));
109
+ }
110
+ function runningExperiments(experiments) {
111
+ return Object.fromEntries(Object.entries(experiments ?? {}).filter(([, e]) => e.status === "running"));
112
+ }
113
+ async function assignExperiments(input) {
114
+ const flags = {};
115
+ for (const [key, experiment] of Object.entries(runningExperiments(input.experiments))) {
116
+ if (!await inPosthogRollout(key, input.distinctId, experiment.rollout ?? 100)) continue;
117
+ const variant = await posthogVariant(key, input.distinctId, experimentVariantTable(experiment));
118
+ if (variant && experiment.variants.includes(variant)) flags[key] = { variant };
119
+ }
120
+ return { distinctId: input.distinctId, flags };
121
+ }
122
+ function variantOf(assignments, key) {
123
+ return assignments?.flags[key]?.variant ?? CONTROL;
124
+ }
125
+ function experimentCacheSuffix(assignments, keys) {
126
+ return [...keys].sort().map((key) => [key, variantOf(assignments, key)]).filter(([, variant]) => variant !== CONTROL).map(([key, variant]) => `${key}:${variant}`).join(",");
127
+ }
128
+ function encodeExperimentHeader(assignments) {
129
+ return encodeURIComponent(JSON.stringify(assignments));
130
+ }
131
+ function decodeExperimentHeader(value) {
132
+ if (!value) return null;
133
+ try {
134
+ const parsed = JSON.parse(decodeURIComponent(value));
135
+ if (!parsed || typeof parsed !== "object" || typeof parsed.flags !== "object" || parsed.flags === null) return null;
136
+ const flags = {};
137
+ for (const [key, a] of Object.entries(parsed.flags)) {
138
+ if (a && typeof a === "object" && typeof a.variant === "string") {
139
+ flags[key] = a;
140
+ }
141
+ }
142
+ return {
143
+ ...typeof parsed.distinctId === "string" && parsed.distinctId ? { distinctId: parsed.distinctId } : {},
144
+ flags
145
+ };
146
+ } catch {
147
+ return null;
148
+ }
149
+ }
150
+
151
+ export { CONTROL, EXPERIMENT_HEADER, NO_ASSIGNMENTS, VISITOR_COOKIE, assignExperiments, decodeExperimentHeader, encodeExperimentHeader, experimentCacheSuffix, experimentVariantTable, inPosthogRollout, isBotUserAgent, isPinnableVisitorId, posthogHash, posthogVariant, readPinnedVisitorId, readVisitorId, runningExperiments, validateExperiments, visitorCookie };
152
+ //# sourceMappingURL=chunk-4REZOGGW.js.map
153
+ //# sourceMappingURL=chunk-4REZOGGW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/experiments/assign.ts"],"names":[],"mappings":"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","file":"chunk-4REZOGGW.js","sourcesContent":["// ---------------------------------------------------------------------------\n// Experiment assignment, decided before the page renders — locally.\n//\n// The variant is computed at the edge from a stable anonymous id with PostHog's\n// own bucketing (sha1 of `<flag>.<id>`, the same rollout and variant tables),\n// so the result equals what PostHog's flag answers for that id while making no\n// network call: a new visitor no longer waits on a flags round trip before the\n// first byte. The store config is the source of truth for who sees what; the\n// PostHog Experiment over the same flag key is where the results are read.\n//\n// Every visitor who is not in a running experiment gets 'control' and is not\n// counted: experiments that are off, crawlers, visitors outside the rollout.\n// ---------------------------------------------------------------------------\n\n/** The variant every visitor gets when nothing else can be decided. */\nexport const CONTROL = 'control'\n\n/** Request header the edge hands the assignment to the renderer in. */\nexport const EXPERIMENT_HEADER = 'x-fayz-experiments'\n\n/** First-party cookie holding the anonymous id when PostHog has not set one yet. */\nexport const VISITOR_COOKIE = 'fz_vid'\n\n/** One experiment the store runs. */\nexport interface StorefrontExperiment {\n /**\n * The variant keys, in the order the PostHog flag lists them (the order\n * buckets are laid out in). Must include 'control'.\n */\n variants: readonly string[]\n /**\n * 'running' assigns visitors; 'off' (the default) shows everyone control and\n * counts no one. Launching or stopping a test is flipping this and deploying.\n */\n status?: 'running' | 'off'\n /** Percent of visitors per variant, summing to 100. Default: even, remainder to the first. */\n split?: Readonly<Record<string, number>>\n /** Percent of visitors in the experiment at all (the rest see control, uncounted). Default 100. */\n rollout?: number\n /** What is being tested and why — for whoever reads the config next. */\n description?: string\n}\n\n/** What a visitor sees for one experiment. */\nexport interface ExperimentAssignment {\n variant: string\n /** Reserved for per-variant data. Local assignment does not set it. */\n payload?: unknown\n}\n\n/** Everything the edge decided for one request. */\nexport interface ExperimentAssignments {\n /** The id the variants were computed for. Kept at the edge: it never\n * travels into the rendered (and cached) page — see `withAssignment`. */\n distinctId?: string\n /**\n * By flag key, ONLY the experiments this visitor is in — 'control' included\n * when bucketed into control. A declared key missing here renders control\n * but reports no exposure: the visitor is outside the experiment.\n */\n flags: Record<string, ExperimentAssignment>\n}\n\n/** The empty decision: every experiment at control. */\nexport const NO_ASSIGNMENTS: ExperimentAssignments = Object.freeze({ flags: {} }) as ExperimentAssignments\n\nconst FLAG_KEY = /^[A-Za-z0-9_-]{1,100}$/\n\n/**\n * Problems with a store's experiment declarations, empty when valid. Checked\n * when the host is created, so a typo fails the build rather than silently\n * running a page with no variants.\n */\nexport function validateExperiments(experiments: Record<string, StorefrontExperiment> | undefined): string[] {\n const issues: string[] = []\n for (const [key, experiment] of Object.entries(experiments ?? {})) {\n const at = `experiments.${key}`\n if (!FLAG_KEY.test(key)) issues.push(`${at}: the key must be a PostHog flag key (letters, digits, - and _)`)\n const variants = experiment?.variants ?? []\n if (!variants.includes(CONTROL)) issues.push(`${at}: variants must include \"${CONTROL}\"`)\n if (variants.length < 2) issues.push(`${at}: an experiment needs at least two variants`)\n if (new Set(variants).size !== variants.length) issues.push(`${at}: variants repeat`)\n for (const variant of variants) {\n if (!FLAG_KEY.test(variant)) issues.push(`${at}: variant \"${variant}\" is not a valid variant key`)\n }\n const status = experiment?.status\n if (status !== undefined && status !== 'running' && status !== 'off') {\n issues.push(`${at}.status: must be \"running\" or \"off\"`)\n }\n if (experiment?.split) {\n const keys = Object.keys(experiment.split)\n const missing = variants.filter((v) => !keys.includes(v))\n const extra = keys.filter((k) => !variants.includes(k))\n if (missing.length || extra.length) {\n issues.push(`${at}.split: keys must be exactly the variants (missing: ${missing.join(', ') || '—'}; unknown: ${extra.join(', ') || '—'})`)\n }\n const values = Object.values(experiment.split)\n if (values.some((n) => typeof n !== 'number' || !Number.isFinite(n) || n < 0 || n > 100)) {\n issues.push(`${at}.split: each share is a percentage from 0 to 100`)\n } else if (Math.abs(values.reduce((a, b) => a + b, 0) - 100) > 1e-9) {\n issues.push(`${at}.split: shares must sum to 100`)\n }\n }\n const rollout = experiment?.rollout\n if (rollout !== undefined && (typeof rollout !== 'number' || !Number.isFinite(rollout) || rollout < 0 || rollout > 100)) {\n issues.push(`${at}.rollout: a percentage from 0 to 100`)\n }\n }\n return issues\n}\n\n// Crawlers, link unfurlers and monitors. They always see control: what is\n// indexed and what a share card shows must not depend on a coin flip, and a\n// bot must not count as an exposure.\nconst BOT_UA =\n /bot\\b|crawler|spider|crawling|slurp|bingpreview|facebookexternalhit|facebot|meta-externalagent|whatsapp|telegrambot|twitterbot|linkedinbot|discordbot|slackbot|embedly|pinterest|google-inspectiontool|googleother|lighthouse|headlesschrome|pagespeed|uptime|pingdom|curl\\/|wget\\/|python-requests|node-fetch|axios\\//i\n\nexport function isBotUserAgent(userAgent: string | null | undefined): boolean {\n if (!userAgent) return true\n return BOT_UA.test(userAgent)\n}\n\nfunction readCookie(header: string | null | undefined, name: string): string | undefined {\n if (!header) return undefined\n for (const part of header.split(';')) {\n const eq = part.indexOf('=')\n if (eq < 0) continue\n if (part.slice(0, eq).trim() === name) return part.slice(eq + 1).trim()\n }\n return undefined\n}\n\n/**\n * The visitor's stable anonymous id, if the browser already has one:\n * posthog-js's own cookie first (so the edge evaluates flags for the very id\n * the browser reports events under), then the store's first-party cookie.\n */\nexport function readVisitorId(cookieHeader: string | null | undefined, posthogKey: string): string | undefined {\n // The store's own pinned id first: it never changes, while PostHog's\n // distinct_id does the moment a buyer identifies (e-mail at checkout) — and a\n // bucket computed from that would move a returning buyer to the other variant.\n const own = readPinnedVisitorId(cookieHeader)\n if (own) return own\n const posthog = readCookie(cookieHeader, `ph_${posthogKey}_posthog`)\n if (posthog) {\n try {\n const id = (JSON.parse(decodeURIComponent(posthog)) as { distinct_id?: unknown }).distinct_id\n if (typeof id === 'string' && id) return id\n } catch {\n /* unreadable cookie: no id */\n }\n }\n return undefined\n}\n\nconst PINNABLE_ID = /^[A-Za-z0-9-]{8,64}$/\n\n/** The `fz_vid` the edge pinned for this browser, if any. */\nexport function readPinnedVisitorId(cookieHeader: string | null | undefined): string | undefined {\n const own = readCookie(cookieHeader, VISITOR_COOKIE)\n return own && PINNABLE_ID.test(own) ? own : undefined\n}\n\n/** Whether an id may be stored in `fz_vid` (an anonymous id, never an e-mail). */\nexport function isPinnableVisitorId(id: string): boolean {\n return PINNABLE_ID.test(id)\n}\n\n/** `Set-Cookie` for a newly minted visitor id: a year, first-party, not readable cross-site. */\nexport function visitorCookie(id: string, secure = true): string {\n return `${VISITOR_COOKIE}=${id}; Path=/; Max-Age=31536000; SameSite=Lax${secure ? '; Secure' : ''}`\n}\n\n// --- PostHog's bucketing -----------------------------------------------------\n//\n// Exactly PostHog's local evaluation (@posthog/core featureFlagLocalEvaluation:\n// getFeatureFlagHash / getFeatureFlagVariantLookupTable; posthog-python's\n// `_hash` and `get_matching_variant`). Pinned against PostHog's own SDK test\n// vectors in test/experiments/posthog-bucketing.test.ts — change nothing here\n// without those passing, or the variant a shopper sees stops being the variant\n// PostHog counts them in.\n\n/** Larger than Number.MAX_SAFE_INTEGER on purpose: PostHog's rounding is part of the contract. */\n// oxlint-disable-next-line no-loss-of-precision\nconst LONG_SCALE = 0xfffffffffffffff\n\nasync function sha1Hex(text: string): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-1', new TextEncoder().encode(text))\n return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\n/** PostHog's bucket for `key` and `distinctId`, in [0, 1]. */\nexport async function posthogHash(key: string, distinctId: string, salt = ''): Promise<number> {\n const hex = await sha1Hex(`${key}.${distinctId}${salt}`)\n return parseInt(hex.slice(0, 15), 16) / LONG_SCALE\n}\n\n/** In the rollout when the bucket is at or below the percentage — PostHog's `>` check, inverted. */\nexport async function inPosthogRollout(key: string, distinctId: string, rolloutPercentage: number): Promise<boolean> {\n return !((await posthogHash(key, distinctId)) > rolloutPercentage / 100)\n}\n\n/** PostHog's variant for a bucketed visitor: the `[min, max)` range its 'variant' hash falls in. */\nexport async function posthogVariant(\n key: string,\n distinctId: string,\n variants: ReadonlyArray<{ key: string; rollout_percentage: number }>,\n): Promise<string | undefined> {\n const hash = await posthogHash(key, distinctId, 'variant')\n let min = 0\n for (const variant of variants) {\n const max = min + variant.rollout_percentage / 100\n if (hash >= min && hash < max) return variant.key\n min = max\n }\n return undefined\n}\n\n/** The split as PostHog's variant list: declared order, even by default (remainder to the first). */\nexport function experimentVariantTable(experiment: StorefrontExperiment): Array<{ key: string; rollout_percentage: number }> {\n const variants = experiment.variants\n if (experiment.split) return variants.map((key) => ({ key, rollout_percentage: experiment.split![key] ?? 0 }))\n const share = Math.floor(100 / variants.length)\n return variants.map((key, i) => ({ key, rollout_percentage: share + (i === 0 ? 100 - share * variants.length : 0) }))\n}\n\n/** Experiments that assign anyone. */\nexport function runningExperiments(experiments: Record<string, StorefrontExperiment> | undefined): Record<string, StorefrontExperiment> {\n return Object.fromEntries(Object.entries(experiments ?? {}).filter(([, e]) => e.status === 'running'))\n}\n\n/**\n * This visitor's variants, computed locally. Only running experiments the\n * visitor's bucket falls inside the rollout of appear in `flags`.\n */\nexport async function assignExperiments(input: {\n experiments: Record<string, StorefrontExperiment> | undefined\n distinctId: string\n}): Promise<ExperimentAssignments> {\n const flags: Record<string, ExperimentAssignment> = {}\n for (const [key, experiment] of Object.entries(runningExperiments(input.experiments))) {\n if (!(await inPosthogRollout(key, input.distinctId, experiment.rollout ?? 100))) continue\n const variant = await posthogVariant(key, input.distinctId, experimentVariantTable(experiment))\n if (variant && experiment.variants.includes(variant)) flags[key] = { variant }\n }\n return { distinctId: input.distinctId, flags }\n}\n\n/** The variant of `key`, control when unassigned. */\nexport function variantOf(assignments: ExperimentAssignments | null | undefined, key: string): string {\n return assignments?.flags[key]?.variant ?? CONTROL\n}\n\n/**\n * The part of the edge cache key that depends on the visitor: one entry per\n * combination of variants, in a fixed order. Control-only visitors (and every\n * crawler) share the plain key with the page as it was before any experiment.\n */\nexport function experimentCacheSuffix(assignments: ExperimentAssignments, keys: readonly string[]): string {\n return [...keys]\n .sort()\n .map((key) => [key, variantOf(assignments, key)] as const)\n .filter(([, variant]) => variant !== CONTROL)\n .map(([key, variant]) => `${key}:${variant}`)\n .join(',')\n}\n\n/** The assignment as a header value (URI-encoded JSON: payloads may carry any text). */\nexport function encodeExperimentHeader(assignments: ExperimentAssignments): string {\n return encodeURIComponent(JSON.stringify(assignments))\n}\n\n/** The renderer's side: never throws, garbage reads as no assignment. */\nexport function decodeExperimentHeader(value: string | null | undefined): ExperimentAssignments | null {\n if (!value) return null\n try {\n const parsed = JSON.parse(decodeURIComponent(value)) as Partial<ExperimentAssignments>\n if (!parsed || typeof parsed !== 'object' || typeof parsed.flags !== 'object' || parsed.flags === null) return null\n const flags: Record<string, ExperimentAssignment> = {}\n for (const [key, a] of Object.entries(parsed.flags)) {\n if (a && typeof a === 'object' && typeof (a as ExperimentAssignment).variant === 'string') {\n flags[key] = a as ExperimentAssignment\n }\n }\n return {\n ...(typeof parsed.distinctId === 'string' && parsed.distinctId ? { distinctId: parsed.distinctId } : {}),\n flags,\n }\n } catch {\n return null\n }\n}\n\n"]}
@@ -1,5 +1,5 @@
1
- import { Link, useStorefrontConfig, useStorefrontConfigOptional, useBlockData, BlockDataError, navigateTo, useRoutePath, toBlockNodes, defaultPageBlocks, PageDataScope, getStorefrontComponents, useLoader, currentStaticRenderPass, matchPath, translate, productSeo, StorefrontConfigProvider, mergePageTheme, pageThemeSelector, MENU_DATA_NEEDS, resolveConfig, useRouterAdapter, shellRouterAdapter, StorefrontRouterProvider, useScrollToTopOnNavigate, checkPageBlocks, normalizeLanguage, useHashPath, resolveStorefrontRoute, matchContentPage } from './chunk-UJHCVRK3.js';
2
- import { CONTROL, readVisitorId } from './chunk-746BEU7H.js';
1
+ import { Link, useStorefrontConfig, useStorefrontConfigOptional, useBlockData, BlockDataError, navigateTo, useRoutePath, toBlockNodes, defaultPageBlocks, PageDataScope, getStorefrontComponents, useLoader, currentStaticRenderPass, matchPath, translate, productSeo, StorefrontConfigProvider, mergePageTheme, pageThemeSelector, MENU_DATA_NEEDS, resolveConfig, useRouterAdapter, shellRouterAdapter, StorefrontRouterProvider, useScrollToTopOnNavigate, checkPageBlocks, normalizeLanguage, useHashPath, resolveStorefrontRoute, matchContentPage } from './chunk-7N5HKQBM.js';
2
+ import { runningExperiments, CONTROL, readVisitorId } from './chunk-4REZOGGW.js';
3
3
  import { create } from 'zustand';
4
4
  import { persist } from 'zustand/middleware';
5
5
  import { normalizePostalCode, getCountrySchema, lookupPostalCode, useBlockPath, BlockRenderer, BlockChildren, defineLoader, repairBlockTree, renderBlocks, clearExtensionPoints, defineExtensionPoint, runExtensionPoint, runEscapeValve, errorMessage, hasEntityDeclaration, resolveEntityDeclaration, defineBlock, formatPostalCode, countryName, defineEntity, defaultRouterAdapter, describeError, validateAddress } from '@fayz-ai/core';
@@ -550,7 +550,7 @@ function ExperimentProvider({ assignments, path, children }) {
550
550
  if (!kept.current && assignments) kept.current = assignments;
551
551
  const current = kept.current ?? { flags: {} };
552
552
  if (typeof window !== "undefined" && kept.current && !bootstrap) bootstrap = kept.current;
553
- const declaredKeys = Object.keys(config.experiments ?? {}).sort().join("\n");
553
+ const declaredKeys = Object.keys(runningExperiments(config.experiments)).sort().join("\n");
554
554
  const declared = React37__default.useMemo(() => new Set(declaredKeys ? declaredKeys.split("\n") : []), [declaredKeys]);
555
555
  const exposed = React37__default.useMemo(() => /* @__PURE__ */ new Set(), [path]);
556
556
  const value = React37__default.useMemo(() => ({ assignments: current, declared, exposed }), [current, declared, exposed]);
@@ -589,7 +589,7 @@ function ExperimentVariantBlock({ node }) {
589
589
  }
590
590
  var experimentBlockMeta = {
591
591
  label: "Experiment",
592
- description: 'A/B test: shows one of its variants to each visitor, as decided by the PostHog flag of the same key. The variant named "control" is the fallback.',
592
+ description: 'A/B test: shows one of its variants to each visitor, as bucketed at the edge for the declared experiment of the same key. The variant named "control" is the fallback.',
593
593
  settings: [
594
594
  {
595
595
  id: "experiment",
@@ -10040,6 +10040,8 @@ function StoreCover({ onShowHours, onOpenAccount }) {
10040
10040
  const compact = size === "compact";
10041
10041
  const modes = config.delivery?.fulfillmentModes ?? ["delivery"];
10042
10042
  const type = useDeliveryStore((s2) => s2.fulfillmentType);
10043
+ const etaInfo = type !== "pickup" && config.delivery?.etaMinutes ? etaParts(config.delivery.etaMinutes) : null;
10044
+ const etaText = etaInfo ? t(etaInfo.approx ? "delivery.eta" : "delivery.etaUpTo", { duration: etaInfo.text }) : "";
10043
10045
  const setFulfillmentType = useDeliveryStore((s2) => s2.setFulfillmentType);
10044
10046
  const refreshService = useServiceStore((s2) => s2.refresh);
10045
10047
  const service = useServiceStore();
@@ -10054,7 +10056,7 @@ function StoreCover({ onShowHours, onOpenAccount }) {
10054
10056
  {
10055
10057
  "data-testid": TID.storeCover,
10056
10058
  "data-cover-size": size,
10057
- className: `relative isolate bg-neutral-900 text-white${compact ? " max-h-40 overflow-hidden" : ""}`,
10059
+ className: "relative isolate bg-neutral-900 text-white",
10058
10060
  children: [
10059
10061
  cover && /* @__PURE__ */ jsx(
10060
10062
  "img",
@@ -10087,9 +10089,9 @@ function StoreCover({ onShowHours, onOpenAccount }) {
10087
10089
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 [text-shadow:0_1px_3px_rgb(0_0_0_/_0.7)]", children: [
10088
10090
  /* @__PURE__ */ jsx("div", { className: `grid ${compact ? "h-11 w-11" : "h-14 w-14"} shrink-0 place-items-center overflow-hidden rounded-xl bg-white/10 ring-1 ring-white/20`, children: logo ? /* @__PURE__ */ jsx("img", { src: logo, alt: "", "aria-hidden": true, className: "h-full w-full object-cover" }) : /* @__PURE__ */ jsx("span", { className: "text-xl font-bold", children: config.name.slice(0, 1) }) }),
10089
10091
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
10090
- /* @__PURE__ */ jsx("h1", { className: "truncate text-xl font-bold leading-tight", children: config.name }),
10091
- locality && /* @__PURE__ */ jsx("p", { className: "truncate text-sm text-white/70", children: locality }),
10092
- config.delivery?.paused ? /* @__PURE__ */ jsxs("p", { "data-testid": "store-delivery-paused", className: "mt-0.5 inline-flex items-center gap-1.5 truncate text-sm font-semibold text-amber-300", children: [
10092
+ /* @__PURE__ */ jsx("h1", { className: `truncate ${compact ? "text-lg" : "text-xl"} font-bold leading-tight`, children: config.name }),
10093
+ compact && !config.delivery?.paused ? locality || etaText ? /* @__PURE__ */ jsx("p", { "data-testid": etaText ? "store-eta" : void 0, className: "truncate text-sm text-white/80", children: [locality, etaText].filter(Boolean).join(" \xB7 ") }) : null : locality ? /* @__PURE__ */ jsx("p", { className: "truncate text-sm text-white/70", children: locality }) : null,
10094
+ compact && !config.delivery?.paused ? null : config.delivery?.paused ? /* @__PURE__ */ jsxs("p", { "data-testid": "store-delivery-paused", className: "mt-0.5 inline-flex items-center gap-1.5 truncate text-sm font-semibold text-amber-300", children: [
10093
10095
  /* @__PURE__ */ jsx("span", { className: "h-2 w-2 shrink-0 rounded-full bg-amber-400", "aria-hidden": true }),
10094
10096
  t("delivery.paused")
10095
10097
  ] }) : (() => {
@@ -12896,5 +12898,5 @@ function createStorefront(options) {
12896
12898
  }
12897
12899
 
12898
12900
  export { AddressForm, AddressSheet, AddressStrip, BEFORE_PLACE_ORDER_VALVE, BUYER_COUNTRY_COOKIE, BenefitsRow, CART_LINE_ANNOTATION_POINT, CHECKOUT_FIELD_POINT, CartDrawer, CatalogPage, CategoryShowcase, CheckoutPage, ClosedStoreDialog, CollectionCta, CollectionHero, ContentPage, CountdownBand, CountrySelect, DeliverToPicker, EmailConfirmationRequiredError, EntityListBlock, ExperimentBlock, ExperimentProvider, ExperimentVariantBlock, FaqSection, FiltersPanel, FormBlock, HeroSection, ImageTiles, ItemSheet, ManifestoBlock, MediaCarousel, MenuItemRow, MenuPage, MenuToolbar, MotifStrip, MyPurchasesPage, NewsletterBand, ORDER_METADATA_POINT, OrderConfirmationPage, OrderTrackingTimeline, PAGE_DATA_SCRIPT_ID, PLACEHOLDER_ASPECTS, PRODUCT_CARD_BADGE_POINT, PaymentTerms, Price, ProductCard, ProductDetailPage, ProductEnquiryForm, ProductGallery, ProductGrid, ProductOptionSelector, ProductRail, ProductReviews, ProductSlider, ProductSpecs, ProductSpotlight, PromoBanner, QuantityInput, Reveal, STOREFRONT_SLOTS, ScheduleSheet, SealsBand, SearchOverlay, ServiceHoursModal, Slot, SmoothImage, StepsSection, StoreCover, StorefrontAnalytics, StorefrontConfigHost, StorefrontFooter, StorefrontHeader, StorefrontPage, StorefrontShell, StorefrontThemeStyle, StoryQuote, THEME_SCALE_FIELDS, THEME_SCALE_VAR_PREFIX, TID, Testimonials, TierCards, applyDeliveryPolicy, applyStoreSettings, bannerPlaceholder, buildEnquiryFromForm, collectOrderMetadata, createStorefront, createStorefrontApp, entityListBlockMeta, establishCustomerSession, formatMoney, formatProductOptionSelection, getCartLineAnnotations, getCheckoutFieldContributions, getCustomerAuthAdapter, getProductCardBadges, getProductOptionGroups, hydratedEntriesFor, identifyStorefrontBuyer, initStorefrontRuntime, isExperimentBinding, listSlots, marketForCountry, marketUnitPrice, matchesFacets, mergeCollectionFacet, nextMilestone, normalizeProductOptionSelection, onStorefrontEvent, orderGalleryImages, overrideComponent, overrideProps, pickProductCardImage, placeStorefrontOrder, prefersReducedMotion, productCardComponentContract, productOptionSelectionKey, productPlaceholder, readHydratedPageData, registerStorefrontBlocks, registerStorefrontEntities, registerStorefrontExtensionPoints, registerStorefrontLoaders, resetPageDiagnostics, resetProductOptionsDeprecationWarnings, resetStorefrontExtensionPoints, resolveAuthAdapter, resolveExperimental, resolveOverride, resolvePaymentTerms, resolveStorefrontProvider, resolveVariantForSelection, roundCents, runBeforePlaceOrderValve, sectionsToBlocks, selectCount, selectDiscountTotal, selectIsOpen, selectRequiresShipping, selectShipping, selectShowsHours, selectSubtotal, selectTotal, selectionPrice, serializePageData, signInByEmail, signOutCustomer, signUpCustomer, startStorefrontPostHog, storeBuyerId, storefrontComponentContracts, themeToCss, toFacetMap, trackStorefront, useBuyerCountry, useBuyerStore, useCartStore, useCatalogStore, useCategories, useDeliveryStore, useDiscountValidator, useEnquiry, useExperiment, useExperimentValue, useInView, useMarketProduct, useMarketStore, useMyOrders, usePopOnChange, useProduct, useProducts, useScrolled, useServiceStore, useSessionStore, useSlotContext, useStorefrontActions, useStorefrontHead, useStorefrontPage, useStorefrontPageOptional, useT, usedSlots, withFacet, withMarketPrice };
12899
- //# sourceMappingURL=chunk-RYWQGQ4M.js.map
12900
- //# sourceMappingURL=chunk-RYWQGQ4M.js.map
12901
+ //# sourceMappingURL=chunk-7GX7JLF7.js.map
12902
+ //# sourceMappingURL=chunk-7GX7JLF7.js.map