@stitchem/core 0.0.3

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.
Files changed (68) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE +21 -0
  3. package/README.md +815 -0
  4. package/dist/container/container.d.ts +79 -0
  5. package/dist/container/container.js +156 -0
  6. package/dist/container/module.map.d.ts +22 -0
  7. package/dist/container/module.map.js +40 -0
  8. package/dist/context/context.d.ts +181 -0
  9. package/dist/context/context.js +395 -0
  10. package/dist/context/scope.d.ts +30 -0
  11. package/dist/context/scope.js +42 -0
  12. package/dist/core/core.lifecycle.d.ts +41 -0
  13. package/dist/core/core.lifecycle.js +37 -0
  14. package/dist/core/core.lifetime.d.ts +21 -0
  15. package/dist/core/core.lifetime.js +22 -0
  16. package/dist/core/core.types.d.ts +2 -0
  17. package/dist/core/core.types.js +2 -0
  18. package/dist/core/core.utils.d.ts +8 -0
  19. package/dist/core/core.utils.js +13 -0
  20. package/dist/decorator/inject.decorator.d.ts +50 -0
  21. package/dist/decorator/inject.decorator.js +78 -0
  22. package/dist/decorator/injectable.decorator.d.ts +45 -0
  23. package/dist/decorator/injectable.decorator.js +46 -0
  24. package/dist/errors/core.error.d.ts +24 -0
  25. package/dist/errors/core.error.js +59 -0
  26. package/dist/errors/error.codes.d.ts +17 -0
  27. package/dist/errors/error.codes.js +21 -0
  28. package/dist/index.d.ts +25 -0
  29. package/dist/index.js +23 -0
  30. package/dist/injector/injector.d.ts +78 -0
  31. package/dist/injector/injector.js +295 -0
  32. package/dist/instance-wrapper/instance-wrapper.d.ts +61 -0
  33. package/dist/instance-wrapper/instance-wrapper.js +142 -0
  34. package/dist/instance-wrapper/instance-wrapper.types.d.ts +18 -0
  35. package/dist/instance-wrapper/instance-wrapper.types.js +2 -0
  36. package/dist/logger/console.logger.d.ts +52 -0
  37. package/dist/logger/console.logger.js +90 -0
  38. package/dist/logger/logger.token.d.ts +23 -0
  39. package/dist/logger/logger.token.js +23 -0
  40. package/dist/logger/logger.types.d.ts +38 -0
  41. package/dist/logger/logger.types.js +12 -0
  42. package/dist/module/module.d.ts +104 -0
  43. package/dist/module/module.decorator.d.ts +28 -0
  44. package/dist/module/module.decorator.js +42 -0
  45. package/dist/module/module.graph.d.ts +52 -0
  46. package/dist/module/module.graph.js +263 -0
  47. package/dist/module/module.js +181 -0
  48. package/dist/module/module.ref.d.ts +81 -0
  49. package/dist/module/module.ref.js +123 -0
  50. package/dist/module/module.types.d.ts +80 -0
  51. package/dist/module/module.types.js +10 -0
  52. package/dist/provider/provider.guards.d.ts +46 -0
  53. package/dist/provider/provider.guards.js +62 -0
  54. package/dist/provider/provider.interface.d.ts +39 -0
  55. package/dist/provider/provider.interface.js +2 -0
  56. package/dist/test/test.d.ts +22 -0
  57. package/dist/test/test.js +23 -0
  58. package/dist/test/test.module-builder.d.ts +136 -0
  59. package/dist/test/test.module-builder.js +377 -0
  60. package/dist/test/test.module.d.ts +71 -0
  61. package/dist/test/test.module.js +151 -0
  62. package/dist/token/lazy.token.d.ts +44 -0
  63. package/dist/token/lazy.token.js +42 -0
  64. package/dist/token/token.types.d.ts +8 -0
  65. package/dist/token/token.types.js +2 -0
  66. package/dist/token/token.utils.d.ts +9 -0
  67. package/dist/token/token.utils.js +19 -0
  68. package/package.json +62 -0
