Conversation
- Add ApiError class and ApiErrorCode enum in shared package
- Add Errors helper with factory methods for common error types
- Support error codes: AUTH_REQUIRED, AUTH_INVALID, AUTH_EXPIRED,
RATE_LIMITED, QUOTA_EXCEEDED, INVALID_*, PROVIDER_ERROR, TIMEOUT
- Update all providers (Gitee, HuggingFace, ModelScope) to throw typed errors
- Parse provider-specific error responses to detect auth/quota/rate limit issues
- Add sendError helper and toApiError converter in app.ts
- Update frontend API client to parse error codes and show user-friendly messages
- Error response format: { error: string, code: ApiErrorCode, details?: {...} }
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add global error handling with onError and notFound handlers - Add request logging middleware with request ID tracing - Add security headers (X-Content-Type-Options, X-Frame-Options, etc.) - Add timeout middleware (120s for generation endpoints) - Add body limit middleware (50KB) - Extract formatDimensions/formatDuration to utils/format.ts - Extract Gradio API helpers to utils/gradio.ts - Optimize CORS by pre-creating middleware instance - Update prompt max length from 10000 to 4000 characters 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add unit tests for utils (formatDimensions, formatDuration) - Add unit tests for error-handler middleware - Add Provider tests with mocked fetch (Gitee, HuggingFace) - Add route integration tests using Hono's app.request() - Add vitest.config.ts for API package - Upgrade vitest to v4.0.15 in root package.json - Remove duplicate vitest from api package (monorepo best practice) 107 tests covering validation, auth, error handling, and API routes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add vitest config with jsdom environment for web package - Add tests for lib/api.ts (error messages, API client) - Add tests for lib/utils.ts (cn utility, URL detection) - Add tests for lib/constants.ts (settings persistence, providers) - Add test setup with localStorage mock 55 tests covering core frontend utilities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Update response format to match actual code (imageDetails) - Add error response format with code and details - Add ModelScope to Chinese README - Update CHANGELOG with v0.5.1 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add section 6: Running Tests with commands and structure - Add section 8: Submitting a PR with pre-check commands - Add section 9: Commit Message Convention (Conventional Commits) - Document CI pipeline requirements 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Fix import order in vitest.config.ts - Fix type assertion in constants.test.ts - Format gitee.test.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Run tests with coverage (pnpm test:coverage) - Upload coverage to Codecov on main branch push - Keep existing lint, build, typecheck jobs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Deploy PR previews to Cloudflare Pages - Comment preview URL on PR automatically - Cancel in-progress deployments for same branch 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Manual trigger with version bump selection (patch/minor/major) - Auto-generate changelog from conventional commits - Create GitHub release with categorized changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Label PRs based on changed files (api, web, shared, docs, ci) - Label PRs based on title prefix (feat, fix, docs, test, etc.) - Add labeler configuration for file patterns 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Mark issues stale after 30 days, close after 7 more - Mark PRs stale after 14 days, close after 7 more - Exempt pinned, security, and help-wanted labels - Run daily at midnight UTC 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- CodeQL analysis for JavaScript/TypeScript - Dependency review on PRs (block high severity) - Run on push to main and weekly schedule 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Weekly dependency updates (Monday 9am Shanghai time) - Separate configs for root, api, web packages - Group dev dependencies and React/UI packages - Auto-label PRs with dependencies and scope 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- PR template with type/scope checklist - Bug report template with fix(scope) title format - Feature request template with feat(scope) title format - Disable blank issues, add docs/discussions links 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Deploy to Cloudflare Pages on main push - Deploy API to Cloudflare Workers (path filtered) - Docker build and push to GHCR on release 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Fix github.sha.substring() syntax in preview.yml - Remove explicit pnpm version (use packageManager from package.json) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add type definitions for API response objects in app.test.ts - Add required fields (model, width, height) to provider tests - Use defaultRequest pattern for cleaner test code 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Restrict CORS on the development server to prevent malicious websites from reading source files. This addresses the esbuild CVE pattern where `Access-Control-Allow-Origin: *` allows any site to fetch dev server files. Note: This only affects `vite dev`, not production builds or deployments. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
✅ Deploy Preview for frolicking-blancmange-ef6637 canceled.
|
WalkthroughAdds comprehensive GitHub CI/CD and repository metadata; introduces centralized API middleware (error handling, logging, security, timeouts, body-size limits, request IDs); standardizes provider error parsing and shared error types; adds Gradio SSE/queue and formatting utilities; extensive tests for API and web; web client error mapping; and documentation updates reflecting the new response shape and contributor workflow. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client
participant ReqID as RequestId MW
participant Logger as RequestLogger MW
participant Security as SecurityHeaders MW
participant BodyLimit as BodyLimit MW
participant Timeout as Timeout MW
participant Handler as Route Handler
participant Provider as Provider (Gitee/HF/ModelScope)
participant Ext as External API
participant ErrorH as ErrorHandler MW
Client->>ReqID: HTTP request (e.g., POST /api/generate)
ReqID->>Logger: next()
Logger->>Security: next()
Security->>BodyLimit: next()
BodyLimit->>Timeout: next()
Timeout->>Handler: next() (start timeout race)
Handler->>Provider: validate & call provider
Provider->>Ext: external API request
alt external success
Ext-->>Provider: image + metadata
Provider-->>Handler: normalized result
Handler-->>Client: 200 + { imageDetails }
else external error
Ext-->>Provider: error response
Provider-->>Handler: throw ApiError
Handler->>ErrorH: propagate
ErrorH-->>Client: JSON error (status from ErrorCodeToStatus)
end
alt timeout triggered
Timeout->>ErrorH: Errors.timeout()
ErrorH-->>Client: 504 timeout response
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Areas needing extra attention:
Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
zenith-image-generator-api | f0bae85 | Commit Preview URL | Dec 13 2025, 08:24 AM |
There was a problem hiding this comment.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
docs/en/API.md (1)
112-182: Clarify request-field naming + make error-code table explicitly complete or explicitly non-exhaustive.
Right now the request example is snake_case (negative_prompt,num_inference_steps) while the parameters table is mostly camelCase (negativePrompt,steps). Consider adding a short “Accepted field aliases” note and standardizing the table to one canonical naming scheme. Also, either add the remaining error codes you may return (e.g.,AUTH_EXPIRED,INVALID_PROVIDER,INVALID_MODEL,INVALID_PARAMS,GENERATION_FAILED,UNKNOWN) or call the table “common error codes” / “non-exhaustive”.docs/zh/API.md (1)
112-182: 同样建议:明确请求字段命名/别名,并说明错误码表是否“完整”。
示例里是 snake_case(negative_prompt,num_inference_steps),参数表里主要是 camelCase(negativePrompt,steps)。建议增加“支持的字段别名/规范字段名”说明并统一参数表口径;同时把可能返回的错误码补全,或注明该表为“常见/非完整列表”。
🧹 Nitpick comments (12)
docs/CHANGELOG.md (1)
3-21: Avoid hard-coding exact test counts in the changelog (likely to drift).“162 tests across 9 files” will become inaccurate quickly as tests evolve; consider wording like “Added comprehensive Vitest test coverage across API and web packages” (or keep the counts only if they’re intended to reflect the release artifact at tag time).
.github/labeler.yml (1)
24-37: Consider wideningci/testingglob coverage (root-only YAML, TSX tests).If the intent is to label YAML anywhere (not just repo-root), use
**/*.yml/**/*.yaml. Similarly, if you have React tests, consider including**/*.test.tsx/**/*.spec.tsx.apps/api/src/middleware/logger.ts (1)
11-26: Consider structured logging for production and document middleware order dependency.The current implementation has a few areas for improvement:
Middleware order dependency: The logger retrieves
requestIdusingc.get('requestId'), which assumes therequest-idmiddleware runs first. This dependency should be documented in the JSDoc comment.Production logging: Using
console.logwith emoji indicators works for development but isn't ideal for production. Consider:
- Using structured JSON logging for production environments
- Conditionally enabling emoji indicators based on environment
- Using proper log levels (info, warn, error)
Error context: If an error occurs during request processing, the logger still logs the response, but additional error context from the error handler might be valuable.
Example improvement:
/** * Simple request logger middleware - * Logs method, path, status, and duration + * Logs method, path, status, and duration + * Note: Must run after request-id middleware to include requestId */ export const requestLogger: MiddlewareHandler = async (c, next) => { const start = Date.now() const method = c.req.method const path = c.req.path await next() const duration = Date.now() - start const status = c.res.status const requestId = c.get('requestId') || '-' - // Color-coded status for development - const statusColor = status >= 500 ? '❌' : status >= 400 ? '⚠️' : '✅' - - console.log(`${statusColor} [${requestId}] ${method} ${path} ${status} ${duration}ms`) + // Use structured logging for production, emoji for dev + const isDev = process.env.NODE_ENV === 'development' + + if (isDev) { + const statusColor = status >= 500 ? '❌' : status >= 400 ? '⚠️' : '✅' + console.log(`${statusColor} [${requestId}] ${method} ${path} ${status} ${duration}ms`) + } else { + console.log(JSON.stringify({ + requestId, + method, + path, + status, + duration, + timestamp: new Date().toISOString() + })) + } }.github/workflows/preview.yml (1)
63-73: Consider pagination for repositories with many comments.The script uses
listCommentswithout pagination handling. For repositories with many PR comments, this could miss the bot comment if it's not in the first page of results.Consider adding pagination or limiting the search:
- const { data: comments } = await github.rest.issues.listComments({ + const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, + per_page: 100 });Alternatively, you could search from the end since the bot comment is likely recent, or use a more specific marker in the comment body to make searching more efficient.
apps/web/vitest.config.ts (1)
18-23: Consider including React components in coverage.The coverage configuration only includes
src/lib/**/*.tsandsrc/hooks/**/*.ts, which excludes React components from coverage reporting.If you have components in
src/components/**/*.tsxthat contain business logic, consider adding them to coverage:coverage: { provider: 'v8', reporter: ['text', 'json', 'html'], - include: ['src/lib/**/*.ts', 'src/hooks/**/*.ts'], + include: ['src/lib/**/*.ts', 'src/hooks/**/*.ts', 'src/components/**/*.tsx'], exclude: ['**/*.test.ts', '**/*.spec.ts'], },If components are purely presentational or tested via integration tests, the current configuration is appropriate.
.github/workflows/ci.yml (2)
57-63: Hardcoded coverage paths may break if output locations change.The coverage upload step specifies exact file paths:
files: ./apps/api/coverage/coverage-final.json,./apps/web/coverage/coverage-final.jsonIf the coverage output directory changes or additional packages are added, this will fail silently.
Consider using a glob pattern or script to discover coverage files:
- name: Upload coverage to Codecov uses: codecov/codecov-action@v4 if: github.event_name == 'push' && github.ref == 'refs/heads/main' with: token: ${{ secrets.CODECOV_TOKEN }} - files: ./apps/api/coverage/coverage-final.json,./apps/web/coverage/coverage-final.json + directory: ./ + flags: unittests fail_ci_if_error: falseThe codecov-action will automatically discover all coverage files when
directoryis specified.
21-89: Consider build artifact caching between jobs.Both
ciandtypecheckjobs install dependencies and build the shared package independently. This duplicates work and increases CI time.You could optimize by:
- Creating a setup job that builds shared and caches it
- Using workflow artifacts to share the build between jobs
Example:
jobs: setup: name: Setup & Build Shared runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 20 cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Build shared package run: pnpm build:shared - name: Cache build artifacts uses: actions/cache/save@v4 with: path: | packages/shared/dist node_modules key: build-${{ github.sha }} ci: needs: setup # ... restore cache and skip shared build typecheck: needs: setup # ... restore cache and skip shared buildHowever, this adds complexity, so only implement if CI time is a concern.
apps/api/vitest.config.ts (1)
3-17: Consider adding deterministic test defaults (timeouts / mock reset).
Config is solid; addingtestTimeoutandclearMocks(orrestoreMocks) can reduce flakes and cross-test coupling..github/workflows/security.yml (1)
31-48: Pin GitHub Actions to commit SHAs (supply-chain hardening).
Using floating tags (e.g.@v3,@v4) is convenient but less secure than SHA pinning.Also applies to: 54-62
.github/workflows/labeler.yml (1)
21-50: Pin actions + tighten conventional-commit title matching.
Consider SHA pinning foractions/labeler/actions/github-script, and use a small regex (case-insensitive) for^(feat|fix|docs|test|refactor|chore|perf)(\\(.+\\))?(!)?:to reduce false positives.apps/api/src/middleware/error-handler.ts (1)
48-56: Consider aligning with the ApiErrorCode enum.The
notFoundHandleruses the literal string'NOT_FOUND'(line 52) rather thanApiErrorCode.NOT_FOUND. IfNOT_FOUNDis not part of theApiErrorCodeenum, this creates an inconsistency with the standardized error system.Consider adding
NOT_FOUNDto theApiErrorCodeenum inpackages/shared/src/types/error.tsor document why 404 responses use a different error format than other API errors. Alternatively, useErrors.unknown()pattern or create a dedicatedErrors.notFound()factory method.#!/bin/bash # Description: Check if NOT_FOUND is defined in ApiErrorCode enum ast-grep --pattern 'export enum ApiErrorCode { $$$ }'packages/shared/src/types/error.ts (1)
72-92: Consider addingError.captureStackTracefor cleaner stack traces.While the current implementation correctly extends
Errorand callssuper(), you could optionally addError.captureStackTrace(this, ApiError)in the constructor to exclude the constructor frame from the stack trace in Node.js environments. This is a nice-to-have enhancement for debugging but not critical since modern JS already captures the stack properly.constructor(message: string, code: ApiErrorCode, details?: ApiErrorResponse['details']) { super(message) this.name = 'ApiError' this.code = code this.statusCode = ErrorCodeToStatus[code] this.details = details + + // Maintain proper stack trace for Node.js + if (Error.captureStackTrace) { + Error.captureStackTrace(this, ApiError) + } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (58)
.github/ISSUE_TEMPLATE/bug_report.md(1 hunks).github/ISSUE_TEMPLATE/config.yml(1 hunks).github/ISSUE_TEMPLATE/feature_request.md(1 hunks).github/PULL_REQUEST_TEMPLATE.md(1 hunks).github/dependabot.yml(1 hunks).github/labeler.yml(1 hunks).github/workflows/ci.yml(1 hunks).github/workflows/deploy-cf-pages.yml(1 hunks).github/workflows/deploy-cf-workers.yml(1 hunks).github/workflows/docker.yml(1 hunks).github/workflows/labeler.yml(1 hunks).github/workflows/preview.yml(1 hunks).github/workflows/release.yml(1 hunks).github/workflows/security.yml(1 hunks).github/workflows/stale.yml(1 hunks).gitignore(0 hunks)README.zh.md(1 hunks)apps/api/package.json(1 hunks)apps/api/src/__tests__/app.test.ts(1 hunks)apps/api/src/__tests__/setup.ts(1 hunks)apps/api/src/app.ts(11 hunks)apps/api/src/middleware/__tests__/error-handler.test.ts(1 hunks)apps/api/src/middleware/body-limit.ts(1 hunks)apps/api/src/middleware/error-handler.ts(1 hunks)apps/api/src/middleware/index.ts(1 hunks)apps/api/src/middleware/logger.ts(1 hunks)apps/api/src/middleware/request-id.ts(1 hunks)apps/api/src/middleware/security.ts(1 hunks)apps/api/src/middleware/timeout.ts(1 hunks)apps/api/src/providers/__tests__/gitee.test.ts(1 hunks)apps/api/src/providers/__tests__/huggingface.test.ts(1 hunks)apps/api/src/providers/gitee.ts(3 hunks)apps/api/src/providers/huggingface.ts(4 hunks)apps/api/src/providers/modelscope.ts(3 hunks)apps/api/src/utils/__tests__/format.test.ts(1 hunks)apps/api/src/utils/format.ts(1 hunks)apps/api/src/utils/gradio.ts(1 hunks)apps/api/src/utils/index.ts(1 hunks)apps/api/vitest.config.ts(1 hunks)apps/web/package.json(1 hunks)apps/web/src/__tests__/setup.ts(1 hunks)apps/web/src/lib/__tests__/api.test.ts(1 hunks)apps/web/src/lib/__tests__/constants.test.ts(1 hunks)apps/web/src/lib/__tests__/utils.test.ts(1 hunks)apps/web/src/lib/api.ts(4 hunks)apps/web/vite.config.ts(1 hunks)apps/web/vitest.config.ts(1 hunks)docs/CHANGELOG.md(1 hunks)docs/en/API.md(4 hunks)docs/en/CONTRIBUTING.md(2 hunks)docs/zh/API.md(4 hunks)docs/zh/CONTRIBUTING.md(2 hunks)package.json(1 hunks)packages/shared/src/constants/models.ts(7 hunks)packages/shared/src/types/error.ts(1 hunks)packages/shared/src/types/index.ts(1 hunks)packages/shared/src/utils/validation.test.ts(1 hunks)packages/shared/src/utils/validation.ts(1 hunks)
💤 Files with no reviewable changes (1)
- .gitignore
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx,json}
📄 CodeRabbit inference engine (CLAUDE.md)
Use Biome for all linting and formatting operations (pnpm lint, pnpm format, pnpm check)
Files:
apps/api/src/middleware/security.tsapps/api/src/middleware/timeout.tsapps/api/src/middleware/__tests__/error-handler.test.tsapps/api/vitest.config.tsapps/api/src/middleware/index.tsapps/api/src/middleware/error-handler.tsapps/api/src/middleware/request-id.tsapps/api/src/middleware/body-limit.tsapps/web/src/__tests__/setup.tsapps/web/package.jsonapps/api/src/utils/index.tsapps/web/src/lib/__tests__/api.test.tsapps/api/src/utils/format.tsapps/web/vitest.config.tsapps/web/src/lib/__tests__/constants.test.tsapps/api/src/providers/__tests__/gitee.test.tspackages/shared/src/types/error.tsapps/api/src/__tests__/app.test.tsapps/api/src/providers/__tests__/huggingface.test.tsapps/api/src/utils/gradio.tsapps/api/src/providers/huggingface.tspackages/shared/src/utils/validation.tsapps/web/src/lib/__tests__/utils.test.tsapps/api/src/middleware/logger.tsapps/api/src/utils/__tests__/format.test.tsapps/api/package.jsonapps/api/src/providers/modelscope.tsapps/web/vite.config.tsapps/api/src/providers/gitee.tspackages/shared/src/constants/models.tspackages/shared/src/types/index.tspackages/shared/src/utils/validation.test.tsapps/api/src/__tests__/setup.tspackage.jsonapps/api/src/app.tsapps/web/src/lib/api.ts
apps/api/src/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
apps/api/src/**/*.ts: POST /api/generate endpoint must support 'gitee' and 'huggingface' as provider parameters, require X-API-Key header for Gitee AI, and support optional X-HF-Token header for HuggingFace
Use Hono API framework for Cloudflare Workers implementation in the API app (apps/api)
Files:
apps/api/src/middleware/security.tsapps/api/src/middleware/timeout.tsapps/api/src/middleware/__tests__/error-handler.test.tsapps/api/src/middleware/index.tsapps/api/src/middleware/error-handler.tsapps/api/src/middleware/request-id.tsapps/api/src/middleware/body-limit.tsapps/api/src/utils/index.tsapps/api/src/utils/format.tsapps/api/src/providers/__tests__/gitee.test.tsapps/api/src/__tests__/app.test.tsapps/api/src/providers/__tests__/huggingface.test.tsapps/api/src/utils/gradio.tsapps/api/src/providers/huggingface.tsapps/api/src/middleware/logger.tsapps/api/src/utils/__tests__/format.test.tsapps/api/src/providers/modelscope.tsapps/api/src/providers/gitee.tsapps/api/src/__tests__/setup.tsapps/api/src/app.ts
apps/web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/web/src/**/*.{ts,tsx}: Use @/ path alias for imports in frontend code (maps to src/)
Persist settings and API keys to localStorage with AES-256-GCM encryption (use src/lib/crypto.ts)
Persist Flow mode state using IndexedDB storage (via src/lib/flow-storage.ts) for nodes, edges, and images
Configure API URL via VITE_API_URL environment variable, defaulting to relative path for same-origin deployment
Files:
apps/web/src/__tests__/setup.tsapps/web/src/lib/__tests__/api.test.tsapps/web/src/lib/__tests__/constants.test.tsapps/web/src/lib/__tests__/utils.test.tsapps/web/src/lib/api.ts
apps/web/src/**/*.{tsx,ts}
📄 CodeRabbit inference engine (CLAUDE.md)
Use React 19 with Vite, Tailwind CSS, and shadcn/ui components in the frontend (apps/web)
Files:
apps/web/src/__tests__/setup.tsapps/web/src/lib/__tests__/api.test.tsapps/web/src/lib/__tests__/constants.test.tsapps/web/src/lib/__tests__/utils.test.tsapps/web/src/lib/api.ts
🧠 Learnings (12)
📚 Learning: 2025-12-12T02:11:01.015Z
Learnt from: CR
Repo: WuMingDao/zenith-image-generator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-12T02:11:01.015Z
Learning: Applies to apps/api/src/**/*.ts : Use Hono API framework for Cloudflare Workers implementation in the API app (apps/api)
Applied to files:
apps/api/src/middleware/security.tsapps/api/src/middleware/error-handler.tsapps/api/src/middleware/request-id.tsapps/api/src/__tests__/app.test.tsapps/api/package.jsonapps/api/src/app.ts
📚 Learning: 2025-12-12T02:11:01.015Z
Learnt from: CR
Repo: WuMingDao/zenith-image-generator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-12T02:11:01.015Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Configure API URL via VITE_API_URL environment variable, defaulting to relative path for same-origin deployment
Applied to files:
apps/api/vitest.config.tsapps/web/src/__tests__/setup.tsapps/web/vitest.config.tsapps/api/package.jsonapps/web/vite.config.tsapps/api/src/__tests__/setup.tsapps/web/src/lib/api.ts
📚 Learning: 2025-12-12T02:11:01.015Z
Learnt from: CR
Repo: WuMingDao/zenith-image-generator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-12T02:11:01.015Z
Learning: Run tests using Vitest with 'pnpm test' for watch mode or 'pnpm test:run' for single run, and use 'pnpm test:coverage' for coverage reports
Applied to files:
apps/api/vitest.config.tsapps/web/src/__tests__/setup.tsapps/web/vitest.config.tsdocs/zh/CONTRIBUTING.mdapps/api/package.jsondocs/en/CONTRIBUTING.mdapps/api/src/__tests__/setup.tspackage.json
📚 Learning: 2025-12-12T02:11:01.015Z
Learnt from: CR
Repo: WuMingDao/zenith-image-generator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-12T02:11:01.015Z
Learning: Applies to apps/api/src/**/*.ts : POST /api/generate endpoint must support 'gitee' and 'huggingface' as provider parameters, require X-API-Key header for Gitee AI, and support optional X-HF-Token header for HuggingFace
Applied to files:
README.zh.mdapps/web/src/lib/__tests__/api.test.tsapps/api/src/providers/__tests__/gitee.test.tsapps/api/src/__tests__/app.test.tsapps/api/src/providers/__tests__/huggingface.test.tsapps/api/src/providers/huggingface.tsapps/api/src/providers/modelscope.tsapps/api/src/providers/gitee.tsdocs/zh/API.mdapps/api/src/app.ts
📚 Learning: 2025-12-12T02:11:01.015Z
Learnt from: CR
Repo: WuMingDao/zenith-image-generator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-12T02:11:01.015Z
Learning: Deploy API to Cloudflare Workers using 'wrangler deploy --minify src/index.ts' from apps/api directory
Applied to files:
.github/workflows/deploy-cf-pages.yml.github/workflows/deploy-cf-workers.ymlapps/api/package.json
📚 Learning: 2025-12-12T02:11:01.015Z
Learnt from: CR
Repo: WuMingDao/zenith-image-generator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-12T02:11:01.015Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Persist settings and API keys to localStorage with AES-256-GCM encryption (use src/lib/crypto.ts)
Applied to files:
apps/web/src/__tests__/setup.tsapps/web/src/lib/__tests__/constants.test.ts
📚 Learning: 2025-12-12T02:11:01.015Z
Learnt from: CR
Repo: WuMingDao/zenith-image-generator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-12T02:11:01.015Z
Learning: Applies to apps/web/.env* : Set VITE_API_URL=http://localhost:8787 in apps/web/.env for local development when running frontend and API separately
Applied to files:
apps/web/src/__tests__/setup.tsdocs/zh/CONTRIBUTING.mddocs/en/CONTRIBUTING.mdapps/web/vite.config.ts
📚 Learning: 2025-12-12T02:11:01.015Z
Learnt from: CR
Repo: WuMingDao/zenith-image-generator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-12T02:11:01.015Z
Learning: Applies to apps/web/src/**/*.{tsx,ts} : Use React 19 with Vite, Tailwind CSS, and shadcn/ui components in the frontend (apps/web)
Applied to files:
apps/web/src/__tests__/setup.tsapps/web/package.jsonapps/web/vitest.config.tsapps/api/package.jsonapps/web/vite.config.tspackage.json
📚 Learning: 2025-12-12T02:11:01.015Z
Learnt from: CR
Repo: WuMingDao/zenith-image-generator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-12T02:11:01.015Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Use @/ path alias for imports in frontend code (maps to src/)
Applied to files:
apps/web/vitest.config.ts
📚 Learning: 2025-12-12T02:11:01.015Z
Learnt from: CR
Repo: WuMingDao/zenith-image-generator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-12T02:11:01.015Z
Learning: Implement provider abstraction pattern for API: define provider interface in apps/api/src/providers/types.ts, implement specific providers in individual files, and register/lookup providers in apps/api/src/providers/registry.ts
Applied to files:
apps/api/src/__tests__/app.test.tsapps/api/src/providers/modelscope.tspackages/shared/src/constants/models.ts
📚 Learning: 2025-12-12T02:11:01.015Z
Learnt from: CR
Repo: WuMingDao/zenith-image-generator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-12T02:11:01.015Z
Learning: Run development with 'pnpm dev' for all apps or use app-specific commands: pnpm dev:web for frontend (port 5173) or pnpm dev:api for API (port 8787)
Applied to files:
docs/zh/CONTRIBUTING.mddocs/en/CONTRIBUTING.md
📚 Learning: 2025-12-12T02:11:01.015Z
Learnt from: CR
Repo: WuMingDao/zenith-image-generator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-12T02:11:01.015Z
Learning: Applies to **/*.{ts,tsx,js,jsx,json} : Use Biome for all linting and formatting operations (pnpm lint, pnpm format, pnpm check)
Applied to files:
package.json
🧬 Code graph analysis (13)
apps/api/src/middleware/timeout.ts (1)
packages/shared/src/types/error.ts (1)
Errors(95-156)
apps/api/src/middleware/error-handler.ts (2)
packages/shared/src/types/error.ts (3)
ApiError(72-92)Errors(95-156)ApiErrorResponse(53-69)apps/api/src/middleware/request-id.ts (1)
requestId(18-27)
apps/api/src/middleware/body-limit.ts (1)
apps/api/src/middleware/error-handler.ts (1)
sendError(28-32)
apps/web/src/lib/__tests__/api.test.ts (2)
apps/web/src/lib/api.ts (3)
getErrorMessage(45-69)generateImage(91-143)upscaleImage(148-188)apps/api/src/index.ts (1)
fetch(17-21)
apps/web/src/lib/__tests__/constants.test.ts (1)
apps/web/src/lib/constants.ts (5)
loadSettings(49-56)STORAGE_KEY(47-47)saveSettings(58-60)PROVIDER_OPTIONS(63-67)getDefaultModel(70-73)
apps/api/src/providers/__tests__/huggingface.test.ts (2)
apps/api/src/providers/types.ts (1)
ProviderGenerateRequest(8-10)apps/api/src/providers/huggingface.ts (1)
HuggingFaceProvider(147-178)
apps/api/src/providers/huggingface.ts (1)
packages/shared/src/types/error.ts (1)
Errors(95-156)
apps/web/src/lib/__tests__/utils.test.ts (1)
apps/web/src/lib/utils.ts (1)
cn(4-6)
apps/api/src/middleware/logger.ts (1)
apps/api/src/middleware/request-id.ts (1)
requestId(18-27)
apps/api/src/utils/__tests__/format.test.ts (1)
apps/api/src/utils/format.ts (2)
formatDimensions(16-21)formatDuration(28-30)
apps/api/src/providers/modelscope.ts (1)
packages/shared/src/types/error.ts (1)
Errors(95-156)
apps/api/src/providers/gitee.ts (2)
packages/shared/src/types/error.ts (1)
Errors(95-156)apps/api/src/providers/types.ts (3)
ImageProvider(21-28)ProviderGenerateRequest(8-10)ProviderGenerateResult(13-18)
apps/web/src/lib/api.ts (1)
packages/shared/src/types/error.ts (1)
ApiErrorResponse(53-69)
🪛 markdownlint-cli2 (0.18.1)
docs/en/CONTRIBUTING.md
161-161: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Workers Builds: zenith-image-generator-api
- GitHub Check: Cloudflare Pages
🔇 Additional comments (84)
docs/CHANGELOG.md (1)
14-18: Changelog “Changed” items look consistent and user-relevant..github/workflows/docker.yml (1)
109-110: Verify VITE_API_URL configuration for the Web image.The Web build passes
VITE_API_URL=as an empty string. Confirm this is intentional and that the API endpoint is configured elsewhere (via environment variables, config file, or runtime injection). If the Web image requires a valid API URL to function properly, this should be set during the build or documented as a runtime requirement.packages/shared/src/constants/models.ts (4)
5-5: LGTM!The switch to single quotes is consistent with Biome formatting standards.
8-148: LGTM!The formatting changes (single quotes, trailing semicolon removal) align with Biome standards. The addition of
guidanceScaleconfigurations for FLUX models enhances the model definitions with appropriate parameter constraints.
151-153: LGTM!The arrow function formatting is consistent and follows Biome standards. The logic remains correct.
Also applies to: 161-166
95-103: TheguidanceScaleomission in the HuggingFace provider'sflux-1-schnellmodel configuration is intentional and accurate. The HuggingFace Gradio API endpoint for this model has a fixed parameter structure[prompt, seed, false, width, height, steps]that does not include guidance scale, unlike the Gitee provider which supports it through its API. The configuration correctly reflects the actual capabilities of each provider.packages/shared/src/types/index.ts (1)
9-11: LGTM!The error type exports follow the established barrel export pattern and provide clean access to shared error utilities.
apps/api/src/utils/index.ts (1)
1-6: LGTM!Clean utility barrel export that centralizes access to format and gradio utilities.
apps/api/package.json (2)
10-12: LGTM!Test scripts follow the project conventions and align with the Vitest testing approach documented in learnings.
Based on learnings, these commands support watch mode (
pnpm test), single run (pnpm test:run), and coverage reports (pnpm test:coverage).
22-27: TypeScript is correctly inherited from workspace root.Verified: TypeScript ^5.7.2 is available at the root package.json and is accessible to apps/api via pnpm workspace hoisting (default behavior with no .npmrc). The CI pipeline successfully builds and type-checks the API package (
pnpm build:apiandpnpm -r exec tsc --noEmit), confirming the build works as intended.docs/en/CONTRIBUTING.md (2)
55-84: Excellent testing documentation!The new testing section provides clear guidance on running tests, test structure, and writing practices. This aligns well with the Vitest setup introduced in this PR and matches the project's testing conventions.
Based on learnings, these test commands are correct for Vitest usage.
126-199: Great additions for PR workflow and commit conventions!The new sections provide clear guidance for contributors on:
- Pre-submission checks to run locally
- CI pipeline expectations
- Commit message conventions with practical examples
This will help maintain code quality and consistency across contributions.
.github/workflows/stale.yml (1)
1-67: LGTM!The stale bot configuration is well-structured with:
- Appropriate timeouts (30 days for issues, 14 days for PRs)
- Clear messaging for contributors
- Sensible exempt labels (pinned, security, work in progress)
- Auto-removal of stale label on updates
This will help maintain repository hygiene without being overly aggressive.
apps/web/src/lib/__tests__/utils.test.ts (1)
12-62: Excellent test coverage for cn() utility!The tests comprehensively cover:
- Basic merging and conditional classes
- Falsy value filtering
- Tailwind class deduplication via tailwind-merge
- Object and array syntax
- Edge cases (empty input, complex scenarios)
This provides strong validation of the className merging behavior.
apps/web/src/lib/__tests__/constants.test.ts (4)
91-121: LGTM!Comprehensive validation of the ASPECT_RATIOS constant. The tests ensure all required ratios are present, have proper structure, and maintain expected properties (icons, presets, square dimensions for 1:1).
123-148: LGTM!Provider options are correctly validated, including the new ModelScope provider. The
requiresAuthflags align with the coding guidelines (HuggingFace doesn't require auth, Gitee and ModelScope do).
150-183: LGTM!Good defensive testing with the type assertion on Line 169 to verify runtime fallback behavior. The tests ensure
getDefaultModelhandles all providers including unknown ones, and thatSTORAGE_KEYremains stable.
16-58: The test correctly focuses on non-sensitive settings storage. API keys and tokens are persisted separately viasrc/lib/crypto.tsusing AES-256-GCM encryption (accessed throughencryptAndStoreToken()anddecryptTokenFromStore()), not through theloadSettings/saveSettingsfunctions tested here. Thesettingsobject contains display preferences (prompt, dimensions, model selection) but not actual authentication credentials—those are managed independently with encryption.docs/zh/CONTRIBUTING.md (2)
55-84: LGTM!Clear and accurate testing documentation. The commands align with project conventions, and the guidance about mocking external APIs is helpful for contributors.
126-200: LGTM!Excellent documentation of the PR workflow and commit conventions. The Conventional Commits format is properly explained with relevant examples that match the project structure.
apps/api/src/providers/modelscope.ts (3)
12-58: LGTM!Well-structured error parsing that maps ModelScope API responses to standardized error types. The fallback chain for extracting error messages is robust, and the status/message-based categorization covers common API error scenarios.
99-115: LGTM!Robust response handling with proper error parsing and graceful JSON parse failure handling. The check for missing image URL before returning is defensive and appropriate.
73-75: Remove arbitrary token length validation or implement format validation.The 8-character minimum length check is inconsistent with ModelScope's actual token format (
ms-prefix followed by a UUID, typically ~39 characters). This validation:
- Does not align with other providers (Gitee and HuggingFace have no length checks)
- Only partially validates the token, catching obviously short strings but missing format requirements
- Should either be removed for consistency or expanded to validate the actual
ms-UUIDformatREADME.zh.md (1)
23-23: LGTM!Correctly updates the feature list to include ModelScope alongside the existing Gitee AI and HuggingFace providers, consistent with the new provider implementation in this PR.
packages/shared/src/utils/validation.test.ts (1)
32-37: LGTM!Tests correctly updated to validate the new 4,000-character limit. Consistent with the changes in
validation.ts..github/ISSUE_TEMPLATE/config.yml (1)
1-8: LGTM!Standard GitHub issue template configuration that appropriately directs users to documentation and discussions before opening issues. The contact links are correctly formatted.
apps/web/vite.config.ts (1)
8-12: The CORS restriction does not break the recommended development setup.The frontend makes API requests using the
VITE_API_URLenvironment variable configured in.env, which defaults tohttp://localhost:8787for local development. These fetch calls go directly to the API server, not through the Vite dev server. Thecors: falsesetting in Vite only restricts static asset serving from the dev server itself—it does not prevent fetch calls to external origins. CORS for API requests is properly handled on the API server side via theCORS_ORIGINSconfiguration inwrangler.toml, as documented inCONTRIBUTING.md(LAN Access section). The recommended setup is working as intended with the current configuration.Likely an incorrect or invalid review comment.
apps/api/src/__tests__/setup.ts (1)
6-16: Mock cleanup looks good; keepconsole.errorsuppression scoped if enabled.If you later uncomment the
console.errorspy, prefer scoping it to specific tests/suites to avoid masking real failures.apps/api/src/utils/__tests__/format.test.ts (1)
9-61: Good coverage for current formatting behavior.Optional: the
1000ms -> 1.0sassertion is duplicated across two tests; you can keep it or drop one for concision..github/ISSUE_TEMPLATE/feature_request.md (1)
1-35: Template looks clean and actionable..github/ISSUE_TEMPLATE/bug_report.md (1)
1-42: Solid bug-report template..github/PULL_REQUEST_TEMPLATE.md (1)
1-36: PR template is clear and consistent with the toolchain.apps/web/package.json (1)
38-49: No action needed. All versions are compatible.The dependencies in
apps/web/package.json(Vite 7.2.4, @vitest/browser 4.0.15, jsdom 27.3.0) are mutually compatible. Both jsdom@27 and @vitest/browser@4 require Node >= 20.19.0, and your CI uses Node 20 (which defaults to the latest 20.x release, meeting the >= 20.19.0 requirement). Vitest 4 was released alongside Vite 7 support, and there are no known peer dependency conflicts. No changes required.apps/web/src/__tests__/setup.ts (1)
1-52: LGTM! Solid test setup with proper mocking.The test setup correctly:
- Mocks localStorage with all required methods (getItem, setItem, removeItem, clear, length, key)
- Provides proper null returns for missing keys
- Stubs import.meta.env with environment variables
- Clears mocks between tests to prevent test contamination
- Restores mocks after each test
This aligns with the project's testing guidelines and provides a clean test environment for web package tests.
.github/workflows/deploy-cf-pages.yml (1)
1-49: LGTM! Clean Cloudflare Pages deployment workflow.The workflow correctly:
- Triggers on pushes to main and manual dispatch
- Sets appropriate permissions (contents: read, deployments: write)
- Uses frozen lockfile for reproducible builds
- Builds dependencies in correct order (shared → web)
- Deploys the web distribution to Cloudflare Pages
apps/api/src/middleware/index.ts (1)
1-10: LGTM (clean middleware barrel).
Nice consolidation point forapp.tswiring and imports.apps/api/src/middleware/__tests__/error-handler.test.ts (3)
9-71: LGTM! Comprehensive test coverage for toApiError.The test suite thoroughly covers ApiError passthrough, Error-to-ApiError conversion (including timeout detection), and unknown type handling. The assertions correctly validate code mapping, message preservation, and details propagation.
73-117: LGTM! ApiError class tests are well-structured.Constructor and toResponse tests validate property initialization, status code mappings, and response structure with proper handling of optional details.
119-151: LGTM! Errors factory tests validate all key factory methods.Tests correctly verify error code assignment, message composition, and details propagation for each factory method.
.github/dependabot.yml (3)
8-31: LGTM! Root package Dependabot configuration is well-structured.The dev-dependencies grouping with minor/patch update types is appropriate for development tooling, and the PR limit of 10 provides flexibility for multiple dependency streams.
33-46: LGTM! API package configuration is appropriate.The lower PR limit and scoped commit prefix align well with app-specific dependency management.
74-86: LGTM! GitHub Actions configuration is appropriate.The configuration properly targets workflow files with suitable labeling and commit prefixes.
apps/api/src/providers/huggingface.ts (1)
66-80: LGTM! Error handling is now consistent with the centralized error system.All error paths properly use the
Errorsfactory orparseHuggingFaceError, ensuring structured error responses. The SSE response truncation to 200 characters (line 79) prevents excessive logging.Also applies to: 96-102, 170-171
apps/api/src/utils/format.ts (3)
8-10: LGTM! Classic Euclidean algorithm implementation.The recursive GCD implementation is correct and efficient for computing the greatest common divisor.
16-21: LGTM! Dimension formatting with aspect ratio is correctly implemented.The function properly computes and displays the reduced aspect ratio using the GCD helper.
28-29: LGTM! Duration formatting is concise and correct.The function appropriately formats durations with one decimal place for seconds and plain milliseconds for sub-second durations.
.github/workflows/release.yml (5)
30-46: LGTM! Setup steps are configured correctly.Full history checkout, pnpm setup, and frozen lockfile installation are appropriate for a release workflow.
48-64: LGTM! Version bumping is correctly implemented.The workflow properly configures git, reads the current version, and bumps it using
npm versionwith--no-git-tag-versionto defer tag creation.
66-96: Verify changelog categorization excludes duplicates.The commit categorization (lines 78-84) filters commits by prefix, but the "Other" category (line 84) excludes all categorized prefixes. However, if a commit matches multiple categories (though unlikely with conventional commits), it might appear multiple times.
The current logic should work correctly for well-formed conventional commits, but consider whether commits can match multiple patterns. The
grep -vEon line 84 properly excludes all known prefixes for "Other".
98-107: LGTM! Git commit and tag operations are properly sequenced.The workflow correctly commits the version bump, creates an annotated tag, and pushes both to the repository.
109-126: LGTM! GitHub release creation is properly configured.The release step correctly uses the GitHub API to create a release with changelog and comparison link. Note that all releases are created as non-draft, non-prerelease by default—workflow modifications would be needed for pre-releases.
apps/api/src/middleware/error-handler.ts (3)
11-23: LGTM! Error normalization logic is comprehensive.The function correctly handles ApiError passthrough, timeout detection, and unknown error fallbacks with appropriate message preservation.
37-43: LGTM! Global error handler integrates well with request ID middleware.The handler properly logs errors with request context and delegates to the standardized error response utility.
28-32: The status code type assertion on line 31 is correct and complete. The union400 | 401 | 429 | 500 | 502 | 504covers all possible status codes fromErrorCodeToStatus: 400 (validation errors), 401 (auth errors), 429 (rate limiting), 500 (generation failures and unknown errors), 502 (provider/upstream errors), and 504 (timeouts). No additional status codes exist in the mapping.apps/web/src/lib/api.ts (5)
19-29: LGTM! Error types provide good client-side error handling structure.The
ApiErrorInfointerface properly aligns with the sharedApiErrorResponsetype, and the discriminatedApiResponseunion enables type-safe error handling.
32-42: LGTM! Error response parsing is defensive and correct.The function safely handles unknown API error payloads with appropriate type guards and fallbacks.
45-69: LGTM! User-friendly error messages are well-crafted.The function provides clear, actionable messages for each error code with appropriate context from error details (provider, retryAfter, upstream, etc.).
127-134: LGTM! generateImage error handling is now centralized and structured.The updated error handling properly parses API errors, generates user-friendly messages, and returns structured error information.
172-179: LGTM! upscaleImage error handling follows the same improved pattern.Consistent error handling with
generateImageimproves code maintainability and user experience.apps/web/src/lib/__tests__/api.test.ts (3)
11-139: LGTM! Comprehensive test coverage for getErrorMessage.Tests thoroughly validate all error code mappings, provider name handling, retry-after values, and fallback behavior. The mix of substring and exact assertions is appropriate.
141-256: LGTM! generateImage tests cover all key scenarios.Tests validate success paths, error handling, network failures, and authentication header inclusion with proper mocking and assertions.
258-313: LGTM! upscaleImage tests validate core functionality.Tests cover successful upscaling, HuggingFace token authentication, and error handling with appropriate assertions.
apps/api/src/providers/__tests__/gitee.test.ts (3)
1-55: Well-structured test setup and auth validation coverage.The test file properly validates that
AUTH_REQUIREDis thrown for both missing and empty tokens, which aligns with the coding guidelines requiring X-API-Key header for Gitee AI. The setup/teardown pattern withvi.stubGlobalandvi.unstubAllGlobalsis clean.
57-174: Comprehensive successful request tests with good edge case coverage.The tests thoroughly verify:
- Correct API endpoint and HTTP method
- Request body structure including all optional fields
- Auth token whitespace trimming (Line 159-173) - this is a useful defensive check
- Random seed generation bounds
176-278: Thorough error handling coverage.The error mapping tests cover the expected scenarios:
- 401 →
AUTH_INVALID- 429 →
RATE_LIMITEDorQUOTA_EXCEEDEDbased on message content- 500 →
PROVIDER_ERROR- JSON parse errors and malformed responses
The comment on Lines 216-217 documenting the expired token behavior is helpful for future maintainers.
apps/api/src/providers/__tests__/huggingface.test.ts (4)
11-17: Correct default request without auth token.The
defaultRequestintentionally omitsauthToken, which correctly reflects that HuggingFace provider doesn't require authentication per the coding guidelines (optional X-HF-Token header).
40-84: Clean helper functions for mocking Gradio API flow.The helper functions (
mockGradioSuccess,mockGradioQueueError,mockGradioSSEError) encapsulate the two-step Gradio queue mechanism (queue request → SSE result), making tests more readable and maintainable.
181-289: Comprehensive error handling tests.Good coverage of various failure modes:
- Queue request failures (500, 429, 401, 503)
- SSE error events
- Missing
event_id- Empty/unexpected response formats
- Timeout errors (504)
222-231: Test correctly uses 402 as a test fixture to isolate quota error handling.The test does not claim HuggingFace returns HTTP 402. According to official HuggingFace documentation, quota/rate-limit errors return HTTP 429. The test deliberately uses 402 to verify that the error handler correctly identifies "quota exceeded" messages without triggering the 429 → RATE_LIMITED check that happens first in
parseHuggingFaceError(). The existing comment adequately explains this rationale.apps/api/src/__tests__/app.test.ts (5)
12-60: Well-defined response type interfaces.The TypeScript interfaces for API responses provide type safety for test assertions and serve as documentation for the expected response shapes.
266-305: Authentication tests align with coding guidelines.The tests correctly verify:
- Gitee provider requires
X-API-Keyheader (returns 401 without it)- HuggingFace provider works without authentication
This aligns with the coding guidelines: "require X-API-Key header for Gitee AI, and support optional X-HF-Token header for HuggingFace."
Based on learnings, the POST /api/generate endpoint correctly supports both 'gitee' and 'huggingface' as provider parameters.
368-391: Good test for default provider behavior.The test verifies that when no provider is specified, the API defaults to 'gitee' and calls the Gitee API endpoint. This ensures backward compatibility.
422-457: Legacy endpoint backward compatibility tests.Good coverage for the
/api/generate-hflegacy endpoint, testing both successful generation and prompt validation. This ensures existing clients continue to work.
459-494: Upscale endpoint security tests.The tests validate URL whitelisting (
https://evil.com/image.pngrejected) and scale validation. This is important for preventing SSRF attacks.apps/api/src/utils/gradio.ts (2)
10-43: Robust SSE parsing with error handling.The
extractCompleteEventDatafunction correctly:
- Parses SSE format line by line
- Handles both 'complete' and 'error' events
- Falls back gracefully when JSON parsing fails (Line 30-32)
- Provides debugging context for unexpected responses (Line 39-42)
62-69: This review comment targets dead code that is not actually used in the application.The file
apps/api/src/utils/gradio.tsis exported from the utils module but not imported anywhere in the codebase. The HuggingFace provider implements its own localcallGradioApifunction (lines 84-110 inapps/api/src/providers/huggingface.ts) with proper error code mapping via theparseHuggingFaceErrorfunction (lines 9-45). This function correctly maps HTTP status codes to specific error types:
- 429 →
RATE_LIMITED- 401/403 →
AUTH_INVALID- 504 →
TIMEOUT- 402 →
QUOTA_EXCEEDED- 503 →
PROVIDER_ERRORThe tests pass because they exercise the provider implementation, which already handles error code mapping correctly. No changes are needed.
Likely an incorrect or invalid review comment.
apps/api/src/app.ts (4)
44-77: Well-structured middleware stack with proper ordering.The middleware is applied in the correct order:
requestId- First for logging correlationcorsMiddleware- Early for preflight handlingsecurityHeaders- Security headersrequestLogger- Logging with request ID availabletimeoutandbodyLimit- Per-route limits for generation endpointsThe 120-second timeout and 50KB body limit are reasonable defaults for image generation.
123-209: POST /api/generate endpoint correctly implements provider requirements.The endpoint:
- Supports 'gitee' and 'huggingface' providers via
providerId(Line 133)- Defaults to 'gitee' for backward compatibility (Line 133)
- Extracts auth token from provider-specific header (Line 140):
X-API-Keyfor Gitee,X-HF-Tokenfor HuggingFace- Enforces auth requirement based on provider config (Lines 143-145)
This aligns with coding guidelines. Based on learnings, the implementation correctly requires X-API-Key for Gitee AI and supports optional X-HF-Token for HuggingFace.
211-282: Legacy endpoint maintains backward compatibility.The
/generate-hfendpoint:
- Works without authentication (HuggingFace is optional)
- Uses
X-HF-Tokenheader when provided- Defaults to 'z-image-turbo' model and standard dimensions
This ensures existing clients continue to work.
284-331: Upscale endpoint with URL whitelisting.The endpoint correctly validates:
- URL presence and type
- URL against whitelist via
isAllowedImageUrl(SSRF protection)- Scale parameter bounds
Error handling uses
sendErrorconsistently with the standardized error responses.packages/shared/src/types/error.ts (4)
6-30: LGTM! Well-organized error code enum.The enum is clearly structured with logical groupings and consistent naming conventions. The inline comments indicating HTTP status code categories are helpful for understanding the error hierarchy.
33-50: LGTM! Correct HTTP status code mappings.The error-to-status mappings are semantically correct and type-safe. Using
Record<ApiErrorCode, number>ensures all enum values are covered at compile time.
53-69: LGTM! Clear and flexible error response structure.The interface provides a standardized format for API error responses with appropriate optional fields for context-specific details. The JSDoc comments enhance usability.
95-156: LGTM! Excellent error factory API.The
Errorshelper object provides a clean, type-safe API for creating common errors with sensible defaults. The factory methods encapsulate error creation logic and ensure consistent error details across the application.
| - name: Deploy to Cloudflare Workers | ||
| uses: cloudflare/wrangler-action@v3 | ||
| with: | ||
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | ||
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | ||
| workingDirectory: apps/api | ||
| command: deploy |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if wrangler.toml exists and examine its configuration
if [ -f "apps/api/wrangler.toml" ]; then
echo "=== wrangler.toml configuration ==="
cat apps/api/wrangler.toml
else
echo "ERROR: apps/api/wrangler.toml not found!"
echo ""
echo "=== Checking apps/api directory structure ==="
ls -la apps/api/ 2>/dev/null || echo "apps/api directory not found"
fiRepository: WuMingDao/zenith-image-generator
Length of output: 868
Add minification to the Cloudflare Workers deployment.
The wrangler.toml correctly specifies the entry point (src/index.ts), so the simplified deploy command will work. However, minification is not configured. Add minification to the build configuration:
[build]
command = ""
minify = trueAlternatively, add the --minify flag to the workflow command if you prefer explicit control.
🤖 Prompt for AI Agents
.github/workflows/deploy-cf-workers.yml around lines 40 to 46: the workflow
deploy step does not enable minification for Cloudflare Workers; either add the
--minify flag to the wrangler command (e.g., set with.command to "deploy
--minify") in this workflow, or enable minification in the project's
wrangler.toml by adding a [build] section with minify = true (and an empty
command if needed); update one of these places so the worker bundle is minified
during deploy.
| return async (c, next) => { | ||
| const contentLength = c.req.header('Content-Length') | ||
|
|
||
| if (contentLength) { | ||
| const size = Number.parseInt(contentLength, 10) | ||
| if (size > maxSize) { | ||
| return sendError( | ||
| c, | ||
| Errors.invalidParams( | ||
| 'body', | ||
| `Request body too large. Maximum size: ${Math.round(maxSize / 1024)}KB` | ||
| ) | ||
| ) | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n apps/api/src/middleware/body-limit.tsRepository: WuMingDao/zenith-image-generator
Length of output: 1064
🏁 Script executed:
# Check how body-limit middleware is used
rg -n "bodyLimit" apps/api/src/ -A 2 -B 2Repository: WuMingDao/zenith-image-generator
Length of output: 1077
🏁 Script executed:
# Check the POST /api/generate endpoint implementation
fd "generate" apps/api/src/ --type fRepository: WuMingDao/zenith-image-generator
Length of output: 58
🏁 Script executed:
# Look at how Hono handles request bodies
rg -n "req\.raw|req\.text|req\.json|body" apps/api/src/ -t ts | head -40Repository: WuMingDao/zenith-image-generator
Length of output: 2865
🏁 Script executed:
# Check package.json for Hono version
cat apps/api/package.json | grep -A 2 -B 2 "hono"Repository: WuMingDao/zenith-image-generator
Length of output: 223
🏁 Script executed:
# Check if there's any Hono configuration for body size
rg -n "maxBodySize|maxSize|limit" apps/api/src/app.ts -B 3 -A 3Repository: WuMingDao/zenith-image-generator
Length of output: 364
🏁 Script executed:
# Check if there's any Cloudflare Workers middleware handling size
rg -n "Cloudflare|wrangler|miniflare" apps/api/ -rRepository: WuMingDao/zenith-image-generator
Length of output: 489
🏁 Script executed:
# Look at the error handler to understand error handling patterns
cat -n apps/api/src/middleware/error-handler.tsRepository: WuMingDao/zenith-image-generator
Length of output: 1832
🌐 Web query:
Hono 4.6.0 body size limit default maximum request body size enforcement
💡 Result:
-
Default maximum request body size in Hono's bodyLimit middleware: 100 * 1024 bytes (100 KB). [1]
-
How it's enforced in v4.6.0: the middleware checks Content-Length first (if present) and otherwise reads the request stream and triggers the configured onError when the size exceeds maxSize. [1]
-
Important security note: versions <= 4.9.6 (which includes 4.6.0) had a bug that could allow bypassing the limit when conflicting headers (Transfer-Encoding: chunked vs Content-Length) were present; this was fixed in 4.9.7. If you rely on body size limits, upgrade or apply mitigations. [2]
Sources:
[1] Hono bodyLimit middleware docs — default maxSize and behavior.
[2] GHSA / CVE advisory for bodyLimit bypass (fixed in 4.9.7).
🌐 Web query:
Hono framework Content-Length header validation req.json() maximum body size
💡 Result:
Short answer: Hono's bodyLimit middleware uses the Content-Length header (if present) to check request size; if Content-Length is missing it reads the stream and enforces the maxSize while streaming. This behavior meant a past bug where Content-Length was (incorrectly) prioritized over Transfer-Encoding: chunked — fixed in Hono v4.9.7. Use bodyLimit({ maxSize }) and upgrade to >= v4.9.7; also ensure your runtime/proxy request-body limits (e.g., Bun, Cloudflare, etc.) are configured appropriately. [1][2][3]
Sources:
- bodyLimit docs (behavior + runtime note). [1]
- Hono security advisory / GHSA describing the Content-Length vs Transfer-Encoding fix (fixed in v4.9.7). [2]
- Hono docs overview. [3]
🏁 Script executed:
# Check git history or comments for why custom bodyLimit was created
rg -n "body.*limit|Content-Length" apps/api/src/ -B 2 -A 2Repository: WuMingDao/zenith-image-generator
Length of output: 1320
🏁 Script executed:
# Check if body is being read/streamed anywhere before size check
rg -n "req\.raw|getRawBody|readable\|stream" apps/api/src/middleware/ -A 3Repository: WuMingDao/zenith-image-generator
Length of output: 58
🏁 Script executed:
# Look at how Hono's built-in bodyLimit is exposed
rg -n "from 'hono" apps/api/src/middleware/body-limit.tsRepository: WuMingDao/zenith-image-generator
Length of output: 123
🌐 Web query:
Cloudflare Workers maximum request body size limit default
💡 Result:
Cloudflare enforces a network-wide request-body size limit tied to your Cloudflare account plan (not the Workers plan): Free/Pro = 100 MB, Business = 200 MB, Enterprise = 500 MB (Enterprise can request higher). [1]
Note: some Cloudflare features (e.g., Rules Engine body fields) have much smaller parsing limits (e.g., 128 KB for Enterprise, 1 MB for Free) — those are separate from the network request-body limit. [2]
Sources: Cloudflare Workers limits; Ruleset-engine http.request.body docs. [1][2]
🌐 Web query:
Hono bodyLimit security bug CVE Content-Length Transfer-Encoding chunked v4.6.0 v4.9.7
💡 Result:
Short answer: This is CVE-2025-59139 — a request-body size limit bypass in Hono's bodyLimit middleware where Content-Length was honored even when Transfer-Encoding: chunked was present. Affects hono versions <= 4.9.6 (so v4.6.0 is vulnerable); fixed in v4.9.7. Upgrade to >=4.9.7. [1][2][3]
References:
- GitHub security advisory (vendor) — details + patched version. [1]
- NVD entry for CVE-2025-59139. [2]
- Patch/commit that fixes the behavior. [3]
Links (by reference):
[1] GitHub advisory: honojs/hono GHSA-92vj-g62v-jqhh.
[2] NVD: CVE-2025-59139.
[3] Fix commit: 605c70560b52f13af10379f79b76717042fafe8d.
🏁 Script executed:
# Check if Hono's bodyLimit is imported/available
rg -n "hono/middleware" apps/api/src/Repository: WuMingDao/zenith-image-generator
Length of output: 58
🏁 Script executed:
# Look at middleware/index to see what's exported
cat -n apps/api/src/middleware/index.tsRepository: WuMingDao/zenith-image-generator
Length of output: 344
🏁 Script executed:
# Check if the streaming body is accessed anywhere in request handling
rg -n "c\.req\.raw\|req\.body\|readable" apps/api/src/ -t tsRepository: WuMingDao/zenith-image-generator
Length of output: 58
Implement streaming body size enforcement and upgrade Hono due to security vulnerabilities.
The current implementation has two critical issues:
-
Content-Length bypass: The middleware only checks the
Content-Lengthheader but doesn't enforce size limits on the actual request body during streaming. A client can omit this header to bypass the limit entirely. -
CVE-2025-59139 vulnerability: Hono 4.6.0 is vulnerable to a request-body size limit bypass where
Content-Lengthis honored even whenTransfer-Encoding: chunkedis present. This allows attackers to send arbitrarily large payloads.
Recommended solutions (in order of preference):
-
Upgrade Hono to v4.9.7 or later (patches CVE-2025-59139) and switch to Hono's built-in
bodyLimitmiddleware, which properly enforces limits during streaming. -
If staying with the custom middleware, implement streaming body size enforcement by reading chunks and tracking cumulative size, then reject when exceeding
maxSize. -
Avoid requiring
Content-Lengthas a mandatory header—it breaks validTransfer-Encoding: chunkedrequests and is not a valid security measure on its own.
🤖 Prompt for AI Agents
In apps/api/src/middleware/body-limit.ts around lines 14-28, the middleware only
checks the Content-Length header (which can be omitted or spoofed) and is
vulnerable to the Hono CVE-2025-59139 request-body limit bypass; fix by
upgrading Hono to v4.9.7+ and replacing this custom logic with Hono's built-in
bodyLimit middleware so limits are enforced while streaming, or if you cannot
upgrade implement streaming enforcement: read the request body as a stream,
accumulate chunk sizes, immediately reject and abort the request when cumulative
size > maxSize, and do not rely on the Content-Length header (support chunked
transfer encoding).
| function generateRequestId(): string { | ||
| return `${Date.now().toString(36)}-${Math.random().toString(36).substring(2, 9)}` | ||
| } | ||
|
|
||
| /** | ||
| * Request ID middleware | ||
| * Assigns a unique ID to each request for logging and tracing | ||
| */ | ||
| export const requestId: MiddlewareHandler = async (c, next) => { | ||
| // Use existing request ID from header if provided, otherwise generate new one | ||
| const id = c.req.header('X-Request-ID') || generateRequestId() | ||
| c.set('requestId', id) | ||
|
|
||
| await next() | ||
|
|
||
| // Add request ID to response header | ||
| c.res.headers.set('X-Request-ID', id) | ||
| } |
There was a problem hiding this comment.
Validate incoming X-Request-ID before reflecting it in the response.
As written, any client-provided value is echoed back; add a length/charset guard (and generate a new ID if invalid). Also consider crypto.randomUUID() when available for higher-quality IDs.
function generateRequestId(): string {
- return `${Date.now().toString(36)}-${Math.random().toString(36).substring(2, 9)}`
+ if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
+ return crypto.randomUUID()
+ }
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`
}
export const requestId: MiddlewareHandler = async (c, next) => {
// Use existing request ID from header if provided, otherwise generate new one
- const id = c.req.header('X-Request-ID') || generateRequestId()
+ const candidate = c.req.header('X-Request-ID')?.trim()
+ const id =
+ candidate && /^[A-Za-z0-9._-]{1,128}$/.test(candidate) ? candidate : generateRequestId()
c.set('requestId', id)
await next()
// Add request ID to response header
c.res.headers.set('X-Request-ID', id)
}| export const securityHeaders: MiddlewareHandler = async (c, next) => { | ||
| await next() | ||
|
|
||
| // Prevent MIME type sniffing | ||
| c.res.headers.set('X-Content-Type-Options', 'nosniff') | ||
|
|
||
| // Prevent clickjacking | ||
| c.res.headers.set('X-Frame-Options', 'DENY') | ||
|
|
||
| // XSS protection (legacy browsers) | ||
| c.res.headers.set('X-XSS-Protection', '1; mode=block') | ||
|
|
||
| // Referrer policy | ||
| c.res.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin') | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Add modern security headers for comprehensive protection.
Consider adding these essential security headers:
- Content-Security-Policy (CSP) - Prevents XSS attacks by controlling resource loading
- Strict-Transport-Security (HSTS) - Forces HTTPS connections
- Permissions-Policy - Controls browser feature access
Also consider upgrading X-Frame-Options: DENY to the modern Content-Security-Policy: frame-ancestors 'none' directive.
Apply this diff to add modern security headers:
await next()
// Prevent MIME type sniffing
c.res.headers.set('X-Content-Type-Options', 'nosniff')
// Prevent clickjacking
c.res.headers.set('X-Frame-Options', 'DENY')
- // XSS protection (legacy browsers)
- c.res.headers.set('X-XSS-Protection', '1; mode=block')
-
// Referrer policy
c.res.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin')
+
+ // Content Security Policy
+ c.res.headers.set(
+ 'Content-Security-Policy',
+ "default-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"
+ )
+
+ // HSTS (only if serving over HTTPS)
+ c.res.headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains')
+
+ // Permissions Policy
+ c.res.headers.set('Permissions-Policy', 'geolocation=(), microphone=(), camera=()')
}Note: Adjust CSP directives based on your actual resource loading requirements, and only enable HSTS if your API is served over HTTPS.
🤖 Prompt for AI Agents
In apps/api/src/middleware/security.ts around lines 11 to 25, the middleware
only sets legacy headers and is missing modern security headers; add a
Content-Security-Policy header (including a frame-ancestors 'none' directive to
replace X-Frame-Options), add Strict-Transport-Security (HSTS) header but only
enable/populate it when the app is served over HTTPS or behind a TLS terminator,
and add a Permissions-Policy header with a conservative feature list (e.g.,
disable geolocation, camera, microphone) while keeping existing safe headers (or
remove X-Frame-Options if CSP frame-ancestors is used); ensure CSP directives
are minimal and reviewed for your resource needs and that HSTS includes max-age
and includeSubDomains/preload only when appropriate.
| // XSS protection (legacy browsers) | ||
| c.res.headers.set('X-XSS-Protection', '1; mode=block') |
There was a problem hiding this comment.
Remove deprecated X-XSS-Protection header.
The X-XSS-Protection header is deprecated and can introduce vulnerabilities in some browsers. Modern browsers use Content Security Policy (CSP) for XSS protection instead. This header should be removed.
Apply this diff:
- // XSS protection (legacy browsers)
- c.res.headers.set('X-XSS-Protection', '1; mode=block')
-📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // XSS protection (legacy browsers) | |
| c.res.headers.set('X-XSS-Protection', '1; mode=block') |
🤖 Prompt for AI Agents
In apps/api/src/middleware/security.ts around lines 20-21, remove the deprecated
X-XSS-Protection header assignment (c.res.headers.set('X-XSS-Protection', '1;
mode=block')) from the middleware; delete that line and any related comment, and
ensure you rely on Content Security Policy (CSP) elsewhere (or add/update CSP
headers if not present) for XSS protections.
| describe('isHuggingFaceUrl (tested via behavior)', () => { | ||
| // These tests document the expected behavior of URL detection | ||
| // The actual function is private, but we test the concept | ||
|
|
||
| const testUrls = [ | ||
| { url: 'https://example.hf.space/file/image.png', expected: true }, | ||
| { url: 'https://huggingface.co/datasets/image.png', expected: true }, | ||
| { url: 'https://mrfakename-z-image-turbo.hf.space/img.png', expected: true }, | ||
| { url: 'https://example.com/image.png', expected: false }, | ||
| { url: 'https://gitee.ai/image.png', expected: false }, | ||
| ] | ||
|
|
||
| it.each(testUrls)('should correctly identify $url as HF=$expected', ({ url, expected }) => { | ||
| const isHF = url.includes('.hf.space') || url.includes('huggingface.co') | ||
| expect(isHF).toBe(expected) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Test doesn't validate the actual isHuggingFaceUrl implementation.
The test on lines 76-79 creates a local variable isHF with inline logic rather than testing the actual isHuggingFaceUrl function. This provides false confidence—if the real function's logic changes or has bugs, this test won't catch it.
Recommended solutions:
- Export the function for testing (add to exports in
utils.ts) - Test indirectly through public APIs that use
isHuggingFaceUrl - Remove this test if the function truly cannot be tested
The current approach documents intended behavior but doesn't validate actual implementation.
Example fix (Option 1 - export for testing):
In apps/web/src/lib/utils.ts:
// Export for testing
export function isHuggingFaceUrl(url: string): boolean {
return url.includes('.hf.space') || url.includes('huggingface.co')
}Then update the test:
-describe('isHuggingFaceUrl (tested via behavior)', () => {
- // These tests document the expected behavior of URL detection
- // The actual function is private, but we test the concept
+import { isHuggingFaceUrl } from '../utils'
+describe('isHuggingFaceUrl', () => {
const testUrls = [
{ url: 'https://example.hf.space/file/image.png', expected: true },
{ url: 'https://huggingface.co/datasets/image.png', expected: true },
{ url: 'https://mrfakename-z-image-turbo.hf.space/img.png', expected: true },
{ url: 'https://example.com/image.png', expected: false },
{ url: 'https://gitee.ai/image.png', expected: false },
]
it.each(testUrls)('should correctly identify $url as HF=$expected', ({ url, expected }) => {
- const isHF = url.includes('.hf.space') || url.includes('huggingface.co')
- expect(isHF).toBe(expected)
+ expect(isHuggingFaceUrl(url)).toBe(expected)
})
})🤖 Prompt for AI Agents
In apps/web/src/lib/__tests__/utils.test.ts around lines 64 to 80, the test
defines a local isHF variable instead of invoking the real isHuggingFaceUrl
function; export the isHuggingFaceUrl function from apps/web/src/lib/utils.ts
(add an exported function that implements the current logic), update the test to
import that function from the module, and replace the inline url.includes(...)
logic with a call to isHuggingFaceUrl(url) so the test validates the actual
implementation (alternatively, if you cannot export it, remove the test or
assert via a public API that uses the function).
| ``` | ||
| <type>(<scope>): <description> | ||
|
|
||
| [optional body] | ||
| ``` | ||
|
|
There was a problem hiding this comment.
Add language identifier to fenced code block.
The fenced code block is missing a language specifier, which triggers a markdown linting warning.
Apply this diff:
-```
+```text
<type>(<scope>): <description>
[optional body]🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
161-161: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
In docs/en/CONTRIBUTING.md around lines 161 to 166, the fenced code block lacks
a language specifier which causes markdown lint warnings; update the opening
fence to include the language identifier by changing the triple backticks to use
"text" (i.e., replace ``` with ```text) so the block reads with a language tag
while leaving the inner content unchanged.
| "@vitest/coverage-v8": "^4.0.15", | ||
| "turbo": "^2.3.3", | ||
| "typescript": "^5.7.2", | ||
| "vitest": "^2.1.8" | ||
| "vitest": "^4.0.15" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd . && pnpm test:runRepository: WuMingDao/zenith-image-generator
Length of output: 291
🌐 Web query:
Vitest v4 breaking changes migration guide from v2
💡 Result:
Here are the key breaking changes and migration notes when moving from Vitest v2 → v4 (concise checklist — read the linked Migration Guide & release post for details):
-
Mocking and spies
- vi.fn().getMockName() default name changed (may affect snapshots). Spies from vi.spyOn keep original name. (main.vitest.dev)
- vi.restoreAllMocks no longer resets automocks (only restores spies created with vi.spyOn). restoreMocks config behavior changed. (main.vitest.dev)
- vi.spyOn on an already-mocked function returns the same mock; automocked instance methods isolation changed. (main.vitest.dev)
-
Module/runtime changes
- Vitest no longer injects __vitest_executor; it injects moduleRunner (ModuleRunner instance) instead. (main.vitest.dev)
- vitest/execute entry point removed (was internal). (main.vitest.dev)
- vite-node is no longer a dependency of Vitest. (main.vitest.dev)
-
Configuration / env / vars
- Environment variable VITE_NODE_DEPS_MODULE_DIRECTORIES renamed to VITEST_MODULE_DIRECTORIES. (main.vitest.dev)
- deps.optimizer.web renamed to deps.optimizer.client (or use custom names for other server environments). (main.vitest.dev)
- Custom environments: transformMode → provide viteEnvironment (or rely on environment name + server.environments). (main.vitest.dev)
-
Browser mode & providers
- Browser Mode graduated to stable; browser providers are now separate packages (e.g. @vitest/browser-playwright, @vitest/browser-webdriverio, @vitest/browser-preview) — you must install/configure provider packages if using browser mode. Import paths for context moved to vitest/browser (old path kept until next major). (vitest.dev)
-
New/changed test features & reporters
- Visual regression built-in (toMatchScreenshot) and Playwright trace support added (new options like browser.trace). (vitest.dev)
- Some reporter defaults removed/changed (basic reporter removed; default reporter prints tree only for single-file runs; new tree reporter available) — update custom reporter/config if you relied on old behaviour. (infoq.com)
-
Other notes
- Several new public APIs and helpers (experimental_parseSpecifications, watcher, enableCoverage/disableCoverage, getSeed, etc.) — check whether you can replace any internal usage with the new public API. (vitest.dev)
Recommended migration steps
- Read the official Migration Guide and changelog and search your codebase for usages of: vi.restoreAllMocks, vi.fn().getMockName(), vitest/execute, VITE_NODE_DEPS_MODULE_DIRECTORIES, vite-node, deps.optimizer.web, custom environment transformMode, and any custom reporters. (main.vitest.dev)
- Install needed browser provider packages if you use Browser Mode. (vitest.dev)
- Run tests, update affected snapshots (mock name changes), and fix any mock/spy-related assumptions. (main.vitest.dev)
Primary sources
- Vitest 4 announcement & migration links. (vitest.dev)
- Official Migration Guide (breaking changes details). (main.vitest.dev)
- Coverage of notable changes (browser mode, reporters, traces). (infoq.com)
If you want, I can produce a grep/replace list of code changes to run across your repo (examples for package installs, config key renames, and common mock/spy fixes).
Citations:
- 1: https://main.vitest.dev/guide/migration.html?utm_source=openai
- 2: https://main.vitest.dev/guide/migration.html?utm_source=openai
- 3: https://main.vitest.dev/guide/migration.html?utm_source=openai
- 4: https://main.vitest.dev/guide/migration.html?utm_source=openai
- 5: https://main.vitest.dev/guide/migration.html?utm_source=openai
- 6: https://main.vitest.dev/guide/migration.html?utm_source=openai
- 7: https://main.vitest.dev/guide/migration.html?utm_source=openai
- 8: https://main.vitest.dev/guide/migration.html?utm_source=openai
- 9: https://main.vitest.dev/guide/migration.html?utm_source=openai
- 10: https://vitest.dev/blog/vitest-4?utm_source=openai
- 11: https://vitest.dev/blog/vitest-4?utm_source=openai
- 12: https://www.infoq.com/news/2025/12/vitest-4-browser-mode/?utm_source=openai
- 13: https://vitest.dev/blog/vitest-4?utm_source=openai
- 14: https://main.vitest.dev/guide/migration.html?utm_source=openai
- 15: https://vitest.dev/blog/vitest-4?utm_source=openai
- 16: https://main.vitest.dev/guide/migration.html?utm_source=openai
- 17: https://vitest.dev/blog/vitest-4?utm_source=openai
- 18: https://main.vitest.dev/guide/migration.html?utm_source=openai
- 19: https://www.infoq.com/news/2025/12/vitest-4-browser-mode/?utm_source=openai
🏁 Script executed:
# Check if vitest is actually in node_modules (it's missing)
ls -la node_modules | grep vitest || echo "vitest not found in node_modules"
# Check the actual versions currently specified vs. what's in node_modules
cat package.json | grep -A 2 -B 2 "vitest"
# Search for breaking change patterns in test files
rg "(vi\.restoreAllMocks|vitest/execute|transformMode|VITE_NODE_DEPS|deps\.optimizer\.web)" --type ts --type tsx --type js --type jsxRepository: WuMingDao/zenith-image-generator
Length of output: 666
🏁 Script executed:
# Check for usage of vi.fn().getMockName() which changed behavior in v4
rg "getMockName|spyOn.*already.*mocked|__vitest_executor|moduleRunner" --type ts --type tsx --type js --type jsxRepository: WuMingDao/zenith-image-generator
Length of output: 103
🏁 Script executed:
# Find all test files and config files
fd "vitest|test\.(ts|js)" --type f | head -20
# Find vitest config file
fd "vitest\.config\.(ts|js)" --type f
# Search for breaking patterns with simpler approach (no -t flag)
rg "restoreAllMocks|vitest/execute|transformMode|VITE_NODE_DEPS|optimizer\.web" .Repository: WuMingDao/zenith-image-generator
Length of output: 732
🏁 Script executed:
# Check for Vitest configuration details
fd "vitest\.config" . --type f -x cat {}
# Check if there are test files and what they look like
fd "\.(test|spec)\.(ts|js)" . --type f -x head -20 {} \; | head -50Repository: WuMingDao/zenith-image-generator
Length of output: 2673
Test setup requires changes for Vitest v4 compatibility.
The upgrade to Vitest v4.0.15 introduces breaking changes that affect your test infrastructure. Specifically, vi.restoreAllMocks() is called in setup files (apps/web/src/__tests__/setup.ts and apps/api/src/__tests__/setup.ts), but v4 changed its behavior—it no longer resets automocks, only restores spies created with vi.spyOn(). Review the Vitest v4 migration guide and update setup files to use vi.clearAllMocks() or adjust mock handling accordingly. Also audit test snapshots for changes in vi.fn().getMockName() default naming behavior in v4.
🤖 Prompt for AI Agents
In package.json around lines 35 to 38, the project upgraded Vitest to v4 which
changed mock restoration semantics so existing setup files calling
vi.restoreAllMocks() no longer clear automocks; update your test setup files
(apps/web/src/__tests__/setup.ts and apps/api/src/__tests__/setup.ts) to call
vi.clearAllMocks() (or both vi.clearAllMocks() and vi.restoreAllMocks() where
you need to both clear mock state and restore spies) and adjust any tests
relying on vi.fn().getMockName() default names by updating snapshots or
explicitly naming mocks with vi.fn().mockName('name') to keep snapshot
stability.
| export function getModelConfig(modelId: string): ModelConfig | undefined { | ||
| return MODEL_CONFIGS.find((m) => m.id === modelId); | ||
| return MODEL_CONFIGS.find((m) => m.id === modelId) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find all usages of getModelConfig function
rg -n 'getModelConfig\(' --type ts --type tsx -A 3
# Also check imports to understand exposure
rg -n 'export.*getModelConfig' --type ts --type tsxRepository: WuMingDao/zenith-image-generator
Length of output: 131
🏁 Script executed:
# Examine the models.ts file to understand the structure and verify the claims
head -200 packages/shared/src/constants/models.ts | tail -100Repository: WuMingDao/zenith-image-generator
Length of output: 2490
🏁 Script executed:
# Check if there are actual duplicate model IDs across providers
rg -n "id:\s*['\"]z-image-turbo['\"]|id:\s*['\"]flux-1-schnell['\"]" packages/shared/src/constants/models.ts -B 5Repository: WuMingDao/zenith-image-generator
Length of output: 578
🏁 Script executed:
# Search for usages with corrected flags
rg 'getModelConfig\(' --type ts -A 3 -B 1
# Also search without type filter to catch all files
rg 'getModelConfig\(' -A 3 -B 1Repository: WuMingDao/zenith-image-generator
Length of output: 811
🏁 Script executed:
# Check for imports and re-exports
rg 'getModelConfig' --type tsRepository: WuMingDao/zenith-image-generator
Length of output: 190
🏁 Script executed:
# Search for any imports of getModelConfig from models.ts
rg 'from.*models' --type ts -A 2 -B 1 | grep -A 2 -B 1 'getModelConfig'
# Broader search for any reference to getModelConfig
rg 'getModelConfig' -r . --no-ignoreRepository: WuMingDao/zenith-image-generator
Length of output: 177
🏁 Script executed:
# Check if models.ts is exported from an index file
rg 'export.*from.*models' --type ts
# Check the entire shared package structure
find packages/shared -name 'index.ts' -o -name 'index.js' | head -5Repository: WuMingDao/zenith-image-generator
Length of output: 277
🏁 Script executed:
# Check the shared package exports
cat packages/shared/src/index.ts 2>/dev/null || cat packages/shared/index.ts 2>/dev/null || echo "No index found"Repository: WuMingDao/zenith-image-generator
Length of output: 272
🏁 Script executed:
# Final confirmation: check MODEL_CONFIGS array order to confirm Gitee comes first
sed -n '8,100p' packages/shared/src/constants/models.ts | grep -E "provider:|id:" | head -20Repository: WuMingDao/zenith-image-generator
Length of output: 528
Ambiguous behavior with duplicate model IDs across providers.
The getModelConfig(modelId) function returns the first match from MODEL_CONFIGS, which always returns the Gitee provider version when duplicate IDs exist (e.g., z-image-turbo, flux-1-schnell appear in both Gitee and HuggingFace). Since this function is exported as part of the public API (@z-image/shared), consumers may unknowingly receive the wrong provider's configuration. Use getModelByProviderAndId(provider, modelId) when the provider is known, or add clear documentation about this behavior.
| if (prompt.length > 4000) { | ||
| return { valid: false, error: 'Prompt exceeds maximum length of 4000 characters' } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the validation.ts file to see the current code and any comments
cat -n packages/shared/src/utils/validation.ts | head -50Repository: WuMingDao/zenith-image-generator
Length of output: 1942
🏁 Script executed:
# Search for any documentation about the prompt length limit change
rg -n -C5 '4000|10000|prompt.*length|length.*limit' packages/shared/src/Repository: WuMingDao/zenith-image-generator
Length of output: 2442
🏁 Script executed:
# Check provider implementations for prompt length limits
rg -n -C3 'prompt.*length|max.*prompt|provider' packages/api/src/ 2>/dev/null || rg -n -C3 'prompt.*length|max.*prompt' . --type ts --type tsx 2>/dev/null | head -100Repository: WuMingDao/zenith-image-generator
Length of output: 58
🏁 Script executed:
# Look for CHANGELOG or release notes
find . -name "CHANGELOG*" -o -name "RELEASE*" -o -name "HISTORY*" | head -10Repository: WuMingDao/zenith-image-generator
Length of output: 95
🏁 Script executed:
# Check git history for the prompt limit change (if available)
git log --oneline --all -S "4000" -- packages/shared/src/utils/validation.ts 2>/dev/null | head -20Repository: WuMingDao/zenith-image-generator
Length of output: 130
🏁 Script executed:
# Check CHANGELOG for entries about prompt length limit
cat -n docs/CHANGELOG.md | grep -i -C5 'prompt\|4000\|10000\|limit'Repository: WuMingDao/zenith-image-generator
Length of output: 976
🏁 Script executed:
# Look for all references to prompt limit in the entire codebase
rg -i 'prompt.*limit|max.*prompt|prompt.*4000|prompt.*10000' --type ts --type tsx --type jsonRepository: WuMingDao/zenith-image-generator
Length of output: 103
🏁 Script executed:
# Check git history more thoroughly for any previous limit values
git log --all --oneline -S "prompt" -- packages/shared/src/utils/validation.ts 2>/dev/null | head -30Repository: WuMingDao/zenith-image-generator
Length of output: 180
🏁 Script executed:
# Search for provider implementations or configuration
find . -path "*/node_modules" -prune -o -type f \( -name "*provider*" -o -name "*config*" \) -print | grep -E "\.(ts|tsx|js|json)$" | head -20Repository: WuMingDao/zenith-image-generator
Length of output: 456
🏁 Script executed:
# Look for any documentation about API constraints
rg -i 'api.*limit|constraint|gitee|huggingface|modelscope' --type ts --type tsx --type json docs/ 2>/dev/null | head -30Repository: WuMingDao/zenith-image-generator
Length of output: 58
🏁 Script executed:
# Check config file for prompt limit settings
cat -n apps/api/src/config.tsRepository: WuMingDao/zenith-image-generator
Length of output: 1883
🏁 Script executed:
# Check provider constants
cat -n packages/shared/src/constants/providers.tsRepository: WuMingDao/zenith-image-generator
Length of output: 1623
🏁 Script executed:
# Search for "10000" anywhere in the codebase
rg -n '10000' --type ts --type js --type jsonRepository: WuMingDao/zenith-image-generator
Length of output: 356
🏁 Script executed:
# Check for any provider-specific implementations with prompt limits
rg -n -B5 -A5 'prompt' apps/api/src/ --type ts | head -100Repository: WuMingDao/zenith-image-generator
Length of output: 6276
🏁 Script executed:
# Check the complete git log for the validation file to see when it was created
git log --all --oneline -- packages/shared/src/utils/validation.ts 2>/dev/nullRepository: WuMingDao/zenith-image-generator
Length of output: 180
Add inline documentation explaining the 4000-character prompt limit.
The current code enforces a 4000-character limit on prompts but provides no rationale. Please clarify:
- Is this limit driven by a provider API constraint (Gitee AI, HuggingFace, or ModelScope)?
- Is this a performance or cost optimization measure?
- Should this be documented in a code comment or the CHANGELOG?
The validation is enforced at lines 19–20, but there is no documentation explaining this specific threshold or whether it represents a breaking change for existing users.
🤖 Prompt for AI Agents
In packages/shared/src/utils/validation.ts around lines 19–20, add an inline
code comment above the prompt length check that states the rationale for the
4000-character limit: explicitly indicate whether the limit is required by a
downstream provider API (name the provider e.g., Gitee AI / HuggingFace /
ModelScope and include a source/link to their docs) or if it was chosen as an
internal performance/cost safeguard, and state whether changing it is a breaking
change for users; also add a brief CHANGELOG entry noting the enforced limit and
its rationale so users are informed.
Fixes pnpm workspace error in Cloudflare Pages deployment workflow. The wrangler-action was failing with ERR_PNPM_ADDING_TO_ROOT when trying to install wrangler in a monorepo setup. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Deploying zenith-image-generator with
|
| Latest commit: |
f0bae85
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://dd796c5c.zenith-image-generator.pages.dev |
| Branch Preview URL: | https://dev.zenith-image-generator.pages.dev |
Update project name from 'z-image' to 'zenith-image-generator' to match the existing Cloudflare Pages project. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
🚀 Preview Deployment
|
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Tests
✏️ Tip: You can customize this high-level summary in your review settings.