diff --git a/ark/json-schema/__tests__/string.test.ts b/ark/json-schema/__tests__/string.test.ts index 1ff8258b11..ead57c8885 100644 --- a/ark/json-schema/__tests__/string.test.ts +++ b/ark/json-schema/__tests__/string.test.ts @@ -1,5 +1,6 @@ import { attest, contextualize } from "@ark/attest" import { jsonSchemaToType } from "@ark/json-schema" +import type { JsonSchemaOrBoolean } from "@ark/schema" contextualize(() => { it("type string", () => { @@ -47,6 +48,17 @@ contextualize(() => { ) }) + it("minLength (0)", () => { + const schema = { + type: "string", + minLength: 0 + } satisfies JsonSchemaOrBoolean + const pattern = jsonSchemaToType(schema) + + attest(() => jsonSchemaToType(schema)) + attest(pattern.expression).snap("string") + }) + it("pattern", () => { const tPatternString = jsonSchemaToType({ type: "string", @@ -57,4 +69,15 @@ contextualize(() => { // https://json-schema.org/draft-07/draft-handrews-json-schema-validation-01#rfc.section.4.3 attest(tPatternString.allows("expression")).equals(true) }) + + it("string enums", () => { + const enumKeys = ["keyOne", "keyTwo"] + + const stringEnums = jsonSchemaToType({ + type: "string", + enum: enumKeys + }) + + attest(stringEnums.expression).snap('"keyOne" | "keyTwo"') + }) }) diff --git a/ark/json-schema/common.ts b/ark/json-schema/common.ts index 9f7915507e..51324bdf12 100644 --- a/ark/json-schema/common.ts +++ b/ark/json-schema/common.ts @@ -12,5 +12,5 @@ export const parseCommonJsonSchema = ( return type.unit(jsonSchema.const) } - if ("enum" in jsonSchema) return type.enumerated(jsonSchema.enum) + if ("enum" in jsonSchema) return type.enumerated(...jsonSchema.enum) } diff --git a/ark/schema/__tests__/bounds.test.ts b/ark/schema/__tests__/bounds.test.ts index a6efffaae8..485aea89da 100644 --- a/ark/schema/__tests__/bounds.test.ts +++ b/ark/schema/__tests__/bounds.test.ts @@ -98,6 +98,11 @@ contextualize(() => { ) }) + it("minLength 0 reduces to unconstrained", () => { + const T = rootSchema({ domain: "string", minLength: 0 }) + attest(T.expression).snap("string") + }) + for (const [min, max] of entriesOf(boundKindPairsByLower)) { describe(`${min}/${max}`, () => { const basis = diff --git a/ark/schema/constraint.ts b/ark/schema/constraint.ts index 4f23842b97..a9640277f4 100644 --- a/ark/schema/constraint.ts +++ b/ark/schema/constraint.ts @@ -145,6 +145,9 @@ export const constraintKeyParser = return nodes.sort((l, r) => (l.hash < r.hash ? -1 : 1)) as never } const child = ctx.$.node(kind, schema) + // If the constraint was reduced to a root node (like unknown for minLength: 0), + // omit it from the schema since it's trivially satisfied + if (child.isRoot()) return return (child.hasOpenIntersection() ? [child] : child) as never } diff --git a/ark/schema/roots/intersection.ts b/ark/schema/roots/intersection.ts index d2f8060fbc..2ba2b74ccc 100644 --- a/ark/schema/roots/intersection.ts +++ b/ark/schema/roots/intersection.ts @@ -426,7 +426,7 @@ const writeIntersectionExpression = (node: Intersection.Node) => { .map(n => n.expression) .join(" & ") - const fullExpression = `${basisExpression}${basisExpression ? " " : ""}${refinementsExpression}` + const fullExpression = `${basisExpression}${basisExpression && refinementsExpression ? " " : ""}${refinementsExpression}` if (fullExpression === "Array == 0") return "[]" diff --git a/ark/type/__tests__/pipe.test.ts b/ark/type/__tests__/pipe.test.ts index 4b536d6128..721168c467 100644 --- a/ark/type/__tests__/pipe.test.ts +++ b/ark/type/__tests__/pipe.test.ts @@ -895,7 +895,7 @@ contextualize(() => { attest(indiscriminable).throws .snap(`ParseError: An unordered union of a type including a morph and a type with overlapping input is indeterminate: -Left: { foo: (In: string ) => Out | false | true } +Left: { foo: (In: string) => Out | false | true } Right: { foo: (In: string) => Out<{ [string]: $jsonObject | number | string | false | null | true }> | false | true }`) })