vovk 3.0.0-draft.99 → 3.0.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 (167) hide show
  1. package/README.md +22 -13
  2. package/bin/index.mjs +10 -0
  3. package/dist/client/createRPC.d.ts +13 -3
  4. package/dist/client/createRPC.js +112 -50
  5. package/dist/client/defaultHandler.d.ts +5 -1
  6. package/dist/client/defaultHandler.js +12 -9
  7. package/dist/client/defaultStreamHandler.d.ts +16 -4
  8. package/dist/client/defaultStreamHandler.js +259 -62
  9. package/dist/client/fetcher.d.ts +41 -3
  10. package/dist/client/fetcher.js +125 -60
  11. package/dist/client/progressive.d.ts +15 -0
  12. package/dist/client/progressive.js +56 -0
  13. package/dist/{utils → client}/serializeQuery.d.ts +2 -2
  14. package/dist/{utils → client}/serializeQuery.js +1 -4
  15. package/dist/core/HttpException.d.ts +16 -0
  16. package/dist/core/HttpException.js +26 -0
  17. package/dist/core/JSONLinesResponder.d.ts +42 -0
  18. package/dist/core/JSONLinesResponder.js +92 -0
  19. package/dist/core/controllersToStaticParams.d.ts +13 -0
  20. package/dist/core/controllersToStaticParams.js +36 -0
  21. package/dist/core/createDecorator.d.ts +12 -0
  22. package/dist/{createDecorator.js → core/createDecorator.js} +18 -12
  23. package/dist/core/decorators.d.ts +59 -0
  24. package/dist/core/decorators.js +132 -0
  25. package/dist/core/getSchema.d.ts +21 -0
  26. package/dist/core/getSchema.js +31 -0
  27. package/dist/core/initSegment.d.ts +33 -0
  28. package/dist/core/initSegment.js +35 -0
  29. package/dist/core/multitenant.d.ts +33 -0
  30. package/dist/core/multitenant.js +132 -0
  31. package/dist/core/resolveGeneratorConfigValues.d.ts +19 -0
  32. package/dist/core/resolveGeneratorConfigValues.js +59 -0
  33. package/dist/{utils → core}/setHandlerSchema.d.ts +2 -2
  34. package/dist/{utils → core}/setHandlerSchema.js +1 -4
  35. package/dist/core/toDownloadResponse.d.ts +11 -0
  36. package/dist/core/toDownloadResponse.js +25 -0
  37. package/dist/core/vovkApp.d.ts +36 -0
  38. package/dist/core/vovkApp.js +316 -0
  39. package/dist/index.d.ts +25 -59
  40. package/dist/index.js +23 -23
  41. package/dist/internal.d.ts +17 -0
  42. package/dist/internal.js +10 -0
  43. package/dist/openapi/error.d.ts +2 -0
  44. package/dist/openapi/error.js +97 -0
  45. package/dist/openapi/openAPIToVovkSchema/applyComponentsSchemas.d.ts +3 -0
  46. package/dist/openapi/openAPIToVovkSchema/applyComponentsSchemas.js +65 -0
  47. package/dist/openapi/openAPIToVovkSchema/index.d.ts +5 -0
  48. package/dist/openapi/openAPIToVovkSchema/index.js +153 -0
  49. package/dist/openapi/openAPIToVovkSchema/inlineRefs.d.ts +9 -0
  50. package/dist/openapi/openAPIToVovkSchema/inlineRefs.js +99 -0
  51. package/dist/openapi/operation.d.ts +10 -0
  52. package/dist/openapi/operation.js +19 -0
  53. package/dist/openapi/tool.d.ts +2 -0
  54. package/dist/openapi/tool.js +12 -0
  55. package/dist/openapi/vovkSchemaToOpenAPI.d.ts +21 -0
  56. package/dist/openapi/vovkSchemaToOpenAPI.js +250 -0
  57. package/dist/req/bufferBody.d.ts +1 -0
  58. package/dist/req/bufferBody.js +30 -0
  59. package/dist/req/parseBody.d.ts +4 -0
  60. package/dist/req/parseBody.js +49 -0
  61. package/dist/req/parseForm.d.ts +1 -0
  62. package/dist/req/parseForm.js +24 -0
  63. package/dist/{utils → req}/parseQuery.d.ts +1 -2
  64. package/dist/{utils → req}/parseQuery.js +2 -5
  65. package/dist/req/reqMeta.d.ts +2 -0
  66. package/dist/{utils → req}/reqMeta.js +1 -4
  67. package/dist/req/reqQuery.d.ts +2 -0
  68. package/dist/req/reqQuery.js +4 -0
  69. package/dist/req/validateContentType.d.ts +1 -0
  70. package/dist/req/validateContentType.js +32 -0
  71. package/dist/samples/createCodeSamples.d.ts +20 -0
  72. package/dist/samples/createCodeSamples.js +293 -0
  73. package/dist/samples/objectToCode.d.ts +8 -0
  74. package/dist/samples/objectToCode.js +38 -0
  75. package/dist/samples/schemaToCode.d.ts +11 -0
  76. package/dist/samples/schemaToCode.js +264 -0
  77. package/dist/samples/schemaToObject.d.ts +2 -0
  78. package/dist/samples/schemaToObject.js +164 -0
  79. package/dist/samples/schemaToTsType.d.ts +2 -0
  80. package/dist/samples/schemaToTsType.js +114 -0
  81. package/dist/tools/ToModelOutput.d.ts +8 -0
  82. package/dist/tools/ToModelOutput.js +10 -0
  83. package/dist/tools/createTool.d.ts +126 -0
  84. package/dist/tools/createTool.js +6 -0
  85. package/dist/tools/createToolFactory.d.ts +135 -0
  86. package/dist/tools/createToolFactory.js +61 -0
  87. package/dist/tools/deriveTools.d.ts +46 -0
  88. package/dist/tools/deriveTools.js +134 -0
  89. package/dist/tools/toModelOutputDefault.d.ts +7 -0
  90. package/dist/tools/toModelOutputDefault.js +7 -0
  91. package/dist/tools/toModelOutputMCP.d.ts +30 -0
  92. package/dist/tools/toModelOutputMCP.js +54 -0
  93. package/dist/tsconfig.tsbuildinfo +1 -0
  94. package/dist/types/client.d.ts +140 -0
  95. package/dist/types/client.js +1 -0
  96. package/dist/types/config.d.ts +151 -0
  97. package/dist/types/config.js +1 -0
  98. package/dist/types/core.d.ts +115 -0
  99. package/dist/types/core.js +1 -0
  100. package/dist/types/enums.d.ts +75 -0
  101. package/dist/{types.js → types/enums.js} +21 -9
  102. package/dist/types/inference.d.ts +117 -0
  103. package/dist/types/inference.js +1 -0
  104. package/dist/types/json-schema.d.ts +51 -0
  105. package/dist/types/json-schema.js +1 -0
  106. package/dist/types/operation.d.ts +5 -0
  107. package/dist/types/operation.js +1 -0
  108. package/dist/types/package.d.ts +544 -0
  109. package/dist/types/package.js +5 -0
  110. package/dist/types/request.d.ts +48 -0
  111. package/dist/types/request.js +1 -0
  112. package/dist/types/standard-schema.d.ts +117 -0
  113. package/dist/types/standard-schema.js +6 -0
  114. package/dist/types/tools.d.ts +43 -0
  115. package/dist/types/tools.js +1 -0
  116. package/dist/types/utils.d.ts +9 -0
  117. package/dist/types/utils.js +1 -0
  118. package/dist/types/validation.d.ts +48 -0
  119. package/dist/types/validation.js +1 -0
  120. package/dist/utils/camelCase.d.ts +6 -0
  121. package/dist/utils/camelCase.js +34 -0
  122. package/dist/utils/deepExtend.d.ts +53 -0
  123. package/dist/utils/deepExtend.js +128 -0
  124. package/dist/utils/fileNameToDisposition.d.ts +1 -0
  125. package/dist/utils/fileNameToDisposition.js +3 -0
  126. package/dist/utils/shim.d.ts +1 -0
  127. package/dist/utils/shim.js +1 -1
  128. package/dist/utils/toKebabCase.d.ts +1 -0
  129. package/dist/utils/toKebabCase.js +5 -0
  130. package/dist/utils/trimPath.d.ts +1 -0
  131. package/dist/utils/trimPath.js +1 -0
  132. package/dist/utils/upperFirst.d.ts +1 -0
  133. package/dist/utils/upperFirst.js +3 -0
  134. package/dist/validation/createStandardValidation.d.ts +268 -0
  135. package/dist/validation/createStandardValidation.js +45 -0
  136. package/dist/validation/createValidateOnClient.d.ts +14 -0
  137. package/dist/validation/createValidateOnClient.js +23 -0
  138. package/dist/validation/procedure.d.ts +261 -0
  139. package/dist/validation/procedure.js +8 -0
  140. package/dist/validation/withValidationLibrary.d.ts +119 -0
  141. package/dist/validation/withValidationLibrary.js +174 -0
  142. package/package.json +44 -10
  143. package/dist/HttpException.d.ts +0 -7
  144. package/dist/HttpException.js +0 -15
  145. package/dist/StreamJSONResponse.d.ts +0 -14
  146. package/dist/StreamJSONResponse.js +0 -57
  147. package/dist/VovkApp.d.ts +0 -29
  148. package/dist/VovkApp.js +0 -188
  149. package/dist/client/index.d.ts +0 -3
  150. package/dist/client/index.js +0 -7
  151. package/dist/client/types.d.ts +0 -104
  152. package/dist/client/types.js +0 -2
  153. package/dist/createDecorator.d.ts +0 -6
  154. package/dist/createVovkApp.d.ts +0 -62
  155. package/dist/createVovkApp.js +0 -118
  156. package/dist/types.d.ts +0 -220
  157. package/dist/utils/generateStaticAPI.d.ts +0 -4
  158. package/dist/utils/generateStaticAPI.js +0 -18
  159. package/dist/utils/getSchema.d.ts +0 -20
  160. package/dist/utils/getSchema.js +0 -33
  161. package/dist/utils/reqForm.d.ts +0 -2
  162. package/dist/utils/reqForm.js +0 -13
  163. package/dist/utils/reqMeta.d.ts +0 -2
  164. package/dist/utils/reqQuery.d.ts +0 -2
  165. package/dist/utils/reqQuery.js +0 -10
  166. package/dist/utils/withValidation.d.ts +0 -20
  167. package/dist/utils/withValidation.js +0 -72
