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.
Files changed (82) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/dist/client/create-rpc.d.ts +3 -3
  3. package/dist/client/create-rpc.js +6 -8
  4. package/dist/client/default-stream-handler.d.ts +1 -5
  5. package/dist/client/default-stream-handler.js +53 -45
  6. package/dist/client/fetcher.d.ts +1 -1
  7. package/dist/client/fetcher.js +53 -4
  8. package/dist/client/progressive.d.ts +1 -5
  9. package/dist/client/progressive.js +19 -9
  10. package/dist/client/serialize-query.d.ts +0 -11
  11. package/dist/client/serialize-query.js +4 -28
  12. package/dist/core/controllers-to-static-params.d.ts +1 -2
  13. package/dist/core/controllers-to-static-params.js +1 -2
  14. package/dist/core/create-decorator.d.ts +1 -1
  15. package/dist/core/decorate.d.ts +2 -6
  16. package/dist/core/decorators.js +3 -3
  17. package/dist/core/get-schema.d.ts +1 -1
  18. package/dist/core/get-schema.js +1 -3
  19. package/dist/core/http-exception.d.ts +1 -1
  20. package/dist/core/http-exception.js +1 -1
  21. package/dist/core/init-segment.js +1 -1
  22. package/dist/core/json-lines-responder.d.ts +8 -12
  23. package/dist/core/json-lines-responder.js +42 -20
  24. package/dist/core/vovk-app.d.ts +1 -1
  25. package/dist/core/vovk-app.js +62 -16
  26. package/dist/index.d.ts +16 -18
  27. package/dist/index.js +14 -15
  28. package/dist/internal.d.ts +14 -12
  29. package/dist/internal.js +8 -7
  30. package/dist/openapi/error.js +2 -2
  31. package/dist/openapi/openapi-to-vovk-schema/apply-components-schemas.d.ts +1 -12
  32. package/dist/openapi/openapi-to-vovk-schema/apply-components-schemas.js +4 -10
  33. package/dist/openapi/openapi-to-vovk-schema/index.d.ts +1 -1
  34. package/dist/openapi/openapi-to-vovk-schema/index.js +20 -6
  35. package/dist/openapi/openapi-to-vovk-schema/inline-refs.d.ts +0 -7
  36. package/dist/openapi/openapi-to-vovk-schema/inline-refs.js +2 -13
  37. package/dist/openapi/openapi-to-vovk-schema/prune-components-schemas.d.ts +0 -5
  38. package/dist/openapi/openapi-to-vovk-schema/prune-components-schemas.js +3 -8
  39. package/dist/openapi/vovk-schema-to-openapi.d.ts +1 -1
  40. package/dist/openapi/vovk-schema-to-openapi.js +9 -3
  41. package/dist/req/parse-query.d.ts +0 -23
  42. package/dist/req/parse-query.js +51 -43
  43. package/dist/req/validate-content-type.js +1 -1
  44. package/dist/samples/create-code-samples.d.ts +1 -1
  45. package/dist/samples/create-code-samples.js +4 -3
  46. package/dist/samples/schema-to-code.d.ts +1 -1
  47. package/dist/samples/schema-to-code.js +18 -15
  48. package/dist/samples/schema-to-object.d.ts +1 -1
  49. package/dist/samples/schema-to-object.js +18 -15
  50. package/dist/tools/create-tool-factory.d.ts +2 -2
  51. package/dist/tools/derive-tools.d.ts +9 -13
  52. package/dist/tools/derive-tools.js +48 -47
  53. package/dist/tools/to-model-output-default.d.ts +2 -2
  54. package/dist/tools/to-model-output-default.js +20 -6
  55. package/dist/tools/to-model-output-mcp.d.ts +3 -2
  56. package/dist/tools/to-model-output.d.ts +1 -1
  57. package/dist/types/client.d.ts +4 -4
  58. package/dist/types/config.d.ts +5 -6
  59. package/dist/types/core.d.ts +4 -1
  60. package/dist/types/enums.d.ts +1 -1
  61. package/dist/types/enums.js +1 -1
  62. package/dist/types/inference.d.ts +2 -3
  63. package/dist/types/request.d.ts +1 -5
  64. package/dist/types/standard-schema.js +0 -2
  65. package/dist/types/standard-tool.d.ts +8 -12
  66. package/dist/types/tools.d.ts +2 -40
  67. package/dist/types/validation.d.ts +3 -3
  68. package/dist/utils/camel-case.d.ts +0 -5
  69. package/dist/utils/camel-case.js +2 -10
  70. package/dist/utils/deep-extend.d.ts +1 -10
  71. package/dist/utils/deep-extend.js +16 -4
  72. package/dist/validation/create-standard-validation.d.ts +3 -126
  73. package/dist/validation/create-standard-validation.js +2 -3
  74. package/dist/validation/create-validate-on-client.d.ts +3 -3
  75. package/dist/validation/json-schema-only-spec.d.ts +14 -0
  76. package/dist/validation/json-schema-only-spec.js +57 -0
  77. package/dist/validation/procedure.d.ts +1 -124
  78. package/dist/validation/validation-schemas-object-to-single-validation-schema.d.ts +0 -9
  79. package/dist/validation/validation-schemas-object-to-single-validation-schema.js +11 -14
  80. package/dist/validation/with-validation-library.d.ts +2 -2
  81. package/dist/validation/with-validation-library.js +11 -5
  82. package/package.json +3 -10
