diff --git a/src/browser.ts b/src/browser.ts index 36898b3..fcf4e14 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -1,5 +1,5 @@ // Browser-compatible exports -export * from "./schemas/latest.js"; export * from "./schemas/index.js"; +export * from "./schemas/validate.js"; export * from "./shared/config.js"; export * from "./types.js"; diff --git a/src/schemas/index.ts b/src/schemas/index.ts index 06ad45c..3afe1b1 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -10,6 +10,7 @@ export * as vLatest from "./latest.js"; export { MANIFEST_VERSION as LATEST_MANIFEST_VERSION, McpbManifestSchema as LatestMcpbManifestSchema, + McpbManifestSchema, // backwards compatibility - exports latest schema } from "./latest.js"; /** diff --git a/src/schemas/validate.ts b/src/schemas/validate.ts new file mode 100644 index 0000000..89063f7 --- /dev/null +++ b/src/schemas/validate.ts @@ -0,0 +1,100 @@ +import type * as z from "zod"; + +import * as v0_1 from "./0.1.js"; +import * as v0_2 from "./0.2.js"; +import * as v0_3 from "./0.3.js"; + +type McpbManifestV0_1 = z.infer; +type McpbManifestV0_2 = z.infer; +type McpbManifestV0_3 = z.infer; + +/** + * Union type representing any supported manifest version. + * This is the return type of validateManifest. + */ +type ValidatedManifest = McpbManifestV0_1 | McpbManifestV0_2 | McpbManifestV0_3; + +/** + * Validates a manifest using the appropriate schema based on its manifest_version. + * + * This function automatically detects the manifest version from the data and applies + * the correct schema. This avoids TypeScript "excessively deep" errors that can occur + * when using union schemas across package boundaries. + * + * @param data - The manifest data to validate + * @returns The validated manifest with the correct type + * @throws ZodError if validation fails + * + * @example + * ```typescript + * import { validateManifest } from '@anthropic-ai/mcpb/browser'; + * + * const manifest = validateManifest(unknownData); + * // manifest is typed as McpbManifestAny (union of all versions) + * ``` + */ +export function validateManifest(data: unknown): ValidatedManifest { + // Read manifest_version or dxt_version from data + const version = (data as any)?.manifest_version || (data as any)?.dxt_version; + + // Use appropriate schema based on version + if (version === "0.1") { + return v0_1.McpbManifestSchema.parse(data); + } + + if (version === "0.2") { + return v0_2.McpbManifestSchema.parse(data); + } + + // Default to latest version (0.3) if not specified or if version is "0.3" + return v0_3.McpbManifestSchema.parse(data); +} + +/** + * Validates a manifest using the appropriate schema, returning a Result-style object. + * + * @param data - The manifest data to validate + * @returns An object with either { success: true, data } or { success: false, error } + * + * @example + * ```typescript + * import { validateManifestSafe } from '@anthropic-ai/mcpb/browser'; + * + * const result = validateManifestSafe(unknownData); + * if (result.success) { + * console.log(result.data); + * } else { + * console.error(result.error); + * } + * ``` + */ +export function validateManifestSafe(data: unknown): + | { success: true; data: ValidatedManifest } + | { success: false; error: unknown } { + try { + return { success: true, data: validateManifest(data) }; + } catch (error) { + return { success: false, error }; + } +} + +/** + * Get the appropriate schema for a given manifest version. + * Useful when you need to work with the schema directly. + * + * @param version - The manifest version (e.g., "0.1", "0.2", "0.3") + * @returns The schema for that version + * + * @example + * ```typescript + * import { getSchemaForVersion } from '@anthropic-ai/mcpb/browser'; + * + * const schema = getSchemaForVersion("0.2"); + * const result = schema.safeParse(data); + * ``` + */ +export function getSchemaForVersion(version: "0.1" | "0.2" | "0.3") { + if (version === "0.1") return v0_1.McpbManifestSchema; + if (version === "0.2") return v0_2.McpbManifestSchema; + return v0_3.McpbManifestSchema; +} diff --git a/src/shared/config.ts b/src/shared/config.ts index 20e4262..bf8391c 100644 --- a/src/shared/config.ts +++ b/src/shared/config.ts @@ -1,6 +1,7 @@ import type { Logger, McpbManifest, + McpbManifestAny, McpbUserConfigValues, McpServerConfig, } from "../types.js"; @@ -85,7 +86,7 @@ export function replaceVariables( } interface GetMcpConfigForManifestOptions { - manifest: McpbManifest; + manifest: McpbManifestAny; extensionPath: string; systemDirs: Record; userConfig: McpbUserConfigValues; @@ -179,7 +180,7 @@ export async function getMcpConfigForManifest( } interface HasRequiredConfigMissingOptions { - manifest: McpbManifest; + manifest: McpbManifestAny; userConfig?: McpbUserConfigValues; }