@syntrologie/runtime-sdk 2.12.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.
@@ -12802,6 +12802,7 @@ var SyntrologieSDK = (() => {
12802
12802
  HighlightZ: () => HighlightZ,
12803
12803
  InsertHtmlZ: () => InsertHtmlZ,
12804
12804
  InsertPositionZ: () => InsertPositionZ,
12805
+ InterventionTracker: () => InterventionTracker,
12805
12806
  MAX_VISIBLE_TOASTS: () => MAX_VISIBLE_TOASTS,
12806
12807
  MatchOpZ: () => MatchOpZ,
12807
12808
  ModalContentZ: () => ModalContentZ,
@@ -20677,7 +20678,7 @@ Please report this to https://github.com/markedjs/marked.`, e2) {
20677
20678
  }
20678
20679
 
20679
20680
  // src/version.ts
20680
- var SDK_VERSION = "2.12.0";
20681
+ var SDK_VERSION = "2.13.0";
20681
20682
 
20682
20683
  // src/types.ts
20683
20684
  var SDK_SCHEMA_VERSION = "2.0";
@@ -20761,9 +20762,8 @@ Please report this to https://github.com/markedjs/marked.`, e2) {
20761
20762
  featureKey
20762
20763
  }) => {
20763
20764
  if (configUri) return configUri;
20764
- if (experiments) {
20765
- const key = featureKey ?? "smart-canvas-config";
20766
- const fromFeature = experiments.getFeatureValue?.(key, null);
20765
+ if (experiments && featureKey) {
20766
+ const fromFeature = experiments.getFeatureValue?.(featureKey, null);
20767
20767
  if (fromFeature) return fromFeature;
20768
20768
  }
20769
20769
  return void 0;
@@ -20795,9 +20795,8 @@ Please report this to https://github.com/markedjs/marked.`, e2) {
20795
20795
  variantFlagPrefix,
20796
20796
  sdkVersion
20797
20797
  }) => async () => {
20798
- const effectiveConfigKey = configFeatureKey ?? "smart-canvas-config";
20799
- if (experiments) {
20800
- const directConfig = experiments.getFeatureValue?.(effectiveConfigKey, null);
20798
+ if (experiments && configFeatureKey) {
20799
+ const directConfig = experiments.getFeatureValue?.(configFeatureKey, null);
20801
20800
  if (directConfig && typeof directConfig === "object") {
20802
20801
  debug("SmartCanvas Config", "Resolved config directly from feature flag", directConfig);
20803
20802
  return directConfig;
@@ -23454,6 +23453,19 @@ ${cssRules}
23454
23453
  }
23455
23454
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { ref: parentRef });
23456
23455
  }
23456
+ var INTERACTION_PATTERNS = [
23457
+ ":toggled",
23458
+ ":clicked",
23459
+ ":feedback",
23460
+ ":navigate",
23461
+ ":expanded",
23462
+ ":collapsed",
23463
+ ":dismissed",
23464
+ ":submitted",
23465
+ ":interacted",
23466
+ ":tip_clicked",
23467
+ ":tip_focused"
23468
+ ];
23457
23469
  function TileCard({
23458
23470
  config,
23459
23471
  surface: _surface,
@@ -23462,10 +23474,35 @@ ${cssRules}
23462
23474
  }) {
23463
23475
  const { title, subtitle, widget, props, icon } = config;
23464
23476
  const [, setTick] = (0, import_react13.useState)(0);
23477
+ const articleRef = (0, import_react13.useRef)(null);
23465
23478
  const runtime7 = useRuntime();
23466
23479
  (0, import_react13.useEffect)(() => {
23467
23480
  if (runtime7) setTick((t2) => t2 + 1);
23468
23481
  }, [runtime7]);
23482
+ (0, import_react13.useEffect)(() => {
23483
+ const tracker = typeof window !== "undefined" ? window.SynOS?.interventionTracker : null;
23484
+ if (!articleRef.current || !tracker) return;
23485
+ const observer2 = new IntersectionObserver(
23486
+ ([entry]) => {
23487
+ if (entry.isIntersecting) {
23488
+ tracker.trackSeen(config.id, config.widget ?? "unknown");
23489
+ observer2.disconnect();
23490
+ }
23491
+ },
23492
+ { threshold: 0.5 }
23493
+ );
23494
+ observer2.observe(articleRef.current);
23495
+ return () => observer2.disconnect();
23496
+ }, [config.id, config.widget]);
23497
+ (0, import_react13.useEffect)(() => {
23498
+ const tracker = typeof window !== "undefined" ? window.SynOS?.interventionTracker : null;
23499
+ if (!runtime7?.events || !tracker) return;
23500
+ return runtime7.events.subscribe((event) => {
23501
+ if (!INTERACTION_PATTERNS.some((p2) => event.name?.includes(p2))) return;
23502
+ if (event.props?.instanceId !== config.id) return;
23503
+ tracker.trackInteracted(config.id, config.widget ?? "unknown", event.name);
23504
+ });
23505
+ }, [runtime7?.events, config.id, config.widget]);
23469
23506
  const registration = (0, import_react13.useMemo)(
23470
23507
  () => runtime7?.widgets?.getRegistration?.(widget),
23471
23508
  [runtime7?.widgets, widget]
@@ -23514,6 +23551,7 @@ ${cssRules}
23514
23551
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
23515
23552
  "article",
23516
23553
  {
23554
+ ref: articleRef,
23517
23555
  "data-shadow-canvas-id": `tile-${config.id}`,
23518
23556
  style: cardStyle,
23519
23557
  onMouseEnter,
@@ -24085,6 +24123,13 @@ ${cssRules}
24085
24123
  // src/hooks/useShadowCanvasConfig.ts
24086
24124
  var import_react15 = __toESM(require_react(), 1);
24087
24125
  var sortTiles = (tiles) => [...tiles].sort((a2, b3) => (b3.priority ?? 0) - (a2.priority ?? 0));
24126
+ function fireTriggeredForTiles(tiles) {
24127
+ const tracker = typeof window !== "undefined" ? window.SynOS?.interventionTracker : null;
24128
+ if (!tracker) return;
24129
+ for (const tile of tiles) {
24130
+ tracker.trackTriggered(tile.id, tile.widget ?? "unknown");
24131
+ }
24132
+ }
24088
24133
  function useShadowCanvasConfig({
24089
24134
  fetcher,
24090
24135
  experiments,
@@ -24107,6 +24152,7 @@ ${cssRules}
24107
24152
  if (experiments) {
24108
24153
  tiles = tiles.filter((tile) => experiments.shouldRenderRectangle(tile));
24109
24154
  }
24155
+ fireTriggeredForTiles(tiles);
24110
24156
  setState((prev) => ({ ...prev, tiles: sortTiles(tiles) }));
24111
24157
  }, [runtime7, experiments]);
24112
24158
  const load = (0, import_react15.useCallback)(async () => {
@@ -24124,6 +24170,7 @@ ${cssRules}
24124
24170
  } else if (experiments) {
24125
24171
  tiles = tiles.filter((tile) => experiments.shouldRenderRectangle(tile));
24126
24172
  }
24173
+ fireTriggeredForTiles(tiles);
24127
24174
  debug("SmartCanvas Config", `Tile count after filtering: ${tiles.length}`);
24128
24175
  const newActions = response.actions || [];
24129
24176
  const newActionsJson = JSON.stringify(newActions);
@@ -37150,6 +37197,55 @@ ${cssRules}
37150
37197
  return new PostHogAdapter(options);
37151
37198
  }
37152
37199
 
37200
+ // src/telemetry/InterventionTracker.ts
37201
+ var InterventionTracker = class {
37202
+ constructor(telemetry, variantId) {
37203
+ __publicField(this, "telemetry");
37204
+ __publicField(this, "variantId");
37205
+ __publicField(this, "seenSet", /* @__PURE__ */ new Set());
37206
+ __publicField(this, "triggeredSet", /* @__PURE__ */ new Set());
37207
+ this.telemetry = telemetry;
37208
+ this.variantId = variantId;
37209
+ }
37210
+ trackServed(tiles, actions) {
37211
+ this.telemetry.track?.("syntro_config_served", {
37212
+ variant_id: this.variantId,
37213
+ tiles,
37214
+ actions
37215
+ });
37216
+ }
37217
+ trackSeen(interventionId, interventionKind) {
37218
+ if (this.seenSet.has(interventionId)) return;
37219
+ this.seenSet.add(interventionId);
37220
+ this.telemetry.track?.("syntro_intervention_seen", {
37221
+ variant_id: this.variantId,
37222
+ intervention_id: interventionId,
37223
+ intervention_kind: interventionKind
37224
+ });
37225
+ }
37226
+ trackTriggered(interventionId, interventionKind) {
37227
+ if (this.triggeredSet.has(interventionId)) return;
37228
+ this.triggeredSet.add(interventionId);
37229
+ this.telemetry.track?.("syntro_intervention_triggered", {
37230
+ variant_id: this.variantId,
37231
+ intervention_id: interventionId,
37232
+ intervention_kind: interventionKind
37233
+ });
37234
+ }
37235
+ trackInteracted(interventionId, interventionKind, interactionType) {
37236
+ this.telemetry.track?.("syntro_intervention_interacted", {
37237
+ variant_id: this.variantId,
37238
+ intervention_id: interventionId,
37239
+ intervention_kind: interventionKind,
37240
+ interaction_type: interactionType
37241
+ });
37242
+ }
37243
+ resetPage() {
37244
+ this.seenSet.clear();
37245
+ this.triggeredSet.clear();
37246
+ }
37247
+ };
37248
+
37153
37249
  // src/actions/executors/core-flow.ts
37154
37250
  var executeSequence = async (action, context) => {
37155
37251
  const handles = [];
@@ -41906,6 +42002,26 @@ ${cssRules}
41906
42002
  const warnedAppFailures = /* @__PURE__ */ new Set();
41907
42003
  const appLoadingFetcher = baseFetcher ? async () => {
41908
42004
  const config = await baseFetcher();
42005
+ const tileCount = config.tiles?.length ?? 0;
42006
+ const actionCount = config.actions?.length ?? 0;
42007
+ const variantId = config.meta?.variant_id;
42008
+ if (tileCount > 0 || actionCount > 0) {
42009
+ if (!variantId) {
42010
+ console.warn(
42011
+ "[Syntro] Config has content but no meta.variant_id \u2014 intervention tracking disabled"
42012
+ );
42013
+ }
42014
+ }
42015
+ if (telemetry && variantId) {
42016
+ const tracker = new InterventionTracker(telemetry, variantId);
42017
+ tracker.trackServed(tileCount, actionCount);
42018
+ if (typeof window !== "undefined") {
42019
+ window.SynOS.interventionTracker = tracker;
42020
+ }
42021
+ runtime7.navigation.subscribe(() => {
42022
+ tracker.resetPage();
42023
+ });
42024
+ }
41909
42025
  console.log(
41910
42026
  "[Syntro Bootstrap] Config fetched:",
41911
42027
  `tiles=${config.tiles?.length ?? 0},`,
@@ -42005,7 +42121,7 @@ ${cssRules}
42005
42121
  }
42006
42122
 
42007
42123
  // src/index.ts
42008
- var RUNTIME_SDK_BUILD = true ? `${"2026-04-09T18:44:05.229Z"} (${"f5d1ea4ae30"})` : "dev";
42124
+ var RUNTIME_SDK_BUILD = true ? `${"2026-04-09T22:33:15.939Z"} (${"8ae8d320f1a"})` : "dev";
42009
42125
  if (typeof window !== "undefined") {
42010
42126
  console.log(`[Syntro Runtime] Build: ${RUNTIME_SDK_BUILD}`);
42011
42127
  const existing = window.SynOS;