latest
esroyo/bottlexsA dependency injection nano container
This package works with Node.js, Deno, BrowsersIt is unknown whether this package works with Cloudflare Workers, Bun
JSR Score
100%
Published
3 months ago (0.6.3)
Bottle XS 👕
A dependency injection micro nano container that draws inspiration from BottleJS (for concepts) and Zod (for type inference and composability).
Features
- ✨ Type inference of the services just works
- ⚡ < 500 bytes (when minified and gzipped)
- 🍺 Terse lazy-loading access to the services as in BottleJS
- 🏭 Supports (by default) the factory pattern from BottleJS
- 🔧 Supports the service and serviceFactory patterns from BottleJS via additional pure helpers (tree-shakeable)
- ♻️ Possiblity to reset the providers to re-instantiate a service
- 🐾 Tracks dependencies: reseting a provider will reset all the dependents (opt-out possible)
- 🔒 Favors immutability by taking the providers at construction time
- 🔌 Favors composability by inheriting from other containers
Examples
Basic usage
import { Bottle } from 'jsr:@esroyo/bottlexs'; interface BarleyLike { water: Water; } interface HopsLike { water: Water; } class Water {} class Nordal implements BarleyLike { constructor(public water: Water) {} } class Hallertau implements HopsLike { constructor(public water: Water) {} } class Beer { public brand = 'San Miguel'; constructor( public barley: BarleyLike, public hops: HopsLike, public water: Water, ) {} } // Services are defined with a name `string | symbol` and a `Factory` function. // A factory function receives an object as an argument (the container), and // should return the constructed service. // // The accurate typing of the object expected by each Factory is important. // The Factory declaration is used to typecheck that all expected dependencies // are effectively available at the end of the day. const providers = { barley: (container: { water: Water }) => new Nordal(container.water), hops: (container: { water: Water }) => new Hallertau(container.water), water: () => new Water(), beer: ( container: { barley: BarleyLike; hops: HopsLike; water: Water }, ) => new Beer(container.barley, container.hops, container.water), }; const bottle = new Bottle(providers); // inferred type type Services = typeof bottle.container; // type SomeServices = { // barley: Nordal; // hops: Hallertau; // water: Water; // beer: Beer; // } console.log(bottle.container.beer.brand); // "San Miguel" console.log( bottle.container.water === bottle.container.beer.water, ); // true
Inherit/compose in multiple bottle instances
import { Bottle } from 'jsr:@esroyo/bottlexs'; class Water {} class Barley { constructor(public water: Water) {} } class Hops { constructor(public water: Water) {} } class Beer { public brand = 'San Miguel'; constructor( public barley: Barley, public hops: Hops, public water: Water, ) {} } const someProviders = { barley: ({ water }: { water: Water }) => new Barley(water), hops: ({ water }: { water: Water }) => new Hops(water), water: () => new Water(), }; const someBottle = new Bottle(someProviders); // inferred type type SomeServices = typeof someBottle.container; // type SomeServices = { // barley: Barley; // hops: Hops; // water: Water; // } const someOtherProviders = { now: () => Date.now, beer: ( { barley, hops, water }: { barley: Barley; hops: Hops; water: Water }, ) => new Beer(barley, hops, water), }; const otherBottle = new Bottle(someOtherProviders, someBottle); // inferred type type OtherServices = typeof otherBottle.container; // type OtherServices = { // now: () => number; // beer: Beer; // barley: Barley; // hops: Hops; // water: Water; // } console.log(otherBottle.container.beer.brand); // "San Miguel" console.log( someBottle.container.water === otherBottle.container.beer.water, ); // true
Use the service
helper with constructors
import { Bottle, service } from 'jsr:@esroyo/bottlexs'; class Water {} class Barley { constructor(public water: Water) {} } class Hops { constructor(public water: Water) {} } const providers = { // The default provider pattern is the Factory pattern: // receives the container as parameter and returns an instance barley: (container: { water: Water }) => new Barley(container.water), water: () => new Water(), // It is possible to use the alternative `service` helper: // takes in a Constructor, and a list of services to be resolved // and passed as arguments to the [[Constructor]] call hops: service(Hops, ['water'] as const), }; const bottle = new Bottle(providers); // inferred type type Services = typeof bottle.container; // type SomeServices = { // barley: Barley; // hops: Hops; // water: Water; // }
Add Package
deno add jsr:@esroyo/bottlexs
Import symbol
import * as _esroyo_bottlexs from "@esroyo/bottlexs";
---- OR ----
Import directly with a jsr specifier
import * as _esroyo_bottlexs from "jsr:@esroyo/bottlexs";
Add Package
npx jsr add @esroyo/bottlexs
Import symbol
import * as _esroyo_bottlexs from "@esroyo/bottlexs";
Add Package
yarn dlx jsr add @esroyo/bottlexs
Import symbol
import * as _esroyo_bottlexs from "@esroyo/bottlexs";
Add Package
pnpm dlx jsr add @esroyo/bottlexs
Import symbol
import * as _esroyo_bottlexs from "@esroyo/bottlexs";
Add Package
bunx jsr add @esroyo/bottlexs
Import symbol
import * as _esroyo_bottlexs from "@esroyo/bottlexs";