feat: migrate ESLint to v9 across packages (#5773)
Co-authored-by: curiouscorrelation <curiouscorrelation@gmail.com>
This commit is contained in:
parent
992579e285
commit
27b817f627
88 changed files with 3223 additions and 3263 deletions
67
packages/hoppscotch-agent/eslint.config.mjs
Normal file
67
packages/hoppscotch-agent/eslint.config.mjs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import pluginVue from "eslint-plugin-vue"
|
||||
import {
|
||||
defineConfigWithVueTs,
|
||||
vueTsConfigs,
|
||||
} from "@vue/eslint-config-typescript"
|
||||
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"
|
||||
import globals from "globals"
|
||||
|
||||
export default defineConfigWithVueTs(
|
||||
{
|
||||
ignores: [
|
||||
"**/*.d.ts",
|
||||
"dist/**",
|
||||
"node_modules/**",
|
||||
"src-tauri/**",
|
||||
],
|
||||
},
|
||||
pluginVue.configs["flat/recommended"],
|
||||
vueTsConfigs.recommended,
|
||||
eslintPluginPrettierRecommended,
|
||||
{
|
||||
files: ["**/*.ts", "**/*.js", "**/*.vue"],
|
||||
linterOptions: {
|
||||
reportUnusedDisableDirectives: false,
|
||||
},
|
||||
languageOptions: {
|
||||
sourceType: "module",
|
||||
ecmaVersion: "latest",
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
parserOptions: {
|
||||
requireConfigFile: false,
|
||||
ecmaFeatures: {
|
||||
jsx: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
semi: [2, "never"],
|
||||
"no-console": "off",
|
||||
"no-debugger": process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
"prettier/prettier":
|
||||
process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
"vue/multi-word-component-names": "off",
|
||||
"vue/no-side-effects-in-computed-properties": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
{
|
||||
args: "all",
|
||||
argsIgnorePattern: "^_",
|
||||
caughtErrors: "all",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
destructuredArrayIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
ignoreRestSiblings: true,
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/no-unused-expressions": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-unsafe-function-type": "off",
|
||||
"no-undef": "off",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
|
@ -7,7 +7,14 @@
|
|||
"dev": "vite",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview",
|
||||
"tauri": "tauri"
|
||||
"tauri": "tauri",
|
||||
"lint": "eslint src",
|
||||
"lint:ts": "vue-tsc --noEmit",
|
||||
"lintfix": "eslint --fix src",
|
||||
"prod-lint": "cross-env HOPP_LINT_FOR_PROD=true pnpm run lint",
|
||||
"do-lint": "pnpm run prod-lint",
|
||||
"do-typecheck": "pnpm run lint:ts",
|
||||
"do-lintfix": "pnpm run lintfix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hoppscotch/ui": "0.2.5",
|
||||
|
|
@ -24,8 +31,16 @@
|
|||
"@tauri-apps/cli": "2.9.3",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/node": "24.10.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.50.0",
|
||||
"@typescript-eslint/parser": "8.50.0",
|
||||
"@vitejs/plugin-vue": "6.0.3",
|
||||
"@vue/eslint-config-typescript": "14.6.0",
|
||||
"autoprefixer": "10.4.23",
|
||||
"cross-env": "10.1.0",
|
||||
"eslint": "9.39.2",
|
||||
"eslint-plugin-prettier": "5.5.4",
|
||||
"eslint-plugin-vue": "10.6.2",
|
||||
"globals": "16.5.0",
|
||||
"postcss": "8.5.6",
|
||||
"tailwindcss": "3.4.16",
|
||||
"typescript": "5.9.3",
|
||||
|
|
|
|||
|
|
@ -5,21 +5,33 @@
|
|||
<template v-if="O.isSome(state().otp)">
|
||||
<div class="flex-grow">
|
||||
<p class="tracking-wide">
|
||||
An app is trying to register against the Hoppscotch Agent. If this was intentional, copy the given code into
|
||||
the app to complete the registration process. Please cancel the registration if you did not initiate this request.
|
||||
The window will hide automatically once registration succeeds. If you minimize this window during registration,
|
||||
you can access it again from the tray by selecting "Maximize Window".
|
||||
An app is trying to register against the Hoppscotch Agent. If this was
|
||||
intentional, copy the given code into the app to complete the
|
||||
registration process. Please cancel the registration if you did not
|
||||
initiate this request. The window will hide automatically once
|
||||
registration succeeds. If you minimize this window during
|
||||
registration, you can access it again from the tray by selecting
|
||||
"Maximize Window".
|
||||
</p>
|
||||
<p
|
||||
class="font-bold text-5xl tracking-wider text-center pt-10 text-white"
|
||||
>{{ pipe(state().otp, O.getOrElse(() => "")) }}</p>
|
||||
>
|
||||
{{
|
||||
pipe(
|
||||
state().otp,
|
||||
O.getOrElse(() => "")
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div class="flex-grow overflow-auto">
|
||||
<HoppSmartTable :headings="tableHeadings" :list="state().registrations">
|
||||
<template #registered_at="{ item }">{{ formatDate(String(item.registered_at)) }}</template>
|
||||
<template #registered_at="{ item }">{{
|
||||
formatDate(String(item.registered_at))
|
||||
}}</template>
|
||||
</HoppSmartTable>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -65,9 +77,9 @@ import {
|
|||
HoppButtonSecondary,
|
||||
HoppSmartTable,
|
||||
} from "@hoppscotch/ui"
|
||||
// @ts-ignore
|
||||
// @ts-expect-error - Icon import has no types
|
||||
import IconCopy from "~icons/lucide/copy"
|
||||
// @ts-ignore
|
||||
// @ts-expect-error - Icon import has no types
|
||||
import IconCheck from "~icons/lucide/check"
|
||||
import { useClipboard, refAutoReset } from "@vueuse/core"
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window"
|
||||
|
|
@ -183,7 +195,7 @@ onMounted(async () => {
|
|||
if (otp) {
|
||||
appState.value = { ...state(), otp: O.some(otp) }
|
||||
} else {
|
||||
updateRegistrations();
|
||||
updateRegistrations()
|
||||
}
|
||||
})
|
||||
)()
|
||||
|
|
@ -204,12 +216,12 @@ onMounted(async () => {
|
|||
getOtp,
|
||||
TE.map((otp: string) => {
|
||||
if (otp) {
|
||||
appState.value = { ...state(), otp: O.some(otp) };
|
||||
appState.value = { ...state(), otp: O.some(otp) }
|
||||
} else {
|
||||
updateRegistrations();
|
||||
updateRegistrations()
|
||||
}
|
||||
})
|
||||
)();
|
||||
)()
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import './index.css'
|
||||
import { createApp } from "vue"
|
||||
import App from "./App.vue"
|
||||
import "./index.css"
|
||||
|
||||
import { plugin as HoppUI } from "@hoppscotch/ui"
|
||||
|
||||
|
|
@ -8,6 +8,4 @@ import "@hoppscotch/ui/themes.css"
|
|||
|
||||
import "@hoppscotch/ui/style.css"
|
||||
|
||||
createApp(App)
|
||||
.use(HoppUI)
|
||||
.mount('#app')
|
||||
createApp(App).use(HoppUI).mount("#app")
|
||||
|
|
|
|||
|
|
@ -3,17 +3,21 @@
|
|||
<h1 class="font-bold text-lg text-white">Agent Registration Request</h1>
|
||||
<div v-if="otpCode">
|
||||
<p class="tracking-wide">
|
||||
An app is trying to register against the Hoppscotch Agent. If this was intentional, copy the given code into
|
||||
the app to complete the registration process. Please hide the window if you did not initiate this request.
|
||||
Do not hide this window until the verification code is entered. The window will hide automatically once done.
|
||||
An app is trying to register against the Hoppscotch Agent. If this was
|
||||
intentional, copy the given code into the app to complete the
|
||||
registration process. Please hide the window if you did not initiate
|
||||
this request. Do not hide this window until the verification code is
|
||||
entered. The window will hide automatically once done.
|
||||
</p>
|
||||
<p class="font-bold text-5xl tracking-wider text-center pt-10 text-white">
|
||||
{{ otpCode }}
|
||||
</p>
|
||||
<p class="font-bold text-5xl tracking-wider text-center pt-10 text-white">{{ otpCode }}</p>
|
||||
</div>
|
||||
<div v-else class="text-center pt-10">
|
||||
<p class="tracking-wide">Waiting for registration requests...</p>
|
||||
<p
|
||||
class="text-sm text-gray-400 mt-2"
|
||||
>You can hide this window and access it again from the tray icon.</p>
|
||||
<p class="text-sm text-gray-400 mt-2">
|
||||
You can hide this window and access it again from the tray icon.
|
||||
</p>
|
||||
</div>
|
||||
<div class="border-t border-divider p-5 flex justify-between">
|
||||
<HoppButtonSecondary
|
||||
|
|
@ -35,10 +39,12 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import { ref, markRaw, onMounted } from "vue"
|
||||
import { HoppButtonPrimary, HoppButtonSecondary } from "@hoppscotch/ui"
|
||||
import { HoppButtonSecondary } from "@hoppscotch/ui"
|
||||
// @ts-expect-error - Icon import has no types
|
||||
import IconCopy from "~icons/lucide/copy"
|
||||
// @ts-expect-error - Icon import has no types
|
||||
import IconCheck from "~icons/lucide/check"
|
||||
import { useClipboard, refAutoReset } from "@vueuse/core"
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window"
|
||||
|
|
@ -64,12 +70,12 @@ onMounted(async () => {
|
|||
const currentWindow = getCurrentWindow()
|
||||
currentWindow.setAlwaysOnTop(true)
|
||||
|
||||
const initialOtp = await invoke("get_otp", {})
|
||||
const initialOtp = await invoke<string>("get_otp", {})
|
||||
if (initialOtp) {
|
||||
otpCode.value = initialOtp
|
||||
}
|
||||
|
||||
await listen("registration-received", (event) => {
|
||||
await listen<string>("registration-received", (event) => {
|
||||
otpCode.value = event.payload
|
||||
currentWindow.setFocus()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@
|
|||
]"
|
||||
:list="registrations"
|
||||
>
|
||||
<template #registered_at="{ item }">{{ formatDate(item.registered_at) }}</template>
|
||||
<template #registered_at="{ item }">{{
|
||||
formatDate(item.registered_at)
|
||||
}}</template>
|
||||
</HoppSmartTable>
|
||||
</div>
|
||||
<div class="border-t border-divider p-5 flex justify-between">
|
||||
|
|
@ -18,17 +20,26 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, markRaw, onMounted } from "vue"
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue"
|
||||
import { HoppButtonPrimary, HoppSmartTable } from "@hoppscotch/ui"
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window"
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
import { listen } from "@tauri-apps/api/event"
|
||||
import { orderBy } from "lodash-es"
|
||||
|
||||
const registrations = ref([])
|
||||
interface Registration {
|
||||
auth_key_hash: string
|
||||
registered_at: string
|
||||
}
|
||||
|
||||
function formatDate(date) {
|
||||
interface ListRegistrationsResult {
|
||||
registrations: Registration[]
|
||||
}
|
||||
|
||||
const registrations = ref<Registration[]>([])
|
||||
|
||||
function formatDate(date: string): string {
|
||||
return new Date(date).toLocaleString()
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +49,7 @@ function hideWindow() {
|
|||
}
|
||||
|
||||
async function loadRegistrations() {
|
||||
const result = await invoke("list_registrations", {})
|
||||
const result = await invoke<ListRegistrationsResult>("list_registrations", {})
|
||||
registrations.value = orderBy(result.registrations, "registered_at", "desc")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,83 +0,0 @@
|
|||
/* eslint-env node */
|
||||
require("@rushstack/eslint-patch/modern-module-resolution")
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: "module",
|
||||
requireConfigFile: false,
|
||||
ecmaFeatures: {
|
||||
jsx: false,
|
||||
},
|
||||
},
|
||||
extends: [
|
||||
"@vue/typescript/recommended",
|
||||
"plugin:vue/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
],
|
||||
ignorePatterns: [
|
||||
"static/**/*",
|
||||
"./helpers/backend/graphql.ts",
|
||||
"**/*.d.ts",
|
||||
"types/**/*",
|
||||
],
|
||||
plugins: ["vue", "prettier"],
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
semi: [2, "never"],
|
||||
"import/named": "off", // because, named import issue with typescript see: https://github.com/typescript-eslint/typescript-eslint/issues/154
|
||||
"no-console": "off",
|
||||
"no-debugger": process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
"prettier/prettier": [
|
||||
process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
{
|
||||
semi: false,
|
||||
trailingComma: "es5",
|
||||
singleQuote: false,
|
||||
printWidth: 80,
|
||||
useTabs: false,
|
||||
tabWidth: 2,
|
||||
},
|
||||
],
|
||||
"vue/multi-word-component-names": "off",
|
||||
"vue/no-side-effects-in-computed-properties": "off",
|
||||
"import/no-named-as-default": "off",
|
||||
"import/no-named-as-default-member": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"import/default": "off",
|
||||
"no-undef": "off",
|
||||
// localStorage block
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
{
|
||||
name: "localStorage",
|
||||
message:
|
||||
"Do not use 'localStorage' directly. Please use the PersistenceService",
|
||||
},
|
||||
],
|
||||
// window.localStorage block
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector: "CallExpression[callee.object.property.name='localStorage']",
|
||||
message:
|
||||
"Do not use 'localStorage' directly. Please use the PersistenceService",
|
||||
},
|
||||
],
|
||||
eqeqeq: 1,
|
||||
"no-else-return": 1,
|
||||
},
|
||||
}
|
||||
96
packages/hoppscotch-common/eslint.config.mjs
Normal file
96
packages/hoppscotch-common/eslint.config.mjs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import pluginVue from "eslint-plugin-vue"
|
||||
import {
|
||||
defineConfigWithVueTs,
|
||||
vueTsConfigs,
|
||||
} from "@vue/eslint-config-typescript"
|
||||
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"
|
||||
import globals from "globals"
|
||||
|
||||
export default defineConfigWithVueTs(
|
||||
{
|
||||
ignores: [
|
||||
"static/**",
|
||||
"src/helpers/backend/graphql.ts",
|
||||
"**/*.d.ts",
|
||||
"types/**",
|
||||
"dist/**",
|
||||
"node_modules/**",
|
||||
],
|
||||
},
|
||||
pluginVue.configs["flat/recommended"],
|
||||
vueTsConfigs.recommended,
|
||||
eslintPluginPrettierRecommended,
|
||||
{
|
||||
files: ["**/*.ts", "**/*.js", "**/*.vue"],
|
||||
linterOptions: {
|
||||
reportUnusedDisableDirectives: false,
|
||||
},
|
||||
languageOptions: {
|
||||
sourceType: "module",
|
||||
ecmaVersion: "latest",
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
parserOptions: {
|
||||
requireConfigFile: false,
|
||||
ecmaFeatures: {
|
||||
jsx: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
semi: [2, "never"],
|
||||
"import/named": "off",
|
||||
"no-console": "off",
|
||||
"no-debugger": process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
"prettier/prettier": [
|
||||
process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
{
|
||||
semi: false,
|
||||
trailingComma: "es5",
|
||||
singleQuote: false,
|
||||
printWidth: 80,
|
||||
useTabs: false,
|
||||
tabWidth: 2,
|
||||
},
|
||||
],
|
||||
"vue/multi-word-component-names": "off",
|
||||
"vue/no-side-effects-in-computed-properties": "off",
|
||||
"import/no-named-as-default": "off",
|
||||
"import/no-named-as-default-member": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/no-unused-expressions": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-unsafe-function-type": "off",
|
||||
"import/default": "off",
|
||||
"no-undef": "off",
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
{
|
||||
name: "localStorage",
|
||||
message:
|
||||
"Do not use 'localStorage' directly. Please use the PersistenceService",
|
||||
},
|
||||
],
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector: "CallExpression[callee.object.property.name='localStorage']",
|
||||
message:
|
||||
"Do not use 'localStorage' directly. Please use the PersistenceService",
|
||||
},
|
||||
],
|
||||
eqeqeq: 1,
|
||||
"no-else-return": 1,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
|
@ -8,10 +8,10 @@
|
|||
"test:watch": "vitest",
|
||||
"dev:vite": "vite",
|
||||
"dev:gql-codegen": "graphql-codegen --require dotenv/config --config gql-codegen.yml --watch dotenv_config_path=\"../../.env\"",
|
||||
"lint": "eslint src --ext .ts,.js,.vue --ignore-path .gitignore .",
|
||||
"lint": "eslint src",
|
||||
"lint:ts": "vue-tsc --noEmit",
|
||||
"prod-lint": "cross-env HOPP_LINT_FOR_PROD=true pnpm run lint",
|
||||
"lintfix": "eslint --fix src --ext .ts,.js,.vue --ignore-path .gitignore .",
|
||||
"lintfix": "eslint --fix src",
|
||||
"preview": "vite preview",
|
||||
"gql-codegen": "graphql-codegen --require dotenv/config --config gql-codegen.yml dotenv_config_path=\"../../.env\"",
|
||||
"postinstall": "pnpm run gql-codegen",
|
||||
|
|
@ -127,6 +127,8 @@
|
|||
"devDependencies": {
|
||||
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
|
||||
"@esbuild-plugins/node-modules-polyfill": "0.2.2",
|
||||
"@eslint/eslintrc": "3.3.3",
|
||||
"@eslint/js": "9.39.2",
|
||||
"@graphql-codegen/add": "6.0.0",
|
||||
"@graphql-codegen/cli": "6.1.0",
|
||||
"@graphql-codegen/typed-document-node": "6.1.5",
|
||||
|
|
@ -153,15 +155,16 @@
|
|||
"@typescript-eslint/parser": "8.50.0",
|
||||
"@vitejs/plugin-vue": "6.0.3",
|
||||
"@vue/compiler-sfc": "3.5.26",
|
||||
"@vue/eslint-config-typescript": "13.0.0",
|
||||
"@vue/eslint-config-typescript": "14.6.0",
|
||||
"@vue/runtime-core": "3.5.26",
|
||||
"autoprefixer": "10.4.23",
|
||||
"cross-env": "10.1.0",
|
||||
"dotenv": "17.2.3",
|
||||
"eslint": "8.57.0",
|
||||
"eslint": "9.39.2",
|
||||
"eslint-plugin-prettier": "5.5.4",
|
||||
"eslint-plugin-vue": "10.6.2",
|
||||
"glob": "13.0.0",
|
||||
"globals": "16.5.0",
|
||||
"jsdom": "27.3.0",
|
||||
"npm-run-all": "4.1.5",
|
||||
"openapi-types": "12.1.3",
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ const fetchAccessTokens = async () => {
|
|||
if (tokensListFetchErrored.value) {
|
||||
tokensListFetchErrored.value = false
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
toast.error(t("error.fetching_access_tokens_list"))
|
||||
tokensListFetchErrored.value = true
|
||||
} finally {
|
||||
|
|
@ -136,7 +136,7 @@ const generateAccessToken = async ({
|
|||
if (tokensListFetchErrored.value) {
|
||||
tokensListFetchErrored.value = false
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
toast.error(t("error.generate_access_token"))
|
||||
showAccessTokensGenerateModal.value = false
|
||||
} finally {
|
||||
|
|
@ -175,7 +175,7 @@ const deleteAccessToken = async () => {
|
|||
if (tokensListFetchErrored.value) {
|
||||
tokensListFetchErrored.value = false
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
toast.error(t("error.delete_access_token"))
|
||||
} finally {
|
||||
tokenDeleteActionLoading.value = false
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ watch(
|
|||
doc: props.contentRight.content,
|
||||
extensions: [jsonLanguage, baseTheme, basicSetup],
|
||||
},
|
||||
// @ts-expect-error attribute mismatch
|
||||
parent: diffEditor.value,
|
||||
highlightChanges: false,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ const request = computed(() => {
|
|||
return pathFolders.value[pathFolders.value.length - 1].requests[
|
||||
requestIndex
|
||||
]
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ const request = computed(() => {
|
|||
return pathFolders.value[pathFolders.value.length - 1].requests[
|
||||
requestIndex
|
||||
]
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ const countPostmanScripts = (
|
|||
if (collection?.item && Array.isArray(collection.item)) {
|
||||
collection.item.forEach(countInItem)
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
// Invalid JSON, skip
|
||||
}
|
||||
})
|
||||
|
|
@ -337,7 +337,7 @@ const HoppAllCollectionImporter: ImporterOrExporter = {
|
|||
exporter: "import_to_teams",
|
||||
platform: "rest",
|
||||
})
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
showImportFailedError()
|
||||
unsetCurrentImportSummary()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -342,7 +342,7 @@ const fetchTeamCollection = async () => {
|
|||
props.collection as TeamCollection
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
fullCollectionData.value = teamCollToHoppRESTColl(
|
||||
props.collection as TeamCollection
|
||||
)
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ function formatJSON(jsonString: string): string {
|
|||
try {
|
||||
const parsed = JSON.parse(jsonString || "{}")
|
||||
return JSON.stringify(parsed, null, 2)
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
return jsonString || ""
|
||||
}
|
||||
}
|
||||
|
|
@ -94,7 +94,7 @@ function formatJSON(jsonString: string): string {
|
|||
function parseFormData(formData: string): { key: string; value: string }[] {
|
||||
try {
|
||||
return typeof formData === "string" ? parseRawKeyValueEntries(formData) : []
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ function isJsonResponse(example: ResponseExample): boolean {
|
|||
try {
|
||||
JSON.parse(example.body || "")
|
||||
return true
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -226,7 +226,7 @@ function formatJSON(jsonString: string): string {
|
|||
try {
|
||||
const parsed = JSON.parse(jsonString || "{}")
|
||||
return JSON.stringify(parsed, null, 2)
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
return jsonString || ""
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ const debouncedOnUpdateQueryState = debounce((update: ViewUpdate) => {
|
|||
const { start, end } = def.loc!
|
||||
return selectedPos >= start && selectedPos <= end
|
||||
}) as OperationDefinitionNode) ?? null
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
if (queryString.trim() === "") {
|
||||
operationDefinitions.value = []
|
||||
}
|
||||
|
|
@ -193,7 +193,7 @@ onMounted(() => {
|
|||
selectedOperation.value = ast.definitions[0] as OperationDefinitionNode
|
||||
return
|
||||
}
|
||||
} catch (error) {}
|
||||
} catch (_error) {}
|
||||
})
|
||||
|
||||
const cmQueryEditor = useCodemirror(
|
||||
|
|
@ -238,7 +238,7 @@ const prettifyQuery = () => {
|
|||
})
|
||||
)
|
||||
prettifyQueryIcon.value = IconCheck
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
toast.error(`${t("error.gql_prettify_invalid_query")}`)
|
||||
prettifyQueryIcon.value = IconInfo
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,14 +77,14 @@ import { platform } from "~/platform"
|
|||
import { KernelInterceptorService } from "~/services/kernel-interceptor.service"
|
||||
import { GQLTabService } from "~/services/tab/graphql"
|
||||
|
||||
const VALID_GQL_OPERATIONS = [
|
||||
const _VALID_GQL_OPERATIONS = [
|
||||
"query",
|
||||
"headers",
|
||||
"variables",
|
||||
"authorization",
|
||||
] as const
|
||||
|
||||
export type GQLOptionTabs = (typeof VALID_GQL_OPERATIONS)[number]
|
||||
export type GQLOptionTabs = (typeof _VALID_GQL_OPERATIONS)[number]
|
||||
|
||||
const interceptorService = useService(KernelInterceptorService)
|
||||
|
||||
|
|
|
|||
|
|
@ -600,7 +600,7 @@ const saveRequest = async () => {
|
|||
})
|
||||
|
||||
toast.success(`${t("request.saved")}`)
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
tab.value.document.saveContext = undefined
|
||||
saveRequest()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ import { defineActionHandler } from "~/helpers/actions"
|
|||
import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties"
|
||||
import { AggregateEnvironment } from "~/newstore/environments"
|
||||
|
||||
const VALID_OPTION_TABS = [
|
||||
const _VALID_OPTION_TABS = [
|
||||
"params",
|
||||
"bodyParams",
|
||||
"headers",
|
||||
|
|
@ -120,7 +120,7 @@ const VALID_OPTION_TABS = [
|
|||
"requestVariables",
|
||||
] as const
|
||||
|
||||
export type RESTOptionTabs = (typeof VALID_OPTION_TABS)[number]
|
||||
export type RESTOptionTabs = (typeof _VALID_OPTION_TABS)[number]
|
||||
|
||||
const t = useI18n()
|
||||
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ function getCurrentPageCategory(): "graphql" | "rest" | "other" {
|
|||
return "rest"
|
||||
}
|
||||
return "other"
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
return "other"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ const retryWithProxy = async () => {
|
|||
} else {
|
||||
toast.error(t("import.failed"))
|
||||
}
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
toast.error(t("import.failed"))
|
||||
} finally {
|
||||
isFetchingUrl.value = false
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ async function register() {
|
|||
try {
|
||||
await agentService.initiateRegistration()
|
||||
registrationStatus.value = "otp_required"
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
toast.error("Failed to initiate registration. Please try again.")
|
||||
registrationStatus.value = "initial"
|
||||
}
|
||||
|
|
@ -85,7 +85,7 @@ async function verifyOTP(otp: string) {
|
|||
await agentService.verifyRegistration(otp)
|
||||
toast.success("Registration successful!")
|
||||
hideModal()
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
toast.error("Failed to verify OTP. Please try again.")
|
||||
registrationStatus.value = "otp_required"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ const copyToClipboardHandler = async (text: string) => {
|
|||
setTimeout(() => {
|
||||
copyIcon.value = IconCopy
|
||||
}, 1000)
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
toast.error(t("error.copy_failed"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ const copyToClipboardHandler = async (text: string) => {
|
|||
setTimeout(() => {
|
||||
copyIcon.value = IconCopy
|
||||
}, 1000)
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
toast.error(t("error.copy_failed"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -668,7 +668,7 @@ async function updateMaskedAuthKey() {
|
|||
try {
|
||||
const registration = await store.fetchRegistrationInfo()
|
||||
maskedAuthKey.value = registration.auth_key_hash
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
toast.error(t("settings.agent_registration_fetch_failed"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ const register = async () => {
|
|||
await updateMaskedAuthKey()
|
||||
toast.success(t("settings.agent_registration_successful"))
|
||||
store.registrationOTP.value = ""
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
} finally {
|
||||
store.isRegistering.value = false
|
||||
}
|
||||
|
|
@ -146,7 +146,7 @@ const updateMaskedAuthKey = async () => {
|
|||
try {
|
||||
const registration = await store.fetchRegistrationInfo()
|
||||
store.maskedAuthKey.value = registration.auth_key_hash
|
||||
} catch (e) {}
|
||||
} catch (_e) {}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export function initializeApp() {
|
|||
platform.analytics?.initAnalytics()
|
||||
|
||||
initialized = true
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
// initializeApp throws exception if we reinitialize
|
||||
initialized = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ type TypeFromPrimitiveArray<P extends JSPrimitive | undefined> =
|
|||
: P extends "symbol"
|
||||
? symbol[]
|
||||
: P extends "function"
|
||||
? Function[] // eslint-disable-line @typescript-eslint/ban-types
|
||||
? Function[]
|
||||
: unknown[]
|
||||
|
||||
// The ban-types silence is because in this case,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export type TypeFromPrimitive<P extends JSPrimitive | undefined> =
|
|||
: P extends "symbol"
|
||||
? symbol
|
||||
: P extends "function"
|
||||
? Function // eslint-disable-line @typescript-eslint/ban-types
|
||||
? Function
|
||||
: unknown
|
||||
|
||||
// The ban-types silence is because in this case,
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ const parseOpenAPIV3Responses = (
|
|||
try {
|
||||
stringifiedBody = JSON.stringify(body ?? "")
|
||||
// the parsing will fail for a circular response schema
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
// eat five star, do nothing
|
||||
}
|
||||
|
||||
|
|
@ -435,7 +435,7 @@ const parseOpenAPIV3Body = (
|
|||
? sampleBody
|
||||
: JSON.stringify(sampleBody, null, 2),
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
// If we can't generate a sample, check for examples
|
||||
if (media.example !== undefined) {
|
||||
return {
|
||||
|
|
@ -1152,7 +1152,7 @@ export const hoppOpenAPIImporter = (fileContents: string[]) =>
|
|||
try {
|
||||
const validatedDoc = await dereferenceDocs(docObj)
|
||||
resultDoc.push(validatedDoc)
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
// Check if the document has unresolved references
|
||||
if (hasUnresolvedRefs(docObj)) {
|
||||
console.warn(
|
||||
|
|
|
|||
|
|
@ -12,12 +12,11 @@ import * as E from "fp-ts/Either"
|
|||
const validateDocs = async (docs: any) => {
|
||||
try {
|
||||
const res = await SwaggerParser.validate(docs, {
|
||||
// @ts-expect-error - this is a valid option, but seems like the types are not updated
|
||||
continueOnError: true,
|
||||
})
|
||||
|
||||
return E.right(res)
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
return E.left("COULD_NOT_VALIDATE" as const)
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +26,7 @@ const dereferenceDocs = async (docs: any) => {
|
|||
const res = await SwaggerParser.dereference(docs)
|
||||
|
||||
return E.right(res)
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
return E.left("COULD_NOT_DEREFERENCE" as const)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ export const isValidUser = async (): Promise<ValidUserResponse> => {
|
|||
|
||||
// For platforms without token verification capability
|
||||
return { valid: true, error: "" }
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
// Handle errors from token verification
|
||||
return attemptTokenRefresh()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ export default function jsonParse(
|
|||
const ast = parseObj()
|
||||
expect("EOF")
|
||||
return ast
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
// Try parsing expecting a root array
|
||||
const ast = parseArr()
|
||||
expect("EOF")
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ export default function jsonParse(
|
|||
const ast = parseObj()
|
||||
expect("EOF")
|
||||
return ast
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
pendingComments = [] // Reset pending comments
|
||||
const ast = parseArr()
|
||||
expect("EOF")
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export function isValidJSONResponse(contents: string | ArrayBuffer): boolean {
|
|||
try {
|
||||
JSON.parse(resolvedStr)
|
||||
return true
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ export function createRESTNetworkRequestStream(
|
|||
try {
|
||||
const result = await execResult
|
||||
if (result) await result.cancel()
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
// Ignore cancel errors - request may have already completed
|
||||
// This is expected behavior and not an actual error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ export function getJSONOutlineAtPos(
|
|||
}
|
||||
|
||||
return path
|
||||
} catch (e: any) {
|
||||
} catch (_e: any) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ export class MQTTConnection {
|
|||
message,
|
||||
},
|
||||
})
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
this.addEvent({
|
||||
time: Date.now(),
|
||||
type: "ERROR",
|
||||
|
|
@ -216,7 +216,7 @@ export class MQTTConnection {
|
|||
onFailure: this.usubFailure.bind(this, topic.name),
|
||||
qos: topic.qos,
|
||||
})
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
this.subscribing$.next(false)
|
||||
this.addEvent({
|
||||
time: Date.now(),
|
||||
|
|
|
|||
|
|
@ -365,7 +365,7 @@ function makeVisitors(server, query, file, messages) {
|
|||
infer.findRefs(cur.ast, cur.scope, name, scope, searchRef(cur))
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
} catch (_e) {}
|
||||
return hasRef
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export function parseUrlAndPath(value) {
|
|||
const url = new URL(value)
|
||||
result.url = url.origin
|
||||
result.path = url.pathname
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
const uriRegex = value.match(
|
||||
/^((http[s]?:\/\/)?(<<[^/]+>>)?[^/]*|)(\/?.*)$/
|
||||
)
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ export function loadMockServers(skip?: number, take?: number) {
|
|||
}
|
||||
)
|
||||
)()
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
// Fallback to user mock servers if workspace service is not available
|
||||
return pipe(
|
||||
getMyMockServers(skip, take),
|
||||
|
|
|
|||
|
|
@ -289,8 +289,8 @@ export function applySetting<K extends keyof SettingsDef>(
|
|||
) {
|
||||
settingsStore.dispatch({
|
||||
dispatcher: "applySetting",
|
||||
payload: {
|
||||
// @ts-expect-error TS is not able to understand the type semantics here
|
||||
payload: {
|
||||
settingKey,
|
||||
value,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -328,7 +328,7 @@ export class AgentInterceptorService extends Service implements Interceptor {
|
|||
try {
|
||||
const proxyInfo = JSON.parse(persistedProxyInfo)
|
||||
this.proxyInfo.value = proxyInfo
|
||||
} catch (e) {}
|
||||
} catch (_e) {}
|
||||
}
|
||||
|
||||
// Load SSL Validation
|
||||
|
|
@ -557,7 +557,7 @@ export class AgentInterceptorService extends Service implements Interceptor {
|
|||
try {
|
||||
await this.performHandshake()
|
||||
this.isAgentRunning.value = true
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
this.isAgentRunning.value = false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { useSetting } from "~/composables/settings"
|
|||
const isEncoded = (value: string) => {
|
||||
try {
|
||||
return value !== decodeURIComponent(value)
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
return false // in case of malformed URI sequence
|
||||
}
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ export const preProcessRequest = (
|
|||
|
||||
// decode the URL to prevent double encoding
|
||||
reqClone.url = decodeURIComponent(url.toString())
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
// making this a non-empty block, so we can make the linter happy.
|
||||
// we should probably use, allowEmptyCatch, or take the time to do something with the caught errors :)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ describe("URLMenuService", () => {
|
|||
try {
|
||||
new URL(url)
|
||||
return true
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
// Fallback to regular expression check
|
||||
const pattern = /^(https?:\/\/)?([\w.-]+)(\.[\w.-]+)+([/?].*)?$/
|
||||
return pattern.test(url)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ function isValidURL(url: string) {
|
|||
// this will fail for endpoints like "localhost:3000", ie without a protocol
|
||||
new URL(url)
|
||||
return true
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
// Fallback to regular expression check
|
||||
const pattern = /^(https?:\/\/)?([\w.-]+)(\.[\w.-]+)+([/?].*)?$/
|
||||
return pattern.test(url)
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ const initAuthCodeOauthFlow = async ({
|
|||
|
||||
try {
|
||||
url = new URL(authEndpoint)
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
return E.left("INVALID_AUTH_ENDPOINT")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ export class PersistenceService extends Service {
|
|||
)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted LOCAL_STATE:`, loadResult)
|
||||
}
|
||||
|
||||
|
|
@ -386,7 +386,7 @@ export class PersistenceService extends Service {
|
|||
)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted SETTINGS:`, loadResult)
|
||||
}
|
||||
|
||||
|
|
@ -418,7 +418,7 @@ export class PersistenceService extends Service {
|
|||
)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted REST_HISTORY:`, restLoadResult)
|
||||
}
|
||||
|
||||
|
|
@ -450,7 +450,7 @@ export class PersistenceService extends Service {
|
|||
)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted GQL_HISTORY:`, gqlLoadResult)
|
||||
}
|
||||
|
||||
|
|
@ -486,7 +486,7 @@ export class PersistenceService extends Service {
|
|||
setRESTCollections(data)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(
|
||||
`Failed parsing persisted REST_COLLECTIONS:`,
|
||||
restLoadResult
|
||||
|
|
@ -523,7 +523,7 @@ export class PersistenceService extends Service {
|
|||
setGraphqlCollections(data)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted GQL_COLLECTIONS:`, gqlLoadResult)
|
||||
}
|
||||
|
||||
|
|
@ -569,7 +569,7 @@ export class PersistenceService extends Service {
|
|||
)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted ENVIRONMENTS:`, loadResult)
|
||||
}
|
||||
|
||||
|
|
@ -607,7 +607,7 @@ export class PersistenceService extends Service {
|
|||
)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted SECRET_ENVIRONMENTS:`, loadResult)
|
||||
}
|
||||
|
||||
|
|
@ -653,7 +653,7 @@ export class PersistenceService extends Service {
|
|||
)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(
|
||||
`Failed parsing persisted CURRENT_ENVIRONMENT_VALUE:`,
|
||||
loadResult
|
||||
|
|
@ -699,7 +699,7 @@ export class PersistenceService extends Service {
|
|||
)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted SELECTED_ENV:`, loadResult)
|
||||
}
|
||||
|
||||
|
|
@ -735,7 +735,7 @@ export class PersistenceService extends Service {
|
|||
)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted CURRENT_SORT_VALUES:`, loadResult)
|
||||
}
|
||||
|
||||
|
|
@ -780,7 +780,7 @@ export class PersistenceService extends Service {
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted WEBSOCKET:`, loadResult)
|
||||
}
|
||||
|
||||
|
|
@ -817,7 +817,7 @@ export class PersistenceService extends Service {
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted SOCKETIO:`, loadResult)
|
||||
}
|
||||
|
||||
|
|
@ -847,7 +847,7 @@ export class PersistenceService extends Service {
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted SSE:`, loadResult)
|
||||
}
|
||||
|
||||
|
|
@ -877,7 +877,7 @@ export class PersistenceService extends Service {
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted MQTT:`, loadResult)
|
||||
}
|
||||
|
||||
|
|
@ -908,7 +908,7 @@ export class PersistenceService extends Service {
|
|||
)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted GLOBAL_ENV:`, loadResult)
|
||||
}
|
||||
|
||||
|
|
@ -955,7 +955,7 @@ export class PersistenceService extends Service {
|
|||
this.restTabService.loadTabsFromPersistedState(loadResult.right)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted REST_TABS:`, loadResult)
|
||||
}
|
||||
|
||||
|
|
@ -998,7 +998,7 @@ export class PersistenceService extends Service {
|
|||
this.gqlTabService.loadTabsFromPersistedState(loadResult.right)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
console.error(`Failed parsing persisted GQL_TABS:`, loadResult)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ export class CollectionsSpotlightSearcherService
|
|||
return "rest"
|
||||
}
|
||||
return "other"
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
return "other"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export class TeamsSpotlightSearcherService
|
|||
return "rest"
|
||||
}
|
||||
return "other"
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
return "other"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ export class TestRunnerService extends Service {
|
|||
if (options.delay && options.delay > 0) {
|
||||
try {
|
||||
await delay(options.delay)
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
if (options.stopRef?.value) {
|
||||
tab.value.document.status = "stopped"
|
||||
throw new Error("Test execution stopped")
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
/* eslint-env node */
|
||||
require("@rushstack/eslint-patch/modern-module-resolution")
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: "module",
|
||||
requireConfigFile: false,
|
||||
ecmaFeatures: {
|
||||
jsx: false,
|
||||
},
|
||||
},
|
||||
extends: [
|
||||
"@vue/typescript/recommended",
|
||||
"plugin:vue/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
],
|
||||
ignorePatterns: [
|
||||
"static/**/*",
|
||||
"./helpers/backend/graphql.ts",
|
||||
"**/*.d.ts",
|
||||
"types/**/*",
|
||||
],
|
||||
plugins: ["vue", "prettier"],
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
semi: [2, "never"],
|
||||
"import/named": "off", // because, named import issue with typescript see: https://github.com/typescript-eslint/typescript-eslint/issues/154
|
||||
"no-console": "off",
|
||||
"no-debugger": process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
"prettier/prettier":
|
||||
process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
"vue/multi-word-component-names": "off",
|
||||
"vue/no-side-effects-in-computed-properties": "off",
|
||||
"import/no-named-as-default": "off",
|
||||
"import/no-named-as-default-member": "off",
|
||||
"@typescript-eslint/no-unused-vars":
|
||||
process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"import/default": "off",
|
||||
"no-undef": "off",
|
||||
// localStorage block
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
{
|
||||
name: "localStorage",
|
||||
message:
|
||||
"Do not use 'localStorage' directly. Please use localpersistence.ts functions or stores",
|
||||
},
|
||||
],
|
||||
// window.localStorage block
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector: "CallExpression[callee.object.property.name='localStorage']",
|
||||
message:
|
||||
"Do not use 'localStorage' directly. Please use localpersistence.ts functions or stores",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
79
packages/hoppscotch-desktop/eslint.config.mjs
Normal file
79
packages/hoppscotch-desktop/eslint.config.mjs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
import pluginVue from "eslint-plugin-vue"
|
||||
import {
|
||||
defineConfigWithVueTs,
|
||||
vueTsConfigs,
|
||||
} from "@vue/eslint-config-typescript"
|
||||
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"
|
||||
import globals from "globals"
|
||||
|
||||
export default defineConfigWithVueTs(
|
||||
{
|
||||
ignores: [
|
||||
"static/**",
|
||||
"src/helpers/backend/graphql.ts",
|
||||
"**/*.d.ts",
|
||||
"types/**",
|
||||
"dist/**",
|
||||
"node_modules/**",
|
||||
],
|
||||
},
|
||||
pluginVue.configs["flat/recommended"],
|
||||
vueTsConfigs.recommended,
|
||||
eslintPluginPrettierRecommended,
|
||||
{
|
||||
files: ["**/*.ts", "**/*.js", "**/*.vue"],
|
||||
linterOptions: {
|
||||
reportUnusedDisableDirectives: false,
|
||||
},
|
||||
languageOptions: {
|
||||
sourceType: "module",
|
||||
ecmaVersion: "latest",
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
parserOptions: {
|
||||
requireConfigFile: false,
|
||||
ecmaFeatures: {
|
||||
jsx: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
semi: [2, "never"],
|
||||
"import/named": "off",
|
||||
"no-console": "off",
|
||||
"no-debugger": process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
"prettier/prettier":
|
||||
process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
"vue/multi-word-component-names": "off",
|
||||
"vue/no-side-effects-in-computed-properties": "off",
|
||||
"import/no-named-as-default": "off",
|
||||
"import/no-named-as-default-member": "off",
|
||||
"@typescript-eslint/no-unused-vars":
|
||||
process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
"@typescript-eslint/no-unused-expressions": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-unsafe-function-type": "off",
|
||||
"import/default": "off",
|
||||
"no-undef": "off",
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
{
|
||||
name: "localStorage",
|
||||
message:
|
||||
"Do not use 'localStorage' directly. Please use the PersistenceService",
|
||||
},
|
||||
],
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector: "CallExpression[callee.object.property.name='localStorage']",
|
||||
message:
|
||||
"Do not use 'localStorage' directly. Please use the PersistenceService",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
|
@ -8,10 +8,13 @@
|
|||
"build": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview",
|
||||
"tauri": "tauri",
|
||||
"lint": "eslint src --ext .ts,.js,.vue --ignore-path .gitignore .",
|
||||
"lint": "eslint src",
|
||||
"lint:ts": "vue-tsc --noEmit",
|
||||
"lintfix": "eslint --fix src --ext .ts,.js,.vue --ignore-path .gitignore .",
|
||||
"lintfix": "eslint --fix src",
|
||||
"prod-lint": "cross-env HOPP_LINT_FOR_PROD=true pnpm run lint",
|
||||
"do-lint": "pnpm run prod-lint",
|
||||
"do-typecheck": "pnpm run lint:ts",
|
||||
"do-lintfix": "pnpm run lintfix",
|
||||
"prepare-web": "(cd ../hoppscotch-selfhost-web && pnpm install && pnpm generate) && (cd crates/webapp-bundler && cargo build --release && cd target/release && ./webapp-bundler --input ../../../../../hoppscotch-selfhost-web/dist --output ../../../../bundle.zip --manifest ../../../../manifest.json)",
|
||||
"dev:full": "pnpm tauri dev",
|
||||
"build:full": "pnpm tauri build",
|
||||
|
|
@ -20,7 +23,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@fontsource-variable/inter": "5.2.8",
|
||||
"@fontsource-variable/material-symbols-rounded": "5.2.24",
|
||||
"@fontsource-variable/material-symbols-rounded": "5.2.30",
|
||||
"@fontsource-variable/roboto-mono": "5.2.8",
|
||||
"@hoppscotch/common": "workspace:^",
|
||||
"@hoppscotch/kernel": "workspace:^",
|
||||
|
|
@ -29,36 +32,38 @@
|
|||
"@tauri-apps/api": "2.1.1",
|
||||
"@tauri-apps/plugin-fs": "2.0.2",
|
||||
"@tauri-apps/plugin-process": "2.2.0",
|
||||
"@tauri-apps/plugin-shell": "2.2.1",
|
||||
"@tauri-apps/plugin-store": "2.2.0",
|
||||
"@tauri-apps/plugin-updater": "2.5.1",
|
||||
"@vueuse/core": "13.7.0",
|
||||
"@tauri-apps/plugin-shell": "2.3.3",
|
||||
"@tauri-apps/plugin-store": "2.4.1",
|
||||
"@tauri-apps/plugin-updater": "2.9.0",
|
||||
"fp-ts": "2.16.11",
|
||||
"rxjs": "7.8.2",
|
||||
"vue": "3.5.22",
|
||||
"vue-router": "4.6.3",
|
||||
"vue": "3.5.26",
|
||||
"vue-router": "4.6.4",
|
||||
"vue-tippy": "6.7.1",
|
||||
"zod": "3.25.32"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/lucide": "1.2.68",
|
||||
"@rushstack/eslint-patch": "1.14.0",
|
||||
"@tauri-apps/cli": "^2",
|
||||
"@typescript-eslint/eslint-plugin": "8.44.1",
|
||||
"@typescript-eslint/parser": "8.44.1",
|
||||
"@vitejs/plugin-vue": "5.1.4",
|
||||
"@vue/eslint-config-typescript": "13.0.0",
|
||||
"autoprefixer": "10.4.21",
|
||||
"eslint": "8.57.0",
|
||||
"@eslint/eslintrc": "3.3.3",
|
||||
"@eslint/js": "9.39.2",
|
||||
"@iconify-json/lucide": "1.2.81",
|
||||
"@rushstack/eslint-patch": "1.15.0",
|
||||
"@tauri-apps/cli": "2.9.3",
|
||||
"@typescript-eslint/eslint-plugin": "8.50.0",
|
||||
"@typescript-eslint/parser": "8.50.0",
|
||||
"@vitejs/plugin-vue": "6.0.3",
|
||||
"@vue/eslint-config-typescript": "14.6.0",
|
||||
"autoprefixer": "10.4.23",
|
||||
"eslint": "9.39.2",
|
||||
"eslint-plugin-prettier": "5.5.4",
|
||||
"eslint-plugin-vue": "10.5.1",
|
||||
"eslint-plugin-vue": "10.6.2",
|
||||
"globals": "16.5.0",
|
||||
"postcss": "8.5.6",
|
||||
"sass": "1.93.2",
|
||||
"sass": "1.97.0",
|
||||
"tailwindcss": "3.4.16",
|
||||
"typescript": "5.9.3",
|
||||
"unplugin-icons": "22.2.0",
|
||||
"unplugin-vue-components": "29.0.0",
|
||||
"vite": "6.3.5",
|
||||
"unplugin-icons": "22.5.0",
|
||||
"unplugin-vue-components": "30.0.0",
|
||||
"vite": "7.3.0",
|
||||
"vue-tsc": "2.2.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// biome-ignore lint: disable
|
||||
// oxlint-disable
|
||||
// ------
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
// biome-ignore lint: disable
|
||||
|
||||
export {}
|
||||
|
||||
/* prettier-ignore */
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
browser: true,
|
||||
},
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
sourceType: "module",
|
||||
requireConfigFile: false,
|
||||
ecmaVersion: 2021,
|
||||
},
|
||||
plugins: ["prettier"],
|
||||
extends: [
|
||||
"prettier/prettier",
|
||||
"eslint:recommended",
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
],
|
||||
rules: {
|
||||
semi: [2, "never"],
|
||||
"prettier/prettier": ["warn", { semi: false, trailingComma: "es5" }],
|
||||
"import/no-named-as-default": "off",
|
||||
"no-undef": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
args: "all",
|
||||
argsIgnorePattern: "^_",
|
||||
caughtErrors: "all",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
destructuredArrayIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
ignoreRestSiblings: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
70
packages/hoppscotch-js-sandbox/eslint.config.cjs
Normal file
70
packages/hoppscotch-js-sandbox/eslint.config.cjs
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
const { FlatCompat } = require("@eslint/eslintrc")
|
||||
const js = require("@eslint/js")
|
||||
const tsParser = require("@typescript-eslint/parser")
|
||||
const typescriptEslintPlugin = require("@typescript-eslint/eslint-plugin")
|
||||
const prettierPlugin = require("eslint-plugin-prettier")
|
||||
const globals = require("globals")
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all,
|
||||
})
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: [
|
||||
"dist/**",
|
||||
"node_modules/**",
|
||||
"**/*.d.ts",
|
||||
"eslint.config.cjs",
|
||||
".prettierrc.cjs",
|
||||
"src/bootstrap-code/**",
|
||||
],
|
||||
},
|
||||
...compat.extends(
|
||||
"eslint:recommended",
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
),
|
||||
{
|
||||
files: ["**/*.ts", "**/*.js"],
|
||||
linterOptions: {
|
||||
reportUnusedDisableDirectives: false,
|
||||
},
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
sourceType: "module",
|
||||
ecmaVersion: 2021,
|
||||
globals: {
|
||||
...globals.node,
|
||||
...globals.jest,
|
||||
...globals.browser,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
"@typescript-eslint": typescriptEslintPlugin,
|
||||
prettier: prettierPlugin,
|
||||
},
|
||||
rules: {
|
||||
semi: [2, "never"],
|
||||
"prettier/prettier": ["warn", { semi: false, trailingComma: "es5" }],
|
||||
"no-undef": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
args: "all",
|
||||
argsIgnorePattern: "^_",
|
||||
caughtErrors: "all",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
destructuredArrayIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
ignoreRestSiblings: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
@ -27,8 +27,8 @@
|
|||
"node": ">=22"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .ts,.js --ignore-path .gitignore .",
|
||||
"lintfix": "eslint --fix --ext .ts,.js --ignore-path .gitignore .",
|
||||
"lint": "eslint .",
|
||||
"lintfix": "eslint --fix .",
|
||||
"test": "vitest run",
|
||||
"build": "vite build && tsc --emitDeclarationOnly",
|
||||
"clean": "pnpm tsc --build --clean",
|
||||
|
|
@ -67,7 +67,10 @@
|
|||
"@types/node": "24.10.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.50.0",
|
||||
"@typescript-eslint/parser": "8.50.0",
|
||||
"eslint": "8.57.0",
|
||||
"@eslint/eslintrc": "3.3.3",
|
||||
"@eslint/js": "9.39.2",
|
||||
"eslint": "9.39.2",
|
||||
"globals": "16.5.0",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"eslint-plugin-prettier": "5.5.4",
|
||||
"io-ts": "2.2.22",
|
||||
|
|
|
|||
52
packages/hoppscotch-kernel/eslint.config.mjs
Normal file
52
packages/hoppscotch-kernel/eslint.config.mjs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import js from "@eslint/js"
|
||||
import tsParser from "@typescript-eslint/parser"
|
||||
import typescriptEslintPlugin from "@typescript-eslint/eslint-plugin"
|
||||
import prettierPlugin from "eslint-plugin-prettier"
|
||||
import globals from "globals"
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: [
|
||||
"dist/**",
|
||||
"node_modules/**",
|
||||
"**/*.d.ts",
|
||||
"vite.config.ts",
|
||||
],
|
||||
},
|
||||
js.configs.recommended,
|
||||
{
|
||||
files: ["src/**/*.ts"],
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
sourceType: "module",
|
||||
ecmaVersion: 2022,
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
"@typescript-eslint": typescriptEslintPlugin,
|
||||
prettier: prettierPlugin,
|
||||
},
|
||||
rules: {
|
||||
...typescriptEslintPlugin.configs.recommended.rules,
|
||||
"prettier/prettier": "warn",
|
||||
"no-undef": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
args: "all",
|
||||
argsIgnorePattern: "^_",
|
||||
caughtErrors: "all",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
destructuredArrayIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
ignoreRestSiblings: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
@ -15,7 +15,11 @@
|
|||
"build:decl": "tsc --project tsconfig.decl.json",
|
||||
"build": "pnpm run build:code && pnpm run build:decl",
|
||||
"prepare": "pnpm run build:code && pnpm run build:decl",
|
||||
"do-typecheck": "pnpm exec tsc --noEmit"
|
||||
"do-typecheck": "pnpm exec tsc --noEmit",
|
||||
"lint": "eslint src",
|
||||
"lintfix": "eslint --fix src",
|
||||
"do-lint": "pnpm run lint",
|
||||
"do-lintfix": "pnpm run lintfix"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
|
|
@ -35,7 +39,13 @@
|
|||
},
|
||||
"homepage": "https://github.com/hoppscotch/hoppscotch#readme",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "9.39.2",
|
||||
"@types/node": "24.9.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.50.0",
|
||||
"@typescript-eslint/parser": "8.50.0",
|
||||
"eslint": "9.39.2",
|
||||
"eslint-plugin-prettier": "5.5.4",
|
||||
"globals": "16.5.0",
|
||||
"typescript": "5.9.3",
|
||||
"vite": "7.3.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import type { Version } from './type/versioning'
|
||||
import type { Version } from "./type/versioning"
|
||||
|
||||
import { VERSIONS as IO_VERSIONS } from './io'
|
||||
import { IO_IMPLS as WEB_IO_IMPLS } from './io/impl/web'
|
||||
import { IO_IMPLS as DESKTOP_IO_IMPLS } from './io/impl/desktop'
|
||||
import { VERSIONS as IO_VERSIONS } from "./io"
|
||||
import { IO_IMPLS as WEB_IO_IMPLS } from "./io/impl/web"
|
||||
import { IO_IMPLS as DESKTOP_IO_IMPLS } from "./io/impl/desktop"
|
||||
|
||||
import { VERSIONS as RELAY_VERSIONS } from './relay'
|
||||
import { RELAY_IMPLS as WEB_RELAY_IMPLS } from './relay/impl/web'
|
||||
import { RELAY_IMPLS as DESKTOP_RELAY_IMPLS } from './relay/impl/desktop'
|
||||
import { VERSIONS as RELAY_VERSIONS } from "./relay"
|
||||
import { RELAY_IMPLS as WEB_RELAY_IMPLS } from "./relay/impl/web"
|
||||
import { RELAY_IMPLS as DESKTOP_RELAY_IMPLS } from "./relay/impl/desktop"
|
||||
|
||||
import { VERSIONS as STORE_VERSIONS } from './store'
|
||||
import { STORE_IMPLS as WEB_STORE_IMPLS } from './store/impl/web'
|
||||
import { STORE_IMPLS as DESKTOP_STORE_IMPLS } from './store/impl/desktop'
|
||||
import { VERSIONS as STORE_VERSIONS } from "./store"
|
||||
import { STORE_IMPLS as WEB_STORE_IMPLS } from "./store/impl/web"
|
||||
import { STORE_IMPLS as DESKTOP_STORE_IMPLS } from "./store/impl/desktop"
|
||||
|
||||
export interface KernelInfo {
|
||||
name: string
|
||||
|
|
@ -25,7 +25,7 @@ export interface KernelAPI {
|
|||
store: typeof STORE_VERSIONS.v1.api
|
||||
}
|
||||
|
||||
export type KernelMode = 'web' | 'desktop'
|
||||
export type KernelMode = "web" | "desktop"
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
|
@ -39,16 +39,16 @@ export function getKernelMode(): KernelMode {
|
|||
}
|
||||
|
||||
export function initKernel(mode?: KernelMode): KernelAPI {
|
||||
if (mode === 'desktop') {
|
||||
if (mode === "desktop") {
|
||||
const kernel: KernelAPI = {
|
||||
info: {
|
||||
name: "desktop-kernel",
|
||||
version: { major: 1, minor: 0, patch: 0 },
|
||||
capabilities: ["basic-io"]
|
||||
capabilities: ["basic-io"],
|
||||
},
|
||||
io: DESKTOP_IO_IMPLS.v1.api,
|
||||
relay: DESKTOP_RELAY_IMPLS.v1.api,
|
||||
store: DESKTOP_STORE_IMPLS.v1.api
|
||||
store: DESKTOP_STORE_IMPLS.v1.api,
|
||||
}
|
||||
|
||||
window.__KERNEL__ = kernel
|
||||
|
|
@ -58,11 +58,11 @@ export function initKernel(mode?: KernelMode): KernelAPI {
|
|||
info: {
|
||||
name: "web-kernel",
|
||||
version: { major: 1, minor: 0, patch: 0 },
|
||||
capabilities: ["basic-io"]
|
||||
capabilities: ["basic-io"],
|
||||
},
|
||||
io: WEB_IO_IMPLS.v1.api,
|
||||
relay: WEB_RELAY_IMPLS.v1.api,
|
||||
store: WEB_STORE_IMPLS.v1.api
|
||||
store: WEB_STORE_IMPLS.v1.api,
|
||||
}
|
||||
|
||||
window.__KERNEL__ = kernel
|
||||
|
|
@ -79,7 +79,7 @@ export type {
|
|||
EventCallback,
|
||||
UnlistenFn,
|
||||
IoV1,
|
||||
} from '@io/v/1'
|
||||
} from "@io/v/1"
|
||||
|
||||
export type {
|
||||
RelayRequest,
|
||||
|
|
@ -98,15 +98,15 @@ export type {
|
|||
RelayCapabilities,
|
||||
RelayEventEmitter,
|
||||
RelayRequestEvents,
|
||||
StatusCode
|
||||
} from '@relay/v/1'
|
||||
StatusCode,
|
||||
} from "@relay/v/1"
|
||||
|
||||
export {
|
||||
content,
|
||||
body,
|
||||
MediaType,
|
||||
relayRequestToNativeAdapter
|
||||
} from '@relay/v/1'
|
||||
relayRequestToNativeAdapter,
|
||||
} from "@relay/v/1"
|
||||
|
||||
export type {
|
||||
StoreCapability,
|
||||
|
|
@ -120,4 +120,4 @@ export type {
|
|||
StoredData,
|
||||
StoreEventEmitter,
|
||||
StoreV1,
|
||||
} from '@store/v/1'
|
||||
} from "@store/v/1"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { implementation as ioV1 } from './v/1'
|
||||
import { implementation as ioV1 } from "./v/1"
|
||||
|
||||
export const IO_IMPLS = {
|
||||
v1: ioV1,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
import { VersionedAPI } from '@type/versioning'
|
||||
import { IoV1, SaveFileWithDialogOptions, OpenExternalLinkOptions } from '@io/v/1'
|
||||
import { VersionedAPI } from "@type/versioning"
|
||||
import {
|
||||
IoV1,
|
||||
SaveFileWithDialogOptions,
|
||||
OpenExternalLinkOptions,
|
||||
} from "@io/v/1"
|
||||
|
||||
import { save } from "@tauri-apps/plugin-dialog"
|
||||
import { writeFile, writeTextFile } from "@tauri-apps/plugin-fs"
|
||||
import { open } from "@tauri-apps/plugin-shell"
|
||||
import { listen as tauriListen, emit as tauriEmit, Event } from '@tauri-apps/api/event'
|
||||
import {
|
||||
listen as tauriListen,
|
||||
emit as tauriEmit,
|
||||
Event,
|
||||
} from "@tauri-apps/api/event"
|
||||
|
||||
export const implementation: VersionedAPI<IoV1> = {
|
||||
version: { major: 1, minor: 0, patch: 0 },
|
||||
|
|
@ -47,6 +55,6 @@ export const implementation: VersionedAPI<IoV1> = {
|
|||
|
||||
async emit(event: string, payload?: unknown) {
|
||||
await tauriEmit(event, payload)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { implementation as ioV1 } from './v/1'
|
||||
import { implementation as ioV1 } from "./v/1"
|
||||
|
||||
export const IO_IMPLS = {
|
||||
v1: ioV1,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
import { VersionedAPI } from '@type/versioning'
|
||||
import { IoV1, SaveFileWithDialogOptions, OpenExternalLinkOptions, Event } from '@io/v/1'
|
||||
import { pipe } from 'fp-ts/function'
|
||||
import * as S from 'fp-ts/string'
|
||||
import * as RNEA from 'fp-ts/ReadonlyNonEmptyArray'
|
||||
import { VersionedAPI } from "@type/versioning"
|
||||
import {
|
||||
IoV1,
|
||||
SaveFileWithDialogOptions,
|
||||
OpenExternalLinkOptions,
|
||||
Event,
|
||||
} from "@io/v/1"
|
||||
import { pipe } from "fp-ts/function"
|
||||
import * as S from "fp-ts/string"
|
||||
import * as RNEA from "fp-ts/ReadonlyNonEmptyArray"
|
||||
|
||||
export const implementation: VersionedAPI<IoV1> = {
|
||||
version: { major: 1, minor: 0, patch: 0 },
|
||||
|
|
@ -20,7 +25,9 @@ export const implementation: VersionedAPI<IoV1> = {
|
|||
const url = URL.createObjectURL(file)
|
||||
|
||||
a.href = url
|
||||
a.download = opts.suggestedFilename ?? pipe(
|
||||
a.download =
|
||||
opts.suggestedFilename ??
|
||||
pipe(
|
||||
url,
|
||||
S.split("/"),
|
||||
RNEA.last,
|
||||
|
|
@ -49,42 +56,42 @@ export const implementation: VersionedAPI<IoV1> = {
|
|||
},
|
||||
|
||||
async listen<T>(event: string, handler: (event: Event<T>) => void) {
|
||||
const listener = (e: HashChangeEvent) => {
|
||||
const listener = (_e: HashChangeEvent) => {
|
||||
const hash = window.location.hash
|
||||
if (hash && hash.startsWith(`#${event}:`)) {
|
||||
const payload = hash.slice(event.length + 2) // Remove #event:
|
||||
handler({
|
||||
event,
|
||||
id: Date.now(),
|
||||
payload: JSON.parse(payload) as T
|
||||
payload: JSON.parse(payload) as T,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('hashchange', listener)
|
||||
return () => window.removeEventListener('hashchange', listener)
|
||||
window.addEventListener("hashchange", listener)
|
||||
return () => window.removeEventListener("hashchange", listener)
|
||||
},
|
||||
|
||||
async once<T>(event: string, handler: (event: Event<T>) => void) {
|
||||
const listener = (e: HashChangeEvent) => {
|
||||
const listener = (_e: HashChangeEvent) => {
|
||||
const hash = window.location.hash
|
||||
if (hash && hash.startsWith(`#${event}:`)) {
|
||||
const payload = hash.slice(event.length + 2) // Remove #event:
|
||||
window.removeEventListener('hashchange', listener)
|
||||
window.removeEventListener("hashchange", listener)
|
||||
handler({
|
||||
event,
|
||||
id: Date.now(),
|
||||
payload: JSON.parse(payload) as T
|
||||
payload: JSON.parse(payload) as T,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('hashchange', listener)
|
||||
return () => window.removeEventListener('hashchange', listener)
|
||||
window.addEventListener("hashchange", listener)
|
||||
return () => window.removeEventListener("hashchange", listener)
|
||||
},
|
||||
|
||||
async emit(event: string, payload?: unknown) {
|
||||
window.location.hash = `${event}:${JSON.stringify(payload)}`
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { v1 } from './v/1'
|
||||
import { v1 } from "./v/1"
|
||||
|
||||
export type {
|
||||
IoV1,
|
||||
|
|
@ -8,8 +8,8 @@ export type {
|
|||
OpenExternalLinkResponse,
|
||||
Event,
|
||||
EventCallback,
|
||||
UnlistenFn
|
||||
} from './v/1'
|
||||
UnlistenFn,
|
||||
} from "./v/1"
|
||||
|
||||
export const VERSIONS = {
|
||||
v1,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { VersionedAPI } from '@type/versioning'
|
||||
import type { VersionedAPI } from "@type/versioning"
|
||||
|
||||
export interface Event<T> {
|
||||
event: string
|
||||
|
|
@ -42,20 +42,11 @@ export interface IoV1 {
|
|||
opts: OpenExternalLinkOptions
|
||||
) => Promise<OpenExternalLinkResponse>
|
||||
|
||||
listen: <T>(
|
||||
event: string,
|
||||
handler: EventCallback<T>
|
||||
) => Promise<UnlistenFn>
|
||||
listen: <T>(event: string, handler: EventCallback<T>) => Promise<UnlistenFn>
|
||||
|
||||
once: <T>(
|
||||
event: string,
|
||||
handler: EventCallback<T>
|
||||
) => Promise<UnlistenFn>
|
||||
once: <T>(event: string, handler: EventCallback<T>) => Promise<UnlistenFn>
|
||||
|
||||
emit: (
|
||||
event: string,
|
||||
payload?: unknown
|
||||
) => Promise<void>
|
||||
emit: (event: string, payload?: unknown) => Promise<void>
|
||||
}
|
||||
|
||||
export const v1: VersionedAPI<IoV1> = {
|
||||
|
|
@ -66,5 +57,5 @@ export const v1: VersionedAPI<IoV1> = {
|
|||
listen: async () => () => {},
|
||||
once: async () => () => {},
|
||||
emit: async () => {},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { implementation as relayV1 } from './v/1'
|
||||
import { implementation as relayV1 } from "./v/1"
|
||||
|
||||
export const RELAY_IMPLS = {
|
||||
v1: relayV1,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { VersionedAPI } from '@type/versioning'
|
||||
import type { VersionedAPI } from "@type/versioning"
|
||||
import {
|
||||
type RelayV1,
|
||||
type RelayRequest,
|
||||
|
|
@ -8,76 +8,61 @@ import {
|
|||
type RelayError,
|
||||
body,
|
||||
relayRequestToNativeAdapter,
|
||||
} from '@relay/v/1'
|
||||
import * as E from 'fp-ts/Either'
|
||||
} from "@relay/v/1"
|
||||
import * as E from "fp-ts/Either"
|
||||
|
||||
import {
|
||||
execute,
|
||||
cancel,
|
||||
type Request,
|
||||
type RequestResult
|
||||
} from '@hoppscotch/plugin-relay'
|
||||
type Request as _Request,
|
||||
type RequestResult,
|
||||
} from "@hoppscotch/plugin-relay"
|
||||
|
||||
export const implementation: VersionedAPI<RelayV1> = {
|
||||
version: { major: 1, minor: 0, patch: 0 },
|
||||
api: {
|
||||
id: 'desktop',
|
||||
id: "desktop",
|
||||
capabilities: {
|
||||
method: new Set([
|
||||
'GET',
|
||||
'POST',
|
||||
'PUT',
|
||||
'DELETE',
|
||||
'PATCH',
|
||||
'HEAD',
|
||||
'OPTIONS'
|
||||
]),
|
||||
header: new Set([
|
||||
'stringvalue',
|
||||
'arrayvalue',
|
||||
'multivalue'
|
||||
"GET",
|
||||
"POST",
|
||||
"PUT",
|
||||
"DELETE",
|
||||
"PATCH",
|
||||
"HEAD",
|
||||
"OPTIONS",
|
||||
]),
|
||||
header: new Set(["stringvalue", "arrayvalue", "multivalue"]),
|
||||
content: new Set([
|
||||
'text',
|
||||
'json',
|
||||
'xml',
|
||||
'form',
|
||||
'binary',
|
||||
'multipart',
|
||||
'urlencoded',
|
||||
'stream',
|
||||
'compression'
|
||||
]),
|
||||
auth: new Set([
|
||||
'basic',
|
||||
'bearer',
|
||||
'digest',
|
||||
'oauth2',
|
||||
'apikey'
|
||||
"text",
|
||||
"json",
|
||||
"xml",
|
||||
"form",
|
||||
"binary",
|
||||
"multipart",
|
||||
"urlencoded",
|
||||
"stream",
|
||||
"compression",
|
||||
]),
|
||||
auth: new Set(["basic", "bearer", "digest", "oauth2", "apikey"]),
|
||||
security: new Set([
|
||||
'clientcertificates',
|
||||
'cacertificates',
|
||||
'certificatevalidation',
|
||||
'hostverification',
|
||||
'peerverification'
|
||||
]),
|
||||
proxy: new Set([
|
||||
'http',
|
||||
'https',
|
||||
'authentication',
|
||||
'certificates'
|
||||
"clientcertificates",
|
||||
"cacertificates",
|
||||
"certificatevalidation",
|
||||
"hostverification",
|
||||
"peerverification",
|
||||
]),
|
||||
proxy: new Set(["http", "https", "authentication", "certificates"]),
|
||||
advanced: new Set([
|
||||
'retry',
|
||||
'redirects',
|
||||
'timeout',
|
||||
'cookies',
|
||||
'keepalive',
|
||||
'tcpoptions',
|
||||
'http2',
|
||||
'http3'
|
||||
])
|
||||
"retry",
|
||||
"redirects",
|
||||
"timeout",
|
||||
"cookies",
|
||||
"keepalive",
|
||||
"tcpoptions",
|
||||
"http2",
|
||||
"http3",
|
||||
]),
|
||||
},
|
||||
|
||||
canHandle(request: RelayRequest) {
|
||||
|
|
@ -86,16 +71,19 @@ export const implementation: VersionedAPI<RelayV1> = {
|
|||
kind: "unsupported_feature",
|
||||
feature: "method",
|
||||
message: `Method ${request.method} is not supported`,
|
||||
relay: "desktop"
|
||||
relay: "desktop",
|
||||
})
|
||||
}
|
||||
|
||||
if (request.content && !this.capabilities.content.has(request.content.kind)) {
|
||||
if (
|
||||
request.content &&
|
||||
!this.capabilities.content.has(request.content.kind)
|
||||
) {
|
||||
return E.left({
|
||||
kind: "unsupported_feature",
|
||||
feature: "content",
|
||||
message: `Content type ${request.content.kind} is not supported`,
|
||||
relay: "desktop"
|
||||
relay: "desktop",
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -104,25 +92,33 @@ export const implementation: VersionedAPI<RelayV1> = {
|
|||
kind: "unsupported_feature",
|
||||
feature: "authentication",
|
||||
message: `Authentication type ${request.auth.kind} is not supported`,
|
||||
relay: "desktop"
|
||||
relay: "desktop",
|
||||
})
|
||||
}
|
||||
|
||||
if (request.security?.certificates && !this.capabilities.security.has('clientcertificates')) {
|
||||
if (
|
||||
request.security?.certificates &&
|
||||
!this.capabilities.security.has("clientcertificates")
|
||||
) {
|
||||
return E.left({
|
||||
kind: "unsupported_feature",
|
||||
feature: "security",
|
||||
message: "Client certificates are not supported",
|
||||
relay: "desktop"
|
||||
relay: "desktop",
|
||||
})
|
||||
}
|
||||
|
||||
if (request.proxy && !this.capabilities.proxy.has(request.proxy.url.startsWith('https') ? 'https' : 'http')) {
|
||||
if (
|
||||
request.proxy &&
|
||||
!this.capabilities.proxy.has(
|
||||
request.proxy.url.startsWith("https") ? "https" : "http"
|
||||
)
|
||||
) {
|
||||
return E.left({
|
||||
kind: "unsupported_feature",
|
||||
feature: "proxy",
|
||||
message: `Proxy protocol ${request.proxy.url.split(':')[0]} is not supported`,
|
||||
relay: "desktop"
|
||||
message: `Proxy protocol ${request.proxy.url.split(":")[0]} is not supported`,
|
||||
relay: "desktop",
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -133,11 +129,11 @@ export const implementation: VersionedAPI<RelayV1> = {
|
|||
const emitter: RelayEventEmitter<RelayRequestEvents> = {
|
||||
on: () => () => {},
|
||||
once: () => () => {},
|
||||
off: () => {}
|
||||
off: () => {},
|
||||
}
|
||||
|
||||
const responsePromise = relayRequestToNativeAdapter(request)
|
||||
.then(request => {
|
||||
.then((request) => {
|
||||
// SAFETY: Type assertion is safe because:
|
||||
// 1. The capabilities system prevents requests with unsupported methods from reaching this point
|
||||
// 2. Content types not supported by the plugin are filtered by capabilities
|
||||
|
|
@ -160,7 +156,7 @@ export const implementation: VersionedAPI<RelayV1> = {
|
|||
return execute(pluginRequest)
|
||||
})
|
||||
.then((result: RequestResult): E.Either<RelayError, RelayResponse> => {
|
||||
if (result.kind === 'success') {
|
||||
if (result.kind === "success") {
|
||||
const response: RelayResponse = {
|
||||
id: result.response.id,
|
||||
status: result.response.status,
|
||||
|
|
@ -168,14 +164,17 @@ export const implementation: VersionedAPI<RelayV1> = {
|
|||
version: result.response.version,
|
||||
headers: result.response.headers,
|
||||
cookies: result.response.cookies,
|
||||
body: body.body(result.response.body.body, result.response.body.mediaType),
|
||||
body: body.body(
|
||||
result.response.body.body,
|
||||
result.response.body.mediaType
|
||||
),
|
||||
meta: {
|
||||
timing: {
|
||||
start: result.response.meta.timing.start,
|
||||
end: result.response.meta.timing.end,
|
||||
},
|
||||
size: result.response.meta.size,
|
||||
}
|
||||
},
|
||||
}
|
||||
return E.right(response)
|
||||
}
|
||||
|
|
@ -183,18 +182,21 @@ export const implementation: VersionedAPI<RelayV1> = {
|
|||
})
|
||||
.catch((error: unknown): E.Either<RelayError, RelayResponse> => {
|
||||
const networkError: RelayError = {
|
||||
kind: 'network',
|
||||
message: error instanceof Error ? error.message : 'Unknown error occurred',
|
||||
cause: error
|
||||
kind: "network",
|
||||
message:
|
||||
error instanceof Error ? error.message : "Unknown error occurred",
|
||||
cause: error,
|
||||
}
|
||||
return E.left(networkError)
|
||||
})
|
||||
|
||||
return {
|
||||
cancel: async () => { await cancel(request.id) },
|
||||
cancel: async () => {
|
||||
await cancel(request.id)
|
||||
},
|
||||
emitter,
|
||||
response: responsePromise
|
||||
}
|
||||
}
|
||||
response: responsePromise,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { implementation as relayV1 } from './v/1'
|
||||
import { implementation as relayV1 } from "./v/1"
|
||||
|
||||
export const RELAY_IMPLS = {
|
||||
v1: relayV1,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
} from "@relay/v/1"
|
||||
import type { VersionedAPI } from "@type/versioning"
|
||||
|
||||
import { AwsV4Signer } from "aws4fetch"
|
||||
import { AwsV4Signer as _AwsV4Signer } from "aws4fetch"
|
||||
import axios, { AxiosRequestConfig } from "axios"
|
||||
|
||||
import * as E from "fp-ts/Either"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { v1 } from './v/1'
|
||||
import { v1 } from "./v/1"
|
||||
|
||||
export type {
|
||||
RelayV1,
|
||||
} from './v/1'
|
||||
export type { RelayV1 } from "./v/1"
|
||||
|
||||
export const VERSIONS = {
|
||||
v1,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { Request, Response } from '@hoppscotch/plugin-relay'
|
||||
import type { VersionedAPI } from '@type/versioning'
|
||||
import { Request, Response } from "@hoppscotch/plugin-relay"
|
||||
import type { VersionedAPI } from "@type/versioning"
|
||||
|
||||
export type PluginRequest = Request
|
||||
export type PluginResponse = Response
|
||||
|
||||
import * as E from 'fp-ts/Either'
|
||||
import * as O from 'fp-ts/Option'
|
||||
import { pipe } from 'fp-ts/function'
|
||||
import * as E from "fp-ts/Either"
|
||||
import * as O from "fp-ts/Option"
|
||||
import { pipe } from "fp-ts/function"
|
||||
|
||||
export type Method =
|
||||
| "GET" // Retrieve resource
|
||||
|
|
@ -101,7 +101,7 @@ export enum MediaType {
|
|||
TEXT_XML = "text/xml",
|
||||
APPLICATION_FORM = "application/x-www-form-urlencoded",
|
||||
APPLICATION_OCTET = "application/octet-stream",
|
||||
MULTIPART_FORM = "multipart/form-data"
|
||||
MULTIPART_FORM = "multipart/form-data",
|
||||
}
|
||||
|
||||
export type ContentType =
|
||||
|
|
@ -109,7 +109,12 @@ export type ContentType =
|
|||
| { kind: "json"; content: unknown; mediaType: MediaType | string }
|
||||
| { kind: "xml"; content: string; mediaType: MediaType | string }
|
||||
| { kind: "form"; content: FormData; mediaType: MediaType | string }
|
||||
| { kind: "binary"; content: Uint8Array; mediaType: MediaType | string; filename?: string }
|
||||
| {
|
||||
kind: "binary"
|
||||
content: Uint8Array
|
||||
mediaType: MediaType | string
|
||||
filename?: string
|
||||
}
|
||||
| { kind: "multipart"; content: FormData; mediaType: MediaType | string }
|
||||
| { kind: "urlencoded"; content: string; mediaType: MediaType | string }
|
||||
| { kind: "stream"; content: ReadableStream; mediaType: MediaType | string }
|
||||
|
|
@ -198,15 +203,21 @@ export type CertificateType =
|
|||
|
||||
export interface RelayRequestEvents {
|
||||
progress: {
|
||||
phase: 'upload' | 'download'
|
||||
phase: "upload" | "download"
|
||||
loaded: number
|
||||
total?: number
|
||||
}
|
||||
stateChange: {
|
||||
state: 'preparing' | 'connecting' | 'sending' | 'waiting' | 'receiving' | 'done'
|
||||
state:
|
||||
| "preparing"
|
||||
| "connecting"
|
||||
| "sending"
|
||||
| "waiting"
|
||||
| "receiving"
|
||||
| "done"
|
||||
}
|
||||
authChallenge: {
|
||||
type: 'basic' | 'digest' | 'oauth2'
|
||||
type: "basic" | "digest" | "oauth2"
|
||||
params: Record<string, string>
|
||||
}
|
||||
cookieReceived: {
|
||||
|
|
@ -217,84 +228,84 @@ export interface RelayRequestEvents {
|
|||
expires?: Date
|
||||
secure?: boolean
|
||||
httpOnly?: boolean
|
||||
sameSite?: 'Strict' | 'Lax' | 'None'
|
||||
sameSite?: "Strict" | "Lax" | "None"
|
||||
}
|
||||
error: {
|
||||
phase: 'preparation' | 'connection' | 'request' | 'response'
|
||||
phase: "preparation" | "connection" | "request" | "response"
|
||||
error: RelayError
|
||||
}
|
||||
}
|
||||
|
||||
export type RelayEventEmitter<T> = {
|
||||
on<K extends keyof T>(event: K, handler: (payload: T[K]) => void): () => void
|
||||
once<K extends keyof T>(event: K, handler: (payload: T[K]) => void): () => void
|
||||
once<K extends keyof T>(
|
||||
event: K,
|
||||
handler: (payload: T[K]) => void
|
||||
): () => void
|
||||
off<K extends keyof T>(event: K, handler: (payload: T[K]) => void): void
|
||||
}
|
||||
|
||||
// NOTE: RelayCapabilities and their corresponding objects being two separate types
|
||||
// even with sometimes identical contents is intentional.
|
||||
export type MethodCapability =
|
||||
| 'GET'
|
||||
| 'POST'
|
||||
| 'PUT'
|
||||
| 'DELETE'
|
||||
| 'PATCH'
|
||||
| 'HEAD'
|
||||
| 'OPTIONS'
|
||||
| 'CONNECT'
|
||||
| 'TRACE'
|
||||
| "GET"
|
||||
| "POST"
|
||||
| "PUT"
|
||||
| "DELETE"
|
||||
| "PATCH"
|
||||
| "HEAD"
|
||||
| "OPTIONS"
|
||||
| "CONNECT"
|
||||
| "TRACE"
|
||||
|
||||
export type HeaderCapability =
|
||||
| 'stringvalue'
|
||||
| 'arrayvalue'
|
||||
| 'multivalue'
|
||||
export type HeaderCapability = "stringvalue" | "arrayvalue" | "multivalue"
|
||||
|
||||
export type ContentCapability =
|
||||
| 'text'
|
||||
| 'json'
|
||||
| 'xml'
|
||||
| 'form'
|
||||
| 'binary'
|
||||
| 'multipart'
|
||||
| 'urlencoded'
|
||||
| 'stream'
|
||||
| 'compression'
|
||||
| "text"
|
||||
| "json"
|
||||
| "xml"
|
||||
| "form"
|
||||
| "binary"
|
||||
| "multipart"
|
||||
| "urlencoded"
|
||||
| "stream"
|
||||
| "compression"
|
||||
|
||||
export type AuthCapability =
|
||||
| 'none'
|
||||
| 'basic'
|
||||
| 'bearer'
|
||||
| 'digest'
|
||||
| 'oauth2'
|
||||
| 'apikey'
|
||||
| 'aws'
|
||||
| 'mtls'
|
||||
| "none"
|
||||
| "basic"
|
||||
| "bearer"
|
||||
| "digest"
|
||||
| "oauth2"
|
||||
| "apikey"
|
||||
| "aws"
|
||||
| "mtls"
|
||||
|
||||
export type SecurityCapability =
|
||||
| 'clientcertificates'
|
||||
| 'cacertificates'
|
||||
| 'certificatevalidation'
|
||||
| 'hostverification'
|
||||
| 'peerverification'
|
||||
| "clientcertificates"
|
||||
| "cacertificates"
|
||||
| "certificatevalidation"
|
||||
| "hostverification"
|
||||
| "peerverification"
|
||||
|
||||
export type ProxyCapability =
|
||||
| 'http'
|
||||
| 'https'
|
||||
| 'socks'
|
||||
| 'authentication'
|
||||
| 'certificates'
|
||||
| "http"
|
||||
| "https"
|
||||
| "socks"
|
||||
| "authentication"
|
||||
| "certificates"
|
||||
|
||||
export type AdvancedCapability =
|
||||
| 'retry'
|
||||
| 'redirects'
|
||||
| 'timeout'
|
||||
| 'cookies'
|
||||
| 'keepalive'
|
||||
| 'tcpoptions'
|
||||
| 'ipv6'
|
||||
| 'http2'
|
||||
| 'http3'
|
||||
| 'localaccess'
|
||||
| "retry"
|
||||
| "redirects"
|
||||
| "timeout"
|
||||
| "cookies"
|
||||
| "keepalive"
|
||||
| "tcpoptions"
|
||||
| "ipv6"
|
||||
| "http2"
|
||||
| "http3"
|
||||
| "localaccess"
|
||||
|
||||
export interface RelayCapabilities {
|
||||
method: Set<MethodCapability>
|
||||
|
|
@ -403,7 +414,7 @@ export interface RelayResponse {
|
|||
expires?: Date
|
||||
secure?: boolean
|
||||
httpOnly?: boolean
|
||||
sameSite?: 'Strict' | 'Lax' | 'None'
|
||||
sameSite?: "Strict" | "Lax" | "None"
|
||||
}>
|
||||
body: RelayResponseBody
|
||||
|
||||
|
|
@ -457,17 +468,17 @@ export interface RelayV1 {
|
|||
|
||||
canHandle(request: RelayRequest): E.Either<UnsupportedFeatureError, true>
|
||||
|
||||
execute(
|
||||
request: RelayRequest
|
||||
): {
|
||||
execute(request: RelayRequest): {
|
||||
cancel: () => Promise<void>
|
||||
emitter: RelayEventEmitter<RelayRequestEvents>
|
||||
response: Promise<E.Either<RelayError, RelayResponse>>
|
||||
}
|
||||
}
|
||||
|
||||
export const hasCapability = <T>(capabilities: Set<T>, capability: T): boolean =>
|
||||
capabilities.has(capability)
|
||||
export const hasCapability = <T>(
|
||||
capabilities: Set<T>,
|
||||
capability: T
|
||||
): boolean => capabilities.has(capability)
|
||||
|
||||
export function findSuitableRelay(
|
||||
request: RelayRequest,
|
||||
|
|
@ -481,9 +492,9 @@ export function findSuitableRelay(
|
|||
}
|
||||
|
||||
const errors = relays
|
||||
.map(i => i.canHandle(request))
|
||||
.map((i) => i.canHandle(request))
|
||||
.filter(E.isLeft)
|
||||
.map(e => e.left)
|
||||
.map((e) => e.left)
|
||||
|
||||
return E.left(errors[0])
|
||||
}
|
||||
|
|
@ -494,9 +505,11 @@ export const body = {
|
|||
contentType?: MediaType | string
|
||||
): RelayResponseBody => ({
|
||||
body: new Uint8Array(body),
|
||||
mediaType: typeof contentType === 'string'
|
||||
? Object.values(MediaType).find(t => contentType.includes(t)) ?? MediaType.APPLICATION_OCTET
|
||||
: contentType ?? MediaType.APPLICATION_OCTET
|
||||
mediaType:
|
||||
typeof contentType === "string"
|
||||
? (Object.values(MediaType).find((t) => contentType.includes(t)) ??
|
||||
MediaType.APPLICATION_OCTET)
|
||||
: (contentType ?? MediaType.APPLICATION_OCTET),
|
||||
}),
|
||||
}
|
||||
|
||||
|
|
@ -511,22 +524,18 @@ export const transform = {
|
|||
urlencoded: (arg: string | Record<string, any>): string =>
|
||||
pipe(
|
||||
arg,
|
||||
(input) => typeof input === 'string'
|
||||
? O.some(input)
|
||||
: O.none,
|
||||
(input) => (typeof input === "string" ? O.some(input) : O.none),
|
||||
O.getOrElse(() => {
|
||||
const params = new URLSearchParams()
|
||||
const obj = arg as Record<string, any>
|
||||
|
||||
Object.entries(obj)
|
||||
.filter(([_, value]) => value !== undefined && value !== null)
|
||||
.forEach(([key, value]) =>
|
||||
params.append(key, value.toString())
|
||||
)
|
||||
.forEach(([key, value]) => params.append(key, value.toString()))
|
||||
|
||||
return params.toString()
|
||||
})
|
||||
)
|
||||
),
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -552,13 +561,10 @@ export const content = {
|
|||
* - Pre-stringified JSON (to avoid encoding escapes)
|
||||
* - Custom text formats
|
||||
*/
|
||||
text: (
|
||||
content: string,
|
||||
mediaType?: MediaType | string
|
||||
): ContentType => ({
|
||||
text: (content: string, mediaType?: MediaType | string): ContentType => ({
|
||||
kind: "text",
|
||||
content: transform.text(content),
|
||||
mediaType: mediaType ?? MediaType.TEXT_PLAIN
|
||||
mediaType: mediaType ?? MediaType.TEXT_PLAIN,
|
||||
}),
|
||||
|
||||
/**
|
||||
|
|
@ -566,25 +572,19 @@ export const content = {
|
|||
* Note: If you already have a JSON string, consider using `text()`
|
||||
* with `APPLICATION_JSON` mediaType to avoid double-encoding.
|
||||
*/
|
||||
json: <T>(
|
||||
content: T,
|
||||
mediaType?: MediaType | string
|
||||
): ContentType => ({
|
||||
json: <T>(content: T, mediaType?: MediaType | string): ContentType => ({
|
||||
kind: "json",
|
||||
content: transform.json(content),
|
||||
mediaType: mediaType ?? MediaType.APPLICATION_JSON
|
||||
mediaType: mediaType ?? MediaType.APPLICATION_JSON,
|
||||
}),
|
||||
|
||||
/**
|
||||
* Creates XML content. Currently processed as text.
|
||||
*/
|
||||
xml: (
|
||||
content: string,
|
||||
mediaType?: MediaType | string
|
||||
): ContentType => ({
|
||||
xml: (content: string, mediaType?: MediaType | string): ContentType => ({
|
||||
kind: "xml",
|
||||
content: transform.xml(content),
|
||||
mediaType: mediaType ?? MediaType.APPLICATION_XML
|
||||
mediaType: mediaType ?? MediaType.APPLICATION_XML,
|
||||
}),
|
||||
|
||||
/**
|
||||
|
|
@ -593,7 +593,7 @@ export const content = {
|
|||
form: (content: FormData, mediaType?: MediaType | string): ContentType => ({
|
||||
kind: "form",
|
||||
content: transform.form(content),
|
||||
mediaType: mediaType ?? MediaType.APPLICATION_FORM
|
||||
mediaType: mediaType ?? MediaType.APPLICATION_FORM,
|
||||
}),
|
||||
|
||||
/**
|
||||
|
|
@ -607,35 +607,44 @@ export const content = {
|
|||
kind: "binary",
|
||||
content: transform.binary(content),
|
||||
mediaType,
|
||||
filename
|
||||
filename,
|
||||
}),
|
||||
|
||||
/**
|
||||
* Creates multipart form content with file upload support.
|
||||
*/
|
||||
multipart: (content: FormData, mediaType?: MediaType | string): ContentType => ({
|
||||
multipart: (
|
||||
content: FormData,
|
||||
mediaType?: MediaType | string
|
||||
): ContentType => ({
|
||||
kind: "multipart",
|
||||
content: transform.multipart(content),
|
||||
mediaType: mediaType ?? MediaType.MULTIPART_FORM
|
||||
mediaType: mediaType ?? MediaType.MULTIPART_FORM,
|
||||
}),
|
||||
|
||||
/**
|
||||
* Creates URL-encoded content from string or object.
|
||||
*/
|
||||
urlencoded: (content: string | Record<string, any>, mediaType?: MediaType | string): ContentType => ({
|
||||
urlencoded: (
|
||||
content: string | Record<string, any>,
|
||||
mediaType?: MediaType | string
|
||||
): ContentType => ({
|
||||
kind: "urlencoded",
|
||||
content: transform.urlencoded(content),
|
||||
mediaType: mediaType ?? MediaType.APPLICATION_FORM
|
||||
mediaType: mediaType ?? MediaType.APPLICATION_FORM,
|
||||
}),
|
||||
|
||||
/**
|
||||
* Creates streaming content for large payloads.
|
||||
*/
|
||||
stream: (content: ReadableStream, mediaType: MediaType | string): ContentType => ({
|
||||
stream: (
|
||||
content: ReadableStream,
|
||||
mediaType: MediaType | string
|
||||
): ContentType => ({
|
||||
kind: "stream",
|
||||
content: transform.stream(content),
|
||||
mediaType
|
||||
})
|
||||
mediaType,
|
||||
}),
|
||||
|
||||
// TODO: Raw content type for pass-through scenarios:
|
||||
// raw: (content: string, mediaType: MediaType | string): ContentType => ({
|
||||
|
|
@ -680,22 +689,21 @@ export const examples = {
|
|||
|
||||
// Custom XML schema
|
||||
soapXml: content.xml(
|
||||
'<soap:Envelope>...</soap:Envelope>',
|
||||
"<soap:Envelope>...</soap:Envelope>",
|
||||
"application/soap+xml"
|
||||
),
|
||||
|
||||
// Custom binary format
|
||||
customBinary: content.binary(
|
||||
new Uint8Array([0x89, 0x50, 0x4E, 0x47]),
|
||||
new Uint8Array([0x89, 0x50, 0x4e, 0x47]),
|
||||
"image/png"
|
||||
),
|
||||
|
||||
// Backwards compatible - uses defaults
|
||||
standardJson: content.json({ name: "John" }),
|
||||
standardText: content.text("Hello world")
|
||||
standardText: content.text("Hello world"),
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to convert standard `FormData` to array of arrays `[string, FormDataValue[]][]`
|
||||
*
|
||||
|
|
@ -713,7 +721,9 @@ export const examples = {
|
|||
* See: https://datatracker.ietf.org/doc/html/rfc7578#section-5.2
|
||||
* > Form processors given forms with a well-defined ordering SHOULD send back results in order.
|
||||
*/
|
||||
const makeFormDataSerializable = async (formData: FormData): Promise<[string, FormDataValue[]][]> => {
|
||||
const makeFormDataSerializable = async (
|
||||
formData: FormData
|
||||
): Promise<[string, FormDataValue[]][]> => {
|
||||
const m = new Map<string, FormDataValue[]>()
|
||||
|
||||
// @ts-expect-error: `formData.entries` does exist but isn't visible,
|
||||
|
|
@ -725,7 +735,7 @@ const makeFormDataSerializable = async (formData: FormData): Promise<[string, Fo
|
|||
kind: "file",
|
||||
filename: value instanceof File ? value.name : "unknown",
|
||||
contentType: value.type || "application/octet-stream",
|
||||
data: new Uint8Array(buffer)
|
||||
data: new Uint8Array(buffer),
|
||||
}
|
||||
|
||||
if (m.has(key)) {
|
||||
|
|
@ -736,7 +746,7 @@ const makeFormDataSerializable = async (formData: FormData): Promise<[string, Fo
|
|||
} else {
|
||||
const textEntry: FormDataValue = {
|
||||
kind: "text",
|
||||
value: value.toString()
|
||||
value: value.toString(),
|
||||
}
|
||||
|
||||
if (m.has(key)) {
|
||||
|
|
@ -751,11 +761,18 @@ const makeFormDataSerializable = async (formData: FormData): Promise<[string, Fo
|
|||
}
|
||||
|
||||
// Helper function to adapt a relay request to work with the plugin
|
||||
export const relayRequestToNativeAdapter = async (request: RelayRequest): Promise<Request> => {
|
||||
const adaptedRequest = { ...request };
|
||||
export const relayRequestToNativeAdapter = async (
|
||||
request: RelayRequest
|
||||
): Promise<Request> => {
|
||||
const adaptedRequest = { ...request }
|
||||
|
||||
if (adaptedRequest.content?.kind === "multipart" && adaptedRequest.content.content instanceof FormData) {
|
||||
const serializableFormData = await makeFormDataSerializable(adaptedRequest.content.content);
|
||||
if (
|
||||
adaptedRequest.content?.kind === "multipart" &&
|
||||
adaptedRequest.content.content instanceof FormData
|
||||
) {
|
||||
const serializableFormData = await makeFormDataSerializable(
|
||||
adaptedRequest.content.content
|
||||
)
|
||||
|
||||
adaptedRequest.content = {
|
||||
...adaptedRequest.content,
|
||||
|
|
@ -766,11 +783,11 @@ export const relayRequestToNativeAdapter = async (request: RelayRequest): Promis
|
|||
// `Maps` it seems like are serialized differently across platforms and serialization libraries,
|
||||
// while `Array` of `Array` tend to maintain more consistent behavior by the sheer ubiquity of it.
|
||||
// @ts-expect-error: This is intentional to work around SuperJSON serialization
|
||||
content: serializableFormData
|
||||
};
|
||||
content: serializableFormData,
|
||||
}
|
||||
}
|
||||
|
||||
return adaptedRequest as Request;
|
||||
return adaptedRequest as Request
|
||||
}
|
||||
|
||||
export const v1: VersionedAPI<RelayV1> = {
|
||||
|
|
@ -784,27 +801,28 @@ export const v1: VersionedAPI<RelayV1> = {
|
|||
auth: new Set<AuthCapability>(),
|
||||
security: new Set<SecurityCapability>(),
|
||||
proxy: new Set<ProxyCapability>(),
|
||||
advanced: new Set<AdvancedCapability>()
|
||||
advanced: new Set<AdvancedCapability>(),
|
||||
},
|
||||
canHandle: () => E.left({
|
||||
canHandle: () =>
|
||||
E.left({
|
||||
kind: "unsupported_feature",
|
||||
feature: "execution",
|
||||
message: "Default relay cannot handle requests",
|
||||
relay: "default"
|
||||
relay: "default",
|
||||
}),
|
||||
execute: () => ({
|
||||
cancel: async () => {},
|
||||
emitter: {
|
||||
on: () => () => {},
|
||||
once: () => () => {},
|
||||
off: () => {}
|
||||
off: () => {},
|
||||
},
|
||||
response: Promise.resolve(
|
||||
E.left({
|
||||
kind: "version",
|
||||
message: "Not implemented"
|
||||
})
|
||||
)
|
||||
message: "Not implemented",
|
||||
})
|
||||
}
|
||||
),
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { implementation as storeV1 } from './v/1'
|
||||
import { implementation as storeV1 } from "./v/1"
|
||||
|
||||
export const STORE_IMPLS = {
|
||||
v1: storeV1,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import * as E from 'fp-ts/Either';
|
||||
import * as E from "fp-ts/Either"
|
||||
|
||||
import { Store } from '@tauri-apps/plugin-store';
|
||||
import { Store } from "@tauri-apps/plugin-store"
|
||||
|
||||
import { VersionedAPI } from '@type/versioning';
|
||||
import { VersionedAPI } from "@type/versioning"
|
||||
import {
|
||||
StoreV1,
|
||||
StoreError,
|
||||
|
|
@ -11,187 +11,204 @@ import {
|
|||
StoredData,
|
||||
StoredDataSchema,
|
||||
StoreEventEmitter,
|
||||
} from '@store/v/1';
|
||||
} from "@store/v/1"
|
||||
|
||||
type NamespacedData = Record<string, Record<string, StoredData>>;
|
||||
type NamespacedData = Record<string, Record<string, StoredData>>
|
||||
|
||||
class TauriStoreManager {
|
||||
private static instances: Map<string, TauriStoreManager> = new Map();
|
||||
private store: Store | null = null;
|
||||
private listeners = new Map<string, Set<(payload: StoreEvents['change']) => void>>();
|
||||
private data: NamespacedData = {};
|
||||
private storePath: string;
|
||||
private static instances: Map<string, TauriStoreManager> = new Map()
|
||||
private store: Store | null = null
|
||||
private listeners = new Map<
|
||||
string,
|
||||
Set<(payload: StoreEvents["change"]) => void>
|
||||
>()
|
||||
private data: NamespacedData = {}
|
||||
private storePath: string
|
||||
|
||||
private constructor(storePath: string) {
|
||||
this.storePath = storePath;
|
||||
this.storePath = storePath
|
||||
}
|
||||
|
||||
static new(storePath: string): TauriStoreManager {
|
||||
if (TauriStoreManager.instances.has(storePath)) {
|
||||
return TauriStoreManager.instances.get(storePath)!;
|
||||
return TauriStoreManager.instances.get(storePath)!
|
||||
}
|
||||
|
||||
const instance = new TauriStoreManager(storePath);
|
||||
TauriStoreManager.instances.set(storePath, instance);
|
||||
return instance;
|
||||
const instance = new TauriStoreManager(storePath)
|
||||
TauriStoreManager.instances.set(storePath, instance)
|
||||
return instance
|
||||
}
|
||||
|
||||
static async closeAll(): Promise<void> {
|
||||
const closePromises = Array.from(TauriStoreManager.instances.values())
|
||||
.map(instance => instance.close());
|
||||
await Promise.all(closePromises);
|
||||
TauriStoreManager.instances.clear();
|
||||
const closePromises = Array.from(TauriStoreManager.instances.values()).map(
|
||||
(instance) => instance.close()
|
||||
)
|
||||
await Promise.all(closePromises)
|
||||
TauriStoreManager.instances.clear()
|
||||
}
|
||||
|
||||
static async closeStore(storePath: string): Promise<void> {
|
||||
const instance = TauriStoreManager.instances.get(storePath);
|
||||
const instance = TauriStoreManager.instances.get(storePath)
|
||||
if (instance) {
|
||||
await instance.close();
|
||||
TauriStoreManager.instances.delete(storePath);
|
||||
await instance.close()
|
||||
TauriStoreManager.instances.delete(storePath)
|
||||
}
|
||||
}
|
||||
|
||||
async init(): Promise<void> {
|
||||
if (!this.store) {
|
||||
this.store = await Store.load(this.storePath);
|
||||
const loadedData = await this.store.get<NamespacedData>('data');
|
||||
this.data = loadedData ?? {};
|
||||
this.store = await Store.load(this.storePath)
|
||||
const loadedData = await this.store.get<NamespacedData>("data")
|
||||
this.data = loadedData ?? {}
|
||||
|
||||
this.store.onChange((_, value: NamespacedData | undefined) => {
|
||||
if (value) {
|
||||
this.data = value;
|
||||
this.notifyListeners();
|
||||
this.data = value
|
||||
this.notifyListeners()
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private notifyListeners(): void {
|
||||
for (const [key, listeners] of this.listeners.entries()) {
|
||||
const [namespace, dataKey] = key.split(':');
|
||||
const value = this.data[namespace]?.[dataKey];
|
||||
listeners.forEach(listener =>
|
||||
const [namespace, dataKey] = key.split(":")
|
||||
const value = this.data[namespace]?.[dataKey]
|
||||
listeners.forEach((listener) =>
|
||||
listener({
|
||||
namespace,
|
||||
key: dataKey,
|
||||
value: value?.data,
|
||||
})
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async set(namespace: string, key: string, value: StoredData): Promise<void> {
|
||||
if (!this.store) throw new Error('Store not initialized');
|
||||
if (!this.store) throw new Error("Store not initialized")
|
||||
|
||||
const validated = StoredDataSchema.parse(value);
|
||||
this.data[namespace] = this.data[namespace] || {};
|
||||
this.data[namespace][key] = validated;
|
||||
await this.store.set('data', this.data);
|
||||
await this.store.save();
|
||||
const validated = StoredDataSchema.parse(value)
|
||||
this.data[namespace] = this.data[namespace] || {}
|
||||
this.data[namespace][key] = validated
|
||||
await this.store.set("data", this.data)
|
||||
await this.store.save()
|
||||
}
|
||||
|
||||
async getRaw(namespace: string, key: string): Promise<StoredData | undefined> {
|
||||
const rawValue = this.data[namespace]?.[key];
|
||||
if (!rawValue) return undefined;
|
||||
async getRaw(
|
||||
namespace: string,
|
||||
key: string
|
||||
): Promise<StoredData | undefined> {
|
||||
const rawValue = this.data[namespace]?.[key]
|
||||
if (!rawValue) return undefined
|
||||
|
||||
const validated = StoredDataSchema.parse(rawValue);
|
||||
return validated;
|
||||
const validated = StoredDataSchema.parse(rawValue)
|
||||
return validated
|
||||
}
|
||||
|
||||
async get<T>(namespace: string, key: string): Promise<T | undefined> {
|
||||
const storedData = await this.getRaw(namespace, key);
|
||||
return storedData?.data as T | undefined;
|
||||
const storedData = await this.getRaw(namespace, key)
|
||||
return storedData?.data as T | undefined
|
||||
}
|
||||
|
||||
async has(namespace: string, key: string): Promise<boolean> {
|
||||
return !!this.data[namespace]?.[key];
|
||||
return !!this.data[namespace]?.[key]
|
||||
}
|
||||
|
||||
async delete(namespace: string, key: string): Promise<boolean> {
|
||||
if (!this.store) throw new Error('Store not initialized');
|
||||
if (!this.store) throw new Error("Store not initialized")
|
||||
|
||||
if (this.data[namespace]?.[key]) {
|
||||
delete this.data[namespace][key];
|
||||
delete this.data[namespace][key]
|
||||
if (Object.keys(this.data[namespace]).length === 0) {
|
||||
delete this.data[namespace];
|
||||
delete this.data[namespace]
|
||||
}
|
||||
await this.store.set('data', this.data);
|
||||
await this.store.save();
|
||||
return true;
|
||||
await this.store.set("data", this.data)
|
||||
await this.store.save()
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
async clear(namespace?: string): Promise<void> {
|
||||
if (!this.store) throw new Error('Store not initialized');
|
||||
if (!this.store) throw new Error("Store not initialized")
|
||||
|
||||
if (namespace) {
|
||||
delete this.data[namespace];
|
||||
delete this.data[namespace]
|
||||
} else {
|
||||
this.data = {};
|
||||
this.data = {}
|
||||
}
|
||||
await this.store.set('data', this.data);
|
||||
await this.store.save();
|
||||
await this.store.set("data", this.data)
|
||||
await this.store.save()
|
||||
}
|
||||
|
||||
async listNamespaces(): Promise<string[]> {
|
||||
return Object.keys(this.data);
|
||||
return Object.keys(this.data)
|
||||
}
|
||||
|
||||
async listKeys(namespace: string): Promise<string[]> {
|
||||
return Object.keys(this.data[namespace] || {});
|
||||
return Object.keys(this.data[namespace] || {})
|
||||
}
|
||||
|
||||
async watch(namespace: string, key: string): Promise<StoreEventEmitter<StoreEvents>> {
|
||||
const watchKey = `${namespace}:${key}`;
|
||||
async watch(
|
||||
namespace: string,
|
||||
key: string
|
||||
): Promise<StoreEventEmitter<StoreEvents>> {
|
||||
const watchKey = `${namespace}:${key}`
|
||||
return {
|
||||
on: <K extends keyof StoreEvents>(
|
||||
event: K,
|
||||
handler: (payload: StoreEvents[K]) => void
|
||||
) => {
|
||||
if (event !== 'change') return () => {};
|
||||
if (event !== "change") return () => {}
|
||||
|
||||
if (!this.listeners.has(watchKey)) {
|
||||
this.listeners.set(watchKey, new Set());
|
||||
this.listeners.set(watchKey, new Set())
|
||||
}
|
||||
this.listeners.get(watchKey)!.add(handler as (payload: StoreEvents['change']) => void);
|
||||
return () => this.listeners.get(watchKey)?.delete(handler as (payload: StoreEvents['change']) => void);
|
||||
this.listeners
|
||||
.get(watchKey)!
|
||||
.add(handler as (payload: StoreEvents["change"]) => void)
|
||||
return () =>
|
||||
this.listeners
|
||||
.get(watchKey)
|
||||
?.delete(handler as (payload: StoreEvents["change"]) => void)
|
||||
},
|
||||
once: <K extends keyof StoreEvents>(
|
||||
event: K,
|
||||
handler: (payload: StoreEvents[K]) => void
|
||||
) => {
|
||||
if (event !== 'change') return () => {};
|
||||
if (event !== "change") return () => {}
|
||||
|
||||
const wrapper = (value: StoreEvents['change']) => {
|
||||
handler(value as StoreEvents[K]);
|
||||
this.listeners.get(watchKey)?.delete(wrapper);
|
||||
};
|
||||
const wrapper = (value: StoreEvents["change"]) => {
|
||||
handler(value as StoreEvents[K])
|
||||
this.listeners.get(watchKey)?.delete(wrapper)
|
||||
}
|
||||
|
||||
if (!this.listeners.has(watchKey)) {
|
||||
this.listeners.set(watchKey, new Set());
|
||||
this.listeners.set(watchKey, new Set())
|
||||
}
|
||||
this.listeners.get(watchKey)!.add(wrapper);
|
||||
return () => this.listeners.get(watchKey)?.delete(wrapper);
|
||||
this.listeners.get(watchKey)!.add(wrapper)
|
||||
return () => this.listeners.get(watchKey)?.delete(wrapper)
|
||||
},
|
||||
off: <K extends keyof StoreEvents>(
|
||||
event: K,
|
||||
handler: (payload: StoreEvents[K]) => void
|
||||
) => {
|
||||
if (event === 'change') {
|
||||
this.listeners.get(watchKey)?.delete(handler as (payload: StoreEvents['change']) => void);
|
||||
if (event === "change") {
|
||||
this.listeners
|
||||
.get(watchKey)
|
||||
?.delete(handler as (payload: StoreEvents["change"]) => void)
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
if (this.store) {
|
||||
await this.store.close();
|
||||
this.store = null;
|
||||
this.data = {};
|
||||
this.listeners.clear();
|
||||
TauriStoreManager.instances.delete(this.storePath);
|
||||
await this.store.close()
|
||||
this.store = null
|
||||
this.data = {}
|
||||
this.listeners.clear()
|
||||
TauriStoreManager.instances.delete(this.storePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -199,28 +216,41 @@ class TauriStoreManager {
|
|||
export const implementation: VersionedAPI<StoreV1> = {
|
||||
version: { major: 1, minor: 0, patch: 0 },
|
||||
api: {
|
||||
id: 'tauri-store',
|
||||
capabilities: new Set(['permanent', 'structured', 'watch', 'namespace', 'secure']),
|
||||
id: "tauri-store",
|
||||
capabilities: new Set([
|
||||
"permanent",
|
||||
"structured",
|
||||
"watch",
|
||||
"namespace",
|
||||
"secure",
|
||||
]),
|
||||
|
||||
async init(storePath: string) {
|
||||
try {
|
||||
const manager = TauriStoreManager.new(storePath);
|
||||
await manager.init();
|
||||
return E.right(undefined);
|
||||
const manager = TauriStoreManager.new(storePath)
|
||||
await manager.init()
|
||||
return E.right(undefined)
|
||||
} catch (error) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: error instanceof Error ? error.message : "Unknown error",
|
||||
cause: error,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async set(storePath: string, namespace: string, key: string, value: unknown, options?: StorageOptions): Promise<E.Either<StoreError, void>> {
|
||||
async set(
|
||||
storePath: string,
|
||||
namespace: string,
|
||||
key: string,
|
||||
value: unknown,
|
||||
options?: StorageOptions
|
||||
): Promise<E.Either<StoreError, void>> {
|
||||
try {
|
||||
const manager = TauriStoreManager.new(storePath);
|
||||
const existingData = await manager.getRaw(namespace, key);
|
||||
const createdAt = existingData?.metadata.createdAt || new Date().toISOString()
|
||||
const manager = TauriStoreManager.new(storePath)
|
||||
const existingData = await manager.getRaw(namespace, key)
|
||||
const createdAt =
|
||||
existingData?.metadata.createdAt || new Date().toISOString()
|
||||
const updatedAt = new Date().toISOString()
|
||||
|
||||
const storedData: StoredData = {
|
||||
|
|
@ -234,101 +264,125 @@ export const implementation: VersionedAPI<StoreV1> = {
|
|||
ttl: options?.ttl,
|
||||
},
|
||||
data: value,
|
||||
};
|
||||
}
|
||||
|
||||
await manager.set(namespace, key, storedData);
|
||||
return E.right(undefined);
|
||||
await manager.set(namespace, key, storedData)
|
||||
return E.right(undefined)
|
||||
} catch (error) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: error instanceof Error ? error.message : "Unknown error",
|
||||
cause: error,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async get<T>(storePath: string, namespace: string, key: string): Promise<E.Either<StoreError, T | undefined>> {
|
||||
async get<T>(
|
||||
storePath: string,
|
||||
namespace: string,
|
||||
key: string
|
||||
): Promise<E.Either<StoreError, T | undefined>> {
|
||||
try {
|
||||
const manager = TauriStoreManager.new(storePath);
|
||||
return E.right(await manager.get<T>(namespace, key));
|
||||
const manager = TauriStoreManager.new(storePath)
|
||||
return E.right(await manager.get<T>(namespace, key))
|
||||
} catch (error) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: error instanceof Error ? error.message : "Unknown error",
|
||||
cause: error,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async has(storePath: string, namespace: string, key: string): Promise<E.Either<StoreError, boolean>> {
|
||||
async has(
|
||||
storePath: string,
|
||||
namespace: string,
|
||||
key: string
|
||||
): Promise<E.Either<StoreError, boolean>> {
|
||||
try {
|
||||
const manager = TauriStoreManager.new(storePath);
|
||||
return E.right(await manager.has(namespace, key));
|
||||
const manager = TauriStoreManager.new(storePath)
|
||||
return E.right(await manager.has(namespace, key))
|
||||
} catch (error) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: error instanceof Error ? error.message : "Unknown error",
|
||||
cause: error,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async remove(storePath: string, namespace: string, key: string): Promise<E.Either<StoreError, boolean>> {
|
||||
async remove(
|
||||
storePath: string,
|
||||
namespace: string,
|
||||
key: string
|
||||
): Promise<E.Either<StoreError, boolean>> {
|
||||
try {
|
||||
const manager = TauriStoreManager.new(storePath);
|
||||
return E.right(await manager.delete(namespace, key));
|
||||
const manager = TauriStoreManager.new(storePath)
|
||||
return E.right(await manager.delete(namespace, key))
|
||||
} catch (error) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: error instanceof Error ? error.message : "Unknown error",
|
||||
cause: error,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async clear(storePath: string, namespace?: string): Promise<E.Either<StoreError, void>> {
|
||||
async clear(
|
||||
storePath: string,
|
||||
namespace?: string
|
||||
): Promise<E.Either<StoreError, void>> {
|
||||
try {
|
||||
const manager = TauriStoreManager.new(storePath);
|
||||
await manager.clear(namespace);
|
||||
return E.right(undefined);
|
||||
const manager = TauriStoreManager.new(storePath)
|
||||
await manager.clear(namespace)
|
||||
return E.right(undefined)
|
||||
} catch (error) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: error instanceof Error ? error.message : "Unknown error",
|
||||
cause: error,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async listNamespaces(storePath: string): Promise<E.Either<StoreError, string[]>> {
|
||||
async listNamespaces(
|
||||
storePath: string
|
||||
): Promise<E.Either<StoreError, string[]>> {
|
||||
try {
|
||||
const manager = TauriStoreManager.new(storePath);
|
||||
return E.right(await manager.listNamespaces());
|
||||
const manager = TauriStoreManager.new(storePath)
|
||||
return E.right(await manager.listNamespaces())
|
||||
} catch (error) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: error instanceof Error ? error.message : "Unknown error",
|
||||
cause: error,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async listKeys(storePath: string, namespace: string): Promise<E.Either<StoreError, string[]>> {
|
||||
async listKeys(
|
||||
storePath: string,
|
||||
namespace: string
|
||||
): Promise<E.Either<StoreError, string[]>> {
|
||||
try {
|
||||
const manager = TauriStoreManager.new(storePath);
|
||||
return E.right(await manager.listKeys(namespace));
|
||||
const manager = TauriStoreManager.new(storePath)
|
||||
return E.right(await manager.listKeys(namespace))
|
||||
} catch (error) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: error instanceof Error ? error.message : "Unknown error",
|
||||
cause: error,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async watch(storePath: string, namespace: string, key: string): Promise<StoreEventEmitter<StoreEvents>> {
|
||||
const manager = TauriStoreManager.new(storePath);
|
||||
return manager.watch(namespace, key);
|
||||
async watch(
|
||||
storePath: string,
|
||||
namespace: string,
|
||||
key: string
|
||||
): Promise<StoreEventEmitter<StoreEvents>> {
|
||||
const manager = TauriStoreManager.new(storePath)
|
||||
return manager.watch(namespace, key)
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { implementation as storeV1 } from './v/1'
|
||||
import { implementation as storeV1 } from "./v/1"
|
||||
|
||||
export const STORE_IMPLS = {
|
||||
v1: storeV1,
|
||||
|
|
|
|||
|
|
@ -1,154 +1,176 @@
|
|||
import * as E from 'fp-ts/Either';
|
||||
import superjson from 'superjson';
|
||||
import * as E from "fp-ts/Either"
|
||||
import superjson from "superjson"
|
||||
|
||||
import type { VersionedAPI } from '@type/versioning';
|
||||
import type { VersionedAPI } from "@type/versioning"
|
||||
import {
|
||||
StoreV1,
|
||||
StoredData,
|
||||
StoredDataSchema,
|
||||
StoreEvents,
|
||||
StoreEventEmitter,
|
||||
} from '@store/v/1';
|
||||
} from "@store/v/1"
|
||||
|
||||
class BrowserStoreManager {
|
||||
private static instance: BrowserStoreManager;
|
||||
private listeners = new Map<string, Set<(payload: StoreEvents['change']) => void>>();
|
||||
private static instance: BrowserStoreManager
|
||||
private listeners = new Map<
|
||||
string,
|
||||
Set<(payload: StoreEvents["change"]) => void>
|
||||
>()
|
||||
|
||||
private constructor() {}
|
||||
|
||||
static new(): BrowserStoreManager {
|
||||
if (!BrowserStoreManager.instance) {
|
||||
BrowserStoreManager.instance = new BrowserStoreManager();
|
||||
BrowserStoreManager.instance = new BrowserStoreManager()
|
||||
}
|
||||
return BrowserStoreManager.instance;
|
||||
return BrowserStoreManager.instance
|
||||
}
|
||||
|
||||
private getFullKey(namespace: string, key: string): string {
|
||||
return `${namespace}:${key}`;
|
||||
return `${namespace}:${key}`
|
||||
}
|
||||
|
||||
private notifyListeners(namespace: string, key: string, value?: unknown) {
|
||||
const fullKey = this.getFullKey(namespace, key);
|
||||
const listeners = this.listeners.get(fullKey) || new Set();
|
||||
listeners.forEach(listener => listener({ namespace, key, value }));
|
||||
const fullKey = this.getFullKey(namespace, key)
|
||||
const listeners = this.listeners.get(fullKey) || new Set()
|
||||
listeners.forEach((listener) => listener({ namespace, key, value }))
|
||||
}
|
||||
|
||||
async set(namespace: string, key: string, value: StoredData): Promise<void> {
|
||||
const validated = StoredDataSchema.parse(value);
|
||||
localStorage.setItem(this.getFullKey(namespace, key), superjson.stringify(validated));
|
||||
this.notifyListeners(namespace, key, validated.data);
|
||||
const validated = StoredDataSchema.parse(value)
|
||||
localStorage.setItem(
|
||||
this.getFullKey(namespace, key),
|
||||
superjson.stringify(validated)
|
||||
)
|
||||
this.notifyListeners(namespace, key, validated.data)
|
||||
}
|
||||
|
||||
async getRaw(namespace: string, key: string): Promise<StoredData | undefined> {
|
||||
const rawValue = localStorage.getItem(this.getFullKey(namespace, key));
|
||||
if (!rawValue) return undefined;
|
||||
async getRaw(
|
||||
namespace: string,
|
||||
key: string
|
||||
): Promise<StoredData | undefined> {
|
||||
const rawValue = localStorage.getItem(this.getFullKey(namespace, key))
|
||||
if (!rawValue) return undefined
|
||||
|
||||
const parsed = superjson.parse(rawValue);
|
||||
const validated = StoredDataSchema.parse(parsed);
|
||||
return validated;
|
||||
const parsed = superjson.parse(rawValue)
|
||||
const validated = StoredDataSchema.parse(parsed)
|
||||
return validated
|
||||
}
|
||||
|
||||
async get<T>(namespace: string, key: string): Promise<T | undefined> {
|
||||
const storedData = await this.getRaw(namespace, key);
|
||||
return storedData?.data as T;
|
||||
const storedData = await this.getRaw(namespace, key)
|
||||
return storedData?.data as T
|
||||
}
|
||||
|
||||
async has(namespace: string, key: string): Promise<boolean> {
|
||||
return localStorage.getItem(this.getFullKey(namespace, key)) !== null;
|
||||
return localStorage.getItem(this.getFullKey(namespace, key)) !== null
|
||||
}
|
||||
|
||||
async delete(namespace: string, key: string): Promise<boolean> {
|
||||
const exists = await this.has(namespace, key);
|
||||
const exists = await this.has(namespace, key)
|
||||
if (exists) {
|
||||
localStorage.removeItem(this.getFullKey(namespace, key));
|
||||
this.notifyListeners(namespace, key, undefined);
|
||||
localStorage.removeItem(this.getFullKey(namespace, key))
|
||||
this.notifyListeners(namespace, key, undefined)
|
||||
}
|
||||
return exists;
|
||||
return exists
|
||||
}
|
||||
|
||||
async clear(namespace?: string): Promise<void> {
|
||||
if (namespace) {
|
||||
const keysToRemove = Object.keys(localStorage).filter(key => key.startsWith(`${namespace}:`));
|
||||
keysToRemove.forEach(key => localStorage.removeItem(key));
|
||||
const keysToRemove = Object.keys(localStorage).filter((key) =>
|
||||
key.startsWith(`${namespace}:`)
|
||||
)
|
||||
keysToRemove.forEach((key) => localStorage.removeItem(key))
|
||||
} else {
|
||||
localStorage.clear();
|
||||
localStorage.clear()
|
||||
}
|
||||
this.listeners.clear();
|
||||
this.listeners.clear()
|
||||
}
|
||||
|
||||
async listNamespaces(): Promise<string[]> {
|
||||
const namespaces = new Set<string>();
|
||||
Object.keys(localStorage).forEach(key => {
|
||||
const [namespace] = key.split(':');
|
||||
namespaces.add(namespace);
|
||||
});
|
||||
return Array.from(namespaces);
|
||||
const namespaces = new Set<string>()
|
||||
Object.keys(localStorage).forEach((key) => {
|
||||
const [namespace] = key.split(":")
|
||||
namespaces.add(namespace)
|
||||
})
|
||||
return Array.from(namespaces)
|
||||
}
|
||||
|
||||
async listKeys(namespace: string): Promise<string[]> {
|
||||
return Object.keys(localStorage)
|
||||
.filter(key => key.startsWith(`${namespace}:`))
|
||||
.map(key => key.replace(`${namespace}:`, ''));
|
||||
.filter((key) => key.startsWith(`${namespace}:`))
|
||||
.map((key) => key.replace(`${namespace}:`, ""))
|
||||
}
|
||||
|
||||
async watch(namespace: string, key: string): Promise<StoreEventEmitter<StoreEvents>> {
|
||||
const fullKey = this.getFullKey(namespace, key);
|
||||
async watch(
|
||||
namespace: string,
|
||||
key: string
|
||||
): Promise<StoreEventEmitter<StoreEvents>> {
|
||||
const fullKey = this.getFullKey(namespace, key)
|
||||
return {
|
||||
on: (event, handler) => {
|
||||
if (event !== 'change') return () => {};
|
||||
if (event !== "change") return () => {}
|
||||
if (!this.listeners.has(fullKey)) {
|
||||
this.listeners.set(fullKey, new Set());
|
||||
this.listeners.set(fullKey, new Set())
|
||||
}
|
||||
this.listeners.get(fullKey)!.add(handler as (payload: StoreEvents['change']) => void);
|
||||
return () => this.listeners.get(fullKey)?.delete(handler as (payload: StoreEvents['change']) => void);
|
||||
this.listeners
|
||||
.get(fullKey)!
|
||||
.add(handler as (payload: StoreEvents["change"]) => void)
|
||||
return () =>
|
||||
this.listeners
|
||||
.get(fullKey)
|
||||
?.delete(handler as (payload: StoreEvents["change"]) => void)
|
||||
},
|
||||
once: (event, handler) => {
|
||||
if (event !== 'change') return () => {};
|
||||
const wrapper = (payload: StoreEvents['change']) => {
|
||||
handler(payload);
|
||||
this.listeners.get(fullKey)?.delete(wrapper);
|
||||
};
|
||||
if (!this.listeners.has(fullKey)) {
|
||||
this.listeners.set(fullKey, new Set());
|
||||
if (event !== "change") return () => {}
|
||||
const wrapper = (payload: StoreEvents["change"]) => {
|
||||
handler(payload)
|
||||
this.listeners.get(fullKey)?.delete(wrapper)
|
||||
}
|
||||
this.listeners.get(fullKey)!.add(wrapper);
|
||||
return () => this.listeners.get(fullKey)?.delete(wrapper);
|
||||
if (!this.listeners.has(fullKey)) {
|
||||
this.listeners.set(fullKey, new Set())
|
||||
}
|
||||
this.listeners.get(fullKey)!.add(wrapper)
|
||||
return () => this.listeners.get(fullKey)?.delete(wrapper)
|
||||
},
|
||||
off: (event, handler) => {
|
||||
if (event === 'change') {
|
||||
this.listeners.get(fullKey)?.delete(handler as (payload: StoreEvents['change']) => void);
|
||||
if (event === "change") {
|
||||
this.listeners
|
||||
.get(fullKey)
|
||||
?.delete(handler as (payload: StoreEvents["change"]) => void)
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const implementation: VersionedAPI<StoreV1> = {
|
||||
version: { major: 1, minor: 0, patch: 0 },
|
||||
api: {
|
||||
id: 'browser-store',
|
||||
capabilities: new Set(['permanent', 'structured', 'watch', 'namespace']),
|
||||
id: "browser-store",
|
||||
capabilities: new Set(["permanent", "structured", "watch", "namespace"]),
|
||||
|
||||
// `init` and other methods in `web` don't `storePath`
|
||||
// but having a consistent API where first param of every method
|
||||
// is the path that filteres to the "realm" makes it easier to reason around
|
||||
async init(_storePath) {
|
||||
try {
|
||||
return E.right(undefined);
|
||||
return E.right(undefined)
|
||||
} catch (e) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: e instanceof Error ? e.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: e instanceof Error ? e.message : "Unknown error",
|
||||
cause: e,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async set(_storePath, namespace, key, value, options) {
|
||||
try {
|
||||
const manager = BrowserStoreManager.new();
|
||||
const existingData = await manager.getRaw(namespace, key);
|
||||
const createdAt = existingData?.metadata.createdAt || new Date().toISOString()
|
||||
const manager = BrowserStoreManager.new()
|
||||
const existingData = await manager.getRaw(namespace, key)
|
||||
const createdAt =
|
||||
existingData?.metadata.createdAt || new Date().toISOString()
|
||||
const updatedAt = new Date().toISOString()
|
||||
|
||||
const storedData: StoredData = {
|
||||
|
|
@ -162,101 +184,101 @@ export const implementation: VersionedAPI<StoreV1> = {
|
|||
ttl: options?.ttl,
|
||||
},
|
||||
data: value,
|
||||
};
|
||||
}
|
||||
|
||||
await manager.set(namespace, key, storedData);
|
||||
return E.right(undefined);
|
||||
await manager.set(namespace, key, storedData)
|
||||
return E.right(undefined)
|
||||
} catch (e) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: e instanceof Error ? e.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: e instanceof Error ? e.message : "Unknown error",
|
||||
cause: e,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async get(_storePath, namespace, key) {
|
||||
try {
|
||||
const manager = BrowserStoreManager.new();
|
||||
return E.right(await manager.get(namespace, key));
|
||||
const manager = BrowserStoreManager.new()
|
||||
return E.right(await manager.get(namespace, key))
|
||||
} catch (e) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: e instanceof Error ? e.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: e instanceof Error ? e.message : "Unknown error",
|
||||
cause: e,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async has(_storePath, namespace, key) {
|
||||
try {
|
||||
const manager = BrowserStoreManager.new();
|
||||
return E.right(await manager.has(namespace, key));
|
||||
const manager = BrowserStoreManager.new()
|
||||
return E.right(await manager.has(namespace, key))
|
||||
} catch (e) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: e instanceof Error ? e.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: e instanceof Error ? e.message : "Unknown error",
|
||||
cause: e,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async remove(_storePath, namespace, key) {
|
||||
try {
|
||||
const manager = BrowserStoreManager.new();
|
||||
return E.right(await manager.delete(namespace, key));
|
||||
const manager = BrowserStoreManager.new()
|
||||
return E.right(await manager.delete(namespace, key))
|
||||
} catch (e) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: e instanceof Error ? e.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: e instanceof Error ? e.message : "Unknown error",
|
||||
cause: e,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async clear(_storePath, namespace) {
|
||||
try {
|
||||
const manager = BrowserStoreManager.new();
|
||||
await manager.clear(namespace);
|
||||
return E.right(undefined);
|
||||
const manager = BrowserStoreManager.new()
|
||||
await manager.clear(namespace)
|
||||
return E.right(undefined)
|
||||
} catch (e) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: e instanceof Error ? e.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: e instanceof Error ? e.message : "Unknown error",
|
||||
cause: e,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async listNamespaces(_storePath) {
|
||||
try {
|
||||
const manager = BrowserStoreManager.new();
|
||||
return E.right(await manager.listNamespaces());
|
||||
const manager = BrowserStoreManager.new()
|
||||
return E.right(await manager.listNamespaces())
|
||||
} catch (e) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: e instanceof Error ? e.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: e instanceof Error ? e.message : "Unknown error",
|
||||
cause: e,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async listKeys(_storePath, namespace) {
|
||||
try {
|
||||
const manager = BrowserStoreManager.new();
|
||||
return E.right(await manager.listKeys(namespace));
|
||||
const manager = BrowserStoreManager.new()
|
||||
return E.right(await manager.listKeys(namespace))
|
||||
} catch (e) {
|
||||
return E.left({
|
||||
kind: 'storage',
|
||||
message: e instanceof Error ? e.message : 'Unknown error',
|
||||
kind: "storage",
|
||||
message: e instanceof Error ? e.message : "Unknown error",
|
||||
cause: e,
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async watch(_storePath, namespace, key) {
|
||||
const manager = BrowserStoreManager.new();
|
||||
return manager.watch(namespace, key);
|
||||
const manager = BrowserStoreManager.new()
|
||||
return manager.watch(namespace, key)
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { v1 } from './v/1'
|
||||
import { v1 } from "./v/1"
|
||||
|
||||
export type {
|
||||
StoreV1,
|
||||
} from './v/1'
|
||||
export type { StoreV1 } from "./v/1"
|
||||
|
||||
export const VERSIONS = {
|
||||
v1,
|
||||
|
|
|
|||
|
|
@ -1,27 +1,27 @@
|
|||
import type { VersionedAPI } from '@type/versioning'
|
||||
import * as E from 'fp-ts/Either'
|
||||
import { z } from 'zod'
|
||||
import type { VersionedAPI } from "@type/versioning"
|
||||
import * as E from "fp-ts/Either"
|
||||
import { z } from "zod"
|
||||
|
||||
export type StoreCapability =
|
||||
| 'permanent'
|
||||
| 'temporary'
|
||||
| 'structured'
|
||||
| 'sync'
|
||||
| 'watch'
|
||||
| 'secure'
|
||||
| 'namespace'
|
||||
| "permanent"
|
||||
| "temporary"
|
||||
| "structured"
|
||||
| "sync"
|
||||
| "watch"
|
||||
| "secure"
|
||||
| "namespace"
|
||||
|
||||
export type StoreError =
|
||||
| { kind: 'not_found'; message: string }
|
||||
| { kind: 'permission'; message: string }
|
||||
| { kind: 'quota'; message: string }
|
||||
| { kind: 'version'; message: string }
|
||||
| { kind: 'parse'; message: string; cause?: unknown }
|
||||
| { kind: 'storage'; message: string; cause?: unknown }
|
||||
| { kind: 'encrypt'; message: string; cause?: unknown }
|
||||
| { kind: "not_found"; message: string }
|
||||
| { kind: "permission"; message: string }
|
||||
| { kind: "quota"; message: string }
|
||||
| { kind: "version"; message: string }
|
||||
| { kind: "parse"; message: string; cause?: unknown }
|
||||
| { kind: "storage"; message: string; cause?: unknown }
|
||||
| { kind: "encrypt"; message: string; cause?: unknown }
|
||||
|
||||
export interface StoreFile {
|
||||
include?: boolean,
|
||||
include?: boolean
|
||||
|
||||
name: string
|
||||
size: number
|
||||
|
|
@ -48,13 +48,15 @@ export interface StoreEvents {
|
|||
export const StoreMetadataSchema = z.object({
|
||||
version: z.number(),
|
||||
lastUpdated: z.string().datetime(),
|
||||
namespaces: z.record(z.object({
|
||||
namespaces: z.record(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
version: z.number(),
|
||||
createdAt: z.string().datetime(),
|
||||
updatedAt: z.string().datetime(),
|
||||
keys: z.array(z.string())
|
||||
}))
|
||||
keys: z.array(z.string()),
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
export type StoreMetadata = z.infer<typeof StoreMetadataSchema>
|
||||
|
|
@ -67,16 +69,19 @@ export const StoredDataSchema = z.object({
|
|||
namespace: z.string(),
|
||||
encrypted: z.boolean().optional(),
|
||||
compressed: z.boolean().optional(),
|
||||
ttl: z.number().optional()
|
||||
ttl: z.number().optional(),
|
||||
}),
|
||||
data: z.unknown()
|
||||
data: z.unknown(),
|
||||
})
|
||||
|
||||
export type StoredData = z.infer<typeof StoredDataSchema>
|
||||
|
||||
export interface StoreEventEmitter<T> {
|
||||
on<K extends keyof T>(event: K, handler: (payload: T[K]) => void): () => void
|
||||
once<K extends keyof T>(event: K, handler: (payload: T[K]) => void): () => void
|
||||
once<K extends keyof T>(
|
||||
event: K,
|
||||
handler: (payload: T[K]) => void
|
||||
): () => void
|
||||
off<K extends keyof T>(event: K, handler: (payload: T[K]) => void): void
|
||||
}
|
||||
|
||||
|
|
@ -85,34 +90,64 @@ export interface StoreV1 {
|
|||
readonly capabilities: Set<StoreCapability>
|
||||
|
||||
init(storePath: string): Promise<E.Either<StoreError, void>>
|
||||
set(storePath: string, namespace: string, key: string, value: unknown, options?: StorageOptions): Promise<E.Either<StoreError, void>>
|
||||
get<T>(storePath: string, namespace: string, key: string): Promise<E.Either<StoreError, T | undefined>>
|
||||
remove(storePath: string, namespace: string, key: string): Promise<E.Either<StoreError, boolean>>
|
||||
clear(storePath: string, namespace?: string): Promise<E.Either<StoreError, void>>
|
||||
has(storePath: string, namespace: string, key: string): Promise<E.Either<StoreError, boolean>>
|
||||
set(
|
||||
storePath: string,
|
||||
namespace: string,
|
||||
key: string,
|
||||
value: unknown,
|
||||
options?: StorageOptions
|
||||
): Promise<E.Either<StoreError, void>>
|
||||
get<T>(
|
||||
storePath: string,
|
||||
namespace: string,
|
||||
key: string
|
||||
): Promise<E.Either<StoreError, T | undefined>>
|
||||
remove(
|
||||
storePath: string,
|
||||
namespace: string,
|
||||
key: string
|
||||
): Promise<E.Either<StoreError, boolean>>
|
||||
clear(
|
||||
storePath: string,
|
||||
namespace?: string
|
||||
): Promise<E.Either<StoreError, void>>
|
||||
has(
|
||||
storePath: string,
|
||||
namespace: string,
|
||||
key: string
|
||||
): Promise<E.Either<StoreError, boolean>>
|
||||
listNamespaces(storePath: string): Promise<E.Either<StoreError, string[]>>
|
||||
listKeys(storePath: string, namespace: string): Promise<E.Either<StoreError, string[]>>
|
||||
watch(storePath: string, namespace: string, key: string): Promise<StoreEventEmitter<StoreEvents>>
|
||||
listKeys(
|
||||
storePath: string,
|
||||
namespace: string
|
||||
): Promise<E.Either<StoreError, string[]>>
|
||||
watch(
|
||||
storePath: string,
|
||||
namespace: string,
|
||||
key: string
|
||||
): Promise<StoreEventEmitter<StoreEvents>>
|
||||
}
|
||||
|
||||
export const v1: VersionedAPI<StoreV1> = {
|
||||
version: { major: 1, minor: 0, patch: 0 },
|
||||
api: {
|
||||
id: 'default',
|
||||
id: "default",
|
||||
capabilities: new Set(),
|
||||
|
||||
init: async () => E.left({ kind: 'version', message: 'Not implemented' }),
|
||||
set: async () => E.left({ kind: 'version', message: 'Not implemented' }),
|
||||
get: async () => E.left({ kind: 'version', message: 'Not implemented' }),
|
||||
remove: async () => E.left({ kind: 'version', message: 'Not implemented' }),
|
||||
clear: async () => E.left({ kind: 'version', message: 'Not implemented' }),
|
||||
has: async () => E.left({ kind: 'version', message: 'Not implemented' }),
|
||||
listNamespaces: async () => E.left({ kind: 'version', message: 'Not implemented' }),
|
||||
listKeys: async () => E.left({ kind: 'version', message: 'Not implemented' }),
|
||||
init: async () => E.left({ kind: "version", message: "Not implemented" }),
|
||||
set: async () => E.left({ kind: "version", message: "Not implemented" }),
|
||||
get: async () => E.left({ kind: "version", message: "Not implemented" }),
|
||||
remove: async () => E.left({ kind: "version", message: "Not implemented" }),
|
||||
clear: async () => E.left({ kind: "version", message: "Not implemented" }),
|
||||
has: async () => E.left({ kind: "version", message: "Not implemented" }),
|
||||
listNamespaces: async () =>
|
||||
E.left({ kind: "version", message: "Not implemented" }),
|
||||
listKeys: async () =>
|
||||
E.left({ kind: "version", message: "Not implemented" }),
|
||||
watch: async () => ({
|
||||
on: () => () => {},
|
||||
once: () => () => {},
|
||||
off: () => {}
|
||||
})
|
||||
}
|
||||
off: () => {},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import type { Version } from '@type/versioning'
|
||||
import type { Version } from "@type/versioning"
|
||||
|
||||
export function checkCapability(required: Version, available: Version): boolean {
|
||||
export function checkCapability(
|
||||
required: Version,
|
||||
available: Version
|
||||
): boolean {
|
||||
if (available.major !== required.major) return false
|
||||
if (available.minor < required.minor) return false
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
/* eslint-env node */
|
||||
require("@rushstack/eslint-patch/modern-module-resolution")
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: "module",
|
||||
requireConfigFile: false,
|
||||
ecmaFeatures: {
|
||||
jsx: false,
|
||||
},
|
||||
},
|
||||
extends: [
|
||||
"@vue/typescript/recommended",
|
||||
"plugin:vue/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
],
|
||||
ignorePatterns: [
|
||||
"static/**/*",
|
||||
"./helpers/backend/graphql.ts",
|
||||
"**/*.d.ts",
|
||||
"types/**/*",
|
||||
],
|
||||
plugins: ["vue", "prettier"],
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
semi: [2, "never"],
|
||||
"import/named": "off", // because, named import issue with typescript see: https://github.com/typescript-eslint/typescript-eslint/issues/154
|
||||
"no-console": "off",
|
||||
"no-debugger": process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
"prettier/prettier":
|
||||
process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
"vue/multi-word-component-names": "off",
|
||||
"vue/no-side-effects-in-computed-properties": "off",
|
||||
"import/no-named-as-default": "off",
|
||||
"import/no-named-as-default-member": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
{
|
||||
"argsIgnorePattern": "^_",
|
||||
"varsIgnorePattern": "^_",
|
||||
"caughtErrorsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"import/default": "off",
|
||||
"no-undef": "off",
|
||||
// localStorage block
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
{
|
||||
name: "localStorage",
|
||||
message:
|
||||
"Do not use 'localStorage' directly. Please use localpersistence.ts functions or stores",
|
||||
},
|
||||
],
|
||||
// window.localStorage block
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector: "CallExpression[callee.object.property.name='localStorage']",
|
||||
message:
|
||||
"Do not use 'localStorage' directly. Please use localpersistence.ts functions or stores",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
85
packages/hoppscotch-selfhost-web/eslint.config.mjs
Normal file
85
packages/hoppscotch-selfhost-web/eslint.config.mjs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import pluginVue from "eslint-plugin-vue"
|
||||
import {
|
||||
defineConfigWithVueTs,
|
||||
vueTsConfigs,
|
||||
} from "@vue/eslint-config-typescript"
|
||||
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"
|
||||
import globals from "globals"
|
||||
|
||||
export default defineConfigWithVueTs(
|
||||
{
|
||||
ignores: [
|
||||
"static/**",
|
||||
"src/api/generated/**",
|
||||
"**/*.d.ts",
|
||||
"types/**",
|
||||
"dist/**",
|
||||
"node_modules/**",
|
||||
],
|
||||
},
|
||||
pluginVue.configs["flat/recommended"],
|
||||
vueTsConfigs.recommended,
|
||||
eslintPluginPrettierRecommended,
|
||||
{
|
||||
files: ["**/*.ts", "**/*.js", "**/*.vue"],
|
||||
linterOptions: {
|
||||
reportUnusedDisableDirectives: false,
|
||||
},
|
||||
languageOptions: {
|
||||
sourceType: "module",
|
||||
ecmaVersion: "latest",
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
parserOptions: {
|
||||
requireConfigFile: false,
|
||||
ecmaFeatures: {
|
||||
jsx: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
semi: [2, "never"],
|
||||
"import/named": "off",
|
||||
"no-console": "off",
|
||||
"no-debugger": process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
"prettier/prettier":
|
||||
process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
"vue/multi-word-component-names": "off",
|
||||
"vue/no-side-effects-in-computed-properties": "off",
|
||||
"import/no-named-as-default": "off",
|
||||
"import/no-named-as-default-member": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
process.env.HOPP_LINT_FOR_PROD === "true" ? "error" : "warn",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/no-unused-expressions": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-unsafe-function-type": "off",
|
||||
"import/default": "off",
|
||||
"no-undef": "off",
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
{
|
||||
name: "localStorage",
|
||||
message:
|
||||
"Do not use 'localStorage' directly. Please use the PersistenceService",
|
||||
},
|
||||
],
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector: "CallExpression[callee.object.property.name='localStorage']",
|
||||
message:
|
||||
"Do not use 'localStorage' directly. Please use the PersistenceService",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
|
@ -9,9 +9,9 @@
|
|||
"dev": "pnpm exec npm-run-all -p -l dev:*",
|
||||
"build": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint src --ext .ts,.js,.vue --ignore-path .gitignore .",
|
||||
"lint": "eslint src",
|
||||
"lint:ts": "vue-tsc --noEmit",
|
||||
"lintfix": "eslint --fix src --ext .ts,.js,.vue --ignore-path .gitignore .",
|
||||
"lintfix": "eslint --fix src",
|
||||
"prod-lint": "cross-env HOPP_LINT_FOR_PROD=true pnpm run lint",
|
||||
"generate": "pnpm run build",
|
||||
"do-dev": "pnpm run dev",
|
||||
|
|
@ -51,6 +51,8 @@
|
|||
"zod": "3.25.32"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "3.3.3",
|
||||
"@eslint/js": "9.39.2",
|
||||
"@graphql-codegen/add": "6.0.0",
|
||||
"@graphql-codegen/cli": "6.1.0",
|
||||
"@graphql-codegen/typed-document-node": "6.1.5",
|
||||
|
|
@ -66,13 +68,14 @@
|
|||
"@typescript-eslint/parser": "8.50.0",
|
||||
"@vitejs/plugin-legacy": "7.2.1",
|
||||
"@vitejs/plugin-vue": "6.0.3",
|
||||
"@vue/eslint-config-typescript": "13.0.0",
|
||||
"@vue/eslint-config-typescript": "14.6.0",
|
||||
"autoprefixer": "10.4.23",
|
||||
"cross-env": "10.1.0",
|
||||
"dotenv": "17.2.3",
|
||||
"eslint": "8.57.0",
|
||||
"eslint": "9.39.2",
|
||||
"eslint-plugin-prettier": "5.5.4",
|
||||
"eslint-plugin-vue": "10.6.2",
|
||||
"globals": "16.5.0",
|
||||
"npm-run-all": "4.1.5",
|
||||
"postcss": "8.5.6",
|
||||
"prettier-plugin-tailwindcss": "0.7.1",
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ async function getInitialUserDetails(): Promise<
|
|||
}
|
||||
}
|
||||
return { error: "auth/cookies_not_found" }
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
return { error: "auth/cookies_not_found" }
|
||||
}
|
||||
}
|
||||
|
|
@ -254,7 +254,7 @@ async function refreshToken() {
|
|||
}
|
||||
|
||||
return isSuccessful
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ async function refreshToken() {
|
|||
}
|
||||
|
||||
return isSuccessful
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -394,7 +394,7 @@ export const def: AuthPlatformDef = {
|
|||
|
||||
// axios automatically throws on error status codes, so if we reach here, it was successful
|
||||
return !!response.data.isValid
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
|
|
|||
2141
pnpm-lock.yaml
2141
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue