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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +163 -61
  2. package/dist/astro/{chunk-O7N6GPAH.js → chunk-CG2QBHNU.js} +1 -1
  3. package/dist/astro/graphql.cjs +58 -10
  4. package/dist/astro/graphql.d.cts +33 -38
  5. package/dist/astro/graphql.d.ts +33 -38
  6. package/dist/astro/graphql.js +59 -11
  7. package/dist/astro/index.cjs +1 -1
  8. package/dist/astro/index.js +1 -1
  9. package/dist/{chunk-VI6QZTBM.js → chunk-LIDUJWWJ.js} +1 -1
  10. package/dist/{chunk-TBKSBXGH.js → chunk-M3RBYJ5J.js} +52 -5
  11. package/dist/{chunk-3JLWZQ37.js → chunk-SF6TN4JR.js} +1 -1
  12. package/dist/{chunk-YWIKKWBR.js → chunk-TN6SNAR2.js} +1 -1
  13. package/dist/graphql/codegen.cjs +51 -4
  14. package/dist/graphql/codegen.js +3 -3
  15. package/dist/graphql.cjs +89 -10
  16. package/dist/graphql.d.cts +69 -39
  17. package/dist/graphql.d.ts +69 -39
  18. package/dist/graphql.js +38 -9
  19. package/dist/index.cjs +1 -1
  20. package/dist/index.d.cts +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +3 -3
  23. package/dist/nextjs/chunk-B4GVZEOL.js +549 -0
  24. package/dist/nextjs/components.cjs +297 -81
  25. package/dist/nextjs/components.d.cts +26 -11
  26. package/dist/nextjs/components.d.ts +26 -11
  27. package/dist/nextjs/components.js +288 -73
  28. package/dist/nextjs/graphql.cjs +856 -266
  29. package/dist/nextjs/graphql.d.cts +184 -116
  30. package/dist/nextjs/graphql.d.ts +184 -116
  31. package/dist/nextjs/graphql.js +543 -266
  32. package/dist/nextjs/index.cjs +116 -54
  33. package/dist/nextjs/index.d.cts +61 -23
  34. package/dist/nextjs/index.d.ts +61 -23
  35. package/dist/nextjs/index.js +97 -299
  36. package/dist/nextjs/{server-CjpELYvq.d.ts → server-BL-sqZvu.d.cts} +1 -1
  37. package/dist/nextjs/{server-CjpELYvq.d.cts → server-BL-sqZvu.d.ts} +1 -1
  38. package/dist/nuxt/{chunk-IQDSBPDN.js → chunk-FMC3DP7N.js} +1 -1
  39. package/dist/nuxt/graphql.cjs +58 -10
  40. package/dist/nuxt/graphql.d.cts +33 -38
  41. package/dist/nuxt/graphql.d.ts +33 -38
  42. package/dist/nuxt/graphql.js +59 -11
  43. package/dist/nuxt/index.cjs +1 -1
  44. package/dist/nuxt/index.js +1 -1
  45. package/dist/react/components.cjs +1 -1
  46. package/dist/react/components.js +1 -1
  47. package/dist/sveltekit/{chunk-OKYZ6HPH.js → chunk-AJPKBAHO.js} +1 -1
  48. package/dist/sveltekit/graphql.cjs +58 -10
  49. package/dist/sveltekit/graphql.d.cts +33 -38
  50. package/dist/sveltekit/graphql.d.ts +33 -38
  51. package/dist/sveltekit/graphql.js +59 -11
  52. package/dist/sveltekit/index.cjs +1 -1
  53. package/dist/sveltekit/index.js +1 -1
  54. package/package.json +1 -1
  55. package/dist/nextjs/chunk-BQ6IGSH6.js +0 -231
@@ -1,6 +1,9 @@
1
1
  import {
2
+ PREPR_ANY_ITEM_TAG,
3
+ PREPR_SCHEMA_TAG,
2
4
  PreprError,
3
- VERSION,
5
+ createPreprDraftRoute,
6
+ createPreprMiddleware,
4
7
  extractAccessToken,
5
8
  getActiveSegment,
6
9
  getActiveVariant,
@@ -8,332 +11,128 @@ import {
8
11
  getPreprUUID,
9
12
  getToolbarProps,
10
13
  isPersonalizedResponse,
11
- resolveFeatures,
14
+ preprDraftRedirect,
15
+ preprItemTag,
16
+ preprModelTag,
12
17
  validatePreprToken
13
- } from "./chunk-BQ6IGSH6.js";
14
-
15
- // src/nextjs/middleware.ts
16
- import { NextResponse } from "next/server";
18
+ } from "./chunk-B4GVZEOL.js";
17
19
 
18
- // src/core/constants.ts
19
- var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
20
- var COOKIE_SEGMENT = "Prepr-Segments";
21
- var COOKIE_VARIANT = "Prepr-ABtesting";
22
- var COOKIE_UID = "__prepr_uid";
23
- var COOKIE_HUBSPOT = "hubspotutk";
24
- var PARAM_SEGMENT = "prepr_preview_segment";
25
- var PARAM_VARIANT = "prepr_preview_ab";
26
- var PARAM_HIDE_BAR = "prepr_hide_bar";
27
- var ONE_YEAR_SECONDS = 60 * 60 * 24 * 365;
28
-
29
- // src/core/middleware.ts
30
- var UTM_PARAMS = [
31
- "utm_source",
32
- "utm_medium",
33
- "utm_term",
34
- "utm_content",
35
- "utm_campaign"
36
- ];
37
- var MAX_HEADER_VALUE_LENGTH = 2048;
38
- function sanitizeHeaderValue(value) {
39
- const stripped = value.replace(/[\u0000-\u001f\u007f]/g, "");
40
- return stripped.length > MAX_HEADER_VALUE_LENGTH ? stripped.slice(0, MAX_HEADER_VALUE_LENGTH) : stripped;
41
- }
42
- function parseCookies(request) {
43
- const cookies2 = /* @__PURE__ */ new Map();
44
- const header = request.headers.get("cookie");
45
- if (!header) return cookies2;
46
- for (const part of header.split(";")) {
47
- const eq = part.indexOf("=");
48
- if (eq === -1) continue;
49
- const name = part.slice(0, eq).trim();
50
- const value = part.slice(eq + 1).trim();
51
- if (!name) continue;
52
- try {
53
- cookies2.set(name, decodeURIComponent(value));
54
- } catch {
55
- cookies2.set(name, value);
56
- }
57
- }
58
- return cookies2;
59
- }
60
- function processFrameworkRequest(request, opts, framework) {
61
- const requestHeaders = new Headers(request.headers);
62
- const responseCookies = [];
63
- const inboundPreprHeaders = [];
64
- requestHeaders.forEach((_value, key) => {
65
- if (key.toLowerCase().startsWith("prepr-")) {
66
- inboundPreprHeaders.push(key);
67
- }
68
- });
69
- for (const key of inboundPreprHeaders) {
70
- requestHeaders.delete(key);
71
- }
72
- const requestCookies = parseCookies(request);
73
- const url = new URL(request.url);
74
- const searchParams = url.searchParams;
75
- const forwardedProto = request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim();
76
- const isSecure = forwardedProto ? forwardedProto === "https" : url.protocol === "https:";
77
- const crossSite = isSecure ? { sameSite: "None", secure: true } : {};
78
- for (const key of UTM_PARAMS) {
79
- const value = searchParams.get(key);
80
- if (value !== null) {
81
- requestHeaders.set(`Prepr-Context-${key}`, sanitizeHeaderValue(value));
20
+ // src/nextjs/webhook.ts
21
+ import { revalidateTag } from "next/cache";
22
+ var CONTENT_EVENTS = /* @__PURE__ */ new Set([
23
+ "content_item.published",
24
+ "content_item.unpublished",
25
+ "content_item.deleted",
26
+ "content_item.invalidated",
27
+ "asset.changed",
28
+ "asset.deleted"
29
+ ]);
30
+ var MODEL_EVENTS = /* @__PURE__ */ new Set([
31
+ "content_item.published",
32
+ "content_item.unpublished",
33
+ "content_item.deleted"
34
+ ]);
35
+ var MAX_AGE_MS = 5 * 60 * 1e3;
36
+ var HEX_SIGNATURE = /^[0-9a-f]{64}$/i;
37
+ var BASE64_SIGNATURE = /^[A-Za-z0-9+/]{43}=$/;
38
+ var SIGNATURE_PREFIX = /^sha256=/i;
39
+ var MAX_ID_LENGTH = 200;
40
+ var text = (body, status) => new Response(body, { status, headers: { "Content-Type": "text/plain" } });
41
+ function signatureBytes(header) {
42
+ const value = header.trim().replace(SIGNATURE_PREFIX, "");
43
+ if (HEX_SIGNATURE.test(value)) {
44
+ const bytes = new Uint8Array(32);
45
+ for (let i = 0; i < 32; i++) {
46
+ bytes[i] = parseInt(value.slice(i * 2, i * 2 + 2), 16);
82
47
  }
48
+ return bytes;
83
49
  }
84
- const referer = request.headers.get("referer");
85
- if (referer) {
86
- requestHeaders.set(
87
- "Prepr-Context-initial_referral",
88
- sanitizeHeaderValue(referer)
89
- );
90
- }
91
- const userAgent = request.headers.get("user-agent");
92
- if (userAgent) {
93
- requestHeaders.set("User-Agent", sanitizeHeaderValue(userAgent));
94
- }
95
- const packageParts = [`@preprio/toolkit@${opts?.version ?? VERSION}`];
96
- if (framework) packageParts.push(`framework=${framework}`);
97
- packageParts.push(`preview=${opts?.preview ? "true" : "false"}`);
98
- requestHeaders.set("Prepr-Package", packageParts.join("; "));
99
- const ip = request.headers.get("Cf-Connecting-Ip") ?? request.headers.get("x-real-ip");
100
- if (ip) {
101
- requestHeaders.set("Prepr-Visitor-IP", sanitizeHeaderValue(ip));
102
- }
103
- const hutkCookie = requestCookies.get(COOKIE_HUBSPOT);
104
- if (hutkCookie) {
105
- requestHeaders.set("Prepr-Hubspot-Id", sanitizeHeaderValue(hutkCookie));
106
- }
107
- let uid = sanitizeHeaderValue(requestCookies.get(COOKIE_UID) ?? "");
108
- if (!uid) {
109
- uid = crypto.randomUUID();
110
- responseCookies.push({
111
- name: COOKIE_UID,
112
- value: uid,
113
- maxAge: ONE_YEAR_SECONDS,
114
- path: "/"
115
- });
116
- requestHeaders.set("Prepr-Customer-Id-Created", "true");
117
- }
118
- requestHeaders.set("Prepr-Customer-Id", uid);
119
- if (!opts?.preview) {
120
- return { requestHeaders, responseCookies };
121
- }
122
- const features = resolveFeatures(opts?.features);
123
- const hasPreviewParams = features.segments && searchParams.has(PARAM_SEGMENT) || features.abTesting && searchParams.has(PARAM_VARIANT);
124
- if (!hasPreviewParams) {
125
- const previewModeCookie = requestCookies.get(COOKIE_PREVIEW_MODE);
126
- const previewModeEnabled = previewModeCookie !== "false";
127
- if (!previewModeEnabled) {
128
- return { requestHeaders, responseCookies };
129
- }
130
- }
131
- requestHeaders.set("Prepr-Preview-Bar", "true");
132
- const isLivePreview = searchParams.get(PARAM_HIDE_BAR) === "true";
133
- if (!isLivePreview && !requestCookies.has(COOKIE_PREVIEW_MODE)) {
134
- responseCookies.push({
135
- name: COOKIE_PREVIEW_MODE,
136
- value: "true",
137
- maxAge: ONE_YEAR_SECONDS,
138
- path: "/",
139
- ...crossSite
140
- });
141
- }
142
- if (!isLivePreview) {
143
- const segmentCookie = features.segments ? requestCookies.get(COOKIE_SEGMENT) : void 0;
144
- if (segmentCookie) {
145
- requestHeaders.set("Prepr-Segments", sanitizeHeaderValue(segmentCookie));
146
- }
147
- const abCookie = features.abTesting ? requestCookies.get(COOKIE_VARIANT) : void 0;
148
- if (abCookie) {
149
- requestHeaders.set("Prepr-ABtesting", sanitizeHeaderValue(abCookie));
150
- }
50
+ if (BASE64_SIGNATURE.test(value)) {
51
+ return Uint8Array.from(atob(value), (char) => char.charCodeAt(0));
151
52
  }
152
- const previewAb = features.abTesting ? searchParams.get(PARAM_VARIANT) : null;
153
- if (previewAb !== null) {
154
- requestHeaders.set("Prepr-ABtesting", sanitizeHeaderValue(previewAb));
155
- if (!isLivePreview) {
156
- responseCookies.push({
157
- name: COOKIE_VARIANT,
158
- value: previewAb,
159
- maxAge: ONE_YEAR_SECONDS,
160
- path: "/",
161
- ...crossSite
162
- });
163
- }
164
- }
165
- const previewSegment = features.segments ? searchParams.get(PARAM_SEGMENT) : null;
166
- if (previewSegment !== null) {
167
- requestHeaders.set("Prepr-Segments", sanitizeHeaderValue(previewSegment));
168
- if (!isLivePreview) {
169
- responseCookies.push({
170
- name: COOKIE_SEGMENT,
171
- value: previewSegment,
172
- maxAge: ONE_YEAR_SECONDS,
173
- path: "/",
174
- ...crossSite
175
- });
176
- }
177
- }
178
- if (requestHeaders.has("Prepr-Segments")) {
179
- requestHeaders.delete("Prepr-Customer-Id");
180
- requestHeaders.delete("Prepr-Customer-Id-Created");
181
- }
182
- return { requestHeaders, responseCookies };
53
+ return null;
183
54
  }
184
-
185
- // src/nextjs/middleware.ts
186
- function createPreprMiddleware(request, responseOrOptions, maybeOptions) {
187
- let chainedResponse;
188
- let options;
189
- if (responseOrOptions instanceof NextResponse) {
190
- chainedResponse = responseOrOptions;
191
- options = maybeOptions;
192
- } else {
193
- options = responseOrOptions;
194
- }
195
- const { requestHeaders, responseCookies } = processFrameworkRequest(
196
- request,
197
- options,
198
- "nextjs"
55
+ async function verify(secret, signature, body) {
56
+ const key = await crypto.subtle.importKey(
57
+ "raw",
58
+ new TextEncoder().encode(secret),
59
+ { name: "HMAC", hash: "SHA-256" },
60
+ false,
61
+ ["verify"]
199
62
  );
200
- const response = chainedResponse ?? NextResponse.next({ request: { headers: requestHeaders } });
201
- if (chainedResponse) {
202
- const existing = response.headers.get("x-middleware-override-headers");
203
- const names = new Set(
204
- existing ? existing.split(",").map((name) => name.trim()).filter(Boolean) : []
205
- );
206
- requestHeaders.forEach((value, key) => {
207
- names.add(key);
208
- response.headers.set(`x-middleware-request-${key}`, value);
209
- });
210
- response.headers.set("x-middleware-override-headers", [...names].join(","));
211
- }
212
- for (const cookie of responseCookies) {
213
- response.cookies.set(cookie.name, cookie.value, {
214
- maxAge: cookie.maxAge,
215
- path: cookie.path,
216
- // Lower-cased: Next's cookie API expects 'none' | 'lax' | 'strict'.
217
- ...cookie.sameSite ? {
218
- sameSite: cookie.sameSite.toLowerCase()
219
- } : {},
220
- ...cookie.secure ? { secure: true } : {}
221
- });
222
- }
223
- return response;
224
- }
225
-
226
- // src/nextjs/bucket.ts
227
- var PREPR_BUCKET_COOKIE = "prepr_ab_bucket";
228
- var isBucket = (value) => value === "A" || value === "B";
229
- function setBucketCookie(request, response, bucket) {
230
- response.cookies.set(PREPR_BUCKET_COOKIE, bucket, {
231
- path: "/",
232
- sameSite: "lax",
233
- // Readable on purpose: PreprStack's inline switch reads it before first
234
- // paint. It holds only "A" or "B".
235
- httpOnly: false,
236
- secure: request.nextUrl.protocol === "https:",
237
- maxAge: 60 * 60 * 24 * 365
238
- });
63
+ return crypto.subtle.verify("HMAC", key, signature, body);
239
64
  }
240
- async function resolvePreprBucket(request, response, options) {
241
- const stored = request.cookies.get(PREPR_BUCKET_COOKIE)?.value;
242
- const forced = response.headers.get("x-middleware-request-prepr-abtesting") ?? request.cookies.get(COOKIE_VARIANT)?.value;
243
- if (options.preview && isBucket(forced)) {
244
- if (forced !== stored) setBucketCookie(request, response, forced);
245
- return forced;
246
- }
247
- if (isBucket(stored)) return stored;
248
- if (options.onlyIf) {
249
- const needed = await Promise.resolve().then(options.onlyIf).catch(() => true);
250
- if (!needed) return "A";
251
- }
252
- const customerId = request.cookies.get(COOKIE_UID)?.value ?? response.cookies.get(COOKIE_UID)?.value;
253
- if (!customerId) return "A";
65
+ function parse(body) {
254
66
  try {
255
- const res = await fetch(options.url, {
256
- method: "POST",
257
- headers: {
258
- "Content-Type": "application/json",
259
- "Prepr-Customer-Id": customerId
260
- },
261
- body: JSON.stringify({ query: "{ __typename }" }),
262
- cache: "no-store",
263
- signal: AbortSignal.timeout(options.timeoutMs ?? 1500)
264
- });
265
- const bucket = res.headers.get("x-prepr-customer-bucket");
266
- if (isBucket(bucket)) {
267
- setBucketCookie(request, response, bucket);
268
- return bucket;
269
- }
67
+ const value = JSON.parse(new TextDecoder().decode(body));
68
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
270
69
  } catch {
70
+ return null;
271
71
  }
272
- return "A";
273
72
  }
274
-
275
- // src/nextjs/draft.ts
276
- import { cookies, draftMode } from "next/headers";
277
- import { redirect } from "next/navigation";
278
- import { NextResponse as NextResponse2 } from "next/server";
279
- var DRAFT_COOKIE = "__prerender_bypass";
280
- var RETURN_PARAM = "prepr_draft";
281
- var DEFAULT_ROUTE = "/api/prepr/draft";
282
- function preprDraftRedirect(request, options) {
283
- if (!options.preview) return null;
284
- if (request.method !== "GET" && request.method !== "HEAD") return null;
285
- const url = request.nextUrl;
286
- if (url.pathname === (options.route ?? DEFAULT_ROUTE)) return null;
287
- if (request.cookies.has(DRAFT_COOKIE)) {
288
- if (!url.searchParams.has(RETURN_PARAM)) return null;
289
- const clean = url.clone();
290
- clean.searchParams.delete(RETURN_PARAM);
291
- return NextResponse2.redirect(clean, 307);
292
- }
293
- if (url.searchParams.has(RETURN_PARAM)) return null;
294
- const target = new URL(options.route ?? DEFAULT_ROUTE, request.url);
295
- target.searchParams.set("to", url.pathname + url.search);
296
- return NextResponse2.redirect(target, 307);
73
+ function eventTime(value) {
74
+ if (typeof value === "number") return value * 1e3;
75
+ if (typeof value === "string") return Date.parse(value);
76
+ return NaN;
77
+ }
78
+ function field(value, key) {
79
+ return typeof value === "object" && value !== null ? value[key] : void 0;
297
80
  }
298
- function createPreprDraftRoute(options) {
81
+ var usable = (value) => typeof value === "string" && value !== "" && value.length <= MAX_ID_LENGTH;
82
+ function tagsFor(event) {
83
+ const name = event.event;
84
+ if (name === "schema.changed") return [PREPR_SCHEMA_TAG];
85
+ if (typeof name !== "string" || !CONTENT_EVENTS.has(name)) return [];
86
+ const tags = [PREPR_ANY_ITEM_TAG];
87
+ const id = field(event.payload, "id");
88
+ if (usable(id)) tags.push(preprItemTag(id));
89
+ const model = field(field(event.payload, "model"), "body_singular");
90
+ if (MODEL_EVENTS.has(name) && usable(model)) tags.push(preprModelTag(model));
91
+ return tags;
92
+ }
93
+ function createPreprWebhookRoute(options) {
94
+ const { profile = { expire: 0 } } = options;
299
95
  return async (request) => {
300
- if (!options.preview) return new Response("Not found", { status: 404 });
301
- (await draftMode()).enable();
302
- const self = new URL(request.url);
303
- if (self.protocol === "https:") {
304
- const jar = await cookies();
305
- const draft = jar.get(DRAFT_COOKIE);
306
- if (draft) {
307
- jar.set({
308
- name: DRAFT_COOKIE,
309
- value: draft.value,
310
- path: "/",
311
- httpOnly: true,
312
- secure: true,
313
- sameSite: "none",
314
- partitioned: true
315
- });
316
- }
96
+ const { secret } = options;
97
+ if (!secret) return text("Webhook secret not configured", 500);
98
+ const signature = signatureBytes(
99
+ request.headers.get("prepr-signature") ?? ""
100
+ );
101
+ let body;
102
+ try {
103
+ body = await request.arrayBuffer();
104
+ } catch {
105
+ return text("Unreadable body", 400);
106
+ }
107
+ if (!signature || !await verify(secret, signature, body)) {
108
+ return text("Invalid signature", 401);
109
+ }
110
+ const event = parse(body);
111
+ if (!event) return text("Invalid body", 400);
112
+ const sentAt = eventTime(event.created_on);
113
+ if (Number.isFinite(sentAt) && Math.abs(Date.now() - sentAt) > MAX_AGE_MS) {
114
+ return text("Expired", 401);
317
115
  }
318
- let to = new URL(self.searchParams.get("to") ?? "/", self);
319
- if (to.origin !== self.origin) to = new URL("/", self);
320
- to.searchParams.set(RETURN_PARAM, "1");
321
- redirect(to.pathname + to.search);
116
+ const tags = tagsFor(event);
117
+ for (const tag of tags) revalidateTag(tag, profile);
118
+ return Response.json({ revalidated: tags });
322
119
  };
323
120
  }
324
121
 
325
122
  // src/nextjs/index.ts
326
123
  import {
124
+ PreprPersonalizationSwitch,
327
125
  PreprToolbar,
328
126
  PreprTrackingPixel
329
127
  } from "./components.js";
330
128
  export {
331
- PREPR_BUCKET_COOKIE,
332
129
  PreprError,
130
+ PreprPersonalizationSwitch,
333
131
  PreprToolbar,
334
132
  PreprTrackingPixel,
335
133
  createPreprDraftRoute,
336
134
  createPreprMiddleware,
135
+ createPreprWebhookRoute,
337
136
  extractAccessToken,
338
137
  getActiveSegment,
339
138
  getActiveVariant,
@@ -342,6 +141,5 @@ export {
342
141
  getToolbarProps,
343
142
  isPersonalizedResponse,
344
143
  preprDraftRedirect,
345
- resolvePreprBucket,
346
144
  validatePreprToken
347
145
  };
@@ -153,4 +153,4 @@ declare function validatePreprToken(token: string): void;
153
153
  */
154
154
  declare function extractAccessToken(graphqlUrl: string): string;
155
155
 
156
- export { type PreprHeaders as P, PreprError as a, type PreprFeatures as b, type PreprToolbarProps as c, type PreprToolbarComponentProps as d, extractAccessToken as e, isPersonalizedResponse as i, validatePreprToken as v };
156
+ export { type PreprHeaders as P, type PreprFeatures as a, PreprError as b, type PreprToolbarProps as c, type PreprToolbarComponentProps as d, extractAccessToken as e, isPersonalizedResponse as i, validatePreprToken as v };
@@ -153,4 +153,4 @@ declare function validatePreprToken(token: string): void;
153
153
  */
154
154
  declare function extractAccessToken(graphqlUrl: string): string;
155
155
 
156
- export { type PreprHeaders as P, PreprError as a, type PreprFeatures as b, type PreprToolbarProps as c, type PreprToolbarComponentProps as d, extractAccessToken as e, isPersonalizedResponse as i, validatePreprToken as v };
156
+ export { type PreprHeaders as P, type PreprFeatures as a, PreprError as b, type PreprToolbarProps as c, type PreprToolbarComponentProps as d, extractAccessToken as e, isPersonalizedResponse as i, validatePreprToken as v };
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var VERSION = "0.6.0-alpha.1";
2
+ var VERSION = "0.6.0-alpha.10";
3
3
 
4
4
  // src/core/constants.ts
5
5
  var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
@@ -178,7 +178,10 @@ function decisionSelectionSet(set, typeName, path, ctx) {
178
178
  path,
179
179
  ctx
180
180
  );
181
- if (pruned) {
181
+ if (!pruned) continue;
182
+ if (fragment.typeCondition.name.value === typeName) {
183
+ selections.push(...pruned.selections);
184
+ } else {
182
185
  selections.push(
183
186
  inlineFragment(fragment.typeCondition.name.value, [
184
187
  ...pruned.selections
@@ -188,13 +191,19 @@ function decisionSelectionSet(set, typeName, path, ctx) {
188
191
  continue;
189
192
  }
190
193
  if (selection.kind === "InlineFragment") {
194
+ const condition = selection.typeCondition?.name.value ?? typeName;
191
195
  const pruned = decisionSelectionSet(
192
196
  selection.selectionSet,
193
- selection.typeCondition?.name.value ?? typeName,
197
+ condition,
194
198
  path,
195
199
  ctx
196
200
  );
197
- if (pruned) selections.push({ ...selection, selectionSet: pruned });
201
+ if (!pruned) continue;
202
+ if (condition === typeName && !selection.directives?.length) {
203
+ selections.push(...pruned.selections);
204
+ } else {
205
+ selections.push({ ...selection, selectionSet: pruned });
206
+ }
198
207
  continue;
199
208
  }
200
209
  if (!selection.selectionSet) continue;
@@ -268,20 +277,58 @@ function toDecisionDocument(document2, schema, groupPaths, options) {
268
277
  const used = /* @__PURE__ */ new Set();
269
278
  collectVariables(pruned, used);
270
279
  collectVariables(operation.directives, used);
280
+ const shared = sharedContextFragment(schema);
271
281
  return {
272
282
  kind: "Document",
273
283
  definitions: [
284
+ ...shared ? [shared.definition] : [],
274
285
  {
275
286
  ...operation,
276
287
  name: operation.name && name(`${operation.name.value}Decision`),
277
288
  variableDefinitions: (operation.variableDefinitions ?? []).filter(
278
289
  (definition) => used.has(definition.variable.name.value)
279
290
  ),
280
- selectionSet: pruned
291
+ selectionSet: shared ? shared.apply(pruned) : pruned
281
292
  }
282
293
  ]
283
294
  };
284
295
  }
296
+ function sharedContextFragment(schema) {
297
+ const contextTypes = new Set(
298
+ Object.values(schema.types).flatMap(
299
+ (type) => type.hasContext ? [type.fields._context.type] : []
300
+ )
301
+ );
302
+ if (contextTypes.size !== 1) return null;
303
+ const [contextType] = contextTypes;
304
+ const fragmentName = "c";
305
+ const definition = {
306
+ kind: "FragmentDefinition",
307
+ name: name(fragmentName),
308
+ typeCondition: { kind: "NamedType", name: name(contextType) },
309
+ selectionSet: selectionSet(
310
+ ["kind", "group_id", "variant_id"].map(
311
+ (field) => ({ kind: "Field", name: name(field) })
312
+ )
313
+ )
314
+ };
315
+ const spread = {
316
+ kind: "FragmentSpread",
317
+ name: name(fragmentName)
318
+ };
319
+ const apply = (set) => selectionSet(
320
+ set.selections.map((selection) => {
321
+ if (selection.kind === "Field" && selection.alias?.value === PREPR_CONTEXT_ALIAS) {
322
+ return { ...selection, selectionSet: selectionSet([spread]) };
323
+ }
324
+ return "selectionSet" in selection && selection.selectionSet ? {
325
+ ...selection,
326
+ selectionSet: apply(selection.selectionSet)
327
+ } : selection;
328
+ })
329
+ );
330
+ return { definition, apply };
331
+ }
285
332
 
286
333
  // src/core/variants/probe-document.ts
287
334
  function probeSelectionSet(set, fragments) {
@@ -862,6 +909,7 @@ function createVaryProbe(run, options) {
862
909
  }
863
910
 
864
911
  // src/core/graphql/client.ts
912
+ var clientSchemas = /* @__PURE__ */ new WeakMap();
865
913
  function isParseableUrl(url) {
866
914
  try {
867
915
  new URL(url);
@@ -898,7 +946,7 @@ function createPreprClient(options) {
898
946
  ));
899
947
  const variantsDocuments = /* @__PURE__ */ new Map();
900
948
  const decisionDocuments = /* @__PURE__ */ new Map();
901
- return {
949
+ const client = {
902
950
  async query(document2, variables, queryOptions = {}) {
903
951
  const { variants, ...rest } = queryOptions;
904
952
  if (!variants) return run(document2, variables, rest);
@@ -910,10 +958,10 @@ function createPreprClient(options) {
910
958
  return run(derived, variables, rest);
911
959
  },
912
960
  async decide(document2, variables, queryOptions = {}) {
913
- const { from, topLevelOnly, ...rest } = queryOptions;
961
+ const { from, ...rest } = queryOptions;
914
962
  const paths = from === void 0 ? void 0 : personalizationPaths(from);
915
963
  if (paths && paths.size === 0) return /* @__PURE__ */ new Map();
916
- const signature = paths ? [...paths].map(([path, types]) => `${path}:${[...types].sort().join(",")}`).sort().join("|") : topLevelOnly ? "top" : "*";
964
+ const signature = paths ? [...paths].map(([path, types]) => `${path}:${[...types].sort().join(",")}`).sort().join("|") : "*";
917
965
  let byPaths = decisionDocuments.get(document2);
918
966
  if (!byPaths) {
919
967
  byPaths = /* @__PURE__ */ new Map();
@@ -921,9 +969,7 @@ function createPreprClient(options) {
921
969
  }
922
970
  let derived = byPaths.get(signature);
923
971
  if (derived === void 0) {
924
- derived = toDecisionDocument(document2, await loadSchema(), paths, {
925
- topLevelOnly: !paths && topLevelOnly
926
- });
972
+ derived = toDecisionDocument(document2, await loadSchema(), paths);
927
973
  byPaths.set(signature, derived);
928
974
  }
929
975
  if (!derived) return /* @__PURE__ */ new Map();
@@ -937,6 +983,8 @@ function createPreprClient(options) {
937
983
  (vary) => vary === null || SEGMENTS_VARY.test(vary)
938
984
  )
939
985
  };
986
+ clientSchemas.set(client, loadSchema);
987
+ return client;
940
988
  }
941
989
 
942
990
  // src/core/graphql/event-client.ts