@syntrologie/runtime-sdk 2.8.0-canary.196 → 2.8.0-canary.198

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/index.js CHANGED
@@ -9590,6 +9590,10 @@ var HealthReporter = class {
9590
9590
  __publicField(this, "timer", null);
9591
9591
  __publicField(this, "pagehideHandler", null);
9592
9592
  __publicField(this, "runtime", null);
9593
+ /** Cleanup function for adapters that registered side effects (e.g. the
9594
+ * web-vitals long-task observer). Called from `stop()` so SPA re-init
9595
+ * via `initHealthReporter` doesn't accumulate observers. */
9596
+ __publicField(this, "adapterCleanup", null);
9593
9597
  var _a3;
9594
9598
  this.resource = config.resource;
9595
9599
  this.flushIntervalMs = (_a3 = config.flushIntervalMs) != null ? _a3 : 6e4;
@@ -9602,6 +9606,15 @@ var HealthReporter = class {
9602
9606
  bindRuntime(binding) {
9603
9607
  this.runtime = binding;
9604
9608
  }
9609
+ /**
9610
+ * Register a cleanup function to run on `stop()`. Used by adapters
9611
+ * (e.g. webVitalsAdapter) that need to release per-bind resources
9612
+ * like PerformanceObservers. Replaces any prior cleanup — only one
9613
+ * adapter cleanup is tracked.
9614
+ */
9615
+ attachAdapterCleanup(cleanup) {
9616
+ this.adapterCleanup = cleanup;
9617
+ }
9605
9618
  increment(signal, by = 1) {
9606
9619
  var _a3;
9607
9620
  this.counters.set(signal, ((_a3 = this.counters.get(signal)) != null ? _a3 : 0) + by);
@@ -9697,6 +9710,13 @@ var HealthReporter = class {
9697
9710
  window.removeEventListener("pagehide", this.pagehideHandler);
9698
9711
  this.pagehideHandler = null;
9699
9712
  }
9713
+ if (this.adapterCleanup !== null) {
9714
+ try {
9715
+ this.adapterCleanup();
9716
+ } catch {
9717
+ }
9718
+ this.adapterCleanup = null;
9719
+ }
9700
9720
  }
9701
9721
  };
9702
9722
  function percentile(sortedAsc, q2) {
@@ -9801,29 +9821,48 @@ function createOtlpEmitter(cfg) {
9801
9821
  };
9802
9822
  }
9803
9823
 
9824
+ // src/observability/sdkScriptUrl.ts
9825
+ function detect() {
9826
+ var _a3;
9827
+ if (typeof document === "undefined") return void 0;
9828
+ const cs = document.currentScript;
9829
+ if (cs && cs.src) return cs.src;
9830
+ try {
9831
+ const scripts = Array.from(document.scripts);
9832
+ for (let i = scripts.length - 1; i >= 0; i -= 1) {
9833
+ const src = (_a3 = scripts[i]) == null ? void 0 : _a3.src;
9834
+ if (typeof src === "string" && (src.includes("runtime-sdk") || src.includes("smart-canvas"))) {
9835
+ return src;
9836
+ }
9837
+ }
9838
+ } catch {
9839
+ }
9840
+ return void 0;
9841
+ }
9842
+ var SDK_SCRIPT_URL = detect();
9843
+ function getSdkScriptUrlPrefix() {
9844
+ if (!SDK_SCRIPT_URL) return void 0;
9845
+ const lastSlash = SDK_SCRIPT_URL.lastIndexOf("/");
9846
+ if (lastSlash === -1) return void 0;
9847
+ return SDK_SCRIPT_URL.substring(0, lastSlash + 1);
9848
+ }
9849
+
9804
9850
  // src/observability/webVitalsAdapter.ts
9805
9851
  import { onCLS, onFCP, onINP, onLCP, onTTFB } from "web-vitals";
9852
+ var VITALS_BOUND_FLAG = "__syntro_vitals_bound";
9853
+ var VITALS_CURRENT_REPORTER = "__syntro_vitals_current_reporter";
9806
9854
  function bindWebVitals(reporter, options) {
9807
9855
  if (typeof window === "undefined") return () => {
9808
9856
  };
9809
- const recordMs = (signal) => {
9810
- return (metric) => {
9811
- reporter.recordHistogram(signal, metric.value);
9812
- };
9813
- };
9814
- const recordCls = (metric) => {
9815
- reporter.recordHistogram("cls_score", metric.value);
9816
- };
9817
- onLCP(recordMs("lcp_ms"));
9818
- onINP(recordMs("inp_ms"));
9819
- onFCP(recordMs("fcp_ms"));
9820
- onTTFB(recordMs("ttfb_ms"));
9821
- onCLS(recordCls);
9857
+ const w2 = window;
9858
+ w2[VITALS_CURRENT_REPORTER] = reporter;
9822
9859
  let longTaskObserver = null;
9823
9860
  const prefix = options == null ? void 0 : options.attributedScriptUrlPrefix;
9824
9861
  if (prefix && "PerformanceObserver" in window) {
9825
9862
  try {
9826
9863
  longTaskObserver = new PerformanceObserver((list) => {
9864
+ const current = w2[VITALS_CURRENT_REPORTER];
9865
+ if (!current) return;
9827
9866
  for (const entry of list.getEntries()) {
9828
9867
  const attribution = entry.attribution;
9829
9868
  if (!attribution) continue;
@@ -9833,8 +9872,8 @@ function bindWebVitals(reporter, options) {
9833
9872
  return src.startsWith(prefix);
9834
9873
  });
9835
9874
  if (matched) {
9836
- reporter.increment("longtasks_self_count");
9837
- reporter.increment("longtasks_self_blocking_ms", Math.round(entry.duration));
9875
+ current.increment("longtasks_self_count");
9876
+ current.increment("longtasks_self_blocking_ms", Math.round(entry.duration));
9838
9877
  }
9839
9878
  }
9840
9879
  });
@@ -9843,13 +9882,28 @@ function bindWebVitals(reporter, options) {
9843
9882
  longTaskObserver = null;
9844
9883
  }
9845
9884
  }
9885
+ if (!w2[VITALS_BOUND_FLAG]) {
9886
+ w2[VITALS_BOUND_FLAG] = true;
9887
+ const recordMs = (signal) => (metric) => {
9888
+ const current = w2[VITALS_CURRENT_REPORTER];
9889
+ current == null ? void 0 : current.recordHistogram(signal, metric.value);
9890
+ };
9891
+ onLCP(recordMs("lcp_ms"));
9892
+ onINP(recordMs("inp_ms"));
9893
+ onFCP(recordMs("fcp_ms"));
9894
+ onTTFB(recordMs("ttfb_ms"));
9895
+ onCLS((metric) => {
9896
+ const current = w2[VITALS_CURRENT_REPORTER];
9897
+ current == null ? void 0 : current.recordHistogram("cls_score", metric.value);
9898
+ });
9899
+ }
9846
9900
  return () => {
9847
9901
  longTaskObserver == null ? void 0 : longTaskObserver.disconnect();
9848
9902
  };
9849
9903
  }
9850
9904
 
9851
9905
  // src/version.ts
9852
- var SDK_VERSION = "2.8.0-canary.196";
9906
+ var SDK_VERSION = "2.8.0-canary.198";
9853
9907
 
9854
9908
  // src/types.ts
9855
9909
  var SDK_SCHEMA_VERSION = "2.0";
@@ -18247,7 +18301,7 @@ function createTelemetryClient(provider, config) {
18247
18301
 
18248
18302
  // src/bootstrap-runtime.ts
18249
18303
  async function _initCore(options) {
18250
- var _a3, _b, _c, _d, _e2, _f, _g, _h, _i, _j, _k, _l, _m;
18304
+ var _a3, _b, _c, _d, _e2, _f, _g, _h, _i, _j, _k, _l, _m, _n;
18251
18305
  initLogger();
18252
18306
  debug("Syntro Bootstrap", "====== INIT ======");
18253
18307
  debug("Syntro Bootstrap", "Options:", {
@@ -18325,17 +18379,17 @@ async function _initCore(options) {
18325
18379
  },
18326
18380
  emit: createOtlpEmitter({ endpoint: otlpEndpoint })
18327
18381
  });
18328
- bindWebVitals(reporter, {
18329
- attributedScriptUrlPrefix: "https://cdn.syntrologie.com/runtime-sdk/"
18330
- });
18382
+ const attributedScriptUrlPrefix = (_c = getSdkScriptUrlPrefix()) != null ? _c : "https://cdn.syntrologie.com/runtime-sdk/";
18383
+ const cleanup = bindWebVitals(reporter, { attributedScriptUrlPrefix });
18384
+ reporter.attachAdapterCleanup(cleanup);
18331
18385
  } catch {
18332
18386
  }
18333
18387
  }
18334
- const editorUrl = getEnvVar("NEXT_PUBLIC_SYNTRO_EDITOR_URL") || getEnvVar("VITE_SYNTRO_EDITOR_URL") || ((_c = options.canvas) == null ? void 0 : _c.editorUrl);
18388
+ const editorUrl = getEnvVar("NEXT_PUBLIC_SYNTRO_EDITOR_URL") || getEnvVar("VITE_SYNTRO_EDITOR_URL") || ((_d = options.canvas) == null ? void 0 : _d.editorUrl);
18335
18389
  const geoHost = (payload == null ? void 0 : payload.g) || getEnvVar("NEXT_PUBLIC_SYNTRO_GEO_HOST") || getEnvVar("VITE_SYNTRO_GEO_HOST") || GEO_DEFAULT_HOST;
18336
18390
  const cachedSegmentAttrs = loadCachedSegmentAttributes();
18337
18391
  const browserMetadata = collectBrowserMetadata();
18338
- const appSignalsInit = (_d = options.appSignalsInit) != null ? _d : {};
18392
+ const appSignalsInit = (_e2 = options.appSignalsInit) != null ? _e2 : {};
18339
18393
  const phaseOneAttrs = { ...browserMetadata, ...cachedSegmentAttrs, ...appSignalsInit };
18340
18394
  debug("Syntro Bootstrap", "Phase 1: Browser metadata:", browserMetadata);
18341
18395
  debug("Syntro Bootstrap", "Phase 1: Cached segment attributes:", cachedSegmentAttrs);
@@ -18479,7 +18533,7 @@ async function _initCore(options) {
18479
18533
  });
