Repository navigation
Releases
v1.1.0
seampact 1.1.0
seampact 1.1.0 adds two server targets, API Platform and Next.js, an axios client, and app input rules on the Node and PHP servers. Every package, in every ecosystem, moves to 1.1.0. Your contracts need no change.
New targets
- A new
seampact/api-platformtarget generates API Platform 5 resources, operations and processors for a contract, served through the Symfony boundary. Every reply goes through the boundary, and the exceptions of API Platform'ssecuritymap to the api's errors. See the API Platform guide. - The API Platform target writes JSON-LD items and Hydra collections with the
@idof each resource, and@seampact/pack-api-platformanswers a failed validation with API Platform's 422violationsbody. - A new
@seampact/codegen-nextjstarget generates a route handler per api for a Next.js 16 catch-all route file, with the contract's access, validation and reply checks around your handlers. See the Next.js guide. - A new
@seampact/codegen-axiostarget generates the typed client over your own axios instance, and the React, Vue and Angular targets run over it unchanged. See the axios client. - Both server targets pass the shared http suite in CI, Next.js on a real
next buildandnext startapp, and the compatibility matrix lists them as tested.
App input rules
Your own checks now run after the contract's checks, written in your validator library, and a failure answers the contract's schema-invalid error with app.<name> issues (ADR 0028):
- Express, Fastify and Hono take typed
inputRulesbesidehooks, with zod, yup or Ajv rules; Next.js takes them inrouteHandler, as Hono does. - NestJS and AdonisJS take them in
forRootandregisterRoutes, with class-validator, VineJS, zod or yup rules. - Laravel takes rules in Laravel's validation syntax, and Symfony takes Symfony constraints.
- The shared http suite has an
input-rulescase; the servers without input rules yet skip it by name.
Python, Spring Boot and API Platform get input rules in 1.2.0. Until then, API Platform refuses a tagged input rules service at cache:warmup with input.rules.unsupported.
Examples
Three runnable apps, each generated from one contract and kept green by CI: NestJS and React, Laravel and Vue, and a full-stack Next.js app.
Registry pages
- Every published package's npm, Packagist and PyPI page opens with a one-sentence description, keywords, an install line and a link to its guide; Maven Central and the Gradle Plugin Portal get the same descriptions and tags.
- The Gradle Plugin Portal approved
io.github.jubasse.seampact: Gradle builds load the plugin from the portal. - The npm packages are published through npm trusted publishing, with no token.
v1.0.1
seampact 1.0.1
seampact 1.0.1 is a patch release: server fixes, clearer generate errors, better docs and agent skills. Every package, in every ecosystem, moves to 1.0.1. Nothing in your contract changes.
Upgrade note
Python, Laravel and Symfony servers now compute the ETag and idempotency digests of a body holding an integer past 2^53 as the other targets do, so the same body gets the same digest on every server. After the upgrade, a retry of such a request stored by 1.0.0 no longer matches its record and is served as a new request.
Server fixes
- AdonisJS servers answer a request body in an unknown content coding with 415 and a corrupt or cut gzip body with 400, where they answered 500 before.
- AdonisJS 6 servers answer a compressed request body with 415, or 413 when it is over the route limit once inflated, where they answered 400 before.
- Fastify servers that register
@fastify/compressnow accept gzip request bodies and answer an unknown content coding with 415, a body over the limit once inflated with 413 and a corrupt gzip body with 400, where they refused, failed or hung before. - The PHP boundary now reads a gzip request body of several members whole, and refuses as invalid the bytes after a gzip or deflate stream that do not start another gzip member.
- The PHP boundary now refuses a gzip or deflate request body cut before its end as invalid, instead of reading it as a shorter body.
- The framework-route allow-list on FastAPI, DRF and Ninja now matches the Node rule: a single
*no longer admits a catch-all route,**matches zero segments, and empty segments are ignored.
Clearer errors
- A
generaterefusal now names its contract file and line, gives a fix of its own (starting with "Ask the user first:" when it changes the API) and ends with the URL of its troubleshooting row. - The Spring Boot, PHP and Python generators no longer tell you to update to a release that does not exist when they refuse a form: the fix says to ask the user before removing the operation, or to remove a waiting artefact switch from the entry, and links the troubleshooting page.
Docs and agents
- The docs are rewritten for every stack, with a new types and validation section.
- The FastAPI, DRF and Ninja guides now show how to write a paged list handler.
- Coding agents get small, focused docs files and seampact skills:
npx skills add jubasse/seampact(AI agents).
Registry pages
- The Maven Central POMs and the Gradle Plugin Portal page link to the docs site, the Spring Boot ones to the Spring Boot guide.
- The Packagist pages link to the docs site, the source and the issues, the Laravel and Symfony ones to their own guide.
- The PyPI pages link to the docs site, the source and the issues, the FastAPI, DRF and Ninja ones to their own guide.
v1.0.0
seampact 1.0.0
seampact 1.0.0 is the first release. You write one contract in YAML. seampact generates the server boundary, the client and the OpenAPI document from it, and checks in CI that nothing drifted.
Every package, in every ecosystem, has the same version. Install shows how to get each one.
Where to get it
| Registry | What you get | Status |
|---|---|---|
| npm | the CLI, the generators, the runtime and the packs | live, 2026-11-07 |
| Maven Central | the Spring Boot runtime, the hooks, the Maven plugin and the compiler | live, 2026-11-07 |
| Gradle Plugin Portal | the Gradle plugin io.github.jubasse.seampact |
pending review, 2026-11-07 |
| Packagist | seampact/laravel, seampact/symfony and their libraries |
live, 2026-11-07 |
| PyPI | seampact-fastapi, seampact-drf, seampact-ninja and their libraries |
live, 2026-11-07 |
| GitHub releases | the compiler binary for five systems, with SHA256SUMS |
live, 2026-11-07 |
The Gradle Plugin Portal reviews each new plugin by hand, which takes a few days. Until it approves, the Maven plugin works at once, and Gradle builds can load the plugin from Maven Central: see the Spring Boot guide.
On release day, pnpm can answer "no matching version" for @seampact/*. Its minimumReleaseAge setting holds back a package published in the last day. Wait, or add minimumReleaseAgeExclude: ["@seampact/*"] to pnpm-workspace.yaml: see Install.
What 1.0 ships
Servers:
- Node.js: NestJS, Express, Fastify, AdonisJS and Hono.
- Java: Spring Boot, with a Gradle plugin and a Maven plugin.
- PHP: Laravel and Symfony, with Composer.
- Python: FastAPI, Django REST Framework and Django Ninja.
Clients and validators:
- A typed
fetchclient, React hooks (TanStack Query, SWR or RTK Query) and TypeScript types. - Validators with zod, yup or class-validator.
Everything else:
- The OpenAPI 3.1 document of each api.
seampact diffclassifies a change as additive or breaking, andseampact checkfails CI on drift.- Access rights, signed webhooks, idempotency keys, paging and conditional requests.
- The standalone compiler binary:
compile,lint,diff,explainandbundlewithout Node.js.
Known limits
A form that 1.0 does not support is refused by name when you run seampact generate. The message says which form and where. Nothing is generated silently wrong. Generate errors explains most of them. Where a line says "remove the operation", list the other operations in the entry's operations filter and write that one by hand.
Every server (the Node servers, Spring Boot, Laravel, Symfony, FastAPI, DRF and Ninja):
- A principal with variants (
viewer_or_device), a principal requirement and a principal binding are refused. Declare one principal kind per route and check the principal in your handler. - A signature scheme that your application verifies is refused. Verify the signature in your own middleware and leave it out of the contract.
- An API key in the query string and HTTP Basic are refused. Send the key in a header, or use a bearer token.
- A scope that your application resolves (
resolver) is refused. Declare the scope from the path (path.projectId) or the subdomain. - An identity header written on a response (also by the
fetchclient), such as the rights-version header (identity.response.header.unsupported), is refused. Remove it from the identity'sheaders, or declare a deadline, trace or actor headerin: request. accessHooks: false(native access) is refused. Keep the hooks and read the framework's own auth in the authenticate hook.- A sort key restricted by a right (
sortable: [{ key, right }], or a key of a field withrestrictedTo) is refused. Leave the key out ofsortable. - A page or an item that writes the
$collection,$resourceor item id slots (JSON:API, HAL, JSON-LD) is refused. Use a page style without them.
Every server and the fetch client:
- Form, multipart and file download bodies are refused. Remove the operation.
- A redirect response is refused. Remove the operation.
- A response header that the route writes itself is refused. Remove the operation.
- A tagged union (
oneOfwith a discriminator) is refused, and by zod too. Remove the operation. - A degradable operation is refused. Remove the operation.
- A query list in brackets form (
tag[]=a) is refused. Setconventions.query.liststorepeatorcomma. - A deprecated operation is refused by the
fetchclient only. Remove the operation from the client entry.
Node servers:
- Optional authentication is refused. Spring Boot, Laravel, Symfony, FastAPI, DRF and Ninja generate it.
- Absolute URLs in responses are refused. Use relative URLs; Spring Boot, Laravel, Symfony, FastAPI, DRF and Ninja write absolute ones from a configured public origin.
- The
splitsort style is refused. Usesigned,pairorkeyed. - An input field right below the top level of the input (
/details/budget) is refused. Put the field at the top level of the input, or check the right in your handler. idempotency.enforce: noneis refused. Keepboundary. Spring Boot, Laravel, Symfony, FastAPI, DRF and Ninja generate it, but the 1.0 test suite does not cover it there yet.
PHP and Python servers (Laravel, Symfony, FastAPI, DRF, Ninja):
- The complete page fact of a changes-since paging style is refused. Use another paging style on these servers.
Spring Boot, Laravel and NestJS:
- The
suffixesoption and per-operation handlers (perOperation) are refused. Use the default names and the defaultperResourcehandlers. - A custom metadata on a right is refused on NestJS and Laravel. Leave it out of the right.
- The
codeNameoption is refused on NestJS. Spring Boot and Laravel support it.
Clients and validators:
- An open enum with a fallback is refused in the types the
fetchclient is generated beside. Declare a closed enum. - An error format that writes its code only inside a text template is refused for the
fetchclient. Put the code in a field of its own. - The
ignoreandkeeppolicies for unknown keys of an operation input are refused with the zod, yup and class-validator validators. Keep the default, which refuses unknown keys.
Known differences between servers. A portable client avoids them:
- A
commalist sent as an empty value (sort=) is an empty list on the Node servers and one empty item on Spring Boot and PHP. - A
commakey sent twice (sort=a&sort=-b) answers 400 on the Node servers. Spring Boot and PHP join both values. limit=1e3is read as 1000 on Node. Spring Boot, Laravel, Symfony and FastAPI answer 400.
Not in 1.0:
- API Platform comes after 1.0. Use Symfony with its own controllers, as the Symfony guide shows.
- The CASL (NestJS) and spatie (Laravel) adapters come in 1.1. Until then, write the rights in your own guard or policy.
- Test helpers, dev docs served by the server, and a ready-made idempotency store are not generated on the Node, Java, PHP and Python servers. The idempotency store is an interface you implement; the Node servers have an in-memory one.
- Express, Fastify, Hono and FastAPI generate no native access export, and Express, Fastify and Hono map no framework security refusals: your own middleware owns them.
- The Angular service does not set HttpClient's XSRF header. Call
withXsrfConfigurationwith the contract's CSRF header name, as the Angular guide says. - The
fetchclient does not retry by the nature of an error code. It retries a rejected request, never a declared error. - The binary and the plugins download over HTTPS only and read no proxy variable. Behind a proxy, put the binary on a mirror and set its URL and hash, or point to a local file.
- macOS binaries carry an ad hoc signature, not an Apple one. If macOS blocks the file, allow it in System Settings.