Something went wrong. Try again.
Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol.
Something went wrong. Try again.
3.2 kB · 75 lines
TypeScript
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576import { Effect, Schema, type Scope } from 'effect'import type { HttpServerStopResult, StoppableHttpServer } from './shutdown'
export class HostingStartupError extends Schema.TaggedError<HostingStartupError>()('HostingStartupError', { component: Schema.Literals([ 'observability', 'private sites database', 'database', 'revalidation', 'storage', 'cache invalidation', 'http', ]),}) {}
export interface HostingLifecycleDependencies { initializeExporters(): Promise<unknown> shutdownExporters(): Promise<void> closeDatabase(): Promise<void> closePrivateDatabase(): Promise<void> closeRevalidation(): Promise<void> acquireStorage?: Effect.Effect<void, unknown, Scope.Scope> startCleanup(): void stopCleanup(): void startInvalidation(): Promise<void> stopInvalidation(): Promise<void> bootstrap(): Promise<void> createServer(): StoppableHttpServer stopServer(server: StoppableHttpServer): Promise<HttpServerStopResult> logFailure(component: string): void logHttpStop(result: HttpServerStopResult): void}
/** Acquire in dependency order; unwind partial startup and drain HTTP before storage users. */export const acquireHosting = Effect.fn('hosting.acquireHosting')(function* ( dependencies: HostingLifecycleDependencies,): Effect.fn.Return<StoppableHttpServer, HostingStartupError, Scope.Scope> { const startup = <A>(component: HostingStartupError['component'], run: () => Promise<A>) => Effect.tryPromise({ try: run, catch: () => new HostingStartupError({ component }), }) const cleanup = (component: HostingStartupError['component'], run: () => Promise<unknown>) => startup(component, run).pipe( Effect.catchTag('HostingStartupError', () => Effect.sync(() => dependencies.logFailure(component))), Effect.asVoid, ) yield* Effect.acquireRelease(startup('observability', dependencies.initializeExporters), () => cleanup('observability', dependencies.shutdownExporters), ) yield* Effect.addFinalizer(() => Effect.gen(function* () { yield* cleanup('private sites database', dependencies.closePrivateDatabase) yield* cleanup('database', dependencies.closeDatabase) }), ) yield* Effect.addFinalizer(() => cleanup('revalidation', dependencies.closeRevalidation)) if (dependencies.acquireStorage) { yield* dependencies.acquireStorage.pipe(Effect.mapError(() => new HostingStartupError({ component: 'storage' }))) } yield* Effect.acquireRelease(Effect.sync(dependencies.startCleanup), () => Effect.sync(dependencies.stopCleanup)) yield* Effect.acquireRelease(startup('cache invalidation', dependencies.startInvalidation), () => cleanup('cache invalidation', dependencies.stopInvalidation), ) // Retain one actual bootstrap promise, not an interruptible wrapper around disk writes. const bootstrap = dependencies.bootstrap().catch(() => dependencies.logFailure('hot cache bootstrap')) yield* Effect.addFinalizer(() => Effect.promise(() => bootstrap)) return yield* Effect.acquireRelease( Effect.try({ try: dependencies.createServer, catch: () => new HostingStartupError({ component: 'http' }), }), (server) => cleanup('http', async () => dependencies.logHttpStop(await dependencies.stopServer(server))), )})