| name | contributing-to-z-schema |
| description | Guides contributors through the z-schema codebase, PR workflow, and common development tasks. Use when the user wants to contribute to z-schema, add a new feature or keyword, add an error code, add a format validator, modify options, write tests, run the test suite, fix a failing test, understand the validation pipeline, navigate the source code architecture, or submit a pull request. Also use when someone mentions contributing, PRs, the z-schema source code, or the JSON Schema Test Suite integration. |
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
npm run format:check
npm run build
npm run build:tests
npm test
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, collects id/$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 under validation/ by category
- Report (
report.ts): errors accumulate in a Report, then convert to ValidateError
Common tasks
Adding a new error code
- Add the error to the
Errors object in src/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
inbuiltValidators record:
const inbuiltValidators = {
'my-format': myFormatValidator,
};
- Add tests in
test/spec/format-validators.spec.ts.
Adding a new option
- Add the option to
ZSchemaOptions in src/z-schema-options.ts.
- Add a default value in
defaultOptions.
- If the option is part of
strictMode, add it to the strictMode block in normalizeOptions.
- Document it in
docs/options.md.
- Write tests.
Implementing a new JSON Schema keyword
- Add validation logic in the appropriate
src/validation/*.ts module (e.g., array.ts for array keywords, object.ts for object keywords, combinators.ts for applicators) or src/schema-validator.ts (for schema-level validation). The orchestration layer in src/json-validation.ts calls into these modules.
- Guard with a draft version check if the keyword is draft-specific.
- Remove relevant entries from
excludedFiles/excludedTests in test/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
npm run test:node
npx vitest run --silent=false --project node -t "draft4/type"
npm run test: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 .js import extensions in src/
.ts import extensions in test/ (via allowImportingTsExtensions)
import type for 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 by scripts/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