diff --git a/.changeset/template-manifest-not-strict.md b/.changeset/template-manifest-not-strict.md new file mode 100644 index 0000000..fa389a1 --- /dev/null +++ b/.changeset/template-manifest-not-strict.md @@ -0,0 +1,5 @@ +--- +"@generata/core": patch +--- + +fix: make TemplateManifest schema strict to reject unknown keys diff --git a/packages/core/src/cli/manifest.test.ts b/packages/core/src/cli/manifest.test.ts index aaf9b08..327c698 100644 --- a/packages/core/src/cli/manifest.test.ts +++ b/packages/core/src/cli/manifest.test.ts @@ -43,6 +43,13 @@ describe("parseManifest", () => { it("rejects bin entry missing name", () => { throws(() => parseManifest({ name: "x", description: "y", requiredBins: [{}] })); }); + + it("rejects unknown top-level keys", () => { + throws( + () => parseManifest({ name: "x", description: "y", requireBins: [] }), + /requireBins/, + ); + }); }); describe("loadManifest", () => { diff --git a/packages/core/src/cli/manifest.ts b/packages/core/src/cli/manifest.ts index 348e219..4380ae0 100644 --- a/packages/core/src/cli/manifest.ts +++ b/packages/core/src/cli/manifest.ts @@ -15,15 +15,17 @@ const RequiredEnvEntry = z.object({ optional: z.boolean().default(false), }); -export const TemplateManifest = z.object({ - name: z.string(), - description: z.string(), - engineVersion: z.string().optional(), - requiredBins: z.array(RequiredBin).default([]), - requiredEnv: z.record(z.string(), RequiredEnvEntry).default({}), - installPaths: z.record(z.string(), z.string()).default({}), - postInstall: z.string().optional(), -}); +export const TemplateManifest = z + .object({ + name: z.string(), + description: z.string(), + engineVersion: z.string().optional(), + requiredBins: z.array(RequiredBin).default([]), + requiredEnv: z.record(z.string(), RequiredEnvEntry).default({}), + installPaths: z.record(z.string(), z.string()).default({}), + postInstall: z.string().optional(), + }) + .strict(); export type TemplateManifest = z.infer; export function parseManifest(input: unknown): TemplateManifest {