@scalar/pre-post-request-scripts 0.4.35 → 0.4.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/components/ScriptEditor.vue.script.js +1 -1
- package/dist/components/ScriptEditor.vue.script.js.map +1 -1
- package/dist/components/ViewLayout/ValueEmitter.vue.d.ts +1 -1
- package/dist/components/ViewLayout/ValueEmitter.vue.d.ts.map +1 -1
- package/dist/components/ViewLayout/ValueEmitter.vue.script.js +1 -1
- package/dist/components/ViewLayout/ValueEmitter.vue.script.js.map +1 -1
- package/dist/components/ViewLayout/ViewLayoutCollapse.vue.d.ts.map +1 -1
- package/dist/components/ViewLayout/ViewLayoutCollapse.vue.script.js +1 -1
- package/dist/components/ViewLayout/ViewLayoutCollapse.vue.script.js.map +1 -1
- package/dist/consts/example-scripts.js.map +1 -1
- package/dist/helpers/get-script.js.map +1 -1
- package/dist/libs/execute-scripts/build-sandbox-context.js.map +1 -1
- package/dist/libs/execute-scripts/context/console.js.map +1 -1
- package/dist/libs/execute-scripts/execute-post-response-script.js.map +1 -1
- package/dist/libs/execute-scripts/execute-pre-request-script.js.map +1 -1
- package/dist/libs/execute-scripts/postman-adapter/request-factory-adapter.js.map +1 -1
- package/dist/libs/execute-scripts/postman-adapter/sandbox-adapter.js +1 -1
- package/dist/libs/execute-scripts/postman-adapter/sandbox-adapter.js.map +1 -1
- package/dist/libs/execute-scripts/postman-adapter/sandbox-frame-entry.js.map +1 -1
- package/dist/libs/execute-scripts/postman-adapter/sandbox-frame-server.js +1 -1
- package/dist/libs/execute-scripts/postman-adapter/sandbox-frame-server.js.map +1 -1
- package/dist/libs/execute-scripts/postman-adapter/sandbox-origins.js.map +1 -1
- package/dist/libs/execute-scripts/variables-store.js.map +1 -1
- package/dist/plugins/request-scripts/components/ExampleScripts.vue.script.js +1 -1
- package/dist/plugins/request-scripts/components/ExampleScripts.vue.script.js.map +1 -1
- package/dist/plugins/request-scripts/components/ScriptsSection.vue.d.ts.map +1 -1
- package/dist/plugins/request-scripts/components/ScriptsSection.vue.script.js +1 -1
- package/dist/plugins/request-scripts/components/ScriptsSection.vue.script.js.map +1 -1
- package/dist/plugins/request-scripts/components/TestResults/TestResultIndicator.vue.script.js +1 -1
- package/dist/plugins/request-scripts/components/TestResults/TestResultIndicator.vue.script.js.map +1 -1
- package/dist/plugins/request-scripts/components/TestResults/TestResultItem.vue.script.js +1 -1
- package/dist/plugins/request-scripts/components/TestResults/TestResultItem.vue.script.js.map +1 -1
- package/dist/plugins/request-scripts/components/TestResults/TestResults.vue.script.js +1 -1
- package/dist/plugins/request-scripts/components/TestResults/TestResults.vue.script.js.map +1 -1
- package/dist/plugins/request-scripts/components/TestResults/TestSummary.vue.script.js +1 -1
- package/dist/plugins/request-scripts/components/TestResults/TestSummary.vue.script.js.map +1 -1
- package/dist/plugins/request-scripts/request-scripts-plugin.js.map +1 -1
- package/package.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -3,7 +3,7 @@ import { javascript } from "@codemirror/lang-javascript";
|
|
|
3
3
|
import { useCodeMirror } from "@scalar/use-codemirror";
|
|
4
4
|
//#region src/components/ScriptEditor.vue?vue&type=script&setup=true&lang.ts
|
|
5
5
|
var _hoisted_1 = { class: "script-editor text-sm" };
|
|
6
|
-
var ScriptEditor_vue_vue_type_script_setup_true_lang_default =
|
|
6
|
+
var ScriptEditor_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
7
7
|
__name: "ScriptEditor",
|
|
8
8
|
props: { modelValue: {} },
|
|
9
9
|
emits: ["update:modelValue"],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScriptEditor.vue.script.js","names":[],"sources":["../../src/components/ScriptEditor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { javascript } from '@codemirror/lang-javascript'\nimport { useCodeMirror } from '@scalar/use-codemirror'\nimport { ref, toRef } from 'vue'\n\nconst props = defineProps<{\n modelValue: string\n}>()\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\nconst codeMirrorRef = ref<HTMLDivElement | null>(null)\n\nuseCodeMirror({\n codeMirrorRef,\n content: toRef(() => props.modelValue),\n language: undefined,\n // We want to use some custom configuration for the syntax highlighting.\n extensions: [javascript()],\n lineNumbers: true,\n onChange: (value) => {\n emit('update:modelValue', value)\n },\n})\n</script>\n\n<template>\n <div class=\"script-editor text-sm\">\n <div\n ref=\"codeMirrorRef\"\n class=\"editor-container\" />\n </div>\n</template>\n"],"mappings":";;;;;;;;;;EAKA,MAAM,QAAQ;EAId,MAAM,OAAO;EAIb,MAAM,gBAAgB,IAA2B,
|
|
1
|
+
{"version":3,"file":"ScriptEditor.vue.script.js","names":[],"sources":["../../src/components/ScriptEditor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { javascript } from '@codemirror/lang-javascript'\nimport { useCodeMirror } from '@scalar/use-codemirror'\nimport { ref, toRef } from 'vue'\n\nconst props = defineProps<{\n modelValue: string\n}>()\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\nconst codeMirrorRef = ref<HTMLDivElement | null>(null)\n\nuseCodeMirror({\n codeMirrorRef,\n content: toRef(() => props.modelValue),\n language: undefined,\n // We want to use some custom configuration for the syntax highlighting.\n extensions: [javascript()],\n lineNumbers: true,\n onChange: (value) => {\n emit('update:modelValue', value)\n },\n})\n</script>\n\n<template>\n <div class=\"script-editor text-sm\">\n <div\n ref=\"codeMirrorRef\"\n class=\"editor-container\" />\n </div>\n</template>\n"],"mappings":";;;;;;;;;;EAKA,MAAM,QAAQ;EAId,MAAM,OAAO;EAIb,MAAM,gBAAgB,IAA2B,IAAI;EAErD,cAAc;GACZ;GACA,SAAS,YAAY,MAAM,UAAU;GACrC,UAAU,KAAA;GAEV,YAAY,CAAC,WAAW,CAAC;GACzB,aAAa;GACb,WAAW,UAAU;IACnB,KAAK,qBAAqB,KAAK;GACjC;EACF,CAAC;;uBAIC,mBAIM,OAJN,YAIM,CAHJ,mBAE6B,OAAA;aADvB;IAAJ,KAAI;IACJ,OAAM"}
|
|
@@ -22,7 +22,7 @@ declare const __VLS_export: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_se
|
|
|
22
22
|
(e: "unmount"): void;
|
|
23
23
|
};
|
|
24
24
|
}>) => import("vue").VNode & {
|
|
25
|
-
__ctx?: Awaited<typeof __VLS_setup
|
|
25
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
26
26
|
};
|
|
27
27
|
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
28
28
|
[K in keyof T]: T[K];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ValueEmitter.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ViewLayout/ValueEmitter.vue"],"names":[],"mappings":"AAuCA;;;;GAIG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAKzC,QAAA,MAAM,YAAY,GAAK,CAAC,EACvB,aAAa,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,EAC9D,YAAY,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,EAC3G,gBAAgB,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,EAClE;WAkCO,OAAO,KAAK,EAAE,WAAW,GAAG,mBAAmB,CAAC;eAjC5B,CAAC;;;;KAiC8D,CAAC,GAAG,CAAC,OAAO,UAAU,SAAS;QAAE,oBAAoB,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,CAAC,GAAG,EAAE,CAAC;YAC5J,CAAC,OAAO,EAAE,EAAE,KAAK,IAAI;WACtB,GAAG;WACH,EAAE;;YAhCJ,QAAQ,SAAS,CAAC,GAAG,IAAI;YACzB,SAAS,GAAG,IAAI;;EAkClB,KACQ,OAAO,KAAK,EAAE,KAAK,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC,OAAO,WAAW,CAAC,CAAA;CAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"ValueEmitter.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ViewLayout/ValueEmitter.vue"],"names":[],"mappings":"AAuCA;;;;GAIG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAKzC,QAAA,MAAM,YAAY,GAAK,CAAC,EACvB,aAAa,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,EAC9D,YAAY,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,EAC3G,gBAAgB,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,EAClE;WAkCO,OAAO,KAAK,EAAE,WAAW,GAAG,mBAAmB,CAAC;eAjC5B,CAAC;;;;KAiC8D,CAAC,GAAG,CAAC,OAAO,UAAU,SAAS;QAAE,oBAAoB,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,CAAC,GAAG,EAAE,CAAC;YAC5J,CAAC,OAAO,EAAE,EAAE,KAAK,IAAI;WACtB,GAAG;WACH,EAAE;;YAhCJ,QAAQ,SAAS,CAAC,GAAG,IAAI;YACzB,SAAS,GAAG,IAAI;;EAkClB,KACQ,OAAO,KAAK,EAAE,KAAK,GAAG;IAAE,KAAK,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,CAAA;CAAI,CAAC;AACzF,KAAK,mBAAmB,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,GAAG,GAAG;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAG,GAAG;KAAG,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAG,CAAC,GAAG,EAAE,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineComponent, onBeforeUnmount, watch } from "vue";
|
|
2
|
-
var ValueEmitter_vue_vue_type_script_setup_true_lang_default =
|
|
2
|
+
var ValueEmitter_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
3
3
|
name: "ValueEmitter",
|
|
4
4
|
props: { value: {} },
|
|
5
5
|
emits: ["change", "unmount"],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ValueEmitter.vue.script.js","names":[],"sources":["../../../src/components/ViewLayout/ValueEmitter.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * This component takes in any value and emits it back\n *\n * This is useful when we need to catch the open state of a disclosure from its v-slot\n */\nexport default {\n name: 'ValueEmitter',\n}\n</script>\n\n<script setup lang=\"ts\" generic=\"T\">\nimport { onBeforeUnmount, watch } from 'vue'\n\nconst { value } = defineProps<{ value: T }>()\nconst emit = defineEmits<{\n (e: 'change', value: T): void\n (e: 'unmount'): void\n}>()\n\nwatch(\n () => value,\n (newValue) => emit('change', newValue),\n { immediate: true },\n)\n\nonBeforeUnmount(() => emit('unmount'))\n</script>\n\n<!-- eslint-disable-next-line vue/valid-template-root -->\n<template>\n <!-- We use this hack to emit the slot value back to the parent -->\n</template>\n"],"mappings":";;CAOE,MAAM;;;;EAQR,MAAM,OAAO;
|
|
1
|
+
{"version":3,"file":"ValueEmitter.vue.script.js","names":[],"sources":["../../../src/components/ViewLayout/ValueEmitter.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * This component takes in any value and emits it back\n *\n * This is useful when we need to catch the open state of a disclosure from its v-slot\n */\nexport default {\n name: 'ValueEmitter',\n}\n</script>\n\n<script setup lang=\"ts\" generic=\"T\">\nimport { onBeforeUnmount, watch } from 'vue'\n\nconst { value } = defineProps<{ value: T }>()\nconst emit = defineEmits<{\n (e: 'change', value: T): void\n (e: 'unmount'): void\n}>()\n\nwatch(\n () => value,\n (newValue) => emit('change', newValue),\n { immediate: true },\n)\n\nonBeforeUnmount(() => emit('unmount'))\n</script>\n\n<!-- eslint-disable-next-line vue/valid-template-root -->\n<template>\n <!-- We use this hack to emit the slot value back to the parent -->\n</template>\n"],"mappings":";;CAOE,MAAM;;;;EAQR,MAAM,OAAO;EAKb,YACQ,QAAA,QACL,aAAa,KAAK,UAAU,QAAQ,GACrC,EAAE,WAAW,KAAK,CACpB;EAEA,sBAAsB,KAAK,SAAS,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ViewLayoutCollapse.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ViewLayout/ViewLayoutCollapse.vue"],"names":[],"mappings":"AA2HA;;;;GAIG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAKzC,QAAA,MAAM,YAAY;IAEhB,iDAAiD;kBACnC,OAAO;IACrB,uDAAuD;gBAC3C,MAAM;IAClB,8DAA8D;eACnD,OAAO;;;;IALlB,iDAAiD;kBACnC,OAAO;IACrB,uDAAuD;gBAC3C,MAAM;IAClB,8DAA8D;eACnD,OAAO;;;;
|
|
1
|
+
{"version":3,"file":"ViewLayoutCollapse.vue.d.ts","sourceRoot":"","sources":["../../../src/components/ViewLayout/ViewLayoutCollapse.vue"],"names":[],"mappings":"AA2HA;;;;GAIG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAKzC,QAAA,MAAM,YAAY;IAEhB,iDAAiD;kBACnC,OAAO;IACrB,uDAAuD;gBAC3C,MAAM;IAClB,8DAA8D;eACnD,OAAO;;;;IALlB,iDAAiD;kBACnC,OAAO;IACrB,uDAAuD;gBAC3C,MAAM;IAClB,8DAA8D;eACnD,OAAO;;;;YAmPR,CAAC,KAAK;;KAAiB,KAAK,GAAG;;cAC7B,CAAC,KAAK;;KAAiB,KAAK,GAAG;;cAC/B,CAAC,KAAK;;KAAiB,KAAK,GAAG;EAUzC,CAAC;AACL,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -19,7 +19,7 @@ var _hoisted_7 = {
|
|
|
19
19
|
key: 0,
|
|
20
20
|
class: "ui-not-open:hidden flex items-center gap-2 pr-0.75"
|
|
21
21
|
};
|
|
22
|
-
var ViewLayoutCollapse_vue_vue_type_script_setup_true_lang_default =
|
|
22
|
+
var ViewLayoutCollapse_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
23
23
|
name: "CollapsibleSection",
|
|
24
24
|
props: {
|
|
25
25
|
defaultOpen: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ViewLayoutCollapse.vue.script.js","names":["$slots","$attrs"],"sources":["../../../src/components/ViewLayout/ViewLayoutCollapse.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Simple collapsible section component that can be used to wrap content that should be collapsed and expanded\n *\n * Would like to replace with details/summary elements, but they are not supported in all browsers yet?\n */\nexport default {\n name: 'CollapsibleSection',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { useId } from 'vue'\n\nimport ValueEmitter from './ValueEmitter.vue'\n\nconst {\n defaultOpen = true,\n itemCount = 0,\n isStatic,\n} = defineProps<{\n /** Whether the disclosure is open by default. */\n defaultOpen?: boolean\n /** Number of items to show in badge when collapsed. */\n itemCount?: number\n /** Whether the disclosure is static and cannot be toggled. */\n isStatic?: boolean\n}>()\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n}>()\n\nconst headingId = useId()\n</script>\n\n<template>\n <Disclosure\n v-slot=\"{ open }\"\n as=\"div\"\n class=\"group/collapse text-c-2 focus-within:text-c-1 last:ui-open:border-b-0 border-b\"\n :class=\"isStatic && 'last-of-type:first-of-type:border-b-0'\"\n :defaultOpen=\"defaultOpen\"\n :static=\"isStatic\">\n <!-- We use this hack to emit the slot value back to the parent -->\n <ValueEmitter\n :value=\"open as boolean\"\n @change=\"(value) => emit('update:modelValue', value)\" />\n\n <section\n :aria-labelledby=\"headingId\"\n class=\"contents\">\n <div\n class=\"bg-b-2 flex items-center\"\n :class=\"isStatic && 'rounded-t-lg border border-b-0'\">\n <!-- Main disclosure button that toggles the panel -->\n <DisclosureButton\n class=\"hover:text-c-1 group box-content flex max-h-8 flex-1 items-center gap-2.5 overflow-hidden px-1 py-1.5 text-base font-medium outline-none md:px-1.5 xl:pr-0.5 xl:pl-2\"\n :class=\"isStatic && '!pl-3'\"\n :disabled=\"isStatic\">\n <ScalarIcon\n v-if=\"!isStatic\"\n class=\"text-c-3 group-hover:text-c-1 rounded-px ui-open:rotate-90 ui-not-open:rotate-0 outline-offset-2 group-focus-visible:outline\"\n icon=\"ChevronRight\"\n size=\"md\" />\n\n <!-- Heading with title -->\n <h2\n class=\"text-c-1 m-0 flex flex-1 items-center gap-1.5 leading-[20px]\">\n <span\n :id=\"headingId\"\n class=\"contents\">\n <slot\n name=\"title\"\n :open=\"open\" />\n <span\n v-if=\"!open\"\n class=\"sr-only\">\n (Collapsed)\n </span>\n </span>\n\n <!-- Badge showing item count when collapsed. -->\n <span\n v-if=\"!open && itemCount\"\n class=\"bg-b-2 text-c-2 inline-flex h-5 w-5 items-center justify-center rounded-full border text-xs font-semibold\">\n {{ itemCount }}\n <span class=\"sr-only\">Item{{ itemCount === 1 ? '' : 's' }}</span>\n </span>\n </h2>\n </DisclosureButton>\n\n <!-- Optional actions slot that hides when the panel is closed. -->\n <div\n v-if=\"$slots.actions\"\n class=\"ui-not-open:hidden flex items-center gap-2 pr-0.75\">\n <slot\n name=\"actions\"\n :open=\"open\" />\n </div>\n </div>\n\n <!-- The collapsible content panel. -->\n <DisclosurePanel\n v-bind=\"$attrs\"\n class=\"diclosure-panel h-full max-h-fit rounded-b\">\n <slot :open=\"open\" />\n </DisclosurePanel>\n </section>\n </Disclosure>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;CAOE,MAAM;;;;;;;;;;;EAwBR,MAAM,OAAO;EAIb,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"ViewLayoutCollapse.vue.script.js","names":["$slots","$attrs"],"sources":["../../../src/components/ViewLayout/ViewLayoutCollapse.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Simple collapsible section component that can be used to wrap content that should be collapsed and expanded\n *\n * Would like to replace with details/summary elements, but they are not supported in all browsers yet?\n */\nexport default {\n name: 'CollapsibleSection',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { useId } from 'vue'\n\nimport ValueEmitter from './ValueEmitter.vue'\n\nconst {\n defaultOpen = true,\n itemCount = 0,\n isStatic,\n} = defineProps<{\n /** Whether the disclosure is open by default. */\n defaultOpen?: boolean\n /** Number of items to show in badge when collapsed. */\n itemCount?: number\n /** Whether the disclosure is static and cannot be toggled. */\n isStatic?: boolean\n}>()\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n}>()\n\nconst headingId = useId()\n</script>\n\n<template>\n <Disclosure\n v-slot=\"{ open }\"\n as=\"div\"\n class=\"group/collapse text-c-2 focus-within:text-c-1 last:ui-open:border-b-0 border-b\"\n :class=\"isStatic && 'last-of-type:first-of-type:border-b-0'\"\n :defaultOpen=\"defaultOpen\"\n :static=\"isStatic\">\n <!-- We use this hack to emit the slot value back to the parent -->\n <ValueEmitter\n :value=\"open as boolean\"\n @change=\"(value) => emit('update:modelValue', value)\" />\n\n <section\n :aria-labelledby=\"headingId\"\n class=\"contents\">\n <div\n class=\"bg-b-2 flex items-center\"\n :class=\"isStatic && 'rounded-t-lg border border-b-0'\">\n <!-- Main disclosure button that toggles the panel -->\n <DisclosureButton\n class=\"hover:text-c-1 group box-content flex max-h-8 flex-1 items-center gap-2.5 overflow-hidden px-1 py-1.5 text-base font-medium outline-none md:px-1.5 xl:pr-0.5 xl:pl-2\"\n :class=\"isStatic && '!pl-3'\"\n :disabled=\"isStatic\">\n <ScalarIcon\n v-if=\"!isStatic\"\n class=\"text-c-3 group-hover:text-c-1 rounded-px ui-open:rotate-90 ui-not-open:rotate-0 outline-offset-2 group-focus-visible:outline\"\n icon=\"ChevronRight\"\n size=\"md\" />\n\n <!-- Heading with title -->\n <h2\n class=\"text-c-1 m-0 flex flex-1 items-center gap-1.5 leading-[20px]\">\n <span\n :id=\"headingId\"\n class=\"contents\">\n <slot\n name=\"title\"\n :open=\"open\" />\n <span\n v-if=\"!open\"\n class=\"sr-only\">\n (Collapsed)\n </span>\n </span>\n\n <!-- Badge showing item count when collapsed. -->\n <span\n v-if=\"!open && itemCount\"\n class=\"bg-b-2 text-c-2 inline-flex h-5 w-5 items-center justify-center rounded-full border text-xs font-semibold\">\n {{ itemCount }}\n <span class=\"sr-only\">Item{{ itemCount === 1 ? '' : 's' }}</span>\n </span>\n </h2>\n </DisclosureButton>\n\n <!-- Optional actions slot that hides when the panel is closed. -->\n <div\n v-if=\"$slots.actions\"\n class=\"ui-not-open:hidden flex items-center gap-2 pr-0.75\">\n <slot\n name=\"actions\"\n :open=\"open\" />\n </div>\n </div>\n\n <!-- The collapsible content panel. -->\n <DisclosurePanel\n v-bind=\"$attrs\"\n class=\"diclosure-panel h-full max-h-fit rounded-b\">\n <slot :open=\"open\" />\n </DisclosurePanel>\n </section>\n </Disclosure>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;CAOE,MAAM;;;;;;;;;;;EAwBR,MAAM,OAAO;EAIb,MAAM,YAAY,MAAM;;uBAItB,YAwEa,MAAA,UAAA,GAAA;IAtEX,IAAG;IACH,OAAK,eAAA,CAAC,kFACE,QAAA,YAAQ,uCAAA,CAAA;IACf,aAAa,QAAA;IACb,QAAQ,QAAA;;sBAIiD,EAThD,WAAI,CAOd,YAE0D,sBAAA;KADvD,OAAO;KACP,UAAM,OAAA,OAAA,OAAA,MAAG,UAAU,KAAI,qBAAsB,KAAK;4BAErD,mBA2DU,WAAA;KA1DP,mBAAiB,MAAA,SAAA;KAClB,OAAM;QACN,mBAgDM,OAAA,EA/CJ,OAAK,eAAA,CAAC,4BACE,QAAA,YAAQ,gCAAA,CAAA,EAAA,GAAA,CAEhB,YAkCmB,MAAA,gBAAA,GAAA;KAjCjB,OAAK,eAAA,CAAC,wKACE,QAAA,YAAQ,OAAA,CAAA;KACf,UAAU,QAAA;;4BAKG,CAAA,CAHL,QAAA,YAAA,UAAA,GADT,YAIc,MAAA,UAAA,GAAA;;MAFZ,OAAM;MACN,MAAK;MACL,MAAK;yCAGP,mBAsBK,MAtBL,YAsBK,CApBH,mBAWO,QAAA;MAVJ,IAAI,MAAA,SAAA;MACL,OAAM;SACN,WAEiB,KAAA,QAAA,SAAA,EAAR,KAAI,CAAA,GAAA,CAEJ,QAAA,UAAA,GADT,mBAIO,QAJP,YAEkB,eAElB,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,GAAA,UAAA,GAAA,CAKO,QAAQ,QAAA,aAAA,UAAA,GADjB,mBAKO,QALP,YAKO,CAAA,gBAAA,gBAFF,QAAA,SAAS,IAAG,KACf,CAAA,GAAA,mBAAiE,QAAjE,YAAsB,SAAI,gBAAG,QAAA,cAAS,IAAA,KAAA,GAAA,GAAA,CAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,CAAA,CAAA;;qCAOpCA,KAAAA,OAAO,WAAA,UAAA,GADf,mBAMM,OANN,YAMM,CAHJ,WAEiB,KAAA,QAAA,WAAA,EAAR,KAAI,CAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,CAAA,GAKjB,YAIkB,MAAA,eAAA,GAJlB,WACUC,KAGQ,QAHF,EACd,OAAM,6CAA4C,CAAA,GAAA;4BAC7B,CAArB,WAAqB,KAAA,QAAA,WAAA,EAAR,KAAI,CAAA,CAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"example-scripts.js","names":[],"sources":["../../src/consts/example-scripts.ts"],"sourcesContent":["type ExampleScript = {\n title: string\n script: string\n mockResponse: {\n body?: any\n status?: number\n headers?: Record<string, string>\n text?: () => string\n }\n}\n\nexport const EXAMPLE_SCRIPTS: ExampleScript[] = [\n {\n title: 'Check the status code',\n script: `pm.test(\"Status code is 200\", () => {\n pm.response.to.have.status(200)\n})`,\n mockResponse: {\n status: 200,\n body: { success: true },\n },\n },\n // TODO: We didn't add the proper response duration yet.\n // {\n // title: 'Check response time',\n // script: `pm.test(\"Response time is acceptable\", () => {\n // pm.expect(pm.response.responseTime).to.be.below(200)\n // })`,\n // mockResponse: {\n // status: 200,\n // body: { success: true },\n // },\n // },\n {\n title: 'Check JSON response',\n script: `pm.test(\"Response is valid JSON\", () => {\n const responseData = pm.response.json()\n pm.expect(responseData).to.be.an('object')\n})`,\n mockResponse: {\n status: 200,\n body: { data: 'test' },\n headers: { 'Content-Type': 'application/json' },\n },\n },\n {\n title: 'Check response headers',\n script: `pm.test(\"Content-Type header is present\", () => {\n pm.response.to.have.header('Content-Type')\n})`,\n mockResponse: {\n status: 200,\n body: { success: true },\n headers: { 'Content-Type': 'application/json' },\n },\n },\n {\n title: 'Validate JSON schema',\n script: `pm.test(\"Response matches schema\", () => {\n const schema = {\n type: 'object',\n required: ['id', 'name'],\n properties: {\n id: { type: 'number' },\n name: { type: 'string' }\n }\n }\n pm.response.to.have.jsonSchema(schema)\n})`,\n mockResponse: {\n status: 200,\n body: { id: 1, name: 'Test Item' },\n headers: { 'Content-Type': 'application/json' },\n },\n },\n {\n title: 'Check response body',\n script: `pm.test(\"Response body contains string\", () => {\n pm.expect(pm.response.text()).to.include('success')\n})`,\n mockResponse: {\n status: 200,\n body: 'This is a successful response',\n },\n },\n {\n title: 'Successful POST request',\n script: `pm.test(\"Successful POST request\", () => {\n pm.expect(pm.response.code).to.be.oneOf([201, 202])\n})`,\n mockResponse: {\n status: 201,\n body: { success: true },\n },\n },\n {\n title: 'Check header value',\n script: `pm.test(\"Content-Type is JSON\", () => {\n pm.expect(pm.response.headers.get('Content-Type')).to.include('application/json')\n})`,\n mockResponse: {\n status: 200,\n body: { success: true },\n headers: { 'Content-Type': 'application/json' },\n },\n },\n {\n title: 'Assert data types',\n script: `pm.test(\"Response data types are correct\", () => {\n const data = pm.response.json()\n pm.expect(data).to.be.an('object')\n pm.expect(data.name).to.be.a('string')\n pm.expect(data.age).to.be.a('number')\n pm.expect(data.hobbies).to.be.an('array')\n})`,\n mockResponse: {\n status: 200,\n body: { name: 'Jane', age: 23, hobbies: ['reading'] },\n headers: { 'Content-Type': 'application/json' },\n },\n },\n {\n title: 'Check object containment',\n script: `pm.test(\"Response contains expected object\", () => {\n const expected = { created: true, errors: [] }\n pm.expect(pm.response.json()).to.deep.include(expected)\n})`,\n mockResponse: {\n status: 200,\n body: { created: true, errors: [], id: 42 },\n headers: { 'Content-Type': 'application/json' },\n },\n },\n]\n"],"mappings":";AAWA,IAAa,kBAAmC;CAC9C;EACE,OAAO;EACP,QAAQ;;;EAGR,cAAc;GACZ,QAAQ;GACR,MAAM,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"example-scripts.js","names":[],"sources":["../../src/consts/example-scripts.ts"],"sourcesContent":["type ExampleScript = {\n title: string\n script: string\n mockResponse: {\n body?: any\n status?: number\n headers?: Record<string, string>\n text?: () => string\n }\n}\n\nexport const EXAMPLE_SCRIPTS: ExampleScript[] = [\n {\n title: 'Check the status code',\n script: `pm.test(\"Status code is 200\", () => {\n pm.response.to.have.status(200)\n})`,\n mockResponse: {\n status: 200,\n body: { success: true },\n },\n },\n // TODO: We didn't add the proper response duration yet.\n // {\n // title: 'Check response time',\n // script: `pm.test(\"Response time is acceptable\", () => {\n // pm.expect(pm.response.responseTime).to.be.below(200)\n // })`,\n // mockResponse: {\n // status: 200,\n // body: { success: true },\n // },\n // },\n {\n title: 'Check JSON response',\n script: `pm.test(\"Response is valid JSON\", () => {\n const responseData = pm.response.json()\n pm.expect(responseData).to.be.an('object')\n})`,\n mockResponse: {\n status: 200,\n body: { data: 'test' },\n headers: { 'Content-Type': 'application/json' },\n },\n },\n {\n title: 'Check response headers',\n script: `pm.test(\"Content-Type header is present\", () => {\n pm.response.to.have.header('Content-Type')\n})`,\n mockResponse: {\n status: 200,\n body: { success: true },\n headers: { 'Content-Type': 'application/json' },\n },\n },\n {\n title: 'Validate JSON schema',\n script: `pm.test(\"Response matches schema\", () => {\n const schema = {\n type: 'object',\n required: ['id', 'name'],\n properties: {\n id: { type: 'number' },\n name: { type: 'string' }\n }\n }\n pm.response.to.have.jsonSchema(schema)\n})`,\n mockResponse: {\n status: 200,\n body: { id: 1, name: 'Test Item' },\n headers: { 'Content-Type': 'application/json' },\n },\n },\n {\n title: 'Check response body',\n script: `pm.test(\"Response body contains string\", () => {\n pm.expect(pm.response.text()).to.include('success')\n})`,\n mockResponse: {\n status: 200,\n body: 'This is a successful response',\n },\n },\n {\n title: 'Successful POST request',\n script: `pm.test(\"Successful POST request\", () => {\n pm.expect(pm.response.code).to.be.oneOf([201, 202])\n})`,\n mockResponse: {\n status: 201,\n body: { success: true },\n },\n },\n {\n title: 'Check header value',\n script: `pm.test(\"Content-Type is JSON\", () => {\n pm.expect(pm.response.headers.get('Content-Type')).to.include('application/json')\n})`,\n mockResponse: {\n status: 200,\n body: { success: true },\n headers: { 'Content-Type': 'application/json' },\n },\n },\n {\n title: 'Assert data types',\n script: `pm.test(\"Response data types are correct\", () => {\n const data = pm.response.json()\n pm.expect(data).to.be.an('object')\n pm.expect(data.name).to.be.a('string')\n pm.expect(data.age).to.be.a('number')\n pm.expect(data.hobbies).to.be.an('array')\n})`,\n mockResponse: {\n status: 200,\n body: { name: 'Jane', age: 23, hobbies: ['reading'] },\n headers: { 'Content-Type': 'application/json' },\n },\n },\n {\n title: 'Check object containment',\n script: `pm.test(\"Response contains expected object\", () => {\n const expected = { created: true, errors: [] }\n pm.expect(pm.response.json()).to.deep.include(expected)\n})`,\n mockResponse: {\n status: 200,\n body: { created: true, errors: [], id: 42 },\n headers: { 'Content-Type': 'application/json' },\n },\n },\n]\n"],"mappings":";AAWA,IAAa,kBAAmC;CAC9C;EACE,OAAO;EACP,QAAQ;;;EAGR,cAAc;GACZ,QAAQ;GACR,MAAM,EAAE,SAAS,KAAK;EACxB;CACF;CAYA;EACE,OAAO;EACP,QAAQ;;;;EAIR,cAAc;GACZ,QAAQ;GACR,MAAM,EAAE,MAAM,OAAO;GACrB,SAAS,EAAE,gBAAgB,mBAAmB;EAChD;CACF;CACA;EACE,OAAO;EACP,QAAQ;;;EAGR,cAAc;GACZ,QAAQ;GACR,MAAM,EAAE,SAAS,KAAK;GACtB,SAAS,EAAE,gBAAgB,mBAAmB;EAChD;CACF;CACA;EACE,OAAO;EACP,QAAQ;;;;;;;;;;;EAWR,cAAc;GACZ,QAAQ;GACR,MAAM;IAAE,IAAI;IAAG,MAAM;GAAY;GACjC,SAAS,EAAE,gBAAgB,mBAAmB;EAChD;CACF;CACA;EACE,OAAO;EACP,QAAQ;;;EAGR,cAAc;GACZ,QAAQ;GACR,MAAM;EACR;CACF;CACA;EACE,OAAO;EACP,QAAQ;;;EAGR,cAAc;GACZ,QAAQ;GACR,MAAM,EAAE,SAAS,KAAK;EACxB;CACF;CACA;EACE,OAAO;EACP,QAAQ;;;EAGR,cAAc;GACZ,QAAQ;GACR,MAAM,EAAE,SAAS,KAAK;GACtB,SAAS,EAAE,gBAAgB,mBAAmB;EAChD;CACF;CACA;EACE,OAAO;EACP,QAAQ;;;;;;;EAOR,cAAc;GACZ,QAAQ;GACR,MAAM;IAAE,MAAM;IAAQ,KAAK;IAAI,SAAS,CAAC,SAAS;GAAE;GACpD,SAAS,EAAE,gBAAgB,mBAAmB;EAChD;CACF;CACA;EACE,OAAO;EACP,QAAQ;;;;EAIR,cAAc;GACZ,QAAQ;GACR,MAAM;IAAE,SAAS;IAAM,QAAQ,CAAC;IAAG,IAAI;GAAG;GAC1C,SAAS,EAAE,gBAAgB,mBAAmB;EAChD;CACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-script.js","names":[],"sources":["../../src/helpers/get-script.ts"],"sourcesContent":["/**\n * Concatenates any number of pre/post-request scripts into a single string,\n * trimming empty/undefined values and separating them with newlines.\n *\n * Useful for merging scripts defined at different levels (document, operation, etc.).\n */\nexport const getScript = (...args: (string | undefined | null)[]): string => {\n return args\n .map((script) => script?.trim())\n .filter((script) => typeof script === 'string' && script.length > 0)\n .join('\\n')\n}\n"],"mappings":";;;;;;;AAMA,IAAa,aAAa,GAAG,SAAgD;
|
|
1
|
+
{"version":3,"file":"get-script.js","names":[],"sources":["../../src/helpers/get-script.ts"],"sourcesContent":["/**\n * Concatenates any number of pre/post-request scripts into a single string,\n * trimming empty/undefined values and separating them with newlines.\n *\n * Useful for merging scripts defined at different levels (document, operation, etc.).\n */\nexport const getScript = (...args: (string | undefined | null)[]): string => {\n return args\n .map((script) => script?.trim())\n .filter((script) => typeof script === 'string' && script.length > 0)\n .join('\\n')\n}\n"],"mappings":";;;;;;;AAMA,IAAa,aAAa,GAAG,SAAgD;CAC3E,OAAO,KACJ,KAAK,WAAW,QAAQ,KAAK,CAAC,CAAC,CAC/B,QAAQ,WAAW,OAAO,WAAW,YAAY,OAAO,SAAS,CAAC,CAAC,CACnE,KAAK,IAAI;AACd"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-sandbox-context.js","names":[],"sources":["../../../src/libs/execute-scripts/build-sandbox-context.ts"],"sourcesContent":["import type { VariableEntry } from '@scalar/workspace-store/request-example'\nimport { VariableList, VariableScope } from 'postman-collection'\n\nimport type { SerializableScopes } from './postman-adapter/sandbox-protocol'\n\nfunction toVariableList(entries: VariableEntry[]): VariableList {\n if (entries.length === 0) {\n // @ts-expect-error - VariableList constructor expects a Property<PropertyDefinition>\n return new VariableList(null, [])\n }\n return new VariableList(\n // @ts-expect-error - VariableList constructor expects a Property<PropertyDefinition>\n null,\n entries.map((e) => ({ key: e.key, value: e.value })),\n )\n}\n\ntype SandboxVariableContext = {\n globals: VariableScope\n collectionVariables: VariableScope\n environment: VariableScope\n data: Record<string, string>\n _variables: VariableScope\n}\n\n/**\n * Builds the Postman sandbox context object for variables (globals, environment,\n * collection, data, _variables) from plain key/value scopes. Used so pm.variables.get/set\n * and precedence match Postman behavior.\n *\n * This depends on postman-collection (`VariableScope`) but not on postman-sandbox, and is meant\n * to run inside the sandbox iframe after the scopes have crossed the `postMessage` boundary.\n */\nexport function buildSandboxContextFromScopes(scopes: SerializableScopes): SandboxVariableContext {\n return {\n globals: new VariableScope(toVariableList(scopes.globals)),\n collectionVariables: new VariableScope(toVariableList(scopes.collectionVariables)),\n environment: new VariableScope(toVariableList(scopes.environment)),\n data: scopes.data,\n _variables: new VariableScope(toVariableList(scopes.local)),\n }\n}\n"],"mappings":";;AAKA,SAAS,eAAe,SAAwC;
|
|
1
|
+
{"version":3,"file":"build-sandbox-context.js","names":[],"sources":["../../../src/libs/execute-scripts/build-sandbox-context.ts"],"sourcesContent":["import type { VariableEntry } from '@scalar/workspace-store/request-example'\nimport { VariableList, VariableScope } from 'postman-collection'\n\nimport type { SerializableScopes } from './postman-adapter/sandbox-protocol'\n\nfunction toVariableList(entries: VariableEntry[]): VariableList {\n if (entries.length === 0) {\n // @ts-expect-error - VariableList constructor expects a Property<PropertyDefinition>\n return new VariableList(null, [])\n }\n return new VariableList(\n // @ts-expect-error - VariableList constructor expects a Property<PropertyDefinition>\n null,\n entries.map((e) => ({ key: e.key, value: e.value })),\n )\n}\n\ntype SandboxVariableContext = {\n globals: VariableScope\n collectionVariables: VariableScope\n environment: VariableScope\n data: Record<string, string>\n _variables: VariableScope\n}\n\n/**\n * Builds the Postman sandbox context object for variables (globals, environment,\n * collection, data, _variables) from plain key/value scopes. Used so pm.variables.get/set\n * and precedence match Postman behavior.\n *\n * This depends on postman-collection (`VariableScope`) but not on postman-sandbox, and is meant\n * to run inside the sandbox iframe after the scopes have crossed the `postMessage` boundary.\n */\nexport function buildSandboxContextFromScopes(scopes: SerializableScopes): SandboxVariableContext {\n return {\n globals: new VariableScope(toVariableList(scopes.globals)),\n collectionVariables: new VariableScope(toVariableList(scopes.collectionVariables)),\n environment: new VariableScope(toVariableList(scopes.environment)),\n data: scopes.data,\n _variables: new VariableScope(toVariableList(scopes.local)),\n }\n}\n"],"mappings":";;AAKA,SAAS,eAAe,SAAwC;CAC9D,IAAI,QAAQ,WAAW,GAErB,OAAO,IAAI,aAAa,MAAM,CAAC,CAAC;CAElC,OAAO,IAAI,aAET,MACA,QAAQ,KAAK,OAAO;EAAE,KAAK,EAAE;EAAK,OAAO,EAAE;CAAM,EAAE,CACrD;AACF;;;;;;;;;AAkBA,SAAgB,8BAA8B,QAAoD;CAChG,OAAO;EACL,SAAS,IAAI,cAAc,eAAe,OAAO,OAAO,CAAC;EACzD,qBAAqB,IAAI,cAAc,eAAe,OAAO,mBAAmB,CAAC;EACjF,aAAa,IAAI,cAAc,eAAe,OAAO,WAAW,CAAC;EACjE,MAAM,OAAO;EACb,YAAY,IAAI,cAAc,eAAe,OAAO,KAAK,CAAC;CAC5D;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"console.js","names":[],"sources":["../../../../src/libs/execute-scripts/context/console.ts"],"sourcesContent":["export interface ConsoleContext {\n log: (...args: any[]) => void\n error: (...args: any[]) => void\n warn: (...args: any[]) => void\n info: (...args: any[]) => void\n debug: (...args: any[]) => void\n trace: (...args: any[]) => void\n table: (tabularData: any, properties?: readonly string[]) => void\n}\n\nexport const createConsoleContext = (): ConsoleContext => ({\n log: (...args: any[]) => console.log('[Script]', ...args),\n error: (...args: any[]) => console.error('[Script Error]', ...args),\n warn: (...args: any[]) => console.warn('[Script Warning]', ...args),\n info: (...args: any[]) => console.info('[Script Info]', ...args),\n debug: (...args: any[]) => console.debug('[Script Debug]', ...args),\n trace: (...args: any[]) => console.trace('[Script Trace]', ...args),\n table: (tabularData: any, properties?: readonly string[]) => console.table(tabularData, properties),\n})\n"],"mappings":";AAUA,IAAa,8BAA8C;CACzD,MAAM,GAAG,SAAgB,QAAQ,IAAI,YAAY,GAAG,
|
|
1
|
+
{"version":3,"file":"console.js","names":[],"sources":["../../../../src/libs/execute-scripts/context/console.ts"],"sourcesContent":["export interface ConsoleContext {\n log: (...args: any[]) => void\n error: (...args: any[]) => void\n warn: (...args: any[]) => void\n info: (...args: any[]) => void\n debug: (...args: any[]) => void\n trace: (...args: any[]) => void\n table: (tabularData: any, properties?: readonly string[]) => void\n}\n\nexport const createConsoleContext = (): ConsoleContext => ({\n log: (...args: any[]) => console.log('[Script]', ...args),\n error: (...args: any[]) => console.error('[Script Error]', ...args),\n warn: (...args: any[]) => console.warn('[Script Warning]', ...args),\n info: (...args: any[]) => console.info('[Script Info]', ...args),\n debug: (...args: any[]) => console.debug('[Script Debug]', ...args),\n trace: (...args: any[]) => console.trace('[Script Trace]', ...args),\n table: (tabularData: any, properties?: readonly string[]) => console.table(tabularData, properties),\n})\n"],"mappings":";AAUA,IAAa,8BAA8C;CACzD,MAAM,GAAG,SAAgB,QAAQ,IAAI,YAAY,GAAG,IAAI;CACxD,QAAQ,GAAG,SAAgB,QAAQ,MAAM,kBAAkB,GAAG,IAAI;CAClE,OAAO,GAAG,SAAgB,QAAQ,KAAK,oBAAoB,GAAG,IAAI;CAClE,OAAO,GAAG,SAAgB,QAAQ,KAAK,iBAAiB,GAAG,IAAI;CAC/D,QAAQ,GAAG,SAAgB,QAAQ,MAAM,kBAAkB,GAAG,IAAI;CAClE,QAAQ,GAAG,SAAgB,QAAQ,MAAM,kBAAkB,GAAG,IAAI;CAClE,QAAQ,aAAkB,eAAmC,QAAQ,MAAM,aAAa,UAAU;AACpG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute-post-response-script.js","names":[],"sources":["../../../src/libs/execute-scripts/execute-post-response-script.ts"],"sourcesContent":["import type { RequestFactory, VariablesStore } from '@scalar/workspace-store/request-example'\n\nimport { createConsoleContext } from './context/console'\nimport { executeInPostmanSandbox } from './postman-adapter/sandbox-adapter'\n\nexport type TestResult = {\n title: string\n passed: boolean\n duration: number\n error?: string\n status: 'pending' | 'passed' | 'failed'\n}\n\nexport const executePostResponseScript = async (\n script: string | undefined,\n data: {\n requestBuilder: RequestFactory\n response: Response\n onTestResultsUpdate?: ((results: TestResult[]) => void) | undefined\n variablesStore?: VariablesStore\n },\n): Promise<void> => {\n if (!script) {\n return\n }\n await executeInPostmanSandbox({\n script,\n type: 'post-response',\n context: {\n requestBuilder: data.requestBuilder,\n response: data.response,\n scriptConsole: createConsoleContext(),\n variablesStore: data.variablesStore,\n },\n onTestResultsUpdate: data.onTestResultsUpdate,\n })\n}\n"],"mappings":";;;AAaA,IAAa,4BAA4B,OACvC,QACA,SAMkB;
|
|
1
|
+
{"version":3,"file":"execute-post-response-script.js","names":[],"sources":["../../../src/libs/execute-scripts/execute-post-response-script.ts"],"sourcesContent":["import type { RequestFactory, VariablesStore } from '@scalar/workspace-store/request-example'\n\nimport { createConsoleContext } from './context/console'\nimport { executeInPostmanSandbox } from './postman-adapter/sandbox-adapter'\n\nexport type TestResult = {\n title: string\n passed: boolean\n duration: number\n error?: string\n status: 'pending' | 'passed' | 'failed'\n}\n\nexport const executePostResponseScript = async (\n script: string | undefined,\n data: {\n requestBuilder: RequestFactory\n response: Response\n onTestResultsUpdate?: ((results: TestResult[]) => void) | undefined\n variablesStore?: VariablesStore\n },\n): Promise<void> => {\n if (!script) {\n return\n }\n await executeInPostmanSandbox({\n script,\n type: 'post-response',\n context: {\n requestBuilder: data.requestBuilder,\n response: data.response,\n scriptConsole: createConsoleContext(),\n variablesStore: data.variablesStore,\n },\n onTestResultsUpdate: data.onTestResultsUpdate,\n })\n}\n"],"mappings":";;;AAaA,IAAa,4BAA4B,OACvC,QACA,SAMkB;CAClB,IAAI,CAAC,QACH;CAEF,MAAM,wBAAwB;EAC5B;EACA,MAAM;EACN,SAAS;GACP,gBAAgB,KAAK;GACrB,UAAU,KAAK;GACf,eAAe,qBAAqB;GACpC,gBAAgB,KAAK;EACvB;EACA,qBAAqB,KAAK;CAC5B,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute-pre-request-script.js","names":[],"sources":["../../../src/libs/execute-scripts/execute-pre-request-script.ts"],"sourcesContent":["import type { RequestFactory, VariablesStore } from '@scalar/workspace-store/request-example'\n\nimport type { TestResult } from '@/libs/execute-scripts'\n\nimport { createConsoleContext } from './context/console'\nimport { executeInPostmanSandbox } from './postman-adapter/sandbox-adapter'\n\n/**\n * Runs the pre-request script in the Postman sandbox with a Postman-shaped\n * `pm.request` built from {@link RequestFactory}. Header and method changes from\n * the script are merged back from the sandbox execution result (the in-memory\n * Postman `Request` on the host is not updated). Variable changes use the store.\n */\nexport const executePreRequestScript = async (\n script: string | undefined,\n data: {\n requestBuilder: RequestFactory\n variablesStore?: VariablesStore\n onTestResultsUpdate?: ((results: TestResult[]) => void) | undefined\n },\n): Promise<void> => {\n if (!script) {\n return\n }\n await executeInPostmanSandbox({\n script,\n type: 'pre-request',\n context: {\n scriptConsole: createConsoleContext(),\n requestBuilder: data.requestBuilder,\n variablesStore: data.variablesStore,\n },\n onTestResultsUpdate: data.onTestResultsUpdate,\n })\n}\n"],"mappings":";;;;;;;;;AAaA,IAAa,0BAA0B,OACrC,QACA,SAKkB;
|
|
1
|
+
{"version":3,"file":"execute-pre-request-script.js","names":[],"sources":["../../../src/libs/execute-scripts/execute-pre-request-script.ts"],"sourcesContent":["import type { RequestFactory, VariablesStore } from '@scalar/workspace-store/request-example'\n\nimport type { TestResult } from '@/libs/execute-scripts'\n\nimport { createConsoleContext } from './context/console'\nimport { executeInPostmanSandbox } from './postman-adapter/sandbox-adapter'\n\n/**\n * Runs the pre-request script in the Postman sandbox with a Postman-shaped\n * `pm.request` built from {@link RequestFactory}. Header and method changes from\n * the script are merged back from the sandbox execution result (the in-memory\n * Postman `Request` on the host is not updated). Variable changes use the store.\n */\nexport const executePreRequestScript = async (\n script: string | undefined,\n data: {\n requestBuilder: RequestFactory\n variablesStore?: VariablesStore\n onTestResultsUpdate?: ((results: TestResult[]) => void) | undefined\n },\n): Promise<void> => {\n if (!script) {\n return\n }\n await executeInPostmanSandbox({\n script,\n type: 'pre-request',\n context: {\n scriptConsole: createConsoleContext(),\n requestBuilder: data.requestBuilder,\n variablesStore: data.variablesStore,\n },\n onTestResultsUpdate: data.onTestResultsUpdate,\n })\n}\n"],"mappings":";;;;;;;;;AAaA,IAAa,0BAA0B,OACrC,QACA,SAKkB;CAClB,IAAI,CAAC,QACH;CAEF,MAAM,wBAAwB;EAC5B;EACA,MAAM;EACN,SAAS;GACP,eAAe,qBAAqB;GACpC,gBAAgB,KAAK;GACrB,gBAAgB,KAAK;EACvB;EACA,qBAAqB,KAAK;CAC5B,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-factory-adapter.js","names":[],"sources":["../../../../src/libs/execute-scripts/postman-adapter/request-factory-adapter.ts"],"sourcesContent":["import type { RequestFactory } from '@scalar/workspace-store/request-example'\nimport { Request as PostmanRequest } from 'postman-collection'\n\nconst assertSupported = (condition: boolean, message: string): void => {\n console.assert(condition, `[Scalar Postman adapter] ${message}`)\n}\n\n/**\n * Maps Scalar {@link RequestFactory} body data into a Postman Collection request\n * body definition for the sandbox (`pm.request.body`).\n *\n * File/Blob parts are not supported in script context; raw non-string bodies are\n * treated as empty with a console assertion.\n */\nconst buildPostmanBodyDefinition = (\n body: RequestFactory['body'],\n):\n | {\n mode: string\n raw?: string\n urlencoded?: { key: string; value: string }[]\n formdata?: { key: string; type: string; value: string }[]\n }\n | undefined => {\n if (!body) {\n return undefined\n }\n\n if (body.mode === 'raw') {\n if (typeof body.value === 'string') {\n return { mode: 'raw', raw: body.value }\n }\n assertSupported(false, 'raw body must be a string for pm.request; File/Blob is not supported')\n return { mode: 'raw', raw: '' }\n }\n\n if (body.mode === 'urlencoded') {\n return {\n mode: 'urlencoded',\n urlencoded: body.value.map(({ key, value }) => ({\n key,\n value,\n })),\n }\n }\n\n if (body.mode === 'formdata') {\n const unsupported = body.value.filter((item) => item.type !== 'text')\n if (unsupported.length > 0) {\n assertSupported(false, 'formdata file/blob parts are not supported in pm.request.body for scripts')\n }\n return {\n mode: 'formdata',\n formdata: body.value\n .filter((item): item is { type: 'text'; key: string; value: string } => item.type === 'text')\n .map(({ key, value }) => ({\n key,\n type: 'text',\n value,\n })),\n }\n }\n\n return undefined\n}\n\n/**\n * Builds a Postman Collection {@link PostmanRequest} from a workspace-store\n * {@link RequestFactory} so `pm.request` in the sandbox matches Postman’s shape\n * (method, url, headers, body) as closely as practical.\n *\n * @see https://learning.postman.com/docs/tests-and-scripts/write-scripts/postman-sandbox-reference/pm-request/\n */\nexport const createPostmanRequestFromFactory = (factory: RequestFactory): PostmanRequest => {\n const headerList: { key: string; value: string }[] = []\n factory.headers.forEach((value, key) => {\n headerList.push({\n key,\n value: value,\n })\n })\n\n const bodyDef = buildPostmanBodyDefinition(factory.body)\n\n return new PostmanRequest({\n url: factory.baseUrl + factory.path.raw,\n method: factory.method,\n header: headerList,\n body: bodyDef,\n })\n}\n\n/**\n * Applies a Postman request definition (e.g. `execution.request` from the sandbox callback after IPC)\n * onto a {@link RequestFactory}. Header and method are copied; URL is not synced to the factory.\n */\nexport const syncPlainPostmanRequestToRequestFactory = (definition: unknown, factory: RequestFactory): void => {\n if (definition === null || definition === undefined || typeof definition !== 'object') {\n return\n }\n try {\n const reconstructed = new PostmanRequest(definition as ConstructorParameters<typeof PostmanRequest>[0])\n syncPostmanRequestToRequestFactory(reconstructed, factory)\n } catch {\n /** Malformed payload after teleport; leave factory unchanged. */\n }\n}\n\n/** Copies header and method from a live {@link PostmanRequest} onto {@link RequestFactory}. */\nconst syncPostmanRequestToRequestFactory = (postmanRequest: PostmanRequest, factory: RequestFactory): void => {\n const method = postmanRequest.method\n if (typeof method === 'string' && method.length > 0) {\n factory.method = method.toUpperCase()\n }\n\n const nextHeaders = new Headers()\n postmanRequest.headers?.each((header) => {\n if (header.disabled) {\n return\n }\n const key = header.key\n if (key) {\n /** `Headers.set` drops prior values for the same name; Postman `headers.add` keeps multiple entries. */\n nextHeaders.append(key, header.value ?? '')\n }\n })\n factory.headers = nextHeaders\n}\n"],"mappings":";;AAGA,IAAM,mBAAmB,WAAoB,YAA0B;
|
|
1
|
+
{"version":3,"file":"request-factory-adapter.js","names":[],"sources":["../../../../src/libs/execute-scripts/postman-adapter/request-factory-adapter.ts"],"sourcesContent":["import type { RequestFactory } from '@scalar/workspace-store/request-example'\nimport { Request as PostmanRequest } from 'postman-collection'\n\nconst assertSupported = (condition: boolean, message: string): void => {\n console.assert(condition, `[Scalar Postman adapter] ${message}`)\n}\n\n/**\n * Maps Scalar {@link RequestFactory} body data into a Postman Collection request\n * body definition for the sandbox (`pm.request.body`).\n *\n * File/Blob parts are not supported in script context; raw non-string bodies are\n * treated as empty with a console assertion.\n */\nconst buildPostmanBodyDefinition = (\n body: RequestFactory['body'],\n):\n | {\n mode: string\n raw?: string\n urlencoded?: { key: string; value: string }[]\n formdata?: { key: string; type: string; value: string }[]\n }\n | undefined => {\n if (!body) {\n return undefined\n }\n\n if (body.mode === 'raw') {\n if (typeof body.value === 'string') {\n return { mode: 'raw', raw: body.value }\n }\n assertSupported(false, 'raw body must be a string for pm.request; File/Blob is not supported')\n return { mode: 'raw', raw: '' }\n }\n\n if (body.mode === 'urlencoded') {\n return {\n mode: 'urlencoded',\n urlencoded: body.value.map(({ key, value }) => ({\n key,\n value,\n })),\n }\n }\n\n if (body.mode === 'formdata') {\n const unsupported = body.value.filter((item) => item.type !== 'text')\n if (unsupported.length > 0) {\n assertSupported(false, 'formdata file/blob parts are not supported in pm.request.body for scripts')\n }\n return {\n mode: 'formdata',\n formdata: body.value\n .filter((item): item is { type: 'text'; key: string; value: string } => item.type === 'text')\n .map(({ key, value }) => ({\n key,\n type: 'text',\n value,\n })),\n }\n }\n\n return undefined\n}\n\n/**\n * Builds a Postman Collection {@link PostmanRequest} from a workspace-store\n * {@link RequestFactory} so `pm.request` in the sandbox matches Postman’s shape\n * (method, url, headers, body) as closely as practical.\n *\n * @see https://learning.postman.com/docs/tests-and-scripts/write-scripts/postman-sandbox-reference/pm-request/\n */\nexport const createPostmanRequestFromFactory = (factory: RequestFactory): PostmanRequest => {\n const headerList: { key: string; value: string }[] = []\n factory.headers.forEach((value, key) => {\n headerList.push({\n key,\n value: value,\n })\n })\n\n const bodyDef = buildPostmanBodyDefinition(factory.body)\n\n return new PostmanRequest({\n url: factory.baseUrl + factory.path.raw,\n method: factory.method,\n header: headerList,\n body: bodyDef,\n })\n}\n\n/**\n * Applies a Postman request definition (e.g. `execution.request` from the sandbox callback after IPC)\n * onto a {@link RequestFactory}. Header and method are copied; URL is not synced to the factory.\n */\nexport const syncPlainPostmanRequestToRequestFactory = (definition: unknown, factory: RequestFactory): void => {\n if (definition === null || definition === undefined || typeof definition !== 'object') {\n return\n }\n try {\n const reconstructed = new PostmanRequest(definition as ConstructorParameters<typeof PostmanRequest>[0])\n syncPostmanRequestToRequestFactory(reconstructed, factory)\n } catch {\n /** Malformed payload after teleport; leave factory unchanged. */\n }\n}\n\n/** Copies header and method from a live {@link PostmanRequest} onto {@link RequestFactory}. */\nconst syncPostmanRequestToRequestFactory = (postmanRequest: PostmanRequest, factory: RequestFactory): void => {\n const method = postmanRequest.method\n if (typeof method === 'string' && method.length > 0) {\n factory.method = method.toUpperCase()\n }\n\n const nextHeaders = new Headers()\n postmanRequest.headers?.each((header) => {\n if (header.disabled) {\n return\n }\n const key = header.key\n if (key) {\n /** `Headers.set` drops prior values for the same name; Postman `headers.add` keeps multiple entries. */\n nextHeaders.append(key, header.value ?? '')\n }\n })\n factory.headers = nextHeaders\n}\n"],"mappings":";;AAGA,IAAM,mBAAmB,WAAoB,YAA0B;CACrE,QAAQ,OAAO,WAAW,4BAA4B,SAAS;AACjE;;;;;;;;AASA,IAAM,8BACJ,SAQe;CACf,IAAI,CAAC,MACH;CAGF,IAAI,KAAK,SAAS,OAAO;EACvB,IAAI,OAAO,KAAK,UAAU,UACxB,OAAO;GAAE,MAAM;GAAO,KAAK,KAAK;EAAM;EAExC,gBAAgB,OAAO,sEAAsE;EAC7F,OAAO;GAAE,MAAM;GAAO,KAAK;EAAG;CAChC;CAEA,IAAI,KAAK,SAAS,cAChB,OAAO;EACL,MAAM;EACN,YAAY,KAAK,MAAM,KAAK,EAAE,KAAK,aAAa;GAC9C;GACA;EACF,EAAE;CACJ;CAGF,IAAI,KAAK,SAAS,YAAY;EAE5B,IADoB,KAAK,MAAM,QAAQ,SAAS,KAAK,SAAS,MAC1D,CAAA,CAAY,SAAS,GACvB,gBAAgB,OAAO,2EAA2E;EAEpG,OAAO;GACL,MAAM;GACN,UAAU,KAAK,MACZ,QAAQ,SAA+D,KAAK,SAAS,MAAM,CAAC,CAC5F,KAAK,EAAE,KAAK,aAAa;IACxB;IACA,MAAM;IACN;GACF,EAAE;EACN;CACF;AAGF;;;;;;;;AASA,IAAa,mCAAmC,YAA4C;CAC1F,MAAM,aAA+C,CAAC;CACtD,QAAQ,QAAQ,SAAS,OAAO,QAAQ;EACtC,WAAW,KAAK;GACd;GACO;EACT,CAAC;CACH,CAAC;CAED,MAAM,UAAU,2BAA2B,QAAQ,IAAI;CAEvD,OAAO,IAAI,QAAe;EACxB,KAAK,QAAQ,UAAU,QAAQ,KAAK;EACpC,QAAQ,QAAQ;EAChB,QAAQ;EACR,MAAM;CACR,CAAC;AACH;;;;;AAMA,IAAa,2CAA2C,YAAqB,YAAkC;CAC7G,IAAI,eAAe,QAAQ,eAAe,KAAA,KAAa,OAAO,eAAe,UAC3E;CAEF,IAAI;EAEF,mCAAmC,IADT,QAAe,UACN,GAAe,OAAO;CAC3D,QAAQ,CAER;AACF;;AAGA,IAAM,sCAAsC,gBAAgC,YAAkC;CAC5G,MAAM,SAAS,eAAe;CAC9B,IAAI,OAAO,WAAW,YAAY,OAAO,SAAS,GAChD,QAAQ,SAAS,OAAO,YAAY;CAGtC,MAAM,cAAc,IAAI,QAAQ;CAChC,eAAe,SAAS,MAAM,WAAW;EACvC,IAAI,OAAO,UACT;EAEF,MAAM,MAAM,OAAO;EACnB,IAAI;;EAEF,YAAY,OAAO,KAAK,OAAO,SAAS,EAAE;CAE9C,CAAC;CACD,QAAQ,UAAU;AACpB"}
|
|
@@ -248,6 +248,6 @@ var executeInPostmanSandbox = async ({ script, type, context: { requestBuilder,
|
|
|
248
248
|
return variablesStore;
|
|
249
249
|
};
|
|
250
250
|
//#endregion
|
|
251
|
-
export { executeInPostmanSandbox, prewarmSandboxFrame };
|
|
251
|
+
export { executeInPostmanSandbox, prewarmSandboxFrame, sandboxFrameUrl, toPostmanResponse };
|
|
252
252
|
|
|
253
253
|
//# sourceMappingURL=sandbox-adapter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox-adapter.js","names":[],"sources":["../../../../src/libs/execute-scripts/postman-adapter/sandbox-adapter.ts"],"sourcesContent":["import { normalizeError } from '@scalar/helpers/errors/normalize-error'\nimport { isElectron } from '@scalar/helpers/general/is-electron'\nimport type { RequestFactory, VariablesStore } from '@scalar/workspace-store/request-example'\n\nimport type { ConsoleContext } from '../context/console'\nimport type { TestResult } from '../execute-post-response-script'\nimport { getVariableScopesFromStore, toVariableEntries } from '../variables-store'\nimport { createPostmanRequestFromFactory, syncPlainPostmanRequestToRequestFactory } from './request-factory-adapter'\nimport { getSandboxOrigins } from './sandbox-origins'\nimport {\n type PostmanResponseDefinition,\n SANDBOX_CHANNEL,\n type SandboxExecuteRequest,\n type SandboxOutboundMessage,\n} from './sandbox-protocol'\n\n/**\n * Host-side adapter for pre/post-request scripts.\n *\n * The actual script execution happens in `postman-sandbox`, which requires `eval`. To keep\n * `'unsafe-eval'` out of the main application CSP, that execution is delegated to an iframe loaded\n * from a real same-origin `.html` file with its own permissive CSP (see `sandbox-frame-server.ts`).\n * This module therefore must NOT import `postman-sandbox`; it only serializes inputs, talks to the\n * iframe over `postMessage`, and applies the results back to the live store/request.\n */\nexport const toPostmanResponse = async (response: Response): Promise<PostmanResponseDefinition> => {\n // Read as ArrayBuffer (not text) so binary payloads — gzipped JSON, images, PDFs, protobuf —\n // survive the trip through `postMessage` byte-for-byte. Round-tripping through `response.text()`\n // + `TextEncoder` corrupts any sequence that is not valid UTF-8 because the decoder replaces\n // invalid units with U+FFFD before we ever encode them back to bytes.\n const buffer = await response.arrayBuffer()\n const responseBytes = Array.from(new Uint8Array(buffer))\n\n return {\n code: response.status,\n status: response.statusText || String(response.status),\n header: Array.from(response.headers.entries()).map(([key, value]) => ({ key, value })),\n stream: {\n type: 'Buffer',\n data: responseBytes,\n },\n }\n}\n\n/**\n * Resolves the sandbox iframe URL.\n *\n * On the web we anchor to the origin root because the SPA router owns the current path: a\n * relative `sandbox.html` would resolve under the active route and get rewritten to `index.html`\n * by the history fallback. In Electron the document path is literal, so a sibling resolution\n * works — and we use the `isElectron` helper rather than a `file://` check because Electron's\n * dev mode is served over `http://localhost`.\n *\n * Exported for tests; production code reaches it through `ensureSandboxFrame`.\n */\nexport const sandboxFrameUrl = (): string => {\n if (isElectron()) {\n return new URL('sandbox.html', document.baseURI).href\n }\n return new URL('/sandbox.html', window.location.origin).href\n}\n\n/**\n * Delivers a single execute request to the sandbox and forwards every message it produces (test\n * results, console output, and the final `done`) to `onMessage`.\n *\n * The default transport is the iframe (see {@link iframeTransport}). It is injectable so that node\n * unit tests can run the real execution in-process (`runSandboxExecution`) without a DOM, and without\n * the production host bundle ever importing `postman-sandbox`.\n */\ntype SandboxTransport = (request: SandboxExecuteRequest, onMessage: (message: SandboxOutboundMessage) => void) => void\n\ntype SandboxFrame = {\n element: HTMLIFrameElement\n id: number\n url: string\n window: Window\n}\n\nlet framePromise: Promise<SandboxFrame> | undefined\nlet activeFrameId: number | undefined\nlet nextFrameId = 0\n\n/**\n * Maximum time we wait for a freshly created sandbox iframe to post its `ready` handshake.\n *\n * The iframe `error` event fires only when the `.html` document itself fails to load. If the\n * document loads (HTTP 200) but the script inside never reaches `startSandboxFrameServer` — a\n * 404 on the bundle, an import error, a CSP block, a crash during boot — no `ready` message is\n * posted and no `error` event fires. Without this ceiling the readiness promise hangs forever;\n * because it is module-cached, every later call awaits the same dead promise and all script\n * execution freezes for the session. The window is generous: readiness only requires loading one\n * same-origin HTML file plus the sandbox bundle, so timing out means the bundle is genuinely\n * broken rather than slow.\n */\nconst SANDBOX_READY_TIMEOUT_MS = 30 * 1000\n\nconst clearActiveFrame = (id: number): void => {\n if (activeFrameId === id) {\n framePromise = undefined\n activeFrameId = undefined\n }\n}\n\nconst invalidateSandboxFrame = ({ element, id }: SandboxFrame): void => {\n clearActiveFrame(id)\n element.remove()\n}\n\nconst assertSandboxFrameLocation = (sandboxFrame: SandboxFrame): void => {\n const { element, url, window: frameWindow } = sandboxFrame\n if (!element.isConnected || !document.body.contains(element)) {\n invalidateSandboxFrame(sandboxFrame)\n throw new Error('Sandbox iframe was detached before script execution')\n }\n\n if (element.contentWindow !== frameWindow) {\n invalidateSandboxFrame(sandboxFrame)\n throw new Error('Sandbox iframe window changed before script execution')\n }\n\n let currentUrl: string\n\n try {\n currentUrl = frameWindow.location.href\n } catch (error) {\n invalidateSandboxFrame(sandboxFrame)\n const message = normalizeError(error).message\n throw new Error(`Could not verify sandbox iframe location before script execution: ${message}`)\n }\n\n if (currentUrl !== url) {\n invalidateSandboxFrame(sandboxFrame)\n throw new Error('Sandbox iframe navigated before script execution')\n }\n}\n\n/** Lazily creates a single hidden sandbox iframe and resolves once it reports readiness. */\nconst ensureSandboxFrame = (): Promise<SandboxFrame> => {\n if (framePromise) {\n return framePromise\n }\n\n framePromise = new Promise<SandboxFrame>((resolve, reject) => {\n const frameId = nextFrameId++\n activeFrameId = frameId\n const iframe = document.createElement('iframe')\n iframe.setAttribute('aria-hidden', 'true')\n iframe.style.display = 'none'\n const iframeUrl = sandboxFrameUrl()\n iframe.src = iframeUrl\n\n const { receive: expectedOrigin } = getSandboxOrigins()\n\n // Cleanup shared by the success, load-error, and readiness-timeout paths: cancel the timer and\n // drop the window listener so exactly one of the three settles the promise without leaking.\n let readyTimeoutId: ReturnType<typeof setTimeout>\n const stopWaiting = () => {\n clearTimeout(readyTimeoutId)\n window.removeEventListener('message', onReady)\n }\n\n const onReady = (event: MessageEvent) => {\n // Same-origin iframe: reject anything that does not come from our own origin and our own frame.\n if (event.origin !== expectedOrigin || event.source !== iframe.contentWindow) {\n return\n }\n const data = event.data as SandboxOutboundMessage | null\n if (!data || data.channel !== SANDBOX_CHANNEL || data.kind !== 'ready') {\n return\n }\n stopWaiting()\n if (!iframe.contentWindow) {\n // Tear the orphaned node down so a retry can append a fresh frame without piling up.\n iframe.remove()\n clearActiveFrame(frameId)\n reject(new Error('Sandbox iframe has no content window'))\n return\n }\n resolve({\n element: iframe,\n id: frameId,\n url: iframeUrl,\n window: iframe.contentWindow,\n })\n }\n\n iframe.addEventListener('error', () => {\n stopWaiting()\n // Drop the broken iframe from the DOM. Without this, a transient failure (network blip, bad\n // bundle path, etc.) leaves a dead `<iframe>` attached, and every retry appends another one,\n // growing the DOM unboundedly across the session.\n iframe.remove()\n clearActiveFrame(frameId)\n reject(new Error('Failed to load the script sandbox iframe'))\n })\n\n // Catch the silent failure mode the `error` event cannot see: the document loaded but its\n // script never announced readiness. Drop the dead frame and clear the cache so the next call\n // retries with a fresh iframe instead of awaiting this one forever.\n readyTimeoutId = setTimeout(() => {\n window.removeEventListener('message', onReady)\n iframe.remove()\n clearActiveFrame(frameId)\n reject(new Error(`Sandbox iframe did not report readiness within ${SANDBOX_READY_TIMEOUT_MS}ms`))\n }, SANDBOX_READY_TIMEOUT_MS)\n\n window.addEventListener('message', onReady)\n document.body.appendChild(iframe)\n })\n\n return framePromise\n}\n\n/**\n * Maximum time we wait for the iframe to emit `kind: 'done'` for a given execute request.\n *\n * postman-sandbox enforces its own per-script timeout inside the sandbox, so reaching this ceiling\n * means the iframe itself wedged (renderer OOM, hard crash, blocking infinite loop in a worker,\n * messages dropped). The default is generous on purpose — this is a circuit-breaker for crashes,\n * not a budget for user scripts.\n */\nconst SANDBOX_EXECUTION_TIMEOUT_MS = 5 * 60 * 1000\n\nconst iframeTransport: SandboxTransport = (request, onMessage) => {\n ensureSandboxFrame()\n .then((sandboxFrame) => {\n assertSandboxFrameLocation(sandboxFrame)\n\n const frame = sandboxFrame.window\n const { receive: expectedOrigin, send: targetOrigin } = getSandboxOrigins()\n\n // Defensive circuit-breaker: if the iframe dies mid-execution we would otherwise wait on a\n // `done` message that will never arrive, leaking the listener and leaving the caller's\n // promise unresolved. The timer is cancelled the moment `done` is observed below.\n const timeoutId = setTimeout(() => {\n window.removeEventListener('message', handleMessage)\n onMessage({\n channel: SANDBOX_CHANNEL,\n kind: 'done',\n id: request.id,\n error: `Script execution timed out after ${SANDBOX_EXECUTION_TIMEOUT_MS}ms`,\n })\n }, SANDBOX_EXECUTION_TIMEOUT_MS)\n\n const handleMessage = (event: MessageEvent) => {\n // Reject anything that is not from our own origin and our own frame. Without this guard,\n // any window in the same browsing context group could send fabricated `done`/`test-results`\n // messages and trick the host into applying attacker-controlled variables and requests.\n if (event.origin !== expectedOrigin || event.source !== frame) {\n return\n }\n const data = event.data as SandboxOutboundMessage | null\n if (!data || data.channel !== SANDBOX_CHANNEL || !('id' in data) || data.id !== request.id) {\n return\n }\n if (data.kind === 'done') {\n clearTimeout(timeoutId)\n window.removeEventListener('message', handleMessage)\n }\n onMessage(data)\n }\n\n window.addEventListener('message', handleMessage)\n // Pin the target origin for web origins. `file://` is the only exception: Chromium does not\n // accept it as a target origin, so Electron file builds use `'*'` and rely on the source-window\n // checks above for receive-side isolation.\n frame.postMessage(request, targetOrigin)\n })\n .catch((error: unknown) => {\n onMessage({\n channel: SANDBOX_CHANNEL,\n kind: 'done',\n id: request.id,\n error: error instanceof Error ? error.message : String(error),\n })\n })\n}\nlet transport: SandboxTransport = iframeTransport\n\n/** Override how the host reaches the sandbox (e.g. an in-process transport for tests). */\nexport const setSandboxTransport = (next: SandboxTransport): void => {\n transport = next\n}\n\n/**\n * Eagerly creates the sandbox iframe so the first script execution does not pay the cold-start\n * cost of loading `sandbox.html` and the `postman-sandbox` bundle (noticeably slow on Electron).\n *\n * This is a best-effort optimization: it only runs in a DOM context, reuses the same module-cached\n * frame {@link executeInPostmanSandbox} later awaits, and swallows failures so a warm-up problem can\n * never surface to the caller — the real execution path retries and reports errors on its own.\n *\n * Call this only when scripts are actually present; warming the sandbox for a request without\n * scripts would defeat the lazy creation that keeps script-free requests cheap.\n */\nexport const prewarmSandboxFrame = (): void => {\n if (typeof document === 'undefined') {\n return\n }\n\n ensureSandboxFrame().catch(() => {\n // Ignored on purpose: warm-up is opportunistic and the execution path handles real errors.\n })\n}\n\nlet nextExecutionId = 0\n\nexport const executeInPostmanSandbox = async ({\n script,\n type,\n context: { requestBuilder, response, variablesStore, scriptConsole },\n onTestResultsUpdate,\n}: {\n script: string\n type: 'pre-request' | 'post-response'\n context: {\n /** Postman Collection request for `pm.request` (not the browser Fetch API Request). */\n requestBuilder?: RequestFactory\n response?: Response\n variablesStore?: VariablesStore\n scriptConsole: ConsoleContext\n }\n onTestResultsUpdate?: ((results: TestResult[]) => void) | undefined\n}): Promise<VariablesStore | undefined> => {\n // Serialize everything that crosses the iframe boundary on the host, so the iframe only deals with\n // plain, structured-cloneable data. The response body is read here (it requires the live Response).\n const requestDefinition = requestBuilder ? createPostmanRequestFromFactory(requestBuilder).toJSON() : undefined\n const responseDefinition = response ? await toPostmanResponse(response) : undefined\n const scopes = variablesStore ? getVariableScopesFromStore(variablesStore) : undefined\n\n const request: SandboxExecuteRequest = {\n channel: SANDBOX_CHANNEL,\n kind: 'execute',\n id: String(nextExecutionId++),\n listen: type === 'pre-request' ? 'prerequest' : 'test',\n script,\n scopes,\n request: requestDefinition,\n response: responseDefinition,\n }\n\n await new Promise<void>((resolve) => {\n const onMessage = (data: SandboxOutboundMessage) => {\n if (data.kind === 'test-results') {\n onTestResultsUpdate?.(data.results)\n return\n }\n\n if (data.kind === 'console') {\n const consoleMethod = scriptConsole[data.level] ?? scriptConsole.log\n ;(consoleMethod as (...params: unknown[]) => void)(...data.args)\n return\n }\n\n if (data.kind === 'done') {\n if (variablesStore && data.variables) {\n const { local, collection, globals, environment } = data.variables\n if (local) {\n variablesStore.setLocalVariables(toVariableEntries(local))\n }\n if (collection) {\n variablesStore.setCollectionVariables?.(toVariableEntries(collection))\n }\n if (globals) {\n variablesStore.setGlobals?.(toVariableEntries(globals))\n }\n if (environment) {\n variablesStore.setEnvironment?.(toVariableEntries(environment))\n }\n }\n\n if (!data.error && type === 'pre-request' && requestBuilder && data.request !== undefined) {\n syncPlainPostmanRequestToRequestFactory(data.request, requestBuilder)\n }\n\n resolve()\n }\n }\n\n transport(request, onMessage)\n })\n\n return variablesStore\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sandbox-adapter.js","names":[],"sources":["../../../../src/libs/execute-scripts/postman-adapter/sandbox-adapter.ts"],"sourcesContent":["import { normalizeError } from '@scalar/helpers/errors/normalize-error'\nimport { isElectron } from '@scalar/helpers/general/is-electron'\nimport type { RequestFactory, VariablesStore } from '@scalar/workspace-store/request-example'\n\nimport type { ConsoleContext } from '../context/console'\nimport type { TestResult } from '../execute-post-response-script'\nimport { getVariableScopesFromStore, toVariableEntries } from '../variables-store'\nimport { createPostmanRequestFromFactory, syncPlainPostmanRequestToRequestFactory } from './request-factory-adapter'\nimport { getSandboxOrigins } from './sandbox-origins'\nimport {\n type PostmanResponseDefinition,\n SANDBOX_CHANNEL,\n type SandboxExecuteRequest,\n type SandboxOutboundMessage,\n} from './sandbox-protocol'\n\n/**\n * Host-side adapter for pre/post-request scripts.\n *\n * The actual script execution happens in `postman-sandbox`, which requires `eval`. To keep\n * `'unsafe-eval'` out of the main application CSP, that execution is delegated to an iframe loaded\n * from a real same-origin `.html` file with its own permissive CSP (see `sandbox-frame-server.ts`).\n * This module therefore must NOT import `postman-sandbox`; it only serializes inputs, talks to the\n * iframe over `postMessage`, and applies the results back to the live store/request.\n */\nexport const toPostmanResponse = async (response: Response): Promise<PostmanResponseDefinition> => {\n // Read as ArrayBuffer (not text) so binary payloads — gzipped JSON, images, PDFs, protobuf —\n // survive the trip through `postMessage` byte-for-byte. Round-tripping through `response.text()`\n // + `TextEncoder` corrupts any sequence that is not valid UTF-8 because the decoder replaces\n // invalid units with U+FFFD before we ever encode them back to bytes.\n const buffer = await response.arrayBuffer()\n const responseBytes = Array.from(new Uint8Array(buffer))\n\n return {\n code: response.status,\n status: response.statusText || String(response.status),\n header: Array.from(response.headers.entries()).map(([key, value]) => ({ key, value })),\n stream: {\n type: 'Buffer',\n data: responseBytes,\n },\n }\n}\n\n/**\n * Resolves the sandbox iframe URL.\n *\n * On the web we anchor to the origin root because the SPA router owns the current path: a\n * relative `sandbox.html` would resolve under the active route and get rewritten to `index.html`\n * by the history fallback. In Electron the document path is literal, so a sibling resolution\n * works — and we use the `isElectron` helper rather than a `file://` check because Electron's\n * dev mode is served over `http://localhost`.\n *\n * Exported for tests; production code reaches it through `ensureSandboxFrame`.\n */\nexport const sandboxFrameUrl = (): string => {\n if (isElectron()) {\n return new URL('sandbox.html', document.baseURI).href\n }\n return new URL('/sandbox.html', window.location.origin).href\n}\n\n/**\n * Delivers a single execute request to the sandbox and forwards every message it produces (test\n * results, console output, and the final `done`) to `onMessage`.\n *\n * The default transport is the iframe (see {@link iframeTransport}). It is injectable so that node\n * unit tests can run the real execution in-process (`runSandboxExecution`) without a DOM, and without\n * the production host bundle ever importing `postman-sandbox`.\n */\ntype SandboxTransport = (request: SandboxExecuteRequest, onMessage: (message: SandboxOutboundMessage) => void) => void\n\ntype SandboxFrame = {\n element: HTMLIFrameElement\n id: number\n url: string\n window: Window\n}\n\nlet framePromise: Promise<SandboxFrame> | undefined\nlet activeFrameId: number | undefined\nlet nextFrameId = 0\n\n/**\n * Maximum time we wait for a freshly created sandbox iframe to post its `ready` handshake.\n *\n * The iframe `error` event fires only when the `.html` document itself fails to load. If the\n * document loads (HTTP 200) but the script inside never reaches `startSandboxFrameServer` — a\n * 404 on the bundle, an import error, a CSP block, a crash during boot — no `ready` message is\n * posted and no `error` event fires. Without this ceiling the readiness promise hangs forever;\n * because it is module-cached, every later call awaits the same dead promise and all script\n * execution freezes for the session. The window is generous: readiness only requires loading one\n * same-origin HTML file plus the sandbox bundle, so timing out means the bundle is genuinely\n * broken rather than slow.\n */\nconst SANDBOX_READY_TIMEOUT_MS = 30 * 1000\n\nconst clearActiveFrame = (id: number): void => {\n if (activeFrameId === id) {\n framePromise = undefined\n activeFrameId = undefined\n }\n}\n\nconst invalidateSandboxFrame = ({ element, id }: SandboxFrame): void => {\n clearActiveFrame(id)\n element.remove()\n}\n\nconst assertSandboxFrameLocation = (sandboxFrame: SandboxFrame): void => {\n const { element, url, window: frameWindow } = sandboxFrame\n if (!element.isConnected || !document.body.contains(element)) {\n invalidateSandboxFrame(sandboxFrame)\n throw new Error('Sandbox iframe was detached before script execution')\n }\n\n if (element.contentWindow !== frameWindow) {\n invalidateSandboxFrame(sandboxFrame)\n throw new Error('Sandbox iframe window changed before script execution')\n }\n\n let currentUrl: string\n\n try {\n currentUrl = frameWindow.location.href\n } catch (error) {\n invalidateSandboxFrame(sandboxFrame)\n const message = normalizeError(error).message\n throw new Error(`Could not verify sandbox iframe location before script execution: ${message}`)\n }\n\n if (currentUrl !== url) {\n invalidateSandboxFrame(sandboxFrame)\n throw new Error('Sandbox iframe navigated before script execution')\n }\n}\n\n/** Lazily creates a single hidden sandbox iframe and resolves once it reports readiness. */\nconst ensureSandboxFrame = (): Promise<SandboxFrame> => {\n if (framePromise) {\n return framePromise\n }\n\n framePromise = new Promise<SandboxFrame>((resolve, reject) => {\n const frameId = nextFrameId++\n activeFrameId = frameId\n const iframe = document.createElement('iframe')\n iframe.setAttribute('aria-hidden', 'true')\n iframe.style.display = 'none'\n const iframeUrl = sandboxFrameUrl()\n iframe.src = iframeUrl\n\n const { receive: expectedOrigin } = getSandboxOrigins()\n\n // Cleanup shared by the success, load-error, and readiness-timeout paths: cancel the timer and\n // drop the window listener so exactly one of the three settles the promise without leaking.\n let readyTimeoutId: ReturnType<typeof setTimeout>\n const stopWaiting = () => {\n clearTimeout(readyTimeoutId)\n window.removeEventListener('message', onReady)\n }\n\n const onReady = (event: MessageEvent) => {\n // Same-origin iframe: reject anything that does not come from our own origin and our own frame.\n if (event.origin !== expectedOrigin || event.source !== iframe.contentWindow) {\n return\n }\n const data = event.data as SandboxOutboundMessage | null\n if (!data || data.channel !== SANDBOX_CHANNEL || data.kind !== 'ready') {\n return\n }\n stopWaiting()\n if (!iframe.contentWindow) {\n // Tear the orphaned node down so a retry can append a fresh frame without piling up.\n iframe.remove()\n clearActiveFrame(frameId)\n reject(new Error('Sandbox iframe has no content window'))\n return\n }\n resolve({\n element: iframe,\n id: frameId,\n url: iframeUrl,\n window: iframe.contentWindow,\n })\n }\n\n iframe.addEventListener('error', () => {\n stopWaiting()\n // Drop the broken iframe from the DOM. Without this, a transient failure (network blip, bad\n // bundle path, etc.) leaves a dead `<iframe>` attached, and every retry appends another one,\n // growing the DOM unboundedly across the session.\n iframe.remove()\n clearActiveFrame(frameId)\n reject(new Error('Failed to load the script sandbox iframe'))\n })\n\n // Catch the silent failure mode the `error` event cannot see: the document loaded but its\n // script never announced readiness. Drop the dead frame and clear the cache so the next call\n // retries with a fresh iframe instead of awaiting this one forever.\n readyTimeoutId = setTimeout(() => {\n window.removeEventListener('message', onReady)\n iframe.remove()\n clearActiveFrame(frameId)\n reject(new Error(`Sandbox iframe did not report readiness within ${SANDBOX_READY_TIMEOUT_MS}ms`))\n }, SANDBOX_READY_TIMEOUT_MS)\n\n window.addEventListener('message', onReady)\n document.body.appendChild(iframe)\n })\n\n return framePromise\n}\n\n/**\n * Maximum time we wait for the iframe to emit `kind: 'done'` for a given execute request.\n *\n * postman-sandbox enforces its own per-script timeout inside the sandbox, so reaching this ceiling\n * means the iframe itself wedged (renderer OOM, hard crash, blocking infinite loop in a worker,\n * messages dropped). The default is generous on purpose — this is a circuit-breaker for crashes,\n * not a budget for user scripts.\n */\nconst SANDBOX_EXECUTION_TIMEOUT_MS = 5 * 60 * 1000\n\nconst iframeTransport: SandboxTransport = (request, onMessage) => {\n ensureSandboxFrame()\n .then((sandboxFrame) => {\n assertSandboxFrameLocation(sandboxFrame)\n\n const frame = sandboxFrame.window\n const { receive: expectedOrigin, send: targetOrigin } = getSandboxOrigins()\n\n // Defensive circuit-breaker: if the iframe dies mid-execution we would otherwise wait on a\n // `done` message that will never arrive, leaking the listener and leaving the caller's\n // promise unresolved. The timer is cancelled the moment `done` is observed below.\n const timeoutId = setTimeout(() => {\n window.removeEventListener('message', handleMessage)\n onMessage({\n channel: SANDBOX_CHANNEL,\n kind: 'done',\n id: request.id,\n error: `Script execution timed out after ${SANDBOX_EXECUTION_TIMEOUT_MS}ms`,\n })\n }, SANDBOX_EXECUTION_TIMEOUT_MS)\n\n const handleMessage = (event: MessageEvent) => {\n // Reject anything that is not from our own origin and our own frame. Without this guard,\n // any window in the same browsing context group could send fabricated `done`/`test-results`\n // messages and trick the host into applying attacker-controlled variables and requests.\n if (event.origin !== expectedOrigin || event.source !== frame) {\n return\n }\n const data = event.data as SandboxOutboundMessage | null\n if (!data || data.channel !== SANDBOX_CHANNEL || !('id' in data) || data.id !== request.id) {\n return\n }\n if (data.kind === 'done') {\n clearTimeout(timeoutId)\n window.removeEventListener('message', handleMessage)\n }\n onMessage(data)\n }\n\n window.addEventListener('message', handleMessage)\n // Pin the target origin for web origins. `file://` is the only exception: Chromium does not\n // accept it as a target origin, so Electron file builds use `'*'` and rely on the source-window\n // checks above for receive-side isolation.\n frame.postMessage(request, targetOrigin)\n })\n .catch((error: unknown) => {\n onMessage({\n channel: SANDBOX_CHANNEL,\n kind: 'done',\n id: request.id,\n error: error instanceof Error ? error.message : String(error),\n })\n })\n}\nlet transport: SandboxTransport = iframeTransport\n\n/** Override how the host reaches the sandbox (e.g. an in-process transport for tests). */\nexport const setSandboxTransport = (next: SandboxTransport): void => {\n transport = next\n}\n\n/**\n * Eagerly creates the sandbox iframe so the first script execution does not pay the cold-start\n * cost of loading `sandbox.html` and the `postman-sandbox` bundle (noticeably slow on Electron).\n *\n * This is a best-effort optimization: it only runs in a DOM context, reuses the same module-cached\n * frame {@link executeInPostmanSandbox} later awaits, and swallows failures so a warm-up problem can\n * never surface to the caller — the real execution path retries and reports errors on its own.\n *\n * Call this only when scripts are actually present; warming the sandbox for a request without\n * scripts would defeat the lazy creation that keeps script-free requests cheap.\n */\nexport const prewarmSandboxFrame = (): void => {\n if (typeof document === 'undefined') {\n return\n }\n\n ensureSandboxFrame().catch(() => {\n // Ignored on purpose: warm-up is opportunistic and the execution path handles real errors.\n })\n}\n\nlet nextExecutionId = 0\n\nexport const executeInPostmanSandbox = async ({\n script,\n type,\n context: { requestBuilder, response, variablesStore, scriptConsole },\n onTestResultsUpdate,\n}: {\n script: string\n type: 'pre-request' | 'post-response'\n context: {\n /** Postman Collection request for `pm.request` (not the browser Fetch API Request). */\n requestBuilder?: RequestFactory\n response?: Response\n variablesStore?: VariablesStore\n scriptConsole: ConsoleContext\n }\n onTestResultsUpdate?: ((results: TestResult[]) => void) | undefined\n}): Promise<VariablesStore | undefined> => {\n // Serialize everything that crosses the iframe boundary on the host, so the iframe only deals with\n // plain, structured-cloneable data. The response body is read here (it requires the live Response).\n const requestDefinition = requestBuilder ? createPostmanRequestFromFactory(requestBuilder).toJSON() : undefined\n const responseDefinition = response ? await toPostmanResponse(response) : undefined\n const scopes = variablesStore ? getVariableScopesFromStore(variablesStore) : undefined\n\n const request: SandboxExecuteRequest = {\n channel: SANDBOX_CHANNEL,\n kind: 'execute',\n id: String(nextExecutionId++),\n listen: type === 'pre-request' ? 'prerequest' : 'test',\n script,\n scopes,\n request: requestDefinition,\n response: responseDefinition,\n }\n\n await new Promise<void>((resolve) => {\n const onMessage = (data: SandboxOutboundMessage) => {\n if (data.kind === 'test-results') {\n onTestResultsUpdate?.(data.results)\n return\n }\n\n if (data.kind === 'console') {\n const consoleMethod = scriptConsole[data.level] ?? scriptConsole.log\n ;(consoleMethod as (...params: unknown[]) => void)(...data.args)\n return\n }\n\n if (data.kind === 'done') {\n if (variablesStore && data.variables) {\n const { local, collection, globals, environment } = data.variables\n if (local) {\n variablesStore.setLocalVariables(toVariableEntries(local))\n }\n if (collection) {\n variablesStore.setCollectionVariables?.(toVariableEntries(collection))\n }\n if (globals) {\n variablesStore.setGlobals?.(toVariableEntries(globals))\n }\n if (environment) {\n variablesStore.setEnvironment?.(toVariableEntries(environment))\n }\n }\n\n if (!data.error && type === 'pre-request' && requestBuilder && data.request !== undefined) {\n syncPlainPostmanRequestToRequestFactory(data.request, requestBuilder)\n }\n\n resolve()\n }\n }\n\n transport(request, onMessage)\n })\n\n return variablesStore\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox-frame-entry.js","names":[],"sources":["../../../../src/libs/execute-scripts/postman-adapter/sandbox-frame-entry.ts"],"sourcesContent":["import { startSandboxFrameServer } from './sandbox-frame-server'\n\n/**\n * Entry point loaded by the sandbox iframe host page (`sandbox.html`).\n *\n * Importing this module pulls in `postman-sandbox`, so it must only ever be loaded inside the sandbox\n * iframe (which has its own permissive CSP) — never from the main application bundle.\n */\nstartSandboxFrameServer()\n"],"mappings":";;;;;;;;AAQA,
|
|
1
|
+
{"version":3,"file":"sandbox-frame-entry.js","names":[],"sources":["../../../../src/libs/execute-scripts/postman-adapter/sandbox-frame-entry.ts"],"sourcesContent":["import { startSandboxFrameServer } from './sandbox-frame-server'\n\n/**\n * Entry point loaded by the sandbox iframe host page (`sandbox.html`).\n *\n * Importing this module pulls in `postman-sandbox`, so it must only ever be loaded inside the sandbox\n * iframe (which has its own permissive CSP) — never from the main application bundle.\n */\nstartSandboxFrameServer()\n"],"mappings":";;;;;;;;AAQA,wBAAwB"}
|
|
@@ -184,6 +184,6 @@ var startSandboxFrameServer = () => {
|
|
|
184
184
|
return () => window.removeEventListener("message", handleMessage);
|
|
185
185
|
};
|
|
186
186
|
//#endregion
|
|
187
|
-
export { startSandboxFrameServer };
|
|
187
|
+
export { runSandboxExecution, startSandboxFrameServer };
|
|
188
188
|
|
|
189
189
|
//# sourceMappingURL=sandbox-frame-server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox-frame-server.js","names":[],"sources":["../../../../src/libs/execute-scripts/postman-adapter/sandbox-frame-server.ts"],"sourcesContent":["import { Request as PostmanRequest } from 'postman-collection'\nimport type { ExecutionResult, SandboxContext } from 'postman-sandbox'\nimport Sandbox from 'postman-sandbox'\n\nimport { buildSandboxContextFromScopes } from '../build-sandbox-context'\nimport type { ConsoleContext } from '../context/console'\nimport type { TestResult } from '../execute-post-response-script'\nimport { getSandboxOrigins } from './sandbox-origins'\nimport { SANDBOX_CHANNEL, type SandboxExecuteRequest, type SandboxOutboundMessage } from './sandbox-protocol'\n\n/**\n * Iframe-side sandbox bridge.\n *\n * This is the ONLY module that imports `postman-sandbox` (and therefore the only place that performs\n * `eval`/`new Function`). It is loaded exclusively by the sandbox `.html` host page, which carries its\n * own permissive CSP. The main application bundle never imports this file, so it stays eval-free and\n * its CSP can drop `'unsafe-eval'`.\n */\n\ntype AssertionEvent = {\n name: string\n index: number\n passed: boolean\n skipped: boolean\n error: { message?: string } | null\n}\n\nconst toErrorMessage = (error: unknown): string => {\n if (error && typeof error === 'object' && 'message' in error && typeof error.message === 'string') {\n return error.message\n }\n\n return String(error)\n}\n\nconst upsertTestResult = (testResults: TestResult[], assertion: AssertionEvent, duration: number): void => {\n const title = assertion.name || `Assertion ${assertion.index + 1}`\n\n const nextResult: TestResult = {\n title,\n passed: assertion.passed,\n duration,\n error: assertion.error?.message,\n status: assertion.passed ? 'passed' : 'failed',\n }\n\n const existingResultIndex = testResults.findIndex((result) => result.title === title)\n if (existingResultIndex === -1) {\n testResults.push(nextResult)\n return\n }\n\n testResults[existingResultIndex] = nextResult\n}\n\nconst createContext = (): Promise<SandboxContext> =>\n new Promise((resolve, reject) => {\n Sandbox.createContext((error: unknown, context: SandboxContext) => {\n if (error) {\n reject(error)\n return\n }\n\n resolve(context)\n })\n })\n\n/**\n * Runs a single script in the postman-sandbox and reports progress/results via {@link emit}.\n *\n * Inputs arrive as plain, structured-cloneable data (already converted on the host) and are rebuilt\n * into postman-collection objects here. Variable mutations and the mutated request are sent back as\n * plain values; the host applies them to the live `VariablesStore`/`RequestFactory`.\n */\nexport const runSandboxExecution = async (\n request: SandboxExecuteRequest,\n emit: (message: SandboxOutboundMessage) => void,\n): Promise<void> => {\n const { id, listen, script, scopes, request: requestDefinition, response } = request\n\n const testResults: TestResult[] = []\n let lastAssertionTime = 0\n let scriptExecutionStartedAt = 0\n\n const sandboxContext = await createContext()\n\n const handleAssertion = (_cursor: unknown, assertions: AssertionEvent[]) => {\n assertions.forEach((assertion) => {\n const duration = Number((performance.now() - lastAssertionTime).toFixed(2))\n lastAssertionTime = performance.now()\n upsertTestResult(testResults, assertion, duration)\n })\n emit({ channel: SANDBOX_CHANNEL, kind: 'test-results', id, results: [...testResults] })\n }\n\n const handleConsole = (_cursor: unknown, level: string, ...args: unknown[]) => {\n emit({ channel: SANDBOX_CHANNEL, kind: 'console', id, level: level as keyof ConsoleContext, args })\n }\n\n try {\n sandboxContext.on('execution.assertion', handleAssertion)\n sandboxContext.on('console', handleConsole)\n\n const variablesContext = scopes ? buildSandboxContextFromScopes(scopes) : undefined\n\n scriptExecutionStartedAt = performance.now()\n lastAssertionTime = scriptExecutionStartedAt\n\n /**\n * Lodash `_.has(context, 'response')` is true even when the value is `undefined`,\n * which makes the sandbox run `new Response(undefined)` and breaks `pm.request`.\n * Only set keys that are actually present.\n */\n const context: Record<string, unknown> = {\n ...(variablesContext ?? {}),\n }\n\n if (requestDefinition !== undefined) {\n context.request = new PostmanRequest(requestDefinition as ConstructorParameters<typeof PostmanRequest>[0])\n }\n if (response !== undefined) {\n context.response = response\n }\n\n await new Promise<void>((resolve) => {\n sandboxContext.execute(\n {\n listen,\n script: {\n exec: [script],\n },\n },\n {\n disableLegacyAPIs: true,\n context,\n },\n (error: unknown, execution?: ExecutionResult) => {\n const done: SandboxOutboundMessage = {\n channel: SANDBOX_CHANNEL,\n kind: 'done',\n id,\n }\n\n if (execution) {\n done.variables = {\n local: execution._variables?.values,\n collection: execution.collectionVariables?.values,\n globals: execution.globals?.values,\n environment: execution.environment?.values,\n }\n }\n\n if (!error && listen === 'prerequest' && execution?.request !== undefined) {\n done.request = execution.request\n }\n\n if (error) {\n const duration = Number((performance.now() - scriptExecutionStartedAt).toFixed(2))\n const errorMessage = toErrorMessage(error)\n done.error = errorMessage\n\n emit({\n channel: SANDBOX_CHANNEL,\n kind: 'console',\n id,\n level: 'error',\n args: [\n `[${listen === 'prerequest' ? 'PRE-REQUEST' : 'POST-RESPONSE'} Script] Error (${duration}ms):`,\n errorMessage,\n ],\n })\n\n testResults.push({\n title: 'Script Execution',\n passed: false,\n duration,\n error: errorMessage,\n status: 'failed',\n })\n emit({ channel: SANDBOX_CHANNEL, kind: 'test-results', id, results: [...testResults] })\n }\n\n emit(done)\n resolve()\n },\n )\n })\n } finally {\n sandboxContext.off('execution.assertion', handleAssertion)\n sandboxContext.off('console', handleConsole)\n sandboxContext.dispose()\n }\n}\n\n/**\n * Boots the sandbox bridge: listens for {@link SandboxExecuteRequest} messages from the host window\n * and announces readiness. Called by the sandbox `.html` entry point.\n *\n * Returns a teardown function that removes the `message` listener it registered. Production code\n * does not need to call it (the iframe lives for the lifetime of the document), but tests and any\n * future hot-reload path can use it to avoid leaking listeners across reboots.\n */\nexport const startSandboxFrameServer = (): (() => void) => {\n /**\n * Origin we pin `postMessage` exchanges to.\n *\n * The iframe is loaded same-origin with its host (see `sandbox.html`), so both the parent we\n * post to and the parent we accept from are pinned to `window.location.origin`. This prevents\n * a hostile cross-origin opener from feeding fake execute requests into the sandbox, and stops\n * sandbox output (which can include user secrets returned via `pm.environment`) from leaking\n * to a different origin if the parent is ever swapped out.\n *\n * For `file://` documents (Electron), Chromium reports message origins as `'null'` and does not\n * allow `'file://'` as a `postMessage` target origin. In that case we target `'*'` and rely on the\n * exact parent/source-window checks on both sides for isolation.\n */\n const { receive: expectedOrigin, send: targetOrigin } = getSandboxOrigins()\n\n const post = (message: SandboxOutboundMessage) => {\n window.parent?.postMessage(message, targetOrigin)\n }\n\n const handleMessage = (event: MessageEvent) => {\n // Reject anything that is not from our parent on our own origin. Without this guard, any\n // window that obtains a reference to this iframe could send fabricated execute payloads and\n // run arbitrary scripts inside the eval-permitted realm.\n if (event.origin !== expectedOrigin || event.source !== window.parent) {\n return\n }\n\n const data = event.data as Partial<SandboxExecuteRequest> | null\n if (!data || data.channel !== SANDBOX_CHANNEL || data.kind !== 'execute') {\n return\n }\n\n void runSandboxExecution(data as SandboxExecuteRequest, post).catch((error: unknown) => {\n post({\n channel: SANDBOX_CHANNEL,\n kind: 'done',\n id: (data as SandboxExecuteRequest).id,\n error: toErrorMessage(error),\n })\n })\n }\n\n window.addEventListener('message', handleMessage)\n post({ channel: SANDBOX_CHANNEL, kind: 'ready' })\n\n return () => window.removeEventListener('message', handleMessage)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sandbox-frame-server.js","names":[],"sources":["../../../../src/libs/execute-scripts/postman-adapter/sandbox-frame-server.ts"],"sourcesContent":["import { Request as PostmanRequest } from 'postman-collection'\nimport type { ExecutionResult, SandboxContext } from 'postman-sandbox'\nimport Sandbox from 'postman-sandbox'\n\nimport { buildSandboxContextFromScopes } from '../build-sandbox-context'\nimport type { ConsoleContext } from '../context/console'\nimport type { TestResult } from '../execute-post-response-script'\nimport { getSandboxOrigins } from './sandbox-origins'\nimport { SANDBOX_CHANNEL, type SandboxExecuteRequest, type SandboxOutboundMessage } from './sandbox-protocol'\n\n/**\n * Iframe-side sandbox bridge.\n *\n * This is the ONLY module that imports `postman-sandbox` (and therefore the only place that performs\n * `eval`/`new Function`). It is loaded exclusively by the sandbox `.html` host page, which carries its\n * own permissive CSP. The main application bundle never imports this file, so it stays eval-free and\n * its CSP can drop `'unsafe-eval'`.\n */\n\ntype AssertionEvent = {\n name: string\n index: number\n passed: boolean\n skipped: boolean\n error: { message?: string } | null\n}\n\nconst toErrorMessage = (error: unknown): string => {\n if (error && typeof error === 'object' && 'message' in error && typeof error.message === 'string') {\n return error.message\n }\n\n return String(error)\n}\n\nconst upsertTestResult = (testResults: TestResult[], assertion: AssertionEvent, duration: number): void => {\n const title = assertion.name || `Assertion ${assertion.index + 1}`\n\n const nextResult: TestResult = {\n title,\n passed: assertion.passed,\n duration,\n error: assertion.error?.message,\n status: assertion.passed ? 'passed' : 'failed',\n }\n\n const existingResultIndex = testResults.findIndex((result) => result.title === title)\n if (existingResultIndex === -1) {\n testResults.push(nextResult)\n return\n }\n\n testResults[existingResultIndex] = nextResult\n}\n\nconst createContext = (): Promise<SandboxContext> =>\n new Promise((resolve, reject) => {\n Sandbox.createContext((error: unknown, context: SandboxContext) => {\n if (error) {\n reject(error)\n return\n }\n\n resolve(context)\n })\n })\n\n/**\n * Runs a single script in the postman-sandbox and reports progress/results via {@link emit}.\n *\n * Inputs arrive as plain, structured-cloneable data (already converted on the host) and are rebuilt\n * into postman-collection objects here. Variable mutations and the mutated request are sent back as\n * plain values; the host applies them to the live `VariablesStore`/`RequestFactory`.\n */\nexport const runSandboxExecution = async (\n request: SandboxExecuteRequest,\n emit: (message: SandboxOutboundMessage) => void,\n): Promise<void> => {\n const { id, listen, script, scopes, request: requestDefinition, response } = request\n\n const testResults: TestResult[] = []\n let lastAssertionTime = 0\n let scriptExecutionStartedAt = 0\n\n const sandboxContext = await createContext()\n\n const handleAssertion = (_cursor: unknown, assertions: AssertionEvent[]) => {\n assertions.forEach((assertion) => {\n const duration = Number((performance.now() - lastAssertionTime).toFixed(2))\n lastAssertionTime = performance.now()\n upsertTestResult(testResults, assertion, duration)\n })\n emit({ channel: SANDBOX_CHANNEL, kind: 'test-results', id, results: [...testResults] })\n }\n\n const handleConsole = (_cursor: unknown, level: string, ...args: unknown[]) => {\n emit({ channel: SANDBOX_CHANNEL, kind: 'console', id, level: level as keyof ConsoleContext, args })\n }\n\n try {\n sandboxContext.on('execution.assertion', handleAssertion)\n sandboxContext.on('console', handleConsole)\n\n const variablesContext = scopes ? buildSandboxContextFromScopes(scopes) : undefined\n\n scriptExecutionStartedAt = performance.now()\n lastAssertionTime = scriptExecutionStartedAt\n\n /**\n * Lodash `_.has(context, 'response')` is true even when the value is `undefined`,\n * which makes the sandbox run `new Response(undefined)` and breaks `pm.request`.\n * Only set keys that are actually present.\n */\n const context: Record<string, unknown> = {\n ...(variablesContext ?? {}),\n }\n\n if (requestDefinition !== undefined) {\n context.request = new PostmanRequest(requestDefinition as ConstructorParameters<typeof PostmanRequest>[0])\n }\n if (response !== undefined) {\n context.response = response\n }\n\n await new Promise<void>((resolve) => {\n sandboxContext.execute(\n {\n listen,\n script: {\n exec: [script],\n },\n },\n {\n disableLegacyAPIs: true,\n context,\n },\n (error: unknown, execution?: ExecutionResult) => {\n const done: SandboxOutboundMessage = {\n channel: SANDBOX_CHANNEL,\n kind: 'done',\n id,\n }\n\n if (execution) {\n done.variables = {\n local: execution._variables?.values,\n collection: execution.collectionVariables?.values,\n globals: execution.globals?.values,\n environment: execution.environment?.values,\n }\n }\n\n if (!error && listen === 'prerequest' && execution?.request !== undefined) {\n done.request = execution.request\n }\n\n if (error) {\n const duration = Number((performance.now() - scriptExecutionStartedAt).toFixed(2))\n const errorMessage = toErrorMessage(error)\n done.error = errorMessage\n\n emit({\n channel: SANDBOX_CHANNEL,\n kind: 'console',\n id,\n level: 'error',\n args: [\n `[${listen === 'prerequest' ? 'PRE-REQUEST' : 'POST-RESPONSE'} Script] Error (${duration}ms):`,\n errorMessage,\n ],\n })\n\n testResults.push({\n title: 'Script Execution',\n passed: false,\n duration,\n error: errorMessage,\n status: 'failed',\n })\n emit({ channel: SANDBOX_CHANNEL, kind: 'test-results', id, results: [...testResults] })\n }\n\n emit(done)\n resolve()\n },\n )\n })\n } finally {\n sandboxContext.off('execution.assertion', handleAssertion)\n sandboxContext.off('console', handleConsole)\n sandboxContext.dispose()\n }\n}\n\n/**\n * Boots the sandbox bridge: listens for {@link SandboxExecuteRequest} messages from the host window\n * and announces readiness. Called by the sandbox `.html` entry point.\n *\n * Returns a teardown function that removes the `message` listener it registered. Production code\n * does not need to call it (the iframe lives for the lifetime of the document), but tests and any\n * future hot-reload path can use it to avoid leaking listeners across reboots.\n */\nexport const startSandboxFrameServer = (): (() => void) => {\n /**\n * Origin we pin `postMessage` exchanges to.\n *\n * The iframe is loaded same-origin with its host (see `sandbox.html`), so both the parent we\n * post to and the parent we accept from are pinned to `window.location.origin`. This prevents\n * a hostile cross-origin opener from feeding fake execute requests into the sandbox, and stops\n * sandbox output (which can include user secrets returned via `pm.environment`) from leaking\n * to a different origin if the parent is ever swapped out.\n *\n * For `file://` documents (Electron), Chromium reports message origins as `'null'` and does not\n * allow `'file://'` as a `postMessage` target origin. In that case we target `'*'` and rely on the\n * exact parent/source-window checks on both sides for isolation.\n */\n const { receive: expectedOrigin, send: targetOrigin } = getSandboxOrigins()\n\n const post = (message: SandboxOutboundMessage) => {\n window.parent?.postMessage(message, targetOrigin)\n }\n\n const handleMessage = (event: MessageEvent) => {\n // Reject anything that is not from our parent on our own origin. Without this guard, any\n // window that obtains a reference to this iframe could send fabricated execute payloads and\n // run arbitrary scripts inside the eval-permitted realm.\n if (event.origin !== expectedOrigin || event.source !== window.parent) {\n return\n }\n\n const data = event.data as Partial<SandboxExecuteRequest> | null\n if (!data || data.channel !== SANDBOX_CHANNEL || data.kind !== 'execute') {\n return\n }\n\n void runSandboxExecution(data as SandboxExecuteRequest, post).catch((error: unknown) => {\n post({\n channel: SANDBOX_CHANNEL,\n kind: 'done',\n id: (data as SandboxExecuteRequest).id,\n error: toErrorMessage(error),\n })\n })\n }\n\n window.addEventListener('message', handleMessage)\n post({ channel: SANDBOX_CHANNEL, kind: 'ready' })\n\n return () => window.removeEventListener('message', handleMessage)\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox-origins.js","names":[],"sources":["../../../../src/libs/execute-scripts/postman-adapter/sandbox-origins.ts"],"sourcesContent":["type SandboxOriginConfig = {\n receive: string\n send: string\n}\n\n/**\n * Origin pair used for sandbox `postMessage` exchanges.\n *\n * Chromium reports `file://` message origins as `'null'` and rejects `file://` as a target origin,\n * so packaged Electron builds must receive from `'null'` and send with `'*'`. Other schemes stay\n * pinned to their exact origin to avoid leaking script payloads or accepting spoofed messages.\n */\nexport const getSandboxOrigins = (): SandboxOriginConfig => {\n const { origin, protocol } = window.location\n const isFileProtocol = protocol === 'file:' || origin === 'file://'\n\n if (isFileProtocol) {\n return {\n receive: 'null',\n send: '*',\n }\n }\n\n return {\n receive: origin,\n send: origin,\n }\n}\n"],"mappings":";;;;;;;;AAYA,IAAa,0BAA+C;CAC1D,MAAM,EAAE,QAAQ,aAAa,OAAO;
|
|
1
|
+
{"version":3,"file":"sandbox-origins.js","names":[],"sources":["../../../../src/libs/execute-scripts/postman-adapter/sandbox-origins.ts"],"sourcesContent":["type SandboxOriginConfig = {\n receive: string\n send: string\n}\n\n/**\n * Origin pair used for sandbox `postMessage` exchanges.\n *\n * Chromium reports `file://` message origins as `'null'` and rejects `file://` as a target origin,\n * so packaged Electron builds must receive from `'null'` and send with `'*'`. Other schemes stay\n * pinned to their exact origin to avoid leaking script payloads or accepting spoofed messages.\n */\nexport const getSandboxOrigins = (): SandboxOriginConfig => {\n const { origin, protocol } = window.location\n const isFileProtocol = protocol === 'file:' || origin === 'file://'\n\n if (isFileProtocol) {\n return {\n receive: 'null',\n send: '*',\n }\n }\n\n return {\n receive: origin,\n send: origin,\n }\n}\n"],"mappings":";;;;;;;;AAYA,IAAa,0BAA+C;CAC1D,MAAM,EAAE,QAAQ,aAAa,OAAO;CAGpC,IAFuB,aAAa,WAAW,WAAW,WAGxD,OAAO;EACL,SAAS;EACT,MAAM;CACR;CAGF,OAAO;EACL,SAAS;EACT,MAAM;CACR;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"variables-store.js","names":[],"sources":["../../../src/libs/execute-scripts/variables-store.ts"],"sourcesContent":["import type { VariableEntry, VariablesStore } from '@scalar/workspace-store/request-example'\n\nfunction toKeyValueArray(input: VariableEntry[] | Record<string, string>): VariableEntry[] {\n if (Array.isArray(input)) {\n return input\n }\n return Object.entries(input).map(([key, value]) => ({ key, value }))\n}\n\n/**\n * Converts a VariablesStore into key-value arrays per scope for building\n * the Postman sandbox context (e.g. with postman-collection VariableScope).\n */\nexport function getVariableScopesFromStore(store: VariablesStore): {\n environment: VariableEntry[]\n globals: VariableEntry[]\n collectionVariables: VariableEntry[]\n data: Record<string, string>\n local: VariableEntry[]\n} {\n return {\n environment: toKeyValueArray(store.getEnvironment()),\n globals: toKeyValueArray(store.getGlobals()),\n collectionVariables: toKeyValueArray(store.getCollectionVariables()),\n data: store.getData(),\n local: toKeyValueArray(store.getLocalVariables()),\n }\n}\n\n/**\n * Parses the sandbox execution result’s _variables (array of { key, value, type })\n * into a key-value record and calls store.setLocalVariables.\n */\nexport const toVariableEntries = (\n values: Array<{ key: string; value: string; type?: string }> | undefined,\n): VariableEntry[] => {\n if (!values?.length) {\n return []\n }\n return values.map((v) => ({ key: v.key, value: v.value }))\n}\n"],"mappings":";AAEA,SAAS,gBAAgB,OAAkE;
|
|
1
|
+
{"version":3,"file":"variables-store.js","names":[],"sources":["../../../src/libs/execute-scripts/variables-store.ts"],"sourcesContent":["import type { VariableEntry, VariablesStore } from '@scalar/workspace-store/request-example'\n\nfunction toKeyValueArray(input: VariableEntry[] | Record<string, string>): VariableEntry[] {\n if (Array.isArray(input)) {\n return input\n }\n return Object.entries(input).map(([key, value]) => ({ key, value }))\n}\n\n/**\n * Converts a VariablesStore into key-value arrays per scope for building\n * the Postman sandbox context (e.g. with postman-collection VariableScope).\n */\nexport function getVariableScopesFromStore(store: VariablesStore): {\n environment: VariableEntry[]\n globals: VariableEntry[]\n collectionVariables: VariableEntry[]\n data: Record<string, string>\n local: VariableEntry[]\n} {\n return {\n environment: toKeyValueArray(store.getEnvironment()),\n globals: toKeyValueArray(store.getGlobals()),\n collectionVariables: toKeyValueArray(store.getCollectionVariables()),\n data: store.getData(),\n local: toKeyValueArray(store.getLocalVariables()),\n }\n}\n\n/**\n * Parses the sandbox execution result’s _variables (array of { key, value, type })\n * into a key-value record and calls store.setLocalVariables.\n */\nexport const toVariableEntries = (\n values: Array<{ key: string; value: string; type?: string }> | undefined,\n): VariableEntry[] => {\n if (!values?.length) {\n return []\n }\n return values.map((v) => ({ key: v.key, value: v.value }))\n}\n"],"mappings":";AAEA,SAAS,gBAAgB,OAAkE;CACzF,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO;CAET,OAAO,OAAO,QAAQ,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,YAAY;EAAE;EAAK;CAAM,EAAE;AACrE;;;;;AAMA,SAAgB,2BAA2B,OAMzC;CACA,OAAO;EACL,aAAa,gBAAgB,MAAM,eAAe,CAAC;EACnD,SAAS,gBAAgB,MAAM,WAAW,CAAC;EAC3C,qBAAqB,gBAAgB,MAAM,uBAAuB,CAAC;EACnE,MAAM,MAAM,QAAQ;EACpB,OAAO,gBAAgB,MAAM,kBAAkB,CAAC;CAClD;AACF;;;;;AAMA,IAAa,qBACX,WACoB;CACpB,IAAI,CAAC,QAAQ,QACX,OAAO,CAAC;CAEV,OAAO,OAAO,KAAK,OAAO;EAAE,KAAK,EAAE;EAAK,OAAO,EAAE;CAAM,EAAE;AAC3D"}
|
|
@@ -3,7 +3,7 @@ import { Fragment, createElementBlock, createElementVNode, defineComponent, open
|
|
|
3
3
|
//#region src/plugins/request-scripts/components/ExampleScripts.vue?vue&type=script&setup=true&lang.ts
|
|
4
4
|
var _hoisted_1 = { class: "flex flex-wrap gap-1" };
|
|
5
5
|
var _hoisted_2 = ["onClick"];
|
|
6
|
-
var ExampleScripts_vue_vue_type_script_setup_true_lang_default =
|
|
6
|
+
var ExampleScripts_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
7
7
|
__name: "ExampleScripts",
|
|
8
8
|
props: { modelValue: {} },
|
|
9
9
|
emits: ["update:modelValue"],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExampleScripts.vue.script.js","names":[],"sources":["../../../../src/plugins/request-scripts/components/ExampleScripts.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { EXAMPLE_SCRIPTS } from '@/consts/example-scripts'\n\nconst props = defineProps<{\n modelValue: string\n}>()\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\nconst addExample = (example: string) => {\n let addition = example\n // Look at current value. If it's not empty, add a new line.\n if (props.modelValue.trim() !== '') {\n addition = `\\n\\n${addition}`\n }\n\n emit('update:modelValue', props.modelValue.trim() + addition)\n}\n</script>\n\n<template>\n <ul class=\"flex flex-wrap gap-1\">\n <li\n v-for=\"example in EXAMPLE_SCRIPTS\"\n :key=\"example.title\"\n class=\"bg-b-2 text-c-2 hover:bg-b-3 inline-flex flex-col rounded-full px-3 py-1 text-xs font-medium\">\n <button\n class=\"w-full\"\n type=\"button\"\n @click=\"addExample(example.script)\">\n {{ example.title }}\n </button>\n </li>\n </ul>\n</template>\n"],"mappings":";;;;;;;;;;EAGA,MAAM,QAAQ;EAId,MAAM,OAAO;EAIb,MAAM,cAAc,YAAoB;GACtC,IAAI,WAAW;
|
|
1
|
+
{"version":3,"file":"ExampleScripts.vue.script.js","names":[],"sources":["../../../../src/plugins/request-scripts/components/ExampleScripts.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { EXAMPLE_SCRIPTS } from '@/consts/example-scripts'\n\nconst props = defineProps<{\n modelValue: string\n}>()\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\nconst addExample = (example: string) => {\n let addition = example\n // Look at current value. If it's not empty, add a new line.\n if (props.modelValue.trim() !== '') {\n addition = `\\n\\n${addition}`\n }\n\n emit('update:modelValue', props.modelValue.trim() + addition)\n}\n</script>\n\n<template>\n <ul class=\"flex flex-wrap gap-1\">\n <li\n v-for=\"example in EXAMPLE_SCRIPTS\"\n :key=\"example.title\"\n class=\"bg-b-2 text-c-2 hover:bg-b-3 inline-flex flex-col rounded-full px-3 py-1 text-xs font-medium\">\n <button\n class=\"w-full\"\n type=\"button\"\n @click=\"addExample(example.script)\">\n {{ example.title }}\n </button>\n </li>\n </ul>\n</template>\n"],"mappings":";;;;;;;;;;EAGA,MAAM,QAAQ;EAId,MAAM,OAAO;EAIb,MAAM,cAAc,YAAoB;GACtC,IAAI,WAAW;GAEf,IAAI,MAAM,WAAW,KAAK,MAAM,IAC9B,WAAW,OAAO;GAGpB,KAAK,qBAAqB,MAAM,WAAW,KAAK,IAAI,QAAQ;EAC9D;;uBAIE,mBAYK,MAZL,YAYK,EAAA,UAAA,IAAA,GAXH,mBAUK,UAAA,MAAA,WATe,MAAA,eAAA,IAAX,YAAO;wBADhB,mBAUK,MAAA;KARF,KAAK,QAAQ;KACd,OAAM;QACN,mBAKS,UAAA;KAJP,OAAM;KACN,MAAK;KACJ,UAAK,WAAE,WAAW,QAAQ,MAAM;uBAC9B,QAAQ,KAAK,GAAA,GAAA,UAAA,CAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScriptsSection.vue.d.ts","sourceRoot":"","sources":["../../../../src/plugins/request-scripts/components/ScriptsSection.vue"],"names":[],"mappings":"AAiFA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8DAA8D,CAAA;AAOnG,KAAK,WAAW,GAAG;IACjB,SAAS,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,eAAe,GAAG,iBAAiB,CAAC,CAAA;CACvE,CAAC;
|
|
1
|
+
{"version":3,"file":"ScriptsSection.vue.d.ts","sourceRoot":"","sources":["../../../../src/plugins/request-scripts/components/ScriptsSection.vue"],"names":[],"mappings":"AAiFA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8DAA8D,CAAA;AAOnG,KAAK,WAAW,GAAG;IACjB,SAAS,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,eAAe,GAAG,iBAAiB,CAAC,CAAA;CACvE,CAAC;AAgLF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -10,7 +10,7 @@ var _hoisted_3 = {
|
|
|
10
10
|
class: "bg-green h-2 w-2 rounded-full"
|
|
11
11
|
};
|
|
12
12
|
var _hoisted_4 = { class: "border-y p-3" };
|
|
13
|
-
var ScriptsSection_vue_vue_type_script_setup_true_lang_default =
|
|
13
|
+
var ScriptsSection_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
14
14
|
__name: "ScriptsSection",
|
|
15
15
|
props: { operation: {} },
|
|
16
16
|
emits: ["operation:update:extension"],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScriptsSection.vue.script.js","names":[],"sources":["../../../../src/plugins/request-scripts/components/ScriptsSection.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { OperationObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed } from 'vue'\n\nimport ScriptEditor from '@/components/ScriptEditor.vue'\nimport { ViewLayoutCollapse } from '@/components/ViewLayout'\nimport ExampleScripts from '@/plugins/request-scripts/components/ExampleScripts.vue'\n\nconst { operation } = defineProps<{\n operation?: Pick<OperationObject, 'x-pre-request' | 'x-post-response'>\n}>()\n\nconst emit = defineEmits<{\n (e: 'operation:update:extension', payload: any): void\n}>()\n\nconst preRequestScript = computed(\n () => (operation?.['x-pre-request'] as string) ?? '',\n)\nconst postResponseScript = computed(\n () => (operation?.['x-post-response'] as string) ?? '',\n)\n\nconst hasAnyScript = computed(\n () =>\n preRequestScript.value.length > 0 || postResponseScript.value.length > 0,\n)\n\nconst updatePreRequestScript = (value: string) => {\n emit('operation:update:extension', { 'x-pre-request': value })\n}\nconst updatePostResponseScript = (value: string) => {\n emit('operation:update:extension', { 'x-post-response': value })\n}\n</script>\n\n<template>\n <div class=\"w-full\">\n <ViewLayoutCollapse\n class=\"w-full\"\n :defaultOpen=\"true\">\n <template #title>Scripts</template>\n <template #suffix>\n <!-- Show an indicator whether we have a script -->\n <div class=\"mr-2\">\n <div\n v-if=\"hasAnyScript\"\n class=\"bg-green h-2 w-2 rounded-full\" />\n </div>\n </template>\n\n <!-- Pre-request -->\n <div\n class=\"text-c-3 flex min-h-8 items-center border-y px-3 py-2 text-sm\">\n Pre-request — run before the request is sent (e.g. set variables,\n headers).\n </div>\n <ScriptEditor\n :modelValue=\"preRequestScript\"\n @update:modelValue=\"updatePreRequestScript\" />\n\n <!-- Post-response -->\n <div\n class=\"text-c-3 flex min-h-8 items-center border-y px-3 py-2 text-sm\">\n Post-response — run after the response is received (e.g. tests,\n assertions).\n </div>\n <ScriptEditor\n :modelValue=\"postResponseScript\"\n @update:modelValue=\"updatePostResponseScript\" />\n <div class=\"border-y p-3\">\n <ExampleScripts\n :modelValue=\"postResponseScript\"\n @update:modelValue=\"updatePostResponseScript\" />\n </div>\n </ViewLayoutCollapse>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;EAYA,MAAM,OAAO;EAIb,MAAM,mBAAmB,eAChB,QAAA,YAAY,oBAA+B,
|
|
1
|
+
{"version":3,"file":"ScriptsSection.vue.script.js","names":[],"sources":["../../../../src/plugins/request-scripts/components/ScriptsSection.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { OperationObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed } from 'vue'\n\nimport ScriptEditor from '@/components/ScriptEditor.vue'\nimport { ViewLayoutCollapse } from '@/components/ViewLayout'\nimport ExampleScripts from '@/plugins/request-scripts/components/ExampleScripts.vue'\n\nconst { operation } = defineProps<{\n operation?: Pick<OperationObject, 'x-pre-request' | 'x-post-response'>\n}>()\n\nconst emit = defineEmits<{\n (e: 'operation:update:extension', payload: any): void\n}>()\n\nconst preRequestScript = computed(\n () => (operation?.['x-pre-request'] as string) ?? '',\n)\nconst postResponseScript = computed(\n () => (operation?.['x-post-response'] as string) ?? '',\n)\n\nconst hasAnyScript = computed(\n () =>\n preRequestScript.value.length > 0 || postResponseScript.value.length > 0,\n)\n\nconst updatePreRequestScript = (value: string) => {\n emit('operation:update:extension', { 'x-pre-request': value })\n}\nconst updatePostResponseScript = (value: string) => {\n emit('operation:update:extension', { 'x-post-response': value })\n}\n</script>\n\n<template>\n <div class=\"w-full\">\n <ViewLayoutCollapse\n class=\"w-full\"\n :defaultOpen=\"true\">\n <template #title>Scripts</template>\n <template #suffix>\n <!-- Show an indicator whether we have a script -->\n <div class=\"mr-2\">\n <div\n v-if=\"hasAnyScript\"\n class=\"bg-green h-2 w-2 rounded-full\" />\n </div>\n </template>\n\n <!-- Pre-request -->\n <div\n class=\"text-c-3 flex min-h-8 items-center border-y px-3 py-2 text-sm\">\n Pre-request — run before the request is sent (e.g. set variables,\n headers).\n </div>\n <ScriptEditor\n :modelValue=\"preRequestScript\"\n @update:modelValue=\"updatePreRequestScript\" />\n\n <!-- Post-response -->\n <div\n class=\"text-c-3 flex min-h-8 items-center border-y px-3 py-2 text-sm\">\n Post-response — run after the response is received (e.g. tests,\n assertions).\n </div>\n <ScriptEditor\n :modelValue=\"postResponseScript\"\n @update:modelValue=\"updatePostResponseScript\" />\n <div class=\"border-y p-3\">\n <ExampleScripts\n :modelValue=\"postResponseScript\"\n @update:modelValue=\"updatePostResponseScript\" />\n </div>\n </ViewLayoutCollapse>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;EAYA,MAAM,OAAO;EAIb,MAAM,mBAAmB,eAChB,QAAA,YAAY,oBAA+B,EACpD;EACA,MAAM,qBAAqB,eAClB,QAAA,YAAY,sBAAiC,EACtD;EAEA,MAAM,eAAe,eAEjB,iBAAiB,MAAM,SAAS,KAAK,mBAAmB,MAAM,SAAS,CAC3E;EAEA,MAAM,0BAA0B,UAAkB;GAChD,KAAK,8BAA8B,EAAE,iBAAiB,MAAM,CAAC;EAC/D;EACA,MAAM,4BAA4B,UAAkB;GAClD,KAAK,8BAA8B,EAAE,mBAAmB,MAAM,CAAC;EACjE;;uBAIE,mBAuCM,OAvCN,YAuCM,CAtCJ,YAqCqB,MAAA,0BAAA,GAAA;IApCnB,OAAM;IACL,aAAa;;IACH,OAAK,cAAQ,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,gBAAP,WAAO,EAAA,CAAA,EAAA,CAAA;IACb,QAAM,cAMT,CAJN,mBAIM,OAJN,YAIM,CAFI,aAAA,SAAA,UAAA,GADR,mBAE0C,OAF1C,UAE0C,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,CAAA,CAAA;2BASxC;+BAJN,mBAIM,OAAA,EAHJ,OAAM,gEAA+D,GAAC,iFAGxE,EAAA;KACA,YAEgD,sBAAA;MAD7C,YAAY,iBAAA;MACZ,uBAAmB;;+BAGtB,mBAIM,OAAA,EAHJ,OAAM,gEAA+D,GAAC,kFAGxE,EAAA;KACA,YAEkD,sBAAA;MAD/C,YAAY,mBAAA;MACZ,uBAAmB;;KACtB,mBAIM,OAJN,YAIM,CAHJ,YAEkD,wBAAA;MAD/C,YAAY,mBAAA;MACZ,uBAAmB"}
|
package/dist/plugins/request-scripts/components/TestResults/TestResultIndicator.vue.script.js
CHANGED
|
@@ -6,7 +6,7 @@ var _hoisted_1 = {
|
|
|
6
6
|
key: 1,
|
|
7
7
|
class: "ml-1.25 capitalize"
|
|
8
8
|
};
|
|
9
|
-
var TestResultIndicator_vue_vue_type_script_setup_true_lang_default =
|
|
9
|
+
var TestResultIndicator_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
10
10
|
__name: "TestResultIndicator",
|
|
11
11
|
props: {
|
|
12
12
|
state: {},
|
package/dist/plugins/request-scripts/components/TestResults/TestResultIndicator.vue.script.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestResultIndicator.vue.script.js","names":[],"sources":["../../../../../src/plugins/request-scripts/components/TestResults/TestResultIndicator.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarIcon, type Icon } from '@scalar/components/icon'\nimport { cva, useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { computed } from 'vue'\n\nimport type { TestResult } from '@/libs/execute-scripts'\n\nconst { state, totalTestsCount, pendingTestsCount, failedTestsCount } =\n defineProps<{\n state?: TestResult['status']\n failedTestsCount: number | undefined\n pendingTestsCount: number | undefined\n totalTestsCount: number | undefined\n inline?: boolean\n }>()\n\nconst { cx } = useBindCx()\n\nconst icon = computed(() => {\n if (state === 'passed') {\n return { name: 'Checkmark', color: 'text-green p-0.25' }\n }\n\n if (state === 'failed') {\n return { name: 'Close', color: 'text-red' }\n }\n\n return { name: 'Ellipses', color: 'text-c-1' }\n})\n\nconst statusVariants = cva({\n base: 'flex items-center gap-1.5 rounded-full border pr-2 pl-1.25',\n variants: {\n status: {\n passed: 'text-green',\n failed: 'text-red',\n pending: 'text-orange',\n },\n },\n})\n\nconst textVariants = cva({\n base: 'text-c-2',\n variants: {\n inline: {\n true: 'text-xs',\n false: 'text-sm',\n },\n },\n})\n\n// Display the number of tests that passed e.g 1/3\nconst getTestCountDisplay = computed(() => {\n // When there are no tests, show nothing\n if (totalTestsCount === undefined) {\n return ''\n }\n\n // When they failed, show failed/total\n if (failedTestsCount) {\n return `${failedTestsCount}/${totalTestsCount}`\n }\n\n // When they passed, show passed/total\n const completedTests =\n totalTestsCount - (pendingTestsCount || 0) - (failedTestsCount || 0)\n return `${completedTests}/${totalTestsCount}`\n})\n</script>\n\n<template>\n <div v-bind=\"cx(statusVariants({ status: state }))\">\n <ScalarIcon\n v-if=\"state\"\n :class=\"icon.color\"\n :icon=\"icon.name as Icon\"\n size=\"sm\" />\n <span\n v-if=\"!inline\"\n class=\"ml-1.25 capitalize\">\n {{ state }}\n </span>\n <span\n v-if=\"totalTestsCount !== undefined\"\n v-bind=\"cx(textVariants({ inline }))\">\n {{ getTestCountDisplay }}\n </span>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;EAgBA,MAAM,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"TestResultIndicator.vue.script.js","names":[],"sources":["../../../../../src/plugins/request-scripts/components/TestResults/TestResultIndicator.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarIcon, type Icon } from '@scalar/components/icon'\nimport { cva, useBindCx } from '@scalar/use-hooks/useBindCx'\nimport { computed } from 'vue'\n\nimport type { TestResult } from '@/libs/execute-scripts'\n\nconst { state, totalTestsCount, pendingTestsCount, failedTestsCount } =\n defineProps<{\n state?: TestResult['status']\n failedTestsCount: number | undefined\n pendingTestsCount: number | undefined\n totalTestsCount: number | undefined\n inline?: boolean\n }>()\n\nconst { cx } = useBindCx()\n\nconst icon = computed(() => {\n if (state === 'passed') {\n return { name: 'Checkmark', color: 'text-green p-0.25' }\n }\n\n if (state === 'failed') {\n return { name: 'Close', color: 'text-red' }\n }\n\n return { name: 'Ellipses', color: 'text-c-1' }\n})\n\nconst statusVariants = cva({\n base: 'flex items-center gap-1.5 rounded-full border pr-2 pl-1.25',\n variants: {\n status: {\n passed: 'text-green',\n failed: 'text-red',\n pending: 'text-orange',\n },\n },\n})\n\nconst textVariants = cva({\n base: 'text-c-2',\n variants: {\n inline: {\n true: 'text-xs',\n false: 'text-sm',\n },\n },\n})\n\n// Display the number of tests that passed e.g 1/3\nconst getTestCountDisplay = computed(() => {\n // When there are no tests, show nothing\n if (totalTestsCount === undefined) {\n return ''\n }\n\n // When they failed, show failed/total\n if (failedTestsCount) {\n return `${failedTestsCount}/${totalTestsCount}`\n }\n\n // When they passed, show passed/total\n const completedTests =\n totalTestsCount - (pendingTestsCount || 0) - (failedTestsCount || 0)\n return `${completedTests}/${totalTestsCount}`\n})\n</script>\n\n<template>\n <div v-bind=\"cx(statusVariants({ status: state }))\">\n <ScalarIcon\n v-if=\"state\"\n :class=\"icon.color\"\n :icon=\"icon.name as Icon\"\n size=\"sm\" />\n <span\n v-if=\"!inline\"\n class=\"ml-1.25 capitalize\">\n {{ state }}\n </span>\n <span\n v-if=\"totalTestsCount !== undefined\"\n v-bind=\"cx(textVariants({ inline }))\">\n {{ getTestCountDisplay }}\n </span>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;EAgBA,MAAM,EAAE,OAAO,UAAU;EAEzB,MAAM,OAAO,eAAe;GAC1B,IAAI,QAAA,UAAU,UACZ,OAAO;IAAE,MAAM;IAAa,OAAO;GAAoB;GAGzD,IAAI,QAAA,UAAU,UACZ,OAAO;IAAE,MAAM;IAAS,OAAO;GAAW;GAG5C,OAAO;IAAE,MAAM;IAAY,OAAO;GAAW;EAC/C,CAAC;EAED,MAAM,iBAAiB,IAAI;GACzB,MAAM;GACN,UAAU,EACR,QAAQ;IACN,QAAQ;IACR,QAAQ;IACR,SAAS;GACX,EACF;EACF,CAAC;EAED,MAAM,eAAe,IAAI;GACvB,MAAM;GACN,UAAU,EACR,QAAQ;IACN,MAAM;IACN,OAAO;GACT,EACF;EACF,CAAC;EAGD,MAAM,sBAAsB,eAAe;GAEzC,IAAI,QAAA,oBAAoB,KAAA,GACtB,OAAO;GAIT,IAAI,QAAA,kBACF,OAAO,GAAG,QAAA,iBAAiB,GAAG,QAAA;GAMhC,OAAO,GADL,QAAA,mBAAmB,QAAA,qBAAqB,MAAM,QAAA,oBAAoB,GAC3C,GAAG,QAAA;EAC9B,CAAC;;uBAIC,mBAgBM,OAAA,eAAA,mBAhBO,MAAA,EAAA,CAAE,CAAC,MAAA,cAAA,CAAc,CAAA,EAAA,QAAW,QAAA,MAAK,CAAA,CAAA,CAAA,CAAA,GAAA;IAEpC,QAAA,SAAA,UAAA,GADR,YAIc,MAAA,UAAA,GAAA;;KAFX,OAAK,eAAE,KAAA,MAAK,KAAK;KACjB,MAAM,KAAA,MAAK;KACZ,MAAK;;KAEE,QAAA,UAAA,UAAA,GADT,mBAIO,QAJP,YAIO,gBADF,QAAA,KAAK,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;IAGF,QAAA,oBAAoB,KAAA,KAAA,UAAA,GAD5B,mBAIO,QAAA,eAAA,WAAA,EAAA,KAAA,EAAA,GAFG,MAAA,EAAA,CAAE,CAAC,MAAA,YAAA,CAAY,CAAA,EAAA,QAAG,QAAA,OAAM,CAAA,CAAA,CAAA,CAAA,GAAA,gBAC7B,oBAAA,KAAmB,GAAA,EAAA,KAAA,mBAAA,IAAA,IAAA"}
|
|
@@ -9,7 +9,7 @@ var _hoisted_5 = {
|
|
|
9
9
|
key: 0,
|
|
10
10
|
class: "bg-b-danger text-c-1 flex items-center pr-3 pb-1.5 pl-9"
|
|
11
11
|
};
|
|
12
|
-
var TestResultItem_vue_vue_type_script_setup_true_lang_default =
|
|
12
|
+
var TestResultItem_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
13
13
|
__name: "TestResultItem",
|
|
14
14
|
props: {
|
|
15
15
|
result: {},
|
package/dist/plugins/request-scripts/components/TestResults/TestResultItem.vue.script.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestResultItem.vue.script.js","names":[],"sources":["../../../../../src/plugins/request-scripts/components/TestResults/TestResultItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarIcon, type Icon } from '@scalar/components/icon'\nimport { computed } from 'vue'\n\nimport type { TestResult } from '@/libs/execute-scripts'\n\nconst { currentState, result } = defineProps<{\n result: TestResult\n currentState: TestResult['status']\n}>()\n\nconst resultStatus = computed(() => {\n if (result.passed) {\n return { icon: 'Checkmark', color: 'text-green p-0.25' }\n }\n\n if (!result.passed && currentState !== 'pending') {\n return { icon: 'Close', color: 'text-red' }\n }\n\n return { icon: 'Ellipses', color: 'text-c-1' }\n})\n</script>\n\n<template>\n <div class=\"flex flex-col\">\n <div\n class=\"flex h-8 items-center gap-2 pr-3 pl-2.25\"\n :class=\"result.error && 'bg-b-danger'\">\n <!-- Title -->\n <div class=\"flex items-center gap-3 p-2\">\n <ScalarIcon\n :class=\"resultStatus.color\"\n :icon=\"resultStatus.icon as Icon\"\n size=\"sm\" />\n\n <span class=\"text-c-2 overflow-hidden text-ellipsis whitespace-nowrap\">\n {{ result.title }}\n </span>\n </div>\n <!-- Duration -->\n <span class=\"text-c-3 ml-auto whitespace-nowrap\">\n {{ result.duration }} ms\n </span>\n </div>\n <!-- Error -->\n <div\n v-if=\"result.error\"\n class=\"bg-b-danger text-c-1 flex items-center pr-3 pb-1.5 pl-9\">\n {{ result.error }}\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;EAWA,MAAM,eAAe,eAAe;
|
|
1
|
+
{"version":3,"file":"TestResultItem.vue.script.js","names":[],"sources":["../../../../../src/plugins/request-scripts/components/TestResults/TestResultItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarIcon, type Icon } from '@scalar/components/icon'\nimport { computed } from 'vue'\n\nimport type { TestResult } from '@/libs/execute-scripts'\n\nconst { currentState, result } = defineProps<{\n result: TestResult\n currentState: TestResult['status']\n}>()\n\nconst resultStatus = computed(() => {\n if (result.passed) {\n return { icon: 'Checkmark', color: 'text-green p-0.25' }\n }\n\n if (!result.passed && currentState !== 'pending') {\n return { icon: 'Close', color: 'text-red' }\n }\n\n return { icon: 'Ellipses', color: 'text-c-1' }\n})\n</script>\n\n<template>\n <div class=\"flex flex-col\">\n <div\n class=\"flex h-8 items-center gap-2 pr-3 pl-2.25\"\n :class=\"result.error && 'bg-b-danger'\">\n <!-- Title -->\n <div class=\"flex items-center gap-3 p-2\">\n <ScalarIcon\n :class=\"resultStatus.color\"\n :icon=\"resultStatus.icon as Icon\"\n size=\"sm\" />\n\n <span class=\"text-c-2 overflow-hidden text-ellipsis whitespace-nowrap\">\n {{ result.title }}\n </span>\n </div>\n <!-- Duration -->\n <span class=\"text-c-3 ml-auto whitespace-nowrap\">\n {{ result.duration }} ms\n </span>\n </div>\n <!-- Error -->\n <div\n v-if=\"result.error\"\n class=\"bg-b-danger text-c-1 flex items-center pr-3 pb-1.5 pl-9\">\n {{ result.error }}\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;EAWA,MAAM,eAAe,eAAe;GAClC,IAAI,QAAA,OAAO,QACT,OAAO;IAAE,MAAM;IAAa,OAAO;GAAoB;GAGzD,IAAI,CAAC,QAAA,OAAO,UAAU,QAAA,iBAAiB,WACrC,OAAO;IAAE,MAAM;IAAS,OAAO;GAAW;GAG5C,OAAO;IAAE,MAAM;IAAY,OAAO;GAAW;EAC/C,CAAC;;uBAIC,mBA0BM,OA1BN,YA0BM,CAzBJ,mBAkBM,OAAA,EAjBJ,OAAK,eAAA,CAAC,4CACE,QAAA,OAAO,SAAK,aAAA,CAAA,EAAA,GAAA,CAEpB,mBASM,OATN,YASM,CARJ,YAGc,MAAA,UAAA,GAAA;IAFX,OAAK,eAAE,aAAA,MAAa,KAAK;IACzB,MAAM,aAAA,MAAa;IACpB,MAAK;mCAEP,mBAEO,QAFP,YAEO,gBADF,QAAA,OAAO,KAAK,GAAA,CAAA,CAAA,CAAA,GAInB,mBAEO,QAFP,YAEO,gBADF,QAAA,OAAO,QAAQ,IAAG,QACvB,CAAA,CAAA,GAAA,CAAA,GAIM,QAAA,OAAO,SAAA,UAAA,GADf,mBAIM,OAJN,YAIM,gBADD,QAAA,OAAO,KAAK,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA"}
|
|
@@ -5,7 +5,7 @@ import TestSummary_default from "./TestSummary.vue.js";
|
|
|
5
5
|
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, openBlock, renderList, unref, withCtx } from "vue";
|
|
6
6
|
//#region src/plugins/request-scripts/components/TestResults/TestResults.vue?vue&type=script&setup=true&lang.ts
|
|
7
7
|
var _hoisted_1 = { class: "max-h-[calc(100%-32px)] divide-y overflow-y-auto border-t" };
|
|
8
|
-
var TestResults_vue_vue_type_script_setup_true_lang_default =
|
|
8
|
+
var TestResults_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
9
9
|
__name: "TestResults",
|
|
10
10
|
props: { results: {} },
|
|
11
11
|
setup(__props) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestResults.vue.script.js","names":[],"sources":["../../../../../src/plugins/request-scripts/components/TestResults/TestResults.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, type Ref } from 'vue'\n\nimport type { TestResult } from '@/libs/execute-scripts'\n\nimport { ViewLayoutCollapse } from '../../../../components/ViewLayout'\nimport TestResultIndicator from './TestResultIndicator.vue'\nimport TestResultItem from './TestResultItem.vue'\nimport TestSummary from './TestSummary.vue'\n\nconst { results } = defineProps<{\n results?: Ref<TestResult[]> | undefined\n}>()\n\nconst passedTests = computed(() =>\n results?.value?.filter((result: TestResult) => result.status === 'passed'),\n)\n\nconst pendingTests = computed(() =>\n results?.value?.filter((result: TestResult) => result.status === 'pending'),\n)\n\nconst failedTests = computed(() =>\n results?.value?.filter((result: TestResult) => result.status === 'failed'),\n)\n\nconst allTestsPassed = computed(\n () => passedTests.value?.length === results?.value?.length,\n)\n\nconst currentState = computed(() => {\n if (allTestsPassed.value) {\n return 'passed'\n }\n\n if (pendingTests.value?.length) {\n return 'pending'\n }\n\n return 'failed'\n})\n\nconst totalDuration = computed(\n () =>\n results?.value?.reduce(\n (acc: number, curr: TestResult) => acc + curr.duration,\n 0,\n ) ?? 0,\n)\n</script>\n\n<template>\n <ViewLayoutCollapse\n v-if=\"results?.value?.length\"\n class=\"overflow-auto text-sm\"\n :defaultOpen=\"true\">\n <template #title> Tests </template>\n <template #suffix>\n <TestResultIndicator\n :failedTestsCount=\"failedTests?.length\"\n inline\n :passedTestsCount=\"passedTests?.length\"\n :pendingTestsCount=\"pendingTests?.length\"\n :state=\"currentState\"\n :totalTestsCount=\"results?.value?.length\" />\n </template>\n\n <div class=\"max-h-[calc(100%-32px)] divide-y overflow-y-auto border-t\">\n <!-- Results -->\n <TestResultItem\n v-for=\"result in results?.value\"\n :key=\"result.title\"\n :currentState=\"result.status\"\n :result=\"result\" />\n\n <TestSummary\n v-if=\"results\"\n :allTestsPassed=\"allTestsPassed\"\n :failedTests=\"failedTests ?? []\"\n :passedTests=\"passedTests ?? []\"\n :pendingTests=\"pendingTests ?? []\"\n :results=\"results?.value\"\n :totalDuration=\"totalDuration\" />\n </div>\n </ViewLayoutCollapse>\n</template>\n"],"mappings":";;;;;;;;;;;EAcA,MAAM,cAAc,eAClB,QAAA,SAAS,OAAO,QAAQ,WAAuB,OAAO,WAAW,
|
|
1
|
+
{"version":3,"file":"TestResults.vue.script.js","names":[],"sources":["../../../../../src/plugins/request-scripts/components/TestResults/TestResults.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, type Ref } from 'vue'\n\nimport type { TestResult } from '@/libs/execute-scripts'\n\nimport { ViewLayoutCollapse } from '../../../../components/ViewLayout'\nimport TestResultIndicator from './TestResultIndicator.vue'\nimport TestResultItem from './TestResultItem.vue'\nimport TestSummary from './TestSummary.vue'\n\nconst { results } = defineProps<{\n results?: Ref<TestResult[]> | undefined\n}>()\n\nconst passedTests = computed(() =>\n results?.value?.filter((result: TestResult) => result.status === 'passed'),\n)\n\nconst pendingTests = computed(() =>\n results?.value?.filter((result: TestResult) => result.status === 'pending'),\n)\n\nconst failedTests = computed(() =>\n results?.value?.filter((result: TestResult) => result.status === 'failed'),\n)\n\nconst allTestsPassed = computed(\n () => passedTests.value?.length === results?.value?.length,\n)\n\nconst currentState = computed(() => {\n if (allTestsPassed.value) {\n return 'passed'\n }\n\n if (pendingTests.value?.length) {\n return 'pending'\n }\n\n return 'failed'\n})\n\nconst totalDuration = computed(\n () =>\n results?.value?.reduce(\n (acc: number, curr: TestResult) => acc + curr.duration,\n 0,\n ) ?? 0,\n)\n</script>\n\n<template>\n <ViewLayoutCollapse\n v-if=\"results?.value?.length\"\n class=\"overflow-auto text-sm\"\n :defaultOpen=\"true\">\n <template #title> Tests </template>\n <template #suffix>\n <TestResultIndicator\n :failedTestsCount=\"failedTests?.length\"\n inline\n :passedTestsCount=\"passedTests?.length\"\n :pendingTestsCount=\"pendingTests?.length\"\n :state=\"currentState\"\n :totalTestsCount=\"results?.value?.length\" />\n </template>\n\n <div class=\"max-h-[calc(100%-32px)] divide-y overflow-y-auto border-t\">\n <!-- Results -->\n <TestResultItem\n v-for=\"result in results?.value\"\n :key=\"result.title\"\n :currentState=\"result.status\"\n :result=\"result\" />\n\n <TestSummary\n v-if=\"results\"\n :allTestsPassed=\"allTestsPassed\"\n :failedTests=\"failedTests ?? []\"\n :passedTests=\"passedTests ?? []\"\n :pendingTests=\"pendingTests ?? []\"\n :results=\"results?.value\"\n :totalDuration=\"totalDuration\" />\n </div>\n </ViewLayoutCollapse>\n</template>\n"],"mappings":";;;;;;;;;;;EAcA,MAAM,cAAc,eAClB,QAAA,SAAS,OAAO,QAAQ,WAAuB,OAAO,WAAW,QAAQ,CAC3E;EAEA,MAAM,eAAe,eACnB,QAAA,SAAS,OAAO,QAAQ,WAAuB,OAAO,WAAW,SAAS,CAC5E;EAEA,MAAM,cAAc,eAClB,QAAA,SAAS,OAAO,QAAQ,WAAuB,OAAO,WAAW,QAAQ,CAC3E;EAEA,MAAM,iBAAiB,eACf,YAAY,OAAO,WAAW,QAAA,SAAS,OAAO,MACtD;EAEA,MAAM,eAAe,eAAe;GAClC,IAAI,eAAe,OACjB,OAAO;GAGT,IAAI,aAAa,OAAO,QACtB,OAAO;GAGT,OAAO;EACT,CAAC;EAED,MAAM,gBAAgB,eAElB,QAAA,SAAS,OAAO,QACb,KAAa,SAAqB,MAAM,KAAK,UAC9C,CACF,KAAK,CACT;;UAKU,QAAA,SAAS,OAAO,UAAA,UAAA,GADxB,YAgCqB,MAAA,0BAAA,GAAA;;IA9BnB,OAAM;IACL,aAAa;;IACH,OAAK,cAAQ,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,gBAAP,WAAO,EAAA,CAAA,EAAA,CAAA;IACb,QAAM,cAO+B,CAN9C,YAM8C,6BAAA;KAL3C,kBAAkB,YAAA,OAAa;KAChC,QAAA;KACC,kBAAkB,YAAA,OAAa;KAC/B,mBAAmB,aAAA,OAAc;KACjC,OAAO,aAAA;KACP,iBAAiB,QAAA,SAAS,OAAO;;;;;;;;2BAmBhC,CAhBN,mBAgBM,OAhBN,YAgBM,EAAA,UAAA,IAAA,GAdJ,mBAIqB,UAAA,MAAA,WAHF,QAAA,SAAS,QAAnB,WAAM;yBADf,YAIqB,wBAAA;MAFlB,KAAK,OAAO;MACZ,cAAc,OAAO;MACb;;eAGH,QAAA,WAAA,UAAA,GADR,YAOmC,qBAAA;;KALhC,gBAAgB,eAAA;KAChB,aAAa,YAAA,SAAW,CAAA;KACxB,aAAa,YAAA,SAAW,CAAA;KACxB,cAAc,aAAA,SAAY,CAAA;KAC1B,SAAS,QAAA,SAAS;KAClB,eAAe,cAAA"}
|
|
@@ -7,7 +7,7 @@ var _hoisted_2 = {
|
|
|
7
7
|
class: "text-orange"
|
|
8
8
|
};
|
|
9
9
|
var _hoisted_3 = { class: "text-c-3 ml-auto" };
|
|
10
|
-
var TestSummary_vue_vue_type_script_setup_true_lang_default =
|
|
10
|
+
var TestSummary_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
11
11
|
__name: "TestSummary",
|
|
12
12
|
props: {
|
|
13
13
|
results: {},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestSummary.vue.script.js","names":[],"sources":["../../../../../src/plugins/request-scripts/components/TestResults/TestSummary.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { TestResult } from '@/libs/execute-scripts'\n\nimport TestResultIndicator from './TestResultIndicator.vue'\n\ndefineProps<{\n results: TestResult[]\n passedTests: TestResult[]\n failedTests: TestResult[]\n pendingTests: TestResult[]\n allTestsPassed: boolean\n totalDuration: number\n}>()\n</script>\n\n<template>\n <div class=\"flex h-8 items-center gap-1.5 pr-3 pl-1\">\n <TestResultIndicator\n :failedTestsCount=\"failedTests?.length\"\n :passedTestsCount=\"passedTests?.length\"\n :pendingTestsCount=\"pendingTests?.length\"\n :state=\"allTestsPassed ? 'passed' : 'failed'\"\n :totalTestsCount=\"results?.length\" />\n <span\n v-if=\"pendingTests?.length\"\n class=\"text-orange\">\n {{ pendingTests?.length }}\n Pending\n </span>\n <span class=\"text-c-3 ml-auto\"> {{ totalDuration.toFixed(1) }} ms </span>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;uBAgBE,mBAcM,OAdN,YAcM;IAbJ,YAKuC,6BAAA;KAJpC,kBAAkB,QAAA,aAAa;KAC/B,kBAAkB,QAAA,aAAa;KAC/B,mBAAmB,QAAA,cAAc;KACjC,OAAO,QAAA,iBAAc,WAAA;KACrB,iBAAiB,QAAA,SAAS;;;;;;;;IAErB,QAAA,cAAc,UAAA,
|
|
1
|
+
{"version":3,"file":"TestSummary.vue.script.js","names":[],"sources":["../../../../../src/plugins/request-scripts/components/TestResults/TestSummary.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { TestResult } from '@/libs/execute-scripts'\n\nimport TestResultIndicator from './TestResultIndicator.vue'\n\ndefineProps<{\n results: TestResult[]\n passedTests: TestResult[]\n failedTests: TestResult[]\n pendingTests: TestResult[]\n allTestsPassed: boolean\n totalDuration: number\n}>()\n</script>\n\n<template>\n <div class=\"flex h-8 items-center gap-1.5 pr-3 pl-1\">\n <TestResultIndicator\n :failedTestsCount=\"failedTests?.length\"\n :passedTestsCount=\"passedTests?.length\"\n :pendingTestsCount=\"pendingTests?.length\"\n :state=\"allTestsPassed ? 'passed' : 'failed'\"\n :totalTestsCount=\"results?.length\" />\n <span\n v-if=\"pendingTests?.length\"\n class=\"text-orange\">\n {{ pendingTests?.length }}\n Pending\n </span>\n <span class=\"text-c-3 ml-auto\"> {{ totalDuration.toFixed(1) }} ms </span>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;uBAgBE,mBAcM,OAdN,YAcM;IAbJ,YAKuC,6BAAA;KAJpC,kBAAkB,QAAA,aAAa;KAC/B,kBAAkB,QAAA,aAAa;KAC/B,mBAAmB,QAAA,cAAc;KACjC,OAAO,QAAA,iBAAc,WAAA;KACrB,iBAAiB,QAAA,SAAS;;;;;;;;IAErB,QAAA,cAAc,UAAA,UAAA,GADtB,mBAKO,QALP,YAKO,gBAFF,QAAA,cAAc,MAAM,IAAG,aAE5B,CAAA,KAAA,mBAAA,IAAA,IAAA;IACA,mBAAyE,QAAzE,YAAyE,gBAAtC,QAAA,cAAc,QAAO,CAAA,CAAA,IAAM,QAAI,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-scripts-plugin.js","names":[],"sources":["../../../src/plugins/request-scripts/request-scripts-plugin.ts"],"sourcesContent":["import type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport { ref } from 'vue'\n\nimport { getScript } from '@/helpers/get-script'\nimport {\n type TestResult,\n executePostResponseScript,\n executePreRequestScript,\n prewarmSandboxFrame,\n} from '@/libs/execute-scripts'\nimport { TestResults } from '@/plugins/request-scripts/components/TestResults'\n\nimport ScriptsSection from './components/ScriptsSection.vue'\n\n/**\n * Unified request scripts plugin for client V2. Provides a single \"Scripts\"\n * section with:\n * - Pre-request script editor (x-pre-request) — runs before each request\n * - Post-response script editor (x-post-response) — runs after each response\n * - Test results from post-response assertions, shown in the response section\n *\n * Hooks: beforeRequest runs pre-request scripts and resets results;\n * responseReceived runs post-response scripts and updates test results.\n */\nexport const requestScriptsPlugin = (): ClientPlugin => {\n const results = ref<TestResult[]>([])\n\n return {\n components: {\n request: {\n component: ScriptsSection,\n },\n response: {\n component: TestResults,\n additionalProps: { results },\n },\n },\n\n hooks: {\n onRequestMount: ({ document, operation }) => {\n // Only warm up the sandbox when this operation actually has scripts. Requests without\n // scripts never touch the sandbox, so creating the iframe for them would waste work.\n const script = getScript(\n document['x-pre-request'],\n operation['x-pre-request'],\n document['x-post-response'],\n operation['x-post-response'],\n )\n\n if (script) {\n prewarmSandboxFrame()\n }\n },\n\n beforeRequest: async ({ requestBuilder, document, operation, variablesStore }) => {\n // Reset test results when a new request is sent\n results.value = []\n const script = getScript(document['x-pre-request'], operation['x-pre-request'])\n await executePreRequestScript(script, {\n requestBuilder,\n variablesStore,\n onTestResultsUpdate: (newResults) => (results.value = [...newResults]),\n })\n },\n\n responseReceived: async ({ requestBuilder, response, operation, document, variablesStore }) => {\n const script = getScript(document['x-post-response'], operation['x-post-response'])\n // Snapshot pre-request results before running post-response script\n const preRequestResults = [...results.value]\n await executePostResponseScript(script, {\n requestBuilder,\n response,\n onTestResultsUpdate: (postResponseResults) =>\n (results.value = [...preRequestResults, ...postResponseResults]),\n variablesStore,\n })\n },\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAwBA,IAAa,6BAA2C;CACtD,MAAM,UAAU,IAAkB,
|
|
1
|
+
{"version":3,"file":"request-scripts-plugin.js","names":[],"sources":["../../../src/plugins/request-scripts/request-scripts-plugin.ts"],"sourcesContent":["import type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport { ref } from 'vue'\n\nimport { getScript } from '@/helpers/get-script'\nimport {\n type TestResult,\n executePostResponseScript,\n executePreRequestScript,\n prewarmSandboxFrame,\n} from '@/libs/execute-scripts'\nimport { TestResults } from '@/plugins/request-scripts/components/TestResults'\n\nimport ScriptsSection from './components/ScriptsSection.vue'\n\n/**\n * Unified request scripts plugin for client V2. Provides a single \"Scripts\"\n * section with:\n * - Pre-request script editor (x-pre-request) — runs before each request\n * - Post-response script editor (x-post-response) — runs after each response\n * - Test results from post-response assertions, shown in the response section\n *\n * Hooks: beforeRequest runs pre-request scripts and resets results;\n * responseReceived runs post-response scripts and updates test results.\n */\nexport const requestScriptsPlugin = (): ClientPlugin => {\n const results = ref<TestResult[]>([])\n\n return {\n components: {\n request: {\n component: ScriptsSection,\n },\n response: {\n component: TestResults,\n additionalProps: { results },\n },\n },\n\n hooks: {\n onRequestMount: ({ document, operation }) => {\n // Only warm up the sandbox when this operation actually has scripts. Requests without\n // scripts never touch the sandbox, so creating the iframe for them would waste work.\n const script = getScript(\n document['x-pre-request'],\n operation['x-pre-request'],\n document['x-post-response'],\n operation['x-post-response'],\n )\n\n if (script) {\n prewarmSandboxFrame()\n }\n },\n\n beforeRequest: async ({ requestBuilder, document, operation, variablesStore }) => {\n // Reset test results when a new request is sent\n results.value = []\n const script = getScript(document['x-pre-request'], operation['x-pre-request'])\n await executePreRequestScript(script, {\n requestBuilder,\n variablesStore,\n onTestResultsUpdate: (newResults) => (results.value = [...newResults]),\n })\n },\n\n responseReceived: async ({ requestBuilder, response, operation, document, variablesStore }) => {\n const script = getScript(document['x-post-response'], operation['x-post-response'])\n // Snapshot pre-request results before running post-response script\n const preRequestResults = [...results.value]\n await executePostResponseScript(script, {\n requestBuilder,\n response,\n onTestResultsUpdate: (postResponseResults) =>\n (results.value = [...preRequestResults, ...postResponseResults]),\n variablesStore,\n })\n },\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAwBA,IAAa,6BAA2C;CACtD,MAAM,UAAU,IAAkB,CAAC,CAAC;CAEpC,OAAO;EACL,YAAY;GACV,SAAS,EACP,WAAW,uBACb;GACA,UAAU;IACR,WAAW;IACX,iBAAiB,EAAE,QAAQ;GAC7B;EACF;EAEA,OAAO;GACL,iBAAiB,EAAE,UAAU,gBAAgB;IAU3C,IAPe,UACb,SAAS,kBACT,UAAU,kBACV,SAAS,oBACT,UAAU,kBAGR,GACF,oBAAoB;GAExB;GAEA,eAAe,OAAO,EAAE,gBAAgB,UAAU,WAAW,qBAAqB;IAEhF,QAAQ,QAAQ,CAAC;IAEjB,MAAM,wBADS,UAAU,SAAS,kBAAkB,UAAU,gBAChC,GAAQ;KACpC;KACA;KACA,sBAAsB,eAAgB,QAAQ,QAAQ,CAAC,GAAG,UAAU;IACtE,CAAC;GACH;GAEA,kBAAkB,OAAO,EAAE,gBAAgB,UAAU,WAAW,UAAU,qBAAqB;IAC7F,MAAM,SAAS,UAAU,SAAS,oBAAoB,UAAU,kBAAkB;IAElF,MAAM,oBAAoB,CAAC,GAAG,QAAQ,KAAK;IAC3C,MAAM,0BAA0B,QAAQ;KACtC;KACA;KACA,sBAAsB,wBACnB,QAAQ,QAAQ,CAAC,GAAG,mBAAmB,GAAG,mBAAmB;KAChE;IACF,CAAC;GACH;EACF;CACF;AACF"}
|
package/package.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"post-response scripts",
|
|
20
20
|
"api client"
|
|
21
21
|
],
|
|
22
|
-
"version": "0.4.
|
|
22
|
+
"version": "0.4.37",
|
|
23
23
|
"engines": {
|
|
24
24
|
"node": ">=22"
|
|
25
25
|
},
|
|
@@ -58,18 +58,18 @@
|
|
|
58
58
|
"@types/postman-collection": "^3.5.11",
|
|
59
59
|
"postman-collection": "^5.3.0",
|
|
60
60
|
"postman-sandbox": "^6.6.1",
|
|
61
|
-
"vue": "^3.5.
|
|
62
|
-
"@scalar/components": "0.27.
|
|
63
|
-
"@scalar/
|
|
64
|
-
"@scalar/oas-utils": "0.19.9",
|
|
61
|
+
"vue": "^3.5.40",
|
|
62
|
+
"@scalar/components": "0.27.11",
|
|
63
|
+
"@scalar/oas-utils": "0.19.11",
|
|
65
64
|
"@scalar/use-hooks": "0.4.9",
|
|
66
|
-
"@scalar/
|
|
67
|
-
"@scalar/
|
|
65
|
+
"@scalar/use-codemirror": "0.14.14",
|
|
66
|
+
"@scalar/workspace-store": "0.57.0",
|
|
67
|
+
"@scalar/helpers": "0.10.0"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
71
|
-
"@vue/test-utils": "2.4.
|
|
72
|
-
"vite": "8.
|
|
70
|
+
"@vitejs/plugin-vue": "^6.0.8",
|
|
71
|
+
"@vue/test-utils": "2.4.11",
|
|
72
|
+
"vite": "8.1.5"
|
|
73
73
|
},
|
|
74
74
|
"scripts": {
|
|
75
75
|
"build": "vite build && vue-tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|