@velajs/vela 1.8.5 → 1.8.7

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.
@@ -401,7 +401,18 @@ const IMPORT_TYPE_HINT = 'Did you use `import type { X }`? TypeScript strips typ
401
401
  m.index,
402
402
  m
403
403
  ]));
404
- const dependencies = paramTypes.map((paramType, index)=>{
404
+ // Constructor arity. Normally `design:paramtypes` (emitDecoratorMetadata)
405
+ // gives the count, but some bundlers — notably esbuild (and therefore
406
+ // Wrangler) — do not emit it, leaving `paramTypes` empty even when
407
+ // `@Inject(token)` recorded explicit tokens. Fall back to the highest
408
+ // `@Inject` index so explicit-token constructors still resolve without
409
+ // emitted metadata. Slots with neither a param type nor an `@Inject` token
410
+ // still hit the unresolved-dependency error below.
411
+ const arity = Math.max(paramTypes.length, injectMetadata.reduce((max, m)=>Math.max(max, m.index + 1), 0));
412
+ const dependencies = Array.from({
413
+ length: arity
414
+ }, (_unused, index)=>{
415
+ const paramType = paramTypes[index];
405
416
  const meta = injectMap.get(index);
406
417
  const rawToken = meta?.token;
407
418
  const isForwardRef = rawToken instanceof ForwardRef;
@@ -1,18 +1,45 @@
1
+ import { getConstructorDependencies, getInjectMetadata } from "../container/decorators.js";
1
2
  // Resolve a class/token through the container if registered, otherwise treat
2
3
  // the input as a plain instance. Used by RouteManager and HandlerExecutor to
3
4
  // materialize middleware, guards, pipes, interceptors, and filters per request.
5
+ //
6
+ // When the input is a class that is NOT registered in the container, we fall
7
+ // back to `new clazz()` so plain parameterless helper classes (e.g. mixin
8
+ // guards, ad-hoc `@UseGuards(LocalGuard)` references) keep working. That
9
+ // fallback is unsafe for classes whose CONSTRUCTOR EXPECTS DEPENDENCIES,
10
+ // because zero-arg construction would leave every injected slot `undefined`
11
+ // — a silent failure mode that surfaces later as
12
+ // `Cannot read properties of undefined (reading '...')` deep inside the
13
+ // class. For those classes we throw a loud, actionable error instead.
14
+ //
15
+ // We treat "expects dependencies" as: any `@Inject(...)` parameter metadata
16
+ // OR a non-empty `design:paramtypes` (the SWC/TS emit for any constructor
17
+ // parameter). `@Injectable()` alone is NOT sufficient — `mixin()` and many
18
+ // parameterless guards are `@Injectable()` and still safe to `new` directly.
4
19
  export function instantiate(classOrInstance, container) {
5
20
  if (typeof classOrInstance === 'function') {
6
- if (container.has(classOrInstance)) {
7
- return container.resolve(classOrInstance);
21
+ const clazz = classOrInstance;
22
+ if (container.has(clazz)) {
23
+ return container.resolve(clazz);
8
24
  }
9
- return new classOrInstance();
25
+ if (constructorExpectsDependencies(clazz)) {
26
+ throw new Error(`Cannot instantiate ${clazz.name}: the class declares constructor ` + `dependencies (\`@Inject(...)\` parameters or typed constructor ` + `parameters), but no matching provider is registered in the ` + `container. Add it to a module's providers (and export it if used ` + `outside its declaring module) instead of relying on the bare ` + `\`new\` fallback, which would construct the class without ` + `honouring its dependency-injection metadata and leave every ` + `injected field \`undefined\`.`);
27
+ }
28
+ return new clazz();
10
29
  }
11
30
  if (container.has(classOrInstance)) {
12
31
  return container.resolve(classOrInstance);
13
32
  }
14
33
  return classOrInstance;
15
34
  }
35
+ // True when calling `new clazz()` would leave an injected slot `undefined`:
36
+ // either the class has explicit `@Inject(...)` metadata, or it has any typed
37
+ // constructor parameter (TS/SWC's `design:paramtypes` emit). Parameterless
38
+ // `@Injectable()` classes return false — `new()` is safe for them.
39
+ function constructorExpectsDependencies(clazz) {
40
+ if (getInjectMetadata(clazz).length > 0) return true;
41
+ return getConstructorDependencies(clazz).length > 0;
42
+ }
16
43
  export function instantiateMany(items, container) {
17
44
  return items.map((item)=>instantiate(item, container));
18
45
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velajs/vela",
3
- "version": "1.8.5",
3
+ "version": "1.8.7",
4
4
  "description": "NestJS-compatible framework for edge runtimes, powered by Hono",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -29,6 +29,14 @@
29
29
  "LICENSE",
30
30
  "CHANGELOG.md"
31
31
  ],
32
+ "scripts": {
33
+ "build": "rm -rf dist && swc src -d dist --strip-leading-paths && tsc --emitDeclarationOnly",
34
+ "test": "vitest run",
35
+ "test:workers": "vitest run --config vitest.config.workers.ts",
36
+ "typecheck": "tsc --noEmit",
37
+ "prepare": "swc src -d dist --strip-leading-paths && tsc --emitDeclarationOnly",
38
+ "prepublishOnly": "pnpm run typecheck && pnpm test"
39
+ },
32
40
  "sideEffects": [
33
41
  "./dist/index.js"
34
42
  ],
@@ -58,24 +66,26 @@
58
66
  "node": ">=20"
59
67
  },
60
68
  "dependencies": {
61
- "hono": "^4"
69
+ "hono": "^4.12.26"
70
+ },
71
+ "packageManager": "pnpm@10.20.0",
72
+ "pnpm": {
73
+ "onlyBuiltDependencies": [
74
+ "@swc/core",
75
+ "esbuild",
76
+ "workerd"
77
+ ]
62
78
  },
63
79
  "devDependencies": {
64
- "@cloudflare/vitest-pool-workers": "^0.15.2",
65
- "@swc/cli": "^0.8.0",
66
- "@swc/core": "^1.15.11",
80
+ "@cloudflare/vitest-pool-workers": "^0.16.18",
81
+ "@swc/cli": "^0.8.1",
82
+ "@swc/core": "^1.15.41",
67
83
  "@types/bun": "latest",
68
- "@vitest/runner": "^4.1.5",
69
- "@vitest/snapshot": "^4.1.5",
70
- "typescript": "^5",
84
+ "@vitest/runner": "^4.1.9",
85
+ "@vitest/snapshot": "^4.1.9",
86
+ "typescript": "^6.0.3",
71
87
  "unplugin-swc": "^1.5.9",
72
- "vitest": "^4.1.5",
73
- "zod": "^4.3.6"
74
- },
75
- "scripts": {
76
- "build": "rm -rf dist && swc src -d dist --strip-leading-paths && tsc --emitDeclarationOnly",
77
- "test": "vitest run",
78
- "test:workers": "vitest run --config vitest.config.workers.ts",
79
- "typecheck": "tsc --noEmit"
88
+ "vitest": "^4.1.9",
89
+ "zod": "^4.4.3"
80
90
  }
81
- }
91
+ }