Skip to content

fix: Update undot to use pojo instead of empty objects#18175

Open
SippieCup wants to merge 2 commits into
sequelize:mainfrom
SippieCup:fix/undot-use-pojo
Open

fix: Update undot to use pojo instead of empty objects#18175
SippieCup wants to merge 2 commits into
sequelize:mainfrom
SippieCup:fix/undot-use-pojo

Conversation

@SippieCup
Copy link
Copy Markdown
Contributor

@SippieCup SippieCup commented Mar 30, 2026

This pull request primarily refactors the way plain objects are created in the undot.ts utility by replacing direct object literals with the pojo() helper from @sequelize/utils. This change is aimed at standardizing object creation, potentially for improved security or performance.

Refactoring object creation in undot.ts:

  • Replaced all instances of {} with pojo() in the setByPathArray, transformRowWithPrecompiled, and acquirePooledObject functions to standardize and potentially harden object instantiation. [1] [2] [3] [4]

Pull Request Checklist

  • Have you added new tests to prevent regressions?
  • If a documentation update is necessary, have you opened a PR to the documentation repository?
  • Did you update the typescript typings accordingly (if applicable)?
  • Does the description below contain a link to an existing issue (Closes #[issue]) or a description of the issue you are solving?
  • Does the name of your PR follow our conventions?

Description of Changes

List of Breaking Changes

Summary by CodeRabbit

  • Chores

    • Cleared project changelog files across multiple packages.
  • Refactor

    • Enhanced internal object allocation patterns for improved performance.

@SippieCup SippieCup requested a review from a team as a code owner March 30, 2026 16:39
@SippieCup SippieCup requested review from Copilot, ephys and sdepold March 30, 2026 16:39
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 30, 2026

📝 Walkthrough

Walkthrough

The PR removes changelog files from the project root and multiple packages, and refactors undot.ts to use a pojo() utility function instead of plain object literals for creating intermediate objects.

Changes

Cohort / File(s) Summary
Changelog Cleanup
CHANGELOG.md, packages/core/CHANGELOG.md, packages/utils/CHANGELOG.md, packages/validator-js/CHANGELOG.md
Entire changelog files removed, including headers and version entries for 7.0.0-alpha.48.
Object Creation Refactor
packages/core/src/utils/undot.ts
Added import of pojo from @sequelize/utils and replaced plain object literals ({}) with pojo() calls in three locations: setByPathArray for intermediate containers, transformRowWithPrecompiled for default output target, and acquirePooledObject for pooled object fallback.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related PRs

Suggested reviewers

  • sdepold
  • ephys

Poem

🐰 Away with changelogs old and worn,
pojo() objects freshly born!
Plain object syntax yields to grace,
Cleaner code in every place! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: updating undot.ts to use pojo() instead of empty objects. It is concise, specific, and directly describes the primary modification in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 OpenGrep (1.16.5)
packages/core/src/utils/undot.ts

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

�[1m Loading rules from local config...�[0m


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

❤️ Share

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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors packages/core/src/utils/undot.ts to create prototype-free plain objects via pojo() from @sequelize/utils, replacing {} allocations in a few hot-path utilities.

Changes:

  • Replace {} with pojo() in setByPathArray, transformRowWithPrecompiled, and acquirePooledObject.
  • Add pojo import to undot.ts.
  • Delete several CHANGELOG.md files (root and multiple packages).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/core/src/utils/undot.ts Switches object creation from {} to pojo() (null-prototype objects).
CHANGELOG.md Deleted.
packages/core/CHANGELOG.md Deleted.
packages/utils/CHANGELOG.md Deleted.
packages/validator-js/CHANGELOG.md Deleted.

Comment thread packages/core/src/utils/undot.ts
Comment on lines 226 to 228
if (obj[seg] == null) {
obj[seg] = nextIsIndex ? [] : {};
obj[seg] = nextIsIndex ? [] : pojo();
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

setByPathArray now creates intermediate objects using pojo(), which yields a null-prototype object. This changes the shape/semantics of nested results (e.g., no hasOwnProperty, different prototype than {}) and can be observable by callers. If the intent is only to avoid prototype-pollution via __proto__, consider whether you want null-prototype objects at all intermediate levels, or limit pojo() usage to the root/explicitly-documented outputs.

Copilot uses AI. Check for mistakes.
Comment on lines 233 to 236
let next = obj[seg];
if (next == null || (typeof next !== 'object' && !Array.isArray(next))) {
obj[seg] = nextIsIndex ? [] : {};
obj[seg] = nextIsIndex ? [] : pojo();
next = obj[seg];
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

Same concern as above: using pojo() here means target[seg] can become a null-prototype object even when target itself is a normal object. If this behavior change is intended, tests should assert the expected prototype (and any downstream code that relies on Object.prototype methods should be updated accordingly).

Copilot uses AI. Check for mistakes.
Comment on lines 258 to +260
out?: Record<string, unknown>,
): Record<string, unknown> {
const target = out ?? {};
const target = out ?? pojo();
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

transformRowWithPrecompiled now defaults to returning a null-prototype object when out is not provided (out ?? pojo()). That is a behavioral change compared to returning {} previously, and may surprise consumers (e.g., result.hasOwnProperty will be undefined). Please confirm this is intended and adjust unit tests / documentation of this utility accordingly.

Copilot uses AI. Check for mistakes.
Comment on lines 286 to 287
// Clear previous contents

Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

acquirePooledObject returns pojo() when the pool is empty, but objects popped from the pool keep whatever prototype they were created with. If the goal is to standardize/harden all pooled objects as prototype-free, consider enforcing this invariant (e.g., calling pojo(obj) on reuse) or documenting that the pool must only contain pojo() objects.

Suggested change
// Clear previous contents
// Only reuse objects that are already prototype-free (created via pojo()).
if (Object.getPrototypeOf(obj) !== null) {
return pojo();
}
// Clear previous contents

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/core/src/utils/undot.ts (1)

281-296: Minor: Transient prototype inconsistency in pooled objects.

When the pool is empty, a null-prototype pojo() is returned. When the pool has objects, the returned object retains its original prototype. This creates a transient inconsistency where some objects may have a prototype and others may not.

In practice, this is unlikely to cause issues since these objects are used as data containers (accessed via direct property lookup or Object.keys()), and the pool will gradually stabilize to pojo() objects as they're released and reacquired.

If you want strict consistency, you could replace pooled objects with pojo() when recycling:

♻️ Optional: Ensure consistent null-prototype objects
 export function acquirePooledObject(pool: Array<Record<string, unknown>>): Record<string, unknown> {
   const obj = pool.pop();
   if (!obj) {
     return pojo();
   }
+  // Ensure consistent null-prototype by replacing prototype objects
+  if (Object.getPrototypeOf(obj) !== null) {
+    return pojo();
+  }
   // Clear previous contents
   const keys = Object.keys(obj);

However, this adds overhead and may not be worth it if the pool is only ever populated with objects from this function.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/utils/undot.ts` around lines 281 - 296, acquirePooledObject
currently returns a null-prototype pojo() when the pool is empty but returns
pooled objects with their original prototype when reused, causing transient
prototype inconsistency; to fix, after you clear properties in
acquirePooledObject (function name: acquirePooledObject, variable: obj,
fallback: pojo()), normalize the prototype of the reused object to null (e.g.,
call Object.setPrototypeOf(obj, null)) before returning so every returned object
has a consistent null prototype; this keeps the same pooling behavior and
minimal overhead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/core/src/utils/undot.ts`:
- Around line 281-296: acquirePooledObject currently returns a null-prototype
pojo() when the pool is empty but returns pooled objects with their original
prototype when reused, causing transient prototype inconsistency; to fix, after
you clear properties in acquirePooledObject (function name: acquirePooledObject,
variable: obj, fallback: pojo()), normalize the prototype of the reused object
to null (e.g., call Object.setPrototypeOf(obj, null)) before returning so every
returned object has a consistent null prototype; this keeps the same pooling
behavior and minimal overhead.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d8e31afc-6817-4c89-8f25-a71609490bb4

📥 Commits

Reviewing files that changed from the base of the PR and between df4a631 and f2460c4.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • packages/core/CHANGELOG.md
  • packages/core/src/utils/undot.ts
  • packages/utils/CHANGELOG.md
  • packages/validator-js/CHANGELOG.md
💤 Files with no reviewable changes (4)
  • packages/utils/CHANGELOG.md
  • packages/core/CHANGELOG.md
  • packages/validator-js/CHANGELOG.md
  • CHANGELOG.md

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.

2 participants