Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adborroto/PLAT 5 Convert to Typescript #367

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open

Conversation

adborroto
Copy link

@adborroto adborroto commented Jan 3, 2025

Note - Since this is a public repository, make sure that we're not publishing private data in the code, commit comments, or this PR.

Note for reviewers - Please add a 2nd reviewer if the PR affects more than 15 files or 100 lines (not counting
package-lock.json), if it incurs significant risk, or if it is going through a 2nd review+fix cycle.

📚 Context/Description Behind The Change

This pull request converts the entire project from JavaScript to TypeScript. The conversion includes adding type annotations, interfaces, and updating the build process to compile TypeScript files.
Motivation and Context:
The conversion to TypeScript enhances type safety, improves developer tooling, and helps prevent runtime errors by catching issues at compile time. This change is part of our ongoing effort to modernize the codebase and improve maintainability.

Types of Changes:

  • JS to TS
  • No new features were added
  • Increase code coverage
  • My change do not require a change to the documentation.
  • All new and existing tests passed for mongoist and native
  • Babel support removed

🚨 Potential Risks & What To Monitor After Deployment

🧑‍🔬 How Has This Been Tested?

  • All existing tests have been updated to TypeScript and run successfully.
  • Added more test over other features that were not fully tested.
  • Manual testing of key features to ensure functionality remains intact.
  • Linting and type-checking have been performed to ensure code quality.

🚚 Release Plan

Merge and run npm run semantic-release over master.

@atlantis-automation
Copy link

Plan Error

running git clone --depth=1 --branch adborroto/PLAT-5 --single-branch https://github.com/mixmaxhq/mongo-cursor-pagination.git /home/atlantis/.atlantis/repos/mixmaxhq/mongo-cursor-pagination/367/default: Cloning into '/home/atlantis/.atlantis/repos/mixmaxhq/mongo-cursor-pagination/367/default'...
fatal: cannot create directory at 'src': No space left on device
warning: Clone succeeded, but checkout failed.
You can inspect what was checked out with 'git status'
and retry with 'git restore --source=HEAD :/'

: exit status 128

Copy link

coderabbitai bot commented Jan 3, 2025

Walkthrough

This pull request represents a comprehensive migration of the project from JavaScript to TypeScript. The changes span across multiple files and involve several key transformations:

  1. Configuration Updates:

    • Removed Babel configuration (.babelrc).
    • Updated .gitignore to ignore the entire dist/ directory.
    • Updated Node.js version in .nvmrc from 18.18.2 to 18.20.0.
    • Added TypeScript configuration files (tsconfig.json, tsconfig-lint.json).
  2. Dependency and Tooling Changes:

    • Removed Babel-related dependencies.
    • Added TypeScript and related type definition packages.
    • Updated Jest configuration to support TypeScript testing.
    • Modified package scripts to incorporate TypeScript compilation and linting.
  3. Source Code Transformation:

    • Converted all JavaScript files (.js) in src/ to TypeScript files (.ts).
    • Introduced type definitions and interfaces.
    • Refactored utility functions and core modules to leverage TypeScript's type system.
    • Maintained existing logic while enhancing type safety.
  4. Test Suite Adaptation:

    • Migrated test files from CommonJS to ES module syntax.
    • Updated import statements.
    • Added type annotations.
    • Preserved existing test logic and coverage.

The migration aims to improve type safety, developer experience, and maintainability of the project by leveraging TypeScript's static typing and enhanced tooling support.


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@adborroto adborroto changed the title adborroto/PLAT 5 adborroto/PLAT 5 Convert to Typescript Jan 7, 2025
@adborroto adborroto requested review from raphaelbs and removed request for raphaelbs January 7, 2025 11:22
@adborroto
Copy link
Author

@coderabbitai review

Copy link

coderabbitai bot commented Jan 7, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@adborroto adborroto self-assigned this Jan 7, 2025
@adborroto adborroto marked this pull request as ready for review January 7, 2025 11:24
@adborroto
Copy link
Author

@coderabbitai full review

Copy link

coderabbitai bot commented Jan 7, 2025

✅ Actions performed

Full review triggered.

Copy link

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

🧹 Nitpick comments (19)
src/utils/resolveFields.ts (2)

Line range hint 50-60: Leverage TypeScript's Type System Instead of Runtime Type Checks

Since TypeScript provides static type checking, you can remove the runtime type checks using Array.isArray and _.isObject. This simplification relies on TypeScript to enforce the correct types at compile time.

Apply this diff to remove the runtime checks:

-function resolveFields(
-  desiredFields?: string[] | null,
-  allowedFields?: Record<string, any> | null,
-  overrideFields?: Record<string, any> | null
-) {
-  if (desiredFields != null && !Array.isArray(desiredFields)) {
-    throw new TypeError('expected nullable array for desiredFields');
-  }
-
-  if (allowedFields != null && !_.isObject(allowedFields)) {
-    throw new TypeError('expected nullable plain object for allowedFields');
-  }
-
-  if (overrideFields !== undefined && !_.isObject(overrideFields)) {
-    throw new TypeError('expected optional plain object for overrideFields');
-  }
+function resolveFields(
+  desiredFields?: string[] | null,
+  allowedFields?: Record<string, any> | null,
+  overrideFields?: Record<string, any> | null
+) {

Line range hint 19-33: Replace _.reduce with Native JavaScript Object.entries

To reduce dependencies and improve performance, consider replacing _.reduce with Object.entries and the native Array.prototype.reduce method.

Apply this diff to make the change:

-const fields = _.reduce(
-  projection,
-  (memo, value, key) => {
+const fields = Object.entries(projection).reduce(
+  (memo, [key, value]) => {
      if (key !== '_id' && value !== undefined && !value) {
        throw new TypeError('projection includes exclusion, but we do not support that');
      }
      if (value || (key === '_id' && value === undefined && includeIdDefault)) {
        memo.push(key);
      }

      return memo;
    },
    []
  );
src/config.ts (1)

15-15: Consider using a more specific type for COLLATION

Using Record<string, any> reduces TypeScript's type safety benefits. Consider defining a more specific interface for the collation structure if possible.

-  COLLATION: Record<string, any>;
+  COLLATION: {
+    locale?: string;
+    caseLevel?: boolean;
+    strength?: number;
+    // Add other specific collation options
+  } | null;
src/index.ts (1)

1-9: Consider organizing imports by category

Group related imports together for better maintainability:

  1. Core functionality
  2. Plugins
  3. Utils
+// Core functionality
 import aggregate from './aggregate';
 import config from './config';
 import find from './find';
 import findWithReq from './findWithReq';
-import mongoosePlugin from './mongoose.plugin';
 import search from './search';
+
+// Plugins
+import mongoosePlugin from './mongoose.plugin';
+
+// Utils
 import { encodePaginationTokens } from './utils/query';
 import sanitizeQuery from './utils/sanitizeQuery';
src/utils/getPropertyViaDotNotation.ts (2)

15-15: Avoid using any type

Using any bypasses TypeScript's type checking. Consider using a more specific type.

-  let prop: any = object;
+  let prop: unknown = object;

9-12: Consider improving return type specificity

The current return type unknown could be more specific based on the input type T.

-): unknown {
+): T[keyof T] | undefined {
test/support/db.ts (2)

11-11: Type the environment variable

Consider using a const assertion or type guard for the driver version.

-const driver_version = process.env.DRIVER_VERSION;
+const driver_version = process.env.DRIVER_VERSION as 'v3' | undefined;

18-18: Type the driver parameter more strictly

The driver parameter could have a more specific type.

-async function db(mongod: MongoMemoryServer, driver: string | null = null): Promise<DbResponse> {
+async function db(mongod: MongoMemoryServer, driver: 'mongoist' | null = null): Promise<DbResponse> {
src/utils/bsonUrlEncoding.ts (1)

8-8: Consider using a more specific type for BSON_UNDEFINED

The as const assertion is good, but the type could be more specific to prevent potential conflicts.

-const BSON_UNDEFINED = '__mixmax__undefined__' as const;
+const BSON_UNDEFINED = '__mixmax__undefined__' as '__mixmax__undefined__';
-export type Encodable = Record<string, unknown> | unknown[] | null | string | number | boolean;
+export type Encodable = Record<string, unknown> | unknown[] | null | string | number | boolean | undefined;

Also applies to: 13-13

src/findWithReq.ts (1)

23-27: Consider adding request query type safety

The function could benefit from a more specific type for the expected query parameters.

+interface FindRequestQuery {
+  limit?: string;
+  next?: string;
+  previous?: string;
+  fields?: string;
+}

 export default async function findWithReq(
-  req: Request,
+  req: Request<any, any, any, FindRequestQuery>,
   collection: Collection<Document>,
   params: FindParams
 ): Promise<PaginationResponse<Document>> {
test/utils/bsonUrlEncoding.test.ts (1)

11-13: Use interface for test context

Define an interface for the test context object to improve type safety.

+interface TestContext {
+  db: typeof mongo | null;
+}

-  const t = {
-    db: null,
-  };
+  const t: TestContext = {
+    db: null,
+  };
src/mongoose.plugin.ts (1)

29-29: Avoid mutating input parameters

The functions modify the input parameters directly. Consider creating a copy instead.

-    params = _.extend({}, params);
+    const extendedParams = _.extend({}, params);
-    return find(this.collection, params);
+    return find(this.collection, extendedParams);

-    params = _.extend({}, params);
+    const extendedParams = _.extend({}, params);
-    return search(this.collection, searchString, params);
+    return search(this.collection, searchString, extendedParams);

Also applies to: 49-49

test/utils/query.test.ts (2)

67-67: Improve test readability with meaningful test values

The test values '1' and '2' in the expected query are not representative of real data and make it hard to understand the test's intent.

Consider using more meaningful test values that reflect actual use cases:

-        $or: [{ name: { $lt: '1' } }, { name: null }, { _id: { $lt: '2' }, name: { $eq: '1' } }],
+        $or: [{ name: { $lt: 'Test' } }, { name: null }, { _id: { $lt: '123' }, name: { $eq: 'Test' } }],

51-70: Add more test cases for edge cases

The current test suite for generateCursorQuery only covers basic scenarios. Consider adding tests for:

  • Invalid cursor values
  • Empty string values
  • Special characters in field names
src/utils/sanitizeQuery.ts (2)

5-11: Consider stricter typing for the index signature

The current index signature [key: string]: any allows any value type, which reduces TypeScript's type safety benefits.

Consider using a more specific type or union type for the index signature value:

-  [key: string]: any;
+  [key: string]: string | string[] | undefined;

Line range hint 53-58: Add type guard for limit parsing

The limit parsing logic could be made more type-safe with a proper type guard.

Consider adding a type guard:

-  if (!_.isEmpty(query.limit)) {
+  if (typeof query.limit === 'string' && query.limit.length > 0) {
     const limit = parseInt(query.limit, 10);
     // Don't let the user specify a higher limit than params.limit, if defined.
     if (!isNaN(limit) && (!params.limit || params.limit > limit)) {
       params.limit = limit;
     }
   }
test/search.test.ts (1)

9-12: Add type definition for test context object

Consider defining an interface for the test context object to improve type safety.

interface TestContext {
  db: typeof import('mongodb').Db | null;
}

const t: TestContext = {
  db: null,
};
README.md (2)

33-36: Enhance TypeScript migration documentation.

While the new section announces the TypeScript conversion, consider adding:

  1. Breaking changes (if any)
  2. Migration guide for existing JavaScript users
  3. Examples of using TypeScript types and interfaces
  4. Location of type definitions for TypeScript users

33-36: Add TypeScript code examples.

Consider updating the code examples in the documentation to TypeScript to:

  1. Show proper type annotations
  2. Demonstrate type safety benefits
  3. Help users understand how to use the library with TypeScript
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d29a49e and 54a54b7.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (43)
  • .babelrc (0 hunks)
  • .gitignore (1 hunks)
  • .nvmrc (1 hunks)
  • README.md (1 hunks)
  • jest.config.js (1 hunks)
  • package.json (4 hunks)
  • src/aggregate.js (0 hunks)
  • src/aggregate.ts (1 hunks)
  • src/config.js (0 hunks)
  • src/config.ts (1 hunks)
  • src/find.js (0 hunks)
  • src/find.ts (1 hunks)
  • src/findWithReq.js (0 hunks)
  • src/findWithReq.ts (1 hunks)
  • src/index.js (0 hunks)
  • src/index.ts (1 hunks)
  • src/mongoose.plugin.js (0 hunks)
  • src/mongoose.plugin.ts (1 hunks)
  • src/search.js (0 hunks)
  • src/search.ts (1 hunks)
  • src/utils/bsonUrlEncoding.js (0 hunks)
  • src/utils/bsonUrlEncoding.ts (1 hunks)
  • src/utils/getPropertyViaDotNotation.js (0 hunks)
  • src/utils/getPropertyViaDotNotation.ts (1 hunks)
  • src/utils/query.js (0 hunks)
  • src/utils/query.ts (1 hunks)
  • src/utils/resolveFields.ts (3 hunks)
  • src/utils/sanitizeParams.ts (4 hunks)
  • src/utils/sanitizeQuery.ts (3 hunks)
  • test/.eslintrc.json (0 hunks)
  • test/aggregate.test.ts (1 hunks)
  • test/find.test.ts (1 hunks)
  • test/findWithReq.test.ts (18 hunks)
  • test/mongoosePlugin.test.ts (4 hunks)
  • test/search.test.ts (1 hunks)
  • test/support/db.js (0 hunks)
  • test/support/db.ts (1 hunks)
  • test/utils/bsonUrlEncoding.test.ts (2 hunks)
  • test/utils/query.test.js (0 hunks)
  • test/utils/query.test.ts (1 hunks)
  • test/utils/resolveFields.test.ts (1 hunks)
  • tsconfig-lint.json (1 hunks)
  • tsconfig.json (1 hunks)
💤 Files with no reviewable changes (14)
  • .babelrc
  • test/.eslintrc.json
  • src/aggregate.js
  • src/config.js
  • src/find.js
  • src/index.js
  • src/utils/getPropertyViaDotNotation.js
  • test/utils/query.test.js
  • src/utils/bsonUrlEncoding.js
  • src/search.js
  • src/mongoose.plugin.js
  • test/support/db.js
  • src/utils/query.js
  • src/findWithReq.js
✅ Files skipped from review due to trivial changes (5)
  • .nvmrc
  • .gitignore
  • tsconfig-lint.json
  • test/utils/resolveFields.test.ts
  • tsconfig.json
🧰 Additional context used
🪛 Biome (1.9.4)
test/mongoosePlugin.test.ts

[error] 70-70: Do not shadow the global "hasOwnProperty" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)


[error] 91-91: Do not shadow the global "hasOwnProperty" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

🔇 Additional comments (9)
jest.config.js (1)

9-9: Verify Impact of Removing moduleNameMapper Configuration

The moduleNameMapper section, which previously handled module path mappings, has been removed. If any tests depended on this mapping, it could lead to module resolution errors.

Please ensure that removing moduleNameMapper does not affect the ability of Jest to resolve modules, especially if there are custom module paths.

test/search.test.ts (1)

1-4: LGTM! Clean ES module imports

The conversion from CommonJS to ES module imports is done correctly, with proper TypeScript type imports.

test/aggregate.test.ts (2)

1-4: LGTM! Consistent import pattern

The import statements follow the same pattern as other test files, maintaining consistency across the codebase.


10-12: Maintain consistent type definitions across test files

For consistency, use the same TestContext interface suggested for search.test.ts.

test/find.test.ts (2)

10-12: Maintain consistent type definitions across test files

For consistency, use the same TestContext interface suggested for search.test.ts.


4-4: Verify the impact of switching ObjectId import source

The change from mongoist to mongodb for ObjectId import is correct for TypeScript, but verify that it maintains the same functionality.

✅ Verification successful

Import change from mongoist to mongodb ObjectId is correct

The ObjectId is only used to generate string IDs via toString(), which is standard functionality supported by both packages. The change is safe and maintains the same behavior.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any remaining mongoist ObjectId usage
rg "require.*mongoist.*ObjectId" || rg "from.*mongoist.*ObjectId"

# Check if mongodb ObjectId is used consistently
rg "ObjectId.*mongodb" -A 2

Length of output: 222


Script:

#!/bin/bash
# Check how ObjectId is used in the test file
rg "ObjectId" test/find.test.ts -A 2

Length of output: 1032

package.json (3)

14-20: LGTM! Build and test scripts are properly configured for TypeScript.

The scripts section has been correctly updated to support TypeScript:

  • Lint script includes TypeScript type checking
  • Build script properly cleans and compiles TypeScript
  • Test scripts are well-organized for different drivers

55-59: LGTM! TypeScript tooling is properly configured.

All necessary TypeScript development dependencies are present with appropriate versions:

  • TypeScript compiler and configuration
  • ESLint TypeScript plugins
  • ts-jest for TypeScript testing

Also applies to: 71-73


42-42: 🛠️ Refactor suggestion

Consider using a stable version of @types/express.

The current version ^5.0.0 is a pre-release version which could introduce instability. Consider using the stable version ^4.17.21 instead.

src/aggregate.ts Outdated Show resolved Hide resolved
src/search.ts Outdated Show resolved Hide resolved
src/search.ts Show resolved Hide resolved
src/find.ts Outdated Show resolved Hide resolved
src/find.ts Show resolved Hide resolved
test/mongoosePlugin.test.ts Show resolved Hide resolved
test/mongoosePlugin.test.ts Show resolved Hide resolved
src/utils/sanitizeParams.ts Outdated Show resolved Hide resolved
src/utils/sanitizeParams.ts Show resolved Hide resolved
test/findWithReq.test.ts Show resolved Hide resolved
Copy link

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

♻️ Duplicate comments (15)
src/utils/bsonUrlEncoding.ts (2)

32-34: ⚠️ Potential issue

Handle all undefined values in arrays

The current implementation replaces undefined only at the first index of arrays. This could miss undefined values at other positions, leading to potential encoding issues.

Apply this diff to replace all occurrences of undefined in arrays:

- if (Array.isArray(obj) && obj[0] === undefined) {
-   obj[0] = BSON_UNDEFINED;
+ if (Array.isArray(obj)) {
+   for (let i = 0; i < obj.length; i++) {
+     if (obj[i] === undefined) {
+       obj[i] = BSON_UNDEFINED;
+     }
+   }
 }

47-49: ⚠️ Potential issue

Handle all BSON_UNDEFINED values in arrays during decoding

Similar to the encode method, the decode method only checks the first element of arrays for BSON_UNDEFINED. This could miss BSON_UNDEFINED values at other positions.

Apply this diff to replace all occurrences of BSON_UNDEFINED in arrays:

- if (Array.isArray(obj) && obj[0] === BSON_UNDEFINED) {
-   obj[0] = undefined;
+ if (Array.isArray(obj)) {
+   for (let i = 0; i < obj.length; i++) {
+     if (obj[i] === BSON_UNDEFINED) {
+       obj[i] = undefined;
+     }
+   }
 }
src/aggregate.ts (1)

46-46: ⚠️ Potential issue

Fix potential error when spreading undefined options

If params.options is undefined, spreading it will cause a TypeError. To prevent this, default params.options to an empty object before spreading.

Apply this diff to fix the issue:

- const options: Record<string, any> = { ...params.options };
+ const options: Record<string, any> = { ...(params.options || {}) };
src/search.ts (3)

6-11: ⚠️ Potential issue

Update SearchParams.next type to handle decoded cursor correctly

The next parameter in SearchParams is currently typed as string, but after decoding, it becomes a tuple [number, any]. This mismatch can lead to type errors and runtime issues when accessing params.next[0] and params.next[1].

Apply this diff to update the SearchParams interface:

 export interface SearchParams {
   query?: Record<string, any>;
   limit?: number;
   fields?: Record<string, number>;
-  next?: string;
+  next?: string | [number, any];
 }

38-39: ⚠️ Potential issue

Correct the type casting of params.next after decoding

After decoding params.next, it should be cast to [number, any] instead of string to reflect its actual structure and ensure type safety when accessing its elements.

Apply this diff to correct the casting:

   if (params.next) {
-    params.next = bsonUrlEncoding.decode(params.next) as string;
+    params.next = bsonUrlEncoding.decode(params.next) as [number, any];
   }

57-61: ⚠️ Potential issue

Prevent params.fields from overriding protected fields in $project stage

Directly spreading params.fields into the $project stage can allow overriding of critical fields like _id and score, which may lead to security risks or unintended behavior. To safeguard these fields, exclude them from params.fields before spreading.

Apply this diff to fix the issue:

       $project: {
-        ...params.fields,
         _id: 1,
         score: { $meta: 'textScore' },
+        ..._.omit(params.fields, ['_id', 'score']),
       },
src/find.ts (2)

14-20: ⚠️ Potential issue

Include hint property in FindParams interface

The hint property is used in the code (line 75) but is not defined in the FindParams interface. This can lead to TypeScript type errors and may cause issues during compilation.

Apply this diff to include the hint property:

 export interface FindParams extends PaginationParams {
   query?: Document;
   limit?: number;
   fields?: Record<string, number>;
   collation?: Record<string, any> | null;
   overrideFields?: Record<string, number>;
+  hint?: any;
 }

37-42: ⚠️ Potential issue

Await the asynchronous aggregate function call

The aggregate function is asynchronous but is not awaited. This means response will be a Promise instead of the expected result, which can lead to unexpected behavior when accessing response.

Apply this diff to fix the issue:

       response = aggregate(
+      response = await aggregate(
         collection,
         Object.assign({}, params, {
           aggregation: params.query ? [{ $match: params.query }] : [],
         })
       );
test/support/db.ts (1)

25-30: ⚠️ Potential issue

Improve error handling and type safety in client connection

The client connection logic needs better error handling and type assertions.

-  const clientToConnect = driver_version === 'v3' ? MongoClientV3 : MongoClient;
-  const [client, dbName] = await Promise.all([clientToConnect.connect(uri), mongod.getDbName()]);
-  return {
-    db: client.db(dbName),
-    client,
-  };
+  const clientToConnect = driver_version === 'v3' ? MongoClientV3 : MongoClient;
+  try {
+    const [client, dbName] = await Promise.all([
+      clientToConnect.connect(uri),
+      mongod.getDbName(),
+    ]) as [MongoClient | MongoClientV3, string];
+    return {
+      db: client.db(dbName),
+      client,
+    };
+  } catch (error) {
+    throw new Error(`Failed to connect to MongoDB: ${error instanceof Error ? error.message : 'Unknown error'}`);
+  }
src/findWithReq.ts (1)

28-32: ⚠️ Potential issue

Add input validation for query parameters

The function should validate query parameters before processing.

+  // Validate query parameters before sanitizing
+  if (req.query.limit && isNaN(Number(req.query.limit))) {
+    throw new Error('Invalid limit parameter: must be a number');
+  }
+
   // Sanitize the query string parameters and merge with additional params
   const sanitizedParams = sanitizeQuery(req.query, params);
 
   // Perform the find operation with the sanitized parameters
   return find(collection, sanitizedParams);
test/utils/bsonUrlEncoding.test.ts (1)

39-39: 🛠️ Refactor suggestion

Replace any type with a proper interface

Using any defeats TypeScript's type checking benefits.

-    const decoded: any = bsonUrlEncoding.decode(str);
+    interface DecodedObject {
+      _id: mongo.ObjectID;
+      date: Date;
+      number: number;
+      string: string;
+    }
+    const decoded = bsonUrlEncoding.decode(str) as DecodedObject;
src/mongoose.plugin.ts (2)

24-31: 🛠️ Refactor suggestion

Improve type safety in findFn implementation

The function should use proper typing for the Mongoose model context and return value.

-  const findFn = async function(this: any, params: FindParams): Promise<any> {
+  const findFn = async function<T>(this: Model<T>, params: FindParams): Promise<PaginationResponse<T>> {
     if (!this.collection) {
       throw new Error('collection property not found');
     }
 
     params = _.extend({}, params);
     return find(this.collection, params);
   };

40-51: 🛠️ Refactor suggestion

Improve type safety in searchFn implementation

The search function needs proper typing for the this context and return type.

   const searchFn = async function(
-    this: any,
+    this: Model<T>,
     searchString: string,
     params: SearchParams
-  ): Promise<any> {
+  ): Promise<PaginationResponse<T>> {
     if (!this.collection) {
       throw new Error('collection property not found');
     }
 
     params = _.extend({}, params);
     return search(this.collection, searchString, params);
   };
test/mongoosePlugin.test.ts (2)

1-3: 🛠️ Refactor suggestion

Proper type definitions needed for plugin methods

While the ES module imports are correct, the file needs proper type definitions for the plugin methods to avoid as any casts throughout the tests.

Consider adding type definitions as suggested in the previous review:

declare module 'mongoose' {
  interface Model<T> {
    paginate(): Promise<PaginationResult<T>>;
    paginateFN(): Promise<PaginationResult<T>>;
    search(query: string, options?: SearchOptions): Promise<SearchResult<T>>;
    searchFN(query: string, options?: SearchOptions): Promise<SearchResult<T>>;
  }
}

70-70: ⚠️ Potential issue

Fix shadowing of global hasOwnProperty

The variable hasOwnProperty shadows the global property of the same name.

Apply this fix:

-    var hasOwnProperty = Object.prototype.hasOwnProperty;
+    const hasOwn = Object.prototype.hasOwnProperty;
-    expect(hasOwnProperty.call(data, 'results')).toBe(true);
+    expect(hasOwn.call(data, 'results')).toBe(true);

Also applies to: 91-91

🧰 Tools
🪛 Biome (1.9.4)

[error] 70-70: Do not shadow the global "hasOwnProperty" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

🧹 Nitpick comments (10)
src/utils/sanitizeQuery.ts (1)

52-52: Remove redundant assignment of params

The variable params already has a default value {} in the function signature. The assignment params = params || {}; is unnecessary and can be removed for cleaner code.

Apply this diff to remove the redundant line:

-   params = params || {};
src/search.ts (1)

46-47: Remove unnecessary non-null assertions on params.limit

The non-null assertion operator ! is unnecessary here because params.limit has been defaulted earlier. Removing ! makes the code cleaner and avoids potential confusion.

Apply this diff to simplify the code:

-   if (params.limit! < 1) params.limit = 1;
-   if (params.limit! > config.MAX_LIMIT) params.limit = config.MAX_LIMIT;
+   if (params.limit < 1) params.limit = 1;
+   if (params.limit > config.MAX_LIMIT) params.limit = config.MAX_LIMIT;
jest.config.js (3)

3-5: Simplify Configuration by Removing Redundant transform Property

When using the ts-jest preset, the transform configuration is set automatically. You can remove the transform property to simplify the configuration.

Apply this diff:

 module.exports = {
   preset: 'ts-jest',
-  transform: {
-    '^.+\\.tsx?$': 'ts-jest',
-  },
   moduleFileExtensions: ['ts', 'js', 'json', 'node'],

6-6: Include 'tsx' in moduleFileExtensions

If your project includes .tsx files (e.g., React components), consider adding 'tsx' to the moduleFileExtensions array to ensure Jest recognizes these files.

Apply this diff:

 moduleFileExtensions: ['ts', 'js', 'json', 'node'],
+moduleFileExtensions: ['ts', 'tsx', 'js', 'json', 'node'],

8-8: Update collectCoverageFrom to Include .tsx Files

To include coverage data for .tsx files, update the collectCoverageFrom pattern.

Apply this diff:

-collectCoverageFrom: ['src/**/*.ts'],
+collectCoverageFrom: ['src/**/*.{ts,tsx}'],
test/support/db.ts (1)

11-11: Consider using a typed environment variable

The driver_version variable could benefit from type safety using a union type of allowed values.

-const driver_version = process.env.DRIVER_VERSION;
+const driver_version = process.env.DRIVER_VERSION as 'v3' | undefined;
test/find.test.ts (1)

10-12: Consider adding type annotations to the test context

While the test setup works, it would be better to add proper TypeScript types to the test context object.

-  const t = {
-    db: null,
-  };
+  interface TestContext {
+    db: any; // or proper MongoDB type
+  }
+  const t: TestContext = {
+    db: null,
+  };
tsconfig-lint.json (2)

2-5: Consider adding strict type checking options

While extending the base config is good, consider explicitly enabling strict type checking options for better type safety.

{
  "extends": "@mixmaxhq/ts-config/tsconfig.json",
  "compilerOptions": {
-    "outDir": "dist/node"
+    "outDir": "dist/node",
+    "strict": true,
+    "noImplicitAny": true,
+    "strictNullChecks": true
  },

6-13: Consider adding more specific exclude patterns

The current exclude patterns are good, but consider adding more specific patterns to exclude test fixtures and generated files.

  "exclude": [
    "node_modules",
-    "dist"
+    "dist",
+    "coverage",
+    "**/*.spec.ts",
+    "**/*.mock.ts",
+    "**/__fixtures__/*"
  ]
test/utils/resolveFields.test.ts (1)

Line range hint 1-74: Consider enhancing tests with TypeScript features.

While the migration is correct, consider enhancing the tests with TypeScript features:

  • Add type annotations for test inputs and expected outputs
  • Use TypeScript's type or interface for complex test data structures
  • Leverage TypeScript's strict type checking in assertions
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d29a49e and 54a54b7.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (43)
  • .babelrc (0 hunks)
  • .gitignore (1 hunks)
  • .nvmrc (1 hunks)
  • README.md (1 hunks)
  • jest.config.js (1 hunks)
  • package.json (4 hunks)
  • src/aggregate.js (0 hunks)
  • src/aggregate.ts (1 hunks)
  • src/config.js (0 hunks)
  • src/config.ts (1 hunks)
  • src/find.js (0 hunks)
  • src/find.ts (1 hunks)
  • src/findWithReq.js (0 hunks)
  • src/findWithReq.ts (1 hunks)
  • src/index.js (0 hunks)
  • src/index.ts (1 hunks)
  • src/mongoose.plugin.js (0 hunks)
  • src/mongoose.plugin.ts (1 hunks)
  • src/search.js (0 hunks)
  • src/search.ts (1 hunks)
  • src/utils/bsonUrlEncoding.js (0 hunks)
  • src/utils/bsonUrlEncoding.ts (1 hunks)
  • src/utils/getPropertyViaDotNotation.js (0 hunks)
  • src/utils/getPropertyViaDotNotation.ts (1 hunks)
  • src/utils/query.js (0 hunks)
  • src/utils/query.ts (1 hunks)
  • src/utils/resolveFields.ts (3 hunks)
  • src/utils/sanitizeParams.ts (4 hunks)
  • src/utils/sanitizeQuery.ts (3 hunks)
  • test/.eslintrc.json (0 hunks)
  • test/aggregate.test.ts (1 hunks)
  • test/find.test.ts (1 hunks)
  • test/findWithReq.test.ts (18 hunks)
  • test/mongoosePlugin.test.ts (4 hunks)
  • test/search.test.ts (1 hunks)
  • test/support/db.js (0 hunks)
  • test/support/db.ts (1 hunks)
  • test/utils/bsonUrlEncoding.test.ts (2 hunks)
  • test/utils/query.test.js (0 hunks)
  • test/utils/query.test.ts (1 hunks)
  • test/utils/resolveFields.test.ts (1 hunks)
  • tsconfig-lint.json (1 hunks)
  • tsconfig.json (1 hunks)
💤 Files with no reviewable changes (14)
  • test/.eslintrc.json
  • .babelrc
  • src/utils/bsonUrlEncoding.js
  • src/utils/getPropertyViaDotNotation.js
  • src/config.js
  • src/find.js
  • src/aggregate.js
  • src/search.js
  • src/index.js
  • test/support/db.js
  • src/mongoose.plugin.js
  • src/utils/query.js
  • src/findWithReq.js
  • test/utils/query.test.js
🧰 Additional context used
🪛 Biome (1.9.4)
test/mongoosePlugin.test.ts

[error] 70-70: Do not shadow the global "hasOwnProperty" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)


[error] 91-91: Do not shadow the global "hasOwnProperty" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

🔇 Additional comments (31)
src/search.ts (1)

86-87: Verify the existence of aggregateAsCursor method on the Collection type

The check 'aggregateAsCursor' in collection assumes that the collection object may have an aggregateAsCursor method. Please verify that this method exists on the Collection<T> type provided by the MongoDB driver. If it does not, this could lead to a runtime error.

Since my knowledge cutoff is in 2023-10, please confirm whether aggregateAsCursor is a valid method. If it's a custom implementation or from a newer version of the driver, ensure it is correctly typed and handled.

src/utils/resolveFields.ts (2)

13-15: Refine type definitions for projection parameter

Using Record<string, any> for the projection parameter reduces type safety, as it allows any values. Consider defining a more specific type to leverage TypeScript's type system effectively.

You can define a type that specifies the allowed values for projection. For example, if projection should map field names to booleans:

type Projection = Record<string, boolean>;

function fieldsFromMongo(
  projection: Projection = {},
  includeIdDefault = false
): ProjectionFieldSet {
  // function body
}

47-49: Improve type safety for allowedFields and overrideFields parameters

Currently, allowedFields and overrideFields are typed as Record<string, any>, which can reduce type safety. Defining more precise types can help catch errors at compile time and make the code more robust.

Consider specifying the expected value types. For example:

type FieldsMap = Record<string, boolean>;

function resolveFields(
  desiredFields?: string[] | null,
  allowedFields?: FieldsMap | null,
  overrideFields?: FieldsMap | null
): Record<string, any> | null {
  // function body
}
src/utils/query.ts (4)

45-45: Handle Undefined paginatedField When Accessing Object Properties

Since params.paginatedField is optional, accessing it without checking may lead to runtime errors. Ensure that paginatedField is defined before using it with objectPath.get.

Consider adding a validation check:

 export function encodePaginationTokens<T>(
   params: PaginationParams,
   response: PaginationResponse<T>,
   previous: T | null,
   next: T | null
 ): void {
+  if (!params.paginatedField) {
+    throw new Error('`paginatedField` is required for pagination tokens');
+  }
   const shouldSecondarySortOnId = params.paginatedField !== '_id';

55-55: Handle Undefined paginatedField When Accessing Object Properties

As with the previous occurrence, ensure that params.paginatedField is defined before using it with objectPath.get to prevent runtime errors.


107-107: Default Optional Boolean Parameter sortAscending

params.sortAscending is optional and may be undefined, which can cause logical inconsistencies. Default this parameter to a boolean value to ensure consistent behavior in sorting.

Apply this diff to set a default value:

 export function generateSort(params: PaginationParams): SortObject {
+  const sortAscending = params.sortAscending ?? true;
   const sortAsc =
-    (!params.sortAscending && params.previous) || (params.sortAscending && !params.previous);
+    (!sortAscending && params.previous) || (sortAscending && !params.previous);

124-214: Validate paginatedField in generateCursorQuery Function

The generateCursorQuery function assumes params.paginatedField is defined. If it's undefined, it may lead to incorrect queries or runtime errors. Validate paginatedField before proceeding.

Consider adding a validation check:

 export function generateCursorQuery(params: PaginationParams): CursorQuery {
+  if (!params.paginatedField) {
+    throw new Error('`paginatedField` is required to generate cursor query');
+  }
   if (!params.next && !params.previous) return {};

   const sortAsc =
     (!params.sortAscending && params.previous) || (params.sortAscending && !params.previous);
src/utils/getPropertyViaDotNotation.ts (1)

13-13: Add Validation for propertyName Parameter

The function should validate that propertyName is a non-empty string to prevent unexpected errors when splitting or accessing properties.

Apply this diff:

 export default function getPropertyViaDotNotation<T extends Record<string, any>>(
   propertyName: string,
   object: T
 ): unknown {
+  if (!propertyName || typeof propertyName !== 'string') {
+    throw new Error('`propertyName` must be a non-empty string');
+  }
   const parts = propertyName.split('.');
test/utils/query.test.ts (5)

8-28: LGTM! Well-structured test suite with proper type safety.

The test suite effectively uses TypeScript generics and type annotations to ensure type safety. The PaginatedType generic constraint and PaginationResponse type are used appropriately.


67-67: Verify the hardcoded test values.

The test uses hardcoded values '1' and '2' in the cursor query assertion. These values seem arbitrary and their significance is not clear from the test context.

Consider either:

  1. Adding comments to explain the significance of these values
  2. Using more meaningful test data that reflects real-world scenarios

17-17: LGTM! Correct usage of mongoose Schema.Types.ObjectId.

The type has been properly updated to use mongoose.Schema.Types.ObjectId which is the correct type for ObjectId references.


70-70: Fix shadowing of global hasOwnProperty.

The variable hasOwnProperty shadows the global property of the same name.

Apply this fix:

-    var hasOwnProperty = Object.prototype.hasOwnProperty;
+    const hasOwn = Object.prototype.hasOwnProperty;
-    expect(hasOwnProperty.call(data, 'results')).toBe(true);
+    expect(hasOwn.call(data, 'results')).toBe(true);

Also applies to: 91-91


59-59: Avoid using as any type assertions.

Multiple instances of as any type assertions reduce TypeScript's type safety benefits.

Consider creating proper type definitions for the mongoose plugin:

declare module 'mongoose' {
  interface Model<T> {
    paginate(): Promise<PaginationResult<T>>;
    paginateFN(): Promise<PaginationResult<T>>;
    search(query: string, options?: SearchOptions): Promise<SearchResult<T>>;
    searchFN(query: string, options?: SearchOptions): Promise<SearchResult<T>>;
  }
}

Also applies to: 64-64, 69-69, 80-80, 85-85, 90-90

test/search.test.ts (1)

1-11: LGTM! Clean conversion to TypeScript with improved type safety.

The changes effectively:

  1. Convert to ES module imports
  2. Add explicit db property initialization
  3. Maintain test structure and readability
test/findWithReq.test.ts (1)

73-78: Improve type safety by creating proper request mock objects.

Multiple instances of type assertions with as any and as Request reduce type safety.

Consider creating properly typed mock objects:

interface MockQuery {
  limit?: string;
  fields?: string;
  next?: string;
  previous?: string;
}

function createMockRequest(query: MockQuery): Request {
  return {
    query,
  } as unknown as Request;
}

// Usage:
const request = createMockRequest({
  limit: '2',
  fields: 'counter,myfield1',
});

Also applies to: 96-104, 120-121, 145-146, 168-169, 187-188, 207-208, 228-229, 252-253, 281-282, 305-306, 333-334, 360-361, 392-393, 421-422, 444-445, 477-478

test/find.test.ts (1)

1-4: Import statements look good but consider organizing them

The conversion to ES module syntax is correct. The imports are properly organized with external dependencies first, followed by internal imports.

.nvmrc (1)

1-1: Verify Node.js version compatibility

The update to Node.js 18.20.0 is good, as it's a stable LTS version with good TypeScript support. However, please verify that all dependencies are compatible with this version.

✅ Verification successful

Node.js 18.20.0 is compatible with all dependencies

All package dependencies have version requirements that are satisfied by Node.js 18.20.0. The highest minimum requirement found is Node.js 18, and there are no maximum version constraints that would conflict with this version.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any package.json engines field restrictions
if [ -f "package.json" ]; then
  echo "Checking engines field in package.json..."
  jq '.engines' package.json
fi

# Check for any dependency version conflicts
if [ -f "package-lock.json" ]; then
  echo "Checking for any dependency version conflicts..."
  jq '.packages | to_entries[] | select(.value.engines != null) | .value.engines' package-lock.json
fi

Length of output: 26851

tsconfig.json (1)

9-13: Verify test file exclusion pattern

The current pattern excludes all test files, but verify if you need to keep any specific test utilities or types.

✅ Verification successful

Test file exclusion pattern is correct

The current exclusion pattern is appropriate. The discovered test utility (test/support/db.ts) is specifically for test infrastructure and should be compiled separately with test-specific configuration, not with the main source code.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any test utility files that might need to be included
echo "Checking for test utility files that might need inclusion..."
fd -e ts -E "*.test.ts" -E "*.spec.ts" . test/

Length of output: 198


Script:

#!/bin/bash
# Check if db.ts is imported in source files
ast-grep --pattern 'import $_ from "$_/test/support/db"'

# Also check relative imports that might resolve to this file
ast-grep --pattern 'import $_ from "$_/db"'

# Check actual content of db.ts to understand its purpose
cat test/support/db.ts

Length of output: 1044

src/config.ts (2)

1-16: Well-structured interface with clear documentation.

The interface definition is clear and well-documented with JSDoc comments.


15-15: Type mismatch between interface and implementation.

The COLLATION property is defined as Record<string, any> but assigned null.

Also applies to: 21-21

src/index.ts (1)

1-19: Clean and well-organized module exports.

The file demonstrates good module organization with clear imports and a structured export block.

.gitignore (1)

43-43: Verify broader dist/ ignore pattern.

The change from dist/node to dist/ will ignore all compiled output. Please ensure this won't accidentally exclude any required distribution files.

✅ Verification successful

Broader dist/ ignore pattern is correct

The change from dist/node to dist/ is appropriate as:

  • All build output is generated in dist/ directory
  • dist/ is properly included in the published npm package
  • No other important files are stored in dist/
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any potentially important files in dist/ that might be accidentally ignored
fd . dist/ --type f --hidden --no-ignore

Length of output: 135


Script:

#!/bin/bash
# Check configuration files and docs for dist references
echo "=== Package.json ==="
fd package.json --type f --exec cat {}

echo -e "\n=== TypeScript Config ==="
fd tsconfig.json --type f --exec cat {}

echo -e "\n=== Build References ==="
rg -i "(\bdist\b|outDir|build|output)" -g "!.git" -g "!*.lock" -g "!dist/*" -g "!{package,tsconfig}.json"

echo -e "\n=== README Build Instructions ==="
fd "README" --type f --exec cat {}

Length of output: 18019

test/utils/resolveFields.test.ts (1)

1-1: Clean migration to ES module syntax.

The import statement has been properly converted to use ES module syntax.

package.json (1)

14-20: Well-structured TypeScript build and test scripts.

The scripts have been properly updated to support TypeScript:

  • Added TypeScript compilation check to lint script
  • Added clean build step with tsc
  • Maintained separate test configurations for different drivers
src/utils/sanitizeParams.ts (3)

1-16: LGTM! Well-structured interface definition.

The SanitizeParams interface is well-defined with appropriate types for MongoDB pagination parameters.


18-21: Use proper MongoDB types for better type safety.

The collection parameter is typed as any, which reduces type safety.


55-55: Add type guards for string operations.

Type assertions are used without first checking if the value is actually a string.

Also applies to: 80-80

README.md (1)

33-36: LGTM! Clear documentation update.

The new section effectively communicates the transition to TypeScript and its benefits.

test/aggregate.test.ts (2)

1-3: LGTM! Clean import syntax.

The conversion to ES module syntax is correct and consistent.


10-12: LGTM! Type-safe initialization.

The object initialization with explicit null assignment improves type safety.

test/mongoosePlugin.test.ts (1)

17-17: LGTM! Improved type clarity

Good change using the explicit mongoose.Schema.Types.ObjectId instead of the deprecated mongoose.Schema.ObjectId.

src/mongoose.plugin.ts Show resolved Hide resolved
tsconfig.json Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
Copy link

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

🔭 Outside diff range comments (2)
package.json (2)

Line range hint 75-77: Update Node.js engine requirement.

The current engine requirement (">= 6.9.1") is outdated for TypeScript support. Consider updating to match the TypeScript version's requirements.

  "engines": {
-    "node": ">= 6.9.1"
+    "node": ">= 14.17.0"
  },

Line range hint 6-8: Update files field for TypeScript declaration files.

Ensure TypeScript declaration files are included in the published package.

  "files": [
    "index.js",
    "dist",
-    "src"
+    "src",
+    "index.d.ts"
  ],
🧹 Nitpick comments (6)
tsconfig.json (1)

14-18: Consider separate test files configuration.

Since test files are excluded, consider creating a separate tsconfig.test.json for test files to ensure proper type checking during testing.

// tsconfig.test.json
{
  "extends": "./tsconfig.json",
  "include": [
    "test/**/*"
  ],
  "exclude": [
    "node_modules",
    "dist"
  ]
}
package.json (1)

14-14: Add type checking to the build script.

Consider adding type checking to the build process to catch type errors before compilation.

-    "lint": "eslint . && tsc --noEmit",
+    "lint": "eslint . && npm run type-check",
+    "type-check": "tsc --noEmit",
-    "build": "rm -rf dist/ && tsc"
+    "build": "rm -rf dist/ && npm run type-check && tsc"

Also applies to: 20-20

test/utils/sanitizeParams.test.ts (2)

15-23: Consider adding more edge cases for parameter decoding.

While the basic decoding test is good, consider adding tests for:

  • Invalid encoded values
  • Empty arrays
  • Null/undefined values

60-77: Enhance test coverage for case-insensitive sorting.

The test only verifies the happy path. Consider adding tests for:

  • Multiple fields with case-insensitive sorting
  • Special characters in field values
  • Empty field values
src/aggregate.ts (1)

12-17: Consider making aggregation type more specific.

The Document[] type for aggregation is too broad. Consider creating a union type of specific aggregation stages for better type safety.

type AggregationStage = 
  | { $match: Document }
  | { $sort: Document }
  | { $limit: number }
  | { $project: Document }
  | { $addFields: Document };

interface AggregateParams extends PaginationParams {
  aggregation?: AggregationStage[];
  // ... rest of the interface
}
src/search.ts (1)

6-11: Consider strengthening the next parameter type.

The Encodable type for next might be too permissive. Consider creating a more specific type that matches the expected structure [number, any].

export interface SearchParams {
  query?: Record<string, any>;
  limit?: number;
  fields?: Record<string, number>;
  next?: [number, any] | string;
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54a54b7 and 240dc62.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • package.json (4 hunks)
  • src/aggregate.ts (1 hunks)
  • src/find.ts (1 hunks)
  • src/search.ts (1 hunks)
  • src/utils/sanitizeParams.ts (4 hunks)
  • test/utils/sanitizeParams.test.ts (1 hunks)
  • tsconfig.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/utils/sanitizeParams.ts
🧰 Additional context used
📓 Learnings (1)
src/find.ts (2)
Learnt from: adborroto
PR: mixmaxhq/mongo-cursor-pagination#367
File: src/find.ts:14-20
Timestamp: 2025-01-07T11:51:25.488Z
Learning: The `hint` property is defined in the `PaginationParams` interface and is inherited by `FindParams` through extension, so it should not be redefined in `FindParams`.
Learnt from: adborroto
PR: mixmaxhq/mongo-cursor-pagination#367
File: src/find.ts:14-20
Timestamp: 2025-01-07T11:51:25.488Z
Learning: The `hint` property is defined in `PaginationParams` as an optional string property (`hint?: string`). Any interface that extends `PaginationParams`, such as `FindParams` or `AggregateParams`, automatically inherits this property through TypeScript's interface extension.
🔇 Additional comments (3)
test/utils/sanitizeParams.test.ts (1)

1-13: LGTM! Well-structured test setup with proper type definitions.

The test file setup is clean with proper TypeScript imports and mock collection initialization.

src/aggregate.ts (1)

46-53: Previous issue with undefined options is fixed correctly.

The spread operator now safely handles undefined options with a fallback to an empty object.

src/search.ts (1)

71-80: ⚠️ Potential issue

Add type assertion for params.next array access.

The array access on params.next needs type safety to prevent runtime errors.

   if (params.next) {
     aggregatePipeline.push({
       $match: {
         $or: [
-          { score: { $lt: params.next[0] } },
-          { score: { $eq: params.next[0] }, _id: { $lt: params.next[1] } },
+          { score: { $lt: (params.next as [number, any])[0] } },
+          { score: { $eq: (params.next as [number, any])[0] }, _id: { $lt: (params.next as [number, any])[1] } },
         ],
       },
     });

Likely invalid or redundant comment.

tsconfig.json Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
src/find.ts Outdated Show resolved Hide resolved
src/find.ts Outdated Show resolved Hide resolved
Copy link

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

🔭 Outside diff range comments (1)
package.json (1)

Line range hint 76-78: Update Node.js version constraint.

The current engines field specifies Node.js ">= 6.9.1" which is incompatible with:

  • TypeScript 4.9.5 (requires Node.js >= 12.20)
  • Modern development tools and dependencies

Update the engines field to reflect current requirements:

  "engines": {
-   "node": ">= 6.9.1"
+   "node": ">= 14.17.0"
  }
♻️ Duplicate comments (1)
package.json (1)

54-59: 🛠️ Refactor suggestion

Address TypeScript dependency configuration issues.

Previous review comments have identified several issues that need attention:

  1. Move @types/express to devDependencies
  2. Pin TypeScript-related package versions for consistency
  3. Ensure compatibility with current Node.js releases

Additionally:

  1. Add tslib as a runtime dependency to optimize the TypeScript helpers:
  "dependencies": {
    "base64-url": "^2.2.0",
+   "tslib": "^2.6.2",
    // ...
  }

Also applies to: 71-73

🧹 Nitpick comments (4)
package.json (1)

14-20: LGTM! Consider adding type declaration file generation.

The TypeScript-related script changes look good, with proper separation of concerns and comprehensive testing setup.

Consider adding the --declaration flag to generate .d.ts files in the build script:

-    "build": "rm -rf dist/ && tsc"
+    "build": "rm -rf dist/ && tsc --declaration"
src/find.ts (3)

22-25: Consider using string literal types for better type safety.

Convert the constants to TypeScript string literal types to get better type checking and autocompletion.

-const COLLECTION_METHODS = {
+const COLLECTION_METHODS = {
   FIND: 'find',
   FIND_AS_CURSOR: 'findAsCursor',
-};
+} as const;
+type CollectionMethod = typeof COLLECTION_METHODS[keyof typeof COLLECTION_METHODS];

31-32: Add null check for params.fields access.

The condition could throw if params.fields is undefined. Consider adding a null check.

   const removePaginatedFieldInResponse =
-    params.fields && !params.fields[params.paginatedField || '_id'];
+    params.fields != null && !params.fields[params.paginatedField || '_id'];

74-75: Consider combining cursor operations.

The cursor operations could be chained for better readability.

-    const cursor = collatedQuery.sort($sort).limit(params.limit + 1);
-    if (params.hint) cursor.hint(params.hint);
+    const cursor = collatedQuery
+      .sort($sort)
+      .limit(params.limit + 1)
+      .hint(params.hint || {});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 240dc62 and 6e7401d.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • package.json (3 hunks)
  • src/find.ts (1 hunks)
🧰 Additional context used
📓 Learnings (1)
src/find.ts (2)
Learnt from: adborroto
PR: mixmaxhq/mongo-cursor-pagination#367
File: src/find.ts:14-20
Timestamp: 2025-01-07T11:51:25.488Z
Learning: The `hint` property is defined in the `PaginationParams` interface and is inherited by `FindParams` through extension, so it should not be redefined in `FindParams`.
Learnt from: adborroto
PR: mixmaxhq/mongo-cursor-pagination#367
File: src/find.ts:14-20
Timestamp: 2025-01-07T11:51:25.488Z
Learning: The `hint` property is defined in `PaginationParams` as an optional string property (`hint?: string`). Any interface that extends `PaginationParams`, such as `FindParams` or `AggregateParams`, automatically inherits this property through TypeScript's interface extension.
🪛 Biome (1.9.4)
src/find.ts

[error] 91-91: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)

🔇 Additional comments (2)
src/find.ts (2)

14-20: Well-structured interface definition!

The FindParams interface properly extends PaginationParams and defines clear, type-safe optional properties. Good job on not redefining the hint property since it's inherited from PaginationParams.


44-45: Type assertion could be unsafe.

The type assertion to SanitizeParams might hide type incompatibilities. Consider adding runtime validation or using a type guard.

✅ Verification successful

Type assertion is safe in this context

The type assertion params as SanitizeParams is safe because:

  • The sanitizeParams() function performs thorough runtime validation and transformation
  • All properties in SanitizeParams are optional
  • FindParams is compatible with SanitizeParams (shares common properties)
  • The function returns a properly validated object
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check the definition of SanitizeParams to ensure compatibility
ast-grep --pattern 'interface SanitizeParams {
  $$$
}'

Length of output: 654


Script:

#!/bin/bash
# Check the params parameter type and sanitizeParams implementation
ast-grep --pattern 'function find($$$) {
  $$$
}'

echo "=== Checking sanitizeParams implementation ==="
ast-grep --pattern 'export async function sanitizeParams($$$) {
  $$$
}'

Length of output: 229


Script:

#!/bin/bash
# Check find.ts content
echo "=== Content of find.ts ==="
cat src/find.ts

echo -e "\n=== Content of sanitizeParams.ts ==="
cat src/utils/sanitizeParams.ts

Length of output: 7355

Comment on lines +89 to +93
function hasFindAsCursor(
collection: unknown
): collection is Collection<Document> & { findAsCursor: Function } {
return typeof (collection as any).findAsCursor === 'function';
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve type safety of the type guard function.

The function uses unsafe type practices:

  1. Uses broad 'Function' type (flagged by static analysis)
  2. Uses type assertion to 'any'
 function hasFindAsCursor(
   collection: unknown
-): collection is Collection<Document> & { findAsCursor: Function } {
-  return typeof (collection as any).findAsCursor === 'function';
+): collection is Collection<Document> & { findAsCursor: (...args: any[]) => any } {
+  return typeof (collection as Partial<Collection<Document>>).findAsCursor === 'function';
 }
📝 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 hasFindAsCursor(
collection: unknown
): collection is Collection<Document> & { findAsCursor: Function } {
return typeof (collection as any).findAsCursor === 'function';
}
function hasFindAsCursor(
collection: unknown
): collection is Collection<Document> & { findAsCursor: (...args: any[]) => any } {
return typeof (collection as Partial<Collection<Document>>).findAsCursor === 'function';
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 91-91: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)

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