@preprio/toolkit 0.6.0-alpha.0 → 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 +268 -59
- package/dist/astro/{chunk-3UVUDDNY.js → chunk-CG2QBHNU.js} +1 -1
- package/dist/astro/graphql.cjs +154 -59
- package/dist/astro/graphql.d.cts +75 -6
- package/dist/astro/graphql.d.ts +75 -6
- package/dist/astro/graphql.js +150 -59
- package/dist/astro/index.cjs +1 -1
- package/dist/astro/index.js +1 -1
- package/dist/chunk-3NCEVJJ3.js +10 -0
- package/dist/{chunk-BJ2LBLWO.js → chunk-LIDUJWWJ.js} +6 -4
- package/dist/chunk-M3RBYJ5J.js +499 -0
- package/dist/{chunk-DGZA7KX7.js → chunk-S5RWVMDJ.js} +0 -24
- package/dist/{chunk-YR7P4IWE.js → chunk-SF6TN4JR.js} +1 -9
- package/dist/chunk-TN6SNAR2.js +26 -0
- package/dist/graphql/codegen.cjs +557 -0
- package/dist/graphql/codegen.d.cts +23 -0
- package/dist/graphql/codegen.d.ts +23 -0
- package/dist/graphql/codegen.js +99 -0
- package/dist/graphql.cjs +185 -59
- package/dist/graphql.d.cts +110 -6
- package/dist/graphql.d.ts +110 -6
- package/dist/graphql.js +91 -430
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +10 -6
- 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 +289 -73
- package/dist/nextjs/graphql.cjs +952 -315
- package/dist/nextjs/graphql.d.cts +225 -83
- package/dist/nextjs/graphql.d.ts +225 -83
- package/dist/nextjs/graphql.js +634 -314
- 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-P6ENNAEE.js → chunk-FMC3DP7N.js} +1 -1
- package/dist/nuxt/graphql.cjs +154 -59
- package/dist/nuxt/graphql.d.cts +75 -6
- package/dist/nuxt/graphql.d.ts +75 -6
- package/dist/nuxt/graphql.js +150 -59
- 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-FSARVMG5.js → chunk-AJPKBAHO.js} +1 -1
- package/dist/sveltekit/graphql.cjs +154 -59
- package/dist/sveltekit/graphql.d.cts +75 -6
- package/dist/sveltekit/graphql.d.ts +75 -6
- package/dist/sveltekit/graphql.js +150 -59
- package/dist/sveltekit/index.cjs +1 -1
- package/dist/sveltekit/index.js +1 -1
- package/package.json +6 -1
- package/dist/nextjs/chunk-U4HXENF7.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,22 +17,109 @@ 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,
|
|
39
|
+
decisionsFrom: () => decisionsFrom,
|
|
28
40
|
isPersonalizedResponse: () => isPersonalizedResponse,
|
|
41
|
+
loadPreprSchema: () => loadPreprSchema,
|
|
42
|
+
preprCacheTags: () => preprCacheTags,
|
|
43
|
+
preprModelTag: () => preprModelTag,
|
|
29
44
|
preprVariantProps: () => preprVariantProps,
|
|
30
45
|
resolveVariants: () => resolveVariants,
|
|
31
|
-
splitStack: () => splitStack
|
|
46
|
+
splitStack: () => splitStack,
|
|
47
|
+
toDecisionQuery: () => toDecisionQuery,
|
|
48
|
+
toVariantsQuery: () => toVariantsQuery
|
|
32
49
|
});
|
|
33
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
|
+
});
|
|
34
123
|
|
|
35
124
|
// src/core/server.ts
|
|
36
125
|
var PreprError = class extends Error {
|
|
@@ -176,7 +265,10 @@ function decisionSelectionSet(set, typeName, path, ctx) {
|
|
|
176
265
|
path,
|
|
177
266
|
ctx
|
|
178
267
|
);
|
|
179
|
-
if (pruned)
|
|
268
|
+
if (!pruned) continue;
|
|
269
|
+
if (fragment.typeCondition.name.value === typeName) {
|
|
270
|
+
selections.push(...pruned.selections);
|
|
271
|
+
} else {
|
|
180
272
|
selections.push(
|
|
181
273
|
inlineFragment(fragment.typeCondition.name.value, [
|
|
182
274
|
...pruned.selections
|
|
@@ -186,13 +278,19 @@ function decisionSelectionSet(set, typeName, path, ctx) {
|
|
|
186
278
|
continue;
|
|
187
279
|
}
|
|
188
280
|
if (selection.kind === "InlineFragment") {
|
|
281
|
+
const condition = selection.typeCondition?.name.value ?? typeName;
|
|
189
282
|
const pruned = decisionSelectionSet(
|
|
190
283
|
selection.selectionSet,
|
|
191
|
-
|
|
284
|
+
condition,
|
|
192
285
|
path,
|
|
193
286
|
ctx
|
|
194
287
|
);
|
|
195
|
-
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
|
+
}
|
|
196
294
|
continue;
|
|
197
295
|
}
|
|
198
296
|
if (!selection.selectionSet) continue;
|
|
@@ -266,20 +364,58 @@ function toDecisionDocument(document2, schema, groupPaths, options) {
|
|
|
266
364
|
const used = /* @__PURE__ */ new Set();
|
|
267
365
|
collectVariables(pruned, used);
|
|
268
366
|
collectVariables(operation.directives, used);
|
|
367
|
+
const shared = sharedContextFragment(schema);
|
|
269
368
|
return {
|
|
270
369
|
kind: "Document",
|
|
271
370
|
definitions: [
|
|
371
|
+
...shared ? [shared.definition] : [],
|
|
272
372
|
{
|
|
273
373
|
...operation,
|
|
274
374
|
name: operation.name && name(`${operation.name.value}Decision`),
|
|
275
375
|
variableDefinitions: (operation.variableDefinitions ?? []).filter(
|
|
276
376
|
(definition) => used.has(definition.variable.name.value)
|
|
277
377
|
),
|
|
278
|
-
selectionSet: pruned
|
|
378
|
+
selectionSet: shared ? shared.apply(pruned) : pruned
|
|
279
379
|
}
|
|
280
380
|
]
|
|
281
381
|
};
|
|
282
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
|
+
}
|
|
283
419
|
|
|
284
420
|
// src/core/variants/probe-document.ts
|
|
285
421
|
function probeSelectionSet(set, fragments) {
|
|
@@ -566,6 +702,63 @@ function toVariantsDocument(document2, schema) {
|
|
|
566
702
|
};
|
|
567
703
|
}
|
|
568
704
|
|
|
705
|
+
// src/core/graphql/document.ts
|
|
706
|
+
var import_printer = require("graphql/language/printer.js");
|
|
707
|
+
var import_stripIgnoredCharacters = require("graphql/utilities/stripIgnoredCharacters.js");
|
|
708
|
+
var printed = /* @__PURE__ */ new WeakMap();
|
|
709
|
+
function isDocumentNode(value) {
|
|
710
|
+
return typeof value === "object" && value !== null && value.kind === "Document" && Array.isArray(value.definitions);
|
|
711
|
+
}
|
|
712
|
+
function invalidDocument(originalError) {
|
|
713
|
+
return new PreprError(
|
|
714
|
+
"Expected a GraphQL query string, TypedDocumentString or DocumentNode",
|
|
715
|
+
"INVALID_DOCUMENT",
|
|
716
|
+
"createPreprClient",
|
|
717
|
+
originalError
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
var minified = /* @__PURE__ */ new Map();
|
|
721
|
+
var MINIFIED_CACHE_LIMIT = 500;
|
|
722
|
+
function minify(query) {
|
|
723
|
+
const cached = minified.get(query);
|
|
724
|
+
if (cached !== void 0) return cached;
|
|
725
|
+
let result = query;
|
|
726
|
+
try {
|
|
727
|
+
result = (0, import_stripIgnoredCharacters.stripIgnoredCharacters)(query);
|
|
728
|
+
} catch {
|
|
729
|
+
}
|
|
730
|
+
if (minified.size < MINIFIED_CACHE_LIMIT) minified.set(query, result);
|
|
731
|
+
return result;
|
|
732
|
+
}
|
|
733
|
+
function resolveQuery(document2) {
|
|
734
|
+
if (typeof document2 === "string") return minify(document2);
|
|
735
|
+
if (document2 instanceof String) return minify(document2.toString());
|
|
736
|
+
if (!isDocumentNode(document2)) throw invalidDocument();
|
|
737
|
+
let query = printed.get(document2);
|
|
738
|
+
if (query === void 0) {
|
|
739
|
+
try {
|
|
740
|
+
query = minify((0, import_printer.print)(document2));
|
|
741
|
+
} catch (error) {
|
|
742
|
+
throw invalidDocument(error instanceof Error ? error : void 0);
|
|
743
|
+
}
|
|
744
|
+
printed.set(document2, query);
|
|
745
|
+
}
|
|
746
|
+
return query;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// src/core/variants/queries.ts
|
|
750
|
+
function toVariantsQuery(document2, schema) {
|
|
751
|
+
return resolveQuery(toVariantsDocument(document2, schema));
|
|
752
|
+
}
|
|
753
|
+
function toDecisionQuery(document2, schema, options = {}) {
|
|
754
|
+
const paths = options.from === void 0 ? void 0 : personalizationPaths(options.from);
|
|
755
|
+
if (paths && paths.size === 0) return null;
|
|
756
|
+
const decision = toDecisionDocument(document2, schema, paths, {
|
|
757
|
+
topLevelOnly: !paths && options.topLevelOnly
|
|
758
|
+
});
|
|
759
|
+
return decision ? resolveQuery(decision) : null;
|
|
760
|
+
}
|
|
761
|
+
|
|
569
762
|
// src/core/graphql/transport.ts
|
|
570
763
|
var import_stega2 = require("@vercel/stega");
|
|
571
764
|
|
|
@@ -625,50 +818,6 @@ function stegaClean(text) {
|
|
|
625
818
|
return (0, import_stega.vercelStegaClean)(text);
|
|
626
819
|
}
|
|
627
820
|
|
|
628
|
-
// src/core/graphql/document.ts
|
|
629
|
-
var import_printer = require("graphql/language/printer.js");
|
|
630
|
-
var import_stripIgnoredCharacters = require("graphql/utilities/stripIgnoredCharacters.js");
|
|
631
|
-
var printed = /* @__PURE__ */ new WeakMap();
|
|
632
|
-
function isDocumentNode(value) {
|
|
633
|
-
return typeof value === "object" && value !== null && value.kind === "Document" && Array.isArray(value.definitions);
|
|
634
|
-
}
|
|
635
|
-
function invalidDocument(originalError) {
|
|
636
|
-
return new PreprError(
|
|
637
|
-
"Expected a GraphQL query string, TypedDocumentString or DocumentNode",
|
|
638
|
-
"INVALID_DOCUMENT",
|
|
639
|
-
"createPreprClient",
|
|
640
|
-
originalError
|
|
641
|
-
);
|
|
642
|
-
}
|
|
643
|
-
var minified = /* @__PURE__ */ new Map();
|
|
644
|
-
var MINIFIED_CACHE_LIMIT = 500;
|
|
645
|
-
function minify(query) {
|
|
646
|
-
const cached = minified.get(query);
|
|
647
|
-
if (cached !== void 0) return cached;
|
|
648
|
-
let result = query;
|
|
649
|
-
try {
|
|
650
|
-
result = (0, import_stripIgnoredCharacters.stripIgnoredCharacters)(query);
|
|
651
|
-
} catch {
|
|
652
|
-
}
|
|
653
|
-
if (minified.size < MINIFIED_CACHE_LIMIT) minified.set(query, result);
|
|
654
|
-
return result;
|
|
655
|
-
}
|
|
656
|
-
function resolveQuery(document2) {
|
|
657
|
-
if (typeof document2 === "string") return minify(document2);
|
|
658
|
-
if (document2 instanceof String) return minify(document2.toString());
|
|
659
|
-
if (!isDocumentNode(document2)) throw invalidDocument();
|
|
660
|
-
let query = printed.get(document2);
|
|
661
|
-
if (query === void 0) {
|
|
662
|
-
try {
|
|
663
|
-
query = minify((0, import_printer.print)(document2));
|
|
664
|
-
} catch (error) {
|
|
665
|
-
throw invalidDocument(error instanceof Error ? error : void 0);
|
|
666
|
-
}
|
|
667
|
-
printed.set(document2, query);
|
|
668
|
-
}
|
|
669
|
-
return query;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
821
|
// src/core/graphql/transport.ts
|
|
673
822
|
var OPERATION_NAME = /\b(?:query|mutation|subscription)\s+([_A-Za-z][_0-9A-Za-z]*)/;
|
|
674
823
|
var STEGA_RAW = new RegExp(
|
|
@@ -847,6 +996,7 @@ function createVaryProbe(run, options) {
|
|
|
847
996
|
}
|
|
848
997
|
|
|
849
998
|
// src/core/graphql/client.ts
|
|
999
|
+
var clientSchemas = /* @__PURE__ */ new WeakMap();
|
|
850
1000
|
function isParseableUrl(url) {
|
|
851
1001
|
try {
|
|
852
1002
|
new URL(url);
|
|
@@ -855,16 +1005,18 @@ function isParseableUrl(url) {
|
|
|
855
1005
|
return false;
|
|
856
1006
|
}
|
|
857
1007
|
}
|
|
858
|
-
function
|
|
859
|
-
const { url } = options;
|
|
1008
|
+
function assertPreprUrl(url, source) {
|
|
860
1009
|
validatePreprToken(url);
|
|
861
1010
|
if (!isParseableUrl(url)) {
|
|
862
1011
|
throw new PreprError(
|
|
863
1012
|
"Prepr GraphQL URL is not a valid URL",
|
|
864
1013
|
"INVALID_TOKEN",
|
|
865
|
-
|
|
1014
|
+
source
|
|
866
1015
|
);
|
|
867
1016
|
}
|
|
1017
|
+
}
|
|
1018
|
+
function createPreprClient(options) {
|
|
1019
|
+
assertPreprUrl(options.url, "createPreprClient");
|
|
868
1020
|
const run = createTransport(options);
|
|
869
1021
|
const probeVary = createVaryProbe(run, options);
|
|
870
1022
|
let schema;
|
|
@@ -881,7 +1033,7 @@ function createPreprClient(options) {
|
|
|
881
1033
|
));
|
|
882
1034
|
const variantsDocuments = /* @__PURE__ */ new Map();
|
|
883
1035
|
const decisionDocuments = /* @__PURE__ */ new Map();
|
|
884
|
-
|
|
1036
|
+
const client = {
|
|
885
1037
|
async query(document2, variables, queryOptions = {}) {
|
|
886
1038
|
const { variants, ...rest } = queryOptions;
|
|
887
1039
|
if (!variants) return run(document2, variables, rest);
|
|
@@ -893,10 +1045,10 @@ function createPreprClient(options) {
|
|
|
893
1045
|
return run(derived, variables, rest);
|
|
894
1046
|
},
|
|
895
1047
|
async decide(document2, variables, queryOptions = {}) {
|
|
896
|
-
const { from,
|
|
1048
|
+
const { from, ...rest } = queryOptions;
|
|
897
1049
|
const paths = from === void 0 ? void 0 : personalizationPaths(from);
|
|
898
1050
|
if (paths && paths.size === 0) return /* @__PURE__ */ new Map();
|
|
899
|
-
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("|") : "*";
|
|
900
1052
|
let byPaths = decisionDocuments.get(document2);
|
|
901
1053
|
if (!byPaths) {
|
|
902
1054
|
byPaths = /* @__PURE__ */ new Map();
|
|
@@ -904,9 +1056,7 @@ function createPreprClient(options) {
|
|
|
904
1056
|
}
|
|
905
1057
|
let derived = byPaths.get(signature);
|
|
906
1058
|
if (derived === void 0) {
|
|
907
|
-
derived = toDecisionDocument(document2, await loadSchema(), paths
|
|
908
|
-
topLevelOnly: !paths && topLevelOnly
|
|
909
|
-
});
|
|
1059
|
+
derived = toDecisionDocument(document2, await loadSchema(), paths);
|
|
910
1060
|
byPaths.set(signature, derived);
|
|
911
1061
|
}
|
|
912
1062
|
if (!derived) return /* @__PURE__ */ new Map();
|
|
@@ -920,28 +1070,91 @@ function createPreprClient(options) {
|
|
|
920
1070
|
(vary) => vary === null || SEGMENTS_VARY.test(vary)
|
|
921
1071
|
)
|
|
922
1072
|
};
|
|
1073
|
+
clientSchemas.set(client, loadSchema);
|
|
1074
|
+
return client;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// src/core/graphql/schema-loader.ts
|
|
1078
|
+
var schemas = /* @__PURE__ */ new Map();
|
|
1079
|
+
async function loadPreprSchema(url, options = {}) {
|
|
1080
|
+
assertPreprUrl(url, "loadPreprSchema");
|
|
1081
|
+
let schema = schemas.get(url);
|
|
1082
|
+
if (!schema) {
|
|
1083
|
+
const run = createTransport({ url, fetch: options.fetch, stega: "none" });
|
|
1084
|
+
schema = run(
|
|
1085
|
+
PREPR_SCHEMA_QUERY
|
|
1086
|
+
).then((result) => toSchemaInfo(result.data)).catch((error) => {
|
|
1087
|
+
schemas.delete(url);
|
|
1088
|
+
if (error instanceof PreprError) throw error;
|
|
1089
|
+
throw new PreprError(
|
|
1090
|
+
"Prepr schema response is not an introspection result",
|
|
1091
|
+
"INVALID_RESPONSE",
|
|
1092
|
+
"loadPreprSchema",
|
|
1093
|
+
error instanceof Error ? error : void 0
|
|
1094
|
+
);
|
|
1095
|
+
});
|
|
1096
|
+
schemas.set(url, schema);
|
|
1097
|
+
}
|
|
1098
|
+
return schema;
|
|
923
1099
|
}
|
|
924
1100
|
|
|
925
1101
|
// src/nextjs/decisions.ts
|
|
1102
|
+
var PREPR_DECIDE_HEADER = "Prepr-Decide";
|
|
1103
|
+
var DEFAULT_DECISION = "*";
|
|
926
1104
|
var PREPR_DECISIONS_COOKIE = "prepr_decisions";
|
|
927
|
-
var
|
|
928
|
-
var
|
|
929
|
-
var
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
)
|
|
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 {
|
|
940
1123
|
}
|
|
941
|
-
return
|
|
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;
|
|
942
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));
|
|
1149
|
+
}
|
|
1150
|
+
return json;
|
|
1151
|
+
}
|
|
1152
|
+
var DECISIONS_MAX_GROUPS = 40;
|
|
1153
|
+
var SAFE_ID = /^[\w-]+$/;
|
|
943
1154
|
function serializeDecisions(decisions) {
|
|
944
|
-
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("~");
|
|
945
1158
|
}
|
|
946
1159
|
function forwardedHeaders(request, response) {
|
|
947
1160
|
const overridden = response.headers.get("x-middleware-override-headers");
|
|
@@ -953,60 +1166,17 @@ function forwardedHeaders(request, response) {
|
|
|
953
1166
|
}
|
|
954
1167
|
return getPreprHeadersFromHeaders(headers2);
|
|
955
1168
|
}
|
|
956
|
-
|
|
1169
|
+
var preprClientInternals = /* @__PURE__ */ new WeakMap();
|
|
1170
|
+
async function decideView(client, headers2, page, timeoutMs = 1e3) {
|
|
1171
|
+
const init = { signal: AbortSignal.timeout(timeoutMs) };
|
|
957
1172
|
try {
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
request.cookies.get(PREPR_DECISIONS_COOKIE)?.value
|
|
963
|
-
);
|
|
964
|
-
const pageKey = `${JSON.stringify(variables ?? null)}`;
|
|
965
|
-
let byVariables = seenGroups.get(document2);
|
|
966
|
-
if (!byVariables) {
|
|
967
|
-
byVariables = /* @__PURE__ */ new Map();
|
|
968
|
-
seenGroups.set(document2, byVariables);
|
|
969
|
-
}
|
|
970
|
-
const known = byVariables.get(pageKey);
|
|
971
|
-
const isNewVisitor = headers2.get("Prepr-Customer-Id-Created") === "true";
|
|
972
|
-
const contextChanged = isNewVisitor || headers2.has("Prepr-Segments") || [...request.nextUrl.searchParams.keys()].some(
|
|
973
|
-
(key) => key.startsWith("utm_")
|
|
974
|
-
);
|
|
975
|
-
if (options?.reuse !== false && !contextChanged && known && [...known].every((group) => stored.has(group))) {
|
|
976
|
-
return;
|
|
977
|
-
}
|
|
978
|
-
const decided = await client.decide(document2, variables, {
|
|
979
|
-
topLevelOnly: true,
|
|
980
|
-
headers: headers2,
|
|
981
|
-
init: { signal: AbortSignal.timeout(options?.timeoutMs ?? 1e3) }
|
|
982
|
-
});
|
|
983
|
-
if (!known && byVariables.size >= SEEN_MAX_PAGES) {
|
|
984
|
-
byVariables.delete(byVariables.keys().next().value);
|
|
985
|
-
}
|
|
986
|
-
const groups = known ?? /* @__PURE__ */ new Set();
|
|
987
|
-
for (const group of decided.keys()) groups.add(group);
|
|
988
|
-
byVariables.set(pageKey, groups);
|
|
989
|
-
const next = /* @__PURE__ */ new Map();
|
|
990
|
-
for (const [group, variants] of decided) {
|
|
991
|
-
if (SAFE_ID.test(group)) {
|
|
992
|
-
next.set(
|
|
993
|
-
group,
|
|
994
|
-
variants.filter((v) => SAFE_ID.test(v))
|
|
995
|
-
);
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
for (const [group, variants] of stored) {
|
|
999
|
-
if (!next.has(group)) next.set(group, variants);
|
|
1000
|
-
}
|
|
1001
|
-
if (next.size === 0) return;
|
|
1002
|
-
response.cookies.set(PREPR_DECISIONS_COOKIE, serializeDecisions(next), {
|
|
1003
|
-
path: "/",
|
|
1004
|
-
sameSite: "lax",
|
|
1005
|
-
// Read by the inline switch script before first paint.
|
|
1006
|
-
httpOnly: false
|
|
1007
|
-
});
|
|
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);
|
|
1008
1177
|
} catch (error) {
|
|
1009
1178
|
if (isFrameworkSignal(error)) throw error;
|
|
1179
|
+
return DEFAULT_DECISION;
|
|
1010
1180
|
}
|
|
1011
1181
|
}
|
|
1012
1182
|
|
|
@@ -1020,227 +1190,668 @@ async function getPreprHeaders() {
|
|
|
1020
1190
|
return getPreprHeadersFromHeaders(h);
|
|
1021
1191
|
}
|
|
1022
1192
|
|
|
1023
|
-
// src/nextjs/
|
|
1024
|
-
var
|
|
1193
|
+
// src/nextjs/proxy.ts
|
|
1194
|
+
var import_server9 = require("next/server");
|
|
1025
1195
|
|
|
1026
|
-
// src/nextjs/
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
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);
|
|
1039
1261
|
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
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));
|
|
1045
1287
|
}
|
|
1046
|
-
state.__preprAbSheet.textContent = css;
|
|
1047
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
|
+
});
|
|
1381
|
+
}
|
|
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 };
|
|
1048
1388
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
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;
|
|
1056
1399
|
}
|
|
1057
|
-
const
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
const
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
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:"
|
|
1071
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
|
|
1072
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
|
+
});
|
|
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", "");
|
|
1073
1576
|
});
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
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 {
|
|
1079
1615
|
}
|
|
1080
|
-
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
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();
|
|
1085
1645
|
}
|
|
1086
|
-
|
|
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
|
+
});
|
|
1665
|
+
}
|
|
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) };
|
|
1087
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));
|
|
1088
1700
|
}
|
|
1089
1701
|
|
|
1090
1702
|
// src/nextjs/stack.ts
|
|
1091
1703
|
var import_components = require("./components.cjs");
|
|
1092
|
-
var
|
|
1093
|
-
var AB_BUCKETS = ["A", "B"];
|
|
1094
|
-
var AB_BASE_CSS = `[${AB_ATTRIBUTE}]{display:contents}[${AB_ATTRIBUTE}]:not([${AB_ATTRIBUTE}="A"]){display:none}`;
|
|
1095
|
-
var AB_SWITCH_SCRIPT = `(${applyPreprAbBucket.toString()})();(${applyPreprDecisions.toString()})()`;
|
|
1096
|
-
var GROUP_ATTRIBUTE = "data-prepr-group";
|
|
1097
|
-
var VARIANT_ATTRIBUTE = "data-prepr-variant";
|
|
1704
|
+
var VARIANT_ATTRIBUTE = "data-prepr-v";
|
|
1098
1705
|
var DEFAULT_ATTRIBUTE = "data-prepr-default";
|
|
1099
|
-
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()";
|
|
1100
1711
|
function variantKeysOf(items) {
|
|
1101
1712
|
return items.flatMap((item) => {
|
|
1102
|
-
const key = preprVariantProps(item)["data-prepr-variant-key"];
|
|
1713
|
+
const key = preprVariantProps(item)["data-prepr-variant-key"] ?? nestedKeyOf(item);
|
|
1103
1714
|
return key ? [key] : [];
|
|
1104
1715
|
});
|
|
1105
1716
|
}
|
|
1106
|
-
function
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
placeholder = false
|
|
1114
|
-
} = options;
|
|
1115
|
-
const renderVariant = (variant, key) => {
|
|
1116
|
-
const resolved = resolveVariants(items, { decisions, bucket: variant });
|
|
1117
|
-
const content = render(resolved, { index, placeholder });
|
|
1118
|
-
const variantKeys = track ? variantKeysOf(resolved) : [];
|
|
1119
|
-
const ab = abTest && !bucket ? variant : void 0;
|
|
1120
|
-
if (variantKeys.length > 0) {
|
|
1121
|
-
return (0, import_react.createElement)(
|
|
1122
|
-
import_components.PreprVariantTracking,
|
|
1123
|
-
{ key, variantKeys, ab },
|
|
1124
|
-
content
|
|
1125
|
-
);
|
|
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;
|
|
1126
1724
|
}
|
|
1127
|
-
|
|
1128
|
-
|
|
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);
|
|
1754
|
+
}
|
|
1755
|
+
Object.values(value).forEach(walk);
|
|
1129
1756
|
};
|
|
1130
|
-
|
|
1131
|
-
return
|
|
1132
|
-
}
|
|
1133
|
-
function
|
|
1134
|
-
const
|
|
1135
|
-
const
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
)
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
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)(
|
|
1145
1777
|
"div",
|
|
1146
1778
|
{
|
|
1147
|
-
key:
|
|
1148
|
-
[
|
|
1149
|
-
[
|
|
1150
|
-
...
|
|
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
|
|
1151
1785
|
},
|
|
1152
|
-
|
|
1786
|
+
renderItems(block.items, render, {
|
|
1153
1787
|
bucket: options.bucket,
|
|
1154
|
-
decisions:
|
|
1155
|
-
abTest: false,
|
|
1788
|
+
decisions: new Map(combination.map(([g, v]) => [g, [v]])),
|
|
1156
1789
|
index: block.index,
|
|
1157
|
-
track: options.track
|
|
1790
|
+
track: options.track,
|
|
1791
|
+
defaultVariant: isDefault
|
|
1158
1792
|
})
|
|
1159
|
-
)
|
|
1160
|
-
);
|
|
1161
|
-
}
|
|
1162
|
-
async function PersonalizedBlock({
|
|
1163
|
-
items,
|
|
1164
|
-
decisions,
|
|
1165
|
-
bucket,
|
|
1166
|
-
abTest,
|
|
1167
|
-
index,
|
|
1168
|
-
render,
|
|
1169
|
-
track
|
|
1170
|
-
}) {
|
|
1171
|
-
const decided = await decisions.catch((error) => {
|
|
1172
|
-
if (isFrameworkSignal(error)) throw error;
|
|
1173
|
-
return void 0;
|
|
1174
|
-
});
|
|
1175
|
-
return renderBuckets(items, render, {
|
|
1176
|
-
bucket,
|
|
1177
|
-
decisions: decided,
|
|
1178
|
-
abTest,
|
|
1179
|
-
index,
|
|
1180
|
-
track
|
|
1793
|
+
);
|
|
1181
1794
|
});
|
|
1182
1795
|
}
|
|
1183
1796
|
function PreprStack({
|
|
1184
1797
|
items,
|
|
1185
|
-
decisions,
|
|
1186
1798
|
render,
|
|
1187
1799
|
bucket,
|
|
1188
1800
|
nonce,
|
|
1189
|
-
trackVariants = false
|
|
1190
|
-
aboveTheFold = 0
|
|
1801
|
+
trackVariants = false
|
|
1191
1802
|
}) {
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
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);
|
|
1197
1808
|
return (0, import_react.createElement)(
|
|
1198
1809
|
import_react.Fragment,
|
|
1199
1810
|
null,
|
|
1200
|
-
|
|
1811
|
+
varies && (0, import_react.createElement)(
|
|
1201
1812
|
import_react.Fragment,
|
|
1202
|
-
{ key: "prepr-
|
|
1813
|
+
{ key: "prepr-switch" },
|
|
1203
1814
|
(0, import_react.createElement)("style", {
|
|
1204
1815
|
nonce,
|
|
1205
|
-
dangerouslySetInnerHTML: {
|
|
1206
|
-
|
|
1207
|
-
|
|
1816
|
+
dangerouslySetInnerHTML: { __html: GROUP_BASE_CSS }
|
|
1817
|
+
}),
|
|
1818
|
+
(0, import_react.createElement)("noscript", {
|
|
1819
|
+
dangerouslySetInnerHTML: { __html: NOSCRIPT_HTML }
|
|
1208
1820
|
}),
|
|
1209
1821
|
(0, import_react.createElement)("script", {
|
|
1210
1822
|
nonce,
|
|
1211
|
-
dangerouslySetInnerHTML: { __html:
|
|
1823
|
+
dangerouslySetInnerHTML: { __html: DECISION_SCRIPT }
|
|
1212
1824
|
}),
|
|
1213
|
-
(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
|
+
})
|
|
1214
1831
|
),
|
|
1215
1832
|
...blocks.map((block) => {
|
|
1216
|
-
if (
|
|
1833
|
+
if (block.groups.size === 0) {
|
|
1217
1834
|
return (0, import_react.createElement)(
|
|
1218
1835
|
import_react.Fragment,
|
|
1219
1836
|
{ key: block.key },
|
|
1220
|
-
|
|
1837
|
+
renderItems(block.items, render, {
|
|
1838
|
+
bucket,
|
|
1839
|
+
index: block.index,
|
|
1840
|
+
track: trackVariants,
|
|
1841
|
+
defaultVariant: true
|
|
1842
|
+
})
|
|
1221
1843
|
);
|
|
1222
1844
|
}
|
|
1223
|
-
const fallback = renderBuckets(block.items, render, {
|
|
1224
|
-
bucket,
|
|
1225
|
-
abTest: block.abTest,
|
|
1226
|
-
index: block.index,
|
|
1227
|
-
track: trackVariants && !block.personalized,
|
|
1228
|
-
placeholder: block.personalized
|
|
1229
|
-
});
|
|
1230
|
-
if (!block.personalized) {
|
|
1231
|
-
return (0, import_react.createElement)(import_react.Fragment, { key: block.key }, fallback);
|
|
1232
|
-
}
|
|
1233
1845
|
return (0, import_react.createElement)(
|
|
1234
|
-
import_react.
|
|
1235
|
-
{ key: block.key
|
|
1236
|
-
(
|
|
1237
|
-
items: block.items,
|
|
1238
|
-
decisions,
|
|
1846
|
+
import_react.Fragment,
|
|
1847
|
+
{ key: block.key },
|
|
1848
|
+
renderVariants(block, block.groups, render, {
|
|
1239
1849
|
bucket,
|
|
1240
|
-
abTest: block.abTest,
|
|
1241
|
-
index: block.index,
|
|
1242
|
-
render,
|
|
1243
1850
|
track: trackVariants
|
|
1851
|
+
}),
|
|
1852
|
+
(0, import_react.createElement)("script", {
|
|
1853
|
+
nonce,
|
|
1854
|
+
dangerouslySetInnerHTML: { __html: APPLY_SCRIPT }
|
|
1244
1855
|
})
|
|
1245
1856
|
);
|
|
1246
1857
|
})
|
|
@@ -1252,33 +1863,59 @@ function createPreprClient2(options) {
|
|
|
1252
1863
|
const { cacheTags = ["prepr"], revalidate, ...coreOptions } = options;
|
|
1253
1864
|
const client = createPreprClient({
|
|
1254
1865
|
// The schema introspection goes through the Data Cache: shared across
|
|
1255
|
-
// server instances, refreshed hourly and by
|
|
1866
|
+
// server instances, refreshed hourly and by a `schema.changed` webhook.
|
|
1256
1867
|
schemaInit: {
|
|
1257
|
-
next: { tags: cacheTags, revalidate: 3600 }
|
|
1868
|
+
next: { tags: [...cacheTags, PREPR_SCHEMA_TAG], revalidate: 3600 }
|
|
1258
1869
|
},
|
|
1259
1870
|
...coreOptions
|
|
1260
1871
|
});
|
|
1261
|
-
const
|
|
1262
|
-
return {
|
|
1872
|
+
const nextClient = {
|
|
1263
1873
|
async query(document2, variables, queryOptions = {}) {
|
|
1264
|
-
const { personalize = false, ...rest } = queryOptions;
|
|
1265
|
-
const init = rest.init;
|
|
1266
|
-
|
|
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 = {
|
|
1267
1880
|
...rest,
|
|
1268
|
-
|
|
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" } : {
|
|
1269
1887
|
...init,
|
|
1270
1888
|
next: {
|
|
1271
|
-
tags: cacheTags,
|
|
1272
1889
|
...revalidate === void 0 ? {} : { revalidate },
|
|
1273
|
-
...
|
|
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 ?? []]
|
|
1274
1894
|
}
|
|
1275
1895
|
}
|
|
1276
1896
|
};
|
|
1277
|
-
if (
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
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;
|
|
1282
1919
|
},
|
|
1283
1920
|
async decide(document2, variables, queryOptions = {}) {
|
|
1284
1921
|
return client.decide(document2, variables, {
|
|
@@ -1289,27 +1926,27 @@ function createPreprClient2(options) {
|
|
|
1289
1926
|
)
|
|
1290
1927
|
});
|
|
1291
1928
|
},
|
|
1292
|
-
hasAbTests: (document2, variables) => client.hasAbTests(document2, variables)
|
|
1293
|
-
resolveDecisions: (request, response, document2, variables, options2) => resolveDecisionsWith(
|
|
1294
|
-
client,
|
|
1295
|
-
seenGroups,
|
|
1296
|
-
request,
|
|
1297
|
-
response,
|
|
1298
|
-
document2,
|
|
1299
|
-
variables,
|
|
1300
|
-
options2
|
|
1301
|
-
)
|
|
1929
|
+
hasAbTests: (document2, variables) => client.hasAbTests(document2, variables)
|
|
1302
1930
|
};
|
|
1931
|
+
preprClientInternals.set(nextClient, { client });
|
|
1932
|
+
return nextClient;
|
|
1303
1933
|
}
|
|
1304
1934
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1305
1935
|
0 && (module.exports = {
|
|
1306
|
-
|
|
1936
|
+
PREPR_ANY_ITEM_TAG,
|
|
1307
1937
|
PREPR_DEFAULT_VARIANT,
|
|
1308
1938
|
PreprError,
|
|
1309
1939
|
PreprStack,
|
|
1310
1940
|
createPreprClient,
|
|
1941
|
+
createPreprProxy,
|
|
1942
|
+
decisionsFrom,
|
|
1311
1943
|
isPersonalizedResponse,
|
|
1944
|
+
loadPreprSchema,
|
|
1945
|
+
preprCacheTags,
|
|
1946
|
+
preprModelTag,
|
|
1312
1947
|
preprVariantProps,
|
|
1313
1948
|
resolveVariants,
|
|
1314
|
-
splitStack
|
|
1949
|
+
splitStack,
|
|
1950
|
+
toDecisionQuery,
|
|
1951
|
+
toVariantsQuery
|
|
1315
1952
|
});
|