Repository navigation
feat: OpenAPI - #743
Open
crookse wants to merge 7 commits into
Open
feat: OpenAPI#743crookse wants to merge 7 commits into
crookse wants to merge 7 commits into
Conversation
Adds src/modules/decorators/OpenAPI.ts: a method decorator carrying an OpenAPI
v3 Operation Object, plus the typings for it and the readers that turn a set of
annotated resources into the `paths` member of a document.
class Users extends Resource {
public override paths = ["/users/:id?"];
@openapi({
summary: "Get a user",
responses: { "200": { description: "The user" } },
})
public override GET(request: Request) { ... }
}
const paths = buildPathsObject([Users]);
These are TC39 stage-3 decorators, which is what TypeScript 6 compiles to with
no `experimentalDecorators` set -- `(value, context)`, not the legacy
`(target, key, descriptor)`.
Operations are held in a module-level WeakMap keyed by the method function,
rather than as a property on the method. Decorating must not change what it
decorates: a property would be visible to anything walking the prototype, and a
subclass overriding GET would inherit it, attributing the parent's
documentation to a different implementation. The decorator returns the method
untouched, so the prototype's method is the key that was registered.
buildPathsObject translates between the two path syntaxes. Drash matches with
URLPattern, so a parameter is `:id`; OpenAPI wants `{id}` and requires path
parameters to be `required: true`. An optional `:id?` therefore cannot map to a
single path -- it expands into `/users` and `/users/{id}`, which is how those
two URLs are expressed in a v3 document. Parameters described in the decorator
win over the derived stubs, so a param can be given a schema or a description.
Decorating a non-HTTP method name, a static method, or a private one throws at
decoration time rather than leaving the operation silently absent from the
generated document.
No YAML serialisation here: the repo has no YAML dependency, and the returned
object is plain enough for JSON.stringify or any YAML library to take.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… point
The decorator and its module take the spec version in their name, so
@OpenAPIv3 says which spec the Operation Object is written against.
src/modules/openapiv3.ts joins http.native.ts and http.polyfill.ts as an entry
point, re-exporting the decorator, the three readers, and all twenty object
types. Consumers import from there rather than reaching into
modules/decorators/, which keeps the decorators directory an implementation
detail the way standard/ is.
The four TypeError messages and the JSDoc example are renamed with it. An error
reading "OpenAPI: `search` is not an HTTP method" names something the user
never wrote, and the reference page quotes those strings verbatim, so leaving
either side alone would make the docs wrong.
Docs follow the rename: the page moves from decorators/open-api to
decorators/openapiv3, mirroring the source file name as the reference sidebar
requires. Nothing redirects on that site, so the inbound links from the Modules
overview -- four table rows and a section card -- are repointed in the same
change. Every import shown now uses modules/openapiv3.js.
Prose about the specification itself ("an OpenAPI v3 document", "required by
OpenAPI 3.0") is left alone. That is the format's name, not an identifier.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Builds an OpenAPI v3 document from resource files, so the glue between
buildPathsObject and a JSON file is not written by hand:
npx --package @drashland/drash drash-openapi ./build/resources -o openapi.json
Takes files or directories, walked recursively and skipping node_modules and
dotdirs. Options cover info.title, info.version, info.description, servers, the
OpenAPI version, and the JSON indent. Exit code is 1 on every failure so it can
gate a build.
It imports the resource files rather than parsing them, so the decorators run
and the document reflects what the code declares. Only classes with a decorated
method are passed on: buildPathsObject constructs what it is given to read
`paths`, and handing it every export would construct unrelated classes.
Point it at COMPILED JavaScript. Decorators are syntax rather than types, so
Node cannot parse a file still containing the decorator, and its type stripping
does not help -- stripping types leaves the decorator behind.
The CLI lives in bin/ rather than src/. tsup's --dts pass runs tsc, which
cannot resolve `node:` builtins here because tsconfig.json sets no
moduleResolution and Classic resolution only handles relative paths. Loosening
that for the whole library would let a runtime-specific global type-check
inside core/, which is the opposite of what this codebase is. A bin is not
library surface, so it gets its own tsup pass with no declarations, and
`deno task check` covers it.
`bin` in package.json is copied into dist/package.json by doit.ts along with
the rest of the manifest, so no publishing change is needed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
modules/openapiv3.ts is the path consumers import from, so it gets the page and modules/decorators/ stops being a destination. The decorators directory is an implementation detail, the same way standard/ internals are not import targets, and a sidebar entry for it invited people to depend on the path. The decorator page's content moves wholesale rather than being rewritten, so the runnable example and its resulting document survive intact. The entry point's framing -- module identity, import, and an exports table -- is grafted on ahead of it, and the CLI section sits with the other way of producing a document, before the hand-written YAML step. Nothing redirects on this site, so the four inbound rows on the Modules overview and its section card move in the same change, and the two cross-page links become in-page anchors. The Modules sidebar is reordered so files come before directories -- the order `ls src/modules` gives -- with `index` still pinned first, since the Modules breadcrumb resolves to whatever is first and a folder route with no page 404s. Terminology throughout is "OpenAPI Specification v3", abbreviated to "OAS v3" after the first use. It is the name of the format; "OpenAPI" alone is the organisation. Identifiers are untouched: the OpenAPIv3 decorator, the openapiv3 module, the --openapi flag, the `openapi` document field, the drash-openapi command, the openapi.json and openapi.yaml filenames, and the four quoted TypeError strings all keep their spelling. bin/openapiv3.ts moves with it. The page documents those flags verbatim, so leaving the CLI alone would have made --help read "OpenAPI version" while the table beside it read "OAS version". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 tasks
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.