@preprio/toolkit 0.6.0-alpha.1 → 0.6.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +163 -61
- package/dist/astro/{chunk-O7N6GPAH.js → chunk-CG2QBHNU.js} +1 -1
- package/dist/astro/graphql.cjs +58 -10
- package/dist/astro/graphql.d.cts +33 -38
- package/dist/astro/graphql.d.ts +33 -38
- package/dist/astro/graphql.js +59 -11
- package/dist/astro/index.cjs +1 -1
- package/dist/astro/index.js +1 -1
- package/dist/{chunk-VI6QZTBM.js → chunk-LIDUJWWJ.js} +1 -1
- package/dist/{chunk-TBKSBXGH.js → chunk-M3RBYJ5J.js} +52 -5
- package/dist/{chunk-3JLWZQ37.js → chunk-SF6TN4JR.js} +1 -1
- package/dist/{chunk-YWIKKWBR.js → chunk-TN6SNAR2.js} +1 -1
- package/dist/graphql/codegen.cjs +51 -4
- package/dist/graphql/codegen.js +3 -3
- package/dist/graphql.cjs +89 -10
- package/dist/graphql.d.cts +69 -39
- package/dist/graphql.d.ts +69 -39
- package/dist/graphql.js +38 -9
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/nextjs/chunk-B4GVZEOL.js +549 -0
- package/dist/nextjs/components.cjs +297 -81
- package/dist/nextjs/components.d.cts +26 -11
- package/dist/nextjs/components.d.ts +26 -11
- package/dist/nextjs/components.js +288 -73
- package/dist/nextjs/graphql.cjs +856 -266
- package/dist/nextjs/graphql.d.cts +184 -116
- package/dist/nextjs/graphql.d.ts +184 -116
- package/dist/nextjs/graphql.js +543 -266
- package/dist/nextjs/index.cjs +116 -54
- package/dist/nextjs/index.d.cts +61 -23
- package/dist/nextjs/index.d.ts +61 -23
- package/dist/nextjs/index.js +97 -299
- package/dist/nextjs/{server-CjpELYvq.d.ts → server-BL-sqZvu.d.cts} +1 -1
- package/dist/nextjs/{server-CjpELYvq.d.cts → server-BL-sqZvu.d.ts} +1 -1
- package/dist/nuxt/{chunk-IQDSBPDN.js → chunk-FMC3DP7N.js} +1 -1
- package/dist/nuxt/graphql.cjs +58 -10
- package/dist/nuxt/graphql.d.cts +33 -38
- package/dist/nuxt/graphql.d.ts +33 -38
- package/dist/nuxt/graphql.js +59 -11
- package/dist/nuxt/index.cjs +1 -1
- package/dist/nuxt/index.js +1 -1
- package/dist/react/components.cjs +1 -1
- package/dist/react/components.js +1 -1
- package/dist/sveltekit/{chunk-OKYZ6HPH.js → chunk-AJPKBAHO.js} +1 -1
- package/dist/sveltekit/graphql.cjs +58 -10
- package/dist/sveltekit/graphql.d.cts +33 -38
- package/dist/sveltekit/graphql.d.ts +33 -38
- package/dist/sveltekit/graphql.js +59 -11
- package/dist/sveltekit/index.cjs +1 -1
- package/dist/sveltekit/index.js +1 -1
- package/package.json +1 -1
- package/dist/nextjs/chunk-BQ6IGSH6.js +0 -231
|
@@ -95,39 +95,6 @@ interface PreprVariantProps {
|
|
|
95
95
|
*/
|
|
96
96
|
declare function preprVariantProps(item: unknown): PreprVariantProps;
|
|
97
97
|
|
|
98
|
-
/**
|
|
99
|
-
* The schema facts the query rewrites need, from `loadPreprSchema`. Pass it
|
|
100
|
-
* along; its shape is not part of the public API.
|
|
101
|
-
*/
|
|
102
|
-
type PreprSchema = PreprSchemaInfo;
|
|
103
|
-
/**
|
|
104
|
-
* Your query, asking for every personalization and A/B variant: what
|
|
105
|
-
* `query(..., { variants: true })` sends, as a string for your own GraphQL
|
|
106
|
-
* client. The result is the same for every visitor, so cache it like any
|
|
107
|
-
* other content query, and render it with `resolveVariants` or `PreprStack`.
|
|
108
|
-
*
|
|
109
|
-
* A query without personalizable fields comes back unchanged (minified).
|
|
110
|
-
*/
|
|
111
|
-
declare function toVariantsQuery(document: PreprDocument<unknown, unknown> | string, schema: PreprSchema): string;
|
|
112
|
-
/**
|
|
113
|
-
* The per-visitor decision query for the same query: send it with the
|
|
114
|
-
* visitor's Prepr headers, never cached, and pass the response's `data` to
|
|
115
|
-
* `decisionsFrom`.
|
|
116
|
-
*
|
|
117
|
-
* Pass the variants result as `from` whenever you have it: the query then
|
|
118
|
-
* asks only about the lists that hold segment groups on this page, which is
|
|
119
|
-
* a few hundred bytes instead of the whole page and markedly faster for a
|
|
120
|
-
* visitor Prepr has not seen before. Without `from` it asks about every
|
|
121
|
-
* personalizable list. `topLevelOnly` stops at the first personalizable list
|
|
122
|
-
* on each path, for middleware that decides only the top of a stack.
|
|
123
|
-
*
|
|
124
|
-
* Null when there is nothing to decide: send no request then.
|
|
125
|
-
*/
|
|
126
|
-
declare function toDecisionQuery(document: PreprDocument<unknown, unknown> | string, schema: PreprSchema, options?: {
|
|
127
|
-
from?: unknown;
|
|
128
|
-
topLevelOnly?: boolean;
|
|
129
|
-
}): string | null;
|
|
130
|
-
|
|
131
98
|
/**
|
|
132
99
|
* A GraphQL document accepted by `query`: a plain string, graphql-codegen's
|
|
133
100
|
* `TypedDocumentString` (`documentMode: 'string'`), or any `DocumentNode`,
|
|
@@ -221,11 +188,6 @@ interface PreprDecideOptions extends Omit<PreprQueryOptions, 'variants'> {
|
|
|
221
188
|
* none, e.g. on pages with only A/B tests or no personalization.
|
|
222
189
|
*/
|
|
223
190
|
from?: unknown;
|
|
224
|
-
/**
|
|
225
|
-
* @internal Ask only about the first personalizable list on each path,
|
|
226
|
-
* without `from`: what middleware needs for groups at the top of a stack.
|
|
227
|
-
*/
|
|
228
|
-
topLevelOnly?: boolean;
|
|
229
191
|
}
|
|
230
192
|
interface PreprQueryResult<TResult> {
|
|
231
193
|
data: TResult;
|
|
@@ -236,6 +198,39 @@ interface PreprQueryResult<TResult> {
|
|
|
236
198
|
personalized: boolean;
|
|
237
199
|
}
|
|
238
200
|
|
|
201
|
+
/**
|
|
202
|
+
* The schema facts the query rewrites need, from `loadPreprSchema`. Pass it
|
|
203
|
+
* along; its shape is not part of the public API.
|
|
204
|
+
*/
|
|
205
|
+
type PreprSchema = PreprSchemaInfo;
|
|
206
|
+
/**
|
|
207
|
+
* Your query, asking for every personalization and A/B variant: what
|
|
208
|
+
* `query(..., { variants: true })` sends, as a string for your own GraphQL
|
|
209
|
+
* client. The result is the same for every visitor, so cache it like any
|
|
210
|
+
* other content query, and render it with `resolveVariants` or `PreprStack`.
|
|
211
|
+
*
|
|
212
|
+
* A query without personalizable fields comes back unchanged (minified).
|
|
213
|
+
*/
|
|
214
|
+
declare function toVariantsQuery(document: PreprDocument<unknown, unknown> | string, schema: PreprSchema): string;
|
|
215
|
+
/**
|
|
216
|
+
* The per-visitor decision query for the same query: send it with the
|
|
217
|
+
* visitor's Prepr headers, never cached, and pass the response's `data` to
|
|
218
|
+
* `decisionsFrom`.
|
|
219
|
+
*
|
|
220
|
+
* Pass the variants result as `from` whenever you have it: the query then
|
|
221
|
+
* asks only about the lists that hold segment groups on this page, which is
|
|
222
|
+
* a few hundred bytes instead of the whole page and markedly faster for a
|
|
223
|
+
* visitor Prepr has not seen before. Without `from` it asks about every
|
|
224
|
+
* personalizable list. `topLevelOnly` stops at the first personalizable list
|
|
225
|
+
* on each path, for middleware that decides only the top of a stack.
|
|
226
|
+
*
|
|
227
|
+
* Null when there is nothing to decide: send no request then.
|
|
228
|
+
*/
|
|
229
|
+
declare function toDecisionQuery(document: PreprDocument<unknown, unknown> | string, schema: PreprSchema, options?: {
|
|
230
|
+
from?: unknown;
|
|
231
|
+
topLevelOnly?: boolean;
|
|
232
|
+
}): string | null;
|
|
233
|
+
|
|
239
234
|
/**
|
|
240
235
|
* Query options for frameworks without an implicit request scope.
|
|
241
236
|
* `personalize: true` requires the request `event`; the compiler enforces it.
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
getPreprHeadersFromLocals,
|
|
5
5
|
isPersonalizedResponse,
|
|
6
6
|
validatePreprToken
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-AJPKBAHO.js";
|
|
8
8
|
|
|
9
9
|
// src/core/variants/constants.ts
|
|
10
10
|
var PREPR_CONTEXT_ALIAS = "_preprContext";
|
|
@@ -93,7 +93,10 @@ function decisionSelectionSet(set, typeName, path, ctx) {
|
|
|
93
93
|
path,
|
|
94
94
|
ctx
|
|
95
95
|
);
|
|
96
|
-
if (pruned)
|
|
96
|
+
if (!pruned) continue;
|
|
97
|
+
if (fragment.typeCondition.name.value === typeName) {
|
|
98
|
+
selections.push(...pruned.selections);
|
|
99
|
+
} else {
|
|
97
100
|
selections.push(
|
|
98
101
|
inlineFragment(fragment.typeCondition.name.value, [
|
|
99
102
|
...pruned.selections
|
|
@@ -103,13 +106,19 @@ function decisionSelectionSet(set, typeName, path, ctx) {
|
|
|
103
106
|
continue;
|
|
104
107
|
}
|
|
105
108
|
if (selection.kind === "InlineFragment") {
|
|
109
|
+
const condition = selection.typeCondition?.name.value ?? typeName;
|
|
106
110
|
const pruned = decisionSelectionSet(
|
|
107
111
|
selection.selectionSet,
|
|
108
|
-
|
|
112
|
+
condition,
|
|
109
113
|
path,
|
|
110
114
|
ctx
|
|
111
115
|
);
|
|
112
|
-
if (pruned)
|
|
116
|
+
if (!pruned) continue;
|
|
117
|
+
if (condition === typeName && !selection.directives?.length) {
|
|
118
|
+
selections.push(...pruned.selections);
|
|
119
|
+
} else {
|
|
120
|
+
selections.push({ ...selection, selectionSet: pruned });
|
|
121
|
+
}
|
|
113
122
|
continue;
|
|
114
123
|
}
|
|
115
124
|
if (!selection.selectionSet) continue;
|
|
@@ -183,20 +192,58 @@ function toDecisionDocument(document2, schema, groupPaths, options) {
|
|
|
183
192
|
const used = /* @__PURE__ */ new Set();
|
|
184
193
|
collectVariables(pruned, used);
|
|
185
194
|
collectVariables(operation.directives, used);
|
|
195
|
+
const shared = sharedContextFragment(schema);
|
|
186
196
|
return {
|
|
187
197
|
kind: "Document",
|
|
188
198
|
definitions: [
|
|
199
|
+
...shared ? [shared.definition] : [],
|
|
189
200
|
{
|
|
190
201
|
...operation,
|
|
191
202
|
name: operation.name && name(`${operation.name.value}Decision`),
|
|
192
203
|
variableDefinitions: (operation.variableDefinitions ?? []).filter(
|
|
193
204
|
(definition) => used.has(definition.variable.name.value)
|
|
194
205
|
),
|
|
195
|
-
selectionSet: pruned
|
|
206
|
+
selectionSet: shared ? shared.apply(pruned) : pruned
|
|
196
207
|
}
|
|
197
208
|
]
|
|
198
209
|
};
|
|
199
210
|
}
|
|
211
|
+
function sharedContextFragment(schema) {
|
|
212
|
+
const contextTypes = new Set(
|
|
213
|
+
Object.values(schema.types).flatMap(
|
|
214
|
+
(type) => type.hasContext ? [type.fields._context.type] : []
|
|
215
|
+
)
|
|
216
|
+
);
|
|
217
|
+
if (contextTypes.size !== 1) return null;
|
|
218
|
+
const [contextType] = contextTypes;
|
|
219
|
+
const fragmentName = "c";
|
|
220
|
+
const definition = {
|
|
221
|
+
kind: "FragmentDefinition",
|
|
222
|
+
name: name(fragmentName),
|
|
223
|
+
typeCondition: { kind: "NamedType", name: name(contextType) },
|
|
224
|
+
selectionSet: selectionSet(
|
|
225
|
+
["kind", "group_id", "variant_id"].map(
|
|
226
|
+
(field) => ({ kind: "Field", name: name(field) })
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
};
|
|
230
|
+
const spread = {
|
|
231
|
+
kind: "FragmentSpread",
|
|
232
|
+
name: name(fragmentName)
|
|
233
|
+
};
|
|
234
|
+
const apply = (set) => selectionSet(
|
|
235
|
+
set.selections.map((selection) => {
|
|
236
|
+
if (selection.kind === "Field" && selection.alias?.value === PREPR_CONTEXT_ALIAS) {
|
|
237
|
+
return { ...selection, selectionSet: selectionSet([spread]) };
|
|
238
|
+
}
|
|
239
|
+
return "selectionSet" in selection && selection.selectionSet ? {
|
|
240
|
+
...selection,
|
|
241
|
+
selectionSet: apply(selection.selectionSet)
|
|
242
|
+
} : selection;
|
|
243
|
+
})
|
|
244
|
+
);
|
|
245
|
+
return { definition, apply };
|
|
246
|
+
}
|
|
200
247
|
|
|
201
248
|
// src/core/variants/probe-document.ts
|
|
202
249
|
function probeSelectionSet(set, fragments) {
|
|
@@ -770,6 +817,7 @@ function createVaryProbe(run, options) {
|
|
|
770
817
|
}
|
|
771
818
|
|
|
772
819
|
// src/core/graphql/client.ts
|
|
820
|
+
var clientSchemas = /* @__PURE__ */ new WeakMap();
|
|
773
821
|
function isParseableUrl(url) {
|
|
774
822
|
try {
|
|
775
823
|
new URL(url);
|
|
@@ -806,7 +854,7 @@ function createPreprClient(options) {
|
|
|
806
854
|
));
|
|
807
855
|
const variantsDocuments = /* @__PURE__ */ new Map();
|
|
808
856
|
const decisionDocuments = /* @__PURE__ */ new Map();
|
|
809
|
-
|
|
857
|
+
const client = {
|
|
810
858
|
async query(document2, variables, queryOptions = {}) {
|
|
811
859
|
const { variants, ...rest } = queryOptions;
|
|
812
860
|
if (!variants) return run(document2, variables, rest);
|
|
@@ -818,10 +866,10 @@ function createPreprClient(options) {
|
|
|
818
866
|
return run(derived, variables, rest);
|
|
819
867
|
},
|
|
820
868
|
async decide(document2, variables, queryOptions = {}) {
|
|
821
|
-
const { from,
|
|
869
|
+
const { from, ...rest } = queryOptions;
|
|
822
870
|
const paths = from === void 0 ? void 0 : personalizationPaths(from);
|
|
823
871
|
if (paths && paths.size === 0) return /* @__PURE__ */ new Map();
|
|
824
|
-
const signature = paths ? [...paths].map(([path, types]) => `${path}:${[...types].sort().join(",")}`).sort().join("|") :
|
|
872
|
+
const signature = paths ? [...paths].map(([path, types]) => `${path}:${[...types].sort().join(",")}`).sort().join("|") : "*";
|
|
825
873
|
let byPaths = decisionDocuments.get(document2);
|
|
826
874
|
if (!byPaths) {
|
|
827
875
|
byPaths = /* @__PURE__ */ new Map();
|
|
@@ -829,9 +877,7 @@ function createPreprClient(options) {
|
|
|
829
877
|
}
|
|
830
878
|
let derived = byPaths.get(signature);
|
|
831
879
|
if (derived === void 0) {
|
|
832
|
-
derived = toDecisionDocument(document2, await loadSchema(), paths
|
|
833
|
-
topLevelOnly: !paths && topLevelOnly
|
|
834
|
-
});
|
|
880
|
+
derived = toDecisionDocument(document2, await loadSchema(), paths);
|
|
835
881
|
byPaths.set(signature, derived);
|
|
836
882
|
}
|
|
837
883
|
if (!derived) return /* @__PURE__ */ new Map();
|
|
@@ -845,6 +891,8 @@ function createPreprClient(options) {
|
|
|
845
891
|
(vary) => vary === null || SEGMENTS_VARY.test(vary)
|
|
846
892
|
)
|
|
847
893
|
};
|
|
894
|
+
clientSchemas.set(client, loadSchema);
|
|
895
|
+
return client;
|
|
848
896
|
}
|
|
849
897
|
|
|
850
898
|
// src/core/graphql/event-client.ts
|
package/dist/sveltekit/index.cjs
CHANGED
package/dist/sveltekit/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@preprio/toolkit",
|
|
3
|
-
"version": "0.6.0-alpha.
|
|
3
|
+
"version": "0.6.0-alpha.10",
|
|
4
4
|
"description": "Prepr personalization, A/B testing and preview toolkit for React, Next.js, Nuxt, Astro, SvelteKit and Express",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Prepr B.V. (https://prepr.io)",
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
// src/version.ts
|
|
2
|
-
var VERSION = "0.6.0-alpha.1";
|
|
3
|
-
|
|
4
|
-
// src/core/features.ts
|
|
5
|
-
function isEnabled(config) {
|
|
6
|
-
if (typeof config === "boolean") return config;
|
|
7
|
-
return config?.enabled ?? true;
|
|
8
|
-
}
|
|
9
|
-
function resolveFeatures(features) {
|
|
10
|
-
return {
|
|
11
|
-
segments: isEnabled(features?.segments),
|
|
12
|
-
abTesting: isEnabled(features?.abTesting),
|
|
13
|
-
editMode: isEnabled(features?.editMode)
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
var ALL_FEATURES_ENABLED = Object.freeze({
|
|
17
|
-
segments: true,
|
|
18
|
-
abTesting: true,
|
|
19
|
-
editMode: true
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
// src/core/server.ts
|
|
23
|
-
var PreprError = class extends Error {
|
|
24
|
-
constructor(message, code, context, originalError) {
|
|
25
|
-
super(message);
|
|
26
|
-
this.code = code;
|
|
27
|
-
this.context = context;
|
|
28
|
-
this.originalError = originalError;
|
|
29
|
-
this.name = "PreprError";
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
function getPreprUUIDFromHeaders(headers2) {
|
|
33
|
-
return headers2.get("prepr-customer-id");
|
|
34
|
-
}
|
|
35
|
-
function getActiveSegmentFromHeaders(headers2) {
|
|
36
|
-
return headers2.get("Prepr-Segments");
|
|
37
|
-
}
|
|
38
|
-
function getActiveVariantFromHeaders(headers2) {
|
|
39
|
-
return headers2.get("Prepr-ABtesting");
|
|
40
|
-
}
|
|
41
|
-
var PREPR_HEADER_KEYS = [
|
|
42
|
-
"prepr-customer-id",
|
|
43
|
-
"Prepr-Segments",
|
|
44
|
-
"Prepr-ABtesting",
|
|
45
|
-
"Prepr-Preview-Bar",
|
|
46
|
-
"Prepr-Context-utm_source",
|
|
47
|
-
"Prepr-Context-utm_medium",
|
|
48
|
-
"Prepr-Context-utm_term",
|
|
49
|
-
"Prepr-Context-utm_content",
|
|
50
|
-
"Prepr-Context-utm_campaign",
|
|
51
|
-
"Prepr-Context-initial_referral",
|
|
52
|
-
"Prepr-Visitor-IP",
|
|
53
|
-
"Prepr-Hubspot-Id",
|
|
54
|
-
"Prepr-Customer-Id-Created",
|
|
55
|
-
"User-Agent",
|
|
56
|
-
"Prepr-Package"
|
|
57
|
-
];
|
|
58
|
-
function getPreprHeadersFromHeaders(headers2) {
|
|
59
|
-
const preprHeaders = {};
|
|
60
|
-
const copy = (key) => {
|
|
61
|
-
const value = headers2.get(key);
|
|
62
|
-
if (value !== null) {
|
|
63
|
-
preprHeaders[key] = value;
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
for (const key of PREPR_HEADER_KEYS) {
|
|
67
|
-
copy(key);
|
|
68
|
-
}
|
|
69
|
-
return preprHeaders;
|
|
70
|
-
}
|
|
71
|
-
function validatePreprToken(token) {
|
|
72
|
-
if (!token) {
|
|
73
|
-
throw new PreprError(
|
|
74
|
-
"Token is required",
|
|
75
|
-
"MISSING_TOKEN",
|
|
76
|
-
"validatePreprToken"
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
if (!token.startsWith("https://")) {
|
|
80
|
-
throw new PreprError(
|
|
81
|
-
"Token must be a valid HTTPS URL",
|
|
82
|
-
"INVALID_TOKEN",
|
|
83
|
-
"validatePreprToken"
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
function extractAccessToken(graphqlUrl) {
|
|
88
|
-
let url;
|
|
89
|
-
try {
|
|
90
|
-
url = new URL(graphqlUrl);
|
|
91
|
-
} catch (error) {
|
|
92
|
-
throw new PreprError(
|
|
93
|
-
"Token must be a valid URL",
|
|
94
|
-
"INVALID_TOKEN",
|
|
95
|
-
"extractAccessToken",
|
|
96
|
-
error instanceof Error ? error : new Error(String(error))
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
if (url.hostname !== "graphql.prepr.io") {
|
|
100
|
-
throw new PreprError(
|
|
101
|
-
"Token must be a graphql.prepr.io URL",
|
|
102
|
-
"INVALID_TOKEN",
|
|
103
|
-
"extractAccessToken"
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
const pathParts = url.pathname.split("/");
|
|
107
|
-
const token = pathParts[pathParts.length - 1];
|
|
108
|
-
if (!token) {
|
|
109
|
-
throw new PreprError(
|
|
110
|
-
"Token URL is missing an access token segment",
|
|
111
|
-
"INVALID_TOKEN",
|
|
112
|
-
"extractAccessToken"
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
return token;
|
|
116
|
-
}
|
|
117
|
-
async function getPreprEnvironmentSegments(token) {
|
|
118
|
-
validatePreprToken(token);
|
|
119
|
-
try {
|
|
120
|
-
const response = await fetch(token, {
|
|
121
|
-
headers: {
|
|
122
|
-
"Prepr-Package": `@preprio/toolkit@${VERSION}`,
|
|
123
|
-
// Duplicates the version in Prepr-Package. It keeps the User-Agent
|
|
124
|
-
// format used by `@preprio/prepr-nextjs`, for Prepr API consumers that
|
|
125
|
-
// still identify the toolbar by that string.
|
|
126
|
-
"User-Agent": `Prepr-Preview-Bar/${VERSION}`,
|
|
127
|
-
"Content-Type": "application/json"
|
|
128
|
-
},
|
|
129
|
-
method: "POST",
|
|
130
|
-
body: JSON.stringify({
|
|
131
|
-
query: `{
|
|
132
|
-
_Segments {
|
|
133
|
-
_id
|
|
134
|
-
name
|
|
135
|
-
}
|
|
136
|
-
}`
|
|
137
|
-
})
|
|
138
|
-
});
|
|
139
|
-
if (!response.ok) {
|
|
140
|
-
throw new PreprError(
|
|
141
|
-
`HTTP ${response.status}: ${response.statusText}`,
|
|
142
|
-
"HTTP_ERROR",
|
|
143
|
-
"getPreprEnvironmentSegments"
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
const json = await response.json();
|
|
147
|
-
if (!json || !json.data || !json.data._Segments) {
|
|
148
|
-
throw new PreprError(
|
|
149
|
-
"Invalid response format from Prepr API",
|
|
150
|
-
"INVALID_RESPONSE",
|
|
151
|
-
"getPreprEnvironmentSegments"
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
return json.data._Segments;
|
|
155
|
-
} catch (error) {
|
|
156
|
-
if (error instanceof PreprError) {
|
|
157
|
-
throw error;
|
|
158
|
-
}
|
|
159
|
-
throw new PreprError(
|
|
160
|
-
"Failed to fetch segments from Prepr API",
|
|
161
|
-
"FETCH_ERROR",
|
|
162
|
-
"getPreprEnvironmentSegments",
|
|
163
|
-
error instanceof Error ? error : new Error(String(error))
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
async function getToolbarPropsFromHeaders(headers2, token, features) {
|
|
168
|
-
const resolved = resolveFeatures(features);
|
|
169
|
-
let segments = [];
|
|
170
|
-
if (resolved.segments) {
|
|
171
|
-
try {
|
|
172
|
-
segments = await getPreprEnvironmentSegments(token);
|
|
173
|
-
} catch {
|
|
174
|
-
segments = [];
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return {
|
|
178
|
-
activeSegment: resolved.segments ? getActiveSegmentFromHeaders(headers2) : null,
|
|
179
|
-
activeVariant: resolved.abTesting ? getActiveVariantFromHeaders(headers2) : null,
|
|
180
|
-
segments,
|
|
181
|
-
data: segments
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// src/nextjs/server.ts
|
|
186
|
-
import { headers } from "next/headers";
|
|
187
|
-
async function readHeaders() {
|
|
188
|
-
return await headers();
|
|
189
|
-
}
|
|
190
|
-
async function getPreprUUID() {
|
|
191
|
-
const h = await readHeaders();
|
|
192
|
-
return getPreprUUIDFromHeaders(h);
|
|
193
|
-
}
|
|
194
|
-
async function getActiveSegment() {
|
|
195
|
-
const h = await readHeaders();
|
|
196
|
-
return getActiveSegmentFromHeaders(h);
|
|
197
|
-
}
|
|
198
|
-
async function getActiveVariant() {
|
|
199
|
-
const h = await readHeaders();
|
|
200
|
-
return getActiveVariantFromHeaders(h);
|
|
201
|
-
}
|
|
202
|
-
async function getPreprHeaders() {
|
|
203
|
-
const h = await readHeaders();
|
|
204
|
-
return getPreprHeadersFromHeaders(h);
|
|
205
|
-
}
|
|
206
|
-
async function getToolbarProps(token, features) {
|
|
207
|
-
const h = await readHeaders();
|
|
208
|
-
return getToolbarPropsFromHeaders(h, token, features);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// src/core/personalization.ts
|
|
212
|
-
var PERSONALIZED_VARY = /\bPrepr-(Segments|ABTesting)/i;
|
|
213
|
-
function isPersonalizedResponse(source) {
|
|
214
|
-
const headers2 = "headers" in source ? source.headers : source;
|
|
215
|
-
return PERSONALIZED_VARY.test(headers2.get("vary") ?? "");
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
export {
|
|
219
|
-
VERSION,
|
|
220
|
-
resolveFeatures,
|
|
221
|
-
PreprError,
|
|
222
|
-
getPreprHeadersFromHeaders,
|
|
223
|
-
validatePreprToken,
|
|
224
|
-
extractAccessToken,
|
|
225
|
-
getPreprUUID,
|
|
226
|
-
getActiveSegment,
|
|
227
|
-
getActiveVariant,
|
|
228
|
-
getPreprHeaders,
|
|
229
|
-
getToolbarProps,
|
|
230
|
-
isPersonalizedResponse
|
|
231
|
-
};
|