@syntrologie/runtime-sdk 2.10.0 → 2.11.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.
@@ -31,9 +31,10 @@ import {
31
31
  WaitZ,
32
32
  WidgetConfigZ,
33
33
  coreActionStepSchemas
34
- } from "../chunk-R5DNAIRI.js";
35
- import "../chunk-XDYJ64IN.js";
36
- import "../chunk-BU4Z6PD7.js";
34
+ } from "../chunk-NBFQGKSV.js";
35
+ import "../chunk-37TTQRH5.js";
36
+ import "../chunk-Q77NT67W.js";
37
+ import "../chunk-JMHRHAEL.js";
37
38
  export {
38
39
  AddClassZ,
39
40
  AnchorIdZ,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ActivationConfigZ,
3
3
  ConditionZ
4
- } from "./chunk-BU4Z6PD7.js";
4
+ } from "./chunk-Q77NT67W.js";
5
5
 
6
6
  // src/config/schema.ts
7
7
  import { z } from "zod";
@@ -175,4 +175,4 @@ export {
175
175
  CanvasConfigResponseZ,
176
176
  configSchemas
177
177
  };
178
- //# sourceMappingURL=chunk-XDYJ64IN.js.map
178
+ //# sourceMappingURL=chunk-37TTQRH5.js.map
@@ -3,7 +3,7 @@ import {
3
3
  __privateGet,
4
4
  __privateSet,
5
5
  __publicField
6
- } from "./chunk-BU4Z6PD7.js";
6
+ } from "./chunk-JMHRHAEL.js";
7
7
 
8
8
  // ../adaptives/adaptive-content/dist/reconciliation-guard.js
