@preprio/toolkit 0.6.0-alpha.6 → 0.6.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -52
- package/dist/astro/{chunk-MWRA3732.js → chunk-3EXK2LHI.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-OQGEL4TV.js → chunk-3QGWCYTL.js} +1 -1
- package/dist/{chunk-W6AVX6FI.js → chunk-PFJO4TYL.js} +52 -5
- package/dist/{chunk-Y7DWVOOY.js → chunk-U7TRFVK7.js} +1 -1
- package/dist/{chunk-NSQOGI5K.js → chunk-VQIPTUN4.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-PIQF5ZI5.js → chunk-CZK7SMBD.js} +58 -53
- package/dist/nextjs/components.cjs +175 -242
- package/dist/nextjs/components.d.cts +25 -21
- package/dist/nextjs/components.d.ts +25 -21
- package/dist/nextjs/components.js +167 -236
- package/dist/nextjs/graphql.cjs +372 -509
- package/dist/nextjs/graphql.d.cts +131 -135
- package/dist/nextjs/graphql.d.ts +131 -135
- package/dist/nextjs/graphql.js +307 -456
- package/dist/nextjs/index.cjs +114 -54
- package/dist/nextjs/index.d.cts +57 -33
- package/dist/nextjs/index.d.ts +57 -33
- package/dist/nextjs/index.js +108 -5
- package/dist/nuxt/{chunk-PF6GVWHV.js → chunk-JL33ENCM.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-5K7HWMIF.js → chunk-S7S276V7.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/graphql.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
+
PREPR_ANY_ITEM_TAG,
|
|
3
|
+
PREPR_SCHEMA_TAG,
|
|
2
4
|
PreprError,
|
|
3
5
|
createPreprMiddleware,
|
|
4
6
|
getPreprHeaders,
|
|
5
7
|
getPreprHeadersFromHeaders,
|
|
6
8
|
isPersonalizedResponse,
|
|
9
|
+
preprCacheTags,
|
|
7
10
|
preprDraftRedirect,
|
|
8
|
-
|
|
11
|
+
preprModelTag,
|
|
12
|
+
preprModelTagsFor,
|
|
9
13
|
validatePreprToken
|
|
10
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-CZK7SMBD.js";
|
|
15
|
+
|
|
16
|
+
// src/nextjs/graphql.ts
|
|
17
|
+
import * as nextCache from "next/cache";
|
|
11
18
|
|
|
12
19
|
// src/core/variants/constants.ts
|
|
13
20
|
var PREPR_CONTEXT_ALIAS = "_preprContext";
|
|
@@ -96,7 +103,10 @@ function decisionSelectionSet(set, typeName, path, ctx) {
|
|
|
96
103
|
path,
|
|
97
104
|
ctx
|
|
98
105
|
);
|
|
99
|
-
if (pruned)
|
|
106
|
+
if (!pruned) continue;
|
|
107
|
+
if (fragment.typeCondition.name.value === typeName) {
|
|
108
|
+
selections.push(...pruned.selections);
|
|
109
|
+
} else {
|
|
100
110
|
selections.push(
|
|
101
111
|
inlineFragment(fragment.typeCondition.name.value, [
|
|
102
112
|
...pruned.selections
|
|
@@ -106,13 +116,19 @@ function decisionSelectionSet(set, typeName, path, ctx) {
|
|
|
106
116
|
continue;
|
|
107
117
|
}
|
|
108
118
|
if (selection.kind === "InlineFragment") {
|
|
119
|
+
const condition = selection.typeCondition?.name.value ?? typeName;
|
|
109
120
|
const pruned = decisionSelectionSet(
|
|
110
121
|
selection.selectionSet,
|
|
111
|
-
|
|
122
|
+
condition,
|
|
112
123
|
path,
|
|
113
124
|
ctx
|
|
114
125
|
);
|
|
115
|
-
if (pruned)
|
|
126
|
+
if (!pruned) continue;
|
|
127
|
+
if (condition === typeName && !selection.directives?.length) {
|
|
128
|
+
selections.push(...pruned.selections);
|
|
129
|
+
} else {
|
|
130
|
+
selections.push({ ...selection, selectionSet: pruned });
|
|
131
|
+
}
|
|
116
132
|
continue;
|
|
117
133
|
}
|
|
118
134
|
if (!selection.selectionSet) continue;
|
|
@@ -186,20 +202,58 @@ function toDecisionDocument(document2, schema, groupPaths, options) {
|
|
|
186
202
|
const used = /* @__PURE__ */ new Set();
|
|
187
203
|
collectVariables(pruned, used);
|
|
188
204
|
collectVariables(operation.directives, used);
|
|
205
|
+
const shared = sharedContextFragment(schema);
|
|
189
206
|
return {
|
|
190
207
|
kind: "Document",
|
|
191
208
|
definitions: [
|
|
209
|
+
...shared ? [shared.definition] : [],
|
|
192
210
|
{
|
|
193
211
|
...operation,
|
|
194
212
|
name: operation.name && name(`${operation.name.value}Decision`),
|
|
195
213
|
variableDefinitions: (operation.variableDefinitions ?? []).filter(
|
|
196
214
|
(definition) => used.has(definition.variable.name.value)
|
|
197
215
|
),
|
|
198
|
-
selectionSet: pruned
|
|
216
|
+
selectionSet: shared ? shared.apply(pruned) : pruned
|
|
199
217
|
}
|
|
200
218
|
]
|
|
201
219
|
};
|
|
202
220
|
}
|
|
221
|
+
function sharedContextFragment(schema) {
|
|
222
|
+
const contextTypes = new Set(
|
|
223
|
+
Object.values(schema.types).flatMap(
|
|
224
|
+
(type) => type.hasContext ? [type.fields._context.type] : []
|
|
225
|
+
)
|
|
226
|
+
);
|
|
227
|
+
if (contextTypes.size !== 1) return null;
|
|
228
|
+
const [contextType] = contextTypes;
|
|
229
|
+
const fragmentName = "c";
|
|
230
|
+
const definition = {
|
|
231
|
+
kind: "FragmentDefinition",
|
|
232
|
+
name: name(fragmentName),
|
|
233
|
+
typeCondition: { kind: "NamedType", name: name(contextType) },
|
|
234
|
+
selectionSet: selectionSet(
|
|
235
|
+
["kind", "group_id", "variant_id"].map(
|
|
236
|
+
(field) => ({ kind: "Field", name: name(field) })
|
|
237
|
+
)
|
|
238
|
+
)
|
|
239
|
+
};
|
|
240
|
+
const spread = {
|
|
241
|
+
kind: "FragmentSpread",
|
|
242
|
+
name: name(fragmentName)
|
|
243
|
+
};
|
|
244
|
+
const apply = (set) => selectionSet(
|
|
245
|
+
set.selections.map((selection) => {
|
|
246
|
+
if (selection.kind === "Field" && selection.alias?.value === PREPR_CONTEXT_ALIAS) {
|
|
247
|
+
return { ...selection, selectionSet: selectionSet([spread]) };
|
|
248
|
+
}
|
|
249
|
+
return "selectionSet" in selection && selection.selectionSet ? {
|
|
250
|
+
...selection,
|
|
251
|
+
selectionSet: apply(selection.selectionSet)
|
|
252
|
+
} : selection;
|
|
253
|
+
})
|
|
254
|
+
);
|
|
255
|
+
return { definition, apply };
|
|
256
|
+
}
|
|
203
257
|
|
|
204
258
|
// src/core/variants/probe-document.ts
|
|
205
259
|
function probeSelectionSet(set, fragments) {
|
|
@@ -773,6 +827,7 @@ function createVaryProbe(run, options) {
|
|
|
773
827
|
}
|
|
774
828
|
|
|
775
829
|
// src/core/graphql/client.ts
|
|
830
|
+
var clientSchemas = /* @__PURE__ */ new WeakMap();
|
|
776
831
|
function isParseableUrl(url) {
|
|
777
832
|
try {
|
|
778
833
|
new URL(url);
|
|
@@ -809,7 +864,7 @@ function createPreprClient(options) {
|
|
|
809
864
|
));
|
|
810
865
|
const variantsDocuments = /* @__PURE__ */ new Map();
|
|
811
866
|
const decisionDocuments = /* @__PURE__ */ new Map();
|
|
812
|
-
|
|
867
|
+
const client = {
|
|
813
868
|
async query(document2, variables, queryOptions = {}) {
|
|
814
869
|
const { variants, ...rest } = queryOptions;
|
|
815
870
|
if (!variants) return run(document2, variables, rest);
|
|
@@ -821,10 +876,10 @@ function createPreprClient(options) {
|
|
|
821
876
|
return run(derived, variables, rest);
|
|
822
877
|
},
|
|
823
878
|
async decide(document2, variables, queryOptions = {}) {
|
|
824
|
-
const { from,
|
|
879
|
+
const { from, ...rest } = queryOptions;
|
|
825
880
|
const paths = from === void 0 ? void 0 : personalizationPaths(from);
|
|
826
881
|
if (paths && paths.size === 0) return /* @__PURE__ */ new Map();
|
|
827
|
-
const signature = paths ? [...paths].map(([path, types]) => `${path}:${[...types].sort().join(",")}`).sort().join("|") :
|
|
882
|
+
const signature = paths ? [...paths].map(([path, types]) => `${path}:${[...types].sort().join(",")}`).sort().join("|") : "*";
|
|
828
883
|
let byPaths = decisionDocuments.get(document2);
|
|
829
884
|
if (!byPaths) {
|
|
830
885
|
byPaths = /* @__PURE__ */ new Map();
|
|
@@ -832,9 +887,7 @@ function createPreprClient(options) {
|
|
|
832
887
|
}
|
|
833
888
|
let derived = byPaths.get(signature);
|
|
834
889
|
if (derived === void 0) {
|
|
835
|
-
derived = toDecisionDocument(document2, await loadSchema(), paths
|
|
836
|
-
topLevelOnly: !paths && topLevelOnly
|
|
837
|
-
});
|
|
890
|
+
derived = toDecisionDocument(document2, await loadSchema(), paths);
|
|
838
891
|
byPaths.set(signature, derived);
|
|
839
892
|
}
|
|
840
893
|
if (!derived) return /* @__PURE__ */ new Map();
|
|
@@ -848,6 +901,8 @@ function createPreprClient(options) {
|
|
|
848
901
|
(vary) => vary === null || SEGMENTS_VARY.test(vary)
|
|
849
902
|
)
|
|
850
903
|
};
|
|
904
|
+
clientSchemas.set(client, loadSchema);
|
|
905
|
+
return client;
|
|
851
906
|
}
|
|
852
907
|
|
|
853
908
|
// src/core/graphql/schema-loader.ts
|
|
@@ -874,69 +929,15 @@ async function loadPreprSchema(url, options = {}) {
|
|
|
874
929
|
return schema;
|
|
875
930
|
}
|
|
876
931
|
|
|
877
|
-
// src/core/refresh.ts
|
|
878
|
-
var PREPR_REFRESH_COOKIE = "prepr_refresh";
|
|
879
|
-
var PREPR_REFRESH_HEADER = "Prepr-Refresh";
|
|
880
|
-
var REFRESH_MAX_PATHS = 5;
|
|
881
|
-
var SAFE_PATH = /^\/(?![/\\])[\w\-./%]{0,200}$/;
|
|
882
|
-
function safePath(path) {
|
|
883
|
-
return SAFE_PATH.test(path) ? path : void 0;
|
|
884
|
-
}
|
|
885
|
-
function isFresh(at, freshFor, now = Date.now()) {
|
|
886
|
-
const age = now - at;
|
|
887
|
-
return at > 0 && age >= 0 && age < freshFor;
|
|
888
|
-
}
|
|
889
|
-
function cleanEntries(entries) {
|
|
890
|
-
const out = [];
|
|
891
|
-
for (const entry of entries) {
|
|
892
|
-
if (out.length === REFRESH_MAX_PATHS) break;
|
|
893
|
-
if (safePath(entry.path) && !out.some((e) => e.path === entry.path)) {
|
|
894
|
-
out.push(entry);
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
return out;
|
|
898
|
-
}
|
|
899
|
-
function parseRefresh(raw) {
|
|
900
|
-
return cleanEntries(
|
|
901
|
-
(raw ?? "").split("|").map((pair) => {
|
|
902
|
-
const split = pair.lastIndexOf("~");
|
|
903
|
-
const at = Number(split < 0 ? "" : pair.slice(split + 1));
|
|
904
|
-
return {
|
|
905
|
-
path: split < 0 ? pair : pair.slice(0, split),
|
|
906
|
-
at: Number.isFinite(at) && at > 0 ? Math.floor(at) : 0
|
|
907
|
-
};
|
|
908
|
-
})
|
|
909
|
-
);
|
|
910
|
-
}
|
|
911
|
-
function serializeRefresh(entries) {
|
|
912
|
-
return cleanEntries(entries).map((entry) => `${entry.path}~${Math.floor(entry.at)}`).join("|");
|
|
913
|
-
}
|
|
914
|
-
function parseRefreshPaths(header) {
|
|
915
|
-
return cleanEntries(
|
|
916
|
-
(header ?? "").split("|").map((path) => ({ path, at: 0 }))
|
|
917
|
-
).map((entry) => entry.path);
|
|
918
|
-
}
|
|
919
|
-
|
|
920
932
|
// src/nextjs/decisions.ts
|
|
921
|
-
var
|
|
933
|
+
var PREPR_DECIDE_HEADER = "Prepr-Decide";
|
|
934
|
+
var DEFAULT_DECISION = "*";
|
|
922
935
|
var DECISIONS_MAX_GROUPS = 40;
|
|
923
936
|
var SAFE_ID = /^[\w-]+$/;
|
|
924
|
-
var SEEN_MAX_PAGES = 1e3;
|
|
925
|
-
function parseDecisions(raw) {
|
|
926
|
-
const out = /* @__PURE__ */ new Map();
|
|
927
|
-
for (const pair of (raw ?? "").split("~")) {
|
|
928
|
-
const at = pair.indexOf(":");
|
|
929
|
-
const group = pair.slice(0, at);
|
|
930
|
-
if (at < 1 || !SAFE_ID.test(group)) continue;
|
|
931
|
-
out.set(
|
|
932
|
-
group,
|
|
933
|
-
pair.slice(at + 1).split("|").filter((variant) => SAFE_ID.test(variant))
|
|
934
|
-
);
|
|
935
|
-
}
|
|
936
|
-
return out;
|
|
937
|
-
}
|
|
938
937
|
function serializeDecisions(decisions) {
|
|
939
|
-
return [...decisions].
|
|
938
|
+
return [...decisions].filter(([group]) => SAFE_ID.test(group)).slice(0, DECISIONS_MAX_GROUPS).map(
|
|
939
|
+
([group, variants]) => `${group}:${variants.filter((v) => SAFE_ID.test(v)).join("|")}`
|
|
940
|
+
).join("~");
|
|
940
941
|
}
|
|
941
942
|
function forwardedHeaders(request, response) {
|
|
942
943
|
const overridden = response.headers.get("x-middleware-override-headers");
|
|
@@ -949,109 +950,16 @@ function forwardedHeaders(request, response) {
|
|
|
949
950
|
return getPreprHeadersFromHeaders(headers);
|
|
950
951
|
}
|
|
951
952
|
var preprClientInternals = /* @__PURE__ */ new WeakMap();
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
if (!await client.hasSegments(document2, variables)) return void 0;
|
|
955
|
-
const decided = await client.decide(document2, variables, {
|
|
956
|
-
topLevelOnly: true,
|
|
957
|
-
headers,
|
|
958
|
-
init: { signal: AbortSignal.timeout(timeoutMs) }
|
|
959
|
-
});
|
|
960
|
-
let byVariables = seenGroups.get(document2);
|
|
961
|
-
if (!byVariables) {
|
|
962
|
-
byVariables = /* @__PURE__ */ new Map();
|
|
963
|
-
seenGroups.set(document2, byVariables);
|
|
964
|
-
}
|
|
965
|
-
const pageKey = JSON.stringify(variables ?? null);
|
|
966
|
-
let groups = byVariables.get(pageKey);
|
|
967
|
-
if (!groups) {
|
|
968
|
-
if (byVariables.size >= SEEN_MAX_PAGES) {
|
|
969
|
-
byVariables.delete(byVariables.keys().next().value);
|
|
970
|
-
}
|
|
971
|
-
groups = /* @__PURE__ */ new Set();
|
|
972
|
-
byVariables.set(pageKey, groups);
|
|
973
|
-
}
|
|
974
|
-
for (const group of decided.keys()) groups.add(group);
|
|
975
|
-
return decided;
|
|
976
|
-
}
|
|
977
|
-
function writeDecisions(request, response, decided, paths) {
|
|
978
|
-
const next = /* @__PURE__ */ new Map();
|
|
979
|
-
for (const decisions of decided) {
|
|
980
|
-
for (const [group, variants] of decisions) {
|
|
981
|
-
if (SAFE_ID.test(group) && !next.has(group)) {
|
|
982
|
-
next.set(
|
|
983
|
-
group,
|
|
984
|
-
variants.filter((v) => SAFE_ID.test(v))
|
|
985
|
-
);
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
for (const [group, variants] of parseDecisions(
|
|
990
|
-
request.cookies.get(PREPR_DECISIONS_COOKIE)?.value
|
|
991
|
-
)) {
|
|
992
|
-
if (!next.has(group)) next.set(group, variants);
|
|
993
|
-
}
|
|
994
|
-
const options = {
|
|
995
|
-
path: "/",
|
|
996
|
-
sameSite: "lax",
|
|
997
|
-
httpOnly: false,
|
|
998
|
-
secure: request.nextUrl.protocol === "https:"
|
|
999
|
-
};
|
|
1000
|
-
if (next.size > 0) {
|
|
1001
|
-
response.cookies.set(
|
|
1002
|
-
PREPR_DECISIONS_COOKIE,
|
|
1003
|
-
serializeDecisions(next),
|
|
1004
|
-
options
|
|
1005
|
-
);
|
|
1006
|
-
}
|
|
1007
|
-
if (paths.length > 0) {
|
|
1008
|
-
const at = Date.now();
|
|
1009
|
-
response.cookies.set(
|
|
1010
|
-
PREPR_REFRESH_COOKIE,
|
|
1011
|
-
serializeRefresh([
|
|
1012
|
-
...paths.map((path) => ({ path, at })),
|
|
1013
|
-
...parseRefresh(request.cookies.get(PREPR_REFRESH_COOKIE)?.value)
|
|
1014
|
-
]),
|
|
1015
|
-
options
|
|
1016
|
-
);
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
function pagePath(request) {
|
|
1020
|
-
return request.nextUrl.basePath + request.nextUrl.pathname;
|
|
1021
|
-
}
|
|
1022
|
-
function contextChanged(request, headers) {
|
|
1023
|
-
return headers.get("Prepr-Customer-Id-Created") === "true" || headers.has("Prepr-Segments") || [...request.nextUrl.searchParams.keys()].some(
|
|
1024
|
-
(key) => key.startsWith("utm_")
|
|
1025
|
-
);
|
|
1026
|
-
}
|
|
1027
|
-
async function resolveDecisionsWith(client, seenGroups, request, response, document2, variables, options) {
|
|
953
|
+
async function decideView(client, headers, page, timeoutMs = 3e3) {
|
|
954
|
+
const init = { signal: AbortSignal.timeout(timeoutMs) };
|
|
1028
955
|
try {
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
const stored = parseDecisions(
|
|
1034
|
-
request.cookies.get(PREPR_DECISIONS_COOKIE)?.value
|
|
1035
|
-
);
|
|
1036
|
-
const decidedAt = parseRefresh(request.cookies.get(PREPR_REFRESH_COOKIE)?.value).find(
|
|
1037
|
-
(entry) => entry.path === path
|
|
1038
|
-
)?.at ?? 0;
|
|
1039
|
-
const fresh = isFresh(decidedAt, options?.freshFor ?? DEFAULT_FRESH_FOR_MS);
|
|
1040
|
-
if (fresh && known && !contextChanged(request, headers) && [...known].every((group) => stored.has(group))) {
|
|
1041
|
-
return;
|
|
1042
|
-
}
|
|
1043
|
-
const decided = await decidePage(
|
|
1044
|
-
client,
|
|
1045
|
-
seenGroups,
|
|
1046
|
-
headers,
|
|
1047
|
-
document2,
|
|
1048
|
-
variables,
|
|
1049
|
-
options?.timeoutMs
|
|
1050
|
-
);
|
|
1051
|
-
if (!decided || decided.size === 0) return;
|
|
1052
|
-
writeDecisions(request, response, [decided], [path]);
|
|
956
|
+
const decided = page.decision ? decisionsFrom(
|
|
957
|
+
(await client.query(page.decision, page.variables, { headers, init })).data
|
|
958
|
+
) : await client.decide(page.document, page.variables, { headers, init });
|
|
959
|
+
return serializeDecisions(decided);
|
|
1053
960
|
} catch (error) {
|
|
1054
961
|
if (isFrameworkSignal(error)) throw error;
|
|
962
|
+
return DEFAULT_DECISION;
|
|
1055
963
|
}
|
|
1056
964
|
}
|
|
1057
965
|
|
|
@@ -1069,7 +977,7 @@ function createPreprProxy(options) {
|
|
|
1069
977
|
"createPreprProxy"
|
|
1070
978
|
);
|
|
1071
979
|
}
|
|
1072
|
-
const { client
|
|
980
|
+
const { client } = internals;
|
|
1073
981
|
const preview = options.preview ?? false;
|
|
1074
982
|
const allowed = async (request) => {
|
|
1075
983
|
if (!options.onlyIf) return true;
|
|
@@ -1086,66 +994,31 @@ function createPreprProxy(options) {
|
|
|
1086
994
|
return null;
|
|
1087
995
|
}
|
|
1088
996
|
};
|
|
1089
|
-
async function
|
|
1090
|
-
|
|
1091
|
-
status: 204,
|
|
1092
|
-
headers: { "Cache-Control": "no-store" }
|
|
1093
|
-
});
|
|
1094
|
-
for (const cookie of response.cookies.getAll()) answer.cookies.set(cookie);
|
|
997
|
+
async function answerDecision(request, response, target) {
|
|
998
|
+
let decision = DEFAULT_DECISION;
|
|
1095
999
|
const site = request.headers.get("sec-fetch-site");
|
|
1096
|
-
if (request.method
|
|
1097
|
-
|
|
1000
|
+
if (target && request.method === "GET" && (site === null || site === "same-origin") && await allowed(request)) {
|
|
1001
|
+
decision = await decideView(
|
|
1002
|
+
client,
|
|
1003
|
+
mergePreprHeaders(forwardedHeaders(request, response)),
|
|
1004
|
+
target,
|
|
1005
|
+
options.timeoutMs
|
|
1006
|
+
);
|
|
1098
1007
|
}
|
|
1099
|
-
const
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
const
|
|
1106
|
-
const decided = await Promise.all(
|
|
1107
|
-
paths.map(async (path) => {
|
|
1108
|
-
if (!own.has(path) || !path.startsWith(basePath)) return void 0;
|
|
1109
|
-
const pathname = path.slice(basePath.length) || "/";
|
|
1110
|
-
if (!isPagePath(pathname)) return void 0;
|
|
1111
|
-
const target = lookup(new URL(pathname, request.url));
|
|
1112
|
-
if (!target) return void 0;
|
|
1113
|
-
try {
|
|
1114
|
-
const decisions = await decidePage(
|
|
1115
|
-
client,
|
|
1116
|
-
seenGroups,
|
|
1117
|
-
headers,
|
|
1118
|
-
target.document,
|
|
1119
|
-
target.variables,
|
|
1120
|
-
options.timeoutMs
|
|
1121
|
-
);
|
|
1122
|
-
return decisions?.size ? [path, decisions] : void 0;
|
|
1123
|
-
} catch (error) {
|
|
1124
|
-
if (isFrameworkSignal(error)) throw error;
|
|
1125
|
-
return void 0;
|
|
1126
|
-
}
|
|
1127
|
-
})
|
|
1128
|
-
);
|
|
1129
|
-
const found = decided.filter((entry) => entry !== void 0);
|
|
1130
|
-
writeDecisions(
|
|
1131
|
-
request,
|
|
1132
|
-
answer,
|
|
1133
|
-
found.map(([, decisions]) => decisions),
|
|
1134
|
-
found.map(([path]) => path)
|
|
1135
|
-
);
|
|
1008
|
+
const answer = new NextResponse(decision, {
|
|
1009
|
+
headers: {
|
|
1010
|
+
"Cache-Control": "private, no-store",
|
|
1011
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
1012
|
+
}
|
|
1013
|
+
});
|
|
1014
|
+
for (const cookie of response.cookies.getAll()) answer.cookies.set(cookie);
|
|
1136
1015
|
return answer;
|
|
1137
1016
|
}
|
|
1138
1017
|
return async (request) => {
|
|
1139
|
-
if (request.headers.has(PREPR_REFRESH_HEADER)) {
|
|
1140
|
-
return refresh(
|
|
1141
|
-
request,
|
|
1142
|
-
createPreprMiddleware(request, { preview, features: options.features }),
|
|
1143
|
-
parseRefreshPaths(request.headers.get(PREPR_REFRESH_HEADER))
|
|
1144
|
-
);
|
|
1145
|
-
}
|
|
1146
1018
|
const rendersPage = (request.method === "GET" || request.method === "HEAD") && isPagePath(request.nextUrl.pathname);
|
|
1147
1019
|
const target = rendersPage ? lookup(request.nextUrl) : null;
|
|
1148
|
-
|
|
1020
|
+
const decisionRequest = request.headers.has(PREPR_DECIDE_HEADER);
|
|
1021
|
+
if (target && preview && !decisionRequest) {
|
|
1149
1022
|
const draft = preprDraftRedirect(request, { preview });
|
|
1150
1023
|
if (draft) return draft;
|
|
1151
1024
|
}
|
|
@@ -1153,24 +1026,7 @@ function createPreprProxy(options) {
|
|
|
1153
1026
|
preview,
|
|
1154
1027
|
features: options.features
|
|
1155
1028
|
});
|
|
1156
|
-
if (
|
|
1157
|
-
await Promise.all([
|
|
1158
|
-
resolvePreprBucket(request, response, {
|
|
1159
|
-
url,
|
|
1160
|
-
preview,
|
|
1161
|
-
onlyIf: () => options.client.hasAbTests(target.document, target.variables)
|
|
1162
|
-
}),
|
|
1163
|
-
resolveDecisionsWith(
|
|
1164
|
-
client,
|
|
1165
|
-
seenGroups,
|
|
1166
|
-
request,
|
|
1167
|
-
response,
|
|
1168
|
-
target.document,
|
|
1169
|
-
target.variables,
|
|
1170
|
-
{ freshFor: options.freshFor, timeoutMs: options.timeoutMs }
|
|
1171
|
-
)
|
|
1172
|
-
]);
|
|
1173
|
-
}
|
|
1029
|
+
if (decisionRequest) return answerDecision(request, response, target);
|
|
1174
1030
|
return response;
|
|
1175
1031
|
};
|
|
1176
1032
|
}
|
|
@@ -1178,243 +1034,223 @@ function createPreprProxy(options) {
|
|
|
1178
1034
|
// src/nextjs/stack.ts
|
|
1179
1035
|
import {
|
|
1180
1036
|
createElement,
|
|
1181
|
-
Fragment
|
|
1182
|
-
Suspense
|
|
1037
|
+
Fragment
|
|
1183
1038
|
} from "react";
|
|
1184
1039
|
|
|
1185
|
-
// src/nextjs/
|
|
1186
|
-
function
|
|
1187
|
-
const match = document.cookie.match(/(?:^|; )prepr_ab_bucket=([AB])(?:;|$)/);
|
|
1188
|
-
const bucket = match ? match[1] : "A";
|
|
1040
|
+
// src/nextjs/decision-switch.ts
|
|
1041
|
+
function applyPreprDecisions(decision) {
|
|
1189
1042
|
const state = window;
|
|
1190
|
-
|
|
1191
|
-
state.
|
|
1192
|
-
const
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
if (
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
if (viewState.__preprDecisionsView === current) return;
|
|
1212
|
-
viewState.__preprDecisionsView = current;
|
|
1213
|
-
const match = document.cookie.match(/(?:^|; )prepr_decisions=([^;]*)/);
|
|
1214
|
-
let raw;
|
|
1215
|
-
try {
|
|
1216
|
-
raw = match ? decodeURIComponent(match[1]) : "";
|
|
1217
|
-
} catch {
|
|
1218
|
-
raw = "";
|
|
1219
|
-
}
|
|
1220
|
-
const state = window;
|
|
1221
|
-
if (state.__preprDecisions === raw) return;
|
|
1222
|
-
state.__preprDecisions = raw;
|
|
1223
|
-
const id = /^[\w-]+$/;
|
|
1224
|
-
let css = "";
|
|
1225
|
-
raw.split("~").forEach((pair) => {
|
|
1226
|
-
const at = pair.indexOf(":");
|
|
1227
|
-
const group = pair.slice(0, at);
|
|
1228
|
-
if (at < 1 || !id.test(group)) return;
|
|
1229
|
-
const selector = '[data-prepr-group="' + group + '"]';
|
|
1230
|
-
css += selector + "{display:none!important}";
|
|
1231
|
-
pair.slice(at + 1).split("|").forEach((variant) => {
|
|
1232
|
-
if (id.test(variant)) {
|
|
1233
|
-
css += selector + '[data-prepr-variant="' + variant + '"]{display:contents!important}';
|
|
1234
|
-
}
|
|
1043
|
+
const key = location.pathname + location.search;
|
|
1044
|
+
if (decision !== void 0) state.__preprDecision = { key, value: decision };
|
|
1045
|
+
const current = state.__preprDecision;
|
|
1046
|
+
if (!current || current.key !== key) return false;
|
|
1047
|
+
document.querySelectorAll("[data-prepr-decision]").forEach((marker) => {
|
|
1048
|
+
marker.setAttribute("data-prepr-decided", "");
|
|
1049
|
+
});
|
|
1050
|
+
const defaults = current.value === "*";
|
|
1051
|
+
const chosen = {};
|
|
1052
|
+
current.value.split("~").forEach((pair) => {
|
|
1053
|
+
const colon = pair.indexOf(":");
|
|
1054
|
+
if (colon > 0)
|
|
1055
|
+
chosen[pair.slice(0, colon)] = pair.slice(colon + 1).split("|");
|
|
1056
|
+
});
|
|
1057
|
+
document.querySelectorAll("[data-prepr-v]").forEach((element) => {
|
|
1058
|
+
const nested = element.hasAttribute("data-prepr-nested");
|
|
1059
|
+
const show = defaults ? element.hasAttribute("data-prepr-default") : (element.getAttribute("data-prepr-v") || "").split(" ").every((token) => {
|
|
1060
|
+
const colon = token.indexOf(":");
|
|
1061
|
+
const variant = token.slice(colon + 1);
|
|
1062
|
+
const variants = chosen[token.slice(0, colon)];
|
|
1063
|
+
return !!variants && (nested ? variants[0] === variant : variants.indexOf(variant) >= 0);
|
|
1235
1064
|
});
|
|
1065
|
+
if (show) element.setAttribute("data-prepr-show", "");
|
|
1066
|
+
else element.removeAttribute("data-prepr-show");
|
|
1236
1067
|
});
|
|
1068
|
+
return true;
|
|
1069
|
+
}
|
|
1070
|
+
function startPreprDecision() {
|
|
1071
|
+
const state = window;
|
|
1072
|
+
const key = location.pathname + location.search;
|
|
1073
|
+
if (state.__preprPending && state.__preprPending.key === key) return;
|
|
1074
|
+
let last;
|
|
1237
1075
|
try {
|
|
1238
|
-
|
|
1239
|
-
const sheet = new CSSStyleSheet();
|
|
1240
|
-
document.adoptedStyleSheets = document.adoptedStyleSheets.concat(sheet);
|
|
1241
|
-
state.__preprDecisionSheet = sheet;
|
|
1242
|
-
}
|
|
1243
|
-
state.__preprDecisionSheet.replaceSync(css);
|
|
1076
|
+
last = JSON.parse(sessionStorage.getItem("prepr_decision") || "{}");
|
|
1244
1077
|
} catch {
|
|
1245
|
-
|
|
1246
|
-
state.__preprDecisionSheet = document.createElement("style");
|
|
1247
|
-
document.head.appendChild(state.__preprDecisionSheet);
|
|
1248
|
-
}
|
|
1249
|
-
state.__preprDecisionSheet.textContent = css;
|
|
1078
|
+
last = {};
|
|
1250
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) => {
|
|
1086
|
+
try {
|
|
1087
|
+
sessionStorage.setItem(
|
|
1088
|
+
"prepr_decision",
|
|
1089
|
+
JSON.stringify({ key, value: answer, at: Date.now() })
|
|
1090
|
+
);
|
|
1091
|
+
} catch {
|
|
1092
|
+
}
|
|
1093
|
+
return answer;
|
|
1094
|
+
});
|
|
1095
|
+
state.__preprPending = { key, value };
|
|
1096
|
+
value.then((answer) => {
|
|
1097
|
+
if (location.pathname + location.search === key) state.__preprApply(answer);
|
|
1098
|
+
});
|
|
1251
1099
|
}
|
|
1252
1100
|
|
|
1253
1101
|
// src/nextjs/stack.ts
|
|
1254
|
-
import {
|
|
1255
|
-
|
|
1256
|
-
PreprPersonalizationSwitch,
|
|
1257
|
-
PreprVariantTracking
|
|
1258
|
-
} from "./components.js";
|
|
1259
|
-
var AB_ATTRIBUTE = "data-prepr-ab";
|
|
1260
|
-
var AB_BUCKETS = ["A", "B"];
|
|
1261
|
-
var AB_BASE_CSS = `[${AB_ATTRIBUTE}]{display:contents}[${AB_ATTRIBUTE}]:not([${AB_ATTRIBUTE}="A"]){display:none}`;
|
|
1262
|
-
var AB_SWITCH_SCRIPT = `(${applyPreprAbBucket.toString()})();(${applyPreprDecisions.toString()})()`;
|
|
1263
|
-
var GROUP_ATTRIBUTE = "data-prepr-group";
|
|
1264
|
-
var VARIANT_ATTRIBUTE = "data-prepr-variant";
|
|
1102
|
+
import { PreprDecisionApplier, PreprVariantTracking } from "./components.js";
|
|
1103
|
+
var VARIANT_ATTRIBUTE = "data-prepr-v";
|
|
1265
1104
|
var DEFAULT_ATTRIBUTE = "data-prepr-default";
|
|
1266
|
-
var
|
|
1105
|
+
var NESTED_ATTRIBUTE = "data-prepr-nested";
|
|
1106
|
+
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
|
+
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()})();`;
|
|
1109
|
+
var APPLY_SCRIPT = "__preprApply()";
|
|
1267
1110
|
function variantKeysOf(items) {
|
|
1268
1111
|
return items.flatMap((item) => {
|
|
1269
|
-
const key = preprVariantProps(item)["data-prepr-variant-key"];
|
|
1112
|
+
const key = preprVariantProps(item)["data-prepr-variant-key"] ?? nestedKeyOf(item);
|
|
1270
1113
|
return key ? [key] : [];
|
|
1271
1114
|
});
|
|
1272
1115
|
}
|
|
1273
|
-
function
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
placeholder = false,
|
|
1281
|
-
defaultVariant = true
|
|
1282
|
-
} = options;
|
|
1283
|
-
const renderVariant = (variant, key) => {
|
|
1284
|
-
const resolved = resolveVariants(items, { decisions, bucket: variant });
|
|
1285
|
-
const ab = abTest && !bucket ? variant : void 0;
|
|
1286
|
-
const content = render(resolved, {
|
|
1287
|
-
index,
|
|
1288
|
-
placeholder,
|
|
1289
|
-
defaultVariant: defaultVariant && (ab === void 0 || ab === "A")
|
|
1290
|
-
});
|
|
1291
|
-
const variantKeys = track ? variantKeysOf(resolved) : [];
|
|
1292
|
-
if (variantKeys.length > 0) {
|
|
1293
|
-
return createElement(
|
|
1294
|
-
PreprVariantTracking,
|
|
1295
|
-
{ key, variantKeys, ab },
|
|
1296
|
-
content
|
|
1297
|
-
);
|
|
1116
|
+
function nestedKeyOf(value) {
|
|
1117
|
+
if (!value || typeof value !== "object") return void 0;
|
|
1118
|
+
for (const child of Object.values(value)) {
|
|
1119
|
+
const items = Array.isArray(child) ? child : [child];
|
|
1120
|
+
for (const item of items) {
|
|
1121
|
+
const key = contextOf(item)?.variant_key ?? (item && nestedKeyOf(item));
|
|
1122
|
+
if (key) return key;
|
|
1298
1123
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
};
|
|
1302
|
-
if (!abTest || bucket) return renderVariant(bucket);
|
|
1303
|
-
return AB_BUCKETS.map((variant) => renderVariant(variant, variant));
|
|
1124
|
+
}
|
|
1125
|
+
return void 0;
|
|
1304
1126
|
}
|
|
1305
|
-
function
|
|
1306
|
-
const
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
];
|
|
1315
|
-
return
|
|
1316
|
-
|
|
1127
|
+
function renderItems(items, render, options) {
|
|
1128
|
+
const resolved = resolveVariants(items, {
|
|
1129
|
+
decisions: options.decisions,
|
|
1130
|
+
bucket: options.bucket
|
|
1131
|
+
});
|
|
1132
|
+
const content = render(resolved, {
|
|
1133
|
+
index: options.index,
|
|
1134
|
+
defaultVariant: options.defaultVariant
|
|
1135
|
+
});
|
|
1136
|
+
const variantKeys = options.track ? variantKeysOf(resolved) : [];
|
|
1137
|
+
return variantKeys.length > 0 ? createElement(PreprVariantTracking, { variantKeys }, content) : content;
|
|
1138
|
+
}
|
|
1139
|
+
function decidedGroups(items, bucket) {
|
|
1140
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1141
|
+
const walk = (value) => {
|
|
1142
|
+
if (Array.isArray(value)) return value.forEach(walk);
|
|
1143
|
+
if (!value || typeof value !== "object") return;
|
|
1144
|
+
const ctx = contextOf(value);
|
|
1145
|
+
const decided = ctx?.kind === "PERSONALIZATION" || ctx?.kind === "AB_TEST" && !bucket;
|
|
1146
|
+
if (decided && ctx.variant_id) {
|
|
1147
|
+
const group = groups.get(ctx.group_id) ?? {
|
|
1148
|
+
variants: /* @__PURE__ */ new Set(),
|
|
1149
|
+
fallback: ctx.kind === "AB_TEST" ? "A" : PREPR_DEFAULT_VARIANT
|
|
1150
|
+
};
|
|
1151
|
+
group.variants.add(ctx.variant_id);
|
|
1152
|
+
groups.set(ctx.group_id, group);
|
|
1153
|
+
}
|
|
1154
|
+
Object.values(value).forEach(walk);
|
|
1155
|
+
};
|
|
1156
|
+
walk(items);
|
|
1157
|
+
return groups;
|
|
1158
|
+
}
|
|
1159
|
+
function renderVariants(block, groups, render, options) {
|
|
1160
|
+
const top = block.key.startsWith("group:") ? block.key.slice("group:".length) : void 0;
|
|
1161
|
+
const nested = !(top !== void 0 && groups.size === 1);
|
|
1162
|
+
const choices = [...groups].map(([group, { variants, fallback }]) => {
|
|
1163
|
+
const ids = nested ? [fallback, ...variants] : [...variants];
|
|
1164
|
+
return [...new Set(ids)].map((variant) => [group, variant]);
|
|
1165
|
+
});
|
|
1166
|
+
const combinations = choices.reduce(
|
|
1167
|
+
(all, pairs) => all.flatMap((combo) => pairs.map((p) => [...combo, p])),
|
|
1168
|
+
[[]]
|
|
1169
|
+
);
|
|
1170
|
+
return combinations.map((combination) => {
|
|
1171
|
+
const pairs = combination.map(([group, variant]) => `${group}:${variant}`);
|
|
1172
|
+
const isDefault = combination.every(
|
|
1173
|
+
([group, variant]) => variant === groups.get(group).fallback
|
|
1174
|
+
);
|
|
1175
|
+
return createElement(
|
|
1317
1176
|
"div",
|
|
1318
1177
|
{
|
|
1319
|
-
key:
|
|
1320
|
-
[
|
|
1321
|
-
[
|
|
1322
|
-
...
|
|
1178
|
+
key: pairs.join(" "),
|
|
1179
|
+
[VARIANT_ATTRIBUTE]: pairs.join(" "),
|
|
1180
|
+
...isDefault ? { [DEFAULT_ATTRIBUTE]: "" } : {},
|
|
1181
|
+
...nested ? { [NESTED_ATTRIBUTE]: "" } : {},
|
|
1182
|
+
// The decision script marks the shown wrapper before hydration.
|
|
1183
|
+
suppressHydrationWarning: true
|
|
1323
1184
|
},
|
|
1324
|
-
|
|
1185
|
+
renderItems(block.items, render, {
|
|
1325
1186
|
bucket: options.bucket,
|
|
1326
|
-
decisions:
|
|
1327
|
-
abTest: false,
|
|
1187
|
+
decisions: new Map(combination.map(([g, v]) => [g, [v]])),
|
|
1328
1188
|
index: block.index,
|
|
1329
1189
|
track: options.track,
|
|
1330
|
-
defaultVariant:
|
|
1190
|
+
defaultVariant: isDefault
|
|
1331
1191
|
})
|
|
1332
|
-
)
|
|
1333
|
-
);
|
|
1334
|
-
}
|
|
1335
|
-
async function PersonalizedBlock({
|
|
1336
|
-
items,
|
|
1337
|
-
decisions,
|
|
1338
|
-
bucket,
|
|
1339
|
-
abTest,
|
|
1340
|
-
index,
|
|
1341
|
-
render,
|
|
1342
|
-
track
|
|
1343
|
-
}) {
|
|
1344
|
-
const decided = await decisions.catch((error) => {
|
|
1345
|
-
if (isFrameworkSignal(error)) throw error;
|
|
1346
|
-
return void 0;
|
|
1347
|
-
});
|
|
1348
|
-
return renderBuckets(items, render, {
|
|
1349
|
-
bucket,
|
|
1350
|
-
decisions: decided,
|
|
1351
|
-
abTest,
|
|
1352
|
-
index,
|
|
1353
|
-
track
|
|
1192
|
+
);
|
|
1354
1193
|
});
|
|
1355
1194
|
}
|
|
1356
1195
|
function PreprStack({
|
|
1357
1196
|
items,
|
|
1358
|
-
decisions,
|
|
1359
1197
|
render,
|
|
1360
1198
|
bucket,
|
|
1361
1199
|
nonce,
|
|
1362
|
-
trackVariants = false
|
|
1363
|
-
aboveTheFold = 1
|
|
1200
|
+
trackVariants = false
|
|
1364
1201
|
}) {
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
const
|
|
1202
|
+
const blocks = splitStack(items).map((block) => ({
|
|
1203
|
+
...block,
|
|
1204
|
+
groups: decidedGroups(block.items, bucket)
|
|
1205
|
+
}));
|
|
1206
|
+
const varies = blocks.some((block) => block.groups.size > 0);
|
|
1370
1207
|
return createElement(
|
|
1371
1208
|
Fragment,
|
|
1372
1209
|
null,
|
|
1373
|
-
|
|
1210
|
+
varies && createElement(
|
|
1374
1211
|
Fragment,
|
|
1375
|
-
{ key: "prepr-
|
|
1212
|
+
{ key: "prepr-switch" },
|
|
1376
1213
|
createElement("style", {
|
|
1377
1214
|
nonce,
|
|
1378
|
-
dangerouslySetInnerHTML: {
|
|
1379
|
-
|
|
1380
|
-
|
|
1215
|
+
dangerouslySetInnerHTML: { __html: GROUP_BASE_CSS }
|
|
1216
|
+
}),
|
|
1217
|
+
createElement("noscript", {
|
|
1218
|
+
dangerouslySetInnerHTML: { __html: NOSCRIPT_HTML }
|
|
1381
1219
|
}),
|
|
1382
1220
|
createElement("script", {
|
|
1383
1221
|
nonce,
|
|
1384
|
-
dangerouslySetInnerHTML: { __html:
|
|
1222
|
+
dangerouslySetInnerHTML: { __html: DECISION_SCRIPT }
|
|
1385
1223
|
}),
|
|
1386
|
-
createElement(
|
|
1387
|
-
|
|
1224
|
+
createElement(PreprDecisionApplier),
|
|
1225
|
+
// Marked decided by the decision switch; never laid out.
|
|
1226
|
+
createElement("template", {
|
|
1227
|
+
"data-prepr-decision": "",
|
|
1228
|
+
suppressHydrationWarning: true
|
|
1229
|
+
})
|
|
1388
1230
|
),
|
|
1389
1231
|
...blocks.map((block) => {
|
|
1390
|
-
if (
|
|
1232
|
+
if (block.groups.size === 0) {
|
|
1391
1233
|
return createElement(
|
|
1392
1234
|
Fragment,
|
|
1393
1235
|
{ key: block.key },
|
|
1394
|
-
|
|
1236
|
+
renderItems(block.items, render, {
|
|
1237
|
+
bucket,
|
|
1238
|
+
index: block.index,
|
|
1239
|
+
track: trackVariants,
|
|
1240
|
+
defaultVariant: true
|
|
1241
|
+
})
|
|
1395
1242
|
);
|
|
1396
1243
|
}
|
|
1397
|
-
const fallback = renderBuckets(block.items, render, {
|
|
1398
|
-
bucket,
|
|
1399
|
-
abTest: block.abTest,
|
|
1400
|
-
index: block.index,
|
|
1401
|
-
track: trackVariants && !block.personalized,
|
|
1402
|
-
placeholder: block.personalized
|
|
1403
|
-
});
|
|
1404
|
-
if (!block.personalized) {
|
|
1405
|
-
return createElement(Fragment, { key: block.key }, fallback);
|
|
1406
|
-
}
|
|
1407
1244
|
return createElement(
|
|
1408
|
-
|
|
1409
|
-
{ key: block.key
|
|
1410
|
-
|
|
1411
|
-
items: block.items,
|
|
1412
|
-
decisions,
|
|
1245
|
+
Fragment,
|
|
1246
|
+
{ key: block.key },
|
|
1247
|
+
renderVariants(block, block.groups, render, {
|
|
1413
1248
|
bucket,
|
|
1414
|
-
abTest: block.abTest,
|
|
1415
|
-
index: block.index,
|
|
1416
|
-
render,
|
|
1417
1249
|
track: trackVariants
|
|
1250
|
+
}),
|
|
1251
|
+
createElement("script", {
|
|
1252
|
+
nonce,
|
|
1253
|
+
dangerouslySetInnerHTML: { __html: APPLY_SCRIPT }
|
|
1418
1254
|
})
|
|
1419
1255
|
);
|
|
1420
1256
|
})
|
|
@@ -1426,33 +1262,59 @@ function createPreprClient2(options) {
|
|
|
1426
1262
|
const { cacheTags = ["prepr"], revalidate, ...coreOptions } = options;
|
|
1427
1263
|
const client = createPreprClient({
|
|
1428
1264
|
// The schema introspection goes through the Data Cache: shared across
|
|
1429
|
-
// server instances, refreshed hourly and by
|
|
1265
|
+
// server instances, refreshed hourly and by a `schema.changed` webhook.
|
|
1430
1266
|
schemaInit: {
|
|
1431
|
-
next: { tags: cacheTags, revalidate: 3600 }
|
|
1267
|
+
next: { tags: [...cacheTags, PREPR_SCHEMA_TAG], revalidate: 3600 }
|
|
1432
1268
|
},
|
|
1433
1269
|
...coreOptions
|
|
1434
1270
|
});
|
|
1435
|
-
const seenGroups = /* @__PURE__ */ new Map();
|
|
1436
1271
|
const nextClient = {
|
|
1437
1272
|
async query(document2, variables, queryOptions = {}) {
|
|
1438
|
-
const { personalize = false, ...rest } = queryOptions;
|
|
1439
|
-
const init = rest.init;
|
|
1440
|
-
|
|
1273
|
+
const { personalize = false, tagItems = false, ...rest } = queryOptions;
|
|
1274
|
+
const { next, ...init } = rest.init ?? {};
|
|
1275
|
+
if (tagItems && typeof nextCache.cacheTag !== "function") {
|
|
1276
|
+
throw new Error("tagItems requires Next.js 16 or later.");
|
|
1277
|
+
}
|
|
1278
|
+
const options2 = {
|
|
1441
1279
|
...rest,
|
|
1442
|
-
|
|
1280
|
+
// With tagItems, `next` is dropped too: tags or revalidate alongside
|
|
1281
|
+
// `no-store` are a conflict Next.js resolves by ignoring the store.
|
|
1282
|
+
// Otherwise tags are attached unconditionally; the core client drops
|
|
1283
|
+
// them again whenever the request ends up `no-store` (personalized,
|
|
1284
|
+
// per-request headers, or preview).
|
|
1285
|
+
init: tagItems ? { ...init, cache: "no-store" } : {
|
|
1443
1286
|
...init,
|
|
1444
1287
|
next: {
|
|
1445
|
-
tags: cacheTags,
|
|
1446
1288
|
...revalidate === void 0 ? {} : { revalidate },
|
|
1447
|
-
...
|
|
1289
|
+
...next,
|
|
1290
|
+
// Merged, so a per-query tag never drops the client's own,
|
|
1291
|
+
// which the publish webhook relies on.
|
|
1292
|
+
tags: [...cacheTags, ...next?.tags ?? []]
|
|
1448
1293
|
}
|
|
1449
1294
|
}
|
|
1450
1295
|
};
|
|
1451
|
-
if (
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1296
|
+
if (personalize) {
|
|
1297
|
+
options2.headers = mergePreprHeaders(
|
|
1298
|
+
await getPreprHeaders(),
|
|
1299
|
+
rest.headers
|
|
1300
|
+
);
|
|
1301
|
+
}
|
|
1302
|
+
const result = await client.query(document2, variables, options2);
|
|
1303
|
+
if (tagItems) {
|
|
1304
|
+
let modelTags;
|
|
1305
|
+
try {
|
|
1306
|
+
modelTags = preprModelTagsFor(
|
|
1307
|
+
documentOf(document2),
|
|
1308
|
+
result.data,
|
|
1309
|
+
await clientSchemas.get(client)()
|
|
1310
|
+
);
|
|
1311
|
+
} catch {
|
|
1312
|
+
modelTags = [PREPR_ANY_ITEM_TAG];
|
|
1313
|
+
}
|
|
1314
|
+
nextCache.cacheTag(...cacheTags, ...next?.tags ?? [], ...modelTags);
|
|
1315
|
+
nextCache.cacheTag(...preprCacheTags(result.data));
|
|
1316
|
+
}
|
|
1317
|
+
return result;
|
|
1456
1318
|
},
|
|
1457
1319
|
async decide(document2, variables, queryOptions = {}) {
|
|
1458
1320
|
return client.decide(document2, variables, {
|
|
@@ -1463,26 +1325,13 @@ function createPreprClient2(options) {
|
|
|
1463
1325
|
)
|
|
1464
1326
|
});
|
|
1465
1327
|
},
|
|
1466
|
-
hasAbTests: (document2, variables) => client.hasAbTests(document2, variables)
|
|
1467
|
-
resolveDecisions: (request, response, document2, variables, options2) => resolveDecisionsWith(
|
|
1468
|
-
client,
|
|
1469
|
-
seenGroups,
|
|
1470
|
-
request,
|
|
1471
|
-
response,
|
|
1472
|
-
document2,
|
|
1473
|
-
variables,
|
|
1474
|
-
options2
|
|
1475
|
-
)
|
|
1328
|
+
hasAbTests: (document2, variables) => client.hasAbTests(document2, variables)
|
|
1476
1329
|
};
|
|
1477
|
-
preprClientInternals.set(nextClient, {
|
|
1478
|
-
client,
|
|
1479
|
-
seenGroups,
|
|
1480
|
-
url: coreOptions.url
|
|
1481
|
-
});
|
|
1330
|
+
preprClientInternals.set(nextClient, { client });
|
|
1482
1331
|
return nextClient;
|
|
1483
1332
|
}
|
|
1484
1333
|
export {
|
|
1485
|
-
|
|
1334
|
+
PREPR_ANY_ITEM_TAG,
|
|
1486
1335
|
PREPR_DEFAULT_VARIANT,
|
|
1487
1336
|
PreprError,
|
|
1488
1337
|
PreprStack,
|
|
@@ -1491,6 +1340,8 @@ export {
|
|
|
1491
1340
|
decisionsFrom,
|
|
1492
1341
|
isPersonalizedResponse,
|
|
1493
1342
|
loadPreprSchema,
|
|
1343
|
+
preprCacheTags,
|
|
1344
|
+
preprModelTag,
|
|
1494
1345
|
preprVariantProps,
|
|
1495
1346
|
resolveVariants,
|
|
1496
1347
|
splitStack,
|