Skip to content

Latest commit

 

History

History
133 lines (93 loc) · 9.11 KB

File metadata and controls

133 lines (93 loc) · 9.11 KB

Agent Guidelines

This file contains instructions for AI coding agents working in this repository.

Project overview

oauth2-mock-server is a TypeScript library and CLI tool that provides a configurable OAuth2/OpenID Connect mock server for automated testing.

Tech stack: TypeScript (ultra-strict) · Node.js · ESM-only · Vitest · ESLint + Prettier

Module map

File Role
src/index.ts Public barrel — all library exports originate here
src/oauth2-mock-server.ts Binary shim — calls cli() from src/cli.ts with process.argv.slice(2); contains no logic
src/cli.ts CLI logic — cli(args) parses argv and starts the server; also exports readJsonFromFile() and shift(); not part of the public API
src/lib/http-server.ts HttpServer — restartable wrapper around node:http/node:https
src/lib/jwk-store.ts JWKStore — manages JWK key pairs; uses jose for key generation/export
src/lib/jwk-store.keys.ts JWK algorithm registry: supportedAlgs list and privateToPublicKeyTransformer; used by JWKStore and test helpers — not exported publicly
src/lib/oauth2-issuer.ts OAuth2Issuer — holds the issuer URL + key store; signs JWTs with jose (SignJWT)
src/lib/oauth2-service.ts OAuth2Service — HTTP request handler; owns all OAuth2/OIDC endpoint logic; composes OAuth2Issuer
src/lib/oauth2-service.http.ts HTTP plumbing for OAuth2Service: body/query-param parsing, route matching — not exported publicly
src/lib/oauth2-service.pkce.ts PKCE utilities: isValidPkceCodeVerifier, createPKCEVerifier, createPKCECodeChallenge — not exported publicly
src/lib/oauth2-server.ts OAuth2Server — convenience façade; combines HttpServer + OAuth2Service + OAuth2Issuer
src/lib/assertions.ts Internal assertion helpers: assertIsString, assertIsStringOrUndefined, assertIsAddressInfo, assertIsPlainObject — not exported publicly
src/lib/types.ts Public types and interfaces (exported via barrel)
src/lib/types-internals.ts Internal types (JWKWithKid, AugmentedRequest, RouteHandler, InternalEvents, supportedPkceAlgorithms) — not re-exported

Key dependency: jose (async, Promise-based API) is used for all JWK generation, JWT signing, and key import/export.

  • Build output: dist/*.mjs (ESM only, generated by tsdown — never edit manually)

Mandatory checks before finishing any task

Every change must pass all three steps below. Do not mark a task done until all three succeed.

1. Lint

npm run lint

This runs tsc --noEmit (type-check) then eslint with zero warnings allowed. Fix all reported issues before proceeding.

2. Tests with coverage

npx vitest --run --coverage --reporter=verbose --coverage.reporter=text
  • --run: non-interactive single pass
  • --reporter=verbose: prints each test name with pass/fail — easy to scan for failures
  • --coverage.reporter=text: prints a coverage table to stdout — no browser needed

All tests must pass. Coverage must not decrease from the baseline reported on master.

Current baseline (master):

File % Stmts % Branch % Funcs
All files 96.24 94.31 96.7
src 87.8 89.65 75
src/lib 97.98 95.05 100

npm run test is equivalent but also triggers lint, which is slower when iterating. Use the npx vitest ... command during development and run npm run test as the final gate.

3. Documentation

Review whether the change requires documentation updates:

  • README.md — update when the public API surface, usage examples, CLI options, or any user-visible behaviour changes.
  • AGENTS.md — update when project structure, module map, test conventions, tooling, or agent-facing rules change.

If neither file needs updating, explicitly confirm that before marking the task done.

TypeScript rules (strict — violations cause npm run lint to fail)

  • noUncheckedIndexedAccess: arr[i] and record[key] are typed T | undefined. Always guard before use.

  • exactOptionalPropertyTypes: { x?: T } does not accept x: undefined — omit the property instead.

  • isolatedDeclarations: exported function/type signatures must be fully explicit — no inferred return types on public exports.

  • import type: use import type for any import used only as a type. ESLint (@typescript-eslint/consistent-type-imports) enforces this.

  • node: prefix: always use the node: protocol for built-in imports (e.g., import { randomUUID } from 'node:crypto').

  • Import order (enforced by eslint-plugin-import-x):

    1. Node.js built-ins (node:*)
    2. External packages
    3. Internal / relative paths

    Separate each group with a blank line.

Coding style

  • Boring over clever. Write straightforward, easy-to-read code. Avoid one-liners, clever tricks, or expressions that require a second read to understand. If it needs a comment to explain what it does, rewrite it instead.
  • Short functions. Each function should do one thing. If a function is growing long or handles multiple concerns, split it.
  • No duplication. Shared logic belongs in a shared helper. Refactoring to eliminate duplication is expected and encouraged — it is part of the job, not optional cleanup.
  • Refactoring is in scope. Both source and test code may be refactored when it improves clarity or removes duplication. Test refactoring does not require special approval as long as no passing test is deleted or weakened (see Test integrity).
  • Readability over brevity. Prefer explicit variable names and intermediate variables over chained expressions. The reader should be able to follow the logic without tracing execution mentally.

Adding new source files

  1. Copy the copyright header from any existing src/lib/*.ts file.
  2. Add a /** @module lib/your-module */ JSDoc comment below the header.
  3. Add JSDoc to every exported symbol (jsdoc/require-jsdoc is enforced for public exports).
  4. If the symbol is part of the public API, re-export it from src/index.ts.

Testing conventions

Tests are the behavioural contract of this library and are more critical than the implementation code itself. The rules below are strictly enforced.

Test integrity — read carefully

  • Every code change must be accompanied by unit tests that cover the new or modified behaviour. A PR without tests for its changes will not be accepted.
  • All code branches must be covered. When a branch genuinely cannot be reached in tests (e.g. a defensive assertion against an impossible runtime state), you must explicitly tell the user which branch is uncovered, why it cannot be tested, and what the underlying reason is. Do not silently leave branches uncovered.
  • Modifying existing passing tests is strictly forbidden without the explicit agreement of the user. If you believe a test must be changed, stop, explain clearly which test you want to modify and exactly why, and wait for approval before touching it.

Conventions

  • Test files live in test/ and mirror the src/lib/ path (e.g., src/lib/foo.tstest/foo.test.ts).
  • Reuse shared utilities — do not reinvent them:
    • test/lib/test_helpers.ts: verifyTokenWithKey, createMockRequest
    • test/lib/cli-fake-runner.ts: exec(args) — runs cli() with captured stdout/stderr; use in CLI tests
    • test/keys/index.ts: getParsedKey(filename) loader for the JSON key fixture files in the same directory (test-rs256-key.json, test-es256-key.json, test-eddsa-key.json)
  • The Vitest setup file (test/lib/_vitest-setup.ts) promotes unhandled rejections to thrown errors — async tests must handle all promise rejections.
  • Do not modify test/integration/ — it tests the published package and runs separately.

Out of scope for agents

Never modify these generated/external directories:

  • dist/ — build output
  • coverage/ — test coverage output
  • node_modules/
  • test/integration/