Skip to content

Feat/schema support - #25

Open
b-Istiak-s wants to merge 7 commits into
mainfrom
feat/schema-support
Open

Feat/schema support#25
b-Istiak-s wants to merge 7 commits into
mainfrom
feat/schema-support

Conversation

@b-Istiak-s

@b-Istiak-s b-Istiak-s commented Jul 24, 2026

Copy link
Copy Markdown
Owner

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:

  • Support inline JSON Schema flags on curl commands for request, response, and field descriptions, propagating them through the backend and frontend.
  • Expose a Schema modal in the playground that renders request/response schemas and shows a live diff against the latest JSON response.
  • Add OpenAPI 3.1 export that builds a single spec with paths, servers, component schemas, and merged field descriptions from curl blocks.

Enhancements:

  • Ensure schema flags are stripped from executed curl invocations while remaining available in API responses and exports.
  • Keep playground curl block IDs stable when schema flags change so stored edits are preserved.
  • Include schema metadata and human-readable field descriptions in Postman and Insomnia exports for better downstream documentation.
  • Summarize JSON response fields on /api/run-curl to drive schema/response interoperability in the playground.

Tests:

  • Extend backend, playground, and export tests to cover schema flag parsing, validation limits, schema-aware exports, response field summarization, and the new Schema UI.
  • Update export UI tests to account for the new OpenAPI 3.1 export option.

Summary by CodeRabbit

  • New Features

    • Attach request and response JSON Schemas, plus field descriptions, to curl commands using --doccurl-* flags.
    • View schemas in a new Playground Schema modal with Request/Response tabs and live response diffs.
    • Export collections as OpenAPI 3.1 documents.
    • Include schema and field documentation in Postman and Insomnia exports.
    • Automatically summarize observed JSON response fields.
  • Documentation

    • Added comprehensive schema attachment, visualization, diff, and export guidance.

b-Istiak-s and others added 6 commits July 24, 2026 16:31
…, 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>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@sourcery-ai

sourcery-ai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds 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 diffs

sequenceDiagram
  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
Loading

File-Level Changes

Change Details Files
Add schema flags to curl parsing, validation, and server response pipeline, including size limits and JSON response field summarization.
  • Extend curl constants with DOCCURL_SCHEMA_FLAGS and new schema size limits for schemas and field descriptions.
  • Update parseCurlCommand and parseCurlForExport to recognize --doccurl-request-schema, --doccurl-response-schema, and --doccurl-field-descriptions in both separate-arg and equals forms, parse inline single-line JSON objects, reject malformed/duplicate/file-ref values, and attach parsed objects to the request spec.
  • Extend parseLegacyRequest and request spec types to always expose requestSchema, responseSchema, and fieldDescriptions fields (default null).
  • Introduce validateSchema and integrate it into validateRequestSpec to enforce object-shape and per-field size limits on request/response schemas and field descriptions.
  • Enhance setupCurlRoutes / run-curl handler to echo attached schemas in the JSON response and to derive a lightweight responseFields summary for JSON responses by inspecting the top-level object fields and basic types using summarizeJsonResponseFields.
engine/curl/constants.js
engine/curl/parse.js
engine/curl/route.js
engine/curl/validate.js
engine/index.js
test/engine/curl-runner.test.js
Add a schema inspection and diff UI to the playground, including schema-aware state, a Schema button, and a shared modal renderer.
  • Introduce frontend/modules/schema.js implementing createSchemaSystem plus helpers to render JSON Schema properties, constraints, and a live diff against summarized response fields; expose selected helpers under test for unit tests.
  • Wire createSchemaSystem into createPlaygroundSystem, tracking lastSchemas and lastResponseFields per block, and opening the schema modal with the latest request/response schemas and response metadata label.
  • Add setSchemaState helper to keep schema state in sync with run-curl / soccli invocations and hide the Schema button when no schemas are attached.
  • Extend playground markup and buildPlaygroundMarkup to include a hidden Schema button in the response actions toolbar, toggled visible when schemas exist, and reset hidden state on initialization/reset.
  • Add stripDocCurlSchemaFlags and use it in createStableCurlBlockId so schema flag changes don’t invalidate stored curl edits in localStorage.
  • Add styling for the schema modal, tabs, tables, and diff labels/rows in frontend/style.css.
