timonel 2.4.0 → 2.5.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.
@@ -0,0 +1,331 @@
1
+ /**
2
+ * @fileoverview Pod template utilities for generating reusable Kubernetes pod specifications
3
+ * @since 2.5.0
4
+ */
5
+ /**
6
+ * Creates a secure pod template with best practices
7
+ * @param spec - Pod template specification
8
+ * @param profile - Security profile to apply
9
+ * @returns Pod template object
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const template = createSecurePodTemplate({
14
+ * name: 'web-app',
15
+ * containers: [{
16
+ * name: 'app',
17
+ * image: 'nginx:1.27',
18
+ * port: 80
19
+ * }]
20
+ * }, 'restricted');
21
+ * ```
22
+ *
23
+ * @since 2.5.0
24
+ */
25
+ export function createSecurePodTemplate(spec, profile = 'baseline') {
26
+ const securityContext = getSecurityContextForProfile(profile);
27
+ const containers = spec.containers.map((container) => applyContainerSecurity(container, profile));
28
+ const metadata = {
29
+ name: spec.name,
30
+ labels: {
31
+ 'app.kubernetes.io/name': spec.name,
32
+ ...(spec.labels || {}),
33
+ },
34
+ };
35
+ if (spec.annotations) {
36
+ metadata['annotations'] = spec.annotations;
37
+ }
38
+ const combinedSecurityContext = { ...securityContext };
39
+ if (spec.securityContext) {
40
+ Object.assign(combinedSecurityContext, spec.securityContext);
41
+ }
42
+ const podSpec = {
43
+ containers,
44
+ restartPolicy: spec.restartPolicy ?? 'Always',
45
+ securityContext: combinedSecurityContext,
46
+ };
47
+ if (spec.initContainers) {
48
+ podSpec['initContainers'] = spec.initContainers.map((container) => applyContainerSecurity(container, profile));
49
+ }
50
+ if (spec.volumes) {
51
+ podSpec['volumes'] = spec.volumes;
52
+ }
53
+ if (spec.serviceAccountName) {
54
+ podSpec['serviceAccountName'] = spec.serviceAccountName;
55
+ }
56
+ if (spec.nodeSelector) {
57
+ podSpec['nodeSelector'] = spec.nodeSelector;
58
+ }
59
+ if (spec.tolerations) {
60
+ podSpec['tolerations'] = spec.tolerations;
61
+ }
62
+ if (spec.affinity) {
63
+ podSpec['affinity'] = spec.affinity;
64
+ }
65
+ if (spec.dnsPolicy) {
66
+ podSpec['dnsPolicy'] = spec.dnsPolicy;
67
+ }
68
+ if (spec.hostNetwork) {
69
+ podSpec['hostNetwork'] = spec.hostNetwork;
70
+ }
71
+ if (spec.priorityClassName) {
72
+ podSpec['priorityClassName'] = spec.priorityClassName;
73
+ }
74
+ return {
75
+ metadata,
76
+ spec: podSpec,
77
+ };
78
+ }
79
+ /**
80
+ * Creates a workload-optimized pod template
81
+ * @param spec - Pod template specification
82
+ * @param workloadType - Type of workload
83
+ * @returns Pod template object
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * const template = createWorkloadPodTemplate({
88
+ * name: 'api-server',
89
+ * containers: [{
90
+ * name: 'api',
91
+ * image: 'myapi:latest',
92
+ * port: 8080
93
+ * }]
94
+ * }, 'api');
95
+ * ```
96
+ *
97
+ * @since 2.5.0
98
+ */
99
+ export function createWorkloadPodTemplate(spec, workloadType) {
100
+ const workloadConfig = getWorkloadConfiguration(workloadType);
101
+ const enhancedSpec = {
102
+ ...spec,
103
+ labels: { ...(spec.labels || {}), ...(workloadConfig.labels || {}) },
104
+ };
105
+ if (workloadConfig.restartPolicy) {
106
+ enhancedSpec.restartPolicy = workloadConfig.restartPolicy;
107
+ }
108
+ return createSecurePodTemplate(enhancedSpec, 'baseline');
109
+ }
110
+ /**
111
+ * Creates a multi-container pod template with sidecar pattern
112
+ * @param mainContainer - Main application container
113
+ * @param sidecars - Sidecar containers
114
+ * @param options - Additional pod options
115
+ * @returns Pod template object
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * const template = createSidecarPodTemplate(
120
+ * { name: 'app', image: 'myapp:latest', port: 8080 },
121
+ * [{ name: 'proxy', image: 'envoy:latest', port: 9901 }],
122
+ * { name: 'app-with-proxy' }
123
+ * );
124
+ * ```
125
+ *
126
+ * @since 2.5.0
127
+ */
128
+ export function createSidecarPodTemplate(mainContainer, sidecars, options) {
129
+ const spec = {
130
+ name: options.name || `${mainContainer.name}-pod`,
131
+ containers: [mainContainer, ...sidecars],
132
+ ...options,
133
+ };
134
+ return createSecurePodTemplate(spec, 'baseline');
135
+ }
136
+ /**
137
+ * Creates a pod template with health checks
138
+ * @param spec - Pod template specification
139
+ * @param healthConfig - Health check configuration
140
+ * @returns Pod template object
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * const template = createHealthyPodTemplate({
145
+ * name: 'web-app',
146
+ * containers: [{
147
+ * name: 'app',
148
+ * image: 'nginx:1.27',
149
+ * port: 80
150
+ * }]
151
+ * }, {
152
+ * httpPath: '/health',
153
+ * port: 80
154
+ * });
155
+ * ```
156
+ *
157
+ * @since 2.5.0
158
+ */
159
+ export function createHealthyPodTemplate(spec, healthConfig) {
160
+ const enhancedContainers = spec.containers.map((container) => ({
161
+ ...container,
162
+ livenessProbe: createHttpProbe(healthConfig, 'liveness'),
163
+ readinessProbe: createHttpProbe(healthConfig, 'readiness'),
164
+ }));
165
+ return createSecurePodTemplate({ ...spec, containers: enhancedContainers }, 'baseline');
166
+ }
167
+ /**
168
+ * Gets security context for the specified profile
169
+ * @private
170
+ */
171
+ function getSecurityContextForProfile(profile) {
172
+ switch (profile) {
173
+ case 'restricted':
174
+ return {
175
+ runAsNonRoot: true,
176
+ runAsUser: 65534,
177
+ runAsGroup: 65534,
178
+ fsGroup: 65534,
179
+ seccompProfile: { type: 'RuntimeDefault' },
180
+ };
181
+ case 'baseline':
182
+ return {
183
+ runAsNonRoot: true,
184
+ runAsUser: 1000,
185
+ runAsGroup: 1000,
186
+ fsGroup: 1000,
187
+ };
188
+ case 'privileged':
189
+ return {};
190
+ default:
191
+ return getSecurityContextForProfile('baseline');
192
+ }
193
+ }
194
+ /**
195
+ * Applies container security based on profile
196
+ * @private
197
+ */
198
+ function applyContainerSecurity(container, profile) {
199
+ const baseContainer = {
200
+ name: container.name,
201
+ image: container.image,
202
+ };
203
+ if (container.port) {
204
+ baseContainer['ports'] = [{ containerPort: container.port }];
205
+ }
206
+ if (container.env) {
207
+ baseContainer['env'] = container.env;
208
+ }
209
+ if (container.resources) {
210
+ baseContainer['resources'] = container.resources;
211
+ }
212
+ if (container.volumeMounts) {
213
+ baseContainer['volumeMounts'] = container.volumeMounts;
214
+ }
215
+ if (container.command) {
216
+ baseContainer['command'] = container.command;
217
+ }
218
+ if (container.args) {
219
+ baseContainer['args'] = container.args;
220
+ }
221
+ if (container.livenessProbe) {
222
+ baseContainer['livenessProbe'] = container.livenessProbe;
223
+ }
224
+ if (container.readinessProbe) {
225
+ baseContainer['readinessProbe'] = container.readinessProbe;
226
+ }
227
+ if (container.startupProbe) {
228
+ baseContainer['startupProbe'] = container.startupProbe;
229
+ }
230
+ const securityContext = getContainerSecurityContext(profile);
231
+ const containerSecurityContext = { ...securityContext };
232
+ if (container.securityContext) {
233
+ Object.assign(containerSecurityContext, container.securityContext);
234
+ }
235
+ baseContainer['securityContext'] = containerSecurityContext;
236
+ return baseContainer;
237
+ }
238
+ /**
239
+ * Gets container security context for profile
240
+ * @private
241
+ */
242
+ function getContainerSecurityContext(profile) {
243
+ switch (profile) {
244
+ case 'restricted':
245
+ return {
246
+ allowPrivilegeEscalation: false,
247
+ readOnlyRootFilesystem: true,
248
+ runAsNonRoot: true,
249
+ capabilities: { drop: ['ALL'] },
250
+ seccompProfile: { type: 'RuntimeDefault' },
251
+ };
252
+ case 'baseline':
253
+ return {
254
+ allowPrivilegeEscalation: false,
255
+ readOnlyRootFilesystem: false,
256
+ runAsNonRoot: true,
257
+ capabilities: { drop: ['ALL'] },
258
+ };
259
+ case 'privileged':
260
+ return {};
261
+ default:
262
+ return getContainerSecurityContext('baseline');
263
+ }
264
+ }
265
+ /**
266
+ * Gets workload-specific configuration
267
+ * @private
268
+ */
269
+ function getWorkloadConfiguration(workloadType) {
270
+ switch (workloadType) {
271
+ case 'web':
272
+ return {
273
+ labels: { 'app.kubernetes.io/component': 'frontend' },
274
+ restartPolicy: 'Always',
275
+ };
276
+ case 'api':
277
+ return {
278
+ labels: { 'app.kubernetes.io/component': 'backend' },
279
+ restartPolicy: 'Always',
280
+ };
281
+ case 'worker':
282
+ return {
283
+ labels: { 'app.kubernetes.io/component': 'worker' },
284
+ restartPolicy: 'Always',
285
+ };
286
+ case 'database':
287
+ return {
288
+ labels: { 'app.kubernetes.io/component': 'database' },
289
+ restartPolicy: 'Always',
290
+ };
291
+ case 'cache':
292
+ return {
293
+ labels: { 'app.kubernetes.io/component': 'cache' },
294
+ restartPolicy: 'Always',
295
+ };
296
+ case 'batch':
297
+ return {
298
+ labels: { 'app.kubernetes.io/component': 'batch' },
299
+ restartPolicy: 'OnFailure',
300
+ };
301
+ default:
302
+ return {};
303
+ }
304
+ }
305
+ /**
306
+ * Creates HTTP probe configuration
307
+ * @private
308
+ */
309
+ function createHttpProbe(config, type) {
310
+ const defaults = {
311
+ liveness: {
312
+ initialDelaySeconds: 30,
313
+ periodSeconds: 10,
314
+ timeoutSeconds: 5,
315
+ failureThreshold: 3,
316
+ },
317
+ readiness: { initialDelaySeconds: 5, periodSeconds: 5, timeoutSeconds: 3, failureThreshold: 3 },
318
+ };
319
+ const defaultConfig = type === 'liveness' ? defaults.liveness : defaults.readiness;
320
+ return {
321
+ httpGet: {
322
+ path: config.httpPath || '/health',
323
+ port: config.port || 8080,
324
+ },
325
+ initialDelaySeconds: config.initialDelaySeconds || defaultConfig.initialDelaySeconds,
326
+ periodSeconds: config.periodSeconds || defaultConfig.periodSeconds,
327
+ timeoutSeconds: config.timeoutSeconds || defaultConfig.timeoutSeconds,
328
+ failureThreshold: config.failureThreshold || defaultConfig.failureThreshold,
329
+ };
330
+ }
331
+ //# sourceMappingURL=podTemplates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"podTemplates.js","sourceRoot":"","sources":["../../../src/lib/utils/podTemplates.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @fileoverview Resource naming utilities for Helm chart generation
3
+ * @since 2.5.0
4
+ */
5
+ /**
6
+ * Kubernetes resource object interface
7
+ * @interface KubernetesResource
8
+ * @since 2.5.0
9
+ */
10
+ export interface KubernetesResource {
11
+ /** Resource kind (e.g., Deployment, Service) */
12
+ kind?: string;
13
+ /** Resource metadata */
14
+ metadata?: {
15
+ /** Resource name */
16
+ name?: string;
17
+ /** Resource namespace */
18
+ namespace?: string;
19
+ };
20
+ }
21
+ /**
22
+ * Naming strategy for manifest files
23
+ * @since 2.5.0
24
+ */
25
+ export type NamingStrategy = 'descriptive' | 'numbered' | 'kind-only' | 'name-only';
26
+ /**
27
+ * Generates descriptive resource name from Kubernetes object
28
+ * @param resource - Kubernetes resource object
29
+ * @returns Descriptive name in format "kind-name"
30
+ * @since 2.5.0
31
+ */
32
+ export declare function getResourceName(resource: unknown): string;
33
+ /**
34
+ * Generates manifest file name based on strategy
35
+ * @param resource - Kubernetes resource object
36
+ * @param strategy - Naming strategy to use
37
+ * @param index - Resource index for numbered strategy
38
+ * @param prefix - Optional prefix for file names
39
+ * @returns Generated file name without extension
40
+ * @since 2.5.0
41
+ */
42
+ export declare function generateManifestName(resource: unknown, strategy?: NamingStrategy, index?: number, prefix?: string): string;
43
+ /**
44
+ * Sanitizes name for Kubernetes compatibility
45
+ * @param name - Raw name to sanitize
46
+ * @returns Kubernetes-compatible name
47
+ * @since 2.5.0
48
+ */
49
+ export declare function sanitizeKubernetesName(name: string): string;
50
+ /**
51
+ * Validates Kubernetes resource name
52
+ * @param name - Name to validate
53
+ * @returns True if valid, false otherwise
54
+ * @since 2.5.0
55
+ */
56
+ export declare function isValidKubernetesName(name: string): boolean;
57
+ //# sourceMappingURL=resourceNaming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resourceNaming.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/resourceNaming.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wBAAwB;IACxB,QAAQ,CAAC,EAAE;QACT,oBAAoB;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,yBAAyB;QACzB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB,aAAa,GACb,UAAU,GACV,WAAW,GACX,WAAW,CAAC;AAEhB;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAoBzD;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,OAAO,EACjB,QAAQ,GAAE,cAA8B,EACxC,KAAK,CAAC,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAyBR;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO3D;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAI3D"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @fileoverview Resource naming utilities for Helm chart generation
3
+ * @since 2.5.0
4
+ */
5
+ /**
6
+ * Generates descriptive resource name from Kubernetes object
7
+ * @param resource - Kubernetes resource object
8
+ * @returns Descriptive name in format "kind-name"
9
+ * @since 2.5.0
10
+ */
11
+ export function getResourceName(resource) {
12
+ if (!resource || typeof resource !== 'object') {
13
+ return 'resource';
14
+ }
15
+ const k8sResource = resource;
16
+ if (k8sResource.kind && k8sResource.metadata?.name) {
17
+ return `${k8sResource.kind.toLowerCase()}-${k8sResource.metadata.name}`;
18
+ }
19
+ if (k8sResource.kind) {
20
+ return k8sResource.kind.toLowerCase();
21
+ }
22
+ if (k8sResource.metadata?.name) {
23
+ return k8sResource.metadata.name;
24
+ }
25
+ return 'resource';
26
+ }
27
+ /**
28
+ * Generates manifest file name based on strategy
29
+ * @param resource - Kubernetes resource object
30
+ * @param strategy - Naming strategy to use
31
+ * @param index - Resource index for numbered strategy
32
+ * @param prefix - Optional prefix for file names
33
+ * @returns Generated file name without extension
34
+ * @since 2.5.0
35
+ */
36
+ export function generateManifestName(resource, strategy = 'descriptive', index, prefix) {
37
+ const k8sResource = resource;
38
+ switch (strategy) {
39
+ case 'descriptive': {
40
+ return getResourceName(resource);
41
+ }
42
+ case 'numbered': {
43
+ const paddedIndex = String(index ?? 1).padStart(3, '0');
44
+ return prefix ? `${prefix}-${paddedIndex}` : `manifests-${paddedIndex}`;
45
+ }
46
+ case 'kind-only': {
47
+ return k8sResource.kind?.toLowerCase() ?? 'resource';
48
+ }
49
+ case 'name-only': {
50
+ return k8sResource.metadata?.name ?? 'resource';
51
+ }
52
+ default: {
53
+ return getResourceName(resource);
54
+ }
55
+ }
56
+ }
57
+ /**
58
+ * Sanitizes name for Kubernetes compatibility
59
+ * @param name - Raw name to sanitize
60
+ * @returns Kubernetes-compatible name
61
+ * @since 2.5.0
62
+ */
63
+ export function sanitizeKubernetesName(name) {
64
+ return name
65
+ .toLowerCase()
66
+ .replace(/[^a-z0-9-]/g, '-')
67
+ .replace(/-+/g, '-')
68
+ .replace(/^-|-$/g, '')
69
+ .substring(0, 63);
70
+ }
71
+ /**
72
+ * Validates Kubernetes resource name
73
+ * @param name - Name to validate
74
+ * @returns True if valid, false otherwise
75
+ * @since 2.5.0
76
+ */
77
+ export function isValidKubernetesName(name) {
78
+ // eslint-disable-next-line security/detect-unsafe-regex -- Safe: simple DNS-1123 validation
79
+ const regex = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/;
80
+ return regex.test(name) && name.length <= 63;
81
+ }
82
+ //# sourceMappingURL=resourceNaming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resourceNaming.js","sourceRoot":"","sources":["../../../src/lib/utils/resourceNaming.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA6BH;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,QAAiB;IAC/C,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC9C,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,WAAW,GAAG,QAA8B,CAAC;IAEnD,IAAI,WAAW,CAAC,IAAI,IAAI,WAAW,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC;QACnD,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,WAAW,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC1E,CAAC;IAED,IAAI,WAAW,CAAC,IAAI,EAAE,CAAC;QACrB,OAAO,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IACxC,CAAC;IAED,IAAI,WAAW,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC/B,OAAO,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC;IACnC,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAiB,EACjB,WAA2B,aAAa,EACxC,KAAc,EACd,MAAe;IAEf,MAAM,WAAW,GAAG,QAA8B,CAAC;IAEnD,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,OAAO,eAAe,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC;QAED,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YACxD,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC,aAAa,WAAW,EAAE,CAAC;QAC1E,CAAC;QAED,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,OAAO,WAAW,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,UAAU,CAAC;QACvD,CAAC;QAED,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,OAAO,WAAW,CAAC,QAAQ,EAAE,IAAI,IAAI,UAAU,CAAC;QAClD,CAAC;QAED,OAAO,CAAC,CAAC,CAAC;YACR,OAAO,eAAe,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,OAAO,IAAI;SACR,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACtB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,4FAA4F;IAC5F,MAAM,KAAK,GAAG,iCAAiC,CAAC;IAChD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;AAC/C,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "timonel",
3
3
  "type": "module",
