Skip to main content

Crate cratefield_client_ts

Crate cratefield_client_ts 

Source
Expand description

The TypeScript client generator (issue #155). See the crate README for the pipeline this sits in; the short version: a /__surface document in, the files of a typed @cratefield/client package out, with no I/O anywhere in between — the caller writes what generate returns.

§cratefield-client-ts

The TypeScript client generator (issue #155): a /__surface contract in, the files of a typed @cratefield/client package out.

GET /__surface  ──►  cratefield_client_ts::generate  ──►  package.json
                                                          tsconfig.json
                                                          README.md
                                                          src/index.ts
                                                          src/runtime.ts
                                                          src/types.ts

A venture’s declared tables are already a contract — the Tables module publishes, per table, exactly the routes it serves, the audience each one is for, and the table’s own JSON Schema on every write. This crate turns that document into the TypeScript a customer imports, so the types always match the venture they were generated from: a table the contract publishes no write for gets no write method, and a table whose key is more than one column gets no single-row call at all. Compile-time absence is the point.

§Shape

The public API is one function and the result around it, mirroring cratefield_manifest::generate:

  • generate(&SurfaceDocument) — the pure step. No disk, no clock, no network; the caller writes the files.
  • GeneratedPackage — the venture, the package name, files in a stable order, and a composition_hash: sha256 over each file’s path and contents, the same construction the manifest generator uses, so a pipeline can pin and re-verify the artifact.
  • GenerateError — a contract this generator cannot honour: a document that speaks a different surface_api, no tables module in it, or a Tables action whose name, method or path does not match the verb shape the module publishes.

Determinism is the contract with the pipeline: the same document always generates byte-identical files, so the hash is stable and a republish is a no-op unless the venture changed.

§What the generated client gives the caller

Per table, an accessor on the client whose methods are exactly the actions the contract publishes — list, and wherever the contract has them, get, create, replace, delete. Typed rows from the contract’s JSON Schema; an equality-only query builder over the page (where / sort / after / page) with an async iterator that walks next to the end; a PublicClient that only reaches tables the contract marks public and an AuthenticatedClient that adds the rest; and problem+json answers surfaced as one error class carrying the stable slug. The only runtime dependency is fetch, and even that can be injected.

§The known gap

A public-read table publishes list-* and read-* and no write, and the write is where a row schema travels. So a read-only table’s surface entry carries no schema at all, and there is nothing in the contract to type its rows from. The generator does not fail over this: the table is generated with a row type of Record<string, unknown>, a TypeScript doc comment saying exactly why, and no typed filters or sort. Widen it with the venture’s own knowledge of the table instead of guessing here. The same fallback applies to any table whose contract entry carries no schema, so generation is total.

§Compilable by construction

The generated package is checked by a test in this crate: the fixture is written to a scratch directory and type-checked with tsc --strict --noEmit. The test skips itself, with the reason printed, on a machine with no TypeScript toolchain (tsc, bunx or npx), so CI without Node still passes.

// what generation is for — the customer's side:
const client = createClient({ baseUrl: venture.url, token });
const page = await client.note.where({ done: false }).sort("-created").page();

MIT. Built in the open for Cratefield, a Factory Zero venture.

Modules§

contract
The input side: the /__surface document read into the normalized Tables model — contract::extract, contract::Contract, contract::TableModel, contract::ColumnModel. Public so a pipeline (and the tests) can inspect the reading without running the emitter behind it. Reads the tables module’s entry out of a /__surface document into the small model the emitter renders.

Structs§

GeneratedFile
A generated file: a package-relative path and its full contents.
GeneratedPackage
The generated client package.

Enums§

GenerateError
Why generation refused. Every case is a contract this generator cannot honour, and every Display says what to do about it.

Functions§

composition_hash_of
The sha256 composition hash over a file set, in write order: each file’s path, a zero byte, its contents, a zero byte. Exported so a publish pipeline can recompute it from the files it wrote and compare it against what the generation recorded — the same construction cratefield_manifest::generate uses for the venture crate.
generate
Generate the files of a typed @cratefield/client package from a /__surface document.