@syntrologie/runtime-sdk 0.2.20 → 0.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CAPABILITIES.md +211 -0
- package/dist/api.js +7 -6
- package/dist/api.js.map +1 -1
- package/dist/bootstrap.d.ts +16 -2
- package/dist/bootstrap.js +63 -14
- package/dist/bootstrap.js.map +1 -1
- package/dist/context/ContextManager.d.ts +66 -0
- package/dist/context/ContextManager.js +268 -0
- package/dist/context/ContextManager.js.map +1 -0
- package/dist/context/index.d.ts +7 -0
- package/dist/context/index.js +7 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/schema.d.ts +360 -0
- package/dist/context/schema.js +50 -0
- package/dist/context/schema.js.map +1 -0
- package/dist/context/types.d.ts +101 -0
- package/dist/context/types.js +8 -0
- package/dist/context/types.js.map +1 -0
- package/dist/decisions/engine.d.ts +43 -0
- package/dist/decisions/engine.js +112 -0
- package/dist/decisions/engine.js.map +1 -0
- package/dist/decisions/index.d.ts +9 -0
- package/dist/decisions/index.js +10 -0
- package/dist/decisions/index.js.map +1 -0
- package/dist/decisions/schema.d.ts +2166 -0
- package/dist/decisions/schema.js +143 -0
- package/dist/decisions/schema.js.map +1 -0
- package/dist/decisions/strategies/rules.d.ts +24 -0
- package/dist/decisions/strategies/rules.js +152 -0
- package/dist/decisions/strategies/rules.js.map +1 -0
- package/dist/decisions/strategies/score.d.ts +10 -0
- package/dist/decisions/strategies/score.js +29 -0
- package/dist/decisions/strategies/score.js.map +1 -0
- package/dist/decisions/types.d.ts +242 -0
- package/dist/decisions/types.js +2 -0
- package/dist/decisions/types.js.map +1 -0
- package/dist/editorLoader.d.ts +10 -0
- package/dist/editorLoader.js +38 -0
- package/dist/editorLoader.js.map +1 -1
- package/dist/events/EventBus.d.ts +59 -0
- package/dist/events/EventBus.js +154 -0
- package/dist/events/EventBus.js.map +1 -0
- package/dist/events/index.d.ts +9 -0
- package/dist/events/index.js +10 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/normalizers/canvas.d.ts +67 -0
- package/dist/events/normalizers/canvas.js +116 -0
- package/dist/events/normalizers/canvas.js.map +1 -0
- package/dist/events/normalizers/posthog.d.ts +29 -0
- package/dist/events/normalizers/posthog.js +155 -0
- package/dist/events/normalizers/posthog.js.map +1 -0
- package/dist/events/schema.d.ts +70 -0
- package/dist/events/schema.js +30 -0
- package/dist/events/schema.js.map +1 -0
- package/dist/events/types.d.ts +73 -0
- package/dist/events/types.js +41 -0
- package/dist/events/types.js.map +1 -0
- package/dist/hooks/useShadowCanvasConfig.d.ts +5 -1
- package/dist/hooks/useShadowCanvasConfig.js +17 -3
- package/dist/hooks/useShadowCanvasConfig.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/overlays/schema.d.ts +48 -48
- package/dist/runtime.d.ts +86 -0
- package/dist/runtime.js +132 -0
- package/dist/runtime.js.map +1 -0
- package/dist/smart-canvas.esm.js +11 -11
- package/dist/smart-canvas.esm.js.map +4 -4
- package/dist/smart-canvas.js +1504 -17
- package/dist/smart-canvas.js.map +4 -4
- package/dist/smart-canvas.min.js +11 -11
- package/dist/smart-canvas.min.js.map +4 -4
- package/dist/state/StateStore.d.ts +41 -0
- package/dist/state/StateStore.js +170 -0
- package/dist/state/StateStore.js.map +1 -0
- package/dist/state/helpers/cooldowns.d.ts +7 -0
- package/dist/state/helpers/cooldowns.js +31 -0
- package/dist/state/helpers/cooldowns.js.map +1 -0
- package/dist/state/helpers/dismissals.d.ts +7 -0
- package/dist/state/helpers/dismissals.js +34 -0
- package/dist/state/helpers/dismissals.js.map +1 -0
- package/dist/state/helpers/frequency.d.ts +8 -0
- package/dist/state/helpers/frequency.js +43 -0
- package/dist/state/helpers/frequency.js.map +1 -0
- package/dist/state/index.d.ts +7 -0
- package/dist/state/index.js +7 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/schema.d.ts +49 -0
- package/dist/state/schema.js +25 -0
- package/dist/state/schema.js.map +1 -0
- package/dist/state/types.d.ts +137 -0
- package/dist/state/types.js +9 -0
- package/dist/state/types.js.map +1 -0
- package/dist/telemetry/adapters/posthog.d.ts +1 -1
- package/dist/telemetry/adapters/posthog.js +1 -1
- package/dist/telemetry/adapters/posthog.js.map +1 -1
- package/dist/types.d.ts +8 -0
- package/package.json +2 -2
- package/schema/canvas-config.schema.json +205 -0
- package/schema/runtime-context.schema.json +131 -0
package/dist/smart-canvas.js
CHANGED
|
@@ -24551,33 +24551,94 @@ var SyntrologieSDK = (() => {
|
|
|
24551
24551
|
// src/index.ts
|
|
24552
24552
|
var index_exports = {};
|
|
24553
24553
|
__export(index_exports, {
|
|
24554
|
+
ActivationConfigZ: () => ActivationConfigZ,
|
|
24555
|
+
AnchorStateZ: () => AnchorStateZ,
|
|
24556
|
+
AnchorVisibleConditionZ: () => AnchorVisibleConditionZ,
|
|
24557
|
+
AugmentationZ: () => AugmentationZ,
|
|
24558
|
+
BoundingBoxZ: () => BoundingBoxZ,
|
|
24559
|
+
CanvasEvents: () => CanvasEvents,
|
|
24554
24560
|
CanvasRecipeZ: () => CanvasRecipeZ,
|
|
24561
|
+
ConditionZ: () => ConditionZ,
|
|
24562
|
+
ContextManager: () => ContextManager,
|
|
24563
|
+
CooldownActiveConditionZ: () => CooldownActiveConditionZ,
|
|
24564
|
+
DecisionStrategyZ: () => DecisionStrategyZ,
|
|
24565
|
+
DismissedConditionZ: () => DismissedConditionZ,
|
|
24566
|
+
EVENT_SCHEMA_VERSION: () => EVENT_SCHEMA_VERSION,
|
|
24567
|
+
EventBus: () => EventBus,
|
|
24568
|
+
EventFilterZ: () => EventFilterZ,
|
|
24569
|
+
EventOccurredConditionZ: () => EventOccurredConditionZ,
|
|
24570
|
+
EventSourceZ: () => EventSourceZ,
|
|
24571
|
+
ExternalStrategyZ: () => ExternalStrategyZ,
|
|
24572
|
+
FrequencyEntryZ: () => FrequencyEntryZ,
|
|
24573
|
+
FrequencyLimitConditionZ: () => FrequencyLimitConditionZ,
|
|
24555
24574
|
HighlightStepZ: () => HighlightStepZ,
|
|
24575
|
+
ModelStrategyZ: () => ModelStrategyZ,
|
|
24576
|
+
NormalizedEventZ: () => NormalizedEventZ,
|
|
24577
|
+
PageContextZ: () => PageContextZ,
|
|
24578
|
+
PageHistoryEntryZ: () => PageHistoryEntryZ,
|
|
24579
|
+
PageUrlConditionZ: () => PageUrlConditionZ,
|
|
24580
|
+
RUNTIME_VERSION: () => RUNTIME_VERSION,
|
|
24581
|
+
RouteConditionZ: () => RouteConditionZ,
|
|
24582
|
+
RouteFilterZ: () => RouteFilterZ,
|
|
24583
|
+
RuleStrategyZ: () => RuleStrategyZ,
|
|
24584
|
+
RuleZ: () => RuleZ,
|
|
24585
|
+
RuntimeContextZ: () => RuntimeContextZ,
|
|
24586
|
+
ScoreStrategyZ: () => ScoreStrategyZ,
|
|
24556
24587
|
SelectorZ: () => SelectorZ,
|
|
24588
|
+
SessionContextZ: () => SessionContextZ,
|
|
24589
|
+
SessionMetricConditionZ: () => SessionMetricConditionZ,
|
|
24557
24590
|
SessionMetricTracker: () => SessionMetricTracker,
|
|
24558
24591
|
ShadowCanvasOverlay: () => ShadowCanvasOverlay,
|
|
24559
24592
|
SmartCanvasApp: () => SmartCanvasApp,
|
|
24560
24593
|
SmartCanvasController: () => SmartCanvasController,
|
|
24561
24594
|
SmartCanvasElement: () => SmartCanvasElement,
|
|
24562
24595
|
SmartCanvasPortal: () => SmartCanvasPortal,
|
|
24596
|
+
StandardEvents: () => StandardEvents,
|
|
24597
|
+
StateEqualsConditionZ: () => StateEqualsConditionZ,
|
|
24598
|
+
StateStore: () => StateStore,
|
|
24599
|
+
StoredValueZ: () => StoredValueZ,
|
|
24563
24600
|
Syntro: () => Syntro,
|
|
24564
24601
|
TileCard: () => TileCard,
|
|
24565
24602
|
TileWheel: () => TileWheel,
|
|
24566
24603
|
TooltipStepZ: () => TooltipStepZ,
|
|
24604
|
+
ViewportConditionZ: () => ViewportConditionZ,
|
|
24605
|
+
ViewportContextZ: () => ViewportContextZ,
|
|
24567
24606
|
createCanvasConfigFetcher: () => createCanvasConfigFetcher,
|
|
24607
|
+
createContextManager: () => createContextManager,
|
|
24608
|
+
createDecisionEngine: () => createDecisionEngine,
|
|
24609
|
+
createEventBus: () => createEventBus,
|
|
24568
24610
|
createGrowthBookClient: () => createGrowthBookClient,
|
|
24569
24611
|
createOverlayRecipeFetcher: () => createOverlayRecipeFetcher,
|
|
24570
24612
|
createPostHogClient: () => createPostHogClient,
|
|
24613
|
+
createPostHogNormalizer: () => createPostHogNormalizer,
|
|
24571
24614
|
createSessionMetricTracker: () => createSessionMetricTracker,
|
|
24572
24615
|
createSmartCanvas: () => createSmartCanvas,
|
|
24573
24616
|
createSmartCanvasController: () => createSmartCanvasController,
|
|
24617
|
+
createSmartCanvasRuntime: () => createSmartCanvasRuntime,
|
|
24618
|
+
createStateStore: () => createStateStore,
|
|
24574
24619
|
decodeToken: () => decodeToken,
|
|
24575
24620
|
encodeToken: () => encodeToken,
|
|
24621
|
+
evaluate: () => evaluate2,
|
|
24622
|
+
evaluateCondition: () => evaluateCondition,
|
|
24623
|
+
evaluateRule: () => evaluateRule,
|
|
24624
|
+
evaluateRuleStrategy: () => evaluateRuleStrategy,
|
|
24625
|
+
evaluateScoreStrategy: () => evaluateScoreStrategy,
|
|
24626
|
+
evaluateSync: () => evaluateSync,
|
|
24576
24627
|
getAntiFlickerSnippet: () => getAntiFlickerSnippet,
|
|
24628
|
+
normalizePostHogEvent: () => normalizePostHogEvent,
|
|
24577
24629
|
registerSmartCanvasElement: () => registerSmartCanvasElement,
|
|
24578
24630
|
resolveConfigUri: () => resolveConfigUri,
|
|
24631
|
+
shouldNormalizeEvent: () => shouldNormalizeEvent,
|
|
24579
24632
|
useShadowCanvasConfig: () => useShadowCanvasConfig,
|
|
24580
|
-
|
|
24633
|
+
validateActivationConfig: () => validateActivationConfig,
|
|
24634
|
+
validateCondition: () => validateCondition,
|
|
24635
|
+
validateEventFilter: () => validateEventFilter,
|
|
24636
|
+
validateFrequencyEntry: () => validateFrequencyEntry,
|
|
24637
|
+
validateNormalizedEvent: () => validateNormalizedEvent,
|
|
24638
|
+
validateRecipe: () => validateRecipe,
|
|
24639
|
+
validateRuntimeContext: () => validateRuntimeContext,
|
|
24640
|
+
validateStoredValue: () => validateStoredValue,
|
|
24641
|
+
validateStrategy: () => validateStrategy
|
|
24581
24642
|
});
|
|
24582
24643
|
|
|
24583
24644
|
// node_modules/posthog-js/dist/module.js
|
|
@@ -29118,7 +29179,7 @@ var SyntrologieSDK = (() => {
|
|
|
29118
29179
|
this.client = Uo;
|
|
29119
29180
|
const enableFeatureFlags = options.enableFeatureFlags ?? true;
|
|
29120
29181
|
this.client.init(options.apiKey, {
|
|
29121
|
-
api_host: options.apiHost ?? "https://
|
|
29182
|
+
api_host: options.apiHost ?? "https://telemetry.syntrologie.com",
|
|
29122
29183
|
// Feature flags for segment membership (in_segment_* flags)
|
|
29123
29184
|
// When enabled, /decide is called to get segment flags
|
|
29124
29185
|
advanced_disable_feature_flags: !enableFeatureFlags,
|
|
@@ -32186,7 +32247,8 @@ var SyntrologieSDK = (() => {
|
|
|
32186
32247
|
function useShadowCanvasConfig({
|
|
32187
32248
|
fetcher,
|
|
32188
32249
|
pollIntervalMs = 3e4,
|
|
32189
|
-
experiments
|
|
32250
|
+
experiments,
|
|
32251
|
+
runtime
|
|
32190
32252
|
}) {
|
|
32191
32253
|
const [state, setState] = (0, import_react.useState)({
|
|
32192
32254
|
tiles: [],
|
|
@@ -32197,7 +32259,15 @@ var SyntrologieSDK = (() => {
|
|
|
32197
32259
|
setState((prev) => ({ ...prev, isLoading: true, error: void 0 }));
|
|
32198
32260
|
const response = await fetcher();
|
|
32199
32261
|
let tiles = response.tiles || [];
|
|
32200
|
-
if (
|
|
32262
|
+
if (runtime && response.routes) {
|
|
32263
|
+
runtime.setRoutes(response.routes);
|
|
32264
|
+
}
|
|
32265
|
+
if (runtime) {
|
|
32266
|
+
tiles = await runtime.filterTiles(tiles);
|
|
32267
|
+
if (experiments) {
|
|
32268
|
+
tiles = tiles.filter((tile) => experiments.shouldRenderRectangle(tile));
|
|
32269
|
+
}
|
|
32270
|
+
} else if (experiments) {
|
|
32201
32271
|
tiles = tiles.filter((tile) => experiments.shouldRenderRectangle(tile));
|
|
32202
32272
|
}
|
|
32203
32273
|
setState({
|
|
@@ -32220,7 +32290,7 @@ var SyntrologieSDK = (() => {
|
|
|
32220
32290
|
error: err instanceof Error ? err.message : "Unknown error"
|
|
32221
32291
|
}));
|
|
32222
32292
|
}
|
|
32223
|
-
}, [experiments, fetcher]);
|
|
32293
|
+
}, [experiments, fetcher, runtime]);
|
|
32224
32294
|
(0, import_react.useEffect)(() => {
|
|
32225
32295
|
load();
|
|
32226
32296
|
if (!pollIntervalMs) return;
|
|
@@ -39021,6 +39091,33 @@ var SyntrologieSDK = (() => {
|
|
|
39021
39091
|
console.log("[Syntro Runtime] ================================");
|
|
39022
39092
|
return shouldLoad;
|
|
39023
39093
|
};
|
|
39094
|
+
var isAuditMode = () => {
|
|
39095
|
+
console.log("[Syntro Runtime] ====== AUDIT MODE CHECK ======");
|
|
39096
|
+
if (typeof window === "undefined") {
|
|
39097
|
+
console.log("[Syntro Runtime] Not in browser - skipping audit");
|
|
39098
|
+
return false;
|
|
39099
|
+
}
|
|
39100
|
+
const params = new URLSearchParams(window.location.search);
|
|
39101
|
+
const hasAuditFlag = params.has("syntro_audit");
|
|
39102
|
+
const auditSessionId = params.get("audit_session_id");
|
|
39103
|
+
console.log("[Syntro Runtime] Audit flags:", {
|
|
39104
|
+
hasAuditFlag,
|
|
39105
|
+
auditSessionId: auditSessionId ? `${auditSessionId.slice(0, 8)}...` : "none"
|
|
39106
|
+
});
|
|
39107
|
+
if (!hasAuditFlag) {
|
|
39108
|
+
console.log("[Syntro Runtime] No syntro_audit param - not in audit mode");
|
|
39109
|
+
console.log("[Syntro Runtime] ================================");
|
|
39110
|
+
return false;
|
|
39111
|
+
}
|
|
39112
|
+
console.log("[Syntro Runtime] \u2713 Audit mode active");
|
|
39113
|
+
console.log("[Syntro Runtime] ================================");
|
|
39114
|
+
return true;
|
|
39115
|
+
};
|
|
39116
|
+
var getActiveSdkMode = () => {
|
|
39117
|
+
if (shouldLoadEditor()) return "editor";
|
|
39118
|
+
if (isAuditMode()) return "audit";
|
|
39119
|
+
return null;
|
|
39120
|
+
};
|
|
39024
39121
|
var loadEditorSdk = async (editorUrlOrOptions) => {
|
|
39025
39122
|
console.log("[Syntro Runtime] ====== LOAD EDITOR SDK ======");
|
|
39026
39123
|
if (typeof window === "undefined") {
|
|
@@ -39107,7 +39204,9 @@ var SyntrologieSDK = (() => {
|
|
|
39107
39204
|
if (removeAntiFlicker) {
|
|
39108
39205
|
removeAntiFlicker();
|
|
39109
39206
|
}
|
|
39110
|
-
|
|
39207
|
+
const sdkMode = getActiveSdkMode();
|
|
39208
|
+
if (sdkMode) {
|
|
39209
|
+
console.log(`[SmartCanvas] Loading editor SDK for ${sdkMode} mode`);
|
|
39111
39210
|
loadEditorSdk(config.editorUrl).catch(console.error);
|
|
39112
39211
|
}
|
|
39113
39212
|
registerSmartCanvasElement();
|
|
@@ -39205,9 +39304,8 @@ var SyntrologieSDK = (() => {
|
|
|
39205
39304
|
}
|
|
39206
39305
|
};
|
|
39207
39306
|
if (typeof window !== "undefined") {
|
|
39208
|
-
const isEditorMode2 = new URLSearchParams(window.location.search).has("editor_token");
|
|
39209
39307
|
const isDev = true;
|
|
39210
|
-
if (
|
|
39308
|
+
if (sdkMode || isDev) {
|
|
39211
39309
|
window.__smartCanvasHandle = handle;
|
|
39212
39310
|
}
|
|
39213
39311
|
}
|
|
@@ -39366,6 +39464,1363 @@ var SyntrologieSDK = (() => {
|
|
|
39366
39464
|
return new SessionMetricTracker(options);
|
|
39367
39465
|
}
|
|
39368
39466
|
|
|
39467
|
+
// src/context/schema.ts
|
|
39468
|
+
var PageContextZ = external_exports.object({
|
|
39469
|
+
url: external_exports.string(),
|
|
39470
|
+
routeId: external_exports.string().optional(),
|
|
39471
|
+
title: external_exports.string().optional(),
|
|
39472
|
+
locale: external_exports.string().optional()
|
|
39473
|
+
});
|
|
39474
|
+
var PageHistoryEntryZ = external_exports.object({
|
|
39475
|
+
url: external_exports.string(),
|
|
39476
|
+
ts: external_exports.number()
|
|
39477
|
+
});
|
|
39478
|
+
var SessionContextZ = external_exports.object({
|
|
39479
|
+
sessionId: external_exports.string(),
|
|
39480
|
+
startTs: external_exports.number(),
|
|
39481
|
+
pageHistory: external_exports.array(PageHistoryEntryZ).optional()
|
|
39482
|
+
});
|
|
39483
|
+
var ViewportContextZ = external_exports.object({
|
|
39484
|
+
width: external_exports.number(),
|
|
39485
|
+
height: external_exports.number()
|
|
39486
|
+
});
|
|
39487
|
+
var BoundingBoxZ = external_exports.object({
|
|
39488
|
+
x: external_exports.number(),
|
|
39489
|
+
y: external_exports.number(),
|
|
39490
|
+
w: external_exports.number(),
|
|
39491
|
+
h: external_exports.number()
|
|
39492
|
+
});
|
|
39493
|
+
var AnchorStateZ = external_exports.object({
|
|
39494
|
+
anchorId: external_exports.string(),
|
|
39495
|
+
present: external_exports.boolean(),
|
|
39496
|
+
visible: external_exports.boolean().optional(),
|
|
39497
|
+
boundingBox: BoundingBoxZ.optional()
|
|
39498
|
+
});
|
|
39499
|
+
var AugmentationZ = external_exports.record(
|
|
39500
|
+
external_exports.union([external_exports.number(), external_exports.string(), external_exports.boolean(), external_exports.undefined()])
|
|
39501
|
+
);
|
|
39502
|
+
var RuntimeContextZ = external_exports.object({
|
|
39503
|
+
page: PageContextZ,
|
|
39504
|
+
session: SessionContextZ,
|
|
39505
|
+
viewport: ViewportContextZ,
|
|
39506
|
+
anchors: external_exports.array(AnchorStateZ).optional(),
|
|
39507
|
+
augmented: AugmentationZ.optional()
|
|
39508
|
+
});
|
|
39509
|
+
function validateRuntimeContext(data) {
|
|
39510
|
+
return RuntimeContextZ.safeParse(data);
|
|
39511
|
+
}
|
|
39512
|
+
|
|
39513
|
+
// src/context/ContextManager.ts
|
|
39514
|
+
function createDefaultContext() {
|
|
39515
|
+
const now = Date.now();
|
|
39516
|
+
return {
|
|
39517
|
+
page: {
|
|
39518
|
+
url: typeof window !== "undefined" ? window.location.href : "",
|
|
39519
|
+
title: typeof document !== "undefined" ? document.title : void 0
|
|
39520
|
+
},
|
|
39521
|
+
session: {
|
|
39522
|
+
sessionId: "",
|
|
39523
|
+
startTs: now,
|
|
39524
|
+
pageHistory: []
|
|
39525
|
+
},
|
|
39526
|
+
viewport: {
|
|
39527
|
+
width: typeof window !== "undefined" ? window.innerWidth : 0,
|
|
39528
|
+
height: typeof window !== "undefined" ? window.innerHeight : 0
|
|
39529
|
+
}
|
|
39530
|
+
};
|
|
39531
|
+
}
|
|
39532
|
+
function matchRoute2(url, routes) {
|
|
39533
|
+
if (!routes) return void 0;
|
|
39534
|
+
let pathname;
|
|
39535
|
+
try {
|
|
39536
|
+
pathname = new URL(url).pathname;
|
|
39537
|
+
} catch {
|
|
39538
|
+
pathname = url;
|
|
39539
|
+
}
|
|
39540
|
+
if (routes.exclude) {
|
|
39541
|
+
for (const pattern of routes.exclude) {
|
|
39542
|
+
if (matchPattern(pathname, pattern)) {
|
|
39543
|
+
return void 0;
|
|
39544
|
+
}
|
|
39545
|
+
}
|
|
39546
|
+
}
|
|
39547
|
+
if (routes.include) {
|
|
39548
|
+
for (const pattern of routes.include) {
|
|
39549
|
+
if (matchPattern(pathname, pattern)) {
|
|
39550
|
+
return pattern;
|
|
39551
|
+
}
|
|
39552
|
+
}
|
|
39553
|
+
return void 0;
|
|
39554
|
+
}
|
|
39555
|
+
return pathname;
|
|
39556
|
+
}
|
|
39557
|
+
function matchPattern(pathname, pattern) {
|
|
39558
|
+
const normalizedPath = pathname.replace(/\/$/, "") || "/";
|
|
39559
|
+
const normalizedPattern = pattern.replace(/\/$/, "") || "/";
|
|
39560
|
+
if (normalizedPath === normalizedPattern) return true;
|
|
39561
|
+
const regexPattern = normalizedPattern.replace(/:[^/]+/g, "[^/]+").replace(/\*/g, ".*");
|
|
39562
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
39563
|
+
return regex.test(normalizedPath);
|
|
39564
|
+
}
|
|
39565
|
+
var ContextManager = class {
|
|
39566
|
+
constructor(options = {}) {
|
|
39567
|
+
__publicField(this, "context");
|
|
39568
|
+
__publicField(this, "previousContext");
|
|
39569
|
+
__publicField(this, "listeners", /* @__PURE__ */ new Set());
|
|
39570
|
+
__publicField(this, "telemetry");
|
|
39571
|
+
__publicField(this, "routes");
|
|
39572
|
+
// Event listener cleanup functions
|
|
39573
|
+
__publicField(this, "cleanupFns", []);
|
|
39574
|
+
this.telemetry = options.telemetry;
|
|
39575
|
+
this.routes = options.routes;
|
|
39576
|
+
this.context = createDefaultContext();
|
|
39577
|
+
this.previousContext = { ...this.context };
|
|
39578
|
+
if (options.telemetry?.getSessionId) {
|
|
39579
|
+
const sessionId = options.telemetry.getSessionId();
|
|
39580
|
+
if (sessionId) {
|
|
39581
|
+
this.context.session.sessionId = sessionId;
|
|
39582
|
+
}
|
|
39583
|
+
}
|
|
39584
|
+
if (options.initialPageHistory) {
|
|
39585
|
+
this.context.session.pageHistory = options.initialPageHistory;
|
|
39586
|
+
}
|
|
39587
|
+
this.context.page.routeId = matchRoute2(this.context.page.url, this.routes);
|
|
39588
|
+
this.addPageToHistory(this.context.page.url);
|
|
39589
|
+
if (typeof window !== "undefined") {
|
|
39590
|
+
this.setupBrowserListeners();
|
|
39591
|
+
}
|
|
39592
|
+
}
|
|
39593
|
+
/**
|
|
39594
|
+
* Get the current runtime context.
|
|
39595
|
+
*/
|
|
39596
|
+
get() {
|
|
39597
|
+
return { ...this.context };
|
|
39598
|
+
}
|
|
39599
|
+
/**
|
|
39600
|
+
* Subscribe to context changes.
|
|
39601
|
+
* Returns an unsubscribe function.
|
|
39602
|
+
*/
|
|
39603
|
+
subscribe(callback) {
|
|
39604
|
+
this.listeners.add(callback);
|
|
39605
|
+
return () => {
|
|
39606
|
+
this.listeners.delete(callback);
|
|
39607
|
+
};
|
|
39608
|
+
}
|
|
39609
|
+
/**
|
|
39610
|
+
* Update the routes config (e.g., when config is fetched).
|
|
39611
|
+
*/
|
|
39612
|
+
setRoutes(routes) {
|
|
39613
|
+
this.routes = routes;
|
|
39614
|
+
const newRouteId = matchRoute2(this.context.page.url, this.routes);
|
|
39615
|
+
if (newRouteId !== this.context.page.routeId) {
|
|
39616
|
+
this.updateContext({ page: { ...this.context.page, routeId: newRouteId } });
|
|
39617
|
+
}
|
|
39618
|
+
}
|
|
39619
|
+
/**
|
|
39620
|
+
* Update anchor states.
|
|
39621
|
+
*/
|
|
39622
|
+
setAnchors(anchors) {
|
|
39623
|
+
this.updateContext({ anchors });
|
|
39624
|
+
}
|
|
39625
|
+
/**
|
|
39626
|
+
* Manually update the session ID (e.g., when telemetry initializes).
|
|
39627
|
+
*/
|
|
39628
|
+
setSessionId(sessionId) {
|
|
39629
|
+
if (sessionId !== this.context.session.sessionId) {
|
|
39630
|
+
this.updateContext({
|
|
39631
|
+
session: { ...this.context.session, sessionId }
|
|
39632
|
+
});
|
|
39633
|
+
}
|
|
39634
|
+
}
|
|
39635
|
+
/**
|
|
39636
|
+
* Clean up event listeners and subscriptions.
|
|
39637
|
+
*/
|
|
39638
|
+
destroy() {
|
|
39639
|
+
for (const cleanup of this.cleanupFns) {
|
|
39640
|
+
cleanup();
|
|
39641
|
+
}
|
|
39642
|
+
this.cleanupFns = [];
|
|
39643
|
+
this.listeners.clear();
|
|
39644
|
+
}
|
|
39645
|
+
// ==================== Private Methods ====================
|
|
39646
|
+
setupBrowserListeners() {
|
|
39647
|
+
let resizeTimeout;
|
|
39648
|
+
const handleResize = () => {
|
|
39649
|
+
clearTimeout(resizeTimeout);
|
|
39650
|
+
resizeTimeout = setTimeout(() => {
|
|
39651
|
+
this.updateViewport();
|
|
39652
|
+
}, 100);
|
|
39653
|
+
};
|
|
39654
|
+
window.addEventListener("resize", handleResize);
|
|
39655
|
+
this.cleanupFns.push(() => window.removeEventListener("resize", handleResize));
|
|
39656
|
+
const handlePopState = () => {
|
|
39657
|
+
this.updatePage();
|
|
39658
|
+
};
|
|
39659
|
+
window.addEventListener("popstate", handlePopState);
|
|
39660
|
+
this.cleanupFns.push(() => window.removeEventListener("popstate", handlePopState));
|
|
39661
|
+
const originalPushState = history.pushState.bind(history);
|
|
39662
|
+
const originalReplaceState = history.replaceState.bind(history);
|
|
39663
|
+
history.pushState = (...args) => {
|
|
39664
|
+
originalPushState(...args);
|
|
39665
|
+
this.updatePage();
|
|
39666
|
+
};
|
|
39667
|
+
history.replaceState = (...args) => {
|
|
39668
|
+
originalReplaceState(...args);
|
|
39669
|
+
this.updatePage();
|
|
39670
|
+
};
|
|
39671
|
+
this.cleanupFns.push(() => {
|
|
39672
|
+
history.pushState = originalPushState;
|
|
39673
|
+
history.replaceState = originalReplaceState;
|
|
39674
|
+
});
|
|
39675
|
+
}
|
|
39676
|
+
updateViewport() {
|
|
39677
|
+
const newViewport = {
|
|
39678
|
+
width: window.innerWidth,
|
|
39679
|
+
height: window.innerHeight
|
|
39680
|
+
};
|
|
39681
|
+
if (newViewport.width !== this.context.viewport.width || newViewport.height !== this.context.viewport.height) {
|
|
39682
|
+
this.updateContext({ viewport: newViewport });
|
|
39683
|
+
}
|
|
39684
|
+
}
|
|
39685
|
+
updatePage() {
|
|
39686
|
+
const url = window.location.href;
|
|
39687
|
+
const title = document.title;
|
|
39688
|
+
const routeId = matchRoute2(url, this.routes);
|
|
39689
|
+
const newPage = {
|
|
39690
|
+
url,
|
|
39691
|
+
title,
|
|
39692
|
+
routeId
|
|
39693
|
+
};
|
|
39694
|
+
if (newPage.url !== this.context.page.url || newPage.title !== this.context.page.title || newPage.routeId !== this.context.page.routeId) {
|
|
39695
|
+
if (newPage.url !== this.context.page.url) {
|
|
39696
|
+
this.addPageToHistory(newPage.url);
|
|
39697
|
+
}
|
|
39698
|
+
this.updateContext({ page: newPage });
|
|
39699
|
+
}
|
|
39700
|
+
}
|
|
39701
|
+
addPageToHistory(url) {
|
|
39702
|
+
const entry = {
|
|
39703
|
+
url,
|
|
39704
|
+
ts: Date.now()
|
|
39705
|
+
};
|
|
39706
|
+
const pageHistory = [...this.context.session.pageHistory || [], entry];
|
|
39707
|
+
if (pageHistory.length > 50) {
|
|
39708
|
+
pageHistory.shift();
|
|
39709
|
+
}
|
|
39710
|
+
this.context.session.pageHistory = pageHistory;
|
|
39711
|
+
}
|
|
39712
|
+
updateContext(partial) {
|
|
39713
|
+
this.previousContext = { ...this.context };
|
|
39714
|
+
this.context = {
|
|
39715
|
+
...this.context,
|
|
39716
|
+
...partial
|
|
39717
|
+
};
|
|
39718
|
+
this.notifyListeners();
|
|
39719
|
+
}
|
|
39720
|
+
notifyListeners() {
|
|
39721
|
+
for (const callback of this.listeners) {
|
|
39722
|
+
try {
|
|
39723
|
+
callback(this.context, this.previousContext);
|
|
39724
|
+
} catch (err) {
|
|
39725
|
+
console.error("[ContextManager] Listener error:", err);
|
|
39726
|
+
}
|
|
39727
|
+
}
|
|
39728
|
+
}
|
|
39729
|
+
};
|
|
39730
|
+
function createContextManager(options = {}) {
|
|
39731
|
+
return new ContextManager(options);
|
|
39732
|
+
}
|
|
39733
|
+
|
|
39734
|
+
// src/events/types.ts
|
|
39735
|
+
var StandardEvents = {
|
|
39736
|
+
// UI events (from PostHog autocapture)
|
|
39737
|
+
UI_CLICK: "ui.click",
|
|
39738
|
+
UI_SCROLL: "ui.scroll",
|
|
39739
|
+
UI_INPUT: "ui.input",
|
|
39740
|
+
UI_CHANGE: "ui.change",
|
|
39741
|
+
UI_SUBMIT: "ui.submit",
|
|
39742
|
+
// Navigation events
|
|
39743
|
+
NAV_PAGE_VIEW: "nav.page_view",
|
|
39744
|
+
NAV_PAGE_LEAVE: "nav.page_leave",
|
|
39745
|
+
// Canvas events
|
|
39746
|
+
CANVAS_OPENED: "canvas.opened",
|
|
39747
|
+
CANVAS_CLOSED: "canvas.closed",
|
|
39748
|
+
TILE_VIEWED: "tile.viewed",
|
|
39749
|
+
TILE_EXPANDED: "tile.expanded",
|
|
39750
|
+
TILE_COLLAPSED: "tile.collapsed",
|
|
39751
|
+
TILE_ACTION: "tile.action",
|
|
39752
|
+
// Overlay/tour events
|
|
39753
|
+
OVERLAY_STARTED: "overlay.started",
|
|
39754
|
+
OVERLAY_COMPLETED: "overlay.completed",
|
|
39755
|
+
OVERLAY_DISMISSED: "overlay.dismissed",
|
|
39756
|
+
OVERLAY_STEP_VIEWED: "overlay.step_viewed",
|
|
39757
|
+
// Derived behavioral signals (Phase 3)
|
|
39758
|
+
BEHAVIOR_RAGE_CLICK: "behavior.rage_click",
|
|
39759
|
+
BEHAVIOR_HESITATION: "behavior.hesitation",
|
|
39760
|
+
BEHAVIOR_CONFUSION: "behavior.confusion"
|
|
39761
|
+
};
|
|
39762
|
+
var EVENT_SCHEMA_VERSION = "1.0.0";
|
|
39763
|
+
|
|
39764
|
+
// src/events/schema.ts
|
|
39765
|
+
var EventSourceZ = external_exports.enum(["posthog", "canvas", "derived"]);
|
|
39766
|
+
var NormalizedEventZ = external_exports.object({
|
|
39767
|
+
ts: external_exports.number(),
|
|
39768
|
+
name: external_exports.string(),
|
|
39769
|
+
source: EventSourceZ,
|
|
39770
|
+
props: external_exports.record(external_exports.unknown()).optional(),
|
|
39771
|
+
schemaVersion: external_exports.string()
|
|
39772
|
+
});
|
|
39773
|
+
var EventFilterZ = external_exports.object({
|
|
39774
|
+
names: external_exports.array(external_exports.string()).optional(),
|
|
39775
|
+
patterns: external_exports.array(external_exports.string()).optional(),
|
|
39776
|
+
sources: external_exports.array(EventSourceZ).optional()
|
|
39777
|
+
});
|
|
39778
|
+
function validateNormalizedEvent(data) {
|
|
39779
|
+
return NormalizedEventZ.safeParse(data);
|
|
39780
|
+
}
|
|
39781
|
+
function validateEventFilter(data) {
|
|
39782
|
+
return EventFilterZ.safeParse(data);
|
|
39783
|
+
}
|
|
39784
|
+
|
|
39785
|
+
// src/events/EventBus.ts
|
|
39786
|
+
function matchesFilter(event, filter) {
|
|
39787
|
+
if (!filter) return true;
|
|
39788
|
+
if (filter.names && filter.names.length > 0) {
|
|
39789
|
+
if (!filter.names.includes(event.name)) {
|
|
39790
|
+
return false;
|
|
39791
|
+
}
|
|
39792
|
+
}
|
|
39793
|
+
if (filter.patterns && filter.patterns.length > 0) {
|
|
39794
|
+
const matched = filter.patterns.some((pattern) => {
|
|
39795
|
+
try {
|
|
39796
|
+
const regex = new RegExp(pattern);
|
|
39797
|
+
return regex.test(event.name);
|
|
39798
|
+
} catch {
|
|
39799
|
+
return false;
|
|
39800
|
+
}
|
|
39801
|
+
});
|
|
39802
|
+
if (!matched) return false;
|
|
39803
|
+
}
|
|
39804
|
+
if (filter.sources && filter.sources.length > 0) {
|
|
39805
|
+
if (!filter.sources.includes(event.source)) {
|
|
39806
|
+
return false;
|
|
39807
|
+
}
|
|
39808
|
+
}
|
|
39809
|
+
return true;
|
|
39810
|
+
}
|
|
39811
|
+
var EventBus = class {
|
|
39812
|
+
constructor(options = {}) {
|
|
39813
|
+
__publicField(this, "subscriptions", /* @__PURE__ */ new Set());
|
|
39814
|
+
__publicField(this, "history", []);
|
|
39815
|
+
__publicField(this, "maxHistorySize");
|
|
39816
|
+
this.maxHistorySize = options.maxHistorySize ?? 100;
|
|
39817
|
+
}
|
|
39818
|
+
/**
|
|
39819
|
+
* Subscribe to events matching an optional filter.
|
|
39820
|
+
* Returns an unsubscribe function.
|
|
39821
|
+
*/
|
|
39822
|
+
subscribe(filterOrCallback, maybeCallback) {
|
|
39823
|
+
let filter;
|
|
39824
|
+
let callback;
|
|
39825
|
+
if (typeof filterOrCallback === "function") {
|
|
39826
|
+
filter = void 0;
|
|
39827
|
+
callback = filterOrCallback;
|
|
39828
|
+
} else {
|
|
39829
|
+
filter = filterOrCallback;
|
|
39830
|
+
callback = maybeCallback;
|
|
39831
|
+
}
|
|
39832
|
+
const subscription = { filter, callback };
|
|
39833
|
+
this.subscriptions.add(subscription);
|
|
39834
|
+
return () => {
|
|
39835
|
+
this.subscriptions.delete(subscription);
|
|
39836
|
+
};
|
|
39837
|
+
}
|
|
39838
|
+
/**
|
|
39839
|
+
* Publish an event to all matching subscribers.
|
|
39840
|
+
*/
|
|
39841
|
+
publish(name, props, source = "canvas") {
|
|
39842
|
+
const event = {
|
|
39843
|
+
ts: Date.now(),
|
|
39844
|
+
name,
|
|
39845
|
+
source,
|
|
39846
|
+
props,
|
|
39847
|
+
schemaVersion: EVENT_SCHEMA_VERSION
|
|
39848
|
+
};
|
|
39849
|
+
this.publishEvent(event);
|
|
39850
|
+
}
|
|
39851
|
+
/**
|
|
39852
|
+
* Publish a pre-constructed NormalizedEvent.
|
|
39853
|
+
*/
|
|
39854
|
+
publishEvent(event) {
|
|
39855
|
+
this.history.push(event);
|
|
39856
|
+
if (this.history.length > this.maxHistorySize) {
|
|
39857
|
+
this.history.shift();
|
|
39858
|
+
}
|
|
39859
|
+
for (const subscription of this.subscriptions) {
|
|
39860
|
+
if (matchesFilter(event, subscription.filter)) {
|
|
39861
|
+
try {
|
|
39862
|
+
subscription.callback(event);
|
|
39863
|
+
} catch (err) {
|
|
39864
|
+
console.error("[EventBus] Subscriber error:", err);
|
|
39865
|
+
}
|
|
39866
|
+
}
|
|
39867
|
+
}
|
|
39868
|
+
}
|
|
39869
|
+
/**
|
|
39870
|
+
* Get recent events matching an optional filter.
|
|
39871
|
+
*/
|
|
39872
|
+
getRecent(filter, limit) {
|
|
39873
|
+
let events = this.history;
|
|
39874
|
+
if (filter) {
|
|
39875
|
+
events = events.filter((e2) => matchesFilter(e2, filter));
|
|
39876
|
+
}
|
|
39877
|
+
if (limit && limit > 0) {
|
|
39878
|
+
events = events.slice(-limit);
|
|
39879
|
+
}
|
|
39880
|
+
return events;
|
|
39881
|
+
}
|
|
39882
|
+
/**
|
|
39883
|
+
* Check if an event with a specific name occurred within a time window.
|
|
39884
|
+
*/
|
|
39885
|
+
hasRecentEvent(eventName, withinMs, source) {
|
|
39886
|
+
const cutoff = Date.now() - withinMs;
|
|
39887
|
+
return this.history.some(
|
|
39888
|
+
(e2) => e2.name === eventName && e2.ts >= cutoff && (source === void 0 || e2.source === source)
|
|
39889
|
+
);
|
|
39890
|
+
}
|
|
39891
|
+
/**
|
|
39892
|
+
* Count events matching a filter within a time window.
|
|
39893
|
+
*/
|
|
39894
|
+
countRecentEvents(filter, withinMs) {
|
|
39895
|
+
let events = this.history.filter((e2) => matchesFilter(e2, filter));
|
|
39896
|
+
if (withinMs !== void 0) {
|
|
39897
|
+
const cutoff = Date.now() - withinMs;
|
|
39898
|
+
events = events.filter((e2) => e2.ts >= cutoff);
|
|
39899
|
+
}
|
|
39900
|
+
return events.length;
|
|
39901
|
+
}
|
|
39902
|
+
/**
|
|
39903
|
+
* Clear all event history.
|
|
39904
|
+
*/
|
|
39905
|
+
clearHistory() {
|
|
39906
|
+
this.history = [];
|
|
39907
|
+
}
|
|
39908
|
+
/**
|
|
39909
|
+
* Get the number of current subscribers.
|
|
39910
|
+
*/
|
|
39911
|
+
getSubscriberCount() {
|
|
39912
|
+
return this.subscriptions.size;
|
|
39913
|
+
}
|
|
39914
|
+
};
|
|
39915
|
+
function createEventBus(options = {}) {
|
|
39916
|
+
return new EventBus(options);
|
|
39917
|
+
}
|
|
39918
|
+
|
|
39919
|
+
// src/events/normalizers/posthog.ts
|
|
39920
|
+
var POSTHOG_EVENT_MAP = {
|
|
39921
|
+
// Autocapture events
|
|
39922
|
+
$autocapture: "ui.click",
|
|
39923
|
+
// Default autocapture is usually clicks
|
|
39924
|
+
$click: StandardEvents.UI_CLICK,
|
|
39925
|
+
$scroll: StandardEvents.UI_SCROLL,
|
|
39926
|
+
$input: StandardEvents.UI_INPUT,
|
|
39927
|
+
$change: StandardEvents.UI_CHANGE,
|
|
39928
|
+
$submit: StandardEvents.UI_SUBMIT,
|
|
39929
|
+
// Navigation events
|
|
39930
|
+
$pageview: StandardEvents.NAV_PAGE_VIEW,
|
|
39931
|
+
$pageleave: StandardEvents.NAV_PAGE_LEAVE,
|
|
39932
|
+
// Session events
|
|
39933
|
+
$session_start: "session.start",
|
|
39934
|
+
// Identify events
|
|
39935
|
+
$identify: "user.identify"
|
|
39936
|
+
};
|
|
39937
|
+
function getEventName(phEvent) {
|
|
39938
|
+
const eventName = phEvent.event;
|
|
39939
|
+
if (POSTHOG_EVENT_MAP[eventName]) {
|
|
39940
|
+
return POSTHOG_EVENT_MAP[eventName];
|
|
39941
|
+
}
|
|
39942
|
+
if (eventName === "$autocapture") {
|
|
39943
|
+
const tagName = phEvent.properties?.$tag_name;
|
|
39944
|
+
const eventType = phEvent.properties?.$event_type;
|
|
39945
|
+
if (eventType === "submit") return StandardEvents.UI_SUBMIT;
|
|
39946
|
+
if (eventType === "change") return StandardEvents.UI_CHANGE;
|
|
39947
|
+
if (tagName === "input" || tagName === "textarea") return StandardEvents.UI_INPUT;
|
|
39948
|
+
return StandardEvents.UI_CLICK;
|
|
39949
|
+
}
|
|
39950
|
+
if (!eventName.startsWith("$")) {
|
|
39951
|
+
return `posthog.${eventName}`;
|
|
39952
|
+
}
|
|
39953
|
+
return eventName.replace("$", "posthog.");
|
|
39954
|
+
}
|
|
39955
|
+
function extractProps(phEvent) {
|
|
39956
|
+
const props = {};
|
|
39957
|
+
const phProps = phEvent.properties || {};
|
|
39958
|
+
if (phProps.$tag_name) props.tagName = phProps.$tag_name;
|
|
39959
|
+
if (phProps.$el_text) props.elementText = phProps.$el_text;
|
|
39960
|
+
if (phProps.$elements) props.elements = phProps.$elements;
|
|
39961
|
+
if (phProps.$current_url) props.url = phProps.$current_url;
|
|
39962
|
+
if (phProps.$pathname) props.pathname = phProps.$pathname;
|
|
39963
|
+
if (phProps.$host) props.host = phProps.$host;
|
|
39964
|
+
if (phProps.$viewport_width) props.viewportWidth = phProps.$viewport_width;
|
|
39965
|
+
if (phProps.$viewport_height) props.viewportHeight = phProps.$viewport_height;
|
|
39966
|
+
if (phProps.$session_id) props.sessionId = phProps.$session_id;
|
|
39967
|
+
if (phProps.$scroll_depth) props.scrollDepth = phProps.$scroll_depth;
|
|
39968
|
+
if (phProps.$scroll_percentage) props.scrollPercentage = phProps.$scroll_percentage;
|
|
39969
|
+
props.originalEvent = phEvent.event;
|
|
39970
|
+
return props;
|
|
39971
|
+
}
|
|
39972
|
+
function normalizePostHogEvent(phEvent) {
|
|
39973
|
+
let ts2;
|
|
39974
|
+
if (typeof phEvent.timestamp === "number") {
|
|
39975
|
+
ts2 = phEvent.timestamp;
|
|
39976
|
+
} else if (typeof phEvent.timestamp === "string") {
|
|
39977
|
+
ts2 = new Date(phEvent.timestamp).getTime();
|
|
39978
|
+
} else {
|
|
39979
|
+
ts2 = Date.now();
|
|
39980
|
+
}
|
|
39981
|
+
return {
|
|
39982
|
+
ts: ts2,
|
|
39983
|
+
name: getEventName(phEvent),
|
|
39984
|
+
source: "posthog",
|
|
39985
|
+
props: extractProps(phEvent),
|
|
39986
|
+
schemaVersion: EVENT_SCHEMA_VERSION
|
|
39987
|
+
};
|
|
39988
|
+
}
|
|
39989
|
+
function shouldNormalizeEvent(phEvent) {
|
|
39990
|
+
const eventName = phEvent.event;
|
|
39991
|
+
const skipEvents = [
|
|
39992
|
+
"$feature_flag_called",
|
|
39993
|
+
"$feature_flags",
|
|
39994
|
+
"$groups",
|
|
39995
|
+
"$groupidentify",
|
|
39996
|
+
"$set",
|
|
39997
|
+
"$set_once",
|
|
39998
|
+
"$unset",
|
|
39999
|
+
"$create_alias",
|
|
40000
|
+
"$capture_metrics",
|
|
40001
|
+
"$performance_event",
|
|
40002
|
+
"$web_vitals",
|
|
40003
|
+
"$exception",
|
|
40004
|
+
"$dead_click",
|
|
40005
|
+
"$heatmap"
|
|
40006
|
+
];
|
|
40007
|
+
if (skipEvents.includes(eventName)) {
|
|
40008
|
+
return false;
|
|
40009
|
+
}
|
|
40010
|
+
return true;
|
|
40011
|
+
}
|
|
40012
|
+
function createPostHogNormalizer(publishFn) {
|
|
40013
|
+
return (eventName, properties) => {
|
|
40014
|
+
const phEvent = {
|
|
40015
|
+
event: eventName,
|
|
40016
|
+
properties,
|
|
40017
|
+
timestamp: Date.now()
|
|
40018
|
+
};
|
|
40019
|
+
if (shouldNormalizeEvent(phEvent)) {
|
|
40020
|
+
const normalizedEvent = normalizePostHogEvent(phEvent);
|
|
40021
|
+
publishFn(normalizedEvent);
|
|
40022
|
+
}
|
|
40023
|
+
};
|
|
40024
|
+
}
|
|
40025
|
+
|
|
40026
|
+
// src/events/normalizers/canvas.ts
|
|
40027
|
+
function createCanvasEvent(name, props) {
|
|
40028
|
+
return {
|
|
40029
|
+
ts: Date.now(),
|
|
40030
|
+
name,
|
|
40031
|
+
source: "canvas",
|
|
40032
|
+
props,
|
|
40033
|
+
schemaVersion: EVENT_SCHEMA_VERSION
|
|
40034
|
+
};
|
|
40035
|
+
}
|
|
40036
|
+
function canvasOpened(surface) {
|
|
40037
|
+
return createCanvasEvent(StandardEvents.CANVAS_OPENED, { surface });
|
|
40038
|
+
}
|
|
40039
|
+
function canvasClosed(surface) {
|
|
40040
|
+
return createCanvasEvent(StandardEvents.CANVAS_CLOSED, { surface });
|
|
40041
|
+
}
|
|
40042
|
+
function tileViewed(tileId, surface) {
|
|
40043
|
+
return createCanvasEvent(StandardEvents.TILE_VIEWED, { tileId, surface });
|
|
40044
|
+
}
|
|
40045
|
+
function tileExpanded(tileId, surface) {
|
|
40046
|
+
return createCanvasEvent(StandardEvents.TILE_EXPANDED, { tileId, surface });
|
|
40047
|
+
}
|
|
40048
|
+
function tileCollapsed(tileId, surface) {
|
|
40049
|
+
return createCanvasEvent(StandardEvents.TILE_COLLAPSED, { tileId, surface });
|
|
40050
|
+
}
|
|
40051
|
+
function tileAction(tileId, actionId, surface) {
|
|
40052
|
+
return createCanvasEvent(StandardEvents.TILE_ACTION, {
|
|
40053
|
+
tileId,
|
|
40054
|
+
actionId,
|
|
40055
|
+
surface
|
|
40056
|
+
});
|
|
40057
|
+
}
|
|
40058
|
+
function overlayStarted(recipeId, recipeName) {
|
|
40059
|
+
return createCanvasEvent(StandardEvents.OVERLAY_STARTED, {
|
|
40060
|
+
recipeId,
|
|
40061
|
+
recipeName
|
|
40062
|
+
});
|
|
40063
|
+
}
|
|
40064
|
+
function overlayCompleted(recipeId, recipeName) {
|
|
40065
|
+
return createCanvasEvent(StandardEvents.OVERLAY_COMPLETED, {
|
|
40066
|
+
recipeId,
|
|
40067
|
+
recipeName
|
|
40068
|
+
});
|
|
40069
|
+
}
|
|
40070
|
+
function overlayDismissed(recipeId, recipeName, stepIndex) {
|
|
40071
|
+
return createCanvasEvent(StandardEvents.OVERLAY_DISMISSED, {
|
|
40072
|
+
recipeId,
|
|
40073
|
+
recipeName,
|
|
40074
|
+
stepIndex
|
|
40075
|
+
});
|
|
40076
|
+
}
|
|
40077
|
+
function overlayStepViewed(recipeId, stepIndex, stepTitle) {
|
|
40078
|
+
return createCanvasEvent(StandardEvents.OVERLAY_STEP_VIEWED, {
|
|
40079
|
+
recipeId,
|
|
40080
|
+
stepIndex,
|
|
40081
|
+
stepTitle
|
|
40082
|
+
});
|
|
40083
|
+
}
|
|
40084
|
+
function customCanvasEvent(name, props) {
|
|
40085
|
+
const eventName = name.startsWith("canvas.") ? name : `canvas.${name}`;
|
|
40086
|
+
return createCanvasEvent(eventName, props);
|
|
40087
|
+
}
|
|
40088
|
+
var CanvasEvents = {
|
|
40089
|
+
canvasOpened,
|
|
40090
|
+
canvasClosed,
|
|
40091
|
+
tileViewed,
|
|
40092
|
+
tileExpanded,
|
|
40093
|
+
tileCollapsed,
|
|
40094
|
+
tileAction,
|
|
40095
|
+
overlayStarted,
|
|
40096
|
+
overlayCompleted,
|
|
40097
|
+
overlayDismissed,
|
|
40098
|
+
overlayStepViewed,
|
|
40099
|
+
custom: customCanvasEvent
|
|
40100
|
+
};
|
|
40101
|
+
|
|
40102
|
+
// src/state/schema.ts
|
|
40103
|
+
var StoredValueZ = external_exports.object({
|
|
40104
|
+
value: external_exports.unknown(),
|
|
40105
|
+
expiresAt: external_exports.number().optional()
|
|
40106
|
+
});
|
|
40107
|
+
var FrequencyEntryZ = external_exports.object({
|
|
40108
|
+
count: external_exports.number(),
|
|
40109
|
+
resetAt: external_exports.number().optional()
|
|
40110
|
+
});
|
|
40111
|
+
function validateStoredValue(data) {
|
|
40112
|
+
return StoredValueZ.safeParse(data);
|
|
40113
|
+
}
|
|
40114
|
+
function validateFrequencyEntry(data) {
|
|
40115
|
+
return FrequencyEntryZ.safeParse(data);
|
|
40116
|
+
}
|
|
40117
|
+
|
|
40118
|
+
// src/state/helpers/dismissals.ts
|
|
40119
|
+
var DISMISSAL_PREFIX = "dismissed:";
|
|
40120
|
+
function createDismissalStore(sessionStorage2, userStorage) {
|
|
40121
|
+
return {
|
|
40122
|
+
mark(key, permanent = false) {
|
|
40123
|
+
const storageKey = DISMISSAL_PREFIX + key;
|
|
40124
|
+
const storage = permanent ? userStorage : sessionStorage2;
|
|
40125
|
+
storage.set(storageKey, true);
|
|
40126
|
+
},
|
|
40127
|
+
isDismissed(key) {
|
|
40128
|
+
const storageKey = DISMISSAL_PREFIX + key;
|
|
40129
|
+
return sessionStorage2.has(storageKey) || userStorage.has(storageKey);
|
|
40130
|
+
},
|
|
40131
|
+
clear(key) {
|
|
40132
|
+
const storageKey = DISMISSAL_PREFIX + key;
|
|
40133
|
+
sessionStorage2.remove(storageKey);
|
|
40134
|
+
userStorage.remove(storageKey);
|
|
40135
|
+
},
|
|
40136
|
+
clearAll() {
|
|
40137
|
+
for (const key of sessionStorage2.keys()) {
|
|
40138
|
+
if (key.startsWith(DISMISSAL_PREFIX)) {
|
|
40139
|
+
sessionStorage2.remove(key);
|
|
40140
|
+
}
|
|
40141
|
+
}
|
|
40142
|
+
for (const key of userStorage.keys()) {
|
|
40143
|
+
if (key.startsWith(DISMISSAL_PREFIX)) {
|
|
40144
|
+
userStorage.remove(key);
|
|
40145
|
+
}
|
|
40146
|
+
}
|
|
40147
|
+
}
|
|
40148
|
+
};
|
|
40149
|
+
}
|
|
40150
|
+
|
|
40151
|
+
// src/state/helpers/cooldowns.ts
|
|
40152
|
+
var COOLDOWN_PREFIX = "cooldown:";
|
|
40153
|
+
function createCooldownStore(storage) {
|
|
40154
|
+
return {
|
|
40155
|
+
set(key, durationMs) {
|
|
40156
|
+
const storageKey = COOLDOWN_PREFIX + key;
|
|
40157
|
+
const expiresAt = Date.now() + durationMs;
|
|
40158
|
+
storage.set(storageKey, expiresAt);
|
|
40159
|
+
},
|
|
40160
|
+
isActive(key) {
|
|
40161
|
+
return this.remaining(key) > 0;
|
|
40162
|
+
},
|
|
40163
|
+
remaining(key) {
|
|
40164
|
+
const storageKey = COOLDOWN_PREFIX + key;
|
|
40165
|
+
const expiresAt = storage.get(storageKey);
|
|
40166
|
+
if (expiresAt === void 0) return 0;
|
|
40167
|
+
const remaining = expiresAt - Date.now();
|
|
40168
|
+
if (remaining <= 0) {
|
|
40169
|
+
storage.remove(storageKey);
|
|
40170
|
+
return 0;
|
|
40171
|
+
}
|
|
40172
|
+
return remaining;
|
|
40173
|
+
},
|
|
40174
|
+
clear(key) {
|
|
40175
|
+
const storageKey = COOLDOWN_PREFIX + key;
|
|
40176
|
+
storage.remove(storageKey);
|
|
40177
|
+
}
|
|
40178
|
+
};
|
|
40179
|
+
}
|
|
40180
|
+
|
|
40181
|
+
// src/state/helpers/frequency.ts
|
|
40182
|
+
var FREQUENCY_PREFIX = "freq:";
|
|
40183
|
+
function createFrequencyStore(storage) {
|
|
40184
|
+
function getEntry(key) {
|
|
40185
|
+
const storageKey = FREQUENCY_PREFIX + key;
|
|
40186
|
+
const entry = storage.get(storageKey);
|
|
40187
|
+
if (!entry) return void 0;
|
|
40188
|
+
if (entry.resetAt && Date.now() >= entry.resetAt) {
|
|
40189
|
+
storage.remove(storageKey);
|
|
40190
|
+
return void 0;
|
|
40191
|
+
}
|
|
40192
|
+
return entry;
|
|
40193
|
+
}
|
|
40194
|
+
return {
|
|
40195
|
+
increment(key, resetAfterMs) {
|
|
40196
|
+
const storageKey = FREQUENCY_PREFIX + key;
|
|
40197
|
+
const existing = getEntry(key);
|
|
40198
|
+
const newCount = (existing?.count ?? 0) + 1;
|
|
40199
|
+
const entry = {
|
|
40200
|
+
count: newCount,
|
|
40201
|
+
// Keep existing resetAt or set new one
|
|
40202
|
+
resetAt: existing?.resetAt ?? (resetAfterMs ? Date.now() + resetAfterMs : void 0)
|
|
40203
|
+
};
|
|
40204
|
+
storage.set(storageKey, entry);
|
|
40205
|
+
return newCount;
|
|
40206
|
+
},
|
|
40207
|
+
count(key) {
|
|
40208
|
+
const entry = getEntry(key);
|
|
40209
|
+
return entry?.count ?? 0;
|
|
40210
|
+
},
|
|
40211
|
+
reset(key) {
|
|
40212
|
+
const storageKey = FREQUENCY_PREFIX + key;
|
|
40213
|
+
storage.remove(storageKey);
|
|
40214
|
+
},
|
|
40215
|
+
hasReachedLimit(key, limit) {
|
|
40216
|
+
return this.count(key) >= limit;
|
|
40217
|
+
}
|
|
40218
|
+
};
|
|
40219
|
+
}
|
|
40220
|
+
|
|
40221
|
+
// src/state/StateStore.ts
|
|
40222
|
+
var DEFAULT_NAMESPACE = "syntro";
|
|
40223
|
+
function createBrowserStorage(storage, namespace) {
|
|
40224
|
+
const prefix = `${namespace}:`;
|
|
40225
|
+
return {
|
|
40226
|
+
get(key) {
|
|
40227
|
+
if (!storage) return void 0;
|
|
40228
|
+
try {
|
|
40229
|
+
const raw = storage.getItem(prefix + key);
|
|
40230
|
+
if (!raw) return void 0;
|
|
40231
|
+
const stored = JSON.parse(raw);
|
|
40232
|
+
if (stored.expiresAt && Date.now() >= stored.expiresAt) {
|
|
40233
|
+
storage.removeItem(prefix + key);
|
|
40234
|
+
return void 0;
|
|
40235
|
+
}
|
|
40236
|
+
return stored.value;
|
|
40237
|
+
} catch {
|
|
40238
|
+
return void 0;
|
|
40239
|
+
}
|
|
40240
|
+
},
|
|
40241
|
+
set(key, value, ttlMs) {
|
|
40242
|
+
if (!storage) return;
|
|
40243
|
+
try {
|
|
40244
|
+
const stored = {
|
|
40245
|
+
value,
|
|
40246
|
+
expiresAt: ttlMs ? Date.now() + ttlMs : void 0
|
|
40247
|
+
};
|
|
40248
|
+
storage.setItem(prefix + key, JSON.stringify(stored));
|
|
40249
|
+
} catch (err) {
|
|
40250
|
+
console.warn("[StateStore] Failed to save:", err);
|
|
40251
|
+
}
|
|
40252
|
+
},
|
|
40253
|
+
remove(key) {
|
|
40254
|
+
if (!storage) return;
|
|
40255
|
+
storage.removeItem(prefix + key);
|
|
40256
|
+
},
|
|
40257
|
+
has(key) {
|
|
40258
|
+
if (!storage) return false;
|
|
40259
|
+
return this.get(key) !== void 0;
|
|
40260
|
+
},
|
|
40261
|
+
keys() {
|
|
40262
|
+
if (!storage) return [];
|
|
40263
|
+
const keys = [];
|
|
40264
|
+
for (let i2 = 0; i2 < storage.length; i2++) {
|
|
40265
|
+
const key = storage.key(i2);
|
|
40266
|
+
if (key?.startsWith(prefix)) {
|
|
40267
|
+
keys.push(key.slice(prefix.length));
|
|
40268
|
+
}
|
|
40269
|
+
}
|
|
40270
|
+
return keys;
|
|
40271
|
+
},
|
|
40272
|
+
clear() {
|
|
40273
|
+
if (!storage) return;
|
|
40274
|
+
const keysToRemove = this.keys();
|
|
40275
|
+
for (const key of keysToRemove) {
|
|
40276
|
+
storage.removeItem(prefix + key);
|
|
40277
|
+
}
|
|
40278
|
+
}
|
|
40279
|
+
};
|
|
40280
|
+
}
|
|
40281
|
+
function createMemoryStorage(namespace) {
|
|
40282
|
+
const store = /* @__PURE__ */ new Map();
|
|
40283
|
+
const prefix = `${namespace}:`;
|
|
40284
|
+
return {
|
|
40285
|
+
get(key) {
|
|
40286
|
+
const stored = store.get(prefix + key);
|
|
40287
|
+
if (!stored) return void 0;
|
|
40288
|
+
if (stored.expiresAt && Date.now() >= stored.expiresAt) {
|
|
40289
|
+
store.delete(prefix + key);
|
|
40290
|
+
return void 0;
|
|
40291
|
+
}
|
|
40292
|
+
return stored.value;
|
|
40293
|
+
},
|
|
40294
|
+
set(key, value, ttlMs) {
|
|
40295
|
+
store.set(prefix + key, {
|
|
40296
|
+
value,
|
|
40297
|
+
expiresAt: ttlMs ? Date.now() + ttlMs : void 0
|
|
40298
|
+
});
|
|
40299
|
+
},
|
|
40300
|
+
remove(key) {
|
|
40301
|
+
store.delete(prefix + key);
|
|
40302
|
+
},
|
|
40303
|
+
has(key) {
|
|
40304
|
+
return this.get(key) !== void 0;
|
|
40305
|
+
},
|
|
40306
|
+
keys() {
|
|
40307
|
+
const keys = [];
|
|
40308
|
+
for (const key of store.keys()) {
|
|
40309
|
+
if (key.startsWith(prefix)) {
|
|
40310
|
+
keys.push(key.slice(prefix.length));
|
|
40311
|
+
}
|
|
40312
|
+
}
|
|
40313
|
+
return keys;
|
|
40314
|
+
},
|
|
40315
|
+
clear() {
|
|
40316
|
+
for (const key of store.keys()) {
|
|
40317
|
+
if (key.startsWith(prefix)) {
|
|
40318
|
+
store.delete(key);
|
|
40319
|
+
}
|
|
40320
|
+
}
|
|
40321
|
+
}
|
|
40322
|
+
};
|
|
40323
|
+
}
|
|
40324
|
+
var StateStore = class {
|
|
40325
|
+
constructor(options = {}) {
|
|
40326
|
+
/** Session-scoped storage (cleared on browser close) */
|
|
40327
|
+
__publicField(this, "session");
|
|
40328
|
+
/** User-scoped storage (persists across sessions) */
|
|
40329
|
+
__publicField(this, "user");
|
|
40330
|
+
/** Dismissal tracking */
|
|
40331
|
+
__publicField(this, "dismissals");
|
|
40332
|
+
/** Cooldown tracking */
|
|
40333
|
+
__publicField(this, "cooldowns");
|
|
40334
|
+
/** Frequency tracking */
|
|
40335
|
+
__publicField(this, "frequency");
|
|
40336
|
+
__publicField(this, "namespace");
|
|
40337
|
+
__publicField(this, "namespacedStorages", /* @__PURE__ */ new Map());
|
|
40338
|
+
this.namespace = options.namespace ?? DEFAULT_NAMESPACE;
|
|
40339
|
+
if (typeof window !== "undefined") {
|
|
40340
|
+
this.session = createBrowserStorage(
|
|
40341
|
+
typeof sessionStorage !== "undefined" ? sessionStorage : void 0,
|
|
40342
|
+
this.namespace
|
|
40343
|
+
);
|
|
40344
|
+
this.user = createBrowserStorage(
|
|
40345
|
+
typeof localStorage !== "undefined" ? localStorage : void 0,
|
|
40346
|
+
this.namespace
|
|
40347
|
+
);
|
|
40348
|
+
} else {
|
|
40349
|
+
this.session = createMemoryStorage(this.namespace);
|
|
40350
|
+
this.user = createMemoryStorage(this.namespace);
|
|
40351
|
+
}
|
|
40352
|
+
this.dismissals = createDismissalStore(this.session, this.user);
|
|
40353
|
+
this.cooldowns = createCooldownStore(this.user);
|
|
40354
|
+
this.frequency = createFrequencyStore(this.session);
|
|
40355
|
+
}
|
|
40356
|
+
/**
|
|
40357
|
+
* Create a namespaced storage scope.
|
|
40358
|
+
* Useful for isolating state by adaptive/tile.
|
|
40359
|
+
*
|
|
40360
|
+
* @param namespace - Additional namespace segment
|
|
40361
|
+
* @returns A ScopedStorage with the nested namespace
|
|
40362
|
+
*/
|
|
40363
|
+
ns(namespace) {
|
|
40364
|
+
const fullNamespace = `${this.namespace}:${namespace}`;
|
|
40365
|
+
if (!this.namespacedStorages.has(fullNamespace)) {
|
|
40366
|
+
const storage = typeof window !== "undefined" && typeof sessionStorage !== "undefined" ? createBrowserStorage(sessionStorage, fullNamespace) : createMemoryStorage(fullNamespace);
|
|
40367
|
+
this.namespacedStorages.set(fullNamespace, storage);
|
|
40368
|
+
}
|
|
40369
|
+
return this.namespacedStorages.get(fullNamespace);
|
|
40370
|
+
}
|
|
40371
|
+
};
|
|
40372
|
+
function createStateStore(options = {}) {
|
|
40373
|
+
return new StateStore(options);
|
|
40374
|
+
}
|
|
40375
|
+
|
|
40376
|
+
// src/decisions/schema.ts
|
|
40377
|
+
var PageUrlConditionZ = external_exports.object({
|
|
40378
|
+
type: external_exports.literal("page_url"),
|
|
40379
|
+
url: external_exports.string()
|
|
40380
|
+
});
|
|
40381
|
+
var RouteConditionZ = external_exports.object({
|
|
40382
|
+
type: external_exports.literal("route"),
|
|
40383
|
+
routeId: external_exports.string()
|
|
40384
|
+
});
|
|
40385
|
+
var AnchorVisibleConditionZ = external_exports.object({
|
|
40386
|
+
type: external_exports.literal("anchor_visible"),
|
|
40387
|
+
anchorId: external_exports.string(),
|
|
40388
|
+
state: external_exports.enum(["visible", "present", "absent"])
|
|
40389
|
+
});
|
|
40390
|
+
var EventOccurredConditionZ = external_exports.object({
|
|
40391
|
+
type: external_exports.literal("event_occurred"),
|
|
40392
|
+
eventName: external_exports.string(),
|
|
40393
|
+
withinMs: external_exports.number().optional()
|
|
40394
|
+
});
|
|
40395
|
+
var StateEqualsConditionZ = external_exports.object({
|
|
40396
|
+
type: external_exports.literal("state_equals"),
|
|
40397
|
+
key: external_exports.string(),
|
|
40398
|
+
value: external_exports.unknown()
|
|
40399
|
+
});
|
|
40400
|
+
var ViewportConditionZ = external_exports.object({
|
|
40401
|
+
type: external_exports.literal("viewport"),
|
|
40402
|
+
minWidth: external_exports.number().optional(),
|
|
40403
|
+
maxWidth: external_exports.number().optional(),
|
|
40404
|
+
minHeight: external_exports.number().optional(),
|
|
40405
|
+
maxHeight: external_exports.number().optional()
|
|
40406
|
+
});
|
|
40407
|
+
var SessionMetricConditionZ = external_exports.object({
|
|
40408
|
+
type: external_exports.literal("session_metric"),
|
|
40409
|
+
key: external_exports.string(),
|
|
40410
|
+
operator: external_exports.enum(["gte", "lte", "eq", "gt", "lt"]),
|
|
40411
|
+
threshold: external_exports.number()
|
|
40412
|
+
});
|
|
40413
|
+
var DismissedConditionZ = external_exports.object({
|
|
40414
|
+
type: external_exports.literal("dismissed"),
|
|
40415
|
+
key: external_exports.string(),
|
|
40416
|
+
inverted: external_exports.boolean().optional()
|
|
40417
|
+
});
|
|
40418
|
+
var CooldownActiveConditionZ = external_exports.object({
|
|
40419
|
+
type: external_exports.literal("cooldown_active"),
|
|
40420
|
+
key: external_exports.string(),
|
|
40421
|
+
inverted: external_exports.boolean().optional()
|
|
40422
|
+
});
|
|
40423
|
+
var FrequencyLimitConditionZ = external_exports.object({
|
|
40424
|
+
type: external_exports.literal("frequency_limit"),
|
|
40425
|
+
key: external_exports.string(),
|
|
40426
|
+
limit: external_exports.number(),
|
|
40427
|
+
inverted: external_exports.boolean().optional()
|
|
40428
|
+
});
|
|
40429
|
+
var ConditionZ = external_exports.discriminatedUnion("type", [
|
|
40430
|
+
PageUrlConditionZ,
|
|
40431
|
+
RouteConditionZ,
|
|
40432
|
+
AnchorVisibleConditionZ,
|
|
40433
|
+
EventOccurredConditionZ,
|
|
40434
|
+
StateEqualsConditionZ,
|
|
40435
|
+
ViewportConditionZ,
|
|
40436
|
+
SessionMetricConditionZ,
|
|
40437
|
+
DismissedConditionZ,
|
|
40438
|
+
CooldownActiveConditionZ,
|
|
40439
|
+
FrequencyLimitConditionZ
|
|
40440
|
+
]);
|
|
40441
|
+
var RuleZ = external_exports.object({
|
|
40442
|
+
conditions: external_exports.array(ConditionZ),
|
|
40443
|
+
value: external_exports.unknown()
|
|
40444
|
+
});
|
|
40445
|
+
var RuleStrategyZ = external_exports.object({
|
|
40446
|
+
type: external_exports.literal("rules"),
|
|
40447
|
+
rules: external_exports.array(RuleZ),
|
|
40448
|
+
default: external_exports.unknown()
|
|
40449
|
+
});
|
|
40450
|
+
var ScoreStrategyZ = external_exports.object({
|
|
40451
|
+
type: external_exports.literal("score"),
|
|
40452
|
+
field: external_exports.string(),
|
|
40453
|
+
threshold: external_exports.number(),
|
|
40454
|
+
above: external_exports.unknown(),
|
|
40455
|
+
below: external_exports.unknown()
|
|
40456
|
+
});
|
|
40457
|
+
var ModelStrategyZ = external_exports.object({
|
|
40458
|
+
type: external_exports.literal("model"),
|
|
40459
|
+
modelId: external_exports.string(),
|
|
40460
|
+
inputs: external_exports.array(external_exports.string()),
|
|
40461
|
+
outputMapping: external_exports.record(external_exports.unknown()),
|
|
40462
|
+
default: external_exports.unknown()
|
|
40463
|
+
});
|
|
40464
|
+
var ExternalStrategyZ = external_exports.object({
|
|
40465
|
+
type: external_exports.literal("external"),
|
|
40466
|
+
endpoint: external_exports.string(),
|
|
40467
|
+
method: external_exports.enum(["GET", "POST"]).optional(),
|
|
40468
|
+
default: external_exports.unknown(),
|
|
40469
|
+
timeoutMs: external_exports.number().optional()
|
|
40470
|
+
});
|
|
40471
|
+
var DecisionStrategyZ = external_exports.discriminatedUnion("type", [
|
|
40472
|
+
RuleStrategyZ,
|
|
40473
|
+
ScoreStrategyZ,
|
|
40474
|
+
ModelStrategyZ,
|
|
40475
|
+
ExternalStrategyZ
|
|
40476
|
+
]);
|
|
40477
|
+
var RouteFilterZ = external_exports.object({
|
|
40478
|
+
include: external_exports.array(external_exports.string()).optional(),
|
|
40479
|
+
exclude: external_exports.array(external_exports.string()).optional()
|
|
40480
|
+
});
|
|
40481
|
+
var ActivationConfigZ = external_exports.object({
|
|
40482
|
+
routes: RouteFilterZ.optional(),
|
|
40483
|
+
strategy: DecisionStrategyZ.optional()
|
|
40484
|
+
});
|
|
40485
|
+
function validateCondition(data) {
|
|
40486
|
+
return ConditionZ.safeParse(data);
|
|
40487
|
+
}
|
|
40488
|
+
function validateStrategy(data) {
|
|
40489
|
+
return DecisionStrategyZ.safeParse(data);
|
|
40490
|
+
}
|
|
40491
|
+
function validateActivationConfig(data) {
|
|
40492
|
+
return ActivationConfigZ.safeParse(data);
|
|
40493
|
+
}
|
|
40494
|
+
|
|
40495
|
+
// src/decisions/strategies/rules.ts
|
|
40496
|
+
function evaluateCondition(condition, evalContext) {
|
|
40497
|
+
const { context, state, events } = evalContext;
|
|
40498
|
+
switch (condition.type) {
|
|
40499
|
+
case "page_url": {
|
|
40500
|
+
const { url } = condition;
|
|
40501
|
+
const currentUrl = context.page.url;
|
|
40502
|
+
const pattern = url.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*");
|
|
40503
|
+
const regex = new RegExp(`^${pattern}$`);
|
|
40504
|
+
return regex.test(currentUrl);
|
|
40505
|
+
}
|
|
40506
|
+
case "route": {
|
|
40507
|
+
return context.page.routeId === condition.routeId;
|
|
40508
|
+
}
|
|
40509
|
+
case "anchor_visible": {
|
|
40510
|
+
const anchor = context.anchors?.find(
|
|
40511
|
+
(a2) => a2.anchorId === condition.anchorId
|
|
40512
|
+
);
|
|
40513
|
+
switch (condition.state) {
|
|
40514
|
+
case "visible":
|
|
40515
|
+
return anchor?.visible === true;
|
|
40516
|
+
case "present":
|
|
40517
|
+
return anchor?.present === true;
|
|
40518
|
+
case "absent":
|
|
40519
|
+
return !anchor?.present;
|
|
40520
|
+
default:
|
|
40521
|
+
return false;
|
|
40522
|
+
}
|
|
40523
|
+
}
|
|
40524
|
+
case "event_occurred": {
|
|
40525
|
+
if (!events) return false;
|
|
40526
|
+
const withinMs = condition.withinMs ?? 6e4;
|
|
40527
|
+
return events.hasRecentEvent(condition.eventName, withinMs);
|
|
40528
|
+
}
|
|
40529
|
+
case "state_equals": {
|
|
40530
|
+
if (!state) return false;
|
|
40531
|
+
return false;
|
|
40532
|
+
}
|
|
40533
|
+
case "viewport": {
|
|
40534
|
+
const { width, height } = context.viewport;
|
|
40535
|
+
if (condition.minWidth !== void 0 && width < condition.minWidth)
|
|
40536
|
+
return false;
|
|
40537
|
+
if (condition.maxWidth !== void 0 && width > condition.maxWidth)
|
|
40538
|
+
return false;
|
|
40539
|
+
if (condition.minHeight !== void 0 && height < condition.minHeight)
|
|
40540
|
+
return false;
|
|
40541
|
+
if (condition.maxHeight !== void 0 && height > condition.maxHeight)
|
|
40542
|
+
return false;
|
|
40543
|
+
return true;
|
|
40544
|
+
}
|
|
40545
|
+
case "session_metric": {
|
|
40546
|
+
if (!state) return false;
|
|
40547
|
+
const metricValue = state.getSessionMetric(condition.key);
|
|
40548
|
+
const { operator, threshold } = condition;
|
|
40549
|
+
switch (operator) {
|
|
40550
|
+
case "gte":
|
|
40551
|
+
return metricValue >= threshold;
|
|
40552
|
+
case "lte":
|
|
40553
|
+
return metricValue <= threshold;
|
|
40554
|
+
case "eq":
|
|
40555
|
+
return metricValue === threshold;
|
|
40556
|
+
case "gt":
|
|
40557
|
+
return metricValue > threshold;
|
|
40558
|
+
case "lt":
|
|
40559
|
+
return metricValue < threshold;
|
|
40560
|
+
default:
|
|
40561
|
+
return false;
|
|
40562
|
+
}
|
|
40563
|
+
}
|
|
40564
|
+
case "dismissed": {
|
|
40565
|
+
if (!state) return condition.inverted ?? false;
|
|
40566
|
+
const isDismissed = state.isDismissed(condition.key);
|
|
40567
|
+
return condition.inverted ? !isDismissed : isDismissed;
|
|
40568
|
+
}
|
|
40569
|
+
case "cooldown_active": {
|
|
40570
|
+
if (!state) return condition.inverted ?? false;
|
|
40571
|
+
const isActive = state.isCooldownActive(condition.key);
|
|
40572
|
+
return condition.inverted ? !isActive : isActive;
|
|
40573
|
+
}
|
|
40574
|
+
case "frequency_limit": {
|
|
40575
|
+
if (!state) return condition.inverted ?? false;
|
|
40576
|
+
const count = state.getFrequencyCount(condition.key);
|
|
40577
|
+
const limitReached = count >= condition.limit;
|
|
40578
|
+
return condition.inverted ? !limitReached : limitReached;
|
|
40579
|
+
}
|
|
40580
|
+
default:
|
|
40581
|
+
console.warn("[RuleStrategy] Unknown condition type:", condition.type);
|
|
40582
|
+
return false;
|
|
40583
|
+
}
|
|
40584
|
+
}
|
|
40585
|
+
function evaluateRule(rule, evalContext) {
|
|
40586
|
+
const conditionResults = [];
|
|
40587
|
+
for (const condition of rule.conditions) {
|
|
40588
|
+
const result = evaluateCondition(condition, evalContext);
|
|
40589
|
+
conditionResults.push({ condition, result });
|
|
40590
|
+
if (!result) {
|
|
40591
|
+
return { matched: false, conditionResults };
|
|
40592
|
+
}
|
|
40593
|
+
}
|
|
40594
|
+
return { matched: true, conditionResults };
|
|
40595
|
+
}
|
|
40596
|
+
function evaluateRuleStrategy(strategy, evalContext) {
|
|
40597
|
+
for (let i2 = 0; i2 < strategy.rules.length; i2++) {
|
|
40598
|
+
const rule = strategy.rules[i2];
|
|
40599
|
+
const { matched, conditionResults } = evaluateRule(rule, evalContext);
|
|
40600
|
+
if (matched) {
|
|
40601
|
+
return {
|
|
40602
|
+
value: rule.value,
|
|
40603
|
+
isFallback: false,
|
|
40604
|
+
matchInfo: {
|
|
40605
|
+
strategyType: "rules",
|
|
40606
|
+
matchedRuleIndex: i2,
|
|
40607
|
+
evaluatedConditions: conditionResults
|
|
40608
|
+
}
|
|
40609
|
+
};
|
|
40610
|
+
}
|
|
40611
|
+
}
|
|
40612
|
+
return {
|
|
40613
|
+
value: strategy.default,
|
|
40614
|
+
isFallback: true,
|
|
40615
|
+
matchInfo: {
|
|
40616
|
+
strategyType: "rules"
|
|
40617
|
+
}
|
|
40618
|
+
};
|
|
40619
|
+
}
|
|
40620
|
+
|
|
40621
|
+
// src/decisions/strategies/score.ts
|
|
40622
|
+
function evaluateScoreStrategy(strategy, evalContext) {
|
|
40623
|
+
const { context } = evalContext;
|
|
40624
|
+
const score = context.augmented?.[strategy.field];
|
|
40625
|
+
if (score === void 0 || typeof score !== "number") {
|
|
40626
|
+
return {
|
|
40627
|
+
value: strategy.below,
|
|
40628
|
+
isFallback: true,
|
|
40629
|
+
matchInfo: {
|
|
40630
|
+
strategyType: "score"
|
|
40631
|
+
}
|
|
40632
|
+
};
|
|
40633
|
+
}
|
|
40634
|
+
const isAbove = score >= strategy.threshold;
|
|
40635
|
+
return {
|
|
40636
|
+
value: isAbove ? strategy.above : strategy.below,
|
|
40637
|
+
isFallback: false,
|
|
40638
|
+
matchInfo: {
|
|
40639
|
+
strategyType: "score"
|
|
40640
|
+
}
|
|
40641
|
+
};
|
|
40642
|
+
}
|
|
40643
|
+
|
|
40644
|
+
// src/decisions/engine.ts
|
|
40645
|
+
function createEvaluationContext(context, options) {
|
|
40646
|
+
const { state, events, sessionMetrics } = options;
|
|
40647
|
+
return {
|
|
40648
|
+
context,
|
|
40649
|
+
state: state ? {
|
|
40650
|
+
isDismissed: (key) => state.dismissals.isDismissed(key),
|
|
40651
|
+
isCooldownActive: (key) => state.cooldowns.isActive(key),
|
|
40652
|
+
getFrequencyCount: (key) => state.frequency.count(key),
|
|
40653
|
+
getSessionMetric: (key) => sessionMetrics?.get(key) ?? 0
|
|
40654
|
+
} : void 0,
|
|
40655
|
+
events: events ? {
|
|
40656
|
+
hasRecentEvent: (eventName, withinMs) => events.hasRecentEvent(eventName, withinMs)
|
|
40657
|
+
} : void 0
|
|
40658
|
+
};
|
|
40659
|
+
}
|
|
40660
|
+
async function evaluate2(strategy, context, options = {}) {
|
|
40661
|
+
const evalContext = createEvaluationContext(context, options);
|
|
40662
|
+
switch (strategy.type) {
|
|
40663
|
+
case "rules":
|
|
40664
|
+
return evaluateRuleStrategy(strategy, evalContext);
|
|
40665
|
+
case "score":
|
|
40666
|
+
return evaluateScoreStrategy(strategy, evalContext);
|
|
40667
|
+
case "model":
|
|
40668
|
+
return {
|
|
40669
|
+
value: strategy.default,
|
|
40670
|
+
isFallback: true,
|
|
40671
|
+
matchInfo: {
|
|
40672
|
+
strategyType: "model"
|
|
40673
|
+
}
|
|
40674
|
+
};
|
|
40675
|
+
case "external":
|
|
40676
|
+
return {
|
|
40677
|
+
value: strategy.default,
|
|
40678
|
+
isFallback: true,
|
|
40679
|
+
matchInfo: {
|
|
40680
|
+
strategyType: "external"
|
|
40681
|
+
}
|
|
40682
|
+
};
|
|
40683
|
+
default:
|
|
40684
|
+
console.warn("[DecisionEngine] Unknown strategy type:", strategy.type);
|
|
40685
|
+
return {
|
|
40686
|
+
value: void 0,
|
|
40687
|
+
isFallback: true,
|
|
40688
|
+
matchInfo: {
|
|
40689
|
+
strategyType: strategy.type
|
|
40690
|
+
}
|
|
40691
|
+
};
|
|
40692
|
+
}
|
|
40693
|
+
}
|
|
40694
|
+
function evaluateSync(strategy, context, options = {}) {
|
|
40695
|
+
const evalContext = createEvaluationContext(context, options);
|
|
40696
|
+
switch (strategy.type) {
|
|
40697
|
+
case "rules":
|
|
40698
|
+
return evaluateRuleStrategy(strategy, evalContext);
|
|
40699
|
+
case "score":
|
|
40700
|
+
return evaluateScoreStrategy(strategy, evalContext);
|
|
40701
|
+
case "model":
|
|
40702
|
+
case "external":
|
|
40703
|
+
return {
|
|
40704
|
+
value: strategy.default,
|
|
40705
|
+
isFallback: true,
|
|
40706
|
+
matchInfo: {
|
|
40707
|
+
strategyType: strategy.type
|
|
40708
|
+
}
|
|
40709
|
+
};
|
|
40710
|
+
default:
|
|
40711
|
+
return {
|
|
40712
|
+
value: void 0,
|
|
40713
|
+
isFallback: true,
|
|
40714
|
+
matchInfo: {
|
|
40715
|
+
strategyType: strategy.type
|
|
40716
|
+
}
|
|
40717
|
+
};
|
|
40718
|
+
}
|
|
40719
|
+
}
|
|
40720
|
+
function createDecisionEngine(options) {
|
|
40721
|
+
return {
|
|
40722
|
+
evaluate: (strategy, context) => evaluate2(strategy, context, options),
|
|
40723
|
+
evaluateSync: (strategy, context) => evaluateSync(strategy, context, options)
|
|
40724
|
+
};
|
|
40725
|
+
}
|
|
40726
|
+
|
|
40727
|
+
// src/runtime.ts
|
|
40728
|
+
var RUNTIME_VERSION = "2.0.0";
|
|
40729
|
+
function matchesRouteFilter(url, filter) {
|
|
40730
|
+
if (!filter) return true;
|
|
40731
|
+
let pathname;
|
|
40732
|
+
try {
|
|
40733
|
+
pathname = new URL(url).pathname;
|
|
40734
|
+
} catch {
|
|
40735
|
+
pathname = url;
|
|
40736
|
+
}
|
|
40737
|
+
const normalizedPath = pathname.replace(/\/$/, "") || "/";
|
|
40738
|
+
if (filter.exclude) {
|
|
40739
|
+
for (const pattern of filter.exclude) {
|
|
40740
|
+
if (matchRoutePattern(normalizedPath, pattern)) {
|
|
40741
|
+
return false;
|
|
40742
|
+
}
|
|
40743
|
+
}
|
|
40744
|
+
}
|
|
40745
|
+
if (filter.include && filter.include.length > 0) {
|
|
40746
|
+
for (const pattern of filter.include) {
|
|
40747
|
+
if (matchRoutePattern(normalizedPath, pattern)) {
|
|
40748
|
+
return true;
|
|
40749
|
+
}
|
|
40750
|
+
}
|
|
40751
|
+
return false;
|
|
40752
|
+
}
|
|
40753
|
+
return true;
|
|
40754
|
+
}
|
|
40755
|
+
function matchRoutePattern(pathname, pattern) {
|
|
40756
|
+
const normalizedPattern = pattern.replace(/\/$/, "") || "/";
|
|
40757
|
+
if (pathname === normalizedPattern) return true;
|
|
40758
|
+
const regexPattern = normalizedPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/:[^/]+/g, "[^/]+");
|
|
40759
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
40760
|
+
return regex.test(pathname);
|
|
40761
|
+
}
|
|
40762
|
+
function createSmartCanvasRuntime(options = {}) {
|
|
40763
|
+
const { telemetry, sessionMetrics, routes, mode = "production", namespace } = options;
|
|
40764
|
+
const context = createContextManager({
|
|
40765
|
+
telemetry,
|
|
40766
|
+
routes
|
|
40767
|
+
});
|
|
40768
|
+
const events = createEventBus();
|
|
40769
|
+
const state = createStateStore({
|
|
40770
|
+
namespace
|
|
40771
|
+
});
|
|
40772
|
+
const decisionEngine = createDecisionEngine({
|
|
40773
|
+
state,
|
|
40774
|
+
events,
|
|
40775
|
+
sessionMetrics
|
|
40776
|
+
});
|
|
40777
|
+
const runtime = {
|
|
40778
|
+
telemetry,
|
|
40779
|
+
context,
|
|
40780
|
+
events,
|
|
40781
|
+
state,
|
|
40782
|
+
sessionMetrics,
|
|
40783
|
+
version: RUNTIME_VERSION,
|
|
40784
|
+
mode,
|
|
40785
|
+
async evaluate(strategy) {
|
|
40786
|
+
return decisionEngine.evaluate(strategy, context.get());
|
|
40787
|
+
},
|
|
40788
|
+
evaluateSync(strategy) {
|
|
40789
|
+
return decisionEngine.evaluateSync(strategy, context.get());
|
|
40790
|
+
},
|
|
40791
|
+
async filterTiles(tiles) {
|
|
40792
|
+
const currentUrl = context.get().page.url;
|
|
40793
|
+
const results = [];
|
|
40794
|
+
for (const tile of tiles) {
|
|
40795
|
+
const activation = tile.activation;
|
|
40796
|
+
if (!activation) {
|
|
40797
|
+
results.push(tile);
|
|
40798
|
+
continue;
|
|
40799
|
+
}
|
|
40800
|
+
if (!matchesRouteFilter(currentUrl, activation.routes)) {
|
|
40801
|
+
continue;
|
|
40802
|
+
}
|
|
40803
|
+
if (!activation.strategy) {
|
|
40804
|
+
results.push(tile);
|
|
40805
|
+
continue;
|
|
40806
|
+
}
|
|
40807
|
+
const result = await this.evaluate(activation.strategy);
|
|
40808
|
+
if (result.value) {
|
|
40809
|
+
results.push(tile);
|
|
40810
|
+
}
|
|
40811
|
+
}
|
|
40812
|
+
return results;
|
|
40813
|
+
},
|
|
40814
|
+
setRoutes(routes2) {
|
|
40815
|
+
context.setRoutes(routes2);
|
|
40816
|
+
},
|
|
40817
|
+
destroy() {
|
|
40818
|
+
context.destroy();
|
|
40819
|
+
}
|
|
40820
|
+
};
|
|
40821
|
+
return runtime;
|
|
40822
|
+
}
|
|
40823
|
+
|
|
39369
40824
|
// src/token.ts
|
|
39370
40825
|
var TOKEN_PREFIX = "syn_";
|
|
39371
40826
|
function decodeToken(token) {
|
|
@@ -39617,6 +41072,20 @@ var SyntrologieSDK = (() => {
|
|
|
39617
41072
|
});
|
|
39618
41073
|
return hasToken;
|
|
39619
41074
|
}
|
|
41075
|
+
function isAuditMode3() {
|
|
41076
|
+
if (typeof window === "undefined") {
|
|
41077
|
+
console.log("[Syntro Bootstrap] isAuditMode: not in browser");
|
|
41078
|
+
return false;
|
|
41079
|
+
}
|
|
41080
|
+
const params = new URLSearchParams(window.location.search);
|
|
41081
|
+
const hasAuditFlag = params.has("syntro_audit");
|
|
41082
|
+
console.log("[Syntro Bootstrap] isAuditMode check:", {
|
|
41083
|
+
url: window.location.href,
|
|
41084
|
+
hasAuditFlag,
|
|
41085
|
+
auditSessionId: params.get("audit_session_id") ?? "none"
|
|
41086
|
+
});
|
|
41087
|
+
return hasAuditFlag;
|
|
41088
|
+
}
|
|
39620
41089
|
var SEGMENT_CACHE_KEY = "syntro_segment_attributes";
|
|
39621
41090
|
function loadCachedSegmentAttributes() {
|
|
39622
41091
|
if (typeof window === "undefined") return {};
|
|
@@ -39659,24 +41128,26 @@ var SyntrologieSDK = (() => {
|
|
|
39659
41128
|
hasCanvasOptions: !!options.canvas
|
|
39660
41129
|
});
|
|
39661
41130
|
const editorMode = isEditorMode();
|
|
39662
|
-
|
|
41131
|
+
const auditMode = isAuditMode3();
|
|
41132
|
+
const sdkMode = editorMode ? "editor" : auditMode ? "audit" : null;
|
|
41133
|
+
console.log("[Syntro Bootstrap] SDK mode:", sdkMode ?? "normal");
|
|
39663
41134
|
let payload;
|
|
39664
41135
|
if (options.token) {
|
|
39665
41136
|
if (options.token.startsWith("syn_")) {
|
|
39666
41137
|
console.log("[Syntro Bootstrap] Token starts with syn_, decoding...");
|
|
39667
41138
|
payload = decodeToken(options.token);
|
|
39668
|
-
} else if (!
|
|
39669
|
-
console.error("[Syntro Bootstrap] \u274C Token does not start with syn_ and NOT in editor mode!");
|
|
41139
|
+
} else if (!sdkMode) {
|
|
41140
|
+
console.error("[Syntro Bootstrap] \u274C Token does not start with syn_ and NOT in editor/audit mode!");
|
|
39670
41141
|
console.error("[Syntro Bootstrap] Token received:", options.token);
|
|
39671
41142
|
throw new Error("Invalid Syntro token: must start with 'syn_'");
|
|
39672
41143
|
} else {
|
|
39673
|
-
console.log(
|
|
41144
|
+
console.log(`[Syntro Bootstrap] \u2713 Non-syn_ token allowed (${sdkMode} mode)`);
|
|
39674
41145
|
}
|
|
39675
|
-
} else if (!
|
|
39676
|
-
console.error("[Syntro Bootstrap] \u274C No token provided and NOT in editor mode!");
|
|
39677
|
-
throw new Error("Syntro token is required (unless in editor mode)");
|
|
41146
|
+
} else if (!sdkMode) {
|
|
41147
|
+
console.error("[Syntro Bootstrap] \u274C No token provided and NOT in editor/audit mode!");
|
|
41148
|
+
throw new Error("Syntro token is required (unless in editor or audit mode)");
|
|
39678
41149
|
} else {
|
|
39679
|
-
console.log(
|
|
41150
|
+
console.log(`[Syntro Bootstrap] \u2713 No token, but ${sdkMode} mode - proceeding`);
|
|
39680
41151
|
}
|
|
39681
41152
|
const experimentHost = getEnvVar("NEXT_PUBLIC_SYNTRO_EXPERIMENT_HOST") || getEnvVar("VITE_SYNTRO_EXPERIMENT_HOST") || payload?.eh;
|
|
39682
41153
|
const telemetryHost = getEnvVar("NEXT_PUBLIC_SYNTRO_TELEMETRY_HOST") || getEnvVar("VITE_SYNTRO_TELEMETRY_HOST") || payload?.th;
|
|
@@ -39752,7 +41223,23 @@ var SyntrologieSDK = (() => {
|
|
|
39752
41223
|
integrations: { experiments, telemetry },
|
|
39753
41224
|
editorUrl
|
|
39754
41225
|
});
|
|
39755
|
-
|
|
41226
|
+
let runtimeMode = "production";
|
|
41227
|
+
if (editorMode) runtimeMode = "editor";
|
|
41228
|
+
else if (auditMode) runtimeMode = "audit";
|
|
41229
|
+
else if (getEnvVar("NODE_ENV") === "development") runtimeMode = "development";
|
|
41230
|
+
const runtime = createSmartCanvasRuntime({
|
|
41231
|
+
telemetry,
|
|
41232
|
+
sessionMetrics,
|
|
41233
|
+
mode: runtimeMode
|
|
41234
|
+
});
|
|
41235
|
+
console.log("[Syntro Bootstrap] Runtime created:", {
|
|
41236
|
+
version: runtime.version,
|
|
41237
|
+
mode: runtime.mode,
|
|
41238
|
+
hasContext: !!runtime.context,
|
|
41239
|
+
hasEvents: !!runtime.events,
|
|
41240
|
+
hasState: !!runtime.state
|
|
41241
|
+
});
|
|
41242
|
+
return { canvas, runtime, experiments, telemetry, sessionMetrics };
|
|
39756
41243
|
}
|
|
39757
41244
|
var Syntro = {
|
|
39758
41245
|
init,
|