Skip to content

REPL doesn't work with nested proxies #57829

Description

@geeksilva97

Version

23.10.0

Platform

Darwin LM-JNVH7RPW6J 23.6.0 Darwin Kernel Version 23.6.0: Thu Dec 19 20:47:53 PST 2024; root:xnu-10063.141.1.703.2~1/RELEASE_ARM64_T6030 arm64

Subsystem

repl

What steps will reproduce the bug?

  1. Open the repl ( type node)
  2. Paste the following code
  const handler = {
    get(target, prop, receiver) {
      console.log(`Accessed prop: ${String(prop)}`);

      return new Proxy(function () {}, {
        apply: function (_target, _thisArg, args) {
          console.log(`Called function: ${String(prop)} with args:`, args);
          return `Result of calling ${prop}`;
        },
        get: function (_target, subProp) {
          console.log(`Accessed nested prop: ${String(subProp)} on ${String(prop)}`);
          return `Nested value of ${subProp}`;
        }
      });
    }
  };

  const obj = new Proxy({}, handler);
  1. Try to access a nested prop like obj.hello.world.

How often does it reproduce? Is there a required condition?

Always

What is the expected behavior? Why is that the expected behavior?

It should allow me to type something like obj.hello.world.

What do you see instead?

I can type obj.hello but once I type the other dot, it already triggers the proxy. See the video:

node-repl-proxy-issue.mov

Additional information

No response

Activity

  1. added
    replIssues and PRs related to the REPL subsystem.
    on Apr 11, 2025
  2. BridgeAR commented on Apr 11, 2025

    @BridgeAR
    Member

    I guess it is related to the input preview triggering util.inspect on it.

  3. dario-piotrowicz commented on Apr 12, 2025

    @dario-piotrowicz
    Member

    I'm pretty sure this is caused by the REPL completer 🤔

    Example

    The following code reproduces the issue:

    const repl = require('node:repl');
    
    const r = repl.start();
    
    r.write(`
      const handler = {
        get(target, prop, receiver) {
          console.log(\`Accessed prop: \${String(prop)}\`);
    
          return new Proxy(function () {}, {
            apply: function (_target, _thisArg, args) {
              console.log(\`Called function: \${String(prop)} with args:\`, args);
              return \`Result of calling \${prop}\`;
            },
            get: function (_target, subProp) {
              console.log(\`Accessed nested prop: \${String(subProp)} on \${String(prop)}\`);
              return \`Nested value of \${subProp}\`;
            }
          });
        }
      };
    
      const obj = new Proxy({}, handler);
    `);

    (+ typing obj.hello.world of course)

    But the following, where the completer if overridden by a noop function does not:

    const repl = require('node:repl');
    
    const r = repl.start({
        completer: () => {
            return [[], ''];
        },
    });
    
    r.write(`
      const handler = {
        get(target, prop, receiver) {
          console.log(\`Accessed prop: \${String(prop)}\`);
    
          return new Proxy(function () {}, {
            apply: function (_target, _thisArg, args) {
              console.log(\`Called function: \${String(prop)} with args:\`, args);
              return \`Result of calling \${prop}\`;
            },
            get: function (_target, subProp) {
              console.log(\`Accessed nested prop: \${String(subProp)} on \${String(prop)}\`);
              return \`Nested value of \${subProp}\`;
            }
          });
        }
      };
    
      const obj = new Proxy({}, handler);
    `);

    (+ typing obj.hello.world of course)

    Actually based on the following code comment I think that this is a known limitation/issue

    node/lib/repl.js

    Lines 1314 to 1315 in 09ecd2e

    // Warning: This eval's code like "foo.bar.baz", so it will run property
    // getter code.

  4. dario-piotrowicz commented on Apr 12, 2025

    @dario-piotrowicz
    Member

    Yeah here it is:

    node/lib/repl.js

    Line 1519 in 09ecd2e

    this.eval(evalExpr, this.context, getREPLResourceName(), (e, obj) => {

    Image

    this is the eval triggering the getter...

    mh... I can't imagine how this could be fixed... besides maybe disabling the completer on nested object fields

    would that be a valid tradeoff? maybe it could be done behind a flag? 🤔

  5. geeksilva97 commented on Jun 24, 2025

    @geeksilva97
    ContributorAuthor

    Can we close this issue, @dario-piotrowicz ?

  6. dario-piotrowicz commented on Jun 24, 2025

    @dario-piotrowicz
    Member

    Hey @geeksilva97 🙂

    In #57909 we decided that there's still some work to do around REPL tab completion (see: #57909 (comment)).

    So that's why my PR didn't close this issue.

    I'm planning to improve the REPL tab completion check more (after #58775) before closing this if that works for you, or if you prefer (since the specific bug your issue referred to has been fixed) we can close this issue and I can open a new one for the extra work, whatever you prefer 🙂

  7. geeksilva97 commented on Jun 24, 2025

    @geeksilva97
    ContributorAuthor

    Nah, we're good. Let's leave it open until it is finished. Thank you.

  8. BridgeAR commented on Sep 11, 2025

    @BridgeAR
    Member

    This got fully resolved by now. Closing as fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    replIssues and PRs related to the REPL subsystem.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions