Compare commits
17 Commits
reference/
...
orphan-pr/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d78e6d209 | ||
|
|
172e459872 | ||
|
|
5233c36904 | ||
|
|
6417ece710 | ||
|
|
a3d92c862c | ||
|
|
f84b67ec39 | ||
|
|
3afd2c1cf2 | ||
|
|
ed49c0b72c | ||
|
|
bc2f81ff25 | ||
|
|
721a201e7a | ||
|
|
b0071e6859 | ||
|
|
3a41d79c2f | ||
|
|
2fd9eb0767 | ||
|
|
2aef9c5691 | ||
|
|
14e9d19ae6 | ||
|
|
a04b634089 | ||
|
|
2244d38439 |
@@ -84,7 +84,7 @@
|
||||
|
||||
_Customized themes are synced with cloud / local session_
|
||||
|
||||
🔥 **PWA:** Install as a [PWA](https://web.dev/what-are-pwas/) on your device.
|
||||
🔥 **PWA:** Install as a [PWA](https://developers.google.com/web/progressive-web-apps) on your device.
|
||||
|
||||
- Instant loading with Service Workers
|
||||
- Offline support
|
||||
|
||||
@@ -6,26 +6,21 @@
|
||||
'!flex-row-reverse': SIDEBAR_ON_LEFT && mdAndLarger,
|
||||
}"
|
||||
:horizontal="!mdAndLarger"
|
||||
@resize="setPaneEvent($event, 'vertical')"
|
||||
>
|
||||
<Pane
|
||||
:size="PANE_MAIN_SIZE"
|
||||
size="75"
|
||||
min-size="65"
|
||||
class="hide-scrollbar !overflow-auto flex flex-col"
|
||||
>
|
||||
<Splitpanes
|
||||
class="smart-splitter"
|
||||
:horizontal="COLUMN_LAYOUT"
|
||||
@resize="setPaneEvent($event, 'horizontal')"
|
||||
>
|
||||
<Splitpanes class="smart-splitter" :horizontal="COLUMN_LAYOUT">
|
||||
<Pane
|
||||
:size="PANE_MAIN_TOP_SIZE"
|
||||
:size="COLUMN_LAYOUT ? 45 : 50"
|
||||
class="hide-scrollbar !overflow-auto flex flex-col"
|
||||
>
|
||||
<slot name="primary" />
|
||||
</Pane>
|
||||
<Pane
|
||||
:size="PANE_MAIN_BOTTOM_SIZE"
|
||||
:size="COLUMN_LAYOUT ? 65 : 50"
|
||||
class="flex flex-col hide-scrollbar !overflow-auto"
|
||||
>
|
||||
<slot name="secondary" />
|
||||
@@ -34,7 +29,7 @@
|
||||
</Pane>
|
||||
<Pane
|
||||
v-if="SIDEBAR && hasSidebar"
|
||||
:size="PANE_SIDEBAR_SIZE"
|
||||
size="25"
|
||||
min-size="20"
|
||||
class="hide-scrollbar !overflow-auto flex flex-col"
|
||||
>
|
||||
@@ -47,9 +42,8 @@
|
||||
import { Splitpanes, Pane } from "splitpanes"
|
||||
import "splitpanes/dist/splitpanes.css"
|
||||
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
|
||||
import { computed, useSlots, ref } from "@nuxtjs/composition-api"
|
||||
import { computed, useSlots } from "@nuxtjs/composition-api"
|
||||
import { useSetting } from "~/newstore/settings"
|
||||
import { setLocalConfig, getLocalConfig } from "~/newstore/localpersistence"
|
||||
|
||||
const SIDEBAR_ON_LEFT = useSetting("SIDEBAR_ON_LEFT")
|
||||
|
||||
@@ -63,60 +57,4 @@ const SIDEBAR = useSetting("SIDEBAR")
|
||||
const slots = useSlots()
|
||||
|
||||
const hasSidebar = computed(() => !!slots.sidebar)
|
||||
|
||||
const props = defineProps({
|
||||
layoutId: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
})
|
||||
|
||||
type PaneEvent = {
|
||||
max: number
|
||||
min: number
|
||||
size: number
|
||||
}
|
||||
|
||||
const PANE_SIDEBAR_SIZE = ref(25)
|
||||
const PANE_MAIN_SIZE = ref(75)
|
||||
const PANE_MAIN_TOP_SIZE = ref(45)
|
||||
const PANE_MAIN_BOTTOM_SIZE = ref(65)
|
||||
|
||||
if (!COLUMN_LAYOUT.value) {
|
||||
PANE_MAIN_TOP_SIZE.value = 50
|
||||
PANE_MAIN_BOTTOM_SIZE.value = 50
|
||||
}
|
||||
|
||||
function setPaneEvent(event: PaneEvent[], type: "vertical" | "horizontal") {
|
||||
if (!props.layoutId) return
|
||||
const storageKey = `${props.layoutId}-pane-config-${type}`
|
||||
setLocalConfig(storageKey, JSON.stringify(event))
|
||||
}
|
||||
|
||||
function populatePaneEvent() {
|
||||
if (!props.layoutId) return
|
||||
|
||||
const verticalPaneData = getPaneData("vertical")
|
||||
if (verticalPaneData) {
|
||||
const [mainPane, sidebarPane] = verticalPaneData
|
||||
PANE_MAIN_SIZE.value = mainPane?.size
|
||||
PANE_SIDEBAR_SIZE.value = sidebarPane?.size
|
||||
}
|
||||
|
||||
const horizontalPaneData = getPaneData("horizontal")
|
||||
if (horizontalPaneData) {
|
||||
const [mainTopPane, mainBottomPane] = horizontalPaneData
|
||||
PANE_MAIN_TOP_SIZE.value = mainTopPane?.size
|
||||
PANE_MAIN_BOTTOM_SIZE.value = mainBottomPane?.size
|
||||
}
|
||||
}
|
||||
|
||||
function getPaneData(type: "vertical" | "horizontal"): PaneEvent[] | null {
|
||||
const storageKey = `${props.layoutId}-pane-config-${type}`
|
||||
const paneEvent = getLocalConfig(storageKey)
|
||||
if (!paneEvent) return null
|
||||
return JSON.parse(paneEvent)
|
||||
}
|
||||
|
||||
populatePaneEvent()
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div v-show="show">
|
||||
<div v-if="show">
|
||||
<SmartTabs
|
||||
:id="'collections_tab'"
|
||||
v-model="selectedCollectionTab"
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
autocomplete="off"
|
||||
:placeholder="$t('action.search')"
|
||||
class="py-2 pl-4 pr-2 bg-transparent"
|
||||
:disabled="collectionsType.type == 'team-collections'"
|
||||
/>
|
||||
</div>
|
||||
<CollectionsChooseType
|
||||
|
||||
@@ -339,14 +339,7 @@ const selectRequest = () => {
|
||||
confirmChange.value = false
|
||||
setRestReq(props.request)
|
||||
} else if (!active.value) {
|
||||
// If the current request is the same as the request to be loaded in, there is no data loss
|
||||
const currentReq = getRESTRequest()
|
||||
|
||||
if (isEqualHoppRESTRequest(currentReq, props.request)) {
|
||||
setRestReq(props.request)
|
||||
} else {
|
||||
confirmChange.value = true
|
||||
}
|
||||
confirmChange.value = true
|
||||
} else {
|
||||
const currentReqWithNoChange = active.value.req
|
||||
const currentFullReq = getRESTRequest()
|
||||
|
||||
@@ -261,7 +261,7 @@ const active = useReadonlyStream(restSaveContext$, null)
|
||||
const isSelected = computed(
|
||||
() =>
|
||||
props.picked &&
|
||||
props.picked.pickedType === "teams-request" &&
|
||||
props.picked.pickedType === "teams-collection" &&
|
||||
props.picked.requestID === props.requestIndex
|
||||
)
|
||||
|
||||
@@ -312,7 +312,7 @@ const selectRequest = () => {
|
||||
if (props.saveRequest) {
|
||||
emit("select", {
|
||||
picked: {
|
||||
pickedType: "teams-request",
|
||||
pickedType: "teams-collection",
|
||||
requestID: props.requestIndex,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -50,18 +50,18 @@
|
||||
</div>
|
||||
<div class="border rounded divide-y divide-dividerLight border-divider">
|
||||
<div
|
||||
v-for="({ id, env }, index) in vars"
|
||||
:key="`variable-${id}-${index}`"
|
||||
v-for="(variable, index) in vars"
|
||||
:key="`variable-${index}`"
|
||||
class="flex divide-x divide-dividerLight"
|
||||
>
|
||||
<input
|
||||
v-model="env.key"
|
||||
v-model="variable.key"
|
||||
class="flex flex-1 px-4 py-2 bg-transparent"
|
||||
:placeholder="`${t('count.variable', { count: index + 1 })}`"
|
||||
:name="'param' + index"
|
||||
/>
|
||||
<SmartEnvInput
|
||||
v-model="env.value"
|
||||
v-model="variable.value"
|
||||
:placeholder="`${t('count.value', { count: index + 1 })}`"
|
||||
:envs="liveEnvs"
|
||||
:name="'value' + index"
|
||||
@@ -119,9 +119,6 @@
|
||||
import clone from "lodash/clone"
|
||||
import { computed, ref, watch } from "@nuxtjs/composition-api"
|
||||
import * as E from "fp-ts/Either"
|
||||
import * as A from "fp-ts/Array"
|
||||
import * as O from "fp-ts/Option"
|
||||
import { pipe, flow } from "fp-ts/function"
|
||||
import { Environment, parseTemplateStringE } from "@hoppscotch/data"
|
||||
import { refAutoReset } from "@vueuse/core"
|
||||
import {
|
||||
@@ -140,14 +137,6 @@ import {
|
||||
useToast,
|
||||
} from "~/helpers/utils/composables"
|
||||
|
||||
type EnvironmentVariable = {
|
||||
id: number
|
||||
env: {
|
||||
key: string
|
||||
value: string
|
||||
}
|
||||
}
|
||||
|
||||
const t = useI18n()
|
||||
const toast = useToast()
|
||||
|
||||
@@ -170,12 +159,8 @@ const emit = defineEmits<{
|
||||
(e: "hide-modal"): void
|
||||
}>()
|
||||
|
||||
const idTicker = ref(0)
|
||||
|
||||
const name = ref<string | null>(null)
|
||||
const vars = ref<EnvironmentVariable[]>([
|
||||
{ id: idTicker.value++, env: { key: "", value: "" } },
|
||||
])
|
||||
const vars = ref([{ key: "", value: "" }])
|
||||
|
||||
const clearIcon = refAutoReset<"trash-2" | "check">("trash-2", 1000)
|
||||
|
||||
@@ -202,15 +187,15 @@ const workingEnv = computed(() => {
|
||||
const envList = useReadonlyStream(environments$, []) || props.envVars()
|
||||
|
||||
const evnExpandError = computed(() => {
|
||||
const variables = pipe(
|
||||
vars.value,
|
||||
A.map((e) => e.env)
|
||||
)
|
||||
for (const variable of vars.value) {
|
||||
const result = parseTemplateStringE(variable.value.toString(), vars.value)
|
||||
|
||||
return pipe(
|
||||
variables,
|
||||
A.exists(({ value }) => E.isLeft(parseTemplateStringE(value, variables)))
|
||||
)
|
||||
if (E.isLeft(result)) {
|
||||
console.error("error", result.left)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
const liveEnvs = computed(() => {
|
||||
@@ -233,38 +218,21 @@ watch(
|
||||
(show) => {
|
||||
if (show) {
|
||||
name.value = workingEnv.value?.name ?? null
|
||||
vars.value = pipe(
|
||||
workingEnv.value?.variables ?? [],
|
||||
A.map((e) => ({
|
||||
id: idTicker.value++,
|
||||
env: clone(e),
|
||||
}))
|
||||
)
|
||||
vars.value = clone(workingEnv.value?.variables ?? [])
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const clearContent = () => {
|
||||
vars.value = [
|
||||
{
|
||||
id: idTicker.value++,
|
||||
env: {
|
||||
key: "",
|
||||
value: "",
|
||||
},
|
||||
},
|
||||
]
|
||||
vars.value = []
|
||||
clearIcon.value = "check"
|
||||
toast.success(`${t("state.cleared")}`)
|
||||
}
|
||||
|
||||
const addEnvironmentVariable = () => {
|
||||
vars.value.push({
|
||||
id: idTicker.value++,
|
||||
env: {
|
||||
key: "",
|
||||
value: "",
|
||||
},
|
||||
key: "",
|
||||
value: "",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -278,19 +246,9 @@ const saveEnvironment = () => {
|
||||
return
|
||||
}
|
||||
|
||||
const filterdVariables = pipe(
|
||||
vars.value,
|
||||
A.filterMap(
|
||||
flow(
|
||||
O.fromPredicate((e) => e.env.key !== ""),
|
||||
O.map((e) => e.env)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
const environmentUpdated: Environment = {
|
||||
name: name.value,
|
||||
variables: filterdVariables,
|
||||
variables: vars.value,
|
||||
}
|
||||
|
||||
if (props.action === "new") {
|
||||
|
||||
@@ -1,13 +1,363 @@
|
||||
<template>
|
||||
<div>
|
||||
<HttpQueryParams />
|
||||
<br />
|
||||
<HttpPathVariables />
|
||||
<div class="flex flex-col flex-1">
|
||||
<div
|
||||
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-upperMobileSecondaryStickyFold sm:top-upperSecondaryStickyFold"
|
||||
>
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ t("request.parameter_list") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
to="https://docs.hoppscotch.io/features/parameters"
|
||||
blank
|
||||
:title="t('app.wiki')"
|
||||
svg="help-circle"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('action.clear_all')"
|
||||
svg="trash-2"
|
||||
@click.native="clearContent()"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('state.bulk_mode')"
|
||||
svg="edit"
|
||||
:class="{ '!text-accent': bulkMode }"
|
||||
@click.native="bulkMode = !bulkMode"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('add.new')"
|
||||
svg="plus"
|
||||
:disabled="bulkMode"
|
||||
@click.native="addParam"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="bulkMode" ref="bulkEditor" class="flex flex-col flex-1"></div>
|
||||
<div v-else>
|
||||
<draggable
|
||||
v-model="workingParams"
|
||||
animation="250"
|
||||
handle=".draggable-handle"
|
||||
draggable=".draggable-content"
|
||||
ghost-class="cursor-move"
|
||||
chosen-class="bg-primaryLight"
|
||||
drag-class="cursor-grabbing"
|
||||
>
|
||||
<div
|
||||
v-for="(param, index) in workingParams"
|
||||
:key="`param-${param.id}-${index}`"
|
||||
class="flex border-b divide-x divide-dividerLight border-dividerLight draggable-content group"
|
||||
>
|
||||
<span>
|
||||
<ButtonSecondary
|
||||
svg="grip-vertical"
|
||||
class="cursor-auto text-primary hover:text-primary"
|
||||
:class="{
|
||||
'draggable-handle group-hover:text-secondaryLight !cursor-grab':
|
||||
index !== workingParams?.length - 1,
|
||||
}"
|
||||
tabindex="-1"
|
||||
/>
|
||||
</span>
|
||||
<SmartEnvInput
|
||||
v-model="param.key"
|
||||
:placeholder="`${t('count.parameter', { count: index + 1 })}`"
|
||||
@change="
|
||||
updateParam(index, {
|
||||
id: param.id,
|
||||
key: $event,
|
||||
value: param.value,
|
||||
active: param.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<SmartEnvInput
|
||||
v-model="param.value"
|
||||
:placeholder="`${t('count.value', { count: index + 1 })}`"
|
||||
@change="
|
||||
updateParam(index, {
|
||||
id: param.id,
|
||||
key: param.key,
|
||||
value: $event,
|
||||
active: param.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<span>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="
|
||||
param.hasOwnProperty('active')
|
||||
? param.active
|
||||
? t('action.turn_off')
|
||||
: t('action.turn_on')
|
||||
: t('action.turn_off')
|
||||
"
|
||||
:svg="
|
||||
param.hasOwnProperty('active')
|
||||
? param.active
|
||||
? 'check-circle'
|
||||
: 'circle'
|
||||
: 'check-circle'
|
||||
"
|
||||
color="green"
|
||||
@click.native="
|
||||
updateParam(index, {
|
||||
id: param.id,
|
||||
key: param.key,
|
||||
value: param.value,
|
||||
active: param.hasOwnProperty('active')
|
||||
? !param.active
|
||||
: false,
|
||||
})
|
||||
"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('action.remove')"
|
||||
svg="trash"
|
||||
color="red"
|
||||
@click.native="deleteParam(index)"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</draggable>
|
||||
<div
|
||||
v-if="workingParams.length === 0"
|
||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
||||
>
|
||||
<img
|
||||
:src="`/images/states/${$colorMode.value}/add_files.svg`"
|
||||
loading="lazy"
|
||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
||||
:alt="`${t('empty.parameters')}`"
|
||||
/>
|
||||
<span class="pb-4 text-center">{{ t("empty.parameters") }}</span>
|
||||
<ButtonSecondary
|
||||
:label="`${t('add.new')}`"
|
||||
svg="plus"
|
||||
filled
|
||||
class="mb-4"
|
||||
@click.native="addParam"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* TODO: Code duplication between QueryParams and Variables
|
||||
*/
|
||||
import { Ref, ref, watch } from "@nuxtjs/composition-api"
|
||||
import { flow, pipe } from "fp-ts/function"
|
||||
import * as O from "fp-ts/Option"
|
||||
import * as A from "fp-ts/Array"
|
||||
import * as RA from "fp-ts/ReadonlyArray"
|
||||
import * as E from "fp-ts/Either"
|
||||
import {
|
||||
HoppRESTParam,
|
||||
parseRawKeyValueEntriesE,
|
||||
rawKeyValueEntriesToString,
|
||||
RawKeyValueEntry,
|
||||
} from "@hoppscotch/data"
|
||||
import isEqual from "lodash/isEqual"
|
||||
import cloneDeep from "lodash/cloneDeep"
|
||||
import draggable from "vuedraggable"
|
||||
import linter from "~/helpers/editor/linting/rawKeyValue"
|
||||
import { useCodemirror } from "~/helpers/editor/codemirror"
|
||||
import { useI18n, useToast, useStream } from "~/helpers/utils/composables"
|
||||
import { restParams$, setRESTParams } from "~/newstore/RESTSession"
|
||||
import { throwError } from "~/helpers/functional/error"
|
||||
import { objRemoveKey } from "~/helpers/functional/object"
|
||||
|
||||
const t = useI18n()
|
||||
const toast = useToast()
|
||||
|
||||
const idTicker = ref(0)
|
||||
|
||||
const bulkMode = ref(false)
|
||||
const bulkParams = ref("")
|
||||
const bulkEditor = ref<any | null>(null)
|
||||
|
||||
const deletionToast = ref<{ goAway: (delay: number) => void } | null>(null)
|
||||
|
||||
useCodemirror(bulkEditor, bulkParams, {
|
||||
extendedEditorConfig: {
|
||||
mode: "text/x-yaml",
|
||||
placeholder: `${t("state.bulk_mode_placeholder")}`,
|
||||
},
|
||||
linter,
|
||||
completer: null,
|
||||
environmentHighlights: true,
|
||||
})
|
||||
|
||||
// The functional parameters list (the parameters actually applied to the session)
|
||||
const params = useStream(restParams$, [], setRESTParams) as Ref<HoppRESTParam[]>
|
||||
|
||||
// The UI representation of the parameters list (has the empty end param)
|
||||
const workingParams = ref<Array<HoppRESTParam & { id: number }>>([
|
||||
{
|
||||
id: idTicker.value++,
|
||||
key: "",
|
||||
value: "",
|
||||
active: true,
|
||||
},
|
||||
])
|
||||
|
||||
// Rule: Working Params always have last element is always an empty param
|
||||
watch(workingParams, (paramsList) => {
|
||||
if (paramsList.length > 0 && paramsList[paramsList.length - 1].key !== "") {
|
||||
workingParams.value.push({
|
||||
id: idTicker.value++,
|
||||
key: "",
|
||||
value: "",
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Sync logic between params and working/bulk params
|
||||
watch(
|
||||
params,
|
||||
(newParamsList) => {
|
||||
// Sync should overwrite working params
|
||||
const filteredWorkingParams: HoppRESTParam[] = pipe(
|
||||
workingParams.value,
|
||||
A.filterMap(
|
||||
flow(
|
||||
O.fromPredicate((e) => e.key !== ""),
|
||||
O.map(objRemoveKey("id"))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
const filteredBulkParams = pipe(
|
||||
parseRawKeyValueEntriesE(bulkParams.value),
|
||||
E.map(
|
||||
flow(
|
||||
RA.filter((e) => e.key !== ""),
|
||||
RA.toArray
|
||||
)
|
||||
),
|
||||
E.getOrElse(() => [] as RawKeyValueEntry[])
|
||||
)
|
||||
|
||||
if (!isEqual(newParamsList, filteredWorkingParams)) {
|
||||
workingParams.value = pipe(
|
||||
newParamsList,
|
||||
A.map((x) => ({ id: idTicker.value++, ...x }))
|
||||
)
|
||||
}
|
||||
|
||||
if (!isEqual(newParamsList, filteredBulkParams)) {
|
||||
bulkParams.value = rawKeyValueEntriesToString(newParamsList)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
watch(workingParams, (newWorkingParams) => {
|
||||
const fixedParams = pipe(
|
||||
newWorkingParams,
|
||||
A.filterMap(
|
||||
flow(
|
||||
O.fromPredicate((e) => e.key !== ""),
|
||||
O.map(objRemoveKey("id"))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if (!isEqual(params.value, fixedParams)) {
|
||||
params.value = cloneDeep(fixedParams)
|
||||
}
|
||||
})
|
||||
|
||||
watch(bulkParams, (newBulkParams) => {
|
||||
const filteredBulkParams = pipe(
|
||||
parseRawKeyValueEntriesE(newBulkParams),
|
||||
E.map(
|
||||
flow(
|
||||
RA.filter((e) => e.key !== ""),
|
||||
RA.toArray
|
||||
)
|
||||
),
|
||||
E.getOrElse(() => [] as RawKeyValueEntry[])
|
||||
)
|
||||
|
||||
if (!isEqual(params.value, filteredBulkParams)) {
|
||||
params.value = filteredBulkParams
|
||||
}
|
||||
})
|
||||
|
||||
const addParam = () => {
|
||||
workingParams.value.push({
|
||||
id: idTicker.value++,
|
||||
key: "",
|
||||
value: "",
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
|
||||
const updateParam = (index: number, param: HoppRESTParam & { id: number }) => {
|
||||
workingParams.value = workingParams.value.map((h, i) =>
|
||||
i === index ? param : h
|
||||
)
|
||||
}
|
||||
|
||||
const deleteParam = (index: number) => {
|
||||
const paramsBeforeDeletion = cloneDeep(workingParams.value)
|
||||
|
||||
if (
|
||||
!(
|
||||
paramsBeforeDeletion.length > 0 &&
|
||||
index === paramsBeforeDeletion.length - 1
|
||||
)
|
||||
) {
|
||||
if (deletionToast.value) {
|
||||
deletionToast.value.goAway(0)
|
||||
deletionToast.value = null
|
||||
}
|
||||
|
||||
deletionToast.value = toast.success(`${t("state.deleted")}`, {
|
||||
action: [
|
||||
{
|
||||
text: `${t("action.undo")}`,
|
||||
onClick: (_, toastObject) => {
|
||||
workingParams.value = paramsBeforeDeletion
|
||||
toastObject.goAway(0)
|
||||
deletionToast.value = null
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
onComplete: () => {
|
||||
deletionToast.value = null
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
workingParams.value = pipe(
|
||||
workingParams.value,
|
||||
A.deleteAt(index),
|
||||
O.getOrElseW(() => throwError("Working Params Deletion Out of Bounds"))
|
||||
)
|
||||
}
|
||||
|
||||
const clearContent = () => {
|
||||
// set params list to the initial state
|
||||
workingParams.value = [
|
||||
{
|
||||
id: idTicker.value++,
|
||||
key: "",
|
||||
value: "",
|
||||
active: true,
|
||||
},
|
||||
]
|
||||
|
||||
bulkParams.value = ""
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
v-if="envExpandError"
|
||||
class="w-full px-4 py-2 mb-2 overflow-auto font-mono text-red-400 whitespace-normal rounded bg-primaryLight"
|
||||
>
|
||||
{{ nestedVars }}
|
||||
</div>
|
||||
<div
|
||||
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-upperMobileSecondaryStickyFold sm:top-upperSecondaryStickyFold"
|
||||
>
|
||||
<label class="font-semibold text-secondaryLight"> My Variables </label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
to="https://docs.hoppscotch.io/features/#"
|
||||
blank
|
||||
:title="t('app.wiki')"
|
||||
svg="help-circle"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('action.clear_all')"
|
||||
svg="trash-2"
|
||||
@click.native="clearContent()"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('add.new')"
|
||||
svg="plus"
|
||||
@click.native="addVar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<draggable
|
||||
v-model="workingVars"
|
||||
animation="250"
|
||||
handle=".draggable-handle"
|
||||
draggable=".draggable-content"
|
||||
ghost-class="cursor-move"
|
||||
chosen-class="bg-primaryLight"
|
||||
drag-class="cursor-grabbing"
|
||||
>
|
||||
<div
|
||||
v-for="(variable, index) in workingVars"
|
||||
:key="`vari-${variable.id}-${index}`"
|
||||
class="flex border-b divide-x divide-dividerLight border-dividerLight draggable-content group"
|
||||
>
|
||||
<span>
|
||||
<ButtonSecondary
|
||||
svg="grip-vertical"
|
||||
class="cursor-auto text-primary hover:text-primary"
|
||||
:class="{
|
||||
'draggable-handle group-hover:text-secondaryLight !cursor-grab':
|
||||
index !== workingVars?.length - 1,
|
||||
}"
|
||||
tabindex="-1"
|
||||
/>
|
||||
</span>
|
||||
<SmartEnvInput
|
||||
v-model="variable.key"
|
||||
:placeholder="`${t('count.variable', { count: index + 1 })}`"
|
||||
@change="
|
||||
updateVar(index, {
|
||||
id: variable.id,
|
||||
key: $event,
|
||||
value: variable.value,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<SmartEnvInput
|
||||
v-model="variable.value"
|
||||
:placeholder="`${t('count.value', { count: index + 1 })}`"
|
||||
@change="
|
||||
updateVar(index, {
|
||||
id: variable.id,
|
||||
key: variable.key,
|
||||
value: $event,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<span>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('action.remove')"
|
||||
svg="trash"
|
||||
color="red"
|
||||
@click.native="deleteVar(index)"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</draggable>
|
||||
<div
|
||||
v-if="workingVars.length === 0"
|
||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
||||
>
|
||||
<img
|
||||
:src="`/images/states/${$colorMode.value}/add_files.svg`"
|
||||
loading="lazy"
|
||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
||||
:alt="`${t('empty.parameters')}`"
|
||||
/>
|
||||
<span class="pb-4 text-center">{{ emptyVars }}</span>
|
||||
<ButtonSecondary
|
||||
:label="`${t('add.new')}`"
|
||||
svg="plus"
|
||||
filled
|
||||
class="mb-4"
|
||||
@click.native="addVar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, Ref, ref, watch } from "@nuxtjs/composition-api"
|
||||
import { flow, pipe } from "fp-ts/function"
|
||||
import * as O from "fp-ts/Option"
|
||||
import * as A from "fp-ts/Array"
|
||||
import { HoppRESTVar, parseMyVariablesString } from "@hoppscotch/data"
|
||||
import draggable from "vuedraggable"
|
||||
import cloneDeep from "lodash/cloneDeep"
|
||||
import isEqual from "lodash/isEqual"
|
||||
import * as E from "fp-ts/Either"
|
||||
import { useI18n, useStream, useToast } from "~/helpers/utils/composables"
|
||||
import { throwError } from "~/helpers/functional/error"
|
||||
import { restVars$, setRESTVars } from "~/newstore/RESTSession"
|
||||
import { objRemoveKey } from "~/helpers/functional/object"
|
||||
|
||||
const t = useI18n()
|
||||
const toast = useToast()
|
||||
|
||||
const emptyVars: string = "Add a new variable"
|
||||
const nestedVars: string = "nested variables greater than 10 levels"
|
||||
|
||||
const idTicker = ref(0)
|
||||
|
||||
const deletionToast = ref<{ goAway: (delay: number) => void } | null>(null)
|
||||
|
||||
// The functional variables list (the variables actually applied to the session)
|
||||
const vars = useStream(restVars$, [], setRESTVars) as Ref<HoppRESTVar[]>
|
||||
|
||||
// The UI representation of the variables list (has the empty end variable)
|
||||
const workingVars = ref<Array<HoppRESTVar & { id: number }>>([
|
||||
{
|
||||
id: idTicker.value++,
|
||||
key: "",
|
||||
value: "",
|
||||
},
|
||||
])
|
||||
|
||||
// Rule: Working vars always have last element is always an empty var
|
||||
watch(workingVars, (varsList) => {
|
||||
if (varsList.length > 0 && varsList[varsList.length - 1].key !== "") {
|
||||
workingVars.value.push({
|
||||
id: idTicker.value++,
|
||||
key: "",
|
||||
value: "",
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Sync logic between params and working/bulk params
|
||||
watch(
|
||||
vars,
|
||||
(newVarsList) => {
|
||||
// Sync should overwrite working params
|
||||
const filteredWorkingVars: HoppRESTVar[] = pipe(
|
||||
workingVars.value,
|
||||
A.filterMap(
|
||||
flow(
|
||||
O.fromPredicate((e) => e.key !== ""),
|
||||
O.map(objRemoveKey("id"))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if (!isEqual(newVarsList, filteredWorkingVars)) {
|
||||
workingVars.value = pipe(
|
||||
newVarsList,
|
||||
A.map((x) => ({ id: idTicker.value++, ...x }))
|
||||
)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
watch(workingVars, (newWorkingVars) => {
|
||||
const fixedVars = pipe(
|
||||
newWorkingVars,
|
||||
A.filterMap(
|
||||
flow(
|
||||
O.fromPredicate((e) => e.key !== ""),
|
||||
O.map(objRemoveKey("id"))
|
||||
)
|
||||
)
|
||||
)
|
||||
if (!isEqual(vars.value, fixedVars)) {
|
||||
vars.value = cloneDeep(fixedVars)
|
||||
}
|
||||
})
|
||||
|
||||
const addVar = () => {
|
||||
workingVars.value.push({
|
||||
id: idTicker.value++,
|
||||
key: "",
|
||||
value: "",
|
||||
})
|
||||
}
|
||||
|
||||
const updateVar = (index: number, vari: HoppRESTVar & { id: number }) => {
|
||||
workingVars.value = workingVars.value.map((h, i) => (i === index ? vari : h))
|
||||
}
|
||||
|
||||
const deleteVar = (index: number) => {
|
||||
const varsBeforeDeletion = cloneDeep(workingVars.value)
|
||||
if (
|
||||
!(varsBeforeDeletion.length > 0 && index === varsBeforeDeletion.length - 1)
|
||||
) {
|
||||
if (deletionToast.value) {
|
||||
deletionToast.value.goAway(0)
|
||||
deletionToast.value = null
|
||||
}
|
||||
|
||||
deletionToast.value = toast.success(`${t("state.deleted")}`, {
|
||||
action: [
|
||||
{
|
||||
text: `${t("action.undo")}`,
|
||||
onClick: (_, toastObject) => {
|
||||
workingVars.value = varsBeforeDeletion
|
||||
toastObject.goAway(0)
|
||||
deletionToast.value = null
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
onComplete: () => {
|
||||
deletionToast.value = null
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
workingVars.value = pipe(
|
||||
workingVars.value,
|
||||
A.deleteAt(index),
|
||||
O.getOrElseW(() => throwError("Working Params Deletion Out of Bounds"))
|
||||
)
|
||||
}
|
||||
|
||||
const envExpandError = computed(() => {
|
||||
const variables = pipe(vars.value)
|
||||
|
||||
return pipe(
|
||||
variables,
|
||||
A.exists(({ value }) => E.isLeft(parseMyVariablesString(value, variables)))
|
||||
)
|
||||
})
|
||||
|
||||
const clearContent = () => {
|
||||
// set params list to the initial state
|
||||
workingVars.value = [
|
||||
{
|
||||
id: idTicker.value++,
|
||||
key: "",
|
||||
value: "",
|
||||
},
|
||||
]
|
||||
}
|
||||
</script>
|
||||
@@ -1,363 +0,0 @@
|
||||
<template>
|
||||
<div class="flex flex-col flex-1">
|
||||
<div
|
||||
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-upperMobileSecondaryStickyFold sm:top-upperSecondaryStickyFold"
|
||||
>
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ t("request.parameter_list") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
to="https://docs.hoppscotch.io/features/parameters"
|
||||
blank
|
||||
:title="t('app.wiki')"
|
||||
svg="help-circle"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('action.clear_all')"
|
||||
svg="trash-2"
|
||||
@click.native="clearContent()"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('state.bulk_mode')"
|
||||
svg="edit"
|
||||
:class="{ '!text-accent': bulkMode }"
|
||||
@click.native="bulkMode = !bulkMode"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('add.new')"
|
||||
svg="plus"
|
||||
:disabled="bulkMode"
|
||||
@click.native="addParam"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="bulkMode" ref="bulkEditor" class="flex flex-col flex-1"></div>
|
||||
<div v-else>
|
||||
<draggable
|
||||
v-model="workingParams"
|
||||
animation="250"
|
||||
handle=".draggable-handle"
|
||||
draggable=".draggable-content"
|
||||
ghost-class="cursor-move"
|
||||
chosen-class="bg-primaryLight"
|
||||
drag-class="cursor-grabbing"
|
||||
>
|
||||
<div
|
||||
v-for="(param, index) in workingParams"
|
||||
:key="`param-${param.id}-${index}`"
|
||||
class="flex border-b divide-x divide-dividerLight border-dividerLight draggable-content group"
|
||||
>
|
||||
<span>
|
||||
<ButtonSecondary
|
||||
svg="grip-vertical"
|
||||
class="cursor-auto text-primary hover:text-primary"
|
||||
:class="{
|
||||
'draggable-handle group-hover:text-secondaryLight !cursor-grab':
|
||||
index !== workingParams?.length - 1,
|
||||
}"
|
||||
tabindex="-1"
|
||||
/>
|
||||
</span>
|
||||
<SmartEnvInput
|
||||
v-model="param.key"
|
||||
:placeholder="`${t('count.parameter', { count: index + 1 })}`"
|
||||
@change="
|
||||
updateParam(index, {
|
||||
id: param.id,
|
||||
key: $event,
|
||||
value: param.value,
|
||||
active: param.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<SmartEnvInput
|
||||
v-model="param.value"
|
||||
:placeholder="`${t('count.value', { count: index + 1 })}`"
|
||||
@change="
|
||||
updateParam(index, {
|
||||
id: param.id,
|
||||
key: param.key,
|
||||
value: $event,
|
||||
active: param.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<span>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="
|
||||
param.hasOwnProperty('active')
|
||||
? param.active
|
||||
? t('action.turn_off')
|
||||
: t('action.turn_on')
|
||||
: t('action.turn_off')
|
||||
"
|
||||
:svg="
|
||||
param.hasOwnProperty('active')
|
||||
? param.active
|
||||
? 'check-circle'
|
||||
: 'circle'
|
||||
: 'check-circle'
|
||||
"
|
||||
color="green"
|
||||
@click.native="
|
||||
updateParam(index, {
|
||||
id: param.id,
|
||||
key: param.key,
|
||||
value: param.value,
|
||||
active: param.hasOwnProperty('active')
|
||||
? !param.active
|
||||
: false,
|
||||
})
|
||||
"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="t('action.remove')"
|
||||
svg="trash"
|
||||
color="red"
|
||||
@click.native="deleteParam(index)"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</draggable>
|
||||
<div
|
||||
v-if="workingParams.length === 0"
|
||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
||||
>
|
||||
<img
|
||||
:src="`/images/states/${$colorMode.value}/add_files.svg`"
|
||||
loading="lazy"
|
||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
||||
:alt="`${t('empty.parameters')}`"
|
||||
/>
|
||||
<span class="pb-4 text-center">{{ t("empty.parameters") }}</span>
|
||||
<ButtonSecondary
|
||||
:label="`${t('add.new')}`"
|
||||
svg="plus"
|
||||
filled
|
||||
class="mb-4"
|
||||
@click.native="addParam"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Ref, ref, watch } from "@nuxtjs/composition-api"
|
||||
import { flow, pipe } from "fp-ts/function"
|
||||
import * as O from "fp-ts/Option"
|
||||
import * as A from "fp-ts/Array"
|
||||
import * as RA from "fp-ts/ReadonlyArray"
|
||||
import * as E from "fp-ts/Either"
|
||||
import {
|
||||
HoppRESTParam,
|
||||
parseRawKeyValueEntriesE,
|
||||
rawKeyValueEntriesToString,
|
||||
RawKeyValueEntry,
|
||||
} from "@hoppscotch/data"
|
||||
import isEqual from "lodash/isEqual"
|
||||
import cloneDeep from "lodash/cloneDeep"
|
||||
import draggable from "vuedraggable"
|
||||
import linter from "~/helpers/editor/linting/rawKeyValue"
|
||||
import { useCodemirror } from "~/helpers/editor/codemirror"
|
||||
import { useI18n, useToast, useStream } from "~/helpers/utils/composables"
|
||||
import { restParams$, setRESTParams } from "~/newstore/RESTSession"
|
||||
import { throwError } from "~/helpers/functional/error"
|
||||
import { objRemoveKey } from "~/helpers/functional/object"
|
||||
|
||||
const t = useI18n()
|
||||
const toast = useToast()
|
||||
|
||||
const idTicker = ref(0)
|
||||
|
||||
const bulkMode = ref(false)
|
||||
const bulkParams = ref("")
|
||||
const bulkEditor = ref<any | null>(null)
|
||||
|
||||
const deletionToast = ref<{ goAway: (delay: number) => void } | null>(null)
|
||||
|
||||
useCodemirror(bulkEditor, bulkParams, {
|
||||
extendedEditorConfig: {
|
||||
mode: "text/x-yaml",
|
||||
placeholder: `${t("state.bulk_mode_placeholder")}`,
|
||||
},
|
||||
linter,
|
||||
completer: null,
|
||||
environmentHighlights: true,
|
||||
})
|
||||
|
||||
// The functional parameters list (the parameters actually applied to the session)
|
||||
const params = useStream(restParams$, [], setRESTParams) as Ref<HoppRESTParam[]>
|
||||
|
||||
// The UI representation of the parameters list (has the empty end param)
|
||||
const workingParams = ref<Array<HoppRESTParam & { id: number }>>([
|
||||
{
|
||||
id: idTicker.value++,
|
||||
key: "",
|
||||
value: "",
|
||||
active: true,
|
||||
},
|
||||
])
|
||||
|
||||
// Rule: Working Params always have last element is always an empty param
|
||||
watch(workingParams, (paramsList) => {
|
||||
if (paramsList.length > 0 && paramsList[paramsList.length - 1].key !== "") {
|
||||
workingParams.value.push({
|
||||
id: idTicker.value++,
|
||||
key: "",
|
||||
value: "",
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Sync logic between params and working/bulk params
|
||||
watch(
|
||||
params,
|
||||
(newParamsList) => {
|
||||
// Sync should overwrite working params
|
||||
const filteredWorkingParams: HoppRESTParam[] = pipe(
|
||||
workingParams.value,
|
||||
A.filterMap(
|
||||
flow(
|
||||
O.fromPredicate((e) => e.key !== ""),
|
||||
O.map(objRemoveKey("id"))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
const filteredBulkParams = pipe(
|
||||
parseRawKeyValueEntriesE(bulkParams.value),
|
||||
E.map(
|
||||
flow(
|
||||
RA.filter((e) => e.key !== ""),
|
||||
RA.toArray
|
||||
)
|
||||
),
|
||||
E.getOrElse(() => [] as RawKeyValueEntry[])
|
||||
)
|
||||
|
||||
if (!isEqual(newParamsList, filteredWorkingParams)) {
|
||||
workingParams.value = pipe(
|
||||
newParamsList,
|
||||
A.map((x) => ({ id: idTicker.value++, ...x }))
|
||||
)
|
||||
}
|
||||
|
||||
if (!isEqual(newParamsList, filteredBulkParams)) {
|
||||
bulkParams.value = rawKeyValueEntriesToString(newParamsList)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
watch(workingParams, (newWorkingParams) => {
|
||||
const fixedParams = pipe(
|
||||
newWorkingParams,
|
||||
A.filterMap(
|
||||
flow(
|
||||
O.fromPredicate((e) => e.key !== ""),
|
||||
O.map(objRemoveKey("id"))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if (!isEqual(params.value, fixedParams)) {
|
||||
params.value = cloneDeep(fixedParams)
|
||||
}
|
||||
})
|
||||
|
||||
watch(bulkParams, (newBulkParams) => {
|
||||
const filteredBulkParams = pipe(
|
||||
parseRawKeyValueEntriesE(newBulkParams),
|
||||
E.map(
|
||||
flow(
|
||||
RA.filter((e) => e.key !== ""),
|
||||
RA.toArray
|
||||
)
|
||||
),
|
||||
E.getOrElse(() => [] as RawKeyValueEntry[])
|
||||
)
|
||||
|
||||
if (!isEqual(params.value, filteredBulkParams)) {
|
||||
params.value = filteredBulkParams
|
||||
}
|
||||
})
|
||||
|
||||
const addParam = () => {
|
||||
workingParams.value.push({
|
||||
id: idTicker.value++,
|
||||
key: "",
|
||||
value: "",
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
|
||||
const updateParam = (index: number, param: HoppRESTParam & { id: number }) => {
|
||||
workingParams.value = workingParams.value.map((h, i) =>
|
||||
i === index ? param : h
|
||||
)
|
||||
}
|
||||
|
||||
const deleteParam = (index: number) => {
|
||||
const paramsBeforeDeletion = cloneDeep(workingParams.value)
|
||||
|
||||
if (
|
||||
!(
|
||||
paramsBeforeDeletion.length > 0 &&
|
||||
index === paramsBeforeDeletion.length - 1
|
||||
)
|
||||
) {
|
||||
if (deletionToast.value) {
|
||||
deletionToast.value.goAway(0)
|
||||
deletionToast.value = null
|
||||
}
|
||||
|
||||
deletionToast.value = toast.success(`${t("state.deleted")}`, {
|
||||
action: [
|
||||
{
|
||||
text: `${t("action.undo")}`,
|
||||
onClick: (_, toastObject) => {
|
||||
workingParams.value = paramsBeforeDeletion
|
||||
toastObject.goAway(0)
|
||||
deletionToast.value = null
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
onComplete: () => {
|
||||
deletionToast.value = null
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
workingParams.value = pipe(
|
||||
workingParams.value,
|
||||
A.deleteAt(index),
|
||||
O.getOrElseW(() => throwError("Working Params Deletion Out of Bounds"))
|
||||
)
|
||||
}
|
||||
|
||||
const clearContent = () => {
|
||||
// set params list to the initial state
|
||||
workingParams.value = [
|
||||
{
|
||||
id: idTicker.value++,
|
||||
key: "",
|
||||
value: "",
|
||||
active: true,
|
||||
},
|
||||
]
|
||||
|
||||
bulkParams.value = ""
|
||||
}
|
||||
</script>
|
||||
@@ -348,8 +348,7 @@ const newSendRequest = async () => {
|
||||
const ensureMethodInEndpoint = () => {
|
||||
if (
|
||||
!/^http[s]?:\/\//.test(newEndpoint.value) &&
|
||||
!newEndpoint.value.startsWith("<<") &&
|
||||
!newEndpoint.value.startsWith("{{")
|
||||
!newEndpoint.value.startsWith("<<")
|
||||
) {
|
||||
const domain = newEndpoint.value.split(/[/:#?]+/)[0]
|
||||
if (domain === "localhost" || /([0-9]+\.)*[0-9]/.test(domain)) {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<SmartTab
|
||||
:id="'params'"
|
||||
:label="`${$t('tab.parameters')}`"
|
||||
:info="`${Number(newActiveParamsCount$) + Number(newActiveVarsCount$)}`"
|
||||
:info="`${newActiveParamsCount$}`"
|
||||
>
|
||||
<HttpParameters />
|
||||
</SmartTab>
|
||||
@@ -50,7 +50,6 @@ import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||
import {
|
||||
restActiveHeadersCount$,
|
||||
restActiveParamsCount$,
|
||||
restActiveVarsCount$,
|
||||
usePreRequestScript,
|
||||
useTestScript,
|
||||
} from "~/newstore/RESTSession"
|
||||
@@ -77,16 +76,6 @@ const newActiveParamsCount$ = useReadonlyStream(
|
||||
null
|
||||
)
|
||||
|
||||
const newActiveVarsCount$ = useReadonlyStream(
|
||||
restActiveVarsCount$.pipe(
|
||||
map((e) => {
|
||||
if (e === 0) return null
|
||||
return `${e}`
|
||||
})
|
||||
),
|
||||
null
|
||||
)
|
||||
|
||||
const newActiveHeadersCount$ = useReadonlyStream(
|
||||
restActiveHeadersCount$.pipe(
|
||||
map((e) => {
|
||||
|
||||
@@ -35,13 +35,10 @@ import { EditorState, Extension } from "@codemirror/state"
|
||||
import clone from "lodash/clone"
|
||||
import { tooltips } from "@codemirror/tooltip"
|
||||
import { history, historyKeymap } from "@codemirror/history"
|
||||
import { HoppRESTVar } from "@hoppscotch/data"
|
||||
import { inputTheme } from "~/helpers/editor/themes/baseTheme"
|
||||
import { HoppReactiveEnvPlugin } from "~/helpers/editor/extensions/HoppEnvironment"
|
||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||
import { AggregateEnvironment, aggregateEnvs$ } from "~/newstore/environments"
|
||||
import { HoppReactiveVarPlugin } from "~/helpers/editor/extensions/HoppVariable"
|
||||
import { restVars$ } from "~/newstore/RESTSession"
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@@ -49,7 +46,6 @@ const props = withDefaults(
|
||||
placeholder: string
|
||||
styles: string
|
||||
envs: { key: string; value: string; source: string }[] | null
|
||||
vars: { key: string; value: string }[] | null
|
||||
focus: boolean
|
||||
readonly: boolean
|
||||
}>(),
|
||||
@@ -58,7 +54,6 @@ const props = withDefaults(
|
||||
placeholder: "",
|
||||
styles: "",
|
||||
envs: null,
|
||||
vars: null,
|
||||
focus: false,
|
||||
readonly: false,
|
||||
}
|
||||
@@ -114,7 +109,6 @@ let pastedValue: string | null = null
|
||||
const aggregateEnvs = useReadonlyStream(aggregateEnvs$, []) as Ref<
|
||||
AggregateEnvironment[]
|
||||
>
|
||||
const aggregateVars = useReadonlyStream(restVars$, []) as Ref<HoppRESTVar[]>
|
||||
|
||||
const envVars = computed(() =>
|
||||
props.envs
|
||||
@@ -126,17 +120,7 @@ const envVars = computed(() =>
|
||||
: aggregateEnvs.value
|
||||
)
|
||||
|
||||
const varVars = computed(() =>
|
||||
props.vars
|
||||
? props.vars.map((x) => ({
|
||||
key: x.key,
|
||||
value: x.value,
|
||||
}))
|
||||
: aggregateVars.value
|
||||
)
|
||||
|
||||
const envTooltipPlugin = new HoppReactiveEnvPlugin(envVars, view)
|
||||
const varTooltipPlugin = new HoppReactiveVarPlugin(varVars, view)
|
||||
|
||||
const initView = (el: any) => {
|
||||
const extensions: Extension = [
|
||||
@@ -162,7 +146,6 @@ const initView = (el: any) => {
|
||||
position: "absolute",
|
||||
}),
|
||||
envTooltipPlugin,
|
||||
varTooltipPlugin,
|
||||
placeholderExt(props.placeholder),
|
||||
EditorView.domEventHandlers({
|
||||
paste(ev) {
|
||||
|
||||
@@ -221,7 +221,7 @@ export const runGQLSubscription = <
|
||||
createRequest(args.query, args.variables)
|
||||
)
|
||||
|
||||
const sub = wonkaPipe(
|
||||
wonkaPipe(
|
||||
source,
|
||||
subscribe((res) => {
|
||||
result$.next(
|
||||
@@ -256,8 +256,7 @@ export const runGQLSubscription = <
|
||||
})
|
||||
)
|
||||
|
||||
// Returns the stream and a subscription handle to unsub
|
||||
return [result$, sub] as const
|
||||
return result$
|
||||
}
|
||||
|
||||
export const useGQLQuery = <DocType, DocVarType, DocErrorType extends string>(
|
||||
|
||||
@@ -809,37 +809,6 @@ const samples = [
|
||||
testScript: "",
|
||||
}),
|
||||
},
|
||||
{
|
||||
command: `curl https://example.com -d "alpha=beta&request_id=4"`,
|
||||
response: makeRESTRequest({
|
||||
method: "POST",
|
||||
name: "Untitled request",
|
||||
endpoint: "https://example.com/",
|
||||
auth: {
|
||||
authType: "none",
|
||||
authActive: true,
|
||||
},
|
||||
body: {
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
body: rawKeyValueEntriesToString([
|
||||
{
|
||||
active: true,
|
||||
key: "alpha",
|
||||
value: "beta",
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
key: "request_id",
|
||||
value: "4",
|
||||
},
|
||||
]),
|
||||
},
|
||||
params: [],
|
||||
headers: [],
|
||||
preRequestScript: "",
|
||||
testScript: "",
|
||||
}),
|
||||
},
|
||||
]
|
||||
|
||||
describe("Parse curl command to Hopp REST Request", () => {
|
||||
|
||||
@@ -93,8 +93,7 @@ export const parseCurlCommand = (curlCommand: string) => {
|
||||
hasBodyBeenParsed = true
|
||||
} else if (
|
||||
rawContentType.includes("application/x-www-form-urlencoded") &&
|
||||
!!pairs &&
|
||||
Array.isArray(rawData)
|
||||
!!pairs
|
||||
) {
|
||||
body = pairs.map((p) => p.join(": ")).join("\n") || null
|
||||
contentType = "application/x-www-form-urlencoded"
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
getAggregateEnvs,
|
||||
} from "~/newstore/environments"
|
||||
|
||||
const HOPP_ENVIRONMENT_REGEX = /(<<[a-zA-Z0-9-_]+>>)/g
|
||||
const HOPP_ENVIRONMENT_REGEX = /(<<\w+>>)/g
|
||||
|
||||
const HOPP_ENV_HIGHLIGHT =
|
||||
"cursor-help transition rounded px-1 focus:outline-none mx-0.5 env-highlight"
|
||||
@@ -44,9 +44,8 @@ const cursorTooltipField = (aggregateEnvs: AggregateEnvironment[]) =>
|
||||
let start = pos
|
||||
let end = pos
|
||||
|
||||
while (start > from && /[a-zA-Z0-9-_]+/.test(text[start - from - 1]))
|
||||
start--
|
||||
while (end < to && /[a-zA-Z0-9-_]+/.test(text[end - from])) end++
|
||||
while (start > from && /\w/.test(text[start - from - 1])) start--
|
||||
while (end < to && /\w/.test(text[end - from])) end++
|
||||
|
||||
if (
|
||||
(start === pos && side < 0) ||
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
import { watch, Ref } from "@nuxtjs/composition-api"
|
||||
import { Compartment } from "@codemirror/state"
|
||||
import { hoverTooltip } from "@codemirror/tooltip"
|
||||
import {
|
||||
Decoration,
|
||||
EditorView,
|
||||
MatchDecorator,
|
||||
ViewPlugin,
|
||||
} from "@codemirror/view"
|
||||
import * as E from "fp-ts/Either"
|
||||
import { HoppRESTVar, parseTemplateStringE } from "@hoppscotch/data"
|
||||
|
||||
const HOPP_ENVIRONMENT_REGEX = /({{\w+}})/g
|
||||
|
||||
const HOPP_ENV_HIGHLIGHT =
|
||||
"cursor-help transition rounded px-1 focus:outline-none mx-0.5 env-highlight"
|
||||
const HOPP_ENV_HIGHLIGHT_FOUND =
|
||||
"bg-accentDark text-accentContrast hover:bg-accent"
|
||||
const HOPP_ENV_HIGHLIGHT_NOT_FOUND =
|
||||
"bg-red-500 text-accentContrast hover:bg-red-600"
|
||||
|
||||
const cursorTooltipField = (aggregateEnvs: HoppRESTVar[]) =>
|
||||
hoverTooltip(
|
||||
(view, pos, side) => {
|
||||
const { from, to, text } = view.state.doc.lineAt(pos)
|
||||
|
||||
// TODO: When Codemirror 6 allows this to work (not make the
|
||||
// popups appear half of the time) use this implementation
|
||||
// const wordSelection = view.state.wordAt(pos)
|
||||
// if (!wordSelection) return null
|
||||
// const word = view.state.doc.sliceString(
|
||||
// wordSelection.from - 2,
|
||||
// wordSelection.to + 2
|
||||
// )
|
||||
// if (!HOPP_ENVIRONMENT_REGEX.test(word)) return null
|
||||
|
||||
// Tracking the start and the end of the words
|
||||
let start = pos
|
||||
let end = pos
|
||||
|
||||
while (start > from && /\w/.test(text[start - from - 1])) start--
|
||||
while (end < to && /\w/.test(text[end - from])) end++
|
||||
|
||||
if (
|
||||
(start === pos && side < 0) ||
|
||||
(end === pos && side > 0) ||
|
||||
!HOPP_ENVIRONMENT_REGEX.test(
|
||||
text.slice(start - from - 2, end - from + 2)
|
||||
)
|
||||
)
|
||||
return null
|
||||
|
||||
const envValue =
|
||||
aggregateEnvs.find(
|
||||
(env) => env.key === text.slice(start - from, end - from)
|
||||
// env.key === word.slice(wordSelection.from + 2, wordSelection.to - 2)
|
||||
)?.value ?? "not found"
|
||||
|
||||
const result = parseTemplateStringE(envValue, aggregateEnvs)
|
||||
|
||||
const finalEnv = E.isLeft(result) ? "error" : result.right
|
||||
|
||||
return {
|
||||
pos: start,
|
||||
end: to,
|
||||
above: true,
|
||||
arrow: true,
|
||||
create() {
|
||||
const dom = document.createElement("span")
|
||||
const xmp = document.createElement("xmp")
|
||||
xmp.textContent = finalEnv
|
||||
dom.appendChild(xmp)
|
||||
dom.className = "tooltip-theme"
|
||||
return { dom }
|
||||
},
|
||||
}
|
||||
},
|
||||
// HACK: This is a hack to fix hover tooltip not coming half of the time
|
||||
// https://github.com/codemirror/tooltip/blob/765c463fc1d5afcc3ec93cee47d72606bed27e1d/src/tooltip.ts#L622
|
||||
// Still doesn't fix the not showing up some of the time issue, but this is atleast more consistent
|
||||
{ hoverTime: 1 } as any
|
||||
)
|
||||
|
||||
function checkEnv(env: string, aggregateEnvs: HoppRESTVar[]) {
|
||||
const className = aggregateEnvs.find(
|
||||
(k: { key: string }) => k.key === env.slice(2, -2)
|
||||
)
|
||||
? HOPP_ENV_HIGHLIGHT_FOUND
|
||||
: HOPP_ENV_HIGHLIGHT_NOT_FOUND
|
||||
|
||||
return Decoration.mark({
|
||||
class: `${HOPP_ENV_HIGHLIGHT} ${className}`,
|
||||
})
|
||||
}
|
||||
|
||||
const getMatchDecorator = (aggregateEnvs: HoppRESTVar[]) =>
|
||||
new MatchDecorator({
|
||||
regexp: HOPP_ENVIRONMENT_REGEX,
|
||||
decoration: (m) => checkEnv(m[0], aggregateEnvs),
|
||||
})
|
||||
|
||||
export const environmentHighlightStyle = (aggregateEnvs: HoppRESTVar[]) => {
|
||||
const decorator = getMatchDecorator(aggregateEnvs)
|
||||
|
||||
return ViewPlugin.define(
|
||||
(view) => ({
|
||||
decorations: decorator.createDeco(view),
|
||||
update(u) {
|
||||
this.decorations = decorator.updateDeco(u, this.decorations)
|
||||
},
|
||||
}),
|
||||
{
|
||||
decorations: (v) => v.decorations,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export class HoppReactiveVarPlugin {
|
||||
private compartment = new Compartment()
|
||||
|
||||
private envs: HoppRESTVar[] = []
|
||||
|
||||
constructor(
|
||||
envsRef: Ref<HoppRESTVar[]>,
|
||||
private editorView: Ref<EditorView | undefined>
|
||||
) {
|
||||
watch(
|
||||
envsRef,
|
||||
(envs) => {
|
||||
this.envs = envs
|
||||
|
||||
this.editorView.value?.dispatch({
|
||||
effects: this.compartment.reconfigure([
|
||||
cursorTooltipField(this.envs),
|
||||
environmentHighlightStyle(this.envs),
|
||||
]),
|
||||
})
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
}
|
||||
|
||||
get extension() {
|
||||
return this.compartment.of([
|
||||
cursorTooltipField(this.envs),
|
||||
environmentHighlightStyle(this.envs),
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -61,8 +61,6 @@ export const baseTheme = EditorView.theme({
|
||||
},
|
||||
".cm-panels.cm-panels-top": {
|
||||
borderBottom: "1px solid var(--divider-light-color)",
|
||||
top: "var(--lower-tertiary-sticky-fold) !important",
|
||||
"z-index": "10",
|
||||
},
|
||||
".cm-panels.cm-panels-bottom": {
|
||||
borderTop: "1px solid var(--divider-light-color)",
|
||||
@@ -390,7 +388,5 @@ export const basicSetup: Extension = [
|
||||
...completionKeymap,
|
||||
...lintKeymap,
|
||||
]),
|
||||
search({
|
||||
top: true,
|
||||
}),
|
||||
search(),
|
||||
]
|
||||
|
||||
@@ -9,6 +9,14 @@ import { flow } from "fp-ts/function"
|
||||
export const safeParseJSON = (str: string): O.Option<object> =>
|
||||
O.tryCatch(() => JSON.parse(str))
|
||||
|
||||
/**
|
||||
* Generates a prettified JSON representation of an object
|
||||
* @param obj The object to get the representation of
|
||||
* @returns The prettified JSON string of the object
|
||||
*/
|
||||
export const prettyPrintJSON = (obj: unknown): O.Option<string> =>
|
||||
O.tryCatch(() => JSON.stringify(obj, null, "\t"))
|
||||
|
||||
/**
|
||||
* Checks if given string is a JSON string
|
||||
* @param str Raw string to be checked
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
/**
|
||||
* Converts an array of key-value tuples (for e.g ["key", "value"]), into a record.
|
||||
* (for eg. output -> { "key": "value" })
|
||||
* NOTE: This function will discard duplicate key occurances and only keep the last occurance. If you do not want that behaviour,
|
||||
* use `tupleWithSamesKeysToRecord`.
|
||||
* @param tuples Array of tuples ([key, value])
|
||||
* @returns A record with value corresponding to the last occurance of that key
|
||||
*/
|
||||
export const tupleToRecord = <
|
||||
KeyType extends string | number | symbol,
|
||||
ValueType
|
||||
@@ -13,32 +5,5 @@ export const tupleToRecord = <
|
||||
tuples: [KeyType, ValueType][]
|
||||
): Record<KeyType, ValueType> =>
|
||||
tuples.length > 0
|
||||
? (Object.assign as any)(...tuples.map(([key, val]) => ({ [key]: val }))) // This is technically valid, but we have no way of telling TypeScript it is valid. Hence the assertion
|
||||
? (Object.assign as any)(...tuples.map(([key, val]) => ({ [key]: val })))
|
||||
: {}
|
||||
|
||||
/**
|
||||
* Converts an array of key-value tuples (for e.g ["key", "value"]), into a record.
|
||||
* (for eg. output -> { "key": ["value"] })
|
||||
* NOTE: If you do not want the array as values (because of duplicate keys) and want to instead get the last occurance, use `tupleToRecord`
|
||||
* @param tuples Array of tuples ([key, value])
|
||||
* @returns A Record with values being arrays corresponding to each key occurance
|
||||
*/
|
||||
export const tupleWithSameKeysToRecord = <
|
||||
KeyType extends string | number | symbol,
|
||||
ValueType
|
||||
>(
|
||||
tuples: [KeyType, ValueType][]
|
||||
): Record<KeyType, ValueType[]> => {
|
||||
// By the end of the function we do ensure this typing, this can't be infered now though, hence the assertion
|
||||
const out = {} as Record<KeyType, ValueType[]>
|
||||
|
||||
for (const [key, value] of tuples) {
|
||||
if (!out[key]) {
|
||||
out[key] = [value]
|
||||
} else {
|
||||
out[key].push(value)
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
import { OpenAPIV2 } from "openapi-types"
|
||||
import * as O from "fp-ts/Option"
|
||||
import { pipe, flow } from "fp-ts/function"
|
||||
import * as A from "fp-ts/Array"
|
||||
import { prettyPrintJSON } from "~/helpers/functional/json"
|
||||
|
||||
type PrimitiveSchemaType = "string" | "integer" | "number" | "boolean"
|
||||
|
||||
type SchemaType = "array" | "object" | PrimitiveSchemaType
|
||||
|
||||
type PrimitiveRequestBodyExample = number | string | boolean
|
||||
|
||||
type RequestBodyExample =
|
||||
| { [name: string]: RequestBodyExample }
|
||||
| Array<RequestBodyExample>
|
||||
| PrimitiveRequestBodyExample
|
||||
|
||||
const getPrimitiveTypePlaceholder = (
|
||||
schemaType: PrimitiveSchemaType
|
||||
): PrimitiveRequestBodyExample => {
|
||||
switch (schemaType) {
|
||||
case "string":
|
||||
return "string"
|
||||
case "integer":
|
||||
case "number":
|
||||
return 1
|
||||
case "boolean":
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const getSchemaTypeFromSchemaObject = (
|
||||
schema: OpenAPIV2.SchemaObject
|
||||
): O.Option<SchemaType> =>
|
||||
pipe(
|
||||
schema.type,
|
||||
O.fromNullable,
|
||||
O.map(
|
||||
(schemaType) =>
|
||||
(Array.isArray(schemaType) ? schemaType[0] : schemaType) as SchemaType
|
||||
)
|
||||
)
|
||||
|
||||
const isSchemaTypePrimitive = (
|
||||
schemaType: string
|
||||
): schemaType is PrimitiveSchemaType =>
|
||||
["string", "integer", "number", "boolean"].includes(schemaType)
|
||||
|
||||
const isSchemaTypeArray = (schemaType: string): schemaType is "array" =>
|
||||
schemaType === "array"
|
||||
|
||||
const isSchemaTypeObject = (schemaType: string): schemaType is "object" =>
|
||||
schemaType === "object"
|
||||
|
||||
const getSampleEnumValueOrPlaceholder = (
|
||||
schema: OpenAPIV2.SchemaObject
|
||||
): RequestBodyExample =>
|
||||
pipe(
|
||||
schema.enum,
|
||||
O.fromNullable,
|
||||
O.map((enums) => enums[0] as RequestBodyExample),
|
||||
O.altW(() =>
|
||||
pipe(
|
||||
schema,
|
||||
getSchemaTypeFromSchemaObject,
|
||||
O.filter(isSchemaTypePrimitive),
|
||||
O.map(getPrimitiveTypePlaceholder)
|
||||
)
|
||||
),
|
||||
O.getOrElseW(() => "")
|
||||
)
|
||||
|
||||
const generateExampleArrayFromOpenAPIV2ItemsObject = (
|
||||
items: OpenAPIV2.ItemsObject
|
||||
): RequestBodyExample =>
|
||||
// ItemsObject can not hold type "object"
|
||||
// https://swagger.io/specification/v2/#itemsObject
|
||||
|
||||
// TODO : Handle array of objects
|
||||
// https://stackoverflow.com/questions/60490974/how-to-define-an-array-of-objects-in-openapi-2-0
|
||||
|
||||
pipe(
|
||||
items,
|
||||
O.fromPredicate(
|
||||
flow((items) => items.type as SchemaType, isSchemaTypePrimitive)
|
||||
),
|
||||
O.map(
|
||||
flow(getSampleEnumValueOrPlaceholder, (arrayItem) => [
|
||||
arrayItem,
|
||||
arrayItem,
|
||||
])
|
||||
),
|
||||
O.getOrElse(() =>
|
||||
// If the type is not primitive, it is "array"
|
||||
// items property is required if type is array
|
||||
[
|
||||
generateExampleArrayFromOpenAPIV2ItemsObject(
|
||||
items.items as OpenAPIV2.ItemsObject
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
const generateRequestBodyExampleFromOpenAPIV2BodySchema = (
|
||||
schema: OpenAPIV2.SchemaObject
|
||||
): RequestBodyExample => {
|
||||
if (schema.example) return schema.example as RequestBodyExample
|
||||
|
||||
const primitiveTypeExample = pipe(
|
||||
schema,
|
||||
O.fromPredicate(
|
||||
flow(
|
||||
getSchemaTypeFromSchemaObject,
|
||||
O.map(isSchemaTypePrimitive),
|
||||
O.getOrElseW(() => false) // No schema type found in the schema object, assume non-primitive
|
||||
)
|
||||
),
|
||||
O.map(getSampleEnumValueOrPlaceholder) // Use enum or placeholder to populate primitive field
|
||||
)
|
||||
|
||||
if (O.isSome(primitiveTypeExample)) return primitiveTypeExample.value
|
||||
|
||||
const arrayTypeExample = pipe(
|
||||
schema,
|
||||
O.fromPredicate(
|
||||
flow(
|
||||
getSchemaTypeFromSchemaObject,
|
||||
O.map(isSchemaTypeArray),
|
||||
O.getOrElseW(() => false) // No schema type found in the schema object, assume type to be different from array
|
||||
)
|
||||
),
|
||||
O.map((schema) => schema.items as OpenAPIV2.ItemsObject),
|
||||
O.map(generateExampleArrayFromOpenAPIV2ItemsObject)
|
||||
)
|
||||
|
||||
if (O.isSome(arrayTypeExample)) return arrayTypeExample.value
|
||||
|
||||
return pipe(
|
||||
schema,
|
||||
O.fromPredicate(
|
||||
flow(
|
||||
getSchemaTypeFromSchemaObject,
|
||||
O.map(isSchemaTypeObject),
|
||||
O.getOrElseW(() => false)
|
||||
)
|
||||
),
|
||||
O.chain((schema) =>
|
||||
pipe(
|
||||
schema.properties,
|
||||
O.fromNullable,
|
||||
O.map(
|
||||
(properties) =>
|
||||
Object.entries(properties) as [string, OpenAPIV2.SchemaObject][]
|
||||
)
|
||||
)
|
||||
),
|
||||
O.getOrElseW(() => [] as [string, OpenAPIV2.SchemaObject][]),
|
||||
A.reduce(
|
||||
{} as { [name: string]: RequestBodyExample },
|
||||
(aggregatedExample, property) => {
|
||||
const example = generateRequestBodyExampleFromOpenAPIV2BodySchema(
|
||||
property[1]
|
||||
)
|
||||
aggregatedExample[property[0]] = example
|
||||
return aggregatedExample
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export const generateRequestBodyExampleFromOpenAPIV2Body = (
|
||||
op: OpenAPIV2.OperationObject
|
||||
): string =>
|
||||
pipe(
|
||||
(op.parameters ?? []) as OpenAPIV2.Parameter[],
|
||||
A.findFirst((param) => param.in === "body"),
|
||||
O.map(
|
||||
flow(
|
||||
(parameter) => parameter.schema,
|
||||
generateRequestBodyExampleFromOpenAPIV2BodySchema
|
||||
)
|
||||
),
|
||||
O.chain(prettyPrintJSON),
|
||||
O.getOrElse(() => "")
|
||||
)
|
||||
@@ -0,0 +1,109 @@
|
||||
import { OpenAPIV3 } from "openapi-types"
|
||||
import { pipe } from "fp-ts/function"
|
||||
import * as O from "fp-ts/Option"
|
||||
import { tupleToRecord } from "~/helpers/functional/record"
|
||||
|
||||
type SchemaType =
|
||||
| OpenAPIV3.ArraySchemaObjectType
|
||||
| OpenAPIV3.NonArraySchemaObjectType
|
||||
|
||||
type PrimitiveSchemaType = Exclude<SchemaType, "array" | "object">
|
||||
|
||||
type PrimitiveRequestBodyExample = string | number | boolean | null
|
||||
|
||||
type RequestBodyExample =
|
||||
| PrimitiveRequestBodyExample
|
||||
| Array<RequestBodyExample>
|
||||
| { [name: string]: RequestBodyExample }
|
||||
|
||||
const isSchemaTypePrimitive = (
|
||||
schemaType: SchemaType
|
||||
): schemaType is PrimitiveSchemaType =>
|
||||
!["array", "object"].includes(schemaType)
|
||||
|
||||
const getPrimitiveTypePlaceholder = (
|
||||
primitiveType: PrimitiveSchemaType
|
||||
): PrimitiveRequestBodyExample => {
|
||||
switch (primitiveType) {
|
||||
case "number":
|
||||
return 0.0
|
||||
case "integer":
|
||||
return 0
|
||||
case "string":
|
||||
return "string"
|
||||
case "boolean":
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Use carefully, call only when type is primitive
|
||||
// TODO(agarwal): Use Enum values, if any
|
||||
const generatePrimitiveRequestBodyExample = (
|
||||
schemaObject: OpenAPIV3.NonArraySchemaObject
|
||||
): RequestBodyExample =>
|
||||
getPrimitiveTypePlaceholder(schemaObject.type as PrimitiveSchemaType)
|
||||
|
||||
// Use carefully, call only when type is object
|
||||
const generateObjectRequestBodyExample = (
|
||||
schemaObject: OpenAPIV3.NonArraySchemaObject
|
||||
): RequestBodyExample =>
|
||||
pipe(
|
||||
schemaObject.properties,
|
||||
O.fromNullable,
|
||||
O.map(Object.entries),
|
||||
O.getOrElseW(() => [] as [string, OpenAPIV3.SchemaObject][]),
|
||||
tupleToRecord
|
||||
)
|
||||
|
||||
const generateArrayRequestBodyExample = (
|
||||
schemaObject: OpenAPIV3.ArraySchemaObject
|
||||
): RequestBodyExample => [
|
||||
generateRequestBodyExampleFromSchemaObject(
|
||||
schemaObject.items as OpenAPIV3.SchemaObject
|
||||
),
|
||||
]
|
||||
|
||||
const generateRequestBodyExampleFromSchemaObject = (
|
||||
schemaObject: OpenAPIV3.SchemaObject
|
||||
): RequestBodyExample => {
|
||||
// TODO: Handle schema objects with allof
|
||||
if (schemaObject.example) return schemaObject.example as RequestBodyExample
|
||||
|
||||
// If request body can be oneof or allof several schema, choose the first schema to generate an example
|
||||
if (schemaObject.oneOf)
|
||||
return generateRequestBodyExampleFromSchemaObject(
|
||||
schemaObject.oneOf[0] as OpenAPIV3.SchemaObject
|
||||
)
|
||||
if (schemaObject.anyOf)
|
||||
return generateRequestBodyExampleFromSchemaObject(
|
||||
schemaObject.anyOf[0] as OpenAPIV3.SchemaObject
|
||||
)
|
||||
|
||||
if (!schemaObject.type) return ""
|
||||
|
||||
if (isSchemaTypePrimitive(schemaObject.type))
|
||||
return generatePrimitiveRequestBodyExample(
|
||||
schemaObject as OpenAPIV3.NonArraySchemaObject
|
||||
)
|
||||
|
||||
if (schemaObject.type === "object")
|
||||
return generateObjectRequestBodyExample(
|
||||
schemaObject as OpenAPIV3.NonArraySchemaObject
|
||||
)
|
||||
|
||||
return generateArrayRequestBodyExample(
|
||||
schemaObject as OpenAPIV3.ArraySchemaObject
|
||||
)
|
||||
}
|
||||
|
||||
export const generateRequestBodyExampleFromMediaObject = (
|
||||
mediaObject: OpenAPIV3.MediaTypeObject
|
||||
): RequestBodyExample => {
|
||||
if (mediaObject.example) return mediaObject.example as RequestBodyExample
|
||||
if (mediaObject.examples) return mediaObject.examples[0] as RequestBodyExample
|
||||
return mediaObject.schema
|
||||
? generateRequestBodyExampleFromSchemaObject(
|
||||
mediaObject.schema as OpenAPIV3.SchemaObject
|
||||
)
|
||||
: ""
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
import { OpenAPIV3_1 as OpenAPIV31 } from "openapi-types"
|
||||
import { pipe } from "fp-ts/function"
|
||||
import * as O from "fp-ts/Option"
|
||||
import * as A from "fp-ts/Array"
|
||||
|
||||
type MixedArraySchemaType = (
|
||||
| OpenAPIV31.ArraySchemaObjectType
|
||||
| OpenAPIV31.NonArraySchemaObjectType
|
||||
)[]
|
||||
|
||||
type SchemaType =
|
||||
| OpenAPIV31.ArraySchemaObjectType
|
||||
| OpenAPIV31.NonArraySchemaObjectType
|
||||
| MixedArraySchemaType
|
||||
|
||||
type PrimitiveSchemaType = Exclude<
|
||||
OpenAPIV31.NonArraySchemaObjectType,
|
||||
"object"
|
||||
>
|
||||
|
||||
type PrimitiveRequestBodyExample = string | number | boolean | null
|
||||
|
||||
type RequestBodyExample =
|
||||
| PrimitiveRequestBodyExample
|
||||
| Array<RequestBodyExample>
|
||||
| { [name: string]: RequestBodyExample }
|
||||
|
||||
const isSchemaTypePrimitive = (
|
||||
schemaType: SchemaType
|
||||
): schemaType is PrimitiveSchemaType =>
|
||||
!Array.isArray(schemaType) && !["array", "object"].includes(schemaType)
|
||||
|
||||
const getPrimitiveTypePlaceholder = (
|
||||
primitiveType: PrimitiveSchemaType
|
||||
): PrimitiveRequestBodyExample => {
|
||||
switch (primitiveType) {
|
||||
case "number":
|
||||
return 0.0
|
||||
case "integer":
|
||||
return 0
|
||||
case "string":
|
||||
return "string"
|
||||
case "boolean":
|
||||
return true
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// Use carefully, the schema type should necessarily be primitive
|
||||
// TODO(agarwal): Use Enum values, if any
|
||||
const generatePrimitiveRequestBodyExample = (
|
||||
schemaObject: OpenAPIV31.NonArraySchemaObject
|
||||
): RequestBodyExample =>
|
||||
getPrimitiveTypePlaceholder(schemaObject.type as PrimitiveSchemaType)
|
||||
|
||||
// Use carefully, the schema type should necessarily be object
|
||||
const generateObjectRequestBodyExample = (
|
||||
schemaObject: OpenAPIV31.NonArraySchemaObject
|
||||
): RequestBodyExample =>
|
||||
pipe(
|
||||
schemaObject.properties,
|
||||
O.fromNullable,
|
||||
O.map(
|
||||
(properties) =>
|
||||
Object.entries(properties) as [string, OpenAPIV31.SchemaObject][]
|
||||
),
|
||||
O.getOrElseW(() => [] as [string, OpenAPIV31.SchemaObject][]),
|
||||
A.reduce(
|
||||
{} as { [name: string]: RequestBodyExample },
|
||||
(aggregatedExample, property) => {
|
||||
aggregatedExample[property[0]] =
|
||||
generateRequestBodyExampleFromSchemaObject(property[1])
|
||||
return aggregatedExample
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
// Use carefully, the schema type should necessarily be mixed array
|
||||
const generateMixedArrayRequestBodyEcample = (
|
||||
schemaObject: OpenAPIV31.SchemaObject
|
||||
): RequestBodyExample =>
|
||||
pipe(
|
||||
schemaObject,
|
||||
(schemaObject) => schemaObject.type as MixedArraySchemaType,
|
||||
A.reduce([] as Array<RequestBodyExample>, (aggregatedExample, itemType) => {
|
||||
// TODO: Figure out how to include non-primitive types as well
|
||||
if (isSchemaTypePrimitive(itemType)) {
|
||||
aggregatedExample.push(getPrimitiveTypePlaceholder(itemType))
|
||||
}
|
||||
return aggregatedExample
|
||||
})
|
||||
)
|
||||
|
||||
const generateArrayRequestBodyExample = (
|
||||
schemaObject: OpenAPIV31.ArraySchemaObject
|
||||
): RequestBodyExample => [
|
||||
generateRequestBodyExampleFromSchemaObject(
|
||||
schemaObject.items as OpenAPIV31.SchemaObject
|
||||
),
|
||||
]
|
||||
|
||||
const generateRequestBodyExampleFromSchemaObject = (
|
||||
schemaObject: OpenAPIV31.SchemaObject
|
||||
): RequestBodyExample => {
|
||||
// TODO: Handle schema objects with oneof or anyof
|
||||
if (schemaObject.example) return schemaObject.example as RequestBodyExample
|
||||
if (schemaObject.examples)
|
||||
return schemaObject.examples[0] as RequestBodyExample
|
||||
if (!schemaObject.type) return ""
|
||||
if (isSchemaTypePrimitive(schemaObject.type))
|
||||
return generatePrimitiveRequestBodyExample(
|
||||
schemaObject as OpenAPIV31.NonArraySchemaObject
|
||||
)
|
||||
if (schemaObject.type === "object")
|
||||
return generateObjectRequestBodyExample(schemaObject)
|
||||
if (schemaObject.type === "array")
|
||||
return generateArrayRequestBodyExample(schemaObject)
|
||||
return generateMixedArrayRequestBodyEcample(schemaObject)
|
||||
}
|
||||
|
||||
export const generateRequestBodyExampleFromMediaObject = (
|
||||
mediaObject: OpenAPIV31.MediaTypeObject
|
||||
): RequestBodyExample => {
|
||||
if (mediaObject.example) return mediaObject.example as RequestBodyExample
|
||||
if (mediaObject.examples) return mediaObject.examples[0] as RequestBodyExample
|
||||
return mediaObject.schema
|
||||
? generateRequestBodyExampleFromSchemaObject(mediaObject.schema)
|
||||
: ""
|
||||
}
|
||||
@@ -24,8 +24,12 @@ import * as S from "fp-ts/string"
|
||||
import * as O from "fp-ts/Option"
|
||||
import * as TE from "fp-ts/TaskEither"
|
||||
import * as RA from "fp-ts/ReadonlyArray"
|
||||
import { step } from "../steps"
|
||||
import { defineImporter, IMPORTER_INVALID_FILE_FORMAT } from "."
|
||||
import { step } from "../../steps"
|
||||
import { defineImporter, IMPORTER_INVALID_FILE_FORMAT } from "../"
|
||||
import { generateRequestBodyExampleFromMediaObject as generateExampleV31 } from "./exampleV31"
|
||||
import { generateRequestBodyExampleFromMediaObject as generateExampleV3 } from "./exampleV3"
|
||||
import { generateRequestBodyExampleFromOpenAPIV2Body } from "./exampleV2"
|
||||
import { prettyPrintJSON } from "~/helpers/functional/json"
|
||||
|
||||
export const OPENAPI_DEREF_ERROR = "openapi/deref_error" as const
|
||||
|
||||
@@ -114,8 +118,12 @@ const parseOpenAPIV2Body = (op: OpenAPIV2.OperationObject): HoppRESTReqBody => {
|
||||
if (
|
||||
obj !== "multipart/form-data" &&
|
||||
obj !== "application/x-www-form-urlencoded"
|
||||
)
|
||||
return { contentType: obj as any, body: "" }
|
||||
) {
|
||||
return {
|
||||
contentType: obj as any,
|
||||
body: generateRequestBodyExampleFromOpenAPIV2Body(op),
|
||||
}
|
||||
}
|
||||
|
||||
const formDataValues = pipe(
|
||||
(op.parameters ?? []) as OpenAPIV2.Parameter[],
|
||||
@@ -178,7 +186,8 @@ const parseOpenAPIV3BodyFormData = (
|
||||
}
|
||||
|
||||
const parseOpenAPIV3Body = (
|
||||
op: OpenAPIV3.OperationObject | OpenAPIV31.OperationObject
|
||||
op: OpenAPIV3.OperationObject | OpenAPIV31.OperationObject,
|
||||
isV31Request: boolean
|
||||
): HoppRESTReqBody => {
|
||||
const objs = Object.entries(
|
||||
(
|
||||
@@ -197,11 +206,20 @@ const parseOpenAPIV3Body = (
|
||||
OpenAPIV3.MediaTypeObject | OpenAPIV31.MediaTypeObject
|
||||
] = objs[0]
|
||||
|
||||
const exampleBody = pipe(
|
||||
prettyPrintJSON(
|
||||
isV31Request
|
||||
? generateExampleV31(media as OpenAPIV31.MediaTypeObject)
|
||||
: generateExampleV3(media as OpenAPIV3.MediaTypeObject)
|
||||
),
|
||||
O.getOrElse(() => "")
|
||||
)
|
||||
|
||||
return contentType in knownContentTypes
|
||||
? contentType === "multipart/form-data" ||
|
||||
contentType === "application/x-www-form-urlencoded"
|
||||
? parseOpenAPIV3BodyFormData(contentType, media)
|
||||
: { contentType: contentType as any, body: "" }
|
||||
: { contentType: contentType as any, body: exampleBody }
|
||||
: { contentType: null, body: null }
|
||||
}
|
||||
|
||||
@@ -213,12 +231,20 @@ const isOpenAPIV3Operation = (
|
||||
typeof doc.openapi === "string" &&
|
||||
doc.openapi.startsWith("3.")
|
||||
|
||||
const isOpenAPIV31Operation = (
|
||||
doc: OpenAPI.Document,
|
||||
op: OpenAPIOperationType
|
||||
): op is OpenAPIV31.OperationObject =>
|
||||
objectHasProperty(doc, "openapi") &&
|
||||
typeof doc.openapi === "string" &&
|
||||
doc.openapi.startsWith("3.1")
|
||||
|
||||
const parseOpenAPIBody = (
|
||||
doc: OpenAPI.Document,
|
||||
op: OpenAPIOperationType
|
||||
): HoppRESTReqBody =>
|
||||
isOpenAPIV3Operation(doc, op)
|
||||
? parseOpenAPIV3Body(op)
|
||||
? parseOpenAPIV3Body(op, isOpenAPIV31Operation(doc, op))
|
||||
: parseOpenAPIV2Body(op)
|
||||
|
||||
const resolveOpenAPIV3SecurityObj = (
|
||||
@@ -56,7 +56,6 @@ export const bindings: {
|
||||
"alt-q": "navigation.jump.graphql",
|
||||
"alt-w": "navigation.jump.realtime",
|
||||
"alt-d": "navigation.jump.documentation",
|
||||
"alt-m": "navigation.jump.profile",
|
||||
"alt-s": "navigation.jump.settings",
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as E from "fp-ts/Either"
|
||||
import { BehaviorSubject, Subscription } from "rxjs"
|
||||
import { Subscription as WSubscription } from "wonka"
|
||||
import { GQLError, runGQLQuery, runGQLSubscription } from "../backend/GQLClient"
|
||||
import {
|
||||
GetUserShortcodesQuery,
|
||||
@@ -23,9 +22,6 @@ export default class ShortcodeListAdapter {
|
||||
private myShortcodesCreated: Subscription | null
|
||||
private myShortcodesRevoked: Subscription | null
|
||||
|
||||
private myShortcodesCreatedSub: WSubscription | null
|
||||
private myShortcodesRevokedSub: WSubscription | null
|
||||
|
||||
constructor(deferInit: boolean = false) {
|
||||
this.error$ = new BehaviorSubject<GQLError<string> | null>(null)
|
||||
this.loading$ = new BehaviorSubject<boolean>(false)
|
||||
@@ -37,8 +33,6 @@ export default class ShortcodeListAdapter {
|
||||
this.isDispose = false
|
||||
this.myShortcodesCreated = null
|
||||
this.myShortcodesRevoked = null
|
||||
this.myShortcodesCreatedSub = null
|
||||
this.myShortcodesRevokedSub = null
|
||||
|
||||
if (!deferInit) this.initialize()
|
||||
}
|
||||
@@ -46,8 +40,6 @@ export default class ShortcodeListAdapter {
|
||||
unsubscribeSubscriptions() {
|
||||
this.myShortcodesCreated?.unsubscribe()
|
||||
this.myShortcodesRevoked?.unsubscribe()
|
||||
this.myShortcodesCreatedSub?.unsubscribe()
|
||||
this.myShortcodesRevokedSub?.unsubscribe()
|
||||
}
|
||||
|
||||
initialize() {
|
||||
@@ -132,12 +124,9 @@ export default class ShortcodeListAdapter {
|
||||
}
|
||||
|
||||
private registerSubscriptions() {
|
||||
const [myShortcodeCreated$, myShortcodeCreatedSub] = runGQLSubscription({
|
||||
this.myShortcodesCreated = runGQLSubscription({
|
||||
query: ShortcodeCreatedDocument,
|
||||
})
|
||||
|
||||
this.myShortcodesCreatedSub = myShortcodeCreatedSub
|
||||
this.myShortcodesCreated = myShortcodeCreated$.subscribe((result) => {
|
||||
}).subscribe((result) => {
|
||||
if (E.isLeft(result)) {
|
||||
console.error(result.left)
|
||||
throw new Error(`Shortcode Create Error ${result.left}`)
|
||||
@@ -146,12 +135,9 @@ export default class ShortcodeListAdapter {
|
||||
this.createShortcode(result.right.myShortcodesCreated)
|
||||
})
|
||||
|
||||
const [myShortcodesRevoked$, myShortcodeRevokedSub] = runGQLSubscription({
|
||||
this.myShortcodesRevoked = runGQLSubscription({
|
||||
query: ShortcodeDeletedDocument,
|
||||
})
|
||||
|
||||
this.myShortcodesRevokedSub = myShortcodeRevokedSub
|
||||
this.myShortcodesRevoked = myShortcodesRevoked$.subscribe((result) => {
|
||||
}).subscribe((result) => {
|
||||
if (E.isLeft(result)) {
|
||||
console.error(result.left)
|
||||
throw new Error(`Shortcode Delete Error ${result.left}`)
|
||||
|
||||
@@ -103,7 +103,7 @@ export default [
|
||||
label: "shortcut.navigation.settings",
|
||||
},
|
||||
{
|
||||
keys: [getPlatformAlternateKey(), "M"],
|
||||
keys: [getPlatformAlternateKey(), "P"],
|
||||
label: "shortcut.navigation.profile",
|
||||
},
|
||||
],
|
||||
@@ -171,7 +171,7 @@ export const spotlight = [
|
||||
icon: "arrow-right",
|
||||
},
|
||||
{
|
||||
keys: [getPlatformAlternateKey(), "M"],
|
||||
keys: [getPlatformAlternateKey(), "P"],
|
||||
label: "shortcut.navigation.profile",
|
||||
action: "navigation.jump.profile",
|
||||
icon: "arrow-right",
|
||||
@@ -267,7 +267,7 @@ export const fuse = [
|
||||
tags: ["settings", "jump", "page", "navigation", "account", "theme", "go"],
|
||||
},
|
||||
{
|
||||
keys: [getPlatformAlternateKey(), "M"],
|
||||
keys: [getPlatformAlternateKey(), "P"],
|
||||
label: "shortcut.navigation.profile",
|
||||
action: "navigation.jump.profile",
|
||||
icon: "arrow-right",
|
||||
|
||||
@@ -3,7 +3,6 @@ import { BehaviorSubject, Subscription } from "rxjs"
|
||||
import { translateToNewRequest } from "@hoppscotch/data"
|
||||
import pull from "lodash/pull"
|
||||
import remove from "lodash/remove"
|
||||
import { Subscription as WSubscription } from "wonka"
|
||||
import { runGQLQuery, runGQLSubscription } from "../backend/GQLClient"
|
||||
import { TeamCollection } from "./TeamCollection"
|
||||
import { TeamRequest } from "./TeamRequest"
|
||||
@@ -194,13 +193,6 @@ export default class NewTeamCollectionAdapter {
|
||||
private teamRequestUpdated$: Subscription | null
|
||||
private teamRequestDeleted$: Subscription | null
|
||||
|
||||
private teamCollectionAddedSub: WSubscription | null
|
||||
private teamCollectionUpdatedSub: WSubscription | null
|
||||
private teamCollectionRemovedSub: WSubscription | null
|
||||
private teamRequestAddedSub: WSubscription | null
|
||||
private teamRequestUpdatedSub: WSubscription | null
|
||||
private teamRequestDeletedSub: WSubscription | null
|
||||
|
||||
constructor(private teamID: string | null) {
|
||||
this.collections$ = new BehaviorSubject<TeamCollection[]>([])
|
||||
this.loadingCollections$ = new BehaviorSubject<string[]>([])
|
||||
@@ -212,13 +204,6 @@ export default class NewTeamCollectionAdapter {
|
||||
this.teamRequestDeleted$ = null
|
||||
this.teamRequestUpdated$ = null
|
||||
|
||||
this.teamCollectionAddedSub = null
|
||||
this.teamCollectionUpdatedSub = null
|
||||
this.teamCollectionRemovedSub = null
|
||||
this.teamRequestAddedSub = null
|
||||
this.teamRequestDeletedSub = null
|
||||
this.teamRequestUpdatedSub = null
|
||||
|
||||
if (this.teamID) this.initialize()
|
||||
}
|
||||
|
||||
@@ -243,13 +228,6 @@ export default class NewTeamCollectionAdapter {
|
||||
this.teamRequestAdded$?.unsubscribe()
|
||||
this.teamRequestDeleted$?.unsubscribe()
|
||||
this.teamRequestUpdated$?.unsubscribe()
|
||||
|
||||
this.teamCollectionAddedSub?.unsubscribe()
|
||||
this.teamCollectionUpdatedSub?.unsubscribe()
|
||||
this.teamCollectionRemovedSub?.unsubscribe()
|
||||
this.teamRequestAddedSub?.unsubscribe()
|
||||
this.teamRequestDeletedSub?.unsubscribe()
|
||||
this.teamRequestUpdatedSub?.unsubscribe()
|
||||
}
|
||||
|
||||
private async initialize() {
|
||||
@@ -428,16 +406,12 @@ export default class NewTeamCollectionAdapter {
|
||||
private registerSubscriptions() {
|
||||
if (!this.teamID) return
|
||||
|
||||
const [teamCollAdded$, teamCollAddedSub] = runGQLSubscription({
|
||||
this.teamCollectionAdded$ = runGQLSubscription({
|
||||
query: TeamCollectionAddedDocument,
|
||||
variables: {
|
||||
teamID: this.teamID,
|
||||
},
|
||||
})
|
||||
|
||||
this.teamCollectionAddedSub = teamCollAddedSub
|
||||
|
||||
this.teamCollectionAdded$ = teamCollAdded$.subscribe((result) => {
|
||||
}).subscribe((result) => {
|
||||
if (E.isLeft(result))
|
||||
throw new Error(`Team Collection Added Error: ${result.left}`)
|
||||
|
||||
@@ -452,15 +426,12 @@ export default class NewTeamCollectionAdapter {
|
||||
)
|
||||
})
|
||||
|
||||
const [teamCollUpdated$, teamCollUpdatedSub] = runGQLSubscription({
|
||||
this.teamCollectionUpdated$ = runGQLSubscription({
|
||||
query: TeamCollectionUpdatedDocument,
|
||||
variables: {
|
||||
teamID: this.teamID,
|
||||
},
|
||||
})
|
||||
|
||||
this.teamCollectionUpdatedSub = teamCollUpdatedSub
|
||||
this.teamCollectionUpdated$ = teamCollUpdated$.subscribe((result) => {
|
||||
}).subscribe((result) => {
|
||||
if (E.isLeft(result))
|
||||
throw new Error(`Team Collection Updated Error: ${result.left}`)
|
||||
|
||||
@@ -470,30 +441,24 @@ export default class NewTeamCollectionAdapter {
|
||||
})
|
||||
})
|
||||
|
||||
const [teamCollRemoved$, teamCollRemovedSub] = runGQLSubscription({
|
||||
this.teamCollectionRemoved$ = runGQLSubscription({
|
||||
query: TeamCollectionRemovedDocument,
|
||||
variables: {
|
||||
teamID: this.teamID,
|
||||
},
|
||||
})
|
||||
|
||||
this.teamCollectionRemovedSub = teamCollRemovedSub
|
||||
this.teamCollectionRemoved$ = teamCollRemoved$.subscribe((result) => {
|
||||
}).subscribe((result) => {
|
||||
if (E.isLeft(result))
|
||||
throw new Error(`Team Collection Removed Error: ${result.left}`)
|
||||
|
||||
this.removeCollection(result.right.teamCollectionRemoved)
|
||||
})
|
||||
|
||||
const [teamReqAdded$, teamReqAddedSub] = runGQLSubscription({
|
||||
this.teamRequestAdded$ = runGQLSubscription({
|
||||
query: TeamRequestAddedDocument,
|
||||
variables: {
|
||||
teamID: this.teamID,
|
||||
},
|
||||
})
|
||||
|
||||
this.teamRequestAddedSub = teamReqAddedSub
|
||||
this.teamRequestAdded$ = teamReqAdded$.subscribe((result) => {
|
||||
}).subscribe((result) => {
|
||||
if (E.isLeft(result))
|
||||
throw new Error(`Team Request Added Error: ${result.left}`)
|
||||
|
||||
@@ -507,15 +472,12 @@ export default class NewTeamCollectionAdapter {
|
||||
})
|
||||
})
|
||||
|
||||
const [teamReqUpdated$, teamReqUpdatedSub] = runGQLSubscription({
|
||||
this.teamRequestUpdated$ = runGQLSubscription({
|
||||
query: TeamRequestUpdatedDocument,
|
||||
variables: {
|
||||
teamID: this.teamID,
|
||||
},
|
||||
})
|
||||
|
||||
this.teamRequestUpdatedSub = teamReqUpdatedSub
|
||||
this.teamRequestUpdated$ = teamReqUpdated$.subscribe((result) => {
|
||||
}).subscribe((result) => {
|
||||
if (E.isLeft(result))
|
||||
throw new Error(`Team Request Updated Error: ${result.left}`)
|
||||
|
||||
@@ -527,15 +489,12 @@ export default class NewTeamCollectionAdapter {
|
||||
})
|
||||
})
|
||||
|
||||
const [teamReqDeleted$, teamReqDeleted] = runGQLSubscription({
|
||||
this.teamRequestDeleted$ = runGQLSubscription({
|
||||
query: TeamRequestDeletedDocument,
|
||||
variables: {
|
||||
teamID: this.teamID,
|
||||
},
|
||||
})
|
||||
|
||||
this.teamRequestUpdatedSub = teamReqDeleted
|
||||
this.teamRequestDeleted$ = teamReqDeleted$.subscribe((result) => {
|
||||
}).subscribe((result) => {
|
||||
if (E.isLeft(result))
|
||||
throw new Error(`Team Request Deleted Error ${result.left}`)
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import * as A from "fp-ts/Array"
|
||||
import * as E from "fp-ts/Either"
|
||||
import * as O from "fp-ts/Option"
|
||||
import * as RA from "fp-ts/ReadonlyArray"
|
||||
import * as S from "fp-ts/string"
|
||||
import qs from "qs"
|
||||
import { flow, pipe } from "fp-ts/function"
|
||||
import { pipe } from "fp-ts/function"
|
||||
import { combineLatest, Observable } from "rxjs"
|
||||
import { map } from "rxjs/operators"
|
||||
import {
|
||||
@@ -13,15 +9,14 @@ import {
|
||||
HoppRESTRequest,
|
||||
parseTemplateString,
|
||||
parseBodyEnvVariables,
|
||||
parseRawKeyValueEntries,
|
||||
Environment,
|
||||
HoppRESTHeader,
|
||||
HoppRESTParam,
|
||||
parseRawKeyValueEntriesE,
|
||||
parseTemplateStringE,
|
||||
} from "@hoppscotch/data"
|
||||
import { arrayFlatMap, arraySort } from "../functional/array"
|
||||
import { toFormData } from "../functional/formData"
|
||||
import { tupleWithSameKeysToRecord } from "../functional/record"
|
||||
import { tupleToRecord } from "../functional/record"
|
||||
import { getGlobalVariables } from "~/newstore/environments"
|
||||
|
||||
export interface EffectiveHoppRESTRequest extends HoppRESTRequest {
|
||||
@@ -34,7 +29,6 @@ export interface EffectiveHoppRESTRequest extends HoppRESTRequest {
|
||||
effectiveFinalHeaders: { key: string; value: string }[]
|
||||
effectiveFinalParams: { key: string; value: string }[]
|
||||
effectiveFinalBody: FormData | string | null
|
||||
effectiveFinalVars: { key: string; value: string }[]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,40 +210,25 @@ function getFinalBodyFromRequest(
|
||||
}
|
||||
|
||||
if (request.body.contentType === "application/x-www-form-urlencoded") {
|
||||
const parsedBodyRecord = pipe(
|
||||
return pipe(
|
||||
request.body.body,
|
||||
parseRawKeyValueEntriesE,
|
||||
E.map(
|
||||
flow(
|
||||
RA.toArray,
|
||||
/**
|
||||
* Filtering out empty keys and non-active pairs.
|
||||
*/
|
||||
A.filter(({ active, key }) => active && !S.isEmpty(key)),
|
||||
parseRawKeyValueEntries,
|
||||
|
||||
/**
|
||||
* Mapping each key-value to template-string-parser with either on array,
|
||||
* which will be resolved in further steps.
|
||||
*/
|
||||
A.map(({ key, value }) => [
|
||||
parseTemplateStringE(key, envVariables),
|
||||
parseTemplateStringE(value, envVariables),
|
||||
]),
|
||||
|
||||
/**
|
||||
* Filtering and mapping only right-eithers for each key-value as [string, string].
|
||||
*/
|
||||
A.filterMap(([key, value]) =>
|
||||
E.isRight(key) && E.isRight(value)
|
||||
? O.some([key.right, value.right] as [string, string])
|
||||
: O.none
|
||||
),
|
||||
tupleWithSameKeysToRecord,
|
||||
(obj) => qs.stringify(obj, { indices: false })
|
||||
)
|
||||
)
|
||||
// Filter out active
|
||||
A.filter((x) => x.active),
|
||||
// Convert to tuple
|
||||
A.map(
|
||||
({ key, value }) =>
|
||||
[
|
||||
parseTemplateString(key, envVariables),
|
||||
parseTemplateString(value, envVariables),
|
||||
] as [string, string]
|
||||
),
|
||||
// Tuple to Record object
|
||||
tupleToRecord,
|
||||
// Stringify
|
||||
qs.stringify
|
||||
)
|
||||
return E.isRight(parsedBodyRecord) ? parsedBodyRecord.right : null
|
||||
}
|
||||
|
||||
if (request.body.contentType === "multipart/form-data") {
|
||||
@@ -319,21 +298,15 @@ export function getEffectiveRESTRequest(
|
||||
value: parseTemplateString(x.value, envVariables),
|
||||
}))
|
||||
)
|
||||
const effectiveFinalVars = request.vars
|
||||
|
||||
const effectiveFinalBody = getFinalBodyFromRequest(request, envVariables)
|
||||
|
||||
return {
|
||||
...request,
|
||||
effectiveFinalURL: parseTemplateString(
|
||||
request.endpoint,
|
||||
envVariables,
|
||||
request.vars
|
||||
),
|
||||
effectiveFinalURL: parseTemplateString(request.endpoint, envVariables),
|
||||
effectiveFinalHeaders,
|
||||
effectiveFinalParams,
|
||||
effectiveFinalBody,
|
||||
effectiveFinalVars,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -143,15 +143,6 @@ export function useStreamSubscriber(): {
|
||||
}
|
||||
}
|
||||
|
||||
export function useI18nPathInfo() {
|
||||
const { localePath, getRouteBaseName } = useContext() as any
|
||||
|
||||
return {
|
||||
localePath: localePath as (x: string) => string,
|
||||
getRouteBaseName: getRouteBaseName as (x?: any) => string, // Should be a route
|
||||
}
|
||||
}
|
||||
|
||||
export function useI18n() {
|
||||
const {
|
||||
app: { i18n },
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"action": {
|
||||
"autoscroll": "自動捲動",
|
||||
"cancel": "取消",
|
||||
"choose_file": "選擇一個檔案",
|
||||
"clear": "清除",
|
||||
@@ -10,11 +9,10 @@
|
||||
"delete": "刪除",
|
||||
"disconnect": "斷開連線",
|
||||
"dismiss": "忽略",
|
||||
"dont_save": "不要儲存",
|
||||
"download_file": "下載檔案",
|
||||
"dont_save": "不要儲存",
|
||||
"duplicate": "複製",
|
||||
"edit": "編輯",
|
||||
"filter_response": "篩選回應",
|
||||
"go_back": "返回",
|
||||
"label": "標籤",
|
||||
"learn_more": "瞭解更多",
|
||||
@@ -22,14 +20,11 @@
|
||||
"more": "更多",
|
||||
"new": "新增",
|
||||
"no": "否",
|
||||
"open_workspace": "開啟工作區",
|
||||
"paste": "貼上",
|
||||
"prettify": "美化",
|
||||
"remove": "移除",
|
||||
"restore": "還原",
|
||||
"save": "儲存",
|
||||
"scroll_to_bottom": "捲動至底部",
|
||||
"scroll_to_top": "捲動至頂部",
|
||||
"search": "搜尋",
|
||||
"send": "傳送",
|
||||
"start": "開始",
|
||||
@@ -51,10 +46,10 @@
|
||||
"contact_us": "聯絡我們",
|
||||
"copy": "複製",
|
||||
"copy_user_id": "複製使用者驗證權杖",
|
||||
"developer_option": "開發者選項",
|
||||
"developer_option_description": "協助開發和維護 Hoppscotch 的工具。",
|
||||
"discord": "Discord",
|
||||
"documentation": "幫助文件",
|
||||
"developer_option": "開發者選項",
|
||||
"developer_option_description": "協助開發和維護 Hoppscotch 的工具。",
|
||||
"github": "GitHub",
|
||||
"help": "幫助與回饋",
|
||||
"home": "主頁",
|
||||
@@ -169,7 +164,6 @@
|
||||
"profile": "登入以檢視您的設定檔",
|
||||
"protocols": "協議為空",
|
||||
"schema": "連線至 GraphQL 端點",
|
||||
"shortcodes": "Shortcodes 為空",
|
||||
"team_name": "團隊名稱為空",
|
||||
"teams": "團隊為空",
|
||||
"tests": "沒有針對該請求的測試"
|
||||
@@ -203,11 +197,9 @@
|
||||
"invalid_link": "連結無效",
|
||||
"invalid_link_description": "您點擊的連結無效或已過期。",
|
||||
"json_prettify_invalid_body": "無法美化無效的請求主體,處理 JSON 語法錯誤並重試",
|
||||
"json_parsing_failed": "JSON 無效",
|
||||
"network_error": "似乎有網路錯誤。請再試一次。",
|
||||
"network_fail": "無法傳送請求",
|
||||
"no_duration": "無持續時間",
|
||||
"no_results_found": "找不到結果",
|
||||
"script_fail": "無法執行預請求指令碼",
|
||||
"something_went_wrong": "發生了一些錯誤",
|
||||
"test_script_fail": "無法執行測試指令碼"
|
||||
@@ -274,19 +266,15 @@
|
||||
"from_url": "從網址匯入",
|
||||
"gist_url": "輸入 Gist 網址",
|
||||
"json_description": "從 Hoppscotch 組合 JSON 檔匯入組合",
|
||||
"title": "匯入",
|
||||
"import_from_url_success": "已匯入組合",
|
||||
"import_from_url_invalid_file_format": "匯入組合時發生錯誤",
|
||||
"import_from_url_invalid_type": "不支援此類型。可接受的值為 'hoppscotch'、'openapi'、'postman'、'insomnia'",
|
||||
"import_from_url_invalid_fetch": "無法從網址取得資料"
|
||||
"title": "匯入"
|
||||
},
|
||||
"layout": {
|
||||
"collapse_collection": "隱藏或顯示組合",
|
||||
"collapse_sidebar": "隱藏或顯示側邊欄",
|
||||
"column": "垂直布局",
|
||||
"name": "配置",
|
||||
"row": "水平布局",
|
||||
"zen_mode": "專注模式"
|
||||
"zen_mode": "專注模式",
|
||||
"collapse_sidebar": "隱藏或顯示側邊欄",
|
||||
"collapse_collection": "隱藏或顯示組合",
|
||||
"name": "配置"
|
||||
},
|
||||
"modal": {
|
||||
"collections": "組合",
|
||||
@@ -343,11 +331,6 @@
|
||||
"body": "請求本體",
|
||||
"choose_language": "選擇語言",
|
||||
"content_type": "內容類型",
|
||||
"content_type_titles": {
|
||||
"others": "其他",
|
||||
"structured": "結構",
|
||||
"text": "文字"
|
||||
},
|
||||
"copy_link": "複製連結",
|
||||
"duration": "持續時間",
|
||||
"enter_curl": "輸入 cURL",
|
||||
@@ -358,9 +341,6 @@
|
||||
"method": "方法",
|
||||
"name": "請求名稱",
|
||||
"new": "新請求",
|
||||
"override": "覆寫",
|
||||
"override_help": "在標頭設置 <xmp>Content-Type</xmp>",
|
||||
"overriden": "已覆寫",
|
||||
"parameter_list": "查詢參數",
|
||||
"parameters": "參數",
|
||||
"path": "路徑",
|
||||
@@ -378,11 +358,12 @@
|
||||
"type": "請求類型",
|
||||
"url": "網址",
|
||||
"variables": "變數",
|
||||
"view_my_links": "檢視我的連結"
|
||||
"override": "覆寫",
|
||||
"override_help": "在標頭設置 <xmp>Content-Type</xmp>",
|
||||
"overriden": "已覆寫"
|
||||
},
|
||||
"response": {
|
||||
"body": "回應本體",
|
||||
"filter_response_body": "篩選 JSON 回應本體 (使用 JSONPath 語法)",
|
||||
"headers": "回應標頭",
|
||||
"html": "HTML",
|
||||
"image": "影像",
|
||||
@@ -434,8 +415,6 @@
|
||||
"proxy_use_toggle": "使用 Proxy 中介軟體傳送請求",
|
||||
"read_the": "閱讀",
|
||||
"reset_default": "重置為預設",
|
||||
"short_codes": "快捷碼",
|
||||
"short_codes_description": "我們為您打造的快捷碼。",
|
||||
"sidebar_on_left": "左側邊欄",
|
||||
"sync": "同步",
|
||||
"sync_collections": "組合",
|
||||
@@ -468,7 +447,7 @@
|
||||
"documentation": "前往文件頁面",
|
||||
"forward": "前往下一頁面",
|
||||
"graphql": "前往 GraphQL 頁面",
|
||||
"profile": "前往個人檔案頁面",
|
||||
"profile": "Go to Profile page",
|
||||
"realtime": "前往實時頁面",
|
||||
"rest": "前往 REST 頁面",
|
||||
"settings": "前往設定頁面",
|
||||
@@ -497,15 +476,6 @@
|
||||
"title": "主題"
|
||||
}
|
||||
},
|
||||
"shortcodes":{
|
||||
"actions":"操作",
|
||||
"created_on": "建立於",
|
||||
"deleted" : "已刪除快捷碼",
|
||||
"method": "方法",
|
||||
"not_found":"找不到快捷碼",
|
||||
"short_code":"快捷碼",
|
||||
"url": "網址"
|
||||
},
|
||||
"show": {
|
||||
"code": "顯示程式碼",
|
||||
"more": "顯示更多",
|
||||
@@ -517,8 +487,7 @@
|
||||
"event_name": "事件名稱",
|
||||
"events": "事件",
|
||||
"log": "日誌",
|
||||
"url": "網址",
|
||||
"connection_not_authorized": "此 SocketIO 連線未使用任何驗證。"
|
||||
"url": "網址"
|
||||
},
|
||||
"sse": {
|
||||
"event_type": "事件類型",
|
||||
@@ -548,19 +517,7 @@
|
||||
"loading": "正在載入……",
|
||||
"none": "無",
|
||||
"nothing_found": "沒有找到",
|
||||
"waiting_send_request": "等待傳送請求",
|
||||
"subscribed_success": "成功訂閱此主題:{topic}",
|
||||
"unsubscribed_success": "成功取消訂閱此主題:{topic}",
|
||||
"subscribed_failed": "無法訂閱此主題:{topic}",
|
||||
"unsubscribed_failed": "無法取消訂閱此主題:{topic}",
|
||||
"published_message": "已將此訊息:{message} 發布至主題:{topic}",
|
||||
"published_error": "將訊息:{topic} 發布至主題:{message} 時發生錯誤",
|
||||
"message_received": "訊息:{message}已抵達主題:{topic}",
|
||||
"mqtt_subscription_failed": "訂閱此主題時發生錯誤:{topic}",
|
||||
"connection_lost": "失去連線",
|
||||
"connection_failed": "連線失敗",
|
||||
"connection_error": "連線失敗",
|
||||
"reconnection_error": "重新連線失敗"
|
||||
"waiting_send_request": "等待傳送請求"
|
||||
},
|
||||
"support": {
|
||||
"changelog": "閱讀更多有關最新版本的內容",
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
FormDataKeyValue,
|
||||
HoppRESTHeader,
|
||||
HoppRESTParam,
|
||||
HoppRESTVar,
|
||||
HoppRESTReqBody,
|
||||
HoppRESTRequest,
|
||||
RESTReqSchemaVersion,
|
||||
@@ -30,7 +29,6 @@ export const getDefaultRESTRequest = (): HoppRESTRequest => ({
|
||||
endpoint: "https://echo.hoppscotch.io",
|
||||
name: "Untitled request",
|
||||
params: [],
|
||||
vars: [],
|
||||
headers: [],
|
||||
method: "GET",
|
||||
auth: {
|
||||
@@ -82,14 +80,6 @@ const dispatchers = defineDispatchers({
|
||||
},
|
||||
}
|
||||
},
|
||||
setVars(curr: RESTSession, { entries }: { entries: HoppRESTVar[] }) {
|
||||
return {
|
||||
request: {
|
||||
...curr.request,
|
||||
vars: entries,
|
||||
},
|
||||
}
|
||||
},
|
||||
addParam(curr: RESTSession, { newParam }: { newParam: HoppRESTParam }) {
|
||||
return {
|
||||
request: {
|
||||
@@ -98,14 +88,6 @@ const dispatchers = defineDispatchers({
|
||||
},
|
||||
}
|
||||
},
|
||||
addVar(curr: RESTSession, { newVar }: { newVar: HoppRESTVar }) {
|
||||
return {
|
||||
request: {
|
||||
...curr.request,
|
||||
vars: [...curr.request.vars, newVar],
|
||||
},
|
||||
}
|
||||
},
|
||||
updateParam(
|
||||
curr: RESTSession,
|
||||
{ index, updatedParam }: { index: number; updatedParam: HoppRESTParam }
|
||||
@@ -122,22 +104,6 @@ const dispatchers = defineDispatchers({
|
||||
},
|
||||
}
|
||||
},
|
||||
updateVar(
|
||||
curr: RESTSession,
|
||||
{ index, updatedVar }: { index: number; updatedVar: HoppRESTVar }
|
||||
) {
|
||||
const newVars = curr.request.vars.map((vari, i) => {
|
||||
if (i === index) return updatedVar
|
||||
else return vari
|
||||
})
|
||||
|
||||
return {
|
||||
request: {
|
||||
...curr.request,
|
||||
vars: newVars,
|
||||
},
|
||||
}
|
||||
},
|
||||
deleteParam(curr: RESTSession, { index }: { index: number }) {
|
||||
const newParams = curr.request.params.filter((_x, i) => i !== index)
|
||||
|
||||
@@ -148,16 +114,6 @@ const dispatchers = defineDispatchers({
|
||||
},
|
||||
}
|
||||
},
|
||||
deleteVar(curr: RESTSession, { index }: { index: number }) {
|
||||
const newVars = curr.request.vars.filter((_x, i) => i !== index)
|
||||
|
||||
return {
|
||||
request: {
|
||||
...curr.request,
|
||||
vars: newVars,
|
||||
},
|
||||
}
|
||||
},
|
||||
deleteAllParams(curr: RESTSession) {
|
||||
return {
|
||||
request: {
|
||||
@@ -417,14 +373,6 @@ export function setRESTParams(entries: HoppRESTParam[]) {
|
||||
},
|
||||
})
|
||||
}
|
||||
export function setRESTVars(entries: HoppRESTVar[]) {
|
||||
restSessionStore.dispatch({
|
||||
dispatcher: "setVars",
|
||||
payload: {
|
||||
entries,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function addRESTParam(newParam: HoppRESTParam) {
|
||||
restSessionStore.dispatch({
|
||||
@@ -434,14 +382,6 @@ export function addRESTParam(newParam: HoppRESTParam) {
|
||||
},
|
||||
})
|
||||
}
|
||||
export function addRESTVar(newVar: HoppRESTVar) {
|
||||
restSessionStore.dispatch({
|
||||
dispatcher: "addVar",
|
||||
payload: {
|
||||
newVar,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function updateRESTParam(index: number, updatedParam: HoppRESTParam) {
|
||||
restSessionStore.dispatch({
|
||||
@@ -452,15 +392,6 @@ export function updateRESTParam(index: number, updatedParam: HoppRESTParam) {
|
||||
},
|
||||
})
|
||||
}
|
||||
export function updateRESTVar(index: number, updatedVar: HoppRESTVar) {
|
||||
restSessionStore.dispatch({
|
||||
dispatcher: "updateVar",
|
||||
payload: {
|
||||
updatedVar,
|
||||
index,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteRESTParam(index: number) {
|
||||
restSessionStore.dispatch({
|
||||
@@ -471,15 +402,6 @@ export function deleteRESTParam(index: number) {
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteRESTVar(index: number) {
|
||||
restSessionStore.dispatch({
|
||||
dispatcher: "deleteVar",
|
||||
payload: {
|
||||
index,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteAllRESTParams() {
|
||||
restSessionStore.dispatch({
|
||||
dispatcher: "deleteAllParams",
|
||||
@@ -670,20 +592,12 @@ export const restParams$ = restSessionStore.subject$.pipe(
|
||||
distinctUntilChanged()
|
||||
)
|
||||
|
||||
export const restVars$ = restSessionStore.subject$.pipe(
|
||||
pluck("request", "vars"),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
|
||||
export const restActiveParamsCount$ = restParams$.pipe(
|
||||
map(
|
||||
(params) =>
|
||||
params.filter((x) => x.active && (x.key !== "" || x.value !== "")).length
|
||||
)
|
||||
)
|
||||
export const restActiveVarsCount$ = restVars$.pipe(
|
||||
map((vars) => vars.filter((x) => x.key !== "" || x.value !== "").length)
|
||||
)
|
||||
|
||||
export const restMethod$ = restSessionStore.subject$.pipe(
|
||||
pluck("request", "method"),
|
||||
|
||||
@@ -315,7 +315,6 @@ completedRESTResponse$.subscribe((res) => {
|
||||
method: res.req.method,
|
||||
name: res.req.name,
|
||||
params: res.req.params,
|
||||
vars: res.req.vars,
|
||||
preRequestScript: res.req.preRequestScript,
|
||||
testScript: res.req.testScript,
|
||||
v: res.req.v,
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
"@codemirror/tooltip": "^0.19.16",
|
||||
"@codemirror/view": "^0.19.48",
|
||||
"@hoppscotch/codemirror-lang-graphql": "workspace:^0.2.0",
|
||||
"@hoppscotch/data": "workspace:^0.4.3",
|
||||
"@hoppscotch/data": "workspace:^0.4.2",
|
||||
"@hoppscotch/js-sandbox": "workspace:^2.0.0",
|
||||
"@nuxtjs/axios": "^5.13.6",
|
||||
"@nuxtjs/composition-api": "^0.32.0",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<AppPaneLayout layout-id="docs">
|
||||
<AppPaneLayout>
|
||||
<template #primary>
|
||||
<div class="flex items-start justify-between p-4">
|
||||
<label>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<AppPaneLayout layout-id="graphql">
|
||||
<AppPaneLayout>
|
||||
<template #primary>
|
||||
<GraphqlRequest :conn="gqlConn" />
|
||||
<GraphqlRequestOptions :conn="gqlConn" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<AppPaneLayout layout-id="http">
|
||||
<AppPaneLayout>
|
||||
<template #primary>
|
||||
<HttpRequest />
|
||||
<HttpRequestOptions />
|
||||
|
||||
@@ -13,10 +13,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch, ref, useRouter, useRoute } from "@nuxtjs/composition-api"
|
||||
import { useI18n, useI18nPathInfo } from "~/helpers/utils/composables"
|
||||
import { useI18n } from "~/helpers/utils/composables"
|
||||
|
||||
const t = useI18n()
|
||||
const { localePath, getRouteBaseName } = useI18nPathInfo()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
@@ -45,21 +44,17 @@ const currentTab = ref<RealtimeNavTab>("websocket")
|
||||
|
||||
// Update the router when the tab is updated
|
||||
watch(currentTab, (newTab) => {
|
||||
router.push(localePath(`/realtime/${newTab}`))
|
||||
router.push(`/realtime/${newTab}`)
|
||||
})
|
||||
|
||||
// Update the tab when router is upgrad
|
||||
watch(
|
||||
route,
|
||||
(updateRoute) => {
|
||||
const path = getRouteBaseName(updateRoute)
|
||||
if (updateRoute.path === "/realtime") router.replace("/realtime/websocket")
|
||||
|
||||
if (path.endsWith("realtime")) {
|
||||
router.replace(localePath(`/realtime/websocket`))
|
||||
return
|
||||
}
|
||||
|
||||
const destination: string | undefined = path.split("realtime-")[1]
|
||||
const destination: string | undefined =
|
||||
updateRoute.path.split("/realtime/")[1]
|
||||
|
||||
const target = REALTIME_NAVIGATION.find(
|
||||
({ target }) => target === destination
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<AppPaneLayout layout-id="mqtt">
|
||||
<AppPaneLayout>
|
||||
<template #primary>
|
||||
<div
|
||||
class="sticky top-0 z-10 flex flex-shrink-0 p-4 overflow-x-auto space-x-2 bg-primary hide-scrollbar"
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<AppPaneLayout layout-id="socketio">
|
||||
<AppPaneLayout>
|
||||
<template #primary>
|
||||
<div
|
||||
class="sticky top-0 z-10 flex flex-shrink-0 p-4 overflow-x-auto space-x-2 bg-primary hide-scrollbar"
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<AppPaneLayout layout-id="sse">
|
||||
<AppPaneLayout>
|
||||
<template #primary>
|
||||
<div
|
||||
class="sticky top-0 z-10 flex flex-shrink-0 p-4 overflow-x-auto space-x-2 bg-primary hide-scrollbar"
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<AppPaneLayout layout-id="websocket">
|
||||
<AppPaneLayout>
|
||||
<template #primary>
|
||||
<div
|
||||
class="sticky top-0 z-10 flex flex-shrink-0 p-4 overflow-x-auto space-x-2 bg-primary hide-scrollbar"
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hoppscotch/cli",
|
||||
"version": "0.3.0",
|
||||
"version": "0.2.1",
|
||||
"description": "A CLI to run Hoppscotch test scripts in CI environments.",
|
||||
"homepage": "https://hoppscotch.io",
|
||||
"main": "dist/index.js",
|
||||
@@ -36,7 +36,7 @@
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"devDependencies": {
|
||||
"@hoppscotch/data": "workspace:^0.4.3",
|
||||
"@hoppscotch/data": "workspace:^0.4.2",
|
||||
"@hoppscotch/js-sandbox": "workspace:^2.0.0",
|
||||
"@relmify/jest-fp-ts": "^2.0.2",
|
||||
"@swc/core": "^1.2.181",
|
||||
|
||||
@@ -92,41 +92,12 @@ describe("Test 'hopp test <file> --env <file>' command:", () => {
|
||||
expect(out).toBe<HoppErrorCode>("FILE_NOT_FOUND");
|
||||
});
|
||||
|
||||
test("No errors occured (exit code 0).", async () => {
|
||||
const TESTS_PATH = getTestJsonFilePath("env-flag-tests.json");
|
||||
const ENV_PATH = getTestJsonFilePath("env-flag-envs.json");
|
||||
const cmd = `node ./bin/hopp test ${TESTS_PATH} --env ${ENV_PATH}`;
|
||||
const { error } = await execAsync(cmd);
|
||||
// test("No errors occured (exit code 0).", async () => {
|
||||
// const TESTS_PATH = getTestJsonFilePath("env-flag-tests.json");
|
||||
// const ENV_PATH = getTestJsonFilePath("env-flag-envs.json");
|
||||
// const cmd = `node ./bin/hopp test ${TESTS_PATH} --env ${ENV_PATH}`;
|
||||
// const { error } = await execAsync(cmd);
|
||||
|
||||
expect(error).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Test 'hopp test <file> --delay <delay_in_ms>' command:", () => {
|
||||
const VALID_TEST_CMD = `node ./bin/hopp test ${getTestJsonFilePath(
|
||||
"passes.json"
|
||||
)}`;
|
||||
|
||||
test("No value passed to delay flag.", async () => {
|
||||
const cmd = `${VALID_TEST_CMD} --delay`;
|
||||
const { stdout } = await execAsync(cmd);
|
||||
const out = getErrorCode(stdout);
|
||||
|
||||
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
|
||||
});
|
||||
|
||||
test("Invalid value passed to delay flag.", async () => {
|
||||
const cmd = `${VALID_TEST_CMD} --delay 'NaN'`;
|
||||
const { stdout } = await execAsync(cmd);
|
||||
const out = getErrorCode(stdout);
|
||||
|
||||
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
|
||||
});
|
||||
|
||||
test("Valid value passed to delay flag.", async () => {
|
||||
const cmd = `${VALID_TEST_CMD} --delay 1`;
|
||||
const { error } = await execAsync(cmd);
|
||||
|
||||
expect(error).toBeNull();
|
||||
});
|
||||
// expect(error).toBeNull();
|
||||
// });
|
||||
});
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import { hrtime } from "process";
|
||||
import { getDurationInSeconds } from "../../../utils/getters";
|
||||
import { delayPromiseFunction } from "../../../utils/request";
|
||||
|
||||
describe("describePromiseFunction", () => {
|
||||
let promiseFunc = (): Promise<number> => new Promise((resolve) => resolve(2));
|
||||
beforeEach(() => {
|
||||
promiseFunc = (): Promise<number> => new Promise((resolve) => resolve(2));
|
||||
});
|
||||
|
||||
it("Should resolve the promise<number> after 2 seconds.", async () => {
|
||||
const start = hrtime();
|
||||
const res = await delayPromiseFunction(promiseFunc, 2000);
|
||||
const end = hrtime(start);
|
||||
const duration = getDurationInSeconds(end);
|
||||
|
||||
expect(Math.floor(duration)).toEqual(2);
|
||||
expect(typeof res).toBe("number");
|
||||
});
|
||||
|
||||
it("Should resolve the promise<number> after 4 seconds.", async () => {
|
||||
const start = hrtime();
|
||||
const res = await delayPromiseFunction(promiseFunc, 4000);
|
||||
const end = hrtime(start);
|
||||
const duration = getDurationInSeconds(end);
|
||||
|
||||
expect(Math.floor(duration)).toEqual(4);
|
||||
expect(typeof res).toBe("number");
|
||||
});
|
||||
});
|
||||
@@ -58,12 +58,7 @@ describe("processRequest", () => {
|
||||
(axios as unknown as jest.Mock).mockResolvedValue(DEFAULT_RESPONSE);
|
||||
|
||||
return expect(
|
||||
processRequest({
|
||||
request: SAMPLE_REQUEST,
|
||||
envs: DEFAULT_ENVS,
|
||||
path: "fake/collection/path",
|
||||
delay: 0,
|
||||
})()
|
||||
processRequest(SAMPLE_REQUEST, DEFAULT_ENVS, "fake/collection/path")()
|
||||
).resolves.toMatchObject({
|
||||
report: {
|
||||
result: true,
|
||||
@@ -84,12 +79,7 @@ describe("processRequest", () => {
|
||||
(axios as unknown as jest.Mock).mockResolvedValue(DEFAULT_RESPONSE);
|
||||
|
||||
return expect(
|
||||
processRequest({
|
||||
request: SAMPLE_REQUEST,
|
||||
envs: DEFAULT_ENVS,
|
||||
path: "fake/collection/path",
|
||||
delay: 0,
|
||||
})()
|
||||
processRequest(SAMPLE_REQUEST, DEFAULT_ENVS, "fake/collection/path")()
|
||||
).resolves.toMatchObject({
|
||||
envs: {
|
||||
selected: [{ key: "ENDPOINT", value: "https://example.com" }],
|
||||
@@ -106,12 +96,7 @@ describe("processRequest", () => {
|
||||
(axios as unknown as jest.Mock).mockResolvedValue(DEFAULT_RESPONSE);
|
||||
|
||||
return expect(
|
||||
processRequest({
|
||||
request: SAMPLE_REQUEST,
|
||||
envs: DEFAULT_ENVS,
|
||||
path: "fake/request/path",
|
||||
delay: 0,
|
||||
})()
|
||||
processRequest(SAMPLE_REQUEST, DEFAULT_ENVS, "fake/request/path")()
|
||||
).resolves.toMatchObject({
|
||||
report: { result: false },
|
||||
});
|
||||
|
||||
@@ -9,14 +9,12 @@ import { handleError } from "../handlers/error";
|
||||
import { parseCollectionData } from "../utils/mutators";
|
||||
import { parseEnvsData } from "../options/test/env";
|
||||
import { TestCmdOptions } from "../types/commands";
|
||||
import { parseDelayOption } from "../options/test/delay";
|
||||
|
||||
export const test = (path: string, options: TestCmdOptions) => async () => {
|
||||
await pipe(
|
||||
TE.Do,
|
||||
TE.bind("envs", () => parseEnvsData(options.env)),
|
||||
TE.bind("collections", () => parseCollectionData(path)),
|
||||
TE.bind("delay", () => parseDelayOption(options.delay)),
|
||||
TE.chainTaskK(collectionsRunner),
|
||||
TE.chainW(flow(collectionsRunnerResult, collectionsRunnerExit, TE.of)),
|
||||
TE.mapLeft((e) => {
|
||||
|
||||
@@ -50,10 +50,6 @@ program
|
||||
"path to a hoppscotch collection.json file for CI testing"
|
||||
)
|
||||
.option("-e, --env <file_path>", "path to an environment variables json file")
|
||||
.option(
|
||||
"-d, --delay <delay_in_ms>",
|
||||
"delay in milliseconds(ms) between consecutive requests within a collection"
|
||||
)
|
||||
.allowExcessArguments(false)
|
||||
.allowUnknownOption(false)
|
||||
.description("running hoppscotch collection.json file")
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import * as TE from "fp-ts/TaskEither";
|
||||
import * as S from "fp-ts/string";
|
||||
import { pipe } from "fp-ts/function";
|
||||
import { error, HoppCLIError } from "../../types/errors";
|
||||
|
||||
export const parseDelayOption = (
|
||||
delay: unknown
|
||||
): TE.TaskEither<HoppCLIError, number> =>
|
||||
!S.isString(delay)
|
||||
? TE.right(0)
|
||||
: pipe(
|
||||
delay,
|
||||
Number,
|
||||
TE.fromPredicate(Number.isSafeInteger, () =>
|
||||
error({
|
||||
code: "INVALID_ARGUMENT",
|
||||
data: "Expected '-d, --delay' value to be number",
|
||||
})
|
||||
)
|
||||
);
|
||||
@@ -4,7 +4,6 @@ import { HoppEnvs } from "./request";
|
||||
export type CollectionRunnerParam = {
|
||||
collections: HoppCollection<HoppRESTRequest>[];
|
||||
envs: HoppEnvs;
|
||||
delay?: number;
|
||||
};
|
||||
|
||||
export type HoppCollectionFileExt = "json";
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export type TestCmdOptions = {
|
||||
env: string;
|
||||
delay: number;
|
||||
};
|
||||
|
||||
export type HoppEnvFileExt = "json";
|
||||
|
||||
@@ -26,10 +26,3 @@ export type RequestReport = {
|
||||
result: boolean;
|
||||
duration: { test: number; request: number; preRequest: number };
|
||||
};
|
||||
|
||||
export type ProcessRequestParams = {
|
||||
request: HoppRESTRequest;
|
||||
envs: HoppEnvs;
|
||||
path: string;
|
||||
delay: number;
|
||||
};
|
||||
|
||||
@@ -5,12 +5,7 @@ import { bold } from "chalk";
|
||||
import { log } from "console";
|
||||
import round from "lodash/round";
|
||||
import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data";
|
||||
import {
|
||||
HoppEnvs,
|
||||
CollectionStack,
|
||||
RequestReport,
|
||||
ProcessRequestParams,
|
||||
} from "../types/request";
|
||||
import { HoppEnvs, CollectionStack, RequestReport } from "../types/request";
|
||||
import {
|
||||
getRequestMetrics,
|
||||
preProcessRequest,
|
||||
@@ -46,7 +41,6 @@ export const collectionsRunner =
|
||||
(param: CollectionRunnerParam): T.Task<RequestReport[]> =>
|
||||
async () => {
|
||||
const envs: HoppEnvs = param.envs;
|
||||
const delay = param.delay ?? 0;
|
||||
const requestsReport: RequestReport[] = [];
|
||||
const collectionStack: CollectionStack[] = getCollectionStack(
|
||||
param.collections
|
||||
@@ -60,18 +54,12 @@ export const collectionsRunner =
|
||||
for (const request of collection.requests) {
|
||||
const _request = preProcessRequest(request);
|
||||
const requestPath = `${path}/${_request.name}`;
|
||||
const processRequestParams: ProcessRequestParams = {
|
||||
path: requestPath,
|
||||
request: _request,
|
||||
envs,
|
||||
delay,
|
||||
};
|
||||
|
||||
// Request processing initiated message.
|
||||
log(WARN(`\nRunning: ${bold(requestPath)}`));
|
||||
|
||||
// Processing current request.
|
||||
const result = await processRequest(processRequestParams)();
|
||||
const result = await processRequest(_request, envs, requestPath)();
|
||||
|
||||
// Updating global & selected envs with new envs from processed-request output.
|
||||
const { global, selected } = result.envs;
|
||||
|
||||
@@ -129,8 +129,3 @@ export const getDurationInSeconds = (
|
||||
const durationInSeconds = (end[0] * 1e9 + end[1]) / 1e9;
|
||||
return round(durationInSeconds, precision);
|
||||
};
|
||||
|
||||
export const roundDuration = (
|
||||
duration: number,
|
||||
precision: number = DEFAULT_DURATION_PRECISION
|
||||
) => round(duration, precision);
|
||||
|
||||
@@ -12,11 +12,7 @@ import { testRunner, getTestScriptParams, hasFailedTestCases } from "./test";
|
||||
import { RequestConfig, EffectiveHoppRESTRequest } from "../interfaces/request";
|
||||
import { RequestRunnerResponse } from "../interfaces/response";
|
||||
import { preRequestScriptRunner } from "./pre-request";
|
||||
import {
|
||||
HoppEnvs,
|
||||
ProcessRequestParams,
|
||||
RequestReport,
|
||||
} from "../types/request";
|
||||
import { HoppEnvs, RequestReport } from "../types/request";
|
||||
import {
|
||||
printPreRequestRunner,
|
||||
printRequestRunner,
|
||||
@@ -193,11 +189,11 @@ const getRequest = {
|
||||
*/
|
||||
export const processRequest =
|
||||
(
|
||||
params: ProcessRequestParams
|
||||
request: HoppRESTRequest,
|
||||
envs: HoppEnvs,
|
||||
path: string
|
||||
): T.Task<{ envs: HoppEnvs; report: RequestReport }> =>
|
||||
async () => {
|
||||
const { envs, path, request, delay } = params;
|
||||
|
||||
// Initialising updatedEnvs with given parameter envs, will eventually get updated.
|
||||
const result = {
|
||||
envs: <HoppEnvs>envs,
|
||||
@@ -251,9 +247,7 @@ export const processRequest =
|
||||
duration: 0,
|
||||
};
|
||||
// Executing request-runner.
|
||||
const requestRunnerRes = await delayPromiseFunction<
|
||||
E.Either<HoppCLIError, RequestRunnerResponse>
|
||||
>(requestRunner(requestConfig), delay);
|
||||
const requestRunnerRes = await requestRunner(requestConfig)();
|
||||
if (E.isLeft(requestRunnerRes)) {
|
||||
// Updating report for errors & current result
|
||||
report.errors.push(requestRunnerRes.left);
|
||||
@@ -364,15 +358,3 @@ export const getRequestMetrics = (
|
||||
hasReqErrors ? { failed: 1, passed: 0 } : { failed: 0, passed: 1 },
|
||||
(requests) => <RequestMetrics>{ requests, duration }
|
||||
);
|
||||
|
||||
/**
|
||||
* A function to execute promises with specific delay in milliseconds.
|
||||
* @param func Function with promise with return type T.
|
||||
* @param delay TIme in milliseconds to delay function.
|
||||
* @returns Promise of type same as func.
|
||||
*/
|
||||
export const delayPromiseFunction = <T>(
|
||||
func: () => Promise<T>,
|
||||
delay: number
|
||||
): Promise<T> =>
|
||||
new Promise((resolve) => setTimeout(() => resolve(func()), delay));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hoppscotch/data",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.2",
|
||||
"description": "Data Types, Validations and Migrations for Hoppscotch Public Data Structures",
|
||||
"main": "dist/index.js",
|
||||
"module": "true",
|
||||
|
||||
@@ -9,13 +9,7 @@ export type Environment = {
|
||||
}[]
|
||||
}
|
||||
|
||||
export type Variables = {
|
||||
key: string
|
||||
value: string
|
||||
}[]
|
||||
|
||||
const REGEX_ENV_VAR = /<<([^>]*)>>/g // "<<myVariable>>"
|
||||
const REGEX_MY_VAR = /{{([^}]*)}}/g // "{{myVariable}}"
|
||||
|
||||
/**
|
||||
* How much times can we expand environment variables
|
||||
@@ -65,37 +59,26 @@ export const parseBodyEnvVariables = (
|
||||
|
||||
export function parseTemplateStringE(
|
||||
str: string,
|
||||
variables: Environment["variables"],
|
||||
myVariables?: Variables | undefined
|
||||
variables: Environment["variables"]
|
||||
) {
|
||||
|
||||
if (!variables || !str) {
|
||||
return E.right(str)
|
||||
}
|
||||
|
||||
let result = str
|
||||
let depthEnv = 0
|
||||
let depthVar = 0
|
||||
let depth = 0
|
||||
|
||||
while (result.match(REGEX_ENV_VAR) != null && depthEnv <= ENV_MAX_EXPAND_LIMIT) {
|
||||
while (result.match(REGEX_ENV_VAR) != null && depth <= ENV_MAX_EXPAND_LIMIT) {
|
||||
result = decodeURI(encodeURI(result)).replace(
|
||||
REGEX_ENV_VAR,
|
||||
(_, p1) => variables.find((x) => x.key === p1)?.value || ""
|
||||
)
|
||||
depthEnv++
|
||||
depth++
|
||||
}
|
||||
|
||||
if (myVariables) {
|
||||
while (result.match(REGEX_MY_VAR) != null && depthVar <= ENV_MAX_EXPAND_LIMIT) {
|
||||
result = decodeURI(encodeURI(result)).replace(
|
||||
REGEX_MY_VAR,
|
||||
(_, p1) => myVariables.find((x) => x.key === p1)?.value || ""
|
||||
)
|
||||
depthVar++
|
||||
}
|
||||
}
|
||||
|
||||
return depthEnv <= ENV_MAX_EXPAND_LIMIT && depthVar <= ENV_MAX_EXPAND_LIMIT ? E.right(result) : E.left(ENV_EXPAND_LOOP);
|
||||
return depth > ENV_MAX_EXPAND_LIMIT
|
||||
? E.left(ENV_EXPAND_LOOP)
|
||||
: E.right(result)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,33 +86,9 @@ export function parseTemplateStringE(
|
||||
*/
|
||||
export const parseTemplateString = (
|
||||
str: string,
|
||||
variables: Environment["variables"],
|
||||
myVariables?: Variables
|
||||
variables: Environment["variables"]
|
||||
) =>
|
||||
pipe(
|
||||
parseTemplateStringE(str, variables, myVariables),
|
||||
parseTemplateStringE(str, variables),
|
||||
E.getOrElse(() => str)
|
||||
)
|
||||
|
||||
export function parseMyVariablesString(
|
||||
str: string,
|
||||
variables: Variables,
|
||||
) {
|
||||
|
||||
if (!variables || !str) {
|
||||
return E.right(str)
|
||||
}
|
||||
|
||||
let result = str
|
||||
let depthVar = 0
|
||||
|
||||
while (result.match(REGEX_MY_VAR) != null && depthVar <= ENV_MAX_EXPAND_LIMIT) {
|
||||
result = decodeURI(encodeURI(result)).replace(
|
||||
REGEX_MY_VAR,
|
||||
(_, p1) => variables.find((x) => x.key === p1)?.value || ""
|
||||
)
|
||||
depthVar++
|
||||
}
|
||||
|
||||
return depthVar <= ENV_MAX_EXPAND_LIMIT ? E.right(result) : E.left(ENV_EXPAND_LOOP);
|
||||
}
|
||||
|
||||
@@ -8,12 +8,6 @@ import * as E from "fp-ts/Either"
|
||||
import * as P from "parser-ts/Parser"
|
||||
import * as S from "parser-ts/string"
|
||||
import * as C from "parser-ts/char"
|
||||
import { recordUpdate } from "./utils/record"
|
||||
|
||||
/**
|
||||
* Special characters in the Raw Key Value Grammar
|
||||
*/
|
||||
const SPECIAL_CHARS = ["#", ":"] as const
|
||||
|
||||
export type RawKeyValueEntry = {
|
||||
key: string
|
||||
@@ -37,31 +31,14 @@ const stringTakeUntilCharsInclusive = flow(
|
||||
P.chainFirst(() => P.sat(() => true)),
|
||||
)
|
||||
|
||||
const quotedString = pipe(
|
||||
S.doubleQuotedString,
|
||||
P.map((x) => JSON.parse(`"${x}"`))
|
||||
)
|
||||
|
||||
const key = pipe(
|
||||
wsSurround(quotedString),
|
||||
|
||||
P.alt(() =>
|
||||
pipe(
|
||||
stringTakeUntilChars([":", "\n"]),
|
||||
P.map(Str.trim)
|
||||
)
|
||||
)
|
||||
stringTakeUntilChars([":", "\n"]),
|
||||
P.map(Str.trim)
|
||||
)
|
||||
|
||||
const value = pipe(
|
||||
wsSurround(quotedString),
|
||||
|
||||
P.alt(() =>
|
||||
pipe(
|
||||
stringTakeUntilChars(["\n"]),
|
||||
P.map(Str.trim)
|
||||
)
|
||||
)
|
||||
stringTakeUntilChars(["\n"]),
|
||||
P.map(Str.trim)
|
||||
)
|
||||
|
||||
const commented = pipe(
|
||||
@@ -128,37 +105,6 @@ const tolerantFile = pipe(
|
||||
|
||||
/* End of Parser Definitions */
|
||||
|
||||
/**
|
||||
* Detect whether the string needs to have escape characters in raw key value strings
|
||||
* @param input The string to check against
|
||||
*/
|
||||
const stringNeedsEscapingForRawKVString = (input: string) => {
|
||||
// If there are any of our special characters, it needs to be escaped definitely
|
||||
if (SPECIAL_CHARS.some((x) => input.includes(x)))
|
||||
return true
|
||||
|
||||
// The theory behind this impl is that if we apply JSON.stringify on a string
|
||||
// it does escaping and then return a JSON string representation.
|
||||
// We remove the quotes of the JSON and see if it can be matched against the input string
|
||||
const stringified = JSON.stringify(input)
|
||||
|
||||
const y = stringified
|
||||
.substring(1, stringified.length - 1)
|
||||
.trim()
|
||||
|
||||
return y !== input
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies Raw Key Value escaping (via quotes + escape chars) if needed
|
||||
* @param input The input to apply escape on
|
||||
* @returns If needed, the escaped string, else the input string itself
|
||||
*/
|
||||
const applyEscapeIfNeeded = (input: string) =>
|
||||
stringNeedsEscapingForRawKVString(input)
|
||||
? JSON.stringify(input)
|
||||
: input
|
||||
|
||||
/**
|
||||
* Converts Raw Key Value Entries to the file string format
|
||||
* @param entries The entries array
|
||||
@@ -167,13 +113,8 @@ const applyEscapeIfNeeded = (input: string) =>
|
||||
export const rawKeyValueEntriesToString = (entries: RawKeyValueEntry[]) =>
|
||||
pipe(
|
||||
entries,
|
||||
A.map(
|
||||
flow(
|
||||
recordUpdate("key", applyEscapeIfNeeded),
|
||||
recordUpdate("value", applyEscapeIfNeeded),
|
||||
({ key, value, active }) =>
|
||||
active ? `${(key)}: ${value}` : `# ${key}: ${value}`
|
||||
)
|
||||
A.map(({ key, value, active }) =>
|
||||
active ? `${key}: ${value}` : `# ${key}: ${value}`
|
||||
),
|
||||
stringArrayJoin("\n")
|
||||
)
|
||||
|
||||
@@ -16,11 +16,6 @@ export type HoppRESTParam = {
|
||||
active: boolean
|
||||
}
|
||||
|
||||
export type HoppRESTVar = {
|
||||
key: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export type HoppRESTHeader = {
|
||||
key: string
|
||||
value: string
|
||||
@@ -56,7 +51,6 @@ export interface HoppRESTRequest {
|
||||
method: string
|
||||
endpoint: string
|
||||
params: HoppRESTParam[]
|
||||
vars: HoppRESTVar[]
|
||||
headers: HoppRESTHeader[]
|
||||
preRequestScript: string
|
||||
testScript: string
|
||||
@@ -80,10 +74,6 @@ export const HoppRESTRequestEq = Eq.struct<HoppRESTRequest>({
|
||||
(arr) => arr.filter((p) => p.key !== "" && p.value !== ""),
|
||||
lodashIsEqualEq
|
||||
),
|
||||
vars: mapThenEq(
|
||||
(arr) => arr.filter((v) => v.key !== "" && v.value !== ""),
|
||||
lodashIsEqualEq
|
||||
),
|
||||
method: S.Eq,
|
||||
name: S.Eq,
|
||||
preRequestScript: S.Eq,
|
||||
@@ -136,9 +126,6 @@ export function safelyExtractRESTRequest(
|
||||
if (x.hasOwnProperty("params") && Array.isArray(x.params))
|
||||
req.params = x.params // TODO: Deep nested checks
|
||||
|
||||
if (x.hasOwnProperty("vars") && Array.isArray(x.vars))
|
||||
req.vars = x.vars // TODO: Deep nested checks
|
||||
|
||||
if (x.hasOwnProperty("headers") && Array.isArray(x.headers))
|
||||
req.headers = x.headers // TODO: Deep nested checks
|
||||
}
|
||||
@@ -199,19 +186,6 @@ export function translateToNewRequest(x: any): HoppRESTRequest {
|
||||
})
|
||||
)
|
||||
|
||||
const vars: HoppRESTVar[] = (x?.vars ?? []).map(
|
||||
({
|
||||
key,
|
||||
value,
|
||||
}: {
|
||||
key: string
|
||||
value: string
|
||||
}) => ({
|
||||
key,
|
||||
value,
|
||||
})
|
||||
)
|
||||
|
||||
const name = x?.name ?? "Untitled request"
|
||||
const method = x?.method ?? ""
|
||||
|
||||
@@ -227,7 +201,6 @@ export function translateToNewRequest(x: any): HoppRESTRequest {
|
||||
endpoint,
|
||||
headers,
|
||||
params,
|
||||
vars,
|
||||
method,
|
||||
preRequestScript,
|
||||
testScript,
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
|
||||
/**
|
||||
* Modify a record value with a mapping function
|
||||
* @param name The key to update
|
||||
* @param func The value to update
|
||||
* @returns The updated record
|
||||
*/
|
||||
export const recordUpdate =
|
||||
<
|
||||
X extends {},
|
||||
K extends keyof X,
|
||||
R
|
||||
>(name: K, func: (input: X[K]) => R) =>
|
||||
(x: X): Omit<X, K> & { [x in K]: R } => ({
|
||||
...x,
|
||||
[name]: func(x[name])
|
||||
})
|
||||
@@ -40,7 +40,7 @@
|
||||
"author": "Hoppscotch (support@hoppscotch.io)",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@hoppscotch/data": "workspace:^0.4.3",
|
||||
"@hoppscotch/data": "workspace:^0.4.2",
|
||||
"fp-ts": "^2.11.10",
|
||||
"lodash": "^4.17.21",
|
||||
"quickjs-emscripten": "^0.15.0",
|
||||
|
||||
376
pnpm-lock.yaml
generated
376
pnpm-lock.yaml
generated
@@ -76,7 +76,7 @@ importers:
|
||||
'@graphql-codegen/urql-introspection': ^2.1.1
|
||||
'@graphql-typed-document-node/core': ^3.1.1
|
||||
'@hoppscotch/codemirror-lang-graphql': workspace:^0.2.0
|
||||
'@hoppscotch/data': workspace:^0.4.3
|
||||
'@hoppscotch/data': workspace:^0.4.2
|
||||
'@hoppscotch/js-sandbox': workspace:^2.0.0
|
||||
'@nuxt/types': ^2.15.8
|
||||
'@nuxt/typescript-build': ^2.1.0
|
||||
@@ -357,7 +357,7 @@ importers:
|
||||
|
||||
packages/hoppscotch-cli:
|
||||
specifiers:
|
||||
'@hoppscotch/data': workspace:^0.4.3
|
||||
'@hoppscotch/data': workspace:^0.4.2
|
||||
'@hoppscotch/js-sandbox': workspace:^2.0.0
|
||||
'@relmify/jest-fp-ts': ^2.0.2
|
||||
'@swc/core': ^1.2.181
|
||||
@@ -427,7 +427,7 @@ importers:
|
||||
packages/hoppscotch-js-sandbox:
|
||||
specifiers:
|
||||
'@digitak/esrun': ^3.1.2
|
||||
'@hoppscotch/data': workspace:^0.4.3
|
||||
'@hoppscotch/data': workspace:^0.4.2
|
||||
'@relmify/jest-fp-ts': ^2.0.1
|
||||
'@types/jest': ^27.4.1
|
||||
'@types/lodash': ^4.14.181
|
||||
@@ -539,13 +539,6 @@ packages:
|
||||
dependencies:
|
||||
'@babel/highlight': 7.17.9
|
||||
|
||||
/@babel/code-frame/7.18.6:
|
||||
resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/highlight': 7.18.6
|
||||
dev: true
|
||||
|
||||
/@babel/compat-data/7.16.0:
|
||||
resolution: {integrity: sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -563,11 +556,6 @@ packages:
|
||||
resolution: {integrity: sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
/@babel/compat-data/7.18.8:
|
||||
resolution: {integrity: sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dev: true
|
||||
|
||||
/@babel/core/7.17.9:
|
||||
resolution: {integrity: sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -590,29 +578,6 @@ packages:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
/@babel/core/7.18.10:
|
||||
resolution: {integrity: sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.1.2
|
||||
'@babel/code-frame': 7.18.6
|
||||
'@babel/generator': 7.18.12
|
||||
'@babel/helper-compilation-targets': 7.18.9_@babel+core@7.18.10
|
||||
'@babel/helper-module-transforms': 7.18.9
|
||||
'@babel/helpers': 7.18.9
|
||||
'@babel/parser': 7.18.11
|
||||
'@babel/template': 7.18.10
|
||||
'@babel/traverse': 7.18.11
|
||||
'@babel/types': 7.18.10
|
||||
convert-source-map: 1.8.0
|
||||
debug: 4.3.4
|
||||
gensync: 1.0.0-beta.2
|
||||
json5: 2.2.1
|
||||
semver: 6.3.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@babel/generator/7.16.5:
|
||||
resolution: {integrity: sha512-kIvCdjZqcdKqoDbVVdt5R99icaRtrtYhYK/xux5qiWCBmfdvEYMFZ68QCrpE5cbFM1JsuArUNs1ZkuKtTtUcZA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -629,15 +594,6 @@ packages:
|
||||
jsesc: 2.5.2
|
||||
source-map: 0.5.7
|
||||
|
||||
/@babel/generator/7.18.12:
|
||||
resolution: {integrity: sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/types': 7.18.10
|
||||
'@jridgewell/gen-mapping': 0.3.2
|
||||
jsesc: 2.5.2
|
||||
dev: true
|
||||
|
||||
/@babel/helper-annotate-as-pure/7.16.7:
|
||||
resolution: {integrity: sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -675,19 +631,6 @@ packages:
|
||||
browserslist: 4.20.2
|
||||
semver: 6.3.0
|
||||
|
||||
/@babel/helper-compilation-targets/7.18.9_@babel+core@7.18.10:
|
||||
resolution: {integrity: sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0
|
||||
dependencies:
|
||||
'@babel/compat-data': 7.18.8
|
||||
'@babel/core': 7.18.10
|
||||
'@babel/helper-validator-option': 7.18.6
|
||||
browserslist: 4.20.2
|
||||
semver: 6.3.0
|
||||
dev: true
|
||||
|
||||
/@babel/helper-create-class-features-plugin/7.16.10_@babel+core@7.17.9:
|
||||
resolution: {integrity: sha512-wDeej0pu3WN/ffTxMNCPW5UCiOav8IcLRxSIyp/9+IF2xJUM9h/OYjg0IJLHaL6F8oU8kqMz9nc1vryXhMsgXg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -773,11 +716,6 @@ packages:
|
||||
dependencies:
|
||||
'@babel/types': 7.17.0
|
||||
|
||||
/@babel/helper-environment-visitor/7.18.9:
|
||||
resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dev: true
|
||||
|
||||
/@babel/helper-explode-assignable-expression/7.16.7:
|
||||
resolution: {integrity: sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -807,14 +745,6 @@ packages:
|
||||
'@babel/template': 7.16.7
|
||||
'@babel/types': 7.17.0
|
||||
|
||||
/@babel/helper-function-name/7.18.9:
|
||||
resolution: {integrity: sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/template': 7.18.10
|
||||
'@babel/types': 7.18.10
|
||||
dev: true
|
||||
|
||||
/@babel/helper-get-function-arity/7.16.0:
|
||||
resolution: {integrity: sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -839,13 +769,6 @@ packages:
|
||||
dependencies:
|
||||
'@babel/types': 7.17.0
|
||||
|
||||
/@babel/helper-hoist-variables/7.18.6:
|
||||
resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/types': 7.18.10
|
||||
dev: true
|
||||
|
||||
/@babel/helper-member-expression-to-functions/7.16.7:
|
||||
resolution: {integrity: sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -865,13 +788,6 @@ packages:
|
||||
dependencies:
|
||||
'@babel/types': 7.17.0
|
||||
|
||||
/@babel/helper-module-imports/7.18.6:
|
||||
resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/types': 7.18.10
|
||||
dev: true
|
||||
|
||||
/@babel/helper-module-transforms/7.16.7:
|
||||
resolution: {integrity: sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -902,22 +818,6 @@ packages:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
/@babel/helper-module-transforms/7.18.9:
|
||||
resolution: {integrity: sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/helper-environment-visitor': 7.18.9
|
||||
'@babel/helper-module-imports': 7.18.6
|
||||
'@babel/helper-simple-access': 7.18.6
|
||||
'@babel/helper-split-export-declaration': 7.18.6
|
||||
'@babel/helper-validator-identifier': 7.18.6
|
||||
'@babel/template': 7.18.10
|
||||
'@babel/traverse': 7.18.11
|
||||
'@babel/types': 7.18.10
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@babel/helper-optimise-call-expression/7.16.7:
|
||||
resolution: {integrity: sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -962,13 +862,6 @@ packages:
|
||||
dependencies:
|
||||
'@babel/types': 7.17.0
|
||||
|
||||
/@babel/helper-simple-access/7.18.6:
|
||||
resolution: {integrity: sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/types': 7.18.10
|
||||
dev: true
|
||||
|
||||
/@babel/helper-skip-transparent-expression-wrappers/7.16.0:
|
||||
resolution: {integrity: sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -987,36 +880,14 @@ packages:
|
||||
dependencies:
|
||||
'@babel/types': 7.17.0
|
||||
|
||||
/@babel/helper-split-export-declaration/7.18.6:
|
||||
resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/types': 7.18.10
|
||||
dev: true
|
||||
|
||||
/@babel/helper-string-parser/7.18.10:
|
||||
resolution: {integrity: sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dev: true
|
||||
|
||||
/@babel/helper-validator-identifier/7.16.7:
|
||||
resolution: {integrity: sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
/@babel/helper-validator-identifier/7.18.6:
|
||||
resolution: {integrity: sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dev: true
|
||||
|
||||
/@babel/helper-validator-option/7.16.7:
|
||||
resolution: {integrity: sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
/@babel/helper-validator-option/7.18.6:
|
||||
resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dev: true
|
||||
|
||||
/@babel/helper-wrap-function/7.16.8:
|
||||
resolution: {integrity: sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -1038,17 +909,6 @@ packages:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
/@babel/helpers/7.18.9:
|
||||
resolution: {integrity: sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/template': 7.18.10
|
||||
'@babel/traverse': 7.18.11
|
||||
'@babel/types': 7.18.10
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@babel/highlight/7.17.9:
|
||||
resolution: {integrity: sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -1057,15 +917,6 @@ packages:
|
||||
chalk: 2.4.2
|
||||
js-tokens: 4.0.0
|
||||
|
||||
/@babel/highlight/7.18.6:
|
||||
resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/helper-validator-identifier': 7.18.6
|
||||
chalk: 2.4.2
|
||||
js-tokens: 4.0.0
|
||||
dev: true
|
||||
|
||||
/@babel/parser/7.16.2:
|
||||
resolution: {integrity: sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
@@ -1089,14 +940,6 @@ packages:
|
||||
dependencies:
|
||||
'@babel/types': 7.17.0
|
||||
|
||||
/@babel/parser/7.18.11:
|
||||
resolution: {integrity: sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@babel/types': 7.18.10
|
||||
dev: true
|
||||
|
||||
/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.16.7_@babel+core@7.17.9:
|
||||
resolution: {integrity: sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -2001,8 +1844,8 @@ packages:
|
||||
dependencies:
|
||||
regenerator-runtime: 0.13.9
|
||||
|
||||
/@babel/standalone/7.18.12:
|
||||
resolution: {integrity: sha512-wDh3K5IUJiSMAY0MLYBFoCaj2RCZwvDz5BHn2uHat9KOsGWEVDFgFQFIOO+81Js2phFKNppLC45iOCsZVfJniw==}
|
||||
/@babel/standalone/7.17.9:
|
||||
resolution: {integrity: sha512-9wL9AtDlga8avxUrBvQJmhUtJWrelsUL0uV+TcP+49Sb6Pj8/bNIzQzU4dDp0NAPOvnZR/7msFIKsKoCl/W1/w==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dev: true
|
||||
|
||||
@@ -2020,15 +1863,6 @@ packages:
|
||||
debug: 4.3.3
|
||||
globals: 11.12.0
|
||||
|
||||
/@babel/template/7.18.10:
|
||||
resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.18.6
|
||||
'@babel/parser': 7.18.11
|
||||
'@babel/types': 7.18.10
|
||||
dev: true
|
||||
|
||||
/@babel/traverse/7.17.3:
|
||||
resolution: {integrity: sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -2064,24 +1898,6 @@ packages:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
/@babel/traverse/7.18.11:
|
||||
resolution: {integrity: sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.18.6
|
||||
'@babel/generator': 7.18.12
|
||||
'@babel/helper-environment-visitor': 7.18.9
|
||||
'@babel/helper-function-name': 7.18.9
|
||||
'@babel/helper-hoist-variables': 7.18.6
|
||||
'@babel/helper-split-export-declaration': 7.18.6
|
||||
'@babel/parser': 7.18.11
|
||||
'@babel/types': 7.18.10
|
||||
debug: 4.3.4
|
||||
globals: 11.12.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@babel/types/7.16.0:
|
||||
resolution: {integrity: sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -2104,15 +1920,6 @@ packages:
|
||||
'@babel/helper-validator-identifier': 7.16.7
|
||||
to-fast-properties: 2.0.0
|
||||
|
||||
/@babel/types/7.18.10:
|
||||
resolution: {integrity: sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/helper-string-parser': 7.18.10
|
||||
'@babel/helper-validator-identifier': 7.18.6
|
||||
to-fast-properties: 2.0.0
|
||||
dev: true
|
||||
|
||||
/@bcoe/v8-coverage/0.2.3:
|
||||
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
|
||||
dev: true
|
||||
@@ -3908,34 +3715,13 @@ packages:
|
||||
chalk: 4.1.2
|
||||
dev: true
|
||||
|
||||
/@jridgewell/gen-mapping/0.3.2:
|
||||
resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
dependencies:
|
||||
'@jridgewell/set-array': 1.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.4.11
|
||||
'@jridgewell/trace-mapping': 0.3.15
|
||||
dev: true
|
||||
|
||||
/@jridgewell/resolve-uri/3.0.5:
|
||||
resolution: {integrity: sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
/@jridgewell/set-array/1.1.2:
|
||||
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
dev: true
|
||||
|
||||
/@jridgewell/sourcemap-codec/1.4.11:
|
||||
resolution: {integrity: sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==}
|
||||
|
||||
/@jridgewell/trace-mapping/0.3.15:
|
||||
resolution: {integrity: sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==}
|
||||
dependencies:
|
||||
'@jridgewell/resolve-uri': 3.0.5
|
||||
'@jridgewell/sourcemap-codec': 1.4.11
|
||||
dev: true
|
||||
|
||||
/@jridgewell/trace-mapping/0.3.4:
|
||||
resolution: {integrity: sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==}
|
||||
dependencies:
|
||||
@@ -4242,12 +4028,12 @@ packages:
|
||||
ufo: 0.7.11
|
||||
dev: false
|
||||
|
||||
/@nuxt/kit-edge/3.0.0-rc.7-27670958.b0bf25c:
|
||||
resolution: {integrity: sha512-4sbICutKR7fOgnva7M6QBdwwDkMFULgaTafZ1oen+av+LTsOJVdNruPjlEKLhn/6gfLmxT3cLuY61jhTz4qP7A==}
|
||||
/@nuxt/kit-edge/3.0.0-rc.4-27605536.8c2c80e:
|
||||
resolution: {integrity: sha512-Fu9ygT3Gi5zbthzZC5PVzaDhVUxLunF1mgfF9b7RoHaO+UoQSWI7AptRwx2jxkUHpftLZjELtDV6MW96xZiWqg==}
|
||||
engines: {node: ^14.16.0 || ^16.11.0 || ^17.0.0 || ^18.0.0}
|
||||
dependencies:
|
||||
'@nuxt/schema': /@nuxt/schema-edge/3.0.0-rc.7-27670958.b0bf25c
|
||||
c12: 0.2.9
|
||||
'@nuxt/schema': /@nuxt/schema-edge/3.0.0-rc.4-27605536.8c2c80e
|
||||
c12: 0.2.7
|
||||
consola: 2.15.3
|
||||
defu: 6.0.0
|
||||
globby: 13.1.2
|
||||
@@ -4256,14 +4042,14 @@ packages:
|
||||
jiti: 1.14.0
|
||||
knitwork: 0.1.2
|
||||
lodash.template: 4.5.0
|
||||
mlly: 0.5.10
|
||||
pathe: 0.3.4
|
||||
mlly: 0.5.3
|
||||
pathe: 0.3.0
|
||||
pkg-types: 0.3.3
|
||||
scule: 0.3.2
|
||||
scule: 0.2.1
|
||||
semver: 7.3.7
|
||||
unctx: 2.0.1
|
||||
unimport: 0.6.7
|
||||
untyped: 0.4.5
|
||||
unctx: 1.1.4
|
||||
unimport: 0.3.0
|
||||
untyped: 0.4.4
|
||||
transitivePeerDependencies:
|
||||
- esbuild
|
||||
- rollup
|
||||
@@ -4296,20 +4082,20 @@ packages:
|
||||
- encoding
|
||||
dev: false
|
||||
|
||||
/@nuxt/schema-edge/3.0.0-rc.7-27670958.b0bf25c:
|
||||
resolution: {integrity: sha512-GwZWyVPqpFWNDsPx1zwczv4DIv2ync/0xTTsec8Rnbg14W83apS9vw2GppHpcDAH7R3Hx8a8pHpeg7nPyD9uCg==}
|
||||
/@nuxt/schema-edge/3.0.0-rc.4-27605536.8c2c80e:
|
||||
resolution: {integrity: sha512-KOFpjN2efx9lXj84kSHhJV/XWJ8n0zztnJjjmEY3RhgBTd7mYtdI7BsYPtZ30Tz5vJGMlHrIGkLZW6c+IYAKzw==}
|
||||
engines: {node: ^14.16.0 || ^16.11.0 || ^17.0.0 || ^18.0.0}
|
||||
dependencies:
|
||||
c12: 0.2.9
|
||||
c12: 0.2.7
|
||||
create-require: 1.1.1
|
||||
defu: 6.0.0
|
||||
jiti: 1.14.0
|
||||
pathe: 0.3.4
|
||||
pathe: 0.3.0
|
||||
postcss-import-resolver: 2.0.0
|
||||
scule: 0.3.2
|
||||
scule: 0.2.1
|
||||
std-env: 3.1.1
|
||||
ufo: 0.8.5
|
||||
unimport: 0.6.7
|
||||
ufo: 0.8.4
|
||||
unimport: 0.3.0
|
||||
transitivePeerDependencies:
|
||||
- esbuild
|
||||
- rollup
|
||||
@@ -6729,12 +6515,6 @@ packages:
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
/acorn/8.8.0:
|
||||
resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/after/0.8.2:
|
||||
resolution: {integrity: sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=}
|
||||
dev: false
|
||||
@@ -7716,15 +7496,15 @@ packages:
|
||||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/c12/0.2.9:
|
||||
resolution: {integrity: sha512-6jYdexgAKr+3kYoTmvC5eDtDHUg7GmFQSdeQqZzAKiPlFAN1heGUoXDbAYYwUCfefZy+WgVJbmAej5TTQpp3jA==}
|
||||
/c12/0.2.7:
|
||||
resolution: {integrity: sha512-ih1nuHbZ6Ltf8Wss96JH6YvKIW5+9+uLAA08LUQAoDrFPGSyvPvQv/QBIRE+dCBWOK4PcwH0ylRkSa9huI1Acw==}
|
||||
dependencies:
|
||||
defu: 6.0.0
|
||||
dotenv: 16.0.1
|
||||
dotenv: 16.0.0
|
||||
gittar: 0.1.1
|
||||
jiti: 1.14.0
|
||||
mlly: 0.5.10
|
||||
pathe: 0.3.4
|
||||
mlly: 0.5.3
|
||||
pathe: 0.2.0
|
||||
rc9: 1.2.2
|
||||
dev: true
|
||||
|
||||
@@ -8470,7 +8250,7 @@ packages:
|
||||
lodash: ^4.17.20
|
||||
marko: ^3.14.4
|
||||
mote: ^0.2.0
|
||||
mustache: ^3.0.0
|
||||
mustache: ^4.0.1
|
||||
nunjucks: ^3.2.2
|
||||
plates: ~0.4.11
|
||||
pug: ^3.0.0
|
||||
@@ -9596,8 +9376,8 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/dotenv/16.0.1:
|
||||
resolution: {integrity: sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==}
|
||||
/dotenv/16.0.0:
|
||||
resolution: {integrity: sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==}
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
@@ -10492,10 +10272,6 @@ packages:
|
||||
/estree-walker/2.0.2:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
|
||||
/estree-walker/3.0.1:
|
||||
resolution: {integrity: sha512-woY0RUD87WzMBUiZLx8NsYr23N5BKsOMZHhu2hoNRVh6NXGfoiT1KOL8G3UHlJAnEDGmfa5ubNA/AacfG+Kb0g==}
|
||||
dev: true
|
||||
|
||||
/esutils/2.0.3:
|
||||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -13856,8 +13632,8 @@ packages:
|
||||
emojis-list: 3.0.0
|
||||
json5: 2.2.1
|
||||
|
||||
/local-pkg/0.4.2:
|
||||
resolution: {integrity: sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==}
|
||||
/local-pkg/0.4.1:
|
||||
resolution: {integrity: sha512-lL87ytIGP2FU5PWwNDo0w3WhIo2gopIAxPg9RxDYF7m4rr5ahuZxP22xnJHIvaLTe4Z9P6uKKY2UHiwyB4pcrw==}
|
||||
engines: {node: '>=14'}
|
||||
dev: true
|
||||
|
||||
@@ -14553,13 +14329,11 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
/mlly/0.5.10:
|
||||
resolution: {integrity: sha512-mY6i+bwcgn0XAdZTiiBt6kyoUjLsm3Cuv0T4CchQJcq/UCSUcGPapSxc4g7whtIsUfcsJ2kGqZAdmqCF/VNC/Q==}
|
||||
/mlly/0.5.3:
|
||||
resolution: {integrity: sha512-im69tuLD9EJh9fc9TZRpJEFvsBcGMez7glUCWDcHWWCKzhvPmNvyaYjp/+h0qJJN/Xovrs//GzGjOOKmFw4Gog==}
|
||||
dependencies:
|
||||
acorn: 8.8.0
|
||||
pathe: 0.3.4
|
||||
pathe: 0.2.0
|
||||
pkg-types: 0.3.3
|
||||
ufo: 0.8.5
|
||||
dev: true
|
||||
|
||||
/mocha/9.2.2:
|
||||
@@ -14933,7 +14707,7 @@ packages:
|
||||
/nuxt-windicss/2.2.11:
|
||||
resolution: {integrity: sha512-xobq725D6vqpIgYOrLJ6CVlR4xLlFGwuq//gZikXKOdoVRpoK8C+NpHazPd4+f17urGQ4H0LqGBCIujTvV1V0g==}
|
||||
dependencies:
|
||||
'@nuxt/kit': /@nuxt/kit-edge/3.0.0-rc.7-27670958.b0bf25c
|
||||
'@nuxt/kit': /@nuxt/kit-edge/3.0.0-rc.4-27605536.8c2c80e
|
||||
'@windicss/plugin-utils': 1.8.4
|
||||
consola: 2.15.3
|
||||
defu: 6.0.0
|
||||
@@ -15511,8 +15285,8 @@ packages:
|
||||
resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==}
|
||||
dev: true
|
||||
|
||||
/pathe/0.3.4:
|
||||
resolution: {integrity: sha512-YWgqEdxf36R6vcsyj0A+yT/rDRPe0wui4J9gRR7T4whjU5Lx/jZOr75ckEgTNaLVQABAwsrlzHRpIKcCdXAQ5A==}
|
||||
/pathe/0.3.0:
|
||||
resolution: {integrity: sha512-3vUjp552BJzCw9vqKsO5sttHkbYqqsZtH0x1PNtItgqx8BXEXzoY1SYRKcL6BTyVh4lGJGLj0tM42elUDMvcYA==}
|
||||
dev: true
|
||||
|
||||
/pause-stream/0.0.11:
|
||||
@@ -15609,8 +15383,8 @@ packages:
|
||||
resolution: {integrity: sha512-6AJcCMnjUQPQv/Wk960w0TOmjhdjbeaQJoSKWRQv9N3rgkessCu6J0Ydsog/nw1MbpnxHuPzYbfOn2KmlZO1FA==}
|
||||
dependencies:
|
||||
jsonc-parser: 3.0.0
|
||||
mlly: 0.5.10
|
||||
pathe: 0.3.4
|
||||
mlly: 0.5.3
|
||||
pathe: 0.3.0
|
||||
dev: true
|
||||
|
||||
/pluralize/8.0.0:
|
||||
@@ -17346,11 +17120,6 @@ packages:
|
||||
|
||||
/scule/0.2.1:
|
||||
resolution: {integrity: sha512-M9gnWtn3J0W+UhJOHmBxBTwv8mZCan5i1Himp60t6vvZcor0wr+IM0URKmIglsWJ7bRujNAVVN77fp+uZaWoKg==}
|
||||
dev: false
|
||||
|
||||
/scule/0.3.2:
|
||||
resolution: {integrity: sha512-zIvPdjOH8fv8CgrPT5eqtxHQXmPNnV/vHJYffZhE43KZkvULvpCTvOt1HPlFaCZx287INL9qaqrZg34e8NgI4g==}
|
||||
dev: true
|
||||
|
||||
/selenium-webdriver/4.0.0-rc-1:
|
||||
resolution: {integrity: sha512-bcrwFPRax8fifRP60p7xkWDGSJJoMkPAzufMlk5K2NyLPht/YZzR2WcIk1+3gR8VOCLlst1P2PI+MXACaFzpIw==}
|
||||
@@ -18165,7 +17934,7 @@ packages:
|
||||
/strip-literal/0.4.0:
|
||||
resolution: {integrity: sha512-ql/sBDoJOybTKSIOWrrh8kgUEMjXMwRAkZTD0EwiwxQH/6tTPkZvMIEjp0CRlpi6V5FMiJyvxeRkEi1KrGISoA==}
|
||||
dependencies:
|
||||
acorn: 8.8.0
|
||||
acorn: 8.7.1
|
||||
dev: true
|
||||
|
||||
/style-mod/4.0.0:
|
||||
@@ -19270,8 +19039,8 @@ packages:
|
||||
/ufo/0.8.3:
|
||||
resolution: {integrity: sha512-AIkk06G21y/P+NCatfU+1qldCmI0XCszZLn8AkuKotffF3eqCvlce0KuwM7ZemLE/my0GSYADOAeM5zDYWMB+A==}
|
||||
|
||||
/ufo/0.8.5:
|
||||
resolution: {integrity: sha512-e4+UtA5IRO+ha6hYklwj6r7BjiGMxS0O+UaSg9HbaTefg4kMkzj4tXzEBajRR+wkxf+golgAWKzLbytCUDMJAA==}
|
||||
/ufo/0.8.4:
|
||||
resolution: {integrity: sha512-/+BmBDe8GvlB2nIflWasLLAInjYG0bC9HRnfEpNi4sw77J2AJNnEVnTDReVrehoh825+Q/evF3THXTAweyam2g==}
|
||||
dev: true
|
||||
|
||||
/uglify-js/3.14.3:
|
||||
@@ -19293,13 +19062,13 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/unctx/2.0.1:
|
||||
resolution: {integrity: sha512-4VkJKSG+lh1yYkvdI0Xd3Gm7y7PU6F0mG5SoJqCI1j2jtIaHvTLAdBfbhDjbHxT93BsRkzcaxaeBtu8W/mX1Sg==}
|
||||
/unctx/1.1.4:
|
||||
resolution: {integrity: sha512-fQMML+GjUpIjQa0HBrrJezo2dFpTAbQbU0/KFKw4T5wpc9deGjLHSYthdfNAo2xSWM34csI6arzedezQkqtfGw==}
|
||||
dependencies:
|
||||
acorn: 8.8.0
|
||||
estree-walker: 3.0.1
|
||||
acorn: 8.7.1
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.26.2
|
||||
unplugin: 0.8.1
|
||||
unplugin: 0.6.3
|
||||
transitivePeerDependencies:
|
||||
- esbuild
|
||||
- rollup
|
||||
@@ -19344,19 +19113,19 @@ packages:
|
||||
engines: {node: '>= 0.4.12'}
|
||||
dev: true
|
||||
|
||||
/unimport/0.6.7:
|
||||
resolution: {integrity: sha512-EMoVqDjswHkU+nD098QYHXH7Mkw7KwGDQAyeRF2lgairJnuO+wpkhIcmCqrD1OPJmsjkTbJ2tW6Ap8St0PuWZA==}
|
||||
/unimport/0.3.0:
|
||||
resolution: {integrity: sha512-RxvfvKBY+CyBmIuYSuBeosSiudgcVakdhVofy5mO5sJ3purQRc5yjLw0Lir7MKHnqe6XT1++8flgAvpxu1UkqQ==}
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 4.2.1
|
||||
escape-string-regexp: 5.0.0
|
||||
fast-glob: 3.2.11
|
||||
local-pkg: 0.4.2
|
||||
local-pkg: 0.4.1
|
||||
magic-string: 0.26.2
|
||||
mlly: 0.5.10
|
||||
pathe: 0.3.4
|
||||
scule: 0.3.2
|
||||
mlly: 0.5.3
|
||||
pathe: 0.3.0
|
||||
scule: 0.2.1
|
||||
strip-literal: 0.4.0
|
||||
unplugin: 0.9.0
|
||||
unplugin: 0.7.0
|
||||
transitivePeerDependencies:
|
||||
- esbuild
|
||||
- rollup
|
||||
@@ -19467,12 +19236,12 @@ packages:
|
||||
webpack-virtual-modules: 0.4.3
|
||||
dev: false
|
||||
|
||||
/unplugin/0.8.1:
|
||||
resolution: {integrity: sha512-o7rUZoPLG1fH4LKinWgb77gDtTE6mw/iry0Pq0Z5UPvZ9+HZ1/4+7fic7t58s8/CGkPrDpGq+RltO+DmswcR4g==}
|
||||
/unplugin/0.6.3:
|
||||
resolution: {integrity: sha512-CoW88FQfCW/yabVc4bLrjikN9HC8dEvMU4O7B6K2jsYMPK0l6iAnd9dpJwqGcmXJKRCU9vwSsy653qg+RK0G6A==}
|
||||
peerDependencies:
|
||||
esbuild: '>=0.13'
|
||||
rollup: ^2.50.0
|
||||
vite: ^2.3.0 || ^3.0.0-0
|
||||
vite: ^2.3.0
|
||||
webpack: 4 || 5
|
||||
peerDependenciesMeta:
|
||||
esbuild:
|
||||
@@ -19484,18 +19253,17 @@ packages:
|
||||
webpack:
|
||||
optional: true
|
||||
dependencies:
|
||||
acorn: 8.8.0
|
||||
chokidar: 3.5.3
|
||||
webpack-sources: 3.2.3
|
||||
webpack-virtual-modules: 0.4.4
|
||||
webpack-virtual-modules: 0.4.3
|
||||
dev: true
|
||||
|
||||
/unplugin/0.9.0:
|
||||
resolution: {integrity: sha512-6o7q8Y9yxdPi5yCPmRuFfeNnVzGumRNZSK6hIkvZ6hd0cfigVdm0qBx/GgQ/NEjs54eUV1qTjvMYKRs9yh3rzw==}
|
||||
/unplugin/0.7.0:
|
||||
resolution: {integrity: sha512-OsiFrgybmqm5bGuaodvbLYhqUrvGuRHRMZDhddKEXTDbuQ1x+hR7M1WpQguXj03whVYjEYChhFo738cZH5RNig==}
|
||||
peerDependencies:
|
||||
esbuild: '>=0.13'
|
||||
rollup: ^2.50.0
|
||||
vite: ^2.3.0 || ^3.0.0-0
|
||||
vite: ^2.3.0
|
||||
webpack: 4 || 5
|
||||
peerDependenciesMeta:
|
||||
esbuild:
|
||||
@@ -19507,10 +19275,10 @@ packages:
|
||||
webpack:
|
||||
optional: true
|
||||
dependencies:
|
||||
acorn: 8.8.0
|
||||
acorn: 8.7.1
|
||||
chokidar: 3.5.3
|
||||
webpack-sources: 3.2.3
|
||||
webpack-virtual-modules: 0.4.4
|
||||
webpack-virtual-modules: 0.4.3
|
||||
dev: true
|
||||
|
||||
/unquote/1.1.1:
|
||||
@@ -19523,13 +19291,13 @@ packages:
|
||||
has-value: 0.3.1
|
||||
isobject: 3.0.1
|
||||
|
||||
/untyped/0.4.5:
|
||||
resolution: {integrity: sha512-buq9URfOj4xAnVfu6BYNKzHZLHAzsCbHsDc/kHy66ESMqRpj00oD9qWf2M2qm0pC0DigsVxRF3uhOa5HJtrwGA==}
|
||||
/untyped/0.4.4:
|
||||
resolution: {integrity: sha512-sY6u8RedwfLfBis0copfU/fzROieyAndqPs8Kn2PfyzTjtA88vCk81J1b5z+8/VJc+cwfGy23/AqOCpvAbkNVw==}
|
||||
dependencies:
|
||||
'@babel/core': 7.18.10
|
||||
'@babel/standalone': 7.18.12
|
||||
'@babel/types': 7.18.10
|
||||
scule: 0.3.2
|
||||
'@babel/core': 7.17.9
|
||||
'@babel/standalone': 7.17.9
|
||||
'@babel/types': 7.17.0
|
||||
scule: 0.2.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -20156,10 +19924,6 @@ packages:
|
||||
/webpack-virtual-modules/0.4.3:
|
||||
resolution: {integrity: sha512-5NUqC2JquIL2pBAAo/VfBP6KuGkHIZQXW/lNKupLPfhViwh8wNsu0BObtl09yuKZszeEUfbXz8xhrHvSG16Nqw==}
|
||||
|
||||
/webpack-virtual-modules/0.4.4:
|
||||
resolution: {integrity: sha512-h9atBP/bsZohWpHnr+2sic8Iecb60GxftXsWNLLLSqewgIsGzByd2gcIID4nXcG+3tNe4GQG3dLcff3kXupdRA==}
|
||||
dev: true
|
||||
|
||||
/webpack/4.46.0:
|
||||
resolution: {integrity: sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==}
|
||||
engines: {node: '>=6.11.5'}
|
||||
|
||||
Reference in New Issue
Block a user