@syntrologie/adapt-overlays 2.19.0 → 2.20.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.
@@ -0,0 +1,38 @@
1
+ /**
2
+ * CTA navigation helper.
3
+ *
4
+ * Shared between modal and tooltip executors so the rules for "should this
5
+ * button navigate, and how" live in one place.
6
+ */
7
+ /**
8
+ * Returns true if the href is safe to pass to window.location.assign or
9
+ * window.open.
10
+ *
11
+ * Uses the URL parser rather than string prefix matching, because browsers
12
+ * strip embedded control characters (\\t, \\n, \\r, etc.) from schemes before
13
+ * resolving — so a naive `.trim().toLowerCase().startsWith('javascript:')`
14
+ * check is bypassed by inputs like `"java\\tscript:alert(1)"`. Running the
15
+ * input through `new URL()` first canonicalizes the scheme using the same
16
+ * algorithm the browser uses, then we check the resolved protocol against an
17
+ * allowlist (http/https/mailto/tel).
18
+ *
19
+ * This is defense-in-depth — the schema layer should already have rejected
20
+ * unsafe values via Zod regex + refine, but SDK consumers building configs
21
+ * outside the parser should still be safe at runtime.
22
+ */
23
+ export declare function isSafeNavigationHref(href: string): boolean;
24
+ export interface NavigateCtaInput {
25
+ actionId: string;
26
+ href?: string;
27
+ target?: '_self' | '_blank';
28
+ }
29
+ /**
30
+ * Navigate per the CTA's href/target. Returns true if a navigation was
31
+ * performed (caller can use this to decide whether to also destroy the
32
+ * overlay). Returns false for: missing href, dismiss action, or unsafe href.
33
+ *
34
+ * Callers should publish their telemetry event BEFORE calling this so a
35
+ * full-page navigation doesn't drop the click event in flight.
36
+ */
37
+ export declare function navigateForCta(input: NavigateCtaInput): boolean;
38
+ //# sourceMappingURL=cta-navigation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cta-navigation.d.ts","sourceRoot":"","sources":["../src/cta-navigation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAU1D;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAW/D"}
@@ -1 +1 @@
1
- {"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["../src/modal.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAkB,WAAW,EAAE,MAAM,SAAS,CAAC;AAW3E;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,WAAW,CAkOpD,CAAC"}
1
+ {"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["../src/modal.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAkB,WAAW,EAAE,MAAM,SAAS,CAAC;AAW3E;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,WAAW,CAwOpD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EAEX,eAAe,EACf,WAAW,EACX,aAAa,EACd,MAAM,SAAS,CAAC;AAIjB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;AAGrC,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAMvE;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAAc,CAAC,eAAe,CAsE5D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,WAAW,CA0GpD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,WAAW,CAoGpD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,aAAa,CAwFxD,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;EAQZ,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkBnB,CAAC"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EAEX,eAAe,EACf,WAAW,EACX,aAAa,EACd,MAAM,SAAS,CAAC;AAIjB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;AAGrC,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAMvE;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAAc,CAAC,eAAe,CAsE5D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,WAAW,CA0GpD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,WAAW,CAoGpD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,aAAa,CA4FxD,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;EAQZ,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkBnB,CAAC"}
package/dist/runtime.js CHANGED
@@ -436,6 +436,30 @@ var executeCelebrate = async (action, context) => {
436
436
  };
437
437
  };
438
438
 
439
+ // src/cta-navigation.ts
440
+ var ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
441
+ function isSafeNavigationHref(href) {
442
+ if (typeof href !== "string" || href.trim().length === 0) return false;
443
+ let parsed;
444
+ try {
445
+ parsed = new URL(href, "https://syntro.local/");
446
+ } catch {
447
+ return false;
448
+ }
449
+ return ALLOWED_PROTOCOLS.has(parsed.protocol);
450
+ }
451
+ function navigateForCta(input) {
452
+ if (input.actionId === "dismiss") return false;
453
+ if (!input.href || !isSafeNavigationHref(input.href)) return false;
454
+ const href = input.href;
455
+ if (input.target === "_blank") {
456
+ window.open(href, "_blank", "noopener,noreferrer");
457
+ } else {
458
+ window.location.assign(href);
459
+ }
460
+ return true;
461
+ }
462
+
439
463
  // src/executors/tour.ts
440
464
  var ACTIVE_TOUR_KEY = "syntro_active_tour";
441
465
  var activeTours = /* @__PURE__ */ new Map();
@@ -1416,10 +1440,16 @@ var executeModal = async (action, context) => {
1416
1440
  const actionId = btn.getAttribute("data-syntro-action");
1417
1441
  if (actionId) {
1418
1442
  actionClicked = actionId;
1443
+ const matchingBtn = ctaButtons?.find((b) => b.actionId === actionId);
1444
+ const href = matchingBtn?.href;
1419
1445
  context.publishEvent("action.modal_cta_clicked", {
1420
- actionId
1446
+ actionId,
1447
+ ...href ? { href } : {}
1421
1448
  });
1422
1449
  handle.destroy();
1450
+ if (matchingBtn) {
1451
+ navigateForCta(matchingBtn);
1452
+ }
1423
1453
  }
1424
1454
  };
1425
1455
  actionBtns.forEach((btn) => btn.addEventListener("click", actionHandler));
@@ -2594,8 +2624,12 @@ var executeTooltip = async (action, context) => {
2594
2624
  context.publishEvent("action.tooltip_cta_clicked", {
2595
2625
  anchorId: action.anchorId,
2596
2626
  actionId,
2597
- label: clickedBtn.label
2627
+ label: clickedBtn.label,
2628
+ ...clickedBtn.href ? { href: clickedBtn.href } : {}
2598
2629
  });
2630
+ handle.destroy();
2631
+ navigateForCta(clickedBtn);
2632
+ return;
2599
2633
  }
2600
2634
  }
2601
2635
  handle.destroy();