@sembix/cli 1.1.0 → 1.2.1

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.
Files changed (48) hide show
  1. package/.env.example +7 -0
  2. package/README.md +161 -8
  3. package/dist/__tests__/config-schema.test.js +1 -1
  4. package/dist/__tests__/config.test.d.ts +2 -0
  5. package/dist/__tests__/config.test.d.ts.map +1 -0
  6. package/dist/__tests__/config.test.js +75 -0
  7. package/dist/__tests__/config.test.js.map +1 -0
  8. package/dist/__tests__/integration/configure.test.d.ts +2 -0
  9. package/dist/__tests__/integration/configure.test.d.ts.map +1 -0
  10. package/dist/__tests__/integration/configure.test.js +247 -0
  11. package/dist/__tests__/integration/configure.test.js.map +1 -0
  12. package/dist/commands/__tests__/configure.test.d.ts +2 -0
  13. package/dist/commands/__tests__/configure.test.d.ts.map +1 -0
  14. package/dist/commands/__tests__/configure.test.js +229 -0
  15. package/dist/commands/__tests__/configure.test.js.map +1 -0
  16. package/dist/commands/configure.d.ts +6 -0
  17. package/dist/commands/configure.d.ts.map +1 -0
  18. package/dist/commands/configure.js +90 -0
  19. package/dist/commands/configure.js.map +1 -0
  20. package/dist/config-schema.d.ts +31 -335
  21. package/dist/config-schema.d.ts.map +1 -1
  22. package/dist/config.d.ts.map +1 -1
  23. package/dist/config.js +19 -3
  24. package/dist/config.js.map +1 -1
  25. package/dist/index.js +17 -0
  26. package/dist/index.js.map +1 -1
  27. package/dist/prompts/__tests__/environment-setup.test.d.ts +2 -0
  28. package/dist/prompts/__tests__/environment-setup.test.d.ts.map +1 -0
  29. package/dist/prompts/__tests__/environment-setup.test.js +206 -0
  30. package/dist/prompts/__tests__/environment-setup.test.js.map +1 -0
  31. package/dist/prompts/__tests__/hub-integration.test.d.ts +2 -0
  32. package/dist/prompts/__tests__/hub-integration.test.d.ts.map +1 -0
  33. package/dist/prompts/__tests__/hub-integration.test.js +126 -0
  34. package/dist/prompts/__tests__/hub-integration.test.js.map +1 -0
  35. package/dist/prompts/hub-integration-step.js +3 -3
  36. package/dist/prompts/hub-integration.js +3 -3
  37. package/dist/sembix-cli-1.2.1.tgz +0 -0
  38. package/dist/utils/__tests__/config-file.test.d.ts +2 -0
  39. package/dist/utils/__tests__/config-file.test.d.ts.map +1 -0
  40. package/dist/utils/__tests__/config-file.test.js +218 -0
  41. package/dist/utils/__tests__/config-file.test.js.map +1 -0
  42. package/dist/utils/config-file.d.ts +23 -0
  43. package/dist/utils/config-file.d.ts.map +1 -0
  44. package/dist/utils/config-file.js +100 -0
  45. package/dist/utils/config-file.js.map +1 -0
  46. package/dist/utils/config-loader.js +7 -7
  47. package/package.json +24 -24
  48. package/dist/sembix-cli-1.1.0.tgz +0 -0
