@signe/di 2.3.3 → 2.4.1

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
@@ -19,11 +19,18 @@ type FactoryFn = (context: ProviderContext) => any;
19
19
  /**
20
20
  * Provider configuration for value-based injection
21
21
  */
22
+ interface ProviderMeta {
23
+ [key: string]: any;
24
+ }
22
25
  interface ValueProvider {
23
26
  /** Token to identify the provider */
24
27
  provide: ProviderToken;
25
28
  /** Value to be injected */
26
29
  useValue: any;
30
+ /** Tokens that must be injected before this provider */
31
+ deps?: ProviderToken[];
32
+ /** Optional metadata for the provider */
33
+ meta?: ProviderMeta;
27
34
  }
28
35
  /**
29
36
  * Provider configuration for class-based injection
@@ -33,6 +40,10 @@ interface ClassProvider {
33
40
  provide: ProviderToken;
34
41
  /** Class to be instantiated */
35
42
  useClass: new (context: ProviderContext) => any;
43
+ /** Tokens that must be injected before this provider */
44
+ deps?: ProviderToken[];
45
+ /** Optional metadata for the provider */
46
+ meta?: ProviderMeta;
36
47
  }
37
48
  /**
38
49
  * Provider configuration for factory-based injection
@@ -40,10 +51,10 @@ interface ClassProvider {
40
51
  interface FactoryProvider {
41
52
  /** Token to identify the provider */
42
53
  provide: ProviderToken;
54
+ /** Tokens that must be injected before this provider */
55
+ deps?: ProviderToken[];
43
56
  /** Optional metadata for the provider */
44
- meta?: {
45
- [key: string]: any;
46
- };
57
+ meta?: ProviderMeta;
47
58
  /** Factory function to create the instance */
48
59
  useFactory: FactoryFn;
49
60
  }
@@ -55,6 +66,10 @@ interface ExistingProvider {
55
66
  provide: ProviderToken;
56
67
  /** Token of the existing provider to use */
57
68
  useExisting: ProviderToken;
69
+ /** Tokens that must be injected before this provider */
70
+ deps?: ProviderToken[];
71
+ /** Optional metadata for the provider */
72
+ meta?: ProviderMeta;
58
73
  }
59
74
  /**
60
75
  * Union type for all possible provider configurations
@@ -227,4 +242,4 @@ declare function mergeConfig(baseConfig: AppConfig, config: AppConfig): AppConfi
227
242
  */
228
243
  declare function injector(context: Context, providers: Providers): Promise<void>;
229
244
 
