@syntrologie/runtime-sdk 2.13.0 → 2.14.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.
@@ -20609,7 +20609,13 @@ Please report this to https://github.com/markedjs/marked.`, e2) {
20609
20609
  }
20610
20610
 
20611
20611
  // src/fetchers/mergeConfigs.ts
20612
- function resolveVariantConfigs(client, keys, _strategy = "first-match") {
20612
+ function resolveVariantConfigs(client, keys, strategy = "merge") {
20613
+ if (strategy === "first-match") {
20614
+ return resolveFirstMatch(client, keys);
20615
+ }
20616
+ return resolveMerge(client, keys);
20617
+ }
20618
+ function resolveFirstMatch(client, keys) {
20613
20619
  for (const key of keys) {
20614
20620
  const value = client.getFeatureValue?.(key, null);
20615
20621
  if (!value || typeof value !== "object") continue;
@@ -20617,20 +20623,59 @@ Please report this to https://github.com/markedjs/marked.`, e2) {
20617
20623
  const hasTiles = variant.tiles && variant.tiles.length > 0;
20618
20624
  const hasActions = variant.actions && variant.actions.length > 0;
20619
20625
  if (hasTiles || hasActions) {
20620
- return {
20621
- tiles: variant.tiles ?? [],
20622
- actions: variant.actions ?? [],
20623
- fetchedAt: variant.fetchedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
20624
- ...variant.schemaVersion && { schemaVersion: variant.schemaVersion },
20625
- ...variant.configVersion && { configVersion: variant.configVersion },
20626
- ...variant.canvasTitle && { canvasTitle: variant.canvasTitle },
20627
- ...variant.theme && { theme: variant.theme },
20628
- ...variant.launcher && { launcher: variant.launcher }
20629
- };
20626
+ return buildConfig([variant]);
20630
20627
  }
20631
20628
  }
20632
20629
  return null;
20633
20630
  }
20631
+ function resolveMerge(client, keys) {
20632
+ const variants = [];
20633
+ for (const key of keys) {
20634
+ const value = client.getFeatureValue?.(key, null);
20635
+ if (!value || typeof value !== "object") continue;
20636
+ const variant = value;
20637
+ const hasTiles = variant.tiles && variant.tiles.length > 0;
20638
+ const hasActions = variant.actions && variant.actions.length > 0;
20639
+ if (hasTiles || hasActions) {
20640
+ variants.push(variant);
20641
+ }
20642
+ }
20643
+ if (variants.length === 0) return null;
20644
+ return buildConfig(variants);
20645
+ }
20646
+ function buildConfig(variants) {
20647
+ const allTiles = [];
20648
+ const allActions = [];
20649
+ let fetchedAt;
20650
+ let schemaVersion;
20651
+ let configVersion;
20652
+ let canvasTitle;
20653
+ let theme;
20654
+ let launcher;
20655
+ let meta;
20656
+ for (const variant of variants) {
20657
+ if (variant.tiles) allTiles.push(...variant.tiles);
20658
+ if (variant.actions) allActions.push(...variant.actions);
20659
+ fetchedAt ?? (fetchedAt = variant.fetchedAt);
20660
+ schemaVersion ?? (schemaVersion = variant.schemaVersion);
20661
+ configVersion ?? (configVersion = variant.configVersion);
20662
+ canvasTitle ?? (canvasTitle = variant.canvasTitle);
20663
+ theme ?? (theme = variant.theme);
20664
+ launcher ?? (launcher = variant.launcher);
20665
+ meta ?? (meta = variant.meta);
20666
+ }
20667
+ return {
20668
+ tiles: allTiles,
20669
+ actions: allActions,
20670
+ fetchedAt: fetchedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
20671
+ ...schemaVersion && { schemaVersion },
20672
+ ...configVersion && { configVersion },
20673
+ ...canvasTitle && { canvasTitle },
20674
+ ...theme && { theme },
20675
+ ...launcher && { launcher },
20676
+ ...meta && { meta }
20677
+ };
20678
+ }
20634
20679
 
20635
20680
  // src/logger.ts
20636
20681
  var debugEnabled = false;
@@ -20678,7 +20723,7 @@ Please report this to https://github.com/markedjs/marked.`, e2) {
20678
20723
  }
20679
20724
 
20680
20725
  // src/version.ts
20681
- var SDK_VERSION = "2.13.0";
20726
+ var SDK_VERSION = "2.14.0";
20682
20727
 
20683
20728
  // src/types.ts
20684
20729
  var SDK_SCHEMA_VERSION = "2.0";
@@ -21845,6 +21890,36 @@ Please report this to https://github.com/markedjs/marked.`, e2) {
21845
21890
  return eventName.replace("$", "posthog.");
21846
21891
  }
21847
21892
  var INTERACTIVE_TAGS = /* @__PURE__ */ new Set(["a", "button", "input", "select", "textarea"]);
21893
+ function parseElementsChain(chain) {
21894
+ if (!chain)
21895
+ return void 0;
21896
+ return chain.split(";").map((segment) => {
21897
+ const el = {};
21898
+ const colonIdx = segment.indexOf(":");
21899
+ const tagPart = colonIdx >= 0 ? segment.slice(0, colonIdx) : segment;
21900
+ const attrPart = colonIdx >= 0 ? segment.slice(colonIdx + 1) : "";
21901
+ const dotIdx = tagPart.indexOf(".");
21902
+ if (dotIdx >= 0) {
21903
+ el.tag_name = tagPart.slice(0, dotIdx);
21904
+ el.attr__class = tagPart.slice(dotIdx + 1).replace(/\./g, " ");
21905
+ } else {
21906
+ el.tag_name = tagPart;
21907
+ }
21908
+ const attrRegex = /([\w$]+)="([^"]*)"/g;
21909
+ let match;
21910
+ while ((match = attrRegex.exec(attrPart)) !== null) {
21911
+ const [, key, value] = match;
21912
+ if (key === "nth-child" || key === "nth-of-type")
21913
+ continue;
21914
+ el[key] = value;
21915
+ }
21916
+ if (el.text) {
21917
+ el.$el_text = el.text;
21918
+ delete el.text;
21919
+ }
21920
+ return el;
21921
+ });
21922
+ }
21848
21923
  function resolveInteractiveTag(elements2, directTag) {
21849
21924
  if (directTag && INTERACTIVE_TAGS.has(directTag))
21850
21925
  return directTag;
@@ -21860,7 +21935,7 @@ Please report this to https://github.com/markedjs/marked.`, e2) {
21860
21935
  function extractProps(phEvent) {
21861
21936
  const props = {};
21862
21937
  const phProps = phEvent.properties || {};
21863
- const elements2 = phProps.$elements;
21938
+ const elements2 = phProps.$elements ?? (typeof phProps.$elements_chain === "string" ? parseElementsChain(phProps.$elements_chain) : void 0);
21864
21939
  const directTag = phProps.$tag_name ?? elements2?.[0]?.tag_name;
21865
21940
  const isClickEvent = phEvent.event === "$autocapture" || phEvent.event === "$click";
21866
21941
  props.tagName = isClickEvent ? resolveInteractiveTag(elements2, directTag) : directTag;
@@ -21868,6 +21943,9 @@ Please report this to https://github.com/markedjs/marked.`, e2) {
21868
21943
  props.elementText = phProps.$el_text;
21869
21944
  if (elements2)
21870
21945
  props.elements = elements2;
21946
+ if (isClickEvent && !elements2) {
21947
+ console.warn(`[PostHogNormalizer] $autocapture click has no element chain. PostHog may have changed wire format. Properties: $elements=${!!phProps.$elements}, $elements_chain=${typeof phProps.$elements_chain}`);
21948
+ }
21871
21949
  if (phProps.$current_url)
21872
21950
  props.url = phProps.$current_url;
21873
21951
  if (phProps.$pathname)
@@ -23622,7 +23700,6 @@ ${cssRules}
23622
23700
  onToggle,
23623
23701
  telemetry,
23624
23702
  launcherLabel: _launcherLabel = "Adaptives",
23625
- launcherIcon,
23626
23703
  launcherAnimate = false,
23627
23704
  launcherAnimationStyle: _launcherAnimationStyle = "pulse",
23628
23705
  notificationCount: _notificationCount,
@@ -24027,10 +24104,10 @@ ${cssRules}
24027
24104
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M6 6l12 12" })
24028
24105
  ]
24029
24106
  }
24030
- ) : launcherIcon ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
24107
+ ) : config.launcher?.icon ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
24031
24108
  "img",
