@preprio/toolkit 0.6.0-alpha.0 → 0.6.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +268 -59
  2. package/dist/astro/{chunk-3UVUDDNY.js → chunk-CG2QBHNU.js} +1 -1
  3. package/dist/astro/graphql.cjs +154 -59
  4. package/dist/astro/graphql.d.cts +75 -6
  5. package/dist/astro/graphql.d.ts +75 -6
  6. package/dist/astro/graphql.js +150 -59
  7. package/dist/astro/index.cjs +1 -1
  8. package/dist/astro/index.js +1 -1
  9. package/dist/chunk-3NCEVJJ3.js +10 -0
  10. package/dist/{chunk-BJ2LBLWO.js → chunk-LIDUJWWJ.js} +6 -4
  11. package/dist/chunk-M3RBYJ5J.js +499 -0
  12. package/dist/{chunk-DGZA7KX7.js → chunk-S5RWVMDJ.js} +0 -24
  13. package/dist/{chunk-YR7P4IWE.js → chunk-SF6TN4JR.js} +1 -9
  14. package/dist/chunk-TN6SNAR2.js +26 -0
  15. package/dist/graphql/codegen.cjs +557 -0
  16. package/dist/graphql/codegen.d.cts +23 -0
  17. package/dist/graphql/codegen.d.ts +23 -0
  18. package/dist/graphql/codegen.js +99 -0
  19. package/dist/graphql.cjs +185 -59
  20. package/dist/graphql.d.cts +110 -6
  21. package/dist/graphql.d.ts +110 -6
  22. package/dist/graphql.js +91 -430
  23. package/dist/index.cjs +1 -1
  24. package/dist/index.d.cts +1 -1
  25. package/dist/index.d.ts +1 -1
  26. package/dist/index.js +10 -6
  27. package/dist/nextjs/chunk-B4GVZEOL.js +549 -0
  28. package/dist/nextjs/components.cjs +297 -81
  29. package/dist/nextjs/components.d.cts +26 -11
  30. package/dist/nextjs/components.d.ts +26 -11
  31. package/dist/nextjs/components.js +289 -73
  32. package/dist/nextjs/graphql.cjs +952 -315
  33. package/dist/nextjs/graphql.d.cts +225 -83
  34. package/dist/nextjs/graphql.d.ts +225 -83
  35. package/dist/nextjs/graphql.js +634 -314
  36. package/dist/nextjs/index.cjs +116 -54
  37. package/dist/nextjs/index.d.cts +61 -23
  38. package/dist/nextjs/index.d.ts +61 -23
  39. package/dist/nextjs/index.js +97 -299
  40. package/dist/nextjs/{server-CjpELYvq.d.ts → server-BL-sqZvu.d.cts} +1 -1
  41. package/dist/nextjs/{server-CjpELYvq.d.cts → server-BL-sqZvu.d.ts} +1 -1
  42. package/dist/nuxt/{chunk-P6ENNAEE.js → chunk-FMC3DP7N.js} +1 -1
  43. package/dist/nuxt/graphql.cjs +154 -59
  44. package/dist/nuxt/graphql.d.cts +75 -6
  45. package/dist/nuxt/graphql.d.ts +75 -6
  46. package/dist/nuxt/graphql.js +150 -59
  47. package/dist/nuxt/index.cjs +1 -1
  48. package/dist/nuxt/index.js +1 -1
  49. package/dist/react/components.cjs +1 -1
  50. package/dist/react/components.js +1 -1
  51. package/dist/sveltekit/{chunk-FSARVMG5.js → chunk-AJPKBAHO.js} +1 -1
  52. package/dist/sveltekit/graphql.cjs +154 -59
  53. package/dist/sveltekit/graphql.d.cts +75 -6
  54. package/dist/sveltekit/graphql.d.ts +75 -6
  55. package/dist/sveltekit/graphql.js +150 -59
  56. package/dist/sveltekit/index.cjs +1 -1
  57. package/dist/sveltekit/index.js +1 -1
  58. package/package.json +6 -1
  59. package/dist/nextjs/chunk-U4HXENF7.js +0 -231
