@digitaldefiance/suite-core-lib 1.1.11 → 1.1.12
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 +83 -90
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -4,37 +4,37 @@
|
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](http://www.typescriptlang.org/)
|
|
6
6
|
|
|
7
|
-
A library
|
|
7
|
+
A comprehensive library providing higher-level primitives and foundational building blocks for creating a cryptographically-secure user management system and Node.js Express server framework. Built on top of **@digitaldefiance/ecies-lib** and **@digitaldefiance/node-ecies-lib**, this package serves as the core foundation for the **node-ecies** and **node-express-suite** projects, which together form a complete full-stack security and user management ecosystem.
|
|
8
8
|
|
|
9
9
|
## 🚀 Framework Ecosystem
|
|
10
10
|
|
|
11
|
-
`@digitaldefiance/
|
|
11
|
+
`@digitaldefiance/suite-core-lib` offers the essential **core primitives and abstractions** that power:
|
|
12
12
|
|
|
13
|
-
- user accounts and authentication
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
13
|
+
- Secure user accounts and authentication mechanisms
|
|
14
|
+
- End-to-end encryption between users and servers
|
|
15
|
+
- Zero-knowledge proof based authentication flows
|
|
16
|
+
- Role-based access control (RBAC) with fine-grained permissions
|
|
17
|
+
- Multi-language internationalization (i18n) with plugin-based architecture
|
|
18
18
|
|
|
19
19
|
## 📦 Installation
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
npm install @digitaldefiance/
|
|
22
|
+
npm install @digitaldefiance/suite-core-lib
|
|
23
23
|
# or
|
|
24
|
-
yarn add @digitaldefiance/
|
|
24
|
+
yarn add @digitaldefiance/suite-core-lib
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
## 🌍 Internationalization
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
This library includes built-in support for **five major languages** using a modern plugin-based i18n architecture:
|
|
30
30
|
|
|
31
|
-
- **English** (US
|
|
31
|
+
- **English** (US and UK variants)
|
|
32
32
|
- **French**
|
|
33
33
|
- **Spanish**
|
|
34
34
|
- **Mandarin Chinese**
|
|
35
35
|
- **Ukrainian**
|
|
36
36
|
|
|
37
|
-
All error messages, validation
|
|
37
|
+
All error messages, validation texts, and user-facing strings are fully localized and designed to be extensible for additional languages.
|
|
38
38
|
|
|
39
39
|
## 🏗️ Architecture Overview
|
|
40
40
|
|
|
@@ -47,16 +47,16 @@ import {
|
|
|
47
47
|
AccountStatus,
|
|
48
48
|
Role,
|
|
49
49
|
CoreLanguage
|
|
50
|
-
} from '@digitaldefiance/
|
|
50
|
+
} from '@digitaldefiance/suite-core-lib';
|
|
51
51
|
|
|
52
|
-
// Frontend-optimized user interface
|
|
52
|
+
// Frontend-optimized user interface with string-based IDs
|
|
53
53
|
interface AppUser extends IFrontendUser<'en'> {
|
|
54
54
|
_id: string;
|
|
55
55
|
siteLanguage: 'en';
|
|
56
56
|
accountStatus: AccountStatus;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
// Backend database interface
|
|
59
|
+
// Backend database interface supporting ObjectId types
|
|
60
60
|
interface DatabaseUser extends IBackendUser<'en'> {
|
|
61
61
|
_id: Types.ObjectId;
|
|
62
62
|
createdBy: Types.ObjectId;
|
|
@@ -67,20 +67,20 @@ interface DatabaseUser extends IBackendUser<'en'> {
|
|
|
67
67
|
### Secure Backup Code System
|
|
68
68
|
|
|
69
69
|
```typescript
|
|
70
|
-
import { BackupCodeString, Constants } from '@digitaldefiance/
|
|
70
|
+
import { BackupCodeString, Constants } from '@digitaldefiance/suite-core-lib';
|
|
71
71
|
|
|
72
72
|
// Generate cryptographically secure backup codes
|
|
73
73
|
const backupCodes = BackupCodeString.generateBackupCodes();
|
|
74
|
-
console.log(backupCodes.length); // 10
|
|
74
|
+
console.log(backupCodes.length); // Default is 10, configurable via Constants.BACKUP_CODES.Count
|
|
75
75
|
|
|
76
|
-
//
|
|
76
|
+
// Format a backup code for user display
|
|
77
77
|
const code = new BackupCodeString('deadbeefcafebabefeedface01234567');
|
|
78
|
-
console.log(code.value); // "dead-beef-cafe-babe-feed-face-0123-4567"
|
|
78
|
+
console.log(code.value); // Outputs: "dead-beef-cafe-babe-feed-face-0123-4567"
|
|
79
79
|
|
|
80
|
-
//
|
|
80
|
+
// Access multiple secure encoding formats
|
|
81
81
|
console.log(code.valueAsHexString); // Hex-encoded UTF-8 bytes
|
|
82
|
-
console.log(code.valueAsBase64String); // Base64-encoded
|
|
83
|
-
console.log(code.valueAsUint8Array); // Raw byte array
|
|
82
|
+
console.log(code.valueAsBase64String); // Base64-encoded string
|
|
83
|
+
console.log(code.valueAsUint8Array); // Raw Uint8Array byte array
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
### Localized Error Handling
|
|
@@ -91,36 +91,36 @@ import {
|
|
|
91
91
|
UsernameInUseError,
|
|
92
92
|
AccountLockedError,
|
|
93
93
|
CoreLanguage
|
|
94
|
-
} from '@digitaldefiance/
|
|
94
|
+
} from '@digitaldefiance/suite-core-lib';
|
|
95
95
|
|
|
96
|
-
//
|
|
96
|
+
// Throw errors with automatic localization support
|
|
97
97
|
throw new UserNotFoundError(CoreLanguage.French);
|
|
98
|
-
// "Compte utilisateur introuvable"
|
|
98
|
+
// Output: "Compte utilisateur introuvable"
|
|
99
99
|
|
|
100
100
|
throw new UsernameInUseError(CoreLanguage.Spanish);
|
|
101
|
-
// "El nombre de usuario ya está en uso"
|
|
101
|
+
// Output: "El nombre de usuario ya está en uso"
|
|
102
102
|
|
|
103
103
|
throw new AccountLockedError(CoreLanguage.German);
|
|
104
|
-
// "Konto ist von einem Administrator gesperrt"
|
|
104
|
+
// Output: "Konto ist von einem Administrator gesperrt"
|
|
105
105
|
```
|
|
106
106
|
|
|
107
107
|
## 🎯 How Frameworks Use These Primitives
|
|
108
108
|
|
|
109
|
-
The **@digitaldefiance/node-express-suite** package builds upon these primitives to create full framework components:
|
|
109
|
+
The **@digitaldefiance/node-express-suite** package builds upon these primitives to create full-featured framework components:
|
|
110
110
|
|
|
111
111
|
### Express.js Framework Usage
|
|
112
112
|
|
|
113
113
|
```typescript
|
|
114
|
-
// node-express-suite
|
|
114
|
+
// Internal usage of suite-core-lib primitives in node-express-suite
|
|
115
115
|
import {
|
|
116
116
|
IFrontendUser,
|
|
117
117
|
AccountStatus,
|
|
118
118
|
BackupCodeString,
|
|
119
119
|
UserNotFoundError,
|
|
120
120
|
CoreLanguage
|
|
121
|
-
} from '@digitaldefiance/
|
|
121
|
+
} from '@digitaldefiance/suite-core-lib';
|
|
122
122
|
|
|
123
|
-
//
|
|
123
|
+
// Middleware example: validate backup code input
|
|
124
124
|
function validateBackupCode(userInput: string, storedCodes: IBackupCode[]) {
|
|
125
125
|
try {
|
|
126
126
|
const inputCode = new BackupCodeString(userInput);
|
|
@@ -129,7 +129,7 @@ function validateBackupCode(userInput: string, storedCodes: IBackupCode[]) {
|
|
|
129
129
|
);
|
|
130
130
|
} catch (error) {
|
|
131
131
|
if (error instanceof InvalidBackupCodeError) {
|
|
132
|
-
//
|
|
132
|
+
// Handle localized error response gracefully
|
|
133
133
|
return false;
|
|
134
134
|
}
|
|
135
135
|
throw error;
|
|
@@ -140,16 +140,16 @@ function validateBackupCode(userInput: string, storedCodes: IBackupCode[]) {
|
|
|
140
140
|
### Frontend Framework Integration
|
|
141
141
|
|
|
142
142
|
```typescript
|
|
143
|
-
// Frontend frameworks consume
|
|
144
|
-
import { IFrontendUser, AccountStatus } from '@digitaldefiance/
|
|
143
|
+
// Frontend frameworks consume type-safe interfaces
|
|
144
|
+
import { IFrontendUser, AccountStatus } from '@digitaldefiance/suite-core-lib';
|
|
145
145
|
|
|
146
|
-
// React/Vue/Angular
|
|
146
|
+
// React/Vue/Angular component props example
|
|
147
147
|
interface UserProfileProps {
|
|
148
148
|
user: IFrontendUser<'en' | 'fr' | 'es'>;
|
|
149
149
|
onStatusChange: (status: AccountStatus) => void;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
//
|
|
152
|
+
// Function to update user status with type safety
|
|
153
153
|
function updateUserStatus(user: IFrontendUser<string>, status: AccountStatus) {
|
|
154
154
|
return {
|
|
155
155
|
...user,
|
|
@@ -162,11 +162,10 @@ function updateUserStatus(user: IFrontendUser<string>, status: AccountStatus) {
|
|
|
162
162
|
### Database Layer Integration
|
|
163
163
|
|
|
164
164
|
```typescript
|
|
165
|
-
//
|
|
166
|
-
import { IBackendUser, IUserRoleBase } from '@digitaldefiance/
|
|
165
|
+
// Database schemas implementing suite-core-lib interfaces
|
|
166
|
+
import { IBackendUser, IUserRoleBase } from '@digitaldefiance/suite-core-lib';
|
|
167
167
|
import { Types } from 'mongoose';
|
|
168
168
|
|
|
169
|
-
// Framework database schemas implement our interfaces
|
|
170
169
|
const userSchema = new mongoose.Schema<IBackendUser<'en'>>({
|
|
171
170
|
_id: { type: Types.ObjectId, required: true },
|
|
172
171
|
username: { type: String, required: true, unique: true },
|
|
@@ -176,7 +175,7 @@ const userSchema = new mongoose.Schema<IBackendUser<'en'>>({
|
|
|
176
175
|
enum: Object.values(AccountStatus),
|
|
177
176
|
default: AccountStatus.PendingEmailVerification
|
|
178
177
|
},
|
|
179
|
-
//
|
|
178
|
+
// Additional schema fields matching the interface
|
|
180
179
|
});
|
|
181
180
|
```
|
|
182
181
|
|
|
@@ -184,14 +183,14 @@ const userSchema = new mongoose.Schema<IBackendUser<'en'>>({
|
|
|
184
183
|
|
|
185
184
|
### Custom Model Extensions & Dynamic Registration
|
|
186
185
|
|
|
187
|
-
> **Note:** The static `ModelName` and `BaseModelName` enums are deprecated. For extensibility, use dynamic model registration
|
|
186
|
+
> **Note:** The static `ModelName` and `BaseModelName` enums are deprecated. For extensibility, use dynamic model registration available in `ModelRegistry` from `node-express-suite`.
|
|
188
187
|
|
|
189
|
-
You can
|
|
188
|
+
You can register and extend models dynamically at runtime:
|
|
190
189
|
|
|
191
190
|
```typescript
|
|
192
191
|
import { ModelRegistry } from '@digitaldefiance/node-express-suite';
|
|
193
192
|
|
|
194
|
-
// Register a custom model
|
|
193
|
+
// Register a custom model, e.g., Organization
|
|
195
194
|
ModelRegistry.instance.register({
|
|
196
195
|
modelName: 'Organization',
|
|
197
196
|
schema: organizationSchema,
|
|
@@ -199,18 +198,18 @@ ModelRegistry.instance.register({
|
|
|
199
198
|
collection: 'organizations',
|
|
200
199
|
});
|
|
201
200
|
|
|
202
|
-
// Retrieve
|
|
201
|
+
// Retrieve the model anywhere in your application
|
|
203
202
|
const orgModel = ModelRegistry.instance.get('Organization')?.model;
|
|
204
203
|
```
|
|
205
204
|
|
|
206
|
-
This approach
|
|
205
|
+
This dynamic approach supports advanced use cases such as multi-tenancy and plugin-based architectures.
|
|
207
206
|
|
|
208
207
|
### Email Token Types
|
|
209
208
|
|
|
210
209
|
```typescript
|
|
211
|
-
import { EmailTokenType } from '@digitaldefiance/
|
|
210
|
+
import { EmailTokenType } from '@digitaldefiance/suite-core-lib';
|
|
212
211
|
|
|
213
|
-
//
|
|
212
|
+
// Common built-in email token types
|
|
214
213
|
const tokenTypes = [
|
|
215
214
|
EmailTokenType.AccountVerification, // Email confirmation
|
|
216
215
|
EmailTokenType.PasswordReset, // Password reset flow
|
|
@@ -223,30 +222,30 @@ const tokenTypes = [
|
|
|
223
222
|
### Account Status Management
|
|
224
223
|
|
|
225
224
|
```typescript
|
|
226
|
-
import { AccountStatus } from '@digitaldefiance/
|
|
225
|
+
import { AccountStatus } from '@digitaldefiance/suite-core-lib';
|
|
227
226
|
|
|
228
|
-
//
|
|
227
|
+
// Manage user account states with fine granularity
|
|
229
228
|
switch (user.accountStatus) {
|
|
230
229
|
case AccountStatus.PendingEmailVerification:
|
|
231
|
-
//
|
|
230
|
+
// Trigger email verification workflow
|
|
232
231
|
break;
|
|
233
232
|
case AccountStatus.Active:
|
|
234
|
-
//
|
|
233
|
+
// Grant full access
|
|
235
234
|
break;
|
|
236
235
|
case AccountStatus.AdminLock:
|
|
237
|
-
//
|
|
236
|
+
// Restrict access pending admin intervention
|
|
238
237
|
break;
|
|
239
238
|
}
|
|
240
239
|
```
|
|
241
240
|
|
|
242
241
|
### Framework Integration (Built by Other Projects)
|
|
243
242
|
|
|
244
|
-
The **@digitaldefiance/node-ecies** and **@digitaldefiance/node-express-suite** packages
|
|
243
|
+
The **@digitaldefiance/node-ecies** and **@digitaldefiance/node-express-suite** packages leverage these primitives to build:
|
|
245
244
|
|
|
246
|
-
-
|
|
247
|
-
-
|
|
248
|
-
-
|
|
249
|
-
-
|
|
245
|
+
- A complete Express.js framework with middleware, routing, and database integration
|
|
246
|
+
- Frontend adapters for React, Vue, Angular, and Svelte
|
|
247
|
+
- Mobile and desktop SDKs for React Native, Flutter, Electron, and Tauri
|
|
248
|
+
- DevOps and deployment tooling including Docker, Kubernetes, and CI/CD pipelines
|
|
250
249
|
|
|
251
250
|
## 🧪 Testing & Quality
|
|
252
251
|
|
|
@@ -260,23 +259,23 @@ import {
|
|
|
260
259
|
registerSuiteCoreRuntimeConfiguration,
|
|
261
260
|
} from '@digitaldefiance/suite-core-lib';
|
|
262
261
|
|
|
263
|
-
//
|
|
262
|
+
// Retrieve current configuration
|
|
264
263
|
const config = getSuiteCoreRuntimeConfiguration();
|
|
265
264
|
|
|
266
|
-
// Register a custom
|
|
265
|
+
// Register a custom configuration with overrides
|
|
267
266
|
const customKey = Symbol('custom-suite-core-config');
|
|
268
267
|
registerSuiteCoreRuntimeConfiguration(customKey, 'example.com', { BcryptRounds: 12 });
|
|
269
268
|
const customConfig = getSuiteCoreRuntimeConfiguration(customKey);
|
|
270
269
|
```
|
|
271
270
|
|
|
272
|
-
All constants are immutable and accessible via the registry/config API. See `src/constants.ts` and `src/defaults.ts` for
|
|
271
|
+
All constants are immutable and accessible via the registry/config API. See `src/constants.ts` and `src/defaults.ts` for more information.
|
|
273
272
|
|
|
274
273
|
## 🏛️ Architectural Conventions
|
|
275
274
|
|
|
276
|
-
- Centralized constants file
|
|
277
|
-
- Immutability via Object.freeze
|
|
275
|
+
- Centralized constants file for configuration
|
|
276
|
+
- Immutability enforced via Object.freeze
|
|
278
277
|
- Registry/config pattern for runtime overrides
|
|
279
|
-
- Type-safe interfaces for all
|
|
278
|
+
- Type-safe interfaces for all configuration objects
|
|
280
279
|
|
|
281
280
|
## 🧪 Testing & Quality
|
|
282
281
|
|
|
@@ -295,13 +294,13 @@ yarn test
|
|
|
295
294
|
|
|
296
295
|
## 🤝 Contributing
|
|
297
296
|
|
|
298
|
-
We welcome contributions to help build the future of secure user management! Areas where
|
|
297
|
+
We welcome contributions to help build the future of secure user management! Areas where contributions are especially appreciated:
|
|
299
298
|
|
|
300
|
-
-
|
|
301
|
-
-
|
|
302
|
-
-
|
|
303
|
-
-
|
|
304
|
-
-
|
|
299
|
+
- Frontend component libraries for popular frameworks
|
|
300
|
+
- Additional language translations and localization
|
|
301
|
+
- Performance optimizations and benchmarking
|
|
302
|
+
- Documentation and example applications
|
|
303
|
+
- Security auditing and penetration testing
|
|
305
304
|
|
|
306
305
|
## 📄 License
|
|
307
306
|
|
|
@@ -327,6 +326,11 @@ MIT © [Digital Defiance](https://github.com/digitaldefiance)
|
|
|
327
326
|
|
|
328
327
|
## ChangeLog
|
|
329
328
|
|
|
329
|
+
## v1.1.12: Update readme
|
|
330
|
+
|
|
331
|
+
- Sun Oct 26 2025 22:31:00 GMT-07 (Pacific Daylight Time)
|
|
332
|
+
- Update readme
|
|
333
|
+
|
|
330
334
|
## v1.1.11: Update libs
|
|
331
335
|
|
|
332
336
|
- Sun Oct 26 2025 20:33:00 GMT-0700 (Pacific Daylight Time)
|
|
@@ -354,8 +358,9 @@ MIT © [Digital Defiance](https://github.com/digitaldefiance)
|
|
|
354
358
|
- Version bump
|
|
355
359
|
|
|
356
360
|
## v1.1.5: add InvalidChallengeResponseError/LoginChallengeExpiredError
|
|
357
|
-
|
|
358
|
-
|
|
361
|
+
|
|
362
|
+
- Thu Oct 23 2025 20:34:00 GMT-0700 (Pacific Daylight Time)
|
|
363
|
+
- add InvalidChallengeResponseError/LoginChallengeExpiredError
|
|
359
364
|
|
|
360
365
|
## v1.1.4: export PrivateKeyRequiredError
|
|
361
366
|
|
|
@@ -398,14 +403,15 @@ MIT © [Digital Defiance](https://github.com/digitaldefiance)
|
|
|
398
403
|
|
|
399
404
|
### Dependency Updates
|
|
400
405
|
|
|
401
|
-
- Updated
|
|
406
|
+
- Updated
|
|
402
407
|
|
|
403
408
|
```plaintext
|
|
404
409
|
@digitaldefiance/ecies-lib
|
|
405
410
|
```
|
|
411
|
+
|
|
406
412
|
from 1.0.31 to 1.0.32
|
|
407
413
|
|
|
408
|
-
- Updated
|
|
414
|
+
- Updated
|
|
409
415
|
|
|
410
416
|
```plaintext
|
|
411
417
|
@digitaldefiance/node-ecies-lib
|
|
@@ -413,10 +419,9 @@ MIT © [Digital Defiance](https://github.com/digitaldefiance)
|
|
|
413
419
|
|
|
414
420
|
from 1.0.12 to 1.0.13
|
|
415
421
|
|
|
416
|
-
|
|
417
422
|
### New Features
|
|
418
423
|
|
|
419
|
-
- Added site configuration constants:
|
|
424
|
+
- Added site configuration constants:
|
|
420
425
|
|
|
421
426
|
```plaintext
|
|
422
427
|
Site
|
|
@@ -426,39 +431,26 @@ MIT © [Digital Defiance](https://github.com/digitaldefiance)
|
|
|
426
431
|
SiteTagline
|
|
427
432
|
```
|
|
428
433
|
|
|
429
|
-
|
|
430
434
|
```plaintext
|
|
431
435
|
SiteDescription
|
|
432
436
|
```
|
|
433
437
|
|
|
434
|
-
|
|
435
438
|
to core constants
|
|
436
439
|
|
|
437
|
-
|
|
438
|
-
|
|
439
440
|
- Added new error type enumerations:
|
|
440
441
|
|
|
441
|
-
|
|
442
442
|
```plaintext
|
|
443
443
|
FecErrorType
|
|
444
444
|
```
|
|
445
445
|
|
|
446
|
-
|
|
447
|
-
|
|
448
446
|
\- Forward Error Correction error types (13 error cases)
|
|
449
447
|
|
|
450
|
-
|
|
451
|
-
|
|
452
448
|
```plaintext
|
|
453
449
|
Pbkdf2ErrorType
|
|
454
450
|
```
|
|
455
451
|
|
|
456
|
-
|
|
457
|
-
|
|
458
452
|
\- PBKDF2 validation error types (2 error cases)
|
|
459
453
|
|
|
460
|
-
|
|
461
|
-
|
|
462
454
|
### String Key Refactoring
|
|
463
455
|
|
|
464
456
|
- Removed unused common string keys (UnexpectedError, Ready, Connecting, Disconnected, MongoDB, Unauthorized, NoActiveRequest, NoUserOnRequest, NoActiveResponse)
|
|
@@ -468,18 +460,19 @@ MIT © [Digital Defiance](https://github.com/digitaldefiance)
|
|
|
468
460
|
|
|
469
461
|
### Module Exports
|
|
470
462
|
|
|
471
|
-
- Updated enumeration index to export new
|
|
463
|
+
- Updated enumeration index to export new
|
|
472
464
|
|
|
473
465
|
```plaintext
|
|
474
466
|
FecErrorType
|
|
475
467
|
```
|
|
468
|
+
|
|
476
469
|
and
|
|
477
470
|
|
|
478
471
|
```plaintext
|
|
479
472
|
Pbkdf2ErrorType
|
|
480
473
|
```
|
|
481
|
-
enumerations
|
|
482
474
|
|
|
475
|
+
enumerations
|
|
483
476
|
|
|
484
477
|
## v1.0.13: Add string
|
|
485
478
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@digitaldefiance/suite-core-lib",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.12",
|
|
4
4
|
"description": "Generic user system and document system common core for applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"publish:public": "npm publish --access public"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@digitaldefiance/ecies-lib": "1.1.
|
|
19
|
+
"@digitaldefiance/ecies-lib": "1.1.8",
|
|
20
20
|
"@digitaldefiance/i18n-lib": "1.3.1",
|
|
21
|
-
"@digitaldefiance/node-ecies-lib": "1.1.
|
|
21
|
+
"@digitaldefiance/node-ecies-lib": "1.1.5"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@babel/core": "^7.28.4",
|