@preprio/toolkit 0.6.0-alpha.1 → 0.6.0-alpha.10

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 (55) hide show
  1. package/README.md +163 -61
  2. package/dist/astro/{chunk-O7N6GPAH.js → chunk-CG2QBHNU.js} +1 -1
  3. package/dist/astro/graphql.cjs +58 -10
  4. package/dist/astro/graphql.d.cts +33 -38
  5. package/dist/astro/graphql.d.ts +33 -38
  6. package/dist/astro/graphql.js +59 -11
  7. package/dist/astro/index.cjs +1 -1
  8. package/dist/astro/index.js +1 -1
  9. package/dist/{chunk-VI6QZTBM.js → chunk-LIDUJWWJ.js} +1 -1
  10. package/dist/{chunk-TBKSBXGH.js → chunk-M3RBYJ5J.js} +52 -5
  11. package/dist/{chunk-3JLWZQ37.js → chunk-SF6TN4JR.js} +1 -1
  12. package/dist/{chunk-YWIKKWBR.js → chunk-TN6SNAR2.js} +1 -1
  13. package/dist/graphql/codegen.cjs +51 -4
  14. package/dist/graphql/codegen.js +3 -3
  15. package/dist/graphql.cjs +89 -10
  16. package/dist/graphql.d.cts +69 -39
  17. package/dist/graphql.d.ts +69 -39
  18. package/dist/graphql.js +38 -9
  19. package/dist/index.cjs +1 -1
  20. package/dist/index.d.cts +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +3 -3
  23. package/dist/nextjs/chunk-B4GVZEOL.js +549 -0
  24. package/dist/nextjs/components.cjs +297 -81
  25. package/dist/nextjs/components.d.cts +26 -11
  26. package/dist/nextjs/components.d.ts +26 -11
  27. package/dist/nextjs/components.js +288 -73
  28. package/dist/nextjs/graphql.cjs +856 -266
  29. package/dist/nextjs/graphql.d.cts +184 -116
  30. package/dist/nextjs/graphql.d.ts +184 -116
  31. package/dist/nextjs/graphql.js +543 -266
  32. package/dist/nextjs/index.cjs +116 -54
  33. package/dist/nextjs/index.d.cts +61 -23
  34. package/dist/nextjs/index.d.ts +61 -23
  35. package/dist/nextjs/index.js +97 -299
  36. package/dist/nextjs/{server-CjpELYvq.d.ts → server-BL-sqZvu.d.cts} +1 -1
  37. package/dist/nextjs/{server-CjpELYvq.d.cts → server-BL-sqZvu.d.ts} +1 -1
  38. package/dist/nuxt/{chunk-IQDSBPDN.js → chunk-FMC3DP7N.js} +1 -1
  39. package/dist/nuxt/graphql.cjs +58 -10
  40. package/dist/nuxt/graphql.d.cts +33 -38
  41. package/dist/nuxt/graphql.d.ts +33 -38
  42. package/dist/nuxt/graphql.js +59 -11
  43. package/dist/nuxt/index.cjs +1 -1
  44. package/dist/nuxt/index.js +1 -1
  45. package/dist/react/components.cjs +1 -1
  46. package/dist/react/components.js +1 -1
  47. package/dist/sveltekit/{chunk-OKYZ6HPH.js → chunk-AJPKBAHO.js} +1 -1
  48. package/dist/sveltekit/graphql.cjs +58 -10
  49. package/dist/sveltekit/graphql.d.cts +33 -38
  50. package/dist/sveltekit/graphql.d.ts +33 -38
  51. package/dist/sveltekit/graphql.js +59 -11
  52. package/dist/sveltekit/index.cjs +1 -1
  53. package/dist/sveltekit/index.js +1 -1
  54. package/package.json +1 -1
  55. package/dist/nextjs/chunk-BQ6IGSH6.js +0 -231
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  createPreprPreview,
4
4
  loadTrackingPixel
5
- } from "../chunk-VI6QZTBM.js";
5
+ } from "../chunk-LIDUJWWJ.js";
6
6
  import "../chunk-S5RWVMDJ.js";
7
- import "../chunk-YWIKKWBR.js";
7
+ import "../chunk-TN6SNAR2.js";
8
8
 
9
9
  // src/nextjs/components.ts
10
10
  import { usePathname, useRouter } from "next/navigation";
@@ -34,68 +34,149 @@ function isReactHydrated(text) {
34
34
  return true;
35
35
  }
36
36
 
37
- // src/nextjs/ab-switch.ts
38
- function applyPreprAbBucket() {
39
- const match = document.cookie.match(/(?:^|; )prepr_ab_bucket=([AB])(?:;|$)/);
40
- const bucket = match ? match[1] : "A";
37
+ // src/nextjs/decision-switch.ts
38
+ function applyPreprDecisions(decision, options = {}) {
41
39
  const state = window;
42
- if (state.__preprAbBucket === bucket) return;
43
- state.__preprAbBucket = bucket;
44
- const css = bucket === "A" ? "" : '[data-prepr-ab="A"]{display:none!important}[data-prepr-ab="' + bucket + '"]{display:contents!important}';
45
- try {
46
- if (!state.__preprAbSheet) {
47
- const sheet = new CSSStyleSheet();
48
- document.adoptedStyleSheets = document.adoptedStyleSheets.concat(sheet);
49
- state.__preprAbSheet = sheet;
50
- }
51
- state.__preprAbSheet.replaceSync(css);
52
- } catch {
53
- if (!state.__preprAbSheet) {
54
- state.__preprAbSheet = document.createElement("style");
55
- document.head.appendChild(state.__preprAbSheet);
56
- }
57
- state.__preprAbSheet.textContent = css;
58
- }
59
- }
60
- function applyPreprDecisions() {
61
- const match = document.cookie.match(/(?:^|; )prepr_decisions=([^;]*)/);
62
- let raw;
63
- try {
64
- raw = match ? decodeURIComponent(match[1]) : "";
65
- } catch {
66
- raw = "";
40
+ const key = location.pathname + location.search;
41
+ const previous = state.__preprDecision;
42
+ let value = decision;
43
+ if (value !== void 0 && value !== "*" && options.refresh && previous && previous.key === key && previous.value !== "*") {
44
+ const pairs = (from) => {
45
+ const out = {};
46
+ from.split("~").forEach((pair) => {
47
+ const colon = pair.indexOf(":");
48
+ if (colon > 0) out[pair.slice(0, colon)] = pair;
49
+ });
50
+ return out;
51
+ };
52
+ const before = pairs(previous.value);
53
+ const after = pairs(value);
54
+ document.querySelectorAll("[data-prepr-v][data-prepr-show]").forEach((element) => {
55
+ const range = document.createRange();
56
+ range.selectNodeContents(element);
57
+ if (range.getBoundingClientRect().top >= window.innerHeight) return;
58
+ (element.getAttribute("data-prepr-v") || "").split(" ").forEach((token) => {
59
+ const group = token.slice(0, token.indexOf(":"));
60
+ if (before[group]) after[group] = before[group];
61
+ });
62
+ });
63
+ value = Object.keys(after).map((group) => after[group]).join("~");
67
64
  }
68
- const state = window;
69
- if (state.__preprDecisions === raw) return;
70
- state.__preprDecisions = raw;
71
- const id = /^[\w-]+$/;
72
- let css = "";
73
- raw.split("~").forEach((pair) => {
74
- const at = pair.indexOf(":");
75
- const group = pair.slice(0, at);
76
- if (at < 1 || !id.test(group)) return;
77
- const selector = '[data-prepr-group="' + group + '"]';
78
- css += selector + "{display:none!important}";
79
- pair.slice(at + 1).split("|").forEach((variant) => {
80
- if (id.test(variant)) {
81
- css += selector + '[data-prepr-variant="' + variant + '"]{display:contents!important}';
82
- }
65
+ if (value !== void 0) state.__preprDecision = { key, value };
66
+ const current = state.__preprDecision;
67
+ if (!current || current.key !== key) return false;
68
+ document.querySelectorAll("[data-prepr-decision]").forEach((marker) => {
69
+ marker.setAttribute("data-prepr-decided", "");
70
+ });
71
+ const defaults = current.value === "*";
72
+ const chosen = {};
73
+ current.value.split("~").forEach((pair) => {
74
+ const colon = pair.indexOf(":");
75
+ if (colon > 0)
76
+ chosen[pair.slice(0, colon)] = pair.slice(colon + 1).split("|");
77
+ });
78
+ document.querySelectorAll("[data-prepr-v]").forEach((element) => {
79
+ const nested = element.hasAttribute("data-prepr-nested");
80
+ const show = defaults ? element.hasAttribute("data-prepr-default") : (element.getAttribute("data-prepr-v") || "").split(" ").every((token) => {
81
+ const colon = token.indexOf(":");
82
+ const variant = token.slice(colon + 1);
83
+ const variants = chosen[token.slice(0, colon)];
84
+ return !!variants && (nested ? variants[0] === variant : variants.indexOf(variant) >= 0);
83
85
  });
86
+ if (show) element.setAttribute("data-prepr-show", "");
87
+ else element.removeAttribute("data-prepr-show");
84
88
  });
85
- try {
86
- if (!state.__preprDecisionSheet) {
87
- const sheet = new CSSStyleSheet();
88
- document.adoptedStyleSheets = document.adoptedStyleSheets.concat(sheet);
89
- state.__preprDecisionSheet = sheet;
89
+ return true;
90
+ }
91
+ function requestPreprDecision(key, options = {}) {
92
+ const fresh = 1e4;
93
+ const keep = 18e5;
94
+ const keepStatic = 36e5;
95
+ const state = window;
96
+ const asking = state.__preprAsking = state.__preprAsking || {};
97
+ const read = () => {
98
+ const all = {};
99
+ try {
100
+ const match = document.cookie.match(/(?:^|; )prepr_decisions=([^;]*)/);
101
+ const parsed = match ? JSON.parse(decodeURIComponent(match[1])) : null;
102
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
103
+ Object.keys(parsed).forEach((stored) => {
104
+ const entry = parsed[stored];
105
+ if (stored.charAt(0) === "/" && Array.isArray(entry) && typeof entry[0] === "string" && typeof entry[1] === "number")
106
+ all[stored] = [entry[0], entry[1]];
107
+ });
108
+ } catch {
90
109
  }
91
- state.__preprDecisionSheet.replaceSync(css);
92
- } catch {
93
- if (!state.__preprDecisionSheet) {
94
- state.__preprDecisionSheet = document.createElement("style");
95
- document.head.appendChild(state.__preprDecisionSheet);
110
+ return all;
111
+ };
112
+ const usable = (entry, now) => {
113
+ if (!entry) return false;
114
+ const age = now - entry[1];
115
+ return age >= 0 && age < (entry[0] === "*" ? fresh : entry[0] === "" ? keepStatic : keep);
116
+ };
117
+ const store = (answer) => {
118
+ const all = read();
119
+ const now = Date.now();
120
+ const kept = all[key];
121
+ const entries = Object.keys(all).filter((stored) => stored !== key && usable(all[stored], now)).map((stored) => [stored, all[stored]]);
122
+ entries.push([
123
+ key,
124
+ answer === "*" && kept && usable(kept, now) && kept[0] !== "*" ? kept : [answer, now]
125
+ ]);
126
+ entries.sort((a, b) => b[1][1] - a[1][1]);
127
+ entries.length = Math.min(entries.length, 20);
128
+ const encode = () => {
129
+ const out = {};
130
+ entries.forEach((entry) => {
131
+ out[entry[0]] = entry[1];
132
+ });
133
+ return encodeURIComponent(JSON.stringify(out));
134
+ };
135
+ let value = encode();
136
+ while (entries.length > 1 && value.length > 3e3) {
137
+ entries.pop();
138
+ value = encode();
139
+ }
140
+ document.cookie = "prepr_decisions=" + value + "; path=/; max-age=3600; samesite=lax" + (location.protocol === "https:" ? "; secure" : "");
141
+ };
142
+ const ask = () => {
143
+ if (!asking[key]) {
144
+ asking[key] = fetch(key, {
145
+ headers: { "Prepr-Decide": "1" },
146
+ cache: "no-store",
147
+ credentials: "same-origin",
148
+ // The proxy gives up on Prepr after its own `timeoutMs`; this only
149
+ // covers a proxy that never answers.
150
+ signal: AbortSignal.timeout ? AbortSignal.timeout(6e3) : void 0
151
+ }).then((response) => response.ok ? response.text() : "*").catch(() => "*").then((answer) => {
152
+ delete asking[key];
153
+ try {
154
+ store(answer);
155
+ } catch {
156
+ }
157
+ return answer;
158
+ });
96
159
  }
97
- state.__preprDecisionSheet.textContent = css;
160
+ return asking[key];
161
+ };
162
+ const last = read()[key];
163
+ if (last && !(last[0] === "" && options.personalized) && usable(last, Date.now())) {
164
+ const value = last[0];
165
+ if (value !== "" && Date.now() - last[1] >= fresh) {
166
+ ask().then((answer) => {
167
+ if (answer !== "*" && answer !== value && options.onChange)
168
+ options.onChange(answer);
169
+ });
170
+ }
171
+ return { result: value, value: Promise.resolve(value) };
98
172
  }
173
+ const pending = {
174
+ value: ask()
175
+ };
176
+ pending.value.then((answer) => {
177
+ pending.result = answer;
178
+ });
179
+ return pending;
99
180
  }
100
181
 
101
182
  // src/react/components.ts
@@ -133,44 +214,178 @@ function PreprToolbar({
133
214
  }, []);
134
215
  return null;
135
216
  }
136
- function PreprAbSwitch() {
217
+ var shown = /* @__PURE__ */ new Map();
218
+ var historyStep = false;
219
+ var onPopState = () => {
220
+ historyStep = true;
221
+ };
222
+ function requestDecision(key, personalized = false) {
223
+ return requestPreprDecision(key, {
224
+ personalized,
225
+ onChange: (value) => {
226
+ if (location.pathname + location.search !== key) return;
227
+ applyPreprDecisions(value, { refresh: true });
228
+ const state = window.__preprDecision;
229
+ if (state) shown.set(key, state.value);
230
+ }
231
+ });
232
+ }
233
+ function linkTarget(event) {
234
+ const anchor = event.target?.closest?.("a[href]");
235
+ if (!(anchor instanceof HTMLAnchorElement)) return null;
236
+ if (anchor.target && anchor.target !== "_self" || anchor.hasAttribute("download")) {
237
+ return null;
238
+ }
239
+ const url = new URL(anchor.href, location.href);
240
+ const key = url.pathname + url.search;
241
+ if (url.origin !== location.origin || key === location.pathname + location.search) {
242
+ return null;
243
+ }
244
+ return key;
245
+ }
246
+ var HOVER_DELAY_MS = 65;
247
+ var hovering = null;
248
+ function onPointerOver(event) {
249
+ if (event.pointerType !== "mouse") return;
250
+ const key = linkTarget(event);
251
+ if (hovering?.key === key) return;
252
+ if (hovering) clearTimeout(hovering.timer);
253
+ hovering = key ? { key, timer: setTimeout(() => requestDecision(key), HOVER_DELAY_MS) } : null;
254
+ }
255
+ function onPointerDown(event) {
256
+ if (event.button !== 0) return;
257
+ const key = linkTarget(event);
258
+ if (key) requestDecision(key);
259
+ }
260
+ var replaying = null;
261
+ function onClick(event) {
262
+ const key = linkTarget(event);
263
+ if (!key) return;
264
+ const anchor = event.target.closest("a[href]");
265
+ if (replaying === anchor) {
266
+ replaying = null;
267
+ return;
268
+ }
269
+ if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
270
+ return;
271
+ }
272
+ const pending = requestDecision(key);
273
+ if (pending.result !== void 0) return;
274
+ event.preventDefault();
275
+ event.stopImmediatePropagation();
276
+ void pending.value.then(() => {
277
+ replaying = anchor;
278
+ anchor.click();
279
+ });
280
+ }
281
+ var listeners = 0;
282
+ function useDecisionRequests() {
283
+ useEffect2(() => {
284
+ if (listeners++ === 0) {
285
+ window.addEventListener("pointerover", onPointerOver, { passive: true });
286
+ window.addEventListener("pointerdown", onPointerDown, {
287
+ capture: true,
288
+ passive: true
289
+ });
290
+ window.addEventListener("click", onClick, true);
291
+ window.addEventListener("popstate", onPopState);
292
+ }
293
+ return () => {
294
+ if (--listeners === 0) {
295
+ window.removeEventListener("pointerover", onPointerOver);
296
+ window.removeEventListener("pointerdown", onPointerDown, true);
297
+ window.removeEventListener("click", onClick, true);
298
+ window.removeEventListener("popstate", onPopState);
299
+ }
300
+ };
301
+ }, []);
302
+ }
303
+ function PreprPersonalizationSwitch() {
304
+ useDecisionRequests();
305
+ return null;
306
+ }
307
+ function PreprDecisionApplier() {
308
+ useDecisionRequests();
137
309
  useLayoutEffect(() => {
138
- applyPreprAbBucket();
139
- applyPreprDecisions();
310
+ const key = location.pathname + location.search;
311
+ const remember = () => {
312
+ const state = window.__preprDecision;
313
+ if (state) shown.set(key, state.value);
314
+ };
315
+ const previous = historyStep ? shown.get(key) : void 0;
316
+ historyStep = false;
317
+ if (applyPreprDecisions(previous)) {
318
+ remember();
319
+ return;
320
+ }
321
+ const pending = requestDecision(key, true);
322
+ if (pending.result !== void 0) {
323
+ applyPreprDecisions(pending.result);
324
+ remember();
325
+ return;
326
+ }
327
+ let live = true;
328
+ void pending.value.then((value) => {
329
+ if (live && location.pathname + location.search === key) {
330
+ applyPreprDecisions(value);
331
+ remember();
332
+ }
333
+ });
334
+ return () => {
335
+ live = false;
336
+ };
140
337
  });
141
338
  return null;
142
339
  }
143
340
  var VARIANT_KEY_ATTRIBUTE = "data-prepr-variant-key";
144
341
  function PreprVariantTracking({
145
342
  variantKeys,
146
- ab,
147
343
  children
148
344
  }) {
149
345
  const ref = useRef(null);
150
346
  const keys = variantKeys.join(" ");
151
347
  useLayoutEffect(() => {
152
- const elements = [...ref.current?.children ?? []];
153
- const list = keys.split(" ");
154
- const pairs = elements.length === list.length ? elements.map((element, i) => [element, list[i]]) : elements.slice(0, 1).map((element) => [element, list[0]]);
155
- const tagged = pairs.filter(([element, key]) => {
156
- if (element.hasAttribute(VARIANT_KEY_ATTRIBUTE)) return false;
157
- element.setAttribute(VARIANT_KEY_ATTRIBUTE, key);
158
- return true;
348
+ let tagged = [];
349
+ const wrapper = ref.current?.closest("[data-prepr-v]");
350
+ const shown2 = () => !wrapper || wrapper.hasAttribute("data-prepr-show");
351
+ const untag = () => {
352
+ for (const [element] of tagged)
353
+ element.removeAttribute(VARIANT_KEY_ATTRIBUTE);
354
+ tagged = [];
355
+ };
356
+ const tag = () => {
357
+ if (!shown2()) return untag();
358
+ if (tagged.length > 0) return;
359
+ const elements = [...ref.current?.children ?? []];
360
+ const list = keys.split(" ");
361
+ const pairs = elements.length === list.length ? elements.map((element, i) => [element, list[i]]) : elements.slice(0, 1).map((element) => [element, list[0]]);
362
+ tagged = pairs.filter(([element, key]) => {
363
+ if (element.hasAttribute(VARIANT_KEY_ATTRIBUTE)) return false;
364
+ element.setAttribute(VARIANT_KEY_ATTRIBUTE, key);
365
+ return true;
366
+ });
367
+ };
368
+ const timer = setTimeout(tag);
369
+ const observer = wrapper && new MutationObserver(tag);
370
+ observer?.observe(wrapper, {
371
+ attributes: true,
372
+ attributeFilter: ["data-prepr-show"]
159
373
  });
160
374
  return () => {
161
- for (const [element] of tagged) {
162
- element.removeAttribute(VARIANT_KEY_ATTRIBUTE);
163
- }
375
+ clearTimeout(timer);
376
+ observer?.disconnect();
377
+ untag();
164
378
  };
165
379
  }, [keys]);
166
380
  return createElement(
167
381
  "div",
168
- ab === void 0 ? { ref, style: { display: "contents" } } : { ref, "data-prepr-ab": ab },
382
+ { ref, style: { display: "contents" } },
169
383
  children
170
384
  );
171
385
  }
172
386
  export {
173
- PreprAbSwitch,
387
+ PreprDecisionApplier,
388
+ PreprPersonalizationSwitch,
174
389
  PreprToolbar,
175
390
  PreprTrackingPixel,
176
391
  PreprVariantTracking