diff --git a/packages/frontend-plugin-api/src/schema/createPortableSchema.test.ts b/packages/frontend-plugin-api/src/schema/createPortableSchema.test.ts index d612578f32..7ea5943f17 100644 --- a/packages/frontend-plugin-api/src/schema/createPortableSchema.test.ts +++ b/packages/frontend-plugin-api/src/schema/createPortableSchema.test.ts @@ -349,5 +349,36 @@ describe('createConfigSchema', () => { additionalProperties: false, }); }); + + it('should throw a clear error for non-object input', () => { + const schema = createConfigSchema({ title: z => z.string() }); + + expect(() => schema.parse('not an object')).toThrow( + 'Invalid config input, expected object but got string', + ); + expect(() => schema.parse(42)).toThrow( + 'Invalid config input, expected object but got number', + ); + expect(() => schema.parse([1, 2])).toThrow( + 'Invalid config input, expected object but got array', + ); + expect(() => schema.parse(true)).toThrow( + 'Invalid config input, expected object but got boolean', + ); + }); + + it('should not mark defaulted zod v3 fields as required in JSON Schema', () => { + const schema = createConfigSchema({ + name: z => z.string(), + title: z => z.string().default('hello'), + count: z => z.number().optional(), + }); + + const result = schema.schema(); + expect(result.schema).toMatchObject({ + type: 'object', + required: ['name'], + }); + }); }); }); diff --git a/packages/frontend-plugin-api/src/schema/createPortableSchema.ts b/packages/frontend-plugin-api/src/schema/createPortableSchema.ts index 0abd3224fa..e1482475ef 100644 --- a/packages/frontend-plugin-api/src/schema/createPortableSchema.ts +++ b/packages/frontend-plugin-api/src/schema/createPortableSchema.ts @@ -100,6 +100,17 @@ function buildPortableSchema( fields: Record, ): MergeablePortableSchema { function parse(input: unknown) { + if ( + input !== undefined && + input !== null && + (typeof input !== 'object' || Array.isArray(input)) + ) { + throw new Error( + `Invalid config input, expected object but got ${ + Array.isArray(input) ? 'array' : typeof input + }`, + ); + } const inputObj = (input ?? {}) as Record; const result: Record = {}; const errors: string[] = []; diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index 33f3bca158..d7d90079a2 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -726,18 +726,16 @@ export function createExtension( ), output: (overrideOptions.output ?? options.output) as ExtensionDataRef[], - config: + configSchema: options.config || options.configSchema || overrideOptions.config || overrideOptions.configSchema ? { - schema: { - ...options.config?.schema, - ...options.configSchema, - ...overrideOptions.config?.schema, - ...overrideOptions.configSchema, - }, + ...options.config?.schema, + ...options.configSchema, + ...overrideOptions.config?.schema, + ...overrideOptions.configSchema, } : undefined, factory: ({ node, apis, config, inputs }) => {