preRequest.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import { pipe } from "fp-ts/function"
  2. import * as O from "fp-ts/Option"
  3. import * as E from "fp-ts/Either"
  4. import * as TE from "fp-ts/lib/TaskEither"
  5. import * as qjs from "quickjs-emscripten"
  6. import cloneDeep from "lodash/clone"
  7. import { Environment, parseTemplateStringE } from "@hoppscotch/data"
  8. import { getEnv, setEnv } from "./utils"
  9. type Envs = {
  10. global: Environment["variables"]
  11. selected: Environment["variables"]
  12. }
  13. export const execPreRequestScript = (
  14. preRequestScript: string,
  15. envs: Envs
  16. ): TE.TaskEither<string, Envs> =>
  17. pipe(
  18. TE.tryCatch(
  19. async () => await qjs.getQuickJS(),
  20. (reason) => `QuickJS initialization failed: ${reason}`
  21. ),
  22. TE.chain((QuickJS) => {
  23. let currentEnvs = cloneDeep(envs)
  24. const vm = QuickJS.createVm()
  25. const pwHandle = vm.newObject()
  26. // Environment management APIs
  27. // TODO: Unified Implementation
  28. const envHandle = vm.newObject()
  29. const envGetHandle = vm.newFunction("get", (keyHandle) => {
  30. const key: unknown = vm.dump(keyHandle)
  31. if (typeof key !== "string") {
  32. return {
  33. error: vm.newString("Expected key to be a string"),
  34. }
  35. }
  36. const result = pipe(
  37. getEnv(key, currentEnvs),
  38. O.match(
  39. () => vm.undefined,
  40. ({ value }) => vm.newString(value)
  41. )
  42. )
  43. return {
  44. value: result,
  45. }
  46. })
  47. const envGetResolveHandle = vm.newFunction("getResolve", (keyHandle) => {
  48. const key: unknown = vm.dump(keyHandle)
  49. if (typeof key !== "string") {
  50. return {
  51. error: vm.newString("Expected key to be a string"),
  52. }
  53. }
  54. const result = pipe(
  55. getEnv(key, currentEnvs),
  56. E.fromOption(() => "INVALID_KEY" as const),
  57. E.map(({ value }) =>
  58. pipe(
  59. parseTemplateStringE(value, [...envs.selected, ...envs.global]),
  60. // If the recursive resolution failed, return the unresolved value
  61. E.getOrElse(() => value)
  62. )
  63. ),
  64. // Create a new VM String
  65. // NOTE: Do not shorten this to map(vm.newString) apparently it breaks it
  66. E.map((x) => vm.newString(x)),
  67. E.getOrElse(() => vm.undefined)
  68. )
  69. return {
  70. value: result,
  71. }
  72. })
  73. const envSetHandle = vm.newFunction("set", (keyHandle, valueHandle) => {
  74. const key: unknown = vm.dump(keyHandle)
  75. const value: unknown = vm.dump(valueHandle)
  76. if (typeof key !== "string") {
  77. return {
  78. error: vm.newString("Expected key to be a string"),
  79. }
  80. }
  81. if (typeof value !== "string") {
  82. return {
  83. error: vm.newString("Expected value to be a string"),
  84. }
  85. }
  86. currentEnvs = setEnv(key, value, currentEnvs)
  87. return {
  88. value: vm.undefined,
  89. }
  90. })
  91. const envResolveHandle = vm.newFunction("resolve", (valueHandle) => {
  92. const value: unknown = vm.dump(valueHandle)
  93. if (typeof value !== "string") {
  94. return {
  95. error: vm.newString("Expected value to be a string"),
  96. }
  97. }
  98. const result = pipe(
  99. parseTemplateStringE(value, [
  100. ...currentEnvs.selected,
  101. ...currentEnvs.global,
  102. ]),
  103. E.getOrElse(() => value)
  104. )
  105. return {
  106. value: vm.newString(result),
  107. }
  108. })
  109. vm.setProp(envHandle, "resolve", envResolveHandle)
  110. envResolveHandle.dispose()
  111. vm.setProp(envHandle, "set", envSetHandle)
  112. envSetHandle.dispose()
  113. vm.setProp(envHandle, "getResolve", envGetResolveHandle)
  114. envGetResolveHandle.dispose()
  115. vm.setProp(envHandle, "get", envGetHandle)
  116. envGetHandle.dispose()
  117. vm.setProp(pwHandle, "env", envHandle)
  118. envHandle.dispose()
  119. vm.setProp(vm.global, "pw", pwHandle)
  120. pwHandle.dispose()
  121. const evalRes = vm.evalCode(preRequestScript)
  122. if (evalRes.error) {
  123. const errorData = vm.dump(evalRes.error)
  124. evalRes.error.dispose()
  125. return TE.left(errorData)
  126. }
  127. vm.dispose()
  128. return TE.right(currentEnvs)
  129. })
  130. )