Repository navigation
feat!: v4 — native renderer by default, typed invalidation maps, Bun 1.4.4 - #104
Open
urbankrepel-povio wants to merge 18 commits into
Open
urbankrepel-povio wants to merge 18 commits into
urbankrepel-povio wants to merge 18 commits into
Conversation
render_local_models called render_common_models once per tag, and each call re-walked every component schema (collect_enum_objects) and rebuilt the property reference names for all schema refs. The inline-enum import lookup also scanned all schemas per enum. Together this was O(tags x schemas). Collect enum objects and property reference names once, apply the per-tag namespace prefix at lookup time, and index inline enum names by code. Native compile on a 400x petstore spec (6,400 paths): 3.3 s -> 345 ms. Output is byte-identical to the JS renderer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the default options (tsNamespaces without modelsInCommon, acl on) the native renderer was not trusted to be complete, so generation went through the hybrid path: Rust rendered everything, then JS rendered again. That Rust render was also super-linear, making the default CLI run ~20x slower than plain JS (12.1 s vs 0.56 s on a 200x petstore spec). - resolve_model_namespace_owners: build the model-owner index once per render (ModelOwners) instead of per tag module, and rewrite doc-comment type references in one pass instead of one replace per owned model. - render_app_acl: emit the untyped AppAbility file when there are no ACL rules, matching the TypeScript renderer. - Allow every namespace/model layout to render fully native; the 64-layout parity suite shows identical output. Default options now render fully native with byte-identical output (up to 6,804 files); generation on a 400x petstore spec takes ~385 ms vs ~600 ms in JS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pin Bun 1.4.3 in packageManager, engines, .tool-versions, @types/bun and the CI workflows. Scripts already run through Bun, so tsx was unused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run `typecheck` with tsrs (oxc-project/tsrs), a Rust port of the TypeScript 7 checker. It reports the same diagnostics as tsc here and is ~1.8x faster with ~2.2x less memory. The version is pinned exactly while tsrs is pre-1.0. Binaries exist for macOS arm64 and Linux; elsewhere use `bunx tsc --noEmit`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace every `any` with a real type or `unknown`: - Runtime library: request `data` is `unknown`, error handlers are `ErrorHandler<never>` (as useAclCheck already uses), interceptor tuples are `unknown[]`, and RestClient's constructor takes `RestInterceptor<[]>` since it attaches them without arguments. - InvalidationMap/InvalidationMapFunc default to `unknown`; method syntax keeps callbacks with annotated parameters assignable. - Generator and CLI helpers use SchemaResolver, OpenAPIV3 and yargs types; the yargs middleware merges the loaded config into argv as yargs would. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The publish workflow only ran for v1-v3 tags. Stripping the release dylib
(`strip = "symbols"`, and `"debuginfo"` too) produces a binary that recent
macOS refuses to load ("mis-aligned LINKEDIT string pool"), which would make
the CLI silently fall back to JS. Ship it unstripped (~0.4 MB larger).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4.0.0-rc.1 typed the clients' errorHandler as ErrorHandler<never>, which rejects handlers with specific codes such as ErrorHandler<"BAD_REQUEST" | "UNAUTHORIZED">. The clients only call rethrowError, so type them with ErrorRethrower (exported), which every ErrorHandler<Codes> satisfies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4.0.0-rc.1 gave invalidation callbacks `unknown` data and variables, so
callbacks like `(data, { officeId, id }) => ...` no longer type-checked.
queryModules.ts now also exports `QueryModuleMutations`: for each module,
`MutationEffectsOf<typeof Queries.useCreate | ...>` over its mutation hooks.
`InvalidationMap<QueryModule, QueryModuleMutations>` types each module's
callback with that module's mutation `data` (union) and `variables` merged
into one object, where keys only some mutations take are optional. Without
the second parameter callbacks keep `unknown`.
Both renderers emit identical output; a new test type-checks a generated
petstore client against a typed map for both renderers and namespace modes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rc.2 typed invalidation callbacks only through `InvalidationMap<QueryModule, QueryModuleMutations>`, so existing `InvalidationMap<QueryModule>` declarations still got `unknown`. The generated queryModules.ts now extends a global `OpenApiCodegenQueryModuleMutations` interface with QueryModuleMutations, and InvalidationMap defaults its mutations to it. A global interface, because interfaces re-exported from the bundled declaration chunks can't be augmented through the package entry points. useMutationEffects calls the callback through the untyped InvalidationMapFunc, as the module is only known at runtime. Verified against the packed package with InvalidationMap imported from the root, /query and /config entry points. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop the `builderConfigs` option and the `*.configs.ts` files it generated, with its options (`filterParamName`, `dataResponseParamNames`, `dynamicInputsImportPath`, `dynamicColumnsImportPath`), the `configs` output type, the CLI flag and the native renderer's option. They are no longer used. Helpers only the configs generator used are removed too. BREAKING CHANGE: `builderConfigs` and its options are gone, and `configs.configs` is no longer part of the `configs` option. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A bare `InvalidationMap` (no type arguments, as the TOS FE declares it) was keyed by any module key, so it never matched the registered mutation types and callbacks still got `unknown`. - queryModules.ts also registers its QueryModule enum globally (OpenApiCodegenQueryModules), and InvalidationMap's default module type is that enum (RegisteredQueryModule), falling back to any key. - Variables merge at every depth: request-body fields only some mutations send become optional (`variables?.data?.projectId`). - Common keys are mapped through Extract<keyof T, PropertyKey>; a homomorphic `keyof T` mapping distributed over the union and skipped the merge. Checked against the TOS FE's invalidationMap.ts: 16 errors -> 4, all in one callback that names its first parameter `variables` although it is `data`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
packageManager, engines, .tool-versions, @types/bun and the CI workflows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merging main brought in #105, whose lazy-schema helpers still took the property reference names as `&HashMap<&str, String>`. This branch replaced that map with `PropertyRefNames` (built once per render), so the native crate didn't compile on the PR's merge ref. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Callback `data` was the union of a module's mutation responses, and `variables` kept `void` for mutations without variables. Both made callback parameters unions of distinct types, which strict lint rules (e.g. TOS's single-value-type check) reject and which forced narrowing before reading a field. - `data` merges like `variables` (MergedMutationData): fields only some responses have become optional, at any depth. - `void` becomes `undefined`, which is what callbacks receive for mutations without variables or response body. Checked against TOS: the variable-type check reports 0 violations, FE tsc and all FE tests pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Templates left spaces at line ends: JSDoc openers (`/** `), `@property`
lines with an empty description, `mutationFn: (...) => { ` and indented
blank lines. Formatters such as `bun fmt` keep whitespace inside comments,
so consumers saw thousands of whitespace-only lines.
Both renderers' files now go through trimTrailingWhitespace before they
are returned, which also covers descriptions taken from the spec. A test
checks petstore and the configuration fixture with both renderers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a 733-path production spec, the native renderer's output differed from the TypeScript renderer's in 66 of 614 files with the default options, and in many more with the frontend options. Both option sets are now byte-identical. - Inline enums with a component enum's values are named by that enum and listed with the other component enums, after the endpoint schemas. Enum codes leave out `null`, both in the resolver's dependency edges and when model imports are collected. - Query and body `@param` docs list every schema constraint, plus the media type's example. - Model imports in namespace mode follow each owner's first use. With `modelsInCommon`, imports go through the module's proxy. Queries import the native transport types before the upload client. - Top-level array schemas document their items' properties (`[0].x`). - Extracted enums keep the owner their component usages gave them. With `modelsInModules` they go to each of those modules, and proxies no longer re-export enums used only by generated schemas. - Recursive properties use `z.lazy(() => …)`, like the TypeScript renderer since #91. `additionalProperties: false` adds no index signature. - A generated schema's dependencies also follow refs that are already registered, and same-valued component enums when `extractEnums` is on. test/renderer-regressions.yaml reproduces each case with neutral names. It joins the parity fixtures, so every configuration scenario checks it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`excludeRedundantZodSchemas` (on by default) drops components that no included operation uses. Some specs carry such schemas on purpose, e.g. payloads that another app consumes, such as the BE's PDF payloads that a PDF renderer validates. `includeSchemas` lists component names to generate anyway, together with the schemas they depend on. It works in the config file and as a comma-separated `--includeSchemas` flag. Both renderers support it, and the parity fixture and option matrix cover it. `resolveConfig` now exports the CLI params type, which the runner used to duplicate. The native model proxies also list a component's extracted enums in extraction order, as the TypeScript renderer does. They used to follow the order of appearance in the schema code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The 4.0 line, released so far as
4.0.0-rc.1…4.0.0-rc.4on npm (next).InvalidationMapcallbacks. Types come from the spec, with noany.anyis removed fromsrc.builderConfigsis removed.Commits
Native renderer
tsNamespaces,acl) used to go through the hybrid path, which was ~20× slower than plain JS (12.1 s vs 0.56 s on a 200× petstore spec). Two more super-linear spots are fixed, and Rust emits the defaultAppAbilityfile. Every layout now renders fully native.v4.*tags. A stripped dylib fails to load on recent macOS ("mis-aligned LINKEDIT string pool"), so the addon now ships unstripped (~0.4 MB larger).Typed invalidation maps
queryModules.tsexportsQueryModuleMutations, built from each module's mutation hooks viaMutationEffectsOf<typeof Queries.useCreate | …>.InvalidationMapandInvalidationMap<QueryModule>are both typed from the spec.datais the union of the module's mutation responses.variablesare merged into one object, and fields that only some mutations send become optional at any depth.Types and cleanup
anyfrom src. Real types orunknowneverywhere.ErrorRethrower(exported), soErrorHandler<"BAD_REQUEST" | …>fits.Tooling
packageManager,engines,.tool-versions,@types/bunand the CI workflows.tsc7.0 on this repo (tsrs targets TS 7.1). It is ~2× faster here and ~10× faster on a 2,400-file app.Breaking changes
builderConfigsand its options are gone:filterParamName,dataResponseParamNames,dynamicInputsImportPath,dynamicColumnsImportPath, andconfigs.configs.QueryModuleMutationsreceiveunknowninstead ofany. Callbacks of generated modules are typed from the spec, so code that reads fields some responses lack, or destructures the variables of a mutation that has none, now needs narrowing.InvalidationMap's second type parameter is now the mutations map (it wasTData).InvalidationMapFuncdefaults tounknown.RestClient'sinterceptorsoption isRestInterceptor<[]>[], since interceptors are attached without arguments.Verification
test:tsandtest:nativepass 1,285 tests each. In the renderer-parity run, JS and native hash identically on 5,324 files across 876 option combinations.cargo testpasses.Quotescallback named itsdataparametervariables), the FEtscrun has 0 errors.Known issues (not changed here)
generate --input … --output …without a config file generates no endpoints.includeTagsappears to resolve to[""].🤖 Generated with Claude Code