Spec patches
Fix a spec you cannot edit upstream. Patches apply before every generation, and a patch that stops matching is reported, never silently dropped.
Some specs have a wrong type, a name that reads badly in code, or a field that should not be there, and the fix upstream is weeks away. Spec patches let a project carry small fixes that apply to the spec before every generation. The source stays untouched. The generated packages get the corrected document.
Shape
typeship projects update <project_id> --spec-patches '[{"op":"set","path":"/paths/~1accounts/get/operationId","value":"listAccounts","reason":"stable name"}]' replaces the whole list; read the current one first with typeship projects get <project_id>. Then typeship projects generate <project_id> and diff the warnings.A patch is a JSON object with an operation and a JSON Pointer path. Add them under spec patches in project settings, or through spec_patches on the typeship API. Up to 50 per project.
[
{ "op": "set", "path": "/components/schemas/Account/properties/id/type", "value": "string", "reason": "ids are strings" },
{ "op": "rename", "path": "/components/schemas/Acct", "to": "Account" },
{ "op": "remove", "path": "/paths/~1internal~1debug" },
{ "op": "append", "path": "/servers", "value": { "url": "https://sandbox.acme.example.com/v1", "description": "Sandbox" } }
]| Op | Does | Requires |
|---|---|---|
set | Replaces the value at the path. Creates a missing final key when the parent exists and the path is literal. | value |
append | Pushes onto the array at the path. | value |
remove | Deletes the key or array element at the path. | |
rename | Renames an object key. Refuses if the destination exists. | to |
reason is optional and shows up in the console and in warnings, so future you knows why the patch exists.
Path patterns
Paths are JSON Pointers with three additions for bulk fixes:
*matches any child.**matches any depth, including zero.[name=account_id]matches children that are objects whosenameproperty isaccount_id.
{ "op": "set", "path": "/paths/**/parameters/[name=account_id]/schema/type", "value": "string" }That one patch retypes every account_id parameter in the document.
Renaming a schema under /components/schemas (or /definitions in Swagger 2.0) rewrites every $ref to it, so nothing dangles.
Where patches apply
Everywhere a project's spec is read:
- Every generation, whatever the trigger.
- Both sides of preview builds, so PR diffs reflect the spec change rather than the patches.
- The hosted MCP endpoint.
Project settings link to the patched spec: the live source with your patches applied, exactly as generation sees it, with a count of applications and any diagnostics.
When a patch misses
A patch whose target no longer exists is skipped and reported as a warning on the generation:
Spec patch matched nothing: not applied: set /components/schemas/Acct/properties/id/type (ids are strings)Nothing drifts silently. If the upstream spec fixes the problem, the warning tells you the patch can go.
Limits
- Patches address OpenAPI documents. GraphQL schemas generate unpatched, with a warning when patches are configured.
- Patches are not a transform language. There is no templating and no conditional logic, on purpose. When a fix needs more than set, append, remove, and rename, fix the spec.
Preview builds
Every pull request that touches your spec gets a generated package, a surface diff, and a commit status that fails on breaking changes.
Config
Everything typeship needs beyond the spec, in one object: global parameters, retry tuning, pagination rules, and how the generated CLI and MCP server behave.