@runbandit/sdk 3.5.0 → 3.5.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.
@@ -62,7 +62,7 @@ declare function serializeTreatmentConfig(content: TreatmentContent): {
62
62
  content: unknown;
63
63
  };
64
64
 
65
- declare const SITE_TARGET_KINDS: readonly ["text", "image"];
65
+ declare const SITE_TARGET_KINDS: readonly ["text", "image", "order"];
66
66
  type SiteTargetKind = (typeof SITE_TARGET_KINDS)[number];
67
67
  interface SiteTarget {
68
68
  page: string;
@@ -89,6 +89,12 @@ declare function goalMatchesPage(goal: Pick<FunnelGoal, 'page'>, pathname: strin
89
89
  declare const MAX_ALTERNATIVES = 5;
90
90
  declare const MAX_TEXT_ALTERNATIVE_LENGTH = 300;
91
91
  declare const MAX_IMAGE_URL_LENGTH = 2048;
92
+ declare const MIN_ORDER_ITEMS = 2;
93
+ declare const MAX_ORDER_ITEMS = 24;
94
+ declare const MAX_ORDER_KEY_LENGTH = 160;
95
+ declare function formatOrder(keys: readonly string[]): string;
96
+ declare function parseOrder(raw: string): string[];
97
+ declare function isReorderOf(original: readonly string[], order: readonly string[]): boolean;
92
98
  interface PickDraft {
93
99
  target?: SiteTarget;
94
100
  alternatives: string[];
@@ -110,9 +116,9 @@ declare function pickStep(state: Pick<PickState, 'draft' | 'funnelGoal' | 'exper
110
116
  declare function parseAlternative(kind: SiteTargetKind, raw: string): string;
111
117
  declare const PICK_CHANNEL = "bandit-pick";
112
118
  declare const PICK_PROTOCOL_VERSION = 1;
113
- declare const PICK_MODES: readonly ["idle", "target", "goal-click"];
119
+ declare const PICK_MODES: readonly ["idle", "target", "order", "goal-click"];
114
120
  type PickMode = (typeof PICK_MODES)[number];
115
- declare const PICK_REJECTIONS: readonly ["not-text-or-image", "has-markup", "not-clickable", "no-selector"];
121
+ declare const PICK_REJECTIONS: readonly ["not-text-or-image", "has-markup", "not-a-list", "not-clickable", "no-selector"];
116
122
  type PickRejection = (typeof PICK_REJECTIONS)[number];
117
123
  type ClickFunnelGoal = Extract<FunnelGoal, {
118
124
  kind: 'click';
@@ -501,4 +507,4 @@ declare function readCookie(header: string | null | undefined, name: string): st
501
507
  declare function parseDecisions(value: string | null | undefined, now: number): CookieDecisions;
502
508
  declare function serializeDecisions(decisions: CookieDecisions): string;
503
509
 
504
- export { type BundleMode as $, type Account as A, type BundleAssignmentResponse as B, type ContextualFeatures as C, DECISIONS_COOKIE as D, type Experiment as E, ForgettingMode as F, type TreatmentStats as G, type APIResponse as H, type CreateExperimentRequest as I, type AssignmentRequest as J, type TrackNamedEventRequest as K, FunnelStatus as L, type Funnel as M, type FunnelRewardRequest as N, type OrderLineItem as O, Posterior as P, type ApiKey as Q, type CreateApiKeyRequest as R, type SnapshotTreatment as S, type Treatment as T, type User as U, VISITOR_COOKIE as V, type CreateApiKeyResponse as W, type IdentifyMergeReason as X, type IdentifyUserRequest as Y, type IdentifyUserResponse as Z, type ResolvedIdentity as _, type CompanySnapshot as a, type Bundle as a0, type BundleAssignmentRequest as a1, type BundleSlotAssignment as a2, STOREFRONT_KINDS as a3, type StorefrontKind as a4, PLACEMENTS_BY_KIND as a5, type Placement as a6, PLACEMENTS as a7, isPlacement as a8, kindOfPlacement as a9, pickStep as aA, parseAlternative as aB, PICK_CHANNEL as aC, PICK_PROTOCOL_VERSION as aD, PICK_MODES as aE, type PickMode as aF, PICK_REJECTIONS as aG, type PickRejection as aH, type ClickFunnelGoal as aI, type PickerToPanel as aJ, type PanelToPicker as aK, PICK_TOKEN_PREFIX as aL, type PickEnvelope as aM, pickEnvelope as aN, parsePickerMessage as aO, parsePanelMessage as aP, type OrderAmounts as aQ, CONTENT_TYPE_BY_KIND as aa, MIN_ORDERING_ITEMS as ab, MAX_ORDERING_ITEMS as ac, type ImageContent as ad, type OfferContent as ae, type TreatmentContent as af, isShopifyCdnUrl as ag, parseTreatmentContent as ah, serializeTreatmentConfig as ai, SITE_TARGET_KINDS as aj, type SiteTargetKind as ak, type SiteTarget as al, type FunnelGoal as am, parseSiteContent as an, normalizeSiteTarget as ao, normalizeFunnelGoal as ap, targetMatchesPage as aq, goalMatchesPage as ar, MAX_ALTERNATIVES as as, MAX_TEXT_ALTERNATIVE_LENGTH as at, MAX_IMAGE_URL_LENGTH as au, type PickDraft as av, type PickState as aw, SUGGESTED_SITE_EXPERIMENTS as ax, moreExperimentsSentence as ay, type PickStep as az, type SnapshotExperiment as b, type CookieDecision as c, type CookieDecisions as d, type Company as e, type Session as f, type AlgorithmResult as g, type BanditResult as h, ExperimentStatus as i, type TreatmentMetadata as j, type VariantConfig as k, BanditAlgorithm as l, type ForgettingConfig as m, normalizePosterior as n, DEFAULT_HALF_LIFE_MS as o, parseDecisions as p, DEFAULT_WINDOW_MS as q, readCookie as r, serializeDecisions as s, STATIONARY as t, resolveForgetting as u, normalizeAlgorithm as v, ContentType as w, getContentTypeCategory as x, type BanditConfig as y, type ExperimentStats as z };
510
+ export { type BundleMode as $, type Account as A, type BundleAssignmentResponse as B, type ContextualFeatures as C, DECISIONS_COOKIE as D, type Experiment as E, ForgettingMode as F, type TreatmentStats as G, type APIResponse as H, type CreateExperimentRequest as I, type AssignmentRequest as J, type TrackNamedEventRequest as K, FunnelStatus as L, type Funnel as M, type FunnelRewardRequest as N, type OrderLineItem as O, Posterior as P, type ApiKey as Q, type CreateApiKeyRequest as R, type SnapshotTreatment as S, type Treatment as T, type User as U, VISITOR_COOKIE as V, type CreateApiKeyResponse as W, type IdentifyMergeReason as X, type IdentifyUserRequest as Y, type IdentifyUserResponse as Z, type ResolvedIdentity as _, type CompanySnapshot as a, type Bundle as a0, type BundleAssignmentRequest as a1, type BundleSlotAssignment as a2, STOREFRONT_KINDS as a3, type StorefrontKind as a4, PLACEMENTS_BY_KIND as a5, type Placement as a6, PLACEMENTS as a7, isPlacement as a8, kindOfPlacement as a9, isReorderOf as aA, type PickDraft as aB, type PickState as aC, SUGGESTED_SITE_EXPERIMENTS as aD, moreExperimentsSentence as aE, type PickStep as aF, pickStep as aG, parseAlternative as aH, PICK_CHANNEL as aI, PICK_PROTOCOL_VERSION as aJ, PICK_MODES as aK, type PickMode as aL, PICK_REJECTIONS as aM, type PickRejection as aN, type ClickFunnelGoal as aO, type PickerToPanel as aP, type PanelToPicker as aQ, PICK_TOKEN_PREFIX as aR, type PickEnvelope as aS, pickEnvelope as aT, parsePickerMessage as aU, parsePanelMessage as aV, type OrderAmounts as aW, CONTENT_TYPE_BY_KIND as aa, MIN_ORDERING_ITEMS as ab, MAX_ORDERING_ITEMS as ac, type ImageContent as ad, type OfferContent as ae, type TreatmentContent as af, isShopifyCdnUrl as ag, parseTreatmentContent as ah, serializeTreatmentConfig as ai, SITE_TARGET_KINDS as aj, type SiteTargetKind as ak, type SiteTarget as al, type FunnelGoal as am, parseSiteContent as an, normalizeSiteTarget as ao, normalizeFunnelGoal as ap, targetMatchesPage as aq, goalMatchesPage as ar, MAX_ALTERNATIVES as as, MAX_TEXT_ALTERNATIVE_LENGTH as at, MAX_IMAGE_URL_LENGTH as au, MIN_ORDER_ITEMS as av, MAX_ORDER_ITEMS as aw, MAX_ORDER_KEY_LENGTH as ax, formatOrder as ay, parseOrder as az, type SnapshotExperiment as b, type CookieDecision as c, type CookieDecisions as d, type Company as e, type Session as f, type AlgorithmResult as g, type BanditResult as h, ExperimentStatus as i, type TreatmentMetadata as j, type VariantConfig as k, BanditAlgorithm as l, type ForgettingConfig as m, normalizePosterior as n, DEFAULT_HALF_LIFE_MS as o, parseDecisions as p, DEFAULT_WINDOW_MS as q, readCookie as r, serializeDecisions as s, STATIONARY as t, resolveForgetting as u, normalizeAlgorithm as v, ContentType as w, getContentTypeCategory as x, type BanditConfig as y, type ExperimentStats as z };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { T as Treatment, C as ContextualFeatures, O as OrderLineItem, B as BundleAssignmentResponse, a as CompanySnapshot, S as SnapshotTreatment } from './decision-cookie-DgVvhn7c.js';
2
- export { H as APIResponse, A as Account, g as AlgorithmResult, Q as ApiKey, J as AssignmentRequest, l as BanditAlgorithm, y as BanditConfig, h as BanditResult, a0 as Bundle, a1 as BundleAssignmentRequest, $ as BundleMode, a2 as BundleSlotAssignment, aa as CONTENT_TYPE_BY_KIND, aI as ClickFunnelGoal, e as Company, w as ContentType, c as CookieDecision, d as CookieDecisions, R as CreateApiKeyRequest, W as CreateApiKeyResponse, I as CreateExperimentRequest, D as DECISIONS_COOKIE, o as DEFAULT_HALF_LIFE_MS, q as DEFAULT_WINDOW_MS, E as Experiment, z as ExperimentStats, i as ExperimentStatus, m as ForgettingConfig, F as ForgettingMode, M as Funnel, am as FunnelGoal, N as FunnelRewardRequest, L as FunnelStatus, X as IdentifyMergeReason, Y as IdentifyUserRequest, Z as IdentifyUserResponse, ad as ImageContent, as as MAX_ALTERNATIVES, au as MAX_IMAGE_URL_LENGTH, ac as MAX_ORDERING_ITEMS, at as MAX_TEXT_ALTERNATIVE_LENGTH, ab as MIN_ORDERING_ITEMS, ae as OfferContent, aQ as OrderAmounts, aC as PICK_CHANNEL, aE as PICK_MODES, aD as PICK_PROTOCOL_VERSION, aG as PICK_REJECTIONS, aL as PICK_TOKEN_PREFIX, a7 as PLACEMENTS, a5 as PLACEMENTS_BY_KIND, aK as PanelToPicker, av as PickDraft, aM as PickEnvelope, aF as PickMode, aH as PickRejection, aw as PickState, az as PickStep, aJ as PickerToPanel, a6 as Placement, P as Posterior, _ as ResolvedIdentity, aj as SITE_TARGET_KINDS, t as STATIONARY, a3 as STOREFRONT_KINDS, ax as SUGGESTED_SITE_EXPERIMENTS, f as Session, al as SiteTarget, ak as SiteTargetKind, b as SnapshotExperiment, a4 as StorefrontKind, K as TrackNamedEventRequest, af as TreatmentContent, j as TreatmentMetadata, G as TreatmentStats, U as User, V as VISITOR_COOKIE, k as VariantConfig, x as getContentTypeCategory, ar as goalMatchesPage, a8 as isPlacement, ag as isShopifyCdnUrl, a9 as kindOfPlacement, ay as moreExperimentsSentence, v as normalizeAlgorithm, ap as normalizeFunnelGoal, n as normalizePosterior, ao as normalizeSiteTarget, aB as parseAlternative, p as parseDecisions, aP as parsePanelMessage, aO as parsePickerMessage, an as parseSiteContent, ah as parseTreatmentContent, aN as pickEnvelope, aA as pickStep, r as readCookie, u as resolveForgetting, s as serializeDecisions, ai as serializeTreatmentConfig, aq as targetMatchesPage } from './decision-cookie-DgVvhn7c.js';
1
+ import { T as Treatment, C as ContextualFeatures, O as OrderLineItem, B as BundleAssignmentResponse, a as CompanySnapshot, S as SnapshotTreatment } from './decision-cookie-g9aDdSx9.js';
2
+ export { H as APIResponse, A as Account, g as AlgorithmResult, Q as ApiKey, J as AssignmentRequest, l as BanditAlgorithm, y as BanditConfig, h as BanditResult, a0 as Bundle, a1 as BundleAssignmentRequest, $ as BundleMode, a2 as BundleSlotAssignment, aa as CONTENT_TYPE_BY_KIND, aO as ClickFunnelGoal, e as Company, w as ContentType, c as CookieDecision, d as CookieDecisions, R as CreateApiKeyRequest, W as CreateApiKeyResponse, I as CreateExperimentRequest, D as DECISIONS_COOKIE, o as DEFAULT_HALF_LIFE_MS, q as DEFAULT_WINDOW_MS, E as Experiment, z as ExperimentStats, i as ExperimentStatus, m as ForgettingConfig, F as ForgettingMode, M as Funnel, am as FunnelGoal, N as FunnelRewardRequest, L as FunnelStatus, X as IdentifyMergeReason, Y as IdentifyUserRequest, Z as IdentifyUserResponse, ad as ImageContent, as as MAX_ALTERNATIVES, au as MAX_IMAGE_URL_LENGTH, ac as MAX_ORDERING_ITEMS, aw as MAX_ORDER_ITEMS, ax as MAX_ORDER_KEY_LENGTH, at as MAX_TEXT_ALTERNATIVE_LENGTH, ab as MIN_ORDERING_ITEMS, av as MIN_ORDER_ITEMS, ae as OfferContent, aW as OrderAmounts, aI as PICK_CHANNEL, aK as PICK_MODES, aJ as PICK_PROTOCOL_VERSION, aM as PICK_REJECTIONS, aR as PICK_TOKEN_PREFIX, a7 as PLACEMENTS, a5 as PLACEMENTS_BY_KIND, aQ as PanelToPicker, aB as PickDraft, aS as PickEnvelope, aL as PickMode, aN as PickRejection, aC as PickState, aF as PickStep, aP as PickerToPanel, a6 as Placement, P as Posterior, _ as ResolvedIdentity, aj as SITE_TARGET_KINDS, t as STATIONARY, a3 as STOREFRONT_KINDS, aD as SUGGESTED_SITE_EXPERIMENTS, f as Session, al as SiteTarget, ak as SiteTargetKind, b as SnapshotExperiment, a4 as StorefrontKind, K as TrackNamedEventRequest, af as TreatmentContent, j as TreatmentMetadata, G as TreatmentStats, U as User, V as VISITOR_COOKIE, k as VariantConfig, ay as formatOrder, x as getContentTypeCategory, ar as goalMatchesPage, a8 as isPlacement, aA as isReorderOf, ag as isShopifyCdnUrl, a9 as kindOfPlacement, aE as moreExperimentsSentence, v as normalizeAlgorithm, ap as normalizeFunnelGoal, n as normalizePosterior, ao as normalizeSiteTarget, aH as parseAlternative, p as parseDecisions, az as parseOrder, aV as parsePanelMessage, aU as parsePickerMessage, an as parseSiteContent, ah as parseTreatmentContent, aT as pickEnvelope, aG as pickStep, r as readCookie, u as resolveForgetting, s as serializeDecisions, ai as serializeTreatmentConfig, aq as targetMatchesPage } from './decision-cookie-g9aDdSx9.js';
3
3
 
4
4
  declare function validateTreatmentWeights(treatments: Treatment[]): boolean;
5
5
  declare function normalizeTreatmentWeights(treatments: Treatment[]): Treatment[];
@@ -145,6 +145,12 @@ declare const SITE_PLATFORMS: readonly [{
145
145
  readonly path: "shopify";
146
146
  readonly codeBox: "";
147
147
  readonly steps: readonly [];
148
+ }, {
149
+ readonly id: "shopify_theme";
150
+ readonly label: "Shopify";
151
+ readonly path: "script";
152
+ readonly codeBox: "Online Store, Themes, Edit code, layout/theme.liquid, just before </head>.";
153
+ readonly steps: readonly ["In your Shopify admin open Online Store, then Themes.", "On your live theme click the three dots, then Edit code.", "Open layout/theme.liquid.", "Paste the script on the line just before </head>.", "Click Save.", "Then add the purchase pixel below, so Bandit learns from orders."];
148
154
  }, {
149
155
  readonly id: "webflow";
150
156
  readonly label: "Webflow";
@@ -279,6 +285,26 @@ type ArrivalSource = {
279
285
  };
280
286
  declare function sourceOf(arrival: Arrival | undefined): ArrivalSource;
281
287
 
288
+ declare const CDP_SOURCES: readonly ["segment", "rudderstack", "posthog"];
289
+ type CdpSource = (typeof CDP_SOURCES)[number];
290
+ interface CdpAlias {
291
+ source: CdpSource;
292
+ externalId: string;
293
+ }
294
+ declare const MAX_CDP_EXTERNAL_ID_LENGTH = 256;
295
+ interface CdpStorage {
296
+ readonly length: number;
297
+ key(index: number): string | null;
298
+ getItem(key: string): string | null;
299
+ }
300
+ interface CdpSourceEntry {
301
+ source: CdpSource;
302
+ key: RegExp;
303
+ parse: (raw: string) => string | null;
304
+ }
305
+ declare const CDP_SOURCE_REGISTRY: readonly CdpSourceEntry[];
306
+ declare function readCdpAliases(cookieHeader: string, storage: CdpStorage | null): CdpAlias[];
307
+
282
308
  type PayoutUnit = 'count' | 'revenue' | 'margin';
283
309
  declare const PAYOUT_UNITS: readonly PayoutUnit[];
284
310
  declare const NEW_PAYOUT_UNIT: PayoutUnit;
@@ -316,6 +342,7 @@ interface BanditsClientConfig {
316
342
  sessionTtl?: number;
317
343
  visitorId?: string;
318
344
  captureArrival?: boolean;
345
+ captureCdpAliases?: boolean;
319
346
  }
320
347
  declare class BanditsRequestError extends Error {
321
348
  readonly status: number | undefined;
@@ -339,6 +366,8 @@ declare class BanditsClient {
339
366
  private exposedIds;
340
367
  private flushTimer?;
341
368
  constructor(config: BanditsClientConfig);
369
+ reportCdpAliases(): Promise<boolean>;
370
+ aliasVisitor(aliases: CdpAlias[]): Promise<void>;
342
371
  reportArrival(): Promise<boolean>;
343
372
  getAssignment(experimentId: string, userId?: string, context?: ContextualFeatures): Promise<Assignment>;
344
373
  trackNamedEvent(event: {
@@ -475,4 +504,4 @@ declare function reportPlanImpressions(plan: EmailPlan, recipientIds: string[],
475
504
  alreadyRecorded: boolean;
476
505
  }>;
477
506
 
478
- export { ARRIVAL_CHANNELS, ARRIVAL_TTL_MS, type Arrival, type ArrivalChannel, type ArrivalSource, type ArrivalTouch, type Assignment, BanditsClient as BanditClient, BanditsClient, type BanditsClientConfig, BanditsRequestError, type Bound, BundleAssignmentResponse, type ButtonBlock, CHANNEL_LABELS, CHANNEL_RULES, CompanySnapshot, ContextualFeatures, DEFAULT_SESSION_TTL_MS, type DividerBlock, EMAIL_TOKEN_PARAM, type EmailBlock, type EmailDocument, type EmailPlan, type EmailTokenPayload, type HeadingBlock, type ImageBlock, MAX_ARRIVAL_VALUE_LENGTH, MAX_RAW_TOUCH_VALUE_LENGTH, MAX_REFERRER_HOST_LENGTH, NEW_PAYOUT_UNIT, OrderLineItem, PASS_THROUGH_HOSTS, PAYOUT_UNITS, PAYOUT_UNIT_COPY, type PayoutUnit, type PlanArm, type PlanRenderResult, type PlanSlot, type RenderEmailOptions, type RenderedDocument, type RenderedEmail, type RewriteOptions, type RewriteResult, SCRIPT_PLATFORMS, SESSION_CREATED_AT_KEY, SESSION_ID_KEY, SITE_PLATFORMS, SITE_PLATFORM_IDS, type ScriptPlatform, type SessionOptions, type SigningKey, type SitePlatform, type SitePlatformId, type SitePlatformPath, type SlotBinding, SnapshotClient, type SnapshotClientConfig, SnapshotTreatment, type SpacerBlock, type StoredArrival, type SyncAssignment, TRACKING_PARAM_SOURCES, TRIAL_RULES, TRIAL_RULE_COPY, type TextBlock, Treatment, type TrialRule, UNKNOWN_SOURCE_KEY, UTM_PARAMS, VISITOR_ID_KEY, type VerifiedToken, appendEmailToken, arrivalFromTouch, buildSubdomainUrl, calculateConfidenceInterval, channelOf, extractDocumentSlots, extractEmailLinks, extractSlotNames, extractSubdomain, formatPayout, generateId, generateSubdomain, getOrCreateSessionId, getOrCreateVisitorId, hashRecipient, impressionManifest, isArrivalActive, isCurrencyUnit, isDirectTouch, isPassThroughHost, isPayoutUnit, isSlotBinding, isTrialRule, isValidEmail, isValidSubdomain, matchesConversionPath, mintEmailToken, normalizeArrivalValue, normalizeConversionPath, normalizeHost, normalizeTreatmentWeights, payoutUnitOfRewardBasis, pickArm, pickTreatment, platformById, renderEmail, renderEmailDocument, renderFromPlan, reportPlanImpressions, rewardValue, rewriteEmailLinks, sampleCombination, sanitizeMetadata, shouldRefreshPlan, shouldReportTouch, sourceOf, touchFromPage, touchSignature, trialRuleFor, trialRuleOf, uniformFor, validateTreatmentWeights, verifyEmailToken };
507
+ export { ARRIVAL_CHANNELS, ARRIVAL_TTL_MS, type Arrival, type ArrivalChannel, type ArrivalSource, type ArrivalTouch, type Assignment, BanditsClient as BanditClient, BanditsClient, type BanditsClientConfig, BanditsRequestError, type Bound, BundleAssignmentResponse, type ButtonBlock, CDP_SOURCES, CDP_SOURCE_REGISTRY, CHANNEL_LABELS, CHANNEL_RULES, type CdpAlias, type CdpSource, type CdpStorage, CompanySnapshot, ContextualFeatures, DEFAULT_SESSION_TTL_MS, type DividerBlock, EMAIL_TOKEN_PARAM, type EmailBlock, type EmailDocument, type EmailPlan, type EmailTokenPayload, type HeadingBlock, type ImageBlock, MAX_ARRIVAL_VALUE_LENGTH, MAX_CDP_EXTERNAL_ID_LENGTH, MAX_RAW_TOUCH_VALUE_LENGTH, MAX_REFERRER_HOST_LENGTH, NEW_PAYOUT_UNIT, OrderLineItem, PASS_THROUGH_HOSTS, PAYOUT_UNITS, PAYOUT_UNIT_COPY, type PayoutUnit, type PlanArm, type PlanRenderResult, type PlanSlot, type RenderEmailOptions, type RenderedDocument, type RenderedEmail, type RewriteOptions, type RewriteResult, SCRIPT_PLATFORMS, SESSION_CREATED_AT_KEY, SESSION_ID_KEY, SITE_PLATFORMS, SITE_PLATFORM_IDS, type ScriptPlatform, type SessionOptions, type SigningKey, type SitePlatform, type SitePlatformId, type SitePlatformPath, type SlotBinding, SnapshotClient, type SnapshotClientConfig, SnapshotTreatment, type SpacerBlock, type StoredArrival, type SyncAssignment, TRACKING_PARAM_SOURCES, TRIAL_RULES, TRIAL_RULE_COPY, type TextBlock, Treatment, type TrialRule, UNKNOWN_SOURCE_KEY, UTM_PARAMS, VISITOR_ID_KEY, type VerifiedToken, appendEmailToken, arrivalFromTouch, buildSubdomainUrl, calculateConfidenceInterval, channelOf, extractDocumentSlots, extractEmailLinks, extractSlotNames, extractSubdomain, formatPayout, generateId, generateSubdomain, getOrCreateSessionId, getOrCreateVisitorId, hashRecipient, impressionManifest, isArrivalActive, isCurrencyUnit, isDirectTouch, isPassThroughHost, isPayoutUnit, isSlotBinding, isTrialRule, isValidEmail, isValidSubdomain, matchesConversionPath, mintEmailToken, normalizeArrivalValue, normalizeConversionPath, normalizeHost, normalizeTreatmentWeights, payoutUnitOfRewardBasis, pickArm, pickTreatment, platformById, readCdpAliases, renderEmail, renderEmailDocument, renderFromPlan, reportPlanImpressions, rewardValue, rewriteEmailLinks, sampleCombination, sanitizeMetadata, shouldRefreshPlan, shouldReportTouch, sourceOf, touchFromPage, touchSignature, trialRuleFor, trialRuleOf, uniformFor, validateTreatmentWeights, verifyEmailToken };
package/dist/index.js CHANGED
@@ -614,7 +614,7 @@ function matchesConversionPath(pattern, pathname) {
614
614
  }
615
615
 
616
616
  // ../shared/dist/site.js
617
- var SITE_TARGET_KINDS = ["text", "image"];
617
+ var SITE_TARGET_KINDS = ["text", "image", "order"];
618
618
  var TAG_NAME = /^[a-z][a-z0-9-]*$/;
619
619
  function collapseWhitespace(raw) {
620
620
  return raw.replace(/\s+/g, " ").trim();
@@ -649,13 +649,13 @@ function parseSiteContent(config) {
649
649
  }
650
650
  function normalizeSiteTarget(target) {
651
651
  if (!SITE_TARGET_KINDS.includes(target.kind))
652
- throw new Error("A target is text or an image");
652
+ throw new Error("A target is text, an image, or a list");
653
653
  return {
654
654
  page: requirePage(target.page),
655
655
  selector: requireText(target.selector, "The picked element has no selector"),
656
656
  tag: requireTag(target.tag),
657
657
  kind: target.kind,
658
- original: requireText(target.original, "The picked element has no content to replace")
658
+ original: target.kind === "order" ? formatOrder(parseOrder(target.original)) : requireText(target.original, "The picked element has no content to replace")
659
659
  };
660
660
  }
661
661
  function normalizeFunnelGoal(goal) {
@@ -679,6 +679,39 @@ function goalMatchesPage(goal, pathname) {
679
679
  var MAX_ALTERNATIVES = 5;
680
680
  var MAX_TEXT_ALTERNATIVE_LENGTH = 300;
681
681
  var MAX_IMAGE_URL_LENGTH = 2048;
682
+ var MIN_ORDER_ITEMS = 2;
683
+ var MAX_ORDER_ITEMS = 24;
684
+ var MAX_ORDER_KEY_LENGTH = 160;
685
+ function formatOrder(keys) {
686
+ return JSON.stringify(keys);
687
+ }
688
+ function parseOrder(raw) {
689
+ let value;
690
+ try {
691
+ value = JSON.parse(raw);
692
+ } catch {
693
+ throw new Error("An order is a list of the items in the order to show them");
694
+ }
695
+ if (!Array.isArray(value) || !value.every((key) => typeof key === "string" && key.length > 0)) {
696
+ throw new Error("An order is a list of the items in the order to show them");
697
+ }
698
+ const keys = value;
699
+ if (keys.length < MIN_ORDER_ITEMS)
700
+ throw new Error(`A list needs at least ${MIN_ORDER_ITEMS} items`);
701
+ if (keys.length > MAX_ORDER_ITEMS)
702
+ throw new Error(`Reorder lists of at most ${MAX_ORDER_ITEMS} items`);
703
+ if (keys.some((key) => key.length > MAX_ORDER_KEY_LENGTH))
704
+ throw new Error("An item in that list is too long");
705
+ if (new Set(keys).size !== keys.length)
706
+ throw new Error("Every item in the list must be different");
707
+ return keys;
708
+ }
709
+ function isReorderOf(original, order) {
710
+ if (original.length !== order.length)
711
+ return false;
712
+ const remaining = new Set(original);
713
+ return order.every((key) => remaining.delete(key));
714
+ }
682
715
  var SUGGESTED_SITE_EXPERIMENTS = 3;
683
716
  function moreExperimentsSentence(running, suggested) {
684
717
  const noun = (count) => count === 1 ? "experiment" : "experiments";
@@ -707,6 +740,8 @@ function parseAlternative(kind, raw) {
707
740
  }
708
741
  return text;
709
742
  }
743
+ if (kind === "order")
744
+ return formatOrder(parseOrder(raw));
710
745
  const trimmed = raw.trim();
711
746
  if (trimmed.length > MAX_IMAGE_URL_LENGTH)
712
747
  throw new Error("That image address is too long");
@@ -722,8 +757,8 @@ function parseAlternative(kind, raw) {
722
757
  }
723
758
  var PICK_CHANNEL = "bandit-pick";
724
759
  var PICK_PROTOCOL_VERSION = 1;
725
- var PICK_MODES = ["idle", "target", "goal-click"];
726
- var PICK_REJECTIONS = ["not-text-or-image", "has-markup", "not-clickable", "no-selector"];
760
+ var PICK_MODES = ["idle", "target", "order", "goal-click"];
761
+ var PICK_REJECTIONS = ["not-text-or-image", "has-markup", "not-a-list", "not-clickable", "no-selector"];
727
762
  var PICK_TOKEN_PREFIX = "bpick_";
728
763
  function pickEnvelope(msg) {
729
764
  return { channel: PICK_CHANNEL, v: PICK_PROTOCOL_VERSION, msg };
@@ -806,6 +841,20 @@ function parsePanelMessage(data) {
806
841
  // ../shared/dist/site-platforms.js
807
842
  var SITE_PLATFORMS = [
808
843
  { id: "shopify", label: "Shopify", path: "shopify", codeBox: "", steps: [] },
844
+ {
845
+ id: "shopify_theme",
846
+ label: "Shopify",
847
+ path: "script",
848
+ codeBox: "Online Store, Themes, Edit code, layout/theme.liquid, just before </head>.",
849
+ steps: [
850
+ "In your Shopify admin open Online Store, then Themes.",
851
+ "On your live theme click the three dots, then Edit code.",
852
+ "Open layout/theme.liquid.",
853
+ "Paste the script on the line just before </head>.",
854
+ "Click Save.",
855
+ "Then add the purchase pixel below, so Bandit learns from orders."
856
+ ]
857
+ },
809
858
  {
810
859
  id: "webflow",
811
860
  label: "Webflow",
@@ -1205,6 +1254,100 @@ function sourceOf(arrival) {
1205
1254
  return { key: "ch:tagged", label: "Tagged" };
1206
1255
  }
1207
1256
 
1257
+ // ../shared/dist/cdp-aliases.js
1258
+ var CDP_SOURCES = ["segment", "rudderstack", "posthog"];
1259
+ var MAX_CDP_EXTERNAL_ID_LENGTH = 256;
1260
+ var RUDDERSTACK_ENCRYPTED_PREFIX = "RS_ENC_v3_";
1261
+ var RUDDERSTACK_LEGACY_ENCRYPTED_PREFIX = "RudderEncrypt:";
1262
+ function decoded(raw) {
1263
+ try {
1264
+ return decodeURIComponent(raw);
1265
+ } catch {
1266
+ return raw;
1267
+ }
1268
+ }
1269
+ function jsonValue(text) {
1270
+ try {
1271
+ return JSON.parse(text);
1272
+ } catch {
1273
+ return void 0;
1274
+ }
1275
+ }
1276
+ function nonEmpty(value) {
1277
+ return typeof value === "string" && value.length > 0 ? value : null;
1278
+ }
1279
+ function plainOrQuoted(raw) {
1280
+ const text = decoded(raw);
1281
+ return nonEmpty(text.startsWith('"') ? jsonValue(text) : text);
1282
+ }
1283
+ function rudderstackValue(raw) {
1284
+ const text = plainOrQuoted(raw);
1285
+ if (text === null || text.startsWith(RUDDERSTACK_LEGACY_ENCRYPTED_PREFIX))
1286
+ return null;
1287
+ if (!text.startsWith(RUDDERSTACK_ENCRYPTED_PREFIX))
1288
+ return text;
1289
+ try {
1290
+ return nonEmpty(jsonValue(atob(text.slice(RUDDERSTACK_ENCRYPTED_PREFIX.length))));
1291
+ } catch {
1292
+ return null;
1293
+ }
1294
+ }
1295
+ function posthogDistinctId(raw) {
1296
+ const parsed = jsonValue(decoded(raw));
1297
+ if (typeof parsed !== "object" || parsed === null)
1298
+ return null;
1299
+ const state = parsed;
1300
+ const deviceId = nonEmpty(state.$device_id);
1301
+ if (deviceId !== null)
1302
+ return deviceId;
1303
+ return state.$user_state === "identified" ? null : nonEmpty(state.distinct_id);
1304
+ }
1305
+ var CDP_SOURCE_REGISTRY = [
1306
+ { source: "segment", key: /^ajs_anonymous_id$/, parse: plainOrQuoted },
1307
+ { source: "rudderstack", key: /^rl_anonymous_id$/, parse: rudderstackValue },
1308
+ { source: "posthog", key: /^ph_.+_posthog$/, parse: posthogDistinctId }
1309
+ ];
1310
+ function cookiePairs(header) {
1311
+ const pairs = [];
1312
+ for (const part of header.split(";")) {
1313
+ const index = part.indexOf("=");
1314
+ if (index === -1)
1315
+ continue;
1316
+ pairs.push([part.slice(0, index).trim(), part.slice(index + 1).trim()]);
1317
+ }
1318
+ return pairs;
1319
+ }
1320
+ function storagePairs(storage) {
1321
+ const pairs = [];
1322
+ try {
1323
+ for (let i = 0; i < (storage?.length ?? 0); i++) {
1324
+ const key = storage.key(i);
1325
+ const value = key === null ? null : storage.getItem(key);
1326
+ if (key !== null && value !== null)
1327
+ pairs.push([key, value]);
1328
+ }
1329
+ } catch {
1330
+ return pairs;
1331
+ }
1332
+ return pairs;
1333
+ }
1334
+ function readCdpAliases(cookieHeader, storage) {
1335
+ const candidates = [...cookiePairs(cookieHeader), ...storagePairs(storage)];
1336
+ const aliases = [];
1337
+ for (const { source, key, parse } of CDP_SOURCE_REGISTRY) {
1338
+ for (const [name, raw] of candidates) {
1339
+ if (!key.test(name))
1340
+ continue;
1341
+ const externalId = parse(raw);
1342
+ if (externalId === null || externalId.length > MAX_CDP_EXTERNAL_ID_LENGTH)
1343
+ continue;
1344
+ aliases.push({ source, externalId });
1345
+ break;
1346
+ }
1347
+ }
1348
+ return aliases;
1349
+ }
1350
+
1208
1351
  // ../shared/dist/payout-unit.js
1209
1352
  var PAYOUT_UNITS = ["count", "revenue", "margin"];
1210
1353
  var NEW_PAYOUT_UNIT = "count";
@@ -1279,6 +1422,22 @@ function readStoredArrival(storage) {
1279
1422
  return null;
1280
1423
  }
1281
1424
  }
1425
+ var CDP_ALIASES_KEY = "bandits_cdp_aliases";
1426
+ function readSentAliases(storage, visitorId) {
1427
+ try {
1428
+ const parsed = JSON.parse(storage?.getItem(CDP_ALIASES_KEY) ?? "null");
1429
+ return parsed?.visitorId === visitorId && typeof parsed.sent === "object" && parsed.sent !== null ? parsed.sent : {};
1430
+ } catch {
1431
+ return {};
1432
+ }
1433
+ }
1434
+ function readCookieHeader() {
1435
+ try {
1436
+ return document.cookie ?? "";
1437
+ } catch {
1438
+ return "";
1439
+ }
1440
+ }
1282
1441
  var BanditsRequestError = class extends Error {
1283
1442
  status;
1284
1443
  constructor(message, status) {
@@ -1316,6 +1475,32 @@ var BanditsClient = class {
1316
1475
  this.visitorId = config.visitorId ?? getOrCreateVisitorId(defaultSessionStorage());
1317
1476
  this.startBatchFlush();
1318
1477
  if (config.captureArrival !== false) void this.reportArrival();
1478
+ if (config.captureCdpAliases !== false) void this.reportCdpAliases();
1479
+ }
1480
+ async reportCdpAliases() {
1481
+ if (typeof document === "undefined") return false;
1482
+ const storage = defaultSessionStorage();
1483
+ const sent = readSentAliases(storage, this.visitorId);
1484
+ const fresh = readCdpAliases(readCookieHeader(), storage).filter(({ source, externalId }) => sent[source] !== externalId);
1485
+ if (fresh.length === 0) return false;
1486
+ try {
1487
+ await this.aliasVisitor(fresh);
1488
+ } catch {
1489
+ return false;
1490
+ }
1491
+ const record = { visitorId: this.visitorId, sent: { ...sent, ...Object.fromEntries(fresh.map((a) => [a.source, a.externalId])) } };
1492
+ try {
1493
+ storage?.setItem(CDP_ALIASES_KEY, JSON.stringify(record));
1494
+ } catch {
1495
+ return true;
1496
+ }
1497
+ return true;
1498
+ }
1499
+ async aliasVisitor(aliases) {
1500
+ const response = await this.makeRequest("/aliases", "POST", { visitorId: this.visitorId, aliases });
1501
+ if (!response.success) {
1502
+ throw requestError(response, "Failed to alias visitor");
1503
+ }
1319
1504
  }
1320
1505
  async reportArrival() {
1321
1506
  if (typeof location === "undefined" || typeof document === "undefined") return false;
@@ -1818,6 +2003,8 @@ export {
1818
2003
  BanditsClient as BanditClient,
1819
2004
  BanditsClient,
1820
2005
  BanditsRequestError,
2006
+ CDP_SOURCES,
2007
+ CDP_SOURCE_REGISTRY,
1821
2008
  CHANNEL_LABELS,
1822
2009
  CHANNEL_RULES,
1823
2010
  CONTENT_TYPE_BY_KIND,
@@ -1832,12 +2019,16 @@ export {
1832
2019
  FunnelStatus,
1833
2020
  MAX_ALTERNATIVES,
1834
2021
  MAX_ARRIVAL_VALUE_LENGTH,
2022
+ MAX_CDP_EXTERNAL_ID_LENGTH,
1835
2023
  MAX_IMAGE_URL_LENGTH,
1836
2024
  MAX_ORDERING_ITEMS,
2025
+ MAX_ORDER_ITEMS,
2026
+ MAX_ORDER_KEY_LENGTH,
1837
2027
  MAX_RAW_TOUCH_VALUE_LENGTH,
1838
2028
  MAX_REFERRER_HOST_LENGTH,
1839
2029
  MAX_TEXT_ALTERNATIVE_LENGTH,
1840
2030
  MIN_ORDERING_ITEMS,
2031
+ MIN_ORDER_ITEMS,
1841
2032
  NEW_PAYOUT_UNIT,
1842
2033
  PASS_THROUGH_HOSTS,
1843
2034
  PAYOUT_UNITS,
@@ -1876,6 +2067,7 @@ export {
1876
2067
  extractEmailLinks,
1877
2068
  extractSlotNames,
1878
2069
  extractSubdomain,
2070
+ formatOrder,
1879
2071
  formatPayout,
1880
2072
  generateId,
1881
2073
  generateSubdomain,
@@ -1891,6 +2083,7 @@ export {
1891
2083
  isPassThroughHost,
1892
2084
  isPayoutUnit,
1893
2085
  isPlacement,
2086
+ isReorderOf,
1894
2087
  isShopifyCdnUrl,
1895
2088
  isSlotBinding,
1896
2089
  isTrialRule,
@@ -1910,6 +2103,7 @@ export {
1910
2103
  normalizeTreatmentWeights,
1911
2104
  parseAlternative,
1912
2105
  parseDecisions,
2106
+ parseOrder,
1913
2107
  parsePanelMessage,
1914
2108
  parsePickerMessage,
1915
2109
  parseSiteContent,
@@ -1920,6 +2114,7 @@ export {
1920
2114
  pickStep,
1921
2115
  pickTreatment,
1922
2116
  platformById,
2117
+ readCdpAliases,
1923
2118
  readCookie,
1924
2119
  renderEmail,
1925
2120
  renderEmailDocument,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../shared/src/types.ts","../../shared/src/utils.ts","../../shared/src/email-token.ts","../../shared/src/email-plan.ts","../../shared/src/email-blocks.ts","../../shared/src/storefront-content.ts","../../shared/src/conversion-path.ts","../../shared/src/site.ts","../../shared/src/site-platforms.ts","../../shared/src/arrival.ts","../../shared/src/payout-unit.ts","../../shared/src/trial-rule.ts","../src/client.ts","../src/email-links.ts","../src/email.ts","../src/email-plan.ts"],"sourcesContent":["import type { Placement } from './storefront-content.js';\nimport type { FunnelGoal, SiteTarget } from './site.js';\nimport type { OrderLineItem } from './order.js';\n\nexport interface Company {\n id: string;\n name: string;\n subdomain?: string;\n domain?: string;\n plan: string;\n apiKey: string;\n status: string;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface User {\n id: string;\n email: string;\n name: string;\n image?: string;\n role: string;\n companyId: string | null;\n emailVerified?: boolean;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface Session {\n id: string;\n userId: string;\n token: string;\n expiresAt: Date;\n ipAddress?: string | null;\n userAgent?: string | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface Account {\n id: string;\n userId: string;\n provider: string;\n providerAccountId: string;\n accessToken?: string;\n refreshToken?: string;\n accessTokenExpiresAt?: Date;\n refreshTokenExpiresAt?: Date;\n scope?: string;\n idToken?: string;\n sessionState?: string;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface Experiment {\n id: string;\n name: string;\n description?: string;\n algorithm: BanditAlgorithm;\n forgetting?: ForgettingConfig;\n status: ExperimentStatus;\n contentType: ContentType;\n treatments: Treatment[];\n context?: Record<string, any>;\n bundleId?: string;\n image?: string;\n emoji?: string;\n assignmentTtl: number;\n companyId: string;\n createdAt: Date;\n updatedAt: Date;\n endAt?: Date;\n placement?: Placement;\n}\n\nexport interface Treatment {\n id: string;\n experimentId: string;\n name: string;\n description?: string;\n config: VariantConfig;\n weight?: number;\n companyId: string;\n createdAt: Date;\n}\n\nexport interface AlgorithmResult {\n treatmentId: string;\n confidence: number;\n probabilityBest?: number;\n}\n\nexport interface BanditResult {\n assignmentId: string;\n treatmentId: string;\n confidence: number;\n probabilityBest?: number;\n metadata?: Record<string, any>;\n}\n\nexport interface ContextualFeatures {\n [key: string]: string | number | boolean;\n}\n\nexport enum ExperimentStatus {\n DRAFT = 'draft',\n ACTIVE = 'active',\n PAUSED = 'paused',\n COMPLETED = 'completed',\n ARCHIVED = 'archived'\n}\n\n\nexport interface TreatmentMetadata {\n strategy?: string;\n tone?: string;\n ctaStyle?: string;\n emotionalHook?: string;\n reasoning?: string;\n tags?: string[];\n generated?: boolean;\n generatedAt?: string;\n}\n\nexport interface VariantConfig {\n content: string;\n metadata?: TreatmentMetadata & Record<string, any>;\n}\n\nexport enum BanditAlgorithm {\n THOMPSON_SAMPLING = 'thompson_sampling',\n CONTEXTUAL_LINEAR = 'contextual_linear'\n}\n\nexport enum Posterior {\n BETA = 'beta',\n NORMAL_GAMMA = 'normal_gamma'\n}\n\nexport function normalizePosterior(posterior: string | null | undefined): Posterior {\n return posterior === Posterior.NORMAL_GAMMA ? Posterior.NORMAL_GAMMA : Posterior.BETA;\n}\n\nexport enum ForgettingMode {\n NONE = 'none',\n DISCOUNTED = 'discounted',\n SLIDING_WINDOW = 'sliding_window'\n}\n\nexport interface ForgettingConfig {\n mode: ForgettingMode;\n halfLifeMs?: number;\n windowMs?: number;\n}\n\nexport const DEFAULT_HALF_LIFE_MS = 7 * 24 * 60 * 60 * 1000;\n\nexport const DEFAULT_WINDOW_MS = 14 * 24 * 60 * 60 * 1000;\n\nexport const STATIONARY: ForgettingConfig = { mode: ForgettingMode.NONE };\n\nexport function resolveForgetting(config?: Partial<ForgettingConfig> | null): ForgettingConfig {\n switch (config?.mode) {\n case ForgettingMode.DISCOUNTED:\n return {\n mode: ForgettingMode.DISCOUNTED,\n halfLifeMs: config.halfLifeMs && config.halfLifeMs > 0\n ? config.halfLifeMs\n : DEFAULT_HALF_LIFE_MS\n };\n case ForgettingMode.SLIDING_WINDOW:\n return {\n mode: ForgettingMode.SLIDING_WINDOW,\n windowMs: config.windowMs && config.windowMs > 0\n ? config.windowMs\n : DEFAULT_WINDOW_MS\n };\n default:\n return STATIONARY;\n }\n}\n\nconst LEGACY_ALGORITHM_ALIASES: Record<string, BanditAlgorithm> = {\n epsilon_greedy: BanditAlgorithm.THOMPSON_SAMPLING,\n ucb1: BanditAlgorithm.THOMPSON_SAMPLING,\n contextual_neural: BanditAlgorithm.CONTEXTUAL_LINEAR\n};\n\nexport function normalizeAlgorithm(algorithm: string | null | undefined): BanditAlgorithm {\n if (algorithm === BanditAlgorithm.THOMPSON_SAMPLING) return BanditAlgorithm.THOMPSON_SAMPLING;\n if (algorithm === BanditAlgorithm.CONTEXTUAL_LINEAR) return BanditAlgorithm.CONTEXTUAL_LINEAR;\n return LEGACY_ALGORITHM_ALIASES[algorithm ?? ''] ?? BanditAlgorithm.THOMPSON_SAMPLING;\n}\n\nexport enum ContentType {\n PLAIN_TEXT = 'plain_text',\n MARKDOWN = 'markdown',\n HTML = 'html',\n CSS = 'css',\n JAVASCRIPT = 'javascript',\n JSON = 'json',\n IMAGE = 'image',\n VIDEO = 'video',\n AUDIO = 'audio',\n DOCUMENT = 'document',\n}\n\nexport function getContentTypeCategory(contentType: ContentType): 'text' | 'code' | 'file' {\n switch (contentType) {\n case ContentType.PLAIN_TEXT:\n case ContentType.MARKDOWN:\n return 'text';\n case ContentType.HTML:\n case ContentType.CSS:\n case ContentType.JAVASCRIPT:\n case ContentType.JSON:\n return 'code';\n case ContentType.IMAGE:\n case ContentType.VIDEO:\n case ContentType.AUDIO:\n case ContentType.DOCUMENT:\n return 'file';\n }\n}\n\nexport interface BanditConfig {\n algorithm: BanditAlgorithm;\n parameters: Record<string, any>;\n}\n\nexport interface ExperimentStats {\n totalAssignments: number;\n totalConversions: number;\n conversionRate: number;\n treatmentStats: Record<string, TreatmentStats>;\n}\n\nexport interface TreatmentStats {\n assignments: number;\n conversions: number;\n conversionRate: number;\n confidence: number;\n lift?: number;\n}\n\nexport interface APIResponse<T = any> {\n success: boolean;\n data?: T;\n error?: string;\n timestamp: Date;\n}\n\nexport interface CreateExperimentRequest {\n name: string;\n description?: string;\n algorithm: BanditAlgorithm;\n contentType: ContentType;\n algorithmConfig?: Record<string, any>;\n treatments?: Omit<Treatment, 'id' | 'experimentId' | 'createdAt'>[];\n}\n\nexport interface AssignmentRequest {\n experimentId: string;\n userId: string;\n context?: ContextualFeatures;\n sessionId?: string;\n pageUrl?: string;\n}\n\n\nexport interface TrackNamedEventRequest {\n name: string;\n category?: string;\n value?: number;\n cost?: number;\n lineItems?: OrderLineItem[];\n metadata?: Record<string, any>;\n sessionId: string;\n userId?: string;\n assignmentId?: string;\n}\n\n\nexport enum FunnelStatus {\n DRAFT = 'draft',\n ACTIVE = 'active',\n PAUSED = 'paused',\n COMPLETED = 'completed',\n}\n\nexport interface Funnel {\n id: string;\n name: string;\n description?: string;\n status: FunnelStatus;\n inactivityTimeoutMs?: number;\n revisionWindowMs?: number;\n companyId: string;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface FunnelRewardRequest {\n funnelId: string;\n userId: string;\n value: number;\n}\n\n\nexport interface ApiKey {\n id: string;\n name: string;\n keyPrefix: string;\n status: 'active' | 'revoked';\n createdBy: string;\n createdAt: Date;\n lastUsedAt?: Date;\n revokedAt?: Date;\n revokedBy?: string;\n}\n\nexport interface CreateApiKeyRequest {\n name: string;\n}\n\nexport interface CreateApiKeyResponse {\n id: string;\n key: string;\n name: string;\n}\n\n\nexport type IdentifyMergeReason =\n | 'user_signup'\n | 'email_verification'\n | 'social_login'\n | 'account_recovery'\n | 'manual';\n\nexport interface IdentifyUserRequest {\n anonymousId: string;\n identifiedUserId: string;\n mergeReason?: IdentifyMergeReason;\n}\n\nexport interface IdentifyUserResponse {\n anonymousId: string;\n identifiedUserId: string;\n alreadyMapped: boolean;\n}\n\nexport interface ResolvedIdentity {\n canonicalId: string;\n anonymousIds: string[];\n isIdentified: boolean;\n}\n\n\nexport type BundleMode = 'factored' | 'joint';\n\nexport interface Bundle {\n id: string;\n name: string;\n description?: string;\n status: ExperimentStatus;\n mode: BundleMode;\n funnelId?: string;\n assignmentTtl: number;\n conversionWindowMs: number;\n inactivityTimeoutMs?: number;\n revisionWindowMs?: number;\n companyId: string;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface BundleAssignmentRequest {\n bundleId: string;\n userId: string;\n context?: ContextualFeatures;\n sessionId?: string;\n pageUrl?: string;\n}\n\nexport interface BundleSlotAssignment {\n assignmentId: string;\n experimentId: string;\n treatmentId: string;\n config: VariantConfig | Record<string, unknown>;\n}\n\nexport interface BundleAssignmentResponse {\n bundleAssignmentId: string;\n slots: Record<string, BundleSlotAssignment>;\n}\n\nexport interface SnapshotTreatment {\n id: string;\n name?: string;\n config: unknown;\n pct: number;\n}\n\nexport interface SnapshotExperiment {\n name?: string;\n mode: 'snapshot' | 'network';\n assignmentTtl: number;\n treatments: SnapshotTreatment[];\n target?: SiteTarget;\n funnelId?: string;\n trialOn?: 'served' | 'exposure';\n}\n\nexport interface CompanySnapshot {\n ttl: number;\n generatedAt: number;\n experiments: Record<string, SnapshotExperiment>;\n goals?: Record<string, FunnelGoal>;\n}\n","import { Treatment } from './types.js';\n\nexport function generateId(): string {\n return Math.random().toString(36).substring(2) + Date.now().toString(36);\n}\n\nexport function validateTreatmentWeights(treatments: Treatment[]): boolean {\n const totalWeight = treatments.reduce((sum, treatment) => sum + (treatment.weight || 1), 0);\n return totalWeight > 0;\n}\n\nexport function normalizeTreatmentWeights(treatments: Treatment[]): Treatment[] {\n const totalWeight = treatments.reduce((sum, treatment) => sum + (treatment.weight || 1), 0);\n \n return treatments.map(treatment => ({\n ...treatment,\n weight: (treatment.weight || 1) / totalWeight\n }));\n}\n\nexport function calculateConfidenceInterval(\n successes: number,\n trials: number,\n confidence: number = 0.95\n): { lower: number; upper: number } {\n if (trials === 0) return { lower: 0, upper: 0 };\n \n const z = confidence === 0.95 ? 1.96 : confidence === 0.99 ? 2.576 : 1.645;\n const p = successes / trials;\n const margin = z * Math.sqrt((p * (1 - p)) / trials);\n \n return {\n lower: Math.max(0, p - margin),\n upper: Math.min(1, p + margin)\n };\n}\n\nexport function isValidEmail(email: string): boolean {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return emailRegex.test(email);\n}\n\nexport function sanitizeMetadata(metadata: Record<string, any>): Record<string, any> {\n const sanitized: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(metadata)) {\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n sanitized[key] = value;\n } else if (value !== null && value !== undefined) {\n sanitized[key] = String(value);\n }\n }\n\n return sanitized;\n}\n\nexport function generateSubdomain(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, '')\n .replace(/\\s+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n .substring(0, 63);\n}\n\nexport function isValidSubdomain(subdomain: string): boolean {\n if (!subdomain || subdomain.length === 0 || subdomain.length > 63) {\n return false;\n }\n\n const subdomainRegex = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;\n\n const reserved = [\n 'www', 'api', 'app', 'admin', 'dashboard', 'mail', 'ftp', 'localhost',\n 'staging', 'dev', 'test', 'demo', 'blog', 'docs', 'status', 'help',\n 'support', 'cdn', 'static', 'assets', 'secure', 'payment', 'pay'\n ];\n\n return subdomainRegex.test(subdomain) && !reserved.includes(subdomain);\n}\n\nexport function extractSubdomain(hostname: string, baseDomain: string): string | null {\n if (!hostname || !baseDomain) {\n return null;\n }\n\n const cleanHostname = hostname.split(':')[0];\n\n if (!cleanHostname.endsWith(baseDomain)) {\n return null;\n }\n\n const subdomain = cleanHostname.slice(0, -(baseDomain.length + 1));\n\n if (!subdomain || subdomain === 'www') {\n return null;\n }\n\n return subdomain;\n}\n\nexport function buildSubdomainUrl(\n subdomain: string,\n baseDomain: string,\n path: string = '',\n protocol: string = 'https'\n): string {\n const url = `${protocol}://${subdomain}.${baseDomain}`;\n return path ? `${url}${path.startsWith('/') ? path : '/' + path}` : url;\n}","\nexport const EMAIL_TOKEN_PARAM = 'bt';\n\nexport interface SigningKey {\n version: number;\n secret: string;\n}\n\nexport interface EmailTokenPayload {\n c: string;\n r: string;\n k: number[];\n l: number;\n v?: number;\n}\n\nexport type VerifiedToken =\n | { valid: true; payload: Required<EmailTokenPayload> }\n | { valid: false; reason: 'malformed' | 'unknown_key' | 'bad_signature' };\n\n\nconst encoder = new TextEncoder();\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction base64UrlDecode(value: string): Uint8Array {\n const padded = value.replace(/-/g, '+').replace(/_/g, '/');\n const binary = atob(padded + '='.repeat((4 - (padded.length % 4)) % 4));\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\nasync function hmac(secret: string, message: string): Promise<Uint8Array> {\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n );\n return new Uint8Array(await crypto.subtle.sign('HMAC', key, encoder.encode(message)));\n}\n\nfunction timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return diff === 0;\n}\n\n\nexport async function hashRecipient(recipientId: string, companySecret: string): Promise<string> {\n const digest = await hmac(companySecret, `recipient:${recipientId}`);\n return base64UrlEncode(digest.slice(0, 16));\n}\n\nexport async function mintEmailToken(\n payload: EmailTokenPayload,\n key: SigningKey\n): Promise<string> {\n const body = base64UrlEncode(\n encoder.encode(\n JSON.stringify({\n c: payload.c,\n r: payload.r,\n k: payload.k,\n l: payload.l,\n v: key.version,\n })\n )\n );\n\n const signature = base64UrlEncode(await hmac(key.secret, body));\n return `${body}.${signature}`;\n}\n\nexport async function verifyEmailToken(\n token: string,\n keys: SigningKey[]\n): Promise<VerifiedToken> {\n const parts = token.split('.');\n if (parts.length !== 2 || !parts[0] || !parts[1]) {\n return { valid: false, reason: 'malformed' };\n }\n const [body, signature] = parts;\n\n let payload: EmailTokenPayload;\n try {\n payload = JSON.parse(new TextDecoder().decode(base64UrlDecode(body)));\n } catch {\n return { valid: false, reason: 'malformed' };\n }\n\n if (\n !payload ||\n typeof payload.c !== 'string' ||\n typeof payload.r !== 'string' ||\n !Array.isArray(payload.k) ||\n !payload.k.every((arm) => Number.isInteger(arm) && arm >= 0) ||\n !Number.isInteger(payload.l) ||\n (payload.l as number) < 0 ||\n !Number.isInteger(payload.v)\n ) {\n return { valid: false, reason: 'malformed' };\n }\n\n const key = keys.find((candidate) => candidate.version === payload.v);\n if (!key) return { valid: false, reason: 'unknown_key' };\n\n const expected = base64UrlEncode(await hmac(key.secret, body));\n if (!timingSafeEqual(expected, signature)) {\n return { valid: false, reason: 'bad_signature' };\n }\n\n return { valid: true, payload: payload as Required<EmailTokenPayload> };\n}\n\nexport function appendEmailToken(url: string, token: string): string {\n const hashIndex = url.indexOf('#');\n const fragment = hashIndex === -1 ? '' : url.slice(hashIndex);\n const base = hashIndex === -1 ? url : url.slice(0, hashIndex);\n const separator = base.includes('?') ? '&' : '?';\n\n return `${base}${separator}${EMAIL_TOKEN_PARAM}=${encodeURIComponent(token)}${fragment}`;\n}\n","\nexport interface PlanArm {\n treatmentId: string;\n armIndex: number;\n}\n\nexport interface PlanSlot {\n experimentId: string;\n name: string;\n arms: PlanArm[];\n probabilities: number[];\n configs: unknown[];\n}\n\nexport interface EmailPlan {\n campaignId: string;\n slots: PlanSlot[];\n linkTable: string[];\n salt: string;\n signingKey: { version: number; secret: string };\n refreshEvery: number;\n expiresAt: number;\n}\n\nexport async function uniformFor(\n salt: string,\n recipientId: string,\n slotIndex: number\n): Promise<number> {\n const key = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(salt),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n );\n const mac = new Uint8Array(\n await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(`${recipientId}:${slotIndex}`))\n );\n\n let value = 0;\n for (let i = 0; i < 6; i++) value = value * 256 + mac[i];\n return value / 2 ** 48;\n}\n\nexport function pickArm(probabilities: number[], uniform: number): number {\n if (probabilities.length === 0) return -1;\n\n const total = probabilities.reduce((sum, p) => sum + Math.max(p, 0), 0);\n if (total <= 0) {\n return Math.min(probabilities.length - 1, Math.floor(uniform * probabilities.length));\n }\n\n const target = uniform * total;\n let cumulative = 0;\n for (let i = 0; i < probabilities.length; i++) {\n cumulative += Math.max(probabilities[i], 0);\n if (target < cumulative) return i;\n }\n\n return probabilities.length - 1;\n}\n\nexport async function sampleCombination(\n plan: EmailPlan,\n recipientId: string\n): Promise<number[]> {\n const combination: number[] = [];\n\n for (let i = 0; i < plan.slots.length; i++) {\n const slot = plan.slots[i];\n const uniform = await uniformFor(plan.salt, recipientId, i);\n const chosen = pickArm(slot.probabilities, uniform);\n combination.push(chosen === -1 ? 0 : slot.arms[chosen]?.armIndex ?? 0);\n }\n\n return combination;\n}\n\nexport async function impressionManifest(\n plan: EmailPlan,\n recipientIds: string[]\n): Promise<Array<{ experimentId: string; treatmentId: string; count: number }>> {\n const counts = new Map<string, { experimentId: string; treatmentId: string; count: number }>();\n\n for (const recipientId of recipientIds) {\n for (let i = 0; i < plan.slots.length; i++) {\n const slot = plan.slots[i];\n const uniform = await uniformFor(plan.salt, recipientId, i);\n const chosen = pickArm(slot.probabilities, uniform);\n const arm = slot.arms[chosen === -1 ? 0 : chosen];\n if (!arm) continue;\n\n const key = `${slot.experimentId}:${arm.treatmentId}`;\n const entry = counts.get(key);\n if (entry) entry.count++;\n else\n counts.set(key, {\n experimentId: slot.experimentId,\n treatmentId: arm.treatmentId,\n count: 1,\n });\n }\n }\n\n return Array.from(counts.values());\n}\n","\nexport interface SlotBinding {\n $slot: string;\n}\n\nexport type Bound<T> = T | SlotBinding;\n\nexport function isSlotBinding(value: unknown): value is SlotBinding {\n return typeof value === 'object' && value !== null && typeof (value as SlotBinding).$slot === 'string';\n}\n\nexport interface HeadingBlock {\n type: 'heading';\n text: Bound<string>;\n level?: 1 | 2 | 3;\n align?: 'left' | 'center' | 'right';\n color?: Bound<string>;\n}\n\nexport interface TextBlock {\n type: 'text';\n text: Bound<string>;\n align?: 'left' | 'center' | 'right';\n color?: Bound<string>;\n}\n\nexport interface ButtonBlock {\n type: 'button';\n text: Bound<string>;\n href: Bound<string>;\n backgroundColor?: Bound<string>;\n textColor?: Bound<string>;\n align?: 'left' | 'center' | 'right';\n}\n\nexport interface ImageBlock {\n type: 'image';\n src: Bound<string>;\n alt: Bound<string>;\n href?: Bound<string>;\n width?: number;\n}\n\nexport interface DividerBlock {\n type: 'divider';\n color?: string;\n}\n\nexport interface SpacerBlock {\n type: 'spacer';\n height?: number;\n}\n\nexport type EmailBlock =\n | HeadingBlock\n | TextBlock\n | ButtonBlock\n | ImageBlock\n | DividerBlock\n | SpacerBlock;\n\nexport interface EmailDocument {\n subject: Bound<string>;\n preheader?: Bound<string>;\n blocks: EmailBlock[];\n backgroundColor?: string;\n contentWidth?: number;\n}\n\nconst CONTENT_WIDTH = 600;\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\nfunction resolve(value: Bound<string> | undefined, slots: Record<string, string>, missing: string[]): string {\n if (value === undefined) return '';\n if (!isSlotBinding(value)) return value;\n\n const resolved = slots[value.$slot];\n if (resolved === undefined) {\n missing.push(value.$slot);\n return '';\n }\n return resolved;\n}\n\nexport function extractDocumentSlots(document: EmailDocument): string[] {\n const names: string[] = [];\n const seen = new Set<string>();\n\n const visit = (value: unknown) => {\n if (isSlotBinding(value)) {\n if (!seen.has(value.$slot)) {\n seen.add(value.$slot);\n names.push(value.$slot);\n }\n return;\n }\n if (Array.isArray(value)) value.forEach(visit);\n else if (typeof value === 'object' && value !== null) Object.values(value).forEach(visit);\n };\n\n visit(document.subject);\n visit(document.preheader);\n document.blocks.forEach(visit);\n\n return names;\n}\n\nfunction renderBlock(block: EmailBlock, slots: Record<string, string>, missing: string[]): string {\n const align = (block as { align?: string }).align ?? 'left';\n\n switch (block.type) {\n case 'heading': {\n const size = block.level === 3 ? 18 : block.level === 2 ? 22 : 28;\n const color = escapeHtml(resolve(block.color, slots, missing) || '#111111');\n return `<tr><td style=\"padding:12px 24px;text-align:${align};font-family:Arial,Helvetica,sans-serif;font-size:${size}px;line-height:1.3;font-weight:bold;color:${color}\">${escapeHtml(\n resolve(block.text, slots, missing)\n )}</td></tr>`;\n }\n\n case 'text': {\n const color = escapeHtml(resolve(block.color, slots, missing) || '#333333');\n return `<tr><td style=\"padding:8px 24px;text-align:${align};font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:1.5;color:${color}\">${escapeHtml(\n resolve(block.text, slots, missing)\n )}</td></tr>`;\n }\n\n case 'button': {\n const href = escapeHtml(resolve(block.href, slots, missing));\n const label = escapeHtml(resolve(block.text, slots, missing));\n const bg = escapeHtml(resolve(block.backgroundColor, slots, missing) || '#1a73e8');\n const fg = escapeHtml(resolve(block.textColor, slots, missing) || '#ffffff');\n\n return `<tr><td style=\"padding:16px 24px;text-align:${align}\">\n<!--[if mso]><v:roundrect xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:w=\"urn:schemas-microsoft-com:office:word\" href=\"${href}\" style=\"height:44px;v-text-anchor:middle;width:220px;\" arcsize=\"12%\" strokecolor=\"${bg}\" fillcolor=\"${bg}\"><w:anchorlock/><center style=\"color:${fg};font-family:Arial,sans-serif;font-size:16px;font-weight:bold;\">${label}</center></v:roundrect><![endif]-->\n<!--[if !mso]><!--><a href=\"${href}\" style=\"display:inline-block;padding:13px 28px;background-color:${bg};color:${fg};font-family:Arial,Helvetica,sans-serif;font-size:16px;font-weight:bold;text-decoration:none;border-radius:6px\">${label}</a><!--<![endif]-->\n</td></tr>`;\n }\n\n case 'image': {\n const src = escapeHtml(resolve(block.src, slots, missing));\n const alt = escapeHtml(resolve(block.alt, slots, missing));\n const width = block.width ?? CONTENT_WIDTH;\n const img = `<img src=\"${src}\" alt=\"${alt}\" width=\"${width}\" style=\"display:block;border:0;outline:none;text-decoration:none;max-width:100%;height:auto\" />`;\n const href = block.href ? escapeHtml(resolve(block.href, slots, missing)) : '';\n\n return `<tr><td style=\"padding:0;text-align:center\">${\n href ? `<a href=\"${href}\">${img}</a>` : img\n }</td></tr>`;\n }\n\n case 'divider':\n return `<tr><td style=\"padding:12px 24px\"><table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\"><tr><td style=\"border-top:1px solid ${escapeHtml(\n block.color ?? '#e5e5e5'\n )};font-size:0;line-height:0\">&nbsp;</td></tr></table></td></tr>`;\n\n case 'spacer':\n return `<tr><td style=\"height:${block.height ?? 24}px;font-size:0;line-height:0\">&nbsp;</td></tr>`;\n\n default:\n return '';\n }\n}\n\nexport interface RenderedDocument {\n subject: string;\n html: string;\n missingSlots: string[];\n}\n\nexport function renderEmailDocument(\n document: EmailDocument,\n slots: Record<string, string>\n): RenderedDocument {\n const missingSlots: string[] = [];\n const width = document.contentWidth ?? CONTENT_WIDTH;\n const background = escapeHtml(document.backgroundColor ?? '#f4f4f5');\n\n const subject = resolve(document.subject, slots, missingSlots);\n const preheader = resolve(document.preheader, slots, missingSlots);\n\n const body = document.blocks.map((block) => renderBlock(block, slots, missingSlots)).join('\\n');\n\n const html = `<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<meta name=\"x-apple-disable-message-reformatting\" />\n<title>${escapeHtml(subject)}</title>\n<!--[if mso]><xml><o:OfficeDocumentSettings><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]-->\n</head>\n<body style=\"margin:0;padding:0;background-color:${background}\">\n<div style=\"display:none;font-size:0;line-height:0;max-height:0;overflow:hidden;mso-hide:all\">${escapeHtml(\n preheader\n )}</div>\n<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"background-color:${background}\">\n<tr><td align=\"center\" style=\"padding:24px 12px\">\n<table role=\"presentation\" width=\"${width}\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"width:${width}px;max-width:100%;background-color:#ffffff;border-radius:8px\">\n${body}\n</table>\n</td></tr>\n</table>\n</body>\n</html>`;\n\n return { subject, html, missingSlots };\n}\n","export const STOREFRONT_KINDS = ['text', 'image', 'ordering', 'offer'] as const;\nexport type StorefrontKind = (typeof STOREFRONT_KINDS)[number];\n\nexport const PLACEMENTS_BY_KIND = {\n text: ['hero_headline', 'hero_subhead', 'cta_primary', 'banner_text', 'badge_text'],\n image: ['hero_image', 'banner_image', 'feature_image'],\n ordering: ['product_list_primary', 'product_list_secondary'],\n offer: ['offer_banner'],\n} as const satisfies Record<StorefrontKind, readonly string[]>;\n\nexport type Placement = (typeof PLACEMENTS_BY_KIND)[StorefrontKind][number];\n\nexport const PLACEMENTS: readonly Placement[] = STOREFRONT_KINDS.flatMap(\n (kind) => PLACEMENTS_BY_KIND[kind]\n);\n\nexport function isPlacement(value: unknown): value is Placement {\n return typeof value === 'string' && (PLACEMENTS as readonly string[]).includes(value);\n}\n\nexport function kindOfPlacement(placement: Placement): StorefrontKind {\n const found = STOREFRONT_KINDS.find((kind) =>\n (PLACEMENTS_BY_KIND[kind] as readonly string[]).includes(placement)\n );\n if (!found) throw new Error(`Unknown placement: ${placement}`);\n return found;\n}\n\nexport const CONTENT_TYPE_BY_KIND = {\n text: { contentType: 'TEXT', contentSubtype: 'plain_text' },\n image: { contentType: 'FILE', contentSubtype: 'image' },\n ordering: { contentType: 'CODE', contentSubtype: 'json' },\n offer: { contentType: 'CODE', contentSubtype: 'json' },\n} as const satisfies Record<StorefrontKind, { contentType: string; contentSubtype: string }>;\n\nexport const MIN_ORDERING_ITEMS = 2;\nexport const MAX_ORDERING_ITEMS = 12;\n\nexport interface ImageContent {\n url: string;\n alt: string;\n width: number;\n height: number;\n sourceId: string;\n}\n\nexport interface OfferContent {\n discountId: string;\n code: string;\n headline: string;\n subtext: string;\n}\n\nexport type TreatmentContent =\n | { kind: 'text'; text: string }\n | { kind: 'image'; image: ImageContent }\n | { kind: 'ordering'; productIds: string[] }\n | { kind: 'offer'; offer: OfferContent | null };\n\nexport function isShopifyCdnUrl(value: string): boolean {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return false;\n }\n return url.protocol === 'https:' && url.hostname === 'cdn.shopify.com';\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction isPositiveInt(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value > 0;\n}\n\nfunction parseImage(raw: unknown): ImageContent | null {\n if (!isRecord(raw)) return null;\n const { url, alt, width, height, sourceId } = raw;\n if (typeof url !== 'string' || !isShopifyCdnUrl(url)) return null;\n if (typeof alt !== 'string') return null;\n if (!isPositiveInt(width) || !isPositiveInt(height)) return null;\n if (!isNonEmptyString(sourceId)) return null;\n return { url, alt, width, height, sourceId };\n}\n\nfunction parseOrdering(raw: unknown): string[] | null {\n if (!Array.isArray(raw)) return null;\n if (raw.length < MIN_ORDERING_ITEMS || raw.length > MAX_ORDERING_ITEMS) return null;\n if (!raw.every((id) => typeof id === 'string' && /^\\d+$/.test(id))) return null;\n if (new Set(raw).size !== raw.length) return null;\n return raw as string[];\n}\n\nfunction parseOffer(raw: unknown): OfferContent | null | undefined {\n if (raw === null) return null;\n if (!isRecord(raw)) return undefined;\n const { discountId, code, headline, subtext } = raw;\n if (!isNonEmptyString(discountId) || !isNonEmptyString(code)) return undefined;\n if (typeof headline !== 'string' || typeof subtext !== 'string') return undefined;\n return { discountId, code, headline, subtext };\n}\n\nexport function parseTreatmentContent(\n kind: StorefrontKind,\n config: unknown\n): TreatmentContent | null {\n if (!isRecord(config)) return null;\n const content = config.content;\n switch (kind) {\n case 'text':\n return isNonEmptyString(content) ? { kind, text: content } : null;\n case 'image': {\n const image = parseImage(content);\n return image ? { kind, image } : null;\n }\n case 'ordering': {\n const productIds = parseOrdering(content);\n return productIds ? { kind, productIds } : null;\n }\n case 'offer': {\n const offer = parseOffer(content);\n return offer === undefined ? null : { kind, offer };\n }\n }\n}\n\nexport function serializeTreatmentConfig(content: TreatmentContent): { content: unknown } {\n switch (content.kind) {\n case 'text':\n return { content: content.text };\n case 'image':\n return { content: content.image };\n case 'ordering':\n return { content: content.productIds };\n case 'offer':\n return { content: content.offer };\n }\n}\n","const SCHEME = /^[a-z][a-z0-9+.-]*:\\/\\//i;\n\nconst isWildcard = (segment: string) => segment === '*' || segment === '**';\n\nfunction segmentsOf(path: string): string[] {\n return path.split('/').filter(Boolean);\n}\n\nexport function normalizeConversionPath(input: string | undefined): string | undefined {\n const trimmed = input?.trim();\n if (!trimmed) return undefined;\n if (/[?#]/.test(trimmed)) {\n throw new Error('Leave out the query string: enter a path like /thank-you or /orders/*/thanks');\n }\n const withScheme = SCHEME.test(trimmed) ? trimmed : `https://placeholder.invalid/${trimmed.replace(/^\\/+/, '')}`;\n let pathname: string;\n try {\n pathname = new URL(withScheme).pathname;\n } catch {\n throw new Error('Enter the conversion page as a path like /thank-you');\n }\n const segments = segmentsOf(pathname);\n if (segments.some((segment) => segment.includes('*') && !isWildcard(segment))) {\n throw new Error('Use * for a whole path segment, like /orders/*/thanks');\n }\n if (segments.length > 0 && segments.every(isWildcard)) {\n throw new Error('That pattern matches every page. Add a fixed part, like /orders/*/thanks');\n }\n return pathname.length > 1 ? pathname.replace(/\\/+$/, '') : pathname;\n}\n\nfunction matchSegments(pattern: string[], path: string[]): boolean {\n if (pattern.length === 0) return path.length === 0;\n const [head, ...rest] = pattern;\n if (head === '**') {\n for (let skip = 0; skip <= path.length; skip++) {\n if (matchSegments(rest, path.slice(skip))) return true;\n }\n return false;\n }\n if (path.length === 0) return false;\n return (head === '*' || head === path[0]) && matchSegments(rest, path.slice(1));\n}\n\nexport function matchesConversionPath(pattern: string, pathname: string): boolean {\n return matchSegments(segmentsOf(pattern), segmentsOf(pathname));\n}\n","import { matchesConversionPath, normalizeConversionPath } from './conversion-path.js';\n\nexport const SITE_TARGET_KINDS = ['text', 'image'] as const;\nexport type SiteTargetKind = (typeof SITE_TARGET_KINDS)[number];\n\nexport interface SiteTarget {\n page: string;\n selector: string;\n tag: string;\n kind: SiteTargetKind;\n original: string;\n}\n\nexport type FunnelGoal =\n | { kind: 'page'; page: string }\n | { kind: 'click'; page: string; selector: string; tag: string; label: string };\n\nconst TAG_NAME = /^[a-z][a-z0-9-]*$/;\n\nfunction collapseWhitespace(raw: string): string {\n return raw.replace(/\\s+/g, ' ').trim();\n}\n\nfunction requireText(raw: string, message: string): string {\n const value = collapseWhitespace(raw);\n if (!value) throw new Error(message);\n return value;\n}\n\nfunction requirePage(raw: string): string {\n const page = normalizeConversionPath(raw);\n if (!page) throw new Error('Enter the page as a path like /pricing');\n return page;\n}\n\nfunction requireTag(raw: string): string {\n const tag = raw.trim().toLowerCase();\n if (!TAG_NAME.test(tag)) throw new Error('Enter the element tag, like h1 or button');\n return tag;\n}\n\nexport function parseSiteContent(config: unknown): string | null {\n if (typeof config !== 'object' || config === null) return null;\n const { content, value } = config as { content?: unknown; value?: unknown };\n for (const candidate of [content, value]) {\n if (typeof candidate === 'string' && candidate.trim()) return candidate;\n }\n return null;\n}\n\nexport function normalizeSiteTarget(target: SiteTarget): SiteTarget {\n if (!SITE_TARGET_KINDS.includes(target.kind)) throw new Error('A target is text or an image');\n return {\n page: requirePage(target.page),\n selector: requireText(target.selector, 'The picked element has no selector'),\n tag: requireTag(target.tag),\n kind: target.kind,\n original: requireText(target.original, 'The picked element has no content to replace'),\n };\n}\n\nexport function normalizeFunnelGoal(goal: FunnelGoal): FunnelGoal {\n const page = requirePage(goal.page);\n if (goal.kind === 'page') return { kind: 'page', page };\n return {\n kind: 'click',\n page,\n selector: requireText(goal.selector, 'The picked element has no selector'),\n tag: requireTag(goal.tag),\n label: collapseWhitespace(goal.label),\n };\n}\n\nexport function targetMatchesPage(target: Pick<SiteTarget, 'page'>, pathname: string): boolean {\n return matchesConversionPath(target.page, pathname);\n}\n\nexport function goalMatchesPage(goal: Pick<FunnelGoal, 'page'>, pathname: string): boolean {\n return matchesConversionPath(goal.page, pathname);\n}\n\nexport const MAX_ALTERNATIVES = 5;\nexport const MAX_TEXT_ALTERNATIVE_LENGTH = 300;\nexport const MAX_IMAGE_URL_LENGTH = 2048;\n\nexport interface PickDraft {\n target?: SiteTarget;\n alternatives: string[];\n goal?: FunnelGoal;\n}\n\nexport interface PickState {\n funnelId: string;\n siteHost: string;\n expiresAt: number;\n draft: PickDraft;\n funnelGoal: FunnelGoal | null;\n experimentId: string | null;\n runningExperiments: number;\n}\n\nexport const SUGGESTED_SITE_EXPERIMENTS = 3;\n\nexport function moreExperimentsSentence(running: number, suggested: number): string {\n const noun = (count: number) => (count === 1 ? 'experiment' : 'experiments');\n const status = `${running} ${noun(running)} running on your site.`;\n if (running >= suggested) return `${status} Keep going if you have more elements to test.`;\n const remaining = suggested - running;\n return `${status} Pick ${remaining} more while you are here. Each element you test is another place to find a win.`;\n}\n\nexport type PickStep = 'pick' | 'alternatives' | 'goal' | 'ready' | 'live';\n\nexport function pickStep(state: Pick<PickState, 'draft' | 'funnelGoal' | 'experimentId'>): PickStep {\n if (state.experimentId !== null) return 'live';\n if (!state.draft.target) return 'pick';\n if (state.draft.alternatives.length === 0) return 'alternatives';\n if (!state.draft.goal && !state.funnelGoal) return 'goal';\n return 'ready';\n}\n\nexport function parseAlternative(kind: SiteTargetKind, raw: string): string {\n if (kind === 'text') {\n const text = requireText(raw, 'Type the text to try');\n if (text.length > MAX_TEXT_ALTERNATIVE_LENGTH) {\n throw new Error(`Keep each option under ${MAX_TEXT_ALTERNATIVE_LENGTH} characters`);\n }\n return text;\n }\n const trimmed = raw.trim();\n if (trimmed.length > MAX_IMAGE_URL_LENGTH) throw new Error('That image address is too long');\n let url: URL;\n try {\n url = new URL(trimmed);\n } catch {\n throw new Error('Enter the image address as a link that starts with https://');\n }\n if (url.protocol !== 'https:') throw new Error('Enter the image address as a link that starts with https://');\n return url.href;\n}\n\nexport const PICK_CHANNEL = 'bandit-pick';\nexport const PICK_PROTOCOL_VERSION = 1;\n\nexport const PICK_MODES = ['idle', 'target', 'goal-click'] as const;\nexport type PickMode = (typeof PICK_MODES)[number];\n\nexport const PICK_REJECTIONS = ['not-text-or-image', 'has-markup', 'not-clickable', 'no-selector'] as const;\nexport type PickRejection = (typeof PICK_REJECTIONS)[number];\n\nexport type ClickFunnelGoal = Extract<FunnelGoal, { kind: 'click' }>;\n\nexport type PickerToPanel =\n | { type: 'navigated'; page: string }\n | { type: 'target-picked'; target: SiteTarget }\n | { type: 'goal-picked'; goal: ClickFunnelGoal }\n | { type: 'rejected'; reason: PickRejection }\n | { type: 'cancelled' };\n\nexport type PanelToPicker =\n | { type: 'hello' }\n | { type: 'mode'; mode: PickMode }\n | { type: 'preview'; target: SiteTarget; content: string }\n | { type: 'clear-preview' }\n | { type: 'finish' }\n | { type: 'renew'; token: string };\n\nexport const PICK_TOKEN_PREFIX = 'bpick_';\n\nexport interface PickEnvelope<M> {\n channel: typeof PICK_CHANNEL;\n v: typeof PICK_PROTOCOL_VERSION;\n msg: M;\n}\n\nexport function pickEnvelope<M extends PickerToPanel | PanelToPicker>(msg: M): PickEnvelope<M> {\n return { channel: PICK_CHANNEL, v: PICK_PROTOCOL_VERSION, msg };\n}\n\ntype Fields = Record<string, unknown>;\n\nfunction isFields(value: unknown): value is Fields {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction isOneOf<T extends string>(options: readonly T[], value: unknown): value is T {\n return options.some((option) => option === value);\n}\n\nfunction openEnvelope(data: unknown): Fields | null {\n if (!isFields(data) || data.channel !== PICK_CHANNEL || data.v !== PICK_PROTOCOL_VERSION) return null;\n return isFields(data.msg) ? data.msg : null;\n}\n\nfunction asSiteTarget(value: unknown): SiteTarget | null {\n if (!isFields(value)) return null;\n const { page, selector, tag, kind, original } = value;\n if (!isString(page) || !isString(selector) || !isString(tag) || !isString(original)) return null;\n if (!isOneOf(SITE_TARGET_KINDS, kind)) return null;\n return { page, selector, tag, kind, original };\n}\n\nfunction asClickGoal(value: unknown): ClickFunnelGoal | null {\n if (!isFields(value) || value.kind !== 'click') return null;\n const { page, selector, tag, label } = value;\n if (!isString(page) || !isString(selector) || !isString(tag) || !isString(label)) return null;\n return { kind: 'click', page, selector, tag, label };\n}\n\nexport function parsePickerMessage(data: unknown): PickerToPanel | null {\n const msg = openEnvelope(data);\n switch (msg?.type) {\n case 'navigated':\n return isString(msg.page) ? { type: 'navigated', page: msg.page } : null;\n case 'target-picked': {\n const target = asSiteTarget(msg.target);\n return target ? { type: 'target-picked', target } : null;\n }\n case 'goal-picked': {\n const goal = asClickGoal(msg.goal);\n return goal ? { type: 'goal-picked', goal } : null;\n }\n case 'rejected':\n return isOneOf(PICK_REJECTIONS, msg.reason) ? { type: 'rejected', reason: msg.reason } : null;\n case 'cancelled':\n return { type: 'cancelled' };\n default:\n return null;\n }\n}\n\nexport function parsePanelMessage(data: unknown): PanelToPicker | null {\n const msg = openEnvelope(data);\n switch (msg?.type) {\n case 'hello':\n return { type: 'hello' };\n case 'mode':\n return isOneOf(PICK_MODES, msg.mode) ? { type: 'mode', mode: msg.mode } : null;\n case 'preview': {\n const target = asSiteTarget(msg.target);\n return target && isString(msg.content) ? { type: 'preview', target, content: msg.content } : null;\n }\n case 'clear-preview':\n return { type: 'clear-preview' };\n case 'finish':\n return { type: 'finish' };\n case 'renew':\n return isString(msg.token) && msg.token.startsWith(PICK_TOKEN_PREFIX) ? { type: 'renew', token: msg.token } : null;\n default:\n return null;\n }\n}\n","export type SitePlatformPath = 'shopify' | 'script' | 'code'\n\nexport interface SitePlatform {\n id: string\n label: string\n path: SitePlatformPath\n codeBox: string\n steps: readonly string[]\n planNote?: string\n}\n\nexport const SITE_PLATFORMS = [\n { id: 'shopify', label: 'Shopify', path: 'shopify', codeBox: '', steps: [] },\n {\n id: 'webflow',\n label: 'Webflow',\n path: 'script',\n codeBox: 'Site settings, Custom code, Head code. Save, then Publish.',\n steps: [\n 'Open the site in Webflow and click the gear for Site settings.',\n 'Open the Custom code tab.',\n 'Paste the script into the Head code box.',\n 'Click Save changes.',\n 'Publish the site. Custom code only goes live when you publish.',\n ],\n planNote: 'Needs a paid Site plan.',\n },\n {\n id: 'wordpress',\n label: 'WordPress',\n path: 'script',\n codeBox: 'Install the free WPCode plugin, then Code Snippets, Header & Footer, Header box.',\n steps: [\n 'In your WordPress admin go to Plugins, Add New, and search for WPCode. Install and activate it.',\n 'In the left menu open Code Snippets, then Header & Footer.',\n 'Paste the script into the Header box.',\n 'Click Save Changes.',\n 'Load your site once so the tag is in the page.',\n ],\n planNote: 'WordPress.com needs the Business plan for plugins.',\n },\n {\n id: 'squarespace',\n label: 'Squarespace',\n path: 'script',\n codeBox: 'Settings, Website Tools, Code Injection, Header.',\n steps: [\n 'From the Home menu open Settings, then Website Tools. On older sites it is under Advanced.',\n 'Click Code Injection.',\n 'Paste the script into the Header box.',\n 'Click Save.',\n ],\n planNote: 'Needs the Core plan or higher (Business on older plans).',\n },\n {\n id: 'framer',\n label: 'Framer',\n path: 'script',\n codeBox: 'Site Settings, General, Custom Code, Start of head. Publish after saving.',\n steps: [\n 'Open your project and click the gear for Site Settings.',\n 'On the General tab scroll to Custom Code.',\n 'Paste the script under Start of head tag.',\n 'Click Save.',\n 'Publish the site.',\n ],\n planNote: 'Needs a paid Site plan.',\n },\n {\n id: 'wix',\n label: 'Wix',\n path: 'script',\n codeBox: 'Settings, Custom Code, Add Custom Code. Choose All pages and place it in Head.',\n steps: [\n 'In your Wix dashboard open Settings, then Custom Code under Advanced.',\n 'Click Add Custom Code.',\n 'Paste the script and name it Bandit.',\n 'Under Add Code to Pages choose All pages and Load code once.',\n 'Under Place Code in choose Head.',\n 'Click Apply.',\n ],\n planNote: 'Needs a Premium plan with a connected domain.',\n },\n {\n id: 'other',\n label: 'Something else',\n path: 'script',\n codeBox: 'Any custom-code or head-injection box, or the HTML template, on every page.',\n steps: [\n 'Find where your builder lets you add code to the head of every page. It is usually called Custom code, Code injection, Header scripts, or Tracking code.',\n 'Paste the script there.',\n 'Save and publish.',\n 'If you edit the HTML yourself, paste it before the closing head tag of every page template.',\n ],\n },\n {\n id: 'nextjs_react',\n label: 'Next.js / React',\n path: 'code',\n codeBox: \"Your root layout's head.\",\n steps: ['Add the tag to the head of your root layout.'],\n },\n {\n id: 'plain_html',\n label: 'Plain HTML',\n path: 'code',\n codeBox: 'The head of every page.',\n steps: ['Paste the tag into the head of every page.'],\n },\n {\n id: 'tag_manager',\n label: 'Tag manager',\n path: 'code',\n codeBox: 'A Custom HTML tag that fires on all pages.',\n steps: ['Create a Custom HTML tag with the script and fire it on all pages.'],\n },\n] as const satisfies readonly SitePlatform[]\n\nexport type SitePlatformId = (typeof SITE_PLATFORMS)[number]['id']\n\nexport const SITE_PLATFORM_IDS: readonly SitePlatformId[] = SITE_PLATFORMS.map((platform) => platform.id)\n\ntype ScriptEntry = Extract<(typeof SITE_PLATFORMS)[number], { path: 'script' }>\n\nexport type ScriptPlatform = SitePlatform & { id: ScriptEntry['id']; path: 'script' }\n\nexport const SCRIPT_PLATFORMS: readonly ScriptPlatform[] = SITE_PLATFORMS.filter(\n (platform): platform is ScriptEntry => platform.path === 'script',\n)\n\nexport function platformById(id: string | undefined): SitePlatform | undefined {\n return SITE_PLATFORMS.find((platform) => platform.id === id)\n}\n","export const ARRIVAL_CHANNELS = ['ad', 'email', 'social', 'search', 'referral', 'direct'] as const\nexport type ArrivalChannel = (typeof ARRIVAL_CHANNELS)[number]\n\nexport const ARRIVAL_TTL_MS = 30 * 24 * 60 * 60 * 1000\nexport const MAX_ARRIVAL_VALUE_LENGTH = 40\nexport const MAX_REFERRER_HOST_LENGTH = 100\nexport const MAX_RAW_TOUCH_VALUE_LENGTH = 200\n\nexport type ArrivalTouch = {\n utmSource?: string\n utmMedium?: string\n utmCampaign?: string\n utmContent?: string\n trackingParam?: string\n klaviyoExchangeId?: string\n referrerHost?: string\n}\n\nexport type Arrival = {\n source?: string\n medium?: string\n campaign?: string\n content?: string\n referrerHost?: string\n channel?: ArrivalChannel\n}\n\nexport const UTM_PARAMS = [\n ['utmSource', 'utm_source'],\n ['utmMedium', 'utm_medium'],\n ['utmCampaign', 'utm_campaign'],\n ['utmContent', 'utm_content'],\n] as const\n\nexport const TRACKING_PARAM_SOURCES: Readonly<Record<string, string>> = {\n gclid: 'google',\n gbraid: 'google',\n wbraid: 'google',\n fbclid: 'facebook',\n ttclid: 'tiktok',\n msclkid: 'bing',\n li_fat_id: 'linkedin',\n twclid: 'x',\n epik: 'pinterest',\n sccid: 'snapchat',\n _kx: 'klaviyo',\n mc_eid: 'mailchimp',\n}\n\ntype ArrivalSignals = Omit<Arrival, 'channel'> & { trackingParam?: string }\n\ntype ChannelRule = {\n channel: ArrivalChannel\n field: 'medium' | 'trackingParam' | 'source' | 'referrerHost'\n values: readonly string[]\n}\n\nexport const CHANNEL_RULES: readonly ChannelRule[] = [\n {\n channel: 'ad',\n field: 'medium',\n values: [\n 'cpc', 'ppc', 'paid', 'paid-social', 'paidsocial', 'paid-search', 'paidsearch', 'display', 'cpm',\n 'cpv', 'cpa', 'banner', 'ad', 'ads', 'paid-media', 'retargeting', 'affiliate',\n ],\n },\n { channel: 'email', field: 'medium', values: ['email', 'e-mail', 'newsletter', 'sms'] },\n { channel: 'social', field: 'medium', values: ['social', 'social-media', 'organic-social', 'sm'] },\n { channel: 'search', field: 'medium', values: ['organic', 'organic-search', 'seo'] },\n { channel: 'referral', field: 'medium', values: ['referral', 'partner'] },\n {\n channel: 'ad',\n field: 'trackingParam',\n values: ['gclid', 'gbraid', 'wbraid', 'fbclid', 'ttclid', 'msclkid', 'li_fat_id', 'twclid', 'epik', 'sccid'],\n },\n { channel: 'email', field: 'trackingParam', values: ['_kx', 'mc_eid'] },\n {\n channel: 'email',\n field: 'source',\n values: [\n 'klaviyo', 'mailchimp', 'sendgrid', 'hubspot', 'hs-email', 'braze', 'iterable', 'customer.io',\n 'customerio', 'customer-io', 'omnisend', 'email', 'newsletter',\n ],\n },\n {\n channel: 'social',\n field: 'source',\n values: [\n 'facebook', 'fb', 'instagram', 'ig', 'tiktok', 'twitter', 'x', 'linkedin', 'pinterest', 'reddit',\n 'youtube', 'snapchat', 'threads',\n ],\n },\n { channel: 'search', field: 'source', values: ['google', 'bing', 'duckduckgo', 'yahoo', 'baidu', 'yandex', 'ecosia'] },\n {\n channel: 'email',\n field: 'referrerHost',\n values: ['mail.google.com', 'outlook.live.com', 'outlook.office.com', 'outlook.office365.com', 'mail.yahoo.com'],\n },\n {\n channel: 'social',\n field: 'referrerHost',\n values: [\n 'facebook.com', 'fb.com', 'fb.me', 'instagram.com', 't.co', 'twitter.com', 'x.com', 'linkedin.com',\n 'lnkd.in', 'pinterest.com', 'pin.it', 'reddit.com', 'youtube.com', 'youtu.be', 'tiktok.com',\n 'snapchat.com', 'threads.net',\n ],\n },\n {\n channel: 'search',\n field: 'referrerHost',\n values: ['google', 'bing.com', 'duckduckgo.com', 'yahoo', 'baidu.com', 'yandex', 'ecosia.org', 'search.brave.com'],\n },\n]\n\nexport function normalizeArrivalValue(raw: string | undefined): string | undefined {\n const value = raw\n ?.trim()\n .toLowerCase()\n .replace(/[\\s_]+/g, '-')\n .slice(0, MAX_ARRIVAL_VALUE_LENGTH)\n return value ? value : undefined\n}\n\nexport function normalizeHost(raw: string | undefined): string | undefined {\n const host = raw?.trim().toLowerCase().replace(/^www\\./, '').slice(0, MAX_REFERRER_HOST_LENGTH)\n return host ? host : undefined\n}\n\nfunction hostMatches(host: string, pattern: string): boolean {\n if (pattern.includes('.')) return host === pattern || host.endsWith(`.${pattern}`)\n return host.split('.').slice(0, -1).includes(pattern)\n}\n\nexport const PASS_THROUGH_HOSTS: readonly string[] = [\n 'checkout.stripe.com', 'stripe.com', 'paypal.com', 'shop.app', 'shopify.com', 'klarna.com', 'afterpay.com',\n 'affirm.com', 'accounts.google.com', 'appleid.apple.com', 'login.microsoftonline.com', 'login.live.com',\n 'auth0.com', 'clerk.accounts.dev', 'clerk.com', 'okta.com',\n]\n\nexport function isPassThroughHost(host: string): boolean {\n return PASS_THROUGH_HOSTS.some((pattern) => hostMatches(host, pattern))\n}\n\nfunction ruleMatches(rule: ChannelRule, signals: ArrivalSignals): boolean {\n const value = signals[rule.field]\n if (!value) return false\n if (rule.field === 'referrerHost') return rule.values.some((pattern) => hostMatches(value, pattern))\n return rule.values.includes(value)\n}\n\nexport function channelOf(signals: ArrivalSignals): ArrivalChannel | undefined {\n const { trackingParam, ...arrival } = signals\n if (!trackingParam && Object.values(arrival).every((value) => !value)) return 'direct'\n const rule = CHANNEL_RULES.find((candidate) => ruleMatches(candidate, signals))\n if (rule) return rule.channel\n return arrival.referrerHost ? 'referral' : undefined\n}\n\nfunction arrivalReferrerHost(raw: string | undefined): string | undefined {\n const host = normalizeHost(raw)\n return host && !isPassThroughHost(host) ? host : undefined\n}\n\nexport function arrivalFromTouch(touch: ArrivalTouch): Arrival {\n const named = touch.trackingParam?.toLowerCase()\n const trackingParam = named && Object.hasOwn(TRACKING_PARAM_SOURCES, named) ? named : undefined\n const fields = {\n source: normalizeArrivalValue(touch.utmSource) ?? (trackingParam ? TRACKING_PARAM_SOURCES[trackingParam] : undefined),\n medium: normalizeArrivalValue(touch.utmMedium),\n campaign: normalizeArrivalValue(touch.utmCampaign),\n content: normalizeArrivalValue(touch.utmContent),\n referrerHost: arrivalReferrerHost(touch.referrerHost),\n }\n const present = Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined))\n const channel = channelOf({ ...present, ...(trackingParam ? { trackingParam } : {}) })\n return { ...present, ...(channel ? { channel } : {}) }\n}\n\nexport function isDirectTouch(touch: ArrivalTouch): boolean {\n return !touch.utmSource && !touch.utmMedium && !touch.utmCampaign && !touch.utmContent && !touch.trackingParam && !touch.referrerHost\n}\n\nfunction safeUrl(raw: string): URL | undefined {\n try {\n return new URL(raw)\n } catch {\n return undefined\n }\n}\n\nexport function touchFromPage(page: { href: string; referrer: string }): ArrivalTouch {\n const url = safeUrl(page.href)\n if (!url) return {}\n const params = new Map<string, string>()\n url.searchParams.forEach((value, key) => {\n const name = key.toLowerCase()\n const trimmed = value.trim()\n if (trimmed && !params.has(name)) params.set(name, trimmed.slice(0, MAX_RAW_TOUCH_VALUE_LENGTH))\n })\n const touch: ArrivalTouch = {}\n for (const [field, param] of UTM_PARAMS) {\n const value = params.get(param)\n if (value) touch[field] = value\n }\n const trackingParam = Object.keys(TRACKING_PARAM_SOURCES).find((name) => params.has(name))\n if (trackingParam) touch.trackingParam = trackingParam\n const klaviyoExchangeId = params.get('_kx')\n if (klaviyoExchangeId) touch.klaviyoExchangeId = klaviyoExchangeId\n const referrerHost = normalizeHost(safeUrl(page.referrer)?.hostname)\n if (referrerHost && referrerHost !== normalizeHost(url.hostname)) touch.referrerHost = referrerHost\n return touch\n}\n\nexport type StoredArrival = { signature: string; sessionId: string; at: number }\n\nexport function touchSignature(touch: ArrivalTouch): string {\n return JSON.stringify([\n touch.utmSource ?? '', touch.utmMedium ?? '', touch.utmCampaign ?? '', touch.utmContent ?? '',\n touch.trackingParam ?? '', touch.referrerHost ?? '', touch.klaviyoExchangeId ?? '',\n ])\n}\n\nexport function shouldReportTouch(touch: ArrivalTouch, stored: StoredArrival | null, sessionId: string, now: number): boolean {\n if (!stored) return true\n if (isDirectTouch(touch)) return now - stored.at >= ARRIVAL_TTL_MS\n return stored.signature !== touchSignature(touch) || stored.sessionId !== sessionId\n}\n\nexport function isArrivalActive(startedAt: number, at: number): boolean {\n return startedAt <= at && at - startedAt < ARRIVAL_TTL_MS\n}\n\nexport const CHANNEL_LABELS: Readonly<Record<ArrivalChannel, string>> = {\n ad: 'Ad',\n email: 'Email',\n social: 'Social',\n search: 'Search',\n referral: 'Referral',\n direct: 'Direct',\n}\n\nexport const UNKNOWN_SOURCE_KEY = 'unknown'\n\nexport type ArrivalSource = { key: string; label: string; channel?: ArrivalChannel }\n\nconst titleCase = (value: string) => value.charAt(0).toUpperCase() + value.slice(1)\n\nexport function sourceOf(arrival: Arrival | undefined): ArrivalSource {\n if (!arrival) return { key: UNKNOWN_SOURCE_KEY, label: 'Unknown' }\n const channel = arrival.channel ? { channel: arrival.channel } : {}\n if (arrival.source && arrival.campaign) {\n return { key: `src:${arrival.source}|${arrival.campaign}`, label: `${titleCase(arrival.source)} · ${arrival.campaign}`, ...channel }\n }\n if (arrival.source) return { key: `src:${arrival.source}`, label: titleCase(arrival.source), ...channel }\n if (arrival.campaign) return { key: `cmp:${arrival.campaign}`, label: arrival.campaign, ...channel }\n if (arrival.referrerHost) return { key: `ref:${arrival.referrerHost}`, label: arrival.referrerHost, ...channel }\n if (arrival.channel) return { key: `ch:${arrival.channel}`, label: CHANNEL_LABELS[arrival.channel], ...channel }\n return { key: 'ch:tagged', label: 'Tagged' }\n}\n","export type PayoutUnit = 'count' | 'revenue' | 'margin'\n\nexport const PAYOUT_UNITS: readonly PayoutUnit[] = ['count', 'revenue', 'margin']\n\nexport const NEW_PAYOUT_UNIT: PayoutUnit = 'count'\n\nexport function isPayoutUnit(value: unknown): value is PayoutUnit {\n return value === 'count' || value === 'revenue' || value === 'margin'\n}\n\nexport function isCurrencyUnit(unit: PayoutUnit): boolean {\n return unit !== 'count'\n}\n\nexport function rewardValue(unit: PayoutUnit, supplied: number | undefined): number {\n if (unit === 'count') return supplied === undefined || supplied > 0 ? 1 : 0\n return supplied ?? 1\n}\n\nexport function payoutUnitOfRewardBasis(rewardBasis: string | undefined): PayoutUnit {\n return rewardBasis === 'revenue' || rewardBasis === 'margin' ? rewardBasis : 'count'\n}\n\nexport const PAYOUT_UNIT_COPY: Record<PayoutUnit, { label: string; hint: string; total: string; perVisitor: string; influence: string }> = {\n count: {\n label: 'Count conversions',\n hint: 'Each conversion counts as 1. Use this when a conversion has no price, like a booked demo call.',\n total: 'Conversions',\n perVisitor: 'Conversions per visitor',\n influence: 'Influence on conversions',\n },\n revenue: {\n label: 'Order value',\n hint: 'Use the order value you send with each conversion.',\n total: 'Total payout',\n perVisitor: 'Revenue per visitor',\n influence: 'Influence on payout',\n },\n margin: {\n label: 'Contribution margin',\n hint: 'Revenue minus product and shipping cost, from orders sent to POST /api/orders. A below-cost order counts against its treatment.',\n total: 'Total margin',\n perVisitor: 'Margin per visitor',\n influence: 'Influence on margin',\n },\n}\n\nconst USD = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 2 })\n\nexport function formatPayout(unit: PayoutUnit, value: number): string {\n return isCurrencyUnit(unit) ? USD.format(value) : value.toLocaleString('en-US', { maximumFractionDigits: 2 })\n}\n","export type TrialRule = 'served' | 'exposure'\n\nexport const TRIAL_RULES: readonly TrialRule[] = ['served', 'exposure']\n\nexport function isTrialRule(value: unknown): value is TrialRule {\n return value === 'served' || value === 'exposure'\n}\n\nexport function trialRuleFor({\n target,\n requested,\n}: {\n target?: unknown\n requested?: TrialRule\n}): TrialRule {\n if (requested) return requested\n return target ? 'exposure' : 'served'\n}\n\nexport function trialRuleOf(experiment: { trialOn?: TrialRule }): TrialRule {\n return experiment.trialOn ?? 'served'\n}\n\nexport const TRIAL_RULE_COPY: Record<TrialRule, string> = {\n served: 'Counts a trial when served',\n exposure: 'Counts a trial when seen',\n}\n","import {\n AssignmentRequest,\n TrackNamedEventRequest,\n OrderLineItem,\n BanditResult,\n APIResponse,\n ContextualFeatures,\n BundleAssignmentRequest,\n BundleAssignmentResponse,\n matchesConversionPath,\n shouldReportTouch,\n touchFromPage,\n touchSignature,\n type StoredArrival,\n} from '@runbandit/shared';\nimport { defaultSessionStorage, generateId, getOrCreateSessionId, getOrCreateVisitorId } from './session.js';\n\nexport interface BanditsClientConfig {\n apiUrl: string;\n apiKey: string;\n timeout?: number;\n batchSize?: number;\n flushInterval?: number;\n sessionTtl?: number;\n visitorId?: string;\n captureArrival?: boolean;\n}\n\nexport const ARRIVAL_KEY = 'bandits_arrival';\n\nfunction readStoredArrival(storage: Storage | null): StoredArrival | null {\n try {\n const parsed = JSON.parse(storage?.getItem(ARRIVAL_KEY) ?? 'null');\n if (typeof parsed?.signature !== 'string' || typeof parsed?.sessionId !== 'string' || typeof parsed?.at !== 'number') return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\nexport class BanditsRequestError extends Error {\n readonly status: number | undefined;\n\n constructor(message: string, status: number | undefined) {\n super(message);\n this.name = 'BanditsRequestError';\n this.status = status;\n }\n}\n\ntype RequestResult<T> = APIResponse<T> & { status?: number };\n\nfunction requestError(response: RequestResult<unknown>, fallback: string): BanditsRequestError {\n return new BanditsRequestError(response.error || fallback, response.status);\n}\n\nexport interface Assignment {\n assignmentId: string;\n treatmentId: string;\n experimentId: string;\n userId: string;\n config: any;\n funnelId?: string;\n trialOn?: 'served' | 'exposure';\n}\n\nfunction currentPageUrl(): string | undefined {\n if (typeof location !== 'undefined' && location.origin && location.pathname) {\n return location.origin + location.pathname;\n }\n return undefined;\n}\n\nexport class BanditsClient {\n private config: BanditsClientConfig;\n private namedEventQueue: TrackNamedEventRequest[] = [];\n readonly sessionId: string;\n readonly visitorId: string;\n private exposureQueue: Array<{ assignmentId: string; exposedAt: number }> = [];\n private exposedIds = new Set<string>();\n private flushTimer?: NodeJS.Timeout;\n\n constructor(config: BanditsClientConfig) {\n this.config = {\n timeout: 5000,\n batchSize: 100,\n flushInterval: 10000,\n sessionTtl: 86400000,\n ...config\n };\n this.sessionId = this.getOrCreateSessionId();\n this.visitorId = config.visitorId ?? getOrCreateVisitorId(defaultSessionStorage());\n this.startBatchFlush();\n if (config.captureArrival !== false) void this.reportArrival();\n }\n\n async reportArrival(): Promise<boolean> {\n if (typeof location === 'undefined' || typeof document === 'undefined') return false;\n const storage = defaultSessionStorage();\n const touch = touchFromPage({ href: location.href, referrer: document.referrer });\n const now = Date.now();\n if (!shouldReportTouch(touch, readStoredArrival(storage), this.sessionId, now)) return false;\n const response = await this.makeRequest('/arrivals', 'POST', { userId: this.visitorId, touch });\n if (!response.success) return false;\n const stored: StoredArrival = { signature: touchSignature(touch), sessionId: this.sessionId, at: now };\n try {\n storage?.setItem(ARRIVAL_KEY, JSON.stringify(stored));\n } catch {\n return true;\n }\n return true;\n }\n\n async getAssignment(\n experimentId: string,\n userId: string = this.visitorId,\n context?: ContextualFeatures\n ): Promise<Assignment> {\n const request: AssignmentRequest = {\n experimentId,\n userId,\n context,\n sessionId: this.sessionId\n };\n const pageUrl = currentPageUrl();\n if (pageUrl) request.pageUrl = pageUrl;\n\n const response = await this.makeRequest<any>(\n '/assignments',\n 'POST',\n request\n );\n\n if (!response.success || !response.data) {\n throw requestError(response, 'Failed to get assignment');\n }\n\n const assignment = response.data?.data ?? response.data;\n\n if (!assignment?.assignmentId) {\n throw new Error('Invalid assignment response');\n }\n\n return {\n assignmentId: assignment.assignmentId,\n treatmentId: assignment.treatmentId,\n experimentId,\n userId,\n config: assignment.treatment?.config || {},\n funnelId: assignment.funnelId,\n trialOn: assignment.trialOn\n };\n }\n\n trackNamedEvent(\n event: { name: string; category?: string; value?: number; cost?: number; lineItems?: OrderLineItem[]; metadata?: Record<string, any> },\n opts?: { immediate?: boolean; userId?: string; assignmentId?: string }\n ): void {\n const request: TrackNamedEventRequest = {\n name: event.name,\n category: event.category || 'custom',\n value: event.value,\n metadata: event.metadata,\n sessionId: this.sessionId,\n };\n if (event.cost !== undefined) request.cost = event.cost;\n if (event.lineItems) request.lineItems = event.lineItems;\n if (opts?.userId) request.userId = opts.userId;\n if (opts?.assignmentId) request.assignmentId = opts.assignmentId;\n\n if (opts?.immediate) {\n const url = `${this.normalizeApiUrl()}/events`;\n try {\n void fetch(url, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(request),\n keepalive: true,\n });\n } catch {\n }\n return;\n }\n\n this.namedEventQueue.push(request);\n if (this.namedEventQueue.length >= this.config.batchSize!) {\n this.flush();\n }\n }\n\n async flush(): Promise<void> {\n const results = await Promise.allSettled([this.flushNamedEvents(), this.flushExposures()]);\n for (const result of results) {\n if (result.status === 'rejected') throw result.reason;\n }\n }\n\n private async flushNamedEvents(): Promise<void> {\n const namedEvents = [...this.namedEventQueue];\n this.namedEventQueue = [];\n if (namedEvents.length === 0) return;\n\n try {\n await fetch(`${this.normalizeApiUrl()}/events/batch`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ events: namedEvents }),\n keepalive: true,\n });\n } catch (error) {\n this.namedEventQueue.unshift(...namedEvents);\n throw error;\n }\n }\n\n private async flushExposures(): Promise<void> {\n const exposures = [...this.exposureQueue];\n this.exposureQueue = [];\n if (exposures.length === 0) return;\n\n try {\n await fetch(`${this.normalizeApiUrl()}/exposures`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ exposures }),\n keepalive: true,\n });\n } catch (error) {\n this.exposureQueue.unshift(...exposures);\n throw error;\n }\n }\n\n exposed(assignmentId: string, opts?: { immediate?: boolean }): void {\n if (this.exposedIds.has(assignmentId)) return;\n this.exposedIds.add(assignmentId);\n this.exposureQueue.push({ assignmentId, exposedAt: Date.now() });\n\n if (opts?.immediate || this.exposureQueue.length >= this.config.batchSize!) {\n this.flushExposures().catch(console.error);\n }\n }\n\n observeExposure(\n assignmentId: string,\n element: Element,\n opts?: { threshold?: number; dwellMs?: number }\n ): () => void {\n if (typeof IntersectionObserver === 'undefined') return () => {};\n const threshold = opts?.threshold ?? 0.5;\n const dwellMs = opts?.dwellMs ?? 1000;\n let dwell: ReturnType<typeof setTimeout> | undefined;\n\n const stop = () => {\n clearTimeout(dwell);\n dwell = undefined;\n };\n const observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[entries.length - 1];\n if (!entry) return;\n const coversViewport =\n entry.rootBounds != null && entry.intersectionRect.height >= threshold * entry.rootBounds.height;\n if (entry.isIntersecting && (entry.intersectionRatio >= threshold || coversViewport)) {\n dwell ??= setTimeout(() => {\n this.exposed(assignmentId);\n disconnect();\n }, dwellMs);\n } else {\n stop();\n }\n },\n { threshold: Array.from({ length: 21 }, (_, i) => i / 20) }\n );\n const disconnect = () => {\n stop();\n observer.disconnect();\n };\n observer.observe(element);\n return disconnect;\n }\n\n async identify(\n anonymousId: string,\n identifiedUserId: string,\n mergeReason?: 'user_signup' | 'email_verification' | 'social_login' | 'account_recovery' | 'manual',\n options?: { email?: string }\n ): Promise<void> {\n const response = await this.makeRequest(\n '/identify',\n 'POST',\n {\n anonymousId,\n identifiedUserId,\n mergeReason: mergeReason || 'manual',\n ...(options?.email ? { email: options.email } : {}),\n }\n );\n\n if (!response.success) {\n throw requestError(response, 'Failed to identify user');\n }\n }\n\n async label(userId: string, labels: Record<string, string | boolean>): Promise<void> {\n const response = await this.makeRequest('/labels', 'POST', { userId, labels });\n\n if (!response.success) {\n throw requestError(response, 'Failed to record labels');\n }\n }\n\n async trackFunnelReward(\n funnelId: string,\n userId: string,\n value: number\n ): Promise<void> {\n await this.flushExposures().catch(() => undefined);\n const response = await this.makeRequest(\n `/funnels/${funnelId}/reward`,\n 'POST',\n { userId, value }\n );\n\n if (!response.success) {\n throw requestError(response, 'Failed to record funnel reward');\n }\n }\n\n convert(funnelId: string, value = 1): Promise<void> {\n return this.trackFunnelReward(funnelId, this.visitorId, value);\n }\n\n async convertOn(funnelId: string, pathPattern: string, value = 1): Promise<boolean> {\n if (typeof location === 'undefined' || !matchesConversionPath(pathPattern, location.pathname)) return false;\n await this.convert(funnelId, value);\n return true;\n }\n\n async getBundle(\n bundleId: string,\n userId: string,\n context?: ContextualFeatures\n ): Promise<BundleAssignmentResponse> {\n const request: BundleAssignmentRequest = {\n bundleId,\n userId,\n context,\n sessionId: this.sessionId,\n };\n const pageUrl = currentPageUrl();\n if (pageUrl) request.pageUrl = pageUrl;\n\n const response = await this.makeRequest<any>('/bundles/assignment', 'POST', request);\n\n if (!response.success || !response.data) {\n throw requestError(response, 'Failed to get bundle assignment');\n }\n\n const payload = response.data?.data ?? response.data;\n if (!payload?.bundleAssignmentId || !payload?.slots) {\n throw new Error('Invalid bundle assignment response');\n }\n\n return {\n bundleAssignmentId: payload.bundleAssignmentId,\n slots: payload.slots,\n };\n }\n\n async trackBundleReward(bundleId: string, userId: string, value: number): Promise<void> {\n const response = await this.makeRequest(`/bundles/${bundleId}/reward`, 'POST', {\n userId,\n value,\n });\n if (!response.success) {\n throw requestError(response, 'Failed to record bundle reward');\n }\n }\n\n private generateId(): string {\n return generateId();\n }\n\n private normalizeApiUrl(): string {\n let baseUrl = this.config.apiUrl.replace(/\\/$/, '');\n if (!baseUrl.endsWith('/api')) {\n baseUrl = `${baseUrl}/api`;\n }\n return baseUrl;\n }\n\n private getOrCreateSessionId(): string {\n return getOrCreateSessionId({\n storage: defaultSessionStorage(),\n ttlMs: this.config.sessionTtl,\n });\n }\n\n private async makeRequest<T = any>(\n endpoint: string,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n body?: any,\n extraHeaders?: Record<string, string>\n ): Promise<RequestResult<T>> {\n const url = `${this.normalizeApiUrl()}${endpoint}`;\n\n const headers: Record<string, string> = {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n ...extraHeaders,\n };\n\n const requestConfig: RequestInit = {\n method,\n headers,\n signal: AbortSignal.timeout(this.config.timeout!),\n keepalive: method === 'POST',\n };\n\n if (body && method !== 'GET') {\n requestConfig.body = JSON.stringify(body);\n }\n\n try {\n const response = await fetch(url, requestConfig);\n const data = await response.json();\n \n return {\n success: response.ok,\n status: response.status,\n data: response.ok ? data as T : undefined,\n error: response.ok ? undefined : (data as any)?.message || 'Request failed',\n timestamp: new Date()\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Network error',\n timestamp: new Date()\n };\n }\n }\n\n private startBatchFlush(): void {\n this.flushTimer = setInterval(() => {\n this.flush().catch(console.error);\n }, this.config.flushInterval);\n }\n\n destroy(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n }\n this.flush().catch(console.error);\n if (typeof window !== 'undefined' && window.localStorage) {\n localStorage.removeItem('bandits_session_id');\n localStorage.removeItem('bandits_session_created_at');\n }\n }\n}","import { EMAIL_TOKEN_PARAM } from '@runbandit/shared';\n\n\nconst UNTRACKABLE_SCHEME = /^(mailto:|tel:|sms:|javascript:|data:|#)/i;\n\nconst NOTRACK_ATTRIBUTE = 'data-bandit-notrack';\n\nexport interface RewriteOptions {\n linkTable: string[];\n tokens: string[];\n}\n\nexport interface RewriteResult {\n html: string;\n rewritten: number;\n untracked: string[];\n}\n\ninterface Anchor {\n valueStart: number;\n valueEnd: number;\n url: string;\n}\n\nfunction isTrackable(url: string): boolean {\n const trimmed = url.trim();\n if (!trimmed || UNTRACKABLE_SCHEME.test(trimmed)) return false;\n return /^https?:\\/\\//i.test(trimmed);\n}\n\nfunction appendToken(url: string, token: string): string {\n const hashIndex = url.indexOf('#');\n const fragment = hashIndex === -1 ? '' : url.slice(hashIndex);\n const base = hashIndex === -1 ? url : url.slice(0, hashIndex);\n\n const separator = base.includes('?') ? (base.includes('&amp;') ? '&amp;' : '&') : '?';\n\n return `${base}${separator}${EMAIL_TOKEN_PARAM}=${token}${fragment}`;\n}\n\nfunction scanAnchors(html: string): Anchor[] {\n const anchors: Anchor[] = [];\n let i = 0;\n\n while (i < html.length) {\n const lt = html.indexOf('<', i);\n if (lt === -1) break;\n\n if (html.startsWith('<!--', lt)) {\n if (html.startsWith('<!--[if', lt) || html.startsWith('<!--<![endif]', lt)) {\n const close = html.indexOf('>', lt);\n i = close === -1 ? html.length : close + 1;\n continue;\n }\n\n const end = html.indexOf('-->', lt);\n i = end === -1 ? html.length : end + 3;\n continue;\n }\n\n const rawText = /^<(script|style)\\b/i.exec(html.slice(lt, lt + 8));\n if (rawText) {\n const closing = new RegExp(`</${rawText[1]}\\\\s*>`, 'i');\n const rest = html.slice(lt);\n const match = closing.exec(rest);\n i = match ? lt + match.index + match[0].length : html.length;\n continue;\n }\n\n const isLinkElement =\n /^<a[\\s/>]/i.test(html.slice(lt, lt + 3)) ||\n /^<v:roundrect[\\s/>]/i.test(html.slice(lt, lt + 13));\n if (!isLinkElement) {\n i = lt + 1;\n continue;\n }\n\n const tagEnd = findTagEnd(html, lt);\n if (tagEnd === -1) break;\n\n const tag = html.slice(lt, tagEnd);\n if (!new RegExp(NOTRACK_ATTRIBUTE, 'i').test(tag)) {\n const href = findHref(html, lt, tagEnd);\n if (href && isTrackable(href.url)) anchors.push(href);\n }\n\n i = tagEnd;\n }\n\n return anchors;\n}\n\nfunction findTagEnd(html: string, start: number): number {\n let quote: string | null = null;\n\n for (let i = start + 1; i < html.length; i++) {\n const ch = html[i];\n if (quote) {\n if (ch === quote) quote = null;\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch;\n continue;\n }\n if (ch === '>') return i + 1;\n }\n\n return -1;\n}\n\nfunction findHref(html: string, tagStart: number, tagEnd: number): Anchor | null {\n const tag = html.slice(tagStart, tagEnd);\n const match = /\\shref\\s*=\\s*(\"([^\"]*)\"|'([^']*)'|([^\\s>]+))/i.exec(tag);\n if (!match) return null;\n\n const quoted = match[2] !== undefined || match[3] !== undefined;\n const value = match[2] ?? match[3] ?? match[4] ?? '';\n\n const valueStart = tagStart + match.index + match[0].indexOf(match[1]) + (quoted ? 1 : 0);\n\n return { valueStart, valueEnd: valueStart + value.length, url: value };\n}\n\nexport function extractEmailLinks(html: string): string[] {\n const seen = new Set<string>();\n const urls: string[] = [];\n\n for (const anchor of scanAnchors(html)) {\n if (seen.has(anchor.url)) continue;\n seen.add(anchor.url);\n urls.push(anchor.url);\n }\n\n return urls;\n}\n\nexport function rewriteEmailLinks(html: string, options: RewriteOptions): RewriteResult {\n const { linkTable, tokens } = options;\n\n const indexOfUrl = new Map<string, number>();\n linkTable.forEach((url, index) => {\n if (!indexOfUrl.has(url)) indexOfUrl.set(url, index);\n });\n\n const anchors = scanAnchors(html);\n const untracked: string[] = [];\n let rewritten = 0;\n\n let output = html;\n for (let i = anchors.length - 1; i >= 0; i--) {\n const anchor = anchors[i];\n const index = indexOfUrl.get(anchor.url);\n\n if (index === undefined || tokens[index] === undefined) {\n untracked.push(anchor.url);\n continue;\n }\n\n output =\n output.slice(0, anchor.valueStart) +\n appendToken(anchor.url, tokens[index]) +\n output.slice(anchor.valueEnd);\n rewritten++;\n }\n\n return { html: output, rewritten, untracked: untracked.reverse() };\n}\n","import { rewriteEmailLinks, extractEmailLinks } from './email-links.js';\n\n\nconst SLOT_PATTERN = /\\{\\{\\s*bandit\\.([a-zA-Z0-9_-]+)\\s*\\}\\}/g;\n\nexport interface RenderEmailOptions {\n slots: Record<string, string>;\n linkTable: string[];\n tokens: string[];\n}\n\nexport interface RenderedEmail {\n html: string;\n rewritten: number;\n untracked: string[];\n missingSlots: string[];\n}\n\nexport function renderEmail(templateHtml: string, options: RenderEmailOptions): RenderedEmail {\n const missingSlots: string[] = [];\n\n const substituted = templateHtml.replace(SLOT_PATTERN, (match, name: string) => {\n const value = options.slots[name];\n if (value === undefined) {\n missingSlots.push(name);\n return match;\n }\n return value;\n });\n\n const { html, rewritten, untracked } = rewriteEmailLinks(substituted, {\n linkTable: options.linkTable,\n tokens: options.tokens,\n });\n\n return { html, rewritten, untracked, missingSlots };\n}\n\nexport function extractSlotNames(templateHtml: string): string[] {\n const names = new Set<string>();\n for (const match of templateHtml.matchAll(SLOT_PATTERN)) names.add(match[1]);\n return [...names];\n}\n\nexport { extractEmailLinks, rewriteEmailLinks };\nexport type { RewriteOptions, RewriteResult } from './email-links.js';\n","import {\n mintEmailToken,\n hashRecipient,\n impressionManifest,\n pickArm,\n uniformFor,\n type EmailPlan,\n} from '@runbandit/shared';\nimport { renderEmail, type RenderedEmail } from './email.js';\n\n\nexport interface PlanRenderResult extends RenderedEmail {\n combination: number[];\n tokens: string[];\n}\n\nexport async function renderFromPlan(\n plan: EmailPlan,\n recipientId: string,\n templateHtml: string,\n companySecret: string\n): Promise<PlanRenderResult> {\n const slots: Record<string, string> = {};\n const combination: number[] = [];\n\n for (let i = 0; i < plan.slots.length; i++) {\n const slot = plan.slots[i];\n const chosen = pickArm(slot.probabilities, await uniformFor(plan.salt, recipientId, i));\n const index = chosen === -1 ? 0 : chosen;\n\n combination.push(slot.arms[index]?.armIndex ?? 0);\n\n const config = slot.configs[index];\n slots[slot.name] =\n typeof config === 'string'\n ? config\n : ((config as { content?: string; text?: string } | undefined)?.content ??\n (config as { content?: string; text?: string } | undefined)?.text ??\n '');\n }\n\n const recipientHash = await hashRecipient(recipientId, companySecret);\n\n const tokens = await Promise.all(\n plan.linkTable.map((_, linkIndex) =>\n mintEmailToken(\n { c: plan.campaignId, r: recipientHash, k: combination, l: linkIndex },\n plan.signingKey\n )\n )\n );\n\n const rendered = renderEmail(templateHtml, {\n slots,\n linkTable: plan.linkTable,\n tokens,\n });\n\n return { ...rendered, combination, tokens };\n}\n\nexport function shouldRefreshPlan(\n plan: EmailPlan,\n recipientsRenderedSincePlan: number,\n now: number = Date.now()\n): boolean {\n return recipientsRenderedSincePlan >= plan.refreshEvery || now >= plan.expiresAt;\n}\n\nexport async function reportPlanImpressions(\n plan: EmailPlan,\n recipientIds: string[],\n options: { apiUrl: string; apiKey: string; batchKey?: string }\n): Promise<{ recorded: boolean; alreadyRecorded: boolean }> {\n const counts = await impressionManifest(plan, recipientIds);\n if (counts.length === 0) return { recorded: false, alreadyRecorded: false };\n\n const response = await fetch(`${options.apiUrl}/api/email/impressions`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'x-api-key': options.apiKey },\n body: JSON.stringify({\n campaignId: plan.campaignId,\n batchKey: options.batchKey ?? (await batchKeyFor(plan.campaignId, recipientIds)),\n counts,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to report impressions: ${response.status}`);\n }\n\n const body = (await response.json()) as { data?: { recorded: boolean; alreadyRecorded: boolean } };\n return body.data ?? { recorded: false, alreadyRecorded: false };\n}\n\nasync function batchKeyFor(campaignId: string, recipientIds: string[]): Promise<string> {\n const material = `${campaignId}|${[...recipientIds].sort().join(',')}`;\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(material));\n return Array.from(new Uint8Array(digest).slice(0, 16))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n"],"mappings":"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","names":["ExperimentStatus","BanditAlgorithm","Posterior","ForgettingMode","ContentType","FunnelStatus","document"]}
1
+ {"version":3,"sources":["../../shared/src/types.ts","../../shared/src/utils.ts","../../shared/src/email-token.ts","../../shared/src/email-plan.ts","../../shared/src/email-blocks.ts","../../shared/src/storefront-content.ts","../../shared/src/conversion-path.ts","../../shared/src/site.ts","../../shared/src/site-platforms.ts","../../shared/src/arrival.ts","../../shared/src/cdp-aliases.ts","../../shared/src/payout-unit.ts","../../shared/src/trial-rule.ts","../src/client.ts","../src/email-links.ts","../src/email.ts","../src/email-plan.ts"],"sourcesContent":["import type { Placement } from './storefront-content.js';\nimport type { FunnelGoal, SiteTarget } from './site.js';\nimport type { OrderLineItem } from './order.js';\n\nexport interface Company {\n id: string;\n name: string;\n subdomain?: string;\n domain?: string;\n plan: string;\n apiKey: string;\n status: string;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface User {\n id: string;\n email: string;\n name: string;\n image?: string;\n role: string;\n companyId: string | null;\n emailVerified?: boolean;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface Session {\n id: string;\n userId: string;\n token: string;\n expiresAt: Date;\n ipAddress?: string | null;\n userAgent?: string | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface Account {\n id: string;\n userId: string;\n provider: string;\n providerAccountId: string;\n accessToken?: string;\n refreshToken?: string;\n accessTokenExpiresAt?: Date;\n refreshTokenExpiresAt?: Date;\n scope?: string;\n idToken?: string;\n sessionState?: string;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface Experiment {\n id: string;\n name: string;\n description?: string;\n algorithm: BanditAlgorithm;\n forgetting?: ForgettingConfig;\n status: ExperimentStatus;\n contentType: ContentType;\n treatments: Treatment[];\n context?: Record<string, any>;\n bundleId?: string;\n image?: string;\n emoji?: string;\n assignmentTtl: number;\n companyId: string;\n createdAt: Date;\n updatedAt: Date;\n endAt?: Date;\n placement?: Placement;\n}\n\nexport interface Treatment {\n id: string;\n experimentId: string;\n name: string;\n description?: string;\n config: VariantConfig;\n weight?: number;\n companyId: string;\n createdAt: Date;\n}\n\nexport interface AlgorithmResult {\n treatmentId: string;\n confidence: number;\n probabilityBest?: number;\n}\n\nexport interface BanditResult {\n assignmentId: string;\n treatmentId: string;\n confidence: number;\n probabilityBest?: number;\n metadata?: Record<string, any>;\n}\n\nexport interface ContextualFeatures {\n [key: string]: string | number | boolean;\n}\n\nexport enum ExperimentStatus {\n DRAFT = 'draft',\n ACTIVE = 'active',\n PAUSED = 'paused',\n COMPLETED = 'completed',\n ARCHIVED = 'archived'\n}\n\n\nexport interface TreatmentMetadata {\n strategy?: string;\n tone?: string;\n ctaStyle?: string;\n emotionalHook?: string;\n reasoning?: string;\n tags?: string[];\n generated?: boolean;\n generatedAt?: string;\n}\n\nexport interface VariantConfig {\n content: string;\n metadata?: TreatmentMetadata & Record<string, any>;\n}\n\nexport enum BanditAlgorithm {\n THOMPSON_SAMPLING = 'thompson_sampling',\n CONTEXTUAL_LINEAR = 'contextual_linear'\n}\n\nexport enum Posterior {\n BETA = 'beta',\n NORMAL_GAMMA = 'normal_gamma'\n}\n\nexport function normalizePosterior(posterior: string | null | undefined): Posterior {\n return posterior === Posterior.NORMAL_GAMMA ? Posterior.NORMAL_GAMMA : Posterior.BETA;\n}\n\nexport enum ForgettingMode {\n NONE = 'none',\n DISCOUNTED = 'discounted',\n SLIDING_WINDOW = 'sliding_window'\n}\n\nexport interface ForgettingConfig {\n mode: ForgettingMode;\n halfLifeMs?: number;\n windowMs?: number;\n}\n\nexport const DEFAULT_HALF_LIFE_MS = 7 * 24 * 60 * 60 * 1000;\n\nexport const DEFAULT_WINDOW_MS = 14 * 24 * 60 * 60 * 1000;\n\nexport const STATIONARY: ForgettingConfig = { mode: ForgettingMode.NONE };\n\nexport function resolveForgetting(config?: Partial<ForgettingConfig> | null): ForgettingConfig {\n switch (config?.mode) {\n case ForgettingMode.DISCOUNTED:\n return {\n mode: ForgettingMode.DISCOUNTED,\n halfLifeMs: config.halfLifeMs && config.halfLifeMs > 0\n ? config.halfLifeMs\n : DEFAULT_HALF_LIFE_MS\n };\n case ForgettingMode.SLIDING_WINDOW:\n return {\n mode: ForgettingMode.SLIDING_WINDOW,\n windowMs: config.windowMs && config.windowMs > 0\n ? config.windowMs\n : DEFAULT_WINDOW_MS\n };\n default:\n return STATIONARY;\n }\n}\n\nconst LEGACY_ALGORITHM_ALIASES: Record<string, BanditAlgorithm> = {\n epsilon_greedy: BanditAlgorithm.THOMPSON_SAMPLING,\n ucb1: BanditAlgorithm.THOMPSON_SAMPLING,\n contextual_neural: BanditAlgorithm.CONTEXTUAL_LINEAR\n};\n\nexport function normalizeAlgorithm(algorithm: string | null | undefined): BanditAlgorithm {\n if (algorithm === BanditAlgorithm.THOMPSON_SAMPLING) return BanditAlgorithm.THOMPSON_SAMPLING;\n if (algorithm === BanditAlgorithm.CONTEXTUAL_LINEAR) return BanditAlgorithm.CONTEXTUAL_LINEAR;\n return LEGACY_ALGORITHM_ALIASES[algorithm ?? ''] ?? BanditAlgorithm.THOMPSON_SAMPLING;\n}\n\nexport enum ContentType {\n PLAIN_TEXT = 'plain_text',\n MARKDOWN = 'markdown',\n HTML = 'html',\n CSS = 'css',\n JAVASCRIPT = 'javascript',\n JSON = 'json',\n IMAGE = 'image',\n VIDEO = 'video',\n AUDIO = 'audio',\n DOCUMENT = 'document',\n}\n\nexport function getContentTypeCategory(contentType: ContentType): 'text' | 'code' | 'file' {\n switch (contentType) {\n case ContentType.PLAIN_TEXT:\n case ContentType.MARKDOWN:\n return 'text';\n case ContentType.HTML:\n case ContentType.CSS:\n case ContentType.JAVASCRIPT:\n case ContentType.JSON:\n return 'code';\n case ContentType.IMAGE:\n case ContentType.VIDEO:\n case ContentType.AUDIO:\n case ContentType.DOCUMENT:\n return 'file';\n }\n}\n\nexport interface BanditConfig {\n algorithm: BanditAlgorithm;\n parameters: Record<string, any>;\n}\n\nexport interface ExperimentStats {\n totalAssignments: number;\n totalConversions: number;\n conversionRate: number;\n treatmentStats: Record<string, TreatmentStats>;\n}\n\nexport interface TreatmentStats {\n assignments: number;\n conversions: number;\n conversionRate: number;\n confidence: number;\n lift?: number;\n}\n\nexport interface APIResponse<T = any> {\n success: boolean;\n data?: T;\n error?: string;\n timestamp: Date;\n}\n\nexport interface CreateExperimentRequest {\n name: string;\n description?: string;\n algorithm: BanditAlgorithm;\n contentType: ContentType;\n algorithmConfig?: Record<string, any>;\n treatments?: Omit<Treatment, 'id' | 'experimentId' | 'createdAt'>[];\n}\n\nexport interface AssignmentRequest {\n experimentId: string;\n userId: string;\n context?: ContextualFeatures;\n sessionId?: string;\n pageUrl?: string;\n}\n\n\nexport interface TrackNamedEventRequest {\n name: string;\n category?: string;\n value?: number;\n cost?: number;\n lineItems?: OrderLineItem[];\n metadata?: Record<string, any>;\n sessionId: string;\n userId?: string;\n assignmentId?: string;\n}\n\n\nexport enum FunnelStatus {\n DRAFT = 'draft',\n ACTIVE = 'active',\n PAUSED = 'paused',\n COMPLETED = 'completed',\n}\n\nexport interface Funnel {\n id: string;\n name: string;\n description?: string;\n status: FunnelStatus;\n inactivityTimeoutMs?: number;\n revisionWindowMs?: number;\n companyId: string;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface FunnelRewardRequest {\n funnelId: string;\n userId: string;\n value: number;\n}\n\n\nexport interface ApiKey {\n id: string;\n name: string;\n keyPrefix: string;\n status: 'active' | 'revoked';\n createdBy: string;\n createdAt: Date;\n lastUsedAt?: Date;\n revokedAt?: Date;\n revokedBy?: string;\n}\n\nexport interface CreateApiKeyRequest {\n name: string;\n}\n\nexport interface CreateApiKeyResponse {\n id: string;\n key: string;\n name: string;\n}\n\n\nexport type IdentifyMergeReason =\n | 'user_signup'\n | 'email_verification'\n | 'social_login'\n | 'account_recovery'\n | 'manual';\n\nexport interface IdentifyUserRequest {\n anonymousId: string;\n identifiedUserId: string;\n mergeReason?: IdentifyMergeReason;\n}\n\nexport interface IdentifyUserResponse {\n anonymousId: string;\n identifiedUserId: string;\n alreadyMapped: boolean;\n}\n\nexport interface ResolvedIdentity {\n canonicalId: string;\n anonymousIds: string[];\n isIdentified: boolean;\n}\n\n\nexport type BundleMode = 'factored' | 'joint';\n\nexport interface Bundle {\n id: string;\n name: string;\n description?: string;\n status: ExperimentStatus;\n mode: BundleMode;\n funnelId?: string;\n assignmentTtl: number;\n conversionWindowMs: number;\n inactivityTimeoutMs?: number;\n revisionWindowMs?: number;\n companyId: string;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface BundleAssignmentRequest {\n bundleId: string;\n userId: string;\n context?: ContextualFeatures;\n sessionId?: string;\n pageUrl?: string;\n}\n\nexport interface BundleSlotAssignment {\n assignmentId: string;\n experimentId: string;\n treatmentId: string;\n config: VariantConfig | Record<string, unknown>;\n}\n\nexport interface BundleAssignmentResponse {\n bundleAssignmentId: string;\n slots: Record<string, BundleSlotAssignment>;\n}\n\nexport interface SnapshotTreatment {\n id: string;\n name?: string;\n config: unknown;\n pct: number;\n}\n\nexport interface SnapshotExperiment {\n name?: string;\n mode: 'snapshot' | 'network';\n assignmentTtl: number;\n treatments: SnapshotTreatment[];\n target?: SiteTarget;\n funnelId?: string;\n trialOn?: 'served' | 'exposure';\n}\n\nexport interface CompanySnapshot {\n ttl: number;\n generatedAt: number;\n experiments: Record<string, SnapshotExperiment>;\n goals?: Record<string, FunnelGoal>;\n}\n","import { Treatment } from './types.js';\n\nexport function generateId(): string {\n return Math.random().toString(36).substring(2) + Date.now().toString(36);\n}\n\nexport function validateTreatmentWeights(treatments: Treatment[]): boolean {\n const totalWeight = treatments.reduce((sum, treatment) => sum + (treatment.weight || 1), 0);\n return totalWeight > 0;\n}\n\nexport function normalizeTreatmentWeights(treatments: Treatment[]): Treatment[] {\n const totalWeight = treatments.reduce((sum, treatment) => sum + (treatment.weight || 1), 0);\n \n return treatments.map(treatment => ({\n ...treatment,\n weight: (treatment.weight || 1) / totalWeight\n }));\n}\n\nexport function calculateConfidenceInterval(\n successes: number,\n trials: number,\n confidence: number = 0.95\n): { lower: number; upper: number } {\n if (trials === 0) return { lower: 0, upper: 0 };\n \n const z = confidence === 0.95 ? 1.96 : confidence === 0.99 ? 2.576 : 1.645;\n const p = successes / trials;\n const margin = z * Math.sqrt((p * (1 - p)) / trials);\n \n return {\n lower: Math.max(0, p - margin),\n upper: Math.min(1, p + margin)\n };\n}\n\nexport function isValidEmail(email: string): boolean {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return emailRegex.test(email);\n}\n\nexport function sanitizeMetadata(metadata: Record<string, any>): Record<string, any> {\n const sanitized: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(metadata)) {\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n sanitized[key] = value;\n } else if (value !== null && value !== undefined) {\n sanitized[key] = String(value);\n }\n }\n\n return sanitized;\n}\n\nexport function generateSubdomain(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, '')\n .replace(/\\s+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n .substring(0, 63);\n}\n\nexport function isValidSubdomain(subdomain: string): boolean {\n if (!subdomain || subdomain.length === 0 || subdomain.length > 63) {\n return false;\n }\n\n const subdomainRegex = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;\n\n const reserved = [\n 'www', 'api', 'app', 'admin', 'dashboard', 'mail', 'ftp', 'localhost',\n 'staging', 'dev', 'test', 'demo', 'blog', 'docs', 'status', 'help',\n 'support', 'cdn', 'static', 'assets', 'secure', 'payment', 'pay'\n ];\n\n return subdomainRegex.test(subdomain) && !reserved.includes(subdomain);\n}\n\nexport function extractSubdomain(hostname: string, baseDomain: string): string | null {\n if (!hostname || !baseDomain) {\n return null;\n }\n\n const cleanHostname = hostname.split(':')[0];\n\n if (!cleanHostname.endsWith(baseDomain)) {\n return null;\n }\n\n const subdomain = cleanHostname.slice(0, -(baseDomain.length + 1));\n\n if (!subdomain || subdomain === 'www') {\n return null;\n }\n\n return subdomain;\n}\n\nexport function buildSubdomainUrl(\n subdomain: string,\n baseDomain: string,\n path: string = '',\n protocol: string = 'https'\n): string {\n const url = `${protocol}://${subdomain}.${baseDomain}`;\n return path ? `${url}${path.startsWith('/') ? path : '/' + path}` : url;\n}","\nexport const EMAIL_TOKEN_PARAM = 'bt';\n\nexport interface SigningKey {\n version: number;\n secret: string;\n}\n\nexport interface EmailTokenPayload {\n c: string;\n r: string;\n k: number[];\n l: number;\n v?: number;\n}\n\nexport type VerifiedToken =\n | { valid: true; payload: Required<EmailTokenPayload> }\n | { valid: false; reason: 'malformed' | 'unknown_key' | 'bad_signature' };\n\n\nconst encoder = new TextEncoder();\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction base64UrlDecode(value: string): Uint8Array {\n const padded = value.replace(/-/g, '+').replace(/_/g, '/');\n const binary = atob(padded + '='.repeat((4 - (padded.length % 4)) % 4));\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\nasync function hmac(secret: string, message: string): Promise<Uint8Array> {\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n );\n return new Uint8Array(await crypto.subtle.sign('HMAC', key, encoder.encode(message)));\n}\n\nfunction timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return diff === 0;\n}\n\n\nexport async function hashRecipient(recipientId: string, companySecret: string): Promise<string> {\n const digest = await hmac(companySecret, `recipient:${recipientId}`);\n return base64UrlEncode(digest.slice(0, 16));\n}\n\nexport async function mintEmailToken(\n payload: EmailTokenPayload,\n key: SigningKey\n): Promise<string> {\n const body = base64UrlEncode(\n encoder.encode(\n JSON.stringify({\n c: payload.c,\n r: payload.r,\n k: payload.k,\n l: payload.l,\n v: key.version,\n })\n )\n );\n\n const signature = base64UrlEncode(await hmac(key.secret, body));\n return `${body}.${signature}`;\n}\n\nexport async function verifyEmailToken(\n token: string,\n keys: SigningKey[]\n): Promise<VerifiedToken> {\n const parts = token.split('.');\n if (parts.length !== 2 || !parts[0] || !parts[1]) {\n return { valid: false, reason: 'malformed' };\n }\n const [body, signature] = parts;\n\n let payload: EmailTokenPayload;\n try {\n payload = JSON.parse(new TextDecoder().decode(base64UrlDecode(body)));\n } catch {\n return { valid: false, reason: 'malformed' };\n }\n\n if (\n !payload ||\n typeof payload.c !== 'string' ||\n typeof payload.r !== 'string' ||\n !Array.isArray(payload.k) ||\n !payload.k.every((arm) => Number.isInteger(arm) && arm >= 0) ||\n !Number.isInteger(payload.l) ||\n (payload.l as number) < 0 ||\n !Number.isInteger(payload.v)\n ) {\n return { valid: false, reason: 'malformed' };\n }\n\n const key = keys.find((candidate) => candidate.version === payload.v);\n if (!key) return { valid: false, reason: 'unknown_key' };\n\n const expected = base64UrlEncode(await hmac(key.secret, body));\n if (!timingSafeEqual(expected, signature)) {\n return { valid: false, reason: 'bad_signature' };\n }\n\n return { valid: true, payload: payload as Required<EmailTokenPayload> };\n}\n\nexport function appendEmailToken(url: string, token: string): string {\n const hashIndex = url.indexOf('#');\n const fragment = hashIndex === -1 ? '' : url.slice(hashIndex);\n const base = hashIndex === -1 ? url : url.slice(0, hashIndex);\n const separator = base.includes('?') ? '&' : '?';\n\n return `${base}${separator}${EMAIL_TOKEN_PARAM}=${encodeURIComponent(token)}${fragment}`;\n}\n","\nexport interface PlanArm {\n treatmentId: string;\n armIndex: number;\n}\n\nexport interface PlanSlot {\n experimentId: string;\n name: string;\n arms: PlanArm[];\n probabilities: number[];\n configs: unknown[];\n}\n\nexport interface EmailPlan {\n campaignId: string;\n slots: PlanSlot[];\n linkTable: string[];\n salt: string;\n signingKey: { version: number; secret: string };\n refreshEvery: number;\n expiresAt: number;\n}\n\nexport async function uniformFor(\n salt: string,\n recipientId: string,\n slotIndex: number\n): Promise<number> {\n const key = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(salt),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n );\n const mac = new Uint8Array(\n await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(`${recipientId}:${slotIndex}`))\n );\n\n let value = 0;\n for (let i = 0; i < 6; i++) value = value * 256 + mac[i];\n return value / 2 ** 48;\n}\n\nexport function pickArm(probabilities: number[], uniform: number): number {\n if (probabilities.length === 0) return -1;\n\n const total = probabilities.reduce((sum, p) => sum + Math.max(p, 0), 0);\n if (total <= 0) {\n return Math.min(probabilities.length - 1, Math.floor(uniform * probabilities.length));\n }\n\n const target = uniform * total;\n let cumulative = 0;\n for (let i = 0; i < probabilities.length; i++) {\n cumulative += Math.max(probabilities[i], 0);\n if (target < cumulative) return i;\n }\n\n return probabilities.length - 1;\n}\n\nexport async function sampleCombination(\n plan: EmailPlan,\n recipientId: string\n): Promise<number[]> {\n const combination: number[] = [];\n\n for (let i = 0; i < plan.slots.length; i++) {\n const slot = plan.slots[i];\n const uniform = await uniformFor(plan.salt, recipientId, i);\n const chosen = pickArm(slot.probabilities, uniform);\n combination.push(chosen === -1 ? 0 : slot.arms[chosen]?.armIndex ?? 0);\n }\n\n return combination;\n}\n\nexport async function impressionManifest(\n plan: EmailPlan,\n recipientIds: string[]\n): Promise<Array<{ experimentId: string; treatmentId: string; count: number }>> {\n const counts = new Map<string, { experimentId: string; treatmentId: string; count: number }>();\n\n for (const recipientId of recipientIds) {\n for (let i = 0; i < plan.slots.length; i++) {\n const slot = plan.slots[i];\n const uniform = await uniformFor(plan.salt, recipientId, i);\n const chosen = pickArm(slot.probabilities, uniform);\n const arm = slot.arms[chosen === -1 ? 0 : chosen];\n if (!arm) continue;\n\n const key = `${slot.experimentId}:${arm.treatmentId}`;\n const entry = counts.get(key);\n if (entry) entry.count++;\n else\n counts.set(key, {\n experimentId: slot.experimentId,\n treatmentId: arm.treatmentId,\n count: 1,\n });\n }\n }\n\n return Array.from(counts.values());\n}\n","\nexport interface SlotBinding {\n $slot: string;\n}\n\nexport type Bound<T> = T | SlotBinding;\n\nexport function isSlotBinding(value: unknown): value is SlotBinding {\n return typeof value === 'object' && value !== null && typeof (value as SlotBinding).$slot === 'string';\n}\n\nexport interface HeadingBlock {\n type: 'heading';\n text: Bound<string>;\n level?: 1 | 2 | 3;\n align?: 'left' | 'center' | 'right';\n color?: Bound<string>;\n}\n\nexport interface TextBlock {\n type: 'text';\n text: Bound<string>;\n align?: 'left' | 'center' | 'right';\n color?: Bound<string>;\n}\n\nexport interface ButtonBlock {\n type: 'button';\n text: Bound<string>;\n href: Bound<string>;\n backgroundColor?: Bound<string>;\n textColor?: Bound<string>;\n align?: 'left' | 'center' | 'right';\n}\n\nexport interface ImageBlock {\n type: 'image';\n src: Bound<string>;\n alt: Bound<string>;\n href?: Bound<string>;\n width?: number;\n}\n\nexport interface DividerBlock {\n type: 'divider';\n color?: string;\n}\n\nexport interface SpacerBlock {\n type: 'spacer';\n height?: number;\n}\n\nexport type EmailBlock =\n | HeadingBlock\n | TextBlock\n | ButtonBlock\n | ImageBlock\n | DividerBlock\n | SpacerBlock;\n\nexport interface EmailDocument {\n subject: Bound<string>;\n preheader?: Bound<string>;\n blocks: EmailBlock[];\n backgroundColor?: string;\n contentWidth?: number;\n}\n\nconst CONTENT_WIDTH = 600;\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\nfunction resolve(value: Bound<string> | undefined, slots: Record<string, string>, missing: string[]): string {\n if (value === undefined) return '';\n if (!isSlotBinding(value)) return value;\n\n const resolved = slots[value.$slot];\n if (resolved === undefined) {\n missing.push(value.$slot);\n return '';\n }\n return resolved;\n}\n\nexport function extractDocumentSlots(document: EmailDocument): string[] {\n const names: string[] = [];\n const seen = new Set<string>();\n\n const visit = (value: unknown) => {\n if (isSlotBinding(value)) {\n if (!seen.has(value.$slot)) {\n seen.add(value.$slot);\n names.push(value.$slot);\n }\n return;\n }\n if (Array.isArray(value)) value.forEach(visit);\n else if (typeof value === 'object' && value !== null) Object.values(value).forEach(visit);\n };\n\n visit(document.subject);\n visit(document.preheader);\n document.blocks.forEach(visit);\n\n return names;\n}\n\nfunction renderBlock(block: EmailBlock, slots: Record<string, string>, missing: string[]): string {\n const align = (block as { align?: string }).align ?? 'left';\n\n switch (block.type) {\n case 'heading': {\n const size = block.level === 3 ? 18 : block.level === 2 ? 22 : 28;\n const color = escapeHtml(resolve(block.color, slots, missing) || '#111111');\n return `<tr><td style=\"padding:12px 24px;text-align:${align};font-family:Arial,Helvetica,sans-serif;font-size:${size}px;line-height:1.3;font-weight:bold;color:${color}\">${escapeHtml(\n resolve(block.text, slots, missing)\n )}</td></tr>`;\n }\n\n case 'text': {\n const color = escapeHtml(resolve(block.color, slots, missing) || '#333333');\n return `<tr><td style=\"padding:8px 24px;text-align:${align};font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:1.5;color:${color}\">${escapeHtml(\n resolve(block.text, slots, missing)\n )}</td></tr>`;\n }\n\n case 'button': {\n const href = escapeHtml(resolve(block.href, slots, missing));\n const label = escapeHtml(resolve(block.text, slots, missing));\n const bg = escapeHtml(resolve(block.backgroundColor, slots, missing) || '#1a73e8');\n const fg = escapeHtml(resolve(block.textColor, slots, missing) || '#ffffff');\n\n return `<tr><td style=\"padding:16px 24px;text-align:${align}\">\n<!--[if mso]><v:roundrect xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:w=\"urn:schemas-microsoft-com:office:word\" href=\"${href}\" style=\"height:44px;v-text-anchor:middle;width:220px;\" arcsize=\"12%\" strokecolor=\"${bg}\" fillcolor=\"${bg}\"><w:anchorlock/><center style=\"color:${fg};font-family:Arial,sans-serif;font-size:16px;font-weight:bold;\">${label}</center></v:roundrect><![endif]-->\n<!--[if !mso]><!--><a href=\"${href}\" style=\"display:inline-block;padding:13px 28px;background-color:${bg};color:${fg};font-family:Arial,Helvetica,sans-serif;font-size:16px;font-weight:bold;text-decoration:none;border-radius:6px\">${label}</a><!--<![endif]-->\n</td></tr>`;\n }\n\n case 'image': {\n const src = escapeHtml(resolve(block.src, slots, missing));\n const alt = escapeHtml(resolve(block.alt, slots, missing));\n const width = block.width ?? CONTENT_WIDTH;\n const img = `<img src=\"${src}\" alt=\"${alt}\" width=\"${width}\" style=\"display:block;border:0;outline:none;text-decoration:none;max-width:100%;height:auto\" />`;\n const href = block.href ? escapeHtml(resolve(block.href, slots, missing)) : '';\n\n return `<tr><td style=\"padding:0;text-align:center\">${\n href ? `<a href=\"${href}\">${img}</a>` : img\n }</td></tr>`;\n }\n\n case 'divider':\n return `<tr><td style=\"padding:12px 24px\"><table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\"><tr><td style=\"border-top:1px solid ${escapeHtml(\n block.color ?? '#e5e5e5'\n )};font-size:0;line-height:0\">&nbsp;</td></tr></table></td></tr>`;\n\n case 'spacer':\n return `<tr><td style=\"height:${block.height ?? 24}px;font-size:0;line-height:0\">&nbsp;</td></tr>`;\n\n default:\n return '';\n }\n}\n\nexport interface RenderedDocument {\n subject: string;\n html: string;\n missingSlots: string[];\n}\n\nexport function renderEmailDocument(\n document: EmailDocument,\n slots: Record<string, string>\n): RenderedDocument {\n const missingSlots: string[] = [];\n const width = document.contentWidth ?? CONTENT_WIDTH;\n const background = escapeHtml(document.backgroundColor ?? '#f4f4f5');\n\n const subject = resolve(document.subject, slots, missingSlots);\n const preheader = resolve(document.preheader, slots, missingSlots);\n\n const body = document.blocks.map((block) => renderBlock(block, slots, missingSlots)).join('\\n');\n\n const html = `<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<meta name=\"x-apple-disable-message-reformatting\" />\n<title>${escapeHtml(subject)}</title>\n<!--[if mso]><xml><o:OfficeDocumentSettings><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]-->\n</head>\n<body style=\"margin:0;padding:0;background-color:${background}\">\n<div style=\"display:none;font-size:0;line-height:0;max-height:0;overflow:hidden;mso-hide:all\">${escapeHtml(\n preheader\n )}</div>\n<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"background-color:${background}\">\n<tr><td align=\"center\" style=\"padding:24px 12px\">\n<table role=\"presentation\" width=\"${width}\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"width:${width}px;max-width:100%;background-color:#ffffff;border-radius:8px\">\n${body}\n</table>\n</td></tr>\n</table>\n</body>\n</html>`;\n\n return { subject, html, missingSlots };\n}\n","export const STOREFRONT_KINDS = ['text', 'image', 'ordering', 'offer'] as const;\nexport type StorefrontKind = (typeof STOREFRONT_KINDS)[number];\n\nexport const PLACEMENTS_BY_KIND = {\n text: ['hero_headline', 'hero_subhead', 'cta_primary', 'banner_text', 'badge_text'],\n image: ['hero_image', 'banner_image', 'feature_image'],\n ordering: ['product_list_primary', 'product_list_secondary'],\n offer: ['offer_banner'],\n} as const satisfies Record<StorefrontKind, readonly string[]>;\n\nexport type Placement = (typeof PLACEMENTS_BY_KIND)[StorefrontKind][number];\n\nexport const PLACEMENTS: readonly Placement[] = STOREFRONT_KINDS.flatMap(\n (kind) => PLACEMENTS_BY_KIND[kind]\n);\n\nexport function isPlacement(value: unknown): value is Placement {\n return typeof value === 'string' && (PLACEMENTS as readonly string[]).includes(value);\n}\n\nexport function kindOfPlacement(placement: Placement): StorefrontKind {\n const found = STOREFRONT_KINDS.find((kind) =>\n (PLACEMENTS_BY_KIND[kind] as readonly string[]).includes(placement)\n );\n if (!found) throw new Error(`Unknown placement: ${placement}`);\n return found;\n}\n\nexport const CONTENT_TYPE_BY_KIND = {\n text: { contentType: 'TEXT', contentSubtype: 'plain_text' },\n image: { contentType: 'FILE', contentSubtype: 'image' },\n ordering: { contentType: 'CODE', contentSubtype: 'json' },\n offer: { contentType: 'CODE', contentSubtype: 'json' },\n} as const satisfies Record<StorefrontKind, { contentType: string; contentSubtype: string }>;\n\nexport const MIN_ORDERING_ITEMS = 2;\nexport const MAX_ORDERING_ITEMS = 12;\n\nexport interface ImageContent {\n url: string;\n alt: string;\n width: number;\n height: number;\n sourceId: string;\n}\n\nexport interface OfferContent {\n discountId: string;\n code: string;\n headline: string;\n subtext: string;\n}\n\nexport type TreatmentContent =\n | { kind: 'text'; text: string }\n | { kind: 'image'; image: ImageContent }\n | { kind: 'ordering'; productIds: string[] }\n | { kind: 'offer'; offer: OfferContent | null };\n\nexport function isShopifyCdnUrl(value: string): boolean {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return false;\n }\n return url.protocol === 'https:' && url.hostname === 'cdn.shopify.com';\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction isPositiveInt(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value > 0;\n}\n\nfunction parseImage(raw: unknown): ImageContent | null {\n if (!isRecord(raw)) return null;\n const { url, alt, width, height, sourceId } = raw;\n if (typeof url !== 'string' || !isShopifyCdnUrl(url)) return null;\n if (typeof alt !== 'string') return null;\n if (!isPositiveInt(width) || !isPositiveInt(height)) return null;\n if (!isNonEmptyString(sourceId)) return null;\n return { url, alt, width, height, sourceId };\n}\n\nfunction parseOrdering(raw: unknown): string[] | null {\n if (!Array.isArray(raw)) return null;\n if (raw.length < MIN_ORDERING_ITEMS || raw.length > MAX_ORDERING_ITEMS) return null;\n if (!raw.every((id) => typeof id === 'string' && /^\\d+$/.test(id))) return null;\n if (new Set(raw).size !== raw.length) return null;\n return raw as string[];\n}\n\nfunction parseOffer(raw: unknown): OfferContent | null | undefined {\n if (raw === null) return null;\n if (!isRecord(raw)) return undefined;\n const { discountId, code, headline, subtext } = raw;\n if (!isNonEmptyString(discountId) || !isNonEmptyString(code)) return undefined;\n if (typeof headline !== 'string' || typeof subtext !== 'string') return undefined;\n return { discountId, code, headline, subtext };\n}\n\nexport function parseTreatmentContent(\n kind: StorefrontKind,\n config: unknown\n): TreatmentContent | null {\n if (!isRecord(config)) return null;\n const content = config.content;\n switch (kind) {\n case 'text':\n return isNonEmptyString(content) ? { kind, text: content } : null;\n case 'image': {\n const image = parseImage(content);\n return image ? { kind, image } : null;\n }\n case 'ordering': {\n const productIds = parseOrdering(content);\n return productIds ? { kind, productIds } : null;\n }\n case 'offer': {\n const offer = parseOffer(content);\n return offer === undefined ? null : { kind, offer };\n }\n }\n}\n\nexport function serializeTreatmentConfig(content: TreatmentContent): { content: unknown } {\n switch (content.kind) {\n case 'text':\n return { content: content.text };\n case 'image':\n return { content: content.image };\n case 'ordering':\n return { content: content.productIds };\n case 'offer':\n return { content: content.offer };\n }\n}\n","const SCHEME = /^[a-z][a-z0-9+.-]*:\\/\\//i;\n\nconst isWildcard = (segment: string) => segment === '*' || segment === '**';\n\nfunction segmentsOf(path: string): string[] {\n return path.split('/').filter(Boolean);\n}\n\nexport function normalizeConversionPath(input: string | undefined): string | undefined {\n const trimmed = input?.trim();\n if (!trimmed) return undefined;\n if (/[?#]/.test(trimmed)) {\n throw new Error('Leave out the query string: enter a path like /thank-you or /orders/*/thanks');\n }\n const withScheme = SCHEME.test(trimmed) ? trimmed : `https://placeholder.invalid/${trimmed.replace(/^\\/+/, '')}`;\n let pathname: string;\n try {\n pathname = new URL(withScheme).pathname;\n } catch {\n throw new Error('Enter the conversion page as a path like /thank-you');\n }\n const segments = segmentsOf(pathname);\n if (segments.some((segment) => segment.includes('*') && !isWildcard(segment))) {\n throw new Error('Use * for a whole path segment, like /orders/*/thanks');\n }\n if (segments.length > 0 && segments.every(isWildcard)) {\n throw new Error('That pattern matches every page. Add a fixed part, like /orders/*/thanks');\n }\n return pathname.length > 1 ? pathname.replace(/\\/+$/, '') : pathname;\n}\n\nfunction matchSegments(pattern: string[], path: string[]): boolean {\n if (pattern.length === 0) return path.length === 0;\n const [head, ...rest] = pattern;\n if (head === '**') {\n for (let skip = 0; skip <= path.length; skip++) {\n if (matchSegments(rest, path.slice(skip))) return true;\n }\n return false;\n }\n if (path.length === 0) return false;\n return (head === '*' || head === path[0]) && matchSegments(rest, path.slice(1));\n}\n\nexport function matchesConversionPath(pattern: string, pathname: string): boolean {\n return matchSegments(segmentsOf(pattern), segmentsOf(pathname));\n}\n","import { matchesConversionPath, normalizeConversionPath } from './conversion-path.js';\n\nexport const SITE_TARGET_KINDS = ['text', 'image', 'order'] as const;\nexport type SiteTargetKind = (typeof SITE_TARGET_KINDS)[number];\n\nexport interface SiteTarget {\n page: string;\n selector: string;\n tag: string;\n kind: SiteTargetKind;\n original: string;\n}\n\nexport type FunnelGoal =\n | { kind: 'page'; page: string }\n | { kind: 'click'; page: string; selector: string; tag: string; label: string };\n\nconst TAG_NAME = /^[a-z][a-z0-9-]*$/;\n\nfunction collapseWhitespace(raw: string): string {\n return raw.replace(/\\s+/g, ' ').trim();\n}\n\nfunction requireText(raw: string, message: string): string {\n const value = collapseWhitespace(raw);\n if (!value) throw new Error(message);\n return value;\n}\n\nfunction requirePage(raw: string): string {\n const page = normalizeConversionPath(raw);\n if (!page) throw new Error('Enter the page as a path like /pricing');\n return page;\n}\n\nfunction requireTag(raw: string): string {\n const tag = raw.trim().toLowerCase();\n if (!TAG_NAME.test(tag)) throw new Error('Enter the element tag, like h1 or button');\n return tag;\n}\n\nexport function parseSiteContent(config: unknown): string | null {\n if (typeof config !== 'object' || config === null) return null;\n const { content, value } = config as { content?: unknown; value?: unknown };\n for (const candidate of [content, value]) {\n if (typeof candidate === 'string' && candidate.trim()) return candidate;\n }\n return null;\n}\n\nexport function normalizeSiteTarget(target: SiteTarget): SiteTarget {\n if (!SITE_TARGET_KINDS.includes(target.kind)) throw new Error('A target is text, an image, or a list');\n return {\n page: requirePage(target.page),\n selector: requireText(target.selector, 'The picked element has no selector'),\n tag: requireTag(target.tag),\n kind: target.kind,\n original:\n target.kind === 'order'\n ? formatOrder(parseOrder(target.original))\n : requireText(target.original, 'The picked element has no content to replace'),\n };\n}\n\nexport function normalizeFunnelGoal(goal: FunnelGoal): FunnelGoal {\n const page = requirePage(goal.page);\n if (goal.kind === 'page') return { kind: 'page', page };\n return {\n kind: 'click',\n page,\n selector: requireText(goal.selector, 'The picked element has no selector'),\n tag: requireTag(goal.tag),\n label: collapseWhitespace(goal.label),\n };\n}\n\nexport function targetMatchesPage(target: Pick<SiteTarget, 'page'>, pathname: string): boolean {\n return matchesConversionPath(target.page, pathname);\n}\n\nexport function goalMatchesPage(goal: Pick<FunnelGoal, 'page'>, pathname: string): boolean {\n return matchesConversionPath(goal.page, pathname);\n}\n\nexport const MAX_ALTERNATIVES = 5;\nexport const MAX_TEXT_ALTERNATIVE_LENGTH = 300;\nexport const MAX_IMAGE_URL_LENGTH = 2048;\nexport const MIN_ORDER_ITEMS = 2;\nexport const MAX_ORDER_ITEMS = 24;\nexport const MAX_ORDER_KEY_LENGTH = 160;\n\nexport function formatOrder(keys: readonly string[]): string {\n return JSON.stringify(keys);\n}\n\nexport function parseOrder(raw: string): string[] {\n let value: unknown;\n try {\n value = JSON.parse(raw);\n } catch {\n throw new Error('An order is a list of the items in the order to show them');\n }\n if (!Array.isArray(value) || !value.every((key) => typeof key === 'string' && key.length > 0)) {\n throw new Error('An order is a list of the items in the order to show them');\n }\n const keys = value as string[];\n if (keys.length < MIN_ORDER_ITEMS) throw new Error(`A list needs at least ${MIN_ORDER_ITEMS} items`);\n if (keys.length > MAX_ORDER_ITEMS) throw new Error(`Reorder lists of at most ${MAX_ORDER_ITEMS} items`);\n if (keys.some((key) => key.length > MAX_ORDER_KEY_LENGTH)) throw new Error('An item in that list is too long');\n if (new Set(keys).size !== keys.length) throw new Error('Every item in the list must be different');\n return keys;\n}\n\nexport function isReorderOf(original: readonly string[], order: readonly string[]): boolean {\n if (original.length !== order.length) return false;\n const remaining = new Set(original);\n return order.every((key) => remaining.delete(key));\n}\n\nexport interface PickDraft {\n target?: SiteTarget;\n alternatives: string[];\n goal?: FunnelGoal;\n}\n\nexport interface PickState {\n funnelId: string;\n siteHost: string;\n expiresAt: number;\n draft: PickDraft;\n funnelGoal: FunnelGoal | null;\n experimentId: string | null;\n runningExperiments: number;\n}\n\nexport const SUGGESTED_SITE_EXPERIMENTS = 3;\n\nexport function moreExperimentsSentence(running: number, suggested: number): string {\n const noun = (count: number) => (count === 1 ? 'experiment' : 'experiments');\n const status = `${running} ${noun(running)} running on your site.`;\n if (running >= suggested) return `${status} Keep going if you have more elements to test.`;\n const remaining = suggested - running;\n return `${status} Pick ${remaining} more while you are here. Each element you test is another place to find a win.`;\n}\n\nexport type PickStep = 'pick' | 'alternatives' | 'goal' | 'ready' | 'live';\n\nexport function pickStep(state: Pick<PickState, 'draft' | 'funnelGoal' | 'experimentId'>): PickStep {\n if (state.experimentId !== null) return 'live';\n if (!state.draft.target) return 'pick';\n if (state.draft.alternatives.length === 0) return 'alternatives';\n if (!state.draft.goal && !state.funnelGoal) return 'goal';\n return 'ready';\n}\n\nexport function parseAlternative(kind: SiteTargetKind, raw: string): string {\n if (kind === 'text') {\n const text = requireText(raw, 'Type the text to try');\n if (text.length > MAX_TEXT_ALTERNATIVE_LENGTH) {\n throw new Error(`Keep each option under ${MAX_TEXT_ALTERNATIVE_LENGTH} characters`);\n }\n return text;\n }\n if (kind === 'order') return formatOrder(parseOrder(raw));\n const trimmed = raw.trim();\n if (trimmed.length > MAX_IMAGE_URL_LENGTH) throw new Error('That image address is too long');\n let url: URL;\n try {\n url = new URL(trimmed);\n } catch {\n throw new Error('Enter the image address as a link that starts with https://');\n }\n if (url.protocol !== 'https:') throw new Error('Enter the image address as a link that starts with https://');\n return url.href;\n}\n\nexport const PICK_CHANNEL = 'bandit-pick';\nexport const PICK_PROTOCOL_VERSION = 1;\n\nexport const PICK_MODES = ['idle', 'target', 'order', 'goal-click'] as const;\nexport type PickMode = (typeof PICK_MODES)[number];\n\nexport const PICK_REJECTIONS = ['not-text-or-image', 'has-markup', 'not-a-list', 'not-clickable', 'no-selector'] as const;\nexport type PickRejection = (typeof PICK_REJECTIONS)[number];\n\nexport type ClickFunnelGoal = Extract<FunnelGoal, { kind: 'click' }>;\n\nexport type PickerToPanel =\n | { type: 'navigated'; page: string }\n | { type: 'target-picked'; target: SiteTarget }\n | { type: 'goal-picked'; goal: ClickFunnelGoal }\n | { type: 'rejected'; reason: PickRejection }\n | { type: 'cancelled' };\n\nexport type PanelToPicker =\n | { type: 'hello' }\n | { type: 'mode'; mode: PickMode }\n | { type: 'preview'; target: SiteTarget; content: string }\n | { type: 'clear-preview' }\n | { type: 'finish' }\n | { type: 'renew'; token: string };\n\nexport const PICK_TOKEN_PREFIX = 'bpick_';\n\nexport interface PickEnvelope<M> {\n channel: typeof PICK_CHANNEL;\n v: typeof PICK_PROTOCOL_VERSION;\n msg: M;\n}\n\nexport function pickEnvelope<M extends PickerToPanel | PanelToPicker>(msg: M): PickEnvelope<M> {\n return { channel: PICK_CHANNEL, v: PICK_PROTOCOL_VERSION, msg };\n}\n\ntype Fields = Record<string, unknown>;\n\nfunction isFields(value: unknown): value is Fields {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction isOneOf<T extends string>(options: readonly T[], value: unknown): value is T {\n return options.some((option) => option === value);\n}\n\nfunction openEnvelope(data: unknown): Fields | null {\n if (!isFields(data) || data.channel !== PICK_CHANNEL || data.v !== PICK_PROTOCOL_VERSION) return null;\n return isFields(data.msg) ? data.msg : null;\n}\n\nfunction asSiteTarget(value: unknown): SiteTarget | null {\n if (!isFields(value)) return null;\n const { page, selector, tag, kind, original } = value;\n if (!isString(page) || !isString(selector) || !isString(tag) || !isString(original)) return null;\n if (!isOneOf(SITE_TARGET_KINDS, kind)) return null;\n return { page, selector, tag, kind, original };\n}\n\nfunction asClickGoal(value: unknown): ClickFunnelGoal | null {\n if (!isFields(value) || value.kind !== 'click') return null;\n const { page, selector, tag, label } = value;\n if (!isString(page) || !isString(selector) || !isString(tag) || !isString(label)) return null;\n return { kind: 'click', page, selector, tag, label };\n}\n\nexport function parsePickerMessage(data: unknown): PickerToPanel | null {\n const msg = openEnvelope(data);\n switch (msg?.type) {\n case 'navigated':\n return isString(msg.page) ? { type: 'navigated', page: msg.page } : null;\n case 'target-picked': {\n const target = asSiteTarget(msg.target);\n return target ? { type: 'target-picked', target } : null;\n }\n case 'goal-picked': {\n const goal = asClickGoal(msg.goal);\n return goal ? { type: 'goal-picked', goal } : null;\n }\n case 'rejected':\n return isOneOf(PICK_REJECTIONS, msg.reason) ? { type: 'rejected', reason: msg.reason } : null;\n case 'cancelled':\n return { type: 'cancelled' };\n default:\n return null;\n }\n}\n\nexport function parsePanelMessage(data: unknown): PanelToPicker | null {\n const msg = openEnvelope(data);\n switch (msg?.type) {\n case 'hello':\n return { type: 'hello' };\n case 'mode':\n return isOneOf(PICK_MODES, msg.mode) ? { type: 'mode', mode: msg.mode } : null;\n case 'preview': {\n const target = asSiteTarget(msg.target);\n return target && isString(msg.content) ? { type: 'preview', target, content: msg.content } : null;\n }\n case 'clear-preview':\n return { type: 'clear-preview' };\n case 'finish':\n return { type: 'finish' };\n case 'renew':\n return isString(msg.token) && msg.token.startsWith(PICK_TOKEN_PREFIX) ? { type: 'renew', token: msg.token } : null;\n default:\n return null;\n }\n}\n","export type SitePlatformPath = 'shopify' | 'script' | 'code'\n\nexport interface SitePlatform {\n id: string\n label: string\n path: SitePlatformPath\n codeBox: string\n steps: readonly string[]\n planNote?: string\n}\n\nexport const SITE_PLATFORMS = [\n { id: 'shopify', label: 'Shopify', path: 'shopify', codeBox: '', steps: [] },\n {\n id: 'shopify_theme',\n label: 'Shopify',\n path: 'script',\n codeBox: 'Online Store, Themes, Edit code, layout/theme.liquid, just before </head>.',\n steps: [\n 'In your Shopify admin open Online Store, then Themes.',\n 'On your live theme click the three dots, then Edit code.',\n 'Open layout/theme.liquid.',\n 'Paste the script on the line just before </head>.',\n 'Click Save.',\n 'Then add the purchase pixel below, so Bandit learns from orders.',\n ],\n },\n {\n id: 'webflow',\n label: 'Webflow',\n path: 'script',\n codeBox: 'Site settings, Custom code, Head code. Save, then Publish.',\n steps: [\n 'Open the site in Webflow and click the gear for Site settings.',\n 'Open the Custom code tab.',\n 'Paste the script into the Head code box.',\n 'Click Save changes.',\n 'Publish the site. Custom code only goes live when you publish.',\n ],\n planNote: 'Needs a paid Site plan.',\n },\n {\n id: 'wordpress',\n label: 'WordPress',\n path: 'script',\n codeBox: 'Install the free WPCode plugin, then Code Snippets, Header & Footer, Header box.',\n steps: [\n 'In your WordPress admin go to Plugins, Add New, and search for WPCode. Install and activate it.',\n 'In the left menu open Code Snippets, then Header & Footer.',\n 'Paste the script into the Header box.',\n 'Click Save Changes.',\n 'Load your site once so the tag is in the page.',\n ],\n planNote: 'WordPress.com needs the Business plan for plugins.',\n },\n {\n id: 'squarespace',\n label: 'Squarespace',\n path: 'script',\n codeBox: 'Settings, Website Tools, Code Injection, Header.',\n steps: [\n 'From the Home menu open Settings, then Website Tools. On older sites it is under Advanced.',\n 'Click Code Injection.',\n 'Paste the script into the Header box.',\n 'Click Save.',\n ],\n planNote: 'Needs the Core plan or higher (Business on older plans).',\n },\n {\n id: 'framer',\n label: 'Framer',\n path: 'script',\n codeBox: 'Site Settings, General, Custom Code, Start of head. Publish after saving.',\n steps: [\n 'Open your project and click the gear for Site Settings.',\n 'On the General tab scroll to Custom Code.',\n 'Paste the script under Start of head tag.',\n 'Click Save.',\n 'Publish the site.',\n ],\n planNote: 'Needs a paid Site plan.',\n },\n {\n id: 'wix',\n label: 'Wix',\n path: 'script',\n codeBox: 'Settings, Custom Code, Add Custom Code. Choose All pages and place it in Head.',\n steps: [\n 'In your Wix dashboard open Settings, then Custom Code under Advanced.',\n 'Click Add Custom Code.',\n 'Paste the script and name it Bandit.',\n 'Under Add Code to Pages choose All pages and Load code once.',\n 'Under Place Code in choose Head.',\n 'Click Apply.',\n ],\n planNote: 'Needs a Premium plan with a connected domain.',\n },\n {\n id: 'other',\n label: 'Something else',\n path: 'script',\n codeBox: 'Any custom-code or head-injection box, or the HTML template, on every page.',\n steps: [\n 'Find where your builder lets you add code to the head of every page. It is usually called Custom code, Code injection, Header scripts, or Tracking code.',\n 'Paste the script there.',\n 'Save and publish.',\n 'If you edit the HTML yourself, paste it before the closing head tag of every page template.',\n ],\n },\n {\n id: 'nextjs_react',\n label: 'Next.js / React',\n path: 'code',\n codeBox: \"Your root layout's head.\",\n steps: ['Add the tag to the head of your root layout.'],\n },\n {\n id: 'plain_html',\n label: 'Plain HTML',\n path: 'code',\n codeBox: 'The head of every page.',\n steps: ['Paste the tag into the head of every page.'],\n },\n {\n id: 'tag_manager',\n label: 'Tag manager',\n path: 'code',\n codeBox: 'A Custom HTML tag that fires on all pages.',\n steps: ['Create a Custom HTML tag with the script and fire it on all pages.'],\n },\n] as const satisfies readonly SitePlatform[]\n\nexport type SitePlatformId = (typeof SITE_PLATFORMS)[number]['id']\n\nexport const SITE_PLATFORM_IDS: readonly SitePlatformId[] = SITE_PLATFORMS.map((platform) => platform.id)\n\ntype ScriptEntry = Extract<(typeof SITE_PLATFORMS)[number], { path: 'script' }>\n\nexport type ScriptPlatform = SitePlatform & { id: ScriptEntry['id']; path: 'script' }\n\nexport const SCRIPT_PLATFORMS: readonly ScriptPlatform[] = SITE_PLATFORMS.filter(\n (platform): platform is ScriptEntry => platform.path === 'script',\n)\n\nexport function platformById(id: string | undefined): SitePlatform | undefined {\n return SITE_PLATFORMS.find((platform) => platform.id === id)\n}\n","export const ARRIVAL_CHANNELS = ['ad', 'email', 'social', 'search', 'referral', 'direct'] as const\nexport type ArrivalChannel = (typeof ARRIVAL_CHANNELS)[number]\n\nexport const ARRIVAL_TTL_MS = 30 * 24 * 60 * 60 * 1000\nexport const MAX_ARRIVAL_VALUE_LENGTH = 40\nexport const MAX_REFERRER_HOST_LENGTH = 100\nexport const MAX_RAW_TOUCH_VALUE_LENGTH = 200\n\nexport type ArrivalTouch = {\n utmSource?: string\n utmMedium?: string\n utmCampaign?: string\n utmContent?: string\n trackingParam?: string\n klaviyoExchangeId?: string\n referrerHost?: string\n}\n\nexport type Arrival = {\n source?: string\n medium?: string\n campaign?: string\n content?: string\n referrerHost?: string\n channel?: ArrivalChannel\n}\n\nexport const UTM_PARAMS = [\n ['utmSource', 'utm_source'],\n ['utmMedium', 'utm_medium'],\n ['utmCampaign', 'utm_campaign'],\n ['utmContent', 'utm_content'],\n] as const\n\nexport const TRACKING_PARAM_SOURCES: Readonly<Record<string, string>> = {\n gclid: 'google',\n gbraid: 'google',\n wbraid: 'google',\n fbclid: 'facebook',\n ttclid: 'tiktok',\n msclkid: 'bing',\n li_fat_id: 'linkedin',\n twclid: 'x',\n epik: 'pinterest',\n sccid: 'snapchat',\n _kx: 'klaviyo',\n mc_eid: 'mailchimp',\n}\n\ntype ArrivalSignals = Omit<Arrival, 'channel'> & { trackingParam?: string }\n\ntype ChannelRule = {\n channel: ArrivalChannel\n field: 'medium' | 'trackingParam' | 'source' | 'referrerHost'\n values: readonly string[]\n}\n\nexport const CHANNEL_RULES: readonly ChannelRule[] = [\n {\n channel: 'ad',\n field: 'medium',\n values: [\n 'cpc', 'ppc', 'paid', 'paid-social', 'paidsocial', 'paid-search', 'paidsearch', 'display', 'cpm',\n 'cpv', 'cpa', 'banner', 'ad', 'ads', 'paid-media', 'retargeting', 'affiliate',\n ],\n },\n { channel: 'email', field: 'medium', values: ['email', 'e-mail', 'newsletter', 'sms'] },\n { channel: 'social', field: 'medium', values: ['social', 'social-media', 'organic-social', 'sm'] },\n { channel: 'search', field: 'medium', values: ['organic', 'organic-search', 'seo'] },\n { channel: 'referral', field: 'medium', values: ['referral', 'partner'] },\n {\n channel: 'ad',\n field: 'trackingParam',\n values: ['gclid', 'gbraid', 'wbraid', 'fbclid', 'ttclid', 'msclkid', 'li_fat_id', 'twclid', 'epik', 'sccid'],\n },\n { channel: 'email', field: 'trackingParam', values: ['_kx', 'mc_eid'] },\n {\n channel: 'email',\n field: 'source',\n values: [\n 'klaviyo', 'mailchimp', 'sendgrid', 'hubspot', 'hs-email', 'braze', 'iterable', 'customer.io',\n 'customerio', 'customer-io', 'omnisend', 'email', 'newsletter',\n ],\n },\n {\n channel: 'social',\n field: 'source',\n values: [\n 'facebook', 'fb', 'instagram', 'ig', 'tiktok', 'twitter', 'x', 'linkedin', 'pinterest', 'reddit',\n 'youtube', 'snapchat', 'threads',\n ],\n },\n { channel: 'search', field: 'source', values: ['google', 'bing', 'duckduckgo', 'yahoo', 'baidu', 'yandex', 'ecosia'] },\n {\n channel: 'email',\n field: 'referrerHost',\n values: ['mail.google.com', 'outlook.live.com', 'outlook.office.com', 'outlook.office365.com', 'mail.yahoo.com'],\n },\n {\n channel: 'social',\n field: 'referrerHost',\n values: [\n 'facebook.com', 'fb.com', 'fb.me', 'instagram.com', 't.co', 'twitter.com', 'x.com', 'linkedin.com',\n 'lnkd.in', 'pinterest.com', 'pin.it', 'reddit.com', 'youtube.com', 'youtu.be', 'tiktok.com',\n 'snapchat.com', 'threads.net',\n ],\n },\n {\n channel: 'search',\n field: 'referrerHost',\n values: ['google', 'bing.com', 'duckduckgo.com', 'yahoo', 'baidu.com', 'yandex', 'ecosia.org', 'search.brave.com'],\n },\n]\n\nexport function normalizeArrivalValue(raw: string | undefined): string | undefined {\n const value = raw\n ?.trim()\n .toLowerCase()\n .replace(/[\\s_]+/g, '-')\n .slice(0, MAX_ARRIVAL_VALUE_LENGTH)\n return value ? value : undefined\n}\n\nexport function normalizeHost(raw: string | undefined): string | undefined {\n const host = raw?.trim().toLowerCase().replace(/^www\\./, '').slice(0, MAX_REFERRER_HOST_LENGTH)\n return host ? host : undefined\n}\n\nfunction hostMatches(host: string, pattern: string): boolean {\n if (pattern.includes('.')) return host === pattern || host.endsWith(`.${pattern}`)\n return host.split('.').slice(0, -1).includes(pattern)\n}\n\nexport const PASS_THROUGH_HOSTS: readonly string[] = [\n 'checkout.stripe.com', 'stripe.com', 'paypal.com', 'shop.app', 'shopify.com', 'klarna.com', 'afterpay.com',\n 'affirm.com', 'accounts.google.com', 'appleid.apple.com', 'login.microsoftonline.com', 'login.live.com',\n 'auth0.com', 'clerk.accounts.dev', 'clerk.com', 'okta.com',\n]\n\nexport function isPassThroughHost(host: string): boolean {\n return PASS_THROUGH_HOSTS.some((pattern) => hostMatches(host, pattern))\n}\n\nfunction ruleMatches(rule: ChannelRule, signals: ArrivalSignals): boolean {\n const value = signals[rule.field]\n if (!value) return false\n if (rule.field === 'referrerHost') return rule.values.some((pattern) => hostMatches(value, pattern))\n return rule.values.includes(value)\n}\n\nexport function channelOf(signals: ArrivalSignals): ArrivalChannel | undefined {\n const { trackingParam, ...arrival } = signals\n if (!trackingParam && Object.values(arrival).every((value) => !value)) return 'direct'\n const rule = CHANNEL_RULES.find((candidate) => ruleMatches(candidate, signals))\n if (rule) return rule.channel\n return arrival.referrerHost ? 'referral' : undefined\n}\n\nfunction arrivalReferrerHost(raw: string | undefined): string | undefined {\n const host = normalizeHost(raw)\n return host && !isPassThroughHost(host) ? host : undefined\n}\n\nexport function arrivalFromTouch(touch: ArrivalTouch): Arrival {\n const named = touch.trackingParam?.toLowerCase()\n const trackingParam = named && Object.hasOwn(TRACKING_PARAM_SOURCES, named) ? named : undefined\n const fields = {\n source: normalizeArrivalValue(touch.utmSource) ?? (trackingParam ? TRACKING_PARAM_SOURCES[trackingParam] : undefined),\n medium: normalizeArrivalValue(touch.utmMedium),\n campaign: normalizeArrivalValue(touch.utmCampaign),\n content: normalizeArrivalValue(touch.utmContent),\n referrerHost: arrivalReferrerHost(touch.referrerHost),\n }\n const present = Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined))\n const channel = channelOf({ ...present, ...(trackingParam ? { trackingParam } : {}) })\n return { ...present, ...(channel ? { channel } : {}) }\n}\n\nexport function isDirectTouch(touch: ArrivalTouch): boolean {\n return !touch.utmSource && !touch.utmMedium && !touch.utmCampaign && !touch.utmContent && !touch.trackingParam && !touch.referrerHost\n}\n\nfunction safeUrl(raw: string): URL | undefined {\n try {\n return new URL(raw)\n } catch {\n return undefined\n }\n}\n\nexport function touchFromPage(page: { href: string; referrer: string }): ArrivalTouch {\n const url = safeUrl(page.href)\n if (!url) return {}\n const params = new Map<string, string>()\n url.searchParams.forEach((value, key) => {\n const name = key.toLowerCase()\n const trimmed = value.trim()\n if (trimmed && !params.has(name)) params.set(name, trimmed.slice(0, MAX_RAW_TOUCH_VALUE_LENGTH))\n })\n const touch: ArrivalTouch = {}\n for (const [field, param] of UTM_PARAMS) {\n const value = params.get(param)\n if (value) touch[field] = value\n }\n const trackingParam = Object.keys(TRACKING_PARAM_SOURCES).find((name) => params.has(name))\n if (trackingParam) touch.trackingParam = trackingParam\n const klaviyoExchangeId = params.get('_kx')\n if (klaviyoExchangeId) touch.klaviyoExchangeId = klaviyoExchangeId\n const referrerHost = normalizeHost(safeUrl(page.referrer)?.hostname)\n if (referrerHost && referrerHost !== normalizeHost(url.hostname)) touch.referrerHost = referrerHost\n return touch\n}\n\nexport type StoredArrival = { signature: string; sessionId: string; at: number }\n\nexport function touchSignature(touch: ArrivalTouch): string {\n return JSON.stringify([\n touch.utmSource ?? '', touch.utmMedium ?? '', touch.utmCampaign ?? '', touch.utmContent ?? '',\n touch.trackingParam ?? '', touch.referrerHost ?? '', touch.klaviyoExchangeId ?? '',\n ])\n}\n\nexport function shouldReportTouch(touch: ArrivalTouch, stored: StoredArrival | null, sessionId: string, now: number): boolean {\n if (!stored) return true\n if (isDirectTouch(touch)) return now - stored.at >= ARRIVAL_TTL_MS\n return stored.signature !== touchSignature(touch) || stored.sessionId !== sessionId\n}\n\nexport function isArrivalActive(startedAt: number, at: number): boolean {\n return startedAt <= at && at - startedAt < ARRIVAL_TTL_MS\n}\n\nexport const CHANNEL_LABELS: Readonly<Record<ArrivalChannel, string>> = {\n ad: 'Ad',\n email: 'Email',\n social: 'Social',\n search: 'Search',\n referral: 'Referral',\n direct: 'Direct',\n}\n\nexport const UNKNOWN_SOURCE_KEY = 'unknown'\n\nexport type ArrivalSource = { key: string; label: string; channel?: ArrivalChannel }\n\nconst titleCase = (value: string) => value.charAt(0).toUpperCase() + value.slice(1)\n\nexport function sourceOf(arrival: Arrival | undefined): ArrivalSource {\n if (!arrival) return { key: UNKNOWN_SOURCE_KEY, label: 'Unknown' }\n const channel = arrival.channel ? { channel: arrival.channel } : {}\n if (arrival.source && arrival.campaign) {\n return { key: `src:${arrival.source}|${arrival.campaign}`, label: `${titleCase(arrival.source)} · ${arrival.campaign}`, ...channel }\n }\n if (arrival.source) return { key: `src:${arrival.source}`, label: titleCase(arrival.source), ...channel }\n if (arrival.campaign) return { key: `cmp:${arrival.campaign}`, label: arrival.campaign, ...channel }\n if (arrival.referrerHost) return { key: `ref:${arrival.referrerHost}`, label: arrival.referrerHost, ...channel }\n if (arrival.channel) return { key: `ch:${arrival.channel}`, label: CHANNEL_LABELS[arrival.channel], ...channel }\n return { key: 'ch:tagged', label: 'Tagged' }\n}\n","export const CDP_SOURCES = ['segment', 'rudderstack', 'posthog'] as const;\nexport type CdpSource = (typeof CDP_SOURCES)[number];\n\nexport interface CdpAlias {\n source: CdpSource;\n externalId: string;\n}\n\nexport const MAX_CDP_EXTERNAL_ID_LENGTH = 256;\n\nexport interface CdpStorage {\n readonly length: number;\n key(index: number): string | null;\n getItem(key: string): string | null;\n}\n\ninterface CdpSourceEntry {\n source: CdpSource;\n key: RegExp;\n parse: (raw: string) => string | null;\n}\n\nconst RUDDERSTACK_ENCRYPTED_PREFIX = 'RS_ENC_v3_';\nconst RUDDERSTACK_LEGACY_ENCRYPTED_PREFIX = 'RudderEncrypt:';\n\nfunction decoded(raw: string): string {\n try {\n return decodeURIComponent(raw);\n } catch {\n return raw;\n }\n}\n\nfunction jsonValue(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\nfunction nonEmpty(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nfunction plainOrQuoted(raw: string): string | null {\n const text = decoded(raw);\n return nonEmpty(text.startsWith('\"') ? jsonValue(text) : text);\n}\n\nfunction rudderstackValue(raw: string): string | null {\n const text = plainOrQuoted(raw);\n if (text === null || text.startsWith(RUDDERSTACK_LEGACY_ENCRYPTED_PREFIX)) return null;\n if (!text.startsWith(RUDDERSTACK_ENCRYPTED_PREFIX)) return text;\n try {\n return nonEmpty(jsonValue(atob(text.slice(RUDDERSTACK_ENCRYPTED_PREFIX.length))));\n } catch {\n return null;\n }\n}\n\nfunction posthogDistinctId(raw: string): string | null {\n const parsed = jsonValue(decoded(raw));\n if (typeof parsed !== 'object' || parsed === null) return null;\n const state = parsed as Record<string, unknown>;\n const deviceId = nonEmpty(state.$device_id);\n if (deviceId !== null) return deviceId;\n return state.$user_state === 'identified' ? null : nonEmpty(state.distinct_id);\n}\n\nexport const CDP_SOURCE_REGISTRY: readonly CdpSourceEntry[] = [\n { source: 'segment', key: /^ajs_anonymous_id$/, parse: plainOrQuoted },\n { source: 'rudderstack', key: /^rl_anonymous_id$/, parse: rudderstackValue },\n { source: 'posthog', key: /^ph_.+_posthog$/, parse: posthogDistinctId },\n];\n\nfunction cookiePairs(header: string): Array<[string, string]> {\n const pairs: Array<[string, string]> = [];\n for (const part of header.split(';')) {\n const index = part.indexOf('=');\n if (index === -1) continue;\n pairs.push([part.slice(0, index).trim(), part.slice(index + 1).trim()]);\n }\n return pairs;\n}\n\nfunction storagePairs(storage: CdpStorage | null): Array<[string, string]> {\n const pairs: Array<[string, string]> = [];\n try {\n for (let i = 0; i < (storage?.length ?? 0); i++) {\n const key = storage!.key(i);\n const value = key === null ? null : storage!.getItem(key);\n if (key !== null && value !== null) pairs.push([key, value]);\n }\n } catch {\n return pairs;\n }\n return pairs;\n}\n\nexport function readCdpAliases(cookieHeader: string, storage: CdpStorage | null): CdpAlias[] {\n const candidates = [...cookiePairs(cookieHeader), ...storagePairs(storage)];\n const aliases: CdpAlias[] = [];\n for (const { source, key, parse } of CDP_SOURCE_REGISTRY) {\n for (const [name, raw] of candidates) {\n if (!key.test(name)) continue;\n const externalId = parse(raw);\n if (externalId === null || externalId.length > MAX_CDP_EXTERNAL_ID_LENGTH) continue;\n aliases.push({ source, externalId });\n break;\n }\n }\n return aliases;\n}\n","export type PayoutUnit = 'count' | 'revenue' | 'margin'\n\nexport const PAYOUT_UNITS: readonly PayoutUnit[] = ['count', 'revenue', 'margin']\n\nexport const NEW_PAYOUT_UNIT: PayoutUnit = 'count'\n\nexport function isPayoutUnit(value: unknown): value is PayoutUnit {\n return value === 'count' || value === 'revenue' || value === 'margin'\n}\n\nexport function isCurrencyUnit(unit: PayoutUnit): boolean {\n return unit !== 'count'\n}\n\nexport function rewardValue(unit: PayoutUnit, supplied: number | undefined): number {\n if (unit === 'count') return supplied === undefined || supplied > 0 ? 1 : 0\n return supplied ?? 1\n}\n\nexport function payoutUnitOfRewardBasis(rewardBasis: string | undefined): PayoutUnit {\n return rewardBasis === 'revenue' || rewardBasis === 'margin' ? rewardBasis : 'count'\n}\n\nexport const PAYOUT_UNIT_COPY: Record<PayoutUnit, { label: string; hint: string; total: string; perVisitor: string; influence: string }> = {\n count: {\n label: 'Count conversions',\n hint: 'Each conversion counts as 1. Use this when a conversion has no price, like a booked demo call.',\n total: 'Conversions',\n perVisitor: 'Conversions per visitor',\n influence: 'Influence on conversions',\n },\n revenue: {\n label: 'Order value',\n hint: 'Use the order value you send with each conversion.',\n total: 'Total payout',\n perVisitor: 'Revenue per visitor',\n influence: 'Influence on payout',\n },\n margin: {\n label: 'Contribution margin',\n hint: 'Revenue minus product and shipping cost, from orders sent to POST /api/orders. A below-cost order counts against its treatment.',\n total: 'Total margin',\n perVisitor: 'Margin per visitor',\n influence: 'Influence on margin',\n },\n}\n\nconst USD = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 2 })\n\nexport function formatPayout(unit: PayoutUnit, value: number): string {\n return isCurrencyUnit(unit) ? USD.format(value) : value.toLocaleString('en-US', { maximumFractionDigits: 2 })\n}\n","export type TrialRule = 'served' | 'exposure'\n\nexport const TRIAL_RULES: readonly TrialRule[] = ['served', 'exposure']\n\nexport function isTrialRule(value: unknown): value is TrialRule {\n return value === 'served' || value === 'exposure'\n}\n\nexport function trialRuleFor({\n target,\n requested,\n}: {\n target?: unknown\n requested?: TrialRule\n}): TrialRule {\n if (requested) return requested\n return target ? 'exposure' : 'served'\n}\n\nexport function trialRuleOf(experiment: { trialOn?: TrialRule }): TrialRule {\n return experiment.trialOn ?? 'served'\n}\n\nexport const TRIAL_RULE_COPY: Record<TrialRule, string> = {\n served: 'Counts a trial when served',\n exposure: 'Counts a trial when seen',\n}\n","import {\n AssignmentRequest,\n TrackNamedEventRequest,\n OrderLineItem,\n BanditResult,\n APIResponse,\n ContextualFeatures,\n BundleAssignmentRequest,\n BundleAssignmentResponse,\n matchesConversionPath,\n shouldReportTouch,\n touchFromPage,\n touchSignature,\n readCdpAliases,\n type CdpAlias,\n type CdpSource,\n type StoredArrival,\n} from '@runbandit/shared';\nimport { defaultSessionStorage, generateId, getOrCreateSessionId, getOrCreateVisitorId } from './session.js';\n\nexport interface BanditsClientConfig {\n apiUrl: string;\n apiKey: string;\n timeout?: number;\n batchSize?: number;\n flushInterval?: number;\n sessionTtl?: number;\n visitorId?: string;\n captureArrival?: boolean;\n captureCdpAliases?: boolean;\n}\n\nexport const ARRIVAL_KEY = 'bandits_arrival';\n\nfunction readStoredArrival(storage: Storage | null): StoredArrival | null {\n try {\n const parsed = JSON.parse(storage?.getItem(ARRIVAL_KEY) ?? 'null');\n if (typeof parsed?.signature !== 'string' || typeof parsed?.sessionId !== 'string' || typeof parsed?.at !== 'number') return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\nexport const CDP_ALIASES_KEY = 'bandits_cdp_aliases';\n\nfunction readSentAliases(storage: Storage | null, visitorId: string): Partial<Record<CdpSource, string>> {\n try {\n const parsed = JSON.parse(storage?.getItem(CDP_ALIASES_KEY) ?? 'null');\n return parsed?.visitorId === visitorId && typeof parsed.sent === 'object' && parsed.sent !== null ? parsed.sent : {};\n } catch {\n return {};\n }\n}\n\nfunction readCookieHeader(): string {\n try {\n return document.cookie ?? '';\n } catch {\n return '';\n }\n}\n\nexport class BanditsRequestError extends Error {\n readonly status: number | undefined;\n\n constructor(message: string, status: number | undefined) {\n super(message);\n this.name = 'BanditsRequestError';\n this.status = status;\n }\n}\n\ntype RequestResult<T> = APIResponse<T> & { status?: number };\n\nfunction requestError(response: RequestResult<unknown>, fallback: string): BanditsRequestError {\n return new BanditsRequestError(response.error || fallback, response.status);\n}\n\nexport interface Assignment {\n assignmentId: string;\n treatmentId: string;\n experimentId: string;\n userId: string;\n config: any;\n funnelId?: string;\n trialOn?: 'served' | 'exposure';\n}\n\nfunction currentPageUrl(): string | undefined {\n if (typeof location !== 'undefined' && location.origin && location.pathname) {\n return location.origin + location.pathname;\n }\n return undefined;\n}\n\nexport class BanditsClient {\n private config: BanditsClientConfig;\n private namedEventQueue: TrackNamedEventRequest[] = [];\n readonly sessionId: string;\n readonly visitorId: string;\n private exposureQueue: Array<{ assignmentId: string; exposedAt: number }> = [];\n private exposedIds = new Set<string>();\n private flushTimer?: NodeJS.Timeout;\n\n constructor(config: BanditsClientConfig) {\n this.config = {\n timeout: 5000,\n batchSize: 100,\n flushInterval: 10000,\n sessionTtl: 86400000,\n ...config\n };\n this.sessionId = this.getOrCreateSessionId();\n this.visitorId = config.visitorId ?? getOrCreateVisitorId(defaultSessionStorage());\n this.startBatchFlush();\n if (config.captureArrival !== false) void this.reportArrival();\n if (config.captureCdpAliases !== false) void this.reportCdpAliases();\n }\n\n async reportCdpAliases(): Promise<boolean> {\n if (typeof document === 'undefined') return false;\n const storage = defaultSessionStorage();\n const sent = readSentAliases(storage, this.visitorId);\n const fresh = readCdpAliases(readCookieHeader(), storage).filter(({ source, externalId }) => sent[source] !== externalId);\n if (fresh.length === 0) return false;\n try {\n await this.aliasVisitor(fresh);\n } catch {\n return false;\n }\n const record = { visitorId: this.visitorId, sent: { ...sent, ...Object.fromEntries(fresh.map((a) => [a.source, a.externalId])) } };\n try {\n storage?.setItem(CDP_ALIASES_KEY, JSON.stringify(record));\n } catch {\n return true;\n }\n return true;\n }\n\n async aliasVisitor(aliases: CdpAlias[]): Promise<void> {\n const response = await this.makeRequest('/aliases', 'POST', { visitorId: this.visitorId, aliases });\n if (!response.success) {\n throw requestError(response, 'Failed to alias visitor');\n }\n }\n\n async reportArrival(): Promise<boolean> {\n if (typeof location === 'undefined' || typeof document === 'undefined') return false;\n const storage = defaultSessionStorage();\n const touch = touchFromPage({ href: location.href, referrer: document.referrer });\n const now = Date.now();\n if (!shouldReportTouch(touch, readStoredArrival(storage), this.sessionId, now)) return false;\n const response = await this.makeRequest('/arrivals', 'POST', { userId: this.visitorId, touch });\n if (!response.success) return false;\n const stored: StoredArrival = { signature: touchSignature(touch), sessionId: this.sessionId, at: now };\n try {\n storage?.setItem(ARRIVAL_KEY, JSON.stringify(stored));\n } catch {\n return true;\n }\n return true;\n }\n\n async getAssignment(\n experimentId: string,\n userId: string = this.visitorId,\n context?: ContextualFeatures\n ): Promise<Assignment> {\n const request: AssignmentRequest = {\n experimentId,\n userId,\n context,\n sessionId: this.sessionId\n };\n const pageUrl = currentPageUrl();\n if (pageUrl) request.pageUrl = pageUrl;\n\n const response = await this.makeRequest<any>(\n '/assignments',\n 'POST',\n request\n );\n\n if (!response.success || !response.data) {\n throw requestError(response, 'Failed to get assignment');\n }\n\n const assignment = response.data?.data ?? response.data;\n\n if (!assignment?.assignmentId) {\n throw new Error('Invalid assignment response');\n }\n\n return {\n assignmentId: assignment.assignmentId,\n treatmentId: assignment.treatmentId,\n experimentId,\n userId,\n config: assignment.treatment?.config || {},\n funnelId: assignment.funnelId,\n trialOn: assignment.trialOn\n };\n }\n\n trackNamedEvent(\n event: { name: string; category?: string; value?: number; cost?: number; lineItems?: OrderLineItem[]; metadata?: Record<string, any> },\n opts?: { immediate?: boolean; userId?: string; assignmentId?: string }\n ): void {\n const request: TrackNamedEventRequest = {\n name: event.name,\n category: event.category || 'custom',\n value: event.value,\n metadata: event.metadata,\n sessionId: this.sessionId,\n };\n if (event.cost !== undefined) request.cost = event.cost;\n if (event.lineItems) request.lineItems = event.lineItems;\n if (opts?.userId) request.userId = opts.userId;\n if (opts?.assignmentId) request.assignmentId = opts.assignmentId;\n\n if (opts?.immediate) {\n const url = `${this.normalizeApiUrl()}/events`;\n try {\n void fetch(url, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(request),\n keepalive: true,\n });\n } catch {\n }\n return;\n }\n\n this.namedEventQueue.push(request);\n if (this.namedEventQueue.length >= this.config.batchSize!) {\n this.flush();\n }\n }\n\n async flush(): Promise<void> {\n const results = await Promise.allSettled([this.flushNamedEvents(), this.flushExposures()]);\n for (const result of results) {\n if (result.status === 'rejected') throw result.reason;\n }\n }\n\n private async flushNamedEvents(): Promise<void> {\n const namedEvents = [...this.namedEventQueue];\n this.namedEventQueue = [];\n if (namedEvents.length === 0) return;\n\n try {\n await fetch(`${this.normalizeApiUrl()}/events/batch`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ events: namedEvents }),\n keepalive: true,\n });\n } catch (error) {\n this.namedEventQueue.unshift(...namedEvents);\n throw error;\n }\n }\n\n private async flushExposures(): Promise<void> {\n const exposures = [...this.exposureQueue];\n this.exposureQueue = [];\n if (exposures.length === 0) return;\n\n try {\n await fetch(`${this.normalizeApiUrl()}/exposures`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ exposures }),\n keepalive: true,\n });\n } catch (error) {\n this.exposureQueue.unshift(...exposures);\n throw error;\n }\n }\n\n exposed(assignmentId: string, opts?: { immediate?: boolean }): void {\n if (this.exposedIds.has(assignmentId)) return;\n this.exposedIds.add(assignmentId);\n this.exposureQueue.push({ assignmentId, exposedAt: Date.now() });\n\n if (opts?.immediate || this.exposureQueue.length >= this.config.batchSize!) {\n this.flushExposures().catch(console.error);\n }\n }\n\n observeExposure(\n assignmentId: string,\n element: Element,\n opts?: { threshold?: number; dwellMs?: number }\n ): () => void {\n if (typeof IntersectionObserver === 'undefined') return () => {};\n const threshold = opts?.threshold ?? 0.5;\n const dwellMs = opts?.dwellMs ?? 1000;\n let dwell: ReturnType<typeof setTimeout> | undefined;\n\n const stop = () => {\n clearTimeout(dwell);\n dwell = undefined;\n };\n const observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[entries.length - 1];\n if (!entry) return;\n const coversViewport =\n entry.rootBounds != null && entry.intersectionRect.height >= threshold * entry.rootBounds.height;\n if (entry.isIntersecting && (entry.intersectionRatio >= threshold || coversViewport)) {\n dwell ??= setTimeout(() => {\n this.exposed(assignmentId);\n disconnect();\n }, dwellMs);\n } else {\n stop();\n }\n },\n { threshold: Array.from({ length: 21 }, (_, i) => i / 20) }\n );\n const disconnect = () => {\n stop();\n observer.disconnect();\n };\n observer.observe(element);\n return disconnect;\n }\n\n async identify(\n anonymousId: string,\n identifiedUserId: string,\n mergeReason?: 'user_signup' | 'email_verification' | 'social_login' | 'account_recovery' | 'manual',\n options?: { email?: string }\n ): Promise<void> {\n const response = await this.makeRequest(\n '/identify',\n 'POST',\n {\n anonymousId,\n identifiedUserId,\n mergeReason: mergeReason || 'manual',\n ...(options?.email ? { email: options.email } : {}),\n }\n );\n\n if (!response.success) {\n throw requestError(response, 'Failed to identify user');\n }\n }\n\n async label(userId: string, labels: Record<string, string | boolean>): Promise<void> {\n const response = await this.makeRequest('/labels', 'POST', { userId, labels });\n\n if (!response.success) {\n throw requestError(response, 'Failed to record labels');\n }\n }\n\n async trackFunnelReward(\n funnelId: string,\n userId: string,\n value: number\n ): Promise<void> {\n await this.flushExposures().catch(() => undefined);\n const response = await this.makeRequest(\n `/funnels/${funnelId}/reward`,\n 'POST',\n { userId, value }\n );\n\n if (!response.success) {\n throw requestError(response, 'Failed to record funnel reward');\n }\n }\n\n convert(funnelId: string, value = 1): Promise<void> {\n return this.trackFunnelReward(funnelId, this.visitorId, value);\n }\n\n async convertOn(funnelId: string, pathPattern: string, value = 1): Promise<boolean> {\n if (typeof location === 'undefined' || !matchesConversionPath(pathPattern, location.pathname)) return false;\n await this.convert(funnelId, value);\n return true;\n }\n\n async getBundle(\n bundleId: string,\n userId: string,\n context?: ContextualFeatures\n ): Promise<BundleAssignmentResponse> {\n const request: BundleAssignmentRequest = {\n bundleId,\n userId,\n context,\n sessionId: this.sessionId,\n };\n const pageUrl = currentPageUrl();\n if (pageUrl) request.pageUrl = pageUrl;\n\n const response = await this.makeRequest<any>('/bundles/assignment', 'POST', request);\n\n if (!response.success || !response.data) {\n throw requestError(response, 'Failed to get bundle assignment');\n }\n\n const payload = response.data?.data ?? response.data;\n if (!payload?.bundleAssignmentId || !payload?.slots) {\n throw new Error('Invalid bundle assignment response');\n }\n\n return {\n bundleAssignmentId: payload.bundleAssignmentId,\n slots: payload.slots,\n };\n }\n\n async trackBundleReward(bundleId: string, userId: string, value: number): Promise<void> {\n const response = await this.makeRequest(`/bundles/${bundleId}/reward`, 'POST', {\n userId,\n value,\n });\n if (!response.success) {\n throw requestError(response, 'Failed to record bundle reward');\n }\n }\n\n private generateId(): string {\n return generateId();\n }\n\n private normalizeApiUrl(): string {\n let baseUrl = this.config.apiUrl.replace(/\\/$/, '');\n if (!baseUrl.endsWith('/api')) {\n baseUrl = `${baseUrl}/api`;\n }\n return baseUrl;\n }\n\n private getOrCreateSessionId(): string {\n return getOrCreateSessionId({\n storage: defaultSessionStorage(),\n ttlMs: this.config.sessionTtl,\n });\n }\n\n private async makeRequest<T = any>(\n endpoint: string,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n body?: any,\n extraHeaders?: Record<string, string>\n ): Promise<RequestResult<T>> {\n const url = `${this.normalizeApiUrl()}${endpoint}`;\n\n const headers: Record<string, string> = {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n ...extraHeaders,\n };\n\n const requestConfig: RequestInit = {\n method,\n headers,\n signal: AbortSignal.timeout(this.config.timeout!),\n keepalive: method === 'POST',\n };\n\n if (body && method !== 'GET') {\n requestConfig.body = JSON.stringify(body);\n }\n\n try {\n const response = await fetch(url, requestConfig);\n const data = await response.json();\n \n return {\n success: response.ok,\n status: response.status,\n data: response.ok ? data as T : undefined,\n error: response.ok ? undefined : (data as any)?.message || 'Request failed',\n timestamp: new Date()\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Network error',\n timestamp: new Date()\n };\n }\n }\n\n private startBatchFlush(): void {\n this.flushTimer = setInterval(() => {\n this.flush().catch(console.error);\n }, this.config.flushInterval);\n }\n\n destroy(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n }\n this.flush().catch(console.error);\n if (typeof window !== 'undefined' && window.localStorage) {\n localStorage.removeItem('bandits_session_id');\n localStorage.removeItem('bandits_session_created_at');\n }\n }\n}","import { EMAIL_TOKEN_PARAM } from '@runbandit/shared';\n\n\nconst UNTRACKABLE_SCHEME = /^(mailto:|tel:|sms:|javascript:|data:|#)/i;\n\nconst NOTRACK_ATTRIBUTE = 'data-bandit-notrack';\n\nexport interface RewriteOptions {\n linkTable: string[];\n tokens: string[];\n}\n\nexport interface RewriteResult {\n html: string;\n rewritten: number;\n untracked: string[];\n}\n\ninterface Anchor {\n valueStart: number;\n valueEnd: number;\n url: string;\n}\n\nfunction isTrackable(url: string): boolean {\n const trimmed = url.trim();\n if (!trimmed || UNTRACKABLE_SCHEME.test(trimmed)) return false;\n return /^https?:\\/\\//i.test(trimmed);\n}\n\nfunction appendToken(url: string, token: string): string {\n const hashIndex = url.indexOf('#');\n const fragment = hashIndex === -1 ? '' : url.slice(hashIndex);\n const base = hashIndex === -1 ? url : url.slice(0, hashIndex);\n\n const separator = base.includes('?') ? (base.includes('&amp;') ? '&amp;' : '&') : '?';\n\n return `${base}${separator}${EMAIL_TOKEN_PARAM}=${token}${fragment}`;\n}\n\nfunction scanAnchors(html: string): Anchor[] {\n const anchors: Anchor[] = [];\n let i = 0;\n\n while (i < html.length) {\n const lt = html.indexOf('<', i);\n if (lt === -1) break;\n\n if (html.startsWith('<!--', lt)) {\n if (html.startsWith('<!--[if', lt) || html.startsWith('<!--<![endif]', lt)) {\n const close = html.indexOf('>', lt);\n i = close === -1 ? html.length : close + 1;\n continue;\n }\n\n const end = html.indexOf('-->', lt);\n i = end === -1 ? html.length : end + 3;\n continue;\n }\n\n const rawText = /^<(script|style)\\b/i.exec(html.slice(lt, lt + 8));\n if (rawText) {\n const closing = new RegExp(`</${rawText[1]}\\\\s*>`, 'i');\n const rest = html.slice(lt);\n const match = closing.exec(rest);\n i = match ? lt + match.index + match[0].length : html.length;\n continue;\n }\n\n const isLinkElement =\n /^<a[\\s/>]/i.test(html.slice(lt, lt + 3)) ||\n /^<v:roundrect[\\s/>]/i.test(html.slice(lt, lt + 13));\n if (!isLinkElement) {\n i = lt + 1;\n continue;\n }\n\n const tagEnd = findTagEnd(html, lt);\n if (tagEnd === -1) break;\n\n const tag = html.slice(lt, tagEnd);\n if (!new RegExp(NOTRACK_ATTRIBUTE, 'i').test(tag)) {\n const href = findHref(html, lt, tagEnd);\n if (href && isTrackable(href.url)) anchors.push(href);\n }\n\n i = tagEnd;\n }\n\n return anchors;\n}\n\nfunction findTagEnd(html: string, start: number): number {\n let quote: string | null = null;\n\n for (let i = start + 1; i < html.length; i++) {\n const ch = html[i];\n if (quote) {\n if (ch === quote) quote = null;\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch;\n continue;\n }\n if (ch === '>') return i + 1;\n }\n\n return -1;\n}\n\nfunction findHref(html: string, tagStart: number, tagEnd: number): Anchor | null {\n const tag = html.slice(tagStart, tagEnd);\n const match = /\\shref\\s*=\\s*(\"([^\"]*)\"|'([^']*)'|([^\\s>]+))/i.exec(tag);\n if (!match) return null;\n\n const quoted = match[2] !== undefined || match[3] !== undefined;\n const value = match[2] ?? match[3] ?? match[4] ?? '';\n\n const valueStart = tagStart + match.index + match[0].indexOf(match[1]) + (quoted ? 1 : 0);\n\n return { valueStart, valueEnd: valueStart + value.length, url: value };\n}\n\nexport function extractEmailLinks(html: string): string[] {\n const seen = new Set<string>();\n const urls: string[] = [];\n\n for (const anchor of scanAnchors(html)) {\n if (seen.has(anchor.url)) continue;\n seen.add(anchor.url);\n urls.push(anchor.url);\n }\n\n return urls;\n}\n\nexport function rewriteEmailLinks(html: string, options: RewriteOptions): RewriteResult {\n const { linkTable, tokens } = options;\n\n const indexOfUrl = new Map<string, number>();\n linkTable.forEach((url, index) => {\n if (!indexOfUrl.has(url)) indexOfUrl.set(url, index);\n });\n\n const anchors = scanAnchors(html);\n const untracked: string[] = [];\n let rewritten = 0;\n\n let output = html;\n for (let i = anchors.length - 1; i >= 0; i--) {\n const anchor = anchors[i];\n const index = indexOfUrl.get(anchor.url);\n\n if (index === undefined || tokens[index] === undefined) {\n untracked.push(anchor.url);\n continue;\n }\n\n output =\n output.slice(0, anchor.valueStart) +\n appendToken(anchor.url, tokens[index]) +\n output.slice(anchor.valueEnd);\n rewritten++;\n }\n\n return { html: output, rewritten, untracked: untracked.reverse() };\n}\n","import { rewriteEmailLinks, extractEmailLinks } from './email-links.js';\n\n\nconst SLOT_PATTERN = /\\{\\{\\s*bandit\\.([a-zA-Z0-9_-]+)\\s*\\}\\}/g;\n\nexport interface RenderEmailOptions {\n slots: Record<string, string>;\n linkTable: string[];\n tokens: string[];\n}\n\nexport interface RenderedEmail {\n html: string;\n rewritten: number;\n untracked: string[];\n missingSlots: string[];\n}\n\nexport function renderEmail(templateHtml: string, options: RenderEmailOptions): RenderedEmail {\n const missingSlots: string[] = [];\n\n const substituted = templateHtml.replace(SLOT_PATTERN, (match, name: string) => {\n const value = options.slots[name];\n if (value === undefined) {\n missingSlots.push(name);\n return match;\n }\n return value;\n });\n\n const { html, rewritten, untracked } = rewriteEmailLinks(substituted, {\n linkTable: options.linkTable,\n tokens: options.tokens,\n });\n\n return { html, rewritten, untracked, missingSlots };\n}\n\nexport function extractSlotNames(templateHtml: string): string[] {\n const names = new Set<string>();\n for (const match of templateHtml.matchAll(SLOT_PATTERN)) names.add(match[1]);\n return [...names];\n}\n\nexport { extractEmailLinks, rewriteEmailLinks };\nexport type { RewriteOptions, RewriteResult } from './email-links.js';\n","import {\n mintEmailToken,\n hashRecipient,\n impressionManifest,\n pickArm,\n uniformFor,\n type EmailPlan,\n} from '@runbandit/shared';\nimport { renderEmail, type RenderedEmail } from './email.js';\n\n\nexport interface PlanRenderResult extends RenderedEmail {\n combination: number[];\n tokens: string[];\n}\n\nexport async function renderFromPlan(\n plan: EmailPlan,\n recipientId: string,\n templateHtml: string,\n companySecret: string\n): Promise<PlanRenderResult> {\n const slots: Record<string, string> = {};\n const combination: number[] = [];\n\n for (let i = 0; i < plan.slots.length; i++) {\n const slot = plan.slots[i];\n const chosen = pickArm(slot.probabilities, await uniformFor(plan.salt, recipientId, i));\n const index = chosen === -1 ? 0 : chosen;\n\n combination.push(slot.arms[index]?.armIndex ?? 0);\n\n const config = slot.configs[index];\n slots[slot.name] =\n typeof config === 'string'\n ? config\n : ((config as { content?: string; text?: string } | undefined)?.content ??\n (config as { content?: string; text?: string } | undefined)?.text ??\n '');\n }\n\n const recipientHash = await hashRecipient(recipientId, companySecret);\n\n const tokens = await Promise.all(\n plan.linkTable.map((_, linkIndex) =>\n mintEmailToken(\n { c: plan.campaignId, r: recipientHash, k: combination, l: linkIndex },\n plan.signingKey\n )\n )\n );\n\n const rendered = renderEmail(templateHtml, {\n slots,\n linkTable: plan.linkTable,\n tokens,\n });\n\n return { ...rendered, combination, tokens };\n}\n\nexport function shouldRefreshPlan(\n plan: EmailPlan,\n recipientsRenderedSincePlan: number,\n now: number = Date.now()\n): boolean {\n return recipientsRenderedSincePlan >= plan.refreshEvery || now >= plan.expiresAt;\n}\n\nexport async function reportPlanImpressions(\n plan: EmailPlan,\n recipientIds: string[],\n options: { apiUrl: string; apiKey: string; batchKey?: string }\n): Promise<{ recorded: boolean; alreadyRecorded: boolean }> {\n const counts = await impressionManifest(plan, recipientIds);\n if (counts.length === 0) return { recorded: false, alreadyRecorded: false };\n\n const response = await fetch(`${options.apiUrl}/api/email/impressions`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'x-api-key': options.apiKey },\n body: JSON.stringify({\n campaignId: plan.campaignId,\n batchKey: options.batchKey ?? (await batchKeyFor(plan.campaignId, recipientIds)),\n counts,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to report impressions: ${response.status}`);\n }\n\n const body = (await response.json()) as { data?: { recorded: boolean; alreadyRecorded: boolean } };\n return body.data ?? { recorded: false, alreadyRecorded: false };\n}\n\nasync function batchKeyFor(campaignId: string, recipientIds: string[]): Promise<string> {\n const material = `${campaignId}|${[...recipientIds].sort().join(',')}`;\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(material));\n return Array.from(new Uint8Array(digest).slice(0, 16))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n"],"mappings":"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","names":["ExperimentStatus","BanditAlgorithm","Posterior","ForgettingMode","ContentType","FunnelStatus","document"]}
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as CompanySnapshot } from './decision-cookie-DgVvhn7c.js';
2
- export { c as CookieDecision, d as CookieDecisions, D as DECISIONS_COOKIE, b as SnapshotExperiment, S as SnapshotTreatment, V as VISITOR_COOKIE, p as parseDecisions, r as readCookie, s as serializeDecisions } from './decision-cookie-DgVvhn7c.js';
1
+ import { a as CompanySnapshot } from './decision-cookie-g9aDdSx9.js';
2
+ export { c as CookieDecision, d as CookieDecisions, D as DECISIONS_COOKIE, b as SnapshotExperiment, S as SnapshotTreatment, V as VISITOR_COOKIE, p as parseDecisions, r as readCookie, s as serializeDecisions } from './decision-cookie-g9aDdSx9.js';
3
3
 
4
4
  interface BanditServerConfig {
5
5
  apiUrl: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runbandit/sdk",
3
- "version": "3.5.0",
3
+ "version": "3.5.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",