Contributing to z-schema
z-schema is a JSON Schema validator (draft-04 through draft-2020-12) written in TypeScript. This skill covers the development workflow, codebase navigation, and common contribution tasks.
Repository setup
git clone --recursive https://github.com/zaggino/z-schema.git
cd z-schema
npm install
If already cloned without --recursive (needed for the json-schema-spec/ submodule):
git submodule update --init --recursive
Quality checks
Run all checks before pushing:
npm run lint:check # ESLint
npm run format:check # Prettier
npm run build # TypeScript + Rollup
npm run build:tests # Type-check tests
npm test # Vitest (node + browser)
Pre-commit hooks auto-run lint + format on staged files. Pre-push hooks run build + type-check.
Codebase map
src/
index.ts → Public API (all exports)
z-schema.ts → Factory + ZSchema/ZSchemaSafe/ZSchemaAsync/ZSchemaAsyncSafe
z-schema-base.ts → Core validation orchestration
schema-compiler.ts → $ref resolution, id collection, schema compilation
schema-validator.ts → Schema-level validation against meta-schemas
json-validation.ts → Validation orchestration (validate, recurse*, collectEvaluated)
validation/ → Keyword validators split by category
shared.ts → Shared types (JsonValidatorFn), vocab helpers, caching utilities
type.ts → type, enum, const validators
numeric.ts → multipleOf, minimum, maximum, exclusiveMin/Max validators
string.ts → minLength, maxLength, pattern, format, content* validators
array.ts → items, prefixItems, contains, min/maxItems, uniqueItems validators
object.ts → properties, patternProperties, additionalProperties, required, etc.
combinators.ts → allOf, anyOf, oneOf, not, if/then/else validators
ref.ts → $dynamicRef/$recursiveRef resolution helpers
schema-cache.ts → Schema caching by URI/id
errors.ts → Error codes (Errors object) + ValidateError class
format-validators.ts → Built-in + custom format validators
report.ts → Error accumulation (Report, SchemaErrorDetail)
json-schema.ts → Common JSON Schema definitions + helpers
json-schema-versions.ts → Draft-specific type unions + version mappings
z-schema-options.ts → Options interface + defaults + normalizeOptions
z-schema-reader.ts → Schema reader type
z-schema-versions.ts → Registers bundled meta-schemas into cache
utils/ → Pure utilities (array, clone, json, uri, etc.)
schemas/ → Bundled meta-schemas (generated at build time)
Validation pipeline
- Schema compilation (
schema-compiler.ts): resolves$ref, collectsid/$id, registers in cache - Schema validation (
schema-validator.ts): validates schema against its meta-schema - JSON validation (
json-validation.ts+validation/*.ts): validates data against compiled schema — type checks, constraints, combiners (allOf/anyOf/oneOf/not),unevaluated*tracking, format checks. Keyword validators are split into modules undervalidation/by category - Report (
report.ts): errors accumulate in aReport, then convert toValidateError
Common tasks
Adding a new error code
- Add the error to the
Errorsobject insrc/errors.ts:MY_NEW_ERROR: 'Description with {0} placeholder', - Use
report.addError('MY_NEW_ERROR', [param])in the validation logic. - Write tests verifying the error code is produced.
Adding a new format validator
- Write the validator function in
src/format-validators.ts:const myFormatValidator: FormatValidatorFn = (input: unknown) => { if (typeof input !== 'string') return true; return /^pattern$/.test(input); }; - Register it in the
inbuiltValidatorsrecord:const inbuiltValidators = { // ...existing 'my-format': myFormatValidator, }; - Add tests in
test/spec/format-validators.spec.ts.
Adding a new option
- Add the option to
ZSchemaOptionsinsrc/z-schema-options.ts. - Add a default value in
defaultOptions. - If the option is part of
strictMode, add it to thestrictModeblock innormalizeOptions. - Document it in
docs/options.md. - Write tests.
Implementing a new JSON Schema keyword
- Add validation logic in the appropriate
src/validation/*.tsmodule (e.g.,array.tsfor array keywords,object.tsfor object keywords,combinators.tsfor applicators) orsrc/schema-validator.ts(for schema-level validation). The orchestration layer insrc/json-validation.tscalls into these modules. - Guard with a draft version check if the keyword is draft-specific.
- Remove relevant entries from
excludedFiles/excludedTestsintest/spec/json-schema-test-suite.common.ts. - Run the JSON Schema Test Suite to confirm compliance:
npx vitest run --silent=false --project node -t "draft2020-12/newKeyword" - Export any new types through
src/index.ts.
Modifying existing behavior
- Find the relevant module using the codebase map above.
- Make changes following code conventions (see below).
- Run the full test suite — regressions often appear in other drafts.
Test framework
- Vitest with
globals: true. - Two projects: node and browser (Playwright: Chromium, Firefox, WebKit).
- Tests live in
test/spec/.
File naming
| Suffix | Runs in |
|---|---|
*.spec.ts | Both node and browser |
*.node-spec.ts | Node only |
*.browser-spec.ts | Browser only |
Running tests
npm test # all
npm run test:node # node only
npx vitest run --silent=false --project node -t "draft4/type" # single test
npm run test:coverage # coverage
Test pattern
import { ZSchema } from '../../src/z-schema.ts';
describe('Feature Name', () => {
it('should accept valid data', () => {
const validator = ZSchema.create();
expect(validator.validate('hello', { type: 'string' })).toBe(true);
});
it('should reject invalid data', () => {
const validator = ZSchema.create();
const { valid, err } = validator.validateSafe(42, { type: 'string' });
expect(valid).toBe(false);
expect(err?.details?.[0]?.code).toBe('INVALID_TYPE');
});
});
JSON Schema Test Suite
Official test cases loaded via test/spec/json-schema-test-suite.common.ts. To enable tests for a newly implemented feature, remove entries from excludedFiles / excludedTests and confirm they pass.
Code conventions
- TypeScript
strict: true, ESM with.jsimport extensions insrc/ .tsimport extensions intest/(viaallowImportingTsExtensions)import typefor type-only imports (enforced by ESLint)- Import order: type-only → side-effect → node builtins → packages → relative
- Prettier: 120 char width, single quotes, trailing commas (es5), semicolons
- Classes/types:
PascalCase— functions/variables:camelCase— errors:UPPER_SNAKE_CASE - All public API exported through
src/index.ts - Internal types stay unexported
- Schemas in
src/schemas/are generated byscripts/copy-schemas.mts— do not edit manually json-schema-spec/is a git submodule — do not commit changes to it
PR checklist
- [ ] Branch from `main`
- [ ] Changes follow code conventions
- [ ] npm run lint:check passes
- [ ] npm run format:check passes
- [ ] npm run build passes
- [ ] npm run build:tests passes
- [ ] npm test passes (node + browser)
- [ ] New public types/values exported through src/index.ts
- [ ] New features have tests
- [ ] docs/ updated if public API changed
- [ ] JSON Schema Test Suite entries un-excluded if applicable
Reference files
- references/architecture-details.md — Full module dependency diagram, factory pattern, build outputs, and internal types