@bezoar/schema

中文文档
0.0.6发布于 2026/11/03MITTypeScriptES Module构建来源已验证近 30 天下载 1757

The bezoar contract: the query model, the type table, and runtime validation

原始使用文档
文档目录7 节
  1. Install
  2. The contract
  3. The type table
  4. defineSchema
  5. Kinds
  6. Types
  7. Related

@bezoar/schema

Types and runtime validation for the bezoar contract. The validation uses zod schemas built from the contract.

Install

pnpm add @bezoar/schema

The contract

A definition declares the contract. @bezoar/kit builds one from the SQL of a database package. The definition is plain data and holds no SQL.

  • enums maps each enum type of the database to its labels, in declaration order.
  • queries maps each query name, such as events.list, to its statement.
  • A statement has its slots and its columns. A statement with no column returns no rows: a write without RETURNING.
  • A slot is a named parameter of the SQL. A column is a field of each row. Each has a name, a type, and a nullable flag. The slots are in position order: the slot at index i binds $i+1.
  • A type is a scalar of the type table, an enum of the definition, or either with the [] suffix.
  • tables maps each table and view of the database to its columns, by its bare name when the search path finds it and by schema.name otherwise. A table column has a name, a type, a nullable flag, and a generated flag: whether the database can fill it, through a default, an identity, or a generation expression. A table column of a type the type table does not have is unknown. A table has a kind: table, or view for one that is read and never written.
  • roles maps each role of the database to the names of the queries it has the privileges to run, as the kit found them. The member is optional: a contract built before roles existed has none, and Role<D> is then never.
const contract = {
  enums: { event_day: ["sunday", "monday" /* … */] },
  queries: {
    "events.list": {
      slots: [
        { name: "day", type: "event_day", nullable: false },
        { name: "location", type: "text", nullable: true },
      ],
      columns: [
        { name: "id", type: "int4", nullable: false },
        { name: "title", type: "text", nullable: false },
        { name: "weeks", type: "int4[]", nullable: true },
      ],
    },
  },
  tables: {
    events: {
      kind: "table",
      columns: [
        { name: "id", type: "int4", nullable: false, generated: true },
        { name: "title", type: "text", nullable: false, generated: false },
        { name: "weeks", type: "int4[]", nullable: true, generated: false },
      ],
    },
  },
} as const satisfies Definition;

The types of the arguments and the rows derive from the definition. A required slot is a required member. A nullable slot is optional and also takes null. A nullable column also holds null.

type D = typeof contract;

Arguments<D, "events.list">; // { day: "sunday" | "monday" | …; location?: string | null }
Row<D, "events.list">; // { id: number; title: string; weeks: (number | null)[] | null }
Returns<D, "events.list">; // Row<D, "events.list">[]
Returns<D, "events.touch">; // number, for a query with no column
Database<D>; // { events: { id: Generated<number>; title: string; weeks: (number | null)[] | null } }
Role<D>; // the role names of the contract: "web" | "admin"
RoleQuery<D, "web">; // the queries the role may run: "events.list" | …

Database<D> is the type parameter of a kysely Kysely over the tables of the definition. A generated column is Generated, so an insert can leave it out. A column of a view is a ColumnType that selects and never inserts or updates. @bezoar/core builds the query builder of a service over this type.

The type table

One table maps each Postgres type to its JavaScript form. Scalars declares the type the kit emits. TABLE holds the zod schema for each, and the compiler holds the two to each other.

PostgresJavaScriptSchema
boolbooleanz.boolean()
int2, int4, int8numberz.int(), so an unsafe int8 fails
float4, float8numberAny number, so NaN and the infinities pass
numericstringz.string(), so no digit is lost
text, varchar, bpcharstringz.string()
uuidstringz.uuid()
date, timestringz.iso.date(), z.iso.time()
timetz, timestamp, intervalstringz.string(), as Postgres writes them
timestamptzDatez.date()
json, jsonbunknownz.json(), a JSON value
byteaUint8Arrayz.instanceof(Uint8Array)

An enum is z.enum over its labels. An array is z.array of its element, with a null element allowed, since Postgres allows one. Postgres does not type the dimensions of an array, so the schema of an array is one-dimensional.

A driver parses the text of Postgres to these types. A time without a zone has no instant, so the temporal types other than timestamptz stay strings.

