@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.
- package/README.md +163 -61
- package/dist/astro/{chunk-O7N6GPAH.js → chunk-CG2QBHNU.js} +1 -1
- package/dist/astro/graphql.cjs +58 -10
- package/dist/astro/graphql.d.cts +33 -38
- package/dist/astro/graphql.d.ts +33 -38
- package/dist/astro/graphql.js +59 -11
- package/dist/astro/index.cjs +1 -1
- package/dist/astro/index.js +1 -1
- package/dist/{chunk-VI6QZTBM.js → chunk-LIDUJWWJ.js} +1 -1
- package/dist/{chunk-TBKSBXGH.js → chunk-M3RBYJ5J.js} +52 -5
- package/dist/{chunk-3JLWZQ37.js → chunk-SF6TN4JR.js} +1 -1
- package/dist/{chunk-YWIKKWBR.js → chunk-TN6SNAR2.js} +1 -1
- package/dist/graphql/codegen.cjs +51 -4
- package/dist/graphql/codegen.js +3 -3
- package/dist/graphql.cjs +89 -10
- package/dist/graphql.d.cts +69 -39
- package/dist/graphql.d.ts +69 -39
- package/dist/graphql.js +38 -9
- 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-B4GVZEOL.js +549 -0
- package/dist/nextjs/components.cjs +297 -81
- package/dist/nextjs/components.d.cts +26 -11
- package/dist/nextjs/components.d.ts +26 -11
- package/dist/nextjs/components.js +288 -73
- package/dist/nextjs/graphql.cjs +856 -266
- package/dist/nextjs/graphql.d.cts +184 -116
- package/dist/nextjs/graphql.d.ts +184 -116
- package/dist/nextjs/graphql.js +543 -266
- package/dist/nextjs/index.cjs +116 -54
- package/dist/nextjs/index.d.cts +61 -23
- package/dist/nextjs/index.d.ts +61 -23
- package/dist/nextjs/index.js +97 -299
- package/dist/nextjs/{server-CjpELYvq.d.ts → server-BL-sqZvu.d.cts} +1 -1
- package/dist/nextjs/{server-CjpELYvq.d.cts → server-BL-sqZvu.d.ts} +1 -1
- package/dist/nuxt/{chunk-IQDSBPDN.js → chunk-FMC3DP7N.js} +1 -1
- package/dist/nuxt/graphql.cjs +58 -10
- package/dist/nuxt/graphql.d.cts +33 -38
- package/dist/nuxt/graphql.d.ts +33 -38
- package/dist/nuxt/graphql.js +59 -11
- 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-OKYZ6HPH.js → chunk-AJPKBAHO.js} +1 -1
- package/dist/sveltekit/graphql.cjs +58 -10
- package/dist/sveltekit/graphql.d.cts +33 -38
- package/dist/sveltekit/graphql.d.ts +33 -38
- package/dist/sveltekit/graphql.js +59 -11
- package/dist/sveltekit/index.cjs +1 -1
- package/dist/sveltekit/index.js +1 -1
- package/package.json +1 -1
- package/dist/nextjs/chunk-BQ6IGSH6.js +0 -231
package/dist/nextjs/graphql.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name2 in all)
|
|
@@ -15,19 +17,30 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/nextjs/graphql.ts
|
|
21
31
|
var graphql_exports = {};
|
|
22
32
|
__export(graphql_exports, {
|
|
23
|
-
|
|
33
|
+
PREPR_ANY_ITEM_TAG: () => PREPR_ANY_ITEM_TAG,
|
|
24
34
|
PREPR_DEFAULT_VARIANT: () => PREPR_DEFAULT_VARIANT,
|
|
25
35
|
PreprError: () => PreprError,
|
|
26
36
|
PreprStack: () => PreprStack,
|
|
27
37
|
createPreprClient: () => createPreprClient2,
|
|
38
|
+
createPreprProxy: () => createPreprProxy,
|
|
28
39
|
decisionsFrom: () => decisionsFrom,
|
|
29
40
|
isPersonalizedResponse: () => isPersonalizedResponse,
|
|
30
41
|
loadPreprSchema: () => loadPreprSchema,
|
|
42
|
+
preprCacheTags: () => preprCacheTags,
|
|
43
|
+
preprModelTag: () => preprModelTag,
|
|
31
44
|
preprVariantProps: () => preprVariantProps,
|
|
32
45
|
resolveVariants: () => resolveVariants,
|
|
33
46
|
splitStack: () => splitStack,
|
|
@@ -35,6 +48,78 @@ __export(graphql_exports, {
|
|
|
35
48
|
toVariantsQuery: () => toVariantsQuery
|
|
36
49
|
});
|
|
37
50
|
module.exports = __toCommonJS(graphql_exports);
|
|
51
|
+
var nextCache = __toESM(require("next/cache"), 1);
|
|
52
|
+
|
|
53
|
+
// src/core/graphql/cache-tags.ts
|
|
54
|
+
var MAX_TAGS = 128;
|
|
55
|
+
var MAX_TAG_LENGTH = 256;
|
|
56
|
+
var PREPR_ANY_ITEM_TAG = "prepr:any";
|
|
57
|
+
var PREPR_SCHEMA_TAG = "prepr:schema";
|
|
58
|
+
var preprItemTag = (id) => `prepr:${id}`;
|
|
59
|
+
var NOT_ALPHANUMERIC = /[^a-z0-9]/g;
|
|
60
|
+
var preprModelTag = (model) => `prepr:model:${model.toLowerCase().replace(NOT_ALPHANUMERIC, "")}`;
|
|
61
|
+
function preprModelTagsFor(document2, data, schema) {
|
|
62
|
+
const operation = document2.definitions.find(
|
|
63
|
+
(definition) => definition.kind === "OperationDefinition" && definition.operation === "query"
|
|
64
|
+
);
|
|
65
|
+
const root = schema.types[schema.queryType];
|
|
66
|
+
if (!operation || !root) return [];
|
|
67
|
+
const models = (type) => {
|
|
68
|
+
const members = schema.types[type]?.possibleTypes ?? [];
|
|
69
|
+
return members.length > 0 ? members : [type];
|
|
70
|
+
};
|
|
71
|
+
const tags = /* @__PURE__ */ new Set();
|
|
72
|
+
for (const selection of operation.selectionSet.selections) {
|
|
73
|
+
if (selection.kind !== "Field") continue;
|
|
74
|
+
const type = root.fields[selection.name.value]?.type;
|
|
75
|
+
const info = type ? schema.types[type] : void 0;
|
|
76
|
+
if (!type || !info) continue;
|
|
77
|
+
const items = info.fields.items?.type;
|
|
78
|
+
const key = (selection.alias ?? selection.name).value;
|
|
79
|
+
const found = typeof data === "object" && data !== null && data[key] != null;
|
|
80
|
+
const affected = items ? models(items) : found ? [] : models(type);
|
|
81
|
+
for (const model of affected) tags.add(preprModelTag(model));
|
|
82
|
+
}
|
|
83
|
+
return [...tags];
|
|
84
|
+
}
|
|
85
|
+
function preprCacheTags(data) {
|
|
86
|
+
const tags = /* @__PURE__ */ new Set();
|
|
87
|
+
const stack = [data];
|
|
88
|
+
while (stack.length > 0) {
|
|
89
|
+
const value = stack.pop();
|
|
90
|
+
if (typeof value !== "object" || value === null) continue;
|
|
91
|
+
const id = value._id;
|
|
92
|
+
if (typeof id === "string" && id !== "") {
|
|
93
|
+
const tag = preprItemTag(id);
|
|
94
|
+
if (tag.length > MAX_TAG_LENGTH) return [PREPR_ANY_ITEM_TAG];
|
|
95
|
+
tags.add(tag);
|
|
96
|
+
if (tags.size > MAX_TAGS) return [PREPR_ANY_ITEM_TAG];
|
|
97
|
+
}
|
|
98
|
+
for (const child of Object.values(value)) stack.push(child);
|
|
99
|
+
}
|
|
100
|
+
return [...tags];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/version.ts
|
|
104
|
+
var VERSION = "0.6.0-alpha.10";
|
|
105
|
+
|
|
106
|
+
// src/core/features.ts
|
|
107
|
+
function isEnabled(config) {
|
|
108
|
+
if (typeof config === "boolean") return config;
|
|
109
|
+
return config?.enabled ?? true;
|
|
110
|
+
}
|
|
111
|
+
function resolveFeatures(features) {
|
|
112
|
+
return {
|
|
113
|
+
segments: isEnabled(features?.segments),
|
|
114
|
+
abTesting: isEnabled(features?.abTesting),
|
|
115
|
+
editMode: isEnabled(features?.editMode)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
var ALL_FEATURES_ENABLED = Object.freeze({
|
|
119
|
+
segments: true,
|
|
120
|
+
abTesting: true,
|
|
121
|
+
editMode: true
|
|
122
|
+
});
|
|
38
123
|
|
|
39
124
|
// src/core/server.ts
|
|
40
125
|
var PreprError = class extends Error {
|
|
@@ -180,7 +265,10 @@ function decisionSelectionSet(set, typeName, path, ctx) {
|
|
|
180
265
|
path,
|
|
181
266
|
ctx
|
|
182
267
|
);
|
|
183
|
-
if (pruned)
|
|
268
|
+
if (!pruned) continue;
|
|
269
|
+
if (fragment.typeCondition.name.value === typeName) {
|
|
270
|
+
selections.push(...pruned.selections);
|
|
271
|
+
} else {
|
|
184
272
|
selections.push(
|
|
185
273
|
inlineFragment(fragment.typeCondition.name.value, [
|
|
186
274
|
...pruned.selections
|
|
@@ -190,13 +278,19 @@ function decisionSelectionSet(set, typeName, path, ctx) {
|
|
|
190
278
|
continue;
|
|
191
279
|
}
|
|
192
280
|
if (selection.kind === "InlineFragment") {
|
|
281
|
+
const condition = selection.typeCondition?.name.value ?? typeName;
|
|
193
282
|
const pruned = decisionSelectionSet(
|
|
194
283
|
selection.selectionSet,
|
|
195
|
-
|
|
284
|
+
condition,
|
|
196
285
|
path,
|
|
197
286
|
ctx
|
|
198
287
|
);
|
|
199
|
-
if (pruned)
|
|
288
|
+
if (!pruned) continue;
|
|
289
|
+
if (condition === typeName && !selection.directives?.length) {
|
|
290
|
+
selections.push(...pruned.selections);
|
|
291
|
+
} else {
|
|
292
|
+
selections.push({ ...selection, selectionSet: pruned });
|
|
293
|
+
}
|
|
200
294
|
continue;
|
|
201
295
|
}
|
|
202
296
|
if (!selection.selectionSet) continue;
|
|
@@ -270,20 +364,58 @@ function toDecisionDocument(document2, schema, groupPaths, options) {
|
|
|
270
364
|
const used = /* @__PURE__ */ new Set();
|
|
271
365
|
collectVariables(pruned, used);
|
|
272
366
|
collectVariables(operation.directives, used);
|
|
367
|
+
const shared = sharedContextFragment(schema);
|
|
273
368
|
return {
|
|
274
369
|
kind: "Document",
|
|
275
370
|
definitions: [
|
|
371
|
+
...shared ? [shared.definition] : [],
|
|
276
372
|
{
|
|
277
373
|
...operation,
|
|
278
374
|
name: operation.name && name(`${operation.name.value}Decision`),
|
|
279
375
|
variableDefinitions: (operation.variableDefinitions ?? []).filter(
|
|
280
376
|
(definition) => used.has(definition.variable.name.value)
|
|
281
377
|
),
|
|
282
|
-
selectionSet: pruned
|
|
378
|
+
selectionSet: shared ? shared.apply(pruned) : pruned
|
|
283
379
|
}
|
|
284
380
|
]
|
|
285
381
|
};
|
|
286
382
|
}
|
|
383
|
+
function sharedContextFragment(schema) {
|
|
384
|
+
const contextTypes = new Set(
|
|
385
|
+
Object.values(schema.types).flatMap(
|
|
386
|
+
(type) => type.hasContext ? [type.fields._context.type] : []
|
|
387
|
+
)
|
|
388
|
+
);
|
|
389
|
+
if (contextTypes.size !== 1) return null;
|
|
390
|
+
const [contextType] = contextTypes;
|
|
391
|
+
const fragmentName = "c";
|
|
392
|
+
const definition = {
|
|
393
|
+
kind: "FragmentDefinition",
|
|
394
|
+
name: name(fragmentName),
|
|
395
|
+
typeCondition: { kind: "NamedType", name: name(contextType) },
|
|
396
|
+
selectionSet: selectionSet(
|
|
397
|
+
["kind", "group_id", "variant_id"].map(
|
|
398
|
+
(field) => ({ kind: "Field", name: name(field) })
|
|
399
|
+
)
|
|
400
|
+
)
|
|
401
|
+
};
|
|
402
|
+
const spread = {
|
|
403
|
+
kind: "FragmentSpread",
|
|
404
|
+
name: name(fragmentName)
|
|
405
|
+
};
|
|
406
|
+
const apply = (set) => selectionSet(
|
|
407
|
+
set.selections.map((selection) => {
|
|
408
|
+
if (selection.kind === "Field" && selection.alias?.value === PREPR_CONTEXT_ALIAS) {
|
|
409
|
+
return { ...selection, selectionSet: selectionSet([spread]) };
|
|
410
|
+
}
|
|
411
|
+
return "selectionSet" in selection && selection.selectionSet ? {
|
|
412
|
+
...selection,
|
|
413
|
+
selectionSet: apply(selection.selectionSet)
|
|
414
|
+
} : selection;
|
|
415
|
+
})
|
|
416
|
+
);
|
|
417
|
+
return { definition, apply };
|
|
418
|
+
}
|
|
287
419
|
|
|
288
420
|
// src/core/variants/probe-document.ts
|
|
289
421
|
function probeSelectionSet(set, fragments) {
|
|
@@ -864,6 +996,7 @@ function createVaryProbe(run, options) {
|
|
|
864
996
|
}
|
|
865
997
|
|
|
866
998
|
// src/core/graphql/client.ts
|
|
999
|
+
var clientSchemas = /* @__PURE__ */ new WeakMap();
|
|
867
1000
|
function isParseableUrl(url) {
|
|
868
1001
|
try {
|
|
869
1002
|
new URL(url);
|
|
@@ -900,7 +1033,7 @@ function createPreprClient(options) {
|
|
|
900
1033
|
));
|
|
901
1034
|
const variantsDocuments = /* @__PURE__ */ new Map();
|
|
902
1035
|
const decisionDocuments = /* @__PURE__ */ new Map();
|
|
903
|
-
|
|
1036
|
+
const client = {
|
|
904
1037
|
async query(document2, variables, queryOptions = {}) {
|
|
905
1038
|
const { variants, ...rest } = queryOptions;
|
|
906
1039
|
if (!variants) return run(document2, variables, rest);
|
|
@@ -912,10 +1045,10 @@ function createPreprClient(options) {
|
|
|
912
1045
|
return run(derived, variables, rest);
|
|
913
1046
|
},
|
|
914
1047
|
async decide(document2, variables, queryOptions = {}) {
|
|
915
|
-
const { from,
|
|
1048
|
+
const { from, ...rest } = queryOptions;
|
|
916
1049
|
const paths = from === void 0 ? void 0 : personalizationPaths(from);
|
|
917
1050
|
if (paths && paths.size === 0) return /* @__PURE__ */ new Map();
|
|
918
|
-
const signature = paths ? [...paths].map(([path, types]) => `${path}:${[...types].sort().join(",")}`).sort().join("|") :
|
|
1051
|
+
const signature = paths ? [...paths].map(([path, types]) => `${path}:${[...types].sort().join(",")}`).sort().join("|") : "*";
|
|
919
1052
|
let byPaths = decisionDocuments.get(document2);
|
|
920
1053
|
if (!byPaths) {
|
|
921
1054
|
byPaths = /* @__PURE__ */ new Map();
|
|
@@ -923,9 +1056,7 @@ function createPreprClient(options) {
|
|
|
923
1056
|
}
|
|
924
1057
|
let derived = byPaths.get(signature);
|
|
925
1058
|
if (derived === void 0) {
|
|
926
|
-
derived = toDecisionDocument(document2, await loadSchema(), paths
|
|
927
|
-
topLevelOnly: !paths && topLevelOnly
|
|
928
|
-
});
|
|
1059
|
+
derived = toDecisionDocument(document2, await loadSchema(), paths);
|
|
929
1060
|
byPaths.set(signature, derived);
|
|
930
1061
|
}
|
|
931
1062
|
if (!derived) return /* @__PURE__ */ new Map();
|
|
@@ -939,6 +1070,8 @@ function createPreprClient(options) {
|
|
|
939
1070
|
(vary) => vary === null || SEGMENTS_VARY.test(vary)
|
|
940
1071
|
)
|
|
941
1072
|
};
|
|
1073
|
+
clientSchemas.set(client, loadSchema);
|
|
1074
|
+
return client;
|
|
942
1075
|
}
|
|
943
1076
|
|
|
944
1077
|
// src/core/graphql/schema-loader.ts
|
|
@@ -966,25 +1099,62 @@ async function loadPreprSchema(url, options = {}) {
|
|
|
966
1099
|
}
|
|
967
1100
|
|
|
968
1101
|
// src/nextjs/decisions.ts
|
|
1102
|
+
var PREPR_DECIDE_HEADER = "Prepr-Decide";
|
|
1103
|
+
var DEFAULT_DECISION = "*";
|
|
969
1104
|
var PREPR_DECISIONS_COOKIE = "prepr_decisions";
|
|
970
|
-
var
|
|
971
|
-
var
|
|
972
|
-
var
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
)
|
|
1105
|
+
var DECISION_FRESH_MS = 1e4;
|
|
1106
|
+
var DECISION_KEEP_MS = 18e5;
|
|
1107
|
+
var DECISION_KEEP_STATIC_MS = 36e5;
|
|
1108
|
+
var STORED_MAX_ENTRIES = 20;
|
|
1109
|
+
var STORED_MAX_LENGTH = 3e3;
|
|
1110
|
+
function readStoredDecisions(raw) {
|
|
1111
|
+
const stored = /* @__PURE__ */ Object.create(null);
|
|
1112
|
+
if (!raw) return stored;
|
|
1113
|
+
try {
|
|
1114
|
+
const parsed = JSON.parse(raw);
|
|
1115
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
1116
|
+
return stored;
|
|
1117
|
+
for (const [key, entry] of Object.entries(parsed)) {
|
|
1118
|
+
if (key.startsWith("/") && Array.isArray(entry) && typeof entry[0] === "string" && typeof entry[1] === "number") {
|
|
1119
|
+
stored[key] = [entry[0], entry[1]];
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
} catch {
|
|
1123
|
+
}
|
|
1124
|
+
return stored;
|
|
1125
|
+
}
|
|
1126
|
+
function usableDecision(stored, key, now) {
|
|
1127
|
+
const entry = stored[key];
|
|
1128
|
+
if (!entry) return void 0;
|
|
1129
|
+
const [value, at] = entry;
|
|
1130
|
+
const age = now - at;
|
|
1131
|
+
const keep = value === DEFAULT_DECISION ? DECISION_FRESH_MS : value === "" ? DECISION_KEEP_STATIC_MS : DECISION_KEEP_MS;
|
|
1132
|
+
return age >= 0 && age < keep ? value : void 0;
|
|
1133
|
+
}
|
|
1134
|
+
function writeStoredDecisions(stored, key, value, now) {
|
|
1135
|
+
const kept = usableDecision(stored, key, now);
|
|
1136
|
+
const entries = Object.entries(stored).filter(
|
|
1137
|
+
([stale]) => stale !== key && usableDecision(stored, stale, now) !== void 0
|
|
1138
|
+
);
|
|
1139
|
+
entries.push([
|
|
1140
|
+
key,
|
|
1141
|
+
value === DEFAULT_DECISION && kept !== void 0 && kept !== DEFAULT_DECISION ? stored[key] : [value, now]
|
|
1142
|
+
]);
|
|
1143
|
+
entries.sort((a, b) => b[1][1] - a[1][1]);
|
|
1144
|
+
entries.length = Math.min(entries.length, STORED_MAX_ENTRIES);
|
|
1145
|
+
let json = JSON.stringify(Object.fromEntries(entries));
|
|
1146
|
+
while (entries.length > 1 && encodeURIComponent(json).length > STORED_MAX_LENGTH) {
|
|
1147
|
+
entries.pop();
|
|
1148
|
+
json = JSON.stringify(Object.fromEntries(entries));
|
|
983
1149
|
}
|
|
984
|
-
return
|
|
1150
|
+
return json;
|
|
985
1151
|
}
|
|
1152
|
+
var DECISIONS_MAX_GROUPS = 40;
|
|
1153
|
+
var SAFE_ID = /^[\w-]+$/;
|
|
986
1154
|
function serializeDecisions(decisions) {
|
|
987
|
-
return [...decisions].
|
|
1155
|
+
return [...decisions].filter(([group]) => SAFE_ID.test(group)).slice(0, DECISIONS_MAX_GROUPS).map(
|
|
1156
|
+
([group, variants]) => `${group}:${variants.filter((v) => SAFE_ID.test(v)).join("|")}`
|
|
1157
|
+
).join("~");
|
|
988
1158
|
}
|
|
989
1159
|
function forwardedHeaders(request, response) {
|
|
990
1160
|
const overridden = response.headers.get("x-middleware-override-headers");
|
|
@@ -996,60 +1166,17 @@ function forwardedHeaders(request, response) {
|
|
|
996
1166
|
}
|
|
997
1167
|
return getPreprHeadersFromHeaders(headers2);
|
|
998
1168
|
}
|
|
999
|
-
|
|
1169
|
+
var preprClientInternals = /* @__PURE__ */ new WeakMap();
|
|
1170
|
+
async function decideView(client, headers2, page, timeoutMs = 1e3) {
|
|
1171
|
+
const init = { signal: AbortSignal.timeout(timeoutMs) };
|
|
1000
1172
|
try {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
request.cookies.get(PREPR_DECISIONS_COOKIE)?.value
|
|
1006
|
-
);
|
|
1007
|
-
const pageKey = `${JSON.stringify(variables ?? null)}`;
|
|
1008
|
-
let byVariables = seenGroups.get(document2);
|
|
1009
|
-
if (!byVariables) {
|
|
1010
|
-
byVariables = /* @__PURE__ */ new Map();
|
|
1011
|
-
seenGroups.set(document2, byVariables);
|
|
1012
|
-
}
|
|
1013
|
-
const known = byVariables.get(pageKey);
|
|
1014
|
-
const isNewVisitor = headers2.get("Prepr-Customer-Id-Created") === "true";
|
|
1015
|
-
const contextChanged = isNewVisitor || headers2.has("Prepr-Segments") || [...request.nextUrl.searchParams.keys()].some(
|
|
1016
|
-
(key) => key.startsWith("utm_")
|
|
1017
|
-
);
|
|
1018
|
-
if (options?.reuse !== false && !contextChanged && known && [...known].every((group) => stored.has(group))) {
|
|
1019
|
-
return;
|
|
1020
|
-
}
|
|
1021
|
-
const decided = await client.decide(document2, variables, {
|
|
1022
|
-
topLevelOnly: true,
|
|
1023
|
-
headers: headers2,
|
|
1024
|
-
init: { signal: AbortSignal.timeout(options?.timeoutMs ?? 1e3) }
|
|
1025
|
-
});
|
|
1026
|
-
if (!known && byVariables.size >= SEEN_MAX_PAGES) {
|
|
1027
|
-
byVariables.delete(byVariables.keys().next().value);
|
|
1028
|
-
}
|
|
1029
|
-
const groups = known ?? /* @__PURE__ */ new Set();
|
|
1030
|
-
for (const group of decided.keys()) groups.add(group);
|
|
1031
|
-
byVariables.set(pageKey, groups);
|
|
1032
|
-
const next = /* @__PURE__ */ new Map();
|
|
1033
|
-
for (const [group, variants] of decided) {
|
|
1034
|
-
if (SAFE_ID.test(group)) {
|
|
1035
|
-
next.set(
|
|
1036
|
-
group,
|
|
1037
|
-
variants.filter((v) => SAFE_ID.test(v))
|
|
1038
|
-
);
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
for (const [group, variants] of stored) {
|
|
1042
|
-
if (!next.has(group)) next.set(group, variants);
|
|
1043
|
-
}
|
|
1044
|
-
if (next.size === 0) return;
|
|
1045
|
-
response.cookies.set(PREPR_DECISIONS_COOKIE, serializeDecisions(next), {
|
|
1046
|
-
path: "/",
|
|
1047
|
-
sameSite: "lax",
|
|
1048
|
-
// Read by the inline switch script before first paint.
|
|
1049
|
-
httpOnly: false
|
|
1050
|
-
});
|
|
1173
|
+
const decided = page.decision ? decisionsFrom(
|
|
1174
|
+
(await client.query(page.decision, page.variables, { headers: headers2, init })).data
|
|
1175
|
+
) : await client.decide(page.document, page.variables, { headers: headers2, init });
|
|
1176
|
+
return serializeDecisions(decided);
|
|
1051
1177
|
} catch (error) {
|
|
1052
1178
|
if (isFrameworkSignal(error)) throw error;
|
|
1179
|
+
return DEFAULT_DECISION;
|
|
1053
1180
|
}
|
|
1054
1181
|
}
|
|
1055
1182
|
|
|
@@ -1063,227 +1190,668 @@ async function getPreprHeaders() {
|
|
|
1063
1190
|
return getPreprHeadersFromHeaders(h);
|
|
1064
1191
|
}
|
|
1065
1192
|
|
|
1066
|
-
// src/nextjs/
|
|
1067
|
-
var
|
|
1193
|
+
// src/nextjs/proxy.ts
|
|
1194
|
+
var import_server9 = require("next/server");
|
|
1068
1195
|
|
|
1069
|
-
// src/nextjs/
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1196
|
+
// src/nextjs/draft.ts
|
|
1197
|
+
var import_headers2 = require("next/headers");
|
|
1198
|
+
var import_navigation = require("next/navigation");
|
|
1199
|
+
var import_server7 = require("next/server");
|
|
1200
|
+
var DRAFT_COOKIE = "__prerender_bypass";
|
|
1201
|
+
var RETURN_PARAM = "prepr_draft";
|
|
1202
|
+
var DEFAULT_ROUTE = "/api/prepr/draft";
|
|
1203
|
+
function preprDraftRedirect(request, options) {
|
|
1204
|
+
if (!options.preview) return null;
|
|
1205
|
+
if (request.method !== "GET" && request.method !== "HEAD") return null;
|
|
1206
|
+
const url = request.nextUrl;
|
|
1207
|
+
if (url.pathname === (options.route ?? DEFAULT_ROUTE)) return null;
|
|
1208
|
+
if (request.cookies.has(DRAFT_COOKIE)) {
|
|
1209
|
+
if (!url.searchParams.has(RETURN_PARAM)) return null;
|
|
1210
|
+
const clean = url.clone();
|
|
1211
|
+
clean.searchParams.delete(RETURN_PARAM);
|
|
1212
|
+
return import_server7.NextResponse.redirect(clean, 307);
|
|
1213
|
+
}
|
|
1214
|
+
if (url.searchParams.has(RETURN_PARAM)) return null;
|
|
1215
|
+
const target = new URL(options.route ?? DEFAULT_ROUTE, request.url);
|
|
1216
|
+
target.searchParams.set("to", url.pathname + url.search);
|
|
1217
|
+
return import_server7.NextResponse.redirect(target, 307);
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
// src/nextjs/middleware.ts
|
|
1221
|
+
var import_server8 = require("next/server");
|
|
1222
|
+
|
|
1223
|
+
// src/core/constants.ts
|
|
1224
|
+
var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
|
|
1225
|
+
var COOKIE_SEGMENT = "Prepr-Segments";
|
|
1226
|
+
var COOKIE_VARIANT = "Prepr-ABtesting";
|
|
1227
|
+
var COOKIE_UID = "__prepr_uid";
|
|
1228
|
+
var COOKIE_HUBSPOT = "hubspotutk";
|
|
1229
|
+
var PARAM_SEGMENT = "prepr_preview_segment";
|
|
1230
|
+
var PARAM_VARIANT = "prepr_preview_ab";
|
|
1231
|
+
var PARAM_HIDE_BAR = "prepr_hide_bar";
|
|
1232
|
+
var ONE_YEAR_SECONDS = 60 * 60 * 24 * 365;
|
|
1233
|
+
|
|
1234
|
+
// src/core/middleware.ts
|
|
1235
|
+
var UTM_PARAMS = [
|
|
1236
|
+
"utm_source",
|
|
1237
|
+
"utm_medium",
|
|
1238
|
+
"utm_term",
|
|
1239
|
+
"utm_content",
|
|
1240
|
+
"utm_campaign"
|
|
1241
|
+
];
|
|
1242
|
+
var MAX_HEADER_VALUE_LENGTH = 2048;
|
|
1243
|
+
function sanitizeHeaderValue(value) {
|
|
1244
|
+
const stripped = value.replace(/[\u0000-\u001f\u007f]/g, "");
|
|
1245
|
+
return stripped.length > MAX_HEADER_VALUE_LENGTH ? stripped.slice(0, MAX_HEADER_VALUE_LENGTH) : stripped;
|
|
1246
|
+
}
|
|
1247
|
+
function parseCookies(request) {
|
|
1248
|
+
const cookies2 = /* @__PURE__ */ new Map();
|
|
1249
|
+
const header = request.headers.get("cookie");
|
|
1250
|
+
if (!header) return cookies2;
|
|
1251
|
+
for (const part of header.split(";")) {
|
|
1252
|
+
const eq = part.indexOf("=");
|
|
1253
|
+
if (eq === -1) continue;
|
|
1254
|
+
const name2 = part.slice(0, eq).trim();
|
|
1255
|
+
const value = part.slice(eq + 1).trim();
|
|
1256
|
+
if (!name2) continue;
|
|
1257
|
+
try {
|
|
1258
|
+
cookies2.set(name2, decodeURIComponent(value));
|
|
1259
|
+
} catch {
|
|
1260
|
+
cookies2.set(name2, value);
|
|
1082
1261
|
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1262
|
+
}
|
|
1263
|
+
return cookies2;
|
|
1264
|
+
}
|
|
1265
|
+
function processFrameworkRequest(request, opts, framework) {
|
|
1266
|
+
const requestHeaders = new Headers(request.headers);
|
|
1267
|
+
const responseCookies = [];
|
|
1268
|
+
const inboundPreprHeaders = [];
|
|
1269
|
+
requestHeaders.forEach((_value, key) => {
|
|
1270
|
+
if (key.toLowerCase().startsWith("prepr-")) {
|
|
1271
|
+
inboundPreprHeaders.push(key);
|
|
1272
|
+
}
|
|
1273
|
+
});
|
|
1274
|
+
for (const key of inboundPreprHeaders) {
|
|
1275
|
+
requestHeaders.delete(key);
|
|
1276
|
+
}
|
|
1277
|
+
const requestCookies = parseCookies(request);
|
|
1278
|
+
const url = new URL(request.url);
|
|
1279
|
+
const searchParams = url.searchParams;
|
|
1280
|
+
const forwardedProto = request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim();
|
|
1281
|
+
const isSecure = forwardedProto ? forwardedProto === "https" : url.protocol === "https:";
|
|
1282
|
+
const crossSite = isSecure ? { sameSite: "None", secure: true } : {};
|
|
1283
|
+
for (const key of UTM_PARAMS) {
|
|
1284
|
+
const value = searchParams.get(key);
|
|
1285
|
+
if (value !== null) {
|
|
1286
|
+
requestHeaders.set(`Prepr-Context-${key}`, sanitizeHeaderValue(value));
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
const referer = request.headers.get("referer");
|
|
1290
|
+
if (referer) {
|
|
1291
|
+
requestHeaders.set(
|
|
1292
|
+
"Prepr-Context-initial_referral",
|
|
1293
|
+
sanitizeHeaderValue(referer)
|
|
1294
|
+
);
|
|
1295
|
+
}
|
|
1296
|
+
const userAgent = request.headers.get("user-agent");
|
|
1297
|
+
if (userAgent) {
|
|
1298
|
+
requestHeaders.set("User-Agent", sanitizeHeaderValue(userAgent));
|
|
1299
|
+
}
|
|
1300
|
+
const packageParts = [`@preprio/toolkit@${opts?.version ?? VERSION}`];
|
|
1301
|
+
if (framework) packageParts.push(`framework=${framework}`);
|
|
1302
|
+
packageParts.push(`preview=${opts?.preview ? "true" : "false"}`);
|
|
1303
|
+
requestHeaders.set("Prepr-Package", packageParts.join("; "));
|
|
1304
|
+
const ip = request.headers.get("Cf-Connecting-Ip") ?? request.headers.get("x-real-ip");
|
|
1305
|
+
if (ip) {
|
|
1306
|
+
requestHeaders.set("Prepr-Visitor-IP", sanitizeHeaderValue(ip));
|
|
1307
|
+
}
|
|
1308
|
+
const hutkCookie = requestCookies.get(COOKIE_HUBSPOT);
|
|
1309
|
+
if (hutkCookie) {
|
|
1310
|
+
requestHeaders.set("Prepr-Hubspot-Id", sanitizeHeaderValue(hutkCookie));
|
|
1311
|
+
}
|
|
1312
|
+
let uid = sanitizeHeaderValue(requestCookies.get(COOKIE_UID) ?? "");
|
|
1313
|
+
if (!uid) {
|
|
1314
|
+
uid = crypto.randomUUID();
|
|
1315
|
+
responseCookies.push({
|
|
1316
|
+
name: COOKIE_UID,
|
|
1317
|
+
value: uid,
|
|
1318
|
+
maxAge: ONE_YEAR_SECONDS,
|
|
1319
|
+
path: "/"
|
|
1320
|
+
});
|
|
1321
|
+
requestHeaders.set("Prepr-Customer-Id-Created", "true");
|
|
1322
|
+
}
|
|
1323
|
+
requestHeaders.set("Prepr-Customer-Id", uid);
|
|
1324
|
+
if (!opts?.preview) {
|
|
1325
|
+
return { requestHeaders, responseCookies };
|
|
1326
|
+
}
|
|
1327
|
+
const features = resolveFeatures(opts?.features);
|
|
1328
|
+
const hasPreviewParams = features.segments && searchParams.has(PARAM_SEGMENT) || features.abTesting && searchParams.has(PARAM_VARIANT);
|
|
1329
|
+
if (!hasPreviewParams) {
|
|
1330
|
+
const previewModeCookie = requestCookies.get(COOKIE_PREVIEW_MODE);
|
|
1331
|
+
const previewModeEnabled = previewModeCookie !== "false";
|
|
1332
|
+
if (!previewModeEnabled) {
|
|
1333
|
+
return { requestHeaders, responseCookies };
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
requestHeaders.set("Prepr-Preview-Bar", "true");
|
|
1337
|
+
const isLivePreview = searchParams.get(PARAM_HIDE_BAR) === "true";
|
|
1338
|
+
if (!isLivePreview && !requestCookies.has(COOKIE_PREVIEW_MODE)) {
|
|
1339
|
+
responseCookies.push({
|
|
1340
|
+
name: COOKIE_PREVIEW_MODE,
|
|
1341
|
+
value: "true",
|
|
1342
|
+
maxAge: ONE_YEAR_SECONDS,
|
|
1343
|
+
path: "/",
|
|
1344
|
+
...crossSite
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
if (!isLivePreview) {
|
|
1348
|
+
const segmentCookie = features.segments ? requestCookies.get(COOKIE_SEGMENT) : void 0;
|
|
1349
|
+
if (segmentCookie) {
|
|
1350
|
+
requestHeaders.set("Prepr-Segments", sanitizeHeaderValue(segmentCookie));
|
|
1351
|
+
}
|
|
1352
|
+
const abCookie = features.abTesting ? requestCookies.get(COOKIE_VARIANT) : void 0;
|
|
1353
|
+
if (abCookie) {
|
|
1354
|
+
requestHeaders.set("Prepr-ABtesting", sanitizeHeaderValue(abCookie));
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
const previewAb = features.abTesting ? searchParams.get(PARAM_VARIANT) : null;
|
|
1358
|
+
if (previewAb !== null) {
|
|
1359
|
+
requestHeaders.set("Prepr-ABtesting", sanitizeHeaderValue(previewAb));
|
|
1360
|
+
if (!isLivePreview) {
|
|
1361
|
+
responseCookies.push({
|
|
1362
|
+
name: COOKIE_VARIANT,
|
|
1363
|
+
value: previewAb,
|
|
1364
|
+
maxAge: ONE_YEAR_SECONDS,
|
|
1365
|
+
path: "/",
|
|
1366
|
+
...crossSite
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
const previewSegment = features.segments ? searchParams.get(PARAM_SEGMENT) : null;
|
|
1371
|
+
if (previewSegment !== null) {
|
|
1372
|
+
requestHeaders.set("Prepr-Segments", sanitizeHeaderValue(previewSegment));
|
|
1373
|
+
if (!isLivePreview) {
|
|
1374
|
+
responseCookies.push({
|
|
1375
|
+
name: COOKIE_SEGMENT,
|
|
1376
|
+
value: previewSegment,
|
|
1377
|
+
maxAge: ONE_YEAR_SECONDS,
|
|
1378
|
+
path: "/",
|
|
1379
|
+
...crossSite
|
|
1380
|
+
});
|
|
1088
1381
|
}
|
|
1089
|
-
state.__preprAbSheet.textContent = css;
|
|
1090
1382
|
}
|
|
1383
|
+
if (requestHeaders.has("Prepr-Segments")) {
|
|
1384
|
+
requestHeaders.delete("Prepr-Customer-Id");
|
|
1385
|
+
requestHeaders.delete("Prepr-Customer-Id-Created");
|
|
1386
|
+
}
|
|
1387
|
+
return { requestHeaders, responseCookies };
|
|
1091
1388
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1389
|
+
|
|
1390
|
+
// src/nextjs/middleware.ts
|
|
1391
|
+
function createPreprMiddleware(request, responseOrOptions, maybeOptions) {
|
|
1392
|
+
let chainedResponse;
|
|
1393
|
+
let options;
|
|
1394
|
+
if (responseOrOptions instanceof import_server8.NextResponse) {
|
|
1395
|
+
chainedResponse = responseOrOptions;
|
|
1396
|
+
options = maybeOptions;
|
|
1397
|
+
} else {
|
|
1398
|
+
options = responseOrOptions;
|
|
1099
1399
|
}
|
|
1100
|
-
const
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
const
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1400
|
+
const { requestHeaders, responseCookies } = processFrameworkRequest(
|
|
1401
|
+
request,
|
|
1402
|
+
options,
|
|
1403
|
+
"nextjs"
|
|
1404
|
+
);
|
|
1405
|
+
const response = chainedResponse ?? import_server8.NextResponse.next({ request: { headers: requestHeaders } });
|
|
1406
|
+
if (chainedResponse) {
|
|
1407
|
+
const existing = response.headers.get("x-middleware-override-headers");
|
|
1408
|
+
const names = new Set(
|
|
1409
|
+
existing ? existing.split(",").map((name2) => name2.trim()).filter(Boolean) : []
|
|
1410
|
+
);
|
|
1411
|
+
requestHeaders.forEach((value, key) => {
|
|
1412
|
+
names.add(key);
|
|
1413
|
+
response.headers.set(`x-middleware-request-${key}`, value);
|
|
1414
|
+
});
|
|
1415
|
+
response.headers.set("x-middleware-override-headers", [...names].join(","));
|
|
1416
|
+
}
|
|
1417
|
+
for (const cookie of responseCookies) {
|
|
1418
|
+
response.cookies.set(cookie.name, cookie.value, {
|
|
1419
|
+
maxAge: cookie.maxAge,
|
|
1420
|
+
path: cookie.path,
|
|
1421
|
+
// Lower-cased: Next's cookie API expects 'none' | 'lax' | 'strict'.
|
|
1422
|
+
...cookie.sameSite ? {
|
|
1423
|
+
sameSite: cookie.sameSite.toLowerCase()
|
|
1424
|
+
} : {},
|
|
1425
|
+
...cookie.secure ? { secure: true } : {}
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
return response;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
// src/nextjs/proxy.ts
|
|
1432
|
+
function isRouterFetch(request) {
|
|
1433
|
+
return request.headers.has("rsc") || request.headers.has("next-url") || request.headers.get("sec-fetch-dest") === "empty";
|
|
1434
|
+
}
|
|
1435
|
+
var STATIC_PAGE_MS = 3e5;
|
|
1436
|
+
var STATIC_PAGES_MAX = 1e3;
|
|
1437
|
+
var staticPages = /* @__PURE__ */ new Map();
|
|
1438
|
+
function isPagePath(pathname) {
|
|
1439
|
+
return !pathname.startsWith("/_next/") && !pathname.startsWith("/api/") && !/\.[^/]*$/.test(pathname);
|
|
1440
|
+
}
|
|
1441
|
+
function createPreprProxy(options) {
|
|
1442
|
+
const internals = preprClientInternals.get(options.client);
|
|
1443
|
+
if (!internals) {
|
|
1444
|
+
throw new PreprError(
|
|
1445
|
+
"createPreprProxy needs a client made by createPreprClient from @preprio/toolkit/nextjs/graphql",
|
|
1446
|
+
"CONTEXT_ERROR",
|
|
1447
|
+
"createPreprProxy"
|
|
1448
|
+
);
|
|
1449
|
+
}
|
|
1450
|
+
const { client } = internals;
|
|
1451
|
+
const preview = options.preview ?? false;
|
|
1452
|
+
const allowed = async (request) => {
|
|
1453
|
+
if (!options.onlyIf) return true;
|
|
1454
|
+
try {
|
|
1455
|
+
return await options.onlyIf(request);
|
|
1456
|
+
} catch {
|
|
1457
|
+
return false;
|
|
1458
|
+
}
|
|
1459
|
+
};
|
|
1460
|
+
const lookup = (target) => {
|
|
1461
|
+
try {
|
|
1462
|
+
return options.page(target) ?? null;
|
|
1463
|
+
} catch {
|
|
1464
|
+
return null;
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
async function answerDecision(request, response, target) {
|
|
1468
|
+
let decision = DEFAULT_DECISION;
|
|
1469
|
+
const site = request.headers.get("sec-fetch-site");
|
|
1470
|
+
if (target && request.method === "GET" && (site === null || site === "same-origin") && await allowed(request)) {
|
|
1471
|
+
decision = await decideView(
|
|
1472
|
+
client,
|
|
1473
|
+
mergePreprHeaders(forwardedHeaders(request, response)),
|
|
1474
|
+
target,
|
|
1475
|
+
options.timeoutMs
|
|
1476
|
+
);
|
|
1477
|
+
}
|
|
1478
|
+
const answer = new import_server9.NextResponse(decision, {
|
|
1479
|
+
headers: {
|
|
1480
|
+
"Cache-Control": "private, no-store",
|
|
1481
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
1482
|
+
}
|
|
1483
|
+
});
|
|
1484
|
+
for (const cookie of response.cookies.getAll()) answer.cookies.set(cookie);
|
|
1485
|
+
return answer;
|
|
1486
|
+
}
|
|
1487
|
+
async function decidePageLoad(request, response, target) {
|
|
1488
|
+
const { basePath, pathname, search } = request.nextUrl;
|
|
1489
|
+
const key = basePath + pathname + search;
|
|
1490
|
+
const now = Date.now();
|
|
1491
|
+
if ((staticPages.get(key) ?? 0) > now) return response;
|
|
1492
|
+
const stored = readStoredDecisions(
|
|
1493
|
+
request.cookies.get(PREPR_DECISIONS_COOKIE)?.value
|
|
1494
|
+
);
|
|
1495
|
+
if (!preview && usableDecision(stored, key, now) !== void 0)
|
|
1496
|
+
return response;
|
|
1497
|
+
if (!await allowed(request)) return response;
|
|
1498
|
+
const decision = await decideView(
|
|
1499
|
+
client,
|
|
1500
|
+
mergePreprHeaders(forwardedHeaders(request, response)),
|
|
1501
|
+
target,
|
|
1502
|
+
options.timeoutMs
|
|
1503
|
+
);
|
|
1504
|
+
if (decision === "") {
|
|
1505
|
+
if (staticPages.size >= STATIC_PAGES_MAX) staticPages.clear();
|
|
1506
|
+
staticPages.set(key, now + STATIC_PAGE_MS);
|
|
1507
|
+
return response;
|
|
1508
|
+
}
|
|
1509
|
+
response.cookies.set(
|
|
1510
|
+
PREPR_DECISIONS_COOKIE,
|
|
1511
|
+
writeStoredDecisions(stored, key, decision, now),
|
|
1512
|
+
{
|
|
1513
|
+
path: "/",
|
|
1514
|
+
maxAge: 3600,
|
|
1515
|
+
sameSite: "lax",
|
|
1516
|
+
secure: request.nextUrl.protocol === "https:"
|
|
1114
1517
|
}
|
|
1518
|
+
);
|
|
1519
|
+
return response;
|
|
1520
|
+
}
|
|
1521
|
+
return async (request) => {
|
|
1522
|
+
const rendersPage = (request.method === "GET" || request.method === "HEAD") && isPagePath(request.nextUrl.pathname);
|
|
1523
|
+
const target = rendersPage ? lookup(request.nextUrl) : null;
|
|
1524
|
+
const decisionRequest = request.headers.has(PREPR_DECIDE_HEADER);
|
|
1525
|
+
if (target && preview && !decisionRequest) {
|
|
1526
|
+
const draft = preprDraftRedirect(request, { preview });
|
|
1527
|
+
if (draft) return draft;
|
|
1528
|
+
}
|
|
1529
|
+
const response = createPreprMiddleware(request, {
|
|
1530
|
+
preview,
|
|
1531
|
+
features: options.features
|
|
1532
|
+
});
|
|
1533
|
+
if (decisionRequest) return answerDecision(request, response, target);
|
|
1534
|
+
if (target && request.method === "GET" && !isRouterFetch(request))
|
|
1535
|
+
return decidePageLoad(request, response, target);
|
|
1536
|
+
return response;
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
// src/nextjs/stack.ts
|
|
1541
|
+
var import_react = require("react");
|
|
1542
|
+
|
|
1543
|
+
// src/nextjs/decision-switch.ts
|
|
1544
|
+
function applyPreprDecisions(decision, options = {}) {
|
|
1545
|
+
const state = window;
|
|
1546
|
+
const key = location.pathname + location.search;
|
|
1547
|
+
const previous = state.__preprDecision;
|
|
1548
|
+
let value = decision;
|
|
1549
|
+
if (value !== void 0 && value !== "*" && options.refresh && previous && previous.key === key && previous.value !== "*") {
|
|
1550
|
+
const pairs = (from) => {
|
|
1551
|
+
const out = {};
|
|
1552
|
+
from.split("~").forEach((pair) => {
|
|
1553
|
+
const colon = pair.indexOf(":");
|
|
1554
|
+
if (colon > 0) out[pair.slice(0, colon)] = pair;
|
|
1555
|
+
});
|
|
1556
|
+
return out;
|
|
1557
|
+
};
|
|
1558
|
+
const before = pairs(previous.value);
|
|
1559
|
+
const after = pairs(value);
|
|
1560
|
+
document.querySelectorAll("[data-prepr-v][data-prepr-show]").forEach((element) => {
|
|
1561
|
+
const range = document.createRange();
|
|
1562
|
+
range.selectNodeContents(element);
|
|
1563
|
+
if (range.getBoundingClientRect().top >= window.innerHeight) return;
|
|
1564
|
+
(element.getAttribute("data-prepr-v") || "").split(" ").forEach((token) => {
|
|
1565
|
+
const group = token.slice(0, token.indexOf(":"));
|
|
1566
|
+
if (before[group]) after[group] = before[group];
|
|
1567
|
+
});
|
|
1115
1568
|
});
|
|
1569
|
+
value = Object.keys(after).map((group) => after[group]).join("~");
|
|
1570
|
+
}
|
|
1571
|
+
if (value !== void 0) state.__preprDecision = { key, value };
|
|
1572
|
+
const current = state.__preprDecision;
|
|
1573
|
+
if (!current || current.key !== key) return false;
|
|
1574
|
+
document.querySelectorAll("[data-prepr-decision]").forEach((marker) => {
|
|
1575
|
+
marker.setAttribute("data-prepr-decided", "");
|
|
1116
1576
|
});
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1577
|
+
const defaults = current.value === "*";
|
|
1578
|
+
const chosen = {};
|
|
1579
|
+
current.value.split("~").forEach((pair) => {
|
|
1580
|
+
const colon = pair.indexOf(":");
|
|
1581
|
+
if (colon > 0)
|
|
1582
|
+
chosen[pair.slice(0, colon)] = pair.slice(colon + 1).split("|");
|
|
1583
|
+
});
|
|
1584
|
+
document.querySelectorAll("[data-prepr-v]").forEach((element) => {
|
|
1585
|
+
const nested = element.hasAttribute("data-prepr-nested");
|
|
1586
|
+
const show = defaults ? element.hasAttribute("data-prepr-default") : (element.getAttribute("data-prepr-v") || "").split(" ").every((token) => {
|
|
1587
|
+
const colon = token.indexOf(":");
|
|
1588
|
+
const variant = token.slice(colon + 1);
|
|
1589
|
+
const variants = chosen[token.slice(0, colon)];
|
|
1590
|
+
return !!variants && (nested ? variants[0] === variant : variants.indexOf(variant) >= 0);
|
|
1591
|
+
});
|
|
1592
|
+
if (show) element.setAttribute("data-prepr-show", "");
|
|
1593
|
+
else element.removeAttribute("data-prepr-show");
|
|
1594
|
+
});
|
|
1595
|
+
return true;
|
|
1596
|
+
}
|
|
1597
|
+
function requestPreprDecision(key, options = {}) {
|
|
1598
|
+
const fresh = 1e4;
|
|
1599
|
+
const keep = 18e5;
|
|
1600
|
+
const keepStatic = 36e5;
|
|
1601
|
+
const state = window;
|
|
1602
|
+
const asking = state.__preprAsking = state.__preprAsking || {};
|
|
1603
|
+
const read = () => {
|
|
1604
|
+
const all = {};
|
|
1605
|
+
try {
|
|
1606
|
+
const match = document.cookie.match(/(?:^|; )prepr_decisions=([^;]*)/);
|
|
1607
|
+
const parsed = match ? JSON.parse(decodeURIComponent(match[1])) : null;
|
|
1608
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
1609
|
+
Object.keys(parsed).forEach((stored) => {
|
|
1610
|
+
const entry = parsed[stored];
|
|
1611
|
+
if (stored.charAt(0) === "/" && Array.isArray(entry) && typeof entry[0] === "string" && typeof entry[1] === "number")
|
|
1612
|
+
all[stored] = [entry[0], entry[1]];
|
|
1613
|
+
});
|
|
1614
|
+
} catch {
|
|
1122
1615
|
}
|
|
1123
|
-
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1616
|
+
return all;
|
|
1617
|
+
};
|
|
1618
|
+
const usable = (entry, now) => {
|
|
1619
|
+
if (!entry) return false;
|
|
1620
|
+
const age = now - entry[1];
|
|
1621
|
+
return age >= 0 && age < (entry[0] === "*" ? fresh : entry[0] === "" ? keepStatic : keep);
|
|
1622
|
+
};
|
|
1623
|
+
const store = (answer) => {
|
|
1624
|
+
const all = read();
|
|
1625
|
+
const now = Date.now();
|
|
1626
|
+
const kept = all[key];
|
|
1627
|
+
const entries = Object.keys(all).filter((stored) => stored !== key && usable(all[stored], now)).map((stored) => [stored, all[stored]]);
|
|
1628
|
+
entries.push([
|
|
1629
|
+
key,
|
|
1630
|
+
answer === "*" && kept && usable(kept, now) && kept[0] !== "*" ? kept : [answer, now]
|
|
1631
|
+
]);
|
|
1632
|
+
entries.sort((a, b) => b[1][1] - a[1][1]);
|
|
1633
|
+
entries.length = Math.min(entries.length, 20);
|
|
1634
|
+
const encode = () => {
|
|
1635
|
+
const out = {};
|
|
1636
|
+
entries.forEach((entry) => {
|
|
1637
|
+
out[entry[0]] = entry[1];
|
|
1638
|
+
});
|
|
1639
|
+
return encodeURIComponent(JSON.stringify(out));
|
|
1640
|
+
};
|
|
1641
|
+
let value = encode();
|
|
1642
|
+
while (entries.length > 1 && value.length > 3e3) {
|
|
1643
|
+
entries.pop();
|
|
1644
|
+
value = encode();
|
|
1645
|
+
}
|
|
1646
|
+
document.cookie = "prepr_decisions=" + value + "; path=/; max-age=3600; samesite=lax" + (location.protocol === "https:" ? "; secure" : "");
|
|
1647
|
+
};
|
|
1648
|
+
const ask = () => {
|
|
1649
|
+
if (!asking[key]) {
|
|
1650
|
+
asking[key] = fetch(key, {
|
|
1651
|
+
headers: { "Prepr-Decide": "1" },
|
|
1652
|
+
cache: "no-store",
|
|
1653
|
+
credentials: "same-origin",
|
|
1654
|
+
// The proxy gives up on Prepr after its own `timeoutMs`; this only
|
|
1655
|
+
// covers a proxy that never answers.
|
|
1656
|
+
signal: AbortSignal.timeout ? AbortSignal.timeout(6e3) : void 0
|
|
1657
|
+
}).then((response) => response.ok ? response.text() : "*").catch(() => "*").then((answer) => {
|
|
1658
|
+
delete asking[key];
|
|
1659
|
+
try {
|
|
1660
|
+
store(answer);
|
|
1661
|
+
} catch {
|
|
1662
|
+
}
|
|
1663
|
+
return answer;
|
|
1664
|
+
});
|
|
1128
1665
|
}
|
|
1129
|
-
|
|
1666
|
+
return asking[key];
|
|
1667
|
+
};
|
|
1668
|
+
const last = read()[key];
|
|
1669
|
+
if (last && !(last[0] === "" && options.personalized) && usable(last, Date.now())) {
|
|
1670
|
+
const value = last[0];
|
|
1671
|
+
if (value !== "" && Date.now() - last[1] >= fresh) {
|
|
1672
|
+
ask().then((answer) => {
|
|
1673
|
+
if (answer !== "*" && answer !== value && options.onChange)
|
|
1674
|
+
options.onChange(answer);
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1677
|
+
return { result: value, value: Promise.resolve(value) };
|
|
1130
1678
|
}
|
|
1679
|
+
const pending = {
|
|
1680
|
+
value: ask()
|
|
1681
|
+
};
|
|
1682
|
+
pending.value.then((answer) => {
|
|
1683
|
+
pending.result = answer;
|
|
1684
|
+
});
|
|
1685
|
+
return pending;
|
|
1686
|
+
}
|
|
1687
|
+
function startPreprDecision() {
|
|
1688
|
+
const state = window;
|
|
1689
|
+
const key = location.pathname + location.search;
|
|
1690
|
+
const apply = (answer, refresh) => {
|
|
1691
|
+
if (location.pathname + location.search === key)
|
|
1692
|
+
state.__preprApply(answer, { refresh });
|
|
1693
|
+
};
|
|
1694
|
+
const pending = state.__preprRequest(key, {
|
|
1695
|
+
onChange: (answer) => apply(answer, true),
|
|
1696
|
+
personalized: true
|
|
1697
|
+
});
|
|
1698
|
+
if (pending.result !== void 0) apply(pending.result);
|
|
1699
|
+
else pending.value.then((answer) => apply(answer));
|
|
1131
1700
|
}
|
|
1132
1701
|
|
|
1133
1702
|
// src/nextjs/stack.ts
|
|
1134
1703
|
var import_components = require("./components.cjs");
|
|
1135
|
-
var
|
|
1136
|
-
var AB_BUCKETS = ["A", "B"];
|
|
1137
|
-
var AB_BASE_CSS = `[${AB_ATTRIBUTE}]{display:contents}[${AB_ATTRIBUTE}]:not([${AB_ATTRIBUTE}="A"]){display:none}`;
|
|
1138
|
-
var AB_SWITCH_SCRIPT = `(${applyPreprAbBucket.toString()})();(${applyPreprDecisions.toString()})()`;
|
|
1139
|
-
var GROUP_ATTRIBUTE = "data-prepr-group";
|
|
1140
|
-
var VARIANT_ATTRIBUTE = "data-prepr-variant";
|
|
1704
|
+
var VARIANT_ATTRIBUTE = "data-prepr-v";
|
|
1141
1705
|
var DEFAULT_ATTRIBUTE = "data-prepr-default";
|
|
1142
|
-
var
|
|
1706
|
+
var NESTED_ATTRIBUTE = "data-prepr-nested";
|
|
1707
|
+
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}`;
|
|
1708
|
+
var NOSCRIPT_HTML = `<style>[${VARIANT_ATTRIBUTE}][${DEFAULT_ATTRIBUTE}]{display:contents!important;visibility:visible!important}</style>`;
|
|
1709
|
+
var DECISION_SCRIPT = `window.__preprApply=${applyPreprDecisions.toString()};window.__preprRequest=${requestPreprDecision.toString()};(${startPreprDecision.toString()})();`;
|
|
1710
|
+
var APPLY_SCRIPT = "__preprApply()";
|
|
1143
1711
|
function variantKeysOf(items) {
|
|
1144
1712
|
return items.flatMap((item) => {
|
|
1145
|
-
const key = preprVariantProps(item)["data-prepr-variant-key"];
|
|
1713
|
+
const key = preprVariantProps(item)["data-prepr-variant-key"] ?? nestedKeyOf(item);
|
|
1146
1714
|
return key ? [key] : [];
|
|
1147
1715
|
});
|
|
1148
1716
|
}
|
|
1149
|
-
function
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1717
|
+
function nestedKeyOf(value) {
|
|
1718
|
+
if (!value || typeof value !== "object") return void 0;
|
|
1719
|
+
for (const child of Object.values(value)) {
|
|
1720
|
+
const items = Array.isArray(child) ? child : [child];
|
|
1721
|
+
for (const item of items) {
|
|
1722
|
+
const key = contextOf(item)?.variant_key ?? (item && nestedKeyOf(item));
|
|
1723
|
+
if (key) return key;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
return void 0;
|
|
1727
|
+
}
|
|
1728
|
+
function renderItems(items, render, options) {
|
|
1729
|
+
const resolved = resolveVariants(items, {
|
|
1730
|
+
decisions: options.decisions,
|
|
1731
|
+
bucket: options.bucket
|
|
1732
|
+
});
|
|
1733
|
+
const content = render(resolved, {
|
|
1734
|
+
index: options.index,
|
|
1735
|
+
defaultVariant: options.defaultVariant
|
|
1736
|
+
});
|
|
1737
|
+
const variantKeys = options.track ? variantKeysOf(resolved) : [];
|
|
1738
|
+
return variantKeys.length > 0 ? (0, import_react.createElement)(import_components.PreprVariantTracking, { variantKeys }, content) : content;
|
|
1739
|
+
}
|
|
1740
|
+
function decidedGroups(items, bucket) {
|
|
1741
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1742
|
+
const walk = (value) => {
|
|
1743
|
+
if (Array.isArray(value)) return value.forEach(walk);
|
|
1744
|
+
if (!value || typeof value !== "object") return;
|
|
1745
|
+
const ctx = contextOf(value);
|
|
1746
|
+
const decided = ctx?.kind === "PERSONALIZATION" || ctx?.kind === "AB_TEST" && !bucket;
|
|
1747
|
+
if (decided && ctx.variant_id) {
|
|
1748
|
+
const group = groups.get(ctx.group_id) ?? {
|
|
1749
|
+
variants: /* @__PURE__ */ new Set(),
|
|
1750
|
+
fallback: ctx.kind === "AB_TEST" ? "A" : PREPR_DEFAULT_VARIANT
|
|
1751
|
+
};
|
|
1752
|
+
group.variants.add(ctx.variant_id);
|
|
1753
|
+
groups.set(ctx.group_id, group);
|
|
1169
1754
|
}
|
|
1170
|
-
|
|
1171
|
-
return content;
|
|
1755
|
+
Object.values(value).forEach(walk);
|
|
1172
1756
|
};
|
|
1173
|
-
|
|
1174
|
-
return
|
|
1175
|
-
}
|
|
1176
|
-
function
|
|
1177
|
-
const
|
|
1178
|
-
const
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
)
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1757
|
+
walk(items);
|
|
1758
|
+
return groups;
|
|
1759
|
+
}
|
|
1760
|
+
function renderVariants(block, groups, render, options) {
|
|
1761
|
+
const top = block.key.startsWith("group:") ? block.key.slice("group:".length) : void 0;
|
|
1762
|
+
const nested = !(top !== void 0 && groups.size === 1);
|
|
1763
|
+
const choices = [...groups].map(([group, { variants, fallback }]) => {
|
|
1764
|
+
const ids = nested ? [fallback, ...variants] : [...variants];
|
|
1765
|
+
return [...new Set(ids)].map((variant) => [group, variant]);
|
|
1766
|
+
});
|
|
1767
|
+
const combinations = choices.reduce(
|
|
1768
|
+
(all, pairs) => all.flatMap((combo) => pairs.map((p) => [...combo, p])),
|
|
1769
|
+
[[]]
|
|
1770
|
+
);
|
|
1771
|
+
return combinations.map((combination) => {
|
|
1772
|
+
const pairs = combination.map(([group, variant]) => `${group}:${variant}`);
|
|
1773
|
+
const isDefault = combination.every(
|
|
1774
|
+
([group, variant]) => variant === groups.get(group).fallback
|
|
1775
|
+
);
|
|
1776
|
+
return (0, import_react.createElement)(
|
|
1188
1777
|
"div",
|
|
1189
1778
|
{
|
|
1190
|
-
key:
|
|
1191
|
-
[
|
|
1192
|
-
[
|
|
1193
|
-
...
|
|
1779
|
+
key: pairs.join(" "),
|
|
1780
|
+
[VARIANT_ATTRIBUTE]: pairs.join(" "),
|
|
1781
|
+
...isDefault ? { [DEFAULT_ATTRIBUTE]: "" } : {},
|
|
1782
|
+
...nested ? { [NESTED_ATTRIBUTE]: "" } : {},
|
|
1783
|
+
// The decision script marks the shown wrapper before hydration.
|
|
1784
|
+
suppressHydrationWarning: true
|
|
1194
1785
|
},
|
|
1195
|
-
|
|
1786
|
+
renderItems(block.items, render, {
|
|
1196
1787
|
bucket: options.bucket,
|
|
1197
|
-
decisions:
|
|
1198
|
-
abTest: false,
|
|
1788
|
+
decisions: new Map(combination.map(([g, v]) => [g, [v]])),
|
|
1199
1789
|
index: block.index,
|
|
1200
|
-
track: options.track
|
|
1790
|
+
track: options.track,
|
|
1791
|
+
defaultVariant: isDefault
|
|
1201
1792
|
})
|
|
1202
|
-
)
|
|
1203
|
-
);
|
|
1204
|
-
}
|
|
1205
|
-
async function PersonalizedBlock({
|
|
1206
|
-
items,
|
|
1207
|
-
decisions,
|
|
1208
|
-
bucket,
|
|
1209
|
-
abTest,
|
|
1210
|
-
index,
|
|
1211
|
-
render,
|
|
1212
|
-
track
|
|
1213
|
-
}) {
|
|
1214
|
-
const decided = await decisions.catch((error) => {
|
|
1215
|
-
if (isFrameworkSignal(error)) throw error;
|
|
1216
|
-
return void 0;
|
|
1217
|
-
});
|
|
1218
|
-
return renderBuckets(items, render, {
|
|
1219
|
-
bucket,
|
|
1220
|
-
decisions: decided,
|
|
1221
|
-
abTest,
|
|
1222
|
-
index,
|
|
1223
|
-
track
|
|
1793
|
+
);
|
|
1224
1794
|
});
|
|
1225
1795
|
}
|
|
1226
1796
|
function PreprStack({
|
|
1227
1797
|
items,
|
|
1228
|
-
decisions,
|
|
1229
1798
|
render,
|
|
1230
1799
|
bucket,
|
|
1231
1800
|
nonce,
|
|
1232
|
-
trackVariants = false
|
|
1233
|
-
aboveTheFold = 0
|
|
1801
|
+
trackVariants = false
|
|
1234
1802
|
}) {
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
const
|
|
1803
|
+
const blocks = splitStack(items).map((block) => ({
|
|
1804
|
+
...block,
|
|
1805
|
+
groups: decidedGroups(block.items, bucket)
|
|
1806
|
+
}));
|
|
1807
|
+
const varies = blocks.some((block) => block.groups.size > 0);
|
|
1240
1808
|
return (0, import_react.createElement)(
|
|
1241
1809
|
import_react.Fragment,
|
|
1242
1810
|
null,
|
|
1243
|
-
|
|
1811
|
+
varies && (0, import_react.createElement)(
|
|
1244
1812
|
import_react.Fragment,
|
|
1245
|
-
{ key: "prepr-
|
|
1813
|
+
{ key: "prepr-switch" },
|
|
1246
1814
|
(0, import_react.createElement)("style", {
|
|
1247
1815
|
nonce,
|
|
1248
|
-
dangerouslySetInnerHTML: {
|
|
1249
|
-
|
|
1250
|
-
|
|
1816
|
+
dangerouslySetInnerHTML: { __html: GROUP_BASE_CSS }
|
|
1817
|
+
}),
|
|
1818
|
+
(0, import_react.createElement)("noscript", {
|
|
1819
|
+
dangerouslySetInnerHTML: { __html: NOSCRIPT_HTML }
|
|
1251
1820
|
}),
|
|
1252
1821
|
(0, import_react.createElement)("script", {
|
|
1253
1822
|
nonce,
|
|
1254
|
-
dangerouslySetInnerHTML: { __html:
|
|
1823
|
+
dangerouslySetInnerHTML: { __html: DECISION_SCRIPT }
|
|
1255
1824
|
}),
|
|
1256
|
-
(0, import_react.createElement)(import_components.
|
|
1825
|
+
(0, import_react.createElement)(import_components.PreprDecisionApplier),
|
|
1826
|
+
// Marked decided by the decision switch; never laid out.
|
|
1827
|
+
(0, import_react.createElement)("template", {
|
|
1828
|
+
"data-prepr-decision": "",
|
|
1829
|
+
suppressHydrationWarning: true
|
|
1830
|
+
})
|
|
1257
1831
|
),
|
|
1258
1832
|
...blocks.map((block) => {
|
|
1259
|
-
if (
|
|
1833
|
+
if (block.groups.size === 0) {
|
|
1260
1834
|
return (0, import_react.createElement)(
|
|
1261
1835
|
import_react.Fragment,
|
|
1262
1836
|
{ key: block.key },
|
|
1263
|
-
|
|
1837
|
+
renderItems(block.items, render, {
|
|
1838
|
+
bucket,
|
|
1839
|
+
index: block.index,
|
|
1840
|
+
track: trackVariants,
|
|
1841
|
+
defaultVariant: true
|
|
1842
|
+
})
|
|
1264
1843
|
);
|
|
1265
1844
|
}
|
|
1266
|
-
const fallback = renderBuckets(block.items, render, {
|
|
1267
|
-
bucket,
|
|
1268
|
-
abTest: block.abTest,
|
|
1269
|
-
index: block.index,
|
|
1270
|
-
track: trackVariants && !block.personalized,
|
|
1271
|
-
placeholder: block.personalized
|
|
1272
|
-
});
|
|
1273
|
-
if (!block.personalized) {
|
|
1274
|
-
return (0, import_react.createElement)(import_react.Fragment, { key: block.key }, fallback);
|
|
1275
|
-
}
|
|
1276
1845
|
return (0, import_react.createElement)(
|
|
1277
|
-
import_react.
|
|
1278
|
-
{ key: block.key
|
|
1279
|
-
(
|
|
1280
|
-
items: block.items,
|
|
1281
|
-
decisions,
|
|
1846
|
+
import_react.Fragment,
|
|
1847
|
+
{ key: block.key },
|
|
1848
|
+
renderVariants(block, block.groups, render, {
|
|
1282
1849
|
bucket,
|
|
1283
|
-
abTest: block.abTest,
|
|
1284
|
-
index: block.index,
|
|
1285
|
-
render,
|
|
1286
1850
|
track: trackVariants
|
|
1851
|
+
}),
|
|
1852
|
+
(0, import_react.createElement)("script", {
|
|
1853
|
+
nonce,
|
|
1854
|
+
dangerouslySetInnerHTML: { __html: APPLY_SCRIPT }
|
|
1287
1855
|
})
|
|
1288
1856
|
);
|
|
1289
1857
|
})
|
|
@@ -1295,33 +1863,59 @@ function createPreprClient2(options) {
|
|
|
1295
1863
|
const { cacheTags = ["prepr"], revalidate, ...coreOptions } = options;
|
|
1296
1864
|
const client = createPreprClient({
|
|
1297
1865
|
// The schema introspection goes through the Data Cache: shared across
|
|
1298
|
-
// server instances, refreshed hourly and by
|
|
1866
|
+
// server instances, refreshed hourly and by a `schema.changed` webhook.
|
|
1299
1867
|
schemaInit: {
|
|
1300
|
-
next: { tags: cacheTags, revalidate: 3600 }
|
|
1868
|
+
next: { tags: [...cacheTags, PREPR_SCHEMA_TAG], revalidate: 3600 }
|
|
1301
1869
|
},
|
|
1302
1870
|
...coreOptions
|
|
1303
1871
|
});
|
|
1304
|
-
const
|
|
1305
|
-
return {
|
|
1872
|
+
const nextClient = {
|
|
1306
1873
|
async query(document2, variables, queryOptions = {}) {
|
|
1307
|
-
const { personalize = false, ...rest } = queryOptions;
|
|
1308
|
-
const init = rest.init;
|
|
1309
|
-
|
|
1874
|
+
const { personalize = false, tagItems = false, ...rest } = queryOptions;
|
|
1875
|
+
const { next, ...init } = rest.init ?? {};
|
|
1876
|
+
if (tagItems && typeof nextCache.cacheTag !== "function") {
|
|
1877
|
+
throw new Error("tagItems requires Next.js 16 or later.");
|
|
1878
|
+
}
|
|
1879
|
+
const options2 = {
|
|
1310
1880
|
...rest,
|
|
1311
|
-
|
|
1881
|
+
// With tagItems, `next` is dropped too: tags or revalidate alongside
|
|
1882
|
+
// `no-store` are a conflict Next.js resolves by ignoring the store.
|
|
1883
|
+
// Otherwise tags are attached unconditionally; the core client drops
|
|
1884
|
+
// them again whenever the request ends up `no-store` (personalized,
|
|
1885
|
+
// per-request headers, or preview).
|
|
1886
|
+
init: tagItems ? { ...init, cache: "no-store" } : {
|
|
1312
1887
|
...init,
|
|
1313
1888
|
next: {
|
|
1314
|
-
tags: cacheTags,
|
|
1315
1889
|
...revalidate === void 0 ? {} : { revalidate },
|
|
1316
|
-
...
|
|
1890
|
+
...next,
|
|
1891
|
+
// Merged, so a per-query tag never drops the client's own,
|
|
1892
|
+
// which the publish webhook relies on.
|
|
1893
|
+
tags: [...cacheTags, ...next?.tags ?? []]
|
|
1317
1894
|
}
|
|
1318
1895
|
}
|
|
1319
1896
|
};
|
|
1320
|
-
if (
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1897
|
+
if (personalize) {
|
|
1898
|
+
options2.headers = mergePreprHeaders(
|
|
1899
|
+
await getPreprHeaders(),
|
|
1900
|
+
rest.headers
|
|
1901
|
+
);
|
|
1902
|
+
}
|
|
1903
|
+
const result = await client.query(document2, variables, options2);
|
|
1904
|
+
if (tagItems) {
|
|
1905
|
+
let modelTags;
|
|
1906
|
+
try {
|
|
1907
|
+
modelTags = preprModelTagsFor(
|
|
1908
|
+
documentOf(document2),
|
|
1909
|
+
result.data,
|
|
1910
|
+
await clientSchemas.get(client)()
|
|
1911
|
+
);
|
|
1912
|
+
} catch {
|
|
1913
|
+
modelTags = [PREPR_ANY_ITEM_TAG];
|
|
1914
|
+
}
|
|
1915
|
+
nextCache.cacheTag(...cacheTags, ...next?.tags ?? [], ...modelTags);
|
|
1916
|
+
nextCache.cacheTag(...preprCacheTags(result.data));
|
|
1917
|
+
}
|
|
1918
|
+
return result;
|
|
1325
1919
|
},
|
|
1326
1920
|
async decide(document2, variables, queryOptions = {}) {
|
|
1327
1921
|
return client.decide(document2, variables, {
|
|
@@ -1332,28 +1926,24 @@ function createPreprClient2(options) {
|
|
|
1332
1926
|
)
|
|
1333
1927
|
});
|
|
1334
1928
|
},
|
|
1335
|
-
hasAbTests: (document2, variables) => client.hasAbTests(document2, variables)
|
|
1336
|
-
resolveDecisions: (request, response, document2, variables, options2) => resolveDecisionsWith(
|
|
1337
|
-
client,
|
|
1338
|
-
seenGroups,
|
|
1339
|
-
request,
|
|
1340
|
-
response,
|
|
1341
|
-
document2,
|
|
1342
|
-
variables,
|
|
1343
|
-
options2
|
|
1344
|
-
)
|
|
1929
|
+
hasAbTests: (document2, variables) => client.hasAbTests(document2, variables)
|
|
1345
1930
|
};
|
|
1931
|
+
preprClientInternals.set(nextClient, { client });
|
|
1932
|
+
return nextClient;
|
|
1346
1933
|
}
|
|
1347
1934
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1348
1935
|
0 && (module.exports = {
|
|
1349
|
-
|
|
1936
|
+
PREPR_ANY_ITEM_TAG,
|
|
1350
1937
|
PREPR_DEFAULT_VARIANT,
|
|
1351
1938
|
PreprError,
|
|
1352
1939
|
PreprStack,
|
|
1353
1940
|
createPreprClient,
|
|
1941
|
+
createPreprProxy,
|
|
1354
1942
|
decisionsFrom,
|
|
1355
1943
|
isPersonalizedResponse,
|
|
1356
1944
|
loadPreprSchema,
|
|
1945
|
+
preprCacheTags,
|
|
1946
|
+
preprModelTag,
|
|
1357
1947
|
preprVariantProps,
|
|
1358
1948
|
resolveVariants,
|
|
1359
1949
|
splitStack,
|