Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

`Schema.toStandardJSONSchemaV1` output has no `$schema`

Closed
#9,763 0 comments 0 reactions 1 assignee View on GitHub

Maintainers usually reply within 1 day

@gcanti is already working on this.

Since Oct 22, 2026.

  • #9772 by @MichaelDeBoey — open

Assessment

This issue has not been assessed yet.

Description

What is the problem this feature would solve?

Schema.toStandardJSONSchemaV1 returns a schema without a root $schema for both supported targets ([email protected]):

import { Schema } from "effect"

const Point = Schema.Tuple([Schema.String, Schema.Boolean])
const { jsonSchema } = Schema.toStandardJSONSchemaV1(Point)["~standard"]

console.log(JSON.stringify(jsonSchema.input({ target: "draft-2020-12" })))
console.log(JSON.stringify(jsonSchema.input({ target: "draft-07" })))
{"type":"array","prefixItems":[{"type":"string"},{"type":"boolean"}],"maxItems":2,"minItems":2}
{"type":"array","maxItems":2,"minItems":2,"items":[{"type":"string"},{"type":"boolean"}]}

Code that receives one of these schemas later, such as a validator or a document generator that collects schemas from several libraries, can't tell from the schema itself which draft it is. That matters for keywords whose meaning changed between drafts: array-form items is a tuple in draft-07 but invalid in draft 2020-12, where prefixItems replaces it.

The other Standard JSON Schema implementations write $schema for both targets ([email protected], [email protected], [email protected] through toStandardJsonSchema from @valibot/[email protected], [email protected]), here for the same tuple:

arktype draft-2020-12 https://json-schema.org/draft/2020-12/schema
arktype draft-07 http://json-schema.org/draft-07/schema#
sury draft-2020-12 https://json-schema.org/draft/2020-12/schema
sury draft-07 http://json-schema.org/draft-07/schema#
valibot draft-2020-12 https://json-schema.org/draft/2020-12/schema
valibot draft-07 http://json-schema.org/draft-07/schema#
zod draft-2020-12 https://json-schema.org/draft/2020-12/schema
zod draft-07 http://json-schema.org/draft-07/schema#

Effect 3's JSONSchema.make writes it too ([email protected]):

import { JSONSchema, Schema } from "effect"

const Point = Schema.Tuple(Schema.String, Schema.Boolean)
console.log(JSON.stringify(JSONSchema.make(Point, { target: "jsonSchema7" })))
console.log(JSON.stringify(JSONSchema.make(Point, { target: "jsonSchema2020-12" })))
{"$schema":"http://json-schema.org/draft-07/schema#","type":"array","minItems":2,"items":[{"type":"string"},{"type":"boolean"}],"additionalItems":false}
{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"array","minItems":2,"prefixItems":[{"type":"string"},{"type":"boolean"}],"items":false}

It does so in make:

https://github.com/Effect-TS/effect/blob/57b747f0cf4ddec47f23c08553c9e7011e31f8a4/packages/effect/src/JSONSchema.ts#L277-L281

In Effect 4, Schema.toJsonSchemaDocument keeps the draft in Document.dialect, so the information exists. It is lost in the Standard JSON Schema adapter, which returns only document.schema, in toBaseStandardJSONSchemaV1:

https://github.com/Effect-TS/effect/blob/110fd22e6555b86301d2e89997386b8c4b71fad3/packages/effect/src/internal/schema/standardSchema.ts#L96-L109

What is the feature you are proposing to solve the problem?

Set the root $schema in toBaseStandardJSONSchemaV1, using the URIs effect/JsonSchema already exports as META_SCHEMA_URI_DRAFT_07 and META_SCHEMA_URI_DRAFT_2020_12.

Setting it once on the draft 2020-12 schema may be enough, because JsonSchema.toDocumentDraft07 already rewrites a root draft 2020-12 $schema to the draft-07 URI in convertMetaSchemaKeyword:

import { JsonSchema, Schema } from "effect"

const doc = Schema.toJsonSchemaDocument(Schema.Tuple([Schema.String, Schema.Boolean]))
const withUri = { ...doc, schema: { $schema: JsonSchema.META_SCHEMA_URI_DRAFT_2020_12, ...doc.schema } }
console.log(JSON.stringify(JsonSchema.toDocumentDraft07(withUri).schema))
{"$schema":"http://json-schema.org/draft-07/schema#","type":"array","maxItems":2,"minItems":2,"items":[{"type":"string"},{"type":"boolean"}]}
What alternatives have you considered?

The caller of input / output knows which target it asked for and can add $schema itself. That only helps when the same code later reads the schema; once the schema is passed on, the target is gone.

The Standard JSON Schema spec doesn't require $schema, so this is a request for consistency with Effect 3 and the other implementations rather than a spec violation.

Dominant language
TypeScript
Stars
16.7k
Forks
808
Avg merge
10h 13m
Merged PRs (30d)
800

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Effect-TS/effect

All issues in Effect-TS/effect

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.