@@ -0,0 +1,218 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { promises as fs } from 'fs';
3
+ import { existsSync } from 'fs';
4
+ import { homedir } from 'os';
5
+ // Mock os module BEFORE importing the module under test
6
+ vi.mock('os', () => ({
7
+ homedir: vi.fn(() => '/home/testuser'),
8
+ }));
9
+ // Mock fs modules
10
+ vi.mock('fs', () => ({
11
+ existsSync: vi.fn(),
12
+ promises: {
13
+ readFile: vi.fn(),
14
+ writeFile: vi.fn(),
15
+ mkdir: vi.fn(),
16
+ },
17
+ }));
18
+ // NOW import the module under test
19
+ import { readConfig, writeConfig, getConfigPath, configExists, } from '../config-file.js';
20
+ const mockExistsSync = vi.mocked(existsSync);
21
+ const mockReadFile = vi.mocked(fs.readFile);
22
+ const mockWriteFile = vi.mocked(fs.writeFile);
23
+ const mockMkdir = vi.mocked(fs.mkdir);
24
+ const mockHomedir = vi.mocked(homedir);
25
+ describe('config-file', () => {
26
+ beforeEach(() => {
27
+ vi.clearAllMocks();
28
+ mockHomedir.mockReturnValue('/home/testuser');
29
+ });
30
+ describe('getConfigPath', () => {
31
+ it('should return the correct config path', () => {
32
+ const path = getConfigPath();
33
+ expect(path).toBe('/home/testuser/.sembix/config');
34
+ });
35
+ });
36
+ describe('configExists', () => {
37
+ it('should return true when config file exists', () => {
38
+ mockExistsSync.mockReturnValue(true);
39
+ expect(configExists()).toBe(true);
40
+ expect(mockExistsSync).toHaveBeenCalledWith('/home/testuser/.sembix/config');
41
+ });
42
+ it('should return false when config file does not exist', () => {
43
+ mockExistsSync.mockReturnValue(false);
44
+ expect(configExists()).toBe(false);
45
+ });
46
+ });
47
+ describe('readConfig', () => {
48
+ it('should return empty object when config file does not exist', async () => {
49
+ mockExistsSync.mockReturnValue(false);
50
+ const config = await readConfig();
51
+ expect(config).toEqual({});
52
+ });
53
+ it('should parse valid config file with github_token', async () => {
54
+ mockExistsSync.mockReturnValue(true);
55
+ mockReadFile.mockResolvedValue('github_token=ghp_test123\n');
56
+ const config = await readConfig();
57
+ expect(config).toEqual({
58
+ github_token: 'ghp_test123',
59
+ });
60
+ });
61
+ it('should parse valid config file with both fields', async () => {
62
+ mockExistsSync.mockReturnValue(true);
63
+ mockReadFile.mockResolvedValue('github_token=ghp_test123\ndefault_github_org=acme-corp\n');
64
+ const config = await readConfig();
65
+ expect(config).toEqual({
66
+ github_token: 'ghp_test123',
67
+ default_github_org: 'acme-corp',
68
+ });
69
+ });
70
+ it('should ignore comments and empty lines', async () => {
71
+ mockExistsSync.mockReturnValue(true);
72
+ mockReadFile.mockResolvedValue(`
73
+ # This is a comment
74
+ github_token=ghp_test123
75
+
76
+ ; Another comment style
77
+ default_github_org=acme-corp
78
+ `);
79
+ const config = await readConfig();
80
+ expect(config).toEqual({
81
+ github_token: 'ghp_test123',
82
+ default_github_org: 'acme-corp',
83
+ });
84
+ });
85
+ it('should handle values with equals signs', async () => {
86
+ mockExistsSync.mockReturnValue(true);
87
+ mockReadFile.mockResolvedValue('github_token=ghp_test=123=456\n');
88
+ const config = await readConfig();
89
+ expect(config).toEqual({
90
+ github_token: 'ghp_test=123=456',
91
+ });
92
+ });
93
+ it('should trim whitespace from keys and values', async () => {
94
+ mockExistsSync.mockReturnValue(true);
95
+ mockReadFile.mockResolvedValue(' github_token = ghp_test123 \n');
96
+ const config = await readConfig();
97
+ expect(config).toEqual({
98
+ github_token: 'ghp_test123',
99
+ });
100
+ });
101
+ it('should ignore unknown keys', async () => {
102
+ mockExistsSync.mockReturnValue(true);
103
+ mockReadFile.mockResolvedValue('github_token=ghp_test123\nunknown_key=value\ndefault_github_org=acme\n');
104
+ const config = await readConfig();
105
+ expect(config).toEqual({
106
+ github_token: 'ghp_test123',
107
+ default_github_org: 'acme',
108
+ });
109
+ });
110
+ it('should return empty object on read error', async () => {
111
+ mockExistsSync.mockReturnValue(true);
112
+ mockReadFile.mockRejectedValue(new Error('Permission denied'));
113
+ const config = await readConfig();
114
+ expect(config).toEqual({});
115
+ });
116
+ it('should handle invalid format gracefully', async () => {
117
+ mockExistsSync.mockReturnValue(true);
118
+ mockReadFile.mockResolvedValue('invalid line without equals\n');
119
+ const config = await readConfig();
120
+ expect(config).toEqual({});
121
+ });
122
+ });
123
+ describe('writeConfig', () => {
124
+ it('should create directory if it does not exist', async () => {
125
+ mockExistsSync.mockReturnValue(false);
126
+ mockMkdir.mockResolvedValue(undefined);
127
+ mockWriteFile.mockResolvedValue(undefined);
128
+ const config = {
129
+ github_token: 'ghp_test123',
130
+ };
131
+ await writeConfig(config);
132
+ expect(mockMkdir).toHaveBeenCalledWith('/home/testuser/.sembix', {
133
+ recursive: true,
134
+ mode: 0o700,
135
+ });
136
+ });
137
+ it('should not create directory if it already exists', async () => {
138
+ mockExistsSync.mockReturnValue(true);
139
+ mockWriteFile.mockResolvedValue(undefined);
140
+ const config = {
141
+ github_token: 'ghp_test123',
142
+ };
143
+ await writeConfig(config);
144
+ expect(mockMkdir).not.toHaveBeenCalled();
145
+ });
146
+ it('should write config with github_token only', async () => {
147
+ mockExistsSync.mockReturnValue(true);
148
+ mockWriteFile.mockResolvedValue(undefined);
149
+ const config = {
150
+ github_token: 'ghp_test123',
151
+ };
152
+ await writeConfig(config);
153
+ expect(mockWriteFile).toHaveBeenCalledWith('/home/testuser/.sembix/config', expect.stringContaining('github_token=ghp_test123'), { mode: 0o600 });
154
+ });
155
+ it('should write config with both fields', async () => {
156
+ mockExistsSync.mockReturnValue(true);
157
+ mockWriteFile.mockResolvedValue(undefined);
158
+ const config = {
159
+ github_token: 'ghp_test123',
160
+ default_github_org: 'acme-corp',
161
+ };
162
+ await writeConfig(config);
163
+ const writeCall = mockWriteFile.mock.calls[0];
164
+ const content = writeCall[1];
165
+ expect(content).toContain('github_token=ghp_test123');
166
+ expect(content).toContain('default_github_org=acme-corp');
167
+ expect(writeCall[2]).toEqual({ mode: 0o600 });
168
+ });
169
+ it('should include header comments', async () => {
170
+ mockExistsSync.mockReturnValue(true);
171
+ mockWriteFile.mockResolvedValue(undefined);
172
+ const config = {
173
+ github_token: 'ghp_test123',
174
+ };
175
+ await writeConfig(config);
176
+ const content = mockWriteFile.mock.calls[0][1];
177
+ expect(content).toContain('# Sembix CLI Configuration');
178
+ expect(content).toContain('# Generated by: sembix configure');
179
+ });
180
+ it('should end with trailing newline', async () => {
181
+ mockExistsSync.mockReturnValue(true);
182
+ mockWriteFile.mockResolvedValue(undefined);
183
+ const config = {
184
+ github_token: 'ghp_test123',
185
+ };
186
+ await writeConfig(config);
187
+ const content = mockWriteFile.mock.calls[0][1];
188
+ expect(content.endsWith('\n')).toBe(true);
189
+ });
190
+ it('should handle empty config object', async () => {
191
+ mockExistsSync.mockReturnValue(true);
192
+ mockWriteFile.mockResolvedValue(undefined);
193
+ const config = {};
194
+ await writeConfig(config);
195
+ const content = mockWriteFile.mock.calls[0][1];
196
+ expect(content).toContain('# Sembix CLI Configuration');
197
+ expect(content).not.toContain('github_token=');
198
+ expect(content).not.toContain('default_github_org=');
199
+ });
200
+ it('should propagate write errors', async () => {
201
+ mockExistsSync.mockReturnValue(true);
202
+ mockWriteFile.mockRejectedValue(new Error('Disk full'));
203
+ const config = {
204
+ github_token: 'ghp_test123',
205
+ };
206
+ await expect(writeConfig(config)).rejects.toThrow('Disk full');
207
+ });
208
+ it('should propagate mkdir errors', async () => {
209
+ mockExistsSync.mockReturnValue(false);
210
+ mockMkdir.mockRejectedValue(new Error('Permission denied'));
211
+ const config = {
212
+ github_token: 'ghp_test123',
213
+ };
214
+ await expect(writeConfig(config)).rejects.toThrow('Permission denied');
215
+ });
216
+ });
217
+ });
218
+ //# sourceMappingURL=config-file.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-file.test.js","sourceRoot":"","sources":["../../../src/utils/__tests__/config-file.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ export interface SembixConfig {
2
+ github_token?: string;
3
+ default_github_org?: string;
4
+ }
5
+ /**
6
+ * Reads the configuration from ~/.sembix/config
7
+ * Returns an empty object if the file doesn't exist
8
+ */
9
+ export declare function readConfig(): Promise<SembixConfig>;
10
+ /**
11
+ * Writes configuration to ~/.sembix/config
12
+ * Creates the directory if it doesn't exist
13
+ */
14
+ export declare function writeConfig(config: SembixConfig): Promise<void>;
15
+ /**
16
+ * Returns the path to the config file for display purposes
17
+ */
18
+ export declare function getConfigPath(): string;
19
+ /**
20
+ * Checks if the config file exists
21
+ */
22
+ export declare function configExists(): boolean;
23
+ //# sourceMappingURL=config-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-file.d.ts","sourceRoot":"","sources":["../../src/utils/config-file.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAWD;;;GAGG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,YAAY,CAAC,CAaxD;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAOrE;AAwDD;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAEtC"}
@@ -0,0 +1,100 @@
1
+ import { homedir } from 'os';
2
+ import { join } from 'path';
3
+ import { promises as fs } from 'fs';
4
+ import { existsSync } from 'fs';
5
+ const CONFIG_DIR = join(homedir(), '.sembix');
6
+ const CONFIG_FILE = join(CONFIG_DIR, 'config');
7
+ /**
8
+ * Ensures the ~/.sembix directory exists with proper permissions
9
+ */
10
+ async function ensureConfigDirectory() {
11
+ if (!existsSync(CONFIG_DIR)) {
12
+ await fs.mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 });
13
+ }
14
+ }
15
+ /**
16
+ * Reads the configuration from ~/.sembix/config
17
+ * Returns an empty object if the file doesn't exist
18
+ */
19
+ export async function readConfig() {
20
+ try {
21
+ if (!existsSync(CONFIG_FILE)) {
22
+ return {};
23
+ }
24
+ const content = await fs.readFile(CONFIG_FILE, 'utf-8');
25
+ return parseConfig(content);
26
+ }
27
+ catch {
28
+ // If we can't read the config, return empty object
29
+ // This allows the app to fall back to other config sources
30
+ return {};
31
+ }
32
+ }
33
+ /**
34
+ * Writes configuration to ~/.sembix/config
35
+ * Creates the directory if it doesn't exist
36
+ */
37
+ export async function writeConfig(config) {
38
+ await ensureConfigDirectory();
39
+ const content = formatConfig(config);
40
+ // Write with restricted permissions (only user can read/write)
41
+ await fs.writeFile(CONFIG_FILE, content, { mode: 0o600 });
42
+ }
43
+ /**
44
+ * Parses INI-style config content into a SembixConfig object
45
+ */
46
+ function parseConfig(content) {
47
+ const config = {};
48
+ const lines = content.split('\n');
49
+ for (const line of lines) {
50
+ const trimmed = line.trim();
51
+ // Skip empty lines and comments
52
+ if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith(';')) {
53
+ continue;
54
+ }
55
+ // Parse key=value pairs
56
+ const match = trimmed.match(/^([^=]+)=(.*)$/);
57
+ if (match) {
58
+ const key = match[1].trim();
59
+ const value = match[2].trim();
60
+ if (key === 'github_token') {
61
+ config.github_token = value;
62
+ }
63
+ else if (key === 'default_github_org') {
64
+ config.default_github_org = value;
65
+ }
66
+ }
67
+ }
68
+ return config;
69
+ }
70
+ /**
71
+ * Formats a SembixConfig object into INI-style content
72
+ */
73
+ function formatConfig(config) {
74
+ const lines = [
75
+ '# Sembix CLI Configuration',
76
+ '# Generated by: sembix configure',
77
+ '',
78
+ ];
79
+ if (config.github_token) {
80
+ lines.push(`github_token=${config.github_token}`);
81
+ }
82
+ if (config.default_github_org) {
83
+ lines.push(`default_github_org=${config.default_github_org}`);
84
+ }
85
+ lines.push(''); // trailing newline
86
+ return lines.join('\n');
87
+ }
88
+ /**
89
+ * Returns the path to the config file for display purposes
90
+ */
91
+ export function getConfigPath() {
92
+ return CONFIG_FILE;
93
+ }
94
+ /**
95
+ * Checks if the config file exists
96
+ */
97
+ export function configExists() {
98
+ return existsSync(CONFIG_FILE);
99
+ }
100
+ //# sourceMappingURL=config-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-file.js","sourceRoot":"","sources":["../../src/utils/config-file.ts"],"names":[],"mappings":"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"}
@@ -27,7 +27,7 @@ export async function loadConfigFile(configPath) {
27
27
  }