defineSchema

defineSchema(definition) returns a Schema<D>. The schema validates untrusted data against the vocabulary of the definition. Data that passes is bound to the contract.

import { defineSchema } from "@bezoar/schema";

const schema = defineSchema(contract);

// Returns a boolean. The type of the value narrows on `true`.
if (!schema.check.query(name)) throw new Error("not a query");

// Throws a SchemaError that lists every issue.
schema.assert.arguments("events.list", await readBody(event));

// Throws on an invalid value. Returns the value with its narrowed type.
const row = schema.parse.row("events.list", candidate);

// Returns { success: true, data } or { success: false, issues }.
const result = schema.inspect.definition(candidate);

The function checks the definition before it reads it. An invalid definition throws when you call defineSchema.

The Schema<D> has these members:

  • base is the definition.
  • validators holds the zod schemas: definition, query, bundle, and for each query under queries its arguments and its row. Use them where a zod schema is wanted, such as a route handler. z.infer of a schema is the type the service uses.
  • check has one boolean predicate for each kind.
  • assert has one assertion for each kind. A failed assertion throws a SchemaError that holds every Issue.
  • parse asserts a value and returns it with the type of its kind.
  • inspect returns a Result for each kind.

Kinds

Each member of check, assert, parse, and inspect validates one kind. A kind of a query takes the query name first.

  • definition is a complete definition.
  • query is a query name of the contract.
  • bundle is the SQL bundle: the text of every query of the contract, by name, and no other.
  • arguments(query, value) is an object with a member for each slot of the query. A required slot must be present and not null. A nullable slot can be absent, null, or a value. A key the query does not name is an issue.
  • row(query, value) is an object with a member for each column of the query and no other. A nullable column holds a value or null.
schema.assert.arguments("events.list", { day: "sunday" }); // passes
schema.assert.arguments("events.list", { day: "someday" }); // SchemaError: day: Invalid option …
schema.check.row("events.list", await res.json()); // true when the row fits the columns

Types

  • Definition is the contract. Statement, Slot, Column, Table, TableColumn, and TableKind are its parts.
  • Query<D>, Enum<D>, and Label<D, E> are names from a definition. RowQuery<D> is the name of a query with a column. Type<D> is a type name the definition can express.
  • Scalars maps each scalar to its JavaScript type. Scalar is the union of its keys. Value<D, T> is the JavaScript type for a type name.
  • Arguments<D, Q>, Row<D, Q>, and Returns<D, Q> are the shapes of a call of the query Q. Returns is the rows of a query with a column and the count of one without.
  • Database<D> is the tables of a definition as kysely types them.
  • Bundle<D> is the SQL bundle of a contract.
  • Validators<D> is the shape of the zod schemas.
  • Schema<D> is the bundle that defineSchema returns. Check<D>, Assert<D>, Parse<D>, and Inspect<D> are its families of functions.
  • Result<V> is the outcome of inspect.
  • Issue is a validation failure, as zod reports it.
  • SchemaError is the error that assert and parse throw. It holds the Issue list.
  • @bezoar/core is the runtime service that uses this schema.
  • @bezoar/kit builds a definition from SQL.

版本与发布记录

@bezoar/schema 共发布 8 个版本,首次发布于 2026/10/18;过去 12 个月发布了 8 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.0.6latest2026/11/037 天前
0.0.52026/10/2416 天前
0.0.42026/10/1922 天前
0.0.32026/10/1822 天前
0.0.22026/10/1823 天前
0.0.12026/10/1823 天前

关于 @bezoar/schema 的常见问题

@bezoar/schema 是什么?

The bezoar contract: the query model, the type table, and runtime validation

如何安装 @bezoar/schema?

在项目目录运行 npm install @bezoar/schema,或使用 pnpm add / yarn add / bun add。

@bezoar/schema 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@bezoar/schema 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@bezoar/schema 可以商用吗?

@bezoar/schema 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@bezoar/schema 的下载量有多少?

根据 npm 官方下载统计,@bezoar/schema 在 2026/10/08 – 2026/11/06 这 30 天内被下载 1,757 次,日均约 59 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@bezoar/schema 还在维护吗?

最新版本 0.0.6 发布于 2026/11/03(7 天前);至今共发布 8 个版本,其中过去 12 个月发布了 8 个;首次发布于 2026/10/18。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。