@syntrologie/runtime-sdk 2.8.0-canary.153 → 2.8.0-canary.155

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.
@@ -166,6 +166,8 @@ export declare class SmartCanvasElementLit extends LitElement {
166
166
  * re-render and re-fetch without React.
167
167
  */
168
168
  mountLitApp(props: SmartCanvasLitProps): void;
169
- _loadConfig(): Promise<void>;
169
+ _loadConfig({ silent }?: {
170
+ silent?: boolean;
171
+ }): Promise<void>;
170
172
  }
171
173
  export declare const registerSmartCanvasElement: () => void;
@@ -0,0 +1,2 @@
1
+ "use strict";(()=>{var d=async(e,t)=>{let{badgeId:i}=e;return t.publishEvent("gamification.badge_awarded",{badgeId:i,awardedAt:Date.now()}),{cleanup:()=>{}}},c=async(e,t)=>{let{points:i,reason:s}=e;return t.publishEvent("gamification.points_added",{points:i,reason:s,timestamp:Date.now()}),{cleanup:()=>{}}},g={names:["page_view","button_click"],handler:(e,t)=>{console.log("[Gamification] Event received for badge trigger check")}},o=[{kind:"gamification:awardBadge",executor:d},{kind:"gamification:addPoints",executor:c}],a=[g],n={id:"adaptive-gamification",version:"1.0.0",name:"Gamification",description:"Badges, rewards, points, and engagement mechanics",executors:o,eventHandlers:a};var r={id:n.id,version:n.version,name:n.name,description:n.description,runtime:{actions:o.map(({kind:e,executor:t})=>({kind:e,executor:t})),events:a},metadata:{isBuiltIn:!1}};if(typeof window<"u"){let e=window.SynOS?.appRegistry;e&&typeof e.register=="function"&&e.register(r)}var m=r;})();
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../adaptives/adaptive-gamification/src/runtime.ts", "../../../../adaptives/adaptive-gamification/src/cdn.ts"],
4
+ "sourcesContent": ["/**\n * Adaptive Gamification - Runtime Module\n *\n * Gamification actions: awardBadge, addPoints.\n * Provides gamification features like badges, points, and rewards.\n */\n\nimport type { ActionExecutor, ExecutorResult } from './types';\n\n// ============================================================================\n// Action Types\n// ============================================================================\n\n/**\n * Award badge action\n */\nexport interface AwardBadgeAction {\n kind: 'gamification:awardBadge';\n badgeId: string;\n anchorId?: string;\n label?: string;\n}\n\n/**\n * Add points action\n */\nexport interface AddPointsAction {\n kind: 'gamification:addPoints';\n points: number;\n reason?: string;\n label?: string;\n}\n\n// ============================================================================\n// Executors\n// ============================================================================\n\n/**\n * Execute an awardBadge action\n *\n * Note: This executor uses publishEvent to track badge awards.\n * State management is handled at the app level via AppContext,\n * not at the action executor level.\n */\nexport const executeAwardBadge: ActionExecutor<AwardBadgeAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n const { badgeId } = action;\n\n // Emit telemetry event (state management handled at app level)\n context.publishEvent('gamification.badge_awarded', {\n badgeId,\n awardedAt: Date.now(),\n });\n\n return {\n cleanup: () => {\n // Badge awards are permanent, no cleanup needed\n },\n };\n};\n\n/**\n * Execute an addPoints action\n *\n * Note: This executor uses publishEvent to track points.\n * State management is handled at the app level via AppContext,\n * not at the action executor level.\n */\nexport const executeAddPoints: ActionExecutor<AddPointsAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n const { points, reason } = action;\n\n // Emit telemetry event (state management handled at app level)\n context.publishEvent('gamification.points_added', {\n points,\n reason,\n timestamp: Date.now(),\n });\n\n return {\n cleanup: () => {\n // Points are permanent, no cleanup needed\n },\n };\n};\n\n// ============================================================================\n// Event Handlers\n// ============================================================================\n\n/**\n * Event handler for auto-awarding badges based on triggers.\n */\nexport const badgeTriggerHandler = {\n names: ['page_view', 'button_click'],\n handler: (_event: unknown, _ctx: unknown) => {\n // Auto-award badges based on event triggers\n // This would check badge trigger conditions in the config\n console.log('[Gamification] Event received for badge trigger check');\n },\n};\n\n// ============================================================================\n// Executor Definitions for Registration\n// ============================================================================\n\n/**\n * All executors provided by this app.\n * These are registered with the runtime's ExecutorRegistry.\n */\nexport const executors = [\n { kind: 'gamification:awardBadge', executor: executeAwardBadge },\n { kind: 'gamification:addPoints', executor: executeAddPoints },\n] as const;\n\n/**\n * Event handlers provided by this app.\n */\nexport const eventHandlers = [badgeTriggerHandler];\n\n/**\n * App runtime manifest.\n */\nexport const runtime = {\n id: 'adaptive-gamification',\n version: '1.0.0',\n name: 'Gamification',\n description: 'Badges, rewards, points, and engagement mechanics',\n executors,\n eventHandlers,\n};\n", "/**\n * CDN Entry Point for Adaptive Gamification\n *\n * Bundled for CDN delivery and loaded on-demand by the host runtime's\n * AppLoader when a config references gamification actions/widgets.\n *\n * Mirrors the pattern in adaptive-mcp/src/cdn.ts: builds the manifest from\n * the runtime export, then self-registers with the host's app registry on\n * import. The host's AppLoader fetches this bundle, esbuild's IIFE wrapper\n * runs the registration, and AppRegistry.activate() can then wire the\n * executors into the runtime.\n *\n * Without this entry, the build script (build-adaptives-only.js) silently\n * skips the package \u2014 gamification ends up \"lazy in name only\" with no\n * bundle on disk, and any config using `gamification:awardBadge` /\n * `gamification:addPoints` hits \"Unknown action kind\".\n */\n\nimport { eventHandlers, executors, runtime } from './runtime';\n\nexport const manifest = {\n id: runtime.id,\n version: runtime.version,\n name: runtime.name,\n description: runtime.description,\n runtime: {\n actions: executors.map(({ kind, executor }) => ({ kind, executor })),\n events: eventHandlers,\n },\n metadata: {\n isBuiltIn: false,\n },\n};\n\ndeclare global {\n interface Window {\n SynOS?: {\n appRegistry?: {\n register?: (m: unknown) => void;\n };\n };\n }\n}\n\nif (typeof window !== 'undefined') {\n const registry = window.SynOS?.appRegistry;\n if (registry && typeof registry.register === 'function') {\n registry.register(manifest);\n }\n}\n\nexport default manifest;\n"],
5
+ "mappings": "mBA4CO,IAAMA,EAAsD,MACjEC,EACAC,IAC4B,CAC5B,GAAM,CAAE,QAAAC,CAAQ,EAAIF,EAGpB,OAAAC,EAAQ,aAAa,6BAA8B,CACjD,QAAAC,EACA,UAAW,KAAK,IAAI,CACtB,CAAC,EAEM,CACL,QAAS,IAAM,CAEf,CACF,CACF,EASaC,EAAoD,MAC/DH,EACAC,IAC4B,CAC5B,GAAM,CAAE,OAAAG,EAAQ,OAAAC,CAAO,EAAIL,EAG3B,OAAAC,EAAQ,aAAa,4BAA6B,CAChD,OAAAG,EACA,OAAAC,EACA,UAAW,KAAK,IAAI,CACtB,CAAC,EAEM,CACL,QAAS,IAAM,CAEf,CACF,CACF,EASaC,EAAsB,CACjC,MAAO,CAAC,YAAa,cAAc,EACnC,QAAS,CAACC,EAAiBC,IAAkB,CAG3C,QAAQ,IAAI,uDAAuD,CACrE,CACF,EAUaC,EAAY,CACvB,CAAE,KAAM,0BAA2B,SAAUV,CAAkB,EAC/D,CAAE,KAAM,yBAA0B,SAAUI,CAAiB,CAC/D,EAKaO,EAAgB,CAACJ,CAAmB,EAKpCK,EAAU,CACrB,GAAI,wBACJ,QAAS,QACT,KAAM,eACN,YAAa,oDACb,UAAAF,EACA,cAAAC,CACF,EClHO,IAAME,EAAW,CACtB,GAAIC,EAAQ,GACZ,QAASA,EAAQ,QACjB,KAAMA,EAAQ,KACd,YAAaA,EAAQ,YACrB,QAAS,CACP,QAASC,EAAU,IAAI,CAAC,CAAE,KAAAC,EAAM,SAAAC,CAAS,KAAO,CAAE,KAAAD,EAAM,SAAAC,CAAS,EAAE,EACnE,OAAQC,CACV,EACA,SAAU,CACR,UAAW,EACb,CACF,EAYA,GAAI,OAAO,OAAW,IAAa,CACjC,IAAMC,EAAW,OAAO,OAAO,YAC3BA,GAAY,OAAOA,EAAS,UAAa,YAC3CA,EAAS,SAASN,CAAQ,CAE9B,CAEA,IAAOO,EAAQP",
6
+ "names": ["executeAwardBadge", "action", "context", "badgeId", "executeAddPoints", "points", "reason", "badgeTriggerHandler", "_event", "_ctx", "executors", "eventHandlers", "runtime", "manifest", "runtime", "executors", "kind", "executor", "eventHandlers", "registry", "cdn_default"]
7
+ }
package/dist/index.js CHANGED
@@ -6456,7 +6456,7 @@ function parseElementsChain(chain) {
6456
6456
  }
6457
6457
  if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(String((_a3 = el.tag_name) != null ? _a3 : "")))
6458
6458
  return null;
6459
- const attrRegex = /([\w$]+)="([^"]*)"/g;
6459
+ const attrRegex = /([\w$-]+)="([^"]*)"/g;
6460
6460
  let match;
6461
6461
  while ((match = attrRegex.exec(attrPart)) !== null) {
6462
6462
  const [, key, value] = match;
@@ -6483,11 +6483,22 @@ function resolveInteractiveTag(elements, directTag) {
6483
6483
  }
6484
6484
  return directTag;
6485
6485
  }
6486
- function extractProps(phEvent) {
6486
+ function mergeEnrichElements(phElements, enrichElements) {
6487
+ return phElements.map((phEl, i) => {
6488
+ const enrichEl = enrichElements[i];
6489
+ if (!enrichEl)
6490
+ return phEl;
6491
+ if (phEl.tag_name !== enrichEl.tag_name)
6492
+ return phEl;
6493
+ return { ...enrichEl, ...phEl };
6494
+ });
6495
+ }
6496
+ function extractProps(phEvent, enrichElements) {
6487
6497
  var _a3, _b, _c;
6488
6498
  const props = {};
6489
6499
  const phProps = phEvent.properties || {};
6490
- const elements = (_a3 = phProps.$elements) != null ? _a3 : typeof phProps.$elements_chain === "string" ? parseElementsChain(phProps.$elements_chain) : void 0;
6500
+ const rawElements = (_a3 = phProps.$elements) != null ? _a3 : typeof phProps.$elements_chain === "string" ? parseElementsChain(phProps.$elements_chain) : void 0;
6501
+ const elements = rawElements && enrichElements ? mergeEnrichElements(rawElements, enrichElements) : rawElements;
6491
6502
  const directTag = (_c = phProps.$tag_name) != null ? _c : (_b = elements == null ? void 0 : elements[0]) == null ? void 0 : _b.tag_name;
6492
6503
  const isClickEvent = phEvent.event === "$autocapture" || phEvent.event === "$click";
6493
6504
  props.tagName = isClickEvent ? resolveInteractiveTag(elements, directTag) : directTag;
@@ -6517,7 +6528,7 @@ function extractProps(phEvent) {
6517
6528
  props.originalEvent = phEvent.event;
6518
6529
  return props;
6519
6530
  }
6520
- function normalizePostHogEvent(phEvent) {
6531
+ function normalizePostHogEvent(phEvent, enrichElements) {
6521
6532
  let ts;
6522
6533
  if (typeof phEvent.timestamp === "number") {
6523
6534
  ts = phEvent.timestamp;
@@ -6530,7 +6541,7 @@ function normalizePostHogEvent(phEvent) {
6530
6541
  ts,
6531
6542
  name: getEventName(phEvent),
6532
6543
  source: "posthog",
6533
- props: extractProps(phEvent),
6544
+ props: extractProps(phEvent, enrichElements),
6534
6545
  schemaVersion: EVENT_SCHEMA_VERSION
6535
6546
  };
6536
6547
  }
@@ -6590,12 +6601,31 @@ function createEventProcessor(options) {
6590
6601
  const idle = new IdleDetector(config, emit2);
6591
6602
  const hover = new HoverTracker(config, emit2, options == null ? void 0 : options.elementResolver);
6592
6603
  const clickAttrBuffer = [];
6604
+ const CLICK_ATTR_TTL_MS = 500;
6605
+ function isClickShapedEvent(phEvent) {
6606
+ var _a3;
6607
+ if (phEvent.event === "$click")
6608
+ return true;
6609
+ if (phEvent.event !== "$autocapture")
6610
+ return false;
6611
+ const eventType = (_a3 = phEvent.properties) == null ? void 0 : _a3.$event_type;
6612
+ return eventType === void 0 || eventType === "click";
6613
+ }
6614
+ function consumeBufferedClickChain(eventTs) {
6615
+ var _a3;
6616
+ const cutoff = eventTs - CLICK_ATTR_TTL_MS;
6617
+ while (clickAttrBuffer.length > 0 && clickAttrBuffer[0].ts < cutoff) {
6618
+ clickAttrBuffer.shift();
6619
+ }
6620
+ return (_a3 = clickAttrBuffer.shift()) == null ? void 0 : _a3.elements;
6621
+ }
6593
6622
  return {
6594
6623
  ingest(raw) {
6595
6624
  if (raw.kind === "posthog") {
6596
6625
  const { kind: _2, ...phEvent } = raw;
6597
6626
  if (shouldNormalizeEvent(phEvent)) {
6598
- emit2(normalizePostHogEvent(phEvent));
6627
+ const enrichElements = isClickShapedEvent(phEvent) ? consumeBufferedClickChain(typeof phEvent.timestamp === "number" ? phEvent.timestamp : Date.now()) : void 0;
6628
+ emit2(normalizePostHogEvent(phEvent, enrichElements));
6599
6629
  }
6600
6630
  } else if (raw.kind === "rrweb") {
6601
6631
  hesitation.ingest(raw);
@@ -6616,7 +6646,7 @@ function createEventProcessor(options) {
6616
6646
  },
6617
6647
  enrichClickAttributes(timestamp, elements) {
6618
6648
  clickAttrBuffer.push({ ts: timestamp, elements });
6619
- const cutoff = timestamp - 500;
6649
+ const cutoff = timestamp - CLICK_ATTR_TTL_MS;
6620
6650
  while (clickAttrBuffer.length > 0 && clickAttrBuffer[0].ts < cutoff) {
6621
6651
  clickAttrBuffer.shift();
6622
6652
  }
@@ -7708,7 +7738,7 @@ function error(prefix, message, data) {
7708
7738
  }
7709
7739
 
7710
7740
  // src/version.ts
7711
- var SDK_VERSION = "2.8.0-canary.153";
7741
+ var SDK_VERSION = "2.8.0-canary.155";
7712
7742
 
7713
7743
  // src/types.ts
7714
7744
  var SDK_SCHEMA_VERSION = "2.0";
@@ -8085,7 +8115,7 @@ function fireTriggeredForTiles(tiles) {
8085
8115
  tracker.trackTriggered(tile.id, (_b = tile.widget) != null ? _b : "unknown");
8086
8116
  }
8087
8117
  }
8088
- var _controller, _controllerUnsub, _overlayContainer, _portalRoot, _batchHandle, _adoptedInitial, _runVersion, _rawConfig, _prevActionsJson, _prevTilesJson, _derivedFetcher, _experimentUnsub, _accumulatorUnsub, _contextUnsub, _eventBusUnsub, _onUrlChange, _themeCtrl, _runtimeProvider, _SmartCanvasElementLit_instances, rebuildFetcher_fn, buildFetcherOptions_fn, _loadConfigVersion, refilterTiles_fn, runActionLifecycle_fn, startUrlTracking_fn, stopUrlTracking_fn, subscribeRuntimeReactivity_fn, unsubscribeRuntimeReactivity_fn, subscribeCanvasRequestOpen_fn, _onCanvasToggle;
8118
+ var _controller, _controllerUnsub, _overlayContainer, _portalRoot, _batchHandle, _adoptedInitial, _runVersion, _rawConfig, _prevActionsJson, _prevTilesJson, _prevThemeJson, _prevLauncherJson, _derivedFetcher, _experimentUnsub, _accumulatorUnsub, _contextUnsub, _eventBusUnsub, _onUrlChange, _themeCtrl, _runtimeProvider, _SmartCanvasElementLit_instances, rebuildFetcher_fn, buildFetcherOptions_fn, _loadConfigVersion, refilterTiles_fn, runActionLifecycle_fn, startUrlTracking_fn, stopUrlTracking_fn, subscribeRuntimeReactivity_fn, unsubscribeRuntimeReactivity_fn, subscribeCanvasRequestOpen_fn, _onCanvasToggle;
8089
8119
  var SmartCanvasElementLit = class extends LitElement8 {
8090
8120
  // ---------- Constructor --------------------------------------------------
8091
8121
  constructor() {
@@ -8142,6 +8172,16 @@ var SmartCanvasElementLit = class extends LitElement8 {
8142
8172
  // Lit re-mounts every tile on each poll, blowing away widget state like
8143
8173
  // open FAQ accordion items.
8144
8174
  __privateAdd(this, _prevTilesJson, "[]");
8175
+ // Same JSON-diff guard pattern for the rest of the reactive config fields.
8176
+ // GrowthBook's onFeaturesChanged refires every ~5s; without these guards
8177
+ // the parent template re-renders on every poll (fresh deserialized theme/
8178
+ // launcher refs), which ripples into widget re-mounts and vega-embed
8179
+ // resize-observer thrash. See SmartCanvasElementLit.test.ts
8180
+ // "reactive-state stability across identical refresh" for the contract.
8181
+ // The _isLoading spinner is gated separately via the `silent` opt on
8182
+ // `_loadConfig` — see "loading-state semantics" tests for that contract.
8183
+ __privateAdd(this, _prevThemeJson, "null");
8184
+ __privateAdd(this, _prevLauncherJson, "null");
8145
8185
  __privateAdd(this, _derivedFetcher, null);
8146
8186
  // Subscriptions
8147
8187
  __privateAdd(this, _experimentUnsub, null);
@@ -8247,7 +8287,9 @@ var SmartCanvasElementLit = class extends LitElement8 {
8247
8287
  __privateMethod(this, _SmartCanvasElementLit_instances, rebuildFetcher_fn).call(this);
8248
8288
  (_b = __privateGet(this, _experimentUnsub)) == null ? void 0 : _b.call(this);
8249
8289
  if ((_c = this.experiments) == null ? void 0 : _c.onFeaturesChanged) {
8250
- __privateSet(this, _experimentUnsub, this.experiments.onFeaturesChanged(() => this._loadConfig()));
8290
+ __privateSet(this, _experimentUnsub, this.experiments.onFeaturesChanged(
8291
+ () => this._loadConfig({ silent: true })
8292
+ ));
8251
8293
  }
8252
8294
  }
8253
8295
  if (fetcherPropsChanged || changed.has("_pageUrl")) {
@@ -8365,12 +8407,14 @@ var SmartCanvasElementLit = class extends LitElement8 {
8365
8407
  this.initialBatchActions = props.initialBatchActions;
8366
8408
  if (props.workspaceTheme !== void 0) this.workspaceTheme = props.workspaceTheme;
8367
8409
  }
8368
- async _loadConfig() {
8369
- var _a3, _b;
8410
+ async _loadConfig({ silent = false } = {}) {
8411
+ var _a3, _b, _c, _d;
8370
8412
  if (!__privateGet(this, _derivedFetcher)) return;
8371
8413
  const version = ++__privateWrapper(this, _loadConfigVersion)._;
8372
8414
  try {
8373
- this._isLoading = true;
8415
+ if (!silent) {
8416
+ this._isLoading = true;
8417
+ }
8374
8418
  this._error = void 0;
8375
8419
  const response = await __privateGet(this, _derivedFetcher).call(this);
8376
8420
  if (version !== __privateGet(this, _loadConfigVersion)) return;
@@ -8406,15 +8450,32 @@ var SmartCanvasElementLit = class extends LitElement8 {
8406
8450
  if (actionsChanged) {
8407
8451
  this._configActions = newActions;
8408
8452
  }
8409
- this._isLoading = false;
8410
- this._canvasTitle = response.canvasTitle;
8411
- this._theme = response.theme;
8412
- this._launcher = response.launcher;
8413
- this._displayMode = (_b = response.displayMode) != null ? _b : "standard";
8453
+ if (!silent) {
8454
+ this._isLoading = false;
8455
+ }
8456
+ const newThemeJson = JSON.stringify((_b = response.theme) != null ? _b : null);
8457
+ if (newThemeJson !== __privateGet(this, _prevThemeJson)) {
8458
+ __privateSet(this, _prevThemeJson, newThemeJson);
8459
+ this._theme = response.theme;
8460
+ }
8461
+ const newLauncherJson = JSON.stringify((_c = response.launcher) != null ? _c : null);
8462
+ if (newLauncherJson !== __privateGet(this, _prevLauncherJson)) {
8463
+ __privateSet(this, _prevLauncherJson, newLauncherJson);
8464
+ this._launcher = response.launcher;
8465
+ }
8466
+ if (response.canvasTitle !== this._canvasTitle) {
8467
+ this._canvasTitle = response.canvasTitle;
8468
+ }
8469
+ const newDisplayMode = (_d = response.displayMode) != null ? _d : "standard";
8470
+ if (newDisplayMode !== this._displayMode) {
8471
+ this._displayMode = newDisplayMode;
8472
+ }
8414
8473
  } catch (err) {
8415
8474
  const message = err instanceof Error ? err.message : "Unknown error";
8416
8475
  console.error("[SmartCanvas Config] Failed to fetch/filter config:", message, err);
8417
- this._isLoading = false;
8476
+ if (!silent) {
8477
+ this._isLoading = false;
8478
+ }
8418
8479
  this._error = message;
8419
8480
  }
8420
8481
  }
@@ -8429,6 +8490,8 @@ _runVersion = new WeakMap();
8429
8490
  _rawConfig = new WeakMap();
8430
8491
  _prevActionsJson = new WeakMap();
8431
8492
  _prevTilesJson = new WeakMap();
8493
+ _prevThemeJson = new WeakMap();
8494
+ _prevLauncherJson = new WeakMap();
8432
8495
  _derivedFetcher = new WeakMap();
8433
8496
  _experimentUnsub = new WeakMap();
8434
8497
  _accumulatorUnsub = new WeakMap();
@@ -15675,6 +15738,29 @@ async function _initCore(options) {
15675
15738
  runtime: runtime5
15676
15739
  // Pass runtime so actions can be applied
15677
15740
  });
15741
+ if (canvas.setOverrideFetcher) {
15742
+ const originalSetOverrideFetcher = canvas.setOverrideFetcher.bind(canvas);
15743
+ canvas.setOverrideFetcher = (newFetcher) => {
15744
+ const wrappedFetcher = async () => {
15745
+ const config = await newFetcher();
15746
+ const configForLoader = config;
15747
+ const requiredApps = appLoader.getRequiredApps(configForLoader);
15748
+ if (requiredApps.length > 0) {
15749
+ const results = await appLoader.loadAppsForConfig(configForLoader);
15750
+ for (const result of results) {
15751
+ if (result.success && runtime5.apps.has(result.appId)) {
15752
+ try {
15753
+ await runtime5.apps.activate(result.appId);
15754
+ } catch {
15755
+ }
15756
+ }
15757
+ }
15758
+ }
15759
+ return config;
15760
+ };
15761
+ originalSetOverrideFetcher(wrappedFetcher);
15762
+ };
15763
+ }
15678
15764
  return { canvas, runtime: runtime5, experiments, telemetry, sessionMetrics, appLoader };
15679
15765
  }
15680
15766