- Define your dependency
import { Dependency } from "@carloitaben/di-thingy"
type Random = {
readonly next: () => number
}
const Random = new Dependency<Random>("Random")- Use your dependency by
awaiting it
import { Dependency } from "@carloitaben/di-thingy"
type Random = {
readonly next: () => number
}
const Random = new Dependency<Random>("Random")
async function program() {
const random = await Random
const randomNumber = random.next()
console.log(`random number: ${randomNumber}`)
}- Provide a live implementation
import { Dependency } from "@carloitaben/di-thingy"
type Random = {
readonly next: () => number
}
const Random = new Dependency<Random>("Random")
async function program() {
const random = await Random
const randomNumber = random.next()
console.log(`random number: ${randomNumber}`)
}
const RandomLive = Random.make(() => ({
next: () => Math.random(),
}))- Build a runtime with the dependency implementation
import { Dependency } from "@carloitaben/di-thingy"
type Random = {
readonly next: () => number
}
const Random = new Dependency<Random>("Random")
async function program() {
const random = await Random
const randomNumber = random.next()
console.log(`random number: ${randomNumber}`)
}
const RandomLive = Random.make(() => ({
next: () => Math.random(),
}))
const runtimeLive = new Runtime(RandomLive)
await runtimeLive.run(program) // stdout: random number: 0.8241872233134417- Provide a test implementation
import { Dependency } from "@carloitaben/di-thingy"
type Random = {
readonly next: () => number
}
const Random = new Dependency<Random>("Random")
async function program() {
const random = await Random
const randomNumber = random.next()
console.log(`random number: ${randomNumber}`)
}
const RandomLive = Random.make(() => ({
next: () => Math.random(),
}))
const runtimeLive = new Runtime(RandomLive)
await runtimeLive.run(program) // stdout: random number: 0.8241872233134417
await runtimeLive.run(program) // stdout: random number: 0.3176275913827688
await runtimeLive.run(program) // stdout: random number: 0.6740024767900261
const RandomTest = Random.make(() => ({
next: () => 0.25,
}))
const runnableTest = new Runtime(RandomTest, program)
await runnableTest.run(program) // stdout: random number: 0.25
await runnableTest.run(program) // stdout: random number: 0.25
await runnableTest.run(program) // stdout: random number: 0.25-
Use the test implementation during tests
-
Profit
Instead of typing the dependency manually, you can provide a default implementation and the type will be inferred from it.
import { Dependency } from "@carloitaben/di-thingy"
export const Random = new Dependency("Random", () => ({
next: () => Math.random(),
}))
export const RandomTest = Random.make(() => ({
next: () => 0.25,
}))Since dependencies are simply functions and implementations are thenables, you can use an async function to create dependencies and await other dependencies within it.
import { Dependency } from "@carloitaben/di-thingy"
import { Client, createClient } from "@libsql/client"
import { drizzle } from "drizzle-orm/libsql"
export const Database = new Dependency<Client>("Database")
// Local SQLite file
export const DatabaseTest = Database.make(() =>
createClient({
url: "file:sqlite.db",
}),
)
// External database
export const DatabaseLive = Database.make(() =>
createClient({
url: process.env.DATABASE_URL,
authToken: process.env.DATABASE_AUTH_TOKEN,
}),
)
// Uses the provided Database to build the ORM
export const Drizzle = new Dependency("Drizzle", async () => {
const database = await Database
return drizzle(database)
})In the example above, you can provide cleanup functions for dependency implementations. These functions are guaranteed to run independently of the runnable result.
import { Client, createClient } from "@libsql/client"
import { Dependency, Runtime } from "../../src"
export const Database = new Dependency<Client>("Database")
export const DatabaseTest = Database.make(
() =>
createClient({
url: "file:sqlite.db",
}),
// Close the client after the runnable finishes
(client) => {
client.close()
console.log("SQLite client closed")
},
)
// ...
async function program() {
throw Error("Oops")
}
const runtime = new Runtime(DatabaseTest)
await runtime.run(program) // stdout: SQLite client closedYou can also add finalizers to default dependency implementations.
import { Dependency } from "@carloitaben/di-thingy"
import { createClient } from "@libsql/client"
export const Database = new Dependency(
"Database",
() =>
createClient({
url: "file:sqlite.db",
}),
(client) => client.close(),
)