Skip to content

Conversation

@fengmk2
Copy link
Member

@fengmk2 fengmk2 commented Dec 30, 2025

Port the co-busboy library as @eggjs/co-busboy with full TypeScript support:

  • Replace chan library with native Promise-based queue implementation
  • Add comprehensive type definitions for Parts, FileStream, FieldTuple
  • Support autoFields, checkField, and checkFile options
  • Handle gzip/deflate compression via inflation
  • Enforce limits with proper 413 error codes
  • Port all tests from Mocha to Vitest (22 tests passing)

close #5756

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Released @eggjs/co-busboy: a TypeScript multipart/form-data parser with async/await API, auto field collection, validation hooks, gzip/deflate decompression, and limit enforcement.
  • Documentation

    • Added README and changelog; included license.
  • Tests

    • Added comprehensive test suite covering parsing scenarios, compression, limits, and validation hooks.
  • Chores

    • Package config, workspace integration, tsconfig and test runner setup; updated multipart plugin to use the new package.
  • Style

    • Updated project ignore rules (node_modules, coverage, dist, .tshy*).

✏️ Tip: You can customize this high-level summary in your review settings.

Port the co-busboy library as @eggjs/co-busboy with full TypeScript support:

- Replace `chan` library with native Promise-based queue implementation
- Add comprehensive type definitions for Parts, FileStream, FieldTuple
- Support autoFields, checkField, and checkFile options
- Handle gzip/deflate compression via inflation
- Enforce limits with proper 413 error codes
- Port all tests from Mocha to Vitest (22 tests passing)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings December 30, 2025 06:43
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds a new TypeScript package @eggjs/co-busboy implementing a Promise-based multipart parser (parse()), with types, validation hooks, auto-field collection, compression handling, tests, docs, license, and workspace/project configuration changes.

Changes

Cohort / File(s) Summary
Package configuration
packages/co-busboy/package.json
New package manifest: metadata, exports, Node >=22.18.0 engine, runtime deps (busboy, inflation, black-hole-stream), devDeps, and publish mappings.
Implementation
packages/co-busboy/src/index.ts
New TypeScript implementation: exported parse(request, options) (default), types (Parts, FileStream, FieldTuple, CoBusboyOptions, CoBusboyError, RequestLike), PromiseQueue, integration with Busboy, autoFields, checkField/checkFile hooks, inflation, and error handling.
Tests
packages/co-busboy/test/index.test.ts
Comprehensive Vitest test suite covering parsing, gzip, autofields, arrays, limits, validation hooks, prototype protection, and async iteration.
Docs & metadata
packages/co-busboy/README.md, packages/co-busboy/CHANGELOG.md, packages/co-busboy/LICENSE, packages/co-busboy/.gitignore
Added README, changelog for v5.0.0-beta.36, MIT license file, and .gitignore entries (node_modules, .tshy*, coverage, dist).
Tooling & config
packages/co-busboy/tsconfig.json, packages/co-busboy/vitest.config.ts
Adds package TypeScript config (extends root) and Vitest project config (15s timeout, include/exclude patterns).
Workspace & root config
pnpm-workspace.yaml, tsconfig.json (root)
Adds new workspace/project reference for packages/co-busboy and registers required packages in the workspace catalog.
Plugin integration
plugins/multipart/package.json, plugins/multipart/src/app/extend/context.ts
plugins/multipart now depends on @eggjs/co-busboy (workspace:*) and updates import to import { parse, type Part } from '@eggjs/co-busboy', with internal typing/cast adjustments preserving external API.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Parser as parse()
    participant Busboy
    participant Queue as PromiseQueue
    participant Validator as checkField/checkFile
    participant Stream as FileStream / BlackHole

    Client->>Parser: parse(request, options)
    Parser->>Busboy: attach listeners (field, file, error, finish)
    Busboy->>Queue: emit field event (name, val, info)
    Queue->>Validator: checkField(name, val)
    alt field valid
        Queue-->>Client: yield FieldTuple
    else field invalid
        Queue-->>Client: emit error / stop
    end

    Busboy->>Queue: emit file event (stream, info)
    Queue->>Validator: checkFile(fieldname, filename, encoding, mime)
    alt file valid
        Queue-->>Client: yield FileStream (readable with metadata)
    else file invalid
        Stream->>BlackHole: pipe to discard
        Queue-->>Client: emit error / skip
    end

    Busboy->>Queue: emit finish/error
    Queue-->>Client: yield null (end)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 A parser hopped in, light and spry,

Parts and fields now neatly fly,
Validation guards the garden gate,
Gzip breezes find their fate,
TypeScript hops — the peace feels right. 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(co-busboy): port co-busboy from JavaScript to TypeScript' clearly and directly describes the primary change: porting co-busboy to TypeScript.
Linked Issues check ✅ Passed The PR successfully addresses the linked issue #5756 by porting co-busboy to TypeScript with full feature parity, including type definitions, Promise-based queue, compression support, validation hooks, and limit enforcement.
Out of Scope Changes check ✅ Passed All changes are scoped to co-busboy porting objectives; however, the PR also updates the multipart plugin to use the new @eggjs/co-busboy package, which is a necessary integration change to maintain functionality.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @fengmk2, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant upgrade to the co-busboy library by porting it entirely to TypeScript. This modernization effort brings full type safety, leverages native Promises for asynchronous operations, and integrates new features for more flexible and secure multipart form data parsing. The changes also include updated testing infrastructure and improved error handling for resource limits.

Highlights

  • TypeScript Port: The co-busboy library has been fully ported to TypeScript, now available as @eggjs/co-busboy, providing comprehensive type definitions for improved code reliability and maintainability.
  • Modernization: The internal chan library has been replaced with a native Promise-based queue implementation, aligning with modern async/await patterns and removing generator dependencies.
  • Enhanced Functionality: New options such as autoFields for automatic field collection, checkField for field validation, and checkFile for file validation have been introduced, along with robust handling for gzip/deflate compression.
  • Error Handling: The library now enforces limits for parts, files, and fields, returning appropriate 413 error codes when these limits are exceeded.
  • Testing Framework Update: All existing tests have been migrated from Mocha to Vitest, ensuring comprehensive test coverage for the new TypeScript implementation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Dec 30, 2025

Deploying egg with  Cloudflare Pages  Cloudflare Pages

Latest commit: fa792f3
Status: ✅  Deploy successful!
Preview URL: https://cbbcaf6a.egg-cci.pages.dev
Branch Preview URL: https://port-co-busboy.egg-cci.pages.dev

View logs

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Dec 30, 2025

Deploying egg-v3 with  Cloudflare Pages  Cloudflare Pages

Latest commit: fa792f3
Status: ✅  Deploy successful!
Preview URL: https://9eb44069.egg-v3.pages.dev
Branch Preview URL: https://port-co-busboy.egg-v3.pages.dev

View logs

@codecov
Copy link

codecov bot commented Dec 30, 2025

Codecov Report

❌ Patch coverage is 99.04762% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 87.67%. Comparing base (19db506) to head (fa792f3).

Files with missing lines Patch % Lines
packages/co-busboy/src/index.ts 99.02% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             next    #5757      +/-   ##
==========================================
+ Coverage   87.56%   87.67%   +0.10%     
==========================================
  Files         563      564       +1     
  Lines       10940    11043     +103     
  Branches     1242     1248       +6     
==========================================
+ Hits         9580     9682     +102     
- Misses       1276     1277       +1     
  Partials       84       84              

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

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

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This is a solid pull request that successfully ports co-busboy to TypeScript, introducing modern async/await patterns and comprehensive type definitions. The replacement of the chan library with a native Promise-based queue is a great improvement. The code is well-structured and the tests are quite thorough.

I've identified a potential bug in the error handling logic within onEnd that could swallow certain errors from busboy. I've also suggested an improvement to a test case to make it more robust by explicitly asserting that an error is thrown under specific conditions. Overall, these are minor issues in an otherwise excellent contribution.

