@velajs/vela 1.8.1 → 1.8.3
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/container/index.d.ts +1 -1
- package/dist/container/types.d.ts +24 -2
- package/dist/http/crud-bridge.d.ts +90 -0
- package/dist/http/crud-bridge.js +25 -0
- package/dist/http/route.manager.js +17 -14
- package/dist/index.d.ts +1 -1
- package/dist/internal.d.ts +2 -0
- package/dist/internal.js +7 -0
- package/dist/openapi/document.js +31 -2
- package/dist/registry/types.d.ts +25 -5
- package/package.json +1 -1
|
@@ -3,4 +3,4 @@ export { Injectable, Inject, Optional, isInjectable, getScope } from './decorato
|
|
|
3
3
|
export { InjectionToken, ForwardRef, forwardRef, ModuleVisibilityError, MultipleProvidersFoundError, ROOT_MODULE_ID, } from './types';
|
|
4
4
|
export { ModuleRef } from './module-ref';
|
|
5
5
|
export { mixin } from './mixin';
|
|
6
|
-
export type { Type, Token, InjectableOptions, InjectMetadata, ProviderOptions, ProviderRegistration, InjectionTokenOptions, ModuleScope, ContainerOptions, Diagnostics, } from './types';
|
|
6
|
+
export type { Type, Token, InferToken, InferTokens, InjectableOptions, InjectMetadata, ProviderOptions, ProviderRegistration, InjectionTokenOptions, ModuleScope, ContainerOptions, Diagnostics, } from './types';
|
|
@@ -16,6 +16,28 @@ export declare class ForwardRef<T = unknown> {
|
|
|
16
16
|
}
|
|
17
17
|
export declare function forwardRef<T>(factory: () => Token<T>): ForwardRef<T>;
|
|
18
18
|
export type Token<T = any> = Type<T> | InjectionToken<T> | string | symbol;
|
|
19
|
+
/**
|
|
20
|
+
* Maps a single DI `Token<T>` to its resolved value type at the type level:
|
|
21
|
+
* - `InjectionToken<T>` → `T`
|
|
22
|
+
* - `Type<T>` / `Constructor<T>` → `T` (the instance type)
|
|
23
|
+
* - `ForwardRef<T>` → `T`
|
|
24
|
+
* - `string` / `symbol` → `unknown` (runtime-only tokens carry no
|
|
25
|
+
* static type info; the consumer asserts).
|
|
26
|
+
*
|
|
27
|
+
* Used by `AsyncModuleOptions` to give `useFactory` parameters their real
|
|
28
|
+
* types based on the literal `inject` tuple — without `as const` at the call
|
|
29
|
+
* site (relies on the `const` type parameter on the consuming generic).
|
|
30
|
+
*/
|
|
31
|
+
export type InferToken<T> = T extends InjectionToken<infer U> ? U : T extends ForwardRef<infer U> ? U : T extends abstract new (...args: any[]) => infer U ? U : unknown;
|
|
32
|
+
/**
|
|
33
|
+
* Map every position of a `Token[]` tuple to its resolved value type.
|
|
34
|
+
* Pairs with `const Inject extends readonly Token<unknown>[]` generics to
|
|
35
|
+
* give `useFactory(...deps)` parameter types inferred from the literal
|
|
36
|
+
* `inject` array.
|
|
37
|
+
*/
|
|
38
|
+
export type InferTokens<T extends readonly unknown[]> = {
|
|
39
|
+
[K in keyof T]: InferToken<T[K]>;
|
|
40
|
+
};
|
|
19
41
|
export interface InjectableOptions {
|
|
20
42
|
scope?: Scope;
|
|
21
43
|
}
|
|
@@ -30,7 +52,7 @@ export interface ProviderOptions<T = unknown> {
|
|
|
30
52
|
useValue?: T;
|
|
31
53
|
useFactory?: (...args: any[]) => T | Promise<T>;
|
|
32
54
|
useClass?: Type<T>;
|
|
33
|
-
inject?: Token[];
|
|
55
|
+
inject?: readonly Token[];
|
|
34
56
|
useExisting?: Token<T>;
|
|
35
57
|
}
|
|
36
58
|
export interface ProviderRegistration<T = unknown> {
|
|
@@ -46,7 +68,7 @@ export interface ProviderRegistration<T = unknown> {
|
|
|
46
68
|
useValue?: T;
|
|
47
69
|
useFactory?: (...args: any[]) => T | Promise<T>;
|
|
48
70
|
useClass?: Type<T>;
|
|
49
|
-
inject?: Token[];
|
|
71
|
+
inject?: readonly Token[];
|
|
50
72
|
useExisting?: Token<T>;
|
|
51
73
|
}
|
|
52
74
|
export interface ModuleScope {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { Hono } from 'hono';
|
|
2
|
+
import type { Type } from '../container/types';
|
|
3
|
+
import type { CanActivate } from '../pipeline/types';
|
|
4
|
+
import type { OpenApiPathItem } from '../openapi/types';
|
|
5
|
+
/**
|
|
6
|
+
* Context passed to {@link CrudBridge.buildRoutes} when mounting CRUD
|
|
7
|
+
* sub-applications for a `@Crud()`-decorated controller.
|
|
8
|
+
*
|
|
9
|
+
* The bridge receives the framework's already-resolved view of the world so
|
|
10
|
+
* it can produce identical routing behavior to a hand-written controller:
|
|
11
|
+
* the same global prefix is prepended to every path, the same global guard
|
|
12
|
+
* instances run before any generated handler, and the same path-joining
|
|
13
|
+
* helper composes URL segments without double slashes.
|
|
14
|
+
*/
|
|
15
|
+
export interface CrudBridgeRouteContext {
|
|
16
|
+
/** Application-wide path prefix (e.g. `/api`), already normalized. */
|
|
17
|
+
globalPrefix: string;
|
|
18
|
+
/** Global guard instances, pre-resolved from the root container. */
|
|
19
|
+
globalGuards: CanActivate[];
|
|
20
|
+
/** Path joiner used by the framework — handles slash normalization. */
|
|
21
|
+
joinPaths: (...parts: string[]) => string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Context passed to {@link CrudBridge.buildOpenApiPaths} when contributing
|
|
25
|
+
* OpenAPI path items for a `@Crud()`-decorated controller.
|
|
26
|
+
*
|
|
27
|
+
* The bridge is responsible for producing fully-qualified path keys that
|
|
28
|
+
* match the routes it actually mounts at request time. The framework supplies
|
|
29
|
+
* the prefixes so the bridge does not have to re-derive them.
|
|
30
|
+
*/
|
|
31
|
+
export interface CrudBridgeOpenApiContext {
|
|
32
|
+
/** Application-wide path prefix (e.g. `/api`), already normalized. */
|
|
33
|
+
globalPrefix: string;
|
|
34
|
+
/** Controller-level prefix as registered with `@Controller(...)`. */
|
|
35
|
+
controllerPrefix: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Contract between `@velajs/vela` and a CRUD route generator (typically
|
|
39
|
+
* `@velajs/crud`). The bridge inverts the previous dynamic-import pattern,
|
|
40
|
+
* which was incompatible with Cloudflare Workers because esbuild leaves
|
|
41
|
+
* `await import(variable)` calls as runtime imports the Workers loader
|
|
42
|
+
* cannot resolve.
|
|
43
|
+
*
|
|
44
|
+
* A consumer registers an implementation once via {@link registerCrudBridge}
|
|
45
|
+
* — usually as a side effect of importing `@velajs/crud` — and the framework
|
|
46
|
+
* delegates both route building (at bootstrap) and OpenAPI generation
|
|
47
|
+
* (at document creation) to it.
|
|
48
|
+
*/
|
|
49
|
+
export interface CrudBridge {
|
|
50
|
+
/**
|
|
51
|
+
* Mount the CRUD sub-app for a controller with `vela:crud` metadata.
|
|
52
|
+
*
|
|
53
|
+
* Called during {@link RouteManager.build} after all `@Get`/`@Post`/etc.
|
|
54
|
+
* routes have been registered. The bridge is expected to attach generated
|
|
55
|
+
* routes (list, read, create, update, delete, …) onto the supplied Hono
|
|
56
|
+
* app under `ctx.globalPrefix + prefix`.
|
|
57
|
+
*/
|
|
58
|
+
buildRoutes(app: Hono, controller: Type, prefix: string, crudConfig: unknown, ctx: CrudBridgeRouteContext): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Contribute OpenAPI path items for a `@Crud()`-decorated controller.
|
|
61
|
+
*
|
|
62
|
+
* Called once per CRUD controller from `createOpenApiDocument` after the
|
|
63
|
+
* standard `@Get`/`@Post`/etc. paths have been collected. The returned
|
|
64
|
+
* record maps full OpenAPI path strings (e.g. `/api/users/{id}`) to path
|
|
65
|
+
* items, and is merged into the document — entries already present from
|
|
66
|
+
* hand-written handlers are preserved verb-by-verb.
|
|
67
|
+
*/
|
|
68
|
+
buildOpenApiPaths(controller: Type, crudConfig: unknown, ctx: CrudBridgeOpenApiContext): Record<string, OpenApiPathItem>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Register a {@link CrudBridge} implementation. Intended to be called once at
|
|
72
|
+
* import time by the CRUD package; subsequent calls overwrite the previous
|
|
73
|
+
* registration (last-writer-wins). This is deliberate so test harnesses can
|
|
74
|
+
* swap in mocks without a teardown step on the global state.
|
|
75
|
+
*/
|
|
76
|
+
export declare function registerCrudBridge(bridge: CrudBridge): void;
|
|
77
|
+
/**
|
|
78
|
+
* Return the currently registered {@link CrudBridge}, or `undefined` if no
|
|
79
|
+
* bridge has been registered. Consumers using `@Crud()` without a registered
|
|
80
|
+
* bridge will receive a clear install-it error from the framework at build
|
|
81
|
+
* time — see `RouteManager.build`.
|
|
82
|
+
*/
|
|
83
|
+
export declare function getCrudBridge(): CrudBridge | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* Reset the global bridge registration. Test-only — not exported from
|
|
86
|
+
* `@velajs/vela/internal`. Use this in `beforeEach` to keep cases isolated.
|
|
87
|
+
*
|
|
88
|
+
* @internal
|
|
89
|
+
*/
|
|
90
|
+
export declare function _resetCrudBridge(): void;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
let registered;
|
|
2
|
+
/**
|
|
3
|
+
* Register a {@link CrudBridge} implementation. Intended to be called once at
|
|
4
|
+
* import time by the CRUD package; subsequent calls overwrite the previous
|
|
5
|
+
* registration (last-writer-wins). This is deliberate so test harnesses can
|
|
6
|
+
* swap in mocks without a teardown step on the global state.
|
|
7
|
+
*/ export function registerCrudBridge(bridge) {
|
|
8
|
+
registered = bridge;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Return the currently registered {@link CrudBridge}, or `undefined` if no
|
|
12
|
+
* bridge has been registered. Consumers using `@Crud()` without a registered
|
|
13
|
+
* bridge will receive a clear install-it error from the framework at build
|
|
14
|
+
* time — see `RouteManager.build`.
|
|
15
|
+
*/ export function getCrudBridge() {
|
|
16
|
+
return registered;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Reset the global bridge registration. Test-only — not exported from
|
|
20
|
+
* `@velajs/vela/internal`. Use this in `beforeEach` to keep cases isolated.
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/ export function _resetCrudBridge() {
|
|
24
|
+
registered = undefined;
|
|
25
|
+
}
|
|
@@ -4,6 +4,7 @@ import { HttpException } from "../errors/http-exception.js";
|
|
|
4
4
|
import { getMetadata } from "../metadata.js";
|
|
5
5
|
import { joinPaths } from "../registry/paths.js";
|
|
6
6
|
import { ArgumentResolver } from "./argument-resolver.js";
|
|
7
|
+
import { getCrudBridge } from "./crud-bridge.js";
|
|
7
8
|
import { buildMiddlewareExecutionContext } from "./execution-context.js";
|
|
8
9
|
import { HandlerExecutor } from "./handler-executor.js";
|
|
9
10
|
import { instantiate, instantiateMany } from "./instantiate.js";
|
|
@@ -279,23 +280,25 @@ export class RouteManager {
|
|
|
279
280
|
}
|
|
280
281
|
}
|
|
281
282
|
// Second pass: mount CRUD sub-apps (/:id routes registered last).
|
|
283
|
+
//
|
|
284
|
+
// Routes are produced by a registered CrudBridge — usually contributed
|
|
285
|
+
// by `@velajs/crud` as an import side effect. Inversion was deliberate:
|
|
286
|
+
// a previous implementation called `await import('@velajs/crud')` via
|
|
287
|
+
// an indirect variable to dodge esbuild's static analysis, which made
|
|
288
|
+
// Cloudflare Workers unable to resolve the module at runtime. The
|
|
289
|
+
// bridge contract removes that hazard — no runtime resolver involved.
|
|
282
290
|
for (const { controller, metadata } of this.controllers){
|
|
283
291
|
const crudConfig = getMetadata('vela:crud', controller);
|
|
284
|
-
if (crudConfig)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
await buildCrudRoutes(app, controller, metadata.prefix, crudConfig, {
|
|
289
|
-
globalPrefix: this.globalPrefix,
|
|
290
|
-
globalGuards: instantiateMany(this.globalGuards, this.container),
|
|
291
|
-
joinPaths
|
|
292
|
-
});
|
|
293
|
-
} catch (err) {
|
|
294
|
-
throw new Error(`@Crud() requires '@velajs/crud'. Install it: pnpm add @velajs/crud`, {
|
|
295
|
-
cause: err
|
|
296
|
-
});
|
|
297
|
-
}
|
|
292
|
+
if (!crudConfig) continue;
|
|
293
|
+
const bridge = getCrudBridge();
|
|
294
|
+
if (!bridge) {
|
|
295
|
+
throw new Error("@Crud() requires '@velajs/crud'. Install it: pnpm add @velajs/crud");
|
|
298
296
|
}
|
|
297
|
+
await bridge.buildRoutes(app, controller, metadata.prefix, crudConfig, {
|
|
298
|
+
globalPrefix: this.globalPrefix,
|
|
299
|
+
globalGuards: instantiateMany(this.globalGuards, this.container),
|
|
300
|
+
joinPaths
|
|
301
|
+
});
|
|
299
302
|
}
|
|
300
303
|
return app;
|
|
301
304
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type { BootstrapOptions, BootstrapResult } from './factory/bootstrap';
|
|
|
7
7
|
export { createOpenApiDocument, ApiDoc, ApiTags, ApiResponse, zodToJsonSchema, } from './openapi/index';
|
|
8
8
|
export type { OpenApiDocument, OpenApiInfo, OpenApiOperation, OpenApiParameter, OpenApiPathItem, OpenApiRequestBody, OpenApiResponse, ApiDocMetadata, ApiResponseEntry, ApiResponseOptions, CreateOpenApiDocumentOptions, HttpVerb, JsonSchema, } from './openapi/index';
|
|
9
9
|
export { Container, Injectable, Inject, Optional, InjectionToken, ForwardRef, forwardRef, ModuleRef, ModuleVisibilityError, MultipleProvidersFoundError, ROOT_MODULE_ID, mixin, } from './container/index';
|
|
10
|
-
export type { Type, Token, InjectableOptions, ProviderOptions, ModuleScope, ContainerOptions, Diagnostics, } from './container/index';
|
|
10
|
+
export type { Type, Token, InferToken, InferTokens, InjectableOptions, ProviderOptions, ModuleScope, ContainerOptions, Diagnostics, } from './container/index';
|
|
11
11
|
export { METADATA_KEYS, HttpMethod, ParamType, Scope } from './constants';
|
|
12
12
|
export { Controller, Version, Get, Post, Put, Patch, Delete, Options, Head, All, Sse, Param, Query, Body, Headers, Req, Res, Ip, Cookie, Cookies, RawBody, HttpCode, Header, Redirect, createParamDecorator, createLazyParamDecorator, applyDecorators, } from './http/index';
|
|
13
13
|
export { REQUEST_CONTEXT } from './http/request-context';
|
package/dist/internal.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export type { ModuleScope, ContainerOptions, Diagnostics, } from './container/ty
|
|
|
5
5
|
export { bindAppProviders } from './pipeline/app-providers';
|
|
6
6
|
export { RouteManager } from './http/route.manager';
|
|
7
7
|
export type { RouteManagerOptions } from './http/route.manager';
|
|
8
|
+
export { registerCrudBridge, getCrudBridge, } from './http/crud-bridge';
|
|
9
|
+
export type { CrudBridge, CrudBridgeRouteContext, CrudBridgeOpenApiContext, } from './http/crud-bridge';
|
|
8
10
|
export { ModuleLoader } from './module/module-loader';
|
|
9
11
|
export { ComponentManager } from './pipeline/component.manager';
|
|
10
12
|
export { VelaApplication } from './application';
|
package/dist/internal.js
CHANGED
|
@@ -6,6 +6,13 @@ export { ModuleRef } from "./container/module-ref.js";
|
|
|
6
6
|
export { ModuleVisibilityError } from "./container/types.js";
|
|
7
7
|
export { bindAppProviders } from "./pipeline/app-providers.js";
|
|
8
8
|
export { RouteManager } from "./http/route.manager.js";
|
|
9
|
+
// CRUD bridge registry — the integration seam for `@velajs/crud` (and any
|
|
10
|
+
// future CRUD route generator). `@velajs/crud` calls `registerCrudBridge`
|
|
11
|
+
// once at import time; the framework consults `getCrudBridge` whenever it
|
|
12
|
+
// encounters a controller carrying `vela:crud` metadata, both at route
|
|
13
|
+
// build time and at OpenAPI document generation. The types are exposed so
|
|
14
|
+
// integrators can write their own bridges or strongly-typed mocks in tests.
|
|
15
|
+
export { registerCrudBridge, getCrudBridge } from "./http/crud-bridge.js";
|
|
9
16
|
export { ModuleLoader } from "./module/module-loader.js";
|
|
10
17
|
export { ComponentManager } from "./pipeline/component.manager.js";
|
|
11
18
|
export { VelaApplication } from "./application.js";
|
package/dist/openapi/document.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { METADATA_KEYS, ParamType } from "../constants.js";
|
|
2
|
+
import { getCrudBridge } from "../http/crud-bridge.js";
|
|
3
|
+
import { getMetadata } from "../metadata.js";
|
|
1
4
|
import { collectControllers } from "../module/graph.js";
|
|
2
5
|
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
3
6
|
import { joinPaths, toOpenApiPath } from "../registry/paths.js";
|
|
4
|
-
import { ParamType } from "../constants.js";
|
|
5
7
|
import { getApiDoc, getApiResponses, getApiTags } from "./decorators.js";
|
|
6
8
|
import { isOptional, zodToJsonSchema } from "./zod-to-json-schema.js";
|
|
7
9
|
/**
|
|
@@ -198,7 +200,8 @@ export function createOpenApiDocument(rootModule, options = {}) {
|
|
|
198
200
|
const paths = {};
|
|
199
201
|
const globalPrefix = options.globalPrefix ?? '';
|
|
200
202
|
const registry = new ComponentsRegistry();
|
|
201
|
-
|
|
203
|
+
const controllers = collectControllers(rootModule);
|
|
204
|
+
for (const controller of controllers){
|
|
202
205
|
const controllerPath = MetadataRegistry.getControllerPath(controller);
|
|
203
206
|
const routes = MetadataRegistry.getRoutes(controller);
|
|
204
207
|
for (const route of routes){
|
|
@@ -212,6 +215,32 @@ export function createOpenApiDocument(rootModule, options = {}) {
|
|
|
212
215
|
paths[pathString] = pathItem;
|
|
213
216
|
}
|
|
214
217
|
}
|
|
218
|
+
// Second pass: include `@Crud()`-generated routes via the registered
|
|
219
|
+
// CrudBridge. The bridge knows how to turn its own metadata config into
|
|
220
|
+
// OpenAPI path items; this loop is silent when no bridge is registered or
|
|
221
|
+
// no controller carries `vela:crud` metadata, so consumers without
|
|
222
|
+
// `@velajs/crud` see no behavioral change.
|
|
223
|
+
const bridge = getCrudBridge();
|
|
224
|
+
if (bridge) {
|
|
225
|
+
for (const controller of controllers){
|
|
226
|
+
const crudConfig = getMetadata(METADATA_KEYS.CRUD, controller);
|
|
227
|
+
if (!crudConfig) continue;
|
|
228
|
+
const controllerPrefix = MetadataRegistry.getControllerPath(controller);
|
|
229
|
+
const crudPaths = bridge.buildOpenApiPaths(controller, crudConfig, {
|
|
230
|
+
globalPrefix,
|
|
231
|
+
controllerPrefix
|
|
232
|
+
});
|
|
233
|
+
for (const [pathKey, pathItem] of Object.entries(crudPaths)){
|
|
234
|
+
// Verb-level merge: a hand-written `@Get('/')` on the same controller
|
|
235
|
+
// is preserved when the bridge contributes `post`/`patch`/etc. on
|
|
236
|
+
// the same path key.
|
|
237
|
+
paths[pathKey] = {
|
|
238
|
+
...paths[pathKey] ?? {},
|
|
239
|
+
...pathItem
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
215
244
|
const document = {
|
|
216
245
|
openapi: '3.1.0',
|
|
217
246
|
info: {
|
package/dist/registry/types.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Context } from 'hono';
|
|
2
2
|
import type { CanActivate, ExceptionFilter, NestInterceptor, NestMiddleware, PipeTransform } from '../pipeline/types';
|
|
3
|
-
import type { Constructor, ForwardRef, InjectionToken, ProviderOptions, Token, Type } from '../container/types';
|
|
4
|
-
export type { Constructor, ForwardRef, InjectionToken, ProviderOptions, Token, Type };
|
|
3
|
+
import type { Constructor, ForwardRef, InferToken, InferTokens, InjectionToken, ProviderOptions, Token, Type } from '../container/types';
|
|
4
|
+
export type { Constructor, ForwardRef, InferToken, InferTokens, InjectionToken, ProviderOptions, Token, Type };
|
|
5
5
|
export type ComponentType = 'middleware' | 'guard' | 'pipe' | 'interceptor' | 'filter';
|
|
6
6
|
export type MiddlewareType = Type<NestMiddleware> | NestMiddleware;
|
|
7
7
|
export type GuardType = Type<CanActivate> | CanActivate;
|
|
@@ -38,10 +38,30 @@ export interface HttpHandlerMeta {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
export type ModuleImport = Type | DynamicModule | ForwardRef;
|
|
41
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Async module options with **type-inferred `useFactory` parameters**.
|
|
43
|
+
*
|
|
44
|
+
* The `Inject` generic captures the literal `inject` tuple via `const` type
|
|
45
|
+
* parameter inference at the call site (TS 5.0+ — vela already requires it).
|
|
46
|
+
* `useFactory`'s parameter types are computed from that tuple via
|
|
47
|
+
* `InferTokens<Inject>`, so:
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* SomeModule.forRootAsync({
|
|
51
|
+
* inject: [D1Service, ConfigService], // captured as readonly [typeof D1Service, typeof ConfigService]
|
|
52
|
+
* useFactory: (d1, config) => { ... }, // d1: D1Service, config: ConfigService — inferred
|
|
53
|
+
* });
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* Backwards compatible: when `inject` isn't a literal tuple (or is omitted),
|
|
57
|
+
* `Inject` falls back to `readonly Token<unknown>[]`, `InferTokens` resolves
|
|
58
|
+
* to `unknown[]`, and `useFactory` accepts variadic `unknown[]` — the prior
|
|
59
|
+
* loose-typing behavior. Existing callers don't break.
|
|
60
|
+
*/
|
|
61
|
+
export interface AsyncModuleOptions<T = unknown, Inject extends readonly Token<unknown>[] = readonly Token<unknown>[]> {
|
|
42
62
|
imports?: ModuleImport[];
|
|
43
|
-
|
|
44
|
-
|
|
63
|
+
inject?: Inject;
|
|
64
|
+
useFactory: (...args: InferTokens<Inject>) => T | Promise<T>;
|
|
45
65
|
}
|
|
46
66
|
export interface DynamicModule {
|
|
47
67
|
module: Type;
|