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.
This commit is contained in:
Josh Corbett
2026-09-29 18:47:06 -06:00
parent 8e360e76fa
commit 2a45334b6c
32 changed files with 494 additions and 322 deletions
-57
View File
@@ -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
-37
View File
@@ -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.
+6 -5
View File
@@ -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
+16 -5
View File
@@ -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
+3 -2
View File
@@ -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
+8 -3
View File
@@ -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
@@ -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
```
@@ -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/
@@ -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.
+1 -1
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
node_modules/
+3
View File
@@ -0,0 +1,3 @@
node_modules/
schema.ts
pnpm-lock.yaml
+7
View File
@@ -0,0 +1,7 @@
{
"trailingComma": "es5",
"printWidth": 120,
"tabWidth": 2,
"semi": true,
"singleQuote": true
}
+9
View File
@@ -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.
+16
View File
@@ -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.
+21
View File
@@ -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": "[email protected]"
}
+270
View File
@@ -0,0 +1,270 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
devDependencies:
openapi-typescript:
specifier: 7.10.1
version: 7.10.1([email protected])
prettier:
specifier: 3.8.3
version: 3.8.3
typescript:
specifier: 5.9.3
version: 5.9.3
packages:
'@babel/[email protected]':
resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
engines: {node: '>=6.9.0'}
'@babel/[email protected]':
resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
engines: {node: '>=6.9.0'}
'@redocly/[email protected]':
resolution: {integrity: sha512-EDtsGZS964mf9zAUXAl9Ew16eYbeyAFWhsPr0fX6oaJxgd8rApYlPBf0joyhnUHz88WxrigyFtTaqqzXNzPgqw==}
'@redocly/[email protected]':
resolution: {integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==}
'@redocly/[email protected]':
resolution: {integrity: sha512-2+O+riuIUgVSuLl3Lyh5AplWZyVMNuG2F98/o6NrutKJfW4/GTZdPpZlIphS0HGgcOHgmWcCSHj+dWFlZaGSHw==}
engines: {node: '>=18.17.0', npm: '>=9.5.0'}
[email protected]:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
[email protected]:
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
engines: {node: '>=6'}
[email protected]:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
[email protected]:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
[email protected]:
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
[email protected]:
resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
[email protected]:
resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==}
[email protected]:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
[email protected]:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
[email protected]:
resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
[email protected]:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
[email protected]:
resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==}
engines: {node: '>=18'}
[email protected]:
resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
engines: {node: '>=0.10.0'}
[email protected]:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
[email protected]:
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
hasBin: true
[email protected]:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
[email protected]:
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
engines: {node: '>=10'}
[email protected]:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
[email protected]:
resolution: {integrity: sha512-rBcU8bjKGGZQT4K2ekSTY2Q5veOQbVG/lTKZ49DeCyT9z62hM2Vj/LLHjDHC9W7LJG8YMHcdXpRZDqC1ojB/lw==}
hasBin: true
peerDependencies:
typescript: ^5.x
[email protected]:
resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==}
engines: {node: '>=18'}
[email protected]:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
[email protected]:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'}
[email protected]:
resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==}
engines: {node: '>=14'}
hasBin: true
[email protected]:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
[email protected]:
resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==}
engines: {node: '>=18'}
[email protected]:
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
engines: {node: '>=16'}
[email protected]:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
hasBin: true
[email protected]:
resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==}
[email protected]:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
snapshots:
'@babel/[email protected]':
dependencies:
'@babel/helper-validator-identifier': 7.29.7
js-tokens: 4.0.0
picocolors: 1.1.1
'@babel/[email protected]': {}
'@redocly/[email protected]':
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/[email protected]': {}
'@redocly/[email protected]([email protected])':
dependencies:
'@redocly/ajv': 8.17.1
'@redocly/config': 0.22.2
colorette: 1.4.0
https-proxy-agent: 7.0.6([email protected])
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
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]:
dependencies:
balanced-match: 1.0.2
[email protected]: {}
[email protected]: {}
[email protected]([email protected]):
dependencies:
ms: 2.1.3
optionalDependencies:
supports-color: 10.2.2
[email protected]: {}
[email protected]: {}
[email protected]([email protected]):
dependencies:
agent-base: 7.1.4
debug: 4.4.3([email protected])
transitivePeerDependencies:
- supports-color
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]:
dependencies:
argparse: 2.0.1
[email protected]: {}
[email protected]:
dependencies:
brace-expansion: 2.0.2
[email protected]: {}
[email protected]([email protected]):
dependencies:
'@redocly/openapi-core': 1.34.6([email protected])
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
[email protected]:
dependencies:
'@babel/code-frame': 7.29.7
index-to-position: 1.2.0
type-fest: 4.41.0
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
+1 -1
View File
@@ -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}...`);
@@ -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 });
}
});
-1
View File
@@ -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
+2 -3
View File
@@ -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.
@@ -239,7 +239,6 @@ export default [
'**/index.html',
'**/.yarn/',
'**/*.scss',
'src/services/api/schema.ts',
'.prettierrc.js',
'.storybook',
],
-1
View File
@@ -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',
-2
View File
@@ -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",
+18 -168
View File
@@ -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([email protected])
prettier:
specifier: ^3.8.3
version: 3.8.3
@@ -1437,16 +1434,6 @@ packages:
'@types/react':
optional: true
'@redocly/[email protected]':
resolution: {integrity: sha512-EDtsGZS964mf9zAUXAl9Ew16eYbeyAFWhsPr0fX6oaJxgd8rApYlPBf0joyhnUHz88WxrigyFtTaqqzXNzPgqw==}
'@redocly/[email protected]':
resolution: {integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==}
'@redocly/[email protected]':
resolution: {integrity: sha512-2+O+riuIUgVSuLl3Lyh5AplWZyVMNuG2F98/o6NrutKJfW4/GTZdPpZlIphS0HGgcOHgmWcCSHj+dWFlZaGSHw==}
engines: {node: '>=18.17.0', npm: '>=9.5.0'}
'@reduxjs/[email protected]':
resolution: {integrity: sha512-MYlOhQ0sLdw4ud48FoC5w0dH9VfWQjtCjreKwYTT3l+r427qYC5Y8PihNutepr8XrNaBUDQo9khWUwQxZaqt5A==}
peerDependencies:
@@ -2180,17 +2167,9 @@ packages:
[email protected]:
resolution: {integrity: sha512-oDzQ4VctavkqGiI1mFN9PIMjVrv0T6MpoIS6SAbzAQXCQs2b3UdyI9VQTrUQdfq9AaHQcR0cIX/hAZ7fyh7Cgw==}
[email protected]:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
[email protected]:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
[email protected]:
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
engines: {node: '>=6'}
[email protected]:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
@@ -2396,9 +2375,6 @@ packages:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
[email protected]:
resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
[email protected]:
resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==}
engines: {node: '>= 16'}
@@ -2438,9 +2414,6 @@ packages:
[email protected]:
resolution: {integrity: sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==}
[email protected]:
resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==}
[email protected]:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
@@ -2899,9 +2872,6 @@ packages:
[email protected]:
resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==}
[email protected]:
resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
[email protected]:
resolution: {integrity: sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==}
@@ -3143,10 +3113,6 @@ packages:
[email protected]:
resolution: {integrity: sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==}
[email protected]:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
[email protected]:
resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==}
@@ -3193,10 +3159,6 @@ packages:
resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
engines: {node: '>=8'}
[email protected]:
resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==}
engines: {node: '>=18'}
[email protected]:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
@@ -3384,10 +3346,6 @@ packages:
[email protected]:
resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==}
[email protected]:
resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
engines: {node: '>=0.10.0'}
[email protected]:
resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==}
@@ -3412,9 +3370,6 @@ packages:
[email protected]:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
[email protected]:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
[email protected]:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
@@ -3637,10 +3592,6 @@ packages:
[email protected]:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
[email protected]:
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
engines: {node: '>=10'}
[email protected]:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -3771,12 +3722,6 @@ packages:
[email protected]:
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
[email protected]:
resolution: {integrity: sha512-rBcU8bjKGGZQT4K2ekSTY2Q5veOQbVG/lTKZ49DeCyT9z62hM2Vj/LLHjDHC9W7LJG8YMHcdXpRZDqC1ojB/lw==}
hasBin: true
peerDependencies:
typescript: ^5.x
[email protected]:
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'}
[email protected]:
resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==}
engines: {node: '>=18'}
[email protected]:
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'}
[email protected]:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'}
[email protected]:
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'}
[email protected]:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
[email protected]:
resolution: {integrity: sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg==}
engines: {node: '>=0.10.5'}
@@ -4539,10 +4472,6 @@ packages:
[email protected]:
resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==}
[email protected]:
resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==}
engines: {node: '>=18'}
[email protected]:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
@@ -4977,9 +4906,6 @@ packages:
[email protected]:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
[email protected]:
resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==}
[email protected]:
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([email protected])
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([email protected])
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([email protected])
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([email protected])
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([email protected])
debug: 4.4.3
transitivePeerDependencies:
- supports-color
@@ -5806,7 +5732,7 @@ snapshots:
'@eslint/[email protected]':
dependencies:
'@eslint/object-schema': 2.1.7
debug: 4.4.3([email protected])
debug: 4.4.3
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -5822,7 +5748,7 @@ snapshots:
'@eslint/[email protected]':
dependencies:
ajv: 6.12.6
debug: 4.4.3([email protected])
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/[email protected]':
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/[email protected]': {}
'@redocly/[email protected]([email protected])':
dependencies:
'@redocly/ajv': 8.17.1
'@redocly/config': 0.22.2
colorette: 1.4.0
https-proxy-agent: 7.0.6([email protected])
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/[email protected]([email protected](@types/[email protected])([email protected])([email protected]))([email protected])':
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([email protected])
'@typescript-eslint/visitor-keys': 8.59.2
debug: 4.4.3([email protected])
debug: 4.4.3
eslint: 9.39.2([email protected])
typescript: 5.9.3
transitivePeerDependencies:
@@ -6716,7 +6619,7 @@ snapshots:
dependencies:
'@typescript-eslint/tsconfig-utils': 8.50.1([email protected])
'@typescript-eslint/types': 8.50.1
debug: 4.4.3([email protected])
debug: 4.4.3
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -6725,7 +6628,7 @@ snapshots:
dependencies:
'@typescript-eslint/tsconfig-utils': 8.59.2([email protected])
'@typescript-eslint/types': 8.59.2
debug: 4.4.3([email protected])
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([email protected])
'@typescript-eslint/utils': 8.59.2([email protected]([email protected]))([email protected])
debug: 4.4.3([email protected])
debug: 4.4.3
eslint: 9.39.2([email protected])
ts-api-utils: 2.5.0([email protected])
typescript: 5.9.3
@@ -6770,7 +6673,7 @@ snapshots:
'@typescript-eslint/tsconfig-utils': 8.50.1([email protected])
'@typescript-eslint/types': 8.50.1
'@typescript-eslint/visitor-keys': 8.50.1
debug: 4.4.3([email protected])
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([email protected])
'@typescript-eslint/types': 8.59.2
'@typescript-eslint/visitor-keys': 8.59.2
debug: 4.4.3([email protected])
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
[email protected]: {}
[email protected]:
dependencies:
fast-deep-equal: 3.1.3
@@ -6979,8 +6880,6 @@ snapshots:
json-schema-traverse: 0.4.1
uri-js: 4.4.1
[email protected]: {}
[email protected]: {}
[email protected]: {}
@@ -7221,8 +7120,6 @@ snapshots:
ansi-styles: 4.3.0
supports-color: 7.2.0
[email protected]: {}
[email protected]: {}
[email protected]: {}
@@ -7261,8 +7158,6 @@ snapshots:
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
@@ -7385,11 +7280,9 @@ snapshots:
dependencies:
ms: 2.1.3
[email protected]([email protected]):
[email protected]:
dependencies:
ms: 2.1.3
optionalDependencies:
supports-color: 10.2.2
[email protected]: {}
@@ -7487,7 +7380,7 @@ snapshots:
[email protected]:
dependencies:
'@socket.io/component-emitter': 3.1.2
debug: 4.4.3([email protected])
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([email protected])
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:
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]:
@@ -8104,13 +7995,6 @@ snapshots:
[email protected]: {}
[email protected]([email protected]):
dependencies:
agent-base: 7.1.4
debug: 4.4.3([email protected])
transitivePeerDependencies:
- supports-color
[email protected]: {}
[email protected]:
@@ -8146,8 +8030,6 @@ snapshots:
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]:
@@ -8334,8 +8216,6 @@ snapshots:
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
@@ -8352,8 +8232,6 @@ snapshots:
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]:
@@ -8555,10 +8433,6 @@ snapshots:
dependencies:
brace-expansion: 1.1.12
[email protected]:
dependencies:
brace-expansion: 2.0.2
[email protected]:
dependencies:
brace-expansion: 2.0.2
@@ -8684,16 +8558,6 @@ snapshots:
[email protected]: {}
[email protected]([email protected]):
dependencies:
'@redocly/openapi-core': 1.34.6([email protected])
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
[email protected]:
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
[email protected]:
dependencies:
'@babel/code-frame': 7.27.1
index-to-position: 1.2.0
type-fest: 4.41.0
[email protected]: {}
[email protected]: {}
@@ -8821,8 +8679,6 @@ snapshots:
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]:
@@ -9169,8 +9025,6 @@ snapshots:
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
@@ -9415,7 +9269,7 @@ snapshots:
[email protected]:
dependencies:
'@socket.io/component-emitter': 3.1.2
debug: 4.4.3([email protected])
debug: 4.4.3
engine.io-client: 6.6.4
socket.io-parser: 4.2.5
transitivePeerDependencies:
@@ -9426,7 +9280,7 @@ snapshots:
[email protected]:
dependencies:
'@socket.io/component-emitter': 3.1.2
debug: 4.4.3([email protected])
debug: 4.4.3
transitivePeerDependencies:
- supports-color
@@ -9582,8 +9436,6 @@ snapshots:
[email protected]: {}
[email protected]: {}
[email protected]:
dependencies:
has-flag: 4.0.0
@@ -9943,8 +9795,6 @@ snapshots:
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
@@ -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.
@@ -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<string, { properties?: { image_names?: ArraySchema } }> };
};
@@ -0,0 +1 @@
export type * from '../../../../api/schema';
+2
View File
@@ -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.
@@ -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);
+2 -1
View File
@@ -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*",
+1 -1
View File
@@ -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