@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.
Files changed (38) hide show
  1. package/dist/SmartCanvasApp.js +57 -2
  2. package/dist/SmartCanvasApp.js.map +1 -1
  3. package/dist/bootstrap.d.ts +14 -0
  4. package/dist/bootstrap.js +102 -4
  5. package/dist/bootstrap.js.map +1 -1
  6. package/dist/experiments/registry.d.ts +5 -0
  7. package/dist/experiments/registry.js.map +1 -1
  8. package/dist/hooks/useShadowCanvasConfig.d.ts +3 -1
  9. package/dist/hooks/useShadowCanvasConfig.js +1 -0
  10. package/dist/hooks/useShadowCanvasConfig.js.map +1 -1
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.js +2 -0
  13. package/dist/index.js.map +1 -1
  14. package/dist/metrics/index.d.ts +4 -0
  15. package/dist/metrics/index.js +5 -0
  16. package/dist/metrics/index.js.map +1 -0
  17. package/dist/metrics/sessionMetrics.d.ts +101 -0
  18. package/dist/metrics/sessionMetrics.js +178 -0
  19. package/dist/metrics/sessionMetrics.js.map +1 -0
  20. package/dist/overlays/runtime/overlay/highlight.d.ts +2 -0
  21. package/dist/overlays/runtime/overlay/highlight.js +17 -9
  22. package/dist/overlays/runtime/overlay/highlight.js.map +1 -1
  23. package/dist/overlays/runtime/overlay/runner.js +5 -3
  24. package/dist/overlays/runtime/overlay/runner.js.map +1 -1
  25. package/dist/smart-canvas.esm.js +11 -11
  26. package/dist/smart-canvas.esm.js.map +4 -4
  27. package/dist/smart-canvas.js +319 -17
  28. package/dist/smart-canvas.js.map +3 -3
  29. package/dist/smart-canvas.min.js +11 -11
  30. package/dist/smart-canvas.min.js.map +4 -4
  31. package/dist/telemetry/adapters/posthog.d.ts +24 -0
  32. package/dist/telemetry/adapters/posthog.js +58 -9
  33. package/dist/telemetry/adapters/posthog.js.map +1 -1
  34. package/dist/telemetry/registry.d.ts +9 -4
  35. package/dist/telemetry/registry.js.map +1 -1
  36. package/dist/telemetry/types.d.ts +15 -0
  37. package/dist/types.d.ts +8 -0
  38. package/package.json +1 -1
@@ -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: blocking ? "auto" : "none",
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
- window.addEventListener("keydown", onKey);
3686
+ if (onEsc) {
3687
+ window.addEventListener("keydown", onKey);
3688
+ }
3684
3689
  const onClick = (event) => {
3685
- if (!blocking) {
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
- window.removeEventListener("keydown", onKey);
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
- // Disable feature flags - we use GrowthBook for experiments
29109
- advanced_disable_feature_flags: true,
29110
- advanced_disable_feature_flags_on_first_load: true,
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
- if (!configState.isLoading && !hasContent) {
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 experiments;
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,