@@ -1,5 +1,9 @@
1
- import { ToModelOutput } from './to-model-output.js';
1
+ import { jsonSchemasObjectToSingleJSONSchemaOnlySpec, jsonSchemaToJSONSchemaOnlySpec, } from '../validation/json-schema-only-spec.js';
2
2
  import { validationSchemasObjectToSingleValidationSchema } from '../validation/validation-schemas-object-to-single-validation-schema.js';
3
+ import { ToModelOutput } from './to-model-output.js';
4
+ function isAsyncIterable(value) {
5
+ return typeof value?.[Symbol.asyncIterator] === 'function';
6
+ }
3
7
  async function caller({ handler, handlerName, body, query, params, meta, toModelOutput }, tool) {
4
8
  if (!handler.isRPC && !handler.fn) {
5
9
  throw new Error('Handler is not a valid RPC or controller method');
@@ -28,10 +32,26 @@ async function caller({ handler, handlerName, body, query, params, meta, toModel
28
32
  else {
29
33
  throw new Error(`Unable to call handler "${handlerName}". It's neither RPC nor controller method with "fn" interface.`);
30
34
  }
31
- return [await toModelOutput(result, tool, req), req];
35
+ // a streaming handler yields its items, collect them so the model sees data instead of an iterator
36
+ if (isAsyncIterable(result)) {
37
+ const items = [];
38
+ for await (const item of result)
39
+ items.push(item);
40
+ result = items;
41
+ }
42
+ return [
43
+ await toModelOutput(result, tool, req),
44
+ req,
45
+ null,
46
+ ];
32
47
  }
33
48
  catch (e) {
34
- return [await toModelOutput(e, tool, null), null];
49
+ // report the error separately, the formatted output is never an Error instance
50
+ return [
51
+ await toModelOutput(e, tool, null),
52
+ null,
53
+ e,
54
+ ];
35
55
  }
36
56
  }
37
57
  const makeTool = ({ moduleName, handlerName, module, meta, toModelOutput, onExecute, onError, }) => {
@@ -43,16 +63,25 @@ const makeTool = ({ moduleName, handlerName, module, meta, toModelOutput, onExec
43
63
  throw new Error(`Handler "${handlerName}" not found in module "${moduleName}".`);
44
64
  }
45
65
  const { schema, definition } = handler;
66
+ const name = schema?.operationObject?.['x-tool']?.name ?? `${moduleName}_${handlerName}`;
46
67
  const inputSchemas = Object.fromEntries(['body', 'query', 'params'].map((key) => [key, definition?.[key]]).filter(([, value]) => Boolean(value)));
47
- const inputSchema = Object.keys(inputSchemas).length > 0 ? validationSchemasObjectToSingleValidationSchema(inputSchemas) : undefined;
48
- if (!schema?.operationObject) {
49
- throw new Error(`Handler "${handlerName}" in module "${moduleName}" does not have a valid schema.`);
50
- }
68
+ // prefer real Standard Schemas from `definition` (procedures); RPC modules only have JSON Schemas
69
+ // in `schema.validation`, so wrap those (envelope-only validate, see json-schema-only-spec.ts)
70
+ const inputSchema = Object.keys(inputSchemas).length > 0
71
+ ? validationSchemasObjectToSingleValidationSchema(inputSchemas)
72
+ : jsonSchemasObjectToSingleJSONSchemaOnlySpec({
73
+ schemas: {
74
+ body: schema?.validation?.body,
75
+ query: schema?.validation?.query,
76
+ params: schema?.validation?.params,
77
+ },
78
+ });
79
+ const outputSchema = definition?.output ??
80
+ (schema?.validation?.output ? jsonSchemaToJSONSchemaOnlySpec({ jsonSchema: schema.validation.output }) : undefined);
51
81
  const execute = async (input) => {
52
82
  const { body, query, params } = input;
53
83
  const callerInput = {
54
84
  schema,
55
- inputSchemas,
56
85
  handler,
57
86
  body,
58
87
  query,
@@ -62,55 +91,32 @@ const makeTool = ({ moduleName, handlerName, module, meta, toModelOutput, onExec
62
91
  moduleName,
63
92
  toModelOutput,
64
93
  };
65
- const [result, req] = await caller(callerInput, tool);
66
- if (result instanceof Error) {
67
- onError(result, tool, req);
94
+ const [result, req, error] = await caller(callerInput, tool);
95
+ if (error) {
96
+ onError(error, tool, req);
68
97
  }
69
98
  else {
70
99
  onExecute(result, tool, req);
71
100
  }
72
101
  return result;
73
102
  };
74
- const parametersProperties = {
75
- ...(schema?.validation?.body
76
- ? {
77
- body: schema.validation.body,
78
- }
79
- : {}),
80
- ...(schema?.validation?.query
81
- ? {
82
- query: schema.validation.query,
83
- }
84
- : {}),
85
- ...(schema?.validation?.params
86
- ? {
87
- params: schema.validation.params,
88
- }
89
- : {}),
90
- };
91
103
  const tool = {
92
- type: 'function',
93
104
  execute,
94
- name: schema.operationObject?.['x-tool']?.name ?? `${moduleName}_${handlerName}`,
105
+ name,
95
106
  inputSchema: inputSchema,
96
- outputSchema: definition?.output,
97
- title: schema.operationObject?.['x-tool']?.title ?? schema.operationObject?.summary,
98
- description: schema.operationObject?.['x-tool']?.description ??
99
- ([schema.operationObject?.summary ?? '', schema.operationObject?.description ?? ''].filter(Boolean).join('\n') ||
107
+ outputSchema: outputSchema,
108
+ title: schema?.operationObject?.['x-tool']?.title ?? schema?.operationObject?.summary,
109
+ description: schema?.operationObject?.['x-tool']?.description ??
110
+ ([schema?.operationObject?.summary ?? '', schema?.operationObject?.description ?? '']
111
+ .filter(Boolean)
112
+ .join('\n') ||
100
113
  handlerName),
101
- parameters: {
102
- type: 'object',
103
- properties: parametersProperties,
104
- required: Object.keys(parametersProperties),
105
- additionalProperties: false,
106
- },
107
- inputSchemas,
108
114
  };
109
115
  return tool;
110
116
  };
111
117
  export function deriveTools(options) {
112
118
  const { modules, meta, toModelOutput = ToModelOutput.DEFAULT, onExecute = (result) => result, onError = () => { }, } = options;
113
- const tools = Object.entries(modules ?? {}).flatMap(([moduleName, module]) => {
119
+ return Object.entries(modules ?? {}).flatMap(([moduleName, module]) => {
114
120
  return Object.entries(module ?? {})
115
121
  .filter(([, handler]) => handler?.schema?.operationObject && !handler?.schema?.operationObject?.['x-tool']?.hidden)
116
122
  .map(([handlerName]) => makeTool({
@@ -123,9 +129,4 @@ export function deriveTools(options) {
123
129
  onError,
124
130
  }));
125
131
  });
126
- const toolsByName = Object.fromEntries(tools.map((tool) => [tool.name, tool]));
127
- return {
128
- tools,
129
- toolsByName,
130
- };
131
132
  }
@@ -1,7 +1,7 @@
1
1
  import type { VovkRequest } from '../types/request.js';
2
- import type { VovkTool } from '../types/tools.js';
2
+ import type { StandardToolV0 } from '../types/standard-tool.js';
3
3
  export type DefaultModelOutput<T> = T | {
4
4
  error: string;
5
5
  };
6
- export type ToModelOutputDefaultFn = <TInput, TOutput>(result: TOutput | Error, tool: VovkTool<TInput, TOutput, unknown>, req: Pick<VovkRequest, 'vovk'> | null) => DefaultModelOutput<TOutput>;
6
+ export type ToModelOutputDefaultFn = <TInput, TOutput>(result: TOutput | Error, tool: StandardToolV0<TInput, TOutput, unknown>, req: Pick<VovkRequest, 'vovk'> | null) => Promise<DefaultModelOutput<TOutput>>;
7
7
  export declare const toModelOutputDefault: ToModelOutputDefaultFn;
@@ -1,7 +1,21 @@
1
- export const toModelOutputDefault = (result,
2
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
3
- _tool,
4
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
5
- _req) => {
6
- return result instanceof Error ? { error: result.message } : result;
1
+ const toBase64 = (buf) => typeof Buffer !== 'undefined'
2
+ ? Buffer.from(buf).toString('base64')
3
+ : btoa([...new Uint8Array(buf)].map((b) => String.fromCharCode(b)).join(''));
4
+ // a Response has no enumerable keys, so handing it to an SDK would serialize it as {} and leave the body unread
5
+ async function responseToData(res) {
6
+ const mimeType = res.headers.get('Content-Type')?.split(';')[0].trim() || '';
7
+ if (mimeType === 'application/json' || mimeType.endsWith('+json')) {
8
+ return res.json();
9
+ }
10
+ if (mimeType.startsWith('text/') || /xml|javascript|yaml/.test(mimeType)) {
11
+ return res.text();
12
+ }
13
+ return { mimeType, data: toBase64(await res.arrayBuffer()) };
14
+ }
15
+ export const toModelOutputDefault = async (result, _tool, _req) => {
16
+ if (result instanceof Error)
17
+ return { error: result.message };
18
+ if (result instanceof Response)
19
+ return (await responseToData(result));
20
+ return result;
7
21
  };
@@ -1,5 +1,6 @@
1
1
  import type { VovkRequest } from '../types/request.js';
2
- import type { VovkTool } from '../types/tools.js';
2
+ import type { StandardToolV0 } from '../types/standard-tool.js';
3
+ import type { KnownAny } from '../types/utils.js';
3
4
  export type MCPModelOutput = {
4
5
  content: [
5
6
  {
@@ -25,6 +26,6 @@ export type MCPModelOutput = {
25
26
  };
26
27
  isError?: boolean;
27
28
  };
28
- type ToModelOutputMCPFn = <TOutput>(result: TOutput | Error, tool: VovkTool, req: Pick<VovkRequest, 'vovk'> | null) => Promise<MCPModelOutput>;
29
+ type ToModelOutputMCPFn = <TOutput>(result: TOutput | Error, tool: StandardToolV0<KnownAny, KnownAny, KnownAny>, req: Pick<VovkRequest, 'vovk'> | null) => Promise<MCPModelOutput>;
29
30
  export declare const toModelOutputMCP: ToModelOutputMCPFn;
30
31
  export {};
@@ -4,5 +4,5 @@
4
4
  */
5
5
  export declare const ToModelOutput: {
6
6
  readonly DEFAULT: import("./to-model-output-default.js").ToModelOutputDefaultFn;
7
- readonly MCP: <TOutput>(result: TOutput | Error, tool: import("../index.js").VovkTool, req: Pick<import("../index.js").VovkRequest, "vovk"> | null) => Promise<import("./to-model-output-mcp.js").MCPModelOutput>;
7
+ readonly MCP: <TOutput>(result: TOutput | Error, tool: import("../internal.js").StandardToolV0<import("../types/utils.js").KnownAny, import("../types/utils.js").KnownAny, import("../types/utils.js").KnownAny>, req: Pick<import("../index.js").VovkRequest, "vovk"> | null) => Promise<import("./to-model-output-mcp.js").MCPModelOutput>;
8
8
  };
@@ -1,11 +1,11 @@
1
1
  import type { NextResponse } from 'next/server.js';
2
- import type { JSONLinesResponder } from '../core/json-lines-responder.js';
3
- import type { defaultStreamHandler } from '../client/default-stream-handler.js';
4
2
  import type { defaultHandler } from '../client/default-handler.js';
5
- import type { VovkRequest } from './request.js';
3
+ import type { defaultStreamHandler } from '../client/default-stream-handler.js';
4
+ import type { JSONLinesResponder } from '../core/json-lines-responder.js';
6
5
  import type { ControllerStaticMethod, VovkControllerSchema, VovkHandlerSchema, VovkSchema, VovkSegmentSchema } from './core.js';
7
- import type { IsEmptyObject, KnownAny, Prettify } from './utils.js';
8
6
  import type { HttpMethod } from './enums.js';
7
+ import type { VovkRequest } from './request.js';
8
+ import type { IsEmptyObject, KnownAny, Prettify } from './utils.js';
9
9
  import type { BodyTypeFromContentType, ContentType, VovkValidateOnClient } from './validation.js';
10
10
  type OmitNullable<T> = {
11
11
  [K in keyof T as T[K] extends null | undefined ? never : K]: T[K];
@@ -1,7 +1,7 @@
1
1
  import type { OpenAPIObject } from 'openapi3-ts/oas31';
2
- import type { PackageJson } from './package.js';
3
2
  import type { HttpMethod, VovkSchemaIdEnum } from './enums.js';
4
3
  import type { VovkOperationObject } from './operation.js';
4
+ import type { PackageJson } from './package.js';
5
5
  import type { KnownAny, RequireFields } from './utils.js';
6
6
  export type VovkPackageJson = PackageJson & {
7
7
  rs_name?: string;
@@ -86,17 +86,16 @@ export interface VovkOpenAPIMixin {
86
86
  object: OpenAPIObject;
87
87
  };
88
88
  apiRoot?: string;
89
- getModuleName?: 'nestjs-operation-id' | (string & {}) | 'api' | GetOpenAPINameFn;
90
- getMethodName?: 'nestjs-operation-id' | 'camel-case-operation-id' | 'auto' | GetOpenAPINameFn;
89
+ getModuleName?: (string & {}) | 'api' | GetOpenAPINameFn;
90
+ getMethodName?: 'camel-case-operation-id' | 'auto' | GetOpenAPINameFn;
91
91
  /**
92
92
  * Keep only operations the predicate returns `true` for; omitted = keep all. Runs before
93
93
  * `getModuleName`/`getMethodName`, so a module whose operations are all filtered out is never created.
94
94
  */
95
95
  filterOperations?: (config: Parameters<GetOpenAPINameFn>[0]) => boolean;
96
96
  /**
97
- * Prune `meta.openAPIObject.components.schemas` to the transitive `$ref` closure of the kept operations,
98
- * shrinking the generated schema for large specs. Removes `Mixins.<Segment>.<Component>` types for
99
- * components nothing kept references — keep `false` (default) if you import such types directly.
97
+ * Prune `meta.openAPIObject.components.schemas` to the `$ref` closure of kept operations (shrinks big
98
+ * specs); removes unreferenced `Mixins.*` types, keep `false` (default) if you import those directly.
100
99
  */
101
100
  pruneComponents?: boolean;
102
101
  errorMessageKey?: string;
@@ -1,11 +1,11 @@
1
1
  import type { OpenAPIObject } from 'openapi3-ts/oas31';
2
+ import type { Responder } from '../core/json-lines-responder.js';
2
3
  import type { VovkPackageJson, VovkStrictConfig } from './config.js';
3
4
  import type { HttpStatus, VovkSchemaIdEnum } from './enums.js';
4
5
  import type { VovkJSONSchemaBase } from './json-schema.js';
5
6
  import type { VovkOperationObject } from './operation.js';
6
7
  import type { VovkRequest } from './request.js';
7
8
  import type { KnownAny, RequireFields, StaticClass } from './utils.js';
8
- import type { Responder } from '../core/json-lines-responder.js';
9
9
  /**
10
10
  * Schema for an individual handler, available as MyRPC.myHandler.schema and MyController.myHandler.schema
11
11
  */
@@ -106,6 +106,9 @@ export type DecoratorOptions = {
106
106
  };
107
107
  export type RouteHandler = ((req: VovkRequest, params: Record<string, string>) => Response | Promise<Response> | Responder | Promise<Responder> | Iterable<unknown> | AsyncIterable<unknown>) & {
108
108
  _options?: DecoratorOptions;
109
+ _sourceMethod?: {
110
+ wrapper?: RouteHandler;
111
+ };
109
112
  };
110
113
  export type ControllerStaticMethod<REQ extends VovkRequest = VovkRequest, TParams extends {
111
114
  [key: string]: string;
@@ -56,7 +56,7 @@ export declare enum HttpStatus {
56
56
  UNPROCESSABLE_ENTITY = 422,
57
57
  FAILED_DEPENDENCY = 424,
58
58
  PRECONDITION_REQUIRED = 428,
59
- TOO_MANY_TRequestS = 429,
59
+ TOO_MANY_REQUESTS = 429,
60
60
  INTERNAL_SERVER_ERROR = 500,
61
61
  NOT_IMPLEMENTED = 501,
62
62
  BAD_GATEWAY = 502,
@@ -58,7 +58,7 @@ export var HttpStatus;
58
58
  HttpStatus[HttpStatus["UNPROCESSABLE_ENTITY"] = 422] = "UNPROCESSABLE_ENTITY";
59
59
  HttpStatus[HttpStatus["FAILED_DEPENDENCY"] = 424] = "FAILED_DEPENDENCY";
60
60
  HttpStatus[HttpStatus["PRECONDITION_REQUIRED"] = 428] = "PRECONDITION_REQUIRED";
61
- HttpStatus[HttpStatus["TOO_MANY_TRequestS"] = 429] = "TOO_MANY_TRequestS";
61
+ HttpStatus[HttpStatus["TOO_MANY_REQUESTS"] = 429] = "TOO_MANY_REQUESTS";
62
62
  HttpStatus[HttpStatus["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
63
63
  HttpStatus[HttpStatus["NOT_IMPLEMENTED"] = 501] = "NOT_IMPLEMENTED";
64
64
  HttpStatus[HttpStatus["BAD_GATEWAY"] = 502] = "BAD_GATEWAY";
@@ -113,9 +113,8 @@ type _OmitUndefinedOrUnknown<T> = {
113
113
  [K in keyof T as unknown extends T[K] ? never : [T[K]] extends [undefined] ? never : K]: T[K];
114
114
  };
115
115
  /**
116
- * Utility type to extract the full input (params, query, body) from both controller and client methods.
117
- * Only includes properties that are actually defined — omits keys whose types resolve to `undefined` or `unknown`.
118
- * Useful for Next.js server actions and other cases where you need all input types at once.
116
+ * Extracts the full input (params, query, body) from both controller and client methods,
117
+ * omitting keys that resolve to `undefined` or `unknown`.
119
118
  * @see https://vovk.dev/inference
120
119
  * @example
121
120
  * ```ts
@@ -8,11 +8,7 @@ export type VovkRequestCookie = {
8
8
  [key: string]: string;
9
9
  };
10
10
  /**
11
- * The Vovk.ts request object, extending Next.js's NextRequest
12
- * Accepts three generic parameters:
13
- * - TBody: the expected shape of the request body (default: unknown)
14
- * - TQuery: the expected shape of the query parameters (default: unknown)
15
- * - TParams: the expected shape of the route parameters (default: unknown)
11
+ * The Vovk.ts request object extending Next.js's NextRequest, generics: TBody, TQuery, TParams.
16
12
  * @see https://vovk.dev/procedure
17
13
  */
18
14
  export interface VovkRequest<TBody = unknown, TQuery = unknown, TParams = unknown> extends Request {
@@ -1,5 +1,3 @@
1
- /* eslint-disable @typescript-eslint/no-empty-object-type */
2
- /* eslint-disable @typescript-eslint/no-namespace */
3
1
  // #########################
4
2
  // ### Standard Typed ###
5
3
  // #########################
@@ -1,17 +1,13 @@
1
- import type { CombinedSpec } from './validation.js';
2
- import type { KnownAny } from './utils.js';
1
+ import type { StandardJSONSchemaV1, StandardSchemaV1 } from './standard-schema.js';
3
2
  /**
4
- * The `standard-tool` convention (https://github.com/finom/standard-tool): a neutral LLM tool shape —
5
- * `name`, `description`, optional `inputSchema`/`outputSchema`, `execute`. Vendored as a type only
6
- * (no logic) so `VovkTool` can extend it with zero added dependencies. Kept identical to the
7
- * `StandardTool` type published by `standard-tool` (`meta` is `KnownAny`, i.e. its `any`).
3
+ * Vendored `standard-tool` type (https://standard-tool.js.org), no dependency, no logic.
4
+ * Keep it identical to the published `StandardToolV0` interface.
8
5
  */
9
- export interface StandardTool<Input = unknown, Output = unknown, FormattedOutput = Output | {
10
- error: string;
11
- }> {
6
+ export interface StandardToolV0<Input = unknown, Output = unknown, FormattedOutput = Output, Meta = unknown> {
12
7
  name: string;
8
+ title?: string;
13
9
  description: string;
14
- inputSchema?: CombinedSpec<Input>;
15
- outputSchema?: CombinedSpec<Output>;
16
- execute(input: Input, meta?: KnownAny): FormattedOutput | Promise<FormattedOutput>;
10
+ inputSchema?: StandardSchemaV1<Input> & StandardJSONSchemaV1<Input>;
11
+ outputSchema?: StandardSchemaV1<Output> & StandardJSONSchemaV1<Output>;
12
+ execute(input: Input, meta?: Meta): FormattedOutput | Promise<FormattedOutput>;
17
13
  }
@@ -1,44 +1,6 @@
1
- import type { VovkJSONSchemaBase } from './json-schema.js';
2
1
  import type { VovkRequest } from './request.js';
3
- import type { CombinedSpec } from './validation.js';
4
- import type { StandardTool } from './standard-tool.js';
5
- import type { KnownAny } from './utils.js';
6
- export type ToModelOutputFn<TInput, TOutput, TFormattedOutput> = (result: TOutput | Error, tool: VovkTool<TInput, TOutput, unknown>, req: Pick<VovkRequest, 'vovk'> | null) => TFormattedOutput | Promise<TFormattedOutput>;
7
- /**
8
- * Vovk tool — produced by both `deriveTools` (procedures → tools) and
9
- * `createTool` (standalone tools); both return the same shape. Extends the
10
- * {@link StandardTool} convention with Vovk specifics (`title`, `parameters`, `type`).
11
- * @see https://vovk.dev/tools
12
- */
13
- export interface VovkTool<TInput = KnownAny, TOutput = KnownAny, TFormattedOutput = KnownAny> extends StandardTool<TInput, TOutput, TFormattedOutput> {
14
- title: string | undefined;
15
- parameters: {
16
- type?: 'object';
17
- properties?: {
18
- body?: VovkJSONSchemaBase;
19
- query?: VovkJSONSchemaBase;
20
- params?: VovkJSONSchemaBase;
21
- };
22
- required?: ('body' | 'query' | 'params')[];
23
- additionalProperties?: false;
24
- };
25
- inputSchema: TInput extends undefined ? undefined : CombinedSpec<TInput>;
26
- outputSchema: TOutput extends undefined ? undefined : CombinedSpec<TOutput>;
27
- /**
28
- * Per-slot Standard Schemas, populated only when the tool was built via
29
- * `deriveTools` from a procedure. Always `undefined` for tools built via
30
- * `createTool`.
31
- *
32
- * @deprecated Use {@link VovkTool.inputSchema} (a merged Standard Schema)
33
- * instead. This field will be removed in the next major version.
34
- */
35
- inputSchemas?: {
36
- body?: CombinedSpec;
37
- query?: CombinedSpec;
38
- params?: CombinedSpec;
39
- };
40
- type: 'function';
41
- }
2
+ import type { StandardToolV0 } from './standard-tool.js';
3
+ export type ToModelOutputFn<TInput, TOutput, TFormattedOutput> = (result: TOutput | Error, tool: StandardToolV0<TInput, TOutput, unknown>, req: Pick<VovkRequest, 'vovk'> | null) => TFormattedOutput | Promise<TFormattedOutput>;
42
4
  export type VovkToolOptions = {
43
5
  hidden?: boolean;
44
6
  name?: string;
@@ -1,7 +1,7 @@
1
- import type { StandardSchemaV1, StandardJSONSchemaV1 } from './standard-schema.js';
2
- import type { KnownAny } from './utils.js';
3
- import type { VovkHandlerSchema, VovkSchema } from './core.js';
4
1
  import type { textTypes } from '../req/parse-body.js';
2
+ import type { VovkHandlerSchema, VovkSchema } from './core.js';
3
+ import type { StandardJSONSchemaV1, StandardSchemaV1 } from './standard-schema.js';
4
+ import type { KnownAny } from './utils.js';
5
5
  export interface CombinedProps<Input = unknown, Output = Input> extends StandardSchemaV1.Props<Input, Output>, StandardJSONSchemaV1.Props<Input, Output> {
6
6
  }
7
7
  /**
@@ -1,6 +1 @@
1
- /**
2
- * Converts string to camel case.
3
- * @param {*} input - The value to convert to camel case.
4
- * @returns {string}
5
- */
6
1
  export declare function camelCase(input: string): string;
@@ -4,19 +4,11 @@ function valToString(value) {
4
4
  }
5
5
  // Regex to match words (including Unicode letters & digits)
6
6
  const reUnicodeWord = /[\p{Lu}]{2,}(?=[\p{Lu}][\p{Ll}]+[0-9]*|\b)|[\p{Lu}]?[\p{Ll}]+[0-9]*|[\p{Lu}]|[0-9]+/gu;
7
- /**
8
- * Splits string into an array of words based on Unicode word boundaries
9
- * @param {string} str
10
- * @returns {string[]}
11
- */
7
+ // splits string into words based on Unicode word boundaries
12
8
  function unicodeWords(str) {
13
9
  return str.match(reUnicodeWord) || [];
14
10
  }
15
- /**
16
- * Converts string to camel case.
17
- * @param {*} input - The value to convert to camel case.
18
- * @returns {string}
19
- */
11
+ // converts string to camel case
20
12
  export function camelCase(input) {
21
13
  const str = valToString(input);
22
14
  // replace separators with space
@@ -36,19 +36,10 @@ declare function cloneSpecificValue(val: SpecificValue): SpecificValue;
36
36
  */
37
37
  declare function deepCloneArray<T = KnownAny>(arr: T[]): T[];
38
38
  declare function safeGetProperty<T extends object>(object: T, property: PropertyKey): KnownAny;
39
- /**
40
- * Extending object that entered in first argument.
41
- *
42
- * Returns extended object or false if have no target object or incorrect type.
43
- *
44
- * If you wish to clone source object (without modify it), just use empty new
45
- * object as first argument, like this:
46
- * deepExtend({}, yourObj_1, [yourObj_N]);
47
- */
48
39
  declare function deepExtend<T extends object>(...args: [T, ...Partial<T>[]]): T;
49
40
  declare function deepExtend<T extends object, U extends object>(target: T, source: U): T & U;
50
41
  declare function deepExtend<T extends object, U extends object, V extends object>(target: T, source1: U, source2: V): T & U & V;
51
42
  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;
52
43
  declare function deepExtend<T extends object>(target: T, ...sources: KnownAny[]): T;
53
- export { deepExtend, deepCloneArray, isSpecificValue, cloneSpecificValue, safeGetProperty };
54
44
  export type { DeepPartial, SpecificValue };
45
+ export { cloneSpecificValue, deepCloneArray, deepExtend, isSpecificValue, safeGetProperty };
@@ -1,4 +1,3 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
1
  /*!
3
2
  * @description Recursive object extending
4
3
  * @author Viacheslav Lotsmanov <lotsmanov89@gmail.com>
@@ -28,6 +27,11 @@
28
27
  function isSpecificValue(val) {
29
28
  return val instanceof Buffer || val instanceof Date || val instanceof RegExp;
30
29
  }
30
+ // class instances like Headers or AbortSignal have no enumerable keys and must not be fake-cloned into {}
31
+ function isPlainObject(val) {
32
+ const proto = Object.getPrototypeOf(val);
33
+ return proto === Object.prototype || proto === null;
34
+ }
31
35
  function cloneSpecificValue(val) {
32
36
  if (val instanceof Buffer) {
33
37
  const x = Buffer.alloc ? Buffer.alloc(val.length) : Buffer.from(val);
@@ -57,6 +61,9 @@ function deepCloneArray(arr) {
57
61
  else if (isSpecificValue(item)) {
58
62
  clone[index] = cloneSpecificValue(item);
59
63
  }
64
+ else if (!isPlainObject(item)) {
65
+ clone[index] = item;
66
+ }
60
67
  else {
61
68
  clone[index] = deepExtend({}, item);
62
69
  }
@@ -110,8 +117,13 @@ function deepExtend(...args) {
110
117
  target[key] = cloneSpecificValue(val);
111
118
  return;
112
119
  }
113
- // overwrite by new value if source isn't object or array
114
- else if (typeof src !== 'object' || src === null || Array.isArray(src)) {
120
+ // pass class instances by reference, cloning by enumerable keys would produce {}
121
+ else if (!isPlainObject(val)) {
122
+ target[key] = val;
123
+ return;
124
+ }
125
+ // overwrite by new value if source isn't a plain object or is an array
126
+ else if (typeof src !== 'object' || src === null || Array.isArray(src) || !isPlainObject(src)) {
115
127
  target[key] = deepExtend({}, val);
116
128
  return;
117
129
  }
@@ -124,4 +136,4 @@ function deepExtend(...args) {
124
136
  });
125
137
  return target;
126
138
  }
127
- export { deepExtend, deepCloneArray, isSpecificValue, cloneSpecificValue, safeGetProperty };
139
+ export { cloneSpecificValue, deepCloneArray, deepExtend, isSpecificValue, safeGetProperty };