sandly 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -17
- package/dist/index.d.ts +394 -226
- package/dist/index.js +319 -136
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,50 +1,27 @@
|
|
|
1
1
|
//#region src/tag.d.ts
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Type representing a tag identifier (string or symbol).
|
|
4
|
+
* @internal
|
|
5
5
|
*/
|
|
6
|
-
|
|
6
|
+
type TagId = string | symbol;
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
* This
|
|
10
|
-
* the original tag information for dependency inference.
|
|
8
|
+
* Symbol used to identify tagged types within the dependency injection system.
|
|
9
|
+
* This symbol is used as a property key to attach metadata to both value tags and service tags.
|
|
11
10
|
*
|
|
12
|
-
*
|
|
11
|
+
* Note: We can't use a symbol here becuase it produced the following TS error:
|
|
12
|
+
* error TS4020: 'extends' clause of exported class 'NotificationService' has or is using private name 'TagIdKey'.
|
|
13
13
|
*
|
|
14
|
-
* @template T - A ValueTag type
|
|
15
|
-
* @returns The value type with optional phantom tag metadata for dependency inference
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```typescript
|
|
19
|
-
* const ApiKeyTag = Tag.of('apiKey')<string>();
|
|
20
|
-
*
|
|
21
|
-
* class UserService extends Tag.Class('UserService') {
|
|
22
|
-
* constructor(
|
|
23
|
-
* private db: DatabaseService, // ClassTag - works automatically
|
|
24
|
-
* private apiKey: Inject<typeof ApiKeyTag> // ValueTag - type is string, tag preserved
|
|
25
|
-
* ) {
|
|
26
|
-
* super();
|
|
27
|
-
* }
|
|
28
|
-
* }
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
type Inject<T extends ValueTag<unknown, string | symbol>> = T extends ValueTag<infer V, string | symbol> ? V & {
|
|
32
|
-
readonly [InjectSource]?: T;
|
|
33
|
-
} : never;
|
|
34
|
-
/**
|
|
35
|
-
* Helper type to extract the original ValueTag from an Inject<T> type.
|
|
36
|
-
* Since InjectSource is optional, we need to check for both presence and absence.
|
|
37
14
|
* @internal
|
|
38
15
|
*/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
} ? U : never;
|
|
16
|
+
declare const ValueTagIdKey = "sandly/ValueTagIdKey";
|
|
17
|
+
declare const ServiceTagIdKey = "sandly/ServiceTagIdKey";
|
|
42
18
|
/**
|
|
43
|
-
* Internal
|
|
44
|
-
* This
|
|
19
|
+
* Internal string used to identify the type of a tagged type within the dependency injection system.
|
|
20
|
+
* This string is used as a property key to attach metadata to both value tags and service tags.
|
|
21
|
+
* It is used to carry the type of the tagged type and should not be used directly.
|
|
45
22
|
* @internal
|
|
46
23
|
*/
|
|
47
|
-
declare const
|
|
24
|
+
declare const TagTypeKey = "sandly/TagTypeKey";
|
|
48
25
|
/**
|
|
49
26
|
* Type representing a value-based dependency tag.
|
|
50
27
|
*
|
|
@@ -58,31 +35,30 @@ declare const TagId: "__tag_id__";
|
|
|
58
35
|
* @example
|
|
59
36
|
* ```typescript
|
|
60
37
|
* // Creates a value tag for string configuration
|
|
61
|
-
* const ApiKeyTag: ValueTag<
|
|
38
|
+
* const ApiKeyTag: ValueTag<'apiKey', string> = Tag.of('apiKey')<string>();
|
|
62
39
|
*
|
|
63
40
|
* // Register in container
|
|
64
41
|
* container.register(ApiKeyTag, () => 'my-secret-key');
|
|
65
42
|
* ```
|
|
66
43
|
*/
|
|
67
|
-
interface ValueTag<
|
|
68
|
-
readonly [
|
|
69
|
-
|
|
70
|
-
readonly __type: T;
|
|
44
|
+
interface ValueTag<Id extends TagId, T> {
|
|
45
|
+
readonly [ValueTagIdKey]: Id;
|
|
46
|
+
readonly [TagTypeKey]: T;
|
|
71
47
|
}
|
|
72
48
|
/**
|
|
73
49
|
* Type representing a class-based dependency tag.
|
|
74
50
|
*
|
|
75
|
-
* Tagged classes are created by Tag.
|
|
51
|
+
* Tagged classes are created by Tag.Service() and serve as both the dependency identifier
|
|
76
52
|
* and the constructor for the service. They extend regular classes with tag metadata
|
|
77
53
|
* that the DI system uses for identification and type safety.
|
|
78
54
|
*
|
|
79
|
-
* @template T - The type of instances created by this tagged class
|
|
80
55
|
* @template Id - The unique identifier for this tag (string or symbol)
|
|
56
|
+
* @template T - The type of instances created by this tagged class
|
|
81
57
|
*
|
|
82
58
|
* @example
|
|
83
59
|
* ```typescript
|
|
84
60
|
* // Creates a tagged class
|
|
85
|
-
* class UserService extends Tag.
|
|
61
|
+
* class UserService extends Tag.Service('UserService') {
|
|
86
62
|
* getUsers() { return []; }
|
|
87
63
|
* }
|
|
88
64
|
*
|
|
@@ -90,13 +66,13 @@ interface ValueTag<T, Id extends string | symbol> {
|
|
|
90
66
|
* container.register(UserService, () => new UserService());
|
|
91
67
|
* ```
|
|
92
68
|
*
|
|
93
|
-
* @internal - Users should use Tag.
|
|
69
|
+
* @internal - Users should use Tag.Service() instead of working with this type directly
|
|
94
70
|
*/
|
|
95
|
-
interface
|
|
71
|
+
interface ServiceTag<Id extends TagId, T> {
|
|
96
72
|
new (...args: any[]): T & {
|
|
97
|
-
readonly [
|
|
73
|
+
readonly [ServiceTagIdKey]?: Id;
|
|
98
74
|
};
|
|
99
|
-
readonly [
|
|
75
|
+
readonly [ServiceTagIdKey]?: Id;
|
|
100
76
|
}
|
|
101
77
|
/**
|
|
102
78
|
* Utility type that extracts the service type from any dependency tag.
|
|
@@ -105,7 +81,7 @@ interface ClassTag<T, Id extends string | symbol> {
|
|
|
105
81
|
* the container and layers to automatically determine what type of service
|
|
106
82
|
* a given tag represents without manual type annotations.
|
|
107
83
|
*
|
|
108
|
-
* @template T - Any dependency tag (ValueTag or
|
|
84
|
+
* @template T - Any dependency tag (ValueTag or ServiceTag)
|
|
109
85
|
* @returns The service type that the tag represents
|
|
110
86
|
*
|
|
111
87
|
* @example With value tags
|
|
@@ -117,9 +93,9 @@ interface ClassTag<T, Id extends string | symbol> {
|
|
|
117
93
|
* type ConfigService = TagType<typeof ConfigTag>; // { apiKey: string }
|
|
118
94
|
* ```
|
|
119
95
|
*
|
|
120
|
-
* @example With
|
|
96
|
+
* @example With service tags
|
|
121
97
|
* ```typescript
|
|
122
|
-
* class UserService extends Tag.
|
|
98
|
+
* class UserService extends Tag.Service('UserService') {
|
|
123
99
|
* getUsers() { return []; }
|
|
124
100
|
* }
|
|
125
101
|
*
|
|
@@ -132,11 +108,11 @@ interface ClassTag<T, Id extends string | symbol> {
|
|
|
132
108
|
* container.register(StringTag, () => 'hello'); // Factory must return string
|
|
133
109
|
* container.register(UserService, () => new UserService()); // Factory must return UserService
|
|
134
110
|
*
|
|
135
|
-
* const str: string = await container.
|
|
136
|
-
* const user: UserService = await container.
|
|
111
|
+
* const str: string = await container.resolve(StringTag); // Automatically typed as string
|
|
112
|
+
* const user: UserService = await container.resolve(UserService); // Automatically typed as UserService
|
|
137
113
|
* ```
|
|
138
114
|
*/
|
|
139
|
-
type TagType<
|
|
115
|
+
type TagType<TTag extends AnyTag> = TTag extends ValueTag<any, infer T> ? T : TTag extends ServiceTag<any, infer T> ? T : never;
|
|
140
116
|
/**
|
|
141
117
|
* Union type representing any valid dependency tag in the system.
|
|
142
118
|
*
|
|
@@ -152,15 +128,15 @@ type TagType<T> = T extends ValueTag<infer V, string | symbol> ? V : T extends C
|
|
|
152
128
|
*
|
|
153
129
|
* @example Class tag
|
|
154
130
|
* ```typescript
|
|
155
|
-
* class DatabaseService extends Tag.
|
|
131
|
+
* class DatabaseService extends Tag.Service('DatabaseService') {}
|
|
156
132
|
* // DatabaseService satisfies AnyTag
|
|
157
133
|
* ```
|
|
158
134
|
*/
|
|
159
|
-
type AnyTag = ValueTag<
|
|
135
|
+
type AnyTag = ValueTag<TagId, any> | ServiceTag<TagId, any>;
|
|
160
136
|
/**
|
|
161
137
|
* Utility object containing factory functions for creating dependency tags.
|
|
162
138
|
*
|
|
163
|
-
* The Tag object provides the primary API for creating both value tags and
|
|
139
|
+
* The Tag object provides the primary API for creating both value tags and service tags
|
|
164
140
|
* used throughout the dependency injection system. It's the main entry point for
|
|
165
141
|
* defining dependencies in a type-safe way.
|
|
166
142
|
*/
|
|
@@ -220,7 +196,7 @@ declare const Tag: {
|
|
|
220
196
|
* }));
|
|
221
197
|
* ```
|
|
222
198
|
*/
|
|
223
|
-
of: <Id extends
|
|
199
|
+
of: <Id extends TagId>(id: Id) => <T>() => ValueTag<Id, T>;
|
|
224
200
|
/**
|
|
225
201
|
* Creates an anonymous value tag with a unique symbol identifier.
|
|
226
202
|
*
|
|
@@ -254,7 +230,7 @@ declare const Tag: {
|
|
|
254
230
|
* console.log(ConfigA === ConfigB); // false
|
|
255
231
|
* ```
|
|
256
232
|
*/
|
|
257
|
-
for: <T>() => ValueTag<
|
|
233
|
+
for: <T>() => ValueTag<symbol, T>;
|
|
258
234
|
/**
|
|
259
235
|
* Creates a base class that can be extended to create service classes with dependency tags.
|
|
260
236
|
*
|
|
@@ -268,7 +244,7 @@ declare const Tag: {
|
|
|
268
244
|
*
|
|
269
245
|
* @example Basic service class
|
|
270
246
|
* ```typescript
|
|
271
|
-
* class UserService extends Tag.
|
|
247
|
+
* class UserService extends Tag.Service('UserService') {
|
|
272
248
|
* getUsers() {
|
|
273
249
|
* return ['alice', 'bob'];
|
|
274
250
|
* }
|
|
@@ -279,11 +255,11 @@ declare const Tag: {
|
|
|
279
255
|
*
|
|
280
256
|
* @example Service with dependencies
|
|
281
257
|
* ```typescript
|
|
282
|
-
* class DatabaseService extends Tag.
|
|
258
|
+
* class DatabaseService extends Tag.Service('DatabaseService') {
|
|
283
259
|
* query(sql: string) { return []; }
|
|
284
260
|
* }
|
|
285
261
|
*
|
|
286
|
-
* class UserRepository extends Tag.
|
|
262
|
+
* class UserRepository extends Tag.Service('UserRepository') {
|
|
287
263
|
* constructor(private db: DatabaseService) {
|
|
288
264
|
* super();
|
|
289
265
|
* }
|
|
@@ -296,22 +272,22 @@ declare const Tag: {
|
|
|
296
272
|
* container
|
|
297
273
|
* .register(DatabaseService, () => new DatabaseService())
|
|
298
274
|
* .register(UserRepository, async (ctx) =>
|
|
299
|
-
* new UserRepository(await ctx.
|
|
275
|
+
* new UserRepository(await ctx.resolve(DatabaseService))
|
|
300
276
|
* );
|
|
301
277
|
* ```
|
|
302
278
|
*
|
|
303
279
|
* @example With symbol identifiers
|
|
304
280
|
* ```typescript
|
|
305
|
-
* const
|
|
281
|
+
* const ServiceId = Symbol('InternalService');
|
|
306
282
|
*
|
|
307
|
-
* class InternalService extends Tag.
|
|
283
|
+
* class InternalService extends Tag.Service(ServiceId) {
|
|
308
284
|
* doInternalWork() { return 'work'; }
|
|
309
285
|
* }
|
|
310
286
|
* ```
|
|
311
287
|
*/
|
|
312
|
-
|
|
313
|
-
readonly
|
|
314
|
-
}
|
|
288
|
+
Service: <Id extends TagId>(id: Id) => ServiceTag<Id, {
|
|
289
|
+
readonly "sandly/ServiceTagIdKey"?: Id;
|
|
290
|
+
}>;
|
|
315
291
|
/**
|
|
316
292
|
* Extracts the string representation of a tag's identifier.
|
|
317
293
|
*
|
|
@@ -319,14 +295,14 @@ declare const Tag: {
|
|
|
319
295
|
* whether it's a string-based or symbol-based tag. Primarily used internally
|
|
320
296
|
* for error messages and debugging.
|
|
321
297
|
*
|
|
322
|
-
* @param tag - Any valid dependency tag (value tag or
|
|
298
|
+
* @param tag - Any valid dependency tag (value tag or service tag)
|
|
323
299
|
* @returns String representation of the tag's identifier
|
|
324
300
|
*
|
|
325
301
|
* @example
|
|
326
302
|
* ```typescript
|
|
327
303
|
* const StringTag = Tag.of('myString')<string>();
|
|
328
304
|
* const SymbolTag = Tag.for<number>();
|
|
329
|
-
* class ServiceClass extends Tag.
|
|
305
|
+
* class ServiceClass extends Tag.Service('MyService') {}
|
|
330
306
|
*
|
|
331
307
|
* console.log(Tag.id(StringTag)); // "myString"
|
|
332
308
|
* console.log(Tag.id(SymbolTag)); // "Symbol()"
|
|
@@ -335,11 +311,54 @@ declare const Tag: {
|
|
|
335
311
|
*
|
|
336
312
|
* @internal - Primarily for internal use in error messages and debugging
|
|
337
313
|
*/
|
|
338
|
-
id: (tag: AnyTag) =>
|
|
314
|
+
id: (tag: AnyTag) => TagId | undefined;
|
|
315
|
+
isTag: (tag: unknown) => tag is AnyTag;
|
|
339
316
|
};
|
|
317
|
+
/**
|
|
318
|
+
* String used to store the original ValueTag in Inject<T> types.
|
|
319
|
+
* This prevents property name collisions while allowing type-level extraction.
|
|
320
|
+
*/
|
|
321
|
+
declare const InjectSource = "sandly/InjectSource";
|
|
322
|
+
/**
|
|
323
|
+
* Helper type for injecting ValueTag dependencies in constructor parameters.
|
|
324
|
+
* This allows clean specification of ValueTag dependencies while preserving
|
|
325
|
+
* the original tag information for dependency inference.
|
|
326
|
+
*
|
|
327
|
+
* The phantom property is optional to allow normal runtime values to be assignable.
|
|
328
|
+
*
|
|
329
|
+
* @template T - A ValueTag type
|
|
330
|
+
* @returns The value type with optional phantom tag metadata for dependency inference
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```typescript
|
|
334
|
+
* const ApiKeyTag = Tag.of('apiKey')<string>();
|
|
335
|
+
*
|
|
336
|
+
* class UserService extends Tag.Service('UserService') {
|
|
337
|
+
* constructor(
|
|
338
|
+
* private db: DatabaseService, // ServiceTag - works automatically
|
|
339
|
+
* private apiKey: Inject<typeof ApiKeyTag> // ValueTag - type is string, tag preserved
|
|
340
|
+
* ) {
|
|
341
|
+
* super();
|
|
342
|
+
* }
|
|
343
|
+
* }
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
346
|
+
type Inject<T extends ValueTag<TagId, unknown>> = T extends ValueTag<any, infer V> ? V & {
|
|
347
|
+
readonly [InjectSource]?: T;
|
|
348
|
+
} : never;
|
|
349
|
+
/**
|
|
350
|
+
* Helper type to extract the original ValueTag from an Inject<T> type.
|
|
351
|
+
* Since InjectSource is optional, we need to check for both presence and absence.
|
|
352
|
+
* @internal
|
|
353
|
+
*/
|
|
354
|
+
type ExtractInjectTag<T> = T extends {
|
|
355
|
+
readonly [InjectSource]?: infer U;
|
|
356
|
+
} ? U : never;
|
|
340
357
|
//#endregion
|
|
341
358
|
//#region src/types.d.ts
|
|
342
359
|
type PromiseOrValue<T> = T | Promise<T>;
|
|
360
|
+
type Contravariant<A> = (_: A) => void;
|
|
361
|
+
type Covariant<A> = (_: never) => A;
|
|
343
362
|
//#endregion
|
|
344
363
|
//#region src/container.d.ts
|
|
345
364
|
/**
|
|
@@ -366,8 +385,8 @@ type PromiseOrValue<T> = T | Promise<T>;
|
|
|
366
385
|
* ```typescript
|
|
367
386
|
* const factory: Factory<UserService, typeof ConfigTag | typeof DatabaseService> = async (ctx) => {
|
|
368
387
|
* const [config, db] = await Promise.all([
|
|
369
|
-
* ctx.
|
|
370
|
-
* ctx.
|
|
388
|
+
* ctx.resolve(ConfigTag),
|
|
389
|
+
* ctx.resolve(DatabaseService)
|
|
371
390
|
* ]);
|
|
372
391
|
* return new UserService(config, db);
|
|
373
392
|
* };
|
|
@@ -427,7 +446,7 @@ type Finalizer<T> = (instance: T) => PromiseOrValue<void>;
|
|
|
427
446
|
*
|
|
428
447
|
* @example Using DependencyLifecycle for registration
|
|
429
448
|
* ```typescript
|
|
430
|
-
* class DatabaseConnection extends Tag.
|
|
449
|
+
* class DatabaseConnection extends Tag.Service('DatabaseConnection') {
|
|
431
450
|
* async connect() { return; }
|
|
432
451
|
* async disconnect() { return; }
|
|
433
452
|
* }
|
|
@@ -443,7 +462,7 @@ type Finalizer<T> = (instance: T) => PromiseOrValue<void>;
|
|
|
443
462
|
* }
|
|
444
463
|
* };
|
|
445
464
|
*
|
|
446
|
-
*
|
|
465
|
+
* Container.empty().register(DatabaseConnection, lifecycle);
|
|
447
466
|
* ```
|
|
448
467
|
*/
|
|
449
468
|
type DependencyLifecycle<T, TReg extends AnyTag> = {
|
|
@@ -465,7 +484,7 @@ type DependencyLifecycle<T, TReg extends AnyTag> = {
|
|
|
465
484
|
* const spec: DependencySpec<typeof UserService, never> =
|
|
466
485
|
* () => new UserService();
|
|
467
486
|
*
|
|
468
|
-
*
|
|
487
|
+
* Container.empty().register(UserService, spec);
|
|
469
488
|
* ```
|
|
470
489
|
*
|
|
471
490
|
* @example Lifecycle registration
|
|
@@ -475,29 +494,34 @@ type DependencyLifecycle<T, TReg extends AnyTag> = {
|
|
|
475
494
|
* finalizer: (conn) => conn.close()
|
|
476
495
|
* };
|
|
477
496
|
*
|
|
478
|
-
*
|
|
497
|
+
* Container.empty().register(DatabaseConnection, spec);
|
|
479
498
|
* ```
|
|
480
499
|
*/
|
|
481
500
|
type DependencySpec<T extends AnyTag, TReg extends AnyTag> = Factory<TagType<T>, TReg> | DependencyLifecycle<TagType<T>, TReg>;
|
|
482
501
|
/**
|
|
483
502
|
* Type representing the context available to factory functions during dependency resolution.
|
|
484
503
|
*
|
|
485
|
-
* This type contains only the `
|
|
504
|
+
* This type contains only the `resolve` and `resolveAll` methods from the container, which are used to retrieve
|
|
486
505
|
* other dependencies during the creation of a service.
|
|
487
506
|
*
|
|
488
507
|
* @template TReg - Union type of all dependencies available in the container
|
|
489
508
|
*/
|
|
490
|
-
type ResolutionContext<TReg extends AnyTag> = Pick<IContainer<TReg>, '
|
|
509
|
+
type ResolutionContext<TReg extends AnyTag> = Pick<IContainer<TReg>, 'resolve' | 'resolveAll'>;
|
|
510
|
+
declare const ContainerTypeId: unique symbol;
|
|
491
511
|
/**
|
|
492
512
|
* Interface representing a container that can register and retrieve dependencies.
|
|
493
513
|
*
|
|
494
514
|
* @template TReg - Union type of all dependencies available in the container
|
|
495
515
|
*/
|
|
496
|
-
interface IContainer<
|
|
497
|
-
|
|
516
|
+
interface IContainer<TReg extends AnyTag = never> {
|
|
517
|
+
readonly [ContainerTypeId]: {
|
|
518
|
+
readonly _TReg: Contravariant<TReg>;
|
|
519
|
+
};
|
|
520
|
+
register: <T extends AnyTag>(tag: T, spec: DependencySpec<T, TReg>) => IContainer<TReg | T>;
|
|
498
521
|
has(tag: AnyTag): boolean;
|
|
499
522
|
exists(tag: AnyTag): boolean;
|
|
500
|
-
|
|
523
|
+
resolve: <T extends TReg>(tag: T) => Promise<TagType<T>>;
|
|
524
|
+
resolveAll: <const T extends readonly TReg[]>(...tags: T) => Promise<{ [K in keyof T]: TagType<T[K]> }>;
|
|
501
525
|
merge<TTarget extends AnyTag>(other: IContainer<TTarget>): IContainer<TReg | TTarget>;
|
|
502
526
|
destroy(): Promise<void>;
|
|
503
527
|
}
|
|
@@ -512,26 +536,26 @@ interface IContainer<in TReg extends AnyTag> {
|
|
|
512
536
|
*
|
|
513
537
|
* @template TReg - Union type of all registered dependency tags in this container
|
|
514
538
|
*
|
|
515
|
-
* @example Basic usage with
|
|
539
|
+
* @example Basic usage with service tags
|
|
516
540
|
* ```typescript
|
|
517
541
|
* import { container, Tag } from 'sandly';
|
|
518
542
|
*
|
|
519
|
-
* class DatabaseService extends Tag.
|
|
543
|
+
* class DatabaseService extends Tag.Service('DatabaseService') {
|
|
520
544
|
* query() { return 'data'; }
|
|
521
545
|
* }
|
|
522
546
|
*
|
|
523
|
-
* class UserService extends Tag.
|
|
547
|
+
* class UserService extends Tag.Service('UserService') {
|
|
524
548
|
* constructor(private db: DatabaseService) {}
|
|
525
549
|
* getUser() { return this.db.query(); }
|
|
526
550
|
* }
|
|
527
551
|
*
|
|
528
|
-
* const
|
|
552
|
+
* const container = Container.empty()
|
|
529
553
|
* .register(DatabaseService, () => new DatabaseService())
|
|
530
554
|
* .register(UserService, async (ctx) =>
|
|
531
|
-
* new UserService(await ctx.
|
|
555
|
+
* new UserService(await ctx.resolve(DatabaseService))
|
|
532
556
|
* );
|
|
533
557
|
*
|
|
534
|
-
* const userService = await c.
|
|
558
|
+
* const userService = await c.resolve(UserService);
|
|
535
559
|
* ```
|
|
536
560
|
*
|
|
537
561
|
* @example Usage with value tags
|
|
@@ -539,22 +563,22 @@ interface IContainer<in TReg extends AnyTag> {
|
|
|
539
563
|
* const ApiKeyTag = Tag.of('apiKey')<string>();
|
|
540
564
|
* const ConfigTag = Tag.of('config')<{ dbUrl: string }>();
|
|
541
565
|
*
|
|
542
|
-
* const
|
|
566
|
+
* const container = Container.empty()
|
|
543
567
|
* .register(ApiKeyTag, () => process.env.API_KEY!)
|
|
544
568
|
* .register(ConfigTag, () => ({ dbUrl: 'postgresql://localhost:5432' }));
|
|
545
569
|
*
|
|
546
|
-
* const apiKey = await c.
|
|
547
|
-
* const config = await c.
|
|
570
|
+
* const apiKey = await c.resolve(ApiKeyTag);
|
|
571
|
+
* const config = await c.resolve(ConfigTag);
|
|
548
572
|
* ```
|
|
549
573
|
*
|
|
550
574
|
* @example With finalizers for cleanup
|
|
551
575
|
* ```typescript
|
|
552
|
-
* class DatabaseConnection extends Tag.
|
|
576
|
+
* class DatabaseConnection extends Tag.Service('DatabaseConnection') {
|
|
553
577
|
* async connect() { return; }
|
|
554
578
|
* async disconnect() { return; }
|
|
555
579
|
* }
|
|
556
580
|
*
|
|
557
|
-
* const
|
|
581
|
+
* const container = Container.empty().register(
|
|
558
582
|
* DatabaseConnection,
|
|
559
583
|
* async () => {
|
|
560
584
|
* const conn = new DatabaseConnection();
|
|
@@ -568,7 +592,11 @@ interface IContainer<in TReg extends AnyTag> {
|
|
|
568
592
|
* await c.destroy(); // Calls all finalizers
|
|
569
593
|
* ```
|
|
570
594
|
*/
|
|
571
|
-
declare class Container<
|
|
595
|
+
declare class Container<TReg extends AnyTag> implements IContainer<TReg> {
|
|
596
|
+
readonly [ContainerTypeId]: {
|
|
597
|
+
readonly _TReg: Contravariant<TReg>;
|
|
598
|
+
};
|
|
599
|
+
protected constructor();
|
|
572
600
|
/**
|
|
573
601
|
* Cache of instantiated dependencies as promises.
|
|
574
602
|
* Ensures singleton behavior and supports concurrent access.
|
|
@@ -590,12 +618,17 @@ declare class Container<in TReg extends AnyTag = never> implements IContainer<TR
|
|
|
590
618
|
* @internal
|
|
591
619
|
*/
|
|
592
620
|
protected isDestroyed: boolean;
|
|
621
|
+
/**
|
|
622
|
+
* Creates a new empty container instance.
|
|
623
|
+
* @returns A new empty Container instance with no registered dependencies.
|
|
624
|
+
*/
|
|
625
|
+
static empty(): Container<never>;
|
|
593
626
|
/**
|
|
594
627
|
* Registers a dependency in the container with a factory function and optional finalizer.
|
|
595
628
|
*
|
|
596
629
|
* The factory function receives the current container instance and must return the
|
|
597
630
|
* service instance (or a Promise of it). The container tracks the registration at
|
|
598
|
-
* the type level, ensuring type safety for subsequent `.
|
|
631
|
+
* the type level, ensuring type safety for subsequent `.resolve()` calls.
|
|
599
632
|
*
|
|
600
633
|
* If a dependency is already registered, this method will override it unless the
|
|
601
634
|
* dependency has already been instantiated, in which case it will throw an error.
|
|
@@ -610,11 +643,11 @@ declare class Container<in TReg extends AnyTag = never> implements IContainer<TR
|
|
|
610
643
|
*
|
|
611
644
|
* @example Registering a simple service
|
|
612
645
|
* ```typescript
|
|
613
|
-
* class LoggerService extends Tag.
|
|
646
|
+
* class LoggerService extends Tag.Service('LoggerService') {
|
|
614
647
|
* log(message: string) { console.log(message); }
|
|
615
648
|
* }
|
|
616
649
|
*
|
|
617
|
-
* const
|
|
650
|
+
* const container = Container.empty().register(
|
|
618
651
|
* LoggerService,
|
|
619
652
|
* () => new LoggerService()
|
|
620
653
|
* );
|
|
@@ -622,24 +655,24 @@ declare class Container<in TReg extends AnyTag = never> implements IContainer<TR
|
|
|
622
655
|
*
|
|
623
656
|
* @example Registering with dependencies
|
|
624
657
|
* ```typescript
|
|
625
|
-
* class UserService extends Tag.
|
|
658
|
+
* class UserService extends Tag.Service('UserService') {
|
|
626
659
|
* constructor(private db: DatabaseService, private logger: LoggerService) {}
|
|
627
660
|
* }
|
|
628
661
|
*
|
|
629
|
-
* const
|
|
662
|
+
* const container = Container.empty()
|
|
630
663
|
* .register(DatabaseService, () => new DatabaseService())
|
|
631
664
|
* .register(LoggerService, () => new LoggerService())
|
|
632
665
|
* .register(UserService, async (ctx) =>
|
|
633
666
|
* new UserService(
|
|
634
|
-
* await ctx.
|
|
635
|
-
* await ctx.
|
|
667
|
+
* await ctx.resolve(DatabaseService),
|
|
668
|
+
* await ctx.resolve(LoggerService)
|
|
636
669
|
* )
|
|
637
670
|
* );
|
|
638
671
|
* ```
|
|
639
672
|
*
|
|
640
673
|
* @example Overriding a dependency
|
|
641
674
|
* ```typescript
|
|
642
|
-
* const
|
|
675
|
+
* const container = Container.empty()
|
|
643
676
|
* .register(DatabaseService, () => new DatabaseService())
|
|
644
677
|
* .register(DatabaseService, () => new MockDatabaseService()); // Overrides the previous registration
|
|
645
678
|
* ```
|
|
@@ -648,7 +681,7 @@ declare class Container<in TReg extends AnyTag = never> implements IContainer<TR
|
|
|
648
681
|
* ```typescript
|
|
649
682
|
* const ConfigTag = Tag.of('config')<{ apiUrl: string }>();
|
|
650
683
|
*
|
|
651
|
-
* const
|
|
684
|
+
* const container = Container.empty().register(
|
|
652
685
|
* ConfigTag,
|
|
653
686
|
* () => ({ apiUrl: 'https://api.example.com' })
|
|
654
687
|
* );
|
|
@@ -656,12 +689,12 @@ declare class Container<in TReg extends AnyTag = never> implements IContainer<TR
|
|
|
656
689
|
*
|
|
657
690
|
* @example With finalizer for cleanup
|
|
658
691
|
* ```typescript
|
|
659
|
-
* class DatabaseConnection extends Tag.
|
|
692
|
+
* class DatabaseConnection extends Tag.Service('DatabaseConnection') {
|
|
660
693
|
* async connect() { return; }
|
|
661
694
|
* async close() { return; }
|
|
662
695
|
* }
|
|
663
696
|
*
|
|
664
|
-
* const
|
|
697
|
+
* const container = Container.empty().register(
|
|
665
698
|
* DatabaseConnection,
|
|
666
699
|
* async () => {
|
|
667
700
|
* const conn = new DatabaseConnection();
|
|
@@ -684,7 +717,7 @@ declare class Container<in TReg extends AnyTag = never> implements IContainer<TR
|
|
|
684
717
|
*
|
|
685
718
|
* @example
|
|
686
719
|
* ```typescript
|
|
687
|
-
* const
|
|
720
|
+
* const container = Container.empty().register(DatabaseService, () => new DatabaseService());
|
|
688
721
|
* console.log(c.has(DatabaseService)); // true
|
|
689
722
|
* ```
|
|
690
723
|
*/
|
|
@@ -715,10 +748,10 @@ declare class Container<in TReg extends AnyTag = never> implements IContainer<TR
|
|
|
715
748
|
*
|
|
716
749
|
* @example Basic usage
|
|
717
750
|
* ```typescript
|
|
718
|
-
* const
|
|
751
|
+
* const container = Container.empty()
|
|
719
752
|
* .register(DatabaseService, () => new DatabaseService());
|
|
720
753
|
*
|
|
721
|
-
* const db = await c.
|
|
754
|
+
* const db = await c.resolve(DatabaseService);
|
|
722
755
|
* db.query('SELECT * FROM users');
|
|
723
756
|
* ```
|
|
724
757
|
*
|
|
@@ -726,9 +759,9 @@ declare class Container<in TReg extends AnyTag = never> implements IContainer<TR
|
|
|
726
759
|
* ```typescript
|
|
727
760
|
* // All three calls will receive the same instance
|
|
728
761
|
* const [db1, db2, db3] = await Promise.all([
|
|
729
|
-
* c.
|
|
730
|
-
* c.
|
|
731
|
-
* c.
|
|
762
|
+
* c.resolve(DatabaseService),
|
|
763
|
+
* c.resolve(DatabaseService),
|
|
764
|
+
* c.resolve(DatabaseService)
|
|
732
765
|
* ]);
|
|
733
766
|
*
|
|
734
767
|
* console.log(db1 === db2 === db3); // true
|
|
@@ -736,17 +769,58 @@ declare class Container<in TReg extends AnyTag = never> implements IContainer<TR
|
|
|
736
769
|
*
|
|
737
770
|
* @example Dependency injection in factories
|
|
738
771
|
* ```typescript
|
|
739
|
-
* const
|
|
772
|
+
* const container = Container.empty()
|
|
740
773
|
* .register(DatabaseService, () => new DatabaseService())
|
|
741
774
|
* .register(UserService, async (ctx) => {
|
|
742
|
-
* const db = await ctx.
|
|
775
|
+
* const db = await ctx.resolve(DatabaseService);
|
|
743
776
|
* return new UserService(db);
|
|
744
777
|
* });
|
|
745
778
|
*
|
|
746
|
-
* const userService = await c.
|
|
779
|
+
* const userService = await c.resolve(UserService);
|
|
780
|
+
* ```
|
|
781
|
+
*/
|
|
782
|
+
resolve<T extends TReg>(tag: T): Promise<TagType<T>>;
|
|
783
|
+
/**
|
|
784
|
+
* Resolves multiple dependencies concurrently using Promise.all.
|
|
785
|
+
*
|
|
786
|
+
* This method takes a variable number of dependency tags and resolves all of them concurrently,
|
|
787
|
+
* returning a tuple with the resolved instances in the same order as the input tags.
|
|
788
|
+
* The method maintains all the same guarantees as the individual resolve method:
|
|
789
|
+
* singleton behavior, circular dependency detection, and proper error handling.
|
|
790
|
+
*
|
|
791
|
+
* @template T - The tuple type of dependency tags to resolve
|
|
792
|
+
* @param tags - Variable number of dependency tags to resolve
|
|
793
|
+
* @returns Promise resolving to a tuple of service instances in the same order
|
|
794
|
+
* @throws {ContainerDestroyedError} If the container has been destroyed
|
|
795
|
+
* @throws {UnknownDependencyError} If any dependency is not registered
|
|
796
|
+
* @throws {CircularDependencyError} If a circular dependency is detected
|
|
797
|
+
* @throws {DependencyCreationError} If any factory function throws an error
|
|
798
|
+
*
|
|
799
|
+
* @example Basic usage
|
|
800
|
+
* ```typescript
|
|
801
|
+
* const container = Container.empty()
|
|
802
|
+
* .register(DatabaseService, () => new DatabaseService())
|
|
803
|
+
* .register(LoggerService, () => new LoggerService());
|
|
804
|
+
*
|
|
805
|
+
* const [db, logger] = await c.resolveAll(DatabaseService, LoggerService);
|
|
806
|
+
* ```
|
|
807
|
+
*
|
|
808
|
+
* @example Mixed tag types
|
|
809
|
+
* ```typescript
|
|
810
|
+
* const ApiKeyTag = Tag.of('apiKey')<string>();
|
|
811
|
+
* const container = Container.empty()
|
|
812
|
+
* .register(ApiKeyTag, () => 'secret-key')
|
|
813
|
+
* .register(UserService, () => new UserService());
|
|
814
|
+
*
|
|
815
|
+
* const [apiKey, userService] = await c.resolveAll(ApiKeyTag, UserService);
|
|
816
|
+
* ```
|
|
817
|
+
*
|
|
818
|
+
* @example Empty array
|
|
819
|
+
* ```typescript
|
|
820
|
+
* const results = await c.resolveAll(); // Returns empty array
|
|
747
821
|
* ```
|
|
748
822
|
*/
|
|
749
|
-
|
|
823
|
+
resolveAll<const T extends readonly TReg[]>(...tags: T): Promise<{ [K in keyof T]: TagType<T[K]> }>;
|
|
750
824
|
/**
|
|
751
825
|
* Copies all registrations from this container to a target container.
|
|
752
826
|
*
|
|
@@ -771,10 +845,10 @@ declare class Container<in TReg extends AnyTag = never> implements IContainer<TR
|
|
|
771
845
|
*
|
|
772
846
|
* @example Merging containers
|
|
773
847
|
* ```typescript
|
|
774
|
-
* const container1 =
|
|
848
|
+
* const container1 = Container.empty()
|
|
775
849
|
* .register(DatabaseService, () => new DatabaseService());
|
|
776
850
|
*
|
|
777
|
-
* const container2 =
|
|
851
|
+
* const container2 = Container.empty()
|
|
778
852
|
* .register(UserService, () => new UserService());
|
|
779
853
|
*
|
|
780
854
|
* const merged = container1.merge(container2);
|
|
@@ -803,7 +877,7 @@ declare class Container<in TReg extends AnyTag = never> implements IContainer<TR
|
|
|
803
877
|
*
|
|
804
878
|
* @example Basic cleanup
|
|
805
879
|
* ```typescript
|
|
806
|
-
* const
|
|
880
|
+
* const container = Container.empty()
|
|
807
881
|
* .register(DatabaseConnection,
|
|
808
882
|
* async () => {
|
|
809
883
|
* const conn = new DatabaseConnection();
|
|
@@ -813,12 +887,12 @@ declare class Container<in TReg extends AnyTag = never> implements IContainer<TR
|
|
|
813
887
|
* (conn) => conn.disconnect() // Finalizer
|
|
814
888
|
* );
|
|
815
889
|
*
|
|
816
|
-
* const db = await c.
|
|
890
|
+
* const db = await c.resolve(DatabaseConnection);
|
|
817
891
|
* await c.destroy(); // Calls conn.disconnect(), container becomes unusable
|
|
818
892
|
*
|
|
819
893
|
* // This will throw an error
|
|
820
894
|
* try {
|
|
821
|
-
* await c.
|
|
895
|
+
* await c.resolve(DatabaseConnection);
|
|
822
896
|
* } catch (error) {
|
|
823
897
|
* console.log(error.message); // "Cannot resolve dependencies from a destroyed container"
|
|
824
898
|
* }
|
|
@@ -826,9 +900,9 @@ declare class Container<in TReg extends AnyTag = never> implements IContainer<TR
|
|
|
826
900
|
*
|
|
827
901
|
* @example Application shutdown
|
|
828
902
|
* ```typescript
|
|
829
|
-
* const appContainer
|
|
903
|
+
* const appContainer Container.empty
|
|
830
904
|
* .register(DatabaseService, () => new DatabaseService())
|
|
831
|
-
* .register(HTTPServer, async (ctx) => new HTTPServer(await ctx.
|
|
905
|
+
* .register(HTTPServer, async (ctx) => new HTTPServer(await ctx.resolve(DatabaseService)));
|
|
832
906
|
*
|
|
833
907
|
* // During application shutdown
|
|
834
908
|
* process.on('SIGTERM', async () => {
|
|
@@ -855,32 +929,6 @@ declare class Container<in TReg extends AnyTag = never> implements IContainer<TR
|
|
|
855
929
|
*/
|
|
856
930
|
destroy(): Promise<void>;
|
|
857
931
|
}
|
|
858
|
-
/**
|
|
859
|
-
* Creates a new empty dependency injection container.
|
|
860
|
-
*
|
|
861
|
-
* This is a convenience factory function that creates a new DependencyContainer instance.
|
|
862
|
-
* The returned container starts with no registered dependencies and the type parameter
|
|
863
|
-
* defaults to `never`, indicating no dependencies are available for retrieval yet.
|
|
864
|
-
*
|
|
865
|
-
* @returns A new empty DependencyContainer instance
|
|
866
|
-
*
|
|
867
|
-
* @example
|
|
868
|
-
* ```typescript
|
|
869
|
-
* import { container, Tag } from 'sandly';
|
|
870
|
-
*
|
|
871
|
-
* class DatabaseService extends Tag.Class('DatabaseService') {}
|
|
872
|
-
* class UserService extends Tag.Class('UserService') {}
|
|
873
|
-
*
|
|
874
|
-
* const c = container()
|
|
875
|
-
* .register(DatabaseService, () => new DatabaseService())
|
|
876
|
-
* .register(UserService, async (ctx) =>
|
|
877
|
-
* new UserService(await ctx.get(DatabaseService))
|
|
878
|
-
* );
|
|
879
|
-
*
|
|
880
|
-
* const userService = await c.get(UserService);
|
|
881
|
-
* ```
|
|
882
|
-
*/
|
|
883
|
-
declare function container(): Container;
|
|
884
932
|
//#endregion
|
|
885
933
|
//#region src/errors.d.ts
|
|
886
934
|
type ErrorProps = {
|
|
@@ -917,7 +965,7 @@ declare class BaseError extends Error {
|
|
|
917
965
|
* @example Catching DI errors
|
|
918
966
|
* ```typescript
|
|
919
967
|
* try {
|
|
920
|
-
* await container.
|
|
968
|
+
* await container.resolve(SomeService);
|
|
921
969
|
* } catch (error) {
|
|
922
970
|
* if (error instanceof ContainerError) {
|
|
923
971
|
* console.error('DI Error:', error.message);
|
|
@@ -938,7 +986,7 @@ declare class DependencyAlreadyInstantiatedError extends ContainerError {}
|
|
|
938
986
|
/**
|
|
939
987
|
* Error thrown when attempting to use a container that has been destroyed.
|
|
940
988
|
*
|
|
941
|
-
* This error occurs when calling `container.
|
|
989
|
+
* This error occurs when calling `container.resolve()`, `container.register()`, or `container.destroy()`
|
|
942
990
|
* on a container that has already been destroyed. It indicates a programming error where the container
|
|
943
991
|
* is being used after it has been destroyed.
|
|
944
992
|
*/
|
|
@@ -946,16 +994,16 @@ declare class ContainerDestroyedError extends ContainerError {}
|
|
|
946
994
|
/**
|
|
947
995
|
* Error thrown when attempting to retrieve a dependency that hasn't been registered.
|
|
948
996
|
*
|
|
949
|
-
* This error occurs when calling `container.
|
|
997
|
+
* This error occurs when calling `container.resolve(Tag)` for a tag that was never
|
|
950
998
|
* registered via `container.register()`. It indicates a programming error where
|
|
951
999
|
* the dependency setup is incomplete.
|
|
952
1000
|
*
|
|
953
1001
|
* @example
|
|
954
1002
|
* ```typescript
|
|
955
|
-
* const
|
|
1003
|
+
* const container = Container.empty(); // Empty container
|
|
956
1004
|
*
|
|
957
1005
|
* try {
|
|
958
|
-
* await c.
|
|
1006
|
+
* await c.resolve(UnregisteredService); // This will throw
|
|
959
1007
|
* } catch (error) {
|
|
960
1008
|
* if (error instanceof UnknownDependencyError) {
|
|
961
1009
|
* console.error('Missing dependency:', error.message);
|
|
@@ -981,19 +1029,19 @@ declare class UnknownDependencyError extends ContainerError {
|
|
|
981
1029
|
*
|
|
982
1030
|
* @example Circular dependency scenario
|
|
983
1031
|
* ```typescript
|
|
984
|
-
* class ServiceA extends Tag.
|
|
985
|
-
* class ServiceB extends Tag.
|
|
1032
|
+
* class ServiceA extends Tag.Service('ServiceA') {}
|
|
1033
|
+
* class ServiceB extends Tag.Service('ServiceB') {}
|
|
986
1034
|
*
|
|
987
|
-
* const
|
|
1035
|
+
* const container = Container.empty()
|
|
988
1036
|
* .register(ServiceA, async (ctx) =>
|
|
989
|
-
* new ServiceA(await ctx.
|
|
1037
|
+
* new ServiceA(await ctx.resolve(ServiceB)) // Depends on B
|
|
990
1038
|
* )
|
|
991
1039
|
* .register(ServiceB, async (ctx) =>
|
|
992
|
-
* new ServiceB(await ctx.
|
|
1040
|
+
* new ServiceB(await ctx.resolve(ServiceA)) // Depends on A - CIRCULAR!
|
|
993
1041
|
* );
|
|
994
1042
|
*
|
|
995
1043
|
* try {
|
|
996
|
-
* await c.
|
|
1044
|
+
* await c.resolve(ServiceA);
|
|
997
1045
|
* } catch (error) {
|
|
998
1046
|
* if (error instanceof CircularDependencyError) {
|
|
999
1047
|
* console.error('Circular dependency:', error.message);
|
|
@@ -1020,14 +1068,14 @@ declare class CircularDependencyError extends ContainerError {
|
|
|
1020
1068
|
*
|
|
1021
1069
|
* @example Factory throwing error
|
|
1022
1070
|
* ```typescript
|
|
1023
|
-
* class DatabaseService extends Tag.
|
|
1071
|
+
* class DatabaseService extends Tag.Service('DatabaseService') {}
|
|
1024
1072
|
*
|
|
1025
|
-
* const
|
|
1073
|
+
* const container = Container.empty().register(DatabaseService, () => {
|
|
1026
1074
|
* throw new Error('Database connection failed');
|
|
1027
1075
|
* });
|
|
1028
1076
|
*
|
|
1029
1077
|
* try {
|
|
1030
|
-
* await c.
|
|
1078
|
+
* await c.resolve(DatabaseService);
|
|
1031
1079
|
* } catch (error) {
|
|
1032
1080
|
* if (error instanceof DependencyCreationError) {
|
|
1033
1081
|
* console.error('Failed to create:', error.message);
|
|
@@ -1077,18 +1125,13 @@ declare class DependencyFinalizationError extends ContainerError {
|
|
|
1077
1125
|
//#endregion
|
|
1078
1126
|
//#region src/layer.d.ts
|
|
1079
1127
|
/**
|
|
1080
|
-
* The most generic layer type that
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
*
|
|
1085
|
-
* - `AnyTag` for TProvides (covariant): Any layer providing specific services can be assigned
|
|
1086
|
-
* to this since the general AnyTag type can represent any specific tag type
|
|
1087
|
-
*
|
|
1088
|
-
* Used internally for functions like Layer.mergeAll() that need to accept arrays of layers
|
|
1089
|
-
* with different requirement/provision types while preserving type safety through variance.
|
|
1128
|
+
* The most generic layer type that accepts any concrete layer.
|
|
1129
|
+
*/
|
|
1130
|
+
type AnyLayer = Layer<any, any>;
|
|
1131
|
+
/**
|
|
1132
|
+
* The type ID for the Layer interface.
|
|
1090
1133
|
*/
|
|
1091
|
-
|
|
1134
|
+
declare const LayerTypeId: unique symbol;
|
|
1092
1135
|
/**
|
|
1093
1136
|
* A dependency layer represents a reusable, composable unit of dependency registrations.
|
|
1094
1137
|
* Layers allow you to organize your dependency injection setup into logical groups
|
|
@@ -1098,12 +1141,12 @@ type AnyLayer = Layer<never, AnyTag>;
|
|
|
1098
1141
|
*
|
|
1099
1142
|
* The Layer interface uses TypeScript's variance annotations to enable safe substitutability:
|
|
1100
1143
|
*
|
|
1101
|
-
* ### TRequires (
|
|
1144
|
+
* ### TRequires (covariant)
|
|
1102
1145
|
* A layer requiring fewer dependencies can substitute one requiring more:
|
|
1103
1146
|
* - `Layer<never, X>` can be used where `Layer<A | B, X>` is expected
|
|
1104
1147
|
* - Intuition: A service that needs nothing is more flexible than one that needs specific deps
|
|
1105
1148
|
*
|
|
1106
|
-
* ### TProvides (
|
|
1149
|
+
* ### TProvides (contravariant)
|
|
1107
1150
|
* A layer providing more services can substitute one providing fewer:
|
|
1108
1151
|
* - `Layer<X, A | B>` can be used where `Layer<X, A>` is expected
|
|
1109
1152
|
* - Intuition: A service that gives you extra things is compatible with expecting fewer things
|
|
@@ -1115,7 +1158,7 @@ type AnyLayer = Layer<never, AnyTag>;
|
|
|
1115
1158
|
* ```typescript
|
|
1116
1159
|
* import { layer, Tag, container } from 'sandly';
|
|
1117
1160
|
*
|
|
1118
|
-
* class DatabaseService extends Tag.
|
|
1161
|
+
* class DatabaseService extends Tag.Service('DatabaseService') {
|
|
1119
1162
|
* query() { return 'data'; }
|
|
1120
1163
|
* }
|
|
1121
1164
|
*
|
|
@@ -1125,10 +1168,10 @@ type AnyLayer = Layer<never, AnyTag>;
|
|
|
1125
1168
|
* );
|
|
1126
1169
|
*
|
|
1127
1170
|
* // Apply the layer to a container
|
|
1128
|
-
* const
|
|
1171
|
+
* const container = Container.empty();
|
|
1129
1172
|
* const finalContainer = databaseLayer.register(c);
|
|
1130
1173
|
*
|
|
1131
|
-
* const db = await finalContainer.
|
|
1174
|
+
* const db = await finalContainer.resolve(DatabaseService);
|
|
1132
1175
|
* ```
|
|
1133
1176
|
*
|
|
1134
1177
|
* @example Layer composition with variance
|
|
@@ -1136,18 +1179,19 @@ type AnyLayer = Layer<never, AnyTag>;
|
|
|
1136
1179
|
* // Layer that requires DatabaseService and provides UserService
|
|
1137
1180
|
* const userLayer = layer<typeof DatabaseService, typeof UserService>((container) =>
|
|
1138
1181
|
* container.register(UserService, async (ctx) =>
|
|
1139
|
-
* new UserService(await ctx.
|
|
1182
|
+
* new UserService(await ctx.resolve(DatabaseService))
|
|
1140
1183
|
* )
|
|
1141
1184
|
* );
|
|
1142
1185
|
*
|
|
1143
1186
|
* // Compose layers: provide database layer to user layer
|
|
1144
1187
|
* const appLayer = userLayer.provide(databaseLayer);
|
|
1145
|
-
*
|
|
1146
|
-
* // Thanks to variance, Layer<never, typeof DatabaseService> automatically works
|
|
1147
|
-
* // where Layer<typeof DatabaseService, typeof UserService> requires DatabaseService
|
|
1148
1188
|
* ```
|
|
1149
1189
|
*/
|
|
1150
|
-
interface Layer<
|
|
1190
|
+
interface Layer<TRequires extends AnyTag, TProvides extends AnyTag> {
|
|
1191
|
+
readonly [LayerTypeId]?: {
|
|
1192
|
+
readonly _TRequires: Covariant<TRequires>;
|
|
1193
|
+
readonly _TProvides: Contravariant<TProvides>;
|
|
1194
|
+
};
|
|
1151
1195
|
/**
|
|
1152
1196
|
* Applies this layer's registrations to the given container.
|
|
1153
1197
|
*
|
|
@@ -1165,13 +1209,13 @@ interface Layer<in TRequires extends AnyTag = never, out TProvides extends AnyTa
|
|
|
1165
1209
|
*
|
|
1166
1210
|
* @example Basic usage
|
|
1167
1211
|
* ```typescript
|
|
1168
|
-
* const
|
|
1212
|
+
* const container = Container.empty();
|
|
1169
1213
|
* const updatedContainer = myLayer.register(c);
|
|
1170
1214
|
* ```
|
|
1171
1215
|
*
|
|
1172
1216
|
* @example With existing services preserved
|
|
1173
1217
|
* ```typescript
|
|
1174
|
-
* const baseContainer =
|
|
1218
|
+
* const baseContainer = Container.empty()
|
|
1175
1219
|
* .register(ExistingService, () => new ExistingService());
|
|
1176
1220
|
*
|
|
1177
1221
|
* const enhanced = myLayer.register(baseContainer);
|
|
@@ -1286,7 +1330,7 @@ interface Layer<in TRequires extends AnyTag = never, out TProvides extends AnyTa
|
|
|
1286
1330
|
* ```typescript
|
|
1287
1331
|
* import { layer, Tag } from 'sandly';
|
|
1288
1332
|
*
|
|
1289
|
-
* class DatabaseService extends Tag.
|
|
1333
|
+
* class DatabaseService extends Tag.Service('DatabaseService') {
|
|
1290
1334
|
* constructor(private url: string = 'sqlite://memory') {}
|
|
1291
1335
|
* query() { return 'data'; }
|
|
1292
1336
|
* }
|
|
@@ -1311,15 +1355,15 @@ interface Layer<in TRequires extends AnyTag = never, out TProvides extends AnyTa
|
|
|
1311
1355
|
* const infraLayer = layer<typeof ConfigTag, typeof DatabaseService | typeof CacheService>(
|
|
1312
1356
|
* (container) =>
|
|
1313
1357
|
* container
|
|
1314
|
-
* .register(DatabaseService, async (ctx) => new DatabaseService(await ctx.
|
|
1315
|
-
* .register(CacheService, async (ctx) => new CacheService(await ctx.
|
|
1358
|
+
* .register(DatabaseService, async (ctx) => new DatabaseService(await ctx.resolve(ConfigTag)))
|
|
1359
|
+
* .register(CacheService, async (ctx) => new CacheService(await ctx.resolve(ConfigTag)))
|
|
1316
1360
|
* );
|
|
1317
1361
|
*
|
|
1318
1362
|
* // Service layer (requires infrastructure)
|
|
1319
1363
|
* const serviceLayer = layer<typeof DatabaseService | typeof CacheService, typeof UserService>(
|
|
1320
1364
|
* (container) =>
|
|
1321
1365
|
* container.register(UserService, async (ctx) =>
|
|
1322
|
-
* new UserService(await ctx.
|
|
1366
|
+
* new UserService(await ctx.resolve(DatabaseService), await ctx.resolve(CacheService))
|
|
1323
1367
|
* )
|
|
1324
1368
|
* );
|
|
1325
1369
|
*
|
|
@@ -1338,7 +1382,7 @@ declare function layer<TRequires extends AnyTag = never, TProvides extends AnyTa
|
|
|
1338
1382
|
*
|
|
1339
1383
|
* @internal
|
|
1340
1384
|
*/
|
|
1341
|
-
type UnionOfRequires<T extends readonly AnyLayer[]> = { [K in keyof T]: T[K] extends Layer<infer R,
|
|
1385
|
+
type UnionOfRequires<T extends readonly AnyLayer[]> = { [K in keyof T]: T[K] extends Layer<infer R, any> ? R : never }[number];
|
|
1342
1386
|
/**
|
|
1343
1387
|
* Helper type that extracts the union of all provisions from an array of layers.
|
|
1344
1388
|
* Used by Layer.mergeAll() to compute the correct provision type for the merged layer.
|
|
@@ -1349,7 +1393,7 @@ type UnionOfRequires<T extends readonly AnyLayer[]> = { [K in keyof T]: T[K] ext
|
|
|
1349
1393
|
*
|
|
1350
1394
|
* @internal
|
|
1351
1395
|
*/
|
|
1352
|
-
type UnionOfProvides<T extends readonly AnyLayer[]> = { [K in keyof T]: T[K] extends Layer<
|
|
1396
|
+
type UnionOfProvides<T extends readonly AnyLayer[]> = { [K in keyof T]: T[K] extends Layer<any, infer P> ? P : never }[number];
|
|
1353
1397
|
/**
|
|
1354
1398
|
* Utility object containing helper functions for working with layers.
|
|
1355
1399
|
*/
|
|
@@ -1370,7 +1414,7 @@ declare const Layer: {
|
|
|
1370
1414
|
* .merge(serviceLayer);
|
|
1371
1415
|
* ```
|
|
1372
1416
|
*/
|
|
1373
|
-
empty(): Layer
|
|
1417
|
+
empty(): Layer<never, never>;
|
|
1374
1418
|
/**
|
|
1375
1419
|
* Merges multiple layers at once in a type-safe way.
|
|
1376
1420
|
* This is equivalent to chaining `.merge()` calls but more convenient for multiple layers.
|
|
@@ -1461,11 +1505,17 @@ declare const Layer: {
|
|
|
1461
1505
|
//#endregion
|
|
1462
1506
|
//#region src/scoped-container.d.ts
|
|
1463
1507
|
type Scope = string | symbol;
|
|
1464
|
-
declare class ScopedContainer<
|
|
1508
|
+
declare class ScopedContainer<TReg extends AnyTag> extends Container<TReg> {
|
|
1465
1509
|
readonly scope: Scope;
|
|
1466
1510
|
private parent;
|
|
1467
1511
|
private readonly children;
|
|
1468
|
-
constructor(parent: IContainer<TReg> | null, scope: Scope);
|
|
1512
|
+
protected constructor(parent: IContainer<TReg> | null, scope: Scope);
|
|
1513
|
+
/**
|
|
1514
|
+
* Creates a new empty scoped container instance.
|
|
1515
|
+
* @param scope - The scope identifier for this container
|
|
1516
|
+
* @returns A new empty ScopedContainer instance with no registered dependencies
|
|
1517
|
+
*/
|
|
1518
|
+
static empty(scope: Scope): ScopedContainer<never>;
|
|
1469
1519
|
/**
|
|
1470
1520
|
* Registers a dependency in the scoped container.
|
|
1471
1521
|
*
|
|
@@ -1496,7 +1546,7 @@ declare class ScopedContainer<in TReg extends AnyTag = never> extends Container<
|
|
|
1496
1546
|
* 3. Otherwise, delegate to parent scope
|
|
1497
1547
|
* 4. If no parent or parent doesn't have it, throw UnknownDependencyError
|
|
1498
1548
|
*/
|
|
1499
|
-
|
|
1549
|
+
resolve<T extends TReg>(tag: T): Promise<TagType<T>>;
|
|
1500
1550
|
/**
|
|
1501
1551
|
* Destroys this scoped container and its children, preserving the container structure for reuse.
|
|
1502
1552
|
*
|
|
@@ -1548,7 +1598,7 @@ declare class ScopedContainer<in TReg extends AnyTag = never> extends Container<
|
|
|
1548
1598
|
* ```typescript
|
|
1549
1599
|
* import { container, scoped } from 'sandly';
|
|
1550
1600
|
*
|
|
1551
|
-
* const appContainer =
|
|
1601
|
+
* const appContainer = Container.empty()
|
|
1552
1602
|
* .register(DatabaseService, () => new DatabaseService())
|
|
1553
1603
|
* .register(ConfigService, () => new ConfigService());
|
|
1554
1604
|
*
|
|
@@ -1560,10 +1610,10 @@ declare class ScopedContainer<in TReg extends AnyTag = never> extends Container<
|
|
|
1560
1610
|
*
|
|
1561
1611
|
* @example Copying complex registrations
|
|
1562
1612
|
* ```typescript
|
|
1563
|
-
* const baseContainer =
|
|
1613
|
+
* const baseContainer = Container.empty()
|
|
1564
1614
|
* .register(DatabaseService, () => new DatabaseService())
|
|
1565
1615
|
* .register(UserService, {
|
|
1566
|
-
* factory: async (ctx) => new UserService(await ctx.
|
|
1616
|
+
* factory: async (ctx) => new UserService(await ctx.resolve(DatabaseService)),
|
|
1567
1617
|
* finalizer: (service) => service.cleanup()
|
|
1568
1618
|
* });
|
|
1569
1619
|
*
|
|
@@ -1575,49 +1625,60 @@ declare function scoped<TReg extends AnyTag>(container: Container<TReg>, scope:
|
|
|
1575
1625
|
//#endregion
|
|
1576
1626
|
//#region src/service.d.ts
|
|
1577
1627
|
/**
|
|
1578
|
-
* Extracts constructor parameter types from a
|
|
1628
|
+
* Extracts constructor parameter types from a ServiceTag.
|
|
1579
1629
|
* Only parameters that extend AnyTag are considered as dependencies.
|
|
1630
|
+
* @internal
|
|
1580
1631
|
*/
|
|
1581
|
-
type ConstructorParams<T extends
|
|
1632
|
+
type ConstructorParams<T extends ServiceTag<TagId, unknown>> = T extends (new (...args: infer A) => unknown) ? A : never;
|
|
1582
1633
|
/**
|
|
1583
|
-
*
|
|
1584
|
-
*
|
|
1634
|
+
* Extracts only dependency tags from a constructor parameter list.
|
|
1635
|
+
* Filters out non‑DI parameters.
|
|
1636
|
+
*
|
|
1637
|
+
* Example:
|
|
1638
|
+
* [DatabaseService, Inject<typeof ConfigTag>, number]
|
|
1639
|
+
* → typeof DatabaseService | typeof ConfigTag
|
|
1640
|
+
* @internal
|
|
1641
|
+
*/
|
|
1642
|
+
type ExtractConstructorDeps<T extends readonly unknown[]> = T extends readonly [] ? never : { [K in keyof T]: T[K] extends {
|
|
1643
|
+
readonly [ServiceTagIdKey]?: infer Id;
|
|
1644
|
+
} ? Id extends TagId ? ServiceTag<Id, T[K]> : never : ExtractInjectTag<T[K]> extends never ? never : ExtractInjectTag<T[K]> }[number];
|
|
1645
|
+
/**
|
|
1646
|
+
* Produces an ordered tuple of constructor parameters
|
|
1647
|
+
* where dependency parameters are replaced with their tag types,
|
|
1648
|
+
* while non‑DI parameters are preserved as‑is.
|
|
1649
|
+
* @internal
|
|
1585
1650
|
*/
|
|
1586
|
-
type
|
|
1587
|
-
readonly [
|
|
1588
|
-
} ? Id extends
|
|
1651
|
+
type InferConstructorDepsTuple<T extends readonly unknown[]> = T extends readonly [] ? never : { [K in keyof T]: T[K] extends {
|
|
1652
|
+
readonly [ServiceTagIdKey]?: infer Id;
|
|
1653
|
+
} ? Id extends TagId ? ServiceTag<Id, T[K]> : never : ExtractInjectTag<T[K]> extends never ? T[K] : ExtractInjectTag<T[K]> };
|
|
1589
1654
|
/**
|
|
1590
|
-
*
|
|
1591
|
-
*
|
|
1592
|
-
* Handles both ClassTag dependencies (automatic) and ValueTag dependencies (via Inject helper).
|
|
1655
|
+
* Union of all dependency tags a ServiceTag constructor requires.
|
|
1656
|
+
* Filters out non‑DI parameters.
|
|
1593
1657
|
*/
|
|
1594
|
-
type
|
|
1595
|
-
readonly [TagId]: string | symbol;
|
|
1596
|
-
} ? InstanceToConstructorType<T[K]> : ExtractInjectTag<T[K]> extends never ? never : ExtractInjectTag<T[K]> }[number];
|
|
1658
|
+
type ServiceDependencies<T extends ServiceTag<TagId, unknown>> = ExtractConstructorDeps<ConstructorParams<T>> extends AnyTag ? ExtractConstructorDeps<ConstructorParams<T>> : never;
|
|
1597
1659
|
/**
|
|
1598
|
-
*
|
|
1599
|
-
*
|
|
1600
|
-
* This is used to determine what dependencies a service requires.
|
|
1660
|
+
* Ordered tuple of dependency tags (and other constructor params)
|
|
1661
|
+
* inferred from a ServiceTag’s constructor.
|
|
1601
1662
|
*/
|
|
1602
|
-
type
|
|
1663
|
+
type ServiceDepsTuple<T extends ServiceTag<TagId, unknown>> = InferConstructorDepsTuple<ConstructorParams<T>>;
|
|
1603
1664
|
/**
|
|
1604
|
-
* Creates a service layer from any tag type (
|
|
1665
|
+
* Creates a service layer from any tag type (ServiceTag or ValueTag) with optional parameters.
|
|
1605
1666
|
*
|
|
1606
|
-
* For
|
|
1667
|
+
* For ServiceTag services:
|
|
1607
1668
|
* - Dependencies are automatically inferred from constructor parameters
|
|
1608
1669
|
* - The factory function must handle dependency injection by resolving dependencies from the container
|
|
1609
1670
|
*
|
|
1610
1671
|
* For ValueTag services:
|
|
1611
1672
|
* - No constructor dependencies are needed since they don't have constructors
|
|
1612
1673
|
*
|
|
1613
|
-
* @template T - The tag representing the service (
|
|
1614
|
-
* @param
|
|
1674
|
+
* @template T - The tag representing the service (ServiceTag or ValueTag)
|
|
1675
|
+
* @param tag - The tag (ServiceTag or ValueTag)
|
|
1615
1676
|
* @param factory - Factory function for service instantiation with container
|
|
1616
1677
|
* @returns The service layer
|
|
1617
1678
|
*
|
|
1618
1679
|
* @example Simple service without dependencies
|
|
1619
1680
|
* ```typescript
|
|
1620
|
-
* class LoggerService extends Tag.
|
|
1681
|
+
* class LoggerService extends Tag.Service('LoggerService') {
|
|
1621
1682
|
* log(message: string) { console.log(message); }
|
|
1622
1683
|
* }
|
|
1623
1684
|
*
|
|
@@ -1626,11 +1687,11 @@ type ServiceDependencies<T extends AnyTag> = T extends ClassTag<unknown, string
|
|
|
1626
1687
|
*
|
|
1627
1688
|
* @example Service with dependencies
|
|
1628
1689
|
* ```typescript
|
|
1629
|
-
* class DatabaseService extends Tag.
|
|
1690
|
+
* class DatabaseService extends Tag.Service('DatabaseService') {
|
|
1630
1691
|
* query() { return []; }
|
|
1631
1692
|
* }
|
|
1632
1693
|
*
|
|
1633
|
-
* class UserService extends Tag.
|
|
1694
|
+
* class UserService extends Tag.Service('UserService') {
|
|
1634
1695
|
* constructor(private db: DatabaseService) {
|
|
1635
1696
|
* super();
|
|
1636
1697
|
* }
|
|
@@ -1639,11 +1700,118 @@ type ServiceDependencies<T extends AnyTag> = T extends ClassTag<unknown, string
|
|
|
1639
1700
|
* }
|
|
1640
1701
|
*
|
|
1641
1702
|
* const userService = service(UserService, async (ctx) =>
|
|
1642
|
-
* new UserService(await ctx.
|
|
1703
|
+
* new UserService(await ctx.resolve(DatabaseService))
|
|
1704
|
+
* );
|
|
1705
|
+
* ```
|
|
1706
|
+
*/
|
|
1707
|
+
declare function service<T extends ServiceTag<TagId, unknown>>(tag: T, spec: DependencySpec<T, ServiceDependencies<T>>): Layer<ServiceDependencies<T>, T>;
|
|
1708
|
+
/**
|
|
1709
|
+
* Specification for autoService.
|
|
1710
|
+
* Can be either a tuple of constructor parameters or an object with dependencies and finalizer.
|
|
1711
|
+
*/
|
|
1712
|
+
type AutoServiceSpec<T extends ServiceTag<TagId, unknown>> = ServiceDepsTuple<T> | {
|
|
1713
|
+
dependencies: ServiceDepsTuple<T>;
|
|
1714
|
+
finalizer?: Finalizer<TagType<T>>;
|
|
1715
|
+
};
|
|
1716
|
+
/**
|
|
1717
|
+
* Creates a service layer with automatic dependency injection by inferring constructor parameters.
|
|
1718
|
+
*
|
|
1719
|
+
* This is a convenience function that automatically resolves constructor dependencies and passes
|
|
1720
|
+
* both DI-managed dependencies and static values to the service constructor in the correct order.
|
|
1721
|
+
* It eliminates the need to manually write factory functions for services with constructor dependencies.
|
|
1722
|
+
*
|
|
1723
|
+
* @template T - The ServiceTag representing the service class
|
|
1724
|
+
* @param tag - The service tag (must be a ServiceTag, not a ValueTag)
|
|
1725
|
+
* @param deps - Tuple of constructor parameters in order - mix of dependency tags and static values
|
|
1726
|
+
* @param finalizer - Optional cleanup function called when the container is destroyed
|
|
1727
|
+
* @returns A service layer that automatically handles dependency injection
|
|
1728
|
+
*
|
|
1729
|
+
* @example Simple service with dependencies
|
|
1730
|
+
* ```typescript
|
|
1731
|
+
* class DatabaseService extends Tag.Service('DatabaseService') {
|
|
1732
|
+
* constructor(private url: string) {
|
|
1733
|
+
* super();
|
|
1734
|
+
* }
|
|
1735
|
+
* connect() { return `Connected to ${this.url}`; }
|
|
1736
|
+
* }
|
|
1737
|
+
*
|
|
1738
|
+
* class UserService extends Tag.Service('UserService') {
|
|
1739
|
+
* constructor(private db: DatabaseService, private timeout: number) {
|
|
1740
|
+
* super();
|
|
1741
|
+
* }
|
|
1742
|
+
* getUsers() { return this.db.query('SELECT * FROM users'); }
|
|
1743
|
+
* }
|
|
1744
|
+
*
|
|
1745
|
+
* // Automatically inject DatabaseService and pass static timeout value
|
|
1746
|
+
* const userService = autoService(UserService, [DatabaseService, 5000]);
|
|
1747
|
+
* ```
|
|
1748
|
+
*
|
|
1749
|
+
* @example Mixed dependencies and static values
|
|
1750
|
+
* ```typescript
|
|
1751
|
+
* class NotificationService extends Tag.Service('NotificationService') {
|
|
1752
|
+
* constructor(
|
|
1753
|
+
* private logger: LoggerService,
|
|
1754
|
+
* private apiKey: string,
|
|
1755
|
+
* private retries: number,
|
|
1756
|
+
* private cache: CacheService
|
|
1757
|
+
* ) {
|
|
1758
|
+
* super();
|
|
1759
|
+
* }
|
|
1760
|
+
* }
|
|
1761
|
+
*
|
|
1762
|
+
* // Mix of DI tags and static values in constructor order
|
|
1763
|
+
* const notificationService = autoService(NotificationService, [
|
|
1764
|
+
* LoggerService, // Will be resolved from container
|
|
1765
|
+
* 'secret-api-key', // Static string value
|
|
1766
|
+
* 3, // Static number value
|
|
1767
|
+
* CacheService // Will be resolved from container
|
|
1768
|
+
* ]);
|
|
1769
|
+
* ```
|
|
1770
|
+
*
|
|
1771
|
+
* @example Compared to manual service creation
|
|
1772
|
+
* ```typescript
|
|
1773
|
+
* // Manual approach (more verbose)
|
|
1774
|
+
* const userServiceManual = service(UserService, async (ctx) => {
|
|
1775
|
+
* const db = await ctx.resolve(DatabaseService);
|
|
1776
|
+
* return new UserService(db, 5000);
|
|
1777
|
+
* });
|
|
1778
|
+
*
|
|
1779
|
+
* // Auto approach (concise)
|
|
1780
|
+
* const userServiceAuto = autoService(UserService, [DatabaseService, 5000]);
|
|
1781
|
+
* ```
|
|
1782
|
+
*
|
|
1783
|
+
* @example With finalizer for cleanup
|
|
1784
|
+
* ```typescript
|
|
1785
|
+
* class DatabaseService extends Tag.Service('DatabaseService') {
|
|
1786
|
+
* constructor(private connectionString: string) {
|
|
1787
|
+
* super();
|
|
1788
|
+
* }
|
|
1789
|
+
*
|
|
1790
|
+
* private connection: Connection | null = null;
|
|
1791
|
+
*
|
|
1792
|
+
* async connect() {
|
|
1793
|
+
* this.connection = await createConnection(this.connectionString);
|
|
1794
|
+
* }
|
|
1795
|
+
*
|
|
1796
|
+
* async disconnect() {
|
|
1797
|
+
* if (this.connection) {
|
|
1798
|
+
* await this.connection.close();
|
|
1799
|
+
* this.connection = null;
|
|
1800
|
+
* }
|
|
1801
|
+
* }
|
|
1802
|
+
* }
|
|
1803
|
+
*
|
|
1804
|
+
* // Service with automatic cleanup
|
|
1805
|
+
* const dbService = autoService(
|
|
1806
|
+
* DatabaseService,
|
|
1807
|
+
* {
|
|
1808
|
+
* dependencies: ['postgresql://localhost:5432/mydb'],
|
|
1809
|
+
* finalizer: (service) => service.disconnect() // Finalizer for cleanup
|
|
1810
|
+
* }
|
|
1643
1811
|
* );
|
|
1644
1812
|
* ```
|
|
1645
1813
|
*/
|
|
1646
|
-
declare function
|
|
1814
|
+
declare function autoService<T extends ServiceTag<TagId, unknown>>(tag: T, spec: AutoServiceSpec<T>): Layer<ServiceDependencies<T>, T>;
|
|
1647
1815
|
//#endregion
|
|
1648
1816
|
//#region src/value.d.ts
|
|
1649
1817
|
/**
|
|
@@ -1664,6 +1832,6 @@ declare function service<T extends AnyTag>(serviceClass: T, spec: DependencySpec
|
|
|
1664
1832
|
* const config = Layer.merge(apiKey, dbUrl);
|
|
1665
1833
|
* ```
|
|
1666
1834
|
*/
|
|
1667
|
-
declare function value<
|
|
1835
|
+
declare function value<Id extends TagId, T>(tag: ValueTag<Id, T>, constantValue: T): Layer<never, ValueTag<Id, T>>;
|
|
1668
1836
|
//#endregion
|
|
1669
|
-
export { type AnyLayer, type AnyTag, CircularDependencyError,
|
|
1837
|
+
export { type AnyLayer, type AnyTag, CircularDependencyError, Container, ContainerDestroyedError, ContainerError, DependencyAlreadyInstantiatedError, DependencyCreationError, DependencyFinalizationError, type DependencyLifecycle, type DependencySpec, type Factory, type Finalizer, type IContainer, type Inject, InjectSource, Layer, type PromiseOrValue, type ResolutionContext, type Scope, ScopedContainer, type ServiceDependencies, type ServiceDepsTuple, type ServiceTag, Tag, type TagType, UnknownDependencyError, type ValueTag, autoService, layer, scoped, service, value };
|