@pubtech-ai/core 1.6.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/LICENSE +202 -0
- package/README.md +527 -0
- package/lib/cjs/Cloneable.d.ts +27 -0
- package/lib/cjs/Cloneable.js +1 -0
- package/lib/cjs/GVL.d.ts +295 -0
- package/lib/cjs/GVL.js +1 -0
- package/lib/cjs/Json.d.ts +20 -0
- package/lib/cjs/Json.js +1 -0
- package/lib/cjs/TCModel.d.ts +337 -0
- package/lib/cjs/TCModel.js +1 -0
- package/lib/cjs/TCString.d.ts +27 -0
- package/lib/cjs/TCString.js +1 -0
- package/lib/cjs/encoder/Base64Url.d.ts +29 -0
- package/lib/cjs/encoder/Base64Url.js +1 -0
- package/lib/cjs/encoder/BitLength.d.ts +32 -0
- package/lib/cjs/encoder/BitLength.js +1 -0
- package/lib/cjs/encoder/EncodingOptions.d.ts +6 -0
- package/lib/cjs/encoder/EncodingOptions.js +1 -0
- package/lib/cjs/encoder/SegmentEncoder.d.ts +8 -0
- package/lib/cjs/encoder/SegmentEncoder.js +1 -0
- package/lib/cjs/encoder/SemanticPreEncoder.d.ts +6 -0
- package/lib/cjs/encoder/SemanticPreEncoder.js +1 -0
- package/lib/cjs/encoder/field/BooleanEncoder.d.ts +4 -0
- package/lib/cjs/encoder/field/BooleanEncoder.js +1 -0
- package/lib/cjs/encoder/field/DateEncoder.d.ts +4 -0
- package/lib/cjs/encoder/field/DateEncoder.js +1 -0
- package/lib/cjs/encoder/field/FieldEncoderMap.d.ts +1 -0
- package/lib/cjs/encoder/field/FieldEncoderMap.js +1 -0
- package/lib/cjs/encoder/field/FixedVectorEncoder.d.ts +5 -0
- package/lib/cjs/encoder/field/FixedVectorEncoder.js +1 -0
- package/lib/cjs/encoder/field/IntEncoder.d.ts +4 -0
- package/lib/cjs/encoder/field/IntEncoder.js +1 -0
- package/lib/cjs/encoder/field/LangEncoder.d.ts +4 -0
- package/lib/cjs/encoder/field/LangEncoder.js +1 -0
- package/lib/cjs/encoder/field/PurposeRestrictionVectorEncoder.d.ts +5 -0
- package/lib/cjs/encoder/field/PurposeRestrictionVectorEncoder.js +1 -0
- package/lib/cjs/encoder/field/VectorEncodingType.d.ts +4 -0
- package/lib/cjs/encoder/field/VectorEncodingType.js +1 -0
- package/lib/cjs/encoder/field/VendorVectorEncoder.d.ts +6 -0
- package/lib/cjs/encoder/field/VendorVectorEncoder.js +1 -0
- package/lib/cjs/encoder/field/index.d.ts +9 -0
- package/lib/cjs/encoder/field/index.js +1 -0
- package/lib/cjs/encoder/index.d.ts +7 -0
- package/lib/cjs/encoder/index.js +1 -0
- package/lib/cjs/encoder/sequence/FieldSequence.d.ts +5 -0
- package/lib/cjs/encoder/sequence/FieldSequence.js +1 -0
- package/lib/cjs/encoder/sequence/SegmentSequence.d.ts +9 -0
- package/lib/cjs/encoder/sequence/SegmentSequence.js +1 -0
- package/lib/cjs/encoder/sequence/SequenceVersionMap.d.ts +7 -0
- package/lib/cjs/encoder/sequence/SequenceVersionMap.js +1 -0
- package/lib/cjs/encoder/sequence/index.d.ts +3 -0
- package/lib/cjs/encoder/sequence/index.js +1 -0
- package/lib/cjs/errors/DecodingError.d.ts +15 -0
- package/lib/cjs/errors/DecodingError.js +1 -0
- package/lib/cjs/errors/EncodingError.d.ts +15 -0
- package/lib/cjs/errors/EncodingError.js +1 -0
- package/lib/cjs/errors/GVLError.d.ts +15 -0
- package/lib/cjs/errors/GVLError.js +1 -0
- package/lib/cjs/errors/TCModelError.d.ts +16 -0
- package/lib/cjs/errors/TCModelError.js +1 -0
- package/lib/cjs/errors/index.d.ts +4 -0
- package/lib/cjs/errors/index.js +1 -0
- package/lib/cjs/index.d.ts +8 -0
- package/lib/cjs/index.js +1 -0
- package/lib/cjs/model/BinarySearchTree.d.ts +30 -0
- package/lib/cjs/model/BinarySearchTree.js +1 -0
- package/lib/cjs/model/ConsentLanguages.d.ts +6 -0
- package/lib/cjs/model/ConsentLanguages.js +1 -0
- package/lib/cjs/model/DeviceDisclosure.d.ts +9 -0
- package/lib/cjs/model/DeviceDisclosure.js +1 -0
- package/lib/cjs/model/DeviceDisclosureStorageAccessType.d.ts +5 -0
- package/lib/cjs/model/DeviceDisclosureStorageAccessType.js +1 -0
- package/lib/cjs/model/Fields.d.ts +29 -0
- package/lib/cjs/model/Fields.js +1 -0
- package/lib/cjs/model/IntMap.d.ts +13 -0
- package/lib/cjs/model/IntMap.js +1 -0
- package/lib/cjs/model/KeyMap.d.ts +12 -0
- package/lib/cjs/model/KeyMap.js +1 -0
- package/lib/cjs/model/PurposeRestriction.d.ts +33 -0
- package/lib/cjs/model/PurposeRestriction.js +1 -0
- package/lib/cjs/model/PurposeRestrictionVector.d.ts +100 -0
- package/lib/cjs/model/PurposeRestrictionVector.js +1 -0
- package/lib/cjs/model/RestrictionType.d.ts +19 -0
- package/lib/cjs/model/RestrictionType.js +1 -0
- package/lib/cjs/model/Segment.d.ts +6 -0
- package/lib/cjs/model/Segment.js +1 -0
- package/lib/cjs/model/SegmentIDs.d.ts +12 -0
- package/lib/cjs/model/SegmentIDs.js +1 -0
- package/lib/cjs/model/Vector.d.ts +72 -0
- package/lib/cjs/model/Vector.js +1 -0
- package/lib/cjs/model/gvl/ByPurposeVendorMap.d.ts +7 -0
- package/lib/cjs/model/gvl/ByPurposeVendorMap.js +1 -0
- package/lib/cjs/model/gvl/DataCategory.d.ts +4 -0
- package/lib/cjs/model/gvl/DataCategory.js +1 -0
- package/lib/cjs/model/gvl/DataRetention.d.ts +6 -0
- package/lib/cjs/model/gvl/DataRetention.js +1 -0
- package/lib/cjs/model/gvl/Declarations.d.ts +13 -0
- package/lib/cjs/model/gvl/Declarations.js +1 -0
- package/lib/cjs/model/gvl/Feature.d.ts +5 -0
- package/lib/cjs/model/gvl/Feature.js +1 -0
- package/lib/cjs/model/gvl/GVLMapItem.d.ts +4 -0
- package/lib/cjs/model/gvl/GVLMapItem.js +1 -0
- package/lib/cjs/model/gvl/IDSetMap.d.ts +2 -0
- package/lib/cjs/model/gvl/IDSetMap.js +1 -0
- package/lib/cjs/model/gvl/Purpose.d.ts +5 -0
- package/lib/cjs/model/gvl/Purpose.js +1 -0
- package/lib/cjs/model/gvl/Stack.d.ts +6 -0
- package/lib/cjs/model/gvl/Stack.js +1 -0
- package/lib/cjs/model/gvl/Vendor.d.ts +24 -0
- package/lib/cjs/model/gvl/Vendor.js +1 -0
- package/lib/cjs/model/gvl/VendorList.d.ts +10 -0
- package/lib/cjs/model/gvl/VendorList.js +1 -0
- package/lib/cjs/model/gvl/VendorUrl.d.ts +5 -0
- package/lib/cjs/model/gvl/VendorUrl.js +1 -0
- package/lib/cjs/model/gvl/index.d.ts +10 -0
- package/lib/cjs/model/gvl/index.js +1 -0
- package/lib/cjs/model/index.d.ts +14 -0
- package/lib/cjs/model/index.js +1 -0
- package/lib/cjs/package.json +3 -0
- package/lib/mjs/Cloneable.d.ts +27 -0
- package/lib/mjs/Cloneable.js +76 -0
- package/lib/mjs/GVL.d.ts +295 -0
- package/lib/mjs/GVL.js +591 -0
- package/lib/mjs/Json.d.ts +20 -0
- package/lib/mjs/Json.js +74 -0
- package/lib/mjs/TCModel.d.ts +337 -0
- package/lib/mjs/TCModel.js +512 -0
- package/lib/mjs/TCString.d.ts +27 -0
- package/lib/mjs/TCString.js +71 -0
- package/lib/mjs/encoder/Base64Url.d.ts +29 -0
- package/lib/mjs/encoder/Base64Url.js +80 -0
- package/lib/mjs/encoder/BitLength.d.ts +32 -0
- package/lib/mjs/encoder/BitLength.js +32 -0
- package/lib/mjs/encoder/EncodingOptions.d.ts +6 -0
- package/lib/mjs/encoder/EncodingOptions.js +1 -0
- package/lib/mjs/encoder/SegmentEncoder.d.ts +8 -0
- package/lib/mjs/encoder/SegmentEncoder.js +106 -0
- package/lib/mjs/encoder/SemanticPreEncoder.d.ts +6 -0
- package/lib/mjs/encoder/SemanticPreEncoder.js +133 -0
- package/lib/mjs/encoder/field/BooleanEncoder.d.ts +4 -0
- package/lib/mjs/encoder/field/BooleanEncoder.js +9 -0
- package/lib/mjs/encoder/field/DateEncoder.d.ts +4 -0
- package/lib/mjs/encoder/field/DateEncoder.js +15 -0
- package/lib/mjs/encoder/field/FieldEncoderMap.d.ts +1 -0
- package/lib/mjs/encoder/field/FieldEncoderMap.js +39 -0
- package/lib/mjs/encoder/field/FixedVectorEncoder.d.ts +5 -0
- package/lib/mjs/encoder/field/FixedVectorEncoder.js +25 -0
- package/lib/mjs/encoder/field/IntEncoder.d.ts +4 -0
- package/lib/mjs/encoder/field/IntEncoder.js +25 -0
- package/lib/mjs/encoder/field/LangEncoder.d.ts +4 -0
- package/lib/mjs/encoder/field/LangEncoder.js +36 -0
- package/lib/mjs/encoder/field/PurposeRestrictionVectorEncoder.d.ts +5 -0
- package/lib/mjs/encoder/field/PurposeRestrictionVectorEncoder.js +109 -0
- package/lib/mjs/encoder/field/VectorEncodingType.d.ts +4 -0
- package/lib/mjs/encoder/field/VectorEncodingType.js +5 -0
- package/lib/mjs/encoder/field/VendorVectorEncoder.d.ts +6 -0
- package/lib/mjs/encoder/field/VendorVectorEncoder.js +156 -0
- package/lib/mjs/encoder/field/index.d.ts +9 -0
- package/lib/mjs/encoder/field/index.js +9 -0
- package/lib/mjs/encoder/index.d.ts +7 -0
- package/lib/mjs/encoder/index.js +7 -0
- package/lib/mjs/encoder/sequence/FieldSequence.d.ts +5 -0
- package/lib/mjs/encoder/sequence/FieldSequence.js +53 -0
- package/lib/mjs/encoder/sequence/SegmentSequence.d.ts +9 -0
- package/lib/mjs/encoder/sequence/SegmentSequence.js +52 -0
- package/lib/mjs/encoder/sequence/SequenceVersionMap.d.ts +7 -0
- package/lib/mjs/encoder/sequence/SequenceVersionMap.js +1 -0
- package/lib/mjs/encoder/sequence/index.d.ts +3 -0
- package/lib/mjs/encoder/sequence/index.js +4 -0
- package/lib/mjs/errors/DecodingError.d.ts +15 -0
- package/lib/mjs/errors/DecodingError.js +18 -0
- package/lib/mjs/errors/EncodingError.d.ts +15 -0
- package/lib/mjs/errors/EncodingError.js +18 -0
- package/lib/mjs/errors/GVLError.d.ts +15 -0
- package/lib/mjs/errors/GVLError.js +18 -0
- package/lib/mjs/errors/TCModelError.d.ts +16 -0
- package/lib/mjs/errors/TCModelError.js +19 -0
- package/lib/mjs/errors/index.d.ts +4 -0
- package/lib/mjs/errors/index.js +4 -0
- package/lib/mjs/index.d.ts +8 -0
- package/lib/mjs/index.js +9 -0
- package/lib/mjs/model/BinarySearchTree.d.ts +30 -0
- package/lib/mjs/model/BinarySearchTree.js +267 -0
- package/lib/mjs/model/ConsentLanguages.d.ts +6 -0
- package/lib/mjs/model/ConsentLanguages.js +42 -0
- package/lib/mjs/model/DeviceDisclosure.d.ts +9 -0
- package/lib/mjs/model/DeviceDisclosure.js +1 -0
- package/lib/mjs/model/DeviceDisclosureStorageAccessType.d.ts +5 -0
- package/lib/mjs/model/DeviceDisclosureStorageAccessType.js +6 -0
- package/lib/mjs/model/Fields.d.ts +29 -0
- package/lib/mjs/model/Fields.js +29 -0
- package/lib/mjs/model/IntMap.d.ts +13 -0
- package/lib/mjs/model/IntMap.js +1 -0
- package/lib/mjs/model/KeyMap.d.ts +12 -0
- package/lib/mjs/model/KeyMap.js +1 -0
- package/lib/mjs/model/PurposeRestriction.d.ts +33 -0
- package/lib/mjs/model/PurposeRestriction.js +69 -0
- package/lib/mjs/model/PurposeRestrictionVector.d.ts +100 -0
- package/lib/mjs/model/PurposeRestrictionVector.js +286 -0
- package/lib/mjs/model/RestrictionType.d.ts +19 -0
- package/lib/mjs/model/RestrictionType.js +20 -0
- package/lib/mjs/model/Segment.d.ts +6 -0
- package/lib/mjs/model/Segment.js +7 -0
- package/lib/mjs/model/SegmentIDs.d.ts +12 -0
- package/lib/mjs/model/SegmentIDs.js +21 -0
- package/lib/mjs/model/Vector.d.ts +72 -0
- package/lib/mjs/model/Vector.js +161 -0
- package/lib/mjs/model/gvl/ByPurposeVendorMap.d.ts +7 -0
- package/lib/mjs/model/gvl/ByPurposeVendorMap.js +1 -0
- package/lib/mjs/model/gvl/DataCategory.d.ts +4 -0
- package/lib/mjs/model/gvl/DataCategory.js +1 -0
- package/lib/mjs/model/gvl/DataRetention.d.ts +6 -0
- package/lib/mjs/model/gvl/DataRetention.js +1 -0
- package/lib/mjs/model/gvl/Declarations.d.ts +13 -0
- package/lib/mjs/model/gvl/Declarations.js +1 -0
- package/lib/mjs/model/gvl/Feature.d.ts +5 -0
- package/lib/mjs/model/gvl/Feature.js +2 -0
- package/lib/mjs/model/gvl/GVLMapItem.d.ts +4 -0
- package/lib/mjs/model/gvl/GVLMapItem.js +1 -0
- package/lib/mjs/model/gvl/IDSetMap.d.ts +2 -0
- package/lib/mjs/model/gvl/IDSetMap.js +1 -0
- package/lib/mjs/model/gvl/Purpose.d.ts +5 -0
- package/lib/mjs/model/gvl/Purpose.js +1 -0
- package/lib/mjs/model/gvl/Stack.d.ts +6 -0
- package/lib/mjs/model/gvl/Stack.js +1 -0
- package/lib/mjs/model/gvl/Vendor.d.ts +24 -0
- package/lib/mjs/model/gvl/Vendor.js +1 -0
- package/lib/mjs/model/gvl/VendorList.d.ts +10 -0
- package/lib/mjs/model/gvl/VendorList.js +1 -0
- package/lib/mjs/model/gvl/VendorUrl.d.ts +5 -0
- package/lib/mjs/model/gvl/VendorUrl.js +1 -0
- package/lib/mjs/model/gvl/index.d.ts +10 -0
- package/lib/mjs/model/gvl/index.js +11 -0
- package/lib/mjs/model/index.d.ts +14 -0
- package/lib/mjs/model/index.js +14 -0
- package/lib/mjs/package.json +3 -0
- package/package.json +95 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { DecodingError, EncodingError } from '../errors/index.js';
|
|
2
|
+
export class Base64Url {
|
|
3
|
+
/**
|
|
4
|
+
* Base 64 URL character set. Different from standard Base64 char set
|
|
5
|
+
* in that '+' and '/' are replaced with '-' and '_'.
|
|
6
|
+
*/
|
|
7
|
+
static DICT = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
|
8
|
+
static REVERSE_DICT = new Map([
|
|
9
|
+
['A', 0], ['B', 1], ['C', 2], ['D', 3], ['E', 4], ['F', 5],
|
|
10
|
+
['G', 6], ['H', 7], ['I', 8], ['J', 9], ['K', 10], ['L', 11],
|
|
11
|
+
['M', 12], ['N', 13], ['O', 14], ['P', 15], ['Q', 16], ['R', 17],
|
|
12
|
+
['S', 18], ['T', 19], ['U', 20], ['V', 21], ['W', 22], ['X', 23],
|
|
13
|
+
['Y', 24], ['Z', 25], ['a', 26], ['b', 27], ['c', 28], ['d', 29],
|
|
14
|
+
['e', 30], ['f', 31], ['g', 32], ['h', 33], ['i', 34], ['j', 35],
|
|
15
|
+
['k', 36], ['l', 37], ['m', 38], ['n', 39], ['o', 40], ['p', 41],
|
|
16
|
+
['q', 42], ['r', 43], ['s', 44], ['t', 45], ['u', 46], ['v', 47],
|
|
17
|
+
['w', 48], ['x', 49], ['y', 50], ['z', 51], ['0', 52], ['1', 53],
|
|
18
|
+
['2', 54], ['3', 55], ['4', 56], ['5', 57], ['6', 58], ['7', 59],
|
|
19
|
+
['8', 60], ['9', 61], ['-', 62], ['_', 63],
|
|
20
|
+
]);
|
|
21
|
+
/**
|
|
22
|
+
* log2(64) = 6
|
|
23
|
+
*/
|
|
24
|
+
static BASIS = 6;
|
|
25
|
+
static LCM = 24;
|
|
26
|
+
/**
|
|
27
|
+
* encodes an arbitrary-length bitfield string into base64url
|
|
28
|
+
*
|
|
29
|
+
* @static
|
|
30
|
+
* @param {string} str - arbitrary-length bitfield string to be encoded to base64url
|
|
31
|
+
* @return {string} - base64url encoded result
|
|
32
|
+
*/
|
|
33
|
+
static encode(str) {
|
|
34
|
+
/**
|
|
35
|
+
* should only be 0 or 1
|
|
36
|
+
*/
|
|
37
|
+
if (!/^[0-1]+$/.test(str)) {
|
|
38
|
+
throw new EncodingError('Invalid bitField');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Pad the end of the string to the least common mutliple of 6 (basis for
|
|
42
|
+
* base64) and 8 (one byte)
|
|
43
|
+
*/
|
|
44
|
+
const padding = str.length % this.LCM;
|
|
45
|
+
str += padding ? '0'.repeat(this.LCM - padding) : '';
|
|
46
|
+
let result = '';
|
|
47
|
+
for (let i = 0; i < str.length; i += this.BASIS) {
|
|
48
|
+
result += this.DICT[parseInt(str.substr(i, this.BASIS), 2)];
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* decodes a base64url encoded bitfield string
|
|
54
|
+
*
|
|
55
|
+
* @static
|
|
56
|
+
* @param {string} str - base64url encoded bitfield string to be decoded
|
|
57
|
+
* @return {string} - bitfield string
|
|
58
|
+
*/
|
|
59
|
+
static decode(str) {
|
|
60
|
+
/**
|
|
61
|
+
* should contain only characters from the base64url set
|
|
62
|
+
*/
|
|
63
|
+
if (!/^[A-Za-z0-9\-_]+$/.test(str)) {
|
|
64
|
+
throw new DecodingError('Invalidly encoded Base64URL string');
|
|
65
|
+
}
|
|
66
|
+
let result = '';
|
|
67
|
+
for (let i = 0; i < str.length; i++) {
|
|
68
|
+
/**
|
|
69
|
+
* index the binary value of the character from out reverse map
|
|
70
|
+
*/
|
|
71
|
+
const strBits = this.REVERSE_DICT.get(str[i]).toString(2);
|
|
72
|
+
/**
|
|
73
|
+
* Since a bit string converted to an integer on encoding will lose
|
|
74
|
+
* leading zeros – pad to the left for those missing leading zeros
|
|
75
|
+
*/
|
|
76
|
+
result += '0'.repeat(this.BASIS - strBits.length) + strBits;
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Fields } from '../model/index.js';
|
|
2
|
+
export declare class BitLength {
|
|
3
|
+
static readonly [Fields.cmpId]: number;
|
|
4
|
+
static readonly [Fields.cmpVersion]: number;
|
|
5
|
+
static readonly [Fields.consentLanguage]: number;
|
|
6
|
+
static readonly [Fields.consentScreen]: number;
|
|
7
|
+
static readonly [Fields.created]: number;
|
|
8
|
+
static readonly [Fields.isServiceSpecific]: number;
|
|
9
|
+
static readonly [Fields.lastUpdated]: number;
|
|
10
|
+
static readonly [Fields.policyVersion]: number;
|
|
11
|
+
static readonly [Fields.publisherCountryCode]: number;
|
|
12
|
+
static readonly [Fields.publisherLegitimateInterests]: number;
|
|
13
|
+
static readonly [Fields.publisherConsents]: number;
|
|
14
|
+
static readonly [Fields.purposeConsents]: number;
|
|
15
|
+
static readonly [Fields.purposeLegitimateInterests]: number;
|
|
16
|
+
static readonly [Fields.purposeOneTreatment]: number;
|
|
17
|
+
static readonly [Fields.specialFeatureOptins]: number;
|
|
18
|
+
static readonly [Fields.useNonStandardStacks]: number;
|
|
19
|
+
static readonly [Fields.vendorListVersion]: number;
|
|
20
|
+
static readonly [Fields.version]: number;
|
|
21
|
+
static readonly anyBoolean: number;
|
|
22
|
+
static readonly encodingType: number;
|
|
23
|
+
static readonly maxId: number;
|
|
24
|
+
static readonly numCustomPurposes: number;
|
|
25
|
+
static readonly numEntries: number;
|
|
26
|
+
static readonly numRestrictions: number;
|
|
27
|
+
static readonly purposeId: number;
|
|
28
|
+
static readonly restrictionType: number;
|
|
29
|
+
static readonly segmentType: number;
|
|
30
|
+
static readonly singleOrRange: number;
|
|
31
|
+
static readonly vendorId: number;
|
|
32
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Fields } from '../model/index.js';
|
|
2
|
+
export class BitLength {
|
|
3
|
+
static [Fields.cmpId] = 12;
|
|
4
|
+
static [Fields.cmpVersion] = 12;
|
|
5
|
+
static [Fields.consentLanguage] = 12;
|
|
6
|
+
static [Fields.consentScreen] = 6;
|
|
7
|
+
static [Fields.created] = 36;
|
|
8
|
+
static [Fields.isServiceSpecific] = 1;
|
|
9
|
+
static [Fields.lastUpdated] = 36;
|
|
10
|
+
static [Fields.policyVersion] = 6;
|
|
11
|
+
static [Fields.publisherCountryCode] = 12;
|
|
12
|
+
static [Fields.publisherLegitimateInterests] = 24;
|
|
13
|
+
static [Fields.publisherConsents] = 24;
|
|
14
|
+
static [Fields.purposeConsents] = 24;
|
|
15
|
+
static [Fields.purposeLegitimateInterests] = 24;
|
|
16
|
+
static [Fields.purposeOneTreatment] = 1;
|
|
17
|
+
static [Fields.specialFeatureOptins] = 12;
|
|
18
|
+
static [Fields.useNonStandardStacks] = 1;
|
|
19
|
+
static [Fields.vendorListVersion] = 12;
|
|
20
|
+
static [Fields.version] = 6;
|
|
21
|
+
static anyBoolean = 1;
|
|
22
|
+
static encodingType = 1;
|
|
23
|
+
static maxId = 16;
|
|
24
|
+
static numCustomPurposes = 6;
|
|
25
|
+
static numEntries = 12;
|
|
26
|
+
static numRestrictions = 12;
|
|
27
|
+
static purposeId = 6;
|
|
28
|
+
static restrictionType = 2;
|
|
29
|
+
static segmentType = 3;
|
|
30
|
+
static singleOrRange = 1;
|
|
31
|
+
static vendorId = 16;
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Segment } from '../model/index.js';
|
|
2
|
+
import { TCModel } from '../index.js';
|
|
3
|
+
export declare class SegmentEncoder {
|
|
4
|
+
private static fieldSequence;
|
|
5
|
+
static encode(tcModel: TCModel, segment: Segment): string;
|
|
6
|
+
static decode(encodedString: string, tcModel: TCModel, segment: string): TCModel;
|
|
7
|
+
private static isPublisherCustom;
|
|
8
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Base64Url } from './Base64Url.js';
|
|
2
|
+
import { BitLength } from './BitLength.js';
|
|
3
|
+
import { FieldEncoderMap, IntEncoder, VendorVectorEncoder } from './field/index.js';
|
|
4
|
+
import { FieldSequence } from './sequence/index.js';
|
|
5
|
+
import { EncodingError, DecodingError } from '../errors/index.js';
|
|
6
|
+
import { Fields } from '../model/Fields.js';
|
|
7
|
+
import { Segment, SegmentIDs } from '../model/index.js';
|
|
8
|
+
export class SegmentEncoder {
|
|
9
|
+
static fieldSequence = new FieldSequence();
|
|
10
|
+
static encode(tcModel, segment) {
|
|
11
|
+
let sequence;
|
|
12
|
+
try {
|
|
13
|
+
sequence = this.fieldSequence[String(tcModel.version)][segment];
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
throw new EncodingError(`Unable to encode version: ${tcModel.version}, segment: ${segment}`);
|
|
17
|
+
}
|
|
18
|
+
let bitField = '';
|
|
19
|
+
/**
|
|
20
|
+
* If this is anything other than the core segment we have a "segment id"
|
|
21
|
+
* to append to the front of the string
|
|
22
|
+
*/
|
|
23
|
+
if (segment !== Segment.CORE) {
|
|
24
|
+
bitField = IntEncoder.encode(SegmentIDs.KEY_TO_ID[segment], BitLength.segmentType);
|
|
25
|
+
}
|
|
26
|
+
const fieldEncoderMap = FieldEncoderMap();
|
|
27
|
+
sequence.forEach((key) => {
|
|
28
|
+
const value = tcModel[key];
|
|
29
|
+
const encoder = fieldEncoderMap[key];
|
|
30
|
+
let numBits = BitLength[key];
|
|
31
|
+
if (numBits === undefined) {
|
|
32
|
+
if (this.isPublisherCustom(key)) {
|
|
33
|
+
/**
|
|
34
|
+
* publisherCustom[Consents | LegitimateInterests] are an edge case
|
|
35
|
+
* because they are of variable length. The length is defined in a
|
|
36
|
+
* separate field named numCustomPurposes.
|
|
37
|
+
*/
|
|
38
|
+
numBits = Number(tcModel[Fields.numCustomPurposes]);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
bitField += encoder.encode(value, numBits);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
throw new EncodingError(`Error encoding ${segment}->${key}: ${err.message}`);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
// base64url encode the string and return
|
|
49
|
+
return Base64Url.encode(bitField);
|
|
50
|
+
}
|
|
51
|
+
static decode(encodedString, tcModel, segment) {
|
|
52
|
+
const bitField = Base64Url.decode(encodedString);
|
|
53
|
+
let bStringIdx = 0;
|
|
54
|
+
if (segment === Segment.CORE) {
|
|
55
|
+
tcModel.version = IntEncoder.decode(bitField.substr(bStringIdx, BitLength[Fields.version]), BitLength[Fields.version]);
|
|
56
|
+
}
|
|
57
|
+
if (segment !== Segment.CORE) {
|
|
58
|
+
bStringIdx += BitLength.segmentType;
|
|
59
|
+
}
|
|
60
|
+
const sequence = this.fieldSequence[String(tcModel.version)][segment];
|
|
61
|
+
const fieldEncoderMap = FieldEncoderMap();
|
|
62
|
+
sequence.forEach((key) => {
|
|
63
|
+
const encoder = fieldEncoderMap[key];
|
|
64
|
+
let numBits = BitLength[key];
|
|
65
|
+
if (numBits === undefined) {
|
|
66
|
+
if (this.isPublisherCustom(key)) {
|
|
67
|
+
/**
|
|
68
|
+
* publisherCustom[Consents | LegitimateInterests] are an edge case
|
|
69
|
+
* because they are of variable length. The length is defined in a
|
|
70
|
+
* separate field named numCustomPurposes.
|
|
71
|
+
*/
|
|
72
|
+
numBits = Number(tcModel[Fields.numCustomPurposes]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (numBits !== 0) {
|
|
76
|
+
/**
|
|
77
|
+
* numBits could be 0 if this is a publisher custom purposes field and
|
|
78
|
+
* no custom purposes are defined. If that is the case, we don't need
|
|
79
|
+
* to gather no bits and we don't need to increment our bStringIdx
|
|
80
|
+
* pointer because those would all be 0 increments and would mess up
|
|
81
|
+
* the next logical if statement.
|
|
82
|
+
*/
|
|
83
|
+
const bits = bitField.substr(bStringIdx, numBits);
|
|
84
|
+
if (encoder === VendorVectorEncoder) {
|
|
85
|
+
tcModel[key] = encoder.decode(bits, tcModel.version);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
tcModel[key] = encoder.decode(bits, numBits);
|
|
89
|
+
}
|
|
90
|
+
if (Number.isInteger(numBits)) {
|
|
91
|
+
bStringIdx += numBits;
|
|
92
|
+
}
|
|
93
|
+
else if (Number.isInteger(tcModel[key].bitLength)) {
|
|
94
|
+
bStringIdx += tcModel[key].bitLength;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
throw new DecodingError(key);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return tcModel;
|
|
102
|
+
}
|
|
103
|
+
static isPublisherCustom(key) {
|
|
104
|
+
return key.indexOf('publisherCustom') === 0;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { EncodingError } from '../errors/index.js';
|
|
2
|
+
import { RestrictionType } from '../model/index.js';
|
|
3
|
+
export class SemanticPreEncoder {
|
|
4
|
+
static processor = [
|
|
5
|
+
(tcModel) => tcModel,
|
|
6
|
+
(tcModel, gvl) => {
|
|
7
|
+
/**
|
|
8
|
+
* in case this wasn't set previously. This should filter out invalid
|
|
9
|
+
* purpose restrictions.
|
|
10
|
+
*/
|
|
11
|
+
tcModel.publisherRestrictions.gvl = gvl;
|
|
12
|
+
/**
|
|
13
|
+
* Purpose 1 is never allowed to be true for legitimate interest
|
|
14
|
+
* As of TCF v2.2 purposes 3,4,5 & 6 are not allowed to be true for LI
|
|
15
|
+
*/
|
|
16
|
+
tcModel.purposeLegitimateInterests.unset([1, 3, 4, 5, 6]);
|
|
17
|
+
/**
|
|
18
|
+
* If a Vendor does not declare a purpose for consent or legitimate
|
|
19
|
+
* interest they should not have a positive signal for it. This code
|
|
20
|
+
* removes positive signals created mistakingly.
|
|
21
|
+
*/
|
|
22
|
+
const vectorToIntMap = new Map();
|
|
23
|
+
vectorToIntMap.set('legIntPurposes', tcModel.vendorLegitimateInterests);
|
|
24
|
+
vectorToIntMap.set('purposes', tcModel.vendorConsents);
|
|
25
|
+
vectorToIntMap.forEach((vector, gvlVendorKey) => {
|
|
26
|
+
vector.forEach((value, vendorId) => {
|
|
27
|
+
if (value) {
|
|
28
|
+
const vendor = gvl.vendors[vendorId];
|
|
29
|
+
if (!vendor || vendor.deletedDate) {
|
|
30
|
+
/**
|
|
31
|
+
* If the vendor doesn't exist, then they should not receive a
|
|
32
|
+
* positive signal
|
|
33
|
+
*/
|
|
34
|
+
vector.unset(vendorId);
|
|
35
|
+
}
|
|
36
|
+
else if (vendor[gvlVendorKey].length === 0) {
|
|
37
|
+
if (gvlVendorKey === 'legIntPurposes' && vendor['purposes'].length === 0 && vendor['legIntPurposes'].length === 0 && vendor['specialPurposes'].length > 0) {
|
|
38
|
+
/**
|
|
39
|
+
* Per June 2021 Policy change, Vendors declaring only Special Purposes must
|
|
40
|
+
* have their legitimate interest Vendor bit set if they have been disclosed.
|
|
41
|
+
* This empty block ensures their LI bit remains set
|
|
42
|
+
*/
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
/**
|
|
46
|
+
* If the vendor does exist, but they haven't declared any
|
|
47
|
+
* purposes for this legal basis, then we need to see if they can
|
|
48
|
+
* possibly have the legal basis from their flexible purposes.
|
|
49
|
+
*/
|
|
50
|
+
if (tcModel.isServiceSpecific) {
|
|
51
|
+
if (vendor.flexiblePurposes.length === 0) {
|
|
52
|
+
/**
|
|
53
|
+
* No flexible basis for any purposes, so we can safely remove
|
|
54
|
+
* this vendor from the legal basis.
|
|
55
|
+
*/
|
|
56
|
+
vector.unset(vendorId);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
/**
|
|
60
|
+
* They have some flexible purposes, we should check for a
|
|
61
|
+
* publisher restriction value that would enable this vendor to
|
|
62
|
+
* have the override-preferred basis.
|
|
63
|
+
*/
|
|
64
|
+
const restrictions = tcModel.publisherRestrictions.getRestrictions(vendorId);
|
|
65
|
+
let isValid = false;
|
|
66
|
+
for (let i = 0, len = restrictions.length; i < len && !isValid; i++) {
|
|
67
|
+
/**
|
|
68
|
+
* If this condition is true the loop will break. If we are
|
|
69
|
+
* dealing with the consent purposes ('purposes') and the
|
|
70
|
+
* publisher restriction overrides to consent then it is
|
|
71
|
+
* valid for the vendor to have a positive signal for
|
|
72
|
+
* consent. Likewise for legitimate interest purposes
|
|
73
|
+
* ('legIntPurposes') and requiring legitimate interest.
|
|
74
|
+
*/
|
|
75
|
+
isValid = ((restrictions[i].restrictionType === RestrictionType.REQUIRE_CONSENT &&
|
|
76
|
+
gvlVendorKey === 'purposes') ||
|
|
77
|
+
(restrictions[i].restrictionType === RestrictionType.REQUIRE_LI &&
|
|
78
|
+
gvlVendorKey === 'legIntPurposes'));
|
|
79
|
+
}
|
|
80
|
+
if (!isValid) {
|
|
81
|
+
/**
|
|
82
|
+
* if we came through the previous loop without finding a
|
|
83
|
+
* valid reasing: no overriding restrictions (changes in
|
|
84
|
+
* legal basis) then it's not valid for this vendor to have
|
|
85
|
+
* this legal basis.
|
|
86
|
+
*/
|
|
87
|
+
vector.unset(vendorId);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
/**
|
|
93
|
+
* This is a globally-scoped string so flexible purposes will not
|
|
94
|
+
* be able to change this value because purposeRestrictions only
|
|
95
|
+
* apply to service-specific strings.
|
|
96
|
+
*/
|
|
97
|
+
vector.unset(vendorId);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
tcModel.vendorsDisclosed.set(gvl.vendors);
|
|
105
|
+
return tcModel;
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
static process(tcModel, options) {
|
|
109
|
+
const gvl = tcModel.gvl;
|
|
110
|
+
if (!gvl) {
|
|
111
|
+
throw new EncodingError('Unable to encode TCModel without a GVL');
|
|
112
|
+
}
|
|
113
|
+
if (!gvl.isReady) {
|
|
114
|
+
throw new EncodingError('Unable to encode TCModel tcModel.gvl.readyPromise is not resolved');
|
|
115
|
+
}
|
|
116
|
+
tcModel = tcModel.clone();
|
|
117
|
+
tcModel.consentLanguage = gvl.language.toUpperCase();
|
|
118
|
+
if (options?.version > 0 && options?.version <= this.processor.length) {
|
|
119
|
+
tcModel.version = options.version;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
/**
|
|
123
|
+
* this is equal to the latest or most current version
|
|
124
|
+
*/
|
|
125
|
+
tcModel.version = this.processor.length;
|
|
126
|
+
}
|
|
127
|
+
const processorFunctionIndex = tcModel.version - 1;
|
|
128
|
+
if (!this.processor[processorFunctionIndex]) {
|
|
129
|
+
throw new EncodingError(`Invalid version: ${tcModel.version}`);
|
|
130
|
+
}
|
|
131
|
+
return this.processor[processorFunctionIndex](tcModel, gvl);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { IntEncoder, } from './IntEncoder.js';
|
|
2
|
+
import { DecodingError, } from '../../errors/index.js';
|
|
3
|
+
export class DateEncoder {
|
|
4
|
+
static encode(value, numBits) {
|
|
5
|
+
return IntEncoder.encode(Math.round(value.getTime() / 100), numBits);
|
|
6
|
+
}
|
|
7
|
+
static decode(value, numBits) {
|
|
8
|
+
if (numBits !== value.length) {
|
|
9
|
+
throw new DecodingError('invalid bit length');
|
|
10
|
+
}
|
|
11
|
+
const date = new Date();
|
|
12
|
+
date.setTime(IntEncoder.decode(value, numBits) * 100);
|
|
13
|
+
return date;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function FieldEncoderMap(): object;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Fields } from '../../model/index.js';
|
|
2
|
+
import { BooleanEncoder } from './BooleanEncoder.js';
|
|
3
|
+
import { DateEncoder } from './DateEncoder.js';
|
|
4
|
+
import { FixedVectorEncoder } from './FixedVectorEncoder.js';
|
|
5
|
+
import { IntEncoder } from './IntEncoder.js';
|
|
6
|
+
import { LangEncoder } from './LangEncoder.js';
|
|
7
|
+
import { PurposeRestrictionVectorEncoder } from './PurposeRestrictionVectorEncoder.js';
|
|
8
|
+
import { VendorVectorEncoder } from './VendorVectorEncoder.js';
|
|
9
|
+
export function FieldEncoderMap() {
|
|
10
|
+
return {
|
|
11
|
+
[Fields.version]: IntEncoder,
|
|
12
|
+
[Fields.created]: DateEncoder,
|
|
13
|
+
[Fields.lastUpdated]: DateEncoder,
|
|
14
|
+
[Fields.cmpId]: IntEncoder,
|
|
15
|
+
[Fields.cmpVersion]: IntEncoder,
|
|
16
|
+
[Fields.consentScreen]: IntEncoder,
|
|
17
|
+
[Fields.consentLanguage]: LangEncoder,
|
|
18
|
+
[Fields.vendorListVersion]: IntEncoder,
|
|
19
|
+
[Fields.policyVersion]: IntEncoder,
|
|
20
|
+
[Fields.isServiceSpecific]: BooleanEncoder,
|
|
21
|
+
[Fields.useNonStandardStacks]: BooleanEncoder,
|
|
22
|
+
[Fields.specialFeatureOptins]: FixedVectorEncoder,
|
|
23
|
+
[Fields.purposeConsents]: FixedVectorEncoder,
|
|
24
|
+
[Fields.purposeLegitimateInterests]: FixedVectorEncoder,
|
|
25
|
+
[Fields.purposeOneTreatment]: BooleanEncoder,
|
|
26
|
+
[Fields.publisherCountryCode]: LangEncoder,
|
|
27
|
+
[Fields.vendorConsents]: VendorVectorEncoder,
|
|
28
|
+
[Fields.vendorLegitimateInterests]: VendorVectorEncoder,
|
|
29
|
+
[Fields.publisherRestrictions]: PurposeRestrictionVectorEncoder,
|
|
30
|
+
segmentType: IntEncoder,
|
|
31
|
+
[Fields.vendorsDisclosed]: VendorVectorEncoder,
|
|
32
|
+
[Fields.vendorsAllowed]: VendorVectorEncoder,
|
|
33
|
+
[Fields.publisherConsents]: FixedVectorEncoder,
|
|
34
|
+
[Fields.publisherLegitimateInterests]: FixedVectorEncoder,
|
|
35
|
+
[Fields.numCustomPurposes]: IntEncoder,
|
|
36
|
+
[Fields.publisherCustomConsents]: FixedVectorEncoder,
|
|
37
|
+
[Fields.publisherCustomLegitimateInterests]: FixedVectorEncoder,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { BooleanEncoder } from './BooleanEncoder.js';
|
|
2
|
+
import { DecodingError } from '../../errors/index.js';
|
|
3
|
+
import { Vector } from '../../model/index.js';
|
|
4
|
+
export class FixedVectorEncoder {
|
|
5
|
+
static encode(value, numBits) {
|
|
6
|
+
let bitString = '';
|
|
7
|
+
for (let i = 1; i <= numBits; i++) {
|
|
8
|
+
bitString += BooleanEncoder.encode(value.has(i));
|
|
9
|
+
}
|
|
10
|
+
return bitString;
|
|
11
|
+
}
|
|
12
|
+
static decode(value, numBits) {
|
|
13
|
+
if (value.length !== numBits) {
|
|
14
|
+
throw new DecodingError('bitfield encoding length mismatch');
|
|
15
|
+
}
|
|
16
|
+
const vector = new Vector();
|
|
17
|
+
for (let i = 1; i <= numBits; i++) {
|
|
18
|
+
if (BooleanEncoder.decode(value[i - 1])) {
|
|
19
|
+
vector.set(i);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
vector.bitLength = value.length;
|
|
23
|
+
return vector;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { EncodingError, DecodingError, } from '../../errors/index.js';
|
|
2
|
+
export class IntEncoder {
|
|
3
|
+
static encode(value, numBits) {
|
|
4
|
+
let bitString;
|
|
5
|
+
if (typeof value === 'string') {
|
|
6
|
+
value = parseInt(value, 10);
|
|
7
|
+
}
|
|
8
|
+
bitString = value.toString(2);
|
|
9
|
+
if (bitString.length > numBits || value < 0) {
|
|
10
|
+
throw new EncodingError(`${value} too large to encode into ${numBits}`);
|
|
11
|
+
}
|
|
12
|
+
// Pad the string if not filling all bits
|
|
13
|
+
if (bitString.length < numBits) {
|
|
14
|
+
// pad left
|
|
15
|
+
bitString = '0'.repeat(numBits - bitString.length) + bitString;
|
|
16
|
+
}
|
|
17
|
+
return bitString;
|
|
18
|
+
}
|
|
19
|
+
static decode(value, numBits) {
|
|
20
|
+
if (numBits !== value.length) {
|
|
21
|
+
throw new DecodingError('invalid bit length');
|
|
22
|
+
}
|
|
23
|
+
return parseInt(value, 2);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { IntEncoder, } from './IntEncoder.js';
|
|
2
|
+
import { DecodingError, EncodingError, } from '../../errors/index.js';
|
|
3
|
+
export class LangEncoder {
|
|
4
|
+
static encode(value, numBits) {
|
|
5
|
+
value = value.toUpperCase();
|
|
6
|
+
const ASCII_START = 65;
|
|
7
|
+
const firstLetter = value.charCodeAt(0) - ASCII_START;
|
|
8
|
+
const secondLetter = value.charCodeAt(1) - ASCII_START;
|
|
9
|
+
// check some things to throw some good errors
|
|
10
|
+
if (firstLetter < 0 || firstLetter > 25 || secondLetter < 0 || secondLetter > 25) {
|
|
11
|
+
throw new EncodingError(`invalid language code: ${value}`);
|
|
12
|
+
}
|
|
13
|
+
if (numBits % 2 === 1) {
|
|
14
|
+
throw new EncodingError(`numBits must be even, ${numBits} is not valid`);
|
|
15
|
+
}
|
|
16
|
+
numBits = numBits / 2;
|
|
17
|
+
const firstLetterBString = IntEncoder.encode(firstLetter, numBits);
|
|
18
|
+
const secondLetterBString = IntEncoder.encode(secondLetter, numBits);
|
|
19
|
+
return firstLetterBString + secondLetterBString;
|
|
20
|
+
}
|
|
21
|
+
static decode(value, numBits) {
|
|
22
|
+
let retr;
|
|
23
|
+
// is it an even number of bits? we have to divide it
|
|
24
|
+
if (numBits === value.length && !(value.length % 2)) {
|
|
25
|
+
const ASCII_START = 65;
|
|
26
|
+
const mid = value.length / 2;
|
|
27
|
+
const firstLetter = IntEncoder.decode(value.slice(0, mid), mid) + ASCII_START;
|
|
28
|
+
const secondLetter = IntEncoder.decode(value.slice(mid), mid) + ASCII_START;
|
|
29
|
+
retr = String.fromCharCode(firstLetter) + String.fromCharCode(secondLetter);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
throw new DecodingError('invalid bit length for language');
|
|
33
|
+
}
|
|
34
|
+
return retr;
|
|
35
|
+
}
|
|
36
|
+
}
|