@pubtech-ai/core 2.2.0 → 2.3.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/lib/cjs/encoder/field/PurposeRestrictionVectorEncoder.js +1 -1
- package/lib/cjs/model/PurposeRestrictionVector.d.ts +3 -0
- package/lib/cjs/model/PurposeRestrictionVector.js +1 -1
- package/lib/mjs/encoder/field/PurposeRestrictionVectorEncoder.js +37 -23
- package/lib/mjs/model/PurposeRestrictionVector.d.ts +3 -0
- package/lib/mjs/model/PurposeRestrictionVector.js +23 -11
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.PurposeRestrictionVectorEncoder=void 0;var BitLength_js_1=require("../BitLength.js"),BooleanEncoder_js_1=require("./BooleanEncoder.js"),index_js_1=require("../../errors/index.js"),IntEncoder_js_1=require("./IntEncoder.js"),index_js_2=require("../../model/index.js"),PurposeRestrictionVectorEncoder=function(){function e(){}return e.encode=function(e){var n=IntEncoder_js_1.IntEncoder.encode(e.numRestrictions,BitLength_js_1.BitLength.numRestrictions);if(!e.isEmpty()){var t=Array.from(e.gvl.vendorIds),r=function(e,n){var r=t.indexOf(e);if(t.indexOf(n)-r>0){var o=t.indexOf(e+1);return t[o]}return e};e.getRestrictions().forEach((function(t){n+=IntEncoder_js_1.IntEncoder.encode(t.purposeId,BitLength_js_1.BitLength.purposeId),n+=IntEncoder_js_1.IntEncoder.encode(t.restrictionType,BitLength_js_1.BitLength.restrictionType);
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.PurposeRestrictionVectorEncoder=void 0;var BitLength_js_1=require("../BitLength.js"),BooleanEncoder_js_1=require("./BooleanEncoder.js"),index_js_1=require("../../errors/index.js"),IntEncoder_js_1=require("./IntEncoder.js"),index_js_2=require("../../model/index.js"),PurposeRestrictionVectorEncoder=function(){function e(){}return e.encode=function(e){var n=IntEncoder_js_1.IntEncoder.encode(e.numRestrictions,BitLength_js_1.BitLength.numRestrictions);if(!e.isEmpty()){var t=Array.from(e.gvl.vendorIds),r=function(e,n){var r=t.indexOf(e);if(t.indexOf(n)-r>0){var o=t.indexOf(e+1);return t[o]}return e},o=new Map;e.getRestrictions().forEach((function(t){n+=IntEncoder_js_1.IntEncoder.encode(t.purposeId,BitLength_js_1.BitLength.purposeId),n+=IntEncoder_js_1.IntEncoder.encode(t.restrictionType,BitLength_js_1.BitLength.restrictionType);var i=e.getVendorsSet(t),s=0,d="";if(o.has(i)){var _=o.get(i);s=_.numEntries,d=_.rangeField}else{for(var c=Array.from(i).sort((function(e,n){return e-n})),g=c.length,B=0,h=0;h<g;h++){var L=c[h];if(0===B&&(s++,B=L),h===g-1||c[h+1]>r(L,c[g-1])){var j=!(L===B);d+=BooleanEncoder_js_1.BooleanEncoder.encode(j),d+=IntEncoder_js_1.IntEncoder.encode(B,BitLength_js_1.BitLength.vendorId),j&&(d+=IntEncoder_js_1.IntEncoder.encode(L,BitLength_js_1.BitLength.vendorId)),B=0}}o.set(i,{numEntries:s,rangeField:d})}n+=IntEncoder_js_1.IntEncoder.encode(s,BitLength_js_1.BitLength.numEntries),n+=d}))}return n},e.decode=function(e){var n=0,t=new index_js_2.PurposeRestrictionVector,r=IntEncoder_js_1.IntEncoder.decode(e.substr(n,BitLength_js_1.BitLength.numRestrictions),BitLength_js_1.BitLength.numRestrictions);n+=BitLength_js_1.BitLength.numRestrictions;for(var o=0;o<r;o++){var i=IntEncoder_js_1.IntEncoder.decode(e.substr(n,BitLength_js_1.BitLength.purposeId),BitLength_js_1.BitLength.purposeId);n+=BitLength_js_1.BitLength.purposeId;var s=IntEncoder_js_1.IntEncoder.decode(e.substr(n,BitLength_js_1.BitLength.restrictionType),BitLength_js_1.BitLength.restrictionType);n+=BitLength_js_1.BitLength.restrictionType;var d=new index_js_2.PurposeRestriction(i,s),_=IntEncoder_js_1.IntEncoder.decode(e.substr(n,BitLength_js_1.BitLength.numEntries),BitLength_js_1.BitLength.numEntries);n+=BitLength_js_1.BitLength.numEntries;for(var c=0;c<_;c++){var g=BooleanEncoder_js_1.BooleanEncoder.decode(e.substr(n,BitLength_js_1.BitLength.anyBoolean));n+=BitLength_js_1.BitLength.anyBoolean;var B=IntEncoder_js_1.IntEncoder.decode(e.substr(n,BitLength_js_1.BitLength.vendorId),BitLength_js_1.BitLength.vendorId);if(n+=BitLength_js_1.BitLength.vendorId,g){var h=IntEncoder_js_1.IntEncoder.decode(e.substr(n,BitLength_js_1.BitLength.vendorId),BitLength_js_1.BitLength.vendorId);if(n+=BitLength_js_1.BitLength.vendorId,h<B)throw new index_js_1.DecodingError("Invalid RangeEntry: endVendorId ".concat(h," is less than ").concat(B));for(var L=B;L<=h;L++)t.add(L,d)}else t.add(B,d)}}return t.bitLength=n,t},e}();exports.PurposeRestrictionVectorEncoder=PurposeRestrictionVectorEncoder;
|
|
@@ -14,6 +14,7 @@ export declare class PurposeRestrictionVector extends Cloneable<PurposeRestricti
|
|
|
14
14
|
*/
|
|
15
15
|
private map;
|
|
16
16
|
private gvl_;
|
|
17
|
+
private cachedVendorMap;
|
|
17
18
|
private has;
|
|
18
19
|
private isOkToHave;
|
|
19
20
|
/**
|
|
@@ -24,6 +25,7 @@ export declare class PurposeRestrictionVector extends Cloneable<PurposeRestricti
|
|
|
24
25
|
* @return {void}
|
|
25
26
|
*/
|
|
26
27
|
add(vendorId: number, purposeRestriction: PurposeRestriction): void;
|
|
28
|
+
private createOrderedSetOfVendorIdsFromOne;
|
|
27
29
|
/**
|
|
28
30
|
* restrictPurposeToLegalBasis - adds all Vendors under a given Purpose Restriction
|
|
29
31
|
*
|
|
@@ -42,6 +44,7 @@ export declare class PurposeRestrictionVector extends Cloneable<PurposeRestricti
|
|
|
42
44
|
* @return {number[]} - Unique ID set of vendors
|
|
43
45
|
*/
|
|
44
46
|
getVendors(purposeRestriction?: PurposeRestriction): number[];
|
|
47
|
+
getVendorsSet(purposeRestriction?: PurposeRestriction): Set<number>;
|
|
45
48
|
getRestrictionType(vendorId: number, purposeId: number): RestrictionType | undefined;
|
|
46
49
|
/**
|
|
47
50
|
* vendorHasRestriction - determines whether a given Vendor ID is under a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var __extends=this&&this.__extends||function(){var
|
|
1
|
+
"use strict";var __extends=this&&this.__extends||function(){var e=function(t,r){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])})(t,r)};return function(t,r){if("function"!=typeof r&&null!==r)throw new TypeError("Class extends value "+String(r)+" is not a constructor or null");function o(){this.constructor=t}e(t,r),t.prototype=null===r?Object.create(r):(o.prototype=r.prototype,new o)}}(),__read=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var o,n,s=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(o=s.next()).done;)i.push(o.value)}catch(e){n={error:e}}finally{try{o&&!o.done&&(r=s.return)&&r.call(s)}finally{if(n)throw n.error}}return i},__spreadArray=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var o,n=0,s=t.length;n<s;n++)!o&&n in t||(o||(o=Array.prototype.slice.call(t,0,n)),o[n]=t[n]);return e.concat(o||Array.prototype.slice.call(t))};Object.defineProperty(exports,"__esModule",{value:!0}),exports.PurposeRestrictionVector=void 0;var PurposeRestriction_js_1=require("./PurposeRestriction.js"),RestrictionType_js_1=require("./RestrictionType.js"),Cloneable_js_1=require("../Cloneable.js"),PurposeRestrictionVector=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.bitLength=0,t.map=new Map,t}return __extends(t,e),t.prototype.has=function(e){return this.map.has(e)},t.prototype.isOkToHave=function(e,t,r){var o,n=!0;if(null===(o=this.gvl)||void 0===o?void 0:o.vendors){var s=this.gvl.vendors[r];if(s)if(e===RestrictionType_js_1.RestrictionType.NOT_ALLOWED)n=s.legIntPurposes.includes(t)||s.purposes.includes(t);else if(s.flexiblePurposes.length)switch(e){case RestrictionType_js_1.RestrictionType.REQUIRE_CONSENT:n=s.flexiblePurposes.includes(t)&&s.legIntPurposes.includes(t);break;case RestrictionType_js_1.RestrictionType.REQUIRE_LI:n=s.flexiblePurposes.includes(t)&&s.purposes.includes(t)}else n=!1;else n=!1}return n},t.prototype.add=function(e,t){if(this.isOkToHave(t.restrictionType,t.purposeId,e)){var r=t.hash;this.has(r)||(this.map.set(r,new Set),this.bitLength=0),this.map.get(r).add(e)}},t.prototype.createOrderedSetOfVendorIdsFromOne=function(e){if(this.cachedVendorMap||(this.cachedVendorMap=new Map),!this.cachedVendorMap.has(e)){var t=new Set(__spreadArray([],__read(Array(e).keys()),!1).map((function(e){return e+1})));this.cachedVendorMap.set(e,t)}return this.cachedVendorMap.get(e)},t.prototype.restrictPurposeToLegalBasis=function(e){var t=Array.from(this.gvl.vendorIds),r=e.hash,o=t[t.length-1];if(this.has(r))for(var n=1;n<=o;n++)this.map.get(r).add(n);else this.map.set(r,this.createOrderedSetOfVendorIdsFromOne(o)),this.bitLength=0},t.prototype.getVendors=function(e){return Array.from(this.getVendorsSet(e)).sort((function(e,t){return e-t}))},t.prototype.getVendorsSet=function(e){var t=new Set;if(e){var r=e.hash;this.has(r)&&(t=this.map.get(r))}else this.map.forEach((function(e){e.forEach((function(e){t.add(e)}))}));return t},t.prototype.getRestrictionType=function(e,t){var r;return this.getRestrictions(e).forEach((function(e){e.purposeId===t&&(void 0===r||r>e.restrictionType)&&(r=e.restrictionType)})),r},t.prototype.vendorHasRestriction=function(e,t){for(var r=!1,o=this.getRestrictions(e),n=0;n<o.length&&!r;n++)r=t.isSameAs(o[n]);return r},t.prototype.getMaxVendorId=function(){var e=0;return this.map.forEach((function(t){e=Math.max(Array.from(t)[t.size-1],e)})),e},t.prototype.getRestrictions=function(e){var t=[];return this.map.forEach((function(r,o){e?r.has(e)&&t.push(PurposeRestriction_js_1.PurposeRestriction.unHash(o)):t.push(PurposeRestriction_js_1.PurposeRestriction.unHash(o))})),t},t.prototype.getPurposes=function(){var e=new Set;return this.map.forEach((function(t,r){e.add(PurposeRestriction_js_1.PurposeRestriction.unHash(r).purposeId)})),Array.from(e)},t.prototype.remove=function(e,t){var r=t.hash,o=this.map.get(r);o&&(o.delete(e),0==o.size&&(this.map.delete(r),this.bitLength=0))},Object.defineProperty(t.prototype,"gvl",{get:function(){return this.gvl_},set:function(e){var t=this;this.gvl_||(this.gvl_=e,this.map.forEach((function(e,r){var o=PurposeRestriction_js_1.PurposeRestriction.unHash(r);Array.from(e).forEach((function(r){t.isOkToHave(o.restrictionType,o.purposeId,r)||e.delete(r)}))})))},enumerable:!1,configurable:!0}),t.prototype.isEmpty=function(){return 0===this.map.size},Object.defineProperty(t.prototype,"numRestrictions",{get:function(){return this.map.size},enumerable:!1,configurable:!0}),t}(Cloneable_js_1.Cloneable);exports.PurposeRestrictionVector=PurposeRestrictionVector;
|
|
@@ -19,45 +19,58 @@ export class PurposeRestrictionVectorEncoder {
|
|
|
19
19
|
}
|
|
20
20
|
return vendorId;
|
|
21
21
|
};
|
|
22
|
+
const cachedResults = new Map();
|
|
22
23
|
// create each restriction group
|
|
23
24
|
prVector.getRestrictions().forEach((purpRestriction) => {
|
|
24
25
|
// every restriction group has the purposeId and the restrictionType;
|
|
25
26
|
bitString += IntEncoder.encode(purpRestriction.purposeId, BitLength.purposeId);
|
|
26
27
|
bitString += IntEncoder.encode(purpRestriction.restrictionType, BitLength.restrictionType);
|
|
27
28
|
// now get all the vendors under that restriction
|
|
28
|
-
const
|
|
29
|
-
const len = vendors.length;
|
|
29
|
+
const vendorsReference = prVector.getVendorsSet(purpRestriction);
|
|
30
30
|
/**
|
|
31
31
|
* numEntries comes first so we will have to keep a counter and the do
|
|
32
32
|
* the encoding at the end
|
|
33
33
|
*/
|
|
34
34
|
let numEntries = 0;
|
|
35
|
-
let startId = 0;
|
|
36
35
|
let rangeField = '';
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
36
|
+
if (cachedResults.has(vendorsReference)) {
|
|
37
|
+
const cachedResult = cachedResults.get(vendorsReference);
|
|
38
|
+
numEntries = cachedResult.numEntries;
|
|
39
|
+
rangeField = cachedResult.rangeField;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const vendors = Array.from(vendorsReference).sort((a, b) => a - b);
|
|
43
|
+
const len = vendors.length;
|
|
44
|
+
let startId = 0;
|
|
45
|
+
for (let i = 0; i < len; i++) {
|
|
46
|
+
const vendorId = vendors[i];
|
|
47
|
+
if (startId === 0) {
|
|
48
|
+
numEntries++;
|
|
49
|
+
startId = vendorId;
|
|
50
|
+
}
|
|
47
51
|
/**
|
|
48
|
-
*
|
|
49
|
-
* ID
|
|
52
|
+
* either end of the loop or there are GVL vendor IDs before the next one
|
|
50
53
|
*/
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
if (i === len - 1 || vendors[i + 1] > nextGvlVendor(vendorId, vendors[len - 1])) {
|
|
55
|
+
/**
|
|
56
|
+
* it's a range entry if we've got something other than the start
|
|
57
|
+
* ID
|
|
58
|
+
*/
|
|
59
|
+
const isRange = !(vendorId === startId);
|
|
60
|
+
// 0 means single 1 means range
|
|
61
|
+
rangeField += BooleanEncoder.encode(isRange);
|
|
62
|
+
rangeField += IntEncoder.encode(startId, BitLength.vendorId);
|
|
63
|
+
if (isRange) {
|
|
64
|
+
rangeField += IntEncoder.encode(vendorId, BitLength.vendorId);
|
|
65
|
+
}
|
|
66
|
+
// reset the startId so we grab the next id in the list
|
|
67
|
+
startId = 0;
|
|
57
68
|
}
|
|
58
|
-
// reset the startId so we grab the next id in the list
|
|
59
|
-
startId = 0;
|
|
60
69
|
}
|
|
70
|
+
cachedResults.set(vendorsReference, {
|
|
71
|
+
numEntries,
|
|
72
|
+
rangeField,
|
|
73
|
+
});
|
|
61
74
|
}
|
|
62
75
|
/**
|
|
63
76
|
* now that the range encoding is built, encode the number of ranges
|
|
@@ -96,6 +109,7 @@ export class PurposeRestrictionVectorEncoder {
|
|
|
96
109
|
if (endVendorId < startOrOnlyVendorId) {
|
|
97
110
|
throw new DecodingError(`Invalid RangeEntry: endVendorId ${endVendorId} is less than ${startOrOnlyVendorId}`);
|
|
98
111
|
}
|
|
112
|
+
// Can be optimized if required
|
|
99
113
|
for (let k = startOrOnlyVendorId; k <= endVendorId; k++) {
|
|
100
114
|
vector.add(k, purposeRestriction);
|
|
101
115
|
}
|
|
@@ -14,6 +14,7 @@ export declare class PurposeRestrictionVector extends Cloneable<PurposeRestricti
|
|
|
14
14
|
*/
|
|
15
15
|
private map;
|
|
16
16
|
private gvl_;
|
|
17
|
+
private cachedVendorMap;
|
|
17
18
|
private has;
|
|
18
19
|
private isOkToHave;
|
|
19
20
|
/**
|
|
@@ -24,6 +25,7 @@ export declare class PurposeRestrictionVector extends Cloneable<PurposeRestricti
|
|
|
24
25
|
* @return {void}
|
|
25
26
|
*/
|
|
26
27
|
add(vendorId: number, purposeRestriction: PurposeRestriction): void;
|
|
28
|
+
private createOrderedSetOfVendorIdsFromOne;
|
|
27
29
|
/**
|
|
28
30
|
* restrictPurposeToLegalBasis - adds all Vendors under a given Purpose Restriction
|
|
29
31
|
*
|
|
@@ -42,6 +44,7 @@ export declare class PurposeRestrictionVector extends Cloneable<PurposeRestricti
|
|
|
42
44
|
* @return {number[]} - Unique ID set of vendors
|
|
43
45
|
*/
|
|
44
46
|
getVendors(purposeRestriction?: PurposeRestriction): number[];
|
|
47
|
+
getVendorsSet(purposeRestriction?: PurposeRestriction): Set<number>;
|
|
45
48
|
getRestrictionType(vendorId: number, purposeId: number): RestrictionType | undefined;
|
|
46
49
|
/**
|
|
47
50
|
* vendorHasRestriction - determines whether a given Vendor ID is under a
|
|
@@ -13,6 +13,7 @@ export class PurposeRestrictionVector extends Cloneable {
|
|
|
13
13
|
*/
|
|
14
14
|
map = new Map();
|
|
15
15
|
gvl_;
|
|
16
|
+
cachedVendorMap;
|
|
16
17
|
has(hash) {
|
|
17
18
|
return this.map.has(hash);
|
|
18
19
|
}
|
|
@@ -89,6 +90,19 @@ export class PurposeRestrictionVector extends Cloneable {
|
|
|
89
90
|
this.map.get(hash).add(vendorId);
|
|
90
91
|
}
|
|
91
92
|
}
|
|
93
|
+
createOrderedSetOfVendorIdsFromOne(lastEntry) {
|
|
94
|
+
if (!this.cachedVendorMap) {
|
|
95
|
+
this.cachedVendorMap = new Map();
|
|
96
|
+
}
|
|
97
|
+
if (!this.cachedVendorMap.has(lastEntry)) {
|
|
98
|
+
/**
|
|
99
|
+
* Create an ordered array of vendor IDs from `1` (the minimum value for Vendor ID) to `lastEntry`
|
|
100
|
+
*/
|
|
101
|
+
const orderedSet = new Set([...Array(lastEntry).keys()].map((i) => i + 1));
|
|
102
|
+
this.cachedVendorMap.set(lastEntry, orderedSet);
|
|
103
|
+
}
|
|
104
|
+
return this.cachedVendorMap.get(lastEntry);
|
|
105
|
+
}
|
|
92
106
|
/**
|
|
93
107
|
* restrictPurposeToLegalBasis - adds all Vendors under a given Purpose Restriction
|
|
94
108
|
*
|
|
@@ -99,12 +113,8 @@ export class PurposeRestrictionVector extends Cloneable {
|
|
|
99
113
|
const vendors = Array.from(this.gvl.vendorIds);
|
|
100
114
|
const hash = purposeRestriction.hash;
|
|
101
115
|
const lastEntry = vendors[vendors.length - 1];
|
|
102
|
-
/**
|
|
103
|
-
* Create an ordered array of vendor IDs from `1` (the minimum value for Vendor ID) to `lastEntry`
|
|
104
|
-
*/
|
|
105
|
-
const values = [...Array(lastEntry).keys()].map((i) => i + 1);
|
|
106
116
|
if (!this.has(hash)) {
|
|
107
|
-
this.map.set(hash,
|
|
117
|
+
this.map.set(hash, this.createOrderedSetOfVendorIdsFromOne(lastEntry));
|
|
108
118
|
this.bitLength = 0;
|
|
109
119
|
}
|
|
110
120
|
else {
|
|
@@ -129,23 +139,25 @@ export class PurposeRestrictionVector extends Cloneable {
|
|
|
129
139
|
* @return {number[]} - Unique ID set of vendors
|
|
130
140
|
*/
|
|
131
141
|
getVendors(purposeRestriction) {
|
|
132
|
-
|
|
142
|
+
const vendorIds = Array.from(this.getVendorsSet(purposeRestriction));
|
|
143
|
+
return vendorIds.sort((a, b) => a - b);
|
|
144
|
+
}
|
|
145
|
+
getVendorsSet(purposeRestriction) {
|
|
146
|
+
let vendorIds = new Set();
|
|
133
147
|
if (purposeRestriction) {
|
|
134
148
|
const hash = purposeRestriction.hash;
|
|
135
149
|
if (this.has(hash)) {
|
|
136
|
-
vendorIds =
|
|
150
|
+
vendorIds = this.map.get(hash);
|
|
137
151
|
}
|
|
138
152
|
}
|
|
139
153
|
else {
|
|
140
|
-
const vendorSet = new Set();
|
|
141
154
|
this.map.forEach((set) => {
|
|
142
155
|
set.forEach((vendorId) => {
|
|
143
|
-
|
|
156
|
+
vendorIds.add(vendorId);
|
|
144
157
|
});
|
|
145
158
|
});
|
|
146
|
-
vendorIds = Array.from(vendorSet);
|
|
147
159
|
}
|
|
148
|
-
return vendorIds
|
|
160
|
+
return vendorIds;
|
|
149
161
|
}
|
|
150
162
|
getRestrictionType(vendorId, purposeId) {
|
|
151
163
|
let rType;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pubtech-ai/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Ensures consistent encoding and decoding of TC Signals for the iab. Transparency and Consent Framework (TCF).",
|
|
5
5
|
"author": "Mayank Mishra <mayank@iabtechlab.com>",
|
|
6
6
|
"homepage": "https://iabtcf.com/",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"test-cov": "rm -rf coverage; nyc --reporter=html mocha"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@pubtech-ai/testing": "2.
|
|
31
|
+
"@pubtech-ai/testing": "2.3.0",
|
|
32
32
|
"@istanbuljs/nyc-config-typescript": "^0.1.3",
|
|
33
33
|
"@types/sinon": "^10.0.11",
|
|
34
34
|
"@types/sinon-chai": "3.2.8",
|