123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- import { pipe } from "fp-ts/function"
- import * as O from "fp-ts/Option"
- import * as E from "fp-ts/Either"
- import * as TE from "fp-ts/lib/TaskEither"
- import * as qjs from "quickjs-emscripten"
- import cloneDeep from "lodash/clone"
- import { Environment, parseTemplateStringE } from "@hoppscotch/data"
- import { getEnv, setEnv } from "./utils"
- type Envs = {
- global: Environment["variables"]
- selected: Environment["variables"]
- }
- export const execPreRequestScript = (
- preRequestScript: string,
- envs: Envs
- ): TE.TaskEither<string, Envs> =>
- pipe(
- TE.tryCatch(
- async () => await qjs.getQuickJS(),
- (reason) => `QuickJS initialization failed: ${reason}`
- ),
- TE.chain((QuickJS) => {
- let currentEnvs = cloneDeep(envs)
- const vm = QuickJS.createVm()
- const pwHandle = vm.newObject()
- // Environment management APIs
- // TODO: Unified Implementation
- const envHandle = vm.newObject()
- const envGetHandle = vm.newFunction("get", (keyHandle) => {
- const key: unknown = vm.dump(keyHandle)
- if (typeof key !== "string") {
- return {
- error: vm.newString("Expected key to be a string"),
- }
- }
- const result = pipe(
- getEnv(key, currentEnvs),
- O.match(
- () => vm.undefined,
- ({ value }) => vm.newString(value)
- )
- )
- return {
- value: result,
- }
- })
- const envGetResolveHandle = vm.newFunction("getResolve", (keyHandle) => {
- const key: unknown = vm.dump(keyHandle)
- if (typeof key !== "string") {
- return {
- error: vm.newString("Expected key to be a string"),
- }
- }
- const result = pipe(
- getEnv(key, currentEnvs),
- E.fromOption(() => "INVALID_KEY" as const),
- E.map(({ value }) =>
- pipe(
- parseTemplateStringE(value, [...envs.selected, ...envs.global]),
- // If the recursive resolution failed, return the unresolved value
- E.getOrElse(() => value)
- )
- ),
- // Create a new VM String
- // NOTE: Do not shorten this to map(vm.newString) apparently it breaks it
- E.map((x) => vm.newString(x)),
- E.getOrElse(() => vm.undefined)
- )
- return {
- value: result,
- }
- })
- const envSetHandle = vm.newFunction("set", (keyHandle, valueHandle) => {
- const key: unknown = vm.dump(keyHandle)
- const value: unknown = vm.dump(valueHandle)
- if (typeof key !== "string") {
- return {
- error: vm.newString("Expected key to be a string"),
- }
- }
- if (typeof value !== "string") {
- return {
- error: vm.newString("Expected value to be a string"),
- }
- }
- currentEnvs = setEnv(key, value, currentEnvs)
- return {
- value: vm.undefined,
- }
- })
- const envResolveHandle = vm.newFunction("resolve", (valueHandle) => {
- const value: unknown = vm.dump(valueHandle)
- if (typeof value !== "string") {
- return {
- error: vm.newString("Expected value to be a string"),
- }
- }
- const result = pipe(
- parseTemplateStringE(value, [
- ...currentEnvs.selected,
- ...currentEnvs.global,
- ]),
- E.getOrElse(() => value)
- )
- return {
- value: vm.newString(result),
- }
- })
- vm.setProp(envHandle, "resolve", envResolveHandle)
- envResolveHandle.dispose()
- vm.setProp(envHandle, "set", envSetHandle)
- envSetHandle.dispose()
- vm.setProp(envHandle, "getResolve", envGetResolveHandle)
- envGetResolveHandle.dispose()
- vm.setProp(envHandle, "get", envGetHandle)
- envGetHandle.dispose()
- vm.setProp(pwHandle, "env", envHandle)
- envHandle.dispose()
- vm.setProp(vm.global, "pw", pwHandle)
- pwHandle.dispose()
- const evalRes = vm.evalCode(preRequestScript)
- if (evalRes.error) {
- const errorData = vm.dump(evalRes.error)
- evalRes.error.dispose()
- return TE.left(errorData)
- }
- vm.dispose()
- return TE.right(currentEnvs)
- })
- )
|