sandly 0.5.2 → 1.0.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.
Files changed (4) hide show
  1. package/README.md +65 -14
  2. package/dist/index.d.ts +486 -402
  3. package/dist/index.js +245 -146
  4. package/package.json +4 -11
package/dist/index.d.ts CHANGED
@@ -197,40 +197,6 @@ declare const Tag: {
197
197
  * ```
198
198
  */
199
199
  of: <Id extends TagId>(id: Id) => <T>() => ValueTag<Id, T>;
200
- /**
201
- * Creates an anonymous value tag with a unique symbol identifier.
202
- *
203
- * This is useful when you want a tag that's guaranteed to be unique but don't
204
- * need a human-readable identifier. Each call creates a new unique symbol,
205
- * making it impossible to accidentally create duplicate tags.
206
- *
207
- * @template T - The type that this tag represents
208
- * @returns A value tag with a unique symbol identifier
209
- *
210
- * @example
211
- * ```typescript
212
- * interface InternalConfig {
213
- * secretKey: string;
214
- * }
215
- *
216
- * const InternalConfigTag = Tag.for<InternalConfig>();
217
- *
218
- * // This tag is guaranteed to be unique - no chance of conflicts
219
- * container.register(InternalConfigTag, () => ({
220
- * secretKey: generateSecret()
221
- * }));
222
- * ```
223
- *
224
- * @example Multiple anonymous tags
225
- * ```typescript
226
- * const ConfigA = Tag.for<string>();
227
- * const ConfigB = Tag.for<string>();
228
- *
229
- * // These are different tags even though they have the same type
230
- * console.log(ConfigA === ConfigB); // false
231
- * ```
232
- */
233
- for: <T>() => ValueTag<symbol, T>;
234
200
  /**
235
201
  * Creates a base class that can be extended to create service classes with dependency tags.
236
202
  *
@@ -301,11 +267,9 @@ declare const Tag: {
301
267
  * @example
302
268
  * ```typescript
303
269
  * const StringTag = Tag.of('myString')<string>();
304
- * const SymbolTag = Tag.for<number>();
305
270
  * class ServiceClass extends Tag.Service('MyService') {}
306
271
  *
307
272
  * console.log(Tag.id(StringTag)); // "myString"
308
- * console.log(Tag.id(SymbolTag)); // "Symbol()"
309
273
  * console.log(Tag.id(ServiceClass)); // "MyService"
310
274
  * ```
311
275
  *
@@ -353,12 +317,12 @@ type Inject<T extends ValueTag<TagId, unknown>> = T extends ValueTag<any, infer
353
317
  */
354
318
  type ExtractInjectTag<T> = T extends {
355
319
  readonly [InjectSource]?: infer U;
356
- } ? U : never;
357
- //#endregion
320
+ } ? U : never; //#endregion
358
321
  //#region src/types.d.ts
359
322
  type PromiseOrValue<T> = T | Promise<T>;
360
323
  type Contravariant<A> = (_: A) => void;
361
324
  type Covariant<A> = (_: never) => A;
325
+
362
326
  //#endregion
363
327
  //#region src/container.d.ts
364
328
  /**
@@ -372,7 +336,7 @@ type Covariant<A> = (_: never) => A;
372
336
  * (returning Promise<T>). The container handles both cases transparently.
373
337
  *
374
338
  * @template T - The type of the service instance being created
375
- * @template TReg - Union type of all dependencies available in the container
339
+ * @template TRequires - Union type of all required dependencies
376
340
  *
377
341
  * @example Synchronous factory
378
342
  * ```typescript
@@ -392,7 +356,7 @@ type Covariant<A> = (_: never) => A;
392
356
  * };
393
357
  * ```
394
358
  */
395
- type Factory<T, TReg extends AnyTag> = (ctx: ResolutionContext<TReg>) => PromiseOrValue<T>;
359
+ type Factory<T, TRequires extends AnyTag> = (ctx: ResolutionContext<TRequires>) => PromiseOrValue<T>;
396
360
  /**
397
361
  * Type representing a finalizer function used to clean up dependency instances.
398
362
  *
@@ -446,7 +410,7 @@ type Finalizer<T> = (instance: T) => PromiseOrValue<void>;
446
410
  * lifecycle logic or want to share lifecycle definitions across multiple services.
447
411
  *
448
412
  * @template T - The instance type
449
- * @template TReg - Union type of all dependencies available in the container
413
+ * @template TRequires - Union type of all required dependencies
450
414
  *
451
415
  * @example Using DependencyLifecycle as an object
452
416
  * ```typescript
@@ -524,8 +488,8 @@ type Finalizer<T> = (instance: T) => PromiseOrValue<void>;
524
488
  * );
525
489
  * ```
526
490
  */
527
- interface DependencyLifecycle<T, TReg extends AnyTag> {
528
- create: Factory<T, TReg>;
491
+ interface DependencyLifecycle<T, TRequires extends AnyTag> {
492
+ create: Factory<T, TRequires>;
529
493
  cleanup?: Finalizer<T>;
530
494
  }
531
495
  /**
@@ -536,7 +500,7 @@ interface DependencyLifecycle<T, TReg extends AnyTag> {
536
500
  * - A complete lifecycle object with both factory and finalizer
537
501
  *
538
502
  * @template T - The dependency tag type
539
- * @template TReg - Union type of all dependencies available in the container
503
+ * @template TRequires - Union type of all required dependencies
540
504
  *
541
505
  * @example Simple factory registration
542
506
  * ```typescript
