@redacto.io/consent-sdk-js 0.1.1 → 1.0.0-beta.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 +4 -0
- package/dist/redacto.d.ts +4 -0
- package/dist/redacto.global.js +2 -2
- package/dist/redacto.global.js.map +1 -1
- package/dist/redacto.js +153 -9
- package/dist/redacto.mjs +153 -9
- package/package.json +3 -3
package/dist/redacto.js
CHANGED
|
@@ -303,6 +303,7 @@ var fetchConsentContentPublic = async ({
|
|
|
303
303
|
var submitConsentEvent = async ({
|
|
304
304
|
accessToken,
|
|
305
305
|
baseUrl,
|
|
306
|
+
ledgerBaseUrl,
|
|
306
307
|
noticeUuid,
|
|
307
308
|
purposes,
|
|
308
309
|
declined,
|
|
@@ -318,7 +319,7 @@ var submitConsentEvent = async ({
|
|
|
318
319
|
if (!decodedToken) throw new Error("Invalid access token");
|
|
319
320
|
const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
|
|
320
321
|
if (!ORG || !WS) throw new Error("Invalid token: missing organization or workspace UUID");
|
|
321
|
-
const apiBaseUrl = baseUrl || BASE_URL;
|
|
322
|
+
const apiBaseUrl = ledgerBaseUrl || baseUrl || BASE_URL;
|
|
322
323
|
const validatedPurposes = purposes.map((purpose) => {
|
|
323
324
|
if (!purpose.uuid) throw new Error("Invalid purpose data: missing uuid");
|
|
324
325
|
const dataElements = (purpose.data_elements || []).filter((element) => element.enabled).map((element) => ({
|
|
@@ -377,13 +378,14 @@ var submitConsentEventPublic = async ({
|
|
|
377
378
|
workspace_uuid,
|
|
378
379
|
accessToken,
|
|
379
380
|
baseUrl,
|
|
381
|
+
ledgerBaseUrl,
|
|
380
382
|
noticeUuid,
|
|
381
383
|
purposes,
|
|
382
384
|
declined,
|
|
383
385
|
meta_data
|
|
384
386
|
}) => {
|
|
385
387
|
var _a, _b;
|
|
386
|
-
const apiBaseUrl = baseUrl || BASE_URL_PUBLIC;
|
|
388
|
+
const apiBaseUrl = ledgerBaseUrl || baseUrl || BASE_URL_PUBLIC;
|
|
387
389
|
const payload = {
|
|
388
390
|
notice_uuid: noticeUuid,
|
|
389
391
|
purposes: purposes.map((purpose) => {
|
|
@@ -616,6 +618,95 @@ var verifyGuardianStatus = async ({
|
|
|
616
618
|
};
|
|
617
619
|
};
|
|
618
620
|
|
|
621
|
+
// src/api/notice-consent-status.ts
|
|
622
|
+
var fetchNoticeConsentStatusForScope = async ({
|
|
623
|
+
accessToken,
|
|
624
|
+
ledgerBaseUrl,
|
|
625
|
+
organisationUuid,
|
|
626
|
+
workspaceUuid,
|
|
627
|
+
noticeUuid,
|
|
628
|
+
signal
|
|
629
|
+
}) => {
|
|
630
|
+
if (!accessToken || !ledgerBaseUrl || !organisationUuid || !workspaceUuid || !noticeUuid) {
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
try {
|
|
634
|
+
const response = await fetch(
|
|
635
|
+
`${ledgerBaseUrl}/public/organisations/${organisationUuid}/workspaces/${workspaceUuid}/notices/${noticeUuid}/check-consent`,
|
|
636
|
+
{
|
|
637
|
+
method: "POST",
|
|
638
|
+
headers: {
|
|
639
|
+
Authorization: `Bearer ${accessToken}`,
|
|
640
|
+
"Content-Type": "application/json"
|
|
641
|
+
},
|
|
642
|
+
body: "{}",
|
|
643
|
+
signal
|
|
644
|
+
}
|
|
645
|
+
);
|
|
646
|
+
if (!response.ok) return null;
|
|
647
|
+
const data = await response.json();
|
|
648
|
+
const body = data && typeof data === "object" && "detail" in data && data.detail ? data.detail : data;
|
|
649
|
+
if (!body || !Array.isArray(body.purposes)) return null;
|
|
650
|
+
return body;
|
|
651
|
+
} catch {
|
|
652
|
+
return null;
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
var fetchNoticeConsentStatus = async ({
|
|
656
|
+
accessToken,
|
|
657
|
+
ledgerBaseUrl,
|
|
658
|
+
noticeUuid,
|
|
659
|
+
signal
|
|
660
|
+
}) => {
|
|
661
|
+
if (!accessToken || !ledgerBaseUrl || !noticeUuid) return null;
|
|
662
|
+
const decodedToken = decodeTokenSafely(accessToken);
|
|
663
|
+
if (!decodedToken) return null;
|
|
664
|
+
const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
|
|
665
|
+
if (!ORG || !WS) return null;
|
|
666
|
+
return fetchNoticeConsentStatusForScope({
|
|
667
|
+
accessToken,
|
|
668
|
+
ledgerBaseUrl,
|
|
669
|
+
organisationUuid: ORG,
|
|
670
|
+
workspaceUuid: WS,
|
|
671
|
+
noticeUuid,
|
|
672
|
+
signal
|
|
673
|
+
});
|
|
674
|
+
};
|
|
675
|
+
var isNoticeFullyConsented = (status) => status.consented && status.all_mandatory_active;
|
|
676
|
+
var buildLedgerConsentOverlay = (status, configPurposes) => {
|
|
677
|
+
var _a, _b;
|
|
678
|
+
const byUuid = new Map(status.purposes.map((p) => [p.uuid, p]));
|
|
679
|
+
const hasHistory = status.purposes.some((p) => (p.status || "INACTIVE") !== "INACTIVE");
|
|
680
|
+
if (!hasHistory) return null;
|
|
681
|
+
const purpose_selections = {};
|
|
682
|
+
for (const cp of configPurposes) {
|
|
683
|
+
const lp = byUuid.get(cp.uuid);
|
|
684
|
+
const purposeStatus = (lp == null ? void 0 : lp.status) || "INACTIVE";
|
|
685
|
+
const selectedByUuid = new Map(((lp == null ? void 0 : lp.data_elements) || []).map((de) => [de.uuid, de.selected]));
|
|
686
|
+
const data_elements = {};
|
|
687
|
+
for (const de of cp.data_elements) {
|
|
688
|
+
data_elements[de.uuid] = {
|
|
689
|
+
selected: (_a = selectedByUuid.get(de.uuid)) != null ? _a : false,
|
|
690
|
+
enabled: de.enabled,
|
|
691
|
+
required: de.required
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
purpose_selections[cp.uuid] = {
|
|
695
|
+
selected: (_b = lp == null ? void 0 : lp.selected) != null ? _b : false,
|
|
696
|
+
status: purposeStatus,
|
|
697
|
+
needs_reconsent: purposeStatus !== "ACTIVE",
|
|
698
|
+
data_elements
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
const reconsent_required = configPurposes.some(
|
|
702
|
+
(cp) => {
|
|
703
|
+
var _a2;
|
|
704
|
+
return (((_a2 = purpose_selections[cp.uuid]) == null ? void 0 : _a2.status) || "INACTIVE") !== "ACTIVE";
|
|
705
|
+
}
|
|
706
|
+
);
|
|
707
|
+
return { reconsent_required, purpose_selections };
|
|
708
|
+
};
|
|
709
|
+
|
|
619
710
|
// src/styles/inject.ts
|
|
620
711
|
var STYLE_ID = "redacto-consent-checkbox-styles";
|
|
621
712
|
var STYLE_CONTENT = `
|
|
@@ -1355,7 +1446,6 @@ var LANGUAGE_NATIVE_LABELS = {
|
|
|
1355
1446
|
English: "English",
|
|
1356
1447
|
Assamese: "\u0985\u09B8\u09AE\u09C0\u09AF\u09BC\u09BE (Assamese)",
|
|
1357
1448
|
Bengali: "\u09AC\u09BE\u0982\u09B2\u09BE (Bengali)",
|
|
1358
|
-
Bhojpuri: "\u092D\u094B\u091C\u092A\u0941\u0930\u0940 (Bhojpuri)",
|
|
1359
1449
|
Bodo: "\u092C\u0921\u093C\u094B (Bodo)",
|
|
1360
1450
|
Dogri: "\u0921\u094B\u0917\u0930\u0940 (Dogri)",
|
|
1361
1451
|
"Goan Konkani": "\u0915\u094B\u0902\u0915\u0923\u0940 (Goan Konkani)",
|
|
@@ -2758,7 +2848,7 @@ var ModalController = class {
|
|
|
2758
2848
|
}
|
|
2759
2849
|
// ─── Private ───
|
|
2760
2850
|
async fetchNotice() {
|
|
2761
|
-
var _a, _b, _c;
|
|
2851
|
+
var _a, _b, _c, _d;
|
|
2762
2852
|
this.abortController = new AbortController();
|
|
2763
2853
|
this.store.setState({ isLoading: true, fetchError: null });
|
|
2764
2854
|
try {
|
|
@@ -2774,7 +2864,23 @@ var ModalController = class {
|
|
|
2774
2864
|
signal: this.abortController.signal
|
|
2775
2865
|
});
|
|
2776
2866
|
const activeConfig = data.detail.active_config;
|
|
2777
|
-
|
|
2867
|
+
let purposeSelections = data.detail.purpose_selections;
|
|
2868
|
+
let reconsentRequired = data.detail.reconsent_required;
|
|
2869
|
+
let ledgerOverlayApplied = false;
|
|
2870
|
+
if (this.options.ledgerBaseUrl && !this.options.applicationId) {
|
|
2871
|
+
const ledgerStatus = await fetchNoticeConsentStatus({
|
|
2872
|
+
accessToken: this.options.accessToken,
|
|
2873
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
2874
|
+
noticeUuid: this.options.noticeId,
|
|
2875
|
+
signal: this.abortController.signal
|
|
2876
|
+
});
|
|
2877
|
+
if (ledgerStatus) {
|
|
2878
|
+
const overlay = buildLedgerConsentOverlay(ledgerStatus, activeConfig.purposes);
|
|
2879
|
+
reconsentRequired = (_a = overlay == null ? void 0 : overlay.reconsent_required) != null ? _a : false;
|
|
2880
|
+
purposeSelections = overlay == null ? void 0 : overlay.purpose_selections;
|
|
2881
|
+
ledgerOverlayApplied = Boolean(overlay);
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2778
2884
|
const initialCollapsed = {};
|
|
2779
2885
|
const initialPurposes = {};
|
|
2780
2886
|
const initialElements = {};
|
|
@@ -2782,7 +2888,7 @@ var ModalController = class {
|
|
|
2782
2888
|
let needsConsentPurposes = [];
|
|
2783
2889
|
let alreadyConsentedPurposes = [];
|
|
2784
2890
|
let isFullyConsentedReviewMode = false;
|
|
2785
|
-
if (purposeSelections &&
|
|
2891
|
+
if (purposeSelections && (reconsentRequired || ledgerOverlayApplied)) {
|
|
2786
2892
|
for (const purpose of activeConfig.purposes) {
|
|
2787
2893
|
const selection = purposeSelections[purpose.uuid];
|
|
2788
2894
|
initialCollapsed[purpose.uuid] = true;
|
|
@@ -2791,7 +2897,7 @@ var ModalController = class {
|
|
|
2791
2897
|
initialPurposes[purpose.uuid] = true;
|
|
2792
2898
|
for (const de of purpose.data_elements) {
|
|
2793
2899
|
const combinedId = `${purpose.uuid}-${de.uuid}`;
|
|
2794
|
-
const deSelection = (
|
|
2900
|
+
const deSelection = (_b = selection.data_elements) == null ? void 0 : _b[de.uuid];
|
|
2795
2901
|
const wasSelected = (deSelection == null ? void 0 : deSelection.selected) || false;
|
|
2796
2902
|
initialElements[combinedId] = wasSelected;
|
|
2797
2903
|
initialDataElementSelections[combinedId] = wasSelected;
|
|
@@ -2880,7 +2986,7 @@ var ModalController = class {
|
|
|
2880
2986
|
this.options.onAccept();
|
|
2881
2987
|
} else {
|
|
2882
2988
|
this.store.setState({ fetchError: error, isLoading: false });
|
|
2883
|
-
(
|
|
2989
|
+
(_d = (_c = this.options).onError) == null ? void 0 : _d.call(_c, error);
|
|
2884
2990
|
}
|
|
2885
2991
|
}
|
|
2886
2992
|
}
|
|
@@ -3588,6 +3694,7 @@ var ModalController = class {
|
|
|
3588
3694
|
await submitConsentEvent({
|
|
3589
3695
|
accessToken: this.options.accessToken,
|
|
3590
3696
|
baseUrl: this.options.baseUrl,
|
|
3697
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
3591
3698
|
noticeUuid: state.content.detail.uuid,
|
|
3592
3699
|
purposes,
|
|
3593
3700
|
declined: false,
|
|
@@ -4338,6 +4445,7 @@ var InlineController = class {
|
|
|
4338
4445
|
this.purposeRefs = null;
|
|
4339
4446
|
this.errorEl = null;
|
|
4340
4447
|
this.hasSubmitted = false;
|
|
4448
|
+
this.hasCheckedLedgerConsent = false;
|
|
4341
4449
|
this.unsubscribe = null;
|
|
4342
4450
|
this.options = options;
|
|
4343
4451
|
this.store = new Store({
|
|
@@ -4349,6 +4457,7 @@ var InlineController = class {
|
|
|
4349
4457
|
selectedPurposes: {},
|
|
4350
4458
|
selectedDataElements: {},
|
|
4351
4459
|
hasAlreadyConsented: false,
|
|
4460
|
+
isCheckingConsent: false,
|
|
4352
4461
|
fetchError: null,
|
|
4353
4462
|
errorMessage: null
|
|
4354
4463
|
});
|
|
@@ -4391,6 +4500,10 @@ var InlineController = class {
|
|
|
4391
4500
|
}
|
|
4392
4501
|
updateToken(accessToken) {
|
|
4393
4502
|
this.options = { ...this.options, accessToken };
|
|
4503
|
+
if (this.shouldCheckLedgerConsent()) {
|
|
4504
|
+
void this.checkLedgerConsent();
|
|
4505
|
+
return;
|
|
4506
|
+
}
|
|
4394
4507
|
this.tryAutoSubmit();
|
|
4395
4508
|
}
|
|
4396
4509
|
// ─── Private ───
|
|
@@ -4417,6 +4530,7 @@ var InlineController = class {
|
|
|
4417
4530
|
initialElements[`${purpose.uuid}-${de.uuid}`] = false;
|
|
4418
4531
|
}
|
|
4419
4532
|
}
|
|
4533
|
+
void this.checkLedgerConsent();
|
|
4420
4534
|
this.store.setState({
|
|
4421
4535
|
content: activeConfig,
|
|
4422
4536
|
isLoading: false,
|
|
@@ -4436,6 +4550,35 @@ var InlineController = class {
|
|
|
4436
4550
|
}
|
|
4437
4551
|
}
|
|
4438
4552
|
}
|
|
4553
|
+
shouldCheckLedgerConsent() {
|
|
4554
|
+
return Boolean(this.options.ledgerBaseUrl) && !this.options.applicationId && Boolean(this.options.accessToken) && !this.hasCheckedLedgerConsent;
|
|
4555
|
+
}
|
|
4556
|
+
// Ask the Go ledger whether this principal has already fully consented —
|
|
4557
|
+
// the ledger equivalent of the legacy Python 409. Skipped without a ledger
|
|
4558
|
+
// (the 409 path governs) and for the magic-link applicationId flow. Any
|
|
4559
|
+
// failure returns null and falls through to the normal flow.
|
|
4560
|
+
async checkLedgerConsent() {
|
|
4561
|
+
var _a, _b;
|
|
4562
|
+
if (!this.shouldCheckLedgerConsent()) return;
|
|
4563
|
+
this.hasCheckedLedgerConsent = true;
|
|
4564
|
+
this.store.setState({ isCheckingConsent: true });
|
|
4565
|
+
try {
|
|
4566
|
+
const status = await fetchNoticeConsentStatusForScope({
|
|
4567
|
+
accessToken: this.options.accessToken,
|
|
4568
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
4569
|
+
organisationUuid: this.options.org_uuid,
|
|
4570
|
+
workspaceUuid: this.options.workspace_uuid,
|
|
4571
|
+
noticeUuid: this.options.notice_uuid
|
|
4572
|
+
});
|
|
4573
|
+
if (status && isNoticeFullyConsented(status)) {
|
|
4574
|
+
this.store.setState({ hasAlreadyConsented: true });
|
|
4575
|
+
(_b = (_a = this.options).onAccept) == null ? void 0 : _b.call(_a);
|
|
4576
|
+
}
|
|
4577
|
+
} finally {
|
|
4578
|
+
this.store.setState({ isCheckingConsent: false });
|
|
4579
|
+
this.tryAutoSubmit();
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4439
4582
|
handleStateChange(state, prevState) {
|
|
4440
4583
|
if (prevState.isLoading && !state.isLoading && state.content) {
|
|
4441
4584
|
this.rebuildUI();
|
|
@@ -4657,7 +4800,7 @@ var InlineController = class {
|
|
|
4657
4800
|
var _a, _b;
|
|
4658
4801
|
const state = this.store.getState();
|
|
4659
4802
|
const accessToken = this.options.accessToken;
|
|
4660
|
-
if (accessToken && state.content && this.areAllRequiredElementsChecked() && !state.isSubmitting && !this.hasSubmitted) {
|
|
4803
|
+
if (accessToken && state.content && this.areAllRequiredElementsChecked() && !state.isSubmitting && !state.isCheckingConsent && !state.hasAlreadyConsented && !this.hasSubmitted) {
|
|
4661
4804
|
this.hasSubmitted = true;
|
|
4662
4805
|
try {
|
|
4663
4806
|
await this.handleSubmit(accessToken);
|
|
@@ -4682,6 +4825,7 @@ var InlineController = class {
|
|
|
4682
4825
|
workspace_uuid: this.options.workspace_uuid,
|
|
4683
4826
|
accessToken: tokenToUse,
|
|
4684
4827
|
baseUrl: this.options.baseUrl,
|
|
4828
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
4685
4829
|
noticeUuid: content.notice_uuid,
|
|
4686
4830
|
purposes: content.purposes.map((purpose) => {
|
|
4687
4831
|
const hasSelectedRequired = purpose.data_elements.some(
|
package/dist/redacto.mjs
CHANGED
|
@@ -273,6 +273,7 @@ var fetchConsentContentPublic = async ({
|
|
|
273
273
|
var submitConsentEvent = async ({
|
|
274
274
|
accessToken,
|
|
275
275
|
baseUrl,
|
|
276
|
+
ledgerBaseUrl,
|
|
276
277
|
noticeUuid,
|
|
277
278
|
purposes,
|
|
278
279
|
declined,
|
|
@@ -288,7 +289,7 @@ var submitConsentEvent = async ({
|
|
|
288
289
|
if (!decodedToken) throw new Error("Invalid access token");
|
|
289
290
|
const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
|
|
290
291
|
if (!ORG || !WS) throw new Error("Invalid token: missing organization or workspace UUID");
|
|
291
|
-
const apiBaseUrl = baseUrl || BASE_URL;
|
|
292
|
+
const apiBaseUrl = ledgerBaseUrl || baseUrl || BASE_URL;
|
|
292
293
|
const validatedPurposes = purposes.map((purpose) => {
|
|
293
294
|
if (!purpose.uuid) throw new Error("Invalid purpose data: missing uuid");
|
|
294
295
|
const dataElements = (purpose.data_elements || []).filter((element) => element.enabled).map((element) => ({
|
|
@@ -347,13 +348,14 @@ var submitConsentEventPublic = async ({
|
|
|
347
348
|
workspace_uuid,
|
|
348
349
|
accessToken,
|
|
349
350
|
baseUrl,
|
|
351
|
+
ledgerBaseUrl,
|
|
350
352
|
noticeUuid,
|
|
351
353
|
purposes,
|
|
352
354
|
declined,
|
|
353
355
|
meta_data
|
|
354
356
|
}) => {
|
|
355
357
|
var _a, _b;
|
|
356
|
-
const apiBaseUrl = baseUrl || BASE_URL_PUBLIC;
|
|
358
|
+
const apiBaseUrl = ledgerBaseUrl || baseUrl || BASE_URL_PUBLIC;
|
|
357
359
|
const payload = {
|
|
358
360
|
notice_uuid: noticeUuid,
|
|
359
361
|
purposes: purposes.map((purpose) => {
|
|
@@ -586,6 +588,95 @@ var verifyGuardianStatus = async ({
|
|
|
586
588
|
};
|
|
587
589
|
};
|
|
588
590
|
|
|
591
|
+
// src/api/notice-consent-status.ts
|
|
592
|
+
var fetchNoticeConsentStatusForScope = async ({
|
|
593
|
+
accessToken,
|
|
594
|
+
ledgerBaseUrl,
|
|
595
|
+
organisationUuid,
|
|
596
|
+
workspaceUuid,
|
|
597
|
+
noticeUuid,
|
|
598
|
+
signal
|
|
599
|
+
}) => {
|
|
600
|
+
if (!accessToken || !ledgerBaseUrl || !organisationUuid || !workspaceUuid || !noticeUuid) {
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
try {
|
|
604
|
+
const response = await fetch(
|
|
605
|
+
`${ledgerBaseUrl}/public/organisations/${organisationUuid}/workspaces/${workspaceUuid}/notices/${noticeUuid}/check-consent`,
|
|
606
|
+
{
|
|
607
|
+
method: "POST",
|
|
608
|
+
headers: {
|
|
609
|
+
Authorization: `Bearer ${accessToken}`,
|
|
610
|
+
"Content-Type": "application/json"
|
|
611
|
+
},
|
|
612
|
+
body: "{}",
|
|
613
|
+
signal
|
|
614
|
+
}
|
|
615
|
+
);
|
|
616
|
+
if (!response.ok) return null;
|
|
617
|
+
const data = await response.json();
|
|
618
|
+
const body = data && typeof data === "object" && "detail" in data && data.detail ? data.detail : data;
|
|
619
|
+
if (!body || !Array.isArray(body.purposes)) return null;
|
|
620
|
+
return body;
|
|
621
|
+
} catch {
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
var fetchNoticeConsentStatus = async ({
|
|
626
|
+
accessToken,
|
|
627
|
+
ledgerBaseUrl,
|
|
628
|
+
noticeUuid,
|
|
629
|
+
signal
|
|
630
|
+
}) => {
|
|
631
|
+
if (!accessToken || !ledgerBaseUrl || !noticeUuid) return null;
|
|
632
|
+
const decodedToken = decodeTokenSafely(accessToken);
|
|
633
|
+
if (!decodedToken) return null;
|
|
634
|
+
const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
|
|
635
|
+
if (!ORG || !WS) return null;
|
|
636
|
+
return fetchNoticeConsentStatusForScope({
|
|
637
|
+
accessToken,
|
|
638
|
+
ledgerBaseUrl,
|
|
639
|
+
organisationUuid: ORG,
|
|
640
|
+
workspaceUuid: WS,
|
|
641
|
+
noticeUuid,
|
|
642
|
+
signal
|
|
643
|
+
});
|
|
644
|
+
};
|
|
645
|
+
var isNoticeFullyConsented = (status) => status.consented && status.all_mandatory_active;
|
|
646
|
+
var buildLedgerConsentOverlay = (status, configPurposes) => {
|
|
647
|
+
var _a, _b;
|
|
648
|
+
const byUuid = new Map(status.purposes.map((p) => [p.uuid, p]));
|
|
649
|
+
const hasHistory = status.purposes.some((p) => (p.status || "INACTIVE") !== "INACTIVE");
|
|
650
|
+
if (!hasHistory) return null;
|
|
651
|
+
const purpose_selections = {};
|
|
652
|
+
for (const cp of configPurposes) {
|
|
653
|
+
const lp = byUuid.get(cp.uuid);
|
|
654
|
+
const purposeStatus = (lp == null ? void 0 : lp.status) || "INACTIVE";
|
|
655
|
+
const selectedByUuid = new Map(((lp == null ? void 0 : lp.data_elements) || []).map((de) => [de.uuid, de.selected]));
|
|
656
|
+
const data_elements = {};
|
|
657
|
+
for (const de of cp.data_elements) {
|
|
658
|
+
data_elements[de.uuid] = {
|
|
659
|
+
selected: (_a = selectedByUuid.get(de.uuid)) != null ? _a : false,
|
|
660
|
+
enabled: de.enabled,
|
|
661
|
+
required: de.required
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
purpose_selections[cp.uuid] = {
|
|
665
|
+
selected: (_b = lp == null ? void 0 : lp.selected) != null ? _b : false,
|
|
666
|
+
status: purposeStatus,
|
|
667
|
+
needs_reconsent: purposeStatus !== "ACTIVE",
|
|
668
|
+
data_elements
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
const reconsent_required = configPurposes.some(
|
|
672
|
+
(cp) => {
|
|
673
|
+
var _a2;
|
|
674
|
+
return (((_a2 = purpose_selections[cp.uuid]) == null ? void 0 : _a2.status) || "INACTIVE") !== "ACTIVE";
|
|
675
|
+
}
|
|
676
|
+
);
|
|
677
|
+
return { reconsent_required, purpose_selections };
|
|
678
|
+
};
|
|
679
|
+
|
|
589
680
|
// src/styles/inject.ts
|
|
590
681
|
var STYLE_ID = "redacto-consent-checkbox-styles";
|
|
591
682
|
var STYLE_CONTENT = `
|
|
@@ -1325,7 +1416,6 @@ var LANGUAGE_NATIVE_LABELS = {
|
|
|
1325
1416
|
English: "English",
|
|
1326
1417
|
Assamese: "\u0985\u09B8\u09AE\u09C0\u09AF\u09BC\u09BE (Assamese)",
|
|
1327
1418
|
Bengali: "\u09AC\u09BE\u0982\u09B2\u09BE (Bengali)",
|
|
1328
|
-
Bhojpuri: "\u092D\u094B\u091C\u092A\u0941\u0930\u0940 (Bhojpuri)",
|
|
1329
1419
|
Bodo: "\u092C\u0921\u093C\u094B (Bodo)",
|
|
1330
1420
|
Dogri: "\u0921\u094B\u0917\u0930\u0940 (Dogri)",
|
|
1331
1421
|
"Goan Konkani": "\u0915\u094B\u0902\u0915\u0923\u0940 (Goan Konkani)",
|
|
@@ -2728,7 +2818,7 @@ var ModalController = class {
|
|
|
2728
2818
|
}
|
|
2729
2819
|
// ─── Private ───
|
|
2730
2820
|
async fetchNotice() {
|
|
2731
|
-
var _a, _b, _c;
|
|
2821
|
+
var _a, _b, _c, _d;
|
|
2732
2822
|
this.abortController = new AbortController();
|
|
2733
2823
|
this.store.setState({ isLoading: true, fetchError: null });
|
|
2734
2824
|
try {
|
|
@@ -2744,7 +2834,23 @@ var ModalController = class {
|
|
|
2744
2834
|
signal: this.abortController.signal
|
|
2745
2835
|
});
|
|
2746
2836
|
const activeConfig = data.detail.active_config;
|
|
2747
|
-
|
|
2837
|
+
let purposeSelections = data.detail.purpose_selections;
|
|
2838
|
+
let reconsentRequired = data.detail.reconsent_required;
|
|
2839
|
+
let ledgerOverlayApplied = false;
|
|
2840
|
+
if (this.options.ledgerBaseUrl && !this.options.applicationId) {
|
|
2841
|
+
const ledgerStatus = await fetchNoticeConsentStatus({
|
|
2842
|
+
accessToken: this.options.accessToken,
|
|
2843
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
2844
|
+
noticeUuid: this.options.noticeId,
|
|
2845
|
+
signal: this.abortController.signal
|
|
2846
|
+
});
|
|
2847
|
+
if (ledgerStatus) {
|
|
2848
|
+
const overlay = buildLedgerConsentOverlay(ledgerStatus, activeConfig.purposes);
|
|
2849
|
+
reconsentRequired = (_a = overlay == null ? void 0 : overlay.reconsent_required) != null ? _a : false;
|
|
2850
|
+
purposeSelections = overlay == null ? void 0 : overlay.purpose_selections;
|
|
2851
|
+
ledgerOverlayApplied = Boolean(overlay);
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2748
2854
|
const initialCollapsed = {};
|
|
2749
2855
|
const initialPurposes = {};
|
|
2750
2856
|
const initialElements = {};
|
|
@@ -2752,7 +2858,7 @@ var ModalController = class {
|
|
|
2752
2858
|
let needsConsentPurposes = [];
|
|
2753
2859
|
let alreadyConsentedPurposes = [];
|
|
2754
2860
|
let isFullyConsentedReviewMode = false;
|
|
2755
|
-
if (purposeSelections &&
|
|
2861
|
+
if (purposeSelections && (reconsentRequired || ledgerOverlayApplied)) {
|
|
2756
2862
|
for (const purpose of activeConfig.purposes) {
|
|
2757
2863
|
const selection = purposeSelections[purpose.uuid];
|
|
2758
2864
|
initialCollapsed[purpose.uuid] = true;
|
|
@@ -2761,7 +2867,7 @@ var ModalController = class {
|
|
|
2761
2867
|
initialPurposes[purpose.uuid] = true;
|
|
2762
2868
|
for (const de of purpose.data_elements) {
|
|
2763
2869
|
const combinedId = `${purpose.uuid}-${de.uuid}`;
|
|
2764
|
-
const deSelection = (
|
|
2870
|
+
const deSelection = (_b = selection.data_elements) == null ? void 0 : _b[de.uuid];
|
|
2765
2871
|
const wasSelected = (deSelection == null ? void 0 : deSelection.selected) || false;
|
|
2766
2872
|
initialElements[combinedId] = wasSelected;
|
|
2767
2873
|
initialDataElementSelections[combinedId] = wasSelected;
|
|
@@ -2850,7 +2956,7 @@ var ModalController = class {
|
|
|
2850
2956
|
this.options.onAccept();
|
|
2851
2957
|
} else {
|
|
2852
2958
|
this.store.setState({ fetchError: error, isLoading: false });
|
|
2853
|
-
(
|
|
2959
|
+
(_d = (_c = this.options).onError) == null ? void 0 : _d.call(_c, error);
|
|
2854
2960
|
}
|
|
2855
2961
|
}
|
|
2856
2962
|
}
|
|
@@ -3558,6 +3664,7 @@ var ModalController = class {
|
|
|
3558
3664
|
await submitConsentEvent({
|
|
3559
3665
|
accessToken: this.options.accessToken,
|
|
3560
3666
|
baseUrl: this.options.baseUrl,
|
|
3667
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
3561
3668
|
noticeUuid: state.content.detail.uuid,
|
|
3562
3669
|
purposes,
|
|
3563
3670
|
declined: false,
|
|
@@ -4308,6 +4415,7 @@ var InlineController = class {
|
|
|
4308
4415
|
this.purposeRefs = null;
|
|
4309
4416
|
this.errorEl = null;
|
|
4310
4417
|
this.hasSubmitted = false;
|
|
4418
|
+
this.hasCheckedLedgerConsent = false;
|
|
4311
4419
|
this.unsubscribe = null;
|
|
4312
4420
|
this.options = options;
|
|
4313
4421
|
this.store = new Store({
|
|
@@ -4319,6 +4427,7 @@ var InlineController = class {
|
|
|
4319
4427
|
selectedPurposes: {},
|
|
4320
4428
|
selectedDataElements: {},
|
|
4321
4429
|
hasAlreadyConsented: false,
|
|
4430
|
+
isCheckingConsent: false,
|
|
4322
4431
|
fetchError: null,
|
|
4323
4432
|
errorMessage: null
|
|
4324
4433
|
});
|
|
@@ -4361,6 +4470,10 @@ var InlineController = class {
|
|
|
4361
4470
|
}
|
|
4362
4471
|
updateToken(accessToken) {
|
|
4363
4472
|
this.options = { ...this.options, accessToken };
|
|
4473
|
+
if (this.shouldCheckLedgerConsent()) {
|
|
4474
|
+
void this.checkLedgerConsent();
|
|
4475
|
+
return;
|
|
4476
|
+
}
|
|
4364
4477
|
this.tryAutoSubmit();
|
|
4365
4478
|
}
|
|
4366
4479
|
// ─── Private ───
|
|
@@ -4387,6 +4500,7 @@ var InlineController = class {
|
|
|
4387
4500
|
initialElements[`${purpose.uuid}-${de.uuid}`] = false;
|
|
4388
4501
|
}
|
|
4389
4502
|
}
|
|
4503
|
+
void this.checkLedgerConsent();
|
|
4390
4504
|
this.store.setState({
|
|
4391
4505
|
content: activeConfig,
|
|
4392
4506
|
isLoading: false,
|
|
@@ -4406,6 +4520,35 @@ var InlineController = class {
|
|
|
4406
4520
|
}
|
|
4407
4521
|
}
|
|
4408
4522
|
}
|
|
4523
|
+
shouldCheckLedgerConsent() {
|
|
4524
|
+
return Boolean(this.options.ledgerBaseUrl) && !this.options.applicationId && Boolean(this.options.accessToken) && !this.hasCheckedLedgerConsent;
|
|
4525
|
+
}
|
|
4526
|
+
// Ask the Go ledger whether this principal has already fully consented —
|
|
4527
|
+
// the ledger equivalent of the legacy Python 409. Skipped without a ledger
|
|
4528
|
+
// (the 409 path governs) and for the magic-link applicationId flow. Any
|
|
4529
|
+
// failure returns null and falls through to the normal flow.
|
|
4530
|
+
async checkLedgerConsent() {
|
|
4531
|
+
var _a, _b;
|
|
4532
|
+
if (!this.shouldCheckLedgerConsent()) return;
|
|
4533
|
+
this.hasCheckedLedgerConsent = true;
|
|
4534
|
+
this.store.setState({ isCheckingConsent: true });
|
|
4535
|
+
try {
|
|
4536
|
+
const status = await fetchNoticeConsentStatusForScope({
|
|
4537
|
+
accessToken: this.options.accessToken,
|
|
4538
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
4539
|
+
organisationUuid: this.options.org_uuid,
|
|
4540
|
+
workspaceUuid: this.options.workspace_uuid,
|
|
4541
|
+
noticeUuid: this.options.notice_uuid
|
|
4542
|
+
});
|
|
4543
|
+
if (status && isNoticeFullyConsented(status)) {
|
|
4544
|
+
this.store.setState({ hasAlreadyConsented: true });
|
|
4545
|
+
(_b = (_a = this.options).onAccept) == null ? void 0 : _b.call(_a);
|
|
4546
|
+
}
|
|
4547
|
+
} finally {
|
|
4548
|
+
this.store.setState({ isCheckingConsent: false });
|
|
4549
|
+
this.tryAutoSubmit();
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4409
4552
|
handleStateChange(state, prevState) {
|
|
4410
4553
|
if (prevState.isLoading && !state.isLoading && state.content) {
|
|
4411
4554
|
this.rebuildUI();
|
|
@@ -4627,7 +4770,7 @@ var InlineController = class {
|
|
|
4627
4770
|
var _a, _b;
|
|
4628
4771
|
const state = this.store.getState();
|
|
4629
4772
|
const accessToken = this.options.accessToken;
|
|
4630
|
-
if (accessToken && state.content && this.areAllRequiredElementsChecked() && !state.isSubmitting && !this.hasSubmitted) {
|
|
4773
|
+
if (accessToken && state.content && this.areAllRequiredElementsChecked() && !state.isSubmitting && !state.isCheckingConsent && !state.hasAlreadyConsented && !this.hasSubmitted) {
|
|
4631
4774
|
this.hasSubmitted = true;
|
|
4632
4775
|
try {
|
|
4633
4776
|
await this.handleSubmit(accessToken);
|
|
@@ -4652,6 +4795,7 @@ var InlineController = class {
|
|
|
4652
4795
|
workspace_uuid: this.options.workspace_uuid,
|
|
4653
4796
|
accessToken: tokenToUse,
|
|
4654
4797
|
baseUrl: this.options.baseUrl,
|
|
4798
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
4655
4799
|
noticeUuid: content.notice_uuid,
|
|
4656
4800
|
purposes: content.purposes.map((purpose) => {
|
|
4657
4801
|
const hasSelectedRequired = purpose.data_elements.some(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redacto.io/consent-sdk-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-beta.0",
|
|
4
4
|
"main": "dist/redacto.js",
|
|
5
5
|
"module": "dist/redacto.mjs",
|
|
6
6
|
"types": "dist/redacto.d.ts",
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
"jwt-decode": "^4.0.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@vitest/coverage-v8": "
|
|
36
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
37
37
|
"jsdom": "^26.1.0",
|
|
38
38
|
"tsup": "^8.4.0",
|
|
39
39
|
"typescript": "5.8.2",
|
|
40
|
-
"vitest": "^
|
|
40
|
+
"vitest": "^4.1.8"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"build": "tsup",
|