vovk 3.4.0 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/compose.d.ts +38 -0
- package/dist/core/compose.js +31 -0
- package/dist/internal.d.ts +1 -0
- package/dist/internal.js +1 -0
- package/dist/openapi/openAPIToVovkSchema/applyComponentsSchemas.d.ts +21 -1
- package/dist/openapi/openAPIToVovkSchema/applyComponentsSchemas.js +37 -12
- package/dist/openapi/openAPIToVovkSchema/index.js +3 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/HttpException.d.ts +0 -7
- package/dist/HttpException.js +0 -15
- package/dist/JSONLinesResponse.d.ts +0 -14
- package/dist/JSONLinesResponse.js +0 -59
- package/dist/VovkApp.d.ts +0 -29
- package/dist/VovkApp.js +0 -189
- package/dist/client/index.d.ts +0 -3
- package/dist/client/index.js +0 -7
- package/dist/client/types.d.ts +0 -102
- package/dist/client/types.js +0 -2
- package/dist/createDecorator.d.ts +0 -6
- package/dist/createDecorator.js +0 -43
- package/dist/createVovkApp.d.ts +0 -62
- package/dist/createVovkApp.js +0 -129
- package/dist/types.d.ts +0 -239
- package/dist/types.js +0 -66
- package/dist/utils/generateStaticAPI.d.ts +0 -4
- package/dist/utils/generateStaticAPI.js +0 -30
- package/dist/utils/getSchema.d.ts +0 -20
- package/dist/utils/getSchema.js +0 -33
- package/dist/utils/parseQuery.d.ts +0 -25
- package/dist/utils/parseQuery.js +0 -156
- package/dist/utils/reqForm.d.ts +0 -2
- package/dist/utils/reqForm.js +0 -32
- package/dist/utils/reqMeta.d.ts +0 -2
- package/dist/utils/reqMeta.js +0 -13
- package/dist/utils/reqQuery.d.ts +0 -2
- package/dist/utils/reqQuery.js +0 -10
- package/dist/utils/serializeQuery.d.ts +0 -13
- package/dist/utils/serializeQuery.js +0 -65
- package/dist/utils/setHandlerSchema.d.ts +0 -4
- package/dist/utils/setHandlerSchema.js +0 -15
- package/dist/utils/withValidation.d.ts +0 -21
- package/dist/utils/withValidation.js +0 -88
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { KnownAny } from '../types/utils.js';
|
|
2
|
+
/**
|
|
3
|
+
* Metadata stored on a handler by HTTP decorators and custom decorators when used outside decorator context (via compose).
|
|
4
|
+
*/
|
|
5
|
+
export type ComposeMetadata = {
|
|
6
|
+
httpMethod?: string;
|
|
7
|
+
path?: string;
|
|
8
|
+
options?: KnownAny;
|
|
9
|
+
decoratorAppliers?: ((controller: KnownAny, propertyKey: string) => void)[];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Composes decorators and a handler/class into a single value.
|
|
13
|
+
*
|
|
14
|
+
* For method-level composition, decorators are stored and applied later by initSegment.
|
|
15
|
+
* For class-level composition, decorators like prefix() and cloneControllerMetadata()
|
|
16
|
+
* are applied immediately to the class in reverse order (matching stacked decorator semantics).
|
|
17
|
+
*
|
|
18
|
+
* @example Method-level
|
|
19
|
+
* ```ts
|
|
20
|
+
* static handleParams = compose(
|
|
21
|
+
* put('x/{foo}/{bar}/y'),
|
|
22
|
+
* authGuard(null),
|
|
23
|
+
* procedure({ params: z.object({ foo: z.string(), bar: z.string() }) })
|
|
24
|
+
* .handle(async (req) => req.vovk.params())
|
|
25
|
+
* );
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @example Class-level
|
|
29
|
+
* ```ts
|
|
30
|
+
* const MyController = compose(
|
|
31
|
+
* prefix('users'),
|
|
32
|
+
* cloneControllerMetadata(),
|
|
33
|
+
* class extends ParentController {}
|
|
34
|
+
* );
|
|
35
|
+
* export default MyController;
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function compose<T>(...args: [...unknown[], T]): T;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function compose(...args) {
|
|
2
|
+
if (args.length === 0)
|
|
3
|
+
throw new Error('compose() requires at least one argument');
|
|
4
|
+
const last = args[args.length - 1];
|
|
5
|
+
const decoratorFns = args.slice(0, -1);
|
|
6
|
+
if (typeof last !== 'function') {
|
|
7
|
+
throw new Error('The last argument to compose() must be a function, handler, or class');
|
|
8
|
+
}
|
|
9
|
+
for (const decoratorFn of decoratorFns) {
|
|
10
|
+
if (typeof decoratorFn !== 'function') {
|
|
11
|
+
throw new Error('All arguments to compose() except the last must be decorator functions');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
// Detect class: native ES class constructors' toString() starts with "class"
|
|
15
|
+
if (last.toString().startsWith('class ')) {
|
|
16
|
+
// Apply class decorators in reverse order (bottom-up, matching stacked decorator semantics).
|
|
17
|
+
// Decorators mutate and return the same class, so .name is preserved.
|
|
18
|
+
for (let i = decoratorFns.length - 1; i >= 0; i--) {
|
|
19
|
+
decoratorFns[i](last);
|
|
20
|
+
}
|
|
21
|
+
return last;
|
|
22
|
+
}
|
|
23
|
+
// Method-level compose: store decorator appliers for deferred execution by initSegment
|
|
24
|
+
const handler = last;
|
|
25
|
+
handler._composeMetadata = handler._composeMetadata ?? {};
|
|
26
|
+
handler._composeMetadata.decoratorAppliers = handler._composeMetadata.decoratorAppliers ?? [];
|
|
27
|
+
for (const decoratorFn of decoratorFns) {
|
|
28
|
+
handler._composeMetadata.decoratorAppliers.push(decoratorFn);
|
|
29
|
+
}
|
|
30
|
+
return handler;
|
|
31
|
+
}
|
package/dist/internal.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { withValidationLibrary } from './validation/withValidationLibrary.js';
|
|
|
5
5
|
export { validationSchemasObjectToSingleValidationSchema } from './validation/validationSchemasObjectToSingleValidationSchema.js';
|
|
6
6
|
export { operation } from './openapi/operation.js';
|
|
7
7
|
export { openAPIToVovkSchema } from './openapi/openAPIToVovkSchema/index.js';
|
|
8
|
+
export { applyComponentsSchemas, reattachMixinDefs, } from './openapi/openAPIToVovkSchema/applyComponentsSchemas.js';
|
|
8
9
|
export { vovkSchemaToOpenAPI } from './openapi/vovkSchemaToOpenAPI.js';
|
|
9
10
|
export { readableStreamToAsyncIterable } from './client/defaultStreamHandler.js';
|
|
10
11
|
export { VovkSchemaIdEnum } from './types/enums.js';
|
package/dist/internal.js
CHANGED
|
@@ -6,6 +6,7 @@ export { withValidationLibrary } from './validation/withValidationLibrary.js';
|
|
|
6
6
|
export { validationSchemasObjectToSingleValidationSchema } from './validation/validationSchemasObjectToSingleValidationSchema.js';
|
|
7
7
|
export { operation } from './openapi/operation.js';
|
|
8
8
|
export { openAPIToVovkSchema } from './openapi/openAPIToVovkSchema/index.js';
|
|
9
|
+
export { applyComponentsSchemas, reattachMixinDefs, } from './openapi/openAPIToVovkSchema/applyComponentsSchemas.js';
|
|
9
10
|
export { vovkSchemaToOpenAPI } from './openapi/vovkSchemaToOpenAPI.js';
|
|
10
11
|
export { readableStreamToAsyncIterable } from './client/defaultStreamHandler.js';
|
|
11
12
|
export { VovkSchemaIdEnum } from './types/enums.js';
|
|
@@ -1,3 +1,23 @@
|
|
|
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
|
|
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
|
+
*/
|
|
15
|
+
export declare function reattachMixinDefs(slot: VovkJSONSchemaBase | undefined, segment: {
|
|
16
|
+
segmentType?: string;
|
|
17
|
+
segmentName: string;
|
|
18
|
+
meta?: {
|
|
19
|
+
openAPIObject?: {
|
|
20
|
+
components?: ComponentsObject;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
}): VovkJSONSchemaBase | VovkJSONSchemaBase[] | undefined;
|
|
@@ -14,14 +14,22 @@ function cloneJSON(obj) {
|
|
|
14
14
|
}
|
|
15
15
|
return result;
|
|
16
16
|
}
|
|
17
|
-
export function applyComponentsSchemas(schema, components, mixinName
|
|
17
|
+
export function applyComponentsSchemas(schema, components, mixinName,
|
|
18
|
+
/**
|
|
19
|
+
* true (default): embed the ref closure in `$defs` (self-contained — for AJV + Rust).
|
|
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
|
+
*/
|
|
23
|
+
emitDefs = true) {
|
|
18
24
|
const key = 'components/schemas';
|
|
19
25
|
if (!components || !Object.keys(components).length)
|
|
20
26
|
return schema;
|
|
21
27
|
// Create a deep copy of the schema
|
|
22
28
|
const result = cloneJSON(schema);
|
|
23
|
-
// Initialize $defs
|
|
24
|
-
|
|
29
|
+
// Initialize $defs only when embedding (self-contained slots).
|
|
30
|
+
if (emitDefs) {
|
|
31
|
+
result.$defs = result.$defs || {};
|
|
32
|
+
}
|
|
25
33
|
// Set to track components we've added to $defs
|
|
26
34
|
const addedComponents = new Set();
|
|
27
35
|
// Process a schema object and replace $refs
|
|
@@ -38,18 +46,22 @@ export function applyComponentsSchemas(schema, components, mixinName) {
|
|
|
38
46
|
if ($ref && typeof $ref === 'string' && $ref.startsWith(`#/${key}/`)) {
|
|
39
47
|
const componentName = $ref.replace(`#/${key}/`, '');
|
|
40
48
|
if (components?.[componentName]) {
|
|
41
|
-
|
|
49
|
+
// Set `x-tsType` so TS resolves the ref without local `$defs`.
|
|
42
50
|
newObj['x-tsType'] ??= `Mixins.${upperFirst(camelCase(mixinName))}.${upperFirst(camelCase(componentName))}`;
|
|
51
|
+
if (emitDefs) {
|
|
52
|
+
// Self-contained slot: local $defs + embedded closure.
|
|
53
|
+
newObj.$ref = `#/$defs/${componentName}`;
|
|
54
|
+
if (!addedComponents.has(componentName)) {
|
|
55
|
+
addedComponents.add(componentName);
|
|
56
|
+
if (result.$defs) {
|
|
57
|
+
result.$defs[componentName] = processSchema(cloneJSON(components[componentName]));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// emitDefs === false: keep `#/components/schemas/X`, no `$defs` (lives once in meta).
|
|
43
62
|
}
|
|
44
63
|
else {
|
|
45
|
-
delete newObj.$ref; //
|
|
46
|
-
}
|
|
47
|
-
// Add the component to $defs if not already added
|
|
48
|
-
if (!addedComponents.has(componentName) && components?.[componentName]) {
|
|
49
|
-
addedComponents.add(componentName);
|
|
50
|
-
if (result.$defs) {
|
|
51
|
-
result.$defs[componentName] = processSchema(cloneJSON(components[componentName]));
|
|
52
|
-
}
|
|
64
|
+
delete newObj.$ref; // $ref to a component not in components (e.g. Telegram API)
|
|
53
65
|
}
|
|
54
66
|
}
|
|
55
67
|
// Process properties recursively
|
|
@@ -63,3 +75,16 @@ export function applyComponentsSchemas(schema, components, mixinName) {
|
|
|
63
75
|
// Process the main schema
|
|
64
76
|
return processSchema(result);
|
|
65
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Re-attach a response slot's `$defs` closure at render time, for generators that
|
|
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
|
+
*/
|
|
83
|
+
export function reattachMixinDefs(slot, segment) {
|
|
84
|
+
if (!slot || segment?.segmentType !== 'mixin')
|
|
85
|
+
return slot;
|
|
86
|
+
const components = segment.meta?.openAPIObject?.components?.schemas;
|
|
87
|
+
if (!components)
|
|
88
|
+
return slot;
|
|
89
|
+
return applyComponentsSchemas(slot, components, segment.segmentName, true);
|
|
90
|
+
}
|
|
@@ -140,10 +140,11 @@ export function openAPIToVovkSchema({ apiRoot, source: { object: openAPIObject }
|
|
|
140
140
|
body: applyComponentsSchemas(body, componentsSchemas, segmentName),
|
|
141
141
|
}),
|
|
142
142
|
...(output && {
|
|
143
|
-
|
|
143
|
+
// Response slot: not validated + typed via x-tsType → skip $defs (dedup).
|
|
144
|
+
output: applyComponentsSchemas(output, componentsSchemas, segmentName, false),
|
|
144
145
|
}),
|
|
145
146
|
...(iteration && {
|
|
146
|
-
iteration: applyComponentsSchemas(iteration, componentsSchemas, segmentName),
|
|
147
|
+
iteration: applyComponentsSchemas(iteration, componentsSchemas, segmentName, false),
|
|
147
148
|
}),
|
|
148
149
|
},
|
|
149
150
|
};
|