@@ -556,33 +520,37 @@ interface DependencyLifecycle<T, TReg extends AnyTag> {
556
520
  * Container.empty().register(DatabaseConnection, spec);
557
521
  * ```
558
522
  */
559
- type DependencySpec<T extends AnyTag, TReg extends AnyTag> = Factory<TagType<T>, TReg> | DependencyLifecycle<TagType<T>, TReg>;
523
+ type DependencySpec<T extends AnyTag, TRequires extends AnyTag> = Factory<TagType<T>, TRequires> | DependencyLifecycle<TagType<T>, TRequires>;
560
524
  /**
561
525
  * Type representing the context available to factory functions during dependency resolution.
562
526
  *
563
527
  * This type contains only the `resolve` and `resolveAll` methods from the container, which are used to retrieve
564
528
  * other dependencies during the creation of a service.
565
529
  *
566
- * @template TReg - Union type of all dependencies available in the container
530
+ * @template TTags - Union type of all dependencies available in the container
567
531
  */
568
- type ResolutionContext<TReg extends AnyTag> = Pick<IContainer<TReg>, 'resolve' | 'resolveAll'>;
532
+ type ResolutionContext<TTags extends AnyTag> = Pick<IContainer<TTags>, 'resolve' | 'resolveAll'>;
569
533
  declare const ContainerTypeId: unique symbol;
570
534
  /**
571
535
  * Interface representing a container that can register and retrieve dependencies.
572
536
  *
573
- * @template TReg - Union type of all dependencies available in the container
537
+ * @template TTags - Union type of all dependencies available in the container
574
538
  */
575
- interface IContainer<TReg extends AnyTag = never> {
539
+ interface IContainer<TTags extends AnyTag = never> {
576
540
  readonly [ContainerTypeId]: {
577
- readonly _TReg: Contravariant<TReg>;
541
+ readonly _TTags: Contravariant<TTags>;
578
542
  };
579
- register: <T extends AnyTag>(tag: T, spec: DependencySpec<T, TReg>) => IContainer<TReg | T>;
543
+ register: <T extends AnyTag>(tag: T, spec: DependencySpec<T, TTags>) => IContainer<TTags | T>;
580
544
  has(tag: AnyTag): boolean;
581
545
  exists(tag: AnyTag): boolean;
582
- resolve: <T extends TReg>(tag: T) => Promise<TagType<T>>;
583
- resolveAll: <const T extends readonly TReg[]>(...tags: T) => Promise<{ [K in keyof T]: TagType<T[K]> }>;
546
+ resolve: <T extends TTags>(tag: T) => Promise<TagType<T>>;
547
+ resolveAll: <const T extends readonly TTags[]>(...tags: T) => Promise<{ [K in keyof T]: TagType<T[K]> }>;
584
548
  destroy(): Promise<void>;
585
549
  }
550
+ /**
551
+ * Extracts the registered tags (TTags) from a container type.
552
+ */
553
+ type ContainerTags<C> = C extends IContainer<infer TTags> ? TTags : never;
586
554
  /**
587
555
  * A type-safe dependency injection container that manages service instantiation,
588
556
  * caching, and lifecycle management with support for async dependencies and
@@ -592,7 +560,7 @@ interface IContainer<TReg extends AnyTag = never> {
592
560
  * at the type level, ensuring that only registered dependencies can be retrieved
593
561
  * and preventing runtime errors.
594
562
  *
595
- * @template TReg - Union type of all registered dependency tags in this container
563
+ * @template TTags - Union type of all registered dependency tags in this container
596
564
  *
597
565
  * @example Basic usage with service tags
598
566
  * ```typescript
@@ -650,9 +618,9 @@ interface IContainer<TReg extends AnyTag = never> {
650
618
  * await c.destroy(); // Calls all finalizers
651
619
  * ```
652
620
  */
653
- declare class Container<TReg extends AnyTag> implements IContainer<TReg> {
621
+ declare class Container<TTags extends AnyTag> implements IContainer<TTags> {
654
622
  readonly [ContainerTypeId]: {
655
- readonly _TReg: Contravariant<TReg>;
623
+ readonly _TTags: Contravariant<TTags>;
656
624
  };
657
625
  protected constructor();
658
626
  /**
@@ -665,7 +633,7 @@ declare class Container<TReg extends AnyTag> implements IContainer<TReg> {
665
633
  * Factory functions for creating dependency instances.
666
634
  * @internal
667
635
  */
668
- protected readonly factories: Map<AnyTag, Factory<unknown, TReg>>;
636
+ protected readonly factories: Map<AnyTag, Factory<unknown, TTags>>;
669
637
  /**
670
638
  * Finalizer functions for cleaning up dependencies when the container is destroyed.
671
639
  * @internal
@@ -763,7 +731,7 @@ declare class Container<TReg extends AnyTag> implements IContainer<TReg> {
763
731
  * );
764
732
  * ```
765
733
  */
766
- register<T extends AnyTag>(tag: T, spec: DependencySpec<T, TReg>): Container<TReg | T>;
734
+ register<T extends AnyTag>(tag: T, spec: DependencySpec<T, TTags>): Container<TTags | T>;
767
735
  /**
768
736
  * Checks if a dependency has been registered in the container.
769
737
  *
@@ -837,13 +805,13 @@ declare class Container<TReg extends AnyTag> implements IContainer<TReg> {
837
805
  * const userService = await c.resolve(UserService);
838
806
  * ```
839
807
  */
840
- resolve<T extends TReg>(tag: T): Promise<TagType<T>>;
808
+ resolve<T extends TTags>(tag: T): Promise<TagType<T>>;
841
809
  /**
842
810
  * Internal resolution method that tracks the dependency chain for circular dependency detection.
843
811
  * Can be overridden by subclasses (e.g., ScopedContainer) to implement custom resolution logic.
844
812
  * @internal
845
813
  */
846
- protected resolveInternal<T extends TReg>(tag: T, chain: AnyTag[]): Promise<TagType<T>>;
814
+ protected resolveInternal<T extends TTags>(tag: T, chain: AnyTag[]): Promise<TagType<T>>;
847
815
  /**
848
816
  * Resolves multiple dependencies concurrently using Promise.all.
849
817
  *
@@ -884,7 +852,7 @@ declare class Container<TReg extends AnyTag> implements IContainer<TReg> {
884
852
  * const results = await c.resolveAll(); // Returns empty array
885
853
  * ```
886
854
  */
887
- resolveAll<const T extends readonly TReg[]>(...tags: T): Promise<{ [K in keyof T]: TagType<T[K]> }>;
855
+ resolveAll<const T extends readonly TTags[]>(...tags: T): Promise<{ [K in keyof T]: TagType<T[K]> }>;
888
856
  /**
889
857
  * Destroys all instantiated dependencies by calling their finalizers and makes the container unusable.
890
858
  *
@@ -958,297 +926,142 @@ declare class Container<TReg extends AnyTag> implements IContainer<TReg> {
958
926
  */
959
927
  destroy(): Promise<void>;
960
928
  }
929
+
961
930
  //#endregion
962
- //#region src/errors.d.ts
963
- type ErrorProps = {
964
- cause?: unknown;
965
- detail?: Record<string, unknown>;
966
- };
967
- type ErrorDump = {
968
- name: string;
969
- message: string;
970
- stack?: string;
971
- error: {
972
- name: string;
973
- message: string;
974
- detail: Record<string, unknown>;
975
- cause?: unknown;
976
- };
977
- };
931
+ //#region src/scoped-container.d.ts
932
+ type Scope = string | symbol;
933
+ declare class ScopedContainer<TTags extends AnyTag> extends Container<TTags> {
934
+ readonly scope: Scope;
935
+ private parent;
936
+ private readonly children;
937
+ protected constructor(parent: IContainer<TTags> | null, scope: Scope);
938
+ /**
939
+ * Creates a new empty scoped container instance.
940
+ * @param scope - The scope identifier for this container
941
+ * @returns A new empty ScopedContainer instance with no registered dependencies
942
+ */
943
+ static empty(scope: Scope): ScopedContainer<never>;
944
+ /**
945
+ * Registers a dependency in the scoped container.
946
+ *
947
+ * Overrides the base implementation to return ScopedContainer type
948
+ * for proper method chaining support.
949
+ */
950
+ register<T extends AnyTag>(tag: T, spec: DependencySpec<T, TTags>): ScopedContainer<TTags | T>;
951
+ /**
952
+ * Checks if a dependency has been registered in this scope or any parent scope.
953
+ *
954
+ * This method checks the current scope first, then walks up the parent chain.
955
+ * Returns true if the dependency has been registered somewhere in the scope hierarchy.
956
+ */
957
+ has(tag: AnyTag): boolean;
958
+ /**
959
+ * Checks if a dependency has been instantiated in this scope or any parent scope.
960
+ *
961
+ * This method checks the current scope first, then walks up the parent chain.
962
+ * Returns true if the dependency has been instantiated somewhere in the scope hierarchy.
963
+ */
964
+ exists(tag: AnyTag): boolean;
965
+ /**
966
+ * Retrieves a dependency instance, resolving from the current scope or parent scopes.
967
+ *
968
+ * Resolution strategy:
969
+ * 1. Check cache in current scope
970
+ * 2. Check if factory exists in current scope - if so, create instance here
971
+ * 3. Otherwise, delegate to parent scope
972
+ * 4. If no parent or parent doesn't have it, throw UnknownDependencyError
973
+ */
974
+ resolve<T extends TTags>(tag: T): Promise<TagType<T>>;
975
+ /**
976
+ * Internal resolution with delegation logic for scoped containers.
977
+ * @internal
978
+ */
979
+ protected resolveInternal<T extends TTags>(tag: T, chain: AnyTag[]): Promise<TagType<T>>;
980
+ /**
981
+ * Destroys this scoped container and its children, preserving the container structure for reuse.
982
+ *
983
+ * This method ensures proper cleanup order while maintaining reusability:
984
+ * 1. Destroys all child scopes first (they may depend on parent scope dependencies)
985
+ * 2. Then calls finalizers for dependencies created in this scope
986
+ * 3. Clears only instance caches - preserves factories, finalizers, and child structure
987
+ *
988
+ * Child destruction happens first to ensure dependencies don't get cleaned up
989
+ * before their dependents.
990
+ */
991
+ destroy(): Promise<void>;
992
+ /**
993
+ * Creates a child scoped container.
994
+ *
995
+ * Child containers inherit access to parent dependencies but maintain
996
+ * their own scope for new registrations and instance caching.
997
+ */
998
+ child(scope: Scope): ScopedContainer<TTags>;
999
+ }
1000
+
1001
+ //#endregion
1002
+ //#region src/layer.d.ts
978
1003
  /**
979
- * Base error class for all library errors.
980
- *
981
- * This extends the native Error class to provide consistent error handling
982
- * and structured error information across the library.
1004
+ * Replaces the TTags type parameter in a container type with a new type.
1005
+ * Preserves the concrete container type (Container, ScopedContainer, or IContainer).
983
1006
  *
984
- * @example Catching library errors
985
- * ```typescript
986
- * try {
987
- * await container.resolve(SomeService);
988
- * } catch (error) {
989
- * if (error instanceof SandlyError) {
990
- * console.error('DI Error:', error.message);
991
- * console.error('Details:', error.detail);
992
- * }
993
- * }
994
- * ```
1007
+ * Uses contravariance to detect container types:
1008
+ * - Any ScopedContainer<X> extends ScopedContainer<never>
1009
+ * - Any Container<X> extends Container<never> (but not ScopedContainer<never>)
1010
+ * - Falls back to IContainer for anything else
1011
+ * @internal
995
1012
  */
996
- declare class SandlyError extends Error {
997
- detail: Record<string, unknown> | undefined;
998
- constructor(message: string, {
999
- cause,
1000
- detail
1001
- }?: ErrorProps);
1002
- static ensure(error: unknown): SandlyError;
1003
- dump(): ErrorDump;
1004
- dumps(): string;
1005
- }
1013
+ type WithContainerTags<TContainer, TNewTags extends AnyTag> = TContainer extends ScopedContainer<never> ? ScopedContainer<TNewTags> : TContainer extends Container<never> ? Container<TNewTags> : IContainer<TNewTags>;
1006
1014
  /**
1007
- * Error thrown when attempting to register a dependency that has already been instantiated.
1008
- *
1009
- * This error occurs when calling `container.register()` for a tag that has already been instantiated.
1010
- * Registration must happen before any instantiation occurs, as cached instances would still be used
1011
- * by existing dependencies.
1015
+ * The most generic layer type that accepts any concrete layer.
1012
1016
  */
1013
- declare class DependencyAlreadyInstantiatedError extends SandlyError {}
1017
+ type AnyLayer = Layer<any, any>;
1014
1018
  /**
1015
- * Error thrown when attempting to use a container that has been destroyed.
1016
- *
1017
- * This error occurs when calling `container.resolve()`, `container.register()`, or `container.destroy()`
1018
- * on a container that has already been destroyed. It indicates a programming error where the container
1019
- * is being used after it has been destroyed.
1019
+ * The type ID for the Layer interface.
1020
1020
  */
1021
- declare class ContainerDestroyedError extends SandlyError {}
1021
+ declare const LayerTypeId: unique symbol;
1022
1022
  /**
1023
- * Error thrown when attempting to retrieve a dependency that hasn't been registered.
1023
+ * A dependency layer represents a reusable, composable unit of dependency registrations.
1024
+ * Layers allow you to organize your dependency injection setup into logical groups
1025
+ * that can be combined and reused across different contexts.
1024
1026
  *
1025
- * This error occurs when calling `container.resolve(Tag)` for a tag that was never
1026
- * registered via `container.register()`. It indicates a programming error where
1027
- * the dependency setup is incomplete.
1027
+ * ## Type Variance
1028
1028
  *
1029
- * @example
1030
- * ```typescript
1031
- * const container = Container.empty(); // Empty container
1029
+ * The Layer interface uses TypeScript's variance annotations to enable safe substitutability:
1032
1030
  *
1033
- * try {
1034
- * await c.resolve(UnregisteredService); // This will throw
1035
- * } catch (error) {
1036
- * if (error instanceof UnknownDependencyError) {
1037
- * console.error('Missing dependency:', error.message);
1038
- * }
1039
- * }
1040
- * ```
1041
- */
1042
- declare class UnknownDependencyError extends SandlyError {
1043
- /**
1044
- * @internal
1045
- * Creates an UnknownDependencyError for the given tag.
1046
- *
1047
- * @param tag - The dependency tag that wasn't found
1048
- */
1049
- constructor(tag: AnyTag);
1050
- }
1051
- /**
1052
- * Error thrown when a circular dependency is detected during dependency resolution.
1031
+ * ### TRequires (covariant)
1032
+ * A layer requiring fewer dependencies can substitute one requiring more:
1033
+ * - `Layer<never, X>` can be used where `Layer<A | B, X>` is expected
1034
+ * - Intuition: A service that needs nothing is more flexible than one that needs specific deps
1053
1035
  *
1054
- * This occurs when service A depends on service B, which depends on service A (directly
1055
- * or through a chain of dependencies). The error includes the full dependency chain
1056
- * to help identify the circular reference.
1036
+ * ### TProvides (contravariant)
1037
+ * A layer providing more services can substitute one providing fewer:
1038
+ * - `Layer<X, A | B>` can be used where `Layer<X, A>` is expected
1039
+ * - Intuition: A service that gives you extra things is compatible with expecting fewer things
1057
1040
  *
1058
- * @example Circular dependency scenario
1059
- * ```typescript
1060
- * class ServiceA extends Tag.Service('ServiceA') {}
1061
- * class ServiceB extends Tag.Service('ServiceB') {}
1041
+ * @template TRequires - The union of tags this layer requires to be satisfied by other layers
1042
+ * @template TProvides - The union of tags this layer provides/registers
1062
1043
  *
1063
- * const container = Container.empty()
1064
- * .register(ServiceA, async (ctx) =>
1065
- * new ServiceA(await ctx.resolve(ServiceB)) // Depends on B
1066
- * )
1067
- * .register(ServiceB, async (ctx) =>
1068
- * new ServiceB(await ctx.resolve(ServiceA)) // Depends on A - CIRCULAR!
1069
- * );
1044
+ * @example Basic layer usage
1045
+ * ```typescript
1046
+ * import { layer, Tag, container } from 'sandly';
1070
1047
  *
1071
- * try {
1072
- * await c.resolve(ServiceA);
1073
- * } catch (error) {
1074
- * if (error instanceof CircularDependencyError) {
1075
- * console.error('Circular dependency:', error.message);
1076
- * // Output: "Circular dependency detected for ServiceA: ServiceA -> ServiceB -> ServiceA"
1077
- * }
1048
+ * class DatabaseService extends Tag.Service('DatabaseService') {
1049
+ * query() { return 'data'; }
1078
1050
  * }
1079
- * ```
1080
- */
1081
- declare class CircularDependencyError extends SandlyError {
1082
- /**
1083
- * @internal
1084
- * Creates a CircularDependencyError with the dependency chain information.
1085
- *
1086
- * @param tag - The tag where the circular dependency was detected
1087
- * @param dependencyChain - The chain of dependencies that led to the circular reference
1088
- */
1089
- constructor(tag: AnyTag, dependencyChain: AnyTag[]);
1090
- }
1091
- /**
1092
- * Error thrown when a dependency factory function throws an error during instantiation.
1093
- *
1094
- * This wraps the original error with additional context about which dependency
1095
- * failed to be created. The original error is preserved as the `cause` property.
1096
1051
  *
1097
- * When dependencies are nested (A depends on B depends on C), and C's factory throws,
1098
- * you get nested DependencyCreationErrors. Use `getRootCause()` to get the original error.
1099
- *
1100
- * @example Factory throwing error
1101
- * ```typescript
1102
- * class DatabaseService extends Tag.Service('DatabaseService') {}
1052
+ * // Create a layer that provides DatabaseService
1053
+ * const databaseLayer = layer<never, typeof DatabaseService>((container) =>
1054
+ * container.register(DatabaseService, () => new DatabaseService())
1055
+ * );
1103
1056
  *
1104
- * const container = Container.empty().register(DatabaseService, () => {
1105
- * throw new Error('Database connection failed');
1106
- * });
1057
+ * // Apply the layer to a container
1058
+ * const container = Container.empty();
1059
+ * const finalContainer = databaseLayer.register(c);
1107
1060
  *
1108
- * try {
1109
- * await c.resolve(DatabaseService);
1110
- * } catch (error) {
1111
- * if (error instanceof DependencyCreationError) {
1112
- * console.error('Failed to create:', error.message);
1113
- * console.error('Original error:', error.cause);
1114
- * }
1115
- * }
1061
+ * const db = await finalContainer.resolve(DatabaseService);
1116
1062
  * ```
1117
1063
  *
1118
- * @example Getting root cause from nested errors
1119
- * ```typescript
1120
- * // ServiceA -> ServiceB -> ServiceC (ServiceC throws)
1121
- * try {
1122
- * await container.resolve(ServiceA);
1123
- * } catch (error) {
1124
- * if (error instanceof DependencyCreationError) {
1125
- * console.error('Top-level error:', error.message); // "Error creating instance of ServiceA"
1126
- * const rootCause = error.getRootCause();
1127
- * console.error('Root cause:', rootCause); // Original error from ServiceC
1128
- * }
1129
- * }
1130
- * ```
1131
- */
1132
- declare class DependencyCreationError extends SandlyError {
1133
- /**
1134
- * @internal
1135
- * Creates a DependencyCreationError wrapping the original factory error.
1136
- *
1137
- * @param tag - The tag of the dependency that failed to be created
1138
- * @param error - The original error thrown by the factory function
1139
- */
1140
- constructor(tag: AnyTag, error: unknown);
1141
- /**
1142
- * Traverses the error chain to find the root cause error.
1143
- *
1144
- * When dependencies are nested, each level wraps the error in a DependencyCreationError.
1145
- * This method unwraps all the layers to get to the original error that started the failure.
1146
- *
1147
- * @returns The root cause error (not a DependencyCreationError unless that's the only error)
1148
- *
1149
- * @example
1150
- * ```typescript
1151
- * try {
1152
- * await container.resolve(UserService);
1153
- * } catch (error) {
1154
- * if (error instanceof DependencyCreationError) {
1155
- * const rootCause = error.getRootCause();
1156
- * console.error('Root cause:', rootCause);
1157
- * }
1158
- * }
1159
- * ```
1160
- */
1161
- getRootCause(): unknown;
1162
- }
1163
- /**
1164
- * Error thrown when one or more finalizers fail during container destruction.
1165
- *
1166
- * This error aggregates multiple finalizer failures that occurred during
1167
- * `container.destroy()`. Even if some finalizers fail, the container cleanup
1168
- * process continues and this error contains details of all failures.
1169
- *
1170
- * @example Handling finalization errors
1171
- * ```typescript
1172
- * try {
1173
- * await container.destroy();
1174
- * } catch (error) {
1175
- * if (error instanceof DependencyFinalizationError) {
1176
- * console.error('Some finalizers failed');
1177
- * console.error('Error details:', error.detail.errors);
1178
- * }
1179
- * }
1180
- * ```
1181
- */
1182
- declare class DependencyFinalizationError extends SandlyError {
1183
- private readonly errors;
1184
- /**
1185
- * @internal
1186
- * Creates a DependencyFinalizationError aggregating multiple finalizer failures.
1187
- *
1188
- * @param errors - Array of errors thrown by individual finalizers
1189
- */
1190
- constructor(errors: unknown[]);
1191
- /**
1192
- * Returns the root causes of the errors that occurred during finalization.
1193
- *
1194
- * @returns An array of the errors that occurred during finalization.
1195
- * You can expect at least one error in the array.
1196
- */
1197
- getRootCauses(): unknown[];
1198
- }
1199
- //#endregion
1200
- //#region src/layer.d.ts
1201
- /**
1202
- * The most generic layer type that accepts any concrete layer.
1203
- */
1204
- type AnyLayer = Layer<any, any>;
1205
- /**
1206
- * The type ID for the Layer interface.
1207
- */
1208
- declare const LayerTypeId: unique symbol;
1209
- /**
1210
- * A dependency layer represents a reusable, composable unit of dependency registrations.
1211
- * Layers allow you to organize your dependency injection setup into logical groups
1212
- * that can be combined and reused across different contexts.
1213
- *
1214
- * ## Type Variance
1215
- *
1216
- * The Layer interface uses TypeScript's variance annotations to enable safe substitutability:
1217
- *
1218
- * ### TRequires (covariant)
1219
- * A layer requiring fewer dependencies can substitute one requiring more:
1220
- * - `Layer<never, X>` can be used where `Layer<A | B, X>` is expected
1221
- * - Intuition: A service that needs nothing is more flexible than one that needs specific deps
1222
- *
1223
- * ### TProvides (contravariant)
1224
- * A layer providing more services can substitute one providing fewer:
1225
- * - `Layer<X, A | B>` can be used where `Layer<X, A>` is expected
1226
- * - Intuition: A service that gives you extra things is compatible with expecting fewer things
1227
- *
1228
- * @template TRequires - The union of tags this layer requires to be satisfied by other layers
1229
- * @template TProvides - The union of tags this layer provides/registers
1230
- *
1231
- * @example Basic layer usage
1232
- * ```typescript
1233
- * import { layer, Tag, container } from 'sandly';
1234
- *
1235
- * class DatabaseService extends Tag.Service('DatabaseService') {
1236
- * query() { return 'data'; }
1237
- * }
1238
- *
1239
- * // Create a layer that provides DatabaseService
1240
- * const databaseLayer = layer<never, typeof DatabaseService>((container) =>
1241
- * container.register(DatabaseService, () => new DatabaseService())
1242
- * );
1243
- *
1244
- * // Apply the layer to a container
1245
- * const container = Container.empty();
1246
- * const finalContainer = databaseLayer.register(c);
1247
- *
1248
- * const db = await finalContainer.resolve(DatabaseService);
1249
- * ```
1250
- *
1251
- * @example Layer composition with variance
1064
+ * @example Layer composition with variance
1252
1065
  * ```typescript
1253
1066
  * // Layer that requires DatabaseService and provides UserService
1254
1067
  * const userLayer = layer<typeof DatabaseService, typeof UserService>((container) =>
@@ -1296,7 +1109,7 @@ interface Layer<TRequires extends AnyTag, TProvides extends AnyTag> {
1296
1109
  * // Enhanced container has both ExistingService and myLayer's provisions
1297
1110
  * ```
1298
1111
  */
1299
- register: <TContainer extends AnyTag>(container: IContainer<TRequires | TContainer>) => IContainer<TRequires | TContainer | TProvides>;
1112
+ register: <TContainer extends IContainer<TRequires>>(container: TContainer) => WithContainerTags<TContainer, ContainerTags<TContainer> | TProvides>;
1300
1113
  /**
1301
1114
  * Provides a dependency layer to this layer, creating a pipeline where the dependency layer's
1302
1115
  * provisions satisfy this layer's requirements. This creates a dependency flow from dependency → this.
@@ -1576,76 +1389,360 @@ declare const Layer: {
1576
1389
  */
1577
1390
  merge<TRequires1 extends AnyTag, TProvides1 extends AnyTag, TRequires2 extends AnyTag, TProvides2 extends AnyTag>(layer1: Layer<TRequires1, TProvides1>, layer2: Layer<TRequires2, TProvides2>): Layer<TRequires1 | TRequires2, TProvides1 | TProvides2>;
1578
1391
  };
