@navios/di 0.1.9 → 0.1.11

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/lib/index.mjs CHANGED
@@ -1,738 +1,968 @@
1
- import { ZodOptional, ZodRecord, z } from "zod";
1
+ // src/decorators/injectable.decorator.mts
2
+ import "zod";
2
3
 
3
- //#region src/enums/injectable-scope.enum.mts
4
- let InjectableScope = /* @__PURE__ */ function(InjectableScope$1) {
5
- /**
6
- * Singleton scope: The instance is created once and shared across the application.
7
- */
8
- InjectableScope$1["Singleton"] = "Singleton";
9
- /**
10
- * Instance scope: A new instance is created for each injection.
11
- */
12
- InjectableScope$1["Instance"] = "Instance";
13
- return InjectableScope$1;
14
- }({});
4
+ // src/enums/injectable-scope.enum.mts
5
+ var InjectableScope = /* @__PURE__ */ ((InjectableScope3) => {
6
+ InjectableScope3["Singleton"] = "Singleton";
7
+ InjectableScope3["Instance"] = "Instance";
8
+ return InjectableScope3;
9
+ })(InjectableScope || {});
15
10
 
16
- //#endregion
17
- //#region src/enums/injectable-type.enum.mts
18
- let InjectableType = /* @__PURE__ */ function(InjectableType$1) {
19
- InjectableType$1["Class"] = "Class";
20
- InjectableType$1["Factory"] = "Factory";
21
- return InjectableType$1;
22
- }({});
11
+ // src/enums/injectable-type.enum.mts
12
+ var InjectableType = /* @__PURE__ */ ((InjectableType2) => {
13
+ InjectableType2["Class"] = "Class";
14
+ InjectableType2["Factory"] = "Factory";
15
+ return InjectableType2;
16
+ })(InjectableType || {});
23
17
 
24
- //#endregion
25
- //#region src/injection-token.mts
26
- const Optional = Symbol("Optional");
27
- const Required = Symbol("Required");
28
- var InjectionToken = class InjectionToken {
29
- id = globalThis.crypto.randomUUID();
30
- formattedName = null;
31
- constructor(name, schema) {
32
- this.name = name;
33
- this.schema = schema;
34
- }
35
- static create(name, schema) {
36
- return new InjectionToken(name, schema);
37
- }
38
- static bound(token, value) {
39
- return new BoundInjectionToken(token, value);
40
- }
41
- static factory(token, factory) {
42
- return new FactoryInjectionToken(token, factory);
43
- }
44
- static refineType(token) {
45
- return token;
46
- }
47
- toString() {
48
- if (this.formattedName) return this.formattedName;
49
- const { name } = this;
50
- if (typeof name === "function") {
51
- const className = name.name;
52
- this.formattedName = `${className}(${this.id})`;
53
- } else if (typeof name === "symbol") this.formattedName = `${name.toString()}(${this.id})`;
54
- else this.formattedName = `${name}(${this.id})`;
55
- return this.formattedName;
56
- }
18
+ // src/injection-token.mts
19
+ import "zod";
20
+ var InjectionToken = class _InjectionToken {
21
+ constructor(name, schema) {
22
+ this.name = name;
23
+ this.schema = schema;
24
+ }
25
+ id = globalThis.crypto.randomUUID();
26
+ formattedName = null;
27
+ static create(name, schema) {
28
+ return new _InjectionToken(name, schema);
29
+ }
30
+ static bound(token, value) {
31
+ return new BoundInjectionToken(token, value);
32
+ }
33
+ static factory(token, factory) {
34
+ return new FactoryInjectionToken(token, factory);
35
+ }
36
+ static refineType(token) {
37
+ return token;
38
+ }
39
+ toString() {
40
+ if (this.formattedName) {
41
+ return this.formattedName;
42
+ }
43
+ const { name } = this;
44
+ if (typeof name === "function") {
45
+ const className = name.name;
46
+ this.formattedName = `${className}(${this.id})`;
47
+ } else if (typeof name === "symbol") {
48
+ this.formattedName = `${name.toString()}(${this.id})`;
49
+ } else {
50
+ this.formattedName = `${name}(${this.id})`;
51
+ }
52
+ return this.formattedName;
53
+ }
57
54
  };
58
55
  var BoundInjectionToken = class {
59
- id;
60
- name;
61
- schema;
62
- constructor(token, value) {
63
- this.token = token;
64
- this.value = value;
65
- this.name = token.name;
66
- this.id = token.id;
67
- this.schema = token.schema;
68
- }
69
- toString() {
70
- return this.token.toString();
71
- }
56
+ constructor(token, value) {
57
+ this.token = token;
58
+ this.value = value;
59
+ this.name = token.name;
60
+ this.id = token.id;
61
+ this.schema = token.schema;
62
+ }
63
+ id;
64
+ name;
65
+ schema;
66
+ toString() {
67
+ return this.token.toString();
68
+ }
72
69
  };
73
70
  var FactoryInjectionToken = class {
74
- value;
75
- resolved = false;
76
- id;
77
- name;
78
- schema;
79
- constructor(token, factory) {
80
- this.token = token;
81
- this.factory = factory;
82
- this.name = token.name;
83
- this.id = token.id;
84
- this.schema = token.schema;
85
- }
86
- async resolve() {
87
- if (!this.value) {
88
- this.value = await this.factory();
89
- this.resolved = true;
90
- }
91
- return this.value;
92
- }
93
- toString() {
94
- return this.token.toString();
95
- }
71
+ constructor(token, factory) {
72
+ this.token = token;
73
+ this.factory = factory;
74
+ this.name = token.name;
75
+ this.id = token.id;
76
+ this.schema = token.schema;
77
+ }
78
+ value;
79
+ resolved = false;
80
+ id;
81
+ name;
82
+ schema;
83
+ async resolve() {
84
+ if (!this.value) {
85
+ this.value = await this.factory();
86
+ this.resolved = true;
87
+ }
88
+ return this.value;
89
+ }
90
+ toString() {
91
+ return this.token.toString();
92
+ }
96
93
  };
97
94
 
98
- //#endregion
99
- //#region src/registry.mts
95
+ // src/registry.mts
96
+ import "zod";
100
97
  var Registry = class {
101
- factories = new Map();
102
- constructor(parent) {
103
- this.parent = parent;
104
- }
105
- has(token) {
106
- if (this.factories.has(token.id)) return true;
107
- if (this.parent) return this.parent.has(token);
108
- return false;
109
- }
110
- get(token) {
111
- const factory = this.factories.get(token.id);
112
- if (!factory) {
113
- if (this.parent) return this.parent.get(token);
114
- throw new Error(`[Registry] No factory found for ${token.toString()}`);
115
- }
116
- return factory;
117
- }
118
- set(token, factory, scope) {
119
- this.factories.set(token.id, {
120
- factory,
121
- scope,
122
- originalToken: token
123
- });
124
- }
125
- delete(token) {
126
- this.factories.delete(token.id);
127
- }
98
+ constructor(parent) {
99
+ this.parent = parent;
100
+ }
101
+ factories = /* @__PURE__ */ new Map();
102
+ has(token) {
103
+ if (this.factories.has(token.id)) {
104
+ return true;
105
+ }
106
+ if (this.parent) {
107
+ return this.parent.has(token);
108
+ }
109
+ return false;
110
+ }
111
+ get(token) {
112
+ const factory = this.factories.get(token.id);
113
+ if (!factory) {
114
+ if (this.parent) {
115
+ return this.parent.get(token);
116
+ }
117
+ throw new Error(`[Registry] No factory found for ${token.toString()}`);
118
+ }
119
+ return factory;
120
+ }
121
+ set(token, factory, scope) {
122
+ this.factories.set(token.id, { factory, scope, originalToken: token });
123
+ }
124
+ delete(token) {
125
+ this.factories.delete(token.id);
126
+ }
128
127
  };
129
- const globalRegistry = new Registry();
128
+ var globalRegistry = new Registry();
130
129
 
131
- //#endregion
132
- //#region src/proxy-service-locator.mts
130
+ // src/proxy-service-locator.mts
133
131
  var ProxyServiceLocator = class {
134
- constructor(serviceLocator, ctx) {
135
- this.serviceLocator = serviceLocator;
136
- this.ctx = ctx;
137
- }
138
- getEventBus() {
139
- return this.serviceLocator.getEventBus();
140
- }
141
- getInstance(token, args) {
142
- return this.ctx.inject(token, args).then((instance) => {
143
- return [void 0, instance];
144
- }, (error) => {
145
- return [error];
146
- });
147
- }
148
- getOrThrowInstance(token, args) {
149
- return this.ctx.inject(token, args);
150
- }
151
- getSyncInstance(token, args) {
152
- return this.serviceLocator.getSyncInstance(token, args);
153
- }
154
- invalidate(service, round) {
155
- return this.serviceLocator.invalidate(service, round);
156
- }
157
- ready() {
158
- return this.serviceLocator.ready();
159
- }
160
- makeInstanceName(token, args) {
161
- return this.serviceLocator.makeInstanceName(token, args);
162
- }
132
+ constructor(serviceLocator, ctx) {
133
+ this.serviceLocator = serviceLocator;
134
+ this.ctx = ctx;
135
+ }
136
+ getEventBus() {
137
+ return this.serviceLocator.getEventBus();
138
+ }
139
+ // @ts-expect-error We don't need all the properties of the class
140
+ getInstance(token, args) {
141
+ return this.ctx.inject(token, args).then(
142
+ (instance) => {
143
+ return [void 0, instance];
144
+ },
145
+ (error) => {
146
+ return [error];
147
+ }
148
+ );
149
+ }
150
+ getOrThrowInstance(token, args) {
151
+ return this.ctx.inject(token, args);
152
+ }
153
+ getSyncInstance(token, args) {
154
+ return this.serviceLocator.getSyncInstance(token, args);
155
+ }
156
+ invalidate(service, round) {
157
+ return this.serviceLocator.invalidate(service, round);
158
+ }
159
+ ready() {
160
+ return this.serviceLocator.ready();
161
+ }
162
+ makeInstanceName(token, args) {
163
+ return this.serviceLocator.makeInstanceName(token, args);
164
+ }
163
165
  };
164
166
  function makeProxyServiceLocator(serviceLocator, ctx) {
165
- return new ProxyServiceLocator(serviceLocator, ctx);
167
+ return new ProxyServiceLocator(serviceLocator, ctx);
166
168
  }
167
169
 
168
- //#endregion
169
- //#region src/symbols/injectable-token.mts
170
- const InjectableTokenMeta = Symbol.for("InjectableTokenMeta");
170
+ // src/symbols/injectable-token.mts
171
+ var InjectableTokenMeta = Symbol.for("InjectableTokenMeta");
171
172
 
172
- //#endregion
173
- //#region src/utils/get-injectors.mts
174
- const InjectorsBase = new Map();
173
+ // src/utils/get-injectors.mts
174
+ var InjectorsBase = /* @__PURE__ */ new Map();
175
175
  function getInjectors({ baseLocator }) {
176
- if (InjectorsBase.has(baseLocator)) return InjectorsBase.get(baseLocator);
177
- let currentLocator = baseLocator;
178
- function getServiceLocator() {
179
- if (!currentLocator) throw new Error("[Injector] Service locator is not initialized. Please provide the service locator before using the @Injectable decorator.");
180
- return currentLocator;
181
- }
182
- function provideServiceLocator$1(locator) {
183
- const original = currentLocator;
184
- currentLocator = locator;
185
- return original;
186
- }
187
- function inject$1(token, args) {
188
- const realToken = token[InjectableTokenMeta] ?? token;
189
- return getServiceLocator().getOrThrowInstance(realToken, args);
190
- }
191
- let promiseCollector = null;
192
- function wrapSyncInit$1(cb) {
193
- return () => {
194
- const promises = [];
195
- const originalPromiseCollector = promiseCollector;
196
- promiseCollector = (promise) => {
197
- promises.push(promise);
198
- };
199
- const result = cb();
200
- promiseCollector = originalPromiseCollector;
201
- return [result, promises];
202
- };
203
- }
204
- function syncInject$1(token, args) {
205
- const realToken = token[InjectableTokenMeta] ?? token;
206
- const instance = getServiceLocator().getSyncInstance(realToken, args);
207
- if (!instance) if (promiseCollector) {
208
- const promise = getServiceLocator().getInstance(realToken, args);
209
- promiseCollector(promise);
210
- } else throw new Error(`[Injector] Cannot initiate ${realToken.toString()}`);
211
- return instance;
212
- }
213
- const injectors = {
214
- inject: inject$1,
215
- syncInject: syncInject$1,
216
- wrapSyncInit: wrapSyncInit$1,
217
- provideServiceLocator: provideServiceLocator$1
218
- };
219
- InjectorsBase.set(baseLocator, injectors);
220
- return injectors;
176
+ if (InjectorsBase.has(baseLocator)) {
177
+ return InjectorsBase.get(baseLocator);
178
+ }
179
+ let currentLocator = baseLocator;
180
+ function getServiceLocator() {
181
+ if (!currentLocator) {
182
+ throw new Error(
183
+ "[Injector] Service locator is not initialized. Please provide the service locator before using the @Injectable decorator."
184
+ );
185
+ }
186
+ return currentLocator;
187
+ }
188
+ function provideServiceLocator2(locator) {
189
+ const original = currentLocator;
190
+ currentLocator = locator;
191
+ return original;
192
+ }
193
+ function inject2(token, args) {
194
+ const realToken = token[InjectableTokenMeta] ?? token;
195
+ return getServiceLocator().getOrThrowInstance(realToken, args);
196
+ }
197
+ let promiseCollector = null;
198
+ function wrapSyncInit2(cb) {
199
+ return () => {
200
+ const promises = [];
201
+ const originalPromiseCollector = promiseCollector;
202
+ promiseCollector = (promise) => {
203
+ promises.push(promise);
204
+ };
205
+ const result = cb();
206
+ promiseCollector = originalPromiseCollector;
207
+ return [result, promises];
208
+ };
209
+ }
210
+ function syncInject2(token, args) {
211
+ const realToken = token[InjectableTokenMeta] ?? token;
212
+ const instance = getServiceLocator().getSyncInstance(realToken, args);
213
+ if (!instance) {
214
+ if (promiseCollector) {
215
+ const promise = getServiceLocator().getInstance(realToken, args);
216
+ promiseCollector(promise);
217
+ } else {
218
+ throw new Error(`[Injector] Cannot initiate ${realToken.toString()}`);
219
+ }
220
+ return new Proxy(
221
+ {},
222
+ {
223
+ get() {
224
+ throw new Error(
225
+ `[Injector] Trying to access ${realToken.toString()} before it's initialized, please use inject() instead of syncInject() or do not use the value outside of class methods`
226
+ );
227
+ }
228
+ }
229
+ );
230
+ }
231
+ return instance;
232
+ }
233
+ const injectors = {
234
+ inject: inject2,
235
+ syncInject: syncInject2,
236
+ wrapSyncInit: wrapSyncInit2,
237
+ provideServiceLocator: provideServiceLocator2
238
+ };
239
+ InjectorsBase.set(baseLocator, injectors);
240
+ return injectors;
221
241
  }
222
242
 
223
- //#endregion
224
- //#region src/utils/get-injectable-token.mts
243
+ // src/utils/get-injectable-token.mts
225
244
  function getInjectableToken(target) {
226
- const token = target[InjectableTokenMeta];
227
- if (!token) throw new Error(`[ServiceLocator] Class ${target.name} is not decorated with @Injectable.`);
228
- return token;
245
+ const token = target[InjectableTokenMeta];
246
+ if (!token) {
247
+ throw new Error(
248
+ `[ServiceLocator] Class ${target.name} is not decorated with @Injectable.`
249
+ );
250
+ }
251
+ return token;
229
252
  }
230
253
 
231
- //#endregion
232
- //#region src/resolve-service.mts
254
+ // src/resolve-service.mts
233
255
  async function resolveService(ctx, target, args = []) {
234
- const proxyServiceLocator = makeProxyServiceLocator(ctx.locator, ctx);
235
- const { wrapSyncInit: wrapSyncInit$1, provideServiceLocator: provideServiceLocator$1 } = getInjectors({ baseLocator: ctx.locator });
236
- const tryLoad = wrapSyncInit$1(() => {
237
- const original = provideServiceLocator$1(proxyServiceLocator);
238
- let result = new target(...args);
239
- provideServiceLocator$1(original);
240
- return result;
241
- });
242
- let [instance, promises] = tryLoad();
243
- if (promises.length > 0) {
244
- await Promise.all(promises);
245
- const newRes = tryLoad();
246
- instance = newRes[0];
247
- promises = newRes[1];
248
- }
249
- if (promises.length > 0) {
250
- console.error(`[ServiceLocator] ${target.name} has problem with it's definition.
256
+ const { wrapSyncInit: wrapSyncInit2, provideServiceLocator: provideServiceLocator2 } = getInjectors({
257
+ baseLocator: ctx.locator
258
+ });
259
+ const proxyServiceLocator = makeProxyServiceLocator(ctx.locator, ctx);
260
+ const tryLoad = wrapSyncInit2(() => {
261
+ const original = provideServiceLocator2(proxyServiceLocator);
262
+ let result = new target(...args);
263
+ provideServiceLocator2(original);
264
+ return result;
265
+ });
266
+ let [instance, promises] = tryLoad();
267
+ if (promises.length > 0) {
268
+ await Promise.allSettled(promises);
269
+ const newRes = tryLoad();
270
+ instance = newRes[0];
271
+ promises = newRes[1];
272
+ }
273
+ if (promises.length > 0) {
274
+ console.error(`[ServiceLocator] ${target.name} has problem with it's definition.
251
275
 
252
276
  One or more of the dependencies are registered as a InjectableScope.Instance and are used with syncInject.
253
277
 
254
278
  Please use inject instead of syncInject to load those dependencies.`);
255
- throw new Error(`[ServiceLocator] Service ${target.name} cannot be instantiated.`);
256
- }
257
- return instance;
279
+ throw new Error(
280
+ `[ServiceLocator] Service ${target.name} cannot be instantiated.`
281
+ );
282
+ }
283
+ return instance;
258
284
  }
259
285
 
260
- //#endregion
261
- //#region src/decorators/injectable.decorator.mts
262
- function Injectable({ scope = InjectableScope.Singleton, type = InjectableType.Class, token, registry = globalRegistry } = {}) {
263
- return (target, context) => {
264
- if (context.kind !== "class") throw new Error("[ServiceLocator] @Injectable decorator can only be used on classes.");
265
- let injectableToken = token ?? InjectionToken.create(target);
266
- if (type === InjectableType.Class) registry.set(injectableToken, async (ctx, args) => resolveService(ctx, target, [args]), scope);
267
- else if (type === InjectableType.Factory) registry.set(injectableToken, async (ctx, args) => {
268
- const builder = await resolveService(ctx, target);
269
- if (typeof builder.create !== "function") throw new Error(`[ServiceLocator] Factory ${target.name} does not implement the create method.`);
270
- return builder.create(ctx, args);
271
- }, scope);
272
- target[InjectableTokenMeta] = injectableToken;
273
- return target;
274
- };
286
+ // src/decorators/injectable.decorator.mts
287
+ function Injectable({
288
+ scope = "Singleton" /* Singleton */,
289
+ type = "Class" /* Class */,
290
+ token,
291
+ registry = globalRegistry
292
+ } = {}) {
293
+ return (target, context) => {
294
+ if (context.kind !== "class") {
295
+ throw new Error(
296
+ "[ServiceLocator] @Injectable decorator can only be used on classes."
297
+ );
298
+ }
299
+ let injectableToken = token ?? InjectionToken.create(target);
300
+ if (type === "Class" /* Class */) {
301
+ registry.set(
302
+ injectableToken,
303
+ async (ctx, args) => resolveService(ctx, target, [args]),
304
+ scope
305
+ );
306
+ } else if (type === "Factory" /* Factory */) {
307
+ registry.set(
308
+ injectableToken,
309
+ async (ctx, args) => {
310
+ const builder = await resolveService(ctx, target);
311
+ if (typeof builder.create !== "function") {
312
+ throw new Error(
313
+ `[ServiceLocator] Factory ${target.name} does not implement the create method.`
314
+ );
315
+ }
316
+ return builder.create(ctx, args);
317
+ },
318
+ scope
319
+ );
320
+ }
321
+ target[InjectableTokenMeta] = injectableToken;
322
+ return target;
323
+ };
275
324
  }
276
325
 
277
- //#endregion
278
- //#region src/errors/errors.enum.mts
279
- let ErrorsEnum = /* @__PURE__ */ function(ErrorsEnum$1) {
280
- ErrorsEnum$1["InstanceExpired"] = "InstanceExpired";
281
- ErrorsEnum$1["InstanceNotFound"] = "InstanceNotFound";
282
- ErrorsEnum$1["InstanceDestroying"] = "InstanceDestroying";
283
- ErrorsEnum$1["UnknownError"] = "UnknownError";
284
- ErrorsEnum$1["FactoryNotFound"] = "FactoryNotFound";
285
- ErrorsEnum$1["FactoryTokenNotResolved"] = "FactoryTokenNotResolved";
286
- return ErrorsEnum$1;
287
- }({});
326
+ // src/errors/errors.enum.mts
327
+ var ErrorsEnum = /* @__PURE__ */ ((ErrorsEnum2) => {
328
+ ErrorsEnum2["InstanceExpired"] = "InstanceExpired";
329
+ ErrorsEnum2["InstanceNotFound"] = "InstanceNotFound";
330
+ ErrorsEnum2["InstanceDestroying"] = "InstanceDestroying";
331
+ ErrorsEnum2["UnknownError"] = "UnknownError";
332
+ ErrorsEnum2["FactoryNotFound"] = "FactoryNotFound";
333
+ ErrorsEnum2["FactoryTokenNotResolved"] = "FactoryTokenNotResolved";
334
+ return ErrorsEnum2;
335
+ })(ErrorsEnum || {});
288
336
 
289
- //#endregion
290
- //#region src/errors/factory-not-found.mts
337
+ // src/errors/factory-not-found.mts
291
338
  var FactoryNotFound = class extends Error {
292
- code = ErrorsEnum.FactoryNotFound;
293
- constructor(name) {
294
- super(`Factory ${name} not found`);
295
- this.name = name;
296
- }
339
+ constructor(name) {
340
+ super(`Factory ${name} not found`);
341
+ this.name = name;
342
+ }
343
+ code = "FactoryNotFound" /* FactoryNotFound */;
297
344
  };
298
345
 
299
- //#endregion
300
- //#region src/errors/factory-token-not-resolved.mts
346
+ // src/errors/factory-token-not-resolved.mts
301
347
  var FactoryTokenNotResolved = class extends Error {
302
- code = ErrorsEnum.FactoryTokenNotResolved;
303
- constructor(name) {
304
- super(`Factory token not resolved: ${name.toString()}`);
305
- }
348
+ code = "FactoryTokenNotResolved" /* FactoryTokenNotResolved */;
349
+ constructor(name) {
350
+ super(`Factory token not resolved: ${name.toString()}`);
351
+ }
306
352
  };
307
353
 
308
- //#endregion
309
- //#region src/errors/instance-destroying.mts
354
+ // src/errors/instance-destroying.mts
310
355
  var InstanceDestroying = class extends Error {
311
- code = ErrorsEnum.InstanceDestroying;
312
- constructor(name) {
313
- super(`Instance ${name} destroying`);
314
- this.name = name;
315
- }
356
+ constructor(name) {
357
+ super(`Instance ${name} destroying`);
358
+ this.name = name;
359
+ }
360
+ code = "InstanceDestroying" /* InstanceDestroying */;
316
361
  };
317
362
 
318
- //#endregion
319
- //#region src/errors/instance-expired.mts
363
+ // src/errors/instance-expired.mts
320
364
  var InstanceExpired = class extends Error {
321
- code = ErrorsEnum.InstanceExpired;
322
- constructor(name) {
323
- super(`Instance ${name} expired`);
324
- this.name = name;
325
- }
365
+ constructor(name) {
366
+ super(`Instance ${name} expired`);
367
+ this.name = name;
368
+ }
369
+ code = "InstanceExpired" /* InstanceExpired */;
326
370
  };
327
371
 
328
- //#endregion
329
- //#region src/errors/instance-not-found.mts
372
+ // src/errors/instance-not-found.mts
330
373
  var InstanceNotFound = class extends Error {
331
- code = ErrorsEnum.InstanceNotFound;
332
- constructor(name) {
333
- super(`Instance ${name} not found`);
334
- this.name = name;
335
- }
374
+ constructor(name) {
375
+ super(`Instance ${name} not found`);
376
+ this.name = name;
377
+ }
378
+ code = "InstanceNotFound" /* InstanceNotFound */;
336
379
  };
337
380
 
338
- //#endregion
339
- //#region src/errors/unknown-error.mts
381
+ // src/errors/unknown-error.mts
340
382
  var UnknownError = class extends Error {
341
- code = ErrorsEnum.UnknownError;
342
- parent;
343
- constructor(message) {
344
- if (message instanceof Error) {
345
- super(message.message);
346
- this.parent = message;
347
- return;
348
- }
349
- super(message);
350
- }
383
+ code = "UnknownError" /* UnknownError */;
384
+ parent;
385
+ constructor(message) {
386
+ if (message instanceof Error) {
387
+ super(message.message);
388
+ this.parent = message;
389
+ return;
390
+ }
391
+ super(message);
392
+ }
351
393
  };
352
394
 
353
- //#endregion
354
- //#region src/event-emitter.mts
395
+ // src/interfaces/factory.interface.mts
396
+ import "zod";
397
+
398
+ // src/event-emitter.mts
355
399
  var EventEmitter = class {
356
- listeners = new Map();
357
- on(event, listener) {
358
- if (!this.listeners.has(event)) this.listeners.set(event, new Set());
359
- this.listeners.get(event).add(listener);
360
- return () => {
361
- this.off(event, listener);
362
- };
363
- }
364
- off(event, listener) {
365
- if (!this.listeners.has(event)) return;
366
- this.listeners.get(event).delete(listener);
367
- if (this.listeners.get(event).size === 0) this.listeners.delete(event);
368
- }
369
- once(event, listener) {
370
- const off = this.on(event, (...args) => {
371
- off();
372
- listener(...args);
373
- });
374
- return off;
375
- }
376
- async emit(event, ...args) {
377
- if (!this.listeners.has(event)) return;
378
- return Promise.all(Array.from(this.listeners.get(event)).map((listener) => listener(...args)));
379
- }
380
- addChannel(ns, event, target) {
381
- return this.on(event, (...args) => target.emit(ns, event, ...args));
382
- }
400
+ listeners = /* @__PURE__ */ new Map();
401
+ on(event, listener) {
402
+ if (!this.listeners.has(event)) {
403
+ this.listeners.set(event, /* @__PURE__ */ new Set());
404
+ }
405
+ this.listeners.get(event).add(listener);
406
+ return () => {
407
+ this.off(event, listener);
408
+ };
409
+ }
410
+ off(event, listener) {
411
+ if (!this.listeners.has(event)) {
412
+ return;
413
+ }
414
+ this.listeners.get(event).delete(listener);
415
+ if (this.listeners.get(event).size === 0) {
416
+ this.listeners.delete(event);
417
+ }
418
+ }
419
+ once(event, listener) {
420
+ const off = this.on(event, (...args) => {
421
+ off();
422
+ listener(...args);
423
+ });
424
+ return off;
425
+ }
426
+ async emit(event, ...args) {
427
+ if (!this.listeners.has(event)) {
428
+ return;
429
+ }
430
+ return Promise.all(Array.from(this.listeners.get(event)).map((listener) => listener(...args)));
431
+ }
432
+ addChannel(ns, event, target) {
433
+ return this.on(event, (...args) => target.emit(ns, event, ...args));
434
+ }
383
435
  };
384
436
 
385
- //#endregion
386
- //#region src/service-locator-event-bus.mts
437
+ // src/service-locator-event-bus.mts
387
438
  var ServiceLocatorEventBus = class {
388
- listeners = new Map();
389
- constructor(logger = null) {
390
- this.logger = logger;
391
- }
392
- on(ns, event, listener) {
393
- this.logger?.debug(`[ServiceLocatorEventBus]#on(): ns:${ns} event:${event}`);
394
- if (!this.listeners.has(ns)) this.listeners.set(ns, new Map());
395
- const nsEvents = this.listeners.get(ns);
396
- if (!nsEvents.has(event)) nsEvents.set(event, new Set());
397
- nsEvents.get(event).add(listener);
398
- return () => {
399
- nsEvents.get(event).delete(listener);
400
- if (nsEvents.get(event)?.size === 0) nsEvents.delete(event);
401
- if (nsEvents.size === 0) this.listeners.delete(ns);
402
- };
403
- }
404
- async emit(key, event) {
405
- if (!this.listeners.has(key)) return;
406
- const events = this.listeners.get(key);
407
- const preEvent = `pre:${event}`;
408
- const postEvent = `post:${event}`;
409
- this.logger?.debug(`[ServiceLocatorEventBus]#emit(): ${key}:${preEvent}`);
410
- await Promise.allSettled([...events.get(preEvent) ?? []].map((listener) => listener(preEvent))).then((results) => {
411
- results.filter((result) => result.status === "rejected").forEach((result) => {
412
- this.logger?.warn(`[ServiceLocatorEventBus]#emit(): ${key}:${preEvent} rejected with`, result.reason);
413
- });
414
- });
415
- this.logger?.debug(`[ServiceLocatorEventBus]#emit(): ${key}:${event}`);
416
- const res = await Promise.allSettled([...events.get(event) ?? []].map((listener) => listener(event))).then((results) => {
417
- const res$1 = results.filter((result) => result.status === "rejected").map((result) => {
418
- this.logger?.warn(`[ServiceLocatorEventBus]#emit(): ${key}:${event} rejected with`, result.reason);
419
- return result;
420
- });
421
- if (res$1.length > 0) return Promise.reject(res$1);
422
- return results;
423
- });
424
- this.logger?.debug(`[ServiceLocatorEventBus]#emit(): ${key}:${postEvent}`);
425
- await Promise.allSettled([...events.get(postEvent) ?? []].map((listener) => listener(postEvent))).then((results) => {
426
- results.filter((result) => result.status === "rejected").forEach((result) => {
427
- this.logger?.warn(`[ServiceLocatorEventBus]#emit(): ${key}:${postEvent} rejected with`, result.reason);
428
- });
429
- });
430
- return res;
431
- }
439
+ constructor(logger = null) {
440
+ this.logger = logger;
441
+ }
442
+ listeners = /* @__PURE__ */ new Map();
443
+ on(ns, event, listener) {
444
+ this.logger?.debug(`[ServiceLocatorEventBus]#on(): ns:${ns} event:${event}`);
445
+ if (!this.listeners.has(ns)) {
446
+ this.listeners.set(ns, /* @__PURE__ */ new Map());
447
+ }
448
+ const nsEvents = this.listeners.get(ns);
449
+ if (!nsEvents.has(event)) {
450
+ nsEvents.set(event, /* @__PURE__ */ new Set());
451
+ }
452
+ nsEvents.get(event).add(listener);
453
+ return () => {
454
+ nsEvents.get(event).delete(listener);
455
+ if (nsEvents.get(event)?.size === 0) {
456
+ nsEvents.delete(event);
457
+ }
458
+ if (nsEvents.size === 0) {
459
+ this.listeners.delete(ns);
460
+ }
461
+ };
462
+ }
463
+ async emit(key, event) {
464
+ if (!this.listeners.has(key)) {
465
+ return;
466
+ }
467
+ const events = this.listeners.get(key);
468
+ const preEvent = `pre:${event}`;
469
+ const postEvent = `post:${event}`;
470
+ this.logger?.debug(`[ServiceLocatorEventBus]#emit(): ${key}:${preEvent}`);
471
+ await Promise.allSettled(
472
+ [...events.get(preEvent) ?? []].map((listener) => listener(preEvent))
473
+ ).then((results) => {
474
+ results.filter((result) => result.status === "rejected").forEach((result) => {
475
+ this.logger?.warn(
476
+ `[ServiceLocatorEventBus]#emit(): ${key}:${preEvent} rejected with`,
477
+ result.reason
478
+ );
479
+ });
480
+ });
481
+ this.logger?.debug(`[ServiceLocatorEventBus]#emit(): ${key}:${event}`);
482
+ const res = await Promise.allSettled(
483
+ [...events.get(event) ?? []].map((listener) => listener(event))
484
+ ).then((results) => {
485
+ const res2 = results.filter((result) => result.status === "rejected").map((result) => {
486
+ this.logger?.warn(
487
+ `[ServiceLocatorEventBus]#emit(): ${key}:${event} rejected with`,
488
+ result.reason
489
+ );
490
+ return result;
491
+ });
492
+ if (res2.length > 0) {
493
+ return Promise.reject(res2);
494
+ }
495
+ return results;
496
+ });
497
+ this.logger?.debug(`[ServiceLocatorEventBus]#emit(): ${key}:${postEvent}`);
498
+ await Promise.allSettled(
499
+ [...events.get(postEvent) ?? []].map((listener) => listener(postEvent))
500
+ ).then((results) => {
501
+ results.filter((result) => result.status === "rejected").forEach((result) => {
502
+ this.logger?.warn(
503
+ `[ServiceLocatorEventBus]#emit(): ${key}:${postEvent} rejected with`,
504
+ result.reason
505
+ );
506
+ });
507
+ });
508
+ return res;
509
+ }
432
510
  };
433
511
 
434
- //#endregion
435
- //#region src/service-locator-instance-holder.mts
436
- let ServiceLocatorInstanceHolderKind = /* @__PURE__ */ function(ServiceLocatorInstanceHolderKind$1) {
437
- ServiceLocatorInstanceHolderKind$1["Instance"] = "instance";
438
- ServiceLocatorInstanceHolderKind$1["Factory"] = "factory";
439
- ServiceLocatorInstanceHolderKind$1["AbstractFactory"] = "abstractFactory";
440
- return ServiceLocatorInstanceHolderKind$1;
441
- }({});
442
- let ServiceLocatorInstanceHolderStatus = /* @__PURE__ */ function(ServiceLocatorInstanceHolderStatus$1) {
443
- ServiceLocatorInstanceHolderStatus$1["Created"] = "created";
444
- ServiceLocatorInstanceHolderStatus$1["Creating"] = "creating";
445
- ServiceLocatorInstanceHolderStatus$1["Destroying"] = "destroying";
446
- return ServiceLocatorInstanceHolderStatus$1;
447
- }({});
512
+ // src/service-locator-instance-holder.mts
513
+ var ServiceLocatorInstanceHolderKind = /* @__PURE__ */ ((ServiceLocatorInstanceHolderKind2) => {
514
+ ServiceLocatorInstanceHolderKind2["Instance"] = "instance";
515
+ ServiceLocatorInstanceHolderKind2["Factory"] = "factory";
516
+ ServiceLocatorInstanceHolderKind2["AbstractFactory"] = "abstractFactory";
517
+ return ServiceLocatorInstanceHolderKind2;
518
+ })(ServiceLocatorInstanceHolderKind || {});
519
+ var ServiceLocatorInstanceHolderStatus = /* @__PURE__ */ ((ServiceLocatorInstanceHolderStatus2) => {
520
+ ServiceLocatorInstanceHolderStatus2["Created"] = "created";
521
+ ServiceLocatorInstanceHolderStatus2["Creating"] = "creating";
522
+ ServiceLocatorInstanceHolderStatus2["Destroying"] = "destroying";
523
+ return ServiceLocatorInstanceHolderStatus2;
524
+ })(ServiceLocatorInstanceHolderStatus || {});
448
525
 
449
- //#endregion
450
- //#region src/service-locator-manager.mts
526
+ // src/service-locator-manager.mts
451
527
  var ServiceLocatorManager = class {
452
- instancesHolders = new Map();
453
- constructor(logger = null) {
454
- this.logger = logger;
455
- }
456
- get(name) {
457
- const holder = this.instancesHolders.get(name);
458
- if (holder) {
459
- if (holder.ttl !== Infinity) {
460
- const now = Date.now();
461
- if (now - holder.createdAt > holder.ttl) {
462
- this.logger?.log(`[ServiceLocatorManager]#getInstanceHolder() TTL expired for ${holder.name}`);
463
- return [new InstanceExpired(holder.name), holder];
464
- }
465
- } else if (holder.status === ServiceLocatorInstanceHolderStatus.Destroying) {
466
- this.logger?.log(`[ServiceLocatorManager]#getInstanceHolder() Instance ${holder.name} is destroying`);
467
- return [new InstanceDestroying(holder.name), holder];
468
- }
469
- return [void 0, holder];
470
- } else {
471
- this.logger?.log(`[ServiceLocatorManager]#getInstanceHolder() Instance ${name} not found`);
472
- return [new InstanceNotFound(name)];
473
- }
474
- }
475
- set(name, holder) {
476
- this.instancesHolders.set(name, holder);
477
- }
478
- has(name) {
479
- const [error, holder] = this.get(name);
480
- if (!error) return [void 0, true];
481
- if ([ErrorsEnum.InstanceExpired, ErrorsEnum.InstanceDestroying].includes(error.code)) return [error];
482
- return [void 0, !!holder];
483
- }
484
- delete(name) {
485
- return this.instancesHolders.delete(name);
486
- }
487
- filter(predicate) {
488
- return new Map([...this.instancesHolders].filter(([key, value]) => predicate(value, key)));
489
- }
528
+ constructor(logger = null) {
529
+ this.logger = logger;
530
+ }
531
+ instancesHolders = /* @__PURE__ */ new Map();
532
+ get(name) {
533
+ const holder = this.instancesHolders.get(name);
534
+ if (holder) {
535
+ if (holder.ttl !== Infinity) {
536
+ const now = Date.now();
537
+ if (now - holder.createdAt > holder.ttl) {
538
+ this.logger?.log(
539
+ `[ServiceLocatorManager]#getInstanceHolder() TTL expired for ${holder.name}`
540
+ );
541
+ return [new InstanceExpired(holder.name), holder];
542
+ }
543
+ } else if (holder.status === "destroying" /* Destroying */) {
544
+ this.logger?.log(
545
+ `[ServiceLocatorManager]#getInstanceHolder() Instance ${holder.name} is destroying`
546
+ );
547
+ return [new InstanceDestroying(holder.name), holder];
548
+ }
549
+ return [void 0, holder];
550
+ } else {
551
+ this.logger?.log(
552
+ `[ServiceLocatorManager]#getInstanceHolder() Instance ${name} not found`
553
+ );
554
+ return [new InstanceNotFound(name)];
555
+ }
556
+ }
557
+ set(name, holder) {
558
+ this.instancesHolders.set(name, holder);
559
+ }
560
+ has(name) {
561
+ const [error, holder] = this.get(name);
562
+ if (!error) {
563
+ return [void 0, true];
564
+ }
565
+ if (["InstanceExpired" /* InstanceExpired */, "InstanceDestroying" /* InstanceDestroying */].includes(
566
+ error.code
567
+ )) {
568
+ return [error];
569
+ }
570
+ return [void 0, !!holder];
571
+ }
572
+ delete(name) {
573
+ return this.instancesHolders.delete(name);
574
+ }
575
+ filter(predicate) {
576
+ return new Map(
577
+ [...this.instancesHolders].filter(
578
+ ([key, value]) => predicate(value, key)
579
+ )
580
+ );
581
+ }
490
582
  };
491
583
 
492
- //#endregion
493
- //#region src/service-locator.mts
584
+ // src/service-locator.mts
494
585
  var ServiceLocator = class {
495
- eventBus;
496
- manager;
497
- constructor(registry = globalRegistry, logger = null) {
498
- this.registry = registry;
499
- this.logger = logger;
500
- this.eventBus = new ServiceLocatorEventBus(logger);
501
- this.manager = new ServiceLocatorManager(logger);
502
- }
503
- getEventBus() {
504
- return this.eventBus;
505
- }
506
- storeInstance(instance, token, args) {
507
- const instanceName = this.getInstanceIdentifier(token, args);
508
- this.manager.set(instanceName, {
509
- name: instanceName,
510
- instance,
511
- status: ServiceLocatorInstanceHolderStatus.Created,
512
- kind: ServiceLocatorInstanceHolderKind.Instance,
513
- createdAt: Date.now(),
514
- ttl: Infinity,
515
- deps: [],
516
- destroyListeners: [],
517
- effects: [],
518
- destroyPromise: null,
519
- creationPromise: null
520
- });
521
- this.notifyListeners(instanceName);
522
- }
523
- removeInstance(token, args) {
524
- const instanceName = this.getInstanceIdentifier(token, args);
525
- return this.invalidate(instanceName);
526
- }
527
- resolveTokenArgs(token, args) {
528
- let realArgs = args;
529
- if (token instanceof BoundInjectionToken) realArgs = token.value;
530
- else if (token instanceof FactoryInjectionToken) if (token.resolved) realArgs = token.value;
531
- else return [new FactoryTokenNotResolved(token.name)];
532
- if (!token.schema) return [void 0, realArgs];
533
- const validatedArgs = token.schema?.safeParse(realArgs);
534
- if (validatedArgs && !validatedArgs.success) {
535
- this.logger?.error(`[ServiceLocator]#getInstance(): Error validating args for ${token.name.toString()}`, validatedArgs.error);
536
- return [new UnknownError(validatedArgs.error)];
537
- }
538
- return [void 0, validatedArgs?.data];
539
- }
540
- getInstanceIdentifier(token, args) {
541
- const [err, realArgs] = this.resolveTokenArgs(token, args);
542
- if (err) throw err;
543
- return this.makeInstanceName(token, realArgs);
544
- }
545
- async getInstance(token, args) {
546
- const [err, realArgs] = this.resolveTokenArgs(token, args);
547
- if (err instanceof UnknownError) return [err];
548
- else if (err instanceof FactoryTokenNotResolved && token instanceof FactoryInjectionToken) {
549
- await token.resolve();
550
- return this.getInstance(token, args);
551
- }
552
- const instanceName = this.makeInstanceName(token, realArgs);
553
- const [error, holder] = this.manager.get(instanceName);
554
- if (!error) {
555
- if (holder.status === ServiceLocatorInstanceHolderStatus.Creating) return holder.creationPromise;
556
- else if (holder.status === ServiceLocatorInstanceHolderStatus.Destroying) return [new UnknownError(ErrorsEnum.InstanceDestroying)];
557
- return [void 0, holder.instance];
558
- }
559
- switch (error.code) {
560
- case ErrorsEnum.InstanceDestroying:
561
- this.logger?.log(`[ServiceLocator]#getInstance() TTL expired for ${holder?.name}`);
562
- await holder?.destroyPromise;
563
- return this.getInstance(token, args);
564
- case ErrorsEnum.InstanceExpired:
565
- this.logger?.log(`[ServiceLocator]#getInstance() TTL expired for ${holder?.name}`);
566
- await this.invalidate(instanceName);
567
- return this.getInstance(token, args);
568
- case ErrorsEnum.InstanceNotFound: break;
569
- default: return [error];
570
- }
571
- return this.createInstance(instanceName, token, realArgs);
572
- }
573
- async getOrThrowInstance(token, args) {
574
- const [error, instance] = await this.getInstance(token, args);
575
- if (error) throw error;
576
- return instance;
577
- }
578
- notifyListeners(name, event = "create") {
579
- this.logger?.log(`[ServiceLocator]#notifyListeners() Notifying listeners for ${name} with event ${event}`);
580
- return this.eventBus.emit(name, event);
581
- }
582
- async createInstance(instanceName, token, args) {
583
- this.logger?.log(`[ServiceLocator]#createInstance() Creating instance for ${instanceName}`);
584
- let realToken = token instanceof BoundInjectionToken || token instanceof FactoryInjectionToken ? token.token : token;
585
- if (this.registry.has(realToken)) return this.resolveInstance(instanceName, realToken, args);
586
- else return [new FactoryNotFound(realToken.name.toString())];
587
- }
588
- async resolveInstance(instanceName, token, args) {
589
- this.logger?.log(`[ServiceLocator]#resolveInstance(): Creating instance for ${instanceName} from abstract factory`);
590
- const ctx = this.createFactoryContext(instanceName);
591
- let { factory, scope } = this.registry.get(token);
592
- const holder = {
593
- name: instanceName,
594
- instance: null,
595
- status: ServiceLocatorInstanceHolderStatus.Creating,
596
- kind: ServiceLocatorInstanceHolderKind.AbstractFactory,
597
- creationPromise: factory(ctx, args).then(async (instance) => {
598
- holder.instance = instance;
599
- holder.status = ServiceLocatorInstanceHolderStatus.Created;
600
- holder.deps = ctx.getDependencies();
601
- holder.destroyListeners = ctx.getDestroyListeners();
602
- holder.ttl = ctx.getTtl();
603
- if (holder.deps.length > 0) {
604
- this.logger?.log(`[ServiceLocator]#createInstanceFromAbstractFactory(): Adding subscriptions for ${instanceName} dependencies for their invalidations: ${holder.deps.join(", ")}`);
605
- holder.deps.forEach((dependency) => {
606
- holder.destroyListeners.push(this.eventBus.on(dependency, "destroy", () => this.invalidate(instanceName)));
607
- });
608
- }
609
- if (holder.ttl === 0 || scope === InjectableScope.Instance) await this.invalidate(instanceName);
610
- await this.notifyListeners(instanceName);
611
- return [void 0, instance];
612
- }).catch((error) => {
613
- this.logger?.error(`[ServiceLocator]#createInstanceFromAbstractFactory(): Error creating instance for ${instanceName}`, error);
614
- return [new UnknownError(error)];
615
- }),
616
- effects: [],
617
- deps: [],
618
- destroyListeners: [],
619
- createdAt: Date.now(),
620
- ttl: Infinity
621
- };
622
- if (scope === InjectableScope.Singleton) this.manager.set(instanceName, holder);
623
- return holder.creationPromise;
624
- }
625
- createFactoryContext(instanceName) {
626
- const dependencies = new Set();
627
- const destroyListeners = new Set();
628
- const self = this;
629
- function invalidate(name = instanceName) {
630
- return self.invalidate(name);
631
- }
632
- function addEffect(listener) {
633
- destroyListeners.add(listener);
634
- }
635
- let ttl = Infinity;
636
- function setTtl(value) {
637
- ttl = value;
638
- }
639
- function getTtl() {
640
- return ttl;
641
- }
642
- function on(key, event, listener) {
643
- destroyListeners.add(self.eventBus.on(key, event, listener));
644
- }
645
- return {
646
- async inject(token, args) {
647
- let injectionToken = token;
648
- if (typeof token === "function") injectionToken = getInjectableToken(token);
649
- if (injectionToken instanceof InjectionToken) {
650
- const validatedArgs = token.schema ? token.schema.safeParse(args) : void 0;
651
- const instanceName$1 = self.makeInstanceName(token, validatedArgs);
652
- dependencies.add(instanceName$1);
653
- return self.getOrThrowInstance(injectionToken, args);
654
- }
655
- throw new Error(`[ServiceLocator]#inject(): Invalid token type: ${typeof token}. Expected a class or an InjectionToken.`);
656
- },
657
- invalidate,
658
- on,
659
- getDependencies: () => Array.from(dependencies),
660
- addEffect,
661
- getDestroyListeners: () => Array.from(destroyListeners),
662
- setTtl,
663
- getTtl,
664
- locator: self
665
- };
666
- }
667
- getSyncInstance(token, args) {
668
- const [err, realArgs] = this.resolveTokenArgs(token, args);
669
- if (err) return null;
670
- const instanceName = this.makeInstanceName(token, realArgs);
671
- const [error, holder] = this.manager.get(instanceName);
672
- if (error) return null;
673
- return holder.instance;
674
- }
675
- invalidate(service, round = 1) {
676
- this.logger?.log(`[ServiceLocator]#invalidate(): Starting Invalidating process of ${service}`);
677
- const toInvalidate = this.manager.filter((holder) => holder.name === service || holder.deps.includes(service));
678
- const promises = [];
679
- for (const [key, holder] of toInvalidate.entries()) {
680
- if (holder.status === ServiceLocatorInstanceHolderStatus.Destroying) {
681
- this.logger?.trace(`[ServiceLocator]#invalidate(): ${key} is already being destroyed`);
682
- promises.push(holder.destroyPromise);
683
- continue;
684
- }
685
- if (holder.status === ServiceLocatorInstanceHolderStatus.Creating) {
686
- this.logger?.trace(`[ServiceLocator]#invalidate(): ${key} is being created, waiting for creation to finish`);
687
- promises.push(holder.creationPromise?.then(() => {
688
- if (round > 3) {
689
- this.logger?.error(`[ServiceLocator]#invalidate(): ${key} creation is triggering a new invalidation round, but it is still not created`);
690
- return;
691
- }
692
- return this.invalidate(key, round + 1);
693
- }));
694
- continue;
695
- }
696
- holder.status = ServiceLocatorInstanceHolderStatus.Destroying;
697
- this.logger?.log(`[ServiceLocator]#invalidate(): Invalidating ${key} and notifying listeners`);
698
- holder.destroyPromise = Promise.all(holder.destroyListeners.map((listener) => listener())).then(async () => {
699
- this.manager.delete(key);
700
- await this.notifyListeners(key, "destroy");
701
- });
702
- promises.push(holder.destroyPromise);
703
- }
704
- return Promise.all(promises);
705
- }
706
- async ready() {
707
- return Promise.all(Array.from(this.manager.filter(() => true)).map(([, holder]) => {
708
- if (holder.status === ServiceLocatorInstanceHolderStatus.Creating) return holder.creationPromise?.then(() => null);
709
- if (holder.status === ServiceLocatorInstanceHolderStatus.Destroying) return holder.destroyPromise.then(() => null);
710
- return Promise.resolve(null);
711
- })).then(() => null);
712
- }
713
- makeInstanceName(token, args) {
714
- const formattedArgs = args ? ":" + JSON.stringify(args, (_, value) => {
715
- if (typeof value === "function") return `function:${value.name}(${value.length})`;
716
- if (typeof value === "symbol") return value.toString();
717
- return value;
718
- }).replaceAll(/"/g, "").replaceAll(/:/g, "=").replaceAll(/,/g, "|") : "";
719
- return `${token.toString()}${formattedArgs}`;
720
- }
586
+ constructor(registry = globalRegistry, logger = null) {
587
+ this.registry = registry;
588
+ this.logger = logger;
589
+ this.eventBus = new ServiceLocatorEventBus(logger);
590
+ this.manager = new ServiceLocatorManager(logger);
591
+ }
592
+ eventBus;
593
+ manager;
594
+ getEventBus() {
595
+ return this.eventBus;
596
+ }
597
+ storeInstance(instance, token, args) {
598
+ const instanceName = this.getInstanceIdentifier(token, args);
599
+ this.manager.set(instanceName, {
600
+ name: instanceName,
601
+ instance,
602
+ status: "created" /* Created */,
603
+ kind: "instance" /* Instance */,
604
+ createdAt: Date.now(),
605
+ ttl: Infinity,
606
+ deps: [],
607
+ destroyListeners: [],
608
+ effects: [],
609
+ destroyPromise: null,
610
+ creationPromise: null
611
+ });
612
+ this.notifyListeners(instanceName);
613
+ }
614
+ removeInstance(token, args) {
615
+ const instanceName = this.getInstanceIdentifier(token, args);
616
+ return this.invalidate(instanceName);
617
+ }
618
+ resolveTokenArgs(token, args) {
619
+ let realArgs = args;
620
+ if (token instanceof BoundInjectionToken) {
621
+ realArgs = token.value;
622
+ } else if (token instanceof FactoryInjectionToken) {
623
+ if (token.resolved) {
624
+ realArgs = token.value;
625
+ } else {
626
+ return [new FactoryTokenNotResolved(token.name)];
627
+ }
628
+ }
629
+ if (!token.schema) {
630
+ return [void 0, realArgs];
631
+ }
632
+ const validatedArgs = token.schema?.safeParse(realArgs);
633
+ if (validatedArgs && !validatedArgs.success) {
634
+ this.logger?.error(
635
+ `[ServiceLocator]#resolveTokenArgs(): Error validating args for ${token.name.toString()}`,
636
+ validatedArgs.error
637
+ );
638
+ return [new UnknownError(validatedArgs.error)];
639
+ }
640
+ return [void 0, validatedArgs?.data];
641
+ }
642
+ getInstanceIdentifier(token, args) {
643
+ const [err, realArgs] = this.resolveTokenArgs(
644
+ token,
645
+ args
646
+ );
647
+ if (err) {
648
+ throw err;
649
+ }
650
+ return this.makeInstanceName(token, realArgs);
651
+ }
652
+ async getInstance(token, args) {
653
+ const [err, realArgs] = this.resolveTokenArgs(token, args);
654
+ if (err instanceof UnknownError) {
655
+ return [err];
656
+ } else if (err instanceof FactoryTokenNotResolved && token instanceof FactoryInjectionToken) {
657
+ this.logger?.log(
658
+ `[ServiceLocator]#getInstance() Factory token not resolved, resolving it`
659
+ );
660
+ await token.resolve();
661
+ return this.getInstance(token);
662
+ }
663
+ const instanceName = this.makeInstanceName(token, realArgs);
664
+ const [error, holder] = this.manager.get(instanceName);
665
+ if (!error) {
666
+ if (holder.status === "creating" /* Creating */) {
667
+ return holder.creationPromise;
668
+ } else if (holder.status === "destroying" /* Destroying */) {
669
+ return [new UnknownError("InstanceDestroying" /* InstanceDestroying */)];
670
+ }
671
+ return [void 0, holder.instance];
672
+ }
673
+ switch (error.code) {
674
+ case "InstanceDestroying" /* InstanceDestroying */:
675
+ this.logger?.log(
676
+ `[ServiceLocator]#getInstance() TTL expired for ${holder?.name}`
677
+ );
678
+ await holder?.destroyPromise;
679
+ return this.getInstance(token, args);
680
+ case "InstanceExpired" /* InstanceExpired */:
681
+ this.logger?.log(
682
+ `[ServiceLocator]#getInstance() TTL expired for ${holder?.name}`
683
+ );
684
+ await this.invalidate(instanceName);
685
+ return this.getInstance(token, args);
686
+ case "InstanceNotFound" /* InstanceNotFound */:
687
+ break;
688
+ default:
689
+ return [error];
690
+ }
691
+ return this.createInstance(instanceName, token, realArgs);
692
+ }
693
+ async getOrThrowInstance(token, args) {
694
+ const [error, instance] = await this.getInstance(token, args);
695
+ if (error) {
696
+ throw error;
697
+ }
698
+ return instance;
699
+ }
700
+ notifyListeners(name, event = "create") {
701
+ this.logger?.log(
702
+ `[ServiceLocator]#notifyListeners() Notifying listeners for ${name} with event ${event}`
703
+ );
704
+ return this.eventBus.emit(name, event);
705
+ }
706
+ async createInstance(instanceName, token, args) {
707
+ this.logger?.log(
708
+ `[ServiceLocator]#createInstance() Creating instance for ${instanceName}`
709
+ );
710
+ let realToken = token instanceof BoundInjectionToken || token instanceof FactoryInjectionToken ? token.token : token;
711
+ if (this.registry.has(realToken)) {
712
+ return this.resolveInstance(instanceName, realToken, args);
713
+ } else {
714
+ return [new FactoryNotFound(realToken.name.toString())];
715
+ }
716
+ }
717
+ resolveInstance(instanceName, token, args) {
718
+ this.logger?.log(
719
+ `[ServiceLocator]#resolveInstance(): Creating instance for ${instanceName} from abstract factory`
720
+ );
721
+ const ctx = this.createFactoryContext(instanceName);
722
+ let { factory, scope } = this.registry.get(token);
723
+ const holder = {
724
+ name: instanceName,
725
+ instance: null,
726
+ status: "creating" /* Creating */,
727
+ kind: "abstractFactory" /* AbstractFactory */,
728
+ // @ts-expect-error TS2322 This is correct type
729
+ creationPromise: factory(ctx, args).then(async (instance) => {
730
+ holder.instance = instance;
731
+ holder.status = "created" /* Created */;
732
+ holder.deps = ctx.getDependencies();
733
+ holder.destroyListeners = ctx.getDestroyListeners();
734
+ holder.ttl = ctx.getTtl();
735
+ if (holder.deps.length > 0) {
736
+ this.logger?.log(
737
+ `[ServiceLocator]#createInstanceFromAbstractFactory(): Adding subscriptions for ${instanceName} dependencies for their invalidations: ${holder.deps.join(
738
+ ", "
739
+ )}`
740
+ );
741
+ holder.deps.forEach((dependency) => {
742
+ holder.destroyListeners.push(
743
+ this.eventBus.on(
744
+ dependency,
745
+ "destroy",
746
+ () => this.invalidate(instanceName)
747
+ )
748
+ );
749
+ });
750
+ }
751
+ if (holder.ttl === 0 || scope === "Instance" /* Instance */) {
752
+ await this.invalidate(instanceName);
753
+ }
754
+ await this.notifyListeners(instanceName);
755
+ return [void 0, instance];
756
+ }).catch((error) => {
757
+ this.logger?.error(
758
+ `[ServiceLocator]#createInstanceFromAbstractFactory(): Error creating instance for ${instanceName}`,
759
+ error
760
+ );
761
+ return [new UnknownError(error)];
762
+ }),
763
+ effects: [],
764
+ deps: [],
765
+ destroyListeners: [],
766
+ createdAt: Date.now(),
767
+ ttl: Infinity
768
+ };
769
+ if (scope === "Singleton" /* Singleton */) {
770
+ this.logger?.debug(
771
+ `[ServiceLocator]#resolveInstance(): Setting instance for ${instanceName}`
772
+ );
773
+ this.manager.set(instanceName, holder);
774
+ }
775
+ return holder.creationPromise;
776
+ }
777
+ createFactoryContext(instanceName) {
778
+ const dependencies = /* @__PURE__ */ new Set();
779
+ const destroyListeners = /* @__PURE__ */ new Set();
780
+ const self = this;
781
+ function invalidate(name = instanceName) {
782
+ return self.invalidate(name);
783
+ }
784
+ function addEffect(listener) {
785
+ destroyListeners.add(listener);
786
+ }
787
+ let ttl = Infinity;
788
+ function setTtl(value) {
789
+ ttl = value;
790
+ }
791
+ function getTtl() {
792
+ return ttl;
793
+ }
794
+ function on(key, event, listener) {
795
+ destroyListeners.add(self.eventBus.on(key, event, listener));
796
+ }
797
+ return {
798
+ // @ts-expect-error This is correct type
799
+ async inject(token, args) {
800
+ let injectionToken = token;
801
+ if (typeof token === "function") {
802
+ injectionToken = getInjectableToken(token);
803
+ }
804
+ if (injectionToken) {
805
+ const [err, validatedArgs] = self.resolveTokenArgs(
806
+ injectionToken,
807
+ args
808
+ );
809
+ if (err) {
810
+ throw err;
811
+ }
812
+ const instanceName2 = self.makeInstanceName(token, validatedArgs);
813
+ dependencies.add(instanceName2);
814
+ return self.getOrThrowInstance(injectionToken, args);
815
+ }
816
+ throw new Error(
817
+ `[ServiceLocator]#inject(): Invalid token type: ${typeof token}. Expected a class or an InjectionToken.`
818
+ );
819
+ },
820
+ invalidate,
821
+ on,
822
+ getDependencies: () => Array.from(dependencies),
823
+ addEffect,
824
+ getDestroyListeners: () => Array.from(destroyListeners),
825
+ setTtl,
826
+ getTtl,
827
+ locator: self
828
+ };
829
+ }
830
+ getSyncInstance(token, args) {
831
+ const [err, realArgs] = this.resolveTokenArgs(token, args);
832
+ if (err) {
833
+ return null;
834
+ }
835
+ const instanceName = this.makeInstanceName(token, realArgs);
836
+ const [error, holder] = this.manager.get(instanceName);
837
+ if (error) {
838
+ return null;
839
+ }
840
+ return holder.instance;
841
+ }
842
+ invalidate(service, round = 1) {
843
+ this.logger?.log(
844
+ `[ServiceLocator]#invalidate(): Starting Invalidating process of ${service}`
845
+ );
846
+ const toInvalidate = this.manager.filter(
847
+ (holder) => holder.name === service || holder.deps.includes(service)
848
+ );
849
+ const promises = [];
850
+ for (const [key, holder] of toInvalidate.entries()) {
851
+ if (holder.status === "destroying" /* Destroying */) {
852
+ this.logger?.trace(
853
+ `[ServiceLocator]#invalidate(): ${key} is already being destroyed`
854
+ );
855
+ promises.push(holder.destroyPromise);
856
+ continue;
857
+ }
858
+ if (holder.status === "creating" /* Creating */) {
859
+ this.logger?.trace(
860
+ `[ServiceLocator]#invalidate(): ${key} is being created, waiting for creation to finish`
861
+ );
862
+ promises.push(
863
+ holder.creationPromise?.then(() => {
864
+ if (round > 3) {
865
+ this.logger?.error(
866
+ `[ServiceLocator]#invalidate(): ${key} creation is triggering a new invalidation round, but it is still not created`
867
+ );
868
+ return;
869
+ }
870
+ return this.invalidate(key, round + 1);
871
+ })
872
+ );
873
+ continue;
874
+ }
875
+ holder.status = "destroying" /* Destroying */;
876
+ this.logger?.log(
877
+ `[ServiceLocator]#invalidate(): Invalidating ${key} and notifying listeners`
878
+ );
879
+ holder.destroyPromise = Promise.all(
880
+ holder.destroyListeners.map((listener) => listener())
881
+ ).then(async () => {
882
+ this.manager.delete(key);
883
+ await this.notifyListeners(key, "destroy");
884
+ });
885
+ promises.push(holder.destroyPromise);
886
+ }
887
+ return Promise.all(promises);
888
+ }
889
+ async ready() {
890
+ return Promise.all(
891
+ Array.from(this.manager.filter(() => true)).map(([, holder]) => {
892
+ if (holder.status === "creating" /* Creating */) {
893
+ return holder.creationPromise?.then(() => null);
894
+ }
895
+ if (holder.status === "destroying" /* Destroying */) {
896
+ return holder.destroyPromise.then(() => null);
897
+ }
898
+ return Promise.resolve(null);
899
+ })
900
+ ).then(() => null);
901
+ }
902
+ makeInstanceName(token, args) {
903
+ const formattedArgs = args ? ":" + Object.entries(args ?? {}).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)).map(([key, value]) => {
904
+ if (typeof value === "function") {
905
+ return `${key}=fn_${value.name}(${value.length})`;
906
+ }
907
+ if (typeof value === "symbol") {
908
+ return `${key}=${value.toString()}`;
909
+ }
910
+ return `${key}=${JSON.stringify(value).slice(0, 40)}`;
911
+ }).join(",").replaceAll(/"/g, "").replaceAll(/:/g, "=") : "";
912
+ return `${token.toString()}${formattedArgs}`;
913
+ }
721
914
  };
722
915
 
723
- //#endregion
724
- //#region src/injector.mts
725
- const globalServiceLocator = new ServiceLocator();
916
+ // src/injector.mts
917
+ var globalServiceLocator = new ServiceLocator(globalRegistry);
726
918
  function getGlobalServiceLocator() {
727
- if (!globalServiceLocator) throw new Error("[ServiceLocator] Service locator is not initialized. Please provide the service locator before using the @Injectable decorator.");
728
- return globalServiceLocator;
919
+ if (!globalServiceLocator) {
920
+ throw new Error(
921
+ "[ServiceLocator] Service locator is not initialized. Please provide the service locator before using the @Injectable decorator."
922
+ );
923
+ }
924
+ return globalServiceLocator;
729
925
  }
730
- const values = getInjectors({ baseLocator: globalServiceLocator });
731
- const inject = values.inject;
732
- const syncInject = values.syncInject;
733
- const wrapSyncInit = values.wrapSyncInit;
734
- const provideServiceLocator = values.provideServiceLocator;
735
-
736
- //#endregion
737
- export { BoundInjectionToken, ErrorsEnum, EventEmitter, FactoryInjectionToken, FactoryNotFound, FactoryTokenNotResolved, Injectable, InjectableScope, InjectableTokenMeta, InjectableType, InjectionToken, InjectorsBase, InstanceDestroying, InstanceExpired, InstanceNotFound, Optional, ProxyServiceLocator, Registry, Required, ServiceLocator, ServiceLocatorEventBus, ServiceLocatorInstanceHolderKind, ServiceLocatorInstanceHolderStatus, ServiceLocatorManager, UnknownError, getGlobalServiceLocator, getInjectableToken, getInjectors, globalRegistry, inject, makeProxyServiceLocator, provideServiceLocator, resolveService, syncInject, wrapSyncInit };
926
+ var values = getInjectors({
927
+ baseLocator: globalServiceLocator
928
+ });
929
+ var inject = values.inject;
930
+ var syncInject = values.syncInject;
931
+ var wrapSyncInit = values.wrapSyncInit;
932
+ var provideServiceLocator = values.provideServiceLocator;
933
+ export {
934
+ BoundInjectionToken,
935
+ ErrorsEnum,
936
+ EventEmitter,
937
+ FactoryInjectionToken,
938
+ FactoryNotFound,
939
+ FactoryTokenNotResolved,
940
+ Injectable,
941
+ InjectableScope,
942
+ InjectableTokenMeta,
943
+ InjectableType,
944
+ InjectionToken,
945
+ InjectorsBase,
946
+ InstanceDestroying,
947
+ InstanceExpired,
948
+ InstanceNotFound,
949
+ ProxyServiceLocator,
950
+ Registry,
951
+ ServiceLocator,
952
+ ServiceLocatorEventBus,
953
+ ServiceLocatorInstanceHolderKind,
954
+ ServiceLocatorInstanceHolderStatus,
955
+ ServiceLocatorManager,
956
+ UnknownError,
957
+ getGlobalServiceLocator,
958
+ getInjectableToken,
959
+ getInjectors,
960
+ globalRegistry,
961
+ inject,
962
+ makeProxyServiceLocator,
963
+ provideServiceLocator,
964
+ resolveService,
965
+ syncInject,
966
+ wrapSyncInit
967
+ };
738
968
  //# sourceMappingURL=index.mjs.map