Skip to main content

API

work in progress

Docs

Tokens

Containers

Containers are an important unit. If you replace them, users will be notified. In React it happens automatically

Events

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

API documentation JS / TS

makeRoot Setting app root

import { makeRoot } from "../../library.root-container"
export function getMainMockAppContainer() {
return makeRoot().add({ kitchen: () => new Kitchen(/* deps */) })
}

items getter

let appRoot = getMainPizzaAppContainer()
let kitchen = await appRoot.items.kitchen
kitchen.oven.pizzaCapacity // 4

getContainerSet

getContainerSetNew

upsert

delete

When containers are updated React is updated too via hooks

Disposing

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

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

disposeAll(): Promise<void>

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

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. :)")

dispose(token:): Promise<void>

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>

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,
}))
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)

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()

Notes on disposing

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