@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/graphql.cjs
CHANGED
|
@@ -20,12 +20,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/graphql.ts
|
|
21
21
|
var graphql_exports = {};
|
|
22
22
|
__export(graphql_exports, {
|
|
23
|
+
PREPR_ANY_ITEM_TAG: () => PREPR_ANY_ITEM_TAG,
|
|
23
24
|
PREPR_DEFAULT_VARIANT: () => PREPR_DEFAULT_VARIANT,
|
|
24
25
|
PreprError: () => PreprError,
|
|
25
26
|
createPreprClient: () => createPreprClient,
|
|
26
27
|
decisionsFrom: () => decisionsFrom,
|
|
27
28
|
isPersonalizedResponse: () => isPersonalizedResponse,
|
|
28
29
|
loadPreprSchema: () => loadPreprSchema,
|
|
30
|
+
preprCacheTags: () => preprCacheTags,
|
|
31
|
+
preprModelTag: () => preprModelTag,
|
|
29
32
|
preprVariantProps: () => preprVariantProps,
|
|
30
33
|
resolveVariants: () => resolveVariants,
|
|
31
34
|
splitStack: () => splitStack,
|
|
@@ -34,6 +37,31 @@ __export(graphql_exports, {
|
|
|
34
37
|
});
|
|
35
38
|
module.exports = __toCommonJS(graphql_exports);
|
|
36
39
|
|
|
40
|
+
// src/core/graphql/cache-tags.ts
|
|
41
|
+
var MAX_TAGS = 128;
|
|
42
|
+
var MAX_TAG_LENGTH = 256;
|
|
43
|
+
var PREPR_ANY_ITEM_TAG = "prepr:any";
|
|
44
|
+
var preprItemTag = (id) => `prepr:${id}`;
|
|
45
|
+
var NOT_ALPHANUMERIC = /[^a-z0-9]/g;
|
|
46
|
+
var preprModelTag = (model) => `prepr:model:${model.toLowerCase().replace(NOT_ALPHANUMERIC, "")}`;
|
|
47
|
+
function preprCacheTags(data) {
|
|
48
|
+
const tags = /* @__PURE__ */ new Set();
|
|
49
|
+
const stack = [data];
|
|
50
|
+
while (stack.length > 0) {
|
|
51
|
+
const value = stack.pop();
|
|
52
|
+
if (typeof value !== "object" || value === null) continue;
|
|
53
|
+
const id = value._id;
|
|
54
|
+
if (typeof id === "string" && id !== "") {
|
|
55
|
+
const tag = preprItemTag(id);
|
|
56
|
+
if (tag.length > MAX_TAG_LENGTH) return [PREPR_ANY_ITEM_TAG];
|
|
57
|
+
tags.add(tag);
|
|
58
|
+
if (tags.size > MAX_TAGS) return [PREPR_ANY_ITEM_TAG];
|
|
59
|
+
}
|
|
60
|
+
for (const child of Object.values(value)) stack.push(child);
|
|
61
|
+
}
|
|
62
|
+
return [...tags];
|
|
63
|
+
}
|
|
64
|
+
|
|
37
65
|
// src/core/server.ts
|
|
38
66
|
var PreprError = class extends Error {
|
|
39
67
|
constructor(message, code, context, originalError) {
|
|
@@ -148,7 +176,10 @@ function decisionSelectionSet(set, typeName, path, ctx) {
|
|
|
148
176
|
path,
|
|
149
177
|
ctx
|
|
150
178
|
);
|
|
151
|
-
if (pruned)
|
|
179
|
+
if (!pruned) continue;
|
|
180
|
+
if (fragment.typeCondition.name.value === typeName) {
|
|
181
|
+
selections.push(...pruned.selections);
|
|
182
|
+
} else {
|
|
152
183
|
selections.push(
|
|
153
184
|
inlineFragment(fragment.typeCondition.name.value, [
|
|
154
185
|
...pruned.selections
|
|
@@ -158,13 +189,19 @@ function decisionSelectionSet(set, typeName, path, ctx) {
|
|
|
158
189
|
continue;
|
|
159
190
|
}
|
|
160
191
|
if (selection.kind === "InlineFragment") {
|
|
192
|
+
const condition = selection.typeCondition?.name.value ?? typeName;
|
|
161
193
|
const pruned = decisionSelectionSet(
|
|
162
194
|
selection.selectionSet,
|
|
163
|
-
|
|
195
|
+
condition,
|
|
164
196
|
path,
|
|
165
197
|
ctx
|
|
166
198
|
);
|
|
167
|
-
if (pruned)
|
|
199
|
+
if (!pruned) continue;
|
|
200
|
+
if (condition === typeName && !selection.directives?.length) {
|
|
201
|
+
selections.push(...pruned.selections);
|
|
202
|
+
} else {
|
|
203
|
+
selections.push({ ...selection, selectionSet: pruned });
|
|
204
|
+
}
|
|
168
205
|
continue;
|
|
169
206
|
}
|
|
170
207
|
if (!selection.selectionSet) continue;
|
|
@@ -238,20 +275,58 @@ function toDecisionDocument(document2, schema, groupPaths, options) {
|
|
|
238
275
|
const used = /* @__PURE__ */ new Set();
|
|
239
276
|
collectVariables(pruned, used);
|
|
240
277
|
collectVariables(operation.directives, used);
|
|
278
|
+
const shared = sharedContextFragment(schema);
|
|
241
279
|
return {
|
|
242
280
|
kind: "Document",
|
|
243
281
|
definitions: [
|
|
282
|
+
...shared ? [shared.definition] : [],
|
|
244
283
|
{
|
|
245
284
|
...operation,
|
|
246
285
|
name: operation.name && name(`${operation.name.value}Decision`),
|
|
247
286
|
variableDefinitions: (operation.variableDefinitions ?? []).filter(
|
|
248
287
|
(definition) => used.has(definition.variable.name.value)
|
|
249
288
|
),
|
|
250
|
-
selectionSet: pruned
|
|
289
|
+
selectionSet: shared ? shared.apply(pruned) : pruned
|
|
251
290
|
}
|
|
252
291
|
]
|
|
253
292
|
};
|
|
254
293
|
}
|
|
294
|
+
function sharedContextFragment(schema) {
|
|
295
|
+
const contextTypes = new Set(
|
|
296
|
+
Object.values(schema.types).flatMap(
|
|
297
|
+
(type) => type.hasContext ? [type.fields._context.type] : []
|
|
298
|
+
)
|
|
299
|
+
);
|
|
300
|
+
if (contextTypes.size !== 1) return null;
|
|
301
|
+
const [contextType] = contextTypes;
|
|
302
|
+
const fragmentName = "c";
|
|
303
|
+
const definition = {
|
|
304
|
+
kind: "FragmentDefinition",
|
|
305
|
+
name: name(fragmentName),
|
|
306
|
+
typeCondition: { kind: "NamedType", name: name(contextType) },
|
|
307
|
+
selectionSet: selectionSet(
|
|
308
|
+
["kind", "group_id", "variant_id"].map(
|
|
309
|
+
(field) => ({ kind: "Field", name: name(field) })
|
|
310
|
+
)
|
|
311
|
+
)
|
|
312
|
+
};
|
|
313
|
+
const spread = {
|
|
314
|
+
kind: "FragmentSpread",
|
|
315
|
+
name: name(fragmentName)
|
|
316
|
+
};
|
|
317
|
+
const apply = (set) => selectionSet(
|
|
318
|
+
set.selections.map((selection) => {
|
|
319
|
+
if (selection.kind === "Field" && selection.alias?.value === PREPR_CONTEXT_ALIAS) {
|
|
320
|
+
return { ...selection, selectionSet: selectionSet([spread]) };
|
|
321
|
+
}
|
|
322
|
+
return "selectionSet" in selection && selection.selectionSet ? {
|
|
323
|
+
...selection,
|
|
324
|
+
selectionSet: apply(selection.selectionSet)
|
|
325
|
+
} : selection;
|
|
326
|
+
})
|
|
327
|
+
);
|
|
328
|
+
return { definition, apply };
|
|
329
|
+
}
|
|
255
330
|
|
|
256
331
|
// src/core/variants/probe-document.ts
|
|
257
332
|
function probeSelectionSet(set, fragments) {
|
|
@@ -825,6 +900,7 @@ function createVaryProbe(run, options) {
|
|
|
825
900
|
}
|
|
826
901
|
|
|
827
902
|
// src/core/graphql/client.ts
|
|
903
|
+
var clientSchemas = /* @__PURE__ */ new WeakMap();
|
|
828
904
|
function isParseableUrl(url) {
|
|
829
905
|
try {
|
|
830
906
|
new URL(url);
|
|
@@ -861,7 +937,7 @@ function createPreprClient(options) {
|
|
|
861
937
|
));
|
|
862
938
|
const variantsDocuments = /* @__PURE__ */ new Map();
|
|
863
939
|
const decisionDocuments = /* @__PURE__ */ new Map();
|
|
864
|
-
|
|
940
|
+
const client = {
|
|
865
941
|
async query(document2, variables, queryOptions = {}) {
|
|
866
942
|
const { variants, ...rest } = queryOptions;
|
|
867
943
|
if (!variants) return run(document2, variables, rest);
|
|
@@ -873,10 +949,10 @@ function createPreprClient(options) {
|
|
|
873
949
|
return run(derived, variables, rest);
|
|
874
950
|
},
|
|
875
951
|
async decide(document2, variables, queryOptions = {}) {
|
|
876
|
-
const { from,
|
|
952
|
+
const { from, ...rest } = queryOptions;
|
|
877
953
|
const paths = from === void 0 ? void 0 : personalizationPaths(from);
|
|
878
954
|
if (paths && paths.size === 0) return /* @__PURE__ */ new Map();
|
|
879
|
-
const signature = paths ? [...paths].map(([path, types]) => `${path}:${[...types].sort().join(",")}`).sort().join("|") :
|
|
955
|
+
const signature = paths ? [...paths].map(([path, types]) => `${path}:${[...types].sort().join(",")}`).sort().join("|") : "*";
|
|
880
956
|
let byPaths = decisionDocuments.get(document2);
|
|
881
957
|
if (!byPaths) {
|
|
882
958
|
byPaths = /* @__PURE__ */ new Map();
|
|
@@ -884,9 +960,7 @@ function createPreprClient(options) {
|
|
|
884
960
|
}
|
|
885
961
|
let derived = byPaths.get(signature);
|
|
886
962
|
if (derived === void 0) {
|
|
887
|
-
derived = toDecisionDocument(document2, await loadSchema(), paths
|
|
888
|
-
topLevelOnly: !paths && topLevelOnly
|
|
889
|
-
});
|
|
963
|
+
derived = toDecisionDocument(document2, await loadSchema(), paths);
|
|
890
964
|
byPaths.set(signature, derived);
|
|
891
965
|
}
|
|
892
966
|
if (!derived) return /* @__PURE__ */ new Map();
|
|
@@ -900,6 +974,8 @@ function createPreprClient(options) {
|
|
|
900
974
|
(vary) => vary === null || SEGMENTS_VARY.test(vary)
|
|
901
975
|
)
|
|
902
976
|
};
|
|
977
|
+
clientSchemas.set(client, loadSchema);
|
|
978
|
+
return client;
|
|
903
979
|
}
|
|
904
980
|
|
|
905
981
|
// src/core/graphql/schema-loader.ts
|
|
@@ -927,12 +1003,15 @@ async function loadPreprSchema(url, options = {}) {
|
|
|
927
1003
|
}
|
|
928
1004
|
// Annotate the CommonJS export names for ESM import in node:
|
|
929
1005
|
0 && (module.exports = {
|
|
1006
|
+
PREPR_ANY_ITEM_TAG,
|
|
930
1007
|
PREPR_DEFAULT_VARIANT,
|
|
931
1008
|
PreprError,
|
|
932
1009
|
createPreprClient,
|
|
933
1010
|
decisionsFrom,
|
|
934
1011
|
isPersonalizedResponse,
|
|
935
1012
|
loadPreprSchema,
|
|
1013
|
+
preprCacheTags,
|
|
1014
|
+
preprModelTag,
|
|
936
1015
|
preprVariantProps,
|
|
937
1016
|
resolveVariants,
|
|
938
1017
|
splitStack,
|
package/dist/graphql.d.cts
CHANGED
|
@@ -96,39 +96,6 @@ interface PreprVariantProps {
|
|
|
96
96
|
*/
|
|
97
97
|
declare function preprVariantProps(item: unknown): PreprVariantProps;
|
|
98
98
|
|
|
99
|
-
/**
|
|
100
|
-
* The schema facts the query rewrites need, from `loadPreprSchema`. Pass it
|
|
101
|
-
* along; its shape is not part of the public API.
|
|
102
|
-
*/
|
|
103
|
-
type PreprSchema = PreprSchemaInfo;
|
|
104
|
-
/**
|
|
105
|
-
* Your query, asking for every personalization and A/B variant: what
|
|
106
|
-
* `query(..., { variants: true })` sends, as a string for your own GraphQL
|
|
107
|
-
* client. The result is the same for every visitor, so cache it like any
|
|
108
|
-
* other content query, and render it with `resolveVariants` or `PreprStack`.
|
|
109
|
-
*
|
|
110
|
-
* A query without personalizable fields comes back unchanged (minified).
|
|
111
|
-
*/
|
|
112
|
-
declare function toVariantsQuery(document: PreprDocument<unknown, unknown> | string, schema: PreprSchema): string;
|
|
113
|
-
/**
|
|
114
|
-
* The per-visitor decision query for the same query: send it with the
|
|
115
|
-
* visitor's Prepr headers, never cached, and pass the response's `data` to
|
|
116
|
-
* `decisionsFrom`.
|
|
117
|
-
*
|
|
118
|
-
* Pass the variants result as `from` whenever you have it: the query then
|
|
119
|
-
* asks only about the lists that hold segment groups on this page, which is
|
|
120
|
-
* a few hundred bytes instead of the whole page and markedly faster for a
|
|
121
|
-
* visitor Prepr has not seen before. Without `from` it asks about every
|
|
122
|
-
* personalizable list. `topLevelOnly` stops at the first personalizable list
|
|
123
|
-
* on each path, for middleware that decides only the top of a stack.
|
|
124
|
-
*
|
|
125
|
-
* Null when there is nothing to decide: send no request then.
|
|
126
|
-
*/
|
|
127
|
-
declare function toDecisionQuery(document: PreprDocument<unknown, unknown> | string, schema: PreprSchema, options?: {
|
|
128
|
-
from?: unknown;
|
|
129
|
-
topLevelOnly?: boolean;
|
|
130
|
-
}): string | null;
|
|
131
|
-
|
|
132
99
|
/**
|
|
133
100
|
* A GraphQL document accepted by `query`: a plain string, graphql-codegen's
|
|
134
101
|
* `TypedDocumentString` (`documentMode: 'string'`), or any `DocumentNode`,
|
|
@@ -222,11 +189,6 @@ interface PreprDecideOptions extends Omit<PreprQueryOptions, 'variants'> {
|
|
|
222
189
|
* none, e.g. on pages with only A/B tests or no personalization.
|
|
223
190
|
*/
|
|
224
191
|
from?: unknown;
|
|
225
|
-
/**
|
|
226
|
-
* @internal Ask only about the first personalizable list on each path,
|
|
227
|
-
* without `from`: what middleware needs for groups at the top of a stack.
|
|
228
|
-
*/
|
|
229
|
-
topLevelOnly?: boolean;
|
|
230
192
|
}
|
|
231
193
|
interface PreprQueryResult<TResult> {
|
|
232
194
|
data: TResult;
|
|
@@ -271,6 +233,74 @@ interface PreprClient {
|
|
|
271
233
|
hasSegments<TVariables>(document: PreprDocument<unknown, TVariables>, variables?: TVariables): Promise<boolean>;
|
|
272
234
|
}
|
|
273
235
|
|
|
236
|
+
/**
|
|
237
|
+
* The schema facts the query rewrites need, from `loadPreprSchema`. Pass it
|
|
238
|
+
* along; its shape is not part of the public API.
|
|
239
|
+
*/
|
|
240
|
+
type PreprSchema = PreprSchemaInfo;
|
|
241
|
+
/**
|
|
242
|
+
* Your query, asking for every personalization and A/B variant: what
|
|
243
|
+
* `query(..., { variants: true })` sends, as a string for your own GraphQL
|
|
244
|
+
* client. The result is the same for every visitor, so cache it like any
|
|
245
|
+
* other content query, and render it with `resolveVariants` or `PreprStack`.
|
|
246
|
+
*
|
|
247
|
+
* A query without personalizable fields comes back unchanged (minified).
|
|
248
|
+
*/
|
|
249
|
+
declare function toVariantsQuery(document: PreprDocument<unknown, unknown> | string, schema: PreprSchema): string;
|
|
250
|
+
/**
|
|
251
|
+
* The per-visitor decision query for the same query: send it with the
|
|
252
|
+
* visitor's Prepr headers, never cached, and pass the response's `data` to
|
|
253
|
+
* `decisionsFrom`.
|
|
254
|
+
*
|
|
255
|
+
* Pass the variants result as `from` whenever you have it: the query then
|
|
256
|
+
* asks only about the lists that hold segment groups on this page, which is
|
|
257
|
+
* a few hundred bytes instead of the whole page and markedly faster for a
|
|
258
|
+
* visitor Prepr has not seen before. Without `from` it asks about every
|
|
259
|
+
* personalizable list. `topLevelOnly` stops at the first personalizable list
|
|
260
|
+
* on each path, for middleware that decides only the top of a stack.
|
|
261
|
+
*
|
|
262
|
+
* Null when there is nothing to decide: send no request then.
|
|
263
|
+
*/
|
|
264
|
+
declare function toDecisionQuery(document: PreprDocument<unknown, unknown> | string, schema: PreprSchema, options?: {
|
|
265
|
+
from?: unknown;
|
|
266
|
+
topLevelOnly?: boolean;
|
|
267
|
+
}): string | null;
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Tag for pages showing more items than fit in Next.js's tag limits. Every
|
|
271
|
+
* webhook event revalidates it, so those pages refresh on any change.
|
|
272
|
+
*/
|
|
273
|
+
declare const PREPR_ANY_ITEM_TAG = "prepr:any";
|
|
274
|
+
/**
|
|
275
|
+
* Cache tag for every page listing items of one model. `tagItems` adds it to
|
|
276
|
+
* list queries and to lookups that found nothing; add it yourself with
|
|
277
|
+
* `cacheTag()` for other pages that depend on which items exist.
|
|
278
|
+
* `createPreprWebhookRoute` revalidates it when an item of that model is
|
|
279
|
+
* published, unpublished or deleted.
|
|
280
|
+
*
|
|
281
|
+
* The name is compared case-insensitively and without spaces or
|
|
282
|
+
* punctuation, so the model's name in Prepr (`'Blog post'`, as webhooks send
|
|
283
|
+
* it) and its GraphQL type (`BlogPost`) give the same tag. A model whose API
|
|
284
|
+
* ID was renamed away from its name gets two different tags.
|
|
285
|
+
*
|
|
286
|
+
* @param model The model's singular name in Prepr or its GraphQL type name.
|
|
287
|
+
*/
|
|
288
|
+
declare const preprModelTag: (model: string) => string;
|
|
289
|
+
/**
|
|
290
|
+
* Cache tags for every Prepr item in a query result: `prepr:<_id>` for each
|
|
291
|
+
* object with a string `_id`, at any depth. Pair it with `cacheTag()` inside
|
|
292
|
+
* `'use cache'`, and a publish webhook revalidates only the pages that show
|
|
293
|
+
* the changed item. Run the query with `variants: true` so the tags cover
|
|
294
|
+
* every variant's items, not only those of one visitor.
|
|
295
|
+
*
|
|
296
|
+
* Next.js accepts at most 128 tags of 256 characters per `cacheTag()` call.
|
|
297
|
+
* A result past either limit returns `[PREPR_ANY_ITEM_TAG]` instead, which
|
|
298
|
+
* every webhook event revalidates.
|
|
299
|
+
*
|
|
300
|
+
* @param data A parsed JSON query result, such as `query()`'s `data`.
|
|
301
|
+
*/
|
|
302
|
+
declare function preprCacheTags(data: unknown): string[];
|
|
303
|
+
|
|
274
304
|
/**
|
|
275
305
|
* Create a GraphQL client for the Prepr API.
|
|
276
306
|
*
|
|
@@ -302,4 +332,4 @@ declare function loadPreprSchema(url: string, options?: {
|
|
|
302
332
|
fetch?: typeof fetch;
|
|
303
333
|
}): Promise<PreprSchemaInfo>;
|
|
304
334
|
|
|
305
|
-
export { PREPR_DEFAULT_VARIANT, type PreprClient, type PreprClientOptions, type PreprDecideOptions, type PreprDecisions, type PreprDocument, PreprHeaders, type PreprQueryOptions, type PreprQueryResult, type PreprSchema, type PreprStackBlock, type PreprStegaMode, type PreprVariantProps, type ResolveVariantsOptions, createPreprClient, decisionsFrom, loadPreprSchema, preprVariantProps, resolveVariants, splitStack, toDecisionQuery, toVariantsQuery };
|
|
335
|
+
export { PREPR_ANY_ITEM_TAG, PREPR_DEFAULT_VARIANT, type PreprClient, type PreprClientOptions, type PreprDecideOptions, type PreprDecisions, type PreprDocument, PreprHeaders, type PreprQueryOptions, type PreprQueryResult, type PreprSchema, type PreprStackBlock, type PreprStegaMode, type PreprVariantProps, type ResolveVariantsOptions, createPreprClient, decisionsFrom, loadPreprSchema, preprCacheTags, preprModelTag, preprVariantProps, resolveVariants, splitStack, toDecisionQuery, toVariantsQuery };
|
package/dist/graphql.d.ts
CHANGED
|
@@ -96,39 +96,6 @@ interface PreprVariantProps {
|
|
|
96
96
|
*/
|
|
97
97
|
declare function preprVariantProps(item: unknown): PreprVariantProps;
|
|
98
98
|
|
|
99
|
-
/**
|
|
100
|
-
* The schema facts the query rewrites need, from `loadPreprSchema`. Pass it
|
|
101
|
-
* along; its shape is not part of the public API.
|
|
102
|
-
*/
|
|
103
|
-
type PreprSchema = PreprSchemaInfo;
|
|
104
|
-
/**
|
|
105
|
-
* Your query, asking for every personalization and A/B variant: what
|
|
106
|
-
* `query(..., { variants: true })` sends, as a string for your own GraphQL
|
|
107
|
-
* client. The result is the same for every visitor, so cache it like any
|
|
108
|
-
* other content query, and render it with `resolveVariants` or `PreprStack`.
|
|
109
|
-
*
|
|
110
|
-
* A query without personalizable fields comes back unchanged (minified).
|
|
111
|
-
*/
|
|
112
|
-
declare function toVariantsQuery(document: PreprDocument<unknown, unknown> | string, schema: PreprSchema): string;
|
|
113
|
-
/**
|
|
114
|
-
* The per-visitor decision query for the same query: send it with the
|
|
115
|
-
* visitor's Prepr headers, never cached, and pass the response's `data` to
|
|
116
|
-
* `decisionsFrom`.
|
|
117
|
-
*
|
|
118
|
-
* Pass the variants result as `from` whenever you have it: the query then
|
|
119
|
-
* asks only about the lists that hold segment groups on this page, which is
|
|
120
|
-
* a few hundred bytes instead of the whole page and markedly faster for a
|
|
121
|
-
* visitor Prepr has not seen before. Without `from` it asks about every
|
|
122
|
-
* personalizable list. `topLevelOnly` stops at the first personalizable list
|
|
123
|
-
* on each path, for middleware that decides only the top of a stack.
|
|
124
|
-
*
|
|
125
|
-
* Null when there is nothing to decide: send no request then.
|
|
126
|
-
*/
|
|
127
|
-
declare function toDecisionQuery(document: PreprDocument<unknown, unknown> | string, schema: PreprSchema, options?: {
|
|
128
|
-
from?: unknown;
|
|
129
|
-
topLevelOnly?: boolean;
|
|
130
|
-
}): string | null;
|
|
131
|
-
|
|
132
99
|
/**
|
|
133
100
|
* A GraphQL document accepted by `query`: a plain string, graphql-codegen's
|
|
134
101
|
* `TypedDocumentString` (`documentMode: 'string'`), or any `DocumentNode`,
|
|
@@ -222,11 +189,6 @@ interface PreprDecideOptions extends Omit<PreprQueryOptions, 'variants'> {
|
|
|
222
189
|
* none, e.g. on pages with only A/B tests or no personalization.
|
|
223
190
|
*/
|
|
224
191
|
from?: unknown;
|
|
225
|
-
/**
|
|
226
|
-
* @internal Ask only about the first personalizable list on each path,
|
|
227
|
-
* without `from`: what middleware needs for groups at the top of a stack.
|
|
228
|
-
*/
|
|
229
|
-
topLevelOnly?: boolean;
|
|
230
192
|
}
|
|
231
193
|
interface PreprQueryResult<TResult> {
|
|
232
194
|
data: TResult;
|
|
@@ -271,6 +233,74 @@ interface PreprClient {
|
|
|
271
233
|
hasSegments<TVariables>(document: PreprDocument<unknown, TVariables>, variables?: TVariables): Promise<boolean>;
|
|
272
234
|
}
|
|
273
235
|
|
|
236
|
+
/**
|
|
237
|
+
* The schema facts the query rewrites need, from `loadPreprSchema`. Pass it
|
|
238
|
+
* along; its shape is not part of the public API.
|
|
239
|
+
*/
|
|
240
|
+
type PreprSchema = PreprSchemaInfo;
|
|
241
|
+
/**
|
|
242
|
+
* Your query, asking for every personalization and A/B variant: what
|
|
243
|
+
* `query(..., { variants: true })` sends, as a string for your own GraphQL
|
|
244
|
+
* client. The result is the same for every visitor, so cache it like any
|
|
245
|
+
* other content query, and render it with `resolveVariants` or `PreprStack`.
|
|
246
|
+
*
|
|
247
|
+
* A query without personalizable fields comes back unchanged (minified).
|
|
248
|
+
*/
|
|
249
|
+
declare function toVariantsQuery(document: PreprDocument<unknown, unknown> | string, schema: PreprSchema): string;
|
|
250
|
+
/**
|
|
251
|
+
* The per-visitor decision query for the same query: send it with the
|
|
252
|
+
* visitor's Prepr headers, never cached, and pass the response's `data` to
|
|
253
|
+
* `decisionsFrom`.
|
|
254
|
+
*
|
|
255
|
+
* Pass the variants result as `from` whenever you have it: the query then
|
|
256
|
+
* asks only about the lists that hold segment groups on this page, which is
|
|
257
|
+
* a few hundred bytes instead of the whole page and markedly faster for a
|
|
258
|
+
* visitor Prepr has not seen before. Without `from` it asks about every
|
|
259
|
+
* personalizable list. `topLevelOnly` stops at the first personalizable list
|
|
260
|
+
* on each path, for middleware that decides only the top of a stack.
|
|
261
|
+
*
|
|
262
|
+
* Null when there is nothing to decide: send no request then.
|
|
263
|
+
*/
|
|
264
|
+
declare function toDecisionQuery(document: PreprDocument<unknown, unknown> | string, schema: PreprSchema, options?: {
|
|
265
|
+
from?: unknown;
|
|
266
|
+
topLevelOnly?: boolean;
|
|
267
|
+
}): string | null;
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Tag for pages showing more items than fit in Next.js's tag limits. Every
|
|
271
|
+
* webhook event revalidates it, so those pages refresh on any change.
|
|
272
|
+
*/
|
|
273
|
+
declare const PREPR_ANY_ITEM_TAG = "prepr:any";
|
|
274
|
+
/**
|
|
275
|
+
* Cache tag for every page listing items of one model. `tagItems` adds it to
|
|
276
|
+
* list queries and to lookups that found nothing; add it yourself with
|
|
277
|
+
* `cacheTag()` for other pages that depend on which items exist.
|
|
278
|
+
* `createPreprWebhookRoute` revalidates it when an item of that model is
|
|
279
|
+
* published, unpublished or deleted.
|
|
280
|
+
*
|
|
281
|
+
* The name is compared case-insensitively and without spaces or
|
|
282
|
+
* punctuation, so the model's name in Prepr (`'Blog post'`, as webhooks send
|
|
283
|
+
* it) and its GraphQL type (`BlogPost`) give the same tag. A model whose API
|
|
284
|
+
* ID was renamed away from its name gets two different tags.
|
|
285
|
+
*
|
|
286
|
+
* @param model The model's singular name in Prepr or its GraphQL type name.
|
|
287
|
+
*/
|
|
288
|
+
declare const preprModelTag: (model: string) => string;
|
|
289
|
+
/**
|
|
290
|
+
* Cache tags for every Prepr item in a query result: `prepr:<_id>` for each
|
|
291
|
+
* object with a string `_id`, at any depth. Pair it with `cacheTag()` inside
|
|
292
|
+
* `'use cache'`, and a publish webhook revalidates only the pages that show
|
|
293
|
+
* the changed item. Run the query with `variants: true` so the tags cover
|
|
294
|
+
* every variant's items, not only those of one visitor.
|
|
295
|
+
*
|
|
296
|
+
* Next.js accepts at most 128 tags of 256 characters per `cacheTag()` call.
|
|
297
|
+
* A result past either limit returns `[PREPR_ANY_ITEM_TAG]` instead, which
|
|
298
|
+
* every webhook event revalidates.
|
|
299
|
+
*
|
|
300
|
+
* @param data A parsed JSON query result, such as `query()`'s `data`.
|
|
301
|
+
*/
|
|
302
|
+
declare function preprCacheTags(data: unknown): string[];
|
|
303
|
+
|
|
274
304
|
/**
|
|
275
305
|
* Create a GraphQL client for the Prepr API.
|
|
276
306
|
*
|
|
@@ -302,4 +332,4 @@ declare function loadPreprSchema(url: string, options?: {
|
|
|
302
332
|
fetch?: typeof fetch;
|
|
303
333
|
}): Promise<PreprSchemaInfo>;
|
|
304
334
|
|
|
305
|
-
export { PREPR_DEFAULT_VARIANT, type PreprClient, type PreprClientOptions, type PreprDecideOptions, type PreprDecisions, type PreprDocument, PreprHeaders, type PreprQueryOptions, type PreprQueryResult, type PreprSchema, type PreprStackBlock, type PreprStegaMode, type PreprVariantProps, type ResolveVariantsOptions, createPreprClient, decisionsFrom, loadPreprSchema, preprVariantProps, resolveVariants, splitStack, toDecisionQuery, toVariantsQuery };
|
|
335
|
+
export { PREPR_ANY_ITEM_TAG, PREPR_DEFAULT_VARIANT, type PreprClient, type PreprClientOptions, type PreprDecideOptions, type PreprDecisions, type PreprDocument, PreprHeaders, type PreprQueryOptions, type PreprQueryResult, type PreprSchema, type PreprStackBlock, type PreprStegaMode, type PreprVariantProps, type ResolveVariantsOptions, createPreprClient, decisionsFrom, loadPreprSchema, preprCacheTags, preprModelTag, preprVariantProps, resolveVariants, splitStack, toDecisionQuery, toVariantsQuery };
|
package/dist/graphql.js
CHANGED
|
@@ -21,12 +21,37 @@ import {
|
|
|
21
21
|
toSchemaInfo,
|
|
22
22
|
toVariantsDocument,
|
|
23
23
|
toVariantsQuery
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-PFJO4TYL.js";
|
|
25
25
|
import {
|
|
26
26
|
PreprError,
|
|
27
27
|
validatePreprToken
|
|
28
|
-
} from "./chunk-
|
|
29
|
-
import "./chunk-
|
|
28
|
+
} from "./chunk-3QGWCYTL.js";
|
|
29
|
+
import "./chunk-VQIPTUN4.js";
|
|
30
|
+
|
|
31
|
+
// src/core/graphql/cache-tags.ts
|
|
32
|
+
var MAX_TAGS = 128;
|
|
33
|
+
var MAX_TAG_LENGTH = 256;
|
|
34
|
+
var PREPR_ANY_ITEM_TAG = "prepr:any";
|
|
35
|
+
var preprItemTag = (id) => `prepr:${id}`;
|
|
36
|
+
var NOT_ALPHANUMERIC = /[^a-z0-9]/g;
|
|
37
|
+
var preprModelTag = (model) => `prepr:model:${model.toLowerCase().replace(NOT_ALPHANUMERIC, "")}`;
|
|
38
|
+
function preprCacheTags(data) {
|
|
39
|
+
const tags = /* @__PURE__ */ new Set();
|
|
40
|
+
const stack = [data];
|
|
41
|
+
while (stack.length > 0) {
|
|
42
|
+
const value = stack.pop();
|
|
43
|
+
if (typeof value !== "object" || value === null) continue;
|
|
44
|
+
const id = value._id;
|
|
45
|
+
if (typeof id === "string" && id !== "") {
|
|
46
|
+
const tag = preprItemTag(id);
|
|
47
|
+
if (tag.length > MAX_TAG_LENGTH) return [PREPR_ANY_ITEM_TAG];
|
|
48
|
+
tags.add(tag);
|
|
49
|
+
if (tags.size > MAX_TAGS) return [PREPR_ANY_ITEM_TAG];
|
|
50
|
+
}
|
|
51
|
+
for (const child of Object.values(value)) stack.push(child);
|
|
52
|
+
}
|
|
53
|
+
return [...tags];
|
|
54
|
+
}
|
|
30
55
|
|
|
31
56
|
// src/core/variants/probe-document.ts
|
|
32
57
|
function probeSelectionSet(set, fragments) {
|
|
@@ -304,6 +329,7 @@ function createVaryProbe(run, options) {
|
|
|
304
329
|
}
|
|
305
330
|
|
|
306
331
|
// src/core/graphql/client.ts
|
|
332
|
+
var clientSchemas = /* @__PURE__ */ new WeakMap();
|
|
307
333
|
function isParseableUrl(url) {
|
|
308
334
|
try {
|
|
309
335
|
new URL(url);
|
|
@@ -340,7 +366,7 @@ function createPreprClient(options) {
|
|
|
340
366
|
));
|
|
341
367
|
const variantsDocuments = /* @__PURE__ */ new Map();
|
|
342
368
|
const decisionDocuments = /* @__PURE__ */ new Map();
|
|
343
|
-
|
|
369
|
+
const client = {
|
|
344
370
|
async query(document, variables, queryOptions = {}) {
|
|
345
371
|
const { variants, ...rest } = queryOptions;
|
|
346
372
|
if (!variants) return run(document, variables, rest);
|
|
@@ -352,10 +378,10 @@ function createPreprClient(options) {
|
|
|
352
378
|
return run(derived, variables, rest);
|
|
353
379
|
},
|
|
354
380
|
async decide(document, variables, queryOptions = {}) {
|
|
355
|
-
const { from,
|
|
381
|
+
const { from, ...rest } = queryOptions;
|
|
356
382
|
const paths = from === void 0 ? void 0 : personalizationPaths(from);
|
|
357
383
|
if (paths && paths.size === 0) return /* @__PURE__ */ new Map();
|
|
358
|
-
const signature = paths ? [...paths].map(([path, types]) => `${path}:${[...types].sort().join(",")}`).sort().join("|") :
|
|
384
|
+
const signature = paths ? [...paths].map(([path, types]) => `${path}:${[...types].sort().join(",")}`).sort().join("|") : "*";
|
|
359
385
|
let byPaths = decisionDocuments.get(document);
|
|
360
386
|
if (!byPaths) {
|
|
361
387
|
byPaths = /* @__PURE__ */ new Map();
|
|
@@ -363,9 +389,7 @@ function createPreprClient(options) {
|
|
|
363
389
|
}
|
|
364
390
|
let derived = byPaths.get(signature);
|
|
365
391
|
if (derived === void 0) {
|
|
366
|
-
derived = toDecisionDocument(document, await loadSchema(), paths
|
|
367
|
-
topLevelOnly: !paths && topLevelOnly
|
|
368
|
-
});
|
|
392
|
+
derived = toDecisionDocument(document, await loadSchema(), paths);
|
|
369
393
|
byPaths.set(signature, derived);
|
|
370
394
|
}
|
|
371
395
|
if (!derived) return /* @__PURE__ */ new Map();
|
|
@@ -379,6 +403,8 @@ function createPreprClient(options) {
|
|
|
379
403
|
(vary) => vary === null || SEGMENTS_VARY.test(vary)
|
|
380
404
|
)
|
|
381
405
|
};
|
|
406
|
+
clientSchemas.set(client, loadSchema);
|
|
407
|
+
return client;
|
|
382
408
|
}
|
|
383
409
|
|
|
384
410
|
// src/core/graphql/schema-loader.ts
|
|
@@ -405,12 +431,15 @@ async function loadPreprSchema(url, options = {}) {
|
|
|
405
431
|
return schema;
|
|
406
432
|
}
|
|
407
433
|
export {
|
|
434
|
+
PREPR_ANY_ITEM_TAG,
|
|
408
435
|
PREPR_DEFAULT_VARIANT,
|
|
409
436
|
PreprError,
|
|
410
437
|
createPreprClient,
|
|
411
438
|
decisionsFrom,
|
|
412
439
|
isPersonalizedResponse,
|
|
413
440
|
loadPreprSchema,
|
|
441
|
+
preprCacheTags,
|
|
442
|
+
preprModelTag,
|
|
414
443
|
preprVariantProps,
|
|
415
444
|
resolveVariants,
|
|
416
445
|
splitStack,
|
package/dist/index.cjs
CHANGED
package/dist/index.d.cts
CHANGED
|
@@ -7,7 +7,7 @@ export { P as PreprPixelConfig, l as loadTrackingPixel, s as setTrackingParam, t
|
|
|
7
7
|
* Package version; must match package.json. Lives in its own leaf module so
|
|
8
8
|
* core can import it without cycling through the barrel.
|
|
9
9
|
*/
|
|
10
|
-
declare const VERSION = "0.6.0-alpha.
|
|
10
|
+
declare const VERSION = "0.6.0-alpha.7";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Resolve a `PreprFeatures` bag to plain booleans, defaulting every omitted key
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export { P as PreprPixelConfig, l as loadTrackingPixel, s as setTrackingParam, t
|
|
|
7
7
|
* Package version; must match package.json. Lives in its own leaf module so
|
|
8
8
|
* core can import it without cycling through the barrel.
|
|
9
9
|
*/
|
|
10
|
-
declare const VERSION = "0.6.0-alpha.
|
|
10
|
+
declare const VERSION = "0.6.0-alpha.7";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Resolve a `PreprFeatures` bag to plain booleans, defaulting every omitted key
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
loadTrackingPixel,
|
|
16
16
|
setTrackingParam,
|
|
17
17
|
trackEvent
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-U7TRFVK7.js";
|
|
19
19
|
import {
|
|
20
20
|
isPersonalizedResponse
|
|
21
21
|
} from "./chunk-3NCEVJJ3.js";
|
|
@@ -32,11 +32,11 @@ import {
|
|
|
32
32
|
getPreprUUIDFromHeaders,
|
|
33
33
|
getToolbarPropsFromHeaders,
|
|
34
34
|
validatePreprToken
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-3QGWCYTL.js";
|
|
36
36
|
import {
|
|
37
37
|
VERSION,
|
|
38
38
|
resolveFeatures
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-VQIPTUN4.js";
|
|
40
40
|
|
|
41
41
|
// src/core/middleware.ts
|
|
42
42
|
var UTM_PARAMS = [
|