This guide is for people maintaining this repository. To install and use the published library, see the package guide. To prepare a pull request, see the contribution guide.
The published package supports Node.js 24 or later. Repository development is
verified with Node.js 24.18.0, recorded in
.node-version, and pnpm 11.9.0, pinned by the root
packageManager field. The workspace is ESM, uses Vitest, and generates
Protobuf-ES v2 schemas.
GitHub Actions verifies the repository on Ubuntu. Its POSIX-oriented scripts are suitable for Linux and macOS, and for Windows through WSL. Native Windows is not CI-verified. This is development-environment guidance, not a restriction on the published package’s Node.js runtime support.
| Surface | Supported | Verified here |
|---|---|---|
| Published package runtime | Node.js 24 or later | Node.js 24.18.0 |
| Repository development | Linux, macOS, or Windows through WSL | Ubuntu |
| Package manager | pnpm 11.9.0 through Corepack | pnpm 11.9.0 |
| Module format | ESM | ESM |
| Test runner | Vitest | Vitest 4.1.9 |
| Protobuf runtime | Protobuf-ES v2 | @bufbuild/protobuf 2.13.0 |
Buf and protoc-gen-es are workspace development dependencies; no global
installation is needed. Use pnpm rather than creating a package-lock.json.
From a fresh checkout at the repository root:
corepack pnpm install --frozen-lockfile
corepack pnpm build
corepack pnpm test:validation
corepack pnpm test:exampleDirect corepack pnpm invocation avoids installing a system shim beside Node.
On a cold host, Corepack may need network access to obtain pnpm 11.9.0; a cold
pnpm store then needs network access to download the packages locked in
pnpm-lock.yaml. The lockfile remains the dependency
authority.
pnpm build generates schemas before compiling TypeScript project references.
Root pnpm test:example expects the validation package’s dist directory, so
the ordered pnpm build then pnpm test:example sequence is safe in a clean
checkout.
Use pnpm example to build and run the console example. Use pnpm example:run
only after a workspace build when you want to run compiled output.
validation-ts/
├── packages/
│ ├── validation/ published package: source, tests, Proto inputs, and docs
│ └── example/ executable consumer and its Vitest scenarios
├── scripts/ repository verification and documentation checks
├── build-protocol/ repository governance and release policy
├── pnpm-lock.yaml locked workspace dependency graph
└── package.json workspace scripts and pinned package-manager version
Generated TypeScript is ignored under package src/generated/ and test
generated directories. Distribution output is also generated. Do not hand-edit
either; run the relevant script.
Run these commands from the repository root after the clean installation steps.
| Command | Use it for |
|---|---|
corepack pnpm generate |
Generate package, test, and example Protobuf-ES schemas. |
corepack pnpm build |
Generate schemas and compile TypeScript project references. |
corepack pnpm typecheck:generated |
Build and typecheck package and example tests. |
corepack pnpm test:validation |
Generate schemas and run validation-package Vitest tests. |
corepack pnpm test:example |
Run example tests after corepack pnpm build has created validation dist. |
corepack pnpm docs:check |
Check maintained docs and examples and generate TypeDoc. |
corepack pnpm source:check |
Check project-owned TypeScript and Proto conventions. |
corepack pnpm proto:verify |
Verify local immutable Proto checksums against the recorded manifest. |
corepack pnpm proto:lint |
Lint project-owned Proto while honoring immutable-input exceptions. |
corepack pnpm format:check |
Check Prettier formatting without modifying files. |
corepack pnpm lint |
Run ESLint. |
corepack pnpm verify |
Run the complete local and CI gate. |
corepack pnpm verify includes Node compatibility, Proto checksum verification, generation,
typechecking, source and formatting checks, linting, deterministic generation,
coverage, documentation, Proto linting, build output, the executable example,
package contents, and Git-diff checks.
There is no consumer registration API. A newly supported official Spine option
follows the repository-owned pattern used by (when); it does not alter an
immutable upstream Proto file.
- Confirm the option is defined by the official Proto input already vendored
under
packages/validation/proto/spine/.(when)is defined bypackages/validation/proto/spine/time_options.proto. If the official input is absent or must change, stop: it needs the separate immutable-Proto intake described below. - Add the smallest valid declaration to
packages/validation/tests/proto/test-when.proto(or a new project-owned fixture beside it). For(when),TimeValidation.future_timestampdeclaresgoogle.protobuf.Timestamp future_timestamp = 2 [(when).in = FUTURE];. - Add a failing generated-schema test in
packages/validation/tests/when.test.ts. The existing model fixes the clock withValidationClock.set()and validatesTimeValidationSchemacreated by the fixture. Start with the expected field path and diagnostic shape, not an implementation detail. - Run
corepack pnpm generate. This refreshes the generated extension module atpackages/validation/src/generated/spine/time_options_pb.tsand the test fixture module atpackages/validation/tests/generated/test-when_pb.ts. Generated files are output, not editing targets. - Import the generated extension in
packages/validation/src/options-registry.tsand add its stable name tooptionRegistry.(when)importswhenfrom./generated/spine/time_options_pb.jsand registers it aswhen. - Implement the option owner under
packages/validation/src/options/; the(when)owner ispackages/validation/src/options/when.tsand reads its extension withValidationOptions.get("when"). Add the narrowest validation, configuration-error, and diagnostic behavior needed by the test. - Wire field-level behavior into the ordered
fieldValidatorsarray inpackages/validation/src/validation.ts.(when)callsWhen.validate. UseValidationOrchestration.adaptAllFieldsValidator()only when the option already evaluates all fields together, as(pattern)does; ordinary field-level options do not need a new orchestration abstraction. - Update the package guide, validation contract, and the executable example when the option is useful to consumers. Add public TSDoc only if an exported API changes.
Run the sequence below after each relevant step:
corepack pnpm generate
corepack pnpm exec vitest run packages/validation/tests/when.test.ts
corepack pnpm test:validation
corepack pnpm proto:lint
corepack pnpm docs:checkFor a concrete (when) example, suppose equality with the clock must become a
failure instead of the current accepted boundary. In
packages/validation/tests/when.test.ts, change the first test’s final
assertion before changing source:
import { create } from "@bufbuild/protobuf";
import { ValidationClock } from "../src/clock.js";
import { validate } from "../src/index.js";
import { TimeValidationSchema } from "./generated/test-when_pb.js";
const now = { seconds: 1_704_067_200n, nanos: 0 };
beforeEach(() => ValidationClock.set(() => now));
afterEach(() => ValidationClock.set());
it("rejects equality with now", () => {
const message = create(TimeValidationSchema, {
pastTimestamp: now,
futureTimestamp: now,
disabled: { seconds: 0n, nanos: 0 },
});
expect(validate(TimeValidationSchema, message)).toHaveLength(2);
});
That assertion fails against the current behavior. The smallest implementation
target is the valid comparison in When.validate in
packages/validation/src/options/when.ts; update only the relevant inclusive
comparison, then rerun:
corepack pnpm generate
corepack pnpm exec vitest run packages/validation/tests/when.test.ts
corepack pnpm exec vitest run packages/validation/tests/when-contract.test.ts
corepack pnpm test:validationUse generated schemas and real descriptors instead of mocks. Add an integration test when traversal, nesting, paths, or option composition changes.
For a project-owned fixture, edit a file under
packages/validation/tests/proto/ or packages/example/proto/, then run:
corepack pnpm generate
corepack pnpm proto:lint
corepack pnpm test:validationNever edit vendored Spine inputs such as spine/options.proto,
spine/time_options.proto, or spine/time/time.proto. Before fetching,
changing, or recording a vendored file or its manifest entry, obtain separate
approval for immutable-Proto intake and compatibility review. The approved
intake retrieves the input byte-for-byte at an exact upstream commit and records
its repository, commit, path, URL, retrieval date, local path, and SHA-256 in
the source manifest. Then run corepack pnpm proto:verify, generation, and linting.
See the immutable Proto guide.
- Update
packages/validation/src/index.tsand the affected exported source declaration. - Update the package README TypeScript example and public TSDoc together.
- Run:
corepack pnpm generate
corepack pnpm typecheck:generated
corepack pnpm test:validation
corepack pnpm docs:check- Update the valid runnable Proto under
packages/example/proto/and the matching scenario inpackages/example/src/scenarios.ts. - Update
packages/example/tests/scenarios.test.tsandpackages/example/README.md. - Run:
corepack pnpm generate
corepack pnpm build
corepack pnpm test:example
corepack pnpm docs:checkInvalid option declarations belong only under packages/example/proto/testing/.
- Update the consumer-facing package README, example README, or package-local reference that owns the claim.
- Run:
corepack pnpm docs:check
corepack pnpm format:check
git diff --check- Update the owning manifest: root
package.jsonfor shared tooling,packages/validation/package.jsonfor published-package dependencies, orpackages/example/package.jsonfor example-only dependencies. - Refresh the lockfile and verify the exact workspace:
corepack pnpm install --lockfile-only
corepack pnpm install --frozen-lockfile
corepack pnpm typecheck:generated
corepack pnpm test:validation
corepack pnpm build
corepack pnpm test:exampleDo not edit the lockfile by hand. Explain why the dependency is needed and check its Node.js and TypeScript compatibility before proposing the change.
Follow the concise version-change rule in the contribution guide.
| Symptom | Likely cause and resolution |
|---|---|
| pnpm is not available as a shell command | Run the command as corepack pnpm <script>; this does not install a system shim. |
| Corepack cannot obtain pnpm | Connect the host to the network so Corepack can download the pinned pnpm release. |
| Install cannot find packages | Connect the host to the network so pnpm can fill its cold package store. |
Example root test cannot resolve validation dist |
Run corepack pnpm build, then rerun corepack pnpm test:example. |
| Generated imports or fixtures are missing | Run corepack pnpm generate; never add generated files manually. |
corepack pnpm proto:verify reports a checksum mismatch |
Restore the immutable file; use the separate approved intake procedure for an intended upstream replacement. |
| A docs TypeScript snippet fails | Import only public package exports and use ESM .js relative imports. |
| A time check differs by zone or range | Read the (when) conversion details in the validation contract and include the exact input in a focused test. |
Inspect the diff and run the checks that cover the change. For broad changes, run the complete gate:
corepack pnpm verifyThe contribution guide explains branches, commits, pull requests, and the
restriction on master.