Skip to content

chore: release - #8

Merged
borisrakovan merged 1 commit into
mainfrom
changeset-release/main
Nov 8, 2026
Merged

borisrakovan merged 1 commit into
mainfrom
changeset-release/main

Conversation

@borisrakovan

Copy link
Copy Markdown
Owner

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or setup this action to publish automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

sandly@4.0.0

Major Changes

  • c1053aa: Sandly 4: containers are built from one provider per tag, singletons are enforced, and missing dependencies are named in type errors.

    Breaking Changes

    • Building a container (Container.from, ScopedContainer.from, childFrom, layer.apply) applies each layer object once, however many composition paths reach it. Previously a shared layer was re-applied on every path and the last registration silently won.
    • Two different layers providing the same tag throw DuplicateProviderError at build time, naming both definition sites. Bindings of one tag to the same value (Layer.value / Layer.mock, compared with Object.is) count as one provider.
    • ContainerBuilder.add / ScopedContainerBuilder.add throw DuplicateProviderError for a tag already registered in the same builder. A child scope may still shadow a parent's tag.
    • Container.from, ScopedContainer.from and childFrom infer the layer's requirements and check them with a branded type, so a type error names only the missing tags (MissingDependencies<typeof Config>) instead of comparing the full layer types. A generic function that passes its layer parameter on to one of them must type it as RootLayer<TRequires, TProvides> or ChildLayer<TRequires, TProvides, TParentTags> to forward the check.
    • container.use(tag, fn) is renamed to container.useOnce(tag, fn), since it destroys the container afterwards.
    • When the callback (or resolution) in useOnce throws and a finalizer throws too, useOnce rejects with a SuppressedError whose error is the DependencyFinalizationError and whose suppressed is the original error, as await using does. Previously the finalization error replaced the original error.
    • ScopedContainer no longer extends Container; both extend a shared base class, so each declares its own static builder, empty and from. A ScopedContainer is still assignable to a Container type, but scopedContainer instanceof Container is now false. The published type declarations no longer report an error in projects that type-check library files (skipLibCheck: false).
    • The type declarations use AsyncDisposable and Symbol.asyncDispose, which come from @types/node (or Bun's types) or from esnext.disposable in lib (TypeScript 5.2+).

    New

    • layer.override(replacement) replaces providers anywhere in the composition, regardless of nesting depth or position. Throws DuplicateOverrideError for two overrides of one tag and OverrideTargetNotFoundError for an override with nothing to replace.
    • Layer.unused(...tags) satisfies requirements left behind by an override with placeholders that throw UnusedDependencyResolvedError if resolved.
    • Layer.singleton(class, deps, options?) and Layer.singleton(layer) declare a dependency that is built only into a root container. The layer requires Root<typeof X>, which only Container.from and ScopedContainer.from satisfy, so childFrom rejects any layer containing a singleton at compile time (SingletonInChildScope<typeof X>). Building one into a child scope also throws SingletonInChildScopeError.
    • Layer.values([tag, value], ...) binds several tags to values in one layer.
    • Containers implement Symbol.asyncDispose: await using container = Container.from(layer) destroys the container when the scope exits.
    • Exported types: RootLayer, ChildLayer, Root, MissingDependencies, SingletonInChildScope.

    Migration

    • Compositions whose behavior changes now fail at build time instead of changing silently. For each DuplicateProviderError, remove one provider, or keep the intended one with override() (typically a test stub that previously lost to a provider nested in the production layer).
    • Wrappers that pass a generic layer to childFrom add a requirements type parameter and type the layer as ChildLayer<TRequires, TProvides, TParentTags>.
    • Wrappers that take a root layer as Layer<never, T> add a requirements type parameter and type the layer as RootLayer<TRequires, T>, which also accepts singleton layers.
    • Rename container.use(...) calls to container.useOnce(...).
    • If the compiler cannot find AsyncDisposable, install @types/node or add esnext.disposable to lib.

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@borisrakovan
borisrakovan merged commit af2dc19 into main Nov 8, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants