"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: Object.getOwnPropertyDescriptor(all, name).get
    });
}
_export(exports, {
    get MIGRATIONS_END () {
        return MIGRATIONS_END;
    },
    get MIGRATIONS_START () {
        return MIGRATIONS_START;
    },
    get listSteps () {
        return listSteps;
    },
    get orderSteps () {
        return orderSteps;
    }
});
const _nodefs = require("node:fs");
const _nodepath = require("node:path");
const _toposort = /*#__PURE__*/ _interop_require_default(require("toposort"));
const _step = require("./step.js");
function _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
function _getRequireWildcardCache(nodeInterop) {
    if (typeof WeakMap !== "function") return null;
    var cacheBabelInterop = new WeakMap();
    var cacheNodeInterop = new WeakMap();
    return (_getRequireWildcardCache = function(nodeInterop) {
        return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
    })(nodeInterop);
}
function _interop_require_wildcard(obj, nodeInterop) {
    if (!nodeInterop && obj && obj.__esModule) {
        return obj;
    }
    if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
        return {
            default: obj
        };
    }
    var cache = _getRequireWildcardCache(nodeInterop);
    if (cache && cache.has(obj)) {
        return cache.get(obj);
    }
    var newObj = {
        __proto__: null
    };
    var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
    for(var key in obj){
        if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
            var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
            if (desc && (desc.get || desc.set)) {
                Object.defineProperty(newObj, key, desc);
            } else {
                newObj[key] = obj[key];
            }
        }
    }
    newObj.default = obj;
    if (cache) {
        cache.set(obj, newObj);
    }
    return newObj;
}
const STEPS_DIR = (0, _nodepath.join)(__dirname, 'steps');
const MIGRATIONS_START = _step.MIGRATION_PREFIX + _step.START;
const MIGRATIONS_END = _step.MIGRATION_PREFIX + _step.END;
async function listSteps() {
    const steps = (await Promise.all((await _nodefs.promises.readdir(STEPS_DIR)).filter((file)=>/^\d+-[^.:]+[.][jt]s$/.test(file)).sort().map(readStep))).flat();
    const migrations = steps.flatMap(({ step })=>(0, _step.onlyMigrations)(step.after).concat((0, _step.onlyMigrations)(step.before)));
    return orderSteps(steps, migrations);
}
async function orderSteps(steps, migrations) {
    // we build a list of edges in a dependency directed graph:
    // an edge A -> B is represented as [A, B]
    //
    // the goal is to convert the step requirement in the config (at, before, after)
    // to a graph, and then convert that via toposort to a single order of steps to
    // apply.
    //
    // migrations are mixed in only to the extent that they're referenced in the steps,
    // and then in the fixed section added at the end in the concat(), which adds the
    // first and last pending migrations to the ordering. this is because we use a
    // "migrate up to" strategy, instead of running each migration individually.
    //
    // steps and migrations are differentiated by their "step ID", which is upgrade/...
    // for upgrade steps and migration/... for migrations. there's an additional thing
    // where upgrade files can have multiple steps in them, so you can define a pre and
    // a post migration step in the same file if they're related. so upgrade steps IDs
    // have an index suffix, such that in upgrade/filename/N, N is the zero-based index
    // of the step within the file. step relationships must specify an index, not doing
    // so throws immediately.
    //
    // when there are before/after dependencies, the at: START|END specifier *biases*
    // the result but doesn't make it absolute. when there are no before/after deps,
    // then START steps will always be before migrations, and the END steps will
    // always be after migrations.
    const edges = edgeFilter(migrations.map((mig, i)=>[
            migrations[i - 1],
            mig
        ])).concat(steps.flatMap(({ id, step })=>{
        const topo = [];
        if (step.at === _step.START) {
            topo.push([
                _step.START,
                id
            ]);
        } else if (step.at === _step.END) {
            topo.push([
                id,
                _step.END
            ]);
        }
        for (const need of step.before){
            topo.push([
                id,
                need
            ]);
        }
        for (const need of step.after){
            topo.push([
                need,
                id
            ]);
        }
        if (step.before.length === 0 && step.after.length === 0) {
            if (step.at === _step.START) {
                topo.push([
                    id,
                    MIGRATIONS_START
                ]);
            } else if (step.at === _step.END) {
                topo.push([
                    MIGRATIONS_END,
                    id
                ]);
            }
        }
        return topo;
    })).concat(edgeFilter([
        [
            _step.START,
            _step.END
        ],
        [
            _step.START,
            MIGRATIONS_START
        ],
        [
            MIGRATIONS_START,
            migrations[0]
        ],
        [
            migrations[migrations.length - 1],
            MIGRATIONS_END
        ],
        [
            MIGRATIONS_END,
            _step.END
        ]
    ]));
    // we return both the order as a list of step IDs and a mapping of
    // step IDs to step definitions. keeping the order list lightweight
    // probably helps performance marginally, but is also the simplest.
    return {
        order: (0, _toposort.default)(edges),
        steps: new Map(steps.map((step)=>[
                step.id,
                step
            ]))
    };
}
async function readStep(file) {
    const stepfile = (0, _nodepath.basename)(file, (0, _nodepath.extname)(file));
    const { STEPS } = await Promise.resolve((0, _nodepath.join)(STEPS_DIR, file)).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
    return STEPS.map((step, i)=>({
            id: `upgrade/${stepfile}/${i}`,
            file,
            step: {
                before: [],
                after: [],
                check: ()=>Promise.resolve(true),
                ...step
            }
        }));
}
function edgeFilter(edges) {
    return edges.filter(([a, b])=>a && b);
}

//# sourceMappingURL=listSteps.js.map