vovk 3.0.0-draft.116 → 3.0.0-draft.117
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/VovkApp.js +2 -1
- package/dist/client/createRPC.js +2 -2
- package/dist/createDecorator.js +1 -1
- package/dist/createVovkApp.js +10 -2
- package/dist/types.d.ts +36 -16
- package/dist/utils/generateStaticAPI.js +15 -3
- package/dist/utils/getSchema.d.ts +1 -1
- package/dist/utils/withValidation.d.ts +2 -2
- package/dist/utils/withValidation.js +4 -4
- package/package.json +3 -2
package/dist/VovkApp.js
CHANGED
|
@@ -125,7 +125,8 @@ class VovkApp {
|
|
|
125
125
|
});
|
|
126
126
|
const { handler, methodParams } = this.#getHandler({ handlers, path, params });
|
|
127
127
|
if (!handler) {
|
|
128
|
-
|
|
128
|
+
console.log(path, params);
|
|
129
|
+
return this.#respondWithError(types_1.HttpStatus.NOT_FOUND, `x Route ${path.join('/')} is not found`);
|
|
129
130
|
}
|
|
130
131
|
const { staticMethod, controller } = handler;
|
|
131
132
|
req.vovk = {
|
package/dist/client/createRPC.js
CHANGED
|
@@ -39,11 +39,11 @@ const createRPC = (fullSchema, segmentName, controllerName, options) => {
|
|
|
39
39
|
const fetcher = input.fetcher ?? settingsFetcher;
|
|
40
40
|
const validate = async ({ body, query, params, endpoint, }) => {
|
|
41
41
|
const validateOnClient = input.validateOnClient ?? options?.validateOnClient;
|
|
42
|
-
if (validateOnClient) {
|
|
42
|
+
if (validateOnClient && validation) {
|
|
43
43
|
if (typeof validateOnClient !== 'function') {
|
|
44
44
|
throw new Error('validateOnClient must be a function');
|
|
45
45
|
}
|
|
46
|
-
await validateOnClient({ body, query, params, endpoint }, validation
|
|
46
|
+
await validateOnClient({ body, query, params, endpoint }, validation, fullSchema);
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
49
|
const internalOptions = {
|
package/dist/createDecorator.js
CHANGED
|
@@ -35,7 +35,7 @@ function createDecorator(handler, initHandler) {
|
|
|
35
35
|
// avoid override of path and httpMethod
|
|
36
36
|
...(initResult?.validation ? { validation: initResult.validation } : {}),
|
|
37
37
|
...(initResult?.openapi ? { openapi: initResult.openapi } : {}),
|
|
38
|
-
...(initResult?.
|
|
38
|
+
...(initResult?.misc ? { misc: initResult.misc } : {}),
|
|
39
39
|
},
|
|
40
40
|
};
|
|
41
41
|
};
|
package/dist/createVovkApp.js
CHANGED
|
@@ -10,9 +10,10 @@ const getSchema_1 = __importDefault(require("./utils/getSchema"));
|
|
|
10
10
|
const trimPath = (path) => path.trim().replace(/^\/|\/$/g, '');
|
|
11
11
|
const isClass = (func) => typeof func === 'function' && /class/.test(func.toString());
|
|
12
12
|
const toKebabCase = (str) => str
|
|
13
|
-
.replace(/([A-Z])/g, '-$
|
|
13
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2') // Add hyphen between lowercase/digit and uppercase
|
|
14
|
+
.replace(/([A-Z])([A-Z])(?=[a-z])/g, '$1-$2') // Add hyphen between uppercase letters if the second one is followed by a lowercase
|
|
14
15
|
.toLowerCase()
|
|
15
|
-
.replace(/^-/, '');
|
|
16
|
+
.replace(/^-/, ''); // Remove leading hyphen
|
|
16
17
|
const assignSchema = ({ controller, propertyKey, path, options, httpMethod, vovkApp, }) => {
|
|
17
18
|
if (typeof window !== 'undefined') {
|
|
18
19
|
throw new Error('Decorators are intended for server-side use only. You have probably imported a controller on the client-side.');
|
|
@@ -40,6 +41,13 @@ const assignSchema = ({ controller, propertyKey, path, options, httpMethod, vovk
|
|
|
40
41
|
};
|
|
41
42
|
methods[path] = controller[propertyKey];
|
|
42
43
|
methods[path]._options = options;
|
|
44
|
+
controller._handlersMetadata = {
|
|
45
|
+
...controller._handlersMetadata,
|
|
46
|
+
[propertyKey]: {
|
|
47
|
+
...(controller._handlersMetadata ?? {})[propertyKey],
|
|
48
|
+
staticParams: options?.staticParams,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
43
51
|
};
|
|
44
52
|
function createVovkApp() {
|
|
45
53
|
const vovkApp = new VovkApp_1.VovkApp();
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { NextRequest } from 'next/server';
|
|
2
2
|
import type { OperationObject } from 'openapi3-ts/oas31';
|
|
3
|
+
import type { PackageJson } from 'type-fest';
|
|
3
4
|
import type { JSONLinesResponse } from './JSONLinesResponse';
|
|
4
5
|
import { VovkStreamAsyncIterable } from './client/types';
|
|
5
6
|
export type KnownAny = any;
|
|
@@ -15,7 +16,7 @@ export type VovkHandlerSchema = {
|
|
|
15
16
|
iteration?: KnownAny;
|
|
16
17
|
};
|
|
17
18
|
openapi?: OperationObject;
|
|
18
|
-
|
|
19
|
+
misc?: Record<string, KnownAny>;
|
|
19
20
|
};
|
|
20
21
|
export type VovkControllerSchema = {
|
|
21
22
|
controllerName: string;
|
|
@@ -38,6 +39,9 @@ export type VovkControllerInternal = {
|
|
|
38
39
|
_controllerName?: VovkControllerSchema['controllerName'];
|
|
39
40
|
_prefix?: VovkControllerSchema['prefix'];
|
|
40
41
|
_handlers: VovkControllerSchema['handlers'];
|
|
42
|
+
_handlersMetadata?: Record<string, {
|
|
43
|
+
staticParams?: Record<string, string>[];
|
|
44
|
+
}>;
|
|
41
45
|
_activated?: true;
|
|
42
46
|
_onError?: (err: Error, req: VovkRequest) => void | Promise<void>;
|
|
43
47
|
};
|
|
@@ -47,6 +51,7 @@ export type VovkController = StaticClass & VovkControllerInternal & {
|
|
|
47
51
|
export type DecoratorOptions = {
|
|
48
52
|
cors?: boolean;
|
|
49
53
|
headers?: Record<string, string>;
|
|
54
|
+
staticParams?: Record<string, string>[];
|
|
50
55
|
before?: (this: VovkController, req: VovkRequest) => unknown;
|
|
51
56
|
};
|
|
52
57
|
export type RouteHandler = ((req: VovkRequest, params: Record<string, string>) => Response | Promise<Response> | Iterable<unknown> | AsyncIterable<unknown>) & {
|
|
@@ -99,9 +104,6 @@ export type StreamAbortMessage = {
|
|
|
99
104
|
type LogLevelNames = 'trace' | 'debug' | 'info' | 'warn' | 'error';
|
|
100
105
|
export type VovkEnv = {
|
|
101
106
|
PORT?: string;
|
|
102
|
-
VOVK_GENERATE_FULL_CLIENT?: string;
|
|
103
|
-
VOVK_GENERATE_SEGMENT_CLIENT?: string;
|
|
104
|
-
VOVK_CLIENT_OUT_DIR?: string;
|
|
105
107
|
VOVK_SCHEMA_OUT_DIR?: string;
|
|
106
108
|
VOVK_IMPORTS_FETCHER?: string;
|
|
107
109
|
VOVK_IMPORTS_VALIDATE_ON_CLIENT?: string;
|
|
@@ -117,20 +119,36 @@ export type VovkEnv = {
|
|
|
117
119
|
__VOVK_START_WATCHER_IN_STANDALONE_MODE__?: 'true';
|
|
118
120
|
__VOVK_EXIT__?: 'true' | 'false';
|
|
119
121
|
};
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
type ClientConfigCommon = {
|
|
123
|
+
enabled?: boolean;
|
|
122
124
|
outDir?: string;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
+
fromTemplates?: string[];
|
|
126
|
+
} & ({
|
|
127
|
+
excludeSegments?: never;
|
|
128
|
+
includeSegments?: string[];
|
|
129
|
+
} | {
|
|
130
|
+
excludeSegments?: string[];
|
|
131
|
+
includeSegments?: never;
|
|
132
|
+
});
|
|
133
|
+
type ClientConfigFull = ClientConfigCommon & {
|
|
134
|
+
package?: PackageJson;
|
|
135
|
+
};
|
|
136
|
+
type ClientConfigSegmented = ClientConfigCommon & {
|
|
137
|
+
packages?: Record<string, PackageJson>;
|
|
138
|
+
};
|
|
139
|
+
export type ClientTemplateDef = {
|
|
140
|
+
extends?: string;
|
|
141
|
+
templatePath: string;
|
|
125
142
|
origin?: string | null;
|
|
143
|
+
fullClient?: Omit<ClientConfigFull, 'fromTemplates' | 'enabled'>;
|
|
144
|
+
segmentedClient?: Omit<ClientConfigSegmented, 'fromTemplates' | 'enabled'>;
|
|
145
|
+
requires?: Record<string, string>;
|
|
126
146
|
};
|
|
127
|
-
type GenerateFrom = (string | GenerateFromTemplate)[];
|
|
128
147
|
export type VovkConfig = {
|
|
129
148
|
emitConfig?: boolean | (keyof VovkStrictConfig)[];
|
|
130
|
-
clientOutDir?: string;
|
|
131
149
|
schemaOutDir?: string;
|
|
132
|
-
|
|
133
|
-
|
|
150
|
+
fullClient?: ClientConfigFull;
|
|
151
|
+
segmentedClient?: ClientConfigSegmented;
|
|
134
152
|
imports?: {
|
|
135
153
|
fetcher?: string | [string, string] | [string];
|
|
136
154
|
validateOnClient?: string | [string, string] | [string];
|
|
@@ -143,23 +161,24 @@ export type VovkConfig = {
|
|
|
143
161
|
logLevel?: LogLevelNames;
|
|
144
162
|
prettifyClient?: boolean;
|
|
145
163
|
devHttps?: boolean;
|
|
146
|
-
|
|
147
|
-
|
|
164
|
+
clientTemplateDefs?: Record<string, ClientTemplateDef>;
|
|
165
|
+
moduleTemplates?: {
|
|
148
166
|
service?: string;
|
|
149
167
|
controller?: string;
|
|
150
168
|
[key: string]: string | undefined;
|
|
151
169
|
};
|
|
152
170
|
libs?: Record<string, KnownAny>;
|
|
153
171
|
};
|
|
154
|
-
export type VovkStrictConfig = Required<Omit<VovkConfig, 'emitConfig' | '
|
|
172
|
+
export type VovkStrictConfig = Required<Omit<VovkConfig, 'emitConfig' | 'libs' | 'imports' | 'fullClient' | 'segmentedClient'>> & {
|
|
155
173
|
emitConfig: (keyof VovkStrictConfig)[];
|
|
156
174
|
imports: {
|
|
157
175
|
fetcher: [string, string] | [string];
|
|
158
176
|
validateOnClient: [string, string] | [string] | null;
|
|
159
177
|
createRPC: [string, string] | [string];
|
|
160
178
|
};
|
|
161
|
-
generateFrom: GenerateFrom;
|
|
162
179
|
libs: Record<string, KnownAny>;
|
|
180
|
+
fullClient: RequireFields<ClientConfigFull, 'enabled' | 'fromTemplates' | 'outDir'>;
|
|
181
|
+
segmentedClient: RequireFields<ClientConfigSegmented, 'enabled' | 'fromTemplates' | 'outDir'>;
|
|
163
182
|
};
|
|
164
183
|
export type VovkFullSchema = {
|
|
165
184
|
config: Partial<VovkStrictConfig>;
|
|
@@ -226,4 +245,5 @@ export declare enum HttpStatus {
|
|
|
226
245
|
GATEWAY_TIMEOUT = 504,
|
|
227
246
|
HTTP_VERSION_NOT_SUPPORTED = 505
|
|
228
247
|
}
|
|
248
|
+
type RequireFields<T, K extends keyof T> = T & Required<Pick<T, K>>;
|
|
229
249
|
export {};
|
|
@@ -9,9 +9,21 @@ function generateStaticAPI(c, slug = 'vovk') {
|
|
|
9
9
|
.map((controller) => {
|
|
10
10
|
const handlers = controller._handlers;
|
|
11
11
|
const splitPrefix = controller._prefix?.split('/') ?? [];
|
|
12
|
-
return Object.
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
return Object.entries(handlers)
|
|
13
|
+
.map(([name, handler]) => {
|
|
14
|
+
const staticParams = controller._handlersMetadata?.[name]?.staticParams;
|
|
15
|
+
if (staticParams?.length) {
|
|
16
|
+
return staticParams.map((paramsItem) => {
|
|
17
|
+
let path = handler.path;
|
|
18
|
+
for (const [key, value] of Object.entries(paramsItem)) {
|
|
19
|
+
path = path.replace(`:${key}`, value);
|
|
20
|
+
}
|
|
21
|
+
return { [slug]: [...splitPrefix, ...path.split('/')].filter(Boolean) };
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return [{ [slug]: [...splitPrefix, ...handler.path.split('/')].filter(Boolean) }];
|
|
25
|
+
})
|
|
26
|
+
.flat();
|
|
15
27
|
})
|
|
16
28
|
.flat(),
|
|
17
29
|
];
|
|
@@ -8,7 +8,7 @@ export declare function getControllerSchema(controller: VovkController, controll
|
|
|
8
8
|
path: string;
|
|
9
9
|
httpMethod: string;
|
|
10
10
|
openapi?: import("openapi3-ts/oas31").OperationObject;
|
|
11
|
-
|
|
11
|
+
misc?: Record<string, import("../types").KnownAny>;
|
|
12
12
|
};
|
|
13
13
|
};
|
|
14
14
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { VovkValidationType, type KnownAny, type VovkRequest } from '../types';
|
|
2
|
-
export declare function withValidation<T extends (req: KnownAny, params: KnownAny) => KnownAny, BODY_MODEL, QUERY_MODEL, PARAMS_MODEL, OUTPUT_MODEL, ITERATION_MODEL>({ disableServerSideValidation, skipSchemaEmission,
|
|
2
|
+
export declare function withValidation<T extends (req: KnownAny, params: KnownAny) => KnownAny, BODY_MODEL, QUERY_MODEL, PARAMS_MODEL, OUTPUT_MODEL, ITERATION_MODEL>({ disableServerSideValidation, skipSchemaEmission, validateEachIteration, body, query, params, output, iteration, handle, getJSONSchemaFromModel, validate, }: {
|
|
3
3
|
disableServerSideValidation?: boolean | VovkValidationType[];
|
|
4
4
|
skipSchemaEmission?: boolean | VovkValidationType[];
|
|
5
|
-
|
|
5
|
+
validateEachIteration?: boolean;
|
|
6
6
|
body?: BODY_MODEL;
|
|
7
7
|
query?: QUERY_MODEL;
|
|
8
8
|
params?: PARAMS_MODEL;
|
|
@@ -5,7 +5,7 @@ const HttpException_1 = require("../HttpException");
|
|
|
5
5
|
const types_1 = require("../types");
|
|
6
6
|
const setHandlerSchema_1 = require("./setHandlerSchema");
|
|
7
7
|
const validationTypes = ['body', 'query', 'params', 'output', 'iteration'];
|
|
8
|
-
function withValidation({ disableServerSideValidation, skipSchemaEmission,
|
|
8
|
+
function withValidation({ disableServerSideValidation, skipSchemaEmission, validateEachIteration, body, query, params, output, iteration, handle, getJSONSchemaFromModel, validate, }) {
|
|
9
9
|
const disableServerSideValidationKeys = disableServerSideValidation === false
|
|
10
10
|
? []
|
|
11
11
|
: disableServerSideValidation === true
|
|
@@ -32,7 +32,7 @@ function withValidation({ disableServerSideValidation, skipSchemaEmission, valid
|
|
|
32
32
|
return (async function* () {
|
|
33
33
|
let i = 0;
|
|
34
34
|
for await (const item of data) {
|
|
35
|
-
if (
|
|
35
|
+
if (validateEachIteration || i === 0) {
|
|
36
36
|
await validate(item, iteration, { type: 'iteration', req, status: 200, i });
|
|
37
37
|
}
|
|
38
38
|
i++;
|
|
@@ -40,8 +40,8 @@ function withValidation({ disableServerSideValidation, skipSchemaEmission, valid
|
|
|
40
40
|
}
|
|
41
41
|
})();
|
|
42
42
|
}
|
|
43
|
-
else if (
|
|
44
|
-
throw new HttpException_1.HttpException(types_1.HttpStatus.INTERNAL_SERVER_ERROR, '
|
|
43
|
+
else if (validateEachIteration) {
|
|
44
|
+
throw new HttpException_1.HttpException(types_1.HttpStatus.INTERNAL_SERVER_ERROR, 'validateEachIteration is set but iteration is not defined.');
|
|
45
45
|
}
|
|
46
46
|
return data;
|
|
47
47
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vovk",
|
|
3
|
-
"version": "3.0.0-draft.
|
|
3
|
+
"version": "3.0.0-draft.117",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"module": "dist/index.js",
|
|
6
6
|
"description": "RESTful RPC for Next.js - Transforms Next.js into a powerful REST API platform with RPC capabilities.",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"next": "*"
|
|
31
31
|
},
|
|
32
32
|
"optionalDependencies": {
|
|
33
|
-
"openapi3-ts": "*"
|
|
33
|
+
"openapi3-ts": "*",
|
|
34
|
+
"type-fest": "*"
|
|
34
35
|
}
|
|
35
36
|
}
|