Server and datapackages/schema/README.md

@arachnejs/schema

Standard Schema V1 DSL for Arachne domain models — the “one schema, many outputs” layer (DB, forms, admin, OpenAPI).

Boot config stays on @arachnejs/config’s thin c.* helpers (ADR 0004). See ADR 0008.

Install #

bun add @arachnejs/schema

Example #

import { s, parse, type Infer } from "@arachnejs/schema";

const User = s.object({
  id: s.string({ min: 1 }),
  age: s.number({ min: 0, int: true }),
  role: s.enum(["admin", "user"]),
  tags: s.array(s.string()),
  nickname: s.optional(s.string()),
});

type User = Infer<typeof User>;

const user = parse(User, {
  id: "u1",
  age: 32,
  role: "admin",
  tags: ["core"],
});

Formats, coercion and cross-field rules #

const Signup = s.refine(
  s.object({
    email: s.email(),                         // trimmed, format-checked
    password: s.string({ min: 12 }),
    confirm: s.string(),
    newsletter: s.defaulted(s.coerce.boolean(), false), // "on" / "true" / "1"
  }),
  (v) => v.password === v.confirm,
  { message: "passwords do not match", path: ["confirm"] },
);

const Query = s.object({
  page: s.defaulted(s.coerce.integer({ min: 1 }), 1),   // "?page=2"
  tag: s.optional(s.coerce.array(s.string())),          // "?tag=a&tag=b"
});

const Upload = s.object({ avatar: s.file({ maxSize: 2_000_000, types: ["image/*"] }) });

Keys whose schema accepts undefined (optional, defaulted) are optional in InferInput<typeof S>; Infer<typeof S> is the parsed output.

BuilderNotes
s.string({ min, max, pattern, format, trim, lowercase })format: email, url, uuid, date-time, date
s.email() s.url() s.uuid() s.datetime() s.isoDate()format shorthands
s.number({ min, max, int }) s.integer()never NaN
s.date() s.file({ maxSize, types }) s.unknown()types accepts image/*
s.object(shape, { unknownKeys })strip (default), reject, passthrough
s.pick s.omit s.partial s.extendderive objects from a shape
s.record(value) s.array(item) s.union([...])
s.refine(schema, check, { message, path })cross-field checks
s.transform(schema, fn) s.preprocess(fn, schema)map output / input
s.describe(schema, { title, description, example })doc metadata
s.coerce.number/integer/boolean/date/arrayfor query strings and form fields

JSON Schema / OpenAPI #

import { toJSONSchema } from "@arachnejs/schema";
toJSONSchema(Signup); // JSON Schema 2020-12, used by @arachnejs/server's OpenAPI output

Foreign Standard Schemas (zod, valibot) validate fine but produce {}.

MCP #

arachne_schema_validate, arachne_schema_json_schema, arachne_schema_api_summary.