Adding a Module
A module is a type alias and a static const object with the same name. The shape keeps the runtime plain, makes tree-shaking predictable, and gives editors one namespace to autocomplete.
Anatomy
Section titled “Anatomy”Import WithKind, WithValue, and WithError from #internal. Do not inline the helpers. Do not
add a class, instanceof, or an of constructor.
Naming
Section titled “Naming”| Category | Pattern | Example |
|---|---|---|
| Constructor | plain name under make.* |
Result.make.ok |
| Native converter | to.<type> / from.<type> |
Maybe.to.nullable, Num.from.string |
| Container converter | to.<Type> / from.<Type> |
Result.to.Maybe |
| Try-wrap | tryCatch |
Result.tryCatch |
The standard surface is is, map, mapError, chain, apply, match, getOrElse, tap,
recover, tryCatch, from.*, and to.*. Add only the operations the module can explain.
Data goes last. match({ ok, err }) is the only elimination form; positional fold is not part of
the API. Name callbacks transform, predicate, sideEffect, reducer, compare, or
combineErrors, not b, a, or f.
Hard rules
Section titled “Hard rules”- No classes, prototype methods, or
instanceof. - No
ofconstructor and notoprefix on constructors. - No alias functions, duplicate modules, or dual-exposed namespaces.
- Every public type is the module type or lives in its companion namespace. No detached options or callback types at the file root.
src/Core/index.tsand the other domain barrels export public modules only. Import siblings relatively inside a domain; never import the domain barrel through#core.- Every public export has JSDoc with a realistic
@exampleand reciprocal@seereferences. - The published package has zero runtime dependencies. A dependency is an architecture decision and needs an issue.
Write a one-line description in plain English. Add an example that resembles application code, not
foo(bar). For complementary pairs, link both sides: map and chain, traverse and sequence,
tap and tapError, from.* and to.*.
Do not use fetch in base Task examples. Fallible async belongs in Task.Result, Task.Maybe,
or Op.
New module checklist
Section titled “New module checklist”- Add
src/<Domain>/Foo.tswith the type alias and companion const object. - Export it from the domain barrel, such as
src/Core/index.tsorsrc/Data/index.ts. - Add
src/<Domain>/__tests__/Foo.test.tsandFoo.test-d.ts. - Add
Foo.prop.test.tswhen the module encodes algebraic laws or stateful sequencing. - Add JSDoc with a realistic example and reciprocal
@see. - Add
docs/src/content/docs/guides/foo.mdusing the guide anatomy. - Add the guide to the sidebar in
docs/astro.config.mjs. - Add a
size-limitentry inpackage.json. - Add the guide to
scripts/docs-manifest.ts. Use{ module: "Foo" }for a module guide and{ module: null }only for an aggregate guide with no module-specific budget. - Run
pnpm size:update, review the diff, then runpnpm check:prandpnpm docs:build.
The bundle-size test fails when a guide is missing from the manifest, when a badge is missing or
duplicated, or when the guide is absent from the sidebar. The size budget test fails when a manifest
entry has no matching size-limit target.
Performance
Section titled “Performance”If the change touches a hot path, run pnpm bench and compare the before and after numbers. Include
the benchmark result in the pull request. Do not replace a loop or a data layout on intuition; the
benchmark appendix documents the existing measurements.
Getting help
Section titled “Getting help”Open an issue for a new public API, a naming decision, or a dependency. The contributor handbook lists the other rules; Testing covers the test contract.