9
9
  function guardAgainstReconciliation(container, anchor, reinsertFn, opts) {
@@ -3435,7 +3435,7 @@ function getAntiFlickerSnippet(config = {}) {
3435
3435
  }
3436
3436
 
3437
3437
  // src/version.ts
3438
- var SDK_VERSION = "2.10.0";
3438
+ var SDK_VERSION = "2.11.0";
3439
3439
 
3440
3440
  // src/types.ts
3441
3441
  var SDK_SCHEMA_VERSION = "2.0";
@@ -3813,6 +3813,7 @@ function useShadowRoot() {
3813
3813
  }
3814
3814
 
3815
3815
  // src/events/types.ts
3816
+ import { EVENT_SCHEMA_VERSION } from "@syntrologie/event-processor";
3816
3817
  var StandardEvents = {
3817
3818
  // UI events (from PostHog autocapture)
3818
3819
  UI_CLICK: "ui.click",
@@ -3853,7 +3854,6 @@ var StandardEvents = {
3853
3854
  SURFACE_MOUNTED: "surface.mounted",
3854
3855
  SURFACE_UNMOUNTED: "surface.unmounted"
3855
3856
  };
3856
- var EVENT_SCHEMA_VERSION = "1.0.0";
3857
3857
 
3858
3858
  // src/events/normalizers/canvas.ts
3859
3859
  function createCanvasEvent(name, props) {
@@ -7071,10 +7071,12 @@ var PostHogAdapter = class {
7071
7071
  __publicField(this, "client");
7072
7072
  __publicField(this, "featureFlagsCallback");
7073
7073
  __publicField(this, "captureCallback");
7074
+ __publicField(this, "rrwebCallback");
7074
7075
  __publicField(this, "consentUnsub");
7075
7076
  this.client = options.client;
7076
7077
  this.featureFlagsCallback = options.onFeatureFlagsLoaded;
7077
7078
  this.captureCallback = options.onCapture;
7079
+ this.rrwebCallback = options.onRRWebEvent;
7078
7080
  if (!this.client && options.consent && options.requireExplicitConsent && typeof window !== "undefined" && options.apiKey) {
7079
7081
  const consent = options.consent;
7080
7082
  const currentStatus = consent.getStatus();
@@ -7161,6 +7163,62 @@ var PostHogAdapter = class {
7161
7163
  },
7162
7164
  instanceName
7163
7165
  );
7166
+ if (this.rrwebCallback && this.client) {
7167
+ this.setupRRWebIntercept();
7168
+ }
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
+ }
7179
+ /**
7180
+ * Set up rrweb event interception on PostHog's session recording.
7181
+ *
7182
+ * PostHog lazy-loads the rrweb recorder. The SessionRecording wrapper has
7183
+ * an `onRRwebEmit` method, but rrweb delivers events directly to the
7184
+ * lazy-loaded recorder instance's `onRRwebEmit`, bypassing the wrapper.
7185
+ * We must find and patch the recorder instance, not the wrapper.
7186
+ *
7187
+ * The recorder instance is stored on a minified property of SessionRecording.
7188
+ * We detect it by looking for an object with both `onRRwebEmit` and `start` methods.
7189
+ */
7190
+ setupRRWebIntercept(retries = 30) {
7191
+ var _a2;
7192
+ const sr = (_a2 = this.client) == null ? void 0 : _a2.sessionRecording;
7193
+ if (!sr) {
7194
+ if (retries > 0) {
7195
+ setTimeout(() => this.setupRRWebIntercept(retries - 1), 500);
7196
+ }
7197
+ return;
7198
+ }
7199
+ let recorder = null;
7200
+ for (const key of Object.getOwnPropertyNames(sr)) {
7201
+ const val = sr[key];
7202
+ if (val && typeof val === "object" && typeof val.onRRwebEmit === "function" && typeof val.start === "function" && val !== sr) {
7203
+ recorder = val;
7204
+ break;
7205
+ }
7206
+ }
7207
+ if (!recorder) {
7208
+ if (retries > 0) {
7209
+ setTimeout(() => this.setupRRWebIntercept(retries - 1), 500);
7210
+ }
7211
+ return;
7212
+ }
7213
+ const originalEmit = recorder.onRRwebEmit.bind(recorder);
7214
+ recorder.onRRwebEmit = (rawEvent) => {
7215
+ var _a3;
7216
+ (_a3 = this.rrwebCallback) == null ? void 0 : _a3.call(this, { kind: "rrweb", ...rawEvent });
7217
+ originalEmit(rawEvent);
7218
+ };
7219
+ if (typeof window !== "undefined") {
7220
+ window.__RRWEB_INTERCEPT_READY__ = true;
7221
+ }
7164
7222
  }
7165
7223
  /**
7166
7224
  * Get all feature flags from PostHog.
@@ -9309,133 +9367,6 @@ function createEventBus(options = {}) {
9309
9367
  return new EventBus(options);
9310
9368
  }
9311
9369
 
9312
- // src/events/normalizers/posthog.ts
9313
- var POSTHOG_EVENT_MAP = {
9314
- // NOTE: $autocapture is intentionally NOT in this map.
9315
- // It's handled below in getEventName() with $event_type refinement
9316
- // so that change/submit events aren't all mapped to ui.click.
9317
- $click: StandardEvents.UI_CLICK,
9318
- $scroll: StandardEvents.UI_SCROLL,
9319
- $input: StandardEvents.UI_INPUT,
9320
- $change: StandardEvents.UI_CHANGE,
9321
- $submit: StandardEvents.UI_SUBMIT,
9322
- // Navigation events
9323
- $pageview: StandardEvents.NAV_PAGE_VIEW,
9324
- $pageleave: StandardEvents.NAV_PAGE_LEAVE,
9325
- // Session events
9326
- $session_start: "session.start",
9327
- // Identify events
9328
- $identify: "user.identify"
9329
- };
9330
- function getEventName(phEvent) {
9331
- var _a2, _b;
9332
- const eventName = phEvent.event;
9333
- if (typeof eventName !== "string") {
9334
- return "posthog.unknown";
9335
- }
9336
- if (POSTHOG_EVENT_MAP[eventName]) {
9337
- return POSTHOG_EVENT_MAP[eventName];
9338
- }
9339
- if (eventName === "$autocapture") {
9340
- const tagName = (_a2 = phEvent.properties) == null ? void 0 : _a2.$tag_name;
9341
- const eventType = (_b = phEvent.properties) == null ? void 0 : _b.$event_type;
9342
- if (eventType === "submit") return StandardEvents.UI_SUBMIT;
9343
- if (eventType === "change") return StandardEvents.UI_CHANGE;
9344
- if (tagName === "input" || tagName === "textarea") return StandardEvents.UI_INPUT;
9345
- return StandardEvents.UI_CLICK;
9346
- }
9347
- if (!eventName.startsWith("$")) {
9348
- return `posthog.${eventName}`;
9349
- }
9350
- return eventName.replace("$", "posthog.");
9351
- }
9352
- var INTERACTIVE_TAGS = /* @__PURE__ */ new Set(["a", "button", "input", "select", "textarea"]);
9353
- function resolveInteractiveTag(elements, directTag) {
9354
- if (directTag && INTERACTIVE_TAGS.has(directTag)) return directTag;
9355
- if (!elements) return directTag;
9356
- for (const el of elements) {
9357
- const tag2 = el.tag_name;
9358
- if (tag2 && INTERACTIVE_TAGS.has(tag2)) return tag2;
9359
- }
9360
- return directTag;
9361
- }
9362
- function extractProps(phEvent) {
9363
- var _a2, _b;
9364
- const props = {};
9365
- const phProps = phEvent.properties || {};
9366
- const elements = phProps.$elements;
9367
- const directTag = (_b = phProps.$tag_name) != null ? _b : (_a2 = elements == null ? void 0 : elements[0]) == null ? void 0 : _a2.tag_name;
9368
- const isClickEvent = phEvent.event === "$autocapture" || phEvent.event === "$click";
9369
- props.tagName = isClickEvent ? resolveInteractiveTag(elements, directTag) : directTag;
9370
- if (phProps.$el_text) props.elementText = phProps.$el_text;
9371
- if (elements) props.elements = elements;
9372
- if (phProps.$current_url) props.url = phProps.$current_url;
9373
- if (phProps.$pathname) props.pathname = phProps.$pathname;
9374
- if (phProps.$host) props.host = phProps.$host;
9375
- if (phProps.$viewport_width) props.viewportWidth = phProps.$viewport_width;
9376
- if (phProps.$viewport_height) props.viewportHeight = phProps.$viewport_height;
9377
- if (phProps.$session_id) props.sessionId = phProps.$session_id;
9378
- if (phProps.$scroll_depth) props.scrollDepth = phProps.$scroll_depth;
9379
- if (phProps.$scroll_percentage) props.scrollPercentage = phProps.$scroll_percentage;
9380
- props.originalEvent = phEvent.event;
9381
- return props;
9382
- }
9383
- function normalizePostHogEvent(phEvent) {
9384
- let ts;
9385
- if (typeof phEvent.timestamp === "number") {
9386
- ts = phEvent.timestamp;
9387
- } else if (typeof phEvent.timestamp === "string") {
9388
- ts = new Date(phEvent.timestamp).getTime();
9389
- } else {
9390
- ts = Date.now();
9391
- }
9392
- return {
9393
- ts,
9394
- name: getEventName(phEvent),
9395
- source: "posthog",
9396
- props: extractProps(phEvent),
9397
- schemaVersion: EVENT_SCHEMA_VERSION
9398
- };
9399
- }
9400
- function shouldNormalizeEvent(phEvent) {
9401
- const eventName = phEvent.event;
9402
- if (typeof eventName !== "string") return false;
9403
- const skipEvents = [
9404
- "$feature_flag_called",
9405
- "$feature_flags",
9406
- "$groups",
9407
- "$groupidentify",
9408
- "$set",
9409
- "$set_once",
9410
- "$unset",
9411
- "$create_alias",
9412
- "$capture_metrics",
9413
- "$performance_event",
9414
- "$web_vitals",
9415
- "$exception",
9416
- "$dead_click",
9417
- "$heatmap"
9418
- ];
9419
- if (skipEvents.includes(eventName)) {
9420
- return false;
9421
- }
9422
- return true;
9423
- }
9424
- function createPostHogNormalizer(publishFn) {
9425
- return (eventName, properties) => {
9426
- if (typeof eventName !== "string") return;
9427
- const phEvent = {
9428
- event: eventName,
9429
- properties,
9430
- timestamp: Date.now()
9431
- };
9432
- if (shouldNormalizeEvent(phEvent)) {
9433
- const normalizedEvent = normalizePostHogEvent(phEvent);
9434
- publishFn(normalizedEvent);
9435
- }
9436
- };
9437
- }
9438
-
9439
9370
  // src/navigation/NavigationMonitor.ts
9440
9371
  var NavigationMonitor = class {
9441
9372
  constructor() {
@@ -10719,6 +10650,9 @@ function encodeToken(payload) {
10719
10650
  return TOKEN_PREFIX + base64;
10720
10651
  }
10721
10652
 
10653
+ // src/bootstrap.ts
10654
+ import { createEventProcessor } from "@syntrologie/event-processor";
10655
+
10722
10656
  // src/experiments/registry.ts
10723
10657
  var adapters = {
10724
10658
  growthbook: (config) => createGrowthBookClient({
@@ -11128,9 +11062,44 @@ async function init(options) {
11128
11062
  let experiments;
11129
11063
  const events = createEventBus();
11130
11064
  console.log("[Syntro Bootstrap] EventBus created");
11131
- const postHogNormalizer = createPostHogNormalizer((event) => {
11132
- events.publishEvent(event);
11065
+ const processor = createEventProcessor({
11066
+ elementResolver: typeof window !== "undefined" ? (x, y) => {
11067
+ const el = document.elementFromPoint(x, y);
11068
+ if (!el) return null;
11069
+ const info = { tag_name: el.tagName.toLowerCase() };
11070
+ if (el.id) info.attr__id = el.id;
11071
+ if (el.className && typeof el.className === "string") {
11072
+ info.classes = el.className.split(" ").filter(Boolean);
11073
+ }
11074
+ for (const attr of el.attributes) {
11075
+ if (attr.name.startsWith("data-")) info[`attr__${attr.name}`] = attr.value;
11076
+ }
11077
+ return info;
11078
+ } : void 0
11133
11079
  });
11080
+ processor.onEvent((event) => events.publishEvent(event));
11081
+ if (typeof window !== "undefined") {
11082
+ setInterval(() => processor.tick(Date.now()), 1e3);
11083
+ document.addEventListener(
11084
+ "click",
11085
+ (e) => {
11086
+ const chain = [];
11087
+ let el = e.target;
11088
+ while (el && el !== document.body) {
11089
+ const info = { tag_name: el.tagName.toLowerCase() };
11090
+ for (const attr of el.attributes) {
11091
+ if (attr.name.startsWith("data-") || attr.name === "id" || attr.name === "class" || attr.name === "aria-label") {
11092
+ info[`attr__${attr.name}`] = attr.value;
11093
+ }
11094
+ }
11095
+ chain.push(info);
11096
+ el = el.parentElement;
11097
+ }
11098
+ processor.enrichClickAttributes(Date.now(), chain);
11099
+ },
11100
+ true
11101
+ );
11102
+ }
11134
11103
  const onFeatureFlagsLoaded = (allFlags) => {
11135
11104
  var _a3, _b2, _c2;
11136
11105
  debug("Syntro Bootstrap", "Phase 2: PostHog feature flags loaded");
@@ -11155,8 +11124,14 @@ async function init(options) {
11155
11124
  enableFeatureFlags: true,
11156
11125
  // Wire up callback for when flags are loaded (Phase 2)
11157
11126
  onFeatureFlagsLoaded,
11158
- // Wire up event capture to feed into EventBus
11159
- onCapture: postHogNormalizer
11127
+ // Wire up event capture to feed into event processor
11128
+ onCapture: (eventName, properties) => {
11129
+ processor.ingest({ kind: "posthog", event: eventName, properties, timestamp: Date.now() });
11130
+ },
11131
+ // Wire rrweb events for behavioral signal detection
11132
+ onRRWebEvent: (event) => {
11133
+ processor.ingest(event);
11134
+ }
11160
11135
  });
11161
11136
  console.log(`[Syntro Bootstrap] Telemetry client created (${provider}) with EventBus wiring`);
11162
11137
  }
@@ -11426,9 +11401,6 @@ export {
11426
11401
  createEventAccumulator,
11427
11402
  EventBus,
11428
11403
  createEventBus,
11429
- normalizePostHogEvent,
11430
- shouldNormalizeEvent,
11431
- createPostHogNormalizer,
11432
11404
  NavigationMonitor,
11433
11405
  StateStore,
11434
11406
  createStateStore,
@@ -11452,4 +11424,4 @@ export {
11452
11424
  encodeToken,
11453
11425
  Syntro
11454
11426
  };
11455
- //# sourceMappingURL=chunk-OIDBMIRB.js.map
11427
+ //# sourceMappingURL=chunk-H3FAYTUV.js.map