@syntrologie/runtime-sdk 0.2.12 → 0.2.14
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/dist/SmartCanvasApp.js +57 -2
- package/dist/SmartCanvasApp.js.map +1 -1
- package/dist/bootstrap.d.ts +14 -0
- package/dist/bootstrap.js +102 -4
- package/dist/bootstrap.js.map +1 -1
- package/dist/experiments/registry.d.ts +5 -0
- package/dist/experiments/registry.js.map +1 -1
- package/dist/hooks/useShadowCanvasConfig.d.ts +3 -1
- package/dist/hooks/useShadowCanvasConfig.js +1 -0
- package/dist/hooks/useShadowCanvasConfig.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/metrics/index.d.ts +4 -0
- package/dist/metrics/index.js +5 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/sessionMetrics.d.ts +101 -0
- package/dist/metrics/sessionMetrics.js +178 -0
- package/dist/metrics/sessionMetrics.js.map +1 -0
- package/dist/overlays/runtime/overlay/highlight.d.ts +2 -0
- package/dist/overlays/runtime/overlay/highlight.js +17 -9
- package/dist/overlays/runtime/overlay/highlight.js.map +1 -1
- package/dist/overlays/runtime/overlay/runner.js +5 -3
- package/dist/overlays/runtime/overlay/runner.js.map +1 -1
- package/dist/smart-canvas.esm.js +11 -11
- package/dist/smart-canvas.esm.js.map +4 -4
- package/dist/smart-canvas.js +319 -17
- package/dist/smart-canvas.js.map +3 -3
- package/dist/smart-canvas.min.js +11 -11
- package/dist/smart-canvas.min.js.map +4 -4
- package/dist/telemetry/adapters/posthog.d.ts +24 -0
- package/dist/telemetry/adapters/posthog.js +58 -9
- package/dist/telemetry/adapters/posthog.js.map +1 -1
- package/dist/telemetry/registry.d.ts +9 -4
- package/dist/telemetry/registry.js.map +1 -1
- package/dist/telemetry/types.d.ts +15 -0
- package/dist/types.d.ts +8 -0
- package/package.json +1 -1
package/dist/smart-canvas.js
CHANGED
|
@@ -3581,16 +3581,19 @@ var SyntrologieSDK = (() => {
|
|
|
3581
3581
|
const opacity = Math.min(Math.max(opts?.scrimOpacity ?? 0.55, 0), 1);
|
|
3582
3582
|
const ringColor = opts?.ringColor ?? "var(--syntro-ring, #5b8cff)";
|
|
3583
3583
|
const blocking = opts?.blocking ?? false;
|
|
3584
|
+
const onClickOutside = opts?.onClickOutside ?? true;
|
|
3585
|
+
const onEsc = opts?.onEsc ?? true;
|
|
3584
3586
|
const rootStyles = getComputedStyle(document.documentElement);
|
|
3585
3587
|
const tokenScrim = rootStyles.getPropertyValue("--syntro-spotlight-backdrop").trim();
|
|
3586
3588
|
const tokenRing = rootStyles.getPropertyValue("--syntro-ring").trim();
|
|
3587
3589
|
const scrim = document.createElement("div");
|
|
3588
3590
|
scrim.className = "syntro-spotlight-scrim";
|
|
3591
|
+
const needsPointerEvents = blocking || onClickOutside;
|
|
3589
3592
|
Object.assign(scrim.style, {
|
|
3590
3593
|
position: "fixed",
|
|
3591
3594
|
inset: "0",
|
|
3592
3595
|
zIndex: "2147483646",
|
|
3593
|
-
pointerEvents:
|
|
3596
|
+
pointerEvents: needsPointerEvents ? "auto" : "none",
|
|
3594
3597
|
background: tokenScrim || `rgba(2, 6, 23, ${opacity})`,
|
|
3595
3598
|
transition: "opacity 220ms ease",
|
|
3596
3599
|
opacity: "0"
|
|
@@ -3678,15 +3681,17 @@ var SyntrologieSDK = (() => {
|
|
|
3678
3681
|
window.addEventListener("scroll", onScroll, true);
|
|
3679
3682
|
window.addEventListener("resize", onResize);
|
|
3680
3683
|
const onKey = (e2) => {
|
|
3681
|
-
if (e2.key === "Escape") handle.destroy();
|
|
3684
|
+
if (e2.key === "Escape" && onEsc) handle.destroy();
|
|
3682
3685
|
};
|
|
3683
|
-
|
|
3686
|
+
if (onEsc) {
|
|
3687
|
+
window.addEventListener("keydown", onKey);
|
|
3688
|
+
}
|
|
3684
3689
|
const onClick = (event) => {
|
|
3685
|
-
if (
|
|
3686
|
-
handle.destroy();
|
|
3687
|
-
} else {
|
|
3690
|
+
if (blocking) {
|
|
3688
3691
|
event.preventDefault();
|
|
3689
3692
|
event.stopPropagation();
|
|
3693
|
+
} else if (onClickOutside) {
|
|
3694
|
+
handle.destroy();
|
|
3690
3695
|
}
|
|
3691
3696
|
};
|
|
3692
3697
|
scrim.addEventListener("click", onClick);
|
|
@@ -3695,7 +3700,9 @@ var SyntrologieSDK = (() => {
|
|
|
3695
3700
|
ro2.disconnect();
|
|
3696
3701
|
window.removeEventListener("scroll", onScroll, true);
|
|
3697
3702
|
window.removeEventListener("resize", onResize);
|
|
3698
|
-
|
|
3703
|
+
if (onEsc) {
|
|
3704
|
+
window.removeEventListener("keydown", onKey);
|
|
3705
|
+
}
|
|
3699
3706
|
scrim.removeEventListener("click", onClick);
|
|
3700
3707
|
scrim.style.opacity = "0";
|
|
3701
3708
|
setTimeout(() => {
|
|
@@ -4119,7 +4126,9 @@ var SyntrologieSDK = (() => {
|
|
|
4119
4126
|
radiusPx: step.ring?.radiusPx,
|
|
4120
4127
|
scrimOpacity: step.scrim?.opacity,
|
|
4121
4128
|
ringColor: step.ringColor,
|
|
4122
|
-
blocking: step.blocking
|
|
4129
|
+
blocking: step.blocking,
|
|
4130
|
+
onClickOutside: step.dismiss?.onClickOutside ?? true,
|
|
4131
|
+
onEsc: step.dismiss?.onEsc ?? true
|
|
4123
4132
|
});
|
|
4124
4133
|
ctx.onEvent?.("syntro_overlay_exposed", { kind: "highlight", stepId: step.id, recipeId: ctx.recipeId });
|
|
4125
4134
|
const timers = [];
|
|
@@ -24545,6 +24554,7 @@ var SyntrologieSDK = (() => {
|
|
|
24545
24554
|
CanvasRecipeZ: () => CanvasRecipeZ,
|
|
24546
24555
|
HighlightStepZ: () => HighlightStepZ,
|
|
24547
24556
|
SelectorZ: () => SelectorZ,
|
|
24557
|
+
SessionMetricTracker: () => SessionMetricTracker,
|
|
24548
24558
|
ShadowCanvasOverlay: () => ShadowCanvasOverlay,
|
|
24549
24559
|
SmartCanvasApp: () => SmartCanvasApp,
|
|
24550
24560
|
SmartCanvasController: () => SmartCanvasController,
|
|
@@ -24558,6 +24568,7 @@ var SyntrologieSDK = (() => {
|
|
|
24558
24568
|
createGrowthBookClient: () => createGrowthBookClient,
|
|
24559
24569
|
createOverlayRecipeFetcher: () => createOverlayRecipeFetcher,
|
|
24560
24570
|
createPostHogClient: () => createPostHogClient,
|
|
24571
|
+
createSessionMetricTracker: () => createSessionMetricTracker,
|
|
24561
24572
|
createSmartCanvas: () => createSmartCanvas,
|
|
24562
24573
|
createSmartCanvasController: () => createSmartCanvasController,
|
|
24563
24574
|
decodeToken: () => decodeToken,
|
|
@@ -29100,14 +29111,18 @@ var SyntrologieSDK = (() => {
|
|
|
29100
29111
|
constructor(options = {}) {
|
|
29101
29112
|
this.options = options;
|
|
29102
29113
|
__publicField(this, "client");
|
|
29114
|
+
__publicField(this, "featureFlagsCallback");
|
|
29103
29115
|
this.client = options.client;
|
|
29116
|
+
this.featureFlagsCallback = options.onFeatureFlagsLoaded;
|
|
29104
29117
|
if (!this.client && typeof window !== "undefined" && options.apiKey) {
|
|
29105
29118
|
this.client = Uo;
|
|
29119
|
+
const enableFeatureFlags = options.enableFeatureFlags ?? true;
|
|
29106
29120
|
this.client.init(options.apiKey, {
|
|
29107
29121
|
api_host: options.apiHost ?? "https://posthog-dev.syntrologie.com",
|
|
29108
|
-
//
|
|
29109
|
-
|
|
29110
|
-
|
|
29122
|
+
// Feature flags for segment membership (in_segment_* flags)
|
|
29123
|
+
// When enabled, /decide is called to get segment flags
|
|
29124
|
+
advanced_disable_feature_flags: !enableFeatureFlags,
|
|
29125
|
+
advanced_disable_feature_flags_on_first_load: !enableFeatureFlags,
|
|
29111
29126
|
// Full-page tracking - all ON by default
|
|
29112
29127
|
autocapture: options.autocapture ?? true,
|
|
29113
29128
|
capture_pageview: options.capturePageview ?? true,
|
|
@@ -29124,10 +29139,43 @@ var SyntrologieSDK = (() => {
|
|
|
29124
29139
|
// Capture performance metrics
|
|
29125
29140
|
capture_performance: true,
|
|
29126
29141
|
// Enable web vitals
|
|
29127
|
-
enable_recording_console_log: true
|
|
29142
|
+
enable_recording_console_log: true,
|
|
29143
|
+
// Bootstrap callback for when flags are loaded
|
|
29144
|
+
loaded: (ph) => {
|
|
29145
|
+
if (enableFeatureFlags && this.featureFlagsCallback) {
|
|
29146
|
+
ph.onFeatureFlags(() => {
|
|
29147
|
+
const allFlags = this.getAllFeatureFlags();
|
|
29148
|
+
if (allFlags && this.featureFlagsCallback) {
|
|
29149
|
+
this.featureFlagsCallback(allFlags);
|
|
29150
|
+
}
|
|
29151
|
+
});
|
|
29152
|
+
}
|
|
29153
|
+
}
|
|
29128
29154
|
});
|
|
29129
29155
|
}
|
|
29130
29156
|
}
|
|
29157
|
+
/**
|
|
29158
|
+
* Get all feature flags from PostHog.
|
|
29159
|
+
* Used to extract segment membership flags (in_segment_*).
|
|
29160
|
+
*/
|
|
29161
|
+
getAllFeatureFlags() {
|
|
29162
|
+
const flags = this.client?.featureFlags?.getFlagVariants?.();
|
|
29163
|
+
return flags;
|
|
29164
|
+
}
|
|
29165
|
+
/**
|
|
29166
|
+
* Get segment membership flags (in_segment_*) from PostHog.
|
|
29167
|
+
*/
|
|
29168
|
+
getSegmentFlags() {
|
|
29169
|
+
const allFlags = this.getAllFeatureFlags();
|
|
29170
|
+
if (!allFlags) return {};
|
|
29171
|
+
const segmentFlags = {};
|
|
29172
|
+
for (const [key, value] of Object.entries(allFlags)) {
|
|
29173
|
+
if (key.startsWith("in_segment_")) {
|
|
29174
|
+
segmentFlags[key] = value === true;
|
|
29175
|
+
}
|
|
29176
|
+
}
|
|
29177
|
+
return segmentFlags;
|
|
29178
|
+
}
|
|
29131
29179
|
identify(id, props) {
|
|
29132
29180
|
this.client?.identify(id, props);
|
|
29133
29181
|
}
|
|
@@ -29176,6 +29224,9 @@ var SyntrologieSDK = (() => {
|
|
|
29176
29224
|
setPersonPropertiesOnce(properties) {
|
|
29177
29225
|
this.client?.capture("$set", { $set_once: properties });
|
|
29178
29226
|
}
|
|
29227
|
+
getDistinctId() {
|
|
29228
|
+
return this.client?.get_distinct_id?.();
|
|
29229
|
+
}
|
|
29179
29230
|
};
|
|
29180
29231
|
function createPostHogClient(options = {}) {
|
|
29181
29232
|
return new PostHogAdapter(options);
|
|
@@ -32159,7 +32210,8 @@ var SyntrologieSDK = (() => {
|
|
|
32159
32210
|
overlayRecipe: response.overlayRecipe,
|
|
32160
32211
|
overlayRecipes: response.overlayRecipes,
|
|
32161
32212
|
theme: response.theme,
|
|
32162
|
-
launcher: response.launcher
|
|
32213
|
+
launcher: response.launcher,
|
|
32214
|
+
routes: response.routes
|
|
32163
32215
|
});
|
|
32164
32216
|
} catch (err) {
|
|
32165
32217
|
setState((prev) => ({
|
|
@@ -38518,6 +38570,23 @@ var SyntrologieSDK = (() => {
|
|
|
38518
38570
|
|
|
38519
38571
|
// src/SmartCanvasApp.tsx
|
|
38520
38572
|
var import_jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
|
|
38573
|
+
function isRouteAllowed(routes, pathname) {
|
|
38574
|
+
if (!routes) return true;
|
|
38575
|
+
if (routes.include?.length) {
|
|
38576
|
+
return routes.include.some((pattern) => matchRoute(pattern, pathname));
|
|
38577
|
+
}
|
|
38578
|
+
if (routes.exclude?.length) {
|
|
38579
|
+
return !routes.exclude.some((pattern) => matchRoute(pattern, pathname));
|
|
38580
|
+
}
|
|
38581
|
+
return true;
|
|
38582
|
+
}
|
|
38583
|
+
function matchRoute(pattern, pathname) {
|
|
38584
|
+
if (pattern.endsWith("/*")) {
|
|
38585
|
+
const prefix = pattern.slice(0, -2);
|
|
38586
|
+
return pathname === prefix || pathname.startsWith(prefix + "/");
|
|
38587
|
+
}
|
|
38588
|
+
return pathname === pattern;
|
|
38589
|
+
}
|
|
38521
38590
|
function SmartCanvasApp({
|
|
38522
38591
|
controller,
|
|
38523
38592
|
fetcher,
|
|
@@ -38539,6 +38608,29 @@ var SyntrologieSDK = (() => {
|
|
|
38539
38608
|
theme
|
|
38540
38609
|
}) {
|
|
38541
38610
|
const [open, setOpen] = (0, import_react10.useState)(controller.getState().open);
|
|
38611
|
+
const [pathname, setPathname] = (0, import_react10.useState)(
|
|
38612
|
+
() => typeof window !== "undefined" ? window.location.pathname : "/"
|
|
38613
|
+
);
|
|
38614
|
+
(0, import_react10.useEffect)(() => {
|
|
38615
|
+
if (typeof window === "undefined") return;
|
|
38616
|
+
const updatePathname = () => setPathname(window.location.pathname);
|
|
38617
|
+
window.addEventListener("popstate", updatePathname);
|
|
38618
|
+
const originalPushState = history.pushState;
|
|
38619
|
+
const originalReplaceState = history.replaceState;
|
|
38620
|
+
history.pushState = function(...args) {
|
|
38621
|
+
originalPushState.apply(this, args);
|
|
38622
|
+
updatePathname();
|
|
38623
|
+
};
|
|
38624
|
+
history.replaceState = function(...args) {
|
|
38625
|
+
originalReplaceState.apply(this, args);
|
|
38626
|
+
updatePathname();
|
|
38627
|
+
};
|
|
38628
|
+
return () => {
|
|
38629
|
+
window.removeEventListener("popstate", updatePathname);
|
|
38630
|
+
history.pushState = originalPushState;
|
|
38631
|
+
history.replaceState = originalReplaceState;
|
|
38632
|
+
};
|
|
38633
|
+
}, []);
|
|
38542
38634
|
const derivedFetcher = (0, import_react10.useMemo)(() => {
|
|
38543
38635
|
if (fetcher) return fetcher;
|
|
38544
38636
|
return createCanvasConfigFetcher({
|
|
@@ -38598,7 +38690,8 @@ var SyntrologieSDK = (() => {
|
|
|
38598
38690
|
// any direct overrides from theme prop
|
|
38599
38691
|
};
|
|
38600
38692
|
}, [configState.theme, theme]);
|
|
38601
|
-
|
|
38693
|
+
const routeAllowed = isRouteAllowed(configState.routes, pathname);
|
|
38694
|
+
if (!configState.isLoading && (!hasContent || !routeAllowed)) {
|
|
38602
38695
|
return null;
|
|
38603
38696
|
}
|
|
38604
38697
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
@@ -39128,6 +39221,151 @@ var SyntrologieSDK = (() => {
|
|
|
39128
39221
|
};
|
|
39129
39222
|
}
|
|
39130
39223
|
|
|
39224
|
+
// src/metrics/sessionMetrics.ts
|
|
39225
|
+
var STORAGE_KEY = "syntro_session_metrics";
|
|
39226
|
+
var SessionMetricTracker = class {
|
|
39227
|
+
constructor(options = {}) {
|
|
39228
|
+
__publicField(this, "metrics", /* @__PURE__ */ new Map());
|
|
39229
|
+
__publicField(this, "experiments");
|
|
39230
|
+
__publicField(this, "attributePrefix");
|
|
39231
|
+
__publicField(this, "onMetricChange");
|
|
39232
|
+
this.experiments = options.experiments;
|
|
39233
|
+
this.attributePrefix = options.attributePrefix ?? "session_";
|
|
39234
|
+
this.onMetricChange = options.onMetricChange;
|
|
39235
|
+
this.loadFromStorage();
|
|
39236
|
+
}
|
|
39237
|
+
/**
|
|
39238
|
+
* Increment a metric by the specified amount.
|
|
39239
|
+
*
|
|
39240
|
+
* @param metricKey - The metric to increment (e.g., "button_clicks", "page_views")
|
|
39241
|
+
* @param amount - Amount to increment by (default: 1)
|
|
39242
|
+
* @returns The new metric value
|
|
39243
|
+
*/
|
|
39244
|
+
increment(metricKey, amount = 1) {
|
|
39245
|
+
const currentValue = this.metrics.get(metricKey) ?? 0;
|
|
39246
|
+
const newValue = currentValue + amount;
|
|
39247
|
+
this.metrics.set(metricKey, newValue);
|
|
39248
|
+
this.saveToStorage();
|
|
39249
|
+
this.updateExperimentAttributes(metricKey, newValue);
|
|
39250
|
+
this.onMetricChange?.(metricKey, newValue);
|
|
39251
|
+
return newValue;
|
|
39252
|
+
}
|
|
39253
|
+
/**
|
|
39254
|
+
* Set a metric to a specific value.
|
|
39255
|
+
*
|
|
39256
|
+
* @param metricKey - The metric to set
|
|
39257
|
+
* @param value - The value to set
|
|
39258
|
+
*/
|
|
39259
|
+
set(metricKey, value) {
|
|
39260
|
+
this.metrics.set(metricKey, value);
|
|
39261
|
+
this.saveToStorage();
|
|
39262
|
+
this.updateExperimentAttributes(metricKey, value);
|
|
39263
|
+
this.onMetricChange?.(metricKey, value);
|
|
39264
|
+
}
|
|
39265
|
+
/**
|
|
39266
|
+
* Get the current value of a metric.
|
|
39267
|
+
*
|
|
39268
|
+
* @param metricKey - The metric to get
|
|
39269
|
+
* @returns The current value (0 if not set)
|
|
39270
|
+
*/
|
|
39271
|
+
get(metricKey) {
|
|
39272
|
+
return this.metrics.get(metricKey) ?? 0;
|
|
39273
|
+
}
|
|
39274
|
+
/**
|
|
39275
|
+
* Check if a metric meets or exceeds a threshold.
|
|
39276
|
+
*
|
|
39277
|
+
* @param metricKey - The metric to check
|
|
39278
|
+
* @param threshold - The threshold value
|
|
39279
|
+
* @returns True if metric >= threshold
|
|
39280
|
+
*/
|
|
39281
|
+
meetsThreshold(metricKey, threshold) {
|
|
39282
|
+
return this.get(metricKey) >= threshold;
|
|
39283
|
+
}
|
|
39284
|
+
/**
|
|
39285
|
+
* Get all current metric values.
|
|
39286
|
+
*
|
|
39287
|
+
* @returns Record of metric keys to values
|
|
39288
|
+
*/
|
|
39289
|
+
getAll() {
|
|
39290
|
+
return Object.fromEntries(this.metrics);
|
|
39291
|
+
}
|
|
39292
|
+
/**
|
|
39293
|
+
* Reset a specific metric to zero.
|
|
39294
|
+
*
|
|
39295
|
+
* @param metricKey - The metric to reset
|
|
39296
|
+
*/
|
|
39297
|
+
reset(metricKey) {
|
|
39298
|
+
this.metrics.delete(metricKey);
|
|
39299
|
+
this.saveToStorage();
|
|
39300
|
+
this.updateExperimentAttributes(metricKey, 0);
|
|
39301
|
+
this.onMetricChange?.(metricKey, 0);
|
|
39302
|
+
}
|
|
39303
|
+
/**
|
|
39304
|
+
* Reset all metrics (clear the session).
|
|
39305
|
+
*/
|
|
39306
|
+
resetAll() {
|
|
39307
|
+
const keys = Array.from(this.metrics.keys());
|
|
39308
|
+
this.metrics.clear();
|
|
39309
|
+
this.saveToStorage();
|
|
39310
|
+
const attrs = {};
|
|
39311
|
+
for (const key of keys) {
|
|
39312
|
+
attrs[`${this.attributePrefix}${key}`] = 0;
|
|
39313
|
+
this.onMetricChange?.(key, 0);
|
|
39314
|
+
}
|
|
39315
|
+
this.experiments?.setAttributes?.(attrs);
|
|
39316
|
+
}
|
|
39317
|
+
/**
|
|
39318
|
+
* Update the experiment client (useful if experiments client changes).
|
|
39319
|
+
*/
|
|
39320
|
+
setExperiments(experiments) {
|
|
39321
|
+
this.experiments = experiments;
|
|
39322
|
+
this.syncAllToExperiments();
|
|
39323
|
+
}
|
|
39324
|
+
// ==================== Private Methods ====================
|
|
39325
|
+
updateExperimentAttributes(metricKey, value) {
|
|
39326
|
+
if (!this.experiments?.setAttributes) return;
|
|
39327
|
+
const attributeKey = `${this.attributePrefix}${metricKey}`;
|
|
39328
|
+
this.experiments.setAttributes({ [attributeKey]: value });
|
|
39329
|
+
}
|
|
39330
|
+
syncAllToExperiments() {
|
|
39331
|
+
if (!this.experiments?.setAttributes) return;
|
|
39332
|
+
const attrs = {};
|
|
39333
|
+
for (const [key, value] of this.metrics) {
|
|
39334
|
+
attrs[`${this.attributePrefix}${key}`] = value;
|
|
39335
|
+
}
|
|
39336
|
+
this.experiments.setAttributes(attrs);
|
|
39337
|
+
}
|
|
39338
|
+
loadFromStorage() {
|
|
39339
|
+
if (typeof window === "undefined" || typeof sessionStorage === "undefined") return;
|
|
39340
|
+
try {
|
|
39341
|
+
const stored = sessionStorage.getItem(STORAGE_KEY);
|
|
39342
|
+
if (stored) {
|
|
39343
|
+
const data = JSON.parse(stored);
|
|
39344
|
+
for (const [key, value] of Object.entries(data)) {
|
|
39345
|
+
this.metrics.set(key, value.count);
|
|
39346
|
+
}
|
|
39347
|
+
}
|
|
39348
|
+
} catch (err) {
|
|
39349
|
+
console.warn("[SessionMetricTracker] Failed to load from sessionStorage:", err);
|
|
39350
|
+
}
|
|
39351
|
+
}
|
|
39352
|
+
saveToStorage() {
|
|
39353
|
+
if (typeof window === "undefined" || typeof sessionStorage === "undefined") return;
|
|
39354
|
+
try {
|
|
39355
|
+
const data = {};
|
|
39356
|
+
for (const [key, count] of this.metrics) {
|
|
39357
|
+
data[key] = { count, lastUpdated: Date.now() };
|
|
39358
|
+
}
|
|
39359
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
|
39360
|
+
} catch (err) {
|
|
39361
|
+
console.warn("[SessionMetricTracker] Failed to save to sessionStorage:", err);
|
|
39362
|
+
}
|
|
39363
|
+
}
|
|
39364
|
+
};
|
|
39365
|
+
function createSessionMetricTracker(options = {}) {
|
|
39366
|
+
return new SessionMetricTracker(options);
|
|
39367
|
+
}
|
|
39368
|
+
|
|
39131
39369
|
// src/token.ts
|
|
39132
39370
|
var TOKEN_PREFIX = "syn_";
|
|
39133
39371
|
function decodeToken(token) {
|
|
@@ -39379,6 +39617,40 @@ var SyntrologieSDK = (() => {
|
|
|
39379
39617
|
});
|
|
39380
39618
|
return hasToken;
|
|
39381
39619
|
}
|
|
39620
|
+
var SEGMENT_CACHE_KEY = "syntro_segment_attributes";
|
|
39621
|
+
function loadCachedSegmentAttributes() {
|
|
39622
|
+
if (typeof window === "undefined") return {};
|
|
39623
|
+
try {
|
|
39624
|
+
const cached = localStorage.getItem(SEGMENT_CACHE_KEY);
|
|
39625
|
+
if (cached) {
|
|
39626
|
+
const attrs = JSON.parse(cached);
|
|
39627
|
+
console.log("[Syntro Bootstrap] Loaded cached segment attributes:", attrs);
|
|
39628
|
+
return attrs;
|
|
39629
|
+
}
|
|
39630
|
+
} catch (err) {
|
|
39631
|
+
console.warn("[Syntro Bootstrap] Failed to load cached segment attributes:", err);
|
|
39632
|
+
}
|
|
39633
|
+
return {};
|
|
39634
|
+
}
|
|
39635
|
+
function cacheSegmentAttributes(attrs) {
|
|
39636
|
+
if (typeof window === "undefined") return;
|
|
39637
|
+
try {
|
|
39638
|
+
localStorage.setItem(SEGMENT_CACHE_KEY, JSON.stringify(attrs));
|
|
39639
|
+
console.log("[Syntro Bootstrap] Cached segment attributes:", attrs);
|
|
39640
|
+
} catch (err) {
|
|
39641
|
+
console.warn("[Syntro Bootstrap] Failed to cache segment attributes:", err);
|
|
39642
|
+
}
|
|
39643
|
+
}
|
|
39644
|
+
function extractSegmentFlags(allFlags) {
|
|
39645
|
+
if (!allFlags) return {};
|
|
39646
|
+
const segmentFlags = {};
|
|
39647
|
+
for (const [key, value] of Object.entries(allFlags)) {
|
|
39648
|
+
if (key.startsWith("in_segment_")) {
|
|
39649
|
+
segmentFlags[key] = value === true;
|
|
39650
|
+
}
|
|
39651
|
+
}
|
|
39652
|
+
return segmentFlags;
|
|
39653
|
+
}
|
|
39382
39654
|
async function init(options) {
|
|
39383
39655
|
console.log("[Syntro Bootstrap] ====== INIT ======");
|
|
39384
39656
|
console.log("[Syntro Bootstrap] Options:", {
|
|
@@ -39409,20 +39681,41 @@ var SyntrologieSDK = (() => {
|
|
|
39409
39681
|
const experimentHost = getEnvVar("NEXT_PUBLIC_SYNTRO_EXPERIMENT_HOST") || getEnvVar("VITE_SYNTRO_EXPERIMENT_HOST") || payload?.eh;
|
|
39410
39682
|
const telemetryHost = getEnvVar("NEXT_PUBLIC_SYNTRO_TELEMETRY_HOST") || getEnvVar("VITE_SYNTRO_TELEMETRY_HOST") || payload?.th;
|
|
39411
39683
|
const editorUrl = getEnvVar("NEXT_PUBLIC_SYNTRO_EDITOR_URL") || getEnvVar("VITE_SYNTRO_EDITOR_URL") || options.canvas?.editorUrl;
|
|
39684
|
+
const cachedSegmentAttrs = loadCachedSegmentAttributes();
|
|
39685
|
+
console.log("[Syntro Bootstrap] Phase 1: Using cached segment attributes:", cachedSegmentAttrs);
|
|
39686
|
+
let experiments;
|
|
39687
|
+
const onFeatureFlagsLoaded = (allFlags) => {
|
|
39688
|
+
console.log("[Syntro Bootstrap] Phase 2: PostHog feature flags loaded");
|
|
39689
|
+
const segmentFlags = extractSegmentFlags(allFlags);
|
|
39690
|
+
console.log("[Syntro Bootstrap] Segment flags from PostHog:", segmentFlags);
|
|
39691
|
+
cacheSegmentAttributes(segmentFlags);
|
|
39692
|
+
if (experiments) {
|
|
39693
|
+
const sessionAttrs = sessionMetrics?.getAll?.() ?? {};
|
|
39694
|
+
const updatedAttrs = { ...sessionAttrs, ...segmentFlags };
|
|
39695
|
+
console.log("[Syntro Bootstrap] Updating GrowthBook with attributes:", updatedAttrs);
|
|
39696
|
+
experiments.setAttributes?.(updatedAttrs);
|
|
39697
|
+
}
|
|
39698
|
+
};
|
|
39412
39699
|
let telemetry;
|
|
39413
39700
|
if (payload?.t) {
|
|
39414
39701
|
telemetry = createTelemetryClient("posthog", {
|
|
39415
39702
|
apiKey: payload.t,
|
|
39416
|
-
apiHost: telemetryHost
|
|
39703
|
+
apiHost: telemetryHost,
|
|
39417
39704
|
// undefined falls back to adapter default
|
|
39705
|
+
// Enable PostHog feature flags for segment membership
|
|
39706
|
+
enableFeatureFlags: true,
|
|
39707
|
+
// Wire up callback for when flags are loaded (Phase 2)
|
|
39708
|
+
onFeatureFlagsLoaded
|
|
39418
39709
|
});
|
|
39419
39710
|
}
|
|
39420
|
-
let
|
|
39711
|
+
let sessionMetrics;
|
|
39421
39712
|
if (payload?.e) {
|
|
39422
39713
|
experiments = createExperimentClient("growthbook", {
|
|
39423
39714
|
clientKey: payload.e,
|
|
39424
39715
|
apiHost: experimentHost,
|
|
39425
39716
|
// undefined falls back to adapter default
|
|
39717
|
+
// Phase 1: Use cached segment attributes for instant evaluation
|
|
39718
|
+
attributes: cachedSegmentAttrs,
|
|
39426
39719
|
// Wire experiment tracking to telemetry provider
|
|
39427
39720
|
onExperimentViewed: telemetry?.trackExperiment ? (key, variationId, variationName) => {
|
|
39428
39721
|
telemetry.trackExperiment(key, variationId, variationName);
|
|
@@ -39444,13 +39737,22 @@ var SyntrologieSDK = (() => {
|
|
|
39444
39737
|
} else if (experiments) {
|
|
39445
39738
|
fetcher = createCanvasConfigFetcher({ experiments });
|
|
39446
39739
|
}
|
|
39740
|
+
if (options.enableSessionMetrics) {
|
|
39741
|
+
sessionMetrics = createSessionMetricTracker({
|
|
39742
|
+
experiments,
|
|
39743
|
+
onMetricChange: (key, value) => {
|
|
39744
|
+
console.log(`[Syntro Bootstrap] Session metric changed: ${key} = ${value}`);
|
|
39745
|
+
}
|
|
39746
|
+
});
|
|
39747
|
+
console.log("[Syntro Bootstrap] SessionMetricTracker created");
|
|
39748
|
+
}
|
|
39447
39749
|
const canvas = await createSmartCanvas({
|
|
39448
39750
|
...options.canvas,
|
|
39449
39751
|
fetcher,
|
|
39450
39752
|
integrations: { experiments, telemetry },
|
|
39451
39753
|
editorUrl
|
|
39452
39754
|
});
|
|
39453
|
-
return { canvas, experiments, telemetry };
|
|
39755
|
+
return { canvas, experiments, telemetry, sessionMetrics };
|
|
39454
39756
|
}
|
|
39455
39757
|
var Syntro = {
|
|
39456
39758
|
init,
|