@velajs/vela 1.15.0 → 1.17.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.
Files changed (74) hide show
  1. package/dist/config/config.module.d.ts +8 -1
  2. package/dist/config/config.module.js +89 -42
  3. package/dist/config/config.service.d.ts +17 -4
  4. package/dist/config/config.service.js +18 -20
  5. package/dist/config/config.store.d.ts +46 -0
  6. package/dist/config/config.store.js +119 -0
  7. package/dist/config/config.tokens.d.ts +11 -0
  8. package/dist/config/config.tokens.js +12 -0
  9. package/dist/config/config.types.d.ts +37 -1
  10. package/dist/config/config.types.js +4 -1
  11. package/dist/config/index.d.ts +5 -2
  12. package/dist/config/index.js +3 -1
  13. package/dist/config/register-as.d.ts +52 -0
  14. package/dist/config/register-as.js +39 -0
  15. package/dist/{storage → crypto}/signed-url.js +5 -0
  16. package/dist/factory/bootstrap.js +10 -0
  17. package/dist/http/decorators.d.ts +18 -9
  18. package/dist/http/decorators.js +4 -1
  19. package/dist/http/index.d.ts +4 -0
  20. package/dist/http/index.js +2 -0
  21. package/dist/http/route-map.d.ts +30 -0
  22. package/dist/http/route-map.js +27 -0
  23. package/dist/http/route.manager.d.ts +2 -0
  24. package/dist/http/route.manager.js +4 -1
  25. package/dist/http/types.d.ts +2 -0
  26. package/dist/http/url/index.d.ts +4 -0
  27. package/dist/http/url/index.js +3 -0
  28. package/dist/http/url/signed-url.guard.d.ts +27 -0
  29. package/dist/http/url/signed-url.guard.js +62 -0
  30. package/dist/http/url/signing-secret.d.ts +20 -0
  31. package/dist/http/url/signing-secret.js +24 -0
  32. package/dist/http/url/url-generator.service.d.ts +52 -0
  33. package/dist/http/url/url-generator.service.js +104 -0
  34. package/dist/index.d.ts +8 -5
  35. package/dist/index.js +5 -3
  36. package/dist/live/index.d.ts +12 -0
  37. package/dist/live/index.js +16 -0
  38. package/dist/live/live.cursor.d.ts +22 -0
  39. package/dist/live/live.cursor.js +56 -0
  40. package/dist/live/live.decorators.d.ts +32 -0
  41. package/dist/live/live.decorators.js +46 -0
  42. package/dist/live/live.delta.d.ts +15 -0
  43. package/dist/live/live.delta.js +51 -0
  44. package/dist/live/live.engine.d.ts +81 -0
  45. package/dist/live/live.engine.js +448 -0
  46. package/dist/live/live.invalidation.d.ts +27 -0
  47. package/dist/live/live.invalidation.js +56 -0
  48. package/dist/live/live.module.d.ts +28 -0
  49. package/dist/live/live.module.js +83 -0
  50. package/dist/live/live.tokens.d.ts +9 -0
  51. package/dist/live/live.tokens.js +8 -0
  52. package/dist/live/live.types.d.ts +138 -0
  53. package/dist/live/live.types.js +1 -0
  54. package/dist/live/presence.d.ts +44 -0
  55. package/dist/live/presence.js +130 -0
  56. package/dist/openapi/document.js +2 -1
  57. package/dist/registry/types.d.ts +2 -0
  58. package/dist/storage/index.d.ts +2 -2
  59. package/dist/storage/index.js +4 -1
  60. package/dist/websocket/index.d.ts +3 -3
  61. package/dist/websocket/index.js +2 -2
  62. package/dist/websocket/websocket.decorators.d.ts +14 -0
  63. package/dist/websocket/websocket.decorators.js +23 -1
  64. package/dist/websocket/websocket.tokens.d.ts +7 -0
  65. package/dist/websocket/websocket.tokens.js +6 -0
  66. package/dist/websocket/websocket.types.d.ts +16 -0
  67. package/dist/websocket/ws-dispatcher.d.ts +9 -0
  68. package/dist/websocket/ws-dispatcher.js +64 -1
  69. package/dist/websocket-node/index.d.ts +2 -0
  70. package/dist/websocket-node/index.js +1 -0
  71. package/dist/websocket-node/redis-live.d.ts +24 -0
  72. package/dist/websocket-node/redis-live.js +57 -0
  73. package/package.json +11 -2
  74. /package/dist/{storage → crypto}/signed-url.d.ts +0 -0
@@ -31,15 +31,24 @@ export declare function Controller(pathOrOptions?: string | ControllerOptions):
31
31
  */
32
32
  export declare function Version(version: number | number[]): MethodDecorator;
33
33
  export declare function getRouteVersion(target: Constructor, propertyKey: string | symbol): number | number[] | undefined;
