var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
    if (typeof path === "string" && /^\.\.?\//.test(path)) {
        return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
            return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
        });
    }
    return path;
};
import * as FileSystem from "@effect/platform/FileSystem";
import * as Path from "@effect/platform/Path";
import * as Data from "effect/Data";
import * as Effect from "effect/Effect";
import * as Either from "effect/Either";
import { identity } from "effect/Function";
import * as Match from "effect/Match";
import * as Predicate from "effect/Predicate";
import * as Schema from "effect/Schema";
import * as Stream from "effect/Stream";
import * as fs from "node:fs";
import * as path from "node:path";
import * as ManifestBuilder from "./ManifestBuilder.js";
import * as Model from "./Model.js";
export class Context {
    definitionPath;
    constructor(definitionPath) {
        this.definitionPath = definitionPath;
    }
    /**
     * Reads a file relative to the directory of the dataset definition.
     */
    functionSource(relativePath) {
        const baseDir = path.dirname(this.definitionPath);
        const fullPath = path.join(baseDir, relativePath);
        let source;
        try {
            source = fs.readFileSync(fullPath, "utf8");
        }
        catch (err) {
            throw new Error(`Failed to read function source at ${fullPath}: ${err.message}`);
        }
        const func = new Model.FunctionSource({
            source,
            filename: path.basename(fullPath),
        });
        return func;
    }
}
export class ConfigLoaderError extends Data.TaggedError("ConfigLoaderError") {
}
export class ConfigLoader extends Effect.Service()("Amp/ConfigLoader", {
    dependencies: [ManifestBuilder.ManifestBuilder.Default],
    effect: Effect.gen(function* () {
        const path = yield* Path.Path;
        const fs = yield* FileSystem.FileSystem;
        const builder = yield* ManifestBuilder.ManifestBuilder;
        const jiti = yield* Effect.tryPromise({
            try: () => import("jiti").then(({ createJiti }) => createJiti(import.meta.url, {
                moduleCache: false,
                tryNative: false,
            })),
            catch: (cause) => new ConfigLoaderError({ cause }),
        }).pipe(Effect.cached);
        const loadTypeScript = Effect.fnUntraced(function* (file) {
            return yield* Effect.tryMapPromise(jiti, {
                try: (jiti) => jiti.import(file, {
                    default: true,
                }),
                catch: identity,
            }).pipe(Effect.map((callback) => callback(new Context(file))), Effect.flatMap(Schema.decodeUnknown(Model.DatasetConfig)), Effect.mapError((cause) => new ConfigLoaderError({
                cause,
                message: `Failed to load config file ${file}`,
            })));
        });
        const loadJavaScript = Effect.fnUntraced(function* (file) {
            return yield* Effect.tryPromise({
                try: () => import(__rewriteRelativeImportExtension(file)).then((module) => module.default),
                catch: identity,
            }).pipe(Effect.map((callback) => callback(new Context(file))), Effect.flatMap(Schema.decodeUnknown(Model.DatasetConfig)), Effect.mapError((cause) => new ConfigLoaderError({
                cause,
                message: `Failed to load config file ${file}`,
            })));
        });
        const loadJson = Effect.fnUntraced(function* (file) {
            return yield* Effect.tryMap(fs.readFileString(file), {
                try: (content) => JSON.parse(content),
                catch: identity,
            }).pipe(Effect.flatMap(Schema.decodeUnknown(Model.DatasetConfig)), Effect.mapError((cause) => new ConfigLoaderError({
                cause,
                message: `Failed to load config file ${file}`,
            })));
        });
        const fileMatcher = Match.type().pipe(Match.when((_) => /\.(ts|mts|cts)$/.test(path.extname(_)), (_) => loadTypeScript(_)), Match.when((_) => /\.(js|mjs|cjs)$/.test(path.extname(_)), (_) => loadJavaScript(_)), Match.when((_) => /\.(json)$/.test(path.extname(_)), (_) => loadJson(_)), Match.orElse((_) => new ConfigLoaderError({
            message: `Unsupported file extension ${path.extname(_)}`,
        })));
        const load = Effect.fnUntraced(function* (file) {
            const resolved = path.resolve(file);
            return yield* fileMatcher(resolved);
        });
        const build = Effect.fnUntraced(function* (file) {
            const config = yield* load(file);
            return yield* builder.build(config).pipe(Effect.mapError((cause) => new ConfigLoaderError({
                cause,
                message: `Failed to build config file ${file}`,
            })));
        });
        const CANDIDATE_CONFIG_FILES = [
            "amp.config.ts",
            "amp.config.mts",
            "amp.config.cts",
            "amp.config.js",
            "amp.config.mjs",
            "amp.config.cjs",
            "amp.config.json",
        ];
        const find = Effect.fnUntraced(function* (cwd = ".") {
            const candidates = CANDIDATE_CONFIG_FILES.map((fileName) => {
                const filePath = path.resolve(cwd, fileName);
                return Effect.as(fs.exists(filePath), filePath);
            });
            return yield* Effect.firstSuccessOf(candidates).pipe(Effect.option);
        });
        const watch = (file, options) => {
            const resolved = path.resolve(file);
            const open = load(resolved).pipe(Effect.tapErrorCause(options?.onError ?? (() => Effect.void)), Effect.either);
            const updates = fs.watch(resolved).pipe(Stream.buffer({ capacity: 1, strategy: "sliding" }), Stream.mapError((cause) => new ConfigLoaderError({
                cause,
                message: "Failed to watch config file",
            })), Stream.filter(Predicate.isTagged("Update")), Stream.mapEffect(() => open));
            const build = (config) => builder.build(config).pipe(Effect.mapError((cause) => new ConfigLoaderError({
                cause,
                message: `Failed to build config file ${file}`,
            })), Effect.tapErrorCause(options?.onError ?? (() => Effect.void)), Effect.either);
            return Stream.fromEffect(open).pipe(Stream.concat(updates), Stream.filterMap(Either.getRight), Stream.changesWith(Schema.equivalence(Model.DatasetConfig)), Stream.mapEffect((config) => build(config)), Stream.filterMap(Either.getRight), Stream.changesWith((a, b) => Schema.equivalence(Model.DatasetMetadata)(a.metadata, b.metadata) &&
                Schema.equivalence(Model.DatasetDerived)(a.manifest, b.manifest)));
        };
        return { load, find, watch, build };
    }),
}) {
}
//# sourceMappingURL=ConfigLoader.js.map