@preprio/toolkit 0.5.0-rc.1 → 0.6.0-alpha.0

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.
Files changed (69) hide show
  1. package/README.md +300 -19
  2. package/dist/astro/chunk-3UVUDDNY.js +173 -0
  3. package/dist/astro/graphql.cjs +983 -0
  4. package/dist/astro/graphql.d.cts +231 -0
  5. package/dist/astro/graphql.d.ts +231 -0
  6. package/dist/astro/graphql.js +893 -0
  7. package/dist/astro/index-XT_z-RYT.d.cts +130 -0
  8. package/dist/astro/index-XT_z-RYT.d.ts +130 -0
  9. package/dist/astro/index.cjs +14 -1
  10. package/dist/astro/index.d.cts +1 -120
  11. package/dist/astro/index.d.ts +1 -120
  12. package/dist/astro/index.js +15 -153
  13. package/dist/{chunk-4JNKYIEV.js → chunk-BJ2LBLWO.js} +69 -268
  14. package/dist/chunk-DGZA7KX7.js +172 -0
  15. package/dist/chunk-YR7P4IWE.js +187 -0
  16. package/dist/graphql.cjs +894 -0
  17. package/dist/graphql.d.cts +231 -0
  18. package/dist/graphql.d.ts +231 -0
  19. package/dist/graphql.js +787 -0
  20. package/dist/index.cjs +74 -111
  21. package/dist/index.d.cts +11 -62
  22. package/dist/index.d.ts +11 -62
  23. package/dist/index.js +23 -167
  24. package/dist/nextjs/chunk-U4HXENF7.js +231 -0
  25. package/dist/nextjs/components.cjs +188 -114
  26. package/dist/nextjs/components.d.cts +33 -2
  27. package/dist/nextjs/components.d.ts +33 -2
  28. package/dist/nextjs/components.js +132 -4
  29. package/dist/nextjs/graphql.cjs +1315 -0
  30. package/dist/nextjs/graphql.d.cts +361 -0
  31. package/dist/nextjs/graphql.d.ts +361 -0
  32. package/dist/nextjs/graphql.js +1218 -0
  33. package/dist/nextjs/index.cjs +126 -6
  34. package/dist/nextjs/index.d.cts +53 -141
  35. package/dist/nextjs/index.d.ts +53 -141
  36. package/dist/nextjs/index.js +113 -203
  37. package/dist/nextjs/server-CjpELYvq.d.cts +156 -0
  38. package/dist/nextjs/server-CjpELYvq.d.ts +156 -0
  39. package/dist/nuxt/chunk-P6ENNAEE.js +417 -0
  40. package/dist/nuxt/graphql.cjs +990 -0
  41. package/dist/nuxt/graphql.d.cts +228 -0
  42. package/dist/nuxt/graphql.d.ts +228 -0
  43. package/dist/nuxt/graphql.js +894 -0
  44. package/dist/nuxt/index.cjs +14 -1
  45. package/dist/nuxt/index.d.cts +3 -148
  46. package/dist/nuxt/index.d.ts +3 -148
  47. package/dist/nuxt/index.js +15 -391
  48. package/dist/nuxt/server-8v6OHbsw.d.cts +158 -0
  49. package/dist/nuxt/server-8v6OHbsw.d.ts +158 -0
  50. package/dist/pixel-DwW7cXJm.d.cts +49 -0
  51. package/dist/pixel-DwW7cXJm.d.ts +49 -0
  52. package/dist/react/components.cjs +86 -112
  53. package/dist/react/components.js +86 -112
  54. package/dist/server-Brr1UgtW.d.ts +59 -0
  55. package/dist/server-DJuS3YU0.d.cts +59 -0
  56. package/dist/sveltekit/chunk-FSARVMG5.js +413 -0
  57. package/dist/sveltekit/graphql.cjs +996 -0
  58. package/dist/sveltekit/graphql.d.cts +233 -0
  59. package/dist/sveltekit/graphql.d.ts +233 -0
  60. package/dist/sveltekit/graphql.js +900 -0
  61. package/dist/sveltekit/index.cjs +14 -1
  62. package/dist/sveltekit/index.d.cts +3 -88
  63. package/dist/sveltekit/index.d.ts +3 -88
  64. package/dist/sveltekit/index.js +15 -387
  65. package/dist/sveltekit/server-CO847mEV.d.cts +98 -0
  66. package/dist/sveltekit/server-CO847mEV.d.ts +98 -0
  67. package/dist/{pixel-e1fUWl2Y.d.cts → types-BYH9XqmN.d.cts} +2 -50
  68. package/dist/{pixel-e1fUWl2Y.d.ts → types-BYH9XqmN.d.ts} +2 -50
  69. package/package.json +31 -1
