@watershed-labs/config
v0.1.0
Published
Zero-path-boilerplate configuration for the Watershed ecosystem
Downloads
21
Readme
@watershed-labs/config
Zero-path-boilerplate configuration for every Watershed template.
Part of the Watershed testing ecosystem.
Overview
@watershed-labs/config provides defineConfig variants for every template type in the Watershed ecosystem. Each variant resolves all file paths relative to the calling file's location — not the cwd, not the monorepo root — so configurations work correctly regardless of where the process starts.
The user passes import.meta.url once. The rest is handled internally.
Installation
npm install @watershed-labs/configQuick Start
// cucumber.js
import { defineConfig } from '@watershed-labs/config';
export default defineConfig(import.meta.url);That is a complete, valid Cucumber configuration. Every path, formatter, and execution default is pre-wired for the template type.
API
defineConfig(callerUrl, overrides?) — Cucumber/Web templates
Used by: create-otter, create-salamander, create-pike, create-egret, create-mussel.
import { defineConfig } from '@watershed-labs/config';
export default defineConfig(import.meta.url, {
parallel: 4,
worldParameters: {
baseUrl: process.env.BASE_URL ?? 'https://staging.example.com'
}
});Defaults:
| Option | Default |
| -------------------------- | ----------------------------------------------------------- |
| paths | features/**/*.feature |
| require | step_definitions/**/*.js, support/**/*.js |
| format | @watershed-labs/reporter, HTML report to reports/report.html |
| parallel | CUCUMBER_WORKERS env var, fallback 1 |
| publishQuiet | true |
| worldParameters.baseUrl | BASE_URL env var, fallback http://localhost:3000 |
| worldParameters.browser | BROWSER env var, fallback chrome |
| worldParameters.headless | true unless HEADLESS=false |
defineApiConfig(callerUrl, overrides?) — API templates
Used by: create-dipper.
import { defineApiConfig } from '@watershed-labs/config';
export default defineApiConfig(import.meta.url, {
worldParameters: {
baseUrl: process.env.BASE_URL ?? 'https://api.example.com',
apiKey: process.env.API_KEY
}
});Same structure as defineConfig but worldParameters contains baseUrl, apiKey, and contentType — no browser or headless.
defineVitestConfig(callerUrl, overrides?) — Unit/component templates
Used by: create-vole.
// vitest.config.js
import { defineVitestConfig } from '@watershed-labs/config';
export default defineVitestConfig(import.meta.url, {
test: {
environment: 'jsdom'
}
});Defaults:
| Option | Default |
| -------------------------------- | --------------------------------------- |
| test.include | test/**/*.test.js, src/**/*.test.js |
| test.environment | node |
| test.coverage.provider | v8 |
| test.coverage.reporter | text, html |
| test.coverage.reportsDirectory | reports/coverage |
defineK6Config(callerUrl, overrides?) — Performance templates
Used by: create-beaver.
// k6.config.js
import { defineK6Config } from '@watershed-labs/config';
export default defineK6Config(import.meta.url);The active load profile is selected via K6_PROFILE:
K6_PROFILE=smoke pnpm test:performance # vus: 1, duration: 1m
K6_PROFILE=load pnpm test:performance # vus: 50, duration: 10m
K6_PROFILE=stress pnpm test:performance # vus: 200, duration: 10m
K6_PROFILE=spike pnpm test:performance # vus: 500, duration: 2m
K6_PROFILE=soak pnpm test:performance # vus: 50, duration: 4hDefault when K6_PROFILE is unset: smoke.
defineVisualConfig(callerUrl, overrides?) — Visual regression templates
Used by: create-egret.
import { defineVisualConfig } from '@watershed-labs/config';
export default defineVisualConfig(import.meta.url, {
worldParameters: {
updateBaseline: process.env.UPDATE_BASELINE === 'true'
}
});defineSecurityConfig(callerUrl, overrides?) — Security templates
Used by: create-pike.
import { defineSecurityConfig } from '@watershed-labs/config';
export default defineSecurityConfig(import.meta.url);defineContractConfig(callerUrl, overrides?) — Contract testing templates
Used by: create-mussel.
import { defineContractConfig } from '@watershed-labs/config';
export default defineContractConfig(import.meta.url);defineHatcheryConfig(callerUrl, overrides?) — Test data templates
Used by: create-hatchery.
import { defineHatcheryConfig } from '@watershed-labs/config';
export default defineHatcheryConfig(import.meta.url, {
seed: process.env.DATA_SEED ?? 42
});Override Behaviour
Arrays replace entirely.
// Only admin features run — default paths array is replaced
export default defineConfig(import.meta.url, {
paths: ['features/admin/**/*.feature']
});Objects deep-merge.
// browser stays 'chrome', headless stays true — only baseUrl changes
export default defineConfig(import.meta.url, {
worldParameters: {
baseUrl: 'https://staging.example.com'
}
});Primitives replace.
// Only parallel changes — everything else keeps its default
export default defineConfig(import.meta.url, {
parallel: 4
});Environment Variables
| Variable | Affects | Default |
| ------------------ | -------------------------------- | ----------------------- |
| BASE_URL | worldParameters.baseUrl | http://localhost:3000 |
| BROWSER | worldParameters.browser | chrome |
| HEADLESS | worldParameters.headless | true |
| CUCUMBER_WORKERS | parallel | 1 |
| K6_PROFILE | k6 load profile | smoke |
| UPDATE_BASELINE | worldParameters.updateBaseline | false |
| API_KEY | worldParameters.apiKey | undefined |
| DATA_SEED | faker seed in hatchery | 42 |
Why import.meta.url
defineConfig needs to know where the config file lives to resolve paths relative to it. import.meta.url is the ES module equivalent of __filename — always accurate, regardless of where the process started.
Passing it explicitly is intentional. The alternative (stack trace detection) breaks under minification, source maps, and across platforms. One extra argument is worth the reliability. Vitest uses the same pattern for the same reason.
Extending With Custom Defaults
If the built-in variants don't cover your project's needs, compose on top of the exposed internals:
import { resolveDir, makeRel, merge } from '@watershed-labs/config/internals';
export function defineMyConfig(callerUrl, overrides = {}) {
const dir = resolveDir(callerUrl);
const rel = makeRel(dir);
const defaults = {
paths: [rel('features/**/*.feature')],
require: [rel('support/**/*.js')],
worldParameters: {
baseUrl: process.env.BASE_URL ?? 'http://localhost:3000',
tenantId: process.env.TENANT_ID
}
};
return merge(defaults, overrides);
}Parallel Safety
Stateless. defineConfig is a pure function — reads arguments and env vars, returns a plain object. No singletons, no shared state, no side effects. Safe at any parallelism level.
Dependencies
None. Uses only Node.js built-ins (node:path, node:url).
Licence
MIT — part of the Watershed ecosystem.
