@feelflow/ffid-sdk 4.3.0 → 5.0.1
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/README.md +33 -0
- package/dist/FFIDCookieLink-VYI2cSAB.d.cts +878 -0
- package/dist/FFIDCookieLink-VYI2cSAB.d.ts +878 -0
- package/dist/{chunk-5MO7G2JW.cjs → chunk-223MPEBU.cjs} +1 -1
- package/dist/chunk-IIC2LBD2.cjs +1836 -0
- package/dist/chunk-VSHBWZXK.js +1776 -0
- package/dist/{chunk-35UOD62N.js → chunk-YOJJ433H.js} +1 -1
- package/dist/components/index.cjs +8 -8
- package/dist/components/index.js +1 -1
- package/dist/consent/index.cjs +242 -0
- package/dist/consent/index.d.cts +166 -0
- package/dist/consent/index.d.ts +166 -0
- package/dist/consent/index.js +1 -0
- package/dist/index.cjs +97 -32
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -2
- package/dist/server/index.cjs +99 -1
- package/dist/server/index.d.cts +149 -1
- package/dist/server/index.d.ts +149 -1
- package/dist/server/index.js +93 -2
- package/package.json +8 -2
|
@@ -0,0 +1,878 @@
|
|
|
1
|
+
import * as React$1 from 'react';
|
|
2
|
+
import { ReactNode, CSSProperties } from 'react';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Cookie Consent — Zod schemas (canonical source of truth).
|
|
7
|
+
*
|
|
8
|
+
* Two layers of schemas live in this file:
|
|
9
|
+
*
|
|
10
|
+
* 1. **SDK consumer-facing schemas** (camelCase, discriminated union). These
|
|
11
|
+
* are what `useFFIDConsent()` / `useFFIDConsentPreferences()` surface to
|
|
12
|
+
* React code, and what spec §6.4 documents as the SDK public contract.
|
|
13
|
+
*
|
|
14
|
+
* 2. **Wire schemas** (`*WireSchema`, snake_case envelopes). These mirror the
|
|
15
|
+
* actual FFID API responses from `src/app/api/v1/ext/consent/*`, which
|
|
16
|
+
* follow the FFID server-side convention of snake_case body fields plus
|
|
17
|
+
* nullable `consent` envelopes. `consent-client.ts` parses responses with
|
|
18
|
+
* these and then maps to the consumer-facing types via `mappers.ts`.
|
|
19
|
+
*
|
|
20
|
+
* TypeScript types are derived in `types.ts` via `z.infer<>`.
|
|
21
|
+
* `as` casting is forbidden; cross-trust-boundary data must be safe-parsed.
|
|
22
|
+
*
|
|
23
|
+
* Refs: docs/02-design/COOKIE_CONSENT.md §6.4 + §5
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
declare const FFIDConsentCategoryCodeSchema: z.ZodEnum<{
|
|
27
|
+
necessary: "necessary";
|
|
28
|
+
functional: "functional";
|
|
29
|
+
analytics: "analytics";
|
|
30
|
+
marketing: "marketing";
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Consent state per category. `necessary` is enforced to be `true` at both
|
|
34
|
+
* type and runtime levels (`z.literal(true)`). The DB CHECK constraint
|
|
35
|
+
* `ucc_necessary_always_true` mirrors this on the storage side.
|
|
36
|
+
*/
|
|
37
|
+
declare const FFIDConsentCategoriesSchema: z.ZodObject<{
|
|
38
|
+
necessary: z.ZodLiteral<true>;
|
|
39
|
+
functional: z.ZodBoolean;
|
|
40
|
+
analytics: z.ZodBoolean;
|
|
41
|
+
marketing: z.ZodBoolean;
|
|
42
|
+
}, z.core.$strip>;
|
|
43
|
+
/**
|
|
44
|
+
* Public sources exposed to SDK consumers. UI logic should only branch on these.
|
|
45
|
+
*/
|
|
46
|
+
declare const FFIDPublicConsentSourceSchema: z.ZodEnum<{
|
|
47
|
+
banner: "banner";
|
|
48
|
+
preference_center: "preference_center";
|
|
49
|
+
}>;
|
|
50
|
+
/**
|
|
51
|
+
* Internal sources used by SDK / API plumbing. Consumers should treat these
|
|
52
|
+
* as opaque (no UI branching).
|
|
53
|
+
*
|
|
54
|
+
* - `sdk_default`: SDK default state before any decision has been recorded
|
|
55
|
+
* (paired with `hasDecided: false`).
|
|
56
|
+
* - `api_withdraw`: row created by `POST /withdraw` server-side.
|
|
57
|
+
* - `sync_merge`: row created by `/sync` merge logic server-side.
|
|
58
|
+
* - `persisted_unknown`: decision is on file (`hasDecided: true`) but
|
|
59
|
+
* `/me` did not surface its original source. The decision is authoritative;
|
|
60
|
+
* the source label is just unavailable.
|
|
61
|
+
*/
|
|
62
|
+
declare const FFIDInternalConsentSourceSchema: z.ZodEnum<{
|
|
63
|
+
sdk_default: "sdk_default";
|
|
64
|
+
api_withdraw: "api_withdraw";
|
|
65
|
+
sync_merge: "sync_merge";
|
|
66
|
+
persisted_unknown: "persisted_unknown";
|
|
67
|
+
}>;
|
|
68
|
+
declare const FFIDConsentSourceSchema: z.ZodUnion<readonly [z.ZodEnum<{
|
|
69
|
+
banner: "banner";
|
|
70
|
+
preference_center: "preference_center";
|
|
71
|
+
}>, z.ZodEnum<{
|
|
72
|
+
sdk_default: "sdk_default";
|
|
73
|
+
api_withdraw: "api_withdraw";
|
|
74
|
+
sync_merge: "sync_merge";
|
|
75
|
+
persisted_unknown: "persisted_unknown";
|
|
76
|
+
}>]>;
|
|
77
|
+
/**
|
|
78
|
+
* Merge strategy returned by `POST /api/v1/ext/consent/sync`.
|
|
79
|
+
* Closed enum — `safeParse` will fail on unknown values so a server-side
|
|
80
|
+
* regression that introduces a new strategy is caught at parse time rather
|
|
81
|
+
* than silently passing through.
|
|
82
|
+
*/
|
|
83
|
+
declare const FFIDConsentMergeStrategySchema: z.ZodEnum<{
|
|
84
|
+
db_wins: "db_wins";
|
|
85
|
+
device_promoted: "device_promoted";
|
|
86
|
+
device_promoted_with_drift: "device_promoted_with_drift";
|
|
87
|
+
cross_user_insert: "cross_user_insert";
|
|
88
|
+
first_insert: "first_insert";
|
|
89
|
+
}>;
|
|
90
|
+
/**
|
|
91
|
+
* Warning literal accompanying merge strategy. Per spec §5.3 the SDK is
|
|
92
|
+
* required to surface non-null warnings to consumers (silent merge forbidden).
|
|
93
|
+
*/
|
|
94
|
+
declare const FFIDConsentMergeWarningSchema: z.ZodEnum<{
|
|
95
|
+
shared_device_detected: "shared_device_detected";
|
|
96
|
+
local_state_overridden: "local_state_overridden";
|
|
97
|
+
drift_detected: "drift_detected";
|
|
98
|
+
}>;
|
|
99
|
+
/**
|
|
100
|
+
* Discriminated union: `hasDecided` drives null-ness of dependent fields.
|
|
101
|
+
* Allows TypeScript narrowing in consumer code:
|
|
102
|
+
* if (state.hasDecided) { state.decidedAt /* Date */ }
|
|
103
|
+
*/
|
|
104
|
+
declare const FFIDConsentStateSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
105
|
+
hasDecided: z.ZodLiteral<false>;
|
|
106
|
+
needsRenewal: z.ZodBoolean;
|
|
107
|
+
categories: z.ZodObject<{
|
|
108
|
+
necessary: z.ZodLiteral<true>;
|
|
109
|
+
functional: z.ZodBoolean;
|
|
110
|
+
analytics: z.ZodBoolean;
|
|
111
|
+
marketing: z.ZodBoolean;
|
|
112
|
+
}, z.core.$strip>;
|
|
113
|
+
decidedAt: z.ZodNull;
|
|
114
|
+
cookiePolicyVersion: z.ZodNull;
|
|
115
|
+
source: z.ZodNull;
|
|
116
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
117
|
+
hasDecided: z.ZodLiteral<true>;
|
|
118
|
+
needsRenewal: z.ZodBoolean;
|
|
119
|
+
categories: z.ZodObject<{
|
|
120
|
+
necessary: z.ZodLiteral<true>;
|
|
121
|
+
functional: z.ZodBoolean;
|
|
122
|
+
analytics: z.ZodBoolean;
|
|
123
|
+
marketing: z.ZodBoolean;
|
|
124
|
+
}, z.core.$strip>;
|
|
125
|
+
decidedAt: z.ZodCoercedDate<unknown>;
|
|
126
|
+
cookiePolicyVersion: z.ZodString;
|
|
127
|
+
source: z.ZodUnion<readonly [z.ZodEnum<{
|
|
128
|
+
banner: "banner";
|
|
129
|
+
preference_center: "preference_center";
|
|
130
|
+
}>, z.ZodEnum<{
|
|
131
|
+
sdk_default: "sdk_default";
|
|
132
|
+
api_withdraw: "api_withdraw";
|
|
133
|
+
sync_merge: "sync_merge";
|
|
134
|
+
persisted_unknown: "persisted_unknown";
|
|
135
|
+
}>]>;
|
|
136
|
+
}, z.core.$strip>], "hasDecided">;
|
|
137
|
+
/**
|
|
138
|
+
* Setter shape — `necessary` is intentionally not toggleable.
|
|
139
|
+
*/
|
|
140
|
+
declare const FFIDConsentUpdateSchema: z.ZodObject<{
|
|
141
|
+
functional: z.ZodOptional<z.ZodBoolean>;
|
|
142
|
+
analytics: z.ZodOptional<z.ZodBoolean>;
|
|
143
|
+
marketing: z.ZodOptional<z.ZodBoolean>;
|
|
144
|
+
}, z.core.$strip>;
|
|
145
|
+
/**
|
|
146
|
+
* Category metadata for i18n-aware preference center rendering.
|
|
147
|
+
* Mirrors the camelCase shape `getCategoriesMaster()` returns in the FFID
|
|
148
|
+
* repository (snake_case DB columns are already converted server-side).
|
|
149
|
+
*/
|
|
150
|
+
declare const FFIDConsentCategoryMetadataSchema: z.ZodObject<{
|
|
151
|
+
code: z.ZodEnum<{
|
|
152
|
+
necessary: "necessary";
|
|
153
|
+
functional: "functional";
|
|
154
|
+
analytics: "analytics";
|
|
155
|
+
marketing: "marketing";
|
|
156
|
+
}>;
|
|
157
|
+
displayOrder: z.ZodNumber;
|
|
158
|
+
isRequired: z.ZodBoolean;
|
|
159
|
+
label: z.ZodString;
|
|
160
|
+
description: z.ZodString;
|
|
161
|
+
exampleServices: z.ZodArray<z.ZodString>;
|
|
162
|
+
}, z.core.$strip>;
|
|
163
|
+
/**
|
|
164
|
+
* Cookie policy document — camelCase shape from `getCurrentCookiePolicy()`.
|
|
165
|
+
*/
|
|
166
|
+
declare const FFIDCookiePolicySchema: z.ZodObject<{
|
|
167
|
+
id: z.ZodString;
|
|
168
|
+
version: z.ZodString;
|
|
169
|
+
title: z.ZodString;
|
|
170
|
+
summary: z.ZodString;
|
|
171
|
+
content: z.ZodString;
|
|
172
|
+
effectiveDate: z.ZodISODate;
|
|
173
|
+
publishedAt: z.ZodISODateTime;
|
|
174
|
+
}, z.core.$strip>;
|
|
175
|
+
/**
|
|
176
|
+
* Body for `POST /api/v1/ext/consent` (banner / preference center submit).
|
|
177
|
+
* `necessary` is intentionally NOT accepted from clients — server enforces true.
|
|
178
|
+
*/
|
|
179
|
+
declare const PostConsentRequestSchema: z.ZodObject<{
|
|
180
|
+
categories: z.ZodObject<{
|
|
181
|
+
functional: z.ZodBoolean;
|
|
182
|
+
analytics: z.ZodBoolean;
|
|
183
|
+
marketing: z.ZodBoolean;
|
|
184
|
+
}, z.core.$strict>;
|
|
185
|
+
source: z.ZodEnum<{
|
|
186
|
+
banner: "banner";
|
|
187
|
+
preference_center: "preference_center";
|
|
188
|
+
}>;
|
|
189
|
+
cookie_policy_version: z.ZodString;
|
|
190
|
+
}, z.core.$strip>;
|
|
191
|
+
/**
|
|
192
|
+
* Body for `POST /api/v1/ext/consent/sync`.
|
|
193
|
+
* `local_consent` is optional — null indicates the SDK has no local state.
|
|
194
|
+
*/
|
|
195
|
+
declare const PostSyncRequestSchema: z.ZodObject<{
|
|
196
|
+
local_consent: z.ZodNullable<z.ZodObject<{
|
|
197
|
+
necessary: z.ZodLiteral<true>;
|
|
198
|
+
functional: z.ZodBoolean;
|
|
199
|
+
analytics: z.ZodBoolean;
|
|
200
|
+
marketing: z.ZodBoolean;
|
|
201
|
+
cookie_policy_version: z.ZodString;
|
|
202
|
+
}, z.core.$strip>>;
|
|
203
|
+
}, z.core.$strip>;
|
|
204
|
+
/** `GET /api/v1/ext/consent/me` response */
|
|
205
|
+
declare const GetConsentMeWireSchema: z.ZodObject<{
|
|
206
|
+
consent: z.ZodNullable<z.ZodObject<{
|
|
207
|
+
necessary: z.ZodBoolean;
|
|
208
|
+
functional: z.ZodBoolean;
|
|
209
|
+
analytics: z.ZodBoolean;
|
|
210
|
+
marketing: z.ZodBoolean;
|
|
211
|
+
cookie_policy_version: z.ZodString;
|
|
212
|
+
decided_at: z.ZodISODateTime;
|
|
213
|
+
}, z.core.$strip>>;
|
|
214
|
+
default_state: z.ZodOptional<z.ZodObject<{
|
|
215
|
+
necessary: z.ZodLiteral<true>;
|
|
216
|
+
functional: z.ZodBoolean;
|
|
217
|
+
analytics: z.ZodBoolean;
|
|
218
|
+
marketing: z.ZodBoolean;
|
|
219
|
+
}, z.core.$strip>>;
|
|
220
|
+
current_policy_version: z.ZodString;
|
|
221
|
+
needs_renewal: z.ZodBoolean;
|
|
222
|
+
degraded: z.ZodOptional<z.ZodBoolean>;
|
|
223
|
+
}, z.core.$strip>;
|
|
224
|
+
/** `POST /api/v1/ext/consent` response */
|
|
225
|
+
declare const PostConsentWireSchema: z.ZodObject<{
|
|
226
|
+
consent: z.ZodObject<{
|
|
227
|
+
necessary: z.ZodBoolean;
|
|
228
|
+
functional: z.ZodBoolean;
|
|
229
|
+
analytics: z.ZodBoolean;
|
|
230
|
+
marketing: z.ZodBoolean;
|
|
231
|
+
cookie_policy_version: z.ZodString;
|
|
232
|
+
decided_at: z.ZodISODateTime;
|
|
233
|
+
id: z.ZodString;
|
|
234
|
+
}, z.core.$strip>;
|
|
235
|
+
}, z.core.$strip>;
|
|
236
|
+
/** `POST /api/v1/ext/consent/sync` response */
|
|
237
|
+
declare const PostSyncWireSchema: z.ZodObject<{
|
|
238
|
+
consent: z.ZodObject<{
|
|
239
|
+
necessary: z.ZodBoolean;
|
|
240
|
+
functional: z.ZodBoolean;
|
|
241
|
+
analytics: z.ZodBoolean;
|
|
242
|
+
marketing: z.ZodBoolean;
|
|
243
|
+
cookie_policy_version: z.ZodString;
|
|
244
|
+
decided_at: z.ZodISODateTime;
|
|
245
|
+
id: z.ZodString;
|
|
246
|
+
}, z.core.$strip>;
|
|
247
|
+
merge_strategy: z.ZodEnum<{
|
|
248
|
+
db_wins: "db_wins";
|
|
249
|
+
device_promoted: "device_promoted";
|
|
250
|
+
device_promoted_with_drift: "device_promoted_with_drift";
|
|
251
|
+
cross_user_insert: "cross_user_insert";
|
|
252
|
+
first_insert: "first_insert";
|
|
253
|
+
}>;
|
|
254
|
+
warning: z.ZodNullable<z.ZodEnum<{
|
|
255
|
+
shared_device_detected: "shared_device_detected";
|
|
256
|
+
local_state_overridden: "local_state_overridden";
|
|
257
|
+
drift_detected: "drift_detected";
|
|
258
|
+
}>>;
|
|
259
|
+
}, z.core.$strip>;
|
|
260
|
+
/** `POST /api/v1/ext/consent/withdraw` response */
|
|
261
|
+
declare const PostWithdrawWireSchema: z.ZodObject<{
|
|
262
|
+
consent: z.ZodObject<{
|
|
263
|
+
necessary: z.ZodBoolean;
|
|
264
|
+
functional: z.ZodBoolean;
|
|
265
|
+
analytics: z.ZodBoolean;
|
|
266
|
+
marketing: z.ZodBoolean;
|
|
267
|
+
cookie_policy_version: z.ZodString;
|
|
268
|
+
decided_at: z.ZodISODateTime;
|
|
269
|
+
id: z.ZodString;
|
|
270
|
+
is_withdrawal: z.ZodLiteral<true>;
|
|
271
|
+
supersedes_id: z.ZodNullable<z.ZodString>;
|
|
272
|
+
}, z.core.$strip>;
|
|
273
|
+
}, z.core.$strip>;
|
|
274
|
+
/** `GET /api/v1/ext/consent/categories` response (already camelCase) */
|
|
275
|
+
declare const GetCategoriesWireSchema: z.ZodObject<{
|
|
276
|
+
categories: z.ZodArray<z.ZodObject<{
|
|
277
|
+
code: z.ZodEnum<{
|
|
278
|
+
necessary: "necessary";
|
|
279
|
+
functional: "functional";
|
|
280
|
+
analytics: "analytics";
|
|
281
|
+
marketing: "marketing";
|
|
282
|
+
}>;
|
|
283
|
+
displayOrder: z.ZodNumber;
|
|
284
|
+
isRequired: z.ZodBoolean;
|
|
285
|
+
label: z.ZodString;
|
|
286
|
+
description: z.ZodString;
|
|
287
|
+
exampleServices: z.ZodArray<z.ZodString>;
|
|
288
|
+
}, z.core.$strip>>;
|
|
289
|
+
}, z.core.$strip>;
|
|
290
|
+
/** `GET /api/v1/ext/consent/document` response (already camelCase) */
|
|
291
|
+
declare const GetDocumentWireSchema: z.ZodObject<{
|
|
292
|
+
document: z.ZodObject<{
|
|
293
|
+
id: z.ZodString;
|
|
294
|
+
version: z.ZodString;
|
|
295
|
+
title: z.ZodString;
|
|
296
|
+
summary: z.ZodString;
|
|
297
|
+
content: z.ZodString;
|
|
298
|
+
effectiveDate: z.ZodISODate;
|
|
299
|
+
publishedAt: z.ZodISODateTime;
|
|
300
|
+
}, z.core.$strip>;
|
|
301
|
+
}, z.core.$strip>;
|
|
302
|
+
/**
|
|
303
|
+
* Validate `X-FFID-Device-Id` header value. Accepts UUIDv4 / UUIDv7 (both
|
|
304
|
+
* 36-char hex with dashes). Reuses the same `UUID_REGEX` as `UuidLikeSchema`
|
|
305
|
+
* to avoid drift between wire id validators.
|
|
306
|
+
*/
|
|
307
|
+
declare const DeviceIdSchema: z.ZodString;
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Cookie Consent — TypeScript types derived from Zod schemas.
|
|
311
|
+
*
|
|
312
|
+
* Always derive types via `z.infer<>`; never hand-roll TypeScript types that
|
|
313
|
+
* could drift from the canonical Zod schema. Refs: schemas.ts
|
|
314
|
+
*/
|
|
315
|
+
|
|
316
|
+
type FFIDConsentCategoryCode = z.infer<typeof FFIDConsentCategoryCodeSchema>;
|
|
317
|
+
type FFIDConsentCategories = z.infer<typeof FFIDConsentCategoriesSchema>;
|
|
318
|
+
type FFIDPublicConsentSource = z.infer<typeof FFIDPublicConsentSourceSchema>;
|
|
319
|
+
type FFIDInternalConsentSource = z.infer<typeof FFIDInternalConsentSourceSchema>;
|
|
320
|
+
type FFIDConsentSource = z.infer<typeof FFIDConsentSourceSchema>;
|
|
321
|
+
type FFIDConsentState = z.infer<typeof FFIDConsentStateSchema>;
|
|
322
|
+
type FFIDConsentUpdate = z.infer<typeof FFIDConsentUpdateSchema>;
|
|
323
|
+
type FFIDConsentCategoryMetadata = z.infer<typeof FFIDConsentCategoryMetadataSchema>;
|
|
324
|
+
type FFIDCookiePolicy = z.infer<typeof FFIDCookiePolicySchema>;
|
|
325
|
+
type FFIDConsentMergeStrategy = z.infer<typeof FFIDConsentMergeStrategySchema>;
|
|
326
|
+
type FFIDConsentMergeWarning = z.infer<typeof FFIDConsentMergeWarningSchema>;
|
|
327
|
+
type PostConsentRequest = z.infer<typeof PostConsentRequestSchema>;
|
|
328
|
+
type PostSyncRequest = z.infer<typeof PostSyncRequestSchema>;
|
|
329
|
+
type GetConsentMeWire = z.infer<typeof GetConsentMeWireSchema>;
|
|
330
|
+
type PostConsentWire = z.infer<typeof PostConsentWireSchema>;
|
|
331
|
+
type PostSyncWire = z.infer<typeof PostSyncWireSchema>;
|
|
332
|
+
type PostWithdrawWire = z.infer<typeof PostWithdrawWireSchema>;
|
|
333
|
+
type GetCategoriesWire = z.infer<typeof GetCategoriesWireSchema>;
|
|
334
|
+
type GetDocumentWire = z.infer<typeof GetDocumentWireSchema>;
|
|
335
|
+
/**
|
|
336
|
+
* Warning event surfaced to `onConsentChange(state, warning)` after a `/sync`
|
|
337
|
+
* call. Spec §5.3 requires non-null warnings to be visible to the user.
|
|
338
|
+
*/
|
|
339
|
+
interface FFIDConsentMergeWarningEvent {
|
|
340
|
+
code: FFIDConsentMergeWarning;
|
|
341
|
+
message: string;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Result of a sync call surfaced to consumers — drops the wire envelope and
|
|
345
|
+
* folds the warning into a structured object so UI code can pattern-match
|
|
346
|
+
* without re-implementing `merge_strategy → warning` knowledge.
|
|
347
|
+
*/
|
|
348
|
+
interface FFIDConsentSyncResult {
|
|
349
|
+
state: FFIDConsentState;
|
|
350
|
+
mergeStrategy: FFIDConsentMergeStrategy;
|
|
351
|
+
warning: FFIDConsentMergeWarning | null;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Cookie Consent — error types and reserved errorIds.
|
|
356
|
+
*
|
|
357
|
+
* Spec §8.2 reserves these errorIds for Sentry / logger correlation between
|
|
358
|
+
* SDK, FFID API, and ops dashboards. SDK consumers can branch on `code` to
|
|
359
|
+
* customize UX (e.g., show retry hint for rate_limited, show "please relogin"
|
|
360
|
+
* for auth_failure).
|
|
361
|
+
*
|
|
362
|
+
* Refs: docs/02-design/COOKIE_CONSENT.md §8
|
|
363
|
+
*/
|
|
364
|
+
/**
|
|
365
|
+
* Error code literal union for consent SDK failures.
|
|
366
|
+
*
|
|
367
|
+
* - The first 10 codes (`cookie_consent_*`) are reserved by spec §8.2 for
|
|
368
|
+
* correlation across SDK / FFID API / ops dashboards.
|
|
369
|
+
* - `auth_failure` is added by the SDK to cover 401 / 403 responses; it is
|
|
370
|
+
* NOT part of spec §8.2 reserved codes — the SDK shares this code with the
|
|
371
|
+
* wider FFID auth surface so consumer error handlers can branch uniformly.
|
|
372
|
+
*/
|
|
373
|
+
declare const FFID_CONSENT_ERROR_CODES: readonly ["cookie_consent_api_unreachable", "cookie_consent_response_invalid", "cookie_consent_rate_limited", "cookie_consent_cookie_write_failed", "cookie_consent_parse_failed", "cookie_consent_sync_conflict", "cookie_consent_immutable_violation", "cookie_consent_policy_outdated", "cookie_consent_capture_failed", "cookie_consent_db_write_failed", "auth_failure"];
|
|
374
|
+
type FFIDConsentErrorCode = (typeof FFID_CONSENT_ERROR_CODES)[number];
|
|
375
|
+
/**
|
|
376
|
+
* Error class for all consent-related failures surfaced through SDK callbacks
|
|
377
|
+
* (`onError`) and hook `error` fields.
|
|
378
|
+
*
|
|
379
|
+
* `message` is intended to be user-displayable in Japanese; consumers should
|
|
380
|
+
* still feel free to override per their i18n needs.
|
|
381
|
+
*/
|
|
382
|
+
declare class FFIDConsentError extends Error {
|
|
383
|
+
readonly code: FFIDConsentErrorCode;
|
|
384
|
+
readonly httpStatus?: number;
|
|
385
|
+
/** For rate-limited errors (HTTP 429): seconds suggested by `Retry-After`. */
|
|
386
|
+
readonly retryAfterSec?: number;
|
|
387
|
+
readonly cause?: unknown;
|
|
388
|
+
constructor(code: FFIDConsentErrorCode, message: string, options?: {
|
|
389
|
+
httpStatus?: number;
|
|
390
|
+
retryAfterSec?: number;
|
|
391
|
+
cause?: unknown;
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Result discriminated union for consent SDK actions.
|
|
396
|
+
* Mirrors `src/lib/common/types.ts` Result pattern but kept SDK-local so the
|
|
397
|
+
* package has no FFID-internal imports.
|
|
398
|
+
*/
|
|
399
|
+
type FFIDConsentResult<T> = {
|
|
400
|
+
ok: true;
|
|
401
|
+
value: T;
|
|
402
|
+
} | {
|
|
403
|
+
ok: false;
|
|
404
|
+
error: FFIDConsentError;
|
|
405
|
+
};
|
|
406
|
+
/**
|
|
407
|
+
* Japanese user-displayable default messages keyed by errorCode.
|
|
408
|
+
* Consumers can override; SDK UI components use these as fallback.
|
|
409
|
+
*/
|
|
410
|
+
declare const DEFAULT_CONSENT_ERROR_MESSAGES: Record<FFIDConsentErrorCode, string>;
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Cookie Consent codec — encode / decode the `ffid_consent` 1st-party cookie.
|
|
414
|
+
*
|
|
415
|
+
* Format: `v1:n+,f+,a-,m+`
|
|
416
|
+
* - `v1:` version prefix (forward-compat for v2+)
|
|
417
|
+
* - n=necessary, f=functional, a=analytics, m=marketing
|
|
418
|
+
* - `+` (granted) / `-` (denied)
|
|
419
|
+
*
|
|
420
|
+
* **Safe-parse contract**: `decodeConsentCookie` NEVER throws. Any malformed,
|
|
421
|
+
* truncated, future-version, or otherwise unparseable input returns
|
|
422
|
+
* `ALL_DENIED_EXCEPT_NECESSARY`. Callers are responsible for logging via
|
|
423
|
+
* `cookie_consent_parse_failed` errorId if needed.
|
|
424
|
+
*
|
|
425
|
+
* This codec is **byte-compatible** with the FFID server-side implementation
|
|
426
|
+
* (`src/lib/cookie-consent/codec.ts`). Both implementations must produce and
|
|
427
|
+
* accept the identical wire format — a roundtrip through either codec must
|
|
428
|
+
* yield the same bytes.
|
|
429
|
+
*
|
|
430
|
+
* Refs: docs/02-design/COOKIE_CONSENT.md §6.5
|
|
431
|
+
*/
|
|
432
|
+
|
|
433
|
+
declare const COOKIE_VERSION: "v1";
|
|
434
|
+
declare const ALL_DENIED_EXCEPT_NECESSARY: FFIDConsentCategories;
|
|
435
|
+
declare function encodeConsentCookie(cats: FFIDConsentCategories): string;
|
|
436
|
+
/**
|
|
437
|
+
* Safely decode the consent cookie string.
|
|
438
|
+
* Returns `ALL_DENIED_EXCEPT_NECESSARY` on any failure (fail-safe).
|
|
439
|
+
*/
|
|
440
|
+
declare function decodeConsentCookie(raw: string | null | undefined): FFIDConsentCategories;
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Cookie Consent — browser cookie read/write with read-back verification.
|
|
444
|
+
*
|
|
445
|
+
* Spec §6.5:
|
|
446
|
+
* - Key: `ffid_consent`
|
|
447
|
+
* - 1st-party, SameSite=Lax, Secure (production only — localhost dev uses
|
|
448
|
+
* non-Secure)
|
|
449
|
+
* - TTL: 1 year
|
|
450
|
+
* - Write must be verified by read-back; failure -> caller surfaces
|
|
451
|
+
* `cookie_consent_cookie_write_failed` and falls back to sessionStorage.
|
|
452
|
+
*
|
|
453
|
+
* This module is SSR-safe: all functions become no-ops when `document` is
|
|
454
|
+
* unavailable (server-side rendering), so calling code does not need to
|
|
455
|
+
* `typeof window` check itself.
|
|
456
|
+
*
|
|
457
|
+
* Refs: docs/02-design/COOKIE_CONSENT.md §6.5
|
|
458
|
+
*/
|
|
459
|
+
|
|
460
|
+
declare const CONSENT_COOKIE_NAME = "ffid_consent";
|
|
461
|
+
/** 1 year in seconds (Max-Age). */
|
|
462
|
+
declare const CONSENT_COOKIE_MAX_AGE_SEC: number;
|
|
463
|
+
/**
|
|
464
|
+
* sessionStorage mirror key (spec §6.5).
|
|
465
|
+
*
|
|
466
|
+
* **Precedence rule** (spec §6.5 "session 内では cookie より優先"): when
|
|
467
|
+
* reading consent state during the same browser session, the mirror takes
|
|
468
|
+
* precedence over the cookie. This protects against the race where the user
|
|
469
|
+
* just made a decision (we wrote to sessionStorage synchronously) but the
|
|
470
|
+
* browser hasn't yet committed the cookie write. Callers MUST read mirror
|
|
471
|
+
* first, then fall back to cookie. See `FFIDAnalyticsProvider.bootstrap`.
|
|
472
|
+
*/
|
|
473
|
+
declare const CONSENT_SESSION_STORAGE_KEY = "ffid_consent_state";
|
|
474
|
+
interface WriteConsentCookieOptions {
|
|
475
|
+
/** Override Secure flag for non-https custom domains. Defaults to production. */
|
|
476
|
+
secure?: boolean;
|
|
477
|
+
/**
|
|
478
|
+
* Cookie domain. Leave undefined for current host only (1st-party). Setting
|
|
479
|
+
* a parent domain (e.g., `.feelflow.net`) enables cross-subdomain sync but
|
|
480
|
+
* requires explicit opt-in.
|
|
481
|
+
*/
|
|
482
|
+
domain?: string;
|
|
483
|
+
/** Path for the cookie. Defaults to '/'. */
|
|
484
|
+
path?: string;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Read the consent cookie. Returns `ALL_DENIED_EXCEPT_NECESSARY` if absent,
|
|
488
|
+
* malformed, or `document` unavailable (SSR).
|
|
489
|
+
*
|
|
490
|
+
* Per spec §6.5 the codec never throws, so consumers receive a well-formed
|
|
491
|
+
* value in every code path.
|
|
492
|
+
*/
|
|
493
|
+
declare function readConsentCookie(): FFIDConsentCategories;
|
|
494
|
+
/**
|
|
495
|
+
* Write the consent cookie and verify via read-back.
|
|
496
|
+
*
|
|
497
|
+
* @returns true if write was verified, false if browser silently rejected
|
|
498
|
+
* (e.g., ITP, all-cookie block, Brave shields, private mode quirk).
|
|
499
|
+
* On false, callers should surface `cookie_consent_cookie_write_failed`
|
|
500
|
+
* and switch to sessionStorage fallback.
|
|
501
|
+
*/
|
|
502
|
+
declare function writeConsentCookie(cats: FFIDConsentCategories, opts?: WriteConsentCookieOptions): boolean;
|
|
503
|
+
/**
|
|
504
|
+
* Delete the consent cookie (used by full withdrawal + tests).
|
|
505
|
+
*
|
|
506
|
+
* @returns true if deletion was verified (cookie absent on read-back).
|
|
507
|
+
*/
|
|
508
|
+
declare function deleteConsentCookie(opts?: WriteConsentCookieOptions): boolean;
|
|
509
|
+
/**
|
|
510
|
+
* Read the sessionStorage mirror. Per spec §6.5, this must be consulted BEFORE
|
|
511
|
+
* the cookie within the same session (write-after-decision race protection).
|
|
512
|
+
* See `CONSENT_SESSION_STORAGE_KEY` doc-comment for the precedence rule.
|
|
513
|
+
*/
|
|
514
|
+
declare function readConsentSessionMirror(): FFIDConsentCategories | null;
|
|
515
|
+
declare function writeConsentSessionMirror(cats: FFIDConsentCategories): boolean;
|
|
516
|
+
declare function clearConsentSessionMirror(): void;
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Cookie Consent — Google Consent Mode v2 wiring.
|
|
520
|
+
*
|
|
521
|
+
* The bridge owns three concerns:
|
|
522
|
+
*
|
|
523
|
+
* 1. **Pre-decision default**: pushes `gtag('consent', 'default', ...)` with
|
|
524
|
+
* ALL_DENIED_EXCEPT_NECESSARY **before** any GA script loads, so GA's
|
|
525
|
+
* initial dataLayer scan picks up the deny defaults. The Provider calls
|
|
526
|
+
* `setDefaultConsent()` synchronously on mount, prior to script injection.
|
|
527
|
+
*
|
|
528
|
+
* 2. **Post-decision update**: after the SDK resolves the consent state from
|
|
529
|
+
* cookie / API, the Provider calls `updateConsent()`, which pushes
|
|
530
|
+
* `gtag('consent', 'update', ...)` mapped from `FFIDConsentCategories`.
|
|
531
|
+
*
|
|
532
|
+
* 3. **GA script injection**: when `analytics` is granted and a measurement ID
|
|
533
|
+
* is configured, the bridge appends the `gtag.js` script tag (no-op when
|
|
534
|
+
* consent is denied or measurement ID is empty).
|
|
535
|
+
*
|
|
536
|
+
* Tests verify the push order (`consent default` precedes any tag wiring) and
|
|
537
|
+
* the boolean → 'granted'|'denied' mapping.
|
|
538
|
+
*
|
|
539
|
+
* Refs: docs/02-design/COOKIE_CONSENT.md §6.2 + §7.5 (testing strategy)
|
|
540
|
+
*/
|
|
541
|
+
|
|
542
|
+
declare global {
|
|
543
|
+
interface Window {
|
|
544
|
+
dataLayer?: unknown[];
|
|
545
|
+
gtag?: (...args: unknown[]) => void;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
interface GtagBridgeOptions {
|
|
549
|
+
/** GA Measurement ID, e.g., `G-XXXXXXX`. Empty/null = GA not wired. */
|
|
550
|
+
gaMeasurementId?: string | null;
|
|
551
|
+
/** Override script src for testing. Default: official gtag URL. */
|
|
552
|
+
gtagScriptSrc?: string;
|
|
553
|
+
/** Document/window injection target (server-side stays no-op). */
|
|
554
|
+
win?: Window;
|
|
555
|
+
}
|
|
556
|
+
interface GtagConsentParams {
|
|
557
|
+
ad_storage: 'granted' | 'denied';
|
|
558
|
+
ad_user_data: 'granted' | 'denied';
|
|
559
|
+
ad_personalization: 'granted' | 'denied';
|
|
560
|
+
analytics_storage: 'granted' | 'denied';
|
|
561
|
+
functionality_storage: 'granted' | 'denied';
|
|
562
|
+
personalization_storage: 'granted' | 'denied';
|
|
563
|
+
security_storage: 'granted' | 'denied';
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Map our 4-category model to Google's Consent Mode v2 keys.
|
|
567
|
+
*
|
|
568
|
+
* - `necessary` → `security_storage` (always granted — regulatory baseline)
|
|
569
|
+
* - `functional` → `functionality_storage` + `personalization_storage`
|
|
570
|
+
* - `analytics` → `analytics_storage`
|
|
571
|
+
* - `marketing` → `ad_storage` + `ad_user_data` + `ad_personalization`
|
|
572
|
+
*/
|
|
573
|
+
declare function mapCategoriesToGtagParams(cats: FFIDConsentCategories): GtagConsentParams;
|
|
574
|
+
interface GtagBridge {
|
|
575
|
+
/**
|
|
576
|
+
* Push `consent default` with ALL_DENIED_EXCEPT_NECESSARY. Must be called
|
|
577
|
+
* **synchronously** before any GA script tag is added. Idempotent.
|
|
578
|
+
*/
|
|
579
|
+
setDefaultConsent(): void;
|
|
580
|
+
/**
|
|
581
|
+
* Push `consent update` mapped from current categories. Safe to call as
|
|
582
|
+
* many times as needed (consent toggle / banner / withdrawal etc.).
|
|
583
|
+
*/
|
|
584
|
+
updateConsent(cats: FFIDConsentCategories): void;
|
|
585
|
+
/**
|
|
586
|
+
* Inject GA loader script when `analytics` is granted. Does nothing when
|
|
587
|
+
* GA measurement ID is empty or analytics is denied.
|
|
588
|
+
*/
|
|
589
|
+
applyGaInjection(cats: FFIDConsentCategories): void;
|
|
590
|
+
}
|
|
591
|
+
declare function createGtagBridge(opts?: GtagBridgeOptions): GtagBridge;
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Cookie Consent — `<FFIDAnalyticsProvider>` (gtag + Sentry wiring).
|
|
595
|
+
*
|
|
596
|
+
* Responsibilities:
|
|
597
|
+
* 1. Mount-time: push `gtag('consent', 'default', ALL_DENIED_EXCEPT_NECESSARY)`
|
|
598
|
+
* synchronously BEFORE any GA script could load (spec §6.2).
|
|
599
|
+
* 2. Resolve initial state: read cookie / session mirror, then GET /me to
|
|
600
|
+
* reconcile against server-side authoritative.
|
|
601
|
+
* 3. On consent change: push `gtag('consent', 'update', ...)`, inject GA
|
|
602
|
+
* script if `analytics` granted + measurement ID configured.
|
|
603
|
+
* 4. Sentry replay gating: when `functional` is granted, start replay;
|
|
604
|
+
* otherwise stop (graceful no-op if `@sentry/react` is not present).
|
|
605
|
+
* 5. Surface merge warnings from `/sync` via `onConsentChange(state, warning)`.
|
|
606
|
+
*
|
|
607
|
+
* Note: Vercel Analytics integration is **not** yet wired — planned for a
|
|
608
|
+
* follow-up issue. Until then, consumers can adopt
|
|
609
|
+
* `@vercel/analytics`'s `<Analytics />` component directly and gate it on
|
|
610
|
+
* `useFFIDConsent().categories.analytics`.
|
|
611
|
+
*
|
|
612
|
+
* Refs: docs/02-design/COOKIE_CONSENT.md §6.2 + §6.3 + §6.7
|
|
613
|
+
*/
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Minimal Sentry shape we depend on. Avoids forcing consumers to install
|
|
617
|
+
* `@sentry/react` if they're not using session replay.
|
|
618
|
+
*/
|
|
619
|
+
interface SentryReplayLike {
|
|
620
|
+
start: () => void;
|
|
621
|
+
stop: () => void | Promise<void>;
|
|
622
|
+
}
|
|
623
|
+
interface SentryNamespace {
|
|
624
|
+
getReplay?: () => SentryReplayLike | undefined;
|
|
625
|
+
}
|
|
626
|
+
interface FFIDAnalyticsProviderProps {
|
|
627
|
+
/** FFID API base URL (e.g., `https://id.feelflow.net`). Required. */
|
|
628
|
+
baseUrl: string;
|
|
629
|
+
/** Service API Key issued by FFID for this app. Required. */
|
|
630
|
+
serviceApiKey: string;
|
|
631
|
+
/** Resolves a Bearer token, or null when logged out. Optional. */
|
|
632
|
+
getAccessToken?: () => string | null | Promise<string | null>;
|
|
633
|
+
/**
|
|
634
|
+
* GA Measurement ID, e.g., `G-XXXXXXX`. Pass `null` to disable GA wiring.
|
|
635
|
+
* 2-state field (`string | null`) — null is the explicit "no GA" sentinel,
|
|
636
|
+
* avoiding the triple-state `string | null | undefined` anti-pattern.
|
|
637
|
+
*/
|
|
638
|
+
gaMeasurementId?: string | null;
|
|
639
|
+
/** When true, control Sentry session replay based on `functional` consent. */
|
|
640
|
+
sentryReplay?: boolean;
|
|
641
|
+
/** Sentry namespace override; defaults to `(window as any).Sentry`. */
|
|
642
|
+
sentry?: SentryNamespace;
|
|
643
|
+
/** Fired whenever consent state changes; second argument is any merge warning. */
|
|
644
|
+
onConsentChange?: (state: FFIDConsentState, warning: FFIDConsentMergeWarningEvent | null) => void;
|
|
645
|
+
/** Fired when any action error occurs. */
|
|
646
|
+
onError?: (error: FFIDConsentError) => void;
|
|
647
|
+
/** Whether to open banner automatically when no decision is on file. Default: true. */
|
|
648
|
+
autoShowBanner?: boolean;
|
|
649
|
+
/** Optional fetch override (mainly for tests). */
|
|
650
|
+
fetchImpl?: typeof fetch;
|
|
651
|
+
children: ReactNode;
|
|
652
|
+
}
|
|
653
|
+
declare function FFIDAnalyticsProvider({ baseUrl, serviceApiKey, getAccessToken, gaMeasurementId, sentryReplay, sentry, onConsentChange, onError, autoShowBanner, fetchImpl, children, }: FFIDAnalyticsProviderProps): React.JSX.Element;
|
|
654
|
+
|
|
655
|
+
interface FFIDConsentContextValue {
|
|
656
|
+
/** Current consent state. Always non-null; "not yet decided" is encoded by `hasDecided=false`. */
|
|
657
|
+
state: FFIDConsentState;
|
|
658
|
+
/** True while the provider is performing initial GET /me / sync. */
|
|
659
|
+
isLoading: boolean;
|
|
660
|
+
/** Last error from any action (auto-cleared on next successful action). */
|
|
661
|
+
error: FFIDConsentError | null;
|
|
662
|
+
/** Category master data; lazily fetched on first preference center open. */
|
|
663
|
+
categoryMetadata: FFIDConsentCategoryMetadata[] | null;
|
|
664
|
+
/** Current cookie_policy document; lazily fetched on banner / settings open. */
|
|
665
|
+
policy: FFIDCookiePolicy | null;
|
|
666
|
+
isBannerOpen: boolean;
|
|
667
|
+
isPreferencesOpen: boolean;
|
|
668
|
+
openBanner: () => void;
|
|
669
|
+
closeBanner: () => void;
|
|
670
|
+
openPreferences: () => Promise<void>;
|
|
671
|
+
closePreferences: () => void;
|
|
672
|
+
acceptAll: () => Promise<FFIDConsentResult<FFIDConsentState>>;
|
|
673
|
+
acceptNecessaryOnly: () => Promise<FFIDConsentResult<FFIDConsentState>>;
|
|
674
|
+
setCategories: (changes: {
|
|
675
|
+
functional?: boolean;
|
|
676
|
+
analytics?: boolean;
|
|
677
|
+
marketing?: boolean;
|
|
678
|
+
}, source?: 'banner' | 'preference_center') => Promise<FFIDConsentResult<FFIDConsentState>>;
|
|
679
|
+
withdraw: () => Promise<FFIDConsentResult<FFIDConsentState>>;
|
|
680
|
+
refresh: () => Promise<void>;
|
|
681
|
+
/**
|
|
682
|
+
* @internal Reconcile local cookie state with server-side via `POST /sync`.
|
|
683
|
+
*
|
|
684
|
+
* Consumers integrating FFID auth should call this **from their OAuth
|
|
685
|
+
* callback handler** so that the freshly-issued Bearer token can be merged
|
|
686
|
+
* with the pre-login device-scoped consent row. The SDK does NOT subscribe
|
|
687
|
+
* to auth state internally — it has no portable way to detect login
|
|
688
|
+
* events across framework conventions (Next.js Middleware, OAuth flows,
|
|
689
|
+
* etc.). Pattern:
|
|
690
|
+
*
|
|
691
|
+
* ```ts
|
|
692
|
+
* import { useContext } from 'react'
|
|
693
|
+
* import { FFIDConsentContext } from '@feelflow/ffid-sdk/consent'
|
|
694
|
+
* function OAuthCallback() {
|
|
695
|
+
* const ctx = useContext(FFIDConsentContext)
|
|
696
|
+
* useEffect(() => { void ctx.syncWithUser() }, [ctx])
|
|
697
|
+
* }
|
|
698
|
+
* ```
|
|
699
|
+
*/
|
|
700
|
+
syncWithUser: () => Promise<FFIDConsentResult<FFIDConsentSyncResult>>;
|
|
701
|
+
}
|
|
702
|
+
declare const FFID_CONSENT_NOT_DECIDED_STATE: FFIDConsentState;
|
|
703
|
+
declare const FFIDConsentContext: React$1.Context<FFIDConsentContextValue>;
|
|
704
|
+
/**
|
|
705
|
+
* Warning literal-keyed default copy for `local_state_overridden` /
|
|
706
|
+
* `shared_device_detected` / `drift_detected`. Surfaced by the Provider via
|
|
707
|
+
* `onConsentChange(state, warning)` per spec §5.3.
|
|
708
|
+
*/
|
|
709
|
+
declare const DEFAULT_MERGE_WARNING_MESSAGES: Record<FFIDConsentMergeWarning, string>;
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* `useFFIDConsent` — primary hook for reading + updating cookie consent.
|
|
713
|
+
*
|
|
714
|
+
* @example
|
|
715
|
+
* ```tsx
|
|
716
|
+
* const { state, isLoading, acceptAll, openPreferences } = useFFIDConsent()
|
|
717
|
+
* if (state.categories.analytics) {
|
|
718
|
+
* // safe to fire analytics events
|
|
719
|
+
* }
|
|
720
|
+
* ```
|
|
721
|
+
*
|
|
722
|
+
* Refs: docs/02-design/COOKIE_CONSENT.md §6.3
|
|
723
|
+
*/
|
|
724
|
+
|
|
725
|
+
interface UseFFIDConsentReturn {
|
|
726
|
+
/** Full discriminated-union state (camelCase). */
|
|
727
|
+
state: FFIDConsentContextValue['state'];
|
|
728
|
+
/** Shortcut for `state.categories`. */
|
|
729
|
+
categories: FFIDConsentCategories;
|
|
730
|
+
/** Whether SDK is performing initial bootstrap / sync. */
|
|
731
|
+
isLoading: boolean;
|
|
732
|
+
/** Last action error (null after a successful action). */
|
|
733
|
+
error: FFIDConsentContextValue['error'];
|
|
734
|
+
/** Whether the banner is currently visible. */
|
|
735
|
+
isBannerOpen: boolean;
|
|
736
|
+
/** Whether the preference center modal is currently visible. */
|
|
737
|
+
isPreferencesOpen: boolean;
|
|
738
|
+
openBanner: FFIDConsentContextValue['openBanner'];
|
|
739
|
+
closeBanner: FFIDConsentContextValue['closeBanner'];
|
|
740
|
+
openPreferences: FFIDConsentContextValue['openPreferences'];
|
|
741
|
+
closePreferences: FFIDConsentContextValue['closePreferences'];
|
|
742
|
+
acceptAll: FFIDConsentContextValue['acceptAll'];
|
|
743
|
+
acceptNecessaryOnly: FFIDConsentContextValue['acceptNecessaryOnly'];
|
|
744
|
+
setCategories: FFIDConsentContextValue['setCategories'];
|
|
745
|
+
withdraw: FFIDConsentContextValue['withdraw'];
|
|
746
|
+
refresh: FFIDConsentContextValue['refresh'];
|
|
747
|
+
}
|
|
748
|
+
declare function useFFIDConsent(): UseFFIDConsentReturn;
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* `useFFIDConsentPreferences` — advanced hook for building custom preference
|
|
752
|
+
* center UI. Provides category metadata and policy document alongside the
|
|
753
|
+
* standard state.
|
|
754
|
+
*
|
|
755
|
+
* @example
|
|
756
|
+
* ```tsx
|
|
757
|
+
* const { categoryMetadata, policy, save } = useFFIDConsentPreferences()
|
|
758
|
+
* return (
|
|
759
|
+
* <>
|
|
760
|
+
* {categoryMetadata?.map(c => <Toggle key={c.code} ... />)}
|
|
761
|
+
* <button onClick={() => save({ analytics: true })}>Save</button>
|
|
762
|
+
* </>
|
|
763
|
+
* )
|
|
764
|
+
* ```
|
|
765
|
+
*
|
|
766
|
+
* Refs: docs/02-design/COOKIE_CONSENT.md §6.3
|
|
767
|
+
*/
|
|
768
|
+
|
|
769
|
+
interface UseFFIDConsentPreferencesReturn {
|
|
770
|
+
state: FFIDConsentState;
|
|
771
|
+
/** Category master with i18n labels; null until `openPreferences()` is called. */
|
|
772
|
+
categoryMetadata: FFIDConsentCategoryMetadata[] | null;
|
|
773
|
+
/** Cookie policy document; null until fetched. */
|
|
774
|
+
policy: FFIDCookiePolicy | null;
|
|
775
|
+
isLoading: boolean;
|
|
776
|
+
/**
|
|
777
|
+
* Save category changes. Source is locked to `preference_center` to make
|
|
778
|
+
* audit trails easier to interpret.
|
|
779
|
+
*/
|
|
780
|
+
save: (changes: {
|
|
781
|
+
functional?: boolean;
|
|
782
|
+
analytics?: boolean;
|
|
783
|
+
marketing?: boolean;
|
|
784
|
+
}) => Promise<FFIDConsentResult<FFIDConsentState>>;
|
|
785
|
+
withdraw: () => Promise<FFIDConsentResult<FFIDConsentState>>;
|
|
786
|
+
}
|
|
787
|
+
declare function useFFIDConsentPreferences(): UseFFIDConsentPreferencesReturn;
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* `<FFIDCookieBanner>` — bottom-sheet consent banner.
|
|
791
|
+
*
|
|
792
|
+
* Renders only when `state.hasDecided === false` or `state.needsRenewal === true`.
|
|
793
|
+
* Three buttons (spec §6.2):
|
|
794
|
+
* 1. "すべて同意" (Accept all)
|
|
795
|
+
* 2. "必要なものだけ" (Necessary only)
|
|
796
|
+
* 3. "詳細設定" (Open preference center)
|
|
797
|
+
*
|
|
798
|
+
* GDPR Article 7(1)/(2) UX requirements:
|
|
799
|
+
* - "同意" and "拒否" share the same size + visual prominence.
|
|
800
|
+
* - Buttons share the same styles by default; consumers can override via
|
|
801
|
+
* `classNames` props (`primaryButton`, `secondaryButton`, `linkButton`).
|
|
802
|
+
*
|
|
803
|
+
* Refs: docs/02-design/COOKIE_CONSENT.md §6.2
|
|
804
|
+
*/
|
|
805
|
+
|
|
806
|
+
interface FFIDCookieBannerClassNames {
|
|
807
|
+
root?: string;
|
|
808
|
+
message?: string;
|
|
809
|
+
buttonGroup?: string;
|
|
810
|
+
primaryButton?: string;
|
|
811
|
+
secondaryButton?: string;
|
|
812
|
+
linkButton?: string;
|
|
813
|
+
}
|
|
814
|
+
interface FFIDCookieBannerProps {
|
|
815
|
+
/** Root container class. */
|
|
816
|
+
className?: string;
|
|
817
|
+
/** Per-element override classes for finer customization. */
|
|
818
|
+
classNames?: FFIDCookieBannerClassNames;
|
|
819
|
+
/** Override the banner copy (default Japanese). */
|
|
820
|
+
message?: ReactNode;
|
|
821
|
+
/** Accept-all button label. Default: "すべて同意". */
|
|
822
|
+
acceptAllLabel?: ReactNode;
|
|
823
|
+
/** Necessary-only button label. Default: "必要なものだけ". */
|
|
824
|
+
necessaryOnlyLabel?: ReactNode;
|
|
825
|
+
/** Preference center link label. Default: "詳細設定". */
|
|
826
|
+
preferencesLabel?: ReactNode;
|
|
827
|
+
/** Custom root style. */
|
|
828
|
+
style?: CSSProperties;
|
|
829
|
+
}
|
|
830
|
+
declare function FFIDCookieBanner({ className, classNames, message, acceptAllLabel, necessaryOnlyLabel, preferencesLabel, style, }: FFIDCookieBannerProps): React.JSX.Element | null;
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* `<FFIDCookieSettings>` — preference center modal.
|
|
834
|
+
*
|
|
835
|
+
* Renders the 4-category toggle list (necessary is `disabled` per spec §6.2,
|
|
836
|
+
* runtime + DB invariant both enforce `necessary=true`). Save / Withdraw
|
|
837
|
+
* buttons commit changes through `useFFIDConsentPreferences`.
|
|
838
|
+
*
|
|
839
|
+
* Refs: docs/02-design/COOKIE_CONSENT.md §6.2 + §6.3
|
|
840
|
+
*/
|
|
841
|
+
interface FFIDCookieSettingsClassNames {
|
|
842
|
+
root?: string;
|
|
843
|
+
backdrop?: string;
|
|
844
|
+
header?: string;
|
|
845
|
+
categoryRow?: string;
|
|
846
|
+
primaryButton?: string;
|
|
847
|
+
secondaryButton?: string;
|
|
848
|
+
}
|
|
849
|
+
interface FFIDCookieSettingsProps {
|
|
850
|
+
className?: string;
|
|
851
|
+
classNames?: FFIDCookieSettingsClassNames;
|
|
852
|
+
/** Open/close override; defaults to the context's `isPreferencesOpen`. */
|
|
853
|
+
open?: boolean;
|
|
854
|
+
/** When true, also expose a "すべて撤回" button. Default: true. */
|
|
855
|
+
showWithdrawButton?: boolean;
|
|
856
|
+
}
|
|
857
|
+
declare function FFIDCookieSettings({ className, classNames, open, showWithdrawButton, }: FFIDCookieSettingsProps): React.JSX.Element | null;
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* `<FFIDCookieLink>` — footer link that opens the preference center.
|
|
861
|
+
*
|
|
862
|
+
* **GDPR Article 7(3) compliance**: this link MUST be present on every page
|
|
863
|
+
* to allow consent withdrawal with the same number of clicks as consent. Use
|
|
864
|
+
* this component in any footer / header to satisfy the requirement.
|
|
865
|
+
*
|
|
866
|
+
* Refs: docs/02-design/COOKIE_CONSENT.md §6.2
|
|
867
|
+
*/
|
|
868
|
+
|
|
869
|
+
interface FFIDCookieLinkProps {
|
|
870
|
+
children?: ReactNode;
|
|
871
|
+
className?: string;
|
|
872
|
+
style?: CSSProperties;
|
|
873
|
+
/** Optional element id for screen-reader-friendly footer landmarks. */
|
|
874
|
+
id?: string;
|
|
875
|
+
}
|
|
876
|
+
declare function FFIDCookieLink({ children, className, style, id, }: FFIDCookieLinkProps): React.JSX.Element;
|
|
877
|
+
|
|
878
|
+
export { GetCategoriesWireSchema as $, ALL_DENIED_EXCEPT_NECESSARY as A, FFIDConsentMergeWarningSchema as B, CONSENT_COOKIE_MAX_AGE_SEC as C, DEFAULT_CONSENT_ERROR_MESSAGES as D, type FFIDConsentSource as E, type FFIDConsentResult as F, type GetConsentMeWire as G, FFIDConsentSourceSchema as H, FFIDConsentStateSchema as I, FFIDConsentUpdateSchema as J, FFIDCookieBanner as K, type FFIDCookieBannerClassNames as L, type FFIDCookieBannerProps as M, FFIDCookieLink as N, type FFIDCookieLinkProps as O, type PostConsentWire as P, FFIDCookiePolicySchema as Q, FFIDCookieSettings as R, type FFIDCookieSettingsClassNames as S, type FFIDCookieSettingsProps as T, type FFIDInternalConsentSource as U, FFIDInternalConsentSourceSchema as V, type FFIDPublicConsentSource as W, FFIDPublicConsentSourceSchema as X, FFID_CONSENT_ERROR_CODES as Y, FFID_CONSENT_NOT_DECIDED_STATE as Z, type GetCategoriesWire as _, type FFIDConsentState as a, GetConsentMeWireSchema as a0, type GetDocumentWire as a1, GetDocumentWireSchema as a2, type GtagBridge as a3, type GtagBridgeOptions as a4, type PostConsentRequest as a5, PostConsentRequestSchema as a6, PostConsentWireSchema as a7, type PostSyncRequest as a8, PostSyncRequestSchema as a9, PostSyncWireSchema as aa, PostWithdrawWireSchema as ab, type UseFFIDConsentPreferencesReturn as ac, type UseFFIDConsentReturn as ad, clearConsentSessionMirror as ae, createGtagBridge as af, decodeConsentCookie as ag, deleteConsentCookie as ah, encodeConsentCookie as ai, mapCategoriesToGtagParams as aj, readConsentCookie as ak, readConsentSessionMirror as al, useFFIDConsent as am, useFFIDConsentPreferences as an, writeConsentCookie as ao, writeConsentSessionMirror as ap, type FFIDConsentUpdate as b, type FFIDConsentCategories as c, type FFIDConsentSyncResult as d, type FFIDConsentCategoryMetadata as e, type FFIDCookiePolicy as f, type PostSyncWire as g, type PostWithdrawWire as h, CONSENT_COOKIE_NAME as i, CONSENT_SESSION_STORAGE_KEY as j, COOKIE_VERSION as k, DEFAULT_MERGE_WARNING_MESSAGES as l, DeviceIdSchema as m, FFIDAnalyticsProvider as n, type FFIDAnalyticsProviderProps as o, FFIDConsentCategoriesSchema as p, type FFIDConsentCategoryCode as q, FFIDConsentCategoryCodeSchema as r, FFIDConsentCategoryMetadataSchema as s, FFIDConsentContext as t, type FFIDConsentContextValue as u, FFIDConsentError as v, type FFIDConsentErrorCode as w, type FFIDConsentMergeStrategy as x, FFIDConsentMergeStrategySchema as y, type FFIDConsentMergeWarning as z };
|