@noxfly/noxus 3.0.0-dev.3 → 3.0.0-dev.4

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.
@@ -1,32 +1,128 @@
1
1
  # Copilot Instructions
2
+
2
3
  ## Core Architecture
3
- - bootstrapApplication in [src/bootstrap.ts](src/bootstrap.ts) waits for Electron app readiness, resolves NoxApp via DI, and returns a configured instance.
4
- - [src/app.ts](src/app.ts) wires ipcMain events, spawns paired request/socket MessageChannels per renderer, and delegates handling to Router and NoxSocket.
5
- - Package exports are split between renderer and main targets in [package.json](package.json); import Electron main APIs from @noxfly/noxus/main and renderer helpers from @noxfly/noxus or @noxfly/noxus/renderer.
4
+ - bootstrapApplication in [src/internal/bootstrap.ts](src/internal/bootstrap.ts) waits for Electron app readiness, applies log level config, wires the controller registrar, flushes DI, resolves NoxApp, registers routes and eager-loads, then returns a configured instance.
5
+ - [src/internal/app.ts](src/internal/app.ts) wires ipcMain events, spawns paired request/socket MessageChannels per renderer, and delegates handling to Router and NoxSocket.
6
+ - Package exports are split between renderer and main targets in [package.json](package.json); import Electron main APIs from @noxfly/noxus/main, renderer helpers from @noxfly/noxus or @noxfly/noxus/renderer, and preload helpers from @noxfly/noxus/preload.
6
7
  - Dependency injection centers on RootInjector in [src/DI/app-injector.ts](src/DI/app-injector.ts); @Injectable triggers auto-registration through [src/DI/injector-explorer.ts](src/DI/injector-explorer.ts) and supports singleton, scope, and transient lifetimes.
7
- - Modules decorated via [src/decorators/module.decorator.ts](src/decorators/module.decorator.ts) compose providers, controllers, and nested modules; bootstrapApplication rejects roots lacking Module metadata.
8
+ - resetRootInjector() in [src/DI/app-injector.ts](src/DI/app-injector.ts) clears all bindings, singletons, and scoped instances use it in tests to get a clean DI state between test suites.
9
+ - There is no Module decorator — controllers and services are standalone, registered via @Injectable and @Controller decorators with explicit deps arrays.
10
+
8
11
  ## Request Lifecycle
9
- - Request objects from [src/request.ts](src/request.ts) wrap Electron MessageEvents and spawn per-request DI scopes on Request.context.
10
- - Router in [src/router.ts](src/router.ts) indexes routes in a radix tree, merges controller-level and method-level decorators, and enforces root middlewares, route middlewares, then guards before invoking controller actions.
11
- - ResponseException subclasses in [src/exceptions.ts](src/exceptions.ts) propagate status codes; throwing one short-circuits the pipeline so Router returns a structured error payload.
12
+ - Request objects from [src/internal/request.ts](src/internal/request.ts) wrap Electron MessageEvents and spawn per-request DI scopes on Request.context. They expose params, body, and query (Record<string, string>).
13
+ - Router in [src/internal/router.ts](src/internal/router.ts) indexes routes in a radix tree, merges controller-level and method-level decorators, and enforces root middlewares, route middlewares, then guards before invoking controller actions.
14
+ - The radix tree in [src/utils/radix-tree.ts](src/utils/radix-tree.ts) prioritizes static segments over parameter segments during search, so `addNote/:id` won't be shadowed by `:id`.
15
+ - ResponseException subclasses in [src/internal/exceptions.ts](src/internal/exceptions.ts) propagate status codes; throwing one short-circuits the pipeline so Router returns a structured error payload.
12
16
  - Batch requests use HTTP method BATCH and normalization logic in Router.handleBatch; payloads must satisfy IBatchRequestPayload to fan out atomic subrequests.
17
+
13
18
  ## Communication Channels
14
19
  - ipcMain listens for gimme-my-port and posts two transferable ports back to the renderer: index 0 carries request/response traffic, index 1 is reserved for socket-style push messages.
