2025-05-31 18:41:00 +00:00
|
|
|
import fs from "fs/promises"
|
|
|
|
|
import { AppPath } from "./path"
|
|
|
|
|
import { Log } from "../util/log"
|
|
|
|
|
import { Context } from "../util/context"
|
2025-05-18 01:31:42 +00:00
|
|
|
|
|
|
|
|
export namespace App {
|
2025-05-31 18:41:00 +00:00
|
|
|
const log = Log.create({ service: "app" })
|
2025-05-18 01:31:42 +00:00
|
|
|
|
2025-05-31 18:41:00 +00:00
|
|
|
export type Info = Awaited<ReturnType<typeof create>>
|
2025-05-18 01:31:42 +00:00
|
|
|
|
2025-05-31 18:41:00 +00:00
|
|
|
const ctx = Context.create<Info>("app")
|
2025-05-18 01:31:42 +00:00
|
|
|
|
2025-05-21 14:30:39 +00:00
|
|
|
async function create(input: { directory: string }) {
|
2025-05-31 18:41:00 +00:00
|
|
|
const dataDir = AppPath.data(input.directory)
|
|
|
|
|
await fs.mkdir(dataDir, { recursive: true })
|
|
|
|
|
await Log.file(input.directory)
|
2025-05-21 14:30:39 +00:00
|
|
|
|
2025-05-31 18:41:00 +00:00
|
|
|
log.info("created", { path: dataDir })
|
2025-05-18 01:31:42 +00:00
|
|
|
|
2025-05-26 18:52:38 +00:00
|
|
|
const services = new Map<
|
|
|
|
|
any,
|
|
|
|
|
{
|
2025-05-31 18:41:00 +00:00
|
|
|
state: any
|
|
|
|
|
shutdown?: (input: any) => Promise<void>
|
2025-05-26 18:52:38 +00:00
|
|
|
}
|
2025-05-31 18:41:00 +00:00
|
|
|
>()
|
2025-05-18 01:31:42 +00:00
|
|
|
|
2025-05-18 06:43:01 +00:00
|
|
|
const result = {
|
|
|
|
|
get services() {
|
2025-05-31 18:41:00 +00:00
|
|
|
return services
|
2025-05-18 06:43:01 +00:00
|
|
|
},
|
2025-05-18 01:31:42 +00:00
|
|
|
get root() {
|
2025-05-31 18:41:00 +00:00
|
|
|
return input.directory
|
2025-05-18 01:31:42 +00:00
|
|
|
},
|
2025-05-31 18:41:00 +00:00
|
|
|
}
|
2025-05-18 06:43:01 +00:00
|
|
|
|
2025-05-31 18:41:00 +00:00
|
|
|
return result
|
2025-05-18 01:31:42 +00:00
|
|
|
}
|
|
|
|
|
|
2025-05-26 18:52:38 +00:00
|
|
|
export function state<State>(
|
|
|
|
|
key: any,
|
|
|
|
|
init: (app: Info) => State,
|
|
|
|
|
shutdown?: (state: Awaited<State>) => Promise<void>,
|
|
|
|
|
) {
|
2025-05-18 06:43:01 +00:00
|
|
|
return () => {
|
2025-05-31 18:41:00 +00:00
|
|
|
const app = ctx.use()
|
|
|
|
|
const services = app.services
|
2025-05-18 06:43:01 +00:00
|
|
|
if (!services.has(key)) {
|
2025-05-31 18:41:00 +00:00
|
|
|
log.info("registering service", { name: key })
|
2025-05-26 18:52:38 +00:00
|
|
|
services.set(key, {
|
|
|
|
|
state: init(app),
|
|
|
|
|
shutdown: shutdown,
|
2025-05-31 18:41:00 +00:00
|
|
|
})
|
2025-05-18 06:43:01 +00:00
|
|
|
}
|
2025-05-31 18:41:00 +00:00
|
|
|
return services.get(key)?.state as State
|
|
|
|
|
}
|
2025-05-18 01:31:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function use() {
|
2025-05-31 18:41:00 +00:00
|
|
|
return ctx.use()
|
2025-05-18 01:31:42 +00:00
|
|
|
}
|
|
|
|
|
|
2025-05-21 14:30:39 +00:00
|
|
|
export async function provide<T extends (app: Info) => any>(
|
|
|
|
|
input: { directory: string },
|
|
|
|
|
cb: T,
|
|
|
|
|
) {
|
2025-05-31 18:41:00 +00:00
|
|
|
const app = await create(input)
|
2025-05-23 20:20:21 +00:00
|
|
|
|
|
|
|
|
return ctx.provide(app, async () => {
|
2025-05-31 18:41:00 +00:00
|
|
|
const result = await cb(app)
|
2025-05-26 18:52:38 +00:00
|
|
|
for (const [key, entry] of app.services.entries()) {
|
2025-05-31 18:41:00 +00:00
|
|
|
log.info("shutdown", { name: key })
|
|
|
|
|
await entry.shutdown?.(await entry.state)
|
2025-05-26 18:52:38 +00:00
|
|
|
}
|
2025-05-31 18:41:00 +00:00
|
|
|
return result
|
|
|
|
|
})
|
2025-05-21 14:30:39 +00:00
|
|
|
}
|
2025-05-18 01:31:42 +00:00
|
|
|
}
|