kenobi-js 0.1.42 → 0.1.43

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/browser/dist.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Kenobi SDK v0.1.41
2
+ * Kenobi SDK v0.1.42
3
3
  * (c) 2026 Kenobi.ai
4
4
  */
5
5
  "use strict";
@@ -1129,6 +1129,200 @@ var KenobiLib = (() => {
1129
1129
  )
1130
1130
  }
1131
1131
  ), "ArrowPathIcon");
1132
+ var IDENTITY_RESOLVED_EVENT = "kenobi:identity-resolved";
1133
+ var KENOBI_SNITCHER_SCRIPT_ID = "kenobi-snitcher";
1134
+ var DEFAULT_DEANON_TIMEOUT_MS = 1600;
1135
+ var dispatchCueCardEvent = /* @__PURE__ */ __name((eventName, detail) => {
1136
+ if (typeof window === "undefined") return;
1137
+ window.dispatchEvent(
1138
+ new CustomEvent(`kenobi:${eventName}`, {
1139
+ detail,
1140
+ bubbles: true
1141
+ })
1142
+ );
1143
+ }, "dispatchCueCardEvent");
1144
+ var resolveDeanonymizedCompany = /* @__PURE__ */ __name((detail) => {
1145
+ if (!detail || detail.type !== "business") return null;
1146
+ const company = detail.company;
1147
+ const name = company?.name || detail.domain || void 0;
1148
+ const domain = company?.domain || detail.domain || void 0;
1149
+ if (!name) return null;
1150
+ return {
1151
+ name,
1152
+ domain,
1153
+ logoUrl: company?.logoUrl || (domain ? `https://logo.clearbit.com/${domain}` : void 0)
1154
+ };
1155
+ }, "resolveDeanonymizedCompany");
1156
+ var normalizeDomain = /* @__PURE__ */ __name((value) => {
1157
+ if (!value) return "";
1158
+ return value.replace(/^https?:\/\//i, "").replace(/\/.*$/, "").replace(/:\d+$/, "").replace(/^www\./i, "").trim().toLowerCase();
1159
+ }, "normalizeDomain");
1160
+ var ensureKenobiSnitcherLoaded = /* @__PURE__ */ __name((config) => {
1161
+ if (typeof window === "undefined" || typeof document === "undefined") return;
1162
+ if (window.__kenobiSnitcherLoaded || window.__kenobiSnitcherLoading) return;
1163
+ if (window.Snitcher || window.SpotterSettings) {
1164
+ window.__kenobiSnitcherLoaded = true;
1165
+ return;
1166
+ }
1167
+ if (document.getElementById(KENOBI_SNITCHER_SCRIPT_ID)) {
1168
+ window.__kenobiSnitcherLoaded = true;
1169
+ return;
1170
+ }
1171
+ const snitcher = config?.snitcher;
1172
+ if (!snitcher?.spotterToken || !snitcher.profileId) {
1173
+ return;
1174
+ }
1175
+ window.__kenobiSnitcherLoading = true;
1176
+ const resolvedLogoTemplate = config?.logoUrlTemplate || "https://logo.clearbit.com/{domain}";
1177
+ const apiEndpoint = snitcher.apiEndpoint || `${window.location.origin}/api/snitcher-proxy/radar`;
1178
+ const cdn = snitcher.cdn || `${window.location.origin}/api/snitcher-proxy/cdn`;
1179
+ const namespace = snitcher.namespace || "Snitcher";
1180
+ const snitcherScript = `
1181
+ (function () {
1182
+ function spotterCb(response) {
1183
+ var rawDomain = (response.company && response.company.domain) || response.domain || "";
1184
+ var normalizedDomain = rawDomain
1185
+ .replace(/^https?:\\/\\//i, "")
1186
+ .replace(/\\/.*$/, "")
1187
+ .replace(/:\\d+$/, "")
1188
+ .replace(/^www\\./i, "");
1189
+ var identity = {
1190
+ provider: "snitcher",
1191
+ type: response.type === "business" ? "business" : "unknown",
1192
+ domain: response.domain || null,
1193
+ ip: response.ip,
1194
+ company: response.company
1195
+ ? {
1196
+ id: response.company.id,
1197
+ name: response.company.name || response.domain,
1198
+ domain: response.company.domain || response.domain,
1199
+ logoUrl: "${resolvedLogoTemplate}".replace("{domain}", normalizedDomain),
1200
+ industry: response.company.industry,
1201
+ location: response.company.location,
1202
+ employeeRange: response.company.employee_range,
1203
+ foundedYear: response.company.founded_year,
1204
+ description: response.company.description,
1205
+ geo: response.company.geo
1206
+ ? {
1207
+ city: response.company.geo.city,
1208
+ country: response.company.geo.country,
1209
+ countryCode: response.company.geo.country_code,
1210
+ region: response.company.geo.region,
1211
+ postalCode: response.company.geo.postal_code,
1212
+ address:
1213
+ response.company.geo.full_address ||
1214
+ response.company.geo.address,
1215
+ }
1216
+ : undefined,
1217
+ profiles: response.company.profiles
1218
+ ? {
1219
+ linkedin: response.company.profiles.linkedin?.url,
1220
+ twitter: response.company.profiles.twitter?.url,
1221
+ facebook: response.company.profiles.facebook?.url,
1222
+ crunchbase: response.company.profiles.crunchbase?.url,
1223
+ }
1224
+ : undefined,
1225
+ }
1226
+ : undefined,
1227
+ raw: response,
1228
+ };
1229
+
1230
+ window.__kenobiIdentityResolution = identity;
1231
+ window.dispatchEvent(
1232
+ new CustomEvent("${IDENTITY_RESOLVED_EVENT}", {
1233
+ detail: identity,
1234
+ bubbles: true,
1235
+ })
1236
+ );
1237
+ }
1238
+
1239
+ window.SpotterSettings = {
1240
+ token: "${snitcher.spotterToken}",
1241
+ callback: spotterCb,
1242
+ };
1243
+
1244
+ !(function (e) {
1245
+ "use strict";
1246
+ var t = e && e.namespace;
1247
+ if (t && e.profileId && e.cdn) {
1248
+ var i = window[t];
1249
+ if (((i && Array.isArray(i)) || (i = window[t] = []), !i.initialized && !i._loaded))
1250
+ if (i._loaded) console && console.warn("[Radar] Duplicate initialization attempted");
1251
+ else {
1252
+ i._loaded = !0;
1253
+ [
1254
+ "track",
1255
+ "page",
1256
+ "identify",
1257
+ "group",
1258
+ "alias",
1259
+ "ready",
1260
+ "debug",
1261
+ "on",
1262
+ "off",
1263
+ "once",
1264
+ "trackClick",
1265
+ "trackSubmit",
1266
+ "trackLink",
1267
+ "trackForm",
1268
+ "pageview",
1269
+ "screen",
1270
+ "reset",
1271
+ "register",
1272
+ "setAnonymousId",
1273
+ "addSourceMiddleware",
1274
+ "addIntegrationMiddleware",
1275
+ "addDestinationMiddleware",
1276
+ "giveCookieConsent",
1277
+ ].forEach(function (e) {
1278
+ var a;
1279
+ i[e] =
1280
+ ((a = e),
1281
+ function () {
1282
+ var e = window[t];
1283
+ if (e.initialized) return e[a].apply(e, arguments);
1284
+ var i = [].slice.call(arguments);
1285
+ return i.unshift(a), e.push(i), e;
1286
+ });
1287
+ }),
1288
+ -1 === e.apiEndpoint.indexOf("http") &&
1289
+ (e.apiEndpoint = "https://" + e.apiEndpoint),
1290
+ (i.bootstrap = function () {
1291
+ var t,
1292
+ i = document.createElement("script");
1293
+ (i.async = !0),
1294
+ (i.type = "text/javascript"),
1295
+ (i.id = "__radar__"),
1296
+ i.setAttribute("data-settings", JSON.stringify(e)),
1297
+ (i.src = [
1298
+ -1 !== (t = e.cdn).indexOf("http") ? "" : "https://",
1299
+ t,
1300
+ "/releases/latest/radar.min.js",
1301
+ ].join(""));
1302
+ var a = document.scripts[0];
1303
+ a.parentNode.insertBefore(i, a);
1304
+ }),
1305
+ i.bootstrap();
1306
+ }
1307
+ } else
1308
+ "undefined" != typeof console &&
1309
+ console.error("[Radar] Configuration incomplete");
1310
+ })({
1311
+ apiEndpoint: "${apiEndpoint}",
1312
+ cdn: "${cdn}",
1313
+ namespace: "${namespace}",
1314
+ profileId: "${snitcher.profileId}",
1315
+ });
1316
+ })();
1317
+ `;
1318
+ const script = document.createElement("script");
1319
+ script.id = KENOBI_SNITCHER_SCRIPT_ID;
1320
+ script.type = "text/javascript";
1321
+ script.text = snitcherScript;
1322
+ document.head?.appendChild(script);
1323
+ window.__kenobiSnitcherLoaded = true;
1324
+ window.__kenobiSnitcherLoading = false;
1325
+ }, "ensureKenobiSnitcherLoaded");
1132
1326
  var STYLES = `
1133
1327
  :host { --kb-font-family: system-ui, -apple-system, sans-serif; --kb-bg-container: rgba(255, 255, 255, 0.05); --kb-border-container: rgba(255, 255, 255, 0.2); --kb-shadow-container: 0 25px 50px -12px rgba(0, 0, 0, 0.25); --kb-backdrop-blur: 16px; --kb-text-title: #ffffff; --kb-text-subtitle: rgba(255, 255, 255, 0.7); --kb-btn-dismiss-bg: rgba(0, 0, 0, 0.7); --kb-btn-dismiss-border: rgba(255, 255, 255, 0.2); --kb-btn-dismiss-text: rgba(255, 255, 255, 0.8); --kb-btn-dismiss-hover-bg: rgba(0, 0, 0, 0.9); --kb-btn-dismiss-hover-text: #ffffff; --kb-btn-trigger-bg: #ffffff; --kb-btn-trigger-text: #000000; --kb-btn-trigger-hover-bg: rgba(255, 255, 255, 0.9); --kb-progress-track: rgba(255, 255, 255, 0.2); --kb-progress-indicator: #ffffff; --kb-popover-bg: rgba(255, 255, 255, 0.05); --kb-popover-border: rgba(255, 255, 255, 0.2); --kb-popover-text: #ffffff; --kb-input-bg: transparent; --kb-input-border: rgba(255, 255, 255, 0.3); --kb-input-text: #ffffff; --kb-input-placeholder: rgba(255, 255, 255, 0.6); --kb-form-label: #ffffff; --kb-success-color: #6ee7b7; --kb-error-text: #ef4444; --kb-focus-blur: 10px; --kb-kbd-bg: rgba(255, 255, 255, 0.15); --kb-kbd-border: rgba(255, 255, 255, 0.4); --kb-kbd-text: #ffffff; --kb-watermark-text: rgba(255, 255, 255, 0.4); font-family: var(--kb-font-family); }
1134
1328
  .theme-light { --kb-bg-container: #ffffff; --kb-border-container: #e2e8f0; --kb-shadow-container: 0 20px 25px -5px rgba(0, 0, 0, 0.1); --kb-backdrop-blur: 0px; --kb-text-title: #0f172a; --kb-text-subtitle: #475569; --kb-btn-dismiss-bg: #ffffff; --kb-btn-dismiss-border: #e2e8f0; --kb-btn-dismiss-text: #64748b; --kb-btn-dismiss-hover-bg: #f1f5f9; --kb-btn-dismiss-hover-text: #0f172a; --kb-btn-trigger-bg: #0f172a; --kb-btn-trigger-text: #ffffff; --kb-btn-trigger-hover-bg: #1e293b; --kb-progress-track: #e2e8f0; --kb-progress-indicator: #0f172a; --kb-popover-bg: #ffffff; --kb-popover-border: #e2e8f0; --kb-popover-text: #0f172a; --kb-input-bg: #ffffff; --kb-input-border: #cbd5e1; --kb-input-text: #0f172a; --kb-input-placeholder: #94a3b8; --kb-form-label: #334155; --kb-success-color: #059669; --kb-error-text: #ef4444; --kb-kbd-bg: #f1f5f9; --kb-kbd-border: #e2e8f0; --kb-kbd-text: #64748b; --kb-watermark-text: #94a3b8; }
@@ -1142,6 +1336,17 @@ var KenobiLib = (() => {
1142
1336
  .launcher .kbd-hint .kbd { background: var(--kb-kbd-bg); border: 1px solid var(--kb-kbd-border); color: var(--kb-kbd-text); }
1143
1337
  .launcher .kbd-hint .kbd-text { color: var(--kb-kbd-text); }
1144
1338
  .launcher:hover { transform: scale(1.02); background-color: var(--kb-bg-container); }
1339
+ .kb-chip-row { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; }
1340
+ .kb-chip { display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.35rem 0.8rem; border-radius: 9999px; border: 1px solid rgba(255,255,255,0.85); background: linear-gradient(135deg, rgba(255,255,255,0.28), rgba(255,255,255,0.08)); color: var(--kb-text-title); font-size: 0.8rem; font-weight: 600; backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); box-shadow: 0 8px 18px rgba(0,0,0,0.4), inset 0 0 0 1px rgba(255,255,255,0.45); }
1341
+ .kb-chip-logo { width: 18px; height: 18px; border-radius: 9999px; overflow: hidden; background: rgba(255,255,255,0.2); display: flex; align-items: center; justify-content: center; }
1342
+ .kb-chip-logo img { width: 100%; height: 100%; object-fit: cover; display: block; }
1343
+ .kb-chip-fallback { width: 18px; height: 18px; border-radius: 9999px; display: flex; align-items: center; justify-content: center; font-size: 0.7rem; font-weight: 700; color: var(--kb-text-title); background: rgba(255,255,255,0.25); }
1344
+ .kb-chip-action { margin-left: 0.35rem; font-size: 0.7rem; color: var(--kb-text-subtitle); text-decoration: underline; background: none; border: none; padding: 0; cursor: pointer; }
1345
+ .kb-chip-action:hover { color: var(--kb-text-title); }
1346
+ .kb-chip--launcher { padding: 0.25rem 0.65rem; font-size: 0.7rem; }
1347
+ .kb-chip-row.launcher-row { margin-bottom: 0; }
1348
+ .launcher-label { display: inline-flex; align-items: center; gap: 0.5rem; }
1349
+ .subtitle-chip { margin-top: 0.35rem; }
1145
1350
  .pos-top-center { left: 50%; translate: -50% 0; top: 1rem; }
1146
1351
  .pos-top-right { right: 1rem; top: 1rem; left: auto; }
1147
1352
  @media (max-width: 768px) { .pos-top-center, .pos-top-right { left: 0.5rem; right: 0.5rem; top: 0.5rem; translate: none; width: auto; max-width: none; } }
@@ -1156,7 +1361,7 @@ var KenobiLib = (() => {
1156
1361
  .text-content { flex: 1; user-select: none; }
1157
1362
  .text-row { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; }
1158
1363
  .title { color: var(--kb-text-title); font-size: 1rem; font-weight: 600; line-height: 1.5; }
1159
- .subtitle { color: var(--kb-text-title); font-size: 0.875rem; line-height: 1.25; opacity: 0.8; }
1364
+ .subtitle-text { color: var(--kb-text-title); font-size: 0.875rem; line-height: 1.25; opacity: 0.8; }
1160
1365
  .btn { display: inline-flex; align-items: center; justify-content: center; border-radius: 0.375rem; font-weight: 500; font-size: 0.875rem; line-height: 1.25rem; padding: 0.5rem 1rem; transition: all 0.2s; cursor: pointer; border: none; outline: none; }
1161
1366
  .btn-trigger { background-color: var(--kb-btn-trigger-bg); color: var(--kb-btn-trigger-text); min-width: 8rem; gap: 0.5rem; transition: opacity 0.2s ease-out, background-color 0.2s ease-out; }
1162
1367
  .btn-trigger.hidden { opacity: 0; pointer-events: none; }
@@ -1321,7 +1526,14 @@ var KenobiLib = (() => {
1321
1526
  }
1322
1527
  return styles;
1323
1528
  }, "getThemeStyles");
1324
- var CueCardContent = /* @__PURE__ */ __name(({ config, isOpen, skipFocus, setIsOpen }) => {
1529
+ var CueCardContent = /* @__PURE__ */ __name(({
1530
+ config,
1531
+ isOpen,
1532
+ skipFocus,
1533
+ setIsOpen,
1534
+ deanonymizedCompany,
1535
+ onClearDeanonymizedCompany
1536
+ }) => {
1325
1537
  const isInline = config.mountMode === "inline";
1326
1538
  const containerRef = A2(null);
1327
1539
  const launcherRef = A2(null);
@@ -1348,6 +1560,14 @@ var KenobiLib = (() => {
1348
1560
  const timerProgressRef = A2(0);
1349
1561
  const timerEnabled = config.enableTimer !== false;
1350
1562
  const timerDuration = Math.max(500, config.timerDurationMs ?? 2e4);
1563
+ const isPending = config.status === "starting";
1564
+ const isSuccess = config.status === "success";
1565
+ const isError = config.status === "error";
1566
+ const isDeanonymized = !!deanonymizedCompany;
1567
+ const isPopoverEnabled = !isDeanonymized;
1568
+ const isPopoverOpen = isPopoverEnabled ? isOpen : false;
1569
+ const deanonymizedLabel = deanonymizedCompany?.name || deanonymizedCompany?.domain || "";
1570
+ const deanonymizedDomain = deanonymizedCompany?.domain;
1351
1571
  const setTimerProgress = q2((value) => {
1352
1572
  timerProgressRef.current = value;
1353
1573
  setTimerProgressState(value);
@@ -1415,7 +1635,7 @@ var KenobiLib = (() => {
1415
1635
  skipFocusRef.current = true;
1416
1636
  }
1417
1637
  y2(() => {
1418
- if (isOpen) {
1638
+ if (isPopoverOpen) {
1419
1639
  if (hasHadSuccess && formRef.current) {
1420
1640
  formRef.current.reset();
1421
1641
  }
@@ -1430,15 +1650,15 @@ var KenobiLib = (() => {
1430
1650
  return () => clearTimeout(focusTimer);
1431
1651
  }
1432
1652
  }
1433
- }, [isOpen]);
1434
- const prevIsOpenForExpandRef = A2(isOpen);
1653
+ }, [isPopoverOpen]);
1654
+ const prevIsOpenForExpandRef = A2(isPopoverOpen);
1435
1655
  y2(() => {
1436
1656
  const wasOpen = prevIsOpenForExpandRef.current;
1437
- prevIsOpenForExpandRef.current = isOpen;
1438
- if (!wasOpen && isOpen && mode === "launcher") {
1657
+ prevIsOpenForExpandRef.current = isPopoverOpen;
1658
+ if (!wasOpen && isPopoverOpen && mode === "launcher") {
1439
1659
  setMode("card");
1440
1660
  }
1441
- }, [isOpen, mode]);
1661
+ }, [isPopoverOpen, mode]);
1442
1662
  const isFirstAppearance = !hasEnteredRef.current;
1443
1663
  const wasLauncherPreviously = previousModeRef.current === "launcher";
1444
1664
  const cardEntranceDelay = isFirstAppearance ? config.entranceDelayMs || 0 : wasLauncherPreviously && mode === "card" ? 400 : 0;
@@ -1528,7 +1748,7 @@ var KenobiLib = (() => {
1528
1748
  e3.preventDefault();
1529
1749
  if (mode === "launcher") {
1530
1750
  setMode("card");
1531
- setIsOpen(true);
1751
+ setIsOpen(isPopoverEnabled);
1532
1752
  } else if (mode === "card") {
1533
1753
  if (config.enableLauncher) {
1534
1754
  setMode("launcher");
@@ -1542,11 +1762,12 @@ var KenobiLib = (() => {
1542
1762
  return () => window.removeEventListener("keydown", handleKey);
1543
1763
  }, [
1544
1764
  config.isVisible,
1545
- isOpen,
1765
+ isPopoverOpen,
1546
1766
  mode,
1547
1767
  config.enableLauncher,
1548
1768
  setIsOpen,
1549
- config.onDismiss
1769
+ config.onDismiss,
1770
+ isPopoverEnabled
1550
1771
  ]);
1551
1772
  const isMac = typeof navigator !== "undefined" && /Mac/.test(navigator.userAgent);
1552
1773
  const shortcutDisplay = (() => {
@@ -1562,9 +1783,24 @@ var KenobiLib = (() => {
1562
1783
  }
1563
1784
  return [isMac ? "\u2318" : "Ctrl", "P"];
1564
1785
  })();
1565
- const isPending = config.status === "starting";
1566
- const isSuccess = config.status === "success";
1567
- const isError = config.status === "error";
1786
+ const [logoFailed, setLogoFailed] = d2(false);
1787
+ const [logoAttempt, setLogoAttempt] = d2(
1788
+ "primary"
1789
+ );
1790
+ y2(() => {
1791
+ setLogoFailed(false);
1792
+ setLogoAttempt("primary");
1793
+ }, [deanonymizedCompany?.logoUrl, deanonymizedLabel, deanonymizedDomain]);
1794
+ y2(() => {
1795
+ if (!isPopoverEnabled && isOpen) {
1796
+ setIsOpen(false);
1797
+ }
1798
+ }, [isPopoverEnabled, isOpen, setIsOpen]);
1799
+ const logoUrlTemplate = config.deanonymization?.logoUrlTemplate;
1800
+ const normalizedLogoDomain = normalizeDomain(deanonymizedDomain);
1801
+ const fallbackLogoUrl = normalizedLogoDomain ? `https://logo.clearbit.com/${normalizedLogoDomain}` : void 0;
1802
+ const primaryLogoUrl = normalizedLogoDomain && logoUrlTemplate ? logoUrlTemplate.replace("{domain}", normalizedLogoDomain) : deanonymizedCompany?.logoUrl || fallbackLogoUrl;
1803
+ const resolvedLogoUrl = logoAttempt === "fallback" && fallbackLogoUrl && fallbackLogoUrl !== primaryLogoUrl ? fallbackLogoUrl : primaryLogoUrl;
1568
1804
  const showAsSuccess = isSuccess && isShowingInitialSuccessRef.current;
1569
1805
  const txt = config.textOverrides || {};
1570
1806
  const titleText = (() => {
@@ -1591,6 +1827,94 @@ var KenobiLib = (() => {
1591
1827
  }
1592
1828
  return txt.btnResting || "Show me";
1593
1829
  })();
1830
+ const launcherLabelText = (() => {
1831
+ if (isDeanonymized) {
1832
+ if (hasHadSuccess) {
1833
+ return txt.launcherLabelDeanonymizedSuccess || txt.launcherLabelDeanonymized || "See it again for";
1834
+ }
1835
+ return txt.launcherLabelDeanonymized || "See it in action for";
1836
+ }
1837
+ if (hasHadSuccess) {
1838
+ return txt.launcherLabelSuccess || txt.launcherLabel || "Personalize again";
1839
+ }
1840
+ return txt.launcherLabel || "Personalize";
1841
+ })();
1842
+ const renderDeanonymizedChip = /* @__PURE__ */ __name((variant) => {
1843
+ if (!deanonymizedCompany || !deanonymizedLabel) return null;
1844
+ const isLauncher = variant === "launcher";
1845
+ const fallbackLetter = deanonymizedLabel.trim().slice(0, 1).toUpperCase();
1846
+ const showLogo = !!resolvedLogoUrl && !logoFailed;
1847
+ return /* @__PURE__ */ u3("div", { class: `kb-chip ${isLauncher ? "kb-chip--launcher" : ""}`, children: [
1848
+ showLogo ? /* @__PURE__ */ u3("span", { class: "kb-chip-logo", children: /* @__PURE__ */ u3(
1849
+ "img",
1850
+ {
1851
+ src: resolvedLogoUrl,
1852
+ alt: "",
1853
+ referrerPolicy: "no-referrer",
1854
+ onError: () => {
1855
+ if (logoAttempt === "primary" && fallbackLogoUrl && fallbackLogoUrl !== primaryLogoUrl) {
1856
+ setLogoAttempt("fallback");
1857
+ return;
1858
+ }
1859
+ setLogoFailed(true);
1860
+ }
1861
+ }
1862
+ ) }) : /* @__PURE__ */ u3("span", { class: "kb-chip-fallback", children: fallbackLetter }),
1863
+ /* @__PURE__ */ u3("span", { children: deanonymizedLabel }),
1864
+ onClearDeanonymizedCompany && /* @__PURE__ */ u3(
1865
+ "button",
1866
+ {
1867
+ class: "kb-chip-action",
1868
+ type: "button",
1869
+ onClick: (e3) => {
1870
+ e3.stopPropagation();
1871
+ onClearDeanonymizedCompany();
1872
+ setIsOpen(true);
1873
+ },
1874
+ children: "Not you?"
1875
+ }
1876
+ )
1877
+ ] });
1878
+ }, "renderDeanonymizedChip");
1879
+ const buildDeanonymizedValues = /* @__PURE__ */ __name(() => {
1880
+ const normalizedInputDomain = normalizeDomain(deanonymizedDomain);
1881
+ const fallbackValue = (normalizedInputDomain || deanonymizedDomain || deanonymizedLabel || "").toString();
1882
+ const values = {};
1883
+ config.fields.forEach((field) => {
1884
+ const key = field.name;
1885
+ const lowerKey = key.toLowerCase();
1886
+ if (lowerKey.includes("domain")) {
1887
+ values[key] = normalizedInputDomain || fallbackValue;
1888
+ return;
1889
+ }
1890
+ if (lowerKey.includes("company")) {
1891
+ values[key] = deanonymizedLabel;
1892
+ return;
1893
+ }
1894
+ values[key] = fallbackValue;
1895
+ });
1896
+ return values;
1897
+ }, "buildDeanonymizedValues");
1898
+ const submitValues = /* @__PURE__ */ __name((values) => {
1899
+ const newErrors = {};
1900
+ let hasErrors = false;
1901
+ config.fields.forEach((field) => {
1902
+ const rawValue = values[field.name] ?? "";
1903
+ const value = typeof rawValue === "string" ? rawValue : String(rawValue);
1904
+ const error = validateField(field.name, value, field);
1905
+ if (error) {
1906
+ newErrors[field.name] = error;
1907
+ hasErrors = true;
1908
+ }
1909
+ });
1910
+ if (hasErrors) {
1911
+ setErrors(newErrors);
1912
+ return;
1913
+ }
1914
+ setErrors({});
1915
+ setIsOpen(false);
1916
+ config.onSubmitPersonalization?.(values);
1917
+ }, "submitValues");
1594
1918
  const validateField = /* @__PURE__ */ __name((name, value, fieldConfig) => {
1595
1919
  if (!fieldConfig) return null;
1596
1920
  if (fieldConfig.required && !value.trim()) {
@@ -1629,25 +1953,11 @@ var KenobiLib = (() => {
1629
1953
  if (isPending) return;
1630
1954
  const formData = new FormData(e3.target);
1631
1955
  const values = {};
1632
- const newErrors = {};
1633
- let hasErrors = false;
1634
1956
  formData.forEach((value, key) => {
1635
1957
  const strValue = value.toString();
1636
1958
  values[key] = strValue;
1637
- const fieldConfig = config.fields.find((f4) => f4.name === key);
1638
- const error = validateField(key, strValue, fieldConfig);
1639
- if (error) {
1640
- newErrors[key] = error;
1641
- hasErrors = true;
1642
- }
1643
1959
  });
1644
- if (hasErrors) {
1645
- setErrors(newErrors);
1646
- return;
1647
- }
1648
- setErrors({});
1649
- setIsOpen(false);
1650
- config.onSubmitPersonalization?.(values);
1960
+ submitValues(values);
1651
1961
  }, "handleSubmit");
1652
1962
  const handleInput = /* @__PURE__ */ __name((e3) => {
1653
1963
  const target = e3.target;
@@ -1666,10 +1976,15 @@ var KenobiLib = (() => {
1666
1976
  });
1667
1977
  }
1668
1978
  }, "handleInput");
1979
+ const handleDeanonymizedSubmit = /* @__PURE__ */ __name(() => {
1980
+ if (isPending || !isDeanonymized) return;
1981
+ submitValues(buildDeanonymizedValues());
1982
+ }, "handleDeanonymizedSubmit");
1669
1983
  const togglePopover = /* @__PURE__ */ __name((e3) => {
1670
1984
  e3.stopPropagation();
1985
+ if (isDeanonymized) return;
1671
1986
  if (isPending || isSuccess) return;
1672
- const opening = !isOpen;
1987
+ const opening = !isPopoverOpen;
1673
1988
  setIsOpen(opening);
1674
1989
  if (opening && config.enableClickSheen) {
1675
1990
  setSheenActive(true);
@@ -1679,7 +1994,7 @@ var KenobiLib = (() => {
1679
1994
  y2(() => {
1680
1995
  if (!timerEnabled) return;
1681
1996
  if (hasTimerCompletedRef.current) return;
1682
- const shouldRun = config.isVisible && mode === "card" && config.status === "resting" && !isOpen && !isHovering;
1997
+ const shouldRun = config.isVisible && mode === "card" && config.status === "resting" && !isPopoverOpen && !isHovering;
1683
1998
  if (!shouldRun) {
1684
1999
  if (timerRafRef.current !== null) {
1685
2000
  cancelAnimationFrame(timerRafRef.current);
@@ -1723,7 +2038,7 @@ var KenobiLib = (() => {
1723
2038
  config.onDismiss,
1724
2039
  config.status,
1725
2040
  isHovering,
1726
- isOpen,
2041
+ isPopoverOpen,
1727
2042
  mode,
1728
2043
  setIsOpen,
1729
2044
  timerDuration,
@@ -1755,12 +2070,12 @@ var KenobiLib = (() => {
1755
2070
  "div",
1756
2071
  {
1757
2072
  ref: containerRef,
1758
- class: `container theme-${config.theme || "glass"} pos-${config.position || "top-center"}${isOpen ? " popover-open" : ""}`,
2073
+ class: `container theme-${config.theme || "glass"} pos-${config.position || "top-center"}${isPopoverOpen ? " popover-open" : ""}`,
1759
2074
  style: {
1760
2075
  ...getThemeStyles(config.customTheme),
1761
2076
  pointerEvents: isContainerVisible ? "auto" : "none"
1762
2077
  },
1763
- onClick: () => !isOpen && !isPending && !isSuccess && setIsOpen(true),
2078
+ onClick: () => !isPopoverOpen && !isPending && !isSuccess && isPopoverEnabled && setIsOpen(true),
1764
2079
  onMouseEnter: () => setIsHovering(true),
1765
2080
  onMouseLeave: () => setIsHovering(false),
1766
2081
  children: [
@@ -1785,7 +2100,7 @@ var KenobiLib = (() => {
1785
2100
  /* @__PURE__ */ u3("div", { class: "content-wrapper", children: /* @__PURE__ */ u3("div", { class: "text-content", children: /* @__PURE__ */ u3("div", { class: "text-row", children: [
1786
2101
  /* @__PURE__ */ u3("div", { children: [
1787
2102
  /* @__PURE__ */ u3("div", { class: "title", children: titleText }),
1788
- /* @__PURE__ */ u3("div", { class: "subtitle", children: subtitleText })
2103
+ isDeanonymized ? /* @__PURE__ */ u3("div", { class: "subtitle-chip", children: renderDeanonymizedChip("card") }) : /* @__PURE__ */ u3("div", { class: "subtitle-text", children: subtitleText })
1789
2104
  ] }),
1790
2105
  isPending ? /* @__PURE__ */ u3("span", { class: "status-text", children: [
1791
2106
  /* @__PURE__ */ u3(LoaderIcon, {}),
@@ -1794,9 +2109,9 @@ var KenobiLib = (() => {
1794
2109
  ] }) : /* @__PURE__ */ u3(
1795
2110
  "button",
1796
2111
  {
1797
- class: `btn btn-trigger ${showAsSuccess ? "success" : ""}${isOpen || isExitingToLauncher ? " hidden" : ""}`,
2112
+ class: `btn btn-trigger ${showAsSuccess ? "success" : ""}${isPopoverOpen || isExitingToLauncher ? " hidden" : ""}`,
1798
2113
  disabled: showAsSuccess,
1799
- onClick: togglePopover,
2114
+ onClick: isDeanonymized ? handleDeanonymizedSubmit : togglePopover,
1800
2115
  children: showAsSuccess ? /* @__PURE__ */ u3(k, { children: [
1801
2116
  /* @__PURE__ */ u3(CheckIcon, {}),
1802
2117
  " ",
@@ -1814,10 +2129,10 @@ var KenobiLib = (() => {
1814
2129
  }
1815
2130
  }
1816
2131
  ) }) }),
1817
- /* @__PURE__ */ u3(
2132
+ !isDeanonymized && /* @__PURE__ */ u3(
1818
2133
  "div",
1819
2134
  {
1820
- class: `popover ${isOpen ? "open" : ""}`,
2135
+ class: `popover ${isPopoverOpen ? "open" : ""}`,
1821
2136
  onClick: (e3) => e3.stopPropagation(),
1822
2137
  children: [
1823
2138
  /* @__PURE__ */ u3("form", { onSubmit: handleSubmit, ref: formRef, children: [
@@ -1884,7 +2199,7 @@ var KenobiLib = (() => {
1884
2199
  config.showKeyboardHints && config.enableFocusMode && /* @__PURE__ */ u3(
1885
2200
  "div",
1886
2201
  {
1887
- class: `kbd-hint ${!isOpen && !isPending && !isSuccess ? "visible" : ""}`,
2202
+ class: `kbd-hint ${!isPopoverOpen && !isPending && !isSuccess ? "visible" : ""}`,
1888
2203
  children: [
1889
2204
  /* @__PURE__ */ u3("div", { class: "kbd-group", children: /* @__PURE__ */ u3("div", { class: "kbd", children: "Esc" }) }),
1890
2205
  /* @__PURE__ */ u3("span", { class: "kbd-text", children: "to dismiss" })
@@ -1908,7 +2223,7 @@ var KenobiLib = (() => {
1908
2223
  e3.stopPropagation();
1909
2224
  setIsDismissed(false);
1910
2225
  setMode("card");
1911
- setIsOpen(true);
2226
+ setIsOpen(isPopoverEnabled);
1912
2227
  },
1913
2228
  role: "button",
1914
2229
  tabIndex: 0,
@@ -1938,7 +2253,10 @@ var KenobiLib = (() => {
1938
2253
  }
1939
2254
  ),
1940
2255
  config.showLauncherLogo !== false && /* @__PURE__ */ u3("div", { class: "launcher-logo", children: /* @__PURE__ */ u3(LogoIcon, {}) }),
1941
- hasHadSuccess ? config.textOverrides?.launcherLabelSuccess || config.textOverrides?.launcherLabel || "Personalize again" : config.textOverrides?.launcherLabel || "Personalize",
2256
+ /* @__PURE__ */ u3("div", { class: "launcher-label", children: [
2257
+ /* @__PURE__ */ u3("span", { children: launcherLabelText }),
2258
+ isDeanonymized && renderDeanonymizedChip("launcher")
2259
+ ] }),
1942
2260
  config.enableUndoToggle && hasHadSuccess && /* @__PURE__ */ u3(
1943
2261
  "button",
1944
2262
  {
@@ -1968,6 +2286,33 @@ var KenobiLib = (() => {
1968
2286
  __publicField(this, "isOpen", false);
1969
2287
  __publicField(this, "skipFocus", false);
1970
2288
  __publicField(this, "mountTarget", null);
2289
+ __publicField(this, "deanonymizedCompany", null);
2290
+ __publicField(this, "deanonymizationStatus", "idle");
2291
+ __publicField(this, "deanonymizationCleanup", null);
2292
+ __publicField(this, "hasStartedDeanonymization", false);
2293
+ __publicField(this, "clearDeanonymizedCompany", /* @__PURE__ */ __name(() => {
2294
+ const previousCompany = this.deanonymizedCompany;
2295
+ const shouldResetStatus = this.config.status === "success" || this.config.isPersonalized;
2296
+ if (this.config.isPersonalized) {
2297
+ this.config.onTogglePersonalization?.();
2298
+ }
2299
+ this.deanonymizedCompany = null;
2300
+ this.deanonymizationStatus = "failed";
2301
+ if (previousCompany) {
2302
+ dispatchCueCardEvent("deanonymization:not-you", {
2303
+ company: previousCompany
2304
+ });
2305
+ }
2306
+ if (shouldResetStatus) {
2307
+ this.config = {
2308
+ ...this.config,
2309
+ status: "resting",
2310
+ progressPct: 0,
2311
+ isPersonalized: false
2312
+ };
2313
+ }
2314
+ this.render();
2315
+ }, "clearDeanonymizedCompany"));
1971
2316
  this.config = {
1972
2317
  position: "top-center",
1973
2318
  direction: "top-to-bottom",
@@ -1990,6 +2335,9 @@ var KenobiLib = (() => {
1990
2335
  ...config
1991
2336
  };
1992
2337
  this.mountTarget = config.mountNode ?? null;
2338
+ if (this.isDeanonymizationEnabled()) {
2339
+ this.deanonymizationStatus = "pending";
2340
+ }
1993
2341
  this.host = document.createElement("div");
1994
2342
  this.host.id = "kenobi-cue-card-root";
1995
2343
  this.applyHostStyles();
@@ -2038,20 +2386,95 @@ var KenobiLib = (() => {
2038
2386
  resolvedTarget.appendChild(this.host);
2039
2387
  }
2040
2388
  this.applyHostStyles();
2389
+ this.startDeanonymization();
2041
2390
  this.render();
2042
2391
  }
2043
2392
  unmount() {
2044
2393
  G(null, this.shadow.lastElementChild);
2045
2394
  this.host.remove();
2395
+ this.deanonymizationCleanup?.();
2396
+ this.deanonymizationCleanup = null;
2046
2397
  }
2047
2398
  update(newState) {
2048
2399
  if (newState.status === "starting" && this.config.status !== "starting") {
2049
2400
  this.isOpen = false;
2050
2401
  }
2402
+ const wasDeanonymizationEnabled = this.isDeanonymizationEnabled();
2051
2403
  this.config = { ...this.config, ...newState };
2404
+ const isDeanonymizationEnabled = this.isDeanonymizationEnabled();
2405
+ if (isDeanonymizationEnabled && !wasDeanonymizationEnabled) {
2406
+ this.deanonymizationStatus = "pending";
2407
+ this.startDeanonymization();
2408
+ }
2409
+ if (!isDeanonymizationEnabled && wasDeanonymizationEnabled) {
2410
+ this.deanonymizedCompany = null;
2411
+ this.deanonymizationStatus = "idle";
2412
+ this.deanonymizationCleanup?.();
2413
+ this.deanonymizationCleanup = null;
2414
+ this.hasStartedDeanonymization = false;
2415
+ }
2052
2416
  this.applyHostStyles();
2053
2417
  this.render();
2054
2418
  }
2419
+ isDeanonymizationEnabled() {
2420
+ return this.config.deanonymization?.provider === "Kenobi";
2421
+ }
2422
+ startDeanonymization() {
2423
+ if (!this.isDeanonymizationEnabled()) return;
2424
+ if (this.hasStartedDeanonymization) return;
2425
+ if (this.deanonymizationStatus !== "pending") return;
2426
+ this.hasStartedDeanonymization = true;
2427
+ if (typeof window === "undefined") {
2428
+ this.deanonymizationStatus = "failed";
2429
+ this.render();
2430
+ return;
2431
+ }
2432
+ const cachedIdentity = window.__kenobiIdentityResolution;
2433
+ if (cachedIdentity) {
2434
+ this.handleDeanonymizationResult(cachedIdentity);
2435
+ return;
2436
+ }
2437
+ const handleEvent = /* @__PURE__ */ __name((event) => {
2438
+ const detail = event.detail;
2439
+ this.handleDeanonymizationResult(detail);
2440
+ }, "handleEvent");
2441
+ window.addEventListener(IDENTITY_RESOLVED_EVENT, handleEvent);
2442
+ ensureKenobiSnitcherLoaded(this.config.deanonymization);
2443
+ const timeoutMs = this.config.deanonymization?.timeoutMs ?? DEFAULT_DEANON_TIMEOUT_MS;
2444
+ const timeoutId = window.setTimeout(() => {
2445
+ window.removeEventListener(IDENTITY_RESOLVED_EVENT, handleEvent);
2446
+ this.handleDeanonymizationResult(null);
2447
+ }, timeoutMs);
2448
+ this.deanonymizationCleanup = () => {
2449
+ window.removeEventListener(IDENTITY_RESOLVED_EVENT, handleEvent);
2450
+ window.clearTimeout(timeoutId);
2451
+ };
2452
+ }
2453
+ handleDeanonymizationResult(detail) {
2454
+ if (this.deanonymizationStatus !== "pending") return;
2455
+ this.deanonymizationCleanup?.();
2456
+ this.deanonymizationCleanup = null;
2457
+ const company = resolveDeanonymizedCompany(detail);
2458
+ this.deanonymizedCompany = company;
2459
+ this.deanonymizationStatus = company ? "resolved" : "failed";
2460
+ if (company) {
2461
+ dispatchCueCardEvent("deanonymization:success", {
2462
+ company,
2463
+ detail
2464
+ });
2465
+ } else {
2466
+ dispatchCueCardEvent("deanonymization:fail", {
2467
+ detail
2468
+ });
2469
+ }
2470
+ this.render();
2471
+ }
2472
+ getRenderConfig() {
2473
+ if (this.deanonymizationStatus === "pending") {
2474
+ return { ...this.config, isVisible: false };
2475
+ }
2476
+ return this.config;
2477
+ }
2055
2478
  setTheme(theme) {
2056
2479
  this.update({ theme });
2057
2480
  }
@@ -2068,9 +2491,11 @@ var KenobiLib = (() => {
2068
2491
  /* @__PURE__ */ u3(
2069
2492
  CueCardContent,
2070
2493
  {
2071
- config: this.config,
2494
+ config: this.getRenderConfig(),
2072
2495
  isOpen: this.isOpen,
2073
2496
  skipFocus: skipFocusOnce,
2497
+ deanonymizedCompany: this.deanonymizedCompany,
2498
+ onClearDeanonymizedCompany: this.clearDeanonymizedCompany,
2074
2499
  setIsOpen: (val) => {
2075
2500
  this.isOpen = val;
2076
2501
  this.config.onOpenChange?.(val);
@@ -2492,6 +2917,8 @@ var KenobiLib = (() => {
2492
2917
  };
2493
2918
  __name(_TransitionOrchestrator, "TransitionOrchestrator");
2494
2919
  var TransitionOrchestrator = _TransitionOrchestrator;
2920
+ var IDENTITY_RESOLVED_EVENT2 = "kenobi:identity-resolved";
2921
+ var DEFAULT_DEANON_TIMEOUT_MS2 = 1600;
2495
2922
  var isCueCardConfigResponse = /* @__PURE__ */ __name((value) => {
2496
2923
  if (typeof value !== "object" || value === null) return false;
2497
2924
  const candidate = value;
@@ -2528,6 +2955,10 @@ var KenobiLib = (() => {
2528
2955
  __publicField(this, "visitorKey", null);
2529
2956
  __publicField(this, "orchestrator");
2530
2957
  __publicField(this, "cueCardInstance", null);
2958
+ __publicField(this, "preparedPersonalization", null);
2959
+ __publicField(this, "isPreparingPersonalization", false);
2960
+ __publicField(this, "preparePersonalizationPromise", null);
2961
+ __publicField(this, "deanonymizationPrefetchCleanup", null);
2531
2962
  //
2532
2963
  __publicField(this, "currentPath");
2533
2964
  __publicField(this, "currentUrl");
@@ -3227,6 +3658,8 @@ var KenobiLib = (() => {
3227
3658
  }
3228
3659
  this.log("debug", "Active template found, mounting CueCard...");
3229
3660
  const serverConfig = configResponse.config;
3661
+ const deanonymizationConfig = serverConfig?.deanonymization;
3662
+ const shouldWaitForDeanonymization = !!deanonymizationConfig?.provider;
3230
3663
  this.cueCardInstance = new CueCard({
3231
3664
  theme: serverConfig?.theme ?? "light",
3232
3665
  position: serverConfig?.position ?? "top-center",
@@ -3241,7 +3674,7 @@ var KenobiLib = (() => {
3241
3674
  errorMessage: "Company name or domain is required"
3242
3675
  }
3243
3676
  ],
3244
- isVisible: true,
3677
+ isVisible: !shouldWaitForDeanonymization,
3245
3678
  enableFocusMode: serverConfig?.enableFocusMode ?? false,
3246
3679
  showWatermark: serverConfig?.showWatermark ?? false,
3247
3680
  showKeyboardHints: serverConfig?.showKeyboardHints ?? true,
@@ -3249,6 +3682,7 @@ var KenobiLib = (() => {
3249
3682
  customTheme: serverConfig?.customTheme,
3250
3683
  textOverrides: serverConfig?.textOverrides,
3251
3684
  enableUndoToggle: serverConfig?.enableUndoToggle ?? false,
3685
+ deanonymization: deanonymizationConfig,
3252
3686
  isPersonalized: this.isPersonalized,
3253
3687
  onDismiss: /* @__PURE__ */ __name(() => {
3254
3688
  this.log("debug", "CueCard dismissed by user");
@@ -3277,8 +3711,220 @@ var KenobiLib = (() => {
3277
3711
  }, "onTogglePersonalization")
3278
3712
  });
3279
3713
  this.cueCardInstance.mount();
3714
+ if (shouldWaitForDeanonymization) {
3715
+ this.setupDeanonymizationPrefetch(
3716
+ deanonymizationConfig,
3717
+ serverConfig?.fields
3718
+ );
3719
+ }
3280
3720
  this.log("debug", "CueCard mounted successfully");
3281
3721
  }, "initCueCard"));
3722
+ __publicField(this, "isSameInput", /* @__PURE__ */ __name((a3, b) => {
3723
+ const aKeys = Object.keys(a3).sort();
3724
+ const bKeys = Object.keys(b).sort();
3725
+ if (aKeys.length !== bKeys.length) return false;
3726
+ for (let i3 = 0; i3 < aKeys.length; i3 += 1) {
3727
+ const key = aKeys[i3];
3728
+ if (key !== bKeys[i3]) return false;
3729
+ if ((a3[key] ?? "") !== (b[key] ?? "")) return false;
3730
+ }
3731
+ return true;
3732
+ }, "isSameInput"));
3733
+ __publicField(this, "buildInputFromFields", /* @__PURE__ */ __name((fields, companyName, companyDomain) => {
3734
+ const fallback = companyDomain || companyName;
3735
+ if (!fallback) return null;
3736
+ const input = {};
3737
+ fields.forEach((field) => {
3738
+ const key = field.name;
3739
+ const lowerKey = key.toLowerCase();
3740
+ if (lowerKey.includes("domain")) {
3741
+ input[key] = companyDomain || fallback;
3742
+ return;
3743
+ }
3744
+ if (lowerKey.includes("company")) {
3745
+ input[key] = companyName || fallback;
3746
+ return;
3747
+ }
3748
+ input[key] = fallback;
3749
+ });
3750
+ return input;
3751
+ }, "buildInputFromFields"));
3752
+ __publicField(this, "buildDeanonymizedInput", /* @__PURE__ */ __name((detail, fields) => {
3753
+ const name = detail.company?.name || detail.domain || "";
3754
+ const domain = detail.company?.domain || detail.domain || "";
3755
+ if (!fields || fields.length === 0) {
3756
+ if (!name && !domain) return null;
3757
+ return {
3758
+ companyName: name || domain,
3759
+ companyDomain: domain || name
3760
+ };
3761
+ }
3762
+ return this.buildInputFromFields(fields, name, domain);
3763
+ }, "buildDeanonymizedInput"));
3764
+ __publicField(this, "requestPersonalization", /* @__PURE__ */ __name(async (input, options) => {
3765
+ const payload = {
3766
+ publicKey: this.config.publicKey,
3767
+ input,
3768
+ pagePath: this.currentPath
3769
+ };
3770
+ if (options?.notifySlack === false) {
3771
+ payload.notifySlack = false;
3772
+ }
3773
+ if (options?.notifyOnly) {
3774
+ payload.notifyOnly = true;
3775
+ }
3776
+ if (options?.predictionSource) {
3777
+ payload.predictionSource = options.predictionSource;
3778
+ }
3779
+ if (options?.prefetchedTransformations) {
3780
+ payload.prefetchedTransformations = options.prefetchedTransformations;
3781
+ }
3782
+ if (options?.prefetchedMetadata) {
3783
+ payload.prefetchedMetadata = options.prefetchedMetadata;
3784
+ }
3785
+ const response = await fetch(`${this.config.apiHost}/v1/personalize`, {
3786
+ method: "POST",
3787
+ headers: {
3788
+ "Content-Type": "application/json"
3789
+ },
3790
+ body: JSON.stringify(payload)
3791
+ });
3792
+ if (!response.ok) {
3793
+ const errorBody = await response.json().catch(() => ({}));
3794
+ throw new Error(errorBody.error || `API error: ${response.status}`);
3795
+ }
3796
+ const result = await response.json();
3797
+ this.log("debug", "Personalization API response:", result);
3798
+ return result;
3799
+ }, "requestPersonalization"));
3800
+ __publicField(this, "preparePersonalization", /* @__PURE__ */ __name(async (input, options) => {
3801
+ if (!this.config.publicKey) {
3802
+ this.log("error", "Cannot prepare personalization: publicKey not configured");
3803
+ return null;
3804
+ }
3805
+ if (this.preparedPersonalization?.input && this.isSameInput(this.preparedPersonalization.input, input)) {
3806
+ if (options?.source && !this.preparedPersonalization.source) {
3807
+ this.preparedPersonalization = {
3808
+ ...this.preparedPersonalization,
3809
+ source: options.source
3810
+ };
3811
+ }
3812
+ return this.preparedPersonalization.result;
3813
+ }
3814
+ if (this.preparePersonalizationPromise) {
3815
+ return this.preparePersonalizationPromise;
3816
+ }
3817
+ this.isPreparingPersonalization = true;
3818
+ this.preparePersonalizationPromise = (async () => {
3819
+ try {
3820
+ const result = await this.requestPersonalization(input, {
3821
+ notifySlack: false,
3822
+ predictionSource: options?.source
3823
+ });
3824
+ this.preparedPersonalization = { input, result, source: options?.source };
3825
+ this.dispatchKenobiEvent("personalization:ready", {
3826
+ input,
3827
+ pagePath: this.currentPath,
3828
+ transformationsCount: result.transformations?.length ?? 0,
3829
+ metadata: result.metadata,
3830
+ source: options?.source
3831
+ });
3832
+ return result;
3833
+ } catch (error) {
3834
+ this.log("error", "Personalization prefetch failed:", error);
3835
+ return null;
3836
+ } finally {
3837
+ this.isPreparingPersonalization = false;
3838
+ this.preparePersonalizationPromise = null;
3839
+ }
3840
+ })();
3841
+ return this.preparePersonalizationPromise;
3842
+ }, "preparePersonalization"));
3843
+ __publicField(this, "notifyPreparedPersonalization", /* @__PURE__ */ __name(async (prepared) => {
3844
+ if (!prepared.result.transformations || !prepared.result.metadata) {
3845
+ this.log(
3846
+ "warn",
3847
+ "Prepared personalization missing transformations or metadata for notify"
3848
+ );
3849
+ return;
3850
+ }
3851
+ try {
3852
+ await this.requestPersonalization(prepared.input, {
3853
+ notifySlack: true,
3854
+ notifyOnly: true,
3855
+ predictionSource: prepared.source,
3856
+ prefetchedTransformations: prepared.result.transformations,
3857
+ prefetchedMetadata: prepared.result.metadata
3858
+ });
3859
+ } catch (error) {
3860
+ this.log(
3861
+ "warn",
3862
+ "Failed to send Slack notification for prepared personalization:",
3863
+ error
3864
+ );
3865
+ }
3866
+ }, "notifyPreparedPersonalization"));
3867
+ __publicField(this, "setupDeanonymizationPrefetch", /* @__PURE__ */ __name((config, fields) => {
3868
+ if (!this.cueCardInstance) return;
3869
+ if (typeof window === "undefined") {
3870
+ this.cueCardInstance.update({ isVisible: true });
3871
+ return;
3872
+ }
3873
+ if (this.deanonymizationPrefetchCleanup) return;
3874
+ let resolved = false;
3875
+ let hasIdentity = false;
3876
+ let shouldDisableDeanonymization = false;
3877
+ const finish = /* @__PURE__ */ __name(() => {
3878
+ if (resolved) return;
3879
+ resolved = true;
3880
+ if (shouldDisableDeanonymization) {
3881
+ this.cueCardInstance?.update({
3882
+ deanonymization: void 0,
3883
+ isVisible: true
3884
+ });
3885
+ } else {
3886
+ this.cueCardInstance?.update({ isVisible: true });
3887
+ }
3888
+ this.deanonymizationPrefetchCleanup?.();
3889
+ this.deanonymizationPrefetchCleanup = null;
3890
+ }, "finish");
3891
+ const handleIdentity = /* @__PURE__ */ __name(async (event) => {
3892
+ if (resolved) return;
3893
+ const detail = event.detail;
3894
+ hasIdentity = true;
3895
+ if (detail?.type !== "business") {
3896
+ finish();
3897
+ return;
3898
+ }
3899
+ const input = this.buildDeanonymizedInput(detail, fields);
3900
+ if (!input) {
3901
+ finish();
3902
+ return;
3903
+ }
3904
+ const prepared = await this.preparePersonalization(input, {
3905
+ source: "deanonymized"
3906
+ });
3907
+ if (!prepared) {
3908
+ shouldDisableDeanonymization = true;
3909
+ }
3910
+ finish();
3911
+ }, "handleIdentity");
3912
+ const cachedIdentity = window.__kenobiIdentityResolution;
3913
+ if (cachedIdentity) {
3914
+ void handleIdentity({ detail: cachedIdentity });
3915
+ }
3916
+ window.addEventListener(IDENTITY_RESOLVED_EVENT2, handleIdentity);
3917
+ const timeoutMs = config?.timeoutMs ?? DEFAULT_DEANON_TIMEOUT_MS2;
3918
+ const timeoutId = window.setTimeout(() => {
3919
+ if (!hasIdentity) {
3920
+ finish();
3921
+ }
3922
+ }, timeoutMs + 50);
3923
+ this.deanonymizationPrefetchCleanup = () => {
3924
+ window.removeEventListener(IDENTITY_RESOLVED_EVENT2, handleIdentity);
3925
+ window.clearTimeout(timeoutId);
3926
+ };
3927
+ }, "setupDeanonymizationPrefetch"));
3282
3928
  /**
3283
3929
  * Syncs CueCard visibility based on the current path.
3284
3930
  * Checks with the server if an active template exists for the new path.
@@ -3323,42 +3969,32 @@ var KenobiLib = (() => {
3323
3969
  }
3324
3970
  const startTime = /* @__PURE__ */ new Date();
3325
3971
  this.log("debug", "Starting personalization with input:", input);
3972
+ const prepared = this.preparedPersonalization?.input && this.isSameInput(this.preparedPersonalization.input, input) ? this.preparedPersonalization : null;
3973
+ const isDeanonymizedInput = prepared?.source === "deanonymized";
3326
3974
  this.dispatchKenobiEvent("personalization:start", {
3327
3975
  input,
3328
- pagePath: this.currentPath
3976
+ pagePath: this.currentPath,
3977
+ deanonymized: isDeanonymizedInput
3329
3978
  });
3330
- if (this.cueCardInstance) {
3331
- this.cueCardInstance.update({ status: "starting", progressPct: 10 });
3332
- }
3333
3979
  try {
3334
- const response = await fetch(`${this.config.apiHost}/v1/personalize`, {
3335
- method: "POST",
3336
- headers: {
3337
- "Content-Type": "application/json"
3338
- },
3339
- body: JSON.stringify({
3340
- publicKey: this.config.publicKey,
3341
- input,
3342
- pagePath: this.currentPath
3343
- })
3344
- });
3345
3980
  if (this.cueCardInstance) {
3346
- this.cueCardInstance.update({ progressPct: 50 });
3981
+ this.cueCardInstance.update({
3982
+ status: "starting",
3983
+ progressPct: prepared ? 80 : 10
3984
+ });
3347
3985
  }
3348
- if (!response.ok) {
3349
- const errorBody = await response.json().catch(() => ({}));
3350
- throw new Error(errorBody.error || `API error: ${response.status}`);
3351
- }
3352
- const result = await response.json();
3353
- this.log("debug", "Personalization API response:", result);
3354
- if (this.cueCardInstance) {
3355
- this.cueCardInstance.update({ progressPct: 80 });
3986
+ if (prepared) {
3987
+ void this.notifyPreparedPersonalization(prepared);
3356
3988
  }
3989
+ const result = prepared ? prepared.result : await this.requestPersonalization(input);
3357
3990
  if (result.transformations && Array.isArray(result.transformations)) {
3358
3991
  await this.applyTransformations(result.transformations, {
3359
3992
  shouldConsolidate: true
3360
3993
  });
3361
3994
  }
3995
+ if (prepared) {
3996
+ this.preparedPersonalization = null;
3997
+ }
3362
3998
  if (this.cueCardInstance) {
3363
3999
  this.cueCardInstance.update({ status: "success", progressPct: 100 });
3364
4000
  }
@@ -3373,7 +4009,9 @@ var KenobiLib = (() => {
3373
4009
  pagePath: this.currentPath,
3374
4010
  durationMs: duration,
3375
4011
  transformationsCount: result.transformations?.length ?? 0,
3376
- metadata: result.metadata
4012
+ metadata: result.metadata,
4013
+ source: prepared ? "prefetch" : "api",
4014
+ deanonymized: isDeanonymizedInput
3377
4015
  });
3378
4016
  } catch (error) {
3379
4017
  this.log("error", "Personalization failed:", error);
@@ -3383,7 +4021,8 @@ var KenobiLib = (() => {
3383
4021
  this.dispatchKenobiEvent("personalization:error", {
3384
4022
  input,
3385
4023
  pagePath: this.currentPath,
3386
- error: error instanceof Error ? error.message : String(error)
4024
+ error: error instanceof Error ? error.message : String(error),
4025
+ deanonymized: isDeanonymizedInput
3387
4026
  });
3388
4027
  }
3389
4028
  }, "personalize"));