feat(ScriptExecutor): implement resolveIsolatedVmModule function and add unit tests
This commit is contained in:
parent
aa408841a5
commit
004edc9d27
2 changed files with 54 additions and 7 deletions
|
|
@ -1,10 +1,34 @@
|
|||
import * as ivm from 'isolated-vm';
|
||||
import ivmImport from 'isolated-vm';
|
||||
import type { Context, Isolate, Reference } from 'isolated-vm';
|
||||
import { ScriptContextData } from '../../../shared/types';
|
||||
import { logger } from '../utils/logger';
|
||||
|
||||
const SCRIPT_TIMEOUT_MS = 5000;
|
||||
const MEMORY_LIMIT_MB = 50;
|
||||
|
||||
type IsolatedVmModule = typeof import('isolated-vm');
|
||||
|
||||
export function resolveIsolatedVmModule(moduleValue: unknown): IsolatedVmModule {
|
||||
if (moduleValue && typeof moduleValue === 'object' && 'Isolate' in moduleValue) {
|
||||
return moduleValue as IsolatedVmModule;
|
||||
}
|
||||
|
||||
if (
|
||||
moduleValue &&
|
||||
typeof moduleValue === 'object' &&
|
||||
'default' in moduleValue &&
|
||||
moduleValue.default &&
|
||||
typeof moduleValue.default === 'object' &&
|
||||
'Isolate' in moduleValue.default
|
||||
) {
|
||||
return moduleValue.default as IsolatedVmModule;
|
||||
}
|
||||
|
||||
throw new Error('isolated-vm module did not expose an Isolate constructor');
|
||||
}
|
||||
|
||||
const ivm = resolveIsolatedVmModule(ivmImport);
|
||||
|
||||
/**
|
||||
* Strip `export` keywords from user script code so it can run in eval() context.
|
||||
* Supports: `export const`, `export function`, `export default`, `export {`.
|
||||
|
|
@ -19,10 +43,10 @@ function preprocessScript(code: string): string {
|
|||
*/
|
||||
export class CompiledScript {
|
||||
private constructor(
|
||||
private isolate: ivm.Isolate,
|
||||
private ctx: ivm.Context,
|
||||
private onRequestRef: ivm.Reference<Function> | null,
|
||||
private onResponseRef: ivm.Reference<Function> | null,
|
||||
private isolate: Isolate,
|
||||
private ctx: Context,
|
||||
private onRequestRef: Reference<Function> | null,
|
||||
private onResponseRef: Reference<Function> | null,
|
||||
) {}
|
||||
|
||||
get hasOnRequest(): boolean {
|
||||
|
|
@ -78,10 +102,10 @@ export class CompiledScript {
|
|||
) as boolean;
|
||||
|
||||
const onRequestRef = hasOnRequest
|
||||
? await ctx.eval('onRequest', { timeout: SCRIPT_TIMEOUT_MS, reference: true }) as ivm.Reference<Function>
|
||||
? await ctx.eval('onRequest', { timeout: SCRIPT_TIMEOUT_MS, reference: true }) as Reference<Function>
|
||||
: null;
|
||||
const onResponseRef = hasOnResponse
|
||||
? await ctx.eval('onResponse', { timeout: SCRIPT_TIMEOUT_MS, reference: true }) as ivm.Reference<Function>
|
||||
? await ctx.eval('onResponse', { timeout: SCRIPT_TIMEOUT_MS, reference: true }) as Reference<Function>
|
||||
: null;
|
||||
|
||||
return new CompiledScript(isolate, ctx, onRequestRef, onResponseRef);
|
||||
|
|
|
|||
23
server/tests/unit/script-executor.test.ts
Normal file
23
server/tests/unit/script-executor.test.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
import { resolveIsolatedVmModule } from '../../src/services/ScriptExecutor';
|
||||
|
||||
describe('resolveIsolatedVmModule', () => {
|
||||
it('accepts a direct isolated-vm export object', () => {
|
||||
const fakeModule = { Isolate: class FakeIsolate {} };
|
||||
|
||||
expect(resolveIsolatedVmModule(fakeModule)).toBe(fakeModule);
|
||||
});
|
||||
|
||||
it('accepts a default-wrapped isolated-vm export object', () => {
|
||||
const fakeModule = { Isolate: class FakeIsolate {} };
|
||||
const wrappedModule = { default: fakeModule };
|
||||
|
||||
expect(resolveIsolatedVmModule(wrappedModule)).toBe(fakeModule);
|
||||
});
|
||||
|
||||
it('throws when Isolate is unavailable', () => {
|
||||
expect(() => resolveIsolatedVmModule({})).toThrow(
|
||||
'isolated-vm module did not expose an Isolate constructor',
|
||||
);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue