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.
Files changed (43) hide show
  1. package/dist/core/compose.d.ts +38 -0
  2. package/dist/core/compose.js +31 -0
  3. package/dist/internal.d.ts +1 -0
  4. package/dist/internal.js +1 -0
  5. package/dist/openapi/openAPIToVovkSchema/applyComponentsSchemas.d.ts +21 -1
  6. package/dist/openapi/openAPIToVovkSchema/applyComponentsSchemas.js +37 -12
  7. package/dist/openapi/openAPIToVovkSchema/index.js +3 -2
  8. package/dist/tsconfig.tsbuildinfo +1 -1
  9. package/package.json +1 -1
  10. package/dist/HttpException.d.ts +0 -7
  11. package/dist/HttpException.js +0 -15
  12. package/dist/JSONLinesResponse.d.ts +0 -14
  13. package/dist/JSONLinesResponse.js +0 -59
  14. package/dist/VovkApp.d.ts +0 -29
  15. package/dist/VovkApp.js +0 -189
  16. package/dist/client/index.d.ts +0 -3
  17. package/dist/client/index.js +0 -7
  18. package/dist/client/types.d.ts +0 -102
  19. package/dist/client/types.js +0 -2
  20. package/dist/createDecorator.d.ts +0 -6
  21. package/dist/createDecorator.js +0 -43
  22. package/dist/createVovkApp.d.ts +0 -62
  23. package/dist/createVovkApp.js +0 -129
  24. package/dist/types.d.ts +0 -239
  25. package/dist/types.js +0 -66
  26. package/dist/utils/generateStaticAPI.d.ts +0 -4
  27. package/dist/utils/generateStaticAPI.js +0 -30
  28. package/dist/utils/getSchema.d.ts +0 -20
  29. package/dist/utils/getSchema.js +0 -33
  30. package/dist/utils/parseQuery.d.ts +0 -25
  31. package/dist/utils/parseQuery.js +0 -156
  32. package/dist/utils/reqForm.d.ts +0 -2
  33. package/dist/utils/reqForm.js +0 -32
  34. package/dist/utils/reqMeta.d.ts +0 -2
  35. package/dist/utils/reqMeta.js +0 -13
  36. package/dist/utils/reqQuery.d.ts +0 -2
  37. package/dist/utils/reqQuery.js +0 -10
  38. package/dist/utils/serializeQuery.d.ts +0 -13
  39. package/dist/utils/serializeQuery.js +0 -65
  40. package/dist/utils/setHandlerSchema.d.ts +0 -4
  41. package/dist/utils/setHandlerSchema.js +0 -15
  42. package/dist/utils/withValidation.d.ts +0 -21
  43. 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
+ }
@@ -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): VovkJSONSchemaBase | VovkJSONSchemaBase[];
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 if it doesn't exist
24
- result.$defs = result.$defs || {};
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
- newObj.$ref = `#/$defs/${componentName}`;
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; // Remove $ref if component not found (Telegram API has Type $refs that is not defined in components)
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
- output: applyComponentsSchemas(output, componentsSchemas, segmentName),
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
  };