Skip to content

Eden Style Client Generation#75

Open
RawToast wants to merge 6 commits intomasterfrom
eden
Open

Eden Style Client Generation#75
RawToast wants to merge 6 commits intomasterfrom
eden

Conversation

@RawToast
Copy link
Copy Markdown
Owner

@RawToast RawToast commented Mar 29, 2026

Summary by CodeRabbit

  • New Features

    • Added a Treaty client generator and runtime for producing typed, nested HTTP clients with structured success/error envelopes.
    • Added zenko treaty CLI command and a treatyOutput config option for generating treaty modules automatically.
    • Package now exposes treaty entrypoints for consumption.
  • Tests

    • Added unit and integration tests validating treaty generation, runtime client behavior, typing, and CLI flows.
  • Documentation

    • Added an implementation plan documenting the Treaty client roadmap and MVP scope.

- Add buildTreatyRouteTree, emitTreatyRouteTree, generateTreatyModuleFromMetadata
- Add zenko treaty CLI and optional treatyOutput in config
- Add zod devDependency for dynamic import of generated modules in tests
- Document plan in docs/plans
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 29, 2026

📝 Walkthrough

Walkthrough

This PR adds a Treaty client generation/runtime pipeline: the schema generator now emits per-operation operationMetadata (method, path, per-status response maps), and new generator/runtime modules build a nested treaty route tree and a Proxy-based, fetch-backed typed client plus CLI/config integration and tests.

Changes

Cohort / File(s) Summary
Docs
docs/plans/2026-03-29-zenko-treaty-client.md
New implementation plan describing two-pass treaty generation, testing, CLI/config integration, and MVP scope.
CLI & Config
packages/zenko/src/cli.ts, packages/zenko/zenko-config.schema.json
Added treaty subcommand, treatyOutput config option, generateTreatySingle and relative-import logic; updated help and config handling.
Core Treaty Runtime & Types
packages/zenko/src/treaty.ts, packages/zenko/src/treaty-types.ts, packages/zenko/src/treaty-infer.ts
New createTreatyClient factory (Proxy-based client), request/response envelope types, and TypeScript inference utilities for nested clients.
Generator & Tree Utilities
packages/zenko/src/treaty-generator.ts, packages/zenko/src/utils/treaty-tree.ts, packages/zenko/src/zenko.ts
Added treaty-module generator (from metadata), route-tree builder/serializer, and modified generator to emit operationMetadata in .gen.ts outputs.
Operation Parsing & Types
packages/zenko/src/core/operation-parser.ts, packages/zenko/src/types/operation.ts
Extended parseOperations and Operation type to include successResponses and errorResponses maps; added helpers to build those maps.
Exports, Build & Package Changes
packages/zenko/index.ts, packages/zenko/package.json, packages/zenko/tsdown.config.ts
Re-exported treaty generators/runtime/types, added ./treaty package export, adjusted Bun export targets, added treaty build entry, and added zod peerDependency.
Examples Integration
packages/examples/generate.js, packages/examples/tsconfig.json
Example generator updated to async IIFE; added generateTreatyModule usage and zenko/treaty path alias.
Tests
packages/examples/src/__tests__/train-travel-treaty-fetch.test.ts, packages/zenko/src/__tests__/treaty-*.test.ts, packages/zenko/src/__tests__/cli.test.ts, packages/zenko/src/__tests__/tictactoe.test.ts, packages/zenko/src/core/__tests__/operation-parser.test.ts, packages/zenko/src/utils/__tests__/treaty-tree.test.ts
Comprehensive tests added for treaty runtime behavior, type inference, generator output, CLI treaty flow, operationMetadata emission, and route-tree utilities.

Sequence Diagrams