@@ -0,0 +1,79 @@
1
+ import type { Token } from '../token/token.types.js';
2
+ import type { constructor } from '../core/core.types.js';
3
+ import type { Scope } from '../context/scope.js';
4
+ import type { InstanceWrapper } from '../instance-wrapper/instance-wrapper.js';
5
+ import type { Module } from '../module/module.js';
6
+ import { ModuleMap } from './module.map.js';
7
+ /**
8
+ * Result of looking up a provider.
9
+ */
10
+ export interface ProviderLookupResult<T = unknown> {
11
+ wrapper: InstanceWrapper<T>;
12
+ module: Module;
13
+ }
14
+ /**
15
+ * Main container that holds all modules.
16
+ * Manages module registration and provider lookup across the module tree.
17
+ */
18
+ export declare class Container {
19
+ /** All registered modules. */
20
+ private readonly _modules;
21
+ /** Global modules (accessible from all modules). */
22
+ private readonly _globalModules;
23
+ /** Cached global exports for visibility checks. */
24
+ private _globalExports;
25
+ /** All modules. */
26
+ get modules(): ModuleMap;
27
+ /** All global modules. */
28
+ get globalModules(): ReadonlySet<Module>;
29
+ /** All global exports. */
30
+ get globalExports(): ReadonlySet<Token>;
31
+ /** Adds a module to the container. */
32
+ addModule(mod: Module): void;
33
+ /** Gets a module by ID. */
34
+ getModule(id: string): Module | undefined;
35
+ /**
36
+ * Gets a module by its class constructor.
37
+ * @throws CoreError (MODULE_NOT_FOUND) if not found
38
+ */
39
+ getModuleByClass(classRef: constructor): Module;
40
+ /**
41
+ * Gets a module by its class constructor, or undefined if not found.
42
+ */
43
+ findModuleByClass(classRef: constructor): Module | undefined;
44
+ /**
45
+ * Rebuilds the global exports cache.
46
+ * Call this after all modules are registered.
47
+ */
48
+ buildGlobalExports(): void;
49
+ /**
50
+ * Looks up a provider by token from a module's perspective.
51
+ * Searches own providers, imports, and globals — then checks visibility.
52
+ *
53
+ * @throws CoreError (PROVIDER_NOT_FOUND) if provider doesn't exist anywhere
54
+ * @throws CoreError (PROVIDER_NOT_VISIBLE) if provider exists but is not accessible
55
+ */
56
+ getProviderByToken<T>(token: Token<T>, fromModule: Module): ProviderLookupResult<T>;
57
+ /**
58
+ * Looks up a provider by token across ALL modules, bypassing visibility rules.
59
+ *
60
+ * @throws CoreError (PROVIDER_NOT_FOUND) if provider not found in any module
61
+ */
62
+ getProviderByTokenGlobal<T>(token: Token<T>): ProviderLookupResult<T>;
63
+ /**
64
+ * Checks if any module in the container has a provider for this token.
65
+ */
66
+ private hasProviderAnywhere;
67
+ /**
68
+ * Searches a module for an exported provider.
69
+ * Recursively searches imports if the token is exported but not provided directly.
70
+ */
71
+ private findExportedProvider;
72
+ /**
73
+ * Disposes all modules in reverse registration order.
74
+ */
75
+ dispose(scope?: Scope): Promise<void>;
76
+ /** Clears all modules from the container. */
77
+ clear(): void;
78
+ }
79
+ //# sourceMappingURL=container.d.ts.map
@@ -0,0 +1,156 @@
1
+ import { ModuleMap } from './module.map.js';
2
+ import { CoreError } from '../errors/core.error.js';
3
+ /**
4
+ * Main container that holds all modules.
5
+ * Manages module registration and provider lookup across the module tree.
6
+ */
7
+ export class Container {
8
+ /** All registered modules. */
9
+ _modules = new ModuleMap();
10
+ /** Global modules (accessible from all modules). */
11
+ _globalModules = new Set();
12
+ /** Cached global exports for visibility checks. */
13
+ _globalExports = new Set();
14
+ /** All modules. */
15
+ get modules() {
16
+ return this._modules;
17
+ }
18
+ /** All global modules. */
19
+ get globalModules() {
20
+ return this._globalModules;
21
+ }
22
+ /** All global exports. */
23
+ get globalExports() {
24
+ return this._globalExports;
25
+ }
26
+ /** Adds a module to the container. */
27
+ addModule(mod) {
28
+ this._modules.set(mod.id, mod);
29
+ if (mod.isGlobal)
30
+ this._globalModules.add(mod);
31
+ }
32
+ /** Gets a module by ID. */
33
+ getModule(id) {
34
+ return this._modules.get(id);
35
+ }
36
+ /**
37
+ * Gets a module by its class constructor.
38
+ * @throws CoreError (MODULE_NOT_FOUND) if not found
39
+ */
40
+ getModuleByClass(classRef) {
41
+ const mod = this._modules.getByClass(classRef);
42
+ if (!mod)
43
+ throw CoreError.moduleNotFound(classRef);
44
+ return mod;
45
+ }
46
+ /**
47
+ * Gets a module by its class constructor, or undefined if not found.
48
+ */
49
+ findModuleByClass(classRef) {
50
+ return this._modules.getByClass(classRef);
51
+ }
52
+ /**
53
+ * Rebuilds the global exports cache.
54
+ * Call this after all modules are registered.
55
+ */
56
+ buildGlobalExports() {
57
+ this._globalExports = new Set();
58
+ for (const mod of this._globalModules) {
59
+ for (const token of mod.exports) {
60
+ this._globalExports.add(token);
61
+ }
62
+ }
63
+ }
64
+ /**
65
+ * Looks up a provider by token from a module's perspective.
66
+ * Searches own providers, imports, and globals — then checks visibility.
67
+ *
68
+ * @throws CoreError (PROVIDER_NOT_FOUND) if provider doesn't exist anywhere
69
+ * @throws CoreError (PROVIDER_NOT_VISIBLE) if provider exists but is not accessible
70
+ */
71
+ getProviderByToken(token, fromModule) {
72
+ // Own providers first.
73
+ const ownProvider = fromModule.getProvider(token);
74
+ if (ownProvider)
75
+ return { wrapper: ownProvider, module: fromModule };
76
+ // Imported modules' exports.
77
+ for (const imported of fromModule.imports) {
78
+ const result = this.findExportedProvider(imported, token);
79
+ if (result)
80
+ return result;
81
+ }
82
+ // Global modules.
83
+ for (const globalMod of this._globalModules) {
84
+ const result = this.findExportedProvider(globalMod, token);
85
+ if (result)
86
+ return result;
87
+ }
88
+ // Not found — determine if it exists elsewhere (visibility issue) or doesn't exist at all.
89
+ if (this.hasProviderAnywhere(token)) {
90
+ throw CoreError.providerNotVisible(token, fromModule.id);
91
+ }
92
+ throw CoreError.providerNotFound(token, fromModule.id);
93
+ }
94
+ /**
95
+ * Looks up a provider by token across ALL modules, bypassing visibility rules.
96
+ *
97
+ * @throws CoreError (PROVIDER_NOT_FOUND) if provider not found in any module
98
+ */
99
+ getProviderByTokenGlobal(token) {
100
+ for (const mod of this._modules.values()) {
101
+ const provider = mod.getProvider(token);
102
+ if (provider)
103
+ return { wrapper: provider, module: mod };
104
+ }
105
+ throw CoreError.providerNotFound(token, 'global');
106
+ }
107
+ /**
108
+ * Checks if any module in the container has a provider for this token.
109
+ */
110
+ hasProviderAnywhere(token) {
111
+ for (const mod of this._modules.values()) {
112
+ if (mod.hasProvider(token))
113
+ return true;
114
+ }
115
+ return false;
116
+ }
117
+ /**
118
+ * Searches a module for an exported provider.
119
+ * Recursively searches imports if the token is exported but not provided directly.
120
+ */
121
+ findExportedProvider(mod, token, visited = new Set()) {
122
+ if (visited.has(mod))
123
+ return undefined;
124
+ visited.add(mod);
125
+ if (!mod.exports.has(token))
126
+ return undefined;
127
+ const provider = mod.getProvider(token);
128
+ if (provider)
129
+ return { wrapper: provider, module: mod };
130
+ // Token is exported but not owned — search imports for actual provider.
131
+ for (const imported of mod.imports) {
132
+ const result = this.findExportedProvider(imported, token, visited);
133
+ if (result)
134
+ return result;
135
+ }
136
+ return undefined;
137
+ }
138
+ /**
139
+ * Disposes all modules in reverse registration order.
140
+ */
141
+ async dispose(scope) {
142
+ const moduleIds = [...this._modules.keys()].reverse();
143
+ for (const id of moduleIds) {
144
+ const mod = this._modules.get(id);
145
+ if (mod)
146
+ await mod.dispose(scope);
147
+ }
148
+ }
149
+ /** Clears all modules from the container. */
150
+ clear() {
151
+ this._modules.clear();
152
+ this._globalModules.clear();
153
+ this._globalExports.clear();
154
+ }
155
+ }
156
+ //# sourceMappingURL=container.js.map
@@ -0,0 +1,22 @@
1
+ import type { constructor } from '../core/core.types.js';
2
+ import type { Module } from '../module/module.js';
3
+ /**
4
+ * Map of module ID → Module with utility lookup methods.
5
+ */
6
+ export declare class ModuleMap extends Map<string, Module> {
7
+ /**
8
+ * Gets a module by its class constructor.
9
+ * Returns the first match (multiple instances possible via DynamicModule).
10
+ */
11
+ getByClass(classRef: constructor): Module | undefined;
12
+ /**
13
+ * Gets all modules with the given class constructor.
14
+ * Useful for DynamicModules where the same class can have multiple instances.
15
+ */
16
+ getAllByClass(classRef: constructor): Module[];
17
+ /**
18
+ * Gets all global modules.
19
+ */
20
+ getGlobalModules(): Module[];
21
+ }
22
+ //# sourceMappingURL=module.map.d.ts.map
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Map of module ID → Module with utility lookup methods.
3
+ */
4
+ export class ModuleMap extends Map {
5
+ /**
6
+ * Gets a module by its class constructor.
7
+ * Returns the first match (multiple instances possible via DynamicModule).
8
+ */
9
+ getByClass(classRef) {
10
+ for (const mod of this.values()) {
11
+ if (mod.classRef === classRef)
12
+ return mod;
13
+ }
14
+ return undefined;
15
+ }
16
+ /**
17
+ * Gets all modules with the given class constructor.
18
+ * Useful for DynamicModules where the same class can have multiple instances.
19
+ */
20
+ getAllByClass(classRef) {
21
+ const modules = [];
22
+ for (const mod of this.values()) {
23
+ if (mod.classRef === classRef)
24
+ modules.push(mod);
25
+ }
26
+ return modules;
27
+ }
28
+ /**
29
+ * Gets all global modules.
30
+ */
31
+ getGlobalModules() {
32
+ const globals = [];
33
+ for (const mod of this.values()) {
34
+ if (mod.isGlobal)
35
+ globals.push(mod);
36
+ }
37
+ return globals;
38
+ }
39
+ }
40
+ //# sourceMappingURL=module.map.js.map
@@ -0,0 +1,181 @@
1
+ import type { constructor } from '../core/core.types.js';
2
+ import type { Token } from '../token/token.types.js';
3
+ import type { ModuleImport } from '../module/module.types.js';
4
+ import type { Logger } from '../logger/logger.types.js';
5
+ import { LogLevel } from '../logger/logger.types.js';
6
+ import { Container } from '../container/container.js';
7
+ import { ModuleRef } from '../module/module.ref.js';
8
+ import { Injector } from '../injector/injector.js';
9
+ import { Scope } from './scope.js';
10
+ /**
11
+ * Options for resolving a provider.
12
+ */
13
+ export interface ResolveOptions {
14
+ /** When true, scopes resolution to root module visibility. */
15
+ strict?: boolean;
16
+ }
17
+ /**
18
+ * Options for creating a Context.
19
+ */
20
+ export interface ContextOptions {
21
+ /**
22
+ * Metadata keys whose values are arrays of constructors that the core
23
+ * should instantiate with DI and lifecycle-manage as "components".
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * // HTTP extension registers 'routers'
28
+ * await using ctx = await Context.create(AppModule, {
29
+ * componentKeys: ['routers'],
30
+ * });
31
+ * ```
32
+ */
33
+ componentKeys?: string[];
34
+ /**
35
+ * Logging configuration.
36
+ *
37
+ * - Omitted / `undefined`: default {@link ConsoleLogger} at {@link LogLevel.DEBUG}.
38
+ * - `false`: logging is disabled (a {@link NoopLogger} is registered).
39
+ * - `{ level }`: default ConsoleLogger at the given level.
40
+ * - `{ logger }`: custom {@link Logger} implementation used as-is.
41
+ */
42
+ logging?: false | LoggingOptions;
43
+ }
44
+ /**
45
+ * Logging configuration for the DI context.
46
+ */
47
+ export interface LoggingOptions {
48
+ /** Custom logger implementation. Defaults to {@link ConsoleLogger}. */
49
+ logger?: Logger;
50
+ /** Log level for the default ConsoleLogger. Ignored when a custom `logger` is provided. */
51
+ level?: LogLevel;
52
+ }
53
+ /**
54
+ * A resolved component instance with its origin information.
55
+ */
56
+ export interface ComponentRef<T = unknown> {
57
+ instance: T;
58
+ classRef: constructor<T>;
59
+ moduleRef: ModuleRef;
60
+ }
61
+ /**
62
+ * The Context orchestrates the DI system.
63
+ * It compiles modules, manages their lifecycle, and provides dependency resolution.
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * await using ctx = await Context.create(AppModule);
68
+ *
69
+ * const userService = await ctx.resolve(UserService);
70
+ *
71
+ * await ctx.withScope(async () => {
72
+ * const requestService = await ctx.resolve(RequestService);
73
+ * });
74
+ * ```
75
+ */
76
+ export declare class Context implements AsyncDisposable {
77
+ readonly container: Container;
78
+ readonly injector: Injector;
79
+ private moduleGraph;
80
+ private rootModule;
81
+ private initialized;
82
+ private componentKeys;
83
+ private constructor();
84
+ /**
85
+ * Creates and initializes a context from a root module.
86
+ *
87
+ * @throws CoreError (INVALID_MODULE) if the root is not decorated with @module()
88
+ */
89
+ static create(rootModule: ModuleImport, options?: ContextOptions): Promise<Context>;
90
+ /**
91
+ * Resolves a provider by token.
92
+ *
93
+ * By default, searches across ALL modules.
94
+ * Pass `strict: true` to scope resolution to root module visibility.
95
+ *
96
+ * Scope auto-detection: if called inside a {@link withScope} or
97
+ * `scope.run()` callback, the current scope is used automatically.
98
+ */
99
+ resolve<T>(token: Token<T>, options?: ResolveOptions): Promise<T>;
100
+ /**
101
+ * Synchronously resolves a provider by token.
102
+ */
103
+ resolveSync<T>(token: Token<T>, options?: ResolveOptions): T;
104
+ /**
105
+ * Instantiates any class by resolving its dependencies.
106
+ * The class does not need to be registered as a provider.
107
+ */
108
+ constructClass<T>(ctor: constructor<T>, options?: ResolveOptions): Promise<T>;
109
+ /**
110
+ * Resolves an injectable class with global dependency resolution.
111
+ *
112
+ * Use this for injectables that are not tied to a specific module
113
+ * (e.g. global guards, filters, middleware registered via `useGlobal`).
114
+ * Dependencies are always resolved globally; the instance is cached
115
+ * on the root module. The `strict` option is accepted for signature
116
+ * compatibility with {@link ModuleRef.resolveInjectable} but is ignored
117
+ * (Context always resolves globally).
118
+ *
119
+ * For module-scoped resolution, use {@link ModuleRef.resolveInjectable}.
120
+ */
121
+ resolveInjectable<T>(ctor: constructor<T>, _options?: {
122
+ strict?: boolean;
123
+ }): Promise<T>;
124
+ /**
125
+ * Runs a callback within a new disposable scope.
126
+ * The scope is automatically disposed when the callback completes.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * await ctx.withScope(async () => {
131
+ * const svc = await ctx.resolve(RequestService);
132
+ * });
133
+ * ```
134
+ */
135
+ withScope<T>(fn: () => T | Promise<T>): Promise<T>;
136
+ /**
137
+ * Creates a new disposable scope for request-scoped dependencies.
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * await using scope = ctx.createScope();
142
+ * const svc = await scope.run(() => ctx.resolve(RequestService));
143
+ * ```
144
+ */
145
+ createScope(): Scope;
146
+ /**
147
+ * Navigates to a specific module for resolution.
148
+ * Returns a ModuleRef bound to the target module.
149
+ */
150
+ select<T extends constructor>(moduleClass: T): ModuleRef;
151
+ /**
152
+ * Returns all component instances for a given metadata key.
153
+ * Components are returned in module dependency order.
154
+ */
155
+ getComponents<T = unknown>(key: string): ComponentRef<T>[];
156
+ /** Disposes the context and all managed instances. */
157
+ [Symbol.asyncDispose](): Promise<void>;
158
+ /**
159
+ * Disposes the context.
160
+ * With scope: only disposes scoped instances for that scope.
161
+ * Without scope: disposes injectables, components, then providers — shuts down.
162
+ */
163
+ private dispose;
164
+ private init;
165
+ /** Registers a ModuleRef factory provider for each module. */
166
+ private registerModuleRefs;
167
+ /** Registers the LOGGER provider in every module that doesn't already have one. */
168
+ private registerLogger;
169
+ /** Eagerly resolves all singleton providers in dependency order. */
170
+ private resolveSingletons;
171
+ /** Instantiates component classes from registered component keys. */
172
+ private resolveComponents;
173
+ /** Calls onReady() on all singleton providers, module classes, and components. */
174
+ private fireOnReady;
175
+ /** Disposes injectables in reverse module order, before component disposal. */
176
+ private disposeInjectables;
177
+ /** Disposes components in reverse module order, before provider disposal. */
178
+ private disposeComponents;
179
+ private ensureInitialized;
180
+ }
181
+ //# sourceMappingURL=context.d.ts.map