15
- - NoxSocket in [src/socket.ts](src/socket.ts) maps sender IDs to {request, socket} channels and emits renderer events exclusively through channels.socket.port1.
16
- - Renderer helpers in [src/renderer-events.ts](src/renderer-events.ts) expose RendererEventRegistry.tryDispatchFromMessageEvent to route push events; the preload script must start both ports and hand the second to this registry.
17
- - Renderer-facing bootstrap lives in [src/renderer-client.ts](src/renderer-client.ts); NoxRendererClient requests ports, wires request/socket handlers, tracks pending promises, and surfaces RendererEventRegistry for push consumption.
18
- - Preload scripts should call exposeNoxusBridge from [src/preload-bridge.ts](src/preload-bridge.ts) to publish window.noxus.requestPort; the helper sends 'gimme-my-port' and forwards both transferable ports with the configured init message.
20
+ - NoxSocket in [src/internal/socket.ts](src/internal/socket.ts) maps sender IDs to {request, socket} channels and emits renderer events exclusively through channels.socket.port1. emit() returns void.
21
+ - Renderer helpers in [src/internal/renderer-events.ts](src/internal/renderer-events.ts) expose RendererEventRegistry.tryDispatchFromMessageEvent to route push events; the preload script must start both ports and hand the second to this registry.
22
+ - Renderer-facing bootstrap lives in [src/internal/renderer-client.ts](src/internal/renderer-client.ts); NoxRendererClient requests ports, wires request/socket handlers, tracks pending promises with configurable timeout (default 10s), and surfaces RendererEventRegistry for push consumption.
23
+ - Preload scripts should call exposeNoxusBridge from [src/internal/preload-bridge.ts](src/internal/preload-bridge.ts) to publish window.noxus.requestPort; the helper sends 'gimme-my-port' and forwards both transferable ports with the configured init message.
19
24
  - When adjusting preload or renderer glue, ensure window.postMessage('init-port', ...) forwards both ports so the socket channel stays alive alongside the request channel.
25
+
26
+ ## Route Definitions
27
+ - defineRoutes in [src/internal/routes.ts](src/internal/routes.ts) is the single source of truth for routing. It validates for duplicate and overlapping prefixes and supports nested children with inherited guards/middlewares.
28
+ - Parent routes can omit the load function and serve only as shared prefix containers for children.
29
+ - flattenRoutes recursively merges parent guards and middlewares into each child before validation.
30
+
20
31
  ## Decorator Conventions
21
32
  - Controller paths omit leading/trailing slashes; method decorators (Get, Post, etc.) auto-trim segments via [src/decorators/method.decorator.ts](src/decorators/method.decorator.ts).
22
33
  - Guards registered through Authorize in [src/decorators/guards.decorator.ts](src/decorators/guards.decorator.ts) aggregate at controller and action scope; they must resolve truthy or Router throws UnauthorizedException.
23
34
  - Injectable lifetimes default to scope; use singleton for app-wide utilities (window managers, sockets) and transient for short-lived resources.
35
+
36
+ ## Dependency Injection Internals
37
+ - InjectorExplorer in [src/DI/injector-explorer.ts](src/DI/injector-explorer.ts) accumulates registrations at import time and flushes in two phases (bind then resolve).
38
+ - To avoid a circular dependency between InjectorExplorer and Router, controller registration is decoupled via a ControllerRegistrar callback set by bootstrapApplication through setControllerRegistrar(). There is no require() call.
39
+ - flushAccumulated is serialized through a Promise-based lock (loadingLock) to prevent concurrent lazy module loads from corrupting the queue. Use waitForFlush() to await completion.
40
+ - Phase 2 validates that declared deps have a corresponding binding or singleton, and emits warnings for missing ones at startup.
41
+
24
42
  ## Logging and Utilities
25
- - Logger in [src/utils/logger.ts](src/utils/logger.ts) standardizes color-coded log, warn, error, and comment output; use it when extending framework behavior.
43
+ - Logger in [src/utils/logger.ts](src/utils/logger.ts) standardizes color-coded log, warn, error, and comment output; it supports configurable log levels via Logger.setLogLevel() and file output via Logger.enableFileLogging().
44
+ - bootstrapApplication accepts a logLevel option ('debug' | 'info' | 'none' | LogLevel[]) to control framework verbosity.
26
45
  - Path resolution relies on RadixTree from [src/utils/radix-tree.ts](src/utils/radix-tree.ts); normalize controller and route paths to avoid duplicate slashes.
27
- - Request.params are filled by Router.extractParams; controllers read route params directly from Request without decorator helpers yet.
46
+ - Request.params are filled by Router.extractParams; Request.query carries query parameters passed from the renderer; controllers read both directly from the Request object.
47
+
28
48
  ## Build and Tooling
29
49
  - Run npm run build to invoke tsup with dual ESM/CJS outputs configured in [tsup.config.ts](tsup.config.ts); the postbuild script at [scripts/postbuild.js](scripts/postbuild.js) deduplicates license banners.
30
- - Node 20 or newer is required; reflect-metadata is a peer dependency so host apps must install and import it before using decorators.
50
+ - Node 20 or newer is required; no reflect-metadata is needed all dependency information comes from explicit deps arrays.
31
51
  - Source uses baseUrl ./ with tsc-alias, so prefer absolute imports like src/module/file when editing framework code to match existing style.
32
52
  - Dist artifacts live under dist/ and are published outputs; regenerate them via the build script rather than editing directly.
53
+
54
+
55
+ ## Validating TypeScript changes
56
+
57
+ MANDATORY: Always check for compilation errors before running any tests or validation scripts, or declaring work complete, then fix all compilation errors before moving forward.
58
+
59
+ ## Coding Guidelines
60
+
61
+ ### Indentation
62
+
63
+ Use spaces, 4 for any file type, except yml that are 2 spaces.
64
+
65
+ ### Naming Conventions
66
+
67
+ Use PascalCase for type names
68
+ Use PascaleCase for class names and interfaces
69
+ Prefix interface names by an 'I'
70
+ Use PascalCase for enum values
71
+ Choose wisely enum over union types or consts
72
+ Prefer interfaces over types when possible
73
+ Use camelCase for function and method names
74
+ Use camelCase for property names and local variables
75
+ Use whole words in names when possible
76
+ Use kebab-case for file names, and avoid using the same name for multiple files in different directories to prevent confusion
77
+ suffix file names with their type (e.g., .service.ts, .controller.ts, .component.ts) to make it clear what the file contains and its role in the architecture
78
+
79
+ ### Types
80
+
81
+ Do not export types or functions unless you need to share it across multiple components
82
+ Do not introduce new types or values to the global namespace unless they are truly global concepts
83
+
84
+ ### Comments
85
+
86
+ Use JSDoc style comments for functions, interfaces, enums, and classes
87
+ Always include a description of what the function/class/interface/enum does, its parameters, its return value and an example (if applicable)
88
+ Use inline comments to explain why a particular implementation was chosen, especially if it's not obvious. Avoid stating what the code is doing, and instead focus on the reasoning behind it.
89
+
90
+ ### Strings
91
+
92
+ Always use "double quotes" for strings.
93
+ Use template literals for string interpolation and multi-line strings instead of concatenation.
94
+
95
+ ### Style
96
+
97
+ Use arrow functions => over anonymous function expressions.
98
+ Only surround arrow function parameters when necessary.
99
+ Only surround arrow function bodies with curly braces when they are not a direct return of an expression.
100
+ Always surround loop and conditional bodies with curly braces.
101
+ Open curly braces always go on the same line as whatever necessitates them
102
+ Use StrouStrup style for control statements (the else is on a new line after the closing brace of the if).
103
+ Whenever possible, use in top-level scopes export function x(…) {…} instead of export const x = (…) => {…}. One advantage of using the function keyword is that the stack-trace shows a good name when debugging.
104
+
105
+ ### Code Quality
106
+
107
+ All files must include the NoxFly copyright header
108
+ Prefer async and await over Promise and then calls
109
+ Always await a promise to make the function appearable in the stack trace, unless you have a good reason not to (e.g., you want it to run in the background and don't care about errors).
110
+ Look for existing test patterns before creating new structures
111
+ Prefer regex capture groups with names over numbered capture groups.
112
+ If you create any temporary new files, scripts, or helper files for iteration, clean up these files by removing them at the end of the task
113
+ Never duplicate imports. Always reuse existing imports if they are present.
114
+ When removing an import, do not leave behind blank lines where the import was. Ensure the surrounding code remains compact.
115
+ Do not use any or unknown as the type for variables, parameters, or return values unless absolutely necessary. If they need type annotations, they should have proper types or interfaces defined.
116
+ Do not duplicate code. Always look for existing utility functions, helpers, or patterns in the codebase before implementing new functionality. Reuse and extend existing code whenever possible.
117
+ Avoid using bind(), call() and apply() solely to control this or partially apply arguments; prefer arrow functions or closures to capture the necessary context, and use these methods only when required by an API or interoperability.
118
+ Always think for the most performant code for future scalability, even if it requires more upfront work. Consider time and space complexity when designing algorithms and data structures, and prefer efficient patterns that will scale well as the codebase grows.
119
+ Always specify the `public`, `private`, or `protected` access modifier for class members, even if the default is public. This improves readability and makes the intended encapsulation clear to other developers.
120
+ Always use explicit return types for functions and methods, even when TypeScript can infer them. This improves readability and helps catch unintended return values or changes in the function's behavior over time.
121
+ Avoid using non-null assertions (!). Instead, handle potential null or undefined values explicitly through type guards, default values, or proper error handling to ensure safer and more robust code.
122
+ Always prefer composition over inheritance. Favor creating small, reusable functions and classes that can be combined to achieve complex behavior, rather than relying on deep inheritance hierarchies which can lead to tight coupling and reduced flexibility.
123
+ Always use strict equality (=== and !==) instead of loose equality (== and !=) to avoid unexpected type coercion and ensure more predictable comparisons.
124
+ Always ensure readme and copilot-instructions files are updated to reflect any architectural, structural, or convention changes made to the codebase. These documents serve as the primary reference for other developers and must accurately represent the current state of the project.
125
+ Do not hesitate to refactor existing code to improve readability, maintainability, or performance, even if it is not directly related to the task at hand. Continuous improvement of the codebase is essential for long-term success and developer satisfaction.
126
+ Do not hesitate to ask for help or clarification if a request is unclear.
127
+ Do not hesitate to suggest improvements or optimizations if you see an opportunity, even if it's outside the scope of your current task.
128
+ Always write unit tests for new functionality and bug fixes, and ensure existing tests are updated as necessary to reflect changes in the codebase. Aim for high test coverage and meaningful test cases that validate the expected behavior of the code.
package/AGENTS.md ADDED
@@ -0,0 +1,5 @@
1
+ # Agents
2
+
3
+ All project conventions, architecture details, and coding guidelines are maintained in the [Copilot Instructions](.github/copilot-instructions.md) file.
4
+
5
+ Please refer to that file for context before making changes to this codebase.
package/README.md CHANGED
@@ -120,19 +120,30 @@ export class AppService implements IApp {
120
120
  }
121
121
  ```
122
122
 
123
- ### 4. Bootstrap the application
123
+ ### 4. Define routes
124
+
125
+ ```ts
126
+ // routes.ts
127
+ import { defineRoutes } from '@noxfly/noxus/main';
128
+
129
+ export const routes = defineRoutes([
130
+ { path: 'users', load: () => import('./controllers/user.controller.js') },
131
+ { path: 'orders', load: () => import('./controllers/order.controller.js') },
132
+ ]);
133
+ ```
134
+
135
+ ### 5. Bootstrap the application
124
136
 
125
137
  ```ts
126
138
  // main.ts
127
139
  import { bootstrapApplication } from '@noxfly/noxus/main';
128
140
  import { AppService } from './app.service';
141
+ import { routes } from './routes';
129
142
 
130
- const noxApp = await bootstrapApplication();
143
+ const noxApp = await bootstrapApplication({ routes });
131
144
 
132
145
  noxApp
133
146
  .configure(AppService)
134
- .lazy('users', () => import('./controllers/user.controller.js'))
135
- .lazy('orders', () => import('./controllers/order.controller.js'))
136
147
  .start();
137
148
  ```
138
149
 
@@ -195,6 +206,9 @@ import { inject } from '@noxfly/noxus/main';
195
206
  const userService = inject(UserService);
196
207
  ```
197
208
 
209
+ When you decide to inject manually with `inject()`, you may not duplicate the dependency in the `deps` array of `@Injectable`.
210
+ The resolve dependency in deps will be ignored in the constructor and will be re-solved at runtime by `inject()`.
211
+
198
212
  Useful outside a constructor — in callbacks, factories, etc.
199
213
 
200
214
  ### `forwardRef()` — Circular dependencies
@@ -238,6 +252,21 @@ getProduct(req: Request) {
238
252
  }
239
253
  ```
240
254
 
255
+ ### Query parameters
256
+
257
+ ```ts
258
+ // Renderer side:
259
+ await client.request({ method: 'GET', path: 'users/list', query: { role: 'admin', page: '1' } });
260
+
261
+ // Controller side:
262
+ @Get('list')
263
+ list(req: Request) {
264
+ const role = req.query['role']; // 'admin'
265
+ const page = req.query['page']; // '1'
266
+ return this.svc.findAll({ role, page: parseInt(page!) });
267
+ }
268
+ ```
269
+
241
270
  ### Request body
242
271
 
243
272
  ```ts
@@ -261,10 +290,43 @@ create(req: Request, res: IResponse) {
261
290
 
262
291
  ---
263
292
 
293
+ ## Route definitions (`defineRoutes`)
294
+
295
+ `defineRoutes` is the single source of truth for your routing table. It validates prefixes, detects duplicates and overlapping paths, and supports nested routes:
296
+
297
+ ```ts
298
+ import { defineRoutes } from '@noxfly/noxus/main';
299
+
300
+ export const routes = defineRoutes([
301
+ { path: 'users', load: () => import('./modules/users/users.controller.js'), guards: [authGuard] },
302
+ { path: 'orders', load: () => import('./modules/orders/orders.controller.js') },
303
+ ]);
304
+ ```
305
+
306
+ ### Nested routes
307
+
308
+ Parent routes can omit `load` and only serve as a shared prefix with inherited guards/middlewares:
309
+
310
+ ```ts
311
+ export const routes = defineRoutes([
312
+ {
313
+ path: 'admin',
314
+ guards: [authGuard, adminGuard],
315
+ children: [
316
+ { path: 'users', load: () => import('./admin/users.controller.js') },
317
+ { path: 'products', load: () => import('./admin/products.controller.js') },
318
+ ],
319
+ },
320
+ ]);
321
+ // Produces flat routes: admin/users and admin/products, both inheriting authGuard + adminGuard.
322
+ ```
323
+
264
324
  ## Lazy loading
265
325
 
266
326
  This is the core mechanism for keeping startup fast. A lazy controller is never imported until an IPC request targets its prefix.
267
327
 
328
+ Routes declared via `defineRoutes` are lazy by default. You can also register lazy routes manually:
329
+
268
330
  ```ts
269
331
  noxApp
270
332
  .lazy('users', () => import('./modules/users/users.controller.js'))
@@ -451,7 +513,7 @@ class UserRepository {
451
513
 
452
514
  ```ts
453
515
  // preload.ts
454
- import { exposeNoxusBridge } from '@noxfly/noxus/renderer';
516
+ import { exposeNoxusBridge } from '@noxfly/noxus/preload';
455
517
 
456
518
  exposeNoxusBridge(); // exposes window.__noxus__ to the renderer
457
519
  ```
@@ -462,12 +524,15 @@ exposeNoxusBridge(); // exposes window.__noxus__ to the renderer
462
524
  // In the renderer (Angular, React, Vue, Vanilla...)
463
525
  import { NoxRendererClient } from '@noxfly/noxus';
464
526
 
465
- const client = new NoxRendererClient();
527
+ const client = new NoxRendererClient({
528
+ requestTimeout: 10_000, // 10s (default). Set to 0 to disable.
529
+ });
466
530
  await client.setup(); // requests the MessagePort from main
467
531
 
468
532
  // Requests
469
533
  const users = await client.request<User[]>({ method: 'GET', path: 'users/list' });
470
534
  const user = await client.request<User> ({ method: 'GET', path: 'users/42' });
535
+ await client.request({ method: 'GET', path: 'users/list', query: { role: 'admin' } });
471
536
  await client.request({ method: 'POST', path: 'users/create', body: { name: 'Bob' } });
472
537
  await client.request({ method: 'PUT', path: 'users/42', body: { name: 'Bob Updated' } });
473
538
  await client.request({ method: 'DELETE', path: 'users/42' });
@@ -511,7 +576,7 @@ Multiple IPC requests in a single round-trip:
511
576
 
512
577
  ```ts
513
578
  const results = await client.batch([
514
- { method: 'GET', path: 'users/list' },
579
+ { method: 'GET', path: 'users/list', query: { role: 'admin' } },
515
580
  { method: 'GET', path: 'products/list' },
516
581
  { method: 'POST', path: 'orders/create', body: { ... } },
517
582
  ]);
@@ -571,3 +636,45 @@ src/
571
636
  ```
572
637
 
573
638
  Each `module/` folder is **self-contained** — the controller imports its own services directly, with no central declaration. `main.ts` only knows the lazy loading paths.
639
+
640
+ ---
641
+
642
+ ## Log level
643
+
644
+ By default, all framework logs are enabled (`debug` level). Control verbosity via `bootstrapApplication`:
645
+
646
+ ```ts
647
+ const noxApp = await bootstrapApplication({
648
+ logLevel: 'none', // 'debug' | 'info' | 'none'
649
+ });
650
+ ```
651
+
652
+ You can also pass an array of specific levels:
653
+
654
+ ```ts
655
+ bootstrapApplication({ logLevel: ['warn', 'error', 'critical'] });
656
+ ```
657
+
658
+ Or change it at runtime:
659
+
660
+ ```ts
661
+ import { Logger } from '@noxfly/noxus/main';
662
+
663
+ Logger.setLogLevel('info');
664
+ ```
665
+
666
+ ---
667
+
668
+ ## Testing
669
+
670
+ To reset the DI container between tests (avoids leaking singletons across test suites):
671
+
672
+ ```ts
673
+ import { resetRootInjector } from '@noxfly/noxus/main';
674
+
675
+ afterEach(() => {
676
+ resetRootInjector();
677
+ });
678
+ ```
679
+
680
+ This clears all bindings, singletons, and scoped instances from the root injector.
package/dist/child.d.mts CHANGED
@@ -109,6 +109,12 @@ declare class AppInjector {
109
109
  * The global root injector. All singletons live here.
110
110
  */
111
111
  declare const RootInjector: AppInjector;
112
+ /**
113
+ * Resets the root injector to a clean state.
114
+ * **Intended for testing only** — clears all bindings, singletons, and scoped instances
115
+ * so that each test can start from a fresh DI container without restarting the process.
116
+ */
117
+ declare function resetRootInjector(): void;
112
118
  /**
113
119
  * Convenience function: resolve a token from the root injector.
114
120
  */
@@ -340,4 +346,4 @@ declare namespace Logger {
340
346
  };
341
347
  }
342
348
 
343
- export { AppInjector, BadGatewayException, BadRequestException, ConflictException, ForbiddenException, type ForwardRefFn, ForwardReference, GatewayTimeoutException, HttpVersionNotSupportedException, type IBinding, Injectable, type InjectableOptions, InsufficientStorageException, InternalServerException, type Lifetime, type LogLevel, Logger, LoopDetectedException, type MaybeAsync, MethodNotAllowedException, NetworkAuthenticationRequiredException, NetworkConnectTimeoutException, NotAcceptableException, NotExtendedException, NotFoundException, NotImplementedException, PaymentRequiredException, RequestTimeoutException, ResponseException, RootInjector, ServiceUnavailableException, Token, type TokenKey, TooManyRequestsException, type Type, UnauthorizedException, UpgradeRequiredException, VariantAlsoNegotiatesException, forwardRef, inject };
349
+ export { AppInjector, BadGatewayException, BadRequestException, ConflictException, ForbiddenException, type ForwardRefFn, ForwardReference, GatewayTimeoutException, HttpVersionNotSupportedException, type IBinding, Injectable, type InjectableOptions, InsufficientStorageException, InternalServerException, type Lifetime, type LogLevel, Logger, LoopDetectedException, type MaybeAsync, MethodNotAllowedException, NetworkAuthenticationRequiredException, NetworkConnectTimeoutException, NotAcceptableException, NotExtendedException, NotFoundException, NotImplementedException, PaymentRequiredException, RequestTimeoutException, ResponseException, RootInjector, ServiceUnavailableException, Token, type TokenKey, TooManyRequestsException, type Type, UnauthorizedException, UpgradeRequiredException, VariantAlsoNegotiatesException, forwardRef, inject, resetRootInjector };
package/dist/child.d.ts CHANGED
@@ -109,6 +109,12 @@ declare class AppInjector {
109
109
  * The global root injector. All singletons live here.
110
110
  */
111
111
  declare const RootInjector: AppInjector;
112
+ /**
113
+ * Resets the root injector to a clean state.
114
+ * **Intended for testing only** — clears all bindings, singletons, and scoped instances
115
+ * so that each test can start from a fresh DI container without restarting the process.
116
+ */
117
+ declare function resetRootInjector(): void;
112
118
  /**
113
119
  * Convenience function: resolve a token from the root injector.
114
120
  */
@@ -340,4 +346,4 @@ declare namespace Logger {
340
346
  };
341
347
  }
342
348
 
343
- export { AppInjector, BadGatewayException, BadRequestException, ConflictException, ForbiddenException, type ForwardRefFn, ForwardReference, GatewayTimeoutException, HttpVersionNotSupportedException, type IBinding, Injectable, type InjectableOptions, InsufficientStorageException, InternalServerException, type Lifetime, type LogLevel, Logger, LoopDetectedException, type MaybeAsync, MethodNotAllowedException, NetworkAuthenticationRequiredException, NetworkConnectTimeoutException, NotAcceptableException, NotExtendedException, NotFoundException, NotImplementedException, PaymentRequiredException, RequestTimeoutException, ResponseException, RootInjector, ServiceUnavailableException, Token, type TokenKey, TooManyRequestsException, type Type, UnauthorizedException, UpgradeRequiredException, VariantAlsoNegotiatesException, forwardRef, inject };
349
+ export { AppInjector, BadGatewayException, BadRequestException, ConflictException, ForbiddenException, type ForwardRefFn, ForwardReference, GatewayTimeoutException, HttpVersionNotSupportedException, type IBinding, Injectable, type InjectableOptions, InsufficientStorageException, InternalServerException, type Lifetime, type LogLevel, Logger, LoopDetectedException, type MaybeAsync, MethodNotAllowedException, NetworkAuthenticationRequiredException, NetworkConnectTimeoutException, NotAcceptableException, NotExtendedException, NotFoundException, NotImplementedException, PaymentRequiredException, RequestTimeoutException, ResponseException, RootInjector, ServiceUnavailableException, Token, type TokenKey, TooManyRequestsException, type Type, UnauthorizedException, UpgradeRequiredException, VariantAlsoNegotiatesException, forwardRef, inject, resetRootInjector };