Skip to content

Conversation

@gcanti
Copy link
Contributor

@gcanti gcanti commented Nov 19, 2025

Generating a Schema from a JSON Schema

Example (Basic object schema generation)

import { FromJsonSchema } from "effect/schema"

// a simple JSON Schema definition
const jsonSchema = {
  type: "object",
  properties: {
    a: { type: "string" },
    b: { type: "integer" }
  },
  required: ["a", "b"],
  additionalProperties: false
} as const

console.log(FromJsonSchema.generate(jsonSchema))
/*
{
  runtime: 'Schema.Struct({ "a": Schema.String, "b": Schema.Int })',
  types: {
    Type: '{ readonly "a": string, readonly "b": number }',
    Encoded: '{ readonly "a": string, readonly "b": number }',
    DecodingServices: 'never',
    EncodingServices: 'never'
  },
  imports: Set(0) {}
}
*/

Example (Using definitions referenced with $ref)

import { FromJsonSchema } from "effect/schema"

const jsonSchema = {
  type: "object",
  properties: {
    a: { type: "string" },
    b: { $ref: "#/definitions/B" }
  },
  required: ["a", "b"],
  additionalProperties: false,
  definitions: {
    B: { type: "integer" }
  }
} as const

console.log(FromJsonSchema.generate(jsonSchema))
/*
{
  runtime: 'Schema.Struct({ "a": Schema.String, "b": B })',
  types: {
    Type: '{ readonly "a": string, readonly "b": B }',
    Encoded: '{ readonly "a": string, readonly "b": B }',
    DecodingServices: 'never',
    EncodingServices: 'never'
  },
  imports: Set(0) {}
}
*/

As shown above, the generated code depends on the referenced definitions.
You can generate definitions and the main schema separately.

Example (Generating only the definitions)

import { FromJsonSchema } from "effect/schema"

const jsonSchema = {
  type: "object",
  properties: {
    a: { type: "string" },
    b: { $ref: "#/definitions/B" }
  },
  required: ["a", "b"],
  additionalProperties: false,
  definitions: {
    B: { type: "integer" }
  }
} as const

console.dir(FromJsonSchema.generateDefinitions(jsonSchema.definitions), { depth: null })
/*
[
  {
    identifier: 'B',
    generation: {
      runtime: 'Schema.Int.annotate({ "identifier": "B" })',
      types: {
        Type: 'number',
        Encoded: 'number',
        DecodingServices: 'never',
        EncodingServices: 'never'
      },
      imports: Set(0) {}
    }
  }
]
*/

You can then combine all generated parts into a single file.

Example (Producing a file that includes definitions and the main schema)

import { FromJsonSchema } from "effect/schema"

// base JSON Schema
const jsonSchema = {
  type: "object",
  properties: {
    a: { type: "string" },
    b: { $ref: "#/definitions/B" }
  },
  required: ["a", "b"],
  additionalProperties: false,
  definitions: {
    B: { type: "integer" }
  }
} as const

const schema = FromJsonSchema.generate(jsonSchema)
const definitions = FromJsonSchema.generateDefinitions(jsonSchema.definitions)

// collect all definitions plus the entry schema
const all: ReadonlyArray<FromJsonSchema.DefinitionGeneration> = [
  ...definitions,
  { identifier: "MySchema", generation: schema }
]

// build a code string containing all definitions
const code = `import { Schema } from "effect/schema"

${all.map((gen) => `const ${gen.identifier} = ${gen.generation.runtime};`).join("\n")}`

console.log(code)
/*
import { Schema } from "effect/schema"

const B = Schema.Int.annotate({ identifier: "B" });
const MySchema = Schema.Struct({ a: Schema.String, b: B });
*/

Options

target

The target option specifies which JSON Schema version is being used.

Example (Generating a tuple schema using prefixItems)

import { FromJsonSchema } from "effect/schema"

// an array defined with prefixItems from JSON Schema 2020-12
const jsonSchema = {
  type: "array",
  prefixItems: [{ type: "string" }, { type: "number" }],
  minItems: 2,
  items: false
} as const

// specify the JSON Schema version
const schema = FromJsonSchema.generate(jsonSchema, { target: "2020-12" })

console.log(schema)
/*
{
  runtime: 'Schema.Tuple([Schema.String, Schema.Number])',
  types: {
    Type: 'readonly [string, number]',
    Encoded: 'readonly [string, number]',
    DecodingServices: 'never',
    EncodingServices: 'never'
  },
  imports: Set(0) {}
}
*/

resolver

A resolver lets you control how $ref identifiers are mapped to imports and identifiers inside the generated code.

Example (Default resolver behavior)

import { FromJsonSchema } from "effect/schema"

// reference to a schema defined under components/schemas
const jsonSchema = {
  type: "object",
  properties: {
    a: { type: "string" },
    b: {
      // a reference to import { HttpApiSchemaError } from "effect/unstable/httpapi/HttpApiError"
      $ref: "#/components/schemas/effect~1HttpApiSchemaError"
    }
  },
  required: ["a", "b"],
  additionalProperties: false
} as const

const schema = FromJsonSchema.generate(jsonSchema, {
  target: "oas3.1"
})

console.log(schema)
/*
{
  runtime: 'Schema.Struct({ "a": Schema.String, "b": effect/HttpApiSchemaError })',
  types: {
    Type: '{ readonly "a": string, readonly "b": effect/HttpApiSchemaError }',
    Encoded: '{ readonly "a": string, readonly "b": effect/HttpApiSchemaError }',
    DecodingServices: 'never',
    EncodingServices: 'never'
  },
  imports: Set(0) {}
}
*/

By default, the identifier is interpreted as "effect/HttpApiSchemaError".
You can override this by providing a custom resolver.

Example (Customizing how $ref identifiers resolve)

import { FromJsonSchema } from "effect/schema"

const jsonSchema = {
  type: "object",
  properties: {
    a: { type: "string" },
    b: {
      $ref: "#/components/schemas/effect~1HttpApiSchemaError"
    },
    c: { $ref: "#/components/schemas/C" }
  },
  required: ["a", "b", "c"],
  additionalProperties: false
} as const

const schema = FromJsonSchema.generate(jsonSchema, {
  target: "oas3.1",
  resolver: (identifier) => {
    if (identifier === "effect/HttpApiSchemaError") {
      // map identifier to a direct import, with both runtime and type info
      return FromJsonSchema.makeGeneration(
        "HttpApiSchemaError",
        FromJsonSchema.makeTypes("typeof HttpApiSchemaError['Type']", "typeof HttpApiSchemaError['Encoded']"),
        new Set([`import { HttpApiSchemaError } from "effect/unstable/httpapi/HttpApiError"`])
      )
    }
    // fallback to the identity resolver
    return FromJsonSchema.resolvers.identity(identifier)
  }
})

console.log(schema)
/*
{
  runtime: 'Schema.Struct({ "a": Schema.String, "b": HttpApiSchemaError, "c": C })',
  types: {
    Type: `{ readonly "a": string, readonly "b": typeof HttpApiSchemaError['Type'], readonly "c": C }`,
    Encoded: `{ readonly "a": string, readonly "b": typeof HttpApiSchemaError['Encoded'], readonly "c": C }`,
    DecodingServices: 'never',
    EncodingServices: 'never'
  },
  imports: Set(1) {
    'import { HttpApiSchemaError } from "effect/unstable/httpapi/HttpApiError"'
  }
}
*/

This approach helps when you want full control over how a referenced schema is imported and represented in the generated code.

@github-actions
Copy link

github-actions bot commented Nov 19, 2025

📊 JSDoc Documentation Analysis

📈 Current Analysis Results
Analyzing 65 TypeScript files in packages/effect/src/ (including schema and config subdirectories)...

============================================================
         EFFECT JSDOC ANALYSIS REPORT
============================================================

📊 SUMMARY STATISTICS
------------------------------
Total files analyzed: 65
Total exported members: 2156
Missing @example: 1031 (47.8%)
Missing @category: 354 (16.4%)

🎯 TOP FILES NEEDING ATTENTION
----------------------------------------
1. schema/Schema.ts
   📝 421 missing examples, 🏷️  241 missing categories
   📦 421 total exports
2. schema/AST.ts
   📝 71 missing examples, 🏷️  13 missing categories
   📦 71 total exports
3. schema/Annotations.ts
   📝 38 missing examples, 🏷️  26 missing categories
   📦 38 total exports
4. schema/Getter.ts
   📝 52 missing examples, 🏷️  1 missing categories
   📦 52 total exports
5. schema/Transformation.ts
   📝 28 missing examples, 🏷️  17 missing categories
   📦 28 total exports
6. PubSub.ts
   📝 25 missing examples, 🏷️  16 missing categories
   📦 37 total exports
7. Config.ts
   📝 33 missing examples, 🏷️  5 missing categories
   📦 33 total exports
8. Cause.ts
   📝 28 missing examples, 🏷️  2 missing categories
   📦 69 total exports
9. Effect.ts
   📝 24 missing examples, 🏷️  1 missing categories
   📦 227 total exports
10. SynchronizedRef.ts
   📝 24 missing examples, 🏷️  0 missing categories
   📦 24 total exports
11. schema/Issue.ts
   📝 22 missing examples, 🏷️  2 missing categories
   📦 22 total exports
12. schema/Parser.ts
   📝 24 missing examples, 🏷️  0 missing categories
   📦 24 total exports
13. ConfigProvider.ts
   📝 21 missing examples, 🏷️  1 missing categories
   📦 21 total exports
14. DateTime.ts
   📝 20 missing examples, 🏷️  0 missing categories
   📦 108 total exports
15. schema/FromJsonSchema.ts
   📝 10 missing examples, 🏷️  10 missing categories
   📦 10 total exports

