@preprio/toolkit 0.2.0-beta.5 → 0.2.0-beta.7

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.
@@ -30,7 +30,7 @@ __export(astro_exports, {
30
30
  module.exports = __toCommonJS(astro_exports);
31
31
 
32
32
  // src/version.ts
33
- var VERSION = "0.2.0-beta.5";
33
+ var VERSION = "0.2.0-beta.7";
34
34
 
35
35
  // src/core/features.ts
36
36
  function isEnabled(config) {
@@ -193,6 +193,11 @@ var UTM_PARAMS = [
193
193
  "utm_content",
194
194
  "utm_campaign"
195
195
  ];
196
+ var MAX_HEADER_VALUE_LENGTH = 2048;
197
+ function sanitizeHeaderValue(value) {
198
+ const stripped = value.replace(/[\u0000-\u001f\u007f]/g, "");
199
+ return stripped.length > MAX_HEADER_VALUE_LENGTH ? stripped.slice(0, MAX_HEADER_VALUE_LENGTH) : stripped;
200
+ }
196
201
  function serializeCookie(cookie) {
197
202
  const parts = [
198
203
  `${cookie.name}=${encodeURIComponent(cookie.value)}`,
@@ -224,6 +229,15 @@ function parseCookies(request) {
224
229
  function processPreprRequest(request, opts) {
225
230
  const requestHeaders = new Headers(request.headers);
226
231
  const responseCookies = [];
232
+ const inboundPreprHeaders = [];
233
+ requestHeaders.forEach((_value, key) => {
234
+ if (key.toLowerCase().startsWith("prepr-")) {
235
+ inboundPreprHeaders.push(key);
236
+ }
237
+ });
238
+ for (const key of inboundPreprHeaders) {
239
+ requestHeaders.delete(key);
240
+ }
227
241
  const requestCookies = parseCookies(request);
228
242
  const url = new URL(request.url);
229
243
  const searchParams = url.searchParams;
@@ -233,25 +247,28 @@ function processPreprRequest(request, opts) {
233
247
  for (const key of UTM_PARAMS) {
234
248
  const value = searchParams.get(key);
235
249
  if (value !== null) {
236
- requestHeaders.set(`Prepr-Context-${key}`, value);
250
+ requestHeaders.set(`Prepr-Context-${key}`, sanitizeHeaderValue(value));
237
251
  }
238
252
  }
239
253
  const referer = request.headers.get("referer");
240
254
  if (referer) {
241
- requestHeaders.set("Prepr-Context-initial_referral", referer);
255
+ requestHeaders.set(
256
+ "Prepr-Context-initial_referral",
257
+ sanitizeHeaderValue(referer)
258
+ );
242
259
  }
243
260
  const userAgent = request.headers.get("user-agent");
244
261
  if (userAgent) {
245
- requestHeaders.set("Prepr-User-Agent", userAgent);
262
+ requestHeaders.set("Prepr-User-Agent", sanitizeHeaderValue(userAgent));
246
263
  }
247
264
  requestHeaders.set("Prepr-Package", `@preprio/toolkit@${opts?.version ?? VERSION}`);
248
265
  const ip = request.headers.get("Cf-Connecting-Ip") ?? request.headers.get("x-real-ip");
249
266
  if (ip) {
250
- requestHeaders.set("Prepr-Visitor-IP", ip);
267
+ requestHeaders.set("Prepr-Visitor-IP", sanitizeHeaderValue(ip));
251
268
  }
252
269
  const hutkCookie = requestCookies.get(COOKIE_HUBSPOT);
253
270
  if (hutkCookie) {
254
- requestHeaders.set("Prepr-Hubspot-Id", hutkCookie);
271
+ requestHeaders.set("Prepr-Hubspot-Id", sanitizeHeaderValue(hutkCookie));
255
272
  }
256
273
  let uid = requestCookies.get(COOKIE_UID);
257
274
  if (!uid) {
@@ -291,16 +308,16 @@ function processPreprRequest(request, opts) {
291
308
  if (!isLivePreview) {
292
309
  const segmentCookie = features.segments ? requestCookies.get(COOKIE_SEGMENT) : void 0;
293
310
  if (segmentCookie) {
294
- requestHeaders.set("Prepr-Segments", segmentCookie);
311
+ requestHeaders.set("Prepr-Segments", sanitizeHeaderValue(segmentCookie));
295
312
  }
296
313
  const abCookie = features.abTesting ? requestCookies.get(COOKIE_VARIANT) : void 0;
297
314
  if (abCookie) {
298
- requestHeaders.set("Prepr-ABtesting", abCookie);
315
+ requestHeaders.set("Prepr-ABtesting", sanitizeHeaderValue(abCookie));
299
316
  }
300
317
  }
301
318
  const previewAb = features.abTesting ? searchParams.get(PARAM_VARIANT) : null;
302
319
  if (previewAb !== null) {
303
- requestHeaders.set("Prepr-ABtesting", previewAb);
320
+ requestHeaders.set("Prepr-ABtesting", sanitizeHeaderValue(previewAb));
304
321
  if (!isLivePreview) {
305
322
  responseCookies.push({
306
323
  name: COOKIE_VARIANT,
@@ -313,7 +330,7 @@ function processPreprRequest(request, opts) {
313
330
  }
314
331
  const previewSegment = features.segments ? searchParams.get(PARAM_SEGMENT) : null;
315
332
  if (previewSegment !== null) {
316
- requestHeaders.set("Prepr-Segments", previewSegment);
333
+ requestHeaders.set("Prepr-Segments", sanitizeHeaderValue(previewSegment));
317
334
  if (!isLivePreview) {
318
335
  responseCookies.push({
319
336
  name: COOKIE_SEGMENT,
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var VERSION = "0.2.0-beta.5";
2
+ var VERSION = "0.2.0-beta.7";
3
3
 
4
4
  // src/core/features.ts
5
5
  function isEnabled(config) {
@@ -162,6 +162,11 @@ var UTM_PARAMS = [
162
162
  "utm_content",
163
163
  "utm_campaign"
164
164
  ];
165
+ var MAX_HEADER_VALUE_LENGTH = 2048;
166
+ function sanitizeHeaderValue(value) {
167
+ const stripped = value.replace(/[\u0000-\u001f\u007f]/g, "");
168
+ return stripped.length > MAX_HEADER_VALUE_LENGTH ? stripped.slice(0, MAX_HEADER_VALUE_LENGTH) : stripped;
169
+ }
165
170
  function serializeCookie(cookie) {
166
171
  const parts = [
167
172
  `${cookie.name}=${encodeURIComponent(cookie.value)}`,
@@ -193,6 +198,15 @@ function parseCookies(request) {
193
198
  function processPreprRequest(request, opts) {
194
199
  const requestHeaders = new Headers(request.headers);
195
200
  const responseCookies = [];
201
+ const inboundPreprHeaders = [];
202
+ requestHeaders.forEach((_value, key) => {
203
+ if (key.toLowerCase().startsWith("prepr-")) {
204
+ inboundPreprHeaders.push(key);
205
+ }
206
+ });
207
+ for (const key of inboundPreprHeaders) {
208
+ requestHeaders.delete(key);
209
+ }
196
210
  const requestCookies = parseCookies(request);
197
211
  const url = new URL(request.url);
198
212
  const searchParams = url.searchParams;
@@ -202,25 +216,28 @@ function processPreprRequest(request, opts) {
202
216
  for (const key of UTM_PARAMS) {
203
217
  const value = searchParams.get(key);
204
218
  if (value !== null) {
205
- requestHeaders.set(`Prepr-Context-${key}`, value);
219
+ requestHeaders.set(`Prepr-Context-${key}`, sanitizeHeaderValue(value));
206
220
  }
207
221
  }
208
222
  const referer = request.headers.get("referer");
209
223
  if (referer) {
210
- requestHeaders.set("Prepr-Context-initial_referral", referer);
224
+ requestHeaders.set(
225
+ "Prepr-Context-initial_referral",
226
+ sanitizeHeaderValue(referer)
227
+ );
211
228
  }
212
229
  const userAgent = request.headers.get("user-agent");
213
230
  if (userAgent) {
214
- requestHeaders.set("Prepr-User-Agent", userAgent);
231
+ requestHeaders.set("Prepr-User-Agent", sanitizeHeaderValue(userAgent));
215
232
  }
216
233
  requestHeaders.set("Prepr-Package", `@preprio/toolkit@${opts?.version ?? VERSION}`);
217
234
  const ip = request.headers.get("Cf-Connecting-Ip") ?? request.headers.get("x-real-ip");
218
235
  if (ip) {
219
- requestHeaders.set("Prepr-Visitor-IP", ip);
236
+ requestHeaders.set("Prepr-Visitor-IP", sanitizeHeaderValue(ip));
220
237
  }
221
238
  const hutkCookie = requestCookies.get(COOKIE_HUBSPOT);
222
239
  if (hutkCookie) {
223
- requestHeaders.set("Prepr-Hubspot-Id", hutkCookie);
240
+ requestHeaders.set("Prepr-Hubspot-Id", sanitizeHeaderValue(hutkCookie));
224
241
  }
225
242
  let uid = requestCookies.get(COOKIE_UID);
226
243
  if (!uid) {
@@ -260,16 +277,16 @@ function processPreprRequest(request, opts) {
260
277
  if (!isLivePreview) {
261
278
  const segmentCookie = features.segments ? requestCookies.get(COOKIE_SEGMENT) : void 0;
262
279
  if (segmentCookie) {
263
- requestHeaders.set("Prepr-Segments", segmentCookie);
280
+ requestHeaders.set("Prepr-Segments", sanitizeHeaderValue(segmentCookie));
264
281
  }
265
282
  const abCookie = features.abTesting ? requestCookies.get(COOKIE_VARIANT) : void 0;
266
283
  if (abCookie) {
267
- requestHeaders.set("Prepr-ABtesting", abCookie);
284
+ requestHeaders.set("Prepr-ABtesting", sanitizeHeaderValue(abCookie));
268
285
  }
269
286
  }
270
287
  const previewAb = features.abTesting ? searchParams.get(PARAM_VARIANT) : null;
271
288
  if (previewAb !== null) {
272
- requestHeaders.set("Prepr-ABtesting", previewAb);
289
+ requestHeaders.set("Prepr-ABtesting", sanitizeHeaderValue(previewAb));
273
290
  if (!isLivePreview) {
274
291
  responseCookies.push({
275
292
  name: COOKIE_VARIANT,
@@ -282,7 +299,7 @@ function processPreprRequest(request, opts) {
282
299
  }
283
300
  const previewSegment = features.segments ? searchParams.get(PARAM_SEGMENT) : null;
284
301
  if (previewSegment !== null) {
285
- requestHeaders.set("Prepr-Segments", previewSegment);
302
+ requestHeaders.set("Prepr-Segments", sanitizeHeaderValue(previewSegment));
286
303
  if (!isLivePreview) {
287
304
  responseCookies.push({
288
305
  name: COOKIE_SEGMENT,
@@ -644,6 +644,8 @@ function createStegaAutoClean() {
644
644
  function scanAndClean() {
645
645
  processing = true;
646
646
  walkTextNodes(document.body, cleanTextNode);
647
+ const title = vercelStegaSplit(document.title);
648
+ if (title.encoded) document.title = title.cleaned;
647
649
  processing = false;
648
650
  }
649
651
  function collectAffected(mutations) {
@@ -689,15 +691,8 @@ function createStegaAutoClean() {
689
691
  }
690
692
  function start() {
691
693
  if (observer || typeof window === "undefined") return;
692
- const run = () => {
693
- scanAndClean();
694
- setupObserver();
695
- };
696
- if ("requestIdleCallback" in window) {
697
- window.requestIdleCallback(run);
698
- } else {
699
- setTimeout(run, 0);
700
- }
694
+ scanAndClean();
695
+ setupObserver();
701
696
  debug6.log("stega auto-clean started");
702
697
  }
703
698
  function stop() {
@@ -1909,7 +1904,10 @@ function createIframeBridge(store, options = {}) {
1909
1904
  };
1910
1905
  return {
1911
1906
  start() {
1912
- sendPreprEvent("loaded", void 0, { allowUntrustedTarget: true });
1907
+ const features = store?.get().features;
1908
+ sendPreprEvent("loaded", features ? { features } : void 0, {
1909
+ allowUntrustedTarget: true
1910
+ });
1913
1911
  window.addEventListener("keydown", onKeyDown);
1914
1912
  window.addEventListener("message", onMessage);
1915
1913
  },
@@ -1931,7 +1929,7 @@ function cookieOpts() {
1931
1929
  };
1932
1930
  }
1933
1931
  function createChangeHandler(deps) {
1934
- const { store, stega, syncAutoClean, features, editingEnabled } = deps;
1932
+ const { store, stega, features, editingEnabled } = deps;
1935
1933
  function updateParams(patch) {
1936
1934
  const [path, existing] = splitPath(deps.currentPath());
1937
1935
  const params = new URLSearchParams(existing);
@@ -1994,16 +1992,17 @@ function createChangeHandler(deps) {
1994
1992
  setCookie(COOKIE_PREVIEW_MODE, String(after.previewMode), cookieOpts());
1995
1993
  setCookie(COOKIE_TOOLBAR_OPEN, "false", cookieOpts());
1996
1994
  sendPreprEvent("preview_mode_toggled", { previewMode: after.previewMode });
1997
- syncAutoClean(after.previewMode);
1998
- const [path, existing] = splitPath(deps.currentPath());
1999
- const params = new URLSearchParams(existing);
2000
- if (params.has(PARAM_SEGMENT) || params.has(PARAM_VARIANT)) {
2001
- params.delete(PARAM_SEGMENT);
2002
- params.delete(PARAM_VARIANT);
2003
- const query = params.toString();
2004
- deps.navigate(query ? `${path}?${query}` : path);
2005
- } else {
2006
- deps.reload();
1995
+ if (!after.isIframe) {
1996
+ const [path, existing] = splitPath(deps.currentPath());
1997
+ const params = new URLSearchParams(existing);
1998
+ if (params.has(PARAM_SEGMENT) || params.has(PARAM_VARIANT)) {
1999
+ params.delete(PARAM_SEGMENT);
2000
+ params.delete(PARAM_VARIANT);
2001
+ const query = params.toString();
2002
+ deps.navigate(query ? `${path}?${query}` : path);
2003
+ } else {
2004
+ deps.reload();
2005
+ }
2007
2006
  }
2008
2007
  }
2009
2008
  if (before.toolbarOpen !== after.toolbarOpen) {
@@ -2026,6 +2025,14 @@ function defaultNavigation() {
2026
2025
  currentPath: () => window.location.pathname + window.location.search
2027
2026
  };
2028
2027
  }
2028
+ function safeEditUrl(href) {
2029
+ try {
2030
+ const url = new URL(href, window.location.href);
2031
+ return url.protocol === "https:" || url.protocol === "http:" ? url.href : null;
2032
+ } catch {
2033
+ return null;
2034
+ }
2035
+ }
2029
2036
  function buildSegments(data) {
2030
2037
  return [{ _id: "all_other_users", name: "All other users" }, ...data];
2031
2038
  }
@@ -2089,24 +2096,25 @@ function createPreprPreview(opts = {}) {
2089
2096
  // In the editor, ask the parent to focus the field instead of opening a new
2090
2097
  // tab. Standalone previews keep the window.open behaviour.
2091
2098
  onEdit: ({ href, origin, id, field }) => {
2099
+ const safeHref = href ? safeEditUrl(href) : null;
2100
+ if (href && !safeHref) {
2101
+ debug7.warn("ignored edit request with unsupported href scheme");
2102
+ return;
2103
+ }
2092
2104
  if (isIframe) {
2093
- sendPreprEvent("field_edit_requested", { href, origin, id, field });
2094
- } else if (href) {
2095
- window.open(href);
2105
+ sendPreprEvent("field_edit_requested", {
2106
+ href: safeHref ?? void 0,
2107
+ origin,
2108
+ id,
2109
+ field
2110
+ });
2111
+ } else if (safeHref) {
2112
+ window.open(safeHref, "_blank", "noopener,noreferrer");
2096
2113
  }
2097
2114
  }
2098
2115
  });
2099
2116
  const autoClean = createStegaAutoClean();
2100
- let autoCleanActive = false;
2101
- function syncAutoClean(active) {
2102
- if (active && !autoCleanActive) {
2103
- autoClean.start();
2104
- autoCleanActive = true;
2105
- } else if (!active && autoCleanActive) {
2106
- autoClean.stop();
2107
- autoCleanActive = false;
2108
- }
2109
- }
2117
+ autoClean.start();
2110
2118
  const handleChange = createChangeHandler({
2111
2119
  store,
2112
2120
  features,
@@ -2114,8 +2122,7 @@ function createPreprPreview(opts = {}) {
2114
2122
  navigate: (url) => navigation.navigate(url),
2115
2123
  currentPath: () => navigation.currentPath(),
2116
2124
  reload: navigation.reload ?? (() => window.location.reload()),
2117
- stega,
2118
- syncAutoClean
2125
+ stega
2119
2126
  });
2120
2127
  let prev = store.get();
2121
2128
  const unsubscribe = store.subscribe((state) => {
@@ -2123,7 +2130,6 @@ function createPreprPreview(opts = {}) {
2123
2130
  prev = state;
2124
2131
  handleChange(before, state);
2125
2132
  });
2126
- syncAutoClean(store.get().previewMode);
2127
2133
  const bridge = createIframeBridge(store, {
2128
2134
  allowedEditorOrigins: options?.allowedEditorOrigins
2129
2135
  });
@@ -2136,7 +2142,7 @@ function createPreprPreview(opts = {}) {
2136
2142
  function destroy() {
2137
2143
  unsubscribe();
2138
2144
  stega.stop();
2139
- syncAutoClean(false);
2145
+ autoClean.stop();
2140
2146
  if (isIframe) {
2141
2147
  bridge.stop();
2142
2148
  }
package/dist/index.cjs CHANGED
@@ -44,7 +44,7 @@ __export(src_exports, {
44
44
  module.exports = __toCommonJS(src_exports);
45
45
 
46
46
  // src/version.ts
47
- var VERSION = "0.2.0-beta.5";
47
+ var VERSION = "0.2.0-beta.7";
48
48
 
49
49
  // src/core/features.ts
50
50
  function isEnabled(config) {
@@ -355,7 +355,10 @@ function createIframeBridge(store, options = {}) {
355
355
  };
356
356
  return {
357
357
  start() {
358
- sendPreprEvent("loaded", void 0, { allowUntrustedTarget: true });
358
+ const features = store?.get().features;
359
+ sendPreprEvent("loaded", features ? { features } : void 0, {
360
+ allowUntrustedTarget: true
361
+ });
359
362
  window.addEventListener("keydown", onKeyDown);
360
363
  window.addEventListener("message", onMessage);
361
364
  },
@@ -461,6 +464,8 @@ function createStegaAutoClean() {
461
464
  function scanAndClean() {
462
465
  processing = true;
463
466
  walkTextNodes(document.body, cleanTextNode);
467
+ const title = (0, import_stega2.vercelStegaSplit)(document.title);
468
+ if (title.encoded) document.title = title.cleaned;
464
469
  processing = false;
465
470
  }
466
471
  function collectAffected(mutations) {
@@ -506,15 +511,8 @@ function createStegaAutoClean() {
506
511
  }
507
512
  function start() {
508
513
  if (observer || typeof window === "undefined") return;
509
- const run = () => {
510
- scanAndClean();
511
- setupObserver();
512
- };
513
- if ("requestIdleCallback" in window) {
514
- window.requestIdleCallback(run);
515
- } else {
516
- setTimeout(run, 0);
517
- }
514
+ scanAndClean();
515
+ setupObserver();
518
516
  debug2.log("stega auto-clean started");
519
517
  }
520
518
  function stop() {
@@ -1007,7 +1005,7 @@ function cookieOpts() {
1007
1005
  };
1008
1006
  }
1009
1007
  function createChangeHandler(deps) {
1010
- const { store, stega, syncAutoClean, features, editingEnabled } = deps;
1008
+ const { store, stega, features, editingEnabled } = deps;
1011
1009
  function updateParams(patch) {
1012
1010
  const [path, existing] = splitPath(deps.currentPath());
1013
1011
  const params = new URLSearchParams(existing);
@@ -1070,16 +1068,17 @@ function createChangeHandler(deps) {
1070
1068
  setCookie(COOKIE_PREVIEW_MODE, String(after.previewMode), cookieOpts());
1071
1069
  setCookie(COOKIE_TOOLBAR_OPEN, "false", cookieOpts());
1072
1070
  sendPreprEvent("preview_mode_toggled", { previewMode: after.previewMode });
1073
- syncAutoClean(after.previewMode);
1074
- const [path, existing] = splitPath(deps.currentPath());
1075
- const params = new URLSearchParams(existing);
1076
- if (params.has(PARAM_SEGMENT) || params.has(PARAM_VARIANT)) {
1077
- params.delete(PARAM_SEGMENT);
1078
- params.delete(PARAM_VARIANT);
1079
- const query = params.toString();
1080
- deps.navigate(query ? `${path}?${query}` : path);
1081
- } else {
1082
- deps.reload();
1071
+ if (!after.isIframe) {
1072
+ const [path, existing] = splitPath(deps.currentPath());
1073
+ const params = new URLSearchParams(existing);
1074
+ if (params.has(PARAM_SEGMENT) || params.has(PARAM_VARIANT)) {
1075
+ params.delete(PARAM_SEGMENT);
1076
+ params.delete(PARAM_VARIANT);
1077
+ const query = params.toString();
1078
+ deps.navigate(query ? `${path}?${query}` : path);
1079
+ } else {
1080
+ deps.reload();
1081
+ }
1083
1082
  }
1084
1083
  }
1085
1084
  if (before.toolbarOpen !== after.toolbarOpen) {
@@ -2074,6 +2073,14 @@ function defaultNavigation() {
2074
2073
  currentPath: () => window.location.pathname + window.location.search
2075
2074
  };
2076
2075
  }
2076
+ function safeEditUrl(href) {
2077
+ try {
2078
+ const url = new URL(href, window.location.href);
2079
+ return url.protocol === "https:" || url.protocol === "http:" ? url.href : null;
2080
+ } catch {
2081
+ return null;
2082
+ }
2083
+ }
2077
2084
  function buildSegments(data) {
2078
2085
  return [{ _id: "all_other_users", name: "All other users" }, ...data];
2079
2086
  }
@@ -2137,24 +2144,25 @@ function createPreprPreview(opts = {}) {
2137
2144
  // In the editor, ask the parent to focus the field instead of opening a new
2138
2145
  // tab. Standalone previews keep the window.open behaviour.
2139
2146
  onEdit: ({ href, origin, id, field }) => {
2147
+ const safeHref = href ? safeEditUrl(href) : null;
2148
+ if (href && !safeHref) {
2149
+ debug7.warn("ignored edit request with unsupported href scheme");
2150
+ return;
2151
+ }
2140
2152
  if (isIframe) {
2141
- sendPreprEvent("field_edit_requested", { href, origin, id, field });
2142
- } else if (href) {
2143
- window.open(href);
2153
+ sendPreprEvent("field_edit_requested", {
2154
+ href: safeHref ?? void 0,
2155
+ origin,
2156
+ id,
2157
+ field
2158
+ });
2159
+ } else if (safeHref) {
2160
+ window.open(safeHref, "_blank", "noopener,noreferrer");
2144
2161
  }
2145
2162
  }
2146
2163
  });
2147
2164
  const autoClean = createStegaAutoClean();
2148
- let autoCleanActive = false;
2149
- function syncAutoClean(active) {
2150
- if (active && !autoCleanActive) {
2151
- autoClean.start();
2152
- autoCleanActive = true;
2153
- } else if (!active && autoCleanActive) {
2154
- autoClean.stop();
2155
- autoCleanActive = false;
2156
- }
2157
- }
2165
+ autoClean.start();
2158
2166
  const handleChange = createChangeHandler({
2159
2167
  store,
2160
2168
  features,
@@ -2162,8 +2170,7 @@ function createPreprPreview(opts = {}) {
2162
2170
  navigate: (url) => navigation.navigate(url),
2163
2171
  currentPath: () => navigation.currentPath(),
2164
2172
  reload: navigation.reload ?? (() => window.location.reload()),
2165
- stega,
2166
- syncAutoClean
2173
+ stega
2167
2174
  });
2168
2175
  let prev = store.get();
2169
2176
  const unsubscribe = store.subscribe((state) => {
@@ -2171,7 +2178,6 @@ function createPreprPreview(opts = {}) {
2171
2178
  prev = state;
2172
2179
  handleChange(before, state);
2173
2180
  });
2174
- syncAutoClean(store.get().previewMode);
2175
2181
  const bridge = createIframeBridge(store, {
2176
2182
  allowedEditorOrigins: options?.allowedEditorOrigins
2177
2183
  });
@@ -2184,7 +2190,7 @@ function createPreprPreview(opts = {}) {
2184
2190
  function destroy() {
2185
2191
  unsubscribe();
2186
2192
  stega.stop();
2187
- syncAutoClean(false);
2193
+ autoClean.stop();
2188
2194
  if (isIframe) {
2189
2195
  bridge.stop();
2190
2196
  }
@@ -2358,6 +2364,11 @@ var UTM_PARAMS = [
2358
2364
  "utm_content",
2359
2365
  "utm_campaign"
2360
2366
  ];
2367
+ var MAX_HEADER_VALUE_LENGTH = 2048;
2368
+ function sanitizeHeaderValue(value) {
2369
+ const stripped = value.replace(/[\u0000-\u001f\u007f]/g, "");
2370
+ return stripped.length > MAX_HEADER_VALUE_LENGTH ? stripped.slice(0, MAX_HEADER_VALUE_LENGTH) : stripped;
2371
+ }
2361
2372
  function parseCookies(request) {
2362
2373
  const cookies = /* @__PURE__ */ new Map();
2363
2374
  const header = request.headers.get("cookie");
@@ -2375,6 +2386,15 @@ function parseCookies(request) {
2375
2386
  function processPreprRequest(request, opts) {
2376
2387
  const requestHeaders = new Headers(request.headers);
2377
2388
  const responseCookies = [];
2389
+ const inboundPreprHeaders = [];
2390
+ requestHeaders.forEach((_value, key) => {
2391
+ if (key.toLowerCase().startsWith("prepr-")) {
2392
+ inboundPreprHeaders.push(key);
2393
+ }
2394
+ });
2395
+ for (const key of inboundPreprHeaders) {
2396
+ requestHeaders.delete(key);
2397
+ }
2378
2398
  const requestCookies = parseCookies(request);
2379
2399
  const url = new URL(request.url);
2380
2400
  const searchParams = url.searchParams;
@@ -2384,25 +2404,28 @@ function processPreprRequest(request, opts) {
2384
2404
  for (const key of UTM_PARAMS) {
2385
2405
  const value = searchParams.get(key);
2386
2406
  if (value !== null) {
2387
- requestHeaders.set(`Prepr-Context-${key}`, value);
2407
+ requestHeaders.set(`Prepr-Context-${key}`, sanitizeHeaderValue(value));
2388
2408
  }
2389
2409
  }
2390
2410
  const referer = request.headers.get("referer");
2391
2411
  if (referer) {
2392
- requestHeaders.set("Prepr-Context-initial_referral", referer);
2412
+ requestHeaders.set(
2413
+ "Prepr-Context-initial_referral",
2414
+ sanitizeHeaderValue(referer)
2415
+ );
2393
2416
  }
2394
2417
  const userAgent = request.headers.get("user-agent");
2395
2418
  if (userAgent) {
2396
- requestHeaders.set("Prepr-User-Agent", userAgent);
2419
+ requestHeaders.set("Prepr-User-Agent", sanitizeHeaderValue(userAgent));
2397
2420
  }
2398
2421
  requestHeaders.set("Prepr-Package", `@preprio/toolkit@${opts?.version ?? VERSION}`);
2399
2422
  const ip = request.headers.get("Cf-Connecting-Ip") ?? request.headers.get("x-real-ip");
2400
2423
  if (ip) {
2401
- requestHeaders.set("Prepr-Visitor-IP", ip);
2424
+ requestHeaders.set("Prepr-Visitor-IP", sanitizeHeaderValue(ip));
2402
2425
  }
2403
2426
  const hutkCookie = requestCookies.get(COOKIE_HUBSPOT);
2404
2427
  if (hutkCookie) {
2405
- requestHeaders.set("Prepr-Hubspot-Id", hutkCookie);
2428
+ requestHeaders.set("Prepr-Hubspot-Id", sanitizeHeaderValue(hutkCookie));
2406
2429
  }
2407
2430
  let uid = requestCookies.get(COOKIE_UID);
2408
2431
  if (!uid) {
@@ -2442,16 +2465,16 @@ function processPreprRequest(request, opts) {
2442
2465
  if (!isLivePreview) {
2443
2466
  const segmentCookie = features.segments ? requestCookies.get(COOKIE_SEGMENT) : void 0;
2444
2467
  if (segmentCookie) {
2445
- requestHeaders.set("Prepr-Segments", segmentCookie);
2468
+ requestHeaders.set("Prepr-Segments", sanitizeHeaderValue(segmentCookie));
2446
2469
  }
2447
2470
  const abCookie = features.abTesting ? requestCookies.get(COOKIE_VARIANT) : void 0;
2448
2471
  if (abCookie) {
2449
- requestHeaders.set("Prepr-ABtesting", abCookie);
2472
+ requestHeaders.set("Prepr-ABtesting", sanitizeHeaderValue(abCookie));
2450
2473
  }
2451
2474
  }
2452
2475
  const previewAb = features.abTesting ? searchParams.get(PARAM_VARIANT) : null;
2453
2476
  if (previewAb !== null) {
2454
- requestHeaders.set("Prepr-ABtesting", previewAb);
2477
+ requestHeaders.set("Prepr-ABtesting", sanitizeHeaderValue(previewAb));
2455
2478
  if (!isLivePreview) {
2456
2479
  responseCookies.push({
2457
2480
  name: COOKIE_VARIANT,
@@ -2464,7 +2487,7 @@ function processPreprRequest(request, opts) {
2464
2487
  }
2465
2488
  const previewSegment = features.segments ? searchParams.get(PARAM_SEGMENT) : null;
2466
2489
  if (previewSegment !== null) {
2467
- requestHeaders.set("Prepr-Segments", previewSegment);
2490
+ requestHeaders.set("Prepr-Segments", sanitizeHeaderValue(previewSegment));
2468
2491
  if (!isLivePreview) {
2469
2492
  responseCookies.push({
2470
2493
  name: COOKIE_SEGMENT,
package/dist/index.d.cts CHANGED
@@ -5,7 +5,7 @@ export { g as PreprEnvironment, h as PreprEventType, i as PreprFeatureConfig, j
5
5
  * Package version; must match package.json. Lives in its own leaf module so
6
6
  * core can import it without cycling through the barrel.
7
7
  */
8
- declare const VERSION = "0.2.0-beta.5";
8
+ declare const VERSION = "0.2.0-beta.7";
9
9
 
10
10
  /**
11
11
  * Resolve a `PreprFeatures` bag to plain booleans, defaulting every omitted key
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export { g as PreprEnvironment, h as PreprEventType, i as PreprFeatureConfig, j
5
5
  * Package version; must match package.json. Lives in its own leaf module so
6
6
  * core can import it without cycling through the barrel.
7
7
  */
8
- declare const VERSION = "0.2.0-beta.5";
8
+ declare const VERSION = "0.2.0-beta.7";
9
9
 
10
10
  /**
11
11
  * Resolve a `PreprFeatures` bag to plain booleans, defaulting every omitted key