@reblu/site-client 5.7.0 → 5.9.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.
package/dist/index.d.cts CHANGED
@@ -1,5 +1,34 @@
1
1
  import * as _reblu_site_contracts from '@reblu/site-contracts';
2
- import { AcademyCourseSort, PaginationMeta, PageType, PagesListSort, ShopSort } from '@reblu/site-contracts';
2
+ import { AcademyCourseSort, PaginationMeta, PageType, PagesListSort, ShopSort, TrustBadgeType } from '@reblu/site-contracts';
3
+
4
+ /**
5
+ * Cache adapter — the seam that severs the SDK from any framework's caching
6
+ * model (design doc D4 / §3.2). The client owns *when* to cache (a per-call
7
+ * `RebluCachePolicy`); the adapter owns *how* (the framework-specific
8
+ * `RequestInit` fields). `@reblu/site-client` ships the framework-agnostic
9
+ * `PlainCacheAdapter`; `@reblu/site-next` (REB-418) supplies a `NextCacheAdapter`
10
+ * that returns `{ next: { revalidate } }`.
11
+ */
12
+ interface RebluCachePolicy {
13
+ /**
14
+ * Seconds to revalidate. `undefined` → use the adapter's default; `0`/`false`
15
+ * → never cache (always fetch fresh).
16
+ */
17
+ revalidate?: number | false;
18
+ }
19
+ interface RebluCacheAdapter {
20
+ /** Map a per-call cache policy to framework-specific `fetch` `RequestInit` fields. */
21
+ resolveRequestInit(policy: RebluCachePolicy): RequestInit;
22
+ }
23
+ /**
24
+ * Default adapter — plain `fetch` cache semantics that work in Node, edge,
25
+ * browser and tests. No `next`, no `server-only`.
26
+ */
27
+ declare class PlainCacheAdapter implements RebluCacheAdapter {
28
+ private readonly defaultRevalidate;
29
+ constructor(defaultRevalidate?: number | false);
30
+ resolveRequestInit(policy: RebluCachePolicy): RequestInit;
31
+ }
3
32
 
4
33
  /**
5
34
  * The shape every list method returns. Unlike demo-site's old bare-array return,
@@ -118,33 +147,31 @@ interface AcademyEnrollmentRequestResult {
118
147
  }
119
148
 
120
149
  /**
121
- * Cache adapter — the seam that severs the SDK from any framework's caching
122
- * model (design doc D4 / §3.2). The client owns *when* to cache (a per-call
123
- * `RebluCachePolicy`); the adapter owns *how* (the framework-specific
124
- * `RequestInit` fields). `@reblu/site-client` ships the framework-agnostic
125
- * `PlainCacheAdapter`; `@reblu/site-next` (REB-418) supplies a `NextCacheAdapter`
126
- * that returns `{ next: { revalidate } }`.
150
+ * Header carrying the visitor IP from a SITE server to base-api (REB-1005).
151
+ * base-api keys its per-visitor limit on public writes on it, and trusts it
152
+ * only behind a valid API key.
127
153
  */
128
- interface RebluCachePolicy {
129
- /**
130
- * Seconds to revalidate. `undefined` → use the adapter's default; `0`/`false`
131
- * → never cache (always fetch fresh).
132
- */
133
- revalidate?: number | false;
134
- }
135
- interface RebluCacheAdapter {
136
- /** Map a per-call cache policy to framework-specific `fetch` `RequestInit` fields. */
137
- resolveRequestInit(policy: RebluCachePolicy): RequestInit;
138
- }
154
+ declare const VISITOR_IP_HEADER = "x-reblu-visitor-ip";
139
155
  /**
140
- * Default adapter — plain `fetch` cache semantics that work in Node, edge,
141
- * browser and tests. No `next`, no `server-only`.
156
+ * Per-call options for a write (REB-1005).
157
+ *
158
+ * - `visitorIp` — the visitor's IP when a SITE calls the SDK server-side, sent
159
+ * as `x-reblu-visitor-ip`. Do NOT rely on forwarding `x-forwarded-for`: on
160
+ * Vercel the SITE→base-api hop gets it overwritten with the SITE function's
161
+ * egress IP. Read it from the INCOMING request with
162
+ * {@link visitorIpFromHeaders}.
163
+ * - `headers` — other extra headers (e.g. `user-agent`). They can never set
164
+ * `x-reblu-visitor-ip` nor replace `X-Reblu-Api-Key`, `X-Reblu-Api-Version`
165
+ * or `Content-Type`.
142
166
  */
