Skip to content

fix: validate generated REST contracts - #815

Merged
kang-heewon merged 13 commits into
trunkfrom
fix/715-contract-graph
Jun 16, 2026
Merged

fix: validate generated REST contracts#815
kang-heewon merged 13 commits into
trunkfrom
fix/715-contract-graph

Conversation

@kang-heewon

@kang-heewon kang-heewon commented Jun 15, 2026

Copy link
Copy Markdown
Member

Summary

  • Introduce a canonical REST ContractGraph in @croco/protocols-core with diagnostics for generated-contract metadata drift.
  • Route OpenAPI and RPC client generation through graph validation, including croco-rpc-codegen --check and croco contracts check.
  • Add the REST SPA template preflight so generated apps run contract:check before OpenAPI/client generation.

Closes #715

Verification

  • pnpm --filter @croco/protocols-core test
  • pnpm --filter @croco/openapi-spec test
  • pnpm --filter @croco/rpc-codegen test
  • pnpm --filter @croco/cli test
  • pnpm --filter create-croco-app test
  • pnpm --filter @croco/protocols-core --filter @croco/openapi-spec --filter @croco/rpc-codegen --filter @croco/cli --filter create-croco-app typecheck
  • pnpm check
  • pnpm changeset-required:check -- --base origin/trunk --head HEAD
  • pnpm create-croco-app:smoke
  • pre-push hook: full pnpm test and pnpm typecheck

Self-review

  • code-reviewer lane: APPROVE, no actionable findings
  • architect lane: CLEAR

Risk

  • generateClientFiles(routes) remains a low-level compatibility API for raw RouteIR[]; CLI and template generation now use ContractGraph, and the raw API guards known invalid generated-contract shapes before writing clients.

Summary by CodeRabbit

Release Notes

  • New Features

    • CLIμ—μ„œ contracts check둜 REST canonical contract graph(라우트 진단 포함) 검증 및 κ²°κ³Ό 좜λ ₯이 κ°€λŠ₯ν•©λ‹ˆλ‹€.
    • rpc-codegen --check 및 contract graph 기반 OpenAPI/RPC ν΄λΌμ΄μ–ΈνŠΈ μƒμ„±μœΌλ‘œ 경둜/νŒŒλΌλ―Έν„°/λ°”λ”” κ·œμΉ™ μœ„λ°˜μ„ 더 μ—„κ²©νžˆ μ κ²€ν•©λ‹ˆλ‹€.
    • spa-be-split ν…œν”Œλ¦Ώμ— pnpm contract:check 단계λ₯Ό μΆ”κ°€ν•˜κ³  OpenAPI/RPC 생성 μˆœμ„œλ₯Ό μ •λ ¬ν–ˆμŠ΅λ‹ˆλ‹€.
  • Bug Fixes

    • catch-all 경둜 νŒŒλΌλ―Έν„° 처리 및 μ •κ·œν™”κ°€ μ„ μ–Έλœ νŒŒλΌλ―Έν„°λͺ… κΈ°μ€€μœΌλ‘œ κ°œμ„ λ˜μ—ˆμŠ΅λ‹ˆλ‹€.
  • Chores

    • κ΄€λ ¨ νŒ¨ν‚€μ§€ 릴리즈 λ ˆλ²¨μ„ patch둜 μ§€μ •ν–ˆμŠ΅λ‹ˆλ‹€.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▢️ Resume reviews
  • πŸ” Trigger review
πŸ“ Walkthrough

Walkthrough

protocols-core에 ContractGraph 데이터 λͺ¨λΈΒ·λΉŒλ”·검증 λ ˆμ΄μ–΄λ₯Ό μ‹ κ·œ μΆ”κ°€ν•˜κ³ , rpc-codegen(loadRoutes, generate, CLI)κ³Ό openapi-spec(emitOpenAPI)이 이λ₯Ό μ†ŒλΉ„ν•˜λ„λ‘ ν†΅ν•©ν•œλ‹€. @croco/cli에 contracts check μ„œλΈŒμ»€λ§¨λ“œλ₯Ό μΆ”κ°€ν•˜λ©°, spa-be-split ν…œν”Œλ¦Ώκ³Ό 슀λͺ¨ν¬ μŠ€ν¬λ¦½νŠΈμ— contract:check 단계λ₯Ό λ°˜μ˜ν•œλ‹€.

Changes

Canonical Contract Graph 톡합

