@velajs/vela 1.17.0 → 1.18.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.
- package/dist/http/argument-resolver.js +1 -1
- package/dist/http/request-container.d.ts +16 -0
- package/dist/http/request-container.js +19 -0
- package/dist/http/route.manager.js +6 -5
- package/dist/http/types.d.ts +6 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/live/index.d.ts +1 -1
- package/dist/live/index.js +1 -1
- package/dist/live/live.invalidation.d.ts +18 -0
- package/dist/live/live.invalidation.js +43 -0
- package/dist/live/live.module.js +6 -2
- package/dist/openapi/document.js +2 -2
- package/dist/registry/types.d.ts +6 -0
- package/package.json +1 -1
|
@@ -65,7 +65,7 @@ export class ArgumentResolver {
|
|
|
65
65
|
const metadata = {
|
|
66
66
|
type: param.type,
|
|
67
67
|
data: param.name,
|
|
68
|
-
metatype: paramTypes?.[param.index]
|
|
68
|
+
metatype: param.metatype ?? paramTypes?.[param.index]
|
|
69
69
|
};
|
|
70
70
|
for (const pipe of pipes){
|
|
71
71
|
value = await pipe.transform(value, metadata);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Context } from 'hono';
|
|
2
|
+
import type { Container } from '../container/container';
|
|
3
|
+
/**
|
|
4
|
+
* Returns the request-scoped child container for the current request.
|
|
5
|
+
*
|
|
6
|
+
* `@Inject(Container)` resolves the ROOT container (request children share the
|
|
7
|
+
* provider map and never rebind `Container`), so module authors who need
|
|
8
|
+
* request-scoped resolution from inside a handler, param-decorator factory, or
|
|
9
|
+
* scoped middleware must read the child seeded by RouteManager instead. The
|
|
10
|
+
* child is created before guards, pipes, params, and handlers run, so it is
|
|
11
|
+
* always present on the Vela request path.
|
|
12
|
+
*
|
|
13
|
+
* Throws outside a Vela-managed request rather than materializing an orphan
|
|
14
|
+
* child (mirrors the REQUEST_CONTEXT fail-fast).
|
|
15
|
+
*/
|
|
16
|
+
export declare function getRequestContainer(c: Context): Container;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the request-scoped child container for the current request.
|
|
3
|
+
*
|
|
4
|
+
* `@Inject(Container)` resolves the ROOT container (request children share the
|
|
5
|
+
* provider map and never rebind `Container`), so module authors who need
|
|
6
|
+
* request-scoped resolution from inside a handler, param-decorator factory, or
|
|
7
|
+
* scoped middleware must read the child seeded by RouteManager instead. The
|
|
8
|
+
* child is created before guards, pipes, params, and handlers run, so it is
|
|
9
|
+
* always present on the Vela request path.
|
|
10
|
+
*
|
|
11
|
+
* Throws outside a Vela-managed request rather than materializing an orphan
|
|
12
|
+
* child (mirrors the REQUEST_CONTEXT fail-fast).
|
|
13
|
+
*/ export function getRequestContainer(c) {
|
|
14
|
+
const container = c.get('container');
|
|
15
|
+
if (!container) {
|
|
16
|
+
throw new Error('getRequestContainer(c) can only be called inside a Vela-managed request — ' + 'the request child container is seeded by RouteManager when the request enters the pipeline.');
|
|
17
|
+
}
|
|
18
|
+
return container;
|
|
19
|
+
}
|
|
@@ -405,7 +405,7 @@ export class RouteManager {
|
|
|
405
405
|
// `await import(variable)` as a runtime import Cloudflare Workers cannot
|
|
406
406
|
// resolve.
|
|
407
407
|
const contributors = getRouteContributors();
|
|
408
|
-
for (const { controller, metadata } of this.controllers){
|
|
408
|
+
for (const { controller, metadata, routes } of this.controllers){
|
|
409
409
|
for (const contributor of contributors){
|
|
410
410
|
const meta = getMetadata(contributor.claimsMetaKey, controller);
|
|
411
411
|
if (meta === undefined) continue;
|
|
@@ -419,10 +419,11 @@ export class RouteManager {
|
|
|
419
419
|
joinPaths
|
|
420
420
|
});
|
|
421
421
|
}
|
|
422
|
-
// DX
|
|
423
|
-
//
|
|
424
|
-
|
|
425
|
-
|
|
422
|
+
// DX warning for stale setups: @Crud() metadata but nothing produced a
|
|
423
|
+
// route — native @velajs/crud (>=1.18) stamps real routes at decoration
|
|
424
|
+
// time, and legacy bridges register a claiming contributor.
|
|
425
|
+
if (getMetadata('vela:crud', controller) !== undefined && routes.length === 0 && !contributors.some((c)=>c.claimsMetaKey === 'vela:crud')) {
|
|
426
|
+
console.warn(`[vela] ${controller.name} carries @Crud() metadata but produced no routes — ` + "install and import '@velajs/crud' (>=1.18), or remove the decorator.");
|
|
426
427
|
}
|
|
427
428
|
}
|
|
428
429
|
return app;
|
package/dist/http/types.d.ts
CHANGED
|
@@ -23,6 +23,12 @@ export interface ParamMetadata {
|
|
|
23
23
|
name?: string;
|
|
24
24
|
pipes?: PipeType[];
|
|
25
25
|
factory?: (data: unknown, ctx: import('hono').Context) => unknown;
|
|
26
|
+
/**
|
|
27
|
+
* Explicit param type for programmatic routes. Methods synthesized at
|
|
28
|
+
* runtime (e.g. `@Crud()` verb handlers) have no `design:paramtypes`, so
|
|
29
|
+
* ValidationPipe and the OpenAPI walk read this instead when present.
|
|
30
|
+
*/
|
|
31
|
+
metatype?: unknown;
|
|
26
32
|
}
|
|
27
33
|
export interface ControllerRegistration {
|
|
28
34
|
controller: Type;
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export { signUrl, verifySignedUrl } from './crypto/signed-url';
|
|
|
16
16
|
export type { SignedUrlOptions } from './crypto/signed-url';
|
|
17
17
|
export { REQUEST_CONTEXT } from './http/request-context';
|
|
18
18
|
export type { RequestContext } from './http/request-context';
|
|
19
|
+
export { getRequestContainer } from './http/request-container';
|
|
19
20
|
export { enableAmbientContainer, getCurrentContainer, getCurrentRequestContext, } from './http/ambient';
|
|
20
21
|
export { Logger, LogLevel } from './services/index';
|
|
21
22
|
export type { LoggerService, ContextProvider, Writer, LoggerLevelName } from './services/index';
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,9 @@ export { Controller, Version, Get, Post, Put, Patch, Delete, Options, Head, All,
|
|
|
16
16
|
export { signUrl, verifySignedUrl } from "./crypto/signed-url.js";
|
|
17
17
|
// Request-scoped context primitive
|
|
18
18
|
export { REQUEST_CONTEXT } from "./http/request-context.js";
|
|
19
|
+
// Explicit request-child container access (for programmatic-route authors,
|
|
20
|
+
// param-decorator factories, and scoped middleware)
|
|
21
|
+
export { getRequestContainer } from "./http/request-container.js";
|
|
19
22
|
// Opt-in ambient container access (ALS via hono/context-storage)
|
|
20
23
|
export { enableAmbientContainer, getCurrentContainer, getCurrentRequestContext } from "./http/ambient.js";
|
|
21
24
|
// Services
|
package/dist/live/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { LiveModule } from './live.module';
|
|
2
2
|
export { LiveResolver, LiveQuery, getLiveQueries } from './live.decorators';
|
|
3
3
|
export { LiveEngine, LIVE_SUBS_DATA_KEY, readPersistedLiveSubscriptions } from './live.engine';
|
|
4
|
-
export { LiveInvalidation, localLive, stampCommitHeaders } from './live.invalidation';
|
|
4
|
+
export { LiveInvalidation, localLive, perAppLiveDriver, stampCommitHeaders } from './live.invalidation';
|
|
5
5
|
export { InMemoryCursorLog } from './live.cursor';
|
|
6
6
|
export { encodeSubscriptionUpdate } from './live.delta';
|
|
7
7
|
export { PresenceService, PresenceResolver, presenceTag, PRESENCE_ROSTER_QUERY } from './presence';
|
package/dist/live/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
export { LiveModule } from "./live.module.js";
|
|
7
7
|
export { LiveResolver, LiveQuery, getLiveQueries } from "./live.decorators.js";
|
|
8
8
|
export { LiveEngine, LIVE_SUBS_DATA_KEY, readPersistedLiveSubscriptions } from "./live.engine.js";
|
|
9
|
-
export { LiveInvalidation, localLive, stampCommitHeaders } from "./live.invalidation.js";
|
|
9
|
+
export { LiveInvalidation, localLive, perAppLiveDriver, stampCommitHeaders } from "./live.invalidation.js";
|
|
10
10
|
export { InMemoryCursorLog } from "./live.cursor.js";
|
|
11
11
|
export { encodeSubscriptionUpdate } from "./live.delta.js";
|
|
12
12
|
export { PresenceService, PresenceResolver, presenceTag, PRESENCE_ROSTER_QUERY } from "./presence.js";
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import type { CommitStamp, InvalidationCommand, LiveDriver } from './live.types';
|
|
2
2
|
/** Single-scope default driver: deliver straight to this process's engine. */
|
|
3
3
|
export declare function localLive(): LiveDriver;
|
|
4
|
+
/**
|
|
5
|
+
* Per-app wrapper around a (possibly SHARED) driver instance.
|
|
6
|
+
*
|
|
7
|
+
* A driver passed to `LiveModule.forRoot({ driver })` is a plain config object
|
|
8
|
+
* — and on Cloudflare the SAME app module (thus the same driver object)
|
|
9
|
+
* bootstraps in the Worker isolate AND inside each Durable Object, often
|
|
10
|
+
* within one isolate. Mutable per-app state must therefore never live on the
|
|
11
|
+
* driver itself: the DO flipping a shared `durableObjectLive()` into local
|
|
12
|
+
* mode would otherwise poison the Worker's engine, which then executes the
|
|
13
|
+
* DO's storage handle in the wrong request context ("Cannot perform I/O on
|
|
14
|
+
* behalf of a different request").
|
|
15
|
+
*
|
|
16
|
+
* The wrapper owns the per-app sink and the per-app local-mode flag; isolate-
|
|
17
|
+
* wide concerns (env capture) forward to the underlying driver. Platform init
|
|
18
|
+
* hooks (`_setLocalMode`, `_initializeEnv`) are exposed pass-through so
|
|
19
|
+
* platform packages can keep type-guarding on their presence.
|
|
20
|
+
*/
|
|
21
|
+
export declare function perAppLiveDriver(underlying: LiveDriver): LiveDriver;
|
|
4
22
|
/**
|
|
5
23
|
* The injectable invalidation surface for custom (non-CRUD) write paths:
|
|
6
24
|
*
|
|
@@ -12,6 +12,49 @@ import { getContext } from "hono/context-storage";
|
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Per-app wrapper around a (possibly SHARED) driver instance.
|
|
17
|
+
*
|
|
18
|
+
* A driver passed to `LiveModule.forRoot({ driver })` is a plain config object
|
|
19
|
+
* — and on Cloudflare the SAME app module (thus the same driver object)
|
|
20
|
+
* bootstraps in the Worker isolate AND inside each Durable Object, often
|
|
21
|
+
* within one isolate. Mutable per-app state must therefore never live on the
|
|
22
|
+
* driver itself: the DO flipping a shared `durableObjectLive()` into local
|
|
23
|
+
* mode would otherwise poison the Worker's engine, which then executes the
|
|
24
|
+
* DO's storage handle in the wrong request context ("Cannot perform I/O on
|
|
25
|
+
* behalf of a different request").
|
|
26
|
+
*
|
|
27
|
+
* The wrapper owns the per-app sink and the per-app local-mode flag; isolate-
|
|
28
|
+
* wide concerns (env capture) forward to the underlying driver. Platform init
|
|
29
|
+
* hooks (`_setLocalMode`, `_initializeEnv`) are exposed pass-through so
|
|
30
|
+
* platform packages can keep type-guarding on their presence.
|
|
31
|
+
*/ export function perAppLiveDriver(underlying) {
|
|
32
|
+
let ownSink;
|
|
33
|
+
let localMode = false;
|
|
34
|
+
const cfHooks = underlying;
|
|
35
|
+
return {
|
|
36
|
+
get kind () {
|
|
37
|
+
return underlying.kind;
|
|
38
|
+
},
|
|
39
|
+
bind (sink) {
|
|
40
|
+
ownSink = sink;
|
|
41
|
+
underlying.bind(sink);
|
|
42
|
+
},
|
|
43
|
+
dispatch (cmd) {
|
|
44
|
+
if (localMode && ownSink) return ownSink.applyInvalidation(cmd);
|
|
45
|
+
return underlying.dispatch(cmd);
|
|
46
|
+
},
|
|
47
|
+
start: underlying.start ? ()=>underlying.start?.() : undefined,
|
|
48
|
+
stop: underlying.stop ? ()=>underlying.stop?.() : undefined,
|
|
49
|
+
// Platform hooks (Cloudflare): local mode is PER APP; env is per isolate.
|
|
50
|
+
_setLocalMode () {
|
|
51
|
+
localMode = true;
|
|
52
|
+
},
|
|
53
|
+
_initializeEnv (env) {
|
|
54
|
+
cfHooks._initializeEnv?.(env);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
15
58
|
/**
|
|
16
59
|
* The injectable invalidation surface for custom (non-CRUD) write paths:
|
|
17
60
|
*
|
package/dist/live/live.module.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineModule } from "../index.js";
|
|
2
2
|
import { InMemoryCursorLog } from "./live.cursor.js";
|
|
3
3
|
import { LiveEngine } from "./live.engine.js";
|
|
4
|
-
import { LiveInvalidation, localLive } from "./live.invalidation.js";
|
|
4
|
+
import { LiveInvalidation, localLive, perAppLiveDriver } from "./live.invalidation.js";
|
|
5
5
|
import { LIVE_CURSOR_LOG, LIVE_DRIVER, LIVE_MODULE_OPTIONS } from "./live.tokens.js";
|
|
6
6
|
import { PresenceResolver, PresenceService } from "./presence.js";
|
|
7
7
|
/**
|
|
@@ -39,7 +39,11 @@ import { PresenceResolver, PresenceService } from "./presence.js";
|
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
provide: LIVE_DRIVER,
|
|
42
|
-
|
|
42
|
+
// Wrapped per app: user-supplied drivers are shared config objects
|
|
43
|
+
// (the same module bootstraps in the Worker AND in each Durable
|
|
44
|
+
// Object), so per-app sink/mode state lives in the wrapper — see
|
|
45
|
+
// perAppLiveDriver.
|
|
46
|
+
useFactory: (o)=>perAppLiveDriver(o.driver ?? localLive()),
|
|
43
47
|
inject: [
|
|
44
48
|
OPTIONS
|
|
45
49
|
]
|
package/dist/openapi/document.js
CHANGED
|
@@ -39,7 +39,7 @@ function isDtoClass(value) {
|
|
|
39
39
|
return typeof value === 'function' && value.schema != null;
|
|
40
40
|
}
|
|
41
41
|
function getParamSchema(param, paramtypes, registry) {
|
|
42
|
-
const metatype = paramtypes?.[param.index];
|
|
42
|
+
const metatype = param.metatype ?? paramtypes?.[param.index];
|
|
43
43
|
if (!metatype) return undefined;
|
|
44
44
|
if (isDtoClass(metatype)) {
|
|
45
45
|
return registry.ref(metatype);
|
|
@@ -51,7 +51,7 @@ function getParamSchema(param, paramtypes, registry) {
|
|
|
51
51
|
return undefined;
|
|
52
52
|
}
|
|
53
53
|
function isParamOptional(param, paramtypes) {
|
|
54
|
-
const metatype = paramtypes?.[param.index];
|
|
54
|
+
const metatype = param.metatype ?? paramtypes?.[param.index];
|
|
55
55
|
if (metatype?.schema) {
|
|
56
56
|
return isOptional(metatype.schema);
|
|
57
57
|
}
|
package/dist/registry/types.d.ts
CHANGED
|
@@ -30,6 +30,12 @@ export interface ParameterMetadata {
|
|
|
30
30
|
name?: string;
|
|
31
31
|
pipes?: PipeType[];
|
|
32
32
|
factory?: (data: unknown, ctx: Context) => unknown;
|
|
33
|
+
/**
|
|
34
|
+
* Explicit param type for programmatic routes. Methods synthesized at
|
|
35
|
+
* runtime (e.g. `@Crud()` verb handlers) have no `design:paramtypes`, so
|
|
36
|
+
* ValidationPipe and the OpenAPI walk read this instead when present.
|
|
37
|
+
*/
|
|
38
|
+
metatype?: unknown;
|
|
33
39
|
}
|
|
34
40
|
export interface HttpHandlerMeta {
|
|
35
41
|
httpCode?: number;
|