import type z from "zod"; import { MANIFEST_CODES, ManifestError } from "../error-codes/error-codes.js"; import { type PluginManifest, pluginManifestSchema, } from "../schema/plugin-manifest.js"; /** * Discriminated result for the safe parse path. `data` is present on * success, `error` on failure — never both. */ export type ParseResult = | { readonly ok: true; readonly data: PluginManifest } | { readonly ok: false; readonly error: ManifestError }; /** * Strict-parse a plugin manifest. Throws `ManifestError` with code * `MANIFEST.PARSE_FAILED` on any Zod issue; the underlying Zod issues * array is preserved on `error.cause` so callers can render granular * field-level diagnostics. * * @throws {ManifestError} `MANIFEST.PARSE_FAILED` */ export function parseManifest(input: unknown): PluginManifest { const result = pluginManifestSchema.safeParse(input); if (result.success) return result.data; throw makeError(result.error); } /** * Non-throwing variant. Returns a discriminated `ParseResult`. The * error carries the same code + cause as the throw path so consumers * can render diagnostics without try/catch. */ export function parseManifestSafe(input: unknown): ParseResult { const result = pluginManifestSchema.safeParse(input); if (result.success) return { ok: true, data: result.data }; return { ok: false, error: makeError(result.error) }; } function makeError(zodError: z.ZodError): ManifestError { return new ManifestError( MANIFEST_CODES.PARSE_FAILED, "manifest.parse_failed", { cause: zodError, context: { issues: zodError.issues }, } ); }