@flink-app/oauth-plugin 0.12.1-alpha.33
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 +21 -0
- package/README.md +783 -0
- package/SECURITY.md +433 -0
- package/dist/OAuthInternalContext.d.ts +45 -0
- package/dist/OAuthInternalContext.js +2 -0
- package/dist/OAuthPlugin.d.ts +70 -0
- package/dist/OAuthPlugin.js +220 -0
- package/dist/OAuthPluginContext.d.ts +49 -0
- package/dist/OAuthPluginContext.js +2 -0
- package/dist/OAuthPluginOptions.d.ts +111 -0
- package/dist/OAuthPluginOptions.js +2 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +66 -0
- package/dist/providers/GitHubProvider.d.ts +32 -0
- package/dist/providers/GitHubProvider.js +82 -0
- package/dist/providers/GoogleProvider.d.ts +32 -0
- package/dist/providers/GoogleProvider.js +83 -0
- package/dist/providers/OAuthProvider.d.ts +69 -0
- package/dist/providers/OAuthProvider.js +2 -0
- package/dist/providers/OAuthProviderBase.d.ts +32 -0
- package/dist/providers/OAuthProviderBase.js +86 -0
- package/dist/providers/ProviderRegistry.d.ts +14 -0
- package/dist/providers/ProviderRegistry.js +24 -0
- package/dist/repos/OAuthConnectionRepo.d.ts +30 -0
- package/dist/repos/OAuthConnectionRepo.js +38 -0
- package/dist/repos/OAuthSessionRepo.d.ts +22 -0
- package/dist/repos/OAuthSessionRepo.js +28 -0
- package/dist/schemas/OAuthConnection.d.ts +12 -0
- package/dist/schemas/OAuthConnection.js +2 -0
- package/dist/schemas/OAuthSession.d.ts +9 -0
- package/dist/schemas/OAuthSession.js +2 -0
- package/dist/utils/encryption-utils.d.ts +34 -0
- package/dist/utils/encryption-utils.js +134 -0
- package/dist/utils/error-utils.d.ts +68 -0
- package/dist/utils/error-utils.js +120 -0
- package/dist/utils/state-utils.d.ts +36 -0
- package/dist/utils/state-utils.js +72 -0
- package/examples/api-client-auth.ts +550 -0
- package/examples/basic-auth.ts +288 -0
- package/examples/multi-provider.ts +409 -0
- package/examples/token-storage.ts +490 -0
- package/package.json +38 -0
- package/spec/OAuthHandlers.spec.ts +146 -0
- package/spec/OAuthPluginSpec.ts +31 -0
- package/spec/ProvidersSpec.ts +178 -0
- package/spec/README.md +365 -0
- package/spec/helpers/mockJwtAuthPlugin.ts +104 -0
- package/spec/helpers/mockOAuthProviders.ts +189 -0
- package/spec/helpers/reporter.ts +41 -0
- package/spec/helpers/testDatabase.ts +107 -0
- package/spec/helpers/testHelpers.ts +192 -0
- package/spec/integration-critical.spec.ts +857 -0
- package/spec/integration.spec.ts +301 -0
- package/spec/repositories.spec.ts +181 -0
- package/spec/support/jasmine.json +7 -0
- package/spec/utils/security.spec.ts +243 -0
- package/src/OAuthInternalContext.ts +46 -0
- package/src/OAuthPlugin.ts +251 -0
- package/src/OAuthPluginContext.ts +53 -0
- package/src/OAuthPluginOptions.ts +122 -0
- package/src/handlers/CallbackOAuth.ts +238 -0
- package/src/handlers/InitiateOAuth.ts +99 -0
- package/src/index.ts +62 -0
- package/src/providers/GitHubProvider.ts +90 -0
- package/src/providers/GoogleProvider.ts +91 -0
- package/src/providers/OAuthProvider.ts +77 -0
- package/src/providers/OAuthProviderBase.ts +98 -0
- package/src/providers/ProviderRegistry.ts +27 -0
- package/src/repos/OAuthConnectionRepo.ts +41 -0
- package/src/repos/OAuthSessionRepo.ts +30 -0
- package/src/repos/TTL_INDEX_NOTE.md +28 -0
- package/src/schemas/CallbackRequest.ts +64 -0
- package/src/schemas/InitiateRequest.ts +10 -0
- package/src/schemas/OAuthConnection.ts +12 -0
- package/src/schemas/OAuthSession.ts +9 -0
- package/src/utils/encryption-utils.ts +148 -0
- package/src/utils/error-utils.ts +139 -0
- package/src/utils/state-utils.ts +70 -0
- package/src/utils/token-response-utils.ts +49 -0
- package/src/utils/validation-utils.ts +120 -0
- package/tsconfig.dist.json +4 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Error Interface and Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides standardized error handling for OAuth flows including
|
|
5
|
+
* user-friendly messages and error code mapping.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Standardized OAuth error structure
|
|
10
|
+
*/
|
|
11
|
+
export interface OAuthError {
|
|
12
|
+
/** Error code for programmatic handling */
|
|
13
|
+
code: string;
|
|
14
|
+
|
|
15
|
+
/** User-friendly error message */
|
|
16
|
+
message: string;
|
|
17
|
+
|
|
18
|
+
/** Additional error details (for logging, not user display) */
|
|
19
|
+
details?: any;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* OAuth error codes
|
|
24
|
+
*/
|
|
25
|
+
export const OAuthErrorCodes = {
|
|
26
|
+
INVALID_STATE: "invalid_state",
|
|
27
|
+
ACCESS_DENIED: "access_denied",
|
|
28
|
+
INVALID_GRANT: "invalid_grant",
|
|
29
|
+
NETWORK_ERROR: "network_error",
|
|
30
|
+
UNKNOWN_PROVIDER: "unknown_provider",
|
|
31
|
+
JWT_GENERATION_FAILED: "jwt_generation_failed",
|
|
32
|
+
INVALID_REQUEST: "invalid_request",
|
|
33
|
+
SERVER_ERROR: "server_error",
|
|
34
|
+
INVALID_PROVIDER: "invalid_provider",
|
|
35
|
+
SESSION_EXPIRED: "session_expired",
|
|
36
|
+
MISSING_CODE: "missing_code",
|
|
37
|
+
} as const;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create a standardized OAuth error
|
|
41
|
+
*
|
|
42
|
+
* @param code - Error code from OAuthErrorCodes
|
|
43
|
+
* @param message - User-friendly error message
|
|
44
|
+
* @param details - Additional error details for logging
|
|
45
|
+
* @returns Standardized OAuthError object
|
|
46
|
+
*/
|
|
47
|
+
export function createOAuthError(code: string, message: string, details?: any): OAuthError {
|
|
48
|
+
return {
|
|
49
|
+
code,
|
|
50
|
+
message,
|
|
51
|
+
details,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Map provider-specific errors to standardized OAuth errors
|
|
57
|
+
*
|
|
58
|
+
* This function converts various error types from OAuth providers
|
|
59
|
+
* into user-friendly standardized errors while sanitizing sensitive data.
|
|
60
|
+
*
|
|
61
|
+
* @param error - Error from OAuth provider or internal error
|
|
62
|
+
* @returns Standardized OAuthError
|
|
63
|
+
*/
|
|
64
|
+
export function handleProviderError(error: any): OAuthError {
|
|
65
|
+
// Handle OAuth provider error responses
|
|
66
|
+
if (error.error) {
|
|
67
|
+
const errorCode = error.error;
|
|
68
|
+
|
|
69
|
+
switch (errorCode) {
|
|
70
|
+
case "access_denied":
|
|
71
|
+
return createOAuthError(OAuthErrorCodes.ACCESS_DENIED, "You denied access to your account. Please try again if you want to continue.", {
|
|
72
|
+
providerError: errorCode,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
case "invalid_grant":
|
|
76
|
+
case "invalid_code":
|
|
77
|
+
return createOAuthError(OAuthErrorCodes.INVALID_GRANT, "The authorization code has expired or is invalid. Please try logging in again.", {
|
|
78
|
+
providerError: errorCode,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
case "invalid_request":
|
|
82
|
+
return createOAuthError(OAuthErrorCodes.INVALID_REQUEST, "There was a problem with the authentication request. Please try again.", {
|
|
83
|
+
providerError: errorCode,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
default:
|
|
87
|
+
return createOAuthError(OAuthErrorCodes.SERVER_ERROR, "An error occurred during authentication. Please try again.", {
|
|
88
|
+
providerError: errorCode,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Handle network errors
|
|
94
|
+
if (error.code === "ENOTFOUND" || error.code === "ECONNREFUSED" || error.code === "ETIMEDOUT") {
|
|
95
|
+
return createOAuthError(OAuthErrorCodes.NETWORK_ERROR, "Unable to connect to the authentication service. Please check your connection and try again.", {
|
|
96
|
+
networkError: error.code,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Handle JWT generation errors
|
|
101
|
+
if (error.message && error.message.includes("JWT")) {
|
|
102
|
+
return createOAuthError(OAuthErrorCodes.JWT_GENERATION_FAILED, "Failed to generate authentication token. Please try again.", {
|
|
103
|
+
originalError: error.message,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Generic error fallback
|
|
108
|
+
return createOAuthError(OAuthErrorCodes.SERVER_ERROR, "An unexpected error occurred. Please try again.", {
|
|
109
|
+
originalError: error.message || "Unknown error",
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Validate provider name
|
|
115
|
+
*
|
|
116
|
+
* @param provider - Provider name to validate
|
|
117
|
+
* @returns true if valid provider
|
|
118
|
+
* @throws OAuthError if invalid
|
|
119
|
+
*/
|
|
120
|
+
export function validateProvider(provider: string): provider is "github" | "google" {
|
|
121
|
+
if (provider !== "github" && provider !== "google") {
|
|
122
|
+
throw createOAuthError(OAuthErrorCodes.INVALID_PROVIDER, `Provider "${provider}" is not supported. Supported providers: github, google`, { provider });
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Validate response_type parameter
|
|
129
|
+
*
|
|
130
|
+
* @param responseType - Response type to validate
|
|
131
|
+
* @returns true if valid
|
|
132
|
+
* @throws OAuthError if invalid
|
|
133
|
+
*/
|
|
134
|
+
export function validateResponseType(responseType?: string): boolean {
|
|
135
|
+
if (responseType && responseType !== "json") {
|
|
136
|
+
throw createOAuthError(OAuthErrorCodes.INVALID_REQUEST, `Invalid response_type "${responseType}". Supported values: json`, { responseType });
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State Parameter Utilities for CSRF Protection
|
|
3
|
+
*
|
|
4
|
+
* Provides cryptographically secure state parameter generation
|
|
5
|
+
* and constant-time comparison for OAuth CSRF protection.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import crypto from "crypto";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Generate a cryptographically secure state parameter
|
|
12
|
+
*
|
|
13
|
+
* Creates a 32-byte random state parameter for CSRF protection
|
|
14
|
+
* in OAuth flows. This state is stored in the session and must
|
|
15
|
+
* match when the OAuth provider redirects back.
|
|
16
|
+
*
|
|
17
|
+
* @returns Hex-encoded 32-byte random string (64 characters)
|
|
18
|
+
*/
|
|
19
|
+
export function generateState(): string {
|
|
20
|
+
return crypto.randomBytes(32).toString("hex");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Validate state parameter using constant-time comparison
|
|
25
|
+
*
|
|
26
|
+
* Compares the provided state with the stored state using a
|
|
27
|
+
* constant-time algorithm to prevent timing attacks.
|
|
28
|
+
*
|
|
29
|
+
* @param provided - State parameter from OAuth provider callback
|
|
30
|
+
* @param stored - State parameter stored in session
|
|
31
|
+
* @returns true if states match, false otherwise
|
|
32
|
+
*/
|
|
33
|
+
export function validateState(provided: string, stored: string): boolean {
|
|
34
|
+
if (!provided || !stored) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Ensure both strings are the same length to prevent timing attacks
|
|
39
|
+
if (provided.length !== stored.length) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
// Use Node.js crypto.timingSafeEqual for constant-time comparison
|
|
45
|
+
const providedBuffer = Buffer.from(provided, "utf8");
|
|
46
|
+
const storedBuffer = Buffer.from(stored, "utf8");
|
|
47
|
+
|
|
48
|
+
// Both buffers must be same length for timingSafeEqual
|
|
49
|
+
if (providedBuffer.length !== storedBuffer.length) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return crypto.timingSafeEqual(providedBuffer, storedBuffer);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
// If comparison fails for any reason, return false
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Generate a session ID for OAuth flow tracking
|
|
62
|
+
*
|
|
63
|
+
* Creates a unique session identifier for correlating OAuth
|
|
64
|
+
* initiation with callback.
|
|
65
|
+
*
|
|
66
|
+
* @returns Hex-encoded random string
|
|
67
|
+
*/
|
|
68
|
+
export function generateSessionId(): string {
|
|
69
|
+
return crypto.randomBytes(16).toString("hex");
|
|
70
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats the OAuth callback response with JWT token.
|
|
3
|
+
* Supports multiple response formats:
|
|
4
|
+
* - JSON response with user and token
|
|
5
|
+
* - URL fragment redirect with token
|
|
6
|
+
* - Query parameter redirect with token
|
|
7
|
+
*
|
|
8
|
+
* @param token - JWT token to return
|
|
9
|
+
* @param user - User object to return
|
|
10
|
+
* @param redirectUrl - Optional redirect URL
|
|
11
|
+
* @param responseType - Response format ('json' or undefined for redirect)
|
|
12
|
+
* @returns Response object for Flink handler
|
|
13
|
+
*/
|
|
14
|
+
export function formatTokenResponse(token: string, user: any, redirectUrl?: string, responseType?: string): any {
|
|
15
|
+
// JSON response format
|
|
16
|
+
if (responseType === "json") {
|
|
17
|
+
return {
|
|
18
|
+
status: 200,
|
|
19
|
+
data: {
|
|
20
|
+
user,
|
|
21
|
+
token,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Redirect format
|
|
27
|
+
if (redirectUrl) {
|
|
28
|
+
// Use URL fragment for better security (token not sent to server)
|
|
29
|
+
const separator = redirectUrl.includes("#") ? "&" : "#";
|
|
30
|
+
const fullRedirectUrl = `${redirectUrl}${separator}token=${encodeURIComponent(token)}`;
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
status: 302,
|
|
34
|
+
headers: {
|
|
35
|
+
Location: fullRedirectUrl,
|
|
36
|
+
},
|
|
37
|
+
data: {},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Default: return JSON if no redirect URL provided
|
|
42
|
+
return {
|
|
43
|
+
status: 200,
|
|
44
|
+
data: {
|
|
45
|
+
user,
|
|
46
|
+
token,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Validation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides validation functions for OAuth handler inputs including
|
|
5
|
+
* provider names, query parameters, and redirect URIs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createOAuthError, OAuthErrorCodes } from "./error-utils";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Validate provider parameter
|
|
12
|
+
*
|
|
13
|
+
* @param provider - Provider name from URL parameter
|
|
14
|
+
* @returns Validated provider name
|
|
15
|
+
* @throws OAuthError if invalid
|
|
16
|
+
*/
|
|
17
|
+
export function validateProvider(provider: string): "github" | "google" {
|
|
18
|
+
if (!provider) {
|
|
19
|
+
throw createOAuthError(OAuthErrorCodes.INVALID_PROVIDER, "Provider is required", { provider });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (provider !== "github" && provider !== "google") {
|
|
23
|
+
throw createOAuthError(OAuthErrorCodes.INVALID_PROVIDER, 'Provider "' + provider + '" is not supported. Supported providers: github, google', {
|
|
24
|
+
provider,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return provider;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Validate response_type parameter
|
|
33
|
+
*
|
|
34
|
+
* @param responseType - Response type from query parameter
|
|
35
|
+
* @returns Validated response type or undefined
|
|
36
|
+
* @throws OAuthError if invalid
|
|
37
|
+
*/
|
|
38
|
+
export function validateResponseType(responseType?: string): string | undefined {
|
|
39
|
+
if (!responseType) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (responseType !== "json") {
|
|
44
|
+
throw createOAuthError(OAuthErrorCodes.INVALID_REQUEST, 'Invalid response_type "' + responseType + '". Supported values: json', { responseType });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return responseType;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Validate redirect URI format
|
|
52
|
+
*
|
|
53
|
+
* @param redirectUri - Redirect URI to validate
|
|
54
|
+
* @returns true if valid or undefined
|
|
55
|
+
* @throws OAuthError if invalid
|
|
56
|
+
*/
|
|
57
|
+
export function validateRedirectUri(redirectUri?: string): boolean {
|
|
58
|
+
if (!redirectUri) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const url = new URL(redirectUri);
|
|
64
|
+
|
|
65
|
+
// Check protocol
|
|
66
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
67
|
+
throw createOAuthError(OAuthErrorCodes.INVALID_REQUEST, "Redirect URI must use HTTP or HTTPS protocol", { redirectUri });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return true;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (error && typeof error === "object" && "code" in error) {
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw createOAuthError(OAuthErrorCodes.INVALID_REQUEST, "Invalid redirect URI format", { redirectUri });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Validate state parameter exists
|
|
82
|
+
*
|
|
83
|
+
* @param state - State parameter from query
|
|
84
|
+
* @throws OAuthError if missing
|
|
85
|
+
*/
|
|
86
|
+
export function validateStateParam(state?: string): void {
|
|
87
|
+
if (!state) {
|
|
88
|
+
throw createOAuthError(OAuthErrorCodes.INVALID_STATE, "State parameter is required", { state });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Validate authorization code exists
|
|
94
|
+
*
|
|
95
|
+
* @param code - Authorization code from query
|
|
96
|
+
* @throws OAuthError if missing
|
|
97
|
+
*/
|
|
98
|
+
export function validateAuthCode(code?: string): void {
|
|
99
|
+
if (!code) {
|
|
100
|
+
throw createOAuthError(OAuthErrorCodes.MISSING_CODE, "Authorization code is required", { code });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Check for OAuth error in callback query parameters
|
|
106
|
+
*
|
|
107
|
+
* @param errorParam - Error parameter from OAuth provider
|
|
108
|
+
* @param errorDescription - Error description from OAuth provider
|
|
109
|
+
* @throws OAuthError if error present
|
|
110
|
+
*/
|
|
111
|
+
export function checkOAuthErrorParam(errorParam?: string, errorDescription?: string): void {
|
|
112
|
+
if (errorParam) {
|
|
113
|
+
const message = errorDescription || "Authentication failed";
|
|
114
|
+
|
|
115
|
+
throw createOAuthError(errorParam === "access_denied" ? OAuthErrorCodes.ACCESS_DENIED : OAuthErrorCodes.SERVER_ERROR, message, {
|
|
116
|
+
error: errorParam,
|
|
117
|
+
description: errorDescription,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"lib": ["ES2020"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"allowSyntheticDefaultImports": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"module": "commonjs",
|
|
12
|
+
"moduleResolution": "node",
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"isolatedModules": true,
|
|
15
|
+
"noEmit": false,
|
|
16
|
+
"declaration": true,
|
|
17
|
+
"experimentalDecorators": true,
|
|
18
|
+
"checkJs": true,
|
|
19
|
+
"outDir": "dist",
|
|
20
|
+
"typeRoots": ["./node_modules/@types"]
|
|
21
|
+
},
|
|
22
|
+
"include": ["./src/*", "./spec/*"],
|
|
23
|
+
"exclude": ["./node_modules/*"]
|
|
24
|
+
}
|