@fanvue/builder-sdk 0.4.0 → 0.5.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 (39) hide show
  1. package/README.md +284 -0
  2. package/dist/bridge/index.d.ts +1 -1
  3. package/dist/bridge/index.js +2 -2
  4. package/dist/{bridge-CGVtI3hr.js → bridge-QnF7P4Co.js} +2 -2
  5. package/dist/{bridge-CGVtI3hr.js.map → bridge-QnF7P4Co.js.map} +1 -1
  6. package/dist/core/index.d.ts +2 -2
  7. package/dist/core/index.js +3 -3
  8. package/dist/{core-BqdYUMrJ.js → core-BhKiA55a.js} +1318 -9
  9. package/dist/core-BhKiA55a.js.map +1 -0
  10. package/dist/index-C3CXrRiw.d.ts +1412 -0
  11. package/dist/index-C3CXrRiw.d.ts.map +1 -0
  12. package/dist/{index-BRDYLBlc.d.ts → index-CEFYZtlb.d.ts} +2 -2
  13. package/dist/{index-BRDYLBlc.d.ts.map → index-CEFYZtlb.d.ts.map} +1 -1
  14. package/dist/{index-Dr-mZ0qP.d.ts → index-cf3ZMnLJ.d.ts} +26 -2
  15. package/dist/index-cf3ZMnLJ.d.ts.map +1 -0
  16. package/dist/index-v_6Z4Q0u.d.ts +84 -0
  17. package/dist/index-v_6Z4Q0u.d.ts.map +1 -0
  18. package/dist/{schemas-DbyHF7Xi.js → index.cjs-teGsk6HB.js} +1058 -977
  19. package/dist/index.cjs-teGsk6HB.js.map +1 -0
  20. package/dist/nextjs/embedded-app/index.d.ts +2 -2
  21. package/dist/nextjs/embedded-app/index.js +3 -3
  22. package/dist/nextjs/off-platform/index.d.ts +3 -3
  23. package/dist/nextjs/off-platform/index.d.ts.map +1 -1
  24. package/dist/nextjs/off-platform/index.js +4 -4
  25. package/dist/nextjs/off-platform/index.js.map +1 -1
  26. package/dist/nextjs-CvVhtMdb.js +144 -0
  27. package/dist/nextjs-CvVhtMdb.js.map +1 -0
  28. package/dist/react/index.d.ts +2 -2
  29. package/dist/react/index.js +3 -3
  30. package/package.json +1 -1
  31. package/dist/core-BqdYUMrJ.js.map +0 -1
  32. package/dist/index-C4ewLil3.d.ts +0 -48
  33. package/dist/index-C4ewLil3.d.ts.map +0 -1
  34. package/dist/index-ClbZoV_Z.d.ts +0 -420
  35. package/dist/index-ClbZoV_Z.d.ts.map +0 -1
  36. package/dist/index-Dr-mZ0qP.d.ts.map +0 -1
  37. package/dist/nextjs-B5Tqgt_n.js +0 -80
  38. package/dist/nextjs-B5Tqgt_n.js.map +0 -1
  39. package/dist/schemas-DbyHF7Xi.js.map +0 -1