34
- export declare const Get: (path?: string) => MethodDecorator;
35
- export declare const Post: (path?: string) => MethodDecorator;
36
- export declare const Put: (path?: string) => MethodDecorator;
37
- export declare const Patch: (path?: string) => MethodDecorator;
38
- export declare const Delete: (path?: string) => MethodDecorator;
39
- export declare const Options: (path?: string) => MethodDecorator;
40
- export declare const Head: (path?: string) => MethodDecorator;
41
- export declare const All: (path?: string) => MethodDecorator;
42
- export declare const Sse: (path?: string) => MethodDecorator;
34
+ /** Per-route options for the HTTP method decorators (`@Get`, `@Post`, …). */
35
+ export interface RouteOptions {
36
+ /**
37
+ * A stable, human-readable name for this route. Enables URL generation
38
+ * (`UrlGeneratorService.urlFor(name, …)`), surfaces on `app.describeRoutes()`,
39
+ * and when set becomes the OpenAPI `operationId`.
40
+ */
41
+ name?: string;
42
+ }
43
+ export declare const Get: (path?: string, options?: RouteOptions) => MethodDecorator;
44
+ export declare const Post: (path?: string, options?: RouteOptions) => MethodDecorator;
45
+ export declare const Put: (path?: string, options?: RouteOptions) => MethodDecorator;
46
+ export declare const Patch: (path?: string, options?: RouteOptions) => MethodDecorator;
47
+ export declare const Delete: (path?: string, options?: RouteOptions) => MethodDecorator;
48
+ export declare const Options: (path?: string, options?: RouteOptions) => MethodDecorator;
49
+ export declare const Head: (path?: string, options?: RouteOptions) => MethodDecorator;
50
+ export declare const All: (path?: string, options?: RouteOptions) => MethodDecorator;
51
+ export declare const Sse: (path?: string, options?: RouteOptions) => MethodDecorator;
43
52
  export declare const Param: (nameOrPipe?: string | PipeType, ...pipes: PipeType[]) => ParameterDecorator;
44
53
  export declare const Query: (nameOrPipe?: string | PipeType, ...pipes: PipeType[]) => ParameterDecorator;
45
54
  export declare const Body: (nameOrPipe?: string | PipeType, ...pipes: PipeType[]) => ParameterDecorator;
@@ -57,7 +57,7 @@ export function getRouteVersion(target, propertyKey) {
57
57
  return MetadataRegistry.getRouteVersion(target, propertyKey);
58
58
  }
59
59
  function createMethodDecorator(method) {
60
- return (path = '')=>{
60
+ return (path = '', options)=>{
61
61
  return (target, propertyKey, _descriptor)=>{
62
62
  const ctor = target.constructor;
63
63
  const normalizedPath = normalizePath(path);
@@ -68,6 +68,9 @@ function createMethodDecorator(method) {
68
68
  handlerName: propertyKey,
69
69
  ...version !== undefined ? {
70
70
  version
71
+ } : {},
72
+ ...options?.name !== undefined ? {
73
+ name: options.name
71
74
  } : {}
72
75
  });
73
76
  };
@@ -3,6 +3,10 @@ export type { RouteManagerOptions } from './route.manager';
3
3
  export { MiddlewareBuilder } from '../module/middleware';
4
4
  export type { MiddlewareConsumer, MiddlewareConfigProxy, RouteInfo, NestModule, MiddlewareRouteDefinition } from '../module/middleware';
5
5
  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, applyDecorators, isController, } from './decorators';
6
+ export type { RouteOptions } from './decorators';
6
7
  export { createLazyParamDecorator } from './lazy-param.decorator';
8
+ export { UrlGeneratorService, SignedUrlGuard, SignedUrl, URL_SIGNING_SECRET, } from './url/index';
9
+ export type { UrlForOptions, SignedUrlGenerateOptions } from './url/index';
10
+ export type { VelaRouteMap, RouteName, RouteParams } from './route-map';
7
11
  export { enableAmbientContainer, getCurrentContainer, getCurrentRequestContext, } from './ambient';
8
12
  export type { RouteMetadata, ControllerMetadata, ControllerOptions, ParamMetadata, ControllerRegistration, } from './types';
@@ -2,4 +2,6 @@ export { RouteManager } from "./route.manager.js";
2
2
  export { MiddlewareBuilder } from "../module/middleware.js";
3
3
  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, applyDecorators, isController } from "./decorators.js";
4
4
  export { createLazyParamDecorator } from "./lazy-param.decorator.js";