18480
18534
  console.log(`[Syntro Bootstrap] Telemetry client created (${provider}) with EventBus wiring`);
18481
18535
  if (Object.keys(appSignalsInit).length > 0) {
18482
- (_e2 = telemetry.track) == null ? void 0 : _e2.call(telemetry, "app_signals_init", appSignalsInit);
18536
+ (_f = telemetry.track) == null ? void 0 : _f.call(telemetry, "app_signals_init", appSignalsInit);
18483
18537
  debug("Syntro Bootstrap", "Tracked app signals event:", appSignalsInit);
18484
18538
  }
18485
18539
  const telemetryForCapture = telemetry;
@@ -18537,7 +18591,7 @@ async function _initCore(options) {
18537
18591
  if ((platformAdapter == null ? void 0 : platformAdapter.name) === "shopify" && telemetryHost) {
18538
18592
  try {
18539
18593
  shopifyPixelBridge = new ShopifyPixelBridge({
18540
- distinctId: (_g = (_f = telemetry.getDistinctId) == null ? void 0 : _f.call(telemetry)) != null ? _g : "",
18594
+ distinctId: (_h = (_g = telemetry.getDistinctId) == null ? void 0 : _g.call(telemetry)) != null ? _h : "",
18541
18595
  telemetryHost,
18542
18596
  telemetryKey: payload.t
18543
18597
  });
@@ -18625,7 +18679,7 @@ async function _initCore(options) {
18625
18679
  debug("Syntro Bootstrap", "auto-load of adaptive-mcp failed (non-fatal):", err);
18626
18680
  });
18627
18681
  }
18628
- const registeredApps = (_j = (_i = (_h = runtime5.apps).list) == null ? void 0 : _i.call(_h)) != null ? _j : [];
18682
+ const registeredApps = (_k = (_j = (_i = runtime5.apps).list) == null ? void 0 : _j.call(_i)) != null ? _k : [];
18629
18683
  console.log(
18630
18684
  `[DIAG] Activation loop: ${registeredApps.length} apps in registry:`,
18631
18685
  registeredApps.map((a) => `${a.manifest.id}(${a.state})`).join(", ")
@@ -18656,13 +18710,13 @@ async function _initCore(options) {
18656
18710
  if (experiments && Object.keys(geoData).length > 0) {
18657
18711
  const mergedAttrs = { ...browserMetadata, ...geoData, ...appSignalsInit };
18658
18712
  debug("Syntro Bootstrap", "Merging geo data into GrowthBook attributes:", geoData);
18659
- (_k = experiments.setAttributes) == null ? void 0 : _k.call(experiments, mergedAttrs);
18713
+ (_l = experiments.setAttributes) == null ? void 0 : _l.call(experiments, mergedAttrs);
18660
18714
  }
18661
18715
  const mcpHost = await mcpPromise;
18662
18716
  if (mcpHost && experiments) {
18663
18717
  browserMetadata.surface_type = "mcp-app";
18664
18718
  browserMetadata.surface_host = mcpHost.name;
18665
- (_l = experiments.setAttributes) == null ? void 0 : _l.call(experiments, { ...browserMetadata, ...geoData });
18719
+ (_m = experiments.setAttributes) == null ? void 0 : _m.call(experiments, { ...browserMetadata, ...geoData });
18666
18720
  runtime5.context.setSurfaceType("mcp-app");
18667
18721
  runtime5.context.setSurfaceHost(mcpHost.name);
18668
18722
  debug("Syntro Bootstrap", "MCP App detected:", mcpHost.name);
@@ -18671,7 +18725,7 @@ async function _initCore(options) {
18671
18725
  if (options.fetcher) {
18672
18726
  baseFetcher = options.fetcher;
18673
18727
  } else if (payload == null ? void 0 : payload.f) {
18674
- const configFetcher = createConfigFetcher(payload.f, (_m = payload.o) != null ? _m : {});
18728
+ const configFetcher = createConfigFetcher(payload.f, (_n = payload.o) != null ? _n : {});
18675
18729
  baseFetcher = async () => {
18676
18730
  var _a4;
18677
18731
  const result = await configFetcher.fetch();