@velajs/vela 1.14.0 → 1.16.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 (50) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/application.d.ts +10 -1
  3. package/dist/application.js +12 -0
  4. package/dist/config/config.module.d.ts +8 -1
  5. package/dist/config/config.module.js +89 -42
  6. package/dist/config/config.service.d.ts +17 -4
  7. package/dist/config/config.service.js +18 -20
  8. package/dist/config/config.store.d.ts +46 -0
  9. package/dist/config/config.store.js +119 -0
  10. package/dist/config/config.tokens.d.ts +11 -0
  11. package/dist/config/config.tokens.js +12 -0
  12. package/dist/config/config.types.d.ts +37 -1
  13. package/dist/config/config.types.js +4 -1
  14. package/dist/config/index.d.ts +5 -2
  15. package/dist/config/index.js +3 -1
  16. package/dist/config/register-as.d.ts +52 -0
  17. package/dist/config/register-as.js +39 -0
  18. package/dist/container/container.d.ts +8 -1
  19. package/dist/container/container.js +39 -1
  20. package/dist/container/index.d.ts +2 -2
  21. package/dist/container/index.js +1 -1
  22. package/dist/container/types.d.ts +23 -0
  23. package/dist/container/types.js +6 -1
  24. package/dist/{storage → crypto}/signed-url.js +5 -0
  25. package/dist/factory/bootstrap.js +10 -0
  26. package/dist/http/decorators.d.ts +18 -9
  27. package/dist/http/decorators.js +4 -1
  28. package/dist/http/index.d.ts +4 -0
  29. package/dist/http/index.js +2 -0
  30. package/dist/http/route-map.d.ts +30 -0
  31. package/dist/http/route-map.js +27 -0
  32. package/dist/http/route.manager.d.ts +24 -0
  33. package/dist/http/route.manager.js +28 -1
  34. package/dist/http/types.d.ts +2 -0
  35. package/dist/http/url/index.d.ts +4 -0
  36. package/dist/http/url/index.js +3 -0
  37. package/dist/http/url/signed-url.guard.d.ts +27 -0
  38. package/dist/http/url/signed-url.guard.js +62 -0
  39. package/dist/http/url/signing-secret.d.ts +20 -0
  40. package/dist/http/url/signing-secret.js +24 -0
  41. package/dist/http/url/url-generator.service.d.ts +52 -0
  42. package/dist/http/url/url-generator.service.js +104 -0
  43. package/dist/index.d.ts +9 -5
  44. package/dist/index.js +5 -3
  45. package/dist/openapi/document.js +2 -1
  46. package/dist/registry/types.d.ts +2 -0
  47. package/dist/storage/index.d.ts +2 -2
  48. package/dist/storage/index.js +4 -1
  49. package/package.json +6 -2
  50. /package/dist/{storage → crypto}/signed-url.d.ts +0 -0
@@ -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
@@ -6,17 +6,21 @@ export { bootstrap } from './factory/bootstrap';
6
6
  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
- export { Container, Injectable, Inject, Optional, InjectionToken, ForwardRef, forwardRef, ModuleRef, ModuleVisibilityError, MultipleProvidersFoundError, ROOT_MODULE_ID, mixin, } from './container/index';
10
- export type { Type, Token, InferToken, InferTokens, InjectableOptions, ProviderOptions, ModuleScope, ContainerOptions, Diagnostics, } from './container/index';
9
+ export { Container, Injectable, Inject, Optional, InjectionToken, ForwardRef, forwardRef, ModuleRef, ModuleVisibilityError, MultipleProvidersFoundError, ROOT_MODULE_ID, mixin, describeToken, } from './container/index';
10
+ export type { Type, Token, InferToken, InferTokens, InjectableOptions, ProviderOptions, ModuleScope, ModuleDescription, ContainerOptions, Diagnostics, } from './container/index';
11
+ export type { RouteDescription } from './http/route.manager';
11
12
  export { METADATA_KEYS, HttpMethod, ParamType, Scope } from './constants';
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
+ 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';
13
17
  export { REQUEST_CONTEXT } from './http/request-context';
14
18
  export type { RequestContext } from './http/request-context';
15
19
  export { enableAmbientContainer, getCurrentContainer, getCurrentRequestContext, } from './http/ambient';
16
20
  export { Logger, LogLevel } from './services/index';
17
21
  export type { LoggerService, ContextProvider, Writer, LoggerLevelName } from './services/index';
