NPM Scripts for Consumer Projects
This page describes the minimum set of scripts a project consuming superman should have in its package.json, why they are enough to cover development, staging, and production, and how to add unit tests with Jest.
Minimum scripts
{
"scripts": {
"dev": "tsx watch src/server.ts",
"build": "tsc",
"start": "node dist/server.js",
"test": "jest"
}
}| Script | Command | When to run |
|---|---|---|
dev | tsx watch src/server.ts | Local development loop — executes TypeScript directly, reloads on save. No build step. |
build | tsc | Compile to dist/ before deploying to staging / production. |
start | node dist/server.js | Run the compiled artifact. Inherits NODE_ENV (or ENV) from the shell / env file / orchestrator. |
test | jest | Unit tests (colocation, *.test.ts next to source). |
These four scripts are all you need. No start:staging, no start:prod, no environment-specific commands.
One build, three environments
The framework resolves per-environment behaviour at runtime — the same compiled artifact works for dev, staging, and prod. You only change the env variable.
// src/server.config.ts
defineConfig({
port: { env: 'PORT', default: 3000 },
prefix: '/api',
environments: {
development: { endpoints: { myApi: 'https://dev.api.example.com' } },
staging: { endpoints: { myApi: 'https://staging.api.example.com' } },
production: { endpoints: { myApi: 'https://api.example.com' } },
},
});At boot, the framework:
- Reads
process.env.ENVorprocess.env.NODE_ENV(default:development). - Picks the matching
environments[...]block —config.endpoints.myApireturns the right URL for that env. - Adjusts defaults based on the env:
LOG_LEVELfalls back toinfoin production anddebugotherwise; file sink paths / console colouring behave consistently;config.isProduction()is available anywhere.
So the single start script covers every environment — the orchestrator (Docker, k8s, PM2, systemd, GitHub Actions) injects NODE_ENV (or ENV) and secrets, and node dist/server.js does the right thing:
# Development (live reload, no build needed)
npm run dev
# Build once
npm run build
# Staging — loads environments.staging.endpoints
NODE_ENV=staging npm start
# Production — loads environments.production.endpoints, logger defaults to info
NODE_ENV=production LOG_LEVEL=info npm startWhy not separate start:dev / start:staging / start:prod scripts?
Because they would just bake NODE_ENV into the package.json, which is exactly what orchestrators already manage. Keeping a single start avoids drift between local scripts and the actual deploy command. If you need an env-file convention for local overrides, rely on dotenv/config — do not hard-code the environment in the script.
Unit tests with Jest
Keep tests simple: colocate them with the source, use the .test.ts suffix, rely on ts-jest to run TypeScript without an intermediate build.
Install
npm install --save-dev jest@^30 ts-jest@^29 @types/jest@^30jest.config.ts
import type { Config } from 'jest';
const config: Config = {
preset: 'ts-jest',
testEnvironment: 'node',
rootDir: 'src',
testMatch: ['**/*.test.ts'],
clearMocks: true,
};
export default config;tsconfig.json tweak
If tsconfig.json uses "module": "nodenext" (recommended), add "isolatedModules": true to silence the ts-jest hybrid-module warning — both tsc and Jest will be happy.
Example test
Colocate next to the service. NODE_ENV is test inside Jest, so the framework logger is silent automatically.
// src/modules/example/services/example.service.test.ts
import { BadRequestException, NotFoundException } from '@supersec-ai/superman';
import { ExampleService } from './example.service';
describe('ExampleService', () => {
it('should return a greeting for a valid name', () => {
// Arrange
const service = new ExampleService();
// Act
const result = service.generatePersonalizedGreeting('Bruno');
// Assert
expect(result).toEqual({ message: 'Hello, Bruno!' });
});
it('should throw BadRequestException when the name is only whitespace', () => {
const service = new ExampleService();
expect(() => service.generatePersonalizedGreeting(' ')).toThrow(BadRequestException);
});
it('should throw NotFoundException when the id is unknown', () => {
const service = new ExampleService();
expect(() => service.findUser('999')).toThrow(NotFoundException);
});
});Run
npm testts-jest compiles .ts files on the fly — no pre-build required. Jest's NODE_ENV=test already flips the framework logger to silent, so your test output stays clean.
What this setup does NOT include (on purpose)
- Linter — add your own ESLint config when you need it; not every app needs it from day one.
- Watch mode for tests —
npm test -- --watchworks out of the box. - Integration tests with supertest — add only when you have HTTP-layer behaviour specific to your app. The framework already covers its own HTTP semantics with 200+ internal tests.
- Separate staging / prod scripts — see the "One build, three environments" section above.
