@redacto.io/consent-sdk-react 10.2.0-beta.0 → 10.2.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/chunk-6EET4YOY.mjs +93 -0
- package/dist/index.js +126 -30
- package/dist/index.mjs +68 -29
- package/dist/privacy-center.js +71 -5
- package/dist/privacy-center.mjs +16 -7
- package/package.json +1 -1
- package/dist/chunk-OGL6OC3A.mjs +0 -34
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// src/shared/idempotency.ts
|
|
2
|
+
var HEADER = "Idempotency-Key";
|
|
3
|
+
var pending = /* @__PURE__ */ new Map();
|
|
4
|
+
var MAX_PENDING = 16;
|
|
5
|
+
var mintKey = () => {
|
|
6
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
7
|
+
return crypto.randomUUID();
|
|
8
|
+
}
|
|
9
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 14)}-${Math.random().toString(36).slice(2, 14)}`;
|
|
10
|
+
};
|
|
11
|
+
var statusOf = (outcome) => {
|
|
12
|
+
if (typeof Response !== "undefined" && outcome instanceof Response) {
|
|
13
|
+
return outcome.status;
|
|
14
|
+
}
|
|
15
|
+
if (outcome && typeof outcome === "object" && "status" in outcome) {
|
|
16
|
+
const status = outcome.status;
|
|
17
|
+
if (typeof status === "number") return status;
|
|
18
|
+
}
|
|
19
|
+
return void 0;
|
|
20
|
+
};
|
|
21
|
+
var settledOnResolve = (result) => {
|
|
22
|
+
const status = statusOf(result);
|
|
23
|
+
return status === void 0 || status < 500;
|
|
24
|
+
};
|
|
25
|
+
var settledOnReject = (error) => {
|
|
26
|
+
const status = statusOf(error);
|
|
27
|
+
return status !== void 0 && status < 500;
|
|
28
|
+
};
|
|
29
|
+
var withIdempotencyKey = async (url, body, send) => {
|
|
30
|
+
const signature = `${url}
|
|
31
|
+
${body}`;
|
|
32
|
+
let entry = pending.get(signature);
|
|
33
|
+
if (!entry) {
|
|
34
|
+
entry = { key: mintKey(), inFlight: 0, keep: false };
|
|
35
|
+
if (pending.size >= MAX_PENDING) {
|
|
36
|
+
const oldest = pending.keys().next().value;
|
|
37
|
+
if (oldest !== void 0) pending.delete(oldest);
|
|
38
|
+
}
|
|
39
|
+
pending.set(signature, entry);
|
|
40
|
+
} else if (entry.inFlight === 0) {
|
|
41
|
+
entry.keep = false;
|
|
42
|
+
}
|
|
43
|
+
entry.inFlight += 1;
|
|
44
|
+
const settle = (settled) => {
|
|
45
|
+
entry.inFlight -= 1;
|
|
46
|
+
if (!settled) entry.keep = true;
|
|
47
|
+
if (entry.inFlight === 0 && !entry.keep) pending.delete(signature);
|
|
48
|
+
};
|
|
49
|
+
try {
|
|
50
|
+
const result = await send({ [HEADER]: entry.key });
|
|
51
|
+
settle(settledOnResolve(result));
|
|
52
|
+
return result;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
settle(settledOnReject(error));
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// src/RedactoNoticeConsent/useMediaQuery.ts
|
|
60
|
+
import { useState, useEffect } from "react";
|
|
61
|
+
var useMediaQuery = (query) => {
|
|
62
|
+
const getMatches = () => typeof window !== "undefined" ? window.matchMedia(query).matches : false;
|
|
63
|
+
const [matches, setMatches] = useState(getMatches);
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (typeof window === "undefined") return;
|
|
66
|
+
const media = window.matchMedia(query);
|
|
67
|
+
const listener = (e) => {
|
|
68
|
+
setMatches(e.matches);
|
|
69
|
+
};
|
|
70
|
+
if (media.addEventListener) {
|
|
71
|
+
media.addEventListener("change", listener);
|
|
72
|
+
} else {
|
|
73
|
+
media.addListener(listener);
|
|
74
|
+
}
|
|
75
|
+
return () => {
|
|
76
|
+
if (media.removeEventListener) {
|
|
77
|
+
media.removeEventListener("change", listener);
|
|
78
|
+
} else {
|
|
79
|
+
media.removeListener(listener);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}, [query]);
|
|
83
|
+
return matches;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// src/shared/sandbox.ts
|
|
87
|
+
var SANDBOX_TOKEN_HEADER = "X-Consent-Token";
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
SANDBOX_TOKEN_HEADER,
|
|
91
|
+
withIdempotencyKey,
|
|
92
|
+
useMediaQuery
|
|
93
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -219,6 +219,64 @@ var sandboxBodyIdentity = (sandbox) => {
|
|
|
219
219
|
};
|
|
220
220
|
var sandboxQueryIdentity = (sandbox) => sandboxBodyIdentity(sandbox);
|
|
221
221
|
|
|
222
|
+
// src/shared/idempotency.ts
|
|
223
|
+
var HEADER = "Idempotency-Key";
|
|
224
|
+
var pending = /* @__PURE__ */ new Map();
|
|
225
|
+
var MAX_PENDING = 16;
|
|
226
|
+
var mintKey = () => {
|
|
227
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
228
|
+
return crypto.randomUUID();
|
|
229
|
+
}
|
|
230
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 14)}-${Math.random().toString(36).slice(2, 14)}`;
|
|
231
|
+
};
|
|
232
|
+
var statusOf = (outcome) => {
|
|
233
|
+
if (typeof Response !== "undefined" && outcome instanceof Response) {
|
|
234
|
+
return outcome.status;
|
|
235
|
+
}
|
|
236
|
+
if (outcome && typeof outcome === "object" && "status" in outcome) {
|
|
237
|
+
const status = outcome.status;
|
|
238
|
+
if (typeof status === "number") return status;
|
|
239
|
+
}
|
|
240
|
+
return void 0;
|
|
241
|
+
};
|
|
242
|
+
var settledOnResolve = (result) => {
|
|
243
|
+
const status = statusOf(result);
|
|
244
|
+
return status === void 0 || status < 500;
|
|
245
|
+
};
|
|
246
|
+
var settledOnReject = (error) => {
|
|
247
|
+
const status = statusOf(error);
|
|
248
|
+
return status !== void 0 && status < 500;
|
|
249
|
+
};
|
|
250
|
+
var withIdempotencyKey = async (url, body, send) => {
|
|
251
|
+
const signature = `${url}
|
|
252
|
+
${body}`;
|
|
253
|
+
let entry = pending.get(signature);
|
|
254
|
+
if (!entry) {
|
|
255
|
+
entry = { key: mintKey(), inFlight: 0, keep: false };
|
|
256
|
+
if (pending.size >= MAX_PENDING) {
|
|
257
|
+
const oldest = pending.keys().next().value;
|
|
258
|
+
if (oldest !== void 0) pending.delete(oldest);
|
|
259
|
+
}
|
|
260
|
+
pending.set(signature, entry);
|
|
261
|
+
} else if (entry.inFlight === 0) {
|
|
262
|
+
entry.keep = false;
|
|
263
|
+
}
|
|
264
|
+
entry.inFlight += 1;
|
|
265
|
+
const settle = (settled) => {
|
|
266
|
+
entry.inFlight -= 1;
|
|
267
|
+
if (!settled) entry.keep = true;
|
|
268
|
+
if (entry.inFlight === 0 && !entry.keep) pending.delete(signature);
|
|
269
|
+
};
|
|
270
|
+
try {
|
|
271
|
+
const result = await send({ [HEADER]: entry.key });
|
|
272
|
+
settle(settledOnResolve(result));
|
|
273
|
+
return result;
|
|
274
|
+
} catch (error) {
|
|
275
|
+
settle(settledOnReject(error));
|
|
276
|
+
throw error;
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
222
280
|
// src/shared/notice-consent-status.ts
|
|
223
281
|
var import_jwt_decode = require("jwt-decode");
|
|
224
282
|
var decodeNoticeConsentToken = (token) => {
|
|
@@ -439,17 +497,21 @@ var submitConsentEvent = async ({
|
|
|
439
497
|
self_declared_adult: selfDeclaredAdult
|
|
440
498
|
};
|
|
441
499
|
const requestBody = sandbox ? { ...payload, ...sandboxBodyIdentity(sandbox) } : payload;
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
500
|
+
const url = `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/submit-consent`;
|
|
501
|
+
const serialisedBody = JSON.stringify(requestBody);
|
|
502
|
+
const response = await withIdempotencyKey(
|
|
503
|
+
url,
|
|
504
|
+
serialisedBody,
|
|
505
|
+
(idempotencyHeaders) => fetch(url, {
|
|
445
506
|
method: "POST",
|
|
446
507
|
headers: {
|
|
447
508
|
...buildAuthHeaders(accessToken, sandbox),
|
|
448
|
-
"Content-Type": "application/json"
|
|
509
|
+
"Content-Type": "application/json",
|
|
510
|
+
...idempotencyHeaders
|
|
449
511
|
},
|
|
450
|
-
body:
|
|
512
|
+
body: serialisedBody,
|
|
451
513
|
signal
|
|
452
|
-
}
|
|
514
|
+
})
|
|
453
515
|
);
|
|
454
516
|
if (!response.ok) {
|
|
455
517
|
throw await parseApiError(
|
|
@@ -912,6 +974,15 @@ var styles = {
|
|
|
912
974
|
padding: 0,
|
|
913
975
|
boxSizing: "border-box"
|
|
914
976
|
},
|
|
977
|
+
// The purposes belonging to one product. Indented so the product chevron and
|
|
978
|
+
// the purpose chevrons below it do not sit on the same vertical line, which
|
|
979
|
+
// read as one flat list rather than a heading and its contents. The trailing
|
|
980
|
+
// margin separates a section from the next product heading.
|
|
981
|
+
sectionBody: {
|
|
982
|
+
paddingLeft: "18px",
|
|
983
|
+
margin: "0 0 14px",
|
|
984
|
+
boxSizing: "border-box"
|
|
985
|
+
},
|
|
915
986
|
// No colour token: the header always overrides it with the notice's
|
|
916
987
|
// headingColor, so one here would imply a default that never applies.
|
|
917
988
|
sectionTitle: {
|
|
@@ -1067,10 +1138,10 @@ var styles = {
|
|
|
1067
1138
|
color: "#ffffff"
|
|
1068
1139
|
},
|
|
1069
1140
|
/**
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1141
|
+
* The accept-all shortcut. Tinted rather than filled so the primary
|
|
1142
|
+
* "accept selected" action stays visually dominant, and outlined-but-coloured
|
|
1143
|
+
* so it still reads as an accept next to the neutral decline button.
|
|
1144
|
+
*/
|
|
1074
1145
|
acceptSecondaryButton: {
|
|
1075
1146
|
backgroundColor: "#ffffff",
|
|
1076
1147
|
border: "1px solid #4f87ff",
|
|
@@ -1478,6 +1549,7 @@ var injectCheckboxStyles = () => {
|
|
|
1478
1549
|
cursor: pointer !important;
|
|
1479
1550
|
position: relative !important;
|
|
1480
1551
|
box-sizing: border-box !important;
|
|
1552
|
+
flex-shrink: 0 !important;
|
|
1481
1553
|
margin: 0 !important;
|
|
1482
1554
|
padding: 4px !important;
|
|
1483
1555
|
}
|
|
@@ -1494,6 +1566,7 @@ var injectCheckboxStyles = () => {
|
|
|
1494
1566
|
cursor: pointer !important;
|
|
1495
1567
|
position: relative !important;
|
|
1496
1568
|
box-sizing: border-box !important;
|
|
1569
|
+
flex-shrink: 0 !important;
|
|
1497
1570
|
margin: 0 !important;
|
|
1498
1571
|
padding: 4px !important;
|
|
1499
1572
|
}
|
|
@@ -1822,9 +1895,9 @@ var toFontFaceCss = (entry, sources) => sources.map(
|
|
|
1822
1895
|
).join("\n\n");
|
|
1823
1896
|
var fontSourceLoads = /* @__PURE__ */ new Map();
|
|
1824
1897
|
var loadFontSources = (entry, importSources) => {
|
|
1825
|
-
const
|
|
1826
|
-
if (
|
|
1827
|
-
return
|
|
1898
|
+
const pending2 = fontSourceLoads.get(entry.id);
|
|
1899
|
+
if (pending2) {
|
|
1900
|
+
return pending2;
|
|
1828
1901
|
}
|
|
1829
1902
|
const load = importSources().then((module2) => module2.NOTICE_FONT_SOURCES).catch((error) => {
|
|
1830
1903
|
fontSourceLoads.delete(entry.id);
|
|
@@ -3762,7 +3835,19 @@ var RedactoNoticeConsent = ({
|
|
|
3762
3835
|
{}
|
|
3763
3836
|
);
|
|
3764
3837
|
setCollapsedPurposes(initialCollapsedState);
|
|
3765
|
-
setCollapsedProducts(
|
|
3838
|
+
setCollapsedProducts(
|
|
3839
|
+
groupPurposesByProduct(
|
|
3840
|
+
activeConfig.products,
|
|
3841
|
+
activeConfig.purposes,
|
|
3842
|
+
activeConfig.product_purpose_order
|
|
3843
|
+
).reduce(
|
|
3844
|
+
(acc, group, index) => ({
|
|
3845
|
+
...acc,
|
|
3846
|
+
[group.product.uuid]: index > 0
|
|
3847
|
+
}),
|
|
3848
|
+
{}
|
|
3849
|
+
)
|
|
3850
|
+
);
|
|
3766
3851
|
const isReconsentRequired = consentContentData.detail.reconsent_required;
|
|
3767
3852
|
const purposeSelections = consentContentData.detail.purpose_selections;
|
|
3768
3853
|
const newCategorizedPurposes = isReconsentRequired ? {
|
|
@@ -5646,6 +5731,7 @@ var RedactoNoticeConsent = ({
|
|
|
5646
5731
|
"div",
|
|
5647
5732
|
{
|
|
5648
5733
|
id: `product-${group.product.uuid}`,
|
|
5734
|
+
style: styles.sectionBody,
|
|
5649
5735
|
hidden: collapsedProducts[group.product.uuid] || false,
|
|
5650
5736
|
children: group.purposes.map((purpose) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
5651
5737
|
PurposeItem,
|
|
@@ -5936,16 +6022,20 @@ var submitConsentEvent2 = async ({
|
|
|
5936
6022
|
declined,
|
|
5937
6023
|
meta_data
|
|
5938
6024
|
};
|
|
5939
|
-
const
|
|
5940
|
-
|
|
5941
|
-
|
|
6025
|
+
const url = `${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/submit-consent`;
|
|
6026
|
+
const serialisedBody = JSON.stringify(payload);
|
|
6027
|
+
const response = await withIdempotencyKey(
|
|
6028
|
+
url,
|
|
6029
|
+
serialisedBody,
|
|
6030
|
+
(idempotencyHeaders) => fetch(url, {
|
|
5942
6031
|
method: "POST",
|
|
5943
6032
|
headers: {
|
|
5944
6033
|
Authorization: `Bearer ${accessToken}`,
|
|
5945
|
-
"Content-Type": "application/json"
|
|
6034
|
+
"Content-Type": "application/json",
|
|
6035
|
+
...idempotencyHeaders
|
|
5946
6036
|
},
|
|
5947
|
-
body:
|
|
5948
|
-
}
|
|
6037
|
+
body: serialisedBody
|
|
6038
|
+
})
|
|
5949
6039
|
);
|
|
5950
6040
|
if (!response.ok) {
|
|
5951
6041
|
throw await parseApiError(
|
|
@@ -8115,16 +8205,22 @@ var submitAssistedConsent = async ({
|
|
|
8115
8205
|
language: language || void 0
|
|
8116
8206
|
};
|
|
8117
8207
|
const url = `${publicScope(ledgerBaseUrl || baseUrl, organisationUuid, workspaceUuid)}/submit-consent`;
|
|
8118
|
-
const
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8208
|
+
const serialisedBody = JSON.stringify(payload);
|
|
8209
|
+
const response = await withIdempotencyKey(
|
|
8210
|
+
url,
|
|
8211
|
+
serialisedBody,
|
|
8212
|
+
(idempotencyHeaders) => fetch(url, {
|
|
8213
|
+
method: "POST",
|
|
8214
|
+
headers: {
|
|
8215
|
+
accept: "application/json",
|
|
8216
|
+
Authorization: `Bearer ${accessToken}`,
|
|
8217
|
+
"Content-Type": "application/json",
|
|
8218
|
+
...idempotencyHeaders
|
|
8219
|
+
},
|
|
8220
|
+
body: serialisedBody,
|
|
8221
|
+
signal
|
|
8222
|
+
})
|
|
8223
|
+
);
|
|
8128
8224
|
if (!response.ok) {
|
|
8129
8225
|
throw await parseApiError(
|
|
8130
8226
|
response,
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SANDBOX_TOKEN_HEADER,
|
|
3
|
-
useMediaQuery
|
|
4
|
-
|
|
3
|
+
useMediaQuery,
|
|
4
|
+
withIdempotencyKey
|
|
5
|
+
} from "./chunk-6EET4YOY.mjs";
|
|
5
6
|
import {
|
|
6
7
|
FONT_FAMILY_SEPARATOR,
|
|
7
8
|
FONT_NAME_QUOTE_CHARACTERS,
|
|
@@ -372,17 +373,21 @@ var submitConsentEvent = async ({
|
|
|
372
373
|
self_declared_adult: selfDeclaredAdult
|
|
373
374
|
};
|
|
374
375
|
const requestBody = sandbox ? { ...payload, ...sandboxBodyIdentity(sandbox) } : payload;
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
376
|
+
const url = `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/submit-consent`;
|
|
377
|
+
const serialisedBody = JSON.stringify(requestBody);
|
|
378
|
+
const response = await withIdempotencyKey(
|
|
379
|
+
url,
|
|
380
|
+
serialisedBody,
|
|
381
|
+
(idempotencyHeaders) => fetch(url, {
|
|
378
382
|
method: "POST",
|
|
379
383
|
headers: {
|
|
380
384
|
...buildAuthHeaders(accessToken, sandbox),
|
|
381
|
-
"Content-Type": "application/json"
|
|
385
|
+
"Content-Type": "application/json",
|
|
386
|
+
...idempotencyHeaders
|
|
382
387
|
},
|
|
383
|
-
body:
|
|
388
|
+
body: serialisedBody,
|
|
384
389
|
signal
|
|
385
|
-
}
|
|
390
|
+
})
|
|
386
391
|
);
|
|
387
392
|
if (!response.ok) {
|
|
388
393
|
throw await parseApiError(
|
|
@@ -845,6 +850,15 @@ var styles = {
|
|
|
845
850
|
padding: 0,
|
|
846
851
|
boxSizing: "border-box"
|
|
847
852
|
},
|
|
853
|
+
// The purposes belonging to one product. Indented so the product chevron and
|
|
854
|
+
// the purpose chevrons below it do not sit on the same vertical line, which
|
|
855
|
+
// read as one flat list rather than a heading and its contents. The trailing
|
|
856
|
+
// margin separates a section from the next product heading.
|
|
857
|
+
sectionBody: {
|
|
858
|
+
paddingLeft: "18px",
|
|
859
|
+
margin: "0 0 14px",
|
|
860
|
+
boxSizing: "border-box"
|
|
861
|
+
},
|
|
848
862
|
// No colour token: the header always overrides it with the notice's
|
|
849
863
|
// headingColor, so one here would imply a default that never applies.
|
|
850
864
|
sectionTitle: {
|
|
@@ -1000,10 +1014,10 @@ var styles = {
|
|
|
1000
1014
|
color: "#ffffff"
|
|
1001
1015
|
},
|
|
1002
1016
|
/**
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1017
|
+
* The accept-all shortcut. Tinted rather than filled so the primary
|
|
1018
|
+
* "accept selected" action stays visually dominant, and outlined-but-coloured
|
|
1019
|
+
* so it still reads as an accept next to the neutral decline button.
|
|
1020
|
+
*/
|
|
1007
1021
|
acceptSecondaryButton: {
|
|
1008
1022
|
backgroundColor: "#ffffff",
|
|
1009
1023
|
border: "1px solid #4f87ff",
|
|
@@ -1411,6 +1425,7 @@ var injectCheckboxStyles = () => {
|
|
|
1411
1425
|
cursor: pointer !important;
|
|
1412
1426
|
position: relative !important;
|
|
1413
1427
|
box-sizing: border-box !important;
|
|
1428
|
+
flex-shrink: 0 !important;
|
|
1414
1429
|
margin: 0 !important;
|
|
1415
1430
|
padding: 4px !important;
|
|
1416
1431
|
}
|
|
@@ -1427,6 +1442,7 @@ var injectCheckboxStyles = () => {
|
|
|
1427
1442
|
cursor: pointer !important;
|
|
1428
1443
|
position: relative !important;
|
|
1429
1444
|
box-sizing: border-box !important;
|
|
1445
|
+
flex-shrink: 0 !important;
|
|
1430
1446
|
margin: 0 !important;
|
|
1431
1447
|
padding: 4px !important;
|
|
1432
1448
|
}
|
|
@@ -3494,7 +3510,19 @@ var RedactoNoticeConsent = ({
|
|
|
3494
3510
|
{}
|
|
3495
3511
|
);
|
|
3496
3512
|
setCollapsedPurposes(initialCollapsedState);
|
|
3497
|
-
setCollapsedProducts(
|
|
3513
|
+
setCollapsedProducts(
|
|
3514
|
+
groupPurposesByProduct(
|
|
3515
|
+
activeConfig.products,
|
|
3516
|
+
activeConfig.purposes,
|
|
3517
|
+
activeConfig.product_purpose_order
|
|
3518
|
+
).reduce(
|
|
3519
|
+
(acc, group, index) => ({
|
|
3520
|
+
...acc,
|
|
3521
|
+
[group.product.uuid]: index > 0
|
|
3522
|
+
}),
|
|
3523
|
+
{}
|
|
3524
|
+
)
|
|
3525
|
+
);
|
|
3498
3526
|
const isReconsentRequired = consentContentData.detail.reconsent_required;
|
|
3499
3527
|
const purposeSelections = consentContentData.detail.purpose_selections;
|
|
3500
3528
|
const newCategorizedPurposes = isReconsentRequired ? {
|
|
@@ -5378,6 +5406,7 @@ var RedactoNoticeConsent = ({
|
|
|
5378
5406
|
"div",
|
|
5379
5407
|
{
|
|
5380
5408
|
id: `product-${group.product.uuid}`,
|
|
5409
|
+
style: styles.sectionBody,
|
|
5381
5410
|
hidden: collapsedProducts[group.product.uuid] || false,
|
|
5382
5411
|
children: group.purposes.map((purpose) => /* @__PURE__ */ jsx3(
|
|
5383
5412
|
PurposeItem,
|
|
@@ -5668,16 +5697,20 @@ var submitConsentEvent2 = async ({
|
|
|
5668
5697
|
declined,
|
|
5669
5698
|
meta_data
|
|
5670
5699
|
};
|
|
5671
|
-
const
|
|
5672
|
-
|
|
5673
|
-
|
|
5700
|
+
const url = `${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/submit-consent`;
|
|
5701
|
+
const serialisedBody = JSON.stringify(payload);
|
|
5702
|
+
const response = await withIdempotencyKey(
|
|
5703
|
+
url,
|
|
5704
|
+
serialisedBody,
|
|
5705
|
+
(idempotencyHeaders) => fetch(url, {
|
|
5674
5706
|
method: "POST",
|
|
5675
5707
|
headers: {
|
|
5676
5708
|
Authorization: `Bearer ${accessToken}`,
|
|
5677
|
-
"Content-Type": "application/json"
|
|
5709
|
+
"Content-Type": "application/json",
|
|
5710
|
+
...idempotencyHeaders
|
|
5678
5711
|
},
|
|
5679
|
-
body:
|
|
5680
|
-
}
|
|
5712
|
+
body: serialisedBody
|
|
5713
|
+
})
|
|
5681
5714
|
);
|
|
5682
5715
|
if (!response.ok) {
|
|
5683
5716
|
throw await parseApiError(
|
|
@@ -7847,16 +7880,22 @@ var submitAssistedConsent = async ({
|
|
|
7847
7880
|
language: language || void 0
|
|
7848
7881
|
};
|
|
7849
7882
|
const url = `${publicScope(ledgerBaseUrl || baseUrl, organisationUuid, workspaceUuid)}/submit-consent`;
|
|
7850
|
-
const
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7883
|
+
const serialisedBody = JSON.stringify(payload);
|
|
7884
|
+
const response = await withIdempotencyKey(
|
|
7885
|
+
url,
|
|
7886
|
+
serialisedBody,
|
|
7887
|
+
(idempotencyHeaders) => fetch(url, {
|
|
7888
|
+
method: "POST",
|
|
7889
|
+
headers: {
|
|
7890
|
+
accept: "application/json",
|
|
7891
|
+
Authorization: `Bearer ${accessToken}`,
|
|
7892
|
+
"Content-Type": "application/json",
|
|
7893
|
+
...idempotencyHeaders
|
|
7894
|
+
},
|
|
7895
|
+
body: serialisedBody,
|
|
7896
|
+
signal
|
|
7897
|
+
})
|
|
7898
|
+
);
|
|
7860
7899
|
if (!response.ok) {
|
|
7861
7900
|
throw await parseApiError(
|
|
7862
7901
|
response,
|
package/dist/privacy-center.js
CHANGED
|
@@ -10032,6 +10032,64 @@ var useAuth = () => {
|
|
|
10032
10032
|
// src/RedactoPrivacyCenter/context/BaseUrlContext.tsx
|
|
10033
10033
|
var import_react4 = require("react");
|
|
10034
10034
|
|
|
10035
|
+
// src/shared/idempotency.ts
|
|
10036
|
+
var HEADER = "Idempotency-Key";
|
|
10037
|
+
var pending = /* @__PURE__ */ new Map();
|
|
10038
|
+
var MAX_PENDING = 16;
|
|
10039
|
+
var mintKey = () => {
|
|
10040
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
10041
|
+
return crypto.randomUUID();
|
|
10042
|
+
}
|
|
10043
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 14)}-${Math.random().toString(36).slice(2, 14)}`;
|
|
10044
|
+
};
|
|
10045
|
+
var statusOf = (outcome) => {
|
|
10046
|
+
if (typeof Response !== "undefined" && outcome instanceof Response) {
|
|
10047
|
+
return outcome.status;
|
|
10048
|
+
}
|
|
10049
|
+
if (outcome && typeof outcome === "object" && "status" in outcome) {
|
|
10050
|
+
const status = outcome.status;
|
|
10051
|
+
if (typeof status === "number") return status;
|
|
10052
|
+
}
|
|
10053
|
+
return void 0;
|
|
10054
|
+
};
|
|
10055
|
+
var settledOnResolve = (result) => {
|
|
10056
|
+
const status = statusOf(result);
|
|
10057
|
+
return status === void 0 || status < 500;
|
|
10058
|
+
};
|
|
10059
|
+
var settledOnReject = (error) => {
|
|
10060
|
+
const status = statusOf(error);
|
|
10061
|
+
return status !== void 0 && status < 500;
|
|
10062
|
+
};
|
|
10063
|
+
var withIdempotencyKey = async (url, body, send) => {
|
|
10064
|
+
const signature = `${url}
|
|
10065
|
+
${body}`;
|
|
10066
|
+
let entry = pending.get(signature);
|
|
10067
|
+
if (!entry) {
|
|
10068
|
+
entry = { key: mintKey(), inFlight: 0, keep: false };
|
|
10069
|
+
if (pending.size >= MAX_PENDING) {
|
|
10070
|
+
const oldest = pending.keys().next().value;
|
|
10071
|
+
if (oldest !== void 0) pending.delete(oldest);
|
|
10072
|
+
}
|
|
10073
|
+
pending.set(signature, entry);
|
|
10074
|
+
} else if (entry.inFlight === 0) {
|
|
10075
|
+
entry.keep = false;
|
|
10076
|
+
}
|
|
10077
|
+
entry.inFlight += 1;
|
|
10078
|
+
const settle = (settled) => {
|
|
10079
|
+
entry.inFlight -= 1;
|
|
10080
|
+
if (!settled) entry.keep = true;
|
|
10081
|
+
if (entry.inFlight === 0 && !entry.keep) pending.delete(signature);
|
|
10082
|
+
};
|
|
10083
|
+
try {
|
|
10084
|
+
const result = await send({ [HEADER]: entry.key });
|
|
10085
|
+
settle(settledOnResolve(result));
|
|
10086
|
+
return result;
|
|
10087
|
+
} catch (error) {
|
|
10088
|
+
settle(settledOnReject(error));
|
|
10089
|
+
throw error;
|
|
10090
|
+
}
|
|
10091
|
+
};
|
|
10092
|
+
|
|
10035
10093
|
// src/RedactoPrivacyCenter/api/client.ts
|
|
10036
10094
|
function createConsentClient(baseUrl, ledgerBaseUrl) {
|
|
10037
10095
|
const ledgerConfigured = Boolean(ledgerBaseUrl);
|
|
@@ -10453,11 +10511,19 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
|
|
|
10453
10511
|
...language ? { language } : {}
|
|
10454
10512
|
};
|
|
10455
10513
|
const url = ledgerUrl(baseRequest, "/submit-consent");
|
|
10456
|
-
|
|
10457
|
-
|
|
10458
|
-
|
|
10459
|
-
|
|
10460
|
-
|
|
10514
|
+
const serialisedBody = JSON.stringify(body);
|
|
10515
|
+
return withIdempotencyKey(url, serialisedBody, (idempotencyHeaders) => {
|
|
10516
|
+
const headers = new Headers(options == null ? void 0 : options.headers);
|
|
10517
|
+
Object.entries(idempotencyHeaders).forEach(
|
|
10518
|
+
([name, value]) => headers.set(name, value)
|
|
10519
|
+
);
|
|
10520
|
+
return privacyCenterApiFetch(url, {
|
|
10521
|
+
...options,
|
|
10522
|
+
method: "POST",
|
|
10523
|
+
body: serialisedBody,
|
|
10524
|
+
headers,
|
|
10525
|
+
authToken: resolveAuthToken(authToken, baseRequest)
|
|
10526
|
+
});
|
|
10461
10527
|
});
|
|
10462
10528
|
},
|
|
10463
10529
|
/**
|
package/dist/privacy-center.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SANDBOX_TOKEN_HEADER,
|
|
3
|
-
useMediaQuery
|
|
4
|
-
|
|
3
|
+
useMediaQuery,
|
|
4
|
+
withIdempotencyKey
|
|
5
|
+
} from "./chunk-6EET4YOY.mjs";
|
|
5
6
|
|
|
6
7
|
// src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx
|
|
7
8
|
import { I18nextProvider } from "react-i18next";
|
|
@@ -10385,11 +10386,19 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
|
|
|
10385
10386
|
...language ? { language } : {}
|
|
10386
10387
|
};
|
|
10387
10388
|
const url = ledgerUrl(baseRequest, "/submit-consent");
|
|
10388
|
-
|
|
10389
|
-
|
|
10390
|
-
|
|
10391
|
-
|
|
10392
|
-
|
|
10389
|
+
const serialisedBody = JSON.stringify(body);
|
|
10390
|
+
return withIdempotencyKey(url, serialisedBody, (idempotencyHeaders) => {
|
|
10391
|
+
const headers = new Headers(options == null ? void 0 : options.headers);
|
|
10392
|
+
Object.entries(idempotencyHeaders).forEach(
|
|
10393
|
+
([name, value]) => headers.set(name, value)
|
|
10394
|
+
);
|
|
10395
|
+
return privacyCenterApiFetch(url, {
|
|
10396
|
+
...options,
|
|
10397
|
+
method: "POST",
|
|
10398
|
+
body: serialisedBody,
|
|
10399
|
+
headers,
|
|
10400
|
+
authToken: resolveAuthToken(authToken, baseRequest)
|
|
10401
|
+
});
|
|
10393
10402
|
});
|
|
10394
10403
|
},
|
|
10395
10404
|
/**
|
package/package.json
CHANGED
package/dist/chunk-OGL6OC3A.mjs
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// src/RedactoNoticeConsent/useMediaQuery.ts
|
|
2
|
-
import { useState, useEffect } from "react";
|
|
3
|
-
var useMediaQuery = (query) => {
|
|
4
|
-
const getMatches = () => typeof window !== "undefined" ? window.matchMedia(query).matches : false;
|
|
5
|
-
const [matches, setMatches] = useState(getMatches);
|
|
6
|
-
useEffect(() => {
|
|
7
|
-
if (typeof window === "undefined") return;
|
|
8
|
-
const media = window.matchMedia(query);
|
|
9
|
-
const listener = (e) => {
|
|
10
|
-
setMatches(e.matches);
|
|
11
|
-
};
|
|
12
|
-
if (media.addEventListener) {
|
|
13
|
-
media.addEventListener("change", listener);
|
|
14
|
-
} else {
|
|
15
|
-
media.addListener(listener);
|
|
16
|
-
}
|
|
17
|
-
return () => {
|
|
18
|
-
if (media.removeEventListener) {
|
|
19
|
-
media.removeEventListener("change", listener);
|
|
20
|
-
} else {
|
|
21
|
-
media.removeListener(listener);
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
}, [query]);
|
|
25
|
-
return matches;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
// src/shared/sandbox.ts
|
|
29
|
-
var SANDBOX_TOKEN_HEADER = "X-Consent-Token";
|
|
30
|
-
|
|
31
|
-
export {
|
|
32
|
-
SANDBOX_TOKEN_HEADER,
|
|
33
|
-
useMediaQuery
|
|
34
|
-
};
|