@mxweb/core 1.0.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +294 -0
- package/dist/application.d.ts +90 -37
- package/dist/application.js +1 -1
- package/dist/application.mjs +1 -1
- package/dist/common.d.ts +255 -3
- package/dist/config.d.ts +18 -0
- package/dist/config.js +1 -1
- package/dist/config.mjs +1 -1
- package/dist/decorator.d.ts +12 -5
- package/dist/execute.d.ts +293 -135
- package/dist/execute.js +1 -1
- package/dist/execute.mjs +1 -1
- package/dist/feature.d.ts +48 -10
- package/dist/feature.js +1 -1
- package/dist/feature.mjs +1 -1
- package/dist/hooks.d.ts +5 -6
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/logger.js +1 -1
- package/dist/logger.mjs +1 -1
- package/dist/response.d.ts +297 -227
- package/dist/response.js +1 -1
- package/dist/response.mjs +1 -1
- package/dist/route.d.ts +106 -2
- package/dist/route.js +1 -1
- package/dist/route.mjs +1 -1
- package/dist/service.d.ts +9 -8
- package/package.json +1 -6
package/dist/execute.d.ts
CHANGED
|
@@ -8,10 +8,225 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @module execute
|
|
10
10
|
*/
|
|
11
|
-
import { NextRequest } from "next/server";
|
|
12
11
|
import { ServerResponse } from "./response";
|
|
13
|
-
import {
|
|
14
|
-
|
|
12
|
+
import { ApplicationInject, FeatureContext, Filter, Guard, InjectEntry, InjectInstanceFactory, InjectRegistry, Interceptor, Pipe, RouteMethod, RouteNextHandler } from "./common";
|
|
13
|
+
/**
|
|
14
|
+
* Core request interface defining the minimal contract for HTTP requests.
|
|
15
|
+
* This abstraction allows @mxweb/core to work with any request implementation
|
|
16
|
+
* (Next.js, Express, Fastify, etc.) as long as it conforms to this interface.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* This interface is designed to be compatible with multiple frameworks:
|
|
20
|
+
* - **Next.js**: Uses `nextUrl` for query params
|
|
21
|
+
* - **Express/Fastify**: Uses `url` (string) and `query` (parsed object)
|
|
22
|
+
* - **Fetch API**: Uses `url` (URL object)
|
|
23
|
+
*
|
|
24
|
+
* The framework adapter should populate the appropriate properties.
|
|
25
|
+
* `switchHttp().query()` will use `nextUrl.searchParams` → `url.searchParams` → `query` fallback.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* // Next.js Request is compatible with CoreRequest (uses nextUrl)
|
|
30
|
+
* const coreReq: CoreRequest = nextRequest;
|
|
31
|
+
*
|
|
32
|
+
* // Express Request adapter
|
|
33
|
+
* const expressReq: CoreRequest = {
|
|
34
|
+
* url: "/api/users?page=1",
|
|
35
|
+
* query: { page: "1" },
|
|
36
|
+
* headers: new Headers(req.headers),
|
|
37
|
+
* body: null,
|
|
38
|
+
* json: async () => req.body,
|
|
39
|
+
* text: async () => JSON.stringify(req.body),
|
|
40
|
+
* formData: async () => new FormData(),
|
|
41
|
+
* blob: async () => new Blob(),
|
|
42
|
+
* arrayBuffer: async () => new ArrayBuffer(0),
|
|
43
|
+
* };
|
|
44
|
+
*
|
|
45
|
+
* // Fetch Request adapter
|
|
46
|
+
* const fetchReq: CoreRequest = {
|
|
47
|
+
* url: new URL("https://example.com/api/users?page=1"),
|
|
48
|
+
* headers: request.headers,
|
|
49
|
+
* body: request.body,
|
|
50
|
+
* json: () => request.json(),
|
|
51
|
+
* text: () => request.text(),
|
|
52
|
+
* formData: () => request.formData(),
|
|
53
|
+
* blob: () => request.blob(),
|
|
54
|
+
* arrayBuffer: () => request.arrayBuffer(),
|
|
55
|
+
* };
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export interface CoreRequest {
|
|
59
|
+
/**
|
|
60
|
+
* The NextURL of the request (Next.js specific).
|
|
61
|
+
* Used for extracting query parameters in Next.js applications.
|
|
62
|
+
* Takes priority over `url` when available.
|
|
63
|
+
*/
|
|
64
|
+
readonly nextUrl?: URL | {
|
|
65
|
+
readonly searchParams: URLSearchParams;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* The URL of the request.
|
|
69
|
+
* Can be:
|
|
70
|
+
* - URL object (Fetch API, custom implementations)
|
|
71
|
+
* - string (Express, Fastify - just the path with query string)
|
|
72
|
+
*/
|
|
73
|
+
readonly url?: URL | string;
|
|
74
|
+
/**
|
|
75
|
+
* Pre-parsed query parameters object (Express/Fastify style).
|
|
76
|
+
* Used as fallback when `nextUrl` and `url` are not available or don't have searchParams.
|
|
77
|
+
*/
|
|
78
|
+
readonly query?: Record<string, string | string[] | undefined>;
|
|
79
|
+
/**
|
|
80
|
+
* The HTTP headers of the request.
|
|
81
|
+
* Must be iterable (for use with Object.fromEntries).
|
|
82
|
+
*/
|
|
83
|
+
readonly headers: Headers;
|
|
84
|
+
/**
|
|
85
|
+
* The body of the request as a ReadableStream, or null if no body.
|
|
86
|
+
*/
|
|
87
|
+
readonly body: ReadableStream<Uint8Array> | null;
|
|
88
|
+
/**
|
|
89
|
+
* Parses the request body as JSON.
|
|
90
|
+
* @returns Promise resolving to the parsed JSON value
|
|
91
|
+
*/
|
|
92
|
+
json(): Promise<unknown>;
|
|
93
|
+
/**
|
|
94
|
+
* Returns the request body as text.
|
|
95
|
+
* @returns Promise resolving to the body text
|
|
96
|
+
*/
|
|
97
|
+
text(): Promise<string>;
|
|
98
|
+
/**
|
|
99
|
+
* Parses the request body as FormData.
|
|
100
|
+
* @returns Promise resolving to the FormData
|
|
101
|
+
*/
|
|
102
|
+
formData(): Promise<FormData>;
|
|
103
|
+
/**
|
|
104
|
+
* Returns the request body as a Blob.
|
|
105
|
+
* @returns Promise resolving to the Blob
|
|
106
|
+
*/
|
|
107
|
+
blob(): Promise<Blob>;
|
|
108
|
+
/**
|
|
109
|
+
* Returns the request body as an ArrayBuffer.
|
|
110
|
+
* @returns Promise resolving to the ArrayBuffer
|
|
111
|
+
*/
|
|
112
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Unified search params class that works with multiple input sources.
|
|
116
|
+
* Provides a consistent API for accessing query parameters regardless of the framework.
|
|
117
|
+
*
|
|
118
|
+
* @remarks
|
|
119
|
+
* This class abstracts the differences between:
|
|
120
|
+
* - URLSearchParams (Fetch API, Next.js)
|
|
121
|
+
* - Query object (Express, Fastify)
|
|
122
|
+
* - Query string (raw URL parsing)
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* // From URLSearchParams
|
|
127
|
+
* const params = new CoreSearchParams(new URLSearchParams("page=1&limit=10"));
|
|
128
|
+
*
|
|
129
|
+
* // From object (Express style)
|
|
130
|
+
* const params = new CoreSearchParams({ page: "1", limit: "10" });
|
|
131
|
+
*
|
|
132
|
+
* // From query string
|
|
133
|
+
* const params = new CoreSearchParams("page=1&limit=10");
|
|
134
|
+
*
|
|
135
|
+
* // Usage
|
|
136
|
+
* params.get("page"); // "1"
|
|
137
|
+
* params.getNumber("page"); // 1
|
|
138
|
+
* params.getAll("tags"); // ["a", "b"]
|
|
139
|
+
* params.toObject(); // { page: "1", limit: "10" }
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
export declare class CoreSearchParams {
|
|
143
|
+
private readonly params;
|
|
144
|
+
/**
|
|
145
|
+
* Creates a new CoreSearchParams instance.
|
|
146
|
+
*
|
|
147
|
+
* @param source - The source of query parameters:
|
|
148
|
+
* - URLSearchParams: Used directly
|
|
149
|
+
* - Record<string, string | string[] | undefined>: Object with query values
|
|
150
|
+
* - string: Query string (with or without leading "?")
|
|
151
|
+
* - undefined/null: Empty params
|
|
152
|
+
*/
|
|
153
|
+
constructor(source?: URLSearchParams | Record<string, string | string[] | undefined> | string | null);
|
|
154
|
+
/**
|
|
155
|
+
* Gets the first value for a parameter.
|
|
156
|
+
* @param name - Parameter name
|
|
157
|
+
* @returns The value or null if not found
|
|
158
|
+
*/
|
|
159
|
+
get(name: string): string | null;
|
|
160
|
+
/**
|
|
161
|
+
* Gets all values for a parameter.
|
|
162
|
+
* @param name - Parameter name
|
|
163
|
+
* @returns Array of values (empty if not found)
|
|
164
|
+
*/
|
|
165
|
+
getAll(name: string): string[];
|
|
166
|
+
/**
|
|
167
|
+
* Checks if a parameter exists.
|
|
168
|
+
* @param name - Parameter name
|
|
169
|
+
* @returns true if the parameter exists
|
|
170
|
+
*/
|
|
171
|
+
has(name: string): boolean;
|
|
172
|
+
/**
|
|
173
|
+
* Gets a parameter as a number.
|
|
174
|
+
* @param name - Parameter name
|
|
175
|
+
* @param defaultValue - Default value if not found or not a valid number
|
|
176
|
+
* @returns The parsed number or default value
|
|
177
|
+
*/
|
|
178
|
+
getNumber(name: string, defaultValue?: number): number | undefined;
|
|
179
|
+
/**
|
|
180
|
+
* Gets a parameter as a boolean.
|
|
181
|
+
* Treats "true", "1", "yes" as true; "false", "0", "no" as false.
|
|
182
|
+
* @param name - Parameter name
|
|
183
|
+
* @param defaultValue - Default value if not found
|
|
184
|
+
* @returns The parsed boolean or default value
|
|
185
|
+
*/
|
|
186
|
+
getBoolean(name: string, defaultValue?: boolean): boolean | undefined;
|
|
187
|
+
/**
|
|
188
|
+
* Returns an iterator of all parameter names.
|
|
189
|
+
*/
|
|
190
|
+
keys(): IterableIterator<string>;
|
|
191
|
+
/**
|
|
192
|
+
* Returns an iterator of all parameter values.
|
|
193
|
+
*/
|
|
194
|
+
values(): IterableIterator<string>;
|
|
195
|
+
/**
|
|
196
|
+
* Returns an iterator of all [name, value] pairs.
|
|
197
|
+
*/
|
|
198
|
+
entries(): IterableIterator<[string, string]>;
|
|
199
|
+
/**
|
|
200
|
+
* Executes a callback for each parameter.
|
|
201
|
+
*/
|
|
202
|
+
forEach(callback: (value: string, key: string, parent: URLSearchParams) => void): void;
|
|
203
|
+
/**
|
|
204
|
+
* Converts to a plain object (first value only for each key).
|
|
205
|
+
* @returns Object with string values
|
|
206
|
+
*/
|
|
207
|
+
toObject(): Record<string, string>;
|
|
208
|
+
/**
|
|
209
|
+
* Converts to a plain object (all values as arrays).
|
|
210
|
+
* @returns Object with string array values
|
|
211
|
+
*/
|
|
212
|
+
toObjectAll(): Record<string, string[]>;
|
|
213
|
+
/**
|
|
214
|
+
* Returns the underlying URLSearchParams.
|
|
215
|
+
*/
|
|
216
|
+
toURLSearchParams(): URLSearchParams;
|
|
217
|
+
/**
|
|
218
|
+
* Converts to query string (without leading "?").
|
|
219
|
+
*/
|
|
220
|
+
toString(): string;
|
|
221
|
+
/**
|
|
222
|
+
* Returns the number of parameters (counting duplicates).
|
|
223
|
+
*/
|
|
224
|
+
get size(): number;
|
|
225
|
+
/**
|
|
226
|
+
* Makes the class iterable.
|
|
227
|
+
*/
|
|
228
|
+
[Symbol.iterator](): IterableIterator<[string, string]>;
|
|
229
|
+
}
|
|
15
230
|
/**
|
|
16
231
|
* Container for route-level metadata, guards, filters, interceptors, and pipes.
|
|
17
232
|
* Created from decorator results and attached to each route.
|
|
@@ -48,12 +263,12 @@ export declare class Reflect {
|
|
|
48
263
|
* Returns the set of exception filter classes attached to this route.
|
|
49
264
|
* @returns Set of filter class constructors
|
|
50
265
|
*/
|
|
51
|
-
getFilters(): Set<Filter
|
|
266
|
+
getFilters(): Set<Filter>;
|
|
52
267
|
/**
|
|
53
268
|
* Returns the set of interceptor classes attached to this route.
|
|
54
269
|
* @returns Set of interceptor class constructors
|
|
55
270
|
*/
|
|
56
|
-
getInterceptors(): Set<Interceptor
|
|
271
|
+
getInterceptors(): Set<Interceptor>;
|
|
57
272
|
/**
|
|
58
273
|
* Returns the set of pipe classes attached to this route.
|
|
59
274
|
* @returns Set of pipe class constructors
|
|
@@ -83,8 +298,8 @@ export declare class Reflect {
|
|
|
83
298
|
* Created for each request and available throughout the request lifecycle.
|
|
84
299
|
*/
|
|
85
300
|
export interface RequestContext {
|
|
86
|
-
/** The incoming
|
|
87
|
-
req:
|
|
301
|
+
/** The incoming HTTP request (CoreRequest-compatible) */
|
|
302
|
+
req: CoreRequest;
|
|
88
303
|
/** The response builder */
|
|
89
304
|
res: ServerResponse;
|
|
90
305
|
/** HTTP method of the request */
|
|
@@ -98,7 +313,7 @@ export interface RequestContext {
|
|
|
98
313
|
/** Route metadata and decorators */
|
|
99
314
|
reflect: Reflect;
|
|
100
315
|
/** The feature that matched this request */
|
|
101
|
-
feature:
|
|
316
|
+
feature: FeatureContext;
|
|
102
317
|
}
|
|
103
318
|
/**
|
|
104
319
|
* Request-scoped execution context using AsyncLocalStorage.
|
|
@@ -176,25 +391,31 @@ export declare class ExecuteContext {
|
|
|
176
391
|
/**
|
|
177
392
|
* Retrieves a global inject by name with lazy initialization.
|
|
178
393
|
* If the inject hasn't been initialized yet, calls the factory to create it.
|
|
394
|
+
* Returns the result of switch() if available, otherwise the raw instance.
|
|
179
395
|
*
|
|
180
|
-
* @template T - Expected type
|
|
396
|
+
* @template T - Expected return type (from switch() or the inject itself)
|
|
181
397
|
* @param name - The name of the inject to retrieve
|
|
182
|
-
* @returns Promise resolving to the
|
|
398
|
+
* @returns Promise resolving to the switched value or undefined if not found
|
|
183
399
|
*
|
|
184
400
|
* @example
|
|
185
401
|
* ```ts
|
|
186
|
-
*
|
|
402
|
+
* // If inject implements switch(), returns the switched value
|
|
403
|
+
* const db = await context.getInject<DatabaseClient>("db");
|
|
404
|
+
* const users = await db.query("SELECT * FROM users");
|
|
405
|
+
*
|
|
406
|
+
* // If inject doesn't implement switch(), returns the inject instance
|
|
187
407
|
* const config = await context.getInject<Config>("config");
|
|
188
408
|
* ```
|
|
189
409
|
*/
|
|
190
|
-
getInject<T
|
|
410
|
+
getInject<T = unknown>(name: string): Promise<T | undefined>;
|
|
191
411
|
/**
|
|
192
412
|
* Retrieves a global inject synchronously (only if already initialized).
|
|
193
413
|
* Returns undefined if the inject hasn't been initialized yet.
|
|
414
|
+
* Returns the result of switch() if available, otherwise the raw instance.
|
|
194
415
|
*
|
|
195
|
-
* @template T - Expected type
|
|
416
|
+
* @template T - Expected return type (from switch() or the inject itself)
|
|
196
417
|
* @param name - The name of the inject to retrieve
|
|
197
|
-
* @returns The
|
|
418
|
+
* @returns The switched value or undefined if not found/not initialized
|
|
198
419
|
*
|
|
199
420
|
* @example
|
|
200
421
|
* ```ts
|
|
@@ -202,7 +423,7 @@ export declare class ExecuteContext {
|
|
|
202
423
|
* const config = context.getInjectSync<Config>("config");
|
|
203
424
|
* ```
|
|
204
425
|
*/
|
|
205
|
-
getInjectSync<T
|
|
426
|
+
getInjectSync<T = unknown>(name: string): T | undefined;
|
|
206
427
|
/**
|
|
207
428
|
* Checks if a global inject factory exists by name.
|
|
208
429
|
*
|
|
@@ -285,8 +506,8 @@ export declare class ExecuteContext {
|
|
|
285
506
|
* ```
|
|
286
507
|
*/
|
|
287
508
|
switchHttp(): {
|
|
288
|
-
/** Returns the
|
|
289
|
-
getRequest: () =>
|
|
509
|
+
/** Returns the CoreRequest object, optionally cast to a specific type */
|
|
510
|
+
getRequest: <T extends CoreRequest = CoreRequest>() => T;
|
|
290
511
|
/** Returns the ServerResponse builder */
|
|
291
512
|
getResponse: () => ServerResponse;
|
|
292
513
|
/** Returns the HTTP method */
|
|
@@ -297,16 +518,16 @@ export declare class ExecuteContext {
|
|
|
297
518
|
params: () => Record<string, string>;
|
|
298
519
|
/** Returns wildcard segments */
|
|
299
520
|
wildcards: () => string[];
|
|
300
|
-
/** Returns query
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
521
|
+
/** Returns CoreSearchParams instance for query parameter access */
|
|
522
|
+
searchParams: () => CoreSearchParams;
|
|
523
|
+
/** Returns query parameters as plain object (first value only) */
|
|
524
|
+
query: () => Record<string, string>;
|
|
304
525
|
/** Returns headers as object */
|
|
305
526
|
headers: () => {
|
|
306
527
|
[k: string]: string;
|
|
307
528
|
};
|
|
308
529
|
/** Returns the raw body ReadableStream */
|
|
309
|
-
body: () => ReadableStream<Uint8Array<
|
|
530
|
+
body: () => ReadableStream<Uint8Array<ArrayBufferLike>> | null;
|
|
310
531
|
/** Parses and returns the JSON body */
|
|
311
532
|
json: <T = unknown>() => Promise<T>;
|
|
312
533
|
/** Returns the body as text */
|
|
@@ -339,14 +560,15 @@ export declare class ExecuteContext {
|
|
|
339
560
|
*
|
|
340
561
|
* @returns The Feature instance or undefined if no context
|
|
341
562
|
*/
|
|
342
|
-
getFeature(): Feature | undefined;
|
|
563
|
+
getFeature<Feature extends FeatureContext>(): Feature | undefined;
|
|
343
564
|
/**
|
|
344
565
|
* Retrieves a feature-local inject by name with lazy initialization.
|
|
345
566
|
* Falls back to global injects if not found in the feature.
|
|
567
|
+
* Returns the result of switch() if available, otherwise the raw instance.
|
|
346
568
|
*
|
|
347
|
-
* @template T - Expected type
|
|
569
|
+
* @template T - Expected return type (from switch() or the inject itself)
|
|
348
570
|
* @param name - The name of the inject to retrieve
|
|
349
|
-
* @returns Promise resolving to the
|
|
571
|
+
* @returns Promise resolving to the switched value or undefined if not found
|
|
350
572
|
*
|
|
351
573
|
* @example
|
|
352
574
|
* ```ts
|
|
@@ -354,14 +576,15 @@ export declare class ExecuteContext {
|
|
|
354
576
|
* const repo = await context.getLocalInject<ProductRepository>("productRepo");
|
|
355
577
|
* ```
|
|
356
578
|
*/
|
|
357
|
-
getLocalInject<T
|
|
579
|
+
getLocalInject<T = unknown>(name: string): Promise<T | undefined>;
|
|
358
580
|
/**
|
|
359
581
|
* Retrieves a feature-local inject synchronously (only if already initialized).
|
|
360
582
|
* Falls back to global injects if not found in the feature.
|
|
583
|
+
* Returns the result of switch() if available, otherwise the raw instance.
|
|
361
584
|
*
|
|
362
|
-
* @template T - Expected type
|
|
585
|
+
* @template T - Expected return type (from switch() or the inject itself)
|
|
363
586
|
* @param name - The name of the inject to retrieve
|
|
364
|
-
* @returns The
|
|
587
|
+
* @returns The switched value or undefined if not found/not initialized
|
|
365
588
|
*
|
|
366
589
|
* @example
|
|
367
590
|
* ```ts
|
|
@@ -369,35 +592,52 @@ export declare class ExecuteContext {
|
|
|
369
592
|
* const repo = context.getLocalInjectSync<ProductRepository>("productRepo");
|
|
370
593
|
* ```
|
|
371
594
|
*/
|
|
372
|
-
getLocalInjectSync<T
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
*
|
|
387
|
-
|
|
388
|
-
|
|
595
|
+
getLocalInjectSync<T = unknown>(name: string): T | undefined;
|
|
596
|
+
/**
|
|
597
|
+
* Retrieves an inject by name and calls its switch() method.
|
|
598
|
+
* Searches in feature injects first (priority), then falls back to global injects.
|
|
599
|
+
*
|
|
600
|
+
* @template T - Expected return type of the switch() method
|
|
601
|
+
* @param name - The name of the inject to switch to
|
|
602
|
+
* @returns Promise resolving to the result of the inject's switch() method
|
|
603
|
+
* @throws {Error} If the inject is not found or doesn't have a switch() method
|
|
604
|
+
*
|
|
605
|
+
* @example
|
|
606
|
+
* ```ts
|
|
607
|
+
* // In a controller or service
|
|
608
|
+
* const db = await this.context.switch<DatabaseClient>("db");
|
|
609
|
+
* const users = await db.query("SELECT * FROM users");
|
|
610
|
+
*
|
|
611
|
+
* // The inject must implement switch():
|
|
612
|
+
* class Connection implements ApplicationInject {
|
|
613
|
+
* switch<T = DatabaseClient>(): T {
|
|
614
|
+
* return this.client as T;
|
|
615
|
+
* }
|
|
616
|
+
* }
|
|
617
|
+
* ```
|
|
618
|
+
*/
|
|
619
|
+
switch<T = unknown>(name: string): Promise<T>;
|
|
389
620
|
/**
|
|
390
|
-
*
|
|
621
|
+
* Retrieves an inject by name and calls its switch() method (synchronous version).
|
|
622
|
+
* Only works if the inject has already been initialized.
|
|
623
|
+
* Searches in feature injects first (priority), then falls back to global injects.
|
|
624
|
+
*
|
|
625
|
+
* @template T - Expected return type of the switch() method
|
|
626
|
+
* @param name - The name of the inject to switch to
|
|
627
|
+
* @returns The result of the inject's switch() method, or undefined if not initialized
|
|
628
|
+
* @throws {Error} If the inject exists but doesn't have a switch() method
|
|
391
629
|
*
|
|
392
|
-
* @
|
|
393
|
-
*
|
|
630
|
+
* @example
|
|
631
|
+
* ```ts
|
|
632
|
+
* // Only use when you're sure the inject is already initialized
|
|
633
|
+
* const db = this.context.switchSync<DatabaseClient>("db");
|
|
634
|
+
* if (db) {
|
|
635
|
+
* const users = await db.query("SELECT * FROM users");
|
|
636
|
+
* }
|
|
637
|
+
* ```
|
|
394
638
|
*/
|
|
395
|
-
|
|
639
|
+
switchSync<T = unknown>(name: string): T | undefined;
|
|
396
640
|
}
|
|
397
|
-
/**
|
|
398
|
-
* Constructor type for guard classes.
|
|
399
|
-
*/
|
|
400
|
-
export type Guard = new (...args: unknown[]) => CanActivate;
|
|
401
641
|
/**
|
|
402
642
|
* Function type for route action handlers (inline handlers).
|
|
403
643
|
*
|
|
@@ -437,85 +677,3 @@ export type RouteHandler<Data = unknown> = (context: ExecuteContext) => Data;
|
|
|
437
677
|
* ```
|
|
438
678
|
*/
|
|
439
679
|
export type RouteMiddleware = (context: ExecuteContext, next: RouteNextHandler) => void | Promise<void>;
|
|
440
|
-
/**
|
|
441
|
-
* Interface for exception filter classes that handle errors.
|
|
442
|
-
* Filters can transform exceptions into custom responses.
|
|
443
|
-
*
|
|
444
|
-
* @template Response - The type of response the filter returns
|
|
445
|
-
*
|
|
446
|
-
* @example
|
|
447
|
-
* ```ts
|
|
448
|
-
* class HttpExceptionFilter implements ExceptionFilter<Response> {
|
|
449
|
-
* catch(exception: unknown, context: ExecuteContext): Response {
|
|
450
|
-
* if (exception instanceof HttpError) {
|
|
451
|
-
* return new Response(JSON.stringify({ error: exception.message }), {
|
|
452
|
-
* status: exception.statusCode,
|
|
453
|
-
* });
|
|
454
|
-
* }
|
|
455
|
-
* throw exception; // Re-throw if not handled
|
|
456
|
-
* }
|
|
457
|
-
* }
|
|
458
|
-
* ```
|
|
459
|
-
*/
|
|
460
|
-
export interface ExceptionFilter<Response = unknown> {
|
|
461
|
-
/**
|
|
462
|
-
* Handles an exception and optionally returns a response.
|
|
463
|
-
*
|
|
464
|
-
* @param exception - The thrown exception
|
|
465
|
-
* @param context - The execution context for the current request
|
|
466
|
-
* @returns A response to send, or re-throw to pass to next filter
|
|
467
|
-
*/
|
|
468
|
-
catch(exception: unknown, context: ExecuteContext): Response | Promise<Response>;
|
|
469
|
-
}
|
|
470
|
-
/**
|
|
471
|
-
* Constructor type for exception filter classes.
|
|
472
|
-
*
|
|
473
|
-
* @template Response - The type of response the filter returns
|
|
474
|
-
*/
|
|
475
|
-
export type Filter<Response = unknown> = new (...args: unknown[]) => ExceptionFilter<Response>;
|
|
476
|
-
/**
|
|
477
|
-
* Interface for interceptor classes that wrap handler execution.
|
|
478
|
-
* Interceptors can transform requests/responses, add caching, logging, etc.
|
|
479
|
-
*
|
|
480
|
-
* @template T - The type of the handler result
|
|
481
|
-
*
|
|
482
|
-
* @example
|
|
483
|
-
* ```ts
|
|
484
|
-
* class LoggingInterceptor implements InterceptorHandler {
|
|
485
|
-
* async intercept(context: ExecuteContext, next: CallHandler): Promise<unknown> {
|
|
486
|
-
* const start = Date.now();
|
|
487
|
-
* const result = await next();
|
|
488
|
-
* console.log(`Request took ${Date.now() - start}ms`);
|
|
489
|
-
* return result;
|
|
490
|
-
* }
|
|
491
|
-
* }
|
|
492
|
-
*
|
|
493
|
-
* class CacheInterceptor implements InterceptorHandler {
|
|
494
|
-
* async intercept(context: ExecuteContext, next: CallHandler): Promise<unknown> {
|
|
495
|
-
* const key = getCacheKey(context);
|
|
496
|
-
* const cached = await cache.get(key);
|
|
497
|
-
* if (cached) return cached;
|
|
498
|
-
*
|
|
499
|
-
* const result = await next();
|
|
500
|
-
* await cache.set(key, result);
|
|
501
|
-
* return result;
|
|
502
|
-
* }
|
|
503
|
-
* }
|
|
504
|
-
* ```
|
|
505
|
-
*/
|
|
506
|
-
export interface InterceptorHandler<T = unknown> {
|
|
507
|
-
/**
|
|
508
|
-
* Intercepts the handler execution.
|
|
509
|
-
*
|
|
510
|
-
* @param context - The execution context for the current request
|
|
511
|
-
* @param next - Function to call the next interceptor or handler
|
|
512
|
-
* @returns The (possibly transformed) result
|
|
513
|
-
*/
|
|
514
|
-
intercept(context: ExecuteContext, next: CallHandler<T>): Promise<T>;
|
|
515
|
-
}
|
|
516
|
-
/**
|
|
517
|
-
* Constructor type for interceptor classes.
|
|
518
|
-
*
|
|
519
|
-
* @template T - The type of the handler result
|
|
520
|
-
*/
|
|
521
|
-
export type Interceptor<T = unknown> = new (...args: unknown[]) => InterceptorHandler<T>;
|
package/dist/execute.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var t=require("async_hooks");class e{constructor(){this.storage=new t.AsyncLocalStorage,this._injections=new Map,this._registry=null}static get instance(){return
|
|
1
|
+
"use strict";var t=require("async_hooks");class e{constructor(t){if(t)if(t instanceof URLSearchParams)this.params=t;else if("string"==typeof t){const e=t.startsWith("?")?t.slice(1):t;this.params=new URLSearchParams(e)}else{this.params=new URLSearchParams;for(const[e,r]of Object.entries(t))if(void 0!==r)if(Array.isArray(r))for(const t of r)this.params.append(e,t);else this.params.set(e,r)}else this.params=new URLSearchParams}get(t){return this.params.get(t)}getAll(t){return this.params.getAll(t)}has(t){return this.params.has(t)}getNumber(t,e){const r=this.params.get(t);if(null===r)return e;const n=Number(r);return isNaN(n)?e:n}getBoolean(t,e){const r=this.params.get(t);if(null===r)return e;const n=r.toLowerCase();return!!["true","1","yes"].includes(n)||!["false","0","no"].includes(n)&&e}keys(){return this.params.keys()}values(){return this.params.values()}entries(){return this.params.entries()}forEach(t){this.params.forEach(t)}toObject(){const t={};for(const e of this.params.keys())t[e]=this.params.get(e);return t}toObjectAll(){const t={};for(const e of this.params.keys())t[e]||(t[e]=this.params.getAll(e));return t}toURLSearchParams(){return this.params}toString(){return this.params.toString()}get size(){let t=0;return this.params.forEach(()=>t++),t}[Symbol.iterator](){return this.params.entries()}}class r{constructor(){this.storage=new t.AsyncLocalStorage,this._injections=new Map,this._registry=null}static get instance(){return r._instance||(r._instance=new r),r._instance}setRegistry(t){this._registry=t}get injections(){return this._injections}setInject(t,e){this._injections.set(t,{factory:e})}async getInject(t){const e=this._injections.get(t);if(e){if(!e.instance){if(!this._registry)throw new Error(`[ExecuteContext] Registry not set. Cannot initialize inject '${t}'.`);e.instance=await e.factory(this._registry),"function"==typeof e.instance.onInit&&await e.instance.onInit()}return"function"==typeof e.instance.switch?e.instance.switch():e.instance}}getInjectSync(t){const e=this._injections.get(t);if(e?.instance)return"function"==typeof e.instance.switch?e.instance.switch():e.instance}hasInject(t){return this._injections.has(t)}isInjectInitialized(t){const e=this._injections.get(t);return!!e?.instance}clearInjections(){this._injections.clear()}run(t,e){return this.storage.run(t,e)}getContext(){return this.storage.getStore()}getContextOrThrow(){const t=this.storage.getStore();if(!t)throw new Error("[ExecuteContext] No context available. Make sure you're inside a request handler.");return t}switchHttp(){const t=this.getContextOrThrow();let r=null;const n=()=>(r||(r=(()=>{if(t.req.nextUrl){const r=(t.req.nextUrl,URL,t.req.nextUrl.searchParams);return new e(r)}if(t.req.url instanceof URL)return new e(t.req.url.searchParams);if("string"==typeof t.req.url&&t.req.url.includes("?")){const r=t.req.url.split("?")[1];return new e(r)}return t.req.query?new e(t.req.query):new e})()),r);return{getRequest:()=>t.req,getResponse:()=>t.res,getMethod:()=>t.method,getPath:()=>t.path,params:()=>t.params,wildcards:()=>t.wildcards,searchParams:n,query:()=>n().toObject(),headers:()=>Object.fromEntries(t.req.headers),body:()=>t.req.body,json:()=>t.req.json(),text:()=>t.req.text(),formData:()=>t.req.formData(),blob:()=>t.req.blob(),arrayBuffer:()=>t.req.arrayBuffer()}}getReflect(){return this.getContext()?.reflect}getMetadata(t){return this.getContext()?.reflect.getMetadata(t)}getFeature(){return this.getContext()?.feature}async getLocalInject(t){const e=this.getContext()?.feature;if(e?.hasInject(t)){const r=await e.getInject(t);if(!r)return;return"function"==typeof r.switch?r.switch():r}return this.getInject(t)}getLocalInjectSync(t){const e=this.getContext()?.feature;if(e?.hasInject(t)){const r=e.getInjectSync(t);if(!r)return;return"function"==typeof r.switch?r.switch():r}return this.getInjectSync(t)}async switch(t){const e=this.getContext()?.feature;if(e?.hasInject(t)){return await e.getInject(t)}const r=this._injections.get(t);if(!r)throw new Error(`[ExecuteContext] Inject '${t}' not found.`);if(!r.instance){if(!this._registry)throw new Error(`[ExecuteContext] Registry not set. Cannot initialize inject '${t}'.`);r.instance=await r.factory(this._registry),"function"==typeof r.instance.onInit&&await r.instance.onInit()}if("function"!=typeof r.instance.switch)throw new Error(`[ExecuteContext] Inject '${t}' does not implement switch() method.`);return r.instance.switch()}switchSync(t){const e=this.getContext()?.feature;let r;if(r=e?.hasInject(t)?e.getInjectSync(t):this.getInjectSync(t),r){if("function"!=typeof r.switch)throw new Error(`[ExecuteContext] Inject '${t}' does not implement switch() method.`);return r.switch()}}}r._instance=null,exports.CoreSearchParams=e,exports.ExecuteContext=r,exports.Reflect=class{constructor(t,e,r,n,s){this.metadata=t,this.guards=e,this.filters=r,this.interceptors=n,this.pipes=s}getGuards(){return this.guards}getFilters(){return this.filters}getInterceptors(){return this.interceptors}getPipes(){return this.pipes}getMetadata(t){return this.metadata.get(t)}getAllMetadata(){return this.metadata}};
|
package/dist/execute.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AsyncLocalStorage as t}from"async_hooks";class e{constructor(t,e,n,r,s){this.metadata=t,this.guards=e,this.filters=
|
|
1
|
+
import{AsyncLocalStorage as t}from"async_hooks";class e{constructor(t){if(t)if(t instanceof URLSearchParams)this.params=t;else if("string"==typeof t){const e=t.startsWith("?")?t.slice(1):t;this.params=new URLSearchParams(e)}else{this.params=new URLSearchParams;for(const[e,r]of Object.entries(t))if(void 0!==r)if(Array.isArray(r))for(const t of r)this.params.append(e,t);else this.params.set(e,r)}else this.params=new URLSearchParams}get(t){return this.params.get(t)}getAll(t){return this.params.getAll(t)}has(t){return this.params.has(t)}getNumber(t,e){const r=this.params.get(t);if(null===r)return e;const n=Number(r);return isNaN(n)?e:n}getBoolean(t,e){const r=this.params.get(t);if(null===r)return e;const n=r.toLowerCase();return!!["true","1","yes"].includes(n)||!["false","0","no"].includes(n)&&e}keys(){return this.params.keys()}values(){return this.params.values()}entries(){return this.params.entries()}forEach(t){this.params.forEach(t)}toObject(){const t={};for(const e of this.params.keys())t[e]=this.params.get(e);return t}toObjectAll(){const t={};for(const e of this.params.keys())t[e]||(t[e]=this.params.getAll(e));return t}toURLSearchParams(){return this.params}toString(){return this.params.toString()}get size(){let t=0;return this.params.forEach(()=>t++),t}[Symbol.iterator](){return this.params.entries()}}class r{constructor(t,e,r,n,s){this.metadata=t,this.guards=e,this.filters=r,this.interceptors=n,this.pipes=s}getGuards(){return this.guards}getFilters(){return this.filters}getInterceptors(){return this.interceptors}getPipes(){return this.pipes}getMetadata(t){return this.metadata.get(t)}getAllMetadata(){return this.metadata}}class n{constructor(){this.storage=new t,this._injections=new Map,this._registry=null}static get instance(){return n._instance||(n._instance=new n),n._instance}setRegistry(t){this._registry=t}get injections(){return this._injections}setInject(t,e){this._injections.set(t,{factory:e})}async getInject(t){const e=this._injections.get(t);if(e){if(!e.instance){if(!this._registry)throw new Error(`[ExecuteContext] Registry not set. Cannot initialize inject '${t}'.`);e.instance=await e.factory(this._registry),"function"==typeof e.instance.onInit&&await e.instance.onInit()}return"function"==typeof e.instance.switch?e.instance.switch():e.instance}}getInjectSync(t){const e=this._injections.get(t);if(e?.instance)return"function"==typeof e.instance.switch?e.instance.switch():e.instance}hasInject(t){return this._injections.has(t)}isInjectInitialized(t){const e=this._injections.get(t);return!!e?.instance}clearInjections(){this._injections.clear()}run(t,e){return this.storage.run(t,e)}getContext(){return this.storage.getStore()}getContextOrThrow(){const t=this.storage.getStore();if(!t)throw new Error("[ExecuteContext] No context available. Make sure you're inside a request handler.");return t}switchHttp(){const t=this.getContextOrThrow();let r=null;const n=()=>(r||(r=(()=>{if(t.req.nextUrl){const r=(t.req.nextUrl,URL,t.req.nextUrl.searchParams);return new e(r)}if(t.req.url instanceof URL)return new e(t.req.url.searchParams);if("string"==typeof t.req.url&&t.req.url.includes("?")){const r=t.req.url.split("?")[1];return new e(r)}return t.req.query?new e(t.req.query):new e})()),r);return{getRequest:()=>t.req,getResponse:()=>t.res,getMethod:()=>t.method,getPath:()=>t.path,params:()=>t.params,wildcards:()=>t.wildcards,searchParams:n,query:()=>n().toObject(),headers:()=>Object.fromEntries(t.req.headers),body:()=>t.req.body,json:()=>t.req.json(),text:()=>t.req.text(),formData:()=>t.req.formData(),blob:()=>t.req.blob(),arrayBuffer:()=>t.req.arrayBuffer()}}getReflect(){return this.getContext()?.reflect}getMetadata(t){return this.getContext()?.reflect.getMetadata(t)}getFeature(){return this.getContext()?.feature}async getLocalInject(t){const e=this.getContext()?.feature;if(e?.hasInject(t)){const r=await e.getInject(t);if(!r)return;return"function"==typeof r.switch?r.switch():r}return this.getInject(t)}getLocalInjectSync(t){const e=this.getContext()?.feature;if(e?.hasInject(t)){const r=e.getInjectSync(t);if(!r)return;return"function"==typeof r.switch?r.switch():r}return this.getInjectSync(t)}async switch(t){const e=this.getContext()?.feature;if(e?.hasInject(t)){return await e.getInject(t)}const r=this._injections.get(t);if(!r)throw new Error(`[ExecuteContext] Inject '${t}' not found.`);if(!r.instance){if(!this._registry)throw new Error(`[ExecuteContext] Registry not set. Cannot initialize inject '${t}'.`);r.instance=await r.factory(this._registry),"function"==typeof r.instance.onInit&&await r.instance.onInit()}if("function"!=typeof r.instance.switch)throw new Error(`[ExecuteContext] Inject '${t}' does not implement switch() method.`);return r.instance.switch()}switchSync(t){const e=this.getContext()?.feature;let r;if(r=e?.hasInject(t)?e.getInjectSync(t):this.getInjectSync(t),r){if("function"!=typeof r.switch)throw new Error(`[ExecuteContext] Inject '${t}' does not implement switch() method.`);return r.switch()}}}n._instance=null;export{e as CoreSearchParams,n as ExecuteContext,r as Reflect};
|