Compare commits

..

24 Commits

Author SHA1 Message Date
Nivedin
1afabec3a8 fix: add @click on row instead on cell 2023-08-23 12:22:48 +05:30
Joel Jacob Stephen
c9927ef032 refactor: can receive list from table 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
d9bddb1094 refactor: updated tables to use custom slot implementations 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
14a3dad013 refactor: updated the table story book component 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
c683a5471c chore: remove unnecessary changes 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
1b215be382 chore: removed cellstyles and modified some styles 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
9e0453d3bb refactor: new slots for table and body 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
caf5d7ec0f refactor: added slots for table header and body 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
6e7db67c9b refactor: modified components to suit the changes in the table component 2023-08-23 11:13:42 +05:30
Anwarul Islam
27eb7690ba feat: prevent row click on certain cell 2023-08-23 11:13:42 +05:30
Anwarul Islam
a875ab1ca9 feat: generic table implementation 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
d5e4211347 refactor: renamed emit event and removed action slot 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
24f32d79b3 refactor: remove badges and subtitle implementation from table and used slots instead 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
f48083ca7e style: removed unnecessary styles 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
c0ee3d02ae chore: updated types 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
fb234cbf7e refactor: introduced variants to table in storybook 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
c19a8be6e9 refactor: replaced text with i18n strings 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
b0e29b2e6c feat: introduced table component in storybook 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
0962718b0c chore: updated types 2023-08-23 11:13:42 +05:30
Joel Jacob Stephen
a08760351d style: modified styles in different components of sh admin 2023-08-23 11:13:41 +05:30
Joel Jacob Stephen
55c191305c refactor: replaced table with hoppsmarttable in invited users page and code cleanup 2023-08-23 11:13:18 +05:30
Joel Jacob Stephen
dfabf7e8bc feat: added the ability to add subtitles in multiple columns 2023-08-23 11:13:18 +05:30
Joel Jacob Stephen
834efce1c2 feat: introducing badges and subtitle support for table 2023-08-23 11:13:18 +05:30
Joel Jacob Stephen
cdbc580ada feat: introducing smart table to hopp ui 2023-08-23 11:13:18 +05:30
81 changed files with 1323 additions and 2219 deletions

View File

@@ -1,2 +1 @@
node_modules
**/*/node_modules
*/**/node_modules

View File

@@ -1,11 +0,0 @@
:3000 {
try_files {path} /
root * /site/selfhost-web
file_server
}
:3100 {
try_files {path} /
root * /site/sh-admin
file_server
}

View File

@@ -1,72 +0,0 @@
#!/usr/local/bin/node
// @ts-check
import { execSync, spawn } from "child_process"
import fs from "fs"
import process from "process"
function runChildProcessWithPrefix(command, args, prefix) {
const childProcess = spawn(command, args);
childProcess.stdout.on('data', (data) => {
const output = data.toString().trim().split('\n');
output.forEach((line) => {
console.log(`${prefix} | ${line}`);
});
});
childProcess.stderr.on('data', (data) => {
const error = data.toString().trim().split('\n');
error.forEach((line) => {
console.error(`${prefix} | ${line}`);
});
});
childProcess.on('close', (code) => {
console.log(`${prefix} Child process exited with code ${code}`);
});
childProcess.on('error', (stuff) => {
console.log("error")
console.log(stuff)
})
return childProcess
}
const envFileContent = Object.entries(process.env)
.filter(([env]) => env.startsWith("VITE_"))
.map(([env, val]) => `${env}=${
(val.startsWith("\"") && val.endsWith("\""))
? val
: `"${val}"`
}`)
.join("\n")
fs.writeFileSync("build.env", envFileContent)
execSync(`npx import-meta-env -x build.env -e build.env -p "/site/**/*"`)
fs.rmSync("build.env")
const caddyProcess = runChildProcessWithPrefix("caddy", ["run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"], "App/Admin Dashboard Caddy")
const backendProcess = runChildProcessWithPrefix("pnpm", ["run", "start:prod"], "Backend Server")
caddyProcess.on("exit", (code) => {
console.log(`Exiting process because Caddy Server exited with code ${code}`)
process.exit(code)
})
backendProcess.on("exit", (code) => {
console.log(`Exiting process because Backend Server exited with code ${code}`)
process.exit(code)
})
process.on('SIGINT', () => {
console.log("SIGINT received, exiting...")
caddyProcess.kill("SIGINT")
backendProcess.kill("SIGINT")
process.exit(0)
})

View File

@@ -7,103 +7,6 @@ services:
# This service runs the backend app in the port 3170
hoppscotch-backend:
container_name: hoppscotch-backend
build:
dockerfile: prod.Dockerfile
context: .
target: backend
env_file:
- ./.env
restart: always
environment:
# Edit the below line to match your PostgresDB URL if you have an outside DB (make sure to update the .env file as well)
- DATABASE_URL=postgresql://postgres:testpass@hoppscotch-db:5432/hoppscotch?connect_timeout=300
- PORT=3170
volumes:
# Uncomment the line below when modifying code. Only applicable when using the "dev" target.
# - ./packages/hoppscotch-backend/:/usr/src/app
- /usr/src/app/node_modules/
depends_on:
hoppscotch-db:
condition: service_healthy
ports:
- "3170:3170"
# The main hoppscotch app. This will be hosted at port 3000
# NOTE: To do TLS or play around with how the app is hosted, you can look into the Caddyfile for
# the SH admin dashboard server at packages/hoppscotch-selfhost-web/Caddyfile
hoppscotch-app:
container_name: hoppscotch-app
build:
dockerfile: prod.Dockerfile
context: .
target: app
env_file:
- ./.env
depends_on:
- hoppscotch-backend
ports:
- "3000:8080"
# The Self Host dashboard for managing the app. This will be hosted at port 3100
# NOTE: To do TLS or play around with how the app is hosted, you can look into the Caddyfile for
# the SH admin dashboard server at packages/hoppscotch-sh-admin/Caddyfile
hoppscotch-sh-admin:
container_name: hoppscotch-sh-admin
build:
dockerfile: prod.Dockerfile
context: .
target: sh_admin
env_file:
- ./.env
depends_on:
- hoppscotch-backend
ports:
- "3100:8080"
# The service that spins up all 3 services at once in one container
hoppscotch-aio:
container_name: hoppscotch-aio
build:
dockerfile: prod.Dockerfile
context: .
target: aio
env_file:
- ./.env
depends_on:
hoppscotch-db:
condition: service_healthy
ports:
- "3000:3000"
- "3100:3100"
- "3170:3170"
# The preset DB service, you can delete/comment the below lines if
# you are using an external postgres instance
# This will be exposed at port 5432
hoppscotch-db:
image: postgres:15
ports:
- "5432:5432"
user: postgres
environment:
# The default user defined by the docker image
POSTGRES_USER: postgres
# NOTE: Please UPDATE THIS PASSWORD!
POSTGRES_PASSWORD: testpass
POSTGRES_DB: hoppscotch
healthcheck:
test:
[
"CMD-SHELL",
"sh -c 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}'"
]
interval: 5s
timeout: 5s
retries: 10
# All the services listed below are deprececated
hoppscotch-old-backend:
container_name: hoppscotch-old-backend
build:
dockerfile: packages/hoppscotch-backend/Dockerfile
context: .
@@ -125,26 +28,54 @@ services:
ports:
- "3170:3000"
hoppscotch-old-app:
container_name: hoppscotch-old-app
# The main hoppscotch app. This will be hosted at port 3000
# NOTE: To do TLS or play around with how the app is hosted, you can look into the Caddyfile for
# the SH admin dashboard server at packages/hoppscotch-selfhost-web/Caddyfile
hoppscotch-app:
container_name: hoppscotch-app
build:
dockerfile: packages/hoppscotch-selfhost-web/Dockerfile
context: .
env_file:
- ./.env
depends_on:
- hoppscotch-old-backend
- hoppscotch-backend
ports:
- "3000:8080"
hoppscotch-old-sh-admin:
container_name: hoppscotch-old-sh-admin
# The Self Host dashboard for managing the app. This will be hosted at port 3100
# NOTE: To do TLS or play around with how the app is hosted, you can look into the Caddyfile for
# the SH admin dashboard server at packages/hoppscotch-sh-admin/Caddyfile
hoppscotch-sh-admin:
container_name: hoppscotch-sh-admin
build:
dockerfile: packages/hoppscotch-sh-admin/Dockerfile
context: .
env_file:
- ./.env
depends_on:
- hoppscotch-old-backend
- hoppscotch-backend
ports:
- "3100:8080"
# The preset DB service, you can delete/comment the below lines if
# you are using an external postgres instance
# This will be exposed at port 5432
hoppscotch-db:
image: postgres:15
ports:
- "5432:5432"
user: postgres
environment:
# The default user defined by the docker image
POSTGRES_USER: postgres
# NOTE: Please UPDATE THIS PASSWORD!
POSTGRES_PASSWORD: testpass
POSTGRES_DB: hoppscotch
healthcheck:
test: ["CMD-SHELL", "sh -c 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}'"]
interval: 5s
timeout: 5s
retries: 10

View File

@@ -1,14 +0,0 @@
#!/bin/bash
curlCheck() {
if ! curl -s --head "$1" | head -n 1 | grep -q "HTTP/1.[01] [23].."; then
echo "URL request failed!"
exit 1
else
echo "URL request succeeded!"
fi
}
curlCheck "http://localhost:3000"
curlCheck "http://localhost:3100"
curlCheck "http://localhost:3170/ping"

View File

@@ -33,7 +33,7 @@
"@nestjs/passport": "^9.0.0",
"@nestjs/platform-express": "^9.2.1",
"@nestjs/throttler": "^4.0.0",
"@prisma/client": "^4.16.2",
"@prisma/client": "^4.7.1",
"apollo-server-express": "^3.11.1",
"apollo-server-plugin-base": "^3.7.1",
"argon2": "^0.30.3",
@@ -57,7 +57,7 @@
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"passport-microsoft": "^1.0.0",
"prisma": "^4.16.2",
"prisma": "^4.7.1",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.6.0"

View File

@@ -5,7 +5,7 @@ datasource db {
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "debian-openssl-1.1.x", "debian-openssl-3.0.x"]
binaryTargets = ["native", "debian-openssl-1.1.x"]
}
model Team {

View File

@@ -1,9 +0,0 @@
import { Controller, Get } from '@nestjs/common';
@Controller('ping')
export class AppController {
@Get()
ping(): string {
return 'Success';
}
}

View File

@@ -19,7 +19,6 @@ import { UserCollectionModule } from './user-collection/user-collection.module';
import { ShortcodeModule } from './shortcode/shortcode.module';
import { COOKIES_NOT_FOUND } from './errors';
import { ThrottlerModule } from '@nestjs/throttler';
import { AppController } from './app.controller';
@Module({
imports: [
@@ -82,6 +81,5 @@ import { AppController } from './app.controller';
ShortcodeModule,
],
providers: [GQLComplexityPlugin],
controllers: [AppController],
})
export class AppModule {}

View File

@@ -150,7 +150,7 @@ export class ShortcodeService implements UserDataHandler, OnModuleInit {
orderBy: {
createdOn: 'desc',
},
skip: args.cursor ? 1 : 0,
skip: 1,
take: args.take,
cursor: args.cursor ? { id: args.cursor } : undefined,
});

View File