@@ -0,0 +1,361 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { DocumentNode } from 'graphql';
3
+ import { P as PreprHeaders } from './server-CjpELYvq.cjs';
4
+ export { a as PreprError, i as isPersonalizedResponse } from './server-CjpELYvq.cjs';
5
+ import { ReactNode, ReactElement } from 'react';
6
+
7
+ /** Prepr's variant id for visitors outside every targeted segment. */
8
+ declare const PREPR_DEFAULT_VARIANT = "ALL_OTHER_USERS";
9
+
10
+ /** The variant ids Prepr chose for one visitor, per personalization group. */
11
+ type PreprDecisions = ReadonlyMap<string, readonly string[]>;
12
+ interface ResolveVariantsOptions {
13
+ /**
14
+ * The visitor's per-group variants (from a decision query). Without it,
15
+ * personalization groups show their default variant.
16
+ */
17
+ decisions?: PreprDecisions;
18
+ /** The visitor's A/B bucket. Without it, A/B groups use the decision, then `A`. */
19
+ bucket?: string;
20
+ }
21
+ /**
22
+ * Keeps, in every list of a variants query result, only the variants this
23
+ * visitor gets, at any depth (page stacks, nested stacks, personalized
24
+ * buttons). Items outside a group are kept as they are.
25
+ */
26
+ declare function resolveVariants<T>(data: T, options?: ResolveVariantsOptions): T;
27
+
28
+ /** A run of stack items that renders the same way: see {@link splitStack}. */
29
+ interface PreprStackBlock<T> {
30
+ key: string;
31
+ items: T[];
32
+ /**
33
+ * Rendered position of the block's first item: a group (whichever variant
34
+ * shows) takes one position, any other item one each. Use it for anything
35
+ * that depends on page position, like which section owns the h1.
36
+ */
37
+ index: number;
38
+ /**
39
+ * Some item in the block (or nested inside one) is a segment variant, so
40
+ * the block depends on the visitor and is decided per request.
41
+ */
42
+ personalized: boolean;
43
+ /**
44
+ * Some item in the block (or nested inside one) is an A/B variant. Without
45
+ * a known bucket, such a block renders once per bucket and the visitor's
46
+ * bucket is picked in the browser before first paint.
47
+ */
48
+ abTest: boolean;
49
+ }
50
+ /**
51
+ * Splits a variants-query stack into blocks. Each personalization or A/B
52
+ * group becomes one block (a variant can hold a different number of items
53
+ * than the default); consecutive items that never vary are merged.
54
+ */
55
+ declare function splitStack<T>(items: readonly T[]): PreprStackBlock<T>[];
56
+
57
+ /** The attribute Prepr's tracking pixel reads impressions and conversions from. */
58
+ interface PreprVariantProps {
59
+ 'data-prepr-variant-key'?: string;
60
+ }
61
+ /**
62
+ * Attributes that let Prepr's tracking pixel count impressions of this item's
63
+ * variant, for an item from a query run with `variants: true`. Spread them on
64
+ * the element that shows the item; an item outside any personalization or A/B
65
+ * group gets none.
66
+ *
67
+ * Items rendered by the Next.js `PreprStack` are tagged for you. Reach for this
68
+ * for items nested inside them (cards, buttons, models in a section) and in
69
+ * frameworks without `PreprStack`.
70
+ *
71
+ * @example
72
+ * <article {...preprVariantProps(card)}>…</article>
73
+ */
74
+ declare function preprVariantProps(item: unknown): PreprVariantProps;
75
+
76
+ /**
77
+ * A GraphQL document accepted by `query`: a plain string, graphql-codegen's
78
+ * `TypedDocumentString` (`documentMode: 'string'`), or any `DocumentNode`,
79
+ * including codegen's default `TypedDocumentNode` output.
80
+ *
81
+ * The phantom `__apiType` field is how codegen attaches result and variable
82
+ * types; matching it structurally keeps `@graphql-typed-document-node/core`
83
+ * out of this package.
84
+ */
85
+ type PreprDocument<TResult, TVariables> = string | ({
86
+ __apiType?: (variables: TVariables) => TResult;
87
+ } & (String | DocumentNode));
88
+ /**
89
+ * How stega markers are stripped from the response:
90
+ * - `'urls'`: only from values under URL-like keys (`url`, `slug`, `link`,
91
+ * `href`), where markers break links. Text keeps its markers for
92
+ * click-to-edit.
93
+ * - `'all'`: from every string.
94
+ * - `'none'`: never.
95
+ */
96
+ type PreprStegaMode = 'urls' | 'all' | 'none';
97
+ interface PreprClientOptions {
98
+ /** Full Prepr GraphQL URL, `https://graphql.prepr.io/<token>`. */
99
+ url: string;
100
+ /**
101
+ * Fetch implementation. Defaults to the global `fetch`, read at call time so
102
+ * framework-patched versions (such as Next.js's) are used.
103
+ */
104
+ fetch?: typeof fetch;
105
+ /**
106
+ * Preview mode: every query skips the HTTP cache so editors see each edit,
107
+ * and stega defaults to `'urls'` so click-to-edit keeps working.
108
+ */
109
+ preview?: boolean;
110
+ /** Default: `'urls'` when `preview` is true, otherwise `'all'`. */
111
+ stega?: PreprStegaMode;
112
+ /**
113
+ * Send queries as a GET with the request body base64-encoded in a
114
+ * `Fastly-Query` header, which Prepr's edge serves from the nearest point
115
+ * of presence (about half the latency of a POST in our measurements).
116
+ * Queries too large for a header still go as a POST. Off by default: it
117
+ * relies on Prepr edge behavior that is not part of the documented API.
118
+ */
119
+ fastlyQuery?: boolean;
120
+ /**
121
+ * @internal Fetch options for the one-time schema introspection, so a
122
+ * framework wrapper can put it in its data cache.
123
+ */
124
+ schemaInit?: RequestInit;
125
+ /**
126
+ * Headers sent with every request, such as a header a proxy in front of the
127
+ * API requires. They are fixed when the client is created, so they cannot
128
+ * vary per visitor and do not stop responses from being cached. Put
129
+ * per-request values in the query's `headers` instead.
130
+ */
131
+ headers?: HeadersInit;
132
+ }
133
+ interface PreprQueryOptions {
134
+ /**
135
+ * Prepr headers to forward, typically from `getPreprHeaders`. Passing any
136
+ * headers object, even an empty one, makes the request `no-store`: the
137
+ * response may be personalized and must not be shared between visitors.
138
+ */
139
+ headers?: PreprHeaders | HeadersInit;
140
+ /**
141
+ * Extra fetch options such as `signal`. `method` and `body` are set by the
142
+ * client. `init.headers` are merged like `headers`, and also make the
143
+ * request `no-store`.
144
+ */
145
+ init?: RequestInit;
146
+ /** Per-query fetch, overriding the client's (e.g. SvelteKit's `event.fetch`). */
147
+ fetch?: typeof fetch;
148
+ /**
149
+ * Fetch every variant instead of the visitor's: `personalize: false` is
150
+ * added to every personalizable field in the query, at any depth, and each
151
+ * item gets its `_context` (under `_preprContext`). The result is the same
152
+ * for every visitor, so it can be cached; pass it to `resolveVariants` with
153
+ * the visitor's `decide()` result to get what they see.
154
+ *
155
+ * The client learns which fields are personalizable from one schema
156
+ * introspection request, made on first use.
157
+ */
158
+ variants?: boolean;
159
+ }
160
+ /** Options for `decide()`. */
161
+ interface PreprDecideOptions extends Omit<PreprQueryOptions, 'variants'> {
162
+ /**
163
+ * The `variants: true` result this decision is for (the whole `data`
164
+ * object). The decision query then asks only about the fields that hold
165
+ * segment personalization groups, and is skipped entirely when there are
166
+ * none, e.g. on pages with only A/B tests or no personalization.
167
+ */
168
+ from?: unknown;
169
+ /**
170
+ * @internal Ask only about the first personalizable list on each path,
171
+ * without `from`: what middleware needs for groups at the top of a stack.
172
+ */
173
+ topLevelOnly?: boolean;
174
+ }
175
+ interface PreprQueryResult<TResult> {
176
+ data: TResult;
177
+ /**
178
+ * True when Prepr reports, through the response's `Vary` header, that the
179
+ * queried fields hold segment variants or A/B tests.
180
+ */
181
+ personalized: boolean;
182
+ }
183
+
184
+ /** The cookie `resolveDecisions` writes and the `PreprStack` switch reads. */
185
+ declare const PREPR_DECISIONS_COOKIE = "prepr_decisions";
186
+
187
+ interface PreprStackProps<T> {
188
+ /** A stack (e.g. `page.content`) from a query run with `variants: true`. */
189
+ items: readonly T[];
190
+ /**
191
+ * The visitor's decisions, as a promise from `decide()`. Only the parts
192
+ * that vary per segment wait for it; a failed decision falls back to the
193
+ * default variants instead of failing the page.
194
+ */
195
+ decisions: Promise<PreprDecisions>;
196
+ /**
197
+ * Renders resolved stack items, e.g. your section switch. `index` is the
198
+ * position of the block's first item in the whole stack, for anything that
199
+ * depends on page position, such as which section owns the h1 or the
200
+ * priority image.
201
+ *
202
+ * `placeholder` is true while this render only stands in for the visitor's
203
+ * variant of a segment group (the default variant, shown until the
204
+ * decision streams in). Leave `data-prepr-variant-key` off it: the tracking
205
+ * pixel would count an impression of a variant the visitor may never get.
206
+ */
207
+ render: (items: T[], position: {
208
+ index: number;
209
+ placeholder: boolean;
210
+ }) => ReactNode;
211
+ /**
212
+ * The visitor's A/B bucket, when the route already knows it (for example
213
+ * from a URL segment). Leave it out to render both variants of each A/B
214
+ * test and pick the visitor's in the browser before first paint.
215
+ */
216
+ bucket?: string;
217
+ /** Nonce for the inline A/B switch script, under a strict CSP. */
218
+ nonce?: string;
219
+ /**
220
+ * Tag each personalization and A/B variant for Prepr's tracking pixel, so
221
+ * it counts impressions (and clicks on `data-prepr-variant-event` elements
222
+ * inside) without `data-prepr-variant-key` in your components. Default
223
+ * `false`.
224
+ *
225
+ * Each item's key goes on the element `render` returns for it, in order,
226
+ * once the page has hydrated. Each variant gets a wrapper with
227
+ * `display: contents`: it takes no box, but CSS child combinators (`>`) and
228
+ * `:first-child` from the stack's parent no longer reach the sections. Lists
229
+ * nested inside an item are not tagged; use `preprVariantProps` there.
230
+ */
231
+ trackVariants?: boolean;
232
+ /**
233
+ * How many blocks, from the top, are on screen before the visitor scrolls.
234
+ * Default `0`.
235
+ *
236
+ * Their segment groups do not stream: every variant is in the cached page,
237
+ * and the variant the proxy decided (`prepr.resolveDecisions`) shows before
238
+ * first paint, so nothing on screen changes while the page loads. Without
239
+ * a decision (no cookie yet, or the proxy's lookup failed) the default
240
+ * variant shows. Costs the HTML of the other variants; images in them still
241
+ * download. An item with a segment group nested inside it keeps streaming.
242
+ */
243
+ aboveTheFold?: number;
244
+ }
245
+ /**
246
+ * Renders a personalized stack with Cache Components / Partial Prerendering.
247
+ *
248
+ * - Items that never vary render in the static shell.
249
+ * - A/B tests render in the static shell too, once per bucket; a tiny inline
250
+ * script shows the visitor's bucket (from `resolvePreprBucket`'s cookie)
251
+ * before first paint, so no variant streams or swaps in. `A` is the
252
+ * default. Pass `bucket` instead when the route already knows it.
253
+ * - Each segment-personalized group, and each item with segment content
254
+ * nested inside it, gets its own `<Suspense>` boundary: the default
255
+ * variant (possibly empty) is the fallback, and this visitor's variant
256
+ * streams in once `decisions` resolves. Because `items` holds every
257
+ * variant, a group with no default variant still gets its place.
258
+ *
259
+ * @example
260
+ * const data = await getPage(slug); // 'use cache' + query(..., { variants: true })
261
+ * <PreprStack
262
+ * items={data.Page.content}
263
+ * decisions={prepr.decide(PageDocument, { slug }, { from: data })}
264
+ * render={(items, { index }) => <Sections sections={items} offset={index} />}
265
+ * />
266
+ */
267
+ declare function PreprStack<T>({ items, decisions, render, bucket, nonce, trackVariants, aboveTheFold, }: PreprStackProps<T>): ReactElement;
268
+
269
+ interface PreprNextClientOptions extends PreprClientOptions {
270
+ /** Tags set on every cacheable fetch, for `revalidateTag`. Default `['prepr']`. */
271
+ cacheTags?: string[];
272
+ /**
273
+ * `next.revalidate` for cacheable fetches. Unset leaves the installed Next
274
+ * version's default in place. `false` caches until `revalidateTag` runs,
275
+ * which suits a publish webhook calling `revalidateTag('prepr')`.
276
+ */
277
+ revalidate?: number | false;
278
+ }
279
+ interface PreprNextQueryOptions extends PreprQueryOptions {
280
+ /**
281
+ * Forward the visitor's Prepr headers (read through `next/headers`) and
282
+ * skip the Data Cache. The route renders per request.
283
+ */
284
+ personalize?: boolean;
285
+ }
286
+ interface PreprNextClient {
287
+ /**
288
+ * Run a GraphQL query against Prepr.
289
+ *
290
+ * Without `personalize`, the fetch is tagged for `revalidateTag` and may be
291
+ * served from the Data Cache. Do not swallow errors thrown here without
292
+ * `unstable_rethrow`: Next.js signals dynamic rendering by throwing from
293
+ * inside fetch, and the client passes those signals through untouched.
294
+ *
295
+ * @throws PreprError — `INVALID_DOCUMENT`, `FETCH_ERROR`, `HTTP_ERROR`,
296
+ * `INVALID_RESPONSE` or `GRAPHQL_ERROR`.
297
+ */
298
+ query<TResult, TVariables>(document: PreprDocument<TResult, TVariables>, variables?: TVariables, options?: PreprNextQueryOptions): Promise<PreprQueryResult<TResult>>;
299
+ /**
300
+ * Which variant of each personalization and A/B group this visitor gets,
301
+ * for the query you render with `variants: true`. Forwards the visitor's
302
+ * Prepr headers (read through `next/headers`); never cached. Prepr decides
303
+ * from everything the middleware forwards: customer profile, UTM
304
+ * parameters, visitor IP, HubSpot id.
305
+ *
306
+ * Start it without awaiting and hand the promise to `PreprStack`: only the
307
+ * parts that vary per visitor wait for it.
308
+ */
309
+ decide<TVariables>(document: PreprDocument<unknown, TVariables>, variables?: TVariables, options?: PreprDecideOptions): Promise<PreprDecisions>;
310
+ /**
311
+ * Whether the query's result holds an A/B test: see `resolvePreprBucket`'s
312
+ * `onlyIf`. Never reads request headers, so it also works in middleware.
313
+ */
314
+ hasAbTests<TVariables>(document: PreprDocument<unknown, TVariables>, variables?: TVariables): Promise<boolean>;
315
+ /**
316
+ * In middleware (`proxy.ts`): asks Prepr which segment variant this visitor
317
+ * gets for each personalization group on the page, and stores the answer in
318
+ * the `prepr_decisions` cookie on `response`. `PreprStack` with
319
+ * `aboveTheFold` then shows those variants before first paint instead of
320
+ * streaming them in.
321
+ *
322
+ * Call it after `createPreprMiddleware`, whose visitor headers (customer id,
323
+ * UTM parameters, IP, HubSpot id) it forwards. A decision is an uncached
324
+ * Prepr request, made once per visit by default (see `reuse`); limit it
325
+ * with `onlyIf` to pages whose personalized content is above the fold.
326
+ * Pages without segment groups are skipped after a cached check. Never throws and gives up after `timeoutMs`
327
+ * (default 1000): the default variant shows instead.
328
+ */
329
+ resolveDecisions<TVariables>(request: NextRequest, response: NextResponse, document: PreprDocument<unknown, TVariables>, variables?: TVariables, options?: {
330
+ timeoutMs?: number;
331
+ /**
332
+ * Reuse the stored decision for the rest of the visit instead of asking
333
+ * Prepr on every page view. Default `true`. Asks again when UTM
334
+ * parameters arrive, on a first visit, and when preview forces a
335
+ * segment. A visitor who joins a segment mid-visit (say, by submitting
336
+ * a form) sees the change from their next asked request.
337
+ */
338
+ reuse?: boolean;
339
+ /** Skip the decision when this resolves to false, e.g. for pages without a personalized hero. */
340
+ onlyIf?: () => boolean | Promise<boolean>;
341
+ }): Promise<void>;
342
+ }
343
+ /**
344
+ * Prepr GraphQL client for Next.js (App Router, server only).
345
+ *
346
+ * Headerless queries go through the Data Cache with `cacheTags`; queries with
347
+ * `personalize: true` forward the visitor's Prepr headers and are never
348
+ * cached.
349
+ *
350
+ * With Cache Components, run headerless queries inside `'use cache'` (the
351
+ * client's fetch tags carry over to the entry, so `revalidateTag` reaches it)
352
+ * and `personalize: true` queries inside `<Suspense>`: `personalize` reads
353
+ * request headers, which `'use cache'` forbids. A headerless query outside
354
+ * both only prerenders when `revalidate` is set. `preview: true` works inside `'use cache'`, but the entry is still
355
+ * cached unless draft mode is enabled, which makes Next skip cached entries.
356
+ *
357
+ * Requires the `graphql` package, which graphql-codegen already installs.
358
+ */
359
+ declare function createPreprClient(options: PreprNextClientOptions): PreprNextClient;
360
+
361
+ export { PREPR_DECISIONS_COOKIE, PREPR_DEFAULT_VARIANT, type PreprDecideOptions, type PreprDecisions, type PreprDocument, type PreprNextClient, type PreprNextClientOptions, type PreprNextQueryOptions, type PreprQueryResult, PreprStack, type PreprStackBlock, type PreprStackProps, type PreprStegaMode, type PreprVariantProps, type ResolveVariantsOptions, createPreprClient, preprVariantProps, resolveVariants, splitStack };