summaryrefslogtreecommitdiffhomepage
path: root/js/src/app/app.ts
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/app/app.ts')
-rw-r--r--js/src/app/app.ts78
1 files changed, 78 insertions, 0 deletions
diff --git a/js/src/app/app.ts b/js/src/app/app.ts
new file mode 100644
index 000000000..0c6260bc7
--- /dev/null
+++ b/js/src/app/app.ts
@@ -0,0 +1,78 @@
+import fs from "fs/promises";
+import { AppPath } from "./path";
+import { Log } from "../util/log";
+import { Context } from "../util/context";
+
+export namespace App {
+ const log = Log.create({ service: "app" });
+
+ export type Info = Awaited<ReturnType<typeof create>>;
+
+ const ctx = Context.create<Info>("app");
+
+ async function create(input: { directory: string }) {
+ const dataDir = AppPath.data(input.directory);
+ await fs.mkdir(dataDir, { recursive: true });
+ await Log.file(input.directory);
+
+ log.info("created", { path: dataDir });
+
+ const services = new Map<
+ any,
+ {
+ state: any;
+ shutdown?: (input: any) => Promise<void>;
+ }
+ >();
+
+ const result = {
+ get services() {
+ return services;
+ },
+ get root() {
+ return input.directory;
+ },
+ };
+
+ return result;
+ }
+
+ export function state<State>(
+ key: any,
+ init: (app: Info) => State,
+ shutdown?: (state: Awaited<State>) => Promise<void>,
+ ) {
+ return () => {
+ const app = ctx.use();
+ const services = app.services;
+ if (!services.has(key)) {
+ log.info("registering service", { name: key });
+ services.set(key, {
+ state: init(app),
+ shutdown: shutdown,
+ });
+ }
+ return services.get(key)?.state as State;
+ };
+ }
+
+ export async function use() {
+ return ctx.use();
+ }
+
+ export async function provide<T extends (app: Info) => any>(
+ input: { directory: string },
+ cb: T,
+ ) {
+ const app = await create(input);
+
+ return ctx.provide(app, async () => {
+ const result = await cb(app);
+ for (const [key, entry] of app.services.entries()) {
+ log.info("shutdown", { name: key });
+ await entry.shutdown?.(await entry.state);
+ }
+ return result;
+ });
+ }
+}