@redacto.io/consent-sdk-js 1.0.0-beta.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/redacto.d.mts +33 -3
- package/dist/redacto.d.ts +33 -3
- package/dist/redacto.global.js +2 -2
- package/dist/redacto.global.js.map +1 -1
- package/dist/redacto.js +320 -91
- package/dist/redacto.mjs +320 -91
- package/package.json +1 -1
package/dist/redacto.mjs
CHANGED
|
@@ -152,17 +152,6 @@ function createMediaQuery(query) {
|
|
|
152
152
|
};
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
// src/core/jwt-decode.ts
|
|
156
|
-
import { jwtDecode } from "jwt-decode";
|
|
157
|
-
function decodeTokenSafely(token) {
|
|
158
|
-
try {
|
|
159
|
-
return jwtDecode(token);
|
|
160
|
-
} catch (error) {
|
|
161
|
-
console.warn("Failed to decode JWT token:", error);
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
155
|
// src/services/cache.ts
|
|
167
156
|
var apiCache = /* @__PURE__ */ new Map();
|
|
168
157
|
var CACHE_TTL = 5 * 60 * 1e3;
|
|
@@ -183,6 +172,133 @@ function clearApiCache() {
|
|
|
183
172
|
apiCache.clear();
|
|
184
173
|
}
|
|
185
174
|
|
|
175
|
+
// src/core/jwt-decode.ts
|
|
176
|
+
import { jwtDecode } from "jwt-decode";
|
|
177
|
+
function decodeTokenSafely(token) {
|
|
178
|
+
try {
|
|
179
|
+
return jwtDecode(token);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.warn("Failed to decode JWT token:", error);
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/api/sandbox.ts
|
|
187
|
+
var SANDBOX_TOKEN_HEADER = "X-Consent-Token";
|
|
188
|
+
var resolveSandboxAuth = (options) => {
|
|
189
|
+
var _a, _b, _c, _d, _e;
|
|
190
|
+
const token = (_a = options.token) == null ? void 0 : _a.trim();
|
|
191
|
+
if (!token) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
const organisationUuid = (_b = options.organisationUuid) == null ? void 0 : _b.trim();
|
|
195
|
+
const workspaceUuid = (_c = options.workspaceUuid) == null ? void 0 : _c.trim();
|
|
196
|
+
const ucic = (_d = options.sandboxUcic) == null ? void 0 : _d.trim();
|
|
197
|
+
const subject = (_e = options.sandboxSubject) == null ? void 0 : _e.trim();
|
|
198
|
+
if (!organisationUuid || !workspaceUuid) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
"Sandbox mode requires organisationUuid and workspaceUuid"
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
if (!ucic && !subject) {
|
|
204
|
+
throw new Error("Sandbox mode requires a UCIC, email, or mobile");
|
|
205
|
+
}
|
|
206
|
+
return { token, ucic, subject: subject != null ? subject : "", organisationUuid, workspaceUuid };
|
|
207
|
+
};
|
|
208
|
+
var resolveRequestAuth = (accessToken, options) => {
|
|
209
|
+
const sandbox = resolveSandboxAuth(options);
|
|
210
|
+
if (sandbox) {
|
|
211
|
+
return {
|
|
212
|
+
organisationUuid: sandbox.organisationUuid,
|
|
213
|
+
workspaceUuid: sandbox.workspaceUuid,
|
|
214
|
+
sandbox
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
const decodedToken = accessToken ? decodeTokenSafely(accessToken) : null;
|
|
218
|
+
if (!decodedToken) {
|
|
219
|
+
throw new Error("Invalid access token");
|
|
220
|
+
}
|
|
221
|
+
const { organisation_uuid, workspace_uuid } = decodedToken;
|
|
222
|
+
if (!organisation_uuid || !workspace_uuid) {
|
|
223
|
+
throw new Error("Invalid token: missing organization or workspace UUID");
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
organisationUuid: organisation_uuid,
|
|
227
|
+
workspaceUuid: workspace_uuid,
|
|
228
|
+
sandbox: null
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
var buildAuthHeaders = (accessToken, sandbox) => {
|
|
232
|
+
if (sandbox) {
|
|
233
|
+
return { [SANDBOX_TOKEN_HEADER]: sandbox.token };
|
|
234
|
+
}
|
|
235
|
+
return { Authorization: `Bearer ${accessToken}` };
|
|
236
|
+
};
|
|
237
|
+
var sandboxBodyIdentity = (sandbox) => {
|
|
238
|
+
if (sandbox.ucic) {
|
|
239
|
+
return { org_user_id: sandbox.ucic };
|
|
240
|
+
}
|
|
241
|
+
return sandbox.subject.includes("@") ? { primary_email: sandbox.subject } : { primary_mobile: sandbox.subject };
|
|
242
|
+
};
|
|
243
|
+
var sandboxQueryIdentity = (sandbox) => sandboxBodyIdentity(sandbox);
|
|
244
|
+
|
|
245
|
+
// src/api/constants.ts
|
|
246
|
+
var CODE_BY_NAME = /* @__PURE__ */ new Map([
|
|
247
|
+
["english", "en"],
|
|
248
|
+
["assamese", "as"],
|
|
249
|
+
["bengali", "bn"],
|
|
250
|
+
["bodo", "brx"],
|
|
251
|
+
["dogri", "doi"],
|
|
252
|
+
["goan konkani", "gom"],
|
|
253
|
+
["gujarati", "gu"],
|
|
254
|
+
["hindi", "hi"],
|
|
255
|
+
["kannada", "kn"],
|
|
256
|
+
["kashmiri", "ks"],
|
|
257
|
+
["maithili", "mai"],
|
|
258
|
+
["malayalam", "ml"],
|
|
259
|
+
["manipuri", "mni-Mtei"],
|
|
260
|
+
["marathi", "mr"],
|
|
261
|
+
["nepali", "ne"],
|
|
262
|
+
["odia", "or"],
|
|
263
|
+
["punjabi", "pa"],
|
|
264
|
+
["sanskrit", "sa"],
|
|
265
|
+
["santali", "sat"],
|
|
266
|
+
["sindhi", "sd"],
|
|
267
|
+
["tamil", "ta"],
|
|
268
|
+
["telugu", "te"],
|
|
269
|
+
["urdu", "ur"]
|
|
270
|
+
]);
|
|
271
|
+
var CODE_BY_LOWER = new Map(
|
|
272
|
+
[...CODE_BY_NAME.values()].map((c) => [c.toLowerCase(), c])
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
// src/api/language.ts
|
|
276
|
+
function toLanguageCode(lang) {
|
|
277
|
+
var _a, _b;
|
|
278
|
+
if (!lang) {
|
|
279
|
+
return void 0;
|
|
280
|
+
}
|
|
281
|
+
const trimmed = lang.trim();
|
|
282
|
+
if (!trimmed) {
|
|
283
|
+
return void 0;
|
|
284
|
+
}
|
|
285
|
+
const lower = trimmed.toLowerCase();
|
|
286
|
+
return (_b = (_a = CODE_BY_LOWER.get(lower)) != null ? _a : CODE_BY_NAME.get(lower)) != null ? _b : trimmed;
|
|
287
|
+
}
|
|
288
|
+
function resolveDisplayLanguage(requested, defaultLanguage, translations) {
|
|
289
|
+
const fallback = defaultLanguage || "en";
|
|
290
|
+
const requestedCode = toLanguageCode(requested);
|
|
291
|
+
if (!requestedCode) {
|
|
292
|
+
return fallback;
|
|
293
|
+
}
|
|
294
|
+
for (const key of Object.keys(translations || {})) {
|
|
295
|
+
if (toLanguageCode(key) === requestedCode) {
|
|
296
|
+
return key;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return fallback;
|
|
300
|
+
}
|
|
301
|
+
|
|
186
302
|
// src/api/index.ts
|
|
187
303
|
var BASE_URL = "https://api.redacto.io/consent";
|
|
188
304
|
var BASE_URL_PUBLIC = "https://api.redacto.tech/consent";
|
|
@@ -194,17 +310,22 @@ var fetchConsentContent = async ({
|
|
|
194
310
|
specific_uuid,
|
|
195
311
|
validate_against = "all",
|
|
196
312
|
include_fully_consented_data = false,
|
|
197
|
-
signal
|
|
313
|
+
signal,
|
|
314
|
+
...sandboxParams
|
|
198
315
|
}) => {
|
|
199
|
-
if (!noticeId || !
|
|
200
|
-
throw new Error("noticeId
|
|
316
|
+
if (!noticeId || !validate_against) {
|
|
317
|
+
throw new Error("noticeId is required");
|
|
201
318
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
319
|
+
if (!accessToken && !sandboxParams.token) {
|
|
320
|
+
throw new Error("accessToken or token is required");
|
|
321
|
+
}
|
|
322
|
+
const { organisationUuid: ORG, workspaceUuid: WS, sandbox } = resolveRequestAuth(
|
|
323
|
+
accessToken,
|
|
324
|
+
sandboxParams
|
|
325
|
+
);
|
|
206
326
|
const apiBaseUrl = baseUrl || BASE_URL;
|
|
207
|
-
const
|
|
327
|
+
const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.subject}` : accessToken;
|
|
328
|
+
const cacheKey = `${cacheIdentity}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
|
|
208
329
|
if (validate_against === "all" && !specific_uuid) {
|
|
209
330
|
const cached = getCachedData(cacheKey);
|
|
210
331
|
if (cached) return cached;
|
|
@@ -213,10 +334,15 @@ var fetchConsentContent = async ({
|
|
|
213
334
|
if (specific_uuid) url.searchParams.append("specific_uuid", specific_uuid);
|
|
214
335
|
url.searchParams.append("validate_against", validate_against);
|
|
215
336
|
if (include_fully_consented_data) url.searchParams.append("include_fully_consented_data", "true");
|
|
337
|
+
if (sandbox) {
|
|
338
|
+
for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
|
|
339
|
+
url.searchParams.append(key, value);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
216
342
|
const response = await fetch(url.toString(), {
|
|
217
343
|
method: "GET",
|
|
218
344
|
headers: {
|
|
219
|
-
|
|
345
|
+
...buildAuthHeaders(accessToken, sandbox),
|
|
220
346
|
"Accept-Language": language
|
|
221
347
|
},
|
|
222
348
|
signal
|
|
@@ -242,18 +368,24 @@ var fetchConsentContentPublic = async ({
|
|
|
242
368
|
org_uuid,
|
|
243
369
|
workspace_uuid,
|
|
244
370
|
notice_uuid,
|
|
371
|
+
accessToken,
|
|
245
372
|
baseUrl,
|
|
246
373
|
language = "en",
|
|
247
374
|
specific_uuid
|
|
248
375
|
}) => {
|
|
249
376
|
const apiBaseUrl = baseUrl || BASE_URL_PUBLIC;
|
|
377
|
+
const noticesUrl = `${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/notices`;
|
|
250
378
|
const url = new URL(
|
|
251
|
-
`${
|
|
379
|
+
accessToken ? `${noticesUrl}/${notice_uuid}` : `${noticesUrl}/get-notice/${notice_uuid}`
|
|
252
380
|
);
|
|
253
381
|
if (specific_uuid) url.searchParams.append("specific_uuid", specific_uuid);
|
|
254
382
|
const response = await fetch(url.toString(), {
|
|
255
383
|
method: "GET",
|
|
256
|
-
headers: {
|
|
384
|
+
headers: {
|
|
385
|
+
"Content-Type": "application/json",
|
|
386
|
+
"Accept-Language": language,
|
|
387
|
+
...accessToken ? { Authorization: `Bearer ${accessToken}` } : {}
|
|
388
|
+
}
|
|
257
389
|
});
|
|
258
390
|
if (response.status === 401) {
|
|
259
391
|
const error = new Error("Unauthorized");
|
|
@@ -280,16 +412,21 @@ var submitConsentEvent = async ({
|
|
|
280
412
|
meta_data,
|
|
281
413
|
guardianVerificationReference,
|
|
282
414
|
selfDeclaredAdult,
|
|
283
|
-
|
|
415
|
+
language,
|
|
416
|
+
signal,
|
|
417
|
+
...sandboxParams
|
|
284
418
|
}) => {
|
|
285
|
-
if (!noticeUuid || !
|
|
286
|
-
throw new Error("noticeUuid
|
|
419
|
+
if (!noticeUuid || !Array.isArray(purposes)) {
|
|
420
|
+
throw new Error("noticeUuid and purposes array are required");
|
|
287
421
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
422
|
+
if (!accessToken && !sandboxParams.token) {
|
|
423
|
+
throw new Error("accessToken or token is required");
|
|
424
|
+
}
|
|
425
|
+
const { organisationUuid: ORG, workspaceUuid: WS, sandbox } = resolveRequestAuth(
|
|
426
|
+
accessToken,
|
|
427
|
+
sandboxParams
|
|
428
|
+
);
|
|
429
|
+
const apiBaseUrl = (sandbox ? null : ledgerBaseUrl) || baseUrl || BASE_URL;
|
|
293
430
|
const validatedPurposes = purposes.map((purpose) => {
|
|
294
431
|
if (!purpose.uuid) throw new Error("Invalid purpose data: missing uuid");
|
|
295
432
|
const dataElements = (purpose.data_elements || []).filter((element) => element.enabled).map((element) => ({
|
|
@@ -310,17 +447,19 @@ var submitConsentEvent = async ({
|
|
|
310
447
|
declined: Boolean(declined),
|
|
311
448
|
meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid, minor_age: meta_data.minor_age } : void 0,
|
|
312
449
|
guardian_verification_reference: guardianVerificationReference,
|
|
313
|
-
self_declared_adult: selfDeclaredAdult
|
|
450
|
+
self_declared_adult: selfDeclaredAdult,
|
|
451
|
+
language: toLanguageCode(language)
|
|
314
452
|
};
|
|
453
|
+
const requestBody = sandbox ? { ...payload, ...sandboxBodyIdentity(sandbox) } : payload;
|
|
315
454
|
const response = await fetch(
|
|
316
455
|
`${apiBaseUrl}/public/organisations/${ORG}/workspaces/${WS}/submit-consent`,
|
|
317
456
|
{
|
|
318
457
|
method: "POST",
|
|
319
458
|
headers: {
|
|
320
|
-
|
|
459
|
+
...buildAuthHeaders(accessToken, sandbox),
|
|
321
460
|
"Content-Type": "application/json"
|
|
322
461
|
},
|
|
323
|
-
body: JSON.stringify(
|
|
462
|
+
body: JSON.stringify(requestBody),
|
|
324
463
|
signal
|
|
325
464
|
}
|
|
326
465
|
);
|
|
@@ -352,7 +491,8 @@ var submitConsentEventPublic = async ({
|
|
|
352
491
|
noticeUuid,
|
|
353
492
|
purposes,
|
|
354
493
|
declined,
|
|
355
|
-
meta_data
|
|
494
|
+
meta_data,
|
|
495
|
+
language
|
|
356
496
|
}) => {
|
|
357
497
|
var _a, _b;
|
|
358
498
|
const apiBaseUrl = ledgerBaseUrl || baseUrl || BASE_URL_PUBLIC;
|
|
@@ -373,7 +513,8 @@ var submitConsentEventPublic = async ({
|
|
|
373
513
|
select_all_mandatory: false,
|
|
374
514
|
source: "WEB",
|
|
375
515
|
declined,
|
|
376
|
-
meta_data
|
|
516
|
+
meta_data,
|
|
517
|
+
language: toLanguageCode(language)
|
|
377
518
|
};
|
|
378
519
|
const response = await fetch(
|
|
379
520
|
`${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/submit-consent`,
|
|
@@ -412,23 +553,32 @@ var submitConsentEventPublic = async ({
|
|
|
412
553
|
var fetchTTSAudioUrls = async ({
|
|
413
554
|
accessToken,
|
|
414
555
|
baseUrl,
|
|
556
|
+
ledgerBaseUrl,
|
|
415
557
|
noticeUuid,
|
|
416
558
|
language,
|
|
417
|
-
signal
|
|
559
|
+
signal,
|
|
560
|
+
...sandboxParams
|
|
418
561
|
}) => {
|
|
419
|
-
if (!accessToken || !noticeUuid || !language) {
|
|
420
|
-
throw new Error("accessToken, noticeUuid, and language are required");
|
|
562
|
+
if (!accessToken && !sandboxParams.token || !noticeUuid || !language) {
|
|
563
|
+
throw new Error("accessToken (or token), noticeUuid, and language are required");
|
|
421
564
|
}
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const apiBaseUrl = baseUrl || BASE_URL;
|
|
427
|
-
const url =
|
|
428
|
-
|
|
565
|
+
const { organisationUuid: ORG, workspaceUuid: WS, sandbox } = resolveRequestAuth(
|
|
566
|
+
accessToken,
|
|
567
|
+
sandboxParams
|
|
568
|
+
);
|
|
569
|
+
const apiBaseUrl = (sandbox ? null : ledgerBaseUrl) || baseUrl || BASE_URL;
|
|
570
|
+
const url = new URL(
|
|
571
|
+
`${apiBaseUrl}/public/organisations/${ORG}/workspaces/${WS}/notices/${noticeUuid}/audio/${language}`
|
|
572
|
+
);
|
|
573
|
+
if (sandbox) {
|
|
574
|
+
for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
|
|
575
|
+
url.searchParams.append(key, value);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
const response = await fetch(url.toString(), {
|
|
429
579
|
method: "GET",
|
|
430
580
|
headers: {
|
|
431
|
-
|
|
581
|
+
...buildAuthHeaders(accessToken, sandbox),
|
|
432
582
|
Accept: "application/json"
|
|
433
583
|
},
|
|
434
584
|
signal
|
|
@@ -895,6 +1045,42 @@ var styles = {
|
|
|
895
1045
|
padding: 0,
|
|
896
1046
|
boxSizing: "border-box"
|
|
897
1047
|
},
|
|
1048
|
+
// The main notice header is two stacked rows: the logo alone on row 1,
|
|
1049
|
+
// aligned per active_config.logo_position, then the title and the
|
|
1050
|
+
// TTS/language controls on row 2. `topSection` and `topLeft` above keep their
|
|
1051
|
+
// single-row shape for the age-verification, guardian and verification
|
|
1052
|
+
// screens, which have no controls to place and share those styles.
|
|
1053
|
+
// Mirrors consent-sdk-react/src/RedactoNoticeConsent/styles.ts.
|
|
1054
|
+
headerStack: {
|
|
1055
|
+
display: "flex",
|
|
1056
|
+
flexDirection: "column",
|
|
1057
|
+
alignItems: "stretch",
|
|
1058
|
+
gap: "10px",
|
|
1059
|
+
paddingBottom: "15px",
|
|
1060
|
+
borderBottom: "none",
|
|
1061
|
+
backgroundColor: "#ffffff",
|
|
1062
|
+
margin: 0,
|
|
1063
|
+
boxSizing: "border-box",
|
|
1064
|
+
textAlign: "left"
|
|
1065
|
+
},
|
|
1066
|
+
logoRow: {
|
|
1067
|
+
display: "flex",
|
|
1068
|
+
alignItems: "center",
|
|
1069
|
+
width: "100%",
|
|
1070
|
+
margin: 0,
|
|
1071
|
+
padding: 0,
|
|
1072
|
+
boxSizing: "border-box"
|
|
1073
|
+
},
|
|
1074
|
+
headerRow: {
|
|
1075
|
+
display: "flex",
|
|
1076
|
+
alignItems: "center",
|
|
1077
|
+
justifyContent: "space-between",
|
|
1078
|
+
gap: "10px",
|
|
1079
|
+
width: "100%",
|
|
1080
|
+
margin: 0,
|
|
1081
|
+
padding: 0,
|
|
1082
|
+
boxSizing: "border-box"
|
|
1083
|
+
},
|
|
898
1084
|
logo: {
|
|
899
1085
|
height: "32px",
|
|
900
1086
|
width: "auto",
|
|
@@ -1126,6 +1312,9 @@ var styles = {
|
|
|
1126
1312
|
},
|
|
1127
1313
|
languageSelectorContainer: {
|
|
1128
1314
|
position: "relative",
|
|
1315
|
+
// Shares `headerRow` with the title, which grows; without this a long
|
|
1316
|
+
// heading squeezes the language button instead of wrapping itself.
|
|
1317
|
+
flexShrink: 0,
|
|
1129
1318
|
margin: 0,
|
|
1130
1319
|
padding: 0,
|
|
1131
1320
|
boxSizing: "border-box"
|
|
@@ -1688,6 +1877,12 @@ var updateConsentState = (updates) => {
|
|
|
1688
1877
|
};
|
|
1689
1878
|
|
|
1690
1879
|
// src/constants.ts
|
|
1880
|
+
var DEFAULT_LOGO_POSITION = "left";
|
|
1881
|
+
var LOGO_POSITION_JUSTIFY = {
|
|
1882
|
+
left: "flex-start",
|
|
1883
|
+
center: "center",
|
|
1884
|
+
right: "flex-end"
|
|
1885
|
+
};
|
|
1691
1886
|
var DPO_AUDIO_ELEMENT_MAP = [
|
|
1692
1887
|
{ audioKey: "grievance_text_audio_url", elementId: "dpo-grievance-text" },
|
|
1693
1888
|
{ audioKey: "grievance_anchor_text_audio_url", elementId: "dpo-grievance-link" },
|
|
@@ -1746,9 +1941,15 @@ var TTSManager = class {
|
|
|
1746
1941
|
const response = await fetchTTSAudioUrls({
|
|
1747
1942
|
accessToken: params.accessToken,
|
|
1748
1943
|
baseUrl: params.baseUrl,
|
|
1944
|
+
ledgerBaseUrl: params.ledgerBaseUrl,
|
|
1749
1945
|
noticeUuid: params.noticeUuid,
|
|
1750
1946
|
language: params.language,
|
|
1751
|
-
signal: this.abortController.signal
|
|
1947
|
+
signal: this.abortController.signal,
|
|
1948
|
+
token: params.token,
|
|
1949
|
+
sandboxUcic: params.sandboxUcic,
|
|
1950
|
+
sandboxSubject: params.sandboxSubject,
|
|
1951
|
+
organisationUuid: params.organisationUuid,
|
|
1952
|
+
workspaceUuid: params.workspaceUuid
|
|
1752
1953
|
});
|
|
1753
1954
|
this.audioUrls = response.detail;
|
|
1754
1955
|
this.buildSequences(params.content);
|
|
@@ -2713,6 +2914,14 @@ function resolveColors(settings, activeConfig) {
|
|
|
2713
2914
|
};
|
|
2714
2915
|
}
|
|
2715
2916
|
|
|
2917
|
+
// src/modal/layout.ts
|
|
2918
|
+
function resolveLogoPosition(raw) {
|
|
2919
|
+
return raw === "center" || raw === "right" ? raw : DEFAULT_LOGO_POSITION;
|
|
2920
|
+
}
|
|
2921
|
+
function logoRowJustify(position) {
|
|
2922
|
+
return LOGO_POSITION_JUSTIFY[position];
|
|
2923
|
+
}
|
|
2924
|
+
|
|
2716
2925
|
// src/modal/index.ts
|
|
2717
2926
|
var ModalController = class {
|
|
2718
2927
|
constructor(options) {
|
|
@@ -2817,21 +3026,41 @@ var ModalController = class {
|
|
|
2817
3026
|
this.langSelectorRefs = null;
|
|
2818
3027
|
}
|
|
2819
3028
|
// ─── Private ───
|
|
3029
|
+
/**
|
|
3030
|
+
* Sandbox auth options to forward to every API call. When `token` is set the
|
|
3031
|
+
* API layer sends the single `X-Consent-Token` header, injects the subject
|
|
3032
|
+
* into each request's payload, and reads org/workspace from these fields
|
|
3033
|
+
* instead of decoding the JWT. Empty in the normal JWT flow.
|
|
3034
|
+
*/
|
|
3035
|
+
sandboxParams() {
|
|
3036
|
+
return {
|
|
3037
|
+
token: this.options.token,
|
|
3038
|
+
// The static token carries no identity; read the acting principal from the
|
|
3039
|
+
// optional ucic/email/mobile. UCIC takes precedence server-side.
|
|
3040
|
+
// sandboxUcic / sandboxSubject stay as fallbacks.
|
|
3041
|
+
sandboxUcic: this.options.ucic || this.options.sandboxUcic,
|
|
3042
|
+
sandboxSubject: this.options.email || this.options.mobile || this.options.sandboxSubject,
|
|
3043
|
+
organisationUuid: this.options.organisationUuid,
|
|
3044
|
+
workspaceUuid: this.options.workspaceUuid
|
|
3045
|
+
};
|
|
3046
|
+
}
|
|
2820
3047
|
async fetchNotice() {
|
|
2821
3048
|
var _a, _b, _c, _d;
|
|
2822
3049
|
this.abortController = new AbortController();
|
|
2823
3050
|
this.store.setState({ isLoading: true, fetchError: null });
|
|
2824
3051
|
try {
|
|
3052
|
+
const noticeBaseUrl = this.options.ledgerBaseUrl && !this.options.applicationId && !this.options.token ? this.options.ledgerBaseUrl : this.options.baseUrl;
|
|
2825
3053
|
const data = await fetchConsentContent({
|
|
2826
3054
|
noticeId: this.options.noticeId,
|
|
2827
3055
|
accessToken: this.options.accessToken,
|
|
2828
3056
|
refreshToken: this.options.refreshToken,
|
|
2829
|
-
baseUrl:
|
|
3057
|
+
baseUrl: noticeBaseUrl,
|
|
2830
3058
|
language: this.options.language,
|
|
2831
3059
|
specific_uuid: this.options.applicationId,
|
|
2832
3060
|
validate_against: this.options.validateAgainst || "all",
|
|
2833
3061
|
include_fully_consented_data: this.options.includeFullyConsentedData,
|
|
2834
|
-
signal: this.abortController.signal
|
|
3062
|
+
signal: this.abortController.signal,
|
|
3063
|
+
...this.sandboxParams()
|
|
2835
3064
|
});
|
|
2836
3065
|
const activeConfig = data.detail.active_config;
|
|
2837
3066
|
let purposeSelections = data.detail.purpose_selections;
|
|
@@ -2893,10 +3122,11 @@ var ModalController = class {
|
|
|
2893
3122
|
if (this.options.includeFullyConsentedData && needsConsentPurposes.length === 0 && alreadyConsentedPurposes.length > 0) {
|
|
2894
3123
|
isFullyConsentedReviewMode = true;
|
|
2895
3124
|
}
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
3125
|
+
const selectedLanguage = resolveDisplayLanguage(
|
|
3126
|
+
this.options.language,
|
|
3127
|
+
activeConfig.default_language,
|
|
3128
|
+
activeConfig.supported_languages_and_translations
|
|
3129
|
+
);
|
|
2900
3130
|
let isMinorFlow = false;
|
|
2901
3131
|
let showAgeVerification = false;
|
|
2902
3132
|
let restoreState = {};
|
|
@@ -3194,49 +3424,32 @@ var ModalController = class {
|
|
|
3194
3424
|
(_g = this.ttsManager) == null ? void 0 : _g.fetchAudioUrls({
|
|
3195
3425
|
accessToken: this.options.accessToken,
|
|
3196
3426
|
baseUrl: this.options.baseUrl,
|
|
3427
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
3197
3428
|
noticeUuid: activeConfig.notice_uuid,
|
|
3198
3429
|
language: this.getLanguageCode(state.selectedLanguage),
|
|
3199
|
-
content: activeConfig
|
|
3430
|
+
content: activeConfig,
|
|
3431
|
+
...this.sandboxParams()
|
|
3200
3432
|
});
|
|
3201
3433
|
const topRight = el("div", {
|
|
3202
3434
|
style: { display: "flex", alignItems: "center", gap: "8px", flexShrink: 0 }
|
|
3203
3435
|
}, this.ttsButton, this.langSelectorRefs.container);
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
{
|
|
3209
|
-
style: {
|
|
3210
|
-
display: "flex",
|
|
3211
|
-
alignItems: "center",
|
|
3212
|
-
gap: "10px",
|
|
3213
|
-
width: "100%"
|
|
3214
|
-
}
|
|
3215
|
-
},
|
|
3216
|
-
...logoImg ? [logoImg] : [],
|
|
3436
|
+
const logoPosition = resolveLogoPosition(activeConfig.logo_position);
|
|
3437
|
+
const headerRows = [];
|
|
3438
|
+
if (logoImg) {
|
|
3439
|
+
headerRows.push(
|
|
3217
3440
|
el(
|
|
3218
3441
|
"div",
|
|
3219
|
-
{ style: {
|
|
3220
|
-
|
|
3442
|
+
{ style: { ...styles.logoRow, justifyContent: logoRowJustify(logoPosition) } },
|
|
3443
|
+
logoImg
|
|
3221
3444
|
)
|
|
3222
3445
|
);
|
|
3223
|
-
topSection = el(
|
|
3224
|
-
"div",
|
|
3225
|
-
{
|
|
3226
|
-
style: {
|
|
3227
|
-
...styles.topSection,
|
|
3228
|
-
flexDirection: "column",
|
|
3229
|
-
alignItems: "stretch",
|
|
3230
|
-
flexWrap: "nowrap"
|
|
3231
|
-
}
|
|
3232
|
-
},
|
|
3233
|
-
headerRow,
|
|
3234
|
-
this.noticeTitleEl
|
|
3235
|
-
);
|
|
3236
|
-
} else {
|
|
3237
|
-
const topLeft = el("div", { style: styles.topLeft }, logoImg, this.noticeTitleEl);
|
|
3238
|
-
topSection = el("div", { style: styles.topSection }, topLeft, topRight);
|
|
3239
3446
|
}
|
|
3447
|
+
headerRows.push(el("div", { style: styles.headerRow }, this.noticeTitleEl, topRight));
|
|
3448
|
+
const topSection = el(
|
|
3449
|
+
"div",
|
|
3450
|
+
{ style: { ...styles.headerStack, gap: isMobile ? "12px" : "10px" } },
|
|
3451
|
+
...headerRows
|
|
3452
|
+
);
|
|
3240
3453
|
this.middleSection = el("div", { style: styles.middleSection });
|
|
3241
3454
|
const baseTextStyle = { ...styles.privacyText, color: textColor, fontFamily: font, fontSize: isMobile ? "14px" : "16px" };
|
|
3242
3455
|
const noticeTextSpan = el("span", { id: "privacy-notice-text" });
|
|
@@ -3671,7 +3884,9 @@ var ModalController = class {
|
|
|
3671
3884
|
meta_data: this.options.applicationId ? { specific_uuid: this.options.applicationId } : void 0,
|
|
3672
3885
|
guardianVerificationReference: state.verificationReference || void 0,
|
|
3673
3886
|
selfDeclaredAdult: state.selfDeclaredAdult || void 0,
|
|
3674
|
-
|
|
3887
|
+
language: state.selectedLanguage,
|
|
3888
|
+
signal: this.abortController.signal,
|
|
3889
|
+
...this.sandboxParams()
|
|
3675
3890
|
});
|
|
3676
3891
|
this.options.onAccept();
|
|
3677
3892
|
this.destroy();
|
|
@@ -4416,6 +4631,8 @@ var InlineController = class {
|
|
|
4416
4631
|
this.errorEl = null;
|
|
4417
4632
|
this.hasSubmitted = false;
|
|
4418
4633
|
this.hasCheckedLedgerConsent = false;
|
|
4634
|
+
this.fetchedWithToken = false;
|
|
4635
|
+
this.fetchGeneration = 0;
|
|
4419
4636
|
this.unsubscribe = null;
|
|
4420
4637
|
this.abortController = null;
|
|
4421
4638
|
this.options = options;
|
|
@@ -4473,6 +4690,10 @@ var InlineController = class {
|
|
|
4473
4690
|
}
|
|
4474
4691
|
updateToken(accessToken) {
|
|
4475
4692
|
this.options = { ...this.options, accessToken };
|
|
4693
|
+
if (accessToken && !this.fetchedWithToken) {
|
|
4694
|
+
void this.fetchNotice();
|
|
4695
|
+
return;
|
|
4696
|
+
}
|
|
4476
4697
|
if (this.shouldCheckLedgerConsent()) {
|
|
4477
4698
|
void this.checkLedgerConsent();
|
|
4478
4699
|
return;
|
|
@@ -4488,17 +4709,20 @@ var InlineController = class {
|
|
|
4488
4709
|
}
|
|
4489
4710
|
async fetchNotice() {
|
|
4490
4711
|
var _a, _b, _c, _d;
|
|
4491
|
-
|
|
4712
|
+
const generation = ++this.fetchGeneration;
|
|
4713
|
+
this.fetchedWithToken = Boolean(this.options.accessToken);
|
|
4714
|
+
this.store.setState({ isLoading: true, fetchError: null, hasAlreadyConsented: false });
|
|
4492
4715
|
try {
|
|
4493
4716
|
const data = await fetchConsentContentPublic({
|
|
4494
4717
|
org_uuid: this.options.org_uuid,
|
|
4495
4718
|
workspace_uuid: this.options.workspace_uuid,
|
|
4496
4719
|
notice_uuid: this.options.notice_uuid,
|
|
4720
|
+
accessToken: this.options.accessToken,
|
|
4497
4721
|
baseUrl: this.options.baseUrl,
|
|
4498
4722
|
language: this.options.language,
|
|
4499
4723
|
specific_uuid: this.options.applicationId
|
|
4500
4724
|
});
|
|
4501
|
-
if (this.isDestroyed()) {
|
|
4725
|
+
if (this.isDestroyed() || generation !== this.fetchGeneration) {
|
|
4502
4726
|
return;
|
|
4503
4727
|
}
|
|
4504
4728
|
const activeConfig = data.detail.active_config;
|
|
@@ -4516,13 +4740,17 @@ var InlineController = class {
|
|
|
4516
4740
|
this.store.setState({
|
|
4517
4741
|
content: activeConfig,
|
|
4518
4742
|
isLoading: false,
|
|
4519
|
-
selectedLanguage:
|
|
4743
|
+
selectedLanguage: resolveDisplayLanguage(
|
|
4744
|
+
this.options.language,
|
|
4745
|
+
activeConfig.default_language,
|
|
4746
|
+
activeConfig.supported_languages_and_translations
|
|
4747
|
+
),
|
|
4520
4748
|
collapsedPurposes: initialCollapsed,
|
|
4521
4749
|
selectedPurposes: initialPurposes,
|
|
4522
4750
|
selectedDataElements: initialElements
|
|
4523
4751
|
});
|
|
4524
4752
|
} catch (err) {
|
|
4525
|
-
if (this.isDestroyed()) {
|
|
4753
|
+
if (this.isDestroyed() || generation !== this.fetchGeneration) {
|
|
4526
4754
|
return;
|
|
4527
4755
|
}
|
|
4528
4756
|
const error = err;
|
|
@@ -4839,7 +5067,8 @@ var InlineController = class {
|
|
|
4839
5067
|
};
|
|
4840
5068
|
}),
|
|
4841
5069
|
declined: false,
|
|
4842
|
-
meta_data: this.options.applicationId ? { specific_uuid: this.options.applicationId } : void 0
|
|
5070
|
+
meta_data: this.options.applicationId ? { specific_uuid: this.options.applicationId } : void 0,
|
|
5071
|
+
language: state.selectedLanguage
|
|
4843
5072
|
});
|
|
4844
5073
|
(_b = (_a = this.options).onAccept) == null ? void 0 : _b.call(_a);
|
|
4845
5074
|
this.hasSubmitted = false;
|