@velajs/vela 1.8.4 → 1.8.7
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/dist/application.d.ts +11 -4
- package/dist/application.js +58 -10
- package/dist/container/container.js +12 -1
- package/dist/http/instantiate.js +30 -3
- package/dist/openapi/index.d.ts +3 -1
- package/dist/openapi/index.js +2 -0
- package/dist/openapi/redoc-ui.d.ts +1 -0
- package/dist/openapi/redoc-ui.js +19 -0
- package/dist/openapi/scalar-ui.d.ts +1 -1
- package/dist/openapi/scalar-ui.js +3 -2
- package/dist/openapi/swagger-ui.d.ts +1 -0
- package/dist/openapi/swagger-ui.js +23 -0
- package/dist/openapi/types.d.ts +18 -4
- package/package.json +27 -17
package/dist/application.d.ts
CHANGED
|
@@ -24,13 +24,20 @@ export declare class VelaApplication {
|
|
|
24
24
|
useGlobalInterceptors(...interceptors: InterceptorType[]): this;
|
|
25
25
|
useGlobalFilters(...filters: FilterType[]): this;
|
|
26
26
|
/**
|
|
27
|
-
* Serve a pre-built OpenAPI document
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
* Serve a pre-built OpenAPI document and one or more interactive doc UIs
|
|
28
|
+
* (Swagger UI, Scalar, ReDoc) on the underlying Hono app, mirroring the
|
|
29
|
+
* hono-crud docs convention. Edge-safe: each UI is a tiny self-contained
|
|
30
|
+
* HTML shell that loads its renderer from a CDN at runtime so nothing is
|
|
31
|
+
* bundled server-side and no extra dependency is required.
|
|
32
|
+
*
|
|
33
|
+
* Defaults: spec at `/openapi.json`, Scalar at `/scalar`, Swagger UI at
|
|
34
|
+
* `/docs`, ReDoc at `/redoc`. The deprecated `path` / `uiPath` aliases are
|
|
35
|
+
* retained for migration from the previous single-UI shape.
|
|
30
36
|
*
|
|
31
37
|
* ```ts
|
|
32
38
|
* const doc = createOpenApiDocument(AppModule);
|
|
33
|
-
* app.mountOpenApi({ document: doc
|
|
39
|
+
* app.mountOpenApi({ document: doc }); // Scalar @ /scalar
|
|
40
|
+
* app.mountOpenApi({ document: doc, ui: 'all' }); // swagger + scalar + redoc
|
|
34
41
|
* ```
|
|
35
42
|
*/
|
|
36
43
|
mountOpenApi(options: MountOpenApiOptions): this;
|
package/dist/application.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { hasBeforeApplicationShutdown, hasOnApplicationBootstrap, hasOnApplicationShutdown, hasOnModuleDestroy, hasOnModuleInit } from "./lifecycle/index.js";
|
|
2
2
|
import { renderScalarUi } from "./openapi/scalar-ui.js";
|
|
3
|
+
import { renderSwaggerUi } from "./openapi/swagger-ui.js";
|
|
4
|
+
import { renderRedocUi } from "./openapi/redoc-ui.js";
|
|
3
5
|
export class VelaApplication {
|
|
4
6
|
container;
|
|
5
7
|
routeManager;
|
|
@@ -54,23 +56,69 @@ export class VelaApplication {
|
|
|
54
56
|
return this;
|
|
55
57
|
}
|
|
56
58
|
/**
|
|
57
|
-
* Serve a pre-built OpenAPI document
|
|
58
|
-
*
|
|
59
|
-
*
|
|
59
|
+
* Serve a pre-built OpenAPI document and one or more interactive doc UIs
|
|
60
|
+
* (Swagger UI, Scalar, ReDoc) on the underlying Hono app, mirroring the
|
|
61
|
+
* hono-crud docs convention. Edge-safe: each UI is a tiny self-contained
|
|
62
|
+
* HTML shell that loads its renderer from a CDN at runtime so nothing is
|
|
63
|
+
* bundled server-side and no extra dependency is required.
|
|
64
|
+
*
|
|
65
|
+
* Defaults: spec at `/openapi.json`, Scalar at `/scalar`, Swagger UI at
|
|
66
|
+
* `/docs`, ReDoc at `/redoc`. The deprecated `path` / `uiPath` aliases are
|
|
67
|
+
* retained for migration from the previous single-UI shape.
|
|
60
68
|
*
|
|
61
69
|
* ```ts
|
|
62
70
|
* const doc = createOpenApiDocument(AppModule);
|
|
63
|
-
* app.mountOpenApi({ document: doc
|
|
71
|
+
* app.mountOpenApi({ document: doc }); // Scalar @ /scalar
|
|
72
|
+
* app.mountOpenApi({ document: doc, ui: 'all' }); // swagger + scalar + redoc
|
|
64
73
|
* ```
|
|
65
74
|
*/ mountOpenApi(options) {
|
|
66
75
|
const app = this.getApp();
|
|
67
|
-
const jsonPath = options.path ?? '/docs.json';
|
|
68
76
|
const doc = options.document;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
const title = options.title;
|
|
78
|
+
// Spec JSON: new default `/openapi.json`. The deprecated `path` alias is
|
|
79
|
+
// honored as an override only (does not change the default).
|
|
80
|
+
const specPath = options.specPath ?? options.path ?? '/openapi.json';
|
|
81
|
+
app.get(specPath, (c)=>c.json(doc));
|
|
82
|
+
// Normalize the requested UI set. Default is Scalar only.
|
|
83
|
+
const uiOption = options.ui;
|
|
84
|
+
let uis;
|
|
85
|
+
let singleStringUi = false;
|
|
86
|
+
if (uiOption === undefined) {
|
|
87
|
+
uis = [
|
|
88
|
+
'scalar'
|
|
89
|
+
];
|
|
90
|
+
} else if (uiOption === 'all') {
|
|
91
|
+
uis = [
|
|
92
|
+
'swagger',
|
|
93
|
+
'scalar',
|
|
94
|
+
'redoc'
|
|
95
|
+
];
|
|
96
|
+
} else if (Array.isArray(uiOption)) {
|
|
97
|
+
uis = uiOption;
|
|
98
|
+
} else {
|
|
99
|
+
uis = [
|
|
100
|
+
uiOption
|
|
101
|
+
];
|
|
102
|
+
singleStringUi = true;
|
|
103
|
+
}
|
|
104
|
+
for (const ui of uis){
|
|
105
|
+
// Back-compat: the old `{ ui: 'scalar', uiPath }` form mapped a single
|
|
106
|
+
// string UI to `uiPath`. Honor that only when exactly one UI was
|
|
107
|
+
// requested as a string.
|
|
108
|
+
const legacyPath = singleStringUi && uis.length === 1 ? options.uiPath : undefined;
|
|
109
|
+
if (ui === 'swagger') {
|
|
110
|
+
const path = legacyPath ?? options.swaggerPath ?? '/docs';
|
|
111
|
+
const html = renderSwaggerUi(specPath, title);
|
|
112
|
+
app.get(path, (c)=>c.html(html));
|
|
113
|
+
} else if (ui === 'redoc') {
|
|
114
|
+
const path = legacyPath ?? options.redocPath ?? '/redoc';
|
|
115
|
+
const html = renderRedocUi(specPath, title);
|
|
116
|
+
app.get(path, (c)=>c.html(html));
|
|
117
|
+
} else {
|
|
118
|
+
const path = legacyPath ?? options.scalarPath ?? '/scalar';
|
|
119
|
+
const html = renderScalarUi(specPath, title);
|
|
120
|
+
app.get(path, (c)=>c.html(html));
|
|
121
|
+
}
|
|
74
122
|
}
|
|
75
123
|
return this;
|
|
76
124
|
}
|
|
@@ -401,7 +401,18 @@ const IMPORT_TYPE_HINT = 'Did you use `import type { X }`? TypeScript strips typ
|
|
|
401
401
|
m.index,
|
|
402
402
|
m
|
|
403
403
|
]));
|
|
404
|
-
|
|
404
|
+
// Constructor arity. Normally `design:paramtypes` (emitDecoratorMetadata)
|
|
405
|
+
// gives the count, but some bundlers — notably esbuild (and therefore
|
|
406
|
+
// Wrangler) — do not emit it, leaving `paramTypes` empty even when
|
|
407
|
+
// `@Inject(token)` recorded explicit tokens. Fall back to the highest
|
|
408
|
+
// `@Inject` index so explicit-token constructors still resolve without
|
|
409
|
+
// emitted metadata. Slots with neither a param type nor an `@Inject` token
|
|
410
|
+
// still hit the unresolved-dependency error below.
|
|
411
|
+
const arity = Math.max(paramTypes.length, injectMetadata.reduce((max, m)=>Math.max(max, m.index + 1), 0));
|
|
412
|
+
const dependencies = Array.from({
|
|
413
|
+
length: arity
|
|
414
|
+
}, (_unused, index)=>{
|
|
415
|
+
const paramType = paramTypes[index];
|
|
405
416
|
const meta = injectMap.get(index);
|
|
406
417
|
const rawToken = meta?.token;
|
|
407
418
|
const isForwardRef = rawToken instanceof ForwardRef;
|
package/dist/http/instantiate.js
CHANGED
|
@@ -1,18 +1,45 @@
|
|
|
1
|
+
import { getConstructorDependencies, getInjectMetadata } from "../container/decorators.js";
|
|
1
2
|
// Resolve a class/token through the container if registered, otherwise treat
|
|
2
3
|
// the input as a plain instance. Used by RouteManager and HandlerExecutor to
|
|
3
4
|
// materialize middleware, guards, pipes, interceptors, and filters per request.
|
|
5
|
+
//
|
|
6
|
+
// When the input is a class that is NOT registered in the container, we fall
|
|
7
|
+
// back to `new clazz()` so plain parameterless helper classes (e.g. mixin
|
|
8
|
+
// guards, ad-hoc `@UseGuards(LocalGuard)` references) keep working. That
|
|
9
|
+
// fallback is unsafe for classes whose CONSTRUCTOR EXPECTS DEPENDENCIES,
|
|
10
|
+
// because zero-arg construction would leave every injected slot `undefined`
|
|
11
|
+
// — a silent failure mode that surfaces later as
|
|
12
|
+
// `Cannot read properties of undefined (reading '...')` deep inside the
|
|
13
|
+
// class. For those classes we throw a loud, actionable error instead.
|
|
14
|
+
//
|
|
15
|
+
// We treat "expects dependencies" as: any `@Inject(...)` parameter metadata
|
|
16
|
+
// OR a non-empty `design:paramtypes` (the SWC/TS emit for any constructor
|
|
17
|
+
// parameter). `@Injectable()` alone is NOT sufficient — `mixin()` and many
|
|
18
|
+
// parameterless guards are `@Injectable()` and still safe to `new` directly.
|
|
4
19
|
export function instantiate(classOrInstance, container) {
|
|
5
20
|
if (typeof classOrInstance === 'function') {
|
|
6
|
-
|
|
7
|
-
|
|
21
|
+
const clazz = classOrInstance;
|
|
22
|
+
if (container.has(clazz)) {
|
|
23
|
+
return container.resolve(clazz);
|
|
8
24
|
}
|
|
9
|
-
|
|
25
|
+
if (constructorExpectsDependencies(clazz)) {
|
|
26
|
+
throw new Error(`Cannot instantiate ${clazz.name}: the class declares constructor ` + `dependencies (\`@Inject(...)\` parameters or typed constructor ` + `parameters), but no matching provider is registered in the ` + `container. Add it to a module's providers (and export it if used ` + `outside its declaring module) instead of relying on the bare ` + `\`new\` fallback, which would construct the class without ` + `honouring its dependency-injection metadata and leave every ` + `injected field \`undefined\`.`);
|
|
27
|
+
}
|
|
28
|
+
return new clazz();
|
|
10
29
|
}
|
|
11
30
|
if (container.has(classOrInstance)) {
|
|
12
31
|
return container.resolve(classOrInstance);
|
|
13
32
|
}
|
|
14
33
|
return classOrInstance;
|
|
15
34
|
}
|
|
35
|
+
// True when calling `new clazz()` would leave an injected slot `undefined`:
|
|
36
|
+
// either the class has explicit `@Inject(...)` metadata, or it has any typed
|
|
37
|
+
// constructor parameter (TS/SWC's `design:paramtypes` emit). Parameterless
|
|
38
|
+
// `@Injectable()` classes return false — `new()` is safe for them.
|
|
39
|
+
function constructorExpectsDependencies(clazz) {
|
|
40
|
+
if (getInjectMetadata(clazz).length > 0) return true;
|
|
41
|
+
return getConstructorDependencies(clazz).length > 0;
|
|
42
|
+
}
|
|
16
43
|
export function instantiateMany(items, container) {
|
|
17
44
|
return items.map((item)=>instantiate(item, container));
|
|
18
45
|
}
|
package/dist/openapi/index.d.ts
CHANGED
|
@@ -2,4 +2,6 @@ export { createOpenApiDocument } from './document';
|
|
|
2
2
|
export { ApiDoc, ApiTags, ApiResponse, API_DOC_METADATA, API_TAGS_METADATA, API_RESPONSES_METADATA, } from './decorators';
|
|
3
3
|
export { zodToJsonSchema } from './zod-to-json-schema';
|
|
4
4
|
export { renderScalarUi } from './scalar-ui';
|
|
5
|
-
export
|
|
5
|
+
export { renderSwaggerUi } from './swagger-ui';
|
|
6
|
+
export { renderRedocUi } from './redoc-ui';
|
|
7
|
+
export type { ApiDocMetadata, ApiResponseEntry, ApiResponseOptions, CreateOpenApiDocumentOptions, HttpVerb, JsonSchema, MountOpenApiOptions, OpenApiUi, OpenApiDocument, OpenApiInfo, OpenApiOperation, OpenApiParameter, OpenApiPathItem, OpenApiRequestBody, OpenApiResponse, } from './types';
|
package/dist/openapi/index.js
CHANGED
|
@@ -2,3 +2,5 @@ export { createOpenApiDocument } from "./document.js";
|
|
|
2
2
|
export { ApiDoc, ApiTags, ApiResponse, API_DOC_METADATA, API_TAGS_METADATA, API_RESPONSES_METADATA } from "./decorators.js";
|
|
3
3
|
export { zodToJsonSchema } from "./zod-to-json-schema.js";
|
|
4
4
|
export { renderScalarUi } from "./scalar-ui.js";
|
|
5
|
+
export { renderSwaggerUi } from "./swagger-ui.js";
|
|
6
|
+
export { renderRedocUi } from "./redoc-ui.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function renderRedocUi(specUrl: string, title?: string): string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Minimal HTML shell that bootstraps ReDoc from a CDN. Served as a static
|
|
2
|
+
// string so it works on every edge runtime with no Node or bundler deps —
|
|
3
|
+
// ReDoc itself is loaded from a CDN at runtime.
|
|
4
|
+
export function renderRedocUi(specUrl, title) {
|
|
5
|
+
const safeUrl = specUrl.replace(/"/g, '"');
|
|
6
|
+
const safeTitle = (title ?? 'API Reference').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
7
|
+
return `<!doctype html>
|
|
8
|
+
<html>
|
|
9
|
+
<head>
|
|
10
|
+
<title>${safeTitle}</title>
|
|
11
|
+
<meta charset="utf-8" />
|
|
12
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<redoc spec-url="${safeUrl}"></redoc>
|
|
16
|
+
<script src="https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js"></script>
|
|
17
|
+
</body>
|
|
18
|
+
</html>`;
|
|
19
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function renderScalarUi(jsonUrl: string): string;
|
|
1
|
+
export declare function renderScalarUi(jsonUrl: string, title?: string): string;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// Minimal HTML shell that bootstraps Scalar's hosted API-reference UI.
|
|
2
2
|
// Served as a static string so it works on every edge runtime with no
|
|
3
3
|
// Node or bundler deps. Scalar itself is loaded from a CDN at runtime.
|
|
4
|
-
export function renderScalarUi(jsonUrl) {
|
|
4
|
+
export function renderScalarUi(jsonUrl, title) {
|
|
5
5
|
const safeUrl = jsonUrl.replace(/"/g, '"');
|
|
6
|
+
const safeTitle = (title ?? 'API Reference').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
6
7
|
return `<!doctype html>
|
|
7
8
|
<html>
|
|
8
9
|
<head>
|
|
9
|
-
<title
|
|
10
|
+
<title>${safeTitle}</title>
|
|
10
11
|
<meta charset="utf-8" />
|
|
11
12
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
12
13
|
</head>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function renderSwaggerUi(specUrl: string, title?: string): string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Minimal HTML shell that bootstraps Swagger UI from a CDN. Served as a
|
|
2
|
+
// static string so it works on every edge runtime with no Node or bundler
|
|
3
|
+
// deps — Swagger UI itself is loaded from a CDN at runtime.
|
|
4
|
+
export function renderSwaggerUi(specUrl, title) {
|
|
5
|
+
const safeUrl = specUrl.replace(/"/g, '"');
|
|
6
|
+
const safeTitle = (title ?? 'API Reference').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
7
|
+
return `<!doctype html>
|
|
8
|
+
<html>
|
|
9
|
+
<head>
|
|
10
|
+
<title>${safeTitle}</title>
|
|
11
|
+
<meta charset="utf-8" />
|
|
12
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
13
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist/swagger-ui.css" />
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<div id="swagger-ui"></div>
|
|
17
|
+
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist/swagger-ui-bundle.js"></script>
|
|
18
|
+
<script>
|
|
19
|
+
window.ui = SwaggerUIBundle({ url: "${safeUrl}", dom_id: '#swagger-ui' });
|
|
20
|
+
</script>
|
|
21
|
+
</body>
|
|
22
|
+
</html>`;
|
|
23
|
+
}
|
package/dist/openapi/types.d.ts
CHANGED
|
@@ -102,13 +102,27 @@ export interface CreateOpenApiDocumentOptions {
|
|
|
102
102
|
description?: string;
|
|
103
103
|
}>;
|
|
104
104
|
}
|
|
105
|
+
export type OpenApiUi = 'swagger' | 'scalar' | 'redoc';
|
|
105
106
|
export interface MountOpenApiOptions {
|
|
106
107
|
/** Pre-built OpenAPI document to serve. */
|
|
107
108
|
document: OpenApiDocument;
|
|
108
|
-
/**
|
|
109
|
+
/** OpenAPI JSON spec path. @default '/openapi.json' */
|
|
110
|
+
specPath?: string;
|
|
111
|
+
/**
|
|
112
|
+
* UI(s) to mount. 'all' mounts swagger + scalar + redoc.
|
|
113
|
+
* @default 'scalar'
|
|
114
|
+
*/
|
|
115
|
+
ui?: OpenApiUi | OpenApiUi[] | 'all';
|
|
116
|
+
/** Swagger UI path. @default '/docs' */
|
|
117
|
+
swaggerPath?: string;
|
|
118
|
+
/** Scalar path. @default '/scalar' */
|
|
119
|
+
scalarPath?: string;
|
|
120
|
+
/** ReDoc path. @default '/redoc' */
|
|
121
|
+
redocPath?: string;
|
|
122
|
+
/** Page title for the UIs. */
|
|
123
|
+
title?: string;
|
|
124
|
+
/** @deprecated use `specPath`. Back-compat alias; if set, overrides specPath default. */
|
|
109
125
|
path?: string;
|
|
110
|
-
/**
|
|
111
|
-
ui?: 'scalar';
|
|
112
|
-
/** Path the UI is served at when `ui` is set. Default `/docs`. */
|
|
126
|
+
/** @deprecated single-UI path override; applies to the single `ui` when a string. */
|
|
113
127
|
uiPath?: string;
|
|
114
128
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velajs/vela",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.7",
|
|
4
4
|
"description": "NestJS-compatible framework for edge runtimes, powered by Hono",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -29,6 +29,14 @@
|
|
|
29
29
|
"LICENSE",
|
|
30
30
|
"CHANGELOG.md"
|
|
31
31
|
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "rm -rf dist && swc src -d dist --strip-leading-paths && tsc --emitDeclarationOnly",
|
|
34
|
+
"test": "vitest run",
|
|
35
|
+
"test:workers": "vitest run --config vitest.config.workers.ts",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"prepare": "swc src -d dist --strip-leading-paths && tsc --emitDeclarationOnly",
|
|
38
|
+
"prepublishOnly": "pnpm run typecheck && pnpm test"
|
|
39
|
+
},
|
|
32
40
|
"sideEffects": [
|
|
33
41
|
"./dist/index.js"
|
|
34
42
|
],
|
|
@@ -58,24 +66,26 @@
|
|
|
58
66
|
"node": ">=20"
|
|
59
67
|
},
|
|
60
68
|
"dependencies": {
|
|
61
|
-
"hono": "^4"
|
|
69
|
+
"hono": "^4.12.26"
|
|
70
|
+
},
|
|
71
|
+
"packageManager": "pnpm@10.20.0",
|
|
72
|
+
"pnpm": {
|
|
73
|
+
"onlyBuiltDependencies": [
|
|
74
|
+
"@swc/core",
|
|
75
|
+
"esbuild",
|
|
76
|
+
"workerd"
|
|
77
|
+
]
|
|
62
78
|
},
|
|
63
79
|
"devDependencies": {
|
|
64
|
-
"@cloudflare/vitest-pool-workers": "^0.
|
|
65
|
-
"@swc/cli": "^0.8.
|
|
66
|
-
"@swc/core": "^1.15.
|
|
80
|
+
"@cloudflare/vitest-pool-workers": "^0.16.18",
|
|
81
|
+
"@swc/cli": "^0.8.1",
|
|
82
|
+
"@swc/core": "^1.15.41",
|
|
67
83
|
"@types/bun": "latest",
|
|
68
|
-
"@vitest/runner": "^4.1.
|
|
69
|
-
"@vitest/snapshot": "^4.1.
|
|
70
|
-
"typescript": "^
|
|
84
|
+
"@vitest/runner": "^4.1.9",
|
|
85
|
+
"@vitest/snapshot": "^4.1.9",
|
|
86
|
+
"typescript": "^6.0.3",
|
|
71
87
|
"unplugin-swc": "^1.5.9",
|
|
72
|
-
"vitest": "^4.1.
|
|
73
|
-
"zod": "^4.3
|
|
74
|
-
},
|
|
75
|
-
"scripts": {
|
|
76
|
-
"build": "rm -rf dist && swc src -d dist --strip-leading-paths && tsc --emitDeclarationOnly",
|
|
77
|
-
"test": "vitest run",
|
|
78
|
-
"test:workers": "vitest run --config vitest.config.workers.ts",
|
|
79
|
-
"typecheck": "tsc --noEmit"
|
|
88
|
+
"vitest": "^4.1.9",
|
|
89
|
+
"zod": "^4.4.3"
|
|
80
90
|
}
|
|
81
|
-
}
|
|
91
|
+
}
|