@buenojs/bueno 0.8.4 → 0.8.6
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/README.md +264 -17
- package/dist/cli/{index.js → bin.js} +413 -332
- package/dist/container/index.js +273 -0
- package/dist/context/index.js +219 -0
- package/dist/database/index.js +493 -0
- package/dist/frontend/index.js +7697 -0
- package/dist/graphql/index.js +2156 -0
- package/dist/health/index.js +364 -0
- package/dist/i18n/index.js +345 -0
- package/dist/index.js +9694 -5047
- package/dist/jobs/index.js +819 -0
- package/dist/lock/index.js +367 -0
- package/dist/logger/index.js +281 -0
- package/dist/metrics/index.js +289 -0
- package/dist/middleware/index.js +77 -0
- package/dist/migrations/index.js +571 -0
- package/dist/modules/index.js +3411 -0
- package/dist/notification/index.js +484 -0
- package/dist/observability/index.js +331 -0
- package/dist/openapi/index.js +795 -0
- package/dist/orm/index.js +1356 -0
- package/dist/router/index.js +886 -0
- package/dist/rpc/index.js +691 -0
- package/dist/schema/index.js +400 -0
- package/dist/telemetry/index.js +595 -0
- package/dist/template/index.js +640 -0
- package/dist/templates/index.js +640 -0
- package/dist/testing/index.js +1111 -0
- package/dist/types/index.js +60 -0
- package/llms.txt +231 -0
- package/package.json +125 -27
- package/src/cache/index.ts +2 -1
- package/src/cli/ARCHITECTURE.md +3 -3
- package/src/cli/bin.ts +2 -2
- package/src/cli/commands/build.ts +183 -165
- package/src/cli/commands/dev.ts +96 -89
- package/src/cli/commands/generate.ts +142 -111
- package/src/cli/commands/help.ts +20 -16
- package/src/cli/commands/index.ts +3 -6
- package/src/cli/commands/migration.ts +124 -105
- package/src/cli/commands/new.ts +294 -232
- package/src/cli/commands/start.ts +81 -79
- package/src/cli/core/args.ts +68 -50
- package/src/cli/core/console.ts +89 -95
- package/src/cli/core/index.ts +4 -4
- package/src/cli/core/prompt.ts +65 -62
- package/src/cli/core/spinner.ts +23 -20
- package/src/cli/index.ts +46 -38
- package/src/cli/templates/database/index.ts +37 -18
- package/src/cli/templates/database/mysql.ts +3 -3
- package/src/cli/templates/database/none.ts +2 -2
- package/src/cli/templates/database/postgresql.ts +3 -3
- package/src/cli/templates/database/sqlite.ts +3 -3
- package/src/cli/templates/deploy.ts +29 -26
- package/src/cli/templates/docker.ts +41 -30
- package/src/cli/templates/frontend/index.ts +33 -15
- package/src/cli/templates/frontend/none.ts +2 -2
- package/src/cli/templates/frontend/react.ts +18 -18
- package/src/cli/templates/frontend/solid.ts +15 -15
- package/src/cli/templates/frontend/svelte.ts +17 -17
- package/src/cli/templates/frontend/vue.ts +15 -15
- package/src/cli/templates/generators/index.ts +29 -29
- package/src/cli/templates/generators/types.ts +21 -21
- package/src/cli/templates/index.ts +6 -6
- package/src/cli/templates/project/api.ts +37 -36
- package/src/cli/templates/project/default.ts +25 -25
- package/src/cli/templates/project/fullstack.ts +28 -26
- package/src/cli/templates/project/index.ts +55 -16
- package/src/cli/templates/project/minimal.ts +17 -12
- package/src/cli/templates/project/types.ts +10 -5
- package/src/cli/templates/project/website.ts +15 -15
- package/src/cli/utils/fs.ts +55 -41
- package/src/cli/utils/index.ts +3 -3
- package/src/cli/utils/strings.ts +47 -33
- package/src/cli/utils/version.ts +14 -8
- package/src/config/env-validation.ts +100 -0
- package/src/config/env.ts +169 -41
- package/src/config/index.ts +28 -20
- package/src/config/loader.ts +25 -16
- package/src/config/merge.ts +21 -10
- package/src/config/types.ts +566 -25
- package/src/config/validation.ts +215 -7
- package/src/container/forward-ref.ts +22 -22
- package/src/container/index.ts +34 -12
- package/src/context/index.ts +11 -1
- package/src/database/index.ts +7 -190
- package/src/database/orm/builder.ts +457 -0
- package/src/database/orm/casts/index.ts +130 -0
- package/src/database/orm/casts/types.ts +25 -0
- package/src/database/orm/compiler.ts +304 -0
- package/src/database/orm/hooks/index.ts +114 -0
- package/src/database/orm/index.ts +61 -0
- package/src/database/orm/model-registry.ts +59 -0
- package/src/database/orm/model.ts +821 -0
- package/src/database/orm/relationships/base.ts +146 -0
- package/src/database/orm/relationships/belongs-to-many.ts +179 -0
- package/src/database/orm/relationships/belongs-to.ts +56 -0
- package/src/database/orm/relationships/has-many.ts +45 -0
- package/src/database/orm/relationships/has-one.ts +41 -0
- package/src/database/orm/relationships/index.ts +11 -0
- package/src/database/orm/scopes/index.ts +55 -0
- package/src/events/__tests__/event-system.test.ts +235 -0
- package/src/events/config.ts +238 -0
- package/src/events/example-usage.ts +185 -0
- package/src/events/index.ts +278 -0
- package/src/events/manager.ts +385 -0
- package/src/events/registry.ts +182 -0
- package/src/events/types.ts +124 -0
- package/src/frontend/api-routes.ts +65 -23
- package/src/frontend/bundler.ts +76 -34
- package/src/frontend/console-client.ts +2 -2
- package/src/frontend/console-stream.ts +94 -38
- package/src/frontend/dev-server.ts +94 -46
- package/src/frontend/file-router.ts +61 -19
- package/src/frontend/frameworks/index.ts +37 -10
- package/src/frontend/frameworks/react.ts +10 -8
- package/src/frontend/frameworks/solid.ts +11 -9
- package/src/frontend/frameworks/svelte.ts +15 -9
- package/src/frontend/frameworks/vue.ts +13 -11
- package/src/frontend/hmr-client.ts +12 -10
- package/src/frontend/hmr.ts +146 -103
- package/src/frontend/index.ts +14 -5
- package/src/frontend/islands.ts +41 -22
- package/src/frontend/isr.ts +59 -37
- package/src/frontend/layout.ts +36 -21
- package/src/frontend/ssr/react.ts +74 -27
- package/src/frontend/ssr/solid.ts +54 -20
- package/src/frontend/ssr/svelte.ts +48 -14
- package/src/frontend/ssr/vue.ts +50 -18
- package/src/frontend/ssr.ts +83 -39
- package/src/frontend/types.ts +91 -56
- package/src/graphql/built-in-engine.ts +598 -0
- package/src/graphql/context-builder.ts +110 -0
- package/src/graphql/decorators.ts +358 -0
- package/src/graphql/execution-pipeline.ts +227 -0
- package/src/graphql/graphql-module.ts +563 -0
- package/src/graphql/index.ts +101 -0
- package/src/graphql/metadata.ts +237 -0
- package/src/graphql/schema-builder.ts +319 -0
- package/src/graphql/subscription-handler.ts +283 -0
- package/src/graphql/types.ts +324 -0
- package/src/health/index.ts +21 -9
- package/src/i18n/engine.ts +305 -0
- package/src/i18n/index.ts +38 -0
- package/src/i18n/loader.ts +218 -0
- package/src/i18n/middleware.ts +164 -0
- package/src/i18n/negotiator.ts +162 -0
- package/src/i18n/types.ts +158 -0
- package/src/index.ts +182 -27
- package/src/jobs/drivers/memory.ts +315 -0
- package/src/jobs/drivers/redis.ts +459 -0
- package/src/jobs/index.ts +30 -0
- package/src/jobs/queue.ts +281 -0
- package/src/jobs/types.ts +295 -0
- package/src/jobs/worker.ts +380 -0
- package/src/logger/index.ts +1 -3
- package/src/logger/transports/index.ts +62 -22
- package/src/metrics/index.ts +25 -16
- package/src/migrations/index.ts +9 -0
- package/src/modules/filters.ts +13 -17
- package/src/modules/guards.ts +49 -26
- package/src/modules/index.ts +457 -299
- package/src/modules/interceptors.ts +58 -20
- package/src/modules/lazy.ts +11 -19
- package/src/modules/lifecycle.ts +15 -7
- package/src/modules/metadata.ts +15 -5
- package/src/modules/pipes.ts +94 -72
- package/src/notification/channels/base.ts +68 -0
- package/src/notification/channels/email.ts +105 -0
- package/src/notification/channels/push.ts +104 -0
- package/src/notification/channels/sms.ts +105 -0
- package/src/notification/channels/whatsapp.ts +104 -0
- package/src/notification/index.ts +48 -0
- package/src/notification/service.ts +354 -0
- package/src/notification/types.ts +344 -0
- package/src/observability/__tests__/observability.test.ts +483 -0
- package/src/observability/breadcrumbs.ts +114 -0
- package/src/observability/index.ts +136 -0
- package/src/observability/interceptor.ts +85 -0
- package/src/observability/service.ts +303 -0
- package/src/observability/trace.ts +37 -0
- package/src/observability/types.ts +196 -0
- package/src/openapi/__tests__/decorators.test.ts +335 -0
- package/src/openapi/__tests__/document-builder.test.ts +285 -0
- package/src/openapi/__tests__/route-scanner.test.ts +334 -0
- package/src/openapi/__tests__/schema-generator.test.ts +275 -0
- package/src/openapi/decorators.ts +328 -0
- package/src/openapi/document-builder.ts +274 -0
- package/src/openapi/index.ts +112 -0
- package/src/openapi/metadata.ts +112 -0
- package/src/openapi/route-scanner.ts +289 -0
- package/src/openapi/schema-generator.ts +256 -0
- package/src/openapi/swagger-module.ts +166 -0
- package/src/openapi/types.ts +398 -0
- package/src/orm/index.ts +10 -0
- package/src/rpc/index.ts +3 -1
- package/src/schema/index.ts +9 -0
- package/src/security/index.ts +15 -6
- package/src/ssg/index.ts +9 -8
- package/src/telemetry/index.ts +76 -22
- package/src/template/index.ts +7 -0
- package/src/templates/engine.ts +224 -0
- package/src/templates/index.ts +9 -0
- package/src/templates/loader.ts +331 -0
- package/src/templates/renderers/markdown.ts +212 -0
- package/src/templates/renderers/simple.ts +269 -0
- package/src/templates/types.ts +154 -0
- package/src/testing/index.ts +100 -27
- package/src/types/optional-deps.d.ts +347 -187
- package/src/validation/index.ts +92 -2
- package/src/validation/schemas.ts +536 -0
- package/tests/integration/cli.test.ts +19 -19
- package/tests/integration/fullstack.test.ts +4 -4
- package/tests/unit/cli.test.ts +1 -1
- package/tests/unit/database.test.ts +2 -72
- package/tests/unit/env-validation.test.ts +166 -0
- package/tests/unit/events.test.ts +910 -0
- package/tests/unit/graphql.test.ts +991 -0
- package/tests/unit/i18n.test.ts +455 -0
- package/tests/unit/jobs.test.ts +493 -0
- package/tests/unit/notification.test.ts +988 -0
- package/tests/unit/observability.test.ts +453 -0
- package/tests/unit/orm/builder.test.ts +323 -0
- package/tests/unit/orm/casts.test.ts +179 -0
- package/tests/unit/orm/compiler.test.ts +220 -0
- package/tests/unit/orm/eager-loading.test.ts +285 -0
- package/tests/unit/orm/hooks.test.ts +191 -0
- package/tests/unit/orm/model.test.ts +373 -0
- package/tests/unit/orm/relationships.test.ts +303 -0
- package/tests/unit/orm/scopes.test.ts +74 -0
- package/tests/unit/templates-simple.test.ts +53 -0
- package/tests/unit/templates.test.ts +454 -0
- package/tests/unit/validation.test.ts +18 -24
- package/tsconfig.json +11 -3
package/src/modules/index.ts
CHANGED
|
@@ -7,108 +7,108 @@
|
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
Container,
|
|
10
|
+
type ForwardRef,
|
|
10
11
|
type Provider,
|
|
11
12
|
type Token,
|
|
12
|
-
type ForwardRef,
|
|
13
13
|
forwardRef,
|
|
14
|
+
getContainerMetadata,
|
|
15
|
+
getInjectTokens,
|
|
14
16
|
isForwardRef,
|
|
15
17
|
resolveForwardRef,
|
|
16
|
-
getInjectTokens,
|
|
17
18
|
setContainerMetadata,
|
|
18
|
-
getContainerMetadata,
|
|
19
19
|
} from "../container";
|
|
20
|
+
import type { Context } from "../context";
|
|
21
|
+
import { Router } from "../router";
|
|
22
|
+
import type { RouteHandler } from "../types";
|
|
23
|
+
import {
|
|
24
|
+
AllExceptionsFilter,
|
|
25
|
+
Catch,
|
|
26
|
+
type ExceptionFilter,
|
|
27
|
+
type ExecuteFiltersOptions,
|
|
28
|
+
type Filter,
|
|
29
|
+
type FilterFn,
|
|
30
|
+
HttpExceptionFilter,
|
|
31
|
+
NotFoundFilter,
|
|
32
|
+
UseFilters,
|
|
33
|
+
ValidationFilter,
|
|
34
|
+
canHandleException,
|
|
35
|
+
createDefaultErrorResponse,
|
|
36
|
+
createInternalErrorResponse,
|
|
37
|
+
executeFilter,
|
|
38
|
+
findAndExecuteFilter,
|
|
39
|
+
getCatchType,
|
|
40
|
+
getClassFilters,
|
|
41
|
+
getMethodFilters,
|
|
42
|
+
isExceptionFilter,
|
|
43
|
+
isFilterFn,
|
|
44
|
+
} from "./filters";
|
|
45
|
+
import {
|
|
46
|
+
type CanActivate,
|
|
47
|
+
type Guard,
|
|
48
|
+
type GuardFn,
|
|
49
|
+
createForbiddenResponse,
|
|
50
|
+
executeGuards,
|
|
51
|
+
getClassGuards,
|
|
52
|
+
getMethodGuards,
|
|
53
|
+
} from "./guards";
|
|
54
|
+
import {
|
|
55
|
+
type CallHandler,
|
|
56
|
+
type Interceptor,
|
|
57
|
+
type InterceptorFn,
|
|
58
|
+
type NestInterceptor,
|
|
59
|
+
executeInterceptors,
|
|
60
|
+
getClassInterceptors,
|
|
61
|
+
getMethodInterceptors,
|
|
62
|
+
isInterceptorFn,
|
|
63
|
+
isNestInterceptor,
|
|
64
|
+
} from "./interceptors";
|
|
20
65
|
import {
|
|
21
|
-
type LazyModuleLoader,
|
|
22
66
|
type Constructor as LazyConstructor,
|
|
23
|
-
type ModuleLoaderFn,
|
|
24
|
-
type LazyModuleMetadata,
|
|
25
|
-
LazyModuleLoaderImpl,
|
|
26
67
|
LazyModule,
|
|
27
|
-
|
|
68
|
+
type LazyModuleLoader,
|
|
69
|
+
LazyModuleLoaderImpl,
|
|
70
|
+
type LazyModuleMetadata,
|
|
71
|
+
LazyModuleRegistry,
|
|
28
72
|
MODULE_LOADER_TOKEN,
|
|
73
|
+
ModuleLoader,
|
|
74
|
+
type ModuleLoaderFn,
|
|
29
75
|
getLazyMetadata,
|
|
30
76
|
isLazyModule,
|
|
31
|
-
LazyModuleRegistry,
|
|
32
77
|
} from "./lazy";
|
|
33
|
-
import type { Context } from "../context";
|
|
34
|
-
import { Router } from "../router";
|
|
35
|
-
import type { RouteHandler } from "../types";
|
|
36
78
|
import {
|
|
79
|
+
type ApplicationLifecycle,
|
|
80
|
+
type BeforeApplicationShutdown,
|
|
81
|
+
type FullLifecycle,
|
|
37
82
|
LifecycleHookManager,
|
|
38
|
-
|
|
39
|
-
type OnModuleInit,
|
|
83
|
+
type OnAfterRequest,
|
|
40
84
|
type OnApplicationBootstrap,
|
|
41
|
-
type OnModuleDestroy,
|
|
42
|
-
type BeforeApplicationShutdown,
|
|
43
85
|
type OnApplicationShutdown,
|
|
44
86
|
type OnBeforeRequest,
|
|
45
|
-
type
|
|
87
|
+
type OnModuleDestroy,
|
|
88
|
+
type OnModuleInit,
|
|
46
89
|
type OnRequestError,
|
|
47
|
-
type ApplicationLifecycle,
|
|
48
90
|
type RequestLifecycle,
|
|
49
|
-
|
|
50
|
-
isOnModuleInit,
|
|
51
|
-
isOnApplicationBootstrap,
|
|
52
|
-
isOnModuleDestroy,
|
|
91
|
+
ShutdownSignalHandler,
|
|
53
92
|
isBeforeApplicationShutdown,
|
|
93
|
+
isOnAfterRequest,
|
|
94
|
+
isOnApplicationBootstrap,
|
|
54
95
|
isOnApplicationShutdown,
|
|
55
96
|
isOnBeforeRequest,
|
|
56
|
-
|
|
97
|
+
isOnModuleDestroy,
|
|
98
|
+
isOnModuleInit,
|
|
57
99
|
isOnRequestError,
|
|
58
100
|
} from "./lifecycle";
|
|
59
101
|
import {
|
|
60
|
-
type
|
|
61
|
-
type CanActivate,
|
|
62
|
-
type GuardFn,
|
|
63
|
-
getClassGuards,
|
|
64
|
-
getMethodGuards,
|
|
65
|
-
executeGuards,
|
|
66
|
-
createForbiddenResponse,
|
|
67
|
-
} from "./guards";
|
|
68
|
-
import {
|
|
69
|
-
type Interceptor,
|
|
70
|
-
type NestInterceptor,
|
|
71
|
-
type InterceptorFn,
|
|
72
|
-
type CallHandler,
|
|
73
|
-
getClassInterceptors,
|
|
74
|
-
getMethodInterceptors,
|
|
75
|
-
executeInterceptors,
|
|
76
|
-
isNestInterceptor,
|
|
77
|
-
isInterceptorFn,
|
|
78
|
-
} from "./interceptors";
|
|
79
|
-
import {
|
|
102
|
+
type ParameterPipeMetadata,
|
|
80
103
|
type Pipe,
|
|
81
|
-
type PipeTransform,
|
|
82
|
-
type PipeFn,
|
|
83
104
|
type PipeContext,
|
|
84
|
-
type
|
|
85
|
-
|
|
105
|
+
type PipeFn,
|
|
106
|
+
type PipeTransform,
|
|
107
|
+
createBadRequestResponse,
|
|
86
108
|
executePipes,
|
|
87
109
|
extractParameterValue,
|
|
88
|
-
|
|
110
|
+
getMethodPipes,
|
|
89
111
|
} from "./pipes";
|
|
90
|
-
import {
|
|
91
|
-
type Filter,
|
|
92
|
-
type ExceptionFilter,
|
|
93
|
-
type FilterFn,
|
|
94
|
-
type ExecuteFiltersOptions,
|
|
95
|
-
UseFilters,
|
|
96
|
-
Catch,
|
|
97
|
-
getClassFilters,
|
|
98
|
-
getMethodFilters,
|
|
99
|
-
getCatchType,
|
|
100
|
-
canHandleException,
|
|
101
|
-
isExceptionFilter,
|
|
102
|
-
isFilterFn,
|
|
103
|
-
executeFilter,
|
|
104
|
-
findAndExecuteFilter,
|
|
105
|
-
HttpExceptionFilter,
|
|
106
|
-
ValidationFilter,
|
|
107
|
-
NotFoundFilter,
|
|
108
|
-
AllExceptionsFilter,
|
|
109
|
-
createDefaultErrorResponse,
|
|
110
|
-
createInternalErrorResponse,
|
|
111
|
-
} from "./filters";
|
|
112
112
|
|
|
113
113
|
// ============= Types =============
|
|
114
114
|
|
|
@@ -129,14 +129,14 @@ export interface LifecycleHooks {
|
|
|
129
129
|
// ============= Metadata Storage =============
|
|
130
130
|
// Import metadata storage and decorators from isolated module to avoid circular dependencies
|
|
131
131
|
import {
|
|
132
|
-
setMetadata,
|
|
133
|
-
getMetadata,
|
|
134
|
-
setPrototypeMetadata,
|
|
135
|
-
getPrototypeMetadata,
|
|
136
|
-
Injectable,
|
|
137
132
|
Controller,
|
|
133
|
+
Injectable,
|
|
138
134
|
Module,
|
|
139
135
|
type ModuleMetadata,
|
|
136
|
+
getMetadata,
|
|
137
|
+
getPrototypeMetadata,
|
|
138
|
+
setMetadata,
|
|
139
|
+
setPrototypeMetadata,
|
|
140
140
|
} from "./metadata";
|
|
141
141
|
|
|
142
142
|
// Re-export decorators and types for external use
|
|
@@ -166,7 +166,10 @@ export function Inject(token: Token | ForwardRef<Token>): ParameterDecorator {
|
|
|
166
166
|
) => {
|
|
167
167
|
const targetObj = target as object;
|
|
168
168
|
const existingTokens: Array<Token | ForwardRef<Token>> =
|
|
169
|
-
getContainerMetadata<Array<Token | ForwardRef<Token>>>(
|
|
169
|
+
getContainerMetadata<Array<Token | ForwardRef<Token>>>(
|
|
170
|
+
targetObj,
|
|
171
|
+
"inject:tokens",
|
|
172
|
+
) ?? [];
|
|
170
173
|
existingTokens[parameterIndex] = token;
|
|
171
174
|
setContainerMetadata(targetObj, "inject:tokens", existingTokens);
|
|
172
175
|
};
|
|
@@ -250,9 +253,9 @@ export class AppModule {
|
|
|
250
253
|
|
|
251
254
|
// Add providers (normalize class references to provider objects)
|
|
252
255
|
if (metadata.providers) {
|
|
253
|
-
const normalizedProviders = metadata.providers.map(p => {
|
|
256
|
+
const normalizedProviders = metadata.providers.map((p) => {
|
|
254
257
|
// If it's a class constructor (function) without token, normalize it
|
|
255
|
-
if (typeof p ===
|
|
258
|
+
if (typeof p === "function" && !p.token) {
|
|
256
259
|
return { token: p, useClass: p };
|
|
257
260
|
}
|
|
258
261
|
return p;
|
|
@@ -349,9 +352,12 @@ export class Application {
|
|
|
349
352
|
private routeGuardMetadata: Map<string, RouteGuardMetadata> = new Map();
|
|
350
353
|
private routePipeMetadata: Map<string, RoutePipeMetadata> = new Map();
|
|
351
354
|
private routeFilterMetadata: Map<string, RouteFilterMetadata> = new Map();
|
|
352
|
-
private routeInterceptorMetadata: Map<string, RouteInterceptorMetadata> =
|
|
355
|
+
private routeInterceptorMetadata: Map<string, RouteInterceptorMetadata> =
|
|
356
|
+
new Map();
|
|
353
357
|
private moduleLoader: ModuleLoader;
|
|
354
358
|
private loadedLazyModules = new Set<Constructor>();
|
|
359
|
+
// biome-ignore lint/suspicious/noExplicitAny: WebSocket data type is user-defined
|
|
360
|
+
private websocketHandler: Bun.WebSocketHandler<any> | null = null;
|
|
355
361
|
|
|
356
362
|
constructor(moduleClass: Constructor) {
|
|
357
363
|
this.container = new Container();
|
|
@@ -381,6 +387,37 @@ export class Application {
|
|
|
381
387
|
return this;
|
|
382
388
|
}
|
|
383
389
|
|
|
390
|
+
/**
|
|
391
|
+
* Get the list of global guards (live reference).
|
|
392
|
+
* Used by the GraphQL module to run guards on resolver methods.
|
|
393
|
+
*/
|
|
394
|
+
getGlobalGuards(): Guard[] {
|
|
395
|
+
return this.globalGuards;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Get the list of global interceptors (live reference).
|
|
400
|
+
* Used by the GraphQL module to run interceptors on resolver methods.
|
|
401
|
+
*/
|
|
402
|
+
getGlobalInterceptors(): Interceptor[] {
|
|
403
|
+
return this.globalInterceptors;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Register a WebSocket handler to be used when the server starts.
|
|
408
|
+
* Called by the GraphQL module to enable subscriptions on the same port.
|
|
409
|
+
*
|
|
410
|
+
* @example
|
|
411
|
+
* ```typescript
|
|
412
|
+
* app.setWebSocketHandler(subscriptionHandler.getWebSocketConfig());
|
|
413
|
+
* await app.listen(3000);
|
|
414
|
+
* ```
|
|
415
|
+
*/
|
|
416
|
+
// biome-ignore lint/suspicious/noExplicitAny: WebSocket data type is user-defined
|
|
417
|
+
setWebSocketHandler(handler: Bun.WebSocketHandler<any>): void {
|
|
418
|
+
this.websocketHandler = handler;
|
|
419
|
+
}
|
|
420
|
+
|
|
384
421
|
/**
|
|
385
422
|
* Add global pipes that apply to all parameters
|
|
386
423
|
* Global pipes run before parameter decorator pipes
|
|
@@ -454,8 +491,13 @@ export class Application {
|
|
|
454
491
|
|
|
455
492
|
for (const provider of providers) {
|
|
456
493
|
const token = provider.token;
|
|
457
|
-
const instance = this.container.resolve(
|
|
458
|
-
|
|
494
|
+
const instance = this.container.resolve(
|
|
495
|
+
token as import("../container").Token,
|
|
496
|
+
);
|
|
497
|
+
this.providerInstances.set(
|
|
498
|
+
token as import("../container").Token,
|
|
499
|
+
instance,
|
|
500
|
+
);
|
|
459
501
|
this.lifecycleManager.registerInstance(instance);
|
|
460
502
|
}
|
|
461
503
|
}
|
|
@@ -486,21 +528,35 @@ export class Application {
|
|
|
486
528
|
// Create controller instance
|
|
487
529
|
// First, check for explicit injection tokens from @Inject decorator
|
|
488
530
|
let injectTokens =
|
|
489
|
-
getInjectTokens<Array<Token | ForwardRef<Token>>>(
|
|
490
|
-
|
|
531
|
+
getInjectTokens<Array<Token | ForwardRef<Token>>>(
|
|
532
|
+
controllerClass,
|
|
533
|
+
"inject:tokens",
|
|
534
|
+
) ?? [];
|
|
535
|
+
|
|
491
536
|
// If no explicit tokens, try to use TypeScript's design:paramtypes metadata
|
|
492
537
|
// This requires the reflect-metadata polyfill to be imported by the user
|
|
493
|
-
if (
|
|
494
|
-
|
|
538
|
+
if (
|
|
539
|
+
injectTokens.length === 0 &&
|
|
540
|
+
typeof Reflect !== "undefined" &&
|
|
541
|
+
typeof Reflect.getMetadata === "function"
|
|
542
|
+
) {
|
|
543
|
+
const paramTypes = Reflect.getMetadata(
|
|
544
|
+
"design:paramtypes",
|
|
545
|
+
controllerClass,
|
|
546
|
+
) as Array<new (...args: unknown[]) => unknown> | undefined;
|
|
495
547
|
if (paramTypes) {
|
|
496
548
|
// Use the constructor parameter types as injection tokens
|
|
497
|
-
injectTokens = paramTypes.map(
|
|
549
|
+
injectTokens = paramTypes.map(
|
|
550
|
+
(paramType) => paramType as unknown as Token,
|
|
551
|
+
);
|
|
498
552
|
}
|
|
499
553
|
}
|
|
500
|
-
|
|
554
|
+
|
|
501
555
|
const deps = injectTokens.map((tokenOrRef) => {
|
|
502
556
|
// Resolve forward reference if needed
|
|
503
|
-
const token = isForwardRef(tokenOrRef)
|
|
557
|
+
const token = isForwardRef(tokenOrRef)
|
|
558
|
+
? resolveForwardRef(tokenOrRef)
|
|
559
|
+
: tokenOrRef;
|
|
504
560
|
return this.container.resolve(token);
|
|
505
561
|
});
|
|
506
562
|
const instance = new controllerClass(...deps);
|
|
@@ -530,19 +586,23 @@ export class Application {
|
|
|
530
586
|
| "options";
|
|
531
587
|
|
|
532
588
|
// Get method-level guards
|
|
533
|
-
const methodGuards =
|
|
589
|
+
const methodGuards =
|
|
590
|
+
getMethodGuards(controllerClass.prototype, route.handler) ?? [];
|
|
534
591
|
|
|
535
592
|
// Get method-level pipes
|
|
536
|
-
const parameterPipes =
|
|
593
|
+
const parameterPipes =
|
|
594
|
+
getMethodPipes(controllerClass.prototype, route.handler) ?? [];
|
|
537
595
|
|
|
538
596
|
// Get class-level filters
|
|
539
597
|
const classFilters = getClassFilters(controllerClass) ?? [];
|
|
540
598
|
|
|
541
599
|
// Get method-level filters
|
|
542
|
-
const methodFilters =
|
|
600
|
+
const methodFilters =
|
|
601
|
+
getMethodFilters(controllerClass.prototype, route.handler) ?? [];
|
|
543
602
|
|
|
544
603
|
// Get method-level interceptors
|
|
545
|
-
const methodInterceptors =
|
|
604
|
+
const methodInterceptors =
|
|
605
|
+
getMethodInterceptors(controllerClass.prototype, route.handler) ?? [];
|
|
546
606
|
|
|
547
607
|
// Store guard metadata for this route
|
|
548
608
|
const routeKey = `${method.toUpperCase()}:${fullPath}`;
|
|
@@ -663,8 +723,13 @@ export class Application {
|
|
|
663
723
|
if (metadata.providers) {
|
|
664
724
|
for (const provider of metadata.providers) {
|
|
665
725
|
this.container.register(provider);
|
|
666
|
-
const instance = this.container.resolve(
|
|
667
|
-
|
|
726
|
+
const instance = this.container.resolve(
|
|
727
|
+
provider.token as import("../container").Token,
|
|
728
|
+
);
|
|
729
|
+
this.providerInstances.set(
|
|
730
|
+
provider.token as import("../container").Token,
|
|
731
|
+
instance,
|
|
732
|
+
);
|
|
668
733
|
this.lifecycleManager.registerInstance(instance);
|
|
669
734
|
}
|
|
670
735
|
|
|
@@ -706,7 +771,7 @@ export class Application {
|
|
|
706
771
|
|
|
707
772
|
/**
|
|
708
773
|
* Perform graceful shutdown
|
|
709
|
-
*
|
|
774
|
+
*
|
|
710
775
|
* Execution order:
|
|
711
776
|
* 1. Stop accepting new requests
|
|
712
777
|
* 2. beforeApplicationShutdown(signal)
|
|
@@ -761,7 +826,21 @@ export class Application {
|
|
|
761
826
|
this.server = Bun.serve({
|
|
762
827
|
port,
|
|
763
828
|
hostname,
|
|
764
|
-
|
|
829
|
+
...(this.websocketHandler ? { websocket: this.websocketHandler } : {}),
|
|
830
|
+
fetch: async (request: Request, server: Bun.Server) => {
|
|
831
|
+
// Handle WebSocket upgrade requests for subscriptions
|
|
832
|
+
if (
|
|
833
|
+
this.websocketHandler &&
|
|
834
|
+
request.headers.get("upgrade")?.toLowerCase() === "websocket"
|
|
835
|
+
) {
|
|
836
|
+
// The GraphQL subscription handler may have registered an upgrade callback
|
|
837
|
+
const wsUpgradeHandler = (this.websocketHandler as unknown as { __upgradeHandler?: (req: Request, srv: Bun.Server) => Response | undefined }).__upgradeHandler;
|
|
838
|
+
if (wsUpgradeHandler) {
|
|
839
|
+
const response = wsUpgradeHandler(request, server);
|
|
840
|
+
if (response) return response;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
765
844
|
// Reject new requests during shutdown
|
|
766
845
|
if (this.isShuttingDown) {
|
|
767
846
|
return new Response("Service Unavailable", { status: 503 });
|
|
@@ -787,7 +866,7 @@ export class Application {
|
|
|
787
866
|
// Execute guards (before interceptors and pipes)
|
|
788
867
|
const routeKey = `${request.method}:${url.pathname}`;
|
|
789
868
|
const guardMetadata = this.routeGuardMetadata.get(routeKey);
|
|
790
|
-
|
|
869
|
+
|
|
791
870
|
if (guardMetadata || this.globalGuards.length > 0) {
|
|
792
871
|
const guardsPassed = await executeGuards(context, {
|
|
793
872
|
globalGuards: this.globalGuards,
|
|
@@ -795,7 +874,11 @@ export class Application {
|
|
|
795
874
|
methodGuards: guardMetadata?.methodGuards ?? [],
|
|
796
875
|
resolveGuard: (guard: Guard) => {
|
|
797
876
|
// Try to resolve from container if it's a token
|
|
798
|
-
if (
|
|
877
|
+
if (
|
|
878
|
+
typeof guard === "object" &&
|
|
879
|
+
guard !== null &&
|
|
880
|
+
!("canActivate" in guard)
|
|
881
|
+
) {
|
|
799
882
|
try {
|
|
800
883
|
return this.container.resolve(guard as Token) as CanActivate;
|
|
801
884
|
} catch {
|
|
@@ -810,120 +893,155 @@ export class Application {
|
|
|
810
893
|
return createForbiddenResponse();
|
|
811
894
|
}
|
|
812
895
|
}
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
896
|
+
|
|
897
|
+
// Get interceptor metadata
|
|
898
|
+
const interceptorMetadata = this.routeInterceptorMetadata.get(routeKey);
|
|
899
|
+
|
|
900
|
+
// Create the handler function that executes pipes and middleware
|
|
901
|
+
const executeHandler = async (): Promise<Response> => {
|
|
902
|
+
// Execute pipes (after guards, before handler)
|
|
903
|
+
const pipeMetadata = this.routePipeMetadata.get(routeKey);
|
|
904
|
+
if (pipeMetadata || this.globalPipes.length > 0) {
|
|
905
|
+
try {
|
|
906
|
+
// Process each parameter with pipes
|
|
907
|
+
const params = pipeMetadata?.parameterPipes ?? [];
|
|
908
|
+
for (const paramMeta of params) {
|
|
909
|
+
// Extract the initial value for this parameter
|
|
910
|
+
const initialValue = await extractParameterValue(
|
|
911
|
+
context,
|
|
912
|
+
paramMeta,
|
|
913
|
+
);
|
|
914
|
+
|
|
915
|
+
// Create pipe context
|
|
916
|
+
const pipeContext: PipeContext = {
|
|
917
|
+
context,
|
|
918
|
+
metadata: {
|
|
919
|
+
index: paramMeta.index,
|
|
920
|
+
name: paramMeta.key,
|
|
921
|
+
decorator: paramMeta.decorator,
|
|
922
|
+
},
|
|
923
|
+
};
|
|
924
|
+
|
|
925
|
+
// Execute pipes for this parameter
|
|
926
|
+
const transformedValue = await executePipes(
|
|
927
|
+
initialValue,
|
|
928
|
+
pipeContext,
|
|
929
|
+
{
|
|
841
930
|
globalPipes: this.globalPipes,
|
|
842
931
|
parameterPipes: paramMeta.pipes,
|
|
843
932
|
resolvePipe: (pipe: Pipe) => {
|
|
844
933
|
// Try to resolve from container if it's a token
|
|
845
|
-
if (
|
|
934
|
+
if (
|
|
935
|
+
typeof pipe === "object" &&
|
|
936
|
+
pipe !== null &&
|
|
937
|
+
!("transform" in pipe)
|
|
938
|
+
) {
|
|
846
939
|
try {
|
|
847
|
-
return this.container.resolve(
|
|
940
|
+
return this.container.resolve(
|
|
941
|
+
pipe as Token,
|
|
942
|
+
) as PipeTransform;
|
|
848
943
|
} catch {
|
|
849
944
|
return null;
|
|
850
945
|
}
|
|
851
946
|
}
|
|
852
947
|
return null;
|
|
853
948
|
},
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
return createBadRequestResponse(new Error("Pipe transformation failed"));
|
|
949
|
+
},
|
|
950
|
+
);
|
|
951
|
+
|
|
952
|
+
// Store the transformed value in context for handler access
|
|
953
|
+
context.set(`pipe:param:${paramMeta.index}`, transformedValue);
|
|
954
|
+
}
|
|
955
|
+
} catch (error) {
|
|
956
|
+
// Pipe transformation failed - return 400 Bad Request
|
|
957
|
+
if (error instanceof Error) {
|
|
958
|
+
return createBadRequestResponse(error);
|
|
865
959
|
}
|
|
960
|
+
return createBadRequestResponse(
|
|
961
|
+
new Error("Pipe transformation failed"),
|
|
962
|
+
);
|
|
866
963
|
}
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// Execute middleware and handler
|
|
967
|
+
const pipeline = compose(
|
|
968
|
+
(match.middleware ?? []) as import("../middleware").Middleware[],
|
|
969
|
+
);
|
|
970
|
+
return pipeline(
|
|
971
|
+
context,
|
|
972
|
+
match.handler as import("../middleware").Handler,
|
|
973
|
+
);
|
|
974
|
+
};
|
|
975
|
+
|
|
976
|
+
// Execute interceptors wrapping around the handler
|
|
977
|
+
// Interceptors run after guards, before pipes
|
|
978
|
+
try {
|
|
979
|
+
const response = await executeInterceptors(context, executeHandler, {
|
|
980
|
+
globalInterceptors: this.globalInterceptors,
|
|
981
|
+
classInterceptors: interceptorMetadata?.classInterceptors ?? [],
|
|
982
|
+
methodInterceptors: interceptorMetadata?.methodInterceptors ?? [],
|
|
983
|
+
resolveInterceptor: (
|
|
984
|
+
interceptor: Interceptor,
|
|
985
|
+
): NestInterceptor | InterceptorFn | null => {
|
|
986
|
+
// Try to resolve from container if it's a token
|
|
987
|
+
if (
|
|
988
|
+
typeof interceptor === "object" &&
|
|
989
|
+
interceptor !== null &&
|
|
990
|
+
!isNestInterceptor(interceptor) &&
|
|
991
|
+
!isInterceptorFn(interceptor)
|
|
992
|
+
) {
|
|
993
|
+
try {
|
|
994
|
+
return this.container.resolve(
|
|
995
|
+
interceptor as Token,
|
|
996
|
+
) as NestInterceptor;
|
|
997
|
+
} catch {
|
|
998
|
+
return null;
|
|
888
999
|
}
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1000
|
+
}
|
|
1001
|
+
// Try to instantiate if it's a class constructor
|
|
1002
|
+
if (
|
|
1003
|
+
typeof interceptor === "function" &&
|
|
1004
|
+
!isInterceptorFn(interceptor)
|
|
1005
|
+
) {
|
|
1006
|
+
try {
|
|
1007
|
+
const Constructor = interceptor as new () => NestInterceptor;
|
|
1008
|
+
const instance = new Constructor();
|
|
1009
|
+
if (isNestInterceptor(instance)) {
|
|
1010
|
+
return instance;
|
|
899
1011
|
}
|
|
1012
|
+
} catch {
|
|
1013
|
+
// Cannot instantiate
|
|
900
1014
|
}
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
return response as Response;
|
|
1015
|
+
}
|
|
1016
|
+
return null;
|
|
1017
|
+
},
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
// Execute onAfterRequest hooks
|
|
1021
|
+
try {
|
|
1022
|
+
await this.lifecycleManager.executeOnAfterRequest(
|
|
1023
|
+
context,
|
|
1024
|
+
response as Response,
|
|
1025
|
+
);
|
|
913
1026
|
} catch (error) {
|
|
914
|
-
|
|
915
|
-
try {
|
|
916
|
-
await this.lifecycleManager.executeOnRequestError(
|
|
917
|
-
context,
|
|
918
|
-
error as Error,
|
|
919
|
-
);
|
|
920
|
-
} catch (hookError) {
|
|
921
|
-
console.error("Error in onRequestError hook:", hookError);
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
// Handle exception with filters
|
|
925
|
-
return this.handleException(error as Error, context, routeKey);
|
|
1027
|
+
console.error("Error in onAfterRequest hook:", error);
|
|
926
1028
|
}
|
|
1029
|
+
|
|
1030
|
+
return response as Response;
|
|
1031
|
+
} catch (error) {
|
|
1032
|
+
// Execute onRequestError hooks
|
|
1033
|
+
try {
|
|
1034
|
+
await this.lifecycleManager.executeOnRequestError(
|
|
1035
|
+
context,
|
|
1036
|
+
error as Error,
|
|
1037
|
+
);
|
|
1038
|
+
} catch (hookError) {
|
|
1039
|
+
console.error("Error in onRequestError hook:", hookError);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// Handle exception with filters
|
|
1043
|
+
return this.handleException(error as Error, context, routeKey);
|
|
1044
|
+
}
|
|
927
1045
|
},
|
|
928
1046
|
});
|
|
929
1047
|
|
|
@@ -949,7 +1067,7 @@ export class Application {
|
|
|
949
1067
|
// Execute guards (before interceptors and pipes)
|
|
950
1068
|
const routeKey = `${request.method}:${url.pathname}`;
|
|
951
1069
|
const guardMetadata = this.routeGuardMetadata.get(routeKey);
|
|
952
|
-
|
|
1070
|
+
|
|
953
1071
|
if (guardMetadata || this.globalGuards.length > 0) {
|
|
954
1072
|
const guardsPassed = await executeGuards(context, {
|
|
955
1073
|
globalGuards: this.globalGuards,
|
|
@@ -957,7 +1075,11 @@ export class Application {
|
|
|
957
1075
|
methodGuards: guardMetadata?.methodGuards ?? [],
|
|
958
1076
|
resolveGuard: (guard: Guard) => {
|
|
959
1077
|
// Try to resolve from container if it's a token
|
|
960
|
-
if (
|
|
1078
|
+
if (
|
|
1079
|
+
typeof guard === "object" &&
|
|
1080
|
+
guard !== null &&
|
|
1081
|
+
!("canActivate" in guard)
|
|
1082
|
+
) {
|
|
961
1083
|
try {
|
|
962
1084
|
return this.container.resolve(guard as Token) as CanActivate;
|
|
963
1085
|
} catch {
|
|
@@ -972,123 +1094,155 @@ export class Application {
|
|
|
972
1094
|
return createForbiddenResponse();
|
|
973
1095
|
}
|
|
974
1096
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1097
|
+
|
|
1098
|
+
// Get interceptor metadata
|
|
1099
|
+
const interceptorMetadata = this.routeInterceptorMetadata.get(routeKey);
|
|
1100
|
+
|
|
1101
|
+
// Create the handler function that executes pipes and middleware
|
|
1102
|
+
const executeHandler = async (): Promise<Response> => {
|
|
1103
|
+
// Execute pipes (after guards, before handler)
|
|
1104
|
+
const pipeMetadata = this.routePipeMetadata.get(routeKey);
|
|
1105
|
+
if (pipeMetadata || this.globalPipes.length > 0) {
|
|
1106
|
+
try {
|
|
1107
|
+
// Process each parameter with pipes
|
|
1108
|
+
const params = pipeMetadata?.parameterPipes ?? [];
|
|
1109
|
+
for (const paramMeta of params) {
|
|
1110
|
+
// Extract the initial value for this parameter
|
|
1111
|
+
const initialValue = await extractParameterValue(
|
|
1112
|
+
context,
|
|
1113
|
+
paramMeta,
|
|
1114
|
+
);
|
|
1115
|
+
|
|
1116
|
+
// Create pipe context
|
|
1117
|
+
const pipeContext: PipeContext = {
|
|
1118
|
+
context,
|
|
1119
|
+
metadata: {
|
|
1120
|
+
index: paramMeta.index,
|
|
1121
|
+
name: paramMeta.key,
|
|
1122
|
+
decorator: paramMeta.decorator,
|
|
1123
|
+
},
|
|
1124
|
+
};
|
|
1125
|
+
|
|
1126
|
+
// Execute pipes for this parameter
|
|
1127
|
+
const transformedValue = await executePipes(
|
|
1128
|
+
initialValue,
|
|
1129
|
+
pipeContext,
|
|
1130
|
+
{
|
|
1003
1131
|
globalPipes: this.globalPipes,
|
|
1004
1132
|
parameterPipes: paramMeta.pipes,
|
|
1005
1133
|
resolvePipe: (pipe: Pipe) => {
|
|
1006
1134
|
// Try to resolve from container if it's a token
|
|
1007
|
-
if (
|
|
1135
|
+
if (
|
|
1136
|
+
typeof pipe === "object" &&
|
|
1137
|
+
pipe !== null &&
|
|
1138
|
+
!("transform" in pipe)
|
|
1139
|
+
) {
|
|
1008
1140
|
try {
|
|
1009
|
-
return this.container.resolve(
|
|
1141
|
+
return this.container.resolve(
|
|
1142
|
+
pipe as Token,
|
|
1143
|
+
) as PipeTransform;
|
|
1010
1144
|
} catch {
|
|
1011
1145
|
return null;
|
|
1012
1146
|
}
|
|
1013
1147
|
}
|
|
1014
1148
|
return null;
|
|
1015
1149
|
},
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
}
|
|
1021
|
-
} catch (error) {
|
|
1022
|
-
// Pipe transformation failed - return 400 Bad Request
|
|
1023
|
-
if (error instanceof Error) {
|
|
1024
|
-
return createBadRequestResponse(error);
|
|
1025
|
-
}
|
|
1026
|
-
return createBadRequestResponse(new Error("Pipe transformation failed"));
|
|
1150
|
+
},
|
|
1151
|
+
);
|
|
1152
|
+
|
|
1153
|
+
// Store the transformed value in context for handler access
|
|
1154
|
+
context.set(`pipe:param:${paramMeta.index}`, transformedValue);
|
|
1027
1155
|
}
|
|
1156
|
+
} catch (error) {
|
|
1157
|
+
// Pipe transformation failed - return 400 Bad Request
|
|
1158
|
+
if (error instanceof Error) {
|
|
1159
|
+
return createBadRequestResponse(error);
|
|
1160
|
+
}
|
|
1161
|
+
return createBadRequestResponse(
|
|
1162
|
+
new Error("Pipe transformation failed"),
|
|
1163
|
+
);
|
|
1028
1164
|
}
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// Execute middleware and handler
|
|
1168
|
+
const pipeline = compose(
|
|
1169
|
+
(match.middleware ?? []) as import("../middleware").Middleware[],
|
|
1170
|
+
);
|
|
1171
|
+
return pipeline(
|
|
1172
|
+
context,
|
|
1173
|
+
match.handler as import("../middleware").Handler,
|
|
1174
|
+
);
|
|
1175
|
+
};
|
|
1176
|
+
|
|
1177
|
+
// Execute interceptors wrapping around the handler
|
|
1178
|
+
// Interceptors run after guards, before pipes
|
|
1179
|
+
try {
|
|
1180
|
+
const response = await executeInterceptors(context, executeHandler, {
|
|
1181
|
+
globalInterceptors: this.globalInterceptors,
|
|
1182
|
+
classInterceptors: interceptorMetadata?.classInterceptors ?? [],
|
|
1183
|
+
methodInterceptors: interceptorMetadata?.methodInterceptors ?? [],
|
|
1184
|
+
resolveInterceptor: (
|
|
1185
|
+
interceptor: Interceptor,
|
|
1186
|
+
): NestInterceptor | InterceptorFn | null => {
|
|
1187
|
+
// Try to resolve from container if it's a token
|
|
1188
|
+
if (
|
|
1189
|
+
typeof interceptor === "object" &&
|
|
1190
|
+
interceptor !== null &&
|
|
1191
|
+
!isNestInterceptor(interceptor) &&
|
|
1192
|
+
!isInterceptorFn(interceptor)
|
|
1193
|
+
) {
|
|
1194
|
+
try {
|
|
1195
|
+
return this.container.resolve(
|
|
1196
|
+
interceptor as Token,
|
|
1197
|
+
) as NestInterceptor;
|
|
1198
|
+
} catch {
|
|
1199
|
+
return null;
|
|
1050
1200
|
}
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1201
|
+
}
|
|
1202
|
+
// Try to instantiate if it's a class constructor
|
|
1203
|
+
if (
|
|
1204
|
+
typeof interceptor === "function" &&
|
|
1205
|
+
!isInterceptorFn(interceptor)
|
|
1206
|
+
) {
|
|
1207
|
+
try {
|
|
1208
|
+
const Constructor = interceptor as new () => NestInterceptor;
|
|
1209
|
+
const instance = new Constructor();
|
|
1210
|
+
if (isNestInterceptor(instance)) {
|
|
1211
|
+
return instance;
|
|
1061
1212
|
}
|
|
1213
|
+
} catch {
|
|
1214
|
+
// Cannot instantiate
|
|
1062
1215
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1216
|
+
}
|
|
1217
|
+
return null;
|
|
1218
|
+
},
|
|
1219
|
+
});
|
|
1220
|
+
|
|
1221
|
+
return response as Response;
|
|
1222
|
+
} catch (error) {
|
|
1223
|
+
// Handle exception with filters
|
|
1224
|
+
return this.handleException(error as Error, context, routeKey);
|
|
1072
1225
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
/**
|
|
1229
|
+
* Get the lifecycle manager for this application
|
|
1230
|
+
*/
|
|
1077
1231
|
getLifecycleManager(): LifecycleHookManager {
|
|
1078
1232
|
return this.lifecycleManager;
|
|
1079
1233
|
}
|
|
1080
1234
|
|
|
1081
1235
|
/**
|
|
1082
|
-
|
|
1083
|
-
|
|
1236
|
+
* Check if the application is shutting down
|
|
1237
|
+
*/
|
|
1084
1238
|
isShuttingDownNow(): boolean {
|
|
1085
1239
|
return this.isShuttingDown;
|
|
1086
1240
|
}
|
|
1087
1241
|
|
|
1088
1242
|
/**
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1243
|
+
* Handle an exception using the filters system
|
|
1244
|
+
* Filters are checked in order: method → class → global
|
|
1245
|
+
*/
|
|
1092
1246
|
private async handleException(
|
|
1093
1247
|
exception: Error,
|
|
1094
1248
|
context: Context,
|
|
@@ -1102,7 +1256,11 @@ export class Application {
|
|
|
1102
1256
|
methodFilters: filterMetadata?.methodFilters ?? [],
|
|
1103
1257
|
resolveFilter: (filter: Filter): ExceptionFilter | null => {
|
|
1104
1258
|
// Try to resolve from container if it's a token
|
|
1105
|
-
if (
|
|
1259
|
+
if (
|
|
1260
|
+
typeof filter === "object" &&
|
|
1261
|
+
filter !== null &&
|
|
1262
|
+
!isExceptionFilter(filter)
|
|
1263
|
+
) {
|
|
1106
1264
|
try {
|
|
1107
1265
|
return this.container.resolve(filter as Token) as ExceptionFilter;
|
|
1108
1266
|
} catch {
|
|
@@ -1138,17 +1296,17 @@ export function createApp(moduleClass: Constructor): Application {
|
|
|
1138
1296
|
export {
|
|
1139
1297
|
LifecycleHookManager,
|
|
1140
1298
|
ShutdownSignalHandler,
|
|
1141
|
-
OnModuleInit,
|
|
1142
|
-
OnApplicationBootstrap,
|
|
1143
|
-
OnModuleDestroy,
|
|
1144
|
-
BeforeApplicationShutdown,
|
|
1145
|
-
OnApplicationShutdown,
|
|
1146
|
-
OnBeforeRequest,
|
|
1147
|
-
OnAfterRequest,
|
|
1148
|
-
OnRequestError,
|
|
1149
|
-
ApplicationLifecycle,
|
|
1150
|
-
RequestLifecycle,
|
|
1151
|
-
FullLifecycle,
|
|
1299
|
+
type OnModuleInit,
|
|
1300
|
+
type OnApplicationBootstrap,
|
|
1301
|
+
type OnModuleDestroy,
|
|
1302
|
+
type BeforeApplicationShutdown,
|
|
1303
|
+
type OnApplicationShutdown,
|
|
1304
|
+
type OnBeforeRequest,
|
|
1305
|
+
type OnAfterRequest,
|
|
1306
|
+
type OnRequestError,
|
|
1307
|
+
type ApplicationLifecycle,
|
|
1308
|
+
type RequestLifecycle,
|
|
1309
|
+
type FullLifecycle,
|
|
1152
1310
|
isOnModuleInit,
|
|
1153
1311
|
isOnApplicationBootstrap,
|
|
1154
1312
|
isOnModuleDestroy,
|
|
@@ -1274,4 +1432,4 @@ export {
|
|
|
1274
1432
|
forwardRef,
|
|
1275
1433
|
isForwardRef,
|
|
1276
1434
|
resolveForwardRef,
|
|
1277
|
-
} from "../container";
|
|
1435
|
+
} from "../container";
|