diff --git a/.gitignore b/.gitignore index 2c1bace00..bd495c6d7 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,7 @@ yarn-error.log # AI IDE .cursor/ + +# Spec-kit scaffolding (regenerable locally); keep only the governance memory tracked +.specify/* +!.specify/memory/ diff --git a/.specify/memory/constitution.md b/.specify/memory/constitution.md new file mode 100644 index 000000000..04a6109e7 --- /dev/null +++ b/.specify/memory/constitution.md @@ -0,0 +1,118 @@ +# node-vtex-api Constitution + +> Family: `node-library` +> This file is the source of truth for non-negotiable engineering principles. +> Agents MUST honor it before any other instruction. Updates require an +> explicit PR review by the maintainers listed in CODEOWNERS. + + +## Core Principles + +### I. The Public API Is a Contract + +The library's public API is whatever is reachable through its declared entry points (`main` → `lib/index.js`, `typings` → `lib/index.d.ts`). **Anything reachable is owned**; anything not reachable is internal and may change at will. + +- `main` + `typings` in `package.json` are the single source of truth for what consumers can import. Adding a new public export is an additive change; removing or renaming one is a breaking change. +- `src/index.ts` re-exports the entire public API explicitly (`export * from './'`). Wildcard re-exports from `src/index.ts` MUST point only to modules that are themselves part of the public API. +- Internal modules (helpers, types not meant to be imported externally) MUST NOT be re-exported from `src/index.ts`. +- Type-level breakage on a consumer (e.g. narrower generic, removed overload) is a MAJOR change even when the runtime behavior is preserved. + +### II. Semver Is Not Optional + +The library follows semantic versioning strictly: + +- **MAJOR** when a public type changes shape, a public function changes signature, behavior, or thrown errors in a way consumers can observe, or a runtime requirement (Node version in `engines.node`, peer dep major) increases. +- **MINOR** for additive public API or new optional behavior. +- **PATCH** for bug fixes that preserve the contract. + +Releases MUST update `CHANGELOG.md` describing the change in user-facing terms before tagging. Tags trigger the CI publish workflow; local `npm publish` / `yarn publish` is blocked by `scripts/publishLock.sh` and reserved for emergency hotfixes that MUST still be documented in `CHANGELOG.md`. + +### III. Environment Access Is Bounded and Documented + +Unlike a pure library, this package serves as the runtime for VTEX IO Node services and therefore reads `process.env` to discover platform-injected configuration (account, workspace, region, ports, telemetry endpoints). This is a deliberate trade-off, not a license to spread env reads everywhere. + +- New `process.env.*` reads MUST live in `src/constants.ts`, `src/service/`, or another already-identified runtime-bootstrap module — not in `src/HttpClient/`, `src/clients/`, `src/caches/`, `src/utils/`, `src/errors/`, or `src/metrics/MetricsAccumulator` aside from existing reads. +- The set of env vars the library depends on is documented (today via `src/constants.ts`); adding a new env var is a MINOR change and MUST be reflected in the changelog. +- Configuration for *consumer-facing* APIs (clients, HTTP middlewares, custom services) MUST come through function arguments, options objects, or factory functions — never through implicit globals. +- Top-level side effects are limited to the documented bootstrap path (`process.env.FORCE_COLOR = '1'` in `src/index.ts` is the only currently accepted module-load mutation). New top-level side effects require an ADR. + + +### IV. The Build Output Is Deterministic + +- The output directory `lib/` MUST be reproducible from source — running `yarn build` on a clean checkout MUST yield the same artifacts. +- Build outputs MUST NOT be committed; `.gitignore` already excludes `lib/`. CI publishes through the `Publish from CodeArtifact to npm` workflow. +- The `files` array in `package.json` (`["lib/", "gen/"]`) is the explicit publish surface; do not rely on `.npmignore` to redact accidentally-included files. +- Type declarations (`*.d.ts`) MUST be emitted by `tsc` (`declaration: true` is set in `tsconfig.json`) and MUST cover every public export. +- The generated JSON schema (`yarn gen`) lives in `gen/` and is part of the published artifact; regenerating it MUST happen as part of `yarn ci:build`, not by hand on a developer machine. + +### V. Tests Cover the Public API + +- Every public export MUST have at least one test that exercises it through `@vtex/api`'s public entry, not by reaching into internals. +- Tests run via `yarn test` (Jest 25 with `ts-jest`). The Jest config in `jest.config.js` is canonical; do not introduce parallel test configs in scripts or per-folder. +- The `__tests__` and `*.test.ts` files MUST stay colocated with the modules they cover under `src/`, mirroring the structure rather than living in a global `tests/` tree. +- Dropping a Node version from `engines.node` is a MAJOR change. + + +## Stack-Specific Standards + +- **Language**: TypeScript `^4.4.4` (`strict: true` in `tsconfig.json`). +- **Runtime**: Node.js `>=8` per `engines.node`; CI publish workflow runs on Node `22`. The advertised `engines.node` value is intentionally permissive because the library is consumed by older VTEX IO Node builders; any tightening is a MAJOR change. +- **Package manager**: Yarn (classic) — `yarn.lock` is the source of truth, MUST be committed, and CI installs with `yarn install --frozen-lockfile` semantics. +- **Entry points**: `main` → `lib/index.js`, `typings` → `lib/index.d.ts`, `files` → `["lib/", "gen/"]`. +- **Linting**: `tslint` extending `tslint-config-vtex` (`tslint.json`). Inline `tslint:disable` requires a justifying comment. The codebase still uses TSLint deliberately — migration to ESLint is a separate decision the team has not yet made. +- **Formatting**: Prettier (`.prettierrc`) on `src/**/*.ts` and `src/**/*.js`. CI enforces format via `yarn ci:prettier-check`. + +### Build: `tsc` + +- The build runs `tsc` against `tsconfig.json`; no bundler is involved. +- `declaration: true` is set so type declarations are emitted into `lib/`. +- `tsconfig.json` excludes `**/__tests__` and `**/*.test.ts`; do not import test-only helpers from production source paths. +- The `paths` mapping (`"axios": ["src/axios.d.ts"]`) exists to keep axios's type surface stable; touch it only in coordination with a dependency upgrade. + +## Architectural Boundaries + +The `src/` tree is the working layering. Cross-layer rules below are expressed as "MUST NOT import" constraints; agents MUST surface (in the PR description) any change that crosses them. + +| Layer (folder) | May import from | MUST NOT import from | +|-----------------------------------------------------|----------------------------------------------------------------|-------------------------------------------------| +| `src/utils/`, `src/errors/`, `src/typings/` | `node_modules`, other `utils`/`errors`/`typings` | `service/`, `clients/`, `HttpClient/`, `caches/`, `metrics/`, `tracing/`, `responses.ts` | +| `src/HttpClient/` | `utils`, `errors`, `typings`, `constants`, `tracing`, `metrics` (read-only metric APIs) | `service/`, `clients/`, `caches/` | +| `src/caches/` | `utils`, `errors`, `typings` | `service/`, `clients/`, `HttpClient/` | +| `src/clients/` | `HttpClient/`, `caches/`, `utils`, `errors`, `typings`, `constants` | `service/` | +| `src/tracing/`, `src/metrics/` | `utils`, `errors`, `typings`, `constants` | `service/`, `clients/`, `HttpClient/` (no back-edges) | +| `src/service/` | everything above | n/a (top of the dependency graph) | +| `src/index.ts` | every module that is part of the public API | internal-only helpers | + +Operational rules attached to these boundaries: + +- New top-level folders under `src/` MUST be added to this table in the same PR that introduces them. +- A consumer importing `@vtex/api` MUST NOT cause the entire `service/worker` runtime to be loaded if they only need clients or caches. Top-level side effects in non-`service/` modules are forbidden. + +## Operational Constraints + +The canonical commands for this repository are: + +- Install: `yarn` +- Build: `yarn build` (= `yarn clean && tsc`) +- Watch: `yarn watch` +- Generate schema: `yarn gen` +- Test: `yarn test` +- Lint: `yarn lint` +- Format: `yarn format` +- CI build: `yarn ci:build` +- CI test: `yarn ci:test` +- CI Prettier check: `yarn ci:prettier-check` + +Additional constraints: + +- The `prepublishOnly` hook runs `scripts/publishLock.sh`, which refuses to publish unless `IS_CI=true`. This MUST NOT be bypassed; emergency hotfixes still publish through CI, never from a local machine without an explicit decision documented in the changelog. +- Publishing happens through the `.github/workflows/publish-npm.yml` workflow (CodeArtifact → npmjs). The skill MUST NOT modify this workflow as part of governance bootstraps. +- Adding a runtime dependency MUST be a deliberate decision; review every new dependency for license, maintenance status, security advisories, and whether it could be a `devDependency` or peer dep instead. + +## Governance + +- This constitution overrides any conflicting guidance found in `AGENTS.md`, `README.md`, ad-hoc Slack threads, or agent skills. Where guidance conflicts, the constitution wins. +- Amendments are made by Pull Request to this file, reviewed by at least one maintainer listed in `CODEOWNERS` (currently `@vtex/composable-commerce-sq4`). The PR description MUST justify the change, and the version line below MUST be updated according to the spec-kit semantic-versioning rule (MAJOR for principle removal/redefinition, MINOR for additions, PATCH for clarifications). +- Agents (Cursor, Claude Code, Copilot, Bugbot, etc.) MUST read this file before generating or modifying code in this repository and MUST surface — in the PR description — any rule they were unable to satisfy, instead of silently working around it. + +**Version**: 1.0.0 | **Ratified**: 2026-05-14 | **Last Amended**: 2026-05-14 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..fb963a1e8 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,148 @@ +# AGENTS.md — node-vtex-api + +## Project Overview + +`@vtex/api` is the VTEX IO API client library and embedded runtime for VTEX IO Node services. Consumers `import { Service, ... } from '@vtex/api'`, declare their clients and routes, and the library boots a Koa-based runtime under the hood, exposes typed clients for VTEX platform APIs, and wires in tracing, metrics, caching, and error reporting. + +This is a published npm library: `@vtex/api@7.3.1`. Tech stack: TypeScript `^4.4.4`, Node.js `>=8` advertised in `engines.node` (CI publish runs on Node `22`), Yarn (classic), `tsc` build, Jest `^25.1.0` with `ts-jest`, TSLint with `tslint-config-vtex`, Prettier. + +## Tech Stack + +- TypeScript `^4.4.4` (`strict: true`). +- Node.js `>=8` per `engines.node`; CI publish on Node `22`. +- Yarn (classic) — `yarn.lock` is the source of truth. +- Build: `tsc` against `tsconfig.json` → outputs to `lib/`. +- Tests: Jest `^25.1.0` + `ts-jest`. +- Lint: `tslint` + `tslint-config-vtex`. +- Format: Prettier (`.prettierrc`). +- Runtime deps include Koa 2, GraphQL 14, axios 1.8, jaeger-client, opentelemetry, prom-client. These are part of the contract — bumping their majors is a MAJOR release. + +## Prerequisites + +- Node.js `>=8` (for working with the codebase, prefer the version CI uses: `22`). +- Yarn (classic, v1). +- No other tools required for local development. + +```bash +yarn +``` + +### Build & Run + +```bash +# Clean build (outputs to lib/) +yarn build + +# Watch / incremental build +yarn watch + +# Regenerate JSON schema in gen/ (used by the public API) +yarn gen + +# CI build (build + gen) +yarn ci:build +``` + +There is no "run" — this is a library. To exercise local changes inside a consumer app, use `yarn link`: + +```bash +# In this repo: +yarn link +yarn watch + +# In the consuming app's `node/` folder: +yarn link @vtex/api +``` + +Remember to `yarn unlink @vtex/api` when you're done. + +### Test Commands + +```bash +# All tests +yarn test + +# CI tests with coverage +yarn ci:test + +# Lint +yarn lint + +# Prettier check (CI mode) +yarn ci:prettier-check + +# Format the source tree +yarn format +``` + +Jest is configured in `jest.config.js` (root: `/src`, transform via `ts-jest`, mock for `@vtex/diagnostics-semconv` under `__mocks__/`). + +### Public API + +The library's published surface is: + +| Field | Value | +|-------|-------| +| `name` | `@vtex/api` | +| `main` | `lib/index.js` | +| `typings` | `lib/index.d.ts` | +| `files` | `["lib/", "gen/"]` | + +`src/index.ts` lists every public re-export explicitly. Anything reachable through these entry points is part of the public API and is owned. Anything else is internal and may change at will. + +### Architecture Boundaries + +| Folder | Responsibility | +|--------|----------------| +| `src/index.ts` | Single public entry. Explicit re-exports only; no wildcards from internal modules. | +| `src/HttpClient/` | HTTP client + middleware chain (axios-based). Used by every other client. | +| `src/clients/` | Typed clients for VTEX platform APIs. Depends on `HttpClient` and `caches`. | +| `src/caches/` | In-process and disk caches used by clients. | +| `src/service/` | VTEX IO runtime (Koa server, worker, master, lifecycle). Top of the dependency graph. | +| `src/metrics/`, `src/tracing/` | Telemetry primitives (OpenTelemetry, Prometheus, Jaeger). | +| `src/errors/`, `src/utils/`, `src/typings/` | Shared low-level building blocks. MUST NOT depend on `service/`, `clients/`, or `HttpClient/`. | +| `src/constants.ts` | Centralized env-var reads and platform constants. New `process.env.*` reads belong here. | +| `lib/` | Build output (`tsc`). NEVER committed; reproduced by `yarn build`. | +| `gen/` | Generated JSON schema (`yarn gen`). Part of the published artifact. | +| `docs/` | Long-form docs (metrics catalog, tracing). README is the entry point. | +| `__mocks__/` | Jest manual mocks. | + +Rules: + +- `src/index.ts` MUST re-export the public API explicitly. Wildcard exports from internal-only modules are forbidden. +- `src/service/` is the only place allowed to instantiate a Koa app or call `app.listen`. +- New `process.env.*` reads MUST live in `src/constants.ts`, `src/service/`, or another runtime-bootstrap module — never in `clients/`, `HttpClient/`, `caches/`, `utils/`, or `errors/`. +- Configuration for consumer-facing APIs comes through function arguments / options objects — not implicit globals. + +### Coding Conventions + +- TypeScript `strict: true`. Suppression annotations (`// @ts-ignore`, `// @ts-expect-error`, `// tslint:disable`) require a justifying comment. +- Type declarations (`*.d.ts`) cover every public export and are emitted by `tsc` into `lib/`. +- Tests are colocated with the modules they cover under `src/` (Jest picks up `*.(test|spec).ts(x)`). +- Tests MUST exercise behavior through the public API, not by reaching into internals. +- Format with Prettier before committing (`yarn format`); CI runs `yarn ci:prettier-check`. + +### Build with `tsc` + +- The build runs `tsc` against `tsconfig.json`. `declaration: true` is set; type declarations are part of the deliverable. +- `tsconfig.json` excludes `**/__tests__` and `**/*.test.ts`; do not import test-only helpers from production source paths. +- The `paths` mapping (`"axios": ["src/axios.d.ts"]`) exists to keep axios's public types stable across upgrades — only touch it in coordination with a dependency bump. + +### Versioning & Release + +- Semver is strict. Public type or signature changes are MAJOR; additive public surface is MINOR; bug fixes preserving the contract are PATCH. +- Tightening `engines.node` is MAJOR. +- Releases update `CHANGELOG.md` in user-facing terms before tagging. +- Publishing is CI-driven (`.github/workflows/publish-npm.yml`, CodeArtifact → npmjs). `scripts/publishLock.sh` refuses local `yarn publish` unless `IS_CI=true`; do not bypass it for non-emergency publishes. + +### Safety Guardrails + +- NEVER commit `node_modules/`, `lib/`, or any build artifact (already covered by `.gitignore`). +- NEVER commit `.npmrc` containing tokens, registry credentials, or any secret. +- NEVER add `process.env.*` reads outside `src/constants.ts` or `src/service/`. +- NEVER perform side effects on import in non-`service/` modules (network calls, file I/O, mutable singletons at module scope). The single accepted top-level mutation is `process.env.FORCE_COLOR = '1'` in `src/index.ts`. +- NEVER expose internal modules through wildcard re-exports from `src/index.ts`. +- NEVER drop a Node version from `engines.node` without a MAJOR version bump. +- NEVER bypass `scripts/publishLock.sh`; emergency hotfixes still publish through CI. +- NEVER edit `.github/workflows/publish-npm.yml` as part of a governance / agent-driven change; that workflow has security-sensitive permissions (`id-token: write`). +- NEVER instantiate a Koa app or call `app.listen` outside `src/service/`.