24032
24109
  {
24033
- src: launcherIcon,
24110
+ src: config.launcher.icon,
24034
24111
  alt: "",
24035
24112
  "aria-hidden": "true",
24036
24113
  style: {
@@ -24375,6 +24452,7 @@ ${cssRules}
24375
24452
  const batchHandleRef = (0, import_react16.useRef)(initialBatchHandle ?? null);
24376
24453
  const adoptedInitialRef = (0, import_react16.useRef)(!!initialBatchHandle);
24377
24454
  const runVersionRef = (0, import_react16.useRef)(0);
24455
+ const pendingRevertRef = (0, import_react16.useRef)(null);
24378
24456
  (0, import_react16.useEffect)(() => {
24379
24457
  if (!runtime7?.actions) return;
24380
24458
  if (adoptedInitialRef.current) {
@@ -24386,6 +24464,10 @@ ${cssRules}
24386
24464
  const version = ++runVersionRef.current;
24387
24465
  const stale = () => version !== runVersionRef.current;
24388
24466
  const run = async () => {
24467
+ if (pendingRevertRef.current) {
24468
+ await pendingRevertRef.current;
24469
+ pendingRevertRef.current = null;
24470
+ }
24389
24471
  if (batchHandleRef.current) {
24390
24472
  try {
24391
24473
  await batchHandleRef.current.revertAll();
@@ -24413,7 +24495,7 @@ ${cssRules}
24413
24495
  run();
24414
24496
  return () => {
24415
24497
  if (batchHandleRef.current) {
24416
- batchHandleRef.current.revertAll().catch((err) => {
24498
+ pendingRevertRef.current = batchHandleRef.current.revertAll().catch((err) => {
24417
24499
  console.error("[SmartCanvasApp] Failed to revert actions on cleanup:", err);
24418
24500
  });
24419
24501
  batchHandleRef.current = null;
@@ -24444,7 +24526,6 @@ ${cssRules}
24444
24526
  canvasTitle: configState.canvasTitle,
24445
24527
  telemetry,
24446
24528
  launcherLabel: launcherLabel ?? configState.launcher?.label,
24447
- launcherIcon: configState.launcher?.icon,
24448
24529
  launcherAnimate: configState.launcher?.animate,
24449
24530
  launcherAnimationStyle: configState.launcher?.animationStyle,
24450
24531
  notificationCount: configState.launcher?.notificationCount ?? configState.tiles.length,
@@ -37038,32 +37119,7 @@ ${cssRules}
37038
37119
  // Capture performance metrics
37039
37120
  capture_performance: true,
37040
37121
  // Enable web vitals
37041
- enable_recording_console_log: true,
37042
- // Bootstrap callback for when flags are loaded
37043
- loaded: (ph) => {
37044
- if (enableFeatureFlags && this.featureFlagsCallback) {
37045
- ph.onFeatureFlags(() => {
37046
- const allFlags = this.getAllFeatureFlags();
37047
- if (allFlags && this.featureFlagsCallback) {
37048
- this.featureFlagsCallback(allFlags);
37049
- }
37050
- });
37051
- const existingFlags = this.getAllFeatureFlags();
37052
- if (existingFlags && Object.keys(existingFlags).length > 0) {
37053
- this.featureFlagsCallback(existingFlags);
37054
- }
37055
- }
37056
- if (this.captureCallback) {
37057
- ph.on("eventCaptured", (...args) => {
37058
- const data = args[0];
37059
- const eventName = typeof data === "string" ? data : data?.event;
37060
- const properties = typeof data === "string" ? void 0 : data?.properties;
37061
- if (typeof eventName === "string") {
37062
- this.captureCallback?.(eventName, properties);
37063
- }
37064
- });
37065
- }
37066
- }
37122
+ enable_recording_console_log: true
37067
37123
  };
37068
37124
  const result = Jo.init(
37069
37125
  options.apiKey,
@@ -37073,6 +37129,28 @@ ${cssRules}
37073
37129
  if (result) {
37074
37130
  this.client = result;
37075
37131
  }
37132
+ if (this.captureCallback && this.client) {
37133
+ this.client.on("eventCaptured", (...args) => {
37134
+ const data = args[0];
37135
+ const eventName = typeof data === "string" ? data : data?.event;
37136
+ const properties = typeof data === "string" ? void 0 : data?.properties;
37137
+ if (typeof eventName === "string") {
37138
+ this.captureCallback?.(eventName, properties);
37139
+ }
37140
+ });
37141
+ }
37142
+ if (enableFeatureFlags && this.featureFlagsCallback && this.client) {
37143
+ this.client.onFeatureFlags(() => {
37144
+ const allFlags = this.getAllFeatureFlags();
37145
+ if (allFlags && this.featureFlagsCallback) {
37146
+ this.featureFlagsCallback(allFlags);
37147
+ }
37148
+ });
37149
+ const existingFlags = this.getAllFeatureFlags();
37150
+ if (existingFlags && Object.keys(existingFlags).length > 0) {
37151
+ this.featureFlagsCallback(existingFlags);
37152
+ }
37153
+ }
37076
37154
  if (this.rrwebCallback && this.client) {
37077
37155
  this.setupRRWebIntercept();
37078
37156
  }
@@ -38394,15 +38472,30 @@ ${cssRules}
38394
38472
  const handles = [];
38395
38473
  const appliedHandles = [];
38396
38474
  try {
38397
- for (const action of actions) {
38398
- const handle = await apply(action);
38399
- handles.push(handle);
38400
- appliedHandles.push(handle);
38475
+ const results = await Promise.allSettled(actions.map((action) => apply(action)));
38476
+ const errors = [];
38477
+ for (const result of results) {
38478
+ if (result.status === "fulfilled") {
38479
+ handles.push(result.value);
38480
+ appliedHandles.push(result.value);
38481
+ } else {
38482
+ errors.push(
38483
+ result.reason instanceof Error ? result.reason : new Error(String(result.reason))
38484
+ );
38485
+ }
38486
+ }
38487
+ if (errors.length > 0 && appliedHandles.length === 0) {
38488
+ throw errors[0];
38489
+ }
38490
+ if (errors.length > 0) {
38491
+ console.warn(
38492
+ `[ActionEngine] ${errors.length}/${actions.length} action(s) failed in batch:`,
38493
+ errors.map((e2) => e2.message).join("; ")
38494
+ );
38401
38495
  }
38402
38496
  } catch (error2) {
38403
38497
  console.error(
38404
- `[ActionEngine] Batch apply FAILED at action ${appliedHandles.length + 1}/${actions.length}.`,
38405
- `Successfully applied: ${appliedHandles.length}. Rolling back...`,
38498
+ `[ActionEngine] Batch apply FAILED. Successfully applied: ${appliedHandles.length}. Rolling back...`,
38406
38499
  error2
38407
38500
  );
38408
38501
  for (const handle of appliedHandles) {
@@ -38478,74 +38571,97 @@ ${cssRules}
38478
38571
  selector: external_exports.string(),
38479
38572
  route: external_exports.union([external_exports.string(), external_exports.array(external_exports.string())])
38480
38573
  }).strict();
38574
+ var COUNTABLE_EVENTS = [
38575
+ // User interactions (from PostHog autocapture normalization)
38576
+ "ui.click",
38577
+ "ui.scroll",
38578
+ "ui.input",
38579
+ "ui.change",
38580
+ "ui.submit",
38581
+ // Behavioral detectors (from event-processor)
38582
+ "ui.hover",
38583
+ "ui.idle",
38584
+ "ui.scroll_thrash",
38585
+ "ui.focus_bounce",
38586
+ // Navigation
38587
+ "nav.page_view",
38588
+ "nav.page_leave",
38589
+ // Derived behavioral signals
38590
+ "behavior.rage_click",
38591
+ "behavior.hesitation",
38592
+ "behavior.confusion"
38593
+ ];
38594
+ var CountableEventZ = external_exports.enum(COUNTABLE_EVENTS).describe("Event name to count. ui.* = user interactions and behavioral detectors, nav.* = page navigation, behavior.* = derived behavioral signals.");
38595
+ var SESSION_METRIC_KEYS = ["time_on_page", "page_views", "scroll_depth"];
38596
+ var SessionMetricKeyZ = external_exports.enum(SESSION_METRIC_KEYS).describe("Session metric key. time_on_page = seconds on current page, page_views = pages visited this session, scroll_depth = 0-100 percentage.");
38481
38597
  var PageUrlConditionZ = external_exports.object({
38482
38598
  type: external_exports.literal("page_url"),
38483
- url: external_exports.string()
38484
- });
38599
+ url: external_exports.string().describe('URL path to match (e.g. "/pricing", "/dashboard")')
38600
+ }).describe('Fires when the current page URL matches. Use for page-specific actions. Example: {"type": "page_url", "url": "/pricing"}');
38485
38601
  var RouteConditionZ = external_exports.object({
38486
38602
  type: external_exports.literal("route"),
38487
- routeId: external_exports.string()
38488
- });
38603
+ routeId: external_exports.string().describe("Named route ID from the route filter")
38604
+ }).describe("Fires when the current route matches a named route ID.");
38489
38605
  var AnchorVisibleConditionZ = external_exports.object({
38490
38606
  type: external_exports.literal("anchor_visible"),
38491
- anchorId: external_exports.string(),
38492
- state: external_exports.enum(["visible", "present", "absent"])
38493
- });
38607
+ anchorId: external_exports.string().describe("CSS selector of the anchor element"),
38608
+ state: external_exports.enum(["visible", "present", "absent"]).describe('"visible" = in viewport, "present" = in DOM, "absent" = not in DOM')
38609
+ }).describe(`Fires based on a DOM element's visibility state. Example: {"type": "anchor_visible", "anchorId": "#cta-button", "state": "visible"}`);
38494
38610
  var EventOccurredConditionZ = external_exports.object({
38495
38611
  type: external_exports.literal("event_occurred"),
38496
- eventName: external_exports.string(),
38497
- withinMs: external_exports.number().optional()
38498
- });
38612
+ eventName: external_exports.string().describe('Event name (e.g. "ui.click", "$pageview")'),
38613
+ withinMs: external_exports.number().optional().describe("Time window in ms. Omit = any time this session.")
38614
+ }).describe('Fires when a specific event has occurred during this session. Example: {"type": "event_occurred", "eventName": "ui.click", "withinMs": 5000}');
38499
38615
  var StateEqualsConditionZ = external_exports.object({
38500
38616
  type: external_exports.literal("state_equals"),
38501
- key: external_exports.string(),
38502
- value: external_exports.unknown()
38503
- });
38617
+ key: external_exports.string().describe("Key in the SDK persistent state store (localStorage). Only valid for keys the host app explicitly sets via syntro.state.set()."),
38618
+ value: external_exports.unknown().describe("Expected value to match against")
38619
+ }).describe("Checks the SDK persistent state store (localStorage). ONLY for host-app state set via syntro.state.set() \u2014 NOT for user attributes like region, device, or UTM params (those are handled by segment targeting). Do NOT use this for targeting. If you do not know the valid state keys, do not use this condition type.");
38504
38620
  var ViewportConditionZ = external_exports.object({
38505
38621
  type: external_exports.literal("viewport"),
38506
- minWidth: external_exports.number().optional(),
38507
- maxWidth: external_exports.number().optional(),
38508
- minHeight: external_exports.number().optional(),
38509
- maxHeight: external_exports.number().optional()
38510
- });
38622
+ minWidth: external_exports.number().optional().describe("Minimum viewport width in pixels"),
38623
+ maxWidth: external_exports.number().optional().describe("Maximum viewport width in pixels"),
38624
+ minHeight: external_exports.number().optional().describe("Minimum viewport height in pixels"),
38625
+ maxHeight: external_exports.number().optional().describe("Maximum viewport height in pixels")
38626
+ }).describe('Fires based on viewport (screen) size. Use for responsive behavior. Example: {"type": "viewport", "minWidth": 768} \u2014 fires on tablet and larger.');
38511
38627
  var SessionMetricConditionZ = external_exports.object({
38512
38628
  type: external_exports.literal("session_metric"),
38513
- key: external_exports.string(),
38629
+ key: SessionMetricKeyZ,
38514
38630
  operator: external_exports.enum(["gte", "lte", "eq", "gt", "lt"]),
38515
- threshold: external_exports.number()
38516
- });
38631
+ threshold: external_exports.number().describe("Numeric threshold to compare against")
38632
+ }).describe('Fires when a session metric crosses a threshold. Valid keys: "time_on_page" (seconds), "page_views" (count), "scroll_depth" (0-100). Example: {"type": "session_metric", "key": "time_on_page", "operator": "gte", "threshold": 30}');
38517
38633
  var DismissedConditionZ = external_exports.object({
38518
38634
  type: external_exports.literal("dismissed"),
38519
- key: external_exports.string(),
38520
- inverted: external_exports.boolean().optional()
38521
- });
38635
+ key: external_exports.string().describe("Dismissal key (usually a tile or action ID)"),
38636
+ inverted: external_exports.boolean().optional().describe("When true, fires if NOT dismissed (default behavior)")
38637
+ }).describe("Checks if an item has been dismissed by the user. Use with inverted: true to show only if not dismissed.");
38522
38638
  var CooldownActiveConditionZ = external_exports.object({
38523
38639
  type: external_exports.literal("cooldown_active"),
38524
- key: external_exports.string(),
38525
- inverted: external_exports.boolean().optional()
38526
- });
38640
+ key: external_exports.string().describe("Cooldown key"),
38641
+ inverted: external_exports.boolean().optional().describe("When true, fires if cooldown is NOT active")
38642
+ }).describe("Checks if a cooldown timer is currently active. Use to prevent showing the same intervention too frequently.");
38527
38643
  var FrequencyLimitConditionZ = external_exports.object({
38528
38644
  type: external_exports.literal("frequency_limit"),
38529
- key: external_exports.string(),
38530
- limit: external_exports.number(),
38531
- inverted: external_exports.boolean().optional()
38532
- });
38645
+ key: external_exports.string().describe("Frequency counter key"),
38646
+ limit: external_exports.number().describe("Maximum allowed count"),
38647
+ inverted: external_exports.boolean().optional().describe("When true, fires if limit NOT reached")
38648
+ }).describe("Checks if a frequency limit has been reached. Use to cap how many times an action fires per session.");
38533
38649
  var MatchOpZ = external_exports.object({
38534
38650
  equals: external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()]).optional(),
