Files
suaveplan/packages/api-stack/api/api-manifest/src/parse/parse.ts
2026-05-27 23:29:53 +01:00

51 lines
1.6 KiB
TypeScript

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 },
}
);
}