# toolcraft-schema

Zero-dependency schema builder for typed command inputs, runtime validation,
and JSON Schema generation.

## Features

- Zero runtime dependencies
- Typed schema descriptors
- `Static<typeof schema>` type inference
- Runtime validation with `validateValue()`
- JSON Schema serialization via `toJsonSchema()`
- JSON Schema document serialization via `toJsonSchemaDocument()`
- Native JSON Schema compilation and property projection with reference support

## Usage

```ts
import { S, toJsonSchema, toJsonSchemaDocument, validateValue } from "toolcraft-schema";
import type { Static } from "toolcraft-schema";

const schema = S.Object({
  name: S.String({ description: "User name", minLength: 1 }),
  retries: S.Optional(S.Number({ default: 3, minimum: 0, jsonType: "integer" })),
  mode: S.Enum(["fast", "safe"] as const, { default: "safe" }),
  tags: S.Array(S.String(), { default: [], maxItems: 5 })
});

type Input = Static<typeof schema>;
// {
//   name: string;
//   retries?: number;
//   mode: "fast" | "safe";
//   tags: string[];
// }

const jsonSchema = toJsonSchema(schema);
const document = toJsonSchemaDocument(schema, {
  id: "https://example.test/schema.json",
  title: "Example schema"
});
const validation = validateValue(schema, {
  name: "Ada",
  mode: "safe",
  tags: []
});
```

## API

### Builders

- `S.String({ description?, default?, short?, cliAliases? })`
- `S.Number({ description?, default?, short?, cliAliases? })`
- `S.Boolean({ description?, default?, short?, cliAliases? })`
- `S.Enum(values, { description?, default?, short?, cliAliases? })`
- `S.Array(itemSchema, { description?, default?, short?, cliAliases? })`
- `S.Record(valueSchema, { description?, default? })`
- `S.Union([schemaA, schemaB], { description?, default? })`
- `S.OneOf([schemaA, schemaB], { description?, default? })`
- `S.Object({ [key]: schema })`
- `S.Optional(schema)`

### Type helpers

- `Static<typeof schema>` infers the runtime TypeScript shape for a schema descriptor.
- Object properties wrapped in `S.Optional(...)` become optional properties in `Static`.
- Schemas declared with `nullable: true` infer `null` in `Static` and emit `nullable: true` in JSON Schema.

### JSON Schema generation

- `toJsonSchema(schema)` converts any schema descriptor to standard JSON Schema.
- `toJsonSchemaDocument(schema, options)` wraps `toJsonSchema(schema)` in a full JSON Schema document with `$schema`, optional `$id`, `title`, and `description`.
- Object properties not wrapped in `S.Optional(...)` are emitted in `required`.
- Defaults provided to schema builders are emitted as JSON Schema `default` and must satisfy the schema.
- String, number, and array constraints are emitted as JSON Schema validation keywords.
- Nested `S.Object(...)` schemas produce nested JSON Schema objects. Object schemas default to `additionalProperties: false`; pass `additionalProperties: true` to allow unknown keys.
- `S.Enum(...)` rejects empty or duplicate values at runtime for JavaScript callers.
- Invalid builder configuration fails fast, including invalid regex patterns, negative lengths/counts, inverted min/max pairs, non-finite numeric bounds, and integer schemas with non-integer defaults.

### Runtime validation

- `validateValue(schema, value)` returns `{ ok: true, value }` for valid input.
- Invalid input returns `{ ok: false, issues }` with path-aware diagnostics.
- Validation applies defaults from schema descriptors.

`compileJsonSchema(document, options)` validates complete native JSON Schema
documents. `projectJsonSchemaProperties(document, options)` supplies stable
property names, unconditional required metadata, resolved schema annotations,
and candidate validators for CLI generation. It follows references, compositions,
embedded resource IDs and conditional declarations using the same compiler.
Property candidate validators accept values matching at least one declaration;
validate the complete object to enforce branch-dependent and combined constraints.
Both functions accept a `registry` for external schema documents without network
fetching.

`isJsonValue(value, options)` checks JSON data without invoking getters or
serialization hooks. It rejects cycles, sparse arrays, non-finite numbers and
non-JSON prototypes. Defaults bound the tree to 10,000 nodes and depth 64.
Use `maxNodes` for larger bounded documents, or `maxDepth` (0–256) to choose a
depth budget. Shared objects count once for each occurrence in the JSON tree;
options never change other calls' budgets.

## Environment Variables

This package exposes no environment variables.

## Configuration

This package currently exposes no package-level configuration options.