✅ PERFECTLY DOCUMENTED FILES
-----------------------------------
   Clock.ts (5 exports)
   Console.ts (21 exports)
   FiberHandle.ts (15 exports)
   FiberMap.ts (19 exports)
   FiberSet.ts (14 exports)
   Match.ts (57 exports)
   MutableRef.ts (17 exports)
   Random.ts (7 exports)
   RcRef.ts (5 exports)
   RegExp.ts (3 exports)
   Runtime.ts (3 exports)
   Schedule.ts (49 exports)
   Symbol.ts (1 exports)
   index.ts (0 exports)
   schema/index.ts (0 exports)

🔍 SAMPLE MISSING ITEMS FROM schema/Schema.ts
-----------------------------------
   Optionality (type, line 53): missing example, category
   Mutability (type, line 60): missing example, category
   ConstructorDefault (type, line 67): missing example, category
   MakeOptions (interface, line 75): missing example, category
   Bottom (interface, line 99): missing example, category
   declareConstructor (interface, line 179): missing example, category
   declareConstructor (function, line 203): missing example
   declare (interface, line 227): missing example
   declare (function, line 236): missing example, category
   revealBottom (function, line 256): missing example, category

📋 BREAKDOWN BY EXPORT TYPE
-----------------------------------
type: 117 missing examples, 63 missing categories
const: 389 missing examples, 101 missing categories
interface: 191 missing examples, 122 missing categories
class: 54 missing examples, 3 missing categories
function: 252 missing examples, 50 missing categories
namespace: 28 missing examples, 15 missing categories

📈 DOCUMENTATION PROGRESS
------------------------------
Examples: 1125/2156 (52.2% complete)
Categories: 1802/2156 (83.6% complete)

============================================================
Analysis complete! 1385 items need attention.
============================================================

📄 Detailed results saved to: jsdoc-analysis-results.json

This comment is automatically updated on each push. View the analysis script for details.

@github-actions
Copy link

github-actions bot commented Nov 19, 2025

Bundle Size Analysis

File Name Current Size Previous Size Difference
bundle/basic.ts 6.12 KB 6.12 KB 0.00 KB (0.00%)
bundle/batching.ts 8.37 KB 8.37 KB 0.00 KB (0.00%)
bundle/brand.ts 6.32 KB 6.32 KB 0.00 KB (0.00%)
bundle/cache.ts 9.44 KB 9.44 KB 0.00 KB (0.00%)
bundle/config.ts 16.18 KB 16.18 KB 0.00 KB (0.00%)
bundle/differ.ts 14.03 KB 14.03 KB 0.00 KB (0.00%)
bundle/http-client.ts 18.42 KB 18.42 KB 0.00 KB (0.00%)
bundle/logger.ts 8.57 KB 8.57 KB 0.00 KB (0.00%)
bundle/metric.ts 8.60 KB 8.60 KB 0.00 KB (0.00%)
bundle/optic.ts 7.58 KB 7.58 KB 0.00 KB (0.00%)
bundle/pubsub.ts 12.94 KB 12.94 KB 0.00 KB (0.00%)
bundle/queue.ts 10.85 KB 10.85 KB 0.00 KB (0.00%)
bundle/schedule.ts 9.29 KB 9.29 KB 0.00 KB (0.00%)
bundle/schema-arbitrary.ts 16.17 KB 16.17 KB 0.00 KB (0.00%)
bundle/schema-equivalence.ts 15.30 KB 15.30 KB 0.00 KB (0.00%)
bundle/schema-formatter.ts 15.25 KB 15.25 KB 0.00 KB (0.00%)
bundle/schema-json-schema.ts 16.52 KB 16.50 KB +0.03 KB (+0.16%)
bundle/schema-record-literals-key.ts 13.65 KB 13.65 KB 0.00 KB (0.00%)
bundle/schema-string-async.ts 11.17 KB 11.17 KB 0.00 KB (0.00%)
bundle/schema-string-check.ts 9.94 KB 9.94 KB 0.00 KB (0.00%)
bundle/schema-string.ts 9.52 KB 9.52 KB 0.00 KB (0.00%)
bundle/schema-template-literal.ts 12.07 KB 12.07 KB 0.00 KB (0.00%)
bundle/schema-toSerializerStringTree.ts 15.06 KB 15.06 KB 0.00 KB (0.00%)
bundle/schema.ts 14.78 KB 14.78 KB 0.00 KB (0.00%)
bundle/stm.ts 11.68 KB 11.68 KB 0.00 KB (0.00%)
bundle/stream.ts 8.30 KB 8.30 KB 0.00 KB (0.00%)

@gcanti gcanti force-pushed the schema-from-json-schema branch 2 times, most recently from fc374af to c8f16cc Compare November 22, 2025 16:40
@gcanti gcanti force-pushed the schema-from-json-schema branch from c8f16cc to ab8bc6a Compare November 22, 2025 18:46
@gcanti gcanti force-pushed the schema-from-json-schema branch from ab8bc6a to 39c7625 Compare November 22, 2025 18:47
@dcdq
Copy link

dcdq commented Nov 27, 2025

oh my god

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants