laverdet / isolated-vm

Secure & isolated JS environments for nodejs
ISC License
2.2k stars 154 forks source link

How to return ReadbleStream from ivm? #398

Closed 1bye closed 1 year ago

1bye commented 1 year ago

# Node.js: 18.17.1 OS: Ubuntu 22.04 Compiler: gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04) # Hi! I'm trying to return ReadableStream from ivm, but nothing is working. And this possible to return ReadableStream? I also tried use unsafeInherit: true but in my case nothing is worked.

(Also this code is example, it would be used with langchain.js which returns ReadableStream, I can post this code here if this needed)

My full ReadableStream example code:

import ivm from 'isolated-vm';
import {Readable} from 'stream'

async function createIsolatedStream() {
    const isolate = new ivm.Isolate({memoryLimit: 128});

    // Create a context in the isolate
    const context = await isolate.createContext();

    context.evalClosureSync(`
        globalThis.Readable = (function() {
            return $1.applySync(undefined, [], { result: {reference: true} })
        })()
    `, [
        ivm,
        new ivm.Reference(function() { return Readable })
    ])
    // ERROR: TypeError: function Readable(options) {
    // if (!(this instanceof Readable))
    // return new Readable(options);
    // Checkin...<omitted>...
    // } could not be cloned.

   // OR (not working also)
   // conext.global.setSync('Readable', Readable, { reference: true })

    // Create and compile a script
    // This class MyReadableStream works in simple node env (and instead of Readable.copySync() is Readable)
    const script = await isolate.compileScript(`
        class MyReadableStream extends Readable.copySync() {
            constructor(options) {
                super(options);
                this.data = ['Hello', 'World', null]; // Data to be streamed
            }

            _read() {
                const chunk = this.data.shift();
                if (chunk === null) {
                    this.push(null); // End of stream
                } else {
                    this.push(chunk); // Push data to the stream
                }
            }
        }
        // return readable stream
        new MyReadableStream();
    `);

    // Run the script in the context and retrieve the result
    return script.run(context);
}

// Use data from stream.
createIsolatedStream().then((stream) => {
    stream.on('data', (chunk) => console.log(chunk));
});
// OR
// const stream = await createIsolatedStream();
// stream.on('data', (chunk) => console.log(chunk));

Thank you! :D

laverdet commented 1 year ago

You can't. It is like asking how to return a ReadableStream from nodejs to Safari. These are separate environments and you must invent something for your use case.