@preprio/toolkit 0.6.0-alpha.2 → 0.6.0-alpha.4

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 (42) hide show
  1. package/README.md +46 -30
  2. package/dist/astro/{chunk-U7VUQL57.js → chunk-TLUVG5P7.js} +1 -1
  3. package/dist/astro/graphql.js +1 -1
  4. package/dist/astro/index.cjs +1 -1
  5. package/dist/astro/index.js +1 -1
  6. package/dist/{chunk-VV4PRH7W.js → chunk-EE3GXKCF.js} +1 -1
  7. package/dist/{chunk-7HSMUULV.js → chunk-MZW2RVGY.js} +1 -1
  8. package/dist/{chunk-UYYTYSOD.js → chunk-XQ4DDVSW.js} +1 -1
  9. package/dist/{chunk-DS3QA3HF.js → chunk-YNZUYENT.js} +1 -1
  10. package/dist/graphql/codegen.js +3 -3
  11. package/dist/graphql.js +3 -3
  12. package/dist/index.cjs +1 -1
  13. package/dist/index.d.cts +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +3 -3
  16. package/dist/nextjs/chunk-HP2SJJSL.js +544 -0
  17. package/dist/nextjs/components.cjs +166 -2
  18. package/dist/nextjs/components.d.cts +32 -4
  19. package/dist/nextjs/components.d.ts +32 -4
  20. package/dist/nextjs/components.js +169 -4
  21. package/dist/nextjs/graphql.cjs +588 -50
  22. package/dist/nextjs/graphql.d.cts +95 -28
  23. package/dist/nextjs/graphql.d.ts +95 -28
  24. package/dist/nextjs/graphql.js +287 -50
  25. package/dist/nextjs/index.cjs +3 -1
  26. package/dist/nextjs/index.d.cts +33 -3
  27. package/dist/nextjs/index.d.ts +33 -3
  28. package/dist/nextjs/index.js +8 -313
  29. package/dist/nextjs/{server-CjpELYvq.d.cts → server-BL-sqZvu.d.cts} +1 -1
  30. package/dist/nextjs/{server-CjpELYvq.d.ts → server-BL-sqZvu.d.ts} +1 -1
  31. package/dist/nuxt/{chunk-GMWF6PQ5.js → chunk-QVY3D5B2.js} +1 -1
  32. package/dist/nuxt/graphql.js +1 -1
  33. package/dist/nuxt/index.cjs +1 -1
  34. package/dist/nuxt/index.js +1 -1
  35. package/dist/react/components.cjs +1 -1
  36. package/dist/react/components.js +1 -1
  37. package/dist/sveltekit/{chunk-5SQUG2L4.js → chunk-VSN5EX3U.js} +1 -1
  38. package/dist/sveltekit/graphql.js +1 -1
  39. package/dist/sveltekit/index.cjs +1 -1
  40. package/dist/sveltekit/index.js +1 -1
  41. package/package.json +1 -1
  42. package/dist/nextjs/chunk-ZIUKEGEN.js +0 -231