38535
38651
  contains: external_exports.string().optional()
38536
- });
38652
+ }).describe("Match operator for counter filters. Exactly one of equals or contains must be specified.");
38537
38653
  var CounterDefZ = external_exports.object({
38538
- events: external_exports.array(external_exports.string()).min(1),
38539
- match: external_exports.record(external_exports.string(), MatchOpZ).optional()
38540
- });
38654
+ events: external_exports.array(CountableEventZ).min(1).describe("Event names to count. Use values from the countable events enum."),
38655
+ match: external_exports.record(external_exports.string(), MatchOpZ).optional().describe("Property filters. Keys are event prop names or element-chain fields (tag_name, $el_text, attr__*). All entries AND together.")
38656
+ }).describe("Defines what events to count. Registered as an accumulator predicate at config-load time.");
38541
38657
  var EventCountConditionZ = external_exports.object({
38542
38658
  type: external_exports.literal("event_count"),
38543
- key: external_exports.string(),
38659
+ key: external_exports.string().describe("Unique key for this counter (used for accumulator registration)"),
38544
38660
  operator: external_exports.enum(["gte", "lte", "eq", "gt", "lt"]),
38545
- count: external_exports.number().int().min(0),
38546
- withinMs: external_exports.number().positive().optional(),
38547
- counter: CounterDefZ.optional()
38548
- });
38661
+ count: external_exports.number().int().min(0).describe("Target count threshold"),
38662
+ withinMs: external_exports.number().positive().optional().describe("Time window in ms. Omit = count across entire session."),
38663
+ counter: CounterDefZ.optional().describe("Inline counter definition. Defines what events to count.")
38664
+ }).describe('Fires when accumulated event count crosses a threshold. Most powerful trigger type. Example: {"type": "event_count", "key": "pricing-clicks", "operator": "gte", "count": 3, "counter": {"events": ["ui.click"], "match": {"attr__data-cta": {"contains": "pricing"}}}}');
38549
38665
  var ConditionZ = external_exports.discriminatedUnion("type", [
38550
38666
  PageUrlConditionZ,
38551
38667
  RouteConditionZ,
@@ -38560,35 +38676,35 @@ ${cssRules}
38560
38676
  EventCountConditionZ
38561
38677
  ]);
