Repository: rudderlabs/rudder-transformer
Pull request #4345: chore: pass copy of metadata to avoid actual metadata modifications
Author: login: Jayachand
Created: 2025-05-23T08:44:24Z | Merged: 2025-05-26T13:44:06Z

## What are the changes introduced in this PR?

Write a brief explainer on your code changes.

## What is the related Linear task?

Resolves DAW-1365

## Please explain the objectives of your changes below

Put down any required details on the broader aspect of your changes. If there are any dependent changes, **mandatorily** mention them here

### Any changes to existing capabilities/behaviour, mention the reason & what are the changes ?

N/A

### Any new dependencies introduced with this change?

N/A

### Any new generic utility introduced or modified. Please explain the changes.

N/A

### Any technical or performance related pointers to consider with the change?

N/A

@coderabbitai review

<hr>

### Developer checklist

- [ ] My code follows the style guidelines of this project

- [ ] **No breaking changes are being introduced.**

- [ ] All related docs linked with the PR?

- [ ] All changes manually tested?

- [ ] Any documentation changes needed with this change?

- [ ] Is the PR limited to 10 file changes?

- [ ] Is the PR limited to one linear task?

- [ ] Are relevant unit and component test-cases added in **new readability format**?

### Reviewer checklist

- [ ] Is the type of change in the PR title appropriate as per the changes?

- [ ] Verified that there are no credentials or confidential data exposed with the changes.