frontend/modules/schema.js
frontend/modules/playground.js
frontend/style.css
test/frontend/playground.test.js
Make export pipeline schema-aware and introduce an OpenAPI 3.1 export target that incorporates attached schemas and field descriptions.
  • Extend parseCurlForExport to extract schema flags into requestSchema, responseSchema, and fieldDescriptions fields, enforcing the same JSON, single-line, and non-file rules and rejecting duplicates.
  • Enhance shared export helpers with functions to serialize schemas, emit schema comment blocks and CLI snippets, build field description markdown, and package schema metadata for exporters.
  • Update Postman exporter to add a fourth export option, include schema metadata and formatted description (field descriptions + formatted JSON schemas) in request.description, and ensure raw body options indicate JSON when applicable; expose internals for testing.
  • Update Insomnia exporter to embed schema metadata into a meta object (including a doccurl://note) and to set description text similarly to Postman when schemas are attached, leaving meta nearly empty when none are present.
  • Add a new OpenAPI 3.1 formatter that walks all exported requests, derives paths, methods, parameters (path/query/header), requestBody and responses from attached schemas or example bodies, merges fieldDescriptions into JSON Schemas as description properties, de-duplicates schemas into components.schemas, and merges operations that share the same path/method.
  • Wire the new OpenAPI format into the export UI, filenames, and tests, and update docs to mention OpenAPI alongside Insomnia/Postman and to document schema flags and behavior in a new schemas.md page.
frontend/modules/export/curl.js
frontend/modules/export/formatters/shared.js
frontend/modules/export/formatters/postman.js
frontend/modules/export/formatters/insomnia.js
frontend/modules/export/formatters/openapi.js
frontend/modules/export/index.js
test/frontend/export.test.js
docs/playground.md
docs/schemas.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@b-Istiak-s, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 46 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f74c3e7-b8c1-41fc-809e-ff394709d1c9

📥 Commits

Reviewing files that changed from the base of the PR and between 57cea3e and 423afaa.

📒 Files selected for processing (2)
  • README.md
  • cli/commands/init.js
📝 Walkthrough

Walkthrough

DocCurl 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.

Changes

DocCurl schema feature

Layer / File(s) Summary
Backend schema parsing and API flow
engine/curl/*, engine/index.js, test/engine/curl-runner.test.js
Schema flags, size limits, API payloads, execution stripping, and JSON response-field inference are implemented and tested.
Playground schema state and modal
frontend/modules/playground.js, frontend/modules/schema.js, frontend/style.css, test/frontend/playground.test.js
The playground adds schema state, a Schema button, schema tables, response diffs, and modal styling.
Schema-aware export formats
frontend/modules/export/*, test/frontend/export.test.js
Postman and Insomnia include schema metadata, while OpenAPI 3.1 export generates paths, parameters, request bodies, responses, and reusable schemas.
Schema documentation
docs/playground.md, docs/schemas.md
Documentation covers flags, limits, rendering, diffs, descriptions, exports, and limitations.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: codex

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.08% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is very generic and doesn't clearly describe the main changes beyond broad schema support. Use a more specific title that mentions the main user-facing change, such as schema-aware export and playground support.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/schema-support

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.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
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>

@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: 7

🧹 Nitpick comments (2)
frontend/modules/schema.js (2)

530-554: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Modal 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 the aria-modal overlay. 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 win

Pass documentRef into the DOM builders.

createSchemaSystem only uses documentRef when appending the modal and validating it exists. buildTable, buildDiffTable, and createElement still call the module-global document, so injected documents end up with modal/table nodes created on the wrong document. Pass documentRef through 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

📥 Commits

Reviewing files that changed from the base of the PR and between c3f6f3d and 57cea3e.

📒 Files selected for processing (19)
  • docs/playground.md
  • docs/schemas.md
  • engine/curl/constants.js
  • engine/curl/parse.js
  • engine/curl/route.js
  • engine/curl/validate.js
  • engine/index.js
  • frontend/modules/export/curl.js
  • frontend/modules/export/formatters/insomnia.js
  • frontend/modules/export/formatters/openapi.js
  • frontend/modules/export/formatters/postman.js
  • frontend/modules/export/formatters/shared.js
  • frontend/modules/export/index.js
  • frontend/modules/playground.js
  • frontend/modules/schema.js
  • frontend/style.css
  • test/engine/curl-runner.test.js
  • test/frontend/export.test.js
  • test/frontend/playground.test.js

Comment thread docs/playground.md
- 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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ 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.

Suggested change
- 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.

Comment thread docs/schemas.md
- 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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ 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.

Suggested change
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.

Comment thread engine/curl/validate.js
Comment on lines +27 to +31
const limit = limits[maxField] || limits.maxSchemaBytes;
const serialized = JSON.stringify(schema);
if (serialized.length > limit) {
throw new Error(`${name} exceeds ${limit} bytes (${serialized.length}).`);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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.

Suggested change
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.

Comment on lines +15 to +41
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;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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.js

Repository: 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-L16
  • docs/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.

Comment on lines +134 to +154
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}` };
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ 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.

Comment on lines +319 to +331
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;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ 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.

Suggested change
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.

Comment on lines +70 to +78
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");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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: fix buildFieldDescriptionText by checking the trimmed value directly instead of relying on endsWith("—") (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 inside buildOperation's description-building (lines 277-280), or better, import and reuse buildFieldDescriptionText from shared.js instead 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.

Suggested change
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.

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