Changelog
This page is generated from the commit history by tools/changelog.php — do not edit it
by hand, fix the commit message instead.
Every user-facing change across the four packages. The bold prefix is the commit’s scope: core, attributes, laravel and inference-phpstan are the packages, and the rest (website, repo, ci) ship no package. Entries begin after v0.1.2; older history is in the repository git log.
Each package repository also carries its own CHANGELOG.md with just its entries.
v0.23.2
Section titled “v0.23.2”Features
Section titled “Features”- core: inspecting a document reads the title and version its info states (#679)
- core: name what every diff change is about, so changes group without taking their path apart (#678)
Bug fixes
Section titled “Bug fixes”- bump vite from 8.2.1 to 8.3.5 in /website (#662)
v0.23.1
Section titled “v0.23.1”Features
Section titled “Features”- core: report the line and column each finding and refusal sits at, in JSON and YAML (#676)
v0.23.0
Section titled “v0.23.0”Breaking changes
Section titled “Breaking changes”- core: key a custom operation’s nodes by where it is written, so none takes another’s key (#673)
DocumentNode::$keychanges for the nodes of an operation under OpenAPI 3.2’sadditionalOperations. The operation is keyedadditionalOperations/COPY /forms/{p0}where it wasCOPY /forms/{p0}, its method as written with its case kept, and the keys of its parameters and responses open the same way. A fixed method’s nodes, and every node of a document withoutadditionalOperations, keep their keys, and a custom operation’s nodes keep their pointers, ids, labels and digests. A caller storing keys should expect each stored custom-operation node to match no new key until it is re-keyed by its pointer, which did not move, and a fixed operation a customgethid to be listed again, as a node it has not stored.
- core: read YAML as it was written, and refuse what symfony/yaml would misread (#672)
DocumentReader::read()refuses YAML it used to read as something else: a!!floaton what is not a number, a merge of anything but a map or a list of maps, a merge symfony/yaml reads otherwise than YAML, and a key written twice after a merge or, on symfony/yaml 7.4, over a null, each withReadFailure::Syntax. It also reads an impossible timestamp, a timestamp key, a keyword in a case YAML does not spell it and a non-specific tag’s value as the text written. UPGRADING.md has the details under v0.23.0.
- core: bound what YAML merge keys cost the parser by the document’s size (#671)
DocumentReader::read()refuses YAML whose text could spell a merge key and that names more aliases to a map or a list than 524,288 / (its separators + 1), where it read up to 128 of them. That refusal, and the one past 128, areReadFailure::AliasExpansionwhere they wereReadFailure::Syntax. UPGRADING.md has the details under v0.23.0.
Bug fixes
Section titled “Bug fixes”- core: diff OpenAPI 3.2 additionalOperations as it diffs every other operation (#670)
- core: read YAML past 128 aliases wherever no merge key can copy a map, and require a symfony/yaml that bounds its own nesting and lexing (#669)
v0.22.1
Section titled “v0.22.1”Bug fixes
Section titled “Bug fixes”- core: bound what the node index spells and how often a $ref is followed, and refuse YAML the parser throws on (#668)
- core: refuse YAML whose aliases repeat text far past its own size (#667)
- core: measure the node budget against the document’s compact JSON, not its whitespace (#665)
v0.22.0
Section titled “v0.22.0”Breaking changes
Section titled “Breaking changes”- core: refuse a document whose node index or diff would expand far past its own size (#663)
DocumentReader::read()refuses a document whose node index would outgrow its bytes, with the newReadFailure::NodeExpansion, so an exhaustivematchoverReadFailureneeds that case.IncomparableDocumentsExceptionnow carries aDiffFailureas$failureand is also thrown when a diff would read shared nodes past its budget; constructing one withnewtakes the failure first.ReadDocument::validate()no longer appends a “more than N findings” line: show your own whenisTruncated()is true. UPGRADING.md has the details under v0.22.0.
v0.21.2
Section titled “v0.21.2”Features
Section titled “Features”- core: write a read document back as JSON, exactly as it was written (#660)
v0.21.1
Section titled “v0.21.1”Features
Section titled “Features”- core: read an uploaded OpenAPI document through a public DocumentReader (#654)
Bug fixes
Section titled “Bug fixes”- core: diff a 3.0 document against a 3.1 or 3.2 one in the dialect they share (#656)
- core: keep an empty security requirement read back from JSON (#653)
- laravel: fold Laravel’s new exclude, excludeWith and excludeWithout rules (#657)
v0.21.0
Section titled “v0.21.0”Breaking changes
Section titled “Breaking changes”- inference-phpstan: read a ternary’s branches and a caught call’s throw point the way PHPStan 2.3 hands them over (#623)
- phpstan declaration changed.
Features
Section titled “Features”- core: a claim’s description and an example’s placeholders go where they are published (#618)
- core: a shared response’s use records a claim only where it did not land (#617)
- core: publish a use’s x-docuccino on its operation, not beside its $ref (UIR 2.1) (#616)
Bug fixes
Section titled “Bug fixes”- bump devalue from 5.9.2 to 5.9.4 in /website (#644)
- bump sharp from 0.35.4 to 0.35.5 in /website (#645)
- bump smol-toml from 1.7.1 to 1.9.0 in /website (#643)
- bump source-map-js from 1.2.1 to 1.2.2 in /website (#642)
- inference-phpstan: apply the catch a callee writes around the closure it runs (#640)
- core: spell null with a type in the OpenAPI 3.0 export, keeping the nullable $ref and union idioms generators read (#639)
- core: drop a component only a dropped member referred to (#638)
- core: publish a class reaching a request shape as its own request component (#637)
- core: adopt a declared tagged union over the object the rules split, refined by the rules (#630)
- laravel: read a collection’s with() for the envelope it is sent in (#629)
- laravel: illustrate a status-text member with the reason phrase its status is sent with (#628)
- laravel: settle a respond() guard on the rendered response’s class (#626)
- inference-phpstan: drop what a catch takes from an undeclared call’s descended throws (#625)
- inference-phpstan: answer a parent-declared property through the parent::__construct() a constructor always runs (#624)
- core: report every place a document fails its schema, not only the first (#615)
v0.20.4
Section titled “v0.20.4”Features
Section titled “Features”- core: tell tooling a blank string on a nullable field is read as null (#591)
- laravel: publish every URL form of a route with optional segments, and host constraints on server variables (#586)
- core: publish a case-insensitive regex, and \s and Unicode properties under /u, as a pattern (#582)
- laravel: publish a request object one field partitions as a union of its shapes (#579)
- laravel: publish route parameter constraints on the path parameter (#574)
Bug fixes
Section titled “Bug fixes”- core: read a $ref an example states as the value it is, not as a reference (#608)
- laravel: give a warm build the memory ceiling a cold one gets (#598)
- laravel: publish a returned collection as the array or object it is sent as (#595)
- laravel: publish every top-level member of a JSON:API document, pages included, and first-party links as sent (#594)
- core: report an attribute PHP cannot construct where nothing would have read it, once per class (#593)
- laravel: publish a resource response as Laravel sends it under #[Collects], preserved keys and $forceWrapping (#590)
- laravel: let a custom filter class omit its #[QueryParameter] name, and report every Docuccino attribute PHP cannot construct (#589)
- laravel: validate a copied header on the header in laravel-actions and inline validation (#585)
- inference-phpstan: locate closures and methods where reflection places them, not at the parser start line (#584)
- laravel: publish the data a with() data key is merged into as what Laravel sends (#583)
- bump devalue from 5.9.0 to 5.9.2 in /website (#506)
- laravel: document a copied header’s rules on the header, not the body (#578)
- core: publish a key the constructor assigns on only some paths as optional (#577)
- laravel: publish the collection a resource’s newCollection() builds, and no forward error for a @method over a real method (#576)
- inference-phpstan: publish an (object) cast as the object it sends (#575)
- core: publish a reference as its pointer alone, and check every Reference Object on every build (#573)
- laravel: describe and name an error at any registered status by its reason phrase (#572)
- laravel: publish the alpha rules and a flagged regex no narrower than the server (#571)
Performance
Section titled “Performance”- core: run a document’s lints beside the rest of its build (#607)
- laravel: check an export’s document against its schema while its targets are written, side by side (#606)
- core: write canonical JSON through json_encode wherever the bytes are the same (#605)
- core: canonicalise a document without rebuilding its member handlers at every node (#604)
- core: parse each component schema once for all of a document’s checks (#602)
- laravel: share a cold build’s operations between forked workers (#601)
- laravel: pause the cycle collector while operations build (#600)
- inference-phpstan: size the walk-recording budget to the memory ceiling (#599)
- core: check a payload against the component schemas it can reach, not every one (#597)
v0.20.3
Section titled “v0.20.3”Features
Section titled “Features”- laravel: publish the request headers an action reads by name (#558)
- laravel: publish the signature and expires query parameters a signed route requires (#557)
- inference-phpstan: read an exception mapper’s returns as the exceptions they build (#554)
- core: let an extension translate a thrown exception before it is rendered (#553)
- laravel: publish a morphTo’s type column as the enum its morph map closes (#552)
- core: publish a union of classes told apart by a fixed tag as a discriminated oneOf (#544)
Bug fixes
Section titled “Bug fixes”- laravel: publish every status a choice of constant codes can send (#561)
- core: describe a response by its own status, and let a declared status retire an unread one (#560)
- laravel: publish a resource sent through ->response() as the resource (#559)
- laravel: publish the 422 a read route’s validated query can answer (#556)
- laravel: document an exception as the class the exception map translates it to (#555)
- core: let a nullable enum or const admit the null it is published beside (#551)
- laravel: publish a field required after an exclude rule as required only while it is kept (#550)
- core: require a plain object’s nullable keys that json_encode always writes (#549)
- laravel: publish the top-level members a root resource’s with() adds (#543)
- laravel: document every error as the respond() callback sends it (#542)
- ci: let the pinned-phpstan fixture leg move larastan back with it (#546)
v0.20.2
Section titled “v0.20.2”Bug fixes
Section titled “Bug fixes”- core: read a JSON object’s members as unordered when diffing values (#529)
v0.20.1
Section titled “v0.20.1”Features
Section titled “Features”- website: lead a docs reader back to the product (#526)
Bug fixes
Section titled “Bug fixes”- laravel: read a workflow id as a name the application chose (#528)
v0.20.0
Section titled “v0.20.0”Breaking changes
Section titled “Breaking changes”- core: rename the “uir” emit format to “full” (#519)
- the emit format id
uiris nowfull, in the CLI and inexport.targets. There is no alias:uiris refused, with an error naming the replacement.
- the emit format id
- core: carry the spec version and schema URL in the extension (#518)
$schemaanduirno longer sit at the document root. Read the spec version fromx-docuccino.generator.specVersionand the schema URL fromx-docuccino.generator.schema. The UIR spec moves to 2.0 — removing two required root members is a shape change against a published 1.0 — and 2.0 publishes two files, a self-contained document schema and a standalone extension schema. No committed artifact’scontentHashchanges, and diff history is preserved.
- core: carry declared workflows in the document, as UIR 1.1 (#511)
- emitted UIR documents declare
uir: 1.1.0and the 1.1$schemaURL, and everycontentHashchanges once as the spec version leaves the hash.
- emitted UIR documents declare
Features
Section titled “Features”- laravel: declare a workflow on the operations that take part in it (#514)
- core: publish declared workflows as an Arazzo description (#512)
- laravel: say which operations a version had not added yet (#508)
- laravel: say what a version did to a published value set (#507)
Bug fixes
Section titled “Bug fixes”- core: follow a schema $ref chain to the shape at the end of it (#523)
- core: read a schema $ref as the schema it names, not as the string it is (#522)
v0.19.1
Section titled “v0.19.1”Features
Section titled “Features”- website: theme the docs site with the brand palette (#504)
v0.19.0
Section titled “v0.19.0”Breaking changes
Section titled “Breaking changes”- laravel: state the move out of the framework config, rather than performing it (#496)
docuccino:migrate-configis removed. Build settings still inconfig/docuccino.phphave to be written intodocuccino.yamlby hand;config.not-migratedandconfig.stale-php-keysname them, and “Settings left inconfig/docuccino.php” in the configuration reference covers the four whose name or form changed.docuccino:installon an unmigrated application now exits1without writingdocuccino.yaml, where it used to write one and exit0.
Features
Section titled “Features”- laravel: say what a minted pagination component is (#498)
Bug fixes
Section titled “Bug fixes”- laravel: publish what an exception class says about the error it names (#502)
- laravel: publish a URL without the credentials it arrived with (#501)
- core: publish the description an enum states about itself (#499)
v0.18.1
Section titled “v0.18.1”Bug fixes
Section titled “Bug fixes”- core: reach every required member of a deepObject container (#492)
- core: claim a date-time format only where the bytes were read (#491)
- laravel: drop the filter an author ignored wherever the document publishes it (#490)
- laravel: answer a cast’s schema in the direction the document asks it (#489)
- laravel: say what each producer recovered, not what the document says (#487)
- laravel: describe one query surface once, in the form the document declares (#486)
- core: publish a date-time as the string it sends, not the object it is (#485)
- laravel: read the declarations that publish a field before calling it undocumented (#482)
- laravel: decide the eloquent notices on what the document published (#481)
- laravel: decide the query-builder notices on what the document publishes (#480)
v0.18.0
Section titled “v0.18.0”Breaking changes
Section titled “Breaking changes”- laravel: publish a validated enum as the component it already has (#479)
- a request field validated against a backed enum class now publishes
$ref: '#/components/schemas/<Enum>'(or ananyOfwithnullwhere it is nullable) in place of an inlineenumof the same values. A client generated from the document gains one shared type for that enum and loses the per-field anonymous one, so a name referring to the old inline type changes.representation.enums.components: falserestores the inline form. A rule listing only some of an enum’s cases, and a rule with no enum class behind it, are unchanged.
- a request field validated against a backed enum class now publishes
Bug fixes
Section titled “Bug fixes”- laravel: name the members of every value set the document publishes (#477)
- laravel: report the filter that reached the document with no type (#476)
- laravel: hold the shipped viewer bundles to the decoration they render (#475)
v0.17.0
Section titled “v0.17.0”Breaking changes
Section titled “Breaking changes”- laravel: check the artifact a consumer receives wherever a run produces one (#469)
docuccino:validatenow exits non-zero for an artifact that is not a valid document of its own format, and for anexport.targetslist it cannot read — both at the default--fail-on=none, asdocuccino:exportalready does. It also reports what a downlevel target loses, so--fail-on=info/hintcan go red where it was green; those sit atinfo, so a pipeline gating atwarningis untouched and anopenapi-3.2target loses nothing on the way out.docuccino:cacheexits non-zero for an invalid payload. Name a code underdiagnostics.acceptto keep it printing and stop it counting.
- laravel: gate –fail-on on everything a run reports, not on the build alone (#468)
docuccino:export --fail-on=warning(orinfo/hint) now exits non-zero for reports an emitter raised while writing an artifact, and forconfig.export-path-ignored. A pipeline that exports anopenapi-3.1,openapi-3.0orpostmantarget can go red on a loss it had been reading past. Ship the 3.2 artifact to consumers that can read it, or list the codes whose price you have accepted underdiagnostics.accept— they keep printing and stop counting. Anopenapi-3.2target loses nothing on the way out and is unaffected.
- core: name an operation whose route has no name (#453)
- every operation whose route has no name now publishes an
operationIdminted from its method and path, in the formget.api.forms.@form. A client generated from the document names its methods after those ids instead of deriving them from the path. Named routes and#[OperationId]are unaffected; name the route or set the attribute to pin a name of your own.
- every operation whose route has no name now publishes an
Features
Section titled “Features”- laravel: fill in the servers and route-binding facts the application already states (#472)
- core: validate every emitted OpenAPI artifact against its own published schema (#450)
- core: share one stored fragment between the documents that build it alike (#449)
Bug fixes
Section titled “Bug fixes”- inference-phpstan: tell spatie’s own Data body from one an application wrote in a trait (#473)
- laravel: read a Data class’s inherited declarations the way the vendor does (#471)
- core: state the contested-slot policy once, and read the ignored-header rule once (#466)
- core: publish no machine directory in a diagnostic that names one (#465)
- laravel: say which exception a response is for, and whether its status was read
- core: hold one entry per name in the collections a document must not repeat (#452)
- laravel: make every reading of spatie’s unwrapping say which value it is about (#451)
- core: make every reason the message scrubber weighs say what it proves (#448)
- core: publish one tags entry per name, whatever the config defines twice (#447)
v0.16.0
Section titled “v0.16.0”Breaking changes
Section titled “Breaking changes”- laravel: descend into the source roots the application declares (#442)
Docuccino\Core\Inference\TypeEngineBuilder::build()takes a newarray $declaredPaths = []parameter — the descend scope before the host narrowed it, which the engine uses as the yardstick for whether a declined hop is the host’s own narrowing (reportable) or the engine’s containment (not). It is optional and empty means “no yardstick, report nothing”, so a host that ignores it keeps working; but any third-party IMPLEMENTOR of the interface must add the parameter to match the signature. The one in-repo caller passes it by name, and a positional caller that previously passed$configFilesixth now hits a TypeError rather than misbehaving silently.
Bug fixes
Section titled “Bug fixes”- core: escape a name once, where the diagnostic that carries it is made (#438)
- inference-phpstan: read the status a declared throw was built with (#440)
- inference-phpstan: read a status the application declared wherever it lives (#437)
- bump astro from 7.2.0 to 7.3.2 in /website (#435)
- bump js-yaml from 4.3.1 to 4.3.2 in /website (#434)
- bump sharp from 0.35.3 to 0.35.4 in /website (#433)
v0.15.0
Section titled “v0.15.0”Breaking changes
Section titled “Breaking changes”- laravel: read a document keyword as the set it declares, not as any string (#425)
config.unknown-error-responsesandconfig.unknown-tag-strategyare retired into a singleconfig.unknown-value(warning), so adiagnostics.acceptlist naming either of the old codes will report them as accepted-but-never-emitted; replace both withconfig.unknown-value. An unrecognised value fordocuments.*.error_responses,tags.default_strategy,versioning,on_route_error,representation.filters,representation.nullable,representation.operation_id,representation.enums.namingor a document’sviewer.sourceis now refused and reported rather than passed through, so a document that was silently carrying a bogus keyword now builds from the documented default and says so.
- laravel: name the setting that says which requests are authenticated, not the mechanism (#420)
documents.*.security.auto_detect_middlewareis renamed todocuments.*.security.auth_middleware. The old spelling is not read and is not reported — it lives inconfig/docuccino.php, whose build keys this release already stops reading and reports wholesale asconfig.not-migratedorconfig.stale-php-keys. Use the new name indocuccino.yaml; behaviour, default (auth*) and position undersecurityare unchanged, anddocument.configHashmoves once for any document that sets it.
- laravel: read a build’s configuration from the tool’s own file (#417)
- build configuration moves out of
config/docuccino.phpinto adocuccino.yamlat the project root, and the keys left behind are no longer read. Everything a build reads moves — each document’sinfo,servers,routes,security,error_responses,tags,api_version,webhooks,content,examples,coverage,overlays,representation,versioning,integrationsandexport, plus top-levelextensions,lint,diagnostics,engine,on_route_error,cache.enabledandcache.path.enabled, each document’sviewerbag andcache.storestay inconfig/docuccino.php, because the service provider reads them when the application boots and when the viewer serves a request. Runphp artisan docuccino:installto write the new file. An application that has not migrated is reported rather than silently ignored:config.not-migratedwhere nodocuccino.yamlexists, andconfig.stale-php-keyswhere one does and leftovers remain. There is no merge and no precedence between the two files — a build key inconfig/docuccino.phphas no effect at all.
- build configuration moves out of
- laravel: name a route filter by class, and drop the key that could never be cached (#415)
documents.*.routes.closureis removed. Move the predicate into a class implementingDocuccino\Core\Extensions\Contracts\RouteFilterand name that class underdocuments.*.routes.filter; it is resolved from the container, so whatever the closure closed over becomes a constructor dependency. Then delete theclosureline — no configuration file has a form for a callable, so there is nothing the key can hold that a build will honour.DocumentConfig::$routeFilteris now?RouteFilterrather than amixedcallable slot, so an extension that read it and called it as a callable callsincludes()instead.
- core: drop the representation keyword no reader has ever read (#413)
documents.*.representation.listsis no longer read. It never affected the emitted document — both of its values produced the identical parameter — so removing it changes no consumer-visible byte, but it does changex-docuccino.document.configHashfor any document that set it, and an application that leaves the key in its own config file will simply have it ignored.
- core: leave the viewer wiring out of the hash that keys a document (#410)
x-docuccino.document.configHashno longer folds in a document’sviewerwiring, so it stops changing when only the viewer’s route, middleware, gate, source, driver, CDN flag or driver configuration changes — and its value changes once, for every document that configures a viewer. Anything comparing the hash across this version boundary will see one difference; anything keying on it continues to work, and now shares a fragment cache entry across viewer-only edits.
Features
Section titled “Features”- laravel: write the configuration file from the one it replaces (#424)
- core: read a project’s YAML configuration as a shape that refuses rather than casts (#411)
- laravel: report a ->can() gate that publishes a 403 no request can provoke (#393)
- core: read a parameter name as the leakage lint already reads a property name (#388)
- laravel: rename a request field and a parameter across an API version (#385)
Bug fixes
Section titled “Bug fixes”- bump svgo (#432)
- core: fingerprint a float the same on every host (#427)
- laravel: read every branch a policy resolves through, not the four one major had (#422)
- repo: regenerate a golden with the version it already records (#416)
- core: refuse a configuration file that expands past what a configuration can hold (#428)
- laravel: refuse a configuration value the build cannot read, instead of defaulting in silence (#429)
- laravel: key a fragment on the code and configuration that shaped it (#426)
- laravel: key a fragment on the mapper that shaped it, not the name it was resolved by (#423)
- laravel: refuse a build whose configuration could not be read, rather than exiting clean (#419)
- laravel: keep in the framework config only what boot and a request need (#418)
- laravel: read every configured switch one way, and refuse a value that names none (#412)
- core: mint a component id by the rule that decides whether two components are one (#409)
- laravel: read the middleware a route inherits, which a console build never saw (#408)
- core: key a signature and a minted name on the order their readers actually resolve in (#407)
- laravel: read the authentication middleware by every name a route can give it (#402)
- laravel: key a digest on the order its reader resolves in, not only on the pairs it holds (#401)
- laravel: report a ->can() gate only where the policy body is one its reader owns (#397)
- inference-phpstan: read a throw as the file the fold gave up in, not the file its class was declared in (#396)
- laravel: publish the status an exception pins in its own constructor, not a 500 (#395)
- inference-phpstan: report every status it could not read, and name the throw it came from (#392)
- laravel: refuse a path-parameter rename, and name the operation that refused one (#387)
Performance
Section titled “Performance”- inference-phpstan: analyse an action once per build, not once per version document (#383)
v0.14.1
Section titled “v0.14.1”Bug fixes
Section titled “Bug fixes”- laravel: check a form request body as the message arrived, not as the application rewrote it (#380)
- laravel: publish every recorded example name, on an error status as on any other (#379)
v0.14.0
Section titled “v0.14.0”Breaking changes
Section titled “Breaking changes”- laravel: remove the problem-details error preset (#358)
- the
problem-detailserror preset is removed.error_responsesnow accepts'default'or'none'only, and the bag form (['preset' => …, 'errors_shape' => …]) is gone with it. A document still configured either way is built as if it said'default'and reportsconfig.unknown-error-responsesat warning, so nothing fails silently. Set it to'default'or'none'explicitly: REMOVING the key is not the same edit, because an absenterror_responsesresolves to'none'and publishes no error responses at all.
- the
Bug fixes
Section titled “Bug fixes”- laravel: say what the too-dynamic warning is, and what a filled example really carries (#374)
- repo: edit the fixture tree as one writer replacing whole files, never truncating one others read (#372)
- core: read a bound as the values it admits, not as the value one unit past it (#371)
- inference-phpstan: depend on a declaration a fold read, and locate one without loading it (#370)
- core: illustrate a schema from every keyword that names a value (#369)
- inference-phpstan: depend on every file a traced body was written in, not only the class that reports it (#368)
- repo: read a config key by presence, and widen three guards to the domain they claim (#363)
- laravel: withhold the framework error body at every producer that fills one in (#362)
- inference-phpstan: read a class by everything that builds it, and depend on every file that decided (#361)
- laravel: illustrate an unread error member with a value its own schema accepts (#360)
- core: publish one generated illustration where a second only filled a member in (#359)
- laravel: publish the media type a handler proved when its body could not be read (#357)
- inference-phpstan: read a status where the throw is not a literal construction (#355)
- laravel: keep the error body a handler proved when its status could not be read (#354)
- ci: install the phpstan the fixture app can actually resolve, and give clover room (#365)
v0.13.1
Section titled “v0.13.1”Features
Section titled “Features”- laravel: scope a scaffolded change only where the application forked the shape (#346)
- laravel: write a version change beside the module that owns the shape (#345)
- laravel: scaffold version-change classes from the diff the product can already compute (#342)
- laravel: read version changes from every directory a modular app keeps them in (#341)
- laravel: put a response field a version removed back into the older document (#336)
- laravel: declare when a field became required, and in which direction (#334)
- laravel: scope a version change to chosen operations (#321)
- laravel: derive a document per API version from declared changes (#320)
- attributes: declare the API version-change vocabulary (#318)
Bug fixes
Section titled “Bug fixes”- ci: retry the type-coverage gate serially, where its cache cannot race itself (#353)
- laravel: publish one error contract for an HTTP error whose status could not be read (#351)
- inference-phpstan: read a construction’s status by one rule, in the scope it is written (#350)
- laravel: document the one status a route-name conditional takes on that route (#349)
- inference-phpstan: read the status from the factory a throw names (#348)
- inference-phpstan: read the status an HttpException subclass sets on itself (#347)
- core: mint a class schema identity in one place, facet and all (#333)
- inference-phpstan: stabilise a walk scope through the interface that promises the call (#339)
- laravel: rewrite an example with the schema a version renames (#328)
- core: key a cached fragment on the document whose identities it carries (#326)
- laravel: refuse to widen a version-change scope, and read a version set as versions (#323)
Performance
Section titled “Performance”- laravel: declare the API version header once and point every operation at it (#327)
v0.13.0
Section titled “v0.13.0”Breaking changes
Section titled “Breaking changes”- core: give one schema direction one verdict, wherever it was computed (#312)
docuccino:diff --enforcenow rejects changes it accepted before, all of them a widening on a RESPONSE: a type set that grew (schema.type-widened) or atypeconstraint that left (schema.type-removed); anallOfbranch or the wholeallOfremoved; anotremoved; acontainsthat was asserting something removed; aminContainslowered and amaxContainsraised. An$idarriving on a schema is also breaking now, where before only a changed or removed one was. Requests are unaffected. Migrating a schema betweennullable: trueandtype: [T, null]now reports nothing at all, where it previously reported a non-breakingschema.type-widened.
- core: report a body declaration no operation could read (#309)
attribute.schema-class-unusableis a new warning, so a pipeline running--fail-on=warningcan newly fail on an application that binds a request type carrying#[BodyParameter]to read routes only. Delete the declaration, describe the parameters with#[QueryParameter]on the action, or accept the code viadiagnostics.accept.
- core: decide every keyword the draft model knows, so no repointed discriminator passes –enforce as safe (#305)
docuccino:diff --enforcenow rejects changes it previously accepted. A discriminator arriving, its tag property renamed, or a mapping entry removed or repointed is breaking on both sides; a mapping entry added, and the discriminator leaving, are breaking on a response, the same argument a value joining a responseenummakes; a null withdrawn is breaking on both sides and one admitted is breaking on a response; and an$id,$anchoror$schemachanged is breaking, because a pointer this diff does not resolve may name the old one and a changed dialect makes every comparison beside it a comparison of two languages. A pipeline gating on the diff may need a version bump it did not need before.
- core: give every refinement keyword a direction, so no tightened bound passes –enforce as safe (#304)
docuccino:diff --enforcenow rejects changes it previously accepted. A refinement tightened (schema.refinement-narrowed) is breaking on both sides; one relaxed (schema.refinement-widened) is breaking on a response, the same argument a value joining a responseenummakes; and one whose direction cannot be computed (schema.refinement-changed) is breaking, because a false alarm costs the author one look and a false “safe” costs the consumer a broken client. A pipeline gating on the diff may need a version bump it did not need before.
- core: let a request type carry the declarations that are true of it (#295)
- a
#[BodyParameter]on a request class used to document nothing and now patches that class’s component, so an application that had written one emits a different request body. A class-target attribute a request type is not read for now raisesattribute.schema-class-unreadrather than being ignored, which a build run with warnings as failures will notice. AndRuleSetNormalizer::report()no longer defaults its source-class argument, so a caller that omits it is refused by PHP rather than silently weighing one declaration site of the two — a signature break for anything outside this repository that calls it.
- a
- core: give every subschema position a polarity, so no composition narrowing passes –enforce as safe (#294)
docuccino:diff --enforcenow rejects changes it previously accepted — a narrowing under any composition or conditional keyword, and any change undernot,ifor a definition store. A pipeline that was green on such an edit will now fail, which is the gate telling the truth for the first time; the change is either honest under the document’sversioningpolicy or it is not.
- core: answer the values OpenAPI 3.2 added to a member’s domain when downleveling (#293)
- A downleveled 3.1 or 3.0 document no longer carries a parameter whose location OpenAPI 3.2 alone defines, nor a
stylevalue it alone defines, and each loss raises adownlevel.value-not-in-3.1warning. The bytes those documents used to carry were invalid against the version they claimed, so a consumer’s validator already refused them; a build gating on warnings will now see one where it previously saw none.
- A downleveled 3.1 or 3.0 document no longer carries a parameter whose location OpenAPI 3.2 alone defines, nor a
Bug fixes
Section titled “Bug fixes”- core: key a security requirement the way every other diff identity is keyed (#311)
- core: publish no path the scrubber could not account for (#310)
- core: ask a recognised root how deep it is before attributing a run to it (#308)
- core: reduce a bare path a brace or a backslash had refused, where a root already accounts for it (#307)
- core: stop taking a compression wrapper as proof of a local file (#306)
- core: read the two keywords that say what an example may not be (#292)
- core: decide every registered stream wrapper, and guard the table against going short (#291)
v0.12.0
Section titled “v0.12.0”Breaking changes
Section titled “Breaking changes”- laravel: let a response-header declaration say nothing about what it did not write (#287)
Exchange::$headersisarray<string, list<string>>;Exchange::header()returns a list. Callers reading a single string must take the first element.
- laravel: publish only the model keys a response actually carries (#285)
- an Eloquent model schema stops publishing keys the server does not return — an append or a
$withrelation named in$hiddenor absent from a$visibleallow-list, a name written in both lists, and the framework’s ownexists,timestamps,incrementing,preventsLazyLoading,wasRecentlyCreatedandusesUniqueIdsproperties. A client generated from the new document loses fields it was never receiving, and a diff against a previously published document reports those keys as removals.
- an Eloquent model schema stops publishing keys the server does not return — an append or a
- attributes: let a parameter declaration say nothing about required (#284)
$requiredon#[QueryParameter],#[HeaderParameter],#[CookieParameter]and#[ResponseHeader]is now?booldefaulting tonull. A declaration that relied on the oldfalsedefault to mark a parameter optional must now sayrequired: false.
- core: tell an absent answer from an answer nobody can read (#281)
ContractParameter::schema()returns aParameterSchemarather than?array. Call->read()for the node and->kindfor why there isn’t one.
- attributes: let a body declaration say a field is optional (#280)
BodyParameter::$requiredis now?booldefaulting tonull. Code constructing the attribute positionally, or reading$requiredas abool, must handle the third state.
- core: drop ValidationField::type(), which answers a union with null (#279)
ValidationField::type()is removed from the rule-transformer surface a third-partyRuleTransformeris handed. Usetypes(): list<string>, which answers every type word the field carries: one for a scalar type, several for a union, none where nothing has typed it yet. Null is never among the words — nullability is a flag the schema applies as it assembles, so a rule running afternullablestill reads what the field is.count($types) === 1 ? $types[0] : nullrestores the old answer exactly, and restores the defect with it: branch on the words instead —$types === []is “nothing has typed this”, and a field stating several is a case to handle, not one to fall through.
Bug fixes
Section titled “Bug fixes”- laravel: report an author-supplied name that matched nothing instead of dropping it silently (#283)
- repo: restore the entries a squashed stack took out of v0.11.0’s changelog (#282)
- laravel: credit a webhook delivery for what the check proved (#277)
- laravel: let a nested body declaration reach the key it names (#276)
v0.11.0
Section titled “v0.11.0”Breaking changes
Section titled “Breaking changes”- core: gate contract coverage on documented responses, not operations (#260)
docuccino:coverage --minis measured against documented responses rather than operations, so an existing floor will read lower.CoverageReport::total()andexercisedCount()are removed rather than silently re-meaning;missing()changes from “operations never exercised” to “operations with any unexercised response”.documentedStatuses()renders in family order, so{500, 5XX, 1XX}reads500, 1XX, 5XXwhere it read1XX, 500, 5XX.assertValidRequest()no longer credits a response.
- laravel: record an example only where an assertion names it (#271)
ApiContract::record()no longer publishes an example for every checked response. An exchange is recorded only where the assertion names the scenario —assertValidExchange(recordAs: 'with-tags')— so a suite that records today records nothing tomorrow until its call sites name what is worth publishing. Committed recordings already on disk are still read and still publish; each build reports them once asexamples.recording-unnamed, since no run will refresh them. An explicitrecordAs: ''now raises rather than being ignored.
Features
Section titled “Features”- core: hold the payload a webhook dispatches to its contract (#262)
- core: check the response headers the document publishes (#261)
- laravel: report a nested data collection that will be wrapped (#265)
- core: hold a security requirement to the schemes the document publishes (#272)
Bug fixes
Section titled “Bug fixes”- laravel: mark the response headers the framework always sends as required (#263)
- core: read a parameter’s type in the grammar the validator reads (#266)
- core: read an ambiguous empty body as the container the contract accepts (#267)
- core: follow a $ref wherever the grammar permits one (#268)
- laravel: read a #[BodyParameter] name as a field path, not a map key (#269)
- laravel: read a bare array rule as either container, not as a list (#270)
- core: resolve a Reference Object before the diff decides what changed (#273)
v0.10.5
Section titled “v0.10.5”Features
Section titled “Features”- core: report an annotation-only schema change without gating on it (#256)
Bug fixes
Section titled “Bug fixes”- core: read a generated key as its kind, not its identity (#257)
v0.10.4
Section titled “v0.10.4”Features
Section titled “Features”- core: describe a request body per operation (#253)
Bug fixes
Section titled “Bug fixes”- laravel: keep the declared container through the rule vocabulary (#252)
- laravel: a date field’s format comes from its most specific source (#251)
- core: read a lint safelist pointer in the spelling every message prints (#250)
v0.10.3
Section titled “v0.10.3”Features
Section titled “Features”- core: let a class describe the schema it publishes (#247)
Bug fixes
Section titled “Bug fixes”- laravel: follow a remapped property to the key the request accepts (#246)
- core: publish an input DTO’s own prose on the request side (#245)
v0.10.2
Section titled “v0.10.2”Bug fixes
Section titled “Bug fixes”- laravel: wrap every arm of a composite at the response root (#243)
v0.10.1
Section titled “v0.10.1”Bug fixes
Section titled “Bug fixes”- core: read a quoted JSON string example as the string it quotes (#239)
- laravel: a union keeps every member a producer contributes one shape to (#238)
Performance
Section titled “Performance”- ci: make the coverage gate fast and separately re-runnable (#241)
v0.10.0
Section titled “v0.10.0”Breaking changes
Section titled “Breaking changes”- laravel: say what became of a configured path instead of failing in silence (#228)
- a document whose
info.description.filecould not be read publisheddescription: ""and now publishes nodescriptionmember, with theconfigHashmoving to match. An empty string claims this API’s description is the empty string; absent is the true answer, and it now arrives with an error or a warning naming the key. Separately, a document namingcoverage.logby absolute path re-fingerprints once, because that path is no longer folded intoconfigHash— it was machine-dependent there.
- a document whose
- core: scrub a machine out of a message without rewriting what the author wrote (#218)
- the drop half of
downlevel.path-item-refis nowdownlevel.path-item-unresolved. A pipeline at--fail-on=warningthat accepted the old code to silence the inlining notice can now fail on the drop, which is the point — the two were never the same finding.
- the drop half of
- attributes: stop declaring a target nothing can honour (#216)
#[Summary]no longer declaresTARGET_PROPERTYand#[Example]no longer declaresTARGET_PARAMETER. Code reflecting either at those targets now throws where it did not, and a#[Summary]written on a property is a PHP error rather than a build diagnostic.
- laravel: consult #[IgnoreResponse] before a producer converts a body (#206)
- a document built from unchanged code can now lose responses it used to publish. A consumer cannot tell a repaired defect from a withdrawn response — a generated client loses a case from its error union, and
docuccino:diffreports a removed response — so this is treated as breaking from the outside even though the old behaviour was the bug.
- a document built from unchanged code can now lose responses it used to publish. A consumer cannot tell a repaired defect from a withdrawn response — a generated client loses a case from its error union, and
- core: derive a schema keyword’s shape from its own contract (#203)
unevaluatedItems,unevaluatedPropertiesandadditionalItemsnow publish as objects rather than empty arrays and take their place in the normative member order. A boolean atitems,contains,not,if,then,elseorpropertyNamesis published as written, which reverses whatnot: falsemeans to a generated client.
- core: downlevel a response whose key is spelled like a keyword (#202)
- a 3.0 document’s
responses.defaultand any component or header named after a schema keyword are now converted rather than passed through, so 2020-12 constructs at those positions become their 3.0 equivalents. Acomponents.pathItemsentry referenced frompathsis inlined at each use site instead of leaving a$refto a bucket 3.0 does not have.
- a 3.0 document’s
- core: stop a numeric or multi-shape component name colliding in silence (#199)
- a shared error response offering two or more representations that each reference a distinct component is now published as
AuthenticationChallenge_ProblemDetailsDatarather thanAuthenticationChallengeProblemDetailsData, renaming that type in generated clients.--yamloutput quotes numeric mapping keys, so everyresponseskey moves from200:to'200':.
- a shared error response offering two or more representations that each reference a distinct component is now published as
- core: give every server variable the default the spec requires (#196)
postman.server-variable-no-defaultis retired in favour ofserver.variable-no-default, which every emitter raises; adiagnostics.acceptentry or--fail-onfilter naming the old code stops matching and will surface asconfig.accept-unused. A server variable declaring anenumand nodefaultnow publishes the enum’s first value as itsdefault; one declaring neither is no longer emitted.
- core: name a contested error response for the body it actually carries (#192)
- an error response carrying a representation its claimed name does not describe is now published under a name derived from the components it references, instead of that claim plus a content hash. A document that published two hash-suffixed variants of one name now publishes the claimed name and a derived one, so a generated client’s type name changes for the contested body.
Features
Section titled “Features”- core: read #[Example] and #[Description] on the properties they declare (#204)
- attributes: let a #[Response] name the error component it declares (#193)
Bug fixes
Section titled “Bug fixes”- core: say a recordings directory was refused instead of publishing nothing (#236)
- core: reduce a path a colon interrupts, and redact a shallow machine prefix (#229)
- core: name a class the same way wherever a build prints one (#227)
- core: read a boolean subschema as the schema it is when building an example (#226)
- core: drop every member OpenAPI 3.2 added when downleveling (#224)
- core: read a boolean at a top-level Schema slot instead of dropping the member (#221)
- laravel: describe the filter kinds the installed query builder ships (#211)
- laravel: recognise every name mapper the installed spatie ships (#209)
- core: close the loose ends a diagnostics sweep left (#208)
- inference-phpstan: follow a fluent call to the status it sets (#207)
- laravel: read an authored @example wherever it is written (#205)
- core: pull JSON into a document through one reader (#200)
- core: write empty maps as maps in YAML output (#197)
- core: give a 3.0 operation the responses the spec requires of it (#195)
- core: keep an authored empty-object example an object from docblock to bytes (#191)
v0.9.1
Section titled “v0.9.1”Bug fixes
Section titled “Bug fixes”- core: audit examples against the document that ships, and never let a lint kill the export (#189)
v0.9.0
Section titled “v0.9.0”Breaking changes
Section titled “Breaking changes”- core: retract the keywords a declared schema shape supersedes (#185)
- a schema whose shape is declared by an attribute, docblock or overlay no longer publishes the inference keywords that declaration supersedes. Documents gain a closed shape where they previously advertised extra keys, and lose
type/itemsbeside a declared$ref.
- a schema whose shape is declared by an attribute, docblock or overlay no longer publishes the inference keywords that declaration supersedes. Documents gain a closed shape where they previously advertised extra keys, and lose
- laravel: patch a recovered request body instead of replacing it (#184)
- an operation with both a recovered request body and a
#[BodyParameter]now publishes the recovered properties with the declared one patched in, rather than only the declared one, and gains the422it was previously missing. Documents affected by the old behaviour will change shape — in the direction of describing what the endpoint actually accepts.
- an operation with both a recovered request body and a
- core: name a shared error component for what it is, not for what surrounded it (#183)
- shared error components are named and grouped differently. Two operations whose error bodies differ only in wording now share one
components.responsesentry — most often gaining a named type where each previously kept an inline schema, since the wording had been keeping them below the sharing threshold — and a hoisted shape under a multi-representation response is namedError<status>rather than inheriting the response’s claimed name. Non-plurality arms gainsummary/descriptionbeside their$refin 3.1/3.2 output; a 3.0 export drops that wording with adownlevel.ref-siblingsnote. Examples now merge across wordings, so an operation may advertise an example another operation recorded. A generated client written against the previous names will need regenerating.
- shared error components are named and grouped differently. Two operations whose error bodies differ only in wording now share one
- core: let a declared response retract the placeholders it supersedes (#181)
- the
inferred-response.unpinned-redirectdiagnostic is nowlint.unpinned-redirect. Anything filtering on the old code, or safelisting it underdiagnostics.accept, needs the new name.
- the
Bug fixes
Section titled “Bug fixes”- laravel: stop three help strings prescribing a remedy that changes nothing (#187)
- laravel: remove an ignored parameter late enough that it stays removed (#186)
- laravel: read a docblock example as the type its schema declares (#180)
- core: honour a document’s configured format samples in the Postman collection (#179)
v0.8.5
Section titled “v0.8.5”Features
Section titled “Features”- laravel: share the pagination envelope links and meta across page components (#177)
- laravel: let a document override filter descriptions and format samples (#176)
- core: synthesize a property example from the validation rules a request recovers (#171)
- laravel: share one page component per item type and paginator kind (#169)
Bug fixes
Section titled “Bug fixes”- laravel: stop the diagnostics channel drowning in noise nobody can act on (#175)
- laravel: stamp coverage-span tests from one base and let a reason replace the generic note (#174)
- laravel: let a QB entry’s own comment and default outrank its filter class attribute (#173)
- laravel: describe each Query Builder filter by the match it performs (#170)
- laravel: declare the packages the integrations target, and name the real fields separator (#168)
- core: publish the reason a deprecated operation carries (#167)
v0.8.4
Section titled “v0.8.4”Features
Section titled “Features”- core: project tag groups and pass viewer configuration through (#158)
- laravel: document sparse fieldsets as enums of the allow-list (#154)
- laravel: describe and name the include and sort enum values (#153)
- laravel: document include and sort as enums of the allow-list (#148)
Bug fixes
Section titled “Bug fixes”- laravel: serve each viewer the OpenAPI version it implements (#157)
- laravel: report unreadable attributes, inherit class-level ones, read @deprecated (#161)
- laravel: type string-backed enum route bindings from the enum (#160)
- core: flag unions whose empty branch erases the typed contract (#162)
- laravel: degrade include and sort typing on spatie/laravel-query-builder below v7 (#152)
- core: emit an explicit empty schema for untyped parameters (#147)
v0.8.3
Section titled “v0.8.3”Features
Section titled “Features”- laravel: type QB foreign-key filters off the related model’s key (#142)
- laravel: type QB filters off the subject model’s primary key (#141)
Bug fixes
Section titled “Bug fixes”- laravel: let a shared filter class declare its schema via a class-level attribute (#143)
v0.8.2
Section titled “v0.8.2”Features
Section titled “Features”- website: add robots.txt pointing crawlers at the sitemap (#139)
v0.8.1
Section titled “v0.8.1”Bug fixes
Section titled “Bug fixes”- laravel: type QB filters off their declared column binding (#137)
v0.8.0
Section titled “v0.8.0”Breaking changes
Section titled “Breaking changes”- attributes: add format to the parameter attributes (#135)
$formatsits before$requiredin the constructors ofQueryParameter,HeaderParameter,CookieParameterandBodyParameter, so positional arguments past$descriptionshift by one; named arguments are unaffected.
v0.7.0
Section titled “v0.7.0”Breaking changes
Section titled “Breaking changes”- core: classify response-side enum changes as breaking (#131)
- docuccino:diff –enforce now fails changesets that add an enum value to, or drop an enum constraint from, a response schema or a referenced component schema; these previously passed as non-breaking.
Features
Section titled “Features”- core: track component schema direction to refine enum classification (#132)
v0.6.1
Section titled “v0.6.1”Performance
Section titled “Performance”- inference-phpstan: replay a recorded file walk instead of re-running the resolver (#128)
- inference-phpstan: harvest a file’s methods, closures and assignments in one pass (#127)
v0.6.0
Section titled “v0.6.0”Breaking changes
Section titled “Breaking changes”- attributes: address the API consumer with #[Summary] and #[Description] (#118)
#[DescriptionFromFile('docs/x.md')]is removed. Use#[Description(file: 'docs/x.md')], which does the same thing and also takes inline prose astext:. A second attribute that does almost the same job is a worse API than one that does both — the same reasoning that gave#[Example]itsfile:argument rather than a sibling attribute.
Features
Section titled “Features”- laravel: make contract coverage a post-run command (#124)
- laravel: walk a new install to its first document (#120)
- laravel: point a diagnostic at the page that documents it (#119)
- laravel: read the provenance trail back with docuccino:explain (#116)
- laravel: accept the diagnostic codes you have already read (#114)
- laravel: record your test suite’s responses as documented examples (#109)
- core: show several named examples, and read one from a file (#108)
- laravel: assert your test suite against the generated contract (#107)
- laravel: rebuild and refresh the viewer as your code changes (#106)
- laravel: resolve the viewer through its contract and ship a Redoc driver (#105)
- core: lint the document for missing prose, unusable ids and undeclared tags (#104)
- laravel: let –fail-on gate on the info and hint rungs (#103)
- laravel: hint how a mock server should fake a property (#102)
- laravel: document the webhooks an API delivers (#101)
- laravel: document binary, file, streamed and SSE responses (#99)
- laravel: let projects extend the engine’s PHPStan configuration (#90)
- core: promote the schema conversion surface used by integrations to a public contract (#85)
- core: emit a Postman collection (#81)
- laravel: write every configured export target in one run (#80)
Bug fixes
Section titled “Bug fixes”- laravel: widen a rule’s values for every entry that names none (#125)
- core: report a credential a recording cannot redact (#126)
- core: place a call’s arguments where its readers index them (#123)
- core: agree the example report’s verb with how many examples lied (#121)
- laravel: say why a driver’s own response never live-reloads (#115)
- repo: stage a cache-invalidation test’s disagreement in its own directory (#110)
- laravel: document a rendered view as text/html instead of reflecting it (#98)
- inference-phpstan: gate the throw-registry rescue on the resolved callee (#89)
- laravel: reject unknown option values and diagnose misconfigured keys (#88)
- laravel: publish the real generator version (#87)
- laravel: print diagnostic help text in command output (#86)
- core: surface engine boot failures and stop caching their degraded fragments (#84)
- core: keep the export destination out of the document config hash (#79)
v0.5.1
Section titled “v0.5.1”Features
Section titled “Features”- core: let a finding the whole document reports travel on the route that found it (#70)
Bug fixes
Section titled “Bug fixes”- laravel: document a page-size key only where the key’s value IS the size (#75)
- inference-phpstan: serve a local’s value only to the method that wrote it, and only until something rewrites it (#74)
- inference-phpstan: report the file a node was written in, which is not always the file being analysed (#73)
- core: read every form that writes a local, not only the plain assignment (#72)
- laravel: report the handler deferrals a warm build had been coming back without (#71)
- laravel: let a diagnostic name the closure it means without naming the machine (#69)
- core: stop a collision blaming an author for a name nothing claimed, and name the remedy (#66)
- laravel: let the tier that cannot read an error body stand aside for one that can (#65)
- inference-phpstan: read a response named in a local as the response it was named from (#64)
- laravel: document the page-size key a list endpoint really reads (#63)
v0.5.0
Section titled “v0.5.0”Breaking changes
Section titled “Breaking changes”- laravel: name only the paging keys an endpoint really reads (#58)
- a Spatie Query Builder list endpoint no longer documents a
per_pagequery parameter, and documents its page key under the name the paginating call site gave it. An application that really reads a page-size key, or one whose helper renames the page key out of sight, declares it with#[QueryParameter].
- a Spatie Query Builder list endpoint no longer documents a
- core: share one error response between arms that only illustrate it differently (#51)
- an application whose operations state one error response with differing examples now publishes one
components.responsesentry with anexamplesmap, where it previously published either an inline response per operation or one hash-discriminated component per illustration. The$refs those operations emit, and the type names a client generated from them carries, change accordingly.
- an application whose operations state one error response with differing examples now publishes one
- core: diff what a client must satisfy, not only what it asks for (#55)
Changeset::$unreferencedSchemasis now$unreferencedComponents(unreferencedComponentsin the JSON payload), since a security scheme nothing requires is stood down into the same list. TheChangeTargetenum gains asecuritySchemecase.
- core: count every parameter an old artifact declares, and print none of its text as written (#46)
- a diff whose old side declares parameters on a path item, or reuses one
x-docuccino.idacross two nodes, now reports removals, additions and edits it previously passed over in silence, sodocuccino:diff --enforcecan fail a comparison it used to allow. The finding is real either way — the gate was reading a document it could not see all of.
- a diff whose old side declares parameters on a path item, or reuses one
- core: read a diff’s $ref’d parameters as the parameters they name (#43)
- a diff of a document whose operations share parameters through
components.parametersnow reports parameter removals, additions and edits it previously passed over in silence, sodocuccino:diff --enforcecan fail a comparison it used to allow. The finding is real either way — the gate was reading a document it could not see all of.
- a diff of a document whose operations share parameters through
- core: let the producer of an error response name the component it publishes under (#40)
- shared error components are published under the name of the error rather than its status. Laravel’s own errors become
BadRequest,Unauthorized,Forbidden,NotFound,UnprocessableEntityandTooManyRequestsincomponents.schemasandcomponents.responses, in place ofError400,Error401,Error403,Error404,Error422andError429; theirx-docuccinocomponent ids change with them. Regenerating a client renames its error types to match — the statuses and bodies are unchanged, so only a hand-writtencatchon the old type name needs updating. A status nothing claims a name for is stillError<status>. To choose your own names, register anExceptionToResponseor claim over a built-in from anOperationExtension; see the docs at /extending/extension-authoring/#naming-the-component-an-error-publishes-under.
- shared error components are published under the name of the error rather than its status. Laravel’s own errors become
- core: pair an exported artifact’s parameters and schemas by the id it carries (#38)
- emitted documents change for existing projects. Response component names are content-derived, so dropping an unbindable member’s placeholder from an example re-mints the hash suffix of any component whose example carried one, and those names flow into generated client type names. A request property recovered as
array<string, V>whose class overridesrules()now emits{"type":"object","additionalProperties":{}}where it emitted{"type":"array"}. Regenerate and re-commit the artifact you diff against.
- emitted documents change for existing projects. Response component names are content-derived, so dropping an unbindable member’s placeholder from an example re-mints the hash suffix of any component whose example carried one, and those names flow into generated client type names. A request property recovered as
Features
Section titled “Features”- laravel: tell a consumer how to ask for the next page, not just which one they are on (#52)
- laravel: let the render method that built an error body name it (#54)
- laravel: let an exception name the error component it publishes under (#45)
Bug fixes
Section titled “Bug fixes”- core: tell the truth about two changes the diff went quiet on (#61)
- laravel: let a thrown message name a file without naming the machine (#60)
- inference-phpstan: read a match arm’s several conditions as alternatives, not requirements (#57)
- core: make every byte a diff prints, and every node it pairs, survive the artifact (#56)
- repo: finish escaping at the render boundary, and guard the fixture app against drift (#53)
- core: stop a schema no operation reaches failing the diff gate as breaking (#50)
- repo: stop the cold type-coverage run deadlocking in a forked child (#49)
- laravel: escape a diagnostic where it is printed, not where it is written (#48)
- inference-phpstan: stop promising a body member the response sometimes omits (#44)
v0.4.0
Section titled “v0.4.0”Breaking changes
Section titled “Breaking changes”- core: name the thing a diagnostic’s reader must go and change (#37)
identity.duplicate-operationis renamed toroute.duplicate-operation. Anything matching that code by name must be updated. The other two renames were added after v0.3.0 and have never shipped.
- core: mint every component name from what it is, and stop warning about correct code (#35)
- two contested definitions in
components.responsesorcomponents.securitySchemesnow take content-derived names instead of a first-come_2suffix, and thesecurityrequirements naming them are repointed. A host-bound operation’sserversURL now carries the port and base path of the document server it inherits from.
- two contested definitions in
- core: publish a component under the name its schema earns, not the slot it landed in (#30)
- a class-derived request body now publishes under a
Request-suffixed component name. The facet applies whether or not the name is contested — that is what makes it local, since adding a read endpoint can then only ever add a name rather than reassign one. A SpatieArticleDataused only as a request body publishes asArticleDataRequestwhere it published asArticleData, so a generated client renames that type once. Pin the old name with#[SchemaName]if you need it unchanged.
- a class-derived request body now publishes under a
- inference-phpstan: delete the worker pool and the engine result cache (#21)
- docuccino/inference-phpstan no longer ships the bin/worker.php binary or depends on symfony/process, and the @internal Orchestration and Cache namespaces are gone along with PhpStanEngineFactory::createOrchestrated() and ::createCaching(). The docuccino/laravel engine.mode values “orchestrated” and “caching” are removed; setting either now degrades to in-process with an engine.mode-unknown warning in place of engine.mode-not-wired.
Bug fixes
Section titled “Bug fixes”- laravel: warn when a published value came from the build machine (#34)
- core: key a fragment on where a fact was written, and give two identities two components (#33)
- laravel: type a bound parameter from the column it names, and stop publishing the catch-all (#32)
- laravel: document a route bound to a host as an operation of its own (#29)
- inference-phpstan: serve a memoised response shape only to a caller that could have earned it (#27)
- core: share an error body by its shape, not by its wording (#26)
- laravel: document rate-limit headers by meaning, not by value (#24)
- laravel: emit real types where the schemas documented nothing (#23)
- laravel: make the fragment cache safe to turn on, and warm builds cheap (#20)
v0.3.0
Section titled “v0.3.0”Breaking changes
Section titled “Breaking changes”- core: carry node identities into exported OpenAPI so the diff stays semantic (#18)
docuccino:exportwrites anx-docuccino-idmember on every node of an OpenAPI artifact. Re-exporting an existing artifact shows that as a one-time diff; pass--drop-idsfor the previous bytes. Emitting through the library is unaffected —OpenApi32Emitter::emit()still drops every Docuccino member by default.
Features
Section titled “Features”- laravel: recover the Query Builder allow-lists a method or a constructor builds (#19)
Bug fixes
Section titled “Bug fixes”- laravel: illustrate an error member with the value its schema states (#16)
v0.2.1
Section titled “v0.2.1”Bug fixes
Section titled “Bug fixes”- laravel: document an error response under the status its body states (#14)
v0.2.0
Section titled “v0.2.0”Breaking changes
Section titled “Breaking changes”- laravel: document spatie’s inherited POST 201 default (#10)
- a POST action returning a spatie Data class without its own
calculateResponseStatus()override is now documented201instead of200. This matches what the application actually returns, but it changes emitted output — and so adocuccino:diffresult — with no action from the user. An app that genuinely answers 200 on a POST is one that overrides the method, and the override is still read first.
- a POST action returning a spatie Data class without its own