Monorepo of TypeScript packages published to npm under the @noem scope.
Built with pnpm workspaces, Turborepo, Changesets for versioning/publishing, and Biome for linting and formatting.
| Package | Description | Version | Build |
|---|---|---|---|
@noem/encryption |
Isomorphic field-level encryption (WebCrypto AES-256-GCM) with key rotation. | ||
@noem/luhn |
Luhn algorithm validation for card / SIN numbers. | ||
@noem/platform-keystore |
TPM-backed hardware key storage — seal/unseal via the Windows NCrypt KSP. |
New packages live under packages/*; (see pnpm-workspace.yaml).
- Node
24(see.nvmrc— runnvm use) - pnpm
11.3.0(declared inpackageManager) - rust
1.95.0(declared in rust-toolchain.toml) (only for native packages)
- Rust
1.95.0— pinned inrust-toolchain.toml; install via rustup, which reads the pin and the declaredtargetsautomatically (no manualrustup target addneeded). - Windows: Visual Studio Build Tools with the "Desktop development with C++"
workload. This provides the MSVC linker
link.exe. VS Code alone is not sufficient. Install via winget, then open a fresh terminal soPATH/env pick up the new tools:winget install Microsoft.VisualStudio.2022.BuildTools --override "--quiet --wait --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"
- Windows: Install llvm-cov:
cargo install cargo-llvm-cov
A build error like
error: linker `link.exe` not found(often cascading acrossserde_json,napi,proc-macro2, …) means the C++ workload above is missing.
pnpm installAll tasks run through Turborepo from the repo root.
pnpm build # build all packages
pnpm test # run all tests (vitest)
pnpm lint # check with Biome
pnpm lint:fix # apply Biome fixes
pnpm dev # watch mode across packagesTarget a single package with a filter:
pnpm build --filter=@noem/luhn
pnpm test --filter=@noem/luhnPublishing is automated via Changesets + GitHub Actions
(.github/workflows/release.yml). Packages publish to npm using
OIDC trusted publishing — no npm token
is stored in the repo.
For every change that should ship, add a changeset describing it:
pnpm changesetPick the affected package(s), choose the bump (patch / minor / major), and write
a summary. This creates a markdown file under .changeset/. Commit it with your change.
The PR workflow (
.github/workflows/pr.yml) gates PRs withchangeset status --since=origin/<base>and fails when no changeset is present, so a change can't merge unversioned. The gate is skipped for the auto-generated "Version Packages" PR (which has no changesets by design).
Push your branch and open a PR. Merge it once approved. The changeset file rides along.
On every push to main, the Release workflow runs the Changesets action:
- If unreleased changesets exist, it opens/updates a "Version Packages" PR that
bumps versions and updates each package's
CHANGELOG.md. - Review and merge that PR.
Merging the "Version Packages" PR triggers the workflow again. With no pending
changesets left, it runs pnpm run release (turbo run build --filter=./packages/* && changeset publish), building the packages and publishing the bumped versions to npm.
Publishing requires the package to be configured as a trusted publisher on npm for this repo/workflow. The workflow needs
id-token: write(already set) and usesnpm@latestbecause OIDC trusted publishing needs npm >= 11.5.1.
add changeset → merge PR to main → merge "Version Packages" PR → npm publish (auto)
OIDC trusted publishing can't create a package that doesn't exist yet — npm only lets
you configure a trusted publisher on a package that's already on the registry. So the
first publish of a new @noem/* package is a one-time manual step; every release
after that is automated (see above).
From the new package's directory (packages/<name>), signed in to an npm account in
the @noem org with 2FA enabled:
# 1. trusted-publisher config + --allow-publish need npm >= 11.15
npm install -g npm@latest
# 2. build so dist/ exists (the published tarball ships dist + src per "files")
pnpm build --filter=@noem/<name>
# 3. copy root LICENSE-MIT / LICENSE-APACHE into the package so they ship
# in the tarball (Apache-2.0 §4(a) requires it). CI does this automatically;
# bootstrap publishes need it run manually.
node scripts/sync-licenses.mts
# 4. publish the initial 0.0.0 to create the package on the registry.
# leave the version at 0.0.0 — your changeset bumps it to 0.0.1 in CI.
npm publish
# 5. register this repo's release workflow as a trusted publisher
npm trust github @noem/<name> --file release.yml --repo noem-inc/noem --allow-publish
# 6. confirm it stuck (prompts for an OTP)
npm trust list @noem/<name>After this, the normal flow takes over: the pending changeset bumps 0.0.0 → 0.0.1
and CI publishes it via OIDC — with provenance, no token.
Gotchas: don't pass
--dry-runon step 5 (it validates but persists nothing), and don't pass--env—release.ymldeclares no GitHub Actions environment, so pinning an environment claim the workflow never sends will make CI publishes fail. The manual0.0.0is the only version without a provenance attestation.
- ESM/CJS packages.
- Build output goes to
dist/viatsup. - Formatting: 2-space indent, single quotes, trailing commas, semicolons (Biome).
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
SPDX-License-Identifier: MIT OR Apache-2.0
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.