Skip to content

API Reference

work in progress

  • container - is a collection of items
  • item - is a value you want to store in a container, like a class instance
  • token - is a unique string identifier for an item

In a container, if you update or replace an item, users will be notified.

In React it happens automatically

const kitchenApp = new RootContainer((ctx) => ({
// you can use tokens (`oven`, `kitchen`) here and later on
oven: async () => ovenContainer(),
kitchen: async () => kitchenContainer(await ctx.oven()),
}))
kitchenApp.on("containerCreated", (event) => {
console.log(`event: 'containerCreated' ~~> token: '${event.key}'`)
// `event.container` is also available here
})
kitchenApp.on("containerRequested", (event) => {
console.log(`event: 'containerRequested' ~~> token: '${event.key}' `)
})
kitchenApp.on("containerRemoved", (event) => {
console.log(`event: 'containerRemoved' ~~> token: '${event.key}' `)
})
await kitchenApp.items.kitchen
// event: 'containerRequested' ~~> token: 'kitchen'
// event: 'containerRequested' ~~> token: 'oven'
// event: 'containerCreated' ~~> token: 'oven'
// event: 'containerCreated' ~~> token: 'kitchen'
// Notice how oven was created before kitchen.
// This is because kitchen depends on oven
import { createContainer } from "iti"
export function getMainMockAppContainer() {
return createContainer().add({ kitchen: () => new Kitchen(/* deps */) })
}
let container = getMainPizzaAppContainer()
let kitchen = await container.items.kitchen
kitchen.oven.pizzaCapacity // 4

When containers are updated React is updated too via hooks

You can launch a live playground on Stackblitz or checkout the playground repo with the playground locally

Terminal window
git clone [email protected]:molszanski/iti-playground.git
cd iti-playground
cat src/examples/0.disposing.ts

Runs cleanup functions on provided (created) dependencies.

Returns a Promise that resolves when all disposers of cached resolutions have resolved.

import { createContainer } from "iti"
container = createContainer()
.add({ a: () => "123" })
.addDisposer({ a: () => {} })
container.get("a") === "123" // true
await container.disposeAll() // Promise<void>

Practical example

disposeAll.ts
import { Client } from "pg"
import { createContainer } from "iti"
const container = createContainer()
.add(() => ({
dbConnection: async () => {
const pg = new Client(process.env["DB_CONNECTION_URL"])
await pg.connect()
return pg
},
}))
.addDisposer({
dbConnection: (dbConnection) => dbConnection.end(),
})
const db = await container.get("dbConnection")
await db.query("...")
// Later..
await container.disposeAll()
console.log("All dependencies disposed, you can exit now. :)")

Disposes individual container

class A {}
class B {}
const container = createContainer()
.add({
a: () => new A(),
b: () => new B(),
})
.addDisposer({
a: (a) => console.log("disposing a", a),
b: (a) => throw new Error("this should not be called"),
})
container.get("a")
// will print `disposing a A {}`
container.dispose("a")

addDisposer(disposer: fn | {}): Promise<void>

Section titled “addDisposer(disposer: fn | {}): Promise<void>”
import { createContainer } from "iti"
class A {
dispose() {}
}
class B {}
container = createContainer().add({
a: () => new A(),
b: () => new B(),
})
// Adding disposer for token `a`
container.addDisposer({ a: () => {} })
// Accessing `A` instance in disposer for token `a`
container.addDisposer({
a: (a: A) => a instanceof A === true,
})
// Accessing ctx in disposer for token `a`
container.addDisposer((ctx) => ({
a: () => ctx.b instanceof B === true,
}))
addDisposer.ts
import { createContainer } from "iti"
import { Client } from "pg"
class A {}
createContainer()
.add({
db: async () => {
const pg = new Client(process.env["DB_CONNECTION_URL"])
await pg.connect()
return pg
},
})
.add((ctx) => ({
a: () => new A(),
b: async () => {
const db = await ctx.db
db.query("SELECT 1")
},
}))
// ↓ `ctx` to access any other dependency
.addDisposer((ctx) => ({
// ↓ `db` is a resolved value of a `DB`
db: (db) => {
console.log(ctx.a)
return db.disconnect()
},
}))
class B {}
const container = createContainer()
.add({
a: () => new A(),
b: () => new B(),
})
.addDisposer({
a: (a) => console.log("disposing a", a),
})
container.dispose("a")

.on('containerDisposed', ({ key: string }) => void)

Section titled “.on('containerDisposed', ({ key: string }) => void)”
import { createContainer } from "iti"
container = createContainer()
.add({ a: () => "123" })
.addDisposer({ a: () => {} })
.on("containerDisposed", (payload) => {
console.log(payload.key) // prints 'a'
})
container.get("a") === "123" // true
await container.disposeAll()

disposal graph Please note that .dispose('token') doesn’t dispose child elements.

This would be risky to implement due to reasons explained in dispose-graph.ts.api.spec.ts.

In a nutshell, due to an async nature of iti we could potentially dispose unrelated dependencies as well.

But there are a few workarounds:

you can create your custom dispose function that will dispose dependencies you care about

import { createContainer } from "iti"
container = createContainer()
.add({
a: () => "A",
b: () => "B",
})
.addDisposer((ctx) => ({
a: () => {
console.log("do something to dispose a: ", ctx.a)
console.log("do something to dispose b: ", ctx.b)
},
}))

you can create multiple containers (createContainer) and dispose them separately

import { createContainer } from "iti"
class A {}
class B {}
class C {}
class D {
constructor(b: B) {}
}
const cont1 = createContainer()
.add({
a: () => new A(),
b: () => new B(),
})
.addDisposer({
a: (a) => console.log("disposing a", a),
b: (b) => console.log("disposing b", b),
})
const cont2 = createContainer().add({
c: () => new C(),
d: () => new D(cont1.get("b")),
})
cont2.get("d") // Creates a new instance of D, which in turn creates a new instance of B
cont1.disposeAll() // Disposes B