230
- export { type AppConfig, type ClassProvider, Context, type ExistingProvider, type FactoryFn, type FactoryProvider, type Provider, type ProviderContext, type ProviderToken, type Providers, type ValueProvider, findProvider, findProviders, inject, injector, isInjected, isProvided, mergeConfig, override, provide };
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 };
package/dist/index.js CHANGED
@@ -131,8 +131,50 @@ function mergeConfig(baseConfig, config) {
131
131
  __name(mergeConfig, "mergeConfig");
132
132
 
133
133
  // src/provider.ts
134
+ function getDeps(provider) {
135
+ if (typeof provider === "function") {
136
+ return provider.deps ?? [];
137
+ }
138
+ return provider.deps ?? [];
139
+ }
140
+ __name(getDeps, "getDeps");
141
+ function sortProviders(providers) {
142
+ const tokenName = /* @__PURE__ */ __name((t) => typeof t === "function" ? t.name : t, "tokenName");
143
+ const map = /* @__PURE__ */ new Map();
144
+ for (const p of providers) {
145
+ const token = tokenName(typeof p === "function" ? p : p.provide);
146
+ map.set(token, p);
147
+ }
148
+ const result = [];
149
+ const visited = /* @__PURE__ */ new Set();
150
+ const stack = /* @__PURE__ */ new Set();
151
+ const visit = /* @__PURE__ */ __name((token) => {
152
+ const name = tokenName(token);
153
+ if (visited.has(name)) return;
154
+ if (stack.has(name)) {
155
+ throw new Error(`Circular dependency detected for provider ${name}`);
156
+ }
157
+ stack.add(name);
158
+ const provider = map.get(name);
159
+ if (provider) {
160
+ for (const dep of getDeps(provider)) {
161
+ visit(dep);
162
+ }
163
+ visited.add(name);
164
+ result.push(provider);
165
+ }
166
+ stack.delete(name);
167
+ }, "visit");
168
+ for (const p of providers) {
169
+ const token = typeof p === "function" ? p : p.provide;
170
+ visit(token);
171
+ }
172
+ return result;
173
+ }
174
+ __name(sortProviders, "sortProviders");
134
175
  async function injector(context, providers) {
135
176
  providers = providers.flat();
177
+ providers = sortProviders(providers);
136
178
  for (const provider of providers) {
137
179
  let token;
138
180
  let instance;
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 * 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 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","injector","context","providers","flat","provider","token","instance","provide","provideUserClass","useClass","isClass","useValue","useFactory","Promise","inject","useExisting","name","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 { 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":";;;;AAgBO,SAASA,QAAWC,SAAkBC,MAAcC,OAAQ;AACjEF,UAAQG,IAAI,YAAYF,MAAMC,KAAAA;AAC9B,SAAOA;AACT;AAHgBH;AAWT,SAASK,WAAWJ,SAAkBC,MAAY;AACvD,SAAOD,QAAQK,IAAI,cAAcJ,IAAAA,MAAU;AAC7C;AAFgBG;AAUT,SAASE,WAAWN,SAAkBC,MAAY;AACvD,SAAOD,QAAQK,IAAI,YAAYJ,IAAAA,MAAUM;AAC3C;AAFgBD;AAaT,SAASE,OACdR,SACAS,SACAC,OAAc,CAAA,GAAE;AAEhB,QAAMC,UAAU,OAAOF,YAAY;AACnC,QAAMR,OAAOU,UAAUF,QAAQR,OAAOQ;AACtC,QAAMP,QAAQF,QAAQK,IAAI,YAAYJ,IAAAA;AACtC,MAAIC,OAAO;AACTF,YAAQG,IAAI,cAAcF,MAAM,IAAA;AAChC,WAAOC;EACT;AACA,QAAM,IAAIU,MAAM,sBAAsBX,IAAAA,YAAgB;AACxD;AAbgBO;AAwBT,SAASK,SACdC,WACAC,aACAC,SAGC;AAED,MAAI,EAAEC,SAAS,OAAOC,IAAG,IAAKF,WAAW,CAAC;AAC1C,MAAI,CAACE,KAAK;AACRA,UACE,OAAOH,gBAAgB,aAAaA,YAAYd,OAAOc,YAAYhB;EAEvE;AAGA,QAAMoB,gBAAgBL,UAAUM,KAAI;AAGpC,QAAMC,SAASF,cAAcG,KAAKC,CAAAA,aAAAA;AAChC,QAAI,OAAOA,aAAa,YAAY;AAClC,aAAOA,SAAStB,SAASiB;IAC3B,WAAW,OAAOK,aAAa,UAAU;AACvC,aAAQA,SAAiBxB,YAAYmB;IACvC;AACA,WAAO;EACT,CAAA;AAGA,QAAMM,kBAAkBL,cAAcM,IAAI,CAACF,aAAAA;AACzC,QAAI,OAAOA,aAAa,cAAcA,SAAStB,SAASiB,KAAK;AAC3D,aAAOH;IACT,WAAW,OAAOQ,aAAa,YAAaA,SAAiBxB,YAAYmB,KAAK;AAC5E,aAAOH;IACT;AACA,WAAOQ;EACT,CAAA;AAGA,MAAIN,UAAU,CAACI,QAAQ;AACrBG,oBAAgBE,KAAKX,WAAAA;EACvB;AAEA,SAAOS;AACT;AA5CgBX;AAqDT,SAASc,cAA6Cb,WAAsBb,MAAqB;AACtG,QAAM2B,UAAiB,CAAA;AAEvB,aAAWL,YAAYT,WAAW;AAChC,QAAIe,MAAMC,QAAQP,QAAAA,GAAW;AAE3BK,cAAQF,KAAI,GAAIC,cAAcJ,UAAUtB,IAAAA,CAAAA;IAC1C,WAAW8B,aAAaR,UAAiBtB,IAAAA,GAAO;AAE9C2B,cAAQF,KAAKH,QAAAA;IACf;EACF;AAEA,SAAOK;AACT;AAdgBD;AAuBT,SAASI,aAA4CjB,WAAsBb,MAAqB;AAErG,MAAI,CAAC4B,MAAMC,QAAQhB,SAAAA,GAAY;AAC7B,QAAI,OAAOA,cAAc,YAAY,aAAaA,WAAW;AAC3D,YAAMS,WAAWT;AACjB,YAAMkB,eAAe,OAAOT,SAASxB,YAAY,aAC7CwB,SAASxB,QAAQE,OACjBsB,SAASxB;AAEb,UAAIE,gBAAgBgC,QAAQ;AAC1B,YAAIhC,KAAKiC,KAAKF,YAAAA,EAAe,QAAOlB;MACtC,OAAO;AACL,YAAIkB,iBAAiB/B,KAAM,QAAOa;MACpC;IACF;AACA,WAAO;EACT;AAGA,aAAWS,YAAYT,WAAW;AAEhC,QAAIe,MAAMC,QAAQP,QAAAA,GAAW;AAC3B,YAAMY,QAAQJ,aAAaR,UAAUtB,IAAAA;AACrC,UAAIkC,MAAO,QAAOA;AAClB;IACF;AAGA,QAAI,OAAOZ,aAAa,YAAY,aAAaA,UAAU;AACzD,YAAMS,eAAe,OAAOT,SAASxB,YAAY,aAC7CwB,SAASxB,QAAQE,OACjBsB,SAASxB;AAGb,UAAIE,gBAAgBgC,QAAQ;AAC1B,YAAIhC,KAAKiC,KAAKF,YAAAA,EAAe,QAAOT;MACtC,OAAO;AAEL,YAAIS,iBAAiB/B,KAAM,QAAOsB;MACpC;IACF;EACF;AACA,SAAO;AACT;AA3CgBQ;;;AC1HhB,SAASK,gBAAgBC,cAAyBC,YAAuBC,UAAa;AAElF,MAAIC,MAAMC,QAAQF,QAAAA,GAAW;AACzB,eAAWG,kBAAkBH,UAAU;AACnCH,sBAAgBC,cAAcC,YAAYI,cAAAA;IAC9C;AACA;EACJ;AAGA,QAAMC,mBAAmBC,aAAaN,WAAWO,WAAWN,SAASO,OAAO;AAC5E,MAAIH,kBAAkB;AAElBN,iBAAaQ,YAAYE,SAASV,aAAaQ,WAAWN,QAAAA;EAC9D,OAAO;AAEHF,iBAAaQ,UAAUG,KAAKT,QAAAA;EAChC;AACJ;AAlBSH;AA0BF,SAASa,YAAYX,YAAuBY,QAAiB;AAEhE,QAAMb,eAA0B;IAC5B,GAAGC;IACH,GAAGY;IACHL,WAAW;SAAIP,WAAWO;;;EAC9B;AAGA,aAAWN,YAAYW,OAAOL,WAAW;AACrCT,oBAAgBC,cAAcC,YAAYC,QAAAA;EAC9C;AAEA,SAAOF;AACX;AAdgBY;;;AC5BhB,SAASE,QAAQC,UAAkB;AAC/B,MAAI,OAAOA,aAAa,YAAY;AAChC,WAAQA,SAAiBC,QAAQ,CAAA;EACrC;AACA,SAAQD,SAAiBC,QAAQ,CAAA;AACrC;AALSF;AAYT,SAASG,cAAcC,WAAqB;AACxC,QAAMC,YAAY,wBAACC,MAAqB,OAAOA,MAAM,aAAaA,EAAEC,OAAOD,GAAzD;AAClB,QAAME,MAAM,oBAAIC,IAAAA;AAChB,aAAWC,KAAKN,WAAW;AACvB,UAAMO,QAAQN,UAAU,OAAOK,MAAM,aAAaA,IAAKA,EAAUE,OAAO;AACxEJ,QAAIK,IAAIF,OAAOD,CAAAA;EACnB;AAEA,QAAMI,SAAqB,CAAA;AAC3B,QAAMC,UAAU,oBAAIC,IAAAA;AACpB,QAAMC,QAAQ,oBAAID,IAAAA;AAElB,QAAME,QAAQ,wBAACP,UAAAA;AACX,UAAMJ,OAAOF,UAAUM,KAAAA;AACvB,QAAII,QAAQI,IAAIZ,IAAAA,EAAO;AACvB,QAAIU,MAAME,IAAIZ,IAAAA,GAAO;AACjB,YAAM,IAAIa,MAAM,6CAA6Cb,IAAAA,EAAM;IACvE;AACAU,UAAMI,IAAId,IAAAA;AACV,UAAMN,WAAWO,IAAIc,IAAIf,IAAAA;AACzB,QAAIN,UAAU;AACV,iBAAWsB,OAAOvB,QAAQC,QAAAA,GAAW;AACjCiB,cAAMK,GAAAA;MACV;AACAR,cAAQM,IAAId,IAAAA;AACZO,aAAOU,KAAKvB,QAAAA;IAChB;AACAgB,UAAMQ,OAAOlB,IAAAA;EACjB,GAhBc;AAkBd,aAAWG,KAAKN,WAAW;AACvB,UAAMO,QAAQ,OAAOD,MAAM,aAAaA,IAAKA,EAAUE;AACvDM,UAAMP,KAAAA;EACV;AAEA,SAAOG;AACX;AApCSX;AAwDT,eAAsBuB,SAASC,SAAkBvB,WAAoB;AACjEA,cAAYA,UAAUwB,KAAI;AAC1BxB,cAAYD,cAAcC,SAAAA;AAE1B,aAAWH,YAAYG,WAAW;AAC9B,QAAIO;AACJ,QAAIkB;AAEJ,QAAI,OAAO5B,aAAa,YAAY;AAEhCU,cAAQV;AACR4B,iBAAW,IAAI5B,SAAS0B,OAAAA;IAC5B,OAAO;AACHhB,cAASV,SAAiBW;AAC1B,YAAMkB,mBAAoB7B,SAAiB8B;AAC3C,YAAMC,UAAU,OAAOF,qBAAqB;AAC5C,UAAIE,SAAS;AACTH,mBAAW,IAAIC,iBAAiBH,OAAAA;MACpC,WAAW,cAAc1B,UAAU;AAC/B4B,mBAAW5B,SAASgC;MACxB,WAAW,gBAAgBhC,UAAU;AACjC4B,mBAAW5B,SAASiC,aAAaP,OAAAA;AACjC,YAAIE,oBAAoBM,SAAS;AAC7BN,qBAAW,MAAMA;QACrB;MACJ,WAAW,iBAAiB5B,UAAU;AAClC4B,mBAAWO,OAAOT,SAAS1B,SAASoC,WAAW;MACnD;IACJ;AAEA,UAAM9B,OAAO,OAAOI,UAAU,aAAaA,MAAMJ,OAAOI;AACxDC,YAAQe,SAASpB,MAAMsB,QAAAA;EAC3B;AACJ;AAjCsBH;;;ACnFf,IAAMY,UAAN,MAAMA;EAXb,OAWaA;;;;EAMDC,SAAkB,CAAC;;;;;;EAO3BC,IAA6BC,KAAQC,OAAmB;AACpD,SAAKH,OAAOE,GAAAA,IAAOC;EACvB;;;;;;EAOAC,IAA6BF,KAAoB;AAC7C,WAAO,KAAKF,OAAOE,GAAAA;EACvB;AACJ;","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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signe/di",
3
- "version": "2.3.3",
3
+ "version": "2.4.1",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {
package/src/provider.ts CHANGED
@@ -19,6 +19,61 @@ function isClassProvider(provider: Provider): provider is ClassProvider {
19
19
  return 'useClass' in provider && typeof provider.useClass === 'function';
20
20
  }
21
21
 
22
+ /**
23
+ * Retrieves dependencies declared on a provider
24
+ * @param provider - Provider to inspect
25
+ * @returns Array of provider tokens this provider depends on
26
+ */
27
+ function getDeps(provider: Provider): ProviderToken[] {
28
+ if (typeof provider === 'function') {
29
+ return (provider as any).deps ?? [];
30
+ }
31
+ return (provider as any).deps ?? [];
32
+ }
33
+
34
+ /**
35
+ * Sorts providers so that dependencies are instantiated first
36
+ * @param providers - Array of providers to sort
37
+ * @throws When a circular dependency is detected
38
+ */
39
+ function sortProviders(providers: Provider[]): Provider[] {
40
+ const tokenName = (t: ProviderToken) => typeof t === 'function' ? t.name : t;
41
+ const map = new Map<string, Provider>();
42
+ for (const p of providers) {
43
+ const token = tokenName(typeof p === 'function' ? p : (p as any).provide);
44
+ map.set(token, p);
45
+ }
46
+
47
+ const result: Provider[] = [];
48
+ const visited = new Set<string>();
49
+ const stack = new Set<string>();
50
+
51
+ const visit = (token: ProviderToken) => {
52
+ const name = tokenName(token);
53
+ if (visited.has(name)) return;
54
+ if (stack.has(name)) {
55
+ throw new Error(`Circular dependency detected for provider ${name}`);
56
+ }
57
+ stack.add(name);
58
+ const provider = map.get(name);
59
+ if (provider) {
60
+ for (const dep of getDeps(provider)) {
61
+ visit(dep);
62
+ }
63
+ visited.add(name);
64
+ result.push(provider);
65
+ }
66
+ stack.delete(name);
67
+ };
68
+
69
+ for (const p of providers) {
70
+ const token = typeof p === 'function' ? p : (p as any).provide;
71
+ visit(token);
72
+ }
73
+
74
+ return result;
75
+ }
76
+
22
77
  /**
23
78
  * Processes and instantiates all providers in the given context
24
79
  * @param context - The injection context
@@ -39,6 +94,8 @@ function isClassProvider(provider: Provider): provider is ClassProvider {
39
94
  */
40
95
  export async function injector(context: Context, providers: Providers) {
41
96
  providers = providers.flat();
97
+ providers = sortProviders(providers as Provider[]);
98
+
42
99
  for (const provider of providers) {
43
100
  let token: ProviderToken;
44
101
  let instance: any;
package/src/types.ts CHANGED
@@ -23,11 +23,19 @@ export type FactoryFn = (context: ProviderContext) => any;
23
23
  /**
24
24
  * Provider configuration for value-based injection
25
25
  */
26
+ export interface ProviderMeta {
27
+ [key: string]: any;
28
+ }
29
+
26
30
  export interface ValueProvider {
27
31
  /** Token to identify the provider */
28
32
  provide: ProviderToken;
29
33
  /** Value to be injected */
30
34
  useValue: any;
35
+ /** Tokens that must be injected before this provider */
36
+ deps?: ProviderToken[];
37
+ /** Optional metadata for the provider */
38
+ meta?: ProviderMeta;
31
39
  }
32
40
 
33
41
  /**
@@ -38,6 +46,10 @@ export interface ClassProvider {
38
46
  provide: ProviderToken;
39
47
  /** Class to be instantiated */
40
48
  useClass: new (context: ProviderContext) => any;
49
+ /** Tokens that must be injected before this provider */
50
+ deps?: ProviderToken[];
51
+ /** Optional metadata for the provider */
52
+ meta?: ProviderMeta;
41
53
  }
42
54
 
43
55
  /**
@@ -46,10 +58,10 @@ export interface ClassProvider {
46
58
  export interface FactoryProvider {
47
59
  /** Token to identify the provider */
48
60
  provide: ProviderToken;
61
+ /** Tokens that must be injected before this provider */
62
+ deps?: ProviderToken[];
49
63
  /** Optional metadata for the provider */
50
- meta?: {
51
- [key: string]: any;
52
- };
64
+ meta?: ProviderMeta;
53
65
  /** Factory function to create the instance */
54
66
  useFactory: FactoryFn;
55
67
  }
@@ -62,6 +74,10 @@ export interface ExistingProvider {
62
74
  provide: ProviderToken;
63
75
  /** Token of the existing provider to use */
64
76
  useExisting: ProviderToken;
77
+ /** Tokens that must be injected before this provider */
78
+ deps?: ProviderToken[];
79
+ /** Optional metadata for the provider */
80
+ meta?: ProviderMeta;
65
81
  }
66
82
 
67
83
  /**