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