1392
+
1579
1393
  //#endregion
1580
- //#region src/scoped-container.d.ts
1581
- type Scope = string | symbol;
1582
- declare class ScopedContainer<TReg extends AnyTag> extends Container<TReg> {
1583
- readonly scope: Scope;
1584
- private parent;
1585
- private readonly children;
1586
- protected constructor(parent: IContainer<TReg> | null, scope: Scope);
1394
+ //#region src/constant.d.ts
1395
+ /**
1396
+ * Creates a layer that provides a constant value for a given tag.
1397
+ *
1398
+ * @param tag - The value tag to provide
1399
+ * @param constantValue - The constant value to provide
1400
+ * @returns A layer with no dependencies that provides the constant value
1401
+ *
1402
+ * @example
1403
+ * ```typescript
1404
+ * const ApiKey = Tag.of('ApiKey')<string>();
1405
+ * const DatabaseUrl = Tag.of('DatabaseUrl')<string>();
1406
+ *
1407
+ * const apiKey = constant(ApiKey, 'my-secret-key');
1408
+ * const dbUrl = constant(DatabaseUrl, 'postgresql://localhost:5432/myapp');
1409
+ *
1410
+ * const config = Layer.merge(apiKey, dbUrl);
1411
+ * ```
1412
+ */
1413
+ declare function constant<T extends ValueTag<TagId, unknown>>(tag: T, constantValue: TagType<T>): Layer<never, T>;
1414
+
1415
+ //#endregion
1416
+ //#region src/dependency.d.ts
1417
+ /**
1418
+ * Extracts a union type from a tuple of tags.
1419
+ * Returns `never` for empty arrays.
1420
+ * @internal
1421
+ */
1422
+ type TagsToUnion<T extends readonly AnyTag[]> = T[number];
1423
+ /**
1424
+ * Creates a layer that provides a single dependency with inferred requirements.
1425
+ *
1426
+ * This is a simplified alternative to `layer()` for the common case of defining
1427
+ * a single dependency. Unlike `service()` and `autoService()`, this works with
1428
+ * any tag type (ServiceTag or ValueTag) and doesn't require extending `Tag.Service()`.
1429
+ *
1430
+ * Requirements are passed as an optional array of tags, allowing TypeScript to infer
1431
+ * both the tag type and the requirements automatically - no explicit type
1432
+ * parameters needed.
1433
+ *
1434
+ * @param tag - The tag (ServiceTag or ValueTag) that identifies this dependency
1435
+ * @param spec - Factory function or lifecycle object for creating the dependency
1436
+ * @param requirements - Optional array of dependency tags this dependency requires (defaults to [])
1437
+ * @returns A layer that requires the specified dependencies and provides the tag
1438
+ *
1439
+ * @example Simple dependency without requirements
1440
+ * ```typescript
1441
+ * const Config = Tag.of('Config')<{ apiUrl: string }>();
1442
+ *
1443
+ * // No requirements - can omit the array
1444
+ * const configDep = dependency(Config, () => ({
1445
+ * apiUrl: process.env.API_URL!
1446
+ * }));
1447
+ * ```
1448
+ *
1449
+ * @example Dependency with requirements
1450
+ * ```typescript
1451
+ * const database = dependency(
1452
+ * Database,
1453
+ * async (ctx) => {
1454
+ * const config = await ctx.resolve(Config);
1455
+ * const logger = await ctx.resolve(Logger);
1456
+ * logger.info('Creating database connection');
1457
+ * return createDb(config.DATABASE);
1458
+ * },
1459
+ * [Config, Logger]
1460
+ * );
1461
+ * ```
1462
+ *
1463
+ * @example Dependency with lifecycle (create + cleanup)
1464
+ * ```typescript
1465
+ * const database = dependency(
1466
+ * Database,
1467
+ * {
1468
+ * create: async (ctx) => {
1469
+ * const config = await ctx.resolve(Config);
1470
+ * const logger = await ctx.resolve(Logger);
1471
+ * logger.info('Creating database connection');
1472
+ * return await createDb(config.DATABASE);
1473
+ * },
1474
+ * cleanup: async (db) => {
1475
+ * await disconnectDb(db);
1476
+ * },
1477
+ * },
1478
+ * [Config, Logger]
1479
+ * );
1480
+ * ```
1481
+ *
1482
+ * @example Comparison with layer()
1483
+ * ```typescript
1484
+ * // Using layer() - verbose, requires explicit type parameters
1485
+ * const database = layer<typeof Config | typeof Logger, typeof Database>(
1486
+ * (container) =>
1487
+ * container.register(Database, async (ctx) => {
1488
+ * const config = await ctx.resolve(Config);
1489
+ * return createDb(config.DATABASE);
1490
+ * })
1491
+ * );
1492
+ *
1493
+ * // Using dependency() - cleaner, fully inferred types
1494
+ * const database = dependency(
1495
+ * Database,
1496
+ * async (ctx) => {
1497
+ * const config = await ctx.resolve(Config);
1498
+ * return createDb(config.DATABASE);
1499
+ * },
1500
+ * [Config, Logger]
1501
+ * );
1502
+ * ```
1503
+ */
1504
+ declare function dependency<TTag extends AnyTag, TRequirements extends readonly AnyTag[] = []>(tag: TTag, spec: DependencySpec<TTag, TagsToUnion<TRequirements>>, requirements?: TRequirements): Layer<TagsToUnion<TRequirements>, TTag>;
1505
+
1506
+ //#endregion
1507
+ //#region src/errors.d.ts
1508
+ type ErrorDump = {
1509
+ name: string;
1510
+ message: string;
1511
+ stack?: string;
1512
+ detail: Record<string, unknown>;
1513
+ cause?: unknown;
1514
+ };
1515
+ type SandlyErrorOptions = {
1516
+ cause?: unknown;
1517
+ detail?: Record<string, unknown>;
1518
+ };
1519
+ /**
1520
+ * Base error class for all library errors.
1521
+ *
1522
+ * This extends the native Error class to provide consistent error handling
1523
+ * and structured error information across the library.
1524
+ *
1525
+ * @example Catching library errors
1526
+ * ```typescript
1527
+ * try {
1528
+ * await container.resolve(SomeService);
1529
+ * } catch (error) {
1530
+ * if (error instanceof SandlyError) {
1531
+ * console.error('DI Error:', error.message);
1532
+ * console.error('Details:', error.detail);
1533
+ * }
1534
+ * }
1535
+ * ```
1536
+ */
1537
+ declare class SandlyError extends Error {
1538
+ detail: Record<string, unknown> | undefined;
1539
+ constructor(message: string, {
1540
+ cause,
1541
+ detail
1542
+ }?: SandlyErrorOptions);
1543
+ static ensure(error: unknown): SandlyError;
1544
+ dump(): ErrorDump;
1545
+ dumps(): string;
1587
1546
  /**
1588
- * Creates a new empty scoped container instance.
1589
- * @param scope - The scope identifier for this container
1590
- * @returns A new empty ScopedContainer instance with no registered dependencies
1547
+ * Recursively extract cause chain from any Error.
1548
+ * Handles both AppError (with dump()) and plain Errors (with cause property).
1591
1549
  */
1592
- static empty(scope: Scope): ScopedContainer<never>;
1550
+ private dumpCause;
1551
+ }
1552
+ /**
1553
+ * Error thrown when attempting to register a dependency that has already been instantiated.
1554
+ *
1555
+ * This error occurs when calling `container.register()` for a tag that has already been instantiated.
1556
+ * Registration must happen before any instantiation occurs, as cached instances would still be used
1557
+ * by existing dependencies.
1558
+ */
1559
+ declare class DependencyAlreadyInstantiatedError extends SandlyError {}
1560
+ /**
1561
+ * Error thrown when attempting to use a container that has been destroyed.
1562
+ *
1563
+ * This error occurs when calling `container.resolve()`, `container.register()`, or `container.destroy()`
1564
+ * on a container that has already been destroyed. It indicates a programming error where the container
1565
+ * is being used after it has been destroyed.
1566
+ */
1567
+ declare class ContainerDestroyedError extends SandlyError {}
1568
+ /**
1569
+ * Error thrown when attempting to retrieve a dependency that hasn't been registered.
1570
+ *
1571
+ * This error occurs when calling `container.resolve(Tag)` for a tag that was never
1572
+ * registered via `container.register()`. It indicates a programming error where
1573
+ * the dependency setup is incomplete.
1574
+ *
1575
+ * @example
1576
+ * ```typescript
1577
+ * const container = Container.empty(); // Empty container
1578
+ *
1579
+ * try {
1580
+ * await c.resolve(UnregisteredService); // This will throw
1581
+ * } catch (error) {
1582
+ * if (error instanceof UnknownDependencyError) {
1583
+ * console.error('Missing dependency:', error.message);
1584
+ * }
1585
+ * }
1586
+ * ```
1587
+ */
1588
+ declare class UnknownDependencyError extends SandlyError {
1593
1589
  /**
1594
- * Registers a dependency in the scoped container.
1590
+ * @internal
1591
+ * Creates an UnknownDependencyError for the given tag.
1595
1592
  *
1596
- * Overrides the base implementation to return ScopedContainer type
1597
- * for proper method chaining support.
1593
+ * @param tag - The dependency tag that wasn't found
1598
1594
  */
1599
- register<T extends AnyTag>(tag: T, spec: DependencySpec<T, TReg>): ScopedContainer<TReg | T>;
1595
+ constructor(tag: AnyTag);
1596
+ }
1597
+ /**
1598
+ * Error thrown when a circular dependency is detected during dependency resolution.
1599
+ *
1600
+ * This occurs when service A depends on service B, which depends on service A (directly
1601
+ * or through a chain of dependencies). The error includes the full dependency chain
1602
+ * to help identify the circular reference.
1603
+ *
1604
+ * @example Circular dependency scenario
1605
+ * ```typescript
1606
+ * class ServiceA extends Tag.Service('ServiceA') {}
1607
+ * class ServiceB extends Tag.Service('ServiceB') {}
1608
+ *
1609
+ * const container = Container.empty()
1610
+ * .register(ServiceA, async (ctx) =>
1611
+ * new ServiceA(await ctx.resolve(ServiceB)) // Depends on B
1612
+ * )
1613
+ * .register(ServiceB, async (ctx) =>
1614
+ * new ServiceB(await ctx.resolve(ServiceA)) // Depends on A - CIRCULAR!
1615
+ * );
1616
+ *
1617
+ * try {
1618
+ * await c.resolve(ServiceA);
1619
+ * } catch (error) {
1620
+ * if (error instanceof CircularDependencyError) {
1621
+ * console.error('Circular dependency:', error.message);
1622
+ * // Output: "Circular dependency detected for ServiceA: ServiceA -> ServiceB -> ServiceA"
1623
+ * }
1624
+ * }
1625
+ * ```
1626
+ */
1627
+ declare class CircularDependencyError extends SandlyError {
1600
1628
  /**
1601
- * Checks if a dependency has been registered in this scope or any parent scope.
1629
+ * @internal
1630
+ * Creates a CircularDependencyError with the dependency chain information.
1602
1631
  *
1603
- * This method checks the current scope first, then walks up the parent chain.
1604
- * Returns true if the dependency has been registered somewhere in the scope hierarchy.
1632
+ * @param tag - The tag where the circular dependency was detected
1633
+ * @param dependencyChain - The chain of dependencies that led to the circular reference
1605
1634
  */
1606
- has(tag: AnyTag): boolean;
1635
+ constructor(tag: AnyTag, dependencyChain: AnyTag[]);
1636
+ }
1637
+ /**
1638
+ * Error thrown when a dependency factory function throws an error during instantiation.
1639
+ *
1640
+ * This wraps the original error with additional context about which dependency
1641
+ * failed to be created. The original error is preserved as the `cause` property.
1642
+ *
1643
+ * When dependencies are nested (A depends on B depends on C), and C's factory throws,
1644
+ * you get nested DependencyCreationErrors. Use `getRootCause()` to get the original error.
1645
+ *
1646
+ * @example Factory throwing error
1647
+ * ```typescript
1648
+ * class DatabaseService extends Tag.Service('DatabaseService') {}
1649
+ *
1650
+ * const container = Container.empty().register(DatabaseService, () => {
1651
+ * throw new Error('Database connection failed');
1652
+ * });
1653
+ *
1654
+ * try {
1655
+ * await c.resolve(DatabaseService);
1656
+ * } catch (error) {
1657
+ * if (error instanceof DependencyCreationError) {
1658
+ * console.error('Failed to create:', error.message);
1659
+ * console.error('Original error:', error.cause);
1660
+ * }
1661
+ * }
1662
+ * ```
1663
+ *
1664
+ * @example Getting root cause from nested errors
1665
+ * ```typescript
1666
+ * // ServiceA -> ServiceB -> ServiceC (ServiceC throws)
1667
+ * try {
1668
+ * await container.resolve(ServiceA);
1669
+ * } catch (error) {
1670
+ * if (error instanceof DependencyCreationError) {
1671
+ * console.error('Top-level error:', error.message); // "Error creating instance of ServiceA"
1672
+ * const rootCause = error.getRootCause();
1673
+ * console.error('Root cause:', rootCause); // Original error from ServiceC
1674
+ * }
1675
+ * }
1676
+ * ```
1677
+ */
1678
+ declare class DependencyCreationError extends SandlyError {
1607
1679
  /**
1608
- * Checks if a dependency has been instantiated in this scope or any parent scope.
1680
+ * @internal
1681
+ * Creates a DependencyCreationError wrapping the original factory error.
1609
1682
  *
1610
- * This method checks the current scope first, then walks up the parent chain.
1611
- * Returns true if the dependency has been instantiated somewhere in the scope hierarchy.
1683
+ * @param tag - The tag of the dependency that failed to be created
1684
+ * @param error - The original error thrown by the factory function
1612
1685
  */
1613
- exists(tag: AnyTag): boolean;
1686
+ constructor(tag: AnyTag, error: unknown);
1614
1687
  /**
1615
- * Retrieves a dependency instance, resolving from the current scope or parent scopes.
1688
+ * Traverses the error chain to find the root cause error.
1616
1689
  *
1617
- * Resolution strategy:
1618
- * 1. Check cache in current scope
1619
- * 2. Check if factory exists in current scope - if so, create instance here
1620
- * 3. Otherwise, delegate to parent scope
1621
- * 4. If no parent or parent doesn't have it, throw UnknownDependencyError
1690
+ * When dependencies are nested, each level wraps the error in a DependencyCreationError.
1691
+ * This method unwraps all the layers to get to the original error that started the failure.
1692
+ *
1693
+ * @returns The root cause error (not a DependencyCreationError unless that's the only error)
1694
+ *
1695
+ * @example
1696
+ * ```typescript
1697
+ * try {
1698
+ * await container.resolve(UserService);
1699
+ * } catch (error) {
1700
+ * if (error instanceof DependencyCreationError) {
1701
+ * const rootCause = error.getRootCause();
1702
+ * console.error('Root cause:', rootCause);
1703
+ * }
1704
+ * }
1705
+ * ```
1622
1706
  */
1623
- resolve<T extends TReg>(tag: T): Promise<TagType<T>>;
1707
+ getRootCause(): unknown;
1708
+ }
1709
+ /**
1710
+ * Error thrown when one or more finalizers fail during container destruction.
1711
+ *
1712
+ * This error aggregates multiple finalizer failures that occurred during
1713
+ * `container.destroy()`. Even if some finalizers fail, the container cleanup
1714
+ * process continues and this error contains details of all failures.
1715
+ *
1716
+ * @example Handling finalization errors
1717
+ * ```typescript
1718
+ * try {
1719
+ * await container.destroy();
1720
+ * } catch (error) {
1721
+ * if (error instanceof DependencyFinalizationError) {
1722
+ * console.error('Some finalizers failed');
1723
+ * console.error('Error details:', error.detail.errors);
1724
+ * }
1725
+ * }
1726
+ * ```
1727
+ */
1728
+ declare class DependencyFinalizationError extends SandlyError {
1729
+ private readonly errors;
1624
1730
  /**
1625
- * Internal resolution with delegation logic for scoped containers.
1626
1731
  * @internal
1627
- */
1628
- protected resolveInternal<T extends TReg>(tag: T, chain: AnyTag[]): Promise<TagType<T>>;
1629
- /**
1630
- * Destroys this scoped container and its children, preserving the container structure for reuse.
1631
- *
1632
- * This method ensures proper cleanup order while maintaining reusability:
1633
- * 1. Destroys all child scopes first (they may depend on parent scope dependencies)
1634
- * 2. Then calls finalizers for dependencies created in this scope
1635
- * 3. Clears only instance caches - preserves factories, finalizers, and child structure
1732
+ * Creates a DependencyFinalizationError aggregating multiple finalizer failures.
1636
1733
  *
1637
- * Child destruction happens first to ensure dependencies don't get cleaned up
1638
- * before their dependents.
1734
+ * @param errors - Array of errors thrown by individual finalizers
1639
1735
  */
1640
- destroy(): Promise<void>;
1736
+ constructor(errors: unknown[]);
1641
1737
  /**
1642
- * Creates a child scoped container.
1738
+ * Returns the root causes of the errors that occurred during finalization.
1643
1739
  *
1644
- * Child containers inherit access to parent dependencies but maintain
1645
- * their own scope for new registrations and instance caching.
1740
+ * @returns An array of the errors that occurred during finalization.
1741
+ * You can expect at least one error in the array.
1646
1742
  */
1647
- child(scope: Scope): ScopedContainer<TReg>;
1743
+ getRootCauses(): unknown[];
1648
1744
  }
1745
+
1649
1746
  //#endregion
1650
1747
  //#region src/service.d.ts
1651
1748
  /**
@@ -1654,6 +1751,13 @@ declare class ScopedContainer<TReg extends AnyTag> extends Container<TReg> {
1654
1751
  * @internal
1655
1752
  */
1656
1753
  type ConstructorParams<T extends ServiceTag<TagId, unknown>> = T extends (new (...args: infer A) => unknown) ? A : never;
1754
+ /**
1755
+ * Helper to normalize a tag type.
1756
+ * For ServiceTags, this strips away extra static properties of the class constructor,
1757
+ * reducing it to the canonical ServiceTag<Id, Instance> form.
1758
+ * @internal
1759
+ */
1760
+ type CanonicalTag<T extends AnyTag> = T extends ServiceTag<infer Id, infer Instance> ? ServiceTag<Id, Instance> : T;
1657
1761
  /**
1658
1762
  * Extracts only dependency tags from a constructor parameter list.
1659
1763
  * Filters out non‑DI parameters.
@@ -1728,7 +1832,7 @@ type ServiceDepsTuple<T extends ServiceTag<TagId, unknown>> = InferConstructorDe
1728
1832
  * );
1729
1833
  * ```
1730
1834
  */
1731
- declare function service<T extends ServiceTag<TagId, unknown>>(tag: T, spec: DependencySpec<T, ServiceDependencies<T>>): Layer<ServiceDependencies<T>, T>;
1835
+ declare function service<T extends ServiceTag<TagId, unknown>>(tag: T, spec: DependencySpec<T, ServiceDependencies<T>>): Layer<ServiceDependencies<T>, CanonicalTag<T>>;
1732
1836
  /**
1733
1837
  * Specification for autoService.
1734
1838
  * Can be either a tuple of constructor parameters or an object with dependencies and finalizer.
@@ -1835,27 +1939,7 @@ type AutoServiceSpec<T extends ServiceTag<TagId, unknown>> = ServiceDepsTuple<T>
1835
1939
  * );
1836
1940
  * ```
1837
1941
  */
1838
- declare function autoService<T extends ServiceTag<TagId, unknown>>(tag: T, spec: AutoServiceSpec<T>): Layer<ServiceDependencies<T>, T>;
1839
- //#endregion
1840
- //#region src/value.d.ts
1841
- /**
1842
- * Creates a layer that provides a constant value for a given tag.
1843
- *
1844
- * @param tag - The value tag to provide
1845
- * @param constantValue - The constant value to provide
1846
- * @returns A layer with no dependencies that provides the constant value
1847
- *
1848
- * @example
1849
- * ```typescript
1850
- * const ApiKey = Tag.of('ApiKey')<string>();
1851
- * const DatabaseUrl = Tag.of('DatabaseUrl')<string>();
1852
- *
1853
- * const apiKey = value(ApiKey, 'my-secret-key');
1854
- * const dbUrl = value(DatabaseUrl, 'postgresql://localhost:5432/myapp');
1855
- *
1856
- * const config = Layer.merge(apiKey, dbUrl);
1857
- * ```
1858
- */
1859
- declare function value<T extends ValueTag<TagId, unknown>>(tag: T, constantValue: TagType<T>): Layer<never, T>;
1942
+ declare function autoService<T extends ServiceTag<TagId, unknown>>(tag: T, spec: AutoServiceSpec<T>): Layer<ServiceDependencies<T>, CanonicalTag<T>>;
1943
+
1860
1944
  //#endregion
1861
- export { type AnyLayer, type AnyTag, CircularDependencyError, Container, ContainerDestroyedError, DependencyAlreadyInstantiatedError, DependencyCreationError, DependencyFinalizationError, type DependencyLifecycle, type DependencySpec, type Factory, type Finalizer, type IContainer, type Inject, InjectSource, Layer, type PromiseOrValue, type ResolutionContext, SandlyError, type Scope, ScopedContainer, type ServiceDependencies, type ServiceDepsTuple, type ServiceTag, Tag, type TagType, UnknownDependencyError, type ValueTag, autoService, layer, service, value };
1945
+ export { AnyLayer, AnyTag, CircularDependencyError, Container, ContainerDestroyedError, ContainerTags, DependencyAlreadyInstantiatedError, DependencyCreationError, DependencyFinalizationError, DependencyLifecycle, DependencySpec, Factory, Finalizer, IContainer, Inject, InjectSource, Layer, PromiseOrValue, ResolutionContext, SandlyError, Scope, ScopedContainer, ServiceDependencies, ServiceDepsTuple, ServiceTag, Tag, TagType, UnknownDependencyError, ValueTag, autoService, constant, dependency, layer, service };