passport-entra 0.0.3 → 0.0.4
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/README.md +29 -0
- package/dist/passport-entra.d.ts +9 -14
- package/dist/passport-entra.js +52 -57
- package/package.json +25 -21
- package/passport-entra.ts +120 -130
- package/tsconfig.json +4 -7
package/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# README #
|
|
2
|
+
|
|
3
|
+
This README would normally document whatever steps are necessary to get your application up and running.
|
|
4
|
+
|
|
5
|
+
### What is this repository for? ###
|
|
6
|
+
|
|
7
|
+
* Quick summary
|
|
8
|
+
* Version
|
|
9
|
+
* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
|
|
10
|
+
|
|
11
|
+
### How do I get set up? ###
|
|
12
|
+
|
|
13
|
+
* Summary of set up
|
|
14
|
+
* Configuration
|
|
15
|
+
* Dependencies
|
|
16
|
+
* Database configuration
|
|
17
|
+
* How to run tests
|
|
18
|
+
* Deployment instructions
|
|
19
|
+
|
|
20
|
+
### Contribution guidelines ###
|
|
21
|
+
|
|
22
|
+
* Writing tests
|
|
23
|
+
* Code review
|
|
24
|
+
* Other guidelines
|
|
25
|
+
|
|
26
|
+
### Who do I talk to? ###
|
|
27
|
+
|
|
28
|
+
* Repo owner or admin
|
|
29
|
+
* Other community or team contact
|
package/dist/passport-entra.d.ts
CHANGED
|
@@ -1,36 +1,31 @@
|
|
|
1
1
|
import { Strategy } from 'passport';
|
|
2
|
+
import type { AuthenticateCallback } from 'passport';
|
|
2
3
|
import { createRemoteJWKSet } from 'jose';
|
|
3
4
|
import type { Request } from 'express';
|
|
4
5
|
/**
|
|
5
6
|
* Bearerstrategy options
|
|
6
7
|
*/
|
|
7
8
|
export interface BearerOptions {
|
|
8
|
-
/** If
|
|
9
|
+
/** If provided, check aud claim against this audience, otherwise use clientID. */
|
|
9
10
|
audience?: string | string[];
|
|
10
|
-
/** ClientID of app in Entra, check aud claim against clientID if audience
|
|
11
|
+
/** ClientID of app in Entra, check aud claim against clientID if audience is not provided. */
|
|
11
12
|
clientID: string;
|
|
12
|
-
/**
|
|
13
|
+
/** Clock skew allowed when checking nbf and exp claims. */
|
|
13
14
|
clockSkew?: string | number;
|
|
14
|
-
/**
|
|
15
|
+
/** Well known oid-configuration endpoint, i.e. _issuer_/.well-known/openid-configuration. */
|
|
15
16
|
identityMetadata: string;
|
|
16
|
-
/** iss claim
|
|
17
|
+
/** Check iss claim, i.e. https://login.microsoftonline.com/_tenantID_/v2.0 - tenantID of app in Entra */
|
|
17
18
|
issuer: string | string[];
|
|
18
|
-
/** scp claim
|
|
19
|
+
/** Check scp claim. */
|
|
19
20
|
scope?: string[];
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
22
23
|
* Called by this BearerStrategy when the token has been validated.
|
|
23
24
|
* Use the token (the bearer JWT payload) to find the user. Then call done.
|
|
24
25
|
*/
|
|
25
|
-
export type VerifyCallback = (token: object, done:
|
|
26
|
+
export type VerifyCallback = (token: object, done: AuthenticateCallback) => void;
|
|
26
27
|
/**
|
|
27
|
-
*
|
|
28
|
-
* The user ends up in req.user, the info in req.authInfo.
|
|
29
|
-
* An error results in an authentication failure. Status is ignored.
|
|
30
|
-
*/
|
|
31
|
-
export type VerifyCompleteCallback = (err: unknown, user?: Express.User | false | null, info?: object, status?: number | number[]) => void;
|
|
32
|
-
/**
|
|
33
|
-
* Bearerstrategy for password.js, used with Microsoft Entra as identity provider
|
|
28
|
+
* Bearerstrategy for password.js, used with Microsoft Entra as identity provider.
|
|
34
29
|
*/
|
|
35
30
|
export default class BearerStrategy extends Strategy {
|
|
36
31
|
audience: string[];
|
package/dist/passport-entra.js
CHANGED
|
@@ -1,91 +1,86 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
const passport_1 = require("passport");
|
|
13
4
|
const jose_1 = require("jose");
|
|
14
5
|
/**
|
|
15
|
-
* Bearerstrategy for password.js, used with Microsoft Entra as identity provider
|
|
6
|
+
* Bearerstrategy for password.js, used with Microsoft Entra as identity provider.
|
|
16
7
|
*/
|
|
17
8
|
class BearerStrategy extends passport_1.Strategy {
|
|
9
|
+
audience;
|
|
10
|
+
clientID;
|
|
11
|
+
clockSkew;
|
|
12
|
+
identityMetadata;
|
|
13
|
+
issuer;
|
|
14
|
+
name = 'oauth-bearer';
|
|
15
|
+
scope;
|
|
16
|
+
verifyFn;
|
|
17
|
+
jwks;
|
|
18
18
|
/**
|
|
19
19
|
* Creates a new BearerStrategy instance.
|
|
20
20
|
* @param options The validation options.
|
|
21
21
|
* @param verifyFn Called when a token is validated, needs to provide the User object.
|
|
22
22
|
*/
|
|
23
23
|
constructor(options, verifyFn) {
|
|
24
|
-
var _a, _b, _c;
|
|
25
24
|
super();
|
|
26
|
-
this.name = 'oauth-bearer';
|
|
27
25
|
this.verifyFn = verifyFn;
|
|
28
|
-
let audience =
|
|
26
|
+
let audience = options.audience ?? options.clientID;
|
|
29
27
|
if (typeof audience === 'string') {
|
|
30
28
|
audience = [audience, `spn:${audience}`];
|
|
31
29
|
}
|
|
32
30
|
this.audience = audience;
|
|
33
31
|
this.clientID = options.clientID;
|
|
34
|
-
this.clockSkew =
|
|
32
|
+
this.clockSkew = options.clockSkew ?? 300;
|
|
35
33
|
this.identityMetadata = options.identityMetadata;
|
|
36
34
|
this.issuer = options.issuer;
|
|
37
|
-
this.scope =
|
|
35
|
+
this.scope = options.scope ?? [];
|
|
38
36
|
}
|
|
39
37
|
/**
|
|
40
38
|
* Validates the Bearer token in a request.
|
|
41
39
|
* When validated, calls VerifyFn to retrieve the User belonging to the token.
|
|
42
40
|
* @param req The Express request
|
|
43
41
|
*/
|
|
44
|
-
authenticate(req) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (!
|
|
49
|
-
|
|
50
|
-
if (!res.ok) {
|
|
51
|
-
throw new Error(`Error ${String(res.status)} when querying identity metadata`);
|
|
52
|
-
}
|
|
53
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
54
|
-
const { jwks_uri } = yield res.json();
|
|
55
|
-
this.jwks = (0, jose_1.createRemoteJWKSet)(new URL(jwks_uri));
|
|
42
|
+
async authenticate(req) {
|
|
43
|
+
try {
|
|
44
|
+
if (!this.jwks) {
|
|
45
|
+
const res = await fetch(this.identityMetadata);
|
|
46
|
+
if (!res.ok) {
|
|
47
|
+
throw new Error(`Error ${String(res.status)} when querying identity metadata`);
|
|
56
48
|
}
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
if (!jwt) {
|
|
61
|
-
throw new Error('Unable to extract Bearer token from Authorization header');
|
|
62
|
-
}
|
|
63
|
-
const { payload } = yield (0, jose_1.jwtVerify)(jwt, this.jwks, {
|
|
64
|
-
audience: this.audience, // aud
|
|
65
|
-
clockTolerance: this.clockSkew, // exp, nbf
|
|
66
|
-
issuer: this.issuer, // iss
|
|
67
|
-
});
|
|
68
|
-
// console.log(JSON.stringify(payload, null, 2));
|
|
69
|
-
const scp = String((_a = payload.scp) !== null && _a !== void 0 ? _a : ''); // scp
|
|
70
|
-
if (this.scope.length > 0
|
|
71
|
-
&& !this.scope.some((scope) => scp.includes(scope))) {
|
|
72
|
-
throw new Error('Scope does not match scp claim');
|
|
73
|
-
}
|
|
74
|
-
this.verifyFn(payload, (err, user, info) => {
|
|
75
|
-
if (err) {
|
|
76
|
-
throw err;
|
|
77
|
-
}
|
|
78
|
-
if (!user) {
|
|
79
|
-
throw new Error('No user found');
|
|
80
|
-
}
|
|
81
|
-
this.success(user, info);
|
|
82
|
-
});
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
50
|
+
const { jwks_uri } = await res.json();
|
|
51
|
+
this.jwks = (0, jose_1.createRemoteJWKSet)(new URL(jwks_uri));
|
|
83
52
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
53
|
+
const header = req.get('authorization');
|
|
54
|
+
const headerParts = header?.split(' ');
|
|
55
|
+
const jwt = headerParts?.length === 2 && headerParts[0]?.toLowerCase() === 'bearer' && headerParts[1];
|
|
56
|
+
if (!jwt) {
|
|
57
|
+
throw new Error('Unable to extract Bearer token from Authorization header');
|
|
87
58
|
}
|
|
88
|
-
|
|
59
|
+
const { payload } = await (0, jose_1.jwtVerify)(jwt, this.jwks, {
|
|
60
|
+
audience: this.audience, // aud
|
|
61
|
+
clockTolerance: this.clockSkew, // exp, nbf
|
|
62
|
+
issuer: this.issuer, // iss
|
|
63
|
+
});
|
|
64
|
+
// console.log(JSON.stringify(payload, null, 2));
|
|
65
|
+
const scp = String(payload['scp'] ?? ''); // scp
|
|
66
|
+
if (this.scope.length > 0
|
|
67
|
+
&& !this.scope.some((scope) => scp.includes(scope))) {
|
|
68
|
+
throw new Error('Scope does not match scp claim');
|
|
69
|
+
}
|
|
70
|
+
this.verifyFn(payload, (err, user, info) => {
|
|
71
|
+
if (err) {
|
|
72
|
+
throw err;
|
|
73
|
+
}
|
|
74
|
+
if (!user) {
|
|
75
|
+
throw new Error('No user found');
|
|
76
|
+
}
|
|
77
|
+
this.success(user, info);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
const message = JSON.stringify(error);
|
|
82
|
+
this.fail(message);
|
|
83
|
+
}
|
|
89
84
|
}
|
|
90
85
|
}
|
|
91
86
|
exports.default = BearerStrategy;
|
package/package.json
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "passport-entra",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "dist/passport-entra.js",
|
|
6
|
-
"types": "dist/passport-entra.d.ts",
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "passport-entra",
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"description": "Microsoft Entra authentication strategy for passport",
|
|
5
|
+
"main": "dist/passport-entra.js",
|
|
6
|
+
"types": "dist/passport-entra.d.ts",
|
|
7
|
+
"homepage": "https://bitbucket.org/janbakker/passport-entra",
|
|
8
|
+
"repository": "git+https://bitbucket.org/janbakker/passport-entra.git",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"author": "Jan Bakker",
|
|
14
|
+
"license": "ISC",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"jose": "^6.0.11",
|
|
17
|
+
"passport": "^0.7.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@tsconfig/node20": "^20.1.6",
|
|
21
|
+
"@tsconfig/strictest": "^2.0.5",
|
|
22
|
+
"@types/passport": "^1.0.17",
|
|
23
|
+
"typescript": "^5.8.3"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/passport-entra.ts
CHANGED
|
@@ -1,130 +1,120 @@
|
|
|
1
|
-
import { Strategy } from 'passport';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
*
|
|
34
|
-
*/
|
|
35
|
-
export
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
this.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
throw new Error('No user found');
|
|
122
|
-
}
|
|
123
|
-
this.success(user, info);
|
|
124
|
-
});
|
|
125
|
-
} catch (error) {
|
|
126
|
-
const message = JSON.stringify(error);
|
|
127
|
-
this.fail(message);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
1
|
+
import { Strategy } from 'passport';
|
|
2
|
+
import type { AuthenticateCallback } from 'passport';
|
|
3
|
+
|
|
4
|
+
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
|
5
|
+
|
|
6
|
+
import type { Request } from 'express';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Bearerstrategy options
|
|
10
|
+
*/
|
|
11
|
+
export interface BearerOptions {
|
|
12
|
+
/** If provided, check aud claim against this audience, otherwise use clientID. */
|
|
13
|
+
audience?: string | string[];
|
|
14
|
+
/** ClientID of app in Entra, check aud claim against clientID if audience is not provided. */
|
|
15
|
+
clientID: string;
|
|
16
|
+
/** Clock skew allowed when checking nbf and exp claims. */
|
|
17
|
+
clockSkew?: string | number;
|
|
18
|
+
/** Well known oid-configuration endpoint, i.e. _issuer_/.well-known/openid-configuration. */
|
|
19
|
+
identityMetadata: string;
|
|
20
|
+
/** Check iss claim, i.e. https://login.microsoftonline.com/_tenantID_/v2.0 - tenantID of app in Entra */
|
|
21
|
+
issuer: string | string[];
|
|
22
|
+
/** Check scp claim. */
|
|
23
|
+
scope?: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Called by this BearerStrategy when the token has been validated.
|
|
28
|
+
* Use the token (the bearer JWT payload) to find the user. Then call done.
|
|
29
|
+
*/
|
|
30
|
+
export type VerifyCallback = (token: object, done: AuthenticateCallback) => void;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Bearerstrategy for password.js, used with Microsoft Entra as identity provider.
|
|
34
|
+
*/
|
|
35
|
+
export default class BearerStrategy extends Strategy {
|
|
36
|
+
audience;
|
|
37
|
+
clientID;
|
|
38
|
+
clockSkew;
|
|
39
|
+
identityMetadata;
|
|
40
|
+
issuer;
|
|
41
|
+
override name = 'oauth-bearer';
|
|
42
|
+
scope;
|
|
43
|
+
verifyFn;
|
|
44
|
+
jwks: ReturnType<typeof createRemoteJWKSet> | undefined;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Creates a new BearerStrategy instance.
|
|
48
|
+
* @param options The validation options.
|
|
49
|
+
* @param verifyFn Called when a token is validated, needs to provide the User object.
|
|
50
|
+
*/
|
|
51
|
+
constructor(options: BearerOptions, verifyFn: VerifyCallback) {
|
|
52
|
+
super();
|
|
53
|
+
|
|
54
|
+
this.verifyFn = verifyFn;
|
|
55
|
+
|
|
56
|
+
let audience = options.audience ?? options.clientID;
|
|
57
|
+
if (typeof audience === 'string') {
|
|
58
|
+
audience = [audience, `spn:${audience}`];
|
|
59
|
+
}
|
|
60
|
+
this.audience = audience;
|
|
61
|
+
|
|
62
|
+
this.clientID = options.clientID;
|
|
63
|
+
this.clockSkew = options.clockSkew ?? 300;
|
|
64
|
+
this.identityMetadata = options.identityMetadata;
|
|
65
|
+
this.issuer = options.issuer;
|
|
66
|
+
this.scope = options.scope ?? [];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Validates the Bearer token in a request.
|
|
71
|
+
* When validated, calls VerifyFn to retrieve the User belonging to the token.
|
|
72
|
+
* @param req The Express request
|
|
73
|
+
*/
|
|
74
|
+
override async authenticate(req: Request) {
|
|
75
|
+
try {
|
|
76
|
+
if (!this.jwks) {
|
|
77
|
+
const res = await fetch(this.identityMetadata);
|
|
78
|
+
if (!res.ok) {
|
|
79
|
+
throw new Error(`Error ${String(res.status)} when querying identity metadata`);
|
|
80
|
+
}
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
82
|
+
const { jwks_uri } = await res.json() as { jwks_uri: string };
|
|
83
|
+
this.jwks = createRemoteJWKSet(new URL(jwks_uri));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const header = req.get('authorization');
|
|
87
|
+
const headerParts = header?.split(' ');
|
|
88
|
+
const jwt = headerParts?.length === 2 && headerParts[0]?.toLowerCase() === 'bearer' && headerParts[1];
|
|
89
|
+
if (!jwt) {
|
|
90
|
+
throw new Error('Unable to extract Bearer token from Authorization header');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const { payload } = await jwtVerify(jwt, this.jwks, {
|
|
94
|
+
audience: this.audience, // aud
|
|
95
|
+
clockTolerance: this.clockSkew, // exp, nbf
|
|
96
|
+
issuer: this.issuer, // iss
|
|
97
|
+
});
|
|
98
|
+
// console.log(JSON.stringify(payload, null, 2));
|
|
99
|
+
|
|
100
|
+
const scp = String(payload['scp'] ?? ''); // scp
|
|
101
|
+
if (this.scope.length > 0
|
|
102
|
+
&& !this.scope.some((scope) => scp.includes(scope))) {
|
|
103
|
+
throw new Error('Scope does not match scp claim');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this.verifyFn(payload, (err: unknown, user: unknown, info?: string | object) => {
|
|
107
|
+
if (err) {
|
|
108
|
+
throw err as Error;
|
|
109
|
+
}
|
|
110
|
+
if (!user) {
|
|
111
|
+
throw new Error('No user found');
|
|
112
|
+
}
|
|
113
|
+
this.success(user, info as object);
|
|
114
|
+
});
|
|
115
|
+
} catch (error) {
|
|
116
|
+
const message = JSON.stringify(error);
|
|
117
|
+
this.fail(message);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -2,17 +2,14 @@
|
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
"declaration": true,
|
|
4
4
|
"outDir": "dist",
|
|
5
|
-
|
|
6
|
-
"target": "es2016",
|
|
7
|
-
"module": "commonjs",
|
|
8
|
-
"esModuleInterop": true,
|
|
9
5
|
"forceConsistentCasingInFileNames": true,
|
|
10
|
-
|
|
11
|
-
"strict": true,
|
|
12
|
-
"skipLibCheck": true
|
|
13
6
|
},
|
|
14
7
|
"exclude": [
|
|
15
8
|
"node_modules",
|
|
16
9
|
"dist"
|
|
10
|
+
],
|
|
11
|
+
"extends": [
|
|
12
|
+
"@tsconfig/strictest/tsconfig.json",
|
|
13
|
+
"@tsconfig/node20/tsconfig.json"
|
|
17
14
|
]
|
|
18
15
|
}
|