Skip to content

fix: handle exports.__proto__ assignment in CJS modules#32262

Closed
Copilot wants to merge 3 commits intomainfrom
copilot/fix-import-missing-properties
Closed

fix: handle exports.__proto__ assignment in CJS modules#32262
Copilot wants to merge 3 commits intomainfrom
copilot/fix-import-missing-properties

Conversation

Copy link
Contributor

Copilot AI commented Feb 22, 2026

Deno deletes Object.prototype.__proto__ by default, which breaks CJS modules that use exports.__proto__ = require('./base') to set up prototype inheritance. The __proto__ setter is gone, so the assignment creates a data property instead of setting the prototype, leaving inherited properties inaccessible.

// base.js
module.exports = { obj: { isplain() {} }, num: { is() {} } };

// index.js
exports.__proto__ = require('./base');  // becomes a data property, not a prototype set
exports.required = function() {};

// consumer
import toi from 'npm:@toi/toi@1.3.0';
toi.obj.isplain(); // TypeError: Cannot read properties of undefined

Changes

  • ext/node/polyfills/01_require.js: After Module._load executes a CJS module, detect own __proto__ data properties on module.exports and convert them to actual prototype assignments via Object.setPrototypeOf()
  • libs/node_resolver/analyze.rs: Filter __proto__ from ESM wrapper named exports (alongside default and module.exports) — it's not a meaningful export name and interferes with prototype chain semantics
  • Tests: Spec test exercising the exports.__proto__ pattern end-to-end, unit test for the wrapper filtering

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • cdn.jsdelivr.net
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node /home/REDACTED/work/_temp/ghcca-node/node/bin/node --enable-source-maps /home/REDACTED/work/_temp/copilot-developer-action-main/dist/index.js (dns block)
  • jsr.io
    • Triggering command: /home/REDACTED/work/deno/deno/target/debug/deno deno run --allow-all --config=tests/config/deno.json ./tools/lint.js --js --check-cfg cfg(docsrs,test) --check-cfg cfg(feature, values("ahash", "alloc", "allocator-api2", "compiler_builtins", "core",�� -C metadata=1b7e5ed4d7eba4eb -O0 f/aws-lc-sys-0.2/home/REDACTED/work/deno/deno/target/debug/deps/test_server-7d0484505fb77ce1.0p36kbash ib f/aws-lc-sys-0.2/home/REDACTED/work/deno/deno/target/debug/deps/test_server-7d0484505fb77ce1.0ulsx--noprofile orted/include pointer py-library afc496e831/out/d--edition=2024 (dns block)
  • unpkg.com
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node /home/REDACTED/work/_temp/ghcca-node/node/bin/node --enable-source-maps /home/REDACTED/work/_temp/copilot-developer-action-main/dist/index.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>npm:@toi/toi@1.3.0 import missing properties</issue_title>
<issue_description>From: #27894

> import * as toi from 'npm:@toi/toi@1.3.0'
undefined
>  const obj = () => toi.required().and(toi.obj.isplain());
  const req = () => toi.required();
  const num = () => toi.num.is();
  const str = () => toi.str.is();

  const isValid = obj().and(
    toi.obj.keys({
      number: req().and(num()),
      negNumber: req().and(num()),
      maxNumber: req().and(num()),
      string: req().and(str()),
      longString: req().and(str()),
      boolean: req().and(toi.bool.is()),
      deeplyNested: obj().and(
        toi.obj.keys({
          foo: req().and(str()),
          num: req().and(num()),
          bool: req().and(toi.bool.is()),
        }),
      ),
    }),
  );

Uncaught TypeError: Cannot read properties of undefined (reading 'isplain')
    at obj (<anonymous>:1:65)
    at <anonymous>:5:17

Version: Deno 2.1.9
</issue_description>

<agent_instructions>Please try to reproduce this problem and investigate what might be the problem - it's enough for you to boil this down to a smaller reproduction that doesn't involve using external packages. You might want to try running with --unstable-unsafe-proto.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI and others added 2 commits February 22, 2026 11:27
When Object.prototype.__proto__ is deleted (Deno's default), CJS modules
using `exports.__proto__ = someObj` to set up prototype chains get a data
property instead of setting the prototype. This fix adds a post-execution
check in Module._load to convert __proto__ data properties into actual
prototype assignments using Object.setPrototypeOf.

Also filters __proto__ from ESM wrapper named exports since it's not a
meaningful export name.

Co-authored-by: bartlomieju <13602871+bartlomieju@users.noreply.github.com>
Co-authored-by: bartlomieju <13602871+bartlomieju@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix missing properties for npm:@toi/toi@1.3.0 fix: handle exports.__proto__ assignment in CJS modules Feb 22, 2026
Copilot AI requested a review from bartlomieju February 22, 2026 11:41
@Hajime-san
Copy link
Contributor

Hajime-san commented Feb 23, 2026

Unfortunately, this PR is wrong approach.
It requires CJS export re-assignment, not need to assignment __proto__.
Please check the built JS file by tsc.

// https://cdn.jsdelivr.net/npm/@toi/toi@1.3.0/build/index.js
...
var date;
(function (date) {
   ...
})(date = exports.date || (exports.date = {}));

Below PR will resolve the issue:

% target/debug/deno repl
import * as toi from 'npm:@toi/toi@1.3.0'Deno 2.6.9
exit using ctrl+d, ctrl+c, or close()
> import * as toi from 'npm:@toi/toi@1.3.0'
undefined
> const obj = () => toi.required().and(toi.obj.isplain());
  const req = () => toi.required();
  const num = () => toi.num.is();
  const str = () => toi.str.is();

  const isValid = obj().and(
    toi.obj.keys({
      number: req().and(num()),
      negNumber: req().and(num()),
      maxNumber: req().and(num()),
      string: req().and(str()),
      longString: req().and(str()),
      boolean: req().and(toi.bool.is()),
      deeplyNested: obj().and(
        toi.obj.keys({
          foo: req().and(str()),
          num: req().and(num()),
          bool: req().and(toi.bool.is()),
        }),
      ),
    }),
  );
undefined

@bartlomieju
Copy link
Member

Closing in favor of #32163

@bartlomieju bartlomieju deleted the copilot/fix-import-missing-properties branch February 23, 2026 14:54
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.

npm:@toi/toi@1.3.0 import missing properties

3 participants