vovk 3.7.0 → 4.0.0-beta.0
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/CHANGELOG.md +74 -0
- package/dist/client/create-rpc.d.ts +3 -3
- package/dist/client/create-rpc.js +6 -8
- package/dist/client/default-stream-handler.d.ts +1 -5
- package/dist/client/default-stream-handler.js +53 -45
- package/dist/client/fetcher.d.ts +1 -1
- package/dist/client/fetcher.js +53 -4
- package/dist/client/progressive.d.ts +1 -5
- package/dist/client/progressive.js +19 -9
- package/dist/client/serialize-query.d.ts +0 -11
- package/dist/client/serialize-query.js +4 -28
- package/dist/core/controllers-to-static-params.d.ts +1 -2
- package/dist/core/controllers-to-static-params.js +1 -2
- package/dist/core/create-decorator.d.ts +1 -1
- package/dist/core/decorate.d.ts +2 -6
- package/dist/core/decorators.js +3 -3
- package/dist/core/get-schema.d.ts +1 -1
- package/dist/core/get-schema.js +1 -3
- package/dist/core/http-exception.d.ts +1 -1
- package/dist/core/http-exception.js +1 -1
- package/dist/core/init-segment.js +1 -1
- package/dist/core/json-lines-responder.d.ts +8 -12
- package/dist/core/json-lines-responder.js +42 -20
- package/dist/core/vovk-app.d.ts +1 -1
- package/dist/core/vovk-app.js +62 -16
- package/dist/index.d.ts +16 -18
- package/dist/index.js +14 -15
- package/dist/internal.d.ts +14 -12
- package/dist/internal.js +8 -7
- package/dist/openapi/error.js +2 -2
- package/dist/openapi/openapi-to-vovk-schema/apply-components-schemas.d.ts +1 -12
- package/dist/openapi/openapi-to-vovk-schema/apply-components-schemas.js +4 -10
- package/dist/openapi/openapi-to-vovk-schema/index.d.ts +1 -1
- package/dist/openapi/openapi-to-vovk-schema/index.js +20 -6
- package/dist/openapi/openapi-to-vovk-schema/inline-refs.d.ts +0 -7
- package/dist/openapi/openapi-to-vovk-schema/inline-refs.js +2 -13
- package/dist/openapi/openapi-to-vovk-schema/prune-components-schemas.d.ts +0 -5
- package/dist/openapi/openapi-to-vovk-schema/prune-components-schemas.js +3 -8
- package/dist/openapi/vovk-schema-to-openapi.d.ts +1 -1
- package/dist/openapi/vovk-schema-to-openapi.js +9 -3
- package/dist/req/parse-query.d.ts +0 -23
- package/dist/req/parse-query.js +51 -43
- package/dist/req/validate-content-type.js +1 -1
- package/dist/samples/create-code-samples.d.ts +1 -1
- package/dist/samples/create-code-samples.js +4 -3
- package/dist/samples/schema-to-code.d.ts +1 -1
- package/dist/samples/schema-to-code.js +18 -15
- package/dist/samples/schema-to-object.d.ts +1 -1
- package/dist/samples/schema-to-object.js +18 -15
- package/dist/tools/create-tool-factory.d.ts +2 -2
- package/dist/tools/derive-tools.d.ts +9 -13
- package/dist/tools/derive-tools.js +48 -47
- package/dist/tools/to-model-output-default.d.ts +2 -2
- package/dist/tools/to-model-output-default.js +20 -6
- package/dist/tools/to-model-output-mcp.d.ts +3 -2
- package/dist/tools/to-model-output.d.ts +1 -1
- package/dist/types/client.d.ts +4 -4
- package/dist/types/config.d.ts +5 -6
- package/dist/types/core.d.ts +4 -1
- package/dist/types/enums.d.ts +1 -1
- package/dist/types/enums.js +1 -1
- package/dist/types/inference.d.ts +2 -3
- package/dist/types/request.d.ts +1 -5
- package/dist/types/standard-schema.js +0 -2
- package/dist/types/standard-tool.d.ts +8 -12
- package/dist/types/tools.d.ts +2 -40
- package/dist/types/validation.d.ts +3 -3
- package/dist/utils/camel-case.d.ts +0 -5
- package/dist/utils/camel-case.js +2 -10
- package/dist/utils/deep-extend.d.ts +1 -10
- package/dist/utils/deep-extend.js +16 -4
- package/dist/validation/create-standard-validation.d.ts +3 -126
- package/dist/validation/create-standard-validation.js +2 -3
- package/dist/validation/create-validate-on-client.d.ts +3 -3
- package/dist/validation/json-schema-only-spec.d.ts +14 -0
- package/dist/validation/json-schema-only-spec.js +57 -0
- package/dist/validation/procedure.d.ts +1 -124
- package/dist/validation/validation-schemas-object-to-single-validation-schema.d.ts +0 -9
- package/dist/validation/validation-schemas-object-to-single-validation-schema.js +11 -14
- package/dist/validation/with-validation-library.d.ts +2 -2
- package/dist/validation/with-validation-library.js +11 -5
- package/package.json +3 -10
|
@@ -4,10 +4,7 @@ export declare abstract class Responder {
|
|
|
4
4
|
response: Response;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
* @see https://vovk.dev/jsonlines
|
|
9
|
-
* @param request The incoming Request object.
|
|
10
|
-
* @param getResponse Optional function to create a custom Response object.
|
|
7
|
+
* Responder subclass for streaming JSON Lines. @see https://vovk.dev/jsonlines
|
|
11
8
|
* @example
|
|
12
9
|
* ```ts
|
|
13
10
|
* import { JSONLinesResponder } from 'vovk';
|
|
@@ -16,19 +13,18 @@ export declare abstract class Responder {
|
|
|
16
13
|
* return new Response(responder.readableStream, { headers: responder.headers });
|
|
17
14
|
* });
|
|
18
15
|
*
|
|
19
|
-
* //
|
|
20
|
-
* responder.
|
|
21
|
-
* // Close the stream when done
|
|
22
|
-
* responder.close();
|
|
23
|
-
* // Or throw an error
|
|
16
|
+
* responder.send({ ... }); // send items
|
|
17
|
+
* responder.close(); // close the stream when done
|
|
24
18
|
* responder.throw(new Error('Something went wrong'));
|
|
25
|
-
* // get the Response object, headers, etc.
|
|
26
19
|
* const { response, headers } = responder;
|
|
27
20
|
* ```
|
|
28
21
|
*/
|
|
29
22
|
export declare class JSONLinesResponder<T> extends Responder {
|
|
30
23
|
private isClosed;
|
|
31
24
|
private i;
|
|
25
|
+
private pendingSends;
|
|
26
|
+
private sendQueue;
|
|
27
|
+
private hasSent;
|
|
32
28
|
private controller?;
|
|
33
29
|
private readonly encoder;
|
|
34
30
|
readonly readableStream: ReadableStream | null;
|
|
@@ -37,6 +33,6 @@ export declare class JSONLinesResponder<T> extends Responder {
|
|
|
37
33
|
constructor(request?: Request | null, getResponse?: (responder: JSONLinesResponder<T>) => Response);
|
|
38
34
|
readonly send: (item: T) => Promise<void>;
|
|
39
35
|
sendLineOrError: (data: T | StreamAbortMessage) => void;
|
|
40
|
-
readonly close: () => void
|
|
41
|
-
readonly throw: (e: unknown) => void
|
|
36
|
+
readonly close: () => Promise<void>;
|
|
37
|
+
readonly throw: (e: unknown) => Promise<void>;
|
|
42
38
|
}
|
|
@@ -3,10 +3,7 @@ export class Responder {
|
|
|
3
3
|
response;
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* @see https://vovk.dev/jsonlines
|
|
8
|
-
* @param request The incoming Request object.
|
|
9
|
-
* @param getResponse Optional function to create a custom Response object.
|
|
6
|
+
* Responder subclass for streaming JSON Lines. @see https://vovk.dev/jsonlines
|
|
10
7
|
* @example
|
|
11
8
|
* ```ts
|
|
12
9
|
* import { JSONLinesResponder } from 'vovk';
|
|
@@ -15,19 +12,19 @@ export class Responder {
|
|
|
15
12
|
* return new Response(responder.readableStream, { headers: responder.headers });
|
|
16
13
|
* });
|
|
17
14
|
*
|
|
18
|
-
* //
|
|
19
|
-
* responder.
|
|
20
|
-
* // Close the stream when done
|
|
21
|
-
* responder.close();
|
|
22
|
-
* // Or throw an error
|
|
15
|
+
* responder.send({ ... }); // send items
|
|
16
|
+
* responder.close(); // close the stream when done
|
|
23
17
|
* responder.throw(new Error('Something went wrong'));
|
|
24
|
-
* // get the Response object, headers, etc.
|
|
25
18
|
* const { response, headers } = responder;
|
|
26
19
|
* ```
|
|
27
20
|
*/
|
|
28
21
|
export class JSONLinesResponder extends Responder {
|
|
29
22
|
isClosed = false;
|
|
30
23
|
i = 0;
|
|
24
|
+
pendingSends = new Set();
|
|
25
|
+
// sends are chained so unawaited calls keep their order, see send()
|
|
26
|
+
sendQueue = Promise.resolve();
|
|
27
|
+
hasSent = false;
|
|
31
28
|
controller;
|
|
32
29
|
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: biome bug
|
|
33
30
|
encoder;
|
|
@@ -63,15 +60,28 @@ export class JSONLinesResponder extends Responder {
|
|
|
63
60
|
this.controller?.enqueue(encoder?.encode(''));
|
|
64
61
|
}
|
|
65
62
|
send = async (item) => {
|
|
63
|
+
// chaining keeps lines in call order even when send() is not awaited
|
|
64
|
+
const promise = this.sendQueue.then(async () => {
|
|
65
|
+
try {
|
|
66
|
+
if (!this.hasSent) {
|
|
67
|
+
this.hasSent = true;
|
|
68
|
+
// zero timeout lets withValidationLibrary set onBeforeSend before the first send,
|
|
69
|
+
// otherwise immediate streaming would skip the first iteration validation
|
|
70
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
71
|
+
}
|
|
72
|
+
this.sendLineOrError(await this.onBeforeSend(item, this.i++));
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
this.throw(e);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
this.sendQueue = promise;
|
|
79
|
+
this.pendingSends.add(promise);
|
|
66
80
|
try {
|
|
67
|
-
|
|
68
|
-
// in case if data is streamed immediately in a controller/service, we're going to lose the first iteration validation
|
|
69
|
-
// the await with zero timeout ensures onBeforeSend is set before the first send
|
|
70
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
71
|
-
this.sendLineOrError(await this.onBeforeSend(item, this.i++));
|
|
81
|
+
await promise;
|
|
72
82
|
}
|
|
73
|
-
|
|
74
|
-
this.
|
|
83
|
+
finally {
|
|
84
|
+
this.pendingSends.delete(promise);
|
|
75
85
|
}
|
|
76
86
|
};
|
|
77
87
|
sendLineOrError = (data) => {
|
|
@@ -80,14 +90,26 @@ export class JSONLinesResponder extends Responder {
|
|
|
80
90
|
return;
|
|
81
91
|
controller?.enqueue(encoder?.encode(`${JSON.stringify(data)}\n`));
|
|
82
92
|
};
|
|
83
|
-
close = () => {
|
|
84
|
-
|
|
93
|
+
close = async () => {
|
|
94
|
+
if (this.isClosed)
|
|
95
|
+
return;
|
|
96
|
+
// let unawaited send() calls finish first, per the documented send-then-close pattern
|
|
97
|
+
while (this.pendingSends.size) {
|
|
98
|
+
await Promise.allSettled([...this.pendingSends]);
|
|
99
|
+
}
|
|
85
100
|
if (this.isClosed)
|
|
86
101
|
return;
|
|
87
102
|
this.isClosed = true;
|
|
88
|
-
controller?.close();
|
|
103
|
+
this.controller?.close();
|
|
89
104
|
};
|
|
90
105
|
throw = (e) => {
|
|
106
|
+
// same rule as a non streaming handler, an error without a statusCode is internal
|
|
107
|
+
const isExpected = typeof e?.statusCode === 'number';
|
|
108
|
+
if (!isExpected && process.env.NODE_ENV === 'production') {
|
|
109
|
+
console.error('🐺 Unhandled error in a Vovk stream:', e);
|
|
110
|
+
this.sendLineOrError({ isError: true, reason: 'Internal server error' });
|
|
111
|
+
return this.close();
|
|
112
|
+
}
|
|
91
113
|
this.sendLineOrError({ isError: true, reason: e instanceof Error ? e.message : e });
|
|
92
114
|
return this.close();
|
|
93
115
|
};
|
package/dist/core/vovk-app.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { DecoratorOptions, RouteHandler, VovkController } from '../types/core.js';
|
|
1
2
|
import { HttpMethod, HttpStatus } from '../types/enums.js';
|
|
2
|
-
import type { RouteHandler, VovkController, DecoratorOptions } from '../types/core.js';
|
|
3
3
|
declare class VovkApp {
|
|
4
4
|
#private;
|
|
5
5
|
private static getHeadersFromDecoratorOptions;
|
package/dist/core/vovk-app.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
var _a;
|
|
2
|
-
import {
|
|
3
|
-
import { JSONLinesResponder, Responder } from './json-lines-responder.js';
|
|
4
|
-
import { reqQuery } from '../req/req-query.js';
|
|
2
|
+
import { parseBody } from '../req/parse-body.js';
|
|
5
3
|
import { reqMeta } from '../req/req-meta.js';
|
|
4
|
+
import { reqQuery } from '../req/req-query.js';
|
|
6
5
|
import { HttpMethod, HttpStatus } from '../types/enums.js';
|
|
7
|
-
import {
|
|
6
|
+
import { HttpException } from './http-exception.js';
|
|
7
|
+
import { JSONLinesResponder, Responder } from './json-lines-responder.js';
|
|
8
8
|
class VovkApp {
|
|
9
9
|
static getHeadersFromDecoratorOptions(options) {
|
|
10
10
|
if (!options)
|
|
@@ -12,7 +12,8 @@ class VovkApp {
|
|
|
12
12
|
const corsHeaders = {
|
|
13
13
|
'access-control-allow-origin': '*',
|
|
14
14
|
'access-control-allow-methods': 'GET, POST, PUT, DELETE, OPTIONS, HEAD',
|
|
15
|
-
|
|
15
|
+
// x-meta is ours, the client sends it whenever meta is set
|
|
16
|
+
'access-control-allow-headers': 'content-type, authorization, x-meta',
|
|
16
17
|
};
|
|
17
18
|
const headers = {
|
|
18
19
|
...(options.cors ? corsHeaders : {}),
|
|
@@ -62,23 +63,29 @@ class VovkApp {
|
|
|
62
63
|
#routeRegexCache = new Map();
|
|
63
64
|
#routeSegmentsCache = new Map();
|
|
64
65
|
#routeParamPositionsCache = new Map();
|
|
65
|
-
|
|
66
|
+
// matches are only valid for the handlers map they were resolved against, so scope by its identity
|
|
67
|
+
#routeMatchCache = new WeakMap();
|
|
68
|
+
// concrete paths come from the URL, cap the per handlers map cache so it can't grow forever
|
|
69
|
+
static #ROUTE_MATCH_CACHE_LIMIT = 1000;
|
|
66
70
|
#getHandler = ({ handlers, path, params, }) => {
|
|
67
71
|
let methodParams = {};
|
|
68
72
|
if (Object.keys(params).length === 0) {
|
|
69
73
|
return { handler: handlers[''], methodParams };
|
|
70
74
|
}
|
|
75
|
+
// a decoded "/" inside one segment makes the joined path ambiguous, /files/a%2Fb vs /files/a/b
|
|
76
|
+
const hasEncodedSlash = path.some((segment) => segment.includes('/'));
|
|
71
77
|
const pathStr = path.join('/');
|
|
72
78
|
// Fast path: Check if this exact path has been matched before
|
|
73
|
-
|
|
79
|
+
let matchCache = hasEncodedSlash ? undefined : this.#routeMatchCache.get(handlers);
|
|
80
|
+
const cachedMatch = matchCache?.get(pathStr);
|
|
74
81
|
if (cachedMatch) {
|
|
75
82
|
return {
|
|
76
83
|
handler: handlers[cachedMatch.route],
|
|
77
84
|
methodParams: cachedMatch.params,
|
|
78
85
|
};
|
|
79
86
|
}
|
|
80
|
-
// Check for direct static route match
|
|
81
|
-
let methodKey = handlers
|
|
87
|
+
// Check for direct static route match, hasOwn so /toString doesn't resolve a prototype member
|
|
88
|
+
let methodKey = !hasEncodedSlash && Object.hasOwn(handlers, pathStr) ? pathStr : null;
|
|
82
89
|
if (!methodKey) {
|
|
83
90
|
const methodKeys = [];
|
|
84
91
|
const pathLength = path.length;
|
|
@@ -140,7 +147,8 @@ class VovkApp {
|
|
|
140
147
|
if (!regex) {
|
|
141
148
|
const regexPattern = routeSegment
|
|
142
149
|
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
143
|
-
|
|
150
|
+
// matched per segment, so a decoded "/" from %2F belongs to the value
|
|
151
|
+
.replace(/\\{(\w+)\\}/g, '(?<$1>[\\s\\S]+)');
|
|
144
152
|
regex = new RegExp(`^${regexPattern}$`);
|
|
145
153
|
this.#routeRegexCache.set(routeSegment, regex);
|
|
146
154
|
}
|
|
@@ -177,9 +185,16 @@ class VovkApp {
|
|
|
177
185
|
throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR, `Conflicting routes found: ${methodKeys.join(', ')}`);
|
|
178
186
|
}
|
|
179
187
|
[methodKey] = methodKeys;
|
|
180
|
-
// Cache successful matches
|
|
181
|
-
if (methodKey) {
|
|
182
|
-
|
|
188
|
+
// Cache successful matches, an ambiguous joined path must not become a cache key
|
|
189
|
+
if (methodKey && !hasEncodedSlash) {
|
|
190
|
+
if (!matchCache) {
|
|
191
|
+
matchCache = new Map();
|
|
192
|
+
this.#routeMatchCache.set(handlers, matchCache);
|
|
193
|
+
}
|
|
194
|
+
if (matchCache.size >= _a.#ROUTE_MATCH_CACHE_LIMIT) {
|
|
195
|
+
matchCache.delete(matchCache.keys().next().value);
|
|
196
|
+
}
|
|
197
|
+
matchCache.set(pathStr, { route: methodKey, params: methodParams });
|
|
183
198
|
}
|
|
184
199
|
}
|
|
185
200
|
if (methodKey) {
|
|
@@ -217,7 +232,20 @@ class VovkApp {
|
|
|
217
232
|
headerList = null;
|
|
218
233
|
}
|
|
219
234
|
const xMeta = headerList?.get('x-meta');
|
|
220
|
-
|
|
235
|
+
let xMetaHeader = null;
|
|
236
|
+
if (xMeta) {
|
|
237
|
+
try {
|
|
238
|
+
xMetaHeader = JSON.parse(xMeta);
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
// malformed client input is a 400, not an uncaught SyntaxError
|
|
242
|
+
return this.#respondWithError({
|
|
243
|
+
req,
|
|
244
|
+
statusCode: HttpStatus.BAD_REQUEST,
|
|
245
|
+
message: 'Invalid x-meta request header',
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
221
249
|
if (xMetaHeader)
|
|
222
250
|
reqMeta(req, { xMetaHeader });
|
|
223
251
|
const { handler, methodParams } = this.#getHandler({ handlers, path, params });
|
|
@@ -240,7 +268,8 @@ class VovkApp {
|
|
|
240
268
|
try {
|
|
241
269
|
await staticMethod._options?.before?.call(controller, req);
|
|
242
270
|
await onBefore?.(req);
|
|
243
|
-
|
|
271
|
+
// dispatch via the latest wrapper so decorators applied above the HTTP decorator still run
|
|
272
|
+
const result = await (staticMethod._sourceMethod?.wrapper ?? staticMethod).call(controller, req, methodParams);
|
|
244
273
|
if (result instanceof Response) {
|
|
245
274
|
await onSuccess?.(result, req);
|
|
246
275
|
// set headers from decorator options
|
|
@@ -279,6 +308,13 @@ class VovkApp {
|
|
|
279
308
|
}
|
|
280
309
|
}
|
|
281
310
|
catch (e) {
|
|
311
|
+
// the outer catch already returned the response, so onError has to run here
|
|
312
|
+
try {
|
|
313
|
+
await controller._onError?.(e, req);
|
|
314
|
+
}
|
|
315
|
+
catch (onErrorError) {
|
|
316
|
+
console.error('An error caught in onError handler:', onErrorError);
|
|
317
|
+
}
|
|
282
318
|
return responder.throw(e);
|
|
283
319
|
}
|
|
284
320
|
return responder.close();
|
|
@@ -296,11 +332,21 @@ class VovkApp {
|
|
|
296
332
|
await controller._onError?.(err, req);
|
|
297
333
|
}
|
|
298
334
|
catch (onErrorError) {
|
|
299
|
-
// eslint-disable-next-line no-console
|
|
300
335
|
console.error('An error caught in onError handler:', onErrorError);
|
|
301
336
|
}
|
|
302
337
|
if (err.message !== 'NEXT_REDIRECT' && err.message !== 'NEXT_NOT_FOUND') {
|
|
303
338
|
const statusCode = err.statusCode || HttpStatus.INTERNAL_SERVER_ERROR;
|
|
339
|
+
// an error without a statusCode is internal, its message and cause stay on the server in production
|
|
340
|
+
const isExpected = typeof err.statusCode === 'number';
|
|
341
|
+
if (!isExpected && process.env.NODE_ENV === 'production') {
|
|
342
|
+
console.error('🐺 Unhandled error in a Vovk handler:', err);
|
|
343
|
+
return this.#respondWithError({
|
|
344
|
+
req,
|
|
345
|
+
statusCode,
|
|
346
|
+
message: 'Internal server error',
|
|
347
|
+
options: staticMethod._options,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
304
350
|
return this.#respondWithError({
|
|
305
351
|
req,
|
|
306
352
|
statusCode,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,26 +1,24 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { createFetcher, fetcher } from './client/fetcher.js';
|
|
2
|
+
export { progressive } from './client/progressive.js';
|
|
3
3
|
export { controllersToStaticParams } from './core/controllers-to-static-params.js';
|
|
4
|
-
export {
|
|
5
|
-
export { JSONLinesResponder } from './core/json-lines-responder.js';
|
|
6
|
-
export { toDownloadResponse } from './core/to-download-response.js';
|
|
7
|
-
export { get, post, put, patch, del, head, options, prefix, cloneControllerMetadata } from './core/decorators.js';
|
|
4
|
+
export { createDecorator } from './core/create-decorator.js';
|
|
8
5
|
export { decorate } from './core/decorate.js';
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
6
|
+
export { cloneControllerMetadata, del, get, head, options, patch, post, prefix, put } from './core/decorators.js';
|
|
7
|
+
export { HttpException } from './core/http-exception.js';
|
|
11
8
|
export { initSegment } from './core/init-segment.js';
|
|
9
|
+
export { JSONLinesResponder } from './core/json-lines-responder.js';
|
|
10
|
+
export { multitenant } from './core/multitenant.js';
|
|
11
|
+
export { toDownloadResponse } from './core/to-download-response.js';
|
|
12
12
|
export { operation } from './openapi/operation.js';
|
|
13
|
-
export { createValidateOnClient } from './validation/create-validate-on-client.js';
|
|
14
|
-
export { procedure } from './validation/procedure.js';
|
|
15
|
-
export { ToModelOutput } from './tools/to-model-output.js';
|
|
16
|
-
export { createTool } from './tools/create-tool.js';
|
|
17
13
|
export { deriveTools } from './tools/derive-tools.js';
|
|
18
|
-
export {
|
|
19
|
-
export type {
|
|
20
|
-
export type { VovkRequest } from './types/request.js';
|
|
21
|
-
export type { VovkJSONSchemaBase } from './types/json-schema.js';
|
|
14
|
+
export { ToModelOutput } from './tools/to-model-output.js';
|
|
15
|
+
export type { VovkFetcher } from './types/client.js';
|
|
22
16
|
export type { VovkConfig } from './types/config.js';
|
|
23
17
|
export type { VovkSchema } from './types/core.js';
|
|
24
|
-
export
|
|
18
|
+
export { HttpMethod, HttpStatus } from './types/enums.js';
|
|
19
|
+
export type { VovkBody, VovkInput, VovkIteration, VovkOutput, VovkParams, VovkQuery, VovkReturnType, VovkYieldType, } from './types/inference.js';
|
|
20
|
+
export type { VovkJSONSchemaBase } from './types/json-schema.js';
|
|
21
|
+
export type { VovkRequest } from './types/request.js';
|
|
25
22
|
export type { VovkValidateOnClient } from './types/validation.js';
|
|
26
|
-
export
|
|
23
|
+
export { createValidateOnClient } from './validation/create-validate-on-client.js';
|
|
24
|
+
export { procedure } from './validation/procedure.js';
|
package/dist/index.js
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
// core
|
|
2
|
-
export {
|
|
3
|
-
export { createDecorator } from './core/create-decorator.js';
|
|
4
|
-
export { controllersToStaticParams } from './core/controllers-to-static-params.js';
|
|
5
|
-
export { multitenant } from './core/multitenant.js';
|
|
6
|
-
export { JSONLinesResponder } from './core/json-lines-responder.js';
|
|
7
|
-
export { toDownloadResponse } from './core/to-download-response.js';
|
|
8
|
-
export { get, post, put, patch, del, head, options, prefix, cloneControllerMetadata } from './core/decorators.js';
|
|
9
|
-
export { decorate } from './core/decorate.js';
|
|
2
|
+
export { createFetcher, fetcher } from './client/fetcher.js';
|
|
10
3
|
// client
|
|
11
4
|
export { progressive } from './client/progressive.js';
|
|
12
|
-
export {
|
|
5
|
+
export { controllersToStaticParams } from './core/controllers-to-static-params.js';
|
|
6
|
+
export { createDecorator } from './core/create-decorator.js';
|
|
7
|
+
export { decorate } from './core/decorate.js';
|
|
8
|
+
export { cloneControllerMetadata, del, get, head, options, patch, post, prefix, put } from './core/decorators.js';
|
|
9
|
+
export { HttpException } from './core/http-exception.js';
|
|
13
10
|
export { initSegment } from './core/init-segment.js';
|
|
11
|
+
export { JSONLinesResponder } from './core/json-lines-responder.js';
|
|
12
|
+
export { multitenant } from './core/multitenant.js';
|
|
13
|
+
export { toDownloadResponse } from './core/to-download-response.js';
|
|
14
14
|
// openapi
|
|
15
15
|
export { operation } from './openapi/operation.js';
|
|
16
|
-
|
|
17
|
-
export { createValidateOnClient } from './validation/create-validate-on-client.js';
|
|
18
|
-
export { procedure } from './validation/procedure.js';
|
|
16
|
+
export { deriveTools } from './tools/derive-tools.js';
|
|
19
17
|
// tools
|
|
20
18
|
export { ToModelOutput } from './tools/to-model-output.js';
|
|
21
|
-
export { createTool } from './tools/create-tool.js';
|
|
22
|
-
export { deriveTools } from './tools/derive-tools.js';
|
|
23
19
|
// types
|
|
24
|
-
export {
|
|
20
|
+
export { HttpMethod, HttpStatus } from './types/enums.js';
|
|
21
|
+
// validation
|
|
22
|
+
export { createValidateOnClient } from './validation/create-validate-on-client.js';
|
|
23
|
+
export { procedure } from './validation/procedure.js';
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { readableStreamToAsyncIterable } from './client/default-stream-handler.js';
|
|
2
2
|
export { resolveGeneratorConfigValues } from './core/resolve-generator-config-values.js';
|
|
3
|
-
export {
|
|
4
|
-
export { withValidationLibrary } from './validation/with-validation-library.js';
|
|
5
|
-
export { validationSchemasObjectToSingleValidationSchema } from './validation/validation-schemas-object-to-single-validation-schema.js';
|
|
6
|
-
export { operation } from './openapi/operation.js';
|
|
7
|
-
export { openAPIToVovkSchema } from './openapi/openapi-to-vovk-schema/index.js';
|
|
3
|
+
export { vovkApp } from './core/vovk-app.js';
|
|
8
4
|
export { applyComponentsSchemas, reattachMixinDefs, } from './openapi/openapi-to-vovk-schema/apply-components-schemas.js';
|
|
5
|
+
export { openAPIToVovkSchema } from './openapi/openapi-to-vovk-schema/index.js';
|
|
6
|
+
export { operation } from './openapi/operation.js';
|
|
9
7
|
export { vovkSchemaToOpenAPI } from './openapi/vovk-schema-to-openapi.js';
|
|
10
|
-
export {
|
|
11
|
-
export { VovkSchemaIdEnum } from './types/enums.js';
|
|
8
|
+
export { createCodeSamples } from './samples/create-code-samples.js';
|
|
12
9
|
export type { MCPModelOutput } from './tools/to-model-output-mcp.js';
|
|
13
|
-
export type {
|
|
14
|
-
export type {
|
|
15
|
-
export type {
|
|
10
|
+
export type { VovkFetcherOptions, VovkRPCModule, VovkStreamAsyncIterable } from './types/client.js';
|
|
11
|
+
export type { VovkBundleConfig, VovkOpenAPIMixin, VovkOpenAPIMixinNormalized, VovkOutputConfig, VovkPackageJson, VovkReadmeConfig, VovkSamplesConfig, VovkSegmentConfig, VovkStrictConfig, } from './types/config.js';
|
|
12
|
+
export type { VovkControllerSchema, VovkErrorResponse, VovkHandlerSchema, VovkMetaSchema, VovkSegmentSchema, VovkValidationType, } from './types/core.js';
|
|
13
|
+
export { VovkSchemaIdEnum } from './types/enums.js';
|
|
16
14
|
export type { VovkOperationObject } from './types/operation.js';
|
|
17
|
-
export type {
|
|
15
|
+
export type { StandardToolV0 } from './types/standard-tool.js';
|
|
18
16
|
export type { IsAny, IsNotAny } from './types/utils.js';
|
|
17
|
+
export type { VovkTypedProcedure } from './types/validation.js';
|
|
18
|
+
export { deepExtend } from './utils/deep-extend.js';
|
|
19
|
+
export { validationSchemasObjectToSingleValidationSchema } from './validation/validation-schemas-object-to-single-validation-schema.js';
|
|
20
|
+
export { withValidationLibrary } from './validation/with-validation-library.js';
|
package/dist/internal.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// internal exports for other packages and tests
|
|
2
|
-
export {
|
|
2
|
+
export { readableStreamToAsyncIterable } from './client/default-stream-handler.js';
|
|
3
3
|
export { resolveGeneratorConfigValues } from './core/resolve-generator-config-values.js';
|
|
4
|
-
export {
|
|
5
|
-
export { withValidationLibrary } from './validation/with-validation-library.js';
|
|
6
|
-
export { validationSchemasObjectToSingleValidationSchema } from './validation/validation-schemas-object-to-single-validation-schema.js';
|
|
7
|
-
export { operation } from './openapi/operation.js';
|
|
8
|
-
export { openAPIToVovkSchema } from './openapi/openapi-to-vovk-schema/index.js';
|
|
4
|
+
export { vovkApp } from './core/vovk-app.js';
|
|
9
5
|
export { applyComponentsSchemas, reattachMixinDefs, } from './openapi/openapi-to-vovk-schema/apply-components-schemas.js';
|
|
6
|
+
export { openAPIToVovkSchema } from './openapi/openapi-to-vovk-schema/index.js';
|
|
7
|
+
export { operation } from './openapi/operation.js';
|
|
10
8
|
export { vovkSchemaToOpenAPI } from './openapi/vovk-schema-to-openapi.js';
|
|
11
|
-
export {
|
|
9
|
+
export { createCodeSamples } from './samples/create-code-samples.js';
|
|
12
10
|
export { VovkSchemaIdEnum } from './types/enums.js';
|
|
11
|
+
export { deepExtend } from './utils/deep-extend.js';
|
|
12
|
+
export { validationSchemasObjectToSingleValidationSchema } from './validation/validation-schemas-object-to-single-validation-schema.js';
|
|
13
|
+
export { withValidationLibrary } from './validation/with-validation-library.js';
|
package/dist/openapi/error.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { HttpStatus } from '../types/enums.js';
|
|
2
1
|
import { createDecorator } from '../core/create-decorator.js';
|
|
2
|
+
import { HttpStatus } from '../types/enums.js';
|
|
3
3
|
const statusDisplayText = {
|
|
4
4
|
[HttpStatus.NULL]: 'Error',
|
|
5
5
|
[HttpStatus.CONTINUE]: 'Continue',
|
|
@@ -43,7 +43,7 @@ const statusDisplayText = {
|
|
|
43
43
|
[HttpStatus.UNPROCESSABLE_ENTITY]: 'Unprocessable Entity',
|
|
44
44
|
[HttpStatus.FAILED_DEPENDENCY]: 'Failed Dependency',
|
|
45
45
|
[HttpStatus.PRECONDITION_REQUIRED]: 'Precondition Required',
|
|
46
|
-
[HttpStatus.
|
|
46
|
+
[HttpStatus.TOO_MANY_REQUESTS]: 'Too Many Requests',
|
|
47
47
|
[HttpStatus.INTERNAL_SERVER_ERROR]: 'Internal Server Error',
|
|
48
48
|
[HttpStatus.NOT_IMPLEMENTED]: 'Not Implemented',
|
|
49
49
|
[HttpStatus.BAD_GATEWAY]: 'Bad Gateway',
|
|
@@ -1,17 +1,6 @@
|
|
|
1
1
|
import type { ComponentsObject } from 'openapi3-ts/oas31';
|
|
2
2
|
import type { VovkJSONSchemaBase } from '../../types/json-schema.js';
|
|
3
|
-
export declare function applyComponentsSchemas(schema: VovkJSONSchemaBase, components: ComponentsObject['schemas'], mixinName: string,
|
|
4
|
-
/**
|
|
5
|
-
* true (default): embed the ref closure in `$defs` (self-contained — for AJV + Rust).
|
|
6
|
-
* false: keep `#/components/schemas/X`, emit no `$defs` (response slots, typed via
|
|
7
|
-
* `x-tsType`) — avoids the per-handler dup that overflows JSON.stringify on big specs.
|
|
8
|
-
*/
|
|
9
|
-
emitDefs?: boolean): VovkJSONSchemaBase | VovkJSONSchemaBase[];
|
|
10
|
-
/**
|
|
11
|
-
* Re-attach a response slot's `$defs` closure at render time, for generators that
|
|
12
|
-
* resolve `$ref` against a self-contained schema (Rust). Pulls components from the
|
|
13
|
-
* segment's shared meta → identical to the `emitDefs=true` slot. No-op for non-mixin.
|
|
14
|
-
*/
|
|
3
|
+
export declare function applyComponentsSchemas(schema: VovkJSONSchemaBase, components: ComponentsObject['schemas'], mixinName: string, emitDefs?: boolean): VovkJSONSchemaBase | VovkJSONSchemaBase[];
|
|
15
4
|
export declare function reattachMixinDefs(slot: VovkJSONSchemaBase | undefined, segment: {
|
|
16
5
|
segmentType?: string;
|
|
17
6
|
segmentName: string;
|
|
@@ -15,11 +15,8 @@ function cloneJSON(obj) {
|
|
|
15
15
|
return result;
|
|
16
16
|
}
|
|
17
17
|
export function applyComponentsSchemas(schema, components, mixinName,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* false: keep `#/components/schemas/X`, emit no `$defs` (response slots, typed via
|
|
21
|
-
* `x-tsType`) — avoids the per-handler dup that overflows JSON.stringify on big specs.
|
|
22
|
-
*/
|
|
18
|
+
// true (default): embed the ref closure in `$defs`, self-contained (AJV + Rust);
|
|
19
|
+
// false: keep `#/components/schemas/X` refs, no `$defs` (avoids the per-handler dup that overflows big specs)
|
|
23
20
|
emitDefs = true) {
|
|
24
21
|
const key = 'components/schemas';
|
|
25
22
|
if (!components || !Object.keys(components).length)
|
|
@@ -75,11 +72,8 @@ emitDefs = true) {
|
|
|
75
72
|
// Process the main schema
|
|
76
73
|
return processSchema(result);
|
|
77
74
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
* resolve `$ref` against a self-contained schema (Rust). Pulls components from the
|
|
81
|
-
* segment's shared meta → identical to the `emitDefs=true` slot. No-op for non-mixin.
|
|
82
|
-
*/
|
|
75
|
+
// re-attaches a response slot's `$defs` closure at render time (Rust needs self-contained schemas);
|
|
76
|
+
// pulls components from the segment's shared meta, no-op for non-mixin
|
|
83
77
|
export function reattachMixinDefs(slot, segment) {
|
|
84
78
|
if (!slot || segment?.segmentType !== 'mixin')
|
|
85
79
|
return slot;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { VovkSchema } from '../../types/core.js';
|
|
2
1
|
import type { VovkOpenAPIMixinNormalized } from '../../types/config.js';
|
|
2
|
+
import type { VovkSchema } from '../../types/core.js';
|
|
3
3
|
export declare function openAPIToVovkSchema({ apiRoot, source: { object: openAPIObject }, getModuleName, getMethodName, filterOperations, pruneComponents, errorMessageKey, segmentName, }: VovkOpenAPIMixinNormalized & {
|
|
4
4
|
segmentName?: string;
|
|
5
5
|
}): VovkSchema;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { schemaToTsType } from '../../samples/schema-to-ts-type.js';
|
|
2
|
+
import { VovkSchemaIdEnum } from '../../types/enums.js';
|
|
1
3
|
import { applyComponentsSchemas } from './apply-components-schemas.js';
|
|
2
4
|
import { inlineRefs } from './inline-refs.js';
|
|
3
5
|
import { pruneComponentsSchemas } from './prune-components-schemas.js';
|
|
4
|
-
import { VovkSchemaIdEnum } from '../../types/enums.js';
|
|
5
|
-
import { schemaToTsType } from '../../samples/schema-to-ts-type.js';
|
|
6
6
|
function getTsTypeString(contentType, schema) {
|
|
7
7
|
const tsTypes = new Set(contentType.flatMap((ct) => {
|
|
8
8
|
switch (ct) {
|
|
@@ -20,8 +20,24 @@ function getTsTypeString(contentType, schema) {
|
|
|
20
20
|
}));
|
|
21
21
|
return [...tsTypes].join(' | ') || schemaToTsType(schema);
|
|
22
22
|
}
|
|
23
|
+
// a spec is third party input, its x-tsType would land in the generated client as raw TS
|
|
24
|
+
function stripXTsType(value) {
|
|
25
|
+
if (Array.isArray(value))
|
|
26
|
+
return value.map(stripXTsType);
|
|
27
|
+
if (!value || typeof value !== 'object')
|
|
28
|
+
return value;
|
|
29
|
+
const result = {};
|
|
30
|
+
for (const [key, val] of Object.entries(value)) {
|
|
31
|
+
if (key === 'x-tsType')
|
|
32
|
+
continue;
|
|
33
|
+
result[key] = stripXTsType(val);
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
23
37
|
export function openAPIToVovkSchema({ apiRoot, source: { object: openAPIObject }, getModuleName, getMethodName, filterOperations, pruneComponents, errorMessageKey, segmentName, }) {
|
|
24
38
|
segmentName = segmentName ?? '';
|
|
39
|
+
// x-tsType is emitted verbatim into the generated client, only ours may reach it
|
|
40
|
+
openAPIObject = stripXTsType(openAPIObject);
|
|
25
41
|
const forceApiRoot = apiRoot ||
|
|
26
42
|
(openAPIObject.servers?.[0]?.url ??
|
|
27
43
|
('host' in openAPIObject
|
|
@@ -161,10 +177,8 @@ export function openAPIToVovkSchema({ apiRoot, source: { object: openAPIObject }
|
|
|
161
177
|
});
|
|
162
178
|
});
|
|
163
179
|
if (pruneComponents && noPathsOpenAPIObject.components?.schemas) {
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
// whole controllers tree (validation slots + raw operation objects) keeps every `$ref`
|
|
167
|
-
// a kept handler carries resolvable against the pruned meta.
|
|
180
|
+
// reassign with fresh objects only, the caller's spec shares references so its
|
|
181
|
+
// components.schemas must stay untouched; walking the whole controllers tree keeps every kept $ref resolvable
|
|
168
182
|
segment.meta = {
|
|
169
183
|
openAPIObject: {
|
|
170
184
|
...noPathsOpenAPIObject,
|
|
@@ -1,9 +1,2 @@
|
|
|
1
1
|
import type { OpenAPIObject } from 'openapi3-ts/oas31';
|
|
2
|
-
/**
|
|
3
|
-
* Resolves $ref references at the first level only (except for components/schemas references)
|
|
4
|
-
* For arrays, checks each item at the first level
|
|
5
|
-
* @param obj - The object to process (may contain $ref properties)
|
|
6
|
-
* @param openAPIObject - The complete OpenAPI document containing definitions
|
|
7
|
-
* @returns The object with resolved references (except components/schemas)
|
|
8
|
-
*/
|
|
9
2
|
export declare function inlineRefs<T extends object>(obj: unknown, openAPIObject: OpenAPIObject): T | null;
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Resolves $ref references at the first level only (except for components/schemas references)
|
|
3
|
-
* For arrays, checks each item at the first level
|
|
4
|
-
* @param obj - The object to process (may contain $ref properties)
|
|
5
|
-
* @param openAPIObject - The complete OpenAPI document containing definitions
|
|
6
|
-
* @returns The object with resolved references (except components/schemas)
|
|
7
|
-
*/
|
|
1
|
+
// resolves $ref at the first level only (skips components/schemas refs), arrays checked per item
|
|
8
2
|
export function inlineRefs(obj, openAPIObject) {
|
|
9
3
|
// Handle null or undefined
|
|
10
4
|
if (obj === null || obj === undefined) {
|
|
@@ -63,12 +57,7 @@ export function inlineRefs(obj, openAPIObject) {
|
|
|
63
57
|
// For regular objects without $ref, return as-is (no recursion)
|
|
64
58
|
return obj;
|
|
65
59
|
}
|
|
66
|
-
|
|
67
|
-
* Resolves a JSON Reference ($ref) to its target value
|
|
68
|
-
* @param ref - The reference string (e.g., "#/components/parameters/id")
|
|
69
|
-
* @param openAPIObject - The complete OpenAPI document
|
|
70
|
-
* @returns The resolved value or undefined if not found
|
|
71
|
-
*/
|
|
60
|
+
// resolves a local $ref like "#/components/parameters/id", undefined if not found
|
|
72
61
|
function resolveRef(ref, openAPIObject) {
|
|
73
62
|
// Handle only local references (starting with #)
|
|
74
63
|
if (!ref.startsWith('#/')) {
|
|
@@ -1,7 +1,2 @@
|
|
|
1
1
|
import type { ComponentsObject } from 'openapi3-ts/oas31';
|
|
2
|
-
/**
|
|
3
|
-
* Shrinks a `components.schemas` dict to the transitive `$ref` closure of `roots`
|
|
4
|
-
* (BFS with a visited set — component graphs of large specs like Stripe are cyclic).
|
|
5
|
-
* Preserves the original key order for deterministic output.
|
|
6
|
-
*/
|
|
7
2
|
export declare function pruneComponentsSchemas(roots: unknown, componentsSchemas: NonNullable<ComponentsObject['schemas']>): NonNullable<ComponentsObject['schemas']>;
|