feat(curl,task): add nuxt curl, nuxt task list and nuxt task run - #1417
Conversation
commit: |
ea261c6 to
d7ac777
Compare
16813ee to
7c46daf
Compare
|
Somewhat (un)related note as I saw |
5020013 to
4a0ac6b
Compare
|
nice! it makes sense. I am assuming vite wouldn't write a lockfile if it's being used programmatically by something like nuxt |
402050d to
c2bcb18
Compare
c2bcb18 to
9050f12
Compare
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (15)
🚧 Files skipped from review as they are similar to previous changes (14)
📝 WalkthroughWalkthroughAdds Estimated code review effort: 4 (Complex) | ~45 minutes 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 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.
Actionable comments posted: 5
🧹 Nitpick comments (1)
packages/nuxt-cli/src/utils/dev-server.ts (1)
99-101: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winAvoid evaluating the user config twice per task command.
resolveTaskServerinpackages/nuxt-cli/src/commands/task/_utils.ts(lines 79-84) callsfindDevServer(cwd)and thenfindNitroDevWorker(cwd). Each call runsresolveLockDir, which can callgetNuxtConfigwhen.nuxtis absent. Loading the user config twice is slow and can run config side effects twice. Resolve the lock directory once in the caller and pass it asbuildDirto both functions.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/nuxt-cli/src/utils/dev-server.ts` around lines 99 - 101, Update resolveTaskServer to resolve the lock directory once and pass that resolved directory as buildDir to both findDevServer and findNitroDevWorker. Ensure these calls no longer independently invoke resolveLockDir or evaluate the user configuration twice, while preserving their existing server-discovery behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/nuxt-cli/src/commands/curl.ts`:
- Around line 86-104: The curl command must reject a defined body when the
resolved method in the method/body setup is GET or HEAD before calling fetch.
Add an early validation that prints a direct usage error and exits, while
preserving the existing request and network-error handling for valid
combinations.
- Around line 136-151: The readRequestBody function assumes data is always a
string, but repeated -d arguments may provide an array and cause startsWith to
throw. Update readRequestBody to normalize repeated values before checking for
stdin or file syntax, or explicitly reject arrays with a clear error while
preserving existing single-value behavior.
In `@packages/nuxt-cli/src/commands/task/_utils.ts`:
- Around line 240-268: Update both httpRequest and socketRequest to enforce a
configurable timeout suitable for long-running tasks. Pass an AbortSignal or
equivalent timeout to fetch in httpRequest, and configure the request/socket
timeout in socketRequest so stalled connections reject rather than waiting
indefinitely; ensure timers and listeners are cleaned up after completion.
In `@packages/nuxt-cli/src/commands/task/run.ts`:
- Around line 100-112: Update assign() to reject unsafe path segments named
__proto__, constructor, or prototype before reading or assigning target[key],
including at every recursive level. Return without mutation when any segment is
unsafe, while preserving existing nested assignment behavior for other keys.
In `@packages/nuxt-cli/test/unit/commands/curl.spec.ts`:
- Around line 56-65: Make the curl test setup deterministic by explicitly
setting process.stdout.isTTY to false in the beforeEach setup, and restore its
original value during cleanup such as afterEach. Keep the existing write spies
and assertions unchanged, ensuring writeResponseBody always follows the non-TTY
output path regardless of the developer’s terminal environment.
---
Nitpick comments:
In `@packages/nuxt-cli/src/utils/dev-server.ts`:
- Around line 99-101: Update resolveTaskServer to resolve the lock directory
once and pass that resolved directory as buildDir to both findDevServer and
findNitroDevWorker. Ensure these calls no longer independently invoke
resolveLockDir or evaluate the user configuration twice, while preserving their
existing server-discovery behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 3358e6b1-b03a-40d0-88fe-a77f08846250
📒 Files selected for processing (15)
packages/nuxt-cli/src/commands/curl.tspackages/nuxt-cli/src/commands/dev.tspackages/nuxt-cli/src/commands/index.tspackages/nuxt-cli/src/commands/task/_utils.tspackages/nuxt-cli/src/commands/task/index.tspackages/nuxt-cli/src/commands/task/list.tspackages/nuxt-cli/src/commands/task/run.tspackages/nuxt-cli/src/utils/dev-server.tspackages/nuxt-cli/src/utils/json-highlight.tspackages/nuxt-cli/test/e2e/commands.spec.tspackages/nuxt-cli/test/unit/commands/curl.spec.tspackages/nuxt-cli/test/unit/commands/task.spec.tspackages/nuxt-cli/test/unit/dev-server.spec.tspackages/nuxt-cli/test/unit/help.spec.tspackages/nuxt-cli/test/unit/utils/json-highlight.spec.ts
| async function readRequestBody(data: string | undefined): Promise<string | undefined> { | ||
| if (data === undefined) { | ||
| return undefined | ||
| } | ||
| if (data === '@-') { | ||
| const chunks: Buffer[] = [] | ||
| for await (const chunk of process.stdin) { | ||
| chunks.push(chunk as Buffer) | ||
| } | ||
| return Buffer.concat(chunks).toString('utf-8') | ||
| } | ||
| if (data.startsWith('@')) { | ||
| return await readFile(data.slice(1), 'utf-8') | ||
| } | ||
| return data | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
fd -t f 'package.json' packages/nuxt-cli --exec cat {} \; | rg -n 'citty'
rg -nP --type=ts -C3 'toArray\(' packages/nuxt-cli/srcRepository: nuxt/cli
Length of output: 1218
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== packages/nuxt-cli/src/commands/curl.ts relevant sections =="
sed -n '1,225p' packages/nuxt-cli/src/commands/curl.ts
echo
echo "== citty package files =="
fd -t f --hidden '^citty\.' packages/nuxt-cli/node_modules pkg 2>/dev/null || true
fd -t f --hidden 'package.json' packages/nuxt-cli/node_modules 2>/dev/null | head -80
echo
echo "== citty source mentions for args handling =="
rg -n --type=text "string|array|parseArgs|Repeated|command" packages/nuxt-cli/src packages/nuxt-cli 2>/dev/null | head -200 || trueRepository: nuxt/cli
Length of output: 5848
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== lockfile/package manager references for citty =="
git ls-files | rg '(^|/)(pnpm-lock\.yaml|package-lock\.json|yarn\.lock|bun\.lockb?)$' || true
for f in pnpm-lock.yaml package-lock.json yarn.lock bun.lock bun.lockb; do
if [ -f "$f" ]; then
echo "--- $f ---"
rg -n '"?citty"?|\.citty' "$f" || true
fi
done
echo
echo "== npm metadata for citty@0.2.2 =="
npm view citty@0.2.2 dist.tarball version --json 2>/dev/null || true
echo
echo "== fetch and parse citty source from tarball =="
tmp="$(mktemp -d)"
tarball="$(npm view citty@0.2.2 dist.tarball 2>/dev/null || true)"
if [ -n "${tarball:-}" ]; then
curl -fsSL "$tarball" -o "$tmp/citty.tgz"
tar -xzf "$tmp/citty.tgz" -C "$tmp"
echo "--- files ---"
find "$tmp/package" -maxdepth 3 -type f | sort | sed 's#^`#-` #'
echo "--- src/parse.ts ---"
sed -n '1,260p' "$tmp/package/src/parse.ts" 2>/dev/null || true
echo "--- src/utils.ts ---"
sed -n '1,240p' "$tmp/package/src/utils.ts" 2>/dev/null || true
echo "--- src/index.ts ---"
sed -n '1,220p' "$tmp/package/src/index.ts" 2>/dev/null || true
echo "--- repeated arg relevant mentions ---"
rg -n "split|trim|isArray|repeat|args|positionals|value" "$tmp/package/src" || true
fi
echo
echo "== local citty if installed =="
if [ -d packages/nuxt-cli/node_modules/citty ]; then
rg -n '"?citty"?|node_modules/citty' package.json packages/nuxt-cli/package.json
sed -n '1,260p' packages/nuxt-cli/node_modules/citty/src/parse.ts 2>/dev/null || true
else
echo "packages/nuxt-cli/node_modules/citty not present"
fiRepository: nuxt/cli
Length of output: 1569
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== npm registry mirror availability =="
npm view citty dist.time --json 2>/dev/null || echo FAILED
npm view citty@0.2.2 --json 2>/dev/null | jq -r '"dist.tarball=" + .dist.tarball' || true
echo
echo "== parse citty@0.2.2 source from npm tarball using registry-compatible fetch =="
tmp="$(mktemp -d)"
tarball="$(npm view citty@0.2.2 dist.tarball 2>/dev/null || true)"
if [ -n "${tarball:-}" ]; then
echo "tarball=$tarball"
curl -fsSL "$tarball" -o "$tmp/citty.tgz"
tar -xzf "$tmp/citty.tgz" -C "$tmp"
echo "--- package files ---"
find "$tmp/package" -maxdepth 3 -type f | sort | sed 's#^`#-` #'
echo "--- dist citty source snippets ---"
rg -n "string|array|split|trim|parseArgs|defineCommand|command" "$tmp/package/dist" -A3 -B3 || true
echo "--- src citty source snippets, if any ---"
(test -d "$tmp/package/src" && rg -n "string|array|split|trim|parseArgs|defineCommand|command" "$tmp/package/src" -A3 -B3 || true)
else
echo "Unable to fetch citty@0.2.2 tarball: $tarball"
fiRepository: nuxt/cli
Length of output: 25937
Handle a repeated -d flag.
data is declared as type string, but citty can return repeated string args as an array, so data.startsWith can throw a TypeError for -d a -d b. Normalize data before body detection, or reject multiple values with a clear message.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/nuxt-cli/src/commands/curl.ts` around lines 136 - 151, The
readRequestBody function assumes data is always a string, but repeated -d
arguments may provide an array and cause startsWith to throw. Update
readRequestBody to normalize repeated values before checking for stdin or file
syntax, or explicitly reject arrays with a clear error while preserving existing
single-value behavior.
| async function httpRequest(url: string, options: RequestOptions): Promise<TaskResponse> { | ||
| const response = await fetch(url, options) | ||
| return { ok: response.ok, status: response.status, data: parseBody(await response.text()) } | ||
| } | ||
|
|
||
| /** | ||
| * Nitro's dev worker usually listens on a unix socket rather than a port, which | ||
| * `fetch` cannot dial. | ||
| */ | ||
| function socketRequest(socketPath: string, path: string, options: RequestOptions): Promise<TaskResponse> { | ||
| return new Promise((resolve, reject) => { | ||
| const req = http.request({ socketPath, path, method: options.method || 'GET', headers: options.headers }, (res) => { | ||
| res.setEncoding('utf-8') | ||
| let body = '' | ||
| res.on('data', (chunk: string) => { | ||
| body += chunk | ||
| }) | ||
| res.on('end', () => { | ||
| const status = res.statusCode || 0 | ||
| resolve({ ok: status >= 200 && status < 300, status, data: parseBody(body) }) | ||
| }) | ||
| }) | ||
| req.on('error', reject) | ||
| if (options.body) { | ||
| req.write(options.body) | ||
| } | ||
| req.end() | ||
| }) | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Add a timeout to both transports.
httpRequest calls fetch without a signal, and socketRequest sets no socket or request timeout. If the dev worker accepts the connection but never answers, the command waits forever and the user must interrupt it. A long-running task also has no upper bound, so pick a timeout that fits task execution, or make it configurable.
♻️ Proposed change for the socket transport
function socketRequest(socketPath: string, path: string, options: RequestOptions): Promise<TaskResponse> {
return new Promise((resolve, reject) => {
const req = http.request({ socketPath, path, method: options.method || 'GET', headers: options.headers }, (res) => {
res.setEncoding('utf-8')
let body = ''
res.on('data', (chunk: string) => {
body += chunk
})
+ res.on('error', reject)
res.on('end', () => {
const status = res.statusCode || 0
resolve({ ok: status >= 200 && status < 300, status, data: parseBody(body) })
})
})
+ req.setTimeout(REQUEST_TIMEOUT_MS, () => {
+ req.destroy(new Error(`Timed out after ${REQUEST_TIMEOUT_MS}ms`))
+ })
req.on('error', reject)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/nuxt-cli/src/commands/task/_utils.ts` around lines 240 - 268, Update
both httpRequest and socketRequest to enforce a configurable timeout suitable
for long-running tasks. Pass an AbortSignal or equivalent timeout to fetch in
httpRequest, and configure the request/socket timeout in socketRequest so
stalled connections reject rather than waiting indefinitely; ensure timers and
listeners are cleaned up after completion.
9050f12 to
8427d76
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/nuxt-cli/src/commands/curl.ts`:
- Around line 196-198: Update the @<file> handling in readRequestBody to catch
readFile failures and report a direct user-facing error through the command’s
existing error/exit flow, rather than allowing the rejection to propagate as an
unhandled stack trace. Preserve successful UTF-8 file reads and the behavior of
other request-body inputs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: cbb98d57-6264-42a0-9c5d-8d719e932b95
📒 Files selected for processing (15)
packages/nuxt-cli/src/commands/curl.tspackages/nuxt-cli/src/commands/dev.tspackages/nuxt-cli/src/commands/index.tspackages/nuxt-cli/src/commands/task/_utils.tspackages/nuxt-cli/src/commands/task/index.tspackages/nuxt-cli/src/commands/task/list.tspackages/nuxt-cli/src/commands/task/run.tspackages/nuxt-cli/src/utils/dev-server.tspackages/nuxt-cli/src/utils/json-highlight.tspackages/nuxt-cli/test/e2e/commands.spec.tspackages/nuxt-cli/test/unit/commands/curl.spec.tspackages/nuxt-cli/test/unit/commands/task.spec.tspackages/nuxt-cli/test/unit/dev-server.spec.tspackages/nuxt-cli/test/unit/help.spec.tspackages/nuxt-cli/test/unit/utils/json-highlight.spec.ts
🚧 Files skipped from review as they are similar to previous changes (12)
- packages/nuxt-cli/src/utils/json-highlight.ts
- packages/nuxt-cli/src/commands/task/index.ts
- packages/nuxt-cli/src/commands/task/list.ts
- packages/nuxt-cli/test/e2e/commands.spec.ts
- packages/nuxt-cli/test/unit/dev-server.spec.ts
- packages/nuxt-cli/test/unit/help.spec.ts
- packages/nuxt-cli/test/unit/commands/task.spec.ts
- packages/nuxt-cli/src/commands/index.ts
- packages/nuxt-cli/src/utils/dev-server.ts
- packages/nuxt-cli/src/commands/task/run.ts
- packages/nuxt-cli/src/commands/task/_utils.ts
- packages/nuxt-cli/test/unit/utils/json-highlight.spec.ts
81bfd82 to
97926e0
Compare
🔗 Linked issue
closes #353
📚 Description
this picks up on the lockfile support added in #1414 to add two more commands that use the lock file to find the running server:
Video preview
Screen.Recording.2026-07-31.at.23.00.53.mov
nuxt curlit auto-detects JSON bodies, pretty-prints JSON responses in a TTY, and exits
22on HTTP errors (matching real curl's--fail).nuxt task listandnuxt task runImportant
--payload.key=valuepairs are always strings, as they are when Nitro reads them from the query string, so the JSON form is an escape hatch.Note
nuxt taskworks with nitropack 2 (nuxt 4) and Nitro 3 (nuxt 5)behind the scenes, both commands resolve the server in this order:
--urlflagnuxt.lock)nuxt taskonly, when there is no lock†† the worker fallback means
nuxt taskworks even when the dev server was started by something other thannuxt dev(for example, programmatically from nuxt/kit).