@@ -0,0 +1,19 @@
1
+ import { createDecorator } from '../core/createDecorator.js';
2
+ import { error } from './error.js';
3
+ import { tool } from './tool.js';
4
+ export const operationDecorator = createDecorator(null, (openAPIOperationObject = {}) => {
5
+ return (handlerSchema) => {
6
+ return {
7
+ ...handlerSchema,
8
+ operationObject: {
9
+ ...handlerSchema?.operationObject,
10
+ ...openAPIOperationObject,
11
+ },
12
+ };
13
+ };
14
+ });
15
+ /**
16
+ * OpenAPI operation decorator to add metadata to API operations. Also includes `error` and `tool` utilities.
17
+ * @see https://vovk.dev/openapi
18
+ */
19
+ export const operation = Object.assign(operationDecorator, { error, tool });
@@ -0,0 +1,2 @@
1
+ import type { VovkToolOptions } from '../types/tools.js';
2
+ export declare const tool: (toolOptions: VovkToolOptions) => (target: import("../types/utils.js").KnownAny, propertyKey: string) => void;
@@ -0,0 +1,12 @@
1
+ import { createDecorator } from '../core/createDecorator.js';
2
+ export const tool = createDecorator(null, (toolOptions) => {
3
+ return (handlerSchema) => {
4
+ return {
5
+ ...handlerSchema,
6
+ operationObject: {
7
+ ...handlerSchema?.operationObject,
8
+ 'x-tool': toolOptions,
9
+ },
10
+ };
11
+ };
12
+ });
@@ -0,0 +1,21 @@
1
+ import type { OpenAPIObject } from 'openapi3-ts/oas31';
2
+ import type { VovkOutputConfig, VovkStrictConfig, VovkReadmeConfig, VovkSamplesConfig, VovkPackageJson } from '../types/config.js';
3
+ import type { VovkSchema } from '../types/core.js';
4
+ export declare function vovkSchemaToOpenAPI({ config, rootEntry, schema: fullSchema, outputConfigs, forceOutputConfigs, isBundle, segmentName: givenSegmentName, projectPackageJson, }: {
5
+ config: VovkStrictConfig | undefined;
6
+ rootEntry?: string;
7
+ schema: VovkSchema;
8
+ outputConfigs: VovkOutputConfig[];
9
+ forceOutputConfigs?: VovkOutputConfig[];
10
+ isBundle: boolean;
11
+ segmentName: string | null;
12
+ projectPackageJson: VovkPackageJson | undefined;
13
+ }): {
14
+ readme: VovkReadmeConfig;
15
+ openAPIObject: OpenAPIObject;
16
+ samples: VovkSamplesConfig;
17
+ origin: string;
18
+ package: VovkPackageJson;
19
+ imports: VovkOutputConfig['imports'];
20
+ reExports: VovkOutputConfig['reExports'];
21
+ };
@@ -0,0 +1,250 @@
1
+ import { createCodeSamples } from '../samples/createCodeSamples.js';
2
+ import { schemaToObject } from '../samples/schemaToObject.js';
3
+ import { resolveGeneratorConfigValues } from '../core/resolveGeneratorConfigValues.js';
4
+ import { HttpStatus } from '../types/enums.js';
5
+ function extractComponents(schema) {
6
+ if (!schema)
7
+ return [undefined, {}];
8
+ const components = {};
9
+ // Function to collect components and replace $refs recursively
10
+ const process = (obj, path = []) => {
11
+ if (!obj || typeof obj !== 'object')
12
+ return obj;
13
+ // Handle arrays
14
+ if (Array.isArray(obj)) {
15
+ return obj.map((item) => process(item, path));
16
+ }
17
+ // Create a copy to modify
18
+ const result = {};
19
+ Object.entries({ ...obj.definitions, ...obj.$defs }).forEach(([key, value]) => {
20
+ components[key] = process(value, [...path, key]);
21
+ });
22
+ // Process all properties
23
+ for (const [key, value] of Object.entries(obj ?? {})) {
24
+ // Skip already processed special properties
25
+ if (key === '$defs' || key === 'definitions')
26
+ continue;
27
+ if (key === '$ref' && typeof value === 'string') {
28
+ // Extract the component name from the reference
29
+ const refParts = value.split('/');
30
+ const refName = refParts[refParts.length - 1];
31
+ // Replace with component reference
32
+ result[key] = `#/components/schemas/${refName}`;
33
+ }
34
+ else {
35
+ // Recursively process other properties
36
+ result[key] = process(value, [...path, key]);
37
+ }
38
+ }
39
+ return result;
40
+ };
41
+ const processedSchema = process(schema);
42
+ return [processedSchema, components];
43
+ }
44
+ // returns OpenAPIObject along with resolved configs
45
+ // TODO: Refactor and decompose
46
+ export function vovkSchemaToOpenAPI({ config, rootEntry = 'api', schema: fullSchema, outputConfigs, forceOutputConfigs, isBundle, segmentName: givenSegmentName, projectPackageJson, }) {
47
+ const paths = {};
48
+ const components = {};
49
+ const { openAPIObject, samples: samplesConfig, package: packageJson, readme: readmeConfig, origin, imports, reExports, } = resolveGeneratorConfigValues({
50
+ config,
51
+ outputConfigs,
52
+ forceOutputConfigs,
53
+ isBundle,
54
+ segmentName: givenSegmentName ?? null,
55
+ projectPackageJson,
56
+ });
57
+ for (const [segmentName, segmentSchema] of givenSegmentName
58
+ ? [[givenSegmentName, fullSchema.segments[givenSegmentName]]]
59
+ : Object.entries(fullSchema.segments ?? {})) {
60
+ for (const c of Object.values(segmentSchema.controllers)) {
61
+ for (const [handlerName, h] of Object.entries(c.handlers ?? {})) {
62
+ if (h.operationObject && !h.misc?.isOpenAPIMixin) {
63
+ const [queryValidation, queryComponents] = extractComponents(h?.validation?.query);
64
+ const [bodyValidation, bodyComponents] = extractComponents(h?.validation?.body);
65
+ const [paramsValidation, paramsComponents] = extractComponents(h?.validation?.params);
66
+ const [outputValidation, outputComponents] = extractComponents(h?.validation?.output);
67
+ const [iterationValidation, iterationComponents] = extractComponents(h?.validation?.iteration);
68
+ // TODO: Handle name conflicts?
69
+ Object.assign(components, queryComponents, bodyComponents, paramsComponents, outputComponents, iterationComponents);
70
+ const { ts, rs, py } = createCodeSamples({
71
+ package: packageJson,
72
+ handlerName,
73
+ handlerSchema: h,
74
+ controllerSchema: c,
75
+ config: samplesConfig,
76
+ });
77
+ const queryParameters = queryValidation && 'type' in queryValidation && 'properties' in queryValidation
78
+ ? Object.entries(queryValidation.properties ?? {}).map(([propName, propSchema]) => ({
79
+ name: propName,
80
+ in: 'query',
81
+ required: queryValidation.required ? queryValidation.required.includes(propName) : false,
82
+ schema: propSchema,
83
+ }))
84
+ : null;
85
+ const pathParameters = paramsValidation && 'type' in paramsValidation && 'properties' in paramsValidation
86
+ ? Object.entries(paramsValidation.properties ?? {}).map(([propName, propSchema]) => ({
87
+ name: propName,
88
+ in: 'path',
89
+ required: paramsValidation.required ? paramsValidation.required.includes(propName) : false,
90
+ schema: propSchema,
91
+ }))
92
+ : null;
93
+ const path = h.misc?.originalPath ??
94
+ '/' + [rootEntry.replace(/^\/+|\/+$/g, ''), segmentName, c.prefix, h.path].filter(Boolean).join('/');
95
+ paths[path] = paths[path] ?? {};
96
+ const httpMethod = h.httpMethod.toLowerCase();
97
+ paths[path][httpMethod] ??= {};
98
+ paths[path][httpMethod] = {
99
+ ...h.operationObject,
100
+ ...paths[path][httpMethod],
101
+ 'x-codeSamples': [
102
+ ...(paths[path][httpMethod]['x-codeSamples'] ?? []),
103
+ ...(h.operationObject?.['x-codeSamples'] ?? []),
104
+ {
105
+ label: 'TypeScript RPC',
106
+ lang: 'typescript',
107
+ source: ts,
108
+ },
109
+ {
110
+ label: 'Python RPC',
111
+ lang: 'python',
112
+ source: py,
113
+ },
114
+ {
115
+ label: 'Rust RPC',
116
+ lang: 'rust',
117
+ source: rs,
118
+ },
119
+ ],
120
+ ...(queryParameters || pathParameters
121
+ ? {
122
+ parameters: h.operationObject?.parameters ?? [...(queryParameters || []), ...(pathParameters || [])],
123
+ }
124
+ : {}),
125
+ ...(paths[path][httpMethod].parameters
126
+ ? {
127
+ parameters: paths[path][httpMethod].parameters,
128
+ }
129
+ : {}),
130
+ ...(outputValidation && 'type' in outputValidation
131
+ ? {
132
+ responses: {
133
+ 200: {
134
+ description: 'description' in outputValidation ? outputValidation.description : 'Success',
135
+ content: {
136
+ 'application/json': {
137
+ schema: outputValidation,
138
+ },
139
+ },
140
+ },
141
+ ...h.operationObject?.responses,
142
+ },
143
+ }
144
+ : {}),
145
+ ...(iterationValidation && 'type' in iterationValidation
146
+ ? {
147
+ responses: {
148
+ 200: {
149
+ description: 'description' in iterationValidation ? iterationValidation.description : 'JSON Lines response',
150
+ content: {
151
+ 'application/jsonl': {
152
+ schema: {
153
+ ...iterationValidation,
154
+ examples: iterationValidation.examples ?? [
155
+ [
156
+ JSON.stringify(schemaToObject(iterationValidation)),
157
+ JSON.stringify(schemaToObject(iterationValidation)),
158
+ JSON.stringify(schemaToObject(iterationValidation)),
159
+ ].join('\n'),
160
+ ],
161
+ },
162
+ },
163
+ },
164
+ },
165
+ ...h.operationObject?.responses,
166
+ },
167
+ }
168
+ : {}),
169
+ ...(paths[path][httpMethod].responses
170
+ ? {
171
+ responses: paths[path][httpMethod].responses,
172
+ }
173
+ : {}),
174
+ ...(bodyValidation && 'type' in bodyValidation
175
+ ? {
176
+ requestBody: h.operationObject?.requestBody ?? {
177
+ description: 'description' in bodyValidation ? bodyValidation.description : 'Request body',
178
+ required: true,
179
+ content: bodyValidation['x-contentType']?.length
180
+ ? Object.fromEntries(bodyValidation['x-contentType'].map((ct) => [ct, { schema: bodyValidation }]))
181
+ : {
182
+ 'application/json': {
183
+ schema: bodyValidation,
184
+ },
185
+ },
186
+ },
187
+ }
188
+ : {}),
189
+ ...(paths[path][httpMethod].requestBody
190
+ ? {
191
+ requestBody: paths[path][httpMethod].requestBody,
192
+ }
193
+ : {}),
194
+ tags: paths[path][httpMethod].tags ?? h.operationObject?.tags,
195
+ };
196
+ }
197
+ }
198
+ }
199
+ }
200
+ return {
201
+ readme: readmeConfig,
202
+ samples: samplesConfig,
203
+ package: packageJson,
204
+ imports,
205
+ reExports,
206
+ origin,
207
+ openAPIObject: {
208
+ ...openAPIObject,
209
+ components: {
210
+ ...openAPIObject?.components,
211
+ schemas: {
212
+ ...(openAPIObject?.components?.schemas ?? components),
213
+ HttpStatus: {
214
+ type: 'integer',
215
+ description: 'HTTP status code',
216
+ enum: Object.keys(HttpStatus)
217
+ .map((k) => HttpStatus[k])
218
+ .filter(Boolean)
219
+ .filter((v) => typeof v === 'number'),
220
+ },
221
+ VovkErrorResponse: {
222
+ type: 'object',
223
+ description: 'Vovk error response',
224
+ properties: {
225
+ cause: {
226
+ description: 'Error cause of any shape',
227
+ },
228
+ statusCode: {
229
+ $ref: '#/components/schemas/HttpStatus',
230
+ },
231
+ message: {
232
+ type: 'string',
233
+ description: 'Error message',
234
+ },
235
+ isError: {
236
+ type: 'boolean',
237
+ const: true,
238
+ description: 'Indicates that this object represents an error',
239
+ },
240
+ },
241
+ required: ['statusCode', 'message', 'isError'],
242
+ additionalProperties: false,
243
+ },
244
+ ...openAPIObject?.components?.schemas,
245
+ },
246
+ },
247
+ paths,
248
+ },
249
+ };
250
+ }
@@ -0,0 +1 @@
1
+ export declare function bufferBody<T extends Request>(req: T): Promise<T>;
@@ -0,0 +1,30 @@
1
+ export async function bufferBody(req) {
2
+ const buffer = await req.arrayBuffer();
3
+ Object.defineProperty(req, 'bodyUsed', {
4
+ get: () => false,
5
+ configurable: true,
6
+ });
7
+ Object.defineProperty(req, 'body', {
8
+ get: () => new ReadableStream({
9
+ start(controller) {
10
+ controller.enqueue(new Uint8Array(buffer.slice(0)));
11
+ controller.close();
12
+ },
13
+ }),
14
+ configurable: true,
15
+ });
16
+ req.json = async () => JSON.parse(new TextDecoder().decode(buffer));
17
+ req.text = async () => new TextDecoder().decode(buffer);
18
+ req.blob = async () => new Blob([buffer.slice(0)]);
19
+ req.arrayBuffer = async () => buffer.slice(0);
20
+ req.bytes = async () => new Uint8Array(buffer.slice(0));
21
+ req.formData = async () => {
22
+ const r = new Request('http://localhost', {
23
+ method: req.method,
24
+ headers: req.headers,
25
+ body: buffer.slice(0),
26
+ });
27
+ return r.formData();
28
+ };
29
+ return req;
30
+ }
@@ -0,0 +1,4 @@
1
+ /** Application MIME types that are parsed as text by parseBody. */
2
+ export declare const textTypes: readonly ["application/xml", "application/xhtml+xml", "application/javascript", "application/x-javascript", "application/ecmascript", "application/yaml", "application/x-yaml", "application/graphql", "application/sql", "application/toml", "application/x-ndjson", "application/ndjson", "application/jsonl", "application/jsonlines", "application/x-jsonlines"];
3
+ export declare const textSuffixPattern: RegExp;
4
+ export declare function parseBody(req: Request): Promise<Record<string, unknown> | FormData | URLSearchParams | string | File>;
@@ -0,0 +1,49 @@
1
+ import { parseForm } from './parseForm.js';
2
+ const formTypes = ['multipart/form-data', 'application/x-www-form-urlencoded'];
3
+ /** Application MIME types that are parsed as text by parseBody. */
4
+ export const textTypes = [
5
+ 'application/xml',
6
+ 'application/xhtml+xml',
7
+ 'application/javascript',
8
+ 'application/x-javascript',
9
+ 'application/ecmascript',
10
+ 'application/yaml',
11
+ 'application/x-yaml',
12
+ 'application/graphql',
13
+ 'application/sql',
14
+ 'application/toml',
15
+ 'application/x-ndjson',
16
+ 'application/ndjson',
17
+ 'application/jsonl',
18
+ 'application/jsonlines',
19
+ 'application/x-jsonlines',
20
+ ];
21
+ export const textSuffixPattern = /\+(xml|text|yaml|json-seq)\b/;
22
+ const includes = (ct, types) => types.some((t) => ct.includes(t));
23
+ export async function parseBody(req) {
24
+ const contentType = req.headers?.get('content-type');
25
+ // application/json or +json suffix types (e.g. application/ld+json, application/vnd.api+json) → object
26
+ if (!contentType || contentType.includes('application/json') || contentType.includes('+json')) {
27
+ const body = await req.json();
28
+ req.json = () => Promise.resolve(body);
29
+ return body;
30
+ }
31
+ // multipart/form-data → FormData
32
+ if (includes(contentType, formTypes)) {
33
+ const body = await req.formData();
34
+ req.formData = () => Promise.resolve(body);
35
+ return parseForm(body);
36
+ }
37
+ // text/* or known text-based application types → string
38
+ if (contentType.startsWith('text/') || includes(contentType, textTypes) || textSuffixPattern.test(contentType)) {
39
+ const body = await req.text();
40
+ req.text = () => Promise.resolve(body);
41
+ return body;
42
+ }
43
+ // Everything else (octet-stream, image/*, video/*, application/pdf, etc.) → File
44
+ const disposition = req.headers?.get('content-disposition');
45
+ const fileName = disposition?.match(/filename="(.+?)"/)?.[1] ?? 'file';
46
+ const body = await req.blob();
47
+ req.blob = () => Promise.resolve(body);
48
+ return new File([body], fileName, { type: contentType });
49
+ }
@@ -0,0 +1 @@
1
+ export declare function parseForm<T>(body: FormData): Promise<T>;
@@ -0,0 +1,24 @@
1
+ export async function parseForm(body) {
2
+ const formData = {};
3
+ for (const [key, value] of body.entries()) {
4
+ const existing = formData[key];
5
+ if (value instanceof File) {
6
+ if (existing) {
7
+ formData[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
8
+ }
9
+ else {
10
+ formData[key] = value;
11
+ }
12
+ }
13
+ else {
14
+ const str = value.toString();
15
+ if (existing) {
16
+ formData[key] = Array.isArray(existing) ? [...existing, str] : [existing, str];
17
+ }
18
+ else {
19
+ formData[key] = str;
20
+ }
21
+ }
22
+ }
23
+ return formData;
24
+ }
@@ -1,4 +1,3 @@
1
- import type { KnownAny } from '../types';
2
1
  /**
3
2
  * Deserialize a bracket-based query string into an object.
4
3
  *
@@ -22,4 +21,4 @@ import type { KnownAny } from '../types';
22
21
  * @param queryString - The raw query string (e.g. location.search.slice(1))
23
22
  * @returns - A nested object representing the query params
24
23
  */
25
- export default function parseQuery(queryString: string): Record<string, KnownAny>;
24
+ export declare function parseQuery(queryString: string): Record<string, unknown>;
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = parseQuery;
4
1
  /**
5
2
  * Parse a bracket-based key (e.g. "z[d][0][x]" or "arr[]")
6
3
  * into an array of path segments (strings or special push-markers).
@@ -135,7 +132,7 @@ function setValue(obj, path, value) {
135
132
  * @param queryString - The raw query string (e.g. location.search.slice(1))
136
133
  * @returns - A nested object representing the query params
137
134
  */
138
- function parseQuery(queryString) {
135
+ export function parseQuery(queryString) {
139
136
  const result = {};
140
137
  if (!queryString)
141
138
  return result;
@@ -145,7 +142,7 @@ function parseQuery(queryString) {
145
142
  .split('&');
146
143
  for (const pair of pairs) {
147
144
  const [rawKey, rawVal = ''] = pair.split('=');
148
- const decodedKey = decodeURIComponent(rawKey || '');
145
+ const decodedKey = decodeURIComponent(rawKey);
149
146
  const decodedVal = decodeURIComponent(rawVal);
150
147
  // Parse bracket notation
151
148
  const pathSegments = parseKey(decodedKey);
@@ -0,0 +1,2 @@
1
+ import type { VovkRequest } from '../types/request.js';
2
+ export declare function reqMeta<T = Record<'mcpOutput' | 'xMetaHeader' | (string & {}), unknown>>(req: Partial<VovkRequest>, meta?: T | null): T;
@@ -1,8 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = reqMeta;
4
1
  const metaMap = new WeakMap();
5
- function reqMeta(req, meta) {
2
+ export function reqMeta(req, meta) {
6
3
  if (meta) {
7
4
  metaMap.set(req, { ...metaMap.get(req), ...meta });
8
5
  }
@@ -0,0 +1,2 @@
1
+ import type { VovkRequest } from '../types/request.js';
2
+ export declare function reqQuery<T extends object | undefined>(req: VovkRequest<unknown, T>): T;
@@ -0,0 +1,4 @@
1
+ import { parseQuery } from './parseQuery.js';
2
+ export function reqQuery(req) {
3
+ return parseQuery(req.nextUrl.search);
4
+ }
@@ -0,0 +1 @@
1
+ export declare function validateContentType(request: Request | undefined, allowed: string[]): Response | null;
@@ -0,0 +1,32 @@
1
+ import { HttpException } from '../core/HttpException.js';
2
+ import { HttpStatus } from '../types/enums.js';
3
+ export function validateContentType(request, allowed) {
4
+ // Wildcard — skip validation
5
+ if (!request?.headers || allowed.includes('*/*'))
6
+ return null;
7
+ const raw = request.headers.get('content-type');
8
+ if (!raw) {
9
+ throw new HttpException(HttpStatus.UNSUPPORTED_MEDIA_TYPE, 'Missing Content-Type header', { allowed });
10
+ }
11
+ // Handle comma-separated content types and strip parameters like charset, boundary
12
+ const contentTypes = raw
13
+ .split(',')
14
+ .map((part) => part.split(';')[0].trim().toLowerCase())
15
+ .filter(Boolean);
16
+ const match = contentTypes.some((contentType) => allowed.some((pattern) => {
17
+ const normalized = pattern.toLowerCase();
18
+ // Partial wildcard: image/*, text/*, etc.
19
+ if (normalized.endsWith('/*')) {
20
+ const prefix = normalized.slice(0, -1);
21
+ return contentType.startsWith(prefix);
22
+ }
23
+ return contentType === normalized;
24
+ }));
25
+ if (!match) {
26
+ throw new HttpException(HttpStatus.UNSUPPORTED_MEDIA_TYPE, `Unsupported media type: ${contentTypes.join(', ')}`, {
27
+ contentTypes,
28
+ allowed,
29
+ });
30
+ }
31
+ return null;
32
+ }
@@ -0,0 +1,20 @@
1
+ import type { VovkControllerSchema, VovkHandlerSchema } from '../types/core.js';
2
+ import type { VovkSamplesConfig } from '../types/config.js';
3
+ export type CodeSamplePackageJson = {
4
+ name?: string;
5
+ version?: string;
6
+ description?: string;
7
+ rs_name?: string;
8
+ py_name?: string;
9
+ };
10
+ export declare function createCodeSamples({ handlerName, handlerSchema, controllerSchema, package: packageJson, config, }: {
11
+ handlerName: string;
12
+ handlerSchema: VovkHandlerSchema;
13
+ controllerSchema: VovkControllerSchema;
14
+ package?: CodeSamplePackageJson;
15
+ config: VovkSamplesConfig;
16
+ }): {
17
+ ts: string;
18
+ py: string;
19
+ rs: string;
20
+ };