Diff:
diff --git a/src/util/customTransformer.js b/src/util/customTransformer.js
index 3fcc8e531f..1ac6e45c63 100644
--- a/src/util/customTransformer.js
+++ b/src/util/customTransformer.js
@@ -121,7 +121,39 @@ async function runUserTransform(
 
   jail.setSync('metadata', function (...args) {
     const eventMetadata = eventsMetadata[args[0].messageId] || {};
-    return new ivm.ExternalCopy(eventMetadata).copyInto();
+    const meta = {
+      sourceId: eventMetadata.sourceId,
+      sourceName: eventMetadata.sourceName,
+      originalSourceId: eventMetadata.originalSourceId,
+      workspaceId: eventMetadata.workspaceId,
+      sourceType: eventMetadata.sourceType,
+      sourceCategory: eventMetadata.sourceCategory,
+      destinationId: eventMetadata.destinationId,
+      destinationType: eventMetadata.destinationType,
+      destinationName: eventMetadata.destinationName,
+      // TODO: remove non required fields
+      namespace: eventMetadata.namespace,
+      trackingPlanId: eventMetadata.trackingPlanId,
+      trackingPlanVersion: eventMetadata.trackingPlanVersion,
+      sourceTpConfig: eventMetadata.sourceTpConfig,
+      mergedTpConfig: eventMetadata.mergedTpConfig,
+      jobId: eventMetadata.jobId,
+      sourceJobId: eventMetadata.sourceJobId,
+      sourceJobRunId: eventMetadata.sourceJobRunId,
+      sourceTaskRunId: eventMetadata.sourceTaskRunId,
+      recordId: eventMetadata.recordId,
+      messageId: eventMetadata.messageId,
+      messageIds: eventMetadata.messageIds,
+      rudderId: eventMetadata.rudderId,
+      receivedAt: eventMetadata.receivedAt,
+      eventName: eventMetadata.eventName,
+      eventType: eventMetadata.eventType,
+      sourceDefinitionId: eventMetadata.sourceDefinitionId,
+      destinationDefinitionId: eventMetadata.destinationDefinitionId,
+      transformationId: eventMetadata.transformationId,
+      transformationVersionId: eventMetadata.transformationVersionId,
+    };
+    return new ivm.ExternalCopy(meta).copyInto();
   });
 
   const bootstrap = await isolate.compileScript(
diff --git a/src/util/ivmFactory.js b/src/util/ivmFactory.js
index e9901cc528..820eb29b82 100644
--- a/src/util/ivmFactory.js
+++ b/src/util/ivmFactory.js
@@ -96,7 +96,40 @@ async function createIvm(
 
       var metadata = function(event) {
         const eventMetadata = event ? eventsMetadata[event.messageId] || {} : {};
-        return eventMetadata;
+        return {
+          sourceId: eventMetadata.sourceId,
+          sourceName: eventMetadata.sourceName,
+          workspaceId: eventMetadata.workspaceId,
+          sourceType: eventMetadata.sourceType,
+          sourceCategory: eventMetadata.sourceCategory,
+          destinationId: eventMetadata.destinationId,
+          destinationType: eventMetadata.destinationType,
+          destinationName: eventMetadata.destinationName,
+
+          // TODO: remove non required fields
+
+          namespace: eventMetadata.namespace,
+          originalSourceId: eventMetadata.originalSourceId,
+          trackingPlanId: eventMetadata.trackingPlanId,
+          trackingPlanVersion: eventMetadata.trackingPlanVersion,
+          sourceTpConfig: eventMetadata.sourceTpConfig,
+          mergedTpConfig: eventMetadata.mergedTpConfig,
+          jobId: eventMetadata.jobId,
+          sourceJobId: eventMetadata.sourceJobId,
+          sourceJobRunId: eventMetadata.sourceJobRunId,
+          sourceTaskRunId: eventMetadata.sourceTaskRunId,
+          recordId: eventMetadata.recordId,
+          messageId: eventMetadata.messageId,
+          messageIds: eventMetadata.messageIds,
+          rudderId: eventMetadata.rudderId,
+          receivedAt: eventMetadata.receivedAt,
+          eventName: eventMetadata.eventName,
+          eventType: eventMetadata.eventType,
+          sourceDefinitionId: eventMetadata.sourceDefinitionId,
+          destinationDefinitionId: eventMetadata.destinationDefinitionId,
+          transformationId: eventMetadata.transformationId,
+          transformationVersionId: eventMetadata.transformationVersionId,
+        };
       }
       switch(transformType) {
         case "transformBatch":


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>src/util/ivmFactory.js (1)</summary><blockquote>

`98-98`: **Effective deep cloning implementation with performance consideration.**

The use of `JSON.parse(JSON.stringify())` successfully prevents mutations to the original metadata object, which aligns with the PR objective. This approach works well for plain objects containing serializable data.

However, consider the performance implications if metadata objects are large or this function is called frequently, as JSON serialization/deserialization can be expensive. For better performance with complex objects, you might consider using a dedicated cloning library like Lodash's `cloneDeep`.

The current implementation is appropriate for typical metadata objects containing primitives and plain objects.




For performance-critical scenarios, consider this alternative:

```diff
-        const eventMetadata = event ? JSON.parse(JSON.stringify(eventsMetadata[event.messageId] || {})) : {};
+        const eventMetadata = event ? require('lodash').cloneDeep(eventsMetadata[event.messageId] || {}) : {};
```

Note: This would require adding lodash as a dependency if not already available.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 20ccf22add29df9df526cd53103b5a68b77aac20 and cd21e3edf14b8c187b3e5a9b8300d03bc5a9c5d8.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `src/util/ivmFactory.js` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (1)</summary>

<details>
<summary>`**/*.js`: Focus on ESLint errors (max 3) and warnings (max 5).</summary>

> `**/*.js`: Focus on ESLint errors (max 3) and warnings (max 5).

- `src/util/ivmFactory.js`

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (8)</summary>

* GitHub Check: Build User Transformer Docker Image - PR / Build Transformer Docker Image AMD64
* GitHub Check: Build Transformer Docker Image - PR / Build Transformer Docker Image AMD64
* GitHub Check: Build Transformer Docker Image - PR / Build Transformer Docker Image ARM64
* GitHub Check: Code Coverage
* GitHub Check: test_and_publish
* GitHub Check: UT Tests
* GitHub Check: Analyze (javascript-typescript)
* GitHub Check: Analyze (go)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T08:46:32Z
user: login: koladilip
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T08:48:54Z
user: login: Jayachand
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-23T08:53:52Z
user: login: abhimanyubabbar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T08:55:32Z
user: login: koladilip
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T09:02:29Z
user: login: Jayachand
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-23T09:30:14Z
user: login: Jayachand
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-23T09:44:22Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>src/util/customTransformer.js (1)</summary><blockquote>

`124-156`: **Excellent defensive programming approach to prevent metadata mutations.**

This change effectively implements the PR objective by creating a controlled copy of metadata fields instead of exposing the original object. The explicit field enumeration provides clear visibility into what data is being shared with the isolated VM context.

However, the TODO comment on line 134 and the extensive field enumeration suggest opportunities for improvement:

```diff
 jail.setSync('metadata', function (...args) {
   const eventMetadata = eventsMetadata[args[0].messageId] || {};
-  const meta = {
-    sourceId: eventMetadata.sourceId,
-    sourceName: eventMetadata.sourceName,
-    // ... 30+ fields
-  };
+  // Define allowed fields in a constant for better maintainability
+  const ALLOWED_METADATA_FIELDS = [
+    'sourceId', 'sourceName', 'originalSourceId', 'workspaceId',
+    'sourceType', 'sourceCategory', 'destinationId', 'destinationType',
+    'destinationName', 'messageId', 'rudderId', 'receivedAt',
+    'eventName', 'eventType', 'transformationId'
+    // Remove deprecated/unnecessary fields as indicated by TODO
+  ];
+  
+  const meta = Object.fromEntries(
+    ALLOWED_METADATA_FIELDS.map(field => [field, eventMetadata[field]])
+  );
   return new ivm.ExternalCopy(meta).copyInto();
 });
```

This approach would:
- Make field management more maintainable
- Enable easier removal of unnecessary fields (addressing the TODO)
- Reduce code duplication if similar filtering is needed elsewhere

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between cd21e3edf14b8c187b3e5a9b8300d03bc5a9c5d8 and 08c9b7b1c627247dec983e001dc0acfea4cbc525.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `src/util/customTransformer.js` (1 hunks)
* `src/util/ivmFactory.js` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* src/util/ivmFactory.js

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (1)</summary>

<details>
<summary>`**/*.js`: Focus on ESLint errors (max 3) and warnings (max 5).</summary>

> `**/*.js`: Focus on ESLint errors (max 3) and warnings (max 5).

- `src/util/customTransformer.js`

</details>

</details><details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>src/util/customTransformer.js (2)</summary><blockquote>

<details>
<summary>src/util/ivmFactory.js (1)</summary>

* `ivm` (1-1)

</details>
<details>
<summary>src/util/customTransformer-v1.js (1)</summary>

* `ivm` (1-1)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (9)</summary>

* GitHub Check: Build User Transformer Docker Image - PR / Build Transformer Docker Image ARM64
* GitHub Check: Build User Transformer Docker Image - PR / Build Transformer Docker Image AMD64
* GitHub Check: Build Transformer Docker Image - PR / Build Transformer Docker Image ARM64
* GitHub Check: Build Transformer Docker Image - PR / Build Transformer Docker Image AMD64
* GitHub Check: Code Coverage
* GitHub Check: test_and_publish
* GitHub Check: UT Tests
* GitHub Check: Analyze (javascript-typescript)
* GitHub Check: Analyze (go)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T12:45:35Z
user: login: abhimanyubabbar
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T12:01:15Z
user: login: Jayachand
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-26T12:06:42Z
user: login: abhimanyubabbar
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T12:51:08Z
user: login: abhimanyubabbar
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T12:51:09Z
user: login: cisse21
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-26T13:43:05Z

Review comments:
diff_hunk: @@ -95,7 +95,7 @@ async function createIvm(
       const isObject = (o) => Object.prototype.toString.call(o) === '[object Object]';
 
       var metadata = function(event) {
-        const eventMetadata = event ? eventsMetadata[event.messageId] || {} : {};
+        const eventMetadata = event ? JSON.parse(JSON.stringify(eventsMetadata[event.messageId] || {})) : {};
path: src/util/ivmFactory.js
user: login: koladilip
body: any reason for not using lodash deepCopy?
created_at: 2025-05-23T08:48:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -95,7 +95,7 @@ async function createIvm(
       const isObject = (o) => Object.prototype.toString.call(o) === '[object Object]';
 
       var metadata = function(event) {
-        const eventMetadata = event ? eventsMetadata[event.messageId] || {} : {};
+        const eventMetadata = event ? JSON.parse(JSON.stringify(eventsMetadata[event.messageId] || {})) : {};
path: src/util/ivmFactory.js
user: login: Jayachand
body: faster this way
created_at: 2025-05-23T08:53:52Z
author_association: COLLABORATOR
diff_hunk: @@ -95,7 +95,7 @@ async function createIvm(
       const isObject = (o) => Object.prototype.toString.call(o) === '[object Object]';
 
       var metadata = function(event) {
-        const eventMetadata = event ? eventsMetadata[event.messageId] || {} : {};
+        const eventMetadata = event ? JSON.parse(JSON.stringify(eventsMetadata[event.messageId] || {})) : {};
path: src/util/ivmFactory.js
user: login: abhimanyubabbar
body: I would suggest we don't pass the metadata as it is even after the copy. We cherry pick the data which we wanna send in through keys and just return that object. 

Wdyt ?
created_at: 2025-05-23T08:55:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -95,7 +95,7 @@ async function createIvm(
       const isObject = (o) => Object.prototype.toString.call(o) === '[object Object]';
 
       var metadata = function(event) {
-        const eventMetadata = event ? eventsMetadata[event.messageId] || {} : {};
+        const eventMetadata = event ? JSON.parse(JSON.stringify(eventsMetadata[event.messageId] || {})) : {};
path: src/util/ivmFactory.js
user: login: koladilip
body: incase we want to clone -> structuredClone is faster and native no need to use lodash cloneDeep

created_at: 2025-05-23T09:02:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -95,7 +95,7 @@ async function createIvm(
       const isObject = (o) => Object.prototype.toString.call(o) === '[object Object]';
 
       var metadata = function(event) {
-        const eventMetadata = event ? eventsMetadata[event.messageId] || {} : {};
+        const eventMetadata = event ? JSON.parse(JSON.stringify(eventsMetadata[event.messageId] || {})) : {};
path: src/util/ivmFactory.js
user: login: Jayachand
body: didn't cherry pick the data because we have to parse transformation codes and validate the required fields
created_at: 2025-05-23T09:30:14Z
author_association: COLLABORATOR
diff_hunk: @@ -95,7 +95,7 @@ async function createIvm(
       const isObject = (o) => Object.prototype.toString.call(o) === '[object Object]';
 
       var metadata = function(event) {
-        const eventMetadata = event ? eventsMetadata[event.messageId] || {} : {};
+        const eventMetadata = event ? JSON.parse(JSON.stringify(eventsMetadata[event.messageId] || {})) : {};
path: src/util/ivmFactory.js
user: login: Jayachand
body: did an online js benchmark test using actual metadata object:

- structuredClone: 123K ops/s ± 2.08%
- JSON way:           186K ops/s ± 0.66%


created_at: 2025-05-23T09:44:22Z
author_association: COLLABORATOR
diff_hunk: @@ -121,7 +121,39 @@ async function runUserTransform(
 
   jail.setSync('metadata', function (...args) {
     const eventMetadata = eventsMetadata[args[0].messageId] || {};
-    return new ivm.ExternalCopy(eventMetadata).copyInto();
+    const meta = {
path: src/util/customTransformer.js
user: login: abhimanyubabbar
body: Given it has many fields and used in 2 places, would it be better to encapsulate it in a function call ?

created_at: 2025-05-26T12:01:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -121,7 +121,39 @@ async function runUserTransform(
 
   jail.setSync('metadata', function (...args) {
     const eventMetadata = eventsMetadata[args[0].messageId] || {};
-    return new ivm.ExternalCopy(eventMetadata).copyInto();
+    const meta = {
path: src/util/customTransformer.js
user: login: Jayachand
body: No, this is for v0 (marked for deprecation). It is fine to repeat only twice
created_at: 2025-05-26T12:06:42Z
author_association: COLLABORATOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-23T08:44:32Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Summary by CodeRabbit

- **Refactor**
  - Limited the metadata returned by certain features to a specific set of fields, reducing the amount of information exposed to users. No changes to the user interface or workflows.
## Summary by CodeRabbit

- **Bug Fixes**
	- Improved reliability by ensuring that metadata returned to users is a fresh copy, preventing unintended side effects from data changes.
- **Enhancements**
	- Restricted metadata exposure by explicitly enumerating and returning only a defined subset of metadata fields instead of the entire metadata object.

## Walkthrough

The update modifies the `metadata` function in both `src/util/ivmFactory.js` and `src/util/customTransformer.js`. Instead of returning the entire metadata object, the function now returns a new object explicitly listing specific metadata fields, reducing exposure and preventing unintended mutations.

## Changes

| File(s)                         | Change Summary                                                                                              |
|--------------------------------|-------------------------------------------------------------------------------------------------------------|
| src/util/ivmFactory.js          | Modified the `metadata` function to return a new object with explicitly selected metadata fields instead of the full metadata object. |
| src/util/customTransformer.js   | Updated the synchronous `metadata` function to construct and return a new object with a fixed set of metadata fields wrapped in `ivm.ExternalCopy`. |

## Suggestions for Improvement

- Consider documenting the rationale for selecting specific metadata fields to clarify the intent for future maintainers.
- Evaluate if further refactoring could centralize the metadata field selection logic to avoid duplication between `ivmFactory.js` and `customTransformer.js`.
- Add unit tests to verify that only the intended metadata fields are exposed and that no unintended fields leak through.
- Monitor performance impact if metadata objects grow large, as constructing new objects may add overhead.

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!NOTE]
> <details>
> <summary>⚡️ AI Code Reviews for VS Code, Cursor, Windsurf</summary>
> 
> CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
> Learn more [here](http://coderabbit.ai/ide).
> 
> </details>

<!-- announcements_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

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.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=rudderlabs/rudder-transformer&utm_content=4345):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `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 explain this code block.`
  -	`@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 explain its main purpose.`
  - `@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.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

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](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@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.

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: devops-github-rudderstack
created_at: 2025-05-23T08:51:00Z
body: Allure Test reports for this run are available at:
- Allure Report: [View Report](https://rudderlabs.github.io/test-reports/rudder-transformer/4345/index.html)
author_association: CONTRIBUTOR
user: login: codecov[bot]
created_at: 2025-05-23T08:51:14Z
body: ## [Codecov](https://app.codecov.io/gh/rudderlabs/rudder-transformer/pull/4345?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rudderlabs) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 91.12%. Comparing base [(`4a18dc9`)](https://app.codecov.io/gh/rudderlabs/rudder-transformer/commit/4a18dc99a37cb25476fbe0b7a4e68b3a96caf77f?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rudderlabs) to head [(`a7b2f16`)](https://app.codecov.io/gh/rudderlabs/rudder-transformer/commit/a7b2f163eb500d7ca110e3a701fee9f9665cbbea?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rudderlabs).
> Report is 15 commits behind head on develop.

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##           develop    #4345      +/-   ##
===========================================
+ Coverage    91.10%   91.12%   +0.01%     
===========================================
  Files          636      636              
  Lines        33378    33405      +27     
  Branches      7888     7893       +5     
===========================================
+ Hits         30410    30440      +30     
+ Misses        2729     2726       -3     
  Partials       239      239              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/rudderlabs/rudder-transformer/pull/4345?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rudderlabs).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rudderlabs).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: devops-github-rudderstack
created_at: 2025-05-23T12:49:45Z
body: Allure Test reports for this run are available at:
- Allure Report: [View Report](https://rudderlabs.github.io/test-reports/rudder-transformer/4345/index.html)
author_association: CONTRIBUTOR
user: login: devops-github-rudderstack
created_at: 2025-05-26T13:18:25Z
body: Allure Test reports for this run are available at:
- Allure Report: [View Report](https://rudderlabs.github.io/test-reports/rudder-transformer/4345/index.html)
author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-05-26T13:20:05Z
body: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=rudderlabs_rudder-transformer&pullRequest=4345) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed.svg '') [5 New Code Smells](https://sonarcloud.io/dashboard?id=rudderlabs_rudder-transformer&pullRequest=4345) (required ≤ 1)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed.svg '') [C Maintainability Rating on New Code](https://sonarcloud.io/dashboard?id=rudderlabs_rudder-transformer&pullRequest=4345) (required ≥ A)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=rudderlabs_rudder-transformer&pullRequest=4345)

##   
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/light_bulb.svg '') Catch issues before they fail your Quality Gate with our IDE extension ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/sonarlint.svg '') [SonarQube for IDE](https://www.sonarsource.com/products/sonarlint/features/connected-mode/?referrer=pull-request)


author_association: NONE