5
+ // Named-route URL generation + signed URLs
6
+ export { UrlGeneratorService, SignedUrlGuard, SignedUrl, URL_SIGNING_SECRET } from "./url/index.js";
5
7
  export { enableAmbientContainer, getCurrentContainer, getCurrentRequestContext } from "./ambient.js";
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Augmentable map of route name → its path params. Users opt in to type-safe
3
+ * URL generation by augmenting this interface — no build step, no codegen:
4
+ *
5
+ * ```ts
6
+ * declare module '@velajs/vela' {
7
+ * interface VelaRouteMap {
8
+ * 'users.index': Record<string, never>;
9
+ * 'users.show': { id: string };
10
+ * }
11
+ * }
12
+ * ```
13
+ *
14
+ * Until augmented it stays empty, so `RouteName` falls back to `string` and
15
+ * `RouteParams<N>` to a loose `Record<string, string> | undefined` — untyped
16
+ * callers keep working unchanged.
17
+ */
18
+ export interface VelaRouteMap {
19
+ }
20
+ /**
21
+ * Every valid route name. Falls back to `string` when `VelaRouteMap` has not
22
+ * been augmented, so `urlFor`/`signedUrl` accept any name in an untyped app.
23
+ */
24
+ export type RouteName = keyof VelaRouteMap extends never ? string : Extract<keyof VelaRouteMap, string>;
25
+ /**
26
+ * The params object required to build a named route's URL. When `VelaRouteMap`
27
+ * is augmented, resolves to the declared params shape for `N`; otherwise a
28
+ * loose `Record<string, string> | undefined`.
29
+ */
30
+ export type RouteParams<N extends RouteName> = N extends keyof VelaRouteMap ? VelaRouteMap[N] : Record<string, string> | undefined;
@@ -0,0 +1,27 @@
1
+ // Zero-codegen, type-safe route names — mirrors the augmentation pattern from
2
+ // stratal `packages/core/src/router/route-map.ts` (MIT © Temitayo Fadojutimi).
3
+ // Adapted for vela: `VelaRouteMap` maps a route name directly to its params
4
+ // object (stratal wraps params in `{ params: … }`; vela keeps it flat to match
5
+ // the `urlFor(name, params)` call shape).
6
+ /**
7
+ * Augmentable map of route name → its path params. Users opt in to type-safe
8
+ * URL generation by augmenting this interface — no build step, no codegen:
9
+ *
10
+ * ```ts
11
+ * declare module '@velajs/vela' {
12
+ * interface VelaRouteMap {
13
+ * 'users.index': Record<string, never>;
14
+ * 'users.show': { id: string };
15
+ * }
16
+ * }
17
+ * ```
18
+ *
19
+ * Until augmented it stays empty, so `RouteName` falls back to `string` and
20
+ * `RouteParams<N>` to a loose `Record<string, string> | undefined` — untyped
21
+ * callers keep working unchanged.
22
+ */ // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- augmentable interface, intentionally empty until a consumer declares routes
23
+ /**
24
+ * The params object required to build a named route's URL. When `VelaRouteMap`
25
+ * is augmented, resolves to the declared params shape for `N`; otherwise a
26
+ * loose `Record<string, string> | undefined`.
27
+ */ export { };
@@ -21,6 +21,8 @@ export interface RouteDescription {
21
21
  controller: string;
22
22
  handler: string;
23
23
  version?: number;
24
+ /** Route name (`@Get(path, { name })`) — the URL-generation / `operationId` key. */
25
+ name?: string;
24
26
  }
25
27
  export interface RouteManagerOptions {
26
28
  getClientIp?: (c: Context) => string | null;
@@ -388,7 +388,10 @@ export class RouteManager {
388
388
  path: fullPath || '/',
389
389
  controller: controller.name,
390
390
  handler: String(route.handlerName),
391
- version: versionsForPaths[pathIndex]
391
+ version: versionsForPaths[pathIndex],
392
+ ...route.name !== undefined ? {
393
+ name: route.name
394
+ } : {}
392
395
  });
393
396
  }
394
397
  }
@@ -6,6 +6,8 @@ export interface RouteMetadata {
6
6
  path: string;
7
7
  handlerName: string | symbol;
8
8
  version?: number | number[];
9
+ /** Route name for URL generation / OpenAPI operationId (`@Get(path, { name })`). */
10
+ name?: string;
9
11
  }
