import { AsyncLocalStorage } from "node:async_hooks"; import type { ComposeSpecification } from "../schema/docker.d"; import { getProject, throwWhen } from "./shared"; import { readCompose, resolveComposeFile } from "./yaml"; type AppContext = { project: string; cwd: string; path(): Promise; compose(): Promise; }; const appContext = new AsyncLocalStorage(); export function provideContext(fn: () => T): T { const cwd = process.cwd(); const path = createCachedGetter("composeFile", () => resolveComposeFile(cwd)); return appContext.run( { project: getProject(), cwd, path, compose: createCachedGetter("compose", () => path() .then( throwWhen( (path): path is undefined => path === undefined, "Compose file cannot be found", ), ) .then(readCompose), ), }, fn, ); } export function ctx() { const store = appContext.getStore(); if (!store) throw new Error("Not in context!"); return store; } export function createCachedGetter( _key: string, fn: () => Promise, ): () => Promise { let cached: Promise | undefined; return () => { cached ??= fn().catch((error) => { cached = undefined; throw error; }); return cached; }; }