feat: mechanical policy-free codegen surface - #158
Conversation
- Export RequireAll, deep Writable, and new deep Mutable direction utilities - Direction-explicit api-schemas.ts: reachability-filtered Responses namespace, @deprecated bare aliases, traceability Source/Referenced-by JSDoc - --default-non-nullable flag forwarded via programmatic openapi-typescript API - Config-file multi-spec codegen (openapi-codegen.config.json), lint-clean output by construction (internal eslint --fix removed, prettier programmatic) - Strip Record<string, never> sentinel from generated unions - x-direction-finalized root-marker detection (log-only advisory)
There was a problem hiding this comment.
🟡 Changes recommended
It introduces a type-level regression/inconsistency (RequireAll lacks the documented leaf guard) and a packaging issue (openapi-typescript listed as both peer and direct dependency) that should be corrected before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR updates the library/CLI for the 0.28.0 release to make code generation fully programmatic (OpenAPI spec → formatted TS output) while expanding the exported “direction” type utilities and adding new codegen surface features (Responses namespace, config-file mode, and traceability JSDoc).
Changes:
- Switch CLI type generation to the programmatic
openapi-typescriptAPI + programmatic Prettier formatting, plus a post-transform to stripRecord<string, never>union sentinels. - Add/expand “direction vocabulary” utilities (
RequireAll, deepWritable, newMutable) and export them from the package root. - Add config-file mode and new/updated tests/fixtures to cover marker detection, defaultNonNullable forwarding, schema direction output, and regression cases.
File summaries
| File | Description |
|---|---|
| vitest.config.ts | Increases suite timeouts to accommodate heavier integration/codegen tests. |
| tests/unit/value-schemas-emit.test.ts | Makes value-schema extraction tolerant of prettier-formatted TS literals; fixes bundling by defining import.meta.url. |
| tests/unit/union-sentinel.test.ts | Adds regression + integration coverage for stripping Record<string, never> from unions. |
| tests/unit/direction-marker.test.ts | Adds subprocess coverage for x-direction-finalized advisory logging behavior. |
| tests/unit/cli.test.ts | Adds unit coverage for --default-non-nullable parsing behavior. |
| tests/unit/cli-integration.test.ts | Expands integration tests for defaultNonNullable, config-file mode, and lint-clean generated output. |
| tests/unit/api-schemas-direction.test.ts | Adds integration tests for Responses namespace emission and Source/Referenced-by JSDoc. |
| tests/typing/direction-utilities.ts | Adds type-level assertions for RequireAll, deep Writable, and Mutable. |
| tests/fixtures/group-union-openapi.json | Adds fixture spec reproducing the union sentinel issue. |
| tests/fixtures/finalized-openapi.json | Adds fixture spec containing x-direction-finalized: true. |
| tests/fixtures/direction-openapi.json | Adds fixture spec to validate response-reachability and Source JSDoc behavior. |
| tests/fixtures/api-types.ts | Updates fixtures for new multi-line JSDoc (Response/StrictResponse) + Source annotations. |
| tests/fixtures/api-schemas.ts | Updates fixtures for Responses namespace + deprecated bare aliases. |
| src/types.ts | Exports and documents RequireAll/deep Writable and adds new deep Mutable utility. |
| src/index.ts | Re-exports the direction utilities from the package root with public-facing docs. |
| src/codegen-transforms.ts | Introduces post-generation transform for stripping the union sentinel artifact. |
| src/cli.ts | Implements programmatic codegen + prettier formatting, config-file mode, marker advisory logging, response-reachability, and Source/Referenced-by JSDoc. |
| README.md | Documents config-file mode, new flags, and direction utilities/Responses namespace usage. |
| package.json | Bumps version to 0.28.0 and adds runtime dependencies for the programmatic CLI. |
| package-lock.json | Lockfile updates for the new runtime dependencies and version bump. |
| CHANGELOG.md | Adds 0.28.0 release notes describing new features, changes, deprecations, and fixes. |
| .gitignore | Adds .pi/ to ignored artifacts. |
Review details
Suppressed comments (1)
src/types.ts:415
RequireAll<T>currently deep-maps any object type, which will also expand built-ins likeDate,Blob,File, and function types into huge structural object types. This contradicts the documented “leaf guard” behavior and can break consumer types (e.g.,RequireAll<Date>no longer beingDate).
export type RequireAll<T> = T extends (infer E)[]
? RequireAll<E>[]
: T extends readonly (infer E)[]
? readonly RequireAll<E>[]
: { [K in keyof T]-?: RequireAll<T[K]> }
- Files reviewed: 20/22 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…ep, correct excludePrefix config doc)
- RequireAll gains the same leaf guard as Writable/Mutable: unknown, Date,
Blob, File and function values pass through instead of degrading to {}
- Sentinel-strip transform preserves unions whose only other member is
null/undefined (e.g. index-signature value types)
- api-schemas.ts omits the RequireAll import when no schema is
response-reachable (avoids TS6133 under noUnusedLocals)
- generateTypes receives a structuredClone of the spec so openapiTS cannot
mutate the object concurrently read by sibling generators
- Config loader validates option values and fails fast on invalid ones
- README no longer claims CLI args override config values
- Comments referencing internal planning documents removed or made
self-contained
Summary
0.28.0 minor release: makes the package a purely mechanical spec→types+runtime surface. All contract policy stays in the OpenAPI spec; consumers no longer need generic type plumbing to compensate for generated-output shape.
Added
RequireAll(response presence policy),Writable(now deep: nestedreadOnlyprops excluded from request types), and newMutable(deep readonly-modifier stripper). Leaf guards keepunknown,Date,Blob,File, and functions intact.api-schemas.ts: newResponsesnamespace for the response-reachable component set (computed $ref closure, never name-suffix heuristics), each member wrapped inRequireAllwithReferenced by:JSDoc. NoRequestsnamespace: request bodies stay operation-shaped (Types.<opId>.Request).Source: components['schemas'][...] (METHOD /path)onTypes.<opId>members; greppable FE-type→BE-schema mapping.--default-non-nullable <true|false>forwarded natively to openapi-typescript (defaulttruethis minor; flips tofalsenext major).openapi-codegen.config.jsonwith shared options + multi-spec list; CLI args override config.x-direction-finalizedmarker detection (log-only advisory that presence policy is spec-side).Changed
openapiTS+astToStringAPI (nonpxshell-out).eslint --fixpost-step removed).Response/StrictResponseduality documented as transitional.Deprecated
api-schemas.tsaliases (@deprecatedpointing toResponses.XorTypes.<opId>.Request); removal in next major.Fixed
Record<string, never>sentinel stripped from generated group unions.Validation
npm run lint,types,types:test,format:check: cleannpm run test:run: 642/642 across 25 files, including fixture byte-equality and generated-output eslint-zero-errors integration tests