"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Toml = exports.MultidocYaml = exports.Yaml = exports.UtcDate = exports.Jsonc = exports.Json5 = exports.Json = void 0;
exports.LooseArray = LooseArray;
exports.LooseRecord = LooseRecord;
const tslib_1 = require("tslib");
const json5_1 = tslib_1.__importDefault(require("json5"));
const JSONC = tslib_1.__importStar(require("jsonc-parser"));
const luxon_1 = require("luxon");
const zod_1 = require("zod");
const toml_1 = require("./toml");
const yaml_1 = require("./yaml");
/**
 * Works like `z.array()`, but drops wrong elements instead of invalidating the whole array.
 *
 * **Important**: non-array inputs are still invalid.
 * Use `LooseArray(...).catch([])` to handle it.
 *
 * @param Elem Schema for array elements
 * @param onError Callback for errors
 * @returns Schema for array
 */
function LooseArray(Elem, { onError } = {}) {
    if (!onError) {
        // Avoid error-related computations inside the loop
        return zod_1.z.array(zod_1.z.any()).transform((input) => {
            const output = [];
            for (const x of input) {
                const parsed = Elem.safeParse(x);
                if (parsed.success) {
                    output.push(parsed.data);
                }
            }
            return output;
        });
    }
    return zod_1.z.array(zod_1.z.any()).transform((input) => {
        const output = [];
        const issues = [];
        for (let idx = 0; idx < input.length; idx += 1) {
            const x = input[idx];
            const parsed = Elem.safeParse(x);
            if (parsed.success) {
                output.push(parsed.data);
                continue;
            }
            for (const issue of parsed.error.issues) {
                issue.path.unshift(idx);
                issues.push(issue);
            }
        }
        if (issues.length) {
            const error = new zod_1.z.ZodError(issues);
            onError({ error, input });
        }
        return output;
    });
}
function LooseRecord(arg1, arg2, arg3) {
    let Key = zod_1.z.any();
    let Value;
    let opts = {};
    if (arg2 && arg3) {
        Key = arg1;
        Value = arg2;
        opts = arg3;
    }
    else if (arg2) {
        if (arg2 instanceof zod_1.z.ZodType) {
            Key = arg1;
            Value = arg2;
        }
        else {
            Value = arg1;
            opts = arg2;
        }
    }
    else {
        Value = arg1;
    }
    const { onError } = opts;
    if (!onError) {
        // Avoid error-related computations inside the loop
        return zod_1.z.record(zod_1.z.any()).transform((input) => {
            const output = {};
            for (const [inputKey, inputVal] of Object.entries(input)) {
                const parsedKey = Key.safeParse(inputKey);
                const parsedValue = Value.safeParse(inputVal);
                if (parsedKey.success && parsedValue.success) {
                    output[parsedKey.data] = parsedValue.data;
                }
            }
            return output;
        });
    }
    return zod_1.z.record(zod_1.z.any()).transform((input) => {
        const output = {};
        const issues = [];
        for (const [inputKey, inputVal] of Object.entries(input)) {
            const parsedKey = Key.safeParse(inputKey);
            if (!parsedKey.success) {
                for (const issue of parsedKey.error.issues) {
                    issue.path.unshift(inputKey);
                    issues.push(issue);
                }
                continue;
            }
            const parsedValue = Value.safeParse(inputVal);
            if (!parsedValue.success) {
                for (const issue of parsedValue.error.issues) {
                    issue.path.unshift(inputKey);
                    issues.push(issue);
                }
                continue;
            }
            output[parsedKey.data] = parsedValue.data;
            continue;
        }
        if (issues.length) {
            const error = new zod_1.z.ZodError(issues);
            onError({ error, input });
        }
        return output;
    });
}
exports.Json = zod_1.z.string().transform((str, ctx) => {
    try {
        return JSON.parse(str);
    }
    catch {
        ctx.addIssue({ code: 'custom', message: 'Invalid JSON' });
        return zod_1.z.NEVER;
    }
});
exports.Json5 = zod_1.z.string().transform((str, ctx) => {
    try {
        return json5_1.default.parse(str);
    }
    catch {
        ctx.addIssue({ code: 'custom', message: 'Invalid JSON5' });
        return zod_1.z.NEVER;
    }
});
exports.Jsonc = zod_1.z.string().transform((str, ctx) => {
    const errors = [];
    const value = JSONC.parse(str, errors);
    if (errors.length === 0) {
        return value;
    }
    ctx.addIssue({ code: 'custom', message: 'Invalid JSONC' });
    return zod_1.z.NEVER;
});
exports.UtcDate = zod_1.z
    .string({ description: 'ISO 8601 string' })
    .transform((str, ctx) => {
    const date = luxon_1.DateTime.fromISO(str, { zone: 'utc' });
    if (!date.isValid) {
        ctx.addIssue({ code: 'custom', message: 'Invalid date' });
        return zod_1.z.NEVER;
    }
    return date;
});
exports.Yaml = zod_1.z.string().transform((str, ctx) => {
    try {
        return (0, yaml_1.parseSingleYaml)(str);
    }
    catch {
        ctx.addIssue({ code: 'custom', message: 'Invalid YAML' });
        return zod_1.z.NEVER;
    }
});
exports.MultidocYaml = zod_1.z.string().transform((str, ctx) => {
    try {
        return (0, yaml_1.parseYaml)(str);
    }
    catch {
        ctx.addIssue({ code: 'custom', message: 'Invalid YAML' });
        return zod_1.z.NEVER;
    }
});
exports.Toml = zod_1.z.string().transform((str, ctx) => {
    try {
        return (0, toml_1.parse)(str);
    }
    catch {
        ctx.addIssue({ code: 'custom', message: 'Invalid TOML' });
        return zod_1.z.NEVER;
    }
});
//# sourceMappingURL=schema-utils.js.map