refactor: typescript support
This commit is contained in:
@@ -17,14 +17,13 @@
|
|||||||
{{ title }}
|
{{ title }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div ref="log" name="log" class="realtime-log">
|
<div name="log" class="realtime-log">
|
||||||
<span v-if="log" class="space-y-2">
|
<span v-if="log" class="space-y-2">
|
||||||
<span
|
<span
|
||||||
v-for="(entry, index) in log"
|
v-for="(entry, index) in log"
|
||||||
:key="`entry-${index}`"
|
:key="`entry-${index}`"
|
||||||
:style="{ color: entry.color }"
|
:style="{ color: entry.color }"
|
||||||
>{{ entry.ts }}{{ getSourcePrefix(entry.source)
|
>{{ entry.ts }}{{ source(entry.source) }}{{ entry.payload }}</span
|
||||||
}}{{ entry.payload }}</span
|
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
<span v-else>{{ $t("response.waiting_for_connection") }}</span>
|
<span v-else>{{ $t("response.waiting_for_connection") }}</span>
|
||||||
@@ -32,27 +31,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { defineComponent } from "@nuxtjs/composition-api"
|
import { getSourcePrefix as source } from "~/helpers/utils/string"
|
||||||
import { getSourcePrefix } from "~/helpers/utils/string"
|
|
||||||
|
|
||||||
export default defineComponent({
|
defineProps({
|
||||||
props: {
|
log: { type: Array, default: () => [] },
|
||||||
log: { type: Array, default: () => [] },
|
title: {
|
||||||
title: {
|
type: String,
|
||||||
type: String,
|
default: "",
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
updated() {
|
|
||||||
this.$nextTick(function () {
|
|
||||||
if (this.$refs.log) {
|
|
||||||
this.$refs.log.scrollBy(0, this.$refs.log.scrollHeight + 100)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getSourcePrefix,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ import { defineComponent } from "@nuxtjs/composition-api"
|
|||||||
import { Splitpanes, Pane } from "splitpanes"
|
import { Splitpanes, Pane } from "splitpanes"
|
||||||
import "splitpanes/dist/splitpanes.css"
|
import "splitpanes/dist/splitpanes.css"
|
||||||
import Paho from "paho-mqtt"
|
import Paho from "paho-mqtt"
|
||||||
import debounce from "~/helpers/utils/debounce"
|
import debounce from "lodash/debounce"
|
||||||
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
|
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
|
||||||
import { useSetting } from "~/newstore/settings"
|
import { useSetting } from "~/newstore/settings"
|
||||||
import useWindowSize from "~/helpers/utils/useWindowSize"
|
import useWindowSize from "~/helpers/utils/useWindowSize"
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ import { Splitpanes, Pane } from "splitpanes"
|
|||||||
import "splitpanes/dist/splitpanes.css"
|
import "splitpanes/dist/splitpanes.css"
|
||||||
import { io as Client } from "socket.io-client"
|
import { io as Client } from "socket.io-client"
|
||||||
import wildcard from "socketio-wildcard"
|
import wildcard from "socketio-wildcard"
|
||||||
import debounce from "~/helpers/utils/debounce"
|
import debounce from "lodash/debounce"
|
||||||
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
|
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
|
||||||
import { useSetting } from "~/newstore/settings"
|
import { useSetting } from "~/newstore/settings"
|
||||||
import useWindowSize from "~/helpers/utils/useWindowSize"
|
import useWindowSize from "~/helpers/utils/useWindowSize"
|
||||||
|
|||||||
@@ -89,8 +89,8 @@
|
|||||||
import { defineComponent } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import { Splitpanes, Pane } from "splitpanes"
|
import { Splitpanes, Pane } from "splitpanes"
|
||||||
import "splitpanes/dist/splitpanes.css"
|
import "splitpanes/dist/splitpanes.css"
|
||||||
|
import debounce from "lodash/debounce"
|
||||||
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
|
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
|
||||||
import debounce from "~/helpers/utils/debounce"
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { Splitpanes, Pane },
|
components: { Splitpanes, Pane },
|
||||||
|
|||||||
@@ -205,8 +205,8 @@
|
|||||||
import { defineComponent } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import { Splitpanes, Pane } from "splitpanes"
|
import { Splitpanes, Pane } from "splitpanes"
|
||||||
import "splitpanes/dist/splitpanes.css"
|
import "splitpanes/dist/splitpanes.css"
|
||||||
|
import debounce from "lodash/debounce"
|
||||||
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
|
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
|
||||||
import debounce from "~/helpers/utils/debounce"
|
|
||||||
import useWindowSize from "~/helpers/utils/useWindowSize"
|
import useWindowSize from "~/helpers/utils/useWindowSize"
|
||||||
import { useSetting } from "~/newstore/settings"
|
import { useSetting } from "~/newstore/settings"
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
const htmlLens = {
|
import { Lens } from "./lenses"
|
||||||
|
|
||||||
|
const htmlLens: Lens = {
|
||||||
lensName: "response.html",
|
lensName: "response.html",
|
||||||
isSupportedContentType: (contentType) =>
|
isSupportedContentType: (contentType) =>
|
||||||
/\btext\/html|application\/xhtml\+xml\b/i.test(contentType),
|
/\btext\/html|application\/xhtml\+xml\b/i.test(contentType),
|
||||||
renderer: "htmlres",
|
renderer: "htmlres",
|
||||||
rendererImport: () =>
|
rendererImport: () =>
|
||||||
import("~/components/lenses/renderers/HTMLLensRenderer"),
|
import("~/components/lenses/renderers/HTMLLensRenderer.vue"),
|
||||||
}
|
}
|
||||||
|
|
||||||
export default htmlLens
|
export default htmlLens
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
const imageLens = {
|
import { Lens } from "./lenses"
|
||||||
|
|
||||||
|
const imageLens: Lens = {
|
||||||
lensName: "response.image",
|
lensName: "response.image",
|
||||||
isSupportedContentType: (contentType) =>
|
isSupportedContentType: (contentType) =>
|
||||||
/\bimage\/(?:gif|jpeg|png|bmp|svg\+xml|x-icon|vnd\.microsoft\.icon)\b/i.test(
|
/\bimage\/(?:gif|jpeg|png|bmp|svg\+xml|x-icon|vnd\.microsoft\.icon)\b/i.test(
|
||||||
@@ -6,7 +8,7 @@ const imageLens = {
|
|||||||
),
|
),
|
||||||
renderer: "imageres",
|
renderer: "imageres",
|
||||||
rendererImport: () =>
|
rendererImport: () =>
|
||||||
import("~/components/lenses/renderers/ImageLensRenderer"),
|
import("~/components/lenses/renderers/ImageLensRenderer.vue"),
|
||||||
}
|
}
|
||||||
|
|
||||||
export default imageLens
|
export default imageLens
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
import { isJSONContentType } from "../utils/contenttypes"
|
import { isJSONContentType } from "../utils/contenttypes"
|
||||||
|
import { Lens } from "./lenses"
|
||||||
|
|
||||||
const jsonLens = {
|
const jsonLens: Lens = {
|
||||||
lensName: "response.json",
|
lensName: "response.json",
|
||||||
isSupportedContentType: isJSONContentType,
|
isSupportedContentType: isJSONContentType,
|
||||||
renderer: "json",
|
renderer: "json",
|
||||||
rendererImport: () =>
|
rendererImport: () =>
|
||||||
import("~/components/lenses/renderers/JSONLensRenderer"),
|
import("~/components/lenses/renderers/JSONLensRenderer.vue"),
|
||||||
}
|
}
|
||||||
|
|
||||||
export default jsonLens
|
export default jsonLens
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import jsonLens from "./jsonLens"
|
|
||||||
import rawLens from "./rawLens"
|
|
||||||
import imageLens from "./imageLens"
|
|
||||||
import htmlLens from "./htmlLens"
|
|
||||||
import xmlLens from "./xmlLens"
|
|
||||||
|
|
||||||
export const lenses = [jsonLens, imageLens, htmlLens, xmlLens, rawLens]
|
|
||||||
|
|
||||||
export function getSuitableLenses(response) {
|
|
||||||
const contentType = response.headers.find((h) => h.key === "content-type")
|
|
||||||
|
|
||||||
if (!contentType) return [rawLens]
|
|
||||||
|
|
||||||
const result = []
|
|
||||||
for (const lens of lenses) {
|
|
||||||
if (lens.isSupportedContentType(contentType.value)) result.push(lens)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLensRenderers() {
|
|
||||||
const response = {}
|
|
||||||
for (const lens of lenses) {
|
|
||||||
response[lens.renderer] = lens.rendererImport
|
|
||||||
}
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
42
helpers/lenses/lenses.ts
Normal file
42
helpers/lenses/lenses.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { HoppRESTResponse } from "../types/HoppRESTResponse"
|
||||||
|
import jsonLens from "./jsonLens"
|
||||||
|
import rawLens from "./rawLens"
|
||||||
|
import imageLens from "./imageLens"
|
||||||
|
import htmlLens from "./htmlLens"
|
||||||
|
import xmlLens from "./xmlLens"
|
||||||
|
|
||||||
|
export type Lens = {
|
||||||
|
lensName: string
|
||||||
|
isSupportedContentType: (contentType: string) => boolean
|
||||||
|
renderer: string
|
||||||
|
rendererImport: () => Promise<typeof import("*.vue")>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const lenses: Lens[] = [jsonLens, imageLens, htmlLens, xmlLens, rawLens]
|
||||||
|
|
||||||
|
export function getSuitableLenses(response: HoppRESTResponse): Lens[] {
|
||||||
|
// return empty array if response is loading or error
|
||||||
|
if (response.type === "loading" || response.type === "network_fail") return []
|
||||||
|
|
||||||
|
const contentType = response.headers.find((h) => h.key === "content-type")
|
||||||
|
|
||||||
|
if (!contentType) return [rawLens]
|
||||||
|
|
||||||
|
const result = []
|
||||||
|
for (const lens of lenses) {
|
||||||
|
if (lens.isSupportedContentType(contentType.value)) result.push(lens)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
type LensRenderers = {
|
||||||
|
[key: string]: Lens["rendererImport"]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLensRenderers(): LensRenderers {
|
||||||
|
const response: LensRenderers = {}
|
||||||
|
for (const lens of lenses) {
|
||||||
|
response[lens.renderer] = lens.rendererImport
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
const rawLens = {
|
|
||||||
lensName: "response.raw",
|
|
||||||
isSupportedContentType: () => true,
|
|
||||||
renderer: "raw",
|
|
||||||
rendererImport: () => import("~/components/lenses/renderers/RawLensRenderer"),
|
|
||||||
}
|
|
||||||
|
|
||||||
export default rawLens
|
|
||||||
11
helpers/lenses/rawLens.ts
Normal file
11
helpers/lenses/rawLens.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Lens } from "./lenses"
|
||||||
|
|
||||||
|
const rawLens: Lens = {
|
||||||
|
lensName: "response.raw",
|
||||||
|
isSupportedContentType: () => true,
|
||||||
|
renderer: "raw",
|
||||||
|
rendererImport: () =>
|
||||||
|
import("~/components/lenses/renderers/RawLensRenderer.vue"),
|
||||||
|
}
|
||||||
|
|
||||||
|
export default rawLens
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
const xmlLens = {
|
import { Lens } from "./lenses"
|
||||||
|
|
||||||
|
const xmlLens: Lens = {
|
||||||
lensName: "response.xml",
|
lensName: "response.xml",
|
||||||
isSupportedContentType: (contentType) => /\bxml\b/i.test(contentType),
|
isSupportedContentType: (contentType) => /\bxml\b/i.test(contentType),
|
||||||
renderer: "xmlres",
|
renderer: "xmlres",
|
||||||
rendererImport: () => import("~/components/lenses/renderers/XMLLensRenderer"),
|
rendererImport: () =>
|
||||||
|
import("~/components/lenses/renderers/XMLLensRenderer.vue"),
|
||||||
}
|
}
|
||||||
|
|
||||||
export default xmlLens
|
export default xmlLens
|
||||||
@@ -8,9 +8,9 @@ import { map } from "rxjs/operators"
|
|||||||
*
|
*
|
||||||
* @returns The constructed object observable
|
* @returns The constructed object observable
|
||||||
*/
|
*/
|
||||||
export function constructFromStreams<T>(
|
export function constructFromStreams<T>(streamObj: {
|
||||||
streamObj: { [key in keyof T]: Observable<T[key]> }
|
[key in keyof T]: Observable<T[key]>
|
||||||
): Observable<T> {
|
}): Observable<T> {
|
||||||
return combineLatest(Object.values<Observable<T[keyof T]>>(streamObj)).pipe(
|
return combineLatest(Object.values<Observable<T[keyof T]>>(streamObj)).pipe(
|
||||||
map((streams) => {
|
map((streams) => {
|
||||||
const keys = Object.keys(streamObj) as (keyof T)[]
|
const keys = Object.keys(streamObj) as (keyof T)[]
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
import debounce from "../debounce"
|
|
||||||
|
|
||||||
describe("debounce", () => {
|
|
||||||
test("doesn't call function right after calling", () => {
|
|
||||||
const fn = jest.fn()
|
|
||||||
|
|
||||||
const debFunc = debounce(fn, 100)
|
|
||||||
debFunc()
|
|
||||||
|
|
||||||
expect(fn).not.toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
test("calls the function after the given timeout", () => {
|
|
||||||
const fn = jest.fn()
|
|
||||||
|
|
||||||
jest.useFakeTimers()
|
|
||||||
|
|
||||||
const debFunc = debounce(fn, 100)
|
|
||||||
debFunc()
|
|
||||||
|
|
||||||
jest.runAllTimers()
|
|
||||||
|
|
||||||
expect(fn).toHaveBeenCalled()
|
|
||||||
// expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 100)
|
|
||||||
})
|
|
||||||
|
|
||||||
test("calls the function only one time within the timeframe", () => {
|
|
||||||
const fn = jest.fn()
|
|
||||||
|
|
||||||
const debFunc = debounce(fn, 1000)
|
|
||||||
|
|
||||||
for (let i = 0; i < 100; i++) debFunc()
|
|
||||||
|
|
||||||
jest.runAllTimers()
|
|
||||||
|
|
||||||
expect(fn).toHaveBeenCalledTimes(1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { parseUrlAndPath } from "../uri"
|
|
||||||
|
|
||||||
describe("parseUrlAndPath", () => {
|
|
||||||
test("has url and path fields", () => {
|
|
||||||
const result = parseUrlAndPath("https://hoppscotch.io/")
|
|
||||||
|
|
||||||
expect(result).toHaveProperty("url")
|
|
||||||
expect(result).toHaveProperty("path")
|
|
||||||
})
|
|
||||||
|
|
||||||
test("parses out URL correctly", () => {
|
|
||||||
const result = parseUrlAndPath("https://hoppscotch.io/test/page")
|
|
||||||
|
|
||||||
expect(result.url).toBe("https://hoppscotch.io")
|
|
||||||
})
|
|
||||||
test("parses out Path correctly", () => {
|
|
||||||
const result = parseUrlAndPath("https://hoppscotch.io/test/page")
|
|
||||||
|
|
||||||
expect(result.path).toBe("/test/page")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export const decodeB64StringToArrayBuffer = (input) => {
|
export function decodeB64StringToArrayBuffer(input: string): ArrayBuffer {
|
||||||
const bytes = Math.floor((input.length / 4) * 3)
|
const bytes = Math.floor((input.length / 4) * 3)
|
||||||
const ab = new ArrayBuffer(bytes)
|
const ab = new ArrayBuffer(bytes)
|
||||||
const uarray = new Uint8Array(ab)
|
const uarray = new Uint8Array(ab)
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
// Debounce is a higher order function which makes its enclosed function be executed
|
|
||||||
// only if the function wasn't called again till 'delay' time has passed, this helps reduce impact of heavy working
|
|
||||||
// functions which might be called frequently
|
|
||||||
// NOTE : Don't use lambda functions as this doesn't get bound properly in them, use the 'function (args) {}' format
|
|
||||||
const debounce = (func, delay) => {
|
|
||||||
let inDebounce
|
|
||||||
return function () {
|
|
||||||
const context = this
|
|
||||||
const args = arguments
|
|
||||||
clearTimeout(inDebounce)
|
|
||||||
inDebounce = setTimeout(() => func.apply(context, args), delay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default debounce
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
export function getSourcePrefix(source) {
|
|
||||||
const sourceEmojis = {
|
|
||||||
// Source used for info messages.
|
|
||||||
info: "\tℹ️ [INFO]:\t",
|
|
||||||
// Source used for client to server messages.
|
|
||||||
client: "\t⬅️ [SENT]:\t",
|
|
||||||
// Source used for server to client messages.
|
|
||||||
server: "\t➡️ [RECEIVED]:\t",
|
|
||||||
}
|
|
||||||
if (Object.keys(sourceEmojis).includes(source)) return sourceEmojis[source]
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
12
helpers/utils/string.ts
Normal file
12
helpers/utils/string.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const sourceEmojis = {
|
||||||
|
// Source used for info messages.
|
||||||
|
info: "\tℹ️ [INFO]:\t",
|
||||||
|
// Source used for client to server messages.
|
||||||
|
client: "\t⬅️ [SENT]:\t",
|
||||||
|
// Source used for server to client messages.
|
||||||
|
server: "\t➡️ [RECEIVED]:\t",
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSourcePrefix(source: keyof typeof sourceEmojis) {
|
||||||
|
return sourceEmojis[source]
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
export function parseUrlAndPath(value) {
|
|
||||||
const result = {}
|
|
||||||
try {
|
|
||||||
const url = new URL(value)
|
|
||||||
result.url = url.origin
|
|
||||||
result.path = url.pathname
|
|
||||||
} catch (e) {
|
|
||||||
const uriRegex = value.match(
|
|
||||||
/^((http[s]?:\/\/)?(<<[^/]+>>)?[^/]*|)(\/?.*)$/
|
|
||||||
)
|
|
||||||
result.url = uriRegex[1]
|
|
||||||
result.path = uriRegex[4]
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user