diff --git a/.changeset/bright-keys-remember.md b/.changeset/bright-keys-remember.md new file mode 100644 index 00000000000..3f850346770 --- /dev/null +++ b/.changeset/bright-keys-remember.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +Preserve broad index constraints when generating JSON Schema pattern properties. diff --git a/packages/effect/src/internal/schema/toJsonSchemaDocument.ts b/packages/effect/src/internal/schema/toJsonSchemaDocument.ts index 509439970c5..d8d6faba0a5 100644 --- a/packages/effect/src/internal/schema/toJsonSchemaDocument.ts +++ b/packages/effect/src/internal/schema/toJsonSchemaDocument.ts @@ -369,6 +369,7 @@ function compileJsonSchema( if (representation.propertySignatures.length > 0) out.properties = properties if (required.length > 0) out.required = required out.additionalProperties = options?.additionalProperties ?? false + let hasBroadIndexSignature = false const patternProperties: Record = {} for (let index = 0; index < representation.indexSignatures.length; index++) { const signature = representation.indexSignatures[index] @@ -383,6 +384,7 @@ function compileJsonSchema( new Set() ) if (patterns.length === 0) { + hasBroadIndexSignature = true out.additionalProperties = type } else { for (const pattern of patterns) InternalRecord.assignProperty(patternProperties, pattern, type) @@ -390,7 +392,7 @@ function compileJsonSchema( } if (Object.keys(patternProperties).length > 0) { out.patternProperties = patternProperties - delete out.additionalProperties + if (!hasBroadIndexSignature) delete out.additionalProperties } if ( typeof out.additionalProperties === "object" && diff --git a/packages/effect/test/schema/representation/toJsonSchemaDocument.test.ts b/packages/effect/test/schema/representation/toJsonSchemaDocument.test.ts index 15d9906e5fc..33940328301 100644 --- a/packages/effect/test/schema/representation/toJsonSchemaDocument.test.ts +++ b/packages/effect/test/schema/representation/toJsonSchemaDocument.test.ts @@ -252,6 +252,31 @@ describe("SchemaRepresentation.toJsonSchemaDocument", () => { { type: "object" } ) }) + + it("retains a broad index constraint alongside patternProperties", () => { + const output = compile({ + _tag: "Objects", + propertySignatures: [], + indexSignatures: [ + { parameter: StringRepresentation, type: NumberRepresentation }, + { + parameter: { + _tag: "TemplateLiteral", + parts: [ + { _tag: "Literal", literal: "x_", checks: [] }, + StringRepresentation + ], + checks: [] + }, + type: StringRepresentation + } + ], + checks: [] + }) + + assert.isDefined(output.patternProperties) + assert.isDefined(output.additionalProperties) + }) }) describe("unions", () => {