@@ -24,8 +24,6 @@ beforeEach(() => {
mockPubSub.publish.mockClear();
});
const date = new Date();
describe('UserHistoryService', () => {
describe('fetchUserHistory', () => {
test('Should return a list of users REST history if exists', async () => {
@@ -402,7 +400,7 @@ describe('UserHistoryService', () => {
request: [{}],
responseMetadata: [{}],
reqType: ReqType.REST,
executedOn: date,
executedOn: new Date(),
isStarred: false,
});
@@ -412,7 +410,7 @@ describe('UserHistoryService', () => {
request: JSON.stringify([{}]),
responseMetadata: JSON.stringify([{}]),
reqType: ReqType.REST,
executedOn: date,
executedOn: new Date(),
isStarred: false,
};

View File

@@ -166,6 +166,12 @@ a {
@apply truncate;
@apply sm:inline-flex;
}
.env-icon {
@apply transition;
@apply inline-flex;
@apply items-center;
}
}
.tippy-svg-arrow {
@@ -326,7 +332,7 @@ pre.ace_editor {
@apply after:font-icon;
@apply after:text-current;
@apply after:right-3;
@apply after:content-["\e5cf"];
@apply after:content-["\e313"];
@apply after:text-lg;
}
@@ -481,10 +487,6 @@ pre.ace_editor {
}
}
.cm-scroller {
@apply overscroll-y-auto;
}
.cm-editor {
.cm-line::selection {
@apply bg-accentDark #{!important};
@@ -574,9 +576,9 @@ details[open] summary .indicator {
}
.gql-operation-not-highlight {
@apply opacity-50;
opacity: 0.5;
}
.gql-operation-highlight {
@apply opacity-100;
opacity: 1;
}

View File

@@ -159,8 +159,8 @@
},
"context_menu": {
"set_environment_variable": "Set as variable",
"add_parameters": "Add to parameters",
"open_request_in_new_tab": "Open request in new tab"
"add_parameter": "Add to parameter",
"open_link_in_new_tab": "Open link in new tab"
},
"count": {
"header": "Header {count}",
@@ -185,6 +185,7 @@
"folder": "Folder is empty",
"headers": "This request does not have any headers",
"history": "History is empty",
"history_suggestions": "History does not have any matching entries",
"invites": "Invite list is empty",
"members": "Team is empty",
"parameters": "This request does not have any parameters",
@@ -194,6 +195,7 @@
"schema": "Connect to a GraphQL endpoint to view schema",
"shortcodes": "Shortcodes are empty",
"subscription": "Subscriptions are empty",
"suggestions": "No matching suggestions found",
"team_name": "Team name empty",
"teams": "You don't belong to any teams",
"tests": "There are no tests for this request"
@@ -606,7 +608,7 @@
"delete_method": "Select DELETE method",
"get_method": "Select GET method",
"head_method": "Select HEAD method",
"rename": "Rename Request",
"rename": "Rename Current Request",
"import_curl": "Import cURL",
"show_code": "Generate code snippet",
"method": "Method",
@@ -649,11 +651,11 @@
},
"spotlight": {
"general": {
"help_menu": "Help and support",
"help_menu": "Open help and support menu",
"chat": "Chat with support",
"open_docs": "Read Documentation",
"open_keybindings": "Keyboard shortcuts",
"social": "Social",
"open_keybindings": "Open keyboard shortcuts",
"social": "Social links and GitHub",
"title": "General"
},
"miscellaneous": {
@@ -661,18 +663,15 @@
"title": "Miscellaneous"
},
"request": {
"switch_to": "Switch to",
"select_method": "Select method",
"save_as_new": "Save as new request",
"tab_parameters": "Parameters tab",
"tab_body": "Body tab",
"tab_headers": "Headers tab",
"tab_authorization": "Authorization tab",
"tab_pre_request_script": "Pre-request script tab",
"tab_tests": "Tests tab"
"tab_parameters": "Open parameters tab",
"tab_body": "Open body tab",
"tab_headers": "Open headers tab",
"tab_authorization": "Open authorization tab",
"tab_pre_request_script": "Open pre-request script tab",
"tab_tests": "Open tests tab"
},
"response": {
"copy": "Copy response",
"copy": "Copy response as JSON",
"download": "Download response as file",
"title": "Response"
},
@@ -695,7 +694,6 @@
"title": "Teams"
},
"tab": {
"duplicate": "Duplicate tab",
"close_current": "Close current tab",
"close_others": "Close other tabs",
"new_tab": "Open a new tab",
@@ -707,7 +705,9 @@
"interface": "Interface",
"interceptor": "Interceptor"
},
"change_interceptor": "Change Interceptor",
"change_language": "Change Language",
"install_extension": "Install Browser Extension",
"settings": {
"theme": {
"black": "Black Mode",
@@ -721,7 +721,8 @@
"size_lg": "Change to Large"
},
"change_interceptor": "Change Interceptor",
"change_language": "Change Language"
"change_language": "Change Language",
"install_extension": "Install Browser Extension"
}
},
"sse": {

View File

@@ -5,221 +5,218 @@
// Read more: https://github.com/vuejs/core/pull/3399
export {}
declare module 'vue' {
declare module "vue" {
export interface GlobalComponents {
AppActionHandler: typeof import('./components/app/ActionHandler.vue')['default']
AppAnnouncement: typeof import('./components/app/Announcement.vue')['default']
AppContextMenu: typeof import('./components/app/ContextMenu.vue')['default']
AppDeveloperOptions: typeof import('./components/app/DeveloperOptions.vue')['default']
AppFooter: typeof import('./components/app/Footer.vue')['default']
AppGitHubStarButton: typeof import('./components/app/GitHubStarButton.vue')['default']
AppHeader: typeof import('./components/app/Header.vue')['default']
AppInspection: typeof import('./components/app/Inspection.vue')['default']
AppInterceptor: typeof import('./components/app/Interceptor.vue')['default']
AppLogo: typeof import('./components/app/Logo.vue')['default']
AppOptions: typeof import('./components/app/Options.vue')['default']
AppPaneLayout: typeof import('./components/app/PaneLayout.vue')['default']
AppShare: typeof import('./components/app/Share.vue')['default']
AppShortcuts: typeof import('./components/app/Shortcuts.vue')['default']
AppShortcutsEntry: typeof import('./components/app/ShortcutsEntry.vue')['default']
AppShortcutsPrompt: typeof import('./components/app/ShortcutsPrompt.vue')['default']
AppSidenav: typeof import('./components/app/Sidenav.vue')['default']
AppSocial: typeof import('./components/app/Social.vue')['default']
AppSpotlight: typeof import('./components/app/spotlight/index.vue')['default']
AppSpotlightEntry: typeof import('./components/app/spotlight/Entry.vue')['default']
AppSpotlightEntryGQLHistory: typeof import('./components/app/spotlight/entry/GQLHistory.vue')['default']
AppSpotlightEntryGQLRequest: typeof import('./components/app/spotlight/entry/GQLRequest.vue')['default']
AppSpotlightEntryIconSelected: typeof import('./components/app/spotlight/entry/IconSelected.vue')['default']
AppSpotlightEntryRESTHistory: typeof import('./components/app/spotlight/entry/RESTHistory.vue')['default']
AppSpotlightEntryRESTRequest: typeof import('./components/app/spotlight/entry/RESTRequest.vue')['default']
AppSupport: typeof import('./components/app/Support.vue')['default']
ButtonPrimary: typeof import('./../../hoppscotch-ui/src/components/button/Primary.vue')['default']
ButtonSecondary: typeof import('./../../hoppscotch-ui/src/components/button/Secondary.vue')['default']
Collections: typeof import('./components/collections/index.vue')['default']
CollectionsAdd: typeof import('./components/collections/Add.vue')['default']
CollectionsAddFolder: typeof import('./components/collections/AddFolder.vue')['default']
CollectionsAddRequest: typeof import('./components/collections/AddRequest.vue')['default']
CollectionsCollection: typeof import('./components/collections/Collection.vue')['default']
CollectionsEdit: typeof import('./components/collections/Edit.vue')['default']
CollectionsEditFolder: typeof import('./components/collections/EditFolder.vue')['default']
CollectionsEditRequest: typeof import('./components/collections/EditRequest.vue')['default']
CollectionsGraphql: typeof import('./components/collections/graphql/index.vue')['default']
CollectionsGraphqlAdd: typeof import('./components/collections/graphql/Add.vue')['default']
CollectionsGraphqlAddFolder: typeof import('./components/collections/graphql/AddFolder.vue')['default']
CollectionsGraphqlAddRequest: typeof import('./components/collections/graphql/AddRequest.vue')['default']
CollectionsGraphqlCollection: typeof import('./components/collections/graphql/Collection.vue')['default']
CollectionsGraphqlEdit: typeof import('./components/collections/graphql/Edit.vue')['default']
CollectionsGraphqlEditFolder: typeof import('./components/collections/graphql/EditFolder.vue')['default']
CollectionsGraphqlEditRequest: typeof import('./components/collections/graphql/EditRequest.vue')['default']
CollectionsGraphqlFolder: typeof import('./components/collections/graphql/Folder.vue')['default']
CollectionsGraphqlImportExport: typeof import('./components/collections/graphql/ImportExport.vue')['default']
CollectionsGraphqlRequest: typeof import('./components/collections/graphql/Request.vue')['default']
CollectionsImportExport: typeof import('./components/collections/ImportExport.vue')['default']
CollectionsMyCollections: typeof import('./components/collections/MyCollections.vue')['default']
CollectionsRequest: typeof import('./components/collections/Request.vue')['default']
CollectionsSaveRequest: typeof import('./components/collections/SaveRequest.vue')['default']
CollectionsTeamCollections: typeof import('./components/collections/TeamCollections.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']
EnvironmentsMy: typeof import('./components/environments/my/index.vue')['default']
EnvironmentsMyDetails: typeof import('./components/environments/my/Details.vue')['default']
EnvironmentsMyEnvironment: typeof import('./components/environments/my/Environment.vue')['default']
EnvironmentsSelector: typeof import('./components/environments/Selector.vue')['default']
EnvironmentsTeams: typeof import('./components/environments/teams/index.vue')['default']
EnvironmentsTeamsDetails: typeof import('./components/environments/teams/Details.vue')['default']
EnvironmentsTeamsEnvironment: typeof import('./components/environments/teams/Environment.vue')['default']
FirebaseLogin: typeof import('./components/firebase/Login.vue')['default']
FirebaseLogout: typeof import('./components/firebase/Logout.vue')['default']
GraphqlAuthorization: typeof import('./components/graphql/Authorization.vue')['default']
GraphqlField: typeof import('./components/graphql/Field.vue')['default']
GraphqlHeaders: typeof import('./components/graphql/Headers.vue')['default']
GraphqlQuery: typeof import('./components/graphql/Query.vue')['default']
GraphqlRequest: typeof import('./components/graphql/Request.vue')['default']
GraphqlRequestOptions: typeof import('./components/graphql/RequestOptions.vue')['default']
GraphqlRequestTab: typeof import('./components/graphql/RequestTab.vue')['default']
GraphqlResponse: typeof import('./components/graphql/Response.vue')['default']
GraphqlSidebar: typeof import('./components/graphql/Sidebar.vue')['default']
GraphqlSubscriptionLog: typeof import('./components/graphql/SubscriptionLog.vue')['default']
GraphqlTabHead: typeof import('./components/graphql/TabHead.vue')['default']
GraphqlType: typeof import('./components/graphql/Type.vue')['default']
GraphqlTypeLink: typeof import('./components/graphql/TypeLink.vue')['default']
GraphqlVariable: typeof import('./components/graphql/Variable.vue')['default']
History: typeof import('./components/history/index.vue')['default']
HistoryGraphqlCard: typeof import('./components/history/graphql/Card.vue')['default']
HistoryRestCard: typeof import('./components/history/rest/Card.vue')['default']
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']
HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture']
HoppSmartPlaceholder: typeof import('@hoppscotch/ui')['HoppSmartPlaceholder']
HoppSmartProgressRing: typeof import('@hoppscotch/ui')['HoppSmartProgressRing']
HoppSmartRadio: typeof import('@hoppscotch/ui')['HoppSmartRadio']
HoppSmartRadioGroup: typeof import('@hoppscotch/ui')['HoppSmartRadioGroup']
HoppSmartSlideOver: typeof import('@hoppscotch/ui')['HoppSmartSlideOver']
HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner']
HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab']
HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs']
HoppSmartToggle: typeof import('@hoppscotch/ui')['HoppSmartToggle']
HoppSmartTree: typeof import('@hoppscotch/ui')['HoppSmartTree']
HoppSmartWindow: typeof import('@hoppscotch/ui')['HoppSmartWindow']
HoppSmartWindows: typeof import('@hoppscotch/ui')['HoppSmartWindows']
HttpAuthorization: typeof import('./components/http/Authorization.vue')['default']
HttpAuthorizationApiKey: typeof import('./components/http/authorization/ApiKey.vue')['default']
HttpAuthorizationBasic: typeof import('./components/http/authorization/Basic.vue')['default']
HttpBody: typeof import('./components/http/Body.vue')['default']
HttpBodyParameters: typeof import('./components/http/BodyParameters.vue')['default']
HttpCodegenModal: typeof import('./components/http/CodegenModal.vue')['default']
HttpHeaders: typeof import('./components/http/Headers.vue')['default']
HttpImportCurl: typeof import('./components/http/ImportCurl.vue')['default']
HttpOAuth2Authorization: typeof import('./components/http/OAuth2Authorization.vue')['default']
HttpParameters: typeof import('./components/http/Parameters.vue')['default']
HttpPreRequestScript: typeof import('./components/http/PreRequestScript.vue')['default']
HttpRawBody: typeof import('./components/http/RawBody.vue')['default']
HttpReqChangeConfirmModal: typeof import('./components/http/ReqChangeConfirmModal.vue')['default']
HttpRequest: typeof import('./components/http/Request.vue')['default']
HttpRequestOptions: typeof import('./components/http/RequestOptions.vue')['default']
HttpRequestTab: typeof import('./components/http/RequestTab.vue')['default']
HttpResponse: typeof import('./components/http/Response.vue')['default']
HttpResponseMeta: typeof import('./components/http/ResponseMeta.vue')['default']
HttpSidebar: typeof import('./components/http/Sidebar.vue')['default']
HttpTabHead: typeof import('./components/http/TabHead.vue')['default']
HttpTestResult: typeof import('./components/http/TestResult.vue')['default']
HttpTestResultEntry: typeof import('./components/http/TestResultEntry.vue')['default']
HttpTestResultEnv: typeof import('./components/http/TestResultEnv.vue')['default']
HttpTestResultReport: typeof import('./components/http/TestResultReport.vue')['default']
HttpTests: typeof import('./components/http/Tests.vue')['default']
HttpURLEncodedParams: typeof import('./components/http/URLEncodedParams.vue')['default']
IconLucideActivity: typeof import('~icons/lucide/activity')['default']
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']
IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default']
IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
IconLucideGlobe: typeof import('~icons/lucide/globe')['default']
IconLucideHelpCircle: typeof import('~icons/lucide/help-circle')['default']
IconLucideInbox: typeof import('~icons/lucide/inbox')['default']
IconLucideInfo: typeof import('~icons/lucide/info')['default']
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']
LensesRenderersAudioLensRenderer: typeof import('./components/lenses/renderers/AudioLensRenderer.vue')['default']
LensesRenderersHTMLLensRenderer: typeof import('./components/lenses/renderers/HTMLLensRenderer.vue')['default']
LensesRenderersImageLensRenderer: typeof import('./components/lenses/renderers/ImageLensRenderer.vue')['default']
LensesRenderersJSONLensRenderer: typeof import('./components/lenses/renderers/JSONLensRenderer.vue')['default']
LensesRenderersPDFLensRenderer: typeof import('./components/lenses/renderers/PDFLensRenderer.vue')['default']
LensesRenderersRawLensRenderer: typeof import('./components/lenses/renderers/RawLensRenderer.vue')['default']
LensesRenderersVideoLensRenderer: typeof import('./components/lenses/renderers/VideoLensRenderer.vue')['default']
LensesRenderersXMLLensRenderer: typeof import('./components/lenses/renderers/XMLLensRenderer.vue')['default']
LensesResponseBodyRenderer: typeof import('./components/lenses/ResponseBodyRenderer.vue')['default']
ProfileShortcode: typeof import('./components/profile/Shortcode.vue')['default']
ProfileShortcodes: typeof import('./components/profile/Shortcodes.vue')['default']
ProfileUserDelete: typeof import('./components/profile/UserDelete.vue')['default']
RealtimeCommunication: typeof import('./components/realtime/Communication.vue')['default']
RealtimeConnectionConfig: typeof import('./components/realtime/ConnectionConfig.vue')['default']
RealtimeLog: typeof import('./components/realtime/Log.vue')['default']
RealtimeLogEntry: typeof import('./components/realtime/LogEntry.vue')['default']
RealtimeSubscription: typeof import('./components/realtime/Subscription.vue')['default']
SettingsExtension: typeof import('./components/settings/Extension.vue')['default']
SettingsProxy: typeof import('./components/settings/Proxy.vue')['default']
SmartAccentModePicker: typeof import('./components/smart/AccentModePicker.vue')['default']
SmartAnchor: typeof import('./../../hoppscotch-ui/src/components/smart/Anchor.vue')['default']
SmartAutoComplete: typeof import('./../../hoppscotch-ui/src/components/smart/AutoComplete.vue')['default']
SmartChangeLanguage: typeof import('./components/smart/ChangeLanguage.vue')['default']
SmartCheckbox: typeof import('./../../hoppscotch-ui/src/components/smart/Checkbox.vue')['default']
SmartColorModePicker: typeof import('./components/smart/ColorModePicker.vue')['default']
SmartConfirmModal: typeof import('./../../hoppscotch-ui/src/components/smart/ConfirmModal.vue')['default']
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']
SmartLink: typeof import('./../../hoppscotch-ui/src/components/smart/Link.vue')['default']
SmartModal: typeof import('./../../hoppscotch-ui/src/components/smart/Modal.vue')['default']
SmartPicture: typeof import('./../../hoppscotch-ui/src/components/smart/Picture.vue')['default']
SmartPlaceholder: typeof import('./../../hoppscotch-ui/src/components/smart/Placeholder.vue')['default']
SmartProgressRing: typeof import('./../../hoppscotch-ui/src/components/smart/ProgressRing.vue')['default']
SmartRadio: typeof import('./../../hoppscotch-ui/src/components/smart/Radio.vue')['default']
SmartRadioGroup: typeof import('./../../hoppscotch-ui/src/components/smart/RadioGroup.vue')['default']
SmartSlideOver: typeof import('./../../hoppscotch-ui/src/components/smart/SlideOver.vue')['default']
SmartSpinner: typeof import('./../../hoppscotch-ui/src/components/smart/Spinner.vue')['default']
SmartTab: typeof import('./../../hoppscotch-ui/src/components/smart/Tab.vue')['default']
SmartTabs: typeof import('./../../hoppscotch-ui/src/components/smart/Tabs.vue')['default']
SmartToggle: typeof import('./../../hoppscotch-ui/src/components/smart/Toggle.vue')['default']
SmartTree: typeof import('./../../hoppscotch-ui/src/components/smart/Tree.vue')['default']
SmartTreeBranch: typeof import('./../../hoppscotch-ui/src/components/smart/TreeBranch.vue')['default']
SmartWindow: typeof import('./../../hoppscotch-ui/src/components/smart/Window.vue')['default']
SmartWindows: typeof import('./../../hoppscotch-ui/src/components/smart/Windows.vue')['default']
TabPrimary: typeof import('./components/tab/Primary.vue')['default']
TabSecondary: typeof import('./components/tab/Secondary.vue')['default']
Teams: typeof import('./components/teams/index.vue')['default']
TeamsAdd: typeof import('./components/teams/Add.vue')['default']
TeamsEdit: typeof import('./components/teams/Edit.vue')['default']
TeamsInvite: typeof import('./components/teams/Invite.vue')['default']
TeamsMemberStack: typeof import('./components/teams/MemberStack.vue')['default']
TeamsModal: typeof import('./components/teams/Modal.vue')['default']
TeamsTeam: typeof import('./components/teams/Team.vue')['default']
Tippy: typeof import('vue-tippy')['Tippy']
WorkspaceCurrent: typeof import('./components/workspace/Current.vue')['default']
WorkspaceSelector: typeof import('./components/workspace/Selector.vue')['default']
AppActionHandler: typeof import("./components/app/ActionHandler.vue")["default"]
AppAnnouncement: typeof import("./components/app/Announcement.vue")["default"]
AppContextMenu: typeof import("./components/app/ContextMenu.vue")["default"]
AppDeveloperOptions: typeof import("./components/app/DeveloperOptions.vue")["default"]
AppFooter: typeof import("./components/app/Footer.vue")["default"]
AppGitHubStarButton: typeof import("./components/app/GitHubStarButton.vue")["default"]
AppHeader: typeof import("./components/app/Header.vue")["default"]
AppInspection: typeof import("./components/app/Inspection.vue")["default"]
AppInterceptor: typeof import("./components/app/Interceptor.vue")["default"]
AppLogo: typeof import("./components/app/Logo.vue")["default"]
AppOptions: typeof import("./components/app/Options.vue")["default"]
AppPaneLayout: typeof import("./components/app/PaneLayout.vue")["default"]
AppShare: typeof import("./components/app/Share.vue")["default"]
AppShortcuts: typeof import("./components/app/Shortcuts.vue")["default"]
AppShortcutsEntry: typeof import("./components/app/ShortcutsEntry.vue")["default"]
AppShortcutsPrompt: typeof import("./components/app/ShortcutsPrompt.vue")["default"]
AppSidenav: typeof import("./components/app/Sidenav.vue")["default"]
AppSocial: typeof import("./components/app/Social.vue")["default"]
AppSpotlight: typeof import("./components/app/spotlight/index.vue")["default"]
AppSpotlightEntry: typeof import("./components/app/spotlight/Entry.vue")["default"]
AppSpotlightEntryGQLHistory: typeof import("./components/app/spotlight/entry/GQLHistory.vue")["default"]
AppSpotlightEntryGQLRequest: typeof import("./components/app/spotlight/entry/GQLRequest.vue")["default"]
AppSpotlightEntryRESTHistory: typeof import("./components/app/spotlight/entry/RESTHistory.vue")["default"]
AppSpotlightEntryRESTRequest: typeof import("./components/app/spotlight/entry/RESTRequest.vue")["default"]
AppSupport: typeof import("./components/app/Support.vue")["default"]
ButtonPrimary: typeof import("./../../hoppscotch-ui/src/components/button/Primary.vue")["default"]
ButtonSecondary: typeof import("./../../hoppscotch-ui/src/components/button/Secondary.vue")["default"]
Collections: typeof import("./components/collections/index.vue")["default"]
CollectionsAdd: typeof import("./components/collections/Add.vue")["default"]
CollectionsAddFolder: typeof import("./components/collections/AddFolder.vue")["default"]
CollectionsAddRequest: typeof import("./components/collections/AddRequest.vue")["default"]
CollectionsCollection: typeof import("./components/collections/Collection.vue")["default"]
CollectionsEdit: typeof import("./components/collections/Edit.vue")["default"]
CollectionsEditFolder: typeof import("./components/collections/EditFolder.vue")["default"]
CollectionsEditRequest: typeof import("./components/collections/EditRequest.vue")["default"]
CollectionsGraphql: typeof import("./components/collections/graphql/index.vue")["default"]
CollectionsGraphqlAdd: typeof import("./components/collections/graphql/Add.vue")["default"]
CollectionsGraphqlAddFolder: typeof import("./components/collections/graphql/AddFolder.vue")["default"]
CollectionsGraphqlAddRequest: typeof import("./components/collections/graphql/AddRequest.vue")["default"]
CollectionsGraphqlCollection: typeof import("./components/collections/graphql/Collection.vue")["default"]
CollectionsGraphqlEdit: typeof import("./components/collections/graphql/Edit.vue")["default"]
CollectionsGraphqlEditFolder: typeof import("./components/collections/graphql/EditFolder.vue")["default"]
CollectionsGraphqlEditRequest: typeof import("./components/collections/graphql/EditRequest.vue")["default"]
CollectionsGraphqlFolder: typeof import("./components/collections/graphql/Folder.vue")["default"]
CollectionsGraphqlImportExport: typeof import("./components/collections/graphql/ImportExport.vue")["default"]
CollectionsGraphqlRequest: typeof import("./components/collections/graphql/Request.vue")["default"]
CollectionsImportExport: typeof import("./components/collections/ImportExport.vue")["default"]
CollectionsMyCollections: typeof import("./components/collections/MyCollections.vue")["default"]
CollectionsRequest: typeof import("./components/collections/Request.vue")["default"]
CollectionsSaveRequest: typeof import("./components/collections/SaveRequest.vue")["default"]
CollectionsTeamCollections: typeof import("./components/collections/TeamCollections.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"]
EnvironmentsMy: typeof import("./components/environments/my/index.vue")["default"]
EnvironmentsMyDetails: typeof import("./components/environments/my/Details.vue")["default"]
EnvironmentsMyEnvironment: typeof import("./components/environments/my/Environment.vue")["default"]
EnvironmentsSelector: typeof import("./components/environments/Selector.vue")["default"]
EnvironmentsTeams: typeof import("./components/environments/teams/index.vue")["default"]
EnvironmentsTeamsDetails: typeof import("./components/environments/teams/Details.vue")["default"]
EnvironmentsTeamsEnvironment: typeof import("./components/environments/teams/Environment.vue")["default"]
FirebaseLogin: typeof import("./components/firebase/Login.vue")["default"]
FirebaseLogout: typeof import("./components/firebase/Logout.vue")["default"]
GraphqlAuthorization: typeof import("./components/graphql/Authorization.vue")["default"]
GraphqlField: typeof import("./components/graphql/Field.vue")["default"]
GraphqlHeaders: typeof import("./components/graphql/Headers.vue")["default"]
GraphqlQuery: typeof import("./components/graphql/Query.vue")["default"]
GraphqlRequest: typeof import("./components/graphql/Request.vue")["default"]
GraphqlRequestOptions: typeof import("./components/graphql/RequestOptions.vue")["default"]
GraphqlRequestTab: typeof import("./components/graphql/RequestTab.vue")["default"]
GraphqlResponse: typeof import("./components/graphql/Response.vue")["default"]
GraphqlSidebar: typeof import("./components/graphql/Sidebar.vue")["default"]
GraphqlSubscriptionLog: typeof import("./components/graphql/SubscriptionLog.vue")["default"]
GraphqlType: typeof import("./components/graphql/Type.vue")["default"]
GraphqlTypeLink: typeof import("./components/graphql/TypeLink.vue")["default"]
GraphqlVariable: typeof import("./components/graphql/Variable.vue")["default"]
History: typeof import("./components/history/index.vue")["default"]
HistoryGraphqlCard: typeof import("./components/history/graphql/Card.vue")["default"]
HistoryRestCard: typeof import("./components/history/rest/Card.vue")["default"]
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"]
HoppSmartPicture: typeof import("@hoppscotch/ui")["HoppSmartPicture"]
HoppSmartPlaceholder: typeof import("@hoppscotch/ui")["HoppSmartPlaceholder"]
HoppSmartProgressRing: typeof import("@hoppscotch/ui")["HoppSmartProgressRing"]
HoppSmartRadio: typeof import("@hoppscotch/ui")["HoppSmartRadio"]
HoppSmartRadioGroup: typeof import("@hoppscotch/ui")["HoppSmartRadioGroup"]
HoppSmartSlideOver: typeof import("@hoppscotch/ui")["HoppSmartSlideOver"]
HoppSmartSpinner: typeof import("@hoppscotch/ui")["HoppSmartSpinner"]
HoppSmartTab: typeof import("@hoppscotch/ui")["HoppSmartTab"]
HoppSmartTabs: typeof import("@hoppscotch/ui")["HoppSmartTabs"]
HoppSmartToggle: typeof import("@hoppscotch/ui")["HoppSmartToggle"]
HoppSmartTree: typeof import("@hoppscotch/ui")["HoppSmartTree"]
HoppSmartWindow: typeof import("@hoppscotch/ui")["HoppSmartWindow"]
HoppSmartWindows: typeof import("@hoppscotch/ui")["HoppSmartWindows"]
HttpAuthorization: typeof import("./components/http/Authorization.vue")["default"]
HttpAuthorizationApiKey: typeof import("./components/http/authorization/ApiKey.vue")["default"]
HttpAuthorizationBasic: typeof import("./components/http/authorization/Basic.vue")["default"]
HttpBody: typeof import("./components/http/Body.vue")["default"]
HttpBodyParameters: typeof import("./components/http/BodyParameters.vue")["default"]
HttpCodegenModal: typeof import("./components/http/CodegenModal.vue")["default"]
HttpHeaders: typeof import("./components/http/Headers.vue")["default"]
HttpImportCurl: typeof import("./components/http/ImportCurl.vue")["default"]
HttpOAuth2Authorization: typeof import("./components/http/OAuth2Authorization.vue")["default"]
HttpParameters: typeof import("./components/http/Parameters.vue")["default"]
HttpPreRequestScript: typeof import("./components/http/PreRequestScript.vue")["default"]
HttpRawBody: typeof import("./components/http/RawBody.vue")["default"]
HttpReqChangeConfirmModal: typeof import("./components/http/ReqChangeConfirmModal.vue")["default"]
HttpRequest: typeof import("./components/http/Request.vue")["default"]
HttpRequestOptions: typeof import("./components/http/RequestOptions.vue")["default"]
HttpRequestTab: typeof import("./components/http/RequestTab.vue")["default"]
HttpResponse: typeof import("./components/http/Response.vue")["default"]
HttpResponseMeta: typeof import("./components/http/ResponseMeta.vue")["default"]
HttpSidebar: typeof import("./components/http/Sidebar.vue")["default"]
HttpTabHead: typeof import("./components/http/TabHead.vue")["default"]
HttpTestResult: typeof import("./components/http/TestResult.vue")["default"]
HttpTestResultEntry: typeof import("./components/http/TestResultEntry.vue")["default"]
HttpTestResultEnv: typeof import("./components/http/TestResultEnv.vue")["default"]
HttpTestResultReport: typeof import("./components/http/TestResultReport.vue")["default"]
HttpTests: typeof import("./components/http/Tests.vue")["default"]
HttpURLEncodedParams: typeof import("./components/http/URLEncodedParams.vue")["default"]
IconLucideActivity: typeof import("~icons/lucide/activity")["default"]
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"]
IconLucideCheckCircle: typeof import("~icons/lucide/check-circle")["default"]
IconLucideChevronRight: typeof import("~icons/lucide/chevron-right")["default"]
IconLucideGlobe: typeof import("~icons/lucide/globe")["default"]
IconLucideHelpCircle: typeof import("~icons/lucide/help-circle")["default"]
IconLucideInbox: typeof import("~icons/lucide/inbox")["default"]
IconLucideInfo: typeof import("~icons/lucide/info")["default"]
IconLucideLayers: typeof import("~icons/lucide/layers")["default"]
IconLucideListEnd: typeof import("~icons/lucide/list-end")["default"]
IconLucideMinus: typeof import("~icons/lucide/minus")["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"]
LensesRenderersAudioLensRenderer: typeof import("./components/lenses/renderers/AudioLensRenderer.vue")["default"]
LensesRenderersHTMLLensRenderer: typeof import("./components/lenses/renderers/HTMLLensRenderer.vue")["default"]
LensesRenderersImageLensRenderer: typeof import("./components/lenses/renderers/ImageLensRenderer.vue")["default"]
LensesRenderersJSONLensRenderer: typeof import("./components/lenses/renderers/JSONLensRenderer.vue")["default"]
LensesRenderersPDFLensRenderer: typeof import("./components/lenses/renderers/PDFLensRenderer.vue")["default"]
LensesRenderersRawLensRenderer: typeof import("./components/lenses/renderers/RawLensRenderer.vue")["default"]
LensesRenderersVideoLensRenderer: typeof import("./components/lenses/renderers/VideoLensRenderer.vue")["default"]
LensesRenderersXMLLensRenderer: typeof import("./components/lenses/renderers/XMLLensRenderer.vue")["default"]
LensesResponseBodyRenderer: typeof import("./components/lenses/ResponseBodyRenderer.vue")["default"]
ProfileShortcode: typeof import("./components/profile/Shortcode.vue")["default"]
ProfileShortcodes: typeof import("./components/profile/Shortcodes.vue")["default"]
ProfileUserDelete: typeof import("./components/profile/UserDelete.vue")["default"]
RealtimeCommunication: typeof import("./components/realtime/Communication.vue")["default"]
RealtimeConnectionConfig: typeof import("./components/realtime/ConnectionConfig.vue")["default"]
RealtimeLog: typeof import("./components/realtime/Log.vue")["default"]
RealtimeLogEntry: typeof import("./components/realtime/LogEntry.vue")["default"]
RealtimeSubscription: typeof import("./components/realtime/Subscription.vue")["default"]
SettingsExtension: typeof import("./components/settings/Extension.vue")["default"]
SettingsProxy: typeof import("./components/settings/Proxy.vue")["default"]
SmartAccentModePicker: typeof import("./components/smart/AccentModePicker.vue")["default"]
SmartAnchor: typeof import("./../../hoppscotch-ui/src/components/smart/Anchor.vue")["default"]
SmartAutoComplete: typeof import("./../../hoppscotch-ui/src/components/smart/AutoComplete.vue")["default"]
SmartChangeLanguage: typeof import("./components/smart/ChangeLanguage.vue")["default"]
SmartCheckbox: typeof import("./../../hoppscotch-ui/src/components/smart/Checkbox.vue")["default"]
SmartColorModePicker: typeof import("./components/smart/ColorModePicker.vue")["default"]
SmartConfirmModal: typeof import("./../../hoppscotch-ui/src/components/smart/ConfirmModal.vue")["default"]
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"]
SmartLink: typeof import("./../../hoppscotch-ui/src/components/smart/Link.vue")["default"]
SmartModal: typeof import("./../../hoppscotch-ui/src/components/smart/Modal.vue")["default"]
SmartPicture: typeof import("./../../hoppscotch-ui/src/components/smart/Picture.vue")["default"]
SmartPlaceholder: typeof import("./../../hoppscotch-ui/src/components/smart/Placeholder.vue")["default"]
SmartProgressRing: typeof import("./../../hoppscotch-ui/src/components/smart/ProgressRing.vue")["default"]
SmartRadio: typeof import("./../../hoppscotch-ui/src/components/smart/Radio.vue")["default"]
SmartRadioGroup: typeof import("./../../hoppscotch-ui/src/components/smart/RadioGroup.vue")["default"]
SmartSlideOver: typeof import("./../../hoppscotch-ui/src/components/smart/SlideOver.vue")["default"]
SmartSpinner: typeof import("./../../hoppscotch-ui/src/components/smart/Spinner.vue")["default"]
SmartTab: typeof import("./../../hoppscotch-ui/src/components/smart/Tab.vue")["default"]
SmartTabs: typeof import("./../../hoppscotch-ui/src/components/smart/Tabs.vue")["default"]
SmartToggle: typeof import("./../../hoppscotch-ui/src/components/smart/Toggle.vue")["default"]
SmartTree: typeof import("./../../hoppscotch-ui/src/components/smart/Tree.vue")["default"]
SmartTreeBranch: typeof import("./../../hoppscotch-ui/src/components/smart/TreeBranch.vue")["default"]
SmartWindow: typeof import("./../../hoppscotch-ui/src/components/smart/Window.vue")["default"]
SmartWindows: typeof import("./../../hoppscotch-ui/src/components/smart/Windows.vue")["default"]
TabPrimary: typeof import("./components/tab/Primary.vue")["default"]
TabSecondary: typeof import("./components/tab/Secondary.vue")["default"]
Teams: typeof import("./components/teams/index.vue")["default"]
TeamsAdd: typeof import("./components/teams/Add.vue")["default"]
TeamsEdit: typeof import("./components/teams/Edit.vue")["default"]
TeamsInvite: typeof import("./components/teams/Invite.vue")["default"]
TeamsMemberStack: typeof import("./components/teams/MemberStack.vue")["default"]
TeamsModal: typeof import("./components/teams/Modal.vue")["default"]
TeamsTeam: typeof import("./components/teams/Team.vue")["default"]
Tippy: typeof import("vue-tippy")["Tippy"]
WorkspaceCurrent: typeof import("./components/workspace/Current.vue")["default"]
WorkspaceSelector: typeof import("./components/workspace/Selector.vue")["default"]
}
}

View File

@@ -1,6 +1,7 @@
<template>
<AppShortcuts :show="showShortcuts" @close="showShortcuts = false" />
<AppShare :show="showShare" @hide-modal="showShare = false" />
<AppSocial :show="showSocial" @hide-modal="showSocial = false" />
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
<HoppSmartConfirmModal
@@ -26,6 +27,7 @@ const t = useI18n()
const showShortcuts = ref(false)
const showShare = ref(false)
const showSocial = ref(false)
const showLogin = ref(false)
const confirmRemove = ref(false)
@@ -58,6 +60,10 @@ defineActionHandler("modals.share.toggle", () => {
showShare.value = !showShare.value
})
defineActionHandler("modals.social.toggle", () => {
showSocial.value = !showSocial.value
})
defineActionHandler("modals.login.toggle", () => {
showLogin.value = !showLogin.value
})

View File

@@ -0,0 +1,135 @@
<template>
<HoppSmartModal
v-if="show"
dialog
:title="t('app.social_links')"
@close="hideModal"
>
<template #body>
<div class="flex flex-col space-y-2">
<div class="grid grid-cols-3 gap-4">
<a
v-for="(platform, index) in platforms"
:key="`platform-${index}`"
:href="platform.link"
target="_blank"
class="social-link"
tabindex="0"
>
<component :is="platform.icon" class="w-6 h-6" />
<span class="mt-3">
{{ platform.name }}
</span>
</a>
<button class="social-link" @click="copyAppLink">
<component :is="copyIcon" class="w-6 h-6 text-xl" />
<span class="mt-3">
{{ t("app.copy") }}
</span>
</button>
</div>
</div>
</template>
<template #footer>
<p class="text-secondaryLight">
{{ t("app.social_description") }}
</p>
</template>
</HoppSmartModal>
</template>
<script setup lang="ts">
import { useI18n } from "@composables/i18n"
import { useToast } from "@composables/toast"
import { refAutoReset } from "@vueuse/core"
import { copyToClipboard } from "~/helpers/utils/clipboard"
import IconFacebook from "~icons/brands/facebook"
import IconLinkedIn from "~icons/brands/linkedin"
import IconReddit from "~icons/brands/reddit"
import IconTwitter from "~icons/brands/twitter"
import IconCheck from "~icons/lucide/check"
import IconCopy from "~icons/lucide/copy"
import IconGitHub from "~icons/lucide/github"
const t = useI18n()
const toast = useToast()
defineProps<{
show: boolean
}>()
const emit = defineEmits<{
(e: "hide-modal"): void
}>()
const url = "https://hoppscotch.io"
const copyIcon = refAutoReset<typeof IconCopy | typeof IconCheck>(
IconCopy,
1000
)
const platforms = [
{
name: "GitHub",
icon: IconGitHub,
link: `https://hoppscotch.io/github`,
},
{
name: "Twitter",
icon: IconTwitter,
link: `https://twitter.com/hoppscotch_io`,
},
{
name: "Facebook",
icon: IconFacebook,
link: `https://www.facebook.com/hoppscotch.io`,
},
{
name: "Reddit",
icon: IconReddit,
link: `https://www.reddit.com/r/hoppscotch`,
},
{
name: "LinkedIn",
icon: IconLinkedIn,
link: `https://www.linkedin.com/company/hoppscotch/`,
},
]
const copyAppLink = () => {
copyToClipboard(url)
copyIcon.value = IconCheck
toast.success(`${t("state.copied_to_clipboard")}`)
}
const hideModal = () => {
emit("hide-modal")
}
</script>
<style lang="scss" scoped>
.social-link {
@apply border border-dividerLight;
@apply rounded;
@apply flex-col flex;
@apply p-4;
@apply items-center;
@apply justify-center;
@apply font-semibold;
@apply hover: (bg-primaryLight text-secondaryDark);
@apply focus: outline-none;
@apply focus-visible: border-divider;
svg {
@apply opacity-80;
}
&:hover {
svg {
@apply opacity-100;
}
}
}
</style>

View File

@@ -1,3 +0,0 @@
<template>
<IconLucideCheckCircle class="text-accent" />
</template>

View File

@@ -111,7 +111,6 @@ import {
SwitchWorkspaceSpotlightSearcherService,
WorkspaceSpotlightSearcherService,
} from "~/services/spotlight/searchers/workspace.searcher"
import { InterceptorSpotlightSearcherService } from "~/services/spotlight/searchers/interceptor.searcher"
const t = useI18n()
@@ -139,7 +138,6 @@ useService(EnvironmentsSpotlightSearcherService)
useService(SwitchEnvSpotlightSearcherService)
useService(WorkspaceSpotlightSearcherService)
useService(SwitchWorkspaceSpotlightSearcherService)
useService(InterceptorSpotlightSearcherService)
const search = ref("")
@@ -266,3 +264,4 @@ function newUseArrowKeysForNavigation() {
return { selectedEntry }
}
</script>
~/services/spotlight/searchers/workspace.searcher

View File

@@ -37,7 +37,6 @@
@click="
emit('add-request', {
path: `${collectionIndex}`,
index: collection.requests.length,
})
"
/>
@@ -220,7 +219,6 @@ import {
moveGraphqlRequest,
} from "~/newstore/collections"
import { Picked } from "~/helpers/types/HoppPicked"
import { getTabsRefTo } from "~/helpers/graphql/tab"
const props = defineProps({
picked: { type: Object, default: null },
@@ -295,22 +293,6 @@ const removeCollection = () => {
emit("select", null)
}
const possibleTabs = getTabsRefTo((tab) => {
const ctx = tab.document.saveContext
if (!ctx) return false
return (
ctx.originLocation === "user-collection" &&
ctx.folderPath.startsWith(props.collectionIndex.toString())
)
})
for (const tab of possibleTabs) {
tab.value.document.saveContext = undefined
tab.value.document.isDirty = true
}
removeGraphqlCollection(props.collectionIndex, props.collection.id)
toast.success(`${t("state.deleted")}`)
}

View File

@@ -34,12 +34,7 @@
:icon="IconFilePlus"
:title="t('request.new')"
class="hidden group-hover:inline-flex"
@click="
emit('add-request', {
path: folderPath,
index: folder.requests.length,
})
"
@click="emit('add-request', { path: folderPath })"
/>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
@@ -203,7 +198,6 @@ import { useI18n } from "@composables/i18n"
import { useColorMode } from "@composables/theming"
import { removeGraphqlFolder, moveGraphqlRequest } from "~/newstore/collections"
import { computed, ref } from "vue"
import { getTabsRefTo } from "~/helpers/graphql/tab"
const toast = useToast()
const t = useI18n()
@@ -255,8 +249,10 @@ const collectionIcon = computed(() => {
const pick = () => {
emit("select", {
pickedType: "gql-my-folder",
folderPath: props.folderPath,
picked: {
pickedType: "gql-my-folder",
folderPath: props.folderPath,
},
})
}
@@ -277,22 +273,6 @@ const removeFolder = () => {
emit("select", { picked: null })
}
const possibleTabs = getTabsRefTo((tab) => {
const ctx = tab.document.saveContext
if (!ctx) return false
return (
ctx.originLocation === "user-collection" &&
ctx.folderPath.startsWith(props.folderPath)
)
})
for (const tab of possibleTabs) {
tab.value.document.saveContext = undefined
tab.value.document.isDirty = true
}
removeGraphqlFolder(props.folderPath, props.folder.id)
toast.success(t("state.deleted"))
}

View File

@@ -20,28 +20,22 @@
/>
</span>
<span
class="flex items-center flex-1 min-w-0 py-2 pr-2 cursor-pointer transition group-hover:text-secondaryDark"
class="flex flex-1 min-w-0 py-2 pr-2 cursor-pointer transition group-hover:text-secondaryDark"
@click="selectRequest()"
>
<span class="truncate" :class="{ 'text-accent': isSelected }">
{{ request.name }}
</span>
<span
v-if="isActive"
v-tippy="{ theme: 'tooltip' }"
class="relative h-1.5 w-1.5 flex flex-shrink-0 mx-3"
:title="`${t('collection.request_in_use')}`"
>
<span
class="absolute inline-flex flex-shrink-0 w-full h-full bg-green-500 rounded-full opacity-75 animate-ping"
>
</span>
<span
class="relative inline-flex flex-shrink-0 rounded-full h-1.5 w-1.5 bg-green-500"
></span>
</span>
</span>
<div class="flex">
<HoppButtonSecondary
v-if="!saveRequest"
v-tippy="{ theme: 'tooltip' }"
:icon="IconRotateCCW"
:title="t('action.restore')"
class="hidden group-hover:inline-flex"
@click="selectRequest()"
/>
<span>
<tippy
ref="options"
@@ -127,6 +121,7 @@
<script setup lang="ts">
import IconCheckCircle from "~icons/lucide/check-circle"
import IconFile from "~icons/lucide/file"
import IconRotateCCW from "~icons/lucide/rotate-ccw"
import IconMoreVertical from "~icons/lucide/more-vertical"
import IconEdit from "~icons/lucide/edit"
import IconCopy from "~icons/lucide/copy"
@@ -137,12 +132,7 @@ import { useToast } from "@composables/toast"
import { HoppGQLRequest, makeGQLRequest } from "@hoppscotch/data"
import { cloneDeep } from "lodash-es"
import { removeGraphqlRequest } from "~/newstore/collections"
import {
createNewTab,
getTabRefWithSaveContext,
currentTabID,
currentActiveTab,
} from "~/helpers/graphql/tab"
import { createNewTab } from "~/helpers/graphql/tab"
// Template refs
const tippyActions = ref<any | null>(null)
@@ -164,18 +154,6 @@ const props = defineProps({
requestIndex: { type: Number, default: null },
})
const isActive = computed(() => {
const saveCtx = currentActiveTab.value?.document.saveContext
if (!saveCtx) return false
return (
saveCtx.originLocation === "user-collection" &&
saveCtx.folderPath === props.folderPath &&
saveCtx.requestIndex === props.requestIndex
)
})
// TODO: Better types please
const emit = defineEmits(["select", "edit-request", "duplicate-request"])
@@ -201,24 +179,7 @@ const selectRequest = () => {
if (props.saveRequest) {
pick()
} else {
const possibleTab = getTabRefWithSaveContext({
originLocation: "user-collection",
folderPath: props.folderPath,
requestIndex: props.requestIndex,
})
// Switch to that request if that request is open
if (possibleTab) {
currentTabID.value = possibleTab.value.id
return
}
createNewTab({
saveContext: {
originLocation: "user-collection",
folderPath: props.folderPath,
requestIndex: props.requestIndex,
},
request: cloneDeep(
makeGQLRequest({
name: props.request.name,
@@ -252,18 +213,6 @@ const removeRequest = () => {
emit("select", null)
}
// Detach the request from any of the tabs
const possibleTab = getTabRefWithSaveContext({
originLocation: "user-collection",
folderPath: props.folderPath,
requestIndex: props.requestIndex,
})
if (possibleTab) {
possibleTab.value.document.saveContext = undefined
possibleTab.value.document.isDirty = true
}
removeGraphqlRequest(props.folderPath, props.requestIndex, props.request.id)
toast.success(`${t("state.deleted")}`)
}

View File

@@ -265,7 +265,7 @@ export default defineComponent({
this.$data.editingCollectionIndex = collectionIndex
this.displayModalEdit(true)
},
onAddRequest({ name, path, index }) {
onAddRequest({ name, path }) {
const newRequest = {
...currentActiveTab.value.document.request,
name,
@@ -274,11 +274,6 @@ export default defineComponent({
saveGraphqlRequestAs(path, newRequest)
createNewTab({
saveContext: {
originLocation: "user-collection",
folderPath: path,
requestIndex: index,
},
request: newRequest,
isDirty: false,
})

View File

@@ -239,7 +239,6 @@ import {
resetTeamRequestsContext,
} from "~/helpers/collection/collection"
import { currentReorderingStatus$ } from "~/newstore/reordering"
import { defineActionHandler } from "~/helpers/actions"
const t = useI18n()
const toast = useToast()
@@ -2068,8 +2067,4 @@ const getErrorMessage = (err: GQLError<string>) => {
}
}
}
defineActionHandler("collection.new", () => {
displayModalAdd(true)
})
</script>

View File

@@ -34,13 +34,6 @@
@hide-modal="displayModalNew(false)"
/>
</div>
<HoppSmartConfirmModal
:show="showConfirmRemoveEnvModal"
:title="t('confirm.remove_team')"
@hide-modal="showConfirmRemoveEnvModal = false"
@resolve="removeSelectedEnvironment()"
/>
</template>
<script setup lang="ts">
@@ -51,7 +44,6 @@ import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
import { useReadonlyStream, useStream } from "@composables/stream"
import { useI18n } from "~/composables/i18n"
import {
getSelectedEnvironmentIndex,
globalEnv$,
selectedEnvironmentIndex$,
setSelectedEnvironmentIndex,
@@ -62,15 +54,8 @@ import { workspaceStatus$ } from "~/newstore/workspace"
import TeamListAdapter from "~/helpers/teams/TeamListAdapter"
import { useLocalState } from "~/newstore/localstate"
import { onLoggedIn } from "~/composables/auth"
import { pipe } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither"
import { GQLError } from "~/helpers/backend/GQLClient"
import { deleteEnvironment } from "~/newstore/environments"
import { deleteTeamEnvironment } from "~/helpers/backend/mutations/TeamEnvironment"
import { useToast } from "~/composables/toast"
const t = useI18n()
const toast = useToast()
type EnvironmentType = "my-environments" | "team-environments"
@@ -183,7 +168,6 @@ watch(
}
)
const showConfirmRemoveEnvModal = ref(false)
const showModalNew = ref(false)
const showModalDetails = ref(false)
const action = ref<"new" | "edit">("edit")
@@ -210,30 +194,6 @@ const editEnvironment = (environmentIndex: "Global") => {
displayModalEdit(true)
}
const removeSelectedEnvironment = () => {
const selectedEnvIndex = getSelectedEnvironmentIndex()
if (selectedEnvIndex?.type === "NO_ENV_SELECTED") return
if (selectedEnvIndex?.type === "MY_ENV") {
deleteEnvironment(selectedEnvIndex.index)
toast.success(`${t("state.deleted")}`)
}
if (selectedEnvIndex?.type === "TEAM_ENV") {
pipe(
deleteTeamEnvironment(selectedEnvIndex.teamEnvID),
TE.match(
(err: GQLError<string>) => {
console.error(err)
},
() => {
toast.success(`${t("team_environment.deleted")}`)
}
)
)()
}
}
const resetSelectedData = () => {
editingEnvironmentIndex.value = null
}
@@ -243,10 +203,6 @@ defineActionHandler("modals.environment.new", () => {
showModalDetails.value = true
})
defineActionHandler("modals.environment.delete-selected", () => {
showConfirmRemoveEnvModal.value = true
})
defineActionHandler(
"modals.my.environment.edit",
({ envName, variableName }) => {

View File

@@ -312,10 +312,8 @@ const authProviders: AuthProviderItem[] = [
},
]
// Do not format the `import.meta.env.VITE_ALLOWED_AUTH_PROVIDERS` call into multiple lines!
// prettier-ignore
const allowedAuthProvidersIDsString =
import.meta.env.VITE_ALLOWED_AUTH_PROVIDERS
const allowedAuthProvidersIDsString: string | undefined = import.meta.env
.VITE_ALLOWED_AUTH_PROVIDERS
const allowedAuthProvidersIDs = allowedAuthProvidersIDsString
? allowedAuthProvidersIDsString.split(",")

View File

@@ -1,7 +1,7 @@
<template>
<div class="flex flex-col flex-1">
<div
class="sticky top-sidebarPrimaryStickyFold z-10 flex items-center justify-between pl-4 border-y bg-primary border-dividerLight"
class="sticky z-10 flex items-center justify-between flex-shrink-0 pl-4 overflow-x-auto border-b bg-primary border-dividerLight"
>
<span class="flex items-center">
<label class="font-semibold truncate text-secondaryLight">

View File

@@ -1,6 +1,6 @@
<template>
<div
class="sticky top-sidebarPrimaryStickyFold z-10 flex items-center justify-between pl-4 border-y bg-primary border-dividerLight"
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight"
>
<label class="font-semibold text-secondaryLight">
{{ t("tab.headers") }}

View File

@@ -1,6 +1,6 @@
<template>
<div
class="sticky top-sidebarPrimaryStickyFold z-10 flex items-center justify-between pl-4 border-y bg-primary border-dividerLight"
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight gqlRunQuery"
>
<label class="font-semibold text-secondaryLight">
{{ t("request.query") }}

View File

@@ -2,7 +2,7 @@
<div class="flex flex-col flex-1 h-full">
<HoppSmartTabs
v-model="selectedOptionTab"
styles="sticky top-0 bg-primary z-10 border-b-0"
styles="sticky bg-primary z-10"
:render-inactive-tabs="true"
>
<HoppSmartTab
@@ -67,7 +67,6 @@ import {
} from "~/helpers/graphql/connection"
import { useService } from "dioc/vue"
import { InterceptorService } from "~/services/interceptor.service"
import { editGraphqlRequest } from "~/newstore/collections"
type OptionTabs = "query" | "headers" | "variables" | "authorization"
const selectedOptionTab = ref<OptionTabs>("query")
@@ -181,29 +180,12 @@ const hideRequestModal = () => {
showSaveRequestModal.value = false
}
const saveRequest = () => {
if (
currentActiveTab.value.document.saveContext &&
currentActiveTab.value.document.saveContext.originLocation ===
"user-collection"
) {
editGraphqlRequest(
currentActiveTab.value.document.saveContext.folderPath,
currentActiveTab.value.document.saveContext.requestIndex,
currentActiveTab.value.document.request
)
currentActiveTab.value.document.isDirty = false
} else {
showSaveRequestModal.value = true
}
showSaveRequestModal.value = true
}
const clearGQLQuery = () => {
request.value.query = ""
}
defineActionHandler("request.send-cancel", runQuery)
defineActionHandler("request.save", saveRequest)
defineActionHandler("request.save-as", () => {
showSaveRequestModal.value = true
})
defineActionHandler("request.reset", clearGQLQuery)
</script>

View File

@@ -129,7 +129,9 @@ const downloadResponse = (str: string) => {
}
defineActionHandler("response.file.download", () =>
downloadResponse(responseString.value)
downloadResponse.bind(responseString.value)
)
defineActionHandler("response.copy", () =>
copyResponse.bind(responseString.value)
)
defineActionHandler("response.copy", () => copyResponse(responseString.value))
</script>

View File

@@ -1,118 +0,0 @@
<template>
<div
v-tippy="{ theme: 'tooltip', delay: [500, 20] }"
:title="tab.document.request.name"
class="truncate px-2 flex items-center"
@dblclick="emit('open-rename-modal')"
@contextmenu.prevent="options?.tippy?.show()"
@click.middle="emit('close-tab')"
>
<tippy
ref="options"
trigger="manual"
interactive
theme="popover"
:on-shown="() => tippyActions!.focus()"
>
<span class="leading-8 px-2 truncate">
{{ tab.document.request.name }}
</span>
<template #content="{ hide }">
<div
ref="tippyActions"
class="flex flex-col focus:outline-none"
tabindex="0"
@keyup.r="renameAction?.$el.click()"
@keyup.d="duplicateAction?.$el.click()"
@keyup.w="closeAction?.$el.click()"
@keyup.x="closeOthersAction?.$el.click()"
@keyup.escape="hide()"
>
<HoppSmartItem
ref="renameAction"
:icon="IconFileEdit"
:label="t('request.rename')"
:shortcut="['R']"
@click="
() => {
emit('open-rename-modal')
hide()
}
"
/>
<HoppSmartItem
ref="duplicateAction"
:icon="IconCopy"
:label="t('tab.duplicate')"
:shortcut="['D']"
@click="
() => {
emit('duplicate-tab')
hide()
}
"
/>
<HoppSmartItem
v-if="isRemovable"
ref="closeAction"
:icon="IconXCircle"
:label="t('tab.close')"
:shortcut="['W']"
@click="
() => {
emit('close-tab')
hide()
}
"
/>
<HoppSmartItem
v-if="isRemovable"
ref="closeOthersAction"
:icon="IconXSquare"
:label="t('tab.close_others')"
:shortcut="['X']"
@click="
() => {
emit('close-other-tabs')
hide()
}
"
/>
</div>
</template>
</tippy>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue"
import { TippyComponent } from "vue-tippy"
import { useI18n } from "~/composables/i18n"
import IconXCircle from "~icons/lucide/x-circle"
import IconXSquare from "~icons/lucide/x-square"
import IconFileEdit from "~icons/lucide/file-edit"
import IconCopy from "~icons/lucide/copy"
import { HoppGQLTab } from "~/helpers/graphql/tab"
const t = useI18n()
defineProps<{
tab: HoppGQLTab
isRemovable: boolean
}>()
const emit = defineEmits<{
(event: "open-rename-modal"): void
(event: "close-tab"): void
(event: "close-other-tabs"): void
(event: "duplicate-tab"): void
}>()
const tippyActions = ref<TippyComponent | null>(null)
const options = ref<TippyComponent | null>(null)
const renameAction = ref<HTMLButtonElement | null>(null)
const closeAction = ref<HTMLButtonElement | null>(null)
const closeOthersAction = ref<HTMLButtonElement | null>(null)
const duplicateAction = ref<HTMLButtonElement | null>(null)
</script>

View File

@@ -1,6 +1,6 @@
<template>
<div
class="sticky top-sidebarPrimaryStickyFold z-10 flex items-center justify-between pl-4 border-y bg-primary border-dividerLight"
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight"
>
<label class="font-semibold text-secondaryLight">
{{ t("request.variables") }}

View File

@@ -56,7 +56,13 @@
:inspection-results="tabResults"
@paste="onPasteUrl($event)"
@enter="newSendRequest"
/>
>
<template #empty>
<span>
{{ t("empty.history_suggestions") }}
</span>
</template>
</SmartEnvInput>
</div>
</div>
<div class="flex mt-2 sm:mt-0">

View File

@@ -4,7 +4,7 @@
:title="tab.document.request.name"
class="truncate px-2 flex items-center"
@dblclick="emit('open-rename-modal')"
@contextmenu.prevent="options?.tippy?.show()"
@contextmenu.prevent="options?.tippy.show()"
@click.middle="emit('close-tab')"
>
<span

View File

@@ -1,5 +1,5 @@
<template>
<div ref="autoCompleteWrapper" class="autocomplete-wrapper">
<div class="autocomplete-wrapper">
<div
class="absolute inset-0 flex flex-1 divide-x divide-dividerLight overflow-x-auto"
>
@@ -18,9 +18,7 @@
/>
</div>
<ul
v-if="
showSuggestionPopover && autoCompleteSource && suggestions.length > 0
"
v-if="showSuggestionPopover && autoCompleteSource"
ref="suggestionsMenu"
class="suggestions"
>
@@ -41,12 +39,20 @@
<span class="ml-2 truncate">to select</span>
</div>
</li>
<li v-if="suggestions.length === 0" class="pointer-events-none">
<div v-if="slots.empty" class="truncate py-0.5">
<slot name="empty"></slot>
</div>
<span v-else class="truncate py-0.5">
{{ t("empty.suggestions") }}
</span>
</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, watch, nextTick, computed, Ref } from "vue"
import { ref, onMounted, watch, nextTick, computed, Ref, useSlots } from "vue"
import {
EditorView,
placeholder as placeholderExt,
@@ -63,6 +69,7 @@ import { HoppReactiveEnvPlugin } from "~/helpers/editor/extensions/HoppEnvironme
import { useReadonlyStream } from "@composables/stream"
import { AggregateEnvironment, aggregateEnvs$ } from "~/newstore/environments"
import { platform } from "~/platform"
import { useI18n } from "~/composables/i18n"
import { onClickOutside, useDebounceFn } from "@vueuse/core"
import { InspectorResult } from "~/services/inspection"
import { invokeAction } from "~/helpers/actions"
@@ -104,6 +111,10 @@ const emit = defineEmits<{
(e: "click", ev: any): void
}>()
const slots = useSlots()
const t = useI18n()
const cachedValue = ref(props.modelValue)
const view = ref<EditorView>()
@@ -114,9 +125,8 @@ const currentSuggestionIndex = ref(-1)
const showSuggestionPopover = ref(false)
const suggestionsMenu = ref<any | null>(null)
const autoCompleteWrapper = ref<any | null>(null)
onClickOutside(autoCompleteWrapper, () => {
onClickOutside(suggestionsMenu, () => {
showSuggestionPopover.value = false
})

View File

@@ -7,7 +7,6 @@ import { BehaviorSubject } from "rxjs"
import { HoppRESTDocument } from "./rest/document"
import { HoppGQLRequest, HoppRESTRequest } from "@hoppscotch/data"
import { RequestOptionTabs } from "~/components/http/RequestOptions.vue"
import { HoppGQLSaveContext } from "./graphql/document"
export type HoppAction =
| "contextmenu.open" // Send/Cancel a Hoppscotch Request
@@ -26,15 +25,14 @@ export type HoppAction =
| "request.method.delete" // Select DELETE Method
| "request.import-curl" // Import cURL
| "request.show-code" // Show generated code
| "collection.new" // Create root collection
| "flyouts.chat.open" // Shows the keybinds flyout
| "flyouts.keybinds.toggle" // Shows the keybinds flyout
| "modals.search.toggle" // Shows the search modal
| "modals.support.toggle" // Shows the support modal
| "modals.share.toggle" // Shows the share modal
| "modals.social.toggle" // Shows the social links modal
| "modals.environment.add" // Show add environment modal via context menu
| "modals.environment.new" // Add new environment
| "modals.environment.delete-selected" // Delete Selected Environment
| "modals.my.environment.edit" // Edit current personal environment
| "modals.team.environment.edit" // Edit current team environment
| "modals.team.new" // Add new team
@@ -109,13 +107,8 @@ type HoppActionArgsMap = {
tab: RequestOptionTabs
}
"request.duplicate-tab": {
tabID: string
}
"gql.request.open": {
request: HoppGQLRequest
saveContext?: HoppGQLSaveContext
}
"modals.environment.add": {
envName: string

View File

@@ -17,9 +17,6 @@ import {
getSelectedEnvironmentType,
} from "~/newstore/environments"
import { invokeAction } from "~/helpers/actions"
import IconUser from "~icons/lucide/user?raw"
import IconUsers from "~icons/lucide/users?raw"
import IconEdit from "~icons/lucide/edit?raw"
const HOPP_ENVIRONMENT_REGEX = /(<<[a-zA-Z0-9-_]+>>)/g
@@ -74,14 +71,14 @@ const cursorTooltipField = (aggregateEnvs: AggregateEnvironment[]) =>
const selectedEnvType = getSelectedEnvironmentType()
const envTypeIcon = `<span class="inline-flex items-center justify-center my-1">${
selectedEnvType === "TEAM_ENV" ? IconUsers : IconUser
const envTypeIcon = `<span class="inline-flex items-center opacity-65 -mx-0.5 text-base font-icon">${
selectedEnvType === "TEAM_ENV" ? "group" : "person"
}</span>`
const appendEditAction = (tooltip: HTMLElement) => {
const editIcon = document.createElement("button")
const editIcon = document.createElement("span")
editIcon.className =
"ml-2 cursor-pointer text-accent hover:text-accentDark"
"ml-2 cursor-pointer env-icon text-accent hover:text-accentDark"
editIcon.addEventListener("click", () => {
const isPersonalEnv =
envName === "Global" || selectedEnvType !== "TEAM_ENV"
@@ -91,7 +88,7 @@ const cursorTooltipField = (aggregateEnvs: AggregateEnvironment[]) =>
variableName: parsedEnvKey,
})
})
editIcon.innerHTML = `<span class="inline-flex items-center justify-center my-1">${IconEdit}</span>`
editIcon.innerHTML = `<span class="inline-flex items-center px-1 -mx-1 text-base font-icon">edit</span>`
tooltip.appendChild(editIcon)
}
@@ -106,7 +103,7 @@ const cursorTooltipField = (aggregateEnvs: AggregateEnvironment[]) =>
const kbd = document.createElement("kbd")
const icon = document.createElement("span")
icon.innerHTML = envTypeIcon
icon.className = "mr-2"
icon.className = "mr-2 env-icon"
kbd.textContent = finalEnv
tooltipContainer.appendChild(icon)
tooltipContainer.appendChild(document.createTextNode(`${envName} `))

View File

@@ -197,30 +197,3 @@ export function getTabsRefTo(func: (tab: HoppGQLTab) => boolean) {
.filter(func)
.map((tab) => getTabRef(tab.id))
}
export function closeOtherTabs(tabID: string) {
if (!tabMap.has(tabID)) {
console.warn(
`The tab to close other tabs does not exist (tab id: ${tabID})`
)
return
}
tabOrdering.value = [tabID]
tabMap.forEach((_, id) => {
if (id !== tabID) tabMap.delete(id)
})
currentTabID.value = tabID
}
export function getDirtyTabsCount() {
let count = 0
for (const tab of tabMap.values()) {
if (tab.document.isDirty) count++
}
return count
}

View File

@@ -1,4 +1,4 @@
import * as HTTPSnippet from "httpsnippet"
import { HTTPSnippet } from "httpsnippet"
import { HoppRESTRequest } from "@hoppscotch/data"
import * as O from "fp-ts/Option"
import * as E from "fp-ts/Either"
@@ -208,10 +208,7 @@ export const generateCode = (
}).convert(codegenInfo.lang, codegenInfo.mode, {
indent: " ",
}),
(e) => {
console.error(e)
return e
}
(e) => e
),
// Only allow string output to pass through, else none

View File

@@ -1,163 +0,0 @@
import axios, { AxiosRequestConfig } from "axios"
import { v4 } from "uuid"
import { pipe } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither"
import { cloneDeep } from "lodash-es"
import { NetworkResponse, NetworkStrategy } from "../network"
import { decodeB64StringToArrayBuffer } from "../utils/b64"
import { settingsStore } from "~/newstore/settings"
let cancelSource = axios.CancelToken.source()
type ProxyHeaders = {
"multipart-part-key"?: string
}
type ProxyPayloadType = FormData | (AxiosRequestConfig & { wantsBinary: true })
export const cancelRunningAxiosRequest = () => {
cancelSource.cancel()
// Create a new cancel token
cancelSource = axios.CancelToken.source()
}
const getProxyPayload = (
req: AxiosRequestConfig,
multipartKey: string | null
) => {
let payload: ProxyPayloadType = {
...req,
wantsBinary: true,
accessToken: import.meta.env.VITE_PROXYSCOTCH_ACCESS_TOKEN ?? "",
}
if (payload.data instanceof FormData) {
const formData = payload.data
payload.data = ""
formData.append(multipartKey!, JSON.stringify(payload))
payload = formData
}
return payload
}
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
}
const axiosWithProxy: NetworkStrategy = (req) =>
pipe(
TE.Do,
TE.bind("processedReq", () => TE.of(preProcessRequest(req))),
// If the request has FormData, the proxy needs a key
TE.bind("multipartKey", ({ processedReq }) =>
TE.of(
processedReq.data instanceof FormData
? `proxyRequestData-${v4()}`
: null
)
),
// Build headers to send
TE.bind("headers", ({ processedReq, multipartKey }) =>
TE.of(
processedReq.data instanceof FormData
? <ProxyHeaders>{
"multipart-part-key": multipartKey,
}
: <ProxyHeaders>{}
)
),
// Create payload
TE.bind("payload", ({ processedReq, multipartKey }) =>
TE.of(getProxyPayload(processedReq, multipartKey))
),
// Run the proxy request
TE.chain(({ payload, headers }) =>
TE.tryCatch(
() =>
axios.post(
settingsStore.value.PROXY_URL || "https://proxy.hoppscotch.io",
payload,
{
headers,
cancelToken: cancelSource.token,
}
),
(reason) =>
axios.isCancel(reason)
? "cancellation" // Convert cancellation errors into cancellation strings
: reason
)
),
// Check success predicate
TE.chain(
TE.fromPredicate(
({ data }) => data.success,
({ data }) => data.data.message || "Proxy Error"
)
),
// Process Base64
TE.chain(({ data }) => {
if (data.isBinary) {
data.data = decodeB64StringToArrayBuffer(data.data)
}
return TE.of(data)
})
)
const axiosWithoutProxy: NetworkStrategy = (req) =>
pipe(
TE.tryCatch(
() =>
axios({
...req,
cancelToken: (cancelSource && cancelSource.token) || "",
responseType: "arraybuffer",
}),
(e) => (axios.isCancel(e) ? "cancellation" : (e as any))
),
TE.orElse((e) =>
e !== "cancellation" && e.response
? TE.right(e.response as NetworkResponse)
: TE.left(e)
)
)
const axiosStrategy: NetworkStrategy = (req) =>
pipe(
req,
settingsStore.value.PROXY_ENABLED ? axiosWithProxy : axiosWithoutProxy
)
export default axiosStrategy

View File

@@ -427,10 +427,6 @@ export function getCurrentEnvironment(): Environment {
}
}
export function getSelectedEnvironmentIndex() {
return environmentsStore.value.selectedEnvironmentIndex
}
export function getSelectedEnvironmentType() {
return environmentsStore.value.selectedEnvironmentIndex.type
}

View File

@@ -21,14 +21,15 @@
:close-visibility="'hover'"
>
<template #tabhead>
<GraphqlTabHead
:tab="tab"
:is-removable="tabs.length > 1"
@open-rename-modal="openReqRenameModal(tab)"
@close-tab="removeTab(tab.id)"
@close-other-tabs="closeOtherTabsAction(tab.id)"
@duplicate-tab="duplicateTab(tab)"
/>
<div
v-tippy="{ theme: 'tooltip', delay: [500, 20] }"
:title="tab.document.request.name"
class="truncate px-2"
>
<span class="leading-8 px-2">
{{ tab.document.request.name }}
</span>
</div>
</template>
<template #suffix>
@@ -58,12 +59,7 @@
<GraphqlSidebar />
</template>
</AppPaneLayout>
<CollectionsEditRequest
v-model="editReqModalReqName"
:show="showRenamingReqNameModalForTabID !== undefined"
@submit="renameReqName"
@hide-modal="showRenamingReqNameModalForTabID = undefined"
/>
<HoppSmartConfirmModal
:show="confirmingCloseForTabID !== null"
:confirm="t('modal.close_unsaved_tab')"
@@ -71,13 +67,6 @@
@hide-modal="onCloseConfirm"
@resolve="onResolveConfirm"
/>
<HoppSmartConfirmModal
:show="confirmingCloseAllTabs"
:confirm="t('modal.close_unsaved_tab')"
:title="t('confirm.close_unsaved_tabs', { count: unsavedTabsCount })"
@hide-modal="confirmingCloseAllTabs = false"
@resolve="onResolveConfirmCloseAllTabs"
/>
</div>
</template>
@@ -91,12 +80,10 @@ import { connection, disconnect } from "~/helpers/graphql/connection"
import { getDefaultGQLRequest } from "~/helpers/graphql/default"
import {
HoppGQLTab,
closeOtherTabs,
closeTab,
createNewTab,
currentTabID,
getActiveTabs,
getDirtyTabsCount,
getTabRef,
updateTab,
updateTabOrdering,
@@ -155,27 +142,6 @@ const onResolveConfirm = () => {
}
}
const confirmingCloseAllTabs = ref(false)
const unsavedTabsCount = ref(0)
const exceptedTabID = ref<string | null>(null)
const closeOtherTabsAction = (tabID: string) => {
const dirtyTabCount = getDirtyTabsCount()
// If there are dirty tabs, show the confirm modal
if (dirtyTabCount > 0) {
confirmingCloseAllTabs.value = true
unsavedTabsCount.value = dirtyTabCount
exceptedTabID.value = tabID
} else {
closeOtherTabs(tabID)
}
}
const onResolveConfirmCloseAllTabs = () => {
if (exceptedTabID.value) closeOtherTabs(exceptedTabID.value)
confirmingCloseAllTabs.value = false
}
const onTabUpdate = (tab: HoppGQLTab) => {
updateTab(tab)
}
@@ -186,34 +152,8 @@ onBeforeUnmount(() => {
}
})
const editReqModalReqName = ref("")
const showRenamingReqNameModalForTabID = ref<string>()
const openReqRenameModal = (tab: HoppGQLTab) => {
editReqModalReqName.value = tab.document.request.name
showRenamingReqNameModalForTabID.value = tab.id
}
const renameReqName = () => {
const tab = getTabRef(showRenamingReqNameModalForTabID.value!)
if (tab.value) {
tab.value.document.request.name = editReqModalReqName.value
updateTab(tab.value)
}
showRenamingReqNameModalForTabID.value = undefined
}
const duplicateTab = (tab: HoppGQLTab) => {
const newTab = createNewTab({
request: tab.document.request,
isDirty: true,
})
currentTabID.value = newTab.id
}
defineActionHandler("gql.request.open", ({ request, saveContext }) => {
defineActionHandler("gql.request.open", ({ request }) => {
createNewTab({
saveContext,
request: request,
isDirty: false,
})

View File

@@ -150,7 +150,6 @@ const showRenamingReqNameModal = ref(false)
const reqName = ref<string>("")
const unsavedTabsCount = ref(0)
const exceptedTabID = ref<string | null>(null)
const renameTabID = ref<string | null>(null)
const t = useI18n()
const toast = useToast()
@@ -258,7 +257,6 @@ const openReqRenameModal = (tabID?: string) => {
if (tabID) {
const tab = getTabRef(tabID)
reqName.value = tab.value.document.request.name
renameTabID.value = tabID
} else {
reqName.value = currentActiveTab.value.document.request.name
}
@@ -266,7 +264,7 @@ const openReqRenameModal = (tabID?: string) => {
}
const renameReqName = () => {
const tab = getTabRef(renameTabID.value ?? currentTabID.value)
const tab = getTabRef(currentTabID.value)
if (tab.value) {
tab.value.document.request.name = reqName.value
updateTab(tab.value)
@@ -461,9 +459,6 @@ defineActionHandler("rest.request.open", ({ doc }) => {
})
defineActionHandler("rest.request.rename", openReqRenameModal)
defineActionHandler("request.duplicate-tab", ({ tabID }) => {
duplicateTab(tabID)
})
const inspectionService = useService(InspectionService)
useService(HeaderInspectorService)

View File

@@ -114,7 +114,7 @@ export class ParameterMenuService extends Service implements ContextMenu {
id: "environment",
text: {
type: "text",
text: this.t("context_menu.add_parameters"),
text: this.t("context_menu.add_parameter"),
},
icon: markRaw(IconArrowDownRight),
action: () => {

View File

@@ -70,7 +70,7 @@ export class URLMenuService extends Service implements ContextMenu {
id: "link-tab",
text: {
type: "text",
text: this.t("context_menu.open_request_in_new_tab"),
text: this.t("context_menu.open_link_in_new_tab"),
},
icon: markRaw(IconCopyPlus),
action: () => {

View File

@@ -1,6 +1,5 @@
import { Service } from "dioc"
import {
SpotlightResultTextType,
SpotlightSearcher,
SpotlightSearcherResult,
SpotlightSearcherSessionState,
@@ -27,7 +26,6 @@ import {
} from "@hoppscotch/data"
import { hoppWorkspaceStore } from "~/newstore/workspace"
import { changeWorkspace } from "~/newstore/workspace"
import { invokeAction } from "~/helpers/actions"
/**
* A spotlight searcher that searches through the user's collections
@@ -145,13 +143,6 @@ export class CollectionsSpotlightSearcherService
},
})
if (pageCategory === "rest" || pageCategory === "graphql") {
minisearch.add({
id: `create-collection`,
name: this.t("collection.new"),
})
}
if (pageCategory === "rest") {
this.loadRESTDocsIntoMinisearch(minisearch)
} else if (pageCategory === "graphql") {
@@ -162,11 +153,6 @@ export class CollectionsSpotlightSearcherService
const scopeHandle = effectScope()
const newCollectionText: SpotlightResultTextType<any> = {
type: "text",
text: this.t("collection.new"),
}
scopeHandle.run(() => {
watch(query, (query) => {
if (pageCategory === "other") {
@@ -179,34 +165,28 @@ export class CollectionsSpotlightSearcherService
results.value = searchResults.map((result) => ({
id: result.id,
text:
result.id === "create-collection"
? newCollectionText
: {
type: "custom",
component: markRaw(RESTRequestSpotlightEntry),
componentProps: {
folderPath: result.id.split("rest-")[1],
},
},
text: {
type: "custom",
component: markRaw(RESTRequestSpotlightEntry),
componentProps: {
folderPath: result.id.split("rest-")[1],
},
},
icon: markRaw(IconFolder),
score: result.score,
}))
} else if (pageCategory === "graphql") {
} else {
const searchResults = minisearch.search(query).slice(0, 10)
results.value = searchResults.map((result) => ({
id: result.id,
text:
result.id === "create-collection"
? newCollectionText
: {
type: "custom",
component: markRaw(GQLRequestSpotlightEntry),
componentProps: {
folderPath: result.id.split("gql-")[1],
},
},
text: {
type: "custom",
component: markRaw(GQLRequestSpotlightEntry),
componentProps: {
folderPath: result.id.split("gql-")[1],
},
},
icon: markRaw(IconFolder),
score: result.score,
}))
@@ -276,8 +256,6 @@ export class CollectionsSpotlightSearcherService
}
public onResultSelect(result: SpotlightSearcherResult): void {
if (result.id === "create-collection") return invokeAction("collection.new")
const [type, path] = result.id.split("-")
if (type === "rest") {
@@ -326,15 +304,13 @@ export class CollectionsSpotlightSearcherService
if (!req) return
createNewGQLTab({
saveContext: {
originLocation: "user-collection",
folderPath: folderPath.join("/"),
requestIndex: reqIndex,
createNewGQLTab(
{
request: req,
isDirty: false,
},
request: req,
isDirty: false,
})
true
)
}
}
}

View File

@@ -21,29 +21,30 @@ import {
StaticSpotlightSearcherService,
} from "./base/static.searcher"
import IconCopy from "~icons/lucide/copy"
import IconEdit from "~icons/lucide/edit"
import IconLayers from "~icons/lucide/layers"
import IconTrash2 from "~icons/lucide/trash-2"
import IconCopy from "~icons/lucide/copy"
import IconLayers from "~icons/lucide/layers"
import { Service } from "dioc"
import * as TE from "fp-ts/TaskEither"
import { pipe } from "fp-ts/function"
import { cloneDeep } from "lodash-es"
import MiniSearch from "minisearch"
import { map } from "rxjs"
import { useStreamStatic } from "~/composables/stream"
import { GQLError } from "~/helpers/backend/GQLClient"
import { deleteTeamEnvironment } from "~/helpers/backend/mutations/TeamEnvironment"
import {
createEnvironment,
currentEnvironment$,
deleteEnvironment,
duplicateEnvironment,
environmentsStore,
getGlobalVariables,
selectedEnvironmentIndex$,
setSelectedEnvironmentIndex,
} from "~/newstore/environments"
import { pipe } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither"
import { deleteTeamEnvironment } from "~/helpers/backend/mutations/TeamEnvironment"
import { GQLError } from "~/helpers/backend/GQLClient"
import { cloneDeep } from "lodash-es"
import { Service } from "dioc"
import MiniSearch from "minisearch"
import { map } from "rxjs"
type Doc = {
text: string
@@ -187,6 +188,29 @@ export class EnvironmentsSpotlightSearcherService extends StaticSpotlightSearche
}
}
removeSelectedEnvironment = () => {
if (this.selectedEnvIndex.value?.type === "NO_ENV_SELECTED") return
if (this.selectedEnvIndex.value?.type === "MY_ENV") {
deleteEnvironment(this.selectedEnvIndex.value.index)
// this.toast.success(`${t("state.deleted")}`)
}
if (this.selectedEnvIndex.value?.type === "TEAM_ENV") {
pipe(
deleteTeamEnvironment(this.selectedEnvIndex.value.teamEnvID),
TE.match(
(err: GQLError<string>) => {
console.error(err)
},
() => {
// this.toast.success(`${this.t("team_environment.deleted")}`)
}
)
)()
}
}
public onDocSelected(id: string): void {
switch (id) {
case "new_environment":
@@ -205,7 +229,7 @@ export class EnvironmentsSpotlightSearcherService extends StaticSpotlightSearche
})
break
case "delete_selected_env":
invokeAction(`modals.environment.delete-selected`)
this.removeSelectedEnvironment()
break
case "duplicate_selected_env":
this.duplicateSelectedEnv()

View File

@@ -7,17 +7,14 @@ import {
StaticSpotlightSearcherService,
} from "./base/static.searcher"
import IconLinkedIn from "~icons/brands/linkedin"
import IconTwitter from "~icons/brands/twitter"
import IconBook from "~icons/lucide/book"
import IconDiscord from "~icons/lucide/link"
import IconGitHub from "~icons/lucide/github"
import IconGithub from "~icons/lucide/github"
import IconLifeBuoy from "~icons/lucide/life-buoy"
import IconMessageCircle from "~icons/lucide/message-circle"
import IconZap from "~icons/lucide/zap"
type Doc = {
text: string | string[]
text: string
alternates: string[]
icon: object | Component
}
@@ -59,25 +56,10 @@ export class GeneralSpotlightSearcherService extends StaticSpotlightSearcherServ
alternates: ["key", "shortcuts", "binding"],
icon: markRaw(IconZap),
},
link_github: {
text: [this.t("spotlight.general.social"), "GitHub"],
alternates: ["social", "github", "link"],
icon: markRaw(IconGitHub),
},
link_twitter: {
text: [this.t("spotlight.general.social"), "Twitter"],
alternates: ["social", "twitter", "link"],
icon: markRaw(IconTwitter),
},
link_discord: {
text: [this.t("spotlight.general.social"), "Discord"],
alternates: ["social", "discord", "link"],
icon: markRaw(IconDiscord),
},
link_linkedin: {
text: [this.t("spotlight.general.social"), "LinkedIn"],
alternates: ["social", "linkedin", "link"],
icon: markRaw(IconLinkedIn),
social_links: {
text: this.t("spotlight.general.social"),
alternates: ["social", "github", "binding"],
icon: markRaw(IconGithub),
},
})
@@ -105,7 +87,8 @@ export class GeneralSpotlightSearcherService extends StaticSpotlightSearcherServ
}
}
private openURL(url: string) {
private openDocs() {
const url = "https://docs.hoppscotch.io"
window.open(url, "_blank")
}
@@ -118,22 +101,13 @@ export class GeneralSpotlightSearcherService extends StaticSpotlightSearcherServ
invokeAction("flyouts.chat.open")
break
case "open_docs":
this.openURL("https://docs.hoppscotch.io")
this.openDocs()
break
case "open_keybindings":
invokeAction("flyouts.keybinds.toggle")
break
case "link_github":
this.openURL("https://hoppscotch.io/github")
break
case "link_twitter":
this.openURL("https://twitter.com/hoppscotch_io")
break
case "link_discord":
this.openURL("https://hoppscotch.io/discord")
break
case "link_linkedin":
this.openURL("https://www.linkedin.com/company/hoppscotch/")
case "social_links":
invokeAction("modals.social.toggle")
break
}
}

View File

@@ -1,126 +0,0 @@
import { Ref, computed, effectScope, markRaw, ref, unref, watch } from "vue"
import { getI18n } from "~/modules/i18n"
import {
SpotlightSearcher,
SpotlightSearcherResult,
SpotlightSearcherSessionState,
SpotlightService,
} from ".."
import { Service } from "dioc"
import { useService } from "dioc/vue"
import MiniSearch from "minisearch"
import IconCheckCircle from "~/components/app/spotlight/entry/IconSelected.vue"
import { InterceptorService } from "~/services/interceptor.service"
import IconCircle from "~icons/lucide/circle"
/**
* This searcher is responsible for searching through the interceptor.
* And switching between them.
*/
export class InterceptorSpotlightSearcherService
extends Service
implements SpotlightSearcher
{
public static readonly ID = "INTERCEPTOR_SPOTLIGHT_SEARCHER_SERVICE"
private t = getI18n()
public searcherID = "interceptor"
public searcherSectionTitle = this.t("settings.interceptor")
private readonly spotlight = this.bind(SpotlightService)
constructor() {
super()
this.spotlight.registerSearcher(this)
}
private interceptorService = useService(InterceptorService)
createSearchSession(
query: Readonly<Ref<string>>
): [Ref<SpotlightSearcherSessionState>, () => void] {
const loading = ref(false)
const results = ref<SpotlightSearcherResult[]>([])
const minisearch = new MiniSearch({
fields: ["name", "alternates"],
storeFields: ["name"],
})
const interceptorSelection = this.interceptorService
.currentInterceptorID as Ref<string>
const interceptors = this.interceptorService.availableInterceptors
minisearch.addAll(
interceptors.value.map((entry) => {
let id = `interceptor-${entry.interceptorID}`
if (entry.interceptorID === interceptorSelection.value) {
id += "-selected"
}
const name = unref(entry.name(this.t))
return {
id,
name,
alternates: ["interceptor", "change", name],
}
})
)
const scopeHandle = effectScope()
scopeHandle.run(() => {
watch(
[query],
([query]) => {
results.value = minisearch
.search(query, {
prefix: true,
fuzzy: true,
boost: {
reltime: 2,
},
weights: {
fuzzy: 0.2,
prefix: 0.8,
},
})
.map((x) => {
return {
id: x.id,
icon: markRaw(
x.id.endsWith("-selected") ? IconCheckCircle : IconCircle
),
score: x.score,
text: {
type: "text",
text: [this.t("spotlight.section.interceptor"), x.name],
},
}
})
},
{ immediate: true }
)
})
const onSessionEnd = () => {
scopeHandle.stop()
minisearch.removeAll()
}
const resultObj = computed<SpotlightSearcherSessionState>(() => ({
loading: loading.value,
results: results.value,
}))
return [resultObj, onSessionEnd]
}
onResultSelect(result: SpotlightSearcherResult): void {
const selectedInterceptor = result.id.split("-")[1]
this.interceptorService.currentInterceptorID.value = selectedInterceptor
}
}

View File

@@ -1,4 +1,4 @@
import { Component, computed, markRaw, reactive } from "vue"
import { Component, markRaw, reactive } from "vue"
import { invokeAction } from "~/helpers/actions"
import { getI18n } from "~/modules/i18n"
import { SpotlightSearcherResult, SpotlightService } from ".."
@@ -7,11 +7,12 @@ import {
StaticSpotlightSearcherService,
} from "./base/static.searcher"
import { useRoute } from "vue-router"
import { RequestOptionTabs } from "~/components/http/RequestOptions.vue"
import { currentActiveTab } from "~/helpers/rest/tab"
import IconWindow from "~icons/lucide/app-window"
import IconCheckCircle from "~icons/lucide/check-circle"
import IconCheck from "~icons/lucide/check"
import IconChevronLeft from "~icons/lucide/chevron-left"
import IconChevronRight from "~icons/lucide/chevron-right"
import IconCode2 from "~icons/lucide/code-2"
import IconCopy from "~icons/lucide/copy"
import IconFileCode from "~icons/lucide/file-code"
@@ -24,7 +25,6 @@ type Doc = {
text: string | string[]
alternates: string[]
icon: object | Component
excludeFromSearch?: boolean
}
/**
@@ -43,160 +43,116 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
private readonly spotlight = this.bind(SpotlightService)
private route = useRoute()
private isRESTPage = computed(() => this.route.name === "index")
private isGQLPage = computed(() => this.route.name === "graphql")
private documents: Record<string, Doc> = reactive({
send_request: {
text: this.t("shortcut.request.send_request"),
alternates: ["request", "send"],
icon: markRaw(IconPlay),
excludeFromSearch: computed(
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
},
save_to_collections: {
text: this.t("spotlight.request.save_as_new"),
text: [
this.t("request.save_as"),
this.t("shortcut.request.save_to_collections"),
],
alternates: ["save", "collections"],
icon: markRaw(IconSave),
excludeFromSearch: computed(
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
},
save_request: {
text: this.t("shortcut.request.save_request"),
alternates: ["save", "request"],
icon: markRaw(IconSave),
excludeFromSearch: computed(
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
},
rename_request: {
text: this.t("shortcut.request.rename"),
alternates: ["rename", "request"],
icon: markRaw(IconRename),
excludeFromSearch: computed(
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
},
copy_request_link: {
text: this.t("shortcut.request.copy_request_link"),
alternates: ["copy", "link"],
icon: markRaw(IconCopy),
excludeFromSearch: computed(() => !this.isRESTPage.value),
},
reset_request: {
text: this.t("shortcut.request.reset_request"),
alternates: ["reset", "request"],
icon: markRaw(IconRotateCCW),
excludeFromSearch: computed(() => !this.isRESTPage.value),
},
import_curl: {
text: this.t("shortcut.request.import_curl"),
alternates: ["import", "curl"],
icon: markRaw(IconFileCode),
excludeFromSearch: computed(() => !this.isRESTPage.value),
},
show_code: {
text: this.t("shortcut.request.show_code"),
alternates: ["show", "code"],
icon: markRaw(IconCode2),
excludeFromSearch: computed(() => !this.isRESTPage.value),
},
// Change request method
next_method: {
text: this.t("shortcut.request.next_method"),
alternates: ["next", "method"],
icon: markRaw(IconChevronRight),
},
previous_method: {
text: this.t("shortcut.request.previous_method"),
alternates: ["previous", "method"],
icon: markRaw(IconChevronLeft),
},
get_method: {
text: [this.t("spotlight.request.select_method"), "GET"],
text: this.t("shortcut.request.get_method"),
alternates: ["get", "method"],
icon: markRaw(IconCheckCircle),
excludeFromSearch: computed(() => !this.isRESTPage.value),
icon: markRaw(IconCheck),
},
head_method: {
text: [this.t("spotlight.request.select_method"), "HEAD"],
text: this.t("shortcut.request.head_method"),
alternates: ["head", "method"],
icon: markRaw(IconCheckCircle),
excludeFromSearch: computed(() => !this.isRESTPage.value),
icon: markRaw(IconCheck),
},
post_method: {
text: [this.t("spotlight.request.select_method"), "POST"],
text: this.t("shortcut.request.post_method"),
alternates: ["post", "method"],
icon: markRaw(IconCheckCircle),
excludeFromSearch: computed(() => !this.isRESTPage.value),
icon: markRaw(IconCheck),
},
put_method: {
text: [this.t("spotlight.request.select_method"), "PUT"],
text: this.t("shortcut.request.put_method"),
alternates: ["put", "method"],
icon: markRaw(IconCheckCircle),
excludeFromSearch: computed(() => !this.isRESTPage.value),
icon: markRaw(IconCheck),
},
delete_method: {
text: [this.t("spotlight.request.select_method"), "DELETE"],
text: this.t("shortcut.request.delete_method"),
alternates: ["delete", "method"],
icon: markRaw(IconCheckCircle),
excludeFromSearch: computed(() => !this.isRESTPage.value),
icon: markRaw(IconCheck),
},
// Change sub tabs
tab_parameters: {
text: [
this.t("spotlight.request.switch_to"),
this.t("spotlight.request.tab_parameters"),
],
text: this.t("spotlight.request.tab_parameters"),
alternates: ["parameters", "tab"],
icon: markRaw(IconWindow),
excludeFromSearch: computed(
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
},
tab_body: {
text: [
this.t("spotlight.request.switch_to"),
this.t("spotlight.request.tab_body"),
],
text: this.t("spotlight.request.tab_body"),
alternates: ["body", "tab"],
icon: markRaw(IconWindow),
excludeFromSearch: computed(
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
},
tab_headers: {
text: [
this.t("spotlight.request.switch_to"),
this.t("spotlight.request.tab_headers"),
],
text: this.t("spotlight.request.tab_headers"),
alternates: ["headers", "tab"],
icon: markRaw(IconWindow),
excludeFromSearch: computed(
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
},
tab_authorization: {
text: [
this.t("spotlight.request.switch_to"),
this.t("spotlight.request.tab_authorization"),
],
text: this.t("spotlight.request.tab_authorization"),
alternates: ["authorization", "tab"],
icon: markRaw(IconWindow),
excludeFromSearch: computed(
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
},
tab_pre_request_script: {
text: [
this.t("spotlight.request.switch_to"),
this.t("spotlight.request.tab_pre_request_script"),
],
text: this.t("spotlight.request.tab_pre_request_script"),
alternates: ["pre-request", "script", "tab"],
icon: markRaw(IconWindow),
excludeFromSearch: computed(() => !this.isRESTPage.value),
},
tab_tests: {
text: [
this.t("spotlight.request.switch_to"),
this.t("spotlight.request.tab_tests"),
],
text: this.t("spotlight.request.tab_tests"),
alternates: ["tests", "tab"],
icon: markRaw(IconWindow),
excludeFromSearch: computed(() => !this.isRESTPage.value),
},
})
@@ -253,6 +209,12 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
case "reset_request":
invokeAction("request.reset")
break
case "next_method":
invokeAction("request.method.next")
break
case "previous_method":
invokeAction("request.method.prev")
break
case "get_method":
invokeAction("request.method.get")
break

View File

@@ -10,10 +10,11 @@ import {
} from "./base/static.searcher"
import IconCloud from "~icons/lucide/cloud"
import IconGlobe from "~icons/lucide/globe"
import IconMonitor from "~icons/lucide/monitor"
import IconMoon from "~icons/lucide/moon"
import IconSun from "~icons/lucide/sun"
import IconGlobe from "~icons/lucide/globe"
import IconShieldCheck from "~icons/lucide/shield-check"
import IconType from "~icons/lucide/type"
type Doc = {
@@ -127,6 +128,22 @@ export class SettingsSpotlightSearcherService extends StaticSpotlightSearcherSer
alternates: ["language", "change language"],
icon: markRaw(IconGlobe),
},
change_interceptor: {
text: [
this.t("spotlight.section.interceptor"),
this.t("spotlight.settings.change_interceptor"),
],
alternates: ["interceptor", "change interceptor"],
icon: markRaw(IconShieldCheck),
},
install_ext: {
text: [
this.t("spotlight.section.interceptor"),
this.t("spotlight.settings.install_extension"),
],
alternates: ["install extension", "extension", "interceptor"],
icon: markRaw(IconShieldCheck),
},
})
constructor() {
@@ -157,12 +174,27 @@ export class SettingsSpotlightSearcherService extends StaticSpotlightSearcherSer
applySetting("BG_COLOR", theme)
}
installExtension() {
const url = navigator.userAgent.includes("Firefox")
? "https://addons.mozilla.org/en-US/firefox/addon/hoppscotch"
: "https://chrome.google.com/webstore/detail/hoppscotch-browser-extens/amknoiejhlmhancpahfcfcfhllgkpbld"
window.open(url, "_blank")
}
public onDocSelected(id: string): void {
switch (id) {
case "change_interceptor":
invokeAction("navigation.jump.settings")
break
case "change_lang":
invokeAction("navigation.jump.settings")
break
case "install_ext":
this.installExtension()
break
// theme actions
case "theme_system":
invokeAction("settings.theme.system")

View File

@@ -1,4 +1,4 @@
import { Component, computed, markRaw, reactive } from "vue"
import { Component, markRaw, reactive } from "vue"
import { getI18n } from "~/modules/i18n"
import { SpotlightSearcherResult, SpotlightService } from ".."
import {
@@ -6,23 +6,19 @@ import {
StaticSpotlightSearcherService,
} from "./base/static.searcher"
import { useRoute } from "vue-router"
import { getDefaultRESTRequest } from "~/helpers/rest/default"
import {
closeOtherTabs,
closeTab,
createNewTab,
currentTabID,
getActiveTabs,
} from "~/helpers/rest/tab"
import IconWindow from "~icons/lucide/app-window"
import { invokeAction } from "~/helpers/actions"
import { getDefaultRESTRequest } from "~/helpers/rest/default"
type Doc = {
text: string
alternates: string[]
icon: object | Component
excludeFromSearch?: boolean
}
/**
@@ -41,39 +37,21 @@ export class TabSpotlightSearcherService extends StaticSpotlightSearcherService<
private readonly spotlight = this.bind(SpotlightService)
private route = useRoute()
private showAction = computed(
() => this.route.name === "index" ?? this.route.name === "graphql"
)
private documents: Record<string, Doc> = reactive({
duplicate_tab: {
text: this.t("spotlight.tab.duplicate"),
alternates: ["tab", "duplicate", "duplicate tab"],
icon: markRaw(IconWindow),
excludeFromSearch: computed(() => !this.showAction.value),
},
close_current_tab: {
text: this.t("spotlight.tab.close_current"),
alternates: ["tab", "close", "close tab"],
icon: markRaw(IconWindow),
excludeFromSearch: computed(
() => !this.showAction.value ?? getActiveTabs().value.length === 1
),
},
close_other_tabs: {
text: this.t("spotlight.tab.close_others"),
alternates: ["tab", "close", "close all"],
icon: markRaw(IconWindow),
excludeFromSearch: computed(
() => !this.showAction.value ?? getActiveTabs().value.length < 2
),
},
open_new_tab: {
text: this.t("spotlight.tab.new_tab"),
alternates: ["tab", "new", "open tab"],
icon: markRaw(IconWindow),
excludeFromSearch: computed(() => !this.showAction.value),
},
})
@@ -102,10 +80,6 @@ export class TabSpotlightSearcherService extends StaticSpotlightSearcherService<
}
public onDocSelected(id: string): void {
if (id === "duplicate_tab")
invokeAction("request.duplicate-tab", {
tabID: currentTabID.value,
})
if (id === "close_current_tab") closeTab(currentTabID.value)
if (id === "close_other_tabs") closeOtherTabs(currentTabID.value)
if (id === "open_new_tab")

View File

@@ -8,7 +8,7 @@ import {
ref,
watch,
} from "vue"
import { invokeAction } from "~/helpers/actions"
import { activeActions$, invokeAction } from "~/helpers/actions"
import { getI18n } from "~/modules/i18n"
import {
SpotlightSearcher,
@@ -24,7 +24,6 @@ import {
import { Service } from "dioc"
import * as E from "fp-ts/Either"
import MiniSearch from "minisearch"
import IconCheckCircle from "~/components/app/spotlight/entry/IconSelected.vue"
import { useStreamStatic } from "~/composables/stream"
import { runGQLQuery } from "~/helpers/backend/GQLClient"
import { GetMyTeamsDocument, GetMyTeamsQuery } from "~/helpers/backend/graphql"
@@ -37,7 +36,7 @@ import IconUserPlus from "~icons/lucide/user-plus"
import IconUsers from "~icons/lucide/users"
type Doc = {
text: string | string[]
text: string
alternates: string[]
icon: object | Component
excludeFromSearch?: boolean
@@ -67,6 +66,14 @@ export class WorkspaceSpotlightSearcherService extends StaticSpotlightSearcherSe
}
)[0]
private activeActions = useStreamStatic(activeActions$, [], () => {
/* noop */
})[0]
private isLoggedInUser = computed(() =>
this.activeActions.value.includes("user.logout")
)
private isTeamSelected = computed(
() =>
this.workspace.value.type === "team" &&
@@ -75,33 +82,31 @@ export class WorkspaceSpotlightSearcherService extends StaticSpotlightSearcherSe
private documents: Record<string, Doc> = reactive({
new_team: {
text: [this.t("team.title"), this.t("spotlight.workspace.new")],
text: this.t("spotlight.workspace.new"),
alternates: ["new", "team", "workspace"],
icon: markRaw(IconUsers),
excludeFromSearch: computed(() => !this.isLoggedInUser.value),
},
edit_team: {
text: [this.t("team.title"), this.t("spotlight.workspace.edit")],
text: this.t("spotlight.workspace.edit"),
alternates: ["edit", "team", "workspace"],
icon: markRaw(IconEdit),
excludeFromSearch: computed(() => !this.isTeamSelected.value),
},
invite_members: {
text: [this.t("team.title"), this.t("spotlight.workspace.invite")],
text: this.t("spotlight.workspace.invite"),
alternates: ["invite", "members", "workspace"],
icon: markRaw(IconUserPlus),
excludeFromSearch: computed(() => !this.isTeamSelected.value),
},
delete_team: {
text: [this.t("team.title"), this.t("spotlight.workspace.delete")],
text: this.t("spotlight.workspace.delete"),
alternates: ["delete", "team", "workspace"],
icon: markRaw(IconTrash2),
excludeFromSearch: computed(() => !this.isTeamSelected.value),
},
switch_to_personal: {
text: [
this.t("team.title"),
this.t("spotlight.workspace.switch_to_personal"),
],
text: this.t("spotlight.workspace.switch_to_personal"),
alternates: ["switch", "team", "workspace", "personal"],
icon: markRaw(IconUser),
excludeFromSearch: computed(() => !this.isTeamSelected.value),
@@ -140,13 +145,8 @@ export class WorkspaceSpotlightSearcherService extends StaticSpotlightSearcherSe
}
public onDocSelected(id: string): void {
if (id === "new_team") {
if (platform.auth.getCurrentUser()) {
invokeAction(`modals.team.new`)
} else {
invokeAction(`modals.login.toggle`)
}
} else if (id === "edit_team") invokeAction(`modals.team.edit`)
if (id === "new_team") invokeAction(`modals.team.new`)
else if (id === "edit_team") invokeAction(`modals.team.edit`)
else if (id === "invite_members") invokeAction(`modals.team.invite`)
else if (id === "delete_team") this.deleteTeam()
else if (id === "switch_to_personal")
@@ -197,14 +197,6 @@ export class SwitchWorkspaceSpotlightSearcherService
})
}
private workspace = useStreamStatic(
workspaceStatus$,
{ type: "personal" },
() => {
/* noop */
}
)[0]
createSearchSession(
query: Readonly<Ref<string>>
): [Ref<SpotlightSearcherSessionState>, () => void] {
@@ -219,16 +211,8 @@ export class SwitchWorkspaceSpotlightSearcherService
this.fetchMyTeams().then((teams) => {
minisearch.addAll(
teams.map((entry) => {
let id = `workspace-${entry.id}`
// if id matches add -selected to it
if (
this.workspace.value.type === "team" &&
this.workspace.value.teamID === entry.id
) {
id += "-selected"
}
return {
id,
id: `workspace-${entry.id}`,
name: entry.name,
alternates: ["team", "workspace", "change", "switch"],
}
@@ -257,9 +241,7 @@ export class SwitchWorkspaceSpotlightSearcherService
.map((x) => {
return {
id: x.id,
icon: markRaw(
x.id.endsWith("-selected") ? IconCheckCircle : IconUsers
),
icon: markRaw(IconUsers),
score: x.score,
text: {
type: "text",

View File

@@ -1,9 +1,6 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script>
globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"')
</script>
<title>Hoppscotch • Open source API development ecosystem</title>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />

View File

@@ -36,7 +36,7 @@ export const META_TAGS = (env: Record<string, string>): IHTMLTag[] => [
},
{
name: "image",
content: `${env.APP_BASE_URL}/banner.png`,
content: `${env.VITE_BASE_URL}/banner.png`,
},
// Open Graph tags
{
@@ -49,7 +49,7 @@ export const META_TAGS = (env: Record<string, string>): IHTMLTag[] => [
},
{
name: "og:image",
content: `${env.APP_BASE_URL}/banner.png`,
content: `${env.VITE_BASE_URL}/banner.png`,
},
// Twitter tags
{
@@ -74,7 +74,7 @@ export const META_TAGS = (env: Record<string, string>): IHTMLTag[] => [
},
{
name: "twitter:image",
content: `${env.APP_BASE_URL}/banner.png`,
content: `${env.VITE_BASE_URL}/banner.png`,
},
// Add to homescreen for Chrome on Android. Fallback for PWA (handled by nuxt)
{
@@ -84,7 +84,7 @@ export const META_TAGS = (env: Record<string, string>): IHTMLTag[] => [
// Windows phone tile icon
{
name: "msapplication-TileImage",
content: `${env.APP_BASE_URL}/icon.png`,
content: `${env.VITE_BASE_URL}/icon.png`,
},
{
name: "msapplication-TileColor",

View File

@@ -29,7 +29,6 @@
"@hoppscotch/common": "workspace:^",
"@hoppscotch/data": "workspace:^",
"axios": "^1.4.0",
"@import-meta-env/unplugin": "^0.4.8",
"buffer": "^6.0.3",
"fp-ts": "^2.16.1",
"process": "^0.11.10",
@@ -75,7 +74,6 @@
"vite-plugin-windicss": "^1.9.1",
"vitest": "^0.34.2",
"vue-tsc": "^1.8.8",
"vite-plugin-fonts": "^0.6.0",
"windicss": "^3.5.6"
}
}

View File

@@ -1,17 +0,0 @@
#!/usr/local/bin/node
import { execSync } from "child_process"
import fs from "fs"
const envFileContent = Object.entries(process.env)
.filter(([env]) => env.startsWith("VITE_"))
.map(
([env, val]) =>
`${env}=${val.startsWith('"') && val.endsWith('"') ? val : `"${val}"`}`
)
.join("\n")
fs.writeFileSync("build.env", envFileContent)
execSync(`npx import-meta-env -x build.env -e build.env -p "/site/**/*"`)
fs.rmSync("build.env")

View File

@@ -17,12 +17,10 @@ import { FileSystemIconLoader } from "unplugin-icons/loaders"
import * as path from "path"
import Unfonts from "unplugin-fonts/vite"
import legacy from "@vitejs/plugin-legacy"
import ImportMetaEnv from "@import-meta-env/unplugin"
const ENV = loadEnv("development", path.resolve(__dirname, "../../"), ["VITE_"])
const ENV = loadEnv("development", path.resolve(__dirname, "../../"))
export default defineConfig({
envPrefix: process.env.HOPP_ALLOW_RUNTIME_ENV ? "VITE_BUILDTIME_" : "VITE_",
envDir: path.resolve(__dirname, "../../"),
// TODO: Migrate @hoppscotch/data to full ESM
define: {
@@ -67,7 +65,6 @@ export default defineConfig({
"@lib": path.resolve(__dirname, "./src/lib"),
stream: "stream-browserify",
util: "util",
querystring: "qs",
},
dedupe: ["vue"],
},
@@ -81,15 +78,14 @@ export default defineConfig({
routeStyle: "nuxt",
dirs: "../hoppscotch-common/src/pages",
importMode: "async",
onRoutesGenerated(routes) {
onRoutesGenerated: (routes) =>
generateSitemap({
routes,
nuxtStyle: true,
allowRobots: true,
dest: ".sitemap-gen",
hostname: ENV.VITE_BASE_URL,
})
},
}),
}),
StaticCopy({
targets: [
@@ -243,11 +239,5 @@ export default defineConfig({
modernPolyfills: ["es.string.replace-all"],
renderLegacyChunks: false,
}),
process.env.HOPP_ALLOW_RUNTIME_ENV
? ImportMetaEnv.vite({
example: "../../.env.example",
env: "../../.env",
})
: [],
],
})

View File

@@ -166,6 +166,12 @@ a {
@apply truncate;
@apply sm:inline-flex;
}
.env-icon {
@apply transition;
@apply inline-flex;
@apply items-center;
}
}
.tippy-svg-arrow {
@@ -326,7 +332,7 @@ pre.ace_editor {
@apply after:font-icon;
@apply after:text-current;
@apply after:right-3;
@apply after:content-["\e5cf"];
@apply after:content-["\e313"];
@apply after:text-lg;
}
@@ -481,10 +487,6 @@ pre.ace_editor {
}
}
.cm-scroller {
@apply overscroll-y-auto;
}
.cm-editor {
.cm-line::selection {
@apply bg-accentDark #{!important};
@@ -572,11 +574,3 @@ details[open] summary .indicator {
@apply rounded;
@apply border-0;
}
.gql-operation-not-highlight {
@apply opacity-50;
}
.gql-operation-highlight {
@apply opacity-100;
}

View File

@@ -2,9 +2,6 @@
<html lang="en" data-font-size="large">
<head>
<script>
globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"')
</script>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -17,4 +14,4 @@
<script type="module" src="/src/main.ts"></script>
</body>
</html>
</html>

View File

@@ -96,9 +96,10 @@
"load_info_error": "Unable to load team info",
"load_list_error": "Unable to Load Teams List",
"members": "Number of members",
"name": "Team name",
"name": "Team Name",
"no_members": "No members in this team. Add members to this team to collaborate",
"no_pending_invites": "No pending invites",
"no_teams": "No teams found",
"pending_invites": "Pending invites",
"remove": "Remove",
"rename": "Rename",
@@ -110,6 +111,7 @@
"team_members_tab": "Team members",
"teams": "Teams",
"uid": "UID",
"unnamed": "(Unnamed Team)",
"valid_name": "Please enter a valid team name",
"valid_owner_email": "Please enter a valid owner email"
},
@@ -123,6 +125,7 @@
"created_on": "Created On",
"date": "Date",
"delete": "Delete",
"delete_user": "Delete User",
"email": "Email",
"email_address": "Email Address",
"id": "User ID",

View File

@@ -51,12 +51,9 @@
"@graphql-codegen/typescript-document-nodes": "3.0.0",
"@graphql-codegen/typescript-operations": "3.0.0",
"@graphql-codegen/urql-introspection": "2.2.1",
"@import-meta-env/cli": "^0.6.3",
"@import-meta-env/unplugin": "^0.4.8",
"@intlify/vite-plugin-vue-i18n": "^7.0.0",
"@vitejs/plugin-vue": "^3.1.0",
"@vue/compiler-sfc": "^3.2.6",
"dotenv": "^16.0.3",
"graphql-tag": "^2.12.6",
"npm-run-all": "^4.1.5",
"sass": "^1.57.1",

View File

@@ -1,18 +0,0 @@
#!/usr/local/bin/node
import { execSync } from "child_process"
import fs from "fs"
const envFileContent = Object.entries(process.env)
.filter(([env]) => env.startsWith("VITE_"))
.map(([env, val]) => `${env}=${
(val.startsWith("\"") && val.endsWith("\""))
? val
: `"${val}"`
}`)
.join("\n")
fs.writeFileSync("build.env", envFileContent)
execSync(`npx import-meta-env -x build.env -e build.env -p "/site/**/*"`)
fs.rmSync("build.env")

View File

@@ -1,44 +1,12 @@
// 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';
import '@vue/runtime-core'
export {};
export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
AppHeader: typeof import('./components/app/Header.vue')['default'];
AppLogin: typeof import('./components/app/Login.vue')['default'];
AppLogout: typeof import('./components/app/Logout.vue')['default'];
AppModal: typeof import('./components/app/Modal.vue')['default'];
AppSidebar: typeof import('./components/app/Sidebar.vue')['default'];
AppToast: typeof import('./components/app/Toast.vue')['default'];
DashboardMetricsCard: typeof import('./components/dashboard/MetricsCard.vue')['default'];
HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary'];
HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary'];
HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor'];
HoppSmartAutoComplete: typeof import('@hoppscotch/ui')['HoppSmartAutoComplete'];
HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal'];
HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput'];
HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem'];
HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal'];
HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture'];
HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner'];
HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab'];
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default'];
IconLucideChevronDown: typeof import('~icons/lucide/chevron-down')['default'];
IconLucideHelpCircle: typeof import('~icons/lucide/help-circle')['default'];
IconLucideInbox: typeof import('~icons/lucide/inbox')['default'];
IconLucideUser: typeof import('~icons/lucide/user')['default'];
TeamsAdd: typeof import('./components/teams/Add.vue')['default'];
TeamsDetails: typeof import('./components/teams/Details.vue')['default'];
TeamsInvite: typeof import('./components/teams/Invite.vue')['default'];
TeamsMembers: typeof import('./components/teams/Members.vue')['default'];
TeamsPendingInvites: typeof import('./components/teams/PendingInvites.vue')['default'];
TeamsTable: typeof import('./components/teams/Table.vue')['default'];
Tippy: typeof import('vue-tippy')['Tippy'];
UsersInviteModal: typeof import('./components/users/InviteModal.vue')['default'];
UsersTable: typeof import('./components/users/Table.vue')['default'];
AppHeader: typeof import('./components/app/Header.vue')['default']
AppLogin: typeof import('./components/app/Login.vue')['default']
AppLogout: typeof import('./components/app/Logout.vue')['default']
@@ -49,18 +17,28 @@ declare module '@vue/runtime-core' {
HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary']
HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary']
HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor']
HoppSmartAutoComplete: typeof import('@hoppscotch/ui')['HoppSmartAutoComplete']
HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal']
HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput']
HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem']
HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal']
HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture']
HoppSmartPlaceholder: typeof import('@hoppscotch/ui')['HoppSmartPlaceholder']
HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner']
HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab']
HoppSmartTable: typeof import('@hoppscotch/ui')['HoppSmartTable']
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
IconLucideChevronDown: typeof import('~icons/lucide/chevron-down')['default']
IconLucideHelpCircle: typeof import('~icons/lucide/help-circle')['default']
IconLucideInbox: typeof import('~icons/lucide/inbox')['default']
IconLucideUser: typeof import('~icons/lucide/user')['default']
TeamsAdd: typeof import('./components/teams/Add.vue')['default']
TeamsDetails: typeof import('./components/teams/Details.vue')['default']
TeamsInvite: typeof import('./components/teams/Invite.vue')['default']
TeamsMembers: typeof import('./components/teams/Members.vue')['default']
TeamsPendingInvites: typeof import('./components/teams/PendingInvites.vue')['default']
TeamsTable: typeof import('./components/teams/Table.vue')['default']
Tippy: typeof import('vue-tippy')['Tippy']
UsersInviteModal: typeof import('./components/users/InviteModal.vue')['default']
UsersTable: typeof import('./components/users/Table.vue')['default']
}
}

View File

@@ -1,102 +0,0 @@
<template>
<table class="w-full">
<thead>
<tr class="text-secondary border-b border-dividerDark text-sm text-left">
<th class="px-3 pb-3">Team ID</th>
<th class="px-3 pb-3">Team Name</th>
<th class="px-3 pb-3">Number of Members</th>
<th class="px-3 pb-3"></th>
</tr>
</thead>
<tbody class="divide-y divide-divider">
<tr v-if="teamList.length === 0">
<div class="py-6 px-3">No teams found ...</div>
</tr>
<tr
v-else
v-for="team in teamList"
:key="team.id"
class="text-secondaryDark hover:bg-divider hover:cursor-pointer rounded-xl"
>
<td
@click="$emit('go-to-team-details', team.id)"
class="py-4 px-3 max-w-50"
>
<div class="flex">
<span class="truncate">
{{ team.id }}
</span>
</div>
</td>
<td
@click="$emit('go-to-team-details', team.id)"
class="py-4 px-3 min-w-80"
>
<span v-if="team.name" class="flex items-center ml-4 truncate">
{{ team.name }}
</span>
<span v-else class="flex items-center ml-4"> (Unnamed team) </span>
</td>
<td @click="$emit('go-to-team-details', team.id)" class="py-4 px-3">
<span class="ml-7">
{{ team.members?.length }}
</span>
</td>
<td>
<div class="relative">
<tippy interactive trigger="click" theme="popover">
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:icon="IconMoreHorizontal"
/>
<template #content="{ hide }">
<div
ref="tippyActions"
class="flex flex-col focus:outline-none"
tabindex="0"
@keyup.escape="hide()"
>
<HoppSmartItem
:icon="IconTrash"
:label="'Delete Team'"
class="!hover:bg-red-600 w-full"
@click="
() => {
$emit('delete-team', team.id);
hide();
}
"
/>
</div>
</template>
</tippy>
</div>
</td>
</tr>
</tbody>
</table>
</template>
<script setup lang="ts">
import { TippyComponent } from 'vue-tippy';
import { ref } from 'vue';
import IconTrash from '~icons/lucide/trash';
import IconMoreHorizontal from '~icons/lucide/more-horizontal';
import { TeamListQuery } from '~/helpers/backend/graphql';
// Template refs
const tippyActions = ref<TippyComponent | null>(null);
defineProps<{
teamList: TeamListQuery['admin']['allTeams'];
}>();
defineEmits<{
(event: 'go-to-team-details', teamID: string): void;
(event: 'delete-team', teamID: string): void;
}>();
</script>

View File

@@ -1,168 +0,0 @@
<template>
<table class="w-full">
<thead>
<tr class="text-secondary border-b border-dividerDark text-sm text-left">
<th class="px-3 pb-3">{{ t('users.id') }}</th>
<th class="px-3 pb-3">{{ t('users.name') }}</th>
<th class="px-3 pb-3">{{ t('users.email') }}</th>
<th class="px-3 pb-3">{{ t('users.date') }}</th>
<th class="px-3 pb-3"></th>
</tr>
</thead>
<tbody class="divide-y divide-divider">
<tr
v-for="user in usersList"
:key="user.uid"
class="text-secondaryDark hover:bg-divider hover:cursor-pointer rounded-xl"
>
<td
@click="$emit('goToUserDetails', user.uid)"
class="py-2 px-3 max-w-30"
>
<div class="flex">
<span class="truncate">
{{ user.uid }}
</span>
</div>
</td>
<td @click="$emit('goToUserDetails', user.uid)" class="py-2 px-3">
<div v-if="user.displayName" class="flex items-center space-x-3">
<span>
{{ user.displayName }}
</span>
<span
v-if="user.isAdmin"
class="text-xs font-medium px-3 py-0.5 rounded-full bg-green-900 text-green-300"
>
{{ t('users.admin') }}
</span>
</div>
<div v-else class="flex items-center space-x-3">
<span> {{ t('users.unnamed') }} </span>
<span
v-if="user.isAdmin"
class="text-xs font-medium px-3 py-0.5 rounded-full bg-green-900 text-green-300"
>
{{ t('users.admin') }}
</span>
</div>
</td>
<td @click="$emit('goToUserDetails', user.uid)" class="py-2 px-3">
<span>
{{ user.email }}
</span>
</td>
<td @click="$emit('goToUserDetails', user.uid)" class="py-2 px-3">
<div class="flex items-center">
<div class="flex flex-col">
{{ getCreatedDate(user.createdOn) }}
<div class="text-gray-400 text-tiny">
{{ getCreatedTime(user.createdOn) }}
</div>
</div>
</div>
</td>
<td>
<div class="relative">
<span>
<tippy interactive trigger="click" theme="popover">
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:icon="IconMoreHorizontal"
/>
<template #content="{ hide }">
<div
ref="tippyActions"
class="flex flex-col focus:outline-none"
tabindex="0"
@keyup.escape="hide()"
>
<HoppSmartItem
v-if="!user.isAdmin"
:icon="IconUserCheck"
:label="'Make Admin'"
class="!hover:bg-emerald-600"
@click="
() => {
$emit('makeUserAdmin', user.uid);
hide();
}
"
/>
<HoppSmartItem
v-else
:icon="IconUserMinus"
:label="'Remove Admin Status'"
class="!hover:bg-emerald-600"
@click="
() => {
$emit('makeAdminToUser', user.uid);
hide();
}
"
/>
<HoppSmartItem
v-if="!user.isAdmin"
:icon="IconTrash"
:label="'Delete User'"
class="!hover:bg-red-600"
@click="
() => {
$emit('deleteUser', user.uid);
hide();
}
"
/>
</div>
</template>
</tippy>
</span>
</div>
</td>
</tr>
</tbody>
</table>
</template>
<script lang="ts" setup>
import { format } from 'date-fns';
import { ref } from 'vue';
import IconTrash from '~icons/lucide/trash';
import IconUserMinus from '~icons/lucide/user-minus';
import IconUserCheck from '~icons/lucide/user-check';
import IconMoreHorizontal from '~icons/lucide/more-horizontal';
import { UsersListQuery } from '~/helpers/backend/graphql';
import { TippyComponent } from 'vue-tippy';
import { useI18n } from '~/composables/i18n';
const t = useI18n();
defineProps<{
usersList: UsersListQuery['admin']['allUsers'];
}>();
defineEmits<{
(event: 'goToUserDetails', uid: string): void;
(event: 'makeUserAdmin', uid: string): void;
(event: 'makeAdminToUser', uid: string): void;
(event: 'deleteUser', uid: string): void;
}>();
// Get Proper Date Formats
const getCreatedDate = (date: string) => format(new Date(date), 'dd-MM-yyyy');
const getCreatedTime = (date: string) => format(new Date(date), 'hh:mm a');
// Template refs
const tippyActions = ref<TippyComponent | null>(null);
</script>
<style scoped>
.tippy-box[data-theme~='popover'] .tippy-content {
padding: 0;
}
</style>

View File

@@ -13,7 +13,7 @@
<div class="overflow-x-auto">
<div
v-if="fetching && !error && teamList.length === 0"
v-if="fetching && !error && teamsList.length === 0"
class="flex justify-center"
>
<HoppSmartSpinner />
@@ -21,16 +21,94 @@
<div v-else-if="error">{{ t('teams.load_list_error') }}</div>
<TeamsTable
v-else
:teamList="teamList"
@goToTeamDetails="goToTeamDetails"
@deleteTeam="deleteTeam"
class=""
/>
<div v-else-if="teamsList.length == 0" class="px-2">
<p class="text-lg">
{{ t('teams.no_teams') }}
</p>
</div>
<div v-else>
<HoppSmartTable :list="teamsList">
<template #head>
<tr
class="text-secondary border-b border-dividerDark text-sm text-left bg-primaryLight"
>
<th class="px-6 py-2">{{ t('teams.id') }}</th>
<th class="px-6 py-3">{{ t('teams.name') }}</th>
<th class="px-6 py-2">{{ t('teams.members') }}</th>
<th class="px-6 py-2"></th>
</tr>
</template>
<template #body="{ list }">
<tr
v-for="team in list"
:key="team.id"
class="text-secondaryDark hover:bg-divider hover:cursor-pointer rounded-xl"
@click="goToTeamDetails(team.id)"
>
<td class="py-4 px-3 max-w-50">
<div class="flex">
<span class="truncate">
{{ team.id }}
</span>
</div>
</td>
<td class="py-4 px-3 min-w-80">
<span
v-if="team.name"
class="flex items-center ml-4 truncate"
>
{{ team.name }}
</span>
<span v-else class="flex items-center ml-4">
{{ t('teams.unnamed') }}
</span>
</td>
<td class="py-4 px-3">
<span class="ml-7">
{{ team.members?.length }}
</span>
</td>
<td @click.stop>
<div class="relative">
<tippy interactive trigger="click" theme="popover">
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:icon="IconMoreHorizontal"
/>
<template #content="{ hide }">
<div
ref="tippyActions"
class="flex flex-col focus:outline-none"
tabindex="0"
@keyup.escape="hide()"
>
<HoppSmartItem
:icon="IconTrash"
:label="t('teams.delete_team')"
class="!hover:bg-red-600 w-full"
@click="
() => {
deleteTeam(team.id);
hide();
}
"
/>
</div>
</template>
</tippy>
</div>
</td>
</tr>
</template>
</HoppSmartTable>
</div>
<div
v-if="hasNextPage && teamList.length >= teamsPerPage"
v-if="hasNextPage && teamsList.length >= teamsPerPage"
class="flex justify-center my-5 px-3 py-2 cursor-pointer font-semibold rounded-3xl bg-dividerDark hover:bg-divider transition mx-auto w-38 text-secondaryDark"
@click="fetchNextTeams"
>
@@ -66,10 +144,12 @@ import {
UsersListDocument,
} from '../../helpers/backend/graphql';
import { usePagedQuery } from '~/composables/usePagedQuery';
import { ref, watch, computed } from 'vue';
import { computed, ref, watch } from 'vue';
import { useMutation, useQuery } from '@urql/vue';
import { useToast } from '~/composables/toast';
import IconAddUsers from '~icons/lucide/plus';
import IconTrash from '~icons/lucide/trash';
import IconMoreHorizontal from '~icons/lucide/more-horizontal';
import { useI18n } from '~/composables/i18n';
const t = useI18n();
@@ -96,7 +176,7 @@ const {
error,
goToNextPage: fetchNextTeams,
refetch,
list: teamList,
list: teamsList,
hasNextPage,
} = usePagedQuery(
TeamListDocument,
@@ -143,9 +223,7 @@ const createTeam = async (newTeamName: string, ownerEmail: string) => {
// Go To Individual Team Details Page
const router = useRouter();
const goToTeamDetails = (teamId: string) => {
router.push('/teams/' + teamId);
};
const goToTeamDetails = (teamId: string) => router.push('/teams/' + teamId);
// Reload Teams Page when routed back to the teams page
const route = useRoute();
@@ -175,7 +253,7 @@ const deleteTeamMutation = async (id: string | null) => {
if (result.error) {
toast.error(`${t('state.delete_team_failure')}`);
} else {
teamList.value = teamList.value.filter((team) => team.id !== id);
teamsList.value = teamsList.value.filter((team) => team.id !== id);
toast.success(`${t('state.delete_team_success')}`);
}
});

View File

@@ -37,7 +37,7 @@
t('users.uid')
}}</label>
<div
class="w-full p-3 mt-2 bg-zinc-800 border-gray-600 rounded-md focus:border-emerald-600 focus:ring focus:ring-opacity-40 focus:ring-emerald-500"
class="w-full p-3 mt-2 bg-divider border-gray-600 rounded-md focus:border-emerald-600 focus:ring focus:ring-opacity-40 focus:ring-emerald-500"
>
{{ user.uid }}
</div>
@@ -47,7 +47,7 @@
t('users.name')
}}</label>
<div
class="w-full p-3 mt-2 bg-zinc-800 border-gray-600 rounded-md focus:border-emerald-600 focus:ring focus:ring-opacity-40 focus:ring-emerald-500"
class="w-full p-3 mt-2 bg-divider border-gray-600 rounded-md focus:border-emerald-600 focus:ring focus:ring-opacity-40 focus:ring-emerald-500"
>
<span v-if="user.displayName">
{{ user.displayName }}
@@ -60,7 +60,7 @@
t('users.email')
}}</label>
<div
class="w-full p-3 mt-2 bg-zinc-800 border-gray-200 border-gray-600 rounded-md focus:border-emerald-600 focus:ring focus:ring-opacity-40 focus:ring-emerald-500"
class="w-full p-3 mt-2 bg-divider border-gray-200 border-gray-600 rounded-md focus:border-emerald-600 focus:ring focus:ring-opacity-40 focus:ring-emerald-500"
>
{{ user.email }}
</div>
@@ -70,7 +70,7 @@
t('users.created_on')
}}</label>
<div
class="w-full p-3 mt-2 bg-zinc-800 border-gray-600 rounded-md focus:border-emerald-600 focus:ring focus:ring-opacity-40 focus:ring-emerald-500"
class="w-full p-3 mt-2 bg-divider border-gray-600 rounded-md focus:border-emerald-600 focus:ring focus:ring-opacity-40 focus:ring-emerald-500"
>
{{ getCreatedDateAndTime(user.createdOn) }}
</div>

View File

@@ -31,16 +31,139 @@
<div v-else-if="error">{{ t('users.load_list_error') }}</div>
<UsersTable
v-else-if="usersList.length >= 1"
:usersList="usersList"
:fetching="fetching"
:error="error"
@goToUserDetails="goToUserDetails"
@makeUserAdmin="makeUserAdmin"
@makeAdminToUser="makeAdminToUser"
@deleteUser="deleteUser"
/>
<div v-else-if="usersList.length > 0">
<HoppSmartTable :list="usersList">
<template #head>
<tr
class="text-secondary border-b border-dividerDark text-sm text-left bg-primaryLight"
>
<th class="px-6 py-2">{{ t('users.id') }}</th>
<th class="px-6 py-2">{{ t('users.name') }}</th>
<th class="px-6 py-2">{{ t('users.email') }}</th>
<th class="px-6 py-2">{{ t('users.date') }}</th>
<th class="px-6 py-2"></th>
</tr>
</template>
<template #body="{ list }">
<tr
v-for="user in list"
:key="user.uid"
class="text-secondaryDark hover:bg-divider hover:cursor-pointer rounded-xl"
@click="goToUserDetails(user.uid)"
>
<td class="py-2 px-3 max-w-30">
<div class="flex">
<span class="truncate">
{{ user.uid }}
</span>
</div>
</td>
<td class="py-2 px-3">
<div
v-if="user.displayName"
class="flex items-center space-x-3"
>
<span>
{{ user.displayName }}
</span>
<span
v-if="user.isAdmin"
class="text-xs font-medium px-3 py-0.5 rounded-full bg-green-900 text-green-300"
>
{{ t('users.admin') }}
</span>
</div>
<div v-else class="flex items-center space-x-3">
<span> {{ t('users.unnamed') }} </span>
<span
v-if="user.isAdmin"
class="text-xs font-medium px-3 py-0.5 rounded-full bg-green-900 text-green-300"
>
{{ t('users.admin') }}
</span>
</div>
</td>
<td class="py-2 px-3">
<span>
{{ user.email }}
</span>
</td>
<td class="py-2 px-3">
<div class="flex items-center">
<div class="flex flex-col">
{{ getCreatedDate(user.createdOn) }}
<div class="text-gray-400 text-tiny">
{{ getCreatedTime(user.createdOn) }}
</div>
</div>
</div>
</td>
<td @click.stop>
<div class="relative">
<span>
<tippy interactive trigger="click" theme="popover">
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:icon="IconMoreHorizontal"
/>
<template #content="{ hide }">
<div
ref="tippyActions"
class="flex flex-col focus:outline-none"
tabindex="0"
@keyup.escape="hide()"
>
<HoppSmartItem
v-if="!user.isAdmin"
:icon="IconUserCheck"
:label="t('users.make_admin')"
class="!hover:bg-emerald-600"
@click="
() => {
makeUserAdmin(user.uid);
hide();
}
"
/>
<HoppSmartItem
v-else
:icon="IconUserMinus"
:label="t('users.remove_admin_status')"
class="!hover:bg-emerald-600"
@click="
() => {
makeAdminToUser(user.uid);
hide();
}
"
/>
<HoppSmartItem
v-if="!user.isAdmin"
:icon="IconTrash"
:label="t('users.delete_user')"
class="!hover:bg-red-600"
@click="
() => {
deleteUser(user.uid);
hide();
}
"
/>
</div>
</template>
</tippy>
</span>
</div>
</td>
</tr>
</template>
</HoppSmartTable>
</div>
<div v-else class="flex justify-center">{{ t('users.no_users') }}</div>
@@ -82,6 +205,7 @@
</template>
<script setup lang="ts">
import { format } from 'date-fns';
import { ref, watch } from 'vue';
import { useMutation } from '@urql/vue';
import {
@@ -96,8 +220,16 @@ import { useRoute, useRouter } from 'vue-router';
import { useToast } from '~/composables/toast';
import { HoppButtonSecondary } from '@hoppscotch/ui';
import IconAddUser from '~icons/lucide/user-plus';
import IconTrash from '~icons/lucide/trash';
import IconUserMinus from '~icons/lucide/user-minus';
import IconUserCheck from '~icons/lucide/user-check';
import IconMoreHorizontal from '~icons/lucide/more-horizontal';
import { useI18n } from '~/composables/i18n';
// Get Proper Date Formats
const getCreatedDate = (date: string) => format(new Date(date), 'dd-MM-yyyy');
const getCreatedTime = (date: string) => format(new Date(date), 'hh:mm a');
const t = useI18n();
const toast = useToast();
@@ -141,9 +273,7 @@ const sendInvite = async (email: string) => {
// Go to Individual User Details Page
const route = useRoute();
const router = useRouter();
const goToUserDetails = (uid: string) => {
router.push('/users/' + uid);
};
const goToUserDetails = (uid: string) => router.push('/users/' + uid);
watch(
() => route.params.id,

View File

@@ -16,64 +16,36 @@
<div v-if="fetching" class="flex justify-center">
<HoppSmartSpinner />
</div>
<div v-else-if="error || invitedUsers === undefined">
<div
v-else-if="
error || invitedUsers === undefined || invitedUsers.length === 0
"
>
<p class="text-xl">{{ t('users.no_invite') }}</p>
</div>
<table v-else class="w-full text-left">
<thead>
<tr
class="text-secondary border-b border-dividerDark text-sm text-left"
>
<th class="px-3 pb-3">{{ t('users.admin_id') }}</th>
<th class="px-3 pb-3">{{ t('users.admin_email') }}</th>
<th class="px-3 pb-3">{{ t('users.invitee_email') }}</th>
<th class="px-3 pb-3">{{ t('users.invited_on') }}</th>
</tr>
</thead>
<tbody class="divide-y divide-divider">
<tr
v-if="invitedUsers.length === 0"
class="text-secondaryDark py-4"
>
<div class="py-6 px-3">{{ t('users.no_invite') }}</div>
</tr>
<tr
v-else
v-for="(user, index) in invitedUsers"
:key="index"
class="text-secondaryDark hover:bg-zinc-800 hover:cursor-pointer rounded-xl"
>
<td class="py-2 px-3 max-w-30">
<div v-else>
<HoppSmartTable :list="newInvitedUsersList" :headings="headings">
<template #invitedOn="{ item }">
<div class="flex flex-col truncate">
<span v-if="item" class="truncate">
{{ getCreatedDate(item.invitedOn) }}
</span>
<span v-else> - </span>
<div>
<span class="truncate">
{{ user?.adminUid }}
</span>
</div>
</td>
<td class="py-2 px-3">
<span class="flex items-center">
{{ user?.adminEmail }}
</span>
</td>
<td class="py-2 px-3">
<span>
{{ user?.inviteeEmail }}
</span>
</td>
<td class="py-2 px-3">
<div class="flex items-center">
<div class="flex flex-col">
{{ getCreatedDate(user?.invitedOn) }}
<div class="text-gray-400 text-xs">
{{ getCreatedTime(user?.invitedOn) }}
</div>
<div class="text-gray-400 text-tiny">
<span>
<span>
{{ getCreatedTime(item.invitedOn) }}
</span>
</span>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</template>
</HoppSmartTable>
</div>
</div>
</div>
</div>
@@ -100,4 +72,24 @@ const getCreatedTime = (date: string) => format(new Date(date), 'hh:mm a');
// Get Invited Users
const { fetching, error, data } = useQuery({ query: InvitedUsersDocument });
const invitedUsers = computed(() => data?.value?.admin.invitedUsers);
// The new invited users list that is used in the table
const newInvitedUsersList = computed(() => {
return invitedUsers.value?.map((user) => {
return {
adminUid: user.adminUid,
adminEmail: user.adminEmail,
inviteeEmail: user.inviteeEmail,
invitedOn: user.invitedOn,
};
});
});
// Table Headings
const headings = [
{ key: 'adminUid', label: t('users.admin_id') },
{ key: 'adminEmail', label: t('users.admin_email') },
{ key: 'inviteeEmail', label: t('users.invitee_email') },
{ key: 'invitedOn', label: t('users.invited_on') },
];
</script>

View File

@@ -10,15 +10,10 @@ import Pages from 'vite-plugin-pages';
import Layouts from 'vite-plugin-vue-layouts';
import VueI18n from '@intlify/vite-plugin-vue-i18n';
import path from 'path';
import ImportMetaEnv from "@import-meta-env/unplugin"
// https://vitejs.dev/config/
export default defineConfig({
envPrefix:
process.env.HOPP_ALLOW_RUNTIME_ENV
? "VITE_BUILDTIME_"
: "VITE_",
envDir: path.resolve(__dirname, "../.."),
envDir: path.resolve(__dirname, '../../'),
server: {
port: 3100,
},
@@ -90,13 +85,7 @@ export default defineConfig({
variables: ["variable-full"],
},
],
}
},
}),
process.env.HOPP_ALLOW_RUNTIME_ENV
? ImportMetaEnv.vite({
example: "../../.env.example",
env: "../../.env",
})
: [],
],
});

View File

@@ -14,7 +14,8 @@
"do-build-ui": "pnpm run story:build"
},
"peerDependencies": {
"vue": "^3.2.25"
"vue": "^3.2.25",
"vue-router": "^4.0.16"
},
"dependencies": {
"@fontsource-variable/inter": "^5.0.5",
@@ -24,10 +25,25 @@
"@lezer/highlight": "^1.0.0",
"@vitejs/plugin-legacy": "^2.3.0",
"@vueuse/core": "^8.7.5",
"@vueuse/head": "^0.7.9",
"acorn-walk": "^8.2.0",
"esprima": "^4.0.1",
"events": "^3.3.0",
"fp-ts": "^2.12.1",
"globalthis": "^1.0.3",
"lodash-es": "^4.17.21",
"path": "^0.12.7",
"rxjs": "^7.5.5",
"splitpanes": "^3.1.1",
"tern": "^0.24.3",
"timers": "^0.1.1",
"tippy.js": "^6.3.7",
"url": "^0.11.0",
"util": "^0.12.4",
"vite-plugin-eslint": "^1.8.1",
"vue-github-button": "^3.0.3",
"vue-router": "^4.0.16",
"vue-tippy": "6.0.0-alpha.58",
"vuedraggable-es": "^4.1.1"
},
"devDependencies": {
@@ -45,10 +61,12 @@
"@vue/compiler-sfc": "^3.2.39",
"@vue/eslint-config-typescript": "^11.0.1",
"@vue/runtime-core": "^3.2.39",
"cross-env": "^7.0.3",
"eslint": "^8.24.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.5.1",
"histoire": "^0.12.4",
"npm-run-all": "^4.1.5",
"rollup-plugin-polyfill-node": "^0.10.1",
"sass": "^1.53.0",
"typescript": "^4.5.4",
@@ -67,6 +85,8 @@
"vite-plugin-vue-layouts": "^0.7.0",
"vite-plugin-windicss": "^1.8.8",
"vue": "^3.2.25",
"vue-loader": "^16.8.3",
"vue-router": "^4.0.16",
"vue-tsc": "^0.38.2",
"windicss": "^3.5.6"
},

View File

@@ -166,6 +166,12 @@ a {
@apply truncate;
@apply sm:inline-flex;
}
.env-icon {
@apply transition;
@apply inline-flex;
@apply items-center;
}
}
.tippy-svg-arrow {
@@ -326,7 +332,7 @@ pre.ace_editor {
@apply after:font-icon;
@apply after:text-current;
@apply after:right-3;
@apply after:content-["\e5cf"];
@apply after:content-["\e313"];
@apply after:text-lg;
}
@@ -481,10 +487,6 @@ pre.ace_editor {
}
}
.cm-scroller {
@apply overscroll-y-auto;
}
.cm-editor {
.cm-line::selection {
@apply bg-accentDark #{!important};
@@ -572,11 +574,3 @@ details[open] summary .indicator {
@apply rounded;
@apply border-0;
}
.gql-operation-not-highlight {
@apply opacity-50;
}
.gql-operation-highlight {
@apply opacity-100;
}

View File

@@ -57,7 +57,7 @@ const emit = defineEmits<{
@apply font-icon;
@apply mr-2;
@apply transition;
@apply content-["\e5ca"];
@apply content-["\e876"];
}
}

View File

@@ -0,0 +1,68 @@
<template>
<div class="overflow-auto rounded-md border border-dividerDark shadow-md">
<table class="w-full">
<thead>
<slot name="head">
<tr
class="text-secondary border-b border-dividerDark text-sm text-left bg-primaryLight"
>
<th v-for="th in headings" scope="col" class="px-6 py-3">
{{ th.label ?? th.key }}
</th>
</tr>
</slot>
</thead>
<tbody class="divide-y divide-divider">
<slot name="body" :list="list">
<tr
v-for="(rowData, rowIndex) in list"
:key="rowIndex"
class="text-secondaryDark hover:bg-divider hover:cursor-pointer rounded-xl"
:class="yBorder ? 'divide-x divide-divider' : ''"
>
<td
v-for="cellHeading in headings"
:key="cellHeading.key"
@click="!cellHeading.preventClick && onRowClicked(rowData)"
class="max-w-40 pl-6 py-1"
>
<!-- Dynamic column slot -->
<slot :name="cellHeading.key" :item="rowData">
<!-- Generic implementation of the column -->
<div class="flex flex-col truncate">
<span class="truncate">
{{ rowData[cellHeading.key] ?? "-" }}
</span>
</div>
</slot>
</td>
</tr>
</slot>
</tbody>
</table>
</div>
</template>
<script lang="ts" setup generic="Item extends Record<string, unknown>">
export type CellHeading = {
key: string
label?: string
preventClick?: boolean
}
defineProps<{
/** Whether to show the vertical border between columns */
yBorder?: boolean
/** The list of items to be displayed in the table */
list?: Item[]
/** The headings of the table */
headings?: CellHeading[]
}>()
const emit = defineEmits<{
(event: "onRowClicked", item: Item): void
}>()
const onRowClicked = (item: Item) => emit("onRowClicked", item)
</script>

View File

@@ -16,6 +16,7 @@ export { default as HoppSmartSlideOver } from "./SlideOver.vue"
export { default as HoppSmartSpinner } from "./Spinner.vue"
export { default as HoppSmartTab } from "./Tab.vue"
export { default as HoppSmartTabs } from "./Tabs.vue"
export { default as HoppSmartTable } from "./Table.vue"
export { default as HoppSmartToggle } from "./Toggle.vue"
export { default as HoppSmartWindow } from "./Window.vue"
export { default as HoppSmartWindows } from "./Windows.vue"

View File

@@ -0,0 +1,58 @@
<template>
<Story title="Table">
<Variant title="General">
<HoppSmartTable :list="list" :headings="headings" />
</Variant>
<Variant title="Custom">
<HoppSmartTable>
<template #head>
<th>ID</th>
<th>Name</th>
<th>Members</th>
<th>Role</th>
</template>
<template #body>
<tr>
<td>123454</td>
<td>Joel</td>
<td>10</td>
<td>Frontend Engineer</td>
</tr>
<tr>
<td>123456</td>
<td>Anwar</td>
<td>12</td>
<td>Frontend Engineer</td>
</tr>
</template>
</HoppSmartTable>
</Variant>
</Story>
</template>
<script setup lang="ts">
import { HoppSmartTable } from "../components/smart"
// Table Headings
const headings = [
{ key: "id", label: "ID" },
{ key: "name", label: "Name" },
{ key: "members", label: "Members" },
{ key: "role", label: "Role" },
]
const list = [
{
id: "123455",
name: "Joel",
members: 10,
role: "Frontend Engineer",
},
{
id: "123456",
name: "Anwar",
members: 12,
role: "Frontend Engineer",
},
]
</script>

View File

@@ -23,7 +23,14 @@
"@workers/*": ["./src/workers/*"],
"@functional/*": ["./src/helpers/functional/*"]
},
"types": ["vite/client", "unplugin-icons/types/vue"]
"types": [
"vite/client",
"unplugin-icons/types/vue",
"unplugin-fonts/client",
"vite-plugin-pages/client",
"vite-plugin-vue-layouts/client",
"vite-plugin-pwa/client"
]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}

View File

@@ -11,8 +11,7 @@ export default defineConfig({
vue(),
dts({
insertTypesEntry: true,
skipDiagnostics: true,
outputDir: ["dist"],
outDir: ["dist"],
}),
WindiCSS({
root: path.resolve(__dirname),
@@ -49,7 +48,7 @@ export default defineConfig({
fileName: (format, entry) => `${entry}.${format}.js`,
},
rollupOptions: {
external: ["vue"],
external: ["vue", "vue-router"],
output: {
exports: "named",
},

384
pnpm-lock.yaml generated
View File

@@ -107,8 +107,8 @@ importers:
specifier: ^4.0.0
version: 4.0.0(@nestjs/common@9.2.1)(@nestjs/core@9.2.1)(reflect-metadata@0.1.13)
'@prisma/client':
specifier: ^4.16.2
version: 4.16.2(prisma@4.16.2)
specifier: ^4.7.1
version: 4.8.1(prisma@4.8.1)
apollo-server-express:
specifier: ^3.11.1
version: 3.11.1(express@4.18.2)(graphql@15.8.0)
@@ -179,8 +179,8 @@ importers:
specifier: ^1.0.0
version: 1.0.0
prisma:
specifier: ^4.16.2
version: 4.16.2
specifier: ^4.7.1
version: 4.8.1
reflect-metadata:
specifier: ^0.1.13
version: 0.1.13
@@ -881,9 +881,6 @@ importers:
'@hoppscotch/data':
specifier: workspace:^
version: link:../hoppscotch-data
'@import-meta-env/unplugin':
specifier: ^0.4.8
version: 0.4.8(@import-meta-env/cli@0.6.3)(dotenv@16.3.1)
axios:
specifier: ^1.4.0
version: 1.4.0
@@ -987,9 +984,6 @@ importers:
vite:
specifier: ^4.4.9
version: 4.4.9(@types/node@17.0.45)(sass@1.53.0)(terser@5.19.2)
vite-plugin-fonts:
specifier: ^0.6.0
version: 0.6.0(vite@4.4.9)
vite-plugin-html-config:
specifier: ^1.0.11
version: 1.0.11(vite@4.4.9)
@@ -1097,7 +1091,7 @@ importers:
version: 0.14.9(@vue/compiler-sfc@3.2.45)(vite@3.2.4)
unplugin-vue-components:
specifier: ^0.21.0
version: 0.21.0(esbuild@0.19.2)(rollup@2.79.1)(vite@3.2.4)(vue@3.2.45)
version: 0.21.0(esbuild@0.19.2)(rollup@2.79.1)(vite@3.2.4)(vue@3.2.45)(webpack@5.88.2)
vue:
specifier: ^3.2.6
version: 3.2.45
@@ -1135,12 +1129,6 @@ importers:
'@graphql-codegen/urql-introspection':
specifier: 2.2.1
version: 2.2.1(graphql@16.6.0)
'@import-meta-env/cli':
specifier: ^0.6.3
version: 0.6.3(@import-meta-env/unplugin@0.4.8)(dotenv@16.3.1)
'@import-meta-env/unplugin':
specifier: ^0.4.8
version: 0.4.8(@import-meta-env/cli@0.6.3)(dotenv@16.3.1)
'@intlify/vite-plugin-vue-i18n':
specifier: ^7.0.0
version: 7.0.0(vite@3.2.4)(vue-i18n@9.2.2)
@@ -1150,9 +1138,6 @@ importers:
'@vue/compiler-sfc':
specifier: ^3.2.6
version: 3.2.45
dotenv:
specifier: ^16.0.3
version: 16.3.1
graphql-tag:
specifier: ^2.12.6
version: 2.12.6(graphql@16.6.0)
@@ -1213,18 +1198,63 @@ importers:
'@vueuse/core':
specifier: ^8.7.5
version: 8.7.5(vue@3.2.45)
'@vueuse/head':
specifier: ^0.7.9
version: 0.7.9(vue@3.2.45)
acorn-walk:
specifier: ^8.2.0
version: 8.2.0
esprima:
specifier: ^4.0.1
version: 4.0.1
events:
specifier: ^3.3.0
version: 3.3.0
fp-ts:
specifier: ^2.12.1
version: 2.13.1
globalthis:
specifier: ^1.0.3
version: 1.0.3
lodash-es:
specifier: ^4.17.21
version: 4.17.21
path:
specifier: ^0.12.7
version: 0.12.7
rxjs:
specifier: ^7.5.5
version: 7.6.0
splitpanes:
specifier: ^3.1.1
version: 3.1.1
tern:
specifier: ^0.24.3
version: 0.24.3
timers:
specifier: ^0.1.1
version: 0.1.1
tippy.js:
specifier: ^6.3.7
version: 6.3.7
url:
specifier: ^0.11.0
version: 0.11.0
util:
specifier: ^0.12.4
version: 0.12.4
vite-plugin-eslint:
specifier: ^1.8.1
version: 1.8.1(eslint@8.29.0)(vite@3.2.4)
vue-github-button:
specifier: ^3.0.3
version: 3.0.3
vue-router:
specifier: ^4.0.16
version: 4.1.0(vue@3.2.45)
vue-tippy:
specifier: 6.0.0-alpha.58
version: 6.0.0-alpha.58(vue@3.2.45)
vuedraggable-es:
specifier: ^4.1.1
version: 4.1.1(vue@3.2.45)
@@ -1271,6 +1301,9 @@ importers:
'@vue/runtime-core':
specifier: ^3.2.39
version: 3.2.45
cross-env:
specifier: ^7.0.3
version: 7.0.3
eslint:
specifier: ^8.24.0
version: 8.29.0
@@ -1283,6 +1316,9 @@ importers:
histoire:
specifier: ^0.12.4
version: 0.12.4(@types/node@17.0.45)(sass@1.53.0)(terser@5.19.2)(vite@3.2.4)
npm-run-all:
specifier: ^4.1.5
version: 4.1.5
rollup-plugin-polyfill-node:
specifier: ^0.10.1
version: 0.10.1(rollup@2.79.1)
@@ -1300,7 +1336,7 @@ importers:
version: 0.15.3(@vue/compiler-sfc@3.2.45)
unplugin-vue-components:
specifier: ^0.21.0
version: 0.21.0(esbuild@0.19.2)(rollup@2.79.1)(vite@3.2.4)(vue@3.2.45)
version: 0.21.0(esbuild@0.19.2)(rollup@2.79.1)(vite@3.2.4)(vue@3.2.45)(webpack@5.88.2)
vite:
specifier: ^3.2.3
version: 3.2.4(@types/node@17.0.45)(sass@1.53.0)(terser@5.19.2)
@@ -1337,6 +1373,9 @@ importers:
vue:
specifier: ^3.2.25
version: 3.2.45
vue-loader:
specifier: ^16.8.3
version: 16.8.3(@vue/compiler-sfc@3.2.45)(vue@3.2.45)(webpack@5.88.2)
vue-tsc:
specifier: ^0.38.2
version: 0.38.2(typescript@4.9.3)
@@ -5978,47 +6017,6 @@ packages:
- supports-color
dev: true
/@import-meta-env/cli@0.6.3(@import-meta-env/unplugin@0.4.8)(dotenv@16.3.1):
resolution: {integrity: sha512-R0tAmEpNXjqvFgWpnPkODAKaBUKQzCUVzb7DDsOxe1OQxz32ja4W1jdtZikapkh4ZjGnJ7S+kCjKTOfh8qSUOQ==}
engines: {node: '>= 14'}
hasBin: true
peerDependencies:
'@import-meta-env/babel': ^0.4.3
'@import-meta-env/swc': ^0.4.5
'@import-meta-env/unplugin': ^0.4.8
dotenv: ^11.0.0 || ^12.0.4 || ^13.0.1 || ^14.3.2 || ^15.0.1 || ^16.0.0
peerDependenciesMeta:
'@import-meta-env/babel':
optional: true
'@import-meta-env/swc':
optional: true
'@import-meta-env/unplugin':
optional: true
dependencies:
'@import-meta-env/unplugin': 0.4.8(@import-meta-env/cli@0.6.3)(dotenv@16.3.1)
commander: 10.0.1
dotenv: 16.3.1
glob: 10.2.7
picocolors: 1.0.0
serialize-javascript: 6.0.1
/@import-meta-env/unplugin@0.4.8(@import-meta-env/cli@0.6.3)(dotenv@16.3.1):
resolution: {integrity: sha512-NMyC8BzR8+cl7Ht6vZPMqYeR60zxEwB5Yo/OsM5DSaLpD/L04jqQQIxeQDmg5/jRqi+OPc7KPTBhYccjSByNpA==}
engines: {node: '>= 14'}
peerDependencies:
'@import-meta-env/cli': ^0.5.1 || ^0.6.0
dotenv: ^11.0.0 || ^12.0.4 || ^13.0.1 || ^14.3.2 || ^15.0.1 || ^16.0.0
peerDependenciesMeta:
'@import-meta-env/cli':
optional: true
dependencies:
'@import-meta-env/cli': 0.6.3(@import-meta-env/unplugin@0.4.8)(dotenv@16.3.1)
dotenv: 16.3.1
magic-string: 0.30.2
object-hash: 3.0.0
picocolors: 1.0.0
unplugin: 1.2.0
/@intlify/bundle-utils@3.4.0(vue-i18n@9.2.2):
resolution: {integrity: sha512-2UQkqiSAOSPEHMGWlybqWm4G2K0X+FyYho5AwXz6QklSX1EY5EDmOSxZmwscn2qmKBnp6OYsme5kUrnN9xrWzQ==}
engines: {node: '>= 12'}
@@ -6210,17 +6208,6 @@ packages:
dev: false
optional: true
/@isaacs/cliui@8.0.2:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
dependencies:
string-width: 5.1.2
string-width-cjs: /string-width@4.2.3
strip-ansi: 7.1.0
strip-ansi-cjs: /strip-ansi@6.0.1
wrap-ansi: 8.1.0
wrap-ansi-cjs: /wrap-ansi@7.0.0
/@istanbuljs/load-nyc-config@1.1.0:
resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
engines: {node: '>=8'}
@@ -7281,12 +7268,6 @@ packages:
resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==}
engines: {node: '>=10'}
/@pkgjs/parseargs@0.11.0:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
requiresBuild: true
optional: true
/@pkgr/utils@2.4.2:
resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
@@ -7307,8 +7288,8 @@ packages:
resolution: {integrity: sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==}
dev: false
/@prisma/client@4.16.2(prisma@4.16.2):
resolution: {integrity: sha512-qCoEyxv1ZrQ4bKy39GnylE8Zq31IRmm8bNhNbZx7bF2cU5aiCCnSa93J2imF88MBjn7J9eUQneNxUQVJdl/rPQ==}
/@prisma/client@4.8.1(prisma@4.8.1):
resolution: {integrity: sha512-d4xhZhETmeXK/yZ7K0KcVOzEfI5YKGGEr4F5SBV04/MU4ncN/HcE28sy3e4Yt8UFW0ZuImKFQJE+9rWt9WbGSQ==}
engines: {node: '>=14.17'}
requiresBuild: true
peerDependencies:
@@ -7317,16 +7298,16 @@ packages:
prisma:
optional: true
dependencies:
'@prisma/engines-version': 4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81
prisma: 4.16.2
'@prisma/engines-version': 4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe
prisma: 4.8.1
dev: false
/@prisma/engines-version@4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81:
resolution: {integrity: sha512-q617EUWfRIDTriWADZ4YiWRZXCa/WuhNgLTVd+HqWLffjMSPzyM5uOWoauX91wvQClSKZU4pzI4JJLQ9Kl62Qg==}
/@prisma/engines-version@4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe:
resolution: {integrity: sha512-MHSOSexomRMom8QN4t7bu87wPPD+pa+hW9+71JnVcF3DqyyO/ycCLhRL1we3EojRpZxKvuyGho2REQsMCvxcJw==}
dev: false
/@prisma/engines@4.16.2:
resolution: {integrity: sha512-vx1nxVvN4QeT/cepQce68deh/Turxy5Mr+4L4zClFuK1GlxN3+ivxfuv+ej/gvidWn1cE1uAhW7ALLNlYbRUAw==}
/@prisma/engines@4.8.1:
resolution: {integrity: sha512-93tctjNXcIS+i/e552IO6tqw17sX8liivv8WX9lDMCpEEe3ci+nT9F+1oHtAafqruXLepKF80i/D20Mm+ESlOw==}
requiresBuild: true
dev: false
@@ -8000,7 +7981,6 @@ packages:
dependencies:
'@types/eslint': 8.4.10
'@types/estree': 0.0.51
dev: true
/@types/eslint@8.4.10:
resolution: {integrity: sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==}
@@ -8017,7 +7997,6 @@ packages:
/@types/estree@1.0.1:
resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==}
dev: true
/@types/express-serve-static-core@4.17.31:
resolution: {integrity: sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==}
@@ -9679,6 +9658,14 @@ packages:
- vue
dev: false
/@vueuse/head@0.7.9(vue@3.2.45):
resolution: {integrity: sha512-5wnRiH2XIUSLLXJDLDDTcpvAg5QXgTIVZl46AU7to/T91KHsdBLHSE4WhRO7kP0jbkAhlxnx64E29cQtwBrMjg==}
peerDependencies:
vue: '>=3'
dependencies:
vue: 3.2.45
dev: false
/@vueuse/head@1.3.1(vue@3.3.4):
resolution: {integrity: sha512-XCcHGfDzkGlHS7KIPJVYN//L7jpfASLsN7MUE19ndHVQLnPIDxqFLDl7IROsY81PKzawVAUe4OYVWcGixseWxA==}
peerDependencies:
@@ -9748,7 +9735,6 @@ packages:
dependencies:
'@webassemblyjs/helper-numbers': 1.11.6
'@webassemblyjs/helper-wasm-bytecode': 1.11.6
dev: true
/@webassemblyjs/floating-point-hex-parser@1.11.1:
resolution: {integrity: sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==}
@@ -9756,7 +9742,6 @@ packages:
/@webassemblyjs/floating-point-hex-parser@1.11.6:
resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==}
dev: true
/@webassemblyjs/helper-api-error@1.11.1:
resolution: {integrity: sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==}
@@ -9764,7 +9749,6 @@ packages:
/@webassemblyjs/helper-api-error@1.11.6:
resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==}
dev: true
/@webassemblyjs/helper-buffer@1.11.1:
resolution: {integrity: sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==}
@@ -9772,7 +9756,6 @@ packages:
/@webassemblyjs/helper-buffer@1.11.6:
resolution: {integrity: sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==}
dev: true
/@webassemblyjs/helper-numbers@1.11.1:
resolution: {integrity: sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==}
@@ -9788,7 +9771,6 @@ packages:
'@webassemblyjs/floating-point-hex-parser': 1.11.6
'@webassemblyjs/helper-api-error': 1.11.6
'@xtuc/long': 4.2.2
dev: true
/@webassemblyjs/helper-wasm-bytecode@1.11.1:
resolution: {integrity: sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==}
@@ -9796,7 +9778,6 @@ packages:
/@webassemblyjs/helper-wasm-bytecode@1.11.6:
resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==}
dev: true
/@webassemblyjs/helper-wasm-section@1.11.1:
resolution: {integrity: sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==}
@@ -9814,7 +9795,6 @@ packages:
'@webassemblyjs/helper-buffer': 1.11.6
'@webassemblyjs/helper-wasm-bytecode': 1.11.6
'@webassemblyjs/wasm-gen': 1.11.6
dev: true
/@webassemblyjs/ieee754@1.11.1:
resolution: {integrity: sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==}
@@ -9826,7 +9806,6 @@ packages:
resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==}
dependencies:
'@xtuc/ieee754': 1.2.0
dev: true
/@webassemblyjs/leb128@1.11.1:
resolution: {integrity: sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==}
@@ -9838,7 +9817,6 @@ packages:
resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==}
dependencies:
'@xtuc/long': 4.2.2
dev: true
/@webassemblyjs/utf8@1.11.1:
resolution: {integrity: sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==}
@@ -9846,7 +9824,6 @@ packages:
/@webassemblyjs/utf8@1.11.6:
resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==}
dev: true
/@webassemblyjs/wasm-edit@1.11.1:
resolution: {integrity: sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==}
@@ -9872,7 +9849,6 @@ packages:
'@webassemblyjs/wasm-opt': 1.11.6
'@webassemblyjs/wasm-parser': 1.11.6
'@webassemblyjs/wast-printer': 1.11.6
dev: true
/@webassemblyjs/wasm-gen@1.11.1:
resolution: {integrity: sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==}
@@ -9892,7 +9868,6 @@ packages:
'@webassemblyjs/ieee754': 1.11.6
'@webassemblyjs/leb128': 1.11.6
'@webassemblyjs/utf8': 1.11.6
dev: true
/@webassemblyjs/wasm-opt@1.11.1:
resolution: {integrity: sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==}
@@ -9910,7 +9885,6 @@ packages:
'@webassemblyjs/helper-buffer': 1.11.6
'@webassemblyjs/wasm-gen': 1.11.6
'@webassemblyjs/wasm-parser': 1.11.6
dev: true
/@webassemblyjs/wasm-parser@1.11.1:
resolution: {integrity: sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==}
@@ -9932,7 +9906,6 @@ packages:
'@webassemblyjs/ieee754': 1.11.6
'@webassemblyjs/leb128': 1.11.6
'@webassemblyjs/utf8': 1.11.6
dev: true
/@webassemblyjs/wast-printer@1.11.1:
resolution: {integrity: sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==}
@@ -9946,7 +9919,6 @@ packages:
dependencies:
'@webassemblyjs/ast': 1.11.6
'@xtuc/long': 4.2.2
dev: true
/@wessberg/stringutil@1.0.19:
resolution: {integrity: sha512-9AZHVXWlpN8Cn9k5BC/O0Dzb9E9xfEMXzYrNunwvkUTvuK7xgQPVRZpLo+jWCOZ5r8oBa8NIrHuPEu1hzbb6bg==}
@@ -10103,11 +10075,9 @@ packages:
/@xtuc/ieee754@1.2.0:
resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
dev: true
/@xtuc/long@4.2.2:
resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
dev: true
/JSONStream@1.3.5:
resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==}
@@ -10166,7 +10136,6 @@ packages:
acorn: ^8
dependencies:
acorn: 8.10.0
dev: true
/acorn-jsx@5.3.2(acorn@7.4.1):
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
@@ -10287,7 +10256,6 @@ packages:
ajv: ^6.9.1
dependencies:
ajv: 6.12.6
dev: true
/ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
@@ -10916,6 +10884,10 @@ packages:
engines: {node: '>=0.6'}
dev: true
/big.js@5.2.2:
resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
dev: true
/binary-extensions@2.2.0:
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
engines: {node: '>=8'}
@@ -11368,7 +11340,6 @@ packages:
/chrome-trace-event@1.0.3:
resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==}
engines: {node: '>=6.0'}
dev: true
/ci-info@3.3.2:
resolution: {integrity: sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==}
@@ -11508,10 +11479,6 @@ packages:
dependencies:
delayed-stream: 1.0.0
/commander@10.0.1:
resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
engines: {node: '>=14'}
/commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
@@ -12372,6 +12339,7 @@ packages:
/dotenv@16.3.1:
resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
engines: {node: '>=12'}
dev: true
/dset@3.1.2:
resolution: {integrity: sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==}
@@ -12445,6 +12413,11 @@ packages:
/emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
/emojis-list@3.0.0:
resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==}
engines: {node: '>= 4'}
dev: true
/encodeurl@1.0.2:
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
engines: {node: '>= 0.8'}
@@ -12563,7 +12536,6 @@ packages:
dependencies:
graceful-fs: 4.2.11
tapable: 2.2.1
dev: true
/entities@2.1.0:
resolution: {integrity: sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==}
@@ -12682,7 +12654,6 @@ packages:
/es-module-lexer@1.3.0:
resolution: {integrity: sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==}
dev: true
/es-set-tostringtag@2.0.1:
resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==}
@@ -13330,7 +13301,6 @@ packages:
dependencies:
esrecurse: 4.3.0
estraverse: 4.3.0
dev: true
/eslint-scope@7.1.1:
resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==}
@@ -14077,13 +14047,6 @@ packages:
dependencies:
is-callable: 1.2.7
/foreground-child@3.1.1:
resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
engines: {node: '>=14'}
dependencies:
cross-spawn: 7.0.3
signal-exit: 4.1.0
/fork-ts-checker-webpack-plugin@7.2.13(typescript@4.8.4)(webpack@5.74.0):
resolution: {integrity: sha512-fR3WRkOb4bQdWB/y7ssDUlVdrclvwtyCUIHCfivAoYxq9dF7XfrDKbMdZIfwJ7hxIAqkYSGeU7lLJE6xrxIBdg==}
engines: {node: '>=12.13.0', yarn: '>=1.0.0'}
@@ -14391,6 +14354,10 @@ packages:
through2: 4.0.2
dev: true
/github-buttons@2.21.1:
resolution: {integrity: sha512-n9bCQ8sj+5oX1YH5NeyWGbAclRDtHEhMBzqw2ctsWpdEHOwVgfruRu0VIVy01Ah10dd/iFajMHYU71L7IBWBOw==}
dev: false
/glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
@@ -14405,18 +14372,6 @@ packages:
/glob-to-regexp@0.4.1:
resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
dev: true
/glob@10.2.7:
resolution: {integrity: sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==}
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
dependencies:
foreground-child: 3.1.1
jackspeak: 2.3.0
minimatch: 9.0.3
minipass: 6.0.2
path-scurry: 1.10.1
/glob@7.1.6:
resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==}
@@ -14922,6 +14877,10 @@ packages:
dependencies:
function-bind: 1.1.1
/hash-sum@2.0.0:
resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==}
dev: true
/he@1.2.0:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
hasBin: true
@@ -15912,14 +15871,6 @@ packages:
resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==}
engines: {node: '>=6'}
/jackspeak@2.3.0:
resolution: {integrity: sha512-uKmsITSsF4rUWQHzqaRUuyAir3fZfW3f202Ee34lz/gZCi970CPZwyQXLGNgWJvvZbvFyzeyGq0+4fcG/mBKZg==}
engines: {node: '>=14'}
dependencies:
'@isaacs/cliui': 8.0.2
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
/jake@10.8.5:
resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==}
engines: {node: '>=10'}
@@ -16936,7 +16887,6 @@ packages:
'@types/node': 18.16.17
merge-stream: 2.0.0
supports-color: 8.1.1
dev: true
/jest-worker@29.4.1:
resolution: {integrity: sha512-O9doU/S1EBe+yp/mstQ0VpPwpv0Clgn68TkNwGxL6/usX/KUW9Arnn4ag8C3jc6qHcXznhsT5Na1liYzAsuAbQ==}
@@ -17474,6 +17424,14 @@ packages:
/loader-runner@4.3.0:
resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==}
engines: {node: '>=6.11.5'}
/loader-utils@2.0.4:
resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==}
engines: {node: '>=8.9.0'}
dependencies:
big.js: 5.2.2
emojis-list: 3.0.0
json5: 2.2.3
dev: true
/local-pkg@0.4.2:
@@ -17602,10 +17560,6 @@ packages:
dependencies:
tslib: 2.6.2
/lru-cache@10.0.1:
resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==}
engines: {node: 14 || >=16.14}
/lru-cache@4.1.5:
resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
dependencies:
@@ -17948,6 +17902,7 @@ packages:
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
brace-expansion: 2.0.1
dev: true
/minimist-options@4.1.0:
resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==}
@@ -17973,10 +17928,6 @@ packages:
dependencies:
yallist: 4.0.0
/minipass@6.0.2:
resolution: {integrity: sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==}
engines: {node: '>=16 || 14 >=14.17'}
/minisearch@6.1.0:
resolution: {integrity: sha512-PNxA/X8pWk+TiqPbsoIYH0GQ5Di7m6326/lwU/S4mlo4wGQddIcf/V//1f9TB0V4j59b57b+HZxt8h3iMROGvg==}
dev: false
@@ -19142,13 +19093,6 @@ packages:
path-root-regex: 0.1.2
dev: true
/path-scurry@1.10.1:
resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
lru-cache: 10.0.1
minipass: 6.0.2
/path-to-regexp@0.1.7:
resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
@@ -19452,13 +19396,13 @@ packages:
- supports-color
dev: false
/prisma@4.16.2:
resolution: {integrity: sha512-SYCsBvDf0/7XSJyf2cHTLjLeTLVXYfqp7pG5eEVafFLeT0u/hLFz/9W196nDRGUOo1JfPatAEb+uEnTQImQC1g==}
/prisma@4.8.1:
resolution: {integrity: sha512-ZMLnSjwulIeYfaU1O6/LF6PEJzxN5par5weykxMykS9Z6ara/j76JH3Yo2AH3bgJbPN4Z6NeCK9s5fDkzf33cg==}
engines: {node: '>=14.17'}
hasBin: true
requiresBuild: true
dependencies:
'@prisma/engines': 4.16.2
'@prisma/engines': 4.8.1
dev: false
/process-nextick-args@2.0.1:
@@ -19620,6 +19564,10 @@ packages:
once: 1.4.0
dev: true
/punycode@1.3.2:
resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==}
dev: false
/punycode@1.4.1:
resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
@@ -19660,6 +19608,12 @@ packages:
dependencies:
side-channel: 1.0.4
/querystring@0.2.0:
resolution: {integrity: sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=}
engines: {node: '>=0.4.x'}
deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
dev: false
/querystringify@2.2.0:
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
dev: true
@@ -19698,7 +19652,6 @@ packages:
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
dependencies:
safe-buffer: 5.2.1
dev: true
/range-parser@1.2.1:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
@@ -20299,7 +20252,6 @@ packages:
'@types/json-schema': 7.0.12
ajv: 6.12.6
ajv-keywords: 3.5.2(ajv@6.12.6)
dev: true
/scuid@1.1.0:
resolution: {integrity: sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg==}
@@ -20397,7 +20349,6 @@ packages:
resolution: {integrity: sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==}
dependencies:
randombytes: 2.1.0
dev: true
/serve-static@1.15.0:
resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==}
@@ -20488,10 +20439,6 @@ packages:
/signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
/signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
/signedsource@1.0.0:
resolution: {integrity: sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww==}
dev: true
@@ -20751,6 +20698,10 @@ packages:
through: 2.3.8
dev: false
/splitpanes@3.1.1:
resolution: {integrity: sha512-VUkxDJfIGSvTM/fm/+OSrx8ha9URwE/9B8FPvfzoBuAxVELIHBWpsfnJXIXv77zVwuex//QQL4kTU9SDBPeHjA==}
dev: false
/splitpanes@3.1.5:
resolution: {integrity: sha512-r3Mq2ITFQ5a2VXLOy4/Sb2Ptp7OfEO8YIbhVJqJXoFc9hc5nTXXkCvtVDjIGbvC0vdE7tse+xTM9BMjsszP6bw==}
dev: false
@@ -21135,7 +21086,6 @@ packages:
engines: {node: '>=10'}
dependencies:
has-flag: 4.0.0
dev: true
/supports-color@9.2.2:
resolution: {integrity: sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==}
@@ -21219,7 +21169,6 @@ packages:
/tapable@2.2.1:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
engines: {node: '>=6'}
dev: true
/tar@6.1.13:
resolution: {integrity: sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==}
@@ -21292,7 +21241,7 @@ packages:
webpack: 5.74.0
dev: true
/terser-webpack-plugin@5.3.9(webpack@5.88.2):
/terser-webpack-plugin@5.3.9(esbuild@0.19.2)(webpack@5.88.2):
resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==}
engines: {node: '>= 10.13.0'}
peerDependencies:
@@ -21309,12 +21258,12 @@ packages:
optional: true
dependencies:
'@jridgewell/trace-mapping': 0.3.19
esbuild: 0.19.2
jest-worker: 27.5.1
schema-utils: 3.3.0
serialize-javascript: 6.0.1
terser: 5.19.2
webpack: 5.88.2
dev: true
webpack: 5.88.2(esbuild@0.19.2)
/terser@5.14.1:
resolution: {integrity: sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ==}
@@ -21642,7 +21591,7 @@ packages:
micromatch: 4.0.5
semver: 7.3.8
typescript: 4.9.3
webpack: 5.88.2
webpack: 5.88.2(esbuild@0.19.2)
dev: true
/ts-log@2.2.4:
@@ -22295,7 +22244,7 @@ packages:
- supports-color
dev: true
/unplugin-vue-components@0.21.0(esbuild@0.19.2)(rollup@2.79.1)(vite@3.2.4)(vue@3.2.45):
/unplugin-vue-components@0.21.0(esbuild@0.19.2)(rollup@2.79.1)(vite@3.2.4)(vue@3.2.45)(webpack@5.88.2):
resolution: {integrity: sha512-U7uOMNmRJ2eAv9CNjP8QRvxs6nAe3FVQUEIUphC1FGguBp3BWSLgGAcSHaX2nQy0gFoDY2mLF2M52W/t/eDaKg==}
engines: {node: '>=14'}
peerDependencies:
@@ -22314,7 +22263,7 @@ packages:
magic-string: 0.26.7
minimatch: 5.1.6
resolve: 1.22.4
unplugin: 0.7.1(esbuild@0.19.2)(rollup@2.79.1)(vite@3.2.4)
unplugin: 0.7.1(esbuild@0.19.2)(rollup@2.79.1)(vite@3.2.4)(webpack@5.88.2)
vue: 3.2.45
transitivePeerDependencies:
- esbuild
@@ -22352,7 +22301,7 @@ packages:
- supports-color
dev: true
/unplugin@0.7.1(esbuild@0.19.2)(rollup@2.79.1)(vite@3.2.4):
/unplugin@0.7.1(esbuild@0.19.2)(rollup@2.79.1)(vite@3.2.4)(webpack@5.88.2):
resolution: {integrity: sha512-Z6hNDXDNh9aimMkPU1mEjtk+2ova8gh0y7rJeJdGH1vWZOHwF2lLQiQ/R97rv9ymmzEQXsR2fyMet72T8jy6ew==}
peerDependencies:
esbuild: '>=0.13'
@@ -22374,6 +22323,7 @@ packages:
esbuild: 0.19.2
rollup: 2.79.1
vite: 3.2.4(@types/node@17.0.45)(sass@1.53.0)(terser@5.19.2)
webpack: 5.88.2(esbuild@0.19.2)
webpack-sources: 3.2.3
webpack-virtual-modules: 0.4.4
@@ -22410,14 +22360,6 @@ packages:
webpack-virtual-modules: 0.5.0
dev: true
/unplugin@1.2.0:
resolution: {integrity: sha512-7lJXQY4CxOK4jZyVskZuuNBqBSOlxezKqBpfQEpH+Odk2Ban3moKAlvzs9rZuZoZp6/1FEhvY9TZXav2FRhaBg==}
dependencies:
acorn: 8.10.0
chokidar: 3.5.3
webpack-sources: 3.2.3
webpack-virtual-modules: 0.5.0
/unplugin@1.4.0:
resolution: {integrity: sha512-5x4eIEL6WgbzqGtF9UV8VEC/ehKptPXDS6L2b0mv4FRMkJxRtjaJfOWDd6a8+kYbqsjklix7yWP0N3SUepjXcg==}
dependencies:
@@ -22493,6 +22435,13 @@ packages:
requires-port: 1.0.0
dev: true
/url@0.11.0:
resolution: {integrity: sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==}
dependencies:
punycode: 1.3.2
querystring: 0.2.0
dev: false
/url@0.11.1:
resolution: {integrity: sha512-rWS3H04/+mzzJkv0eZ7vEDGiQbgquI1fGfOad6zKvgYQi1SzMmhl7c/DdRGxhaWrVH6z0qWITo8rpnxK/RfEhA==}
dependencies:
@@ -22523,6 +22472,17 @@ packages:
inherits: 2.0.3
dev: false
/util@0.12.4:
resolution: {integrity: sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==}
dependencies:
inherits: 2.0.4
is-arguments: 1.1.1
is-generator-function: 1.0.10
is-typed-array: 1.1.12
safe-buffer: 5.2.1
which-typed-array: 1.1.11
dev: false
/util@0.12.5:
resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}
dependencies:
@@ -22812,19 +22772,10 @@ packages:
peerDependencies:
vite: ^2.0.0 || ^3.0.0
dependencies:
fast-glob: 3.3.1
fast-glob: 3.2.12
vite: 3.2.4(@types/node@17.0.45)(sass@1.53.0)(terser@5.19.2)
dev: true
/vite-plugin-fonts@0.6.0(vite@4.4.9):
resolution: {integrity: sha512-dV6nnLEju8k5EmvlBH6egxkVZ+rgc5zWsJr9+cNRXBMEDnpRGHcZPI260UEDNg2yB99wSTNER2eduEvZFbMIGw==}
peerDependencies:
vite: ^2.0.0 || ^3.0.0
dependencies:
fast-glob: 3.3.1
vite: 4.4.9(@types/node@17.0.45)(sass@1.53.0)(terser@5.19.2)
dev: true
/vite-plugin-html-config@1.0.10(vite@3.2.4):
resolution: {integrity: sha512-qJCVKC/mR4BIy4EG7AHQ3nGo1BF+3fOjVIka0kXKQMlxT12dl9G5YKmjhLohDzySijOb03R2PzYiAdavwKkqQQ==}
engines: {node: '>=12.0.0'}
@@ -23635,6 +23586,12 @@ packages:
- supports-color
dev: true
/vue-github-button@3.0.3:
resolution: {integrity: sha512-O2Kv5HxRMn1qqgt2sSy8N7y2C6WGOeICzgGj6y+JFcnLjorTTzNR8vY5abiPOYDiW03WZ/9hUIn7Gm9CG9pIuA==}
dependencies:
github-buttons: 2.21.1
dev: false
/vue-i18n@9.2.2(vue@3.2.45):
resolution: {integrity: sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==}
engines: {node: '>= 14'}
@@ -23659,6 +23616,26 @@ packages:
'@vue/devtools-api': 6.2.1
vue: 3.3.4
/vue-loader@16.8.3(@vue/compiler-sfc@3.2.45)(vue@3.2.45)(webpack@5.88.2):
resolution: {integrity: sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==}
peerDependencies:
'@vue/compiler-sfc': ^3.0.8
vue: ^3.2.13
webpack: ^4.1.0 || ^5.0.0-0
peerDependenciesMeta:
'@vue/compiler-sfc':
optional: true
vue:
optional: true
dependencies:
'@vue/compiler-sfc': 3.2.45
chalk: 4.1.2
hash-sum: 2.0.0
loader-utils: 2.0.4
vue: 3.2.45
webpack: 5.88.2(esbuild@0.19.2)
dev: true
/vue-pdf-embed@1.1.6(vue@3.3.4):
resolution: {integrity: sha512-CRQIw8OxiD6H1n8KT2zVWbp/00fA3PgSV/JYJ0Ut+FdC1jHrRDHNBj3BvaRVwZFZg3EJ8LLjyEDYxWWUMOjrDw==}
peerDependencies:
@@ -23830,7 +23807,6 @@ packages:
dependencies:
glob-to-regexp: 0.4.1
graceful-fs: 4.2.10
dev: true
/wcwidth@1.0.1:
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
@@ -23916,6 +23892,7 @@ packages:
/webpack-virtual-modules@0.5.0:
resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==}
dev: true
/webpack@5.74.0:
resolution: {integrity: sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==}
@@ -23957,7 +23934,7 @@ packages:
- uglify-js
dev: true
/webpack@5.88.2:
/webpack@5.88.2(esbuild@0.19.2):
resolution: {integrity: sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==}
engines: {node: '>=10.13.0'}
hasBin: true
@@ -23988,14 +23965,13 @@ packages:
neo-async: 2.6.2
schema-utils: 3.3.0
tapable: 2.2.1
terser-webpack-plugin: 5.3.9(webpack@5.88.2)
terser-webpack-plugin: 5.3.9(esbuild@0.19.2)(webpack@5.88.2)
watchpack: 2.4.0
webpack-sources: 3.2.3
transitivePeerDependencies:
- '@swc/core'
- esbuild
- uglify-js
dev: true
/whatwg-encoding@1.0.5:
resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==}
@@ -24462,14 +24438,6 @@ packages:
string-width: 4.2.3
strip-ansi: 6.0.1
/wrap-ansi@8.1.0:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
dependencies:
ansi-styles: 6.1.0
string-width: 5.1.2
strip-ansi: 7.1.0
/wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}

View File

@@ -1,69 +0,0 @@
FROM node:18-alpine3.16 as base_builder
WORKDIR /usr/src/app
ENV HOPP_ALLOW_RUNTIME_ENV=true
RUN npm install -g pnpm
COPY pnpm-lock.yaml .
RUN pnpm fetch
COPY . .
RUN pnpm install -f --offline
FROM base_builder as backend
WORKDIR /usr/src/app/packages/hoppscotch-backend
RUN pnpm exec prisma generate
RUN pnpm run build
# Remove the env file to avoid backend copying it in and using it
RUN rm "../../.env"
ENV PRODUCTION="true"
ENV PORT=3170
ENV APP_PORT=${PORT}
ENV DB_URL=${DATABASE_URL}
CMD ["pnpm", "run", "start:prod"]
EXPOSE 3170
FROM base_builder as fe_builder
WORKDIR /usr/src/app/packages/hoppscotch-selfhost-web
RUN pnpm run generate
FROM caddy:2-alpine as app
WORKDIR /site
COPY --from=fe_builder /usr/src/app/packages/hoppscotch-sh-admin/prod_run.mjs /usr
COPY --from=fe_builder /usr/src/app/packages/hoppscotch-selfhost-web/Caddyfile /etc/caddy/Caddyfile
COPY --from=fe_builder /usr/src/app/packages/hoppscotch-selfhost-web/dist/ .
RUN apk add nodejs npm
RUN npm install -g @import-meta-env/cli
EXPOSE 8080
CMD ["/bin/sh", "-c", "node /usr/prod_run.mjs && caddy run --config /etc/caddy/Caddyfile --adapter caddyfile"]
FROM base_builder as sh_admin_builder
WORKDIR /usr/src/app/packages/hoppscotch-sh-admin
RUN pnpm run build
FROM caddy:2-alpine as sh_admin
WORKDIR /site
COPY --from=sh_admin_builder /usr/src/app/packages/hoppscotch-sh-admin/prod_run.mjs /usr
COPY --from=sh_admin_builder /usr/src/app/packages/hoppscotch-sh-admin/Caddyfile /etc/caddy/Caddyfile
COPY --from=sh_admin_builder /usr/src/app/packages/hoppscotch-sh-admin/dist/ .
RUN apk add nodejs npm
RUN npm install -g @import-meta-env/cli
EXPOSE 8080
CMD ["/bin/sh", "-c", "node /usr/prod_run.mjs && caddy run --config /etc/caddy/Caddyfile --adapter caddyfile"]
FROM backend as aio
RUN apk add caddy tini
RUN npm install -g @import-meta-env/cli
COPY --from=fe_builder /usr/src/app/packages/hoppscotch-selfhost-web/dist /site/selfhost-web
COPY --from=sh_admin_builder /usr/src/app/packages/hoppscotch-sh-admin/dist /site/sh-admin
COPY aio.Caddyfile /etc/caddy/Caddyfile
ENTRYPOINT [ "tini", "--" ]
RUN apk --no-cache add curl
COPY --chmod=755 healthcheck.sh .
HEALTHCHECK --interval=2s CMD /bin/sh ./healthcheck.sh
CMD ["node", "/usr/src/app/aio_run.mjs"]
EXPOSE 3170
EXPOSE 3000
EXPOSE 3100