JS Build Tools: helpers for building, benchmarking & testing secure JS apps.
- 🤖 workflows: Secure GitHub CI actions for testing & token-less publishing of JS packages using OIDC.
- 🏋🏻 bench: Benchmark JS projects with nanosecond resolution.
- 📝 test: Multi-env testing framework with familiar syntax & parallel execution
- 🏗️ jsbt: Single CLI for release builds and shared doc/tree-shaking audits
- ⚙️ tsconfig: Strict typescript configs, friendly to type stripping
- 🪶 No dependencies
Used by noble cryptography and others.
npm install @paulmillr/jsbt
jsr add jsr:@paulmillr/jsbt
Secure GitHub CI configs for testing & publishing JS packages.
The files reside in .github/workflows:
test-js.yml: runs tests on LTS node.js, bun, deno, linter, and calculates coveragetest-ts.yml: the same, but runs typescript instead of js on supported node.js (v22+) On node.js v26, it executestest:tscto compile files instead.release.ymlpublishes package on NPM, JSR and creates single-file output if it exists- Uses brand new token-less GitHub OIDC connector to NPM, ensure to link package in npm settings first
- The Trusted Publishing also provides provenance statements by default
- It happens after GitHub release is created
You can copy them, or depend on them directly:
name: Publish release
on:
release:
types: [created]
jobs:
release-js:
name: 'jsbt v0.4.5'
uses: paulmillr/jsbt/.github/workflows/release.yml@570adcfe0ed96b477bb9b35400fb43fd9406fb47
permissions:
contents: read
id-token: writeBenchmark JS projects with nanosecond resolution
- Precise: 1ns resolution using
process.hrtime - Lightweight: ~200 lines of code
- Readable: utilizes colors and nice units, shows rel. margin of error only if it's high
import bench from '@paulmillr/jsbt/bench.js';
(async () => {
await bench('printing', () => Promise.resolve(0));
await bench('base', () => Promise.resolve(1));
await bench('sqrt', 10000, () => Math.sqrt(2));
})();Example output:
getPublicKey x 6,072 ops/sec @ 164μs/op ± 8.22% [143μs..17ms]
sign x 4,980 ops/sec @ 200μs/op
verify x 969 ops/sec @ 1ms/op
recoverPublicKey x 890 ops/sec @ 1ms/op
getSharedSecret x 585 ops/sec @ 1ms/op
Multi-env testing framework with familiar syntax & parallel execution.
- Familiar syntax: similar to Mocha / Jest / Vitest
- Multi-env: runs on node.js, deno, bun, cloudflare, browsers and others
- No "global" magic:
it.run()in the end simplifies logic and browser runs - Parallel: easily run in node.js and bun
- Great UI: beautiful tree reporter, optional "quiet" dot reporter
node a.test.js
MSHOULD_FAST=1 MSHOULD_QUIET=1 node a.test.js
import { should } from 'micro-should';
import { equal } from 'node:assert';
// Any assertion library can be used e.g. Chai or Expect.js
should('add', () => {
equal(2 + 2, 4);
});
should('work in async env', async () => {
equal(await Promise.resolve(123), 123);
});
describe('nested', () => {
describe('nested 2', () => {
should('multiply', () => {
equal(2 * 2, 4);
});
should.skip('disable test by using skip', () => {
equal(true, false); // skip
});
// should.only('execute only one test', () => {
// equal(true, true);
// });
});
});
should.runWhen(import.meta.url);
// or
// should.run();
// should.opts.STOP_AT_ERROR = false; // default=true
// should.opts.MSHOULD_QUIET = true; // same as env varUsage:
should(title, case)orit(title, case)syntax to register a test functionshould.only,should.skipallows to limit tests to one case / skip testsbeforeEach,afterEachexecute code before / after function indescribeblockshould.runWhen(import.meta.url)must be executed in the end- The helper ensures tests are not ran when imported from other file
- It compares import.meta.url to CLI argument
should.run()orit.run()must always be executed in the end
ENV variables, specifiable via command line or through code:
MSHOULD_FAST=1enables parallel execution in node.js and Bun. Values >1 will set worker count.MSHOULD_QUIET=1enables "quiet" dot reporter
jsbt dispatches the release build and shared audit helpers shipped by the package.
Current subcommands:
jsbt esbuild: bundle one repo's test/build input via esbuild
jsbt esbuild <build-dir> [--auto] [--no-prefix]
jsbt check <package.json>
jsbt check <package.json> bigint
jsbt check <package.json> bytes
jsbt check <package.json> comments
jsbt check <package.json> errors
jsbt check <package.json> importtime
jsbt check <package.json> jsdoc
jsbt check <package.json> jsr
jsbt check <package.json> jsrpublish
jsbt check <package.json> mutate
jsbt check <package.json> patterns
jsbt check <package.json> readme
jsbt check <package.json> tests
jsbt check <package.json> treeshake [out-dir]
jsbt check <package.json> tsdoc
jsbt check <package.json> typeimport
jsbt check-install <package.json>
jsbt bigint <package.json>
jsbt bytes <package.json>
jsbt comments <package.json>
jsbt errors <package.json>
jsbt importtime <package.json>
jsbt jsr <package.json>
jsbt jsrpublish <package.json>
jsbt mutate <package.json>
jsbt readme <package.json>
jsbt tests <package.json>
jsbt treeshake <package.json> <out-dir>
jsbt typeimport <package.json>
jsbt tsdoc <package.json>
With the standard package script:
npm run check bigint
npm run check bytes
npm run check comments
npm run check errors
npm run check importtime
npm run check jsdoc
npm run check jsr
npm run check jsrpublish
npm run check mutate
npm run check patterns
npm run check readme
npm run check tests
npm run check treeshake
npm run check tsdoc
npm run check typeimportThe published package exposes a single bin, so all of these work through:
npx --no @paulmillr/jsbt <subcommand> ...
The command would execute following subcommands and produce several files:
cd test/build
npm install
npx esbuild --bundle input.js --outfile=out/noble-hashes.js --global-name=nobleHashes
npx esbuild --bundle input.js --outfile=out/noble-hashes.min.js --global-name=nobleHashes --minify
# 11d1900e99f3aa945603bb5e7d82bdd9ec6ddf5d30e2fcab69b836840cff76d2 test/build/out/noble-hashes.js
# 0be3876ff0816c44d21a401e6572fdb76d06012c760a23a5cb771c6f612106f5 test/build/out/noble-hashes.min.js
3790 LOC noble-hashes.js
58.21 KB noble-hashes.min.js
21.10 KB +gzip
19.57 KB +zstdStrict typescript v6+ configs, friendly to type stripping.
tsconfig.test.jsonis for typescript tests, with looser checks
Option descriptions:
isolatedDeclarationsensures types are "fast" and friendly to JSR.ioverbatimModuleSyntax- ensures files are friendly to "type erasure" / "type ignore" node.js and others
MIT License