From 98d1ff4afca6fcb531e66c7cd889302fcc7cf9e0 Mon Sep 17 00:00:00 2001 From: Albrecht Degering Date: Wed, 2 Sep 2026 10:18:45 +0200 Subject: [PATCH] Release API Tools 0.2.0 --- .gitea/workflows/verify.yml | 39 ++ CHANGELOG.md | 6 + README.md | 38 +- SOURCE.md | 4 +- docs/ARCHITECTURE.md | 24 +- docs/PRIVACY-SECURITY.md | 19 +- package-lock.json | 40 +- package.json | 10 +- playwright.config.ts | 22 +- public/CHANGELOG.md | 6 + public/LICENSES/npm-runtime-licenses.txt | 6 +- public/README.md | 38 +- public/SOURCE.md | 4 +- public/docs/ARCHITECTURE.md | 24 +- public/docs/PRIVACY-SECURITY.md | 19 +- public/sw.js | 2 +- public/toolbox-app.json | 15 +- src/components/HelpDialog.tsx | 11 +- src/components/Workbench.tsx | 285 +++++++- src/core/asyncapi.ts | 365 ++++++++++ src/core/compare.ts | 334 ++++++++- src/core/contract.ts | 809 ++++++++++++++++++++++ src/core/har.ts | 3 + src/core/operations.ts | 75 +- src/core/parse.ts | 60 +- src/core/refs.ts | 2 +- src/core/types.ts | 1 + src/styles.css | 17 + src/toolbox/manifest.source.json | 32 +- src/version.ts | 2 +- tests/browser/app.spec.ts | 4 +- tests/browser/responsive.spec.ts | 18 + tests/components/workbench.test.tsx | 36 +- tests/core/operations-compare-har.test.ts | 172 ++++- tests/core/parse-refs.test.ts | 88 ++- 35 files changed, 2483 insertions(+), 147 deletions(-) create mode 100644 .gitea/workflows/verify.yml create mode 100644 src/core/asyncapi.ts create mode 100644 src/core/contract.ts create mode 100644 tests/browser/responsive.spec.ts diff --git a/.gitea/workflows/verify.yml b/.gitea/workflows/verify.yml new file mode 100644 index 0000000..b84a7cb --- /dev/null +++ b/.gitea/workflows/verify.yml @@ -0,0 +1,39 @@ +name: Verify + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +concurrency: + group: verify-${{ gitea.repository }}-${{ gitea.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-latest + timeout-minutes: 45 + env: + CI: "true" + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "22" + cache: npm + - name: Select declared npm version + run: npm install --global npm@11.17.0 + - name: Install dependencies + run: npm ci + - name: Audit runtime dependencies + run: npm audit --omit=dev --audit-level=moderate + - name: Check, test, and build + run: npm run check + - name: Install browser engines + run: npx playwright install --with-deps chromium firefox webkit + - name: Browser tests + run: npm run test:browser diff --git a/CHANGELOG.md b/CHANGELOG.md index 15fdbd8..2429b54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.2.0 - 2026-09-02 + +- Added OpenAPI 3.1/3.2 webhook receiver inventory, inert placeholder examples, + and operation-level revision comparison without treating webhooks as captured + client requests. + ## 0.1.0 - 2026-09-01 - Initial local-only OpenAPI 3.0/3.1 inspection release. diff --git a/README.md b/README.md index e7f2225..0ff7aa7 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,17 @@ # API Tools -API Tools is a local-first OpenAPI workbench. Version 0.1.0 parses bounded JSON -or YAML OpenAPI 3.0/3.1 descriptions, navigates operations, resolves `$ref` -values from explicitly supplied local files, inventories security schemes, -generates schema/request/response examples and inert client command text, -compares revisions, and summarizes saved HTTP exchanges or HAR 1.2 files. +API Tools is a local-first HTTP and event API workbench. It parses bounded JSON +or YAML OpenAPI 3.0–3.2 and AsyncAPI 2.0–3.1 descriptions, navigates path and +webhook receiver operations, channels, and messages, resolves `$ref` values +from explicitly supplied local files, inventories security schemes, generates +focused schema examples, compares revisions, and checks saved HAR 1.2 traffic +against a loaded OpenAPI contract while keeping request and broker execution +disabled. -It never executes HTTP, follows a URL, resolves a remote reference, stores a -credential or loads an external asset. Generated curl, Fetch and Python snippets -are text for review and may reproduce untrusted description values safely -quoted for their target syntax. +It never executes HTTP, connects or subscribes to a broker, follows a URL, +resolves a remote reference, stores a credential, or loads an external asset. +Generated curl, Fetch and Python snippets are text for review and may reproduce +untrusted description values safely quoted for their target syntax. ## Development and release @@ -22,20 +24,26 @@ npm run test:browser npm run release:artifact ``` -The release command writes deterministic `release/api-tools-0.1.0.zip` and a +The release command writes deterministic `release/api-tools-0.2.0.zip` and a SHA-256 sidecar. Relative assets are tested at `/deep/nested/api/`. ## v0.1 boundary - JSON/YAML files up to 4 MiB each, 20 files/16 MiB per selected workspace, depth 64, 100,000 values, 50 YAML aliases, 2,000 operations; -- focused OpenAPI identity/info/paths checks and remote-reference rejection; +- focused OpenAPI identity/info/paths checks, including 3.2 `query` and + `additionalOperations` and 3.1/3.2 webhook receiver inventory/comparison, + plus AsyncAPI 2.x/3.x channel/operation/message inventories and + remote-reference rejection; - fragment/relative-file JSON Pointer resolution with missing/cycle reports; - bounded sample generation for common schema composition and scalar formats; - path/query/request/response examples and inert curl/Fetch/Python output; - security scheme inventory, conservative operation compatibility report; -- HAR or raw saved-exchange summary without rendering bodies or secrets. +- HAR coverage and focused method/path/parameter/status/media-type/JSON-body + contract checks without rendering bodies or secrets; raw exchanges retain a + summary-only mode. -This is not a complete OpenAPI/JSON Schema validator, code generator, API -client, proxy or security scanner. See the documented limitations and review -generated values before use. Licensed under GPL-3.0-or-later. +This is not a complete OpenAPI, AsyncAPI, or JSON Schema validator, code +generator, API client, broker client, proxy, or security scanner. See the +documented limitations and review generated values before use. Licensed under +GPL-3.0-or-later. diff --git a/SOURCE.md b/SOURCE.md index e936430..467b607 100644 --- a/SOURCE.md +++ b/SOURCE.md @@ -1,8 +1,8 @@ # Corresponding source -The corresponding source for API Tools 0.1.0 is available at: +The corresponding source for API Tools 0.2.0 is available at: -https://git.add-ideas.de/lotobo/api-tools/src/tag/v0.1.0 +https://git.add-ideas.de/lotobo/api-tools/src/tag/v0.2.0 Build with Node.js 22+, npm 11+, and `package-lock.json` by running `npm ci && npm run release:artifact`. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 63c274a..fd35886 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -2,11 +2,29 @@ The shell lazy-loads a React workbench. Pure core modules parse and validate JSON-like values, hold a named in-memory document map, resolve only relative -local references, collect operations, derive bounded examples, compare -operation contracts and summarize saved exchanges. No module exposes a request -executor. +local references, collect OpenAPI HTTP operations or AsyncAPI channels, +send/receive operations and messages, derive bounded examples, compare +operation contracts and summarize saved exchanges. OpenAPI 3.2 fixed `query` +and bounded custom `additionalOperations` method tokens are retained. OpenAPI +3.1/3.2 webhook Path Items are inventoried and compared as receiver operations; +their names do not declare real delivery URLs, so examples use an explicit +placeholder and HAR matching excludes them. AsyncAPI 3.x root +operation/channel/message references and the older 2.x +publish/subscribe channel shape have separate, explicit adapters. HAR validation matches +captured URLs to the most specific path template, checks required parameters, +status and media declarations, and applies a bounded JSON Schema subset to +available JSON bodies. It emits only locations and messages: captured header +and body values never enter the report. No module exposes a request executor. JSON uses hardened shared helpers. YAML is converted with bounded alias count and then recursively checked for depth, node count, dangerous keys and plain JSON values. Rendering uses React text nodes and read-only textareas. The same-origin service worker caches only packaged application resources. + +Contract checking is limited to 2,000 exchanges, 100,000 schema evaluation +steps, 5,000 diagnostics, 2 MiB per decoded JSON body, local references, and a +focused type/composition/object/array/scalar subset. It is evidence and coverage +analysis rather than a claim of complete OpenAPI conformance. AsyncAPI bindings, +traits, correlation expressions and multi-format schemas are inventoried as +inert data; only JSON-Schema-like payload/header objects receive heuristic +sample generation. diff --git a/docs/PRIVACY-SECURITY.md b/docs/PRIVACY-SECURITY.md index c99e29b..e661ca4 100644 --- a/docs/PRIVACY-SECURITY.md +++ b/docs/PRIVACY-SECURITY.md @@ -1,12 +1,17 @@ # Privacy and security Descriptions and exchanges remain in memory and are never transmitted or -persisted. All remote/absolute `$ref` forms fail closed. Displayed URLs are text, -not links. There is no Try It button, OAuth flow, DNS lookup, HTTP execution, -telemetry or external asset. +persisted. All remote/absolute `$ref` forms fail closed. Displayed URLs, broker +hosts, channel addresses, protocol bindings, and generated commands are text, +not live controls. There is no Try It button, broker connection, subscription, +publish action, OAuth flow, DNS lookup, HTTP execution, telemetry, or external +asset. HAR and saved exchanges commonly contain tokens, cookies, personal data and -payloads. The summary does not render header values, yet the source editor still -contains them. Clear it before sharing. Generated examples are heuristic and -must not be treated as valid production data. Validation and compatibility -checks cover a useful subset, not every OpenAPI or JSON Schema rule. +payloads. Header and cookie values may be used transiently to check parameter +presence and shape, and JSON bodies may be parsed for local schema checks, but +the summary and contract report never retain or render those values. The source +editor still contains the original capture; clear it before sharing. Generated +examples are heuristic and must not be treated as valid production data. +Validation and compatibility checks cover a useful bounded subset, not every +OpenAPI or JSON Schema rule. diff --git a/package-lock.json b/package-lock.json index 1497c82..d8f951e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,23 @@ { "name": "api-tools", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "api-tools", - "version": "0.1.0", + "version": "0.2.0", "license": "GPL-3.0-or-later", "dependencies": { - "@add-ideas/toolbox-contract": "0.2.3", - "@add-ideas/toolbox-helpers": "0.1.0", - "@add-ideas/toolbox-shell-react": "0.2.3", + "@add-ideas/toolbox-contract": "0.3.0", + "@add-ideas/toolbox-helpers": "0.2.0", + "@add-ideas/toolbox-shell-react": "0.3.0", "react": "19.2.8", "react-dom": "19.2.8", "yaml": "2.9.0" }, "devDependencies": { - "@add-ideas/toolbox-testkit": "0.2.3", + "@add-ideas/toolbox-testkit": "0.3.0", "@eslint/js": "10.0.1", "@playwright/test": "1.62.1", "@testing-library/jest-dom": "6.9.1", @@ -43,24 +43,24 @@ } }, "node_modules/@add-ideas/toolbox-contract": { - "version": "0.2.3", - "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-contract/-/0.2.3/toolbox-contract-0.2.3.tgz", - "integrity": "sha512-T0PVSuMT40GjTDfQJhEEY3ZawQq8zz1/ry95JdKI6W39CdLacaRXdGnEpDCMHt+jUbf1Jz7Nat/M5dFCgKVM9A==", + "version": "0.3.0", + "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-contract/-/0.3.0/toolbox-contract-0.3.0.tgz", + "integrity": "sha512-dKrK7BjOFwqJaBfJuhKxZKIld4sH0AKjEn6a0yLnbdMUFY+fFv4VSLGV2tNSBD016gumc2iNqOjUj/ld7x4rtA==", "license": "Apache-2.0" }, "node_modules/@add-ideas/toolbox-helpers": { - "version": "0.1.0", - "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-helpers/-/0.1.0/toolbox-helpers-0.1.0.tgz", - "integrity": "sha512-UKl1Oxekedf8D2df86VrnVA53AcMhrnh6iUPXY+k8frirBXotb0yd8SGT+IF/3hcqYwcYe/v9WVFuSgKtIYVnw==", + "version": "0.2.0", + "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-helpers/-/0.2.0/toolbox-helpers-0.2.0.tgz", + "integrity": "sha512-SdOqkw+P+3J3fa5iVkzb5P15rVepB001GNV21Oh8w0CZcVL+YRltgD/s+MVcTyrNijWQf3E5vtQON/3N2LLyKg==", "license": "GPL-3.0-or-later" }, "node_modules/@add-ideas/toolbox-shell-react": { - "version": "0.2.3", - "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-shell-react/-/0.2.3/toolbox-shell-react-0.2.3.tgz", - "integrity": "sha512-DT5lQDH48BFkFcmFLZnQh7+Cm73JzBPcmp5WzUXypfkUXpEyDYHzaXgmW4kZ0edSwh4RK4sPmx+JPtK0X4aKCQ==", + "version": "0.3.0", + "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-shell-react/-/0.3.0/toolbox-shell-react-0.3.0.tgz", + "integrity": "sha512-74p6JzAOG0YCAKdlc1hLofV4ZIko7vb448S75cIiM88PKm93EHl5VD7g8YVyfM56Ui97UY9dmy+Whiq4sGzpsg==", "license": "Apache-2.0", "dependencies": { - "@add-ideas/toolbox-contract": "0.2.3" + "@add-ideas/toolbox-contract": "0.3.0" }, "peerDependencies": { "react": ">=18 <20", @@ -68,13 +68,13 @@ } }, "node_modules/@add-ideas/toolbox-testkit": { - "version": "0.2.3", - "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-testkit/-/0.2.3/toolbox-testkit-0.2.3.tgz", - "integrity": "sha512-sq1MwhKWfFKen+N+124hl74qQimRSvmQ9sOU7jdcI+2qCKZ67+2B8rWyezeV80uTFu4Jv6deHksfYQ/tKNV6XQ==", + "version": "0.3.0", + "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-testkit/-/0.3.0/toolbox-testkit-0.3.0.tgz", + "integrity": "sha512-4Fk+oSvZFspOMIXr8Xy040nhAaBsIQAzsGyXWSpjn3+k3yBKq7nB1r5zCHhsXzfdLzvPDAx2KcmSNOhM330D9w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@add-ideas/toolbox-contract": "0.2.3" + "@add-ideas/toolbox-contract": "0.3.0" }, "bin": { "toolbox-check": "dist/cli.js" diff --git a/package.json b/package.json index 8551567..d941fd0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "api-tools", - "version": "0.1.0", + "version": "0.2.0", "description": "Inspect, compare and derive examples from OpenAPI descriptions locally in the browser.", "license": "GPL-3.0-or-later", "author": "Albrecht Degering", @@ -39,15 +39,15 @@ "release:artifact": "npm run check && npm run test:browser && npm run package:release -- --force" }, "dependencies": { - "@add-ideas/toolbox-contract": "0.2.3", - "@add-ideas/toolbox-helpers": "0.1.0", - "@add-ideas/toolbox-shell-react": "0.2.3", + "@add-ideas/toolbox-contract": "0.3.0", + "@add-ideas/toolbox-helpers": "0.2.0", + "@add-ideas/toolbox-shell-react": "0.3.0", "react": "19.2.8", "react-dom": "19.2.8", "yaml": "2.9.0" }, "devDependencies": { - "@add-ideas/toolbox-testkit": "0.2.3", + "@add-ideas/toolbox-testkit": "0.3.0", "@eslint/js": "10.0.1", "@playwright/test": "1.62.1", "@testing-library/jest-dom": "6.9.1", diff --git a/playwright.config.ts b/playwright.config.ts index 1e7b8e7..2b6681a 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -15,7 +15,25 @@ export default defineConfig({ timeout: 180_000, }, projects: [ - { name: "chromium", use: { ...devices["Desktop Chrome"] } }, - { name: "firefox", use: { ...devices["Desktop Firefox"] } }, + { + name: "chromium", + testIgnore: /responsive\.spec\.ts/, + use: { ...devices["Desktop Chrome"] }, + }, + { + name: "firefox", + testIgnore: /responsive\.spec\.ts/, + use: { ...devices["Desktop Firefox"] }, + }, + { + name: "webkit", + testIgnore: /responsive\.spec\.ts/, + use: { ...devices["Desktop Safari"] }, + }, + { + name: "mobile-chromium", + testMatch: /responsive\.spec\.ts/, + use: { ...devices["Pixel 5"] }, + }, ], }); diff --git a/public/CHANGELOG.md b/public/CHANGELOG.md index 15fdbd8..2429b54 100644 --- a/public/CHANGELOG.md +++ b/public/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.2.0 - 2026-09-02 + +- Added OpenAPI 3.1/3.2 webhook receiver inventory, inert placeholder examples, + and operation-level revision comparison without treating webhooks as captured + client requests. + ## 0.1.0 - 2026-09-01 - Initial local-only OpenAPI 3.0/3.1 inspection release. diff --git a/public/LICENSES/npm-runtime-licenses.txt b/public/LICENSES/npm-runtime-licenses.txt index 467d8fe..13b0849 100644 --- a/public/LICENSES/npm-runtime-licenses.txt +++ b/public/LICENSES/npm-runtime-licenses.txt @@ -1,5 +1,5 @@ ============================================================================== -@add-ideas/toolbox-contract@0.2.3 +@add-ideas/toolbox-contract@0.3.0 Declared licence: Apache-2.0 ============================================================================== --- LICENSE --- @@ -198,7 +198,7 @@ Declared licence: Apache-2.0 ============================================================================== -@add-ideas/toolbox-helpers@0.1.0 +@add-ideas/toolbox-helpers@0.2.0 Declared licence: GPL-3.0-or-later ============================================================================== --- LICENSE --- @@ -879,7 +879,7 @@ Public License instead of this License. But first, please read ============================================================================== -@add-ideas/toolbox-shell-react@0.2.3 +@add-ideas/toolbox-shell-react@0.3.0 Declared licence: Apache-2.0 ============================================================================== --- LICENSE --- diff --git a/public/README.md b/public/README.md index e7f2225..0ff7aa7 100644 --- a/public/README.md +++ b/public/README.md @@ -1,15 +1,17 @@ # API Tools -API Tools is a local-first OpenAPI workbench. Version 0.1.0 parses bounded JSON -or YAML OpenAPI 3.0/3.1 descriptions, navigates operations, resolves `$ref` -values from explicitly supplied local files, inventories security schemes, -generates schema/request/response examples and inert client command text, -compares revisions, and summarizes saved HTTP exchanges or HAR 1.2 files. +API Tools is a local-first HTTP and event API workbench. It parses bounded JSON +or YAML OpenAPI 3.0–3.2 and AsyncAPI 2.0–3.1 descriptions, navigates path and +webhook receiver operations, channels, and messages, resolves `$ref` values +from explicitly supplied local files, inventories security schemes, generates +focused schema examples, compares revisions, and checks saved HAR 1.2 traffic +against a loaded OpenAPI contract while keeping request and broker execution +disabled. -It never executes HTTP, follows a URL, resolves a remote reference, stores a -credential or loads an external asset. Generated curl, Fetch and Python snippets -are text for review and may reproduce untrusted description values safely -quoted for their target syntax. +It never executes HTTP, connects or subscribes to a broker, follows a URL, +resolves a remote reference, stores a credential, or loads an external asset. +Generated curl, Fetch and Python snippets are text for review and may reproduce +untrusted description values safely quoted for their target syntax. ## Development and release @@ -22,20 +24,26 @@ npm run test:browser npm run release:artifact ``` -The release command writes deterministic `release/api-tools-0.1.0.zip` and a +The release command writes deterministic `release/api-tools-0.2.0.zip` and a SHA-256 sidecar. Relative assets are tested at `/deep/nested/api/`. ## v0.1 boundary - JSON/YAML files up to 4 MiB each, 20 files/16 MiB per selected workspace, depth 64, 100,000 values, 50 YAML aliases, 2,000 operations; -- focused OpenAPI identity/info/paths checks and remote-reference rejection; +- focused OpenAPI identity/info/paths checks, including 3.2 `query` and + `additionalOperations` and 3.1/3.2 webhook receiver inventory/comparison, + plus AsyncAPI 2.x/3.x channel/operation/message inventories and + remote-reference rejection; - fragment/relative-file JSON Pointer resolution with missing/cycle reports; - bounded sample generation for common schema composition and scalar formats; - path/query/request/response examples and inert curl/Fetch/Python output; - security scheme inventory, conservative operation compatibility report; -- HAR or raw saved-exchange summary without rendering bodies or secrets. +- HAR coverage and focused method/path/parameter/status/media-type/JSON-body + contract checks without rendering bodies or secrets; raw exchanges retain a + summary-only mode. -This is not a complete OpenAPI/JSON Schema validator, code generator, API -client, proxy or security scanner. See the documented limitations and review -generated values before use. Licensed under GPL-3.0-or-later. +This is not a complete OpenAPI, AsyncAPI, or JSON Schema validator, code +generator, API client, broker client, proxy, or security scanner. See the +documented limitations and review generated values before use. Licensed under +GPL-3.0-or-later. diff --git a/public/SOURCE.md b/public/SOURCE.md index e936430..467b607 100644 --- a/public/SOURCE.md +++ b/public/SOURCE.md @@ -1,8 +1,8 @@ # Corresponding source -The corresponding source for API Tools 0.1.0 is available at: +The corresponding source for API Tools 0.2.0 is available at: -https://git.add-ideas.de/lotobo/api-tools/src/tag/v0.1.0 +https://git.add-ideas.de/lotobo/api-tools/src/tag/v0.2.0 Build with Node.js 22+, npm 11+, and `package-lock.json` by running `npm ci && npm run release:artifact`. diff --git a/public/docs/ARCHITECTURE.md b/public/docs/ARCHITECTURE.md index 63c274a..fd35886 100644 --- a/public/docs/ARCHITECTURE.md +++ b/public/docs/ARCHITECTURE.md @@ -2,11 +2,29 @@ The shell lazy-loads a React workbench. Pure core modules parse and validate JSON-like values, hold a named in-memory document map, resolve only relative -local references, collect operations, derive bounded examples, compare -operation contracts and summarize saved exchanges. No module exposes a request -executor. +local references, collect OpenAPI HTTP operations or AsyncAPI channels, +send/receive operations and messages, derive bounded examples, compare +operation contracts and summarize saved exchanges. OpenAPI 3.2 fixed `query` +and bounded custom `additionalOperations` method tokens are retained. OpenAPI +3.1/3.2 webhook Path Items are inventoried and compared as receiver operations; +their names do not declare real delivery URLs, so examples use an explicit +placeholder and HAR matching excludes them. AsyncAPI 3.x root +operation/channel/message references and the older 2.x +publish/subscribe channel shape have separate, explicit adapters. HAR validation matches +captured URLs to the most specific path template, checks required parameters, +status and media declarations, and applies a bounded JSON Schema subset to +available JSON bodies. It emits only locations and messages: captured header +and body values never enter the report. No module exposes a request executor. JSON uses hardened shared helpers. YAML is converted with bounded alias count and then recursively checked for depth, node count, dangerous keys and plain JSON values. Rendering uses React text nodes and read-only textareas. The same-origin service worker caches only packaged application resources. + +Contract checking is limited to 2,000 exchanges, 100,000 schema evaluation +steps, 5,000 diagnostics, 2 MiB per decoded JSON body, local references, and a +focused type/composition/object/array/scalar subset. It is evidence and coverage +analysis rather than a claim of complete OpenAPI conformance. AsyncAPI bindings, +traits, correlation expressions and multi-format schemas are inventoried as +inert data; only JSON-Schema-like payload/header objects receive heuristic +sample generation. diff --git a/public/docs/PRIVACY-SECURITY.md b/public/docs/PRIVACY-SECURITY.md index c99e29b..e661ca4 100644 --- a/public/docs/PRIVACY-SECURITY.md +++ b/public/docs/PRIVACY-SECURITY.md @@ -1,12 +1,17 @@ # Privacy and security Descriptions and exchanges remain in memory and are never transmitted or -persisted. All remote/absolute `$ref` forms fail closed. Displayed URLs are text, -not links. There is no Try It button, OAuth flow, DNS lookup, HTTP execution, -telemetry or external asset. +persisted. All remote/absolute `$ref` forms fail closed. Displayed URLs, broker +hosts, channel addresses, protocol bindings, and generated commands are text, +not live controls. There is no Try It button, broker connection, subscription, +publish action, OAuth flow, DNS lookup, HTTP execution, telemetry, or external +asset. HAR and saved exchanges commonly contain tokens, cookies, personal data and -payloads. The summary does not render header values, yet the source editor still -contains them. Clear it before sharing. Generated examples are heuristic and -must not be treated as valid production data. Validation and compatibility -checks cover a useful subset, not every OpenAPI or JSON Schema rule. +payloads. Header and cookie values may be used transiently to check parameter +presence and shape, and JSON bodies may be parsed for local schema checks, but +the summary and contract report never retain or render those values. The source +editor still contains the original capture; clear it before sharing. Generated +examples are heuristic and must not be treated as valid production data. +Validation and compatibility checks cover a useful bounded subset, not every +OpenAPI or JSON Schema rule. diff --git a/public/sw.js b/public/sw.js index 97ae963..dd6bcf6 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,5 +1,5 @@ const CACHE_PREFIX = "api-tools-shell-"; -const CACHE_NAME = CACHE_PREFIX + "0.1.0"; +const CACHE_NAME = CACHE_PREFIX + "0.2.0"; const CORE = [ "./", "./manifest.webmanifest", diff --git a/public/toolbox-app.json b/public/toolbox-app.json index 2408351..cc25815 100644 --- a/public/toolbox-app.json +++ b/public/toolbox-app.json @@ -3,12 +3,12 @@ "schemaVersion": 1, "id": "de.add-ideas.api-tools", "name": "API Tools", - "version": "0.1.0", - "description": "Inspect and compare API descriptions locally.", + "version": "0.2.0", + "description": "Inspect and compare HTTP and event API descriptions locally.", "entry": "./", "icon": "./favicon.svg", "categories": ["developer", "data", "network"], - "tags": ["openapi", "swagger", "har", "schema", "http"], + "tags": ["openapi", "asyncapi", "har", "schema", "http", "events"], "integration": { "contextVersion": 1, "launchModes": ["navigate", "new-tab"], @@ -21,6 +21,15 @@ "crossOriginIsolated": false, "topLevelContext": false }, + "io": { + "accepts": [ + { "mediaType": "application/json", "extensions": [".json", ".har"] }, + { "mediaType": "application/yaml", "extensions": [".yaml", ".yml"] }, + { "mediaType": "text/plain", "extensions": [".txt", ".http"] } + ], + "produces": [{ "mediaType": "application/json", "extensions": [".json"] }] + }, + "capabilities": { "required": [], "optional": ["workers"] }, "privacy": { "processing": "local", "fileUploads": true, diff --git a/src/components/HelpDialog.tsx b/src/components/HelpDialog.tsx index 7969a2a..01463b9 100644 --- a/src/components/HelpDialog.tsx +++ b/src/components/HelpDialog.tsx @@ -31,13 +31,16 @@ export function HelpDialog({

- Open JSON or YAML OpenAPI 3.0/3.1 descriptions, resolve references from - files you explicitly add, inspect operations and security schemes, + Open JSON or YAML OpenAPI 3.0–3.2 or AsyncAPI 2.0–3.1 descriptions, + resolve references from files you explicitly add, inspect path and + webhook receiver operations, channels, messages, and security schemes, generate examples, and compare revisions.

- Generated curl, Fetch and Python requests are inert text. Version 0.1 - never executes HTTP, follows URLs, or resolves remote references. + Generated curl, Fetch and Python requests are inert text. Broker, + channel, and binding information is also inert. The application never + executes HTTP, connects to a broker, follows URLs, or resolves remote + references.

Validation is a focused structural review, not full conformance diff --git a/src/components/Workbench.tsx b/src/components/Workbench.tsx index 6ea58c5..b86440f 100644 --- a/src/components/Workbench.tsx +++ b/src/components/Workbench.tsx @@ -3,7 +3,12 @@ import { stableStringify, triggerBlobDownload, } from "@add-ideas/toolbox-helpers"; -import { compareApis } from "../core/compare"; +import { collectAsyncOperations, type AsyncOperation } from "../core/asyncapi"; +import { compareApiDescriptions } from "../core/compare"; +import { + validateHarAgainstOpenApi, + type HarContractReport, +} from "../core/contract"; import { inspectHar, inspectRawExchange, @@ -14,7 +19,11 @@ import { operationExample, securityInventory, } from "../core/operations"; -import { parseApiDocument, validateOpenApi } from "../core/parse"; +import { + apiDescriptionKind, + parseApiDocument, + validateApiDescription, +} from "../core/parse"; import { createWorkspace } from "../core/refs"; import type { ApiDocument, ApiOperation } from "../core/types"; @@ -114,6 +123,7 @@ function OperationList({ {operation.path} + {operation.kind === "webhook" ? "Webhook · " : ""} {operation.summary ?? operation.operationId ?? "No summary"} @@ -124,6 +134,36 @@ function OperationList({ ); } +function AsyncOperationList({ + operations, + selected, + onSelect, +}: { + operations: AsyncOperation[]; + selected?: string; + onSelect: (key: string) => void; +}) { + return ( +

+ ); +} + export function Workbench() { const initial = useMemo(() => parseApiDocument(SAMPLE, "openapi.yaml"), []); const [source, setSource] = useState(SAMPLE); @@ -135,20 +175,38 @@ export function Workbench() { const [selectedKey, setSelectedKey] = useState("GET /books/{bookId}"); const [compareSource, setCompareSource] = useState(COMPARE_SAMPLE); const [comparison, setComparison] = useState(() => - compareApis(initial, parseApiDocument(COMPARE_SAMPLE, "comparison.yaml")), + compareApiDescriptions( + initial, + parseApiDocument(COMPARE_SAMPLE, "comparison.yaml"), + ), ); const [exchangeSource, setExchangeSource] = useState(HAR_SAMPLE); const [exchanges, setExchanges] = useState(() => inspectHar(HAR_SAMPLE), ); + const [contractReport, setContractReport] = useState< + HarContractReport | undefined + >(() => validateHarAgainstOpenApi(HAR_SAMPLE, createWorkspace(initial))); const [exchangeError, setExchangeError] = useState(); const workspace = useMemo( () => createWorkspace(entry, supporting), [entry, supporting], ); - const problems = useMemo(() => validateOpenApi(entry), [entry]); - const operations = useMemo(() => collectOperations(entry.value), [entry]); + const descriptionKind = apiDescriptionKind(entry); + const isAsyncApi = descriptionKind === "asyncapi"; + const problems = useMemo(() => validateApiDescription(entry), [entry]); + const operations = useMemo( + () => + apiDescriptionKind(entry) === "openapi" + ? collectOperations(entry.value) + : [], + [entry], + ); + const asyncOperations = useMemo( + () => (isAsyncApi ? collectAsyncOperations(workspace) : []), + [isAsyncApi, workspace], + ); const visibleOperations = useMemo(() => { const needle = query.trim().toLowerCase(); return needle @@ -165,6 +223,23 @@ export function Workbench() { const selected = operations.find((operation) => operation.key === selectedKey) ?? operations[0]; + const visibleAsyncOperations = useMemo(() => { + const needle = query.trim().toLowerCase(); + return needle + ? asyncOperations.filter((operation) => + [ + operation.key, + operation.operationId, + operation.summary, + operation.channelId, + ...operation.protocols, + ].some((value) => value?.toLowerCase().includes(needle)), + ) + : asyncOperations; + }, [asyncOperations, query]); + const selectedAsync = + asyncOperations.find((operation) => operation.key === selectedKey) ?? + asyncOperations[0]; const example = useMemo( () => (selected ? operationExample(workspace, selected) : undefined), [selected, workspace], @@ -228,7 +303,10 @@ export function Workbench() { const runComparison = () => { try { setComparison( - compareApis(entry, parseApiDocument(compareSource, "comparison.yaml")), + compareApiDescriptions( + entry, + parseApiDocument(compareSource, "comparison.yaml"), + ), ); setError(undefined); } catch (reason) { @@ -241,10 +319,14 @@ export function Workbench() { }; const inspectExchange = () => { try { - const value = /^\s*\{/u.test(exchangeSource) - ? inspectHar(exchangeSource) - : inspectRawExchange(exchangeSource); - setExchanges(value); + if (/^\s*\{/u.test(exchangeSource) && !isAsyncApi) { + const report = validateHarAgainstOpenApi(exchangeSource, workspace); + setExchanges(report.exchanges); + setContractReport(report); + } else { + setExchanges(inspectRawExchange(exchangeSource)); + setContractReport(undefined); + } setExchangeError(undefined); } catch (reason) { setExchangeError( @@ -262,9 +344,9 @@ export function Workbench() {

Offline API description laboratory

API Tools

- Navigate OpenAPI descriptions, resolve explicitly supplied local - references, derive examples and compare revisions—without sending a - request. + Navigate OpenAPI and AsyncAPI descriptions, resolve explicitly + supplied local references, derive examples and compare + revisions—without connecting to an API or broker.

Generation only @@ -312,7 +394,7 @@ export function Workbench() {