feat: extend platform API support for experimental scripting sandbox (#5097)
This commit is contained in:
parent
935bc10c0b
commit
aab2924139
18 changed files with 560 additions and 541 deletions
|
|
@ -180,6 +180,44 @@ describe("hopp test [options] <file_path_or_id>", { timeout: 100000 }, () => {
|
|||
expect(error).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
test("Successfully display console logs and recognizes platform APIs in the experimental scripting sandbox", async () => {
|
||||
const args = `test ${getTestJsonFilePath(
|
||||
"test-scripting-sandbox-modes-coll.json",
|
||||
"collection"
|
||||
)}`;
|
||||
const { error, stdout } = await runCLI(args);
|
||||
|
||||
expect(error).toBeNull();
|
||||
|
||||
const expectedStaticParts = [
|
||||
"https://example.com/path?foo=bar&baz=qux",
|
||||
"'0': 72",
|
||||
"'12': 33",
|
||||
"Decoded: Hello, world!",
|
||||
"Hello after 1s",
|
||||
];
|
||||
|
||||
// Assert that each stable part appears in the output
|
||||
expectedStaticParts.forEach((part) => {
|
||||
expect(stdout).toContain(part);
|
||||
});
|
||||
|
||||
const every500msCount = (stdout.match(/Every 500ms/g) || []).length;
|
||||
expect(every500msCount).toBe(3);
|
||||
});
|
||||
|
||||
test("Fails to display console logs and recognize platform APIs in the legacy scripting sandbox", async () => {
|
||||
const args = `test ${getTestJsonFilePath(
|
||||
"test-scripting-sandbox-modes-coll.json",
|
||||
"collection"
|
||||
)} --legacy-sandbox`;
|
||||
const { error, stdout } = await runCLI(args);
|
||||
|
||||
expect(error).toBeTruthy();
|
||||
expect(stdout).not.toContain("https://example.com/path?foo=bar&baz=qux");
|
||||
expect(stdout).not.toContain("Encoded");
|
||||
});
|
||||
});
|
||||
|
||||
test("Ensures tests run in sequence order based on request path", async () => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"v": 7,
|
||||
"id": "cmb4vtsqh00nxwvqryk6jmnaz",
|
||||
"name": "test-scripting-sandbox-modes",
|
||||
"folders": [],
|
||||
"requests": [
|
||||
{
|
||||
"v": "12",
|
||||
"name": "sample-req",
|
||||
"method": "GET",
|
||||
"endpoint": "https://echo.hoppscotch.io",
|
||||
"params": [],
|
||||
"headers": [],
|
||||
"preRequestScript": "const url = new URL('https://example.com/path?foo=bar');\nurl.searchParams.set('baz', 'qux');\nurl.toString(); // 'https://example.com/path?foo=bar&baz=qux'\n\nconsole.debug(url)\n\nconst encoder = new TextEncoder();\n\nconst text = \"Hello, world!\";\n\nconst encoded = encoder.encode(text);\nconsole.log(\"Encoded:\", encoded);\n\nconst decoder = new TextDecoder();\n\nconst decoded = decoder.decode(encoded);\nconsole.log(\"Decoded:\", decoded);\n\nsetTimeout(() => console.log(\"Hello after 1s\"), 1000);\n \nconst intervalId = setInterval(() => console.log(\"Every 500ms\"), 500);\n\nsetTimeout(() => clearInterval(intervalId), 2000);",
|
||||
"testScript": "console.log(JSON.stringify(pw, null, 2))\n\n\npw.test(\"Sample assertion\", ()=> {\n pw.expect(pw.response.status).toBeLevel2xx()\n \tconsole.log(\"Status code received is \", pw.response.status)\n});\n\n",
|
||||
"auth": {
|
||||
"authType": "inherit",
|
||||
"authActive": true
|
||||
},
|
||||
"body": {
|
||||
"contentType": null,
|
||||
"body": null
|
||||
},
|
||||
"requestVariables": [],
|
||||
"responses": {}
|
||||
}
|
||||
],
|
||||
"auth": {
|
||||
"authType": "inherit",
|
||||
"authActive": true
|
||||
},
|
||||
"headers": [],
|
||||
"_ref_id": "coll_mb4vvacd_e015964c-b5c8-4b90-a564-4a8b62bc1631"
|
||||
}
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
"dependencies": {
|
||||
"@hoppscotch/data": "workspace:^",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"faraday-cage": "0.0.15",
|
||||
"faraday-cage": "0.0.16",
|
||||
"fp-ts": "2.16.9",
|
||||
"lodash": "4.17.21",
|
||||
"lodash-es": "4.17.21"
|
||||
|
|
|
|||
|
|
@ -9,52 +9,37 @@
|
|||
resolve: (key) => inputs.envResolve(key),
|
||||
},
|
||||
expect: (expectVal) => {
|
||||
return {
|
||||
const isDateInstance = expectVal instanceof Date
|
||||
|
||||
const expectation = {
|
||||
toBe: (expectedVal) => inputs.expectToBe(expectVal, expectedVal),
|
||||
toBeLevel2xx: (expectedVal) =>
|
||||
inputs.expectToBeLevel2xx(expectVal, expectedVal),
|
||||
toBeLevel3xx: (expectedVal) =>
|
||||
inputs.expectToBeLevel3xx(expectVal, expectedVal),
|
||||
toBeLevel4xx: (expectedVal) =>
|
||||
inputs.expectToBeLevel4xx(expectVal, expectedVal),
|
||||
toBeLevel5xx: (expectedVal) =>
|
||||
inputs.expectToBeLevel5xx(expectVal, expectedVal),
|
||||
toBeType: (expectedVal) => {
|
||||
const isExpectValDateInstance = expectVal instanceof Date
|
||||
return inputs.expectToBeType(
|
||||
expectVal,
|
||||
expectedVal,
|
||||
isExpectValDateInstance
|
||||
)
|
||||
},
|
||||
toHaveLength: (expectedVal) =>
|
||||
inputs.expectToHaveLength(expectVal, expectedVal),
|
||||
toInclude: (expectedVal) =>
|
||||
inputs.expectToInclude(expectVal, expectedVal),
|
||||
not: {
|
||||
toBe: (expectedVal) => inputs.expectNotToBe(expectVal, expectedVal),
|
||||
toBeLevel2xx: (expectedVal) =>
|
||||
inputs.expectNotToBeLevel2xx(expectVal, expectedVal),
|
||||
toBeLevel3xx: (expectedVal) =>
|
||||
inputs.expectNotToBeLevel3xx(expectVal, expectedVal),
|
||||
toBeLevel4xx: (expectedVal) =>
|
||||
inputs.expectNotToBeLevel4xx(expectVal, expectedVal),
|
||||
toBeLevel5xx: (expectedVal) =>
|
||||
inputs.expectNotToBeLevel5xx(expectVal, expectedVal),
|
||||
toBeType: (expectedVal) => {
|
||||
const isExpectValDateInstance = expectVal instanceof Date
|
||||
return inputs.expectNotToBeType(
|
||||
expectVal,
|
||||
expectedVal,
|
||||
isExpectValDateInstance
|
||||
)
|
||||
},
|
||||
toHaveLength: (expectedVal) =>
|
||||
inputs.expectNotToHaveLength(expectVal, expectedVal),
|
||||
toInclude: (expectedVal) =>
|
||||
inputs.expectNotToInclude(expectVal, expectedVal),
|
||||
},
|
||||
toBeLevel2xx: () => inputs.expectToBeLevel2xx(expectVal),
|
||||
toBeLevel3xx: () => inputs.expectToBeLevel3xx(expectVal),
|
||||
toBeLevel4xx: () => inputs.expectToBeLevel4xx(expectVal),
|
||||
toBeLevel5xx: () => inputs.expectToBeLevel5xx(expectVal),
|
||||
toBeType: (expectedType) =>
|
||||
inputs.expectToBeType(expectVal, expectedType, isDateInstance),
|
||||
toHaveLength: (expectedLength) =>
|
||||
inputs.expectToHaveLength(expectVal, expectedLength),
|
||||
toInclude: (needle) => inputs.expectToInclude(expectVal, needle),
|
||||
}
|
||||
|
||||
Object.defineProperty(expectation, "not", {
|
||||
get: () => ({
|
||||
toBe: (expectedVal) => inputs.expectNotToBe(expectVal, expectedVal),
|
||||
toBeLevel2xx: () => inputs.expectNotToBeLevel2xx(expectVal),
|
||||
toBeLevel3xx: () => inputs.expectNotToBeLevel3xx(expectVal),
|
||||
toBeLevel4xx: () => inputs.expectNotToBeLevel4xx(expectVal),
|
||||
toBeLevel5xx: () => inputs.expectNotToBeLevel5xx(expectVal),
|
||||
toBeType: (expectedType) =>
|
||||
inputs.expectNotToBeType(expectVal, expectedType, isDateInstance),
|
||||
toHaveLength: (expectedLength) =>
|
||||
inputs.expectNotToHaveLength(expectVal, expectedLength),
|
||||
toInclude: (needle) => inputs.expectNotToInclude(expectVal, needle),
|
||||
}),
|
||||
})
|
||||
|
||||
return expectation
|
||||
},
|
||||
test: (descriptor, testFn) => {
|
||||
inputs.preTest(descriptor)
|
||||
|
|
|
|||
68
packages/hoppscotch-js-sandbox/src/cage-modules/default.ts
Normal file
68
packages/hoppscotch-js-sandbox/src/cage-modules/default.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import {
|
||||
blobPolyfill,
|
||||
ConsoleEntry,
|
||||
console as ConsoleModule,
|
||||
crypto,
|
||||
encoding,
|
||||
esmModuleLoader,
|
||||
fetch,
|
||||
urlPolyfill,
|
||||
timers,
|
||||
} from "faraday-cage/modules"
|
||||
|
||||
type DefaultModulesConfig = {
|
||||
handleConsoleEntry: (consoleEntries: ConsoleEntry) => void
|
||||
}
|
||||
|
||||
export const defaultModules = (config?: DefaultModulesConfig) => {
|
||||
return [
|
||||
urlPolyfill,
|
||||
blobPolyfill,
|
||||
ConsoleModule({
|
||||
onLog(level, ...args) {
|
||||
console[level](...args)
|
||||
|
||||
config?.handleConsoleEntry({
|
||||
type: level,
|
||||
args,
|
||||
timestamp: Date.now(),
|
||||
})
|
||||
},
|
||||
onCount(...args) {
|
||||
console.count(args[0])
|
||||
},
|
||||
onTime(...args) {
|
||||
console.timeEnd(args[0])
|
||||
},
|
||||
onTimeLog(...args) {
|
||||
console.timeLog(...args)
|
||||
},
|
||||
onGroup(...args) {
|
||||
console.group(...args)
|
||||
},
|
||||
onGroupEnd(...args) {
|
||||
console.groupEnd(...args)
|
||||
},
|
||||
onClear(...args) {
|
||||
console.clear(...args)
|
||||
},
|
||||
onAssert(...args) {
|
||||
console.assert(...args)
|
||||
},
|
||||
onDir(...args) {
|
||||
console.dir(...args)
|
||||
},
|
||||
onTable(...args) {
|
||||
console.table(...args)
|
||||
},
|
||||
}),
|
||||
crypto({
|
||||
cryptoImpl: globalThis.crypto,
|
||||
}),
|
||||
|
||||
esmModuleLoader,
|
||||
fetch(),
|
||||
encoding(),
|
||||
timers(),
|
||||
]
|
||||
}
|
||||
2
packages/hoppscotch-js-sandbox/src/cage-modules/index.ts
Normal file
2
packages/hoppscotch-js-sandbox/src/cage-modules/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export { defaultModules } from "./default"
|
||||
export { pwPostRequestModule, pwPreRequestModule } from "./pw"
|
||||
|
|
@ -261,8 +261,8 @@ const createPwModule = (
|
|||
})
|
||||
}
|
||||
|
||||
export const pwPostRequestModule = (config: PwPostRequestModuleConfig) =>
|
||||
createPwModule("post", postRequestBootstrapCode, config)
|
||||
|
||||
export const pwPreRequestModule = (config: PwPreRequestModuleConfig) =>
|
||||
createPwModule("pre", preRequestBootstrapCode, config)
|
||||
|
||||
export const pwPostRequestModule = (config: PwPostRequestModuleConfig) =>
|
||||
createPwModule("post", postRequestBootstrapCode, config)
|
||||
|
|
|
|||
|
|
@ -1,165 +0,0 @@
|
|||
import { FaradayCage } from "faraday-cage"
|
||||
import {
|
||||
blobPolyfill,
|
||||
console as ConsoleModule,
|
||||
crypto,
|
||||
esmModuleLoader,
|
||||
fetch,
|
||||
} from "faraday-cage/modules"
|
||||
import { pipe } from "fp-ts/function"
|
||||
import * as TE from "fp-ts/lib/TaskEither"
|
||||
import type ivmT from "isolated-vm"
|
||||
import { cloneDeep } from "lodash"
|
||||
import { pwPreRequestModule } from "~/cage-modules/pw"
|
||||
|
||||
import { createRequire } from "module"
|
||||
import { TestResult } from "~/types"
|
||||
|
||||
import { getPreRequestScriptMethods } from "~/shared-utils"
|
||||
import { getSerializedAPIMethods } from "./utils"
|
||||
|
||||
const nodeRequire = createRequire(import.meta.url)
|
||||
const ivm = nodeRequire("isolated-vm")
|
||||
|
||||
export const runPreRequestScript = (
|
||||
preRequestScript: string,
|
||||
envs: TestResult["envs"],
|
||||
experimentalScriptingSandbox = true
|
||||
): TE.TaskEither<string, TestResult["envs"]> => {
|
||||
if (!experimentalScriptingSandbox) {
|
||||
return pipe(
|
||||
TE.tryCatch(
|
||||
async () => {
|
||||
const isolate: ivmT.Isolate = new ivm.Isolate()
|
||||
const context = await isolate.createContext()
|
||||
return { isolate, context }
|
||||
},
|
||||
(reason) => `Context initialization failed: ${reason}`
|
||||
),
|
||||
TE.chain(({ isolate, context }) =>
|
||||
pipe(
|
||||
TE.tryCatch(
|
||||
async () => {
|
||||
const jail = context.global
|
||||
const { pw, updatedEnvs } = getPreRequestScriptMethods(envs)
|
||||
const serializedAPIMethods = getSerializedAPIMethods(pw)
|
||||
jail.setSync("serializedAPIMethods", serializedAPIMethods, {
|
||||
copy: true,
|
||||
})
|
||||
jail.setSync("atob", atob)
|
||||
jail.setSync("btoa", btoa)
|
||||
// Methods in the isolate context can't be invoked straightaway
|
||||
const finalScript = `
|
||||
const pw = new Proxy(serializedAPIMethods, {
|
||||
get: (pwObjTarget, pwObjProp) => {
|
||||
const topLevelEntry = pwObjTarget[pwObjProp]
|
||||
// "pw.env" set of API methods
|
||||
if (topLevelEntry && typeof topLevelEntry === "object") {
|
||||
return new Proxy(topLevelEntry, {
|
||||
get: (subTarget, subProp) => {
|
||||
const subLevelProperty = subTarget[subProp]
|
||||
if (subLevelProperty && subLevelProperty.typeof === "function") {
|
||||
return (...args) => subLevelProperty.applySync(null, args)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
${preRequestScript}
|
||||
`
|
||||
// Create a script and compile it
|
||||
const script = await isolate.compileScript(finalScript)
|
||||
// Run the pre-request script in the provided context
|
||||
await script.run(context)
|
||||
return updatedEnvs
|
||||
},
|
||||
(reason) => reason
|
||||
),
|
||||
TE.fold(
|
||||
(error) => TE.left(`Script execution failed: ${error}`),
|
||||
(result) =>
|
||||
pipe(
|
||||
TE.tryCatch(
|
||||
async () => {
|
||||
await isolate.dispose()
|
||||
return result
|
||||
},
|
||||
(disposeError) => `Isolate disposal failed: ${disposeError}`
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return pipe(
|
||||
TE.tryCatch(
|
||||
async (): Promise<TestResult["envs"]> => {
|
||||
let finalEnvs = envs
|
||||
|
||||
const cage = await FaradayCage.create()
|
||||
|
||||
const result = await cage.runCode(preRequestScript, [
|
||||
pwPreRequestModule({
|
||||
envs: cloneDeep(envs),
|
||||
handleSandboxResults: ({ envs }) => {
|
||||
finalEnvs = envs
|
||||
},
|
||||
}),
|
||||
blobPolyfill,
|
||||
ConsoleModule({
|
||||
onLog(...args) {
|
||||
console[args[0]](...args)
|
||||
},
|
||||
onCount(...args) {
|
||||
console.count(args[0])
|
||||
},
|
||||
onTime(...args) {
|
||||
console.timeEnd(args[0])
|
||||
},
|
||||
onTimeLog(...args) {
|
||||
console.timeLog(...args)
|
||||
},
|
||||
onGroup(...args) {
|
||||
console.group(...args)
|
||||
},
|
||||
onGroupEnd(...args) {
|
||||
console.groupEnd(...args)
|
||||
},
|
||||
onClear(...args) {
|
||||
console.clear(...args)
|
||||
},
|
||||
onAssert(...args) {
|
||||
console.assert(...args)
|
||||
},
|
||||
onDir(...args) {
|
||||
console.dir(...args)
|
||||
},
|
||||
onTable(...args) {
|
||||
console.table(...args)
|
||||
},
|
||||
}),
|
||||
crypto(),
|
||||
esmModuleLoader,
|
||||
fetch(),
|
||||
])
|
||||
|
||||
if (result.type === "error") {
|
||||
throw result.err
|
||||
}
|
||||
|
||||
return finalEnvs
|
||||
},
|
||||
(error) => {
|
||||
if (error !== null && typeof error === "object" && "message" in error) {
|
||||
const reason = `${"name" in error ? error.name : ""}: ${error.message}`
|
||||
return `Script execution failed: ${reason}`
|
||||
}
|
||||
|
||||
return `Script execution failed: ${String(error)}`
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import { FaradayCage } from "faraday-cage"
|
||||
import { pipe } from "fp-ts/function"
|
||||
import * as TE from "fp-ts/lib/TaskEither"
|
||||
import { cloneDeep } from "lodash"
|
||||
|
||||
import { defaultModules, pwPreRequestModule } from "~/cage-modules"
|
||||
import { TestResult } from "~/types"
|
||||
|
||||
export const runPreRequestScriptWithFaradayCage = (
|
||||
preRequestScript: string,
|
||||
envs: TestResult["envs"]
|
||||
): TE.TaskEither<string, TestResult["envs"]> => {
|
||||
return pipe(
|
||||
TE.tryCatch(
|
||||
async (): Promise<TestResult["envs"]> => {
|
||||
let finalEnvs = envs
|
||||
|
||||
const cage = await FaradayCage.create()
|
||||
|
||||
const result = await cage.runCode(preRequestScript, [
|
||||
...defaultModules(),
|
||||
|
||||
pwPreRequestModule({
|
||||
envs: cloneDeep(envs),
|
||||
handleSandboxResults: ({ envs }) => {
|
||||
finalEnvs = envs
|
||||
},
|
||||
}),
|
||||
])
|
||||
|
||||
if (result.type === "error") {
|
||||
throw result.err
|
||||
}
|
||||
|
||||
return finalEnvs
|
||||
},
|
||||
(error) => {
|
||||
if (error !== null && typeof error === "object" && "message" in error) {
|
||||
const reason = `${"name" in error ? error.name : ""}: ${error.message}`
|
||||
return `Script execution failed: ${reason}`
|
||||
}
|
||||
|
||||
return `Script execution failed: ${String(error)}`
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
14
packages/hoppscotch-js-sandbox/src/node/pre-request/index.ts
Normal file
14
packages/hoppscotch-js-sandbox/src/node/pre-request/index.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import * as TE from "fp-ts/lib/TaskEither"
|
||||
import { TestResult } from "~/types"
|
||||
|
||||
import { runPreRequestScriptWithFaradayCage } from "./experimental"
|
||||
import { runPreRequestScriptWithIsolatedVm } from "./legacy"
|
||||
|
||||
export const runPreRequestScript = (
|
||||
preRequestScript: string,
|
||||
envs: TestResult["envs"],
|
||||
experimentalScriptingSandbox = true
|
||||
): TE.TaskEither<string, TestResult["envs"]> =>
|
||||
experimentalScriptingSandbox
|
||||
? runPreRequestScriptWithFaradayCage(preRequestScript, envs)
|
||||
: runPreRequestScriptWithIsolatedVm(preRequestScript, envs)
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
import { pipe } from "fp-ts/function"
|
||||
import * as TE from "fp-ts/lib/TaskEither"
|
||||
import type ivmT from "isolated-vm"
|
||||
import { createRequire } from "module"
|
||||
|
||||
import { getPreRequestScriptMethods } from "~/shared-utils"
|
||||
import { TestResult } from "~/types"
|
||||
import { getSerializedAPIMethods } from "../utils"
|
||||
|
||||
const nodeRequire = createRequire(import.meta.url)
|
||||
const ivm = nodeRequire("isolated-vm")
|
||||
|
||||
export const runPreRequestScriptWithIsolatedVm = (
|
||||
preRequestScript: string,
|
||||
envs: TestResult["envs"]
|
||||
): TE.TaskEither<string, TestResult["envs"]> => {
|
||||
return pipe(
|
||||
TE.tryCatch(
|
||||
async () => {
|
||||
const isolate: ivmT.Isolate = new ivm.Isolate()
|
||||
const context = await isolate.createContext()
|
||||
return { isolate, context }
|
||||
},
|
||||
(reason) => `Context initialization failed: ${reason}`
|
||||
),
|
||||
TE.chain(({ isolate, context }) =>
|
||||
pipe(
|
||||
TE.tryCatch(
|
||||
async () => {
|
||||
const jail = context.global
|
||||
const { pw, updatedEnvs } = getPreRequestScriptMethods(envs)
|
||||
const serializedAPIMethods = getSerializedAPIMethods(pw)
|
||||
jail.setSync("serializedAPIMethods", serializedAPIMethods, {
|
||||
copy: true,
|
||||
})
|
||||
jail.setSync("atob", atob)
|
||||
jail.setSync("btoa", btoa)
|
||||
// Methods in the isolate context can't be invoked straightaway
|
||||
const finalScript = `
|
||||
const pw = new Proxy(serializedAPIMethods, {
|
||||
get: (pwObjTarget, pwObjProp) => {
|
||||
const topLevelEntry = pwObjTarget[pwObjProp]
|
||||
// "pw.env" set of API methods
|
||||
if (topLevelEntry && typeof topLevelEntry === "object") {
|
||||
return new Proxy(topLevelEntry, {
|
||||
get: (subTarget, subProp) => {
|
||||
const subLevelProperty = subTarget[subProp]
|
||||
if (subLevelProperty && subLevelProperty.typeof === "function") {
|
||||
return (...args) => subLevelProperty.applySync(null, args)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
${preRequestScript}
|
||||
`
|
||||
// Create a script and compile it
|
||||
const script = await isolate.compileScript(finalScript)
|
||||
// Run the pre-request script in the provided context
|
||||
await script.run(context)
|
||||
return updatedEnvs
|
||||
},
|
||||
(reason) => reason
|
||||
),
|
||||
TE.fold(
|
||||
(error) => TE.left(`Script execution failed: ${error}`),
|
||||
(result) =>
|
||||
pipe(
|
||||
TE.tryCatch(
|
||||
async () => {
|
||||
await isolate.dispose()
|
||||
return result
|
||||
},
|
||||
(disposeError) => `Isolate disposal failed: ${disposeError}`
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
import { FaradayCage } from "faraday-cage"
|
||||
import * as TE from "fp-ts/TaskEither"
|
||||
import { pipe } from "fp-ts/function"
|
||||
import { cloneDeep } from "lodash"
|
||||
|
||||
import { defaultModules, pwPostRequestModule } from "~/cage-modules"
|
||||
import { TestDescriptor, TestResponse, TestResult } from "~/types"
|
||||
|
||||
export const runTestScriptWithFaradayCage = (
|
||||
testScript: string,
|
||||
envs: TestResult["envs"],
|
||||
response: TestResponse
|
||||
): TE.TaskEither<string, TestResult> => {
|
||||
return pipe(
|
||||
TE.tryCatch(
|
||||
async (): Promise<TestResult> => {
|
||||
const testRunStack: TestDescriptor[] = [
|
||||
{ descriptor: "root", expectResults: [], children: [] },
|
||||
]
|
||||
|
||||
let finalEnvs = envs
|
||||
let finalTestResults = testRunStack
|
||||
|
||||
const cage = await FaradayCage.create()
|
||||
|
||||
const result = await cage.runCode(testScript, [
|
||||
...defaultModules(),
|
||||
|
||||
pwPostRequestModule({
|
||||
envs: cloneDeep(envs),
|
||||
testRunStack: cloneDeep(testRunStack),
|
||||
response,
|
||||
handleSandboxResults: ({ envs, testRunStack }) => {
|
||||
finalEnvs = envs
|
||||
finalTestResults = testRunStack
|
||||
},
|
||||
}),
|
||||
])
|
||||
|
||||
if (result.type === "error") {
|
||||
throw result.err
|
||||
}
|
||||
|
||||
return {
|
||||
tests: finalTestResults,
|
||||
envs: finalEnvs,
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
if (error !== null && typeof error === "object" && "message" in error) {
|
||||
const reason = `${"name" in error ? error.name : ""}: ${error.message}`
|
||||
return `Script execution failed: ${reason}`
|
||||
}
|
||||
|
||||
return `Script execution failed: ${String(error)}`
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
24
packages/hoppscotch-js-sandbox/src/node/test-runner/index.ts
Normal file
24
packages/hoppscotch-js-sandbox/src/node/test-runner/index.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import * as E from "fp-ts/Either"
|
||||
import * as TE from "fp-ts/TaskEither"
|
||||
|
||||
import { preventCyclicObjects } from "~/shared-utils"
|
||||
import { TestResponse, TestResult } from "~/types"
|
||||
import { runTestScriptWithFaradayCage } from "./experimental"
|
||||
import { runTestScriptWithIsolatedVm } from "./legacy"
|
||||
|
||||
export const runTestScript = (
|
||||
testScript: string,
|
||||
envs: TestResult["envs"],
|
||||
response: TestResponse,
|
||||
experimentalScriptingSandbox = true
|
||||
): TE.TaskEither<string, TestResult> => {
|
||||
const responseObjHandle = preventCyclicObjects<TestResponse>(response)
|
||||
|
||||
if (E.isLeft(responseObjHandle)) {
|
||||
return TE.left(`Response marshalling failed: ${responseObjHandle.left}`)
|
||||
}
|
||||
|
||||
return experimentalScriptingSandbox
|
||||
? runTestScriptWithFaradayCage(testScript, envs, responseObjHandle.right)
|
||||
: runTestScriptWithIsolatedVm(testScript, envs, responseObjHandle.right)
|
||||
}
|
||||
|
|
@ -1,159 +1,19 @@
|
|||
import { FaradayCage } from "faraday-cage"
|
||||
import {
|
||||
blobPolyfill,
|
||||
console as ConsoleModule,
|
||||
crypto,
|
||||
esmModuleLoader,
|
||||
fetch,
|
||||
} from "faraday-cage/modules"
|
||||
import * as E from "fp-ts/Either"
|
||||
import * as TE from "fp-ts/TaskEither"
|
||||
import { pipe } from "fp-ts/function"
|
||||
import type ivmT from "isolated-vm"
|
||||
import { cloneDeep } from "lodash"
|
||||
import { createRequire } from "module"
|
||||
import { pwPostRequestModule } from "~/cage-modules/pw"
|
||||
|
||||
import {
|
||||
getTestRunnerScriptMethods,
|
||||
preventCyclicObjects,
|
||||
} from "~/shared-utils"
|
||||
import { TestDescriptor, TestResponse, TestResult } from "~/types"
|
||||
import { getSerializedAPIMethods } from "./utils"
|
||||
import { TestResponse, TestResult } from "~/types"
|
||||
import { getSerializedAPIMethods } from "../utils"
|
||||
|
||||
const nodeRequire = createRequire(import.meta.url)
|
||||
const ivm = nodeRequire("isolated-vm")
|
||||
|
||||
export const runTestScript = (
|
||||
testScript: string,
|
||||
envs: TestResult["envs"],
|
||||
response: TestResponse,
|
||||
experimentalScriptingSandbox = true
|
||||
): TE.TaskEither<string, TestResult> => {
|
||||
const responseObjHandle = preventCyclicObjects(response)
|
||||
|
||||
if (E.isLeft(responseObjHandle)) {
|
||||
return TE.left(`Response marshalling failed: ${responseObjHandle.left}`)
|
||||
}
|
||||
|
||||
if (!experimentalScriptingSandbox) {
|
||||
return pipe(
|
||||
TE.tryCatch(
|
||||
async () => {
|
||||
const isolate: ivmT.Isolate = new ivm.Isolate()
|
||||
const context = await isolate.createContext()
|
||||
return { isolate, context }
|
||||
},
|
||||
(reason) => `Context initialization failed: ${reason}`
|
||||
),
|
||||
TE.chain(({ isolate, context }) =>
|
||||
pipe(
|
||||
TE.tryCatch(
|
||||
async () =>
|
||||
executeScriptInContext(
|
||||
testScript,
|
||||
envs,
|
||||
response,
|
||||
isolate,
|
||||
context
|
||||
),
|
||||
(reason) => `Script execution failed: ${reason}`
|
||||
),
|
||||
TE.chain((result) =>
|
||||
TE.tryCatch(
|
||||
async () => {
|
||||
await isolate.dispose()
|
||||
return result
|
||||
},
|
||||
(disposeReason) => `Isolate disposal failed: ${disposeReason}`
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return pipe(
|
||||
TE.tryCatch(
|
||||
async (): Promise<TestResult> => {
|
||||
const testRunStack: TestDescriptor[] = [
|
||||
{ descriptor: "root", expectResults: [], children: [] },
|
||||
]
|
||||
|
||||
let finalEnvs = envs
|
||||
let finalTestResults = testRunStack
|
||||
|
||||
const cage = await FaradayCage.create()
|
||||
|
||||
const result = await cage.runCode(testScript, [
|
||||
pwPostRequestModule({
|
||||
envs: cloneDeep(envs),
|
||||
testRunStack: cloneDeep(testRunStack),
|
||||
response,
|
||||
handleSandboxResults: ({ envs, testRunStack }) => {
|
||||
finalEnvs = envs
|
||||
finalTestResults = testRunStack
|
||||
},
|
||||
}),
|
||||
blobPolyfill,
|
||||
ConsoleModule({
|
||||
onLog(...args) {
|
||||
console[args[0]](...args)
|
||||
},
|
||||
onCount(...args) {
|
||||
console.count(args[0])
|
||||
},
|
||||
onTime(...args) {
|
||||
console.timeEnd(args[0])
|
||||
},
|
||||
onTimeLog(...args) {
|
||||
console.timeLog(...args)
|
||||
},
|
||||
onGroup(...args) {
|
||||
console.group(...args)
|
||||
},
|
||||
onGroupEnd(...args) {
|
||||
console.groupEnd(...args)
|
||||
},
|
||||
onClear(...args) {
|
||||
console.clear(...args)
|
||||
},
|
||||
onAssert(...args) {
|
||||
console.assert(...args)
|
||||
},
|
||||
onDir(...args) {
|
||||
console.dir(...args)
|
||||
},
|
||||
onTable(...args) {
|
||||
console.table(...args)
|
||||
},
|
||||
}),
|
||||
crypto(),
|
||||
esmModuleLoader,
|
||||
fetch(),
|
||||
])
|
||||
|
||||
if (result.type === "error") {
|
||||
throw result.err
|
||||
}
|
||||
|
||||
return {
|
||||
tests: finalTestResults,
|
||||
envs: finalEnvs,
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
if (error !== null && typeof error === "object" && "message" in error) {
|
||||
const reason = `${"name" in error ? error.name : ""}: ${error.message}`
|
||||
return `Script execution failed: ${reason}`
|
||||
}
|
||||
|
||||
return `Script execution failed: ${String(error)}`
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const executeScriptInContext = (
|
||||
testScript: string,
|
||||
envs: TestResult["envs"],
|
||||
|
|
@ -163,7 +23,7 @@ const executeScriptInContext = (
|
|||
): Promise<TestResult> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Parse response object
|
||||
const responseObjHandle = preventCyclicObjects(response)
|
||||
const responseObjHandle = preventCyclicObjects<TestResponse>(response)
|
||||
if (E.isLeft(responseObjHandle)) {
|
||||
return reject(`Response parsing failed: ${responseObjHandle.left}`)
|
||||
}
|
||||
|
|
@ -315,3 +175,44 @@ const executeScriptInContext = (
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const runTestScriptWithIsolatedVm = (
|
||||
testScript: string,
|
||||
envs: TestResult["envs"],
|
||||
response: TestResponse
|
||||
): TE.TaskEither<string, TestResult> => {
|
||||
return pipe(
|
||||
TE.tryCatch(
|
||||
async () => {
|
||||
const isolate: ivmT.Isolate = new ivm.Isolate()
|
||||
const context = await isolate.createContext()
|
||||
return { isolate, context }
|
||||
},
|
||||
(reason) => `Context initialization failed: ${reason}`
|
||||
),
|
||||
TE.chain(({ isolate, context }) =>
|
||||
pipe(
|
||||
TE.tryCatch(
|
||||
async () =>
|
||||
executeScriptInContext(
|
||||
testScript,
|
||||
envs,
|
||||
response,
|
||||
isolate,
|
||||
context
|
||||
),
|
||||
(reason) => `Script execution failed: ${reason}`
|
||||
),
|
||||
TE.chain((result) =>
|
||||
TE.tryCatch(
|
||||
async () => {
|
||||
await isolate.dispose()
|
||||
return result
|
||||
},
|
||||
(disposeReason) => `Isolate disposal failed: ${disposeReason}`
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -213,9 +213,9 @@ const getResolvedExpectValue = (expectVal: any) => {
|
|||
}
|
||||
}
|
||||
|
||||
export function preventCyclicObjects(
|
||||
obj: Record<string, any>
|
||||
): E.Left<string> | E.Right<Record<string, any>> {
|
||||
export function preventCyclicObjects<T extends object = Record<string, any>>(
|
||||
obj: T
|
||||
): E.Left<string> | E.Right<T> {
|
||||
let jsonString
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,97 +1,52 @@
|
|||
import { FaradayCage } from "faraday-cage"
|
||||
import { ConsoleEntry } from "faraday-cage/modules"
|
||||
import * as E from "fp-ts/Either"
|
||||
|
||||
import { cloneDeep } from "lodash"
|
||||
import { SandboxPreRequestResult, TestResult } from "~/types"
|
||||
|
||||
import { FaradayCage } from "faraday-cage"
|
||||
import {
|
||||
blobPolyfill,
|
||||
ConsoleEntry,
|
||||
console as ConsoleModule,
|
||||
crypto,
|
||||
esmModuleLoader,
|
||||
fetch,
|
||||
} from "faraday-cage/modules"
|
||||
import { cloneDeep } from "lodash"
|
||||
|
||||
import * as TE from "fp-ts/lib/TaskEither"
|
||||
import { pwPreRequestModule } from "~/cage-modules/pw"
|
||||
import { defaultModules, pwPreRequestModule } from "~/cage-modules"
|
||||
|
||||
import Worker from "./worker?worker&inline"
|
||||
|
||||
export const runPreRequestScript = async (
|
||||
const runPreRequestScriptWithWebWorker = (
|
||||
preRequestScript: string,
|
||||
envs: TestResult["envs"],
|
||||
experimentalScriptingSandbox = true
|
||||
envs: TestResult["envs"]
|
||||
): Promise<E.Either<string, SandboxPreRequestResult>> => {
|
||||
return new Promise((resolve) => {
|
||||
const worker = new Worker()
|
||||
|
||||
// Listen for the results from the web worker
|
||||
worker.addEventListener("message", (event: MessageEvent) => {
|
||||
worker.terminate()
|
||||
return resolve(event.data.results)
|
||||
})
|
||||
|
||||
// Send the script to the web worker
|
||||
worker.postMessage({
|
||||
preRequestScript,
|
||||
envs,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const runPreRequestScriptWithFaradayCage = async (
|
||||
preRequestScript: string,
|
||||
envs: TestResult["envs"]
|
||||
): Promise<E.Either<string, SandboxPreRequestResult>> => {
|
||||
const consoleEntries: ConsoleEntry[] = []
|
||||
let finalEnvs = envs
|
||||
|
||||
if (!experimentalScriptingSandbox) {
|
||||
return new Promise((resolve) => {
|
||||
const worker = new Worker()
|
||||
|
||||
// Listen for the results from the web worker
|
||||
worker.addEventListener("message", (event: MessageEvent) =>
|
||||
resolve(event.data.results)
|
||||
)
|
||||
|
||||
// Send the script to the web worker
|
||||
worker.postMessage({
|
||||
preRequestScript,
|
||||
envs,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const cage = await FaradayCage.create()
|
||||
|
||||
const result = await cage.runCode(preRequestScript, [
|
||||
...defaultModules({
|
||||
handleConsoleEntry: (consoleEntry) => consoleEntries.push(consoleEntry),
|
||||
}),
|
||||
|
||||
pwPreRequestModule({
|
||||
envs: cloneDeep(envs),
|
||||
handleSandboxResults: ({ envs }) => {
|
||||
finalEnvs = envs
|
||||
},
|
||||
handleSandboxResults: ({ envs }) => (finalEnvs = envs),
|
||||
}),
|
||||
blobPolyfill,
|
||||
ConsoleModule({
|
||||
onLog(...args) {
|
||||
console[args[0]](...args)
|
||||
},
|
||||
onCount(...args) {
|
||||
console.count(args[0])
|
||||
},
|
||||
onTime(...args) {
|
||||
console.timeEnd(args[0])
|
||||
},
|
||||
onTimeLog(...args) {
|
||||
console.timeLog(...args)
|
||||
},
|
||||
onGroup(...args) {
|
||||
console.group(...args)
|
||||
},
|
||||
onGroupEnd(...args) {
|
||||
console.groupEnd(...args)
|
||||
},
|
||||
onClear(...args) {
|
||||
console.clear(...args)
|
||||
},
|
||||
onAssert(...args) {
|
||||
console.assert(...args)
|
||||
},
|
||||
onDir(...args) {
|
||||
console.dir(...args)
|
||||
},
|
||||
onTable(...args) {
|
||||
console.table(...args)
|
||||
},
|
||||
onFinish(entries) {
|
||||
consoleEntries.push(...entries)
|
||||
},
|
||||
}),
|
||||
crypto(),
|
||||
esmModuleLoader,
|
||||
fetch(),
|
||||
esmModuleLoader,
|
||||
])
|
||||
|
||||
if (result.type === "error") {
|
||||
|
|
@ -100,14 +55,23 @@ export const runPreRequestScript = async (
|
|||
typeof result.err === "object" &&
|
||||
"message" in result.err
|
||||
) {
|
||||
return TE.left(`Script execution failed: ${result.err.message}`)()
|
||||
return E.left(`Script execution failed: ${result.err.message}`)
|
||||
}
|
||||
|
||||
return TE.left(`Script execution failed: ${String(result.err)}`)()
|
||||
return E.left(`Script execution failed: ${String(result.err)}`)
|
||||
}
|
||||
|
||||
return TE.right({
|
||||
return E.right({
|
||||
envs: finalEnvs,
|
||||
consoleEntries,
|
||||
})()
|
||||
})
|
||||
}
|
||||
|
||||
export const runPreRequestScript = async (
|
||||
preRequestScript: string,
|
||||
envs: TestResult["envs"],
|
||||
experimentalScriptingSandbox = true
|
||||
): Promise<E.Either<string, SandboxPreRequestResult>> =>
|
||||
experimentalScriptingSandbox
|
||||
? runPreRequestScriptWithFaradayCage(preRequestScript, envs)
|
||||
: runPreRequestScriptWithWebWorker(preRequestScript, envs)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
import { FaradayCage } from "faraday-cage"
|
||||
import { ConsoleEntry } from "faraday-cage/modules"
|
||||
import * as E from "fp-ts/Either"
|
||||
import { cloneDeep } from "lodash-es"
|
||||
|
||||
import { defaultModules, pwPostRequestModule } from "~/cage-modules"
|
||||
import { preventCyclicObjects } from "~/shared-utils"
|
||||
import {
|
||||
SandboxTestResult,
|
||||
TestDescriptor,
|
||||
|
|
@ -7,30 +12,34 @@ import {
|
|||
TestResult,
|
||||
} from "~/types"
|
||||
|
||||
import {
|
||||
blobPolyfill,
|
||||
ConsoleEntry,
|
||||
console as ConsoleModule,
|
||||
crypto,
|
||||
esmModuleLoader,
|
||||
fetch,
|
||||
} from "faraday-cage/modules"
|
||||
|
||||
import { FaradayCage } from "faraday-cage"
|
||||
|
||||
import * as TE from "fp-ts/lib/TaskEither"
|
||||
|
||||
import { cloneDeep } from "lodash-es"
|
||||
import { pwPostRequestModule } from "~/cage-modules/pw"
|
||||
import { preventCyclicObjects } from "~/shared-utils"
|
||||
|
||||
import Worker from "./worker?worker&inline"
|
||||
|
||||
export const runTestScript = async (
|
||||
const runTestScriptWithWebWorker = (
|
||||
testScript: string,
|
||||
envs: TestResult["envs"],
|
||||
response: TestResponse,
|
||||
experimentalScriptingSandbox = true
|
||||
response: TestResponse
|
||||
): Promise<E.Either<string, SandboxTestResult>> => {
|
||||
return new Promise((resolve) => {
|
||||
const worker = new Worker()
|
||||
|
||||
// Listen for the results from the web worker
|
||||
worker.addEventListener("message", (event: MessageEvent) =>
|
||||
resolve(event.data.results)
|
||||
)
|
||||
|
||||
// Send the script to the web worker
|
||||
worker.postMessage({
|
||||
testScript,
|
||||
envs,
|
||||
response,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const runTestScriptWithFaradayCage = async (
|
||||
testScript: string,
|
||||
envs: TestResult["envs"],
|
||||
response: TestResponse
|
||||
): Promise<E.Either<string, SandboxTestResult>> => {
|
||||
const testRunStack: TestDescriptor[] = [
|
||||
{ descriptor: "root", expectResults: [], children: [] },
|
||||
|
|
@ -40,100 +49,56 @@ export const runTestScript = async (
|
|||
let finalTestResults = testRunStack
|
||||
const consoleEntries: ConsoleEntry[] = []
|
||||
|
||||
const responseObjHandle = preventCyclicObjects(response)
|
||||
|
||||
if (E.isLeft(responseObjHandle)) {
|
||||
return TE.left(`Response marshalling failed: ${responseObjHandle.left}`)()
|
||||
}
|
||||
|
||||
if (!experimentalScriptingSandbox) {
|
||||
return new Promise((resolve) => {
|
||||
const worker = new Worker()
|
||||
|
||||
// Listen for the results from the web worker
|
||||
worker.addEventListener("message", (event: MessageEvent) =>
|
||||
resolve(event.data.results)
|
||||
)
|
||||
|
||||
// Send the script to the web worker
|
||||
worker.postMessage({
|
||||
testScript,
|
||||
envs,
|
||||
response,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const cage = await FaradayCage.create()
|
||||
|
||||
const result = await cage.runCode(testScript, [
|
||||
...defaultModules({
|
||||
handleConsoleEntry: (consoleEntry) => consoleEntries.push(consoleEntry),
|
||||
}),
|
||||
|
||||
pwPostRequestModule({
|
||||
envs: cloneDeep(envs),
|
||||
testRunStack: cloneDeep(testRunStack),
|
||||
response: responseObjHandle.right as TestResponse,
|
||||
response,
|
||||
handleSandboxResults: ({ envs, testRunStack }) => {
|
||||
finalEnvs = envs
|
||||
finalTestResults = testRunStack
|
||||
},
|
||||
}),
|
||||
blobPolyfill,
|
||||
ConsoleModule({
|
||||
onLog(...args) {
|
||||
console[args[0]](...args.slice(1))
|
||||
},
|
||||
onCount(...args) {
|
||||
console.count(args[0])
|
||||
},
|
||||
onTime(...args) {
|
||||
console.timeEnd(args[0])
|
||||
},
|
||||
onTimeLog(...args) {
|
||||
console.timeLog(...args)
|
||||
},
|
||||
onGroup(...args) {
|
||||
console.group(...args)
|
||||
},
|
||||
onGroupEnd(...args) {
|
||||
console.groupEnd(...args)
|
||||
},
|
||||
onClear(...args) {
|
||||
console.clear(...args)
|
||||
},
|
||||
onAssert(...args) {
|
||||
console.assert(...args)
|
||||
},
|
||||
onDir(...args) {
|
||||
console.dir(...args)
|
||||
},
|
||||
onTable(...args) {
|
||||
console.table(...args)
|
||||
},
|
||||
onFinish(entries) {
|
||||
consoleEntries.push(...entries)
|
||||
},
|
||||
}),
|
||||
crypto(),
|
||||
esmModuleLoader,
|
||||
fetch(),
|
||||
])
|
||||
|
||||
if (result.type === "error") {
|
||||
if (result.type === "error") {
|
||||
if (
|
||||
result.err !== null &&
|
||||
typeof result.err === "object" &&
|
||||
"message" in result.err
|
||||
) {
|
||||
return TE.left(`Script execution failed: ${result.err.message}`)()
|
||||
}
|
||||
|
||||
return TE.left(`Script execution failed: ${String(result.err)}`)()
|
||||
if (
|
||||
result.err !== null &&
|
||||
typeof result.err === "object" &&
|
||||
"message" in result.err
|
||||
) {
|
||||
return E.left(`Script execution failed: ${result.err.message}`)
|
||||
}
|
||||
|
||||
return E.left(`Script execution failed: ${String(result.err)}`)
|
||||
}
|
||||
|
||||
return TE.right(<SandboxTestResult>{
|
||||
return E.right(<SandboxTestResult>{
|
||||
tests: finalTestResults[0],
|
||||
envs: finalEnvs,
|
||||
consoleEntries,
|
||||
})()
|
||||
})
|
||||
}
|
||||
|
||||
export const runTestScript = async (
|
||||
testScript: string,
|
||||
envs: TestResult["envs"],
|
||||
response: TestResponse,
|
||||
experimentalScriptingSandbox = true
|
||||
): Promise<E.Either<string, SandboxTestResult>> => {
|
||||
const responseObjHandle = preventCyclicObjects<TestResponse>(response)
|
||||
|
||||
if (E.isLeft(responseObjHandle)) {
|
||||
return E.left(`Response marshalling failed: ${responseObjHandle.left}`)
|
||||
}
|
||||
|
||||
return experimentalScriptingSandbox
|
||||
? runTestScriptWithFaradayCage(testScript, envs, responseObjHandle.right)
|
||||
: runTestScriptWithWebWorker(testScript, envs, responseObjHandle.right)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1133,8 +1133,8 @@ importers:
|
|||
specifier: 4.17.12
|
||||
version: 4.17.12
|
||||
faraday-cage:
|
||||
specifier: 0.0.15
|
||||
version: 0.0.15
|
||||
specifier: 0.0.16
|
||||
version: 0.0.16
|
||||
fp-ts:
|
||||
specifier: 2.16.9
|
||||
version: 2.16.9
|
||||
|
|
@ -8859,8 +8859,8 @@ packages:
|
|||
resolution: {integrity: sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ==}
|
||||
engines: {node: ^12.20 || >= 14.13}
|
||||
|
||||
faraday-cage@0.0.15:
|
||||
resolution: {integrity: sha512-ZERKHFsea2fZ0gfDVFOkXmtryhp+7CyUBFaa9nvRq7Gbf6XVnZ0f0dKsZjwoBzYqSD3Lhl2DFfK8bW4PEo1y0g==}
|
||||
faraday-cage@0.0.16:
|
||||
resolution: {integrity: sha512-+BHq/8LnqSsZ+qygj4+dgdis3iR+bBtB5dbWcw8ZNhdhoYQ+X9FS00qhQ6B6tIoEBNBHBSeQN3ydMXOjoE0y3Q==}
|
||||
|
||||
fast-decode-uri-component@1.0.1:
|
||||
resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
|
||||
|
|
@ -10938,6 +10938,7 @@ packages:
|
|||
multer@1.4.5-lts.2:
|
||||
resolution: {integrity: sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
deprecated: Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.
|
||||
|
||||
mute-stream@0.0.8:
|
||||
resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==}
|
||||
|
|
@ -24439,7 +24440,7 @@ snapshots:
|
|||
|
||||
extract-files@11.0.0: {}
|
||||
|
||||
faraday-cage@0.0.15:
|
||||
faraday-cage@0.0.16:
|
||||
dependencies:
|
||||
'@jitl/quickjs-ffi-types': 0.31.0
|
||||
'@jitl/quickjs-singlefile-mjs-release-asyncify': 0.31.0
|
||||
|
|
|
|||
Loading…
Reference in a new issue