28
28
  else {
29
29
  // Extract error messages from Zod
30
- const errors = result.error.errors.map(err => {
30
+ const errors = result.error.issues.map(err => {
31
31
  const path = err.path.join('.');
32
32
  return `${path}: ${err.message}`;
33
33
  });
@@ -305,19 +305,19 @@ function validateMergedConfig(merged, cliFlags) {
305
305
  if (cliFlags.awsAccountId !== undefined && merged.awsAccountId) {
306
306
  const result = awsAccountIdSchema.safeParse(merged.awsAccountId);
307
307
  if (!result.success) {
308
- errors.push(`--aws-account-id: ${result.error.errors[0].message}`);
308
+ errors.push(`--aws-account-id: ${result.error.issues[0].message}`);
309
309
  }
310
310
  }
311
311
  if (cliFlags.customerRoleArn !== undefined && merged.customerRoleArn) {
312
312
  const result = iamRoleArnSchema.safeParse(merged.customerRoleArn);
313
313
  if (!result.success) {
314
- errors.push(`--customer-role-arn: ${result.error.errors[0].message}`);
314
+ errors.push(`--customer-role-arn: ${result.error.issues[0].message}`);
315
315
  }
316
316
  }
317
317
  if (cliFlags.cloudfrontCertArn !== undefined && merged.tls?.cloudfrontCertArn) {
318
318
  const result = acmCertArnSchema.safeParse(merged.tls.cloudfrontCertArn);
319
319
  if (!result.success) {
320
- errors.push(`--cloudfront-cert-arn: ${result.error.errors[0].message}`);
320
+ errors.push(`--cloudfront-cert-arn: ${result.error.issues[0].message}`);
321
321
  }
322
322
  // Special check: CloudFront cert must be in us-east-1
323
323
  if (result.success && !merged.tls.cloudfrontCertArn.includes('us-east-1')) {
@@ -327,13 +327,13 @@ function validateMergedConfig(merged, cliFlags) {
327
327
  if (cliFlags.bffAlbCertArn !== undefined && merged.tls?.bffAlbCertificateArn) {
328
328
  const result = acmCertArnSchema.safeParse(merged.tls.bffAlbCertificateArn);
329
329
  if (!result.success) {
330
- errors.push(`--bff-alb-cert-arn: ${result.error.errors[0].message}`);
330
+ errors.push(`--bff-alb-cert-arn: ${result.error.issues[0].message}`);
331
331
  }
332
332
  }
333
333
  if (cliFlags.hostedZoneId !== undefined && merged.tls?.hostedZoneId) {
334
334
  const result = route53ZoneIdSchema.safeParse(merged.tls.hostedZoneId);
335
335
  if (!result.success) {
336
- errors.push(`--hosted-zone-id: ${result.error.errors[0].message}`);
336
+ errors.push(`--hosted-zone-id: ${result.error.issues[0].message}`);
337
337
  }
338
338
  }
339
339
  // Validate IAM roles from CLI flags
@@ -348,7 +348,7 @@ function validateMergedConfig(merged, cliFlags) {
348
348
  if (value) {
349
349
  const result = iamRoleArnSchema.safeParse(value);
350
350
  if (!result.success) {
351
- errors.push(`${name}: ${result.error.errors[0].message}`);
351
+ errors.push(`${name}: ${result.error.issues[0].message}`);
352
352
  }
353
353
  }
354
354
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sembix/cli",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "CLI tool for managing Sembix products",
5
5
  "type": "module",
6
6
  "bin": {
@@ -53,37 +53,37 @@
53
53
  "access": "public"
54
54
  },
55
55
  "dependencies": {
56
- "@inquirer/prompts": "^7.2.0",
57
- "@octokit/rest": "^21.0.2",
58
- "chalk": "^5.3.0",
59
- "commander": "^12.1.0",
60
- "dotenv": "^16.4.7",
61
- "js-yaml": "^4.1.0",
56
+ "@inquirer/prompts": "^8.0.1",
57
+ "@octokit/rest": "^22.0.1",
58
+ "chalk": "^5.6.2",
59
+ "commander": "^14.0.2",
60
+ "dotenv": "^17.2.3",
61
+ "js-yaml": "^4.1.1",
62
62
  "libsodium-wrappers": "^0.7.15",
63
- "ora": "^8.1.1",
64
- "zod": "^3.23.8"
63
+ "ora": "^9.0.0",
64
+ "zod": "^4.1.12"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@semantic-release/changelog": "^6.0.3",
68
- "@semantic-release/commit-analyzer": "^13.0.0",
68
+ "@semantic-release/commit-analyzer": "^13.0.1",
69
69
  "@semantic-release/git": "^10.0.1",
70
- "@semantic-release/github": "^11.0.0",
71
- "@semantic-release/npm": "^12.0.0",
72
- "@semantic-release/release-notes-generator": "^14.0.0",
70
+ "@semantic-release/github": "^12.0.2",
71
+ "@semantic-release/npm": "^13.1.2",
72
+ "@semantic-release/release-notes-generator": "^14.1.0",
73
73
  "@types/js-yaml": "^4.0.9",
74
- "conventional-changelog-conventionalcommits": "^8.0.0",
74
+ "conventional-changelog-conventionalcommits": "^9.1.0",
75
75
  "@types/libsodium-wrappers": "^0.7.14",
76
- "@types/node": "^20.17.10",
77
- "@typescript-eslint/eslint-plugin": "^8.0.0",
78
- "@typescript-eslint/parser": "^8.0.0",
79
- "@vitest/coverage-v8": "^4.0.12",
80
- "@vitest/ui": "^4.0.12",
81
- "eslint": "^9.0.0",
76
+ "@types/node": "^24.10.1",
77
+ "@typescript-eslint/eslint-plugin": "^8.47.0",
78
+ "@typescript-eslint/parser": "^8.47.0",
79
+ "@vitest/coverage-v8": "^4.0.13",
80
+ "@vitest/ui": "^4.0.13",
81
+ "eslint": "^9.39.1",
82
82
  "happy-dom": "^20.0.10",
83
- "semantic-release": "^24.0.0",
84
- "tsx": "^4.19.2",
85
- "typescript": "^5.8.0",
86
- "vitest": "^4.0.12"
83
+ "semantic-release": "^25.0.2",
84
+ "tsx": "^4.20.6",
85
+ "typescript": "^5.9.3",
86
+ "vitest": "^4.0.13"
87
87
  },
88
88
  "engines": {
89
89
  "node": ">=20.0.0"
Binary file