pico-auth 0.0.34 → 0.0.39
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/pico-auth.d.ts
CHANGED
|
@@ -48,7 +48,7 @@ declare module "core/auth" {
|
|
|
48
48
|
export const authenticateWithScratchCard: (cardCode: string, userProvider: UserProvider, scratchCardProvider: ScratchCardProvider, jwtSpecs: JWTSpecs, requesterLogin?: string) => Promise<{
|
|
49
49
|
token: any;
|
|
50
50
|
refreshToken: any;
|
|
51
|
-
user:
|
|
51
|
+
user: BaseUser;
|
|
52
52
|
}>;
|
|
53
53
|
/**
|
|
54
54
|
* When new short lived token is requested
|
|
@@ -61,6 +61,7 @@ declare module "core/auth" {
|
|
|
61
61
|
*/
|
|
62
62
|
export const refreshToken: (login: string, refreshToken: string, userProvider: UserProvider, jwtSpecs: JWTSpecs) => Promise<{
|
|
63
63
|
token: any;
|
|
64
|
+
refreshToken: any;
|
|
64
65
|
}>;
|
|
65
66
|
/**
|
|
66
67
|
* Will prepare user for MFA activation. Next step is to call verify with token generated in MFA app by the user.
|
package/dist/pico-auth.esm.js
CHANGED
|
@@ -132,15 +132,15 @@ const authenticateWithScratchCard = async (cardCode, userProvider, scratchCardPr
|
|
|
132
132
|
throw new Error(`Failed card authentication attempt ${requesterLogin} (Blocked as Target)`);
|
|
133
133
|
// ok so we will use targetUser as a user that will be actually logged in
|
|
134
134
|
// in impersonation scenario targetUser may be different then the user.
|
|
135
|
-
const
|
|
135
|
+
const jwtData = await issueJwtToken(targetUser, userProvider, jwtSpecs, false);
|
|
136
136
|
let refreshToken;
|
|
137
137
|
if (jwtSpecs.refreshExpiryTimeMs)
|
|
138
138
|
refreshToken = (await issueJwtToken(targetUser, userProvider, jwtSpecs, true)).token;
|
|
139
139
|
console.info(`Card authentication success. Requester:${requesterLogin} Target:${targetUser.id}`);
|
|
140
140
|
return {
|
|
141
|
-
token,
|
|
141
|
+
token: jwtData.token,
|
|
142
142
|
refreshToken,
|
|
143
|
-
user:
|
|
143
|
+
user: jwtData.clearedUser // just in case its impersonation so the actual resulting user will be different that the requester login user
|
|
144
144
|
};
|
|
145
145
|
}
|
|
146
146
|
catch (error) {
|
|
@@ -172,8 +172,11 @@ const refreshToken = async (login, refreshToken, userProvider, jwtSpecs) => {
|
|
|
172
172
|
throw new Error(`Failed refresh token attempt ${login} (Invalid Token)`);
|
|
173
173
|
}
|
|
174
174
|
const token = (await issueJwtToken(user, userProvider, jwtSpecs, false)).token;
|
|
175
|
+
let newRefreshToken;
|
|
176
|
+
if (jwtSpecs.refreshExpiryTimeMs)
|
|
177
|
+
newRefreshToken = (await issueJwtToken(user, userProvider, jwtSpecs, true)).token;
|
|
175
178
|
console.log(`Successful token refresh: ${user.id}`);
|
|
176
|
-
return { token };
|
|
179
|
+
return { token, refreshToken: newRefreshToken };
|
|
177
180
|
}
|
|
178
181
|
else {
|
|
179
182
|
throw new Error(`Failed refresh token attempt ${login}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const speakeasy=require("speakeasy"),qrcode=require("qrcode"),md5=require("md5"),jwt=require("jsonwebtoken"),issueJwtToken=async(e,t,r,a)=>{let i=r.secretKey,o=t.getSafeUser?await t.getSafeUser(e):e;o=t.getUserPostAuthenticate?await t.getUserPostAuthenticate(o):o;let s,n={time:Date.now(),user:o};return a&&r.refreshExpiryTimeMs?s=jwt.sign(n,i,{expiresIn:r.refreshExpiryTimeMs}):r.expiryTimeMs&&(s=jwt.sign(n,i,{expiresIn:r.expiryTimeMs})),{token:s,clearedUser:o}},authenticate=async(e,t,r,a,i,o,s)=>{var n;let c=await i.getUser(e);const l=i.userSecretPath?c[i.userSecretPath]:c.mfa,d=i.userPasswordPath?c[i.userPasswordPath]:c.password;if(null==l?void 0:l.enabled){if(!speakeasy.totp.verify({secret:null===(n=null==l?void 0:l.secret)||void 0===n?void 0:n.actual,encoding:"base32",token:r,window:1}))throw new Error(`Failed authentication attempt ${e} (MFA Enabled)`)}if(c.blocked)throw new Error(`Failed authentication attempt ${e} (Blocked)`);if(md5(t||"")==d){const e=a,t=c;if(e){let r=!1;if(e.startsWith("@")){if(r=r||await o.canImpersonate(c,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);await o.impersonateOrg(c,e)}else{const a=await i.getUser(e);if(r=r||await o.canImpersonate(c,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);c=a}console.info(`Impersonate success. From: ${t.login} into ${e}`)}const r=(await issueJwtToken(c,i,s,!1)).token;let n;return s.refreshExpiryTimeMs&&(n=(await issueJwtToken(c,i,s,!0)).token),console.log(`Successful login: ${c.id}`),{token:r,refreshToken:n}}throw new Error(`Failed authentication attempt ${e}`)},authenticateWithScratchCard=async(e,t,r,a,i)=>{let o,s=i?await t.getUser(i):void 0;if(s&&s.blocked)throw new Error(`Failed card authentication attempt ${i} (Blocked)`);if(i&&!s)throw new Error(`Failed card authentication attempt ${i} (Missing user)`);try{o=await r.consume(e,s)}catch(e){throw new Error(`Failed card authentication attempt ${i} (Consume Failed)`)}try{if(!o)throw new Error(`Failed card authentication attempt ${i} (Consume Failed)`);if(o.blocked)throw new Error(`Failed card authentication attempt ${i} (Blocked as Target)`);const e=
|
|
1
|
+
const speakeasy=require("speakeasy"),qrcode=require("qrcode"),md5=require("md5"),jwt=require("jsonwebtoken"),issueJwtToken=async(e,t,r,a)=>{let i=r.secretKey,o=t.getSafeUser?await t.getSafeUser(e):e;o=t.getUserPostAuthenticate?await t.getUserPostAuthenticate(o):o;let s,n={time:Date.now(),user:o};return a&&r.refreshExpiryTimeMs?s=jwt.sign(n,i,{expiresIn:r.refreshExpiryTimeMs}):r.expiryTimeMs&&(s=jwt.sign(n,i,{expiresIn:r.expiryTimeMs})),{token:s,clearedUser:o}},authenticate=async(e,t,r,a,i,o,s)=>{var n;let c=await i.getUser(e);const l=i.userSecretPath?c[i.userSecretPath]:c.mfa,d=i.userPasswordPath?c[i.userPasswordPath]:c.password;if(null==l?void 0:l.enabled){if(!speakeasy.totp.verify({secret:null===(n=null==l?void 0:l.secret)||void 0===n?void 0:n.actual,encoding:"base32",token:r,window:1}))throw new Error(`Failed authentication attempt ${e} (MFA Enabled)`)}if(c.blocked)throw new Error(`Failed authentication attempt ${e} (Blocked)`);if(md5(t||"")==d){const e=a,t=c;if(e){let r=!1;if(e.startsWith("@")){if(r=r||await o.canImpersonate(c,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);await o.impersonateOrg(c,e)}else{const a=await i.getUser(e);if(r=r||await o.canImpersonate(c,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);c=a}console.info(`Impersonate success. From: ${t.login} into ${e}`)}const r=(await issueJwtToken(c,i,s,!1)).token;let n;return s.refreshExpiryTimeMs&&(n=(await issueJwtToken(c,i,s,!0)).token),console.log(`Successful login: ${c.id}`),{token:r,refreshToken:n}}throw new Error(`Failed authentication attempt ${e}`)},authenticateWithScratchCard=async(e,t,r,a,i)=>{let o,s=i?await t.getUser(i):void 0;if(s&&s.blocked)throw new Error(`Failed card authentication attempt ${i} (Blocked)`);if(i&&!s)throw new Error(`Failed card authentication attempt ${i} (Missing user)`);try{o=await r.consume(e,s)}catch(e){throw new Error(`Failed card authentication attempt ${i} (Consume Failed)`)}try{if(!o)throw new Error(`Failed card authentication attempt ${i} (Consume Failed)`);if(o.blocked)throw new Error(`Failed card authentication attempt ${i} (Blocked as Target)`);const e=await issueJwtToken(o,t,a,!1);let r;return a.refreshExpiryTimeMs&&(r=(await issueJwtToken(o,t,a,!0)).token),console.info(`Card authentication success. Requester:${i} Target:${o.id}`),{token:e.token,refreshToken:r,user:e.clearedUser}}catch(e){throw new Error(`Failed card authentication attempt ${i}`)}},refreshToken=async(e,t,r,a)=>{let i=await r.getUser(e);if(i.blocked)throw new Error(`Failed refresh token attempt ${e} (Blocked)`);if(t){let o=a.secretKey;const s=jwt.verify(t,o).user;if(!s||s.id!=i.id)throw new Error(`Failed refresh token attempt ${e} (Invalid Token)`);const n=(await issueJwtToken(i,r,a,!1)).token;let c;return a.refreshExpiryTimeMs&&(c=(await issueJwtToken(i,r,a,!0)).token),console.log(`Successful token refresh: ${i.id}`),{token:n,refreshToken:c}}throw new Error(`Failed refresh token attempt ${e}`)},mfaRegister=async(e,t,r)=>new Promise(async(a,i)=>{let o=await r.getUser(t),s=r.userSecretPath?o[r.userSecretPath]:o.mfa;const n=speakeasy.generateSecret({name:`${e}: ${t}`});if(!s){s={secret:{temp:void 0,actual:void 0},enabled:!1};o[r.userSecretPath?r.userSecretPath:"mfa"]=s}s.secret.temp=n.base32,s.secret.actual=void 0,await r.putUser(o),qrcode.toDataURL(n.otpauth_url,(e,t)=>{if(e)throw new Error("Error generating QR code");a({qr_code:t,secret:n.base32})})}),mfaVerify=async(e,t,r)=>{var a,i;const o=t;let s=await r.getUser(e);const n=r.userSecretPath?s[r.userSecretPath]:s.mfa;return speakeasy.totp.verify({secret:null===(a=null==n?void 0:n.secret)||void 0===a?void 0:a.temp,encoding:"base32",token:o})?(n.secret.actual=null===(i=null==n?void 0:n.secret)||void 0===i?void 0:i.temp,n.enabled=!0,await r.putUser(s),!0):(console.log(`Failed mfa verification for ${e}`),!1)},mfaEnabled=async(e,t)=>{let r=await t.getUser(e);const a=t.userSecretPath?r[t.userSecretPath]:r.mfa;return(null==a?void 0:a.enabled)||!1};export{authenticate,authenticateWithScratchCard,issueJwtToken,mfaEnabled,mfaRegister,mfaVerify,refreshToken};
|
package/dist/pico-auth.umd.js
CHANGED
|
@@ -138,15 +138,15 @@
|
|
|
138
138
|
throw new Error(`Failed card authentication attempt ${requesterLogin} (Blocked as Target)`);
|
|
139
139
|
// ok so we will use targetUser as a user that will be actually logged in
|
|
140
140
|
// in impersonation scenario targetUser may be different then the user.
|
|
141
|
-
const
|
|
141
|
+
const jwtData = await issueJwtToken(targetUser, userProvider, jwtSpecs, false);
|
|
142
142
|
let refreshToken;
|
|
143
143
|
if (jwtSpecs.refreshExpiryTimeMs)
|
|
144
144
|
refreshToken = (await issueJwtToken(targetUser, userProvider, jwtSpecs, true)).token;
|
|
145
145
|
console.info(`Card authentication success. Requester:${requesterLogin} Target:${targetUser.id}`);
|
|
146
146
|
return {
|
|
147
|
-
token,
|
|
147
|
+
token: jwtData.token,
|
|
148
148
|
refreshToken,
|
|
149
|
-
user:
|
|
149
|
+
user: jwtData.clearedUser // just in case its impersonation so the actual resulting user will be different that the requester login user
|
|
150
150
|
};
|
|
151
151
|
}
|
|
152
152
|
catch (error) {
|
|
@@ -178,8 +178,11 @@
|
|
|
178
178
|
throw new Error(`Failed refresh token attempt ${login} (Invalid Token)`);
|
|
179
179
|
}
|
|
180
180
|
const token = (await issueJwtToken(user, userProvider, jwtSpecs, false)).token;
|
|
181
|
+
let newRefreshToken;
|
|
182
|
+
if (jwtSpecs.refreshExpiryTimeMs)
|
|
183
|
+
newRefreshToken = (await issueJwtToken(user, userProvider, jwtSpecs, true)).token;
|
|
181
184
|
console.log(`Successful token refresh: ${user.id}`);
|
|
182
|
-
return { token };
|
|
185
|
+
return { token, refreshToken: newRefreshToken };
|
|
183
186
|
}
|
|
184
187
|
else {
|
|
185
188
|
throw new Error(`Failed refresh token attempt ${login}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).picoAuth={})}(this,function(e){"use strict";const t=require("speakeasy"),r=require("qrcode"),a=require("md5"),o=require("jsonwebtoken"),i=async(e,t,r,a)=>{let i=r.secretKey,n=t.getSafeUser?await t.getSafeUser(e):e;n=t.getUserPostAuthenticate?await t.getUserPostAuthenticate(n):n;let s,c={time:Date.now(),user:n};return a&&r.refreshExpiryTimeMs?s=o.sign(c,i,{expiresIn:r.refreshExpiryTimeMs}):r.expiryTimeMs&&(s=o.sign(c,i,{expiresIn:r.expiryTimeMs})),{token:s,clearedUser:n}};e.authenticate=async(e,r,o,n,s,c,l)=>{var d;let u=await s.getUser(e);const f=s.userSecretPath?u[s.userSecretPath]:u.mfa,h=s.userPasswordPath?u[s.userPasswordPath]:u.password;if(null==f?void 0:f.enabled){if(!t.totp.verify({secret:null===(d=null==f?void 0:f.secret)||void 0===d?void 0:d.actual,encoding:"base32",token:o,window:1}))throw new Error(`Failed authentication attempt ${e} (MFA Enabled)`)}if(u.blocked)throw new Error(`Failed authentication attempt ${e} (Blocked)`);if(a(r||"")==h){const e=n,t=u;if(e){let r=!1;if(e.startsWith("@")){if(r=r||await c.canImpersonate(u,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);await c.impersonateOrg(u,e)}else{const a=await s.getUser(e);if(r=r||await c.canImpersonate(u,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);u=a}console.info(`Impersonate success. From: ${t.login} into ${e}`)}const r=(await i(u,s,l,!1)).token;let a;return l.refreshExpiryTimeMs&&(a=(await i(u,s,l,!0)).token),console.log(`Successful login: ${u.id}`),{token:r,refreshToken:a}}throw new Error(`Failed authentication attempt ${e}`)},e.authenticateWithScratchCard=async(e,t,r,a,o)=>{let n,s=o?await t.getUser(o):void 0;if(s&&s.blocked)throw new Error(`Failed card authentication attempt ${o} (Blocked)`);if(o&&!s)throw new Error(`Failed card authentication attempt ${o} (Missing user)`);try{n=await r.consume(e,s)}catch(e){throw new Error(`Failed card authentication attempt ${o} (Consume Failed)`)}try{if(!n)throw new Error(`Failed card authentication attempt ${o} (Consume Failed)`);if(n.blocked)throw new Error(`Failed card authentication attempt ${o} (Blocked as Target)`);const e=
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).picoAuth={})}(this,function(e){"use strict";const t=require("speakeasy"),r=require("qrcode"),a=require("md5"),o=require("jsonwebtoken"),i=async(e,t,r,a)=>{let i=r.secretKey,n=t.getSafeUser?await t.getSafeUser(e):e;n=t.getUserPostAuthenticate?await t.getUserPostAuthenticate(n):n;let s,c={time:Date.now(),user:n};return a&&r.refreshExpiryTimeMs?s=o.sign(c,i,{expiresIn:r.refreshExpiryTimeMs}):r.expiryTimeMs&&(s=o.sign(c,i,{expiresIn:r.expiryTimeMs})),{token:s,clearedUser:n}};e.authenticate=async(e,r,o,n,s,c,l)=>{var d;let u=await s.getUser(e);const f=s.userSecretPath?u[s.userSecretPath]:u.mfa,h=s.userPasswordPath?u[s.userPasswordPath]:u.password;if(null==f?void 0:f.enabled){if(!t.totp.verify({secret:null===(d=null==f?void 0:f.secret)||void 0===d?void 0:d.actual,encoding:"base32",token:o,window:1}))throw new Error(`Failed authentication attempt ${e} (MFA Enabled)`)}if(u.blocked)throw new Error(`Failed authentication attempt ${e} (Blocked)`);if(a(r||"")==h){const e=n,t=u;if(e){let r=!1;if(e.startsWith("@")){if(r=r||await c.canImpersonate(u,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);await c.impersonateOrg(u,e)}else{const a=await s.getUser(e);if(r=r||await c.canImpersonate(u,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);u=a}console.info(`Impersonate success. From: ${t.login} into ${e}`)}const r=(await i(u,s,l,!1)).token;let a;return l.refreshExpiryTimeMs&&(a=(await i(u,s,l,!0)).token),console.log(`Successful login: ${u.id}`),{token:r,refreshToken:a}}throw new Error(`Failed authentication attempt ${e}`)},e.authenticateWithScratchCard=async(e,t,r,a,o)=>{let n,s=o?await t.getUser(o):void 0;if(s&&s.blocked)throw new Error(`Failed card authentication attempt ${o} (Blocked)`);if(o&&!s)throw new Error(`Failed card authentication attempt ${o} (Missing user)`);try{n=await r.consume(e,s)}catch(e){throw new Error(`Failed card authentication attempt ${o} (Consume Failed)`)}try{if(!n)throw new Error(`Failed card authentication attempt ${o} (Consume Failed)`);if(n.blocked)throw new Error(`Failed card authentication attempt ${o} (Blocked as Target)`);const e=await i(n,t,a,!1);let r;return a.refreshExpiryTimeMs&&(r=(await i(n,t,a,!0)).token),console.info(`Card authentication success. Requester:${o} Target:${n.id}`),{token:e.token,refreshToken:r,user:e.clearedUser}}catch(e){throw new Error(`Failed card authentication attempt ${o}`)}},e.issueJwtToken=i,e.mfaEnabled=async(e,t)=>{let r=await t.getUser(e);const a=t.userSecretPath?r[t.userSecretPath]:r.mfa;return(null==a?void 0:a.enabled)||!1},e.mfaRegister=async(e,a,o)=>new Promise(async(i,n)=>{let s=await o.getUser(a),c=o.userSecretPath?s[o.userSecretPath]:s.mfa;const l=t.generateSecret({name:`${e}: ${a}`});if(!c){c={secret:{temp:void 0,actual:void 0},enabled:!1};s[o.userSecretPath?o.userSecretPath:"mfa"]=c}c.secret.temp=l.base32,c.secret.actual=void 0,await o.putUser(s),r.toDataURL(l.otpauth_url,(e,t)=>{if(e)throw new Error("Error generating QR code");i({qr_code:t,secret:l.base32})})}),e.mfaVerify=async(e,r,a)=>{var o,i;const n=r;let s=await a.getUser(e);const c=a.userSecretPath?s[a.userSecretPath]:s.mfa;return t.totp.verify({secret:null===(o=null==c?void 0:c.secret)||void 0===o?void 0:o.temp,encoding:"base32",token:n})?(c.secret.actual=null===(i=null==c?void 0:c.secret)||void 0===i?void 0:i.temp,c.enabled=!0,await a.putUser(s),!0):(console.log(`Failed mfa verification for ${e}`),!1)},e.refreshToken=async(e,t,r,a)=>{let n=await r.getUser(e);if(n.blocked)throw new Error(`Failed refresh token attempt ${e} (Blocked)`);if(t){let s=a.secretKey;const c=o.verify(t,s).user;if(!c||c.id!=n.id)throw new Error(`Failed refresh token attempt ${e} (Invalid Token)`);const l=(await i(n,r,a,!1)).token;let d;return a.refreshExpiryTimeMs&&(d=(await i(n,r,a,!0)).token),console.log(`Successful token refresh: ${n.id}`),{token:l,refreshToken:d}}throw new Error(`Failed refresh token attempt ${e}`)},Object.defineProperty(e,"__esModule",{value:!0})});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pico-auth",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.39",
|
|
4
4
|
"description": "Minimal auth with user/pass, impersonation and mfa authentication",
|
|
5
5
|
"main": "dist/pico-auth.umd.js",
|
|
6
6
|
"types": "dist/pico-auth.d.ts",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"build:minjs:umd": "terser dist/pico-auth.umd.js --compress --mangle > dist/pico-auth.umd.min.js",
|
|
36
36
|
"build:minjs:esm": "terser dist/pico-auth.esm.js --compress --mangle > dist/pico-auth.esm.min.js",
|
|
37
37
|
"build:types": "tsc -t esnext --moduleResolution node -d --emitDeclarationOnly --outFile dist/pico-auth.d.ts src/pico-auth.ts",
|
|
38
|
-
"test": "
|
|
38
|
+
"test": "jest --coverage",
|
|
39
|
+
"test:jest": "jest",
|
|
39
40
|
"coverage": "nyc --reporter html --reporter text npm test",
|
|
40
41
|
"release-minor": "npm install && npm update && npm run build && npm run coverage && npm version minor && git push origin && git push origin --tags && npm publish",
|
|
41
42
|
"release-patch": "npm install && npm update && npm run build && npm version patch && npm publish"
|
|
@@ -57,20 +58,20 @@
|
|
|
57
58
|
"@rollup/plugin-commonjs": "28.0.1",
|
|
58
59
|
"@types/chai": "4.2.14",
|
|
59
60
|
"@types/chai-as-promised": "7.1.3",
|
|
60
|
-
"@types/
|
|
61
|
+
"@types/jest": "^29.5.12",
|
|
61
62
|
"@types/node": "20.5.0",
|
|
62
|
-
"@types/sinon": "9.0.8",
|
|
63
63
|
"@typescript-eslint/eslint-plugin": "4.6.1",
|
|
64
64
|
"@typescript-eslint/parser": "4.6.1",
|
|
65
65
|
"chai": "^3.5.0",
|
|
66
66
|
"chai-as-promised": "^6.0.0",
|
|
67
67
|
"eslint": "7.12.1",
|
|
68
|
-
"
|
|
68
|
+
"jest": "^29.7.0",
|
|
69
69
|
"nyc": "14.1.x",
|
|
70
70
|
"prettier": "^2.8.8",
|
|
71
71
|
"pretty-quick": "^3.1.3",
|
|
72
72
|
"rollup": "^2.79.1",
|
|
73
73
|
"sinon": "^9.2.4",
|
|
74
|
+
"ts-jest": "^29.1.1",
|
|
74
75
|
"terser": "^5.19.2",
|
|
75
76
|
"ts-node": "10.8.2",
|
|
76
77
|
"tslib": "^2.6.2",
|