{"version":3,"file":"index.mjs","names":[],"sources":["../src/error.ts","../src/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2024-2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport {\n    IssueCode,\n    defineIssueItem,\n    isIssueItem,\n} from '@ebec/core';\nimport { hasOwnProperty } from 'validup';\nimport { getPathValue } from 'pathtrace';\nimport type { $ZodIssue, $ZodRawIssue } from 'zod/v4/core';\nimport type { ZodError } from 'zod';\nimport type { Issue } from '@ebec/core';\nimport type { ValidupError } from 'validup';\n\nexport type ZodIssue = $ZodRawIssue;\n\n/**\n * Names of zod 4 `too_small` / `too_big` issue `origin` values that\n * measure a sequence length (string, array, etc.) rather than a numeric\n * magnitude. Used to pick between validup's `MIN_LENGTH` / `MAX_LENGTH`\n * (length-shaped) and `MIN_VALUE` / `MAX_VALUE` (magnitude-shaped) codes.\n */\nconst LENGTH_LIKE_ORIGINS = new Set(['string', 'array', 'set', 'file']);\n\n/**\n * Internal — picks a validup `IssueCode` for a zod issue and (when the\n * code calls for it) builds the structured `data` payload templates\n * rely on (`{{min}}`, `{{max}}`, `{{pattern}}`, …).\n *\n * Falls back to `VALUE_INVALID` for zod codes that don't have a clear\n * vocabulary match — the original zod `message` carries through on the\n * resulting validup `IssueItem.message` so consumer-side rendering still\n * has something to display.\n */\nfunction mapZodIssue(\n    raw: ZodIssue | $ZodIssue,\n    input: unknown,\n    inputProvided: boolean,\n): {\n    code: string,\n    data?: Record<string, unknown>,\n} {\n    switch (raw.code) {\n        case 'invalid_type': {\n            // zod 4 strips `input` / `received` from the formatted issue\n            // (only `expected` is populated), so structurally we can't tell\n            // missing-key from wrong-type from the issue alone. When the\n            // caller threads the original parsed input through, we can\n            // recover REQUIRED by looking up the leaf value at the issue\n            // path — if it's `undefined`, the field was absent (or\n            // explicitly `undefined`), which is the REQUIRED case.\n            // Wrong-type stays on VALUE_INVALID; the zod-supplied English\n            // message still surfaces as the fallback display string in\n            // both branches.\n            if (inputProvided) {\n                const path = raw.path ?? [];\n                const leaf = path.length === 0 ?\n                    input :\n                    getPathValue(input, path as PropertyKey[]);\n                if (typeof leaf === 'undefined') {\n                    return { code: IssueCode.REQUIRED };\n                }\n            }\n            return { code: IssueCode.VALUE_INVALID };\n        }\n        case 'too_small': {\n            const origin = String(raw.origin ?? '');\n            const min = Number(raw.minimum);\n            if (LENGTH_LIKE_ORIGINS.has(origin)) {\n                return { code: IssueCode.MIN_LENGTH, data: { min } };\n            }\n            return { code: IssueCode.MIN_VALUE, data: { min } };\n        }\n        case 'too_big': {\n            const origin = String(raw.origin ?? '');\n            const max = Number(raw.maximum);\n            if (LENGTH_LIKE_ORIGINS.has(origin)) {\n                return { code: IssueCode.MAX_LENGTH, data: { max } };\n            }\n            return { code: IssueCode.MAX_VALUE, data: { max } };\n        }\n        case 'invalid_format': {\n            const format = String(raw.format ?? '');\n            switch (format) {\n                case 'email':\n                    return { code: IssueCode.EMAIL };\n                case 'url':\n                    return { code: IssueCode.URL };\n                case 'uuid':\n                case 'guid':\n                    return { code: IssueCode.UUID };\n                // nanoid / cuid / cuid2 / ulid are distinct ID formats — an\n                // i18n template keyed on `UUID` would render \"must be a valid\n                // UUID\" against a nanoid field, which is wrong. Fall back to\n                // the generic code so consumers can add catalog entries for\n                // these formats explicitly if they care.\n                case 'nanoid':\n                case 'cuid':\n                case 'cuid2':\n                case 'ulid':\n                    return { code: IssueCode.VALUE_INVALID };\n                case 'date':\n                case 'time':\n                case 'datetime':\n                case 'duration':\n                    return { code: IssueCode.DATE };\n                case 'ipv4':\n                case 'ipv6':\n                case 'cidrv4':\n                case 'cidrv6':\n                    return { code: IssueCode.IP_ADDRESS };\n                case 'base64':\n                case 'base64url':\n                    return { code: IssueCode.BASE64 };\n                case 'json_string':\n                    return { code: IssueCode.JSON };\n                case 'regex': {\n                    const pattern = typeof raw.pattern === 'string' ?\n                        raw.pattern :\n                        String(raw.pattern ?? '');\n                    return { code: IssueCode.PATTERN, data: { pattern } };\n                }\n                default:\n                    return { code: IssueCode.VALUE_INVALID };\n            }\n        }\n        case 'invalid_value':\n            // zod 4 emits `invalid_value` for both enum (`z.enum([...])`)\n            // and literal (`z.literal(...)`) mismatches — the value didn't\n            // match any of a closed set of options. That's the same shape\n            // as a failed `oneOf` container, so reuse the existing\n            // vocabulary entry.\n            return { code: IssueCode.ONE_OF_FAILED };\n        // The remaining zod codes don't have a clean vocabulary match —\n        // `invalid_union`, `invalid_key`, `invalid_element`,\n        // `unrecognized_keys`, `not_multiple_of`, `custom`. Fall back to\n        // the generic code; the zod-supplied `message` still surfaces\n        // verbatim on the IssueItem.\n        default:\n            return { code: IssueCode.VALUE_INVALID };\n    }\n}\n\n/**\n * Translate a `ZodError` into validup `Issue`s. Each zod issue gets:\n *\n * - `code` — mapped onto the validup `IssueCode` vocabulary where\n *   possible (see {@link mapZodIssue} for the table); falls back to\n *   `IssueCode.VALUE_INVALID` for zod codes that don't have a vocabulary\n *   equivalent.\n * - `message` — zod's own message, verbatim. Consumer-side i18n\n *   catalogs override this via the `code` lookup; `message` is the\n *   English fallback when no translation is registered.\n * - `data` — structured payload for code-aware templates\n *   (`{{min}}` / `{{max}}` / `{{pattern}}`); absent when the code\n *   carries no parameters.\n * - `expected` / `received` — vendor-specific zod fields, passed\n *   through when present.\n *\n * Pass the original parsed input as the second argument to enable the\n * `invalid_type` → `REQUIRED` promotion: zod 4's formatted issues don't\n * carry `received`, so we recover the missing-key signal by looking up\n * the issue's path against the input and checking whether the leaf is\n * `undefined`. {@link createValidator} threads `ctx.value` through\n * automatically; ad-hoc callers can opt in by passing it explicitly.\n */\nexport function buildIssuesForZodError(error: ZodError, input?: unknown): Issue[] {\n    const issues : Issue[] = [];\n    const inputProvided = arguments.length > 1;\n\n    for (let i = 0; i < error.issues.length; i++) {\n        const issue = error.issues[i];\n\n        const { code, data } = mapZodIssue(issue, input, inputProvided);\n\n        let expected : unknown;\n        if (hasOwnProperty(issue, 'expected')) {\n            expected = issue.expected;\n        }\n\n        let received : unknown;\n        if (hasOwnProperty(issue, 'received')) {\n            received = issue.received;\n        }\n\n        issues.push(defineIssueItem({\n            path: issue.path || [],\n            message: issue.message,\n            code,\n            data,\n            expected,\n            received,\n        }));\n    }\n\n    return issues;\n}\n\n/**\n * Build Zod Issues for validup issue.\n *\n * @param issue\n */\nexport function buildZodIssuesForIssue(issue: Issue) : ZodIssue[] {\n    if (isIssueItem(issue)) {\n        return [\n            {\n                code: 'custom',\n                message: issue.message,\n                path: issue.path,\n                input: issue.received,\n            },\n        ];\n    }\n\n    const output : ZodIssue[] = [];\n    for (let i = 0; i < issue.issues.length; i++) {\n        const child = issue.issues[i];\n\n        output.push(...buildZodIssuesForIssue(child));\n    }\n\n    return output;\n}\n\nexport function buildZodIssuesForError(error: ValidupError) : ZodIssue[] {\n    return error.issues\n        .flatMap((issue) => buildZodIssuesForIssue(issue));\n}\n","/*\n * Copyright (c) 2024.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { ValidatorContext, ValidatorDescriptor } from 'validup';\nimport { ValidupError, defineValidator } from 'validup';\nimport type { output as ZodOutput, ZodType } from 'zod';\nimport { buildIssuesForZodError } from './error';\n\ntype ZodCreateFn<C, Z extends ZodType> = (ctx: ValidatorContext<C>) => Z;\n\n/**\n * Options accepted by {@link createValidator}.\n */\nexport type ZodCreateValidatorOptions = {\n    /**\n     * Mark the resulting validator as having side effects (network\n     * calls, sibling-field reads, observable state) so the framework\n     * never caches its outcome. Default `false` — most zod schemas\n     * (`z.string().email()`, length / regex / enum checks) are\n     * deterministic and safe to cache by `(ctx.value, ctx.context,\n     * ctx.group)`. Set to `true` for schemas that use async refines,\n     * `superRefine` calls reading external state, or transforms with\n     * side effects.\n     */\n    sideEffect?: boolean,\n};\n\n/**\n * Wrap a zod schema as a validup `ValidatorDescriptor`. The returned\n * descriptor infers its `Out` type from the schema, so the builder API\n * (`defineSchema`) and any caller using the second `Validator<C, Out>`\n * generic see the parsed shape (`z.output<Z>`) rather than `unknown`.\n *\n * Pass a factory `(ctx) => schema` to build per-context schemas.\n *\n * Returned as a {@link ValidatorDescriptor} (not a bare function) so it\n * can carry the `sideEffect` flag the validup runtime consults for its\n * result cache. Mounts unchanged via the same `container.mount(...)`\n * signatures — bare-function and descriptor inputs are interchangeable\n * at the mount site.\n */\nexport function createValidator<\n    C = unknown,\n    Z extends ZodType = ZodType,\n>(\n    input: Z | ZodCreateFn<C, Z>,\n    options: ZodCreateValidatorOptions = {},\n): ValidatorDescriptor<C, ZodOutput<Z>> {\n    return defineValidator<C, ZodOutput<Z>>({\n        sideEffect: options.sideEffect,\n        run: async (ctx) => {\n            const zod = typeof input === 'function' ? input(ctx) : input;\n\n            const outcome = await zod.safeParseAsync(ctx.value);\n            if (outcome.success) {\n                return outcome.data as ZodOutput<Z>;\n            }\n\n            throw new ValidupError(buildIssuesForZodError(outcome.error, ctx.value));\n        },\n    });\n}\n"],"mappings":"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"}