38562
38678
  var RuleZ = external_exports.object({
38563
- conditions: external_exports.array(ConditionZ),
38564
- value: external_exports.unknown()
38565
- });
38679
+ conditions: external_exports.array(ConditionZ).describe("Array of conditions \u2014 ALL must match (AND logic) for this rule to fire."),
38680
+ value: external_exports.unknown().describe("Value returned when all conditions match. For triggerWhen: true = fire the action.")
38681
+ }).describe("A single rule. ALL conditions must match (AND logic). Rules in a strategy are evaluated top-to-bottom \u2014 first rule where all conditions match wins and returns its value.");
38566
38682
  var RuleStrategyZ = external_exports.object({
38567
38683
  type: external_exports.literal("rules"),
38568
- rules: external_exports.array(RuleZ),
38569
- default: external_exports.unknown()
38570
- });
38684
+ rules: external_exports.array(RuleZ).describe("Ordered list of rules. Evaluated top-to-bottom \u2014 first match wins."),
38685
+ default: external_exports.unknown().describe("Fallback value when no rule matches. For triggerWhen: false = do not fire by default.")
38686
+ }).describe("Rule-based strategy. Evaluates rules top-to-bottom. First rule where ALL conditions match returns its value. If no rule matches, returns default. For triggerWhen: set value=true on matching rules, default=false.");
38571
38687
  var ScoreStrategyZ = external_exports.object({
38572
38688
  type: external_exports.literal("score"),
38573
38689
  field: external_exports.string(),
38574
38690
  threshold: external_exports.number(),
38575
38691
  above: external_exports.unknown(),
38576
38692
  below: external_exports.unknown()
38577
- });
38693
+ }).describe("Score-based strategy. Compares a field value against a threshold.");
38578
38694
  var ModelStrategyZ = external_exports.object({
38579
38695
  type: external_exports.literal("model"),
38580
38696
  modelId: external_exports.string(),
38581
38697
  inputs: external_exports.array(external_exports.string()),
38582
38698
  outputMapping: external_exports.record(external_exports.string(), external_exports.unknown()),
38583
38699
  default: external_exports.unknown()
38584
- });
38700
+ }).describe("ML model strategy. Sends inputs to a model and maps outputs.");
38585
38701
  var ExternalStrategyZ = external_exports.object({
38586
38702
  type: external_exports.literal("external"),
38587
38703
  endpoint: external_exports.string(),
38588
38704
  method: external_exports.enum(["GET", "POST"]).optional(),
38589
38705
  default: external_exports.unknown(),
38590
38706
  timeoutMs: external_exports.number().optional()
38591
- });
38707
+ }).describe("External API strategy. Calls an endpoint to determine the value.");
38592
38708
  var DecisionStrategyZ = external_exports.discriminatedUnion("type", [
38593
38709
  RuleStrategyZ,
38594
38710
  ScoreStrategyZ,
@@ -38684,7 +38800,8 @@ ${cssRules}
38684
38800
  color: external_exports.string().optional(),
38685
38801
  size: external_exports.string().optional(),
38686
38802
  shadow: external_exports.string().optional(),
38687
- borderRadius: external_exports.string().optional()
38803
+ borderRadius: external_exports.string().optional(),
38804
+ icon: external_exports.string().optional()
38688
38805
  });
