@amritk/generate-examples

中文文档
0.8.12发布于 2026/10/23MITTypeScriptES Module构建来源已验证近 30 天下载 5.9万

Generate fast-check arbitraries and example values from JSON Schemas.

原始使用文档
文档目录7 节
  1. Overview
  2. Installation
  3. Usage
  4. Lower-level API
  5. Supported keywords
  6. Known limits
  7. License

@amritk/generate-examples

Programmatic API for generating fast-check arbitraries and example values from JSON Schemas.

status  version  license  JSON Schema  node  vibe coded


Overview

@amritk/generate-examples turns a JSON Schema into test data. Where the other mjst generators give you code that consumes data at runtime (parsers, validators, types), this one closes the loop by giving you data to exercise that code with.

Each generated file exports:

  • A TypeScript type definition for the schema
  • A fast-check arbitrary (FooArbitrary) that produces schema-valid values — ideal for property-based testing
  • A concrete, self-contained example value (fooExample) — ideal for fixtures, seeds, and documentation

An index.ts barrel re-exports everything.

[!NOTE] The generated arbitraries import fast-check, so consumers need it installed (npm i -D fast-check). An arbitrary whose schema uses a keyword no fc.* combinator captures on its own (if/then/else, not, exclusive oneOf, contains, and the presence-gated object keywords — patternProperties, propertyNames, dependent*, min/maxProperties) also imports @amritk/runtime-validators for a post-generation validating filter; files that need no such filter don't. The static fooExample values have no runtime dependencies.

@amritk/runtime-validators is a dependency here rather than a peer, because this generator imports it itself. That resolves it for the generator, but not necessarily for the generated file — that file lands in your source tree, so under pnpm's strict layout or Yarn PnP it resolves from your project, not from this package's. If your schemas use any of those keywords, install it directly (npm i @amritk/runtime-validators). It cannot also be declared a peer: Bun rejects a workspace package listed as both, and --frozen-lockfile then fails for the whole repo.


Installation

npm install @amritk/generate-examples
# or
pnpm add @amritk/generate-examples
# or
yarn add @amritk/generate-examples
# or
bun add @amritk/generate-examples

Usage

import { buildExampleSchema } from '@amritk/generate-examples'

const schema = {
  type: 'object',
  properties: {
    id: { type: 'string', format: 'uuid' },
    age: { type: 'integer', minimum: 0 },
  },
  required: ['id'],
} as const

const files = await buildExampleSchema(schema, 'User')
// → [{ filename: 'user.ts', content: '...' }, { filename: 'index.ts', content: '...' }]

The generated user.ts looks like:

import * as fc from 'fast-check'

export type User = { id: string; age?: number }

export const UserArbitrary: fc.Arbitrary<User> = fc.record(
  { "id": fc.uuid(), "age": fc.integer({ min: 0 }) },
  { requiredKeys: ["id"] },
)

export const userExample: User = { "id": "00000000-0000-0000-0000-000000000000", "age": 0 }

Use the arbitrary in a property test:

import { test, fc } from '@fast-check/vitest'
import { UserArbitrary } from './generated'
import { parseUser } from './parsers'

test.prop([UserArbitrary])('parseUser round-trips any valid User', (user) => {
  expect(parseUser(user)).toEqual(user)
})

…or grab the static example as a fixture:

import { userExample } from './generated'

const res = await fetch('/users', { method: 'POST', body: JSON.stringify(userExample) })

Lower-level API

ExportDescription
buildExampleSchema(schema, rootName, suffix?)Walks the $ref graph and returns a GeneratedFile[] (one file per schema + an index.ts).
generateArbitrary(schema, typeName, suffix?, lazyRefFilenames?, rootSchema?)Returns the export const …Arbitrary source for a single schema node.
generateExampleConst(schema, typeName, rootSchema?)Returns the export const …Example source for a single schema node.
deriveExample(schema, rootSchema?)Returns a concrete, schema-valid JavaScript value (no code-generation).
serializeValue(value)Serializes a derived value to a TypeScript source expression (handles Date/bigint).

Supported keywords

type — including multi-type unions like ['string', 'null'] — (string/number/integer/boolean/null/array/object), properties, required, additionalProperties, items, prefixItems, minItems/maxItems, uniqueItems, minLength/maxLength, pattern, format, minimum/maximum, exclusiveMinimum/exclusiveMaximum, multipleOf, enum (filtered by sibling constraints), const, minProperties/maxProperties, patternProperties, propertyNames, dependentRequired, dependentSchemas, contains, minContains, allOf (merged into one schema), oneOf/anyOf, if/then/else, not, $ref, and the x-mjst extension (Date, bigint). if/then/else, not, and oneOf exclusivity are enforced by validating generated candidates against the schema and retrying/rejecting. Unsupported constructs degrade to fc.anything() in arbitraries and null in static examples.

Static examples cover every format @amritk/runtime-validators knows how to check: email, idn-email, date, date-time, time, duration, uuid, uri, iri, uri-reference, iri-reference, uri-template, json-pointer, relative-json-pointer, hostname, idn-hostname, ipv4, ipv6, regex, plus OpenAPI's url. An unrecognized format falls back to "string".


Known limits

Every fooExample is validated against its own schema before it is written. When the value does not satisfy the schema it is still emitted — so the module always compiles — but the generator prints a console.warn naming the type. Reach for FooArbitrary in those cases: the arbitrary carries a runtime validating filter and stays correct where the static value cannot.

The value falls short for three reasons:

  • The schema has no instance. { pattern: '^ab$', minLength: 5 }, uniqueItems over booleans with minItems: 3, a required key that additionalProperties: false forbids, or a oneOf whose branches every value matches twice. Nothing correct exists to emit; the warning is pointing at the schema, not the generator.
  • The constraint is beyond the deriver. pattern is sampled by a best-effort recursive-descent walk of the regex, so lookarounds and backreferences fall back to "string"; an unrecognized format does the same.
  • The bound is larger than any fixture should be. A derived string, array, or object stops growing at 10,000 characters / elements / keys, so a document asking for minLength: 50000000 yields a capped value and a warning rather than a 50 MB literal. FooArbitrary still honours the real bound.

A few more shapes worth knowing about, each of which keeps the generated file compiling rather than making it correct:

  • A schema can require a key its generated type never declares — required naming something absent from properties, a dependentRequired / dependentSchemas dependency, or a minProperties filler on an object with no index signature. The example keeps the key (a fixture missing what its schema demands is broken data) and is emitted as … as Foo, since a bare object literal with an excess property fails to compile.
  • An authored examples[0] (or, when there are no examples, the default) is used only when it satisfies its own schema. A hint that does not ({ type: 'string', default: 42 } — common in documents whose field types changed after the hint was written) is ignored in favour of a structurally derived value, because the generated type follows the schema and would reject the hint outright. const is always honoured: the type is the const's own literal type, so the two cannot disagree.
  • An unsatisfiable range (minLength: 10, maxLength: 2) collapses onto its upper bound in the arbitrary. Every bounded fc.* combinator asserts min <= max and throws at import, which would take down every other export in the file alongside it. Integer bounds are also confined to fast-check's own 32-bit range, and length/count bounds to non-negative integers.
  • A recursive definition's example has to stop somewhere and stops with null, which the non-nullable type does not admit — so it is emitted as … as unknown as Node. NodeArbitrary ties the recursion properly through fc.letrec and needs no such escape.
  • A pattern that is not a valid JavaScript regex, or that uses a lookahead or lookbehind, falls back to a plain fc.string(). fc.stringMatching compiles the pattern at module scope and cannot generate from an assertion, so honouring it would throw where the whole file becomes unusable rather than just that one arbitrary being loose.
  • Nesting deeper than 400 levels is refused with an error naming the limit. Building an arbitrary costs several stack frames per schema level, so a deeper document exhausts the stack — the cap turns that into a message that says what is wrong.

Two shapes stay impossible to generate from, and the arbitrary will retry forever if you sample it. Both are schemas with no instance, and the example warns:

  • A pattern no string of the required length can match ({ pattern: '^[a-z]{2}$', minLength: 5 }). A satisfiable-but-narrow pairing ({ pattern: '^[a-f0-9]+$', minLength: 32, maxLength: 32 }) is slow for the same reason — fc.stringMatching rarely lands on the exact length.
  • A minLength/minItems so large that no value of that size can be built.

One more gap is not this package's to close: a $ref that resolves nowhere in the document is typed by its name (Nope) but never imported, because it was never generated as a file. The arbitrary degrades to fc.anything(), but the type still names it, so the file does not compile. Same for { "type": [] }, which types as export type Foo = ;. Both come from @amritk/helpers/generate-type-definition.

[!TIP] The example for a $ref is inlined by value, so a definition graph with wide fan-out produces a correspondingly large literal. That cost is in the output size, not in generation time — each definition is derived once per document.


License

MIT

版本与发布记录

@amritk/generate-examples 共发布 40 个版本,首次发布于 2026/05/30;过去 12 个月发布了 40 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.8.12latest2026/10/2314 天前
0.8.112026/10/1027 天前
0.8.102026/09/261 个月前
0.8.92026/09/241 个月前
0.8.82026/09/231 个月前
0.8.72026/09/221 个月前

关于 @amritk/generate-examples 的常见问题

@amritk/generate-examples 是什么?

Generate fast-check arbitraries and example values from JSON Schemas.

如何安装 @amritk/generate-examples?

在项目目录运行 npm install @amritk/generate-examples,或使用 pnpm add / yarn add / bun add。

@amritk/generate-examples 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@amritk/generate-examples 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@amritk/generate-examples 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@amritk/generate-examples 可以商用吗?

@amritk/generate-examples 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@amritk/generate-examples 的下载量有多少?

根据 npm 官方下载统计,@amritk/generate-examples 在 2026/10/06 – 2026/11/04 这 30 天内被下载 59,176 次,日均约 1,973 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@amritk/generate-examples 还在维护吗?

最新版本 0.8.12 发布于 2026/10/23(14 天前);至今共发布 40 个版本,其中过去 12 个月发布了 40 个;首次发布于 2026/05/30。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。