@redacto.io/consent-sdk-react 10.3.1 → 11.0.0-beta.2
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 +195 -34
- package/dist/{chunk-RG4PVVKF.mjs → chunk-W45JQYDG.mjs} +155 -58
- package/dist/index.d.mts +38 -4
- package/dist/index.d.ts +38 -4
- package/dist/index.js +523 -389
- package/dist/index.mjs +80 -39
- package/dist/privacy-center.d.mts +359 -31
- package/dist/privacy-center.d.ts +359 -31
- package/dist/privacy-center.js +4823 -2245
- package/dist/privacy-center.mjs +4605 -2200
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -117,6 +117,67 @@ npm install @tanstack/react-query @tanstack/react-table \
|
|
|
117
117
|
|
|
118
118
|
If you only use `RedactoNoticeConsent` or `RedactoNoticeConsentInline`, these peers are not needed.
|
|
119
119
|
|
|
120
|
+
### API host (`baseUrl`)
|
|
121
|
+
|
|
122
|
+
Pass the Redacto **API host** as `baseUrl`, for example `https://api.redacto.io`. Every component, `createConsentClient` and `BaseUrlProvider` take it, and the SDK reaches each backend service under it:
|
|
123
|
+
|
|
124
|
+
| Service | URL |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| Consent server | `{baseUrl}/consent` |
|
|
127
|
+
| Consent ledger | `{baseUrl}/consent-ledger` |
|
|
128
|
+
| User service (Privacy Center branding) | `{baseUrl}/user/api/0` |
|
|
129
|
+
|
|
130
|
+
A host behind a path prefix works the same way: `https://gateway.example.com/redacto` reaches the consent server at `https://gateway.example.com/redacto/consent`, and so on.
|
|
131
|
+
|
|
132
|
+
| Request | Service |
|
|
133
|
+
| --- | --- |
|
|
134
|
+
| `RedactoNoticeConsent` notice read | Ledger with a JWT; consent server in sandbox and in the magic-link (`applicationId`) flow |
|
|
135
|
+
| `RedactoNoticeConsent` notice audio | Ledger, sandbox included |
|
|
136
|
+
| `RedactoNoticeConsentInline` notice read | Ledger with an `accessToken`; consent server without one and in the magic-link (`applicationId`) flow |
|
|
137
|
+
| Consent writes (`submit-consent`) and the already-consented check (`check-consent`) | Ledger |
|
|
138
|
+
| `RedactoNoticeAssisted` notice, audio and OTP; guardian verification (DigiLocker) | Consent server |
|
|
139
|
+
| Privacy Center user consents, product purposes, receipts, manage consent | Ledger |
|
|
140
|
+
| Privacy Center DSAR form, cases, activities, identities, OTP, token exchange, notifications, uploads, products, settings | Consent server |
|
|
141
|
+
| Privacy Center org branding (by organisation UUID) | User service |
|
|
142
|
+
|
|
143
|
+
For custom Privacy Center compositions, `useBaseUrl()` returns the consent server URL as `baseUrl`, the ledger URL as `ledgerBaseUrl` and the host as `apiHost`.
|
|
144
|
+
|
|
145
|
+
#### The consent server URL (deprecated, still supported)
|
|
146
|
+
|
|
147
|
+
Earlier releases documented the **consent server URL** as `baseUrl` (`https://api.redacto.io/consent`), with an optional `ledgerBaseUrl`. Both keep working exactly as before, so upgrading changes nothing until you switch:
|
|
148
|
+
|
|
149
|
+
- With the consent server URL, every request goes where earlier releases sent it. Consent writes, the already-consented check and the Privacy Center's consents and receipts use the ledger only when `ledgerBaseUrl` is set; without it they stay on the consent server's legacy routes. `useBaseUrl()` returns both values as passed, with `apiHost` undefined.
|
|
150
|
+
- Without `baseUrl`, the notice components still use the built-in production default.
|
|
151
|
+
- The SDK tells the two apart by shape: an absolute `http(s)` URL that does not end in `/consent` is the API host; anything else (omitted, ending in `/consent`, or relative) is used as passed. A `baseUrl` ending in `/consent-ledger` resolves to its host the same way, so the ledger URL earlier releases took as `ledgerBaseUrl` also works as `baseUrl`. A consent server that is not mounted at `/consent` is therefore read as a host, so keep passing its URL ending in `/consent` until you switch.
|
|
152
|
+
- Each deprecated form logs one console warning per page load; passing the host clears them.
|
|
153
|
+
|
|
154
|
+
Compared with the consent server URL plus `ledgerBaseUrl`, the host changes two routes: `RedactoNoticeConsentInline` reads its notice from the ledger when it has an `accessToken`, and `RedactoNoticeConsent` plays sandbox audio from the ledger. The user service also keeps any path prefix of the host.
|
|
155
|
+
|
|
156
|
+
#### `ledgerBaseUrl` (deprecated)
|
|
157
|
+
|
|
158
|
+
Read only when `baseUrl` is the consent server URL (`…/consent`) or omitted, as in earlier releases. Ignored when `baseUrl` is the API host: the ledger is then always `{baseUrl}/consent-ledger`, and the SDK logs one console warning asking you to remove `ledgerBaseUrl`.
|
|
159
|
+
|
|
160
|
+
#### Local development
|
|
161
|
+
|
|
162
|
+
Put the services behind one local gateway, the way production does, and pass its address as `baseUrl`. For example, with [Caddy](https://caddyserver.com), the consent server on port 8000 and the ledger on port 8090:
|
|
163
|
+
|
|
164
|
+
```caddyfile
|
|
165
|
+
:8080 {
|
|
166
|
+
handle /consent-ledger/* {
|
|
167
|
+
reverse_proxy localhost:8090
|
|
168
|
+
}
|
|
169
|
+
handle /consent/* {
|
|
170
|
+
reverse_proxy localhost:8000
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Then pass `baseUrl="http://localhost:8080"`. Both services serve their own path prefix (`/consent/…`, `/consent-ledger/…`), so the gateway forwards paths unchanged. Any reverse proxy works the same way (nginx, Traefik, your docker-compose gateway).
|
|
176
|
+
|
|
177
|
+
#### When your backend reuses the value
|
|
178
|
+
|
|
179
|
+
Your backend's token calls go to the consent server. If your frontend sends its `baseUrl` to your backend (as `base_url` in [Backend Server Setup](#backend-server-setup)), the backend appends `/consent` to the host: `{host}/consent/public/organisations/{organisation_uuid}/workspaces/{workspace_uuid}/tokens/generate`. A backend that still receives the consent server URL keeps appending `/public/...` to it directly.
|
|
180
|
+
|
|
120
181
|
## Sandbox Mode (No Backend Required)
|
|
121
182
|
|
|
122
183
|
Sandbox mode lets you build and test a full consent PoC — **notice → Privacy Center → DSAR** — straight from your app, with **no backend and no token minting**. Instead of your server generating a JWT, you paste a **static sandbox token** into the SDK. Everything you create is recorded as test data, isolated from your live records.
|
|
@@ -138,6 +199,7 @@ Pass the sandbox `token`, org/workspace, and a test identity (`email`, `mobile`,
|
|
|
138
199
|
import { RedactoNoticeConsent } from "@redacto.io/consent-sdk-react";
|
|
139
200
|
|
|
140
201
|
<RedactoNoticeConsent
|
|
202
|
+
baseUrl="https://api.redacto.io"
|
|
141
203
|
noticeId={NOTICE_UUID}
|
|
142
204
|
token={SANDBOX_TOKEN}
|
|
143
205
|
organisationUuid={ORG_UUID}
|
|
@@ -156,11 +218,11 @@ Point the Privacy Center at the same sandbox token and test identity. The same t
|
|
|
156
218
|
import { RedactoPrivacyCenter } from "@redacto.io/consent-sdk-react/privacy-center";
|
|
157
219
|
|
|
158
220
|
<RedactoPrivacyCenter
|
|
221
|
+
baseUrl="https://api.redacto.io"
|
|
159
222
|
token={SANDBOX_TOKEN}
|
|
160
223
|
organisationUuid={ORG_UUID}
|
|
161
224
|
workspaceUuid={WORKSPACE_UUID}
|
|
162
225
|
email="test.user@example.com" // or `mobile`, or `ucic`
|
|
163
|
-
slug="acme-co"
|
|
164
226
|
onError={(error) => {
|
|
165
227
|
console.error(error);
|
|
166
228
|
return false;
|
|
@@ -183,7 +245,7 @@ import { RedactoPrivacyCenter } from "@redacto.io/consent-sdk-react/privacy-cent
|
|
|
183
245
|
|
|
184
246
|
- The SDK sends the sandbox token as an `X-Consent-Token` header and the test identity inside each request (query params on reads, body on writes) — no JWT decode, no token refresh (a `401` fails fast).
|
|
185
247
|
- The server records everything under `environment=test`, namespaced so sandbox data never mixes with — or reads — your live records.
|
|
186
|
-
- Sandbox
|
|
248
|
+
- Sandbox requests go to the same services as live ones (see [API host](#api-host-baseurl)), except that the notice read stays on the consent server.
|
|
187
249
|
|
|
188
250
|
### Going live
|
|
189
251
|
|
|
@@ -216,6 +278,7 @@ app.post(
|
|
|
216
278
|
async (req: Request, res: Response): Promise<void> => {
|
|
217
279
|
try {
|
|
218
280
|
const { email } = req.body;
|
|
281
|
+
// base_url is the API host the frontend passes as baseUrl, e.g. https://api.redacto.io
|
|
219
282
|
const { organisation_uuid, workspace_uuid, base_url } = req.query;
|
|
220
283
|
|
|
221
284
|
if (!CMS_API_KEY) {
|
|
@@ -224,7 +287,7 @@ app.post(
|
|
|
224
287
|
|
|
225
288
|
// Call Redacto API to generate access token
|
|
226
289
|
const response = await axios.post(
|
|
227
|
-
`${base_url}/public/organisations/${organisation_uuid}/workspaces/${workspace_uuid}/tokens/generate
|
|
290
|
+
`${base_url}/consent/public/organisations/${organisation_uuid}/workspaces/${workspace_uuid}/tokens/generate`,
|
|
228
291
|
{
|
|
229
292
|
email,
|
|
230
293
|
// expires_in_days: 1, // Optional: set token expiration
|
|
@@ -263,7 +326,7 @@ app.post(
|
|
|
263
326
|
|
|
264
327
|
// Call Redacto API to refresh access token
|
|
265
328
|
const response = await axios.post(
|
|
266
|
-
`${base_url}/consent/public/organisations/${organisation_uuid}/workspaces/${workspace_uuid}/tokens/refresh
|
|
329
|
+
`${base_url}/consent/public/organisations/${organisation_uuid}/workspaces/${workspace_uuid}/tokens/refresh`,
|
|
267
330
|
{ refresh_token: refresh_token },
|
|
268
331
|
{
|
|
269
332
|
headers: {
|
|
@@ -287,16 +350,16 @@ app.post(
|
|
|
287
350
|
|
|
288
351
|
#### Redacto API Endpoints Used
|
|
289
352
|
|
|
290
|
-
Your backend needs to call the following Redacto API endpoints:
|
|
353
|
+
Your backend needs to call the following Redacto API endpoints on the API host (e.g. `https://api.redacto.io`):
|
|
291
354
|
|
|
292
355
|
1. **Generate Access Token**
|
|
293
|
-
- **Endpoint**: `POST /public/organisations/{organisation_uuid}/workspaces/{workspace_uuid}/tokens/generate
|
|
356
|
+
- **Endpoint**: `POST /consent/public/organisations/{organisation_uuid}/workspaces/{workspace_uuid}/tokens/generate`
|
|
294
357
|
- **Headers**: `X-CMS-API-Key: {your-cms-api-key}`
|
|
295
358
|
- **Body**: `{ email, expires_in_days? }`
|
|
296
359
|
- **Response**: `{ detail: { token, refresh_token, expires_at } }`
|
|
297
360
|
|
|
298
361
|
2. **Refresh Access Token**
|
|
299
|
-
- **Endpoint**: `POST /consent/public/organisations/{organisation_uuid}/workspaces/{workspace_uuid}/tokens/refresh
|
|
362
|
+
- **Endpoint**: `POST /consent/public/organisations/{organisation_uuid}/workspaces/{workspace_uuid}/tokens/refresh`
|
|
300
363
|
- **Headers**: `X-CMS-API-Key: {your-cms-api-key}`
|
|
301
364
|
- **Body**: `{ refresh_token }`
|
|
302
365
|
- **Response**: `{ detail: { token, refresh_token, expires_at } }`
|
|
@@ -322,8 +385,8 @@ A modal popup component for consent collection. Displays a full-screen modal wit
|
|
|
322
385
|
| `noticeId` | `string` | Yes | - | UUID of the consent notice |
|
|
323
386
|
| `accessToken` | `string` | Yes | - | Authentication token for API calls |
|
|
324
387
|
| `refreshToken` | `string` | Yes | - | Refresh token for token renewal |
|
|
325
|
-
| `baseUrl` | `string` | No |
|
|
326
|
-
| `ledgerBaseUrl` | `string` | No | - |
|
|
388
|
+
| `baseUrl` | `string` | No | Production API | API host, e.g. `https://api.redacto.io` (see [API host](#api-host-baseurl)). The consent server URL (`…/consent`) still works but is deprecated |
|
|
389
|
+
| `ledgerBaseUrl` | `string` | No | - | **Deprecated.** Read only when `baseUrl` is the consent server URL (`…/consent`) or omitted, as in earlier releases. Ignored when `baseUrl` is the API host. There, it routes consent writes and prior-consent checks through the ledger (falls back: ledger → baseUrl → default) |
|
|
327
390
|
| `blockUI` | `boolean` | No | `true` | Whether to block UI interaction |
|
|
328
391
|
| `language` | `string` | No | `"en"` | Language code |
|
|
329
392
|
| `settings` | `object` | No | - | Optional code overrides; win over the console appearance key by key. See [Appearance](#notice-appearance) |
|
|
@@ -409,8 +472,8 @@ The component automatically submits consent when all of the following conditions
|
|
|
409
472
|
| `notice_uuid` | `string` | Yes | - | Notice UUID |
|
|
410
473
|
| `accessToken` | `string` | No | - | Authentication token |
|
|
411
474
|
| `refreshToken` | `string` | No | - | Refresh token |
|
|
412
|
-
| `baseUrl` | `string` | No |
|
|
413
|
-
| `ledgerBaseUrl` | `string` | No | - |
|
|
475
|
+
| `baseUrl` | `string` | No | Production API | API host, e.g. `https://api.redacto.io` (see [API host](#api-host-baseurl)). The consent server URL (`…/consent`) still works but is deprecated |
|
|
476
|
+
| `ledgerBaseUrl` | `string` | No | - | **Deprecated.** Read only when `baseUrl` is the consent server URL (`…/consent`) or omitted, as in earlier releases. Ignored when `baseUrl` is the API host. There, it routes consent writes and already-consented checks through the ledger |
|
|
414
477
|
| `language` | `string` | No | `"en"` | Language code |
|
|
415
478
|
| `settings` | `object` | No | - | Optional code overrides of the console appearance |
|
|
416
479
|
| `applicationId` | `string` | No | - | Application-specific UUID |
|
|
@@ -440,8 +503,8 @@ A modal, agent-assisted consent widget for collecting consent from an end-user w
|
|
|
440
503
|
| `organisationUuid` | `string` | Yes | - | Organisation UUID (path segment of the public notice endpoint) |
|
|
441
504
|
| `workspaceUuid` | `string` | Yes | - | Workspace UUID (path segment of the public notice endpoint) |
|
|
442
505
|
| `noticeUuid` | `string` | Yes | - | Notice UUID to fetch and render |
|
|
443
|
-
| `baseUrl` | `string` | No | Production
|
|
444
|
-
| `ledgerBaseUrl` | `string` | No | - |
|
|
506
|
+
| `baseUrl` | `string` | No | Production API | API host, e.g. `https://api.redacto.io` (see [API host](#api-host-baseurl)). The consent server URL (`…/consent`) still works but is deprecated |
|
|
507
|
+
| `ledgerBaseUrl` | `string` | No | - | **Deprecated.** Read only when `baseUrl` is the consent server URL (`…/consent`) or omitted, as in earlier releases. Ignored when `baseUrl` is the API host. There, it routes the consent write through the ledger (falls back: ledger → baseUrl → default); notice fetch, OTP, and TTS stay on the consent server |
|
|
445
508
|
| `onDecline` | `() => void` | No | Logs and resets the flow | Called when the agent/customer declines (host should unmount the modal) |
|
|
446
509
|
| `onComplete` | `() => void` | No | Logs and resets the flow | Called when the agent clicks "Proceed" on the confirmed screen |
|
|
447
510
|
|
|
@@ -478,6 +541,7 @@ function SignUpPage() {
|
|
|
478
541
|
|
|
479
542
|
try {
|
|
480
543
|
// Step 1: Get tokens from your backend
|
|
544
|
+
// BASE_URL is the Redacto API host, e.g. https://api.redacto.io
|
|
481
545
|
const response = await fetch(
|
|
482
546
|
`${BACKEND_URL}/api/consent/token?workspace_uuid=${WORKSPACE_UUID}&organisation_uuid=${ORG_UUID}&base_url=${BASE_URL}`,
|
|
483
547
|
{
|
|
@@ -715,7 +779,7 @@ import { RedactoNoticeAssisted } from "@redacto.io/consent-sdk-react";
|
|
|
715
779
|
const ORG_UUID = "your-organisation-uuid";
|
|
716
780
|
const WORKSPACE_UUID = "your-workspace-uuid";
|
|
717
781
|
const NOTICE_UUID = "your-notice-uuid";
|
|
718
|
-
const BASE_URL = "https://api.redacto.io
|
|
782
|
+
const BASE_URL = "https://api.redacto.io"; // The Redacto API host
|
|
719
783
|
|
|
720
784
|
export function AssistedConsent() {
|
|
721
785
|
const [open, setOpen] = useState(false);
|
|
@@ -960,7 +1024,6 @@ import { RedactoPrivacyCenter } from "@redacto.io/consent-sdk-react/privacy-cent
|
|
|
960
1024
|
|
|
961
1025
|
<RedactoPrivacyCenter
|
|
962
1026
|
baseUrl={BASE_URL}
|
|
963
|
-
slug="acme-co"
|
|
964
1027
|
accessToken={accessToken}
|
|
965
1028
|
refreshToken={refreshToken}
|
|
966
1029
|
settings={{ theme: "glass" }}
|
|
@@ -974,23 +1037,121 @@ import { RedactoPrivacyCenter } from "@redacto.io/consent-sdk-react/privacy-cent
|
|
|
974
1037
|
|
|
975
1038
|
**Props:**
|
|
976
1039
|
|
|
977
|
-
| Prop
|
|
978
|
-
|
|
|
979
|
-
| `baseUrl`
|
|
980
|
-
| `ledgerBaseUrl`
|
|
981
|
-
| `slug`
|
|
982
|
-
| `accessToken`
|
|
983
|
-
| `refreshToken`
|
|
984
|
-
| `
|
|
985
|
-
| `
|
|
986
|
-
| `
|
|
987
|
-
| `
|
|
1040
|
+
| Prop | Type | Required | Default | Description |
|
|
1041
|
+
| ----------------- | ------------------------------------------------- | -------- | ------------------- | ----------- |
|
|
1042
|
+
| `baseUrl` | `string` | Yes | - | API host, e.g. `https://api.redacto.io` (see [API host](#api-host-baseurl)). The consent server URL (`…/consent`) still works but is deprecated |
|
|
1043
|
+
| `ledgerBaseUrl` | `string` | No | - | **Deprecated.** Read only when `baseUrl` is the consent server URL (`…/consent`) or omitted, as in earlier releases. Ignored when `baseUrl` is the API host. There, it routes user consents, group purposes, receipts, and manage-consent through the ledger and overlays the DSAR form's product picker from ledger-served consents; omitted, the legacy `/dsar/privacy-center/*` routes are used |
|
|
1044
|
+
| `slug` | `string` | No | - | **Deprecated** and ignored; will be removed in a future major release. Passing it logs a one-time console warning. The organisation comes from the access token, or from `organisationUuid` and `workspaceUuid` |
|
|
1045
|
+
| `accessToken` | `string` | No | - | JWT access token. Without one the Privacy Center waits for it, or shows its built-in sign-in when `builtInSignIn` is on (see [Sign-in](#privacy-center-sign-in)) |
|
|
1046
|
+
| `refreshToken` | `string` | No | - | JWT refresh token |
|
|
1047
|
+
| `organisationUuid`| `string` | No | - | Organisation UUID for sign-in, settings and branding when there is no token; the built-in sign-in needs it with `workspaceUuid` (also the sandbox org) |
|
|
1048
|
+
| `workspaceUuid` | `string` | No | - | Workspace UUID for sign-in when there is no token; the built-in sign-in needs it with `organisationUuid` (also the sandbox workspace) |
|
|
1049
|
+
| `onError` | `(error: Error) => boolean` | Yes | - | Error handler; return `true` if handled |
|
|
1050
|
+
| `settings` | `PrivacyCenterSettings` | No | - | Overrides for the settings published from the console, merged field by field per group (see [Settings](#privacy-center-settings)). `theme` is kept as an alias for `branding.style` |
|
|
1051
|
+
| `builtInSignIn` | `boolean` | No | `false` | Show the built-in OTP sign-in when there is no token. Off, the Privacy Center keeps its loading state until the host passes a token |
|
|
1052
|
+
| `initialPage` | `"consent-manager" \| "form" \| "activity" \| "case-details" \| "notifications" \| "receipts"` | No | `"consent-manager"` | Page to render first; a section switched off in the settings falls back to the first enabled one |
|
|
1053
|
+
| `onBack` | `"back" \| "signout"` | No | - | Behaviour of the navbar back/signout control |
|
|
1054
|
+
| `onReady` | `(event: { signedIn: boolean }) => void` | No | - | Called once, when the first screen (sign-in or the Privacy Center) is on show |
|
|
1055
|
+
| `onConsentChange` | `(change: PrivacyCenterConsentChange) => void` | No | - | Called after a consent was withdrawn, re-granted or renewed |
|
|
1056
|
+
| `onRequestSubmit` | `(request: PrivacyCenterRequestSubmission) => void` | No | - | Called after a data request (case) was created |
|
|
1057
|
+
| `onSignOut` | `(event: { reason: "user" \| "timeout" \| "session-expired" }) => void` | No | - | Called when the visitor is signed out |
|
|
1058
|
+
|
|
1059
|
+
The callbacks never throw into the Privacy Center: an exception in a handler is logged and ignored. Their payloads:
|
|
1060
|
+
|
|
1061
|
+
```ts
|
|
1062
|
+
type PrivacyCenterConsentChange = {
|
|
1063
|
+
action: "revoke" | "regrant" | "renew";
|
|
1064
|
+
purposeUuid: string;
|
|
1065
|
+
productUuid: string | null;
|
|
1066
|
+
dataElementUuids: readonly string[]; // empty when the change covered the whole purpose
|
|
1067
|
+
nominatorUuid: string | null; // set when acting on behalf of a nominator
|
|
1068
|
+
};
|
|
1069
|
+
|
|
1070
|
+
type PrivacyCenterRequestSubmission = {
|
|
1071
|
+
caseId: string; // the case id shown to the visitor
|
|
1072
|
+
caseUuid: string;
|
|
1073
|
+
requestType: "access" | "correction" | "erasure" | "nomination" | "grievance";
|
|
1074
|
+
productUuid: string | null; // null for nomination and grievance
|
|
1075
|
+
};
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
<a id="privacy-center-settings"></a>
|
|
1079
|
+
|
|
1080
|
+
### Settings
|
|
1081
|
+
|
|
1082
|
+
The Privacy Center reads the settings an admin publishes in the Redacto console (Rights Management › Privacy Center › Settings) from the consent server's public endpoint `GET {consent server}/public/organisations/{organisation_uuid}/privacy-center-settings/?lang={language}` (`{baseUrl}/consent/public/...` with the API host). The organisation comes from `organisationUuid`, the access token, or the session. The settings are fetched again, localised, when the visitor changes language, and cached in memory per organisation and language.
|
|
1083
|
+
|
|
1084
|
+
Rendering never waits for them: the Privacy Center starts with the defaults and swaps the published values in when they arrive. **With nothing published, or if the request fails, it looks and behaves exactly as before settings existed.**
|
|
1085
|
+
|
|
1086
|
+
What the settings control:
|
|
1087
|
+
|
|
1088
|
+
- **Branding**: classic or glass style, the organisation's own colours, font, name and logo instead of the workspace branding, logo height and corner radius.
|
|
1089
|
+
- **Menu**: section order, labels, and which sections are on. A section switched off disappears from the menu and can't be opened; switching off `notifications` hides the bell too.
|
|
1090
|
+
- **Welcome message** at the top of the consent manager.
|
|
1091
|
+
- **Requests**: which request types the form offers, in what order and under what names (narrowed to the server's own list when it sends one), which grievance categories are offered, whether erasure also revokes consent, and the default withdraw warning (a purpose's own warning still wins).
|
|
1092
|
+
- **Languages**: which languages the switcher offers, whether it shows, the default language and whether the browser's language is detected. Once settings are published (or the host overrides them), Urdu, Sindhi and Kashmiri render right to left; with nothing published the text direction is inherited as before.
|
|
1093
|
+
- **Session timeout**: sign the visitor out after 15, 30 or 60 minutes without activity.
|
|
1094
|
+
- **Footer**: links, privacy policy, grievance contact, copyright and "Powered by".
|
|
1095
|
+
- **Sign-in screen**: layout, background, copy, channels and default country code (see below).
|
|
1096
|
+
- **Custom CSS** (see below).
|
|
1097
|
+
|
|
1098
|
+
Copy can use `{brand}`, `{legal}`, `{year}`, `{contact}` and `{name}`; `resolvePlaceholders(text, context)` is exported for the same substitution in your own UI.
|
|
1099
|
+
|
|
1100
|
+
Override any of it in code; each group merges field by field over what was published:
|
|
1101
|
+
|
|
1102
|
+
```tsx
|
|
1103
|
+
<RedactoPrivacyCenter
|
|
1104
|
+
baseUrl={BASE_URL}
|
|
1105
|
+
accessToken={accessToken}
|
|
1106
|
+
onError={() => false}
|
|
1107
|
+
settings={{
|
|
1108
|
+
branding: { style: "glass" },
|
|
1109
|
+
portal: { welcome_enabled: true, session_timeout_minutes: 30 },
|
|
1110
|
+
languages: { enabled: ["en", "hi"], default: "en" },
|
|
1111
|
+
}}
|
|
1112
|
+
/>
|
|
1113
|
+
```
|
|
1114
|
+
|
|
1115
|
+
The full shape is exported as `ResolvedPrivacyCenterSettings` (with `DEFAULT_PRIVACY_CENTER_SETTINGS`); `PrivacyCenterSettings` is the override type.
|
|
1116
|
+
|
|
1117
|
+
<a id="privacy-center-sign-in"></a>
|
|
1118
|
+
|
|
1119
|
+
### Sign-in
|
|
1120
|
+
|
|
1121
|
+
Pass `builtInSignIn` and, with no `accessToken`, the Privacy Center shows its own sign-in instead of a loader: the visitor enters a mobile number or email, receives a one-time code (resend after 50 seconds) and is signed straight in with the tokens it issues. Layout (card, split or minimal), background, copy, channels (SMS, email or both), default country code, notice line and support link come from the `sign_in` settings. It is off by default, so a host that passes its token a moment after mounting never flashes a sign-in screen.
|
|
1122
|
+
|
|
1123
|
+
```tsx
|
|
1124
|
+
<RedactoPrivacyCenter
|
|
1125
|
+
baseUrl={BASE_URL}
|
|
1126
|
+
organisationUuid={ORG_UUID}
|
|
1127
|
+
workspaceUuid={WORKSPACE_UUID}
|
|
1128
|
+
builtInSignIn
|
|
1129
|
+
onError={() => false}
|
|
1130
|
+
/>
|
|
1131
|
+
```
|
|
1132
|
+
|
|
1133
|
+
### Custom CSS
|
|
1134
|
+
|
|
1135
|
+
`custom_css` from the settings is injected inside an `@scope` for this Privacy Center only, after the same safety checks as a notice's custom CSS, so it never reaches the host page. Target these stable parts:
|
|
1136
|
+
|
|
1137
|
+
| Part | Element |
|
|
1138
|
+
| --- | --- |
|
|
1139
|
+
| `[data-redacto-part="topbar"]` | Top bar |
|
|
1140
|
+
| `[data-redacto-part="nav"]` / `"nav-item"` | Menu and each menu entry |
|
|
1141
|
+
| `[data-redacto-part="welcome"]` | Welcome message |
|
|
1142
|
+
| `[data-redacto-part="consent-card"]` | Consent manager card (and each product card on phones) |
|
|
1143
|
+
| `[data-redacto-part="request-form"]` | Data request form |
|
|
1144
|
+
| `[data-redacto-part="button-primary"]` | Primary buttons |
|
|
1145
|
+
| `[data-redacto-part="footer"]` | Footer |
|
|
1146
|
+
| `[data-redacto-part="signin-card"]` | Sign-in form |
|
|
1147
|
+
|
|
1148
|
+
The `--pc-*` theme variables are scoped the same way: workspace branding and the settings' colours apply to this Privacy Center (and its menus and tooltips) without writing to the host page's `<html>`.
|
|
988
1149
|
|
|
989
1150
|
<a id="privacy-center-appearance"></a>
|
|
990
1151
|
|
|
991
1152
|
### Theme
|
|
992
1153
|
|
|
993
|
-
`settings.theme` picks the visual style; colours, logo and brand name
|
|
1154
|
+
`settings.theme` (or `settings.branding.style`) picks the visual style; colours, logo and brand name come from the workspace's branding unless the published settings replace them.
|
|
994
1155
|
|
|
995
1156
|
- **`classic`** (default): the standard look.
|
|
996
1157
|
- **`glass`**: frosted, translucent surfaces over a soft wash of the brand colour, pill-shaped controls and motion. On phones, dialogs and the navigation menu rise as bottom sheets; on wider screens dialogs float as centred cards. Motion respects `prefers-reduced-motion`.
|
|
@@ -1016,11 +1177,11 @@ These rely on the same context providers (`AuthProvider`, `BaseUrlProvider`, `Na
|
|
|
1016
1177
|
|
|
1017
1178
|
For consumers who want their own UI, the headless core exposes the API client, orchestration helpers, hooks, and auth utilities:
|
|
1018
1179
|
|
|
1019
|
-
- **API client**: `createConsentClient` (returns a typed `BaseRequest`-aware fetcher)
|
|
1180
|
+
- **API client**: `createConsentClient(baseUrl, ledgerBaseUrl?)` (takes the API host, like the components; returns a typed `BaseRequest`-aware fetcher that picks the service per request. The consent server URL and `ledgerBaseUrl` still work as before, deprecated; `ledgerBaseUrl` is ignored with the API host)
|
|
1020
1181
|
- **Orchestration helpers** (handle auth retry / token refresh internally): `getFormData`, `createCaseRequest`, `fetchProducts`, `fetchUserConsents`, `manageConsent`, `fetchActivities`, `fetchCaseHistory`, `createOtp`, `verifyOtp`, `exchangeToken`
|
|
1021
1182
|
- **Auth utilities**: `decodeJwtToken`, `isJwtExpired`, `ensureValidToken`, `signOutPrivacyCenter`, `getCookieValue`, `setRefreshTokenCookie`, `checkAndRefreshAuth`, `compareEmails`, `isUserAuthenticated`, `getInitialState`, `getActivityTypeStatus`, `formatActivityType`
|
|
1022
1183
|
- **React Query hook**: `useCaseHistory`
|
|
1023
|
-
- **Context providers**: `AuthProvider` + `useAuth`, `BaseUrlProvider` + `useBaseUrl`, `NavigationProvider` + `useInternalNavigation`
|
|
1184
|
+
- **Context providers**: `AuthProvider` + `useAuth`, `BaseUrlProvider` + `useBaseUrl` (`BaseUrlProvider` takes the API host as `baseUrl`; `useBaseUrl()` returns the consent server URL as `baseUrl`, the ledger URL as `ledgerBaseUrl`, the host as `apiHost` and the client), `NavigationProvider` + `useInternalNavigation`
|
|
1024
1185
|
- **Types**: `Workspace`, `OrganizationDetail`, `DataElement`, `Purpose`, `UserConsent`, `Activity`, `CaseHistoryDetail`, `RequestTypeEnum`, `GrievanceTypeEnum`, `ConsentStatusEnum`, and more (see `privacy-center.ts` for the full export list)
|
|
1025
1186
|
|
|
1026
1187
|
### Style Injection
|
|
@@ -1073,7 +1234,7 @@ injectPCStyles();
|
|
|
1073
1234
|
- Verify authentication setup is correct for your environment
|
|
1074
1235
|
- Check that API key generation is working
|
|
1075
1236
|
- Ensure organisation and workspace UUIDs are correct
|
|
1076
|
-
- Verify
|
|
1237
|
+
- Verify `base_url` is your environment's API host (staging vs production) and that your backend calls the consent server under it (`{host}/consent/...`)
|
|
1077
1238
|
|
|
1078
1239
|
### Validation not working
|
|
1079
1240
|
|
|
@@ -1085,11 +1246,11 @@ injectPCStyles();
|
|
|
1085
1246
|
|
|
1086
1247
|
### Redacto CMP API Endpoints
|
|
1087
1248
|
|
|
1088
|
-
The SDK communicates with Redacto's CMP API. Key endpoints used:
|
|
1249
|
+
The SDK communicates with Redacto's CMP API on the API host (see [API host](#api-host-baseurl)). Key endpoints used:
|
|
1089
1250
|
|
|
1090
|
-
- **Get Consent Notice
|
|
1091
|
-
- **Submit Consent**: `POST /public/organisations/{org_uuid}/workspaces/{workspace_uuid}/submit-consent`
|
|
1092
|
-
- **Generate Access Token
|
|
1251
|
+
- **Get Consent Notice** (JWT session): `GET {baseUrl}/consent-ledger/public/organisations/{org_uuid}/workspaces/{workspace_uuid}/notices/{notice_uuid}`
|
|
1252
|
+
- **Submit Consent**: `POST {baseUrl}/consent-ledger/public/organisations/{org_uuid}/workspaces/{workspace_uuid}/submit-consent`
|
|
1253
|
+
- **Generate Access Token** (your backend): `POST {baseUrl}/consent/public/organisations/{org_uuid}/workspaces/{workspace_uuid}/tokens/generate`
|
|
1093
1254
|
|
|
1094
1255
|
For complete API documentation, visit: [Redacto API Documentation](https://staging.api.redacto.io/consent/docs)
|
|
1095
1256
|
|
|
@@ -218,6 +218,8 @@ var NOTICE_DISALLOWED_CSS_TOKENS = [
|
|
|
218
218
|
"expression(",
|
|
219
219
|
"behavior:"
|
|
220
220
|
];
|
|
221
|
+
var CSS_SCOPE_AT_RULE = "@scope";
|
|
222
|
+
var CSS_STRING_SPECIALS_RE = /["\\\n]/g;
|
|
221
223
|
var CSS_ESCAPE_RE = /\\([0-9a-f]{1,6})(?:\r\n|[ \t\r\n\f])?|\\([\s\S])/gi;
|
|
222
224
|
var CSS_BRACKET_PAIRS = {
|
|
223
225
|
"{": "}",
|
|
@@ -255,6 +257,63 @@ var buildTtsCssVariables = (colors) => Object.fromEntries(
|
|
|
255
257
|
);
|
|
256
258
|
var buildTtsRootAttributes = (colors) => colors.tts_highlight_text ? { [TTS_TEXT_ATTRIBUTE]: "" } : {};
|
|
257
259
|
|
|
260
|
+
// src/shared/appearance/customCss.ts
|
|
261
|
+
var decodeCssEscapes = (value) => value.replace(
|
|
262
|
+
CSS_ESCAPE_RE,
|
|
263
|
+
(_match, hex, literal) => {
|
|
264
|
+
if (hex === void 0) {
|
|
265
|
+
return literal;
|
|
266
|
+
}
|
|
267
|
+
const codePoint = Number.parseInt(hex, HEX_RADIX);
|
|
268
|
+
if (codePoint === 0 || codePoint > MAX_CODE_POINT || codePoint >= SURROGATE_MIN && codePoint <= SURROGATE_MAX) {
|
|
269
|
+
return CSS_REPLACEMENT_CHARACTER;
|
|
270
|
+
}
|
|
271
|
+
return String.fromCodePoint(codePoint);
|
|
272
|
+
}
|
|
273
|
+
);
|
|
274
|
+
var hasBalancedBlocks = (css) => {
|
|
275
|
+
const open = [];
|
|
276
|
+
let quote;
|
|
277
|
+
let inComment = false;
|
|
278
|
+
for (let index = 0; index < css.length; index += 1) {
|
|
279
|
+
const char = css[index];
|
|
280
|
+
if (inComment) {
|
|
281
|
+
if (char === "*" && css[index + 1] === "/") {
|
|
282
|
+
inComment = false;
|
|
283
|
+
index += 1;
|
|
284
|
+
}
|
|
285
|
+
} else if (char === "\\") {
|
|
286
|
+
index += 1;
|
|
287
|
+
} else if (quote) {
|
|
288
|
+
if (char === quote) {
|
|
289
|
+
quote = void 0;
|
|
290
|
+
} else if (CSS_NEWLINES.includes(char)) {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
} else if (char === "/" && css[index + 1] === "*") {
|
|
294
|
+
inComment = true;
|
|
295
|
+
index += 1;
|
|
296
|
+
} else if (CSS_QUOTES.includes(char)) {
|
|
297
|
+
quote = char;
|
|
298
|
+
} else if (char in CSS_BRACKET_PAIRS) {
|
|
299
|
+
open.push(CSS_BRACKET_PAIRS[char]);
|
|
300
|
+
} else if (CSS_CLOSING_BRACKETS.includes(char) && open.pop() !== char) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return open.length === 0 && !quote && !inComment;
|
|
305
|
+
};
|
|
306
|
+
var isSafeCustomCss = (css) => {
|
|
307
|
+
if (css.length > NOTICE_CUSTOM_CSS_MAX_LENGTH || !hasBalancedBlocks(css)) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
const lowered = css.toLowerCase();
|
|
311
|
+
const decoded = decodeCssEscapes(css).toLowerCase();
|
|
312
|
+
return NOTICE_DISALLOWED_CSS_TOKENS.every(
|
|
313
|
+
(token) => !lowered.includes(token) && !decoded.includes(token)
|
|
314
|
+
);
|
|
315
|
+
};
|
|
316
|
+
|
|
258
317
|
// src/shared/appearance/registry.ts
|
|
259
318
|
var DEFAULT_APPEARANCE = "classic";
|
|
260
319
|
var APPEARANCE_ATTRIBUTE = "data-redacto-appearance";
|
|
@@ -325,63 +384,6 @@ var prefersReducedMotion = () => {
|
|
|
325
384
|
return typeof window !== "undefined" && ((_b = (_a = window.matchMedia) == null ? void 0 : _a.call(window, "(prefers-reduced-motion: reduce)")) == null ? void 0 : _b.matches) === true;
|
|
326
385
|
};
|
|
327
386
|
|
|
328
|
-
// src/shared/appearance/customCss.ts
|
|
329
|
-
var decodeCssEscapes = (value) => value.replace(
|
|
330
|
-
CSS_ESCAPE_RE,
|
|
331
|
-
(_match, hex, literal) => {
|
|
332
|
-
if (hex === void 0) {
|
|
333
|
-
return literal;
|
|
334
|
-
}
|
|
335
|
-
const codePoint = Number.parseInt(hex, HEX_RADIX);
|
|
336
|
-
if (codePoint === 0 || codePoint > MAX_CODE_POINT || codePoint >= SURROGATE_MIN && codePoint <= SURROGATE_MAX) {
|
|
337
|
-
return CSS_REPLACEMENT_CHARACTER;
|
|
338
|
-
}
|
|
339
|
-
return String.fromCodePoint(codePoint);
|
|
340
|
-
}
|
|
341
|
-
);
|
|
342
|
-
var hasBalancedBlocks = (css) => {
|
|
343
|
-
const open = [];
|
|
344
|
-
let quote;
|
|
345
|
-
let inComment = false;
|
|
346
|
-
for (let index = 0; index < css.length; index += 1) {
|
|
347
|
-
const char = css[index];
|
|
348
|
-
if (inComment) {
|
|
349
|
-
if (char === "*" && css[index + 1] === "/") {
|
|
350
|
-
inComment = false;
|
|
351
|
-
index += 1;
|
|
352
|
-
}
|
|
353
|
-
} else if (char === "\\") {
|
|
354
|
-
index += 1;
|
|
355
|
-
} else if (quote) {
|
|
356
|
-
if (char === quote) {
|
|
357
|
-
quote = void 0;
|
|
358
|
-
} else if (CSS_NEWLINES.includes(char)) {
|
|
359
|
-
return false;
|
|
360
|
-
}
|
|
361
|
-
} else if (char === "/" && css[index + 1] === "*") {
|
|
362
|
-
inComment = true;
|
|
363
|
-
index += 1;
|
|
364
|
-
} else if (CSS_QUOTES.includes(char)) {
|
|
365
|
-
quote = char;
|
|
366
|
-
} else if (char in CSS_BRACKET_PAIRS) {
|
|
367
|
-
open.push(CSS_BRACKET_PAIRS[char]);
|
|
368
|
-
} else if (CSS_CLOSING_BRACKETS.includes(char) && open.pop() !== char) {
|
|
369
|
-
return false;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
return open.length === 0 && !quote && !inComment;
|
|
373
|
-
};
|
|
374
|
-
var isSafeCustomCss = (css) => {
|
|
375
|
-
if (css.length > NOTICE_CUSTOM_CSS_MAX_LENGTH || !hasBalancedBlocks(css)) {
|
|
376
|
-
return false;
|
|
377
|
-
}
|
|
378
|
-
const lowered = css.toLowerCase();
|
|
379
|
-
const decoded = decodeCssEscapes(css).toLowerCase();
|
|
380
|
-
return NOTICE_DISALLOWED_CSS_TOKENS.every(
|
|
381
|
-
(token) => !lowered.includes(token) && !decoded.includes(token)
|
|
382
|
-
);
|
|
383
|
-
};
|
|
384
|
-
|
|
385
387
|
// src/shared/appearance/resolve.ts
|
|
386
388
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
387
389
|
var pickColors = (raw) => {
|
|
@@ -680,6 +682,66 @@ var useMediaQuery = (query) => {
|
|
|
680
682
|
return matches;
|
|
681
683
|
};
|
|
682
684
|
|
|
685
|
+
// src/shared/endpoints.ts
|
|
686
|
+
var CONSENT_PATH = "/consent";
|
|
687
|
+
var LEDGER_PATH = "/consent-ledger";
|
|
688
|
+
var ABSOLUTE_HTTP_URL = /^https?:\/\/(?:\[[0-9a-f:.]+\]|[^\s/?#@:[\]]+)(?::\d+)?(?:\/[^\s?#]*)?$/i;
|
|
689
|
+
var trimUrl = (value) => (value != null ? value : "").trim().replace(/\/+$/, "");
|
|
690
|
+
var isConsentServerUrl = (trimmed) => ABSOLUTE_HTTP_URL.test(trimmed) && trimmed.endsWith(CONSENT_PATH);
|
|
691
|
+
var isHostForm = (trimmed) => ABSOLUTE_HTTP_URL.test(trimmed) && !trimmed.endsWith(CONSENT_PATH);
|
|
692
|
+
function resolveEndpoints(baseUrl, ledgerBaseUrl) {
|
|
693
|
+
const trimmed = trimUrl(baseUrl);
|
|
694
|
+
if (!isHostForm(trimmed)) {
|
|
695
|
+
return { mode: "legacy", apiHost: void 0, baseUrl, ledgerBaseUrl };
|
|
696
|
+
}
|
|
697
|
+
const apiHost = trimmed.endsWith(LEDGER_PATH) ? trimmed.slice(0, -LEDGER_PATH.length) : trimmed;
|
|
698
|
+
return {
|
|
699
|
+
mode: "host",
|
|
700
|
+
apiHost,
|
|
701
|
+
baseUrl: `${apiHost}${CONSENT_PATH}`,
|
|
702
|
+
ledgerBaseUrl: `${apiHost}${LEDGER_PATH}`
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
var ENDPOINT_WARNINGS = {
|
|
706
|
+
missingBaseUrl: "[Redacto] baseUrl is not set, so the SDK's built-in production default is used. Pass baseUrl: your API host.",
|
|
707
|
+
consentServerBaseUrl: "[Redacto] baseUrl is the consent server URL (ending in /consent), which is deprecated. Pass the API host instead; the SDK derives the consent server and ledger URLs from it.",
|
|
708
|
+
ledgerBaseUrl: "[Redacto] ledgerBaseUrl is deprecated. Pass the Redacto API host as baseUrl instead; the SDK then reaches the consent server and the ledger under it.",
|
|
709
|
+
ledgerBaseUrlIgnored: "[Redacto] ledgerBaseUrl is ignored because baseUrl is the API host; the SDK reaches the ledger under that host. Remove ledgerBaseUrl.",
|
|
710
|
+
slugIgnored: "[Redacto] slug is deprecated and ignored. The Privacy Center reads the organisation from the access token, or from organisationUuid and workspaceUuid. Remove slug."
|
|
711
|
+
};
|
|
712
|
+
var issuedWarnings = /* @__PURE__ */ new Set();
|
|
713
|
+
var warnOnce = (warning) => {
|
|
714
|
+
if (issuedWarnings.has(warning)) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
issuedWarnings.add(warning);
|
|
718
|
+
console.warn(ENDPOINT_WARNINGS[warning]);
|
|
719
|
+
};
|
|
720
|
+
var warnEndpointUsage = (baseUrl, ledgerBaseUrl, { hasBuiltInDefault }) => {
|
|
721
|
+
if (!baseUrl) {
|
|
722
|
+
if (hasBuiltInDefault) {
|
|
723
|
+
warnOnce("missingBaseUrl");
|
|
724
|
+
}
|
|
725
|
+
} else if (isConsentServerUrl(trimUrl(baseUrl))) {
|
|
726
|
+
warnOnce("consentServerBaseUrl");
|
|
727
|
+
}
|
|
728
|
+
if (trimUrl(ledgerBaseUrl)) {
|
|
729
|
+
warnOnce(isHostForm(trimUrl(baseUrl)) ? "ledgerBaseUrlIgnored" : "ledgerBaseUrl");
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
var warnSlugDeprecated = () => {
|
|
733
|
+
warnOnce("slugIgnored");
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
// src/shared/useResolvedEndpoints.ts
|
|
737
|
+
import { useMemo } from "react";
|
|
738
|
+
var useResolvedEndpoints = (baseUrl, ledgerBaseUrl, { warn = true, hasBuiltInDefault = true } = {}) => useMemo(() => {
|
|
739
|
+
if (warn) {
|
|
740
|
+
warnEndpointUsage(baseUrl, ledgerBaseUrl, { hasBuiltInDefault });
|
|
741
|
+
}
|
|
742
|
+
return resolveEndpoints(baseUrl, ledgerBaseUrl);
|
|
743
|
+
}, [baseUrl, ledgerBaseUrl, warn, hasBuiltInDefault]);
|
|
744
|
+
|
|
683
745
|
// src/shared/sandbox.ts
|
|
684
746
|
var SANDBOX_TOKEN_HEADER = "X-Consent-Token";
|
|
685
747
|
|
|
@@ -687,6 +749,36 @@ var SANDBOX_TOKEN_HEADER = "X-Consent-Token";
|
|
|
687
749
|
var DEFAULT_CONSENT_API_BASE_URL = "https://api.redacto.io/consent";
|
|
688
750
|
var USER_SERVICE_PATH = "/user/api/0";
|
|
689
751
|
|
|
752
|
+
// src/shared/appearance/scopedCss.ts
|
|
753
|
+
var escapesScope = (style) => {
|
|
754
|
+
var _a, _b;
|
|
755
|
+
return Array.from((_b = (_a = style.sheet) == null ? void 0 : _a.cssRules) != null ? _b : []).some(
|
|
756
|
+
(rule) => !rule.cssText.startsWith(CSS_SCOPE_AT_RULE)
|
|
757
|
+
);
|
|
758
|
+
};
|
|
759
|
+
var applyScopedCustomCss = ({
|
|
760
|
+
scopeAttribute,
|
|
761
|
+
scopeId,
|
|
762
|
+
markerAttribute,
|
|
763
|
+
css
|
|
764
|
+
}) => {
|
|
765
|
+
if (typeof document === "undefined" || !css.trim() || !isSafeCustomCss(css)) {
|
|
766
|
+
return () => void 0;
|
|
767
|
+
}
|
|
768
|
+
const id = scopeId.replace(CSS_STRING_SPECIALS_RE, "\\$&");
|
|
769
|
+
const style = document.createElement("style");
|
|
770
|
+
style.setAttribute(markerAttribute, scopeId);
|
|
771
|
+
style.textContent = `${CSS_SCOPE_AT_RULE} ([${scopeAttribute}="${id}"]) {
|
|
772
|
+
${css}
|
|
773
|
+
}`;
|
|
774
|
+
document.head.appendChild(style);
|
|
775
|
+
if (escapesScope(style)) {
|
|
776
|
+
style.remove();
|
|
777
|
+
return () => void 0;
|
|
778
|
+
}
|
|
779
|
+
return () => style.remove();
|
|
780
|
+
};
|
|
781
|
+
|
|
690
782
|
export {
|
|
691
783
|
SANDBOX_TOKEN_HEADER,
|
|
692
784
|
withIdempotencyKey,
|
|
@@ -724,5 +816,10 @@ export {
|
|
|
724
816
|
Collapse,
|
|
725
817
|
configureNoticeFonts,
|
|
726
818
|
loadNoticeWebfont,
|
|
727
|
-
useMediaQuery
|
|
819
|
+
useMediaQuery,
|
|
820
|
+
applyScopedCustomCss,
|
|
821
|
+
resolveEndpoints,
|
|
822
|
+
warnEndpointUsage,
|
|
823
|
+
warnSlugDeprecated,
|
|
824
|
+
useResolvedEndpoints
|
|
728
825
|
};
|