@preprio/toolkit 0.6.0-alpha.5 → 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-CD4LXNR3.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-5O7RGYHX.js → chunk-3QGWCYTL.js} +1 -1
- package/dist/{chunk-ZWKHACNA.js → chunk-PFJO4TYL.js} +52 -5
- package/dist/{chunk-HPWTLDLR.js → chunk-U7TRFVK7.js} +1 -1
- package/dist/{chunk-OH2PPC76.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-L4L6X3JJ.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 +374 -510
- package/dist/nextjs/graphql.d.cts +131 -135
- package/dist/nextjs/graphql.d.ts +131 -135
- package/dist/nextjs/graphql.js +309 -457
- 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-PJZMLKJ2.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-SKBW7RBD.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,65 +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
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
parseRefreshPaths(request.headers.get(PREPR_REFRESH_HEADER))
|
|
1144
|
-
);
|
|
1145
|
-
}
|
|
1146
|
-
const target = isPagePath(request.nextUrl.pathname) ? lookup(request.nextUrl) : null;
|
|
1147
|
-
if (target && preview) {
|
|
1018
|
+
const rendersPage = (request.method === "GET" || request.method === "HEAD") && isPagePath(request.nextUrl.pathname);
|
|
1019
|
+
const target = rendersPage ? lookup(request.nextUrl) : null;
|
|
1020
|
+
const decisionRequest = request.headers.has(PREPR_DECIDE_HEADER);
|
|
1021
|
+
if (target && preview && !decisionRequest) {
|
|
1148
1022
|
const draft = preprDraftRedirect(request, { preview });
|
|
1149
1023
|
if (draft) return draft;
|
|
1150
1024
|
}
|
|
@@ -1152,24 +1026,7 @@ function createPreprProxy(options) {
|
|
|
1152
1026
|
preview,
|
|
1153
1027
|
features: options.features
|
|
1154
1028
|
});
|
|
1155
|
-
if (
|
|
1156
|
-
await Promise.all([
|
|
1157
|
-
resolvePreprBucket(request, response, {
|
|
1158
|
-
url,
|
|
1159
|
-
preview,
|
|
1160
|
-
onlyIf: () => options.client.hasAbTests(target.document, target.variables)
|
|
1161
|
-
}),
|
|
1162
|
-
resolveDecisionsWith(
|
|
1163
|
-
client,
|
|
1164
|
-
seenGroups,
|
|
1165
|
-
request,
|
|
1166
|
-
response,
|
|
1167
|
-
target.document,
|
|
1168
|
-
target.variables,
|
|
1169
|
-
{ freshFor: options.freshFor, timeoutMs: options.timeoutMs }
|
|
1170
|
-
)
|
|
1171
|
-
]);
|
|
1172
|
-
}
|
|
1029
|
+
if (decisionRequest) return answerDecision(request, response, target);
|
|
1173
1030
|
return response;
|
|
1174
1031
|
};
|
|
1175
1032
|
}
|
|
@@ -1177,243 +1034,223 @@ function createPreprProxy(options) {
|
|
|
1177
1034
|
// src/nextjs/stack.ts
|
|
1178
1035
|
import {
|
|
1179
1036
|
createElement,
|
|
1180
|
-
Fragment
|
|
1181
|
-
Suspense
|
|
1037
|
+
Fragment
|
|
1182
1038
|
} from "react";
|
|
1183
1039
|
|
|
1184
|
-
// src/nextjs/
|
|
1185
|
-
function
|
|
1186
|
-
const match = document.cookie.match(/(?:^|; )prepr_ab_bucket=([AB])(?:;|$)/);
|
|
1187
|
-
const bucket = match ? match[1] : "A";
|
|
1040
|
+
// src/nextjs/decision-switch.ts
|
|
1041
|
+
function applyPreprDecisions(decision) {
|
|
1188
1042
|
const state = window;
|
|
1189
|
-
|
|
1190
|
-
state.
|
|
1191
|
-
const
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
if (
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
if (viewState.__preprDecisionsView === current) return;
|
|
1211
|
-
viewState.__preprDecisionsView = current;
|
|
1212
|
-
const match = document.cookie.match(/(?:^|; )prepr_decisions=([^;]*)/);
|
|
1213
|
-
let raw;
|
|
1214
|
-
try {
|
|
1215
|
-
raw = match ? decodeURIComponent(match[1]) : "";
|
|
1216
|
-
} catch {
|
|
1217
|
-
raw = "";
|
|
1218
|
-
}
|
|
1219
|
-
const state = window;
|
|
1220
|
-
if (state.__preprDecisions === raw) return;
|
|
1221
|
-
state.__preprDecisions = raw;
|
|
1222
|
-
const id = /^[\w-]+$/;
|
|
1223
|
-
let css = "";
|
|
1224
|
-
raw.split("~").forEach((pair) => {
|
|
1225
|
-
const at = pair.indexOf(":");
|
|
1226
|
-
const group = pair.slice(0, at);
|
|
1227
|
-
if (at < 1 || !id.test(group)) return;
|
|
1228
|
-
const selector = '[data-prepr-group="' + group + '"]';
|
|
1229
|
-
css += selector + "{display:none!important}";
|
|
1230
|
-
pair.slice(at + 1).split("|").forEach((variant) => {
|
|
1231
|
-
if (id.test(variant)) {
|
|
1232
|
-
css += selector + '[data-prepr-variant="' + variant + '"]{display:contents!important}';
|
|
1233
|
-
}
|
|
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);
|
|
1234
1064
|
});
|
|
1065
|
+
if (show) element.setAttribute("data-prepr-show", "");
|
|
1066
|
+
else element.removeAttribute("data-prepr-show");
|
|
1235
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;
|
|
1236
1075
|
try {
|
|
1237
|
-
|
|
1238
|
-
const sheet = new CSSStyleSheet();
|
|
1239
|
-
document.adoptedStyleSheets = document.adoptedStyleSheets.concat(sheet);
|
|
1240
|
-
state.__preprDecisionSheet = sheet;
|
|
1241
|
-
}
|
|
1242
|
-
state.__preprDecisionSheet.replaceSync(css);
|
|
1076
|
+
last = JSON.parse(sessionStorage.getItem("prepr_decision") || "{}");
|
|
1243
1077
|
} catch {
|
|
1244
|
-
|
|
1245
|
-
state.__preprDecisionSheet = document.createElement("style");
|
|
1246
|
-
document.head.appendChild(state.__preprDecisionSheet);
|
|
1247
|
-
}
|
|
1248
|
-
state.__preprDecisionSheet.textContent = css;
|
|
1078
|
+
last = {};
|
|
1249
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
|
+
});
|
|
1250
1099
|
}
|
|
1251
1100
|
|
|
1252
1101
|
// src/nextjs/stack.ts
|
|
1253
|
-
import {
|
|
1254
|
-
|
|
1255
|
-
PreprPersonalizationSwitch,
|
|
1256
|
-
PreprVariantTracking
|
|
1257
|
-
} from "./components.js";
|
|
1258
|
-
var AB_ATTRIBUTE = "data-prepr-ab";
|
|
1259
|
-
var AB_BUCKETS = ["A", "B"];
|
|
1260
|
-
var AB_BASE_CSS = `[${AB_ATTRIBUTE}]{display:contents}[${AB_ATTRIBUTE}]:not([${AB_ATTRIBUTE}="A"]){display:none}`;
|
|
1261
|
-
var AB_SWITCH_SCRIPT = `(${applyPreprAbBucket.toString()})();(${applyPreprDecisions.toString()})()`;
|
|
1262
|
-
var GROUP_ATTRIBUTE = "data-prepr-group";
|
|
1263
|
-
var VARIANT_ATTRIBUTE = "data-prepr-variant";
|
|
1102
|
+
import { PreprDecisionApplier, PreprVariantTracking } from "./components.js";
|
|
1103
|
+
var VARIANT_ATTRIBUTE = "data-prepr-v";
|
|
1264
1104
|
var DEFAULT_ATTRIBUTE = "data-prepr-default";
|
|
1265
|
-
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()";
|
|
1266
1110
|
function variantKeysOf(items) {
|
|
1267
1111
|
return items.flatMap((item) => {
|
|
1268
|
-
const key = preprVariantProps(item)["data-prepr-variant-key"];
|
|
1112
|
+
const key = preprVariantProps(item)["data-prepr-variant-key"] ?? nestedKeyOf(item);
|
|
1269
1113
|
return key ? [key] : [];
|
|
1270
1114
|
});
|
|
1271
1115
|
}
|
|
1272
|
-
function
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
placeholder = false,
|
|
1280
|
-
defaultVariant = true
|
|
1281
|
-
} = options;
|
|
1282
|
-
const renderVariant = (variant, key) => {
|
|
1283
|
-
const resolved = resolveVariants(items, { decisions, bucket: variant });
|
|
1284
|
-
const ab = abTest && !bucket ? variant : void 0;
|
|
1285
|
-
const content = render(resolved, {
|
|
1286
|
-
index,
|
|
1287
|
-
placeholder,
|
|
1288
|
-
defaultVariant: defaultVariant && (ab === void 0 || ab === "A")
|
|
1289
|
-
});
|
|
1290
|
-
const variantKeys = track ? variantKeysOf(resolved) : [];
|
|
1291
|
-
if (variantKeys.length > 0) {
|
|
1292
|
-
return createElement(
|
|
1293
|
-
PreprVariantTracking,
|
|
1294
|
-
{ key, variantKeys, ab },
|
|
1295
|
-
content
|
|
1296
|
-
);
|
|
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;
|
|
1297
1123
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
};
|
|
1301
|
-
if (!abTest || bucket) return renderVariant(bucket);
|
|
1302
|
-
return AB_BUCKETS.map((variant) => renderVariant(variant, variant));
|
|
1124
|
+
}
|
|
1125
|
+
return void 0;
|
|
1303
1126
|
}
|
|
1304
|
-
function
|
|
1305
|
-
const
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
];
|
|
1314
|
-
return
|
|
1315
|
-
|
|
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(
|
|
1316
1176
|
"div",
|
|
1317
1177
|
{
|
|
1318
|
-
key:
|
|
1319
|
-
[
|
|
1320
|
-
[
|
|
1321
|
-
...
|
|
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
|
|
1322
1184
|
},
|
|
1323
|
-
|
|
1185
|
+
renderItems(block.items, render, {
|
|
1324
1186
|
bucket: options.bucket,
|
|
1325
|
-
decisions:
|
|
1326
|
-
abTest: false,
|
|
1187
|
+
decisions: new Map(combination.map(([g, v]) => [g, [v]])),
|
|
1327
1188
|
index: block.index,
|
|
1328
1189
|
track: options.track,
|
|
1329
|
-
defaultVariant:
|
|
1190
|
+
defaultVariant: isDefault
|
|
1330
1191
|
})
|
|
1331
|
-
)
|
|
1332
|
-
);
|
|
1333
|
-
}
|
|
1334
|
-
async function PersonalizedBlock({
|
|
1335
|
-
items,
|
|
1336
|
-
decisions,
|
|
1337
|
-
bucket,
|
|
1338
|
-
abTest,
|
|
1339
|
-
index,
|
|
1340
|
-
render,
|
|
1341
|
-
track
|
|
1342
|
-
}) {
|
|
1343
|
-
const decided = await decisions.catch((error) => {
|
|
1344
|
-
if (isFrameworkSignal(error)) throw error;
|
|
1345
|
-
return void 0;
|
|
1346
|
-
});
|
|
1347
|
-
return renderBuckets(items, render, {
|
|
1348
|
-
bucket,
|
|
1349
|
-
decisions: decided,
|
|
1350
|
-
abTest,
|
|
1351
|
-
index,
|
|
1352
|
-
track
|
|
1192
|
+
);
|
|
1353
1193
|
});
|
|
1354
1194
|
}
|
|
1355
1195
|
function PreprStack({
|
|
1356
1196
|
items,
|
|
1357
|
-
decisions,
|
|
1358
1197
|
render,
|
|
1359
1198
|
bucket,
|
|
1360
1199
|
nonce,
|
|
1361
|
-
trackVariants = false
|
|
1362
|
-
aboveTheFold = 1
|
|
1200
|
+
trackVariants = false
|
|
1363
1201
|
}) {
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
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);
|
|
1369
1207
|
return createElement(
|
|
1370
1208
|
Fragment,
|
|
1371
1209
|
null,
|
|
1372
|
-
|
|
1210
|
+
varies && createElement(
|
|
1373
1211
|
Fragment,
|
|
1374
|
-
{ key: "prepr-
|
|
1212
|
+
{ key: "prepr-switch" },
|
|
1375
1213
|
createElement("style", {
|
|
1376
1214
|
nonce,
|
|
1377
|
-
dangerouslySetInnerHTML: {
|
|
1378
|
-
|
|
1379
|
-
|
|
1215
|
+
dangerouslySetInnerHTML: { __html: GROUP_BASE_CSS }
|
|
1216
|
+
}),
|
|
1217
|
+
createElement("noscript", {
|
|
1218
|
+
dangerouslySetInnerHTML: { __html: NOSCRIPT_HTML }
|
|
1380
1219
|
}),
|
|
1381
1220
|
createElement("script", {
|
|
1382
1221
|
nonce,
|
|
1383
|
-
dangerouslySetInnerHTML: { __html:
|
|
1222
|
+
dangerouslySetInnerHTML: { __html: DECISION_SCRIPT }
|
|
1384
1223
|
}),
|
|
1385
|
-
createElement(
|
|
1386
|
-
|
|
1224
|
+
createElement(PreprDecisionApplier),
|
|
1225
|
+
// Marked decided by the decision switch; never laid out.
|
|
1226
|
+
createElement("template", {
|
|
1227
|
+
"data-prepr-decision": "",
|
|
1228
|
+
suppressHydrationWarning: true
|
|
1229
|
+
})
|
|
1387
1230
|
),
|
|
1388
1231
|
...blocks.map((block) => {
|
|
1389
|
-
if (
|
|
1232
|
+
if (block.groups.size === 0) {
|
|
1390
1233
|
return createElement(
|
|
1391
1234
|
Fragment,
|
|
1392
1235
|
{ key: block.key },
|
|
1393
|
-
|
|
1236
|
+
renderItems(block.items, render, {
|
|
1237
|
+
bucket,
|
|
1238
|
+
index: block.index,
|
|
1239
|
+
track: trackVariants,
|
|
1240
|
+
defaultVariant: true
|
|
1241
|
+
})
|
|
1394
1242
|
);
|
|
1395
1243
|
}
|
|
1396
|
-
const fallback = renderBuckets(block.items, render, {
|
|
1397
|
-
bucket,
|
|
1398
|
-
abTest: block.abTest,
|
|
1399
|
-
index: block.index,
|
|
1400
|
-
track: trackVariants && !block.personalized,
|
|
1401
|
-
placeholder: block.personalized
|
|
1402
|
-
});
|
|
1403
|
-
if (!block.personalized) {
|
|
1404
|
-
return createElement(Fragment, { key: block.key }, fallback);
|
|
1405
|
-
}
|
|
1406
1244
|
return createElement(
|
|
1407
|
-
|
|
1408
|
-
{ key: block.key
|
|
1409
|
-
|
|
1410
|
-
items: block.items,
|
|
1411
|
-
decisions,
|
|
1245
|
+
Fragment,
|
|
1246
|
+
{ key: block.key },
|
|
1247
|
+
renderVariants(block, block.groups, render, {
|
|
1412
1248
|
bucket,
|
|
1413
|
-
abTest: block.abTest,
|
|
1414
|
-
index: block.index,
|
|
1415
|
-
render,
|
|
1416
1249
|
track: trackVariants
|
|
1250
|
+
}),
|
|
1251
|
+
createElement("script", {
|
|
1252
|
+
nonce,
|
|
1253
|
+
dangerouslySetInnerHTML: { __html: APPLY_SCRIPT }
|
|
1417
1254
|
})
|
|
1418
1255
|
);
|
|
1419
1256
|
})
|
|
@@ -1425,33 +1262,59 @@ function createPreprClient2(options) {
|
|
|
1425
1262
|
const { cacheTags = ["prepr"], revalidate, ...coreOptions } = options;
|
|
1426
1263
|
const client = createPreprClient({
|
|
1427
1264
|
// The schema introspection goes through the Data Cache: shared across
|
|
1428
|
-
// server instances, refreshed hourly and by
|
|
1265
|
+
// server instances, refreshed hourly and by a `schema.changed` webhook.
|
|
1429
1266
|
schemaInit: {
|
|
1430
|
-
next: { tags: cacheTags, revalidate: 3600 }
|
|
1267
|
+
next: { tags: [...cacheTags, PREPR_SCHEMA_TAG], revalidate: 3600 }
|
|
1431
1268
|
},
|
|
1432
1269
|
...coreOptions
|
|
1433
1270
|
});
|
|
1434
|
-
const seenGroups = /* @__PURE__ */ new Map();
|
|
1435
1271
|
const nextClient = {
|
|
1436
1272
|
async query(document2, variables, queryOptions = {}) {
|
|
1437
|
-
const { personalize = false, ...rest } = queryOptions;
|
|
1438
|
-
const init = rest.init;
|
|
1439
|
-
|
|
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 = {
|
|
1440
1279
|
...rest,
|
|
1441
|
-
|
|
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" } : {
|
|
1442
1286
|
...init,
|
|
1443
1287
|
next: {
|
|
1444
|
-
tags: cacheTags,
|
|
1445
1288
|
...revalidate === void 0 ? {} : { revalidate },
|
|
1446
|
-
...
|
|
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 ?? []]
|
|
1447
1293
|
}
|
|
1448
1294
|
}
|
|
1449
1295
|
};
|
|
1450
|
-
if (
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
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;
|
|
1455
1318
|
},
|
|
1456
1319
|
async decide(document2, variables, queryOptions = {}) {
|
|
1457
1320
|
return client.decide(document2, variables, {
|
|
@@ -1462,26 +1325,13 @@ function createPreprClient2(options) {
|
|
|
1462
1325
|
)
|
|
1463
1326
|
});
|
|
1464
1327
|
},
|
|
1465
|
-
hasAbTests: (document2, variables) => client.hasAbTests(document2, variables)
|
|
1466
|
-
resolveDecisions: (request, response, document2, variables, options2) => resolveDecisionsWith(
|
|
1467
|
-
client,
|
|
1468
|
-
seenGroups,
|
|
1469
|
-
request,
|
|
1470
|
-
response,
|
|
1471
|
-
document2,
|
|
1472
|
-
variables,
|
|
1473
|
-
options2
|
|
1474
|
-
)
|
|
1328
|
+
hasAbTests: (document2, variables) => client.hasAbTests(document2, variables)
|
|
1475
1329
|
};
|
|
1476
|
-
preprClientInternals.set(nextClient, {
|
|
1477
|
-
client,
|
|
1478
|
-
seenGroups,
|
|
1479
|
-
url: coreOptions.url
|
|
1480
|
-
});
|
|
1330
|
+
preprClientInternals.set(nextClient, { client });
|
|
1481
1331
|
return nextClient;
|
|
1482
1332
|
}
|
|
1483
1333
|
export {
|
|
1484
|
-
|
|
1334
|
+
PREPR_ANY_ITEM_TAG,
|
|
1485
1335
|
PREPR_DEFAULT_VARIANT,
|
|
1486
1336
|
PreprError,
|
|
1487
1337
|
PreprStack,
|
|
@@ -1490,6 +1340,8 @@ export {
|
|
|
1490
1340
|
decisionsFrom,
|
|
1491
1341
|
isPersonalizedResponse,
|
|
1492
1342
|
loadPreprSchema,
|
|
1343
|
+
preprCacheTags,
|
|
1344
|
+
preprModelTag,
|
|
1493
1345
|
preprVariantProps,
|
|
1494
1346
|
resolveVariants,
|
|
1495
1347
|
splitStack,
|