package/README.md CHANGED
@@ -345,52 +345,59 @@ Conversions stay your choice: put `data-prepr-variant-event` (or `data-prepr-var
345
345
 
346
346
  - A hidden A/B variant never scrolls into view, so it records no impression.
347
347
 
348
- ##### A/B tests
348
+ ##### The proxy
349
349
 
350
- `PreprStack` renders both variants of an A/B test in the static shell and shows the visitor's one before first paint; A is the default, also for crawlers. The visitor's bucket comes from a cookie set in your middleware. Prepr assigns the bucket, once per visitor, and only on pages that have an A/B test:
350
+ One call in `proxy.ts` gives `PreprStack` what it needs from the request: the visitor's Prepr headers and customer id, the A/B bucket, and the segment variants of the groups at the top of the page. Tell it which query each URL renders; most sites route every page through one query:
351
351
 
352
352
  ```ts
353
353
  // proxy.ts
354
- const response = createPreprMiddleware(request, { preview });
355
- await resolvePreprBucket(request, response, {
356
- url: process.env.PREPR_GRAPHQL_URL!,
357
- preview,
358
- onlyIf: () => prepr.hasAbTests(PageDocument, { slug }),
354
+ import { createPreprProxy } from '@preprio/toolkit/nextjs/graphql';
355
+
356
+ const handlePrepr = createPreprProxy({
357
+ client: prepr,
358
+ // URL → the query that page renders with `variants: true`; null for non-CMS URLs.
359
+ page: (url) => ({
360
+ document: PageDocument,
361
+ variables: { slug: url.pathname },
362
+ }),
363
+ preview: process.env.PREPR_ENV === 'preview',
359
364
  });
360
- return response;
365
+
366
+ export function proxy(request: NextRequest) {
367
+ return handlePrepr(request); // add your own cookies or headers to this response
368
+ }
369
+ ```
370
+
371
+ ```tsx
372
+ // app/layout.tsx, once
373
+ import { PreprPersonalizationSwitch } from '@preprio/toolkit/nextjs';
374
+
375
+ <PreprPersonalizationSwitch />;
361
376
  ```
362
377
 
363
- Sites without A/B tests leave this out. Under a strict Content-Security-Policy, pass `nonce` to `PreprStack`.
378
+ API routes, `/_next/` and URLs whose last segment has a file extension (`/logo.png`, but also `/docs/v1.2`) are skipped, and so are pages without A/B tests or segment groups at the top (after a cached check). Pass `onlyIf: (request) => boolean` to skip Prepr for some requests, such as crawlers: they carry no cookies, so every crawler request is a first visit that waits for Prepr. In preview it also sends the editor through the [draft route](#preview) first. The pieces it combines stay exported (`createPreprMiddleware`, `resolvePreprBucket`, `prepr.resolveDecisions`, `preprDraftRedirect`) for proxies that need them separately.
379
+
380
+ ##### A/B tests
381
+
382
+ `PreprStack` renders both variants of an A/B test in the static shell and shows the visitor's one before first paint; A is the default, also for crawlers. Prepr assigns the bucket once per visitor; the proxy stores it in a cookie, and only on pages that have an A/B test. Under a strict Content-Security-Policy, pass `nonce` to `PreprStack`.
364
383
 
365
384
  ##### Personalized content above the fold
366
385
 
367
- A segment group streams in: the default variant shows first, then this visitor's variant replaces it. Below the fold nobody sees that. In the hero they do, so for the blocks on screen before the visitor scrolls, decide in the proxy instead:
386
+ A segment group streams in: the default variant shows first, then this visitor's variant replaces it. Below the fold nobody sees that. On the first screen they do, so the first block of a `PreprStack` is decided in the proxy instead: every variant of its segment groups is in the cached page, and the variant the proxy decided shows before first paint. Set `aboveTheFold` to the number of blocks on your first screen (per page; take the larger screen when it differs per device), or `0` to stream everything:
368
387
 
369
388
  ```tsx
370
- <PreprStack items={data.Page.content} decisions={decisions} aboveTheFold={1} render={…} />
389
+ <PreprStack items={data.Page.content} decisions={decisions} aboveTheFold={3} render={…} />
371
390
  ```
372
391
 
373
- ```ts
374
- // proxy.ts, after createPreprMiddleware
375
- await prepr.resolveDecisions(
376
- request,
377
- response,
378
- PageDocument,
379
- { slug },
380
- {
381
- // Only pages whose personalized content is above the fold need it.
382
- onlyIf: () => slug === '/',
383
- },
384
- );
385
- ```
392
+ Decisions stay fresh within a visit, so a visitor who joins a segment (by reading pages, clicking, submitting a form) sees its variant on their next page view, without anything on screen changing:
386
393
 
387
- `PreprStack` puts every variant of those blocks' segment groups in the cached page, and the A/B switch script shows the variant the proxy decided before first paint: nothing on screen changes. `resolveDecisions` forwards the visitor's headers (customer id, UTM parameters, IP, HubSpot id) to Prepr and stores the answer in the `prepr_decisions` cookie.
388
-
389
- - It asks Prepr once per visit, not per page view: later requests reuse the stored decision unless something arrives that can change the segment (UTM parameters, a first visit, a segment forced in preview). Pass `reuse: false` to ask on every request. A visitor who joins a segment mid-visit sees it from the next request that asks.
390
- - That request is uncached, so its time depends on how far your functions run from Prepr: deploy them in the region closest to your visitors and to Prepr, and use `onlyIf` to limit it to pages that need it. Pages without segment groups are skipped after a cached check (the same one `hasAbTests` uses).
391
- - Without a decision (the lookup failed or took longer than `timeoutMs`, default 1000), the default variant shows.
392
- - The page carries the HTML of every variant of those groups, and images in hidden variants still download.
394
+ - `PreprPersonalizationSwitch` asks the proxy for new decisions in the background: about 3 and 10 seconds after each page view (Prepr needs a few seconds to process the visitor's activity), when the visitor points at a link to a personalized page, and when the tab comes back into view. A refresh is one small request that the proxy answers with cookies and no page.
395
+ - The proxy keeps a decision that covers the page for `freshFor` (default 10 seconds), so most page views, client-side or full, wait for nothing. An older decision, a first visit, UTM parameters and a segment forced in preview make it ask Prepr; the page then waits for the answer, at most `timeoutMs` (default 1000), after which the default variant shows.
396
+ - A decision that arrives while a page is being read applies from the next page view: content never changes under the reader. A visitor who joins a segment and clicks within a few seconds sees it one page view later.
397
+ - Next.js prefetches links as they scroll into view and on hover; a prefetch of a page without a fresh decision gets one, so even the first visit to a personalized page shows its variant at once.
398
+ - The page carries the HTML of every variant of those groups. `render` gets `defaultVariant`: give `priority` images only to the default variant and `loading="lazy"` to the others, or the browser downloads every variant's hero image.
393
399
  - An item with a segment group nested inside it keeps streaming; only groups at the top level of the list are decided in the proxy.
400
+ - Decisions are uncached Prepr requests, so their time depends on how far your functions run from Prepr: deploy them in the region closest to your visitors and to Prepr.
394
401
 
395
402
  ##### Preview
396
403
 
@@ -954,6 +961,15 @@ The toolbar. Takes the props returned by `getToolbarProps`, and renders nothing
954
961
  <PreprToolbar {...toolbarProps} />
955
962
  ```
956
963
 
964
+ #### `PreprPersonalizationSwitch`
965
+
966
+ From `@preprio/toolkit/nextjs`. Shows the segment variants the proxy decided for the groups above the fold before each page paints, and refreshes those decisions in the background (see [Personalized content above the fold](#personalized-content-above-the-fold)). Mount it once in the root layout. The decisions come from `createPreprProxy` or `prepr.resolveDecisions` in `proxy.ts`; the background refreshes only from `createPreprProxy` (with a hand-written `resolveDecisions` proxy, the first refresh gets a page back and refreshing stops for the browser session). Renders nothing.
967
+
968
+ | Prop | Default | Description |
969
+ | ---------- | ------- | ----------------------------------------------- |
970
+ | `refresh` | `true` | Refresh decisions in the background. |
971
+ | `freshFor` | `10000` | Same as `createPreprProxy`'s `freshFor`, in ms. |
972
+
957
973
  #### `PreprPreview`
958
974
 
959
975
  From `@preprio/toolkit/react`. The framework-free equivalent of `PreprToolbar`: same preview runtime, with the router binding left to you. Renders nothing.
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var VERSION = "0.6.0-alpha.2";
2
+ var VERSION = "0.6.0-alpha.4";
3
3
 
4
4
  // src/core/features.ts
5
5
  function isEnabled(config) {
@@ -3,7 +3,7 @@ import {
3
3
  getPreprHeadersFromHeaders,
4
4
  isPersonalizedResponse,
5
5
  validatePreprToken
6
- } from "./chunk-U7VUQL57.js";
6
+ } from "./chunk-TLUVG5P7.js";
7
7
 
8
8
  // src/core/variants/constants.ts
9
9
  var PREPR_CONTEXT_ALIAS = "_preprContext";
@@ -31,7 +31,7 @@ __export(astro_exports, {
31
31
  module.exports = __toCommonJS(astro_exports);
32
32
 
33
33
  // src/version.ts
34
- var VERSION = "0.6.0-alpha.2";
34
+ var VERSION = "0.6.0-alpha.4";
35
35
 
36
36
  // src/core/features.ts
37
37
  function isEnabled(config) {
@@ -7,7 +7,7 @@ import {
7
7
  getToolbarPropsFromHeaders,
8
8
  isPersonalizedResponse,
9
9
  resolveFeatures
10
- } from "./chunk-U7VUQL57.js";
10
+ } from "./chunk-TLUVG5P7.js";
11
11
 
12
12
  // src/core/constants.ts
13
13
  var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var VERSION = "0.6.0-alpha.2";
2
+ var VERSION = "0.6.0-alpha.4";
3
3
 
4
4
  // src/core/features.ts
5
5
  function isEnabled(config) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  PreprError
3
- } from "./chunk-UYYTYSOD.js";
3
+ } from "./chunk-XQ4DDVSW.js";
4
4
 
5
5
  // src/core/variants/constants.ts
6
6
  var PREPR_CONTEXT_ALIAS = "_preprContext";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  VERSION,
3
3
  resolveFeatures
4
- } from "./chunk-VV4PRH7W.js";
4
+ } from "./chunk-EE3GXKCF.js";
5
5
 
6
6
  // src/core/server.ts
7
7
  var PreprError = class extends Error {
@@ -11,7 +11,7 @@ import {
11
11
  ALL_FEATURES_ENABLED,
12
12
  VERSION,
13
13
  resolveFeatures
14
- } from "./chunk-VV4PRH7W.js";
14
+ } from "./chunk-EE3GXKCF.js";
15
15
 
16
16
  // src/core/stega/dom.ts
17
17
  var IGNORED_ANCESTORS = "script, style, noscript";
@@ -2,9 +2,9 @@ import {
2
2
  toDecisionQuery,
3
3
  toSchemaInfo,
4
4
  toVariantsQuery
5
- } from "../chunk-7HSMUULV.js";
6
- import "../chunk-UYYTYSOD.js";
7
- import "../chunk-VV4PRH7W.js";
5
+ } from "../chunk-MZW2RVGY.js";
6
+ import "../chunk-XQ4DDVSW.js";
7
+ import "../chunk-EE3GXKCF.js";
8
8
 
9
9
  // src/codegen/index.ts
10
10
  function typeRef(type) {
package/dist/graphql.js CHANGED
@@ -21,12 +21,12 @@ import {
21
21
  toSchemaInfo,
22
22
  toVariantsDocument,
23
23
  toVariantsQuery
24
- } from "./chunk-7HSMUULV.js";
24
+ } from "./chunk-MZW2RVGY.js";
25
25
  import {
26
26
  PreprError,
27
27
  validatePreprToken
28
- } from "./chunk-UYYTYSOD.js";
29
- import "./chunk-VV4PRH7W.js";
28
+ } from "./chunk-XQ4DDVSW.js";
29
+ import "./chunk-EE3GXKCF.js";
30
30
 
31
31
  // src/core/variants/probe-document.ts
32
32
  function probeSelectionSet(set, fragments) {
package/dist/index.cjs CHANGED
@@ -45,7 +45,7 @@ __export(src_exports, {
45
45
  module.exports = __toCommonJS(src_exports);
46
46
 
47
47
  // src/version.ts
48
- var VERSION = "0.6.0-alpha.2";
48
+ var VERSION = "0.6.0-alpha.4";
49
49
 
50
50
  // src/core/features.ts
51
51
  function isEnabled(config) {
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.2";
10
+ declare const VERSION = "0.6.0-alpha.4";
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.2";
10
+ declare const VERSION = "0.6.0-alpha.4";
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-DS3QA3HF.js";
18
+ } from "./chunk-YNZUYENT.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-UYYTYSOD.js";
35
+ } from "./chunk-XQ4DDVSW.js";
36
36
  import {
37
37
  VERSION,
38
38
  resolveFeatures
39
- } from "./chunk-VV4PRH7W.js";
39
+ } from "./chunk-EE3GXKCF.js";
40
40
 
41
41
  // src/core/middleware.ts
42
42
  var UTM_PARAMS = [