@preprio/toolkit 0.6.0-alpha.7 → 0.6.0-alpha.9
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/README.md +5 -5
- package/dist/astro/{chunk-3EXK2LHI.js → chunk-UN4JADNU.js} +1 -1
- package/dist/astro/graphql.js +1 -1
- package/dist/astro/index.cjs +1 -1
- package/dist/astro/index.js +1 -1
- package/dist/{chunk-VQIPTUN4.js → chunk-5QIWVDP2.js} +1 -1
- package/dist/{chunk-U7TRFVK7.js → chunk-J66UOCK6.js} +1 -1
- package/dist/{chunk-3QGWCYTL.js → chunk-WQBKQG4R.js} +1 -1
- package/dist/{chunk-PFJO4TYL.js → chunk-XL4Y7FGW.js} +1 -1
- package/dist/graphql/codegen.js +3 -3
- package/dist/graphql.js +3 -3
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/nextjs/{chunk-CZK7SMBD.js → chunk-CMV6ZP6E.js} +1 -1
- package/dist/nextjs/components.cjs +128 -67
- package/dist/nextjs/components.d.cts +14 -13
- package/dist/nextjs/components.d.ts +14 -13
- package/dist/nextjs/components.js +129 -68
- package/dist/nextjs/graphql.cjs +99 -27
- package/dist/nextjs/graphql.d.cts +5 -5
- package/dist/nextjs/graphql.d.ts +5 -5
- package/dist/nextjs/graphql.js +99 -27
- package/dist/nextjs/index.cjs +1 -1
- package/dist/nextjs/index.d.cts +9 -7
- package/dist/nextjs/index.d.ts +9 -7
- package/dist/nextjs/index.js +1 -1
- package/dist/nuxt/{chunk-JL33ENCM.js → chunk-XWOABVMW.js} +1 -1
- package/dist/nuxt/graphql.js +1 -1
- package/dist/nuxt/index.cjs +1 -1
- package/dist/nuxt/index.js +1 -1
- package/dist/react/components.cjs +1 -1
- package/dist/react/components.js +1 -1
- package/dist/sveltekit/{chunk-S7S276V7.js → chunk-3S5RA6G5.js} +1 -1
- package/dist/sveltekit/graphql.js +1 -1
- package/dist/sveltekit/index.cjs +1 -1
- package/dist/sveltekit/index.js +1 -1
- package/package.json +1 -1
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
createPreprPreview,
|
|
4
4
|
loadTrackingPixel
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-J66UOCK6.js";
|
|
6
6
|
import "../chunk-S5RWVMDJ.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-5QIWVDP2.js";
|
|
8
8
|
|
|
9
9
|
// src/nextjs/components.ts
|
|
10
10
|
import { usePathname, useRouter } from "next/navigation";
|
|
@@ -35,10 +35,34 @@ function isReactHydrated(text) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// src/nextjs/decision-switch.ts
|
|
38
|
-
function applyPreprDecisions(decision) {
|
|
38
|
+
function applyPreprDecisions(decision, options = {}) {
|
|
39
39
|
const state = window;
|
|
40
40
|
const key = location.pathname + location.search;
|
|
41
|
-
|
|
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("~");
|
|
64
|
+
}
|
|
65
|
+
if (value !== void 0) state.__preprDecision = { key, value };
|
|
42
66
|
const current = state.__preprDecision;
|
|
43
67
|
if (!current || current.key !== key) return false;
|
|
44
68
|
document.querySelectorAll("[data-prepr-decision]").forEach((marker) => {
|
|
@@ -64,6 +88,70 @@ function applyPreprDecisions(decision) {
|
|
|
64
88
|
});
|
|
65
89
|
return true;
|
|
66
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
|
+
try {
|
|
99
|
+
const all = JSON.parse(sessionStorage.getItem("prepr_decisions") || "{}");
|
|
100
|
+
return all && typeof all === "object" ? all : {};
|
|
101
|
+
} catch {
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const ask = () => {
|
|
106
|
+
if (!asking[key]) {
|
|
107
|
+
asking[key] = fetch(key, {
|
|
108
|
+
headers: { "Prepr-Decide": "1" },
|
|
109
|
+
cache: "no-store",
|
|
110
|
+
credentials: "same-origin",
|
|
111
|
+
// The proxy gives up on Prepr after its own `timeoutMs`; this only
|
|
112
|
+
// covers a proxy that never answers.
|
|
113
|
+
signal: AbortSignal.timeout ? AbortSignal.timeout(6e3) : void 0
|
|
114
|
+
}).then((response) => response.ok ? response.text() : "*").catch(() => "*").then((answer) => {
|
|
115
|
+
delete asking[key];
|
|
116
|
+
try {
|
|
117
|
+
const all = read();
|
|
118
|
+
const at = Date.now();
|
|
119
|
+
Object.keys(all).forEach((stored) => {
|
|
120
|
+
const entry = all[stored];
|
|
121
|
+
if (!(entry && typeof entry.at === "number" && at - entry.at < keepStatic))
|
|
122
|
+
delete all[stored];
|
|
123
|
+
});
|
|
124
|
+
const kept = all[key];
|
|
125
|
+
if (answer !== "*" || !kept || kept.value === "*")
|
|
126
|
+
all[key] = { value: answer, at };
|
|
127
|
+
sessionStorage.setItem("prepr_decisions", JSON.stringify(all));
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
return answer;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return asking[key];
|
|
134
|
+
};
|
|
135
|
+
const last = read()[key];
|
|
136
|
+
const age = last && typeof last.at === "number" ? Date.now() - last.at : -1;
|
|
137
|
+
const value = last && typeof last.value === "string" ? last.value : null;
|
|
138
|
+
if (value !== null && !(value === "" && options.personalized) && age >= 0 && age < (value === "*" ? fresh : value === "" ? keepStatic : keep)) {
|
|
139
|
+
if (value !== "" && age >= fresh) {
|
|
140
|
+
ask().then((answer) => {
|
|
141
|
+
if (answer !== "*" && answer !== value && options.onChange)
|
|
142
|
+
options.onChange(answer);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return { result: value, value: Promise.resolve(value) };
|
|
146
|
+
}
|
|
147
|
+
const pending = {
|
|
148
|
+
value: ask()
|
|
149
|
+
};
|
|
150
|
+
pending.value.then((answer) => {
|
|
151
|
+
pending.result = answer;
|
|
152
|
+
});
|
|
153
|
+
return pending;
|
|
154
|
+
}
|
|
67
155
|
|
|
68
156
|
// src/react/components.ts
|
|
69
157
|
import { useEffect } from "react";
|
|
@@ -100,35 +188,22 @@ function PreprToolbar({
|
|
|
100
188
|
}, []);
|
|
101
189
|
return null;
|
|
102
190
|
}
|
|
103
|
-
var DECIDE_HEADER = "Prepr-Decide";
|
|
104
|
-
var DECIDE_TIMEOUT_MS = 6e3;
|
|
105
|
-
function requestDecision(key) {
|
|
106
|
-
const pending = { key, value: null };
|
|
107
|
-
pending.value = fetch(key, {
|
|
108
|
-
headers: { [DECIDE_HEADER]: "1" },
|
|
109
|
-
cache: "no-store",
|
|
110
|
-
credentials: "same-origin",
|
|
111
|
-
signal: AbortSignal.timeout(DECIDE_TIMEOUT_MS)
|
|
112
|
-
}).then((response) => response.ok ? response.text() : "*").catch(() => "*").then((value) => {
|
|
113
|
-
pending.result = value;
|
|
114
|
-
try {
|
|
115
|
-
sessionStorage.setItem(
|
|
116
|
-
"prepr_decision",
|
|
117
|
-
JSON.stringify({ key, value, at: Date.now() })
|
|
118
|
-
);
|
|
119
|
-
} catch {
|
|
120
|
-
}
|
|
121
|
-
return value;
|
|
122
|
-
});
|
|
123
|
-
return pending;
|
|
124
|
-
}
|
|
125
|
-
var pressed = null;
|
|
126
|
-
var replaying = null;
|
|
127
191
|
var shown = /* @__PURE__ */ new Map();
|
|
128
192
|
var historyStep = false;
|
|
129
193
|
var onPopState = () => {
|
|
130
194
|
historyStep = true;
|
|
131
195
|
};
|
|
196
|
+
function requestDecision(key, personalized = false) {
|
|
197
|
+
return requestPreprDecision(key, {
|
|
198
|
+
personalized,
|
|
199
|
+
onChange: (value) => {
|
|
200
|
+
if (location.pathname + location.search !== key) return;
|
|
201
|
+
applyPreprDecisions(value, { refresh: true });
|
|
202
|
+
const state = window.__preprDecision;
|
|
203
|
+
if (state) shown.set(key, state.value);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
132
207
|
function linkTarget(event) {
|
|
133
208
|
const anchor = event.target?.closest?.("a[href]");
|
|
134
209
|
if (!(anchor instanceof HTMLAnchorElement)) return null;
|
|
@@ -140,55 +215,37 @@ function linkTarget(event) {
|
|
|
140
215
|
if (url.origin !== location.origin || key === location.pathname + location.search) {
|
|
141
216
|
return null;
|
|
142
217
|
}
|
|
143
|
-
return
|
|
218
|
+
return key;
|
|
219
|
+
}
|
|
220
|
+
var HOVER_DELAY_MS = 65;
|
|
221
|
+
var hovering = null;
|
|
222
|
+
function onPointerOver(event) {
|
|
223
|
+
if (event.pointerType !== "mouse") return;
|
|
224
|
+
const key = linkTarget(event);
|
|
225
|
+
if (hovering?.key === key) return;
|
|
226
|
+
if (hovering) clearTimeout(hovering.timer);
|
|
227
|
+
hovering = key ? { key, timer: setTimeout(() => requestDecision(key), HOVER_DELAY_MS) } : null;
|
|
144
228
|
}
|
|
145
229
|
function onPointerDown(event) {
|
|
146
230
|
if (event.button !== 0) return;
|
|
147
|
-
const
|
|
148
|
-
if (
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
pressed = requestDecision(target.key);
|
|
152
|
-
}
|
|
153
|
-
function onClick(event) {
|
|
154
|
-
const target = linkTarget(event);
|
|
155
|
-
if (!target) return;
|
|
156
|
-
if (replaying === target.anchor) {
|
|
157
|
-
replaying = null;
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
const pending = pressed && pressed.key === target.key ? pressed : requestDecision(target.key);
|
|
164
|
-
pressed = null;
|
|
165
|
-
const record = (value) => {
|
|
166
|
-
window.__preprDecision = { key: target.key, value };
|
|
167
|
-
};
|
|
168
|
-
if (pending.result !== void 0) {
|
|
169
|
-
record(pending.result);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
event.preventDefault();
|
|
173
|
-
event.stopImmediatePropagation();
|
|
174
|
-
void pending.value.then((value) => {
|
|
175
|
-
record(value);
|
|
176
|
-
replaying = target.anchor;
|
|
177
|
-
target.anchor.click();
|
|
178
|
-
});
|
|
231
|
+
const key = linkTarget(event);
|
|
232
|
+
if (key) requestDecision(key);
|
|
179
233
|
}
|
|
180
234
|
var listeners = 0;
|
|
181
235
|
function useDecisionRequests() {
|
|
182
236
|
useEffect2(() => {
|
|
183
237
|
if (listeners++ === 0) {
|
|
184
|
-
window.addEventListener("
|
|
185
|
-
window.addEventListener("
|
|
238
|
+
window.addEventListener("pointerover", onPointerOver, { passive: true });
|
|
239
|
+
window.addEventListener("pointerdown", onPointerDown, {
|
|
240
|
+
capture: true,
|
|
241
|
+
passive: true
|
|
242
|
+
});
|
|
186
243
|
window.addEventListener("popstate", onPopState);
|
|
187
244
|
}
|
|
188
245
|
return () => {
|
|
189
246
|
if (--listeners === 0) {
|
|
247
|
+
window.removeEventListener("pointerover", onPointerOver);
|
|
190
248
|
window.removeEventListener("pointerdown", onPointerDown, true);
|
|
191
|
-
window.removeEventListener("click", onClick, true);
|
|
192
249
|
window.removeEventListener("popstate", onPopState);
|
|
193
250
|
}
|
|
194
251
|
};
|
|
@@ -212,12 +269,16 @@ function PreprDecisionApplier() {
|
|
|
212
269
|
remember();
|
|
213
270
|
return;
|
|
214
271
|
}
|
|
272
|
+
const pending = requestDecision(key, true);
|
|
273
|
+
if (pending.result !== void 0) {
|
|
274
|
+
applyPreprDecisions(pending.result);
|
|
275
|
+
remember();
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
215
278
|
let live = true;
|
|
216
|
-
|
|
217
|
-
const value = started?.key === key ? started.value : requestDecision(key).value;
|
|
218
|
-
void value.then((value2) => {
|
|
279
|
+
void pending.value.then((value) => {
|
|
219
280
|
if (live && location.pathname + location.search === key) {
|
|
220
|
-
applyPreprDecisions(
|
|
281
|
+
applyPreprDecisions(value);
|
|
221
282
|
remember();
|
|
222
283
|
}
|
|
223
284
|
});
|
package/dist/nextjs/graphql.cjs
CHANGED
|
@@ -101,7 +101,7 @@ function preprCacheTags(data) {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
// src/version.ts
|
|
104
|
-
var VERSION = "0.6.0-alpha.
|
|
104
|
+
var VERSION = "0.6.0-alpha.9";
|
|
105
105
|
|
|
106
106
|
// src/core/features.ts
|
|
107
107
|
function isEnabled(config) {
|
|
@@ -1451,10 +1451,34 @@ function createPreprProxy(options) {
|
|
|
1451
1451
|
var import_react = require("react");
|
|
1452
1452
|
|
|
1453
1453
|
// src/nextjs/decision-switch.ts
|
|
1454
|
-
function applyPreprDecisions(decision) {
|
|
1454
|
+
function applyPreprDecisions(decision, options = {}) {
|
|
1455
1455
|
const state = window;
|
|
1456
1456
|
const key = location.pathname + location.search;
|
|
1457
|
-
|
|
1457
|
+
const previous = state.__preprDecision;
|
|
1458
|
+
let value = decision;
|
|
1459
|
+
if (value !== void 0 && value !== "*" && options.refresh && previous && previous.key === key && previous.value !== "*") {
|
|
1460
|
+
const pairs = (from) => {
|
|
1461
|
+
const out = {};
|
|
1462
|
+
from.split("~").forEach((pair) => {
|
|
1463
|
+
const colon = pair.indexOf(":");
|
|
1464
|
+
if (colon > 0) out[pair.slice(0, colon)] = pair;
|
|
1465
|
+
});
|
|
1466
|
+
return out;
|
|
1467
|
+
};
|
|
1468
|
+
const before = pairs(previous.value);
|
|
1469
|
+
const after = pairs(value);
|
|
1470
|
+
document.querySelectorAll("[data-prepr-v][data-prepr-show]").forEach((element) => {
|
|
1471
|
+
const range = document.createRange();
|
|
1472
|
+
range.selectNodeContents(element);
|
|
1473
|
+
if (range.getBoundingClientRect().top >= window.innerHeight) return;
|
|
1474
|
+
(element.getAttribute("data-prepr-v") || "").split(" ").forEach((token) => {
|
|
1475
|
+
const group = token.slice(0, token.indexOf(":"));
|
|
1476
|
+
if (before[group]) after[group] = before[group];
|
|
1477
|
+
});
|
|
1478
|
+
});
|
|
1479
|
+
value = Object.keys(after).map((group) => after[group]).join("~");
|
|
1480
|
+
}
|
|
1481
|
+
if (value !== void 0) state.__preprDecision = { key, value };
|
|
1458
1482
|
const current = state.__preprDecision;
|
|
1459
1483
|
if (!current || current.key !== key) return false;
|
|
1460
1484
|
document.querySelectorAll("[data-prepr-decision]").forEach((marker) => {
|
|
@@ -1480,35 +1504,83 @@ function applyPreprDecisions(decision) {
|
|
|
1480
1504
|
});
|
|
1481
1505
|
return true;
|
|
1482
1506
|
}
|
|
1483
|
-
function
|
|
1507
|
+
function requestPreprDecision(key, options = {}) {
|
|
1508
|
+
const fresh = 1e4;
|
|
1509
|
+
const keep = 18e5;
|
|
1510
|
+
const keepStatic = 36e5;
|
|
1484
1511
|
const state = window;
|
|
1485
|
-
const
|
|
1486
|
-
|
|
1487
|
-
let last;
|
|
1488
|
-
try {
|
|
1489
|
-
last = JSON.parse(sessionStorage.getItem("prepr_decision") || "{}");
|
|
1490
|
-
} catch {
|
|
1491
|
-
last = {};
|
|
1492
|
-
}
|
|
1493
|
-
const age = Date.now() - (last.at || 0);
|
|
1494
|
-
const value = last.key === key && typeof last.value === "string" && age >= 0 && age < 1e4 ? Promise.resolve(last.value) : fetch(key, {
|
|
1495
|
-
headers: { "Prepr-Decide": "1" },
|
|
1496
|
-
cache: "no-store",
|
|
1497
|
-
credentials: "same-origin"
|
|
1498
|
-
}).then((response) => response.ok ? response.text() : "*").catch(() => "*").then((answer) => {
|
|
1512
|
+
const asking = state.__preprAsking = state.__preprAsking || {};
|
|
1513
|
+
const read = () => {
|
|
1499
1514
|
try {
|
|
1500
|
-
sessionStorage.
|
|
1501
|
-
|
|
1502
|
-
JSON.stringify({ key, value: answer, at: Date.now() })
|
|
1503
|
-
);
|
|
1515
|
+
const all = JSON.parse(sessionStorage.getItem("prepr_decisions") || "{}");
|
|
1516
|
+
return all && typeof all === "object" ? all : {};
|
|
1504
1517
|
} catch {
|
|
1518
|
+
return {};
|
|
1505
1519
|
}
|
|
1506
|
-
|
|
1520
|
+
};
|
|
1521
|
+
const ask = () => {
|
|
1522
|
+
if (!asking[key]) {
|
|
1523
|
+
asking[key] = fetch(key, {
|
|
1524
|
+
headers: { "Prepr-Decide": "1" },
|
|
1525
|
+
cache: "no-store",
|
|
1526
|
+
credentials: "same-origin",
|
|
1527
|
+
// The proxy gives up on Prepr after its own `timeoutMs`; this only
|
|
1528
|
+
// covers a proxy that never answers.
|
|
1529
|
+
signal: AbortSignal.timeout ? AbortSignal.timeout(6e3) : void 0
|
|
1530
|
+
}).then((response) => response.ok ? response.text() : "*").catch(() => "*").then((answer) => {
|
|
1531
|
+
delete asking[key];
|
|
1532
|
+
try {
|
|
1533
|
+
const all = read();
|
|
1534
|
+
const at = Date.now();
|
|
1535
|
+
Object.keys(all).forEach((stored) => {
|
|
1536
|
+
const entry = all[stored];
|
|
1537
|
+
if (!(entry && typeof entry.at === "number" && at - entry.at < keepStatic))
|
|
1538
|
+
delete all[stored];
|
|
1539
|
+
});
|
|
1540
|
+
const kept = all[key];
|
|
1541
|
+
if (answer !== "*" || !kept || kept.value === "*")
|
|
1542
|
+
all[key] = { value: answer, at };
|
|
1543
|
+
sessionStorage.setItem("prepr_decisions", JSON.stringify(all));
|
|
1544
|
+
} catch {
|
|
1545
|
+
}
|
|
1546
|
+
return answer;
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
return asking[key];
|
|
1550
|
+
};
|
|
1551
|
+
const last = read()[key];
|
|
1552
|
+
const age = last && typeof last.at === "number" ? Date.now() - last.at : -1;
|
|
1553
|
+
const value = last && typeof last.value === "string" ? last.value : null;
|
|
1554
|
+
if (value !== null && !(value === "" && options.personalized) && age >= 0 && age < (value === "*" ? fresh : value === "" ? keepStatic : keep)) {
|
|
1555
|
+
if (value !== "" && age >= fresh) {
|
|
1556
|
+
ask().then((answer) => {
|
|
1557
|
+
if (answer !== "*" && answer !== value && options.onChange)
|
|
1558
|
+
options.onChange(answer);
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1561
|
+
return { result: value, value: Promise.resolve(value) };
|
|
1562
|
+
}
|
|
1563
|
+
const pending = {
|
|
1564
|
+
value: ask()
|
|
1565
|
+
};
|
|
1566
|
+
pending.value.then((answer) => {
|
|
1567
|
+
pending.result = answer;
|
|
1507
1568
|
});
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1569
|
+
return pending;
|
|
1570
|
+
}
|
|
1571
|
+
function startPreprDecision() {
|
|
1572
|
+
const state = window;
|
|
1573
|
+
const key = location.pathname + location.search;
|
|
1574
|
+
const apply = (answer, refresh) => {
|
|
1575
|
+
if (location.pathname + location.search === key)
|
|
1576
|
+
state.__preprApply(answer, { refresh });
|
|
1577
|
+
};
|
|
1578
|
+
const pending = state.__preprRequest(key, {
|
|
1579
|
+
onChange: (answer) => apply(answer, true),
|
|
1580
|
+
personalized: true
|
|
1511
1581
|
});
|
|
1582
|
+
if (pending.result !== void 0) apply(pending.result);
|
|
1583
|
+
else pending.value.then((answer) => apply(answer));
|
|
1512
1584
|
}
|
|
1513
1585
|
|
|
1514
1586
|
// src/nextjs/stack.ts
|
|
@@ -1518,7 +1590,7 @@ var DEFAULT_ATTRIBUTE = "data-prepr-default";
|
|
|
1518
1590
|
var NESTED_ATTRIBUTE = "data-prepr-nested";
|
|
1519
1591
|
var GROUP_BASE_CSS = `[${VARIANT_ATTRIBUTE}]{display:none}[${VARIANT_ATTRIBUTE}][data-prepr-show]{display:contents}[data-prepr-decision]:not([data-prepr-decided])~[${VARIANT_ATTRIBUTE}][${DEFAULT_ATTRIBUTE}]{display:contents;visibility:hidden}`;
|
|
1520
1592
|
var NOSCRIPT_HTML = `<style>[${VARIANT_ATTRIBUTE}][${DEFAULT_ATTRIBUTE}]{display:contents!important;visibility:visible!important}</style>`;
|
|
1521
|
-
var DECISION_SCRIPT = `window.__preprApply=${applyPreprDecisions.toString()};(${startPreprDecision.toString()})();`;
|
|
1593
|
+
var DECISION_SCRIPT = `window.__preprApply=${applyPreprDecisions.toString()};window.__preprRequest=${requestPreprDecision.toString()};(${startPreprDecision.toString()})();`;
|
|
1522
1594
|
var APPLY_SCRIPT = "__preprApply()";
|
|
1523
1595
|
function variantKeysOf(items) {
|
|
1524
1596
|
return items.flatMap((item) => {
|
|
@@ -326,7 +326,7 @@ interface PreprProxyOptions {
|
|
|
326
326
|
*
|
|
327
327
|
* Pages themselves never wait for Prepr: they are the same for every visitor
|
|
328
328
|
* and pass straight through. The browser sends a decision request for each
|
|
329
|
-
* page view, from the page load and when a visitor
|
|
329
|
+
* page view, from the page load and when a visitor points at a link; the proxy
|
|
330
330
|
* answers it with the visitor's variant of every A/B test and personalization
|
|
331
331
|
* group on that page, asked from Prepr in one request, as text and no page.
|
|
332
332
|
* Add your own cookies or headers to the response it returns.
|
|
@@ -398,10 +398,10 @@ interface PreprStackProps<T> {
|
|
|
398
398
|
* stack or nested inside an item, renders every variant, hidden. The page
|
|
399
399
|
* is the same for every visitor and never waits for Prepr: an inline script
|
|
400
400
|
* asks `createPreprProxy` for this page view's decision while the page
|
|
401
|
-
* loads, and shows the visitor's variant when it answers.
|
|
402
|
-
*
|
|
403
|
-
* next page paints. Nothing streams, and the
|
|
404
|
-
* first to be replaced.
|
|
401
|
+
* loads, and shows the visitor's variant when it answers.
|
|
402
|
+
* `PreprPersonalizationSwitch` asks for a link's decision on hover, so it
|
|
403
|
+
* is mostly in before the next page paints. Nothing streams, and the
|
|
404
|
+
* default variant never shows first to be replaced.
|
|
405
405
|
*
|
|
406
406
|
* The page carries the HTML of every variant: an item with nested groups
|
|
407
407
|
* repeats once per combination of their variants.
|
package/dist/nextjs/graphql.d.ts
CHANGED
|
@@ -326,7 +326,7 @@ interface PreprProxyOptions {
|
|
|
326
326
|
*
|
|
327
327
|
* Pages themselves never wait for Prepr: they are the same for every visitor
|
|
328
328
|
* and pass straight through. The browser sends a decision request for each
|
|
329
|
-
* page view, from the page load and when a visitor
|
|
329
|
+
* page view, from the page load and when a visitor points at a link; the proxy
|
|
330
330
|
* answers it with the visitor's variant of every A/B test and personalization
|
|
331
331
|
* group on that page, asked from Prepr in one request, as text and no page.
|
|
332
332
|
* Add your own cookies or headers to the response it returns.
|
|
@@ -398,10 +398,10 @@ interface PreprStackProps<T> {
|
|
|
398
398
|
* stack or nested inside an item, renders every variant, hidden. The page
|
|
399
399
|
* is the same for every visitor and never waits for Prepr: an inline script
|
|
400
400
|
* asks `createPreprProxy` for this page view's decision while the page
|
|
401
|
-
* loads, and shows the visitor's variant when it answers.
|
|
402
|
-
*
|
|
403
|
-
* next page paints. Nothing streams, and the
|
|
404
|
-
* first to be replaced.
|
|
401
|
+
* loads, and shows the visitor's variant when it answers.
|
|
402
|
+
* `PreprPersonalizationSwitch` asks for a link's decision on hover, so it
|
|
403
|
+
* is mostly in before the next page paints. Nothing streams, and the
|
|
404
|
+
* default variant never shows first to be replaced.
|
|
405
405
|
*
|
|
406
406
|
* The page carries the HTML of every variant: an item with nested groups
|
|
407
407
|
* repeats once per combination of their variants.
|
package/dist/nextjs/graphql.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
preprModelTag,
|
|
12
12
|
preprModelTagsFor,
|
|
13
13
|
validatePreprToken
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-CMV6ZP6E.js";
|
|
15
15
|
|
|
16
16
|
// src/nextjs/graphql.ts
|
|
17
17
|
import * as nextCache from "next/cache";
|
|
@@ -1038,10 +1038,34 @@ import {
|
|
|
1038
1038
|
} from "react";
|
|
1039
1039
|
|
|
1040
1040
|
// src/nextjs/decision-switch.ts
|
|
1041
|
-
function applyPreprDecisions(decision) {
|
|
1041
|
+
function applyPreprDecisions(decision, options = {}) {
|
|
1042
1042
|
const state = window;
|
|
1043
1043
|
const key = location.pathname + location.search;
|
|
1044
|
-
|
|
1044
|
+
const previous = state.__preprDecision;
|
|
1045
|
+
let value = decision;
|
|
1046
|
+
if (value !== void 0 && value !== "*" && options.refresh && previous && previous.key === key && previous.value !== "*") {
|
|
1047
|
+
const pairs = (from) => {
|
|
1048
|
+
const out = {};
|
|
1049
|
+
from.split("~").forEach((pair) => {
|
|
1050
|
+
const colon = pair.indexOf(":");
|
|
1051
|
+
if (colon > 0) out[pair.slice(0, colon)] = pair;
|
|
1052
|
+
});
|
|
1053
|
+
return out;
|
|
1054
|
+
};
|
|
1055
|
+
const before = pairs(previous.value);
|
|
1056
|
+
const after = pairs(value);
|
|
1057
|
+
document.querySelectorAll("[data-prepr-v][data-prepr-show]").forEach((element) => {
|
|
1058
|
+
const range = document.createRange();
|
|
1059
|
+
range.selectNodeContents(element);
|
|
1060
|
+
if (range.getBoundingClientRect().top >= window.innerHeight) return;
|
|
1061
|
+
(element.getAttribute("data-prepr-v") || "").split(" ").forEach((token) => {
|
|
1062
|
+
const group = token.slice(0, token.indexOf(":"));
|
|
1063
|
+
if (before[group]) after[group] = before[group];
|
|
1064
|
+
});
|
|
1065
|
+
});
|
|
1066
|
+
value = Object.keys(after).map((group) => after[group]).join("~");
|
|
1067
|
+
}
|
|
1068
|
+
if (value !== void 0) state.__preprDecision = { key, value };
|
|
1045
1069
|
const current = state.__preprDecision;
|
|
1046
1070
|
if (!current || current.key !== key) return false;
|
|
1047
1071
|
document.querySelectorAll("[data-prepr-decision]").forEach((marker) => {
|
|
@@ -1067,35 +1091,83 @@ function applyPreprDecisions(decision) {
|
|
|
1067
1091
|
});
|
|
1068
1092
|
return true;
|
|
1069
1093
|
}
|
|
1070
|
-
function
|
|
1094
|
+
function requestPreprDecision(key, options = {}) {
|
|
1095
|
+
const fresh = 1e4;
|
|
1096
|
+
const keep = 18e5;
|
|
1097
|
+
const keepStatic = 36e5;
|
|
1071
1098
|
const state = window;
|
|
1072
|
-
const
|
|
1073
|
-
|
|
1074
|
-
let last;
|
|
1075
|
-
try {
|
|
1076
|
-
last = JSON.parse(sessionStorage.getItem("prepr_decision") || "{}");
|
|
1077
|
-
} catch {
|
|
1078
|
-
last = {};
|
|
1079
|
-
}
|
|
1080
|
-
const age = Date.now() - (last.at || 0);
|
|
1081
|
-
const value = last.key === key && typeof last.value === "string" && age >= 0 && age < 1e4 ? Promise.resolve(last.value) : fetch(key, {
|
|
1082
|
-
headers: { "Prepr-Decide": "1" },
|
|
1083
|
-
cache: "no-store",
|
|
1084
|
-
credentials: "same-origin"
|
|
1085
|
-
}).then((response) => response.ok ? response.text() : "*").catch(() => "*").then((answer) => {
|
|
1099
|
+
const asking = state.__preprAsking = state.__preprAsking || {};
|
|
1100
|
+
const read = () => {
|
|
1086
1101
|
try {
|
|
1087
|
-
sessionStorage.
|
|
1088
|
-
|
|
1089
|
-
JSON.stringify({ key, value: answer, at: Date.now() })
|
|
1090
|
-
);
|
|
1102
|
+
const all = JSON.parse(sessionStorage.getItem("prepr_decisions") || "{}");
|
|
1103
|
+
return all && typeof all === "object" ? all : {};
|
|
1091
1104
|
} catch {
|
|
1105
|
+
return {};
|
|
1092
1106
|
}
|
|
1093
|
-
|
|
1107
|
+
};
|
|
1108
|
+
const ask = () => {
|
|
1109
|
+
if (!asking[key]) {
|
|
1110
|
+
asking[key] = fetch(key, {
|
|
1111
|
+
headers: { "Prepr-Decide": "1" },
|
|
1112
|
+
cache: "no-store",
|
|
1113
|
+
credentials: "same-origin",
|
|
1114
|
+
// The proxy gives up on Prepr after its own `timeoutMs`; this only
|
|
1115
|
+
// covers a proxy that never answers.
|
|
1116
|
+
signal: AbortSignal.timeout ? AbortSignal.timeout(6e3) : void 0
|
|
1117
|
+
}).then((response) => response.ok ? response.text() : "*").catch(() => "*").then((answer) => {
|
|
1118
|
+
delete asking[key];
|
|
1119
|
+
try {
|
|
1120
|
+
const all = read();
|
|
1121
|
+
const at = Date.now();
|
|
1122
|
+
Object.keys(all).forEach((stored) => {
|
|
1123
|
+
const entry = all[stored];
|
|
1124
|
+
if (!(entry && typeof entry.at === "number" && at - entry.at < keepStatic))
|
|
1125
|
+
delete all[stored];
|
|
1126
|
+
});
|
|
1127
|
+
const kept = all[key];
|
|
1128
|
+
if (answer !== "*" || !kept || kept.value === "*")
|
|
1129
|
+
all[key] = { value: answer, at };
|
|
1130
|
+
sessionStorage.setItem("prepr_decisions", JSON.stringify(all));
|
|
1131
|
+
} catch {
|
|
1132
|
+
}
|
|
1133
|
+
return answer;
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
return asking[key];
|
|
1137
|
+
};
|
|
1138
|
+
const last = read()[key];
|
|
1139
|
+
const age = last && typeof last.at === "number" ? Date.now() - last.at : -1;
|
|
1140
|
+
const value = last && typeof last.value === "string" ? last.value : null;
|
|
1141
|
+
if (value !== null && !(value === "" && options.personalized) && age >= 0 && age < (value === "*" ? fresh : value === "" ? keepStatic : keep)) {
|
|
1142
|
+
if (value !== "" && age >= fresh) {
|
|
1143
|
+
ask().then((answer) => {
|
|
1144
|
+
if (answer !== "*" && answer !== value && options.onChange)
|
|
1145
|
+
options.onChange(answer);
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
return { result: value, value: Promise.resolve(value) };
|
|
1149
|
+
}
|
|
1150
|
+
const pending = {
|
|
1151
|
+
value: ask()
|
|
1152
|
+
};
|
|
1153
|
+
pending.value.then((answer) => {
|
|
1154
|
+
pending.result = answer;
|
|
1094
1155
|
});
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1156
|
+
return pending;
|
|
1157
|
+
}
|
|
1158
|
+
function startPreprDecision() {
|
|
1159
|
+
const state = window;
|
|
1160
|
+
const key = location.pathname + location.search;
|
|
1161
|
+
const apply = (answer, refresh) => {
|
|
1162
|
+
if (location.pathname + location.search === key)
|
|
1163
|
+
state.__preprApply(answer, { refresh });
|
|
1164
|
+
};
|
|
1165
|
+
const pending = state.__preprRequest(key, {
|
|
1166
|
+
onChange: (answer) => apply(answer, true),
|
|
1167
|
+
personalized: true
|
|
1098
1168
|
});
|
|
1169
|
+
if (pending.result !== void 0) apply(pending.result);
|
|
1170
|
+
else pending.value.then((answer) => apply(answer));
|
|
1099
1171
|
}
|
|
1100
1172
|
|
|
1101
1173
|
// src/nextjs/stack.ts
|
|
@@ -1105,7 +1177,7 @@ var DEFAULT_ATTRIBUTE = "data-prepr-default";
|
|
|
1105
1177
|
var NESTED_ATTRIBUTE = "data-prepr-nested";
|
|
1106
1178
|
var GROUP_BASE_CSS = `[${VARIANT_ATTRIBUTE}]{display:none}[${VARIANT_ATTRIBUTE}][data-prepr-show]{display:contents}[data-prepr-decision]:not([data-prepr-decided])~[${VARIANT_ATTRIBUTE}][${DEFAULT_ATTRIBUTE}]{display:contents;visibility:hidden}`;
|
|
1107
1179
|
var NOSCRIPT_HTML = `<style>[${VARIANT_ATTRIBUTE}][${DEFAULT_ATTRIBUTE}]{display:contents!important;visibility:visible!important}</style>`;
|
|
1108
|
-
var DECISION_SCRIPT = `window.__preprApply=${applyPreprDecisions.toString()};(${startPreprDecision.toString()})();`;
|
|
1180
|
+
var DECISION_SCRIPT = `window.__preprApply=${applyPreprDecisions.toString()};window.__preprRequest=${requestPreprDecision.toString()};(${startPreprDecision.toString()})();`;
|
|
1109
1181
|
var APPLY_SCRIPT = "__preprApply()";
|
|
1110
1182
|
function variantKeysOf(items) {
|
|
1111
1183
|
return items.flatMap((item) => {
|
package/dist/nextjs/index.cjs
CHANGED
|
@@ -43,7 +43,7 @@ module.exports = __toCommonJS(nextjs_exports);
|
|
|
43
43
|
var import_server = require("next/server");
|
|
44
44
|
|
|
45
45
|
// src/version.ts
|
|
46
|
-
var VERSION = "0.6.0-alpha.
|
|
46
|
+
var VERSION = "0.6.0-alpha.9";
|
|
47
47
|
|
|
48
48
|
// src/core/constants.ts
|
|
49
49
|
var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
|
package/dist/nextjs/index.d.cts
CHANGED
|
@@ -175,15 +175,17 @@ declare function PreprTrackingPixel({ id, config, }: PreprTrackingPixelProps): n
|
|
|
175
175
|
*/
|
|
176
176
|
declare function PreprToolbar({ options, ...props }: PreprToolbarComponentProps): null;
|
|
177
177
|
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
178
|
+
* Gets each client-side navigation's decision in before the click: when the
|
|
179
|
+
* visitor hovers over a same-site link (or presses it, on touch devices), it
|
|
180
|
+
* asks `createPreprProxy` which variants they get on that page. Clicks are
|
|
181
|
+
* never held: the new page paints with those variants when the answer is in,
|
|
182
|
+
* and keeps its personalized sections hidden until then otherwise. Pages
|
|
183
|
+
* without personalization answer with no groups and are not asked again for
|
|
184
|
+
* an hour.
|
|
183
185
|
*
|
|
184
186
|
* Mount it once in the root layout, so links on pages without a
|
|
185
|
-
* `PreprStack` are covered too.
|
|
186
|
-
*
|
|
187
|
+
* `PreprStack` are covered too. Links to other sites and links that open a
|
|
188
|
+
* new tab or download are left alone.
|
|
187
189
|
*
|
|
188
190
|
* @example
|
|
189
191
|
* <PreprPersonalizationSwitch />
|