18
- export { ConfigModule, ConfigService, CONFIG_OPTIONS } from './config/index';
19
- 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';
20
24
  export { HttpModule, HttpService, HTTP_MODULE_OPTIONS, HttpRequestException } from './fetch/index';
21
25
  export type { HttpModuleOptions, HttpResponse, HttpRequestConfig } from './fetch/index';
22
26
  export { CorsModule, CORS_OPTIONS } from './cors/index';
package/dist/index.js CHANGED
@@ -7,11 +7,13 @@ export { bootstrap } from "./factory/bootstrap.js";
7
7
  // OpenAPI
8
8
  export { createOpenApiDocument, ApiDoc, ApiTags, ApiResponse, zodToJsonSchema } from "./openapi/index.js";
9
9
  // DI Container
10
- export { Container, Injectable, Inject, Optional, InjectionToken, ForwardRef, forwardRef, ModuleRef, ModuleVisibilityError, MultipleProvidersFoundError, ROOT_MODULE_ID, mixin } from "./container/index.js";
10
+ export { Container, Injectable, Inject, Optional, InjectionToken, ForwardRef, forwardRef, ModuleRef, ModuleVisibilityError, MultipleProvidersFoundError, ROOT_MODULE_ID, mixin, describeToken } from "./container/index.js";
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
@@ -182,7 +182,8 @@ function buildOperation(controller, route, pathString, registry) {
182
182
  if (requestBody) operation.requestBody = requestBody;
183
183
  if (docMeta?.summary) operation.summary = docMeta.summary;
184
184
  if (docMeta?.description) operation.description = docMeta.description;
185
- if (docMeta?.operationId) operation.operationId = docMeta.operationId;
185
+ const operationId = docMeta?.operationId ?? route.name;
186
+ if (operationId) operation.operationId = operationId;
186
187
  if (docMeta?.deprecated) operation.deprecated = docMeta.deprecated;
187
188
  if (mergedTags.length > 0) operation.tags = mergedTags;
188
189
  return operation;
@@ -21,6 +21,8 @@ export interface RouteDefinition {
21
21
  path: string;
22
22
  handlerName: string | symbol;
23
23
  version?: number | number[];
24
+ /** Route name for URL generation / OpenAPI operationId (`@Get(path, { name })`). */
25
+ name?: string;
24
26
  }
25
27
  export interface ParameterMetadata {
26
28
  index: number;
@@ -1,4 +1,4 @@
1
- export { signUrl, verifySignedUrl } from './signed-url';
2
- export type { SignedUrlOptions } from './signed-url';
1
+ export { signUrl, verifySignedUrl } from '../crypto/signed-url';
2
+ export type { SignedUrlOptions } from '../crypto/signed-url';
3
3
  export { expandPathTemplate, joinStoragePath } from './path-template';
4
4
  export type { StorageBody, StorageDriver, PresignMethod, UploadOptions, UploadResult, DownloadResult, PresignedUrlResult, } from './storage.types';
@@ -1,2 +1,5 @@
1
- export { signUrl, verifySignedUrl } from "./signed-url.js";
1
+ // The HMAC signed-URL util now lives in `src/crypto/` (core consumes it there
2
+ // for the URL generator + guard). Re-exported here so `@velajs/vela/storage`'s
3
+ // public API is unchanged for existing consumers.
4
+ export { signUrl, verifySignedUrl } from "../crypto/signed-url.js";
2
5
  export { expandPathTemplate, joinStoragePath } from "./path-template.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velajs/vela",
3
- "version": "1.14.0",
3
+ "version": "1.16.0",
4
4
  "description": "NestJS-compatible framework for edge runtimes, powered by Hono",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -102,6 +102,7 @@
102
102
  "@vitest/runner": "^4.1.9",
103
103
  "@vitest/snapshot": "^4.1.9",
104
104
  "intl-messageformat": "^11.2.9",
105
+ "typedoc": "~0.28.19",
105
106
  "typescript": "^6.0.3",
106
107
  "unplugin-swc": "^1.5.9",
107
108
  "vitest": "^4.1.9",
@@ -112,6 +113,9 @@
112
113
  "test": "vitest run",
113
114
  "test:workers": "vitest run --config vitest.config.workers.ts",
114
115
  "test:workers:als": "vitest run --config vitest.config.workers-als.ts",
115
- "typecheck": "tsc --noEmit"
116
+ "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.type-tests.json",
117
+ "check:skill": "node scripts/check-skill.mjs",
118
+ "docs": "typedoc",
119
+ "docs:check": "typedoc --emit none"
116
120
  }
117
121
  }
File without changes