Feat/schema support - #25
Conversation
…, and --doccurl-field-descriptions flags Recognize three new doccurl-only flags embedded in the curl command and strip them before the command is passed to curl. Values are JSON objects (JSON Schema 2020-12 for the schema flags, plain object map for descriptions). Schemas are size-limited (16 KB each) and single-line JSON only; file references and duplicates are rejected. The flags are exposed on the parsed spec as `requestSchema`, `responseSchema`, and `fieldDescriptions` (null when absent). The parser also returns these fields on the legacy payload path so the response shape is consistent across both entry points. Co-Authored-By: Sonnet 4.6 <noreply@puku.sh>
…run-curl response The success response now includes a `schemas` object containing the attached request schema, response schema, and field descriptions map (each null when unset). A `responseFields` summary is also computed for JSON content types: top-level field names with their JSON Schema type tag (string, number, boolean, null, object, array<T>) plus a hasChildren flag indicating nested structure. responseFields is null for non-JSON or empty/oversized bodies. This pairs with the parser changes: the curl invocation still never sees the --doccurl-* flags, only the schemas ride on the response back to the playground. Co-Authored-By: Sonnet 4.6 <noreply@puku.sh>
A new "Schema" button on the response panel opens a modal that documents the attached request and response schemas as a five-column table (Field, Type, Presence, Constraints, Description). The Response tab also overlays a live diff against the actual response fields captured during the most recent run, marking missing fields, type mismatches, and extras (when additionalProperties is false). The modal is built once per playground and reused. Schema button is hidden when no schemas are attached, so the existing UI is unchanged for old curl blocks. Block ID stability is preserved by stripping --doccurl-*-schema flags before hashing, so toggling schema presence on a curl block does not wipe localStorage edits. Co-Authored-By: Sonnet 4.6 <noreply@puku.sh>
Parse --doccurl-request-schema, --doccurl-response-schema, and --doccurl-field-descriptions inside parseCurlForExport so every exporter sees the documentation attached to each curl block. Postman items now embed the raw JSON body with a json language hint and carry the schemas plus field descriptions in request.description. Insomnia requests get the same description plus a meta object so consumers can round-trip the schemas. Co-Authored-By: Sonnet 4.6 <noreply@puku.sh>
Walk every curl block in the export model and assemble a single OpenAPI 3.1 document. Paths are auto-derived from the URL (after protocol + host), methods from -X/--request, request bodies from --doccurl-request-schema, responses from --doccurl-response-schema, and per-field descriptions from --doccurl-field-descriptions merged into the schema as `description` keywords. Numeric and UUID segments are converted to path templates (var1, var2, ...). Schemas are deduplicated by content hash into components.schemas. Adds an "OpenAPI 3.1" option to the export modal. Co-Authored-By: Sonnet 4.6 <noreply@puku.sh>
Add docs/schemas.md covering the request schema, response schema, and field descriptions flags, the live diff semantics, the supported JSON Schema 2020-12 vocabulary, and the OpenAPI 3.1 exporter. Reference the new page from playground.md alongside the updated export options list. Co-Authored-By: Sonnet 4.6 <noreply@puku.sh>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Reviewer's GuideAdds DocCurl schema awareness across engine, playground UI, and export formats: curl commands can now carry inline JSON request/response schemas and field descriptions, which are validated, surfaced via a new Schema modal with live response diffs, and exported to Insomnia, Postman, and a new OpenAPI 3.1 export option. Sequence diagram for opening the Schema modal and computing live response diffssequenceDiagram
actor User
participant Playground as playground.createPlaygroundSystem
participant SchemaSystem as frontend.createSchemaSystem
participant SchemaHelpers as schema.__test__
User ->> Playground: click schemaBtn
Playground ->> SchemaSystem: open({requestSchema,responseSchema,fieldDescriptions,responseFields,responseLabel})
SchemaSystem ->> SchemaHelpers: buildRows(requestSchema,fieldDescriptions)
SchemaSystem ->> SchemaHelpers: computeResponseDiff(responseSchema,responseFields)
SchemaSystem ->> User: render schemaModal with request/response tabs and diff
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Warning Review limit reached
Next review available in: 46 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughDocCurl schema flags are parsed, validated, removed before execution, returned with inferred response fields, displayed in a new playground modal, and propagated into Postman, Insomnia, and OpenAPI 3.1 exports. Documentation and tests cover parsing, rendering, diffs, and export behavior. ChangesDocCurl schema feature
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ 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 |
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- The parsing and validation logic for the doccurl schema flags is duplicated between the engine (curl/parse.js, curl/validate.js) and frontend export (export/curl.js, shared.js); consider centralizing this into a shared helper to keep behavior and error messages aligned over time.
- stripDocCurlSchemaFlags currently skips the token following each schema flag as its value; this will also skip a following non-flag token if the value is omitted, so it may be worth tightening the value detection or short‑circuiting on missing values to avoid unintentionally dropping arguments.
- MAX_OPENAPI_SCHEMA_BYTES in the OpenAPI formatter is defined but not enforced anywhere; either hook it into schema size checks or remove it to avoid confusion about unused limits.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The parsing and validation logic for the doccurl schema flags is duplicated between the engine (curl/parse.js, curl/validate.js) and frontend export (export/curl.js, shared.js); consider centralizing this into a shared helper to keep behavior and error messages aligned over time.
- stripDocCurlSchemaFlags currently skips the token following each schema flag as its value; this will also skip a following non-flag token if the value is omitted, so it may be worth tightening the value detection or short‑circuiting on missing values to avoid unintentionally dropping arguments.
- MAX_OPENAPI_SCHEMA_BYTES in the OpenAPI formatter is defined but not enforced anywhere; either hook it into schema size checks or remove it to avoid confusion about unused limits.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Add a "Documenting Request/Response Shapes" section to the playground feature list, mention the Schema button and the OpenAPI 3.1 export option, link to docs/schemas.md, and add JSON Schema + field descriptions + OpenAPI export rows to the supported-features table. Add schemas.md to STARTER_DOC_FILES so new projects created via `doccurl init` ship with the schemas guide alongside overview.md, playground.md, and self-test-api.md. Co-Authored-By: Sonnet 4.6 <noreply@puku.sh>
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (2)
frontend/modules/schema.js (2)
530-554: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winModal lacks focus management.
On
open()focus is not moved into the dialog and there is no focus trap, so keyboard users remain on background content behind thearia-modaloverlay. Moving initial focus to the close button (and optionally restoring focus on close) is a low-cost a11y improvement.🤖 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 `@frontend/modules/schema.js` around lines 530 - 554, Update open() to move keyboard focus into the modal after displaying it, targeting the existing close button or equivalent dialog control, and ensure the target is focusable. Preserve the current schema initialization and tab-selection behavior; optionally store and restore the previously focused element when the modal closes.
383-389: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winPass
documentRefinto the DOM builders.
createSchemaSystemonly usesdocumentRefwhen appending the modal and validating it exists.buildTable,buildDiffTable, andcreateElementstill call the module-globaldocument, so injected documents end up with modal/table nodes created on the wrong document. PassdocumentRefthrough these helpers for consistency.🤖 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 `@frontend/modules/schema.js` around lines 383 - 389, Update createSchemaSystem and the DOM builder helpers buildTable, buildDiffTable, and createElement to accept and propagate documentRef, replacing module-global document usage with the injected reference so all modal and table nodes are created in the same document.
🤖 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 `@docs/playground.md`:
- Line 49: Update the Export Curls documentation to replace the nonexistent
Hoppscotch format with Markdown, matching the formats supported by the export
implementation.
In `@docs/schemas.md`:
- Line 95: Update the export-format statement in the schemas documentation to
distinguish the formats accurately: both Postman and Insomnia include schemas in
request.description, but only Insomnia exports include the meta object. Do not
imply that Postman items carry meta.
In `@engine/curl/validate.js`:
- Around line 27-31: Update the schema size check in the validation flow to
measure the serialized JSON using Buffer.byteLength rather than
serialized.length, matching the existing body-size check and ensuring the
reported byte count and limit comparison reflect actual UTF-8 bytes.
In `@frontend/modules/export/curl.js`:
- Around line 15-41: Enforce the documented 16 KB schema limit by adding a UTF-8
byte-length check on the JSON-stringified parsed object in parseSchemaFlagValue,
and apply the same MAX_OPENAPI_SCHEMA_BYTES check in
frontend/modules/export/formatters/openapi.js:16. Preserve the existing
validation errors and reject oversized schemas before export;
frontend/modules/export/curl.js:15-41 and
frontend/modules/export/formatters/openapi.js:16 require implementation changes,
while docs/schemas.md:24 requires no direct change because the documented limit
will be enforced.
In `@frontend/modules/export/formatters/openapi.js`:
- Around line 319-331: Update mergeOperations so requestBody and responses from
both colliding operations are merged instead of being silently replaced by b’s
values. Preserve the existing precedence/shape expected by OpenAPI and retain
the current summary, description, and parameters merging behavior; ensure data
from a is not discarded when b omits or defines different schema fields.
- Around line 134-154: Update buildSchemaRef and buildResponseSchemaRef to use a
shared resolveComponentName helper that compares the existing component content
with the current schema key, rather than checking only refName presence. On a
hash collision with different content, append an incrementing numeric suffix
until finding a free or matching component, then store the schema under that
resolved name and return its reference.
In `@frontend/modules/export/formatters/shared.js`:
- Around line 70-78: The field-description formatters incorrectly retain entries
with empty descriptions because they filter the rendered string after adding
trailing whitespace. In frontend/modules/export/formatters/shared.js:70-78,
update buildFieldDescriptionText to trim and validate the description value
before constructing each entry; in
frontend/modules/export/formatters/openapi.js:265-296, apply the same validation
in buildOperation or reuse buildFieldDescriptionText, ensuring empty
descriptions are omitted in both implementations.
---
Nitpick comments:
In `@frontend/modules/schema.js`:
- Around line 530-554: Update open() to move keyboard focus into the modal after
displaying it, targeting the existing close button or equivalent dialog control,
and ensure the target is focusable. Preserve the current schema initialization
and tab-selection behavior; optionally store and restore the previously focused
element when the modal closes.
- Around line 383-389: Update createSchemaSystem and the DOM builder helpers
buildTable, buildDiffTable, and createElement to accept and propagate
documentRef, replacing module-global document usage with the injected reference
so all modal and table nodes are created in the same document.
🪄 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 Plus
Run ID: 9544c69e-ade8-460a-a25d-57723dd680a8
📒 Files selected for processing (19)
docs/playground.mddocs/schemas.mdengine/curl/constants.jsengine/curl/parse.jsengine/curl/route.jsengine/curl/validate.jsengine/index.jsfrontend/modules/export/curl.jsfrontend/modules/export/formatters/insomnia.jsfrontend/modules/export/formatters/openapi.jsfrontend/modules/export/formatters/postman.jsfrontend/modules/export/formatters/shared.jsfrontend/modules/export/index.jsfrontend/modules/playground.jsfrontend/modules/schema.jsfrontend/style.csstest/engine/curl-runner.test.jstest/frontend/export.test.jstest/frontend/playground.test.js
| - Use `Copy` to copy current env exports plus the exact curl block you are editing. | ||
| - Use `Upload Files` for non-generated multipart file fields; generated `@R&{...}` fields still work without browser uploads. | ||
| - Use `Export Curls` to export every markdown file’s curl examples as Insomnia, Postman, or Hoppscotch JSON. | ||
| - Use `Export Curls` to export every markdown file’s curl examples as Insomnia, OpenAPI 3.1, Postman, or Hoppscotch JSON. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
"Hoppscotch" isn't a real export format.
No code in this PR (or the prior EXPORT_OPTIONS per the summary) defines a Hoppscotch exporter — the actual formats are Insomnia, OpenAPI 3.1, Postman, and Markdown (frontend/modules/export/index.js). This line advertises a feature that doesn't exist.
✏️ Suggested fix
-- Use `Export Curls` to export every markdown file’s curl examples as Insomnia, OpenAPI 3.1, Postman, or Hoppscotch JSON.
+- Use `Export Curls` to export every markdown file’s curl examples as Insomnia, OpenAPI 3.1, Postman, or Markdown.📝 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.
| - Use `Export Curls` to export every markdown file’s curl examples as Insomnia, OpenAPI 3.1, Postman, or Hoppscotch JSON. | |
| - Use `Export Curls` to export every markdown file’s curl examples as Insomnia, OpenAPI 3.1, Postman, or Markdown. |
🧰 Tools
🪛 LanguageTool
[style] ~49-~49: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...s still work without browser uploads. - Use Export Curls to export every markdown...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
🤖 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 `@docs/playground.md` at line 49, Update the Export Curls documentation to
replace the nonexistent Hoppscotch format with Markdown, matching the formats
supported by the export implementation.
| - Each response schema is added the same way and attached to the `200` response. | ||
| - Field descriptions are merged into the schemas as `description` keywords. | ||
|
|
||
| Postman and Insomnia exports also surface the schemas inside `request.description` plus a `meta` object so consumers can round-trip the documentation. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Overstates Postman's export fidelity.
Only Insomnia exports get a meta object (frontend/modules/export/formatters/insomnia.js); Postman items (frontend/modules/export/formatters/postman.js) only get request.description, no meta. As written, this sentence implies both formats carry both.
✏️ Suggested wording
-Postman and Insomnia exports also surface the schemas inside `request.description` plus a `meta` object so consumers can round-trip the documentation.
+Postman exports surface the schemas inside `request.description`. Insomnia exports include both `request.description` and a `meta` object so consumers can round-trip the documentation.📝 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.
| Postman and Insomnia exports also surface the schemas inside `request.description` plus a `meta` object so consumers can round-trip the documentation. | |
| Postman exports surface the schemas inside `request.description`. Insomnia exports include both `request.description` and a `meta` object so consumers can round-trip the documentation. |
🤖 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 `@docs/schemas.md` at line 95, Update the export-format statement in the
schemas documentation to distinguish the formats accurately: both Postman and
Insomnia include schemas in request.description, but only Insomnia exports
include the meta object. Do not imply that Postman items carry meta.
| const limit = limits[maxField] || limits.maxSchemaBytes; | ||
| const serialized = JSON.stringify(schema); | ||
| if (serialized.length > limit) { | ||
| throw new Error(`${name} exceeds ${limit} bytes (${serialized.length}).`); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Byte limit is measured in UTF-16 characters, not bytes.
serialized.length counts UTF-16 code units, so multibyte content (non-ASCII descriptions/enums) undercounts the true byte size while the error still reports "bytes". This is inconsistent with the body check on Line 80, which uses Buffer.byteLength(...).
🔧 Align with Buffer.byteLength
- const limit = limits[maxField] || limits.maxSchemaBytes;
- const serialized = JSON.stringify(schema);
- if (serialized.length > limit) {
- throw new Error(`${name} exceeds ${limit} bytes (${serialized.length}).`);
- }
+ const limit = limits[maxField] || limits.maxSchemaBytes;
+ const serializedBytes = Buffer.byteLength(JSON.stringify(schema), "utf8");
+ if (serializedBytes > limit) {
+ throw new Error(`${name} exceeds ${limit} bytes (${serializedBytes}).`);
+ }📝 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.
| const limit = limits[maxField] || limits.maxSchemaBytes; | |
| const serialized = JSON.stringify(schema); | |
| if (serialized.length > limit) { | |
| throw new Error(`${name} exceeds ${limit} bytes (${serialized.length}).`); | |
| } | |
| const limit = limits[maxField] || limits.maxSchemaBytes; | |
| const serializedBytes = Buffer.byteLength(JSON.stringify(schema), "utf8"); | |
| if (serializedBytes > limit) { | |
| throw new Error(`${name} exceeds ${limit} bytes (${serializedBytes}).`); | |
| } |
🤖 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 `@engine/curl/validate.js` around lines 27 - 31, Update the schema size check
in the validation flow to measure the serialized JSON using Buffer.byteLength
rather than serialized.length, matching the existing body-size check and
ensuring the reported byte count and limit comparison reflect actual UTF-8
bytes.
| function parseSchemaFlagValue(flag, rawValue) { | ||
| if (typeof rawValue !== "string") { | ||
| throw new Error(`Missing value for ${flag}`); | ||
| } | ||
| const trimmed = rawValue.trim(); | ||
| if (trimmed.length === 0) { | ||
| throw new Error(`Empty value for ${flag}`); | ||
| } | ||
| if (trimmed.startsWith("@")) { | ||
| throw new Error( | ||
| `File references are not supported for ${flag}; paste the JSON inline.`, | ||
| ); | ||
| } | ||
| if (/[\r\n]/.test(trimmed)) { | ||
| throw new Error(`${flag} must be a single-line JSON value.`); | ||
| } | ||
| let parsed; | ||
| try { | ||
| parsed = JSON.parse(trimmed); | ||
| } catch (error) { | ||
| throw new Error(`Invalid JSON in ${flag}: ${error.message}`); | ||
| } | ||
| if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) { | ||
| throw new Error(`${flag} must be a JSON object.`); | ||
| } | ||
| return parsed; | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check whether the backend engine enforces schema size limits independently
# of the frontend export parser, and whether MAX_OPENAPI_SCHEMA_BYTES is used anywhere.
rg -n "16 \* 1024|MAX_.*SCHEMA.*BYTES|byteLength" engine/curl/validate.js engine/curl/parse.js frontend/modules/export/formatters/openapi.js frontend/modules/export/curl.jsRepository: b-Istiak-s/DocCurl
Length of output: 418
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== file sizes =="
wc -l frontend/modules/export/curl.js frontend/modules/export/formatters/openapi.js docs/schemas.md engine/curl/validate.js engine/curl/parse.js || true
echo
echo "== frontend curl outline =="
ast-grep outline frontend/modules/export/curl.js --view expanded || true
echo
echo "== openapi outline relevant =="
ast-grep outline frontend/modules/export/formatters/openapi.js --view expanded || true
echo
echo "== docs schemas relevant =="
sed -n '1,80p' docs/schemas.md
echo
echo "== frontend curl relevant parse/build sections =="
rg -n "parseSchemaFlagValue|parse|buildSchemaRef|buildRequestBody|MAX_OPENAPI_SCHEMA_BYTES|schema" frontend/modules/export/curl.js frontend/modules/export/formatters/openapi.js -C 3
echo
echo "== engine curl validate relevant sections =="
sed -n '1,140p' engine/curl/validate.js
echo
echo "== backend schema size enforcement precise search =="
rg -n "schema.*(byte|size|limit)|size.*(schema|16)|16 *1024|16 *1024|byteLength" engine -S
echo
echo "== all MAX_OPENAPI_SCHEMA_BYTES references =="
rg -n "MAX_OPENAPI_SCHEMA_BYTES|16 \\* 1024" .Repository: b-Istiak-s/DocCurl
Length of output: 30824
Enforce the documented schema-size limit in the export parser. docs/schemas.md says “16 KB per schema/sidecar (enforced at parse time),” but frontend/modules/export/curl.js#L15-L41 only checks single-line JSON shape, and frontend/modules/export/formatters/openapi.js#L16 never uses MAX_OPENAPI_SCHEMA_BYTES. Add the same JSON-stringified byte check there, or narrow the docs to state the limit applies only to the runtime/backend path.
📍 Affects 3 files
frontend/modules/export/curl.js#L15-L41(this comment)frontend/modules/export/formatters/openapi.js#L16-L16docs/schemas.md#L24-L24
🤖 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 `@frontend/modules/export/curl.js` around lines 15 - 41, Enforce the documented
16 KB schema limit by adding a UTF-8 byte-length check on the JSON-stringified
parsed object in parseSchemaFlagValue, and apply the same
MAX_OPENAPI_SCHEMA_BYTES check in
frontend/modules/export/formatters/openapi.js:16. Preserve the existing
validation errors and reject oversized schemas before export;
frontend/modules/export/curl.js:15-41 and
frontend/modules/export/formatters/openapi.js:16 require implementation changes,
while docs/schemas.md:24 requires no direct change because the documented limit
will be enforced.
| function buildSchemaRef(request, components) { | ||
| const schema = withFieldDescriptions(request.requestSchema, request.fieldDescriptions); | ||
| if (!schema) return null; | ||
| const key = safeStringify(schema); | ||
| const refName = `Schema_${shortHash(key)}`; | ||
| if (!components[refName]) { | ||
| components[refName] = schema; | ||
| } | ||
| return { $ref: `#/components/schemas/${refName}` }; | ||
| } | ||
|
|
||
| function buildResponseSchemaRef(request, components) { | ||
| if (!request.responseSchema) return null; | ||
| const schema = withFieldDescriptions(request.responseSchema, request.fieldDescriptions); | ||
| const key = safeStringify(schema); | ||
| const refName = `Schema_${shortHash(key)}`; | ||
| if (!components[refName]) { | ||
| components[refName] = schema; | ||
| } | ||
| return { $ref: `#/components/schemas/${refName}` }; | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Hash-collision risk in schema dedup can silently attach the wrong schema.
shortHash is a 32-bit djb2-style hash truncated to base36. if (!components[refName]) only checks presence, not content — if two different schemas hash-collide, the second schema is dropped and its $ref ends up pointing at the first schema's (wrong) content. Rare, but the resulting exported OpenAPI doc would be silently incorrect.
🛡️ Suggested guard
function buildSchemaRef(request, components) {
const schema = withFieldDescriptions(request.requestSchema, request.fieldDescriptions);
if (!schema) return null;
const key = safeStringify(schema);
- const refName = `Schema_${shortHash(key)}`;
- if (!components[refName]) {
- components[refName] = schema;
- }
- return { $ref: `#/components/schemas/${refName}` };
+ const refName = resolveComponentName(components, key, schema);
+ return { $ref: `#/components/schemas/${refName}` };
}Add a shared resolveComponentName(components, key, schema) that, on a name collision with different stored content, appends a numeric suffix until it finds a free/matching slot — same helper can be reused by buildResponseSchemaRef, addressing the duplication too.
🤖 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 `@frontend/modules/export/formatters/openapi.js` around lines 134 - 154, Update
buildSchemaRef and buildResponseSchemaRef to use a shared resolveComponentName
helper that compares the existing component content with the current schema key,
rather than checking only refName presence. On a hash collision with different
content, append an incrementing numeric suffix until finding a free or matching
component, then store the schema under that resolved name and return its
reference.
| function mergeOperations(a, b) { | ||
| const merged = { ...a, ...b }; | ||
| if (a.summary || b.summary) { | ||
| merged.summary = [a.summary, b.summary].filter(Boolean).join(" / "); | ||
| } | ||
| if (a.description || b.description) { | ||
| merged.description = [a.description, b.description].filter(Boolean).join("\n\n---\n\n"); | ||
| } | ||
| if (a.parameters || b.parameters) { | ||
| merged.parameters = [...(a.parameters || []), ...(b.parameters || [])]; | ||
| } | ||
| return merged; | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
mergeOperations silently drops the first block's requestBody/responses on path+method collisions.
{ ...a, ...b } gives b unconditional priority for every key it defines. summary, description, and parameters are explicitly re-merged afterward, but requestBody and responses are not — so when two curl blocks share the same derived path+method (e.g. one with a request schema, one without, or two with different response schemas), a's request/response data is silently discarded from the exported spec. The existing merge test (export.test.js lines 752-761) only exercises two schema-less requests, so this isn't caught.
🐛 Proposed fix
function mergeOperations(a, b) {
const merged = { ...a, ...b };
if (a.summary || b.summary) {
merged.summary = [a.summary, b.summary].filter(Boolean).join(" / ");
}
if (a.description || b.description) {
merged.description = [a.description, b.description].filter(Boolean).join("\n\n---\n\n");
}
if (a.parameters || b.parameters) {
merged.parameters = [...(a.parameters || []), ...(b.parameters || [])];
}
+ if (a.requestBody || b.requestBody) {
+ merged.requestBody = {
+ content: { ...(a.requestBody?.content || {}), ...(b.requestBody?.content || {}) },
+ };
+ }
+ if (a.responses || b.responses) {
+ merged.responses = { ...(a.responses || {}), ...(b.responses || {}) };
+ }
return merged;
}📝 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.
| function mergeOperations(a, b) { | |
| const merged = { ...a, ...b }; | |
| if (a.summary || b.summary) { | |
| merged.summary = [a.summary, b.summary].filter(Boolean).join(" / "); | |
| } | |
| if (a.description || b.description) { | |
| merged.description = [a.description, b.description].filter(Boolean).join("\n\n---\n\n"); | |
| } | |
| if (a.parameters || b.parameters) { | |
| merged.parameters = [...(a.parameters || []), ...(b.parameters || [])]; | |
| } | |
| return merged; | |
| } | |
| function mergeOperations(a, b) { | |
| const merged = { ...a, ...b }; | |
| if (a.summary || b.summary) { | |
| merged.summary = [a.summary, b.summary].filter(Boolean).join(" / "); | |
| } | |
| if (a.description || b.description) { | |
| merged.description = [a.description, b.description].filter(Boolean).join("\n\n---\n\n"); | |
| } | |
| if (a.parameters || b.parameters) { | |
| merged.parameters = [...(a.parameters || []), ...(b.parameters || [])]; | |
| } | |
| if (a.requestBody || b.requestBody) { | |
| merged.requestBody = { | |
| content: { ...(a.requestBody?.content || {}), ...(b.requestBody?.content || {}) }, | |
| }; | |
| } | |
| if (a.responses || b.responses) { | |
| merged.responses = { ...(a.responses || {}), ...(b.responses || {}) }; | |
| } | |
| return merged; | |
| } |
🤖 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 `@frontend/modules/export/formatters/openapi.js` around lines 319 - 331, Update
mergeOperations so requestBody and responses from both colliding operations are
merged instead of being silently replaced by b’s values. Preserve the existing
precedence/shape expected by OpenAPI and retain the current summary,
description, and parameters merging behavior; ensure data from a is not
discarded when b omits or defines different schema fields.
| export function buildFieldDescriptionText(request) { | ||
| if (!request || !request.fieldDescriptions) return ""; | ||
| const entries = Object.entries(request.fieldDescriptions); | ||
| if (entries.length === 0) return ""; | ||
| return entries | ||
| .map(([name, text]) => `- \`${name}\` — ${String(text ?? "").trim()}`) | ||
| .filter((entry) => !entry.endsWith("—")) | ||
| .join("\n"); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
**Empty field descriptions render as dangling "- \field` — "lines instead of being dropped.** Both implementations use ``- `${name}` — ${String(text ?? "").trim()}`` followed by.filter((entry) => !entry.endsWith("—")). The template always inserts a space before the interpolated text, so an empty description leaves the string ending in "— "(space), never"—"` — the filter's intended condition can never trigger for the case it's meant to catch.
frontend/modules/export/formatters/shared.js#L70-L78: fixbuildFieldDescriptionTextby checking the trimmed value directly instead of relying onendsWith("—")(see diff below); this also fixes Postman's and Insomnia's descriptions, which both call this helper.frontend/modules/export/formatters/openapi.js#L265-L296: apply the same fix to the inline duplicate insidebuildOperation's description-building (lines 277-280), or better, import and reusebuildFieldDescriptionTextfromshared.jsinstead of duplicating the logic.
🐛 Proposed fix (apply to both sites)
- .map(([name, text]) => `- \`${name}\` — ${String(text ?? "").trim()}`)
- .filter((entry) => !entry.endsWith("—"))
+ .map(([name, text]) => {
+ const value = String(text ?? "").trim();
+ return value ? `- \`${name}\` — ${value}` : null;
+ })
+ .filter(Boolean)📝 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.
| export function buildFieldDescriptionText(request) { | |
| if (!request || !request.fieldDescriptions) return ""; | |
| const entries = Object.entries(request.fieldDescriptions); | |
| if (entries.length === 0) return ""; | |
| return entries | |
| .map(([name, text]) => `- \`${name}\` — ${String(text ?? "").trim()}`) | |
| .filter((entry) => !entry.endsWith("—")) | |
| .join("\n"); | |
| } | |
| export function buildFieldDescriptionText(request) { | |
| if (!request || !request.fieldDescriptions) return ""; | |
| const entries = Object.entries(request.fieldDescriptions); | |
| if (entries.length === 0) return ""; | |
| return entries | |
| .map(([name, text]) => { | |
| const value = String(text ?? "").trim(); | |
| return value ? `- \`${name}\` — ${value}` : null; | |
| }) | |
| .filter(Boolean) | |
| .join("\n"); | |
| } |
📍 Affects 2 files
frontend/modules/export/formatters/shared.js#L70-L78(this comment)frontend/modules/export/formatters/openapi.js#L265-L296
🤖 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 `@frontend/modules/export/formatters/shared.js` around lines 70 - 78, The
field-description formatters incorrectly retain entries with empty descriptions
because they filter the rendered string after adding trailing whitespace. In
frontend/modules/export/formatters/shared.js:70-78, update
buildFieldDescriptionText to trim and validate the description value before
constructing each entry; in
frontend/modules/export/formatters/openapi.js:265-296, apply the same validation
in buildOperation or reuse buildFieldDescriptionText, ensuring empty
descriptions are omitted in both implementations.
Summary by Sourcery
Add first-class schema support across curl parsing, validation, playground UI, and export formats, including OpenAPI 3.1 generation and response/schema diffing.
New Features:
Enhancements:
Tests:
Summary by CodeRabbit
New Features
--doccurl-*flags.Documentation