diff --git a/AGENTS.md b/AGENTS.md index 32de3f1ba7..cc192d7481 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -71,9 +71,17 @@ Do not hand-edit `COMMAND_CATALOG`, `OPERATION_MEMBER_PATH_MAP`, `OPERATION_REFE - `pnpm build` - build all packages - `pnpm test` - unit tests - `pnpm dev` - dev server from `examples/` -- `pnpm run generate:all` - regenerate schemas, SDK clients, tool catalogs, reference docs -- `pnpm check:public-contract` - validate the published public type contract: wraps build + consumer typecheck matrix + strict supported-root audit. ~3 min. Scoped to the public type surface, not a replacement for `pnpm test` or `pnpm build`. Also the single command CI runs (with `--skip-build` after its own Build step). SD-3256 / SD-673. -- `pnpm report:public-contract` - print the public-contract tier metadata (supported / legacy / legacy-raw / asset / deprecated). Read-only. Source of truth: `packages/superdoc/scripts/type-surface.config.cjs` (`publicContract` export). SD-3256. +- `pnpm check:types` - raw TS compile across all referenced projects (alias of `pnpm run type-check`). Does NOT run the public-interface chain. +- `pnpm check:public` - **canonical pre-merge command for typed public surfaces.** Validates both `superdoc` (vite build + postbuild chain + consumer typecheck matrix + deep-type audit) and Document API (contract parity + output staleness + examples + overview). ~5 min. Non-mutating. Combines `check:public:superdoc` + `check:public:docapi`. +- `pnpm check:public:superdoc` - SuperDoc public package surface only (alias of `check:public-contract`). +- `pnpm check:public:docapi` - Document API public surface only (alias of `docapi:check`). Requires generated artifacts to be current; if it fails on staleness, run `pnpm generate:docapi`. +- `pnpm generate:docapi` - regenerate Document API outputs after editing the contract (alias of `docapi:sync`). Writes gitignored generated artifacts under the Document API package. Run before `check:public:docapi` if it fails on missing files. +- `pnpm generate:all` - regenerate schemas, SDK clients, tool catalogs, reference docs. +- `pnpm report:public:superdoc` - print public-contract tier metadata (supported / legacy / asset / deprecated). Read-only, not a gate. Source of truth: `packages/superdoc/scripts/type-surface.config.cjs`. + +Full system reference (script catalog, dataflow, CI vs local): `packages/superdoc/scripts/README.md`. + +Naming convention: `check:*` = non-mutating, safe in CI. `generate:*` = mutates files. `report:*` = read-only information, not a gate. Older command names (`check:public-contract`, `docapi:sync`, `report:public-contract`, etc.) remain as aliases. ## Testing diff --git a/package.json b/package.json index a806d24079..c875705e03 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,12 @@ "docapi:sync": "pnpm exec tsx packages/document-api/scripts/generate-contract-outputs.ts", "docapi:check": "pnpm exec tsx packages/document-api/scripts/check-contract-parity.ts && pnpm exec tsx packages/document-api/scripts/check-contract-outputs.ts && pnpm exec tsx packages/document-api/scripts/check-examples.ts && pnpm exec tsx packages/document-api/scripts/check-overview-alignment.ts", "docapi:sync:check": "pnpm run docapi:sync && pnpm run docapi:check", + "check:types": "pnpm run type-check", + "check:public:superdoc": "pnpm run check:public-contract", + "check:public:docapi": "pnpm run docapi:check", + "check:public": "pnpm run check:public:superdoc && pnpm run check:public:docapi", + "generate:docapi": "pnpm run docapi:sync", + "report:public:superdoc": "pnpm run report:public-contract", "test:cli": "pnpm --prefix apps/cli run test", "cli:prepare": "pnpm run test:cli && pnpm --prefix apps/cli run build:prepublish", "cli:publish:raw": "pnpm run cli:prepare && pnpm --prefix apps/cli run publish:platforms", diff --git a/packages/superdoc/scripts/README.md b/packages/superdoc/scripts/README.md new file mode 100644 index 0000000000..08354274f0 --- /dev/null +++ b/packages/superdoc/scripts/README.md @@ -0,0 +1,191 @@ +# SuperDoc public-interface validation scripts + +This directory holds the scripts that validate the published SuperDoc package's +public TypeScript surface. The repository also has a Document API public +interface under `packages/document-api/scripts/`; both are summarized here so +contributors have one place to learn the system. + +If you only have time for one sentence: **run `pnpm check:public` before +opening or merging anything that touches public types.** It's the +public type/interface preflight — it does not replace `pnpm test` or +`pnpm build` for product correctness. + +--- + +## TL;DR — what command to run when + +| Question | Command | Speed | Mutates files? | +|---|---|---|---| +| Are public interfaces safe? | `pnpm check:public` | ~5 min | no | +| TypeScript compiles cleanly? | `pnpm check:types` | seconds | no | +| Only SuperDoc public surface changed? | `pnpm check:public:superdoc` | ~3 min | no | +| Only Document API contract changed? | `pnpm check:public:docapi` | seconds | no | +| Regenerate Document API artifacts after editing the contract? | `pnpm generate:docapi` | seconds | **yes** | +| Quickly see public-contract tier metadata? | `pnpm report:public:superdoc` | seconds | no | + +`check:*` commands are non-mutating and safe to run anywhere. `generate:*` +commands write to the worktree (commit the changes). `report:*` commands are +read-only and not CI gates. + +--- + +## Vocabulary + +The repo's typed surface is split into two "public interfaces": + +- **SuperDoc** — the `superdoc` npm package. Public types and runtime exports + flow through `packages/superdoc/src/public/index.ts` and the subpath + facades (`./types`, `./ui`, `./ui/react`, `./headless-toolbar`, etc.). +- **Document API** — `editor.doc.*` programmatic API typed in + `packages/document-api/src/`. Built as TypeScript-only; the contract lives + in `operation-definitions.ts` and several artifacts are generated from it. + +Both are validated by the same root-level catch-all (`pnpm check:public`) +but have separate script chains because the validation needs differ. + +--- + +## Command catalog (root `package.json`) + +### Public-interface validation + +| Command | Runs | What it gates | +|---|---|---| +| `check:public` | `check:public:superdoc` + `check:public:docapi` | Both public interfaces. The umbrella to run before merging. | +| `check:public:superdoc` | `check:public-contract` (legacy alias) | SuperDoc package: vite build + postbuild chain, consumer typecheck matrix, deep-type audit. | +| `check:public:docapi` | `docapi:check` (legacy alias) | Document API: contract parity, generated outputs are not stale, examples compile, overview alignment. **Requires generated artifacts to be up-to-date** — if it fails on staleness, run `generate:docapi` to refresh. | +| `report:public:superdoc` | `report:public-contract` (legacy alias) | Read-only tier metadata (supported / legacy / asset / deprecated). Not a gate. | + +### TypeScript compiler + +| Command | Runs | What it gates | +|---|---|---| +| `check:types` | `type-check` (legacy alias) | `tsc -b tsconfig.references.json` — raw TS compile across all referenced projects. Does NOT run the SuperDoc public-contract chain. | +| `type-check:force` | `tsc -b --force` | Same as above but ignores incremental cache. | + +### Generation + +| Command | Runs | Mutates? | +|---|---|---| +| `generate:docapi` | `docapi:sync` (legacy alias) | yes — writes Document API artifacts under `packages/document-api/generated/` (gitignored; run before `check:public:docapi` if it fails on missing files). | +| `generate:all` | schemas, SDK clients, tool catalogs, reference docs | yes — multi-target generator. Some outputs are gitignored (`generated/`), others are committed (e.g. `apps/docs/document-api/reference/`); any tracked generated changes should be committed. | + +### Legacy aliases + +These names predate the standardized vocabulary and stay as `pnpm` +aliases for back-compat. New CI workflows and docs should use the +canonical names above. + +| Legacy | Canonical | +|---|---| +| `type-check` | `check:types` | +| `check:public-contract` | `check:public:superdoc` | +| `docapi:check` | `check:public:docapi` | +| `docapi:sync` | `generate:docapi` | +| `docapi:sync:check` | `generate:docapi && check:public:docapi` | +| `report:public-contract` | `report:public:superdoc` | + +--- + +## SuperDoc scripts in this directory + +These run as part of `check:public:superdoc` (most via the vite build's +postbuild chain). Each is described by what it gates and what would fail if +it stopped running. + +| Script | Stage | Gates | If removed | +|---|---|---|---| +| `type-surface.config.cjs` | data | Canonical taxonomy: relocations, base includes, shared-common targets, allowlists. Source of truth for 5+ other scripts. | Every consumer becomes independent; drift between vite / tsconfig / audit is invisible. | +| `ensure-types.cjs` | postbuild | Rewrites workspace specifiers in emitted `.d.ts` so consumers can resolve them; copies hand-written `.d.ts` files into dist. | Published declarations contain unresolvable `@superdoc/*` imports. | +| `check-tsconfig-type-surface.cjs` | postbuild | Asserts `tsconfig.json#include` equals the union of base + relocation paths in `type-surface.config.cjs`. | tsconfig and vite drift silently; IDE checks diverge from CI. | +| `audit-bundle.cjs` | postbuild | prosemirror-view single-instance check + bundle size budgets. | Duplicate PM instances break collaboration; no size discipline. | +| `audit-declarations.cjs` | postbuild | Rule 1: bare `@superdoc/*` leaks. Rule 2: pnpm-internal paths. Rule 3: `_internal-shims.d.ts` regression. | Private specifiers ship to consumers; consumers hit unresolvable imports. | +| `check-export-coverage.cjs` | postbuild | Every `package.json#exports` subpath carries a `types` field or is on the runtime-only allowlist. | `TS7016` returns for consumers on runtime-only subpaths. | +| `verify-public-facade-emit.cjs` | postbuild | Per-facade expected symbol set + ESM/CJS parity + legacy command-signature compat. Has a hand-maintained `expectedNames` allowlist per facade (consolidation tracked separately). | Symbol set drift ships silently; CJS shims diverge from ESM. | +| `report-declaration-reachability.cjs` | postbuild | Instrumentation (not a gate): per-bucket reachability ratio of emitted declarations. | Loses visibility into unreachable emit (the SD-2952 trim target). | +| `check-jsdoc.cjs` | CI step | Per-file checkJs gate for files in a hand-curated `CHECKED_FILES` allowlist. Currently 6 files. **Note**: `SuperDoc.js` now has `// @ts-check` but is gated by `check:types`, not this script. The 6-file list is a historical ratchet from before the broader enablement; consolidating with `check:types` is tracked separately. | A targeted regression on one of the 6 ratcheted files ships silently. | + +--- + +## Consumer-typecheck infrastructure (`tests/consumer-typecheck/`) + +These run against the **packed and installed** tarball, so they validate +what an actual consumer would see — not the workspace source. + +| Script | Gates | Notes | +|---|---|---| +| `typecheck-matrix.mjs` | 83 scenarios across module resolution (bundler/node16/nodenext) × strict × skipLibCheck × import path. | Packs the superdoc tarball into `node_modules/` once; later scripts reuse the install. | +| `deep-type-audit.mjs --strict-supported-root` | Walks every type reachable from `superdoc`'s public exports; fails on `any` leaks at any depth. | Compares against a committed allowlist; new findings fail, stale findings fail. | +| `snapshot.mjs --all --check` | No-growth snapshots: super-editor package exports, legacy subpath exports, root facade symbol inventory. | `--write` regenerates snapshots. | +| `check-all-public-types-fixture.mjs` | Asserts every type-only root export has an `AssertNotAny` line in `src/all-public-types.ts`. | Derives the expected set from `superdoc-root-classification.json`. | +| `package-shape-gate.mjs` | External package-shape linters (publint + attw) against the packed tarball. | Catches condition ordering, masquerading exports, missing field declarations. | +| `check-root-classification-closure.mjs` | Asserts no `supported-root` or `legacy-root` export references an `internal-candidate` symbol in its public declared type. | Closure rule from SD-3212. | + +`check:public:superdoc` runs `typecheck-matrix` and `deep-type-audit` +directly. `package-shape-gate`, `snapshot --all --check`, and +`check-root-classification-closure` currently run as separate CI steps — +folding them into `check:public:superdoc` is tracked as a follow-up so +release workflows can call one command without losing coverage. + +--- + +## Document API scripts (`packages/document-api/scripts/`) + +The Document API is contract-first: `operation-definitions.ts` is the +canonical source, and several artifacts are generated from it. + +| Script | Phase | What it does | +|---|---|---| +| `generate-contract-outputs.ts` | generate | Writes `generated/schemas/**` + `generated/agent/**` from the contract. Called by `generate:docapi`. | +| `check-contract-parity.ts` | check | Asserts derived maps (`operation-registry`, `invoke`, etc.) project from `operation-definitions` correctly. | +| `check-contract-outputs.ts` | check | Asserts the committed `generated/**` files match what regeneration would produce. Fails if stale. | +| `check-examples.ts` | check | Asserts contract examples compile. | +| `check-overview-alignment.ts` | check | Asserts the documentation overview reflects the current operation set. | + +The four `check-*` scripts run together via `check:public:docapi`. +Currently they require `generate:docapi` to have run first (the +stale-file check trips otherwise). Making the check self-contained +(in-memory generate and compare) is tracked as a follow-up. + +--- + +## Dataflow + +``` +type-surface.config.cjs (single source of truth) + │ + ├─ vite.config.js ──> emits dist/**/*.d.ts + ├─ tsconfig.json#include ──> typecheck source tree + ├─ ensure-types.cjs ──> rewrites + copies in dist + ├─ check-tsconfig-type-surface ──> tsconfig parity gate + └─ audit-declarations.cjs ──> reads relocationGuardPackages + +src/public/index.ts (declarative root facade) + │ + ├─ vite-plugin-dts ──> emits public/index.d.ts + ├─ verify-public-facade-emit ──> expected names allowlist + ├─ snapshot.mjs (root family) ──> drift snapshot + └─ check-all-public-types-fixture ──> consumer fixture coverage + +packages/document-api/src/contract/operation-definitions.ts + │ + ├─ generate-contract-outputs ──> writes generated/** + ├─ check-contract-parity ──> registry/invoke drift gate + └─ check-contract-outputs ──> committed-vs-fresh gate +``` + +--- + +## CI vs local + +- **`ci-superdoc.yml`** runs `pnpm check:public-contract --skip-build` after + its own Build step. This is the single command for the SuperDoc public + surface in CI. +- **`release-superdoc.yml`** currently runs the consumer-typecheck matrix, + deep-type audit, package-shape gate, snapshot check, and classification + closure as separate steps. Migrating to `check:public:superdoc` once that + command covers all the gates is tracked separately. + +Local pre-commit: just run `pnpm check:public`. If anything fails, the +failure message tells you which script and (for `check:public:docapi`) +which command to run to regenerate stale artifacts.