From 2a45334b6cf6484c135632c533aeb1ae90e3a5fa Mon Sep 17 00:00:00 2001 From: Josh Corbett Date: Tue, 29 Sep 2026 18:47:06 -0600 Subject: [PATCH] ci(frontend): retire legacy gates and share contract tooling Run UI quality checks on webv2 and generate contracts with an independent locked API package. Retain legacy type imports and bundle packaging; update generation commands, tests and ownership docs. --- .github/workflows/frontend-checks.yml | 57 ---- .github/workflows/frontend-tests.yml | 37 --- .github/workflows/openapi-checks.yml | 11 +- .github/workflows/typegen-checks.yml | 21 +- AGENTS.md | 5 +- Makefile | 11 +- .../contributing/new-model-integration.mdx | 8 +- .../docs/development/Front End/index.md | 9 +- .../development/Process/release-process.mdx | 11 +- invokeai/app/AGENTS.md | 2 +- invokeai/frontend/api/.gitignore | 1 + invokeai/frontend/api/.prettierignore | 3 + invokeai/frontend/api/.prettierrc.json | 7 + invokeai/frontend/api/AGENTS.md | 9 + invokeai/frontend/api/README.md | 16 ++ invokeai/frontend/api/package.json | 21 ++ invokeai/frontend/api/pnpm-lock.yaml | 270 ++++++++++++++++++ invokeai/frontend/api/scripts/typegen.js | 2 +- .../frontend/api/scripts/typegen.test.mjs | 78 +++++ invokeai/frontend/webv1/.prettierignore | 1 - invokeai/frontend/webv1/AGENTS.md | 5 +- invokeai/frontend/webv1/eslint.config.mjs | 1 - invokeai/frontend/webv1/knip.ts | 1 - invokeai/frontend/webv1/package.json | 2 - invokeai/frontend/webv1/pnpm-lock.yaml | 186 ++---------- .../frontend/webv1/src/services/api/README.md | 13 +- .../src/services/api/endpoints/images.test.ts | 4 +- .../frontend/webv1/src/services/api/schema.ts | 1 + invokeai/frontend/webv2/AGENTS.md | 2 + .../architecture/architectureWorkflow.test.ts | 16 +- pyproject.toml | 3 +- scripts/build_wheel.sh | 2 +- 32 files changed, 494 insertions(+), 322 deletions(-) create mode 100644 invokeai/frontend/api/.gitignore create mode 100644 invokeai/frontend/api/.prettierignore create mode 100644 invokeai/frontend/api/.prettierrc.json create mode 100644 invokeai/frontend/api/AGENTS.md create mode 100644 invokeai/frontend/api/README.md create mode 100644 invokeai/frontend/api/package.json create mode 100644 invokeai/frontend/api/pnpm-lock.yaml create mode 100644 invokeai/frontend/api/scripts/typegen.test.mjs create mode 100644 invokeai/frontend/webv1/src/services/api/schema.ts diff --git a/.github/workflows/frontend-checks.yml b/.github/workflows/frontend-checks.yml index 62c43a897c..bb00170406 100644 --- a/.github/workflows/frontend-checks.yml +++ b/.github/workflows/frontend-checks.yml @@ -40,63 +40,6 @@ concurrency: cancel-in-progress: true jobs: - frontend-checks: - runs-on: ubuntu-latest - timeout-minutes: 10 # expected run time: <2 min - defaults: - run: - working-directory: invokeai/frontend/webv1 - steps: - - uses: actions/checkout@v6 - - - name: check for changed frontend files - if: ${{ inputs.always_run != true }} - id: changed-files - # Pinned to the _hash_ for v45.0.9 to prevent supply-chain attacks. - # See: - # - CVE-2025-30066 - # - https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised - # - https://github.com/tj-actions/changed-files/issues/2463 - uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 - with: - files_yaml: | - frontend: - - '.github/workflows/frontend-checks.yml' - - '.nvmrc' - - '.github/actions/install-frontend-deps/**' - - 'invokeai/frontend/webv1/**' - - - name: install dependencies - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} - uses: ./.github/actions/install-frontend-deps - with: - working-directory: invokeai/frontend/webv1 - - - name: tsc - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} - run: 'pnpm lint:tsc' - shell: bash - - - name: dpdm - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} - run: 'pnpm lint:dpdm' - shell: bash - - - name: eslint - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} - run: 'pnpm lint:eslint' - shell: bash - - - name: prettier - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} - run: 'pnpm lint:prettier' - shell: bash - - - name: knip - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} - run: 'pnpm lint:knip' - shell: bash - frontend-webv2-checks: runs-on: ubuntu-latest timeout-minutes: 10 # expected run time: <2 min diff --git a/.github/workflows/frontend-tests.yml b/.github/workflows/frontend-tests.yml index c524306072..347b2d4d30 100644 --- a/.github/workflows/frontend-tests.yml +++ b/.github/workflows/frontend-tests.yml @@ -40,43 +40,6 @@ concurrency: cancel-in-progress: true jobs: - frontend-tests: - runs-on: ubuntu-latest - timeout-minutes: 10 # expected run time: <2 min - defaults: - run: - working-directory: invokeai/frontend/webv1 - steps: - - uses: actions/checkout@v6 - - - name: check for changed frontend files - if: ${{ inputs.always_run != true }} - id: changed-files - # Pinned to the _hash_ for v45.0.9 to prevent supply-chain attacks. - # See: - # - CVE-2025-30066 - # - https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised - # - https://github.com/tj-actions/changed-files/issues/2463 - uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 - with: - files_yaml: | - frontend: - - '.github/workflows/frontend-tests.yml' - - '.nvmrc' - - '.github/actions/install-frontend-deps/**' - - 'invokeai/frontend/webv1/**' - - - name: install dependencies - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} - uses: ./.github/actions/install-frontend-deps - with: - working-directory: invokeai/frontend/webv1 - - - name: vitest - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} - run: 'pnpm test:no-watch' - shell: bash - frontend-webv2-tests: runs-on: ubuntu-latest timeout-minutes: 20 # Includes browser suites, performance sampling, project files, and accessibility. diff --git a/.github/workflows/openapi-checks.yml b/.github/workflows/openapi-checks.yml index 591012ce24..e4299cf57c 100644 --- a/.github/workflows/openapi-checks.yml +++ b/.github/workflows/openapi-checks.yml @@ -83,7 +83,8 @@ jobs: - '.github/actions/install-frontend-deps/**' - 'scripts/generate_openapi_schema.py' - 'invokeai/**' - # The schema is generated from Python and compared against legacy web's artifacts. + # Shared contracts are generated from Python; neither UI package owns these artifacts. + - '!invokeai/frontend/webv1/**' - '!invokeai/frontend/webv2/**' - name: setup uv @@ -110,22 +111,22 @@ jobs: if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} uses: ./.github/actions/install-frontend-deps with: - working-directory: invokeai/frontend/webv1 + working-directory: invokeai/frontend/api - name: copy schema if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} - run: cp invokeai/frontend/webv1/openapi.json invokeai/frontend/webv1/openapi_orig.json + run: cp invokeai/frontend/api/openapi.json invokeai/frontend/api/openapi_orig.json shell: bash - name: generate schema if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} - run: cd invokeai/frontend/webv1 && uv run ../../../scripts/generate_openapi_schema.py > openapi.json && pnpm prettier --write openapi.json + run: cd invokeai/frontend/api && uv run ../../../scripts/generate_openapi_schema.py > openapi.json && pnpm format:openapi shell: bash - name: compare files if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} run: | - if ! diff invokeai/frontend/webv1/openapi.json invokeai/frontend/webv1/openapi_orig.json; then + if ! diff invokeai/frontend/api/openapi.json invokeai/frontend/api/openapi_orig.json; then echo "Files are different!"; exit 1; fi diff --git a/.github/workflows/typegen-checks.yml b/.github/workflows/typegen-checks.yml index fae6925b8c..89227660fd 100644 --- a/.github/workflows/typegen-checks.yml +++ b/.github/workflows/typegen-checks.yml @@ -83,7 +83,8 @@ jobs: - '.github/actions/install-frontend-deps/**' - 'scripts/generate_openapi_schema.py' - 'invokeai/**' - # The schema is generated from Python and compared against legacy web's artifacts. + # Shared contracts are generated from Python; neither UI package owns these artifacts. + - '!invokeai/frontend/webv1/**' - '!invokeai/frontend/webv2/**' - name: setup uv @@ -110,22 +111,32 @@ jobs: if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} uses: ./.github/actions/install-frontend-deps with: - working-directory: invokeai/frontend/webv1 + working-directory: invokeai/frontend/api + + - name: lint contract package + if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} + run: pnpm -C invokeai/frontend/api lint + shell: bash + + - name: test contract generation + if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} + run: pnpm -C invokeai/frontend/api test + shell: bash - name: copy schema if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} - run: cp invokeai/frontend/webv1/src/services/api/schema.ts invokeai/frontend/webv1/src/services/api/schema_orig.ts + run: cp invokeai/frontend/api/schema.ts invokeai/frontend/api/schema_orig.ts shell: bash - name: generate schema if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} - run: set -o pipefail; cd invokeai/frontend/webv1 && uv run ../../../scripts/generate_openapi_schema.py | pnpm typegen + run: set -o pipefail; cd invokeai/frontend/api && uv run ../../../scripts/generate_openapi_schema.py | pnpm typegen shell: bash - name: compare files if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} run: | - if ! diff invokeai/frontend/webv1/src/services/api/schema.ts invokeai/frontend/webv1/src/services/api/schema_orig.ts; then + if ! diff invokeai/frontend/api/schema.ts invokeai/frontend/api/schema_orig.ts; then echo "Files are different!"; exit 1; fi diff --git a/AGENTS.md b/AGENTS.md index 4ecead9358..ce309621bd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,11 +15,12 @@ Before editing, read `AGENTS.md` along each target path; scoped rules supplement | API, services, invocations | [app/AGENTS.md](invokeai/app/AGENTS.md) | | Inference and model management | [backend/AGENTS.md](invokeai/backend/AGENTS.md) | | Python tests | [tests/AGENTS.md](tests/AGENTS.md); also read guidance for the production code under test | -| Legacy frontend and generated API artifacts | [webv1/AGENTS.md](invokeai/frontend/webv1/AGENTS.md) | +| Legacy frontend | [webv1/AGENTS.md](invokeai/frontend/webv1/AGENTS.md) | +| Shared generated API contracts | [api/AGENTS.md](invokeai/frontend/api/AGENTS.md) | | CI and contribution tooling | [.github/AGENTS.md](.github/AGENTS.md) | | Documentation | `docs/README.md` and `docs/package.json`; keep documentation about the implemented product current | -Ordinary frontend work targets **webv2**, served by default; `--webv2` remains a compatibility alias and `--web-legacy` selects legacy web. Verify build/launch targets for UI investigations. Legacy web owns generated OpenAPI/type artifacts. +Ordinary frontend work targets **webv2**, served by default; `--webv2` remains a compatibility alias and `--web-legacy` selects legacy web. Verify build/launch targets for UI investigations. The shared `frontend/api` package owns generated OpenAPI/type artifacts. ## Engineering standard diff --git a/Makefile b/Makefile index cd0cb2caeb..b3a656e9a6 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ help: @echo "frontend-test Run the frontend test suite once" @echo "frontend-dev Run the frontend in developer mode on localhost:5173" @echo "frontend-legacy-dev Run the legacy frontend development server" + @echo "frontend-api-install Install shared API generation dependencies" @echo "frontend-openapi Generate the OpenAPI schema" @echo "frontend-typegen Generate types for the frontend from the OpenAPI schema" @echo "frontend-lint Run frontend checks and fixable lint/format steps" @@ -89,14 +90,18 @@ frontendv2-dev: frontend-dev frontend-legacy-dev: cd invokeai/frontend/webv1 && pnpm run dev +# Install the shared contract generation tools +frontend-api-install: + pnpm -C invokeai/frontend/api install --frozen-lockfile + # Generate the OpenAPI Schema for the app frontend-openapi: - cd invokeai/frontend/webv1 && \ + cd invokeai/frontend/api && \ python ../../../scripts/generate_openapi_schema.py > openapi.json && \ - pnpm prettier --write openapi.json + pnpm format:openapi frontend-typegen: - set -o pipefail; cd invokeai/frontend/webv1 && python ../../../scripts/generate_openapi_schema.py | pnpm typegen + set -o pipefail; cd invokeai/frontend/api && python ../../../scripts/generate_openapi_schema.py | pnpm typegen frontend-lint: pnpm -C invokeai/frontend/webv2 lint diff --git a/docs/src/content/docs/contributing/new-model-integration.mdx b/docs/src/content/docs/contributing/new-model-integration.mdx index 85f47ef24e..4424f8d7cf 100644 --- a/docs/src/content/docs/contributing/new-model-integration.mdx +++ b/docs/src/content/docs/contributing/new-model-integration.mdx @@ -23,7 +23,7 @@ The examples use a hypothetical architecture, `NewModel`, with the base value `n ::: :::caution[Frontend scope] -Frontend work targets **webv2** (`invokeai/frontend/webv2`) only. The legacy UI in `invokeai/frontend/webv1` gets no graph builders or UI for new architectures. It still owns the generated OpenAPI artifacts, which CI checks; see [Tests and CI gates](#13-tests-and-ci-gates). +Frontend work targets **webv2** (`invokeai/frontend/webv2`) only. The legacy UI in `invokeai/frontend/webv1` gets no graph builders or UI for new architectures. Shared generated OpenAPI artifacts live in `invokeai/frontend/api`, which CI checks; see [Tests and CI gates](#13-tests-and-ci-gates). ::: :::tip[Start with the scaffolder] @@ -866,10 +866,12 @@ Beyond those tables, test what can silently go wrong: Use real lightweight modules with small configs rather than mocks. -**CI gates for generated artifacts.** New or changed invocations change the OpenAPI schema. As long as `invokeai/frontend/webv1` exists, CI (`openapi-checks.yml`, `typegen-checks.yml`) requires its generated `openapi.json` and `src/services/api/schema.ts` to be current. Regenerate both from `invokeai/frontend/webv1` in the repository's Python environment. This is a generated-file gate, not legacy UI work: +**CI gates for generated artifacts.** New or changed invocations change the OpenAPI schema. CI (`openapi-checks.yml`, `typegen-checks.yml`) requires the shared package's `invokeai/frontend/api/openapi.json` and `invokeai/frontend/api/schema.ts` to be current. Regenerate both from `invokeai/frontend/api` in the repository's Python environment after installing that package's locked dependencies: ```sh -python ../../../scripts/generate_openapi_schema.py > openapi.json && pnpm exec prettier --write openapi.json +pnpm install --frozen-lockfile +set -o pipefail +python ../../../scripts/generate_openapi_schema.py > openapi.json && pnpm format:openapi python ../../../scripts/generate_openapi_schema.py | pnpm typegen ``` diff --git a/docs/src/content/docs/development/Front End/index.md b/docs/src/content/docs/development/Front End/index.md index a3b0ec470e..7399579dcf 100644 --- a/docs/src/content/docs/development/Front End/index.md +++ b/docs/src/content/docs/development/Front End/index.md @@ -23,17 +23,18 @@ Run these in `invokeai/frontend/webv2`: - `test:browser`: run Chromium interaction tests - `check:release`: run the complete release gates, including performance, project-file, and accessibility journeys -The legacy package has its own scripts and lockfile. See each package's `AGENTS.md` for its commands and ownership rules. +The legacy package has its own scripts and lockfile. Frontend CI runs webv2's checks and release gate; legacy lint and tests remain available locally. See each package's `AGENTS.md` for its commands and ownership rules. ## Type generation -The legacy `web` package owns generation, including shared backend contracts consumed by webv2. We use [openapi-typescript] to generate types from the app's OpenAPI schema. The generated types are committed to the repo in [schema.ts]. +The shared `invokeai/frontend/api` package owns OpenAPI/type generation for backend contracts. CI checks these artifacts independently of either UI package. We use [openapi-typescript] to generate types from the app's OpenAPI schema. The generated types are committed to the repo in [schema.ts]. If you make backend changes, it's important to regenerate the frontend types: ```sh set -o pipefail -cd invokeai/frontend/webv1 && python ../../../scripts/generate_openapi_schema.py | pnpm typegen +pnpm -C invokeai/frontend/api install --frozen-lockfile +cd invokeai/frontend/api && python ../../../scripts/generate_openapi_schema.py | pnpm typegen ``` On macOS and Linux, you can run `make frontend-typegen` as a shortcut for the above snippet. @@ -121,7 +122,7 @@ Colocate unit tests and Chromium browser tests with the owning code. Use real br [i18next]: https://github.com/i18next/react-i18next [Weblate]: https://hosted.weblate.org/engage/invokeai/ [openapi-typescript]: https://github.com/openapi-ts/openapi-typescript -[schema.ts]: https://github.com/invoke-ai/InvokeAI/blob/main/invokeai/frontend/web/src/services/api/schema.ts +[schema.ts]: https://github.com/invoke-ai/InvokeAI-7/blob/main/invokeai/frontend/api/schema.ts [conventional commits]: https://www.conventionalcommits.org/en/v1.0.0/ [Workflows - Design and Implementation]: ./workflows/ [State Management]: ./state-management/ diff --git a/docs/src/content/docs/development/Process/release-process.mdx b/docs/src/content/docs/development/Process/release-process.mdx index 9869e4940e..2764f4b101 100644 --- a/docs/src/content/docs/development/Process/release-process.mdx +++ b/docs/src/content/docs/development/Process/release-process.mdx @@ -1,6 +1,6 @@ --- title: Release Process -lastUpdated: 2025-12-26 +lastUpdated: 2026-09-29 --- The Invoke application is published as a python package on [PyPI]. This includes both a source distribution and built distribution (a wheel). @@ -61,13 +61,14 @@ Next, these jobs run and must pass. They are the same jobs that are run for ever - **`python-tests`**: runs `pytest` on matrix of platforms - **`python-checks`**: runs `ruff` (format and lint) -- **`frontend-tests`**: runs `vitest` -- **`frontend-checks`**: runs `prettier` (format), `eslint` (lint), `dpdm` (circular refs), `tsc` (static type check) and `knip` (unused imports) -- **`typegen-checks`**: ensures the frontend and backend types are synced +- **`frontend-tests`**: runs webv2's `check:release`, including unit/browser tests, performance, project-file, and accessibility gates +- **`frontend-checks`**: runs webv2 formatting, Oxc lint, TypeScript, architecture checks, and its production build + +PRs also run **`openapi-checks`** and **`typegen-checks`** against the shared `invokeai/frontend/api` package to detect backend contract drift. Legacy webv1 lint/tests remain local commands. #### `build-wheel` Job -This sets up both python and frontend dependencies and builds the python package. Internally, this runs `./scripts/build_wheel.sh` and uploads `dist.zip`, which contains the wheel and unarchived build. +This sets up both python and frontend dependencies and builds the python package. Internally, this runs `./scripts/build_wheel.sh`, bundles both webv2 and the legacy webv1 fallback, and uploads `dist.zip`, which contains the wheel and unarchived build. Distribution CI builds the legacy bundle without running its local lint/test completion command. You don't need to download or test these artifacts. diff --git a/invokeai/app/AGENTS.md b/invokeai/app/AGENTS.md index a5e6cdf021..635b689682 100644 --- a/invokeai/app/AGENTS.md +++ b/invokeai/app/AGENTS.md @@ -6,7 +6,7 @@ - Before changing flows, inspect `api/dependencies.py`, affected routers, service base/default/storage implementations, and tests. Preserve dependency/start/stop lifecycles; avoid parallel global services. - Server-authorize every account-owned resource, including queries, writes, events, exports, and recovery; UI guards are insufficient. - Trace frontend/saved-workflow consumers before changing Pydantic models, invocation fields/versions, HTTP DTOs, or socket-event names, defaults, validation, or errors. -- API changes may require regenerating `frontend/webv1/openapi.json` and `frontend/webv1/src/services/api/schema.ts` under `invokeai/`, even for webv2. Follow legacy frontend generation guidance; never hand-edit output. +- API changes may require regenerating `frontend/api/openapi.json` and `frontend/api/schema.ts` under `invokeai/`, even for webv2. Follow shared API package generation guidance; never hand-edit output. ## Persistence and lifecycle diff --git a/invokeai/frontend/api/.gitignore b/invokeai/frontend/api/.gitignore new file mode 100644 index 0000000000..c2658d7d1b --- /dev/null +++ b/invokeai/frontend/api/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/invokeai/frontend/api/.prettierignore b/invokeai/frontend/api/.prettierignore new file mode 100644 index 0000000000..a38a73757f --- /dev/null +++ b/invokeai/frontend/api/.prettierignore @@ -0,0 +1,3 @@ +node_modules/ +schema.ts +pnpm-lock.yaml diff --git a/invokeai/frontend/api/.prettierrc.json b/invokeai/frontend/api/.prettierrc.json new file mode 100644 index 0000000000..4438806121 --- /dev/null +++ b/invokeai/frontend/api/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "trailingComma": "es5", + "printWidth": 120, + "tabWidth": 2, + "semi": true, + "singleQuote": true +} diff --git a/invokeai/frontend/api/AGENTS.md b/invokeai/frontend/api/AGENTS.md new file mode 100644 index 0000000000..c59d5ad254 --- /dev/null +++ b/invokeai/frontend/api/AGENTS.md @@ -0,0 +1,9 @@ +# Shared API contracts + +This package owns `openapi.json`, generated `schema.ts`, and the generator used to check backend/frontend contract drift. It has no UI runtime. Frontend CI runs webv2; contract CI installs this package only. + +- Use pnpm 10 and this package's lockfile. Keep the pinned generator, TypeScript 5 peer, and formatter versions intentional; webv2's TypeScript version is independent. +- Never hand-edit generated output. In the repository's activated Python environment, run `make frontend-api-install`, `make frontend-openapi`, and `make frontend-typegen` from the root. Shell pipelines must preserve generator failures. +- Run `pnpm lint` and `pnpm test` here. The generator tests compile independent consumer expectations against real generated types and exercise invalid input. +- Keep the legacy `../webv1/src/services/api/schema.ts` type-only re-export compatible. Validate legacy TypeScript when shared types or that boundary change. +- API shape changes follow `invokeai/app/AGENTS.md` and require owning backend tests. Moving artifact ownership must preserve the generated contracts. diff --git a/invokeai/frontend/api/README.md b/invokeai/frontend/api/README.md new file mode 100644 index 0000000000..3ff7342240 --- /dev/null +++ b/invokeai/frontend/api/README.md @@ -0,0 +1,16 @@ +# Shared API contracts + +`openapi.json` records the backend OpenAPI schema. `schema.ts` is generated by openapi-typescript with the binary-upload, metadata, and enum handling in `scripts/typegen.js`. Both are checked for drift in CI using this package, independently of the UI packages. + +From the repository root with its Python environment activated: + +```sh +make frontend-api-install +make frontend-openapi +make frontend-typegen +pnpm -C invokeai/frontend/api test +``` + +Legacy webv1 keeps its existing `services/api/schema` import path through a type-only re-export. Webv2 maps backend wire contracts through its owning platform/features adapters. The API package does not add a frontend runtime dependency. + +The generator is pinned to its TypeScript 5 peer and existing formatter versions; the active frontend remains on TypeScript 7. See `AGENTS.md` for validation and ownership rules. diff --git a/invokeai/frontend/api/package.json b/invokeai/frontend/api/package.json new file mode 100644 index 0000000000..d605d970ed --- /dev/null +++ b/invokeai/frontend/api/package.json @@ -0,0 +1,21 @@ +{ + "name": "@invoke-ai/api-contracts", + "version": "0.0.1", + "private": true, + "type": "module", + "scripts": { + "lint": "prettier --check .", + "format:openapi": "prettier --write openapi.json", + "test": "node --test scripts/typegen.test.mjs", + "typegen": "node scripts/typegen.js" + }, + "devDependencies": { + "openapi-typescript": "7.10.1", + "prettier": "3.8.3", + "typescript": "5.9.3" + }, + "engines": { + "pnpm": "10" + }, + "packageManager": "pnpm@10.12.4" +} diff --git a/invokeai/frontend/api/pnpm-lock.yaml b/invokeai/frontend/api/pnpm-lock.yaml new file mode 100644 index 0000000000..ef3a52cdcd --- /dev/null +++ b/invokeai/frontend/api/pnpm-lock.yaml @@ -0,0 +1,270 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + openapi-typescript: + specifier: 7.10.1 + version: 7.10.1(typescript@5.9.3) + prettier: + specifier: 3.8.3 + version: 3.8.3 + typescript: + specifier: 5.9.3 + version: 5.9.3 + +packages: + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@redocly/ajv@8.17.1': + resolution: {integrity: sha512-EDtsGZS964mf9zAUXAl9Ew16eYbeyAFWhsPr0fX6oaJxgd8rApYlPBf0joyhnUHz88WxrigyFtTaqqzXNzPgqw==} + + '@redocly/config@0.22.2': + resolution: {integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==} + + '@redocly/openapi-core@1.34.6': + resolution: {integrity: sha512-2+O+riuIUgVSuLl3Lyh5AplWZyVMNuG2F98/o6NrutKJfW4/GTZdPpZlIphS0HGgcOHgmWcCSHj+dWFlZaGSHw==} + engines: {node: '>=18.17.0', npm: '>=9.5.0'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + + colorette@1.4.0: + resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + index-to-position@1.2.0: + resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} + engines: {node: '>=18'} + + js-levenshtein@1.1.6: + resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} + engines: {node: '>=0.10.0'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + openapi-typescript@7.10.1: + resolution: {integrity: sha512-rBcU8bjKGGZQT4K2ekSTY2Q5veOQbVG/lTKZ49DeCyT9z62hM2Vj/LLHjDHC9W7LJG8YMHcdXpRZDqC1ojB/lw==} + hasBin: true + peerDependencies: + typescript: ^5.x + + parse-json@8.3.0: + resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} + engines: {node: '>=18'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + prettier@3.8.3: + resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} + engines: {node: '>=14'} + hasBin: true + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + yaml-ast-parser@0.0.43: + resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + +snapshots: + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/helper-validator-identifier@7.29.7': {} + + '@redocly/ajv@8.17.1': + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + '@redocly/config@0.22.2': {} + + '@redocly/openapi-core@1.34.6(supports-color@10.2.2)': + dependencies: + '@redocly/ajv': 8.17.1 + '@redocly/config': 0.22.2 + colorette: 1.4.0 + https-proxy-agent: 7.0.6(supports-color@10.2.2) + js-levenshtein: 1.1.6 + js-yaml: 4.1.1 + minimatch: 5.1.6 + pluralize: 8.0.0 + yaml-ast-parser: 0.0.43 + transitivePeerDependencies: + - supports-color + + agent-base@7.1.4: {} + + ansi-colors@4.1.3: {} + + argparse@2.0.1: {} + + balanced-match@1.0.2: {} + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + change-case@5.4.4: {} + + colorette@1.4.0: {} + + debug@4.4.3(supports-color@10.2.2): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 + + fast-deep-equal@3.1.3: {} + + fast-uri@3.1.0: {} + + https-proxy-agent@7.0.6(supports-color@10.2.2): + dependencies: + agent-base: 7.1.4 + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + index-to-position@1.2.0: {} + + js-levenshtein@1.1.6: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + json-schema-traverse@1.0.0: {} + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.2 + + ms@2.1.3: {} + + openapi-typescript@7.10.1(typescript@5.9.3): + dependencies: + '@redocly/openapi-core': 1.34.6(supports-color@10.2.2) + ansi-colors: 4.1.3 + change-case: 5.4.4 + parse-json: 8.3.0 + supports-color: 10.2.2 + typescript: 5.9.3 + yargs-parser: 21.1.1 + + parse-json@8.3.0: + dependencies: + '@babel/code-frame': 7.29.7 + index-to-position: 1.2.0 + type-fest: 4.41.0 + + picocolors@1.1.1: {} + + pluralize@8.0.0: {} + + prettier@3.8.3: {} + + require-from-string@2.0.2: {} + + supports-color@10.2.2: {} + + type-fest@4.41.0: {} + + typescript@5.9.3: {} + + yaml-ast-parser@0.0.43: {} + + yargs-parser@21.1.1: {} diff --git a/invokeai/frontend/api/scripts/typegen.js b/invokeai/frontend/api/scripts/typegen.js index f526b14946..1e1689728c 100644 --- a/invokeai/frontend/api/scripts/typegen.js +++ b/invokeai/frontend/api/scripts/typegen.js @@ -5,7 +5,7 @@ import openapiTS, { astToString } from 'openapi-typescript'; import ts from 'typescript'; const OPENAPI_URL = 'http://127.0.0.1:9090/openapi.json'; -const OUTPUT_FILE = 'src/services/api/schema.ts'; +const OUTPUT_FILE = 'schema.ts'; async function generateTypes(schema) { process.stdout.write(`Generating types ${OUTPUT_FILE}...`); diff --git a/invokeai/frontend/api/scripts/typegen.test.mjs b/invokeai/frontend/api/scripts/typegen.test.mjs new file mode 100644 index 0000000000..6d6200f5ee --- /dev/null +++ b/invokeai/frontend/api/scripts/typegen.test.mjs @@ -0,0 +1,78 @@ +import assert from 'node:assert/strict'; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import { test } from 'node:test'; +import ts from 'typescript'; + +const generator = fileURLToPath(new URL('./typegen.js', import.meta.url)); + +test('generated contracts preserve upload, metadata, enum, and optional-default types', () => { + const cwd = mkdtempSync(join(tmpdir(), 'invokeai-contracts-')); + try { + const schema = { + openapi: '3.1.0', + info: { title: 'Contract fixture', version: '1' }, + paths: {}, + components: { + schemas: { + Upload: { type: 'string', contentMediaType: 'application/octet-stream' }, + LegacyUpload: { type: 'string', format: 'binary', nullable: true }, + MetadataField: { type: 'object', title: 'MetadataField' }, + Mode: { type: 'string', enum: ['used', 'unused'] }, + Variant: { type: 'object', properties: { mode: { $ref: '#/components/schemas/Mode' } } }, + VariantUnion: { + oneOf: [{ $ref: '#/components/schemas/Variant' }], + discriminator: { propertyName: 'mode', mapping: { used: '#/components/schemas/Variant' } }, + }, + Defaulted: { type: 'object', properties: { count: { type: 'integer', default: 1 } } }, + }, + }, + }; + const result = spawnSync(process.execPath, [generator], { cwd, input: JSON.stringify(schema), encoding: 'utf8' }); + assert.equal(result.status, 0, result.stderr); + const consumer = join(cwd, 'consumer.ts'); + writeFileSync( + consumer, + `import type { components } from './schema'; +const upload: components['schemas']['Upload'] = new Blob(); +const nullableUpload: components['schemas']['LegacyUpload'] = null; +const legacyUpload: components['schemas']['LegacyUpload'] = new Blob(); +const metadata: components['schemas']['MetadataField'] = { arbitrary: { nested: true } }; +const usedMode: components['schemas']['Mode'] = 'used'; +const unusedMode: components['schemas']['Mode'] = 'unused'; +const defaulted: components['schemas']['Defaulted'] = {}; +// @ts-expect-error Uploads are binary, not strings. +const invalidUpload: components['schemas']['Upload'] = 'file contents'; +// @ts-expect-error Modern uploads are not nullable without a nullable declaration. +const invalidNull: components['schemas']['Upload'] = null; +// @ts-expect-error Metadata must remain an object. +const invalidMetadata: components['schemas']['MetadataField'] = 'metadata'; +// @ts-expect-error The enum must remain bounded. +const invalidMode: components['schemas']['Mode'] = 'other'; +` + ); + const program = ts.createProgram([consumer], { noEmit: true, strict: true, skipLibCheck: true }); + const diagnostics = ts.getPreEmitDiagnostics(program); + assert.deepEqual( + diagnostics.map((d) => ts.flattenDiagnosticMessageText(d.messageText, '\n')), + [] + ); + } finally { + rmSync(cwd, { recursive: true, force: true }); + } +}); + +test('invalid input fails without replacing an existing contract', () => { + const cwd = mkdtempSync(join(tmpdir(), 'invokeai-contracts-invalid-')); + try { + writeFileSync(join(cwd, 'schema.ts'), 'previous contract\n'); + const result = spawnSync(process.execPath, [generator], { cwd, input: '{invalid', encoding: 'utf8' }); + assert.notEqual(result.status, 0); + assert.equal(readFileSync(join(cwd, 'schema.ts'), 'utf8'), 'previous contract\n'); + } finally { + rmSync(cwd, { recursive: true, force: true }); + } +}); diff --git a/invokeai/frontend/webv1/.prettierignore b/invokeai/frontend/webv1/.prettierignore index 658baa261e..1fb66a5ef8 100644 --- a/invokeai/frontend/webv1/.prettierignore +++ b/invokeai/frontend/webv1/.prettierignore @@ -9,7 +9,6 @@ index.html .yarn/ .yalc/ *.scss -src/services/api/schema.ts static/ src/theme/css/overlayscrollbars.css src/theme_/css/overlayscrollbars.css diff --git a/invokeai/frontend/webv1/AGENTS.md b/invokeai/frontend/webv1/AGENTS.md index 4296f0e465..2bf00db6f9 100644 --- a/invokeai/frontend/webv1/AGENTS.md +++ b/invokeai/frontend/webv1/AGENTS.md @@ -2,7 +2,7 @@ Change legacy UI only when explicitly requested or required for shared compatibility. Ordinary frontend work belongs in `../webv2/`; read its guidance. -This package owns `openapi.json`, generated `src/services/api/schema.ts`, and generation tooling, including for backend contracts consumed by webv2. +Shared OpenAPI/type artifacts and generation tooling belong to [../api/AGENTS.md](../api/AGENTS.md). This package consumes those types through `src/services/api/schema.ts`; keep that type-only compatibility import path. ## Commands @@ -11,7 +11,6 @@ Run from this directory with this package's lockfile: - `pnpm lint`: legacy TypeScript, dependency-cycle, ESLint, Prettier, and unused-dependency checks. - `pnpm test:no-watch`: legacy Vitest; colocate tests following existing conventions. - `pnpm build`: legacy production build. -- In the activated repository Python environment, regenerate schema: `python ../../../scripts/generate_openapi_schema.py > openapi.json`, then `pnpm exec prettier --write openapi.json`. -- Regenerate types in that environment: `python ../../../scripts/generate_openapi_schema.py | pnpm typegen`. Enable shell pipeline failure propagation and verify generator success; never hand-edit generated files. +- Regenerate shared contracts from `../api/` using its commands and lockfile. Use root Ruff/pytest for Python changes. Do not apply webv2's Chakra version, @dnd-kit APIs, Oxc commands, or browser-test assumptions here. Preserve existing persisted-state migrations when changing legacy state. diff --git a/invokeai/frontend/webv1/eslint.config.mjs b/invokeai/frontend/webv1/eslint.config.mjs index c1f1eeb0ff..642adcccbe 100644 --- a/invokeai/frontend/webv1/eslint.config.mjs +++ b/invokeai/frontend/webv1/eslint.config.mjs @@ -239,7 +239,6 @@ export default [ '**/index.html', '**/.yarn/', '**/*.scss', - 'src/services/api/schema.ts', '.prettierrc.js', '.storybook', ], diff --git a/invokeai/frontend/webv1/knip.ts b/invokeai/frontend/webv1/knip.ts index 7a32b8dd6e..c5451c7ae4 100644 --- a/invokeai/frontend/webv1/knip.ts +++ b/invokeai/frontend/webv1/knip.ts @@ -6,7 +6,6 @@ const config: KnipConfig = { // This file is only used during debugging 'src/app/store/middleware/debugLoggerMiddleware.ts', // Autogenerated types - shouldn't ever touch these - 'src/services/api/schema.ts', 'src/features/nodes/types/v1/**', 'src/features/nodes/types/v2/**', 'src/features/parameters/types/parameterSchemas.ts', diff --git a/invokeai/frontend/webv1/package.json b/invokeai/frontend/webv1/package.json index 8b3b63ae8e..cd490c357d 100644 --- a/invokeai/frontend/webv1/package.json +++ b/invokeai/frontend/webv1/package.json @@ -22,7 +22,6 @@ "dev": "vite dev", "dev:host": "vite dev --host", "build": "pnpm run lint && vitest run && vite build", - "typegen": "node scripts/typegen.js", "preview": "vite preview", "lint:knip": "knip --tags=-knipignore", "lint:dpdm": "dpdm --no-warning --no-tree --transform --exit-code circular:1 src/main.tsx", @@ -147,7 +146,6 @@ "knip": "^5.77.4", "magic-string": "^0.30.21", "openapi-types": "^12.1.3", - "openapi-typescript": "^7.10.1", "prettier": "^3.8.3", "rollup-plugin-visualizer": "^6.0.5", "storybook": "^10.3.6", diff --git a/invokeai/frontend/webv1/pnpm-lock.yaml b/invokeai/frontend/webv1/pnpm-lock.yaml index 968212d94a..7692321d1d 100644 --- a/invokeai/frontend/webv1/pnpm-lock.yaml +++ b/invokeai/frontend/webv1/pnpm-lock.yaml @@ -312,9 +312,6 @@ importers: openapi-types: specifier: ^12.1.3 version: 12.1.3 - openapi-typescript: - specifier: ^7.10.1 - version: 7.10.1(typescript@5.9.3) prettier: specifier: ^3.8.3 version: 3.8.3 @@ -1437,16 +1434,6 @@ packages: '@types/react': optional: true - '@redocly/ajv@8.17.1': - resolution: {integrity: sha512-EDtsGZS964mf9zAUXAl9Ew16eYbeyAFWhsPr0fX6oaJxgd8rApYlPBf0joyhnUHz88WxrigyFtTaqqzXNzPgqw==} - - '@redocly/config@0.22.2': - resolution: {integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==} - - '@redocly/openapi-core@1.34.6': - resolution: {integrity: sha512-2+O+riuIUgVSuLl3Lyh5AplWZyVMNuG2F98/o6NrutKJfW4/GTZdPpZlIphS0HGgcOHgmWcCSHj+dWFlZaGSHw==} - engines: {node: '>=18.17.0', npm: '>=9.5.0'} - '@reduxjs/toolkit@2.8.2': resolution: {integrity: sha512-MYlOhQ0sLdw4ud48FoC5w0dH9VfWQjtCjreKwYTT3l+r427qYC5Y8PihNutepr8XrNaBUDQo9khWUwQxZaqt5A==} peerDependencies: @@ -2180,17 +2167,9 @@ packages: ag-psd@28.5.1: resolution: {integrity: sha512-oDzQ4VctavkqGiI1mFN9PIMjVrv0T6MpoIS6SAbzAQXCQs2b3UdyI9VQTrUQdfq9AaHQcR0cIX/hAZ7fyh7Cgw==} - agent-base@7.1.4: - resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} - engines: {node: '>= 14'} - ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -2396,9 +2375,6 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - change-case@5.4.4: - resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} - check-error@2.1.3: resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} engines: {node: '>= 16'} @@ -2438,9 +2414,6 @@ packages: color2k@2.0.3: resolution: {integrity: sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==} - colorette@1.4.0: - resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} - commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -2899,9 +2872,6 @@ packages: fast-shallow-equal@1.0.0: resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==} - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fastest-stable-stringify@2.0.2: resolution: {integrity: sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==} @@ -3143,10 +3113,6 @@ packages: html-to-image@1.11.13: resolution: {integrity: sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==} - https-proxy-agent@7.0.6: - resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} - engines: {node: '>= 14'} - hyphenate-style-name@1.1.0: resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} @@ -3193,10 +3159,6 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} - index-to-position@1.2.0: - resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} - engines: {node: '>=18'} - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -3384,10 +3346,6 @@ packages: js-cookie@2.2.1: resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} - js-levenshtein@1.1.6: - resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} - engines: {node: '>=0.10.0'} - js-tokens@10.0.0: resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} @@ -3412,9 +3370,6 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -3637,10 +3592,6 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -3771,12 +3722,6 @@ packages: openapi-types@12.1.3: resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - openapi-typescript@7.10.1: - resolution: {integrity: sha512-rBcU8bjKGGZQT4K2ekSTY2Q5veOQbVG/lTKZ49DeCyT9z62hM2Vj/LLHjDHC9W7LJG8YMHcdXpRZDqC1ojB/lw==} - hasBin: true - peerDependencies: - typescript: ^5.x - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -3829,10 +3774,6 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - parse-json@8.3.0: - resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} - engines: {node: '>=18'} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -3881,10 +3822,6 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} - pluralize@8.0.0: - resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} - engines: {node: '>=4'} - possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -4193,10 +4130,6 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - requireindex@1.1.0: resolution: {integrity: sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg==} engines: {node: '>=0.10.5'} @@ -4539,10 +4472,6 @@ packages: stylis@4.3.6: resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} - supports-color@10.2.2: - resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} - engines: {node: '>=18'} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -4977,9 +4906,6 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml-ast-parser@0.0.43: - resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==} - yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} @@ -5075,7 +5001,7 @@ snapshots: '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -5095,7 +5021,7 @@ snapshots: '@babel/types': 7.29.0 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -5344,7 +5270,7 @@ snapshots: '@babel/parser': 7.28.5 '@babel/template': 7.27.2 '@babel/types': 7.28.5 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -5356,7 +5282,7 @@ snapshots: '@babel/parser': 7.29.3 '@babel/template': 7.28.6 '@babel/types': 7.29.0 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -5368,7 +5294,7 @@ snapshots: '@babel/parser': 7.29.7 '@babel/template': 7.29.7 '@babel/types': 7.29.7 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -5806,7 +5732,7 @@ snapshots: '@eslint/config-array@0.21.1': dependencies: '@eslint/object-schema': 2.1.7 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -5822,7 +5748,7 @@ snapshots: '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -6204,29 +6130,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@redocly/ajv@8.17.1': - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - '@redocly/config@0.22.2': {} - - '@redocly/openapi-core@1.34.6(supports-color@10.2.2)': - dependencies: - '@redocly/ajv': 8.17.1 - '@redocly/config': 0.22.2 - colorette: 1.4.0 - https-proxy-agent: 7.0.6(supports-color@10.2.2) - js-levenshtein: 1.1.6 - js-yaml: 4.1.1 - minimatch: 5.1.6 - pluralize: 8.0.0 - yaml-ast-parser: 0.0.43 - transitivePeerDependencies: - - supports-color - '@reduxjs/toolkit@2.8.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.6)(redux@5.0.1))(react@19.2.6)': dependencies: '@standard-schema/spec': 1.1.0 @@ -6706,7 +6609,7 @@ snapshots: '@typescript-eslint/types': 8.59.2 '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.59.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: @@ -6716,7 +6619,7 @@ snapshots: dependencies: '@typescript-eslint/tsconfig-utils': 8.50.1(typescript@5.9.3) '@typescript-eslint/types': 8.50.1 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -6725,7 +6628,7 @@ snapshots: dependencies: '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) '@typescript-eslint/types': 8.59.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -6753,7 +6656,7 @@ snapshots: '@typescript-eslint/types': 8.59.2 '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.9.3) '@typescript-eslint/utils': 8.59.2(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 eslint: 9.39.2(jiti@2.6.1) ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 @@ -6770,7 +6673,7 @@ snapshots: '@typescript-eslint/tsconfig-utils': 8.50.1(typescript@5.9.3) '@typescript-eslint/types': 8.50.1 '@typescript-eslint/visitor-keys': 8.50.1 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 minimatch: 9.0.5 semver: 7.7.3 tinyglobby: 0.2.15 @@ -6785,7 +6688,7 @@ snapshots: '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) '@typescript-eslint/types': 8.59.2 '@typescript-eslint/visitor-keys': 8.59.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 minimatch: 10.2.5 semver: 7.8.0 tinyglobby: 0.2.16 @@ -6970,8 +6873,6 @@ snapshots: base64-js: 1.5.1 pako: 2.1.0 - agent-base@7.1.4: {} - ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -6979,8 +6880,6 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ansi-colors@4.1.3: {} - ansi-regex@5.0.1: {} ansi-regex@6.2.2: {} @@ -7221,8 +7120,6 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - change-case@5.4.4: {} - check-error@2.1.3: {} classcat@5.0.5: {} @@ -7261,8 +7158,6 @@ snapshots: color2k@2.0.3: {} - colorette@1.4.0: {} - commander@2.20.3: {} compare-versions@6.1.1: {} @@ -7385,11 +7280,9 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.4.3(supports-color@10.2.2): + debug@4.4.3: dependencies: ms: 2.1.3 - optionalDependencies: - supports-color: 10.2.2 decode-uri-component@0.4.1: {} @@ -7487,7 +7380,7 @@ snapshots: engine.io-client@6.6.4: dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 engine.io-parser: 5.2.3 ws: 8.18.3 xmlhttprequest-ssl: 2.1.2 @@ -7792,7 +7685,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -7862,8 +7755,6 @@ snapshots: fast-shallow-equal@1.0.0: {} - fast-uri@3.1.0: {} - fastest-stable-stringify@2.0.2: {} fastq@1.20.1: @@ -8104,13 +7995,6 @@ snapshots: html-to-image@1.11.13: {} - https-proxy-agent@7.0.6(supports-color@10.2.2): - dependencies: - agent-base: 7.1.4 - debug: 4.4.3(supports-color@10.2.2) - transitivePeerDependencies: - - supports-color - hyphenate-style-name@1.1.0: {} i18next-http-backend@3.0.2: @@ -8146,8 +8030,6 @@ snapshots: indent-string@4.0.0: {} - index-to-position@1.2.0: {} - inherits@2.0.4: {} inline-style-prefixer@7.0.1: @@ -8334,8 +8216,6 @@ snapshots: js-cookie@2.2.1: {} - js-levenshtein@1.1.6: {} - js-tokens@10.0.0: {} js-tokens@4.0.0: {} @@ -8352,8 +8232,6 @@ snapshots: json-schema-traverse@0.4.1: {} - json-schema-traverse@1.0.0: {} - json-stable-stringify-without-jsonify@1.0.1: {} json5@1.0.2: @@ -8555,10 +8433,6 @@ snapshots: dependencies: brace-expansion: 1.1.12 - minimatch@5.1.6: - dependencies: - brace-expansion: 2.0.2 - minimatch@9.0.5: dependencies: brace-expansion: 2.0.2 @@ -8684,16 +8558,6 @@ snapshots: openapi-types@12.1.3: {} - openapi-typescript@7.10.1(typescript@5.9.3): - dependencies: - '@redocly/openapi-core': 1.34.6(supports-color@10.2.2) - ansi-colors: 4.1.3 - change-case: 5.4.4 - parse-json: 8.3.0 - supports-color: 10.2.2 - typescript: 5.9.3 - yargs-parser: 21.1.1 - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -8783,12 +8647,6 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - parse-json@8.3.0: - dependencies: - '@babel/code-frame': 7.27.1 - index-to-position: 1.2.0 - type-fest: 4.41.0 - path-exists@4.0.0: {} path-key@3.1.1: {} @@ -8821,8 +8679,6 @@ snapshots: picomatch@4.0.4: {} - pluralize@8.0.0: {} - possible-typed-array-names@1.1.0: {} postcss@8.5.14: @@ -9169,8 +9025,6 @@ snapshots: require-directory@2.1.1: {} - require-from-string@2.0.2: {} - requireindex@1.1.0: {} reselect@5.1.1: {} @@ -9415,7 +9269,7 @@ snapshots: socket.io-client@4.8.3: dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 engine.io-client: 6.6.4 socket.io-parser: 4.2.5 transitivePeerDependencies: @@ -9426,7 +9280,7 @@ snapshots: socket.io-parser@4.2.5: dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -9582,8 +9436,6 @@ snapshots: stylis@4.3.6: {} - supports-color@10.2.2: {} - supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -9943,8 +9795,6 @@ snapshots: yallist@3.1.1: {} - yaml-ast-parser@0.0.43: {} - yaml@1.10.2: {} yargs-parser@21.1.1: {} diff --git a/invokeai/frontend/webv1/src/services/api/README.md b/invokeai/frontend/webv1/src/services/api/README.md index ab65b841d8..817eb2d85b 100644 --- a/invokeai/frontend/webv1/src/services/api/README.md +++ b/invokeai/frontend/webv1/src/services/api/README.md @@ -6,15 +6,6 @@ It defines a simple base query with special handling for OpenAPI schema queries ## Types -The API provides an OpenAPI schema and we generate TS types from it. They are stored in: invokeai/frontend/webv1/src/services/api/schema.ts +The shared `invokeai/frontend/api` package owns the generated `openapi.json`, `schema.ts`, and tooling. This package's `src/services/api/schema.ts` keeps the existing `services/api/schema` import path as a type-only re-export. -We use https://github.com/openapi-ts/openapi-typescript/ to generate the types. - -- Python script to output the OpenAPI schema: scripts/generate_openapi_schema.py -- Node script to call openapi-typescript and generate the TS types: invokeai/frontend/webv1/scripts/typegen.js - -Pipe the output of the python script to the node script to update the types. There is a `make` target that does this in one fell swoop (after activating venv): `make frontend-typegen` - -Alternatively, start the ptyhon server and run `pnpm typegen`. - -The schema.ts file is pushed to the repo, and a CI check ensures it is up to date. +From the repository root in its activated Python environment, run `make frontend-api-install`, `make frontend-openapi`, and `make frontend-typegen`. See `invokeai/frontend/api/README.md` for ownership and generation details. Contract CI runs in that shared package; frontend CI runs webv2. diff --git a/invokeai/frontend/webv1/src/services/api/endpoints/images.test.ts b/invokeai/frontend/webv1/src/services/api/endpoints/images.test.ts index 3bb3efd3f2..3e9b557275 100644 --- a/invokeai/frontend/webv1/src/services/api/endpoints/images.test.ts +++ b/invokeai/frontend/webv1/src/services/api/endpoints/images.test.ts @@ -32,11 +32,11 @@ describe('IMAGE_BATCH_CHUNK_SIZE', () => { it('matches the bound the server actually publishes', () => { // The client-side chunk size exists only to satisfy the server-side cap, and the two are // declared in different languages. openapi.json is the committed contract between them - // (typegen-checks keeps it in sync with the routers), so drift on either side fails here + // (openapi-checks keeps it in sync with the routers), so drift on either side fails here // rather than at runtime as a batch of 422s. type ArraySchema = { maxItems?: number; anyOf?: ArraySchema[] }; const openapi = JSON.parse( - readFileSync(fileURLToPath(new URL('../../../../openapi.json', import.meta.url)), 'utf8') + readFileSync(fileURLToPath(new URL('../../../../../api/openapi.json', import.meta.url)), 'utf8') ) as { components: { schemas: Record }; }; diff --git a/invokeai/frontend/webv1/src/services/api/schema.ts b/invokeai/frontend/webv1/src/services/api/schema.ts new file mode 100644 index 0000000000..e8cfae791d --- /dev/null +++ b/invokeai/frontend/webv1/src/services/api/schema.ts @@ -0,0 +1 @@ +export type * from '../../../../api/schema'; diff --git a/invokeai/frontend/webv2/AGENTS.md b/invokeai/frontend/webv2/AGENTS.md index b50759902e..0991b56f37 100644 --- a/invokeai/frontend/webv2/AGENTS.md +++ b/invokeai/frontend/webv2/AGENTS.md @@ -2,6 +2,8 @@ Read [ARCHITECTURE.md](ARCHITECTURE.md) for ownership/interface changes and affected owner-local READMEs for lifecycle/persistence. Stack: React 19, Vite, Chakra UI 3, TanStack Query/Router, @dnd-kit; legacy web differs. Webv2 is served by default; `--webv2` is a compatibility alias and `--web-legacy` selects `../webv1/`. +Shared generated API contracts and their CI gates belong to [../api/AGENTS.md](../api/AGENTS.md). + ## Ownership and state - Follow `app -> workbench/features -> platform` and `src/architecture/` policy. App composes dependencies; Platform owns domain-neutral infrastructure; Features own domain behavior; Workbench owns the project aggregate, shell, and Canvas. diff --git a/invokeai/frontend/webv2/src/architecture/architectureWorkflow.test.ts b/invokeai/frontend/webv2/src/architecture/architectureWorkflow.test.ts index c0f2a9245b..131c9a0b4c 100644 --- a/invokeai/frontend/webv2/src/architecture/architectureWorkflow.test.ts +++ b/invokeai/frontend/webv2/src/architecture/architectureWorkflow.test.ts @@ -54,18 +54,18 @@ describe('architecture workflow', () => { expect(action.inputs['working-directory'].default).toBe('invokeai/frontend/webv2'); for (const [path, source] of Object.entries(sources).filter(([path]) => path.includes('/workflows/'))) { const jobs = parse(source).jobs; - for (const [id, job] of Object.entries(jobs) as [ - string, - { - defaults?: { run?: { 'working-directory'?: string } }; - steps: { uses?: string; with?: { 'working-directory'?: string } }[]; - }, - ][]) { + for (const job of Object.values(jobs) as { + defaults?: { run?: { 'working-directory'?: string } }; + steps: { uses?: string; with?: { 'working-directory'?: string } }[]; + }[]) { const install = job.steps.find((step) => step.uses === './.github/actions/install-frontend-deps'); if (!install) { continue; } - const expected = id.startsWith('frontend-webv2-') ? 'invokeai/frontend/webv2' : 'invokeai/frontend/webv1'; + const expected = + path.endsWith('/frontend-checks.yml') || path.endsWith('/frontend-tests.yml') + ? 'invokeai/frontend/webv2' + : 'invokeai/frontend/api'; expect(install.with?.['working-directory'] ?? action.inputs['working-directory'].default, path).toBe(expected); if (job.defaults?.run) { expect(job.defaults.run['working-directory'], path).toBe(expected); diff --git a/pyproject.toml b/pyproject.toml index d86c406145..7346f6a6cb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ dependencies = [ # is fixed as of 0.124.0 — no change to AnyInvocation was needed. # # Two later changes did need adapting to, both handled: 0.130 emits `contentMediaType` instead of - # `format: binary` for file uploads (see the Blob mapping in frontend/webv1/scripts/typegen.js), and 0.141 keeps + # `format: binary` for file uploads (see the Blob mapping in frontend/api/scripts/typegen.js), and 0.141 keeps # included routers as a single node in `app.routes` rather than copying their routes into it (see # `_iter_route_contexts` in tests/app/routers/test_model_manager_authorization.py). Keep the minor-version bound: # both of those were silent breakages that only surfaced because something happened to assert on them. @@ -370,6 +370,7 @@ exclude = [ "__pycache__", "build", "dist", + "invokeai/frontend/api/node_modules/", "invokeai/frontend/webv1/node_modules/", "invokeai/frontend/webv2/node_modules/", ".venv*", diff --git a/scripts/build_wheel.sh b/scripts/build_wheel.sh index ac25173255..2eb695b445 100755 --- a/scripts/build_wheel.sh +++ b/scripts/build_wheel.sh @@ -51,7 +51,7 @@ for frontend in webv2 webv1; do echo "Installing $frontend dependencies..." pnpm install --frozen-lockfile if [[ ! -z ${CI} ]]; then - # CI runs each package's completion gates before building distributions. + # CI validates webv2 separately; distribution builds only bundle the legacy fallback. pnpm exec vite build else pnpm build