143
- declare class PlainCacheAdapter implements RebluCacheAdapter {
144
- private readonly defaultRevalidate;
145
- constructor(defaultRevalidate?: number | false);
146
- resolveRequestInit(policy: RebluCachePolicy): RequestInit;
167
+ interface RebluMutationOptions {
168
+ visitorIp?: string;
169
+ headers?: Record<string, string>;
147
170
  }
171
+ /** The visitor IP of an incoming request: first `x-forwarded-for` entry, else `x-real-ip`. */
172
+ declare function visitorIpFromHeaders(headers: {
173
+ get(name: string): string | null;
174
+ }): string | undefined;
148
175
 
149
176
  /**
150
177
  * The `details` shape `/api/site/contact-form` and `/api/site/leads` attach on a
@@ -257,6 +284,12 @@ interface FeaturedPagesOptions {
257
284
  cache?: RebluCachePolicy;
258
285
  }
259
286
 
287
+ /** Filters for {@link makeTrustBadgesNamespace}'s `list` (REB-996). */
288
+ interface TrustBadgeListParams {
289
+ /** Only badges of this type (e.g. `'SUBVENTION'`). Omit for every type. */
290
+ type?: TrustBadgeType;
291
+ }
292
+
260
293
  /**
261
294
  * Stripe Checkout redirect-origin guard (REB-836, defence in depth).
262
295
  *
@@ -327,7 +360,10 @@ declare function createRebluClient(config: RebluClientConfig): {
327
360
  get: (cache?: RebluCachePolicy) => Promise<_reblu_site_contracts.Legal>;
328
361
  };
329
362
  trustBadges: {
330
- list: (cache?: RebluCachePolicy) => Promise<ListResult<_reblu_site_contracts.TrustBadge>>;
363
+ list: {
364
+ (cache?: RebluCachePolicy): Promise<ListResult<_reblu_site_contracts.TrustBadge>>;
365
+ (params: TrustBadgeListParams, cache?: RebluCachePolicy): Promise<ListResult<_reblu_site_contracts.TrustBadge>>;
366
+ };
331
367
  };
332
368
  ctas: {
333
369
  list: (slot: string, cache?: RebluCachePolicy) => Promise<ListResult<_reblu_site_contracts.Cta>>;
@@ -365,13 +401,16 @@ declare function createRebluClient(config: RebluClientConfig): {
365
401
  enrollmentRequest: (body: AcademyEnrollmentRequestInput) => Promise<AcademyEnrollmentRequestResult>;
366
402
  };
367
403
  contactForm: {
368
- submit: (body: _reblu_site_contracts.ContactFormInput) => Promise<_reblu_site_contracts.ContactFormSubmission>;
404
+ submit: (body: _reblu_site_contracts.ContactFormInput, options?: RebluMutationOptions) => Promise<_reblu_site_contracts.ContactFormSubmission>;
369
405
  };
370
406
  leads: {
371
407
  create: (body: _reblu_site_contracts.LeadInput) => Promise<_reblu_site_contracts.LeadSubmission>;
372
408
  };
409
+ dataRequests: {
410
+ submit: (body: _reblu_site_contracts.DataRequestInput, options?: RebluMutationOptions) => Promise<_reblu_site_contracts.DataRequestSubmission>;
411
+ };
373
412
  };
374
413
  /** The assembled client returned by {@link createRebluClient}. */
375
414
  type RebluClient = ReturnType<typeof createRebluClient>;
376
415
 
377
- export { type AcademyCourseListParams, type AcademyEnrollmentRequestInput, type AcademyEnrollmentRequestResult, AmbiguousPageSlugError, ApiError, type ApiErrorBody, type ApiErrorDetails, type BlogListParams, DEFAULT_CHECKOUT_REDIRECT_HOSTS, type FeaturedPagesOptions, type ListResult, type PageBySlugOptions, type PageSlugCandidate, type PagesListParams, PlainCacheAdapter, type RebluCacheAdapter, type RebluCachePolicy, type RebluClient, type RebluClientConfig, type ShopProductsParams, UnsafeCheckoutRedirectError, createRebluClient, isSafeCheckoutRedirectUrl };
416
+ export { type AcademyCourseListParams, type AcademyEnrollmentRequestInput, type AcademyEnrollmentRequestResult, AmbiguousPageSlugError, ApiError, type ApiErrorBody, type ApiErrorDetails, type BlogListParams, DEFAULT_CHECKOUT_REDIRECT_HOSTS, type FeaturedPagesOptions, type ListResult, type PageBySlugOptions, type PageSlugCandidate, type PagesListParams, PlainCacheAdapter, type RebluCacheAdapter, type RebluCachePolicy, type RebluClient, type RebluClientConfig, type RebluMutationOptions, type ShopProductsParams, type TrustBadgeListParams, UnsafeCheckoutRedirectError, VISITOR_IP_HEADER, createRebluClient, isSafeCheckoutRedirectUrl, visitorIpFromHeaders };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,34 @@
1
1
  import * as _reblu_site_contracts from '@reblu/site-contracts';
2
- import { AcademyCourseSort, PaginationMeta, PageType, PagesListSort, ShopSort } from '@reblu/site-contracts';
2
+ import { AcademyCourseSort, PaginationMeta, PageType, PagesListSort, ShopSort, TrustBadgeType } from '@reblu/site-contracts';
3
+
4
+ /**
5
+ * Cache adapter — the seam that severs the SDK from any framework's caching
6
+ * model (design doc D4 / §3.2). The client owns *when* to cache (a per-call
7
+ * `RebluCachePolicy`); the adapter owns *how* (the framework-specific
8
+ * `RequestInit` fields). `@reblu/site-client` ships the framework-agnostic
9
+ * `PlainCacheAdapter`; `@reblu/site-next` (REB-418) supplies a `NextCacheAdapter`
10
+ * that returns `{ next: { revalidate } }`.
11
+ */
12
+ interface RebluCachePolicy {
13
+ /**
14
+ * Seconds to revalidate. `undefined` → use the adapter's default; `0`/`false`
15
+ * → never cache (always fetch fresh).
16
+ */
17
+ revalidate?: number | false;
18
+ }
19
+ interface RebluCacheAdapter {
20
+ /** Map a per-call cache policy to framework-specific `fetch` `RequestInit` fields. */
21
+ resolveRequestInit(policy: RebluCachePolicy): RequestInit;
22
+ }
23
+ /**
24
+ * Default adapter — plain `fetch` cache semantics that work in Node, edge,
25
+ * browser and tests. No `next`, no `server-only`.
26
+ */
27
+ declare class PlainCacheAdapter implements RebluCacheAdapter {
28
+ private readonly defaultRevalidate;
29
+ constructor(defaultRevalidate?: number | false);
30
+ resolveRequestInit(policy: RebluCachePolicy): RequestInit;
31
+ }
3
32
 
4
33
  /**
5
34
  * The shape every list method returns. Unlike demo-site's old bare-array return,
@@ -118,33 +147,31 @@ interface AcademyEnrollmentRequestResult {
118
147
  }
119
148
 
120
149
  /**
121
- * Cache adapter — the seam that severs the SDK from any framework's caching
122
- * model (design doc D4 / §3.2). The client owns *when* to cache (a per-call
123
- * `RebluCachePolicy`); the adapter owns *how* (the framework-specific
124
- * `RequestInit` fields). `@reblu/site-client` ships the framework-agnostic
125
- * `PlainCacheAdapter`; `@reblu/site-next` (REB-418) supplies a `NextCacheAdapter`
126
- * that returns `{ next: { revalidate } }`.
150
+ * Header carrying the visitor IP from a SITE server to base-api (REB-1005).
151
+ * base-api keys its per-visitor limit on public writes on it, and trusts it
152
+ * only behind a valid API key.
127
153
  */
128
- interface RebluCachePolicy {
129
- /**
130
- * Seconds to revalidate. `undefined` → use the adapter's default; `0`/`false`
131
- * → never cache (always fetch fresh).
132
- */
133
- revalidate?: number | false;
134
- }
135
- interface RebluCacheAdapter {
136
- /** Map a per-call cache policy to framework-specific `fetch` `RequestInit` fields. */
137
- resolveRequestInit(policy: RebluCachePolicy): RequestInit;
138
- }
154
+ declare const VISITOR_IP_HEADER = "x-reblu-visitor-ip";
139
155
  /**
140
- * Default adapter — plain `fetch` cache semantics that work in Node, edge,
141
- * browser and tests. No `next`, no `server-only`.
156
+ * Per-call options for a write (REB-1005).
157
+ *
158
+ * - `visitorIp` — the visitor's IP when a SITE calls the SDK server-side, sent
159
+ * as `x-reblu-visitor-ip`. Do NOT rely on forwarding `x-forwarded-for`: on
160
+ * Vercel the SITE→base-api hop gets it overwritten with the SITE function's
161
+ * egress IP. Read it from the INCOMING request with
162
+ * {@link visitorIpFromHeaders}.
163
+ * - `headers` — other extra headers (e.g. `user-agent`). They can never set
164
+ * `x-reblu-visitor-ip` nor replace `X-Reblu-Api-Key`, `X-Reblu-Api-Version`
165
+ * or `Content-Type`.
142
166
  */
143
- declare class PlainCacheAdapter implements RebluCacheAdapter {
144
- private readonly defaultRevalidate;
145
- constructor(defaultRevalidate?: number | false);
146
- resolveRequestInit(policy: RebluCachePolicy): RequestInit;
167
+ interface RebluMutationOptions {
168
+ visitorIp?: string;
169
+ headers?: Record<string, string>;
147
170
  }
171
+ /** The visitor IP of an incoming request: first `x-forwarded-for` entry, else `x-real-ip`. */
172
+ declare function visitorIpFromHeaders(headers: {
173
+ get(name: string): string | null;
174
+ }): string | undefined;
148
175
 
149
176
  /**
150
177
  * The `details` shape `/api/site/contact-form` and `/api/site/leads` attach on a
@@ -257,6 +284,12 @@ interface FeaturedPagesOptions {
257
284
  cache?: RebluCachePolicy;
258
285
  }
259
286
 
287
+ /** Filters for {@link makeTrustBadgesNamespace}'s `list` (REB-996). */
288
+ interface TrustBadgeListParams {
289
+ /** Only badges of this type (e.g. `'SUBVENTION'`). Omit for every type. */
290
+ type?: TrustBadgeType;
291
+ }
292
+
260
293
  /**
261
294
  * Stripe Checkout redirect-origin guard (REB-836, defence in depth).
262
295
  *
@@ -327,7 +360,10 @@ declare function createRebluClient(config: RebluClientConfig): {
327
360
  get: (cache?: RebluCachePolicy) => Promise<_reblu_site_contracts.Legal>;
328
361
  };
329
362
  trustBadges: {
330
- list: (cache?: RebluCachePolicy) => Promise<ListResult<_reblu_site_contracts.TrustBadge>>;
363
+ list: {
364
+ (cache?: RebluCachePolicy): Promise<ListResult<_reblu_site_contracts.TrustBadge>>;
365
+ (params: TrustBadgeListParams, cache?: RebluCachePolicy): Promise<ListResult<_reblu_site_contracts.TrustBadge>>;
366
+ };
331
367
  };
332
368
  ctas: {
333
369
  list: (slot: string, cache?: RebluCachePolicy) => Promise<ListResult<_reblu_site_contracts.Cta>>;
@@ -365,13 +401,16 @@ declare function createRebluClient(config: RebluClientConfig): {
365
401
  enrollmentRequest: (body: AcademyEnrollmentRequestInput) => Promise<AcademyEnrollmentRequestResult>;
366
402
  };
367
403
  contactForm: {
368
- submit: (body: _reblu_site_contracts.ContactFormInput) => Promise<_reblu_site_contracts.ContactFormSubmission>;
404
+ submit: (body: _reblu_site_contracts.ContactFormInput, options?: RebluMutationOptions) => Promise<_reblu_site_contracts.ContactFormSubmission>;
369
405
  };
370
406
  leads: {
371
407
  create: (body: _reblu_site_contracts.LeadInput) => Promise<_reblu_site_contracts.LeadSubmission>;
372
408
  };
409
+ dataRequests: {
410
+ submit: (body: _reblu_site_contracts.DataRequestInput, options?: RebluMutationOptions) => Promise<_reblu_site_contracts.DataRequestSubmission>;
411
+ };
373
412
  };
374
413
  /** The assembled client returned by {@link createRebluClient}. */
375
414
  type RebluClient = ReturnType<typeof createRebluClient>;
376
415
 
377
- export { type AcademyCourseListParams, type AcademyEnrollmentRequestInput, type AcademyEnrollmentRequestResult, AmbiguousPageSlugError, ApiError, type ApiErrorBody, type ApiErrorDetails, type BlogListParams, DEFAULT_CHECKOUT_REDIRECT_HOSTS, type FeaturedPagesOptions, type ListResult, type PageBySlugOptions, type PageSlugCandidate, type PagesListParams, PlainCacheAdapter, type RebluCacheAdapter, type RebluCachePolicy, type RebluClient, type RebluClientConfig, type ShopProductsParams, UnsafeCheckoutRedirectError, createRebluClient, isSafeCheckoutRedirectUrl };
416
+ export { type AcademyCourseListParams, type AcademyEnrollmentRequestInput, type AcademyEnrollmentRequestResult, AmbiguousPageSlugError, ApiError, type ApiErrorBody, type ApiErrorDetails, type BlogListParams, DEFAULT_CHECKOUT_REDIRECT_HOSTS, type FeaturedPagesOptions, type ListResult, type PageBySlugOptions, type PageSlugCandidate, type PagesListParams, PlainCacheAdapter, type RebluCacheAdapter, type RebluCachePolicy, type RebluClient, type RebluClientConfig, type RebluMutationOptions, type ShopProductsParams, type TrustBadgeListParams, UnsafeCheckoutRedirectError, VISITOR_IP_HEADER, createRebluClient, isSafeCheckoutRedirectUrl, visitorIpFromHeaders };
package/dist/index.js CHANGED
@@ -164,11 +164,33 @@ async function requestList(ctx, path, opts) {
164
164
  const body = await request(ctx, path, opts);
165
165
  return { data: body.data, meta: body.meta };
166
166
  }
167
- async function requestMutation(ctx, path, body) {
167
+ var VISITOR_IP_HEADER = "x-reblu-visitor-ip";
168
+ function visitorIpFromHeaders(headers) {
169
+ return headers.get("x-forwarded-for")?.split(",")[0]?.trim() || headers.get("x-real-ip")?.trim() || void 0;
170
+ }
171
+ var RESERVED_HEADERS = /* @__PURE__ */ new Set([
172
+ "x-reblu-api-key",
173
+ "x-reblu-api-version",
174
+ "content-type",
175
+ VISITOR_IP_HEADER
176
+ ]);
177
+ function extraHeaders(options) {
178
+ if (!options?.headers && !options?.visitorIp) return void 0;
179
+ const safe = {};
180
+ for (const [name, value] of Object.entries(options.headers ?? {})) {
181
+ if (!RESERVED_HEADERS.has(name.toLowerCase())) safe[name] = value;
182
+ }
183
+ const visitorIp = options.visitorIp?.trim();
184
+ if (visitorIp) safe[VISITOR_IP_HEADER] = visitorIp;
185
+ return safe;
186
+ }
187
+ async function requestMutation(ctx, path, body, options) {
188
+ const headers = extraHeaders(options);
168
189
  const envelope = await request(ctx, path, {
169
190
  method: "POST",
170
191
  body: JSON.stringify(body),
171
- cachePolicy: { revalidate: false }
192
+ cachePolicy: { revalidate: false },
193
+ ...headers ? { headers } : {}
172
194
  });
173
195
  return envelope.data;
174
196
  }
@@ -242,8 +264,18 @@ function makeConfigNamespace(ctx) {
242
264
  // src/domains/contact-form.ts
243
265
  function makeContactFormNamespace(ctx) {
244
266
  return {
245
- /** Submit a contact form. Returns the created lead acknowledgement. */
246
- submit: (body) => requestMutation(ctx, "/api/site/contact-form", body)
267
+ /**
268
+ * Submit a contact form. Returns the created lead acknowledgement.
269
+ * Server-side (a Server Action / Route Handler), pass `options.visitorIp`
270
+ * (from the incoming request, see `visitorIpFromHeaders`) so base-api's
271
+ * per-IP limit (REB-1005) applies to the real visitor.
272
+ */
273
+ submit: (body, options) => requestMutation(
274
+ ctx,
275
+ "/api/site/contact-form",
276
+ body,
277
+ options
278
+ )
247
279
  };
248
280
  }
249
281
 
@@ -254,6 +286,24 @@ function makeCtasNamespace(ctx) {
254
286
  };
255
287
  }
256
288
 
289
+ // src/domains/data-requests.ts
290
+ function makeDataRequestsNamespace(ctx) {
291
+ return {
292
+ /**
293
+ * Submit a data-subject request. Returns its acknowledgement with the 72h
294
+ * deadline. Server-side, pass `options.visitorIp` (from the incoming
295
+ * request, see `visitorIpFromHeaders`) so base-api's per-IP limit
296
+ * (REB-1005) sees the real visitor.
297
+ */
298
+ submit: (body, options) => requestMutation(
299
+ ctx,
300
+ "/api/site/data-requests",
301
+ body,
302
+ options
303
+ )
304
+ };
305
+ }
306
+
257
307
  // src/domains/leads.ts
258
308
  function makeLeadsNamespace(ctx) {
259
309
  return {
@@ -401,10 +451,20 @@ function makeShopNamespace(ctx) {
401
451
  }
402
452
 
403
453
  // src/domains/trust-badges.ts
454
+ function isCachePolicy(value) {
455
+ return Object.prototype.hasOwnProperty.call(value, "revalidate");
456
+ }
404
457
  function makeTrustBadgesNamespace(ctx) {
405
- return {
406
- list: (cache) => requestList(ctx, "/api/site/trust-badges", { cachePolicy: cache })
407
- };
458
+ function list(paramsOrCache, cache) {
459
+ const legacyCache = paramsOrCache && isCachePolicy(paramsOrCache) ? paramsOrCache : void 0;
460
+ const params = legacyCache ? void 0 : paramsOrCache;
461
+ return requestList(
462
+ ctx,
463
+ buildUrl("/api/site/trust-badges", { type: params?.type }),
464
+ { cachePolicy: legacyCache ?? cache }
465
+ );
466
+ }
467
+ return { list };
408
468
  }
409
469
 
410
470
  // src/index.ts
@@ -432,10 +492,11 @@ function createRebluClient(config) {
432
492
  shop: makeShopNamespace(ctx),
433
493
  academy: makeAcademyNamespace(ctx),
434
494
  contactForm: makeContactFormNamespace(ctx),
435
- leads: makeLeadsNamespace(ctx)
495
+ leads: makeLeadsNamespace(ctx),
496
+ dataRequests: makeDataRequestsNamespace(ctx)
436
497
  };
437
498
  }
438
499
 
439
- export { AmbiguousPageSlugError, ApiError, DEFAULT_CHECKOUT_REDIRECT_HOSTS, PlainCacheAdapter, UnsafeCheckoutRedirectError, createRebluClient, isSafeCheckoutRedirectUrl };
500
+ export { AmbiguousPageSlugError, ApiError, DEFAULT_CHECKOUT_REDIRECT_HOSTS, PlainCacheAdapter, UnsafeCheckoutRedirectError, VISITOR_IP_HEADER, createRebluClient, isSafeCheckoutRedirectUrl, visitorIpFromHeaders };
440
501
  //# sourceMappingURL=index.js.map
441
502
  //# sourceMappingURL=index.js.map