@@ -0,0 +1,1412 @@
1
+ import { C as $strip, S as $loose, _ as ZodType, a as ZodDefault, c as ZodLiteral, d as ZodObject, f as ZodOptional, g as ZodTransform, h as ZodString, i as ZodCatch, l as ZodNullable, n as ZodArray, o as ZodDiscriminatedUnion, p as ZodPipe, r as ZodBoolean, s as ZodEnum, t as Result, u as ZodNumber, v as ZodURL, x as output, y as ZodUnion } from "./index-cf3ZMnLJ.js";
2
+
3
+ //#region src/core/constants.d.ts
4
+ /** The `Bearer ` token prefix (includes trailing space). */
5
+ declare const BEARER_PREFIX: "Bearer ";
6
+ /** Response header carrying a refreshed session JWT back to the client. */
7
+ declare const HEADER_UPDATED_SESSION: "X-Updated-Session";
8
+ //#endregion
9
+ //#region src/core/contracts/access-mode.d.ts
10
+ /**
11
+ * Access modes an experience can propose or carry on Fanvue.
12
+ *
13
+ * - `FREE` — visible and playable by anyone.
14
+ * - `SUBSCRIPTION` — requires an active subscription to the creator.
15
+ * - `PAID` — requires a one-off purchase.
16
+ * - `HIDDEN` — reachable only through a share grant, never listed.
17
+ */
18
+ declare const FANVUE_ACCESS_MODES: readonly ["FREE", "SUBSCRIPTION", "PAID", "HIDDEN"];
19
+ /** An experience access mode. */
20
+ type FanvueAccessMode = (typeof FANVUE_ACCESS_MODES)[number];
21
+ /** Zod schema for {@link FanvueAccessMode}. */
22
+ declare const FanvueAccessModeSchema: ZodEnum<{
23
+ FREE: "FREE";
24
+ SUBSCRIPTION: "SUBSCRIPTION";
25
+ PAID: "PAID";
26
+ HIDDEN: "HIDDEN";
27
+ }>;
28
+ /**
29
+ * Every reason the platform reports when a fan **is** entitled to an
30
+ * experience, as emitted by fanguard's `resolveExperienceAccess`.
31
+ *
32
+ * Documented for completeness and for exhaustive logging/analytics; apps should
33
+ * branch on `entitlement.isEntitled` rather than on a specific reason, because
34
+ * this list grows without an API version bump.
35
+ *
36
+ * - `owner` — the requester is the creator who owns the experience.
37
+ * - `entitled_via_free`, `free` — the experience is `FREE`.
38
+ * - `entitled_via_subscription`, `subscribed` — active creator subscription.
39
+ * - `entitled_via_share` — reached through a share grant.
40
+ * - `purchased` — one-off purchase of the experience.
41
+ * - `app_subscribed` — subscription to the app itself.
42
+ * - `purchased_before_mode_change`, `app_subscribed_before_mode_change` —
43
+ * entitlement grandfathered from before the creator changed the access mode.
44
+ * - `hidden_token_exchange` — the documented exemption to the fail-closed rule:
45
+ * a `HIDDEN` experience exchanges its token without a prior grant.
46
+ */
47
+ declare const EXPERIENCE_ENTITLED_REASONS: readonly ["owner", "entitled_via_free", "entitled_via_share", "entitled_via_subscription", "free", "subscribed", "purchased", "app_subscribed", "purchased_before_mode_change", "app_subscribed_before_mode_change", "hidden_token_exchange"];
48
+ /**
49
+ * A reason the platform reports for an entitled exchange.
50
+ *
51
+ * Open-ended (`string & {}`) for forward compatibility: the platform can add a
52
+ * reason at any time, and an app must not break on one it has not seen.
53
+ */
54
+ type ExperienceEntitledReason = (typeof EXPERIENCE_ENTITLED_REASONS)[number] | (string & {});
55
+ /**
56
+ * Every reason the platform reports when a fan is **denied** an experience.
57
+ *
58
+ * These are the only three values fanguard emits today; the type stays open for
59
+ * forward compatibility.
60
+ */
61
+ declare const EXPERIENCE_DENIAL_REASONS: readonly ["not_subscribed", "not_purchased", "no_share_grant"];
62
+ /**
63
+ * A reason the platform reports for a denied exchange.
64
+ *
65
+ * Open-ended (`string & {}`) so a newly added reason narrows to `null` in
66
+ * {@link accessModeFromDenialReason} instead of failing to type-check.
67
+ */
68
+ type ExperienceDenialReason = (typeof EXPERIENCE_DENIAL_REASONS)[number] | (string & {});
69
+ /**
70
+ * Recovers the access mode a denied experience must be in, from the denial
71
+ * reason.
72
+ *
73
+ * The token exchange fails closed: an unentitled fan gets a 403 carrying only a
74
+ * denial `reason`, and the experience payload — with it the access mode — is
75
+ * withheld. Mapping the reason back is the only way a locked screen can show
76
+ * the right copy ("subscribe" vs "buy" vs "ask for the link"). An unrecognised
77
+ * reason maps to `null`, meaning "show the generic locked copy".
78
+ *
79
+ * @param reason - The `reason` field from the platform's 403 body.
80
+ * @returns The implied access mode, or `null` when the reason is unrecognised.
81
+ * @example
82
+ * accessModeFromDenialReason('not_subscribed'); // 'SUBSCRIPTION'
83
+ * accessModeFromDenialReason('not_purchased'); // 'PAID'
84
+ * accessModeFromDenialReason('no_share_grant'); // 'HIDDEN'
85
+ * accessModeFromDenialReason('something_new'); // null
86
+ */
87
+ declare function accessModeFromDenialReason(reason: ExperienceDenialReason): FanvueAccessMode | null;
88
+ //#endregion
89
+ //#region src/core/contracts/env.d.ts
90
+ /**
91
+ * Zod schema for the `FANVUE_*` environment contract.
92
+ *
93
+ * - `FANVUE_APP_UUID` — the app's UUID on Fanvue. Checked against the `appUuid`
94
+ * an exchanged experience token carries, so a token minted for another app is
95
+ * rejected. Defaults to `""`.
96
+ * - `FANVUE_CLIENT_ID` / `FANVUE_CLIENT_SECRET` / `FANVUE_OAUTH_REDIRECT_URI` —
97
+ * OAuth client credentials. Default to `""`; see {@link isFanvueConfigured}.
98
+ * - `FANVUE_API_BASE_URL` — bare origin of the Fanvue API (no `/v0` suffix).
99
+ * Blank falls back to the default.
100
+ * - `FANVUE_AUTH_BASE_URL` — origin of the Fanvue authorization server. Blank
101
+ * falls back to the default.
102
+ * - `FANVUE_WEB_ORIGIN` — origin of the Fanvue web shell. The only variable
103
+ * with real URL validation, because it is concatenated into hrefs a creator
104
+ * or fan clicks: a schemeless value ships a dead link. Blank falls back to
105
+ * the default. Note this does not constrain the scheme.
106
+ * - `FANVUE_API_VERSION` — value sent as `X-Fanvue-API-Version`. Blank falls
107
+ * back to the default.
108
+ * - `FANVUE_EXPERIENCE_URL_TEMPLATE` — optional operator override for
109
+ * per-experience share links. Empty means "derive the URL".
110
+ */
111
+ declare const FanvueEnvSchema: ZodObject<{
112
+ FANVUE_APP_UUID: ZodDefault<ZodString>;
113
+ FANVUE_CLIENT_ID: ZodDefault<ZodString>;
114
+ FANVUE_CLIENT_SECRET: ZodDefault<ZodString>;
115
+ FANVUE_OAUTH_REDIRECT_URI: ZodDefault<ZodString>;
116
+ FANVUE_API_BASE_URL: ZodPipe<ZodTransform<unknown, unknown>, ZodDefault<ZodString>>;
117
+ FANVUE_AUTH_BASE_URL: ZodPipe<ZodTransform<unknown, unknown>, ZodDefault<ZodString>>;
118
+ FANVUE_WEB_ORIGIN: ZodPipe<ZodTransform<unknown, unknown>, ZodDefault<ZodURL>>;
119
+ FANVUE_API_VERSION: ZodPipe<ZodTransform<unknown, unknown>, ZodDefault<ZodString>>;
120
+ FANVUE_EXPERIENCE_URL_TEMPLATE: ZodDefault<ZodString>;
121
+ }, $strip>;
122
+ /** The validated `FANVUE_*` environment. */
123
+ type FanvueEnv = output<typeof FanvueEnvSchema>;
124
+ /** A read-only view of an environment variable bag. */
125
+ type EnvSource = Readonly<Record<string, string | undefined>>;
126
+ /**
127
+ * Validates an environment bag against {@link FanvueEnvSchema}.
128
+ *
129
+ * Pure and uncached — takes the environment as a parameter so it is unit
130
+ * testable. Prefer {@link fanvueEnv} in application code.
131
+ *
132
+ * @param source - The variables to validate.
133
+ * @returns The validated environment.
134
+ * @throws If a present value fails validation (today: a non-URL
135
+ * `FANVUE_WEB_ORIGIN`).
136
+ */
137
+ declare function parseFanvueEnv(source: EnvSource): FanvueEnv;
138
+ /**
139
+ * Returns the validated `FANVUE_*` environment, parsing `process.env` on first
140
+ * use and caching the result.
141
+ *
142
+ * @returns The validated environment.
143
+ * @throws If a present value fails validation.
144
+ * @example
145
+ * const baseUrl = fanvueEnv().FANVUE_API_BASE_URL;
146
+ */
147
+ declare function fanvueEnv(): FanvueEnv;
148
+ /**
149
+ * Clears every piece of memoised state in this module: the {@link fanvueEnv}
150
+ * cache and {@link flagEnabled}'s warned-flag set.
151
+ *
152
+ * Intended for tests, which mutate `process.env` between cases and would
153
+ * otherwise see whichever value the first case happened to freeze. The warn-once
154
+ * set is cleared for the same reason — left in place, whether a case sees the
155
+ * warning depends on which case ran first.
156
+ */
157
+ declare function resetFanvueEnvCache(): void;
158
+ /**
159
+ * Whether the four Fanvue credential variables are all set.
160
+ *
161
+ * Covers exactly the values OAuth configuration requires, because config
162
+ * creation throws on a missing one and an uncaught throw in a route surfaces as
163
+ * a 500. Call this first and return a "not configured" response instead.
164
+ *
165
+ * @param env - The environment to check. Defaults to the cached
166
+ * {@link fanvueEnv}.
167
+ * @returns `true` when app UUID, client id, client secret and redirect URI are
168
+ * all non-blank. Whitespace-only counts as unset, matching how the schema
169
+ * treats blank values everywhere else in this module.
170
+ * @example
171
+ * if (!isFanvueConfigured()) {
172
+ * return Response.json({ error: { code: 'fanvue_not_configured', message: '…' } }, { status: 503 });
173
+ * }
174
+ */
175
+ declare function isFanvueConfigured(env?: FanvueEnv): boolean;
176
+ /**
177
+ * Reads a boolean environment flag.
178
+ *
179
+ * A strict `=== "true"` is how one app's `MCP_ENABLED="TRUE"` switched a whole
180
+ * subsystem off in staging: every route answered 404 and the value looked
181
+ * correct in the dashboard. A flag that fails closed on a casing typo and says
182
+ * nothing costs more than one that is simply off, because there is no signal to
183
+ * find. So surrounding whitespace and case do not matter, and a value that
184
+ * still cannot be read as a boolean is warned about **once per flag name** and
185
+ * treated as off — off is the safe answer, but not a silent one.
186
+ *
187
+ * @param name - The environment variable name.
188
+ * @param source - The variables to read from. Defaults to `process.env`.
189
+ * @returns `true` only for `"true"` (after trimming and lowercasing).
190
+ * @see {@link resetFanvueEnvCache} to clear the warned-flag set between tests.
191
+ * @example
192
+ * flagEnabled('MY_APP_DEV_MODE'); // reads process.env.MY_APP_DEV_MODE
193
+ * flagEnabled('MY_APP_DEV_MODE', { MY_APP_DEV_MODE: ' TRUE ' }); // true
194
+ */
195
+ declare function flagEnabled(name: string, source?: EnvSource): boolean;
196
+ //#endregion
197
+ //#region src/core/contracts/errors.d.ts
198
+ /**
199
+ * `{ message }` — 404s, contactability rejections, invalid creator UUIDs, and
200
+ * the experience-exchange 403 (which adds `reason`). `code` appears on
201
+ * bulk-media per-item errors.
202
+ */
203
+ declare const FanvueMessageErrorBodySchema: ZodObject<{
204
+ message: ZodString;
205
+ reason: ZodCatch<ZodOptional<ZodNullable<ZodString>>>;
206
+ code: ZodCatch<ZodOptional<ZodNullable<ZodString>>>;
207
+ }, $loose>;
208
+ /**
209
+ * `{ error }` — 401/403/500/503 and upstream tRPC failures, where `error` is the
210
+ * human text; and `{ error, message }` — version and age-verification failures,
211
+ * where `error` is a machine-readable code and `message` the human text.
212
+ */
213
+ declare const FanvueErrorFieldBodySchema: ZodObject<{
214
+ error: ZodString;
215
+ message: ZodCatch<ZodOptional<ZodNullable<ZodString>>>;
216
+ reason: ZodCatch<ZodOptional<ZodNullable<ZodString>>>;
217
+ }, $loose>;
218
+ /** `{ errors: [{ code, message }] }` — Zod request-validation failures. */
219
+ declare const FanvueIssueListErrorBodySchema: ZodObject<{
220
+ errors: ZodArray<ZodObject<{
221
+ code: ZodCatch<ZodOptional<ZodNullable<ZodString>>>;
222
+ message: ZodString;
223
+ }, $loose>>;
224
+ }, $loose>;
225
+ /** `{ errors: ["Invalid page: too small"] }` — the pagination middleware. */
226
+ declare const FanvueStringListErrorBodySchema: ZodObject<{
227
+ errors: ZodArray<ZodString>;
228
+ }, $loose>;
229
+ /**
230
+ * Any of the four documented Fanvue API error bodies.
231
+ *
232
+ * Member order matters: the `errors` array shapes are tried first because they
233
+ * carry no `message`/`error` key of their own, then `{ error }` (which may also
234
+ * carry a `message`), then `{ message }` alone. Exported for callers that want
235
+ * to validate a body in one step; {@link parseFanvueErrorBody} tries the members
236
+ * individually so that one unreadable field cannot cost the whole body.
237
+ */
238
+ declare const FanvueErrorBodySchema: ZodUnion<readonly [ZodObject<{
239
+ errors: ZodArray<ZodString>;
240
+ }, $loose>, ZodObject<{
241
+ errors: ZodArray<ZodObject<{
242
+ code: ZodCatch<ZodOptional<ZodNullable<ZodString>>>;
243
+ message: ZodString;
244
+ }, $loose>>;
245
+ }, $loose>, ZodObject<{
246
+ error: ZodString;
247
+ message: ZodCatch<ZodOptional<ZodNullable<ZodString>>>;
248
+ reason: ZodCatch<ZodOptional<ZodNullable<ZodString>>>;
249
+ }, $loose>, ZodObject<{
250
+ message: ZodString;
251
+ reason: ZodCatch<ZodOptional<ZodNullable<ZodString>>>;
252
+ code: ZodCatch<ZodOptional<ZodNullable<ZodString>>>;
253
+ }, $loose>]>;
254
+ /** A parsed Fanvue API error body, before normalisation. */
255
+ type FanvueErrorBody = output<typeof FanvueErrorBodySchema>;
256
+ /**
257
+ * A Fanvue API error body reduced to the three fields a caller can act on.
258
+ *
259
+ * @property message - Human-readable text for logs and debugging. Empty string
260
+ * when the body carried none; callers substitute their own generic message
261
+ * (which is what an end user should see — never upstream text).
262
+ * @property reason - The machine-readable reason, when the platform sent one.
263
+ * Load-bearing on the experience exchange: a 403 **with** a reason is an
264
+ * entitlement refusal, a bare 403 is an app-binding mismatch.
265
+ * @property code - The machine-readable code, when the platform sent one.
266
+ */
267
+ type NormalisedFanvueError = {
268
+ message: string;
269
+ reason: string | null;
270
+ code: string | null;
271
+ };
272
+ /**
273
+ * Normalises any Fanvue API error body into {@link NormalisedFanvueError}.
274
+ *
275
+ * Never throws and never inspects the HTTP status: a body it cannot read yields
276
+ * an empty `message`, which the transport replaces with its own status-derived
277
+ * text. Multiple validation issues are joined with `; ` so a single log line
278
+ * carries all of them.
279
+ *
280
+ * The `{ error }` shape is disambiguated by whether a `message` sits next to it:
281
+ * `{ error: 'age_verification_required', message: 'Verify…' }` puts the code in
282
+ * `code` and the text in `message`, while a bare `{ error: 'Unauthorized' }` is
283
+ * treated as text only.
284
+ *
285
+ * @param body - A parsed JSON body (or anything at all).
286
+ * @returns The normalised error fields.
287
+ * @example
288
+ * parseFanvueErrorBody({ message: 'Fan is not entitled', reason: 'not_subscribed' });
289
+ * // { message: 'Fan is not entitled', reason: 'not_subscribed', code: null }
290
+ * parseFanvueErrorBody({ errors: [{ code: 'invalid_type', message: 'Expected string' }] });
291
+ * // { message: 'Expected string', reason: null, code: 'invalid_type' }
292
+ * parseFanvueErrorBody('<html>502</html>');
293
+ * // { message: '', reason: null, code: null }
294
+ */
295
+ declare function parseFanvueErrorBody(body: unknown): NormalisedFanvueError;
296
+ /**
297
+ * Zod schema for an app's own error envelope: `{ error: { code, message } }`.
298
+ *
299
+ * Every Fanvue app helper produces this shape and every client parses it, so it
300
+ * is a contract rather than a convention. The `message` is what an end user
301
+ * reads — upstream and internal text belongs in the log, never in this body.
302
+ */
303
+ declare const AppErrorEnvelopeSchema: ZodObject<{
304
+ error: ZodObject<{
305
+ code: ZodString;
306
+ message: ZodString;
307
+ }, $strip>;
308
+ }, $strip>;
309
+ /** An app's error response body. */
310
+ type AppErrorEnvelope = output<typeof AppErrorEnvelopeSchema>;
311
+ /**
312
+ * Canonical app-facing error codes for Fanvue-grant failures.
313
+ *
314
+ * - `fanvue_reconnect_required` (401) — the stored Fanvue grant is missing or
315
+ * dead. The creator must reopen the app from Fanvue.
316
+ * - `fanvue_permission_denied` (403) — the grant is alive but lacks the scope
317
+ * for this capability. Reconnecting re-consents the new scope.
318
+ * - `fanvue_api_error` (502) — the Fanvue API failed for any other reason.
319
+ */
320
+ declare const FANVUE_APP_ERROR_CODES: {
321
+ readonly reconnectRequired: "fanvue_reconnect_required";
322
+ readonly permissionDenied: "fanvue_permission_denied";
323
+ readonly apiError: "fanvue_api_error";
324
+ };
325
+ /** One of the canonical app-facing Fanvue error codes. */
326
+ type FanvueAppErrorCode = (typeof FANVUE_APP_ERROR_CODES)[keyof typeof FANVUE_APP_ERROR_CODES];
327
+ /**
328
+ * The two 401 codes that mean the **Fanvue grant** is gone while the **app
329
+ * session** is still valid.
330
+ *
331
+ * A client that treats every 401 as session expiry signs the creator out of a
332
+ * working session and offers "Reload" — the one action that cannot restore a
333
+ * missing grant. Check membership of this set before running expiry handling,
334
+ * and route these to a reconnect prompt instead.
335
+ *
336
+ * @example
337
+ * if (status === 401 && !NON_SESSION_401_CODES.has(code)) {
338
+ * onSessionExpired();
339
+ * }
340
+ */
341
+ declare const NON_SESSION_401_CODES: ReadonlySet<string>;
342
+ /**
343
+ * Zod schema for an OAuth 2.0 error response body, normalised to camelCase.
344
+ *
345
+ * Only `error` and `error_description` are kept. Hydra also returns
346
+ * `error_hint` and `error_debug`, which can echo request details back and have
347
+ * no place in an app's own error path.
348
+ */
349
+ declare const OAuthErrorBodySchema: ZodPipe<ZodObject<{
350
+ error: ZodString;
351
+ error_description: ZodOptional<ZodNullable<ZodString>>;
352
+ }, $loose>, ZodTransform<{
353
+ error: string;
354
+ errorDescription: string | null;
355
+ }, {
356
+ [x: string]: unknown;
357
+ error: string;
358
+ error_description?: string | null | undefined;
359
+ }>>;
360
+ /**
361
+ * An OAuth 2.0 error response body.
362
+ *
363
+ * @property error - The RFC 6749 error code (`invalid_grant`, `invalid_client`,
364
+ * `invalid_scope`, …). This is the distinction that tells a dead refresh token
365
+ * apart from bad client credentials.
366
+ * @property errorDescription - The provider's human-readable description, or
367
+ * `null`. For logs and debugging only.
368
+ */
369
+ type OAuthErrorBody = output<typeof OAuthErrorBodySchema>;
370
+ //#endregion
371
+ //#region src/core/contracts/experience-protocol.d.ts
372
+ /** Message type an app posts to request the publish modal. */
373
+ declare const PUBLISH_REQUEST_MESSAGE: "fanvue:experience:publish-request";
374
+ /** Message type Fanvue posts back with the outcome of a publish request. */
375
+ declare const PUBLISH_RESULT_MESSAGE: "fanvue:experience:publish-result";
376
+ /** Message type an app posts to request the unpublish confirmation. */
377
+ declare const UNPUBLISH_REQUEST_MESSAGE: "fanvue:experience:unpublish-request";
378
+ /** Message type Fanvue posts back with the outcome of an unpublish request. */
379
+ declare const UNPUBLISH_RESULT_MESSAGE: "fanvue:experience:unpublish-result";
380
+ /**
381
+ * Every message type in the protocol, in request/result pairs.
382
+ *
383
+ * Useful for filtering an incoming `MessageEvent` before schema validation.
384
+ */
385
+ declare const EXPERIENCE_MESSAGE_TYPES: readonly ["fanvue:experience:publish-request", "fanvue:experience:publish-result", "fanvue:experience:unpublish-request", "fanvue:experience:unpublish-result"];
386
+ /** One of the four `fanvue:experience:*` message types. */
387
+ type ExperienceMessageType = (typeof EXPERIENCE_MESSAGE_TYPES)[number];
388
+ /**
389
+ * Zod schema for the app → Fanvue publish request.
390
+ *
391
+ * `token` is the opaque request token minted by
392
+ * `POST /v0/experiences/request-token` with `action: "publish"`.
393
+ */
394
+ declare const PublishRequestMessageSchema: ZodObject<{
395
+ type: ZodLiteral<"fanvue:experience:publish-request">;
396
+ token: ZodString;
397
+ }, $strip>;
398
+ /** The app → Fanvue publish request message. */
399
+ type PublishRequestMessage = output<typeof PublishRequestMessageSchema>;
400
+ /**
401
+ * Zod schema for the app → Fanvue unpublish request.
402
+ *
403
+ * `token` is the opaque request token minted by
404
+ * `POST /v0/experiences/request-token` with `action: "unpublish"`.
405
+ */
406
+ declare const UnpublishRequestMessageSchema: ZodObject<{
407
+ type: ZodLiteral<"fanvue:experience:unpublish-request">;
408
+ token: ZodString;
409
+ }, $strip>;
410
+ /** The app → Fanvue unpublish request message. */
411
+ type UnpublishRequestMessage = output<typeof UnpublishRequestMessageSchema>;
412
+ /**
413
+ * Zod schema for the Fanvue → app publish result.
414
+ *
415
+ * `experienceId` is optional rather than nullable because it mirrors the wire
416
+ * shape: Fanvue omits the key entirely when the creator cancels. `cancelled`
417
+ * covers both a dismissed modal and a server-side rejection of the request
418
+ * token — the two are not distinguishable from this side.
419
+ *
420
+ * Any string is accepted, including `""`. A receiver must not reject an
421
+ * otherwise well-formed result over a useless id: from the app's side a rejected
422
+ * message is indistinguishable from no reply at all, so a publish that did
423
+ * happen would hang until the bridge timed out. Treat a falsy `experienceId` the
424
+ * way an absent one is treated.
425
+ */
426
+ declare const PublishResultMessageSchema: ZodObject<{
427
+ type: ZodLiteral<"fanvue:experience:publish-result">;
428
+ status: ZodEnum<{
429
+ published: "published";
430
+ cancelled: "cancelled";
431
+ }>;
432
+ experienceId: ZodOptional<ZodString>;
433
+ }, $strip>;
434
+ /** The Fanvue → app publish result message. */
435
+ type PublishResultMessage = output<typeof PublishResultMessageSchema>;
436
+ /** Zod schema for the Fanvue → app unpublish result. */
437
+ declare const UnpublishResultMessageSchema: ZodObject<{
438
+ type: ZodLiteral<"fanvue:experience:unpublish-result">;
439
+ status: ZodEnum<{
440
+ cancelled: "cancelled";
441
+ unpublished: "unpublished";
442
+ }>;
443
+ }, $strip>;
444
+ /** The Fanvue → app unpublish result message. */
445
+ type UnpublishResultMessage = output<typeof UnpublishResultMessageSchema>;
446
+ /**
447
+ * Zod schema for any message in the protocol, discriminated on `type`.
448
+ *
449
+ * @example
450
+ * const parsed = ExperienceMessageSchema.safeParse(event.data);
451
+ * if (parsed.success && parsed.data.type === PUBLISH_RESULT_MESSAGE) {
452
+ * // parsed.data is a PublishResultMessage
453
+ * }
454
+ */
455
+ declare const ExperienceMessageSchema: ZodDiscriminatedUnion<[ZodObject<{
456
+ type: ZodLiteral<"fanvue:experience:publish-request">;
457
+ token: ZodString;
458
+ }, $strip>, ZodObject<{
459
+ type: ZodLiteral<"fanvue:experience:publish-result">;
460
+ status: ZodEnum<{
461
+ published: "published";
462
+ cancelled: "cancelled";
463
+ }>;
464
+ experienceId: ZodOptional<ZodString>;
465
+ }, $strip>, ZodObject<{
466
+ type: ZodLiteral<"fanvue:experience:unpublish-request">;
467
+ token: ZodString;
468
+ }, $strip>, ZodObject<{
469
+ type: ZodLiteral<"fanvue:experience:unpublish-result">;
470
+ status: ZodEnum<{
471
+ cancelled: "cancelled";
472
+ unpublished: "unpublished";
473
+ }>;
474
+ }, $strip>], "type">;
475
+ /** Any message in the `fanvue:experience:*` protocol. */
476
+ type ExperienceMessage = output<typeof ExperienceMessageSchema>;
477
+ /**
478
+ * Narrows unknown `MessageEvent` data to a {@link PublishResultMessage}.
479
+ *
480
+ * @param value - The raw `event.data` from a `message` event.
481
+ * @returns `true` when the value validates against the publish-result schema.
482
+ * @example
483
+ * if (isPublishResultMessage(event.data) && isFanvueOrigin(event.origin)) {
484
+ * settle(event.data);
485
+ * }
486
+ */
487
+ declare function isPublishResultMessage(value: unknown): value is PublishResultMessage;
488
+ /**
489
+ * Narrows unknown `MessageEvent` data to an {@link UnpublishResultMessage}.
490
+ *
491
+ * @param value - The raw `event.data` from a `message` event.
492
+ * @returns `true` when the value validates against the unpublish-result schema.
493
+ */
494
+ declare function isUnpublishResultMessage(value: unknown): value is UnpublishResultMessage;
495
+ /**
496
+ * Whether an origin belongs to the Fanvue web shell.
497
+ *
498
+ * Mirrors the `frame-ancestors 'self' https://fanvue.com https://*.fanvue.com`
499
+ * CSP allowlist every embedded app ships. Eden posts its replies with
500
+ * `targetOrigin: "*"`, so validating `event.origin` on receipt is the only
501
+ * app-side control against a sibling frame forging a publish success. Requires
502
+ * `https:` — a plaintext origin is never Fanvue.
503
+ *
504
+ * @param origin - The `event.origin` of an incoming message.
505
+ * @returns `true` for `https://fanvue.com` and any `https://*.fanvue.com` host.
506
+ * @example
507
+ * isFanvueOrigin('https://www.fanvue.com'); // true
508
+ * isFanvueOrigin('http://fanvue.com'); // false (not https)
509
+ * isFanvueOrigin('https://fanvue.com.evil.test'); // false
510
+ */
511
+ declare function isFanvueOrigin(origin: string): boolean;
512
+ //#endregion
513
+ //#region src/core/contracts/pagination.d.ts
514
+ /**
515
+ * Largest `size` the platform accepts on an offset-paginated route.
516
+ *
517
+ * The API responds `400` above this, so callers clamp client-side (see
518
+ * {@link clampPageSize}) rather than turning an over-large request into a
519
+ * failure the caller cannot act on.
520
+ */
521
+ declare const MAX_PAGE_SIZE = 50;
522
+ /** The platform's default `size` when the query parameter is omitted. */
523
+ declare const DEFAULT_PAGE_SIZE = 15;
524
+ /**
525
+ * Clamps a requested page size into the platform's accepted range.
526
+ *
527
+ * Non-finite and non-integer inputs fall back to {@link DEFAULT_PAGE_SIZE}
528
+ * rather than being rounded, because a fractional `size` is a caller bug and
529
+ * the platform would reject it.
530
+ *
531
+ * @param requested - The page size a caller asked for.
532
+ * @returns An integer in `[1, MAX_PAGE_SIZE]`.
533
+ * @example
534
+ * clampPageSize(200); // 50
535
+ * clampPageSize(0); // 1
536
+ * clampPageSize(NaN); // 15
537
+ */
538
+ declare function clampPageSize(requested: number): number;
539
+ /**
540
+ * Zod schema for the `pagination` object on offset-paginated `v0` responses.
541
+ *
542
+ * `size` is the number of items actually returned, not the requested page size
543
+ * (styx builds it as `data.length`), and `hasMore` is the only reliable
544
+ * end-of-list signal — the platform does not send a total on most routes.
545
+ *
546
+ * `size` therefore allows `0`: paging past the end returns `{ data: [], size: 0 }`,
547
+ * which is a well-formed response and must not fail validation. The `1..50`
548
+ * bound belongs to the *request* parameter — see {@link clampPageSize}.
549
+ */
550
+ declare const OffsetPaginationSchema: ZodObject<{
551
+ page: ZodNumber;
552
+ size: ZodNumber;
553
+ hasMore: ZodBoolean;
554
+ }, $strip>;
555
+ /** The `pagination` object on an offset-paginated response. */
556
+ type OffsetPagination = output<typeof OffsetPaginationSchema>;
557
+ /**
558
+ * Zod schema for `GET /v0/subscribers`' hybrid pagination.
559
+ *
560
+ * Offset fields plus a keyset cursor that is `null` when the caller sorts by
561
+ * name (that ordering cannot be resumed from a cursor).
562
+ */
563
+ declare const HybridPaginationSchema: ZodObject<{
564
+ page: ZodNumber;
565
+ size: ZodNumber;
566
+ hasMore: ZodBoolean;
567
+ nextCursor: ZodNullable<ZodString>;
568
+ }, $strip>;
569
+ /** The `pagination` object on the hybrid `v0` subscribers response. */
570
+ type HybridPagination = output<typeof HybridPaginationSchema>;
571
+ /**
572
+ * Builds the response schema for an offset-paginated list of `item`.
573
+ *
574
+ * @param item - Schema for a single row of `data`.
575
+ * @returns A schema for `{ data, pagination }`.
576
+ * @example
577
+ * const SubscribersPageSchema = offsetPageSchema(SubscriberSchema);
578
+ * const page = SubscribersPageSchema.parse(body);
579
+ * page.pagination.hasMore; // boolean
580
+ */
581
+ declare function offsetPageSchema<TItem extends ZodType>(item: TItem): ZodObject<{
582
+ data: ZodArray<TItem>;
583
+ pagination: typeof OffsetPaginationSchema;
584
+ }>;
585
+ /**
586
+ * Builds the response schema for a cursor-paginated list of `item`.
587
+ *
588
+ * `nextCursor` is `null` on the last page. Cursors are opaque and pin the
589
+ * sort, filters and creator they were minted for, so they cannot be replayed
590
+ * against a different query.
591
+ *
592
+ * `total` is the third field of the platform's `v1` keyset envelope
593
+ * (`{ data, nextCursor, total }`): the platform sends a number only where a
594
+ * count is free or cheap, `null` on large keyset lists (the count is the
595
+ * expensive query keyset avoids), and the older `v0` cursor routes omit the
596
+ * key entirely. All three normalise to `total: null` or a number here, so a
597
+ * caller never branches on "absent vs null".
598
+ *
599
+ * @param item - Schema for a single row of `data`.
600
+ * @returns A schema for `{ data, nextCursor, total }`.
601
+ * @example
602
+ * const PaymentsPageSchema = cursorPageSchema(CheckoutPaymentSchema);
603
+ * const page = PaymentsPageSchema.parse(body);
604
+ * const done = page.nextCursor === null;
605
+ */
606
+ declare function cursorPageSchema<TItem extends ZodType>(item: TItem): ZodObject<{
607
+ data: ZodArray<TItem>;
608
+ nextCursor: ZodNullable<ZodString>;
609
+ total: ZodDefault<ZodNullable<ZodNumber>>;
610
+ }>;
611
+ //#endregion
612
+ //#region src/core/defaults.d.ts
613
+ /** Default OAuth scopes requested during authorization. */
614
+ declare const DEFAULT_SCOPES: "openid offline_access offline";
615
+ /** Default OAuth issuer URL for Fanvue authentication. */
616
+ declare const DEFAULT_ISSUER_URL: "https://auth.fanvue.com";
617
+ /** Default base URL for the Fanvue API. */
618
+ declare const DEFAULT_API_BASE_URL: "https://api.fanvue.com";
619
+ /** Default base URL for the Fanvue platform (embedded authorize-on-behalf). */
620
+ declare const DEFAULT_PLATFORM_URL: "https://www.fanvue.com";
621
+ /** The Fanvue API version header value sent with every request. */
622
+ declare const API_VERSION: "2025-06-26";
623
+ /**
624
+ * Validates that a URL belongs to the `fanvue.com` domain.
625
+ *
626
+ * @param url - The URL string to validate.
627
+ * @throws If the URL is malformed or its hostname is not `fanvue.com`
628
+ * (or a subdomain of it).
629
+ */
630
+ declare function assertFanvueDomain(url: string): void;
631
+ //#endregion
632
+ //#region src/core/types.d.ts
633
+ /**
634
+ * Configuration for the OAuth client.
635
+ *
636
+ * @property clientId - The OAuth client ID.
637
+ * @property clientSecret - The OAuth client secret.
638
+ * @property redirectUri - The URI to redirect to after authentication.
639
+ * @property issuerUrl - The OAuth issuer URL, or `null` to use the default.
640
+ * @property apiBaseUrl - The Fanvue API base URL, or `null` to use the default.
641
+ * @property scopes - Additional OAuth scopes to request beyond the defaults, or `null` for defaults only.
642
+ * @property responseMode - The OAuth response mode (e.g., `fragment`), or `null` to omit.
643
+ * @property prompt - The OAuth prompt parameter (e.g., `consent`), or `null` to omit.
644
+ */
645
+ interface OAuthConfig {
646
+ clientId: string;
647
+ clientSecret: string;
648
+ redirectUri: string;
649
+ issuerUrl: string | null;
650
+ apiBaseUrl: string | null;
651
+ scopes: string | null;
652
+ responseMode: string | null;
653
+ prompt: string | null;
654
+ }
655
+ /**
656
+ * The raw token response returned by the OAuth token endpoint.
657
+ *
658
+ * @property access_token - The access token issued by the authorization server.
659
+ * @property refresh_token - The refresh token, or `null` if not provided.
660
+ * @property expires_in - The lifetime in seconds of the access token.
661
+ * @property token_type - The type of the token (e.g., `Bearer`).
662
+ * @property scope - The scope of the access token, or `null` if not provided.
663
+ * @property id_token - The ID token, or `null` if not provided.
664
+ */
665
+ interface TokenResponse {
666
+ access_token: string;
667
+ refresh_token: string | null;
668
+ expires_in: number;
669
+ token_type: string;
670
+ scope: string | null;
671
+ id_token: string | null;
672
+ }
673
+ /**
674
+ * The payload stored inside the encrypted session JWT.
675
+ *
676
+ * @property accessToken - The OAuth access token.
677
+ * @property refreshToken - The OAuth refresh token, or `null` if unavailable.
678
+ * @property expiresAt - The timestamp (in milliseconds) when the access token expires.
679
+ * @property tokenType - The type of the token (e.g., `Bearer`), or `null` if unavailable.
680
+ * @property scope - The granted scope, or `null` if unavailable.
681
+ * @property idToken - The ID token, or `null` if unavailable.
682
+ * @property userUuid - The unique identifier of the authenticated user.
683
+ * @property handle - The user's handle/username.
684
+ * @property displayName - The user's display name.
685
+ * @property isCreator - Whether the user is a creator.
686
+ * @property avatarUrl - The URL of the user's avatar, or `null` if not set.
687
+ */
688
+ interface SessionPayload {
689
+ accessToken: string;
690
+ refreshToken: string | null;
691
+ expiresAt: number;
692
+ tokenType: string | null;
693
+ scope: string | null;
694
+ idToken: string | null;
695
+ userUuid: string;
696
+ handle: string;
697
+ displayName: string;
698
+ isCreator: boolean;
699
+ avatarUrl: string | null;
700
+ }
701
+ /**
702
+ * A Fanvue user profile as returned by the API.
703
+ *
704
+ * @property uuid - The unique identifier of the user.
705
+ * @property email - The user's email address.
706
+ * @property handle - The user's handle/username.
707
+ * @property displayName - The user's display name.
708
+ * @property isCreator - Whether the user is a creator.
709
+ * @property avatarUrl - The URL of the user's avatar, or `null` if not set.
710
+ * @property bannerUrl - The URL of the user's banner, or `null` if not set.
711
+ * @property createdAt - The ISO 8601 timestamp of when the user was created.
712
+ * @property updatedAt - The ISO 8601 timestamp of when the user was last updated, or `null`.
713
+ */
714
+ interface FanvueUser {
715
+ uuid: string;
716
+ email: string;
717
+ handle: string;
718
+ displayName: string;
719
+ isCreator: boolean;
720
+ avatarUrl: string | null;
721
+ bannerUrl: string | null;
722
+ createdAt: string;
723
+ updatedAt: string | null;
724
+ }
725
+ /**
726
+ * Configuration for the embedded-app (delegated authorize-on-behalf) flow.
727
+ *
728
+ * Extends {@link OAuthConfig} with the Fanvue platform base URL that hosts
729
+ * the `authorize-on-behalf` endpoint.
730
+ *
731
+ * @property platformUrl - The Fanvue platform base URL, or `null` to use the default.
732
+ */
733
+ interface EmbeddedAuthConfig extends OAuthConfig {
734
+ platformUrl: string | null;
735
+ }
736
+ /** Error type for JSON parsing failures. */
737
+ type JsonParseError = {
738
+ code: 'JSON_PARSE_ERROR';
739
+ rawText: string;
740
+ message: string;
741
+ };
742
+ /**
743
+ * Error type for OAuth operations (token exchange, refresh).
744
+ *
745
+ * `oauthError` carries the authorization server's own error body when it sent a
746
+ * readable one, and is `null` for network errors and unreadable bodies. It is
747
+ * the only way to tell a retired refresh token (`invalid_grant` — the creator
748
+ * must reconnect) from bad client credentials (`invalid_client` — the app is
749
+ * misconfigured); `statusCode` is `400` for both. Diagnostic only: never render
750
+ * it to an end user.
751
+ */
752
+ type OAuthError = {
753
+ code: 'TOKEN_EXCHANGE_FAILED';
754
+ statusCode: number;
755
+ message: string;
756
+ oauthError: OAuthErrorBody | null;
757
+ } | {
758
+ code: 'TOKEN_REFRESH_FAILED';
759
+ statusCode: number;
760
+ message: string;
761
+ oauthError: OAuthErrorBody | null;
762
+ } | {
763
+ code: 'OAUTH_JSON_PARSE_ERROR';
764
+ rawText: string;
765
+ message: string;
766
+ } | {
767
+ code: 'OAUTH_VALIDATION_ERROR';
768
+ message: string;
769
+ };
770
+ /** Error type for Fanvue API requests. */
771
+ type ApiError = {
772
+ code: 'API_REQUEST_FAILED';
773
+ statusCode: number;
774
+ message: string;
775
+ } | {
776
+ code: 'API_JSON_PARSE_ERROR';
777
+ rawText: string;
778
+ message: string;
779
+ } | {
780
+ code: 'API_VALIDATION_ERROR';
781
+ message: string;
782
+ };
783
+ /** Error type for session JWT verification. */
784
+ type SessionVerifyError = {
785
+ code: 'JWT_VERIFY_FAILED';
786
+ message: string;
787
+ } | {
788
+ code: 'SESSION_VALIDATION_ERROR';
789
+ message: string;
790
+ };
791
+ /**
792
+ * Error type for the embedded authorize-on-behalf step.
793
+ *
794
+ * - `SESSION_TOKEN_REJECTED` — the platform rejected the session token
795
+ * (missing, malformed, or expired — they live ~60 seconds). The user must
796
+ * reopen the embedded surface to receive a fresh one.
797
+ * - `CONSENT_REQUIRED` — the creator has not approved (or has revoked) the
798
+ * in-platform consent for this app. Consent is granted in the Fanvue UI;
799
+ * the app cannot create it.
800
+ * - `AUTHORIZE_ON_BEHALF_FAILED` — any other failure (network error,
801
+ * unexpected status). `statusCode` is `0` for network errors.
802
+ * - `AUTHORIZE_STATE_MISMATCH` — the returned `state` did not match the one
803
+ * sent; the response must not be trusted.
804
+ */
805
+ type EmbeddedAuthError = {
806
+ code: 'SESSION_TOKEN_REJECTED';
807
+ statusCode: number;
808
+ message: string;
809
+ } | {
810
+ code: 'CONSENT_REQUIRED';
811
+ statusCode: number;
812
+ message: string;
813
+ } | {
814
+ code: 'AUTHORIZE_ON_BEHALF_FAILED';
815
+ statusCode: number;
816
+ message: string;
817
+ } | {
818
+ code: 'AUTHORIZE_STATE_MISMATCH';
819
+ message: string;
820
+ } | {
821
+ code: 'EMBEDDED_JSON_PARSE_ERROR';
822
+ rawText: string;
823
+ message: string;
824
+ } | {
825
+ code: 'EMBEDDED_VALIDATION_ERROR';
826
+ message: string;
827
+ };
828
+ //#endregion
829
+ //#region src/core/oauth.d.ts
830
+ /**
831
+ * Builds an OAuth 2.0 authorization URL with PKCE parameters.
832
+ *
833
+ * @param config - The OAuth configuration.
834
+ * @param opts - Optional overrides. Pass `state` to use a deterministic state value.
835
+ * @returns The authorization URL, the PKCE code verifier, and the state parameter.
836
+ */
837
+ declare function createAuthorizationUrl(config: OAuthConfig, opts?: {
838
+ state: string | null;
839
+ } | null): Promise<{
840
+ url: URL;
841
+ codeVerifier: string;
842
+ state: string;
843
+ }>;
844
+ /**
845
+ * Exchanges an authorization code for tokens using the OAuth token endpoint.
846
+ *
847
+ * @param config - The OAuth configuration.
848
+ * @param opts - The authorization code, PKCE code verifier, and optional redirect URI override.
849
+ * @returns A `Result` containing `TokenResponse` on success or `OAuthError` on
850
+ * failure. A `TOKEN_EXCHANGE_FAILED` error carries the authorization server's
851
+ * own `oauthError` body when it sent a readable one.
852
+ */
853
+ declare function exchangeCodeForToken(config: OAuthConfig, opts: {
854
+ code: string;
855
+ codeVerifier: string;
856
+ redirectUri: string | null;
857
+ }): Promise<Result<TokenResponse, OAuthError>>;
858
+ /**
859
+ * Refreshes an access token using a refresh token.
860
+ *
861
+ * @param config - The OAuth configuration.
862
+ * @param refreshToken - The refresh token to use.
863
+ * @returns A `Result` containing `TokenResponse` on success or `OAuthError` on
864
+ * failure. A `TOKEN_REFRESH_FAILED` error carries the authorization server's
865
+ * own `oauthError` body when it sent a readable one — `invalid_grant` there
866
+ * means the stored refresh token is retired and the creator must reconnect.
867
+ */
868
+ declare function refreshAccessToken(config: OAuthConfig, refreshToken: string): Promise<Result<TokenResponse, OAuthError>>;
869
+ //#endregion
870
+ //#region src/core/session.d.ts
871
+ /**
872
+ * Creates a signed JWT containing the given session payload.
873
+ *
874
+ * @param secret - The secret key used to sign the JWT.
875
+ * @param payload - The session data to embed in the token.
876
+ * @param expiresIn - How long until the token expires (e.g., `"30d"`), or `null` for the default (`"30d"`).
877
+ * @returns The signed JWT string.
878
+ */
879
+ declare function createSessionJwt(secret: string, payload: SessionPayload, expiresIn?: string | null): Promise<string>;
880
+ /**
881
+ * Verifies a session JWT and returns the validated session payload.
882
+ *
883
+ * @param secret - The secret key used to verify the JWT signature.
884
+ * @param token - The JWT string to verify.
885
+ * @returns A {@link Result} containing the validated {@link SessionPayload}, or a {@link SessionVerifyError}.
886
+ */
887
+ declare function verifySessionJwt(secret: string, token: string): Promise<Result<SessionPayload, SessionVerifyError>>;
888
+ //#endregion
889
+ //#region src/core/embedded.d.ts
890
+ /**
891
+ * The creator's active colour scheme, as resolved by Fanvue.
892
+ *
893
+ * Fanvue resolves a creator's "system" preference to the scheme actually being
894
+ * rendered, so this is always a definite value — never `'system'`.
895
+ */
896
+ type FanvueTheme = 'light' | 'dark';
897
+ /**
898
+ * Extracts the embedded session token from a URL.
899
+ *
900
+ * When Fanvue opens an embedded app, it loads the app's embed URL with a
901
+ * short-lived session token appended as a `?token=` query parameter.
902
+ *
903
+ * @param url - The URL to read the token from (e.g. `window.location.href`).
904
+ * @returns The session token, or `null` when absent or the URL is malformed.
905
+ */
906
+ declare function getSessionTokenFromUrl(url: string | URL): string | null;
907
+ /**
908
+ * Extracts the creator's active colour scheme from a URL.
909
+ *
910
+ * When Fanvue opens an embedded app, it appends the creator's resolved colour
911
+ * scheme as a `?theme=` query parameter (`light` or `dark`), letting the app
912
+ * theme-match Fanvue.
913
+ *
914
+ * @param url - The URL to read the theme from (e.g. `window.location.href`).
915
+ * @returns The {@link FanvueTheme}, or `null` when absent, unrecognised, or the
916
+ * URL is malformed (e.g. the app was opened outside Fanvue).
917
+ */
918
+ declare function getThemeFromUrl(url: string | URL): FanvueTheme | null;
919
+ /**
920
+ * Requests an authorization code from the Fanvue platform on behalf of the
921
+ * creator currently using the embedded app.
922
+ *
923
+ * This is the "authorize" half of the delegated flow: the platform verifies
924
+ * the session token, runs the OAuth authorize as the creator, and returns the
925
+ * resulting authorization code. The PKCE verifier and client secret never
926
+ * leave the caller — only the challenge is sent.
927
+ *
928
+ * Most apps should use {@link exchangeSessionToken}, which composes this with
929
+ * the code exchange.
930
+ *
931
+ * @param config - The embedded auth configuration.
932
+ * @param sessionToken - The short-lived session token received in the iframe.
933
+ * @param opts - The PKCE code challenge and state to bind the code to.
934
+ * @returns A `Result` containing the authorization code on success or
935
+ * {@link EmbeddedAuthError} on failure.
936
+ */
937
+ declare function requestAuthorizationCodeOnBehalf(config: EmbeddedAuthConfig, sessionToken: string, opts: {
938
+ codeChallenge: string;
939
+ state: string;
940
+ }): Promise<Result<{
941
+ code: string;
942
+ }, EmbeddedAuthError>>;
943
+ /**
944
+ * Exchanges an embedded session token for OAuth access and refresh tokens.
945
+ *
946
+ * Runs the complete delegated authorize-on-behalf flow:
947
+ *
948
+ * 1. Generates a PKCE verifier/challenge pair and a random `state`.
949
+ * 2. Asks the Fanvue platform to authorize as the creator
950
+ * ({@link requestAuthorizationCodeOnBehalf}).
951
+ * 3. Exchanges the returned code at the token endpoint using the client
952
+ * secret and PKCE verifier.
953
+ *
954
+ * Must run server-side: it uses the client secret. The session token should
955
+ * be used promptly — it expires after ~60 seconds.
956
+ *
957
+ * @param config - The embedded auth configuration. `redirectUri` must be a
958
+ * redirect URI registered on the OAuth client (it is never visited; it only
959
+ * binds the authorization code).
960
+ * @param sessionToken - The short-lived session token received in the iframe.
961
+ * @returns A `Result` containing {@link TokenResponse} on success or an
962
+ * {@link EmbeddedAuthError} / {@link OAuthError} on failure.
963
+ */
964
+ declare function exchangeSessionToken(config: EmbeddedAuthConfig, sessionToken: string): Promise<Result<TokenResponse, EmbeddedAuthError | OAuthError>>;
965
+ //#endregion
966
+ //#region src/core/json.d.ts
967
+ /**
968
+ * Safely parses a JSON string, returning a `Result` instead of throwing.
969
+ *
970
+ * @param text - The raw text to parse as JSON.
971
+ * @returns `ok(parsed)` on success, or `err({ code, rawText, message })` on failure.
972
+ */
973
+ declare function safeJsonParse(text: string): Result<unknown, JsonParseError>;
974
+ //#endregion
975
+ //#region src/core/schemas.d.ts
976
+ /** Zod schema for the raw token response from the OAuth token endpoint. */
977
+ declare const TokenResponseSchema: ZodObject<{
978
+ access_token: ZodString;
979
+ refresh_token: ZodDefault<ZodOptional<ZodNullable<ZodString>>>;
980
+ expires_in: ZodNumber;
981
+ token_type: ZodString;
982
+ scope: ZodDefault<ZodOptional<ZodNullable<ZodString>>>;
983
+ id_token: ZodDefault<ZodOptional<ZodNullable<ZodString>>>;
984
+ }, $strip>;
985
+ /** Zod schema for a Fanvue user profile. */
986
+ declare const FanvueUserSchema: ZodObject<{
987
+ uuid: ZodString;
988
+ email: ZodString;
989
+ handle: ZodString;
990
+ displayName: ZodString;
991
+ isCreator: ZodBoolean;
992
+ avatarUrl: ZodNullable<ZodString>;
993
+ bannerUrl: ZodNullable<ZodString>;
994
+ createdAt: ZodString;
995
+ updatedAt: ZodNullable<ZodString>;
996
+ }, $strip>;
997
+ /** Zod schema for the authorize-on-behalf response from the Fanvue platform. */
998
+ declare const AuthorizeOnBehalfResponseSchema: ZodObject<{
999
+ code: ZodString;
1000
+ state: ZodString;
1001
+ }, $strip>;
1002
+ /** Zod schema for the session JWT payload. */
1003
+ declare const SessionPayloadSchema: ZodObject<{
1004
+ accessToken: ZodString;
1005
+ refreshToken: ZodNullable<ZodString>;
1006
+ expiresAt: ZodNumber;
1007
+ tokenType: ZodNullable<ZodString>;
1008
+ scope: ZodNullable<ZodString>;
1009
+ idToken: ZodNullable<ZodString>;
1010
+ userUuid: ZodString;
1011
+ handle: ZodString;
1012
+ displayName: ZodString;
1013
+ isCreator: ZodBoolean;
1014
+ avatarUrl: ZodNullable<ZodString>;
1015
+ }, $loose>;
1016
+ //#endregion
1017
+ //#region src/core/client.d.ts
1018
+ /**
1019
+ * A client for making authenticated requests to the Fanvue API.
1020
+ */
1021
+ interface FanvueClient {
1022
+ /**
1023
+ * Fetches the currently authenticated user's profile.
1024
+ *
1025
+ * @returns A `Result` containing the {@link FanvueUser} on success or `ApiError` on failure.
1026
+ */
1027
+ getCurrentUser(): Promise<Result<FanvueUser, ApiError>>;
1028
+ }
1029
+ /**
1030
+ * Creates a new {@link FanvueClient} for making authenticated requests to the Fanvue API.
1031
+ *
1032
+ * @param accessToken - The OAuth access token to authenticate requests.
1033
+ * @param apiBaseUrl - The base URL for the Fanvue API, or `null` to use the default.
1034
+ * @returns A {@link FanvueClient} instance.
1035
+ */
1036
+ declare function createFanvueClient(accessToken: string, apiBaseUrl: string | null): FanvueClient;
1037
+ //#endregion
1038
+ //#region src/core/machine-auth/machine-auth.d.ts
1039
+ /**
1040
+ * The outcome of authenticating a machine (cron / queue) request.
1041
+ *
1042
+ * `not_configured` is a separate arm on purpose: a deployment with no usable
1043
+ * credential has an operator problem, not an attacker, and the two must map to
1044
+ * different HTTP statuses (503 vs 401). Collapsing them hides a broken deploy
1045
+ * behind what looks like ordinary auth noise, and tells a caller to retry with
1046
+ * better credentials when no credential could ever work.
1047
+ */
1048
+ type MachineAuthResult = {
1049
+ status: 'authorized';
1050
+ } | {
1051
+ status: 'unauthorized';
1052
+ } | {
1053
+ status: 'not_configured';
1054
+ };
1055
+ /**
1056
+ * A pluggable signed-request strategy — the second way a machine request can
1057
+ * authenticate itself.
1058
+ *
1059
+ * Implemented by the app so the SDK takes no queue-provider dependency. The
1060
+ * verifier owns its own key material and rotation; it must return `false`
1061
+ * (never throw) when it cannot verify, though a throw is caught and treated as
1062
+ * a failure regardless.
1063
+ */
1064
+ interface SignedRequestVerifier {
1065
+ /**
1066
+ * @param request - The incoming request, whose URL and headers are usually
1067
+ * part of the signed material.
1068
+ * @param rawBody - The exact bytes received. Never a reserialized body: a
1069
+ * round-trip through `JSON.parse`/`stringify` changes whitespace and key
1070
+ * order and invalidates the signature.
1071
+ * @returns Whether the signature is authentic.
1072
+ */
1073
+ verify: (request: Request, rawBody: Uint8Array) => Promise<boolean>;
1074
+ }
1075
+ /**
1076
+ * Credentials and strategies available to {@link requireMachineAuth}.
1077
+ */
1078
+ interface MachineAuthOptions {
1079
+ /**
1080
+ * Shared bearer secret (the `CRON_SECRET` pattern). `null` when unset. A
1081
+ * secret shorter than {@link MINIMUM_BEARER_SECRET_LENGTH} is treated as
1082
+ * unset rather than compared.
1083
+ */
1084
+ bearerSecret: string | null;
1085
+ /** Signed-request strategy, or `null` when the app plugs none in. */
1086
+ signedRequestVerifier: SignedRequestVerifier | null;
1087
+ /**
1088
+ * The exact request bytes, required by {@link signedRequestVerifier}. `null`
1089
+ * is allowed (and reads as an empty body) when no verifier is configured.
1090
+ */
1091
+ rawBody: Uint8Array | null;
1092
+ }
1093
+ /**
1094
+ * Shortest bearer secret the SDK will compare against.
1095
+ *
1096
+ * A cron endpoint is reachable by anyone on the internet and its secret is
1097
+ * never rotated by a user, so a guessable one is a standing invitation. Below
1098
+ * this length the secret is treated as absent — the endpoint reports
1099
+ * `not_configured` instead of quietly accepting a weak credential.
1100
+ */
1101
+ declare const MINIMUM_BEARER_SECRET_LENGTH = 32;
1102
+ /**
1103
+ * Authenticates a machine-invoked route (cron tick, queue drain, sweeper).
1104
+ *
1105
+ * Two strategies, tried in order, either of which is sufficient:
1106
+ *
1107
+ * 1. a shared bearer secret compared in constant time, and
1108
+ * 2. an app-supplied {@link SignedRequestVerifier}.
1109
+ *
1110
+ * Deny by default. When neither strategy is usable — no verifier and a missing
1111
+ * or too-short `bearerSecret` — the result is `not_configured`, which the
1112
+ * caller should surface as 503; `unauthorized` (401) means a strategy was
1113
+ * available and the request failed it.
1114
+ *
1115
+ * Web-standard `Request` only, so this works unchanged in a Next.js route
1116
+ * handler, a Node server and an edge function.
1117
+ *
1118
+ * @param request - The incoming machine request.
1119
+ * @param options - Available credentials and strategies.
1120
+ * @returns The discriminated {@link MachineAuthResult}.
1121
+ * @example
1122
+ * // Bearer-only cron route.
1123
+ * const auth = await requireMachineAuth(request, {
1124
+ * bearerSecret: process.env.CRON_SECRET ?? null,
1125
+ * signedRequestVerifier: null,
1126
+ * rawBody: null,
1127
+ * });
1128
+ * if (auth.status === 'not_configured') return new Response(null, { status: 503 });
1129
+ * if (auth.status === 'unauthorized') return new Response(null, { status: 401 });
1130
+ * @example
1131
+ * // Queue drain that also accepts QStash-signed deliveries. `@upstash/qstash`
1132
+ * // is intentionally NOT a dependency of this package — the app owns it:
1133
+ * //
1134
+ * // import { Receiver } from '@upstash/qstash';
1135
+ * //
1136
+ * // const qstashVerifier: SignedRequestVerifier = {
1137
+ * // verify: async (request, rawBody) => {
1138
+ * // const signature = request.headers.get('upstash-signature');
1139
+ * // const currentSigningKey = process.env.QSTASH_CURRENT_SIGNING_KEY;
1140
+ * // const nextSigningKey = process.env.QSTASH_NEXT_SIGNING_KEY;
1141
+ * // if (!signature || !currentSigningKey || !nextSigningKey) return false;
1142
+ * // return new Receiver({ currentSigningKey, nextSigningKey }).verify({
1143
+ * // signature,
1144
+ * // body: new TextDecoder().decode(rawBody),
1145
+ * // url: request.url,
1146
+ * // upstashRegion: request.headers.get('upstash-region') ?? undefined,
1147
+ * // });
1148
+ * // },
1149
+ * // };
1150
+ * //
1151
+ * // Pass the raw bytes, never a reparsed body:
1152
+ * const rawBody = new Uint8Array(await request.arrayBuffer());
1153
+ * const auth = await requireMachineAuth(request, {
1154
+ * bearerSecret: process.env.CRON_SECRET ?? null,
1155
+ * signedRequestVerifier: qstashVerifier,
1156
+ * rawBody,
1157
+ * });
1158
+ */
1159
+ declare function requireMachineAuth(request: Request, options: MachineAuthOptions): Promise<MachineAuthResult>;
1160
+ /**
1161
+ * Clamps a caller-supplied batch size to a safe positive bound.
1162
+ *
1163
+ * Machine routes take `?limit=` to let an operator drain faster, which is also
1164
+ * an unauthenticated-shaped lever on database load, so the value is bounded
1165
+ * rather than trusted. Anything unparseable, non-integer, zero or negative
1166
+ * falls back to the default; the default itself is capped by `max`, so a
1167
+ * misconfigured default cannot exceed the ceiling either.
1168
+ *
1169
+ * The result is always a positive safe integer, even when `defaultSize` or
1170
+ * `max` is itself invalid (non-integer, zero or negative): a query builder
1171
+ * handed a negative limit can interpret it as "no limit" — the exact fail-open
1172
+ * this function exists to prevent — so a misconfigured constant degrades to a
1173
+ * batch size of 1, never to an unbounded one.
1174
+ *
1175
+ * @param requested - Raw query value, e.g. `searchParams.get('limit')`.
1176
+ * @param defaultSize - Size to use when `requested` is absent or invalid.
1177
+ * @param max - Hard ceiling.
1178
+ * @returns A positive safe integer batch size.
1179
+ * @example
1180
+ * boundedBatchSize(null, 20, 100); // 20
1181
+ * boundedBatchSize('0', 20, 100); // 20
1182
+ * boundedBatchSize('1000', 20, 100); // 100
1183
+ */
1184
+ declare function boundedBatchSize(requested: string | null, defaultSize: number, max: number): number;
1185
+ //#endregion
1186
+ //#region src/core/observability/log-fields.d.ts
1187
+ /**
1188
+ * The log levels a structured event can be emitted at.
1189
+ */
1190
+ type LogLevel = 'info' | 'warn' | 'error';
1191
+ /**
1192
+ * A scalar that is safe to place in a log line.
1193
+ *
1194
+ * Objects and arrays are deliberately absent: an allowlisted key whose value is
1195
+ * a structure would smuggle un-reviewed fields (an Error, a token bag, a whole
1196
+ * request) past the allowlist, so those values are dropped rather than
1197
+ * serialised.
1198
+ */
1199
+ type SafeLogValue = string | number | boolean | null;
1200
+ /**
1201
+ * Filters an arbitrary field bag down to the fields that are safe to log.
1202
+ *
1203
+ * Created by {@link createSafeLogFields}.
1204
+ */
1205
+ type SafeLogFields = (fields?: Record<string, unknown>) => Record<string, SafeLogValue>;
1206
+ /**
1207
+ * Emits one structured, allowlisted log event.
1208
+ *
1209
+ * Created by {@link createLogEvent}.
1210
+ */
1211
+ type LogEvent = (level: LogLevel, event: string, fields?: Record<string, unknown>) => void;
1212
+ /**
1213
+ * The subset of `console` a {@link LogEvent} writes to. Injectable so the
1214
+ * privacy behaviour can be asserted in tests.
1215
+ */
1216
+ interface LogSink {
1217
+ info: (...args: readonly unknown[]) => void;
1218
+ warn: (...args: readonly unknown[]) => void;
1219
+ error: (...args: readonly unknown[]) => void;
1220
+ }
1221
+ /**
1222
+ * Options for {@link createLogEvent}.
1223
+ */
1224
+ interface LogEventOptions {
1225
+ /** Prefix written before the payload, e.g. `'[my-app]'`. */
1226
+ prefix: string;
1227
+ /** Allowlist to apply. Defaults to `createSafeLogFields()`. */
1228
+ safeLogFields: SafeLogFields;
1229
+ /** Where lines are written. Defaults to the global `console`. */
1230
+ sink: LogSink;
1231
+ }
1232
+ /**
1233
+ * The generic Fanvue log keys every app shares.
1234
+ *
1235
+ * Anything app-specific (a wheel id, a bounty id, a claim status) is added per
1236
+ * app via `createSafeLogFields(['wheelId', ...])`.
1237
+ */
1238
+ declare const BASE_ALLOWED_LOG_KEYS: readonly string[];
1239
+ /**
1240
+ * Builds an allowlisting log-field filter.
1241
+ *
1242
+ * The returned function is the only sanctioned way to get caller-supplied
1243
+ * fields into a log line. It fails closed in three independent ways:
1244
+ *
1245
+ * 1. A key that is not allowlisted is dropped entirely (so a new field cannot
1246
+ * reach the logs until someone adds it to the list on purpose).
1247
+ * 2. A value that is not `string | number | boolean | null` is dropped — an
1248
+ * `Error`, a response body or a token bag is never serialised, even under an
1249
+ * allowlisted key.
1250
+ * 3. An allowlisted string value containing a UUID becomes `'[redacted-uuid]'`
1251
+ * (platform UUIDs identify real people; any version matches, including v7 —
1252
+ * see `UUID_PATTERN`), and one containing an email address becomes
1253
+ * `'[redacted-email]'`. The whole value is replaced, not just the match,
1254
+ * because the surrounding text is usually the same identifier restated.
1255
+ *
1256
+ * `undefined` values are dropped rather than logged as `undefined`.
1257
+ *
1258
+ * @param extraAllowedKeys - App-specific keys to allow in addition to
1259
+ * {@link BASE_ALLOWED_LOG_KEYS}.
1260
+ * @returns A `safeLogFields(fields)` filter.
1261
+ * @example
1262
+ * const safeLogFields = createSafeLogFields(['wheelId']);
1263
+ * safeLogFields({ wheelId: 'wheel_1', retryCount: 2, accessToken: 'secret' });
1264
+ * // => { wheelId: 'wheel_1', retryCount: 2 }
1265
+ */
1266
+ declare function createSafeLogFields(extraAllowedKeys?: readonly string[]): SafeLogFields;
1267
+ /**
1268
+ * Reads a loggable name out of an unknown thrown value.
1269
+ *
1270
+ * Only the class name is returned — never `error.message`, which routinely
1271
+ * carries the very data the allowlist exists to keep out of the logs (upstream
1272
+ * response bodies, signed URLs, secrets embedded in connection strings).
1273
+ *
1274
+ * @param error - Any caught value.
1275
+ * @returns The `Error` subclass name, or the `typeof` of a non-Error throw.
1276
+ * @example
1277
+ * errorName(new TypeError('secret=abc')); // 'TypeError'
1278
+ * errorName('boom'); // 'string'
1279
+ */
1280
+ declare function errorName(error: unknown): string;
1281
+ /**
1282
+ * Builds a structured event logger over an allowlist.
1283
+ *
1284
+ * The emitted shape is `prefix` followed by `{ event, ...safeFields }`, matching
1285
+ * the convention the apps already log in, so existing log-based dashboards keep
1286
+ * working after the lift.
1287
+ *
1288
+ * @param options - Prefix, allowlist and sink overrides. Every field is
1289
+ * optional; the defaults are `'[fanvue]'`, `createSafeLogFields()` and the
1290
+ * global `console`.
1291
+ * @returns A `logEvent(level, event, fields)` function.
1292
+ * @example
1293
+ * const logEvent = createLogEvent({ prefix: '[spinwheel]' });
1294
+ * logEvent('warn', 'exchange.retry', { httpStatus: 503, retryCount: 1 });
1295
+ * // [spinwheel] { event: 'exchange.retry', httpStatus: 503, retryCount: 1 }
1296
+ */
1297
+ declare function createLogEvent(options?: Partial<LogEventOptions>): LogEvent;
1298
+ /**
1299
+ * A ready-made {@link LogEvent} over the base allowlist, prefixed `[fanvue]`.
1300
+ *
1301
+ * Use {@link createLogEvent} instead when the app has its own prefix or extra
1302
+ * allowlisted keys.
1303
+ *
1304
+ * @example
1305
+ * logEvent('error', 'session.exchange_failed', { errorName: errorName(err) });
1306
+ */
1307
+ declare const logEvent: LogEvent;
1308
+ //#endregion
1309
+ //#region src/core/observability/readiness.d.ts
1310
+ /**
1311
+ * One named configuration check.
1312
+ */
1313
+ interface ReadinessCheck {
1314
+ /** Stable machine-readable check name, e.g. `'fanvue_oauth'`. */
1315
+ name: string;
1316
+ /** Whether the check passes. */
1317
+ ready: boolean;
1318
+ /** Why it fails, or `null` when it passes. Never contains a secret value. */
1319
+ detail: string | null;
1320
+ }
1321
+ /** The environment shape the checks read, so `process.env` is never read directly. */
1322
+ type ReadinessEnv = Readonly<Record<string, string | undefined>>;
1323
+ /**
1324
+ * Reports whether the generic Fanvue configuration an embedded app depends on
1325
+ * is present, as a list of named checks suitable for a `/api/readyz` body.
1326
+ *
1327
+ * Pure by construction — the environment is a parameter, so a deployment's
1328
+ * readiness can be asserted in tests without mutating `process.env`. Two
1329
+ * checks:
1330
+ *
1331
+ * - `app_url` — the app's public base URL is set and https. Fanvue refuses to
1332
+ * embed a non-https origin, so an http (or unset) value means creators would
1333
+ * see an empty frame rather than an error.
1334
+ * - `fanvue_oauth` — all four OAuth credential variables are present. The
1335
+ * detail names the missing variables, never their values.
1336
+ *
1337
+ * Provider-specific checks are deliberately out of scope: webhook signature
1338
+ * readiness belongs to the webhooks module (which owns the contract), and
1339
+ * queue/cron credentials belong to whichever machine-auth strategy the app
1340
+ * plugs in.
1341
+ *
1342
+ * @param env - Environment to read. Defaults to `process.env`; on a runtime
1343
+ * with no `process` global (browser, some edge workers) it defaults to an
1344
+ * empty environment, so the probe reports not-ready instead of throwing a
1345
+ * `ReferenceError`.
1346
+ * @returns One {@link ReadinessCheck} per generic concern, in a stable order.
1347
+ * @example
1348
+ * const checks = configurationReadiness();
1349
+ * const ready = checks.every((check) => check.ready);
1350
+ * return Response.json({ ready, checks }, { status: ready ? 200 : 503 });
1351
+ */
1352
+ declare function configurationReadiness(env?: ReadinessEnv): readonly ReadinessCheck[];
1353
+ //#endregion
1354
+ //#region src/core/observability/sentry-scrubber.d.ts
1355
+ /**
1356
+ * A `beforeSend`-compatible scrubber.
1357
+ *
1358
+ * Generic in the event type so it satisfies Sentry's `beforeSend` option
1359
+ * without this package importing `@sentry/*` — the returned function is
1360
+ * structurally assignable to `(event, hint) => event | null`.
1361
+ */
1362
+ type SentryScrubber = <T>(event: T) => T;
1363
+ /**
1364
+ * Key-name fragments whose values are never allowed to reach an error tracker.
1365
+ *
1366
+ * Matched as a substring, case-insensitively, so `Authorization`,
1367
+ * `authorizationHeader`, `x-authorization`, `x-api-key` and `apiKey` all hit.
1368
+ * Substring matching over-redacts on purpose (`emailSent`, `iphoneModel`) —
1369
+ * losing a benign field fails closed, letting a credential or a fan's contact
1370
+ * detail through does not. Anything app-specific (`prizedetail`, `fanhmac`,
1371
+ * `seed`, …) is added per app via the `extraSensitiveKeyPattern` argument.
1372
+ */
1373
+ declare const BASE_SENSITIVE_KEY_PATTERN: RegExp;
1374
+ /**
1375
+ * Builds a recursive `beforeSend` scrubber for Sentry (or any error tracker
1376
+ * with the same hook shape).
1377
+ *
1378
+ * Four redaction rules, applied to every node of the event:
1379
+ *
1380
+ * - a key matching {@link BASE_SENSITIVE_KEY_PATTERN} (or the app's extra
1381
+ * pattern) has its value replaced with `'[redacted]'`, whatever its type;
1382
+ * - every UUID inside a string becomes `'[redacted-uuid]'` — any version,
1383
+ * including v7;
1384
+ * - a string that parses as a URL keeps its origin and path but loses its query
1385
+ * string, fragment and userinfo;
1386
+ * - every email address inside a string becomes `'[redacted-email]'`.
1387
+ *
1388
+ * Bounded and total by construction, because a `beforeSend` that throws loses
1389
+ * the event and raises inside the caller: repeat visits yield `'[circular]'`
1390
+ * (`WeakSet`), nodes past a depth of 32 yield `'[max-depth]'`, and a property
1391
+ * whose getter throws yields `'[unreadable]'`. Each of those fails closed — an
1392
+ * untraversed value cannot leak.
1393
+ *
1394
+ * Deliberately dependency-free: the event is typed structurally, so the privacy
1395
+ * behaviour is unit-testable without a Sentry client and this package takes no
1396
+ * dependency on `@sentry/*`.
1397
+ *
1398
+ * @param extraSensitiveKeyPattern - Extra key fragments to redact, merged with
1399
+ * the base pattern. Its flags are ignored; matching is always
1400
+ * case-insensitive and stateless.
1401
+ * @returns A function suitable for `Sentry.init({ beforeSend })`.
1402
+ * @example
1403
+ * Sentry.init({
1404
+ * dsn,
1405
+ * enabled: Boolean(dsn),
1406
+ * beforeSend: createSentryScrubber(/(prizedetail|fanhmac|seed)/i),
1407
+ * });
1408
+ */
1409
+ declare function createSentryScrubber(extraSensitiveKeyPattern?: RegExp | null): SentryScrubber;
1410
+ //#endregion
1411
+ export { DEFAULT_ISSUER_URL as $, resetFanvueEnvCache as $t, safeJsonParse as A, isUnpublishResultMessage as At, refreshAccessToken as B, FanvueStringListErrorBodySchema as Bt, requireMachineAuth as C, UNPUBLISH_RESULT_MESSAGE as Ct, FanvueUserSchema as D, UnpublishResultMessageSchema as Dt, AuthorizeOnBehalfResponseSchema as E, UnpublishResultMessage as Et, requestAuthorizationCodeOnBehalf as F, FanvueErrorBody as Ft, JsonParseError as G, parseFanvueErrorBody as Gt, EmbeddedAuthConfig as H, NormalisedFanvueError as Ht, createSessionJwt as I, FanvueErrorBodySchema as It, SessionPayload as J, FanvueEnvSchema as Jt, OAuthConfig as K, EnvSource as Kt, verifySessionJwt as L, FanvueErrorFieldBodySchema as Lt, exchangeSessionToken as M, AppErrorEnvelopeSchema as Mt, getSessionTokenFromUrl as N, FANVUE_APP_ERROR_CODES as Nt, SessionPayloadSchema as O, isFanvueOrigin as Ot, getThemeFromUrl as P, FanvueAppErrorCode as Pt, DEFAULT_API_BASE_URL as Q, parseFanvueEnv as Qt, createAuthorizationUrl as R, FanvueIssueListErrorBodySchema as Rt, boundedBatchSize as S, UNPUBLISH_REQUEST_MESSAGE as St, createFanvueClient as T, UnpublishRequestMessageSchema as Tt, EmbeddedAuthError as U, OAuthErrorBody as Ut, ApiError as V, NON_SESSION_401_CODES as Vt, FanvueUser as W, OAuthErrorBodySchema as Wt, TokenResponse as X, flagEnabled as Xt, SessionVerifyError as Y, fanvueEnv as Yt, API_VERSION as Z, isFanvueConfigured as Zt, logEvent as _, PUBLISH_RESULT_MESSAGE as _t, ReadinessEnv as a, FanvueAccessMode as an, HybridPaginationSchema as at, MachineAuthResult as b, PublishResultMessage as bt, LogEvent as c, BEARER_PREFIX as cn, OffsetPaginationSchema as ct, LogSink as d, offsetPageSchema as dt, EXPERIENCE_DENIAL_REASONS as en, DEFAULT_PLATFORM_URL as et, SafeLogFields as f, EXPERIENCE_MESSAGE_TYPES as ft, errorName as g, PUBLISH_REQUEST_MESSAGE as gt, createSafeLogFields as h, ExperienceMessageType as ht, ReadinessCheck as i, FANVUE_ACCESS_MODES as in, HybridPagination as it, FanvueTheme as j, AppErrorEnvelope as jt, TokenResponseSchema as k, isPublishResultMessage as kt, LogEventOptions as l, HEADER_UPDATED_SESSION as ln, clampPageSize as lt, createLogEvent as m, ExperienceMessageSchema as mt, SentryScrubber as n, ExperienceDenialReason as nn, assertFanvueDomain as nt, configurationReadiness as o, FanvueAccessModeSchema as on, MAX_PAGE_SIZE as ot, SafeLogValue as p, ExperienceMessage as pt, OAuthError as q, FanvueEnv as qt, createSentryScrubber as r, ExperienceEntitledReason as rn, DEFAULT_PAGE_SIZE as rt, BASE_ALLOWED_LOG_KEYS as s, accessModeFromDenialReason as sn, OffsetPagination as st, BASE_SENSITIVE_KEY_PATTERN as t, EXPERIENCE_ENTITLED_REASONS as tn, DEFAULT_SCOPES as tt, LogLevel as u, cursorPageSchema as ut, MINIMUM_BEARER_SECRET_LENGTH as v, PublishRequestMessage as vt, FanvueClient as w, UnpublishRequestMessage as wt, SignedRequestVerifier as x, PublishResultMessageSchema as xt, MachineAuthOptions as y, PublishRequestMessageSchema as yt, exchangeCodeForToken as z, FanvueMessageErrorBodySchema as zt };
1412
+ //# sourceMappingURL=index-C3CXrRiw.d.ts.map