vovk 3.1.0 → 3.1.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.js +4 -7
- package/dist/core/controllersToStaticParams.js +1 -1
- package/dist/core/decorate.d.ts +43 -0
- package/dist/core/decorate.js +24 -0
- package/dist/core/decorators.js +1 -1
- package/dist/core/getSchema.js +1 -1
- package/dist/core/initSegment.js +18 -22
- package/dist/core/vovkApp.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/types/core.d.ts +1 -1
- package/package.json +1 -1
package/dist/core/compose.js
CHANGED
|
@@ -13,15 +13,12 @@ export function compose(...args) {
|
|
|
13
13
|
}
|
|
14
14
|
// Detect class: native ES class constructors' toString() starts with "class"
|
|
15
15
|
if (last.toString().startsWith('class ')) {
|
|
16
|
-
// Apply class decorators
|
|
17
|
-
|
|
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
18
|
for (let i = decoratorFns.length - 1; i >= 0; i--) {
|
|
19
|
-
|
|
20
|
-
if (transformed !== undefined) {
|
|
21
|
-
result = transformed;
|
|
22
|
-
}
|
|
19
|
+
decoratorFns[i](last);
|
|
23
20
|
}
|
|
24
|
-
return
|
|
21
|
+
return last;
|
|
25
22
|
}
|
|
26
23
|
// Method-level compose: store decorator appliers for deferred execution by initSegment
|
|
27
24
|
const handler = last;
|
|
@@ -13,7 +13,7 @@ export function controllersToStaticParams(c, slug = 'vovk') {
|
|
|
13
13
|
{ [slug]: ['_schema_'] },
|
|
14
14
|
...Object.values(controllers).flatMap((controller) => {
|
|
15
15
|
const handlers = controller._handlers;
|
|
16
|
-
const splitPrefix = controller.
|
|
16
|
+
const splitPrefix = controller.prefix?.split('/') ?? [];
|
|
17
17
|
return Object.entries(handlers ?? {}).flatMap(([name, handler]) => {
|
|
18
18
|
const staticParams = controller._handlersMetadata?.[name]?.staticParams;
|
|
19
19
|
if (staticParams?.length) {
|
|
@@ -0,0 +1,43 @@
|
|
|
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 decorate).
|
|
4
|
+
*/
|
|
5
|
+
export type DecorateMetadata = {
|
|
6
|
+
httpMethod?: string;
|
|
7
|
+
path?: string;
|
|
8
|
+
options?: KnownAny;
|
|
9
|
+
decoratorAppliers?: ((controller: KnownAny, propertyKey: string) => void)[];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Applies decorators to a handler without using decorator syntax.
|
|
13
|
+
* Returns an object with `.handle()` to register the handler function.
|
|
14
|
+
*
|
|
15
|
+
* When the last argument is a procedure result (has `.handle`), its `.handle()` is proxied.
|
|
16
|
+
* Otherwise, `.handle()` wraps a plain handler directly.
|
|
17
|
+
*
|
|
18
|
+
* @example With procedure
|
|
19
|
+
* ```ts
|
|
20
|
+
* static handleParams = decorate(
|
|
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
|
+
* @example Without procedure
|
|
28
|
+
* ```ts
|
|
29
|
+
* static getMethod = decorate(
|
|
30
|
+
* get(),
|
|
31
|
+
* ).handle(async () => {
|
|
32
|
+
* return { method: 'get' };
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function decorate<H extends {
|
|
37
|
+
handle: (...args: KnownAny[]) => KnownAny;
|
|
38
|
+
}>(...args: [...unknown[], H]): {
|
|
39
|
+
handle: H['handle'];
|
|
40
|
+
};
|
|
41
|
+
export declare function decorate(...args: unknown[]): {
|
|
42
|
+
handle: <T extends (...args: KnownAny[]) => KnownAny>(fn: T) => T;
|
|
43
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function decorate(...args) {
|
|
2
|
+
if (args.length === 0)
|
|
3
|
+
throw new Error('decorate() requires at least one argument');
|
|
4
|
+
const last = args[args.length - 1];
|
|
5
|
+
const hasProcedure = typeof last === 'function' && 'handle' in last && typeof last.handle === 'function';
|
|
6
|
+
const procedureResult = hasProcedure ? last : null;
|
|
7
|
+
const decoratorFns = (hasProcedure ? args.slice(0, -1) : args);
|
|
8
|
+
for (const decoratorFn of decoratorFns) {
|
|
9
|
+
if (typeof decoratorFn !== 'function') {
|
|
10
|
+
throw new Error('All decorator arguments to decorate() must be functions');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
handle(fn) {
|
|
15
|
+
const handler = procedureResult ? procedureResult.handle(fn) : fn;
|
|
16
|
+
handler._decorateMetadata = handler._decorateMetadata ?? {};
|
|
17
|
+
handler._decorateMetadata.decoratorAppliers = handler._decorateMetadata.decoratorAppliers ?? [];
|
|
18
|
+
for (const decoratorFn of decoratorFns) {
|
|
19
|
+
handler._decorateMetadata.decoratorAppliers.push(decoratorFn);
|
|
20
|
+
}
|
|
21
|
+
return handler;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
package/dist/core/decorators.js
CHANGED
package/dist/core/getSchema.js
CHANGED
package/dist/core/initSegment.js
CHANGED
|
@@ -5,41 +5,37 @@ export const initSegment = (options) => {
|
|
|
5
5
|
const segmentName = trimPath(options.segmentName ?? '');
|
|
6
6
|
options.segmentName = segmentName;
|
|
7
7
|
const controllerEntries = Object.entries(options.controllers ?? {});
|
|
8
|
-
|
|
8
|
+
const controllerSet = new Set(controllerEntries.map(([, c]) => c));
|
|
9
|
+
// Sort so parent controllers are initialized before their children
|
|
10
|
+
controllerEntries.sort(([, a], [, b]) => {
|
|
11
|
+
return (Number(controllerSet.has(Object.getPrototypeOf(a))) -
|
|
12
|
+
Number(controllerSet.has(Object.getPrototypeOf(b))));
|
|
13
|
+
});
|
|
9
14
|
for (const [rpcModuleName, controller] of controllerEntries) {
|
|
10
15
|
controller._segmentName = segmentName;
|
|
11
16
|
controller._rpcModuleName = rpcModuleName;
|
|
12
17
|
controller._onError = options?.onError;
|
|
13
18
|
controller._onSuccess = options?.onSuccess;
|
|
14
19
|
controller._onBefore = options?.onBefore;
|
|
15
|
-
// Apply
|
|
20
|
+
// Apply deferred decorate() decorator appliers in reverse order (bottom-up, matching stacked decorator semantics)
|
|
16
21
|
for (const key of Object.getOwnPropertyNames(controller)) {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
for (let i = metadata.decoratorAppliers.length - 1; i >= 0; i--) {
|
|
23
|
-
// Call decorator function with (controller, propertyKey) simulating experimental decorator context
|
|
24
|
-
metadata.decoratorAppliers[i](controller, key);
|
|
25
|
-
}
|
|
22
|
+
const appliers = controller[key]?._decorateMetadata
|
|
23
|
+
?.decoratorAppliers;
|
|
24
|
+
if (appliers) {
|
|
25
|
+
for (let i = appliers.length - 1; i >= 0; i--) {
|
|
26
|
+
appliers[i](controller, key);
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// This is needed because cloneControllerMetadata() may run before compose metadata is applied
|
|
32
|
-
// (e.g., when using class-level compose with a parent that uses method-level compose).
|
|
33
|
-
const controllerSet = new Set(controllerEntries.map(([, c]) => c));
|
|
34
|
-
for (const [, controller] of controllerEntries) {
|
|
30
|
+
// Re-clone metadata if this controller extends another registered controller
|
|
31
|
+
// (cloneControllerMetadata() runs at class-definition time, before decorate() metadata is applied)
|
|
35
32
|
const parent = Object.getPrototypeOf(controller);
|
|
36
|
-
if (
|
|
33
|
+
if (controllerSet.has(parent) && parent._handlers) {
|
|
37
34
|
controller._handlers = { ...parent._handlers, ...controller._handlers };
|
|
38
35
|
controller._handlersMetadata = { ...parent._handlersMetadata, ...controller._handlersMetadata };
|
|
39
|
-
Object.values(vovkApp.routes)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
});
|
|
36
|
+
for (const methods of Object.values(vovkApp.routes)) {
|
|
37
|
+
methods.set(controller, { ...(methods.get(parent) ?? {}), ...methods.get(controller) });
|
|
38
|
+
}
|
|
43
39
|
}
|
|
44
40
|
}
|
|
45
41
|
async function GET_DEV(req, data) {
|
package/dist/core/vovkApp.js
CHANGED
|
@@ -194,7 +194,7 @@ class VovkApp {
|
|
|
194
194
|
controllers.forEach((staticMethods, controller) => {
|
|
195
195
|
if (segmentName !== controller._segmentName)
|
|
196
196
|
return;
|
|
197
|
-
const prefix = controller.
|
|
197
|
+
const prefix = controller.prefix ?? '';
|
|
198
198
|
Object.entries(staticMethods ?? {}).forEach(([path, staticMethod]) => {
|
|
199
199
|
const fullPath = [prefix, path].filter(Boolean).join('/');
|
|
200
200
|
handlers[fullPath] = { staticMethod, controller };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { multitenant } from './core/multitenant.js';
|
|
|
5
5
|
export { JSONLinesResponder } from './core/JSONLinesResponder.js';
|
|
6
6
|
export { toDownloadResponse } from './core/toDownloadResponse.js';
|
|
7
7
|
export { get, post, put, patch, del, head, options, prefix, cloneControllerMetadata } from './core/decorators.js';
|
|
8
|
-
export {
|
|
8
|
+
export { decorate } from './core/decorate.js';
|
|
9
9
|
export { progressive } from './client/progressive.js';
|
|
10
10
|
export { fetcher, createFetcher } from './client/fetcher.js';
|
|
11
11
|
export { initSegment } from './core/initSegment.js';
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ export { multitenant } from './core/multitenant.js';
|
|
|
6
6
|
export { JSONLinesResponder } from './core/JSONLinesResponder.js';
|
|
7
7
|
export { toDownloadResponse } from './core/toDownloadResponse.js';
|
|
8
8
|
export { get, post, put, patch, del, head, options, prefix, cloneControllerMetadata } from './core/decorators.js';
|
|
9
|
-
export {
|
|
9
|
+
export { decorate } from './core/decorate.js';
|
|
10
10
|
// client
|
|
11
11
|
export { progressive } from './client/progressive.js';
|
|
12
12
|
export { fetcher, createFetcher } from './client/fetcher.js';
|
package/dist/types/core.d.ts
CHANGED
|
@@ -86,7 +86,7 @@ export type StreamAbortMessage = {
|
|
|
86
86
|
export type VovkControllerInternal = {
|
|
87
87
|
_segmentName: string;
|
|
88
88
|
_rpcModuleName?: VovkControllerSchema['rpcModuleName'];
|
|
89
|
-
|
|
89
|
+
prefix?: VovkControllerSchema['prefix'];
|
|
90
90
|
_handlers: VovkControllerSchema['handlers'];
|
|
91
91
|
_handlersMetadata?: Record<string, {
|
|
92
92
|
staticParams?: Record<string, string>[];
|