@redacto.io/consent-sdk-js 0.1.2-beta.0 → 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.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 = `
@@ -2757,7 +2848,7 @@ var ModalController = class {
2757
2848
  }
2758
2849
  // ─── Private ───
2759
2850
  async fetchNotice() {
2760
- var _a, _b, _c;
2851
+ var _a, _b, _c, _d;
2761
2852
  this.abortController = new AbortController();
2762
2853
  this.store.setState({ isLoading: true, fetchError: null });
2763
2854
  try {
@@ -2773,7 +2864,23 @@ var ModalController = class {
2773
2864
  signal: this.abortController.signal
2774
2865
  });
2775
2866
  const activeConfig = data.detail.active_config;
2776
- const purposeSelections = data.detail.purpose_selections;
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
+ }
2777
2884
  const initialCollapsed = {};
2778
2885
  const initialPurposes = {};
2779
2886
  const initialElements = {};
@@ -2781,7 +2888,7 @@ var ModalController = class {
2781
2888
  let needsConsentPurposes = [];
2782
2889
  let alreadyConsentedPurposes = [];
2783
2890
  let isFullyConsentedReviewMode = false;
2784
- if (purposeSelections && data.detail.reconsent_required) {
2891
+ if (purposeSelections && (reconsentRequired || ledgerOverlayApplied)) {
2785
2892
  for (const purpose of activeConfig.purposes) {
2786
2893
  const selection = purposeSelections[purpose.uuid];
2787
2894
  initialCollapsed[purpose.uuid] = true;
@@ -2790,7 +2897,7 @@ var ModalController = class {
2790
2897
  initialPurposes[purpose.uuid] = true;
2791
2898
  for (const de of purpose.data_elements) {
2792
2899
  const combinedId = `${purpose.uuid}-${de.uuid}`;
2793
- const deSelection = (_a = selection.data_elements) == null ? void 0 : _a[de.uuid];
2900
+ const deSelection = (_b = selection.data_elements) == null ? void 0 : _b[de.uuid];
2794
2901
  const wasSelected = (deSelection == null ? void 0 : deSelection.selected) || false;
2795
2902
  initialElements[combinedId] = wasSelected;
2796
2903
  initialDataElementSelections[combinedId] = wasSelected;
@@ -2879,7 +2986,7 @@ var ModalController = class {
2879
2986
  this.options.onAccept();
2880
2987
  } else {
2881
2988
  this.store.setState({ fetchError: error, isLoading: false });
2882
- (_c = (_b = this.options).onError) == null ? void 0 : _c.call(_b, error);
2989
+ (_d = (_c = this.options).onError) == null ? void 0 : _d.call(_c, error);
2883
2990
  }
2884
2991
  }
2885
2992
  }
@@ -3587,6 +3694,7 @@ var ModalController = class {
3587
3694
  await submitConsentEvent({
3588
3695
  accessToken: this.options.accessToken,
3589
3696
  baseUrl: this.options.baseUrl,
3697
+ ledgerBaseUrl: this.options.ledgerBaseUrl,
3590
3698
  noticeUuid: state.content.detail.uuid,
3591
3699
  purposes,
3592
3700
  declined: false,
@@ -4337,6 +4445,7 @@ var InlineController = class {
4337
4445
  this.purposeRefs = null;
4338
4446
  this.errorEl = null;
4339
4447
  this.hasSubmitted = false;
4448
+ this.hasCheckedLedgerConsent = false;
4340
4449
  this.unsubscribe = null;
4341
4450
  this.options = options;
4342
4451
  this.store = new Store({
@@ -4348,6 +4457,7 @@ var InlineController = class {
4348
4457
  selectedPurposes: {},
4349
4458
  selectedDataElements: {},
4350
4459
  hasAlreadyConsented: false,
4460
+ isCheckingConsent: false,
4351
4461
  fetchError: null,
4352
4462
  errorMessage: null
4353
4463
  });
@@ -4390,6 +4500,10 @@ var InlineController = class {
4390
4500
  }
4391
4501
  updateToken(accessToken) {
4392
4502
  this.options = { ...this.options, accessToken };
4503
+ if (this.shouldCheckLedgerConsent()) {
4504
+ void this.checkLedgerConsent();
4505
+ return;
4506
+ }
4393
4507
  this.tryAutoSubmit();
4394
4508
  }
4395
4509
  // ─── Private ───
@@ -4416,6 +4530,7 @@ var InlineController = class {
4416
4530
  initialElements[`${purpose.uuid}-${de.uuid}`] = false;
4417
4531
  }
4418
4532
  }
4533
+ void this.checkLedgerConsent();
4419
4534
  this.store.setState({
4420
4535
  content: activeConfig,
4421
4536
  isLoading: false,
@@ -4435,6 +4550,35 @@ var InlineController = class {
4435
4550
  }
4436
4551
  }
4437
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
+ }
4438
4582
  handleStateChange(state, prevState) {
4439
4583
  if (prevState.isLoading && !state.isLoading && state.content) {
4440
4584
  this.rebuildUI();
@@ -4656,7 +4800,7 @@ var InlineController = class {
4656
4800
  var _a, _b;
4657
4801
  const state = this.store.getState();
4658
4802
  const accessToken = this.options.accessToken;
4659
- 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) {
4660
4804
  this.hasSubmitted = true;
4661
4805
  try {
4662
4806
  await this.handleSubmit(accessToken);
@@ -4681,6 +4825,7 @@ var InlineController = class {
4681
4825
  workspace_uuid: this.options.workspace_uuid,
4682
4826
  accessToken: tokenToUse,
4683
4827
  baseUrl: this.options.baseUrl,
4828
+ ledgerBaseUrl: this.options.ledgerBaseUrl,
4684
4829
  noticeUuid: content.notice_uuid,
4685
4830
  purposes: content.purposes.map((purpose) => {
4686
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 = `
@@ -2727,7 +2818,7 @@ var ModalController = class {
2727
2818
  }
