-
Notifications
You must be signed in to change notification settings - Fork 158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to pass JSON to VM? I get "A non-transferable value was passed" #514
Comments
Take a look at TransferOptions in the docs |
Sorry I don't manage to get it yet const ivm = require('isolated-vm');
const isolate = new ivm.Isolate({ memoryLimit: 16 });
const context = isolate.createContextSync();
// this works
context.global.setSync('JSONs', v=>JSON.stringify(v), {})
context.evalSync('JSONs(null)')
// 'null'
// this doesn't work yet, I'm probably misunderstanding
context.global.setSync('JSON', {s: v=>JSON.stringify(v)}, {copy:true}) // trying this option
// Uncaught TypeError: v=>JSON.stringify(v) could not be cloned.
context.global.setSync('JSON', {s:v=>JSON.stringify(v)}, {reference:true}) // trying this other option
// It doesn't throw but
context.evalSync('JSON.s(null)')
// Uncaught TypeError: JSON.s is not a function Optional question (if you don't mind) are there still benefits in passing |
Otherwise, you are trying to pass a function to an isolate. It's not how isolates work. This is covered in the frequently asked question: https://github.com/laverdet/isolated-vm?tab=readme-ov-file#frequently-asked-question |
We're trying to migrate out of Here's a small example of such code: async function onUserUpdated({ api, data }) {
const user = await api.get(`users/${data.userId}`);
if (user.signinAt < Date.now() - 86400000) {
await api.put(`users/${data.userId}`, { isActive: false });
return { note: 'updated' };
}
} I've been thinking quite a lot on how to proxy data with the isolate, but since there can be any number of Maybe the best idea I have is to ensure this code is formatted a bit like React functional components with hooks, that way we would be able to fetch all data with The other solution for us is to use Node's vm to ensure the code doesn't run too long and doesn't use too much memory, and for the security part we'd use eslint to ensure there are no side-effects except using If you've time later, not urgent, I'd be very gratful to hear your thoughts Thanks for your work on this clever lib |
node vm is made for unit tests, not for security. vm2 is just a thing on top of vm. Literally, just think of isolated-vm as a web browser. How would you write this if isolated-vm was a browser and you wanted to run the database calls on the server? |
In a browser / server scenario, I'd use Otherwise I think we need to communicate back and forth between node and isolate, but it's not very easy in our case as we'd like to run one chunk of code in one go I think my idea 1 in previous message, similar to hooks could work, but it's restrictive for us, and we have to rewrite them to this type of format async function onUserUpdated({ data }) {
const user = get(`users/${data.userId}`);
if (user.signinAt < Date.now() - 86400000) {
return [{ method: 'put', path: `users/${user.id}`, body: { isActive: false } }];
}
} We could parse this code as an AST and extract all async function onUserUpdated({ data }) {
const user = { id: 'userId1', email: '....' };
if (user.signinAt < Date.now() - 86400000) {
return [{ method: 'put', path: `users/${user.id}`, body: { isActive: false } }];
}
} We can run this in ivm and then use the result to execute the final api mutations |
With #322 and #49 I"m able to get something close import ivm from 'isolated-vm';
const isolate = new ivm.Isolate({ memoryLimit: 16 });
const context = await isolate.createContext();
const global = context.global;
await global.set('log', console.log);
await global.set('api', new ivm.ExternalCopy({}).copyInto());
const api = await global.get('api')
await api.set('get', new ivm.Reference(
async function (path) {
const r = await fetch(`https://dummyjson.com${path}`);
return r.json();
}
));
const fn = await context.eval(`
(async function onUserUpdated() {
const user = await api.get.applySyncPromise(null, ['/users/1'], { result: { copy: true } });
log('fetched user', user.id);
return user;
})`, { reference: true });
const value = await fn.apply(undefined, [], { result: { promise: true, copy: true } })
console.log(value); But I get an error: (according to the docs that's a valid option)
With const user = await api.get.applySyncPromise(null, ['/users/1'], { copy: true }); I get
If using Another issue is I'd like to pass the userId as an object argument |
Objects aren't transferable. You would need to use ExternalCopy / copyInto. I don't think you need applySyncPromise for this, you are making your life harder than it needs to be. That API is meant for replicating fs.readSync without blocking the main nodejs loop. |
Ok thanks, here it is, it may work for our usecases, thanks import ivm from 'isolated-vm';
const isolate = new ivm.Isolate({ memoryLimit: 16 });
const context = await isolate.createContext();
const global = context.global;
await global.set('log', console.log);
await global.set('fetchDummmy', new ivm.Reference(
async function (path) {
const r = await fetch(`https://dummyjson.com${path}`);
const o = await r.json();
console.log('node fetchDummmy:', o.id);
return new ivm.ExternalCopy(o);
}
));
const fn = await context.eval(`
(async function onUserUpdated() {
const user = await fetchDummmy.applySyncPromise(null, ['/users/1']);
log('isolate:', user.copy());
return user;
})`, { reference: true });
const value = await fn.apply(null, [], { result: { promise: true, copy: true } })
console.log('node result:', value);
I didn't manage to have it working without Tried something like
but |
It seems that whenever passing an object as context value it fails
The text was updated successfully, but these errors were encountered: