Skip to content

Commit c755cd1

Browse files
authored
Document SDK and API contract versioning (#585)
* Document SDK contract versioning and require SDK-breaking majors * Publish dependency guidance in both SDK names
1 parent 00b42ff commit c755cd1

13 files changed

Lines changed: 304 additions & 45 deletions

File tree

‎.ai/skills/release/SKILL.md‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,18 @@ description: Checklist for releasing packages from this monorepo (code PR -> Ver
1717
2. Run `corepack yarn verify:full` locally once before pushing.
1818
- This catches wrapper sync drift early and still runs the deeper repo checks.
1919
3. If `verify` / `verify:full` fails with transloadit wrapper drift, run `node scripts/prepare-transloadit.ts`, commit the tracked `packages/transloadit` updates, and re-run `corepack yarn verify:full`.
20-
4. If you add a changeset for `@transloadit/node`, also add a similar changeset for `@transloadit/mcp-server` if it could affect its workings. The chances are, they are, since the latter is mostly a thin wrapper around the former.
20+
4. Classify changes using [the versioning policy](../../../CONTRIBUTING.md#versioning-policy).
21+
- API2 contract type/schema changes ship in minors, with a `Contract:` summary and a
22+
`Type-breaking:` line per type break. Description-only syncs are patches; pre-1.0 package
23+
exceptions follow the policy.
24+
- Every major changeset must contain a nonempty `SDK-breaking:` line naming the SDK surface
25+
it breaks. `yarn lint:changesets` enforces this for each file.
26+
5. If you add a changeset for `@transloadit/node`, also add a similar changeset for `@transloadit/mcp-server` if it could affect its workings. The chances are, they are, since the latter is mostly a thin wrapper around the former.
2127
- This repo enforces a one-way coupling: node releases should also publish a new mcp-server version (but mcp-server releases do not require node releases).
2228
- `yarn check`/`yarn verify` will fail fast if you forget.
23-
5. Commit + push branch
24-
6. Open PR, wait for CI green
25-
7. Squash-merge the PR
29+
6. Commit + push branch
30+
7. Open PR, wait for CI green
31+
8. Squash-merge the PR
2632

2733
Notes:
2834
1. When creating PRs with `gh pr create` from a shell, avoid unescaped backticks in the `--body` string.

‎.changeset/contract-versioning.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@transloadit/node': patch
3+
'transloadit': patch
4+
'@transloadit/types': patch
5+
'@transloadit/zod': patch
6+
'@transloadit/mcp-server': patch
7+
---
8+
9+
Document how SDK exports follow strict SemVer while API contract types and schemas follow API2
10+
in minor releases, with guidance on dependency ranges and narrowing parsed output.

‎CONTRIBUTING.md‎

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,106 @@ The `packages/transloadit` folder is a generated legacy wrapper. Tracked files u
110110
yarn pack
111111
```
112112

113+
## Versioning policy
114+
115+
These packages mirror a live API. When API2 changes a Robot parameter, every customer gets that
116+
change at once, whatever SDK version they installed. A major for each such change would give us
117+
dozens of majors a year and teach users to ignore them. So we version two surfaces differently:
118+
the **SDK surface** follows strict SemVer, and the **API contract** follows the API.
119+
120+
### SDK surface: strict SemVer
121+
122+
A breaking change to any of these requires a major:
123+
124+
- Package names, `exports` entry points and subpaths, module format and the supported Node.js
125+
range. We support TypeScript releases from the last two years, like DefinitelyTyped; dropping an
126+
older one is not breaking.
127+
- Names of exported symbols, generated ones included. When API2 renames something, add the new
128+
name, keep the old one as a `@deprecated` alias, and remove it in the next major.
129+
- The `Transloadit` client: constructor options, method names and parameters, return wrappers,
130+
error classes and their documented fields, and retry, polling and upload behavior.
131+
- Hand-written helpers and types, such as `prepareInputFiles`, the signing helpers and
132+
`StoredImageReceipt`, and everything in `@transloadit/utils`.
133+
- CLI command names, hand-written flags, exit codes, credential resolution order and `--json`
134+
output.
135+
- The helper functions and supported Zod versions of `@transloadit/zod`.
136+
- What the SDK, CLI and MCP server send, accept or reject at runtime for unchanged caller code,
137+
apart from explicit API-contract schema parsing and lint validation described below.
138+
139+
### API contract: follows API2
140+
141+
Types and schemas synced from alphalib describe what the Transloadit API accepts and returns today:
142+
Robot parameters (names, optionality, nullability, defaults, enums, limits and refinements),
143+
Assembly Instructions, Assembly status and other response types, error-code lists, Robot
144+
metadata, model lists, descriptions and examples, lint rules that mirror API validation, and CLI
145+
intent flags generated from Robot parameters. Their shapes, and Zod internals such as wrapper
146+
classes, `.shape` and parse-time defaults, follow the API rather than SemVer.
147+
148+
### Bump rules
149+
150+
| Change | Bump |
151+
| --- | --- |
152+
| SDK bug fix, documentation, or a contract sync that only changes descriptions or examples | patch |
153+
| New SDK feature or export, or an additive contract sync: new Robot, optional parameter, enum value or response field | minor |
154+
| Type-breaking contract sync: an input type accepts less, or an output type returns more (`null`, `undefined`, a wider union, a removed parse-time default) | minor, with a `Type-breaking:` line |
155+
| Breaking change to the SDK surface | major |
156+
| Contract sync that changes SDK runtime behavior for unchanged code, for example the CLI rejecting instructions the API still accepts | major, or a warning in a minor first |
157+
158+
- Stable-package patch releases never contain contract type or validation changes, or type-breaking
159+
changes, so a `~` range only brings fixes. We do not backport fixes to older minors.
160+
- A contract sync may still justify a major if it also breaks the SDK surface and the README quick
161+
start or `examples/` need edits to compile. Name that SDK break in the changeset; a contract-only
162+
type break still ships in a minor.
163+
- SDK majors are batched: deprecate in a minor, collect removals in one tracking issue, and remove
164+
them together in the next major.
165+
- Pre-1.0 packages (`@transloadit/mcp-server`, `@transloadit/notify-url-relay` and the alpha
166+
`@transloadit/viewer`) use a minor for breaking changes and a patch for everything else. Contract
167+
syncs are patches there unless their tools, CLI or exports change.
168+
169+
### Writing contract changesets
170+
171+
- Bump `@transloadit/node`, `transloadit`, `@transloadit/types` and `@transloadit/zod` by `minor`,
172+
or by `patch` for description-only syncs. Add `@transloadit/mcp-server` as described under
173+
Releasing.
174+
- Start the summary with `Contract:` and link the API2 pull requests being synced.
175+
- Add one `Type-breaking:` bullet per type-breaking change: what changed, who is affected (authors
176+
of Assembly Instructions, or readers of parsed output and API responses) and how to adapt.
177+
- A contract-only sync is never `major`. Every `major` changeset contains an `SDK-breaking:` line
178+
that names the SDK surface it breaks.
179+
180+
```md
181+
---
182+
'@transloadit/node': minor
183+
'transloadit': minor
184+
'@transloadit/types': minor
185+
'@transloadit/zod': minor
186+
'@transloadit/mcp-server': patch
187+
---
188+
189+
Contract: sync Robot schemas from API2 (transloadit/api2#9897, transloadit/api2#9938).
190+
191+
- Type-breaking: `/video/subtitle` no longer fills in `font`, `font_size` and `bold` while
192+
parsing. Parsed values (`RobotVideoSubtitleInstructions`, `AssemblyInstructions`,
193+
`schema.parse()`) can be `undefined`; the API still applies Arial, 16 and `false`, or the
194+
`social` style’s defaults. Instruction input types are unchanged.
195+
- Type-breaking: `/video/thumbs` accepts `null` for `offsets`, `width` and `height`, so parsed
196+
values can be `null`. Treat `null` like an omitted value.
197+
```
198+
199+
### What we tell consumers
200+
201+
Repeat this in the package READMEs:
202+
203+
- Commit your lockfile and type-check dependency update PRs. Type-breaking contract changes show
204+
up there, not in production.
205+
- For the stable SDK packages, use `~` ranges for stable types: patch releases never change contract
206+
types. Use `^` to follow the API as it evolves. Pre-1.0 packages follow the exceptions above.
207+
- Type the instructions you write with the input types, for example
208+
`const params = { steps } satisfies CreateAssemblyParams`. API changes usually widen these.
209+
- Narrow fields from responses and parsed output before using them (`null`, `undefined`, unknown
210+
strings).
211+
- Do not depend on Zod internals such as wrapper classes, `.shape` or parse-time defaults.
212+
113213
## Releasing
114214

115215
Only maintainers can make releases. Releases to [npm](https://www.npmjs.com) are automated using GitHub Actions and Changesets (including the legacy `transloadit` package).

‎docs/fingerprint/transloadit-baseline.json‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"packageDir": "packages/transloadit",
33
"tarball": {
4-
"filename": "transloadit-6.1.1.tgz",
5-
"sizeBytes": 1406530,
6-
"sha256": "ef663482bdedeec0d556dcd485ac547d0725e929139288a4f32d89964d5bab4b"
4+
"filename": "transloadit-6.1.2.tgz",
5+
"sizeBytes": 1406977,
6+
"sha256": "f45d608797e6963f62066cc1a4e16babb1e04305674b3136f7b6dde2caf25313"
77
},
88
"packageJson": {
99
"name": "transloadit",
10-
"version": "6.1.1",
10+
"version": "6.1.2",
1111
"main": "./dist/Transloadit.js",
1212
"exports": {
1313
".": "./dist/Transloadit.js",
@@ -1034,7 +1034,7 @@
10341034
{
10351035
"path": "package.json",
10361036
"sizeBytes": 2770,
1037-
"sha256": "465e0c3528f85fe43ef3eb993679477d7196e25f441e5f5ceb2415bcab891d6e"
1037+
"sha256": "1b7049ee8de43da6af0fcfe735a162dec54f907f995b703efb2680fe8145259c"
10381038
},
10391039
{
10401040
"path": "dist/alphalib/types/robots/_ai-models.d.ts.map",
@@ -3053,8 +3053,8 @@
30533053
},
30543054
{
30553055
"path": "README.md",
3056-
"sizeBytes": 112372,
3057-
"sha256": "aff8599d46547bdbfbcbe3f84233776f2f74522a14128a75b6bce742b2a7cf79"
3056+
"sizeBytes": 113193,
3057+
"sha256": "beef7dc51ec496758f54f740a88deb4a1b0260ac16a227c4283018ff79cf620e"
30583058
},
30593059
{
30603060
"path": "dist/alphalib/types/robots/_ai-models.d.ts",

‎docs/fingerprint/transloadit-baseline.package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "transloadit",
3-
"version": "6.1.1",
3+
"version": "6.1.2",
44
"description": "Node.js SDK for Transloadit",
55
"homepage": "https://github.com/transloadit/node-sdk/tree/main/packages/node",
66
"bugs": {

‎package.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"test:img:fixture": "node scripts/test-img-next-fixture.ts",
3131
"test:init:fixture": "node scripts/test-init-example.ts",
3232
"test:sdk:edge": "node scripts/test-sdk-edge.ts",
33-
"test:unit": "vitest run ./scripts/fingerprint-pack.test.ts ./scripts/withProcess.test.ts ./scripts/img-next-fixture.test.ts ./scripts/knip.test.ts ./scripts/publish-release.test.ts ./scripts/release-run.test.ts ./scripts/sdk-edge.test.ts ./scripts/sync-robot-index.test.ts && yarn workspace @transloadit/utils test:unit && yarn workspace @transloadit/viewer test:unit && yarn workspace @transloadit/node test:unit && yarn workspace @transloadit/mcp-server test:unit && yarn workspace @transloadit/types test:unit && yarn workspace @transloadit/zod test:unit && yarn workspace @transloadit/notify-url-relay test:unit",
33+
"test:unit": "vitest run ./scripts/fingerprint-pack.test.ts ./scripts/guard-changesets.test.ts ./scripts/withProcess.test.ts ./scripts/img-next-fixture.test.ts ./scripts/knip.test.ts ./scripts/publish-release.test.ts ./scripts/release-run.test.ts ./scripts/sdk-edge.test.ts ./scripts/sync-robot-index.test.ts && yarn workspace @transloadit/utils test:unit && yarn workspace @transloadit/viewer test:unit && yarn workspace @transloadit/node test:unit && yarn workspace @transloadit/mcp-server test:unit && yarn workspace @transloadit/types test:unit && yarn workspace @transloadit/zod test:unit && yarn workspace @transloadit/notify-url-relay test:unit",
3434
"test:types": "yarn workspace @transloadit/zod test:types",
3535
"test:widget-visual": "node scripts/test-mcp-widget-visual.ts",
3636
"test:widget-visual:update": "node scripts/test-mcp-widget-visual.ts --update --states-dir /tmp/widget-states",
@@ -45,6 +45,7 @@
4545
"devDependencies": {
4646
"@biomejs/biome": "2.4.16",
4747
"@changesets/cli": "^2.31.0",
48+
"@changesets/parse": "^0.4.3",
4849
"@types/node": "^25.8.0",
4950
"@vitest/coverage-v8": "^4.1.6",
5051
"execa": "^9.6.1",

‎packages/node/README.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,20 @@ npm install --save @transloadit/node
4545
The legacy npm package name `transloadit` is kept as an alias for backward compatibility, but
4646
`@transloadit/node` is the canonical package name.
4747

48+
## Versioning and dependency updates
49+
50+
The SDK surface follows strict SemVer. API contract types and schemas follow API2 and can change
51+
in minor releases, including type-breaking updates. See the
52+
[versioning policy](https://github.com/transloadit/node-sdk/blob/main/CONTRIBUTING.md#versioning-policy).
53+
54+
- Commit your lockfile and type-check dependency update PRs.
55+
- Use `~` ranges for stable contract types within a minor; patch releases do not change contract
56+
types. Use `^` to follow the API as it evolves.
57+
- Use input types for instructions you write, for example
58+
`const params = { steps } satisfies CreateAssemblyParams`.
59+
- Narrow fields from responses and parsed output before use (`null`, `undefined`, unknown strings).
60+
- Do not depend on Zod internals such as wrapper classes, `.shape` or parse-time defaults.
61+
4862
## Command Line Interface (CLI)
4963

5064
This package includes a full-featured CLI for interacting with Transloadit from your terminal.

‎packages/transloadit/README.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,20 @@ npm install --save @transloadit/node
4545
The legacy npm package name `transloadit` is kept as an alias for backward compatibility, but
4646
`@transloadit/node` is the canonical package name.
4747

48+
## Versioning and dependency updates
49+
50+
The SDK surface follows strict SemVer. API contract types and schemas follow API2 and can change
51+
in minor releases, including type-breaking updates. See the
52+
[versioning policy](https://github.com/transloadit/node-sdk/blob/main/CONTRIBUTING.md#versioning-policy).
53+
54+
- Commit your lockfile and type-check dependency update PRs.
55+
- Use `~` ranges for stable contract types within a minor; patch releases do not change contract
56+
types. Use `^` to follow the API as it evolves.
57+
- Use input types for instructions you write, for example
58+
`const params = { steps } satisfies CreateAssemblyParams`.
59+
- Narrow fields from responses and parsed output before use (`null`, `undefined`, unknown strings).
60+
- Do not depend on Zod internals such as wrapper classes, `.shape` or parse-time defaults.
61+
4862
## Command Line Interface (CLI)
4963

5064
This package includes a full-featured CLI for interacting with Transloadit from your terminal.

‎packages/types/README.md‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
# @transloadit/types
22

3-
**Stability:** This package is still relatively new and may evolve quickly.
3+
**Stability:** Exported names follow strict SemVer. API contract types and schemas
4+
follow API2 and can change in minor releases, including type-breaking updates. See the
5+
[versioning policy](https://github.com/transloadit/node-sdk/blob/main/CONTRIBUTING.md#versioning-policy).
6+
7+
- Commit your lockfile and type-check dependency update PRs.
8+
- Use `~` ranges for stable contract types within a minor; patch releases do not change contract
9+
types. Use `^` to follow the API as it evolves.
10+
- Use input types for instructions you write, such as `AssemblyInstructionsInput`; narrow fields
11+
from responses and parsed output before use (`null`, `undefined`, unknown strings).
412

513
This package provides type-only exports generated from Transloadit schemas.
614

7-
For the stable SDK, use the unscoped `transloadit` package.
15+
For the SDK client, use `@transloadit/node` or the unscoped `transloadit` package.

‎packages/zod/README.md‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
# @transloadit/zod
22

3-
**Stability:** This package is still relatively new and may evolve quickly.
3+
**Stability:** Exported names, helper functions and supported Zod versions follow strict SemVer.
4+
API contract types and schemas follow API2 and can change in minor releases, including
5+
type-breaking updates. See the
6+
[versioning policy](https://github.com/transloadit/node-sdk/blob/main/CONTRIBUTING.md#versioning-policy).
7+
8+
- Commit your lockfile and type-check dependency update PRs.
9+
- Use `~` ranges for stable contract types within a minor; patch releases do not change contract
10+
types. Use `^` to follow the API as it evolves.
11+
- Use input types for instructions you write, such as `AssemblyInstructionsInput`; narrow fields
12+
from responses and parsed output before use (`null`, `undefined`, unknown strings).
13+
- Do not depend on Zod internals such as wrapper classes, `.shape` or parse-time defaults.
414

515
Exports Zod schemas derived from Transloadit types.
616

7-
For the stable SDK, use the unscoped `transloadit` package.
17+
For the SDK client, use `@transloadit/node` or the unscoped `transloadit` package.
818

919
## Migrating schema consumers
1020

0 commit comments

Comments
 (0)