import { join } from "node:path"; import { FileSystem } from "@effect/platform"; import { NodeFileSystem } from "@effect/platform-node"; import type { OpenApiSourceStorage } from "@executor/plugin-openapi-sdk"; import { OpenApiStoredSourceDataSchema, type OpenApiStoredSourceData, } from "@executor/plugin-openapi-shared"; import * as Effect from "effect/Effect"; import * as Schema from "effect/Schema"; const decodeStoredSourceData = Schema.decodeUnknownSync(OpenApiStoredSourceDataSchema); const encodeStoredSourceData = Schema.encodeSync(OpenApiStoredSourceDataSchema); const storagePath = (input: { rootDir: string; scopeId: string; sourceId: string; }) => join(input.rootDir, input.scopeId, `${input.sourceId}.json`); const toError = (cause: unknown): Error => cause instanceof Error ? cause : new Error(String(cause)); const bindNodeFileSystem = ( effect: Effect.Effect, ): Effect.Effect => effect.pipe(Effect.provide(NodeFileSystem.layer)); const readStoredSourceData = ( path: string, ): Effect.Effect => bindNodeFileSystem( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const exists = yield* fileSystem.exists(path).pipe(Effect.mapError(toError)); if (!exists) { return null; } const contents = yield* fileSystem.readFileString(path, "utf8").pipe( Effect.mapError(toError), ); return decodeStoredSourceData(JSON.parse(contents)); }), ); export const createFileOpenApiSourceStorage = (input: { rootDir: string; }): OpenApiSourceStorage => ({ get: ({ scopeId, sourceId }) => readStoredSourceData( storagePath({ rootDir: input.rootDir, scopeId, sourceId, }), ), put: ({ scopeId, sourceId, value }) => bindNodeFileSystem( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const path = storagePath({ rootDir: input.rootDir, scopeId, sourceId, }); yield* fileSystem.makeDirectory(join(input.rootDir, scopeId), { recursive: true, }).pipe(Effect.mapError(toError)); yield* fileSystem.writeFileString( path, `${JSON.stringify(encodeStoredSourceData(value), null, 2)}\n`, ).pipe(Effect.mapError(toError)); }), ), remove: ({ scopeId, sourceId }) => bindNodeFileSystem( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const path = storagePath({ rootDir: input.rootDir, scopeId, sourceId, }); const exists = yield* fileSystem.exists(path).pipe(Effect.mapError(toError)); if (exists) { yield* fileSystem.remove(path).pipe(Effect.mapError(toError)); } }), ), });