@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.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 || !accessToken || !validate_against) {
200
- throw new Error("noticeId and accessToken are required");
316
+ if (!noticeId || !validate_against) {
317
+ throw new Error("noticeId is required");
201
318
  }
202
- const decodedToken = decodeTokenSafely(accessToken);
203
- if (!decodedToken) throw new Error("Invalid access token");
204
- const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
205
- if (!ORG || !WS) throw new Error("Invalid token: missing organization or workspace UUID");
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 cacheKey = `${accessToken}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
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
- Authorization: `Bearer ${accessToken}`,
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
- `${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/notices/get-notice/${notice_uuid}`
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: { "Content-Type": "application/json" }
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
- signal
415
+ language,
416
+ signal,
417
+ ...sandboxParams
284
418
  }) => {
285
- if (!noticeUuid || !accessToken || !Array.isArray(purposes)) {
286
- throw new Error("noticeUuid, accessToken, and purposes array are required");
419
+ if (!noticeUuid || !Array.isArray(purposes)) {
420
+ throw new Error("noticeUuid and purposes array are required");
287
421
  }
288
- const decodedToken = decodeTokenSafely(accessToken);
289
- if (!decodedToken) throw new Error("Invalid access token");
290
- const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
291
- if (!ORG || !WS) throw new Error("Invalid token: missing organization or workspace UUID");
292
- const apiBaseUrl = ledgerBaseUrl || baseUrl || BASE_URL;
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
- Authorization: `Bearer ${accessToken}`,
459
+ ...buildAuthHeaders(accessToken, sandbox),
321
460
  "Content-Type": "application/json"
322
461
  },
323
- body: JSON.stringify(payload),
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 decodedToken = decodeTokenSafely(accessToken);
423
- if (!decodedToken) throw new Error("Invalid access token");
424
- const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
425
- if (!ORG || !WS) throw new Error("Invalid token: missing organization or workspace UUID");
426
- const apiBaseUrl = baseUrl || BASE_URL;
427
- const url = `${apiBaseUrl}/public/organisations/${ORG}/workspaces/${WS}/notices/${noticeUuid}/audio/${language}`;
428
- const response = await fetch(url, {
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
- Authorization: `Bearer ${accessToken}`,
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: this.options.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
- let selectedLanguage = this.options.language || "en";
2897
- if (activeConfig.default_language) {
2898
- selectedLanguage = activeConfig.default_language;
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
- let topSection;
3205
- if (isMobile) {
3206
- const headerRow = el(
3207
- "div",
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: { marginLeft: "auto", display: "flex", alignItems: "center" } },
3220
- topRight
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
- signal: this.abortController.signal
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
- this.store.setState({ isLoading: true, fetchError: null });
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: activeConfig.default_language || this.options.language || "en",
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redacto.io/consent-sdk-js",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0",
4
4
  "main": "dist/redacto.js",
5
5
  "module": "dist/redacto.mjs",
6
6
  "types": "dist/redacto.d.ts",