Comment on lines +347 to +351
// Ignore 'Unexpected end of form' if we already have an error
if (!lastError && err && err.message !== 'Unexpected end of form') {
lastError = err;
debug('set lastError');
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The current logic for handling errors in onEnd seems to incorrectly swallow the 'Unexpected end of form' error, even when it's the only error that has occurred. This contradicts the behavior expected by the test should handle error: Unexpected end of form.

The condition err.message !== 'Unexpected end of form' prevents lastError from being set for this specific error, causing promiseQueue.push(null) to be called instead of propagating the error.

To align with the test expectations and provide more robust error handling, this condition should be removed to ensure all errors from busboy are captured and propagated if no other error has been recorded.

Suggested change
// Ignore 'Unexpected end of form' if we already have an error
if (!lastError && err && err.message !== 'Unexpected end of form') {
lastError = err;
debug('set lastError');
}
// If an error is emitted by busboy and we haven't already captured an error,
// let's store it.
if (!lastError && err) {
lastError = err;
debug('set lastError: %s', err);
}
Comment on lines +606 to +632
it('will get nothing if set wrong encoding on gziped content', async () => {
const stream = gziped();
delete (stream.headers as { 'content-encoding'?: string })['content-encoding'];

const parts = parse(stream as unknown as { headers: Record<string, string> }, {
autoFields: true,
});
let promise;
let part;
let fields = 0;
let streams = 0;

while (((promise = parts()), (part = await promise))) {
assert.ok(promise instanceof Promise);
if (Array.isArray(part)) {
fields++;
} else {
streams++;
part.resume();
}
}

assert.strictEqual(fields, 0);
assert.strictEqual(streams, 0);
assert.strictEqual(parts.fields.length, 0);
assert.strictEqual(Object.keys(parts.field).length, 0);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This test correctly checks that no parts are parsed when the content-encoding header is missing from a gzipped request. However, it doesn't assert that an error is thrown, which is the expected behavior from busboy when it receives malformed data. The test currently passes if an error is thrown (because the while loop breaks), but it doesn't verify that this is what actually happened.

It would be more robust to explicitly assert that the promise rejects with an appropriate error, such as 'Unexpected end of form'. This ensures the error handling path is working as expected.

    it('should throw an error for wrong encoding on gzipped content', async () => {
      const stream = gziped();
      delete (stream.headers as { 'content-encoding'?: string })['content-encoding'];

      const parts = parse(stream as unknown as { headers: Record<string, string> }, {
        autoFields: true,
      });

      await assert.rejects(async () => {
        // eslint-disable-next-line no-empty
        while (await parts()) {}
      }, (err: Error) => {
        assert.ok(
          /Malformed part header|Unexpected end of form/.test(err.message),
          `Expected a parsing error, but got: "${err.message}"`
        );
        return true;
      });

      assert.strictEqual(parts.fields.length, 0);
      assert.strictEqual(Object.keys(parts.field).length, 0);
    });
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (4)
packages/co-busboy/package.json (2)

32-38: Consider adding explicit types export in publishConfig

The publishConfig.exports maps "." to "./dist/index.js" but doesn't include an explicit types field. While TypeScript can often resolve types automatically, explicitly specifying types improves compatibility with different TypeScript moduleResolution settings.

🔎 Suggested enhancement
   "publishConfig": {
     "access": "public",
     "exports": {
-      ".": "./dist/index.js",
+      ".": {
+        "types": "./dist/index.d.ts",
+        "import": "./dist/index.js",
+        "default": "./dist/index.js"
+      },
       "./package.json": "./package.json"
     }
   },

39-41: Consider adding standard scripts for consistency

The scripts section only includes typecheck. Other packages in the monorepo typically include build and test scripts. If these are handled at the root level, this is fine, but consider adding them for standalone package development.

packages/co-busboy/test/index.test.ts (2)

390-399: Test fixture cleanup is properly handled.

The beforeAll/afterAll hooks correctly manage the 10MB test file lifecycle. Consider wrapping fs.unlinkSync in a try-catch to prevent test failures if the file was already removed.

🔎 Defensive cleanup
     afterAll(() => {
-      fs.unlinkSync(logfile);
+      try {
+        fs.unlinkSync(logfile);
+      } catch {
+        // File may already be removed
+      }
     });

653-672: Clarify test intent with a comment.

This test verifies that checkFile errors are overridden by busboy's "Unexpected end of form" error when the form is malformed. The expected behavior might not be immediately obvious to future maintainers.

🔎 Add clarifying comment
-    it('should handle error: Unexpected end of form with checkFile', async () => {
+    // Verifies that busboy's parsing error takes precedence over checkFile validation errors
+    it('should handle error: Unexpected end of form with checkFile', async () => {
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 19db506 and 379106b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • packages/co-busboy/.gitignore
  • packages/co-busboy/CHANGELOG.md
  • packages/co-busboy/LICENSE
  • packages/co-busboy/README.md
  • packages/co-busboy/package.json
  • packages/co-busboy/src/index.ts
  • packages/co-busboy/test/index.test.ts
  • packages/co-busboy/tsconfig.json
  • packages/co-busboy/vitest.config.ts
  • pnpm-workspace.yaml
  • tsconfig.json
🧰 Additional context used
📓 Path-based instructions (12)
packages/*/tsconfig.json

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

All sub-project tsconfig.json files MUST extend from root tsconfig.json using 'extends': '../../tsconfig.json'

Files:

  • packages/co-busboy/tsconfig.json
**/tsconfig.json

📄 CodeRabbit inference engine (CLAUDE.md)

**/tsconfig.json: Sub-project TypeScript configurations must follow the minimal pattern with only 'extends: ../../tsconfig.json' and no additional options - all settings are managed at workspace root
Each package in the monorepo must include a 'tsconfig.json' file that extends from the root workspace tsconfig.json with only the 'extends' field

Files:

  • packages/co-busboy/tsconfig.json
  • tsconfig.json
**/package.json

📄 CodeRabbit inference engine (CLAUDE.md)

**/package.json: Use 'workspace:*' for internal package dependencies in package.json to ensure workspace linking
Use 'catalog:' prefix for external dependencies defined in pnpm-workspace.yaml catalog in package.json files
Add 'type: module' to package.json to enable ES Module support and convert from CommonJS to ESM

Files:

  • packages/co-busboy/package.json
packages/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

packages/**/*.ts: Use TypeScript throughout all packages in the Eggjs monorepo
Use strict TypeScript mode in all packages

Files:

  • packages/co-busboy/vitest.config.ts
  • packages/co-busboy/test/index.test.ts
  • packages/co-busboy/src/index.ts
**/*.{ts,tsx,js,mjs}

📄 CodeRabbit inference engine (AGENTS.md)

Use .ts sources over CommonJS and prefer ESM for exports

Use ESM (ES Modules) syntax with 'import' and 'export' statements - add 'type: module' to package.json for CommonJS to ESM migration

Files:

  • packages/co-busboy/vitest.config.ts
  • packages/co-busboy/test/index.test.ts
  • packages/co-busboy/src/index.ts
{packages,plugins}/**/*.{ts,tsx,js,mjs}

📄 CodeRabbit inference engine (AGENTS.md)

Name files in lowercase with hyphens (e.g. loader-context.ts)

Files:

  • packages/co-busboy/vitest.config.ts
  • packages/co-busboy/test/index.test.ts
  • packages/co-busboy/src/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Name classes in PascalCase
Name functions and variables in camelCase

Files:

  • packages/co-busboy/vitest.config.ts
  • packages/co-busboy/test/index.test.ts
  • packages/co-busboy/src/index.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: All exported functions and methods must have explicit return type annotations to support TypeScript's isolatedDeclarations flag
Avoid using computed property names with symbols in class declarations - use override methods instead for custom property definitions
Use explicit type annotations for class properties and add 'unique symbol' type for exported symbols to support isolatedDeclarations
Enable strict mode in all TypeScript packages and use explicit return types for public APIs
Prefer interfaces over type aliases for object shapes in TypeScript
Use readonly modifiers where appropriate in TypeScript class properties and interface definitions
Avoid using 'any' type in TypeScript - use 'unknown' when type is truly unknown
Use BaseContextClass as the base class for context-aware components like services and subscriptions in Egg applications
Monitor memory usage and implement proper cleanup in lifecycle hooks to avoid memory leaks in long-running processes
Avoid global state in worker processes - use application context or request context for state management

Files:

  • packages/co-busboy/vitest.config.ts
  • packages/co-busboy/test/index.test.ts
  • packages/co-busboy/src/index.ts
**/{tsdown,vitest}.config.ts

📄 CodeRabbit inference engine (CLAUDE.md)

All config file exports (tsdown.config.ts, vitest.config.ts) must use typed intermediate variables with explicit type annotations

Files:

  • packages/co-busboy/vitest.config.ts
packages/**/test/**/*.test.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

packages/**/test/**/*.test.ts: Use Vitest as the standard test runner for all packages; test files follow pattern 'test/**/*.test.ts'
Use 'import { describe, it } from "vitest"' for test functions in Vitest test files
Use Node.js built-in 'assert' module for assertions in tests

Files:

  • packages/co-busboy/test/index.test.ts
**/test/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

Configure Vitest to discover **/test/**/*.test.ts within each package and mirror this pattern when adding test suites

**/test/**/*.test.ts: Test files in Vitest-based packages must follow the naming pattern 'test/**/*.test.ts' and import test functions from vitest
Configure test files to use Node.js built-in 'assert' module for assertions in Vitest tests
Import test functions (describe, it, expect, beforeEach, afterEach) from 'vitest' package in test files

Files:

  • packages/co-busboy/test/index.test.ts
pnpm-workspace.yaml

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Define dependencies in pnpm-workspace.yaml catalog section and reference catalog entries with 'package-name': 'catalog:' or internal workspace dependencies with 'package-name': 'workspace:*'

Files:

  • pnpm-workspace.yaml
🧠 Learnings (46)
📓 Common learnings
Learnt from: CR
Repo: eggjs/egg PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T14:37:59.711Z
Learning: Applies to packages/**/*.ts : Use TypeScript throughout all packages in the Eggjs monorepo
📚 Learning: 2025-12-20T09:24:59.960Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-20T09:24:59.960Z
Learning: Run `pnpm run test` for Vitest suite or `pnpm run test:cov` for coverage before submitting

Applied to files:

  • packages/co-busboy/.gitignore
  • packages/co-busboy/test/index.test.ts
📚 Learning: 2025-12-20T09:24:59.960Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-20T09:24:59.960Z
Learning: Maintain Egg as a pnpm monorepo with core runtime code in `packages/egg`, supporting modules in `packages/core`, `packages/cluster`, and `packages/utils`, optional integrations under `plugins/`, examples in `examples/`, documentation site in `site/`, and unit tests beside packages under `test/` directories with fixtures in `test/fixtures/`

Applied to files:

  • packages/co-busboy/.gitignore
  • packages/co-busboy/package.json
  • tsconfig.json
📚 Learning: 2025-12-21T03:05:13.313Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: tegg/CLAUDE.md:0-0
Timestamp: 2025-12-21T03:05:13.313Z
Learning: Applies to tegg/core/*/tsconfig.json : Each new core package should include `tsconfig.json` extending `eggjs/tsconfig`

Applied to files:

  • packages/co-busboy/tsconfig.json
  • packages/co-busboy/package.json
  • tsconfig.json
📚 Learning: 2025-11-27T14:37:59.711Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T14:37:59.711Z
Learning: Applies to packages/*/tsconfig.json : All sub-project tsconfig.json files MUST extend from root tsconfig.json using 'extends': '../../tsconfig.json'

Applied to files:

  • packages/co-busboy/tsconfig.json
  • tsconfig.json
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to **/tsconfig.json : Sub-project TypeScript configurations must follow the minimal pattern with only 'extends: ../../tsconfig.json' and no additional options - all settings are managed at workspace root

Applied to files:

  • packages/co-busboy/tsconfig.json
  • packages/co-busboy/vitest.config.ts
  • tsconfig.json
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to **/tsconfig.json : Each package in the monorepo must include a 'tsconfig.json' file that extends from the root workspace tsconfig.json with only the 'extends' field

Applied to files:

  • packages/co-busboy/tsconfig.json
  • tsconfig.json
📚 Learning: 2025-11-27T14:37:59.711Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T14:37:59.711Z
Learning: Applies to packages/*/tsdown.config.ts : Use tsdown for unbundled ESM builds that preserves file structure; configure via tsdown.config.ts in each package

Applied to files:

  • packages/co-busboy/tsconfig.json
📚 Learning: 2025-11-27T14:37:59.711Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T14:37:59.711Z
Learning: Applies to packages/**/*.ts : Use TypeScript throughout all packages in the Eggjs monorepo

Applied to files:

  • packages/co-busboy/tsconfig.json
  • packages/co-busboy/package.json
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to /tsconfig.json : Root tsconfig.json must extend from 'eggjs/tsconfig' and use ${configDir} variable for dynamic path resolution in sub-projects

Applied to files:

  • packages/co-busboy/tsconfig.json
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: All packages must include TypeScript type checking using 'tsc --noEmit' in the typecheck script

Applied to files:

  • packages/co-busboy/tsconfig.json
  • tsconfig.json
📚 Learning: 2025-11-27T14:37:59.711Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T14:37:59.711Z
Learning: Applies to packages/**/*.ts : Use strict TypeScript mode in all packages

Applied to files:

  • packages/co-busboy/tsconfig.json
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to **/*.ts : Enable strict mode in all TypeScript packages and use explicit return types for public APIs

Applied to files:

  • packages/co-busboy/tsconfig.json
  • packages/co-busboy/src/index.ts
📚 Learning: 2025-12-20T09:24:59.960Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-20T09:24:59.960Z
Learning: Applies to **/*.{ts,tsx,js,mjs} : Use `.ts` sources over CommonJS and prefer ESM for exports

Applied to files:

  • packages/co-busboy/tsconfig.json
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to **/*.{ts,tsx,js,mjs} : Use ESM (ES Modules) syntax with 'import' and 'export' statements - add 'type: module' to package.json for CommonJS to ESM migration

Applied to files:

  • packages/co-busboy/tsconfig.json
  • packages/co-busboy/package.json
  • packages/co-busboy/src/index.ts
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to **vitest.config.ts : All plugin Vitest configurations should use a vitest.config.ts file for test setup and configuration

Applied to files:

  • packages/co-busboy/tsconfig.json
  • packages/co-busboy/vitest.config.ts
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to /tsconfig.json : Root tsconfig.json must use project references for all sub-packages and include them in the references array for proper incremental builds

Applied to files:

  • packages/co-busboy/tsconfig.json
  • tsconfig.json
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to plugins/*/tsdown.config.ts : Most plugins do NOT need a tsdown.config.ts file - they inherit all settings from workspace root, only create one if you need custom options like copying assets

Applied to files:

  • packages/co-busboy/tsconfig.json
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to plugins/*/package.json : Plugin package.json exports must mirror between development (./src/*.ts) and published (./dist/*.js) configurations - use publishConfig.exports to override for npm

Applied to files:

  • packages/co-busboy/package.json
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to **/package.json : Add 'type: module' to package.json to enable ES Module support and convert from CommonJS to ESM

Applied to files:

  • packages/co-busboy/package.json
📚 Learning: 2025-12-21T03:05:13.313Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: tegg/CLAUDE.md:0-0
Timestamp: 2025-12-21T03:05:13.313Z
Learning: Applies to tegg/core/*/package.json : New core packages should include standard scripts in `package.json` with `typecheck` script using tsgo

Applied to files:

  • packages/co-busboy/package.json
📚 Learning: 2025-12-21T03:05:13.313Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: tegg/CLAUDE.md:0-0
Timestamp: 2025-12-21T03:05:13.313Z
Learning: Applies to tegg/**/package.json : Node.js version must be >= 22.18.0

Applied to files:

  • packages/co-busboy/package.json
📚 Learning: 2025-12-21T03:05:13.313Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: tegg/CLAUDE.md:0-0
Timestamp: 2025-12-21T03:05:13.313Z
Learning: Applies to tegg/**/package.json : Use `workspace:*` protocol for internal monorepo dependencies (both tegg and egg packages)

Applied to files:

  • packages/co-busboy/package.json
  • pnpm-workspace.yaml
📚 Learning: 2025-12-21T03:05:13.313Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: tegg/CLAUDE.md:0-0
Timestamp: 2025-12-21T03:05:13.313Z
Learning: Applies to tegg/core/*/src/index.ts : Core packages should export public API through `src/index.ts`

Applied to files:

  • packages/co-busboy/package.json
📚 Learning: 2025-12-21T03:05:13.313Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: tegg/CLAUDE.md:0-0
Timestamp: 2025-12-21T03:05:13.313Z
Learning: Applies to tegg/**/package.json : Egg.js version must be >= 4.1.0

Applied to files:

  • packages/co-busboy/package.json
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to plugins/*/package.json : All plugin package.json files must include egg in peerDependencies to ensure compatibility with the framework

Applied to files:

  • packages/co-busboy/package.json
📚 Learning: 2025-12-21T03:05:13.313Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: tegg/CLAUDE.md:0-0
Timestamp: 2025-12-21T03:05:13.313Z
Learning: Applies to tegg/**/vitest.config.ts : Configure test discovery through monorepo root `vitest.config.ts` which includes all tegg packages

Applied to files:

  • packages/co-busboy/vitest.config.ts
📚 Learning: 2025-11-27T14:37:59.711Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T14:37:59.711Z
Learning: Applies to packages/**/test/**/*.test.ts : Use Vitest as the standard test runner for all packages; test files follow pattern 'test/**/*.test.ts'

Applied to files:

  • packages/co-busboy/vitest.config.ts
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to **/test/**/*.test.ts : Import test functions (describe, it, expect, beforeEach, afterEach) from 'vitest' package in test files

Applied to files:

  • packages/co-busboy/vitest.config.ts
  • packages/co-busboy/test/index.test.ts
📚 Learning: 2025-12-20T09:24:59.960Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-20T09:24:59.960Z
Learning: Applies to **/test/**/*.test.ts : Configure Vitest to discover `**/test/**/*.test.ts` within each package and mirror this pattern when adding test suites

Applied to files:

  • packages/co-busboy/vitest.config.ts
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to **/test/**/*.test.ts : Configure test files to use Node.js built-in 'assert' module for assertions in Vitest tests

Applied to files:

  • packages/co-busboy/vitest.config.ts
  • packages/co-busboy/test/index.test.ts
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to **/test/**/*.test.ts : Test files in Vitest-based packages must follow the naming pattern 'test/**/*.test.ts' and import test functions from vitest

Applied to files:

  • packages/co-busboy/vitest.config.ts
📚 Learning: 2025-11-27T14:37:59.711Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T14:37:59.711Z
Learning: Applies to packages/**/test/**/*.test.ts : Use 'import { describe, it } from "vitest"' for test functions in Vitest test files

Applied to files:

  • packages/co-busboy/vitest.config.ts
📚 Learning: 2025-12-21T03:05:13.313Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: tegg/CLAUDE.md:0-0
Timestamp: 2025-12-21T03:05:13.313Z
Learning: Applies to tegg/**/__tests__/**/*.test.{ts,tsx,js} : Use Vitest for testing all tegg packages

Applied to files:

  • packages/co-busboy/vitest.config.ts
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to **/{tsdown,vitest}.config.ts : All config file exports (tsdown.config.ts, vitest.config.ts) must use typed intermediate variables with explicit type annotations

Applied to files:

  • packages/co-busboy/vitest.config.ts
📚 Learning: 2025-12-20T09:24:59.960Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-20T09:24:59.960Z
Learning: Add regression cases that exercise both CommonJS and TypeScript example apps when features affect HTTP or process orchestration

Applied to files:

  • packages/co-busboy/test/index.test.ts
📚 Learning: 2025-11-27T14:37:59.711Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T14:37:59.711Z
Learning: Applies to packages/**/test/**/*.test.ts : Use Node.js built-in 'assert' module for assertions in tests

Applied to files:

  • packages/co-busboy/test/index.test.ts
📚 Learning: 2025-11-27T14:37:59.711Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T14:37:59.711Z
Learning: Run 'pnpm run test' to validate changes (set timeout to 180+ seconds for all tests, 120+ seconds for coverage) and never cancel the command

Applied to files:

  • packages/co-busboy/test/index.test.ts
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to **/*.ts : Use explicit type annotations for class properties and add 'unique symbol' type for exported symbols to support isolatedDeclarations

Applied to files:

  • packages/co-busboy/src/index.ts
📚 Learning: 2025-11-27T14:37:59.711Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T14:37:59.711Z
Learning: Applies to pnpm-workspace.yaml : Define dependencies in pnpm-workspace.yaml catalog section and reference catalog entries with 'package-name': 'catalog:' or internal workspace dependencies with 'package-name': 'workspace:*'

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Use pnpm workspace with catalog mode for centralized dependency management - define dependencies once in pnpm-workspace.yaml catalog and reference them using 'catalog:' in individual packages

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to **/package.json : Use 'catalog:' prefix for external dependencies defined in pnpm-workspace.yaml catalog in package.json files

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-12-21T03:05:13.313Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: tegg/CLAUDE.md:0-0
Timestamp: 2025-12-21T03:05:13.313Z
Learning: Applies to tegg/**/pnpm-workspace.yaml : Use pnpm workspaces with `catalog:` protocol for shared external dependencies

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-12-21T14:11:29.307Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-21T14:11:29.307Z
Learning: Applies to **/package.json : Use 'workspace:*' for internal package dependencies in package.json to ensure workspace linking

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-12-20T09:24:59.960Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-20T09:24:59.960Z
Learning: Run `pnpm install` to hydrate the workspace (Node.js ≥ 20.19.0 required)

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-12-21T03:05:13.313Z
Learnt from: CR
Repo: eggjs/egg PR: 0
File: tegg/CLAUDE.md:0-0
Timestamp: 2025-12-21T03:05:13.313Z
Learning: Applies to tegg/**/pnpm-workspace.yaml : Centralize all shared dependency versions in the root `pnpm-workspace.yaml` rather than in individual tegg package files

Applied to files:

  • pnpm-workspace.yaml
🧬 Code graph analysis (1)
packages/co-busboy/test/index.test.ts (1)
packages/co-busboy/src/index.ts (1)
  • parse (193-370)
🪛 Biome (2.1.2)
packages/co-busboy/src/index.ts

[error] 266-266: The function should not return a value because its return type is void.

The function is here:

'void' signals the absence of value. The returned value is likely to be ignored by the caller.

(lint/correctness/noVoidTypeReturn)


[error] 318-318: The function should not return a value because its return type is void.

The function is here:

'void' signals the absence of value. The returned value is likely to be ignored by the caller.

(lint/correctness/noVoidTypeReturn)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (22)
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: Test (macos-latest, 22, 1/3)
  • GitHub Check: Test (macos-latest, 22, 3/3)
  • GitHub Check: Test (ubuntu-latest, 22, 1/3)
  • GitHub Check: Test (ubuntu-latest, 24, 3/3)
  • GitHub Check: Test (macos-latest, 20, 2/3)
  • GitHub Check: Test (macos-latest, 20, 1/3)
  • GitHub Check: Test (macos-latest, 24, 1/3)
  • GitHub Check: Test (ubuntu-latest, 24, 1/3)
  • GitHub Check: Test (ubuntu-latest, 22, 2/3)
  • GitHub Check: Test (windows-latest, 20, 3/3)
  • GitHub Check: Test (windows-latest, 22, 3/3)
  • GitHub Check: Test (ubuntu-latest, 20, 3/3)
  • GitHub Check: Test bin (ubuntu-latest, 24, 1/3)
  • GitHub Check: Test bin (ubuntu-latest, 24, 0/3)
  • GitHub Check: Test bin (windows-latest, 24, 2/3)
  • GitHub Check: Test bin (windows-latest, 24, 0/3)
  • GitHub Check: Test bin (ubuntu-latest, 24, 2/3)
  • GitHub Check: Test bin (windows-latest, 24, 1/3)
  • GitHub Check: typecheck
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Cloudflare Pages: egg-v3
🔇 Additional comments (18)
packages/co-busboy/LICENSE (1)

1-21: LGTM!

Standard MIT license text is correctly applied to the new package.

packages/co-busboy/.gitignore (1)

1-4: LGTM!

Standard ignore patterns for a TypeScript package with build outputs and test coverage.

packages/co-busboy/tsconfig.json (1)

1-3: LGTM!

The TypeScript configuration correctly follows the monorepo pattern by extending from the root tsconfig.json with no additional options.

As per coding guidelines, this is the required minimal pattern for sub-project configurations.

tsconfig.json (1)

35-37: LGTM!

The project reference for the new co-busboy package is correctly added to enable TypeScript's incremental builds and project references.

packages/co-busboy/README.md (1)

1-142: LGTM!

The documentation is comprehensive and well-structured, covering:

  • Installation and basic usage
  • Complete API reference with TypeScript interfaces
  • Error handling patterns
  • Compression support

The examples clearly demonstrate both basic and advanced usage patterns.

packages/co-busboy/CHANGELOG.md (1)

13-13: The Node.js >= 22.18.0 requirement in the changelog is correct and intentional. This minimum version is documented in CLAUDE.md as being enforced across all packages in the monorepo to support modern JavaScript features and optimal performance. Node.js 22.18.0 introduced native TypeScript file execution support, which aligns with the framework's ESM-first approach.

Likely an incorrect or invalid review comment.

pnpm-workspace.yaml (1)

27-27: The dependency versions are current and stable. Verification confirms:

  • busboy@^1.6.0 — Latest version (1.6.0)
  • inflation@^2.1.0 — Latest version (2.1.0)
  • @types/busboy@^1.5.4 — Latest version (1.5.4, published May 2024)
  • black-hole-stream@0.0.1 — Only published version (pre-1.0, last published ~12 years ago; no newer version available)
packages/co-busboy/package.json (1)

1-56: Package configuration follows project conventions.

The package correctly uses ESM ("type": "module"), workspace:* for internal dependencies, and catalog: for external dependencies. The engine requirement and exports configuration align with the monorepo standards.

packages/co-busboy/test/index.test.ts (3)

1-11: Imports follow project conventions.

The test file correctly imports from vitest for test functions and uses Node.js built-in assert module for assertions, as required by the coding guidelines.


20-81: Well-structured test helpers with proper multipart fixtures.

The mock request helpers correctly construct multipart form data with appropriate boundaries and content types. The type casting approach (as unknown as MockRequest) is verbose but acceptable for test fixtures.


133-706: Comprehensive test suite with good coverage.

The test suite thoroughly exercises the parse function across multiple scenarios including autoFields mode, gzip compression, field/file limits, validation hooks, and error handling. The promise-based iteration tests in the "with promise" describe block validate the async API contract.

packages/co-busboy/src/index.ts (7)

1-14: Clean imports and utility setup.

The @ts-expect-error comments for untyped packages (black-hole-stream, inflation) are appropriate. Using debuglog from node:util is the correct approach for optional debug output.


16-108: Well-designed type definitions.

The types are explicit and support isolatedDeclarations. The Parts interface correctly models the callable function with attached properties. CoBusboyOptions appropriately omits headers from BusboyConfig since headers come from the request.


114-160: Solid PromiseQueue implementation.

The queue correctly handles the producer-consumer pattern with proper error propagation. One consideration: if finished is set to true and there are items in pendingResolvers, they would remain unresolved. However, this scenario shouldn't occur in normal usage since onEnd always pushes null or an error before finishing.


244-256: Good defensive initialization of field/fields.

Initializing parts.field and parts.fields even when autoFields is false ensures type safety and prevents runtime errors if consumers accidentally access these properties.


361-370: Proper cleanup prevents memory leaks.

The cleanup function correctly removes event listeners from both the inflated request stream and busboy. Keeping finish/error listeners ensures all data is processed before completion.


193-256: Well-structured parse function with comprehensive feature support.

The implementation correctly handles:

  • Koa context and raw Node.js requests
  • Compressed request bodies (gzip/deflate)
  • Field and file validation hooks
  • Request limits with proper 413 status codes
  • Auto-field collection mode

The docblock example is helpful for API consumers.


337-359: Well-handled end/error lifecycle with proper cleanup.

The setImmediate for removing the error listener is a smart technique to catch late errors. The logic to ignore "Unexpected end of form" when lastError exists ensures user-defined errors (from checkField/checkFile) take precedence.

Note: When there's no prior error, "Unexpected end of form" will still be thrown since the condition !lastError && err.message !== 'Unexpected end of form' prevents it from being set as lastError, but then line 354-355 won't execute either. Actually, reviewing again - if err.message === 'Unexpected end of form' and !lastError, then lastError stays undefined and null is pushed. But the test expects the error to be thrown. Let me check the test again...

The test at line 636-650 shows the error IS thrown. This works because busboy emits the error, and since there's no lastError, the condition on line 348 evaluates to false (because err.message === 'Unexpected end of form'), so lastError remains undefined, and null is pushed. Wait, that would mean the loop ends normally without error.

Looking at the test more carefully - the error must come from somewhere else. The invalidRequest() creates a form that will trigger an error in busboy during parsing, which might be a different error path.

#!/bin/bash
# Check how busboy handles invalid boundaries and what errors it emits
rg -n "Unexpected end of form" --type ts -C 3
Comment on lines +258 to +268
function onField(name: string, val: string, info: FieldInfo): void {
const fieldnameTruncated = info.nameTruncated;
const valTruncated = info.valueTruncated;

if (checkField) {
const err = checkField(name, val, fieldnameTruncated, valTruncated);
if (err) {
debug('onField error: %s', err);
return onError(err);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix void return type violation.

The static analysis tool correctly identifies that return onError(err) returns a value from a function with implicit void return type. The return is used for early exit, but the value is meaningless.

🔎 Proposed fix
     if (checkField) {
       const err = checkField(name, val, fieldnameTruncated, valTruncated);
       if (err) {
         debug('onField error: %s', err);
-        return onError(err);
+        onError(err);
+        return;
       }
     }
📝 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 onField(name: string, val: string, info: FieldInfo): void {
const fieldnameTruncated = info.nameTruncated;
const valTruncated = info.valueTruncated;
if (checkField) {
const err = checkField(name, val, fieldnameTruncated, valTruncated);
if (err) {
debug('onField error: %s', err);
return onError(err);
}
}
function onField(name: string, val: string, info: FieldInfo): void {
const fieldnameTruncated = info.nameTruncated;
const valTruncated = info.valueTruncated;
if (checkField) {
const err = checkField(name, val, fieldnameTruncated, valTruncated);
if (err) {
debug('onField error: %s', err);
onError(err);
return;
}
}
🧰 Tools
🪛 Biome (2.1.2)

[error] 266-266: The function should not return a value because its return type is void.

The function is here:

'void' signals the absence of value. The returned value is likely to be ignored by the caller.

(lint/correctness/noVoidTypeReturn)

🤖 Prompt for AI Agents
packages/co-busboy/src/index.ts around lines 258 to 268: the handler uses
"return onError(err)" which violates the function's void return type by
returning a value; call onError(err) without returning its value and explicitly
exit the function by using a bare "return;" (i.e. replace "return onError(err)"
with "onError(err); return;") so the early-exit is preserved but no value is
returned.
Comment on lines +312 to +320
if (checkFile) {
const err = checkFile(fieldname, file, filename, encoding, mimeType);
if (err) {
// Make sure request stream's data has been read
const blackHoleStream = new BlackHoleStream();
file.pipe(blackHoleStream);
return onError(err);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix void return type violation.

Same issue as in onField - the return onError(err) statement returns a value from a void function.

🔎 Proposed fix
     if (checkFile) {
       const err = checkFile(fieldname, file, filename, encoding, mimeType);
       if (err) {
         // Make sure request stream's data has been read
         const blackHoleStream = new BlackHoleStream();
         file.pipe(blackHoleStream);
-        return onError(err);
+        onError(err);
+        return;
       }
     }
📝 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
if (checkFile) {
const err = checkFile(fieldname, file, filename, encoding, mimeType);
if (err) {
// Make sure request stream's data has been read
const blackHoleStream = new BlackHoleStream();
file.pipe(blackHoleStream);
return onError(err);
}
}
if (checkFile) {
const err = checkFile(fieldname, file, filename, encoding, mimeType);
if (err) {
// Make sure request stream's data has been read
const blackHoleStream = new BlackHoleStream();
file.pipe(blackHoleStream);
onError(err);
return;
}
}
🧰 Tools
🪛 Biome (2.1.2)

[error] 318-318: The function should not return a value because its return type is void.

The function is here:

'void' signals the absence of value. The returned value is likely to be ignored by the caller.

(lint/correctness/noVoidTypeReturn)

🤖 Prompt for AI Agents
In packages/co-busboy/src/index.ts around lines 312 to 320, the handler is using
"return onError(err)" inside a void function which causes a void return type
violation; instead call onError(err) without returning its value and then return
void to stop execution. Concretely, after piping the file to the
BlackHoleStream, invoke onError(err); and then use a bare return (or otherwise
exit the function) so no value is returned from the void function.
Comment on lines +1 to +9
import { defineProject } from 'vitest/config';

export default defineProject({
test: {
testTimeout: 15000,
include: ['test/**/*.test.ts'],
exclude: ['**/test/fixtures/**', '**/node_modules/**', '**/dist/**'],
},
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add explicit type annotation for the config export.

According to coding guidelines, config file exports must use typed intermediate variables with explicit type annotations to support better type checking and IDE support.

🔎 Proposed fix
-import { defineProject } from 'vitest/config';
+import { defineProject, type UserProjectConfigExport } from 'vitest/config';

-export default defineProject({
+const config: UserProjectConfigExport = defineProject({
   test: {
     testTimeout: 15000,
     include: ['test/**/*.test.ts'],
     exclude: ['**/test/fixtures/**', '**/node_modules/**', '**/dist/**'],
   },
 });
+
+export default config;

Based on coding guidelines for config file exports.

📝 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
import { defineProject } from 'vitest/config';
export default defineProject({
test: {
testTimeout: 15000,
include: ['test/**/*.test.ts'],
exclude: ['**/test/fixtures/**', '**/node_modules/**', '**/dist/**'],
},
});
import { defineProject, type UserProject } from 'vitest/config';
const config: UserProject = defineProject({
test: {
testTimeout: 15000,
include: ['test/**/*.test.ts'],
exclude: ['**/test/fixtures/**', '**/node_modules/**', '**/dist/**'],
},
});
export default config;
🤖 Prompt for AI Agents
In packages/co-busboy/vitest.config.ts lines 1-9, the default export lacks an
explicit typed intermediate variable; create a const (e.g., const config:
UserConfig) with an explicit type annotation imported as a type from Vitest
(import type { UserConfig } from 'vitest' or 'vitest/config'), assign the
existing object to that const, and then pass that typed const into defineProject
and export it as the default export to satisfy the typing guideline and improve
IDE/type-checking support.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR ports the co-busboy library from JavaScript to TypeScript as a new monorepo package @eggjs/co-busboy. The implementation replaces the deprecated chan library with a native Promise-based queue, adds comprehensive TypeScript type definitions, and migrates all tests from Mocha to Vitest.

Key changes include:

  • New TypeScript-based Promise queue implementation replacing the chan dependency
  • Comprehensive type definitions for Parts, FileStream, FieldTuple, and configuration options
  • Full test suite migration to Vitest with 22 passing tests covering all functionality

Reviewed changes

Copilot reviewed 12 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tsconfig.json Adds co-busboy package reference to project configuration
pnpm-workspace.yaml Adds required dependencies (@types/busboy, busboy, inflation, black-hole-stream) to catalog
pnpm-lock.yaml Lock file updates for new dependencies
packages/co-busboy/package.json Package configuration with ESM exports and Node.js >=22.18.0 requirement
packages/co-busboy/tsconfig.json TypeScript configuration extending root config
packages/co-busboy/vitest.config.ts Vitest test configuration
packages/co-busboy/src/index.ts Main implementation with PromiseQueue and parse function
packages/co-busboy/test/index.test.ts Comprehensive test suite (22 tests) covering parsing, compression, validation, and error handling
packages/co-busboy/README.md API documentation and usage examples
packages/co-busboy/CHANGELOG.md Initial release changelog
packages/co-busboy/LICENSE MIT license
packages/co-busboy/.gitignore Standard ignores for dist and coverage
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported
Comment on lines +131 to +144
if (this.finished) return;

// If item is null, mark as finished
if (item === null) {
this.finished = true;
}

const resolver = this.pendingResolvers.shift();
if (resolver) {
if (item instanceof Error) {
resolver.reject(item);
} else {
resolver.resolve(item);
}
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The PromiseQueue implementation has a potential memory leak. When an error is pushed to the queue after the queue is marked as finished, pending resolvers are never cleared. If push(null) is called to mark the queue as finished, followed by push(error), the error will be ignored (due to the early return on line 123), but any pending resolvers waiting for data will never be resolved or rejected, causing a memory leak.

This can occur if multiple errors are encountered during stream processing or if errors arrive after the stream finishes. Consider removing the early return or handling errors even when finished is true to ensure all pending resolvers are properly cleaned up.

Suggested change
if (this.finished) return;
// If item is null, mark as finished
if (item === null) {
this.finished = true;
}
const resolver = this.pendingResolvers.shift();
if (resolver) {
if (item instanceof Error) {
resolver.reject(item);
} else {
resolver.resolve(item);
}
// Handle terminal signals even if we're already marked as finished
if (item === null) {
// Normal completion: mark finished and resolve all pending pulls with null
this.finished = true;
while (this.pendingResolvers.length > 0) {
const { resolve } = this.pendingResolvers.shift()!;
resolve(null);
}
return;
}
if (item instanceof Error) {
// Error completion: mark finished and reject all pending pulls
this.finished = true;
while (this.pendingResolvers.length > 0) {
const { reject } = this.pendingResolvers.shift()!;
reject(item);
}
return;
}
// Non-terminal item; ignore if we've already finished
if (this.finished) return;
const resolver = this.pendingResolvers.shift();
if (resolver) {
resolver.resolve(item);
Copilot uses AI. Check for mistakes.
// It's a file stream with additional properties
console.log('File:', part.filename, part.mimeType);
// Consume the stream
part.pipe(fs.createWriteStream(`./uploads/${part.filename}`));
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

In the usage example, the code writes uploaded files directly to ./uploads/${part.filename}, which uses the attacker-controlled filename value to construct a filesystem path. An attacker can supply values like ../../../../etc/passwd as filename to perform path traversal and overwrite or create arbitrary files outside the intended upload directory. To avoid this, normalize and strictly validate or replace the original filename before using it in a filesystem path, and join it to a fixed upload directory in a way that prevents escaping that directory.

Copilot uses AI. Check for mistakes.
- Update multipart plugin to use the new @eggjs/co-busboy package
- Import Part type for proper type narrowing
- Add truncated property to FileStream interface
- Fix type assertions for AsyncIterable return

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@socket-security
Copy link

socket-security bot commented Dec 30, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​types/​mocha@​10.0.101001007780100

View full report

@fengmk2 fengmk2 marked this pull request as draft December 30, 2025 07:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants