UNPKG

paqad-ai

Version:

Spec-driven development framework — AI agents that think before they type

96 lines (64 loc) • 5.11 kB
# Node.js Library Conventions ## Public API Surface Design - Export everything intentionally via `src/index.ts` (or `src/index.js`). Do not rely on consumers importing internal paths. - Use explicit named exports. Avoid default exports for libraries — they are harder to tree-shake and rename consistently. - Group exports logically using barrel files (`src/utils/index.ts`, `src/types/index.ts`), but do not create barrel files that re-export everything indiscriminately — this prevents tree-shaking. - Mark the `exports` field in `package.json` with conditional exports for CJS/ESM dual-build when needed. ## Type Export Patterns - Ship declaration files (`.d.ts`) alongside compiled output — never rely on consumers having `ts-node` at runtime. - Use `types` (or `typings`) field in `package.json` to point to the primary `.d.ts` entry. - Avoid exporting internal implementation types through the public API. Use `@internal` JSDoc tags on types that must be exported for structural reasons but are not intended for consumers. - When using `tsup`, `rollup`, or `unbuild`, verify the output includes correct source maps and declaration maps. ## Semver Discipline Breaking changes require a **major version bump**. The following are always breaking: - Removing or renaming exported functions, classes, or types - Adding required parameters to public functions - Changing the return type of a public function - Removing fields from public interfaces or types - Changing a synchronous API to async (or vice versa) Non-breaking additions require a **minor version bump**. Bug fixes require a **patch version bump**. Use `@changesets/cli` or `semantic-release` to automate changelog generation and version management. ## Peer Dependency Management - Declare framework/runtime packages as `peerDependencies`, not `dependencies`, to avoid duplication in consumer projects. - Always declare a `peerDependenciesMeta` entry marking peer deps as optional when the library works without them. - Test against the full range of declared peer dependency versions in CI. ## Tree-Shaking Readiness - Set `"sideEffects": false` in `package.json` if the library has no side effects at module evaluation time. - Avoid top-level `console.log`, `require()` calls, or global mutations in the module scope. - Publish ESM output (`"type": "module"` or `.mjs` files) to enable static analysis by bundlers. - Do not use `require()` or `import()` with dynamic, user-controlled paths in library code. ## Bundle Size Awareness - Track bundle size in CI using `bundlesize`, `size-limit`, or equivalent. - Avoid bundling large dependencies — prefer marking them as `peerDependencies` or `optionalDependencies`. - Audit the published package contents using `npm pack --dry-run` and configure the `files` field in `package.json` to exclude tests, fixtures, and source maps from the published artefact. ## Changelog and Release Workflow - Maintain a `CHANGELOG.md` in the standard Keep a Changelog format. - Every PR that changes public behaviour must include a changeset entry. - Automate releases from the main branch using CI to prevent manual version-bumping errors. - Tag releases in git before publishing to npm. ## Testing Patterns ### Unit Tests - Write unit tests for every public function and class, testing the public contract, not the implementation. - Test edge cases at API boundaries: `null`, `undefined`, empty arrays, empty strings, very large inputs. - Never test private methods directly — refactor if the private implementation needs its own test coverage. ### Integration Tests - Test complex interactions between public APIs (e.g., composing multiple exported utilities). - If the library wraps I/O (file system, network, database), write integration tests against the real system, not mocks. ## Security ### Input Validation on Public API Surface - Validate all arguments on every exported function and method. Do not trust the caller. - Throw descriptive `TypeError` or `RangeError` for invalid input rather than returning `undefined` silently. - Sanitize string inputs that will be used in file paths, URLs, or shell commands. ### Prototype Pollution Defense - Never write to `obj[key]` where `key` comes from untrusted input without checking `key !== '__proto__'`, `key !== 'constructor'`, and `key !== 'prototype'`. - Use `Object.create(null)` for internal maps that store user-supplied keys. - Prefer `Map` and `Set` over plain objects for dynamic key storage. ### Unsafe Patterns to Avoid - Never use `eval()` or the `Function()` constructor with dynamic strings. - Never use `child_process.exec()` with user-supplied input — use `execFile()` with argument arrays. - Never deserialize untrusted data with `JSON.parse()` without schema validation. ### Supply Chain Hygiene - Commit your lockfile (`pnpm-lock.yaml` or `package-lock.json`). - Run `pnpm audit` in CI and fail on high/critical advisories. - Minimize the dependency tree — prefer zero-dependency implementations for simple utilities. - Review all transitive dependencies before publishing a new major version.