@velajs/vela 1.8.0 → 1.8.2
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/CHANGELOG.md +5 -5
- package/dist/application.d.ts +0 -10
- package/dist/application.js +1 -23
- package/dist/container/index.d.ts +1 -1
- package/dist/container/types.d.ts +24 -2
- package/dist/factory.js +0 -12
- package/dist/index.d.ts +2 -2
- package/dist/lifecycle/index.d.ts +0 -20
- package/dist/lifecycle/index.js +0 -3
- package/dist/registry/types.d.ts +25 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 1.
|
|
3
|
+
## 1.8.1 (2026-05-14)
|
|
4
4
|
|
|
5
|
-
###
|
|
5
|
+
### Revert
|
|
6
6
|
|
|
7
|
-
- **`OnFirstRequest` lifecycle hook.**
|
|
7
|
+
- **`OnFirstRequest` lifecycle hook removed.** Shipped in 1.8.0 to bridge module-load and request-time semantics for runtime-bound state (Cloudflare bindings, etc.). In practice, consumers can achieve the same deferral with a plain `@Injectable()` service holding the state as a lazy-cached field via a getter — the idiomatic NestJS pattern, no framework primitive required. Adding a lifecycle hook with zero in-tree consumers was YAGNI; reverting before it accumulates dependents. Apps that pinned to 1.8.0 and used `OnFirstRequest` should migrate to the lazy-service pattern before upgrading.
|
|
8
8
|
|
|
9
9
|
### Notes
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
11
|
+
- 1.8.0 remains published on npm but no longer the `latest` tag.
|
|
12
|
+
- `OnApplicationBootstrap`, `OnModuleInit`, and the existing lifecycle hooks are unchanged.
|
|
13
13
|
|
|
14
14
|
## [1.6.0]
|
|
15
15
|
|
package/dist/application.d.ts
CHANGED
|
@@ -9,7 +9,6 @@ export declare class VelaApplication {
|
|
|
9
9
|
private readonly routeManager;
|
|
10
10
|
private instances;
|
|
11
11
|
private honoApp;
|
|
12
|
-
private firstRequestPromise;
|
|
13
12
|
constructor(container: Container, routeManager: RouteManager);
|
|
14
13
|
/** Pre-build routes (handles async CRUD imports). Called by VelaFactory. */
|
|
15
14
|
initRoutes(): Promise<void>;
|
|
@@ -37,14 +36,5 @@ export declare class VelaApplication {
|
|
|
37
36
|
mountOpenApi(options: MountOpenApiOptions): this;
|
|
38
37
|
callOnModuleInit(): Promise<void>;
|
|
39
38
|
callOnApplicationBootstrap(): Promise<void>;
|
|
40
|
-
/**
|
|
41
|
-
* Fire `OnFirstRequest` hooks exactly once across the application's
|
|
42
|
-
* lifetime. Auto-invoked by a global middleware on the first HTTP request;
|
|
43
|
-
* also safe to call manually from non-HTTP entry points (CLI, tests).
|
|
44
|
-
*
|
|
45
|
-
* Concurrent calls share the same in-flight promise — vela guarantees
|
|
46
|
-
* each instance's `onFirstRequest()` runs at most once.
|
|
47
|
-
*/
|
|
48
|
-
callOnFirstRequest(): Promise<void>;
|
|
49
39
|
close(signal?: string): Promise<void>;
|
|
50
40
|
}
|
package/dist/application.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import { hasBeforeApplicationShutdown, hasOnApplicationBootstrap, hasOnApplicationShutdown,
|
|
1
|
+
import { hasBeforeApplicationShutdown, hasOnApplicationBootstrap, hasOnApplicationShutdown, hasOnModuleDestroy, hasOnModuleInit } from "./lifecycle/index.js";
|
|
2
2
|
import { renderScalarUi } from "./openapi/scalar-ui.js";
|
|
3
3
|
export class VelaApplication {
|
|
4
4
|
container;
|
|
5
5
|
routeManager;
|
|
6
6
|
instances = [];
|
|
7
7
|
honoApp = null;
|
|
8
|
-
// Memoizes the first-request hook fire. Concurrent first requests share
|
|
9
|
-
// this promise so the hook runs exactly once.
|
|
10
|
-
firstRequestPromise;
|
|
11
8
|
constructor(container, routeManager){
|
|
12
9
|
this.container = container;
|
|
13
10
|
this.routeManager = routeManager;
|
|
@@ -92,25 +89,6 @@ export class VelaApplication {
|
|
|
92
89
|
}
|
|
93
90
|
}
|
|
94
91
|
}
|
|
95
|
-
/**
|
|
96
|
-
* Fire `OnFirstRequest` hooks exactly once across the application's
|
|
97
|
-
* lifetime. Auto-invoked by a global middleware on the first HTTP request;
|
|
98
|
-
* also safe to call manually from non-HTTP entry points (CLI, tests).
|
|
99
|
-
*
|
|
100
|
-
* Concurrent calls share the same in-flight promise — vela guarantees
|
|
101
|
-
* each instance's `onFirstRequest()` runs at most once.
|
|
102
|
-
*/ callOnFirstRequest() {
|
|
103
|
-
if (!this.firstRequestPromise) {
|
|
104
|
-
this.firstRequestPromise = (async ()=>{
|
|
105
|
-
for (const instance of this.instances){
|
|
106
|
-
if (hasOnFirstRequest(instance)) {
|
|
107
|
-
await instance.onFirstRequest();
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
})();
|
|
111
|
-
}
|
|
112
|
-
return this.firstRequestPromise;
|
|
113
|
-
}
|
|
114
92
|
async close(signal) {
|
|
115
93
|
const reversed = [
|
|
116
94
|
...this.instances
|
|
@@ -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 {
|
package/dist/factory.js
CHANGED
|
@@ -6,18 +6,6 @@ export const VelaFactory = {
|
|
|
6
6
|
const app = new VelaApplication(container, routeManager);
|
|
7
7
|
const instances = await loader.resolveAllInstances();
|
|
8
8
|
app.setInstances(instances);
|
|
9
|
-
// Register the first-request middleware AFTER user-supplied middleware
|
|
10
|
-
// (registered by bootstrap from `options.middleware`). Ordering matters:
|
|
11
|
-
// runtime adapters like `@velajs/cloudflare` install binding-init
|
|
12
|
-
// middleware that must run BEFORE OnFirstRequest hooks read those
|
|
13
|
-
// bindings. Vela's middleware is appended last → fires after all user
|
|
14
|
-
// middleware, before route handlers (which are registered separately).
|
|
15
|
-
routeManager.useGlobalMiddleware({
|
|
16
|
-
use: async (_c, next)=>{
|
|
17
|
-
await app.callOnFirstRequest();
|
|
18
|
-
await next();
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
9
|
await app.callOnModuleInit();
|
|
22
10
|
await app.callOnApplicationBootstrap();
|
|
23
11
|
// Build routes (async — supports CRUD integration with dynamic imports)
|
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';
|
|
@@ -41,7 +41,7 @@ export type { Constructor, MiddlewareType, GuardType, PipeType, InterceptorType,
|
|
|
41
41
|
export { ParseIntPipe, ParseFloatPipe, ParseBoolPipe, ParseUUIDPipe, ParseEnumPipe, ParseArrayPipe, DefaultValuePipe, RequiredPipe, ZodValidationPipe, } from './pipeline/index';
|
|
42
42
|
export type { ParseUUIDPipeOptions, ParseArrayPipeOptions } from './pipeline/index';
|
|
43
43
|
export { HttpException, BadRequestException, UnauthorizedException, ForbiddenException, NotFoundException, MethodNotAllowedException, NotAcceptableException, RequestTimeoutException, ConflictException, GoneException, PayloadTooLargeException, UnsupportedMediaTypeException, UnprocessableEntityException, TooManyRequestsException, InternalServerErrorException, NotImplementedException, BadGatewayException, ServiceUnavailableException, GatewayTimeoutException, } from './errors/index';
|
|
44
|
-
export type { OnModuleInit, OnApplicationBootstrap,
|
|
44
|
+
export type { OnModuleInit, OnApplicationBootstrap, OnModuleDestroy, OnApplicationShutdown, BeforeApplicationShutdown, } from './lifecycle/index';
|
|
45
45
|
export { MetadataRegistry } from './registry/metadata.registry';
|
|
46
46
|
export { createZodDto, ValidationPipe } from './validation/index';
|
|
47
47
|
export type { CreateZodDtoOptions } from './validation/index';
|
|
@@ -4,25 +4,6 @@ export interface OnModuleInit {
|
|
|
4
4
|
export interface OnApplicationBootstrap {
|
|
5
5
|
onApplicationBootstrap(): void | Promise<void>;
|
|
6
6
|
}
|
|
7
|
-
/**
|
|
8
|
-
* Fires exactly once, on the first incoming HTTP request, AFTER user-supplied
|
|
9
|
-
* middleware (so binding initializers like `@velajs/cloudflare`'s env capture
|
|
10
|
-
* run first) and BEFORE route handlers. The hook bridges module-load and
|
|
11
|
-
* request-time semantics — use it for state that depends on values only
|
|
12
|
-
* available at request time (Cloudflare bindings, Deno Deploy env, etc.).
|
|
13
|
-
*
|
|
14
|
-
* Concurrency: vela memoizes the call. Concurrent first requests all await
|
|
15
|
-
* the same promise; the hook runs exactly once.
|
|
16
|
-
*
|
|
17
|
-
* Non-HTTP consumers (CLI, tests) can trigger it manually via
|
|
18
|
-
* `app.callOnFirstRequest()`.
|
|
19
|
-
*
|
|
20
|
-
* Edge-safe: the hook itself is just `() => void | Promise<void>`. The
|
|
21
|
-
* runtime contract is async/Promise — no Node-only APIs are involved.
|
|
22
|
-
*/
|
|
23
|
-
export interface OnFirstRequest {
|
|
24
|
-
onFirstRequest(): void | Promise<void>;
|
|
25
|
-
}
|
|
26
7
|
export interface OnModuleDestroy {
|
|
27
8
|
onModuleDestroy(): void | Promise<void>;
|
|
28
9
|
}
|
|
@@ -34,7 +15,6 @@ export interface BeforeApplicationShutdown {
|
|
|
34
15
|
}
|
|
35
16
|
export declare function hasOnModuleInit(instance: unknown): instance is OnModuleInit;
|
|
36
17
|
export declare function hasOnApplicationBootstrap(instance: unknown): instance is OnApplicationBootstrap;
|
|
37
|
-
export declare function hasOnFirstRequest(instance: unknown): instance is OnFirstRequest;
|
|
38
18
|
export declare function hasOnModuleDestroy(instance: unknown): instance is OnModuleDestroy;
|
|
39
19
|
export declare function hasBeforeApplicationShutdown(instance: unknown): instance is BeforeApplicationShutdown;
|
|
40
20
|
export declare function hasOnApplicationShutdown(instance: unknown): instance is OnApplicationShutdown;
|
package/dist/lifecycle/index.js
CHANGED
|
@@ -4,9 +4,6 @@ export function hasOnModuleInit(instance) {
|
|
|
4
4
|
export function hasOnApplicationBootstrap(instance) {
|
|
5
5
|
return instance !== null && typeof instance === 'object' && typeof instance.onApplicationBootstrap === 'function';
|
|
6
6
|
}
|
|
7
|
-
export function hasOnFirstRequest(instance) {
|
|
8
|
-
return instance !== null && typeof instance === 'object' && typeof instance.onFirstRequest === 'function';
|
|
9
|
-
}
|
|
10
7
|
export function hasOnModuleDestroy(instance) {
|
|
11
8
|
return instance !== null && typeof instance === 'object' && typeof instance.onModuleDestroy === 'function';
|
|
12
9
|
}
|
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;
|