Thanks for considering a contribution. This document covers how to work on the project, what kinds of contributions are valuable, and the standards we hold the codebase to.
sigilid is a small, focused library. The goal is not to be the most feature-rich ID library — it is to be the most trustworthy one for TypeScript developers who care about bundle size, API clarity, and long-term maintenance.
That means:
- Less is more. A small, stable public surface is harder to misuse and easier to document.
- Explicitness over convenience. Subpath imports are more verbose but more intentional.
- Zero surprises. Behavior should be deterministic, documented, and predictable.
- Docs are not optional. A feature without documentation does not exist for most users.
Things we are actively interested in:
- Bug fixes with a clear reproduction
- Documentation improvements (typos, clarity, missing examples)
- Test coverage gaps
- Tooling improvements (CI, build config, DX)
- New subpath exports that fit the scope and follow the design constraints
Things that need discussion first:
- New subpath exports (open an issue before writing code)
- Changes to the public API of existing entrypoints
- Adding runtime dependencies
Things that are unlikely to be accepted:
- Convenience re-exports from the root entrypoint
- Features that belong in application code, not a utility library
- Complexity that breaks the "junior developer can understand this" test
You will need Node.js 20 or later and npm 9 or later.
git clone https://github.com/moritzmyrz/sigilid.git
cd sigilid
npm installBuild the main package:
npm run buildRun tests:
npm run testLint and format:
npm run lint
npm run lint:fixTypecheck:
npm run typechecksigilid/
├── src/ ← library source
├── test/ ← Vitest test suite
├── tools/ ← local playground and benchmark scripts
├── package.json ← scripts, exports map, size-limit budgets
└── tsup.config.ts ← build entrypoint configuration
Most contributions will touch src/, test/, README.md, and build config.
The playground is useful for quickly verifying usage patterns after a change. Run it with:
npm run playground- TypeScript throughout. No plain JS files in
src/. - Strict TypeScript config. Do not loosen the
tsconfig.jsonsettings. - Biome handles formatting and linting. Run
npm run lint:fixbefore committing. - No runtime dependencies. If a proposed change requires one, that is a signal to reconsider the approach.
- Prefer explicit over implicit. Named exports only; no default exports.
- Keep functions small. If a function is doing more than one thing, split it.
- All public API functions must have tests.
- Tests live in
test/, one file per entrypoint. - Use Vitest. Keep tests deterministic.
- Do not write statistical collision tests that could flake under load.
- Edge cases (empty strings, zero lengths, invalid alphabets) should be tested explicitly.
- If you add a new public function, add tests before submitting.
- Keep the public surface small and stable.
- Parameter order should be consistent with existing functions.
- Optional parameters should have sensible defaults.
- Functions should throw with descriptive messages on invalid input, never silently return garbage.
- Types should be precise but not opaque. A type that requires reading the source to understand is a problem.
Before adding to the public surface, ask:
- Is this something a caller cannot reasonably implement themselves in two lines?
- Will it be used by more than a handful of people?
- Does it stay under the existing pattern of the entrypoint it belongs in?
Internal utilities go under src/internal/. They are not exported and not part of any promise to callers.
If you are not sure whether something belongs in src/internal/ or as a public export, it probably belongs in src/internal/.
- Use Conventional Commits:
feat:,fix:,docs:,chore:,test:, etc. - Subject line: 50 characters or fewer, imperative mood.
- PR title should match the commit subject.
- One logical change per PR.
- Do not mix formatting changes with behavior changes.
- No changeset workflow is used in this repository.
Open a GitHub issue first. Describe:
- What the export does
- Why it cannot live in an existing entrypoint
- Who the intended audience is
- Whether it requires any new dependencies
New subpath exports also require updates to:
package.json(exports map)tsup.config.ts(build entry)README.md(API docs)ARCHITECTURE.md(if the structure changes)
Do not export anything from src/internal/. If a function in src/internal/ looks useful to external callers, open an issue to discuss whether it should become a public export with proper API design — not just a quiet re-export.
Documentation matters as much as the code. If your change introduces new behavior or a new public API:
- Update
README.mdwith a usage example - Make sure the example compiles and runs correctly
- Keep language concise and practical
Docs that are out of sync with the code are bugs.
Open a GitHub issue. Label it question.