sequenceDiagram
    participant User as User/CLI
    participant SchemaGen as Schema<br/>Generator
    participant OpParser as Operation<br/>Parser
    participant TreatyGen as Treaty<br/>Generator
    participant TreeBuilder as Route Tree<br/>Builder
    participant Output as Output<br/>File

    User->>SchemaGen: Invoke with spec
    SchemaGen->>OpParser: parseOperations()
    OpParser->>OpParser: Extract method, path,<br/>success/error response maps
    OpParser-->>SchemaGen: Operations + response maps
    SchemaGen->>Output: Write .gen.ts with<br/>operationMetadata

    User->>TreatyGen: generateTreatyModule(input .gen.ts)
    TreatyGen->>TreatyGen: Import .gen.ts & read metadata
    TreatyGen->>TreeBuilder: buildTreatyRouteTree(metadata)
    TreeBuilder-->>TreatyGen: Nested route tree
    TreatyGen->>Output: Write .treaty.gen.ts (routes + createClient)
Loading
sequenceDiagram
    participant App as Application
    participant Client as Treaty<br/>Client (proxy)
    participant Router as Proxy<br/>Router
    participant Fetcher as Fetch<br/>Layer
    participant Server as HTTP<br/>Server

    App->>Client: createTreatyClient({ baseUrl, routes })
    App->>Router: client.board.get()
    Router->>Router: Walk route tree, detect leaf
    Router->>Fetcher: Invoke leaf caller (opts)
    Fetcher->>Server: HTTP request (URL, method, body, headers)
    Server-->>Fetcher: HTTP response
    Fetcher->>App: TreatySuccess or TreatyFailure envelope
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I stitched routes from spec to code with care,

Paths nested gently, params in the air.
Metadata mapped each status and name,
A fetch-bound client hops into frame.
Hooray — a treaty, typed and fair! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Eden Style Client Generation' accurately reflects the main change: implementing treaty client generation with an Eden-style API. The PR adds comprehensive treaty client functionality including type-safe nested route trees, factories, and generators.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch eden

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.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 1, 2026

Codecov Report

❌ Patch coverage is 85.82090% with 57 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.11%. Comparing base (ee31d80) to head (db4eae5).

Files with missing lines Patch % Lines
packages/zenko/src/treaty.ts 80.68% 34 Missing ⚠️
packages/zenko/src/treaty-generator.ts 65.21% 16 Missing ⚠️
packages/zenko/src/utils/treaty-tree.ts 92.47% 7 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master      #75      +/-   ##
==========================================
- Coverage   97.92%   96.11%   -1.82%     
==========================================
  Files          16       19       +3     
  Lines        2309     2700     +391     
==========================================
+ Hits         2261     2595     +334     
- Misses         48      105      +57     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

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/zenko/package.json (1)

27-34: ⚠️ Potential issue | 🟠 Major

All bun export targets are excluded from the published package