2728
2819
  // ─── Private ───
2729
2820
  async fetchNotice() {
2730
- var _a, _b, _c;
2821
+ var _a, _b, _c, _d;
2731
2822
  this.abortController = new AbortController();
2732
2823
  this.store.setState({ isLoading: true, fetchError: null });
2733
2824
  try {
@@ -2743,7 +2834,23 @@ var ModalController = class {
2743
2834
  signal: this.abortController.signal
2744
2835
  });
2745
2836
  const activeConfig = data.detail.active_config;
2746
- const purposeSelections = data.detail.purpose_selections;
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
+ }
2747
2854
  const initialCollapsed = {};
2748
2855
  const initialPurposes = {};
2749
2856
  const initialElements = {};
@@ -2751,7 +2858,7 @@ var ModalController = class {
2751
2858
  let needsConsentPurposes = [];
2752
2859
  let alreadyConsentedPurposes = [];
2753
2860
  let isFullyConsentedReviewMode = false;
2754
- if (purposeSelections && data.detail.reconsent_required) {
2861
+ if (purposeSelections && (reconsentRequired || ledgerOverlayApplied)) {
2755
2862
  for (const purpose of activeConfig.purposes) {
2756
2863
  const selection = purposeSelections[purpose.uuid];
2757
2864
  initialCollapsed[purpose.uuid] = true;
@@ -2760,7 +2867,7 @@ var ModalController = class {
2760
2867
  initialPurposes[purpose.uuid] = true;
2761
2868
  for (const de of purpose.data_elements) {
2762
2869
  const combinedId = `${purpose.uuid}-${de.uuid}`;
2763
- const deSelection = (_a = selection.data_elements) == null ? void 0 : _a[de.uuid];
2870
+ const deSelection = (_b = selection.data_elements) == null ? void 0 : _b[de.uuid];
2764
2871
  const wasSelected = (deSelection == null ? void 0 : deSelection.selected) || false;
2765
2872
  initialElements[combinedId] = wasSelected;
2766
2873
  initialDataElementSelections[combinedId] = wasSelected;
@@ -2849,7 +2956,7 @@ var ModalController = class {
2849
2956
  this.options.onAccept();
2850
2957
  } else {
2851
2958
  this.store.setState({ fetchError: error, isLoading: false });
2852
- (_c = (_b = this.options).onError) == null ? void 0 : _c.call(_b, error);
2959
+ (_d = (_c = this.options).onError) == null ? void 0 : _d.call(_c, error);
2853
2960
  }
2854
2961
  }
2855
2962
  }
@@ -3557,6 +3664,7 @@ var ModalController = class {
3557
3664
  await submitConsentEvent({
3558
3665
  accessToken: this.options.accessToken,
3559
3666
  baseUrl: this.options.baseUrl,
3667
+ ledgerBaseUrl: this.options.ledgerBaseUrl,
3560
3668
  noticeUuid: state.content.detail.uuid,
3561
3669
  purposes,
3562
3670
  declined: false,
@@ -4307,6 +4415,7 @@ var InlineController = class {
4307
4415
  this.purposeRefs = null;
4308
4416
  this.errorEl = null;
4309
4417
  this.hasSubmitted = false;
4418
+ this.hasCheckedLedgerConsent = false;
4310
4419
  this.unsubscribe = null;
4311
4420
  this.options = options;
4312
4421
  this.store = new Store({
@@ -4318,6 +4427,7 @@ var InlineController = class {
4318
4427
  selectedPurposes: {},
4319
4428
  selectedDataElements: {},
4320
4429
  hasAlreadyConsented: false,
4430
+ isCheckingConsent: false,
4321
4431
  fetchError: null,
4322
4432
  errorMessage: null
4323
4433
  });
@@ -4360,6 +4470,10 @@ var InlineController = class {
4360
4470
  }
4361
4471
  updateToken(accessToken) {
4362
4472
  this.options = { ...this.options, accessToken };
4473
+ if (this.shouldCheckLedgerConsent()) {
4474
+ void this.checkLedgerConsent();
4475
+ return;
4476
+ }
4363
4477
  this.tryAutoSubmit();
4364
4478
  }
4365
4479
  // ─── Private ───
@@ -4386,6 +4500,7 @@ var InlineController = class {
4386
4500
  initialElements[`${purpose.uuid}-${de.uuid}`] = false;
4387
4501
  }
4388
4502
  }
4503
+ void this.checkLedgerConsent();
4389
4504
  this.store.setState({
4390
4505
  content: activeConfig,
4391
4506
  isLoading: false,
@@ -4405,6 +4520,35 @@ var InlineController = class {
4405
4520
  }
4406
4521
  }
4407
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
+ }
4408
4552
  handleStateChange(state, prevState) {
4409
4553
  if (prevState.isLoading && !state.isLoading && state.content) {
4410
4554
  this.rebuildUI();
@@ -4626,7 +4770,7 @@ var InlineController = class {
4626
4770
  var _a, _b;
4627
4771
  const state = this.store.getState();
4628
4772
  const accessToken = this.options.accessToken;
4629
- 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) {
4630
4774
  this.hasSubmitted = true;
4631
4775
  try {
4632
4776
  await this.handleSubmit(accessToken);
@@ -4651,6 +4795,7 @@ var InlineController = class {
4651
4795
  workspace_uuid: this.options.workspace_uuid,
4652
4796
  accessToken: tokenToUse,
4653
4797
  baseUrl: this.options.baseUrl,
4798
+ ledgerBaseUrl: this.options.ledgerBaseUrl,
4654
4799
  noticeUuid: content.notice_uuid,
4655
4800
  purposes: content.purposes.map((purpose) => {
4656
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.1.2-beta.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",