diff --git a/packages/hoppscotch-common/locales/en.json b/packages/hoppscotch-common/locales/en.json index 35450a5fb..fd5eb94dc 100644 --- a/packages/hoppscotch-common/locales/en.json +++ b/packages/hoppscotch-common/locales/en.json @@ -1,5 +1,6 @@ { "action": { + "add": "Add", "autoscroll": "Autoscroll", "cancel": "Cancel", "choose_file": "Choose a file", @@ -54,9 +55,28 @@ "new": "Add new", "star": "Add star" }, + "cookies": { + "modal": { + "new_domain_name": "New domain name", + "set": "Set a cookie", + "cookie_string": "Cookie string", + "enter_cookie_string": "Enter cookie string", + "cookie_name": "Name", + "cookie_value": "Value", + "cookie_path": "Path", + "cookie_expires": "Expires", + "managed_tab": "Managed", + "raw_tab": "Raw", + "interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.", + "empty_domains": "Domain list is empty", + "empty_domain": "Domain is empty", + "no_cookies_in_domain": "No cookies set for this domain" + } + }, "app": { "chat_with_us": "Chat with us", "contact_us": "Contact us", + "cookies": "Cookies", "copy": "Copy", "copy_user_id": "Copy User Auth Token", "developer_option": "Developer options", @@ -764,7 +784,7 @@ "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", "published_message": "Published message: {message} to topic: {topic}", "reconnection_error": "Failed to reconnect", - "show":"Show", + "show": "Show", "subscribed_failed": "Failed to subscribe to topic: {topic}", "subscribed_success": "Successfully subscribed to topic: {topic}", "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", diff --git a/packages/hoppscotch-common/package.json b/packages/hoppscotch-common/package.json index 30fe6ed4c..8f6cd0d24 100644 --- a/packages/hoppscotch-common/package.json +++ b/packages/hoppscotch-common/package.json @@ -52,6 +52,7 @@ "acorn-walk": "^8.2.0", "axios": "^1.4.0", "buffer": "^6.0.3", + "cookie-es": "^1.0.0", "dioc": "workspace:^", "esprima": "^4.0.1", "events": "^3.3.0", @@ -76,6 +77,8 @@ "process": "^0.11.10", "qs": "^6.11.2", "rxjs": "^7.8.1", + "set-cookie-parser": "^2.6.0", + "set-cookie-parser-es": "^1.0.5", "socket.io-client-v2": "npm:socket.io-client@^2.4.0", "socket.io-client-v3": "npm:socket.io-client@^3.1.3", "socket.io-client-v4": "npm:socket.io-client@^4.4.1", @@ -98,7 +101,8 @@ "wonka": "^6.3.4", "workbox-window": "^7.0.0", "xml-formatter": "^3.5.0", - "yargs-parser": "^21.1.1" + "yargs-parser": "^21.1.1", + "zod": "^3.22.2" }, "devDependencies": { "@esbuild-plugins/node-globals-polyfill": "^0.2.3", diff --git a/packages/hoppscotch-common/src/components.d.ts b/packages/hoppscotch-common/src/components.d.ts index f7d8352a2..786d61c73 100644 --- a/packages/hoppscotch-common/src/components.d.ts +++ b/packages/hoppscotch-common/src/components.d.ts @@ -1,11 +1,11 @@ -/* eslint-disable */ -/* prettier-ignore */ -// @ts-nocheck -// Generated by unplugin-vue-components +// generated by unplugin-vue-components +// We suggest you to commit this file into source control // Read more: https://github.com/vuejs/core/pull/3399 +import '@vue/runtime-core' + export {} -declare module 'vue' { +declare module '@vue/runtime-core' { export interface GlobalComponents { AppActionHandler: typeof import('./components/app/ActionHandler.vue')['default'] AppAnnouncement: typeof import('./components/app/Announcement.vue')['default'] @@ -58,6 +58,8 @@ declare module 'vue' { CollectionsRequest: typeof import('./components/collections/Request.vue')['default'] CollectionsSaveRequest: typeof import('./components/collections/SaveRequest.vue')['default'] CollectionsTeamCollections: typeof import('./components/collections/TeamCollections.vue')['default'] + CookiesAllModal: typeof import('./components/cookies/AllModal.vue')['default'] + CookiesEditCookie: typeof import('./components/cookies/EditCookie.vue')['default'] Environments: typeof import('./components/environments/index.vue')['default'] EnvironmentsAdd: typeof import('./components/environments/Add.vue')['default'] EnvironmentsImportExport: typeof import('./components/environments/ImportExport.vue')['default'] @@ -90,13 +92,11 @@ declare module 'vue' { HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary'] HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary'] HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor'] - HoppSmartAutoComplete: typeof import('@hoppscotch/ui')['HoppSmartAutoComplete'] HoppSmartCheckbox: typeof import('@hoppscotch/ui')['HoppSmartCheckbox'] HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal'] HoppSmartExpand: typeof import('@hoppscotch/ui')['HoppSmartExpand'] HoppSmartFileChip: typeof import('@hoppscotch/ui')['HoppSmartFileChip'] HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput'] - HoppSmartIntersection: typeof import('@hoppscotch/ui')['HoppSmartIntersection'] HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem'] HoppSmartLink: typeof import('@hoppscotch/ui')['HoppSmartLink'] HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal'] @@ -143,7 +143,6 @@ declare module 'vue' { IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default'] IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default'] IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default'] - IconLucideBrush: typeof import('~icons/lucide/brush')['default'] IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default'] IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default'] IconLucideGlobe: typeof import('~icons/lucide/globe')['default'] @@ -153,10 +152,8 @@ declare module 'vue' { IconLucideLayers: typeof import('~icons/lucide/layers')['default'] IconLucideListEnd: typeof import('~icons/lucide/list-end')['default'] IconLucideMinus: typeof import('~icons/lucide/minus')['default'] - IconLucideRss: typeof import('~icons/lucide/rss')['default'] IconLucideSearch: typeof import('~icons/lucide/search')['default'] IconLucideUsers: typeof import('~icons/lucide/users')['default'] - IconLucideVerified: typeof import('~icons/lucide/verified')['default'] InterceptorsExtensionSubtitle: typeof import('./components/interceptors/ExtensionSubtitle.vue')['default'] LensesHeadersRenderer: typeof import('./components/lenses/HeadersRenderer.vue')['default'] LensesHeadersRendererEntry: typeof import('./components/lenses/HeadersRendererEntry.vue')['default'] @@ -189,7 +186,6 @@ declare module 'vue' { SmartEnvInput: typeof import('./components/smart/EnvInput.vue')['default'] SmartExpand: typeof import('./../../hoppscotch-ui/src/components/smart/Expand.vue')['default'] SmartFileChip: typeof import('./../../hoppscotch-ui/src/components/smart/FileChip.vue')['default'] - SmartFontSizePicker: typeof import('./components/smart/FontSizePicker.vue')['default'] SmartInput: typeof import('./../../hoppscotch-ui/src/components/smart/Input.vue')['default'] SmartIntersection: typeof import('./../../hoppscotch-ui/src/components/smart/Intersection.vue')['default'] SmartItem: typeof import('./../../hoppscotch-ui/src/components/smart/Item.vue')['default'] @@ -222,4 +218,5 @@ declare module 'vue' { WorkspaceCurrent: typeof import('./components/workspace/Current.vue')['default'] WorkspaceSelector: typeof import('./components/workspace/Selector.vue')['default'] } + } diff --git a/packages/hoppscotch-common/src/components/app/Footer.vue b/packages/hoppscotch-common/src/components/app/Footer.vue index fd44e7708..a2ca305ad 100644 --- a/packages/hoppscotch-common/src/components/app/Footer.vue +++ b/packages/hoppscotch-common/src/components/app/Footer.vue @@ -20,6 +20,12 @@ +
+
diff --git a/packages/hoppscotch-common/src/components/collections/index.vue b/packages/hoppscotch-common/src/components/collections/index.vue index c903a068f..a19370048 100644 --- a/packages/hoppscotch-common/src/components/collections/index.vue +++ b/packages/hoppscotch-common/src/components/collections/index.vue @@ -1866,28 +1866,25 @@ const getJSONCollection = async () => { * @param collectionJSON - JSON string of the collection * @param name - Name of the collection set as the file name */ -const initializeDownloadCollection = ( +const initializeDownloadCollection = async ( collectionJSON: string, name: string | null ) => { - const file = new Blob([collectionJSON], { type: "application/json" }) - const a = document.createElement("a") - const url = URL.createObjectURL(file) - a.href = url + const result = await platform.io.saveFileWithDialog({ + data: collectionJSON, + contentType: "application/json", + suggestedFilename: `${name ?? "collection"}.json`, + filters: [ + { + name: "Hoppscotch Collection JSON file", + extensions: ["json"], + }, + ], + }) - if (name) { - a.download = `${name}.json` - } else { - a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json` + if (result.type === "unknown" || result.type === "saved") { + toast.success(t("state.download_started").toString()) } - - document.body.appendChild(a) - a.click() - toast.success(t("state.download_started").toString()) - setTimeout(() => { - document.body.removeChild(a) - URL.revokeObjectURL(url) - }, 1000) } /** @@ -1916,11 +1913,14 @@ const exportData = async ( exportLoading.value = false return }, - (coll) => { + async (coll) => { const hoppColl = teamCollToHoppRESTColl(coll) const collectionJSONString = JSON.stringify(hoppColl) - initializeDownloadCollection(collectionJSONString, hoppColl.name) + await initializeDownloadCollection( + collectionJSONString, + hoppColl.name + ) exportLoading.value = false } ) diff --git a/packages/hoppscotch-common/src/components/cookies/AllModal.vue b/packages/hoppscotch-common/src/components/cookies/AllModal.vue new file mode 100644 index 000000000..80aed5b43 --- /dev/null +++ b/packages/hoppscotch-common/src/components/cookies/AllModal.vue @@ -0,0 +1,269 @@ + + + diff --git a/packages/hoppscotch-common/src/components/cookies/EditCookie.vue b/packages/hoppscotch-common/src/components/cookies/EditCookie.vue new file mode 100644 index 000000000..d72a56a67 --- /dev/null +++ b/packages/hoppscotch-common/src/components/cookies/EditCookie.vue @@ -0,0 +1,195 @@ + + + + + diff --git a/packages/hoppscotch-common/src/components/environments/ImportExport.vue b/packages/hoppscotch-common/src/components/environments/ImportExport.vue index a84461d3e..4fa07b55e 100644 --- a/packages/hoppscotch-common/src/components/environments/ImportExport.vue +++ b/packages/hoppscotch-common/src/components/environments/ImportExport.vue @@ -375,7 +375,7 @@ const importFromPostman = ({ importFromHoppscotch(environments) } -const exportJSON = () => { +const exportJSON = async () => { const dataToWrite = environmentJson.value const parsedCollections = JSON.parse(dataToWrite) @@ -385,19 +385,27 @@ const exportJSON = () => { } const file = new Blob([dataToWrite], { type: "application/json" }) - const a = document.createElement("a") const url = URL.createObjectURL(file) - a.href = url - // TODO: get uri from meta - a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json` - document.body.appendChild(a) - a.click() - toast.success(t("state.download_started").toString()) - setTimeout(() => { - document.body.removeChild(a) - URL.revokeObjectURL(url) - }, 1000) + const filename = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json` + + URL.revokeObjectURL(url) + + const result = await platform.io.saveFileWithDialog({ + data: dataToWrite, + contentType: "application/json", + suggestedFilename: filename, + filters: [ + { + name: "JSON file", + extensions: ["json"], + }, + ], + }) + + if (result.type === "unknown" || result.type === "saved") { + toast.success(t("state.download_started").toString()) + } } const getErrorMessage = (err: GQLError) => { diff --git a/packages/hoppscotch-common/src/components/graphql/Response.vue b/packages/hoppscotch-common/src/components/graphql/Response.vue index 76f7ae65f..54010711a 100644 --- a/packages/hoppscotch-common/src/components/graphql/Response.vue +++ b/packages/hoppscotch-common/src/components/graphql/Response.vue @@ -59,6 +59,7 @@ import { useToast } from "@composables/toast" import { defineActionHandler } from "~/helpers/actions" import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils" import { GQLResponseEvent } from "~/helpers/graphql/connection" +import { platform } from "~/platform" const t = useI18n() const toast = useToast() @@ -111,21 +112,31 @@ const copyResponse = (str: string) => { toast.success(`${t("state.copied_to_clipboard")}`) } -const downloadResponse = (str: string) => { +const downloadResponse = async (str: string) => { const dataToWrite = str const file = new Blob([dataToWrite!], { type: "application/json" }) - const a = document.createElement("a") const url = URL.createObjectURL(file) - a.href = url - a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}` - document.body.appendChild(a) - a.click() - downloadResponseIcon.value = IconCheck - toast.success(`${t("state.download_started")}`) - setTimeout(() => { - document.body.removeChild(a) - URL.revokeObjectURL(url) - }, 1000) + + const filename = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json` + + URL.revokeObjectURL(url) + + const result = await platform.io.saveFileWithDialog({ + data: dataToWrite, + contentType: "application/json", + suggestedFilename: filename, + filters: [ + { + name: "JSON file", + extensions: ["json"], + }, + ], + }) + + if (result.type === "unknown" || result.type === "saved") { + downloadResponseIcon.value = IconCheck + toast.success(`${t("state.download_started")}`) + } } defineActionHandler( diff --git a/packages/hoppscotch-common/src/components/graphql/Sidebar.vue b/packages/hoppscotch-common/src/components/graphql/Sidebar.vue index b297814ab..fbf51e047 100644 --- a/packages/hoppscotch-common/src/components/graphql/Sidebar.vue +++ b/packages/hoppscotch-common/src/components/graphql/Sidebar.vue @@ -202,6 +202,7 @@ import { schemaString, subscriptionFields, } from "~/helpers/graphql/connection" +import { platform } from "~/platform" type NavigationTabs = "history" | "collection" | "docs" | "schema" type GqlTabs = "queries" | "mutations" | "subscriptions" | "types" @@ -372,21 +373,33 @@ useCodemirror( }) ) -const downloadSchema = () => { - const dataToWrite = JSON.stringify(schemaString.value, null, 2) +const downloadSchema = async () => { + const dataToWrite = schemaString.value const file = new Blob([dataToWrite], { type: "application/graphql" }) - const a = document.createElement("a") const url = URL.createObjectURL(file) - a.href = url - a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.graphql` - document.body.appendChild(a) - a.click() - downloadSchemaIcon.value = IconCheck - toast.success(`${t("state.download_started")}`) - setTimeout(() => { - document.body.removeChild(a) - URL.revokeObjectURL(url) - }, 1000) + + const filename = `${ + url.split("/").pop()!.split("#")[0].split("?")[0] + }.graphql` + + URL.revokeObjectURL(url) + + const result = await platform.io.saveFileWithDialog({ + data: dataToWrite, + contentType: "application/graphql", + suggestedFilename: filename, + filters: [ + { + name: "GraphQL Schema File", + extensions: ["graphql"], + }, + ], + }) + + if (result.type === "unknown" || result.type === "saved") { + downloadSchemaIcon.value = IconCheck + toast.success(`${t("state.download_started")}`) + } } const copySchema = () => { diff --git a/packages/hoppscotch-common/src/composables/lens-actions.ts b/packages/hoppscotch-common/src/composables/lens-actions.ts index c2f22a614..160365c9a 100644 --- a/packages/hoppscotch-common/src/composables/lens-actions.ts +++ b/packages/hoppscotch-common/src/composables/lens-actions.ts @@ -10,6 +10,7 @@ import { useI18n } from "./i18n" import { refAutoReset } from "@vueuse/core" import { copyToClipboard } from "@helpers/utils/clipboard" import { HoppRESTResponse } from "@helpers/types/HoppRESTResponse" +import { platform } from "~/platform" export function useCopyResponse(responseBodyText: Ref) { const toast = useToast() @@ -40,15 +41,14 @@ export function useDownloadResponse( const toast = useToast() const t = useI18n() - const downloadResponse = () => { + const downloadResponse = async () => { const dataToWrite = responseBody.value - const file = new Blob([dataToWrite], { type: contentType }) - const a = document.createElement("a") - const url = URL.createObjectURL(file) - a.href = url - // TODO: get uri from meta - a.download = pipe( + // Guess extension and filename + const file = new Blob([dataToWrite], { type: contentType }) + const url = URL.createObjectURL(file) + + const filename = pipe( url, S.split("/"), RNEA.last, @@ -58,15 +58,24 @@ export function useDownloadResponse( RNEA.head ) - document.body.appendChild(a) - a.click() - downloadIcon.value = IconCheck - toast.success(`${t("state.download_started")}`) - setTimeout(() => { - document.body.removeChild(a) - URL.revokeObjectURL(url) - }, 1000) + URL.revokeObjectURL(url) + + console.log(filename) + + // TODO: Look at the mime type and determine extension ? + const result = await platform.io.saveFileWithDialog({ + data: dataToWrite, + contentType: contentType, + suggestedFilename: filename, + }) + + // Assume success if unknown as we cannot determine + if (result.type === "unknown" || result.type === "saved") { + downloadIcon.value = IconCheck + toast.success(`${t("state.download_started")}`) + } } + return { downloadIcon, downloadResponse, diff --git a/packages/hoppscotch-common/src/helpers/import-export/export/environment.ts b/packages/hoppscotch-common/src/helpers/import-export/export/environment.ts index 5bc6a42e0..82c812e57 100644 --- a/packages/hoppscotch-common/src/helpers/import-export/export/environment.ts +++ b/packages/hoppscotch-common/src/helpers/import-export/export/environment.ts @@ -1,6 +1,7 @@ import { Environment } from "@hoppscotch/data" import { TeamEnvironment } from "~/helpers/teams/TeamEnvironment" import { cloneDeep } from "lodash-es" +import { platform } from "~/platform" const getEnvironmentJson = ( environmentObj: TeamEnvironment | Environment, @@ -32,17 +33,24 @@ export const exportAsJSON = ( if (!dataToWrite) return false const file = new Blob([dataToWrite], { type: "application/json" }) - const a = document.createElement("a") const url = URL.createObjectURL(file) - a.href = url - // Extracts the path from url, removes fragment identifier and query parameters if any, appends the ".json" extension, and assigns it - a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json` - document.body.appendChild(a) - a.click() - setTimeout(() => { - document.body.removeChild(a) - window.URL.revokeObjectURL(url) - }, 0) + URL.revokeObjectURL(url) + + platform.io.saveFileWithDialog({ + data: dataToWrite, + contentType: "application/json", + // Extracts the path from url, removes fragment identifier and query parameters if any, appends the ".json" extension, and assigns it + suggestedFilename: `${ + url.split("/").pop()!.split("#")[0].split("?")[0] + }.json`, + filters: [ + { + name: "JSON file", + extensions: ["json"], + }, + ], + }) + return true } diff --git a/packages/hoppscotch-common/src/newstore/localpersistence.ts b/packages/hoppscotch-common/src/newstore/localpersistence.ts index e3308df8d..1911f0053 100644 --- a/packages/hoppscotch-common/src/newstore/localpersistence.ts +++ b/packages/hoppscotch-common/src/newstore/localpersistence.ts @@ -47,6 +47,9 @@ import { StorageLike, watchDebounced } from "@vueuse/core" import { getService } from "~/modules/dioc" import { RESTTabService } from "~/services/tab/rest" import { GQLTabService } from "~/services/tab/graphql" +import { z } from "zod" +import { CookieJarService } from "~/services/cookie-jar.service" +import { watch } from "vue" function checkAndMigrateOldSettings() { if (window.localStorage.getItem("selectedEnvIndex")) { @@ -182,6 +185,35 @@ function setupHistoryPersistence() { }) } +const cookieSchema = z.record(z.array(z.string())) + +function setupCookiesPersistence() { + const cookieJarService = getService(CookieJarService) + + try { + const cookieData = JSON.parse( + window.localStorage.getItem("cookieJar") || "{}" + ) + + const parseResult = cookieSchema.safeParse(cookieData) + + if (parseResult.success) { + for (const domain in parseResult.data) { + cookieJarService.bulkApplyCookiesToDomain( + parseResult.data[domain], + domain + ) + } + } + } catch (e) {} + + watch(cookieJarService.cookieJar, (cookieJar) => { + const data = JSON.stringify(Object.fromEntries(cookieJar.entries())) + + window.localStorage.setItem("cookieJar", data) + }) +} + function setupCollectionsPersistence() { const restCollectionData = JSON.parse( window.localStorage.getItem("collections") || "[]" @@ -382,6 +414,8 @@ export function setupLocalPersistence() { setupSocketIOPersistence() setupSSEPersistence() setupMQTTPersistence() + + setupCookiesPersistence() } /** diff --git a/packages/hoppscotch-common/src/platform/index.ts b/packages/hoppscotch-common/src/platform/index.ts index 22cbaf112..e527d6174 100644 --- a/packages/hoppscotch-common/src/platform/index.ts +++ b/packages/hoppscotch-common/src/platform/index.ts @@ -9,12 +9,14 @@ import { AnalyticsPlatformDef } from "./analytics" import { InterceptorsPlatformDef } from "./interceptors" import { HoppModule } from "~/modules" import { InspectorsPlatformDef } from "./inspectors" +import { IOPlatformDef } from "./io" export type PlatformDef = { ui?: UIPlatformDef addedHoppModules?: HoppModule[] auth: AuthPlatformDef analytics?: AnalyticsPlatformDef + io: IOPlatformDef sync: { environments: EnvironmentsPlatformDef collections: CollectionsPlatformDef @@ -27,6 +29,12 @@ export type PlatformDef = { platformFeatureFlags: { exportAsGIST: boolean hasTelemetry: boolean + + /** + * Whether the platform supports cookies (affects whether the cookies footer item is shown) + * If a value is not given, then the value is assumed to be false + */ + cookiesEnabled?: boolean } } diff --git a/packages/hoppscotch-common/src/platform/io.ts b/packages/hoppscotch-common/src/platform/io.ts new file mode 100644 index 000000000..97d32a7a5 --- /dev/null +++ b/packages/hoppscotch-common/src/platform/io.ts @@ -0,0 +1,84 @@ +/** + * Defines how to save a file to the user's filesystem. + */ +export type SaveFileWithDialogOptions = { + /** + * The data to be saved + */ + data: string | ArrayBuffer + + /** + * The suggested filename for the file. This name will be shown in the + * save dialog by default when a save is initiated. + */ + suggestedFilename: string + + /** + * The content type mime type of the data to be saved. + * + * NOTE: The usage of this data might be platform dependent. + * For example, this field is used in the web, but not in the desktop app. + */ + contentType: string + + /** + * Defines the filters (like in Windows, on the right side, where you can + * select the file type) for the file dialog. + * + * NOTE: The usage of this data might be platform dependent. + * For example, this field is used in the web, but not in the desktop app. + */ + filters?: Array<{ + /** + * The name of the filter (in Windows, if the filter looks + * like "Images (*.png, *.jpg)", the name would be "Images") + */ + name: string + + /** + * The array of extensions that are supported, without the dot. + */ + extensions: string[] + }> +} + +export type SaveFileResponse = + | { + /** + * The implementation was unable to determine the status of the save operation. + * This cannot be considered a success or a failure and should be handled as an uncertainity. + * The browser standard implementation (std) returns this value as there is no way to + * check if the user downloaded the file or not. + */ + type: "unknown" + } + | { + /** + * The result is known and the user cancelled the save. + */ + type: "cancelled" + } + | { + /** + * The result is known and the user saved the file. + */ + type: "saved" + + /** + * The full path of where the file was saved + */ + path: string + } + +/** + * Platform definitions for how to handle IO operations. + */ +export type IOPlatformDef = { + /** + * Defines how to save a file to the user's filesystem. + * The expected behaviour is for the browser to show a prompt to save the file. + */ + saveFileWithDialog: ( + opts: SaveFileWithDialogOptions + ) => Promise +} diff --git a/packages/hoppscotch-common/src/platform/std/io.ts b/packages/hoppscotch-common/src/platform/std/io.ts new file mode 100644 index 000000000..568a7ecbf --- /dev/null +++ b/packages/hoppscotch-common/src/platform/std/io.ts @@ -0,0 +1,37 @@ +import { IOPlatformDef } from "../io" +import { pipe } from "fp-ts/function" +import * as S from "fp-ts/string" +import * as RNEA from "fp-ts/ReadonlyNonEmptyArray" + +/** + * Implementation for how to handle IO operations in the browser. + */ +export const browserIODef: IOPlatformDef = { + saveFileWithDialog(opts) { + const file = new Blob([opts.data], { type: opts.contentType }) + const a = document.createElement("a") + const url = URL.createObjectURL(file) + + a.href = url + a.download = pipe( + url, + S.split("/"), + RNEA.last, + S.split("#"), + RNEA.head, + S.split("?"), + RNEA.head + ) + + document.body.appendChild(a) + a.click() + + setTimeout(() => { + document.body.removeChild(a) + URL.revokeObjectURL(url) + }, 1000) + + // Browsers provide no way for us to know the save went successfully. + return Promise.resolve({ type: "unknown" }) + }, +} diff --git a/packages/hoppscotch-common/src/services/__tests__/interceptor.service.spec.ts b/packages/hoppscotch-common/src/services/__tests__/interceptor.service.spec.ts index 35c21b33a..0c060fcce 100644 --- a/packages/hoppscotch-common/src/services/__tests__/interceptor.service.spec.ts +++ b/packages/hoppscotch-common/src/services/__tests__/interceptor.service.spec.ts @@ -72,6 +72,61 @@ describe("InterceptorService", () => { expect(service.currentInterceptorID.value).not.toEqual("unknown") }) + it("currentInterceptor points to the instance of the currently selected interceptor", () => { + const container = new TestContainer() + + const service = container.bind(InterceptorService) + + const interceptor = { + interceptorID: "test", + name: () => "test interceptor", + selectable: { type: "selectable" as const }, + runRequest: () => { + throw new Error("not implemented") + }, + } + + service.registerInterceptor(interceptor) + service.currentInterceptorID.value = "test" + + expect(service.currentInterceptor.value).toBe(interceptor) + }) + + it("currentInterceptor updates when the currentInterceptorID changes", () => { + const container = new TestContainer() + + const service = container.bind(InterceptorService) + + const interceptor = { + interceptorID: "test", + name: () => "test interceptor", + selectable: { type: "selectable" as const }, + runRequest: () => { + throw new Error("not implemented") + }, + } + + const interceptor_2 = { + interceptorID: "test2", + name: () => "test interceptor", + selectable: { type: "selectable" as const }, + runRequest: () => { + throw new Error("not implemented") + }, + } + + service.registerInterceptor(interceptor) + service.registerInterceptor(interceptor_2) + + service.currentInterceptorID.value = "test" + + expect(service.currentInterceptor.value).toBe(interceptor) + + service.currentInterceptorID.value = "test2" + expect(service.currentInterceptor.value).not.toBe(interceptor) + expect(service.currentInterceptor.value).toBe(interceptor_2) + }) + describe("registerInterceptor", () => { it("should register the interceptor", () => { const container = new TestContainer() diff --git a/packages/hoppscotch-common/src/services/cookie-jar.service.ts b/packages/hoppscotch-common/src/services/cookie-jar.service.ts new file mode 100644 index 000000000..b41340a7a --- /dev/null +++ b/packages/hoppscotch-common/src/services/cookie-jar.service.ts @@ -0,0 +1,69 @@ +import { Service } from "dioc" +import { ref } from "vue" +import { parseString as setCookieParse } from "set-cookie-parser-es" + +export type CookieDef = { + name: string + value: string + domain: string + path: string + expires: string +} + +export class CookieJarService extends Service { + public static readonly ID = "COOKIE_JAR_SERVICE" + + /** + * The cookie jar that stores all relevant cookie info. + * The keys correspond to the domain of the cookie. + * The cookie strings are stored as an array of strings corresponding to the domain + */ + public cookieJar = ref(new Map()) + + constructor() { + super() + } + + public parseSetCookieString(setCookieString: string) { + return setCookieParse(setCookieString) + } + + public bulkApplyCookiesToDomain(cookies: string[], domain: string) { + const existingDomainEntries = this.cookieJar.value.get(domain) ?? [] + existingDomainEntries.push(...cookies) + + this.cookieJar.value.set(domain, existingDomainEntries) + } + + public getCookiesForURL(url: URL) { + const relevantDomains = Array.from(this.cookieJar.value.keys()).filter( + (domain) => url.hostname.endsWith(domain) + ) + + return relevantDomains + .flatMap((domain) => { + // Assemble the list of cookie entries from all the relevant domains + + const cookieStrings = this.cookieJar.value.get(domain)! // We know not nullable from how we filter above + + return cookieStrings.map((cookieString) => + this.parseSetCookieString(cookieString) + ) + }) + .filter((cookie) => { + // Perform the required checks on the cookies + + const passesPathCheck = url.pathname.startsWith(cookie.path ?? "/") + + const passesExpiresCheck = !cookie.expires + ? true + : cookie.expires.getTime() >= new Date().getTime() + + const passesSecureCheck = !cookie.secure + ? true + : url.protocol === "https:" + + return passesPathCheck && passesExpiresCheck && passesSecureCheck + }) + } +} diff --git a/packages/hoppscotch-common/src/services/interceptor.service.ts b/packages/hoppscotch-common/src/services/interceptor.service.ts index 9a3a2662e..4ed5b113e 100644 --- a/packages/hoppscotch-common/src/services/interceptor.service.ts +++ b/packages/hoppscotch-common/src/services/interceptor.service.ts @@ -85,6 +85,12 @@ export type Interceptor = { */ name: (t: ReturnType) => MaybeRef + /** + * Defines whether the interceptor has support for cookies. + * If this field is undefined, it is assumed as not supporting cookies. + */ + supportsCookies?: boolean + /** * Defines what to render in the Interceptor section of the Settings page. * Use this space to define interceptor specific settings. @@ -161,6 +167,16 @@ export class InterceptorService extends Service { Array.from(this.interceptors.values()) ) + /** + * Gives an instance to the current interceptor. + * NOTE: Do not update from here, this is only for reading. + */ + public currentInterceptor = computed(() => { + if (this.currentInterceptorID.value === null) return null + + return this.interceptors.get(this.currentInterceptorID.value) + }) + constructor() { super() diff --git a/packages/hoppscotch-selfhost-desktop/.gitignore b/packages/hoppscotch-selfhost-desktop/.gitignore new file mode 100644 index 000000000..358c8d9a8 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/.gitignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Sitemap +.sitemap-gen + +# Backend Code generation +src/api/generated diff --git a/packages/hoppscotch-selfhost-desktop/.vscode/extensions.json b/packages/hoppscotch-selfhost-desktop/.vscode/extensions.json new file mode 100644 index 000000000..cf4385bdd --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "Vue.volar", + "tauri-apps.tauri-vscode", + "rust-lang.rust-analyzer" + ] +} diff --git a/packages/hoppscotch-selfhost-desktop/README.md b/packages/hoppscotch-selfhost-desktop/README.md new file mode 100644 index 000000000..e6b0bd5e8 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/README.md @@ -0,0 +1,16 @@ +# Tauri + Vue 3 + TypeScript + +This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` + + + + \ No newline at end of file diff --git a/packages/hoppscotch-selfhost-desktop/meta.ts b/packages/hoppscotch-selfhost-desktop/meta.ts new file mode 100644 index 000000000..529fbcb1d --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/meta.ts @@ -0,0 +1,118 @@ +import { IHTMLTag } from "vite-plugin-html-config" + +export const APP_INFO = { + name: "Hoppscotch", + shortDescription: "Open source API development ecosystem", + description: + "Helps you create requests faster, saving precious time on development.", + keywords: + "hoppscotch, hopp scotch, hoppscotch online, hoppscotch app, postwoman, postwoman chrome, postwoman online, postwoman for mac, postwoman app, postwoman for windows, postwoman google chrome, postwoman chrome app, get postwoman, postwoman web, postwoman android, postwoman app for chrome, postwoman mobile app, postwoman web app, api, request, testing, tool, rest, websocket, sse, graphql, socketio", + app: { + background: "#202124", + }, + social: { + twitter: "@hoppscotch_io", + }, +} as const + +export const META_TAGS = (env: Record): IHTMLTag[] => [ + { + name: "keywords", + content: APP_INFO.keywords, + }, + { + name: "X-UA-Compatible", + content: "IE=edge, chrome=1", + }, + { + name: "name", + content: `${APP_INFO.name} • ${APP_INFO.shortDescription}`, + }, + { + name: "description", + content: APP_INFO.description, + }, + { + name: "image", + content: `${env.VITE_BASE_URL}/banner.png`, + }, + // Open Graph tags + { + name: "og:title", + content: `${APP_INFO.name} • ${APP_INFO.shortDescription}`, + }, + { + name: "og:description", + content: APP_INFO.description, + }, + { + name: "og:image", + content: `${env.VITE_BASE_URL}/banner.png`, + }, + // Twitter tags + { + name: "twitter:card", + content: "summary_large_image", + }, + { + name: "twitter:site", + content: APP_INFO.social.twitter, + }, + { + name: "twitter:creator", + content: APP_INFO.social.twitter, + }, + { + name: "twitter:title", + content: `${APP_INFO.name} • ${APP_INFO.shortDescription}`, + }, + { + name: "twitter:description", + content: APP_INFO.description, + }, + { + name: "twitter:image", + content: `${env.VITE_BASE_URL}/banner.png`, + }, + // Add to homescreen for Chrome on Android. Fallback for PWA (handled by nuxt) + { + name: "application-name", + content: APP_INFO.name, + }, + // Windows phone tile icon + { + name: "msapplication-TileImage", + content: `${env.VITE_BASE_URL}/icon.png`, + }, + { + name: "msapplication-TileColor", + content: APP_INFO.app.background, + }, + { + name: "msapplication-tap-highlight", + content: "no", + }, + // iOS Safari + { + name: "apple-mobile-web-app-title", + content: APP_INFO.name, + }, + { + name: "apple-mobile-web-app-capable", + content: "yes", + }, + { + name: "apple-mobile-web-app-status-bar-style", + content: "black-translucent", + }, + // PWA + { + name: "theme-color", + content: APP_INFO.app.background, + }, + { + name: "mask-icon", + content: "/icon.png", + color: APP_INFO.app.background, + }, +] \ No newline at end of file diff --git a/packages/hoppscotch-selfhost-desktop/package.json b/packages/hoppscotch-selfhost-desktop/package.json new file mode 100644 index 000000000..f8e5223a1 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/package.json @@ -0,0 +1,65 @@ +{ + "name": "@hoppscotch/selfhost-desktop", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build", + "preview": "vite preview", + "tauri": "tauri" + }, + "dependencies": { + "dioc": "workspace:^", + "@hoppscotch/common": "workspace:^", + "@platform/auth": "^0.1.106", + "@tauri-apps/api": "^1.3.0", + "@tauri-apps/cli": "^1.3.0", + "@vueuse/core": "^10.4.1", + "axios": "^0.21.4", + "buffer": "^6.0.3", + "environments.api": "link:@platform/environments/environments.api", + "event": "link:@tauri-apps/api/event", + "fp-ts": "^2.16.0", + "lodash-es": "^4.17.21", + "process": "^0.11.10", + "rxjs": "^7.8.1", + "shell": "link:@tauri-apps/api/shell", + "stream-browserify": "^3.0.0", + "tauri": "link:@tauri-apps/api/tauri", + "tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store#v1", + "util": "^0.12.4", + "vue": "^3.2.45", + "workbox-window": "^6.5.4" + }, + "devDependencies": { + "@intlify/vite-plugin-vue-i18n": "^6.0.1", + "@rushstack/eslint-patch": "^1.1.4", + "@types/lodash-es": "^4.17.9", + "@types/node": "^18.7.10", + "@typescript-eslint/eslint-plugin": "^5.19.0", + "@typescript-eslint/parser": "^5.19.0", + "@vitejs/plugin-legacy": "^2.3.0", + "@vitejs/plugin-vue": "^4.0.0", + "@vue/eslint-config-typescript": "^11.0.1", + "cross-env": "^7.0.3", + "eslint": "^8.28.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-vue": "^9.5.1", + "typescript": "^4.9.5", + "unplugin-icons": "^0.14.9", + "unplugin-vue-components": "^0.21.0", + "vite": "^4.2.1", + "vite-plugin-fonts": "^0.6.0", + "vite-plugin-html-config": "^1.0.10", + "vite-plugin-inspect": "^0.7.4", + "vite-plugin-pages": "^0.26.0", + "vite-plugin-pages-sitemap": "^1.4.0", + "vite-plugin-pwa": "^0.13.1", + "vite-plugin-static-copy": "^0.12.0", + "vite-plugin-vue-layouts": "^0.7.0", + "vite-plugin-windicss": "^1.8.8", + "vue-tsc": "^1.0.11", + "windicss": "^3.5.6" + } +} diff --git a/packages/hoppscotch-selfhost-desktop/public/tauri.svg b/packages/hoppscotch-selfhost-desktop/public/tauri.svg new file mode 100644 index 000000000..31b62c928 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/public/tauri.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/hoppscotch-selfhost-desktop/public/vite.svg b/packages/hoppscotch-selfhost-desktop/public/vite.svg new file mode 100644 index 000000000..e7b8dfb1b --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/.gitignore b/packages/hoppscotch-selfhost-desktop/src-tauri/.gitignore new file mode 100644 index 000000000..f4dfb82b2 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/.gitignore @@ -0,0 +1,4 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/Cargo.lock b/packages/hoppscotch-selfhost-desktop/src-tauri/Cargo.lock new file mode 100644 index 000000000..5ce14de2e --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/Cargo.lock @@ -0,0 +1,4316 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" + +[[package]] +name = "atk" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" +dependencies = [ + "atk-sys", + "bitflags 1.3.2", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps 6.1.1", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bstr" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] + +[[package]] +name = "cairo-rs" +version = "0.15.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "glib", + "libc", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" +dependencies = [ + "glib-sys", + "libc", + "system-deps 6.1.1", +] + +[[package]] +name = "cargo_toml" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "599aa35200ffff8f04c1925aa1acc92fa2e08874379ef42e210a80e527e60838" +dependencies = [ + "serde", + "toml 0.7.6", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3431df59f28accaf4cb4eed4a9acc66bea3f3c3753aa6cdc2f024174ef232af7" +dependencies = [ + "smallvec", +] + +[[package]] +name = "cfg-expr" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "215c0072ecc28f92eeb0eea38ba63ddfcb65c2828c46311d646f1a3ff5f9841c" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "winapi", +] + +[[package]] +name = "cocoa" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics 0.22.3", + "foreign-types 0.3.2", + "libc", + "objc", +] + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics 0.23.1", + "foreign-types 0.5.0", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "foreign-types 0.3.2", + "libc", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.3.2", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.26", +] + +[[package]] +name = "ctor" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.26", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "embed-resource" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7f1e82a60222fc67bfd50d752a9c89da5cce4c39ed39decc84a443b07bbd69a" +dependencies = [ + "cc", + "rustc_version", + "toml 0.7.6", + "vswhom", + "winreg 0.11.0", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "filetime" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.2.16", + "windows-sys 0.48.0", +] + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" +dependencies = [ + "bitflags 1.3.2", + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps 6.1.1", +] + +[[package]] +name = "gdk-sys" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps 6.1.1", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cca49a59ad8cfdf36ef7330fe7bdfbe1d34323220cc16a0de2679ee773aee2c2" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps 6.1.1", +] + +[[package]] +name = "gdkx11-sys" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b7f8c7a84b407aa9b143877e267e848ff34106578b64d1e0a24bf550716178" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps 6.1.1", + "x11", +] + +[[package]] +name = "generator" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "windows 0.48.0", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + +[[package]] +name = "gio" +version = "0.15.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" +dependencies = [ + "bitflags 1.3.2", + "futures-channel", + "futures-core", + "futures-io", + "gio-sys", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps 6.1.1", + "winapi", +] + +[[package]] +name = "glib" +version = "0.15.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" +dependencies = [ + "bitflags 1.3.2", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.15.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a" +dependencies = [ + "anyhow", + "heck 0.4.1", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "glib-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" +dependencies = [ + "libc", + "system-deps 6.1.1", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "gobject-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" +dependencies = [ + "glib-sys", + "libc", + "system-deps 6.1.1", +] + +[[package]] +name = "gtk" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" +dependencies = [ + "atk", + "bitflags 1.3.2", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "once_cell", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps 6.1.1", +] + +[[package]] +name = "gtk3-macros" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "684c0456c086e8e7e9af73ec5b84e35938df394712054550e81558d21c44ab0d" +dependencies = [ + "anyhow", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "h2" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 1.9.3", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex_color" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff917051cbc87800de93ddcf39b59c9f2a0a4d809411a341c0ac422771219808" +dependencies = [ + "rand 0.8.5", +] + +[[package]] +name = "hoppscotch-desktop" +version = "0.0.0" +dependencies = [ + "cocoa 0.25.0", + "hex_color", + "objc", + "reqwest", + "serde_json", + "tauri", + "tauri-build", + "tauri-plugin-deep-link", + "tauri-plugin-store", + "tauri-plugin-window-state", + "url", + "windows 0.51.1", +] + +[[package]] +name = "html5ever" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.9", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 1.0.9", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows 0.48.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3804960be0bb5e4edb1e1ad67afd321a9ecfd875c3e65c099468fd2717d7cae" +dependencies = [ + "byteorder", + "png", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" +dependencies = [ + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] +name = "image" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-rational", + "num-traits", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + +[[package]] +name = "infer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a898e4b7951673fce96614ce5751d13c40fc5674bc2d759288e46c3ab62598b3" +dependencies = [ + "cfb", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "interprocess" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb" +dependencies = [ + "cfg-if", + "libc", + "rustc_version", + "to_method", + "winapi", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipnet" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "javascriptcore-rs" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "905fbb87419c5cde6e3269537e4ea7d46431f3008c5d057e915ef3f115e7793c" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps 5.0.0", +] + +[[package]] +name = "jni" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f54898088ccb91df1b492cc80029a6fdf1c48ca0db7c6822a8babad69c94658" +dependencies = [ + "serde", + "serde_json", + "thiserror", + "treediff", +] + +[[package]] +name = "kuchiki" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358" +dependencies = [ + "cssparser", + "html5ever", + "matches", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "loom" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "markup5ever" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd" +dependencies = [ + "log", + "phf 0.8.0", + "phf_codegen", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ndk" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e1d07c6eab1ce8b6382b8e3c7246fe117ff3f8b34be065f5ebace6749fe845" + +[[package]] +name = "objc2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "open" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" +dependencies = [ + "pathdiff", + "windows-sys 0.42.0", +] + +[[package]] +name = "openssl" +version = "0.10.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +dependencies = [ + "bitflags 2.4.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db7e971c2c2bba161b2d2fdf37080177eff520b3bc044787c7f1f5f9e78d869b" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "os_info" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" +dependencies = [ + "log", + "serde", + "winapi", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pango" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" +dependencies = [ + "bitflags 1.3.2", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps 6.1.1", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.3.5", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "plist" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06" +dependencies = [ + "base64 0.21.2", + "indexmap 1.9.3", + "line-wrap", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.10", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.10", + "redox_syscall 0.2.16", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.3.3", + "regex-syntax 0.7.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "reqwest" +version = "0.11.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +dependencies = [ + "base64 0.21.2", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg 0.50.0", +] + +[[package]] +name = "rfd" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea" +dependencies = [ + "block", + "dispatch", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "lazy_static", + "log", + "objc", + "objc-foundation", + "objc_id", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.37.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.37.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa 1.0.9", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d89a8107374290037607734c0b73a85db7ed80cae314b3c5791f192a496e731" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.9", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513" +dependencies = [ + "base64 0.21.2", + "chrono", + "hex", + "indexmap 1.9.3", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "soup2" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" +dependencies = [ + "bitflags 1.3.2", + "gio", + "glib", + "libc", + "once_cell", + "soup2-sys", +] + +[[package]] +name = "soup2-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" +dependencies = [ + "bitflags 1.3.2", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps 5.0.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "state" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" +dependencies = [ + "loom", +] + +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sys-locale" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8a11bd9c338fdba09f7881ab41551932ad42e405f61d01e8406baea71c07aee" +dependencies = [ + "js-sys", + "libc", + "wasm-bindgen", + "web-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "system-deps" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18db855554db7bd0e73e06cf7ba3df39f97812cb11d3f75e71c39bf45171797e" +dependencies = [ + "cfg-expr 0.9.1", + "heck 0.3.3", + "pkg-config", + "toml 0.5.11", + "version-compare 0.0.11", +] + +[[package]] +name = "system-deps" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" +dependencies = [ + "cfg-expr 0.15.3", + "heck 0.4.1", + "pkg-config", + "toml 0.7.6", + "version-compare 0.1.1", +] + +[[package]] +name = "tao" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6d198e01085564cea63e976ad1566c1ba2c2e4cc79578e35d9f05521505e31" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "cc", + "cocoa 0.24.1", + "core-foundation", + "core-graphics 0.22.3", + "crossbeam-channel", + "dispatch", + "gdk", + "gdk-pixbuf", + "gdk-sys", + "gdkwayland-sys", + "gdkx11-sys", + "gio", + "glib", + "glib-sys", + "gtk", + "image", + "instant", + "jni", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "png", + "raw-window-handle", + "scopeguard", + "serde", + "tao-macros", + "unicode-segmentation", + "uuid", + "windows 0.39.0", + "windows-implement", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b27a4bcc5eb524658234589bdffc7e7bfb996dbae6ce9393bfd39cb4159b445" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tar" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec96d2ffad078296368d46ff1cb309be1c23c513b4ab0e22a45de0185275ac96" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "target-lexicon" +version = "0.12.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8e77cb757a61f51b947ec4a7e3646efd825b73561db1c232a8ccb639e611a0" + +[[package]] +name = "tauri" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbe522898e35407a8e60dc3870f7579fea2fc262a6a6072eccdd37ae1e1d91e" +dependencies = [ + "anyhow", + "bytes", + "cocoa 0.24.1", + "dirs-next", + "embed_plist", + "encoding_rs", + "flate2", + "futures-util", + "glib", + "glob", + "gtk", + "heck 0.4.1", + "http", + "ignore", + "objc", + "once_cell", + "open", + "os_info", + "percent-encoding", + "rand 0.8.5", + "raw-window-handle", + "regex", + "reqwest", + "rfd", + "semver", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "state", + "sys-locale", + "tar", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "tempfile", + "thiserror", + "tokio", + "url", + "uuid", + "webkit2gtk", + "webview2-com", + "windows 0.39.0", +] + +[[package]] +name = "tauri-build" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d2edd6a259b5591c8efdeb9d5702cb53515b82a6affebd55c7fd6d3a27b7d1b" +dependencies = [ + "anyhow", + "cargo_toml", + "heck 0.4.1", + "json-patch", + "semver", + "serde", + "serde_json", + "tauri-utils", + "tauri-winres", +] + +[[package]] +name = "tauri-codegen" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ad2d49fdeab4a08717f5b49a163bdc72efc3b1950b6758245fcde79b645e1a" +dependencies = [ + "base64 0.21.2", + "brotli", + "ico", + "json-patch", + "plist", + "png", + "proc-macro2", + "quote", + "regex", + "semver", + "serde", + "serde_json", + "sha2", + "tauri-utils", + "thiserror", + "time", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb12a2454e747896929338d93b0642144bb51e0dddbb36e579035731f0d76b7" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin-deep-link" +version = "0.1.2" +source = "git+https://github.com/FabianLars/tauri-plugin-deep-link?branch=main#0c28b75ac663b4ba0fb1f0d1ea4220f82a583764" +dependencies = [ + "dirs", + "interprocess", + "log", + "objc2", + "once_cell", + "tauri-utils", + "windows-sys 0.48.0", + "winreg 0.50.0", +] + +[[package]] +name = "tauri-plugin-store" +version = "0.0.0" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#5b814f56e6368fdec46c4ddb04a07e0923ff995a" +dependencies = [ + "log", + "serde", + "serde_json", + "tauri", + "thiserror", +] + +[[package]] +name = "tauri-plugin-window-state" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f15dab0be2ce3ce8a57d0d2de17d201d0c2f3230d68981ff3f0942684de03eb" +dependencies = [ + "bincode", + "serde", + "tauri", + "thiserror", +] + +[[package]] +name = "tauri-runtime" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "108683199cb18f96d2d4134187bb789964143c845d2d154848dda209191fd769" +dependencies = [ + "gtk", + "http", + "http-range", + "rand 0.8.5", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils", + "thiserror", + "url", + "uuid", + "webview2-com", + "windows 0.39.0", +] + +[[package]] +name = "tauri-runtime-wry" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7aa256a1407a3a091b5d843eccc1a5042289baf0a43d1179d9f0fcfea37c1b" +dependencies = [ + "cocoa 0.24.1", + "gtk", + "percent-encoding", + "rand 0.8.5", + "raw-window-handle", + "tauri-runtime", + "tauri-utils", + "uuid", + "webkit2gtk", + "webview2-com", + "windows 0.39.0", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03fc02bb6072bb397e1d473c6f76c953cda48b4a2d0cce605df284aa74a12e84" +dependencies = [ + "brotli", + "ctor", + "dunce", + "glob", + "heck 0.4.1", + "html5ever", + "infer", + "json-patch", + "kuchiki", + "memchr", + "phf 0.10.1", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "serde_with", + "thiserror", + "url", + "walkdir", + "windows 0.39.0", +] + +[[package]] +name = "tauri-winres" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb" +dependencies = [ + "embed-resource", + "toml 0.7.6", +] + +[[package]] +name = "tempfile" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +dependencies = [ + "autocfg", + "cfg-if", + "fastrand", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thiserror" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" +dependencies = [ + "itoa 1.0.9", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "to_method" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" + +[[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg", + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +dependencies = [ + "indexmap 2.0.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "treediff" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303" +dependencies = [ + "serde_json", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "uuid" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +dependencies = [ + "getrandom 0.2.10", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" + +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b17ae1f6c8a2b28506cd96d412eebf83b4a0ff2cbefeeb952f2f9dfa44ba18" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.26", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasm-streams" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webkit2gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup2", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3" +dependencies = [ + "atk-sys", + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pango-sys", + "pkg-config", + "soup2-sys", + "system-deps 6.1.1", +] + +[[package]] +name = "webview2-com" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a769c9f1a64a8734bde70caafac2b96cada12cd4aefa49196b3a386b8b4178" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows 0.39.0", + "windows-implement", +] + +[[package]] +name = "webview2-com-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "webview2-com-sys" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac48ef20ddf657755fdcda8dfed2a7b4fc7e4581acce6fe9b88c3d64f29dee7" +dependencies = [ + "regex", + "serde", + "serde_json", + "thiserror", + "windows 0.39.0", + "windows-bindgen", + "windows-metadata", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" +dependencies = [ + "windows_aarch64_msvc 0.37.0", + "windows_i686_gnu 0.37.0", + "windows_i686_msvc 0.37.0", + "windows_x86_64_gnu 0.37.0", + "windows_x86_64_msvc 0.37.0", +] + +[[package]] +name = "windows" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" +dependencies = [ + "windows-implement", + "windows_aarch64_msvc 0.39.0", + "windows_i686_gnu 0.39.0", + "windows_i686_msvc 0.39.0", + "windows_x86_64_gnu 0.39.0", + "windows_x86_64_msvc 0.39.0", +] + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" +dependencies = [ + "windows-core", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-bindgen" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68003dbd0e38abc0fb85b939240f4bce37c43a5981d3df37ccbaaa981b47cb41" +dependencies = [ + "windows-metadata", + "windows-tokens", +] + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-implement" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" +dependencies = [ + "syn 1.0.109", + "windows-tokens", +] + +[[package]] +name = "windows-metadata" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-tokens" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" + +[[package]] +name = "windows_i686_gnu" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" + +[[package]] +name = "windows_i686_msvc" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a1a57ff50e9b408431e8f97d5456f2807f8eb2a2cd79b06068fc87f8ecf189" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wry" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33748f35413c8a98d45f7a08832d848c0c5915501803d1faade5a4ebcd258cea" +dependencies = [ + "base64 0.13.1", + "block", + "cocoa 0.24.1", + "core-graphics 0.22.3", + "crossbeam-channel", + "dunce", + "gdk", + "gio", + "glib", + "gtk", + "html5ever", + "http", + "kuchiki", + "libc", + "log", + "objc", + "objc_id", + "once_cell", + "serde", + "serde_json", + "sha2", + "soup2", + "tao", + "thiserror", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows 0.39.0", + "windows-implement", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/Cargo.toml b/packages/hoppscotch-selfhost-desktop/src-tauri/Cargo.toml new file mode 100644 index 000000000..12a43c3fc --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "hoppscotch-desktop" +version = "0.0.0" +description = "A Tauri App" +authors = ["you"] +license = "" +repository = "" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[build-dependencies] +tauri-build = { version = "1.4.0", features = [] } + +[dependencies] +tauri = { version = "1.4.1", features = [ + "dialog-save", + "fs-write-file", + "http-all", + "os-all", + "shell-open", + "window-start-dragging", + "http-multipart", +] } +tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } +tauri-plugin-deep-link = { git = "https://github.com/FabianLars/tauri-plugin-deep-link", branch = "main" } +tauri-plugin-window-state = "0.1.0" +reqwest = "0.11.20" +serde_json = "1.0.107" +url = "2.4.1" +hex_color = "2.0.0" + +[target.'cfg(target_os = "macos")'.dependencies] +cocoa = "0.25.0" +objc = "0.2.7" + +[target.'cfg(target_os = "windows")'.dependencies] +windows = { version = "0.51.1", features = [ + "Win32_Graphics_Dwm", + "Win32_Foundation", + "Win32_UI_Controls", +] } + +[features] +# this feature is used for production builds or when `devPath` points to the filesystem +# DO NOT REMOVE!! +custom-protocol = ["tauri/custom-protocol"] diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/Info.plist b/packages/hoppscotch-selfhost-desktop/src-tauri/Info.plist new file mode 100644 index 000000000..2af419d4e --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleURLTypes + + + CFBundleURLName + + io.hoppscotch.desktop + CFBundleURLSchemes + + + hoppscotch + + + + + \ No newline at end of file diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/build.rs b/packages/hoppscotch-selfhost-desktop/src-tauri/build.rs new file mode 100644 index 000000000..795b9b7c8 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/128x128.png b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/128x128.png new file mode 100644 index 000000000..deb04401e Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/128x128.png differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/128x128@2x.png b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/128x128@2x.png new file mode 100644 index 000000000..21e55044a Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/128x128@2x.png differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/32x32.png b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/32x32.png new file mode 100644 index 000000000..ac7d5af8e Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/32x32.png differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square107x107Logo.png b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 000000000..1cbc5179a Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square107x107Logo.png differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square142x142Logo.png b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 000000000..1ab47c52a Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square142x142Logo.png differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square150x150Logo.png b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 000000000..348f11d08 Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square150x150Logo.png differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square284x284Logo.png b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 000000000..45feeef5f Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square284x284Logo.png differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square30x30Logo.png b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 000000000..7dded529f Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square30x30Logo.png differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square310x310Logo.png b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 000000000..ea2dd24f9 Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square310x310Logo.png differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square44x44Logo.png b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 000000000..094d05796 Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square44x44Logo.png differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square71x71Logo.png b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 000000000..6451a311a Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square71x71Logo.png differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square89x89Logo.png b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 000000000..773422a6a Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/Square89x89Logo.png differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/StoreLogo.png b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/StoreLogo.png new file mode 100644 index 000000000..8a465c400 Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/StoreLogo.png differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/icon.icns b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/icon.icns new file mode 100644 index 000000000..f6d9061ca Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/icon.icns differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/icon.ico b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/icon.ico new file mode 100644 index 000000000..652ed2530 Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/icon.ico differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/icons/icon.png b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/icon.png new file mode 100644 index 000000000..08d418b6a Binary files /dev/null and b/packages/hoppscotch-selfhost-desktop/src-tauri/icons/icon.png differ diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/src/mac/mod.rs b/packages/hoppscotch-selfhost-desktop/src-tauri/src/mac/mod.rs new file mode 100644 index 000000000..61b63f177 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/src/mac/mod.rs @@ -0,0 +1 @@ +pub mod window; diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/src/mac/window.rs b/packages/hoppscotch-selfhost-desktop/src-tauri/src/mac/window.rs new file mode 100644 index 000000000..1330bfba5 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/src/mac/window.rs @@ -0,0 +1,390 @@ +use hex_color::HexColor; +use tauri::{App, Manager, Runtime, Window}; + +// If anything breaks on macOS, this should be the place which is broken +// We have to override Tauri (Tao) 's built-in NSWindowDelegate implementation with a +// custom implementation so we can emit events on full screen mode changes. +// Our custom implementation tries to mock the Tauri implementation. So please do refer to the relevant parts + +// Apple's NSWindowDelegate reference: https://developer.apple.com/documentation/appkit/nswindowdelegate?language=objc +// Tao's Window Delegate Implementation: https://github.com/tauri-apps/tao/blob/dev/src/platform_impl/macos/window_delegate.rs + +#[allow(dead_code)] +pub enum ToolbarThickness { + Thick, + Medium, + Thin, +} + +const WINDOW_CONTROL_PAD_X: f64 = 15.0; +const WINDOW_CONTROL_PAD_Y: f64 = 23.0; + +pub trait WindowExt { + #[cfg(target_os = "macos")] + fn set_transparent_titlebar(&self); +} + +#[cfg(target_os = "macos")] +unsafe fn set_transparent_titlebar(id: cocoa::base::id) { + use cocoa::appkit::NSWindow; + + id.setTitlebarAppearsTransparent_(cocoa::base::YES); + id.setTitleVisibility_(cocoa::appkit::NSWindowTitleVisibility::NSWindowTitleHidden); +} + +struct UnsafeWindowHandle(*mut std::ffi::c_void); +unsafe impl Send for UnsafeWindowHandle {} +unsafe impl Sync for UnsafeWindowHandle {} + +#[cfg(target_os = "macos")] +fn update_window_theme(window: &tauri::Window, color: HexColor) { + use cocoa::appkit::{ + NSAppearance, NSAppearanceNameVibrantDark, NSAppearanceNameVibrantLight, NSWindow, + }; + + let brightness = (color.r as u64 + color.g as u64 + color.b as u64) / 3; + + unsafe { + let window_handle = UnsafeWindowHandle(window.ns_window().unwrap()); + + let _ = window.run_on_main_thread(move || { + let handle = window_handle; + + let selected_appearance = if brightness >= 128 { + NSAppearance(NSAppearanceNameVibrantLight) + } else { + NSAppearance(NSAppearanceNameVibrantDark) + }; + + NSWindow::setAppearance(handle.0 as cocoa::base::id, selected_appearance); + set_window_controls_pos( + handle.0 as cocoa::base::id, + WINDOW_CONTROL_PAD_X, + WINDOW_CONTROL_PAD_Y, + ); + }); + } +} + +#[cfg(target_os = "macos")] +fn set_window_controls_pos(window: cocoa::base::id, x: f64, y: f64) { + use cocoa::{ + appkit::{NSView, NSWindow, NSWindowButton}, + foundation::NSRect, + }; + + unsafe { + let close = window.standardWindowButton_(NSWindowButton::NSWindowCloseButton); + let miniaturize = window.standardWindowButton_(NSWindowButton::NSWindowMiniaturizeButton); + let zoom = window.standardWindowButton_(NSWindowButton::NSWindowZoomButton); + + let title_bar_container_view = close.superview().superview(); + + let close_rect: NSRect = msg_send![close, frame]; + let button_height = close_rect.size.height; + + let title_bar_frame_height = button_height + y; + let mut title_bar_rect = NSView::frame(title_bar_container_view); + title_bar_rect.size.height = title_bar_frame_height; + title_bar_rect.origin.y = NSView::frame(window).size.height - title_bar_frame_height; + let _: () = msg_send![title_bar_container_view, setFrame: title_bar_rect]; + + let window_buttons = vec![close, miniaturize, zoom]; + let space_between = NSView::frame(miniaturize).origin.x - NSView::frame(close).origin.x; + + for (i, button) in window_buttons.into_iter().enumerate() { + let mut rect: NSRect = NSView::frame(button); + rect.origin.x = x + (i as f64 * space_between); + button.setFrameOrigin(rect.origin); + } + } +} + +impl WindowExt for Window { + #[cfg(target_os = "macos")] + fn set_transparent_titlebar(&self) { + unsafe { + let id = self.ns_window().unwrap() as cocoa::base::id; + + set_transparent_titlebar(id); + + set_window_controls_pos(id, WINDOW_CONTROL_PAD_X, WINDOW_CONTROL_PAD_Y); + } + } +} + +#[cfg(target_os = "macos")] +#[derive(Debug)] +struct HoppAppState { + window: Window, +} + +#[cfg(target_os = "macos")] +pub fn setup_mac_window(app: &mut App) { + use cocoa::appkit::NSWindow; + use cocoa::base::{id, BOOL}; + use cocoa::foundation::NSUInteger; + use objc::runtime::{Object, Sel}; + use std::ffi::c_void; + + fn with_hopp_app T, T>(this: &Object, func: F) { + let ptr = unsafe { + let x: *mut c_void = *this.get_ivar("hoppApp"); + &mut *(x as *mut HoppAppState) + }; + func(ptr); + } + + let window = app.get_window("main").unwrap(); + + unsafe { + let ns_win = window.ns_window().unwrap() as id; + + let current_delegate: id = ns_win.delegate(); + + extern "C" fn on_window_should_close(this: &Object, _cmd: Sel, sender: id) -> BOOL { + unsafe { + let super_del: id = *this.get_ivar("super_delegate"); + msg_send![super_del, windowShouldClose: sender] + } + } + extern "C" fn on_window_will_close(this: &Object, _cmd: Sel, notification: id) { + unsafe { + let super_del: id = *this.get_ivar("super_delegate"); + let _: () = msg_send![super_del, windowWillClose: notification]; + } + } + extern "C" fn on_window_did_resize(this: &Object, _cmd: Sel, notification: id) { + unsafe { + with_hopp_app(&*this, |state| { + let id = state.window.ns_window().unwrap() as id; + + set_window_controls_pos(id, WINDOW_CONTROL_PAD_X, WINDOW_CONTROL_PAD_Y); + }); + + let super_del: id = *this.get_ivar("super_delegate"); + let _: () = msg_send![super_del, windowDidResize: notification]; + } + } + extern "C" fn on_window_did_move(this: &Object, _cmd: Sel, notification: id) { + unsafe { + let super_del: id = *this.get_ivar("super_delegate"); + let _: () = msg_send![super_del, windowDidMove: notification]; + } + } + extern "C" fn on_window_did_change_backing_properties( + this: &Object, + _cmd: Sel, + notification: id, + ) { + unsafe { + let super_del: id = *this.get_ivar("super_delegate"); + let _: () = msg_send![super_del, windowDidChangeBackingProperties: notification]; + } + } + extern "C" fn on_window_did_become_key(this: &Object, _cmd: Sel, notification: id) { + unsafe { + let super_del: id = *this.get_ivar("super_delegate"); + let _: () = msg_send![super_del, windowDidBecomeKey: notification]; + } + } + extern "C" fn on_window_did_resign_key(this: &Object, _cmd: Sel, notification: id) { + unsafe { + let super_del: id = *this.get_ivar("super_delegate"); + let _: () = msg_send![super_del, windowDidResignKey: notification]; + } + } + extern "C" fn on_dragging_entered(this: &Object, _cmd: Sel, notification: id) -> BOOL { + unsafe { + let super_del: id = *this.get_ivar("super_delegate"); + msg_send![super_del, draggingEntered: notification] + } + } + extern "C" fn on_prepare_for_drag_operation( + this: &Object, + _cmd: Sel, + notification: id, + ) -> BOOL { + unsafe { + let super_del: id = *this.get_ivar("super_delegate"); + msg_send![super_del, prepareForDragOperation: notification] + } + } + extern "C" fn on_perform_drag_operation(this: &Object, _cmd: Sel, sender: id) -> BOOL { + unsafe { + let super_del: id = *this.get_ivar("super_delegate"); + msg_send![super_del, performDragOperation: sender] + } + } + extern "C" fn on_conclude_drag_operation(this: &Object, _cmd: Sel, notification: id) { + unsafe { + let super_del: id = *this.get_ivar("super_delegate"); + let _: () = msg_send![super_del, concludeDragOperation: notification]; + } + } + extern "C" fn on_dragging_exited(this: &Object, _cmd: Sel, notification: id) { + unsafe { + let super_del: id = *this.get_ivar("super_delegate"); + let _: () = msg_send![super_del, draggingExited: notification]; + } + } + extern "C" fn on_window_will_use_full_screen_presentation_options( + this: &Object, + _cmd: Sel, + window: id, + proposed_options: NSUInteger, + ) -> NSUInteger { + unsafe { + let super_del: id = *this.get_ivar("super_delegate"); + msg_send![super_del, window: window willUseFullScreenPresentationOptions: proposed_options] + } + } + extern "C" fn on_window_did_enter_full_screen(this: &Object, _cmd: Sel, notification: id) { + unsafe { + with_hopp_app(&*this, |state| { + state.window.emit("did-enter-fullscreen", ()).unwrap(); + }); + + let super_del: id = *this.get_ivar("super_delegate"); + let _: () = msg_send![super_del, windowDidEnterFullScreen: notification]; + } + } + extern "C" fn on_window_will_enter_full_screen(this: &Object, _cmd: Sel, notification: id) { + unsafe { + with_hopp_app(&*this, |state| { + state.window.emit("will-enter-fullscreen", ()).unwrap(); + }); + + let super_del: id = *this.get_ivar("super_delegate"); + let _: () = msg_send![super_del, windowWillEnterFullScreen: notification]; + } + } + extern "C" fn on_window_did_exit_full_screen(this: &Object, _cmd: Sel, notification: id) { + unsafe { + with_hopp_app(&*this, |state| { + state.window.emit("did-exit-fullscreen", ()).unwrap(); + + let id = state.window.ns_window().unwrap() as id; + set_window_controls_pos(id, WINDOW_CONTROL_PAD_X, WINDOW_CONTROL_PAD_Y); + }); + + let super_del: id = *this.get_ivar("super_delegate"); + let _: () = msg_send![super_del, windowDidExitFullScreen: notification]; + } + } + extern "C" fn on_window_will_exit_full_screen(this: &Object, _cmd: Sel, notification: id) { + unsafe { + with_hopp_app(&*this, |state| { + state.window.emit("will-exit-fullscreen", ()).unwrap(); + }); + + let super_del: id = *this.get_ivar("super_delegate"); + let _: () = msg_send![super_del, windowWillExitFullScreen: notification]; + } + } + extern "C" fn on_window_did_fail_to_enter_full_screen( + this: &Object, + _cmd: Sel, + window: id, + ) { + unsafe { + let super_del: id = *this.get_ivar("super_delegate"); + let _: () = msg_send![super_del, windowDidFailToEnterFullScreen: window]; + } + } + extern "C" fn on_effective_appearance_did_change( + this: &Object, + _cmd: Sel, + notification: id, + ) { + unsafe { + let super_del: id = *this.get_ivar("super_delegate"); + let _: () = msg_send![super_del, effectiveAppearanceDidChange: notification]; + } + } + extern "C" fn on_effective_appearance_did_changed_on_main_thread( + this: &Object, + _cmd: Sel, + notification: id, + ) { + unsafe { + let super_del: id = *this.get_ivar("super_delegate"); + let _: () = msg_send![ + super_del, + effectiveAppearanceDidChangedOnMainThread: notification + ]; + } + } + + // extern fn on_dealloc(this: &Object, cmd: Sel) { + // unsafe { + // let super_del: id = *this.get_ivar("super_delegate"); + // let _: () = msg_send![super_del, dealloc]; + // } + // } + + // extern fn on_mark_is_checking_zoomed_in(this: &Object, cmd: Sel) { + // unsafe { + // let super_del: id = *this.get_ivar("super_delegate"); + // let _: () = msg_send![super_del, markIsCheckingZoomedIn]; + // } + // } + + // extern fn on_clear_is_checking_zoomed_in(this: &Object, cmd: Sel) { + // unsafe { + // let super_del: id = *this.get_ivar("super_delegate"); + // let _: () = msg_send![super_del, clearIsCheckingZoomedIn]; + // } + // } + + // Are we deallocing this properly ? (I miss safe Rust :( ) + let app_state = HoppAppState { window }; + let app_box = Box::into_raw(Box::new(app_state)) as *mut c_void; + + ns_win.setDelegate_(delegate!("MainWindowDelegate", { + window: id = ns_win, + hoppApp: *mut c_void = app_box, + toolbar: id = cocoa::base::nil, + super_delegate: id = current_delegate, + // (dealloc) => on_dealloc as extern fn(&Object, Sel), + // (markIsCheckingZoomedIn) => on_mark_is_checking_zoomed_in as extern fn(&Object, Sel), + // (clearIsCheckingZoomedIn) => on_clear_is_checking_zoomed_in as extern fn(&Object, Sel), + (windowShouldClose:) => on_window_should_close as extern fn(&Object, Sel, id) -> BOOL, + (windowWillClose:) => on_window_will_close as extern fn(&Object, Sel, id), + (windowDidResize:) => on_window_did_resize as extern fn(&Object, Sel, id), + (windowDidMove:) => on_window_did_move as extern fn(&Object, Sel, id), + (windowDidChangeBackingProperties:) => on_window_did_change_backing_properties as extern fn(&Object, Sel, id), + (windowDidBecomeKey:) => on_window_did_become_key as extern fn(&Object, Sel, id), + (windowDidResignKey:) => on_window_did_resign_key as extern fn(&Object, Sel, id), + (draggingEntered:) => on_dragging_entered as extern fn(&Object, Sel, id) -> BOOL, + (prepareForDragOperation:) => on_prepare_for_drag_operation as extern fn(&Object, Sel, id) -> BOOL, + (performDragOperation:) => on_perform_drag_operation as extern fn(&Object, Sel, id) -> BOOL, + (concludeDragOperation:) => on_conclude_drag_operation as extern fn(&Object, Sel, id), + (draggingExited:) => on_dragging_exited as extern fn(&Object, Sel, id), + (window:willUseFullScreenPresentationOptions:) => on_window_will_use_full_screen_presentation_options as extern fn(&Object, Sel, id, NSUInteger) -> NSUInteger, + (windowDidEnterFullScreen:) => on_window_did_enter_full_screen as extern fn(&Object, Sel, id), + (windowWillEnterFullScreen:) => on_window_will_enter_full_screen as extern fn(&Object, Sel, id), + (windowDidExitFullScreen:) => on_window_did_exit_full_screen as extern fn(&Object, Sel, id), + (windowWillExitFullScreen:) => on_window_will_exit_full_screen as extern fn(&Object, Sel, id), + (windowDidFailToEnterFullScreen:) => on_window_did_fail_to_enter_full_screen as extern fn(&Object, Sel, id), + (effectiveAppearanceDidChange:) => on_effective_appearance_did_change as extern fn(&Object, Sel, id), + (effectiveAppearanceDidChangedOnMainThread:) => on_effective_appearance_did_changed_on_main_thread as extern fn(&Object, Sel, id) + })) + } + + app.get_window("main").unwrap().set_transparent_titlebar(); + + let window_handle = app.get_window("main").unwrap(); + update_window_theme(&window_handle, HexColor::WHITE); + + // Control window theme based on app update_window + app.listen_global("hopp-bg-changed", move |ev| { + let payload = serde_json::from_str::<&str>(ev.payload().unwrap()) + .unwrap() + .trim(); + + let color = HexColor::parse_rgb(payload).unwrap(); + + update_window_theme(&window_handle, color); + }); +} diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/src/main.rs b/packages/hoppscotch-selfhost-desktop/src-tauri/src/main.rs new file mode 100644 index 000000000..34171068b --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/src/main.rs @@ -0,0 +1,52 @@ +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +#[cfg(target_os = "macos")] +#[macro_use] +extern crate cocoa; + +#[cfg(target_os = "macos")] +#[macro_use] +extern crate objc; + +#[cfg(target_os = "macos")] +mod mac; + +#[cfg(target_os = "windows")] +mod win; + +use tauri::Manager; + +fn main() { + tauri_plugin_deep_link::prepare("io.hoppscotch.desktop"); + + tauri::Builder::default() + .plugin(tauri_plugin_window_state::Builder::default().build()) + .plugin(tauri_plugin_store::Builder::default().build()) + .setup(|app| { + if cfg!(target_os = "macos") { + #[cfg(target_os = "macos")] + use mac::window::setup_mac_window; + + #[cfg(target_os = "macos")] + setup_mac_window(app); + } else if cfg!(target_os = "windows") { + #[cfg(target_os = "windows")] + use win::window::setup_win_window; + + #[cfg(target_os = "windows")] + setup_win_window(app); + } + + let handle = app.handle(); + tauri_plugin_deep_link::register("hoppscotch", move |request| { + println!("{:?}", request); + handle.emit_all("scheme-request-received", request).unwrap(); + }) + .unwrap(); + + Ok(()) + }) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/src/win/mod.rs b/packages/hoppscotch-selfhost-desktop/src-tauri/src/win/mod.rs new file mode 100644 index 000000000..61b63f177 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/src/win/mod.rs @@ -0,0 +1 @@ +pub mod window; diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/src/win/window.rs b/packages/hoppscotch-selfhost-desktop/src-tauri/src/win/window.rs new file mode 100644 index 000000000..bff1408c5 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/src/win/window.rs @@ -0,0 +1,86 @@ +use hex_color::HexColor; +use tauri::App; +use tauri::Manager; + +use std::mem::transmute; +use std::{ptr, ffi::c_void, mem::size_of}; + +use windows::Win32::UI::Controls::{WTA_NONCLIENT, WTNCA_NODRAWICON, WTNCA_NOSYSMENU, WTNCA_NOMIRRORHELP}; + +use windows::Win32::UI::Controls::SetWindowThemeAttribute; +use windows::Win32::UI::Controls::WTNCA_NODRAWCAPTION; +use windows::Win32::Graphics::Dwm::DWMWA_CAPTION_COLOR; +use windows::Win32::Foundation::COLORREF; +use windows::Win32::Foundation::BOOL; +use windows::Win32::Graphics::Dwm::DwmSetWindowAttribute; +use windows::Win32::Foundation::HWND; +use windows::Win32::Graphics::Dwm::{DWMWA_USE_IMMERSIVE_DARK_MODE}; + +fn hex_color_to_colorref(color: HexColor) -> COLORREF { + // TODO: Remove this unsafe, This operation doesn't need to be unsafe! + unsafe { + COLORREF(transmute::<[u8; 4], u32>([color.r, color.g, color.b, 0])) + } +} + +struct WinThemeAttribute { + flag: u32, + mask: u32 +} + +#[cfg(target_os = "windows")] +fn update_bg_color(hwnd: &HWND, bg_color: HexColor) { + + let use_dark_mode = BOOL::from(true); + + let final_color = hex_color_to_colorref(bg_color); + + unsafe { + DwmSetWindowAttribute( + HWND(hwnd.0), + DWMWA_USE_IMMERSIVE_DARK_MODE, + ptr::addr_of!(use_dark_mode) as *const c_void, + size_of::().try_into().unwrap() + ).unwrap(); + + DwmSetWindowAttribute( + HWND(hwnd.0), + DWMWA_CAPTION_COLOR, + ptr::addr_of!(final_color) as *const c_void, + size_of::().try_into().unwrap() + ).unwrap(); + + let flags = WTNCA_NODRAWCAPTION | WTNCA_NODRAWICON; + let mask = WTNCA_NODRAWCAPTION | WTNCA_NODRAWICON | WTNCA_NOSYSMENU | WTNCA_NOMIRRORHELP; + let options = WinThemeAttribute { flag: flags, mask }; + + SetWindowThemeAttribute( + HWND(hwnd.0), + WTA_NONCLIENT, + ptr::addr_of!(options) as *const c_void, + size_of::().try_into().unwrap() + ).unwrap(); + } +} + +#[cfg(target_os = "windows")] +pub fn setup_win_window(app: &mut App) { + + let window = app.get_window("main").unwrap(); + let win_handle = window.hwnd().unwrap(); + + let win_clone = win_handle.clone(); + + app.listen_global("hopp-bg-changed", move |ev| { + + let payload = serde_json::from_str::<&str>(ev.payload().unwrap()) + .unwrap() + .trim(); + + let color = HexColor::parse_rgb(payload).unwrap(); + + update_bg_color(&HWND(win_clone.0), color); + }); + + update_bg_color(&HWND(win_handle.0), HexColor::rgb(23, 23, 23)); +} diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/tauri.conf.json b/packages/hoppscotch-selfhost-desktop/src-tauri/tauri.conf.json new file mode 100644 index 000000000..916edca72 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/tauri.conf.json @@ -0,0 +1,67 @@ +{ + "build": { + "beforeDevCommand": "pnpm dev", + "beforeBuildCommand": "pnpm build", + "devPath": "http://localhost:3000", + "distDir": "../dist", + "withGlobalTauri": false + }, + "package": { + "productName": "hoppscotch-desktop", + "version": "0.0.0" + }, + "tauri": { + "allowlist": { + "all": false, + "shell": { + "all": false, + "open": true + }, + "os": { + "all": true + }, + "fs": { + "writeFile": true + }, + "dialog": { + "save": true + }, + "http": { + "all": true, + "request": true, + "scope": ["http://*", "https://*", "wss://*"] + }, + "window": { + "startDragging": true + } + }, + "bundle": { + "active": true, + "category": "DeveloperTool", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "identifier": "io.hoppscotch.desktop", + "targets": "all" + }, + "security": { + "csp": "none" + }, + "updater": { + "active": false + }, + "windows": [ + { + "fullscreen": false, + "resizable": true, + "title": "Hoppscotch", + "width": 800, + "height": 600 + } + ] + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/ClearGlobalEnvironments.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/ClearGlobalEnvironments.graphql new file mode 100644 index 000000000..5a184fcf8 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/ClearGlobalEnvironments.graphql @@ -0,0 +1,5 @@ +mutation ClearGlobalEnvironments($id: ID!) { + clearGlobalEnvironments(id: $id) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateGQLChildUserCollection.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateGQLChildUserCollection.graphql new file mode 100644 index 000000000..54dc80c48 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateGQLChildUserCollection.graphql @@ -0,0 +1,11 @@ +mutation CreateGQLChildUserCollection( + $title: String! + $parentUserCollectionID: ID! +) { + createGQLChildUserCollection( + title: $title + parentUserCollectionID: $parentUserCollectionID + ) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateGQLRootUserCollection.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateGQLRootUserCollection.graphql new file mode 100644 index 000000000..eecdebe17 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateGQLRootUserCollection.graphql @@ -0,0 +1,5 @@ +mutation CreateGQLRootUserCollection($title: String!) { + createGQLRootUserCollection(title: $title) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateGQLUserRequest.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateGQLUserRequest.graphql new file mode 100644 index 000000000..02423e864 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateGQLUserRequest.graphql @@ -0,0 +1,13 @@ +mutation CreateGQLUserRequest( + $title: String! + $request: String! + $collectionID: ID! +) { + createGQLUserRequest( + title: $title + request: $request + collectionID: $collectionID + ) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateRESTChildUserCollection.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateRESTChildUserCollection.graphql new file mode 100644 index 000000000..40b38aca7 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateRESTChildUserCollection.graphql @@ -0,0 +1,11 @@ +mutation CreateRESTChildUserCollection( + $title: String! + $parentUserCollectionID: ID! +) { + createRESTChildUserCollection( + title: $title + parentUserCollectionID: $parentUserCollectionID + ) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateRESTRootUserCollection.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateRESTRootUserCollection.graphql new file mode 100644 index 000000000..8c54f0c2b --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateRESTRootUserCollection.graphql @@ -0,0 +1,5 @@ +mutation CreateRESTRootUserCollection($title: String!) { + createRESTRootUserCollection(title: $title) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateRESTUserRequest.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateRESTUserRequest.graphql new file mode 100644 index 000000000..01d51b0c0 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateRESTUserRequest.graphql @@ -0,0 +1,13 @@ +mutation CreateRESTUserRequest( + $collectionID: ID! + $title: String! + $request: String! +) { + createRESTUserRequest( + collectionID: $collectionID + title: $title + request: $request + ) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateUserEnvironment.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateUserEnvironment.graphql new file mode 100644 index 000000000..186510644 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateUserEnvironment.graphql @@ -0,0 +1,9 @@ +mutation CreateUserEnvironment($name: String!, $variables: String!) { + createUserEnvironment(name: $name, variables: $variables) { + id + userUid + name + variables + isGlobal + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateUserGlobalEnvironment.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateUserGlobalEnvironment.graphql new file mode 100644 index 000000000..8e7553f32 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateUserGlobalEnvironment.graphql @@ -0,0 +1,5 @@ +mutation CreateUserGlobalEnvironment($variables: String!) { + createUserGlobalEnvironment(variables: $variables) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateUserHistory.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateUserHistory.graphql new file mode 100644 index 000000000..ef9da5c66 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateUserHistory.graphql @@ -0,0 +1,13 @@ +mutation CreateUserHistory( + $reqData: String! + $resMetadata: String! + $reqType: ReqType! +) { + createUserHistory( + reqData: $reqData + resMetadata: $resMetadata + reqType: $reqType + ) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateUserSettings.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateUserSettings.graphql new file mode 100644 index 000000000..2ada8a1fa --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/CreateUserSettings.graphql @@ -0,0 +1,5 @@ +mutation CreateUserSettings($properties: String!) { + createUserSettings(properties: $properties) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/DeleteAllUserHistory.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/DeleteAllUserHistory.graphql new file mode 100644 index 000000000..3e5085870 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/DeleteAllUserHistory.graphql @@ -0,0 +1,6 @@ +mutation DeleteAllUserHistory($reqType: ReqType!) { + deleteAllUserHistory(reqType: $reqType) { + count + reqType + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/DeleteUserCollection.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/DeleteUserCollection.graphql new file mode 100644 index 000000000..cafa7b08a --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/DeleteUserCollection.graphql @@ -0,0 +1,3 @@ +mutation DeleteUserCollection($userCollectionID: ID!) { + deleteUserCollection(userCollectionID: $userCollectionID) +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/DeleteUserEnvironments.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/DeleteUserEnvironments.graphql new file mode 100644 index 000000000..148971933 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/DeleteUserEnvironments.graphql @@ -0,0 +1,3 @@ +mutation DeleteUserEnvironment($id: ID!) { + deleteUserEnvironment(id: $id) +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/DeleteUserRequest.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/DeleteUserRequest.graphql new file mode 100644 index 000000000..c4855e807 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/DeleteUserRequest.graphql @@ -0,0 +1,3 @@ +mutation DeleteUserRequest($requestID: ID!) { + deleteUserRequest(id: $requestID) +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/MoveUserCollection.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/MoveUserCollection.graphql new file mode 100644 index 000000000..ac280122f --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/MoveUserCollection.graphql @@ -0,0 +1,8 @@ +mutation MoveUserCollection($destCollectionID: ID, $userCollectionID: ID!) { + moveUserCollection( + destCollectionID: $destCollectionID + userCollectionID: $userCollectionID + ) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/MoveUserRequest.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/MoveUserRequest.graphql new file mode 100644 index 000000000..38f8e6ca7 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/MoveUserRequest.graphql @@ -0,0 +1,15 @@ +mutation MoveUserRequest( + $sourceCollectionID: ID! + $requestID: ID! + $destinationCollectionID: ID! + $nextRequestID: ID +) { + moveUserRequest( + sourceCollectionID: $sourceCollectionID + requestID: $requestID + destinationCollectionID: $destinationCollectionID + nextRequestID: $nextRequestID + ) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/RemoveRequestFromHistory.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/RemoveRequestFromHistory.graphql new file mode 100644 index 000000000..a0dd3e988 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/RemoveRequestFromHistory.graphql @@ -0,0 +1,5 @@ +mutation RemoveRequestFromHistory($id: ID!) { + removeRequestFromHistory(id: $id) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/RenameUserCollection.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/RenameUserCollection.graphql new file mode 100644 index 000000000..35603cea6 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/RenameUserCollection.graphql @@ -0,0 +1,8 @@ +mutation RenameUserCollection($userCollectionID: ID!, $newTitle: String!) { + renameUserCollection( + userCollectionID: $userCollectionID + newTitle: $newTitle + ) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/ToggleHistoryStarStatus.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/ToggleHistoryStarStatus.graphql new file mode 100644 index 000000000..33bf9c173 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/ToggleHistoryStarStatus.graphql @@ -0,0 +1,5 @@ +mutation ToggleHistoryStarStatus($id: ID!) { + toggleHistoryStarStatus(id: $id) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateGQLUserRequest.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateGQLUserRequest.graphql new file mode 100644 index 000000000..253fed7db --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateGQLUserRequest.graphql @@ -0,0 +1,5 @@ +mutation UpdateGQLUserRequest($id: ID!, $request: String!, $title: String) { + updateGQLUserRequest(id: $id, request: $request, title: $title) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateRESTUserRequest.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateRESTUserRequest.graphql new file mode 100644 index 000000000..b7899df9e --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateRESTUserRequest.graphql @@ -0,0 +1,7 @@ +mutation UpdateRESTUserRequest($id: ID!, $title: String!, $request: String!) { + updateRESTUserRequest(id: $id, title: $title, request: $request) { + id + collectionID + request + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateUserCollectionOrder.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateUserCollectionOrder.graphql new file mode 100644 index 000000000..c9e665be0 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateUserCollectionOrder.graphql @@ -0,0 +1,6 @@ +mutation UpdateUserCollectionOrder($collectionID: ID!, $nextCollectionID: ID) { + updateUserCollectionOrder( + collectionID: $collectionID + nextCollectionID: $nextCollectionID + ) +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateUserEnvironment.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateUserEnvironment.graphql new file mode 100644 index 000000000..c849a1541 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateUserEnvironment.graphql @@ -0,0 +1,9 @@ +mutation UpdateUserEnvironment($id: ID!, $name: String!, $variables: String!) { + updateUserEnvironment(id: $id, name: $name, variables: $variables) { + id + userUid + name + variables + isGlobal + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateUserSession.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateUserSession.graphql new file mode 100644 index 000000000..5ce87e5b6 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateUserSession.graphql @@ -0,0 +1,11 @@ +mutation UpdateUserSession( + $currentSession: String! + $sessionType: SessionType! +) { + updateUserSessions( + currentSession: $currentSession + sessionType: $sessionType + ) { + currentRESTSession + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateUserSettings.graphql b/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateUserSettings.graphql new file mode 100644 index 000000000..85bb4b0e6 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/mutations/UpdateUserSettings.graphql @@ -0,0 +1,5 @@ +mutation UpdateUserSettings($properties: String!) { + updateUserSettings(properties: $properties) { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/queries/CreateUserEnvironment.graphql b/packages/hoppscotch-selfhost-desktop/src/api/queries/CreateUserEnvironment.graphql new file mode 100644 index 000000000..186510644 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/queries/CreateUserEnvironment.graphql @@ -0,0 +1,9 @@ +mutation CreateUserEnvironment($name: String!, $variables: String!) { + createUserEnvironment(name: $name, variables: $variables) { + id + userUid + name + variables + isGlobal + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/queries/ExportUserCollectionsToJSON.graphql b/packages/hoppscotch-selfhost-desktop/src/api/queries/ExportUserCollectionsToJSON.graphql new file mode 100644 index 000000000..09f54adb9 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/queries/ExportUserCollectionsToJSON.graphql @@ -0,0 +1,12 @@ +query ExportUserCollectionsToJSON( + $collectionID: ID + $collectionType: ReqType! +) { + exportUserCollectionsToJSON( + collectionID: $collectionID + collectionType: $collectionType + ) { + collectionType + exportedCollection + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/queries/GetCurrentRESTSession.graphql b/packages/hoppscotch-selfhost-desktop/src/api/queries/GetCurrentRESTSession.graphql new file mode 100644 index 000000000..fee09e491 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/queries/GetCurrentRESTSession.graphql @@ -0,0 +1,5 @@ +query GetCurrentRESTSession { + me { + currentRESTSession + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/queries/GetGlobalEnvironments.graphql b/packages/hoppscotch-selfhost-desktop/src/api/queries/GetGlobalEnvironments.graphql new file mode 100644 index 000000000..08d3c2143 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/queries/GetGlobalEnvironments.graphql @@ -0,0 +1,11 @@ +query GetGlobalEnvironments { + me { + globalEnvironments { + id + isGlobal + name + userUid + variables + } + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/queries/GetRestUserHistory.graphql b/packages/hoppscotch-selfhost-desktop/src/api/queries/GetRestUserHistory.graphql new file mode 100644 index 000000000..144861594 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/queries/GetRestUserHistory.graphql @@ -0,0 +1,23 @@ +query GetRESTUserHistory { + me { + RESTHistory { + id + userUid + reqType + request + responseMetadata + isStarred + executedOn + } + + GQLHistory { + id + userUid + reqType + request + responseMetadata + isStarred + executedOn + } + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/queries/GetRootGQLUserCollections.graphql b/packages/hoppscotch-selfhost-desktop/src/api/queries/GetRootGQLUserCollections.graphql new file mode 100644 index 000000000..fcc80d993 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/queries/GetRootGQLUserCollections.graphql @@ -0,0 +1,13 @@ +query GetGQLRootUserCollections { + # the frontend doesnt paginate right now, so giving take a big enough value to get all collections at once + rootGQLUserCollections(take: 99999) { + id + title + type + childrenGQL { + id + title + type + } + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/queries/GetUserEnvironments.graphql b/packages/hoppscotch-selfhost-desktop/src/api/queries/GetUserEnvironments.graphql new file mode 100644 index 000000000..0bbeddbb4 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/queries/GetUserEnvironments.graphql @@ -0,0 +1,11 @@ +query GetUserEnvironments { + me { + environments { + id + isGlobal + name + userUid + variables + } + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/queries/GetUserRootCollections.graphql b/packages/hoppscotch-selfhost-desktop/src/api/queries/GetUserRootCollections.graphql new file mode 100644 index 000000000..0b6842339 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/queries/GetUserRootCollections.graphql @@ -0,0 +1,13 @@ +query GetUserRootCollections { + # the frontend doesnt paginate right now, so giving take a big enough value to get all collections at once + rootRESTUserCollections(take: 99999) { + id + title + type + childrenREST { + id + title + type + } + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/queries/GetUserSettings.graphql b/packages/hoppscotch-selfhost-desktop/src/api/queries/GetUserSettings.graphql new file mode 100644 index 000000000..1ac94553e --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/queries/GetUserSettings.graphql @@ -0,0 +1,8 @@ +query GetUserSettings { + me { + settings { + id + properties + } + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserCollectionCreated.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserCollectionCreated.graphql new file mode 100644 index 000000000..99870281c --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserCollectionCreated.graphql @@ -0,0 +1,10 @@ +subscription UserCollectionCreated { + userCollectionCreated { + parent { + id + } + id + title + type + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserCollectionMoved.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserCollectionMoved.graphql new file mode 100644 index 000000000..ed69fa99b --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserCollectionMoved.graphql @@ -0,0 +1,9 @@ +subscription UserCollectionMoved { + userCollectionMoved { + id + parent { + id + } + type + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserCollectionOrderUpdated.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserCollectionOrderUpdated.graphql new file mode 100644 index 000000000..6b181af88 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserCollectionOrderUpdated.graphql @@ -0,0 +1,17 @@ +subscription UserCollectionOrderUpdated { + userCollectionOrderUpdated { + userCollection { + id + parent { + id + } + } + + nextUserCollection { + id + parent { + id + } + } + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserCollectionRemoved.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserCollectionRemoved.graphql new file mode 100644 index 000000000..a3deba560 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserCollectionRemoved.graphql @@ -0,0 +1,6 @@ +subscription UserCollectionRemoved { + userCollectionRemoved { + id + type + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserCollectionUpdated.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserCollectionUpdated.graphql new file mode 100644 index 000000000..3fd8487d0 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserCollectionUpdated.graphql @@ -0,0 +1,10 @@ +subscription userCollectionUpdated { + userCollectionUpdated { + id + title + type + parent { + id + } + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserEnvironmentCreated.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserEnvironmentCreated.graphql new file mode 100644 index 000000000..49fb4b651 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserEnvironmentCreated.graphql @@ -0,0 +1,9 @@ +subscription UserEnvironmentCreated { + userEnvironmentCreated { + id + isGlobal + name + userUid + variables + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserEnvironmentDeleted.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserEnvironmentDeleted.graphql new file mode 100644 index 000000000..1ceb27e21 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserEnvironmentDeleted.graphql @@ -0,0 +1,5 @@ +subscription UserEnvironmentDeleted { + userEnvironmentDeleted { + id + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserEnvironmentUpdated.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserEnvironmentUpdated.graphql new file mode 100644 index 000000000..730c33e57 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserEnvironmentUpdated.graphql @@ -0,0 +1,9 @@ +subscription UserEnvironmentUpdated { + userEnvironmentUpdated { + id + userUid + name + variables + isGlobal + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserHistoryCreated.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserHistoryCreated.graphql new file mode 100644 index 000000000..f513671ab --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserHistoryCreated.graphql @@ -0,0 +1,10 @@ +subscription UserHistoryCreated { + userHistoryCreated { + id + reqType + request + responseMetadata + isStarred + executedOn + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserHistoryDeleted.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserHistoryDeleted.graphql new file mode 100644 index 000000000..ba6540fe2 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserHistoryDeleted.graphql @@ -0,0 +1,6 @@ +subscription userHistoryDeleted { + userHistoryDeleted { + id + reqType + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserHistoryDeletedMany.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserHistoryDeletedMany.graphql new file mode 100644 index 000000000..fae7aca62 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserHistoryDeletedMany.graphql @@ -0,0 +1,6 @@ +subscription UserHistoryDeletedMany { + userHistoryDeletedMany { + count + reqType + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserHistoryUpdated.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserHistoryUpdated.graphql new file mode 100644 index 000000000..a673edb72 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserHistoryUpdated.graphql @@ -0,0 +1,10 @@ +subscription UserHistoryUpdated { + userHistoryUpdated { + id + reqType + request + responseMetadata + isStarred + executedOn + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserRequestCreated.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserRequestCreated.graphql new file mode 100644 index 000000000..462bc2461 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserRequestCreated.graphql @@ -0,0 +1,9 @@ +subscription UserRequestCreated { + userRequestCreated { + id + collectionID + title + request + type + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserRequestDeleted.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserRequestDeleted.graphql new file mode 100644 index 000000000..6ef26f14c --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserRequestDeleted.graphql @@ -0,0 +1,9 @@ +subscription UserRequestDeleted { + userRequestDeleted { + id + collectionID + title + request + type + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserRequestMoved.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserRequestMoved.graphql new file mode 100644 index 000000000..01a0fb24a --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserRequestMoved.graphql @@ -0,0 +1,13 @@ +subscription UserRequestMoved { + userRequestMoved { + request { + id + collectionID + type + } + nextRequest { + id + collectionID + } + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserRequestUpdated.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserRequestUpdated.graphql new file mode 100644 index 000000000..b8c7f6e27 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserRequestUpdated.graphql @@ -0,0 +1,9 @@ +subscription UserRequestUpdated { + userRequestUpdated { + id + collectionID + title + request + type + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserSettingsUpdated.graphql b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserSettingsUpdated.graphql new file mode 100644 index 000000000..4b8f6a6e7 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/api/subscriptions/UserSettingsUpdated.graphql @@ -0,0 +1,6 @@ +subscription UserSettingsUpdated { + userSettingsUpdated { + id + properties + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/lib/sync/index.ts b/packages/hoppscotch-selfhost-desktop/src/lib/sync/index.ts new file mode 100644 index 000000000..1427e600d --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/lib/sync/index.ts @@ -0,0 +1,102 @@ +import { Observable } from "rxjs" +import DispatchingStore from "@hoppscotch/common/newstore/DispatchingStore" + +export type DispatchersOf> = + T extends DispatchingStore + ? U extends Record + ? D + : never + : never + +export type StoreSyncDefinitionOf> = { + [x in DispatchersOf]?: T extends DispatchingStore + ? U extends Record + ? U[x] extends (x: any, y: infer Y) => any + ? (payload: Y) => void + : never + : never + : never +} + +let _isRunningDispatchWithoutSyncing = true + +export function runDispatchWithOutSyncing(func: () => void) { + _isRunningDispatchWithoutSyncing = false + func() + _isRunningDispatchWithoutSyncing = true +} + +export const getSyncInitFunction = >( + store: T, + storeSyncDefinition: StoreSyncDefinitionOf, + shouldSyncValue: () => boolean, + shouldSyncObservable?: Observable +) => { + let startSubscriptions: () => () => void | undefined + let stopSubscriptions: () => void | undefined + + let oldSyncStatus = shouldSyncValue() + + // Start and stop the subscriptions according to the sync settings from profile + shouldSyncObservable && + shouldSyncObservable.subscribe((newSyncStatus) => { + if (oldSyncStatus === true && newSyncStatus === false) { + stopListeningToSubscriptions() + } else if (oldSyncStatus === false && newSyncStatus === true) { + startListeningToSubscriptions() + } + + oldSyncStatus = newSyncStatus + }) + + function startStoreSync() { + store.dispatches$.subscribe((actionParams) => { + // typescript cannot understand that the dispatcher can be the index, so casting to any + if ((storeSyncDefinition as any)[actionParams.dispatcher]) { + const dispatcher = actionParams.dispatcher + const payload = actionParams.payload + + const operationMapperFunction = (storeSyncDefinition as any)[dispatcher] + + if ( + operationMapperFunction && + _isRunningDispatchWithoutSyncing && + shouldSyncValue() + ) { + operationMapperFunction(payload) + } + } + }) + } + + function setupSubscriptions(func: () => () => void) { + startSubscriptions = func + } + + function startListeningToSubscriptions() { + if (!startSubscriptions) { + console.warn( + "We don't have a function to start subscriptions. Please use `setupSubscriptions` to setup the start function." + ) + } + + stopSubscriptions = startSubscriptions() + } + + function stopListeningToSubscriptions() { + if (!stopSubscriptions) { + console.warn( + "We don't have a function to unsubscribe. make sure you return the unsubscribe function when using setupSubscriptions" + ) + } + + stopSubscriptions() + } + + return { + startStoreSync, + setupSubscriptions, + startListeningToSubscriptions, + stopListeningToSubscriptions, + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/lib/sync/mapper.ts b/packages/hoppscotch-selfhost-desktop/src/lib/sync/mapper.ts new file mode 100644 index 000000000..b303243a6 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/lib/sync/mapper.ts @@ -0,0 +1,42 @@ +export const createMapper = < + LocalIDType extends string | number, + BackendIDType extends string | number +>() => { + const backendIDByLocalIDMap = new Map< + LocalIDType, + BackendIDType | undefined + >() + const localIDByBackendIDMap = new Map< + BackendIDType, + LocalIDType | undefined + >() + + return { + addEntry(localIdentifier: LocalIDType, backendIdentifier: BackendIDType) { + backendIDByLocalIDMap.set(localIdentifier, backendIdentifier) + localIDByBackendIDMap.set(backendIdentifier, localIdentifier) + }, + getValue() { + return backendIDByLocalIDMap + }, + getBackendIDByLocalID(localIdentifier: LocalIDType) { + return backendIDByLocalIDMap.get(localIdentifier) + }, + getLocalIDByBackendID(backendId: BackendIDType) { + return localIDByBackendIDMap.get(backendId) + }, + removeEntry(backendId?: BackendIDType, index?: LocalIDType) { + if (backendId) { + const index = localIDByBackendIDMap.get(backendId) + + localIDByBackendIDMap.delete(backendId) + index && backendIDByLocalIDMap.delete(index) + } else if (index) { + const backendId = backendIDByLocalIDMap.get(index) + + backendIDByLocalIDMap.delete(index) + backendId && localIDByBackendIDMap.delete(backendId) + } + }, + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/main.ts b/packages/hoppscotch-selfhost-desktop/src/main.ts new file mode 100644 index 000000000..c150e3d3a --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/main.ts @@ -0,0 +1,113 @@ +import { createHoppApp } from "@hoppscotch/common" +import { def as authDef } from "./platform/auth" +import { def as environmentsDef } from "./platform/environments/environments.platform" +import { def as collectionsDef } from "./platform/collections/collections.platform" +import { def as settingsDef } from "./platform/settings/settings.platform" +import { def as historyDef } from "./platform/history/history.platform" +import { def as tabStateDef } from "./platform/tabState/tabState.platform" +import { proxyInterceptor } from "@hoppscotch/common/platform/std/interceptors/proxy" +import { ExtensionInspectorService } from "@hoppscotch/common/platform/std/inspections/extension.inspector" +import { NativeInterceptorService } from "./platform/interceptors/native" +import { nextTick, ref, watch } from "vue" +import { emit, listen } from "@tauri-apps/api/event" +import { type } from "@tauri-apps/api/os" +import { useSettingStatic } from "@hoppscotch/common/composables/settings" +import { appWindow } from "@tauri-apps/api/window" +import { stdFooterItems } from "@hoppscotch/common/platform/std/ui/footerItem" +import { stdSupportOptionItems } from "@hoppscotch/common/platform/std/ui/supportOptionsItem" +import { useMousePressed } from "@vueuse/core" +import { ioDef } from "./platform/io" + +const headerPaddingLeft = ref("0px") +const headerPaddingTop = ref("0px") + +createHoppApp("#app", { + ui: { + additionalFooterMenuItems: stdFooterItems, + additionalSupportOptionsMenuItems: stdSupportOptionItems, + appHeader: { + paddingLeft: headerPaddingLeft, + paddingTop: headerPaddingTop, + }, + }, + io: ioDef, + auth: authDef, + sync: { + environments: environmentsDef, + collections: collectionsDef, + settings: settingsDef, + history: historyDef, + tabState: tabStateDef, + }, + interceptors: { + default: "native", + interceptors: [ + { type: "service", service: NativeInterceptorService }, + { type: "standalone", interceptor: proxyInterceptor }, + ], + }, + additionalInspectors: [ + { type: "service", service: ExtensionInspectorService }, + ], + platformFeatureFlags: { + exportAsGIST: false, + hasTelemetry: false, + cookiesEnabled: true, + }, +}) + +watch( + useSettingStatic("BG_COLOR")[0], + async () => { + await nextTick() + + await emit( + "hopp-bg-changed", + getComputedStyle(document.documentElement).getPropertyValue( + "--primary-color" + ) + ) + }, + { immediate: true } +) +;(async () => { + const platform = await type() + + if (platform === "Darwin") { + listen("will-enter-fullscreen", () => { + headerPaddingTop.value = "0px" + headerPaddingLeft.value = "0px" + }) + + listen("will-exit-fullscreen", () => { + headerPaddingTop.value = "2px" + headerPaddingLeft.value = "70px" + }) + + headerPaddingTop.value = "2px" + headerPaddingLeft.value = "70px" + } + + const { pressed } = useMousePressed() + + document.addEventListener("mousemove", (ev) => { + const { clientX, clientY } = ev + + const el = document.querySelector("header") + + if (!el) return + + const { left, top, width, height } = el.getBoundingClientRect() + + if ( + clientX >= left && + clientX <= left + width && + clientY >= top && + clientY <= top + height + ) { + if (pressed.value) { + appWindow.startDragging() + } + } + }) +})() diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/auth.ts b/packages/hoppscotch-selfhost-desktop/src/platform/auth.ts new file mode 100644 index 000000000..d1094625c --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/auth.ts @@ -0,0 +1,374 @@ +import axios from "axios" +import { + AuthEvent, + AuthPlatformDef, + HoppUser, +} from "@hoppscotch/common/platform/auth" +import { BehaviorSubject, Subject } from "rxjs" +import { + getLocalConfig, + removeLocalConfig, + setLocalConfig, +} from "@hoppscotch/common/newstore/localpersistence" +import { Ref, ref, watch } from "vue" +import { open } from '@tauri-apps/api/shell' +import { Body, getClient } from '@tauri-apps/api/http' +import { listen } from '@tauri-apps/api/event' +import { Store } from "tauri-plugin-store-api"; + +export const authEvents$ = new Subject() +const currentUser$ = new BehaviorSubject(null) +export const probableUser$ = new BehaviorSubject(null) + +const APP_DATA_PATH = "~/.hopp-desktop-app-data.dat" + +async function logout() { + let client = await getClient(); + await client.get(`${import.meta.env.VITE_BACKEND_API_URL}/auth/logout`) + + const store = new Store(APP_DATA_PATH) + await store.set("refresh_token", {}) + await store.set("access_token", {}) + await store.save() +} + +async function signInUserWithGithubFB() { + await open(`${import.meta.env.VITE_BACKEND_API_URL}/auth/github?redirect_uri=desktop`); +} + +async function signInUserWithGoogleFB() { + await open(`${import.meta.env.VITE_BACKEND_API_URL}/auth/google?redirect_uri=desktop`); +} + +async function signInUserWithMicrosoftFB() { + await open(`${import.meta.env.VITE_BACKEND_API_URL}/auth/microsoft?redirect_uri=desktop`); +} + +async function getInitialUserDetails() { + const store = new Store(APP_DATA_PATH); + + try { + const accessToken = await store.get("access_token") + let client = await getClient() + let body = { + query: `query Me { + me { + uid + displayName + email + photoURL + isAdmin + createdOn + } + }`} + + let res = await client.post(`${import.meta.env.VITE_BACKEND_GQL_URL}`, + Body.json(body), { + headers: { + "Cookie": `access_token=${accessToken.value}`, + } + } + ) + + return res.data + } catch (error) { + let res = { + error: "auth/cookies_not_found" + } + + return res + } +} + +const isGettingInitialUser: Ref = ref(null) + +function setUser(user: HoppUser | null) { + currentUser$.next(user) + probableUser$.next(user) + + setLocalConfig("login_state", JSON.stringify(user)) +} + +async function setInitialUser() { + isGettingInitialUser.value = true + const res = await getInitialUserDetails() + + const error = res.errors && res.errors[0] + + // no cookies sent. so the user is not logged in + if (error && error.message === "auth/cookies_not_found") { + setUser(null) + isGettingInitialUser.value = false + return + } + + if (error && error.message === "user/not_found") { + setUser(null) + isGettingInitialUser.value = false + return + } + + // cookies sent, but it is expired, we need to refresh the token + if (error && error.message === "Unauthorized") { + const isRefreshSuccess = await refreshToken() + + if (isRefreshSuccess) { + setInitialUser() + } else { + setUser(null) + isGettingInitialUser.value = false + } + + return + } + + // no errors, we have a valid user + if (res.data && res.data.me) { + const hoppBackendUser = res.data.me + + const hoppUser: HoppUser = { + uid: hoppBackendUser.uid, + displayName: hoppBackendUser.displayName, + email: hoppBackendUser.email, + photoURL: hoppBackendUser.photoURL, + // all our signin methods currently guarantees the email is verified + emailVerified: true, + } + + setUser(hoppUser) + + isGettingInitialUser.value = false + + authEvents$.next({ + event: "login", + user: hoppUser, + }) + + return + } +} + +async function refreshToken() { + const store = new Store(APP_DATA_PATH); + try { + const refreshToken = await store.get("refresh_token") + + let client = await getClient() + let res = await client.get(`${import.meta.env.VITE_BACKEND_API_URL}/auth/refresh`, { + headers: { "Cookie": `refresh_token=${refreshToken.value}` } + }) + + setAuthCookies(res.rawHeaders) + + const isSuccessful = res.status === 200 + + if (isSuccessful) { + authEvents$.next({ + event: "token_refresh", + }) + } + + return isSuccessful + } catch (err) { + return false + } +} + +async function sendMagicLink(email: string) { + const client = await getClient(); + let url = `${import.meta.env.VITE_BACKEND_API_URL}/auth/signin?origin=desktop`; + + const res = await client.post(url, Body.json({ email })); + + if (res.data && res.data.deviceIdentifier) { + setLocalConfig("deviceIdentifier", res.data.deviceIdentifier) + } else { + throw new Error("test: does not get device identifier") + } + + return res.data +} + +async function setAuthCookies(rawHeaders: Array) { + let cookies = rawHeaders['set-cookie'].join("|") + + const accessTokenMatch = cookies.match(/access_token=([^;]+)/); + const refreshTokenMatch = cookies.match(/refresh_token=([^;]+)/); + + const store = new Store(APP_DATA_PATH) + + if (accessTokenMatch) { + const accessToken = accessTokenMatch[1]; + await store.set("access_token", { value: accessToken }) + } + + if (refreshTokenMatch) { + const refreshToken = refreshTokenMatch[1]; + await store.set("refresh_token", { value: refreshToken }) + } + + await store.save() +} + + +export const def: AuthPlatformDef = { + getCurrentUserStream: () => currentUser$, + getAuthEventsStream: () => authEvents$, + getProbableUserStream: () => probableUser$, + + getCurrentUser: () => currentUser$.value, + getProbableUser: () => probableUser$.value, + + getBackendHeaders() { + return {} + }, + getGQLClientOptions() { + return { + fetchOptions: { + credentials: "include", + }, + } + }, + + /** + * it is not possible for us to know if the current cookie is expired because we cannot access http-only cookies from js + * hence just returning if the currentUser$ has a value associated with it + */ + willBackendHaveAuthError() { + return !currentUser$.value + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onBackendGQLClientShouldReconnect(func) { + authEvents$.subscribe((event) => { + if ( + event.event == "login" || + event.event == "logout" || + event.event == "token_refresh" + ) { + func() + } + }) + }, + + /** + * we cannot access our auth cookies from javascript, so leaving this as null + */ + getDevOptsBackendIDToken() { + return null + }, + async performAuthInit() { + const probableUser = JSON.parse(getLocalConfig("login_state") ?? "null") + probableUser$.next(probableUser) + await setInitialUser() + + await listen('scheme-request-received', async (event: any) => { + let deep_link = event.payload as string; + + const params = new URLSearchParams(deep_link.split('?')[1]); + const accessToken = params.get('access_token'); + const refreshToken = params.get('refresh_token'); + const token = params.get('token'); + + function isNotNullOrUndefined(x: any) { + return x !== null && x !== undefined; + } + + if (isNotNullOrUndefined(accessToken) && isNotNullOrUndefined(refreshToken)) { + const store = new Store(APP_DATA_PATH) + + await store.set("access_token", { value: accessToken }); + await store.set("refresh_token", { value: refreshToken } ); + await store.save() + + window.location.href = "/" + return; + } + + if (isNotNullOrUndefined(token)) { + setLocalConfig("verifyToken", token) + await this.signInWithEmailLink("", "") + await setInitialUser() + } + }); + }, + + waitProbableLoginToConfirm() { + return new Promise((resolve, reject) => { + if (this.getCurrentUser()) { + resolve() + } + + if (!probableUser$.value) reject(new Error("no_probable_user")) + + const unwatch = watch(isGettingInitialUser, (val) => { + if (val === true || val === false) { + resolve() + unwatch() + } + }) + }) + }, + + async signInWithEmail(email: string) { + await sendMagicLink(email) + }, + + async verifyEmailAddress() { + return + }, + async signInUserWithGoogle() { + await signInUserWithGoogleFB() + }, + async signInUserWithGithub() { + await signInUserWithGithubFB() + return undefined + }, + async signInUserWithMicrosoft() { + await signInUserWithMicrosoftFB() + }, + async signInWithEmailLink(_email, _url) { + const deviceIdentifier = getLocalConfig("deviceIdentifier") + + if (!deviceIdentifier) { + throw new Error( + "Device Identifier not found, you can only signin from the browser you generated the magic link" + ) + } + + let verifyToken = getLocalConfig("verifyToken") + + const client = await getClient(); + let res = await client.post(`${import.meta.env.VITE_BACKEND_API_URL}/auth/verify`, Body.json({ + token: verifyToken, + deviceIdentifier + })); + + setAuthCookies(res.rawHeaders) + + removeLocalConfig("deviceIdentifier") + removeLocalConfig("verifyToken") + window.location.href = "/" + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async setEmailAddress(_email: string) { + return + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async setDisplayName(name: string) { + return + }, + + async signOutUser() { + // if (!currentUser$.value) throw new Error("No user has logged in") + + await logout() + + probableUser$.next(null) + currentUser$.next(null) + removeLocalConfig("login_state") + + authEvents$.next({ + event: "logout", + }) + }, +} diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/collections/collections.api.ts b/packages/hoppscotch-selfhost-desktop/src/platform/collections/collections.api.ts new file mode 100644 index 000000000..2373d7d06 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/collections/collections.api.ts @@ -0,0 +1,305 @@ +import { + runGQLQuery, + runGQLSubscription, + runMutation, +} from "@hoppscotch/common/helpers/backend/GQLClient" +import { + CreateRestRootUserCollectionDocument, + CreateRestRootUserCollectionMutation, + CreateRestRootUserCollectionMutationVariables, + CreateRestUserRequestMutation, + CreateRestUserRequestMutationVariables, + CreateRestUserRequestDocument, + CreateRestChildUserCollectionMutation, + CreateRestChildUserCollectionMutationVariables, + CreateRestChildUserCollectionDocument, + DeleteUserCollectionMutation, + DeleteUserCollectionMutationVariables, + DeleteUserCollectionDocument, + RenameUserCollectionMutation, + RenameUserCollectionMutationVariables, + RenameUserCollectionDocument, + MoveUserCollectionMutation, + MoveUserCollectionMutationVariables, + MoveUserCollectionDocument, + DeleteUserRequestMutation, + DeleteUserRequestMutationVariables, + DeleteUserRequestDocument, + MoveUserRequestDocument, + MoveUserRequestMutation, + MoveUserRequestMutationVariables, + UpdateUserCollectionOrderMutation, + UpdateUserCollectionOrderMutationVariables, + UpdateUserCollectionOrderDocument, + GetUserRootCollectionsQuery, + GetUserRootCollectionsQueryVariables, + GetUserRootCollectionsDocument, + UserCollectionCreatedDocument, + UserCollectionUpdatedDocument, + UserCollectionRemovedDocument, + UserCollectionMovedDocument, + UserCollectionOrderUpdatedDocument, + ExportUserCollectionsToJsonQuery, + ExportUserCollectionsToJsonQueryVariables, + ExportUserCollectionsToJsonDocument, + UserRequestCreatedDocument, + UserRequestUpdatedDocument, + UserRequestMovedDocument, + UserRequestDeletedDocument, + UpdateRestUserRequestMutation, + UpdateRestUserRequestMutationVariables, + UpdateRestUserRequestDocument, + CreateGqlRootUserCollectionMutation, + CreateGqlRootUserCollectionMutationVariables, + CreateGqlRootUserCollectionDocument, + CreateGqlUserRequestMutation, + CreateGqlUserRequestMutationVariables, + CreateGqlUserRequestDocument, + CreateGqlChildUserCollectionMutation, + CreateGqlChildUserCollectionMutationVariables, + CreateGqlChildUserCollectionDocument, + UpdateGqlUserRequestMutation, + UpdateGqlUserRequestMutationVariables, + UpdateGqlUserRequestDocument, + GetGqlRootUserCollectionsQuery, + GetGqlRootUserCollectionsQueryVariables, + GetGqlRootUserCollectionsDocument, + ReqType, +} from "../../api/generated/graphql" + +export const createRESTRootUserCollection = (title: string) => + runMutation< + CreateRestRootUserCollectionMutation, + CreateRestRootUserCollectionMutationVariables, + "" + >(CreateRestRootUserCollectionDocument, { + title, + })() + +export const createGQLRootUserCollection = (title: string) => + runMutation< + CreateGqlRootUserCollectionMutation, + CreateGqlRootUserCollectionMutationVariables, + "" + >(CreateGqlRootUserCollectionDocument, { + title, + })() + +export const createRESTUserRequest = ( + title: string, + request: string, + collectionID: string +) => + runMutation< + CreateRestUserRequestMutation, + CreateRestUserRequestMutationVariables, + "" + >(CreateRestUserRequestDocument, { + title, + request, + collectionID, + })() + +export const createGQLUserRequest = ( + title: string, + request: string, + collectionID: string +) => + runMutation< + CreateGqlUserRequestMutation, + CreateGqlUserRequestMutationVariables, + "" + >(CreateGqlUserRequestDocument, { + title, + request, + collectionID, + })() + +export const createRESTChildUserCollection = ( + title: string, + parentUserCollectionID: string +) => + runMutation< + CreateRestChildUserCollectionMutation, + CreateRestChildUserCollectionMutationVariables, + "" + >(CreateRestChildUserCollectionDocument, { + title, + parentUserCollectionID, + })() + +export const createGQLChildUserCollection = ( + title: string, + parentUserCollectionID: string +) => + runMutation< + CreateGqlChildUserCollectionMutation, + CreateGqlChildUserCollectionMutationVariables, + "" + >(CreateGqlChildUserCollectionDocument, { + title, + parentUserCollectionID, + })() + +export const deleteUserCollection = (userCollectionID: string) => + runMutation< + DeleteUserCollectionMutation, + DeleteUserCollectionMutationVariables, + "" + >(DeleteUserCollectionDocument, { + userCollectionID, + })() + +export const renameUserCollection = ( + userCollectionID: string, + newTitle: string +) => + runMutation< + RenameUserCollectionMutation, + RenameUserCollectionMutationVariables, + "" + >(RenameUserCollectionDocument, { userCollectionID, newTitle })() + +export const moveUserCollection = ( + sourceCollectionID: string, + destinationCollectionID?: string +) => + runMutation< + MoveUserCollectionMutation, + MoveUserCollectionMutationVariables, + "" + >(MoveUserCollectionDocument, { + userCollectionID: sourceCollectionID, + destCollectionID: destinationCollectionID, + })() + +export const editUserRequest = ( + requestID: string, + title: string, + request: string +) => + runMutation< + UpdateRestUserRequestMutation, + UpdateRestUserRequestMutationVariables, + "" + >(UpdateRestUserRequestDocument, { + id: requestID, + request, + title, + })() + +export const editGQLUserRequest = ( + requestID: string, + title: string, + request: string +) => + runMutation< + UpdateGqlUserRequestMutation, + UpdateGqlUserRequestMutationVariables, + "" + >(UpdateGqlUserRequestDocument, { + id: requestID, + request, + title, + })() + +export const deleteUserRequest = (requestID: string) => + runMutation< + DeleteUserRequestMutation, + DeleteUserRequestMutationVariables, + "" + >(DeleteUserRequestDocument, { + requestID, + })() + +export const moveUserRequest = ( + sourceCollectionID: string, + destinationCollectionID: string, + requestID: string, + nextRequestID?: string +) => + runMutation( + MoveUserRequestDocument, + { + sourceCollectionID, + destinationCollectionID, + requestID, + nextRequestID, + } + )() + +export const updateUserCollectionOrder = ( + collectionID: string, + nextCollectionID?: string +) => + runMutation< + UpdateUserCollectionOrderMutation, + UpdateUserCollectionOrderMutationVariables, + "" + >(UpdateUserCollectionOrderDocument, { + collectionID, + nextCollectionID, + })() + +export const getUserRootCollections = () => + runGQLQuery< + GetUserRootCollectionsQuery, + GetUserRootCollectionsQueryVariables, + "" + >({ + query: GetUserRootCollectionsDocument, + variables: {}, + }) + +export const getGQLRootUserCollections = () => + runGQLQuery< + GetGqlRootUserCollectionsQuery, + GetGqlRootUserCollectionsQueryVariables, + "" + >({ + query: GetGqlRootUserCollectionsDocument, + variables: {}, + }) + +export const exportUserCollectionsToJSON = ( + collectionID?: string, + collectionType: ReqType.Rest | ReqType.Gql = ReqType.Rest +) => + runGQLQuery< + ExportUserCollectionsToJsonQuery, + ExportUserCollectionsToJsonQueryVariables, + "" + >({ + query: ExportUserCollectionsToJsonDocument, + variables: { collectionID, collectionType }, + }) + +export const runUserCollectionCreatedSubscription = () => + runGQLSubscription({ query: UserCollectionCreatedDocument, variables: {} }) + +export const runUserCollectionUpdatedSubscription = () => + runGQLSubscription({ query: UserCollectionUpdatedDocument, variables: {} }) + +export const runUserCollectionRemovedSubscription = () => + runGQLSubscription({ query: UserCollectionRemovedDocument, variables: {} }) + +export const runUserCollectionMovedSubscription = () => + runGQLSubscription({ query: UserCollectionMovedDocument, variables: {} }) + +export const runUserCollectionOrderUpdatedSubscription = () => + runGQLSubscription({ + query: UserCollectionOrderUpdatedDocument, + variables: {}, + }) + +export const runUserRequestCreatedSubscription = () => + runGQLSubscription({ query: UserRequestCreatedDocument, variables: {} }) + +export const runUserRequestUpdatedSubscription = () => + runGQLSubscription({ query: UserRequestUpdatedDocument, variables: {} }) + +export const runUserRequestMovedSubscription = () => + runGQLSubscription({ query: UserRequestMovedDocument, variables: {} }) + +export const runUserRequestDeletedSubscription = () => + runGQLSubscription({ query: UserRequestDeletedDocument, variables: {} }) diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/collections/collections.platform.ts b/packages/hoppscotch-selfhost-desktop/src/platform/collections/collections.platform.ts new file mode 100644 index 000000000..59d3ce965 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/collections/collections.platform.ts @@ -0,0 +1,789 @@ +import { authEvents$, def as platformAuth } from "@platform/auth" +import { CollectionsPlatformDef } from "@hoppscotch/common/platform/collections" +import { runDispatchWithOutSyncing } from "../../lib/sync" + +import { + exportUserCollectionsToJSON, + runUserCollectionCreatedSubscription, + runUserCollectionMovedSubscription, + runUserCollectionOrderUpdatedSubscription, + runUserCollectionRemovedSubscription, + runUserCollectionUpdatedSubscription, + runUserRequestCreatedSubscription, + runUserRequestDeletedSubscription, + runUserRequestMovedSubscription, + runUserRequestUpdatedSubscription, +} from "./collections.api" +import { collectionsSyncer, getStoreByCollectionType } from "./collections.sync" + +import * as E from "fp-ts/Either" +import { + addRESTCollection, + setRESTCollections, + editRESTCollection, + removeRESTCollection, + moveRESTFolder, + updateRESTCollectionOrder, + saveRESTRequestAs, + navigateToFolderWithIndexPath, + editRESTRequest, + removeRESTRequest, + moveRESTRequest, + updateRESTRequestOrder, + addRESTFolder, + editRESTFolder, + removeRESTFolder, + addGraphqlFolder, + addGraphqlCollection, + editGraphqlFolder, + editGraphqlCollection, + removeGraphqlFolder, + removeGraphqlCollection, + saveGraphqlRequestAs, + editGraphqlRequest, + moveGraphqlRequest, + removeGraphqlRequest, + setGraphqlCollections, + restCollectionStore, +} from "@hoppscotch/common/newstore/collections" +import { runGQLSubscription } from "@hoppscotch/common/helpers/backend/GQLClient" +import { + HoppCollection, + HoppGQLRequest, + HoppRESTRequest, +} from "@hoppscotch/data" +import { gqlCollectionsSyncer } from "./gqlCollections.sync" +import { ReqType } from "../../api/generated/graphql" + +function initCollectionsSync() { + const currentUser$ = platformAuth.getCurrentUserStream() + collectionsSyncer.startStoreSync() + collectionsSyncer.setupSubscriptions(setupSubscriptions) + + gqlCollectionsSyncer.startStoreSync() + + loadUserCollections("REST") + loadUserCollections("GQL") + + // TODO: test & make sure the auth thing is working properly + currentUser$.subscribe(async (user) => { + if (user) { + loadUserCollections("REST") + loadUserCollections("GQL") + } + }) + + authEvents$.subscribe((event) => { + if (event.event == "login" || event.event == "token_refresh") { + collectionsSyncer.startListeningToSubscriptions() + } + + if (event.event == "logout") { + collectionsSyncer.stopListeningToSubscriptions() + } + }) +} + +type ExportedUserCollectionREST = { + id?: string + folders: ExportedUserCollectionREST[] + requests: Array + name: string +} + +type ExportedUserCollectionGQL = { + id?: string + folders: ExportedUserCollectionGQL[] + requests: Array + name: string +} + +function exportedCollectionToHoppCollection( + collection: ExportedUserCollectionREST | ExportedUserCollectionGQL, + collectionType: "REST" | "GQL" +): HoppCollection { + if (collectionType == "REST") { + const restCollection = collection as ExportedUserCollectionREST + + return { + id: restCollection.id, + v: 1, + name: restCollection.name, + folders: restCollection.folders.map((folder) => + exportedCollectionToHoppCollection(folder, collectionType) + ), + requests: restCollection.requests.map( + ({ + id, + v, + auth, + body, + endpoint, + headers, + method, + name, + params, + preRequestScript, + testScript, + }) => ({ + id, + v, + auth, + body, + endpoint, + headers, + method, + name, + params, + preRequestScript, + testScript, + }) + ), + } + } else { + const gqlCollection = collection as ExportedUserCollectionGQL + + return { + id: gqlCollection.id, + v: 1, + name: gqlCollection.name, + folders: gqlCollection.folders.map((folder) => + exportedCollectionToHoppCollection(folder, collectionType) + ), + requests: gqlCollection.requests.map( + ({ v, auth, headers, name, id }) => ({ + id, + v, + auth, + headers, + name, + }) + ) as HoppGQLRequest[], + } + } +} + +async function loadUserCollections(collectionType: "REST" | "GQL") { + const res = await exportUserCollectionsToJSON( + undefined, + collectionType == "REST" ? ReqType.Rest : ReqType.Gql + ) + + if (E.isRight(res)) { + const collectionsJSONString = + res.right.exportUserCollectionsToJSON.exportedCollection + const exportedCollections = ( + JSON.parse(collectionsJSONString) as Array< + ExportedUserCollectionGQL | ExportedUserCollectionREST + > + ).map((collection) => ({ v: 1, ...collection })) + + runDispatchWithOutSyncing(() => { + collectionType == "REST" + ? setRESTCollections( + exportedCollections.map( + (collection) => + exportedCollectionToHoppCollection( + collection, + "REST" + ) as HoppCollection + ) + ) + : setGraphqlCollections( + exportedCollections.map( + (collection) => + exportedCollectionToHoppCollection( + collection, + "GQL" + ) as HoppCollection + ) + ) + }) + } +} + +function setupSubscriptions() { + let subs: ReturnType[1][] = [] + + const userCollectionCreatedSub = setupUserCollectionCreatedSubscription() + const userCollectionUpdatedSub = setupUserCollectionUpdatedSubscription() + const userCollectionRemovedSub = setupUserCollectionRemovedSubscription() + const userCollectionMovedSub = setupUserCollectionMovedSubscription() + const userCollectionOrderUpdatedSub = + setupUserCollectionOrderUpdatedSubscription() + const userRequestCreatedSub = setupUserRequestCreatedSubscription() + const userRequestUpdatedSub = setupUserRequestUpdatedSubscription() + const userRequestDeletedSub = setupUserRequestDeletedSubscription() + const userRequestMovedSub = setupUserRequestMovedSubscription() + + subs = [ + userCollectionCreatedSub, + userCollectionUpdatedSub, + userCollectionRemovedSub, + userCollectionMovedSub, + userCollectionOrderUpdatedSub, + userRequestCreatedSub, + userRequestUpdatedSub, + userRequestDeletedSub, + userRequestMovedSub, + ] + + return () => { + subs.forEach((sub) => sub.unsubscribe()) + } +} + +function setupUserCollectionCreatedSubscription() { + const [userCollectionCreated$, userCollectionCreatedSub] = + runUserCollectionCreatedSubscription() + + userCollectionCreated$.subscribe((res) => { + if (E.isRight(res)) { + const collectionType = res.right.userCollectionCreated.type + + const { collectionStore } = getStoreByCollectionType(collectionType) + + const userCollectionBackendID = res.right.userCollectionCreated.id + const parentCollectionID = res.right.userCollectionCreated.parent?.id + + const userCollectionLocalID = getCollectionPathFromCollectionID( + userCollectionBackendID, + collectionStore.value.state + ) + + // collection already exists in store ( this instance created it ) + if (userCollectionLocalID) { + return + } + + const parentCollectionPath = + parentCollectionID && + getCollectionPathFromCollectionID( + parentCollectionID, + collectionStore.value.state + ) + + // only folders will have parent collection id + if (parentCollectionID && parentCollectionPath) { + runDispatchWithOutSyncing(() => { + collectionType == "GQL" + ? addGraphqlFolder( + res.right.userCollectionCreated.title, + parentCollectionPath + ) + : addRESTFolder( + res.right.userCollectionCreated.title, + parentCollectionPath + ) + + const parentCollection = navigateToFolderWithIndexPath( + collectionStore.value.state, + parentCollectionPath + .split("/") + .map((pathIndex) => parseInt(pathIndex)) + ) + + if (parentCollection) { + const folderIndex = parentCollection.folders.length - 1 + + const addedFolder = parentCollection.folders[folderIndex] + addedFolder.id = userCollectionBackendID + } + }) + } else { + // root collections won't have parentCollectionID + runDispatchWithOutSyncing(() => { + collectionType == "GQL" + ? addGraphqlCollection({ + name: res.right.userCollectionCreated.title, + folders: [], + requests: [], + v: 1, + }) + : addRESTCollection({ + name: res.right.userCollectionCreated.title, + folders: [], + requests: [], + v: 1, + }) + + const localIndex = collectionStore.value.state.length - 1 + + const addedCollection = collectionStore.value.state[localIndex] + addedCollection.id = userCollectionBackendID + }) + } + } + }) + + return userCollectionCreatedSub +} + +function setupUserCollectionUpdatedSubscription() { + const [userCollectionUpdated$, userCollectionUpdatedSub] = + runUserCollectionUpdatedSubscription() + + userCollectionUpdated$.subscribe((res) => { + if (E.isRight(res)) { + const collectionType = res.right.userCollectionUpdated.type + + const { collectionStore } = getStoreByCollectionType(collectionType) + + const updatedCollectionBackendID = res.right.userCollectionUpdated.id + const updatedCollectionLocalPath = getCollectionPathFromCollectionID( + updatedCollectionBackendID, + collectionStore.value.state + ) + + const isFolder = + updatedCollectionLocalPath && + updatedCollectionLocalPath.split("/").length > 1 + + // updated collection is a folder + if (isFolder) { + runDispatchWithOutSyncing(() => { + collectionType == "REST" + ? editRESTFolder(updatedCollectionLocalPath, { + name: res.right.userCollectionUpdated.title, + }) + : editGraphqlFolder(updatedCollectionLocalPath, { + name: res.right.userCollectionUpdated.title, + }) + }) + } + + // updated collection is a root collection + if (updatedCollectionLocalPath && !isFolder) { + runDispatchWithOutSyncing(() => { + collectionType == "REST" + ? editRESTCollection(parseInt(updatedCollectionLocalPath), { + name: res.right.userCollectionUpdated.title, + }) + : editGraphqlCollection(parseInt(updatedCollectionLocalPath), { + name: res.right.userCollectionUpdated.title, + }) + }) + } + } + }) + + return userCollectionUpdatedSub +} + +function setupUserCollectionMovedSubscription() { + const [userCollectionMoved$, userCollectionMovedSub] = + runUserCollectionMovedSubscription() + + userCollectionMoved$.subscribe((res) => { + if (E.isRight(res)) { + const movedMetadata = res.right.userCollectionMoved + + const sourcePath = getCollectionPathFromCollectionID( + movedMetadata.id, + restCollectionStore.value.state + ) + + let destinationPath: string | undefined + + if (movedMetadata.parent?.id) { + destinationPath = + getCollectionPathFromCollectionID( + movedMetadata.parent?.id, + restCollectionStore.value.state + ) ?? undefined + } + + sourcePath && + runDispatchWithOutSyncing(() => { + moveRESTFolder(sourcePath, destinationPath ?? null) + }) + } + }) + + return userCollectionMovedSub +} + +function setupUserCollectionRemovedSubscription() { + const [userCollectionRemoved$, userCollectionRemovedSub] = + runUserCollectionRemovedSubscription() + + userCollectionRemoved$.subscribe((res) => { + if (E.isRight(res)) { + const removedCollectionBackendID = res.right.userCollectionRemoved.id + const collectionType = res.right.userCollectionRemoved.type + + const { collectionStore } = getStoreByCollectionType(collectionType) + + const removedCollectionLocalPath = getCollectionPathFromCollectionID( + removedCollectionBackendID, + collectionStore.value.state + ) + + const isFolder = + removedCollectionLocalPath && + removedCollectionLocalPath.split("/").length > 1 + + if (removedCollectionLocalPath && isFolder) { + runDispatchWithOutSyncing(() => { + collectionType == "REST" + ? removeRESTFolder(removedCollectionLocalPath) + : removeGraphqlFolder(removedCollectionLocalPath) + }) + } + + if (removedCollectionLocalPath && !isFolder) { + runDispatchWithOutSyncing(() => { + collectionType == "REST" + ? removeRESTCollection(parseInt(removedCollectionLocalPath)) + : removeGraphqlCollection(parseInt(removedCollectionLocalPath)) + }) + } + } + }) + + return userCollectionRemovedSub +} + +function setupUserCollectionOrderUpdatedSubscription() { + const [userCollectionOrderUpdated$, userCollectionOrderUpdatedSub] = + runUserCollectionOrderUpdatedSubscription() + + userCollectionOrderUpdated$.subscribe((res) => { + if (E.isRight(res)) { + const { userCollection, nextUserCollection } = + res.right.userCollectionOrderUpdated + + const sourceCollectionID = userCollection.id + const destinationCollectionID = nextUserCollection?.id + + const sourcePath = getCollectionPathFromCollectionID( + sourceCollectionID, + restCollectionStore.value.state + ) + + let destinationPath: string | null | undefined + + if (destinationCollectionID) { + destinationPath = getCollectionPathFromCollectionID( + destinationCollectionID, + restCollectionStore.value.state + ) + } + + runDispatchWithOutSyncing(() => { + if (sourcePath) { + updateRESTCollectionOrder(sourcePath, destinationPath ?? null) + } + }) + } + }) + + return userCollectionOrderUpdatedSub +} + +function setupUserRequestCreatedSubscription() { + const [userRequestCreated$, userRequestCreatedSub] = + runUserRequestCreatedSubscription() + + userRequestCreated$.subscribe((res) => { + if (E.isRight(res)) { + const collectionID = res.right.userRequestCreated.collectionID + const request = JSON.parse(res.right.userRequestCreated.request) + const requestID = res.right.userRequestCreated.id + + const requestType = res.right.userRequestCreated.type + + const { collectionStore } = getStoreByCollectionType(requestType) + + const hasAlreadyHappened = getRequestPathFromRequestID( + requestID, + collectionStore.value.state + ) + + if (!!hasAlreadyHappened) { + return + } + + const collectionPath = getCollectionPathFromCollectionID( + collectionID, + collectionStore.value.state + ) + + if (collectionID && collectionPath) { + runDispatchWithOutSyncing(() => { + requestType == "REST" + ? saveRESTRequestAs(collectionPath, request) + : saveGraphqlRequestAs(collectionPath, request) + + const target = navigateToFolderWithIndexPath( + collectionStore.value.state, + collectionPath.split("/").map((index) => parseInt(index)) + ) + + const targetRequest = target?.requests[target?.requests.length - 1] + + if (targetRequest) { + targetRequest.id = requestID + } + }) + } + } + }) + + return userRequestCreatedSub +} + +function setupUserRequestUpdatedSubscription() { + const [userRequestUpdated$, userRequestUpdatedSub] = + runUserRequestUpdatedSubscription() + + userRequestUpdated$.subscribe((res) => { + if (E.isRight(res)) { + const requestType = res.right.userRequestUpdated.type + + const { collectionStore } = getStoreByCollectionType(requestType) + + const requestPath = getRequestPathFromRequestID( + res.right.userRequestUpdated.id, + collectionStore.value.state + ) + + const collectionPath = requestPath?.collectionPath + const requestIndex = requestPath?.requestIndex + + ;(requestIndex || requestIndex == 0) && + collectionPath && + runDispatchWithOutSyncing(() => { + requestType == "REST" + ? editRESTRequest( + collectionPath, + requestIndex, + JSON.parse(res.right.userRequestUpdated.request) + ) + : editGraphqlRequest( + collectionPath, + requestIndex, + JSON.parse(res.right.userRequestUpdated.request) + ) + }) + } + }) + + return userRequestUpdatedSub +} + +function setupUserRequestMovedSubscription() { + const [userRequestMoved$, userRequestMovedSub] = + runUserRequestMovedSubscription() + + userRequestMoved$.subscribe((res) => { + if (E.isRight(res)) { + const { request, nextRequest } = res.right.userRequestMoved + + const { + collectionID: destinationCollectionID, + id: sourceRequestID, + type: requestType, + } = request + + const { collectionStore } = getStoreByCollectionType(requestType) + + const sourceRequestPath = getRequestPathFromRequestID( + sourceRequestID, + collectionStore.value.state + ) + + const destinationCollectionPath = getCollectionPathFromCollectionID( + destinationCollectionID, + collectionStore.value.state + ) + + const destinationRequestIndex = destinationCollectionPath + ? (() => { + const requestsLength = navigateToFolderWithIndexPath( + collectionStore.value.state, + destinationCollectionPath + .split("/") + .map((index) => parseInt(index)) + )?.requests.length + + return requestsLength || requestsLength == 0 + ? requestsLength - 1 + : undefined + })() + : undefined + + // there is no nextRequest, so request is moved + if ( + (destinationRequestIndex || destinationRequestIndex == 0) && + destinationCollectionPath && + sourceRequestPath && + !nextRequest + ) { + runDispatchWithOutSyncing(() => { + requestType == "REST" + ? moveRESTRequest( + sourceRequestPath.collectionPath, + sourceRequestPath.requestIndex, + destinationCollectionPath + ) + : moveGraphqlRequest( + sourceRequestPath.collectionPath, + sourceRequestPath.requestIndex, + destinationCollectionPath + ) + }) + } + + // there is nextRequest, so request is reordered + if ( + (destinationRequestIndex || destinationRequestIndex == 0) && + destinationCollectionPath && + nextRequest && + // we don't have request reordering for graphql yet + requestType == "REST" + ) { + const { collectionID: nextCollectionID, id: nextRequestID } = + nextRequest + + const nextCollectionPath = + getCollectionPathFromCollectionID( + nextCollectionID, + collectionStore.value.state + ) ?? undefined + + const nextRequestIndex = nextCollectionPath + ? getRequestIndex( + nextRequestID, + nextCollectionPath, + collectionStore.value.state + ) + : undefined + + nextRequestIndex && + nextCollectionPath && + sourceRequestPath && + runDispatchWithOutSyncing(() => { + updateRESTRequestOrder( + sourceRequestPath?.requestIndex, + nextRequestIndex, + nextCollectionPath + ) + }) + } + } + }) + + return userRequestMovedSub +} + +function setupUserRequestDeletedSubscription() { + const [userRequestDeleted$, userRequestDeletedSub] = + runUserRequestDeletedSubscription() + + userRequestDeleted$.subscribe((res) => { + if (E.isRight(res)) { + const requestType = res.right.userRequestDeleted.type + + const { collectionStore } = getStoreByCollectionType(requestType) + + const deletedRequestPath = getRequestPathFromRequestID( + res.right.userRequestDeleted.id, + collectionStore.value.state + ) + + ;(deletedRequestPath?.requestIndex || + deletedRequestPath?.requestIndex == 0) && + deletedRequestPath.collectionPath && + runDispatchWithOutSyncing(() => { + requestType == "REST" + ? removeRESTRequest( + deletedRequestPath.collectionPath, + deletedRequestPath.requestIndex + ) + : removeGraphqlRequest( + deletedRequestPath.collectionPath, + deletedRequestPath.requestIndex + ) + }) + } + }) + + return userRequestDeletedSub +} + +export const def: CollectionsPlatformDef = { + initCollectionsSync, +} + +function getCollectionPathFromCollectionID( + collectionID: string, + collections: HoppCollection[], + parentPath?: string +): string | null { + for (const collectionIndex in collections) { + if (collections[collectionIndex].id == collectionID) { + return parentPath + ? `${parentPath}/${collectionIndex}` + : `${collectionIndex}` + } else { + const collectionPath = getCollectionPathFromCollectionID( + collectionID, + collections[collectionIndex].folders, + parentPath ? `${parentPath}/${collectionIndex}` : `${collectionIndex}` + ) + + if (collectionPath) return collectionPath + } + } + + return null +} + +function getRequestPathFromRequestID( + requestID: string, + collections: HoppCollection[], + parentPath?: string +): { collectionPath: string; requestIndex: number } | null { + for (const collectionIndex in collections) { + const requestIndex = collections[collectionIndex].requests.findIndex( + (request) => request.id == requestID + ) + + if (requestIndex != -1) { + return { + collectionPath: parentPath + ? `${parentPath}/${collectionIndex}` + : `${collectionIndex}`, + requestIndex, + } + } else { + const requestPath = getRequestPathFromRequestID( + requestID, + collections[collectionIndex].folders, + parentPath ? `${parentPath}/${collectionIndex}` : `${collectionIndex}` + ) + + if (requestPath) return requestPath + } + } + + return null +} + +function getRequestIndex( + requestID: string, + parentCollectionPath: string, + collections: HoppCollection[] +) { + const collection = navigateToFolderWithIndexPath( + collections, + parentCollectionPath?.split("/").map((index) => parseInt(index)) + ) + + const requestIndex = collection?.requests.findIndex( + (request) => request.id == requestID + ) + + return requestIndex +} diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/collections/collections.sync.ts b/packages/hoppscotch-selfhost-desktop/src/platform/collections/collections.sync.ts new file mode 100644 index 000000000..218cb2251 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/collections/collections.sync.ts @@ -0,0 +1,543 @@ +import { + graphqlCollectionStore, + navigateToFolderWithIndexPath, + removeDuplicateRESTCollectionOrFolder, + restCollectionStore, +} from "@hoppscotch/common/newstore/collections" +import { + getSettingSubject, + settingsStore, +} from "@hoppscotch/common/newstore/settings" + +import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data" + +import { getSyncInitFunction } from "../../lib/sync" + +import { StoreSyncDefinitionOf } from "../../lib/sync" +import { createMapper } from "../../lib/sync/mapper" +import { + createRESTChildUserCollection, + createRESTRootUserCollection, + createRESTUserRequest, + deleteUserCollection, + deleteUserRequest, + editUserRequest, + moveUserCollection, + moveUserRequest, + renameUserCollection, + updateUserCollectionOrder, +} from "./collections.api" + +import * as E from "fp-ts/Either" + +// restCollectionsMapper uses the collectionPath as the local identifier +export const restCollectionsMapper = createMapper() + +// restRequestsMapper uses the collectionPath/requestIndex as the local identifier +export const restRequestsMapper = createMapper() + +// temp implementation untill the backend implements an endpoint that accepts an entire collection +// TODO: use importCollectionsJSON to do this +const recursivelySyncCollections = async ( + collection: HoppCollection, + collectionPath: string, + parentUserCollectionID?: string +) => { + let parentCollectionID = parentUserCollectionID + + // if parentUserCollectionID does not exist, create the collection as a root collection + if (!parentUserCollectionID) { + const res = await createRESTRootUserCollection(collection.name) + + if (E.isRight(res)) { + parentCollectionID = res.right.createRESTRootUserCollection.id + + collection.id = parentCollectionID + removeDuplicateRESTCollectionOrFolder(parentCollectionID, collectionPath) + } else { + parentCollectionID = undefined + } + } else { + // if parentUserCollectionID exists, create the collection as a child collection + const res = await createRESTChildUserCollection( + collection.name, + parentUserCollectionID + ) + + if (E.isRight(res)) { + const childCollectionId = res.right.createRESTChildUserCollection.id + + collection.id = childCollectionId + + removeDuplicateRESTCollectionOrFolder( + childCollectionId, + `${collectionPath}` + ) + } + } + + // create the requests + if (parentCollectionID) { + collection.requests.forEach(async (request) => { + const res = + parentCollectionID && + (await createRESTUserRequest( + request.name, + JSON.stringify(request), + parentCollectionID + )) + + if (res && E.isRight(res)) { + const requestId = res.right.createRESTUserRequest.id + + request.id = requestId + } + }) + } + + // create the folders aka child collections + if (parentCollectionID) + collection.folders.forEach(async (folder, index) => { + recursivelySyncCollections( + folder, + `${collectionPath}/${index}`, + parentCollectionID + ) + }) +} + +// TODO: generalize this +// TODO: ask backend to send enough info on the subscription to not need this +export const collectionReorderOrMovingOperations: { + sourceCollectionID: string + destinationCollectionID?: string + reorderOperation: { + fromPath: string + toPath?: string + } +}[] = [] + +type OperationStatus = "pending" | "completed" + +type OperationCollectionRemoved = { + type: "COLLECTION_REMOVED" + collectionBackendID: string + status: OperationStatus +} + +export const restCollectionsOperations: Array = [] + +export const storeSyncDefinition: StoreSyncDefinitionOf< + typeof restCollectionStore +> = { + appendCollections({ entries }) { + let indexStart = restCollectionStore.value.state.length - entries.length + + entries.forEach((collection) => { + recursivelySyncCollections(collection, `${indexStart}`) + indexStart++ + }) + }, + async addCollection({ collection }) { + const lastCreatedCollectionIndex = + restCollectionStore.value.state.length - 1 + + recursivelySyncCollections(collection, `${lastCreatedCollectionIndex}`) + }, + async removeCollection({ collectionID }) { + if (collectionID) { + await deleteUserCollection(collectionID) + } + }, + editCollection({ partialCollection: collection, collectionIndex }) { + const collectionID = navigateToFolderWithIndexPath( + restCollectionStore.value.state, + [collectionIndex] + )?.id + + if (collectionID && collection.name) { + renameUserCollection(collectionID, collection.name) + } + }, + async addFolder({ name, path }) { + const parentCollection = navigateToFolderWithIndexPath( + restCollectionStore.value.state, + path.split("/").map((index) => parseInt(index)) + ) + + const parentCollectionBackendID = parentCollection?.id + + if (parentCollectionBackendID) { + const foldersLength = parentCollection.folders.length + + const res = await createRESTChildUserCollection( + name, + parentCollectionBackendID + ) + + if (E.isRight(res)) { + const { id } = res.right.createRESTChildUserCollection + + if (foldersLength) { + parentCollection.folders[foldersLength - 1].id = id + removeDuplicateRESTCollectionOrFolder( + id, + `${path}/${foldersLength - 1}` + ) + } + } + } + }, + editFolder({ folder, path }) { + const folderID = navigateToFolderWithIndexPath( + restCollectionStore.value.state, + path.split("/").map((index) => parseInt(index)) + )?.id + + const folderName = folder.name + + if (folderID && folderName) { + renameUserCollection(folderID, folderName) + } + }, + async removeFolder({ folderID }) { + if (folderID) { + await deleteUserCollection(folderID) + } + }, + async moveFolder({ destinationPath, path }) { + const { newSourcePath, newDestinationPath } = getPathsAfterMoving( + path, + destinationPath ?? undefined + ) + + if (newSourcePath) { + const sourceCollectionID = navigateToFolderWithIndexPath( + restCollectionStore.value.state, + newSourcePath.split("/").map((index) => parseInt(index)) + )?.id + + const destinationCollectionID = destinationPath + ? newDestinationPath && + navigateToFolderWithIndexPath( + restCollectionStore.value.state, + newDestinationPath.split("/").map((index) => parseInt(index)) + )?.id + : undefined + + if (sourceCollectionID) { + await moveUserCollection(sourceCollectionID, destinationCollectionID) + } + } + }, + editRequest({ path, requestIndex, requestNew }) { + const request = navigateToFolderWithIndexPath( + restCollectionStore.value.state, + path.split("/").map((index) => parseInt(index)) + )?.requests[requestIndex] + + const requestBackendID = request?.id + + if (requestBackendID) { + editUserRequest( + requestBackendID, + (requestNew as HoppRESTRequest).name, + JSON.stringify(requestNew) + ) + } + }, + async saveRequestAs({ path, request }) { + const folder = navigateToFolderWithIndexPath( + restCollectionStore.value.state, + path.split("/").map((index) => parseInt(index)) + ) + + const parentCollectionBackendID = folder?.id + + if (parentCollectionBackendID) { + const newRequest = folder.requests[folder.requests.length - 1] + + const res = await createRESTUserRequest( + (request as HoppRESTRequest).name, + JSON.stringify(request), + parentCollectionBackendID + ) + + if (E.isRight(res)) { + const { id } = res.right.createRESTUserRequest + + newRequest.id = id + removeDuplicateRESTCollectionOrFolder( + id, + `${path}/${folder.requests.length - 1}`, + "request" + ) + } + } + }, + async removeRequest({ requestID }) { + if (requestID) { + await deleteUserRequest(requestID) + } + }, + moveRequest({ destinationPath, path, requestIndex }) { + moveOrReorderRequests(requestIndex, path, destinationPath) + }, + updateRequestOrder({ + destinationCollectionPath, + destinationRequestIndex, + requestIndex, + }) { + /** + * currently the FE implementation only supports reordering requests between the same collection, + * so destinationCollectionPath and sourceCollectionPath will be same + */ + moveOrReorderRequests( + requestIndex, + destinationCollectionPath, + destinationCollectionPath, + destinationRequestIndex ?? undefined + ) + }, + async updateCollectionOrder({ + collectionIndex: collectionPath, + destinationCollectionIndex: destinationCollectionPath, + }) { + const collections = restCollectionStore.value.state + + const sourcePathIndexes = getParentPathIndexesFromPath(collectionPath) + const sourceCollectionIndex = getCollectionIndexFromPath(collectionPath) + + const destinationCollectionIndex = !!destinationCollectionPath + ? getCollectionIndexFromPath(destinationCollectionPath) + : undefined + + let updatedCollectionIndexs: + | [newSourceIndex: number, newDestinationIndex: number | undefined] + | undefined + + if ( + (sourceCollectionIndex || sourceCollectionIndex == 0) && + (destinationCollectionIndex || destinationCollectionIndex == 0) + ) { + updatedCollectionIndexs = getIndexesAfterReorder( + sourceCollectionIndex, + destinationCollectionIndex + ) + } else if (sourceCollectionIndex || sourceCollectionIndex == 0) { + if (sourcePathIndexes.length == 0) { + // we're reordering root collections + updatedCollectionIndexs = [collections.length - 1, undefined] + } else { + const sourceCollection = navigateToFolderWithIndexPath(collections, [ + ...sourcePathIndexes, + ]) + + if (sourceCollection && sourceCollection.folders.length > 0) { + updatedCollectionIndexs = [ + sourceCollection.folders.length - 1, + undefined, + ] + } + } + } + + const sourceCollectionID = + updatedCollectionIndexs && + navigateToFolderWithIndexPath(collections, [ + ...sourcePathIndexes, + updatedCollectionIndexs[0], + ])?.id + + const destinationCollectionID = + updatedCollectionIndexs && + (updatedCollectionIndexs[1] || updatedCollectionIndexs[1] == 0) + ? navigateToFolderWithIndexPath(collections, [ + ...sourcePathIndexes, + updatedCollectionIndexs[1], + ])?.id + : undefined + + if (sourceCollectionID) { + await updateUserCollectionOrder( + sourceCollectionID, + destinationCollectionID + ) + } + }, +} + +export const collectionsSyncer = getSyncInitFunction( + restCollectionStore, + storeSyncDefinition, + () => settingsStore.value.syncCollections, + getSettingSubject("syncCollections") +) + +export async function moveOrReorderRequests( + requestIndex: number, + path: string, + destinationPath: string, + nextRequestIndex?: number, + requestType: "REST" | "GQL" = "REST" +) { + const { collectionStore } = getStoreByCollectionType(requestType) + + const sourceCollectionBackendID = navigateToFolderWithIndexPath( + collectionStore.value.state, + path.split("/").map((index) => parseInt(index)) + )?.id + + const destinationCollection = navigateToFolderWithIndexPath( + collectionStore.value.state, + destinationPath.split("/").map((index) => parseInt(index)) + ) + + const destinationCollectionBackendID = destinationCollection?.id + + let requestBackendID: string | undefined + + let nextRequestBackendID: string | undefined + + // we only need this for reordering requests, not for moving requests + if (nextRequestIndex) { + // reordering + const [newRequestIndex, newDestinationIndex] = getIndexesAfterReorder( + requestIndex, + nextRequestIndex + ) + + requestBackendID = + destinationCollection?.requests[newRequestIndex]?.id ?? undefined + + nextRequestBackendID = + destinationCollection?.requests[newDestinationIndex]?.id ?? undefined + } else { + // moving + const requests = destinationCollection?.requests + requestBackendID = + requests && requests.length > 0 + ? requests[requests.length - 1]?.id + : undefined + } + + if ( + sourceCollectionBackendID && + destinationCollectionBackendID && + requestBackendID + ) { + await moveUserRequest( + sourceCollectionBackendID, + destinationCollectionBackendID, + requestBackendID, + nextRequestBackendID + ) + } +} + +function getParentPathIndexesFromPath(path: string) { + const indexes = path.split("/") + indexes.pop() + return indexes.map((index) => parseInt(index)) +} + +export function getCollectionIndexFromPath(collectionPath: string) { + const sourceCollectionIndexString = collectionPath.split("/").pop() + const sourceCollectionIndex = sourceCollectionIndexString + ? parseInt(sourceCollectionIndexString) + : undefined + + return sourceCollectionIndex +} + +/** + * the sync function is called after the reordering has happened on the store + * because of this we need to find the new source and destination indexes after the reordering + */ +function getIndexesAfterReorder( + oldSourceIndex: number, + oldDestinationIndex: number +): [newSourceIndex: number, newDestinationIndex: number] { + // Source Becomes Destination -1 + // Destination Remains Same + if (oldSourceIndex < oldDestinationIndex) { + return [oldDestinationIndex - 1, oldDestinationIndex] + } + + // Source Becomes The Destination + // Destintion Becomes Source + 1 + if (oldSourceIndex > oldDestinationIndex) { + return [oldDestinationIndex, oldDestinationIndex + 1] + } + + throw new Error("Source and Destination are the same") +} + +/** + * the sync function is called after moving a folder has happened on the store, + * because of this the source index given to the sync function is not the live one + * we need to find the new source index after the moving + */ +function getPathsAfterMoving(sourcePath: string, destinationPath?: string) { + if (!destinationPath) { + return { + newSourcePath: `${restCollectionStore.value.state.length - 1}`, + newDestinationPath: destinationPath, + } + } + + const sourceParentPath = getParentPathFromPath(sourcePath) + const destinationParentPath = getParentPathFromPath(destinationPath) + + const isSameParentPath = sourceParentPath === destinationParentPath + + let newDestinationPath: string + + if (isSameParentPath) { + const sourceIndex = getCollectionIndexFromPath(sourcePath) + const destinationIndex = getCollectionIndexFromPath(destinationPath) + + if ( + (sourceIndex || sourceIndex == 0) && + (destinationIndex || destinationIndex == 0) && + sourceIndex < destinationIndex + ) { + newDestinationPath = destinationParentPath + ? `${destinationParentPath}/${destinationIndex - 1}` + : `${destinationIndex - 1}` + } else { + newDestinationPath = destinationPath + } + } else { + newDestinationPath = destinationPath + } + + const destinationFolder = navigateToFolderWithIndexPath( + restCollectionStore.value.state, + newDestinationPath.split("/").map((index) => parseInt(index)) + ) + + const newSourcePath = destinationFolder + ? `${newDestinationPath}/${destinationFolder?.folders.length - 1}` + : undefined + + return { + newSourcePath, + newDestinationPath, + } +} + +function getParentPathFromPath(path: string | undefined) { + const indexes = path ? path.split("/") : [] + indexes.pop() + + return indexes.join("/") +} + +export function getStoreByCollectionType(type: "GQL" | "REST") { + const isGQL = type == "GQL" + + const collectionStore = isGQL ? graphqlCollectionStore : restCollectionStore + + return { collectionStore } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/collections/gqlCollections.sync.ts b/packages/hoppscotch-selfhost-desktop/src/platform/collections/gqlCollections.sync.ts new file mode 100644 index 000000000..d65970dcc --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/collections/gqlCollections.sync.ts @@ -0,0 +1,269 @@ +import { + graphqlCollectionStore, + navigateToFolderWithIndexPath, + removeDuplicateGraphqlCollectionOrFolder, +} from "@hoppscotch/common/newstore/collections" +import { + getSettingSubject, + settingsStore, +} from "@hoppscotch/common/newstore/settings" + +import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data" + +import { getSyncInitFunction } from "../../lib/sync" + +import { StoreSyncDefinitionOf } from "../../lib/sync" +import { createMapper } from "../../lib/sync/mapper" +import { + createGQLChildUserCollection, + createGQLRootUserCollection, + createGQLUserRequest, + deleteUserCollection, + deleteUserRequest, + editGQLUserRequest, + renameUserCollection, +} from "./collections.api" + +import * as E from "fp-ts/Either" +import { moveOrReorderRequests } from "./collections.sync" + +// gqlCollectionsMapper uses the collectionPath as the local identifier +export const gqlCollectionsMapper = createMapper() + +// gqlRequestsMapper uses the collectionPath/requestIndex as the local identifier +export const gqlRequestsMapper = createMapper() + +// temp implementation untill the backend implements an endpoint that accepts an entire collection +// TODO: use importCollectionsJSON to do this +const recursivelySyncCollections = async ( + collection: HoppCollection, + collectionPath: string, + parentUserCollectionID?: string +) => { + let parentCollectionID = parentUserCollectionID + + // if parentUserCollectionID does not exist, create the collection as a root collection + if (!parentUserCollectionID) { + const res = await createGQLRootUserCollection(collection.name) + + if (E.isRight(res)) { + parentCollectionID = res.right.createGQLRootUserCollection.id + + collection.id = parentCollectionID + removeDuplicateGraphqlCollectionOrFolder( + parentCollectionID, + collectionPath + ) + } else { + parentCollectionID = undefined + } + } else { + // if parentUserCollectionID exists, create the collection as a child collection + const res = await createGQLChildUserCollection( + collection.name, + parentUserCollectionID + ) + + if (E.isRight(res)) { + const childCollectionId = res.right.createGQLChildUserCollection.id + + collection.id = childCollectionId + + removeDuplicateGraphqlCollectionOrFolder( + childCollectionId, + `${collectionPath}` + ) + } + } + + // create the requests + if (parentCollectionID) { + collection.requests.forEach(async (request) => { + const res = + parentCollectionID && + (await createGQLUserRequest( + request.name, + JSON.stringify(request), + parentCollectionID + )) + + if (res && E.isRight(res)) { + const requestId = res.right.createGQLUserRequest.id + + request.id = requestId + } + }) + } + + // create the folders aka child collections + if (parentCollectionID) + collection.folders.forEach(async (folder, index) => { + recursivelySyncCollections( + folder, + `${collectionPath}/${index}`, + parentCollectionID + ) + }) +} + +// TODO: generalize this +// TODO: ask backend to send enough info on the subscription to not need this +export const collectionReorderOrMovingOperations: { + sourceCollectionID: string + destinationCollectionID?: string + reorderOperation: { + fromPath: string + toPath?: string + } +}[] = [] + +type OperationStatus = "pending" | "completed" + +type OperationCollectionRemoved = { + type: "COLLECTION_REMOVED" + collectionBackendID: string + status: OperationStatus +} + +export const gqlCollectionsOperations: Array = [] + +export const storeSyncDefinition: StoreSyncDefinitionOf< + typeof graphqlCollectionStore +> = { + appendCollections({ entries }) { + let indexStart = graphqlCollectionStore.value.state.length - entries.length + + entries.forEach((collection) => { + recursivelySyncCollections(collection, `${indexStart}`) + indexStart++ + }) + }, + async addCollection({ collection }) { + const lastCreatedCollectionIndex = + graphqlCollectionStore.value.state.length - 1 + + await recursivelySyncCollections( + collection, + `${lastCreatedCollectionIndex}` + ) + }, + async removeCollection({ collectionID }) { + if (collectionID) { + await deleteUserCollection(collectionID) + } + }, + editCollection({ collection, collectionIndex }) { + const collectionID = navigateToFolderWithIndexPath( + graphqlCollectionStore.value.state, + [collectionIndex] + )?.id + + if (collectionID && collection.name) { + renameUserCollection(collectionID, collection.name) + } + }, + async addFolder({ name, path }) { + const parentCollection = navigateToFolderWithIndexPath( + graphqlCollectionStore.value.state, + path.split("/").map((index) => parseInt(index)) + ) + + const parentCollectionBackendID = parentCollection?.id + + if (parentCollectionBackendID) { + const foldersLength = parentCollection.folders.length + + const res = await createGQLChildUserCollection( + name, + parentCollectionBackendID + ) + + if (E.isRight(res)) { + const { id } = res.right.createGQLChildUserCollection + + if (foldersLength) { + parentCollection.folders[foldersLength - 1].id = id + removeDuplicateGraphqlCollectionOrFolder( + id, + `${path}/${foldersLength - 1}` + ) + } + } + } + }, + editFolder({ folder, path }) { + const folderBackendId = navigateToFolderWithIndexPath( + graphqlCollectionStore.value.state, + path.split("/").map((index) => parseInt(index)) + )?.id + + if (folderBackendId && folder.name) { + renameUserCollection(folderBackendId, folder.name) + } + }, + async removeFolder({ folderID }) { + if (folderID) { + await deleteUserCollection(folderID) + } + }, + editRequest({ path, requestIndex, requestNew }) { + const request = navigateToFolderWithIndexPath( + graphqlCollectionStore.value.state, + path.split("/").map((index) => parseInt(index)) + )?.requests[requestIndex] + + const requestBackendID = request?.id + + if (requestBackendID) { + editGQLUserRequest( + requestBackendID, + (requestNew as HoppRESTRequest).name, + JSON.stringify(requestNew) + ) + } + }, + async saveRequestAs({ path, request }) { + const folder = navigateToFolderWithIndexPath( + graphqlCollectionStore.value.state, + path.split("/").map((index) => parseInt(index)) + ) + + const parentCollectionBackendID = folder?.id + + if (parentCollectionBackendID) { + const newRequest = folder.requests[folder.requests.length - 1] + + const res = await createGQLUserRequest( + (request as HoppRESTRequest).name, + JSON.stringify(request), + parentCollectionBackendID + ) + + if (E.isRight(res)) { + const { id } = res.right.createGQLUserRequest + + newRequest.id = id + removeDuplicateGraphqlCollectionOrFolder( + id, + `${path}/${folder.requests.length - 1}`, + "request" + ) + } + } + }, + async removeRequest({ requestID }) { + if (requestID) { + await deleteUserRequest(requestID) + } + }, + moveRequest({ destinationPath, path, requestIndex }) { + moveOrReorderRequests(requestIndex, path, destinationPath, undefined, "GQL") + }, +} + +export const gqlCollectionsSyncer = getSyncInitFunction( + graphqlCollectionStore, + storeSyncDefinition, + () => settingsStore.value.syncCollections, + getSettingSubject("syncCollections") +) diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/environments/environments.api.ts b/packages/hoppscotch-selfhost-desktop/src/platform/environments/environments.api.ts new file mode 100644 index 000000000..a6ed2530f --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/environments/environments.api.ts @@ -0,0 +1,117 @@ +import { + runMutation, + runGQLQuery, + runGQLSubscription, +} from "@hoppscotch/common/helpers/backend/GQLClient" + +import { + CreateUserEnvironmentDocument, + CreateUserEnvironmentMutation, + CreateUserEnvironmentMutationVariables, + UpdateUserEnvironmentMutation, + UpdateUserEnvironmentMutationVariables, + UpdateUserEnvironmentDocument, + DeleteUserEnvironmentMutation, + DeleteUserEnvironmentMutationVariables, + DeleteUserEnvironmentDocument, + ClearGlobalEnvironmentsMutation, + ClearGlobalEnvironmentsMutationVariables, + ClearGlobalEnvironmentsDocument, + CreateUserGlobalEnvironmentMutation, + CreateUserGlobalEnvironmentMutationVariables, + CreateUserGlobalEnvironmentDocument, + GetGlobalEnvironmentsDocument, + GetGlobalEnvironmentsQueryVariables, + GetGlobalEnvironmentsQuery, + GetUserEnvironmentsDocument, + UserEnvironmentCreatedDocument, + UserEnvironmentUpdatedDocument, + UserEnvironmentDeletedDocument, +} from "./../../api/generated/graphql" + +import { Environment } from "@hoppscotch/data" + +export const createUserEnvironment = (name: string, variables: string) => + runMutation< + CreateUserEnvironmentMutation, + CreateUserEnvironmentMutationVariables, + "" + >(CreateUserEnvironmentDocument, { + name, + variables, + })() + +export const updateUserEnvironment = ( + id: string, + { name, variables }: Environment +) => + runMutation< + UpdateUserEnvironmentMutation, + UpdateUserEnvironmentMutationVariables, + "" + >(UpdateUserEnvironmentDocument, { + id, + name, + variables: JSON.stringify(variables), + }) + +export const deleteUserEnvironment = (id: string) => + runMutation< + DeleteUserEnvironmentMutation, + DeleteUserEnvironmentMutationVariables, + "" + >(DeleteUserEnvironmentDocument, { + id, + }) + +export const clearGlobalEnvironmentVariables = (id: string) => + runMutation< + ClearGlobalEnvironmentsMutation, + ClearGlobalEnvironmentsMutationVariables, + "" + >(ClearGlobalEnvironmentsDocument, { + id, + })() + +export const getUserEnvironments = () => + runGQLQuery({ + query: GetUserEnvironmentsDocument, + variables: {}, + }) + +export const getGlobalEnvironments = () => + runGQLQuery< + GetGlobalEnvironmentsQuery, + GetGlobalEnvironmentsQueryVariables, + "user_environment/user_env_does_not_exists" + >({ + query: GetGlobalEnvironmentsDocument, + variables: {}, + }) + +export const createUserGlobalEnvironment = (variables: string) => + runMutation< + CreateUserGlobalEnvironmentMutation, + CreateUserGlobalEnvironmentMutationVariables, + "" + >(CreateUserGlobalEnvironmentDocument, { + variables, + })() + +export const runUserEnvironmentCreatedSubscription = () => + runGQLSubscription({ + query: UserEnvironmentCreatedDocument, + variables: {}, + }) + +export const runUserEnvironmentUpdatedSubscription = () => + runGQLSubscription({ + query: UserEnvironmentUpdatedDocument, + variables: {}, + }) + +export const runUserEnvironmentDeletedSubscription = () => + runGQLSubscription({ + query: UserEnvironmentDeletedDocument, + variables: {}, + }) diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/environments/environments.platform.ts b/packages/hoppscotch-selfhost-desktop/src/platform/environments/environments.platform.ts new file mode 100644 index 000000000..8d5042228 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/environments/environments.platform.ts @@ -0,0 +1,200 @@ +import { authEvents$, def as platformAuth } from "@platform/auth" +import { + createEnvironment, + deleteEnvironment, + environmentsStore, + getLocalIndexByEnvironmentID, + replaceEnvironments, + setGlobalEnvID, + setGlobalEnvVariables, + updateEnvironment, +} from "@hoppscotch/common/newstore/environments" + +import { EnvironmentsPlatformDef } from "@hoppscotch/common/src/platform/environments" +import { runGQLSubscription } from "@hoppscotch/common/helpers/backend/GQLClient" + +import { environnmentsSyncer } from "@platform/environments/environments.sync" + +import * as E from "fp-ts/Either" +import { runDispatchWithOutSyncing } from "@lib/sync" +import { + createUserGlobalEnvironment, + getGlobalEnvironments, + getUserEnvironments, + runUserEnvironmentCreatedSubscription, + runUserEnvironmentDeletedSubscription, + runUserEnvironmentUpdatedSubscription, +} from "@platform/environments/environments.api" + +export function initEnvironmentsSync() { + const currentUser$ = platformAuth.getCurrentUserStream() + + environnmentsSyncer.startStoreSync() + environnmentsSyncer.setupSubscriptions(setupSubscriptions) + + currentUser$.subscribe(async (user) => { + if (user) { + await loadAllEnvironments() + } + }) + + authEvents$.subscribe((event) => { + if (event.event == "login" || event.event == "token_refresh") { + environnmentsSyncer.startListeningToSubscriptions() + } + + if (event.event == "logout") { + environnmentsSyncer.stopListeningToSubscriptions() + } + }) +} + +export const def: EnvironmentsPlatformDef = { + initEnvironmentsSync, +} + +function setupSubscriptions() { + let subs: ReturnType[1][] = [] + + const userEnvironmentCreatedSub = setupUserEnvironmentCreatedSubscription() + const userEnvironmentUpdatedSub = setupUserEnvironmentUpdatedSubscription() + const userEnvironmentDeletedSub = setupUserEnvironmentDeletedSubscription() + + subs = [ + userEnvironmentCreatedSub, + userEnvironmentUpdatedSub, + userEnvironmentDeletedSub, + ] + + return () => { + subs.forEach((sub) => sub.unsubscribe()) + } +} + +async function loadUserEnvironments() { + const res = await getUserEnvironments() + + if (E.isRight(res)) { + const environments = res.right.me.environments + + if (environments.length > 0) { + runDispatchWithOutSyncing(() => { + replaceEnvironments( + environments.map(({ id, variables, name }) => ({ + id, + name, + variables: JSON.parse(variables), + })) + ) + }) + } + } +} + +async function loadGlobalEnvironments() { + const res = await getGlobalEnvironments() + + if (E.isRight(res)) { + const globalEnv = res.right.me.globalEnvironments + + if (globalEnv) { + runDispatchWithOutSyncing(() => { + setGlobalEnvVariables(JSON.parse(globalEnv.variables)) + setGlobalEnvID(globalEnv.id) + }) + } + } else if (res.left.error == "user_environment/user_env_does_not_exists") { + const res = await createUserGlobalEnvironment(JSON.stringify([])) + + if (E.isRight(res)) { + const backendId = res.right.createUserGlobalEnvironment.id + setGlobalEnvID(backendId) + } + } +} + +async function loadAllEnvironments() { + await loadUserEnvironments() + await loadGlobalEnvironments() +} + +function setupUserEnvironmentCreatedSubscription() { + const [userEnvironmentCreated$, userEnvironmentCreatedSub] = + runUserEnvironmentCreatedSubscription() + + userEnvironmentCreated$.subscribe((res) => { + if (E.isRight(res)) { + const { name, variables, id } = res.right.userEnvironmentCreated + + const isAlreadyExisting = environmentsStore.value.environments.some( + (env) => env.id == id + ) + + if (name && !isAlreadyExisting) { + runDispatchWithOutSyncing(() => { + createEnvironment(name, JSON.parse(variables), id) + }) + } + } + }) + + return userEnvironmentCreatedSub +} + +function setupUserEnvironmentUpdatedSubscription() { + const [userEnvironmentUpdated$, userEnvironmentUpdatedSub] = + runUserEnvironmentUpdatedSubscription() + + userEnvironmentUpdated$.subscribe((res) => { + if (E.isRight(res)) { + const { name, variables, id, isGlobal } = res.right.userEnvironmentUpdated + + // handle the case for global environments + if (isGlobal) { + runDispatchWithOutSyncing(() => { + setGlobalEnvVariables(JSON.parse(variables)) + }) + } else { + // handle the case for normal environments + + const localIndex = environmentsStore.value.environments.findIndex( + (env) => env.id == id + ) + + if ((localIndex || localIndex == 0) && name) { + runDispatchWithOutSyncing(() => { + updateEnvironment(localIndex, { + id, + name, + variables: JSON.parse(variables), + }) + }) + } + } + } + }) + + return userEnvironmentUpdatedSub +} + +function setupUserEnvironmentDeletedSubscription() { + const [userEnvironmentDeleted$, userEnvironmentDeletedSub] = + runUserEnvironmentDeletedSubscription() + + userEnvironmentDeleted$.subscribe((res) => { + if (E.isRight(res)) { + const { id } = res.right.userEnvironmentDeleted + + // TODO: move getLocalIndexByID to a getter in the environmentsStore + const localIndex = getLocalIndexByEnvironmentID(id) + + if (localIndex || localIndex === 0) { + runDispatchWithOutSyncing(() => { + deleteEnvironment(localIndex) + }) + } + } + }) + + return userEnvironmentDeletedSub +} diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/environments/environments.sync.ts b/packages/hoppscotch-selfhost-desktop/src/platform/environments/environments.sync.ts new file mode 100644 index 000000000..fb4094204 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/environments/environments.sync.ts @@ -0,0 +1,117 @@ +import { + environmentsStore, + getGlobalVariableID, + removeDuplicateEntry, +} from "@hoppscotch/common/newstore/environments" +import { + getSettingSubject, + settingsStore, +} from "@hoppscotch/common/newstore/settings" + +import { getSyncInitFunction } from "../../lib/sync" + +import * as E from "fp-ts/Either" + +import { StoreSyncDefinitionOf } from "../../lib/sync" +import { createMapper } from "../../lib/sync/mapper" +import { + clearGlobalEnvironmentVariables, + createUserEnvironment, + deleteUserEnvironment, + updateUserEnvironment, +} from "./environments.api" + +export const environmentsMapper = createMapper() +export const globalEnvironmentMapper = createMapper() + +export const storeSyncDefinition: StoreSyncDefinitionOf< + typeof environmentsStore +> = { + async createEnvironment({ name, variables }) { + const lastCreatedEnvIndex = environmentsStore.value.environments.length - 1 + + const res = await createUserEnvironment(name, JSON.stringify(variables)) + + if (E.isRight(res)) { + const id = res.right.createUserEnvironment.id + environmentsStore.value.environments[lastCreatedEnvIndex].id = id + removeDuplicateEntry(id) + } + }, + async appendEnvironments({ envs }) { + const appendListLength = envs.length + let appendStart = + environmentsStore.value.environments.length - appendListLength - 1 + + envs.forEach((env) => { + const envId = ++appendStart + + ;(async function () { + const res = await createUserEnvironment( + env.name, + JSON.stringify(env.variables) + ) + + if (E.isRight(res)) { + const id = res.right.createUserEnvironment.id + environmentsStore.value.environments[envId].id = id + + removeDuplicateEntry(id) + } + })() + }) + }, + async duplicateEnvironment({ envIndex }) { + const environmentToDuplicate = environmentsStore.value.environments.find( + (_, index) => index === envIndex + ) + + const lastCreatedEnvIndex = environmentsStore.value.environments.length - 1 + + if (environmentToDuplicate) { + const res = await createUserEnvironment( + `${environmentToDuplicate?.name} - Duplicate`, + JSON.stringify(environmentToDuplicate?.variables) + ) + + if (E.isRight(res)) { + const id = res.right.createUserEnvironment.id + environmentsStore.value.environments[lastCreatedEnvIndex].id = id + + removeDuplicateEntry(id) + } + } + }, + updateEnvironment({ envIndex, updatedEnv }) { + const backendId = environmentsStore.value.environments[envIndex].id + + if (backendId) { + updateUserEnvironment(backendId, updatedEnv)() + } + }, + async deleteEnvironment({ envID }) { + if (envID) { + await deleteUserEnvironment(envID)() + } + }, + setGlobalVariables({ entries }) { + const backendId = getGlobalVariableID() + if (backendId) { + updateUserEnvironment(backendId, { name: "", variables: entries })() + } + }, + clearGlobalVariables() { + const backendId = getGlobalVariableID() + + if (backendId) { + clearGlobalEnvironmentVariables(backendId) + } + }, +} + +export const environnmentsSyncer = getSyncInitFunction( + environmentsStore, + storeSyncDefinition, + () => settingsStore.value.syncEnvironments, + getSettingSubject("syncEnvironments") +) diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/history/history.api.ts b/packages/hoppscotch-selfhost-desktop/src/platform/history/history.api.ts new file mode 100644 index 000000000..862f776f7 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/history/history.api.ts @@ -0,0 +1,100 @@ +import { + runMutation, + runGQLQuery, + runGQLSubscription, +} from "@hoppscotch/common/helpers/backend/GQLClient" + +import { + CreateUserHistoryDocument, + CreateUserHistoryMutation, + CreateUserHistoryMutationVariables, + DeleteAllUserHistoryDocument, + DeleteAllUserHistoryMutation, + DeleteAllUserHistoryMutationVariables, + GetRestUserHistoryDocument, + GetRestUserHistoryQuery, + GetRestUserHistoryQueryVariables, + RemoveRequestFromHistoryDocument, + RemoveRequestFromHistoryMutation, + RemoveRequestFromHistoryMutationVariables, + ReqType, + ToggleHistoryStarStatusDocument, + ToggleHistoryStarStatusMutation, + ToggleHistoryStarStatusMutationVariables, + UserHistoryCreatedDocument, + UserHistoryDeletedDocument, + UserHistoryDeletedManyDocument, + UserHistoryUpdatedDocument, +} from "../../api/generated/graphql" + +export const getUserHistoryEntries = () => + runGQLQuery({ + query: GetRestUserHistoryDocument, + variables: {}, + }) + +export const createUserHistory = ( + reqData: string, + resMetadata: string, + reqType: ReqType +) => + runMutation< + CreateUserHistoryMutation, + CreateUserHistoryMutationVariables, + "" + >(CreateUserHistoryDocument, { + reqData, + resMetadata, + reqType, + })() + +export const toggleHistoryStarStatus = (id: string) => + runMutation< + ToggleHistoryStarStatusMutation, + ToggleHistoryStarStatusMutationVariables, + "" + >(ToggleHistoryStarStatusDocument, { + id, + })() + +export const removeRequestFromHistory = (id: string) => + runMutation< + RemoveRequestFromHistoryMutation, + RemoveRequestFromHistoryMutationVariables, + "" + >(RemoveRequestFromHistoryDocument, { + id, + })() + +export const deleteAllUserHistory = (reqType: ReqType) => + runMutation< + DeleteAllUserHistoryMutation, + DeleteAllUserHistoryMutationVariables, + "" + >(DeleteAllUserHistoryDocument, { + reqType, + })() + +export const runUserHistoryCreatedSubscription = () => + runGQLSubscription({ + query: UserHistoryCreatedDocument, + variables: {}, + }) + +export const runUserHistoryUpdatedSubscription = () => + runGQLSubscription({ + query: UserHistoryUpdatedDocument, + variables: {}, + }) + +export const runUserHistoryDeletedSubscription = () => + runGQLSubscription({ + query: UserHistoryDeletedDocument, + variables: {}, + }) + +export const runUserHistoryDeletedManySubscription = () => + runGQLSubscription({ + query: UserHistoryDeletedManyDocument, + variables: {}, + }) diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/history/history.platform.ts b/packages/hoppscotch-selfhost-desktop/src/platform/history/history.platform.ts new file mode 100644 index 000000000..fd0042a71 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/history/history.platform.ts @@ -0,0 +1,261 @@ +import { authEvents$, def as platformAuth } from "@platform/auth" +import { + restHistoryStore, + RESTHistoryEntry, + setRESTHistoryEntries, + addRESTHistoryEntry, + toggleRESTHistoryEntryStar, + deleteRESTHistoryEntry, + clearRESTHistory, + setGraphqlHistoryEntries, + GQLHistoryEntry, + addGraphqlHistoryEntry, + toggleGraphqlHistoryEntryStar, + graphqlHistoryStore, + deleteGraphqlHistoryEntry, + clearGraphqlHistory, +} from "@hoppscotch/common/newstore/history" +import { HistoryPlatformDef } from "@hoppscotch/common/platform/history" +import { + getUserHistoryEntries, + runUserHistoryCreatedSubscription, + runUserHistoryDeletedManySubscription, + runUserHistoryDeletedSubscription, + runUserHistoryUpdatedSubscription, +} from "./history.api" + +import * as E from "fp-ts/Either" +import { restHistorySyncer, gqlHistorySyncer } from "./history.sync" +import { runGQLSubscription } from "@hoppscotch/common/helpers/backend/GQLClient" +import { runDispatchWithOutSyncing } from "@lib/sync" +import { ReqType } from "../../api/generated/graphql" + +function initHistorySync() { + const currentUser$ = platformAuth.getCurrentUserStream() + + restHistorySyncer.startStoreSync() + restHistorySyncer.setupSubscriptions(setupSubscriptions) + + gqlHistorySyncer.startStoreSync() + + loadHistoryEntries() + + currentUser$.subscribe(async (user) => { + if (user) { + await loadHistoryEntries() + } + }) + + authEvents$.subscribe((event) => { + if (event.event == "login" || event.event == "token_refresh") { + restHistorySyncer.startListeningToSubscriptions() + } + + if (event.event == "logout") { + restHistorySyncer.stopListeningToSubscriptions() + } + }) +} + +function setupSubscriptions() { + let subs: ReturnType[1][] = [] + + const userHistoryCreatedSub = setupUserHistoryCreatedSubscription() + const userHistoryUpdatedSub = setupUserHistoryUpdatedSubscription() + const userHistoryDeletedSub = setupUserHistoryDeletedSubscription() + const userHistoryDeletedManySub = setupUserHistoryDeletedManySubscription() + + subs = [ + userHistoryCreatedSub, + userHistoryUpdatedSub, + userHistoryDeletedSub, + userHistoryDeletedManySub, + ] + + return () => { + subs.forEach((sub) => sub.unsubscribe()) + } +} + +async function loadHistoryEntries() { + const res = await getUserHistoryEntries() + + if (E.isRight(res)) { + const restEntries = res.right.me.RESTHistory + const gqlEntries = res.right.me.GQLHistory + + const restHistoryEntries: RESTHistoryEntry[] = restEntries.map((entry) => ({ + v: 1, + request: JSON.parse(entry.request), + responseMeta: JSON.parse(entry.responseMetadata), + star: entry.isStarred, + updatedOn: new Date(entry.executedOn), + id: entry.id, + })) + + const gqlHistoryEntries: GQLHistoryEntry[] = gqlEntries.map((entry) => ({ + v: 1, + request: JSON.parse(entry.request), + response: JSON.parse(entry.responseMetadata), + star: entry.isStarred, + updatedOn: new Date(entry.executedOn), + id: entry.id, + })) + + runDispatchWithOutSyncing(() => { + setRESTHistoryEntries(restHistoryEntries) + setGraphqlHistoryEntries(gqlHistoryEntries) + }) + } +} + +function setupUserHistoryCreatedSubscription() { + const [userHistoryCreated$, userHistoryCreatedSub] = + runUserHistoryCreatedSubscription() + + userHistoryCreated$.subscribe((res) => { + if (E.isRight(res)) { + const { id, reqType, request, responseMetadata, isStarred, executedOn } = + res.right.userHistoryCreated + + const hasAlreadyBeenAdded = + reqType == ReqType.Rest + ? restHistoryStore.value.state.some((entry) => entry.id == id) + : graphqlHistoryStore.value.state.some((entry) => entry.id == id) + + !hasAlreadyBeenAdded && + runDispatchWithOutSyncing(() => { + reqType == ReqType.Rest + ? addRESTHistoryEntry({ + v: 1, + id, + request: JSON.parse(request), + responseMeta: JSON.parse(responseMetadata), + star: isStarred, + updatedOn: new Date(executedOn), + }) + : addGraphqlHistoryEntry({ + v: 1, + id, + request: JSON.parse(request), + response: JSON.parse(responseMetadata), + star: isStarred, + updatedOn: new Date(executedOn), + }) + }) + } + }) + + return userHistoryCreatedSub +} + +// currently the updates are only for toggling the star +function setupUserHistoryUpdatedSubscription() { + const [userHistoryUpdated$, userHistoryUpdatedSub] = + runUserHistoryUpdatedSubscription() + + userHistoryUpdated$.subscribe((res) => { + if (E.isRight(res)) { + const { id, executedOn, isStarred, request, responseMetadata, reqType } = + res.right.userHistoryUpdated + + if (reqType == ReqType.Rest) { + const updatedRestEntryIndex = restHistoryStore.value.state.findIndex( + (entry) => entry.id == id + ) + + if (updatedRestEntryIndex != -1) { + runDispatchWithOutSyncing(() => { + toggleRESTHistoryEntryStar({ + v: 1, + id, + request: JSON.parse(request), + responseMeta: JSON.parse(responseMetadata), + // because the star will be toggled in the store, we need to pass the opposite value + star: !isStarred, + updatedOn: new Date(executedOn), + }) + }) + } + } + + if (reqType == ReqType.Gql) { + const updatedGQLEntryIndex = graphqlHistoryStore.value.state.findIndex( + (entry) => entry.id == id + ) + + if (updatedGQLEntryIndex != -1) { + runDispatchWithOutSyncing(() => { + toggleGraphqlHistoryEntryStar({ + v: 1, + id, + request: JSON.parse(request), + response: JSON.parse(responseMetadata), + // because the star will be toggled in the store, we need to pass the opposite value + star: !isStarred, + updatedOn: new Date(executedOn), + }) + }) + } + } + } + }) + + return userHistoryUpdatedSub +} + +function setupUserHistoryDeletedSubscription() { + const [userHistoryDeleted$, userHistoryDeletedSub] = + runUserHistoryDeletedSubscription() + + userHistoryDeleted$.subscribe((res) => { + if (E.isRight(res)) { + const { id, reqType } = res.right.userHistoryDeleted + + if (reqType == ReqType.Gql) { + const deletedEntry = graphqlHistoryStore.value.state.find( + (entry) => entry.id == id + ) + + deletedEntry && + runDispatchWithOutSyncing(() => { + deleteGraphqlHistoryEntry(deletedEntry) + }) + } + + if (reqType == ReqType.Rest) { + const deletedEntry = restHistoryStore.value.state.find( + (entry) => entry.id == id + ) + + deletedEntry && + runDispatchWithOutSyncing(() => { + deleteRESTHistoryEntry(deletedEntry) + }) + } + } + }) + + return userHistoryDeletedSub +} + +function setupUserHistoryDeletedManySubscription() { + const [userHistoryDeletedMany$, userHistoryDeletedManySub] = + runUserHistoryDeletedManySubscription() + + userHistoryDeletedMany$.subscribe((res) => { + if (E.isRight(res)) { + const { reqType } = res.right.userHistoryDeletedMany + + runDispatchWithOutSyncing(() => { + reqType == ReqType.Rest ? clearRESTHistory() : clearGraphqlHistory() + }) + } + }) + + return userHistoryDeletedManySub +} + +export const def: HistoryPlatformDef = { + initHistorySync, +} diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/history/history.sync.ts b/packages/hoppscotch-selfhost-desktop/src/platform/history/history.sync.ts new file mode 100644 index 000000000..340c185a5 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/history/history.sync.ts @@ -0,0 +1,101 @@ +import { + graphqlHistoryStore, + removeDuplicateRestHistoryEntry, + removeDuplicateGraphqlHistoryEntry, + restHistoryStore, +} from "@hoppscotch/common/newstore/history" +import { + getSettingSubject, + settingsStore, +} from "@hoppscotch/common/newstore/settings" + +import { getSyncInitFunction } from "../../lib/sync" + +import * as E from "fp-ts/Either" + +import { StoreSyncDefinitionOf } from "../../lib/sync" +import { + createUserHistory, + deleteAllUserHistory, + removeRequestFromHistory, + toggleHistoryStarStatus, +} from "./history.api" +import { ReqType } from "../../api/generated/graphql" + +export const restHistoryStoreSyncDefinition: StoreSyncDefinitionOf< + typeof restHistoryStore +> = { + async addEntry({ entry }) { + const res = await createUserHistory( + JSON.stringify(entry.request), + JSON.stringify(entry.responseMeta), + ReqType.Rest + ) + + if (E.isRight(res)) { + entry.id = res.right.createUserHistory.id + + // preventing double insertion from here and subscription + removeDuplicateRestHistoryEntry(entry.id) + } + }, + deleteEntry({ entry }) { + if (entry.id) { + removeRequestFromHistory(entry.id) + } + }, + toggleStar({ entry }) { + if (entry.id) { + toggleHistoryStarStatus(entry.id) + } + }, + clearHistory() { + deleteAllUserHistory(ReqType.Rest) + }, +} + +export const gqlHistoryStoreSyncDefinition: StoreSyncDefinitionOf< + typeof graphqlHistoryStore +> = { + async addEntry({ entry }) { + const res = await createUserHistory( + JSON.stringify(entry.request), + JSON.stringify(entry.response), + ReqType.Gql + ) + + if (E.isRight(res)) { + entry.id = res.right.createUserHistory.id + + // preventing double insertion from here and subscription + removeDuplicateGraphqlHistoryEntry(entry.id) + } + }, + deleteEntry({ entry }) { + if (entry.id) { + removeRequestFromHistory(entry.id) + } + }, + toggleStar({ entry }) { + if (entry.id) { + toggleHistoryStarStatus(entry.id) + } + }, + clearHistory() { + deleteAllUserHistory(ReqType.Gql) + }, +} + +export const restHistorySyncer = getSyncInitFunction( + restHistoryStore, + restHistoryStoreSyncDefinition, + () => settingsStore.value.syncHistory, + getSettingSubject("syncHistory") +) + +export const gqlHistorySyncer = getSyncInitFunction( + graphqlHistoryStore, + gqlHistoryStoreSyncDefinition, + () => settingsStore.value.syncHistory, + getSettingSubject("syncHistory") +) diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/interceptors/native.ts b/packages/hoppscotch-selfhost-desktop/src/platform/interceptors/native.ts new file mode 100644 index 000000000..716c57436 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/interceptors/native.ts @@ -0,0 +1,169 @@ +import * as E from "fp-ts/Either" +import { + Interceptor, + InterceptorError, + RequestRunResult, +} from "@hoppscotch/common/services/interceptor.service" +import { CookieJarService } from "@hoppscotch/common/services/cookie-jar.service" +import axios, { AxiosRequestConfig, CancelToken } from "axios" +import { cloneDeep } from "lodash-es" +import { Body, HttpVerb, ResponseType, getClient } from "@tauri-apps/api/http" +import { Service } from "dioc" + +export const preProcessRequest = ( + req: AxiosRequestConfig +): AxiosRequestConfig => { + const reqClone = cloneDeep(req) + + // If the parameters are URLSearchParams, inject them to URL instead + // This prevents issues of marshalling the URLSearchParams to the proxy + if (reqClone.params instanceof URLSearchParams) { + try { + const url = new URL(reqClone.url ?? "") + + for (const [key, value] of reqClone.params.entries()) { + url.searchParams.append(key, value) + } + + reqClone.url = url.toString() + } 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 :) + } + + reqClone.params = {} + } + + return reqClone +} + +async function runRequest( + req: AxiosRequestConfig, + cancelled: () => boolean +): RequestRunResult["response"] { + const timeStart = Date.now() + + const processedReq = preProcessRequest(req) + try { + const client = await getClient() + + if (cancelled()) { + client.drop() + return E.left("cancellation") + } + + let body = Body.text(processedReq.data ?? "") + + if (processedReq.data instanceof FormData) { + let body_data = {} + for (const entry of processedReq.data.entries()) { + const [name, value] = entry + + if (value instanceof File) { + let file_data = await value.arrayBuffer() + + body_data[name] = { + file: new Uint8Array(file_data), + fileName: value.name, + } + } + } + + body = Body.form(body_data) + } + + const res = await client.request({ + method: processedReq.method as HttpVerb, + url: processedReq.url ?? "", + responseType: ResponseType.Binary, + headers: processedReq.headers, + body: body, + }) + + if (cancelled()) { + client.drop() + return E.left("cancellation") + } + + res.data = new Uint8Array(res.data as number[]).buffer + + const timeEnd = Date.now() + + return E.right({ + ...res, + config: { + timeData: { + startTime: timeStart, + endTime: timeEnd, + }, + }, + }) + } catch (e) { + const timeEnd = Date.now() + + if (axios.isAxiosError(e) && e.response) { + return E.right({ + ...e.response, + config: { + timeData: { + startTime: timeStart, + endTime: timeEnd, + }, + }, + }) + } else if (axios.isCancel(e)) { + return E.left("cancellation") + } else { + return E.left({ + humanMessage: { + heading: (t) => t("error.network_fail"), + description: (t) => t("helpers.network_fail"), + }, + error: e, + }) + } + } +} + +export class NativeInterceptorService extends Service implements Interceptor { + public static readonly ID = "NATIVE_INTERCEPTOR_SERVICE" + + public interceptorID = "native" // TODO: i18n this + + public name = () => "Native" + + public selectable = { type: "selectable" as const } + + public supportsCookies = true + + public cookieJarService = this.bind(CookieJarService) + + constructor() { + super() + } + + public runRequest(req: any) { + const processedReq = preProcessRequest(req) + + const relevantCookies = this.cookieJarService.getCookiesForURL( + new URL(processedReq.url!) + ) + + processedReq.headers["Cookie"] = relevantCookies + .map((cookie) => `${cookie.name!}=${cookie.value!}`) + .join(";") + + let cancelled = false + + const checkCancelled = () => { + return cancelled + } + + return { + cancel: () => { + cancelled = true + }, + response: runRequest(processedReq, checkCancelled), + } + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/io.ts b/packages/hoppscotch-selfhost-desktop/src/platform/io.ts new file mode 100644 index 000000000..0d4b9d2b8 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/io.ts @@ -0,0 +1,24 @@ +import { IOPlatformDef } from "@hoppscotch/common/platform/io" +import { save } from "@tauri-apps/api/dialog" +import { writeBinaryFile, writeTextFile } from "@tauri-apps/api/fs" + +export const ioDef: IOPlatformDef = { + async saveFileWithDialog(opts) { + const path = await save({ + filters: opts.filters, + defaultPath: opts.suggestedFilename, + }) + + if (path === null) { + return { type: "cancelled" } + } + + if (typeof opts.data === "string") { + await writeTextFile(path, opts.data) + } else { + await writeBinaryFile(path, opts.data) + } + + return { type: "saved", path } + }, +} diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/settings/settings.api.ts b/packages/hoppscotch-selfhost-desktop/src/platform/settings/settings.api.ts new file mode 100644 index 000000000..198701a56 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/settings/settings.api.ts @@ -0,0 +1,51 @@ +import { + runGQLQuery, + runGQLSubscription, + runMutation, +} from "@hoppscotch/common/helpers/backend/GQLClient" +import { + CreateUserSettingsDocument, + CreateUserSettingsMutation, + CreateUserSettingsMutationVariables, + GetUserSettingsDocument, + GetUserSettingsQuery, + GetUserSettingsQueryVariables, + UpdateUserSettingsDocument, + UpdateUserSettingsMutation, + UpdateUserSettingsMutationVariables, + UserSettingsUpdatedDocument, +} from "../../api/generated/graphql" + +export const getUserSettings = () => + runGQLQuery< + GetUserSettingsQuery, + GetUserSettingsQueryVariables, + "user_settings/not_found" + >({ + query: GetUserSettingsDocument, + variables: {}, + }) + +export const createUserSettings = (properties: string) => + runMutation< + CreateUserSettingsMutation, + CreateUserSettingsMutationVariables, + "" + >(CreateUserSettingsDocument, { + properties, + })() + +export const updateUserSettings = (properties: string) => + runMutation< + UpdateUserSettingsMutation, + UpdateUserSettingsMutationVariables, + "" + >(UpdateUserSettingsDocument, { + properties, + })() + +export const runUserSettingsUpdatedSubscription = () => + runGQLSubscription({ + query: UserSettingsUpdatedDocument, + variables: {}, + }) diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/settings/settings.platform.ts b/packages/hoppscotch-selfhost-desktop/src/platform/settings/settings.platform.ts new file mode 100644 index 000000000..a8aca2e72 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/settings/settings.platform.ts @@ -0,0 +1,88 @@ +import { SettingsPlatformDef } from "@hoppscotch/common/platform/settings" +import { settingsSyncer } from "./settings.sync" +import { authEvents$, def as platformAuth } from "@platform/auth" +import { + createUserSettings, + getUserSettings, + runUserSettingsUpdatedSubscription, +} from "./settings.api" +import * as E from "fp-ts/Either" +import { runGQLSubscription } from "@hoppscotch/common/helpers/backend/GQLClient" +import { + bulkApplySettings, + getDefaultSettings, +} from "@hoppscotch/common/newstore/settings" +import { runDispatchWithOutSyncing } from "@lib/sync" + +function initSettingsSync() { + const currentUser$ = platformAuth.getCurrentUserStream() + settingsSyncer.startStoreSync() + settingsSyncer.setupSubscriptions(setupSubscriptions) + + // load the settings + + loadUserSettings() + + currentUser$.subscribe(async (user) => { + if (user) { + // load the settings + loadUserSettings() + } + }) + + authEvents$.subscribe((event) => { + if (event.event == "login" || event.event == "token_refresh") { + settingsSyncer.startListeningToSubscriptions() + } + + if (event.event == "logout") { + settingsSyncer.stopListeningToSubscriptions() + } + }) +} + +async function loadUserSettings() { + const res = await getUserSettings() + + // create user settings if it doesn't exist + E.isLeft(res) && + res.left.error == "user_settings/not_found" && + (await createUserSettings(JSON.stringify(getDefaultSettings()))) + + if (E.isRight(res)) { + runDispatchWithOutSyncing(() => { + bulkApplySettings(JSON.parse(res.right.me.settings.properties)) + }) + } +} + +function setupSubscriptions() { + let subs: ReturnType[1][] = [] + + const userSettingsUpdatedSub = setupUserSettingsUpdatedSubscription() + + subs = [userSettingsUpdatedSub] + + return () => { + subs.forEach((sub) => sub.unsubscribe()) + } +} + +function setupUserSettingsUpdatedSubscription() { + const [userSettingsUpdated$, userSettingsUpdatedSub] = + runUserSettingsUpdatedSubscription() + + userSettingsUpdated$.subscribe((res) => { + if (E.isRight(res)) { + runDispatchWithOutSyncing(() => { + bulkApplySettings(JSON.parse(res.right.userSettingsUpdated.properties)) + }) + } + }) + + return userSettingsUpdatedSub +} + +export const def: SettingsPlatformDef = { + initSettingsSync, +} diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/settings/settings.sync.ts b/packages/hoppscotch-selfhost-desktop/src/platform/settings/settings.sync.ts new file mode 100644 index 000000000..19df5001a --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/settings/settings.sync.ts @@ -0,0 +1,21 @@ +import { settingsStore } from "@hoppscotch/common/newstore/settings" + +import { getSyncInitFunction } from "../../lib/sync" + +import { StoreSyncDefinitionOf } from "../../lib/sync" + +import { updateUserSettings } from "./settings.api" + +export const settingsSyncDefinition: StoreSyncDefinitionOf< + typeof settingsStore +> = { + applySetting() { + updateUserSettings(JSON.stringify(settingsStore.value)) + }, +} + +export const settingsSyncer = getSyncInitFunction( + settingsStore, + settingsSyncDefinition, + () => true +) diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/tabState/tabState.api.ts b/packages/hoppscotch-selfhost-desktop/src/platform/tabState/tabState.api.ts new file mode 100644 index 000000000..89d87a537 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/tabState/tabState.api.ts @@ -0,0 +1,36 @@ +import { + runMutation, + runGQLQuery, +} from "@hoppscotch/common/helpers/backend/GQLClient" +import { + GetCurrentRestSessionDocument, + GetCurrentRestSessionQuery, + GetCurrentRestSessionQueryVariables, + SessionType, + UpdateUserSessionDocument, + UpdateUserSessionMutation, + UpdateUserSessionMutationVariables, +} from "../../api/generated/graphql" + +export const updateUserSession = ( + currentSession: string, + sessionType: SessionType +) => + runMutation< + UpdateUserSessionMutation, + UpdateUserSessionMutationVariables, + "" + >(UpdateUserSessionDocument, { + sessionType, + currentSession, + })() + +export const getCurrentRestSession = () => + runGQLQuery< + GetCurrentRestSessionQuery, + GetCurrentRestSessionQueryVariables, + "" + >({ + query: GetCurrentRestSessionDocument, + variables: {}, + }) diff --git a/packages/hoppscotch-selfhost-desktop/src/platform/tabState/tabState.platform.ts b/packages/hoppscotch-selfhost-desktop/src/platform/tabState/tabState.platform.ts new file mode 100644 index 000000000..30420da0d --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/platform/tabState/tabState.platform.ts @@ -0,0 +1,37 @@ +import { PersistableRESTTabState } from "@hoppscotch/common/helpers/rest/tab" +import { HoppUser } from "@hoppscotch/common/platform/auth" +import { TabStatePlatformDef } from "@hoppscotch/common/platform/tab" +import { def as platformAuth } from "@platform/auth" +import { getCurrentRestSession, updateUserSession } from "./tabState.api" +import { SessionType } from "../../api/generated/graphql" +import * as E from "fp-ts/Either" + +async function writeCurrentTabState( + _: HoppUser, + persistableTabState: PersistableRESTTabState +) { + await updateUserSession(JSON.stringify(persistableTabState), SessionType.Rest) +} + +async function loadTabStateFromSync(): Promise { + const currentUser = platformAuth.getCurrentUser() + + if (!currentUser) + throw new Error("Cannot load request from sync without login") + + const res = await getCurrentRestSession() + + if (E.isRight(res)) { + const currentRESTSession = res.right.me.currentRESTSession + + return currentRESTSession ? JSON.parse(currentRESTSession) : null + } else { + } + + return null +} + +export const def: TabStatePlatformDef = { + loadTabStateFromSync, + writeCurrentTabState, +} diff --git a/packages/hoppscotch-selfhost-desktop/src/vite-env.d.ts b/packages/hoppscotch-selfhost-desktop/src/vite-env.d.ts new file mode 100644 index 000000000..fc812394b --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/vite-env.d.ts @@ -0,0 +1,7 @@ +/// + +declare module "*.vue" { + import type { DefineComponent } from "vue"; + const component: DefineComponent<{}, {}, any>; + export default component; +} diff --git a/packages/hoppscotch-selfhost-desktop/tsconfig.json b/packages/hoppscotch-selfhost-desktop/tsconfig.json new file mode 100644 index 000000000..969f1e100 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "moduleResolution": "Node", + "strict": true, + "jsx": "preserve", + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "lib": ["ESNext", "DOM"], + "skipLibCheck": true, + "noEmit": true, + "paths": { + "@hoppscotch/common": [ "../hoppscotch-common/src/index.ts" ], + "@hoppscotch/common/*": [ "../hoppscotch-common/src/*" ], + "@platform/*": ["./src/platform/*"], + "@lib/*": ["./src/lib/*"], + } + + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/packages/hoppscotch-selfhost-desktop/tsconfig.node.json b/packages/hoppscotch-selfhost-desktop/tsconfig.node.json new file mode 100644 index 000000000..28e5a5e9b --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts", "meta.ts"] +} diff --git a/packages/hoppscotch-selfhost-desktop/vite.config.ts b/packages/hoppscotch-selfhost-desktop/vite.config.ts new file mode 100644 index 000000000..ca6e5ac77 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/vite.config.ts @@ -0,0 +1,201 @@ +import { defineConfig, loadEnv, normalizePath } from "vite" +import { APP_INFO, META_TAGS } from "./meta" +import { viteStaticCopy as StaticCopy } from "vite-plugin-static-copy" +import generateSitemap from "vite-plugin-pages-sitemap" +import HtmlConfig from "vite-plugin-html-config" +import Vue from "@vitejs/plugin-vue" +import VueI18n from "@intlify/vite-plugin-vue-i18n" +import Components from "unplugin-vue-components/vite" +import Icons from "unplugin-icons/vite" +import Inspect from "vite-plugin-inspect" +import WindiCSS from "vite-plugin-windicss" +import { VitePWA } from "vite-plugin-pwa" +import Pages from "vite-plugin-pages" +import Layouts from "vite-plugin-vue-layouts" +import IconResolver from "unplugin-icons/resolver" +import { FileSystemIconLoader } from "unplugin-icons/loaders" +import * as path from "path" +import { VitePluginFonts } from "vite-plugin-fonts" +import legacy from "@vitejs/plugin-legacy" + +const ENV = loadEnv("development", path.resolve(__dirname, "../../")) + +export default defineConfig({ + envDir: path.resolve(__dirname, "../../"), + // TODO: Migrate @hoppscotch/data to full ESM + define: { + // For 'util' polyfill required by dep of '@apidevtools/swagger-parser' + "process.env": {}, + }, + server: { + port: 3000, + }, + preview: { + port: 3000, + }, + publicDir: path.resolve(__dirname, "../hoppscotch-common/public"), + build: { + sourcemap: true, + emptyOutDir: true, + rollupOptions: { + maxParallelFileOps: 2, + }, + }, + resolve: { + alias: { + // TODO: Maybe leave ~ only for individual apps and not use on common + "~": path.resolve(__dirname, "../hoppscotch-common/src"), + "@hoppscotch/common": "@hoppscotch/common/src", + "@composables": path.resolve( + __dirname, + "../hoppscotch-common/src/composables" + ), + "@modules": path.resolve(__dirname, "../hoppscotch-common/src/modules"), + "@components": path.resolve( + __dirname, + "../hoppscotch-common/src/components" + ), + "@helpers": path.resolve(__dirname, "../hoppscotch-common/src/helpers"), + "@functional": path.resolve( + __dirname, + "../hoppscotch-common/src/helpers/functional" + ), + "@workers": path.resolve(__dirname, "../hoppscotch-common/src/workers"), + "@platform": path.resolve(__dirname, "./src/platform"), + "@lib": path.resolve(__dirname, "./src/lib"), + stream: "stream-browserify", + util: "util", + }, + dedupe: ["vue"], + }, + plugins: [ + Inspect(), // go to url -> /__inspect + HtmlConfig({ + metas: META_TAGS(ENV), + }), + Vue(), + Pages({ + routeStyle: "nuxt", + dirs: "../hoppscotch-common/src/pages", + importMode: "async", + onRoutesGenerated(routes) { + return ((generateSitemap as any) as typeof generateSitemap)({ + routes, + nuxtStyle: true, + allowRobots: true, + dest: ".sitemap-gen", + hostname: ENV.VITE_BASE_URL, + }) + }, + }), + StaticCopy({ + targets: [ + { + src: normalizePath(path.resolve(__dirname, "./.sitemap-gen/*")), + dest: normalizePath(path.resolve(__dirname, "./dist")), + }, + ], + }), + Layouts({ + layoutsDirs: "../hoppscotch-common/src/layouts", + defaultLayout: "default", + }), + VueI18n({ + runtimeOnly: false, + compositionOnly: true, + include: [path.resolve(__dirname, "locales")], + }), + WindiCSS({ + root: path.resolve(__dirname, "../hoppscotch-common"), + }), + Components({ + dts: "../hoppscotch-common/src/components.d.ts", + dirs: [ + "../hoppscotch-common/src/components", + "../hoppscotch-ui/src/components", + ], + directoryAsNamespace: true, + resolvers: [ + IconResolver({ + prefix: "icon", + customCollections: ["hopp", "auth", "brands"], + }), + (compName: string) => { + if (compName.startsWith("Hopp")) + return { name: compName, from: "@hoppscotch/ui" } + else return undefined + }, + ], + types: [ + { + from: "vue-tippy", + names: ["Tippy"], + }, + ], + }), + Icons({ + compiler: "vue3", + customCollections: { + hopp: FileSystemIconLoader("../hoppscotch-common/assets/icons"), + auth: FileSystemIconLoader("../hoppscotch-common/assets/icons/auth"), + brands: FileSystemIconLoader( + "../hoppscotch-common/assets/icons/brands" + ), + }, + }), + VitePWA({ + manifest: { + name: APP_INFO.name, + short_name: APP_INFO.name, + description: APP_INFO.shortDescription, + start_url: "/?source=pwa", + background_color: APP_INFO.app.background, + theme_color: APP_INFO.app.background, + icons: [ + { + src: "/icon.png", + sizes: "512x512", + type: "image/png", + purpose: "any maskable", + }, + { + src: "/logo.svg", + sizes: "48x48 72x72 96x96 128x128 256x256 512x512", + type: "image/svg+xml", + purpose: "any maskable", + }, + ], + }, + registerType: "prompt", + workbox: { + cleanupOutdatedCaches: true, + maximumFileSizeToCacheInBytes: 4194304, + navigateFallbackDenylist: [ + /robots.txt/, + /sitemap.xml/, + /discord/, + /telegram/, + /beta/, + /careers/, + /newsletter/, + /twitter/, + /github/, + /announcements/, + ], + }, + }), + VitePluginFonts({ + google: { + families: [ + "Inter:wght@400;500;600;700;800", + "Roboto+Mono:wght@400;500", + "Material+Icons", + ], + }, + }), + legacy({ + modernPolyfills: ["es.string.replace-all"], + renderLegacyChunks: false, + }), + ], +}) diff --git a/packages/hoppscotch-selfhost-web/src/main.ts b/packages/hoppscotch-selfhost-web/src/main.ts index 1b7420dbf..6d3a3278e 100644 --- a/packages/hoppscotch-selfhost-web/src/main.ts +++ b/packages/hoppscotch-selfhost-web/src/main.ts @@ -11,6 +11,7 @@ import { ExtensionInspectorService } from "@hoppscotch/common/platform/std/inspe import { ExtensionInterceptorService } from "@hoppscotch/common/platform/std/interceptors/extension" import { stdFooterItems } from "@hoppscotch/common/platform/std/ui/footerItem" import { stdSupportOptionItems } from "@hoppscotch/common/platform/std/ui/supportOptionsItem" +import { browserIODef } from "@hoppscotch/common/platform/std/io" createHoppApp("#app", { ui: { @@ -18,6 +19,7 @@ createHoppApp("#app", { additionalSupportOptionsMenuItems: stdSupportOptionItems, }, auth: authDef, + io: browserIODef, sync: { environments: environmentsDef, collections: collectionsDef, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 56d85a873..04dd6c281 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -460,6 +460,9 @@ importers: buffer: specifier: ^6.0.3 version: 6.0.3 + cookie-es: + specifier: ^1.0.0 + version: 1.0.0 dioc: specifier: workspace:^ version: link:../dioc @@ -532,6 +535,12 @@ importers: rxjs: specifier: ^7.8.1 version: 7.8.1 + set-cookie-parser: + specifier: ^2.6.0 + version: 2.6.0 + set-cookie-parser-es: + specifier: ^1.0.5 + version: 1.0.5 socket.io-client-v2: specifier: npm:socket.io-client@^2.4.0 version: /socket.io-client@2.4.0 @@ -601,6 +610,9 @@ importers: yargs-parser: specifier: ^21.1.1 version: 21.1.1 + zod: + specifier: ^3.22.2 + version: 3.22.2 devDependencies: '@esbuild-plugins/node-globals-polyfill': specifier: ^0.2.3 @@ -866,6 +878,157 @@ importers: specifier: ^4.6.3 version: 4.7.4 + packages/hoppscotch-selfhost-desktop: + dependencies: + '@hoppscotch/common': + specifier: workspace:^ + version: link:../hoppscotch-common + '@platform/auth': + specifier: ^0.1.106 + version: 0.1.106 + '@tauri-apps/api': + specifier: ^1.3.0 + version: 1.5.1 + '@tauri-apps/cli': + specifier: ^1.3.0 + version: 1.5.6 + '@vueuse/core': + specifier: ^10.4.1 + version: 10.5.0(vue@3.3.4) + axios: + specifier: ^0.21.4 + version: 0.21.4 + buffer: + specifier: ^6.0.3 + version: 6.0.3 + dioc: + specifier: workspace:^ + version: link:../dioc + environments.api: + specifier: link:@platform/environments/environments.api + version: link:@platform/environments/environments.api + event: + specifier: link:@tauri-apps/api/event + version: link:@tauri-apps/api/event + fp-ts: + specifier: ^2.16.0 + version: 2.16.1 + lodash-es: + specifier: ^4.17.21 + version: 4.17.21 + process: + specifier: ^0.11.10 + version: 0.11.10 + rxjs: + specifier: ^7.8.1 + version: 7.8.1 + shell: + specifier: link:@tauri-apps/api/shell + version: link:@tauri-apps/api/shell + stream-browserify: + specifier: ^3.0.0 + version: 3.0.0 + tauri: + specifier: link:@tauri-apps/api/tauri + version: link:@tauri-apps/api/tauri + tauri-plugin-store-api: + specifier: github:tauri-apps/tauri-plugin-store#v1 + version: github.com/tauri-apps/tauri-plugin-store/6e19887b1bdea9b921a31993d72396a350731e07 + util: + specifier: ^0.12.4 + version: 0.12.5 + vue: + specifier: ^3.2.45 + version: 3.3.4 + workbox-window: + specifier: ^6.5.4 + version: 6.6.0 + devDependencies: + '@intlify/vite-plugin-vue-i18n': + specifier: ^6.0.1 + version: 6.0.1(vite@4.5.0) + '@rushstack/eslint-patch': + specifier: ^1.1.4 + version: 1.3.3 + '@types/lodash-es': + specifier: ^4.17.9 + version: 4.17.10 + '@types/node': + specifier: ^18.7.10 + version: 18.18.8 + '@typescript-eslint/eslint-plugin': + specifier: ^5.19.0 + version: 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.47.0)(typescript@4.9.5) + '@typescript-eslint/parser': + specifier: ^5.19.0 + version: 5.62.0(eslint@8.47.0)(typescript@4.9.5) + '@vitejs/plugin-legacy': + specifier: ^2.3.0 + version: 2.3.0(terser@5.24.0)(vite@4.5.0) + '@vitejs/plugin-vue': + specifier: ^4.0.0 + version: 4.3.1(vite@4.5.0)(vue@3.3.4) + '@vue/eslint-config-typescript': + specifier: ^11.0.1 + version: 11.0.3(eslint-plugin-vue@9.17.0)(eslint@8.47.0)(typescript@4.9.5) + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + eslint: + specifier: ^8.28.0 + version: 8.47.0 + eslint-plugin-prettier: + specifier: ^4.2.1 + version: 4.2.1(eslint@8.47.0)(prettier@2.8.4) + eslint-plugin-vue: + specifier: ^9.5.1 + version: 9.17.0(eslint@8.47.0) + typescript: + specifier: ^4.9.5 + version: 4.9.5 + unplugin-icons: + specifier: ^0.14.9 + version: 0.14.9(rollup@2.79.1)(vite@4.5.0) + unplugin-vue-components: + specifier: ^0.21.0 + version: 0.21.0(rollup@2.79.1)(vite@4.5.0)(vue@3.3.4) + vite: + specifier: ^4.2.1 + version: 4.5.0(@types/node@18.18.8)(terser@5.24.0) + vite-plugin-fonts: + specifier: ^0.6.0 + version: 0.6.0(vite@4.5.0) + vite-plugin-html-config: + specifier: ^1.0.10 + version: 1.0.11(vite@4.5.0) + vite-plugin-inspect: + specifier: ^0.7.4 + version: 0.7.38(rollup@2.79.1)(vite@4.5.0) + vite-plugin-pages: + specifier: ^0.26.0 + version: 0.26.0(vite@4.5.0) + vite-plugin-pages-sitemap: + specifier: ^1.4.0 + version: 1.6.1 + vite-plugin-pwa: + specifier: ^0.13.1 + version: 0.13.1(vite@4.5.0)(workbox-build@6.6.0)(workbox-window@6.6.0) + vite-plugin-static-copy: + specifier: ^0.12.0 + version: 0.12.0(vite@4.5.0) + vite-plugin-vue-layouts: + specifier: ^0.7.0 + version: 0.7.0(vite@4.5.0)(vue-router@4.2.5)(vue@3.3.4) + vite-plugin-windicss: + specifier: ^1.8.8 + version: 1.9.1(vite@4.5.0) + vue-tsc: + specifier: ^1.0.11 + version: 1.8.8(typescript@4.9.5) + windicss: + specifier: ^3.5.6 + version: 3.5.6 + packages/hoppscotch-selfhost-web: dependencies: '@fontsource-variable/inter': @@ -1442,7 +1605,6 @@ packages: dependencies: execa: 5.1.1 find-up: 5.0.0 - dev: false /@antfu/install-pkg@0.1.1: resolution: {integrity: sha512-LyB/8+bSfa0DFGC06zpCEfs89/XoWZwws5ygEa5D+Xsm3OfI+aXQ86VgVG7Acyef+rSZ5HE7J8rrxzrQeM3PjQ==} @@ -4089,7 +4251,6 @@ packages: /@babel/standalone@7.20.0: resolution: {integrity: sha512-8toFReoMyknVN538KZYS9HJLUlpvibQiPQqt8TYFeyV+FlZUmM8TG2zcS8q4vAijCRLoAKT1EzeBVvbxjMfi9A==} engines: {node: '>=6.9.0'} - dev: false /@babel/template@7.20.7: resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} @@ -4386,10 +4547,10 @@ packages: '@commitlint/execute-rule': 16.2.1 '@commitlint/resolve-extends': 16.2.1 '@commitlint/types': 16.2.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 cosmiconfig: 7.0.1 - cosmiconfig-typescript-loader: 2.0.2(@types/node@17.0.27)(cosmiconfig@7.0.1)(typescript@4.9.5) + cosmiconfig-typescript-loader: 2.0.2(@types/node@18.18.8)(cosmiconfig@7.0.1)(typescript@4.9.5) lodash: 4.17.21 resolve-from: 5.0.0 typescript: 4.9.5 @@ -7761,7 +7922,6 @@ packages: /@iconify/types@1.1.0: resolution: {integrity: sha512-Jh0llaK2LRXQoYsorIH8maClebsnzTcve+7U3rQUSnC11X4jtPnFuyatqFLvMxZ8MLG8dB4zfHsbPfuvxluONw==} - dev: false /@iconify/types@2.0.0: resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -7778,7 +7938,6 @@ packages: local-pkg: 0.4.2 transitivePeerDependencies: - supports-color - dev: false /@iconify/utils@2.1.4: resolution: {integrity: sha512-7vzsYIvxv5Hng0MNEtSSnyMBD/+zqnORqmKiYsSgpMBGSz1r93URgBZHPYCZ1/gpoaVstYW4/SVLGCMJBNMCLQ==} @@ -8025,6 +8184,32 @@ packages: - supports-color dev: true + /@intlify/vite-plugin-vue-i18n@6.0.1(vite@4.5.0): + resolution: {integrity: sha512-FFVcxVU4bR9vdDLNbltM5mrhndnXMErO01i0RrpdyMegEt3Nu/YLoH0sFdjRun7/RY4vaEnhTnFvVf9uO0dQvg==} + engines: {node: '>= 14.6'} + peerDependencies: + petite-vue-i18n: '*' + vite: ^2.9.0 || ^3.0.0 + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vite: + optional: true + vue-i18n: + optional: true + dependencies: + '@intlify/bundle-utils': 7.0.0 + '@intlify/shared': 9.4.1 + '@rollup/pluginutils': 4.2.1 + debug: 4.3.4(supports-color@9.2.2) + fast-glob: 3.2.11 + source-map: 0.6.1 + vite: 4.5.0(@types/node@18.18.8)(terser@5.24.0) + transitivePeerDependencies: + - supports-color + dev: true + /@intlify/vite-plugin-vue-i18n@7.0.0(vite@4.4.9)(vue-i18n@9.2.2): resolution: {integrity: sha512-2TbDOQ8XD+vkc0s5OFmr+IY/k4mYMC7pzvx0xGQn+cU/ev314+yi7Z7N7rWcBgiYk1WOUalbGSo3d4nJDxOOyw==} engines: {node: '>= 14.6'} @@ -8098,7 +8283,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 jest-message-util: 27.5.1 jest-util: 27.5.1 @@ -8110,7 +8295,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.5.0 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 jest-message-util: 29.5.0 jest-util: 29.5.0 @@ -8131,7 +8316,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.8.1 @@ -8176,14 +8361,14 @@ packages: '@jest/test-result': 29.4.1 '@jest/transform': 29.4.1 '@jest/types': 29.4.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.3.2 exit: 0.1.2 graceful-fs: 4.2.10 jest-changed-files: 29.4.0 - jest-config: 29.4.1(@types/node@17.0.27)(ts-node@10.9.1) + jest-config: 29.4.1(@types/node@18.18.8)(ts-node@10.9.1) jest-haste-map: 29.4.1 jest-message-util: 29.4.1 jest-regex-util: 29.2.0 @@ -8210,7 +8395,7 @@ packages: dependencies: '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 jest-mock: 27.5.1 dev: true @@ -8220,7 +8405,7 @@ packages: dependencies: '@jest/fake-timers': 29.4.1 '@jest/types': 29.5.0 - '@types/node': 17.0.27 + '@types/node': 18.18.8 jest-mock: 29.4.1 dev: true @@ -8261,7 +8446,7 @@ packages: dependencies: '@jest/types': 27.5.1 '@sinonjs/fake-timers': 8.1.0 - '@types/node': 17.0.27 + '@types/node': 18.18.8 jest-message-util: 27.5.1 jest-mock: 27.5.1 jest-util: 27.5.1 @@ -8273,7 +8458,7 @@ packages: dependencies: '@jest/types': 29.5.0 '@sinonjs/fake-timers': 10.0.2 - '@types/node': 17.0.27 + '@types/node': 18.18.8 jest-message-util: 29.5.0 jest-mock: 29.4.1 jest-util: 29.5.0 @@ -8314,7 +8499,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -8353,7 +8538,7 @@ packages: '@jest/transform': 29.4.1 '@jest/types': 29.5.0 '@jridgewell/trace-mapping': 0.3.17 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -8515,7 +8700,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 '@types/yargs': 16.0.4 chalk: 4.1.2 dev: true @@ -8527,7 +8712,7 @@ packages: '@jest/schemas': 28.0.2 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 '@types/yargs': 17.0.10 chalk: 4.1.2 dev: true @@ -8539,7 +8724,7 @@ packages: '@jest/schemas': 29.4.0 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 '@types/yargs': 17.0.10 chalk: 4.1.2 dev: true @@ -8551,7 +8736,7 @@ packages: '@jest/schemas': 29.4.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 '@types/yargs': 17.0.10 chalk: 4.1.2 dev: true @@ -8740,7 +8925,7 @@ packages: '@rushstack/ts-command-line': 4.15.1 colors: 1.2.5 lodash: 4.17.21 - resolve: 1.22.4 + resolve: 1.22.8 semver: 7.5.4 source-map: 0.6.1 typescript: 5.0.4 @@ -9171,6 +9356,27 @@ packages: tslib: 2.6.2 dev: true + /@platform/auth@0.1.106: + resolution: {integrity: sha512-Z3lgZYBybBBki0bH2Xr8ceX49okscoowui+nkUWbs7YTWI0Cx8KnW6pwwsOVFJvG8FPEh624ItvAfacEovF6GQ==} + deprecated: pre-release module deprecation + dependencies: + '@platform/libs': 0.3.2 + '@platform/util.is': 0.0.165 + dev: false + + /@platform/libs@0.3.2: + resolution: {integrity: sha512-Iwdzd/XizT0Gytnpird5aVkImnTSXM1tGHa+RI4XgLVC6ONVGTevGDCtrvY+ABO4B8UxCvVqOcabRFpQ7WwBzw==} + dependencies: + '@types/ramda': 0.28.15 + ramda: 0.28.0 + rxjs: 7.5.6 + dev: false + + /@platform/util.is@0.0.165: + resolution: {integrity: sha512-zWh8OsTydmUZiwFT2Fqc5BYCzoO9pVVty6nTxC6YS75rWqQ5Ig0VR9ey97h4yV8DUdVXrP2XHH/ADAByHdsktA==} + deprecated: pre-release module deprecation + dev: false + /@polka/url@1.0.0-next.21: resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} dev: true @@ -9443,7 +9649,7 @@ packages: fs-extra: 7.0.1 import-lazy: 4.0.0 jju: 1.4.0 - resolve: 1.22.4 + resolve: 1.22.8 semver: 7.5.4 z-schema: 5.0.5 dev: true @@ -9451,7 +9657,7 @@ packages: /@rushstack/rig-package@0.4.0: resolution: {integrity: sha512-FnM1TQLJYwSiurP6aYSnansprK5l8WUK8VG38CmAaZs29ZeL1msjK0AP1VS4ejD33G0kE/2cpsPsS9jDenBMxw==} dependencies: - resolve: 1.22.6 + resolve: 1.22.8 strip-json-comments: 3.1.1 dev: true @@ -9643,6 +9849,118 @@ packages: '@swc/core-win32-ia32-msvc': 1.2.213 '@swc/core-win32-x64-msvc': 1.2.213 + /@tauri-apps/api@1.5.1: + resolution: {integrity: sha512-6unsZDOdlXTmauU3NhWhn+Cx0rODV+rvNvTdvolE5Kls5ybA6cqndQENDt1+FS0tF7ozCP66jwWoH6a5h90BrA==} + engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} + dev: false + + /@tauri-apps/cli-darwin-arm64@1.5.6: + resolution: {integrity: sha512-NNvG3XLtciCMsBahbDNUEvq184VZmOveTGOuy0So2R33b/6FDkuWaSgWZsR1mISpOuP034htQYW0VITCLelfqg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@tauri-apps/cli-darwin-x64@1.5.6: + resolution: {integrity: sha512-nkiqmtUQw3N1j4WoVjv81q6zWuZFhBLya/RNGUL94oafORloOZoSY0uTZJAoeieb3Y1YK0rCHSDl02MyV2Fi4A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@tauri-apps/cli-linux-arm-gnueabihf@1.5.6: + resolution: {integrity: sha512-z6SPx+axZexmWXTIVPNs4Tg7FtvdJl9EKxYN6JPjOmDZcqA13iyqWBQal2DA/GMZ1Xqo3vyJf6EoEaKaliymPQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@tauri-apps/cli-linux-arm64-gnu@1.5.6: + resolution: {integrity: sha512-QuQjMQmpsCbzBrmtQiG4uhnfAbdFx3nzm+9LtqjuZlurc12+Mj5MTgqQ3AOwQedH3f7C+KlvbqD2AdXpwTg7VA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@tauri-apps/cli-linux-arm64-musl@1.5.6: + resolution: {integrity: sha512-8j5dH3odweFeom7bRGlfzDApWVOT4jIq8/214Wl+JeiNVehouIBo9lZGeghZBH3XKFRwEvU23i7sRVjuh2s8mg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@tauri-apps/cli-linux-x64-gnu@1.5.6: + resolution: {integrity: sha512-gbFHYHfdEGW0ffk8SigDsoXks6USpilF6wR0nqB/JbWzbzFR/sBuLVNQlJl1RKNakyJHu+lsFxGy0fcTdoX8xA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@tauri-apps/cli-linux-x64-musl@1.5.6: + resolution: {integrity: sha512-9v688ogoLkeFYQNgqiSErfhTreLUd8B3prIBSYUt+x4+5Kcw91zWvIh+VSxL1n3KCGGsM7cuXhkGPaxwlEh1ug==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@tauri-apps/cli-win32-arm64-msvc@1.5.6: + resolution: {integrity: sha512-DRNDXFNZb6y5IZrw+lhTTA9l4wbzO4TNRBAlHAiXUrH+pRFZ/ZJtv5WEuAj9ocVSahVw2NaK5Yaold4NPAxHog==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@tauri-apps/cli-win32-ia32-msvc@1.5.6: + resolution: {integrity: sha512-oUYKNR/IZjF4fsOzRpw0xesl2lOjhsQEyWlgbpT25T83EU113Xgck9UjtI7xemNI/OPCv1tPiaM1e7/ABdg5iA==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@tauri-apps/cli-win32-x64-msvc@1.5.6: + resolution: {integrity: sha512-RmEf1os9C8//uq2hbjXi7Vgz9ne7798ZxqemAZdUwo1pv3oLVZSz1/IvZmUHPdy2e6zSeySqWu1D0Y3QRNN+dg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@tauri-apps/cli@1.5.6: + resolution: {integrity: sha512-k4Y19oVCnt7WZb2TnDzLqfs7o98Jq0tUoVMv+JQSzuRDJqaVu2xMBZ8dYplEn+EccdR5SOMyzaLBJWu38TVK1A==} + engines: {node: '>= 10'} + hasBin: true + optionalDependencies: + '@tauri-apps/cli-darwin-arm64': 1.5.6 + '@tauri-apps/cli-darwin-x64': 1.5.6 + '@tauri-apps/cli-linux-arm-gnueabihf': 1.5.6 + '@tauri-apps/cli-linux-arm64-gnu': 1.5.6 + '@tauri-apps/cli-linux-arm64-musl': 1.5.6 + '@tauri-apps/cli-linux-x64-gnu': 1.5.6 + '@tauri-apps/cli-linux-x64-musl': 1.5.6 + '@tauri-apps/cli-win32-arm64-msvc': 1.5.6 + '@tauri-apps/cli-win32-ia32-msvc': 1.5.6 + '@tauri-apps/cli-win32-x64-msvc': 1.5.6 + dev: false + /@tootallnate/once@1.1.2: resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} engines: {node: '>= 6'} @@ -9717,7 +10035,7 @@ packages: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.35 - '@types/node': 17.0.27 + '@types/node': 18.18.8 /@types/chai-subset@1.3.3: resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} @@ -9740,7 +10058,7 @@ packages: /@types/connect@3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 /@types/cookie-parser@1.4.3: resolution: {integrity: sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w==} @@ -9759,7 +10077,7 @@ packages: /@types/cors@2.8.13: resolution: {integrity: sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==} dependencies: - '@types/node': 18.11.10 + '@types/node': 18.18.8 dev: false /@types/debug@4.1.8: @@ -9813,7 +10131,7 @@ packages: /@types/express-serve-static-core@4.17.31: resolution: {integrity: sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 @@ -9842,13 +10160,13 @@ packages: /@types/fs-extra@9.0.13: resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 dev: true /@types/graceful-fs@4.1.5: resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 dev: true /@types/har-format@1.2.12: @@ -9906,25 +10224,31 @@ packages: /@types/jsonwebtoken@8.5.9: resolution: {integrity: sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 dev: true /@types/jsonwebtoken@9.0.1: resolution: {integrity: sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 dev: true /@types/jsonwebtoken@9.0.2: resolution: {integrity: sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q==} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 dev: false /@types/linkify-it@3.0.2: resolution: {integrity: sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==} dev: true + /@types/lodash-es@4.17.10: + resolution: {integrity: sha512-YJP+w/2khSBwbUSFdGsSqmDvmnN3cCKoPOL7Zjle6s30ZtemkkqhjVfFqGwPN7ASil5VyjE2GtyU/yqYY6mC0A==} + dependencies: + '@types/lodash': 4.14.200 + dev: true + /@types/lodash-es@4.17.6: resolution: {integrity: sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==} dependencies: @@ -9982,7 +10306,7 @@ packages: /@types/node-fetch@2.6.8: resolution: {integrity: sha512-nnH5lV9QCMPsbEVdTb5Y+F3GQxLSw1xQgIydrb2gSfEavRPs50FnMr+KUaa+LoPSqibm2N+ZZxH7lavZlAT4GA==} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 form-data: 4.0.0 dev: false @@ -9995,6 +10319,7 @@ packages: /@types/node@18.11.10: resolution: {integrity: sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==} + dev: true /@types/node@18.17.6: resolution: {integrity: sha512-fGmT/P7z7ecA6bv/ia5DlaWCH4YeZvAQMNpUhrJjtAhOhZfoxS1VLUgU2pdk63efSjQaOJWdXMuAJsws+8I6dg==} @@ -10022,7 +10347,7 @@ packages: /@types/oauth@0.9.1: resolution: {integrity: sha512-a1iY62/a3yhZ7qH7cNUsxoI3U/0Fe9+RnuFrpTKr+0WVOzbKlSLojShCKe20aOD1Sppv+i8Zlq0pLDuTJnwS4A==} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 dev: true /@types/object-path@0.11.1: @@ -10106,13 +10431,19 @@ packages: /@types/qs@6.9.7: resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} + /@types/ramda@0.28.15: + resolution: {integrity: sha512-FCaLNVZry65jW8x/FDnKgjgkCNQxgc5AYMQwdNn6yW5M+62R+0nt2Y36U43dTNora9hcquemfrY5gxhE5pcilQ==} + dependencies: + ts-toolbelt: 6.15.5 + dev: false + /@types/range-parser@1.2.4: resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} /@types/resolve@1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 dev: true /@types/semver@7.5.0: @@ -10123,7 +10454,7 @@ packages: resolution: {integrity: sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==} dependencies: '@types/mime': 3.0.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 /@types/splitpanes@2.2.1: resolution: {integrity: sha512-H5BgO6UdJRzz5ddRzuGvLBiPSPEuuHXb5ET+7avLLrEx1uc7f5Ut5oLMDQsfvGtHBBAFczt1QNYuDf27wHbvDQ==} @@ -10147,7 +10478,7 @@ packages: resolution: {integrity: sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==} dependencies: '@types/cookiejar': 2.1.2 - '@types/node': 17.0.27 + '@types/node': 18.18.8 dev: true /@types/supertest@2.0.12: @@ -10161,7 +10492,6 @@ packages: /@types/trusted-types@2.0.5: resolution: {integrity: sha512-I3pkr8j/6tmQtKV/ZzHtuaqYSQvyjGRKH4go60Rr0IDLlFxuRT5V32uvB1mecM5G1EVAUyF/4r4QZ1GHgz+mxA==} - dev: true /@types/ua-parser-js@0.7.36: resolution: {integrity: sha512-N1rW+njavs70y2cApeIw1vLMYXRwfBy+7trgavGuuTfOd7j1Yh7QTRc/yqsPl6ncokt72ZXuxEU0PiCp9bSwNQ==} @@ -10183,16 +10513,20 @@ packages: resolution: {integrity: sha512-4p9vcSmxAayx72yn70joFoL44c9MO/0+iVEBIQXe3v2h2SiAsEIo/G5v6ObFWvNKRFjbrVadNf9LqEEZeQPzdA==} dev: false + /@types/web-bluetooth@0.0.18: + resolution: {integrity: sha512-v/ZHEj9xh82usl8LMR3GarzFY1IrbXJw5L4QfQhokjRV91q+SelFqxQWSep1ucXEZ22+dSTwLFkXeur25sPIbw==} + dev: false + /@types/ws@8.5.3: resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 dev: false /@types/ws@8.5.5: resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 dev: true /@types/yargs-parser@21.0.0: @@ -10293,6 +10627,34 @@ packages: - supports-color dev: true + /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.47.0)(typescript@4.9.5): + resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.6.2 + '@typescript-eslint/parser': 5.62.0(eslint@8.47.0)(typescript@4.9.5) + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/type-utils': 5.62.0(eslint@8.47.0)(typescript@4.9.5) + '@typescript-eslint/utils': 5.62.0(eslint@8.47.0)(typescript@4.9.5) + debug: 4.3.4(supports-color@9.2.2) + eslint: 8.47.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare-lite: 1.4.0 + semver: 7.5.4 + tsutils: 3.21.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.47.0)(typescript@5.1.6): resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -10410,6 +10772,26 @@ packages: - supports-color dev: true + /@typescript-eslint/parser@5.62.0(eslint@8.47.0)(typescript@4.9.5): + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) + debug: 4.3.4(supports-color@9.2.2) + eslint: 8.47.0 + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/parser@5.62.0(eslint@8.47.0)(typescript@5.1.6): resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -10542,6 +10924,26 @@ packages: - supports-color dev: true + /@typescript-eslint/type-utils@5.62.0(eslint@8.47.0)(typescript@4.9.5): + resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) + '@typescript-eslint/utils': 5.62.0(eslint@8.47.0)(typescript@4.9.5) + debug: 4.3.4(supports-color@9.2.2) + eslint: 8.47.0 + tsutils: 3.21.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/type-utils@5.62.0(eslint@8.47.0)(typescript@5.1.6): resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -10665,6 +11067,27 @@ packages: - supports-color dev: true + /@typescript-eslint/typescript-estree@5.62.0(typescript@4.9.5): + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + debug: 4.3.4(supports-color@9.2.2) + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + tsutils: 3.21.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/typescript-estree@5.62.0(typescript@5.1.6): resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -10765,6 +11188,26 @@ packages: - typescript dev: true + /@typescript-eslint/utils@5.62.0(eslint@8.47.0)(typescript@4.9.5): + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.47.0) + '@types/json-schema': 7.0.12 + '@types/semver': 7.5.0 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) + eslint: 8.47.0 + eslint-scope: 5.1.1 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@typescript-eslint/utils@5.62.0(eslint@8.47.0)(typescript@5.1.6): resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -11014,6 +11457,22 @@ packages: vite: 3.2.4(@types/node@17.0.27)(sass@1.53.0)(terser@5.24.0) dev: false + /@vitejs/plugin-legacy@2.3.0(terser@5.24.0)(vite@4.5.0): + resolution: {integrity: sha512-Bh62i0gzQvvT8AeAAb78nOnqSYXypkRmQmOTImdPZ39meHR9e2une3AIFmVo4s1SDmcmJ6qj18Sa/lRc/14KaA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + terser: ^5.4.0 + vite: ^3.0.0 + dependencies: + '@babel/standalone': 7.20.0 + core-js: 3.32.1 + magic-string: 0.26.7 + regenerator-runtime: 0.13.11 + systemjs: 6.14.2 + terser: 5.24.0 + vite: 4.5.0(@types/node@18.18.8)(terser@5.24.0) + dev: true + /@vitejs/plugin-legacy@4.1.1(terser@5.24.0)(vite@4.4.9): resolution: {integrity: sha512-um3gbVouD2Q/g19C0qpDfHwveXDCAHzs8OC3e9g6aXpKoD1H14himgs7wkMnhAynBJy7QqUoZNAXDuqN8zLR2g==} engines: {node: ^14.18.0 || >=16.0.0} @@ -11055,6 +11514,17 @@ packages: vue: 3.3.4 dev: true + /@vitejs/plugin-vue@4.3.1(vite@4.5.0)(vue@3.3.4): + resolution: {integrity: sha512-tUBEtWcF7wFtII7ayNiLNDTCE1X1afySEo+XNVMNkFXaThENyCowIEX095QqbJZGTgoOcSVDJGlnde2NG4jtbQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.0.0 + vue: ^3.2.25 + dependencies: + vite: 4.5.0(@types/node@18.18.8)(terser@5.24.0) + vue: 3.3.4 + dev: true + /@vitest/expect@0.29.8: resolution: {integrity: sha512-xlcVXn5I5oTq6NiZSY3ykyWixBxr5mG8HYtjvpgg6KaqHm0mvhX18xuwl5YGxIRNt/A5jidd7CWcNHrSvgaQqQ==} dependencies: @@ -11327,6 +11797,27 @@ packages: - supports-color dev: true + /@vue/eslint-config-typescript@11.0.3(eslint-plugin-vue@9.17.0)(eslint@8.47.0)(typescript@4.9.5): + resolution: {integrity: sha512-dkt6W0PX6H/4Xuxg/BlFj5xHvksjpSlVjtkQCpaYJBIEuKj2hOVU7r+TIe+ysCwRYFz/lGqvklntRkCAibsbPw==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 + eslint-plugin-vue: ^9.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.47.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.62.0(eslint@8.47.0)(typescript@4.9.5) + eslint: 8.47.0 + eslint-plugin-vue: 9.17.0(eslint@8.47.0) + typescript: 4.9.5 + vue-eslint-parser: 9.3.1(eslint@8.47.0) + transitivePeerDependencies: + - supports-color + dev: true + /@vue/eslint-config-typescript@11.0.3(eslint-plugin-vue@9.17.0)(eslint@8.47.0)(typescript@5.1.6): resolution: {integrity: sha512-dkt6W0PX6H/4Xuxg/BlFj5xHvksjpSlVjtkQCpaYJBIEuKj2hOVU7r+TIe+ysCwRYFz/lGqvklntRkCAibsbPw==} engines: {node: ^14.17.0 || >=16.0.0} @@ -11367,6 +11858,25 @@ packages: vue-template-compiler: 2.7.14 dev: true + /@vue/language-core@1.8.8(typescript@4.9.5): + resolution: {integrity: sha512-i4KMTuPazf48yMdYoebTkgSOJdFraE4pQf0B+FTOFkbB+6hAfjrSou/UmYWRsWyZV6r4Rc6DDZdI39CJwL0rWw==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@volar/language-core': 1.10.1 + '@volar/source-map': 1.10.1 + '@vue/compiler-dom': 3.3.4 + '@vue/reactivity': 3.3.4 + '@vue/shared': 3.3.4 + minimatch: 9.0.3 + muggle-string: 0.3.1 + typescript: 4.9.5 + vue-template-compiler: 2.7.14 + dev: true + /@vue/language-core@1.8.8(typescript@5.1.6): resolution: {integrity: sha512-i4KMTuPazf48yMdYoebTkgSOJdFraE4pQf0B+FTOFkbB+6hAfjrSou/UmYWRsWyZV6r4Rc6DDZdI39CJwL0rWw==} peerDependencies: @@ -11473,6 +11983,15 @@ packages: - typescript dev: true + /@vue/typescript@1.8.8(typescript@4.9.5): + resolution: {integrity: sha512-jUnmMB6egu5wl342eaUH236v8tdcEPXXkPgj+eI/F6JwW/lb+yAU6U07ZbQ3MVabZRlupIlPESB7ajgAGixhow==} + dependencies: + '@volar/typescript': 1.10.1 + '@vue/language-core': 1.8.8(typescript@4.9.5) + transitivePeerDependencies: + - typescript + dev: true + /@vue/typescript@1.8.8(typescript@5.1.6): resolution: {integrity: sha512-jUnmMB6egu5wl342eaUH236v8tdcEPXXkPgj+eI/F6JwW/lb+yAU6U07ZbQ3MVabZRlupIlPESB7ajgAGixhow==} dependencies: @@ -11494,6 +12013,18 @@ packages: - vue dev: false + /@vueuse/core@10.5.0(vue@3.3.4): + resolution: {integrity: sha512-z/tI2eSvxwLRjOhDm0h/SXAjNm8N5ld6/SC/JQs6o6kpJ6Ya50LnEL8g5hoYu005i28L0zqB5L5yAl8Jl26K3A==} + dependencies: + '@types/web-bluetooth': 0.0.18 + '@vueuse/metadata': 10.5.0 + '@vueuse/shared': 10.5.0(vue@3.3.4) + vue-demi: 0.14.6(vue@3.3.4) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: false + /@vueuse/core@8.7.5(vue@3.2.45): resolution: {integrity: sha512-tqgzeZGoZcXzoit4kOGLWJibDMLp0vdm6ZO41SSUQhkhtrPhAg6dbIEPiahhUu6sZAmSYvVrZgEr5aKD51nrLA==} peerDependencies: @@ -11540,6 +12071,10 @@ packages: resolution: {integrity: sha512-Ema3YhNOa4swDsV0V7CEY5JXvK19JI/o1szFO1iWxdFg3vhdFtCtSTP26PCvbUpnUtNHBY2wx5y3WDXND5Pvnw==} dev: false + /@vueuse/metadata@10.5.0: + resolution: {integrity: sha512-fEbElR+MaIYyCkeM0SzWkdoMtOpIwO72x8WsZHRE7IggiOlILttqttM69AS13nrDxosnDBYdyy3C5mR1LCxHsw==} + dev: false + /@vueuse/metadata@8.7.5: resolution: {integrity: sha512-emJZKRQSaEnVqmlu39NpNp8iaW+bPC2kWykWoWOZMSlO/0QVEmO/rt8A5VhOEJTKLX3vwTevqbiRy9WJRwVOQg==} dev: false @@ -11557,6 +12092,15 @@ packages: - vue dev: false + /@vueuse/shared@10.5.0(vue@3.3.4): + resolution: {integrity: sha512-18iyxbbHYLst9MqU1X1QNdMHIjks6wC7XTVf0KNOv5es/Ms6gjVFCAAWTVP2JStuGqydg3DT+ExpFORUEi9yhg==} + dependencies: + vue-demi: 0.14.6(vue@3.3.4) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: false + /@vueuse/shared@8.7.5(vue@3.2.45): resolution: {integrity: sha512-THXPvMBFmg6Gf6AwRn/EdTh2mhqwjGsB2Yfp374LNQSQVKRHtnJ0I42bsZTn7nuEliBxqUrGQm/lN6qUHmhJLw==} peerDependencies: @@ -13326,6 +13870,10 @@ packages: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} dev: true + /cookie-es@1.0.0: + resolution: {integrity: sha512-mWYvfOLrfEc996hlKcdABeIiPHUPC6DM2QYZdGGOvhOTbA3tjm2eBwqlJpoFdjC89NI4Qt6h0Pu06Mp+1Pj5OQ==} + dev: false + /cookie-parser@1.4.6: resolution: {integrity: sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==} engines: {node: '>= 0.8.0'} @@ -13395,7 +13943,7 @@ packages: '@iarna/toml': 2.2.5 dev: false - /cosmiconfig-typescript-loader@2.0.2(@types/node@17.0.27)(cosmiconfig@7.0.1)(typescript@4.9.5): + /cosmiconfig-typescript-loader@2.0.2(@types/node@18.18.8)(cosmiconfig@7.0.1)(typescript@4.9.5): resolution: {integrity: sha512-KmE+bMjWMXJbkWCeY4FJX/npHuZPNr9XF9q9CIQ/bpFwi1qHfCmSiKarrCcRa0LO4fWjk93pVoeRtJAkTGcYNw==} engines: {node: '>=12', npm: '>=6'} peerDependencies: @@ -13403,9 +13951,9 @@ packages: cosmiconfig: '>=7' typescript: '>=3' dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 cosmiconfig: 7.0.1 - ts-node: 10.8.2(@types/node@17.0.27)(typescript@4.9.5) + ts-node: 10.8.2(@types/node@18.18.8)(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: - '@swc/core' @@ -14923,6 +15471,22 @@ packages: prettier-linter-helpers: 1.0.0 dev: true + /eslint-plugin-prettier@4.2.1(eslint@8.47.0)(prettier@2.8.4): + resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + eslint: '>=7.28.0' + eslint-config-prettier: '*' + prettier: '>=2.0.0' + peerDependenciesMeta: + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.47.0 + prettier: 2.8.4 + prettier-linter-helpers: 1.0.0 + dev: true + /eslint-plugin-prettier@5.0.0(eslint@8.47.0)(prettier@3.0.3): resolution: {integrity: sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==} engines: {node: ^14.18.0 || >=16.0.0} @@ -15950,7 +16514,6 @@ packages: /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - dev: true /function.prototype.name@1.1.5: resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} @@ -16693,7 +17256,6 @@ packages: engines: {node: '>= 0.4'} dependencies: function-bind: 1.1.2 - dev: true /he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} @@ -16757,6 +17319,7 @@ packages: - bufferutil - canvas - less + - lightningcss - sass - stylus - sugarss @@ -17324,7 +17887,6 @@ packages: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: hasown: 2.0.0 - dev: true /is-core-module@2.9.0: resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==} @@ -17745,7 +18307,7 @@ packages: '@jest/environment': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -17773,7 +18335,7 @@ packages: '@jest/expect': 29.4.1 '@jest/test-result': 29.4.1 '@jest/types': 29.5.0 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -17890,7 +18452,7 @@ packages: - utf-8-validate dev: true - /jest-config@29.4.1(@types/node@17.0.27)(ts-node@10.9.1): + /jest-config@29.4.1(@types/node@18.11.10)(ts-node@10.9.1): resolution: {integrity: sha512-g7p3q4NuXiM4hrS4XFATTkd+2z0Ml2RhFmFPM8c3WyKwVDNszbl4E7cV7WIx1YZeqqCtqbtTtZhGZWJlJqngzg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -17905,7 +18467,7 @@ packages: '@babel/core': 7.22.10 '@jest/test-sequencer': 29.4.1 '@jest/types': 29.5.0 - '@types/node': 17.0.27 + '@types/node': 18.11.10 babel-jest: 29.4.1(@babel/core@7.22.10) chalk: 4.1.2 ci-info: 3.3.2 @@ -17930,7 +18492,7 @@ packages: - supports-color dev: true - /jest-config@29.4.1(@types/node@18.11.10)(ts-node@10.9.1): + /jest-config@29.4.1(@types/node@18.18.8)(ts-node@10.9.1): resolution: {integrity: sha512-g7p3q4NuXiM4hrS4XFATTkd+2z0Ml2RhFmFPM8c3WyKwVDNszbl4E7cV7WIx1YZeqqCtqbtTtZhGZWJlJqngzg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -17945,7 +18507,7 @@ packages: '@babel/core': 7.22.10 '@jest/test-sequencer': 29.4.1 '@jest/types': 29.5.0 - '@types/node': 18.11.10 + '@types/node': 18.18.8 babel-jest: 29.4.1(@babel/core@7.22.10) chalk: 4.1.2 ci-info: 3.3.2 @@ -18043,7 +18605,7 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 jest-mock: 27.5.1 jest-util: 27.5.1 jsdom: 16.7.0 @@ -18061,7 +18623,7 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 jest-mock: 27.5.1 jest-util: 27.5.1 dev: true @@ -18073,7 +18635,7 @@ packages: '@jest/environment': 29.4.1 '@jest/fake-timers': 29.4.1 '@jest/types': 29.5.0 - '@types/node': 17.0.27 + '@types/node': 18.18.8 jest-mock: 29.4.1 jest-util: 29.5.0 dev: true @@ -18104,7 +18666,7 @@ packages: dependencies: '@jest/types': 27.5.1 '@types/graceful-fs': 4.1.5 - '@types/node': 17.0.27 + '@types/node': 18.18.8 anymatch: 3.1.3 fb-watchman: 2.0.1 graceful-fs: 4.2.11 @@ -18124,7 +18686,7 @@ packages: dependencies: '@jest/types': 29.5.0 '@types/graceful-fs': 4.1.5 - '@types/node': 17.0.27 + '@types/node': 18.18.8 anymatch: 3.1.3 fb-watchman: 2.0.1 graceful-fs: 4.2.10 @@ -18145,7 +18707,7 @@ packages: '@jest/source-map': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 co: 4.6.0 expect: 27.5.1 @@ -18309,7 +18871,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 dev: true /jest-mock@29.4.1: @@ -18317,7 +18879,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.5.0 - '@types/node': 17.0.27 + '@types/node': 18.18.8 jest-util: 29.5.0 dev: true @@ -18387,7 +18949,7 @@ packages: jest-pnp-resolver: 1.2.2(jest-resolve@27.5.1) jest-util: 27.5.1 jest-validate: 27.5.1 - resolve: 1.22.1 + resolve: 1.22.8 resolve.exports: 1.1.0 slash: 3.0.0 dev: true @@ -18402,7 +18964,7 @@ packages: jest-pnp-resolver: 1.2.2(jest-resolve@29.4.1) jest-util: 29.5.0 jest-validate: 29.4.1 - resolve: 1.22.1 + resolve: 1.22.8 resolve.exports: 2.0.0 slash: 3.0.0 dev: true @@ -18416,7 +18978,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 emittery: 0.8.1 graceful-fs: 4.2.11 @@ -18448,7 +19010,7 @@ packages: '@jest/test-result': 29.4.1 '@jest/transform': 29.4.1 '@jest/types': 29.5.0 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.10 @@ -18509,7 +19071,7 @@ packages: '@jest/test-result': 29.4.1 '@jest/transform': 29.4.1 '@jest/types': 29.5.0 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 @@ -18533,7 +19095,7 @@ packages: resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 graceful-fs: 4.2.11 dev: true @@ -18604,7 +19166,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 ci-info: 3.3.2 graceful-fs: 4.2.10 @@ -18616,7 +19178,7 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@jest/types': 28.1.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 ci-info: 3.3.2 graceful-fs: 4.2.11 @@ -18628,7 +19190,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.4.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 ci-info: 3.3.2 graceful-fs: 4.2.10 @@ -18640,7 +19202,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.5.0 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 ci-info: 3.3.2 graceful-fs: 4.2.10 @@ -18652,7 +19214,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.5.0 - '@types/node': 17.0.27 + '@types/node': 18.18.8 chalk: 4.1.2 ci-info: 3.3.2 graceful-fs: 4.2.11 @@ -18689,7 +19251,7 @@ packages: dependencies: '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 17.0.27 + '@types/node': 18.18.8 ansi-escapes: 4.3.2 chalk: 4.1.2 jest-util: 27.5.1 @@ -18702,7 +19264,7 @@ packages: dependencies: '@jest/test-result': 29.4.1 '@jest/types': 29.5.0 - '@types/node': 17.0.27 + '@types/node': 18.18.8 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -18714,7 +19276,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 merge-stream: 2.0.0 supports-color: 7.2.0 dev: true @@ -18723,7 +19285,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true @@ -18732,7 +19294,7 @@ packages: resolution: {integrity: sha512-O9doU/S1EBe+yp/mstQ0VpPwpv0Clgn68TkNwGxL6/usX/KUW9Arnn4ag8C3jc6qHcXznhsT5Na1liYzAsuAbQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 17.0.27 + '@types/node': 18.18.8 jest-util: 29.5.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -18850,7 +19412,7 @@ packages: optional: true dependencies: abab: 2.0.6 - acorn: 8.10.0 + acorn: 8.11.2 acorn-globals: 6.0.0 cssom: 0.4.4 cssstyle: 2.3.0 @@ -19110,7 +19672,6 @@ packages: /kolorist@1.5.1: resolution: {integrity: sha512-lxpCM3HTvquGxKGzHeknB/sUjuVoUElLlfYnXZT73K8geR9jQbroGlSCFBax9/0mpGoD3kzcMLnOlGQPJJNyqQ==} - dev: false /kolorist@1.7.0: resolution: {integrity: sha512-ymToLHqL02udwVdbkowNpzjFd6UzozMtshPQKVi5k1EjKRqKqBrOnE9QbLEb0/pV76SAiIT13hdL8R6suc+f3g==} @@ -19275,7 +19836,6 @@ packages: /local-pkg@0.4.2: resolution: {integrity: sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==} engines: {node: '>=14'} - dev: false /local-pkg@0.4.3: resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} @@ -21543,6 +22103,10 @@ packages: resolution: {integrity: sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA==} dev: false + /ramda@0.28.0: + resolution: {integrity: sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==} + dev: false + /random-bytes@1.0.0: resolution: {integrity: sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==} engines: {node: '>= 0.8'} @@ -21861,7 +22425,7 @@ packages: /resolve@1.19.0: resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} dependencies: - is-core-module: 2.13.0 + is-core-module: 2.13.1 path-parse: 1.0.7 dev: true @@ -21897,7 +22461,6 @@ packages: is-core-module: 2.13.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: true /restore-cursor@3.1.0: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} @@ -22088,6 +22651,12 @@ packages: dependencies: queue-microtask: 1.2.3 + /rxjs@7.5.6: + resolution: {integrity: sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==} + dependencies: + tslib: 2.6.2 + dev: false + /rxjs@7.6.0: resolution: {integrity: sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ==} dependencies: @@ -22303,6 +22872,14 @@ packages: /set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + /set-cookie-parser-es@1.0.5: + resolution: {integrity: sha512-nU27kVj4O6+a1wOOWB6uezxB9SWLCjEmYJr6eRBmkAZfOx/TBg2p0jkCl1dMgeYtmFRAJSGe4u9VN7dwPu9PRQ==} + dev: false + + /set-cookie-parser@2.6.0: + resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + dev: false + /set-function-length@1.1.1: resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==} engines: {node: '>= 0.4'} @@ -23563,7 +24140,7 @@ packages: - '@types/node' dev: false - /ts-node@10.8.2(@types/node@17.0.27)(typescript@4.9.5): + /ts-node@10.8.2(@types/node@18.18.8)(typescript@4.9.5): resolution: {integrity: sha512-LYdGnoGddf1D6v8REPtIH+5iq/gTDuZqv2/UJUU7tKjuEU8xVZorBM+buCGNjj+pGEud+sOoM4CX3/YzINpENA==} hasBin: true peerDependencies: @@ -23582,7 +24159,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.3 - '@types/node': 17.0.27 + '@types/node': 18.18.8 acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 @@ -23671,6 +24248,10 @@ packages: yn: 3.1.1 dev: false + /ts-toolbelt@6.15.5: + resolution: {integrity: sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==} + dev: false + /tsconfig-paths-webpack-plugin@4.1.0: resolution: {integrity: sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==} engines: {node: '>=10.13.0'} @@ -23778,6 +24359,16 @@ packages: typescript: 4.9.3 dev: true + /tsutils@3.21.0(typescript@4.9.5): + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 4.9.5 + dev: true + /tsutils@3.21.0(typescript@5.1.6): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} @@ -24126,6 +24717,38 @@ packages: - webpack dev: false + /unplugin-icons@0.14.9(rollup@2.79.1)(vite@4.5.0): + resolution: {integrity: sha512-vPyVfNREH88dP6gszdaoGkAEFPpiScXj1A8eWN905jQgT53A3tsiPEiqJjCHOUVcsUaREt2JSudzumFOsCA78A==} + peerDependencies: + '@svgr/core': '>=5.5.0' + '@vue/compiler-sfc': ^3.0.2 + vue-template-compiler: ^2.6.12 + vue-template-es2015-compiler: ^1.9.0 + peerDependenciesMeta: + '@svgr/core': + optional: true + '@vue/compiler-sfc': + optional: true + vue-template-compiler: + optional: true + vue-template-es2015-compiler: + optional: true + dependencies: + '@antfu/install-pkg': 0.1.1 + '@antfu/utils': 0.5.2 + '@iconify/utils': 1.0.33 + debug: 4.3.4(supports-color@9.2.2) + kolorist: 1.8.0 + local-pkg: 0.4.3 + unplugin: 0.9.5(rollup@2.79.1)(vite@4.5.0) + transitivePeerDependencies: + - esbuild + - rollup + - supports-color + - vite + - webpack + dev: true + /unplugin-icons@0.15.3(@vue/compiler-sfc@3.2.45): resolution: {integrity: sha512-YWgJqv5AahrokeOnta8uX/m1damZA6Rf6zPClgHg2Fa/45iyOe3Lj+Wn/Ba+CSsq9yBffn17YfKfJNyWCNZPvw==} peerDependencies: @@ -24205,7 +24828,7 @@ packages: local-pkg: 0.4.3 magic-string: 0.26.7 minimatch: 5.1.6 - resolve: 1.22.4 + resolve: 1.22.8 unplugin: 0.7.1(esbuild@0.19.5)(rollup@2.79.1)(vite@3.2.4) vue: 3.2.45 transitivePeerDependencies: @@ -24216,6 +24839,35 @@ packages: - webpack dev: true + /unplugin-vue-components@0.21.0(rollup@2.79.1)(vite@4.5.0)(vue@3.3.4): + resolution: {integrity: sha512-U7uOMNmRJ2eAv9CNjP8QRvxs6nAe3FVQUEIUphC1FGguBp3BWSLgGAcSHaX2nQy0gFoDY2mLF2M52W/t/eDaKg==} + engines: {node: '>=14'} + peerDependencies: + '@babel/parser': ^7.15.8 + vue: 2 || 3 + peerDependenciesMeta: + '@babel/parser': + optional: true + dependencies: + '@antfu/utils': 0.5.2 + '@rollup/pluginutils': 4.2.1 + chokidar: 3.5.3 + debug: 4.3.4(supports-color@9.2.2) + fast-glob: 3.3.1 + local-pkg: 0.4.3 + magic-string: 0.26.7 + minimatch: 5.1.6 + resolve: 1.22.8 + unplugin: 0.7.1(rollup@2.79.1)(vite@4.5.0) + vue: 3.3.4 + transitivePeerDependencies: + - esbuild + - rollup + - supports-color + - vite + - webpack + dev: true + /unplugin-vue-components@0.21.0(vite@3.2.4)(vue@3.2.45): resolution: {integrity: sha512-U7uOMNmRJ2eAv9CNjP8QRvxs6nAe3FVQUEIUphC1FGguBp3BWSLgGAcSHaX2nQy0gFoDY2mLF2M52W/t/eDaKg==} engines: {node: '>=14'} @@ -24234,7 +24886,7 @@ packages: local-pkg: 0.4.3 magic-string: 0.26.7 minimatch: 5.1.6 - resolve: 1.22.4 + resolve: 1.22.8 unplugin: 0.7.1(vite@3.2.4) vue: 3.2.45 transitivePeerDependencies: @@ -24320,7 +24972,7 @@ packages: webpack: optional: true dependencies: - acorn: 8.10.0 + acorn: 8.11.2 chokidar: 3.5.3 esbuild: 0.19.5 rollup: 2.79.1 @@ -24329,6 +24981,31 @@ packages: webpack-virtual-modules: 0.4.4 dev: true + /unplugin@0.7.1(rollup@2.79.1)(vite@4.5.0): + resolution: {integrity: sha512-Z6hNDXDNh9aimMkPU1mEjtk+2ova8gh0y7rJeJdGH1vWZOHwF2lLQiQ/R97rv9ymmzEQXsR2fyMet72T8jy6ew==} + peerDependencies: + esbuild: '>=0.13' + rollup: ^2.50.0 + vite: ^2.3.0 || ^3.0.0-0 + webpack: 4 || 5 + peerDependenciesMeta: + esbuild: + optional: true + rollup: + optional: true + vite: + optional: true + webpack: + optional: true + dependencies: + acorn: 8.11.2 + chokidar: 3.5.3 + rollup: 2.79.1 + vite: 4.5.0(@types/node@18.18.8)(terser@5.24.0) + webpack-sources: 3.2.3 + webpack-virtual-modules: 0.4.4 + dev: true + /unplugin@0.7.1(vite@3.2.4): resolution: {integrity: sha512-Z6hNDXDNh9aimMkPU1mEjtk+2ova8gh0y7rJeJdGH1vWZOHwF2lLQiQ/R97rv9ymmzEQXsR2fyMet72T8jy6ew==} peerDependencies: @@ -24346,13 +25023,38 @@ packages: webpack: optional: true dependencies: - acorn: 8.10.0 + acorn: 8.11.2 chokidar: 3.5.3 vite: 3.2.4(@types/node@18.18.8)(sass@1.58.0) webpack-sources: 3.2.3 webpack-virtual-modules: 0.4.4 dev: false + /unplugin@0.9.5(rollup@2.79.1)(vite@4.5.0): + resolution: {integrity: sha512-luraheyfxwtvkvHpsOvMNv7IjLdORTWKZp0gWYNHGLi2ImON3iIZOj464qEyyEwLA/EMt12fC415HW9zRpOfTg==} + peerDependencies: + esbuild: '>=0.13' + rollup: ^2.50.0 + vite: ^2.3.0 || ^3.0.0-0 + webpack: 4 || 5 + peerDependenciesMeta: + esbuild: + optional: true + rollup: + optional: true + vite: + optional: true + webpack: + optional: true + dependencies: + acorn: 8.11.2 + chokidar: 3.5.3 + rollup: 2.79.1 + vite: 4.5.0(@types/node@18.18.8)(terser@5.24.0) + webpack-sources: 3.2.3 + webpack-virtual-modules: 0.4.4 + dev: true + /unplugin@0.9.5(vite@3.2.4): resolution: {integrity: sha512-luraheyfxwtvkvHpsOvMNv7IjLdORTWKZp0gWYNHGLi2ImON3iIZOj464qEyyEwLA/EMt12fC415HW9zRpOfTg==} peerDependencies: @@ -24370,7 +25072,7 @@ packages: webpack: optional: true dependencies: - acorn: 8.10.0 + acorn: 8.11.2 chokidar: 3.5.3 vite: 3.2.4(@types/node@18.18.8)(sass@1.58.0) webpack-sources: 3.2.3 @@ -24598,10 +25300,11 @@ packages: pathe: 0.2.0 source-map: 0.6.1 source-map-support: 0.5.21 - vite: 3.2.4(@types/node@17.0.27)(sass@1.53.0)(terser@5.24.0) + vite: 4.5.0(@types/node@17.0.27)(sass@1.53.0)(terser@5.24.0) transitivePeerDependencies: - '@types/node' - less + - lightningcss - sass - stylus - sugarss @@ -24619,10 +25322,11 @@ packages: mlly: 1.2.0 pathe: 1.1.0 picocolors: 1.0.0 - vite: 4.0.4(@types/node@18.17.6) + vite: 4.5.0(@types/node@18.17.6)(sass@1.66.0)(terser@5.24.0) transitivePeerDependencies: - '@types/node' - less + - lightningcss - sass - stylus - sugarss @@ -24640,7 +25344,7 @@ packages: mlly: 1.4.0 pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.4.9(@types/node@18.17.6)(sass@1.66.0)(terser@5.24.0) + vite: 4.5.0(@types/node@18.17.6)(sass@1.66.0)(terser@5.24.0) transitivePeerDependencies: - '@types/node' - less @@ -24806,6 +25510,16 @@ packages: vite: 4.4.9(@types/node@17.0.27)(sass@1.66.0)(terser@5.24.0) dev: true + /vite-plugin-fonts@0.6.0(vite@4.5.0): + resolution: {integrity: sha512-dV6nnLEju8k5EmvlBH6egxkVZ+rgc5zWsJr9+cNRXBMEDnpRGHcZPI260UEDNg2yB99wSTNER2eduEvZFbMIGw==} + deprecated: renamed to `unplugin-fonts`, see https://github.com/cssninjaStudio/unplugin-fonts/releases/tag/v1.0.0 + peerDependencies: + vite: ^2.0.0 || ^3.0.0 + dependencies: + fast-glob: 3.3.1 + vite: 4.5.0(@types/node@18.18.8)(terser@5.24.0) + dev: true + /vite-plugin-html-config@1.0.10(vite@3.2.4): resolution: {integrity: sha512-qJCVKC/mR4BIy4EG7AHQ3nGo1BF+3fOjVIka0kXKQMlxT12dl9G5YKmjhLohDzySijOb03R2PzYiAdavwKkqQQ==} engines: {node: '>=12.0.0'} @@ -24824,6 +25538,15 @@ packages: vite: 4.4.9(@types/node@17.0.27)(sass@1.66.0)(terser@5.24.0) dev: true + /vite-plugin-html-config@1.0.11(vite@4.5.0): + resolution: {integrity: sha512-hUybhgI+/LQQ5q6xoMMsTvI4PBuQD/Wv6Z1vtDPVWjanS8weCIexXuLLYNGD/93f0v8W2hpNfXpmxgpZMahJ0g==} + engines: {node: '>=12.0.0'} + peerDependencies: + vite: '>=2.0.0' + dependencies: + vite: 4.5.0(@types/node@18.18.8)(terser@5.24.0) + dev: true + /vite-plugin-inspect@0.7.38(rollup@2.79.1)(vite@4.4.9): resolution: {integrity: sha512-+p6pJVtBOLGv+RBrcKAFUdx+euizg0bjL35HhPyM0MjtKlqoC5V9xkCmO9Ctc8JrTyXqODbHqiLWJKumu5zJ7g==} engines: {node: '>=14'} @@ -24848,6 +25571,30 @@ packages: - supports-color dev: true + /vite-plugin-inspect@0.7.38(rollup@2.79.1)(vite@4.5.0): + resolution: {integrity: sha512-+p6pJVtBOLGv+RBrcKAFUdx+euizg0bjL35HhPyM0MjtKlqoC5V9xkCmO9Ctc8JrTyXqODbHqiLWJKumu5zJ7g==} + engines: {node: '>=14'} + peerDependencies: + '@nuxt/kit': '*' + vite: ^3.1.0 || ^4.0.0 + peerDependenciesMeta: + '@nuxt/kit': + optional: true + dependencies: + '@antfu/utils': 0.7.6 + '@rollup/pluginutils': 5.0.3(rollup@2.79.1) + debug: 4.3.4(supports-color@9.2.2) + error-stack-parser-es: 0.1.1 + fs-extra: 11.1.1 + open: 9.1.0 + picocolors: 1.0.0 + sirv: 2.0.3 + vite: 4.5.0(@types/node@18.18.8)(terser@5.24.0) + transitivePeerDependencies: + - rollup + - supports-color + dev: true + /vite-plugin-inspect@0.7.38(rollup@3.29.4)(vite@4.4.9): resolution: {integrity: sha512-+p6pJVtBOLGv+RBrcKAFUdx+euizg0bjL35HhPyM0MjtKlqoC5V9xkCmO9Ctc8JrTyXqODbHqiLWJKumu5zJ7g==} engines: {node: '>=14'} @@ -24921,6 +25668,29 @@ packages: - supports-color dev: true + /vite-plugin-pages@0.26.0(vite@4.5.0): + resolution: {integrity: sha512-yJZvwHEt7puYIf19S89IvkDsWPjWleSied4H8hmdW6i8buCA93z1UAU1ipW1d8fNKrC4FzXsUHHbPm6+kl1p9w==} + peerDependencies: + '@vue/compiler-sfc': ^2.7.0 || ^3.0.0 + vite: ^2.0.0 || ^3.0.0-0 + peerDependenciesMeta: + '@vue/compiler-sfc': + optional: true + dependencies: + '@types/debug': 4.1.8 + debug: 4.3.4(supports-color@9.2.2) + deep-equal: 2.2.2 + extract-comments: 1.1.0 + fast-glob: 3.3.1 + json5: 2.2.3 + local-pkg: 0.4.3 + picocolors: 1.0.0 + vite: 4.5.0(@types/node@18.18.8)(terser@5.24.0) + yaml: 2.3.1 + transitivePeerDependencies: + - supports-color + dev: true + /vite-plugin-pages@0.31.0(@vue/compiler-sfc@3.3.4)(vite@4.4.9): resolution: {integrity: sha512-fw3onBfVTXQI7rOzAbSZhmfwvk50+3qNnGZpERjmD93c8nEjrGLyd53eFXYMxcJV4KA1vzi4qIHt2+6tS4dEMw==} peerDependencies: @@ -24963,6 +25733,24 @@ packages: - supports-color dev: true + /vite-plugin-pwa@0.13.1(vite@4.5.0)(workbox-build@6.6.0)(workbox-window@6.6.0): + resolution: {integrity: sha512-NR3dIa+o2hzlzo4lF4Gu0cYvoMjSw2DdRc6Epw1yjmCqWaGuN86WK9JqZie4arNlE1ZuWT3CLiMdiX5wcmmUmg==} + peerDependencies: + vite: ^3.1.0 + workbox-build: ^6.5.4 + workbox-window: ^6.5.4 + dependencies: + debug: 4.3.4(supports-color@9.2.2) + fast-glob: 3.3.1 + pretty-bytes: 6.1.1 + rollup: 2.79.1 + vite: 4.5.0(@types/node@18.18.8)(terser@5.24.0) + workbox-build: 6.6.0 + workbox-window: 6.6.0 + transitivePeerDependencies: + - supports-color + dev: true + /vite-plugin-pwa@0.16.4(vite@4.4.9)(workbox-build@7.0.0)(workbox-window@7.0.0): resolution: {integrity: sha512-lmwHFIs9zI2H9bXJld/zVTbCqCQHZ9WrpyDMqosICDV0FVnCJwniX1NMDB79HGTIZzOQkY4gSZaVTJTw6maz/Q==} engines: {node: '>=16.0.0'} @@ -24981,6 +25769,19 @@ packages: - supports-color dev: true + /vite-plugin-static-copy@0.12.0(vite@4.5.0): + resolution: {integrity: sha512-5a8hCjYJdf/rl8s7ct/YWt97gXdGPGNSOoJtkY5IYhbnSq04X1gTt5GpFHKfAxhHoed1Grfw3Ed13t7AjJi7gw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^3.0.0 + dependencies: + chokidar: 3.5.3 + fast-glob: 3.3.1 + fs-extra: 10.1.0 + picocolors: 1.0.0 + vite: 4.5.0(@types/node@18.18.8)(terser@5.24.0) + dev: true + /vite-plugin-static-copy@0.17.0(vite@4.4.9): resolution: {integrity: sha512-2HpNbHfDt8SDy393AGXh9llHkc8FJMQkI8s3T5WsH3SWLMO+f5cFIyPErl4yGKU9Uh3Vaqsd4lHZYTf042fQ2A==} engines: {node: ^14.18.0 || >=16.0.0} @@ -25028,6 +25829,23 @@ packages: - supports-color dev: true + /vite-plugin-vue-layouts@0.7.0(vite@4.5.0)(vue-router@4.2.5)(vue@3.3.4): + resolution: {integrity: sha512-k5XDmRNFo4M/GmUjhbRXj2WmJiFcGoVI8l/uZ72RHyRDQr4wE/6Zq/KFq0lqXomWQxTSzakQRUswzNwtvZLE8A==} + peerDependencies: + vite: ^2.5.0 || ^3.0.0-0 + vue: ^2.6.12 || ^3.2.4 + vue-router: ^3.5.1 || ^ 4.0.11 + dependencies: + '@vue/compiler-sfc': 3.3.4 + debug: 4.3.4(supports-color@9.2.2) + fast-glob: 3.3.1 + vite: 4.5.0(@types/node@18.18.8)(terser@5.24.0) + vue: 3.3.4 + vue-router: 4.2.5(vue@3.3.4) + transitivePeerDependencies: + - supports-color + dev: true + /vite-plugin-vue-layouts@0.8.0(vite@4.4.9)(vue-router@4.2.4)(vue@3.3.4): resolution: {integrity: sha512-UZW2nSV2LraTSe7gsAL46hfdi7a0X1RvkGGoJVtA2O8beu7anzpXFwQLou8+kHy31CzVycT4gIPySBsHhtBN5g==} peerDependencies: @@ -25090,6 +25908,20 @@ packages: - supports-color dev: true + /vite-plugin-windicss@1.9.1(vite@4.5.0): + resolution: {integrity: sha512-CWm1b/tXVCJTbEGn4oB8B7Gev9xDuY9k4E/KiJqDuLYspBUFQyZKPF2mSZ3DfNdojsfqgzxu9ervqvlb9jJ7fw==} + peerDependencies: + vite: ^2.0.1 || ^3.0.0 || ^4.0.0 + dependencies: + '@windicss/plugin-utils': 1.9.1 + debug: 4.3.4(supports-color@9.2.2) + kolorist: 1.8.0 + vite: 4.5.0(@types/node@18.18.8)(terser@5.24.0) + windicss: 3.5.6 + transitivePeerDependencies: + - supports-color + dev: true + /vite@3.2.4(@types/node@17.0.27)(sass@1.53.0)(terser@5.24.0): resolution: {integrity: sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==} engines: {node: ^14.18.0 || >=16.0.0} @@ -25340,6 +26172,81 @@ packages: fsevents: 2.3.3 dev: true + /vite@4.5.0(@types/node@18.17.6)(sass@1.66.0)(terser@5.24.0): + resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.17.6 + esbuild: 0.18.20 + postcss: 8.4.31 + rollup: 3.29.4 + sass: 1.66.0 + terser: 5.24.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vite@4.5.0(@types/node@18.18.8)(terser@5.24.0): + resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.18.8 + esbuild: 0.18.20 + postcss: 8.4.31 + rollup: 3.29.4 + terser: 5.24.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /vitest@0.29.8: resolution: {integrity: sha512-JIAVi2GK5cvA6awGpH0HvH/gEG9PZ0a/WoxdiV3PmqK+3CjQMf8c+J/Vhv4mdZ2nRyXFw66sAg6qz7VNkaHfDQ==} engines: {node: '>=v14.16.0'} @@ -25397,6 +26304,7 @@ packages: why-is-node-running: 2.2.2 transitivePeerDependencies: - less + - lightningcss - sass - stylus - sugarss @@ -25475,7 +26383,7 @@ packages: deprecated: The library contains critical security issues and should not be used for production! The maintenance of the project has been discontinued. Consider migrating your code to isolated-vm. hasBin: true dependencies: - acorn: 8.10.0 + acorn: 8.11.2 acorn-walk: 8.2.0 dev: false @@ -25688,6 +26596,21 @@ packages: vue: 3.3.4 dev: false + /vue-demi@0.14.6(vue@3.3.4): + resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + vue: 3.3.4 + dev: false + /vue-eslint-parser@9.1.0(eslint@8.29.0): resolution: {integrity: sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==} engines: {node: ^14.17.0 || >=16.0.0} @@ -25866,6 +26789,18 @@ packages: typescript: 4.9.3 dev: true + /vue-tsc@1.8.8(typescript@4.9.5): + resolution: {integrity: sha512-bSydNFQsF7AMvwWsRXD7cBIXaNs/KSjvzWLymq/UtKE36697sboX4EccSHFVxvgdBlI1frYPc/VMKJNB7DFeDQ==} + hasBin: true + peerDependencies: + typescript: '*' + dependencies: + '@vue/language-core': 1.8.8(typescript@4.9.5) + '@vue/typescript': 1.8.8(typescript@4.9.5) + semver: 7.5.4 + typescript: 4.9.5 + dev: true + /vue-tsc@1.8.8(typescript@5.1.6): resolution: {integrity: sha512-bSydNFQsF7AMvwWsRXD7cBIXaNs/KSjvzWLymq/UtKE36697sboX4EccSHFVxvgdBlI1frYPc/VMKJNB7DFeDQ==} hasBin: true @@ -26376,7 +27311,6 @@ packages: /workbox-core@6.6.0: resolution: {integrity: sha512-GDtFRF7Yg3DD859PMbPAYPeJyg5gJYXuBQAC+wyrWuuXgpfoOrIQIvFRZnQ7+czTIQjIr1DhLEGFzZanAT/3bQ==} - dev: true /workbox-core@7.0.0: resolution: {integrity: sha512-81JkAAZtfVP8darBpfRTovHg8DGAVrKFgHpOArZbdFd78VqHr5Iw65f2guwjE2NlCFbPFDoez3D3/6ZvhI/rwQ==} @@ -26526,7 +27460,6 @@ packages: dependencies: '@types/trusted-types': 2.0.5 workbox-core: 6.6.0 - dev: true /workbox-window@7.0.0: resolution: {integrity: sha512-j7P/bsAWE/a7sxqTzXo3P2ALb1reTfZdvVp6OJ/uLr/C2kZAMvjeWGm8V4htQhor7DOvYg0sSbFN2+flT5U0qA==} @@ -26916,8 +27849,15 @@ packages: /zod@3.22.2: resolution: {integrity: sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==} - dev: true /zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false + + github.com/tauri-apps/tauri-plugin-store/6e19887b1bdea9b921a31993d72396a350731e07: + resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-store/tar.gz/6e19887b1bdea9b921a31993d72396a350731e07} + name: tauri-plugin-store-api + version: 0.0.0 + dependencies: + '@tauri-apps/api': 1.5.1 + dev: false