diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c69bd91..ac693c8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -220,7 +220,7 @@ jobs: try { const address = server.address(); const origin = `http://127.0.0.1:${address.port}`; - const login = await fetch(`${origin}/example-login`); + const login = await fetch(`${origin}/docs/example-login`); if (!login.ok) throw new Error(`Express demo login returned ${login.status}`); const setCookie = login.headers.get('set-cookie'); if (!setCookie) throw new Error('Express demo login did not issue a session cookie'); diff --git a/LICENSE b/LICENSE index e0480d56..261eeb9e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,19 +1,3 @@ -SPDX-License-Identifier: Apache-2.0 - -Copyright 2025 Vishnu R - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ diff --git a/NOTICE b/NOTICE index 55d7602f..dd074993 100644 --- a/NOTICE +++ b/NOTICE @@ -1,3 +1,6 @@ +FlexDoc +Copyright 2025 Vishnu R + ** tslib; version 2.8.1 -- https://www.typescriptlang.org/ Copyright (c) Microsoft Corporation diff --git a/README.md b/README.md index 83c5a715..7295c72a 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,7 @@ FlexDoc -

Backend-native OpenAPI documentation, API exploration, and execution.

-

One canonical renderer. Thin native adapters. No FlexDoc control plane required.

+

Your OpenAPI says one thing. Your backend may be running another.

CI @@ -22,9 +21,43 @@

-FlexDoc is Prauga's open-source, self-hosted OpenAPI documentation renderer and API explorer. It ships one canonical browser renderer and thin ecosystem adapters so supported backends expose the same documentation, Try It, API Client, Runtime Intelligence, and backend-produced Contract Validation behavior where the host can genuinely observe it. +FlexDoc runs inside the backend and reads the routes the framework actually registered. **On Node today,** it compares that runtime surface with OpenAPI, fails `flexdoc validate` on unacknowledged disagreement, and lets you open an undocumented runtime route directly in the built-in API Client. -No FlexDoc account, hosted dashboard, telemetry service, or runtime CDN is required. +It also provides self-hosted API documentation, exploration, and execution. No FlexDoc account or control plane required. + +Against the Express example, after its demo login: + +```text +$ npx @prauga/flexdoc-cli validate \ + http://localhost:3000/docs/__flexdoc/runtime \ + --header 'Cookie: flexdoc-example-session=demo' + +FlexDoc contract validation: FAIL +1 error, 0 warnings, 1 info · discovery complete + +[INFO] runtime.operation-undocumented · GET /internal/health · acknowledged +Runtime implements GET /internal/health, but OpenAPI does not document that operation. + +[ERROR] runtime.operation-undocumented · POST /internal/reindex +Runtime implements POST /internal/reindex, but OpenAPI does not document that operation. +``` + +```text +Open FlexDoc + ↓ +Runtime + ↓ +POST /internal/reindex + ↓ +Contract: OpenAPI does not declare this operation +Runtime: POST /internal/reindex is registered + ↓ +Open in API Client +``` + +**[Try the disagreement loop with Express](./examples/javascript-express).** That example is the evaluation path. `GET /internal/health` is runtime-only and acknowledged, so it does not fail validation. `POST /internal/reindex` is runtime-only and not acknowledged, so the check fails and the finding opens the route. + +Once FlexDoc finds the disagreement, the same screen already has the documentation and API Client you need to investigate it. The package matrix and framework list below are the rest of the surface. ## Published packages @@ -57,7 +90,7 @@ No FlexDoc account, hosted dashboard, telemetry service, or runtime CDN is requi - **Rust:** Axum, Actix Web - **Elixir:** Plug, Phoenix -The backend-coverage program shipped in 2.3.0 and the coordinated 2.x line culminated in published **2.9.9**. Stable **3.0.0** added backend-native Runtime Intelligence, published **3.1.0** added operation-level Contract Validation on Node Express/Fastify/Hono/NestJS with matching renderer and CLI consumption, and **3.2.0** took the canonical API Client request/script/collection model into headless CLI/CI execution while reusing the existing advertised host-execution contract. **3.3.0** expands hardened native API-host execution across the supported backend ecosystems and makes an available API host the default transport for ordinary interactive API Client sends. See [`docs/releases/3.3.md`](./docs/releases/3.3.md) for the security and operational release notes and [`CHANGELOG.md`](./CHANGELOG.md) for published product releases. +**3.5.0** is the current JavaScript release. On Node, a route the framework registered and OpenAPI does not document fails `flexdoc validate` when discovery is complete, and that finding opens in the API Client. See [`docs/releases/3.5.0.md`](./docs/releases/3.5.0.md). Earlier releases added Runtime Intelligence (**3.0.0**), Node contract validation (**3.1.0**), the headless Runner (**3.2.0**), and native API-host execution (**3.3.0**). [`CHANGELOG.md`](./CHANGELOG.md) records the published line. ## CLI @@ -72,7 +105,7 @@ npx @prauga/flexdoc-cli validate http://127.0.0.1:3000/docs/__flexdoc/runtime node tools/flexdoc-cli/bin/flexdoc.js run ./pets.flexdoc.json --json ``` -`validate` consumes the installed Node backend's structured 3.1 validation result rather than reimplementing contract comparison. It supports JSON output, custom headers, bearer/basic authentication for protected Runtime Intelligence endpoints, and opt-in stricter CI failure policies such as `--fail-on warning`. By default it exits `1` only when the backend reports `fail` (or the endpoint/payload cannot be consumed). +`validate` reads the Node backend's validation result. When discovery is complete, an undocumented runtime route is an error, the output names the method and path, and the command exits `1`. An acknowledged route stays in that output as info and does not fail the check. Partial discovery keeps an unacknowledged undocumented route a warning. The command also accepts JSON output, custom headers, and bearer or basic authentication for a protected runtime endpoint. `--fail-on warning` or `--fail-on info` makes the exit stricter. `run` consumes a versioned artifact exported from the canonical API Client workspace and delegates to the same collection/request executor and `flex.*` script/test runtime. Ordinary reusable/headless requests execute directly from Node unless host semantics are explicitly selected or required. See [`docs/headless-runner.md`](./docs/headless-runner.md) for the artifact, security, reporting, and cancellation contract. @@ -97,7 +130,7 @@ node tools/flexdoc-cli/bin/flexdoc.js run ./pets.flexdoc.json --json | Hex | `prauga_flexdoc` | `0.5.0` | | Go | `github.com/prauga/flexdoc/adapters/go` | `0.6.0` | -Ecosystem package versions are intentionally independent. FlexDoc 3.3.0 is the coordinated product/source release; native adapters retain their established ecosystem semver histories. Renderer contract v1 remains the cross-language compatibility boundary. +Ecosystem package versions are intentionally independent. Client, backend, and CLI **3.5.0** / **0.8.0** publish the Node disagreement loop. Native adapters keep their own version lines. Renderer contract v1 remains the cross-language compatibility boundary. > The package table reflects the versions encoded by the current source commit. Release-preparation commits update these source versions only when the matching release is ready; source version numbers alone do not mean an artifact has been published. @@ -126,6 +159,6 @@ FlexDoc follows the [Contributor Covenant](./CODE_OF_CONDUCT.md). Bug reports an ## License -FlexDoc is licensed under **Apache-2.0**. See [LICENSE](./LICENSE). +FlexDoc is licensed under **Apache-2.0**. See [LICENSE](./LICENSE). Copyright 2025 Vishnu R is recorded in [NOTICE](./NOTICE). The renderer, API Client, headless runner, CLI and every framework adapter are permissively licensed and stay that way — you can embed them in a proprietary application without obligation. Any future hosted or fleet control-plane product is separate code and will carry its own license; nothing in this repository depends on it. \ No newline at end of file diff --git a/examples/README.md b/examples/README.md index fbe2ec47..5cc6a3ff 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,6 +1,8 @@ # FlexDoc examples -These examples are the executable capability map for the current FlexDoc 3.3 release. Registry-consuming manifests and generated locks are pinned to the published package release versions listed below so clean standalone installs remain reproducible. Repository CI may substitute packages built from the current commit when validating source changes. +Start with [Try the disagreement loop with Express](./javascript-express). That service registers two routes OpenAPI does not document. `GET /internal/health` is acknowledged and does not fail validation. `POST /internal/reindex` is not acknowledged, so `flexdoc validate` fails and the finding opens in the API Client. + +The table is the rest of the integrations. Registry-consuming manifests and generated locks are pinned to the published package versions listed below. Repository CI may substitute packages built from the current commit when validating source changes. The examples deliberately separate two kinds of capability: @@ -13,7 +15,7 @@ The examples deliberately separate two kinds of capability: | [`interactive-demo`](./interactive-demo) | React + `@prauga/flexdoc-client` `3.5.0` | **Complete framework-neutral renderer**: deep links, preferences, command palette, Try It → API Client, downloads, code samples, responsive/a11y chrome | — client only | | [`api-client`](./api-client) | Full API Client + `@prauga/flexdoc-client` `3.5.0` | **Complete standalone API Client**: environments, variables, CodeMirror scripts, runner/history, response inspection, persistence, shortcuts | — client only | | [`nestjs`](./nestjs) | NestJS + `@prauga/flexdoc-backend` `3.5.0` | Code-first OpenAPI + full renderer/API Client | **Yes — underlying Express/Fastify adapter** | -| [`javascript-express`](./javascript-express) | Express + `@prauga/flexdoc-backend` `3.5.0` | Primary backend-native showcase with intentional route drift | **Yes — live Express router** | +| [`javascript-express`](./javascript-express) | Express + `@prauga/flexdoc-backend` `3.5.0` | Evaluation path: acknowledged `GET /internal/health` passes; `POST /internal/reindex` fails and opens in the API Client | **Yes — live Express router** | | [`javascript-fastify`](./javascript-fastify) | Fastify + `@prauga/flexdoc-backend` `3.5.0` | Shared OpenAPI showcase + intentional runtime-only route | **Yes — live Fastify route tree** | | [`javascript-hono`](./javascript-hono) | Hono + `@prauga/flexdoc-backend` `3.5.0` | Small inline contract + intentional runtime-only route | **Yes — live Hono route inventory** | | [`javascript-fleet`](./javascript-fleet) | Express fleet + `@prauga/flexdoc-backend` `3.5.0` | Multi-instance host execution: shared secret, Redis jar store, fleet admission, observation merge | **Yes — live Express router** | @@ -41,7 +43,7 @@ The examples deliberately separate two kinds of capability: ## What to run for the current surface -Start with `javascript-express` when evaluating FlexDoc as a product: it demonstrates the full documentation/API Client experience **and** the backend-native distinction, including a route that exists in the running application but not in OpenAPI. Use `javascript-fleet` when you need a horizontally scaled host-execution deployment with a shared session jar and fleet observation merge. Use `interactive-demo` for the framework-neutral renderer surface and `api-client` for focused API-development workflows. +Use [`javascript-express`](./javascript-express) to evaluate the product. The other examples are additional integrations: `javascript-fleet` for a horizontally scaled host-execution deployment, `interactive-demo` for the framework-neutral renderer, and `api-client` for the API Client on its own. Runtime-capable examples intentionally include either a runtime-only route or a live controller/router inventory so the Runtime panel demonstrates evidence from the running backend rather than repeating the specification. diff --git a/examples/javascript-express/README.md b/examples/javascript-express/README.md index 3bc9c250..d085e345 100644 --- a/examples/javascript-express/README.md +++ b/examples/javascript-express/README.md @@ -1,16 +1,27 @@ -# Express + FlexDoc 3.1 +# Try the disagreement loop with Express -This is the primary backend-native FlexDoc 3.1 showcase. Express serves the shared OpenAPI 3.1 contract, while FlexDoc runs inside the same backend and exposes the completed documentation/API Client surface plus live Runtime Intelligence. +This is the evaluation path. Express registers two routes the OpenAPI document does not describe. -`GET /internal/health` is registered and listed in `runtimeIntelligence.acknowledgedUndocumented`. It stays a runtime-only route, the finding is informational, and it does not fail validation. `POST /internal/reindex` is also registered and is not acknowledged, so contract validation fails. Open **Runtime**, then that finding, to see the registered route beside the missing contract and send it from the API Client. +| Route | What FlexDoc records | Validation | +| --- | --- | --- | +| `GET /internal/health` | Runtime-only, acknowledged | Passes. The finding stays informational. | +| `POST /internal/reindex` | Runtime-only, not acknowledged | Fails. The finding names the method and path. | -The renderer also demonstrates the completed documentation/API Client surface: persisted viewer preferences, deep links, keyboard command palette, mobile navigation, JSON/YAML download, Basic/Advanced Try It, handoff to the sibling API Client page, environments, scripts, the CodeMirror-backed editor, response inspection, collection/history workflows, and code samples. +Open **Runtime**, then `POST /internal/reindex`. The page shows that OpenAPI does not declare the operation beside the route Express registered. **Open in API Client** sends that method and path. ```bash npm install npm start ``` -Open `http://localhost:3000/docs`. +Open `http://localhost:3000/docs/example-login` first. That sets the demo cookie for `/docs`. Then open `http://localhost:3000/docs`. -The standalone FlexDoc dependency is pinned to `3.3.0`, the current published release, for reproducible installs. Repository CI replaces it with the backend package built from the current commit when validating source changes. +```bash +npx @prauga/flexdoc-cli validate \ + http://localhost:3000/docs/__flexdoc/runtime \ + --header 'Cookie: flexdoc-example-session=demo' +``` + +The command prints the acknowledged health route and exits `1` on `POST /internal/reindex`. + +The same page also includes the documentation and API Client surface: Try It, code samples, environments, and scripts. The standalone dependency is pinned to published `@prauga/flexdoc-backend` `3.5.0`. Repository CI replaces it with the backend package built from the current commit. diff --git a/examples/javascript-express/index.js b/examples/javascript-express/index.js index 4f6e4482..2c8c6178 100644 --- a/examples/javascript-express/index.js +++ b/examples/javascript-express/index.js @@ -14,7 +14,7 @@ function hasDemoDocsSession(req) { function requireDocsSession(req, res, next) { if (!hasDemoDocsSession(req)) { - res.status(401).json({ error: 'Open /example-login before using the protected FlexDoc example.' }); + res.status(401).json({ error: 'Open /docs/example-login before using the protected FlexDoc example.' }); return; } next(); @@ -35,7 +35,7 @@ function buildApp() { app.use(express.json()); app.use(express.urlencoded({ extended: true })); - app.get('/example-login', (_req, res) => { + app.get('/docs/example-login', (_req, res) => { res.cookie('flexdoc-example-session', 'demo', { httpOnly: true, sameSite: 'strict', @@ -75,9 +75,9 @@ function buildApp() { setupExpressFlexDoc(app, '/docs', { spec, options: { - title: 'FlexDoc Express 3.3 showcase', - description: 'OpenAPI 3.1 documentation, API Client workflows, native host execution, and live Express Runtime Intelligence in one backend-native example.', - version: '3.3.0', + title: 'FlexDoc Express disagreement loop', + description: 'GET /internal/health is acknowledged. POST /internal/reindex is registered by Express and absent from OpenAPI.', + version: '3.5.0', favicon: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"%3E%3Crect width="32" height="32" rx="8" fill="%237c3aed"/%3E%3Ctext x="8" y="22" fill="white" font-size="18"%3EF%3C/text%3E%3C/svg%3E', customCss: '.flexdoc-root { --express-showcase: 1; }', customJs: 'document.documentElement.dataset.flexdocExample="express";', @@ -99,7 +99,7 @@ function buildApp() { hostExecution: { enabled: true, allowedOrigins: ['http://localhost:3000'] }, }, codeSamples: { enabled: true, languages: ['curl', 'javascript', 'python', 'go', 'java'] }, - footer: { copyright: 'Prauga FlexDoc 3.3 showcase', link: [{ text: 'Repository', url: 'https://github.com/prauga/flexdoc' }] }, + footer: { copyright: 'Prauga FlexDoc', link: [{ text: 'Repository', url: 'https://github.com/prauga/flexdoc' }] }, }, }); @@ -107,7 +107,7 @@ function buildApp() { } if (require.main === module) { - buildApp().listen(3000, () => console.log('Login: http://localhost:3000/example-login\nAPI: http://localhost:3000/pets\nDocs: http://localhost:3000/docs\nGET /internal/health is acknowledged. POST /internal/reindex is undocumented and fails validation.')); + buildApp().listen(3000, () => console.log('Login: http://localhost:3000/docs/example-login\nAPI: http://localhost:3000/pets\nDocs: http://localhost:3000/docs\nGET /internal/health is acknowledged. POST /internal/reindex is undocumented and fails validation.')); } module.exports = { buildApp }; diff --git a/examples/javascript-fastify/README.md b/examples/javascript-fastify/README.md index 000f11ff..98bc2fb8 100644 --- a/examples/javascript-fastify/README.md +++ b/examples/javascript-fastify/README.md @@ -11,4 +11,4 @@ npm start Open `http://localhost:3000/docs`. -The standalone FlexDoc dependency is pinned to `3.3.0`, the current published release, for reproducible installs. Repository CI substitutes the backend package built from the same commit when validating source changes. +The standalone FlexDoc dependency is pinned to published `@prauga/flexdoc-backend` `3.5.0`. Repository CI substitutes the backend package built from the same commit when validating source changes. diff --git a/examples/javascript-hono/README.md b/examples/javascript-hono/README.md index 4cbdabb2..3eea1c17 100644 --- a/examples/javascript-hono/README.md +++ b/examples/javascript-hono/README.md @@ -1,6 +1,6 @@ # Hono + FlexDoc 3.1 -This example uses Hono `4.13.x` with the optional `setupHonoFlexDoc` integration. Its registry dependency is pinned to published `@prauga/flexdoc-backend` `3.3.0` for reproducible standalone installs, while repository CI validates against the backend package built from the current commit. Runtime Intelligence reads Hono's actual registered routes instead of inferring them from the OpenAPI document. +This example uses Hono `4.13.x` with the optional `setupHonoFlexDoc` integration. Its registry dependency is pinned to published `@prauga/flexdoc-backend` `3.5.0` for reproducible standalone installs, while repository CI validates against the backend package built from the current commit. Runtime Intelligence reads Hono's actual registered routes instead of inferring them from the OpenAPI document. The contract documents `/health` and `/pets/{petId}`. `GET /internal/health` is listed in `runtimeIntelligence.acknowledgedUndocumented`, so that finding is informational. `POST /internal/reindex` is registered only in Hono and is not acknowledged, so contract validation fails. The docs surface also enables Basic/Advanced Try It, handoff to the sibling API Client page, persisted workspace state and code samples.