@signe/di 2.4.6 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,30 @@
1
+ /**
2
+ * Context class for managing dependency injection state
3
+ * @module @signe/di/context
4
+ */
5
+ /**
6
+ * Stores and manages the state of injected dependencies
7
+ * @template TState - Type for the state
8
+ * @template TActions - Type for the actions
9
+ * @template TValues - Type for additional values
10
+ */
11
+ declare class Context<TState = any, TActions = any, TValues extends Record<string, any> = Record<string, any>> {
12
+ /** Internal storage for injected values */
13
+ private values;
14
+ /**
15
+ * Sets a value in the context
16
+ * @param key - Unique identifier for the value
17
+ * @param value - Value to store
18
+ */
19
+ set<K extends keyof TValues>(key: K, value: TValues[K]): void;
20
+ /**
21
+ * Retrieves a value from the context
22
+ * @param key - Unique identifier for the value
23
+ * @returns The stored value or undefined if not found
24
+ */
25
+ get<K extends keyof TValues>(key: K): TValues[K];
26
+ }
27
+
1
28
  /**
2
29
  * Type definitions for the dependency injection system
3
30
  * @module @signe/di/types
@@ -22,7 +49,51 @@ type FactoryFn = (context: ProviderContext) => any;
22
49
  interface ProviderMeta {
23
50
  [key: string]: any;
24
51
  }
25
- interface ValueProvider {
52
+ /**
53
+ * Common options available to all providers
54
+ */
55
+ interface ProviderOptions {
56
+ /**
57
+ * When true, allows multiple instances to be registered for the same token
58
+ */
59
+ multi?: boolean;
60
+ /**
61
+ * Optional name used to register and resolve a specific instance
62
+ */
63
+ name?: string;
64
+ }
65
+ /**
66
+ * Options that can be passed when calling {@link provide}
67
+ */
68
+ interface ProvideOptions extends ProviderOptions {
69
+ }
70
+ /**
71
+ * Options that can be passed when calling {@link inject}
72
+ */
73
+ interface InjectionOptions {
74
+ /**
75
+ * Optional name used to resolve a specific instance
76
+ */
77
+ name?: string;
78
+ /**
79
+ * When true, allows retrieving all instances registered with {@link ProviderOptions.multi}
80
+ */
81
+ multi?: boolean;
82
+ /**
83
+ * When true, `inject` will return `undefined` instead of throwing if the instance is missing
84
+ */
85
+ optional?: boolean;
86
+ }
87
+ /**
88
+ * Options used when checking if an instance exists in the context
89
+ */
90
+ interface InstanceLookupOptions {
91
+ /**
92
+ * Optional name of the instance to check
93
+ */
94
+ name?: string;
95
+ }
96
+ interface ValueProvider extends ProviderOptions {
26
97
  /** Token to identify the provider */
27
98
  provide: ProviderToken;
28
99
  /** Value to be injected */
@@ -35,7 +106,7 @@ interface ValueProvider {
35
106
  /**
36
107
  * Provider configuration for class-based injection
37
108
  */
38
- interface ClassProvider {
109
+ interface ClassProvider extends ProviderOptions {
39
110
  /** Token to identify the provider */
40
111
  provide: ProviderToken;
41
112
  /** Class to be instantiated */
@@ -48,7 +119,7 @@ interface ClassProvider {
48
119
  /**
49
120
  * Provider configuration for factory-based injection
50
121
  */
51
- interface FactoryProvider {
122
+ interface FactoryProvider extends ProviderOptions {
52
123
  /** Token to identify the provider */
53
124
  provide: ProviderToken;
54
125
  /** Tokens that must be injected before this provider */
@@ -61,7 +132,7 @@ interface FactoryProvider {
61
132
  /**
62
133
  * Provider configuration for alias-based injection
63
134
  */
64
- interface ExistingProvider {
135
+ interface ExistingProvider extends ProviderOptions {
65
136
  /** Token to identify the provider */
66
137
  provide: ProviderToken;
67
138
  /** Token of the existing provider to use */
@@ -96,33 +167,6 @@ type Provider = (new (...args: any[]) => any) | (ValueProvider & {
96
167
  */
97
168
  type Providers = (Provider | Providers)[];
98
169
 
99
- /**
100
- * Context class for managing dependency injection state
101
- * @module @signe/di/context
102
- */
103
- /**
104
- * Stores and manages the state of injected dependencies
105
- * @template TState - Type for the state
106
- * @template TActions - Type for the actions
107
- * @template TValues - Type for additional values
108
- */
109
- declare class Context<TState = any, TActions = any, TValues extends Record<string, any> = Record<string, any>> {
110
- /** Internal storage for injected values */
111
- private values;
112
- /**
113
- * Sets a value in the context
114
- * @param key - Unique identifier for the value
115
- * @param value - Value to store
116
- */
117
- set<K extends keyof TValues>(key: K, value: TValues[K]): void;
118
- /**
119
- * Retrieves a value from the context
120
- * @param key - Unique identifier for the value
121
- * @returns The stored value or undefined if not found
122
- */
123
- get<K extends keyof TValues>(key: K): TValues[K];
124
- }
125
-
126
170
  /**
127
171
  * Core functionality for dependency injection
128
172
  * @module @signe/di/inject
@@ -132,35 +176,43 @@ declare class Context<TState = any, TActions = any, TValues extends Record<strin
132
176
  * Provides a value to the dependency injection context
133
177
  * @template T - Type of the value being provided
134
178
  * @param context - The injection context
135
- * @param name - Identifier for the provided value
179
+ * @param token - Identifier for the provided value
136
180
  * @param value - The value to provide
181
+ * @param options - Configuration options for the provided value
137
182
  * @returns The provided value
138
183
  */
139
- declare function provide<T>(context: Context, name: string, value: T): T;
184
+ declare function provide<T>(context: Context, token: ProviderToken | string, value: T, options?: ProvideOptions): T;
140
185
  /**
141
186
  * Checks if a service has been injected into the context
142
187
  * @param context - The injection context
143
- * @param name - Name of the service to check
188
+ * @param token - Token of the service to check
189
+ * @param options - Optional lookup configuration
144
190
  * @returns True if the service has been injected, false otherwise
145
191
  */
146
- declare function isInjected(context: Context, name: string): boolean;
192
+ declare function isInjected(context: Context, token: ProviderToken | string, options?: InstanceLookupOptions): boolean;
147
193
  /**
148
194
  * Checks if a service has been provided in the context
149
195
  * @param context - The injection context
150
- * @param name - Name of the service to check
196
+ * @param token - Token of the service to check
197
+ * @param options - Optional lookup configuration
151
198
  * @returns True if the service has been provided, false otherwise
152
199
  */
153
- declare function isProvided(context: Context, name: string): boolean;
200
+ declare function isProvided(context: Context, token: ProviderToken | string, options?: InstanceLookupOptions): boolean;
154
201
  /**
155
- * Retrieves a service from the dependency injection context
156
- * @template T - Type of the service to inject
202
+ * Checks if an instance exists in the context
157
203
  * @param context - The injection context
158
- * @param service - Class constructor or string identifier of the service
159
- * @param args - Optional arguments for service instantiation
160
- * @returns The injected service instance
161
- * @throws {Error} If the requested service is not found in the context
204
+ * @param token - Token of the service to check
205
+ * @param options - Optional lookup configuration
206
+ * @returns True if the instance exists, false otherwise
162
207
  */
163
- declare function inject<T>(context: Context, service: (new (...args: any[]) => T) | string, args?: any[]): T;
208
+ declare function hasInstance(context: Context, token: ProviderToken | string, options?: InstanceLookupOptions): boolean;
209
+ declare function inject<T>(context: Context, token: ProviderToken | string, options: InjectionOptions & {
210
+ multi: true;
211
+ }): T[];
212
+ declare function inject<T>(context: Context, token: ProviderToken | string, options: InjectionOptions & {
213
+ optional: true;
214
+ }): T | undefined;
215
+ declare function inject<T>(context: Context, token: ProviderToken | string, options?: InjectionOptions): T;
164
216
  /**
165
217
  * Overrides or adds a provider in the providers array
166
218
  * @param providers - Array of existing providers
@@ -242,4 +294,4 @@ declare function mergeConfig(baseConfig: AppConfig, config: AppConfig): AppConfi
242
294
  */
243
295
  declare function injector(context: Context, providers: Providers): Promise<void>;
244
296
 
245
- export { type AppConfig, type ClassProvider, Context, type ExistingProvider, type FactoryFn, type FactoryProvider, type Provider, type ProviderContext, type ProviderMeta, type ProviderToken, type Providers, type ValueProvider, findProvider, findProviders, inject, injector, isInjected, isProvided, mergeConfig, override, provide };
297
+ export { type AppConfig, type ClassProvider, Context, type ExistingProvider, type FactoryFn, type FactoryProvider, type InjectionOptions, type InstanceLookupOptions, type ProvideOptions, type Provider, type ProviderContext, type ProviderMeta, type ProviderOptions, type ProviderToken, type Providers, type ValueProvider, findProvider, findProviders, hasInstance, inject, injector, isInjected, isProvided, mergeConfig, override, provide };
package/dist/index.js CHANGED
@@ -2,29 +2,136 @@ var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
4
  // src/inject.ts
5
- function provide(context, name, value) {
6
- context.set("inject:" + name, value);
5
+ var DEFAULT_INSTANCE_KEY = "__default__";
6
+ function toTokenName(token) {
7
+ return typeof token === "function" ? token.name : token;
8
+ }
9
+ __name(toTokenName, "toTokenName");
10
+ function toInstanceKey(name) {
11
+ return name ?? DEFAULT_INSTANCE_KEY;
12
+ }
13
+ __name(toInstanceKey, "toInstanceKey");
14
+ function getRecord(context, token) {
15
+ return context.get("inject:" + toTokenName(token));
16
+ }
17
+ __name(getRecord, "getRecord");
18
+ function ensureRecord(context, token) {
19
+ const key = "inject:" + toTokenName(token);
20
+ let record = context.get(key);
21
+ if (!record) {
22
+ record = {
23
+ multi: false,
24
+ values: /* @__PURE__ */ new Map(),
25
+ injected: /* @__PURE__ */ new Set()
26
+ };
27
+ }
28
+ context.set(key, record);
29
+ return record;
30
+ }
31
+ __name(ensureRecord, "ensureRecord");
32
+ function provide(context, token, value, options = {}) {
33
+ const record = ensureRecord(context, token);
34
+ const instanceKey = toInstanceKey(options.name);
35
+ if (options.multi) {
36
+ record.multi = true;
37
+ }
38
+ if (!record.multi && instanceKey !== DEFAULT_INSTANCE_KEY) {
39
+ record.multi = true;
40
+ }
41
+ record.values.set(instanceKey, value);
7
42
  return value;
8
43
  }
9
44
  __name(provide, "provide");
10
- function isInjected(context, name) {
11
- return context.get("injected:" + name) === true;
45
+ function isInjected(context, token, options = {}) {
46
+ const record = getRecord(context, token);
47
+ if (!record) {
48
+ return false;
49
+ }
50
+ if (options.name) {
51
+ return record.injected.has(toInstanceKey(options.name));
52
+ }
53
+ if (record.multi) {
54
+ return record.injected.size > 0;
55
+ }
56
+ return record.injected.has(DEFAULT_INSTANCE_KEY);
12
57
  }
13
58
  __name(isInjected, "isInjected");
14
- function isProvided(context, name) {
15
- return context.get("inject:" + name) !== void 0;
59
+ function isProvided(context, token, options = {}) {
60
+ const record = getRecord(context, token);
61
+ if (!record) {
62
+ return false;
63
+ }
64
+ if (options.name) {
65
+ return record.values.has(toInstanceKey(options.name));
66
+ }
67
+ if (record.multi) {
68
+ return record.values.size > 0;
69
+ }
70
+ return record.values.has(DEFAULT_INSTANCE_KEY);
16
71
  }
17
72
  __name(isProvided, "isProvided");
18
- function inject(context, service, args = []) {
19
- const isClass = typeof service === "function";
20
- const name = isClass ? service.name : service;
21
- const value = context.get("inject:" + name);
22
- if (value) {
23
- context.set("injected:" + name, true);
24
- return value;
73
+ function hasInstance(context, token, options = {}) {
74
+ return isProvided(context, token, options);
75
+ }
76
+ __name(hasInstance, "hasInstance");
77
+ function handleMissingInjection(token, options) {
78
+ const name = toTokenName(token);
79
+ if (options.name) {
80
+ throw new Error(`Injection provider ${name} with name ${options.name} not found`);
25
81
  }
26
82
  throw new Error(`Injection provider ${name} not found`);
27
83
  }
84
+ __name(handleMissingInjection, "handleMissingInjection");
85
+ function markInjected(record, key) {
86
+ record.injected.add(key);
87
+ }
88
+ __name(markInjected, "markInjected");
89
+ function markAllInjected(record) {
90
+ for (const key of record.values.keys()) {
91
+ record.injected.add(key);
92
+ }
93
+ }
94
+ __name(markAllInjected, "markAllInjected");
95
+ function inject(context, token, options = {}) {
96
+ const record = getRecord(context, token);
97
+ if (!record) {
98
+ if (options.optional) {
99
+ return options.multi ? [] : void 0;
100
+ }
101
+ return handleMissingInjection(token, options);
102
+ }
103
+ if (options.name) {
104
+ const instanceKey = toInstanceKey(options.name);
105
+ if (!record.values.has(instanceKey)) {
106
+ if (options.optional) {
107
+ return void 0;
108
+ }
109
+ return handleMissingInjection(token, options);
110
+ }
111
+ const value2 = record.values.get(instanceKey);
112
+ markInjected(record, instanceKey);
113
+ return value2;
114
+ }
115
+ if (options.multi || record.multi) {
116
+ if (record.values.size === 0) {
117
+ if (options.optional) {
118
+ return [];
119
+ }
120
+ return handleMissingInjection(token, options);
121
+ }
122
+ markAllInjected(record);
123
+ return Array.from(record.values.values());
124
+ }
125
+ const value = record.values.get(DEFAULT_INSTANCE_KEY);
126
+ if (value === void 0) {
127
+ if (options.optional) {
128
+ return void 0;
129
+ }
130
+ return handleMissingInjection(token, options);
131
+ }
132
+ markInjected(record, DEFAULT_INSTANCE_KEY);
133
+ return value;
134
+ }
28
135
  __name(inject, "inject");
29
136
  function override(providers, newProvider, options) {
30
137
  let { upsert = false, key } = options ?? {};
@@ -131,6 +238,20 @@ function mergeConfig(baseConfig, config) {
131
238
  __name(mergeConfig, "mergeConfig");
132
239
 
133
240
  // src/provider.ts
241
+ function extractProvideOptions(source) {
242
+ if (!source) {
243
+ return void 0;
244
+ }
245
+ const { multi, name } = source;
246
+ if (multi === void 0 && name === void 0) {
247
+ return void 0;
248
+ }
249
+ return {
250
+ multi,
251
+ name
252
+ };
253
+ }
254
+ __name(extractProvideOptions, "extractProvideOptions");
134
255
  function getDeps(provider) {
135
256
  if (typeof provider === "function") {
136
257
  return provider.deps ?? [];
@@ -143,7 +264,14 @@ function sortProviders(providers) {
143
264
  const map = /* @__PURE__ */ new Map();
144
265
  for (const p of providers) {
145
266
  const token = tokenName(typeof p === "function" ? p : p.provide);
146
- map.set(token, p);
267
+ const list = map.get(token);
268
+ if (list) {
269
+ list.push(p);
270
+ } else {
271
+ map.set(token, [
272
+ p
273
+ ]);
274
+ }
147
275
  }
148
276
  const result = [];
149
277
  const visited = /* @__PURE__ */ new Set();
@@ -155,13 +283,15 @@ function sortProviders(providers) {
155
283
  throw new Error(`Circular dependency detected for provider ${name}`);
156
284
  }
157
285
  stack.add(name);
158
- const provider = map.get(name);
159
- if (provider) {
160
- for (const dep of getDeps(provider)) {
161
- visit(dep);
286
+ const providersForToken = map.get(name);
287
+ if (providersForToken) {
288
+ for (const provider of providersForToken) {
289
+ for (const dep of getDeps(provider)) {
290
+ visit(dep);
291
+ }
292
+ result.push(provider);
162
293
  }
163
294
  visited.add(name);
164
- result.push(provider);
165
295
  }
166
296
  stack.delete(name);
167
297
  }, "visit");
@@ -178,11 +308,15 @@ async function injector(context, providers) {
178
308
  for (const provider of providers) {
179
309
  let token;
180
310
  let instance;
311
+ let options;
181
312
  if (typeof provider === "function") {
182
313
  token = provider;
183
314
  instance = new provider(context);
315
+ const diOptions = extractProvideOptions(provider.diOptions ?? provider.di);
316
+ options = diOptions;
184
317
  } else {
185
318
  token = provider.provide;
319
+ options = extractProvideOptions(provider);
186
320
  const provideUserClass = provider.useClass;
187
321
  const isClass = typeof provideUserClass === "function";
188
322
  if (isClass) {
@@ -198,8 +332,7 @@ async function injector(context, providers) {
198
332
  instance = inject(context, provider.useExisting);
199
333
  }
200
334
  }
201
- const name = typeof token === "function" ? token.name : token;
202
- provide(context, name, instance);
335
+ provide(context, token, instance, options);
203
336
  }
204
337
  }
205
338
  __name(injector, "injector");
@@ -232,6 +365,7 @@ export {
232
365
  Context,
233
366
  findProvider,
234
367
  findProviders,
368
+ hasInstance,
235
369
  inject,
236
370
  injector,
237
371
  isInjected,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/inject.ts","../src/merge-config.ts","../src/provider.ts","../src/context.ts"],"sourcesContent":["/**\n * Core functionality for dependency injection\n * @module @signe/di/inject\n */\n\nimport { Provider, Providers } from \"./types\";\nimport { Context } from \"./context\";\n\n/**\n * Provides a value to the dependency injection context\n * @template T - Type of the value being provided\n * @param context - The injection context\n * @param name - Identifier for the provided value\n * @param value - The value to provide\n * @returns The provided value\n */\nexport function provide<T>(context: Context, name: string, value: T): T {\n context.set(\"inject:\" + name, value);\n return value;\n}\n\n/**\n * Checks if a service has been injected into the context\n * @param context - The injection context\n * @param name - Name of the service to check\n * @returns True if the service has been injected, false otherwise\n */\nexport function isInjected(context: Context, name: string): boolean {\n return context.get('injected:' + name) === true;\n}\n\n/**\n * Checks if a service has been provided in the context\n * @param context - The injection context\n * @param name - Name of the service to check\n * @returns True if the service has been provided, false otherwise\n */\nexport function isProvided(context: Context, name: string): boolean {\n return context.get('inject:' + name) !== undefined;\n}\n\n/**\n * Retrieves a service from the dependency injection context\n * @template T - Type of the service to inject\n * @param context - The injection context\n * @param service - Class constructor or string identifier of the service\n * @param args - Optional arguments for service instantiation\n * @returns The injected service instance\n * @throws {Error} If the requested service is not found in the context\n */\nexport function inject<T>(\n context: Context,\n service: (new (...args: any[]) => T) | string,\n args: any[] = []\n): T {\n const isClass = typeof service === \"function\";\n const name = isClass ? service.name : service;\n const value = context.get(\"inject:\" + name);\n if (value) {\n context.set('injected:' + name, true)\n return value as T;\n }\n throw new Error(`Injection provider ${name} not found`);\n}\n\n/**\n * Overrides or adds a provider in the providers array\n * @param providers - Array of existing providers\n * @param newProvider - Provider to add or replace with\n * @param options - Configuration options\n * @param options.upsert - If true, adds the provider when not found\n * @param options.key - Custom key to identify the provider\n * @returns Updated array of providers\n */\nexport function override(\n providers: Providers,\n newProvider: Provider,\n options?: {\n upsert?: boolean,\n key?: string\n }\n) {\n let { upsert = false, key } = options ?? {};\n if (!key) {\n key = (\n typeof newProvider === \"function\" ? newProvider.name : newProvider.provide\n ) as string;\n }\n\n // Flatten the providers array\n const flatProviders = providers.flat();\n \n // Check if provider exists\n const exists = flatProviders.some(provider => {\n if (typeof provider === \"function\") {\n return provider.name === key;\n } else if (typeof provider === \"object\") {\n return (provider as any).provide === key;\n }\n return false;\n });\n\n // Map and replace if found\n const mappedProviders = flatProviders.map((provider) => {\n if (typeof provider === \"function\" && provider.name === key) {\n return newProvider;\n } else if (typeof provider === \"object\" && (provider as any).provide === key) {\n return newProvider;\n }\n return provider;\n });\n\n // If upsert is true and provider wasn't found, add it to the end\n if (upsert && !exists) {\n mappedProviders.push(newProvider);\n }\n\n return mappedProviders;\n}\n\n/**\n * Finds all providers matching the given name or pattern\n * @template T - Type of provider to return\n * @param providers - Array of providers to search\n * @param name - String or RegExp to match against provider names\n * @returns Array of matching providers\n */\nexport function findProviders<T extends Provider = Provider>(providers: Providers, name: string | RegExp): T[] {\n const results: any[] = [];\n \n for (const provider of providers) {\n if (Array.isArray(provider)) {\n // Recursively search in nested arrays and concat results\n results.push(...findProviders(provider, name));\n } else if (findProvider(provider as any, name)) {\n // Add matching provider to results\n results.push(provider as T);\n }\n }\n \n return results;\n}\n\n/**\n * Finds the first provider matching the given name or pattern\n * @template T - Type of provider to return\n * @param providers - Array of providers to search\n * @param name - String or RegExp to match against provider names\n * @returns Matching provider or null if not found\n */\nexport function findProvider<T extends Provider = Provider>(providers: Providers, name: string | RegExp): T | null {\n // Handle single provider case\n if (!Array.isArray(providers)) {\n if (typeof providers === \"object\" && 'provide' in providers) {\n const provider = providers as any\n const providerName = typeof provider.provide === \"function\"\n ? provider.provide.name \n : provider.provide;\n\n if (name instanceof RegExp) {\n if (name.test(providerName)) return providers as T;\n } else {\n if (providerName === name) return providers as T;\n }\n }\n return null;\n }\n\n // Original array handling logic\n for (const provider of providers) {\n // If provider is an array, recursively search in it\n if (Array.isArray(provider)) {\n const found = findProvider(provider, name);\n if (found) return found as T;\n continue;\n }\n\n // Check object provider\n if (typeof provider === \"object\" && 'provide' in provider) {\n const providerName = typeof provider.provide === \"function\" \n ? provider.provide.name \n : provider.provide;\n\n // Handle RegExp matching\n if (name instanceof RegExp) {\n if (name.test(providerName)) return provider as T;\n } else {\n // Handle exact string matching\n if (providerName === name) return provider as T;\n }\n }\n }\n return null;\n}\n","/**\n * Configuration merging functionality for dependency injection\n * @module @signe/di/merge-config\n */\n\nimport { findProvider, override } from \"./inject\";\n\n/**\n * Application configuration interface\n */\nexport interface AppConfig {\n /** Array of dependency providers */\n providers: any[];\n /** Optional static files configuration */\n staticFiles?: {\n /** Path to static files */\n path: string;\n /** Static file serving configuration */\n serve: any;\n }\n}\n\n/**\n * Process a provider or nested provider array and add it to the merged config\n * @param mergedConfig - Configuration being built\n * @param baseConfig - Original base configuration \n * @param provider - Provider or nested provider array to process\n */\nfunction processProvider(mergedConfig: AppConfig, baseConfig: AppConfig, provider: any) {\n // Handle nested arrays of providers\n if (Array.isArray(provider)) {\n for (const nestedProvider of provider) {\n processProvider(mergedConfig, baseConfig, nestedProvider);\n }\n return;\n }\n\n // Handle individual provider\n const existingProvider = findProvider(baseConfig.providers, provider.provide);\n if (existingProvider) {\n // Replace existing provider\n mergedConfig.providers = override(mergedConfig.providers, provider);\n } else {\n // Add new provider\n mergedConfig.providers.push(provider);\n }\n}\n\n/**\n * Merges two application configurations\n * @param baseConfig - Base configuration to merge into\n * @param config - Configuration to merge with base\n * @returns Merged configuration with providers properly combined\n */\nexport function mergeConfig(baseConfig: AppConfig, config: AppConfig): AppConfig {\n // Create a new config object with everything except providers\n const mergedConfig: AppConfig = {\n ...baseConfig,\n ...config,\n providers: [...baseConfig.providers] // Start with a copy of base providers\n }\n\n // Process each provider from the config to merge\n for (const provider of config.providers) {\n processProvider(mergedConfig, baseConfig, provider);\n }\n\n return mergedConfig;\n}\n","/**\n * Provider system implementation for dependency injection\n * @module @signe/di/provider\n */\n\nimport { ClassProvider, Provider, Providers, ProviderToken } from \"./types\";\nimport { inject, provide } from \"./inject\";\nimport { Context } from \"./context\";\n\n/**\n * Type guard to check if a provider is a ClassProvider\n * @param provider - Provider to check\n * @returns True if the provider is a ClassProvider\n */\nfunction isClassProvider(provider: Provider): provider is ClassProvider {\n if (typeof provider === 'string') {\n return false;\n }\n return 'useClass' in provider && typeof provider.useClass === 'function';\n}\n\n/**\n * Retrieves dependencies declared on a provider\n * @param provider - Provider to inspect\n * @returns Array of provider tokens this provider depends on\n */\nfunction getDeps(provider: Provider): ProviderToken[] {\n if (typeof provider === 'function') {\n return (provider as any).deps ?? [];\n }\n return (provider as any).deps ?? [];\n}\n\n/**\n * Sorts providers so that dependencies are instantiated first\n * @param providers - Array of providers to sort\n * @throws When a circular dependency is detected\n */\nfunction sortProviders(providers: Provider[]): Provider[] {\n const tokenName = (t: ProviderToken) => typeof t === 'function' ? t.name : t;\n const map = new Map<string, Provider>();\n for (const p of providers) {\n const token = tokenName(typeof p === 'function' ? p : (p as any).provide);\n map.set(token, p);\n }\n\n const result: Provider[] = [];\n const visited = new Set<string>();\n const stack = new Set<string>();\n\n const visit = (token: ProviderToken) => {\n const name = tokenName(token);\n if (visited.has(name)) return;\n if (stack.has(name)) {\n throw new Error(`Circular dependency detected for provider ${name}`);\n }\n stack.add(name);\n const provider = map.get(name);\n if (provider) {\n for (const dep of getDeps(provider)) {\n visit(dep);\n }\n visited.add(name);\n result.push(provider);\n }\n stack.delete(name);\n };\n\n for (const p of providers) {\n const token = typeof p === 'function' ? p : (p as any).provide;\n visit(token);\n }\n\n return result;\n}\n\n/**\n * Processes and instantiates all providers in the given context\n * @param context - The injection context\n * @param providers - Array of providers to process\n * @returns Promise that resolves when all providers are processed\n * \n * @example\n * ```typescript\n * const context = new Context();\n * const providers = [\n * UserService,\n * { provide: 'CONFIG', useValue: { apiUrl: 'http://api.example.com' } },\n * { provide: AuthService, useFactory: (ctx) => new AuthService(ctx) }\n * ];\n * \n * await injector(context, providers);\n * ```\n */\nexport async function injector(context: Context, providers: Providers) {\n providers = providers.flat();\n providers = sortProviders(providers as Provider[]);\n\n for (const provider of providers) {\n let token: ProviderToken;\n let instance: any;\n\n if (typeof provider === 'function') {\n // If provider is a class, treat it as a ClassProvider\n token = provider;\n instance = new provider(context);\n } else {\n token = (provider as any).provide;\n const provideUserClass = (provider as any).useClass;\n const isClass = typeof provideUserClass === 'function';\n if (isClass) {\n instance = new provideUserClass(context);\n } else if ('useValue' in provider) {\n instance = provider.useValue;\n } else if ('useFactory' in provider) {\n instance = provider.useFactory?.(context);\n if (instance instanceof Promise) {\n instance = await instance;\n }\n } else if ('useExisting' in provider) {\n instance = inject(context, provider.useExisting);\n }\n }\n\n const name = typeof token === 'function' ? token.name : token;\n provide(context, name, instance);\n }\n}","/**\n * Context class for managing dependency injection state\n * @module @signe/di/context\n */\n\n/**\n * Stores and manages the state of injected dependencies\n * @template TState - Type for the state\n * @template TActions - Type for the actions\n * @template TValues - Type for additional values\n */\nexport class Context<\n TState = any,\n TActions = any,\n TValues extends Record<string, any> = Record<string, any>\n> {\n /** Internal storage for injected values */\n private values: TValues = {} as TValues\n\n /**\n * Sets a value in the context\n * @param key - Unique identifier for the value\n * @param value - Value to store\n */\n set<K extends keyof TValues>(key: K, value: TValues[K]) {\n this.values[key] = value\n }\n\n /**\n * Retrieves a value from the context\n * @param key - Unique identifier for the value\n * @returns The stored value or undefined if not found\n */\n get<K extends keyof TValues>(key: K): TValues[K] {\n return this.values[key]\n }\n}"],"mappings":"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","names":["provide","context","name","value","set","isInjected","get","isProvided","undefined","inject","service","args","isClass","Error","override","providers","newProvider","options","upsert","key","flatProviders","flat","exists","some","provider","mappedProviders","map","push","findProviders","results","Array","isArray","findProvider","providerName","RegExp","test","found","processProvider","mergedConfig","baseConfig","provider","Array","isArray","nestedProvider","existingProvider","findProvider","providers","provide","override","push","mergeConfig","config","getDeps","provider","deps","sortProviders","providers","tokenName","t","name","map","Map","p","token","provide","set","result","visited","Set","stack","visit","has","Error","add","get","dep","push","delete","injector","context","flat","instance","provideUserClass","useClass","isClass","useValue","useFactory","Promise","inject","useExisting","Context","values","set","key","value","get"]}
1
+ {"version":3,"sources":["../src/inject.ts","../src/merge-config.ts","../src/provider.ts","../src/context.ts"],"sourcesContent":["/**\n * Core functionality for dependency injection\n * @module @signe/di/inject\n */\n\nimport { Context } from \"./context\";\nimport {\n InjectionOptions,\n InstanceLookupOptions,\n ProvideOptions,\n Provider,\n ProviderToken,\n Providers\n} from \"./types\";\n\nconst DEFAULT_INSTANCE_KEY = \"__default__\";\n\ninterface ProviderRecord {\n multi: boolean;\n values: Map<string, any>;\n injected: Set<string>;\n}\n\nfunction toTokenName(token: ProviderToken | string): string {\n return typeof token === \"function\" ? token.name : token;\n}\n\nfunction toInstanceKey(name?: string): string {\n return name ?? DEFAULT_INSTANCE_KEY;\n}\n\nfunction getRecord(context: Context, token: ProviderToken | string): ProviderRecord | undefined {\n return context.get(\"inject:\" + toTokenName(token));\n}\n\nfunction ensureRecord(context: Context, token: ProviderToken | string): ProviderRecord {\n const key = \"inject:\" + toTokenName(token);\n let record = context.get(key) as ProviderRecord | undefined;\n if (!record) {\n record = {\n multi: false,\n values: new Map<string, any>(),\n injected: new Set<string>()\n };\n }\n context.set(key, record);\n return record;\n}\n\n/**\n * Provides a value to the dependency injection context\n * @template T - Type of the value being provided\n * @param context - The injection context\n * @param token - Identifier for the provided value\n * @param value - The value to provide\n * @param options - Configuration options for the provided value\n * @returns The provided value\n */\nexport function provide<T>(\n context: Context,\n token: ProviderToken | string,\n value: T,\n options: ProvideOptions = {}\n): T {\n const record = ensureRecord(context, token);\n const instanceKey = toInstanceKey(options.name);\n\n if (options.multi) {\n record.multi = true;\n }\n\n if (!record.multi && instanceKey !== DEFAULT_INSTANCE_KEY) {\n // If we are switching from single to named instance without multi, enable multi mode implicitly\n record.multi = true;\n }\n\n record.values.set(instanceKey, value);\n return value;\n}\n\n/**\n * Checks if a service has been injected into the context\n * @param context - The injection context\n * @param token - Token of the service to check\n * @param options - Optional lookup configuration\n * @returns True if the service has been injected, false otherwise\n */\nexport function isInjected(\n context: Context,\n token: ProviderToken | string,\n options: InstanceLookupOptions = {}\n): boolean {\n const record = getRecord(context, token);\n if (!record) {\n return false;\n }\n\n if (options.name) {\n return record.injected.has(toInstanceKey(options.name));\n }\n\n if (record.multi) {\n return record.injected.size > 0;\n }\n\n return record.injected.has(DEFAULT_INSTANCE_KEY);\n}\n\n/**\n * Checks if a service has been provided in the context\n * @param context - The injection context\n * @param token - Token of the service to check\n * @param options - Optional lookup configuration\n * @returns True if the service has been provided, false otherwise\n */\nexport function isProvided(\n context: Context,\n token: ProviderToken | string,\n options: InstanceLookupOptions = {}\n): boolean {\n const record = getRecord(context, token);\n if (!record) {\n return false;\n }\n\n if (options.name) {\n return record.values.has(toInstanceKey(options.name));\n }\n\n if (record.multi) {\n return record.values.size > 0;\n }\n\n return record.values.has(DEFAULT_INSTANCE_KEY);\n}\n\n/**\n * Checks if an instance exists in the context\n * @param context - The injection context\n * @param token - Token of the service to check\n * @param options - Optional lookup configuration\n * @returns True if the instance exists, false otherwise\n */\nexport function hasInstance(\n context: Context,\n token: ProviderToken | string,\n options: InstanceLookupOptions = {}\n): boolean {\n return isProvided(context, token, options);\n}\n\nfunction handleMissingInjection(\n token: ProviderToken | string,\n options: InjectionOptions\n): never {\n const name = toTokenName(token);\n if (options.name) {\n throw new Error(`Injection provider ${name} with name ${options.name} not found`);\n }\n throw new Error(`Injection provider ${name} not found`);\n}\n\nfunction markInjected(record: ProviderRecord, key: string) {\n record.injected.add(key);\n}\n\nfunction markAllInjected(record: ProviderRecord) {\n for (const key of record.values.keys()) {\n record.injected.add(key);\n }\n}\n\nexport function inject<T>(context: Context, token: ProviderToken | string, options: InjectionOptions & { multi: true }): T[];\nexport function inject<T>(context: Context, token: ProviderToken | string, options: InjectionOptions & { optional: true }): T | undefined;\nexport function inject<T>(context: Context, token: ProviderToken | string, options?: InjectionOptions): T;\n/**\n * Retrieves a service from the dependency injection context\n * @template T - Type of the service to inject\n * @param context - The injection context\n * @param token - Class constructor or string identifier of the service\n * @param options - Optional configuration for resolving the service\n * @returns The injected service instance or `undefined` when optional\n * @throws {Error} If the requested service is not found in the context\n */\nexport function inject<T>(\n context: Context,\n token: ProviderToken | string,\n options: InjectionOptions = {}\n): T | T[] | undefined {\n const record = getRecord(context, token);\n\n if (!record) {\n if (options.optional) {\n return options.multi ? [] : undefined;\n }\n return handleMissingInjection(token, options);\n }\n\n if (options.name) {\n const instanceKey = toInstanceKey(options.name);\n if (!record.values.has(instanceKey)) {\n if (options.optional) {\n return undefined;\n }\n return handleMissingInjection(token, options);\n }\n const value = record.values.get(instanceKey);\n markInjected(record, instanceKey);\n return value as T;\n }\n\n if (options.multi || record.multi) {\n if (record.values.size === 0) {\n if (options.optional) {\n return [];\n }\n return handleMissingInjection(token, options);\n }\n markAllInjected(record);\n return Array.from(record.values.values()) as T[];\n }\n\n const value = record.values.get(DEFAULT_INSTANCE_KEY);\n if (value === undefined) {\n if (options.optional) {\n return undefined;\n }\n return handleMissingInjection(token, options);\n }\n\n markInjected(record, DEFAULT_INSTANCE_KEY);\n return value as T;\n}\n\n/**\n * Overrides or adds a provider in the providers array\n * @param providers - Array of existing providers\n * @param newProvider - Provider to add or replace with\n * @param options - Configuration options\n * @param options.upsert - If true, adds the provider when not found\n * @param options.key - Custom key to identify the provider\n * @returns Updated array of providers\n */\nexport function override(\n providers: Providers,\n newProvider: Provider,\n options?: {\n upsert?: boolean,\n key?: string\n }\n) {\n let { upsert = false, key } = options ?? {};\n if (!key) {\n key = (\n typeof newProvider === \"function\" ? newProvider.name : newProvider.provide\n ) as string;\n }\n\n // Flatten the providers array\n const flatProviders = providers.flat();\n\n // Check if provider exists\n const exists = flatProviders.some(provider => {\n if (typeof provider === \"function\") {\n return provider.name === key;\n } else if (typeof provider === \"object\") {\n return (provider as any).provide === key;\n }\n return false;\n });\n\n // Map and replace if found\n const mappedProviders = flatProviders.map((provider) => {\n if (typeof provider === \"function\" && provider.name === key) {\n return newProvider;\n } else if (typeof provider === \"object\" && (provider as any).provide === key) {\n return newProvider;\n }\n return provider;\n });\n\n // If upsert is true and provider wasn't found, add it to the end\n if (upsert && !exists) {\n mappedProviders.push(newProvider);\n }\n\n return mappedProviders;\n}\n\n/**\n * Finds all providers matching the given name or pattern\n * @template T - Type of provider to return\n * @param providers - Array of providers to search\n * @param name - String or RegExp to match against provider names\n * @returns Array of matching providers\n */\nexport function findProviders<T extends Provider = Provider>(providers: Providers, name: string | RegExp): T[] {\n const results: any[] = [];\n\n for (const provider of providers) {\n if (Array.isArray(provider)) {\n // Recursively search in nested arrays and concat results\n results.push(...findProviders(provider, name));\n } else if (findProvider(provider as any, name)) {\n // Add matching provider to results\n results.push(provider as T);\n }\n }\n\n return results;\n}\n\n/**\n * Finds the first provider matching the given name or pattern\n * @template T - Type of provider to return\n * @param providers - Array of providers to search\n * @param name - String or RegExp to match against provider names\n * @returns Matching provider or null if not found\n */\nexport function findProvider<T extends Provider = Provider>(providers: Providers, name: string | RegExp): T | null {\n // Handle single provider case\n if (!Array.isArray(providers)) {\n if (typeof providers === \"object\" && 'provide' in providers) {\n const provider = providers as any\n const providerName = typeof provider.provide === \"function\"\n ? provider.provide.name\n : provider.provide;\n\n if (name instanceof RegExp) {\n if (name.test(providerName)) return providers as T;\n } else {\n if (providerName === name) return providers as T;\n }\n }\n return null;\n }\n\n // Original array handling logic\n for (const provider of providers) {\n // If provider is an array, recursively search in it\n if (Array.isArray(provider)) {\n const found = findProvider(provider, name);\n if (found) return found as T;\n continue;\n }\n\n // Check object provider\n if (typeof provider === \"object\" && 'provide' in provider) {\n const providerName = typeof provider.provide === \"function\"\n ? provider.provide.name\n : provider.provide;\n\n // Handle RegExp matching\n if (name instanceof RegExp) {\n if (name.test(providerName)) return provider as T;\n } else {\n // Handle exact string matching\n if (providerName === name) return provider as T;\n }\n }\n }\n return null;\n}\n","/**\n * Configuration merging functionality for dependency injection\n * @module @signe/di/merge-config\n */\n\nimport { findProvider, override } from \"./inject\";\n\n/**\n * Application configuration interface\n */\nexport interface AppConfig {\n /** Array of dependency providers */\n providers: any[];\n /** Optional static files configuration */\n staticFiles?: {\n /** Path to static files */\n path: string;\n /** Static file serving configuration */\n serve: any;\n }\n}\n\n/**\n * Process a provider or nested provider array and add it to the merged config\n * @param mergedConfig - Configuration being built\n * @param baseConfig - Original base configuration \n * @param provider - Provider or nested provider array to process\n */\nfunction processProvider(mergedConfig: AppConfig, baseConfig: AppConfig, provider: any) {\n // Handle nested arrays of providers\n if (Array.isArray(provider)) {\n for (const nestedProvider of provider) {\n processProvider(mergedConfig, baseConfig, nestedProvider);\n }\n return;\n }\n\n // Handle individual provider\n const existingProvider = findProvider(baseConfig.providers, provider.provide);\n if (existingProvider) {\n // Replace existing provider\n mergedConfig.providers = override(mergedConfig.providers, provider);\n } else {\n // Add new provider\n mergedConfig.providers.push(provider);\n }\n}\n\n/**\n * Merges two application configurations\n * @param baseConfig - Base configuration to merge into\n * @param config - Configuration to merge with base\n * @returns Merged configuration with providers properly combined\n */\nexport function mergeConfig(baseConfig: AppConfig, config: AppConfig): AppConfig {\n // Create a new config object with everything except providers\n const mergedConfig: AppConfig = {\n ...baseConfig,\n ...config,\n providers: [...baseConfig.providers] // Start with a copy of base providers\n }\n\n // Process each provider from the config to merge\n for (const provider of config.providers) {\n processProvider(mergedConfig, baseConfig, provider);\n }\n\n return mergedConfig;\n}\n","/**\n * Provider system implementation for dependency injection\n * @module @signe/di/provider\n */\n\nimport { ClassProvider, ProvideOptions, Provider, Providers, ProviderToken } from \"./types\";\nimport { inject, provide } from \"./inject\";\nimport { Context } from \"./context\";\n\nfunction extractProvideOptions(source: { multi?: boolean; name?: string } | undefined): ProvideOptions | undefined {\n if (!source) {\n return undefined;\n }\n\n const { multi, name } = source;\n if (multi === undefined && name === undefined) {\n return undefined;\n }\n\n return { multi, name };\n}\n\n/**\n * Type guard to check if a provider is a ClassProvider\n * @param provider - Provider to check\n * @returns True if the provider is a ClassProvider\n */\nfunction isClassProvider(provider: Provider): provider is ClassProvider {\n if (typeof provider === 'string') {\n return false;\n }\n return 'useClass' in provider && typeof provider.useClass === 'function';\n}\n\n/**\n * Retrieves dependencies declared on a provider\n * @param provider - Provider to inspect\n * @returns Array of provider tokens this provider depends on\n */\nfunction getDeps(provider: Provider): ProviderToken[] {\n if (typeof provider === 'function') {\n return (provider as any).deps ?? [];\n }\n return (provider as any).deps ?? [];\n}\n\n/**\n * Sorts providers so that dependencies are instantiated first\n * @param providers - Array of providers to sort\n * @throws When a circular dependency is detected\n */\nfunction sortProviders(providers: Provider[]): Provider[] {\n const tokenName = (t: ProviderToken) => typeof t === 'function' ? t.name : t;\n const map = new Map<string, Provider[]>();\n for (const p of providers) {\n const token = tokenName(typeof p === 'function' ? p : (p as any).provide);\n const list = map.get(token);\n if (list) {\n list.push(p);\n } else {\n map.set(token, [p]);\n }\n }\n\n const result: Provider[] = [];\n const visited = new Set<string>();\n const stack = new Set<string>();\n\n const visit = (token: ProviderToken) => {\n const name = tokenName(token);\n if (visited.has(name)) return;\n if (stack.has(name)) {\n throw new Error(`Circular dependency detected for provider ${name}`);\n }\n stack.add(name);\n const providersForToken = map.get(name);\n if (providersForToken) {\n for (const provider of providersForToken) {\n for (const dep of getDeps(provider)) {\n visit(dep);\n }\n result.push(provider);\n }\n visited.add(name);\n }\n stack.delete(name);\n };\n\n for (const p of providers) {\n const token = typeof p === 'function' ? p : (p as any).provide;\n visit(token);\n }\n\n return result;\n}\n\n/**\n * Processes and instantiates all providers in the given context\n * @param context - The injection context\n * @param providers - Array of providers to process\n * @returns Promise that resolves when all providers are processed\n * \n * @example\n * ```typescript\n * const context = new Context();\n * const providers = [\n * UserService,\n * { provide: 'CONFIG', useValue: { apiUrl: 'http://api.example.com' } },\n * { provide: AuthService, useFactory: (ctx) => new AuthService(ctx) }\n * ];\n * \n * await injector(context, providers);\n * ```\n */\nexport async function injector(context: Context, providers: Providers) {\n providers = providers.flat();\n providers = sortProviders(providers as Provider[]);\n\n for (const provider of providers) {\n let token: ProviderToken;\n let instance: any;\n let options: ProvideOptions | undefined;\n\n if (typeof provider === 'function') {\n // If provider is a class, treat it as a ClassProvider\n token = provider;\n instance = new provider(context);\n const diOptions = extractProvideOptions((provider as any).diOptions ?? (provider as any).di);\n options = diOptions;\n } else {\n token = (provider as any).provide;\n options = extractProvideOptions(provider as any);\n const provideUserClass = (provider as any).useClass;\n const isClass = typeof provideUserClass === 'function';\n if (isClass) {\n instance = new provideUserClass(context);\n } else if ('useValue' in provider) {\n instance = provider.useValue;\n } else if ('useFactory' in provider) {\n instance = provider.useFactory?.(context);\n if (instance instanceof Promise) {\n instance = await instance;\n }\n } else if ('useExisting' in provider) {\n instance = inject(context, provider.useExisting);\n }\n }\n\n provide(context, token, instance, options);\n }\n}","/**\n * Context class for managing dependency injection state\n * @module @signe/di/context\n */\n\n/**\n * Stores and manages the state of injected dependencies\n * @template TState - Type for the state\n * @template TActions - Type for the actions\n * @template TValues - Type for additional values\n */\nexport class Context<\n TState = any,\n TActions = any,\n TValues extends Record<string, any> = Record<string, any>\n> {\n /** Internal storage for injected values */\n private values: TValues = {} as TValues\n\n /**\n * Sets a value in the context\n * @param key - Unique identifier for the value\n * @param value - Value to store\n */\n set<K extends keyof TValues>(key: K, value: TValues[K]) {\n this.values[key] = value\n }\n\n /**\n * Retrieves a value from the context\n * @param key - Unique identifier for the value\n * @returns The stored value or undefined if not found\n */\n get<K extends keyof TValues>(key: K): TValues[K] {\n return this.values[key]\n }\n}"],"mappings":"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","names":["DEFAULT_INSTANCE_KEY","toTokenName","token","name","toInstanceKey","getRecord","context","get","ensureRecord","key","record","multi","values","Map","injected","Set","set","provide","value","options","instanceKey","isInjected","has","size","isProvided","hasInstance","handleMissingInjection","Error","markInjected","add","markAllInjected","keys","inject","optional","undefined","Array","from","override","providers","newProvider","upsert","flatProviders","flat","exists","some","provider","mappedProviders","map","push","findProviders","results","isArray","findProvider","providerName","RegExp","test","found","processProvider","mergedConfig","baseConfig","provider","Array","isArray","nestedProvider","existingProvider","findProvider","providers","provide","override","push","mergeConfig","config","extractProvideOptions","source","undefined","multi","name","getDeps","provider","deps","sortProviders","providers","tokenName","t","name","map","Map","p","token","provide","list","get","push","set","result","visited","Set","stack","visit","has","Error","add","providersForToken","dep","delete","injector","context","flat","instance","options","diOptions","extractProvideOptions","di","provideUserClass","useClass","isClass","useValue","useFactory","Promise","inject","useExisting","Context","values","set","key","value","get"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signe/di",
3
- "version": "2.4.6",
3
+ "version": "2.5.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {
package/readme.md CHANGED
@@ -20,13 +20,15 @@ pnpm add @signe/di
20
20
  - Context-based injection
21
21
  - Override capabilities for testing and customization
22
22
  - Support for nested providers
23
+ - Optional injection resolution helpers
24
+ - Support for multiple named instances per token
23
25
 
24
26
  ## Usage
25
27
 
26
28
  ### Basic Usage
27
29
 
28
30
  ```typescript
29
- import { provide, inject, Context, Providers } from '@signe/di';
31
+ import { provide, inject, Context, Providers, injector } from '@signe/di';
30
32
 
31
33
  const context = new Context();
32
34
 
@@ -57,8 +59,8 @@ const providers: Providers = [
57
59
  }
58
60
  ];
59
61
 
60
- // Provide the service
61
- provide(context, providers);
62
+ // Register the services
63
+ await injector(context, providers);
62
64
 
63
65
  // Inject and use the service
64
66
  const userService = inject(context, UserService);
@@ -83,6 +85,36 @@ const updatedProviders = override(providers, {
83
85
  }, { upsert: true });
84
86
  ```
85
87
 
88
+ ### Optional injection
89
+
90
+ ```typescript
91
+ import { inject } from '@signe/di';
92
+
93
+ const maybeService = inject(context, 'UNKNOWN_SERVICE', { optional: true });
94
+ if (!maybeService) {
95
+ // Handle missing service without throwing an exception
96
+ }
97
+ ```
98
+
99
+ ### Multiple named instances
100
+
101
+ ```typescript
102
+ import { provide, inject } from '@signe/di';
103
+
104
+ provide(context, UserService, new UserService('primary'), {
105
+ multi: true,
106
+ name: 'primary'
107
+ });
108
+
109
+ provide(context, UserService, new UserService('secondary'), {
110
+ multi: true,
111
+ name: 'secondary'
112
+ });
113
+
114
+ const allInstances = inject<UserService>(context, UserService, { multi: true });
115
+ const secondary = inject<UserService>(context, UserService, { name: 'secondary' });
116
+ ```
117
+
86
118
  ### Check Injection Status
87
119
 
88
120
  ```typescript
@@ -93,6 +125,68 @@ if (isInjected(context, UserService)) {
93
125
  }
94
126
  ```
95
127
 
128
+ ### Dependency Declaration
129
+
130
+ You can declare dependencies using the `deps` property. The injector will automatically sort providers to ensure dependencies are instantiated before the services that need them.
131
+
132
+ ```typescript
133
+ import { provide, inject, Context, Providers, injector } from '@signe/di';
134
+
135
+ const context = new Context();
136
+
137
+ class DatabaseService {
138
+ connect() {
139
+ return 'Connected to database';
140
+ }
141
+ }
142
+
143
+ class UserRepository {
144
+ constructor(context: Context) {
145
+ this.db = inject(context, DatabaseService);
146
+ }
147
+
148
+ static deps = [DatabaseService];
149
+ }
150
+
151
+ class UserService {
152
+ constructor(context: Context) {
153
+ this.repository = inject(context, UserRepository);
154
+ }
155
+
156
+ static deps = [UserRepository];
157
+ }
158
+
159
+ const providers: Providers = [
160
+ UserService,
161
+ UserRepository,
162
+ DatabaseService
163
+ ];
164
+
165
+ // The injector will automatically sort: DatabaseService -> UserRepository -> UserService
166
+ await injector(context, providers);
167
+ ```
168
+
169
+ You can also declare dependencies on provider objects:
170
+
171
+ ```typescript
172
+ const providers: Providers = [
173
+ {
174
+ provide: 'API_CLIENT',
175
+ useFactory: (context) => {
176
+ const config = inject(context, 'CONFIG');
177
+ return new ApiClient(config);
178
+ },
179
+ deps: ['CONFIG']
180
+ },
181
+ {
182
+ provide: 'CONFIG',
183
+ useValue: { apiUrl: 'https://api.example.com' }
184
+ }
185
+ ];
186
+ ```
187
+
188
+ **Note:** The injector will detect and throw an error if circular dependencies are found.
189
+
96
190
  ### Find Providers
97
191
 
98
192
  ```typescript
@@ -107,14 +201,27 @@ const allServices = findProviders(providers, /Service$/);
107
201
 
108
202
  ## API Reference
109
203
 
110
- ### `provide(context, key, value)`
204
+ ### `provide(context, token, value, options?)`
111
205
  Stores a value in the context for dependency injection.
112
206
 
113
- ### `inject(context, key)`
207
+ - `options.multi` — When `true`, allows multiple instances for the same token
208
+ - `options.name` — Registers the instance under a specific name
209
+
210
+ ### `inject(context, token, options?)`
114
211
  Retrieves an injected value from the context.
115
212
 
116
- ### `isInjected(context, key)`
117
- Checks if a value has been injected.
213
+ - `options.optional` — Returns `undefined`/`[]` instead of throwing when missing
214
+ - `options.multi` Returns all registered instances as an array
215
+ - `options.name` — Retrieves a specific named instance
216
+
217
+ ### `isInjected(context, token, options?)`
218
+ Checks if a value has been injected. Supports named lookups via the `name` option.
219
+
220
+ ### `isProvided(context, token, options?)`
221
+ Checks if a value has been registered in the context.
222
+
223
+ ### `hasInstance(context, token, options?)`
224
+ Alias of `isProvided`, kept for readability when checking for instance existence.
118
225
 
119
226
  ### `override(providers, newProvider, options?)`
120
227
  Overrides or adds new providers to the existing provider array.
package/src/inject.ts CHANGED
@@ -3,64 +3,233 @@
3
3
  * @module @signe/di/inject
4
4
  */
5
5
 
6
- import { Provider, Providers } from "./types";
7
6
  import { Context } from "./context";
7
+ import {
8
+ InjectionOptions,
9
+ InstanceLookupOptions,
10
+ ProvideOptions,
11
+ Provider,
12
+ ProviderToken,
13
+ Providers
14
+ } from "./types";
15
+
16
+ const DEFAULT_INSTANCE_KEY = "__default__";
17
+
18
+ interface ProviderRecord {
19
+ multi: boolean;
20
+ values: Map<string, any>;
21
+ injected: Set<string>;
22
+ }
23
+
24
+ function toTokenName(token: ProviderToken | string): string {
25
+ return typeof token === "function" ? token.name : token;
26
+ }
27
+
28
+ function toInstanceKey(name?: string): string {
29
+ return name ?? DEFAULT_INSTANCE_KEY;
30
+ }
31
+
32
+ function getRecord(context: Context, token: ProviderToken | string): ProviderRecord | undefined {
33
+ return context.get("inject:" + toTokenName(token));
34
+ }
35
+
36
+ function ensureRecord(context: Context, token: ProviderToken | string): ProviderRecord {
37
+ const key = "inject:" + toTokenName(token);
38
+ let record = context.get(key) as ProviderRecord | undefined;
39
+ if (!record) {
40
+ record = {
41
+ multi: false,
42
+ values: new Map<string, any>(),
43
+ injected: new Set<string>()
44
+ };
45
+ }
46
+ context.set(key, record);
47
+ return record;
48
+ }
8
49
 
9
50
  /**
10
51
  * Provides a value to the dependency injection context
11
52
  * @template T - Type of the value being provided
12
53
  * @param context - The injection context
13
- * @param name - Identifier for the provided value
54
+ * @param token - Identifier for the provided value
14
55
  * @param value - The value to provide
56
+ * @param options - Configuration options for the provided value
15
57
  * @returns The provided value
16
58
  */
17
- export function provide<T>(context: Context, name: string, value: T): T {
18
- context.set("inject:" + name, value);
59
+ export function provide<T>(
60
+ context: Context,
61
+ token: ProviderToken | string,
62
+ value: T,
63
+ options: ProvideOptions = {}
64
+ ): T {
65
+ const record = ensureRecord(context, token);
66
+ const instanceKey = toInstanceKey(options.name);
67
+
68
+ if (options.multi) {
69
+ record.multi = true;
70
+ }
71
+
72
+ if (!record.multi && instanceKey !== DEFAULT_INSTANCE_KEY) {
73
+ // If we are switching from single to named instance without multi, enable multi mode implicitly
74
+ record.multi = true;
75
+ }
76
+
77
+ record.values.set(instanceKey, value);
19
78
  return value;
20
79
  }
21
80
 
22
81
  /**
23
82
  * Checks if a service has been injected into the context
24
83
  * @param context - The injection context
25
- * @param name - Name of the service to check
84
+ * @param token - Token of the service to check
85
+ * @param options - Optional lookup configuration
26
86
  * @returns True if the service has been injected, false otherwise
27
87
  */
28
- export function isInjected(context: Context, name: string): boolean {
29
- return context.get('injected:' + name) === true;
88
+ export function isInjected(
89
+ context: Context,
90
+ token: ProviderToken | string,
91
+ options: InstanceLookupOptions = {}
92
+ ): boolean {
93
+ const record = getRecord(context, token);
94
+ if (!record) {
95
+ return false;
96
+ }
97
+
98
+ if (options.name) {
99
+ return record.injected.has(toInstanceKey(options.name));
100
+ }
101
+
102
+ if (record.multi) {
103
+ return record.injected.size > 0;
104
+ }
105
+
106
+ return record.injected.has(DEFAULT_INSTANCE_KEY);
30
107
  }
31
108
 
32
109
  /**
33
110
  * Checks if a service has been provided in the context
34
111
  * @param context - The injection context
35
- * @param name - Name of the service to check
112
+ * @param token - Token of the service to check
113
+ * @param options - Optional lookup configuration
36
114
  * @returns True if the service has been provided, false otherwise
37
115
  */
38
- export function isProvided(context: Context, name: string): boolean {
39
- return context.get('inject:' + name) !== undefined;
116
+ export function isProvided(
117
+ context: Context,
118
+ token: ProviderToken | string,
119
+ options: InstanceLookupOptions = {}
120
+ ): boolean {
121
+ const record = getRecord(context, token);
122
+ if (!record) {
123
+ return false;
124
+ }
125
+
126
+ if (options.name) {
127
+ return record.values.has(toInstanceKey(options.name));
128
+ }
129
+
130
+ if (record.multi) {
131
+ return record.values.size > 0;
132
+ }
133
+
134
+ return record.values.has(DEFAULT_INSTANCE_KEY);
135
+ }
136
+
137
+ /**
138
+ * Checks if an instance exists in the context
139
+ * @param context - The injection context
140
+ * @param token - Token of the service to check
141
+ * @param options - Optional lookup configuration
142
+ * @returns True if the instance exists, false otherwise
143
+ */
144
+ export function hasInstance(
145
+ context: Context,
146
+ token: ProviderToken | string,
147
+ options: InstanceLookupOptions = {}
148
+ ): boolean {
149
+ return isProvided(context, token, options);
150
+ }
151
+
152
+ function handleMissingInjection(
153
+ token: ProviderToken | string,
154
+ options: InjectionOptions
155
+ ): never {
156
+ const name = toTokenName(token);
157
+ if (options.name) {
158
+ throw new Error(`Injection provider ${name} with name ${options.name} not found`);
159
+ }
160
+ throw new Error(`Injection provider ${name} not found`);
161
+ }
162
+
163
+ function markInjected(record: ProviderRecord, key: string) {
164
+ record.injected.add(key);
40
165
  }
41
166
 
167
+ function markAllInjected(record: ProviderRecord) {
168
+ for (const key of record.values.keys()) {
169
+ record.injected.add(key);
170
+ }
171
+ }
172
+
173
+ export function inject<T>(context: Context, token: ProviderToken | string, options: InjectionOptions & { multi: true }): T[];
174
+ export function inject<T>(context: Context, token: ProviderToken | string, options: InjectionOptions & { optional: true }): T | undefined;
175
+ export function inject<T>(context: Context, token: ProviderToken | string, options?: InjectionOptions): T;
42
176
  /**
43
177
  * Retrieves a service from the dependency injection context
44
178
  * @template T - Type of the service to inject
45
179
  * @param context - The injection context
46
- * @param service - Class constructor or string identifier of the service
47
- * @param args - Optional arguments for service instantiation
48
- * @returns The injected service instance
180
+ * @param token - Class constructor or string identifier of the service
181
+ * @param options - Optional configuration for resolving the service
182
+ * @returns The injected service instance or `undefined` when optional
49
183
  * @throws {Error} If the requested service is not found in the context
50
184
  */
51
185
  export function inject<T>(
52
186
  context: Context,
53
- service: (new (...args: any[]) => T) | string,
54
- args: any[] = []
55
- ): T {
56
- const isClass = typeof service === "function";
57
- const name = isClass ? service.name : service;
58
- const value = context.get("inject:" + name);
59
- if (value) {
60
- context.set('injected:' + name, true)
187
+ token: ProviderToken | string,
188
+ options: InjectionOptions = {}
189
+ ): T | T[] | undefined {
190
+ const record = getRecord(context, token);
191
+
192
+ if (!record) {
193
+ if (options.optional) {
194
+ return options.multi ? [] : undefined;
195
+ }
196
+ return handleMissingInjection(token, options);
197
+ }
198
+
199
+ if (options.name) {
200
+ const instanceKey = toInstanceKey(options.name);
201
+ if (!record.values.has(instanceKey)) {
202
+ if (options.optional) {
203
+ return undefined;
204
+ }
205
+ return handleMissingInjection(token, options);
206
+ }
207
+ const value = record.values.get(instanceKey);
208
+ markInjected(record, instanceKey);
61
209
  return value as T;
62
210
  }
63
- throw new Error(`Injection provider ${name} not found`);
211
+
212
+ if (options.multi || record.multi) {
213
+ if (record.values.size === 0) {
214
+ if (options.optional) {
215
+ return [];
216
+ }
217
+ return handleMissingInjection(token, options);
218
+ }
219
+ markAllInjected(record);
220
+ return Array.from(record.values.values()) as T[];
221
+ }
222
+
223
+ const value = record.values.get(DEFAULT_INSTANCE_KEY);
224
+ if (value === undefined) {
225
+ if (options.optional) {
226
+ return undefined;
227
+ }
228
+ return handleMissingInjection(token, options);
229
+ }
230
+
231
+ markInjected(record, DEFAULT_INSTANCE_KEY);
232
+ return value as T;
64
233
  }
65
234
 
66
235
  /**
@@ -89,7 +258,7 @@ export function override(
89
258
 
90
259
  // Flatten the providers array
91
260
  const flatProviders = providers.flat();
92
-
261
+
93
262
  // Check if provider exists
94
263
  const exists = flatProviders.some(provider => {
95
264
  if (typeof provider === "function") {
@@ -127,7 +296,7 @@ export function override(
127
296
  */
128
297
  export function findProviders<T extends Provider = Provider>(providers: Providers, name: string | RegExp): T[] {
129
298
  const results: any[] = [];
130
-
299
+
131
300
  for (const provider of providers) {
132
301
  if (Array.isArray(provider)) {
133
302
  // Recursively search in nested arrays and concat results
@@ -137,7 +306,7 @@ export function findProviders<T extends Provider = Provider>(providers: Provider
137
306
  results.push(provider as T);
138
307
  }
139
308
  }
140
-
309
+
141
310
  return results;
142
311
  }
143
312
 
@@ -154,7 +323,7 @@ export function findProvider<T extends Provider = Provider>(providers: Providers
154
323
  if (typeof providers === "object" && 'provide' in providers) {
155
324
  const provider = providers as any
156
325
  const providerName = typeof provider.provide === "function"
157
- ? provider.provide.name
326
+ ? provider.provide.name
158
327
  : provider.provide;
159
328
 
160
329
  if (name instanceof RegExp) {
@@ -177,8 +346,8 @@ export function findProvider<T extends Provider = Provider>(providers: Providers
177
346
 
178
347
  // Check object provider
179
348
  if (typeof provider === "object" && 'provide' in provider) {
180
- const providerName = typeof provider.provide === "function"
181
- ? provider.provide.name
349
+ const providerName = typeof provider.provide === "function"
350
+ ? provider.provide.name
182
351
  : provider.provide;
183
352
 
184
353
  // Handle RegExp matching
package/src/provider.ts CHANGED
@@ -3,10 +3,23 @@
3
3
  * @module @signe/di/provider
4
4
  */
5
5
 
6
- import { ClassProvider, Provider, Providers, ProviderToken } from "./types";
6
+ import { ClassProvider, ProvideOptions, Provider, Providers, ProviderToken } from "./types";
7
7
  import { inject, provide } from "./inject";
8
8
  import { Context } from "./context";
9
9
 
10
+ function extractProvideOptions(source: { multi?: boolean; name?: string } | undefined): ProvideOptions | undefined {
11
+ if (!source) {
12
+ return undefined;
13
+ }
14
+
15
+ const { multi, name } = source;
16
+ if (multi === undefined && name === undefined) {
17
+ return undefined;
18
+ }
19
+
20
+ return { multi, name };
21
+ }
22
+
10
23
  /**
11
24
  * Type guard to check if a provider is a ClassProvider
12
25
  * @param provider - Provider to check
@@ -38,10 +51,15 @@ function getDeps(provider: Provider): ProviderToken[] {
38
51
  */
39
52
  function sortProviders(providers: Provider[]): Provider[] {
40
53
  const tokenName = (t: ProviderToken) => typeof t === 'function' ? t.name : t;
41
- const map = new Map<string, Provider>();
54
+ const map = new Map<string, Provider[]>();
42
55
  for (const p of providers) {
43
56
  const token = tokenName(typeof p === 'function' ? p : (p as any).provide);
44
- map.set(token, p);
57
+ const list = map.get(token);
58
+ if (list) {
59
+ list.push(p);
60
+ } else {
61
+ map.set(token, [p]);
62
+ }
45
63
  }
46
64
 
47
65
  const result: Provider[] = [];
@@ -55,13 +73,15 @@ function sortProviders(providers: Provider[]): Provider[] {
55
73
  throw new Error(`Circular dependency detected for provider ${name}`);
56
74
  }
57
75
  stack.add(name);
58
- const provider = map.get(name);
59
- if (provider) {
60
- for (const dep of getDeps(provider)) {
61
- visit(dep);
76
+ const providersForToken = map.get(name);
77
+ if (providersForToken) {
78
+ for (const provider of providersForToken) {
79
+ for (const dep of getDeps(provider)) {
80
+ visit(dep);
81
+ }
82
+ result.push(provider);
62
83
  }
63
84
  visited.add(name);
64
- result.push(provider);
65
85
  }
66
86
  stack.delete(name);
67
87
  };
@@ -99,13 +119,17 @@ export async function injector(context: Context, providers: Providers) {
99
119
  for (const provider of providers) {
100
120
  let token: ProviderToken;
101
121
  let instance: any;
122
+ let options: ProvideOptions | undefined;
102
123
 
103
124
  if (typeof provider === 'function') {
104
125
  // If provider is a class, treat it as a ClassProvider
105
126
  token = provider;
106
127
  instance = new provider(context);
128
+ const diOptions = extractProvideOptions((provider as any).diOptions ?? (provider as any).di);
129
+ options = diOptions;
107
130
  } else {
108
131
  token = (provider as any).provide;
132
+ options = extractProvideOptions(provider as any);
109
133
  const provideUserClass = (provider as any).useClass;
110
134
  const isClass = typeof provideUserClass === 'function';
111
135
  if (isClass) {
@@ -122,7 +146,6 @@ export async function injector(context: Context, providers: Providers) {
122
146
  }
123
147
  }
124
148
 
125
- const name = typeof token === 'function' ? token.name : token;
126
- provide(context, name, instance);
149
+ provide(context, token, instance, options);
127
150
  }
128
151
  }
package/src/types.ts CHANGED
@@ -27,7 +27,54 @@ export interface ProviderMeta {
27
27
  [key: string]: any;
28
28
  }
29
29
 
30
- export interface ValueProvider {
30
+ /**
31
+ * Common options available to all providers
32
+ */
33
+ export interface ProviderOptions {
34
+ /**
35
+ * When true, allows multiple instances to be registered for the same token
36
+ */
37
+ multi?: boolean;
38
+ /**
39
+ * Optional name used to register and resolve a specific instance
40
+ */
41
+ name?: string;
42
+ }
43
+
44
+ /**
45
+ * Options that can be passed when calling {@link provide}
46
+ */
47
+ export interface ProvideOptions extends ProviderOptions {}
48
+
49
+ /**
50
+ * Options that can be passed when calling {@link inject}
51
+ */
52
+ export interface InjectionOptions {
53
+ /**
54
+ * Optional name used to resolve a specific instance
55
+ */
56
+ name?: string;
57
+ /**
58
+ * When true, allows retrieving all instances registered with {@link ProviderOptions.multi}
59
+ */
60
+ multi?: boolean;
61
+ /**
62
+ * When true, `inject` will return `undefined` instead of throwing if the instance is missing
63
+ */
64
+ optional?: boolean;
65
+ }
66
+
67
+ /**
68
+ * Options used when checking if an instance exists in the context
69
+ */
70
+ export interface InstanceLookupOptions {
71
+ /**
72
+ * Optional name of the instance to check
73
+ */
74
+ name?: string;
75
+ }
76
+
77
+ export interface ValueProvider extends ProviderOptions {
31
78
  /** Token to identify the provider */
32
79
  provide: ProviderToken;
33
80
  /** Value to be injected */
@@ -41,7 +88,7 @@ export interface ValueProvider {
41
88
  /**
42
89
  * Provider configuration for class-based injection
43
90
  */
44
- export interface ClassProvider {
91
+ export interface ClassProvider extends ProviderOptions {
45
92
  /** Token to identify the provider */
46
93
  provide: ProviderToken;
47
94
  /** Class to be instantiated */
@@ -55,7 +102,7 @@ export interface ClassProvider {
55
102
  /**
56
103
  * Provider configuration for factory-based injection
57
104
  */
58
- export interface FactoryProvider {
105
+ export interface FactoryProvider extends ProviderOptions {
59
106
  /** Token to identify the provider */
60
107
  provide: ProviderToken;
61
108
  /** Tokens that must be injected before this provider */
@@ -69,7 +116,7 @@ export interface FactoryProvider {
69
116
  /**
70
117
  * Provider configuration for alias-based injection
71
118
  */
72
- export interface ExistingProvider {
119
+ export interface ExistingProvider extends ProviderOptions {
73
120
  /** Token to identify the provider */
74
121
  provide: ProviderToken;
75
122
  /** Token of the existing provider to use */