@preprio/toolkit 0.5.0-rc.1 → 0.6.0-alpha.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.
Files changed (69) hide show
  1. package/README.md +300 -19
  2. package/dist/astro/chunk-3UVUDDNY.js +173 -0
  3. package/dist/astro/graphql.cjs +983 -0
  4. package/dist/astro/graphql.d.cts +231 -0
  5. package/dist/astro/graphql.d.ts +231 -0
  6. package/dist/astro/graphql.js +893 -0
  7. package/dist/astro/index-XT_z-RYT.d.cts +130 -0
  8. package/dist/astro/index-XT_z-RYT.d.ts +130 -0
  9. package/dist/astro/index.cjs +14 -1
  10. package/dist/astro/index.d.cts +1 -120
  11. package/dist/astro/index.d.ts +1 -120
  12. package/dist/astro/index.js +15 -153
  13. package/dist/{chunk-4JNKYIEV.js → chunk-BJ2LBLWO.js} +69 -268
  14. package/dist/chunk-DGZA7KX7.js +172 -0
  15. package/dist/chunk-YR7P4IWE.js +187 -0
  16. package/dist/graphql.cjs +894 -0
  17. package/dist/graphql.d.cts +231 -0
  18. package/dist/graphql.d.ts +231 -0
  19. package/dist/graphql.js +787 -0
  20. package/dist/index.cjs +74 -111
  21. package/dist/index.d.cts +11 -62
  22. package/dist/index.d.ts +11 -62
  23. package/dist/index.js +23 -167
  24. package/dist/nextjs/chunk-U4HXENF7.js +231 -0
  25. package/dist/nextjs/components.cjs +188 -114
  26. package/dist/nextjs/components.d.cts +33 -2
  27. package/dist/nextjs/components.d.ts +33 -2
  28. package/dist/nextjs/components.js +132 -4
  29. package/dist/nextjs/graphql.cjs +1315 -0
  30. package/dist/nextjs/graphql.d.cts +361 -0
  31. package/dist/nextjs/graphql.d.ts +361 -0
  32. package/dist/nextjs/graphql.js +1218 -0
  33. package/dist/nextjs/index.cjs +126 -6
  34. package/dist/nextjs/index.d.cts +53 -141
  35. package/dist/nextjs/index.d.ts +53 -141
  36. package/dist/nextjs/index.js +113 -203
  37. package/dist/nextjs/server-CjpELYvq.d.cts +156 -0
  38. package/dist/nextjs/server-CjpELYvq.d.ts +156 -0
  39. package/dist/nuxt/chunk-P6ENNAEE.js +417 -0
  40. package/dist/nuxt/graphql.cjs +990 -0
  41. package/dist/nuxt/graphql.d.cts +228 -0
  42. package/dist/nuxt/graphql.d.ts +228 -0
  43. package/dist/nuxt/graphql.js +894 -0
  44. package/dist/nuxt/index.cjs +14 -1
  45. package/dist/nuxt/index.d.cts +3 -148
  46. package/dist/nuxt/index.d.ts +3 -148
  47. package/dist/nuxt/index.js +15 -391
  48. package/dist/nuxt/server-8v6OHbsw.d.cts +158 -0
  49. package/dist/nuxt/server-8v6OHbsw.d.ts +158 -0
  50. package/dist/pixel-DwW7cXJm.d.cts +49 -0
  51. package/dist/pixel-DwW7cXJm.d.ts +49 -0
  52. package/dist/react/components.cjs +86 -112
  53. package/dist/react/components.js +86 -112
  54. package/dist/server-Brr1UgtW.d.ts +59 -0
  55. package/dist/server-DJuS3YU0.d.cts +59 -0
  56. package/dist/sveltekit/chunk-FSARVMG5.js +413 -0
  57. package/dist/sveltekit/graphql.cjs +996 -0
  58. package/dist/sveltekit/graphql.d.cts +233 -0
  59. package/dist/sveltekit/graphql.d.ts +233 -0
  60. package/dist/sveltekit/graphql.js +900 -0
  61. package/dist/sveltekit/index.cjs +14 -1
  62. package/dist/sveltekit/index.d.cts +3 -88
  63. package/dist/sveltekit/index.d.ts +3 -88
  64. package/dist/sveltekit/index.js +15 -387
  65. package/dist/sveltekit/server-CO847mEV.d.cts +98 -0
  66. package/dist/sveltekit/server-CO847mEV.d.ts +98 -0
  67. package/dist/{pixel-e1fUWl2Y.d.cts → types-BYH9XqmN.d.cts} +2 -50
  68. package/dist/{pixel-e1fUWl2Y.d.ts → types-BYH9XqmN.d.ts} +2 -50
  69. package/package.json +31 -1
package/dist/index.cjs CHANGED
@@ -33,6 +33,7 @@ __export(src_exports, {
33
33
  getPreprHeadersFromHeaders: () => getPreprHeadersFromHeaders,
34
34
  getPreprUUIDFromHeaders: () => getPreprUUIDFromHeaders,
35
35
  getToolbarPropsFromHeaders: () => getToolbarPropsFromHeaders,
36
+ isPersonalizedResponse: () => isPersonalizedResponse,
36
37
  loadTrackingPixel: () => loadTrackingPixel,
37
38
  processPreprRequest: () => processPreprRequest,
38
39
  resolveFeatures: () => resolveFeatures,
@@ -44,7 +45,7 @@ __export(src_exports, {
44
45
  module.exports = __toCommonJS(src_exports);
45
46
 
46
47
  // src/version.ts
47
- var VERSION = "0.5.0-rc.1";
48
+ var VERSION = "0.6.0-alpha.0";
48
49
 
49
50
  // src/core/features.ts
50
51
  function isEnabled(config) {
@@ -312,7 +313,6 @@ function createElementCache(query, ttl = 1e3) {
312
313
  }
313
314
 
314
315
  // src/core/iframe-bridge.ts
315
- var debug = createScopedLogger("iframe-bridge");
316
316
  var EDITOR_BASE_DOMAIN = "prepr.io";
317
317
  function isAllowedEditorOrigin(origin, allowed) {
318
318
  if (allowed) return allowed.includes(origin);
@@ -330,7 +330,6 @@ function isAllowedEditorOrigin(origin, allowed) {
330
330
  }
331
331
  function createIframeBridge(store, options = {}) {
332
332
  const allowedOrigins = options.allowedEditorOrigins;
333
- const onScrollToField = options.onScrollToField;
334
333
  let parentOrigin = null;
335
334
  const onKeyDown = (event) => {
336
335
  const key = event.key.toLowerCase();
@@ -354,15 +353,6 @@ function createIframeBridge(store, options = {}) {
354
353
  const currentScrollY = window.scrollY || document.documentElement.scrollTop;
355
354
  sendPreprEvent("getScrollPosition", { value: currentScrollY });
356
355
  }
357
- if (data?.event === "prepr:scrollToField") {
358
- if (typeof data.field !== "string" || !data.field) return;
359
- if (data.id !== void 0 && typeof data.id !== "string") return;
360
- try {
361
- onScrollToField?.({ field: data.field, id: data.id });
362
- } catch (error) {
363
- debug.warn("scrollToField handler failed", error);
364
- }
365
- }
366
356
  };
367
357
  return {
368
358
  start() {
@@ -387,7 +377,7 @@ var import_stega2 = require("@vercel/stega");
387
377
 
388
378
  // src/core/stega/clean.ts
389
379
  var import_stega = require("@vercel/stega");
390
- var debug2 = createScopedLogger("stega:clean");
380
+ var debug = createScopedLogger("stega:clean");
391
381
  function stegaClean(text) {
392
382
  if (!text) return text;
393
383
  return (0, import_stega.vercelStegaClean)(text);
@@ -400,7 +390,7 @@ function decodeStega(str) {
400
390
  return decoded;
401
391
  }
402
392
  } catch (error) {
403
- debug2.log("error decoding stega data", error);
393
+ debug.log("error decoding stega data", error);
404
394
  const match = str.match(/{"origin.*?}/);
405
395
  if (match) {
406
396
  try {
@@ -409,7 +399,7 @@ function decodeStega(str) {
409
399
  return decodedMatch;
410
400
  }
411
401
  } catch (innerError) {
412
- debug2.log("error decoding stega regex match", innerError);
402
+ debug.log("error decoding stega regex match", innerError);
413
403
  }
414
404
  }
415
405
  }
@@ -445,37 +435,15 @@ function isRenderedElement(element) {
445
435
  if (!style) return true;
446
436
  return style.visibility !== "hidden" && style.display !== "none";
447
437
  }
448
- function parseEditHref(href) {
449
- try {
450
- const url = new URL(href, window.location.origin);
451
- return {
452
- id: url.pathname.split("/").pop() || void 0,
453
- field: url.searchParams.get("field") || void 0,
454
- locale: url.searchParams.get("locale") || void 0
455
- };
456
- } catch {
457
- return {};
458
- }
459
- }
460
- function setOrRemove(element, name, value) {
461
- if (value) {
462
- element.setAttribute(name, value);
463
- } else {
464
- element.removeAttribute(name);
465
- }
466
- }
467
438
  function tagEncodedElement(element, decoded) {
468
439
  element.setAttribute("data-prepr-encoded", "");
469
440
  element.setAttribute("data-prepr-href", decoded.href);
470
441
  element.setAttribute("data-prepr-origin", decoded.origin);
471
- const { id, field, locale } = parseEditHref(decoded.href);
472
- setOrRemove(element, "data-prepr-id", id);
473
- setOrRemove(element, "data-prepr-field", field);
474
- setOrRemove(element, "data-prepr-locale", locale);
475
442
  }
476
443
 
477
444
  // src/core/stega/auto-clean.ts
478
- var debug3 = createScopedLogger("stega:auto-clean");
445
+ var debug2 = createScopedLogger("stega:auto-clean");
446
+ var HYDRATION_RETRY_MS = 100;
479
447
  function createStegaAutoClean(options) {
480
448
  const stripText = options?.enabled ?? true;
481
449
  let observer = null;
@@ -484,8 +452,29 @@ function createStegaAutoClean(options) {
484
452
  let pendingHidden = /* @__PURE__ */ new Set();
485
453
  let pendingDirty = /* @__PURE__ */ new Set();
486
454
  let debounceTimeout = null;
455
+ const isHydrated = options?.isHydrated;
456
+ let pendingHydration = /* @__PURE__ */ new Set();
457
+ let hydrationTimeout = null;
458
+ function retryPendingHydration() {
459
+ hydrationTimeout = null;
460
+ processing = true;
461
+ const batch = [...pendingHydration];
462
+ pendingHydration.clear();
463
+ batch.forEach((textNode) => {
464
+ if (textNode.isConnected) cleanTextNode(textNode);
465
+ });
466
+ processing = false;
467
+ }
487
468
  function cleanTextNode(textNode) {
488
469
  if (isIgnoredTextNode(textNode)) return false;
470
+ if (isHydrated && !isHydrated(textNode)) {
471
+ pendingHydration.add(textNode);
472
+ hydrationTimeout ?? (hydrationTimeout = setTimeout(
473
+ retryPendingHydration,
474
+ HYDRATION_RETRY_MS
475
+ ));
476
+ return false;
477
+ }
489
478
  const textContent = textNode.textContent;
490
479
  const info = cleaned.get(textNode);
491
480
  const settled = stripText ? info?.cleaned : info?.original;
@@ -518,7 +507,7 @@ function createStegaAutoClean(options) {
518
507
  }
519
508
  pendingHidden.add(textNode);
520
509
  } catch (error) {
521
- debug3.log("error cleaning text node", error);
510
+ debug2.log("error cleaning text node", error);
522
511
  }
523
512
  return false;
524
513
  }
@@ -604,13 +593,17 @@ function createStegaAutoClean(options) {
604
593
  if (observer || typeof window === "undefined") return;
605
594
  scanAndClean();
606
595
  setupObserver();
607
- debug3.log("stega auto-clean started");
596
+ debug2.log("stega auto-clean started");
608
597
  }
609
598
  function stop() {
610
599
  if (debounceTimeout) {
611
600
  clearTimeout(debounceTimeout);
612
601
  debounceTimeout = null;
613
602
  }
603
+ if (hydrationTimeout) {
604
+ clearTimeout(hydrationTimeout);
605
+ hydrationTimeout = null;
606
+ }
614
607
  if (observer) {
615
608
  observer.disconnect();
616
609
  observer = null;
@@ -618,13 +611,14 @@ function createStegaAutoClean(options) {
618
611
  cleaned = /* @__PURE__ */ new WeakMap();
619
612
  pendingHidden = /* @__PURE__ */ new Set();
620
613
  pendingDirty = /* @__PURE__ */ new Set();
621
- debug3.log("stega auto-clean stopped");
614
+ pendingHydration = /* @__PURE__ */ new Set();
615
+ debug2.log("stega auto-clean stopped");
622
616
  }
623
617
  return { start, stop };
624
618
  }
625
619
 
626
620
  // src/core/stega/elements.ts
627
- var debug4 = createScopedLogger("stega:elements");
621
+ var debug3 = createScopedLogger("stega:elements");
628
622
  var StegaElements = class {
629
623
  constructor(decode = decodeStega) {
630
624
  this.observer = null;
@@ -669,7 +663,7 @@ var StegaElements = class {
669
663
  walkTextNodes(document.body, (textNode) => {
670
664
  if (this.tagFromTextNode(textNode)) encodedCount++;
671
665
  });
672
- debug4.log("document scan complete, encoded", encodedCount, "elements");
666
+ debug3.log("document scan complete, encoded", encodedCount, "elements");
673
667
  this.elements = document.querySelectorAll("[data-prepr-encoded]");
674
668
  }
675
669
  /** `onUpdate` fires after each debounced batch, for refreshing derived state. */
@@ -725,9 +719,6 @@ var StegaElements = class {
725
719
  element.removeAttribute("data-prepr-encoded");
726
720
  element.removeAttribute("data-prepr-href");
727
721
  element.removeAttribute("data-prepr-origin");
728
- element.removeAttribute("data-prepr-id");
729
- element.removeAttribute("data-prepr-field");
730
- element.removeAttribute("data-prepr-locale");
731
722
  element.classList.remove("prepr-overlay-active");
732
723
  });
733
724
  this.elements = void 0;
@@ -735,7 +726,7 @@ var StegaElements = class {
735
726
  };
736
727
 
737
728
  // src/core/stega/overlay.ts
738
- var debug5 = createScopedLogger("stega:overlay");
729
+ var debug4 = createScopedLogger("stega:overlay");
739
730
  var StegaOverlay = class {
740
731
  constructor(onEdit) {
741
732
  this.overlay = null;
@@ -780,8 +771,7 @@ var StegaOverlay = class {
780
771
  const origin = element.getAttribute("data-prepr-origin");
781
772
  const id = element.getAttribute("data-prepr-id") ?? void 0;
782
773
  const field = element.getAttribute("data-prepr-field") ?? void 0;
783
- const locale = element.getAttribute("data-prepr-locale") ?? void 0;
784
- debug5.log("showing overlay", { href, origin });
774
+ debug4.log("showing overlay", { href, origin });
785
775
  const overlay = this.overlay;
786
776
  overlay.style.display = "block";
787
777
  overlay.style.top = `${rect.top + window.scrollY - 2}px`;
@@ -813,7 +803,7 @@ var StegaOverlay = class {
813
803
  tooltip.style.left = `${left}px`;
814
804
  });
815
805
  tooltip.onclick = () => {
816
- this.onEdit({ href, origin, id, field, locale });
806
+ this.onEdit({ href, origin, id, field });
817
807
  };
818
808
  }
819
809
  this.currentElement = element;
@@ -853,7 +843,7 @@ var StegaOverlay = class {
853
843
  };
854
844
 
855
845
  // src/core/stega/proximity.ts
856
- var debug6 = createScopedLogger("stega:proximity");
846
+ var debug5 = createScopedLogger("stega:proximity");
857
847
  var StegaProximity = class {
858
848
  constructor() {
859
849
  this.highlighted = /* @__PURE__ */ new Set();
@@ -888,9 +878,9 @@ var StegaProximity = class {
888
878
  );
889
879
  const nodes = this.getEncodedElements();
890
880
  nodes.forEach((el) => this.observer.observe(el));
891
- debug6.log("observing", nodes.length, "encoded elements");
881
+ debug5.log("observing", nodes.length, "encoded elements");
892
882
  } catch (error) {
893
- debug6.log("error setting up IntersectionObserver", error);
883
+ debug5.log("error setting up IntersectionObserver", error);
894
884
  }
895
885
  }
896
886
  stopObserving() {
@@ -960,7 +950,7 @@ function removeStegaStyles() {
960
950
  }
961
951
 
962
952
  // src/core/stega/scan.ts
963
- var debug7 = createScopedLogger("stega:scan");
953
+ var debug6 = createScopedLogger("stega:scan");
964
954
  var EDITOR_MODE_STYLES = "[data-prepr-encoded]{cursor:pointer}";
965
955
  function createStegaController(opts) {
966
956
  const tooltipEnabled = opts.tooltip ?? true;
@@ -993,13 +983,12 @@ function createStegaController(opts) {
993
983
  href: encoded.getAttribute("data-prepr-href") ?? "",
994
984
  origin: encoded.getAttribute("data-prepr-origin") ?? "",
995
985
  id: encoded.getAttribute("data-prepr-id") ?? void 0,
996
- field: encoded.getAttribute("data-prepr-field") ?? void 0,
997
- locale: encoded.getAttribute("data-prepr-locale") ?? void 0
986
+ field: encoded.getAttribute("data-prepr-field") ?? void 0
998
987
  });
999
988
  };
1000
989
  const start = () => {
1001
990
  if (initialized) {
1002
- debug7.log("already initialized, skipping setup");
991
+ debug6.log("already initialized, skipping setup");
1003
992
  return;
1004
993
  }
1005
994
  if (!tooltipEnabled) {
@@ -1009,7 +998,7 @@ function createStegaController(opts) {
1009
998
  });
1010
999
  document.addEventListener("click", handleClick, { capture: true });
1011
1000
  initialized = true;
1012
- debug7.log("stega controller started (editor mode)");
1001
+ debug6.log("stega controller started (editor mode)");
1013
1002
  return;
1014
1003
  }
1015
1004
  injectStegaStyles();
@@ -1040,7 +1029,7 @@ function createStegaController(opts) {
1040
1029
  passive: true
1041
1030
  });
1042
1031
  initialized = true;
1043
- debug7.log("stega controller started");
1032
+ debug6.log("stega controller started");
1044
1033
  };
1045
1034
  const stop = () => {
1046
1035
  if (!initialized) return;
@@ -1049,7 +1038,7 @@ function createStegaController(opts) {
1049
1038
  elements.cleanup();
1050
1039
  removeStegaStyles();
1051
1040
  initialized = false;
1052
- debug7.log("stega controller stopped (editor mode)");
1041
+ debug6.log("stega controller stopped (editor mode)");
1053
1042
  return;
1054
1043
  }
1055
1044
  if (throttledMouseMove) {
@@ -1069,43 +1058,11 @@ function createStegaController(opts) {
1069
1058
  elements.cleanup();
1070
1059
  removeStegaStyles();
1071
1060
  initialized = false;
1072
- debug7.log("stega controller stopped");
1061
+ debug6.log("stega controller stopped");
1073
1062
  };
1074
1063
  return { start, stop };
1075
1064
  }
1076
1065
 
1077
- // src/core/stega/scroll-to-field.ts
1078
- var debug8 = createScopedLogger("stega:scroll");
1079
- var FLASH_DURATION_MS = 1200;
1080
- var flashTimeout = null;
1081
- function scrollToField(request) {
1082
- const { field, id } = request;
1083
- if (!field) return false;
1084
- const candidates = document.querySelectorAll(
1085
- "[data-prepr-encoded]"
1086
- );
1087
- let target = null;
1088
- for (const candidate of candidates) {
1089
- if (candidate.getAttribute("data-prepr-field") !== field) continue;
1090
- if (id && candidate.getAttribute("data-prepr-id") !== id) continue;
1091
- target = candidate;
1092
- break;
1093
- }
1094
- if (!target) {
1095
- debug8.log("no element found for field", field);
1096
- return false;
1097
- }
1098
- target.scrollIntoView({ behavior: "smooth", block: "center" });
1099
- if (flashTimeout) clearTimeout(flashTimeout);
1100
- document.querySelectorAll(".prepr-overlay-active").forEach((el) => el.classList.remove("prepr-overlay-active"));
1101
- target.classList.add("prepr-overlay-active");
1102
- flashTimeout = setTimeout(() => {
1103
- target.classList.remove("prepr-overlay-active");
1104
- flashTimeout = null;
1105
- }, FLASH_DURATION_MS);
1106
- return true;
1107
- }
1108
-
1109
1066
  // src/core/store.ts
1110
1067
  var DEFAULT_STATE = {
1111
1068
  locale: "en",
@@ -2218,7 +2175,7 @@ function definePreprToolbar() {
2218
2175
  }
2219
2176
 
2220
2177
  // src/core/create-preview.ts
2221
- var debug9 = createScopedLogger("create-preview");
2178
+ var debug7 = createScopedLogger("create-preview");
2222
2179
  var controllerStores = /* @__PURE__ */ new WeakMap();
2223
2180
  function defaultNavigation() {
2224
2181
  return {
@@ -2260,7 +2217,7 @@ function createPreprPreview(opts = {}) {
2260
2217
  const search = new URLSearchParams(window.location.search);
2261
2218
  const hideBar = search.get(PARAM_HIDE_BAR) === "true";
2262
2219
  if (hideBar) {
2263
- debug9.log(`${PARAM_HIDE_BAR}=true \u2014 no visible toolbar`);
2220
+ debug7.log(`${PARAM_HIDE_BAR}=true \u2014 no visible toolbar`);
2264
2221
  }
2265
2222
  const mountUi = (options?.ui ?? true) && !isIframe && !hideBar;
2266
2223
  const locale = resolveLocale(options);
@@ -2296,17 +2253,18 @@ function createPreprPreview(opts = {}) {
2296
2253
  tooltip: mountUi,
2297
2254
  // In the editor, ask the parent to focus the field instead of opening a new
2298
2255
  // tab. Standalone previews keep the window.open behaviour.
2299
- onEdit: ({ href, id, field, locale: contentLocale }) => {
2256
+ onEdit: ({ href, origin, id, field }) => {
2300
2257
  const safeHref = href ? safeEditUrl(href) : null;
2301
2258
  if (href && !safeHref) {
2302
- debug9.warn("ignored edit request with unsupported href scheme");
2259
+ debug7.warn("ignored edit request with unsupported href scheme");
2303
2260
  return;
2304
2261
  }
2305
2262
  if (isIframe) {
2306
2263
  sendPreprEvent("field_edit_requested", {
2264
+ href: safeHref ?? void 0,
2265
+ origin,
2307
2266
  id,
2308
- field,
2309
- locale: contentLocale
2267
+ field
2310
2268
  });
2311
2269
  } else if (safeHref) {
2312
2270
  window.open(safeHref, "_blank", "noopener,noreferrer");
@@ -2314,7 +2272,8 @@ function createPreprPreview(opts = {}) {
2314
2272
  }
2315
2273
  });
2316
2274
  const autoClean = createStegaAutoClean({
2317
- enabled: options?.autoClean
2275
+ enabled: options?.autoClean,
2276
+ isHydrated: opts.isHydrated
2318
2277
  });
2319
2278
  autoClean.start();
2320
2279
  const handleChange = createChangeHandler({
@@ -2333,15 +2292,7 @@ function createPreprPreview(opts = {}) {
2333
2292
  handleChange(before, state);
2334
2293
  });
2335
2294
  const bridge = createIframeBridge(store, {
2336
- allowedEditorOrigins: options?.allowedEditorOrigins,
2337
- // The editor selecting a field scrolls the matching element into view. The
2338
- // lookup relies on the attributes the stega scan writes, so it finds
2339
- // nothing until encoded content has been tagged.
2340
- onScrollToField: ({ field, id }) => {
2341
- if (!scrollToField({ field, id })) {
2342
- debug9.log("no element on this page renders field", field);
2343
- }
2344
- }
2295
+ allowedEditorOrigins: options?.allowedEditorOrigins
2345
2296
  });
2346
2297
  sendPreprEvent(
2347
2298
  "getScrollPosition",
@@ -2361,13 +2312,20 @@ function createPreprPreview(opts = {}) {
2361
2312
  bridge.stop();
2362
2313
  }
2363
2314
  el?.remove();
2364
- debug9.log("preview destroyed");
2315
+ debug7.log("preview destroyed");
2365
2316
  }
2366
2317
  const controller = { destroy };
2367
2318
  controllerStores.set(controller, store);
2368
2319
  return controller;
2369
2320
  }
2370
2321
 
2322
+ // src/core/personalization.ts
2323
+ var PERSONALIZED_VARY = /\bPrepr-(Segments|ABTesting)/i;
2324
+ function isPersonalizedResponse(source) {
2325
+ const headers = "headers" in source ? source.headers : source;
2326
+ return PERSONALIZED_VARY.test(headers.get("vary") ?? "");
2327
+ }
2328
+
2371
2329
  // src/core/server.ts
2372
2330
  var PreprError = class extends Error {
2373
2331
  constructor(message, code, context, originalError) {
@@ -2683,6 +2641,10 @@ function processFrameworkRequest(request, opts, framework) {
2683
2641
  });
2684
2642
  }
2685
2643
  }
2644
+ if (requestHeaders.has("Prepr-Segments")) {
2645
+ requestHeaders.delete("Prepr-Customer-Id");
2646
+ requestHeaders.delete("Prepr-Customer-Id-Created");
2647
+ }
2686
2648
  return { requestHeaders, responseCookies };
2687
2649
  }
2688
2650
 
@@ -2765,6 +2727,7 @@ function setTrackingParam(key, value) {
2765
2727
  getPreprHeadersFromHeaders,
2766
2728
  getPreprUUIDFromHeaders,
2767
2729
  getToolbarPropsFromHeaders,
2730
+ isPersonalizedResponse,
2768
2731
  loadTrackingPixel,
2769
2732
  processPreprRequest,
2770
2733
  resolveFeatures,
package/dist/index.d.cts CHANGED
@@ -1,11 +1,13 @@
1
- import { P as PreprFeatures, R as ResolvedPreprFeatures, L as Locale, a as PreprSegment, b as PreprVariant, c as PreprToolbarProps, d as PreprPreviewOptions, e as PreprErrorCode, f as PreprHeaders } from './pixel-e1fUWl2Y.cjs';
2
- export { g as PreprEnvironment, h as PreprEventType, i as PreprFeatureConfig, j as PreprHeaderName, k as PreprPixelConfig, l as PreprToolbarComponentProps, m as PreprToolbarOptions, n as loadTrackingPixel, s as setTrackingParam, t as trackEvent } from './pixel-e1fUWl2Y.cjs';
1
+ import { b as PreprFeatures, R as ResolvedPreprFeatures, L as Locale, c as PreprSegment, d as PreprVariant, e as PreprToolbarProps, f as PreprPreviewOptions } from './types-BYH9XqmN.cjs';
2
+ export { g as PreprEnvironment, a as PreprErrorCode, h as PreprEventType, i as PreprFeatureConfig, j as PreprHeaderName, P as PreprHeaders, k as PreprToolbarComponentProps, l as PreprToolbarOptions } from './types-BYH9XqmN.cjs';
3
+ export { P as PreprError, e as extractAccessToken, g as getActiveSegmentFromHeaders, a as getActiveVariantFromHeaders, b as getPreprEnvironmentSegments, c as getPreprHeadersFromHeaders, d as getPreprUUIDFromHeaders, f as getToolbarPropsFromHeaders, i as isPersonalizedResponse, v as validatePreprToken } from './server-DJuS3YU0.cjs';
4
+ export { P as PreprPixelConfig, l as loadTrackingPixel, s as setTrackingParam, t as trackEvent } from './pixel-DwW7cXJm.cjs';
3
5
 
4
6
  /**
5
7
  * Package version; must match package.json. Lives in its own leaf module so
6
8
  * core can import it without cycling through the barrel.
7
9
  */
8
- declare const VERSION = "0.5.0-rc.1";
10
+ declare const VERSION = "0.6.0-alpha.0";
9
11
 
10
12
  /**
11
13
  * Resolve a `PreprFeatures` bag to plain booleans, defaulting every omitted key
@@ -65,6 +67,11 @@ interface CreatePreprPreviewOptions {
65
67
  props?: PreprToolbarProps;
66
68
  options?: PreprPreviewOptions;
67
69
  navigation?: PreprNavigationAdapter;
70
+ /**
71
+ * @internal Supplied by framework wrappers so stega stripping waits until
72
+ * the framework has hydrated the text it would change.
73
+ */
74
+ isHydrated?: (node: Text) => boolean;
68
75
  }
69
76
  interface PreprPreviewController {
70
77
  destroy(): void;
@@ -137,72 +144,14 @@ declare class PreprToolbarElement extends HTMLElementBase {
137
144
  /** Idempotent, including across bundles that each try to define the element. */
138
145
  declare function definePreprToolbar(): void;
139
146
 
140
- /** Which field the editor asked the preview to bring on screen. */
141
- interface ScrollToFieldMessage {
142
- field: string;
143
- id?: string;
144
- }
145
147
  interface IframeBridgeOptions {
146
148
  /**
147
149
  * Replace the `*.prepr.io` wildcard with an exact origin list. Intended for
148
150
  * self-hosted editors; when set, the wildcard no longer applies.
149
151
  */
150
152
  allowedEditorOrigins?: string[];
151
- /**
152
- * Called when the editor selects a field and wants the preview to scroll to
153
- * it. Only fires for messages from the handshaked editor origin. The bridge
154
- * stays free of DOM lookups: the preview runtime supplies `scrollToField`.
155
- */
156
- onScrollToField?: (message: ScrollToFieldMessage) => void;
157
153
  }
158
154
 
159
- /** Error thrown by the token helpers and the segments fetch. */
160
- declare class PreprError extends Error {
161
- readonly code: PreprErrorCode;
162
- readonly context?: string | undefined;
163
- readonly originalError?: Error | undefined;
164
- constructor(message: string, code: PreprErrorCode, context?: string | undefined, originalError?: Error | undefined);
165
- }
166
- /** Prepr Customer ID, or null if the middleware did not run for this request. */
167
- declare function getPreprUUIDFromHeaders(headers: Headers): string | null;
168
- /** Active segment ID, or null when no segment is selected. */
169
- declare function getActiveSegmentFromHeaders(headers: Headers): string | null;
170
- /** Active A/B variant (`'A'`/`'B'`), or null when no test is running. */
171
- declare function getActiveVariantFromHeaders(headers: Headers): string | null;
172
- /**
173
- * Collect the Prepr headers into a `PreprHeaders`. Lookups are case-insensitive
174
- * per the Fetch spec, but the returned keys use the declared casing.
175
- */
176
- declare function getPreprHeadersFromHeaders(headers: Headers): PreprHeaders;
177
- /**
178
- * Asserts `token` is the full `https://graphql.prepr.io/<token>` URL.
179
- * @throws PreprError if the token is missing or not an HTTPS URL.
180
- */
181
- declare function validatePreprToken(token: string): void;
182
- /**
183
- * Pulls the access token out of a GraphQL URL: `https://graphql.prepr.io/abc123`
184
- * -> `abc123`.
185
- * @throws PreprError if the URL is not a valid `graphql.prepr.io` URL.
186
- */
187
- declare function extractAccessToken(graphqlUrl: string): string;
188
- /**
189
- * Fetches the environment's segments from the Prepr API.
190
- *
191
- * `token` must be a Prepr GraphQL URL with scope 'segments'.
192
- * @throws PreprError if the token is invalid or the request fails.
193
- */
194
- declare function getPreprEnvironmentSegments(token: string): Promise<PreprSegment[]>;
195
- /**
196
- * Never throws: a failed segments fetch yields an empty list so the toolbar
197
- * still renders. The list is returned under both `segments` and the deprecated
198
- * `data` alias.
199
- *
200
- * Pass the same `features` object you give the toolbar and the middleware: with
201
- * segments disabled the `_Segments` request is skipped entirely, saving a
202
- * round-trip per page.
203
- */
204
- declare function getToolbarPropsFromHeaders(headers: Headers, token: string, features?: PreprFeatures): Promise<PreprToolbarProps>;
205
-
206
155
  interface CookieSpec {
207
156
  name: string;
208
157
  value: string;
@@ -241,4 +190,4 @@ interface PreprMiddlewareOptions {
241
190
  */
242
191
  declare function processPreprRequest(request: Request, opts?: PreprMiddlewareOptions): PreprMiddlewareResult;
243
192
 
244
- export { type CookieSpec, type CreatePreprPreviewOptions, type IframeBridgeOptions, PreprError, PreprErrorCode, PreprFeatures, PreprHeaders, type PreprMiddlewareOptions, type PreprMiddlewareResult, type PreprNavigationAdapter, type PreprPreviewController, PreprPreviewOptions, PreprSegment, PreprToolbarElement, PreprToolbarProps, PreprVariant, ResolvedPreprFeatures, type ToolbarState, type ToolbarStore, VERSION, createPreprPreview, createToolbarStore, definePreprToolbar, extractAccessToken, getActiveSegmentFromHeaders, getActiveVariantFromHeaders, getPreprEnvironmentSegments, getPreprHeadersFromHeaders, getPreprUUIDFromHeaders, getToolbarPropsFromHeaders, processPreprRequest, resolveFeatures, stegaClean, validatePreprToken };
193
+ export { type CookieSpec, type CreatePreprPreviewOptions, type IframeBridgeOptions, PreprFeatures, type PreprMiddlewareOptions, type PreprMiddlewareResult, type PreprNavigationAdapter, type PreprPreviewController, PreprPreviewOptions, PreprSegment, PreprToolbarElement, PreprToolbarProps, PreprVariant, ResolvedPreprFeatures, type ToolbarState, type ToolbarStore, VERSION, createPreprPreview, createToolbarStore, definePreprToolbar, processPreprRequest, resolveFeatures, stegaClean };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,13 @@
1
- import { P as PreprFeatures, R as ResolvedPreprFeatures, L as Locale, a as PreprSegment, b as PreprVariant, c as PreprToolbarProps, d as PreprPreviewOptions, e as PreprErrorCode, f as PreprHeaders } from './pixel-e1fUWl2Y.js';
2
- export { g as PreprEnvironment, h as PreprEventType, i as PreprFeatureConfig, j as PreprHeaderName, k as PreprPixelConfig, l as PreprToolbarComponentProps, m as PreprToolbarOptions, n as loadTrackingPixel, s as setTrackingParam, t as trackEvent } from './pixel-e1fUWl2Y.js';
1
+ import { b as PreprFeatures, R as ResolvedPreprFeatures, L as Locale, c as PreprSegment, d as PreprVariant, e as PreprToolbarProps, f as PreprPreviewOptions } from './types-BYH9XqmN.js';
2
+ export { g as PreprEnvironment, a as PreprErrorCode, h as PreprEventType, i as PreprFeatureConfig, j as PreprHeaderName, P as PreprHeaders, k as PreprToolbarComponentProps, l as PreprToolbarOptions } from './types-BYH9XqmN.js';
3
+ export { P as PreprError, e as extractAccessToken, g as getActiveSegmentFromHeaders, a as getActiveVariantFromHeaders, b as getPreprEnvironmentSegments, c as getPreprHeadersFromHeaders, d as getPreprUUIDFromHeaders, f as getToolbarPropsFromHeaders, i as isPersonalizedResponse, v as validatePreprToken } from './server-Brr1UgtW.js';
4
+ export { P as PreprPixelConfig, l as loadTrackingPixel, s as setTrackingParam, t as trackEvent } from './pixel-DwW7cXJm.js';
3
5
 
4
6
  /**
5
7
  * Package version; must match package.json. Lives in its own leaf module so
6
8
  * core can import it without cycling through the barrel.
7
9
  */
8
- declare const VERSION = "0.5.0-rc.1";
10
+ declare const VERSION = "0.6.0-alpha.0";
9
11
 
10
12
  /**
11
13
  * Resolve a `PreprFeatures` bag to plain booleans, defaulting every omitted key
@@ -65,6 +67,11 @@ interface CreatePreprPreviewOptions {
65
67
  props?: PreprToolbarProps;
66
68
  options?: PreprPreviewOptions;
67
69
  navigation?: PreprNavigationAdapter;
70
+ /**
71
+ * @internal Supplied by framework wrappers so stega stripping waits until
72
+ * the framework has hydrated the text it would change.
73
+ */
74
+ isHydrated?: (node: Text) => boolean;
68
75
  }
69
76
  interface PreprPreviewController {
70
77
  destroy(): void;
@@ -137,72 +144,14 @@ declare class PreprToolbarElement extends HTMLElementBase {
137
144
  /** Idempotent, including across bundles that each try to define the element. */
138
145
  declare function definePreprToolbar(): void;
139
146
 
140
- /** Which field the editor asked the preview to bring on screen. */
141
- interface ScrollToFieldMessage {
142
- field: string;
143
- id?: string;
144
- }
145
147
  interface IframeBridgeOptions {
146
148
  /**
147
149
  * Replace the `*.prepr.io` wildcard with an exact origin list. Intended for
148
150
  * self-hosted editors; when set, the wildcard no longer applies.
149
151
  */
150
152
  allowedEditorOrigins?: string[];
151
- /**
152
- * Called when the editor selects a field and wants the preview to scroll to
153
- * it. Only fires for messages from the handshaked editor origin. The bridge
154
- * stays free of DOM lookups: the preview runtime supplies `scrollToField`.
155
- */
156
- onScrollToField?: (message: ScrollToFieldMessage) => void;
157
153
  }
158
154
 
159
- /** Error thrown by the token helpers and the segments fetch. */
160
- declare class PreprError extends Error {
161
- readonly code: PreprErrorCode;
162
- readonly context?: string | undefined;
163
- readonly originalError?: Error | undefined;
164
- constructor(message: string, code: PreprErrorCode, context?: string | undefined, originalError?: Error | undefined);
165
- }
166
- /** Prepr Customer ID, or null if the middleware did not run for this request. */
167
- declare function getPreprUUIDFromHeaders(headers: Headers): string | null;
168
- /** Active segment ID, or null when no segment is selected. */
169
- declare function getActiveSegmentFromHeaders(headers: Headers): string | null;
170
- /** Active A/B variant (`'A'`/`'B'`), or null when no test is running. */
171
- declare function getActiveVariantFromHeaders(headers: Headers): string | null;
172
- /**
173
- * Collect the Prepr headers into a `PreprHeaders`. Lookups are case-insensitive
174
- * per the Fetch spec, but the returned keys use the declared casing.
175
- */
176
- declare function getPreprHeadersFromHeaders(headers: Headers): PreprHeaders;
177
- /**
178
- * Asserts `token` is the full `https://graphql.prepr.io/<token>` URL.
179
- * @throws PreprError if the token is missing or not an HTTPS URL.
180
- */
181
- declare function validatePreprToken(token: string): void;
182
- /**
183
- * Pulls the access token out of a GraphQL URL: `https://graphql.prepr.io/abc123`
184
- * -> `abc123`.
185
- * @throws PreprError if the URL is not a valid `graphql.prepr.io` URL.
186
- */
187
- declare function extractAccessToken(graphqlUrl: string): string;
188
- /**
189
- * Fetches the environment's segments from the Prepr API.
190
- *
191
- * `token` must be a Prepr GraphQL URL with scope 'segments'.
192
- * @throws PreprError if the token is invalid or the request fails.
193
- */
194
- declare function getPreprEnvironmentSegments(token: string): Promise<PreprSegment[]>;
195
- /**
196
- * Never throws: a failed segments fetch yields an empty list so the toolbar
197
- * still renders. The list is returned under both `segments` and the deprecated
198
- * `data` alias.
199
- *
200
- * Pass the same `features` object you give the toolbar and the middleware: with
201
- * segments disabled the `_Segments` request is skipped entirely, saving a
202
- * round-trip per page.
203
- */
204
- declare function getToolbarPropsFromHeaders(headers: Headers, token: string, features?: PreprFeatures): Promise<PreprToolbarProps>;
205
-
206
155
  interface CookieSpec {
207
156
  name: string;
208
157
  value: string;
@@ -241,4 +190,4 @@ interface PreprMiddlewareOptions {
241
190
  */
242
191
  declare function processPreprRequest(request: Request, opts?: PreprMiddlewareOptions): PreprMiddlewareResult;
243
192
 
244
- export { type CookieSpec, type CreatePreprPreviewOptions, type IframeBridgeOptions, PreprError, PreprErrorCode, PreprFeatures, PreprHeaders, type PreprMiddlewareOptions, type PreprMiddlewareResult, type PreprNavigationAdapter, type PreprPreviewController, PreprPreviewOptions, PreprSegment, PreprToolbarElement, PreprToolbarProps, PreprVariant, ResolvedPreprFeatures, type ToolbarState, type ToolbarStore, VERSION, createPreprPreview, createToolbarStore, definePreprToolbar, extractAccessToken, getActiveSegmentFromHeaders, getActiveVariantFromHeaders, getPreprEnvironmentSegments, getPreprHeadersFromHeaders, getPreprUUIDFromHeaders, getToolbarPropsFromHeaders, processPreprRequest, resolveFeatures, stegaClean, validatePreprToken };
193
+ export { type CookieSpec, type CreatePreprPreviewOptions, type IframeBridgeOptions, PreprFeatures, type PreprMiddlewareOptions, type PreprMiddlewareResult, type PreprNavigationAdapter, type PreprPreviewController, PreprPreviewOptions, PreprSegment, PreprToolbarElement, PreprToolbarProps, PreprVariant, ResolvedPreprFeatures, type ToolbarState, type ToolbarStore, VERSION, createPreprPreview, createToolbarStore, definePreprToolbar, processPreprRequest, resolveFeatures, stegaClean };