@syntrologie/runtime-sdk 2.11.0 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CAPABILITIES.md +261 -173
- package/README.md +2 -0
- package/dist/actions/schema.d.ts +7 -7
- package/dist/actions/schema.js +3 -4
- package/dist/actions/types.d.ts +1 -1
- package/dist/actions/validation-core.d.ts +24 -0
- package/dist/actions/validation-rules.d.ts +74 -0
- package/dist/actions/validation.d.ts +5 -11
- package/dist/bootstrap-init.d.ts +33 -0
- package/dist/bootstrap-runtime.d.ts +7 -0
- package/dist/bootstrap-types.d.ts +90 -0
- package/dist/bootstrap.d.ts +19 -83
- package/dist/{chunk-Q77NT67W.js → chunk-BU4Z6PD7.js} +16 -1
- package/dist/{chunk-Q77NT67W.js.map → chunk-BU4Z6PD7.js.map} +1 -1
- package/dist/{chunk-H3FAYTUV.js → chunk-GF364MMB.js} +1343 -393
- package/dist/chunk-GF364MMB.js.map +7 -0
- package/dist/{chunk-NBFQGKSV.js → chunk-L6RJMBR2.js} +4 -4
- package/dist/{chunk-NBFQGKSV.js.map → chunk-L6RJMBR2.js.map} +2 -2
- package/dist/{chunk-37TTQRH5.js → chunk-XDYJ64IN.js} +2 -2
- package/dist/config/schema.js +2 -3
- package/dist/decisions/schema.js +1 -2
- package/dist/events/EventBus.d.ts +27 -1
- package/dist/events/history.d.ts +9 -0
- package/dist/events/index.d.ts +3 -0
- package/dist/events/types.d.ts +24 -0
- package/dist/events/validation.d.ts +7 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.js +1133 -2039
- package/dist/index.js.map +4 -4
- package/dist/overlays/runtime/overlay/overlay-runner.d.ts +4 -0
- package/dist/overlays/runtime/overlay/overlay-state.d.ts +21 -0
- package/dist/overlays/types.d.ts +3 -1
- package/dist/react.js +6 -5
- package/dist/react.js.map +2 -2
- package/dist/smart-canvas.esm.js +92 -108
- package/dist/smart-canvas.esm.js.map +4 -4
- package/dist/smart-canvas.js +4763 -4955
- package/dist/smart-canvas.js.map +4 -4
- package/dist/smart-canvas.min.js +92 -108
- package/dist/smart-canvas.min.js.map +4 -4
- package/dist/telemetry/InterventionTracker.d.ts +23 -0
- package/dist/telemetry/adapters/posthog.d.ts +5 -10
- package/dist/telemetry/index.d.ts +1 -0
- package/dist/test/setup.d.ts +1 -0
- package/dist/token.d.ts +2 -0
- package/dist/version.d.ts +1 -1
- package/package.json +23 -29
- package/schema/canvas-config.schema.json +1 -1
- package/scripts/syntroReactPlugin.mjs +3 -0
- package/scripts/validate-config.mjs +42 -0
- package/dist/chunk-H3FAYTUV.js.map +0 -7
- package/dist/chunk-JMHRHAEL.js +0 -18
- package/dist/chunk-JMHRHAEL.js.map +0 -7
- package/dist/replayMirror-QZ3GQ527.js +0 -32
- package/dist/replayMirror-QZ3GQ527.js.map +0 -7
- package/dist/telemetry/replayMirror.d.ts +0 -7
- /package/dist/{chunk-37TTQRH5.js.map → chunk-XDYJ64IN.js.map} +0 -0
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
__privateGet,
|
|
4
4
|
__privateSet,
|
|
5
5
|
__publicField
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-BU4Z6PD7.js";
|
|
7
7
|
|
|
8
8
|
// ../adaptives/adaptive-content/dist/reconciliation-guard.js
|
|
9
9
|
function guardAgainstReconciliation(container, anchor, reinsertFn, opts) {
|
|
@@ -127,6 +127,12 @@ function sanitizeHtml(html) {
|
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
|
+
const svgs = Array.from(root.querySelectorAll("svg"));
|
|
131
|
+
for (const svg of svgs) {
|
|
132
|
+
if (toRemove.some((el) => svg.contains(el) && el.tagName.toLowerCase() === "script")) {
|
|
133
|
+
toRemove.push(svg);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
130
136
|
for (const el of toRemove) {
|
|
131
137
|
while (el.firstChild) {
|
|
132
138
|
(_a2 = el.parentNode) == null ? void 0 : _a2.insertBefore(el.firstChild, el);
|
|
@@ -755,6 +761,10 @@ var CelebrationEngine = class {
|
|
|
755
761
|
this.container = null;
|
|
756
762
|
}
|
|
757
763
|
start(container, effect, config) {
|
|
764
|
+
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
765
|
+
if (prefersReducedMotion) {
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
758
768
|
this.container = container;
|
|
759
769
|
this.effect = effect;
|
|
760
770
|
this.duration = config.duration;
|
|
@@ -1398,7 +1408,7 @@ var alert = {
|
|
|
1398
1408
|
var tag = {
|
|
1399
1409
|
content: slateGrey[10],
|
|
1400
1410
|
border: slateGrey[4],
|
|
1401
|
-
background:
|
|
1411
|
+
background: slateGrey[3]
|
|
1402
1412
|
};
|
|
1403
1413
|
var menu = {
|
|
1404
1414
|
backgroundDefault: slateGrey[2],
|
|
@@ -2283,7 +2293,7 @@ function extractWorkflowsFromActive(activeActions) {
|
|
|
2283
2293
|
const workflows = /* @__PURE__ */ new Map();
|
|
2284
2294
|
for (const entry of activeActions) {
|
|
2285
2295
|
const action = entry.action;
|
|
2286
|
-
if (action.kind === "
|
|
2296
|
+
if (action.kind === "overlays:tour" && action.workflow && action.tourId) {
|
|
2287
2297
|
const meta = action.workflow;
|
|
2288
2298
|
const rawSteps = action.steps || [];
|
|
2289
2299
|
const steps = rawSteps.map((s) => {
|
|
@@ -2806,7 +2816,7 @@ var executors2 = [
|
|
|
2806
2816
|
{ kind: "overlays:badge", executor: executeBadge },
|
|
2807
2817
|
{ kind: "overlays:tooltip", executor: executeTooltip },
|
|
2808
2818
|
{ kind: "overlays:modal", executor: executeModal },
|
|
2809
|
-
{ kind: "
|
|
2819
|
+
{ kind: "overlays:tour", executor: executeTour },
|
|
2810
2820
|
{ kind: "overlays:celebrate", executor: executeCelebrate }
|
|
2811
2821
|
];
|
|
2812
2822
|
var runtime2 = {
|
|
@@ -2926,7 +2936,15 @@ var CORE_ACTION_KINDS = /* @__PURE__ */ new Set([
|
|
|
2926
2936
|
"core:wait",
|
|
2927
2937
|
"core:sequence",
|
|
2928
2938
|
"core:parallel",
|
|
2929
|
-
"
|
|
2939
|
+
"overlays:tour"
|
|
2940
|
+
]);
|
|
2941
|
+
var BUNDLED_APP_IDS = /* @__PURE__ */ new Set([
|
|
2942
|
+
"adaptive-chatbot",
|
|
2943
|
+
"adaptive-content",
|
|
2944
|
+
"adaptive-faq",
|
|
2945
|
+
"adaptive-gamification",
|
|
2946
|
+
"adaptive-nav",
|
|
2947
|
+
"adaptive-overlays"
|
|
2930
2948
|
]);
|
|
2931
2949
|
var NAMESPACE_TO_APP_ID = {
|
|
2932
2950
|
faq: "adaptive-faq",
|
|
@@ -3080,6 +3098,9 @@ function createAppLoader(options) {
|
|
|
3080
3098
|
}
|
|
3081
3099
|
}
|
|
3082
3100
|
}
|
|
3101
|
+
for (const bundled of BUNDLED_APP_IDS) {
|
|
3102
|
+
appIds.delete(bundled);
|
|
3103
|
+
}
|
|
3083
3104
|
return Array.from(appIds);
|
|
3084
3105
|
}
|
|
3085
3106
|
async function loadAppsForConfig(config) {
|
|
@@ -3435,7 +3456,7 @@ function getAntiFlickerSnippet(config = {}) {
|
|
|
3435
3456
|
}
|
|
3436
3457
|
|
|
3437
3458
|
// src/version.ts
|
|
3438
|
-
var SDK_VERSION = "2.
|
|
3459
|
+
var SDK_VERSION = "2.13.0";
|
|
3439
3460
|
|
|
3440
3461
|
// src/types.ts
|
|
3441
3462
|
var SDK_SCHEMA_VERSION = "2.0";
|
|
@@ -3586,12 +3607,14 @@ function getCachedConfig(sdkVersion) {
|
|
|
3586
3607
|
var resolveConfigUri = ({
|
|
3587
3608
|
configUri,
|
|
3588
3609
|
experiments,
|
|
3589
|
-
featureKey
|
|
3610
|
+
featureKey
|
|
3590
3611
|
}) => {
|
|
3591
3612
|
var _a2;
|
|
3592
3613
|
if (configUri) return configUri;
|
|
3593
|
-
|
|
3594
|
-
|
|
3614
|
+
if (experiments && featureKey) {
|
|
3615
|
+
const fromFeature = (_a2 = experiments.getFeatureValue) == null ? void 0 : _a2.call(experiments, featureKey, null);
|
|
3616
|
+
if (fromFeature) return fromFeature;
|
|
3617
|
+
}
|
|
3595
3618
|
return void 0;
|
|
3596
3619
|
};
|
|
3597
3620
|
function getVariantFlagKeys(experiments, manifestKey, variantFlagPrefix) {
|
|
@@ -3617,7 +3640,7 @@ var createCanvasConfigFetcher = ({
|
|
|
3617
3640
|
experiments,
|
|
3618
3641
|
featureKey,
|
|
3619
3642
|
credentials,
|
|
3620
|
-
configFeatureKey
|
|
3643
|
+
configFeatureKey,
|
|
3621
3644
|
manifestKey,
|
|
3622
3645
|
variantFlagPrefix,
|
|
3623
3646
|
sdkVersion
|
|
@@ -3812,9 +3835,529 @@ function useShadowRoot() {
|
|
|
3812
3835
|
return ctx;
|
|
3813
3836
|
}
|
|
3814
3837
|
|
|
3815
|
-
//
|
|
3816
|
-
|
|
3838
|
+
// ../event-processor/dist/types.js
|
|
3839
|
+
var EVENT_SCHEMA_VERSION = "1.0.0";
|
|
3817
3840
|
var StandardEvents = {
|
|
3841
|
+
UI_CLICK: "ui.click",
|
|
3842
|
+
UI_SCROLL: "ui.scroll",
|
|
3843
|
+
UI_INPUT: "ui.input",
|
|
3844
|
+
UI_CHANGE: "ui.change",
|
|
3845
|
+
UI_SUBMIT: "ui.submit",
|
|
3846
|
+
NAV_PAGE_VIEW: "nav.page_view",
|
|
3847
|
+
NAV_PAGE_LEAVE: "nav.page_leave",
|
|
3848
|
+
UI_HESITATE: "ui.hesitate",
|
|
3849
|
+
UI_RAGE_CLICK: "ui.rage_click",
|
|
3850
|
+
UI_SCROLL_THRASH: "ui.scroll_thrash",
|
|
3851
|
+
UI_FOCUS_BOUNCE: "ui.focus_bounce",
|
|
3852
|
+
UI_IDLE: "ui.idle",
|
|
3853
|
+
UI_HOVER: "ui.hover"
|
|
3854
|
+
};
|
|
3855
|
+
var RRWebSource = {
|
|
3856
|
+
Mutation: 0,
|
|
3857
|
+
MouseMove: 1,
|
|
3858
|
+
MouseInteraction: 2,
|
|
3859
|
+
Scroll: 3,
|
|
3860
|
+
ViewportResize: 4,
|
|
3861
|
+
Input: 5,
|
|
3862
|
+
TouchMove: 6,
|
|
3863
|
+
MediaInteraction: 7,
|
|
3864
|
+
Drag: 12
|
|
3865
|
+
};
|
|
3866
|
+
var RRWebMouseInteraction = {
|
|
3867
|
+
MouseUp: 0,
|
|
3868
|
+
MouseDown: 1,
|
|
3869
|
+
Click: 2,
|
|
3870
|
+
ContextMenu: 3,
|
|
3871
|
+
DblClick: 4,
|
|
3872
|
+
Focus: 5,
|
|
3873
|
+
Blur: 6,
|
|
3874
|
+
TouchStart: 7,
|
|
3875
|
+
TouchEnd: 9
|
|
3876
|
+
};
|
|
3877
|
+
var DEFAULT_DETECTOR_CONFIG = {
|
|
3878
|
+
hesitationMs: 3e3,
|
|
3879
|
+
hesitationRadiusPx: 10,
|
|
3880
|
+
rageClickCount: 3,
|
|
3881
|
+
rageClickWindowMs: 1e3,
|
|
3882
|
+
rageClickRadiusPx: 30,
|
|
3883
|
+
scrollThrashReversals: 3,
|
|
3884
|
+
scrollThrashWindowMs: 2e3,
|
|
3885
|
+
focusBounceMaxInputs: 0,
|
|
3886
|
+
idleMs: 5e3,
|
|
3887
|
+
hoverSampleMs: 100
|
|
3888
|
+
};
|
|
3889
|
+
|
|
3890
|
+
// ../event-processor/dist/normalizers/posthog.js
|
|
3891
|
+
var POSTHOG_EVENT_MAP = {
|
|
3892
|
+
// NOTE: $autocapture is intentionally NOT in this map.
|
|
3893
|
+
// It's handled below in getEventName() with $event_type refinement
|
|
3894
|
+
// so that change/submit events aren't all mapped to ui.click.
|
|
3895
|
+
$click: StandardEvents.UI_CLICK,
|
|
3896
|
+
$scroll: StandardEvents.UI_SCROLL,
|
|
3897
|
+
$input: StandardEvents.UI_INPUT,
|
|
3898
|
+
$change: StandardEvents.UI_CHANGE,
|
|
3899
|
+
$submit: StandardEvents.UI_SUBMIT,
|
|
3900
|
+
// Navigation events
|
|
3901
|
+
$pageview: StandardEvents.NAV_PAGE_VIEW,
|
|
3902
|
+
$pageleave: StandardEvents.NAV_PAGE_LEAVE,
|
|
3903
|
+
// Session events
|
|
3904
|
+
$session_start: "session.start",
|
|
3905
|
+
// Identify events
|
|
3906
|
+
$identify: "user.identify"
|
|
3907
|
+
};
|
|
3908
|
+
function getEventName(phEvent) {
|
|
3909
|
+
var _a2, _b;
|
|
3910
|
+
const eventName = phEvent.event;
|
|
3911
|
+
if (typeof eventName !== "string") {
|
|
3912
|
+
return "posthog.unknown";
|
|
3913
|
+
}
|
|
3914
|
+
if (POSTHOG_EVENT_MAP[eventName]) {
|
|
3915
|
+
return POSTHOG_EVENT_MAP[eventName];
|
|
3916
|
+
}
|
|
3917
|
+
if (eventName === "$autocapture") {
|
|
3918
|
+
const tagName = (_a2 = phEvent.properties) == null ? void 0 : _a2.$tag_name;
|
|
3919
|
+
const eventType = (_b = phEvent.properties) == null ? void 0 : _b.$event_type;
|
|
3920
|
+
if (eventType === "submit")
|
|
3921
|
+
return StandardEvents.UI_SUBMIT;
|
|
3922
|
+
if (eventType === "change")
|
|
3923
|
+
return StandardEvents.UI_CHANGE;
|
|
3924
|
+
if (tagName === "input" || tagName === "textarea")
|
|
3925
|
+
return StandardEvents.UI_INPUT;
|
|
3926
|
+
return StandardEvents.UI_CLICK;
|
|
3927
|
+
}
|
|
3928
|
+
if (!eventName.startsWith("$")) {
|
|
3929
|
+
return `posthog.${eventName}`;
|
|
3930
|
+
}
|
|
3931
|
+
return eventName.replace("$", "posthog.");
|
|
3932
|
+
}
|
|
3933
|
+
var INTERACTIVE_TAGS = /* @__PURE__ */ new Set(["a", "button", "input", "select", "textarea"]);
|
|
3934
|
+
function resolveInteractiveTag(elements, directTag) {
|
|
3935
|
+
if (directTag && INTERACTIVE_TAGS.has(directTag))
|
|
3936
|
+
return directTag;
|
|
3937
|
+
if (!elements)
|
|
3938
|
+
return directTag;
|
|
3939
|
+
for (const el of elements) {
|
|
3940
|
+
const tag2 = el.tag_name;
|
|
3941
|
+
if (tag2 && INTERACTIVE_TAGS.has(tag2))
|
|
3942
|
+
return tag2;
|
|
3943
|
+
}
|
|
3944
|
+
return directTag;
|
|
3945
|
+
}
|
|
3946
|
+
function extractProps(phEvent) {
|
|
3947
|
+
var _a2, _b;
|
|
3948
|
+
const props = {};
|
|
3949
|
+
const phProps = phEvent.properties || {};
|
|
3950
|
+
const elements = phProps.$elements;
|
|
3951
|
+
const directTag = (_b = phProps.$tag_name) != null ? _b : (_a2 = elements == null ? void 0 : elements[0]) == null ? void 0 : _a2.tag_name;
|
|
3952
|
+
const isClickEvent = phEvent.event === "$autocapture" || phEvent.event === "$click";
|
|
3953
|
+
props.tagName = isClickEvent ? resolveInteractiveTag(elements, directTag) : directTag;
|
|
3954
|
+
if (phProps.$el_text)
|
|
3955
|
+
props.elementText = phProps.$el_text;
|
|
3956
|
+
if (elements)
|
|
3957
|
+
props.elements = elements;
|
|
3958
|
+
if (phProps.$current_url)
|
|
3959
|
+
props.url = phProps.$current_url;
|
|
3960
|
+
if (phProps.$pathname)
|
|
3961
|
+
props.pathname = phProps.$pathname;
|
|
3962
|
+
if (phProps.$host)
|
|
3963
|
+
props.host = phProps.$host;
|
|
3964
|
+
if (phProps.$viewport_width)
|
|
3965
|
+
props.viewportWidth = phProps.$viewport_width;
|
|
3966
|
+
if (phProps.$viewport_height)
|
|
3967
|
+
props.viewportHeight = phProps.$viewport_height;
|
|
3968
|
+
if (phProps.$session_id)
|
|
3969
|
+
props.sessionId = phProps.$session_id;
|
|
3970
|
+
if (phProps.$scroll_depth)
|
|
3971
|
+
props.scrollDepth = phProps.$scroll_depth;
|
|
3972
|
+
if (phProps.$scroll_percentage)
|
|
3973
|
+
props.scrollPercentage = phProps.$scroll_percentage;
|
|
3974
|
+
props.originalEvent = phEvent.event;
|
|
3975
|
+
return props;
|
|
3976
|
+
}
|
|
3977
|
+
function normalizePostHogEvent(phEvent) {
|
|
3978
|
+
let ts;
|
|
3979
|
+
if (typeof phEvent.timestamp === "number") {
|
|
3980
|
+
ts = phEvent.timestamp;
|
|
3981
|
+
} else if (typeof phEvent.timestamp === "string") {
|
|
3982
|
+
ts = new Date(phEvent.timestamp).getTime();
|
|
3983
|
+
} else {
|
|
3984
|
+
ts = Date.now();
|
|
3985
|
+
}
|
|
3986
|
+
return {
|
|
3987
|
+
ts,
|
|
3988
|
+
name: getEventName(phEvent),
|
|
3989
|
+
source: "posthog",
|
|
3990
|
+
props: extractProps(phEvent),
|
|
3991
|
+
schemaVersion: EVENT_SCHEMA_VERSION
|
|
3992
|
+
};
|
|
3993
|
+
}
|
|
3994
|
+
function shouldNormalizeEvent(phEvent) {
|
|
3995
|
+
const eventName = phEvent.event;
|
|
3996
|
+
if (typeof eventName !== "string")
|
|
3997
|
+
return false;
|
|
3998
|
+
const skipEvents = [
|
|
3999
|
+
"$feature_flag_called",
|
|
4000
|
+
"$feature_flags",
|
|
4001
|
+
"$groups",
|
|
4002
|
+
"$groupidentify",
|
|
4003
|
+
"$set",
|
|
4004
|
+
"$set_once",
|
|
4005
|
+
"$unset",
|
|
4006
|
+
"$create_alias",
|
|
4007
|
+
"$capture_metrics",
|
|
4008
|
+
"$performance_event",
|
|
4009
|
+
"$web_vitals",
|
|
4010
|
+
"$exception",
|
|
4011
|
+
"$dead_click",
|
|
4012
|
+
"$heatmap"
|
|
4013
|
+
];
|
|
4014
|
+
if (skipEvents.includes(eventName)) {
|
|
4015
|
+
return false;
|
|
4016
|
+
}
|
|
4017
|
+
return true;
|
|
4018
|
+
}
|
|
4019
|
+
function createPostHogNormalizer(publishFn) {
|
|
4020
|
+
return (eventName, properties) => {
|
|
4021
|
+
if (typeof eventName !== "string")
|
|
4022
|
+
return;
|
|
4023
|
+
const phEvent = {
|
|
4024
|
+
event: eventName,
|
|
4025
|
+
properties,
|
|
4026
|
+
timestamp: Date.now()
|
|
4027
|
+
};
|
|
4028
|
+
if (shouldNormalizeEvent(phEvent)) {
|
|
4029
|
+
const normalizedEvent = normalizePostHogEvent(phEvent);
|
|
4030
|
+
publishFn(normalizedEvent);
|
|
4031
|
+
}
|
|
4032
|
+
};
|
|
4033
|
+
}
|
|
4034
|
+
|
|
4035
|
+
// ../event-processor/dist/detectors/focus-bounce.js
|
|
4036
|
+
var FocusBounceDetector = class {
|
|
4037
|
+
constructor(config, emit2) {
|
|
4038
|
+
this.config = config;
|
|
4039
|
+
this.emit = emit2;
|
|
4040
|
+
this.focused = /* @__PURE__ */ new Map();
|
|
4041
|
+
}
|
|
4042
|
+
ingest(raw) {
|
|
4043
|
+
var _a2, _b;
|
|
4044
|
+
if (raw.type !== 3)
|
|
4045
|
+
return;
|
|
4046
|
+
const ts = raw.timestamp;
|
|
4047
|
+
if (raw.data.source === RRWebSource.MouseInteraction) {
|
|
4048
|
+
const id = (_a2 = raw.data.id) != null ? _a2 : 0;
|
|
4049
|
+
if (raw.data.type === RRWebMouseInteraction.Focus) {
|
|
4050
|
+
this.focused.set(id, { id, focusTs: ts, inputCount: 0 });
|
|
4051
|
+
} else if (raw.data.type === RRWebMouseInteraction.Blur) {
|
|
4052
|
+
const entry = this.focused.get(id);
|
|
4053
|
+
if (entry && entry.inputCount <= this.config.focusBounceMaxInputs) {
|
|
4054
|
+
this.emit({
|
|
4055
|
+
ts,
|
|
4056
|
+
name: StandardEvents.UI_FOCUS_BOUNCE,
|
|
4057
|
+
source: "rrweb",
|
|
4058
|
+
schemaVersion: EVENT_SCHEMA_VERSION,
|
|
4059
|
+
props: {
|
|
4060
|
+
elementId: id,
|
|
4061
|
+
duration_ms: ts - entry.focusTs
|
|
4062
|
+
}
|
|
4063
|
+
});
|
|
4064
|
+
}
|
|
4065
|
+
this.focused.delete(id);
|
|
4066
|
+
}
|
|
4067
|
+
} else if (raw.data.source === RRWebSource.Input) {
|
|
4068
|
+
const id = (_b = raw.data.id) != null ? _b : 0;
|
|
4069
|
+
const entry = this.focused.get(id);
|
|
4070
|
+
if (entry) {
|
|
4071
|
+
entry.inputCount++;
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
}
|
|
4075
|
+
};
|
|
4076
|
+
|
|
4077
|
+
// ../event-processor/dist/detectors/hesitation.js
|
|
4078
|
+
var HesitationDetector = class {
|
|
4079
|
+
constructor(config, emit2) {
|
|
4080
|
+
this.config = config;
|
|
4081
|
+
this.emit = emit2;
|
|
4082
|
+
this.anchorX = 0;
|
|
4083
|
+
this.anchorY = 0;
|
|
4084
|
+
this.anchorTs = 0;
|
|
4085
|
+
this.emitted = false;
|
|
4086
|
+
this.hasPosition = false;
|
|
4087
|
+
}
|
|
4088
|
+
ingest(raw) {
|
|
4089
|
+
var _a2;
|
|
4090
|
+
if (raw.type !== 3 || raw.data.source !== RRWebSource.MouseMove)
|
|
4091
|
+
return;
|
|
4092
|
+
const positions = raw.data.positions;
|
|
4093
|
+
if (!positions || positions.length === 0)
|
|
4094
|
+
return;
|
|
4095
|
+
const last = positions[positions.length - 1];
|
|
4096
|
+
const ts = raw.timestamp + ((_a2 = last.timeOffset) != null ? _a2 : 0);
|
|
4097
|
+
if (!this.hasPosition) {
|
|
4098
|
+
this.anchorX = last.x;
|
|
4099
|
+
this.anchorY = last.y;
|
|
4100
|
+
this.anchorTs = ts;
|
|
4101
|
+
this.hasPosition = true;
|
|
4102
|
+
this.emitted = false;
|
|
4103
|
+
return;
|
|
4104
|
+
}
|
|
4105
|
+
const dx = last.x - this.anchorX;
|
|
4106
|
+
const dy = last.y - this.anchorY;
|
|
4107
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
4108
|
+
if (dist > this.config.hesitationRadiusPx) {
|
|
4109
|
+
this.anchorX = last.x;
|
|
4110
|
+
this.anchorY = last.y;
|
|
4111
|
+
this.anchorTs = ts;
|
|
4112
|
+
this.emitted = false;
|
|
4113
|
+
}
|
|
4114
|
+
}
|
|
4115
|
+
tick(now) {
|
|
4116
|
+
if (!this.hasPosition || this.emitted)
|
|
4117
|
+
return;
|
|
4118
|
+
const elapsed = now - this.anchorTs;
|
|
4119
|
+
if (elapsed >= this.config.hesitationMs) {
|
|
4120
|
+
this.emit({
|
|
4121
|
+
ts: now,
|
|
4122
|
+
name: StandardEvents.UI_HESITATE,
|
|
4123
|
+
source: "rrweb",
|
|
4124
|
+
schemaVersion: EVENT_SCHEMA_VERSION,
|
|
4125
|
+
props: {
|
|
4126
|
+
x: this.anchorX,
|
|
4127
|
+
y: this.anchorY,
|
|
4128
|
+
duration_ms: elapsed
|
|
4129
|
+
}
|
|
4130
|
+
});
|
|
4131
|
+
this.emitted = true;
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
};
|
|
4135
|
+
|
|
4136
|
+
// ../event-processor/dist/detectors/hover.js
|
|
4137
|
+
var HoverTracker = class {
|
|
4138
|
+
constructor(config, emit2, elementResolver) {
|
|
4139
|
+
this.config = config;
|
|
4140
|
+
this.emit = emit2;
|
|
4141
|
+
this.elementResolver = elementResolver;
|
|
4142
|
+
this.currentElement = null;
|
|
4143
|
+
this.hoverStartTs = null;
|
|
4144
|
+
this.lastX = 0;
|
|
4145
|
+
this.lastY = 0;
|
|
4146
|
+
this.lastSampleTs = -Infinity;
|
|
4147
|
+
}
|
|
4148
|
+
ingest(raw) {
|
|
4149
|
+
if (raw.type !== 3 || raw.data.source !== RRWebSource.MouseMove)
|
|
4150
|
+
return;
|
|
4151
|
+
const positions = raw.data.positions;
|
|
4152
|
+
if (!positions || positions.length === 0)
|
|
4153
|
+
return;
|
|
4154
|
+
const last = positions[positions.length - 1];
|
|
4155
|
+
this.lastX = last.x;
|
|
4156
|
+
this.lastY = last.y;
|
|
4157
|
+
}
|
|
4158
|
+
tick(now) {
|
|
4159
|
+
if (!this.elementResolver)
|
|
4160
|
+
return;
|
|
4161
|
+
if (now - this.lastSampleTs < this.config.hoverSampleMs)
|
|
4162
|
+
return;
|
|
4163
|
+
this.lastSampleTs = now;
|
|
4164
|
+
const newElement = this.elementResolver(this.lastX, this.lastY);
|
|
4165
|
+
const newKey = newElement ? elementKey(newElement) : null;
|
|
4166
|
+
const currentKey = this.currentElement ? elementKey(this.currentElement) : null;
|
|
4167
|
+
if (newKey !== currentKey) {
|
|
4168
|
+
if (this.currentElement && this.hoverStartTs !== null) {
|
|
4169
|
+
this.emit({
|
|
4170
|
+
ts: now,
|
|
4171
|
+
name: StandardEvents.UI_HOVER,
|
|
4172
|
+
source: "rrweb",
|
|
4173
|
+
schemaVersion: EVENT_SCHEMA_VERSION,
|
|
4174
|
+
props: {
|
|
4175
|
+
x: this.lastX,
|
|
4176
|
+
y: this.lastY,
|
|
4177
|
+
duration_ms: now - this.hoverStartTs,
|
|
4178
|
+
element: this.currentElement
|
|
4179
|
+
}
|
|
4180
|
+
});
|
|
4181
|
+
}
|
|
4182
|
+
this.currentElement = newElement;
|
|
4183
|
+
this.hoverStartTs = now;
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
};
|
|
4187
|
+
function elementKey(el) {
|
|
4188
|
+
var _a2, _b, _c;
|
|
4189
|
+
return `${(_a2 = el.tag_name) != null ? _a2 : ""}|${(_b = el.attr__id) != null ? _b : ""}|${((_c = el.classes) != null ? _c : []).join(",")}`;
|
|
4190
|
+
}
|
|
4191
|
+
|
|
4192
|
+
// ../event-processor/dist/detectors/idle.js
|
|
4193
|
+
var IdleDetector = class {
|
|
4194
|
+
constructor(config, emit2) {
|
|
4195
|
+
this.config = config;
|
|
4196
|
+
this.emit = emit2;
|
|
4197
|
+
this.lastActivityTs = null;
|
|
4198
|
+
this.emitted = false;
|
|
4199
|
+
}
|
|
4200
|
+
ingest(raw) {
|
|
4201
|
+
if (raw.type !== 3)
|
|
4202
|
+
return;
|
|
4203
|
+
const src = raw.data.source;
|
|
4204
|
+
if (src === RRWebSource.MouseMove || src === RRWebSource.MouseInteraction || src === RRWebSource.Scroll) {
|
|
4205
|
+
this.lastActivityTs = raw.timestamp;
|
|
4206
|
+
this.emitted = false;
|
|
4207
|
+
}
|
|
4208
|
+
}
|
|
4209
|
+
tick(now) {
|
|
4210
|
+
if (this.lastActivityTs === null || this.emitted)
|
|
4211
|
+
return;
|
|
4212
|
+
if (now - this.lastActivityTs >= this.config.idleMs) {
|
|
4213
|
+
this.emit({
|
|
4214
|
+
ts: now,
|
|
4215
|
+
name: StandardEvents.UI_IDLE,
|
|
4216
|
+
source: "rrweb",
|
|
4217
|
+
schemaVersion: EVENT_SCHEMA_VERSION,
|
|
4218
|
+
props: {
|
|
4219
|
+
idle_ms: now - this.lastActivityTs
|
|
4220
|
+
}
|
|
4221
|
+
});
|
|
4222
|
+
this.emitted = true;
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
};
|
|
4226
|
+
|
|
4227
|
+
// ../event-processor/dist/detectors/rage-click.js
|
|
4228
|
+
var RageClickDetector = class {
|
|
4229
|
+
constructor(config, emit2) {
|
|
4230
|
+
this.config = config;
|
|
4231
|
+
this.emit = emit2;
|
|
4232
|
+
this.clicks = [];
|
|
4233
|
+
}
|
|
4234
|
+
ingest(raw) {
|
|
4235
|
+
var _a2, _b;
|
|
4236
|
+
if (raw.type !== 3 || raw.data.source !== RRWebSource.MouseInteraction)
|
|
4237
|
+
return;
|
|
4238
|
+
if (raw.data.type !== RRWebMouseInteraction.Click)
|
|
4239
|
+
return;
|
|
4240
|
+
const x = (_a2 = raw.data.x) != null ? _a2 : 0;
|
|
4241
|
+
const y = (_b = raw.data.y) != null ? _b : 0;
|
|
4242
|
+
const ts = raw.timestamp;
|
|
4243
|
+
const cutoff = ts - this.config.rageClickWindowMs;
|
|
4244
|
+
this.clicks = this.clicks.filter((c) => c.ts >= cutoff);
|
|
4245
|
+
this.clicks.push({ ts, x, y });
|
|
4246
|
+
const nearby = this.clicks.filter((c) => {
|
|
4247
|
+
const dx = c.x - x;
|
|
4248
|
+
const dy = c.y - y;
|
|
4249
|
+
return Math.sqrt(dx * dx + dy * dy) <= this.config.rageClickRadiusPx;
|
|
4250
|
+
});
|
|
4251
|
+
if (nearby.length >= this.config.rageClickCount) {
|
|
4252
|
+
this.emit({
|
|
4253
|
+
ts,
|
|
4254
|
+
name: StandardEvents.UI_RAGE_CLICK,
|
|
4255
|
+
source: "rrweb",
|
|
4256
|
+
schemaVersion: EVENT_SCHEMA_VERSION,
|
|
4257
|
+
props: {
|
|
4258
|
+
x,
|
|
4259
|
+
y,
|
|
4260
|
+
clickCount: nearby.length,
|
|
4261
|
+
duration_ms: ts - nearby[0].ts
|
|
4262
|
+
}
|
|
4263
|
+
});
|
|
4264
|
+
this.clicks = [];
|
|
4265
|
+
}
|
|
4266
|
+
}
|
|
4267
|
+
};
|
|
4268
|
+
|
|
4269
|
+
// ../event-processor/dist/detectors/scroll-thrash.js
|
|
4270
|
+
var ScrollThrashDetector = class {
|
|
4271
|
+
constructor(config, emit2) {
|
|
4272
|
+
this.config = config;
|
|
4273
|
+
this.emit = emit2;
|
|
4274
|
+
this.lastY = null;
|
|
4275
|
+
this.lastDirection = null;
|
|
4276
|
+
this.reversals = [];
|
|
4277
|
+
}
|
|
4278
|
+
ingest(raw) {
|
|
4279
|
+
var _a2;
|
|
4280
|
+
if (raw.type !== 3 || raw.data.source !== RRWebSource.Scroll)
|
|
4281
|
+
return;
|
|
4282
|
+
const y = (_a2 = raw.data.y) != null ? _a2 : 0;
|
|
4283
|
+
const ts = raw.timestamp;
|
|
4284
|
+
if (this.lastY !== null) {
|
|
4285
|
+
const direction = y > this.lastY ? "down" : y < this.lastY ? "up" : this.lastDirection;
|
|
4286
|
+
if (direction && direction !== this.lastDirection) {
|
|
4287
|
+
const cutoff = ts - this.config.scrollThrashWindowMs;
|
|
4288
|
+
this.reversals = this.reversals.filter((t) => t > cutoff);
|
|
4289
|
+
this.reversals.push(ts);
|
|
4290
|
+
if (this.reversals.length >= this.config.scrollThrashReversals) {
|
|
4291
|
+
this.emit({
|
|
4292
|
+
ts,
|
|
4293
|
+
name: StandardEvents.UI_SCROLL_THRASH,
|
|
4294
|
+
source: "rrweb",
|
|
4295
|
+
schemaVersion: EVENT_SCHEMA_VERSION,
|
|
4296
|
+
props: {
|
|
4297
|
+
reversals: this.reversals.length,
|
|
4298
|
+
duration_ms: ts - this.reversals[0]
|
|
4299
|
+
}
|
|
4300
|
+
});
|
|
4301
|
+
this.reversals = [];
|
|
4302
|
+
}
|
|
4303
|
+
}
|
|
4304
|
+
this.lastDirection = direction;
|
|
4305
|
+
}
|
|
4306
|
+
this.lastY = y;
|
|
4307
|
+
}
|
|
4308
|
+
};
|
|
4309
|
+
|
|
4310
|
+
// ../event-processor/dist/processor.js
|
|
4311
|
+
function createEventProcessor(options) {
|
|
4312
|
+
const config = { ...DEFAULT_DETECTOR_CONFIG, ...options == null ? void 0 : options.config };
|
|
4313
|
+
const listeners = [];
|
|
4314
|
+
function emit2(event) {
|
|
4315
|
+
for (const cb of listeners)
|
|
4316
|
+
cb(event);
|
|
4317
|
+
}
|
|
4318
|
+
const hesitation = new HesitationDetector(config, emit2);
|
|
4319
|
+
const rageClick = new RageClickDetector(config, emit2);
|
|
4320
|
+
const scrollThrash = new ScrollThrashDetector(config, emit2);
|
|
4321
|
+
const focusBounce = new FocusBounceDetector(config, emit2);
|
|
4322
|
+
const idle = new IdleDetector(config, emit2);
|
|
4323
|
+
const hover = new HoverTracker(config, emit2, options == null ? void 0 : options.elementResolver);
|
|
4324
|
+
const clickAttrBuffer = [];
|
|
4325
|
+
return {
|
|
4326
|
+
ingest(raw) {
|
|
4327
|
+
if (raw.kind === "posthog") {
|
|
4328
|
+
const { kind: _, ...phEvent } = raw;
|
|
4329
|
+
if (shouldNormalizeEvent(phEvent)) {
|
|
4330
|
+
emit2(normalizePostHogEvent(phEvent));
|
|
4331
|
+
}
|
|
4332
|
+
} else if (raw.kind === "rrweb") {
|
|
4333
|
+
hesitation.ingest(raw);
|
|
4334
|
+
rageClick.ingest(raw);
|
|
4335
|
+
scrollThrash.ingest(raw);
|
|
4336
|
+
focusBounce.ingest(raw);
|
|
4337
|
+
idle.ingest(raw);
|
|
4338
|
+
hover.ingest(raw);
|
|
4339
|
+
}
|
|
4340
|
+
},
|
|
4341
|
+
onEvent(callback) {
|
|
4342
|
+
listeners.push(callback);
|
|
4343
|
+
},
|
|
4344
|
+
tick(timestamp) {
|
|
4345
|
+
hesitation.tick(timestamp);
|
|
4346
|
+
idle.tick(timestamp);
|
|
4347
|
+
hover.tick(timestamp);
|
|
4348
|
+
},
|
|
4349
|
+
enrichClickAttributes(timestamp, elements) {
|
|
4350
|
+
clickAttrBuffer.push({ ts: timestamp, elements });
|
|
4351
|
+
const cutoff = timestamp - 500;
|
|
4352
|
+
while (clickAttrBuffer.length > 0 && clickAttrBuffer[0].ts < cutoff) {
|
|
4353
|
+
clickAttrBuffer.shift();
|
|
4354
|
+
}
|
|
4355
|
+
}
|
|
4356
|
+
};
|
|
4357
|
+
}
|
|
4358
|
+
|
|
4359
|
+
// src/events/types.ts
|
|
4360
|
+
var StandardEvents2 = {
|
|
3818
4361
|
// UI events (from PostHog autocapture)
|
|
3819
4362
|
UI_CLICK: "ui.click",
|
|
3820
4363
|
UI_SCROLL: "ui.scroll",
|
|
@@ -3866,48 +4409,48 @@ function createCanvasEvent(name, props) {
|
|
|
3866
4409
|
};
|
|
3867
4410
|
}
|
|
3868
4411
|
function canvasOpened(surface) {
|
|
3869
|
-
return createCanvasEvent(
|
|
4412
|
+
return createCanvasEvent(StandardEvents2.CANVAS_OPENED, { surface });
|
|
3870
4413
|
}
|
|
3871
4414
|
function canvasClosed(surface) {
|
|
3872
|
-
return createCanvasEvent(
|
|
4415
|
+
return createCanvasEvent(StandardEvents2.CANVAS_CLOSED, { surface });
|
|
3873
4416
|
}
|
|
3874
4417
|
function tileViewed(tileId, surface) {
|
|
3875
|
-
return createCanvasEvent(
|
|
4418
|
+
return createCanvasEvent(StandardEvents2.TILE_VIEWED, { tileId, surface });
|
|
3876
4419
|
}
|
|
3877
4420
|
function tileExpanded(tileId, surface) {
|
|
3878
|
-
return createCanvasEvent(
|
|
4421
|
+
return createCanvasEvent(StandardEvents2.TILE_EXPANDED, { tileId, surface });
|
|
3879
4422
|
}
|
|
3880
4423
|
function tileCollapsed(tileId, surface) {
|
|
3881
|
-
return createCanvasEvent(
|
|
4424
|
+
return createCanvasEvent(StandardEvents2.TILE_COLLAPSED, { tileId, surface });
|
|
3882
4425
|
}
|
|
3883
4426
|
function tileAction(tileId, actionId, surface) {
|
|
3884
|
-
return createCanvasEvent(
|
|
4427
|
+
return createCanvasEvent(StandardEvents2.TILE_ACTION, {
|
|
3885
4428
|
tileId,
|
|
3886
4429
|
actionId,
|
|
3887
4430
|
surface
|
|
3888
4431
|
});
|
|
3889
4432
|
}
|
|
3890
4433
|
function overlayStarted(recipeId, recipeName) {
|
|
3891
|
-
return createCanvasEvent(
|
|
4434
|
+
return createCanvasEvent(StandardEvents2.OVERLAY_STARTED, {
|
|
3892
4435
|
recipeId,
|
|
3893
4436
|
recipeName
|
|
3894
4437
|
});
|
|
3895
4438
|
}
|
|
3896
4439
|
function overlayCompleted(recipeId, recipeName) {
|
|
3897
|
-
return createCanvasEvent(
|
|
4440
|
+
return createCanvasEvent(StandardEvents2.OVERLAY_COMPLETED, {
|
|
3898
4441
|
recipeId,
|
|
3899
4442
|
recipeName
|
|
3900
4443
|
});
|
|
3901
4444
|
}
|
|
3902
4445
|
function overlayDismissed(recipeId, recipeName, stepIndex) {
|
|
3903
|
-
return createCanvasEvent(
|
|
4446
|
+
return createCanvasEvent(StandardEvents2.OVERLAY_DISMISSED, {
|
|
3904
4447
|
recipeId,
|
|
3905
4448
|
recipeName,
|
|
3906
4449
|
stepIndex
|
|
3907
4450
|
});
|
|
3908
4451
|
}
|
|
3909
4452
|
function overlayStepViewed(recipeId, stepIndex, stepTitle) {
|
|
3910
|
-
return createCanvasEvent(
|
|
4453
|
+
return createCanvasEvent(StandardEvents2.OVERLAY_STEP_VIEWED, {
|
|
3911
4454
|
recipeId,
|
|
3912
4455
|
stepIndex,
|
|
3913
4456
|
stepTitle
|
|
@@ -4233,7 +4776,7 @@ function useNotifications(eventBus, tiles) {
|
|
|
4233
4776
|
const timerIds = useRef2(/* @__PURE__ */ new Map());
|
|
4234
4777
|
const publishDismissed = useCallback2(
|
|
4235
4778
|
(notif) => {
|
|
4236
|
-
eventBus == null ? void 0 : eventBus.publish(
|
|
4779
|
+
eventBus == null ? void 0 : eventBus.publish(StandardEvents2.NOTIFICATION_DISMISSED, {
|
|
4237
4780
|
notificationId: notif.id,
|
|
4238
4781
|
tileId: notif.tileId,
|
|
4239
4782
|
itemId: notif.itemId
|
|
@@ -4298,7 +4841,7 @@ function useNotifications(eventBus, tiles) {
|
|
|
4298
4841
|
}
|
|
4299
4842
|
return next;
|
|
4300
4843
|
});
|
|
4301
|
-
eventBus.publish(
|
|
4844
|
+
eventBus.publish(StandardEvents2.NOTIFICATION_SHOWN, {
|
|
4302
4845
|
notificationId: matched.id,
|
|
4303
4846
|
tileId: matched.tileId,
|
|
4304
4847
|
itemId: matched.itemId,
|
|
@@ -4511,7 +5054,7 @@ function WidgetMount({ widgetId, props }) {
|
|
|
4511
5054
|
prevPropsJsonRef.current = propsJson;
|
|
4512
5055
|
(_a3 = handleRef.current) == null ? void 0 : _a3.update(propsRef.current);
|
|
4513
5056
|
}, [propsJson]);
|
|
4514
|
-
if (!registry
|
|
5057
|
+
if (!(registry == null ? void 0 : registry.has(widgetId))) {
|
|
4515
5058
|
return /* @__PURE__ */ jsxs2(
|
|
4516
5059
|
"div",
|
|
4517
5060
|
{
|
|
@@ -4530,6 +5073,19 @@ function WidgetMount({ widgetId, props }) {
|
|
|
4530
5073
|
}
|
|
4531
5074
|
return /* @__PURE__ */ jsx6("div", { ref: parentRef });
|
|
4532
5075
|
}
|
|
5076
|
+
var INTERACTION_PATTERNS = [
|
|
5077
|
+
":toggled",
|
|
5078
|
+
":clicked",
|
|
5079
|
+
":feedback",
|
|
5080
|
+
":navigate",
|
|
5081
|
+
":expanded",
|
|
5082
|
+
":collapsed",
|
|
5083
|
+
":dismissed",
|
|
5084
|
+
":submitted",
|
|
5085
|
+
":interacted",
|
|
5086
|
+
":tip_clicked",
|
|
5087
|
+
":tip_focused"
|
|
5088
|
+
];
|
|
4533
5089
|
function TileCard({
|
|
4534
5090
|
config,
|
|
4535
5091
|
surface: _surface,
|
|
@@ -4538,10 +5094,42 @@ function TileCard({
|
|
|
4538
5094
|
}) {
|
|
4539
5095
|
const { title, subtitle, widget, props, icon } = config;
|
|
4540
5096
|
const [, setTick] = useState4(0);
|
|
5097
|
+
const articleRef = useRef4(null);
|
|
4541
5098
|
const runtime3 = useRuntime();
|
|
4542
5099
|
useEffect5(() => {
|
|
4543
5100
|
if (runtime3) setTick((t) => t + 1);
|
|
4544
5101
|
}, [runtime3]);
|
|
5102
|
+
useEffect5(() => {
|
|
5103
|
+
var _a2;
|
|
5104
|
+
const tracker = typeof window !== "undefined" ? (_a2 = window.SynOS) == null ? void 0 : _a2.interventionTracker : null;
|
|
5105
|
+
if (!articleRef.current || !tracker) return;
|
|
5106
|
+
const observer = new IntersectionObserver(
|
|
5107
|
+
([entry]) => {
|
|
5108
|
+
var _a3;
|
|
5109
|
+
if (entry.isIntersecting) {
|
|
5110
|
+
tracker.trackSeen(config.id, (_a3 = config.widget) != null ? _a3 : "unknown");
|
|
5111
|
+
observer.disconnect();
|
|
5112
|
+
}
|
|
5113
|
+
},
|
|
5114
|
+
{ threshold: 0.5 }
|
|
5115
|
+
);
|
|
5116
|
+
observer.observe(articleRef.current);
|
|
5117
|
+
return () => observer.disconnect();
|
|
5118
|
+
}, [config.id, config.widget]);
|
|
5119
|
+
useEffect5(() => {
|
|
5120
|
+
var _a2;
|
|
5121
|
+
const tracker = typeof window !== "undefined" ? (_a2 = window.SynOS) == null ? void 0 : _a2.interventionTracker : null;
|
|
5122
|
+
if (!(runtime3 == null ? void 0 : runtime3.events) || !tracker) return;
|
|
5123
|
+
return runtime3.events.subscribe((event) => {
|
|
5124
|
+
var _a3, _b;
|
|
5125
|
+
if (!INTERACTION_PATTERNS.some((p) => {
|
|
5126
|
+
var _a4;
|
|
5127
|
+
return (_a4 = event.name) == null ? void 0 : _a4.includes(p);
|
|
5128
|
+
})) return;
|
|
5129
|
+
if (((_a3 = event.props) == null ? void 0 : _a3.instanceId) !== config.id) return;
|
|
5130
|
+
tracker.trackInteracted(config.id, (_b = config.widget) != null ? _b : "unknown", event.name);
|
|
5131
|
+
});
|
|
5132
|
+
}, [runtime3 == null ? void 0 : runtime3.events, config.id, config.widget]);
|
|
4545
5133
|
const registration = useMemo3(
|
|
4546
5134
|
() => {
|
|
4547
5135
|
var _a2, _b;
|
|
@@ -4595,6 +5183,7 @@ function TileCard({
|
|
|
4595
5183
|
return /* @__PURE__ */ jsxs2(
|
|
4596
5184
|
"article",
|
|
4597
5185
|
{
|
|
5186
|
+
ref: articleRef,
|
|
4598
5187
|
"data-shadow-canvas-id": `tile-${config.id}`,
|
|
4599
5188
|
style: cardStyle,
|
|
4600
5189
|
onMouseEnter,
|
|
@@ -5132,7 +5721,7 @@ function ShadowCanvasOverlay({
|
|
|
5132
5721
|
const handleNotificationClick = useCallback4(
|
|
5133
5722
|
(notif) => {
|
|
5134
5723
|
if (runtime3) {
|
|
5135
|
-
runtime3.events.publish(
|
|
5724
|
+
runtime3.events.publish(StandardEvents2.NOTIFICATION_CLICKED, {
|
|
5136
5725
|
notificationId: notif.id,
|
|
5137
5726
|
tileId: notif.tileId,
|
|
5138
5727
|
itemId: notif.itemId
|
|
@@ -5142,7 +5731,7 @@ function ShadowCanvasOverlay({
|
|
|
5142
5731
|
onToggle();
|
|
5143
5732
|
}
|
|
5144
5733
|
if (runtime3 && notif.tileId) {
|
|
5145
|
-
runtime3.events.publish(
|
|
5734
|
+
runtime3.events.publish(StandardEvents2.NOTIFICATION_DEEP_LINK, {
|
|
5146
5735
|
tileId: notif.tileId,
|
|
5147
5736
|
itemId: notif.itemId
|
|
5148
5737
|
});
|
|
@@ -5215,6 +5804,17 @@ function ShadowCanvasOverlay({
|
|
|
5215
5804
|
}
|
|
5216
5805
|
onToggle();
|
|
5217
5806
|
}, [isOpen, telemetry, runtime3, onToggle]);
|
|
5807
|
+
useEffect7(() => {
|
|
5808
|
+
if (!isOpen) return;
|
|
5809
|
+
const handleOutsideClick = (e) => {
|
|
5810
|
+
const path = e.composedPath();
|
|
5811
|
+
if (containerRef.current && !path.includes(containerRef.current) && launcherRef.current && !path.includes(launcherRef.current)) {
|
|
5812
|
+
toggle2();
|
|
5813
|
+
}
|
|
5814
|
+
};
|
|
5815
|
+
document.addEventListener("mousedown", handleOutsideClick);
|
|
5816
|
+
return () => document.removeEventListener("mousedown", handleOutsideClick);
|
|
5817
|
+
}, [isOpen, toggle2]);
|
|
5218
5818
|
const onLauncherPointerDown = useCallback4((e) => {
|
|
5219
5819
|
const rect = e.currentTarget.getBoundingClientRect();
|
|
5220
5820
|
dragRef.current = {
|
|
@@ -5253,6 +5853,7 @@ function ShadowCanvasOverlay({
|
|
|
5253
5853
|
const isPush = config.canvas.layout === "push";
|
|
5254
5854
|
const canvasBorder = (_b = config.canvas.border) != null ? _b : "none";
|
|
5255
5855
|
const containerRef = useRef5(null);
|
|
5856
|
+
const launcherRef = useRef5(null);
|
|
5256
5857
|
const zIndex = 2147483600;
|
|
5257
5858
|
useEffect7(() => {
|
|
5258
5859
|
var _a3, _b2, _c2, _d2;
|
|
@@ -5340,7 +5941,7 @@ function ShadowCanvasOverlay({
|
|
|
5340
5941
|
style: {
|
|
5341
5942
|
position: "fixed",
|
|
5342
5943
|
inset: 0,
|
|
5343
|
-
pointerEvents:
|
|
5944
|
+
pointerEvents: "none",
|
|
5344
5945
|
zIndex
|
|
5345
5946
|
},
|
|
5346
5947
|
children: /* @__PURE__ */ jsxs3("div", { style: wrapperStyle, children: [
|
|
@@ -5413,17 +6014,7 @@ function ShadowCanvasOverlay({
|
|
|
5413
6014
|
) }),
|
|
5414
6015
|
footerSlot
|
|
5415
6016
|
] }),
|
|
5416
|
-
/* @__PURE__ */ jsx8(
|
|
5417
|
-
"div",
|
|
5418
|
-
{
|
|
5419
|
-
onClick: toggle2,
|
|
5420
|
-
style: {
|
|
5421
|
-
flex: "1 1 auto",
|
|
5422
|
-
pointerEvents: isOpen ? "auto" : "none",
|
|
5423
|
-
cursor: "default"
|
|
5424
|
-
}
|
|
5425
|
-
}
|
|
5426
|
-
)
|
|
6017
|
+
/* @__PURE__ */ jsx8("div", { style: { flex: "1 1 auto" } })
|
|
5427
6018
|
] })
|
|
5428
6019
|
}
|
|
5429
6020
|
);
|
|
@@ -5453,6 +6044,7 @@ function ShadowCanvasOverlay({
|
|
|
5453
6044
|
/* @__PURE__ */ jsxs3(
|
|
5454
6045
|
"button",
|
|
5455
6046
|
{
|
|
6047
|
+
ref: launcherRef,
|
|
5456
6048
|
type: "button",
|
|
5457
6049
|
"aria-label": "Toggle shadow canvas",
|
|
5458
6050
|
className: launcherAnimate && !isOpen ? "syntro-launcher-animate" : void 0,
|
|
@@ -5614,6 +6206,14 @@ var sortTiles = (tiles) => [...tiles].sort((a, b) => {
|
|
|
5614
6206
|
var _a2, _b;
|
|
5615
6207
|
return ((_a2 = b.priority) != null ? _a2 : 0) - ((_b = a.priority) != null ? _b : 0);
|
|
5616
6208
|
});
|
|
6209
|
+
function fireTriggeredForTiles(tiles) {
|
|
6210
|
+
var _a2, _b;
|
|
6211
|
+
const tracker = typeof window !== "undefined" ? (_a2 = window.SynOS) == null ? void 0 : _a2.interventionTracker : null;
|
|
6212
|
+
if (!tracker) return;
|
|
6213
|
+
for (const tile of tiles) {
|
|
6214
|
+
tracker.trackTriggered(tile.id, (_b = tile.widget) != null ? _b : "unknown");
|
|
6215
|
+
}
|
|
6216
|
+
}
|
|
5617
6217
|
function useShadowCanvasConfig({
|
|
5618
6218
|
fetcher,
|
|
5619
6219
|
experiments,
|
|
@@ -5636,6 +6236,7 @@ function useShadowCanvasConfig({
|
|
|
5636
6236
|
if (experiments) {
|
|
5637
6237
|
tiles = tiles.filter((tile) => experiments.shouldRenderRectangle(tile));
|
|
5638
6238
|
}
|
|
6239
|
+
fireTriggeredForTiles(tiles);
|
|
5639
6240
|
setState((prev) => ({ ...prev, tiles: sortTiles(tiles) }));
|
|
5640
6241
|
}, [runtime3, experiments]);
|
|
5641
6242
|
const load = useCallback5(async () => {
|
|
@@ -5653,6 +6254,7 @@ function useShadowCanvasConfig({
|
|
|
5653
6254
|
} else if (experiments) {
|
|
5654
6255
|
tiles = tiles.filter((tile) => experiments.shouldRenderRectangle(tile));
|
|
5655
6256
|
}
|
|
6257
|
+
fireTriggeredForTiles(tiles);
|
|
5656
6258
|
debug("SmartCanvas Config", `Tile count after filtering: ${tiles.length}`);
|
|
5657
6259
|
const newActions = response.actions || [];
|
|
5658
6260
|
const newActionsJson = JSON.stringify(newActions);
|
|
@@ -5715,8 +6317,8 @@ function SmartCanvasApp({
|
|
|
5715
6317
|
controller,
|
|
5716
6318
|
fetcher,
|
|
5717
6319
|
configUri,
|
|
5718
|
-
configUriFeatureKey
|
|
5719
|
-
configFeatureKey
|
|
6320
|
+
configUriFeatureKey,
|
|
6321
|
+
configFeatureKey,
|
|
5720
6322
|
fetchCredentials = "include",
|
|
5721
6323
|
pollIntervalMs,
|
|
5722
6324
|
experiments,
|
|
@@ -5786,10 +6388,10 @@ function SmartCanvasAppInner({
|
|
|
5786
6388
|
controller,
|
|
5787
6389
|
fetcher,
|
|
5788
6390
|
configUri,
|
|
5789
|
-
configUriFeatureKey
|
|
5790
|
-
configFeatureKey
|
|
6391
|
+
configUriFeatureKey,
|
|
6392
|
+
configFeatureKey,
|
|
5791
6393
|
fetchCredentials = "include",
|
|
5792
|
-
pollIntervalMs,
|
|
6394
|
+
pollIntervalMs: _pollIntervalMs,
|
|
5793
6395
|
experiments,
|
|
5794
6396
|
telemetry,
|
|
5795
6397
|
runtime: runtime3,
|
|
@@ -7072,7 +7674,6 @@ var PostHogAdapter = class {
|
|
|
7072
7674
|
__publicField(this, "featureFlagsCallback");
|
|
7073
7675
|
__publicField(this, "captureCallback");
|
|
7074
7676
|
__publicField(this, "rrwebCallback");
|
|
7075
|
-
__publicField(this, "consentUnsub");
|
|
7076
7677
|
this.client = options.client;
|
|
7077
7678
|
this.featureFlagsCallback = options.onFeatureFlagsLoaded;
|
|
7078
7679
|
this.captureCallback = options.onCapture;
|
|
@@ -7083,7 +7684,7 @@ var PostHogAdapter = class {
|
|
|
7083
7684
|
if (currentStatus === "granted") {
|
|
7084
7685
|
this.initPostHog();
|
|
7085
7686
|
}
|
|
7086
|
-
|
|
7687
|
+
consent.subscribe((status) => {
|
|
7087
7688
|
if (status === "granted") {
|
|
7088
7689
|
if (!this.client) {
|
|
7089
7690
|
this.initPostHog();
|
|
@@ -7110,71 +7711,67 @@ var PostHogAdapter = class {
|
|
|
7110
7711
|
if (!options.apiKey) return;
|
|
7111
7712
|
const enableFeatureFlags = (_a2 = options.enableFeatureFlags) != null ? _a2 : true;
|
|
7112
7713
|
const instanceName = `syntro_${options.apiKey.slice(-6) || "sdk"}`;
|
|
7113
|
-
|
|
7114
|
-
options.
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
if (allFlags && this.featureFlagsCallback) {
|
|
7144
|
-
this.featureFlagsCallback(allFlags);
|
|
7145
|
-
}
|
|
7146
|
-
});
|
|
7147
|
-
const existingFlags = this.getAllFeatureFlags();
|
|
7148
|
-
if (existingFlags && Object.keys(existingFlags).length > 0) {
|
|
7149
|
-
this.featureFlagsCallback(existingFlags);
|
|
7714
|
+
const initOptions = {
|
|
7715
|
+
api_host: (_b = options.apiHost) != null ? _b : "https://telemetry.syntrologie.com",
|
|
7716
|
+
// Feature flags for segment membership (in_segment_* flags)
|
|
7717
|
+
// When enabled, /decide is called to get segment flags
|
|
7718
|
+
advanced_disable_feature_flags: !enableFeatureFlags,
|
|
7719
|
+
advanced_disable_feature_flags_on_first_load: !enableFeatureFlags,
|
|
7720
|
+
// Full-page tracking - all ON by default
|
|
7721
|
+
autocapture: (_c = options.autocapture) != null ? _c : true,
|
|
7722
|
+
capture_pageview: (_d = options.capturePageview) != null ? _d : "history_change",
|
|
7723
|
+
capture_pageleave: (_e = options.capturePageleave) != null ? _e : true,
|
|
7724
|
+
disable_session_recording: !((_f = options.sessionRecording) != null ? _f : true),
|
|
7725
|
+
// CRITICAL: Disable user agent filtering to allow headless Chrome
|
|
7726
|
+
// PostHog blocks "HeadlessChrome" user agents by default as bot detection
|
|
7727
|
+
// This enables session recording in Playwright/crawler sessions
|
|
7728
|
+
opt_out_useragent_filter: true,
|
|
7729
|
+
// Cross-domain iframe recording for embeds
|
|
7730
|
+
session_recording: {
|
|
7731
|
+
recordCrossDomainIFrames: true
|
|
7732
|
+
},
|
|
7733
|
+
// Capture performance metrics
|
|
7734
|
+
capture_performance: true,
|
|
7735
|
+
// Enable web vitals
|
|
7736
|
+
enable_recording_console_log: true,
|
|
7737
|
+
// Bootstrap callback for when flags are loaded
|
|
7738
|
+
loaded: (ph) => {
|
|
7739
|
+
if (enableFeatureFlags && this.featureFlagsCallback) {
|
|
7740
|
+
ph.onFeatureFlags(() => {
|
|
7741
|
+
const allFlags = this.getAllFeatureFlags();
|
|
7742
|
+
if (allFlags && this.featureFlagsCallback) {
|
|
7743
|
+
this.featureFlagsCallback(allFlags);
|
|
7150
7744
|
}
|
|
7151
|
-
}
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
const eventName = typeof data === "string" ? data : data == null ? void 0 : data.event;
|
|
7156
|
-
const properties = typeof data === "string" ? void 0 : data == null ? void 0 : data.properties;
|
|
7157
|
-
if (typeof eventName === "string") {
|
|
7158
|
-
(_a3 = this.captureCallback) == null ? void 0 : _a3.call(this, eventName, properties);
|
|
7159
|
-
}
|
|
7160
|
-
});
|
|
7745
|
+
});
|
|
7746
|
+
const existingFlags = this.getAllFeatureFlags();
|
|
7747
|
+
if (existingFlags && Object.keys(existingFlags).length > 0) {
|
|
7748
|
+
this.featureFlagsCallback(existingFlags);
|
|
7161
7749
|
}
|
|
7162
7750
|
}
|
|
7163
|
-
|
|
7751
|
+
if (this.captureCallback) {
|
|
7752
|
+
ph.on("eventCaptured", (...args) => {
|
|
7753
|
+
var _a3;
|
|
7754
|
+
const data = args[0];
|
|
7755
|
+
const eventName = typeof data === "string" ? data : data == null ? void 0 : data.event;
|
|
7756
|
+
const properties = typeof data === "string" ? void 0 : data == null ? void 0 : data.properties;
|
|
7757
|
+
if (typeof eventName === "string") {
|
|
7758
|
+
(_a3 = this.captureCallback) == null ? void 0 : _a3.call(this, eventName, properties);
|
|
7759
|
+
}
|
|
7760
|
+
});
|
|
7761
|
+
}
|
|
7762
|
+
}
|
|
7763
|
+
};
|
|
7764
|
+
const result = posthog.init(
|
|
7765
|
+
options.apiKey,
|
|
7766
|
+
initOptions,
|
|
7164
7767
|
instanceName
|
|
7165
7768
|
);
|
|
7769
|
+
if (result) {
|
|
7770
|
+
this.client = result;
|
|
7771
|
+
}
|
|
7166
7772
|
if (this.rrwebCallback && this.client) {
|
|
7167
7773
|
this.setupRRWebIntercept();
|
|
7168
7774
|
}
|
|
7169
|
-
if (options.replayMirrorEndpoint && this.client) {
|
|
7170
|
-
import("./replayMirror-QZ3GQ527.js").then(({ setupReplayMirror }) => {
|
|
7171
|
-
var _a3;
|
|
7172
|
-
setupReplayMirror({
|
|
7173
|
-
posthogHost: (_a3 = options.apiHost) != null ? _a3 : "https://telemetry.syntrologie.com",
|
|
7174
|
-
mirrorEndpoint: options.replayMirrorEndpoint
|
|
7175
|
-
});
|
|
7176
|
-
});
|
|
7177
|
-
}
|
|
7178
7775
|
}
|
|
7179
7776
|
/**
|
|
7180
7777
|
* Set up rrweb event interception on PostHog's session recording.
|
|
@@ -7197,8 +7794,9 @@ var PostHogAdapter = class {
|
|
|
7197
7794
|
return;
|
|
7198
7795
|
}
|
|
7199
7796
|
let recorder = null;
|
|
7200
|
-
|
|
7201
|
-
|
|
7797
|
+
const srRecord = sr;
|
|
7798
|
+
for (const key of Object.getOwnPropertyNames(srRecord)) {
|
|
7799
|
+
const val = srRecord[key];
|
|
7202
7800
|
if (val && typeof val === "object" && typeof val.onRRwebEmit === "function" && typeof val.start === "function" && val !== sr) {
|
|
7203
7801
|
recorder = val;
|
|
7204
7802
|
break;
|
|
@@ -7225,9 +7823,8 @@ var PostHogAdapter = class {
|
|
|
7225
7823
|
* Used to extract segment membership flags (in_segment_*).
|
|
7226
7824
|
*/
|
|
7227
7825
|
getAllFeatureFlags() {
|
|
7228
|
-
var _a2, _b
|
|
7229
|
-
|
|
7230
|
-
return flags;
|
|
7826
|
+
var _a2, _b;
|
|
7827
|
+
return (_b = (_a2 = this.client) == null ? void 0 : _a2.featureFlags) == null ? void 0 : _b.getFlagVariants();
|
|
7231
7828
|
}
|
|
7232
7829
|
/**
|
|
7233
7830
|
* Get segment membership flags (in_segment_*) from PostHog.
|
|
@@ -7308,6 +7905,59 @@ function createPostHogClient(options = {}) {
|
|
|
7308
7905
|
return new PostHogAdapter(options);
|
|
7309
7906
|
}
|
|
7310
7907
|
|
|
7908
|
+
// src/telemetry/InterventionTracker.ts
|
|
7909
|
+
var InterventionTracker = class {
|
|
7910
|
+
constructor(telemetry, variantId) {
|
|
7911
|
+
__publicField(this, "telemetry");
|
|
7912
|
+
__publicField(this, "variantId");
|
|
7913
|
+
__publicField(this, "seenSet", /* @__PURE__ */ new Set());
|
|
7914
|
+
__publicField(this, "triggeredSet", /* @__PURE__ */ new Set());
|
|
7915
|
+
this.telemetry = telemetry;
|
|
7916
|
+
this.variantId = variantId;
|
|
7917
|
+
}
|
|
7918
|
+
trackServed(tiles, actions) {
|
|
7919
|
+
var _a2, _b;
|
|
7920
|
+
(_b = (_a2 = this.telemetry).track) == null ? void 0 : _b.call(_a2, "syntro_config_served", {
|
|
7921
|
+
variant_id: this.variantId,
|
|
7922
|
+
tiles,
|
|
7923
|
+
actions
|
|
7924
|
+
});
|
|
7925
|
+
}
|
|
7926
|
+
trackSeen(interventionId, interventionKind) {
|
|
7927
|
+
var _a2, _b;
|
|
7928
|
+
if (this.seenSet.has(interventionId)) return;
|
|
7929
|
+
this.seenSet.add(interventionId);
|
|
7930
|
+
(_b = (_a2 = this.telemetry).track) == null ? void 0 : _b.call(_a2, "syntro_intervention_seen", {
|
|
7931
|
+
variant_id: this.variantId,
|
|
7932
|
+
intervention_id: interventionId,
|
|
7933
|
+
intervention_kind: interventionKind
|
|
7934
|
+
});
|
|
7935
|
+
}
|
|
7936
|
+
trackTriggered(interventionId, interventionKind) {
|
|
7937
|
+
var _a2, _b;
|
|
7938
|
+
if (this.triggeredSet.has(interventionId)) return;
|
|
7939
|
+
this.triggeredSet.add(interventionId);
|
|
7940
|
+
(_b = (_a2 = this.telemetry).track) == null ? void 0 : _b.call(_a2, "syntro_intervention_triggered", {
|
|
7941
|
+
variant_id: this.variantId,
|
|
7942
|
+
intervention_id: interventionId,
|
|
7943
|
+
intervention_kind: interventionKind
|
|
7944
|
+
});
|
|
7945
|
+
}
|
|
7946
|
+
trackInteracted(interventionId, interventionKind, interactionType) {
|
|
7947
|
+
var _a2, _b;
|
|
7948
|
+
(_b = (_a2 = this.telemetry).track) == null ? void 0 : _b.call(_a2, "syntro_intervention_interacted", {
|
|
7949
|
+
variant_id: this.variantId,
|
|
7950
|
+
intervention_id: interventionId,
|
|
7951
|
+
intervention_kind: interventionKind,
|
|
7952
|
+
interaction_type: interactionType
|
|
7953
|
+
});
|
|
7954
|
+
}
|
|
7955
|
+
resetPage() {
|
|
7956
|
+
this.seenSet.clear();
|
|
7957
|
+
this.triggeredSet.clear();
|
|
7958
|
+
}
|
|
7959
|
+
};
|
|
7960
|
+
|
|
7311
7961
|
// src/actions/executors/core-flow.ts
|
|
7312
7962
|
var executeSequence = async (action, context) => {
|
|
7313
7963
|
const handles = [];
|
|
@@ -7578,187 +8228,36 @@ function hasExecutor(kind) {
|
|
|
7578
8228
|
return executorRegistry.has(kind);
|
|
7579
8229
|
}
|
|
7580
8230
|
|
|
7581
|
-
// src/actions/validation.ts
|
|
7582
|
-
|
|
7583
|
-
"
|
|
7584
|
-
"onerror",
|
|
7585
|
-
"onload",
|
|
7586
|
-
"onmouseover",
|
|
7587
|
-
"onfocus",
|
|
7588
|
-
"onblur",
|
|
7589
|
-
"onchange",
|
|
7590
|
-
"onsubmit",
|
|
7591
|
-
"onkeydown",
|
|
7592
|
-
"onkeyup",
|
|
7593
|
-
"onkeypress"
|
|
7594
|
-
]);
|
|
7595
|
-
var MAX_HTML_LENGTH = 5e4;
|
|
7596
|
-
var MAX_STYLE_COUNT = 50;
|
|
7597
|
-
function validateAction(action) {
|
|
7598
|
-
const errors = [];
|
|
7599
|
-
const warnings = [];
|
|
7600
|
-
if (!action || typeof action !== "object") {
|
|
8231
|
+
// src/actions/validation-rules.ts
|
|
8232
|
+
function validateBadgeAction(action, errors, warnings) {
|
|
8233
|
+
if (!action.content || typeof action.content !== "string") {
|
|
7601
8234
|
errors.push({
|
|
7602
|
-
code: "
|
|
7603
|
-
message: "
|
|
8235
|
+
code: "MISSING_CONTENT",
|
|
8236
|
+
message: "Badge action requires 'content' property",
|
|
8237
|
+
field: "content"
|
|
8238
|
+
});
|
|
8239
|
+
} else if (action.content.length > 100) {
|
|
8240
|
+
warnings.push({
|
|
8241
|
+
code: "LONG_BADGE_CONTENT",
|
|
8242
|
+
message: "Badge content is quite long",
|
|
8243
|
+
suggestion: "Keep badge content short (under 100 characters)"
|
|
7604
8244
|
});
|
|
7605
|
-
return { valid: false, errors, warnings };
|
|
7606
8245
|
}
|
|
7607
|
-
|
|
7608
|
-
|
|
8246
|
+
}
|
|
8247
|
+
function validateTooltipAction(action, errors, _warnings) {
|
|
8248
|
+
if (!action.content || typeof action.content !== "object") {
|
|
7609
8249
|
errors.push({
|
|
7610
|
-
code: "
|
|
7611
|
-
message: "
|
|
8250
|
+
code: "MISSING_CONTENT",
|
|
8251
|
+
message: "Tooltip action requires 'content' object",
|
|
8252
|
+
field: "content"
|
|
7612
8253
|
});
|
|
7613
|
-
return
|
|
8254
|
+
return;
|
|
7614
8255
|
}
|
|
7615
|
-
if (!
|
|
7616
|
-
const registered = executorRegistry.list();
|
|
7617
|
-
console.error(
|
|
7618
|
-
`[ActionValidation] Unknown action kind: "${kind}". Registered kinds (${registered.length}): [${registered.join(", ")}]. This usually means the app that provides "${kind}" hasn't been activated yet, or the executor was never registered in ExecutorRegistry.`
|
|
7619
|
-
);
|
|
8256
|
+
if (!action.content.body || typeof action.content.body !== "string") {
|
|
7620
8257
|
errors.push({
|
|
7621
|
-
code: "
|
|
7622
|
-
message:
|
|
7623
|
-
field: "
|
|
7624
|
-
});
|
|
7625
|
-
return { valid: false, errors, warnings };
|
|
7626
|
-
}
|
|
7627
|
-
switch (kind) {
|
|
7628
|
-
case "overlays:highlight":
|
|
7629
|
-
case "overlays:pulse":
|
|
7630
|
-
case "navigation:scrollTo":
|
|
7631
|
-
validateAnchorAction(action, errors, warnings);
|
|
7632
|
-
break;
|
|
7633
|
-
case "overlays:badge":
|
|
7634
|
-
validateAnchorAction(action, errors, warnings);
|
|
7635
|
-
validateBadgeAction(action, errors, warnings);
|
|
7636
|
-
break;
|
|
7637
|
-
case "overlays:tooltip":
|
|
7638
|
-
validateAnchorAction(action, errors, warnings);
|
|
7639
|
-
validateTooltipAction(action, errors, warnings);
|
|
7640
|
-
break;
|
|
7641
|
-
case "overlays:modal":
|
|
7642
|
-
validateModalAction(action, errors, warnings);
|
|
7643
|
-
break;
|
|
7644
|
-
case "content:insertHtml":
|
|
7645
|
-
validateAnchorAction(action, errors, warnings);
|
|
7646
|
-
validateInsertHtmlAction(action, errors, warnings);
|
|
7647
|
-
break;
|
|
7648
|
-
case "content:setText":
|
|
7649
|
-
validateAnchorAction(action, errors, warnings);
|
|
7650
|
-
validateSetTextAction(action, errors, warnings);
|
|
7651
|
-
break;
|
|
7652
|
-
case "content:setAttr":
|
|
7653
|
-
validateAnchorAction(action, errors, warnings);
|
|
7654
|
-
validateSetAttrAction(action, errors, warnings);
|
|
7655
|
-
break;
|
|
7656
|
-
case "content:addClass":
|
|
7657
|
-
case "content:removeClass":
|
|
7658
|
-
validateAnchorAction(action, errors, warnings);
|
|
7659
|
-
validateClassAction(action, errors, warnings);
|
|
7660
|
-
break;
|
|
7661
|
-
case "content:setStyle":
|
|
7662
|
-
validateAnchorAction(action, errors, warnings);
|
|
7663
|
-
validateSetStyleAction(action, errors, warnings);
|
|
7664
|
-
break;
|
|
7665
|
-
case "core:mountWidget":
|
|
7666
|
-
validateMountWidgetAction(action, errors, warnings);
|
|
7667
|
-
break;
|
|
7668
|
-
case "core:wait":
|
|
7669
|
-
validateWaitAction(action, errors, warnings);
|
|
7670
|
-
break;
|
|
7671
|
-
case "core:sequence":
|
|
7672
|
-
validateSequenceAction(action, errors, warnings);
|
|
7673
|
-
break;
|
|
7674
|
-
case "core:parallel":
|
|
7675
|
-
validateParallelAction(action, errors, warnings);
|
|
7676
|
-
break;
|
|
7677
|
-
case "core:tour":
|
|
7678
|
-
validateTourAction(action, errors, warnings);
|
|
7679
|
-
break;
|
|
7680
|
-
case "navigation:navigate":
|
|
7681
|
-
validateNavigateAction(action, errors, warnings);
|
|
7682
|
-
break;
|
|
7683
|
-
}
|
|
7684
|
-
return {
|
|
7685
|
-
valid: errors.length === 0,
|
|
7686
|
-
errors,
|
|
7687
|
-
warnings
|
|
7688
|
-
};
|
|
7689
|
-
}
|
|
7690
|
-
function validateAnchorAction(action, errors, warnings) {
|
|
7691
|
-
const anchorId = action.anchorId;
|
|
7692
|
-
if (!anchorId || typeof anchorId !== "object") {
|
|
7693
|
-
errors.push({
|
|
7694
|
-
code: "MISSING_ANCHOR_ID",
|
|
7695
|
-
message: "Action requires an 'anchorId' object with a 'selector' string",
|
|
7696
|
-
field: "anchorId"
|
|
7697
|
-
});
|
|
7698
|
-
return;
|
|
7699
|
-
}
|
|
7700
|
-
if (!anchorId.selector || typeof anchorId.selector !== "string") {
|
|
7701
|
-
errors.push({
|
|
7702
|
-
code: "MISSING_ANCHOR_SELECTOR",
|
|
7703
|
-
message: "anchorId requires a 'selector' string",
|
|
7704
|
-
field: "anchorId.selector"
|
|
7705
|
-
});
|
|
7706
|
-
} else if (anchorId.selector.length > 200) {
|
|
7707
|
-
warnings.push({
|
|
7708
|
-
code: "LONG_ANCHOR_ID",
|
|
7709
|
-
message: "Anchor selector is unusually long",
|
|
7710
|
-
suggestion: "Consider using a shorter, more descriptive selector"
|
|
7711
|
-
});
|
|
7712
|
-
}
|
|
7713
|
-
if (anchorId.route === void 0 || anchorId.route === null) {
|
|
7714
|
-
errors.push({
|
|
7715
|
-
code: "MISSING_ANCHOR_ROUTE",
|
|
7716
|
-
message: `anchorId requires a 'route' (string or array of strings). Use "**" for all routes.`,
|
|
7717
|
-
field: "anchorId.route"
|
|
7718
|
-
});
|
|
7719
|
-
} else {
|
|
7720
|
-
const routes = Array.isArray(anchorId.route) ? anchorId.route : [anchorId.route];
|
|
7721
|
-
for (const route of routes) {
|
|
7722
|
-
if (typeof route !== "string") {
|
|
7723
|
-
errors.push({
|
|
7724
|
-
code: "INVALID_ANCHOR_ROUTE",
|
|
7725
|
-
message: "anchorId.route must be a string or array of strings",
|
|
7726
|
-
field: "anchorId.route"
|
|
7727
|
-
});
|
|
7728
|
-
break;
|
|
7729
|
-
}
|
|
7730
|
-
}
|
|
7731
|
-
}
|
|
7732
|
-
}
|
|
7733
|
-
function validateBadgeAction(action, errors, warnings) {
|
|
7734
|
-
if (!action.content || typeof action.content !== "string") {
|
|
7735
|
-
errors.push({
|
|
7736
|
-
code: "MISSING_CONTENT",
|
|
7737
|
-
message: "Badge action requires 'content' property",
|
|
7738
|
-
field: "content"
|
|
7739
|
-
});
|
|
7740
|
-
} else if (action.content.length > 100) {
|
|
7741
|
-
warnings.push({
|
|
7742
|
-
code: "LONG_BADGE_CONTENT",
|
|
7743
|
-
message: "Badge content is quite long",
|
|
7744
|
-
suggestion: "Keep badge content short (under 100 characters)"
|
|
7745
|
-
});
|
|
7746
|
-
}
|
|
7747
|
-
}
|
|
7748
|
-
function validateTooltipAction(action, errors, _warnings) {
|
|
7749
|
-
if (!action.content || typeof action.content !== "object") {
|
|
7750
|
-
errors.push({
|
|
7751
|
-
code: "MISSING_CONTENT",
|
|
7752
|
-
message: "Tooltip action requires 'content' object",
|
|
7753
|
-
field: "content"
|
|
7754
|
-
});
|
|
7755
|
-
return;
|
|
7756
|
-
}
|
|
7757
|
-
if (!action.content.body || typeof action.content.body !== "string") {
|
|
7758
|
-
errors.push({
|
|
7759
|
-
code: "MISSING_BODY",
|
|
7760
|
-
message: "Tooltip content requires 'body' property",
|
|
7761
|
-
field: "content.body"
|
|
8258
|
+
code: "MISSING_BODY",
|
|
8259
|
+
message: "Tooltip content requires 'body' property",
|
|
8260
|
+
field: "content.body"
|
|
7762
8261
|
});
|
|
7763
8262
|
}
|
|
7764
8263
|
}
|
|
@@ -8116,6 +8615,159 @@ function validateTourAction(action, errors, warnings) {
|
|
|
8116
8615
|
}
|
|
8117
8616
|
}
|
|
8118
8617
|
}
|
|
8618
|
+
|
|
8619
|
+
// src/actions/validation-core.ts
|
|
8620
|
+
var DANGEROUS_ATTRS = /* @__PURE__ */ new Set([
|
|
8621
|
+
"onclick",
|
|
8622
|
+
"onerror",
|
|
8623
|
+
"onload",
|
|
8624
|
+
"onmouseover",
|
|
8625
|
+
"onfocus",
|
|
8626
|
+
"onblur",
|
|
8627
|
+
"onchange",
|
|
8628
|
+
"onsubmit",
|
|
8629
|
+
"onkeydown",
|
|
8630
|
+
"onkeyup",
|
|
8631
|
+
"onkeypress"
|
|
8632
|
+
]);
|
|
8633
|
+
var MAX_HTML_LENGTH = 5e4;
|
|
8634
|
+
var MAX_STYLE_COUNT = 50;
|
|
8635
|
+
function validateAction(action) {
|
|
8636
|
+
const errors = [];
|
|
8637
|
+
const warnings = [];
|
|
8638
|
+
if (!action || typeof action !== "object") {
|
|
8639
|
+
errors.push({
|
|
8640
|
+
code: "INVALID_ACTION",
|
|
8641
|
+
message: "Action must be an object"
|
|
8642
|
+
});
|
|
8643
|
+
return { valid: false, errors, warnings };
|
|
8644
|
+
}
|
|
8645
|
+
const { kind } = action;
|
|
8646
|
+
if (!kind || typeof kind !== "string") {
|
|
8647
|
+
errors.push({
|
|
8648
|
+
code: "MISSING_KIND",
|
|
8649
|
+
message: "Action must have a 'kind' property"
|
|
8650
|
+
});
|
|
8651
|
+
return { valid: false, errors, warnings };
|
|
8652
|
+
}
|
|
8653
|
+
if (!hasExecutor(kind) && kind !== "core:mountWidget") {
|
|
8654
|
+
const registered = executorRegistry.list();
|
|
8655
|
+
console.error(
|
|
8656
|
+
`[ActionValidation] Unknown action kind: "${kind}". Registered kinds (${registered.length}): [${registered.join(", ")}]. This usually means the app that provides "${kind}" hasn't been activated yet, or the executor was never registered in ExecutorRegistry.`
|
|
8657
|
+
);
|
|
8658
|
+
errors.push({
|
|
8659
|
+
code: "UNKNOWN_KIND",
|
|
8660
|
+
message: `Unknown action kind: ${kind}`,
|
|
8661
|
+
field: "kind"
|
|
8662
|
+
});
|
|
8663
|
+
return { valid: false, errors, warnings };
|
|
8664
|
+
}
|
|
8665
|
+
switch (kind) {
|
|
8666
|
+
case "overlays:highlight":
|
|
8667
|
+
case "overlays:pulse":
|
|
8668
|
+
case "navigation:scrollTo":
|
|
8669
|
+
validateAnchorAction(action, errors, warnings);
|
|
8670
|
+
break;
|
|
8671
|
+
case "overlays:badge":
|
|
8672
|
+
validateAnchorAction(action, errors, warnings);
|
|
8673
|
+
validateBadgeAction(action, errors, warnings);
|
|
8674
|
+
break;
|
|
8675
|
+
case "overlays:tooltip":
|
|
8676
|
+
validateAnchorAction(action, errors, warnings);
|
|
8677
|
+
validateTooltipAction(action, errors, warnings);
|
|
8678
|
+
break;
|
|
8679
|
+
case "overlays:modal":
|
|
8680
|
+
validateModalAction(action, errors, warnings);
|
|
8681
|
+
break;
|
|
8682
|
+
case "content:insertHtml":
|
|
8683
|
+
validateAnchorAction(action, errors, warnings);
|
|
8684
|
+
validateInsertHtmlAction(action, errors, warnings);
|
|
8685
|
+
break;
|
|
8686
|
+
case "content:setText":
|
|
8687
|
+
validateAnchorAction(action, errors, warnings);
|
|
8688
|
+
validateSetTextAction(action, errors, warnings);
|
|
8689
|
+
break;
|
|
8690
|
+
case "content:setAttr":
|
|
8691
|
+
validateAnchorAction(action, errors, warnings);
|
|
8692
|
+
validateSetAttrAction(action, errors, warnings);
|
|
8693
|
+
break;
|
|
8694
|
+
case "content:addClass":
|
|
8695
|
+
case "content:removeClass":
|
|
8696
|
+
validateAnchorAction(action, errors, warnings);
|
|
8697
|
+
validateClassAction(action, errors, warnings);
|
|
8698
|
+
break;
|
|
8699
|
+
case "content:setStyle":
|
|
8700
|
+
validateAnchorAction(action, errors, warnings);
|
|
8701
|
+
validateSetStyleAction(action, errors, warnings);
|
|
8702
|
+
break;
|
|
8703
|
+
case "core:mountWidget":
|
|
8704
|
+
validateMountWidgetAction(action, errors, warnings);
|
|
8705
|
+
break;
|
|
8706
|
+
case "core:wait":
|
|
8707
|
+
validateWaitAction(action, errors, warnings);
|
|
8708
|
+
break;
|
|
8709
|
+
case "core:sequence":
|
|
8710
|
+
validateSequenceAction(action, errors, warnings);
|
|
8711
|
+
break;
|
|
8712
|
+
case "core:parallel":
|
|
8713
|
+
validateParallelAction(action, errors, warnings);
|
|
8714
|
+
break;
|
|
8715
|
+
case "overlays:tour":
|
|
8716
|
+
validateTourAction(action, errors, warnings);
|
|
8717
|
+
break;
|
|
8718
|
+
case "navigation:navigate":
|
|
8719
|
+
validateNavigateAction(action, errors, warnings);
|
|
8720
|
+
break;
|
|
8721
|
+
}
|
|
8722
|
+
return {
|
|
8723
|
+
valid: errors.length === 0,
|
|
8724
|
+
errors,
|
|
8725
|
+
warnings
|
|
8726
|
+
};
|
|
8727
|
+
}
|
|
8728
|
+
function validateAnchorAction(action, errors, warnings) {
|
|
8729
|
+
const anchorId = action.anchorId;
|
|
8730
|
+
if (!anchorId || typeof anchorId !== "object") {
|
|
8731
|
+
errors.push({
|
|
8732
|
+
code: "MISSING_ANCHOR_ID",
|
|
8733
|
+
message: "Action requires an 'anchorId' object with a 'selector' string",
|
|
8734
|
+
field: "anchorId"
|
|
8735
|
+
});
|
|
8736
|
+
return;
|
|
8737
|
+
}
|
|
8738
|
+
if (!anchorId.selector || typeof anchorId.selector !== "string") {
|
|
8739
|
+
errors.push({
|
|
8740
|
+
code: "MISSING_ANCHOR_SELECTOR",
|
|
8741
|
+
message: "anchorId requires a 'selector' string",
|
|
8742
|
+
field: "anchorId.selector"
|
|
8743
|
+
});
|
|
8744
|
+
} else if (anchorId.selector.length > 200) {
|
|
8745
|
+
warnings.push({
|
|
8746
|
+
code: "LONG_ANCHOR_ID",
|
|
8747
|
+
message: "Anchor selector is unusually long",
|
|
8748
|
+
suggestion: "Consider using a shorter, more descriptive selector"
|
|
8749
|
+
});
|
|
8750
|
+
}
|
|
8751
|
+
if (anchorId.route === void 0 || anchorId.route === null) {
|
|
8752
|
+
errors.push({
|
|
8753
|
+
code: "MISSING_ANCHOR_ROUTE",
|
|
8754
|
+
message: `anchorId requires a 'route' (string or array of strings). Use "**" for all routes.`,
|
|
8755
|
+
field: "anchorId.route"
|
|
8756
|
+
});
|
|
8757
|
+
} else {
|
|
8758
|
+
const routes = Array.isArray(anchorId.route) ? anchorId.route : [anchorId.route];
|
|
8759
|
+
for (const route of routes) {
|
|
8760
|
+
if (typeof route !== "string") {
|
|
8761
|
+
errors.push({
|
|
8762
|
+
code: "INVALID_ANCHOR_ROUTE",
|
|
8763
|
+
message: "anchorId.route must be a string or array of strings",
|
|
8764
|
+
field: "anchorId.route"
|
|
8765
|
+
});
|
|
8766
|
+
break;
|
|
8767
|
+
}
|
|
8768
|
+
}
|
|
8769
|
+
}
|
|
8770
|
+
}
|
|
8119
8771
|
function validateActions(actions) {
|
|
8120
8772
|
var _a2;
|
|
8121
8773
|
const errors = [];
|
|
@@ -8262,7 +8914,7 @@ function createActionEngine(options) {
|
|
|
8262
8914
|
}
|
|
8263
8915
|
return executor(action, context);
|
|
8264
8916
|
}
|
|
8265
|
-
function subscribeForReeval(id, action, triggerWhen,
|
|
8917
|
+
function subscribeForReeval(id, action, triggerWhen, _handle) {
|
|
8266
8918
|
if (!runtime3) return;
|
|
8267
8919
|
const unsubs = [];
|
|
8268
8920
|
const onReeval = async () => {
|
|
@@ -9232,6 +9884,66 @@ function createEventAccumulator(options) {
|
|
|
9232
9884
|
};
|
|
9233
9885
|
}
|
|
9234
9886
|
|
|
9887
|
+
// src/events/validation.ts
|
|
9888
|
+
var APP_PREFIX = "app:";
|
|
9889
|
+
var RESERVED_PREFIX = "syntro:";
|
|
9890
|
+
var SEGMENT_PATTERN = /^[a-z][a-z0-9_]*$/;
|
|
9891
|
+
function validateEventName(name) {
|
|
9892
|
+
if (!name) {
|
|
9893
|
+
return { valid: false, reason: "Event name cannot be empty" };
|
|
9894
|
+
}
|
|
9895
|
+
if (name.startsWith(RESERVED_PREFIX)) {
|
|
9896
|
+
return { valid: false, reason: '"syntro:" prefix is reserved for internal SDK events' };
|
|
9897
|
+
}
|
|
9898
|
+
if (!name.startsWith(APP_PREFIX)) {
|
|
9899
|
+
return { valid: false, reason: `Custom events must start with "app:" prefix. Got: "${name}"` };
|
|
9900
|
+
}
|
|
9901
|
+
const segments = name.slice(APP_PREFIX.length).split(":");
|
|
9902
|
+
if (segments.length < 2) {
|
|
9903
|
+
return {
|
|
9904
|
+
valid: false,
|
|
9905
|
+
reason: `Event name must have at least 2 segments after "app:" (app:{category}:{action}). Got: "${name}"`
|
|
9906
|
+
};
|
|
9907
|
+
}
|
|
9908
|
+
for (const segment of segments) {
|
|
9909
|
+
if (!SEGMENT_PATTERN.test(segment)) {
|
|
9910
|
+
return {
|
|
9911
|
+
valid: false,
|
|
9912
|
+
reason: `Segment "${segment}" must be lowercase alphanumeric + underscores. Got: "${name}"`
|
|
9913
|
+
};
|
|
9914
|
+
}
|
|
9915
|
+
}
|
|
9916
|
+
return { valid: true };
|
|
9917
|
+
}
|
|
9918
|
+
function isSerializable(value) {
|
|
9919
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null;
|
|
9920
|
+
}
|
|
9921
|
+
function checkDepth(obj, maxDepth, current = 0) {
|
|
9922
|
+
if (current >= maxDepth) return false;
|
|
9923
|
+
for (const value of Object.values(obj)) {
|
|
9924
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
9925
|
+
if (!checkDepth(value, maxDepth, current + 1)) return false;
|
|
9926
|
+
}
|
|
9927
|
+
}
|
|
9928
|
+
return true;
|
|
9929
|
+
}
|
|
9930
|
+
function validateProps(props) {
|
|
9931
|
+
if (props === void 0) return { valid: true };
|
|
9932
|
+
if (props === null || typeof props !== "object" || Array.isArray(props)) {
|
|
9933
|
+
return { valid: false, reason: "Props must be a plain object" };
|
|
9934
|
+
}
|
|
9935
|
+
if (!checkDepth(props, 2)) {
|
|
9936
|
+
return { valid: false, reason: "Props nesting depth exceeds 2 levels" };
|
|
9937
|
+
}
|
|
9938
|
+
const stripped = [];
|
|
9939
|
+
for (const [key, value] of Object.entries(props)) {
|
|
9940
|
+
if (value !== null && typeof value === "object") continue;
|
|
9941
|
+
if (!isSerializable(value)) stripped.push(key);
|
|
9942
|
+
}
|
|
9943
|
+
if (stripped.length > 0) return { valid: true, stripped };
|
|
9944
|
+
return { valid: true };
|
|
9945
|
+
}
|
|
9946
|
+
|
|
9235
9947
|
// src/events/EventBus.ts
|
|
9236
9948
|
function matchesFilter(event, filter) {
|
|
9237
9949
|
if (!filter) return true;
|
|
@@ -9263,8 +9975,16 @@ var EventBus = class {
|
|
|
9263
9975
|
__publicField(this, "subscriptions", /* @__PURE__ */ new Set());
|
|
9264
9976
|
__publicField(this, "history", []);
|
|
9265
9977
|
__publicField(this, "maxHistorySize");
|
|
9266
|
-
|
|
9978
|
+
__publicField(this, "debug");
|
|
9979
|
+
__publicField(this, "emitHistory");
|
|
9980
|
+
__publicField(this, "posthogCapture");
|
|
9981
|
+
__publicField(this, "testMode");
|
|
9982
|
+
var _a2, _b, _c, _d, _e;
|
|
9267
9983
|
this.maxHistorySize = (_a2 = options.maxHistorySize) != null ? _a2 : 100;
|
|
9984
|
+
this.debug = (_b = options.debug) != null ? _b : false;
|
|
9985
|
+
this.emitHistory = (_c = options.history) != null ? _c : null;
|
|
9986
|
+
this.posthogCapture = (_d = options.posthogCapture) != null ? _d : null;
|
|
9987
|
+
this.testMode = (_e = options.testMode) != null ? _e : false;
|
|
9268
9988
|
}
|
|
9269
9989
|
/**
|
|
9270
9990
|
* Subscribe to events matching an optional filter.
|
|
@@ -9293,29 +10013,106 @@ var EventBus = class {
|
|
|
9293
10013
|
const event = {
|
|
9294
10014
|
ts: Date.now(),
|
|
9295
10015
|
name,
|
|
9296
|
-
source,
|
|
10016
|
+
source,
|
|
10017
|
+
props,
|
|
10018
|
+
schemaVersion: EVENT_SCHEMA_VERSION
|
|
10019
|
+
};
|
|
10020
|
+
this.publishEvent(event);
|
|
10021
|
+
}
|
|
10022
|
+
/**
|
|
10023
|
+
* Publish a pre-constructed NormalizedEvent.
|
|
10024
|
+
*/
|
|
10025
|
+
publishEvent(event) {
|
|
10026
|
+
this.history.push(event);
|
|
10027
|
+
if (this.history.length > this.maxHistorySize) {
|
|
10028
|
+
this.history.shift();
|
|
10029
|
+
}
|
|
10030
|
+
for (const subscription of this.subscriptions) {
|
|
10031
|
+
if (matchesFilter(event, subscription.filter)) {
|
|
10032
|
+
try {
|
|
10033
|
+
subscription.callback(event);
|
|
10034
|
+
} catch (err) {
|
|
10035
|
+
console.error("[EventBus] Subscriber error:", err);
|
|
10036
|
+
}
|
|
10037
|
+
}
|
|
10038
|
+
}
|
|
10039
|
+
}
|
|
10040
|
+
/**
|
|
10041
|
+
* Emit a validated custom event from the host application.
|
|
10042
|
+
*
|
|
10043
|
+
* Custom events must use the `app:` prefix (e.g. `app:cart:abandoned`).
|
|
10044
|
+
* In debug mode, returns an EmitResult with delivery details.
|
|
10045
|
+
* In production mode, returns undefined.
|
|
10046
|
+
*/
|
|
10047
|
+
emit(name, props) {
|
|
10048
|
+
const nameResult = validateEventName(name);
|
|
10049
|
+
if (!nameResult.valid) {
|
|
10050
|
+
console.warn(`[EventBus] emit() rejected: ${nameResult.reason}`);
|
|
10051
|
+
return this.debug ? { delivered: false, matchedRules: [], posthogCaptured: false, listenersNotified: 0 } : void 0;
|
|
10052
|
+
}
|
|
10053
|
+
const propsResult = validateProps(props);
|
|
10054
|
+
if (!propsResult.valid) {
|
|
10055
|
+
console.warn(`[EventBus] emit() rejected props: ${propsResult.reason}`);
|
|
10056
|
+
return this.debug ? { delivered: false, matchedRules: [], posthogCaptured: false, listenersNotified: 0 } : void 0;
|
|
10057
|
+
}
|
|
10058
|
+
const event = {
|
|
10059
|
+
ts: Date.now(),
|
|
10060
|
+
name,
|
|
10061
|
+
source: "custom",
|
|
9297
10062
|
props,
|
|
9298
10063
|
schemaVersion: EVENT_SCHEMA_VERSION
|
|
9299
10064
|
};
|
|
9300
|
-
this.publishEvent(event);
|
|
9301
|
-
}
|
|
9302
|
-
/**
|
|
9303
|
-
* Publish a pre-constructed NormalizedEvent.
|
|
9304
|
-
*/
|
|
9305
|
-
publishEvent(event) {
|
|
9306
10065
|
this.history.push(event);
|
|
9307
10066
|
if (this.history.length > this.maxHistorySize) {
|
|
9308
10067
|
this.history.shift();
|
|
9309
10068
|
}
|
|
10069
|
+
let listenersNotified = 0;
|
|
9310
10070
|
for (const subscription of this.subscriptions) {
|
|
9311
10071
|
if (matchesFilter(event, subscription.filter)) {
|
|
9312
10072
|
try {
|
|
9313
10073
|
subscription.callback(event);
|
|
10074
|
+
listenersNotified++;
|
|
9314
10075
|
} catch (err) {
|
|
9315
10076
|
console.error("[EventBus] Subscriber error:", err);
|
|
10077
|
+
listenersNotified++;
|
|
9316
10078
|
}
|
|
9317
10079
|
}
|
|
9318
10080
|
}
|
|
10081
|
+
let posthogCaptured = false;
|
|
10082
|
+
if (this.posthogCapture && !this.testMode) {
|
|
10083
|
+
try {
|
|
10084
|
+
this.posthogCapture(name, props);
|
|
10085
|
+
posthogCaptured = true;
|
|
10086
|
+
} catch (err) {
|
|
10087
|
+
console.error("[EventBus] PostHog capture error:", err);
|
|
10088
|
+
}
|
|
10089
|
+
}
|
|
10090
|
+
if (this.emitHistory) {
|
|
10091
|
+
this.emitHistory.record({
|
|
10092
|
+
name,
|
|
10093
|
+
props,
|
|
10094
|
+
source: "custom",
|
|
10095
|
+
timestamp: event.ts,
|
|
10096
|
+
matchedRules: []
|
|
10097
|
+
});
|
|
10098
|
+
}
|
|
10099
|
+
if (this.debug) {
|
|
10100
|
+
console.debug("[EventBus] emit()", { name, props, listenersNotified, posthogCaptured });
|
|
10101
|
+
return {
|
|
10102
|
+
delivered: true,
|
|
10103
|
+
matchedRules: [],
|
|
10104
|
+
posthogCaptured,
|
|
10105
|
+
listenersNotified
|
|
10106
|
+
};
|
|
10107
|
+
}
|
|
10108
|
+
return void 0;
|
|
10109
|
+
}
|
|
10110
|
+
/**
|
|
10111
|
+
* Set the PostHog capture function after construction.
|
|
10112
|
+
* Used by bootstrap to wire PostHog after the EventBus is created.
|
|
10113
|
+
*/
|
|
10114
|
+
setPosthogCapture(fn) {
|
|
10115
|
+
this.posthogCapture = fn;
|
|
9319
10116
|
}
|
|
9320
10117
|
/**
|
|
9321
10118
|
* Get recent events matching an optional filter.
|
|
@@ -9367,6 +10164,27 @@ function createEventBus(options = {}) {
|
|
|
9367
10164
|
return new EventBus(options);
|
|
9368
10165
|
}
|
|
9369
10166
|
|
|
10167
|
+
// src/events/history.ts
|
|
10168
|
+
var EventHistory = class {
|
|
10169
|
+
constructor(maxSize = 100) {
|
|
10170
|
+
__publicField(this, "entries", []);
|
|
10171
|
+
__publicField(this, "maxSize");
|
|
10172
|
+
this.maxSize = maxSize;
|
|
10173
|
+
}
|
|
10174
|
+
record(entry) {
|
|
10175
|
+
this.entries.push(entry);
|
|
10176
|
+
if (this.maxSize > 0 && this.entries.length > this.maxSize) {
|
|
10177
|
+
this.entries.shift();
|
|
10178
|
+
}
|
|
10179
|
+
}
|
|
10180
|
+
getAll() {
|
|
10181
|
+
return [...this.entries];
|
|
10182
|
+
}
|
|
10183
|
+
clear() {
|
|
10184
|
+
this.entries = [];
|
|
10185
|
+
}
|
|
10186
|
+
};
|
|
10187
|
+
|
|
9370
10188
|
// src/navigation/NavigationMonitor.ts
|
|
9371
10189
|
var NavigationMonitor = class {
|
|
9372
10190
|
constructor() {
|
|
@@ -10478,7 +11296,16 @@ function matchesAnchorRoute(anchorId) {
|
|
|
10478
11296
|
const pathname = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
10479
11297
|
const normalizedPath = pathname.replace(/\/$/, "") || "/";
|
|
10480
11298
|
const routes = Array.isArray(anchorId.route) ? anchorId.route : [anchorId.route];
|
|
10481
|
-
return routes.some((pattern) =>
|
|
11299
|
+
return routes.some((pattern) => {
|
|
11300
|
+
let normalized = pattern;
|
|
11301
|
+
if (/^https?:\/\//.test(normalized)) {
|
|
11302
|
+
try {
|
|
11303
|
+
normalized = new URL(normalized).pathname;
|
|
11304
|
+
} catch {
|
|
11305
|
+
}
|
|
11306
|
+
}
|
|
11307
|
+
return matchRoutePattern(normalizedPath, normalized);
|
|
11308
|
+
});
|
|
10482
11309
|
}
|
|
10483
11310
|
function createSmartCanvasRuntime(options = {}) {
|
|
10484
11311
|
var _a2, _b, _c, _d;
|
|
@@ -10650,8 +11477,141 @@ function encodeToken(payload) {
|
|
|
10650
11477
|
return TOKEN_PREFIX + base64;
|
|
10651
11478
|
}
|
|
10652
11479
|
|
|
10653
|
-
// src/bootstrap.ts
|
|
10654
|
-
|
|
11480
|
+
// src/bootstrap-init.ts
|
|
11481
|
+
function getEnvVar(name) {
|
|
11482
|
+
if (typeof process !== "undefined" && process.env) {
|
|
11483
|
+
return process.env[name];
|
|
11484
|
+
}
|
|
11485
|
+
try {
|
|
11486
|
+
const meta = (0, eval)("import.meta");
|
|
11487
|
+
if (meta == null ? void 0 : meta.env) {
|
|
11488
|
+
return meta.env[name];
|
|
11489
|
+
}
|
|
11490
|
+
} catch {
|
|
11491
|
+
}
|
|
11492
|
+
return void 0;
|
|
11493
|
+
}
|
|
11494
|
+
var SEGMENT_CACHE_KEY = "syntro_segment_attributes";
|
|
11495
|
+
function loadCachedSegmentAttributes() {
|
|
11496
|
+
if (typeof window === "undefined") return {};
|
|
11497
|
+
try {
|
|
11498
|
+
const cached = localStorage.getItem(SEGMENT_CACHE_KEY);
|
|
11499
|
+
if (cached) {
|
|
11500
|
+
const attrs = JSON.parse(cached);
|
|
11501
|
+
debug("Syntro Bootstrap", "Loaded cached segment attributes:", attrs);
|
|
11502
|
+
return attrs;
|
|
11503
|
+
}
|
|
11504
|
+
} catch (err) {
|
|
11505
|
+
warn("Syntro Bootstrap", "Failed to load cached segment attributes:", err);
|
|
11506
|
+
}
|
|
11507
|
+
return {};
|
|
11508
|
+
}
|
|
11509
|
+
function cacheSegmentAttributes(attrs) {
|
|
11510
|
+
if (typeof window === "undefined") return;
|
|
11511
|
+
try {
|
|
11512
|
+
localStorage.setItem(SEGMENT_CACHE_KEY, JSON.stringify(attrs));
|
|
11513
|
+
debug("Syntro Bootstrap", "Cached segment attributes:", attrs);
|
|
11514
|
+
} catch (err) {
|
|
11515
|
+
warn("Syntro Bootstrap", "Failed to cache segment attributes:", err);
|
|
11516
|
+
}
|
|
11517
|
+
}
|
|
11518
|
+
function extractSegmentFlags(allFlags) {
|
|
11519
|
+
if (!allFlags) return {};
|
|
11520
|
+
const segmentFlags = {};
|
|
11521
|
+
for (const [key, value] of Object.entries(allFlags)) {
|
|
11522
|
+
if (key.startsWith("in_segment_")) {
|
|
11523
|
+
segmentFlags[key] = value === true;
|
|
11524
|
+
}
|
|
11525
|
+
}
|
|
11526
|
+
return segmentFlags;
|
|
11527
|
+
}
|
|
11528
|
+
function collectBrowserMetadata() {
|
|
11529
|
+
var _a2;
|
|
11530
|
+
if (typeof window === "undefined") return {};
|
|
11531
|
+
const attrs = {};
|
|
11532
|
+
try {
|
|
11533
|
+
const params = new URLSearchParams(window.location.search);
|
|
11534
|
+
for (const key of ["utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term"]) {
|
|
11535
|
+
const val = params.get(key);
|
|
11536
|
+
if (val) attrs[key] = val;
|
|
11537
|
+
}
|
|
11538
|
+
} catch {
|
|
11539
|
+
}
|
|
11540
|
+
try {
|
|
11541
|
+
if (navigator.language) attrs.browser_language = navigator.language;
|
|
11542
|
+
if ((_a2 = navigator.languages) == null ? void 0 : _a2.length) attrs.browser_languages = [...navigator.languages];
|
|
11543
|
+
} catch {
|
|
11544
|
+
}
|
|
11545
|
+
try {
|
|
11546
|
+
const w = window.innerWidth;
|
|
11547
|
+
attrs.device_type = w < 768 ? "mobile" : w < 1024 ? "tablet" : "desktop";
|
|
11548
|
+
} catch {
|
|
11549
|
+
}
|
|
11550
|
+
try {
|
|
11551
|
+
if (document.referrer) {
|
|
11552
|
+
attrs.referrer = document.referrer;
|
|
11553
|
+
try {
|
|
11554
|
+
attrs.referrer_hostname = new URL(document.referrer).hostname;
|
|
11555
|
+
} catch {
|
|
11556
|
+
}
|
|
11557
|
+
}
|
|
11558
|
+
} catch {
|
|
11559
|
+
}
|
|
11560
|
+
try {
|
|
11561
|
+
const ua = navigator.userAgent;
|
|
11562
|
+
if (ua.includes("Edg/")) attrs.browser = "Edge";
|
|
11563
|
+
else if (ua.includes("OPR/") || ua.includes("Opera")) attrs.browser = "Opera";
|
|
11564
|
+
else if (ua.includes("Chrome/") && !ua.includes("Chromium")) attrs.browser = "Chrome";
|
|
11565
|
+
else if (ua.includes("Safari/") && !ua.includes("Chrome")) attrs.browser = "Safari";
|
|
11566
|
+
else if (ua.includes("Firefox/")) attrs.browser = "Firefox";
|
|
11567
|
+
if (ua.includes("Windows")) attrs.os = "Windows";
|
|
11568
|
+
else if (ua.includes("iPhone") || ua.includes("iPad")) attrs.os = "iOS";
|
|
11569
|
+
else if (ua.includes("Mac OS X") || ua.includes("Macintosh")) attrs.os = "macOS";
|
|
11570
|
+
else if (ua.includes("Android")) attrs.os = "Android";
|
|
11571
|
+
else if (ua.includes("Linux")) attrs.os = "Linux";
|
|
11572
|
+
} catch {
|
|
11573
|
+
}
|
|
11574
|
+
try {
|
|
11575
|
+
attrs.page_url = window.location.href;
|
|
11576
|
+
attrs.page_path = window.location.pathname;
|
|
11577
|
+
attrs.page_host = window.location.hostname;
|
|
11578
|
+
if (window.location.search) attrs.page_query = window.location.search;
|
|
11579
|
+
} catch {
|
|
11580
|
+
}
|
|
11581
|
+
return attrs;
|
|
11582
|
+
}
|
|
11583
|
+
var GEO_CACHE_KEY = "syntro_geo";
|
|
11584
|
+
var GEO_DEFAULT_HOST = "https://geo.syntrologie.com";
|
|
11585
|
+
async function fetchGeo(geoHost) {
|
|
11586
|
+
if (typeof window === "undefined") return {};
|
|
11587
|
+
try {
|
|
11588
|
+
const cached = localStorage.getItem(GEO_CACHE_KEY);
|
|
11589
|
+
if (cached) {
|
|
11590
|
+
const parsed = JSON.parse(cached);
|
|
11591
|
+
debug("Syntro Bootstrap", "Geo: using cached data:", parsed);
|
|
11592
|
+
return parsed;
|
|
11593
|
+
}
|
|
11594
|
+
} catch {
|
|
11595
|
+
}
|
|
11596
|
+
try {
|
|
11597
|
+
const res = await fetch(geoHost, { signal: AbortSignal.timeout(2e3) });
|
|
11598
|
+
if (res.ok) {
|
|
11599
|
+
const geo = await res.json();
|
|
11600
|
+
const cleaned = {};
|
|
11601
|
+
for (const [k, v] of Object.entries(geo)) {
|
|
11602
|
+
if (typeof v === "string" && v) cleaned[k] = v;
|
|
11603
|
+
}
|
|
11604
|
+
try {
|
|
11605
|
+
localStorage.setItem(GEO_CACHE_KEY, JSON.stringify(cleaned));
|
|
11606
|
+
} catch {
|
|
11607
|
+
}
|
|
11608
|
+
debug("Syntro Bootstrap", "Geo: fetched from worker:", cleaned);
|
|
11609
|
+
return cleaned;
|
|
11610
|
+
}
|
|
11611
|
+
} catch {
|
|
11612
|
+
}
|
|
11613
|
+
return {};
|
|
11614
|
+
}
|
|
10655
11615
|
|
|
10656
11616
|
// src/experiments/registry.ts
|
|
10657
11617
|
var adapters = {
|
|
@@ -10790,6 +11750,11 @@ var ExperimentsFetcher = class {
|
|
|
10790
11750
|
};
|
|
10791
11751
|
}
|
|
10792
11752
|
}
|
|
11753
|
+
if (!this.featureKey) {
|
|
11754
|
+
throw new Error(
|
|
11755
|
+
"[SmartCanvas] No featureKey configured and no variant flags found. Ensure at least one config feature flag exists in your experiment platform."
|
|
11756
|
+
);
|
|
11757
|
+
}
|
|
10793
11758
|
const config = (_b = (_a2 = this.client).getFeatureValue) == null ? void 0 : _b.call(_a2, this.featureKey, null);
|
|
10794
11759
|
if (!config || typeof config !== "object") {
|
|
10795
11760
|
throw new Error(
|
|
@@ -10901,95 +11866,9 @@ function createTelemetryClient(provider, config) {
|
|
|
10901
11866
|
return factory(config);
|
|
10902
11867
|
}
|
|
10903
11868
|
|
|
10904
|
-
// src/bootstrap.ts
|
|
10905
|
-
function
|
|
10906
|
-
|
|
10907
|
-
return process.env[name];
|
|
10908
|
-
}
|
|
10909
|
-
try {
|
|
10910
|
-
const meta = (0, eval)("import.meta");
|
|
10911
|
-
if (meta == null ? void 0 : meta.env) {
|
|
10912
|
-
return meta.env[name];
|
|
10913
|
-
}
|
|
10914
|
-
} catch {
|
|
10915
|
-
}
|
|
10916
|
-
return void 0;
|
|
10917
|
-
}
|
|
10918
|
-
var SEGMENT_CACHE_KEY = "syntro_segment_attributes";
|
|
10919
|
-
function loadCachedSegmentAttributes() {
|
|
10920
|
-
if (typeof window === "undefined") return {};
|
|
10921
|
-
try {
|
|
10922
|
-
const cached = localStorage.getItem(SEGMENT_CACHE_KEY);
|
|
10923
|
-
if (cached) {
|
|
10924
|
-
const attrs = JSON.parse(cached);
|
|
10925
|
-
debug("Syntro Bootstrap", "Loaded cached segment attributes:", attrs);
|
|
10926
|
-
return attrs;
|
|
10927
|
-
}
|
|
10928
|
-
} catch (err) {
|
|
10929
|
-
warn("Syntro Bootstrap", "Failed to load cached segment attributes:", err);
|
|
10930
|
-
}
|
|
10931
|
-
return {};
|
|
10932
|
-
}
|
|
10933
|
-
function cacheSegmentAttributes(attrs) {
|
|
10934
|
-
if (typeof window === "undefined") return;
|
|
10935
|
-
try {
|
|
10936
|
-
localStorage.setItem(SEGMENT_CACHE_KEY, JSON.stringify(attrs));
|
|
10937
|
-
debug("Syntro Bootstrap", "Cached segment attributes:", attrs);
|
|
10938
|
-
} catch (err) {
|
|
10939
|
-
warn("Syntro Bootstrap", "Failed to cache segment attributes:", err);
|
|
10940
|
-
}
|
|
10941
|
-
}
|
|
10942
|
-
function extractSegmentFlags(allFlags) {
|
|
10943
|
-
if (!allFlags) return {};
|
|
10944
|
-
const segmentFlags = {};
|
|
10945
|
-
for (const [key, value] of Object.entries(allFlags)) {
|
|
10946
|
-
if (key.startsWith("in_segment_")) {
|
|
10947
|
-
segmentFlags[key] = value === true;
|
|
10948
|
-
}
|
|
10949
|
-
}
|
|
10950
|
-
return segmentFlags;
|
|
10951
|
-
}
|
|
10952
|
-
function collectBrowserMetadata() {
|
|
10953
|
-
var _a2;
|
|
10954
|
-
if (typeof window === "undefined") return {};
|
|
10955
|
-
const attrs = {};
|
|
10956
|
-
try {
|
|
10957
|
-
const params = new URLSearchParams(window.location.search);
|
|
10958
|
-
for (const key of ["utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term"]) {
|
|
10959
|
-
const val = params.get(key);
|
|
10960
|
-
if (val) attrs[key] = val;
|
|
10961
|
-
}
|
|
10962
|
-
} catch {
|
|
10963
|
-
}
|
|
10964
|
-
try {
|
|
10965
|
-
if (navigator.language) attrs.browser_language = navigator.language;
|
|
10966
|
-
if ((_a2 = navigator.languages) == null ? void 0 : _a2.length) attrs.browser_languages = [...navigator.languages];
|
|
10967
|
-
} catch {
|
|
10968
|
-
}
|
|
10969
|
-
try {
|
|
10970
|
-
const w = window.innerWidth;
|
|
10971
|
-
attrs.device_type = w < 768 ? "mobile" : w < 1024 ? "tablet" : "desktop";
|
|
10972
|
-
} catch {
|
|
10973
|
-
}
|
|
10974
|
-
try {
|
|
10975
|
-
if (document.referrer) {
|
|
10976
|
-
attrs.referrer = document.referrer;
|
|
10977
|
-
try {
|
|
10978
|
-
attrs.referrer_hostname = new URL(document.referrer).hostname;
|
|
10979
|
-
} catch {
|
|
10980
|
-
}
|
|
10981
|
-
}
|
|
10982
|
-
} catch {
|
|
10983
|
-
}
|
|
10984
|
-
try {
|
|
10985
|
-
attrs.page_url = window.location.href;
|
|
10986
|
-
attrs.page_path = window.location.pathname;
|
|
10987
|
-
} catch {
|
|
10988
|
-
}
|
|
10989
|
-
return attrs;
|
|
10990
|
-
}
|
|
10991
|
-
async function init(options) {
|
|
10992
|
-
var _a2, _b, _c, _d, _e, _f;
|
|
11869
|
+
// src/bootstrap-runtime.ts
|
|
11870
|
+
async function _initCore(options) {
|
|
11871
|
+
var _a2, _b, _c, _d, _e, _f, _g;
|
|
10993
11872
|
initLogger();
|
|
10994
11873
|
debug("Syntro Bootstrap", "====== INIT ======");
|
|
10995
11874
|
debug("Syntro Bootstrap", "Options:", {
|
|
@@ -11054,13 +11933,20 @@ async function init(options) {
|
|
|
11054
11933
|
const experimentHost = getEnvVar("NEXT_PUBLIC_SYNTRO_EXPERIMENT_HOST") || getEnvVar("VITE_SYNTRO_EXPERIMENT_HOST") || (payload == null ? void 0 : payload.eh);
|
|
11055
11934
|
const telemetryHost = getEnvVar("NEXT_PUBLIC_SYNTRO_TELEMETRY_HOST") || getEnvVar("VITE_SYNTRO_TELEMETRY_HOST") || (payload == null ? void 0 : payload.th);
|
|
11056
11935
|
const editorUrl = getEnvVar("NEXT_PUBLIC_SYNTRO_EDITOR_URL") || getEnvVar("VITE_SYNTRO_EDITOR_URL") || ((_b = options.canvas) == null ? void 0 : _b.editorUrl);
|
|
11936
|
+
const geoHost = (payload == null ? void 0 : payload.g) || getEnvVar("NEXT_PUBLIC_SYNTRO_GEO_HOST") || getEnvVar("VITE_SYNTRO_GEO_HOST") || GEO_DEFAULT_HOST;
|
|
11057
11937
|
const cachedSegmentAttrs = loadCachedSegmentAttributes();
|
|
11058
11938
|
const browserMetadata = collectBrowserMetadata();
|
|
11059
11939
|
const phaseOneAttrs = { ...browserMetadata, ...cachedSegmentAttrs };
|
|
11060
11940
|
debug("Syntro Bootstrap", "Phase 1: Browser metadata:", browserMetadata);
|
|
11061
11941
|
debug("Syntro Bootstrap", "Phase 1: Cached segment attributes:", cachedSegmentAttrs);
|
|
11942
|
+
const geoPromise = fetchGeo(geoHost);
|
|
11062
11943
|
let experiments;
|
|
11063
|
-
const
|
|
11944
|
+
const isDebugOrTest = options.debug || options.testMode;
|
|
11945
|
+
const events = createEventBus({
|
|
11946
|
+
debug: options.debug,
|
|
11947
|
+
history: isDebugOrTest ? new EventHistory(options.testMode ? 0 : 100) : void 0,
|
|
11948
|
+
testMode: options.testMode
|
|
11949
|
+
});
|
|
11064
11950
|
console.log("[Syntro Bootstrap] EventBus created");
|
|
11065
11951
|
const processor = createEventProcessor({
|
|
11066
11952
|
elementResolver: typeof window !== "undefined" ? (x, y) => {
|
|
@@ -11122,6 +12008,9 @@ async function init(options) {
|
|
|
11122
12008
|
// undefined falls back to adapter default
|
|
11123
12009
|
// Enable PostHog feature flags for segment membership
|
|
11124
12010
|
enableFeatureFlags: true,
|
|
12011
|
+
// Disable session recording in debug/dev mode (mock telemetry doesn't
|
|
12012
|
+
// support the PostHog recorder extension, causing console errors)
|
|
12013
|
+
sessionRecording: !payload.d,
|
|
11125
12014
|
// Wire up callback for when flags are loaded (Phase 2)
|
|
11126
12015
|
onFeatureFlagsLoaded,
|
|
11127
12016
|
// Wire up event capture to feed into event processor
|
|
@@ -11134,6 +12023,11 @@ async function init(options) {
|
|
|
11134
12023
|
}
|
|
11135
12024
|
});
|
|
11136
12025
|
console.log(`[Syntro Bootstrap] Telemetry client created (${provider}) with EventBus wiring`);
|
|
12026
|
+
const telemetryForCapture = telemetry;
|
|
12027
|
+
events.setPosthogCapture((name, props) => {
|
|
12028
|
+
var _a3;
|
|
12029
|
+
(_a3 = telemetryForCapture.track) == null ? void 0 : _a3.call(telemetryForCapture, name, props);
|
|
12030
|
+
});
|
|
11137
12031
|
}
|
|
11138
12032
|
let sessionMetrics;
|
|
11139
12033
|
if (payload == null ? void 0 : payload.e) {
|
|
@@ -11226,11 +12120,17 @@ async function init(options) {
|
|
|
11226
12120
|
warn("Syntro Bootstrap", "Failed to load GrowthBook features:", err);
|
|
11227
12121
|
}
|
|
11228
12122
|
}
|
|
12123
|
+
const geoData = await geoPromise;
|
|
12124
|
+
if (experiments && Object.keys(geoData).length > 0) {
|
|
12125
|
+
const mergedAttrs = { ...browserMetadata, ...geoData };
|
|
12126
|
+
debug("Syntro Bootstrap", "Merging geo data into GrowthBook attributes:", geoData);
|
|
12127
|
+
(_f = experiments.setAttributes) == null ? void 0 : _f.call(experiments, mergedAttrs);
|
|
12128
|
+
}
|
|
11229
12129
|
let baseFetcher;
|
|
11230
12130
|
if (options.fetcher) {
|
|
11231
12131
|
baseFetcher = options.fetcher;
|
|
11232
12132
|
} else if (payload == null ? void 0 : payload.f) {
|
|
11233
|
-
const configFetcher = createConfigFetcher(payload.f, (
|
|
12133
|
+
const configFetcher = createConfigFetcher(payload.f, (_g = payload.o) != null ? _g : {});
|
|
11234
12134
|
baseFetcher = async () => {
|
|
11235
12135
|
var _a3;
|
|
11236
12136
|
const result = await configFetcher.fetch();
|
|
@@ -11244,15 +12144,35 @@ async function init(options) {
|
|
|
11244
12144
|
}
|
|
11245
12145
|
const warnedAppFailures = /* @__PURE__ */ new Set();
|
|
11246
12146
|
const appLoadingFetcher = baseFetcher ? async () => {
|
|
11247
|
-
var _a3, _b2, _c2, _d2, _e2, _f2,
|
|
12147
|
+
var _a3, _b2, _c2, _d2, _e2, _f2, _g2, _h, _i, _j, _k, _l;
|
|
11248
12148
|
const config = await baseFetcher();
|
|
12149
|
+
const tileCount = (_b2 = (_a3 = config.tiles) == null ? void 0 : _a3.length) != null ? _b2 : 0;
|
|
12150
|
+
const actionCount = (_d2 = (_c2 = config.actions) == null ? void 0 : _c2.length) != null ? _d2 : 0;
|
|
12151
|
+
const variantId = (_e2 = config.meta) == null ? void 0 : _e2.variant_id;
|
|
12152
|
+
if (tileCount > 0 || actionCount > 0) {
|
|
12153
|
+
if (!variantId) {
|
|
12154
|
+
console.warn(
|
|
12155
|
+
"[Syntro] Config has content but no meta.variant_id \u2014 intervention tracking disabled"
|
|
12156
|
+
);
|
|
12157
|
+
}
|
|
12158
|
+
}
|
|
12159
|
+
if (telemetry && variantId) {
|
|
12160
|
+
const tracker = new InterventionTracker(telemetry, variantId);
|
|
12161
|
+
tracker.trackServed(tileCount, actionCount);
|
|
12162
|
+
if (typeof window !== "undefined") {
|
|
12163
|
+
window.SynOS.interventionTracker = tracker;
|
|
12164
|
+
}
|
|
12165
|
+
runtime3.navigation.subscribe(() => {
|
|
12166
|
+
tracker.resetPage();
|
|
12167
|
+
});
|
|
12168
|
+
}
|
|
11249
12169
|
console.log(
|
|
11250
12170
|
"[Syntro Bootstrap] Config fetched:",
|
|
11251
|
-
`tiles=${(
|
|
11252
|
-
`actions=${(
|
|
11253
|
-
`theme=${(
|
|
12171
|
+
`tiles=${(_g2 = (_f2 = config.tiles) == null ? void 0 : _f2.length) != null ? _g2 : 0},`,
|
|
12172
|
+
`actions=${(_i = (_h = config.actions) == null ? void 0 : _h.length) != null ? _i : 0},`,
|
|
12173
|
+
`theme=${(_k = (_j = config.theme) == null ? void 0 : _j.name) != null ? _k : "none"}`
|
|
11254
12174
|
);
|
|
11255
|
-
if (((
|
|
12175
|
+
if (((_l = config.actions) == null ? void 0 : _l.length) > 0) {
|
|
11256
12176
|
console.log(
|
|
11257
12177
|
"[Syntro Bootstrap] Actions in config:",
|
|
11258
12178
|
config.actions.map(
|
|
@@ -11315,10 +12235,33 @@ async function init(options) {
|
|
|
11315
12235
|
});
|
|
11316
12236
|
return { canvas, runtime: runtime3, experiments, telemetry, sessionMetrics, appLoader };
|
|
11317
12237
|
}
|
|
12238
|
+
|
|
12239
|
+
// src/bootstrap.ts
|
|
12240
|
+
async function init(options) {
|
|
12241
|
+
var _a2;
|
|
12242
|
+
try {
|
|
12243
|
+
return await _initCore(options);
|
|
12244
|
+
} catch (err) {
|
|
12245
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12246
|
+
console.warn("[Syntrologie] SDK initialization failed:", message);
|
|
12247
|
+
if (typeof document !== "undefined") {
|
|
12248
|
+
(_a2 = document.getElementById("syntrologie-anti-flicker")) == null ? void 0 : _a2.remove();
|
|
12249
|
+
}
|
|
12250
|
+
return void 0;
|
|
12251
|
+
}
|
|
12252
|
+
}
|
|
12253
|
+
function emit(eventName, props = {}) {
|
|
12254
|
+
var _a2, _b;
|
|
12255
|
+
if (typeof window === "undefined") return;
|
|
12256
|
+
const runtime3 = (_a2 = window.SynOS) == null ? void 0 : _a2.runtime;
|
|
12257
|
+
if (!((_b = runtime3 == null ? void 0 : runtime3.events) == null ? void 0 : _b.publish)) return;
|
|
12258
|
+
runtime3.events.publish({ name: eventName, source: "custom", props });
|
|
12259
|
+
}
|
|
11318
12260
|
var Syntro = {
|
|
11319
12261
|
init,
|
|
11320
12262
|
encodeToken,
|
|
11321
|
-
decodeToken
|
|
12263
|
+
decodeToken,
|
|
12264
|
+
events: { emit }
|
|
11322
12265
|
};
|
|
11323
12266
|
if (typeof window !== "undefined") {
|
|
11324
12267
|
window.Syntro = Syntro;
|
|
@@ -11349,8 +12292,11 @@ export {
|
|
|
11349
12292
|
createSmartCanvasController,
|
|
11350
12293
|
ShadowRootProvider,
|
|
11351
12294
|
useShadowRoot,
|
|
11352
|
-
StandardEvents,
|
|
11353
12295
|
EVENT_SCHEMA_VERSION,
|
|
12296
|
+
normalizePostHogEvent,
|
|
12297
|
+
shouldNormalizeEvent,
|
|
12298
|
+
createPostHogNormalizer,
|
|
12299
|
+
StandardEvents2 as StandardEvents,
|
|
11354
12300
|
CanvasEvents,
|
|
11355
12301
|
NotificationToastStack,
|
|
11356
12302
|
MAX_VISIBLE_TOASTS,
|
|
@@ -11381,6 +12327,7 @@ export {
|
|
|
11381
12327
|
createSessionMetricTracker,
|
|
11382
12328
|
createNoopClient,
|
|
11383
12329
|
createPostHogClient,
|
|
12330
|
+
InterventionTracker,
|
|
11384
12331
|
ExecutorRegistry,
|
|
11385
12332
|
executorRegistry,
|
|
11386
12333
|
getExecutor,
|
|
@@ -11399,8 +12346,11 @@ export {
|
|
|
11399
12346
|
evaluateSync,
|
|
11400
12347
|
createDecisionEngine,
|
|
11401
12348
|
createEventAccumulator,
|
|
12349
|
+
validateEventName,
|
|
12350
|
+
validateProps,
|
|
11402
12351
|
EventBus,
|
|
11403
12352
|
createEventBus,
|
|
12353
|
+
EventHistory,
|
|
11404
12354
|
NavigationMonitor,
|
|
11405
12355
|
StateStore,
|
|
11406
12356
|
createStateStore,
|
|
@@ -11424,4 +12374,4 @@ export {
|
|
|
11424
12374
|
encodeToken,
|
|
11425
12375
|
Syntro
|
|
11426
12376
|
};
|
|
11427
|
-
//# sourceMappingURL=chunk-
|
|
12377
|
+
//# sourceMappingURL=chunk-GF364MMB.js.map
|