The bun conditions in all three exports—., ./types, and ./treaty—point to source files (./index.ts, ./src/types.ts, ./src/treaty.ts) that are not covered by the files whitelist. The whitelist only includes dist/** artifacts (plus LICENSE and README.md), so installed consumers resolving the bun condition will fail at runtime due to missing files.

💡 Suggested fix (point all bun conditions to published dist files)
   "exports": {
     ".": {
       "types": "./dist/index.d.ts",
-      "bun": "./index.ts",
+      "bun": "./dist/index.mjs",
       "import": "./dist/index.mjs",
       "require": "./dist/index.cjs"
     },
     "./types": {
       "types": "./dist/types.d.ts",
-      "bun": "./src/types.ts",
+      "bun": "./dist/types.mjs",
       "import": "./dist/types.mjs",
       "require": "./dist/types.cjs"
     },
     "./treaty": {
       "types": "./dist/treaty.d.ts",
-      "bun": "./src/treaty.ts",
+      "bun": "./dist/treaty.mjs",
       "import": "./dist/treaty.mjs",
       "require": "./dist/treaty.cjs"
     }
   },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zenko/package.json` around lines 27 - 34, The package's "exports"
bun conditions (for "." , "./types", and "./treaty") point to source files
(e.g., "./index.ts", "./src/types.ts", "./src/treaty.ts") that are not included
in the "files" whitelist; update each bun condition to reference the published
build artifacts inside dist (for example point "." bun to the built ESM like
"./dist/index.mjs", "./types" bun to "./dist/src/types.mjs" or
"./dist/types.mjs" as appropriate, and "./treaty" bun to its corresponding
"./dist/..." artifact) and ensure those dist files are present in the package
files list so consumers resolving the bun condition will find the compiled
outputs.
🧹 Nitpick comments (9)
packages/zenko/src/__tests__/tictactoe.test.ts (1)

109-114: Use snapshot assertion for generated metadata output.

These string-fragment checks are brittle to formatting-only changes. Prefer a named snapshot for this generated block.

♻️ Suggested change
-    expect(result).toContain("export const operationMetadata = {")
-    expect(result).toContain("getSquare: {")
-    expect(result).toContain('method: "get"')
-    expect(result).toContain('path: "/board/{row}/{column}"')
-    expect(result).toContain('"200":"mark"')
-    expect(result).toContain('"400":"errorMessage"')
+    expect(result).toMatchSnapshot("tictactoe-operation-metadata-output")

As per coding guidelines "Use snapshot testing with expect(result).toMatchSnapshot() for output verification."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zenko/src/__tests__/tictactoe.test.ts` around lines 109 - 114,
Replace the brittle fragment assertions on the generated metadata block with a
snapshot assertion: remove the multiple expect(result).toContain(...) checks
that reference strings like "export const operationMetadata = {", "getSquare:
{", 'method: "get"', 'path: "/board/{row}/{column}"', '"200":"mark"', and
'"400":"errorMessage"' and instead call expect(result).toMatchSnapshot()
(optionally with a descriptive snapshot name) to capture the full generated
metadata output in one stable snapshot; update the test around the result
variable in the tictactoe test to use this single snapshot assertion.
packages/zenko/src/__tests__/treaty-runtime.test.ts (1)

48-70: Test name and asserted behavior are inconsistent.

The test says “JSON bodies” but validates a raw body string ("X"). Either serialize/assert JSON payload explicitly or rename the test to avoid ambiguity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zenko/src/__tests__/treaty-runtime.test.ts` around lines 48 - 70,
The test titled "walks dynamic segments and sends JSON bodies" is inconsistent
because it asserts a raw string body ("X") from the call via createTreatyClient
-> client.board(...).put; update the test to either (A) assert a JSON body by
calling .put with an object payload and expect fetchMock to receive body:
JSON.stringify(yourObject) and Content-Type: "application/json", or (B) rename
the test to reflect that it sends raw string bodies and keep the existing
assertion; adjust references in the assertion to use fetchMock and the
client.board({ row: "1" })({ column: "2" }).put call accordingly so the test
name and the fetchMock expect.objectContaining check are consistent.
packages/zenko/src/__tests__/cli.test.ts (1)

113-116: Prefer snapshots for treaty output verification.

For generated module content, these toContain checks are narrow and formatting-sensitive. Use named snapshots so structural changes are reviewed explicitly.

♻️ Suggested change
-    expect(output).toContain("export const treatyRoutes = {")
-    expect(output).toContain(
-      'import { createTreatyClient, type TreatyClient } from "zenko/treaty"'
-    )
+    expect(output).toMatchSnapshot("cli-treaty-command-output")
...
-    expect(treaty).toContain("export const treatyRoutes = {")
+    expect(treaty).toMatchSnapshot("cli-config-treaty-output")

As per coding guidelines "Use snapshot testing with expect(result).toMatchSnapshot() for output verification."

Also applies to: 207-209

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zenko/src/__tests__/cli.test.ts` around lines 113 - 116, Replace the
brittle string-contains assertions that check generated treaty module text (the
expectations referencing "export const treatyRoutes = {" and the import of
createTreatyClient/type TreatyClient) with a named snapshot assertion so the
entire generated output is compared structurally; locate the test that builds
the treaty module output (the variable currently asserted via
expect(output).toContain(...)) and change those assertions to a single
expect(<that output variable>).toMatchSnapshot('treaty-treatyModule-output')
(also update the analogous checks near the other occurrence mentioned) so
formatting changes are reviewed via snapshot diffs.
packages/examples/src/__tests__/train-travel-treaty-fetch.test.ts (1)

68-71: Avoid asserting full query-string ordering

Consider asserting query params via URL.searchParams instead of full URL string equality to reduce test brittleness when serialization order changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/examples/src/__tests__/train-travel-treaty-fetch.test.ts` around
lines 68 - 71, The test currently asserts the full URL string when checking
fetchMock calls
(`expect(fetchMock).toHaveBeenCalledWith(\`${origin}/stations?...`, which is
brittle due to query-string ordering); instead, extract the actual URL argument
from the fetchMock call (e.g., inspect fetchMock.mock.calls[0][0] or use
fetchMock.mock.calls to find the call) and create a new URL(...) from it, then
assert individual query params via url.searchParams.get('limit'),
url.searchParams.get('page'), url.searchParams.get('country') and assert the
pathname equals '/stations' and the method via expect.objectContaining({ method:
'GET' }) as before. Ensure you update the assertion around
`expect(fetchMock).toHaveBeenCalledWith(...)` to use these URL.searchParams
checks so the test no longer depends on param ordering.
packages/zenko/src/utils/__tests__/treaty-tree.test.ts (1)

37-45: Prefer snapshot assertion for emitted route-tree output

This output-verification test is a good candidate for toMatchSnapshot(...) so regressions in emitted structure are caught holistically, not only by a few toContain checks.

As per coding guidelines: "Use snapshot testing with expect(result).toMatchSnapshot() for output verification" and "Name snapshots descriptively when using toMatchSnapshot()"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zenko/src/utils/__tests__/treaty-tree.test.ts` around lines 37 - 45,
Replace the ad-hoc contains assertions in the "emitTreatyRouteTree emits quoted
:param keys" test with a snapshot assertion: call emitTreatyRouteTree(tree) and
use expect(emitted).toMatchSnapshot("emitTreatyRouteTree - quoted param keys")
so the entire emitted structure from buildTreatyRouteTree/emitTreatyRouteTree is
captured; update the test name if needed to remain descriptive and remove the
three expect(...).toContain(...) lines referencing "board: {", '":row": {', and
"getSquare,".
packages/zenko/src/__tests__/treaty-generator.test.ts (1)

39-50: Use snapshot verification for generated module output

This generated-source assertion set would be stronger as toMatchSnapshot("treaty-generator-tictactoe-output"), with a few focused toContain checks kept only for critical anchors if needed.

As per coding guidelines: "Use snapshot testing with expect(result).toMatchSnapshot() for output verification" and "Name snapshots descriptively when using toMatchSnapshot()"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zenko/src/__tests__/treaty-generator.test.ts` around lines 39 - 50,
Replace the many granular expect(...).toContain assertions with a snapshot
assertion to verify the generated module output: call
expect(output).toMatchSnapshot("treaty-generator-tictactoe-output") in the test
and optionally retain a couple of critical anchor checks (e.g., that output
contains "export const treatyRoutes =" or "createClient(") if you want focused
guarantees; update the test in treaty-generator.test.ts to remove the repetitive
toContain checks for "import { createTreatyClient, type TreatyClient }",
"getBoard", "getSquare", "putSquare", and the object fragments, and add the
descriptive snapshot name per guidelines.
packages/zenko/src/treaty-infer.ts (1)

12-20: Consider using unknown[] instead of any[] for type safety.

Per coding guidelines, prefer unknown over any where possible. The function signature (...args: any[]) => string could be (...args: unknown[]) => string.

Suggested change
 type AnyOperationDefinition = OperationDefinition<
   RequestMethod,
-  (...args: any[]) => string,
+  (...args: unknown[]) => string,
   unknown,
   unknown,
   AnyHeaderFn | undefined,
   OperationErrors | undefined,
   readonly SecurityRequirement[] | undefined
 >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zenko/src/treaty-infer.ts` around lines 12 - 20, Update the
AnyOperationDefinition type to use unknown[] for the varargs function signature:
replace the function type (...args: any[]) => string with (...args: unknown[])
=> string inside the OperationDefinition type alias (symbol:
AnyOperationDefinition) to improve type safety; ensure any downstream usage
expecting any[] is adjusted or narrowed via proper type assertions/casts where
necessary.
packages/zenko/src/core/operation-parser.ts (1)

326-343: Consider typing the schema parameter more precisely.

The schema parameter uses any type. While this matches the existing codebase patterns, consider using a more specific type or unknown for better type safety. This is a minor nit given the existing code style.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zenko/src/core/operation-parser.ts` around lines 326 - 343, The
schema parameter in buildErrorResponsesMap is typed as any; change it to a
stricter type (prefer unknown or a specific Schema type used elsewhere) to
improve type safety. Update the function signature for buildErrorResponsesMap to
use schema: unknown (or the chosen Schema interface) and adjust any downstream
usage: particularly the call to resolveResponseType so its parameter type
matches (or perform a safe type-assertion/validation before passing schema).
Make sure to update any related type declarations for resolveResponseType if
needed and keep identifiers intact (buildErrorResponsesMap, resolveResponseType,
mapStatusToIdentifier).
packages/zenko/src/treaty-generator.ts (1)

41-58: Verify error handling for missing input file.

The dynamic import on line 46 will throw if the file doesn't exist, but the error message may not be user-friendly. Consider whether wrapping this with a more descriptive error would improve the developer experience.

Suggested improvement for clearer error messages
 export async function generateTreatyModule(options: {
   inputFile: string
   importPath: string
 }): Promise<string> {
   const url = pathToFileURL(options.inputFile).href
-  const mod = await import(url)
+  let mod: Record<string, unknown>
+  try {
+    mod = await import(url)
+  } catch (err) {
+    throw new Error(
+      `Failed to import ${options.inputFile}: ${err instanceof Error ? err.message : String(err)}`
+    )
+  }
 
   if (!mod.operationMetadata) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zenko/src/treaty-generator.ts` around lines 41 - 58, The dynamic
import in generateTreatyModule (specifically the import(url) call using
options.inputFile) can throw an unfriendly error if the file is missing or
invalid; wrap the await import(url) in a try/catch, catch any error, and throw a
new, clearer Error that mentions the problematic options.inputFile and includes
the original error message/details (e.g., via error.message or by attaching the
original error) so callers see a descriptive failure when the input file cannot
be loaded.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/zenko/src/treaty.ts`:
- Around line 209-215: The fetch options spread currently allows init to
override the previously set method/body (in the fetchImpl call that uses url,
upper, headers, isGetOrHead, requestBody), so prevent accidental overrides by
ensuring init is applied safely: either spread init first before explicitly
setting method/body, or create a safeInit that strips method and body (e.g.,
destructure init to exclude method/body) and spread safeInit after your explicit
method/body/headers/body entries; update the fetchImpl invocation to use that
safe ordering or safeInit.

In `@packages/zenko/src/utils/treaty-tree.ts`:
- Around line 20-30: Docstring example in pathTemplateToSegments is wrong:
update the comment so parameter segments show colon prefixes (i.e., change
`/{a}/{b} → ["a", ":b"]` to `/{a}/{b} → [":a", ":b"]`) to match the function
behavior; locate the docstring above the pathTemplateToSegments function and
correct the example text accordingly.

---

Outside diff comments:
In `@packages/zenko/package.json`:
- Around line 27-34: The package's "exports" bun conditions (for "." ,
"./types", and "./treaty") point to source files (e.g., "./index.ts",
"./src/types.ts", "./src/treaty.ts") that are not included in the "files"
whitelist; update each bun condition to reference the published build artifacts
inside dist (for example point "." bun to the built ESM like "./dist/index.mjs",
"./types" bun to "./dist/src/types.mjs" or "./dist/types.mjs" as appropriate,
and "./treaty" bun to its corresponding "./dist/..." artifact) and ensure those
dist files are present in the package files list so consumers resolving the bun
condition will find the compiled outputs.

---

Nitpick comments:
In `@packages/examples/src/__tests__/train-travel-treaty-fetch.test.ts`:
- Around line 68-71: The test currently asserts the full URL string when
checking fetchMock calls
(`expect(fetchMock).toHaveBeenCalledWith(\`${origin}/stations?...`, which is
brittle due to query-string ordering); instead, extract the actual URL argument
from the fetchMock call (e.g., inspect fetchMock.mock.calls[0][0] or use
fetchMock.mock.calls to find the call) and create a new URL(...) from it, then
assert individual query params via url.searchParams.get('limit'),
url.searchParams.get('page'), url.searchParams.get('country') and assert the
pathname equals '/stations' and the method via expect.objectContaining({ method:
'GET' }) as before. Ensure you update the assertion around
`expect(fetchMock).toHaveBeenCalledWith(...)` to use these URL.searchParams
checks so the test no longer depends on param ordering.

In `@packages/zenko/src/__tests__/cli.test.ts`:
- Around line 113-116: Replace the brittle string-contains assertions that check
generated treaty module text (the expectations referencing "export const
treatyRoutes = {" and the import of createTreatyClient/type TreatyClient) with a
named snapshot assertion so the entire generated output is compared
structurally; locate the test that builds the treaty module output (the variable
currently asserted via expect(output).toContain(...)) and change those
assertions to a single expect(<that output
variable>).toMatchSnapshot('treaty-treatyModule-output') (also update the
analogous checks near the other occurrence mentioned) so formatting changes are
reviewed via snapshot diffs.

In `@packages/zenko/src/__tests__/tictactoe.test.ts`:
- Around line 109-114: Replace the brittle fragment assertions on the generated
metadata block with a snapshot assertion: remove the multiple
expect(result).toContain(...) checks that reference strings like "export const
operationMetadata = {", "getSquare: {", 'method: "get"', 'path:
"/board/{row}/{column}"', '"200":"mark"', and '"400":"errorMessage"' and instead
call expect(result).toMatchSnapshot() (optionally with a descriptive snapshot
name) to capture the full generated metadata output in one stable snapshot;
update the test around the result variable in the tictactoe test to use this
single snapshot assertion.

In `@packages/zenko/src/__tests__/treaty-generator.test.ts`:
- Around line 39-50: Replace the many granular expect(...).toContain assertions
with a snapshot assertion to verify the generated module output: call
expect(output).toMatchSnapshot("treaty-generator-tictactoe-output") in the test
and optionally retain a couple of critical anchor checks (e.g., that output
contains "export const treatyRoutes =" or "createClient(") if you want focused
guarantees; update the test in treaty-generator.test.ts to remove the repetitive
toContain checks for "import { createTreatyClient, type TreatyClient }",
"getBoard", "getSquare", "putSquare", and the object fragments, and add the
descriptive snapshot name per guidelines.

In `@packages/zenko/src/__tests__/treaty-runtime.test.ts`:
- Around line 48-70: The test titled "walks dynamic segments and sends JSON
bodies" is inconsistent because it asserts a raw string body ("X") from the call
via createTreatyClient -> client.board(...).put; update the test to either (A)
assert a JSON body by calling .put with an object payload and expect fetchMock
to receive body: JSON.stringify(yourObject) and Content-Type:
"application/json", or (B) rename the test to reflect that it sends raw string
bodies and keep the existing assertion; adjust references in the assertion to
use fetchMock and the client.board({ row: "1" })({ column: "2" }).put call
accordingly so the test name and the fetchMock expect.objectContaining check are
consistent.

In `@packages/zenko/src/core/operation-parser.ts`:
- Around line 326-343: The schema parameter in buildErrorResponsesMap is typed
as any; change it to a stricter type (prefer unknown or a specific Schema type
used elsewhere) to improve type safety. Update the function signature for
buildErrorResponsesMap to use schema: unknown (or the chosen Schema interface)
and adjust any downstream usage: particularly the call to resolveResponseType so
its parameter type matches (or perform a safe type-assertion/validation before
passing schema). Make sure to update any related type declarations for
resolveResponseType if needed and keep identifiers intact
(buildErrorResponsesMap, resolveResponseType, mapStatusToIdentifier).

In `@packages/zenko/src/treaty-generator.ts`:
- Around line 41-58: The dynamic import in generateTreatyModule (specifically
the import(url) call using options.inputFile) can throw an unfriendly error if
the file is missing or invalid; wrap the await import(url) in a try/catch, catch
any error, and throw a new, clearer Error that mentions the problematic
options.inputFile and includes the original error message/details (e.g., via
error.message or by attaching the original error) so callers see a descriptive
failure when the input file cannot be loaded.

In `@packages/zenko/src/treaty-infer.ts`:
- Around line 12-20: Update the AnyOperationDefinition type to use unknown[] for
the varargs function signature: replace the function type (...args: any[]) =>
string with (...args: unknown[]) => string inside the OperationDefinition type
alias (symbol: AnyOperationDefinition) to improve type safety; ensure any
downstream usage expecting any[] is adjusted or narrowed via proper type
assertions/casts where necessary.

In `@packages/zenko/src/utils/__tests__/treaty-tree.test.ts`:
- Around line 37-45: Replace the ad-hoc contains assertions in the
"emitTreatyRouteTree emits quoted :param keys" test with a snapshot assertion:
call emitTreatyRouteTree(tree) and use
expect(emitted).toMatchSnapshot("emitTreatyRouteTree - quoted param keys") so
the entire emitted structure from buildTreatyRouteTree/emitTreatyRouteTree is
captured; update the test name if needed to remain descriptive and remove the
three expect(...).toContain(...) lines referencing "board: {", '":row": {', and
"getSquare,".
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4f0fca64-c758-4400-b4d6-ef5c6628fe4b

📥 Commits

Reviewing files that changed from the base of the PR and between ee31d80 and c68ba7d.

⛔ Files ignored due to path filters (20)
  • bun.lock is excluded by !**/*.lock
  • packages/zenko/src/__tests__/__snapshots__/additional-properties.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/anyof-combinations.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/cli.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/complex-composition.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/date-enum.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/form-data.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/inline-response-array.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/mixed-headers.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/no-response-content.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/non-json-responses.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/oneof-discriminator.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/petstore.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/property-metadata.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/security-schemes.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/selective-operations.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/tictactoe.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/train-travel.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/webhook.test.ts.snap is excluded by !**/*.snap
  • packages/zenko/src/__tests__/__snapshots__/zenko.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (24)
  • docs/plans/2026-03-29-zenko-treaty-client.md
  • packages/examples/generate.js
  • packages/examples/src/__tests__/train-travel-treaty-fetch.test.ts
  • packages/examples/tsconfig.json
  • packages/zenko/index.ts
  • packages/zenko/package.json
  • packages/zenko/src/__tests__/cli.test.ts
  • packages/zenko/src/__tests__/tictactoe.test.ts
  • packages/zenko/src/__tests__/treaty-client-types.test.ts
  • packages/zenko/src/__tests__/treaty-generator.test.ts
  • packages/zenko/src/__tests__/treaty-runtime.test.ts
  • packages/zenko/src/cli.ts
  • packages/zenko/src/core/__tests__/operation-parser.test.ts
  • packages/zenko/src/core/operation-parser.ts
  • packages/zenko/src/treaty-generator.ts
  • packages/zenko/src/treaty-infer.ts
  • packages/zenko/src/treaty-types.ts
  • packages/zenko/src/treaty.ts
  • packages/zenko/src/types/operation.ts
  • packages/zenko/src/utils/__tests__/treaty-tree.test.ts
  • packages/zenko/src/utils/treaty-tree.ts
  • packages/zenko/src/zenko.ts
  • packages/zenko/tsdown.config.ts
  • packages/zenko/zenko-config.schema.json

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/zenko/src/utils/treaty-tree.ts (2)

83-87: Line exceeds 80-character limit.

The error message template string on line 85 exceeds the 80-character line width guideline when fully indented.

♻️ Suggested refactor
       if (bucket[method] !== undefined) {
+        const existing = JSON.stringify(bucket[method])
         throw new Error(
-          `Duplicate ${method} on ${segment} for ${operationExport} vs ${JSON.stringify(bucket[method])}`
+          `Duplicate ${method} on ${segment} ` +
+            `for ${operationExport} vs ${existing}`
         )
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zenko/src/utils/treaty-tree.ts` around lines 83 - 87, The error
string in the duplicate-check (bucket[method] !== undefined) is longer than 80
chars; split the message across concatenated strings or build it in parts so
each source line stays under 80 chars while preserving content and using
JSON.stringify(bucket[method]) and the same variables (method, segment,
operationExport, bucket[method]); update the throw in the function handling the
duplicate check in treaty-tree.ts to construct the message in shorter chunks
(e.g., separate descriptive part and the JSON detail) so indentation + line
length remain within the limit.

40-57: Consider Result-style returns for error handling.

This function throws exceptions for invalid HTTP methods. As per coding guidelines for packages/zenko/src/**: "Prefer Result-style returns for recoverable errors in library code rather than throwing exceptions."

Malformed metadata (unsupported HTTP method) could be considered a recoverable error that callers might want to handle gracefully.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zenko/src/utils/treaty-tree.ts` around lines 40 - 57,
buildTreatyRouteTree currently throws on unsupported HTTP methods; change it to
return a Result-style value instead of throwing so callers can handle
recoverable metadata errors. Update the function signature (e.g., to return
Result<TreatyRouteTree, Error>) and inside, when you detect an invalid method
via HTTP_METHODS and meta.method, return a failure Result containing a clear
error message referencing operationExport and meta.method rather than throwing;
on success return an Ok Result with the built TreatyRouteTree. Keep the
insertOperation usage and other logic the same, and update any callers of
buildTreatyRouteTree to handle the Result (check for Ok/Err) instead of relying
on exceptions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/zenko/src/utils/treaty-tree.ts`:
- Around line 83-87: The error string in the duplicate-check (bucket[method] !==
undefined) is longer than 80 chars; split the message across concatenated
strings or build it in parts so each source line stays under 80 chars while
preserving content and using JSON.stringify(bucket[method]) and the same
variables (method, segment, operationExport, bucket[method]); update the throw
in the function handling the duplicate check in treaty-tree.ts to construct the
message in shorter chunks (e.g., separate descriptive part and the JSON detail)
so indentation + line length remain within the limit.
- Around line 40-57: buildTreatyRouteTree currently throws on unsupported HTTP
methods; change it to return a Result-style value instead of throwing so callers
can handle recoverable metadata errors. Update the function signature (e.g., to
return Result<TreatyRouteTree, Error>) and inside, when you detect an invalid
method via HTTP_METHODS and meta.method, return a failure Result containing a
clear error message referencing operationExport and meta.method rather than
throwing; on success return an Ok Result with the built TreatyRouteTree. Keep
the insertOperation usage and other logic the same, and update any callers of
buildTreatyRouteTree to handle the Result (check for Ok/Err) instead of relying
on exceptions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 35c415f8-56da-4dc6-8a24-aa425b161f60

📥 Commits

Reviewing files that changed from the base of the PR and between c68ba7d and db4eae5.

📒 Files selected for processing (4)
  • packages/zenko/package.json
  • packages/zenko/src/__tests__/treaty-runtime.test.ts
  • packages/zenko/src/treaty.ts
  • packages/zenko/src/utils/treaty-tree.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/zenko/src/tests/treaty-runtime.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/zenko/package.json
  • packages/zenko/src/treaty.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.

1 participant