vovk 3.0.0 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/bin/index.mjs +1 -1
- package/dist/client/createRPC.js +1 -1
- package/dist/client/defaultStreamHandler.js +5 -2
- package/dist/client/fetcher.js +1 -1
- package/dist/client/serializeQuery.js +1 -1
- package/dist/core/JSONLinesResponder.js +3 -1
- package/dist/core/controllersToStaticParams.js +4 -8
- package/dist/core/createDecorator.d.ts +7 -1
- package/dist/core/createDecorator.js +49 -38
- package/dist/core/decorators.d.ts +23 -16
- package/dist/core/decorators.js +38 -15
- package/dist/core/vovkApp.js +3 -1
- package/dist/openapi/error.d.ts +5 -1
- package/dist/openapi/openAPIToVovkSchema/applyComponentsSchemas.js +3 -3
- package/dist/openapi/operation.d.ts +20 -4
- package/dist/openapi/tool.d.ts +5 -1
- package/dist/openapi/vovkSchemaToOpenAPI.js +1 -1
- package/dist/req/parseQuery.js +10 -7
- package/dist/samples/createCodeSamples.js +2 -2
- package/dist/samples/schemaToCode.js +2 -2
- package/dist/samples/schemaToObject.js +2 -2
- package/dist/samples/schemaToTsType.d.ts +1 -1
- package/dist/tools/createToolFactory.js +1 -0
- package/dist/tools/deriveTools.d.ts +1 -1
- package/dist/tools/deriveTools.js +2 -4
- package/dist/types/client.d.ts +9 -10
- package/dist/types/json-schema.d.ts +2 -2
- package/dist/utils/camelCase.js +2 -2
- package/dist/utils/deepExtend.d.ts +5 -4
- package/dist/utils/deepExtend.js +0 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
<br>
|
|
10
10
|
<strong>Back-end Framework for Next.js App Router</strong>
|
|
11
11
|
<br />
|
|
12
|
+
<em>One codebase → type-safe clients, OpenAPI, and AI tools</em>
|
|
13
|
+
<br />
|
|
12
14
|
<a href="https://vovk.dev/">Documentation</a>
|
|
13
15
|
|
|
14
16
|
<a href="https://vovk.dev/quick-install">Quick Start</a>
|
|
@@ -23,7 +25,7 @@
|
|
|
23
25
|
The Vovk.ts runtime library with [100% self-composition](https://bundlephobia.com/result?p=vovk). It provides a wrapper for Next.js API routes, client-side tooling, utilities and types.
|
|
24
26
|
|
|
25
27
|
```sh
|
|
26
|
-
npm install vovk
|
|
28
|
+
npm install vovk
|
|
27
29
|
```
|
|
28
30
|
|
|
29
31
|
## Links
|
package/bin/index.mjs
CHANGED
package/dist/client/createRPC.js
CHANGED
|
@@ -87,7 +87,7 @@ export const createRPC = (givenSchema, segmentName, rpcModuleName, givenFetcher,
|
|
|
87
87
|
!(input.body instanceof FormData || input.body instanceof URLSearchParams || input.body instanceof Blob)) {
|
|
88
88
|
processedBody = new FormData();
|
|
89
89
|
for (const [key, value] of Object.entries(input.body)) {
|
|
90
|
-
if (value
|
|
90
|
+
if (Array.isArray(value)) {
|
|
91
91
|
value.forEach((item) => {
|
|
92
92
|
processedBody.append(key, item);
|
|
93
93
|
});
|
|
@@ -101,7 +101,7 @@ export const readableStreamToAsyncIterable = ({ readableStream, abortController,
|
|
|
101
101
|
if (error?.name === 'AbortError' && isAbortedWithoutError) {
|
|
102
102
|
break;
|
|
103
103
|
}
|
|
104
|
-
const err = new Error(
|
|
104
|
+
const err = new Error(`JSONLines stream error. ${String(error)}`);
|
|
105
105
|
err.cause = error;
|
|
106
106
|
setStreamError(err);
|
|
107
107
|
return;
|
|
@@ -113,7 +113,10 @@ export const readableStreamToAsyncIterable = ({ readableStream, abortController,
|
|
|
113
113
|
: new TextDecoder().decode(value);
|
|
114
114
|
buffer += chunk;
|
|
115
115
|
let newlineIdx;
|
|
116
|
-
while (
|
|
116
|
+
while (true) {
|
|
117
|
+
newlineIdx = buffer.indexOf('\n');
|
|
118
|
+
if (newlineIdx === -1)
|
|
119
|
+
break;
|
|
117
120
|
if (abortController?.signal.aborted && isAbortedWithoutError) {
|
|
118
121
|
break;
|
|
119
122
|
}
|
package/dist/client/fetcher.js
CHANGED
|
@@ -92,7 +92,7 @@ export function createFetcher({ prepareRequestInit, transformResponse, onSuccess
|
|
|
92
92
|
}
|
|
93
93
|
catch (e) {
|
|
94
94
|
// handle network errors
|
|
95
|
-
throw new HttpException(HttpStatus.NULL,
|
|
95
|
+
throw new HttpException(HttpStatus.NULL, `${e?.message ?? DEFAULT_ERROR_MESSAGE} ${endpoint}`, {
|
|
96
96
|
body,
|
|
97
97
|
query,
|
|
98
98
|
params,
|
|
@@ -53,7 +53,7 @@ export function serializeQuery(obj) {
|
|
|
53
53
|
// Collect query segments
|
|
54
54
|
const segments = [];
|
|
55
55
|
for (const key in obj) {
|
|
56
|
-
if (Object.
|
|
56
|
+
if (Object.hasOwn(obj, key)) {
|
|
57
57
|
const value = obj[key];
|
|
58
58
|
segments.push(...buildParams(key, value));
|
|
59
59
|
}
|
|
@@ -29,6 +29,7 @@ export class JSONLinesResponder extends Responder {
|
|
|
29
29
|
isClosed = false;
|
|
30
30
|
i = 0;
|
|
31
31
|
controller;
|
|
32
|
+
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: biome bug
|
|
32
33
|
encoder;
|
|
33
34
|
readableStream;
|
|
34
35
|
headers;
|
|
@@ -54,6 +55,7 @@ export class JSONLinesResponder extends Responder {
|
|
|
54
55
|
this.headers = headers;
|
|
55
56
|
this.readableStream = readableStream;
|
|
56
57
|
this.encoder = encoder;
|
|
58
|
+
// biome-ignore lint/style/noNonNullAssertion: assigned at readableStream start
|
|
57
59
|
this.controller = readableController;
|
|
58
60
|
this.response = getResponse?.(this) ?? new Response(readableStream, { headers });
|
|
59
61
|
request?.signal?.addEventListener('abort', this.close, { once: true });
|
|
@@ -76,7 +78,7 @@ export class JSONLinesResponder extends Responder {
|
|
|
76
78
|
const { controller, encoder } = this;
|
|
77
79
|
if (this.isClosed)
|
|
78
80
|
return;
|
|
79
|
-
controller?.enqueue(encoder?.encode(JSON.stringify(data)
|
|
81
|
+
controller?.enqueue(encoder?.encode(`${JSON.stringify(data)}\n`));
|
|
80
82
|
};
|
|
81
83
|
close = () => {
|
|
82
84
|
const { controller } = this;
|
|
@@ -11,12 +11,10 @@ export function controllersToStaticParams(c, slug = 'vovk') {
|
|
|
11
11
|
const controllers = c;
|
|
12
12
|
return [
|
|
13
13
|
{ [slug]: ['_schema_'] },
|
|
14
|
-
...Object.values(controllers)
|
|
15
|
-
.map((controller) => {
|
|
14
|
+
...Object.values(controllers).flatMap((controller) => {
|
|
16
15
|
const handlers = controller._handlers;
|
|
17
16
|
const splitPrefix = controller._prefix?.split('/') ?? [];
|
|
18
|
-
return Object.entries(handlers ?? {})
|
|
19
|
-
.map(([name, handler]) => {
|
|
17
|
+
return Object.entries(handlers ?? {}).flatMap(([name, handler]) => {
|
|
20
18
|
const staticParams = controller._handlersMetadata?.[name]?.staticParams;
|
|
21
19
|
if (staticParams?.length) {
|
|
22
20
|
return staticParams.map((paramsItem) => {
|
|
@@ -28,9 +26,7 @@ export function controllersToStaticParams(c, slug = 'vovk') {
|
|
|
28
26
|
});
|
|
29
27
|
}
|
|
30
28
|
return [{ [slug]: [...splitPrefix, ...handler.path.split('/')].filter(Boolean) }];
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
})
|
|
34
|
-
.flat(),
|
|
29
|
+
});
|
|
30
|
+
}),
|
|
35
31
|
];
|
|
36
32
|
}
|
|
@@ -2,11 +2,17 @@ import type { VovkHandlerSchema, VovkController } from '../types/core.js';
|
|
|
2
2
|
import type { VovkRequest } from '../types/request.js';
|
|
3
3
|
import type { KnownAny } from '../types/utils.js';
|
|
4
4
|
type Next = () => Promise<unknown>;
|
|
5
|
+
/** Minimal shape shared by all TC39 Stage 3 decorator context objects. */
|
|
6
|
+
type _Stage3Context = {
|
|
7
|
+
kind: string;
|
|
8
|
+
name: string | symbol;
|
|
9
|
+
addInitializer: (fn: () => void) => void;
|
|
10
|
+
};
|
|
5
11
|
/**
|
|
6
12
|
* Creates a custom decorator for Vovk controllers.
|
|
7
13
|
* @see https://vovk.dev/decorator
|
|
8
14
|
*/
|
|
9
15
|
export declare function createDecorator<TArgs extends unknown[], TRequest = VovkRequest>(handler: null | ((this: VovkController, req: TRequest, next: Next, ...args: TArgs) => unknown), initHandler?: (this: VovkController, ...args: TArgs) => Omit<VovkHandlerSchema, 'path' | 'httpMethod'> | ((handlerSchema: VovkHandlerSchema | null, options: {
|
|
10
16
|
handlerName: string;
|
|
11
|
-
}) => Omit<Partial<VovkHandlerSchema>, 'path' | 'httpMethod'>) | null | undefined): (...args: TArgs) => (target: KnownAny,
|
|
17
|
+
}) => Omit<Partial<VovkHandlerSchema>, 'path' | 'httpMethod'>) | null | undefined): (...args: TArgs) => (target: KnownAny, propertyKeyOrContext: string | _Stage3Context) => void;
|
|
12
18
|
export {};
|
|
@@ -4,46 +4,57 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export function createDecorator(handler, initHandler) {
|
|
6
6
|
return function decoratorCreator(...args) {
|
|
7
|
-
return function decorator(target,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
return function decorator(target, propertyKeyOrContext) {
|
|
8
|
+
if (typeof propertyKeyOrContext === 'object' && propertyKeyOrContext !== null && 'kind' in propertyKeyOrContext) {
|
|
9
|
+
// TC39 Stage 3 decorator — defer to addInitializer where `this` is the class
|
|
10
|
+
const propertyKey = String(propertyKeyOrContext.name);
|
|
11
|
+
propertyKeyOrContext.addInitializer(function () {
|
|
12
|
+
applyDecorator(this, propertyKey);
|
|
13
|
+
});
|
|
14
|
+
return;
|
|
12
15
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
// Experimental decorator — target is the class for static members
|
|
17
|
+
applyDecorator(target, propertyKeyOrContext);
|
|
18
|
+
function applyDecorator(controller, propertyKey) {
|
|
19
|
+
const originalMethod = controller[propertyKey];
|
|
20
|
+
if (typeof originalMethod !== 'function') {
|
|
21
|
+
throw new Error(`Unable to decorate: ${propertyKey} is not a function`);
|
|
22
|
+
}
|
|
23
|
+
const sourceMethod = originalMethod._sourceMethod ?? originalMethod;
|
|
24
|
+
const method = function method(req, params) {
|
|
25
|
+
const next = async () => {
|
|
26
|
+
return await originalMethod.call(controller, req, params);
|
|
27
|
+
};
|
|
28
|
+
return handler ? handler.call(controller, req, next, ...args) : next();
|
|
17
29
|
};
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
};
|
|
30
|
+
controller[propertyKey] = method;
|
|
31
|
+
method._controller = controller;
|
|
32
|
+
method._sourceMethod = sourceMethod;
|
|
33
|
+
method.fn = originalMethod.fn;
|
|
34
|
+
method.definition = originalMethod.definition;
|
|
35
|
+
sourceMethod.wrapper = method;
|
|
36
|
+
// TODO define internal method type
|
|
37
|
+
originalMethod._controller = controller;
|
|
38
|
+
const handlerSchema = controller._handlers?.[propertyKey] ?? null;
|
|
39
|
+
const initResultReturn = initHandler?.call(controller, ...args);
|
|
40
|
+
const initResult = typeof initResultReturn === 'function'
|
|
41
|
+
? initResultReturn(handlerSchema, {
|
|
42
|
+
handlerName: propertyKey,
|
|
43
|
+
})
|
|
44
|
+
: initResultReturn;
|
|
45
|
+
const methodSchema = {
|
|
46
|
+
...handlerSchema,
|
|
47
|
+
// avoid override of path and httpMethod
|
|
48
|
+
...(initResult?.validation ? { validation: initResult.validation } : {}),
|
|
49
|
+
...(initResult?.operationObject ? { operationObject: initResult.operationObject } : {}),
|
|
50
|
+
...(initResult?.misc ? { misc: initResult.misc } : {}),
|
|
51
|
+
};
|
|
52
|
+
method.schema = methodSchema;
|
|
53
|
+
controller._handlers = {
|
|
54
|
+
...controller._handlers,
|
|
55
|
+
[propertyKey]: methodSchema,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
47
58
|
};
|
|
48
59
|
};
|
|
49
60
|
}
|
|
@@ -1,59 +1,66 @@
|
|
|
1
1
|
import type { DecoratorOptions } from '../types/core.js';
|
|
2
2
|
import type { KnownAny } from '../types/utils.js';
|
|
3
|
+
/** Minimal shape shared by all TC39 Stage 3 decorator context objects. */
|
|
4
|
+
type _Stage3Context = {
|
|
5
|
+
kind: string;
|
|
6
|
+
name: string | symbol;
|
|
7
|
+
addInitializer: (fn: () => void) => void;
|
|
8
|
+
};
|
|
3
9
|
/**
|
|
4
10
|
* Prefix for all routes in the controller.
|
|
5
11
|
*/
|
|
6
|
-
export declare const prefix: (givenPath?: string) => (givenTarget: KnownAny) => any;
|
|
12
|
+
export declare const prefix: (givenPath?: string) => (givenTarget: KnownAny, _context?: KnownAny) => any;
|
|
7
13
|
/**
|
|
8
14
|
* Clones metadata from parent controller to child controller.
|
|
9
15
|
*/
|
|
10
|
-
export declare function cloneControllerMetadata(): <T extends new (...args: KnownAny[]) => KnownAny>(c: T) => T;
|
|
16
|
+
export declare function cloneControllerMetadata(): <T extends new (...args: KnownAny[]) => KnownAny>(c: T, _context?: KnownAny) => T;
|
|
11
17
|
/**
|
|
12
18
|
* GET HTTP method decorator.
|
|
13
19
|
*/
|
|
14
20
|
export declare const get: {
|
|
15
|
-
(givenPath?: string | undefined, options?: DecoratorOptions | undefined): (givenTarget: unknown,
|
|
16
|
-
auto: (options?: DecoratorOptions) => (givenTarget: unknown,
|
|
21
|
+
(givenPath?: string | undefined, options?: DecoratorOptions | undefined): (givenTarget: unknown, propertyKeyOrContext: string | _Stage3Context) => void;
|
|
22
|
+
auto: (options?: DecoratorOptions) => (givenTarget: unknown, propertyKeyOrContext: string | _Stage3Context) => void;
|
|
17
23
|
};
|
|
18
24
|
/**
|
|
19
25
|
* POST HTTP method decorator.
|
|
20
26
|
*/
|
|
21
27
|
export declare const post: {
|
|
22
|
-
(givenPath?: string | undefined, options?: Omit<DecoratorOptions, "staticParams"> | undefined): (givenTarget: unknown,
|
|
23
|
-
auto: (options?: DecoratorOptions) => (givenTarget: unknown,
|
|
28
|
+
(givenPath?: string | undefined, options?: Omit<DecoratorOptions, "staticParams"> | undefined): (givenTarget: unknown, propertyKeyOrContext: string | _Stage3Context) => void;
|
|
29
|
+
auto: (options?: DecoratorOptions) => (givenTarget: unknown, propertyKeyOrContext: string | _Stage3Context) => void;
|
|
24
30
|
};
|
|
25
31
|
/**
|
|
26
32
|
* PUT HTTP method decorator.
|
|
27
33
|
*/
|
|
28
34
|
export declare const put: {
|
|
29
|
-
(givenPath?: string | undefined, options?: Omit<DecoratorOptions, "staticParams"> | undefined): (givenTarget: unknown,
|
|
30
|
-
auto: (options?: DecoratorOptions) => (givenTarget: unknown,
|
|
35
|
+
(givenPath?: string | undefined, options?: Omit<DecoratorOptions, "staticParams"> | undefined): (givenTarget: unknown, propertyKeyOrContext: string | _Stage3Context) => void;
|
|
36
|
+
auto: (options?: DecoratorOptions) => (givenTarget: unknown, propertyKeyOrContext: string | _Stage3Context) => void;
|
|
31
37
|
};
|
|
32
38
|
/**
|
|
33
39
|
* PATCH HTTP method decorator.
|
|
34
40
|
*/
|
|
35
41
|
export declare const patch: {
|
|
36
|
-
(givenPath?: string | undefined, options?: Omit<DecoratorOptions, "staticParams"> | undefined): (givenTarget: unknown,
|
|
37
|
-
auto: (options?: DecoratorOptions) => (givenTarget: unknown,
|
|
42
|
+
(givenPath?: string | undefined, options?: Omit<DecoratorOptions, "staticParams"> | undefined): (givenTarget: unknown, propertyKeyOrContext: string | _Stage3Context) => void;
|
|
43
|
+
auto: (options?: DecoratorOptions) => (givenTarget: unknown, propertyKeyOrContext: string | _Stage3Context) => void;
|
|
38
44
|
};
|
|
39
45
|
/**
|
|
40
46
|
* DELETE HTTP method decorator.
|
|
41
47
|
*/
|
|
42
48
|
export declare const del: {
|
|
43
|
-
(givenPath?: string | undefined, options?: Omit<DecoratorOptions, "staticParams"> | undefined): (givenTarget: unknown,
|
|
44
|
-
auto: (options?: DecoratorOptions) => (givenTarget: unknown,
|
|
49
|
+
(givenPath?: string | undefined, options?: Omit<DecoratorOptions, "staticParams"> | undefined): (givenTarget: unknown, propertyKeyOrContext: string | _Stage3Context) => void;
|
|
50
|
+
auto: (options?: DecoratorOptions) => (givenTarget: unknown, propertyKeyOrContext: string | _Stage3Context) => void;
|
|
45
51
|
};
|
|
46
52
|
/**
|
|
47
53
|
* HEAD HTTP method decorator.
|
|
48
54
|
*/
|
|
49
55
|
export declare const head: {
|
|
50
|
-
(givenPath?: string | undefined, options?: Omit<DecoratorOptions, "staticParams"> | undefined): (givenTarget: unknown,
|
|
51
|
-
auto: (options?: DecoratorOptions) => (givenTarget: unknown,
|
|
56
|
+
(givenPath?: string | undefined, options?: Omit<DecoratorOptions, "staticParams"> | undefined): (givenTarget: unknown, propertyKeyOrContext: string | _Stage3Context) => void;
|
|
57
|
+
auto: (options?: DecoratorOptions) => (givenTarget: unknown, propertyKeyOrContext: string | _Stage3Context) => void;
|
|
52
58
|
};
|
|
53
59
|
/**
|
|
54
60
|
* OPTIONS HTTP method decorator.
|
|
55
61
|
*/
|
|
56
62
|
export declare const options: {
|
|
57
|
-
(givenPath?: string | undefined, options?: Omit<DecoratorOptions, "staticParams"> | undefined): (givenTarget: unknown,
|
|
58
|
-
auto: (options?: DecoratorOptions) => (givenTarget: unknown,
|
|
63
|
+
(givenPath?: string | undefined, options?: Omit<DecoratorOptions, "staticParams"> | undefined): (givenTarget: unknown, propertyKeyOrContext: string | _Stage3Context) => void;
|
|
64
|
+
auto: (options?: DecoratorOptions) => (givenTarget: unknown, propertyKeyOrContext: string | _Stage3Context) => void;
|
|
59
65
|
};
|
|
66
|
+
export {};
|
package/dist/core/decorators.js
CHANGED
|
@@ -3,6 +3,8 @@ import { trimPath } from '../utils/trimPath.js';
|
|
|
3
3
|
import { toKebabCase } from '../utils/toKebabCase.js';
|
|
4
4
|
import { HttpMethod } from '../types/enums.js';
|
|
5
5
|
const isClass = (func) => typeof func === 'function' && /class/.test(func.toString());
|
|
6
|
+
/** Detects whether the second decorator argument is a TC39 Stage 3 context object. */
|
|
7
|
+
const _isStage3 = (arg) => typeof arg === 'object' && arg !== null && 'kind' in arg;
|
|
6
8
|
const assignSchema = ({ controller, propertyKey, path, options, httpMethod, }) => {
|
|
7
9
|
if (typeof window !== 'undefined') {
|
|
8
10
|
throw new Error('HTTP decorators can be used on server-side only. You have probably imported a controller on the client-side.');
|
|
@@ -34,7 +36,7 @@ const assignSchema = ({ controller, propertyKey, path, options, httpMethod, }) =
|
|
|
34
36
|
...controller._handlers,
|
|
35
37
|
[propertyKey]: {
|
|
36
38
|
...schema,
|
|
37
|
-
...
|
|
39
|
+
...controller._handlers?.[propertyKey],
|
|
38
40
|
path,
|
|
39
41
|
httpMethod,
|
|
40
42
|
},
|
|
@@ -44,7 +46,7 @@ const assignSchema = ({ controller, propertyKey, path, options, httpMethod, }) =
|
|
|
44
46
|
controller._handlersMetadata = {
|
|
45
47
|
...controller._handlersMetadata,
|
|
46
48
|
[propertyKey]: {
|
|
47
|
-
...
|
|
49
|
+
...controller._handlersMetadata?.[propertyKey],
|
|
48
50
|
staticParams: options?.staticParams,
|
|
49
51
|
},
|
|
50
52
|
};
|
|
@@ -52,22 +54,43 @@ const assignSchema = ({ controller, propertyKey, path, options, httpMethod, }) =
|
|
|
52
54
|
function createHTTPDecorator(httpMethod) {
|
|
53
55
|
function decoratorFactory(givenPath = '', options) {
|
|
54
56
|
const path = trimPath(givenPath);
|
|
55
|
-
function decorator(givenTarget,
|
|
57
|
+
function decorator(givenTarget, propertyKeyOrContext) {
|
|
58
|
+
if (_isStage3(propertyKeyOrContext)) {
|
|
59
|
+
const propertyKey = String(propertyKeyOrContext.name);
|
|
60
|
+
propertyKeyOrContext.addInitializer(function () {
|
|
61
|
+
assignSchema({ controller: this, propertyKey, path, options, httpMethod });
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
56
65
|
const controller = givenTarget;
|
|
57
|
-
assignSchema({ controller, propertyKey, path, options, httpMethod });
|
|
66
|
+
assignSchema({ controller, propertyKey: propertyKeyOrContext, path, options, httpMethod });
|
|
58
67
|
}
|
|
59
68
|
return decorator;
|
|
60
69
|
}
|
|
61
70
|
const auto = (options) => {
|
|
62
|
-
function decorator(givenTarget,
|
|
71
|
+
function decorator(givenTarget, propertyKeyOrContext) {
|
|
72
|
+
if (_isStage3(propertyKeyOrContext)) {
|
|
73
|
+
const propertyKey = String(propertyKeyOrContext.name);
|
|
74
|
+
propertyKeyOrContext.addInitializer(function () {
|
|
75
|
+
const controller = this;
|
|
76
|
+
// validation is already assigned at procedure function
|
|
77
|
+
const properties = Object.keys(controller._handlers?.[propertyKey]?.validation?.params?.properties ?? {});
|
|
78
|
+
const kebabCasePath = toKebabCase(propertyKey);
|
|
79
|
+
const path = properties.length
|
|
80
|
+
? `${kebabCasePath}/${properties.map((prop) => `{${prop}}`).join('/')}`
|
|
81
|
+
: kebabCasePath;
|
|
82
|
+
assignSchema({ controller, propertyKey, path, options, httpMethod });
|
|
83
|
+
});
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
63
86
|
const controller = givenTarget;
|
|
64
87
|
// validation is already assigned at procedure function
|
|
65
|
-
const properties = Object.keys(
|
|
66
|
-
const kebabCasePath = toKebabCase(
|
|
88
|
+
const properties = Object.keys(controller._handlers?.[propertyKeyOrContext]?.validation?.params?.properties ?? {});
|
|
89
|
+
const kebabCasePath = toKebabCase(propertyKeyOrContext);
|
|
67
90
|
const path = properties.length
|
|
68
91
|
? `${kebabCasePath}/${properties.map((prop) => `{${prop}}`).join('/')}`
|
|
69
92
|
: kebabCasePath;
|
|
70
|
-
assignSchema({ controller, propertyKey, path, options, httpMethod });
|
|
93
|
+
assignSchema({ controller, propertyKey: propertyKeyOrContext, path, options, httpMethod });
|
|
71
94
|
}
|
|
72
95
|
return decorator;
|
|
73
96
|
};
|
|
@@ -80,7 +103,7 @@ function createHTTPDecorator(httpMethod) {
|
|
|
80
103
|
*/
|
|
81
104
|
export const prefix = (givenPath = '') => {
|
|
82
105
|
const path = trimPath(givenPath);
|
|
83
|
-
return (givenTarget) => {
|
|
106
|
+
return (givenTarget, _context) => {
|
|
84
107
|
const controller = givenTarget;
|
|
85
108
|
controller._prefix = path;
|
|
86
109
|
return givenTarget;
|
|
@@ -90,16 +113,16 @@ export const prefix = (givenPath = '') => {
|
|
|
90
113
|
* Clones metadata from parent controller to child controller.
|
|
91
114
|
*/
|
|
92
115
|
export function cloneControllerMetadata() {
|
|
93
|
-
return function inherit(c) {
|
|
116
|
+
return function inherit(c, _context) {
|
|
94
117
|
const parent = Object.getPrototypeOf(c);
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
118
|
+
const controller = c;
|
|
119
|
+
controller._handlers = { ...parent._handlers, ...controller._handlers };
|
|
120
|
+
controller._handlersMetadata = { ...parent._handlersMetadata, ...controller._handlersMetadata };
|
|
98
121
|
Object.values(vovkApp.routes).forEach((methods) => {
|
|
99
122
|
const parentMethods = methods.get(parent) ?? {};
|
|
100
|
-
methods.set(
|
|
123
|
+
methods.set(controller, { ...parentMethods, ...methods.get(controller) });
|
|
101
124
|
});
|
|
102
|
-
return
|
|
125
|
+
return controller;
|
|
103
126
|
};
|
|
104
127
|
}
|
|
105
128
|
/**
|
package/dist/core/vovkApp.js
CHANGED
|
@@ -115,6 +115,8 @@ class VovkApp {
|
|
|
115
115
|
const candidateRoutes = routesByLength.get(pathLength) || [];
|
|
116
116
|
for (const p of candidateRoutes) {
|
|
117
117
|
const routeSegments = this.#routeSegmentsCache.get(p);
|
|
118
|
+
if (!routeSegments)
|
|
119
|
+
continue; // This should never happen, fix TS error
|
|
118
120
|
const params = {};
|
|
119
121
|
// Fast path for routes with parameters
|
|
120
122
|
const paramPositions = this.#routeParamPositionsCache.get(p);
|
|
@@ -261,7 +263,7 @@ class VovkApp {
|
|
|
261
263
|
}
|
|
262
264
|
const isIterator = typeof result === 'object' &&
|
|
263
265
|
!!result &&
|
|
264
|
-
!(result
|
|
266
|
+
!Array.isArray(result) &&
|
|
265
267
|
((Reflect.has(result, Symbol.iterator) &&
|
|
266
268
|
typeof result[Symbol.iterator] === 'function') ||
|
|
267
269
|
(Reflect.has(result, Symbol.asyncIterator) &&
|
package/dist/openapi/error.d.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import { HttpStatus } from '../types/enums.js';
|
|
2
|
-
export declare const error: (status: HttpStatus, message: string) => (target: import("../types/utils.js").KnownAny,
|
|
2
|
+
export declare const error: (status: HttpStatus, message: string) => (target: import("../types/utils.js").KnownAny, propertyKeyOrContext: string | {
|
|
3
|
+
kind: string;
|
|
4
|
+
name: string | symbol;
|
|
5
|
+
addInitializer: (fn: () => void) => void;
|
|
6
|
+
}) => void;
|
|
@@ -37,7 +37,7 @@ export function applyComponentsSchemas(schema, components, mixinName) {
|
|
|
37
37
|
const $ref = newObj.$ref;
|
|
38
38
|
if ($ref && typeof $ref === 'string' && $ref.startsWith(`#/${key}/`)) {
|
|
39
39
|
const componentName = $ref.replace(`#/${key}/`, '');
|
|
40
|
-
if (components[componentName]) {
|
|
40
|
+
if (components?.[componentName]) {
|
|
41
41
|
newObj.$ref = `#/$defs/${componentName}`;
|
|
42
42
|
newObj['x-tsType'] ??= `Mixins.${upperFirst(camelCase(mixinName))}.${upperFirst(camelCase(componentName))}`;
|
|
43
43
|
}
|
|
@@ -45,7 +45,7 @@ export function applyComponentsSchemas(schema, components, mixinName) {
|
|
|
45
45
|
delete newObj.$ref; // Remove $ref if component not found (Telegram API has Type $refs that is not defined in components)
|
|
46
46
|
}
|
|
47
47
|
// Add the component to $defs if not already added
|
|
48
|
-
if (!addedComponents.has(componentName) && components[componentName]) {
|
|
48
|
+
if (!addedComponents.has(componentName) && components?.[componentName]) {
|
|
49
49
|
addedComponents.add(componentName);
|
|
50
50
|
if (result.$defs) {
|
|
51
51
|
result.$defs[componentName] = processSchema(cloneJSON(components[componentName]));
|
|
@@ -54,7 +54,7 @@ export function applyComponentsSchemas(schema, components, mixinName) {
|
|
|
54
54
|
}
|
|
55
55
|
// Process properties recursively
|
|
56
56
|
for (const key in newObj) {
|
|
57
|
-
if (Object.
|
|
57
|
+
if (Object.hasOwn(newObj, key)) {
|
|
58
58
|
newObj[key] = processSchema(newObj[key]);
|
|
59
59
|
}
|
|
60
60
|
}
|
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import type { VovkOperationObject } from '../types/operation.js';
|
|
2
|
-
export declare const operationDecorator: (openAPIOperationObject?: VovkOperationObject | undefined) => (target: import("../types/utils.js").KnownAny,
|
|
2
|
+
export declare const operationDecorator: (openAPIOperationObject?: VovkOperationObject | undefined) => (target: import("../types/utils.js").KnownAny, propertyKeyOrContext: string | {
|
|
3
|
+
kind: string;
|
|
4
|
+
name: string | symbol;
|
|
5
|
+
addInitializer: (fn: () => void) => void;
|
|
6
|
+
}) => void;
|
|
3
7
|
/**
|
|
4
8
|
* OpenAPI operation decorator to add metadata to API operations. Also includes `error` and `tool` utilities.
|
|
5
9
|
* @see https://vovk.dev/openapi
|
|
6
10
|
*/
|
|
7
|
-
export declare const operation: ((openAPIOperationObject?: VovkOperationObject | undefined) => (target: import("../types/utils.js").KnownAny,
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
export declare const operation: ((openAPIOperationObject?: VovkOperationObject | undefined) => (target: import("../types/utils.js").KnownAny, propertyKeyOrContext: string | {
|
|
12
|
+
kind: string;
|
|
13
|
+
name: string | symbol;
|
|
14
|
+
addInitializer: (fn: () => void) => void;
|
|
15
|
+
}) => void) & {
|
|
16
|
+
error: (status: import("../index.js").HttpStatus, message: string) => (target: import("../types/utils.js").KnownAny, propertyKeyOrContext: string | {
|
|
17
|
+
kind: string;
|
|
18
|
+
name: string | symbol;
|
|
19
|
+
addInitializer: (fn: () => void) => void;
|
|
20
|
+
}) => void;
|
|
21
|
+
tool: (toolOptions: import("../types/tools.js").VovkToolOptions) => (target: import("../types/utils.js").KnownAny, propertyKeyOrContext: string | {
|
|
22
|
+
kind: string;
|
|
23
|
+
name: string | symbol;
|
|
24
|
+
addInitializer: (fn: () => void) => void;
|
|
25
|
+
}) => void;
|
|
10
26
|
};
|
package/dist/openapi/tool.d.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import type { VovkToolOptions } from '../types/tools.js';
|
|
2
|
-
export declare const tool: (toolOptions: VovkToolOptions) => (target: import("../types/utils.js").KnownAny,
|
|
2
|
+
export declare const tool: (toolOptions: VovkToolOptions) => (target: import("../types/utils.js").KnownAny, propertyKeyOrContext: string | {
|
|
3
|
+
kind: string;
|
|
4
|
+
name: string | symbol;
|
|
5
|
+
addInitializer: (fn: () => void) => void;
|
|
6
|
+
}) => void;
|
|
@@ -91,7 +91,7 @@ export function vovkSchemaToOpenAPI({ config, rootEntry = 'api', schema: fullSch
|
|
|
91
91
|
}))
|
|
92
92
|
: null;
|
|
93
93
|
const path = h.misc?.originalPath ??
|
|
94
|
-
|
|
94
|
+
`/${[rootEntry.replace(/^\/+|\/+$/g, ''), segmentName, c.prefix, h.path].filter(Boolean).join('/')}`;
|
|
95
95
|
paths[path] = paths[path] ?? {};
|
|
96
96
|
const httpMethod = h.httpMethod.toLowerCase();
|
|
97
97
|
paths[path][httpMethod] ??= {};
|
package/dist/req/parseQuery.js
CHANGED
|
@@ -19,7 +19,10 @@ function parseKey(key) {
|
|
|
19
19
|
// Now capture all bracket parts: [something], [0], []
|
|
20
20
|
const bracketRegex = /\[([^[\]]*)\]/g;
|
|
21
21
|
let match;
|
|
22
|
-
while (
|
|
22
|
+
while (true) {
|
|
23
|
+
match = bracketRegex.exec(key);
|
|
24
|
+
if (match === null)
|
|
25
|
+
break;
|
|
23
26
|
// match[1] is the content inside the brackets
|
|
24
27
|
segments.push(match[1]);
|
|
25
28
|
}
|
|
@@ -44,7 +47,7 @@ function setValue(obj, path, value) {
|
|
|
44
47
|
}
|
|
45
48
|
current.push(value);
|
|
46
49
|
}
|
|
47
|
-
else if (!isNaN(Number(segment))) {
|
|
50
|
+
else if (!Number.isNaN(Number(segment))) {
|
|
48
51
|
// Numeric segment => array index
|
|
49
52
|
const idx = Number(segment);
|
|
50
53
|
if (!Array.isArray(current)) {
|
|
@@ -70,9 +73,9 @@ function setValue(obj, path, value) {
|
|
|
70
73
|
// for the next segment. We'll push something and move current to that.
|
|
71
74
|
if (current.length === 0) {
|
|
72
75
|
// nothing in array yet
|
|
73
|
-
current.push(typeof nextSegment === 'string' && !isNaN(Number(nextSegment)) ? [] : {});
|
|
76
|
+
current.push(typeof nextSegment === 'string' && !Number.isNaN(Number(nextSegment)) ? [] : {});
|
|
74
77
|
}
|
|
75
|
-
else if (typeof nextSegment === 'string' && !isNaN(Number(nextSegment))) {
|
|
78
|
+
else if (typeof nextSegment === 'string' && !Number.isNaN(Number(nextSegment))) {
|
|
76
79
|
// next is numeric => we want an array
|
|
77
80
|
if (!Array.isArray(current[current.length - 1])) {
|
|
78
81
|
current[current.length - 1] = [];
|
|
@@ -86,7 +89,7 @@ function setValue(obj, path, value) {
|
|
|
86
89
|
}
|
|
87
90
|
current = current[current.length - 1];
|
|
88
91
|
}
|
|
89
|
-
else if (!isNaN(Number(segment))) {
|
|
92
|
+
else if (!Number.isNaN(Number(segment))) {
|
|
90
93
|
// segment is numeric => array index
|
|
91
94
|
const idx = Number(segment);
|
|
92
95
|
if (!Array.isArray(current)) {
|
|
@@ -94,7 +97,7 @@ function setValue(obj, path, value) {
|
|
|
94
97
|
}
|
|
95
98
|
if (current[idx] === undefined) {
|
|
96
99
|
// Create placeholder for next segment
|
|
97
|
-
current[idx] = typeof nextSegment === 'string' && !isNaN(Number(nextSegment)) ? [] : {};
|
|
100
|
+
current[idx] = typeof nextSegment === 'string' && !Number.isNaN(Number(nextSegment)) ? [] : {};
|
|
98
101
|
}
|
|
99
102
|
current = current[idx];
|
|
100
103
|
}
|
|
@@ -102,7 +105,7 @@ function setValue(obj, path, value) {
|
|
|
102
105
|
// segment is an object key
|
|
103
106
|
if (current[segment] === undefined) {
|
|
104
107
|
// Create placeholder
|
|
105
|
-
current[segment] = typeof nextSegment === 'string' && !isNaN(Number(nextSegment)) ? [] : {};
|
|
108
|
+
current[segment] = typeof nextSegment === 'string' && !Number.isNaN(Number(nextSegment)) ? [] : {};
|
|
106
109
|
}
|
|
107
110
|
current = current[segment];
|
|
108
111
|
}
|
|
@@ -81,7 +81,7 @@ ${[
|
|
|
81
81
|
}`
|
|
82
82
|
: '';
|
|
83
83
|
const TS_CODE = `import { ${rpcName} } from '${packageName}';
|
|
84
|
-
${bodyValidation && isForm(bodyValidation) ? getTsFormSample(bodyValidation)
|
|
84
|
+
${bodyValidation && isForm(bodyValidation) ? `${getTsFormSample(bodyValidation)}\n` : ''}
|
|
85
85
|
${iterationValidation ? 'using' : 'const'} response = await ${rpcName}.${handlerName}(${tsArgs});
|
|
86
86
|
${outputValidation
|
|
87
87
|
? `
|
|
@@ -220,7 +220,7 @@ use serde_json::{
|
|
|
220
220
|
json
|
|
221
221
|
};
|
|
222
222
|
${iterationValidation ? 'use futures_util::StreamExt;\n' : ''}${bodyValidation && isForm(bodyValidation) ? `use reqwest::multipart;\n` : ''}#[tokio::main]
|
|
223
|
-
async fn main() {${bodyValidation && isForm(bodyValidation) ?
|
|
223
|
+
async fn main() {${bodyValidation && isForm(bodyValidation) ? `\n ${getRsFormSample(bodyValidation)}\n` : ''}
|
|
224
224
|
let response = ${rpcNameSnake}::${handlerNameSnake}(
|
|
225
225
|
${bodyValidation ? getBody(bodyValidation) : '()'}, /* body */
|
|
226
226
|
${queryValidation ? serdeUnwrap(getRsJSONSample(queryValidation)) : '()'}, /* query */
|
|
@@ -47,7 +47,7 @@ export function getSampleValue(schema, rootSchema, ignoreBinary) {
|
|
|
47
47
|
}
|
|
48
48
|
if (schema.allOf && schema.allOf.length > 0) {
|
|
49
49
|
// Merge all schemas in allOf
|
|
50
|
-
const mergedSchema = schema.allOf.reduce((acc, s) => (
|
|
50
|
+
const mergedSchema = schema.allOf.reduce((acc, s) => Object.assign(acc, s), {});
|
|
51
51
|
return getSampleValue(mergedSchema, rootSchema, ignoreBinary);
|
|
52
52
|
}
|
|
53
53
|
// Handle different types
|
|
@@ -235,7 +235,7 @@ function handleObject(schema, rootSchema, ignoreBinary) {
|
|
|
235
235
|
if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
|
|
236
236
|
const value = getSampleValue(schema.additionalProperties, rootSchema, ignoreBinary);
|
|
237
237
|
if (value !== undefined) {
|
|
238
|
-
result
|
|
238
|
+
result.additionalProp = value;
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
241
|
return result;
|
|
@@ -32,7 +32,7 @@ export function schemaToObject(schema, rootSchema) {
|
|
|
32
32
|
}
|
|
33
33
|
if (schema.allOf && schema.allOf.length > 0) {
|
|
34
34
|
// Merge all schemas in allOf
|
|
35
|
-
const mergedSchema = schema.allOf.reduce((acc, s) => (
|
|
35
|
+
const mergedSchema = schema.allOf.reduce((acc, s) => Object.assign(acc, s), {});
|
|
36
36
|
return schemaToObject(mergedSchema, rootSchema);
|
|
37
37
|
}
|
|
38
38
|
// Handle different types
|
|
@@ -148,7 +148,7 @@ function handleObject(schema, rootSchema) {
|
|
|
148
148
|
}
|
|
149
149
|
// Handle additionalProperties
|
|
150
150
|
if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
|
|
151
|
-
result
|
|
151
|
+
result.additionalProp = schemaToObject(schema.additionalProperties, rootSchema);
|
|
152
152
|
}
|
|
153
153
|
return result;
|
|
154
154
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { VovkJSONSchemaBase } from 'vovk';
|
|
1
|
+
import type { VovkJSONSchemaBase } from 'vovk';
|
|
2
2
|
export declare function schemaToTsType(jsonSchema: VovkJSONSchemaBase | boolean): string;
|
|
@@ -9,6 +9,7 @@ export function createToolFactory({ toJSONSchema, }) {
|
|
|
9
9
|
title,
|
|
10
10
|
description,
|
|
11
11
|
get parameters() {
|
|
12
|
+
// biome-ignore lint/suspicious/noAssignInExpressions: TODO
|
|
12
13
|
return (parameters ??= inputSchema ? toJSONSchema(inputSchema, { validationType: 'tool-input', target }) : {});
|
|
13
14
|
},
|
|
14
15
|
inputSchema: inputSchema,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DefaultModelOutput } from './toModelOutputDefault.js';
|
|
1
|
+
import type { DefaultModelOutput } from './toModelOutputDefault.js';
|
|
2
2
|
import type { VovkRequest } from '../types/request.js';
|
|
3
3
|
import type { VovkToolDerived, ToModelOutputFn } from '../types/tools.js';
|
|
4
4
|
type DerivedToolInput = {
|
|
@@ -111,8 +111,7 @@ const makeTool = ({ moduleName, handlerName, module, meta, toModelOutput, onExec
|
|
|
111
111
|
};
|
|
112
112
|
export function deriveTools(options) {
|
|
113
113
|
const { modules, meta, toModelOutput = ToModelOutput.DEFAULT, onExecute = (result) => result, onError = () => { }, } = options;
|
|
114
|
-
const tools = Object.entries(modules ?? {})
|
|
115
|
-
.map(([moduleName, module]) => {
|
|
114
|
+
const tools = Object.entries(modules ?? {}).flatMap(([moduleName, module]) => {
|
|
116
115
|
return Object.entries(module ?? {})
|
|
117
116
|
.filter(([, handler]) => handler?.schema?.operationObject && !handler?.schema?.operationObject?.['x-tool']?.hidden)
|
|
118
117
|
.map(([handlerName]) => makeTool({
|
|
@@ -124,8 +123,7 @@ export function deriveTools(options) {
|
|
|
124
123
|
onExecute,
|
|
125
124
|
onError,
|
|
126
125
|
}));
|
|
127
|
-
})
|
|
128
|
-
.flat();
|
|
126
|
+
});
|
|
129
127
|
const toolsByName = Object.fromEntries(tools.map((tool) => [tool.name, tool]));
|
|
130
128
|
return {
|
|
131
129
|
tools,
|
package/dist/types/client.d.ts
CHANGED
|
@@ -10,28 +10,27 @@ import type { BodyTypeFromContentType, ContentType, VovkValidateOnClient } from
|
|
|
10
10
|
type OmitNullable<T> = {
|
|
11
11
|
[K in keyof T as T[K] extends null | undefined ? never : K]: T[K];
|
|
12
12
|
};
|
|
13
|
-
type Empty = {};
|
|
14
13
|
export type StaticMethodInput<T extends ((req: VovkRequest<KnownAny, KnownAny, KnownAny>, params: KnownAny) => KnownAny) & {
|
|
15
14
|
__types?: {
|
|
16
15
|
contentType: ContentType[];
|
|
17
16
|
};
|
|
18
17
|
}> = OmitNullable<(Parameters<T>[0] extends VovkRequest<infer TBody, infer TQuery, infer TParams> ? (T['__types'] extends {
|
|
19
18
|
contentType: infer CT extends ContentType[];
|
|
20
|
-
} ? unknown extends TBody ?
|
|
19
|
+
} ? unknown extends TBody ? unknown : {
|
|
21
20
|
body: BodyTypeFromContentType<CT, TBody>;
|
|
22
21
|
} : TBody extends Record<KnownAny, KnownAny> ? {
|
|
23
22
|
body: TBody;
|
|
24
|
-
} :
|
|
23
|
+
} : unknown) & (TQuery extends Record<KnownAny, KnownAny> ? {
|
|
25
24
|
query: TQuery;
|
|
26
|
-
} :
|
|
25
|
+
} : unknown) & (TParams extends Record<KnownAny, KnownAny> ? {
|
|
27
26
|
params: TParams;
|
|
28
|
-
} :
|
|
27
|
+
} : unknown) & {
|
|
29
28
|
meta?: {
|
|
30
29
|
[key: string]: KnownAny;
|
|
31
30
|
};
|
|
32
|
-
} :
|
|
31
|
+
} : unknown) & (Parameters<T>[1] extends Record<KnownAny, KnownAny> ? {
|
|
33
32
|
params: Parameters<T>[1];
|
|
34
|
-
} :
|
|
33
|
+
} : unknown)>;
|
|
35
34
|
type ToPromise<T> = T extends PromiseLike<unknown> ? T : Promise<T>;
|
|
36
35
|
export type VovkStreamAsyncIterable<T> = {
|
|
37
36
|
status: number;
|
|
@@ -49,7 +48,7 @@ type ActualReturnType<T extends ControllerStaticMethod> = T extends {
|
|
|
49
48
|
} ? R : ReturnType<T>;
|
|
50
49
|
type StaticMethodReturn<T extends ControllerStaticMethod> = IsNextJs extends true ? ActualReturnType<T> extends NextResponse<infer U> | Promise<NextResponse<infer U>> ? U : ActualReturnType<T> extends Response | Promise<Response> ? Awaited<ActualReturnType<T>> : ActualReturnType<T> : ActualReturnType<T> extends Response | Promise<Response> ? Awaited<ActualReturnType<T>> : ActualReturnType<T>;
|
|
51
50
|
type StaticMethodReturnPromise<T extends ControllerStaticMethod> = ToPromise<StaticMethodReturn<T>>;
|
|
52
|
-
type StaticMethodOptions<T extends (req: VovkRequest<KnownAny, KnownAny, KnownAny>, params: KnownAny) =>
|
|
51
|
+
type StaticMethodOptions<T extends (req: VovkRequest<KnownAny, KnownAny, KnownAny>, params: KnownAny) => undefined | object | JSONLinesResponder<TStreamIteration> | Promise<JSONLinesResponder<TStreamIteration>>, TFetcherOptions extends Record<string, KnownAny>, TStreamIteration, R, F extends VovkFetcherOptions<KnownAny>> = Partial<TFetcherOptions & {
|
|
53
52
|
transform: (staticMethodReturn: T extends {
|
|
54
53
|
__types: {
|
|
55
54
|
iteration: infer U;
|
|
@@ -57,12 +56,12 @@ type StaticMethodOptions<T extends (req: VovkRequest<KnownAny, KnownAny, KnownAn
|
|
|
57
56
|
} ? unknown extends U ? Awaited<StaticMethodReturn<T>> : VovkStreamAsyncIterable<U> : Awaited<StaticMethodReturn<T>> extends JSONLinesResponder<infer U> ? VovkStreamAsyncIterable<U> : Awaited<StaticMethodReturn<T>>, resp: Response) => R;
|
|
58
57
|
fetcher: VovkFetcher<F>;
|
|
59
58
|
}>;
|
|
60
|
-
export type ClientMethodReturn<T extends (req: VovkRequest<KnownAny, KnownAny, KnownAny>, params: KnownAny) =>
|
|
59
|
+
export type ClientMethodReturn<T extends (req: VovkRequest<KnownAny, KnownAny, KnownAny>, params: KnownAny) => undefined | object | JSONLinesResponder<TStreamIteration> | Promise<JSONLinesResponder<TStreamIteration>>, TStreamIteration, R> = R extends object ? Promise<Awaited<R>> : T extends {
|
|
61
60
|
__types: {
|
|
62
61
|
iteration: infer U;
|
|
63
62
|
};
|
|
64
63
|
} ? unknown extends U ? StaticMethodReturnPromise<T> : Promise<VovkStreamAsyncIterable<U>> : ActualReturnType<T> extends Promise<JSONLinesResponder<infer U>> | JSONLinesResponder<infer U> | Iterator<infer U> | AsyncIterator<infer U> ? Promise<VovkStreamAsyncIterable<U>> : StaticMethodReturnPromise<T>;
|
|
65
|
-
export type ClientMethod<T extends ((req: VovkRequest<KnownAny, KnownAny, KnownAny>, params: KnownAny) =>
|
|
64
|
+
export type ClientMethod<T extends ((req: VovkRequest<KnownAny, KnownAny, KnownAny>, params: KnownAny) => undefined | object | JSONLinesResponder<TStreamIteration> | Promise<JSONLinesResponder<TStreamIteration>>) & {
|
|
66
65
|
__types?: {
|
|
67
66
|
body: KnownAny;
|
|
68
67
|
query: KnownAny;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { KnownAny } from './utils.js';
|
|
2
|
-
import { ContentType } from './validation.js';
|
|
1
|
+
import type { KnownAny } from './utils.js';
|
|
2
|
+
import type { ContentType } from './validation.js';
|
|
3
3
|
type Type = 'object' | 'array' | 'string' | 'number' | 'boolean' | 'null' | 'integer';
|
|
4
4
|
/**
|
|
5
5
|
* Base JSON Schema type used in Vovk.ts for validation and code generation.
|
package/dist/utils/camelCase.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Convert any value to string
|
|
2
|
-
function
|
|
2
|
+
function valToString(value) {
|
|
3
3
|
return value == null ? '' : String(value);
|
|
4
4
|
}
|
|
5
5
|
// Regex to match words (including Unicode letters & digits)
|
|
@@ -18,7 +18,7 @@ function unicodeWords(str) {
|
|
|
18
18
|
* @returns {string}
|
|
19
19
|
*/
|
|
20
20
|
export function camelCase(input) {
|
|
21
|
-
const str =
|
|
21
|
+
const str = valToString(input);
|
|
22
22
|
// replace separators with space
|
|
23
23
|
const sanitized = str.replace(/[\s_-]+/g, ' ').trim();
|
|
24
24
|
const words = unicodeWords(sanitized);
|
|
@@ -24,17 +24,18 @@
|
|
|
24
24
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
25
25
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
26
26
|
*/
|
|
27
|
+
import type { KnownAny } from '../types/utils.js';
|
|
27
28
|
type DeepPartial<T> = {
|
|
28
29
|
[P in keyof T]?: T[P] extends (infer U)[] ? DeepPartial<U>[] : T[P] extends object ? DeepPartial<T[P]> : T[P];
|
|
29
30
|
};
|
|
30
31
|
type SpecificValue = Buffer | Date | RegExp;
|
|
31
|
-
declare function isSpecificValue(val:
|
|
32
|
+
declare function isSpecificValue(val: KnownAny): val is SpecificValue;
|
|
32
33
|
declare function cloneSpecificValue(val: SpecificValue): SpecificValue;
|
|
33
34
|
/**
|
|
34
35
|
* Recursive cloning array.
|
|
35
36
|
*/
|
|
36
|
-
declare function deepCloneArray<T =
|
|
37
|
-
declare function safeGetProperty<T extends object>(object: T, property: PropertyKey):
|
|
37
|
+
declare function deepCloneArray<T = KnownAny>(arr: T[]): T[];
|
|
38
|
+
declare function safeGetProperty<T extends object>(object: T, property: PropertyKey): KnownAny;
|
|
38
39
|
/**
|
|
39
40
|
* Extending object that entered in first argument.
|
|
40
41
|
*
|
|
@@ -48,6 +49,6 @@ declare function deepExtend<T extends object>(...args: [T, ...Partial<T>[]]): T;
|
|
|
48
49
|
declare function deepExtend<T extends object, U extends object>(target: T, source: U): T & U;
|
|
49
50
|
declare function deepExtend<T extends object, U extends object, V extends object>(target: T, source1: U, source2: V): T & U & V;
|
|
50
51
|
declare function deepExtend<T extends object, U extends object, V extends object, W extends object>(target: T, source1: U, source2: V, source3: W): T & U & V & W;
|
|
51
|
-
declare function deepExtend<T extends object>(target: T, ...sources:
|
|
52
|
+
declare function deepExtend<T extends object>(target: T, ...sources: KnownAny[]): T;
|
|
52
53
|
export { deepExtend, deepCloneArray, isSpecificValue, cloneSpecificValue, safeGetProperty };
|
|
53
54
|
export type { DeepPartial, SpecificValue };
|
package/dist/utils/deepExtend.js
CHANGED
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
26
26
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
27
27
|
*/
|
|
28
|
-
'use strict';
|
|
29
28
|
function isSpecificValue(val) {
|
|
30
29
|
return val instanceof Buffer || val instanceof Date || val instanceof RegExp;
|
|
31
30
|
}
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
],
|
|
8
8
|
"main": "./dist/index.js",
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
|
-
"version": "3.0.
|
|
10
|
+
"version": "3.0.2",
|
|
11
11
|
"bin": {
|
|
12
12
|
"vovk-cli-npx": "./bin/index.mjs"
|
|
13
13
|
},
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"postbuild": "chmod +x ./bin/index.mjs",
|
|
43
43
|
"build": "rm -f *.tsbuildinfo && tsc",
|
|
44
44
|
"rm-dist": "rm -rf mjs cjs",
|
|
45
|
-
"lint": "
|
|
45
|
+
"lint": "biome check --fix",
|
|
46
46
|
"tsc": "tsc --noEmit",
|
|
47
47
|
"npm-publish": "if [ -z \"$NPM_TAG\" ]; then echo 'Error: NPM_TAG is not set'; exit 1; fi; npm publish --tag=$NPM_TAG",
|
|
48
48
|
"ncu": "npm-check-updates -u"
|