10
12
  export interface ControllerOptions {
11
13
  path?: string;
@@ -0,0 +1,4 @@
1
+ export { UrlGeneratorService } from './url-generator.service';
2
+ export type { UrlForOptions, SignedUrlGenerateOptions } from './url-generator.service';
3
+ export { SignedUrlGuard, SignedUrl } from './signed-url.guard';
4
+ export { URL_SIGNING_SECRET } from './signing-secret';
@@ -0,0 +1,3 @@
1
+ export { UrlGeneratorService } from "./url-generator.service.js";
2
+ export { SignedUrlGuard, SignedUrl } from "./signed-url.guard.js";
3
+ export { URL_SIGNING_SECRET } from "./signing-secret.js";
@@ -0,0 +1,27 @@
1
+ import { applyDecorators } from '../decorators';
2
+ import type { CanActivate, ExecutionContext } from '../../pipeline/types';
3
+ /**
4
+ * Verifies the HMAC signature (and `expires`) of the incoming request URL,
5
+ * using the same secret source as {@link UrlGeneratorService.signedUrl}: the
6
+ * {@link URL_SIGNING_SECRET} token, else `CONFIG_ENV`. Throws
7
+ * `ForbiddenException` (403) when the signature is missing, tampered, or
8
+ * expired. Registered app-wide by `bootstrap`; apply it per-route with
9
+ * {@link SignedUrl}.
10
+ */
11
+ export declare class SignedUrlGuard implements CanActivate {
12
+ private readonly secretToken?;
13
+ private readonly env;
14
+ constructor(secretToken?: string | undefined, env?: Record<string, unknown>);
15
+ canActivate(context: ExecutionContext): Promise<boolean>;
16
+ }
17
+ /**
18
+ * Guards a route with {@link SignedUrlGuard} — the request must carry a valid,
19
+ * unexpired HMAC signature (produced by `UrlGeneratorService.signedUrl`).
20
+ *
21
+ * ```ts
22
+ * @Get('download', { name: 'file.download' })
23
+ * @SignedUrl()
24
+ * download() { ... }
25
+ * ```
26
+ */
27
+ export declare function SignedUrl(): ReturnType<typeof applyDecorators>;
@@ -0,0 +1,62 @@
1
+ function _ts_decorate(decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ }
7
+ function _ts_metadata(k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ }
10
+ function _ts_param(paramIndex, decorator) {
11
+ return function(target, key) {
12
+ decorator(target, key, paramIndex);
13
+ };
14
+ }
15
+ import { Injectable, Inject, Optional } from "../../container/decorators.js";
16
+ import { CONFIG_ENV } from "../../config/config.tokens.js";
17
+ import { verifySignedUrl } from "../../crypto/signed-url.js";
18
+ import { ForbiddenException } from "../../errors/http-exception.js";
19
+ import { applyDecorators } from "../decorators.js";
20
+ import { UseGuards } from "../../pipeline/decorators.js";
21
+ import { URL_SIGNING_SECRET, resolveSigningSecret } from "./signing-secret.js";
22
+ export class SignedUrlGuard {
23
+ secretToken;
24
+ env;
25
+ constructor(secretToken, env = {}){
26
+ this.secretToken = secretToken;
27
+ this.env = env;
28
+ }
29
+ async canActivate(context) {
30
+ const request = context.switchToHttp().getRequest();
31
+ const secret = resolveSigningSecret(undefined, this.secretToken, this.env);
32
+ const valid = await verifySignedUrl(request.url, secret);
33
+ if (!valid) {
34
+ throw new ForbiddenException('Invalid or expired signed URL');
35
+ }
36
+ return true;
37
+ }
38
+ }
39
+ SignedUrlGuard = _ts_decorate([
40
+ Injectable(),
41
+ _ts_param(0, Optional()),
42
+ _ts_param(0, Inject(URL_SIGNING_SECRET)),
43
+ _ts_param(1, Optional()),
44
+ _ts_param(1, Inject(CONFIG_ENV)),
45
+ _ts_metadata("design:type", Function),
46
+ _ts_metadata("design:paramtypes", [
47
+ String,
48
+ typeof Record === "undefined" ? Object : Record
49
+ ])
50
+ ], SignedUrlGuard);
51
+ /**
52
+ * Guards a route with {@link SignedUrlGuard} — the request must carry a valid,
53
+ * unexpired HMAC signature (produced by `UrlGeneratorService.signedUrl`).
54
+ *
55
+ * ```ts
56
+ * @Get('download', { name: 'file.download' })
57
+ * @SignedUrl()
58
+ * download() { ... }
59
+ * ```
60
+ */ export function SignedUrl() {
61
+ return applyDecorators(UseGuards(SignedUrlGuard));
62
+ }
@@ -0,0 +1,20 @@
1
+ import { InjectionToken } from '../../container/types';
2
+ /**
3
+ * Optional DI token holding the HMAC secret used to sign / verify URLs. Provide
4
+ * it globally (e.g. from a platform adapter or a `@Global` module) so both
5
+ * {@link UrlGeneratorService} and the signed-URL guard can read it:
6
+ *
7
+ * ```ts
8
+ * { provide: URL_SIGNING_SECRET, useValue: env.URL_SIGNING_SECRET }
9
+ * ```
10
+ */
11
+ export declare const URL_SIGNING_SECRET: InjectionToken<string>;
12
+ /** Key read from `CONFIG_ENV` when no explicit secret / token is available. */
13
+ export declare const URL_SIGNING_SECRET_ENV_KEY = "URL_SIGNING_SECRET";
14
+ /**
15
+ * Resolve the signing secret from, in order: an explicit argument, the
16
+ * {@link URL_SIGNING_SECRET} token, then `CONFIG_ENV[URL_SIGNING_SECRET]`.
17
+ * Throws a descriptive error when none is available — signing must never fall
18
+ * back to an empty/implicit key.
19
+ */
20
+ export declare function resolveSigningSecret(explicit: string | undefined, token: string | undefined, env: Record<string, unknown> | undefined): string;
@@ -0,0 +1,24 @@
1
+ import { InjectionToken } from "../../container/types.js";
2
+ /**
3
+ * Optional DI token holding the HMAC secret used to sign / verify URLs. Provide
4
+ * it globally (e.g. from a platform adapter or a `@Global` module) so both
5
+ * {@link UrlGeneratorService} and the signed-URL guard can read it:
6
+ *
7
+ * ```ts
8
+ * { provide: URL_SIGNING_SECRET, useValue: env.URL_SIGNING_SECRET }
9
+ * ```
10
+ */ export const URL_SIGNING_SECRET = new InjectionToken('URL_SIGNING_SECRET');
11
+ /** Key read from `CONFIG_ENV` when no explicit secret / token is available. */ export const URL_SIGNING_SECRET_ENV_KEY = 'URL_SIGNING_SECRET';
12
+ /**
13
+ * Resolve the signing secret from, in order: an explicit argument, the
14
+ * {@link URL_SIGNING_SECRET} token, then `CONFIG_ENV[URL_SIGNING_SECRET]`.
15
+ * Throws a descriptive error when none is available — signing must never fall
16
+ * back to an empty/implicit key.
17
+ */ export function resolveSigningSecret(explicit, token, env) {
18
+ const fromEnv = env?.[URL_SIGNING_SECRET_ENV_KEY];
19
+ const secret = explicit ?? token ?? (typeof fromEnv === 'string' ? fromEnv : undefined);
20
+ if (!secret) {
21
+ throw new Error('No URL signing secret is available. Pass one explicitly, register a ' + '`URL_SIGNING_SECRET` provider, or set `URL_SIGNING_SECRET` in `CONFIG_ENV`.');
22
+ }
23
+ return secret;
24
+ }
@@ -0,0 +1,52 @@
1
+ import { RouteManager } from '../route.manager';
2
+ import type { RouteName, RouteParams } from '../route-map';
3
+ /** Value accepted for a path param or query entry. */
4
+ type UrlParamValue = string | number | boolean;
5
+ /** Extra options for {@link UrlGeneratorService.urlFor}. */
6
+ export interface UrlForOptions {
7
+ /** Additional query-string entries merged after any leftover params. */
8
+ query?: Record<string, UrlParamValue>;
9
+ }
10
+ /** Options for {@link UrlGeneratorService.signedUrl}. */
11
+ export interface SignedUrlGenerateOptions {
12
+ /** Time-to-live in seconds; enforced on verification via the `expires` param. */
13
+ expiresIn?: number;
14
+ /** Override the signing secret (else the `URL_SIGNING_SECRET` token / `CONFIG_ENV`). */
15
+ secret?: string;
16
+ }
17
+ /**
18
+ * Builds URLs for named routes (Laravel-flavoured `route()` ergonomics).
19
+ *
20
+ * Route descriptions are read LAZILY from the {@link RouteManager} on first use
21
+ * — post-build, so composition (global prefix + version + controller prefix) is
22
+ * already baked into each path — and it never instantiates controllers, so it
23
+ * plays nicely with lazy modules. Registered as an app-level singleton by
24
+ * `bootstrap`, so it is injectable anywhere.
25
+ */
26
+ export declare class UrlGeneratorService {
27
+ private readonly routeManager;
28
+ private readonly secretToken?;
29
+ private readonly env;
30
+ private routeMap;
31
+ constructor(routeManager: RouteManager, secretToken?: string | undefined, env?: Record<string, unknown>);
32
+ /**
33
+ * Build the path for a named route, filling `:param` placeholders from
34
+ * `params`. Params that don't match a placeholder become query-string
35
+ * entries; `opts.query` is merged after them.
36
+ *
37
+ * @throws if no route carries `name`, or a required `:param` is missing.
38
+ */
39
+ urlFor<N extends RouteName>(name: N, params?: RouteParams<N>, opts?: UrlForOptions): string;
40
+ /**
41
+ * Build a named-route URL and HMAC-sign it (path + query). The secret comes
42
+ * from `options.secret`, else the {@link URL_SIGNING_SECRET} token, else
43
+ * `CONFIG_ENV`; a descriptive error is thrown when none is available.
44
+ */
45
+ signedUrl<N extends RouteName>(name: N, params?: RouteParams<N>, options?: SignedUrlGenerateOptions): Promise<string>;
46
+ /**
47
+ * Lazily index named route descriptions. Rebuilt while empty (routes may not
48
+ * be built yet), cached once populated.
49
+ */
50
+ private routes;
51
+ }
52
+ export {};
@@ -0,0 +1,104 @@
1
+ function _ts_decorate(decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ }
7
+ function _ts_metadata(k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ }
10
+ function _ts_param(paramIndex, decorator) {
11
+ return function(target, key) {
12
+ decorator(target, key, paramIndex);
13
+ };
14
+ }
15
+ import { Injectable, Inject, Optional } from "../../container/decorators.js";
16
+ import { CONFIG_ENV } from "../../config/config.tokens.js";
17
+ import { signUrl } from "../../crypto/signed-url.js";
18
+ import { RouteManager } from "../route.manager.js";
19
+ import { URL_SIGNING_SECRET, resolveSigningSecret } from "./signing-secret.js";
20
+ export class UrlGeneratorService {
21
+ routeManager;
22
+ secretToken;
23
+ env;
24
+ routeMap = null;
25
+ constructor(routeManager, secretToken, env = {}){
26
+ this.routeManager = routeManager;
27
+ this.secretToken = secretToken;
28
+ this.env = env;
29
+ }
30
+ /**
31
+ * Build the path for a named route, filling `:param` placeholders from
32
+ * `params`. Params that don't match a placeholder become query-string
33
+ * entries; `opts.query` is merged after them.
34
+ *
35
+ * @throws if no route carries `name`, or a required `:param` is missing.
36
+ */ urlFor(name, params, opts) {
37
+ const description = this.routes().get(name);
38
+ if (!description) {
39
+ throw new Error(`No route named "${String(name)}" was found. Give the route a name ` + `via \`@Get(path, { name: '${String(name)}' })\`.`);
40
+ }
41
+ const provided = {
42
+ ...params ?? {}
43
+ };
44
+ const consumed = new Set();
45
+ const path = description.path.replace(/:([A-Za-z0-9_]+)/g, (_match, key)=>{
46
+ const value = provided[key];
47
+ if (value === undefined || value === null) {
48
+ throw new Error(`Missing route param "${key}" for route "${String(name)}".`);
49
+ }
50
+ consumed.add(key);
51
+ return encodeURIComponent(String(value));
52
+ });
53
+ const query = new URLSearchParams();
54
+ for (const [key, value] of Object.entries(provided)){
55
+ if (consumed.has(key) || value === undefined || value === null) continue;
56
+ query.set(key, String(value));
57
+ }
58
+ for (const [key, value] of Object.entries(opts?.query ?? {})){
59
+ if (value === undefined || value === null) continue;
60
+ query.set(key, String(value));
61
+ }
62
+ const qs = query.toString();
63
+ return qs ? `${path}?${qs}` : path;
64
+ }
65
+ /**
66
+ * Build a named-route URL and HMAC-sign it (path + query). The secret comes
67
+ * from `options.secret`, else the {@link URL_SIGNING_SECRET} token, else
68
+ * `CONFIG_ENV`; a descriptive error is thrown when none is available.
69
+ */ async signedUrl(name, params, options = {}) {
70
+ const url = this.urlFor(name, params);
71
+ const secret = resolveSigningSecret(options.secret, this.secretToken, this.env);
72
+ return signUrl(url, secret, options.expiresIn !== undefined ? {
73
+ expiresIn: options.expiresIn
74
+ } : undefined);
75
+ }
76
+ /**
77
+ * Lazily index named route descriptions. Rebuilt while empty (routes may not
78
+ * be built yet), cached once populated.
79
+ */ routes() {
80
+ if (this.routeMap && this.routeMap.size > 0) return this.routeMap;
81
+ const map = new Map();
82
+ for (const description of this.routeManager.getRouteDescriptions()){
83
+ if (description.name && !map.has(description.name)) {
84
+ map.set(description.name, description);
85
+ }
86
+ }
87
+ this.routeMap = map;
88
+ return map;
89
+ }
90
+ }
91
+ UrlGeneratorService = _ts_decorate([
92
+ Injectable(),
93
+ _ts_param(0, Inject(RouteManager)),
94
+ _ts_param(1, Optional()),
95
+ _ts_param(1, Inject(URL_SIGNING_SECRET)),
96
+ _ts_param(2, Optional()),
97
+ _ts_param(2, Inject(CONFIG_ENV)),
98
+ _ts_metadata("design:type", Function),
99
+ _ts_metadata("design:paramtypes", [
100
+ typeof RouteManager === "undefined" ? Object : RouteManager,
101
+ String,
102
+ typeof Record === "undefined" ? Object : Record
103
+ ])
104
+ ], UrlGeneratorService);
package/dist/index.d.ts CHANGED
@@ -10,14 +10,17 @@ export { Container, Injectable, Inject, Optional, InjectionToken, ForwardRef, fo
10
10
  export type { Type, Token, InferToken, InferTokens, InjectableOptions, ProviderOptions, ModuleScope, ModuleDescription, ContainerOptions, Diagnostics, } from './container/index';
11
11
  export type { RouteDescription } from './http/route.manager';
12
12
  export { METADATA_KEYS, HttpMethod, ParamType, Scope } from './constants';
13
- 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
+ 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, UrlGeneratorService, SignedUrlGuard, SignedUrl, URL_SIGNING_SECRET, } from './http/index';
14
+ export type { RouteOptions, UrlForOptions, SignedUrlGenerateOptions, VelaRouteMap, RouteName, RouteParams, } from './http/index';
15
+ export { signUrl, verifySignedUrl } from './crypto/signed-url';
16
+ export type { SignedUrlOptions } from './crypto/signed-url';
14
17
  export { REQUEST_CONTEXT } from './http/request-context';
15
18
  export type { RequestContext } from './http/request-context';
16
19
  export { enableAmbientContainer, getCurrentContainer, getCurrentRequestContext, } from './http/ambient';
17
20
  export { Logger, LogLevel } from './services/index';
18
21
  export type { LoggerService, ContextProvider, Writer, LoggerLevelName } from './services/index';
19
- export { ConfigModule, ConfigService, CONFIG_OPTIONS } from './config/index';
20
- export type { ConfigModuleOptions } from './config/index';
22
+ export { ConfigModule, ConfigService, ConfigStore, CONFIG_OPTIONS, CONFIG_ENV, registerAs, } from './config/index';
23
+ export type { ConfigModuleOptions, ConfigSchema, ConfigNamespace, AnyConfigNamespace, InferConfigType, ConfigType, ConfigPath, ConfigPathValue, } from './config/index';
21
24
  export { HttpModule, HttpService, HTTP_MODULE_OPTIONS, HttpRequestException } from './fetch/index';
22
25
  export type { HttpModuleOptions, HttpResponse, HttpRequestConfig } from './fetch/index';
23
26
  export { CorsModule, CORS_OPTIONS } from './cors/index';
@@ -28,8 +31,8 @@ export { EventEmitterModule, EventEmitter, EventEmitterSubscriber, OnEvent, ON_E
28
31
  export type { EventHandler, OnEventMetadata } from './event-emitter/index';
29
32
  export { ScheduleModule, ScheduleRegistry, Cron, Interval, parseCron, CRON_METADATA, INTERVAL_METADATA, } from './schedule/index';
30
33
  export type { RegisteredCronJob, RegisteredIntervalJob, CronMetadata, IntervalMetadata, CronMatcher, } from './schedule/index';
31
- export { WebSocketModule, WebSocketGateway, SubscribeMessage, MessageBody, ConnectedSocket, WebSocketServer, WsDispatcher, WsException, WS_SERVER, } from './websocket/index';
32
- export type { WebSocketModuleOptions, WsClient, WsServer, WsMessage, WsResponse, WsExecutionContext, WsArgumentsHost, BroadcastCommand, SyncDriver, RoomRegistry, OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect, } from './websocket/index';
34
+ export { WebSocketModule, WebSocketGateway, SubscribeMessage, MessageBody, ConnectedSocket, WebSocketServer, ReservedWsEvent, WsDispatcher, WsException, WS_SERVER, RESERVED_WS_EVENT_PREFIX, } from './websocket/index';
35
+ export type { WebSocketModuleOptions, WsClient, WsServer, WsMessage, WsResponse, ReservedWsEventMetadata, ReservedWsEventHandler, WsExecutionContext, WsArgumentsHost, BroadcastCommand, SyncDriver, RoomRegistry, OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect, } from './websocket/index';
33
36
  export { HealthModule, HealthCheckService, HealthIndicatorService, HttpHealthIndicator, } from './health/index';
34
37
  export type { HealthCheckResult, HealthCheckStatus, HealthIndicatorResult, HealthIndicatorFunction, ResponseCheckCallback, HttpPingOptions, } from './health/index';
35
38
  export { ThrottlerModule, ThrottlerGuard, ThrottlerStorage, Throttle, SkipThrottle, THROTTLER_OPTIONS, THROTTLER_STORAGE, THROTTLE_METADATA, SKIP_THROTTLE_METADATA, } from './throttler/index';
package/dist/index.js CHANGED
@@ -11,7 +11,9 @@ export { Container, Injectable, Inject, Optional, InjectionToken, ForwardRef, fo
11
11
  // Constants
12
12
  export { METADATA_KEYS, HttpMethod, ParamType, Scope } from "./constants.js";
13
13
  // HTTP Decorators
14
- 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.js";
14
+ 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, UrlGeneratorService, SignedUrlGuard, SignedUrl, URL_SIGNING_SECRET } from "./http/index.js";
15
+ // Edge-safe HMAC signed-URL primitives (also re-exported from `@velajs/vela/storage`)
16
+ export { signUrl, verifySignedUrl } from "./crypto/signed-url.js";
15
17
  // Request-scoped context primitive
16
18
  export { REQUEST_CONTEXT } from "./http/request-context.js";
17
19
  // Opt-in ambient container access (ALS via hono/context-storage)
@@ -19,7 +21,7 @@ export { enableAmbientContainer, getCurrentContainer, getCurrentRequestContext }
19
21
  // Services
20
22
  export { Logger, LogLevel } from "./services/index.js";
21
23
  // Config
22
- export { ConfigModule, ConfigService, CONFIG_OPTIONS } from "./config/index.js";
24
+ export { ConfigModule, ConfigService, ConfigStore, CONFIG_OPTIONS, CONFIG_ENV, registerAs } from "./config/index.js";
23
25
  // HTTP Client
24
26
  export { HttpModule, HttpService, HTTP_MODULE_OPTIONS, HttpRequestException } from "./fetch/index.js";
25
27
  // CORS
@@ -31,7 +33,7 @@ export { EventEmitterModule, EventEmitter, EventEmitterSubscriber, OnEvent, ON_E
31
33
  // Schedule
32
34
  export { ScheduleModule, ScheduleRegistry, Cron, Interval, parseCron, CRON_METADATA, INTERVAL_METADATA } from "./schedule/index.js";
33
35
  // WebSocket (edge-safe core; transport-facing internals live at @velajs/vela/websocket)
34
- export { WebSocketModule, WebSocketGateway, SubscribeMessage, MessageBody, ConnectedSocket, WebSocketServer, WsDispatcher, WsException, WS_SERVER } from "./websocket/index.js";
36
+ export { WebSocketModule, WebSocketGateway, SubscribeMessage, MessageBody, ConnectedSocket, WebSocketServer, ReservedWsEvent, WsDispatcher, WsException, WS_SERVER, RESERVED_WS_EVENT_PREFIX } from "./websocket/index.js";
35
37
  // Health
36
38
  export { HealthModule, HealthCheckService, HealthIndicatorService, HttpHealthIndicator } from "./health/index.js";
37
39
  // Throttler
@@ -0,0 +1,12 @@
1
+ export { LiveModule } from './live.module';
2
+ export { LiveResolver, LiveQuery, getLiveQueries } from './live.decorators';
3
+ export { LiveEngine, LIVE_SUBS_DATA_KEY, readPersistedLiveSubscriptions } from './live.engine';
4
+ export { LiveInvalidation, localLive, stampCommitHeaders } from './live.invalidation';
5
+ export { InMemoryCursorLog } from './live.cursor';
6
+ export { encodeSubscriptionUpdate } from './live.delta';
7
+ export { PresenceService, PresenceResolver, presenceTag, PRESENCE_ROSTER_QUERY } from './presence';
8
+ export type { PresenceMember } from './presence';
9
+ export { LIVE_DRIVER, LIVE_CURSOR_LOG, LIVE_MODULE_OPTIONS, LIVE_RESOLVER_METADATA, LIVE_QUERY_METADATA, } from './live.tokens';
10
+ export type { CommitStamp, CursorLog, InvalidationCommand, LiveDriver, LiveEntrypointMeta, LiveIdentity, LiveInvalidationSink, LiveModuleOptions, LivePresenceOptions, LiveQueryContext, LiveQueryMetadata, LiveQueryOptions, LiveResolverMetadata, ResumeVerdict, SubscriptionRecord, } from './live.types';
11
+ export { LIVE_PROTOCOL, LIVE_EVENT, LIVE_ERROR_CODES, COMMIT_CURSOR_HEADER, COMMIT_EPOCH_HEADER, DEFAULT_KEY_FIELD, encodeLiveEnvelope, encodeLiveFrame, readLiveEnvelope, isClientLiveFrame, isServerLiveFrame, encodeListDelta, applyListDelta, } from '@velajs/live-protocol';
12
+ export type { ClientLiveFrame, LiveFrame, RowOp, ServerLiveFrame } from '@velajs/live-protocol';
@@ -0,0 +1,16 @@
1
+ // @velajs/vela/live — first-party live-query subsystem (tag-based realtime
2
+ // reactivity), authored entirely on the public API plus the shared wire
3
+ // package @velajs/live-protocol (the live-openness audit test enforces it).
4
+ // Subpath-only, like @velajs/vela/queue: the main barrel stays lean and the
5
+ // name stays free for platform packages.
6
+ export { LiveModule } from "./live.module.js";
7
+ export { LiveResolver, LiveQuery, getLiveQueries } from "./live.decorators.js";
8
+ export { LiveEngine, LIVE_SUBS_DATA_KEY, readPersistedLiveSubscriptions } from "./live.engine.js";
9
+ export { LiveInvalidation, localLive, stampCommitHeaders } from "./live.invalidation.js";
10
+ export { InMemoryCursorLog } from "./live.cursor.js";
11
+ export { encodeSubscriptionUpdate } from "./live.delta.js";
12
+ export { PresenceService, PresenceResolver, presenceTag, PRESENCE_ROSTER_QUERY } from "./presence.js";
13
+ export { LIVE_DRIVER, LIVE_CURSOR_LOG, LIVE_MODULE_OPTIONS, LIVE_RESOLVER_METADATA, LIVE_QUERY_METADATA } from "./live.tokens.js";
14
+ // The wire contract (frames, delta codec, headers) is re-exported so app code
15
+ // and transports need a single import surface.
16
+ export { LIVE_PROTOCOL, LIVE_EVENT, LIVE_ERROR_CODES, COMMIT_CURSOR_HEADER, COMMIT_EPOCH_HEADER, DEFAULT_KEY_FIELD, encodeLiveEnvelope, encodeLiveFrame, readLiveEnvelope, isClientLiveFrame, isServerLiveFrame, encodeListDelta, applyListDelta } from "@velajs/live-protocol";
@@ -0,0 +1,22 @@
1
+ import type { CommitStamp, CursorLog, ResumeVerdict } from './live.types';
2
+ /**
3
+ * The in-core `CursorLog`: a bounded in-memory ring, epoch minted per process
4
+ * (Web Crypto). A restart rolls the epoch, so every reconnecting client falls
5
+ * back to snapshot — exactly the documented node/dev semantics. Within one
6
+ * process lifetime resume works for gaps the ring still covers; a trimmed gap
7
+ * degrades to snapshot (mirrors lunora's CDC retention rule,
8
+ * `ctx-db-cdc.ts` — retention gap → snapshot).
9
+ *
10
+ * The Cloudflare transport replaces this with a Durable-Object SQLite log so
11
+ * resume survives hibernation and eviction.
12
+ */
13
+ export declare class InMemoryCursorLog implements CursorLog {
14
+ private readonly maxEntries;
15
+ private readonly epoch;
16
+ private readonly entries;
17
+ private seq;
18
+ constructor(maxEntries?: number);
19
+ append(tags: string[]): CommitStamp;
20
+ current(): CommitStamp;
21
+ evaluateResume(sinceCursor: number, sinceEpoch: string, subscriptionTags: string[]): ResumeVerdict;
22
+ }