@fractary/faber-cli 1.3.2 → 1.3.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 +101 -4
- package/dist/commands/auth/index.d.ts +13 -0
- package/dist/commands/auth/index.d.ts.map +1 -0
- package/dist/commands/auth/index.js +336 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/lib/github-app-auth.d.ts +122 -0
- package/dist/lib/github-app-auth.d.ts.map +1 -0
- package/dist/lib/github-app-auth.js +294 -0
- package/dist/lib/github-app-setup.d.ts +142 -0
- package/dist/lib/github-app-setup.d.ts.map +1 -0
- package/dist/lib/github-app-setup.js +365 -0
- package/dist/lib/repo-client.d.ts +22 -2
- package/dist/lib/repo-client.d.ts.map +1 -1
- package/dist/lib/repo-client.js +52 -8
- package/dist/lib/sdk-config-adapter.d.ts +52 -0
- package/dist/lib/sdk-config-adapter.d.ts.map +1 -1
- package/dist/lib/sdk-config-adapter.js +167 -6
- package/dist/types/config.d.ts +12 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/utils/github-manifest.d.ts +39 -0
- package/dist/utils/github-manifest.d.ts.map +1 -0
- package/dist/utils/github-manifest.js +84 -0
- package/package.json +4 -2
|
@@ -2,7 +2,107 @@
|
|
|
2
2
|
* SDK Configuration Adapter
|
|
3
3
|
*
|
|
4
4
|
* Converts FABER CLI configuration to @fractary/core SDK configuration format
|
|
5
|
+
* Supports both PAT and GitHub App authentication methods.
|
|
5
6
|
*/
|
|
7
|
+
import { GitHubAppAuth, StaticTokenProvider, GitHubAppTokenProvider, } from './github-app-auth.js';
|
|
8
|
+
// Singleton token provider for reuse across SDK instances
|
|
9
|
+
let tokenProvider = null;
|
|
10
|
+
let tokenProviderConfig = null;
|
|
11
|
+
/**
|
|
12
|
+
* Get or create a token provider based on configuration
|
|
13
|
+
*
|
|
14
|
+
* @param faberConfig - FABER CLI configuration
|
|
15
|
+
* @returns TokenProvider instance
|
|
16
|
+
*/
|
|
17
|
+
function getTokenProvider(faberConfig) {
|
|
18
|
+
const appConfig = faberConfig.github?.app;
|
|
19
|
+
const patToken = faberConfig.github?.token;
|
|
20
|
+
// Determine which auth method to use
|
|
21
|
+
// GitHub App takes precedence over PAT if configured
|
|
22
|
+
if (appConfig?.id && appConfig?.installation_id) {
|
|
23
|
+
// Check if we can reuse existing provider
|
|
24
|
+
if (tokenProvider && tokenProviderConfig === appConfig) {
|
|
25
|
+
return tokenProvider;
|
|
26
|
+
}
|
|
27
|
+
// Create new GitHub App token provider
|
|
28
|
+
const auth = new GitHubAppAuth(appConfig);
|
|
29
|
+
tokenProvider = new GitHubAppTokenProvider(auth);
|
|
30
|
+
tokenProviderConfig = appConfig;
|
|
31
|
+
return tokenProvider;
|
|
32
|
+
}
|
|
33
|
+
// Fall back to PAT
|
|
34
|
+
if (patToken) {
|
|
35
|
+
// Check if we can reuse existing provider
|
|
36
|
+
if (tokenProvider && tokenProviderConfig === patToken) {
|
|
37
|
+
return tokenProvider;
|
|
38
|
+
}
|
|
39
|
+
tokenProvider = new StaticTokenProvider(patToken);
|
|
40
|
+
tokenProviderConfig = patToken;
|
|
41
|
+
return tokenProvider;
|
|
42
|
+
}
|
|
43
|
+
throw new Error('GitHub authentication not configured. Either:\n' +
|
|
44
|
+
' 1. Set GITHUB_TOKEN environment variable, or\n' +
|
|
45
|
+
' 2. Configure GitHub App in .fractary/settings.json:\n' +
|
|
46
|
+
' {\n' +
|
|
47
|
+
' "github": {\n' +
|
|
48
|
+
' "app": {\n' +
|
|
49
|
+
' "id": "<app-id>",\n' +
|
|
50
|
+
' "installation_id": "<installation-id>",\n' +
|
|
51
|
+
' "private_key_path": "~/.github/your-app.pem"\n' +
|
|
52
|
+
' }\n' +
|
|
53
|
+
' }\n' +
|
|
54
|
+
' }');
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Get the current token from the provider
|
|
58
|
+
* Used internally and for SDK configuration
|
|
59
|
+
*
|
|
60
|
+
* @param faberConfig - FABER CLI configuration
|
|
61
|
+
* @returns Promise resolving to the current token
|
|
62
|
+
*/
|
|
63
|
+
export async function getToken(faberConfig) {
|
|
64
|
+
const provider = getTokenProvider(faberConfig);
|
|
65
|
+
return provider.getToken();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get the token provider for dynamic token refresh
|
|
69
|
+
* Useful for long-running operations that need fresh tokens
|
|
70
|
+
*
|
|
71
|
+
* @param faberConfig - FABER CLI configuration
|
|
72
|
+
* @returns TokenProvider instance
|
|
73
|
+
*/
|
|
74
|
+
export function getTokenProviderInstance(faberConfig) {
|
|
75
|
+
return getTokenProvider(faberConfig);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Validate GitHub authentication configuration
|
|
79
|
+
*
|
|
80
|
+
* @param faberConfig - FABER CLI configuration
|
|
81
|
+
* @throws Error if configuration is invalid
|
|
82
|
+
*/
|
|
83
|
+
export async function validateGitHubAuth(faberConfig) {
|
|
84
|
+
const appConfig = faberConfig.github?.app;
|
|
85
|
+
if (appConfig?.id && appConfig?.installation_id) {
|
|
86
|
+
// Validate GitHub App configuration
|
|
87
|
+
const auth = new GitHubAppAuth(appConfig);
|
|
88
|
+
await auth.validate();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// Validate PAT
|
|
92
|
+
if (!faberConfig.github?.token) {
|
|
93
|
+
throw new Error('GitHub token not found. Set GITHUB_TOKEN environment variable or configure in .fractary/settings.json');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Check if GitHub App authentication is configured
|
|
98
|
+
*
|
|
99
|
+
* @param faberConfig - FABER CLI configuration
|
|
100
|
+
* @returns true if GitHub App is configured
|
|
101
|
+
*/
|
|
102
|
+
export function isGitHubAppConfigured(faberConfig) {
|
|
103
|
+
const appConfig = faberConfig.github?.app;
|
|
104
|
+
return !!(appConfig?.id && appConfig?.installation_id);
|
|
105
|
+
}
|
|
6
106
|
/**
|
|
7
107
|
* Create WorkConfig for WorkManager from FaberConfig
|
|
8
108
|
*
|
|
@@ -11,15 +111,49 @@
|
|
|
11
111
|
* @throws Error if required fields are missing
|
|
12
112
|
*/
|
|
13
113
|
export function createWorkConfig(faberConfig) {
|
|
14
|
-
const token = faberConfig.github?.token;
|
|
15
114
|
const owner = faberConfig.github?.organization;
|
|
16
115
|
const repo = faberConfig.github?.project;
|
|
17
|
-
if (!
|
|
18
|
-
throw new Error('GitHub
|
|
116
|
+
if (!owner || !repo) {
|
|
117
|
+
throw new Error('GitHub organization and project must be configured in .fractary/settings.json');
|
|
118
|
+
}
|
|
119
|
+
// Get token provider (validates auth config)
|
|
120
|
+
const provider = getTokenProvider(faberConfig);
|
|
121
|
+
// For SDK config, we need a synchronous token
|
|
122
|
+
// The SDK will use this initially; for long-running operations,
|
|
123
|
+
// use createWorkConfigAsync or the token provider directly
|
|
124
|
+
let token;
|
|
125
|
+
if (provider instanceof StaticTokenProvider) {
|
|
126
|
+
// For PAT, we can get token synchronously via internal access
|
|
127
|
+
token = faberConfig.github?.token || '';
|
|
19
128
|
}
|
|
129
|
+
else {
|
|
130
|
+
// For GitHub App, throw helpful error - use async version
|
|
131
|
+
throw new Error('GitHub App authentication requires async initialization. ' +
|
|
132
|
+
'Use createWorkConfigAsync() instead of createWorkConfig().');
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
platform: 'github',
|
|
136
|
+
owner,
|
|
137
|
+
repo,
|
|
138
|
+
token,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Create WorkConfig for WorkManager from FaberConfig (async version)
|
|
143
|
+
*
|
|
144
|
+
* This version supports both PAT and GitHub App authentication.
|
|
145
|
+
*
|
|
146
|
+
* @param faberConfig - FABER CLI configuration
|
|
147
|
+
* @returns Promise resolving to WorkConfig for @fractary/core WorkManager
|
|
148
|
+
* @throws Error if required fields are missing
|
|
149
|
+
*/
|
|
150
|
+
export async function createWorkConfigAsync(faberConfig) {
|
|
151
|
+
const owner = faberConfig.github?.organization;
|
|
152
|
+
const repo = faberConfig.github?.project;
|
|
20
153
|
if (!owner || !repo) {
|
|
21
154
|
throw new Error('GitHub organization and project must be configured in .fractary/settings.json');
|
|
22
155
|
}
|
|
156
|
+
const token = await getToken(faberConfig);
|
|
23
157
|
return {
|
|
24
158
|
platform: 'github',
|
|
25
159
|
owner,
|
|
@@ -35,12 +169,39 @@ export function createWorkConfig(faberConfig) {
|
|
|
35
169
|
* @throws Error if required fields are missing
|
|
36
170
|
*/
|
|
37
171
|
export function createRepoConfig(faberConfig) {
|
|
38
|
-
const token = faberConfig.github?.token;
|
|
39
172
|
const owner = faberConfig.github?.organization;
|
|
40
173
|
const repo = faberConfig.github?.project;
|
|
41
|
-
|
|
42
|
-
|
|
174
|
+
// Get token provider (validates auth config)
|
|
175
|
+
const provider = getTokenProvider(faberConfig);
|
|
176
|
+
// For SDK config, we need a synchronous token
|
|
177
|
+
let token;
|
|
178
|
+
if (provider instanceof StaticTokenProvider) {
|
|
179
|
+
token = faberConfig.github?.token || '';
|
|
43
180
|
}
|
|
181
|
+
else {
|
|
182
|
+
throw new Error('GitHub App authentication requires async initialization. ' +
|
|
183
|
+
'Use createRepoConfigAsync() instead of createRepoConfig().');
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
platform: 'github',
|
|
187
|
+
owner,
|
|
188
|
+
repo,
|
|
189
|
+
token,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Create RepoConfig for RepoManager from FaberConfig (async version)
|
|
194
|
+
*
|
|
195
|
+
* This version supports both PAT and GitHub App authentication.
|
|
196
|
+
*
|
|
197
|
+
* @param faberConfig - FABER CLI configuration
|
|
198
|
+
* @returns Promise resolving to RepoConfig for @fractary/core RepoManager
|
|
199
|
+
* @throws Error if required fields are missing
|
|
200
|
+
*/
|
|
201
|
+
export async function createRepoConfigAsync(faberConfig) {
|
|
202
|
+
const owner = faberConfig.github?.organization;
|
|
203
|
+
const repo = faberConfig.github?.project;
|
|
204
|
+
const token = await getToken(faberConfig);
|
|
44
205
|
return {
|
|
45
206
|
platform: 'github',
|
|
46
207
|
owner,
|
package/dist/types/config.d.ts
CHANGED
|
@@ -6,11 +6,23 @@
|
|
|
6
6
|
export interface AnthropicConfig {
|
|
7
7
|
api_key?: string;
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* GitHub App authentication configuration
|
|
11
|
+
*/
|
|
12
|
+
export interface GitHubAppConfig {
|
|
13
|
+
id: string;
|
|
14
|
+
installation_id: string;
|
|
15
|
+
private_key_path?: string;
|
|
16
|
+
private_key_env_var?: string;
|
|
17
|
+
created_via?: 'manifest-flow' | 'manual';
|
|
18
|
+
created_at?: string;
|
|
19
|
+
}
|
|
9
20
|
export interface GitHubConfig {
|
|
10
21
|
token?: string;
|
|
11
22
|
organization?: string;
|
|
12
23
|
project?: string;
|
|
13
24
|
repo?: string;
|
|
25
|
+
app?: GitHubAppConfig;
|
|
14
26
|
}
|
|
15
27
|
export interface WorktreeConfig {
|
|
16
28
|
location?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,eAAe,GAAG,QAAQ,CAAC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,eAAe,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE;QACT,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper utilities for GitHub App Manifest flow
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Parse code parameter from GitHub redirect URL or direct code input
|
|
6
|
+
*
|
|
7
|
+
* @param input - Either a full GitHub URL or just the code
|
|
8
|
+
* @returns The extracted code, or null if invalid
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseCodeFromUrl(input: string): string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Validate manifest code format
|
|
13
|
+
*
|
|
14
|
+
* GitHub manifest codes are typically long alphanumeric strings
|
|
15
|
+
* with underscores and hyphens, at least 20 characters long.
|
|
16
|
+
*
|
|
17
|
+
* @param code - The code to validate
|
|
18
|
+
* @returns true if code appears valid
|
|
19
|
+
*/
|
|
20
|
+
export declare function validateManifestCode(code: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Detect GitHub context from git remote
|
|
23
|
+
*
|
|
24
|
+
* Parses the git remote URL to extract organization and repository names.
|
|
25
|
+
* Supports both HTTPS and SSH URL formats.
|
|
26
|
+
*
|
|
27
|
+
* @returns Object with org and repo, or null if not detectable
|
|
28
|
+
*/
|
|
29
|
+
export declare function detectGitHubContext(): {
|
|
30
|
+
org: string;
|
|
31
|
+
repo: string;
|
|
32
|
+
} | null;
|
|
33
|
+
/**
|
|
34
|
+
* Validate if current directory is a git repository
|
|
35
|
+
*
|
|
36
|
+
* @returns true if current directory is a git repository
|
|
37
|
+
*/
|
|
38
|
+
export declare function isGitRepository(): boolean;
|
|
39
|
+
//# sourceMappingURL=github-manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-manifest.d.ts","sourceRoot":"","sources":["../../src/utils/github-manifest.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoB7D;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,IAAI;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAqB1E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAQzC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper utilities for GitHub App Manifest flow
|
|
3
|
+
*/
|
|
4
|
+
import { Git } from '@fractary/faber';
|
|
5
|
+
/**
|
|
6
|
+
* Parse code parameter from GitHub redirect URL or direct code input
|
|
7
|
+
*
|
|
8
|
+
* @param input - Either a full GitHub URL or just the code
|
|
9
|
+
* @returns The extracted code, or null if invalid
|
|
10
|
+
*/
|
|
11
|
+
export function parseCodeFromUrl(input) {
|
|
12
|
+
const trimmed = input.trim();
|
|
13
|
+
// Try to parse as URL first
|
|
14
|
+
try {
|
|
15
|
+
const url = new URL(trimmed);
|
|
16
|
+
const code = url.searchParams.get('code');
|
|
17
|
+
if (code) {
|
|
18
|
+
return code;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Not a valid URL, check if it's just the code itself
|
|
23
|
+
}
|
|
24
|
+
// Check if input looks like a code (alphanumeric, underscores, hyphens)
|
|
25
|
+
if (/^[a-zA-Z0-9_-]+$/.test(trimmed)) {
|
|
26
|
+
return trimmed;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate manifest code format
|
|
32
|
+
*
|
|
33
|
+
* GitHub manifest codes are typically long alphanumeric strings
|
|
34
|
+
* with underscores and hyphens, at least 20 characters long.
|
|
35
|
+
*
|
|
36
|
+
* @param code - The code to validate
|
|
37
|
+
* @returns true if code appears valid
|
|
38
|
+
*/
|
|
39
|
+
export function validateManifestCode(code) {
|
|
40
|
+
return /^[a-zA-Z0-9_-]{20,}$/.test(code);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Detect GitHub context from git remote
|
|
44
|
+
*
|
|
45
|
+
* Parses the git remote URL to extract organization and repository names.
|
|
46
|
+
* Supports both HTTPS and SSH URL formats.
|
|
47
|
+
*
|
|
48
|
+
* @returns Object with org and repo, or null if not detectable
|
|
49
|
+
*/
|
|
50
|
+
export function detectGitHubContext() {
|
|
51
|
+
try {
|
|
52
|
+
const git = new Git();
|
|
53
|
+
const remoteUrl = git.exec('remote get-url origin').trim();
|
|
54
|
+
// Parse various GitHub URL formats
|
|
55
|
+
// HTTPS: https://github.com/org/repo.git
|
|
56
|
+
// SSH: git@github.com:org/repo.git
|
|
57
|
+
const match = remoteUrl.match(/github\.com[/:]([^/]+)\/([^/]+?)(\.git)?$/);
|
|
58
|
+
if (match) {
|
|
59
|
+
const org = match[1];
|
|
60
|
+
const repo = match[2].replace(/\.git$/, '');
|
|
61
|
+
return { org, repo };
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Not a git repository or no origin remote
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Validate if current directory is a git repository
|
|
72
|
+
*
|
|
73
|
+
* @returns true if current directory is a git repository
|
|
74
|
+
*/
|
|
75
|
+
export function isGitRepository() {
|
|
76
|
+
try {
|
|
77
|
+
const git = new Git();
|
|
78
|
+
git.exec('rev-parse --git-dir');
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fractary/faber-cli",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.4",
|
|
4
4
|
"description": "FABER CLI - Command-line interface for FABER development toolkit",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -40,10 +40,12 @@
|
|
|
40
40
|
"@fractary/faber": "^2.1.1",
|
|
41
41
|
"ajv": "^8.12.0",
|
|
42
42
|
"chalk": "^5.0.0",
|
|
43
|
-
"commander": "^12.0.0"
|
|
43
|
+
"commander": "^12.0.0",
|
|
44
|
+
"jsonwebtoken": "^9.0.0"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@types/jest": "^30.0.0",
|
|
48
|
+
"@types/jsonwebtoken": "^9.0.0",
|
|
47
49
|
"@types/node": "^20.19.26",
|
|
48
50
|
"@typescript-eslint/eslint-plugin": "^6.19.0",
|
|
49
51
|
"@typescript-eslint/parser": "^6.19.0",
|