4
- "version": "2.4.0",
4
+ "version": "2.5.1",
5
5
  "description": "Timonel: programmatic Helm chart generator using cdk8s (TypeScript)",
6
6
  "bin": {
7
7
  "timonel": "dist/cli.js",
@@ -31,13 +31,18 @@
31
31
  "format": "prettier --write .",
32
32
  "format:check": "prettier --check .",
33
33
  "security:lint": "eslint .",
34
- "security:audit": "pnpm audit --audit-level=moderate",
34
+ "security:audit": "pnpm audit --audit-level=high",
35
35
  "security:check": "pnpm run security:lint && pnpm run security:audit",
36
36
  "deps:check": "pnpm outdated",
37
37
  "deps:update": "pnpm update --latest",
38
38
  "md:lint": "markdownlint \"**/*.md\"",
39
39
  "md:fix": "markdownlint --fix \"**/*.md\"",
40
- "test": "echo \"No tests specified\" && exit 0",
40
+ "test": "pnpm test:unit && pnpm test:integration",
41
+ "test:unit": "vitest run --config vitest.config.ts",
42
+ "test:integration": "vitest run --config vitest.integration.config.ts",
43
+ "test:watch": "vitest",
44
+ "test:coverage": "vitest run --coverage",
45
+ "test:basic-integration": "vitest run tests/integration/basic-chart-generation.int.spec.ts --config vitest.integration.config.ts",
41
46
  "ci:check": "pnpm typecheck && pnpm lint && pnpm format:check && pnpm build",
42
47
  "release": "semantic-release",
43
48
  "release:dry": "semantic-release --dry-run",
@@ -94,6 +99,7 @@
94
99
  "@types/node": "^24.3.0",
95
100
  "@typescript-eslint/eslint-plugin": "^8.42.0",
96
101
  "@typescript-eslint/parser": "^8.42.0",
102
+ "@vitest/coverage-v8": "^2.1.9",
97
103
  "conventional-changelog-cli": "^5.0.0",
98
104
  "eslint": "^9.35.0",
99
105
  "eslint-config-prettier": "^10.1.8",
@@ -108,7 +114,8 @@
108
114
  "markdownlint-cli": "^0.45.0",
109
115
  "prettier": "^3.6.2",
110
116
  "semantic-release": "^24.2.7",
111
- "typescript": "^5.9.2"
117
+ "typescript": "^5.9.2",
118
+ "vitest": "^2.1.9"
112
119
  },
113
120
  "files": [
114
121
  "dist",