@sentzunhat/zacatl 0.0.24 → 0.0.26
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/build/dependency-injection/container.d.ts +3 -0
- package/build/dependency-injection/container.d.ts.map +1 -1
- package/build/dependency-injection/container.js +37 -0
- package/build/dependency-injection/container.js.map +1 -1
- package/build/service/architecture/architecture.d.ts +1 -1
- package/build/service/architecture/architecture.d.ts.map +1 -1
- package/build/service/architecture/architecture.js +0 -1
- package/build/service/architecture/architecture.js.map +1 -1
- package/build/test/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/dependency-injection/container.ts +105 -0
- package/src/service/architecture/architecture.ts +9 -5
package/package.json
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
2
|
import { container as tsyringeContainer } from "tsyringe";
|
|
3
3
|
import type { DependencyContainer, InjectionToken } from "tsyringe";
|
|
4
|
+
import type { Constructor } from "../service/architecture/architecture";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Track registered classes to avoid requiring decorator metadata
|
|
8
|
+
*/
|
|
9
|
+
const registeredClasses = new Set<Constructor>();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Cache for singleton instances
|
|
13
|
+
*/
|
|
14
|
+
const singletonInstances = new Map<Constructor, object>();
|
|
4
15
|
|
|
5
16
|
/**
|
|
6
17
|
* Standalone DI container decoupled from microservice architecture.
|
|
@@ -83,10 +94,104 @@ export const resolveDependency = <T>(token: InjectionToken<T>): T => {
|
|
|
83
94
|
return tsyringeContainer.resolve(token);
|
|
84
95
|
};
|
|
85
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Register a dependency with explicit dependencies (no decorator metadata required)
|
|
99
|
+
* Useful for tsx/ts-node/ESM environments where decorator metadata is not available
|
|
100
|
+
*
|
|
101
|
+
* @param serviceClass - Class to register
|
|
102
|
+
* @param dependencies - Array of dependency classes to inject (in constructor order)
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* import { registerWithDependencies } from '@sentzunhat/zacatl';
|
|
107
|
+
*
|
|
108
|
+
* // Register with no deps
|
|
109
|
+
* registerWithDependencies(MachineRepository, []);
|
|
110
|
+
*
|
|
111
|
+
* // Register with explicit deps
|
|
112
|
+
* registerWithDependencies(MachineService, [MachineRepository]);
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export const registerWithDependencies = <T extends object>(
|
|
116
|
+
serviceClass: Constructor<T>,
|
|
117
|
+
dependencies: Constructor[] = [],
|
|
118
|
+
): void => {
|
|
119
|
+
// Track this class as registered
|
|
120
|
+
registeredClasses.add(serviceClass);
|
|
121
|
+
|
|
122
|
+
tsyringeContainer.register(serviceClass, {
|
|
123
|
+
useFactory: () => {
|
|
124
|
+
const resolvedDeps = dependencies.map((dep) => {
|
|
125
|
+
// Check if dependency was registered
|
|
126
|
+
if (!registeredClasses.has(dep)) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Dependency ${dep.name} not registered. Register it before ${serviceClass.name}.`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
// Resolve from container (works because we registered it)
|
|
132
|
+
return tsyringeContainer.resolve(dep);
|
|
133
|
+
});
|
|
134
|
+
return new serviceClass(...resolvedDeps);
|
|
135
|
+
},
|
|
136
|
+
} as never);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Register a singleton with explicit dependencies (no decorator metadata required)
|
|
141
|
+
* Useful for tsx/ts-node/ESM environments where decorator metadata is not available
|
|
142
|
+
*
|
|
143
|
+
* @param serviceClass - Class to register as singleton
|
|
144
|
+
* @param dependencies - Array of dependency classes to inject (in constructor order)
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```typescript
|
|
148
|
+
* import { registerSingletonWithDependencies } from '@sentzunhat/zacatl';
|
|
149
|
+
*
|
|
150
|
+
* // Register singleton with deps
|
|
151
|
+
* registerSingletonWithDependencies(MachineService, [MachineRepository]);
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
export const registerSingletonWithDependencies = <T extends object>(
|
|
155
|
+
serviceClass: Constructor<T>,
|
|
156
|
+
dependencies: Constructor[] = [],
|
|
157
|
+
): void => {
|
|
158
|
+
// Track this class as registered
|
|
159
|
+
registeredClasses.add(serviceClass);
|
|
160
|
+
|
|
161
|
+
// Use register() with manual singleton pattern (lifecycle can't be used with factories)
|
|
162
|
+
tsyringeContainer.register(serviceClass, {
|
|
163
|
+
useFactory: () => {
|
|
164
|
+
// Check if we already have a singleton instance
|
|
165
|
+
if (singletonInstances.has(serviceClass)) {
|
|
166
|
+
return singletonInstances.get(serviceClass) as T;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Create new instance
|
|
170
|
+
const resolvedDeps = dependencies.map((dep) => {
|
|
171
|
+
// Check if dependency was registered
|
|
172
|
+
if (!registeredClasses.has(dep)) {
|
|
173
|
+
throw new Error(
|
|
174
|
+
`Dependency ${dep.name} not registered. Register it before ${serviceClass.name}.`,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
// Resolve from container (works because we registered it)
|
|
178
|
+
return tsyringeContainer.resolve(dep);
|
|
179
|
+
});
|
|
180
|
+
const instance = new serviceClass(...resolvedDeps);
|
|
181
|
+
|
|
182
|
+
// Cache the singleton instance
|
|
183
|
+
singletonInstances.set(serviceClass, instance);
|
|
184
|
+
return instance;
|
|
185
|
+
},
|
|
186
|
+
} as never);
|
|
187
|
+
};
|
|
188
|
+
|
|
86
189
|
/**
|
|
87
190
|
* Clear all registrations (useful for testing)
|
|
88
191
|
*/
|
|
89
192
|
export const clearContainer = (): void => {
|
|
90
193
|
tsyringeContainer.clearInstances();
|
|
91
194
|
tsyringeContainer.reset();
|
|
195
|
+
singletonInstances.clear();
|
|
196
|
+
registeredClasses.clear();
|
|
92
197
|
};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { container } from "tsyringe";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Constructor type that accepts classes with any parameter signature
|
|
5
|
+
* Uses contravariant any[] to allow flexibility in DI container
|
|
6
|
+
*/
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
+
export type Constructor<T = object> = new (...args: any[]) => T;
|
|
4
9
|
|
|
5
10
|
type Architecture = {
|
|
6
11
|
start: () => void;
|
|
@@ -13,9 +18,8 @@ export abstract class AbstractArchitecture implements Architecture {
|
|
|
13
18
|
*/
|
|
14
19
|
protected registerDependencies<T>(dependencies: Array<Constructor<T>>): void {
|
|
15
20
|
for (const dependency of dependencies) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
container.register<typeof instance>(dependency.name, {
|
|
21
|
+
// Register class directly - tsyringe handles instantiation with DI
|
|
22
|
+
container.register(dependency.name, {
|
|
19
23
|
useClass: dependency,
|
|
20
24
|
});
|
|
21
25
|
}
|
|
@@ -28,7 +32,7 @@ export abstract class AbstractArchitecture implements Architecture {
|
|
|
28
32
|
*/
|
|
29
33
|
protected registerAndStoreDependencies<T>(
|
|
30
34
|
dependencies: Array<new () => T>,
|
|
31
|
-
storage: Array<T
|
|
35
|
+
storage: Array<T>,
|
|
32
36
|
): void {
|
|
33
37
|
for (const dependency of dependencies) {
|
|
34
38
|
container.register(dependency.name, { useClass: dependency });
|