@syntrologie/runtime-sdk 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.
@@ -3136,6 +3136,28 @@ var SyntrologieSDK = (() => {
3136
3136
  }
3137
3137
  };
3138
3138
  };
3139
+ var ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
3140
+ function isSafeNavigationHref(href) {
3141
+ if (typeof href !== "string" || href.trim().length === 0) return false;
3142
+ let parsed;
3143
+ try {
3144
+ parsed = new URL(href, "https://syntro.local/");
3145
+ } catch {
3146
+ return false;
3147
+ }
3148
+ return ALLOWED_PROTOCOLS.has(parsed.protocol);
3149
+ }
3150
+ function navigateForCta(input) {
3151
+ if (input.actionId === "dismiss") return false;
3152
+ if (!input.href || !isSafeNavigationHref(input.href)) return false;
3153
+ const href = input.href;
3154
+ if (input.target === "_blank") {
3155
+ window.open(href, "_blank", "noopener,noreferrer");
3156
+ } else {
3157
+ window.location.assign(href);
3158
+ }
3159
+ return true;
3160
+ }
3139
3161
  var ACTIVE_TOUR_KEY = "syntro_active_tour";
3140
3162
  var activeTours = /* @__PURE__ */ new Map();
3141
3163
  function getTourState(tourId) {
@@ -4105,10 +4127,16 @@ var SyntrologieSDK = (() => {
4105
4127
  const actionId = btn.getAttribute("data-syntro-action");
4106
4128
  if (actionId) {
4107
4129
  actionClicked = actionId;
4130
+ const matchingBtn = ctaButtons?.find((b6) => b6.actionId === actionId);
4131
+ const href = matchingBtn?.href;
4108
4132
  context.publishEvent("action.modal_cta_clicked", {
4109
- actionId
4133
+ actionId,
4134
+ ...href ? { href } : {}
4110
4135
  });
4111
4136
  handle.destroy();
4137
+ if (matchingBtn) {
4138
+ navigateForCta(matchingBtn);
4139
+ }
4112
4140
  }
4113
4141
  };
4114
4142
  actionBtns.forEach((btn) => btn.addEventListener("click", actionHandler));
@@ -5256,8 +5284,12 @@ var SyntrologieSDK = (() => {
5256
5284
  context.publishEvent("action.tooltip_cta_clicked", {
5257
5285
  anchorId: action.anchorId,
5258
5286
  actionId,
5259
- label: clickedBtn.label
5287
+ label: clickedBtn.label,
5288
+ ...clickedBtn.href ? { href: clickedBtn.href } : {}
5260
5289
  });
5290
+ handle.destroy();
5291
+ navigateForCta(clickedBtn);
5292
+ return;
5261
5293
  }
5262
5294
  }
5263
5295
  handle.destroy();
@@ -16696,7 +16728,7 @@ Please report this to https://github.com/markedjs/marked.`, e10) {
16696
16728
  }
16697
16729
 
16698
16730
  // src/version.ts
16699
- var SDK_VERSION = "2.19.0";
16731
+ var SDK_VERSION = "2.20.0";
16700
16732
 
16701
16733
  // src/types.ts
16702
16734
  var SDK_SCHEMA_VERSION = "2.0";
@@ -20909,10 +20941,32 @@ ${cssRules}
20909
20941
  "right-start",
20910
20942
  "right-end"
20911
20943
  ]);
20944
+ var SAFE_HREF_PATTERN = /^(?:[/.#?]|[hH][tT][tT][pP][sS]?:\/\/|[mM][aA][iI][lL][tT][oO]:|[tT][eE][lL]:)/;
20945
+ var HrefZ = external_exports.string().min(1).regex(SAFE_HREF_PATTERN, {
20946
+ message: "href must start with /, ./, ../, #, ?, http(s)://, mailto:, or tel: \u2014 javascript:, data:, and vbscript: are not allowed"
20947
+ }).refine(
20948
+ (s9) => {
20949
+ try {
20950
+ const parsed = new URL(s9, "https://syntro.local/");
20951
+ return ["http:", "https:", "mailto:", "tel:"].includes(parsed.protocol);
20952
+ } catch {
20953
+ return false;
20954
+ }
20955
+ },
20956
+ { message: "href resolves to an unsupported protocol" }
20957
+ );
20912
20958
  var CtaButtonZ = external_exports.object({
20913
- label: external_exports.string(),
20914
- actionId: external_exports.string(),
20915
- primary: external_exports.boolean().optional()
20959
+ label: external_exports.string().describe("Visible button label."),
20960
+ actionId: external_exports.string().describe(
20961
+ 'Identifier emitted on action.modal_cta_clicked / action.tooltip_cta_clicked. Use "dismiss" to close the overlay without publishing an event or navigating, even if href is set.'
20962
+ ),
20963
+ primary: external_exports.boolean().optional().describe("When true, renders as the primary (filled) button. Defaults to false."),
20964
+ href: HrefZ.optional().describe(
20965
+ 'URL to navigate to when the button is clicked. Use this to drive users to a destination (product page, signup, etc.) \u2014 set href on a non-dismiss button rather than relying on actionId conventions or content:setAttr tricks. Accepts relative paths ("/product/x"), fragments ("#section"), query strings, and absolute http(s) URLs. javascript:/data:/vbscript: are rejected.'
20966
+ ),
20967
+ target: external_exports.enum(["_self", "_blank"]).optional().describe(
20968
+ 'Navigation target. "_self" (default) replaces the current tab; "_blank" opens a new tab with noopener,noreferrer.'
20969
+ )
20916
20970
  }).strict();
20917
20971
  var TooltipContentZ = external_exports.object({
20918
20972
  title: external_exports.string().optional(),
@@ -34746,7 +34800,7 @@ ${cssRules}
34746
34800
  }
34747
34801
 
34748
34802
  // src/index.ts
34749
- var RUNTIME_SDK_BUILD = true ? `${"2026-05-06T04:46:30.034Z"} (${"542c106817c"})` : "dev";
34803
+ var RUNTIME_SDK_BUILD = true ? `${"2026-05-07T04:09:15.562Z"} (${"e170c20927b"})` : "dev";
34750
34804
  if (typeof window !== "undefined") {
34751
34805
  console.log(`[Syntro Runtime] Build: ${RUNTIME_SDK_BUILD} (Lit)`);
34752
34806
  const existing = window.SynOS;