# @asterql/schema

```sh
npm install @asterql/schema
```

TypeNode schemas for AsterQL data shaping and code generation.

This package is intentionally small. It gives the rest of the AsterQL stack a
plain JSON-like type graph that can represent practical TypeScript-shaped JSON,
including recursive and reused shapes through named references.

```ts
import {
  arrayType,
  defineSchema,
  objectType,
  optionalProp,
  refType,
  stringType,
} from "@asterql/schema";

const schema = defineSchema({
  root: refType("Category"),
  definitions: {
    Category: objectType({
      id: stringType(),
      title: stringType(),
      parent: optionalProp(refType("Category")),
      children: arrayType(refType("Category")),
    }),
  },
});
```

## Model

```ts
type TypeSchema = {
  root: TypeNode;
  definitions: Record<string, TypeNode>;
};
```

`refType("Name")` points at `definitions.Name`. References are never expanded
during normalization, which keeps recursive graphs finite and deterministic.

## API

- `defineSchema(input)`: normalize a schema and verify all refs are defined.
- `normalizeTypeNode(node)`: canonicalize a TypeNode.
- `serializeTypeNode(node)`: stable JSON string for a TypeNode.
- `serializeSchema(schema)`: stable JSON string for a schema.
- `collectRefs(node)`: collect named refs from a TypeNode.
- Builders: `unknownType`, `neverType`, `nullType`, `booleanType`,
  `numberType`, `stringType`, `literalType`, `arrayType`, `objectType`,
  `prop`, `optionalProp`, `unionType`, `intersectionType`, `recordType`,
  `tupleType`, `refType`, and `nullableType`.

## Why named refs?

AsterQL codegen needs to preserve shape identity. If two fields point at the
same named type, codegen should know that. If a type points at itself, codegen
must not expand forever. Named refs keep those cases simple and explicit.
