From 461d4fc48bd5cbbf4ab1b22f86ce3ea6134d7780 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 10 Apr 2026 15:09:02 +0200 Subject: [PATCH] Clean up parse output and schema enumerability Skip assigning undefined keys for absent optional fields in parse, matching the previous zod-object behavior. Make the schema getter non-enumerable so JSON.stringify on the portable schema only serializes parse and _fields. Signed-off-by: Patrik Oldsberg Made-with: Cursor --- .../src/blueprints/ApiBlueprint.test.ts | 1 - .../src/blueprints/NavItemBlueprint.test.tsx | 1 - .../src/blueprints/PageBlueprint.test.tsx | 1 - .../src/schema/createPortableSchema.test.ts | 12 ++++++++++++ .../src/schema/createPortableSchema.ts | 4 ++-- .../alpha/blueprints/EntityCardBlueprint.test.tsx | 1 - .../alpha/blueprints/EntityContentBlueprint.test.tsx | 1 - .../EntityContextMenuItemBlueprint.test.tsx | 1 - .../SearchResultListItemBlueprint.test.tsx | 1 - 9 files changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts b/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts index fabc15b3f4..283ce66e8c 100644 --- a/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts +++ b/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts @@ -190,7 +190,6 @@ describe('ApiBlueprint', () => { }, }, "parse": [Function], - "schema": [Function], }, "disabled": false, "factory": [Function], diff --git a/packages/frontend-plugin-api/src/blueprints/NavItemBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/NavItemBlueprint.test.tsx index ccdaa53a55..4efe16df12 100644 --- a/packages/frontend-plugin-api/src/blueprints/NavItemBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/NavItemBlueprint.test.tsx @@ -47,7 +47,6 @@ describe('NavItemBlueprint', () => { }, }, "parse": [Function], - "schema": [Function], }, "disabled": false, "factory": [Function], diff --git a/packages/frontend-plugin-api/src/blueprints/PageBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/PageBlueprint.test.tsx index 9ac58b3b06..cc043e6283 100644 --- a/packages/frontend-plugin-api/src/blueprints/PageBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/PageBlueprint.test.tsx @@ -61,7 +61,6 @@ describe('PageBlueprint', () => { }, }, "parse": [Function], - "schema": [Function], }, "disabled": false, "factory": [Function], diff --git a/packages/frontend-plugin-api/src/schema/createPortableSchema.test.ts b/packages/frontend-plugin-api/src/schema/createPortableSchema.test.ts index 7ea5943f17..55d5421adb 100644 --- a/packages/frontend-plugin-api/src/schema/createPortableSchema.test.ts +++ b/packages/frontend-plugin-api/src/schema/createPortableSchema.test.ts @@ -367,6 +367,18 @@ describe('createConfigSchema', () => { ); }); + it('should not produce undefined keys for absent optional fields', () => { + const schema = createConfigSchema({ + name: z => z.string(), + title: z => z.string().optional(), + count: z => z.number().default(42), + }); + + const result = schema.parse({ name: 'hello' }); + expect(result).toEqual({ name: 'hello', count: 42 }); + expect(Object.keys(result as object)).toEqual(['name', 'count']); + }); + it('should not mark defaulted zod v3 fields as required in JSON Schema', () => { const schema = createConfigSchema({ name: z => z.string(), diff --git a/packages/frontend-plugin-api/src/schema/createPortableSchema.ts b/packages/frontend-plugin-api/src/schema/createPortableSchema.ts index e1482475ef..2efa93b2e3 100644 --- a/packages/frontend-plugin-api/src/schema/createPortableSchema.ts +++ b/packages/frontend-plugin-api/src/schema/createPortableSchema.ts @@ -119,7 +119,7 @@ function buildPortableSchema( const validated = field.validate(inputObj[key]); if ('errors' in validated) { errors.push(...validated.errors); - } else { + } else if (validated.value !== undefined || key in inputObj) { result[key] = validated.value; } } @@ -152,7 +152,7 @@ function buildPortableSchema( return cached; }, configurable: true, - enumerable: true, + enumerable: false, }); return result; diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx index 0bf88b18c4..776c297fe0 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx @@ -58,7 +58,6 @@ describe('EntityCardBlueprint', () => { }, }, "parse": [Function], - "schema": [Function], }, "disabled": false, "factory": [Function], diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx index d0dff2a939..ff330473bf 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx @@ -75,7 +75,6 @@ describe('EntityContentBlueprint', () => { }, }, "parse": [Function], - "schema": [Function], }, "disabled": false, "factory": [Function], diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx index 463bc7cf60..467068c668 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx @@ -71,7 +71,6 @@ describe('EntityContextMenuItemBlueprint', () => { }, }, "parse": [Function], - "schema": [Function], }, "disabled": false, "factory": [Function], diff --git a/plugins/search-react/src/alpha/blueprints/SearchResultListItemBlueprint.test.tsx b/plugins/search-react/src/alpha/blueprints/SearchResultListItemBlueprint.test.tsx index 847a1df635..fe3fd29fc3 100644 --- a/plugins/search-react/src/alpha/blueprints/SearchResultListItemBlueprint.test.tsx +++ b/plugins/search-react/src/alpha/blueprints/SearchResultListItemBlueprint.test.tsx @@ -53,7 +53,6 @@ describe('SearchResultListItemBlueprint', () => { }, }, "parse": [Function], - "schema": [Function], }, "disabled": false, "factory": [Function],