38689
38806
  var TileElementConfigZ = external_exports.object({
38690
38807
  background: external_exports.string().optional(),
@@ -38764,7 +38881,6 @@ ${cssRules}
38764
38881
  var LauncherConfigZ = external_exports.object({
38765
38882
  enabled: external_exports.boolean().optional(),
38766
38883
  label: external_exports.string().optional(),
38767
- icon: external_exports.string().optional(),
38768
38884
  position: external_exports.string().optional(),
38769
38885
  animate: external_exports.boolean().optional(),
38770
38886
  animationStyle: external_exports.enum(["pulse", "bounce", "glow"]).optional(),
@@ -41585,7 +41701,7 @@ ${cssRules}
41585
41701
  __publicField(this, "manifestKey");
41586
41702
  __publicField(this, "variantFlagPrefix");
41587
41703
  this.client = options.client;
41588
- this.featureKey = options.featureKey ?? "smart-canvas-config";
41704
+ this.featureKey = options.featureKey;
41589
41705
  this.manifestKey = options.manifestKey;
41590
41706
  this.variantFlagPrefix = options.variantFlagPrefix;
41591
41707
  }
@@ -42121,7 +42237,7 @@ ${cssRules}
42121
42237
  }
42122
42238
 
42123
42239
  // src/index.ts
42124
- var RUNTIME_SDK_BUILD = true ? `${"2026-04-09T22:33:15.939Z"} (${"8ae8d320f1a"})` : "dev";
42240
+ var RUNTIME_SDK_BUILD = true ? `${"2026-04-15T20:50:41.408Z"} (${"acd804e29d3"})` : "dev";
42125
42241
  if (typeof window !== "undefined") {
42126
42242
  console.log(`[Syntro Runtime] Build: ${RUNTIME_SDK_BUILD}`);
42127
42243
  const existing = window.SynOS;