@mxweb/core 1.0.2 → 1.1.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/dist/config.d.ts CHANGED
@@ -272,4 +272,22 @@ export declare class Config implements ApplicationInject {
272
272
  * ```
273
273
  */
274
274
  reset(): void;
275
+ /**
276
+ * Returns the Config instance itself for context switching.
277
+ * Allows accessing Config via `context.switch("config")`.
278
+ *
279
+ * @template T - The expected return type (defaults to Config)
280
+ * @returns The Config instance
281
+ *
282
+ * @example
283
+ * ```ts
284
+ * // In a controller or service
285
+ * const config = await this.context.switch<Config>("config");
286
+ * const dbHost = config.get<string>("db.host");
287
+ *
288
+ * // Or access config values directly
289
+ * const port = config.get<number>("port", 3000);
290
+ * ```
291
+ */
292
+ switch<T = Config>(): T;
275
293
  }
package/dist/config.js CHANGED
@@ -1 +1 @@
1
- "use strict";class t{static forRoot(r="config"){return s=>{s.set(r,()=>(t.instance||(t.instance=new t),t.instance))}}async onInit(){await t.buildAsync()}static register(r,s){return t.providers.set(r,s),t}static registerAsync(r,s){return t.asyncProviders.set(r,s),t}static build(){if(!t.built){for(const[r,s]of t.providers)t.store.has(r)||t.store.set(r,s());t.built=!0}}static async buildAsync(){if(t.build(),!t.asyncBuilt){for(const[r,s]of t.asyncProviders)t.store.has(r)||t.store.set(r,await s());t.asyncBuilt=!0}}get(r,s){return t.build(),t.store.get(r)??s}async getAsync(r,s){return await t.buildAsync(),t.store.get(r)??s}getOrThrow(r){if(t.build(),!t.store.has(r))throw new Error(`[Config] Config key "${r}" not found`);return t.store.get(r)}async getAsyncOrThrow(r){if(await t.buildAsync(),!t.store.has(r))throw new Error(`[Config] Config key "${r}" not found`);return t.store.get(r)}has(r){return t.store.has(r)||t.providers.has(r)||t.asyncProviders.has(r)}getAll(){return new Map(t.store)}reset(){t.instance=null,t.store.clear(),t.providers.clear(),t.asyncProviders.clear(),t.built=!1,t.asyncBuilt=!1}}t.instance=null,t.store=new Map,t.providers=new Map,t.asyncProviders=new Map,t.built=!1,t.asyncBuilt=!1,exports.Config=t;
1
+ "use strict";class t{static forRoot(r="config"){return s=>{s.set(r,()=>(t.instance||(t.instance=new t),t.instance))}}async onInit(){await t.buildAsync()}static register(r,s){return t.providers.set(r,s),t}static registerAsync(r,s){return t.asyncProviders.set(r,s),t}static build(){if(!t.built){for(const[r,s]of t.providers)t.store.has(r)||t.store.set(r,s());t.built=!0}}static async buildAsync(){if(t.build(),!t.asyncBuilt){for(const[r,s]of t.asyncProviders)t.store.has(r)||t.store.set(r,await s());t.asyncBuilt=!0}}get(r,s){return t.build(),t.store.get(r)??s}async getAsync(r,s){return await t.buildAsync(),t.store.get(r)??s}getOrThrow(r){if(t.build(),!t.store.has(r))throw new Error(`[Config] Config key "${r}" not found`);return t.store.get(r)}async getAsyncOrThrow(r){if(await t.buildAsync(),!t.store.has(r))throw new Error(`[Config] Config key "${r}" not found`);return t.store.get(r)}has(r){return t.store.has(r)||t.providers.has(r)||t.asyncProviders.has(r)}getAll(){return new Map(t.store)}reset(){t.instance=null,t.store.clear(),t.providers.clear(),t.asyncProviders.clear(),t.built=!1,t.asyncBuilt=!1}switch(){return this}}t.instance=null,t.store=new Map,t.providers=new Map,t.asyncProviders=new Map,t.built=!1,t.asyncBuilt=!1,exports.Config=t;
package/dist/config.mjs CHANGED
@@ -1 +1 @@
1
- class r{static forRoot(t="config"){return s=>{s.set(t,()=>(r.instance||(r.instance=new r),r.instance))}}async onInit(){await r.buildAsync()}static register(t,s){return r.providers.set(t,s),r}static registerAsync(t,s){return r.asyncProviders.set(t,s),r}static build(){if(!r.built){for(const[t,s]of r.providers)r.store.has(t)||r.store.set(t,s());r.built=!0}}static async buildAsync(){if(r.build(),!r.asyncBuilt){for(const[t,s]of r.asyncProviders)r.store.has(t)||r.store.set(t,await s());r.asyncBuilt=!0}}get(t,s){return r.build(),r.store.get(t)??s}async getAsync(t,s){return await r.buildAsync(),r.store.get(t)??s}getOrThrow(t){if(r.build(),!r.store.has(t))throw new Error(`[Config] Config key "${t}" not found`);return r.store.get(t)}async getAsyncOrThrow(t){if(await r.buildAsync(),!r.store.has(t))throw new Error(`[Config] Config key "${t}" not found`);return r.store.get(t)}has(t){return r.store.has(t)||r.providers.has(t)||r.asyncProviders.has(t)}getAll(){return new Map(r.store)}reset(){r.instance=null,r.store.clear(),r.providers.clear(),r.asyncProviders.clear(),r.built=!1,r.asyncBuilt=!1}}r.instance=null,r.store=new Map,r.providers=new Map,r.asyncProviders=new Map,r.built=!1,r.asyncBuilt=!1;export{r as Config};
1
+ class t{static forRoot(r="config"){return s=>{s.set(r,()=>(t.instance||(t.instance=new t),t.instance))}}async onInit(){await t.buildAsync()}static register(r,s){return t.providers.set(r,s),t}static registerAsync(r,s){return t.asyncProviders.set(r,s),t}static build(){if(!t.built){for(const[r,s]of t.providers)t.store.has(r)||t.store.set(r,s());t.built=!0}}static async buildAsync(){if(t.build(),!t.asyncBuilt){for(const[r,s]of t.asyncProviders)t.store.has(r)||t.store.set(r,await s());t.asyncBuilt=!0}}get(r,s){return t.build(),t.store.get(r)??s}async getAsync(r,s){return await t.buildAsync(),t.store.get(r)??s}getOrThrow(r){if(t.build(),!t.store.has(r))throw new Error(`[Config] Config key "${r}" not found`);return t.store.get(r)}async getAsyncOrThrow(r){if(await t.buildAsync(),!t.store.has(r))throw new Error(`[Config] Config key "${r}" not found`);return t.store.get(r)}has(r){return t.store.has(r)||t.providers.has(r)||t.asyncProviders.has(r)}getAll(){return new Map(t.store)}reset(){t.instance=null,t.store.clear(),t.providers.clear(),t.asyncProviders.clear(),t.built=!1,t.asyncBuilt=!1}switch(){return this}}t.instance=null,t.store=new Map,t.providers=new Map,t.asyncProviders=new Map,t.built=!1,t.asyncBuilt=!1;export{t as Config};
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * @module decorator
10
10
  */
11
- import { ExecuteContext, Filter, Guard, Interceptor } from "./execute";
11
+ import { CoreRequest, ExecuteContext, Filter, Guard, Interceptor } from "./execute";
12
12
  import { FeatureInject, Pipe } from "./common";
13
13
  /**
14
14
  * Result object from decorator functions.
@@ -234,18 +234,25 @@ export declare function QueryParam(key: string): string | undefined;
234
234
  /**
235
235
  * Extracts the raw Request object.
236
236
  *
237
- * @returns The underlying Request object (NextRequest)
237
+ * @template T - The specific request type (e.g., NextRequest, Express.Request)
238
+ * @returns The underlying CoreRequest object, cast to the specified type
238
239
  *
239
240
  * @example
240
241
  * ```ts
242
+ * // Default (CoreRequest)
241
243
  * findAll() {
242
244
  * const request = Request();
243
245
  * const url = request.url;
244
- * const method = request.method;
246
+ * }
247
+ *
248
+ * // With specific type (Next.js)
249
+ * findAll() {
250
+ * const request = Request<NextRequest>();
251
+ * const nextUrl = request.nextUrl; // ✅ Type-safe
245
252
  * }
246
253
  * ```
247
254
  */
248
- export declare function Request(): globalThis.Request;
255
+ export declare function Request<T extends CoreRequest = CoreRequest>(): T;
249
256
  /**
250
257
  * Extracts and parses FormData from the request body.
251
258
  *
package/dist/execute.d.ts CHANGED
@@ -8,10 +8,226 @@
8
8
  *
9
9
  * @module execute
10
10
  */
11
- import { NextRequest } from "next/server";
12
11
  import { ServerResponse } from "./response";
13
12
  import { Feature } from "./feature";
14
- import { ApplicationInject, CallHandler, FeatureInject, InjectEntry, InjectInstanceFactory, InjectRegistry, Pipe, RouteMethod, RouteNextHandler } from "./common";
13
+ import { ApplicationInject, InjectEntry, InjectInstanceFactory, InjectRegistry, Pipe, RouteMethod, RouteNextHandler } from "./common";
14
+ /**
15
+ * Core request interface defining the minimal contract for HTTP requests.
16
+ * This abstraction allows @mxweb/core to work with any request implementation
17
+ * (Next.js, Express, Fastify, etc.) as long as it conforms to this interface.
18
+ *
19
+ * @remarks
20
+ * This interface is designed to be compatible with multiple frameworks:
21
+ * - **Next.js**: Uses `nextUrl` for query params
22
+ * - **Express/Fastify**: Uses `url` (string) and `query` (parsed object)
23
+ * - **Fetch API**: Uses `url` (URL object)
24
+ *
25
+ * The framework adapter should populate the appropriate properties.
26
+ * `switchHttp().query()` will use `nextUrl.searchParams` → `url.searchParams` → `query` fallback.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * // Next.js Request is compatible with CoreRequest (uses nextUrl)
31
+ * const coreReq: CoreRequest = nextRequest;
32
+ *
33
+ * // Express Request adapter
34
+ * const expressReq: CoreRequest = {
35
+ * url: "/api/users?page=1",
36
+ * query: { page: "1" },
37
+ * headers: new Headers(req.headers),
38
+ * body: null,
39
+ * json: async () => req.body,
40
+ * text: async () => JSON.stringify(req.body),
41
+ * formData: async () => new FormData(),
42
+ * blob: async () => new Blob(),
43
+ * arrayBuffer: async () => new ArrayBuffer(0),
44
+ * };
45
+ *
46
+ * // Fetch Request adapter
47
+ * const fetchReq: CoreRequest = {
48
+ * url: new URL("https://example.com/api/users?page=1"),
49
+ * headers: request.headers,
50
+ * body: request.body,
51
+ * json: () => request.json(),
52
+ * text: () => request.text(),
53
+ * formData: () => request.formData(),
54
+ * blob: () => request.blob(),
55
+ * arrayBuffer: () => request.arrayBuffer(),
56
+ * };
57
+ * ```
58
+ */
59
+ export interface CoreRequest {
60
+ /**
61
+ * The NextURL of the request (Next.js specific).
62
+ * Used for extracting query parameters in Next.js applications.
63
+ * Takes priority over `url` when available.
64
+ */
65
+ readonly nextUrl?: URL | {
66
+ readonly searchParams: URLSearchParams;
67
+ };
68
+ /**
69
+ * The URL of the request.
70
+ * Can be:
71
+ * - URL object (Fetch API, custom implementations)
72
+ * - string (Express, Fastify - just the path with query string)
73
+ */
74
+ readonly url?: URL | string;
75
+ /**
76
+ * Pre-parsed query parameters object (Express/Fastify style).
77
+ * Used as fallback when `nextUrl` and `url` are not available or don't have searchParams.
78
+ */
79
+ readonly query?: Record<string, string | string[] | undefined>;
80
+ /**
81
+ * The HTTP headers of the request.
82
+ * Must be iterable (for use with Object.fromEntries).
83
+ */
84
+ readonly headers: Headers;
85
+ /**
86
+ * The body of the request as a ReadableStream, or null if no body.
87
+ */
88
+ readonly body: ReadableStream<Uint8Array> | null;
89
+ /**
90
+ * Parses the request body as JSON.
91
+ * @returns Promise resolving to the parsed JSON value
92
+ */
93
+ json(): Promise<unknown>;
94
+ /**
95
+ * Returns the request body as text.
96
+ * @returns Promise resolving to the body text
97
+ */
98
+ text(): Promise<string>;
99
+ /**
100
+ * Parses the request body as FormData.
101
+ * @returns Promise resolving to the FormData
102
+ */
103
+ formData(): Promise<FormData>;
104
+ /**
105
+ * Returns the request body as a Blob.
106
+ * @returns Promise resolving to the Blob
107
+ */
108
+ blob(): Promise<Blob>;
109
+ /**
110
+ * Returns the request body as an ArrayBuffer.
111
+ * @returns Promise resolving to the ArrayBuffer
112
+ */
113
+ arrayBuffer(): Promise<ArrayBuffer>;
114
+ }
115
+ /**
116
+ * Unified search params class that works with multiple input sources.
117
+ * Provides a consistent API for accessing query parameters regardless of the framework.
118
+ *
119
+ * @remarks
120
+ * This class abstracts the differences between:
121
+ * - URLSearchParams (Fetch API, Next.js)
122
+ * - Query object (Express, Fastify)
123
+ * - Query string (raw URL parsing)
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * // From URLSearchParams
128
+ * const params = new CoreSearchParams(new URLSearchParams("page=1&limit=10"));
129
+ *
130
+ * // From object (Express style)
131
+ * const params = new CoreSearchParams({ page: "1", limit: "10" });
132
+ *
133
+ * // From query string
134
+ * const params = new CoreSearchParams("page=1&limit=10");
135
+ *
136
+ * // Usage
137
+ * params.get("page"); // "1"
138
+ * params.getNumber("page"); // 1
139
+ * params.getAll("tags"); // ["a", "b"]
140
+ * params.toObject(); // { page: "1", limit: "10" }
141
+ * ```
142
+ */
143
+ export declare class CoreSearchParams {
144
+ private readonly params;
145
+ /**
146
+ * Creates a new CoreSearchParams instance.
147
+ *
148
+ * @param source - The source of query parameters:
149
+ * - URLSearchParams: Used directly
150
+ * - Record<string, string | string[] | undefined>: Object with query values
151
+ * - string: Query string (with or without leading "?")
152
+ * - undefined/null: Empty params
153
+ */
154
+ constructor(source?: URLSearchParams | Record<string, string | string[] | undefined> | string | null);
155
+ /**
156
+ * Gets the first value for a parameter.
157
+ * @param name - Parameter name
158
+ * @returns The value or null if not found
159
+ */
160
+ get(name: string): string | null;
161
+ /**
162
+ * Gets all values for a parameter.
163
+ * @param name - Parameter name
164
+ * @returns Array of values (empty if not found)
165
+ */
166
+ getAll(name: string): string[];
167
+ /**
168
+ * Checks if a parameter exists.
169
+ * @param name - Parameter name
170
+ * @returns true if the parameter exists
171
+ */
172
+ has(name: string): boolean;
173
+ /**
174
+ * Gets a parameter as a number.
175
+ * @param name - Parameter name
176
+ * @param defaultValue - Default value if not found or not a valid number
177
+ * @returns The parsed number or default value
178
+ */
179
+ getNumber(name: string, defaultValue?: number): number | undefined;
180
+ /**
181
+ * Gets a parameter as a boolean.
182
+ * Treats "true", "1", "yes" as true; "false", "0", "no" as false.
183
+ * @param name - Parameter name
184
+ * @param defaultValue - Default value if not found
185
+ * @returns The parsed boolean or default value
186
+ */
187
+ getBoolean(name: string, defaultValue?: boolean): boolean | undefined;
188
+ /**
189
+ * Returns an iterator of all parameter names.
190
+ */
191
+ keys(): IterableIterator<string>;
192
+ /**
193
+ * Returns an iterator of all parameter values.
194
+ */
195
+ values(): IterableIterator<string>;
196
+ /**
197
+ * Returns an iterator of all [name, value] pairs.
198
+ */
199
+ entries(): IterableIterator<[string, string]>;
200
+ /**
201
+ * Executes a callback for each parameter.
202
+ */
203
+ forEach(callback: (value: string, key: string, parent: URLSearchParams) => void): void;
204
+ /**
205
+ * Converts to a plain object (first value only for each key).
206
+ * @returns Object with string values
207
+ */
208
+ toObject(): Record<string, string>;
209
+ /**
210
+ * Converts to a plain object (all values as arrays).
211
+ * @returns Object with string array values
212
+ */
213
+ toObjectAll(): Record<string, string[]>;
214
+ /**
215
+ * Returns the underlying URLSearchParams.
216
+ */
217
+ toURLSearchParams(): URLSearchParams;
218
+ /**
219
+ * Converts to query string (without leading "?").
220
+ */
221
+ toString(): string;
222
+ /**
223
+ * Returns the number of parameters (counting duplicates).
224
+ */
225
+ get size(): number;
226
+ /**
227
+ * Makes the class iterable.
228
+ */
229
+ [Symbol.iterator](): IterableIterator<[string, string]>;
230
+ }
15
231
  /**
16
232
  * Container for route-level metadata, guards, filters, interceptors, and pipes.
17
233
  * Created from decorator results and attached to each route.
@@ -83,8 +299,8 @@ export declare class Reflect {
83
299
  * Created for each request and available throughout the request lifecycle.
84
300
  */
85
301
  export interface RequestContext {
86
- /** The incoming Next.js request */
87
- req: NextRequest;
302
+ /** The incoming HTTP request (CoreRequest-compatible) */
303
+ req: CoreRequest;
88
304
  /** The response builder */
89
305
  res: ServerResponse;
90
306
  /** HTTP method of the request */
@@ -176,25 +392,31 @@ export declare class ExecuteContext {
176
392
  /**
177
393
  * Retrieves a global inject by name with lazy initialization.
178
394
  * If the inject hasn't been initialized yet, calls the factory to create it.
395
+ * Returns the result of switch() if available, otherwise the raw instance.
179
396
  *
180
- * @template T - Expected type of the inject (must extend ApplicationInject)
397
+ * @template T - Expected return type (from switch() or the inject itself)
181
398
  * @param name - The name of the inject to retrieve
182
- * @returns Promise resolving to the inject instance or undefined if not found
399
+ * @returns Promise resolving to the switched value or undefined if not found
183
400
  *
184
401
  * @example
185
402
  * ```ts
186
- * const db = await context.getInject<DatabaseConnection>("db");
403
+ * // If inject implements switch(), returns the switched value
404
+ * const db = await context.getInject<DatabaseClient>("db");
405
+ * const users = await db.query("SELECT * FROM users");
406
+ *
407
+ * // If inject doesn't implement switch(), returns the inject instance
187
408
  * const config = await context.getInject<Config>("config");
188
409
  * ```
189
410
  */
190
- getInject<T extends ApplicationInject = ApplicationInject>(name: string): Promise<T | undefined>;
411
+ getInject<T = unknown>(name: string): Promise<T | undefined>;
191
412
  /**
192
413
  * Retrieves a global inject synchronously (only if already initialized).
193
414
  * Returns undefined if the inject hasn't been initialized yet.
415
+ * Returns the result of switch() if available, otherwise the raw instance.
194
416
  *
195
- * @template T - Expected type of the inject (must extend ApplicationInject)
417
+ * @template T - Expected return type (from switch() or the inject itself)
196
418
  * @param name - The name of the inject to retrieve
197
- * @returns The inject instance or undefined if not found/not initialized
419
+ * @returns The switched value or undefined if not found/not initialized
198
420
  *
199
421
  * @example
200
422
  * ```ts
@@ -202,7 +424,7 @@ export declare class ExecuteContext {
202
424
  * const config = context.getInjectSync<Config>("config");
203
425
  * ```
204
426
  */
205
- getInjectSync<T extends ApplicationInject = ApplicationInject>(name: string): T | undefined;
427
+ getInjectSync<T = unknown>(name: string): T | undefined;
206
428
  /**
207
429
  * Checks if a global inject factory exists by name.
208
430
  *
@@ -285,8 +507,8 @@ export declare class ExecuteContext {
285
507
  * ```
286
508
  */
287
509
  switchHttp(): {
288
- /** Returns the Next.js request object */
289
- getRequest: () => NextRequest;
510
+ /** Returns the CoreRequest object, optionally cast to a specific type */
511
+ getRequest: <T extends CoreRequest = CoreRequest>() => T;
290
512
  /** Returns the ServerResponse builder */
291
513
  getResponse: () => ServerResponse;
292
514
  /** Returns the HTTP method */
@@ -297,16 +519,16 @@ export declare class ExecuteContext {
297
519
  params: () => Record<string, string>;
298
520
  /** Returns wildcard segments */
299
521
  wildcards: () => string[];
300
- /** Returns query parameters as object */
301
- query: () => {
302
- [k: string]: string;
303
- };
522
+ /** Returns CoreSearchParams instance for query parameter access */
523
+ searchParams: () => CoreSearchParams;
524
+ /** Returns query parameters as plain object (first value only) */
525
+ query: () => Record<string, string>;
304
526
  /** Returns headers as object */
305
527
  headers: () => {
306
528
  [k: string]: string;
307
529
  };
308
530
  /** Returns the raw body ReadableStream */
309
- body: () => ReadableStream<Uint8Array<ArrayBuffer>> | null;
531
+ body: () => ReadableStream<Uint8Array<ArrayBufferLike>> | null;
310
532
  /** Parses and returns the JSON body */
311
533
  json: <T = unknown>() => Promise<T>;
312
534
  /** Returns the body as text */
@@ -343,10 +565,11 @@ export declare class ExecuteContext {
343
565
  /**
344
566
  * Retrieves a feature-local inject by name with lazy initialization.
345
567
  * Falls back to global injects if not found in the feature.
568
+ * Returns the result of switch() if available, otherwise the raw instance.
346
569
  *
347
- * @template T - Expected type of the inject (must extend FeatureInject)
570
+ * @template T - Expected return type (from switch() or the inject itself)
348
571
  * @param name - The name of the inject to retrieve
349
- * @returns Promise resolving to the inject instance or undefined if not found
572
+ * @returns Promise resolving to the switched value or undefined if not found
350
573
  *
351
574
  * @example
352
575
  * ```ts
@@ -354,14 +577,15 @@ export declare class ExecuteContext {
354
577
  * const repo = await context.getLocalInject<ProductRepository>("productRepo");
355
578
  * ```
356
579
  */
357
- getLocalInject<T extends FeatureInject>(name: string): Promise<T | undefined>;
580
+ getLocalInject<T = unknown>(name: string): Promise<T | undefined>;
358
581
  /**
359
582
  * Retrieves a feature-local inject synchronously (only if already initialized).
360
583
  * Falls back to global injects if not found in the feature.
584
+ * Returns the result of switch() if available, otherwise the raw instance.
361
585
  *
362
- * @template T - Expected type of the inject (must extend FeatureInject)
586
+ * @template T - Expected return type (from switch() or the inject itself)
363
587
  * @param name - The name of the inject to retrieve
364
- * @returns The inject instance or undefined if not found/not initialized
588
+ * @returns The switched value or undefined if not found/not initialized
365
589
  *
366
590
  * @example
367
591
  * ```ts
@@ -369,7 +593,51 @@ export declare class ExecuteContext {
369
593
  * const repo = context.getLocalInjectSync<ProductRepository>("productRepo");
370
594
  * ```
371
595
  */
372
- getLocalInjectSync<T extends FeatureInject>(name: string): T | undefined;
596
+ getLocalInjectSync<T = unknown>(name: string): T | undefined;
597
+ /**
598
+ * Retrieves an inject by name and calls its switch() method.
599
+ * Searches in feature injects first (priority), then falls back to global injects.
600
+ *
601
+ * @template T - Expected return type of the switch() method
602
+ * @param name - The name of the inject to switch to
603
+ * @returns Promise resolving to the result of the inject's switch() method
604
+ * @throws {Error} If the inject is not found or doesn't have a switch() method
605
+ *
606
+ * @example
607
+ * ```ts
608
+ * // In a controller or service
609
+ * const db = await this.context.switch<DatabaseClient>("db");
610
+ * const users = await db.query("SELECT * FROM users");
611
+ *
612
+ * // The inject must implement switch():
613
+ * class Connection implements ApplicationInject {
614
+ * switch<T = DatabaseClient>(): T {
615
+ * return this.client as T;
616
+ * }
617
+ * }
618
+ * ```
619
+ */
620
+ switch<T = unknown>(name: string): Promise<T>;
621
+ /**
622
+ * Retrieves an inject by name and calls its switch() method (synchronous version).
623
+ * Only works if the inject has already been initialized.
624
+ * Searches in feature injects first (priority), then falls back to global injects.
625
+ *
626
+ * @template T - Expected return type of the switch() method
627
+ * @param name - The name of the inject to switch to
628
+ * @returns The result of the inject's switch() method, or undefined if not initialized
629
+ * @throws {Error} If the inject exists but doesn't have a switch() method
630
+ *
631
+ * @example
632
+ * ```ts
633
+ * // Only use when you're sure the inject is already initialized
634
+ * const db = this.context.switchSync<DatabaseClient>("db");
635
+ * if (db) {
636
+ * const users = await db.query("SELECT * FROM users");
637
+ * }
638
+ * ```
639
+ */
640
+ switchSync<T = unknown>(name: string): T | undefined;
373
641
  }
374
642
  /**
375
643
  * Interface for guard classes that determine if a request should proceed.
@@ -474,48 +742,34 @@ export interface ExceptionFilter<Response = unknown> {
474
742
  */
475
743
  export type Filter<Response = unknown> = new (...args: unknown[]) => ExceptionFilter<Response>;
476
744
  /**
477
- * Interface for interceptor classes that wrap handler execution.
478
- * Interceptors can transform requests/responses, add caching, logging, etc.
745
+ * Constructor type for response interceptor classes.
746
+ * Interceptors transform the CoreResponse after the handler returns.
479
747
  *
480
- * @template T - The type of the handler result
748
+ * @template T - The type of the response data
749
+ *
750
+ * @remarks
751
+ * The old `InterceptorHandler` interface with `intercept(context, next)` is deprecated.
752
+ * Use `ResponseInterceptorHandler` from `response.ts` instead for the new pattern.
481
753
  *
482
754
  * @example
483
755
  * ```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;
756
+ * import { ResponseInterceptorHandler, CoreResponse } from "@mxweb/core";
757
+ *
758
+ * class LoggingInterceptor implements ResponseInterceptorHandler {
759
+ * transform(response: CoreResponse): CoreResponse {
760
+ * console.log(`Response: ${response.status} ${response.body.message}`);
761
+ * return response;
490
762
  * }
491
763
  * }
492
764
  *
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;
765
+ * class CacheHeaderInterceptor implements ResponseInterceptorHandler {
766
+ * transform(response: CoreResponse): CoreResponse {
767
+ * if (response.status >= 200 && response.status < 300) {
768
+ * response.headers.set("Cache-Control", "max-age=3600");
769
+ * }
770
+ * return response;
502
771
  * }
503
772
  * }
504
773
  * ```
505
774
  */
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>;
775
+ export type Interceptor<T = unknown> = new (...args: unknown[]) => import("./response").ResponseInterceptorHandler<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 e._instance||(e._instance=new e),e._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 e.instance}}getInjectSync(t){const e=this._injections.get(t);return 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();return{getRequest:()=>t.req,getResponse:()=>t.res,getMethod:()=>t.method,getPath:()=>t.path,params:()=>t.params,wildcards:()=>t.wildcards,query:()=>Object.fromEntries(t.req.nextUrl.searchParams),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;return e?.hasInject(t)?e.getInject(t):this.getInject(t)}getLocalInjectSync(t){const e=this.getContext()?.feature;return e?.hasInject(t)?e.getInjectSync(t):this.getInjectSync(t)}}e._instance=null,exports.ExecuteContext=e,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}};
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=n,this.interceptors=r,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 e.instance}}getInjectSync(t){const e=this._injections.get(t);return 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();return{getRequest:()=>t.req,getResponse:()=>t.res,getMethod:()=>t.method,getPath:()=>t.path,params:()=>t.params,wildcards:()=>t.wildcards,query:()=>Object.fromEntries(t.req.nextUrl.searchParams),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;return e?.hasInject(t)?e.getInject(t):this.getInject(t)}getLocalInjectSync(t){const e=this.getContext()?.feature;return e?.hasInject(t)?e.getInjectSync(t):this.getInjectSync(t)}}n._instance=null;export{n as ExecuteContext,e as Reflect};
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};
package/dist/feature.d.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  import { ControllerConstructor } from "./controller";
11
11
  import { RouteMatched, Router } from "./router";
12
12
  import { FeatureHooks, FeatureHooksOptions } from "./hooks";
13
- import { FeatureInject, FeatureInjectFactory, RouteMethod } from "./common";
13
+ import { FeatureInjectFactory, RouteMethod } from "./common";
14
14
  /**
15
15
  * Type for feature-specific dependency injection configuration.
16
16
  * Array of factory functions that register injects.
@@ -208,25 +208,27 @@ export declare class Feature {
208
208
  /**
209
209
  * Retrieves a feature-specific inject by name (async - lazy initialization).
210
210
  * If instance doesn't exist, calls factory to create it and runs onFeature().
211
+ * Returns the result of switch() if available, otherwise the raw instance.
211
212
  *
212
- * @template T - Expected type of the inject (must extend FeatureInject)
213
+ * @template T - Expected return type (from switch() or the inject itself)
213
214
  * @param name - The name of the inject to retrieve
214
- * @returns Promise resolving to the inject instance or undefined if not found
215
+ * @returns Promise resolving to the switched value or undefined if not found
215
216
  *
216
217
  * @example
217
218
  * ```ts
218
219
  * const repo = await feature.getInject<ProductRepository>("productRepo");
219
220
  * ```
220
221
  */
221
- getInject<T extends FeatureInject>(name: string): Promise<T | undefined>;
222
+ getInject<T = unknown>(name: string): Promise<T | undefined>;
222
223
  /**
223
224
  * Retrieves a feature-specific inject synchronously (only if already initialized).
225
+ * Returns the result of switch() if available, otherwise the raw instance.
224
226
  *
225
- * @template T - Expected type of the inject (must extend FeatureInject)
227
+ * @template T - Expected return type (from switch() or the inject itself)
226
228
  * @param name - The name of the inject to retrieve
227
- * @returns The inject instance or undefined if not found/not initialized
229
+ * @returns The switched value or undefined if not found/not initialized
228
230
  */
229
- getInjectSync<T extends FeatureInject>(name: string): T | undefined;
231
+ getInjectSync<T = unknown>(name: string): T | undefined;
230
232
  /**
231
233
  * Checks if a feature-specific inject factory is registered by name.
232
234
  *
package/dist/feature.js CHANGED
@@ -1 +1 @@
1
- "use strict";var t=require("./hooks.js");class e{constructor(e,s,i){this.options=e,this.ControllerContructor=s,this.router=i,this.routeMatched=null,this.controller=null,this.injects=new Map,this.factoriesRegistered=!1,this.importsLoaded=!1,this.hooks=new t.FeatureHooks(this.options),this.registry=this.createInjectRegistry()}createInjectRegistry(){return{get:t=>this.getInject(t),set:(t,e)=>{this.injects.set(t,{factory:e})}}}static create(t){return new e(t,t.controller,t.router)}async loadImports(){if(this.importsLoaded)return;this.importsLoaded=!0;const{imports:t}=this.options;if(!t)return;"function"==typeof t&&await t()}async loadInjects(){if(await this.loadImports(),this.factoriesRegistered)return;this.factoriesRegistered=!0;const{injects:t}=this.options;if(t&&t.length)for(const e of t)await e(this.registry)}async destroyInjects(){for(const[t,e]of this.injects)if(e.instance&&e.instance.onFeatureDestroy)try{await e.instance.onFeatureDestroy()}catch(t){}this.injects.clear(),this.factoriesRegistered=!1}async getInject(t){const e=this.injects.get(t);if(e)return e.instance||(e.instance=await e.factory(this.registry),"function"==typeof e.instance.onFeature&&await e.instance.onFeature()),e.instance}getInjectSync(t){const e=this.injects.get(t);return e?.instance}hasInject(t){return this.injects.has(t)}isInjectInitialized(t){const e=this.injects.get(t);return!!e?.instance}getController(){if(!this.controller){const t=this.ControllerContructor;this.controller=new t}return this.controller}getHooks(){return this.hooks}matchRoute(t,e){return this.routeMatched=this.router.match(t,e),this.routeMatched}}exports.Feature=e;
1
+ "use strict";var t=require("./hooks.js");class e{constructor(e,s,i){this.options=e,this.ControllerContructor=s,this.router=i,this.routeMatched=null,this.controller=null,this.injects=new Map,this.factoriesRegistered=!1,this.importsLoaded=!1,this.hooks=new t.FeatureHooks(this.options),this.registry=this.createInjectRegistry()}createInjectRegistry(){return{get:t=>this.getInject(t),set:(t,e)=>{this.injects.set(t,{factory:e})}}}static create(t){return new e(t,t.controller,t.router)}async loadImports(){if(this.importsLoaded)return;this.importsLoaded=!0;const{imports:t}=this.options;if(!t)return;"function"==typeof t&&await t()}async loadInjects(){if(await this.loadImports(),this.factoriesRegistered)return;this.factoriesRegistered=!0;const{injects:t}=this.options;if(t&&t.length)for(const e of t)await e(this.registry)}async destroyInjects(){for(const[t,e]of this.injects)if(e.instance&&e.instance.onFeatureDestroy)try{await e.instance.onFeatureDestroy()}catch(t){}this.injects.clear(),this.factoriesRegistered=!1}async getInject(t){const e=this.injects.get(t);if(e)return e.instance||(e.instance=await e.factory(this.registry),"function"==typeof e.instance.onFeature&&await e.instance.onFeature()),"function"==typeof e.instance.switch?e.instance.switch():e.instance}getInjectSync(t){const e=this.injects.get(t);if(e?.instance)return"function"==typeof e.instance.switch?e.instance.switch():e.instance}hasInject(t){return this.injects.has(t)}isInjectInitialized(t){const e=this.injects.get(t);return!!e?.instance}getController(){if(!this.controller){const t=this.ControllerContructor;this.controller=new t}return this.controller}getHooks(){return this.hooks}matchRoute(t,e){return this.routeMatched=this.router.match(t,e),this.routeMatched}}exports.Feature=e;