@signe/di 1.0.2

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.
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Type definitions for the dependency injection system
3
+ * @module @signe/di/types
4
+ */
5
+ /**
6
+ * Context object used for dependency injection
7
+ */
8
+ type ProviderContext = any;
9
+ /**
10
+ * Token used to identify a provider. Can be either a class constructor or a string
11
+ */
12
+ type ProviderToken = (new (...args: any[]) => any) | string;
13
+ /**
14
+ * Factory function type that creates instances using the provided context
15
+ * @param context - The injection context
16
+ * @returns The created instance
17
+ */
18
+ type FactoryFn = (context: ProviderContext) => any;
19
+ /**
20
+ * Provider configuration for value-based injection
21
+ */
22
+ interface ValueProvider {
23
+ /** Token to identify the provider */
24
+ provide: ProviderToken;
25
+ /** Value to be injected */
26
+ useValue: any;
27
+ }
28
+ /**
29
+ * Provider configuration for class-based injection
30
+ */
31
+ interface ClassProvider {
32
+ /** Token to identify the provider */
33
+ provide: ProviderToken;
34
+ /** Class to be instantiated */
35
+ useClass: new (context: ProviderContext) => any;
36
+ }
37
+ /**
38
+ * Provider configuration for factory-based injection
39
+ */
40
+ interface FactoryProvider {
41
+ /** Token to identify the provider */
42
+ provide: ProviderToken;
43
+ /** Optional metadata for the provider */
44
+ meta?: {
45
+ [key: string]: any;
46
+ };
47
+ /** Factory function to create the instance */
48
+ useFactory: FactoryFn;
49
+ }
50
+ /**
51
+ * Provider configuration for alias-based injection
52
+ */
53
+ interface ExistingProvider {
54
+ /** Token to identify the provider */
55
+ provide: ProviderToken;
56
+ /** Token of the existing provider to use */
57
+ useExisting: ProviderToken;
58
+ }
59
+ /**
60
+ * Union type for all possible provider configurations
61
+ */
62
+ type Provider = (new (...args: any[]) => any) | (ValueProvider & {
63
+ useClass?: never;
64
+ useFactory?: never;
65
+ useExisting?: never;
66
+ }) | (ClassProvider & {
67
+ useValue?: never;
68
+ useFactory?: never;
69
+ useExisting?: never;
70
+ }) | (FactoryProvider & {
71
+ useValue?: never;
72
+ useClass?: never;
73
+ useExisting?: never;
74
+ }) | (ExistingProvider & {
75
+ useValue?: never;
76
+ useClass?: never;
77
+ useFactory?: never;
78
+ });
79
+ /**
80
+ * Array of providers that can be nested one level deep
81
+ */
82
+ type Providers = (Provider | Providers)[];
83
+
84
+ /**
85
+ * Context class for managing dependency injection state
86
+ * @module @signe/di/context
87
+ */
88
+ /**
89
+ * Stores and manages the state of injected dependencies
90
+ */
91
+ declare class Context {
92
+ /** Internal storage for injected values */
93
+ private values;
94
+ /**
95
+ * Sets a value in the context
96
+ * @param key - Unique identifier for the value
97
+ * @param value - Value to store
98
+ */
99
+ set(key: string, value: any): void;
100
+ /**
101
+ * Retrieves a value from the context
102
+ * @param key - Unique identifier for the value
103
+ * @returns The stored value or undefined if not found
104
+ */
105
+ get(key: string): any;
106
+ }
107
+
108
+ /**
109
+ * Core functionality for dependency injection
110
+ * @module @signe/di/inject
111
+ */
112
+
113
+ /**
114
+ * Provides a value to the dependency injection context
115
+ * @template T - Type of the value being provided
116
+ * @param context - The injection context
117
+ * @param name - Identifier for the provided value
118
+ * @param value - The value to provide
119
+ * @returns The provided value
120
+ */
121
+ declare function provide<T>(context: Context, name: string, value: T): T;
122
+ /**
123
+ * Checks if a service has been injected into the context
124
+ * @param context - The injection context
125
+ * @param name - Name of the service to check
126
+ * @returns True if the service has been injected, false otherwise
127
+ */
128
+ declare function isInjected(context: Context, name: string): boolean;
129
+ /**
130
+ * Retrieves a service from the dependency injection context
131
+ * @template T - Type of the service to inject
132
+ * @param context - The injection context
133
+ * @param service - Class constructor or string identifier of the service
134
+ * @param args - Optional arguments for service instantiation
135
+ * @returns The injected service instance
136
+ * @throws {Error} If the requested service is not found in the context
137
+ */
138
+ declare function inject<T>(context: Context, service: (new (...args: any[]) => T) | string, args?: any[]): T;
139
+ /**
140
+ * Overrides or adds a provider in the providers array
141
+ * @param providers - Array of existing providers
142
+ * @param newProvider - Provider to add or replace with
143
+ * @param options - Configuration options
144
+ * @param options.upsert - If true, adds the provider when not found
145
+ * @param options.key - Custom key to identify the provider
146
+ * @returns Updated array of providers
147
+ */
148
+ declare function override(providers: Providers, newProvider: Provider, options?: {
149
+ upsert?: boolean;
150
+ key?: string;
151
+ }): (Provider | Providers)[];
152
+ /**
153
+ * Finds all providers matching the given name or pattern
154
+ * @template T - Type of provider to return
155
+ * @param providers - Array of providers to search
156
+ * @param name - String or RegExp to match against provider names
157
+ * @returns Array of matching providers
158
+ */
159
+ declare function findProviders<T extends Provider = Provider>(providers: Providers, name: string | RegExp): T[];
160
+ /**
161
+ * Finds the first provider matching the given name or pattern
162
+ * @template T - Type of provider to return
163
+ * @param providers - Array of providers to search
164
+ * @param name - String or RegExp to match against provider names
165
+ * @returns Matching provider or null if not found
166
+ */
167
+ declare function findProvider<T extends Provider = Provider>(providers: Providers, name: string | RegExp): T | null;
168
+
169
+ /**
170
+ * Configuration merging functionality for dependency injection
171
+ * @module @signe/di/merge-config
172
+ */
173
+ /**
174
+ * Application configuration interface
175
+ */
176
+ interface AppConfig {
177
+ /** Array of dependency providers */
178
+ providers: any[];
179
+ /** Optional static files configuration */
180
+ staticFiles?: {
181
+ /** Path to static files */
182
+ path: string;
183
+ /** Static file serving configuration */
184
+ serve: any;
185
+ };
186
+ }
187
+ /**
188
+ * Merges two application configurations
189
+ * @param baseConfig - Base configuration to merge into
190
+ * @param config - Configuration to merge with base
191
+ * @returns Merged configuration with providers properly combined
192
+ */
193
+ declare function mergeConfig(baseConfig: AppConfig, config: AppConfig): AppConfig;
194
+
195
+ /**
196
+ * Provider system implementation for dependency injection
197
+ * @module @signe/di/provider
198
+ */
199
+
200
+ /**
201
+ * Processes and instantiates all providers in the given context
202
+ * @param context - The injection context
203
+ * @param providers - Array of providers to process
204
+ * @returns Promise that resolves when all providers are processed
205
+ *
206
+ * @example
207
+ * ```typescript
208
+ * const context = new Context();
209
+ * const providers = [
210
+ * UserService,
211
+ * { provide: 'CONFIG', useValue: { apiUrl: 'http://api.example.com' } },
212
+ * { provide: AuthService, useFactory: (ctx) => new AuthService(ctx) }
213
+ * ];
214
+ *
215
+ * await injector(context, providers);
216
+ * ```
217
+ */
218
+ declare function injector(context: Context, providers: Providers): Promise<void>;
219
+
220
+ 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, mergeConfig, override, provide };
package/dist/index.js ADDED
@@ -0,0 +1,182 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/inject.ts
5
+ function provide(context, name, value) {
6
+ context.set("inject:" + name, value);
7
+ return value;
8
+ }
9
+ __name(provide, "provide");
10
+ function isInjected(context, name) {
11
+ return context.get("injected:" + name) === true;
12
+ }
13
+ __name(isInjected, "isInjected");
14
+ function inject(context, service, args = []) {
15
+ const isClass = typeof service === "function";
16
+ const name = isClass ? service.name : service;
17
+ const value = context.get("inject:" + name);
18
+ if (value) {
19
+ context.set("injected:" + name, true);
20
+ return value;
21
+ }
22
+ throw new Error(`Injection provider ${name} not found`);
23
+ }
24
+ __name(inject, "inject");
25
+ function override(providers, newProvider, options) {
26
+ let { upsert = false, key } = options ?? {};
27
+ if (!key) {
28
+ key = typeof newProvider === "function" ? newProvider.name : newProvider.provide;
29
+ }
30
+ const flatProviders = providers.flat();
31
+ const exists = flatProviders.some((provider) => {
32
+ if (typeof provider === "function") {
33
+ return provider.name === key;
34
+ } else if (typeof provider === "object") {
35
+ return provider.provide === key;
36
+ }
37
+ return false;
38
+ });
39
+ const mappedProviders = flatProviders.map((provider) => {
40
+ if (typeof provider === "function" && provider.name === key) {
41
+ return newProvider;
42
+ } else if (typeof provider === "object" && provider.provide === key) {
43
+ return newProvider;
44
+ }
45
+ return provider;
46
+ });
47
+ if (upsert && !exists) {
48
+ mappedProviders.push(newProvider);
49
+ }
50
+ return mappedProviders;
51
+ }
52
+ __name(override, "override");
53
+ function findProviders(providers, name) {
54
+ const results = [];
55
+ for (const provider of providers) {
56
+ if (Array.isArray(provider)) {
57
+ results.push(...findProviders(provider, name));
58
+ } else if (findProvider(provider, name)) {
59
+ results.push(provider);
60
+ }
61
+ }
62
+ return results;
63
+ }
64
+ __name(findProviders, "findProviders");
65
+ function findProvider(providers, name) {
66
+ if (!Array.isArray(providers)) {
67
+ if (typeof providers === "object" && "provide" in providers) {
68
+ const provider = providers;
69
+ const providerName = typeof provider.provide === "function" ? provider.provide.name : provider.provide;
70
+ if (name instanceof RegExp) {
71
+ if (name.test(providerName)) return providers;
72
+ } else {
73
+ if (providerName === name) return providers;
74
+ }
75
+ }
76
+ return null;
77
+ }
78
+ for (const provider of providers) {
79
+ if (Array.isArray(provider)) {
80
+ const found = findProvider(provider, name);
81
+ if (found) return found;
82
+ continue;
83
+ }
84
+ if (typeof provider === "object" && "provide" in provider) {
85
+ const providerName = typeof provider.provide === "function" ? provider.provide.name : provider.provide;
86
+ if (name instanceof RegExp) {
87
+ if (name.test(providerName)) return provider;
88
+ } else {
89
+ if (providerName === name) return provider;
90
+ }
91
+ }
92
+ }
93
+ return null;
94
+ }
95
+ __name(findProvider, "findProvider");
96
+
97
+ // src/merge-config.ts
98
+ function mergeConfig(baseConfig, config) {
99
+ const mergedConfig = {
100
+ ...baseConfig,
101
+ ...config
102
+ };
103
+ for (let provider of config.providers) {
104
+ const isFound = findProvider(baseConfig.providers, provider.provide);
105
+ if (!isFound) {
106
+ mergedConfig.providers = override(baseConfig.providers, provider, {
107
+ upsert: true
108
+ });
109
+ }
110
+ }
111
+ return mergedConfig;
112
+ }
113
+ __name(mergeConfig, "mergeConfig");
114
+
115
+ // src/provider.ts
116
+ async function injector(context, providers) {
117
+ providers = providers.flat();
118
+ for (const provider of providers) {
119
+ let token;
120
+ let instance;
121
+ if (typeof provider === "function") {
122
+ token = provider;
123
+ instance = new provider(context);
124
+ } else {
125
+ token = provider.provide;
126
+ const provideUserClass = provider.useClass;
127
+ const isClass = typeof provideUserClass === "function";
128
+ if (isClass) {
129
+ instance = new provideUserClass(context);
130
+ } else if ("useValue" in provider) {
131
+ instance = provider.useValue;
132
+ } else if ("useFactory" in provider) {
133
+ instance = provider.useFactory?.(context);
134
+ if (instance instanceof Promise) {
135
+ instance = await instance;
136
+ }
137
+ } else if ("useExisting" in provider) {
138
+ instance = inject(context, provider.useExisting);
139
+ }
140
+ }
141
+ const name = typeof token === "function" ? token.name : token;
142
+ provide(context, name, instance);
143
+ }
144
+ }
145
+ __name(injector, "injector");
146
+
147
+ // src/context.ts
148
+ var Context = class {
149
+ static {
150
+ __name(this, "Context");
151
+ }
152
+ /** Internal storage for injected values */
153
+ values = {};
154
+ /**
155
+ * Sets a value in the context
156
+ * @param key - Unique identifier for the value
157
+ * @param value - Value to store
158
+ */
159
+ set(key, value) {
160
+ this.values[key] = value;
161
+ }
162
+ /**
163
+ * Retrieves a value from the context
164
+ * @param key - Unique identifier for the value
165
+ * @returns The stored value or undefined if not found
166
+ */
167
+ get(key) {
168
+ return this.values[key];
169
+ }
170
+ };
171
+ export {
172
+ Context,
173
+ findProvider,
174
+ findProviders,
175
+ inject,
176
+ injector,
177
+ isInjected,
178
+ mergeConfig,
179
+ override,
180
+ provide
181
+ };
182
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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 * 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 * 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 const mergedConfig: AppConfig = {\n ...baseConfig,\n ...config\n }\n for (let provider of config.providers) {\n const isFound = findProvider(baseConfig.providers, provider.provide)\n if (!isFound) {\n mergedConfig.providers = override(baseConfig.providers, provider, { upsert: true })\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 */\nexport class Context {\n /** Internal storage for injected values */\n private values: { [key: string]: any } = {}\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(key: string, value: any) {\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(key: string) {\n return this.values[key]\n }\n}"],"mappings":"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","names":["provide","context","name","value","set","isInjected","get","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","mergeConfig","baseConfig","config","mergedConfig","provider","providers","isFound","findProvider","provide","override","upsert","injector","context","providers","flat","provider","token","instance","provide","provideUserClass","useClass","isClass","useValue","useFactory","Promise","inject","useExisting","name","Context","values","set","key","value","get"]}
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@signe/di",
3
+ "version": "1.0.2",
4
+ "description": "",
5
+ "main": "./dist/index.js",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./dist/index.js",
9
+ "types": "./dist/index.d.ts"
10
+ },
11
+ "./*": "./*"
12
+ },
13
+ "keywords": [],
14
+ "author": "Samuel Ronce",
15
+ "license": "MIT",
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "type": "module",
20
+ "scripts": {
21
+ "build": "tsup src/index.ts",
22
+ "dev": "tsup src/index.ts --watch"
23
+ }
24
+ }
package/readme.md ADDED
@@ -0,0 +1,130 @@
1
+ # @signe/di
2
+
3
+ A lightweight and flexible dependency injection system for JavaScript/TypeScript applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @signe/di
9
+ # or
10
+ yarn add @signe/di
11
+ # or
12
+ pnpm add @signe/di
13
+ ```
14
+
15
+ ## Features
16
+
17
+ - Simple and intuitive API
18
+ - Type-safe dependency injection
19
+ - Provider system with multiple configuration options
20
+ - Context-based injection
21
+ - Override capabilities for testing and customization
22
+ - Support for nested providers
23
+
24
+ ## Usage
25
+
26
+ ### Basic Usage
27
+
28
+ ```typescript
29
+ import { provide, inject, Context, Providers } from '@signe/di';
30
+
31
+ const context = new Context();
32
+
33
+ class UserService {
34
+ getUser(id: string) {
35
+ return { id, name: 'John Doe' };
36
+ }
37
+ }
38
+
39
+ class AuthService {
40
+ constructor(private config: any) {}
41
+ }
42
+
43
+ const providers: Providers = [
44
+ UserService,
45
+ {
46
+ provide: 'CONFIG',
47
+ useValue: {
48
+ apiUrl: 'https://api.example.com'
49
+ }
50
+ },
51
+ {
52
+ provide: AuthService,
53
+ useFactory: (context) => {
54
+ const config = inject(context, 'CONFIG');
55
+ return new AuthService(config);
56
+ }
57
+ }
58
+ ];
59
+
60
+ // Provide the service
61
+ provide(context, providers);
62
+
63
+ // Inject and use the service
64
+ const userService = inject(context, UserService);
65
+ const user = userService.getUser('123');
66
+ ```
67
+
68
+ ### Override Providers
69
+
70
+ ```typescript
71
+ import { override } from '@signe/di';
72
+
73
+ // Override existing provider
74
+ const newProviders = override(providers, {
75
+ provide: UserService,
76
+ useValue: new MockUserService()
77
+ });
78
+
79
+ // Add new provider with upsert option
80
+ const updatedProviders = override(providers, {
81
+ provide: 'NEW_SERVICE',
82
+ useValue: service
83
+ }, { upsert: true });
84
+ ```
85
+
86
+ ### Check Injection Status
87
+
88
+ ```typescript
89
+ import { isInjected } from '@signe/di';
90
+
91
+ if (isInjected(context, UserService)) {
92
+ // Service is already injected
93
+ }
94
+ ```
95
+
96
+ ### Find Providers
97
+
98
+ ```typescript
99
+ import { findProvider, findProviders } from '@signe/di';
100
+
101
+ // Find single provider
102
+ const userProvider = findProvider(providers, UserService);
103
+
104
+ // Find multiple providers by regex
105
+ const allServices = findProviders(providers, /Service$/);
106
+ ```
107
+
108
+ ## API Reference
109
+
110
+ ### `provide(context, key, value)`
111
+ Stores a value in the context for dependency injection.
112
+
113
+ ### `inject(context, key)`
114
+ Retrieves an injected value from the context.
115
+
116
+ ### `isInjected(context, key)`
117
+ Checks if a value has been injected.
118
+
119
+ ### `override(providers, newProvider, options?)`
120
+ Overrides or adds new providers to the existing provider array.
121
+
122
+ ### `findProvider(providers, query)`
123
+ Finds a single provider by name or regex.
124
+
125
+ ### `findProviders(providers, query)`
126
+ Finds all providers matching the query.
127
+
128
+ ## License
129
+
130
+ MIT © Samuel Ronce
package/src/context.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Context class for managing dependency injection state
3
+ * @module @signe/di/context
4
+ */
5
+
6
+ /**
7
+ * Stores and manages the state of injected dependencies
8
+ */
9
+ export class Context {
10
+ /** Internal storage for injected values */
11
+ private values: { [key: string]: any } = {}
12
+
13
+ /**
14
+ * Sets a value in the context
15
+ * @param key - Unique identifier for the value
16
+ * @param value - Value to store
17
+ */
18
+ set(key: string, value: any) {
19
+ this.values[key] = value
20
+ }
21
+
22
+ /**
23
+ * Retrieves a value from the context
24
+ * @param key - Unique identifier for the value
25
+ * @returns The stored value or undefined if not found
26
+ */
27
+ get(key: string) {
28
+ return this.values[key]
29
+ }
30
+ }
package/src/index.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @signe/di - A lightweight dependency injection system
3
+ * @module @signe/di
4
+ *
5
+ * @description
6
+ * This package provides a complete dependency injection system with the following features:
7
+ * - Type-safe dependency injection
8
+ * - Provider system with multiple configuration options
9
+ * - Context-based injection
10
+ * - Override capabilities for testing
11
+ * - Support for nested providers
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { Context, provide, inject } from '@signe/di';
16
+ *
17
+ * const context = new Context();
18
+ * provide(context, 'config', { apiUrl: 'http://api.example.com' });
19
+ * const config = inject(context, 'config');
20
+ * ```
21
+ */
22
+
23
+ export * from "./inject";
24
+ export * from "./types";
25
+ export * from "./merge-config";
26
+ export * from "./provider";
27
+ export * from "./context";
package/src/inject.ts ADDED
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Core functionality for dependency injection
3
+ * @module @signe/di/inject
4
+ */
5
+
6
+ import { Provider, Providers } from "./types";
7
+ import { Context } from "./context";
8
+
9
+ /**
10
+ * Provides a value to the dependency injection context
11
+ * @template T - Type of the value being provided
12
+ * @param context - The injection context
13
+ * @param name - Identifier for the provided value
14
+ * @param value - The value to provide
15
+ * @returns The provided value
16
+ */
17
+ export function provide<T>(context: Context, name: string, value: T): T {
18
+ context.set("inject:" + name, value);
19
+ return value;
20
+ }
21
+
22
+ /**
23
+ * Checks if a service has been injected into the context
24
+ * @param context - The injection context
25
+ * @param name - Name of the service to check
26
+ * @returns True if the service has been injected, false otherwise
27
+ */
28
+ export function isInjected(context: Context, name: string): boolean {
29
+ return context.get('injected:' + name) === true;
30
+ }
31
+
32
+ /**
33
+ * Retrieves a service from the dependency injection context
34
+ * @template T - Type of the service to inject
35
+ * @param context - The injection context
36
+ * @param service - Class constructor or string identifier of the service
37
+ * @param args - Optional arguments for service instantiation
38
+ * @returns The injected service instance
39
+ * @throws {Error} If the requested service is not found in the context
40
+ */
41
+ export function inject<T>(
42
+ context: Context,
43
+ service: (new (...args: any[]) => T) | string,
44
+ args: any[] = []
45
+ ): T {
46
+ const isClass = typeof service === "function";
47
+ const name = isClass ? service.name : service;
48
+ const value = context.get("inject:" + name);
49
+ if (value) {
50
+ context.set('injected:' + name, true)
51
+ return value as T;
52
+ }
53
+ throw new Error(`Injection provider ${name} not found`);
54
+ }
55
+
56
+ /**
57
+ * Overrides or adds a provider in the providers array
58
+ * @param providers - Array of existing providers
59
+ * @param newProvider - Provider to add or replace with
60
+ * @param options - Configuration options
61
+ * @param options.upsert - If true, adds the provider when not found
62
+ * @param options.key - Custom key to identify the provider
63
+ * @returns Updated array of providers
64
+ */
65
+ export function override(
66
+ providers: Providers,
67
+ newProvider: Provider,
68
+ options?: {
69
+ upsert?: boolean,
70
+ key?: string
71
+ }
72
+ ) {
73
+ let { upsert = false, key } = options ?? {};
74
+ if (!key) {
75
+ key = (
76
+ typeof newProvider === "function" ? newProvider.name : newProvider.provide
77
+ ) as string;
78
+ }
79
+
80
+ // Flatten the providers array
81
+ const flatProviders = providers.flat();
82
+
83
+ // Check if provider exists
84
+ const exists = flatProviders.some(provider => {
85
+ if (typeof provider === "function") {
86
+ return provider.name === key;
87
+ } else if (typeof provider === "object") {
88
+ return (provider as any).provide === key;
89
+ }
90
+ return false;
91
+ });
92
+
93
+ // Map and replace if found
94
+ const mappedProviders = flatProviders.map((provider) => {
95
+ if (typeof provider === "function" && provider.name === key) {
96
+ return newProvider;
97
+ } else if (typeof provider === "object" && (provider as any).provide === key) {
98
+ return newProvider;
99
+ }
100
+ return provider;
101
+ });
102
+
103
+ // If upsert is true and provider wasn't found, add it to the end
104
+ if (upsert && !exists) {
105
+ mappedProviders.push(newProvider);
106
+ }
107
+
108
+ return mappedProviders;
109
+ }
110
+
111
+ /**
112
+ * Finds all providers matching the given name or pattern
113
+ * @template T - Type of provider to return
114
+ * @param providers - Array of providers to search
115
+ * @param name - String or RegExp to match against provider names
116
+ * @returns Array of matching providers
117
+ */
118
+ export function findProviders<T extends Provider = Provider>(providers: Providers, name: string | RegExp): T[] {
119
+ const results: any[] = [];
120
+
121
+ for (const provider of providers) {
122
+ if (Array.isArray(provider)) {
123
+ // Recursively search in nested arrays and concat results
124
+ results.push(...findProviders(provider, name));
125
+ } else if (findProvider(provider as any, name)) {
126
+ // Add matching provider to results
127
+ results.push(provider as T);
128
+ }
129
+ }
130
+
131
+ return results;
132
+ }
133
+
134
+ /**
135
+ * Finds the first provider matching the given name or pattern
136
+ * @template T - Type of provider to return
137
+ * @param providers - Array of providers to search
138
+ * @param name - String or RegExp to match against provider names
139
+ * @returns Matching provider or null if not found
140
+ */
141
+ export function findProvider<T extends Provider = Provider>(providers: Providers, name: string | RegExp): T | null {
142
+ // Handle single provider case
143
+ if (!Array.isArray(providers)) {
144
+ if (typeof providers === "object" && 'provide' in providers) {
145
+ const provider = providers as any
146
+ const providerName = typeof provider.provide === "function"
147
+ ? provider.provide.name
148
+ : provider.provide;
149
+
150
+ if (name instanceof RegExp) {
151
+ if (name.test(providerName)) return providers as T;
152
+ } else {
153
+ if (providerName === name) return providers as T;
154
+ }
155
+ }
156
+ return null;
157
+ }
158
+
159
+ // Original array handling logic
160
+ for (const provider of providers) {
161
+ // If provider is an array, recursively search in it
162
+ if (Array.isArray(provider)) {
163
+ const found = findProvider(provider, name);
164
+ if (found) return found as T;
165
+ continue;
166
+ }
167
+
168
+ // Check object provider
169
+ if (typeof provider === "object" && 'provide' in provider) {
170
+ const providerName = typeof provider.provide === "function"
171
+ ? provider.provide.name
172
+ : provider.provide;
173
+
174
+ // Handle RegExp matching
175
+ if (name instanceof RegExp) {
176
+ if (name.test(providerName)) return provider as T;
177
+ } else {
178
+ // Handle exact string matching
179
+ if (providerName === name) return provider as T;
180
+ }
181
+ }
182
+ }
183
+ return null;
184
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Configuration merging functionality for dependency injection
3
+ * @module @signe/di/merge-config
4
+ */
5
+
6
+ import { findProvider, override } from "./inject";
7
+
8
+ /**
9
+ * Application configuration interface
10
+ */
11
+ export interface AppConfig {
12
+ /** Array of dependency providers */
13
+ providers: any[];
14
+ /** Optional static files configuration */
15
+ staticFiles?: {
16
+ /** Path to static files */
17
+ path: string;
18
+ /** Static file serving configuration */
19
+ serve: any;
20
+ }
21
+ }
22
+
23
+ /**
24
+ * Merges two application configurations
25
+ * @param baseConfig - Base configuration to merge into
26
+ * @param config - Configuration to merge with base
27
+ * @returns Merged configuration with providers properly combined
28
+ */
29
+ export function mergeConfig(baseConfig: AppConfig, config: AppConfig): AppConfig {
30
+ const mergedConfig: AppConfig = {
31
+ ...baseConfig,
32
+ ...config
33
+ }
34
+ for (let provider of config.providers) {
35
+ const isFound = findProvider(baseConfig.providers, provider.provide)
36
+ if (!isFound) {
37
+ mergedConfig.providers = override(baseConfig.providers, provider, { upsert: true })
38
+ }
39
+ }
40
+ return mergedConfig;
41
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Provider system implementation for dependency injection
3
+ * @module @signe/di/provider
4
+ */
5
+
6
+ import { ClassProvider, Provider, Providers, ProviderToken } from "./types";
7
+ import { inject, provide } from "./inject";
8
+ import { Context } from "./context";
9
+
10
+ /**
11
+ * Type guard to check if a provider is a ClassProvider
12
+ * @param provider - Provider to check
13
+ * @returns True if the provider is a ClassProvider
14
+ */
15
+ function isClassProvider(provider: Provider): provider is ClassProvider {
16
+ if (typeof provider === 'string') {
17
+ return false;
18
+ }
19
+ return 'useClass' in provider && typeof provider.useClass === 'function';
20
+ }
21
+
22
+ /**
23
+ * Processes and instantiates all providers in the given context
24
+ * @param context - The injection context
25
+ * @param providers - Array of providers to process
26
+ * @returns Promise that resolves when all providers are processed
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const context = new Context();
31
+ * const providers = [
32
+ * UserService,
33
+ * { provide: 'CONFIG', useValue: { apiUrl: 'http://api.example.com' } },
34
+ * { provide: AuthService, useFactory: (ctx) => new AuthService(ctx) }
35
+ * ];
36
+ *
37
+ * await injector(context, providers);
38
+ * ```
39
+ */
40
+ export async function injector(context: Context, providers: Providers) {
41
+ providers = providers.flat();
42
+ for (const provider of providers) {
43
+ let token: ProviderToken;
44
+ let instance: any;
45
+
46
+ if (typeof provider === 'function') {
47
+ // If provider is a class, treat it as a ClassProvider
48
+ token = provider;
49
+ instance = new provider(context);
50
+ } else {
51
+ token = (provider as any).provide;
52
+ const provideUserClass = (provider as any).useClass;
53
+ const isClass = typeof provideUserClass === 'function';
54
+ if (isClass) {
55
+ instance = new provideUserClass(context);
56
+ } else if ('useValue' in provider) {
57
+ instance = provider.useValue;
58
+ } else if ('useFactory' in provider) {
59
+ instance = provider.useFactory?.(context);
60
+ if (instance instanceof Promise) {
61
+ instance = await instance;
62
+ }
63
+ } else if ('useExisting' in provider) {
64
+ instance = inject(context, provider.useExisting);
65
+ }
66
+ }
67
+
68
+ const name = typeof token === 'function' ? token.name : token;
69
+ provide(context, name, instance);
70
+ }
71
+ }
package/src/types.ts ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Type definitions for the dependency injection system
3
+ * @module @signe/di/types
4
+ */
5
+
6
+ /**
7
+ * Context object used for dependency injection
8
+ */
9
+ export type ProviderContext = any;
10
+
11
+ /**
12
+ * Token used to identify a provider. Can be either a class constructor or a string
13
+ */
14
+ export type ProviderToken = (new (...args: any[]) => any) | string;
15
+
16
+ /**
17
+ * Factory function type that creates instances using the provided context
18
+ * @param context - The injection context
19
+ * @returns The created instance
20
+ */
21
+ export type FactoryFn = (context: ProviderContext) => any;
22
+
23
+ /**
24
+ * Provider configuration for value-based injection
25
+ */
26
+ export interface ValueProvider {
27
+ /** Token to identify the provider */
28
+ provide: ProviderToken;
29
+ /** Value to be injected */
30
+ useValue: any;
31
+ }
32
+
33
+ /**
34
+ * Provider configuration for class-based injection
35
+ */
36
+ export interface ClassProvider {
37
+ /** Token to identify the provider */
38
+ provide: ProviderToken;
39
+ /** Class to be instantiated */
40
+ useClass: new (context: ProviderContext) => any;
41
+ }
42
+
43
+ /**
44
+ * Provider configuration for factory-based injection
45
+ */
46
+ export interface FactoryProvider {
47
+ /** Token to identify the provider */
48
+ provide: ProviderToken;
49
+ /** Optional metadata for the provider */
50
+ meta?: {
51
+ [key: string]: any;
52
+ };
53
+ /** Factory function to create the instance */
54
+ useFactory: FactoryFn;
55
+ }
56
+
57
+ /**
58
+ * Provider configuration for alias-based injection
59
+ */
60
+ export interface ExistingProvider {
61
+ /** Token to identify the provider */
62
+ provide: ProviderToken;
63
+ /** Token of the existing provider to use */
64
+ useExisting: ProviderToken;
65
+ }
66
+
67
+ /**
68
+ * Union type for all possible provider configurations
69
+ */
70
+ export type Provider =
71
+ | (new (...args: any[]) => any) // Allow direct class usage
72
+ | (ValueProvider & { useClass?: never, useFactory?: never, useExisting?: never })
73
+ | (ClassProvider & { useValue?: never, useFactory?: never, useExisting?: never })
74
+ | (FactoryProvider & { useValue?: never, useClass?: never, useExisting?: never })
75
+ | (ExistingProvider & { useValue?: never, useClass?: never, useFactory?: never });
76
+
77
+ /**
78
+ * Array of providers that can be nested one level deep
79
+ */
80
+ export type Providers = (Provider | Providers)[];