Layer / File(s) Summary
ContractGraph 데이터 λͺ¨λΈΒ·λΉŒλ”·진단 μœ ν‹Έ
packages/protocols-core/src/libs/ContractGraph.ts
ContractGraphVersion, ContractDiagnostic, ContractGraphRoute, ContractGraph νƒ€μž…κ³Ό ContractGraphDiagnosticError 클래슀λ₯Ό μ •μ˜ν•œλ‹€. buildContractGraphλŠ” 컨트둀러 λ©”νƒ€λ°μ΄ν„°μ—μ„œ 라우트λ₯Ό μΆ”μΆœν•΄ routeId/operationIdλ₯Ό λΆ€μ—¬ν•˜κ³ , path param 뢈일치, 닀쀑 body param, Zod effects κ²½κ³ , 쀑볡 routeId λ“± 라우트 λ‹¨μœ„ 검증을 μˆ˜ν–‰ν•΄ ContractGraphλ₯Ό λ°˜ν™˜ν•œλ‹€. 포맷·쑰회 헬퍼도 ν•¨κ»˜ μ œκ³΅λœλ‹€.
protocols-core 곡개 API ν™•μž₯
packages/protocols-core/src/index.ts, packages/protocols-core/src/libs/sharedTypes.ts, packages/protocols-core/src/tests/helpers/test-decorators.ts
ContractGraph κ΄€λ ¨ κ°’Β·νƒ€μž… 18쒅을 index.tsμ—μ„œ μž¬λ‚΄λ³΄λ‚΄λ©°, REST_GUARDS_KEY, REST_ROLES_KEY 메타데이터 ν‚€λ₯Ό sharedTypes.ts에 μΆ”κ°€ν•œλ‹€. UseGuards, Roles ν…ŒμŠ€νŠΈ λ°μ½”λ ˆμ΄ν„°λ₯Ό test-decorators.ts에 μΆ”κ°€ν•˜μ—¬ κ°€λ“œ/λ‘€ 메타데이터 검증을 μ§€μ›ν•œλ‹€.
ContractGraph λ‹¨μœ„ ν…ŒμŠ€νŠΈ
packages/protocols-core/src/tests/ContractGraph.spec.ts
κ·Έλž˜ν”„ λ…Έλ“œ 생성 μ•ˆμ •μ„±, 진단 μ½”λ“œΒ·severity μ •ν™•μ„±, path param 뢈일치·Zod effects 경고·닀쀑 body param μ—λŸ¬Β·operationId μ€‘λ³΅Β·μ»¨νŠΈλ‘€λŸ¬ 이름 쀑볡 검증, assertContractGraphHasNoErrors μ˜ˆμ™Έ νˆ¬μ²™μ„ κ²€μ¦ν•˜λŠ” 9개 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€λ₯Ό μΆ”κ°€ν•œλ‹€.
rpc-codegen: loadContractGraph λ„μž…Β·loadRoutes λ¦¬νŒ©ν„°
packages/rpc-codegen/src/libs/loadRoutes.ts
loadContractGraph(glob)λ₯Ό μ‹ κ·œ exportν•˜λ©°, ts-morph 컴파일 ν›„ 컨트둀러 μƒμ„±μžλ₯Ό μˆ˜μ§‘ν•΄ buildContractGraph둜 ContractGraphλ₯Ό λ°˜ν™˜ν•œλ‹€. loadRoutesλŠ” 이 ν•¨μˆ˜μ— μœ„μž„ν•˜κ³  graph.routesλ₯Ό λ°˜ν™˜ν•˜λ„λ‘ λ³€κ²½λœλ‹€. NoRestControllersFoundProblem이 Problem 기반으둜 λ³€κ²½λœλ‹€.
rpc-codegen: generateClientFilesFromContractGraph·경둜 검증 κ°•ν™”
packages/rpc-codegen/src/libs/generate.ts, packages/rpc-codegen/src/index.ts, packages/rpc-codegen/vitest.config.ts
generateClientFilesFromContractGraphλ₯Ό μΆ”κ°€ν•΄ ContractGraphλ₯Ό 사전 검증 ν›„ generateClientFiles에 μœ„μž„ν•œλ‹€. 닀쀑 @Body() 차단과 경둜 νŒŒλΌλ―Έν„° μ„ μ–ΈΒ·λ©”νƒ€λ°μ΄ν„°Β·μŠ€ν‚€λ§ˆ ν‚€ 일치 검사가 μΆ”κ°€λ˜λ©°, getPathExpression이 getContractPathParams 기반 토큰 μΉ˜ν™˜μœΌλ‘œ λ³€κ²½λœλ‹€. Vitest 별칭 μ„€μ • μΆ”κ°€.
rpc-codegen CLI: --check ν”Œλž˜κ·Έ 및 진단 리포트
packages/rpc-codegen/src/libs/cli.ts
runCliκ°€ contract graph λ‘œλ“œ 기반으둜 λ³€κ²½λ˜λ©°, --check μ‹œ ν΄λΌμ΄μ–ΈνŠΈ 생성 없이 진단을 좜λ ₯ν•˜κ³  였λ₯˜ μœ λ¬΄μ— 따라 μ’…λ£Œ μ½”λ“œλ₯Ό λ°˜ν™˜ν•œλ‹€. κ·Έλž˜ν”„ μ—λŸ¬κ°€ μ‘΄μž¬ν•˜λ©΄ 생성을 μ€‘λ‹¨ν•œλ‹€. parseArgs에 --checkΒ·outDir nullable 지원, reportContractGraph/reportContractDiagnostics 헬퍼, 도움말 갱신이 ν¬ν•¨λœλ‹€.
rpc-codegen 톡합 ν…ŒμŠ€νŠΈ
packages/rpc-codegen/src/tests/Cli.spec.ts, packages/rpc-codegen/src/tests/ContractCheckCli.spec.ts, packages/rpc-codegen/src/tests/codegen.spec.ts, packages/rpc-codegen/src/tests/loadRoutes.spec.ts, packages/rpc-codegen/src/tests/PublishedCli.spec.ts
--check κ²€μ¦Β·κ·Έλž˜ν”„ μ—λŸ¬ μ‹œ 생성 μ‹€νŒ¨ ν…ŒμŠ€νŠΈ, 닀쀑 body/경둜 νŒŒλΌλ―Έν„° 뢈일치 μΌ€μ΄μŠ€, loadContractGraph 톡합 ν…ŒμŠ€νŠΈ, μ‹€μ œ CLI μ‹€ν–‰μœΌλ‘œ 닀쀑 body param μ—λŸ¬ 좜λ ₯ 검증이 μΆ”κ°€λœλ‹€. λͺ© ꡬ쑰에 loadContractGraph 포함과 λΉŒλ“œ ν•„μš”μ„± νŒλ‹¨ 둜직(ensureBuilt)이 λ°˜μ˜λœλ‹€.
openapi-spec: ContractGraph μ†ŒλΉ„μž 톡합
packages/openapi-spec/src/libs/emitOpenAPI.ts, packages/openapi-spec/src/libs/loadControllers.ts, packages/openapi-spec/src/index.ts, packages/openapi-spec/src/tests/emitOpenAPI.spec.ts
emitOpenAPIκ°€ buildContractGraph β†’ emitOpenAPIFromContractGraph둜 μœ„μž„ν•˜λ„λ‘ λ³€κ²½λ˜λ©°, emitOpenAPIFromContractGraph μ‹œκ·Έλ‹ˆμ²˜κ°€ ContractGraphλ₯Ό λ°›λŠ” ν˜•νƒœλ‘œ 바뀐닀. 라우트 헬퍼듀이 ContractGraphRoute 기반으둜 λ§ˆμ΄κ·Έλ ˆμ΄μ…˜λ˜κ³ , operationId/summaryκ°€ κ·Έλž˜ν”„ νŒŒμƒ 값을 μ‚¬μš©ν•˜λ„λ‘ λ³€κ²½λœλ‹€. NoRestControllersFoundProblem이 Problem 기반으둜 λ³€κ²½λ˜κ³ , canonical κ·Έλž˜ν”„ 검증 ν…ŒμŠ€νŠΈκ°€ μΆ”κ°€λœλ‹€.
@croco/cli: contracts check μ„œλΈŒμ»€λ§¨λ“œ
packages/cli/src/commands/contracts.ts, packages/cli/src/commands/contractsCheck.ts, packages/cli/src/bin/croco.ts, packages/cli/src/index.ts, packages/cli/src/tests/contractsCheck.spec.ts
contracts μ»€λ§¨λ“œμ™€ check μ„œλΈŒμ»€λ§¨λ“œλ₯Ό μ •μ˜ν•œλ‹€. runContractsCheckλŠ” resolveRpcCodegenBin으둜 λ°”μ΄λ„ˆλ¦¬λ₯Ό κ²°μ •ν•˜κ³  --check ν”Œλž˜κ·Έμ™€ ν•¨κ»˜ μžμ‹ ν”„λ‘œμ„ΈμŠ€λ‘œ μ‹€ν–‰ν•˜λ©° exit/error 이벀트λ₯Ό μ²˜λ¦¬ν•œλ‹€. μ§„μž…μ Β·κ³΅κ°œ API 등둝과 λ‹¨μœ„ ν…ŒμŠ€νŠΈκ°€ ν¬ν•¨λœλ‹€.
spa-be-split ν…œν”Œλ¦ΏΒ·μŠ€λͺ¨ν¬ κ°±μ‹ 
packages/create-croco-app/templates/spa-be-split/package.json.hbs, packages/create-croco-app/templates/spa-be-split/README.md.hbs, packages/create-croco-app/src/tests/templates-build.spec.ts, scripts/create-croco-app-generated-smoke.mts, .changeset/contract-graph-check.md
contract:check 슀크립트λ₯Ό ν…œν”Œλ¦Ώμ— μΆ”κ°€ν•˜κ³  README의 μ‹€ν–‰ μˆœμ„œλ₯Ό check β†’ openapi β†’ client β†’ codegen으둜 κ°±μ‹ ν•œλ‹€. ν…œν”Œλ¦Ώ λΉŒλ“œ ν…ŒμŠ€νŠΈμ™€ 슀λͺ¨ν¬ νŒŒμ΄ν”„λΌμΈμ— --check 단계가 반영되며, changeset 메타데이터가 μΆ”κ°€λœλ‹€.
transports-http: catch-all 경둜 νŒŒλΌλ―Έν„° λŸ°νƒ€μž„ μ •κ·œν™”
packages/transports-http/src/libs/RouteCompiler.ts, packages/transports-http/src/tests/CrocoApp.spec.ts
RouteCompilerμ—μ„œ 경둜 컴파일 μ‹œ toRuntimeRoutePathλ₯Ό μΆ”κ°€λ‘œ ν˜ΈμΆœν•˜μ—¬ catch-all νŒŒλΌλ―Έν„°(:...id) ν˜•νƒœλ₯Ό λŸ°νƒ€μž„ 라우트 νŒ¨ν„΄μœΌλ‘œ μ •κ·œν™”ν•œλ‹€. ν…ŒμŠ€νŠΈμ— catch-all 경둜 νŒŒλΌλ―Έν„° μΆ”μΆœ μΌ€μ΄μŠ€λ₯Ό μΆ”κ°€ν•˜μ—¬ /api/assets/:...id 라우트 λ™μž‘μ„ κ²€μ¦ν•œλ‹€.
νŒ¨ν‚€μ§€ μ˜μ‘΄μ„± 및 개발 μ˜μ‘΄μ„± μΆ”κ°€
packages/protocols-core/package.json, packages/openapi-spec/package.json, packages/rpc-codegen/package.json, packages/cli/package.json
protocols-core/openapi-spec/rpc-codegen이 @croco/problems-core(workspace:*)λ₯Ό μ˜μ‘΄μ„±μ— μΆ”κ°€ν•˜κ³ , protocols-core/openapi-spec/cliκ°€ typedi(0.10.0)λ₯Ό 개발 μ˜μ‘΄μ„±μ— μΆ”κ°€ν•œλ‹€.

Sequence Diagram(s)

sequenceDiagram
  actor Developer
  participant CrocoCLI as croco CLI
  participant runContractsCheck
  participant rpcCodegenCLI as croco-rpc-codegen --check
  participant loadContractGraph
  participant buildContractGraph
  participant assertContractGraphHasNoErrors

  Developer->>CrocoCLI: croco contracts check --controllers '...'
  CrocoCLI->>runContractsCheck: rawArgs
  runContractsCheck->>rpcCodegenCLI: spawn(node, [dist/cli.js, --check, ...args])
  rpcCodegenCLI->>loadContractGraph: glob νŒ¨ν„΄
  loadContractGraph->>buildContractGraph: controllerConstructors[]
  buildContractGraph-->>loadContractGraph: ContractGraph (routes, diagnostics)
  loadContractGraph-->>rpcCodegenCLI: ContractGraph
  rpcCodegenCLI->>assertContractGraphHasNoErrors: ContractGraph
  alt μ—λŸ¬ μ—†μŒ
    assertContractGraphHasNoErrors-->>rpcCodegenCLI: 톡과
    rpcCodegenCLI-->>Developer: exit 0
  else μ—λŸ¬ 있음
    assertContractGraphHasNoErrors-->>rpcCodegenCLI: ContractGraphDiagnosticError
    rpcCodegenCLI-->>Developer: 진단 좜λ ₯ + exit 1
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

  • croco-dev/framework#684: λ³Έ PR의 contract-graph 검증·생성 νŒŒμ΄ν”„λΌμΈμ΄ @All 라우트λ₯Ό κ±°λΆ€ν•˜λ©°, @croco/openapi-specκ³Ό @croco/rpc-codegen μ–‘μͺ½μ—μ„œ λ™μΌν•œ 진단 μ½”λ“œλ‘œ 이λ₯Ό κ΅¬ν˜„ν•˜μ—¬, ν•΄λ‹Ή PR의 @All κ±°λΆ€ λͺ©ν‘œμ™€ μ§κ²°λœλ‹€.
  • croco-dev/framework#688: λ³Έ PR의 rpc-codegen이 μž„ν¬νŠΈλœ λͺ¨λ“ˆλ‘œλΆ€ν„° REST 컨트둀러 μƒμ„±μžλ₯Ό 메타데이터 기반 발견으둜 μˆ˜μ§‘ν•˜μ—¬ contract graphλ₯Ό λΉŒλ“œν•˜λ©°, ν•΄λ‹Ή PR의 컨트둀러 λ°œκ²¬Β·λ‘œλ”© 메타데이터 λ ˆμ΄μ–΄μ™€ 직접 κ΄€λ ¨λœλ‹€.
πŸš₯ Pre-merge checks | βœ… 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
βœ… Passed checks (4 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed 제λͺ© 'fix: validate generated REST contracts'λŠ” PR의 핡심 변경사항을 λͺ…ν™•ν•˜κ³  κ°„κ²°ν•˜κ²Œ μš”μ•½ν•©λ‹ˆλ‹€. μƒˆλ‘œμš΄ ContractGraph 검증 κΈ°λŠ₯ λ„μž…μ΄λΌλŠ” μ£Όμš” λͺ©ν‘œμ™€ μΌμΉ˜ν•©λ‹ˆλ‹€.
Linked Issues check βœ… Passed PR이 #715 μš”κ΅¬μ‚¬ν•­μ„ μ™„λ²½νžˆ μΆ©μ‘±ν•©λ‹ˆλ‹€: ContractGraph μ •μ˜ 및 생성 [protocols-core], OpenAPI/RPC 생성 톡합 [openapi-spec/rpc-codegen], contract check λͺ…λ Ή μΆ”κ°€ [cli], ν…œν”Œλ¦Ώ μ—…λ°μ΄νŠΈ [create-croco-app].
Out of Scope Changes check βœ… Passed λͺ¨λ“  변경사항이 ContractGraph 검증 κΈ°λŠ₯ κ΅¬ν˜„κ³Ό 직접 관련이 μžˆμŠ΅λ‹ˆλ‹€. λΆˆν•„μš”ν•œ μ™ΈλΆ€ λ³€κ²½μ΄λ‚˜ λ²”μœ„ λ²—μ–΄λ‚œ μˆ˜μ •μ΄ μ—†μŠ΅λ‹ˆλ‹€.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/715-contract-graph

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.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown

πŸ“Š Benchmark Results

❌ Some benchmarks failed

Gate failures

  • Lambda cold-start simulation: p75 210.6ΞΌs exceeds baseline 70.2ΞΌs by more than 20%
  • Lambda cold-start with headers: p75 146.9ΞΌs exceeds baseline 66.7ΞΌs by more than 20%
  • Lambda cold-start with binary body: p75 125.9ΞΌs exceeds baseline 63.2ΞΌs by more than 20%
  • Lambda cold-start with query params: p75 113.8ΞΌs exceeds baseline 63.9ΞΌs by more than 20%
  • Lambda cold-start with authorizer context: p75 118.3ΞΌs exceeds baseline 59.8ΞΌs by more than 20%
  • Lambda cold-start realistic scenario: p75 114.6ΞΌs exceeds baseline 60.2ΞΌs by more than 20%
  • EventBusConfig.start (10 handlers): p75 1.3ΞΌs exceeds baseline 0.9ΞΌs by more than 20%
  • EventPublisher.publishNow single event: p75 1.6ΞΌs exceeds baseline 1.1ΞΌs by more than 20%
  • DefaultHandlerResolver.resolve Γ— 10: p75 0.1ΞΌs exceeds baseline 0.0ΞΌs by more than 20%
  • Container.get singleton (cold): p75 1.0ΞΌs exceeds baseline 0.6ΞΌs by more than 20%
  • Container.get singleton (warm): p75 0.4ΞΌs exceeds baseline 0.3ΞΌs by more than 20%
  • lambdaPreset config creation: p75 1.5ΞΌs exceeds baseline 1.0ΞΌs by more than 20%
Benchmark p75 Threshold Baseline vs Baseline Status Notes
CrocoApp constructor 3.0ΞΌs 30.0ms 3.4ΞΌs -9.7% βœ… -
CrocoApp lambdaHandler (10 controllers) 37.1ΞΌs 50.0ms 33.3ΞΌs +11.5% βœ… -
Lambda cold-start simulation 210.6μs 80.0ms 70.2μs +200.1% ❌ -
Lambda cold-start with headers 146.9μs 80.0ms 66.7μs +120.1% ❌ -
Lambda cold-start with binary body 125.9μs 80.0ms 63.2μs +99.3% ❌ -
Lambda cold-start with query params 113.8μs 80.0ms 63.9μs +78.1% ❌ -
Lambda cold-start with authorizer context 118.3μs 80.0ms 59.8μs +97.8% ❌ -
Lambda cold-start realistic scenario 114.6μs 80.0ms 60.2μs +90.5% ❌ -
EventBusConfig.start (10 handlers) 1.3μs 10.0ms 0.9μs +50.1% ❌ -
EventPublisher.publishNow single event 1.6μs 2.0ms 1.1μs +46.2% ❌ -
DefaultHandlerResolver.resolve Γ— 10 0.1ΞΌs 5.0ms 0.0ΞΌs +90.5% ❌ -
Container.get singleton (cold) 1.0μs 5.0ms 0.6μs +58.6% ❌ -
Container.register Γ— 50 components 12.1ΞΌs 10.0ms 12.1ΞΌs +0.1% βœ… -
Container.validate (50 components) 29.4ΞΌs 20.0ms 29.7ΞΌs -1.0% βœ… -
Container.get singleton (warm) 0.4μs 500.0μs 0.3μs +47.6% ❌ -
TelemetryRuntime.init (lambda preset) 1.1ms 200.0ms 69.3ms -98.5% βœ… -
lambdaPreset config creation 1.5μs 2.0ms 1.0μs +52.3% ❌ -

Updated: 2026-06-16T06:49:23.839Z Β· Commit: 325bded

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/openapi-spec/src/libs/emitOpenAPI.ts (1)

301-317: ⚠️ Potential issue | 🟠 Major | ⚑ Quick win

toHttpMethodμ—μ„œ generic Errorλ₯Ό throw ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

μ—λŸ¬ 경둜(@All, unsupported method) λͺ¨λ‘ Problem subclass둜 톡일해야 μƒμœ„ κ³„μΈ΅μ˜ RFC7807 처리 계약이 μœ μ§€λ©λ‹ˆλ‹€.
As per coding guidelines, "Throw only Problem subclasses for error handling; never throw generic Error instances (RFC 7807 Problem-based error handling)."

πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openapi-spec/src/libs/emitOpenAPI.ts` around lines 301 - 317, The
toHttpMethod function is throwing generic Error instances instead of Problem
subclass instances, which breaks the RFC7807 error handling contract in upper
layers. Replace both throw statements in toHttpMethod (the one for the "`@All`"
route case and the one for the unsupported HTTP method case) with appropriate
Problem subclass instances instead of generic Error, ensuring the descriptive
error messages are preserved in the Problem instances.

Source: Coding guidelines

πŸ€– Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/src/tests/contractsCheck.spec.ts`:
- Around line 4-63: The test file contractsCheck.spec.ts is missing the required
DI Container reset for test isolation. Add a beforeEach hook inside the describe
block that calls Container.reset() to ensure proper test isolation as per the
project's testing guidelines. Import the Container from the appropriate DI
module and call Container.reset() within a beforeEach() function that wraps all
the it test cases in the describe block.

In `@packages/create-croco-app/src/tests/templates-build.spec.ts`:
- Line 87: The validation for the `contract:check` key is too lenient as it only
checks for the presence of `--check` flag using expect.stringContaining, which
could allow incorrect command strings to pass the test. Strengthen this
assertion by also verifying that the command body `croco-rpc-codegen` is present
in the value, ensuring the complete and correct command is validated. Use a
matcher that checks for both the command name and the flag to prevent false
positives.

In `@packages/create-croco-app/templates/spa-be-split/package.json.hbs`:
- Around line 8-11: The contract:check script is defined but not enforced as a
prerequisite before code generation runs, allowing contract:openapi and
contract:client to be executed directly without validation. Update the codegen
script to chain contract:check before contract:client by modifying its value to
first run the validation step, ensuring validation is always performed before
any code generation occurs at the template level.

In `@packages/openapi-spec/src/tests/emitOpenAPI.spec.ts`:
- Around line 19-22: Add the missing test initialization setup to comply with
coding guidelines. First, update the vitest import statement to include
beforeEach alongside the existing describe, expect, and it imports. Then, add a
beforeEach hook after the imports that calls Container.reset() to ensure the DI
Container is reset before each test runs. This addresses the required testing
utilities import set and the mandatory Container cleanup pattern that must be
applied throughout the test file (lines 23-439).

In `@packages/protocols-core/src/libs/ContractGraph.ts`:
- Around line 41-48: The ContractGraphDiagnosticError class extends Error
instead of Problem, which violates the RFC 7807 Problem-based error handling
policy. Change the base class of ContractGraphDiagnosticError from Error to
Problem, and update the constructor to properly initialize the Problem base
class with appropriate problem details (such as type, title, status, and detail
fields) while maintaining the diagnostics field and its initialization.
- Around line 81-82: The code currently validates routeId uniqueness using
validateUniqueRouteIds but lacks validation for operationId uniqueness, which
can cause naming collisions in OpenAPI client generation. Add a new validation
function (similar to validateUniqueRouteIds) to check for duplicate normalized
operationIds across the graph routes, then call this validation function and
push its results to the diagnostics array alongside the existing
validateUniqueRouteIds call at the anchor location (line 81-82). Also add the
same operationId validation check at the sibling locations (lines 113-121 and
256-279) where similar diagnostics collection occurs, ensuring operationId
uniqueness is validated consistently across all graph validation points.

In `@packages/protocols-core/src/tests/ContractGraph.spec.ts`:
- Around line 2-3: The test file is missing required vitest and DI container
initialization patterns per coding guidelines. Add beforeEach to the vitest
imports at the top of the file, and import the Container class from the DI
container module. Then add a beforeEach() block that calls Container.reset() to
ensure the dependency injection container is reset before each test runs. This
setup applies to the entire test suite in ContractGraph.spec.ts and ensures test
isolation according to the project's testing standards.

In `@packages/rpc-codegen/src/libs/generate.ts`:
- Around line 73-80: The assertGeneratedClientPathParams function is missing a
validation check in one direction: it does not verify that all parameters
present in schemaParamNames also exist in pathParamNames. Currently it only
checks path→declared, path→schema, and declared→path directions, but not
schema→path. Add a validation assertion that ensures every parameter name in
schemaParamNames is contained in pathParamNames, blocking cases where
inputSchemas.path contains keys that do not exist in the actual route path. This
will prevent invalid shape influx in the RouteIR validation.
- Around line 63-66: The code is throwing generic Error instances at multiple
locations (lines 63-66, 84-86, 90-92, and 98-100) in
packages/rpc-codegen/src/libs/generate.ts, which violates RFC 7807 Problem-based
error handling guidelines. Replace all instances of new Error with appropriate
Problem subclasses at all four locations. For the error at lines 63-66 that
checks bodyParamCount and calls formatRoute(route), and similarly for the errors
at lines 84-86, 90-92, and 98-100, use the corresponding Problem subclass type
instead of generic Error to maintain consistency with the Problem-based error
contract.

---

Outside diff comments:
In `@packages/openapi-spec/src/libs/emitOpenAPI.ts`:
- Around line 301-317: The toHttpMethod function is throwing generic Error
instances instead of Problem subclass instances, which breaks the RFC7807 error
handling contract in upper layers. Replace both throw statements in toHttpMethod
(the one for the "`@All`" route case and the one for the unsupported HTTP method
case) with appropriate Problem subclass instances instead of generic Error,
ensuring the descriptive error messages are preserved in the Problem instances.
πŸͺ„ Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e915f948-09b2-424d-b12f-c083baec448f

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between f4560b0 and a258d1e.

πŸ“’ Files selected for processing (24)
  • .changeset/contract-graph-check.md
  • packages/cli/src/bin/croco.ts
  • packages/cli/src/commands/contracts.ts
  • packages/cli/src/commands/contractsCheck.ts
  • packages/cli/src/index.ts
  • packages/cli/src/tests/contractsCheck.spec.ts
  • packages/create-croco-app/src/tests/templates-build.spec.ts
  • packages/create-croco-app/templates/spa-be-split/README.md.hbs
  • packages/create-croco-app/templates/spa-be-split/package.json.hbs
  • packages/openapi-spec/src/index.ts
  • packages/openapi-spec/src/libs/emitOpenAPI.ts
  • packages/openapi-spec/src/tests/emitOpenAPI.spec.ts
  • packages/protocols-core/src/index.ts
  • packages/protocols-core/src/libs/ContractGraph.ts
  • packages/protocols-core/src/tests/ContractGraph.spec.ts
  • packages/rpc-codegen/src/index.ts
  • packages/rpc-codegen/src/libs/cli.ts
  • packages/rpc-codegen/src/libs/generate.ts
  • packages/rpc-codegen/src/libs/loadRoutes.ts
  • packages/rpc-codegen/src/tests/Cli.spec.ts
  • packages/rpc-codegen/src/tests/ContractCheckCli.spec.ts
  • packages/rpc-codegen/src/tests/codegen.spec.ts
  • packages/rpc-codegen/src/tests/loadRoutes.spec.ts
  • scripts/create-croco-app-generated-smoke.mts

Comment thread packages/cli/src/tests/contractsCheck.spec.ts Outdated
Comment thread packages/create-croco-app/src/tests/templates-build.spec.ts Outdated
Comment thread packages/create-croco-app/templates/spa-be-split/package.json.hbs
Comment thread packages/openapi-spec/src/tests/emitOpenAPI.spec.ts Outdated
Comment thread packages/protocols-core/src/libs/ContractGraph.ts Outdated
Comment thread packages/protocols-core/src/libs/ContractGraph.ts
Comment thread packages/protocols-core/src/tests/ContractGraph.spec.ts Outdated
Comment thread packages/rpc-codegen/src/libs/generate.ts
Comment thread packages/rpc-codegen/src/libs/generate.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/rpc-codegen/src/libs/generate.ts (2)

552-554: ⚠️ Potential issue | 🟠 Major | ⚑ Quick win

:...param 경둜 νŒŒλΌλ―Έν„° νŒŒμ‹±μ΄ 잘λͺ»λ˜μ–΄ 정상 λΌμš°νŠΈκ°€ κ±°λΆ€λ©λ‹ˆλ‹€

Line 553μ—μ„œ :...idλ₯Ό ...id둜 보관해 @Param('id')/inputSchemas.path.id와 λΆˆμΌμΉ˜κ°€ λ°œμƒν•©λ‹ˆλ‹€. catch-all κ²½λ‘œκ°€ μ˜€νƒμœΌλ‘œ μ‹€νŒ¨ν•©λ‹ˆλ‹€.

μˆ˜μ • μ œμ•ˆ
 function getRoutePathParamNames(pathname: string): string[] {
-  return [...pathname.matchAll(/:([^/]+)/g)].map((match) => match[1]).filter(Boolean);
+  return [...pathname.matchAll(/:([^/]+)/g)]
+    .map((match) => match[1].replace(/^\.\.\./, ''))
+    .filter((name) => name.length > 0);
 }
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/rpc-codegen/src/libs/generate.ts` around lines 552 - 554, The
getRoutePathParamNames function is capturing path parameter names including
spread operator syntax (e.g., capturing `...id` from `:...id`), but the
corresponding decorator names and schema keys only use the base parameter name
(e.g., `id`). Modify the function to strip the leading `...` from catch-all
parameters after extracting them with the regex, so that both `:id` and `:...id`
route definitions return the same parameter name `id` from this function.

626-633: ⚠️ Potential issue | 🟠 Major

RFC 7807 Problem 기반 μ˜ˆμ™Έ 처리 계약 μœ„λ°˜

Line 632μ—μ„œ throw new Error(...)λ₯Ό μ‚¬μš©ν•˜κ³  있으며, μ΄λŠ” μ½”λ”© κ°€μ΄λ“œλΌμΈ "Throw only Problem subclasses for error handling; never throw generic Error instances (RFC 7807 Problem-based error handling)"에 μœ„λ°°λ©λ‹ˆλ‹€. 파일 λ‚΄ λ‹€λ₯Έ λͺ¨λ“  μ˜ˆμ™Έ 처리(lines 63, 71, 91, 97, 105, 113)μ—μ„œλŠ” RpcCodegenContractProblem을 μ‚¬μš©ν•˜λ―€λ‘œ, 이 ν•¨μˆ˜λ„ 톡일해야 ν•©λ‹ˆλ‹€.

μˆ˜μ • μ œμ•ˆ
 function assertNoZodImport(content: string): void {
   if (
     content.includes("from 'zod'") ||
     content.includes("import { z }") ||
     content.includes("zod")
   ) {
-    throw new Error("Generated client must not import zod.");
+    throw new RpcCodegenContractProblem('Generated client must not import zod.');
   }
 }
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/rpc-codegen/src/libs/generate.ts` around lines 626 - 633, The
assertNoZodImport function throws a generic Error instead of
RpcCodegenContractProblem, which violates the coding guideline that requires RFC
7807 Problem-based error handling. Replace the throw new Error statement in the
assertNoZodImport function with throw new RpcCodegenContractProblem to maintain
consistency with all other error handling in the file (as seen at lines 63, 71,
91, 97, 105, 113). Keep the error message content the same, just wrap it in
RpcCodegenContractProblem instead of Error.

Source: Coding guidelines

πŸ€– Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/rpc-codegen/src/libs/generate.ts`:
- Around line 552-554: The getRoutePathParamNames function is capturing path
parameter names including spread operator syntax (e.g., capturing `...id` from
`:...id`), but the corresponding decorator names and schema keys only use the
base parameter name (e.g., `id`). Modify the function to strip the leading `...`
from catch-all parameters after extracting them with the regex, so that both
`:id` and `:...id` route definitions return the same parameter name `id` from
this function.
- Around line 626-633: The assertNoZodImport function throws a generic Error
instead of RpcCodegenContractProblem, which violates the coding guideline that
requires RFC 7807 Problem-based error handling. Replace the throw new Error
statement in the assertNoZodImport function with throw new
RpcCodegenContractProblem to maintain consistency with all other error handling
in the file (as seen at lines 63, 71, 91, 97, 105, 113). Keep the error message
content the same, just wrap it in RpcCodegenContractProblem instead of Error.

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 58e33135-3381-4d99-ade9-8ce1e470db59

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between a258d1e and c4ce87b.

β›” Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
πŸ“’ Files selected for processing (17)
  • .changeset/contract-graph-check.md
  • packages/cli/package.json
  • packages/cli/src/tests/contractsCheck.spec.ts
  • packages/create-croco-app/src/tests/templates-build.spec.ts
  • packages/create-croco-app/templates/spa-be-split/package.json.hbs
  • packages/openapi-spec/package.json
  • packages/openapi-spec/src/libs/emitOpenAPI.ts
  • packages/openapi-spec/src/libs/loadControllers.ts
  • packages/openapi-spec/src/tests/emitOpenAPI.spec.ts
  • packages/protocols-core/package.json
  • packages/protocols-core/src/libs/ContractGraph.ts
  • packages/protocols-core/src/tests/ContractGraph.spec.ts
  • packages/rpc-codegen/package.json
  • packages/rpc-codegen/src/libs/generate.ts
  • packages/rpc-codegen/src/libs/loadRoutes.ts
  • packages/rpc-codegen/src/tests/codegen.spec.ts
  • scripts/create-croco-app-generated-smoke.mts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/protocols-core/src/tests/ContractGraph.spec.ts (1)

3-3: ⚠️ Potential issue | 🟠 Major | ⚑ Quick win

vitest ν•„μˆ˜ import μ„ΈνŠΈμ—μ„œ viκ°€ λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

Line 3μ—μ„œ vi importκ°€ λΉ μ Έ μžˆμ–΄ ν…ŒμŠ€νŠΈ κ·œμΉ™κ³Ό λΆˆμΌμΉ˜ν•©λ‹ˆλ‹€. beforeEachμ—μ„œ vi.restoreAllMocks()λ₯Ό ν•¨κ»˜ ν˜ΈμΆœν•˜λ©΄ κ·œμΉ™κ³Ό 격리성을 같이 맞좜 수 μžˆμŠ΅λ‹ˆλ‹€.

As per coding guidelines, "Test files must import testing utilities from 'vitest' (beforeEach, describe, expect, it, vi)".

πŸ”§ μ œμ•ˆ μˆ˜μ •μ•ˆ
-import { beforeEach, describe, expect, it } from "vitest";
+import { beforeEach, describe, expect, it, vi } from "vitest";

 beforeEach(() => {
   Container.reset();
+  vi.restoreAllMocks();
 });

Also applies to: 23-25

πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/protocols-core/src/tests/ContractGraph.spec.ts` at line 3, The
import statement is missing `vi` from the vitest utilities, which violates the
coding guidelines that require importing `beforeEach, describe, expect, it, vi`
from vitest. Add `vi` to the import statement on line 3, then locate the
`beforeEach` hook (around lines 23-25) and call `vi.restoreAllMocks()` within it
to ensure proper test isolation and mock restoration between test cases.

Source: Coding guidelines

πŸ€– Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/openapi-spec/src/libs/emitOpenAPI.ts`:
- Around line 305-309: The path token substitution in the `toOpenAPIPath`
function uses a simple string split/join approach that incorrectly matches
parameter name prefixes. When a path contains parameters like `:id` and `:id2`,
the current logic splits on `:id` and corrupts `:id2` to `{id}2`. Replace the
simple split and join in the reduce callback with a boundary-based substitution
method, such as using a regex pattern that matches the parameter token only when
followed by a path delimiter or end of string, to ensure exact parameter
matching without prefix collisions.

In `@packages/rpc-codegen/src/libs/generate.ts`:
- Around line 535-540: The pathExpression assignment in the pathParams.reduce
function has a prefix collision issue where replacing shorter parameter tokens
like `:id` before longer ones like `:id2` corrupts the longer tokens during
string replacement. Fix this by either sorting pathParams in descending order by
token length before processing, or by replacing all path parameters in a single
regex-based operation instead of sequential string replacements, ensuring that
longer token names are processed first and preventing any prefix matching
issues.

---

Outside diff comments:
In `@packages/protocols-core/src/tests/ContractGraph.spec.ts`:
- Line 3: The import statement is missing `vi` from the vitest utilities, which
violates the coding guidelines that require importing `beforeEach, describe,
expect, it, vi` from vitest. Add `vi` to the import statement on line 3, then
locate the `beforeEach` hook (around lines 23-25) and call
`vi.restoreAllMocks()` within it to ensure proper test isolation and mock
restoration between test cases.
πŸͺ„ Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d0cb04e5-d3dc-4b42-a8e9-edb8762351d4

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between c4ce87b and 7fba88f.

πŸ“’ Files selected for processing (15)
  • .changeset/contract-graph-check.md
  • packages/openapi-spec/src/libs/emitOpenAPI.ts
  • packages/openapi-spec/src/tests/emitOpenAPI.spec.ts
  • packages/protocols-core/src/index.ts
  • packages/protocols-core/src/libs/ContractGraph.ts
  • packages/protocols-core/src/libs/sharedTypes.ts
  • packages/protocols-core/src/tests/ContractGraph.spec.ts
  • packages/protocols-core/src/tests/helpers/test-decorators.ts
  • packages/rpc-codegen/src/libs/cli.ts
  • packages/rpc-codegen/src/libs/generate.ts
  • packages/rpc-codegen/src/tests/Cli.spec.ts
  • packages/rpc-codegen/src/tests/ContractCheckCli.spec.ts
  • packages/rpc-codegen/src/tests/PublishedCli.spec.ts
  • packages/rpc-codegen/src/tests/codegen.spec.ts
  • packages/rpc-codegen/src/tests/loadRoutes.spec.ts
πŸ’€ Files with no reviewable changes (1)
  • .changeset/contract-graph-check.md

Comment thread packages/openapi-spec/src/libs/emitOpenAPI.ts
Comment thread packages/rpc-codegen/src/libs/generate.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/protocols-core/src/libs/ContractGraph.ts (1)

325-341: ⚠️ Potential issue | 🟠 Major

μ€‘μ²©λœ Zod effects도 진단 λŒ€μƒμœΌλ‘œ μˆœνšŒν•΄ μ£Όμ„Έμš”.

ν˜„μž¬ μ½”λ“œλŠ” getRouteSchemasκ°€ λ°˜ν™˜ν•˜λŠ” μ΅œμƒμœ„ μŠ€ν‚€λ§ˆλ§Œ isZodEffects둜 κ²€μ‚¬ν•©λ‹ˆλ‹€. z.object({ name: z.string().transform(...) })처럼 객체 내뢀에 μžˆλŠ” transform/refinementλŠ” 객체 μžμ²΄κ°€ ZodEffectsκ°€ μ•„λ‹ˆλ―€λ‘œ κ²½κ³  없이 ν†΅κ³Όν•©λ‹ˆλ‹€. 이 경우 μƒμ„±λœ 계약은 μ—¬μ „νžˆ 이런 effectsλ₯Ό ν‘œν˜„ν•˜μ§€ λͺ»ν•˜λŠ”λ°, 검증 둜직이 이λ₯Ό κ°μ§€ν•˜μ§€ μ•Šμ•„ drift κ°€λŠ₯성이 μˆ¨κ²¨μ§‘λ‹ˆλ‹€.

ZodObject의 .shape 속성을 톡해 μ€‘μ²©λœ ν•„λ“œμ— μ ‘κ·Όν•˜κ³  μž¬κ·€μ μœΌλ‘œ effectsλ₯Ό κ²€μ‚¬ν•˜λŠ” 둜직이 ν•„μš”ν•©λ‹ˆλ‹€.

κ²€μ¦μš© ν…ŒμŠ€νŠΈ μ˜ˆμ‹œ
   it("should warn when generated contracts unwrap Zod effects", () => {
@@
   });
+
+  it("should warn when generated contracts unwrap nested Zod effects", () => {
+    `@Controller`("/profiles")
+    class ProfilesController {
+      `@Post`("/")
+      createProfile(
+        `@Body`(z.object({ name: z.string().transform((value) => value.trim()) }))
+        _body: { name: string },
+      ): void {}
+    }
+
+    const graph = buildContractGraph([ProfilesController]);
+
+    expect(graph.diagnostics).toEqual([
+      expect.objectContaining({
+        code: "contract-schema-zod-effects-unwrapped",
+        severity: "warning",
+        routeId: "ProfilesController.createProfile",
+      }),
+    ]);
+  });
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/protocols-core/src/libs/ContractGraph.ts` around lines 325 - 341,
The validateSchemaEffects function currently only checks top-level schemas
returned by getRouteSchemas for ZodEffects, but misses nested effects that exist
within ZodObject properties (such as z.string().transform(...) nested inside
z.object()). Add a recursive helper function that traverses into ZodObject's
.shape property to inspect nested schemas, and modify the main loop in
validateSchemaEffects to call this helper function for each top-level schema.
This recursive traversal should detect ZodEffects at any depth and generate
diagnostics for each occurrence, preventing the drift that occurs when generated
contracts cannot represent these hidden effects.
πŸ€– Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/protocols-core/src/libs/ContractGraph.ts`:
- Around line 477-500: The getMetadataReference function is silently discarding
guard metadata when the function or constructor has an empty name by returning
null, which causes incomplete access metadata. Instead of filtering out guards
based on name.length > 0 in both the function check and the constructor check,
you should handle unnamed guards appropriatelyβ€”either by creating references
with a generated or placeholder name, or by logging warnings about skipped
guards rather than silently returning null. This ensures that guard metadata is
preserved in the access graph even when names are missing, preventing incomplete
contract metadata and missing drift diagnostics.

In `@packages/protocols-core/src/tests/ContractGraph.spec.ts`:
- Around line 239-240: Remove the definite assignment assertion (!) from the
FirstController and SecondController variable declarations. These are local
variables within a test block that are assigned immediately, not test instance
variables declared in describe blocks. According to the coding guidelines, the
non-null assertion (!) is prohibited for local test variables and should only be
used for test instance variables declared in describe scopes. Simply declare the
variables without the ! operator.

In `@packages/rpc-codegen/src/libs/generate.ts`:
- Around line 535-540: The path parameter accessor in the replace callback of
the pathExpression generation currently uses dot notation (input.path.${name})
which fails for parameter names containing hyphens or other characters that are
not valid JavaScript identifiers. Modify the return statement in the replace
callback to check if the parameter name is a valid JavaScript identifier; if it
is, use dot notation as currently implemented, but if it is not, use bracket
notation with quoted string notation (input.path['${name}']) instead. This
ensures that path parameters like user-id are accessed correctly as
input.path['user-id'] rather than the invalid input.path.user-id.

In `@packages/rpc-codegen/src/tests/codegen.spec.ts`:
- Line 473: Remove the `as any` type assertion from the path schema object in
the test. Since this object is being passed as part of a `RouteIR[]` array, it
has contextual typing that will properly infer the types without needing the
explicit `as any` cast. This aligns with the coding guideline that prohibits
explicit use of the any type. Simply delete the `as any` portion while keeping
the z.object({ id: z.string(), id2: z.string() }) expression as is.

---

Outside diff comments:
In `@packages/protocols-core/src/libs/ContractGraph.ts`:
- Around line 325-341: The validateSchemaEffects function currently only checks
top-level schemas returned by getRouteSchemas for ZodEffects, but misses nested
effects that exist within ZodObject properties (such as
z.string().transform(...) nested inside z.object()). Add a recursive helper
function that traverses into ZodObject's .shape property to inspect nested
schemas, and modify the main loop in validateSchemaEffects to call this helper
function for each top-level schema. This recursive traversal should detect
ZodEffects at any depth and generate diagnostics for each occurrence, preventing
the drift that occurs when generated contracts cannot represent these hidden
effects.
πŸͺ„ Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7b725239-fbaa-455d-8826-8dce7398be5a

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 7fba88f and 64df88d.

πŸ“’ Files selected for processing (10)
  • .changeset/contract-graph-check.md
  • packages/openapi-spec/src/libs/emitOpenAPI.ts
  • packages/openapi-spec/src/tests/emitOpenAPI.spec.ts
  • packages/protocols-core/src/index.ts
  • packages/protocols-core/src/libs/ContractGraph.ts
  • packages/protocols-core/src/tests/ContractGraph.spec.ts
  • packages/rpc-codegen/src/libs/generate.ts
  • packages/rpc-codegen/src/tests/codegen.spec.ts
  • packages/transports-http/src/libs/RouteCompiler.ts
  • packages/transports-http/src/tests/CrocoApp.spec.ts

Comment thread packages/protocols-core/src/libs/ContractGraph.ts
Comment thread packages/protocols-core/src/tests/ContractGraph.spec.ts Outdated
Comment thread packages/rpc-codegen/src/libs/generate.ts
Comment thread packages/rpc-codegen/src/tests/codegen.spec.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/protocols-core/src/libs/ContractGraph.ts (1)

436-438: 🧹 Nitpick | πŸ”΅ Trivial | πŸ’€ Low value

constructor.name 기반 μ²΄ν¬λŠ” μ½”λ“œ λ‚œλ…ν™” ν™˜κ²½μ—μ„œ μ·¨μ•½ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

isZodEffects ν•¨μˆ˜κ°€ schema.constructor.name === "ZodEffects"λ₯Ό μ‚¬μš©ν•˜λŠ”λ°, μ΄λŠ” minification λ˜λŠ” λ²ˆλ“€λŸ¬ 섀정에 따라 클래슀 이름이 변경될 경우 λ™μž‘ν•˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.

μ„œλ²„ μ‚¬μ΄λ“œ μ½”λ“œλ‘œμ„œ 일반적으둜 λ‚œλ…ν™”λ˜μ§€ μ•ŠμœΌλ―€λ‘œ ν˜„μž¬ κ΅¬ν˜„μ€ μ‹€μš©μ μ΄μ§€λ§Œ, ν–₯ν›„ 이슈 κ°€λŠ₯성을 인지해 λ‘μ‹œκΈ° λ°”λžλ‹ˆλ‹€. Zod의 schema._def.typeName 접근도 κ³ λ €ν•  수 μžˆμœΌλ‚˜, λ‚΄λΆ€ API μ˜μ‘΄μ„± μΈ‘λ©΄μ—μ„œλŠ” ν˜„μž¬ 방식도 ν•©λ¦¬μ μž…λ‹ˆλ‹€.

πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/protocols-core/src/libs/ContractGraph.ts` around lines 436 - 438,
The isZodEffects function currently relies on schema.constructor.name to detect
ZodEffects instances, which can fail if code is minified or obfuscated. Replace
the constructor.name check with a more robust approach by accessing Zod's
internal _def.typeName property (e.g., checking if schema._def?.typeName ===
"ZodEffects"), which is more reliable across different bundling and obfuscation
scenarios. If using the internal _def API is not preferred, add a comment
explaining the current limitation and the assumption that server-side code will
not be obfuscated.
πŸ€– Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/protocols-core/src/libs/ContractGraph.ts`:
- Around line 436-438: The isZodEffects function currently relies on
schema.constructor.name to detect ZodEffects instances, which can fail if code
is minified or obfuscated. Replace the constructor.name check with a more robust
approach by accessing Zod's internal _def.typeName property (e.g., checking if
schema._def?.typeName === "ZodEffects"), which is more reliable across different
bundling and obfuscation scenarios. If using the internal _def API is not
preferred, add a comment explaining the current limitation and the assumption
that server-side code will not be obfuscated.

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e5dbf8ff-7b12-4385-b04f-3c586e21f244

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 64df88d and fa1058e.

πŸ“’ Files selected for processing (5)
  • packages/protocols-core/src/libs/ContractGraph.ts
  • packages/protocols-core/src/tests/ContractGraph.spec.ts
  • packages/rpc-codegen/src/libs/generate.ts
  • packages/rpc-codegen/src/tests/codegen.spec.ts
  • packages/rpc-codegen/vitest.config.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Croco Contract Graph

1 participant