Skip to content
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

Browser support #91

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [Unreleased][unreleased]

- Add browser polyfill
- Using optional chaining operator

## [1.2.1][] - 2022-07-07
Expand Down
1 change: 1 addition & 0 deletions dist/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"extends": "../.eslintrc.json",
"parserOptions": {
"sourceType": "module"
}
Expand Down
25 changes: 25 additions & 0 deletions dist/metavm.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export type Context = {
[k: string]: unknown & ({ bind?: never } | { call?: never });
};

export type ScriptOptions = {
timeout: number;
isWorker: boolean;
context: Context;
};

export function createContext(context?: Context): Context;

export class MetaScript {
constructor(name: string, src: string, options?: ScriptOptions);
name: string;
script: string;
context: Context;
exports: any;
}

export function createScript(
name: string,
src: string,
options?: ScriptOptions,
): MetaScript;
1 change: 0 additions & 1 deletion dist/metavm.js

This file was deleted.

90 changes: 90 additions & 0 deletions dist/metavm.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
const script = `onmessage = (e) => {
const func =
new Function(\`return \${e.data.source}\`)()(...e.data.values);
const value = typeof func === 'function' ? func() : func;
postMessage({
value: value,
name: e.data.name,
});
};`;

const RUN_OPTIONS = {
timeout: 1000,
inWorker: true,
};

class VMWorker {
constructor() {
this.calls = new Map();
this.worker = new Worker(URL.createObjectURL(new Blob([script])));
this.worker.addEventListener('message', (event) => {
const { name, value } = event.data;
if (!this.calls.has(name)) return;
if (value) this.calls.get(name).resolve(value);
else this.calls.get(name).reject(`Couldn't execute script ${name}`);
});
}

execScript(name, script, context, { inWorker, timeout }) {
const source = `(${Object.keys(context)}) => ${script}`;
const values = Object.values(context);
if (!inWorker) {
const func = Function(`return ${source}`)()(...values);
return typeof func === 'function' ? func() : func;
}
return new Promise((resolve, reject) => {
this.worker.postMessage({
name,
source,
values,
});
this.calls.set(name, { resolve, reject });
setTimeout(() => {
reject('Response timeout');
}, timeout);
});
}
}

const worker = new VMWorker();

// adding it to keep compability with packages that already using it
// most of the browser apis are already available when running script
// can be used for additional libriries
const COMMON_CONTEXT = Object.freeze({});

const EMPTY_CONTEXT = Object.freeze({});

const createContext = (context) => {
if (context === undefined) return EMPTY_CONTEXT;
return context;
};

class MetaScript {
constructor(name, src, options = {}) {
this.name = name;
this.src = src;
this.runOptions = { ...RUN_OPTIONS, ...options };

this.context = options.context ? options.context : createContext();
}

get exports() {
return worker.execScript(
this.name,
this.src,
this.context,
this.runOptions,
);
}
}

const createScript = (name, src, options) => new MetaScript(name, src, options);

export {
createContext,
createScript,
MetaScript,
EMPTY_CONTEXT,
COMMON_CONTEXT,
};