@abdokouta/ts-application 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Refine
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.cjs ADDED
@@ -0,0 +1,199 @@
1
+ 'use strict';
2
+
3
+ var tsContainer = require('@abdokouta/ts-container');
4
+
5
+ // src/application-context.ts
6
+ var ApplicationContext = class _ApplicationContext {
7
+ container;
8
+ instanceLoader;
9
+ isInitialized = false;
10
+ constructor(container, instanceLoader) {
11
+ this.container = container;
12
+ this.instanceLoader = instanceLoader;
13
+ }
14
+ // ─────────────────────────────────────────────────────────────────────────
15
+ // Static factory
16
+ // ─────────────────────────────────────────────────────────────────────────
17
+ /**
18
+ * Create and bootstrap an application context.
19
+ *
20
+ * This is the main entry point. It:
21
+ * 1. Scans the module tree starting from the root module
22
+ * 2. Resolves all providers (creates instances, injects dependencies)
23
+ * 3. Calls `onModuleInit()` lifecycle hooks
24
+ *
25
+ * @param rootModule - The root module class (your AppModule)
26
+ * @returns A fully bootstrapped ApplicationContext
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * import { ApplicationContext } from '@pixielity/application';
31
+ * import { AppModule } from './app.module';
32
+ *
33
+ * const app = await ApplicationContext.create(AppModule);
34
+ *
35
+ * const userService = app.get(UserService);
36
+ * const config = app.get<CacheConfig>(CACHE_CONFIG);
37
+ * ```
38
+ */
39
+ static async create(rootModule) {
40
+ const container = new tsContainer.NestContainer();
41
+ const scanner = new tsContainer.DependenciesScanner(container);
42
+ const instanceLoader = new tsContainer.InstanceLoader(container);
43
+ await scanner.scan(rootModule);
44
+ await instanceLoader.createInstances();
45
+ const app = new _ApplicationContext(container, instanceLoader);
46
+ app.isInitialized = true;
47
+ return app;
48
+ }
49
+ // ─────────────────────────────────────────────────────────────────────────
50
+ // ContainerResolver interface (used by @abdokouta/ts-container/react)
51
+ // ─────────────────────────────────────────────────────────────────────────
52
+ /**
53
+ * Resolve a provider by its injection token.
54
+ *
55
+ * Searches all modules for the provider. For singleton providers,
56
+ * returns the cached instance.
57
+ *
58
+ * @param token - The injection token (class, string, or symbol)
59
+ * @returns The resolved provider instance
60
+ * @throws Error if the provider is not found
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * const userService = app.get(UserService);
65
+ * const config = app.get<CacheConfig>(CACHE_CONFIG);
66
+ * const apiUrl = app.get<string>('API_URL');
67
+ * ```
68
+ */
69
+ get(token) {
70
+ this.assertInitialized();
71
+ for (const [, moduleRef] of this.container.getModules()) {
72
+ const wrapper = moduleRef.providers.get(token);
73
+ if (!wrapper) continue;
74
+ if (wrapper.isResolved && !wrapper.isTransient) {
75
+ return wrapper.instance;
76
+ }
77
+ if (wrapper.isTransient && wrapper.metatype) {
78
+ return this.instantiateTransient(wrapper, moduleRef);
79
+ }
80
+ if (wrapper.isTransient && wrapper.instance !== null) {
81
+ return wrapper.instance;
82
+ }
83
+ }
84
+ throw new Error(
85
+ `Provider '${this.getTokenName(token)}' not found in any module. Make sure it is provided in a module that has been imported.`
86
+ );
87
+ }
88
+ /**
89
+ * Try to resolve a provider, returning `undefined` if not found.
90
+ *
91
+ * @param token - The injection token
92
+ * @returns The resolved instance or undefined
93
+ */
94
+ getOptional(token) {
95
+ try {
96
+ return this.get(token);
97
+ } catch {
98
+ return void 0;
99
+ }
100
+ }
101
+ /**
102
+ * Check if a provider is registered in any module.
103
+ *
104
+ * @param token - The injection token to check
105
+ */
106
+ has(token) {
107
+ for (const [, moduleRef] of this.container.getModules()) {
108
+ if (moduleRef.providers.has(token)) return true;
109
+ }
110
+ return false;
111
+ }
112
+ // ─────────────────────────────────────────────────────────────────────────
113
+ // Advanced API
114
+ // ─────────────────────────────────────────────────────────────────────────
115
+ /**
116
+ * Select a specific module and resolve a provider from it.
117
+ *
118
+ * Useful when the same token exists in multiple modules.
119
+ *
120
+ * @param moduleClass - The module class to search in
121
+ * @param token - The injection token
122
+ */
123
+ select(moduleClass, token) {
124
+ this.assertInitialized();
125
+ for (const [, moduleRef] of this.container.getModules()) {
126
+ if (moduleRef.metatype === moduleClass) {
127
+ const wrapper = moduleRef.providers.get(token);
128
+ if (wrapper?.isResolved) {
129
+ return wrapper.instance;
130
+ }
131
+ throw new Error(
132
+ `Provider '${this.getTokenName(token)}' not found in module '${moduleClass.name}'.`
133
+ );
134
+ }
135
+ }
136
+ throw new Error(`Module '${moduleClass.name}' not found in the container.`);
137
+ }
138
+ /**
139
+ * Get the underlying NestContainer (for advanced use cases).
140
+ */
141
+ getContainer() {
142
+ return this.container;
143
+ }
144
+ /**
145
+ * Gracefully shut down the application.
146
+ *
147
+ * Calls `onModuleDestroy()` on all providers that implement it,
148
+ * in reverse module order (leaf modules first).
149
+ */
150
+ async close() {
151
+ await this.instanceLoader.destroy();
152
+ this.isInitialized = false;
153
+ }
154
+ // ─────────────────────────────────────────────────────────────────────────
155
+ // Private
156
+ // ─────────────────────────────────────────────────────────────────────────
157
+ assertInitialized() {
158
+ if (!this.isInitialized) {
159
+ throw new Error(
160
+ "ApplicationContext is not initialized. Call ApplicationContext.create() first."
161
+ );
162
+ }
163
+ }
164
+ getTokenName(token) {
165
+ if (typeof token === "function") return token.name;
166
+ if (typeof token === "symbol") return token.toString();
167
+ return String(token);
168
+ }
169
+ /**
170
+ * Synchronously instantiate a transient provider.
171
+ *
172
+ * After bootstrap, all dependencies of a transient provider are already
173
+ * resolved singletons. So we can synchronously look them up and call
174
+ * `new Class(...deps)` without awaiting anything.
175
+ */
176
+ instantiateTransient(wrapper, moduleRef) {
177
+ const injector = this.instanceLoader.getInjector();
178
+ const metatype = wrapper.metatype;
179
+ const deps = injector.getConstructorDependencies(metatype);
180
+ const optionalIndices = injector.getOptionalDependencies(metatype);
181
+ const resolvedDeps = deps.map((dep, index) => {
182
+ if (dep === void 0 || dep === null || dep === Object) {
183
+ if (optionalIndices.includes(index)) return void 0;
184
+ return void 0;
185
+ }
186
+ const result = injector.lookupProvider(dep, moduleRef);
187
+ if (!result) {
188
+ if (optionalIndices.includes(index)) return void 0;
189
+ throw new Error(`Cannot resolve transient dependency '${this.getTokenName(dep)}'`);
190
+ }
191
+ return result.wrapper.instance;
192
+ });
193
+ return new metatype(...resolvedDeps);
194
+ }
195
+ };
196
+
197
+ exports.ApplicationContext = ApplicationContext;
198
+ //# sourceMappingURL=index.cjs.map
199
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/application-context.ts"],"names":["NestContainer","DependenciesScanner","InstanceLoader"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @fileoverview ApplicationContext — the main entry point for bootstrapping the DI system.\n *\n * This is the equivalent of NestJS's `NestFactory.createApplicationContext()`.\n * It orchestrates the full bootstrap sequence:\n *\n * 1. **Scan** — Walk the module tree, register all modules/providers/imports/exports\n * 2. **Instantiate** — Resolve all providers (create instances, inject dependencies)\n * 3. **Lifecycle** — Call `onModuleInit()` on providers that implement it\n *\n * After bootstrap, the ApplicationContext provides `get()` to resolve any provider.\n *\n * ## Usage:\n *\n * ```typescript\n * import { ApplicationContext } from '@pixielity/application';\n * import { AppModule } from './app.module';\n *\n * // Bootstrap\n * const app = await ApplicationContext.create(AppModule);\n *\n * // Resolve providers\n * const cache = app.get(CacheManager);\n * const config = app.get<AppConfig>(APP_CONFIG);\n *\n * // Shutdown\n * await app.close();\n * ```\n *\n * ## With React:\n *\n * ```tsx\n * import { ContainerProvider } from '@abdokouta/ts-container/react';\n *\n * const app = await ApplicationContext.create(AppModule);\n *\n * ReactDOM.createRoot(root).render(\n * <ContainerProvider context={app}>\n * <App />\n * </ContainerProvider>\n * );\n * ```\n *\n * The ApplicationContext implements the `ContainerResolver` interface from\n * `@abdokouta/ts-container/react`, so it can be passed directly to `ContainerProvider`.\n *\n * @module application-context\n */\n\nimport type { Type, InjectionToken } from '@abdokouta/ts-container';\nimport {\n NestContainer,\n DependenciesScanner,\n InstanceLoader,\n ModuleRef,\n} from '@abdokouta/ts-container';\nimport type { IApplicationContext } from './interfaces/application-context.interface';\n\n/**\n * The bootstrapped application context.\n *\n * Provides access to the DI container after all modules have been\n * scanned and all providers have been instantiated.\n *\n * Implements `IApplicationContext` (which extends `ContainerResolver`)\n * so it can be used directly with `<ContainerProvider context={app}>`\n * from `@abdokouta/ts-container/react`.\n */\nexport class ApplicationContext implements IApplicationContext {\n private readonly container: NestContainer;\n private readonly instanceLoader: InstanceLoader;\n private isInitialized = false;\n\n private constructor(container: NestContainer, instanceLoader: InstanceLoader) {\n this.container = container;\n this.instanceLoader = instanceLoader;\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Static factory\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Create and bootstrap an application context.\n *\n * This is the main entry point. It:\n * 1. Scans the module tree starting from the root module\n * 2. Resolves all providers (creates instances, injects dependencies)\n * 3. Calls `onModuleInit()` lifecycle hooks\n *\n * @param rootModule - The root module class (your AppModule)\n * @returns A fully bootstrapped ApplicationContext\n *\n * @example\n * ```typescript\n * import { ApplicationContext } from '@pixielity/application';\n * import { AppModule } from './app.module';\n *\n * const app = await ApplicationContext.create(AppModule);\n *\n * const userService = app.get(UserService);\n * const config = app.get<CacheConfig>(CACHE_CONFIG);\n * ```\n */\n public static async create(rootModule: Type<any>): Promise<ApplicationContext> {\n const container = new NestContainer();\n const scanner = new DependenciesScanner(container);\n const instanceLoader = new InstanceLoader(container);\n\n // Phase 1: Scan the module tree\n await scanner.scan(rootModule);\n\n // Phase 2: Create all provider instances\n await instanceLoader.createInstances();\n\n const app = new ApplicationContext(container, instanceLoader);\n app.isInitialized = true;\n\n return app;\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // ContainerResolver interface (used by @abdokouta/ts-container/react)\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Resolve a provider by its injection token.\n *\n * Searches all modules for the provider. For singleton providers,\n * returns the cached instance.\n *\n * @param token - The injection token (class, string, or symbol)\n * @returns The resolved provider instance\n * @throws Error if the provider is not found\n *\n * @example\n * ```typescript\n * const userService = app.get(UserService);\n * const config = app.get<CacheConfig>(CACHE_CONFIG);\n * const apiUrl = app.get<string>('API_URL');\n * ```\n */\n public get<T = any>(token: InjectionToken<T>): T {\n this.assertInitialized();\n\n for (const [, moduleRef] of this.container.getModules()) {\n const wrapper = moduleRef.providers.get(token);\n if (!wrapper) continue;\n\n // Singleton or value provider — return cached instance\n if (wrapper.isResolved && !wrapper.isTransient) {\n return wrapper.instance as T;\n }\n\n // Transient provider — create a fresh instance each time.\n // All dependencies should already be resolved after bootstrap,\n // so we can synchronously instantiate the class.\n if (wrapper.isTransient && wrapper.metatype) {\n return this.instantiateTransient<T>(wrapper, moduleRef);\n }\n\n // Transient provider that was resolved during bootstrap\n // (has a cached instance from the initial resolution)\n if (wrapper.isTransient && wrapper.instance !== null) {\n return wrapper.instance as T;\n }\n }\n\n throw new Error(\n `Provider '${this.getTokenName(token)}' not found in any module. ` +\n `Make sure it is provided in a module that has been imported.`,\n );\n }\n\n /**\n * Try to resolve a provider, returning `undefined` if not found.\n *\n * @param token - The injection token\n * @returns The resolved instance or undefined\n */\n public getOptional<T = any>(token: InjectionToken<T>): T | undefined {\n try {\n return this.get(token);\n } catch {\n return undefined;\n }\n }\n\n /**\n * Check if a provider is registered in any module.\n *\n * @param token - The injection token to check\n */\n public has(token: InjectionToken): boolean {\n for (const [, moduleRef] of this.container.getModules()) {\n if (moduleRef.providers.has(token)) return true;\n }\n return false;\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Advanced API\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Select a specific module and resolve a provider from it.\n *\n * Useful when the same token exists in multiple modules.\n *\n * @param moduleClass - The module class to search in\n * @param token - The injection token\n */\n public select<T = any>(moduleClass: Type<any>, token: InjectionToken<T>): T {\n this.assertInitialized();\n\n for (const [, moduleRef] of this.container.getModules()) {\n if (moduleRef.metatype === moduleClass) {\n const wrapper = moduleRef.providers.get(token);\n if (wrapper?.isResolved) {\n return wrapper.instance as T;\n }\n throw new Error(\n `Provider '${this.getTokenName(token)}' not found in module '${moduleClass.name}'.`,\n );\n }\n }\n\n throw new Error(`Module '${moduleClass.name}' not found in the container.`);\n }\n\n /**\n * Get the underlying NestContainer (for advanced use cases).\n */\n public getContainer(): NestContainer {\n return this.container;\n }\n\n /**\n * Gracefully shut down the application.\n *\n * Calls `onModuleDestroy()` on all providers that implement it,\n * in reverse module order (leaf modules first).\n */\n public async close(): Promise<void> {\n await this.instanceLoader.destroy();\n this.isInitialized = false;\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Private\n // ─────────────────────────────────────────────────────────────────────────\n\n private assertInitialized(): void {\n if (!this.isInitialized) {\n throw new Error(\n 'ApplicationContext is not initialized. Call ApplicationContext.create() first.',\n );\n }\n }\n\n private getTokenName(token: InjectionToken): string {\n if (typeof token === 'function') return token.name;\n if (typeof token === 'symbol') return token.toString();\n return String(token);\n }\n\n /**\n * Synchronously instantiate a transient provider.\n *\n * After bootstrap, all dependencies of a transient provider are already\n * resolved singletons. So we can synchronously look them up and call\n * `new Class(...deps)` without awaiting anything.\n */\n private instantiateTransient<T>(wrapper: any, moduleRef: ModuleRef): T {\n const injector = this.instanceLoader.getInjector();\n\n // Look up constructor dependencies — they should all be resolved singletons\n const metatype = wrapper.metatype;\n const deps = (injector as any).getConstructorDependencies(metatype);\n const optionalIndices: number[] = (injector as any).getOptionalDependencies(metatype);\n\n const resolvedDeps = deps.map((dep: InjectionToken, index: number) => {\n if (dep === undefined || dep === null || dep === Object) {\n if (optionalIndices.includes(index)) return undefined;\n return undefined;\n }\n\n const result = injector.lookupProvider(dep, moduleRef);\n if (!result) {\n if (optionalIndices.includes(index)) return undefined;\n throw new Error(`Cannot resolve transient dependency '${this.getTokenName(dep)}'`);\n }\n\n return result.wrapper.instance;\n });\n\n return new metatype(...resolvedDeps);\n }\n}\n"]}
@@ -0,0 +1,265 @@
1
+ import { ContainerResolver, InjectionToken, Type, NestContainer } from '@abdokouta/ts-container';
2
+
3
+ /**
4
+ * @fileoverview IApplicationContext — interface for the bootstrapped application.
5
+ *
6
+ * Defines the public API of the ApplicationContext. Extends `ContainerResolver`
7
+ * (from `@abdokouta/ts-container`) with additional methods for module selection,
8
+ * container access, and graceful shutdown.
9
+ *
10
+ * ## Why an interface?
11
+ *
12
+ * - Enables mocking in tests without importing the concrete class
13
+ * - Documents the public API contract clearly
14
+ * - Allows alternative implementations (e.g., a test context, a lazy context)
15
+ *
16
+ * @module interfaces/application-context
17
+ */
18
+
19
+ /**
20
+ * The public API of a bootstrapped application context.
21
+ *
22
+ * Extends `ContainerResolver` with:
23
+ * - `select()` — resolve from a specific module
24
+ * - `getContainer()` — access the raw container
25
+ * - `close()` — graceful shutdown with lifecycle hooks
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * // Type your variable as the interface for testability
30
+ * let app: IApplicationContext;
31
+ *
32
+ * beforeAll(async () => {
33
+ * app = await ApplicationContext.create(AppModule);
34
+ * });
35
+ *
36
+ * afterAll(async () => {
37
+ * await app.close();
38
+ * });
39
+ *
40
+ * test('resolves UserService', () => {
41
+ * const userService = app.get(UserService);
42
+ * expect(userService).toBeDefined();
43
+ * });
44
+ * ```
45
+ */
46
+ interface IApplicationContext extends ContainerResolver {
47
+ /**
48
+ * Resolve a provider by its injection token.
49
+ *
50
+ * Searches all modules for the provider. For singleton providers,
51
+ * returns the cached instance.
52
+ *
53
+ * @param token - The injection token (class, string, or symbol)
54
+ * @returns The resolved provider instance
55
+ * @throws Error if the provider is not found
56
+ */
57
+ get<T = any>(token: InjectionToken<T>): T;
58
+ /**
59
+ * Try to resolve a provider, returning `undefined` if not found.
60
+ *
61
+ * @param token - The injection token
62
+ * @returns The resolved instance or undefined
63
+ */
64
+ getOptional<T = any>(token: InjectionToken<T>): T | undefined;
65
+ /**
66
+ * Check if a provider is registered in any module.
67
+ *
68
+ * @param token - The injection token to check
69
+ */
70
+ has(token: InjectionToken): boolean;
71
+ /**
72
+ * Select a specific module and resolve a provider from it.
73
+ *
74
+ * Useful when the same token exists in multiple modules and you
75
+ * need to specify which one to resolve from.
76
+ *
77
+ * @param moduleClass - The module class to search in
78
+ * @param token - The injection token
79
+ * @returns The resolved provider instance
80
+ * @throws Error if the module or provider is not found
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * // Resolve CacheManager specifically from CacheModule
85
+ * const cache = app.select(CacheModule, CacheManager);
86
+ * ```
87
+ */
88
+ select<T = any>(moduleClass: Type<any>, token: InjectionToken<T>): T;
89
+ /**
90
+ * Get the underlying NestContainer.
91
+ *
92
+ * For advanced use cases like inspecting the module graph,
93
+ * accessing raw InstanceWrappers, or building dev tools.
94
+ */
95
+ getContainer(): NestContainer;
96
+ /**
97
+ * Gracefully shut down the application.
98
+ *
99
+ * Calls `onModuleDestroy()` on all providers that implement it,
100
+ * in reverse module order (leaf modules first, root module last).
101
+ *
102
+ * After calling `close()`, the context is no longer usable.
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * // In your app's cleanup logic
107
+ * window.addEventListener('beforeunload', () => {
108
+ * app.close();
109
+ * });
110
+ * ```
111
+ */
112
+ close(): Promise<void>;
113
+ }
114
+
115
+ /**
116
+ * @fileoverview ApplicationContext — the main entry point for bootstrapping the DI system.
117
+ *
118
+ * This is the equivalent of NestJS's `NestFactory.createApplicationContext()`.
119
+ * It orchestrates the full bootstrap sequence:
120
+ *
121
+ * 1. **Scan** — Walk the module tree, register all modules/providers/imports/exports
122
+ * 2. **Instantiate** — Resolve all providers (create instances, inject dependencies)
123
+ * 3. **Lifecycle** — Call `onModuleInit()` on providers that implement it
124
+ *
125
+ * After bootstrap, the ApplicationContext provides `get()` to resolve any provider.
126
+ *
127
+ * ## Usage:
128
+ *
129
+ * ```typescript
130
+ * import { ApplicationContext } from '@pixielity/application';
131
+ * import { AppModule } from './app.module';
132
+ *
133
+ * // Bootstrap
134
+ * const app = await ApplicationContext.create(AppModule);
135
+ *
136
+ * // Resolve providers
137
+ * const cache = app.get(CacheManager);
138
+ * const config = app.get<AppConfig>(APP_CONFIG);
139
+ *
140
+ * // Shutdown
141
+ * await app.close();
142
+ * ```
143
+ *
144
+ * ## With React:
145
+ *
146
+ * ```tsx
147
+ * import { ContainerProvider } from '@abdokouta/ts-container/react';
148
+ *
149
+ * const app = await ApplicationContext.create(AppModule);
150
+ *
151
+ * ReactDOM.createRoot(root).render(
152
+ * <ContainerProvider context={app}>
153
+ * <App />
154
+ * </ContainerProvider>
155
+ * );
156
+ * ```
157
+ *
158
+ * The ApplicationContext implements the `ContainerResolver` interface from
159
+ * `@abdokouta/ts-container/react`, so it can be passed directly to `ContainerProvider`.
160
+ *
161
+ * @module application-context
162
+ */
163
+
164
+ /**
165
+ * The bootstrapped application context.
166
+ *
167
+ * Provides access to the DI container after all modules have been
168
+ * scanned and all providers have been instantiated.
169
+ *
170
+ * Implements `IApplicationContext` (which extends `ContainerResolver`)
171
+ * so it can be used directly with `<ContainerProvider context={app}>`
172
+ * from `@abdokouta/ts-container/react`.
173
+ */
174
+ declare class ApplicationContext implements IApplicationContext {
175
+ private readonly container;
176
+ private readonly instanceLoader;
177
+ private isInitialized;
178
+ private constructor();
179
+ /**
180
+ * Create and bootstrap an application context.
181
+ *
182
+ * This is the main entry point. It:
183
+ * 1. Scans the module tree starting from the root module
184
+ * 2. Resolves all providers (creates instances, injects dependencies)
185
+ * 3. Calls `onModuleInit()` lifecycle hooks
186
+ *
187
+ * @param rootModule - The root module class (your AppModule)
188
+ * @returns A fully bootstrapped ApplicationContext
189
+ *
190
+ * @example
191
+ * ```typescript
192
+ * import { ApplicationContext } from '@pixielity/application';
193
+ * import { AppModule } from './app.module';
194
+ *
195
+ * const app = await ApplicationContext.create(AppModule);
196
+ *
197
+ * const userService = app.get(UserService);
198
+ * const config = app.get<CacheConfig>(CACHE_CONFIG);
199
+ * ```
200
+ */
201
+ static create(rootModule: Type<any>): Promise<ApplicationContext>;
202
+ /**
203
+ * Resolve a provider by its injection token.
204
+ *
205
+ * Searches all modules for the provider. For singleton providers,
206
+ * returns the cached instance.
207
+ *
208
+ * @param token - The injection token (class, string, or symbol)
209
+ * @returns The resolved provider instance
210
+ * @throws Error if the provider is not found
211
+ *
212
+ * @example
213
+ * ```typescript
214
+ * const userService = app.get(UserService);
215
+ * const config = app.get<CacheConfig>(CACHE_CONFIG);
216
+ * const apiUrl = app.get<string>('API_URL');
217
+ * ```
218
+ */
219
+ get<T = any>(token: InjectionToken<T>): T;
220
+ /**
221
+ * Try to resolve a provider, returning `undefined` if not found.
222
+ *
223
+ * @param token - The injection token
224
+ * @returns The resolved instance or undefined
225
+ */
226
+ getOptional<T = any>(token: InjectionToken<T>): T | undefined;
227
+ /**
228
+ * Check if a provider is registered in any module.
229
+ *
230
+ * @param token - The injection token to check
231
+ */
232
+ has(token: InjectionToken): boolean;
233
+ /**
234
+ * Select a specific module and resolve a provider from it.
235
+ *
236
+ * Useful when the same token exists in multiple modules.
237
+ *
238
+ * @param moduleClass - The module class to search in
239
+ * @param token - The injection token
240
+ */
241
+ select<T = any>(moduleClass: Type<any>, token: InjectionToken<T>): T;
242
+ /**
243
+ * Get the underlying NestContainer (for advanced use cases).
244
+ */
245
+ getContainer(): NestContainer;
246
+ /**
247
+ * Gracefully shut down the application.
248
+ *
249
+ * Calls `onModuleDestroy()` on all providers that implement it,
250
+ * in reverse module order (leaf modules first).
251
+ */
252
+ close(): Promise<void>;
253
+ private assertInitialized;
254
+ private getTokenName;
255
+ /**
256
+ * Synchronously instantiate a transient provider.
257
+ *
258
+ * After bootstrap, all dependencies of a transient provider are already
259
+ * resolved singletons. So we can synchronously look them up and call
260
+ * `new Class(...deps)` without awaiting anything.
261
+ */
262
+ private instantiateTransient;
263
+ }
264
+
265
+ export { ApplicationContext, type IApplicationContext };
@@ -0,0 +1,265 @@
1
+ import { ContainerResolver, InjectionToken, Type, NestContainer } from '@abdokouta/ts-container';
2
+
3
+ /**
4
+ * @fileoverview IApplicationContext — interface for the bootstrapped application.
5
+ *
6
+ * Defines the public API of the ApplicationContext. Extends `ContainerResolver`
7
+ * (from `@abdokouta/ts-container`) with additional methods for module selection,
8
+ * container access, and graceful shutdown.
9
+ *
10
+ * ## Why an interface?
11
+ *
12
+ * - Enables mocking in tests without importing the concrete class
13
+ * - Documents the public API contract clearly
14
+ * - Allows alternative implementations (e.g., a test context, a lazy context)
15
+ *
16
+ * @module interfaces/application-context
17
+ */
18
+
19
+ /**
20
+ * The public API of a bootstrapped application context.
21
+ *
22
+ * Extends `ContainerResolver` with:
23
+ * - `select()` — resolve from a specific module
24
+ * - `getContainer()` — access the raw container
25
+ * - `close()` — graceful shutdown with lifecycle hooks
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * // Type your variable as the interface for testability
30
+ * let app: IApplicationContext;
31
+ *
32
+ * beforeAll(async () => {
33
+ * app = await ApplicationContext.create(AppModule);
34
+ * });
35
+ *
36
+ * afterAll(async () => {
37
+ * await app.close();
38
+ * });
39
+ *
40
+ * test('resolves UserService', () => {
41
+ * const userService = app.get(UserService);
42
+ * expect(userService).toBeDefined();
43
+ * });
44
+ * ```
45
+ */
46
+ interface IApplicationContext extends ContainerResolver {
47
+ /**
48
+ * Resolve a provider by its injection token.
49
+ *
50
+ * Searches all modules for the provider. For singleton providers,
51
+ * returns the cached instance.
52
+ *
53
+ * @param token - The injection token (class, string, or symbol)
54
+ * @returns The resolved provider instance
55
+ * @throws Error if the provider is not found
56
+ */
57
+ get<T = any>(token: InjectionToken<T>): T;
58
+ /**
59
+ * Try to resolve a provider, returning `undefined` if not found.
60
+ *
61
+ * @param token - The injection token
62
+ * @returns The resolved instance or undefined
63
+ */
64
+ getOptional<T = any>(token: InjectionToken<T>): T | undefined;
65
+ /**
66
+ * Check if a provider is registered in any module.
67
+ *
68
+ * @param token - The injection token to check
69
+ */
70
+ has(token: InjectionToken): boolean;
71
+ /**
72
+ * Select a specific module and resolve a provider from it.
73
+ *
74
+ * Useful when the same token exists in multiple modules and you
75
+ * need to specify which one to resolve from.
76
+ *
77
+ * @param moduleClass - The module class to search in
78
+ * @param token - The injection token
79
+ * @returns The resolved provider instance
80
+ * @throws Error if the module or provider is not found
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * // Resolve CacheManager specifically from CacheModule
85
+ * const cache = app.select(CacheModule, CacheManager);
86
+ * ```
87
+ */
88
+ select<T = any>(moduleClass: Type<any>, token: InjectionToken<T>): T;
89
+ /**
90
+ * Get the underlying NestContainer.
91
+ *
92
+ * For advanced use cases like inspecting the module graph,
93
+ * accessing raw InstanceWrappers, or building dev tools.
94
+ */
95
+ getContainer(): NestContainer;
96
+ /**
97
+ * Gracefully shut down the application.
98
+ *
99
+ * Calls `onModuleDestroy()` on all providers that implement it,
100
+ * in reverse module order (leaf modules first, root module last).
101
+ *
102
+ * After calling `close()`, the context is no longer usable.
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * // In your app's cleanup logic
107
+ * window.addEventListener('beforeunload', () => {
108
+ * app.close();
109
+ * });
110
+ * ```
111
+ */
112
+ close(): Promise<void>;
113
+ }
114
+
115
+ /**
116
+ * @fileoverview ApplicationContext — the main entry point for bootstrapping the DI system.
117
+ *
118
+ * This is the equivalent of NestJS's `NestFactory.createApplicationContext()`.
119
+ * It orchestrates the full bootstrap sequence:
120
+ *
121
+ * 1. **Scan** — Walk the module tree, register all modules/providers/imports/exports
122
+ * 2. **Instantiate** — Resolve all providers (create instances, inject dependencies)
123
+ * 3. **Lifecycle** — Call `onModuleInit()` on providers that implement it
124
+ *
125
+ * After bootstrap, the ApplicationContext provides `get()` to resolve any provider.
126
+ *
127
+ * ## Usage:
128
+ *
129
+ * ```typescript
130
+ * import { ApplicationContext } from '@pixielity/application';
131
+ * import { AppModule } from './app.module';
132
+ *
133
+ * // Bootstrap
134
+ * const app = await ApplicationContext.create(AppModule);
135
+ *
136
+ * // Resolve providers
137
+ * const cache = app.get(CacheManager);
138
+ * const config = app.get<AppConfig>(APP_CONFIG);
139
+ *
140
+ * // Shutdown
141
+ * await app.close();
142
+ * ```
143
+ *
144
+ * ## With React:
145
+ *
146
+ * ```tsx
147
+ * import { ContainerProvider } from '@abdokouta/ts-container/react';
148
+ *
149
+ * const app = await ApplicationContext.create(AppModule);
150
+ *
151
+ * ReactDOM.createRoot(root).render(
152
+ * <ContainerProvider context={app}>
153
+ * <App />
154
+ * </ContainerProvider>
155
+ * );
156
+ * ```
157
+ *
158
+ * The ApplicationContext implements the `ContainerResolver` interface from
159
+ * `@abdokouta/ts-container/react`, so it can be passed directly to `ContainerProvider`.
160
+ *
161
+ * @module application-context
162
+ */
163
+
164
+ /**
165
+ * The bootstrapped application context.
166
+ *
167
+ * Provides access to the DI container after all modules have been
168
+ * scanned and all providers have been instantiated.
169
+ *
170
+ * Implements `IApplicationContext` (which extends `ContainerResolver`)
171
+ * so it can be used directly with `<ContainerProvider context={app}>`
172
+ * from `@abdokouta/ts-container/react`.
173
+ */
174
+ declare class ApplicationContext implements IApplicationContext {
175
+ private readonly container;
176
+ private readonly instanceLoader;
177
+ private isInitialized;
178
+ private constructor();
179
+ /**
180
+ * Create and bootstrap an application context.
181
+ *
182
+ * This is the main entry point. It:
183
+ * 1. Scans the module tree starting from the root module
184
+ * 2. Resolves all providers (creates instances, injects dependencies)
185
+ * 3. Calls `onModuleInit()` lifecycle hooks
186
+ *
187
+ * @param rootModule - The root module class (your AppModule)
188
+ * @returns A fully bootstrapped ApplicationContext
189
+ *
190
+ * @example
191
+ * ```typescript
192
+ * import { ApplicationContext } from '@pixielity/application';
193
+ * import { AppModule } from './app.module';
194
+ *
195
+ * const app = await ApplicationContext.create(AppModule);
196
+ *
197
+ * const userService = app.get(UserService);
198
+ * const config = app.get<CacheConfig>(CACHE_CONFIG);
199
+ * ```
200
+ */
201
+ static create(rootModule: Type<any>): Promise<ApplicationContext>;
202
+ /**
203
+ * Resolve a provider by its injection token.
204
+ *
205
+ * Searches all modules for the provider. For singleton providers,
206
+ * returns the cached instance.
207
+ *
208
+ * @param token - The injection token (class, string, or symbol)
209
+ * @returns The resolved provider instance
210
+ * @throws Error if the provider is not found
211
+ *
212
+ * @example
213
+ * ```typescript
214
+ * const userService = app.get(UserService);
215
+ * const config = app.get<CacheConfig>(CACHE_CONFIG);
216
+ * const apiUrl = app.get<string>('API_URL');
217
+ * ```
218
+ */
219
+ get<T = any>(token: InjectionToken<T>): T;
220
+ /**
221
+ * Try to resolve a provider, returning `undefined` if not found.
222
+ *
223
+ * @param token - The injection token
224
+ * @returns The resolved instance or undefined
225
+ */
226
+ getOptional<T = any>(token: InjectionToken<T>): T | undefined;
227
+ /**
228
+ * Check if a provider is registered in any module.
229
+ *
230
+ * @param token - The injection token to check
231
+ */
232
+ has(token: InjectionToken): boolean;
233
+ /**
234
+ * Select a specific module and resolve a provider from it.
235
+ *
236
+ * Useful when the same token exists in multiple modules.
237
+ *
238
+ * @param moduleClass - The module class to search in
239
+ * @param token - The injection token
240
+ */
241
+ select<T = any>(moduleClass: Type<any>, token: InjectionToken<T>): T;
242
+ /**
243
+ * Get the underlying NestContainer (for advanced use cases).
244
+ */
245
+ getContainer(): NestContainer;
246
+ /**
247
+ * Gracefully shut down the application.
248
+ *
249
+ * Calls `onModuleDestroy()` on all providers that implement it,
250
+ * in reverse module order (leaf modules first).
251
+ */
252
+ close(): Promise<void>;
253
+ private assertInitialized;
254
+ private getTokenName;
255
+ /**
256
+ * Synchronously instantiate a transient provider.
257
+ *
258
+ * After bootstrap, all dependencies of a transient provider are already
259
+ * resolved singletons. So we can synchronously look them up and call
260
+ * `new Class(...deps)` without awaiting anything.
261
+ */
262
+ private instantiateTransient;
263
+ }
264
+
265
+ export { ApplicationContext, type IApplicationContext };
package/dist/index.js ADDED
@@ -0,0 +1,197 @@
1
+ import { NestContainer, DependenciesScanner, InstanceLoader } from '@abdokouta/ts-container';
2
+
3
+ // src/application-context.ts
4
+ var ApplicationContext = class _ApplicationContext {
5
+ container;
6
+ instanceLoader;
7
+ isInitialized = false;
8
+ constructor(container, instanceLoader) {
9
+ this.container = container;
10
+ this.instanceLoader = instanceLoader;
11
+ }
12
+ // ─────────────────────────────────────────────────────────────────────────
13
+ // Static factory
14
+ // ─────────────────────────────────────────────────────────────────────────
15
+ /**
16
+ * Create and bootstrap an application context.
17
+ *
18
+ * This is the main entry point. It:
19
+ * 1. Scans the module tree starting from the root module
20
+ * 2. Resolves all providers (creates instances, injects dependencies)
21
+ * 3. Calls `onModuleInit()` lifecycle hooks
22
+ *
23
+ * @param rootModule - The root module class (your AppModule)
24
+ * @returns A fully bootstrapped ApplicationContext
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * import { ApplicationContext } from '@pixielity/application';
29
+ * import { AppModule } from './app.module';
30
+ *
31
+ * const app = await ApplicationContext.create(AppModule);
32
+ *
33
+ * const userService = app.get(UserService);
34
+ * const config = app.get<CacheConfig>(CACHE_CONFIG);
35
+ * ```
36
+ */
37
+ static async create(rootModule) {
38
+ const container = new NestContainer();
39
+ const scanner = new DependenciesScanner(container);
40
+ const instanceLoader = new InstanceLoader(container);
41
+ await scanner.scan(rootModule);
42
+ await instanceLoader.createInstances();
43
+ const app = new _ApplicationContext(container, instanceLoader);
44
+ app.isInitialized = true;
45
+ return app;
46
+ }
47
+ // ─────────────────────────────────────────────────────────────────────────
48
+ // ContainerResolver interface (used by @abdokouta/ts-container/react)
49
+ // ─────────────────────────────────────────────────────────────────────────
50
+ /**
51
+ * Resolve a provider by its injection token.
52
+ *
53
+ * Searches all modules for the provider. For singleton providers,
54
+ * returns the cached instance.
55
+ *
56
+ * @param token - The injection token (class, string, or symbol)
57
+ * @returns The resolved provider instance
58
+ * @throws Error if the provider is not found
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * const userService = app.get(UserService);
63
+ * const config = app.get<CacheConfig>(CACHE_CONFIG);
64
+ * const apiUrl = app.get<string>('API_URL');
65
+ * ```
66
+ */
67
+ get(token) {
68
+ this.assertInitialized();
69
+ for (const [, moduleRef] of this.container.getModules()) {
70
+ const wrapper = moduleRef.providers.get(token);
71
+ if (!wrapper) continue;
72
+ if (wrapper.isResolved && !wrapper.isTransient) {
73
+ return wrapper.instance;
74
+ }
75
+ if (wrapper.isTransient && wrapper.metatype) {
76
+ return this.instantiateTransient(wrapper, moduleRef);
77
+ }
78
+ if (wrapper.isTransient && wrapper.instance !== null) {
79
+ return wrapper.instance;
80
+ }
81
+ }
82
+ throw new Error(
83
+ `Provider '${this.getTokenName(token)}' not found in any module. Make sure it is provided in a module that has been imported.`
84
+ );
85
+ }
86
+ /**
87
+ * Try to resolve a provider, returning `undefined` if not found.
88
+ *
89
+ * @param token - The injection token
90
+ * @returns The resolved instance or undefined
91
+ */
92
+ getOptional(token) {
93
+ try {
94
+ return this.get(token);
95
+ } catch {
96
+ return void 0;
97
+ }
98
+ }
99
+ /**
100
+ * Check if a provider is registered in any module.
101
+ *
102
+ * @param token - The injection token to check
103
+ */
104
+ has(token) {
105
+ for (const [, moduleRef] of this.container.getModules()) {
106
+ if (moduleRef.providers.has(token)) return true;
107
+ }
108
+ return false;
109
+ }
110
+ // ─────────────────────────────────────────────────────────────────────────
111
+ // Advanced API
112
+ // ─────────────────────────────────────────────────────────────────────────
113
+ /**
114
+ * Select a specific module and resolve a provider from it.
115
+ *
116
+ * Useful when the same token exists in multiple modules.
117
+ *
118
+ * @param moduleClass - The module class to search in
119
+ * @param token - The injection token
120
+ */
121
+ select(moduleClass, token) {
122
+ this.assertInitialized();
123
+ for (const [, moduleRef] of this.container.getModules()) {
124
+ if (moduleRef.metatype === moduleClass) {
125
+ const wrapper = moduleRef.providers.get(token);
126
+ if (wrapper?.isResolved) {
127
+ return wrapper.instance;
128
+ }
129
+ throw new Error(
130
+ `Provider '${this.getTokenName(token)}' not found in module '${moduleClass.name}'.`
131
+ );
132
+ }
133
+ }
134
+ throw new Error(`Module '${moduleClass.name}' not found in the container.`);
135
+ }
136
+ /**
137
+ * Get the underlying NestContainer (for advanced use cases).
138
+ */
139
+ getContainer() {
140
+ return this.container;
141
+ }
142
+ /**
143
+ * Gracefully shut down the application.
144
+ *
145
+ * Calls `onModuleDestroy()` on all providers that implement it,
146
+ * in reverse module order (leaf modules first).
147
+ */
148
+ async close() {
149
+ await this.instanceLoader.destroy();
150
+ this.isInitialized = false;
151
+ }
152
+ // ─────────────────────────────────────────────────────────────────────────
153
+ // Private
154
+ // ─────────────────────────────────────────────────────────────────────────
155
+ assertInitialized() {
156
+ if (!this.isInitialized) {
157
+ throw new Error(
158
+ "ApplicationContext is not initialized. Call ApplicationContext.create() first."
159
+ );
160
+ }
161
+ }
162
+ getTokenName(token) {
163
+ if (typeof token === "function") return token.name;
164
+ if (typeof token === "symbol") return token.toString();
165
+ return String(token);
166
+ }
167
+ /**
168
+ * Synchronously instantiate a transient provider.
169
+ *
170
+ * After bootstrap, all dependencies of a transient provider are already
171
+ * resolved singletons. So we can synchronously look them up and call
172
+ * `new Class(...deps)` without awaiting anything.
173
+ */
174
+ instantiateTransient(wrapper, moduleRef) {
175
+ const injector = this.instanceLoader.getInjector();
176
+ const metatype = wrapper.metatype;
177
+ const deps = injector.getConstructorDependencies(metatype);
178
+ const optionalIndices = injector.getOptionalDependencies(metatype);
179
+ const resolvedDeps = deps.map((dep, index) => {
180
+ if (dep === void 0 || dep === null || dep === Object) {
181
+ if (optionalIndices.includes(index)) return void 0;
182
+ return void 0;
183
+ }
184
+ const result = injector.lookupProvider(dep, moduleRef);
185
+ if (!result) {
186
+ if (optionalIndices.includes(index)) return void 0;
187
+ throw new Error(`Cannot resolve transient dependency '${this.getTokenName(dep)}'`);
188
+ }
189
+ return result.wrapper.instance;
190
+ });
191
+ return new metatype(...resolvedDeps);
192
+ }
193
+ };
194
+
195
+ export { ApplicationContext };
196
+ //# sourceMappingURL=index.js.map
197
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/application-context.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @fileoverview ApplicationContext — the main entry point for bootstrapping the DI system.\n *\n * This is the equivalent of NestJS's `NestFactory.createApplicationContext()`.\n * It orchestrates the full bootstrap sequence:\n *\n * 1. **Scan** — Walk the module tree, register all modules/providers/imports/exports\n * 2. **Instantiate** — Resolve all providers (create instances, inject dependencies)\n * 3. **Lifecycle** — Call `onModuleInit()` on providers that implement it\n *\n * After bootstrap, the ApplicationContext provides `get()` to resolve any provider.\n *\n * ## Usage:\n *\n * ```typescript\n * import { ApplicationContext } from '@pixielity/application';\n * import { AppModule } from './app.module';\n *\n * // Bootstrap\n * const app = await ApplicationContext.create(AppModule);\n *\n * // Resolve providers\n * const cache = app.get(CacheManager);\n * const config = app.get<AppConfig>(APP_CONFIG);\n *\n * // Shutdown\n * await app.close();\n * ```\n *\n * ## With React:\n *\n * ```tsx\n * import { ContainerProvider } from '@abdokouta/ts-container/react';\n *\n * const app = await ApplicationContext.create(AppModule);\n *\n * ReactDOM.createRoot(root).render(\n * <ContainerProvider context={app}>\n * <App />\n * </ContainerProvider>\n * );\n * ```\n *\n * The ApplicationContext implements the `ContainerResolver` interface from\n * `@abdokouta/ts-container/react`, so it can be passed directly to `ContainerProvider`.\n *\n * @module application-context\n */\n\nimport type { Type, InjectionToken } from '@abdokouta/ts-container';\nimport {\n NestContainer,\n DependenciesScanner,\n InstanceLoader,\n ModuleRef,\n} from '@abdokouta/ts-container';\nimport type { IApplicationContext } from './interfaces/application-context.interface';\n\n/**\n * The bootstrapped application context.\n *\n * Provides access to the DI container after all modules have been\n * scanned and all providers have been instantiated.\n *\n * Implements `IApplicationContext` (which extends `ContainerResolver`)\n * so it can be used directly with `<ContainerProvider context={app}>`\n * from `@abdokouta/ts-container/react`.\n */\nexport class ApplicationContext implements IApplicationContext {\n private readonly container: NestContainer;\n private readonly instanceLoader: InstanceLoader;\n private isInitialized = false;\n\n private constructor(container: NestContainer, instanceLoader: InstanceLoader) {\n this.container = container;\n this.instanceLoader = instanceLoader;\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Static factory\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Create and bootstrap an application context.\n *\n * This is the main entry point. It:\n * 1. Scans the module tree starting from the root module\n * 2. Resolves all providers (creates instances, injects dependencies)\n * 3. Calls `onModuleInit()` lifecycle hooks\n *\n * @param rootModule - The root module class (your AppModule)\n * @returns A fully bootstrapped ApplicationContext\n *\n * @example\n * ```typescript\n * import { ApplicationContext } from '@pixielity/application';\n * import { AppModule } from './app.module';\n *\n * const app = await ApplicationContext.create(AppModule);\n *\n * const userService = app.get(UserService);\n * const config = app.get<CacheConfig>(CACHE_CONFIG);\n * ```\n */\n public static async create(rootModule: Type<any>): Promise<ApplicationContext> {\n const container = new NestContainer();\n const scanner = new DependenciesScanner(container);\n const instanceLoader = new InstanceLoader(container);\n\n // Phase 1: Scan the module tree\n await scanner.scan(rootModule);\n\n // Phase 2: Create all provider instances\n await instanceLoader.createInstances();\n\n const app = new ApplicationContext(container, instanceLoader);\n app.isInitialized = true;\n\n return app;\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // ContainerResolver interface (used by @abdokouta/ts-container/react)\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Resolve a provider by its injection token.\n *\n * Searches all modules for the provider. For singleton providers,\n * returns the cached instance.\n *\n * @param token - The injection token (class, string, or symbol)\n * @returns The resolved provider instance\n * @throws Error if the provider is not found\n *\n * @example\n * ```typescript\n * const userService = app.get(UserService);\n * const config = app.get<CacheConfig>(CACHE_CONFIG);\n * const apiUrl = app.get<string>('API_URL');\n * ```\n */\n public get<T = any>(token: InjectionToken<T>): T {\n this.assertInitialized();\n\n for (const [, moduleRef] of this.container.getModules()) {\n const wrapper = moduleRef.providers.get(token);\n if (!wrapper) continue;\n\n // Singleton or value provider — return cached instance\n if (wrapper.isResolved && !wrapper.isTransient) {\n return wrapper.instance as T;\n }\n\n // Transient provider — create a fresh instance each time.\n // All dependencies should already be resolved after bootstrap,\n // so we can synchronously instantiate the class.\n if (wrapper.isTransient && wrapper.metatype) {\n return this.instantiateTransient<T>(wrapper, moduleRef);\n }\n\n // Transient provider that was resolved during bootstrap\n // (has a cached instance from the initial resolution)\n if (wrapper.isTransient && wrapper.instance !== null) {\n return wrapper.instance as T;\n }\n }\n\n throw new Error(\n `Provider '${this.getTokenName(token)}' not found in any module. ` +\n `Make sure it is provided in a module that has been imported.`,\n );\n }\n\n /**\n * Try to resolve a provider, returning `undefined` if not found.\n *\n * @param token - The injection token\n * @returns The resolved instance or undefined\n */\n public getOptional<T = any>(token: InjectionToken<T>): T | undefined {\n try {\n return this.get(token);\n } catch {\n return undefined;\n }\n }\n\n /**\n * Check if a provider is registered in any module.\n *\n * @param token - The injection token to check\n */\n public has(token: InjectionToken): boolean {\n for (const [, moduleRef] of this.container.getModules()) {\n if (moduleRef.providers.has(token)) return true;\n }\n return false;\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Advanced API\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Select a specific module and resolve a provider from it.\n *\n * Useful when the same token exists in multiple modules.\n *\n * @param moduleClass - The module class to search in\n * @param token - The injection token\n */\n public select<T = any>(moduleClass: Type<any>, token: InjectionToken<T>): T {\n this.assertInitialized();\n\n for (const [, moduleRef] of this.container.getModules()) {\n if (moduleRef.metatype === moduleClass) {\n const wrapper = moduleRef.providers.get(token);\n if (wrapper?.isResolved) {\n return wrapper.instance as T;\n }\n throw new Error(\n `Provider '${this.getTokenName(token)}' not found in module '${moduleClass.name}'.`,\n );\n }\n }\n\n throw new Error(`Module '${moduleClass.name}' not found in the container.`);\n }\n\n /**\n * Get the underlying NestContainer (for advanced use cases).\n */\n public getContainer(): NestContainer {\n return this.container;\n }\n\n /**\n * Gracefully shut down the application.\n *\n * Calls `onModuleDestroy()` on all providers that implement it,\n * in reverse module order (leaf modules first).\n */\n public async close(): Promise<void> {\n await this.instanceLoader.destroy();\n this.isInitialized = false;\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Private\n // ─────────────────────────────────────────────────────────────────────────\n\n private assertInitialized(): void {\n if (!this.isInitialized) {\n throw new Error(\n 'ApplicationContext is not initialized. Call ApplicationContext.create() first.',\n );\n }\n }\n\n private getTokenName(token: InjectionToken): string {\n if (typeof token === 'function') return token.name;\n if (typeof token === 'symbol') return token.toString();\n return String(token);\n }\n\n /**\n * Synchronously instantiate a transient provider.\n *\n * After bootstrap, all dependencies of a transient provider are already\n * resolved singletons. So we can synchronously look them up and call\n * `new Class(...deps)` without awaiting anything.\n */\n private instantiateTransient<T>(wrapper: any, moduleRef: ModuleRef): T {\n const injector = this.instanceLoader.getInjector();\n\n // Look up constructor dependencies — they should all be resolved singletons\n const metatype = wrapper.metatype;\n const deps = (injector as any).getConstructorDependencies(metatype);\n const optionalIndices: number[] = (injector as any).getOptionalDependencies(metatype);\n\n const resolvedDeps = deps.map((dep: InjectionToken, index: number) => {\n if (dep === undefined || dep === null || dep === Object) {\n if (optionalIndices.includes(index)) return undefined;\n return undefined;\n }\n\n const result = injector.lookupProvider(dep, moduleRef);\n if (!result) {\n if (optionalIndices.includes(index)) return undefined;\n throw new Error(`Cannot resolve transient dependency '${this.getTokenName(dep)}'`);\n }\n\n return result.wrapper.instance;\n });\n\n return new metatype(...resolvedDeps);\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@abdokouta/ts-application",
3
+ "version": "1.0.0",
4
+ "description": "Application bootstrap layer for @abdokouta/ts-container. Provides ApplicationContext.create() to scan modules, instantiate providers, and run lifecycle hooks.",
5
+ "keywords": [
6
+ "dependency-injection",
7
+ "di",
8
+ "application",
9
+ "bootstrap",
10
+ "nestjs",
11
+ "lifecycle",
12
+ "typescript"
13
+ ],
14
+ "license": "MIT",
15
+ "author": "Pixielity",
16
+ "type": "module",
17
+ "sideEffects": false,
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js",
22
+ "require": "./dist/index.cjs"
23
+ }
24
+ },
25
+ "main": "dist/index.cjs",
26
+ "module": "dist/index.mjs",
27
+ "types": "dist/index.d.ts",
28
+ "files": [
29
+ "dist",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "dependencies": {
34
+ "@abdokouta/ts-container": "1.0.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^25.5.2",
38
+ "tsup": "^8.5.1",
39
+ "typescript": "^6.0.2",
40
+ "@nesvel/typescript-config": "^1.0.4",
41
+ "vitest": "^4.1.2"
42
+ },
43
+ "engines": {
44
+ "node": ">=18"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "scripts": {
50
+ "build": "tsup",
51
+ "dev": "tsup --watch",
52
+ "typecheck": "tsc --noEmit",
53
+ "clean": "rm -rf dist",
54
+ "test": "vitest run --passWithNoTests"
55
+ }
56
+ }