@@ -20,12 +20,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/nextjs/index.ts
21
21
  var nextjs_exports = {};
22
22
  __export(nextjs_exports, {
23
- PREPR_BUCKET_COOKIE: () => PREPR_BUCKET_COOKIE,
24
23
  PreprError: () => PreprError,
24
+ PreprPersonalizationSwitch: () => import_components.PreprPersonalizationSwitch,
25
25
  PreprToolbar: () => import_components.PreprToolbar,
26
26
  PreprTrackingPixel: () => import_components.PreprTrackingPixel,
27
27
  createPreprDraftRoute: () => createPreprDraftRoute,
28
28
  createPreprMiddleware: () => createPreprMiddleware,
29
+ createPreprWebhookRoute: () => createPreprWebhookRoute,
29
30
  extractAccessToken: () => extractAccessToken,
30
31
  getActiveSegment: () => getActiveSegment,
31
32
  getActiveVariant: () => getActiveVariant,
@@ -34,7 +35,6 @@ __export(nextjs_exports, {
34
35
  getToolbarProps: () => getToolbarProps,
35
36
  isPersonalizedResponse: () => isPersonalizedResponse,
36
37
  preprDraftRedirect: () => preprDraftRedirect,
37
- resolvePreprBucket: () => resolvePreprBucket,
38
38
  validatePreprToken: () => validatePreprToken
39
39
  });
40
40
  module.exports = __toCommonJS(nextjs_exports);
@@ -43,7 +43,7 @@ module.exports = __toCommonJS(nextjs_exports);
43
43
  var import_server = require("next/server");
44
44
 
45
45
  // src/version.ts
46
- var VERSION = "0.6.0-alpha.0";
46
+ var VERSION = "0.6.0-alpha.10";
47
47
 
48
48
  // src/core/constants.ts
49
49
  var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
@@ -469,55 +469,6 @@ function isPersonalizedResponse(source) {
469
469
  return PERSONALIZED_VARY.test(headers2.get("vary") ?? "");
470
470
  }
471
471
 
472
- // src/nextjs/bucket.ts
473
- var PREPR_BUCKET_COOKIE = "prepr_ab_bucket";
474
- var isBucket = (value) => value === "A" || value === "B";
475
- function setBucketCookie(request, response, bucket) {
476
- response.cookies.set(PREPR_BUCKET_COOKIE, bucket, {
477
- path: "/",
478
- sameSite: "lax",
479
- // Readable on purpose: PreprStack's inline switch reads it before first
480
- // paint. It holds only "A" or "B".
481
- httpOnly: false,
482
- secure: request.nextUrl.protocol === "https:",
483
- maxAge: 60 * 60 * 24 * 365
484
- });
485
- }
486
- async function resolvePreprBucket(request, response, options) {
487
- const stored = request.cookies.get(PREPR_BUCKET_COOKIE)?.value;
488
- const forced = response.headers.get("x-middleware-request-prepr-abtesting") ?? request.cookies.get(COOKIE_VARIANT)?.value;
489
- if (options.preview && isBucket(forced)) {
490
- if (forced !== stored) setBucketCookie(request, response, forced);
491
- return forced;
492
- }
493
- if (isBucket(stored)) return stored;
494
- if (options.onlyIf) {
495
- const needed = await Promise.resolve().then(options.onlyIf).catch(() => true);
496
- if (!needed) return "A";
497
- }
498
- const customerId = request.cookies.get(COOKIE_UID)?.value ?? response.cookies.get(COOKIE_UID)?.value;
499
- if (!customerId) return "A";
500
- try {
501
- const res = await fetch(options.url, {
502
- method: "POST",
503
- headers: {
504
- "Content-Type": "application/json",
505
- "Prepr-Customer-Id": customerId
506
- },
507
- body: JSON.stringify({ query: "{ __typename }" }),
508
- cache: "no-store",
509
- signal: AbortSignal.timeout(options.timeoutMs ?? 1500)
510
- });
511
- const bucket = res.headers.get("x-prepr-customer-bucket");
512
- if (isBucket(bucket)) {
513
- setBucketCookie(request, response, bucket);
514
- return bucket;
515
- }
516
- } catch {
517
- }
518
- return "A";
519
- }
520
-
521
472
  // src/nextjs/draft.ts
522
473
  var import_headers2 = require("next/headers");
523
474
  var import_navigation = require("next/navigation");
@@ -568,16 +519,128 @@ function createPreprDraftRoute(options) {
568
519
  };
569
520
  }
570
521
 
522
+ // src/nextjs/webhook.ts
523
+ var import_cache = require("next/cache");
524
+
525
+ // src/core/graphql/cache-tags.ts
526
+ var PREPR_ANY_ITEM_TAG = "prepr:any";
527
+ var PREPR_SCHEMA_TAG = "prepr:schema";
528
+ var preprItemTag = (id) => `prepr:${id}`;
529
+ var NOT_ALPHANUMERIC = /[^a-z0-9]/g;
530
+ var preprModelTag = (model) => `prepr:model:${model.toLowerCase().replace(NOT_ALPHANUMERIC, "")}`;
531
+
532
+ // src/nextjs/webhook.ts
533
+ var CONTENT_EVENTS = /* @__PURE__ */ new Set([
534
+ "content_item.published",
535
+ "content_item.unpublished",
536
+ "content_item.deleted",
537
+ "content_item.invalidated",
538
+ "asset.changed",
539
+ "asset.deleted"
540
+ ]);
541
+ var MODEL_EVENTS = /* @__PURE__ */ new Set([
542
+ "content_item.published",
543
+ "content_item.unpublished",
544
+ "content_item.deleted"
545
+ ]);
546
+ var MAX_AGE_MS = 5 * 60 * 1e3;
547
+ var HEX_SIGNATURE = /^[0-9a-f]{64}$/i;
548
+ var BASE64_SIGNATURE = /^[A-Za-z0-9+/]{43}=$/;
549
+ var SIGNATURE_PREFIX = /^sha256=/i;
550
+ var MAX_ID_LENGTH = 200;
551
+ var text = (body, status) => new Response(body, { status, headers: { "Content-Type": "text/plain" } });
552
+ function signatureBytes(header) {
553
+ const value = header.trim().replace(SIGNATURE_PREFIX, "");
554
+ if (HEX_SIGNATURE.test(value)) {
555
+ const bytes = new Uint8Array(32);
556
+ for (let i = 0; i < 32; i++) {
557
+ bytes[i] = parseInt(value.slice(i * 2, i * 2 + 2), 16);
558
+ }
559
+ return bytes;
560
+ }
561
+ if (BASE64_SIGNATURE.test(value)) {
562
+ return Uint8Array.from(atob(value), (char) => char.charCodeAt(0));
563
+ }
564
+ return null;
565
+ }
566
+ async function verify(secret, signature, body) {
567
+ const key = await crypto.subtle.importKey(
568
+ "raw",
569
+ new TextEncoder().encode(secret),
570
+ { name: "HMAC", hash: "SHA-256" },
571
+ false,
572
+ ["verify"]
573
+ );
574
+ return crypto.subtle.verify("HMAC", key, signature, body);
575
+ }
576
+ function parse(body) {
577
+ try {
578
+ const value = JSON.parse(new TextDecoder().decode(body));
579
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
580
+ } catch {
581
+ return null;
582
+ }
583
+ }
584
+ function eventTime(value) {
585
+ if (typeof value === "number") return value * 1e3;
586
+ if (typeof value === "string") return Date.parse(value);
587
+ return NaN;
588
+ }
589
+ function field(value, key) {
590
+ return typeof value === "object" && value !== null ? value[key] : void 0;
591
+ }
592
+ var usable = (value) => typeof value === "string" && value !== "" && value.length <= MAX_ID_LENGTH;
593
+ function tagsFor(event) {
594
+ const name = event.event;
595
+ if (name === "schema.changed") return [PREPR_SCHEMA_TAG];
596
+ if (typeof name !== "string" || !CONTENT_EVENTS.has(name)) return [];
597
+ const tags = [PREPR_ANY_ITEM_TAG];
598
+ const id = field(event.payload, "id");
599
+ if (usable(id)) tags.push(preprItemTag(id));
600
+ const model = field(field(event.payload, "model"), "body_singular");
601
+ if (MODEL_EVENTS.has(name) && usable(model)) tags.push(preprModelTag(model));
602
+ return tags;
603
+ }
604
+ function createPreprWebhookRoute(options) {
605
+ const { profile = { expire: 0 } } = options;
606
+ return async (request) => {
607
+ const { secret } = options;
608
+ if (!secret) return text("Webhook secret not configured", 500);
609
+ const signature = signatureBytes(
610
+ request.headers.get("prepr-signature") ?? ""
611
+ );
612
+ let body;
613
+ try {
614
+ body = await request.arrayBuffer();
615
+ } catch {
616
+ return text("Unreadable body", 400);
617
+ }
618
+ if (!signature || !await verify(secret, signature, body)) {
619
+ return text("Invalid signature", 401);
620
+ }
621
+ const event = parse(body);
622
+ if (!event) return text("Invalid body", 400);
623
+ const sentAt = eventTime(event.created_on);
624
+ if (Number.isFinite(sentAt) && Math.abs(Date.now() - sentAt) > MAX_AGE_MS) {
625
+ return text("Expired", 401);
626
+ }
627
+ const tags = tagsFor(event);
628
+ for (const tag of tags) (0, import_cache.revalidateTag)(tag, profile);
629
+ return Response.json({ revalidated: tags });
630
+ };
631
+ }
632
+
571
633
  // src/nextjs/index.ts
572
634
  var import_components = require("./components.cjs");
573
635
  // Annotate the CommonJS export names for ESM import in node:
574
636
  0 && (module.exports = {
575
- PREPR_BUCKET_COOKIE,
576
637
  PreprError,
638
+ PreprPersonalizationSwitch,
577
639
  PreprToolbar,
578
640
  PreprTrackingPixel,
579
641
  createPreprDraftRoute,
580
642
  createPreprMiddleware,
643
+ createPreprWebhookRoute,
581
644
  extractAccessToken,
582
645
  getActiveSegment,
583
646
  getActiveVariant,
@@ -586,6 +649,5 @@ var import_components = require("./components.cjs");
586
649
  getToolbarProps,
587
650
  isPersonalizedResponse,
588
651
  preprDraftRedirect,
589
- resolvePreprBucket,
590
652
  validatePreprToken
591
653
  });
@@ -1,6 +1,6 @@
1
1
  import { NextRequest, NextResponse } from 'next/server';
2
- import { b as PreprFeatures, P as PreprHeaders, c as PreprToolbarProps, d as PreprToolbarComponentProps } from './server-CjpELYvq.cjs';
3
- export { a as PreprError, e as extractAccessToken, i as isPersonalizedResponse, v as validatePreprToken } from './server-CjpELYvq.cjs';
2
+ import { a as PreprFeatures, P as PreprHeaders, c as PreprToolbarProps, d as PreprToolbarComponentProps } from './server-BL-sqZvu.cjs';
3
+ export { b as PreprError, e as extractAccessToken, i as isPersonalizedResponse, v as validatePreprToken } from './server-BL-sqZvu.cjs';
4
4
 
5
5
  interface PreprMiddlewareOptions {
6
6
  /** Enable preview-mode headers (segment/AB overrides, preview bar). */
@@ -44,26 +44,6 @@ declare function getPreprHeaders(): Promise<PreprHeaders>;
44
44
  */
45
45
  declare function getToolbarProps(token: string, features?: PreprFeatures): Promise<PreprToolbarProps>;
46
46
 
47
- /** Cookie holding the visitor's A/B bucket, as Prepr assigned it. */
48
- declare const PREPR_BUCKET_COOKIE = "prepr_ab_bucket";
49
- type PreprBucket = 'A' | 'B';
50
- interface ResolvePreprBucketOptions {
51
- /** Full Prepr GraphQL URL. */
52
- url: string;
53
- /** Preview mode: the toolbar's A/B override wins. */
54
- preview?: boolean;
55
- /** Abort the one-time lookup after this many ms. Default 1500. */
56
- timeoutMs?: number;
57
- /**
58
- * Look the bucket up only when this resolves to true, typically "does this
59
- * page have an A/B test": `() => prepr.hasAbTests(PageDocument, { slug })`.
60
- * Only called for visitors without a bucket cookie, so visitors on pages
61
- * without A/B tests never wait for the lookup.
62
- */
63
- onlyIf?: () => boolean | Promise<boolean>;
64
- }
65
- declare function resolvePreprBucket(request: NextRequest, response: NextResponse, options: ResolvePreprBucketOptions): Promise<PreprBucket>;
66
-
67
47
  interface PreprDraftOptions {
68
48
  /** Whether this deployment serves preview content. Outside it, both helpers do nothing. */
69
49
  preview: boolean;
@@ -100,6 +80,47 @@ declare function preprDraftRedirect(request: NextRequest, options: PreprDraftOpt
100
80
  */
101
81
  declare function createPreprDraftRoute(options: Pick<PreprDraftOptions, 'preview'>): (request: Request) => Promise<Response>;
102
82
 
83
+ /** How a revalidated page refreshes, as Next.js's `revalidateTag` takes it. */
84
+ type PreprRevalidateProfile = string | {
85
+ expire?: number;
86
+ };
87
+ interface PreprWebhookOptions {
88
+ /**
89
+ * The webhook's ID, shown in Prepr under Settings → Webhooks. Prepr signs
90
+ * each request with it (HMAC-SHA256 of the body, in the `Prepr-Signature`
91
+ * header). Keep it in an environment variable.
92
+ */
93
+ secret: string | undefined;
94
+ /**
95
+ * Passed to `revalidateTag`. The default `{ expire: 0 }` shows a publish
96
+ * on the very next request; `'max'` serves the old page once more while
97
+ * the new one renders in the background.
98
+ */
99
+ profile?: PreprRevalidateProfile;
100
+ }
101
+ /**
102
+ * Route handler for a Prepr webhook that refreshes only the cached pages
103
+ * showing the changed item: those tagged by `query(..., { tagItems: true })`
104
+ * or `preprCacheTags`. On publish, unpublish and delete it also revalidates
105
+ * the item's `preprModelTag`, so list pages and lookups that found nothing
106
+ * pick up new and removed items. `schema.changed` refreshes the schema the
107
+ * client rewrites queries with.
108
+ *
109
+ * Other events (draft edits, new assets, visitors, segments) are
110
+ * acknowledged and revalidate nothing, so the webhook can subscribe to
111
+ * everything.
112
+ *
113
+ * Requests without a valid `Prepr-Signature` or older than five minutes get
114
+ * 401; without `secret` every request gets 500.
115
+ *
116
+ * @example
117
+ * // app/api/prepr/webhook/route.ts
118
+ * export const POST = createPreprWebhookRoute({
119
+ * secret: process.env.PREPR_WEBHOOK_SECRET,
120
+ * });
121
+ */
122
+ declare function createPreprWebhookRoute(options: PreprWebhookOptions): (request: Request) => Promise<Response>;
123
+
103
124
  /**
104
125
  * Loader + typed facade for Prepr's CDN tracking pixel, so consumers don't have
105
126
  * to paste a `<script>` tag. The pixel itself lives on the CDN; this only sets
@@ -153,5 +174,22 @@ declare function PreprTrackingPixel({ id, config, }: PreprTrackingPixelProps): n
153
174
  * `createPreprPreview` mounts imperatively.
154
175
  */
155
176
  declare function PreprToolbar({ options, ...props }: PreprToolbarComponentProps): null;
177
+ /**
178
+ * Gets each client-side navigation's decision in before the click: when the
179
+ * visitor hovers over a same-site link (or presses it, on touch devices), it
180
+ * asks `createPreprProxy` which variants they get on that page, so the new
181
+ * page paints with them. A click whose decision is not in yet (a quick tap,
182
+ * a link reached by keyboard) waits for it; one the visitor's recent
183
+ * decisions already cover, or one to a page without personalization, goes
184
+ * through at once.
185
+ *
186
+ * Mount it once in the root layout, so links on pages without a
187
+ * `PreprStack` are covered too. Clicks with a modifier key, links to other
188
+ * sites and links that open a new tab or download are left alone.
189
+ *
190
+ * @example
191
+ * <PreprPersonalizationSwitch />
192
+ */
193
+ declare function PreprPersonalizationSwitch(): null;
156
194
 
157
- export { PREPR_BUCKET_COOKIE, type PreprBucket, type PreprDraftOptions, type PreprMiddlewareOptions, PreprToolbar, PreprTrackingPixel, type PreprTrackingPixelProps, type ResolvePreprBucketOptions, createPreprDraftRoute, createPreprMiddleware, getActiveSegment, getActiveVariant, getPreprHeaders, getPreprUUID, getToolbarProps, preprDraftRedirect, resolvePreprBucket };
195
+ export { type PreprDraftOptions, type PreprMiddlewareOptions, PreprPersonalizationSwitch, PreprToolbar, PreprTrackingPixel, type PreprTrackingPixelProps, type PreprWebhookOptions, createPreprDraftRoute, createPreprMiddleware, createPreprWebhookRoute, getActiveSegment, getActiveVariant, getPreprHeaders, getPreprUUID, getToolbarProps, preprDraftRedirect };
@@ -1,6 +1,6 @@
1
1
  import { NextRequest, NextResponse } from 'next/server';
2
- import { b as PreprFeatures, P as PreprHeaders, c as PreprToolbarProps, d as PreprToolbarComponentProps } from './server-CjpELYvq.js';
3
- export { a as PreprError, e as extractAccessToken, i as isPersonalizedResponse, v as validatePreprToken } from './server-CjpELYvq.js';
2
+ import { a as PreprFeatures, P as PreprHeaders, c as PreprToolbarProps, d as PreprToolbarComponentProps } from './server-BL-sqZvu.js';
3
+ export { b as PreprError, e as extractAccessToken, i as isPersonalizedResponse, v as validatePreprToken } from './server-BL-sqZvu.js';
4
4
 
5
5
  interface PreprMiddlewareOptions {
6
6
  /** Enable preview-mode headers (segment/AB overrides, preview bar). */
@@ -44,26 +44,6 @@ declare function getPreprHeaders(): Promise<PreprHeaders>;
44
44
  */
45
45
  declare function getToolbarProps(token: string, features?: PreprFeatures): Promise<PreprToolbarProps>;
46
46
 
47
- /** Cookie holding the visitor's A/B bucket, as Prepr assigned it. */
48
- declare const PREPR_BUCKET_COOKIE = "prepr_ab_bucket";
49
- type PreprBucket = 'A' | 'B';
50
- interface ResolvePreprBucketOptions {
51
- /** Full Prepr GraphQL URL. */
52
- url: string;
53
- /** Preview mode: the toolbar's A/B override wins. */
54
- preview?: boolean;
55
- /** Abort the one-time lookup after this many ms. Default 1500. */
56
- timeoutMs?: number;
57
- /**
58
- * Look the bucket up only when this resolves to true, typically "does this
59
- * page have an A/B test": `() => prepr.hasAbTests(PageDocument, { slug })`.
60
- * Only called for visitors without a bucket cookie, so visitors on pages
61
- * without A/B tests never wait for the lookup.
62
- */
63
- onlyIf?: () => boolean | Promise<boolean>;
64
- }
65
- declare function resolvePreprBucket(request: NextRequest, response: NextResponse, options: ResolvePreprBucketOptions): Promise<PreprBucket>;
66
-
67
47
  interface PreprDraftOptions {
68
48
  /** Whether this deployment serves preview content. Outside it, both helpers do nothing. */
69
49
  preview: boolean;
@@ -100,6 +80,47 @@ declare function preprDraftRedirect(request: NextRequest, options: PreprDraftOpt
100
80
  */
101
81
  declare function createPreprDraftRoute(options: Pick<PreprDraftOptions, 'preview'>): (request: Request) => Promise<Response>;
102
82
 
83
+ /** How a revalidated page refreshes, as Next.js's `revalidateTag` takes it. */
84
+ type PreprRevalidateProfile = string | {
85
+ expire?: number;
86
+ };
87
+ interface PreprWebhookOptions {
88
+ /**
89
+ * The webhook's ID, shown in Prepr under Settings → Webhooks. Prepr signs
90
+ * each request with it (HMAC-SHA256 of the body, in the `Prepr-Signature`
91
+ * header). Keep it in an environment variable.
92
+ */
93
+ secret: string | undefined;
94
+ /**
95
+ * Passed to `revalidateTag`. The default `{ expire: 0 }` shows a publish
96
+ * on the very next request; `'max'` serves the old page once more while
97
+ * the new one renders in the background.
98
+ */
99
+ profile?: PreprRevalidateProfile;
100
+ }
101
+ /**
102
+ * Route handler for a Prepr webhook that refreshes only the cached pages
103
+ * showing the changed item: those tagged by `query(..., { tagItems: true })`
104
+ * or `preprCacheTags`. On publish, unpublish and delete it also revalidates
105
+ * the item's `preprModelTag`, so list pages and lookups that found nothing
106
+ * pick up new and removed items. `schema.changed` refreshes the schema the
107
+ * client rewrites queries with.
108
+ *
109
+ * Other events (draft edits, new assets, visitors, segments) are
110
+ * acknowledged and revalidate nothing, so the webhook can subscribe to
111
+ * everything.
112
+ *
113
+ * Requests without a valid `Prepr-Signature` or older than five minutes get
114
+ * 401; without `secret` every request gets 500.
115
+ *
116
+ * @example
117
+ * // app/api/prepr/webhook/route.ts
118
+ * export const POST = createPreprWebhookRoute({
119
+ * secret: process.env.PREPR_WEBHOOK_SECRET,
120
+ * });
121
+ */
122
+ declare function createPreprWebhookRoute(options: PreprWebhookOptions): (request: Request) => Promise<Response>;
123
+
103
124
  /**
104
125
  * Loader + typed facade for Prepr's CDN tracking pixel, so consumers don't have
105
126
  * to paste a `<script>` tag. The pixel itself lives on the CDN; this only sets
@@ -153,5 +174,22 @@ declare function PreprTrackingPixel({ id, config, }: PreprTrackingPixelProps): n
153
174
  * `createPreprPreview` mounts imperatively.
154
175
  */
155
176
  declare function PreprToolbar({ options, ...props }: PreprToolbarComponentProps): null;
177
+ /**
178
+ * Gets each client-side navigation's decision in before the click: when the
179
+ * visitor hovers over a same-site link (or presses it, on touch devices), it
180
+ * asks `createPreprProxy` which variants they get on that page, so the new
181
+ * page paints with them. A click whose decision is not in yet (a quick tap,
182
+ * a link reached by keyboard) waits for it; one the visitor's recent
183
+ * decisions already cover, or one to a page without personalization, goes
184
+ * through at once.
185
+ *
186
+ * Mount it once in the root layout, so links on pages without a
187
+ * `PreprStack` are covered too. Clicks with a modifier key, links to other
188
+ * sites and links that open a new tab or download are left alone.
189
+ *
190
+ * @example
191
+ * <PreprPersonalizationSwitch />
192
+ */
193
+ declare function PreprPersonalizationSwitch(): null;
156
194
 
157
- export { PREPR_BUCKET_COOKIE, type PreprBucket, type PreprDraftOptions, type PreprMiddlewareOptions, PreprToolbar, PreprTrackingPixel, type PreprTrackingPixelProps, type ResolvePreprBucketOptions, createPreprDraftRoute, createPreprMiddleware, getActiveSegment, getActiveVariant, getPreprHeaders, getPreprUUID, getToolbarProps, preprDraftRedirect, resolvePreprBucket };
195
+ export { type PreprDraftOptions, type PreprMiddlewareOptions, PreprPersonalizationSwitch, PreprToolbar, PreprTrackingPixel, type PreprTrackingPixelProps, type PreprWebhookOptions, createPreprDraftRoute, createPreprMiddleware, createPreprWebhookRoute, getActiveSegment, getActiveVariant, getPreprHeaders, getPreprUUID, getToolbarProps, preprDraftRedirect };