From 81a7e23a1262d12d7d3fe3b483f115c23e32d517 Mon Sep 17 00:00:00 2001 From: Andrew Bastin Date: Wed, 7 Jun 2023 15:15:56 +0530 Subject: [PATCH 01/30] feat: introduce dioc into hoppscotch-common --- packages/dioc/.gitignore | 24 ++ packages/dioc/README.md | 141 ++++++++++ packages/dioc/index.d.ts | 2 + packages/dioc/lib/container.ts | 147 ++++++++++ packages/dioc/lib/main.ts | 2 + packages/dioc/lib/service.ts | 65 +++++ packages/dioc/lib/testing.ts | 33 +++ packages/dioc/lib/vue.ts | 34 +++ packages/dioc/package.json | 54 ++++ packages/dioc/test/container.spec.ts | 262 ++++++++++++++++++ packages/dioc/test/service.spec.ts | 66 +++++ packages/dioc/test/test-container.spec.ts | 92 ++++++ packages/dioc/testing.d.ts | 2 + packages/dioc/tsconfig.json | 21 ++ packages/dioc/vite.config.ts | 16 ++ packages/dioc/vitest.config.ts | 7 + packages/dioc/vue.d.ts | 2 + packages/hoppscotch-common/package.json | 1 + .../hoppscotch-common/src/modules/dioc.ts | 13 + pnpm-lock.yaml | 198 ++++++++----- 20 files changed, 1117 insertions(+), 65 deletions(-) create mode 100644 packages/dioc/.gitignore create mode 100644 packages/dioc/README.md create mode 100644 packages/dioc/index.d.ts create mode 100644 packages/dioc/lib/container.ts create mode 100644 packages/dioc/lib/main.ts create mode 100644 packages/dioc/lib/service.ts create mode 100644 packages/dioc/lib/testing.ts create mode 100644 packages/dioc/lib/vue.ts create mode 100644 packages/dioc/package.json create mode 100644 packages/dioc/test/container.spec.ts create mode 100644 packages/dioc/test/service.spec.ts create mode 100644 packages/dioc/test/test-container.spec.ts create mode 100644 packages/dioc/testing.d.ts create mode 100644 packages/dioc/tsconfig.json create mode 100644 packages/dioc/vite.config.ts create mode 100644 packages/dioc/vitest.config.ts create mode 100644 packages/dioc/vue.d.ts create mode 100644 packages/hoppscotch-common/src/modules/dioc.ts diff --git a/packages/dioc/.gitignore b/packages/dioc/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/packages/dioc/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/dioc/README.md b/packages/dioc/README.md new file mode 100644 index 000000000..506faa656 --- /dev/null +++ b/packages/dioc/README.md @@ -0,0 +1,141 @@ +# dioc + +A small and lightweight dependency injection / inversion of control system. + +### About + +`dioc` is a really simple **DI/IOC** system where you write services (which are singletons per container) that can depend on each other and emit events that can be listened upon. + +### Demo + +```ts +import { Service, Container } from "dioc" + +// Here is a simple service, which you can define by extending the Service class +// and providing an ID static field (of type string) +export class PersistenceService extends Service { + // This should be unique for each container + public static ID = "PERSISTENCE_SERVICE" + + public read(key: string): string | undefined { + // ... + } + + public write(key: string, value: string) { + // ... + } +} + +type TodoServiceEvent = + | { type: "TODO_CREATED"; index: number } + | { type: "TODO_DELETED"; index: number } + +// Services have a built in event system +// Define the generic argument to say what are the possible emitted values +export class TodoService extends Service { + public static ID = "TODO_SERVICE" + + // Inject persistence service into this service + private readonly persistence = this.bind(PersistenceService) + + public todos = [] + + // Service constructors cannot have arguments + constructor() { + super() + + this.todos = JSON.parse(this.persistence.read("todos") ?? "[]") + } + + public addTodo(text: string) { + // ... + + // You can access services via the bound fields + this.persistence.write("todos", JSON.stringify(this.todos)) + + // This is how you emit an event + this.emit({ + type: "TODO_CREATED", + index, + }) + } + + public removeTodo(index: number) { + // ... + + this.emit({ + type: "TODO_DELETED", + index, + }) + } +} + +// Services need a container to run in +const container = new Container() + +// You can initialize and get services using Container#bind +// It will automatically initialize the service (and its dependencies) +const todoService = container.bind(TodoService) // Returns an instance of TodoService +``` + +### Demo (Unit Test) + +`dioc/testing` contains `TestContainer` which lets you bind mocked services to the container. + +```ts +import { TestContainer } from "dioc/testing" +import { TodoService, PersistenceService } from "./demo.ts" // The above demo code snippet +import { describe, it, expect, vi } from "vitest" + +describe("TodoService", () => { + it("addTodo writes to persistence", () => { + const container = new TestContainer() + + const writeFn = vi.fn() + + // The first parameter is the service to mock and the second parameter + // is the mocked service fields and functions + container.bindMock(PersistenceService, { + read: () => undefined, // Not really important for this test + write: writeFn, + }) + + // the peristence service bind in TodoService will now use the + // above defined mocked implementation + const todoService = container.bind(TodoService) + + todoService.addTodo("sup") + + expect(writeFn).toHaveBeenCalledOnce() + expect(writeFn).toHaveBeenCalledWith("todos", JSON.stringify(["sup"])) + }) +}) +``` + +### Demo (Vue) + +`dioc/vue` contains a Vue Plugin and a `useService` composable that allows Vue components to use the defined services. + +In the app entry point: + +```ts +import { createApp } from "vue" +import { diocPlugin } from "dioc/vue" + +const app = createApp() + +app.use(diocPlugin, { + container: new Container(), // You can pass in the container you want to provide to the components here +}) +``` + +In your Vue components: + +```vue + +``` diff --git a/packages/dioc/index.d.ts b/packages/dioc/index.d.ts new file mode 100644 index 000000000..bbb9d0161 --- /dev/null +++ b/packages/dioc/index.d.ts @@ -0,0 +1,2 @@ +export { default } from "./dist/main.d.ts" +export * from "./dist/main.d.ts" diff --git a/packages/dioc/lib/container.ts b/packages/dioc/lib/container.ts new file mode 100644 index 000000000..bad74f3f5 --- /dev/null +++ b/packages/dioc/lib/container.ts @@ -0,0 +1,147 @@ +import { Service } from "./service" +import { Observable, Subject } from 'rxjs' + +/** + * Stores the current container instance in the current operating context. + * + * NOTE: This should not be used outside of dioc library code + */ +export let currentContainer: Container | null = null + +/** + * The events emitted by the container + * + * `SERVICE_BIND` - emitted when a service is bound to the container directly or as a dependency to another service + * `SERVICE_INIT` - emitted when a service is initialized + */ +export type ContainerEvent = + | { + type: 'SERVICE_BIND'; + + /** The Service ID of the service being bounded (the dependency) */ + boundeeID: string; + + /** + * The Service ID of the bounder that is binding the boundee (the dependent) + * + * NOTE: This will be undefined if the service is bound directly to the container + */ + bounderID: string | undefined + } + | { + type: 'SERVICE_INIT'; + + /** The Service ID of the service being initialized */ + serviceID: string + } + +/** + * The dependency injection container, allows for services to be initialized and maintains the dependency trees. + */ +export class Container { + /** Used during the `bind` operation to detect circular dependencies */ + private bindStack: string[] = [] + + /** The map of bound services to their IDs */ + protected boundMap = new Map>() + + /** The RxJS observable representing the event stream */ + protected event$ = new Subject() + + /** + * Returns whether a container has the given service bound + * @param service The service to check for + */ + public hasBound< + T extends typeof Service & { ID: string } + >(service: T): boolean { + return this.boundMap.has(service.ID) + } + + /** + * Returns the service bound to the container with the given ID or if not found, undefined. + * + * NOTE: This is an advanced method and should not be used as much as possible. + * + * @param serviceID The ID of the service to get + */ + public getBoundServiceWithID(serviceID: string): Service | undefined { + return this.boundMap.get(serviceID) + } + + /** + * Binds a service to the container. This is equivalent to marking a service as a dependency. + * @param service The class reference of a service to bind + * @param bounder The class reference of the service that is binding the service (if bound directly to the container, this should be undefined) + */ + public bind & { ID: string }>( + service: T, + bounder: ((typeof Service) & { ID: string }) | undefined = undefined + ): InstanceType { + // We need to store the current container in a variable so that we can restore it after the bind operation + const oldCurrentContainer = currentContainer; + currentContainer = this; + + // If the service is already bound, return the existing instance + if (this.hasBound(service)) { + this.event$.next({ + type: 'SERVICE_BIND', + boundeeID: service.ID, + bounderID: bounder?.ID // Return the bounder ID if it is defined, else assume its the container + }) + + return this.boundMap.get(service.ID) as InstanceType // Casted as InstanceType because service IDs and types are expected to match + } + + // Detect circular dependency and throw error + if (this.bindStack.findIndex((serviceID) => serviceID === service.ID) !== -1) { + const circularServices = `${this.bindStack.join(' -> ')} -> ${service.ID}` + + throw new Error(`Circular dependency detected.\nChain: ${circularServices}`) + } + + // Push the service ID onto the bind stack to detect circular dependencies + this.bindStack.push(service.ID) + + // Initialize the service and emit events + + // NOTE: We need to cast the service to any as TypeScript thinks that the service is abstract + const instance: Service = new (service as any)() + + this.boundMap.set(service.ID, instance) + + this.bindStack.pop() + + this.event$.next({ + type: 'SERVICE_INIT', + serviceID: service.ID, + }) + + this.event$.next({ + type: 'SERVICE_BIND', + boundeeID: service.ID, + bounderID: bounder?.ID + }) + + + // Restore the current container + currentContainer = oldCurrentContainer; + + // We expect the return type to match the service definition + return instance as InstanceType + } + + /** + * Returns an iterator of the currently bound service IDs and their instances + */ + public getBoundServices(): IterableIterator<[string, Service]> { + return this.boundMap.entries() + } + + /** + * Returns the public container event stream + */ + public getEventStream(): Observable { + return this.event$.asObservable() + } +} diff --git a/packages/dioc/lib/main.ts b/packages/dioc/lib/main.ts new file mode 100644 index 000000000..a6882c575 --- /dev/null +++ b/packages/dioc/lib/main.ts @@ -0,0 +1,2 @@ +export * from "./container" +export * from "./service" diff --git a/packages/dioc/lib/service.ts b/packages/dioc/lib/service.ts new file mode 100644 index 000000000..1e9cb6b8f --- /dev/null +++ b/packages/dioc/lib/service.ts @@ -0,0 +1,65 @@ +import { Observable, Subject } from 'rxjs' +import { Container, currentContainer } from './container' + +/** + * A Dioc service that can bound to a container and can bind dependency services. + * + * NOTE: Services cannot have a constructor that takes arguments. + * + * @template EventDef The type of events that can be emitted by the service. These will be accessible by event streams + */ +export abstract class Service { + + /** + * The internal event stream of the service + */ + private event$ = new Subject() + + /** The container the service is bound to */ + #container: Container + + constructor() { + if (!currentContainer) { + throw new Error( + `Tried to initialize service with no container (ID: ${ (this.constructor as any).ID })` + ) + } + + this.#container = currentContainer + } + + /** + * Binds a dependency service into this service. + * @param service The class reference of the service to bind + */ + protected bind & { ID: string }>(service: T): InstanceType { + if (!currentContainer) { + throw new Error('No currentContainer defined.') + } + + return currentContainer.bind(service, this.constructor as typeof Service & { ID: string }) + } + + /** + * Returns the container the service is bound to + */ + protected getContainer(): Container { + return this.#container + } + + /** + * Emits an event on the service's event stream + * @param event The event to emit + */ + protected emit(event: EventDef) { + this.event$.next(event) + } + + /** + * Returns the event stream of the service + */ + public getEventStream(): Observable { + + return this.event$.asObservable() + } +} diff --git a/packages/dioc/lib/testing.ts b/packages/dioc/lib/testing.ts new file mode 100644 index 000000000..0464f036b --- /dev/null +++ b/packages/dioc/lib/testing.ts @@ -0,0 +1,33 @@ +import { Container, Service } from "./main"; + +/** + * A container that can be used for writing tests, contains additional methods + * for binding suitable for writing tests. (see `bindMock`). + */ +export class TestContainer extends Container { + + /** + * Binds a mock service to the container. + * + * @param service + * @param mock + */ + public bindMock< + T extends typeof Service & { ID: string }, + U extends Partial> + >(service: T, mock: U): U { + if (this.boundMap.has(service.ID)) { + throw new Error(`Service '${service.ID}' already bound to container. Did you already call bindMock on this ?`) + } + + this.boundMap.set(service.ID, mock as any) + + this.event$.next({ + type: "SERVICE_BIND", + boundeeID: service.ID, + bounderID: undefined, + }) + + return mock + } +} diff --git a/packages/dioc/lib/vue.ts b/packages/dioc/lib/vue.ts new file mode 100644 index 000000000..1342010f4 --- /dev/null +++ b/packages/dioc/lib/vue.ts @@ -0,0 +1,34 @@ +import { Plugin, inject } from "vue" +import { Container } from "./container" +import { Service } from "./service" + +const VUE_CONTAINER_KEY = Symbol() + +// TODO: Some Vue version issue with plugin generics is breaking type checking +/** + * The Vue Dioc Plugin, this allows the composables to work and access the container + * + * NOTE: Make sure you add `vue` as dependency to be able to use this plugin (duh) + */ +export const diocPlugin: Plugin = { + install(app, { container }) { + app.provide(VUE_CONTAINER_KEY, container) + } +} + +/** + * A composable that binds a service to a Vue Component + * + * @param service The class reference of the service to bind + */ +export function useService< + T extends typeof Service & { ID: string } +>(service: T): InstanceType { + const container = inject(VUE_CONTAINER_KEY) as Container | undefined | null + + if (!container) { + throw new Error("Container not found, did you forget to install the dioc plugin?") + } + + return container.bind(service) +} diff --git a/packages/dioc/package.json b/packages/dioc/package.json new file mode 100644 index 000000000..0a0f4634c --- /dev/null +++ b/packages/dioc/package.json @@ -0,0 +1,54 @@ +{ + "name": "dioc", + "private": true, + "version": "0.1.0", + "type": "module", + "files": [ + "dist", + "index.d.ts" + ], + "main": "./dist/counter.umd.cjs", + "module": "./dist/counter.js", + "types": "./index.d.ts", + "exports": { + ".": { + "types": "./dist/main.d.ts", + "require": "./dist/index.cjs", + "import": "./dist/index.js" + }, + "./vue": { + "types": "./dist/vue.d.ts", + "require": "./dist/vue.cjs", + "import": "./dist/vue.js" + }, + "./testing": { + "types": "./dist/testing.d.ts", + "require": "./dist/testing.cjs", + "import": "./dist/testing.js" + } + }, + "scripts": { + "dev": "vite", + "build": "vite build && tsc --emitDeclarationOnly", + "prepare": "pnpm run build", + "test": "vitest run", + "do-test": "pnpm run test", + "test:watch": "vitest" + }, + "devDependencies": { + "typescript": "^4.9.4", + "vite": "^4.0.4", + "vitest": "^0.29.3" + }, + "dependencies": { + "rxjs": "^7.8.1" + }, + "peerDependencies": { + "vue": "^3.2.25" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } + } +} diff --git a/packages/dioc/test/container.spec.ts b/packages/dioc/test/container.spec.ts new file mode 100644 index 000000000..f70e6e677 --- /dev/null +++ b/packages/dioc/test/container.spec.ts @@ -0,0 +1,262 @@ +import { it, expect, describe, vi } from "vitest" +import { Service } from "../lib/service" +import { Container, currentContainer, ContainerEvent } from "../lib/container" + +class TestServiceA extends Service { + public static ID = "TestServiceA" +} + +class TestServiceB extends Service { + public static ID = "TestServiceB" + + // Marked public to allow for testing + public readonly serviceA = this.bind(TestServiceA) +} + +describe("Container", () => { + describe("getBoundServiceWithID", () => { + it("returns the service instance if it is bound to the container", () => { + const container = new Container() + + const service = container.bind(TestServiceA) + + expect(container.getBoundServiceWithID(TestServiceA.ID)).toBe(service) + }) + + it("returns undefined if the service is not bound to the container", () => { + const container = new Container() + + expect(container.getBoundServiceWithID(TestServiceA.ID)).toBeUndefined() + }) + }) + + describe("bind", () => { + it("correctly binds the service to it", () => { + const container = new Container() + + const service = container.bind(TestServiceA) + + // @ts-expect-error getContainer is defined as a protected property, but we are leveraging it here to check + expect(service.getContainer()).toBe(container) + }) + + it("after bind, the current container is set back to its previous value", () => { + const originalValue = currentContainer + + const container = new Container() + container.bind(TestServiceA) + + expect(currentContainer).toBe(originalValue) + }) + + it("dependent services are registered in the same container", () => { + const container = new Container() + + const serviceB = container.bind(TestServiceB) + + // @ts-expect-error getContainer is defined as a protected property, but we are leveraging it here to check + expect(serviceB.serviceA.getContainer()).toBe(container) + }) + + it("binding an already initialized service returns the initialized instance (services are singletons)", () => { + const container = new Container() + + const serviceA = container.bind(TestServiceA) + const serviceA2 = container.bind(TestServiceA) + + expect(serviceA).toBe(serviceA2) + }) + + it("binding a service which is a dependency of another service returns the same instance created from the dependency resolution (services are singletons)", () => { + const container = new Container() + + const serviceB = container.bind(TestServiceB) + const serviceA = container.bind(TestServiceA) + + expect(serviceB.serviceA).toBe(serviceA) + }) + + it("binding an initialized service as a dependency returns the same instance", () => { + const container = new Container() + + const serviceA = container.bind(TestServiceA) + const serviceB = container.bind(TestServiceB) + + expect(serviceB.serviceA).toBe(serviceA) + }) + + it("container emits an init event when an uninitialized service is initialized via bind and event only called once", () => { + const container = new Container() + + const serviceFunc = vi.fn< + [ContainerEvent & { type: "SERVICE_INIT" }], + void + >() + + container.getEventStream().subscribe((ev) => { + if (ev.type === "SERVICE_INIT") { + serviceFunc(ev) + } + }) + + const instance = container.bind(TestServiceA) + + expect(serviceFunc).toHaveBeenCalledOnce() + expect(serviceFunc).toHaveBeenCalledWith({ + type: "SERVICE_INIT", + serviceID: TestServiceA.ID, + }) + }) + + it("the bind event emitted has an undefined bounderID when the service is bound directly to the container", () => { + const container = new Container() + + const serviceFunc = vi.fn< + [ContainerEvent & { type: "SERVICE_BIND" }], + void + >() + + container.getEventStream().subscribe((ev) => { + if (ev.type === "SERVICE_BIND") { + serviceFunc(ev) + } + }) + + container.bind(TestServiceA) + + expect(serviceFunc).toHaveBeenCalledOnce() + expect(serviceFunc).toHaveBeenCalledWith({ + type: "SERVICE_BIND", + boundeeID: TestServiceA.ID, + bounderID: undefined, + }) + }) + + it("the bind event emitted has the correct bounderID when the service is bound to another service", () => { + const container = new Container() + + const serviceFunc = vi.fn< + [ContainerEvent & { type: "SERVICE_BIND" }], + void + >() + + container.getEventStream().subscribe((ev) => { + // We only care about the bind event of TestServiceA + if (ev.type === "SERVICE_BIND" && ev.boundeeID === TestServiceA.ID) { + serviceFunc(ev) + } + }) + + container.bind(TestServiceB) + + expect(serviceFunc).toHaveBeenCalledOnce() + expect(serviceFunc).toHaveBeenCalledWith({ + type: "SERVICE_BIND", + boundeeID: TestServiceA.ID, + bounderID: TestServiceB.ID, + }) + }) + }) + + describe("hasBound", () => { + it("returns true if the given service is bound to the container", () => { + const container = new Container() + + container.bind(TestServiceA) + + expect(container.hasBound(TestServiceA)).toEqual(true) + }) + + it("returns false if the given service is not bound to the container", () => { + const container = new Container() + + expect(container.hasBound(TestServiceA)).toEqual(false) + }) + + it("returns true when the service is bound because it is a dependency of another service", () => { + const container = new Container() + + container.bind(TestServiceB) + + expect(container.hasBound(TestServiceA)).toEqual(true) + }) + }) + + describe("getEventStream", () => { + it("returns an observable which emits events correctly when services are initialized", () => { + const container = new Container() + + const serviceFunc = vi.fn< + [ContainerEvent & { type: "SERVICE_INIT" }], + void + >() + + container.getEventStream().subscribe((ev) => { + if (ev.type === "SERVICE_INIT") { + serviceFunc(ev) + } + }) + + container.bind(TestServiceB) + + expect(serviceFunc).toHaveBeenCalledTimes(2) + expect(serviceFunc).toHaveBeenNthCalledWith(1, { + type: "SERVICE_INIT", + serviceID: TestServiceA.ID, + }) + expect(serviceFunc).toHaveBeenNthCalledWith(2, { + type: "SERVICE_INIT", + serviceID: TestServiceB.ID, + }) + }) + + it("returns an observable which emits events correctly when services are bound", () => { + const container = new Container() + + const serviceFunc = vi.fn< + [ContainerEvent & { type: "SERVICE_BIND" }], + void + >() + + container.getEventStream().subscribe((ev) => { + if (ev.type === "SERVICE_BIND") { + serviceFunc(ev) + } + }) + + container.bind(TestServiceB) + + expect(serviceFunc).toHaveBeenCalledTimes(2) + expect(serviceFunc).toHaveBeenNthCalledWith(1, { + type: "SERVICE_BIND", + boundeeID: TestServiceA.ID, + bounderID: TestServiceB.ID, + }) + expect(serviceFunc).toHaveBeenNthCalledWith(2, { + type: "SERVICE_BIND", + boundeeID: TestServiceB.ID, + bounderID: undefined, + }) + }) + }) + + describe("getBoundServices", () => { + it("returns an iterator over all services bound to the container in the format [service id, service instance]", () => { + const container = new Container() + + const instanceB = container.bind(TestServiceB) + const instanceA = instanceB.serviceA + + expect(Array.from(container.getBoundServices())).toEqual([ + [TestServiceA.ID, instanceA], + [TestServiceB.ID, instanceB], + ]) + }) + + it("returns an empty iterator if no services are bound", () => { + const container = new Container() + + expect(Array.from(container.getBoundServices())).toEqual([]) + }) + }) +}) diff --git a/packages/dioc/test/service.spec.ts b/packages/dioc/test/service.spec.ts new file mode 100644 index 000000000..e25524086 --- /dev/null +++ b/packages/dioc/test/service.spec.ts @@ -0,0 +1,66 @@ +import { describe, expect, it, vi } from "vitest" +import { Service, Container } from "../lib/main" + +class TestServiceA extends Service { + public static ID = "TestServiceA" +} + +class TestServiceB extends Service<"test"> { + public static ID = "TestServiceB" + + // Marked public to allow for testing + public readonly serviceA = this.bind(TestServiceA) + + public emitTestEvent() { + this.emit("test") + } +} + +describe("Service", () => { + describe("constructor", () => { + it("throws an error if the service is initialized without a container", () => { + expect(() => new TestServiceA()).toThrowError( + "Tried to initialize service with no container (ID: TestServiceA)" + ) + }) + }) + + describe("bind", () => { + it("correctly binds the dependency service using the container", () => { + const container = new Container() + + const serviceA = container.bind(TestServiceA) + + const serviceB = container.bind(TestServiceB) + expect(serviceB.serviceA).toBe(serviceA) + }) + }) + + describe("getContainer", () => { + it("returns the container the service is bound to", () => { + const container = new Container() + + const serviceA = container.bind(TestServiceA) + + // @ts-expect-error getContainer is a protected member, we are just using it to help with testing + expect(serviceA.getContainer()).toBe(container) + }) + }) + + describe("getEventStream", () => { + it("returns the valid event stream of the service", () => { + const container = new Container() + + const serviceB = container.bind(TestServiceB) + + const serviceFunc = vi.fn() + + serviceB.getEventStream().subscribe(serviceFunc) + + serviceB.emitTestEvent() + + expect(serviceFunc).toHaveBeenCalledOnce() + expect(serviceFunc).toHaveBeenCalledWith("test") + }) + }) +}) diff --git a/packages/dioc/test/test-container.spec.ts b/packages/dioc/test/test-container.spec.ts new file mode 100644 index 000000000..b51914d19 --- /dev/null +++ b/packages/dioc/test/test-container.spec.ts @@ -0,0 +1,92 @@ +import { describe, expect, it, vi } from "vitest" +import { TestContainer } from "../lib/testing" +import { Service } from "../lib/service" +import { ContainerEvent } from "../lib/container" + +class TestServiceA extends Service { + public static ID = "TestServiceA" + + public test() { + return "real" + } +} + +class TestServiceB extends Service { + public static ID = "TestServiceB" + + // declared public to help with testing + public readonly serviceA = this.bind(TestServiceA) + + public test() { + return this.serviceA.test() + } +} + +describe("TestContainer", () => { + describe("bindMock", () => { + it("returns the fake service defined", () => { + const container = new TestContainer() + + const fakeService = { + test: () => "fake", + } + + const result = container.bindMock(TestServiceA, fakeService) + + expect(result).toBe(fakeService) + }) + + it("new services bound to the container get the mock service", () => { + const container = new TestContainer() + + const fakeServiceA = { + test: () => "fake", + } + + container.bindMock(TestServiceA, fakeServiceA) + + const serviceB = container.bind(TestServiceB) + + expect(serviceB.serviceA).toBe(fakeServiceA) + }) + + it("container emits SERVICE_BIND event", () => { + const container = new TestContainer() + + const fakeServiceA = { + test: () => "fake", + } + + const serviceFunc = vi.fn<[ContainerEvent, void]>() + + container.getEventStream().subscribe((ev) => { + serviceFunc(ev) + }) + + container.bindMock(TestServiceA, fakeServiceA) + + expect(serviceFunc).toHaveBeenCalledOnce() + expect(serviceFunc).toHaveBeenCalledWith({ + type: "SERVICE_BIND", + boundeeID: TestServiceA.ID, + bounderID: undefined, + }) + }) + + it("throws if service already bound", () => { + const container = new TestContainer() + + const fakeServiceA = { + test: () => "fake", + } + + container.bindMock(TestServiceA, fakeServiceA) + + expect(() => { + container.bindMock(TestServiceA, fakeServiceA) + }).toThrowError( + "Service 'TestServiceA' already bound to container. Did you already call bindMock on this ?" + ) + }) + }) +}) diff --git a/packages/dioc/testing.d.ts b/packages/dioc/testing.d.ts new file mode 100644 index 000000000..f0219165a --- /dev/null +++ b/packages/dioc/testing.d.ts @@ -0,0 +1,2 @@ +export { default } from "./dist/testing.d.ts" +export * from "./dist/testing.d.ts" diff --git a/packages/dioc/tsconfig.json b/packages/dioc/tsconfig.json new file mode 100644 index 000000000..8033b856e --- /dev/null +++ b/packages/dioc/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "declaration": true, + "sourceMap": true, + "outDir": "dist", + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "skipLibCheck": true + }, + "include": ["lib"] +} diff --git a/packages/dioc/vite.config.ts b/packages/dioc/vite.config.ts new file mode 100644 index 000000000..51388192e --- /dev/null +++ b/packages/dioc/vite.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + lib: { + entry: { + index: './lib/main.ts', + vue: './lib/vue.ts', + testing: './lib/testing.ts', + }, + }, + rollupOptions: { + external: ['vue'], + } + }, +}) diff --git a/packages/dioc/vitest.config.ts b/packages/dioc/vitest.config.ts new file mode 100644 index 000000000..1d45d7226 --- /dev/null +++ b/packages/dioc/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vitest/config" + +export default defineConfig({ + test: { + + } +}) diff --git a/packages/dioc/vue.d.ts b/packages/dioc/vue.d.ts new file mode 100644 index 000000000..63ea4bff4 --- /dev/null +++ b/packages/dioc/vue.d.ts @@ -0,0 +1,2 @@ +export { default } from "./dist/vue.d.ts" +export * from "./dist/vue.d.ts" diff --git a/packages/hoppscotch-common/package.json b/packages/hoppscotch-common/package.json index dcbab993f..79143474b 100644 --- a/packages/hoppscotch-common/package.json +++ b/packages/hoppscotch-common/package.json @@ -18,6 +18,7 @@ "do-lintfix": "pnpm run lintfix" }, "dependencies": { + "dioc": "workspace:^", "@apidevtools/swagger-parser": "^10.1.0", "@codemirror/autocomplete": "^6.0.3", "@codemirror/commands": "^6.0.1", diff --git a/packages/hoppscotch-common/src/modules/dioc.ts b/packages/hoppscotch-common/src/modules/dioc.ts new file mode 100644 index 000000000..bdbb89619 --- /dev/null +++ b/packages/hoppscotch-common/src/modules/dioc.ts @@ -0,0 +1,13 @@ +import { HoppModule } from "." +import { Container } from "dioc" +import { diocPlugin } from "dioc/vue" + +export default { + onVueAppInit(app) { + // TODO: look into this + // @ts-expect-error Something weird with Vue versions + app.use(diocPlugin, { + container: new Container(), + }) + }, +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 051edc531..b71abda4b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,6 +58,25 @@ importers: specifier: ^4.6.3 version: 4.7.4 + packages/dioc: + dependencies: + rxjs: + specifier: ^7.8.1 + version: 7.8.1 + vue: + specifier: ^3.2.25 + version: 3.2.45 + devDependencies: + typescript: + specifier: ^4.9.4 + version: 4.9.4 + vite: + specifier: ^4.0.4 + version: 4.0.4(@types/node@17.0.45) + vitest: + specifier: ^0.29.3 + version: 0.29.8(terser@5.14.1) + packages/hoppscotch-backend: dependencies: '@nestjs-modules/mailer': @@ -442,6 +461,9 @@ importers: buffer: specifier: ^6.0.3 version: 6.0.3 + dioc: + specifier: workspace:^ + version: link:../dioc esprima: specifier: ^4.0.1 version: 4.0.1 @@ -1619,7 +1641,7 @@ packages: '@babel/parser': 7.21.2 '@babel/runtime': 7.18.6 '@babel/traverse': 7.18.6 - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 babel-preset-fbjs: 3.4.0(@babel/core@7.18.6) chalk: 4.1.2 fb-watchman: 2.0.1 @@ -1648,7 +1670,7 @@ packages: '@babel/parser': 7.21.2 '@babel/runtime': 7.18.6 '@babel/traverse': 7.18.6 - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 babel-preset-fbjs: 3.4.0(@babel/core@7.18.6) chalk: 4.1.2 fb-watchman: 2.0.1 @@ -1696,10 +1718,10 @@ packages: '@babel/helper-compilation-targets': 7.18.6(@babel/core@7.18.6) '@babel/helper-module-transforms': 7.18.6 '@babel/helpers': 7.18.6 - '@babel/parser': 7.21.2 + '@babel/parser': 7.18.6 '@babel/template': 7.18.6 '@babel/traverse': 7.18.6 - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 convert-source-map: 1.8.0 debug: 4.3.4(supports-color@9.2.2) gensync: 1.0.0-beta.2 @@ -1713,7 +1735,7 @@ packages: resolution: {integrity: sha512-shck+7VLlY72a2w9c3zYWuE1pwOKEiQHV7GTUbSnhyl5eu3i04t30tBY82ZRWrDfo3gkakCFtevExnxbkf2a3A==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 '@jridgewell/gen-mapping': 0.3.2 jsesc: 2.5.2 dev: true @@ -1820,14 +1842,14 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.18.6 - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 dev: true /@babel/helper-hoist-variables@7.18.6: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 dev: true /@babel/helper-member-expression-to-functions@7.18.6: @@ -1852,10 +1874,10 @@ packages: '@babel/helper-module-imports': 7.18.6 '@babel/helper-simple-access': 7.18.6 '@babel/helper-split-export-declaration': 7.18.6 - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-validator-identifier': 7.18.6 '@babel/template': 7.18.6 '@babel/traverse': 7.18.6 - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 transitivePeerDependencies: - supports-color dev: true @@ -1923,13 +1945,17 @@ packages: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 dev: true /@babel/helper-string-parser@7.19.4: resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} engines: {node: '>=6.9.0'} + /@babel/helper-validator-identifier@7.18.6: + resolution: {integrity: sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==} + engines: {node: '>=6.9.0'} + /@babel/helper-validator-identifier@7.19.1: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} @@ -1957,7 +1983,7 @@ packages: dependencies: '@babel/template': 7.18.6 '@babel/traverse': 7.18.6 - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 transitivePeerDependencies: - supports-color dev: true @@ -1966,7 +1992,7 @@ packages: resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-validator-identifier': 7.18.6 chalk: 2.4.2 js-tokens: 4.0.0 @@ -1982,7 +2008,7 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.21.2 + '@babel/types': 7.20.7 dev: true /@babel/parser@7.21.2: @@ -1990,7 +2016,7 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.18.7 + '@babel/types': 7.21.2 /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.18.6): resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} @@ -2686,7 +2712,7 @@ packages: '@babel/helper-module-imports': 7.18.6 '@babel/helper-plugin-utils': 7.18.6 '@babel/plugin-syntax-jsx': 7.18.6(@babel/core@7.18.6) - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 dev: true /@babel/plugin-transform-regenerator@7.18.6(@babel/core@7.18.6): @@ -2897,7 +2923,7 @@ packages: dependencies: '@babel/code-frame': 7.18.6 '@babel/parser': 7.21.2 - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 dev: true /@babel/template@7.20.7: @@ -2905,8 +2931,8 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.18.6 - '@babel/parser': 7.21.2 - '@babel/types': 7.21.2 + '@babel/parser': 7.20.15 + '@babel/types': 7.20.7 dev: true /@babel/traverse@7.18.6: @@ -2920,7 +2946,7 @@ packages: '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-split-export-declaration': 7.18.6 '@babel/parser': 7.21.2 - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 debug: 4.3.4(supports-color@9.2.2) globals: 11.12.0 transitivePeerDependencies: @@ -2931,7 +2957,7 @@ packages: resolution: {integrity: sha512-QG3yxTcTIBoAcQmkCs+wAPYZhu7Dk9rXKacINfNbdJDNERTbLQbHGyVG8q/YGMPeCJRIhSY0+fTc5+xuh6WPSQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-validator-identifier': 7.18.6 to-fast-properties: 2.0.0 /@babel/types@7.20.7: @@ -3178,10 +3204,10 @@ packages: '@commitlint/execute-rule': 16.2.1 '@commitlint/resolve-extends': 16.2.1 '@commitlint/types': 16.2.1 - '@types/node': 18.11.10 + '@types/node': 17.0.45 chalk: 4.1.2 cosmiconfig: 7.0.1 - cosmiconfig-typescript-loader: 2.0.2(@types/node@18.11.10)(cosmiconfig@7.0.1)(typescript@4.9.4) + cosmiconfig-typescript-loader: 2.0.2(@types/node@17.0.45)(cosmiconfig@7.0.1)(typescript@4.9.4) lodash: 4.17.21 resolve-from: 5.0.0 typescript: 4.9.4 @@ -4952,9 +4978,9 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@babel/parser': 7.21.2 + '@babel/parser': 7.18.6 '@babel/traverse': 7.18.6 - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 '@graphql-tools/utils': 8.8.0(graphql@15.8.0) graphql: 15.8.0 tslib: 2.5.0 @@ -4967,7 +4993,7 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@babel/parser': 7.21.2 + '@babel/parser': 7.20.15 '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.18.6) '@babel/traverse': 7.18.6 '@babel/types': 7.21.2 @@ -5416,7 +5442,7 @@ packages: engines: {node: ^8.13.0 || >=10.10.0} dependencies: '@grpc/proto-loader': 0.6.13 - '@types/node': 18.11.10 + '@types/node': 17.0.45 dev: false /@grpc/proto-loader@0.6.13: @@ -5772,7 +5798,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 18.11.10 + '@types/node': 17.0.45 chalk: 4.1.2 jest-message-util: 27.5.1 jest-util: 27.5.1 @@ -5805,7 +5831,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.11.10 + '@types/node': 17.0.45 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.8.1 @@ -5884,7 +5910,7 @@ packages: dependencies: '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.11.10 + '@types/node': 17.0.45 jest-mock: 27.5.1 dev: true @@ -5935,7 +5961,7 @@ packages: dependencies: '@jest/types': 27.5.1 '@sinonjs/fake-timers': 8.1.0 - '@types/node': 18.11.10 + '@types/node': 17.0.45 jest-message-util: 27.5.1 jest-mock: 27.5.1 jest-util: 27.5.1 @@ -5988,7 +6014,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.11.10 + '@types/node': 17.0.45 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -6182,7 +6208,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.11.10 + '@types/node': 17.0.45 '@types/yargs': 16.0.4 chalk: 4.1.2 dev: true @@ -7274,7 +7300,7 @@ packages: resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==} dependencies: '@babel/parser': 7.21.2 - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.17.1 @@ -7296,7 +7322,7 @@ packages: /@types/babel__traverse@7.17.1: resolution: {integrity: sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==} dependencies: - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 dev: true /@types/bcrypt@5.0.0: @@ -8500,7 +8526,7 @@ packages: /@vue/compiler-core@3.2.45: resolution: {integrity: sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==} dependencies: - '@babel/parser': 7.21.2 + '@babel/parser': 7.18.6 '@vue/shared': 3.2.45 estree-walker: 2.0.2 source-map: 0.6.1 @@ -8534,7 +8560,7 @@ packages: /@vue/compiler-sfc@3.2.37: resolution: {integrity: sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg==} dependencies: - '@babel/parser': 7.21.2 + '@babel/parser': 7.18.6 '@vue/compiler-core': 3.2.37 '@vue/compiler-dom': 3.2.37 '@vue/compiler-ssr': 3.2.37 @@ -8542,7 +8568,7 @@ packages: '@vue/shared': 3.2.37 estree-walker: 2.0.2 magic-string: 0.25.9 - postcss: 8.4.21 + postcss: 8.4.19 source-map: 0.6.1 /@vue/compiler-sfc@3.2.39: @@ -8657,7 +8683,7 @@ packages: /@vue/reactivity-transform@3.2.45: resolution: {integrity: sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==} dependencies: - '@babel/parser': 7.21.2 + '@babel/parser': 7.18.6 '@vue/compiler-core': 3.2.45 '@vue/shared': 3.2.45 estree-walker: 2.0.2 @@ -9070,12 +9096,12 @@ packages: acorn-walk: 8.2.0 dev: true - /acorn-import-assertions@1.8.0(acorn@8.8.2): + /acorn-import-assertions@1.8.0(acorn@8.8.0): resolution: {integrity: sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==} peerDependencies: acorn: ^8 dependencies: - acorn: 8.8.2 + acorn: 8.8.0 /acorn-jsx@5.3.2(acorn@7.4.1): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -10582,7 +10608,7 @@ packages: dependencies: '@iarna/toml': 2.2.5 - /cosmiconfig-typescript-loader@2.0.2(@types/node@18.11.10)(cosmiconfig@7.0.1)(typescript@4.9.4): + /cosmiconfig-typescript-loader@2.0.2(@types/node@17.0.45)(cosmiconfig@7.0.1)(typescript@4.9.4): resolution: {integrity: sha512-KmE+bMjWMXJbkWCeY4FJX/npHuZPNr9XF9q9CIQ/bpFwi1qHfCmSiKarrCcRa0LO4fWjk93pVoeRtJAkTGcYNw==} engines: {node: '>=12', npm: '>=6'} peerDependencies: @@ -10590,9 +10616,9 @@ packages: cosmiconfig: '>=7' typescript: '>=3' dependencies: - '@types/node': 18.11.10 + '@types/node': 17.0.45 cosmiconfig: 7.0.1 - ts-node: 10.8.2(@types/node@18.11.10)(typescript@4.9.4) + ts-node: 10.8.2(@types/node@17.0.45)(typescript@4.9.4) typescript: 4.9.4 transitivePeerDependencies: - '@swc/core' @@ -13498,7 +13524,7 @@ packages: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: graphql: 15.8.0 - tslib: 2.5.0 + tslib: 2.4.1 /graphql-tag@2.12.6(graphql@16.6.0): resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} @@ -13507,7 +13533,7 @@ packages: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: graphql: 16.6.0 - tslib: 2.5.0 + tslib: 2.4.1 dev: true /graphql-ws@5.11.3(graphql@16.6.0): @@ -14481,7 +14507,7 @@ packages: engines: {node: '>=8'} dependencies: '@babel/core': 7.18.6 - '@babel/parser': 7.21.2 + '@babel/parser': 7.18.6 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.0 @@ -14559,7 +14585,7 @@ packages: '@jest/environment': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.11.10 + '@types/node': 17.0.45 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -14827,7 +14853,7 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.11.10 + '@types/node': 17.0.45 jest-mock: 27.5.1 jest-util: 27.5.1 jsdom: 16.7.0 @@ -14845,7 +14871,7 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.11.10 + '@types/node': 17.0.45 jest-mock: 27.5.1 jest-util: 27.5.1 dev: true @@ -14883,7 +14909,7 @@ packages: dependencies: '@jest/types': 27.5.1 '@types/graceful-fs': 4.1.5 - '@types/node': 18.11.10 + '@types/node': 17.0.45 anymatch: 3.1.2 fb-watchman: 2.0.1 graceful-fs: 4.2.10 @@ -14924,7 +14950,7 @@ packages: '@jest/source-map': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.11.10 + '@types/node': 17.0.45 chalk: 4.1.2 co: 4.6.0 expect: 27.5.1 @@ -14984,7 +15010,7 @@ packages: chalk: 4.1.2 jest-diff: 29.3.1 jest-get-type: 29.2.0 - pretty-format: 29.4.1 + pretty-format: 29.3.1 dev: true /jest-matcher-utils@29.4.1: @@ -15037,7 +15063,7 @@ packages: chalk: 4.1.2 graceful-fs: 4.2.10 micromatch: 4.0.5 - pretty-format: 29.4.1 + pretty-format: 29.3.1 slash: 3.0.0 stack-utils: 2.0.5 dev: true @@ -15073,7 +15099,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 18.11.10 + '@types/node': 17.0.45 dev: true /jest-mock@29.4.1: @@ -15180,7 +15206,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.11.10 + '@types/node': 17.0.45 chalk: 4.1.2 emittery: 0.8.1 graceful-fs: 4.2.10 @@ -15297,7 +15323,7 @@ packages: resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@types/node': 18.11.10 + '@types/node': 17.0.45 graceful-fs: 4.2.10 dev: true @@ -15309,7 +15335,7 @@ packages: '@babel/generator': 7.18.7 '@babel/plugin-syntax-typescript': 7.18.6(@babel/core@7.18.6) '@babel/traverse': 7.18.6 - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 '@types/babel__traverse': 7.17.1 @@ -15340,7 +15366,7 @@ packages: '@babel/plugin-syntax-jsx': 7.18.6(@babel/core@7.18.6) '@babel/plugin-syntax-typescript': 7.18.6(@babel/core@7.18.6) '@babel/traverse': 7.18.6 - '@babel/types': 7.21.2 + '@babel/types': 7.18.7 '@jest/expect-utils': 29.4.1 '@jest/transform': 29.4.1 '@jest/types': 29.4.1 @@ -15368,7 +15394,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 18.11.10 + '@types/node': 17.0.45 chalk: 4.1.2 ci-info: 3.3.2 graceful-fs: 4.2.10 @@ -15441,7 +15467,7 @@ packages: dependencies: '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 18.11.10 + '@types/node': 17.0.45 ansi-escapes: 4.3.2 chalk: 4.1.2 jest-util: 27.5.1 @@ -18591,6 +18617,14 @@ packages: optionalDependencies: fsevents: 2.3.2 + /rollup@3.24.0: + resolution: {integrity: sha512-OgraHOIg2YpHQTjl0/ymWfFNBEyPucB7lmhXrQUh38qNOegxLapSPFs9sNr0qKR75awW41D93XafoR2QfhBdUQ==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + /run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} @@ -19934,7 +19968,7 @@ packages: - '@types/node' dev: false - /ts-node@10.8.2(@types/node@18.11.10)(typescript@4.9.4): + /ts-node@10.8.2(@types/node@17.0.45)(typescript@4.9.4): resolution: {integrity: sha512-LYdGnoGddf1D6v8REPtIH+5iq/gTDuZqv2/UJUU7tKjuEU8xVZorBM+buCGNjj+pGEud+sOoM4CX3/YzINpENA==} hasBin: true peerDependencies: @@ -19953,7 +19987,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.3 - '@types/node': 18.11.10 + '@types/node': 17.0.45 acorn: 8.8.2 acorn-walk: 8.2.0 arg: 4.1.3 @@ -21070,7 +21104,7 @@ packages: dependencies: '@types/node': 17.0.45 esbuild: 0.15.15 - postcss: 8.4.19 + postcss: 8.4.21 resolve: 1.22.1 rollup: 2.79.1 sass: 1.53.0 @@ -21078,6 +21112,40 @@ packages: optionalDependencies: fsevents: 2.3.2 + /vite@4.0.4(@types/node@17.0.45): + resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 17.0.45 + esbuild: 0.16.17 + postcss: 8.4.21 + resolve: 1.22.1 + rollup: 3.24.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /vitest@0.29.8(terser@5.14.1): resolution: {integrity: sha512-JIAVi2GK5cvA6awGpH0HvH/gEG9PZ0a/WoxdiV3PmqK+3CjQMf8c+J/Vhv4mdZ2nRyXFw66sAg6qz7VNkaHfDQ==} engines: {node: '>=v14.16.0'} @@ -21669,8 +21737,8 @@ packages: '@webassemblyjs/ast': 1.11.1 '@webassemblyjs/wasm-edit': 1.11.1 '@webassemblyjs/wasm-parser': 1.11.1 - acorn: 8.8.2 - acorn-import-assertions: 1.8.0(acorn@8.8.2) + acorn: 8.8.0 + acorn-import-assertions: 1.8.0(acorn@8.8.0) browserslist: 4.21.1 chrome-trace-event: 1.0.3 enhanced-resolve: 5.12.0 From 331d482b22ef6210a3184f6ab359478507b8b5ca Mon Sep 17 00:00:00 2001 From: Joel Jacob Stephen <70131076+JoelJacobStephen@users.noreply.github.com> Date: Fri, 16 Jun 2023 07:17:00 +0300 Subject: [PATCH 02/30] feat: introducing i18n support to admin dashboard (#3051) --- packages/hoppscotch-sh-admin/.dockerignore | 1 + packages/hoppscotch-sh-admin/languages.json | 195 ++++++++++++++++++ packages/hoppscotch-sh-admin/locales/af.json | 3 + packages/hoppscotch-sh-admin/locales/ar.json | 3 + packages/hoppscotch-sh-admin/locales/ca.json | 3 + packages/hoppscotch-sh-admin/locales/cn.json | 3 + packages/hoppscotch-sh-admin/locales/cs.json | 3 + packages/hoppscotch-sh-admin/locales/da.json | 3 + packages/hoppscotch-sh-admin/locales/de.json | 3 + packages/hoppscotch-sh-admin/locales/el.json | 3 + packages/hoppscotch-sh-admin/locales/en.json | 132 ++++++++++++ packages/hoppscotch-sh-admin/locales/es.json | 3 + packages/hoppscotch-sh-admin/locales/fi.json | 3 + packages/hoppscotch-sh-admin/locales/fr.json | 3 + packages/hoppscotch-sh-admin/locales/he.json | 3 + packages/hoppscotch-sh-admin/locales/hi.json | 3 + packages/hoppscotch-sh-admin/locales/hu.json | 3 + packages/hoppscotch-sh-admin/locales/id.json | 3 + packages/hoppscotch-sh-admin/locales/it.json | 3 + packages/hoppscotch-sh-admin/locales/ja.json | 3 + packages/hoppscotch-sh-admin/locales/ko.json | 3 + packages/hoppscotch-sh-admin/locales/nl.json | 3 + packages/hoppscotch-sh-admin/locales/no.json | 3 + packages/hoppscotch-sh-admin/locales/pl.json | 3 + .../hoppscotch-sh-admin/locales/pt-br.json | 3 + packages/hoppscotch-sh-admin/locales/pt.json | 3 + packages/hoppscotch-sh-admin/locales/ro.json | 3 + packages/hoppscotch-sh-admin/locales/ru.json | 3 + packages/hoppscotch-sh-admin/locales/sr.json | 3 + packages/hoppscotch-sh-admin/locales/sv.json | 3 + packages/hoppscotch-sh-admin/locales/tr.json | 3 + packages/hoppscotch-sh-admin/locales/tw.json | 3 + packages/hoppscotch-sh-admin/locales/uk.json | 3 + packages/hoppscotch-sh-admin/locales/vi.json | 3 + packages/hoppscotch-sh-admin/package.json | 4 +- .../hoppscotch-sh-admin/src/components.d.ts | 8 - .../src/components/app/Header.vue | 25 ++- .../src/components/app/Login.vue | 15 +- .../src/components/app/Logout.vue | 11 +- .../src/components/app/Sidebar.vue | 17 +- .../src/components/teams/Add.vue | 22 +- .../src/components/teams/Details.vue | 25 ++- .../src/components/teams/Invite.vue | 20 +- .../src/components/teams/Members.vue | 31 +-- .../src/components/teams/PendingInvites.vue | 15 +- .../src/components/users/InviteModal.vue | 11 +- .../src/components/users/Table.vue | 17 +- .../src/composables/i18n.ts | 6 + .../hoppscotch-sh-admin/src/helpers/error.ts | 3 + .../hoppscotch-sh-admin/src/modules/i18n.ts | 154 ++++++++++++++ .../src/pages/dashboard.vue | 17 +- .../src/pages/teams/_id.vue | 31 +-- .../src/pages/teams/index.vue | 33 +-- .../src/pages/users/_id.vue | 63 +++--- .../src/pages/users/index.vue | 51 ++--- .../src/pages/users/invited.vue | 19 +- packages/hoppscotch-sh-admin/vite.config.ts | 6 + pnpm-lock.yaml | 64 +++++- 58 files changed, 905 insertions(+), 184 deletions(-) create mode 100644 packages/hoppscotch-sh-admin/.dockerignore create mode 100644 packages/hoppscotch-sh-admin/languages.json create mode 100644 packages/hoppscotch-sh-admin/locales/af.json create mode 100644 packages/hoppscotch-sh-admin/locales/ar.json create mode 100644 packages/hoppscotch-sh-admin/locales/ca.json create mode 100644 packages/hoppscotch-sh-admin/locales/cn.json create mode 100644 packages/hoppscotch-sh-admin/locales/cs.json create mode 100644 packages/hoppscotch-sh-admin/locales/da.json create mode 100644 packages/hoppscotch-sh-admin/locales/de.json create mode 100644 packages/hoppscotch-sh-admin/locales/el.json create mode 100644 packages/hoppscotch-sh-admin/locales/en.json create mode 100644 packages/hoppscotch-sh-admin/locales/es.json create mode 100644 packages/hoppscotch-sh-admin/locales/fi.json create mode 100644 packages/hoppscotch-sh-admin/locales/fr.json create mode 100644 packages/hoppscotch-sh-admin/locales/he.json create mode 100644 packages/hoppscotch-sh-admin/locales/hi.json create mode 100644 packages/hoppscotch-sh-admin/locales/hu.json create mode 100644 packages/hoppscotch-sh-admin/locales/id.json create mode 100644 packages/hoppscotch-sh-admin/locales/it.json create mode 100644 packages/hoppscotch-sh-admin/locales/ja.json create mode 100644 packages/hoppscotch-sh-admin/locales/ko.json create mode 100644 packages/hoppscotch-sh-admin/locales/nl.json create mode 100644 packages/hoppscotch-sh-admin/locales/no.json create mode 100644 packages/hoppscotch-sh-admin/locales/pl.json create mode 100644 packages/hoppscotch-sh-admin/locales/pt-br.json create mode 100644 packages/hoppscotch-sh-admin/locales/pt.json create mode 100644 packages/hoppscotch-sh-admin/locales/ro.json create mode 100644 packages/hoppscotch-sh-admin/locales/ru.json create mode 100644 packages/hoppscotch-sh-admin/locales/sr.json create mode 100644 packages/hoppscotch-sh-admin/locales/sv.json create mode 100644 packages/hoppscotch-sh-admin/locales/tr.json create mode 100644 packages/hoppscotch-sh-admin/locales/tw.json create mode 100644 packages/hoppscotch-sh-admin/locales/uk.json create mode 100644 packages/hoppscotch-sh-admin/locales/vi.json create mode 100644 packages/hoppscotch-sh-admin/src/composables/i18n.ts create mode 100644 packages/hoppscotch-sh-admin/src/helpers/error.ts create mode 100644 packages/hoppscotch-sh-admin/src/modules/i18n.ts diff --git a/packages/hoppscotch-sh-admin/.dockerignore b/packages/hoppscotch-sh-admin/.dockerignore new file mode 100644 index 000000000..4b904442b --- /dev/null +++ b/packages/hoppscotch-sh-admin/.dockerignore @@ -0,0 +1 @@ +./node_modules diff --git a/packages/hoppscotch-sh-admin/languages.json b/packages/hoppscotch-sh-admin/languages.json new file mode 100644 index 000000000..870bc07bf --- /dev/null +++ b/packages/hoppscotch-sh-admin/languages.json @@ -0,0 +1,195 @@ +[ + { + "code": "af", + "file": "af.json", + "iso": "af-AF", + "name": "Afrikaans" + }, + { + "code": "ar", + "dir": "rtl", + "file": "ar.json", + "iso": "ar-AR", + "name": "عربى" + }, + { + "code": "ca", + "file": "ca.json", + "iso": "ca-CA", + "name": "Català" + }, + { + "code": "cn", + "file": "cn.json", + "iso": "zh-CN", + "name": "简体中文" + }, + { + "code": "cs", + "file": "cs.json", + "iso": "cs-CS", + "name": "Čeština" + }, + { + "code": "da", + "file": "da.json", + "iso": "da-DA", + "name": "Dansk" + }, + { + "code": "de", + "file": "de.json", + "iso": "de-DE", + "name": "Deutsch" + }, + { + "code": "el", + "file": "el.json", + "iso": "el-EL", + "name": "Ελληνικά" + }, + { + "code": "en", + "file": "en.json", + "iso": "en-US", + "name": "English" + }, + { + "code": "es", + "file": "es.json", + "iso": "es-ES", + "name": "Español" + }, + { + "code": "fi", + "file": "fi.json", + "iso": "fi-FI", + "name": "Suomalainen" + }, + { + "code": "fr", + "file": "fr.json", + "iso": "fr-FR", + "name": "Français" + }, + { + "code": "he", + "file": "he.json", + "iso": "he-HE", + "name": "עִברִית" + }, + { + "code": "hi", + "file": "hi.json", + "iso": "hi-HI", + "name": "हिन्दी" + }, + { + "code": "hu", + "file": "hu.json", + "iso": "hu-HU", + "name": "Magyar" + }, + { + "code": "id", + "file": "id.json", + "iso": "id", + "name": "Indonesian" + }, + { + "code": "it", + "file": "it.json", + "iso": "it", + "name": "Italiano" + }, + { + "code": "ja", + "file": "ja.json", + "iso": "ja-JA", + "name": "日本語" + }, + { + "code": "ko", + "file": "ko.json", + "iso": "ko-KO", + "name": "한국어" + }, + { + "code": "nl", + "file": "nl.json", + "iso": "nl-NL", + "name": "Nederlands" + }, + { + "code": "no", + "file": "no.json", + "iso": "no-NO", + "name": "Norsk" + }, + { + "code": "pl", + "file": "pl.json", + "iso": "pl-PL", + "name": "Polskie" + }, + { + "code": "pt-br", + "file": "pt-br.json", + "iso": "pt-BR", + "name": "Português Brasileiro" + }, + { + "code": "pt", + "file": "pt.json", + "iso": "pt-PT", + "name": "Português" + }, + { + "code": "ro", + "file": "ro.json", + "iso": "ro-RO", + "name": "Română" + }, + { + "code": "ru", + "file": "ru.json", + "iso": "ru-RU", + "name": "Pусский" + }, + { + "code": "sr", + "file": "sr.json", + "iso": "sr-SR", + "name": "Српски" + }, + { + "code": "sv", + "file": "sv.json", + "iso": "sv-SV", + "name": "Svenska" + }, + { + "code": "tr", + "file": "tr.json", + "iso": "tr-TR", + "name": "Türkçe" + }, + { + "code": "tw", + "file": "tw.json", + "iso": "zh-TW", + "name": "繁體中文" + }, + { + "code": "uk", + "file": "uk.json", + "iso": "uk-UK", + "name": "Українська" + }, + { + "code": "vi", + "file": "vi.json", + "iso": "vi-VI", + "name": "Tiếng Việt" + } +] diff --git a/packages/hoppscotch-sh-admin/locales/af.json b/packages/hoppscotch-sh-admin/locales/af.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/af.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/ar.json b/packages/hoppscotch-sh-admin/locales/ar.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/ar.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/ca.json b/packages/hoppscotch-sh-admin/locales/ca.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/ca.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/cn.json b/packages/hoppscotch-sh-admin/locales/cn.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/cn.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/cs.json b/packages/hoppscotch-sh-admin/locales/cs.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/cs.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/da.json b/packages/hoppscotch-sh-admin/locales/da.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/da.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/de.json b/packages/hoppscotch-sh-admin/locales/de.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/de.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/el.json b/packages/hoppscotch-sh-admin/locales/el.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/el.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/en.json b/packages/hoppscotch-sh-admin/locales/en.json new file mode 100644 index 000000000..a955b839a --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/en.json @@ -0,0 +1,132 @@ +{ + "app": { + "collapse_sidebar": "Collapse Sidebar", + "expand_sidebar": "Expand Sidebar", + "name": "HOPPSCOTCH", + "no_name": "No name", + "open_navigation": "Open Navigation" + }, + "metrics": { + "dashboard": "Dashboard", + "no_metrics": "No metrics found", + "total_collections": "Total Collections", + "total_requests": "Total Requests", + "total_teams": "Total Teams", + "total_users": "Total Users" + }, + "role": { + "editor": "EDITOR", + "owner": "OWNER", + "viewer": "VIEWER" + }, + "state": { + "add_user_failure": "Failed to add user to the team!!", + "add_user_success": "User is now a member of the team!!", + "admin_failure": "Failed to make user an admin!!", + "admin_success": "User is now an admin!!", + "confirm_logout": "Confirm Logout", + "create_team_failure": "Failed to create team!!", + "create_team_success": "Team created successfully!!", + "delete_team_failure": "Team deletion failed!!", + "delete_team_success": "Team deleted successfully!!", + "delete_user_failure": "User deletion failed!!", + "delete_user_success": "User deleted successfully!!", + "email_failure": "Failed to send invitation", + "email_success": "Email invitation sent successfully", + "enter_team_email": "Please enter email of team owner!!", + "error": "Something went wrong", + "github_signin_failure": "Failed to login with Github", + "google_signin_failure": "Failed to login with Google", + "invalid_email": "Please enter a valid email address", + "logged_out": "Logged out", + "logout": "Logout", + "non_admin_login": "You are logged in. But you're not an admin", + "remove_admin_failure": "Failed to remove admin status!!", + "remove_admin_success": "Admin status removed!!", + "remove_admin_to_delete_user": "Remove admin privilege to delete the user!!", + "remove_invitee_failure": "Removal of invitee failed!!", + "remove_invitee_success": "Removal of invitee is successfull!!", + "remove_member_failure": "Member couldn't be removed!!", + "remove_member_success": "Member removed successfully!!", + "rename_team_failure": "Failed to rename team!!", + "rename_team_success": "Team renamed successfully!", + "role_update_failed": "Roles updation has failed!!", + "role_update_success": "Roles updated successfully!!", + "team_name_long": "Team name should be atleast 6 characters long!!", + "user_not_found": "User not found in the infra!!" + }, + "teams": { + "add_members": "Add Members", + "admin": "Admin", + "admin_Email": "Admin Email", + "admin_id": "Admin ID", + "cancel": "Cancel", + "confirm_team_deletion": "Confirm Deletion of the team?", + "create_team": "Create team", + "date": "Date", + "delete_team": "Delete Team", + "details": "Details", + "edit": "Edit", + "email": "Team owner email", + "email_address": "Email Address", + "error": "Something went wrong. Please try again later.", + "id": "Team ID", + "invited_email": "Invitee Email", + "invited_on": "Invited On", + "invites": "Invites", + "load_info_error": "Unable to load team info", + "load_list_error": "Unable to Load Teams List", + "members": "Number of members", + "name": "Team name", + "no_members": "No members in this team. Add members to this team to collaborate", + "no_pending_invites": "No pending invites", + "pending_invites": "Pending invites", + "remove": "Remove", + "rename": "Rename", + "save": "Save", + "send_invite": "Send Invite", + "show_more": "Show more", + "team_details": "Team details", + "team_members": "Members", + "team_members_tab": "Team members", + "teams": "Teams", + "uid": "UID", + "valid_name": "Please enter a valid team name", + "valid_owner_email": "Please enter a valid owner email" + }, + "users": { + "admin": "Admin", + "admin_email": "Admin Email", + "admin_id": "Admin ID", + "confirm_admin_to_user": "Do you want to remove admin status from this user?", + "confirm_user_deletion": "Confirm user deletion?", + "confirm_user_to_admin": "Do you want to make this user into an admin?", + "created_on": "Created On", + "date": "Date", + "delete": "Delete", + "email": "Email", + "email_address": "Email Address", + "id": "User ID", + "invite_user": "Invite User", + "invited_on": "Invited On", + "invitee_email": "Invitee Email", + "invited_users": "Invited Users", + "invalid_user": "Invalid User", + "load_info_error": "Unable to load user info", + "load_list_error": "Unable to Load Users List", + "make_admin": "Make admin", + "name": "Name", + "no_invite": "No invited users found", + "no_users": "No users found", + "not_found": "User not found", + "remove_admin_privilege": "Remove Admin Privilege", + "remove_admin_status": "Remove Admin Status", + "send_invite": "Send Invite", + "show_more": "Show more", + "uid": "UID", + "unnamed": "(Unnamed User)", + "user_not_found": "User not found in the infra!!", + "users": "Users", + "valid_email": "Please enter a valid email address" + } +} diff --git a/packages/hoppscotch-sh-admin/locales/es.json b/packages/hoppscotch-sh-admin/locales/es.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/es.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/fi.json b/packages/hoppscotch-sh-admin/locales/fi.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/fi.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/fr.json b/packages/hoppscotch-sh-admin/locales/fr.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/fr.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/he.json b/packages/hoppscotch-sh-admin/locales/he.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/he.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/hi.json b/packages/hoppscotch-sh-admin/locales/hi.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/hi.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/hu.json b/packages/hoppscotch-sh-admin/locales/hu.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/hu.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/id.json b/packages/hoppscotch-sh-admin/locales/id.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/id.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/it.json b/packages/hoppscotch-sh-admin/locales/it.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/it.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/ja.json b/packages/hoppscotch-sh-admin/locales/ja.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/ja.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/ko.json b/packages/hoppscotch-sh-admin/locales/ko.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/ko.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/nl.json b/packages/hoppscotch-sh-admin/locales/nl.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/nl.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/no.json b/packages/hoppscotch-sh-admin/locales/no.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/no.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/pl.json b/packages/hoppscotch-sh-admin/locales/pl.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/pl.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/pt-br.json b/packages/hoppscotch-sh-admin/locales/pt-br.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/pt-br.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/pt.json b/packages/hoppscotch-sh-admin/locales/pt.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/pt.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/ro.json b/packages/hoppscotch-sh-admin/locales/ro.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/ro.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/ru.json b/packages/hoppscotch-sh-admin/locales/ru.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/ru.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/sr.json b/packages/hoppscotch-sh-admin/locales/sr.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/sr.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/sv.json b/packages/hoppscotch-sh-admin/locales/sv.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/sv.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/tr.json b/packages/hoppscotch-sh-admin/locales/tr.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/tr.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/tw.json b/packages/hoppscotch-sh-admin/locales/tw.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/tw.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/uk.json b/packages/hoppscotch-sh-admin/locales/uk.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/uk.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/locales/vi.json b/packages/hoppscotch-sh-admin/locales/vi.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/packages/hoppscotch-sh-admin/locales/vi.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/packages/hoppscotch-sh-admin/package.json b/packages/hoppscotch-sh-admin/package.json index 3e192ea11..fb0e9b5ce 100644 --- a/packages/hoppscotch-sh-admin/package.json +++ b/packages/hoppscotch-sh-admin/package.json @@ -30,12 +30,13 @@ "io-ts": "^2.2.16", "lodash-es": "^4.17.21", "rxjs": "^7.8.0", + "tippy.js": "^6.3.7", "ts-node-dev": "^2.0.0", "unplugin-icons": "^0.14.9", "unplugin-vue-components": "^0.21.0", "vue": "^3.2.6", + "vue-i18n": "^9.2.2", "vue-router": "4", - "tippy.js": "^6.3.7", "vue-tippy": "6.0.0-alpha.58" }, "devDependencies": { @@ -47,6 +48,7 @@ "@graphql-codegen/typescript-document-nodes": "3.0.0", "@graphql-codegen/typescript-operations": "3.0.0", "@graphql-codegen/urql-introspection": "2.2.1", + "@intlify/vite-plugin-vue-i18n": "^7.0.0", "@vitejs/plugin-vue": "^3.1.0", "@vue/compiler-sfc": "^3.2.6", "graphql-tag": "^2.12.6", diff --git a/packages/hoppscotch-sh-admin/src/components.d.ts b/packages/hoppscotch-sh-admin/src/components.d.ts index 472fa9266..c55f69a7e 100644 --- a/packages/hoppscotch-sh-admin/src/components.d.ts +++ b/packages/hoppscotch-sh-admin/src/components.d.ts @@ -14,14 +14,6 @@ declare module '@vue/runtime-core' { 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'] - HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal'] - HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem'] - IconLucideInbox: typeof import('~icons/lucide/inbox')['default'] - IconLucideUser: typeof import('~icons/lucide/user')['default'] - ProfilePicture: typeof import('./components/profile/Picture.vue')['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'] diff --git a/packages/hoppscotch-sh-admin/src/components/app/Header.vue b/packages/hoppscotch-sh-admin/src/components/app/Header.vue index 8d42cdb4f..b0280daff 100644 --- a/packages/hoppscotch-sh-admin/src/components/app/Header.vue +++ b/packages/hoppscotch-sh-admin/src/components/app/Header.vue @@ -5,14 +5,18 @@
diff --git a/packages/hoppscotch-common/src/components/app/spotlight/Entry.vue b/packages/hoppscotch-common/src/components/app/spotlight/Entry.vue new file mode 100644 index 000000000..1d0593ba9 --- /dev/null +++ b/packages/hoppscotch-common/src/components/app/spotlight/Entry.vue @@ -0,0 +1,122 @@ + + + + + + + diff --git a/packages/hoppscotch-common/src/components/app/spotlight/entry/GQLHistory.vue b/packages/hoppscotch-common/src/components/app/spotlight/entry/GQLHistory.vue new file mode 100644 index 000000000..b1a845dbb --- /dev/null +++ b/packages/hoppscotch-common/src/components/app/spotlight/entry/GQLHistory.vue @@ -0,0 +1,30 @@ + + + diff --git a/packages/hoppscotch-common/src/components/app/spotlight/entry/RESTHistory.vue b/packages/hoppscotch-common/src/components/app/spotlight/entry/RESTHistory.vue new file mode 100644 index 000000000..da1d8d21e --- /dev/null +++ b/packages/hoppscotch-common/src/components/app/spotlight/entry/RESTHistory.vue @@ -0,0 +1,43 @@ + + + diff --git a/packages/hoppscotch-common/src/components/app/spotlight/index.vue b/packages/hoppscotch-common/src/components/app/spotlight/index.vue new file mode 100644 index 000000000..cebd053f7 --- /dev/null +++ b/packages/hoppscotch-common/src/components/app/spotlight/index.vue @@ -0,0 +1,238 @@ + + + diff --git a/packages/hoppscotch-common/src/components/firebase/Logout.vue b/packages/hoppscotch-common/src/components/firebase/Logout.vue index 8da86d6ad..58c5e2779 100644 --- a/packages/hoppscotch-common/src/components/firebase/Logout.vue +++ b/packages/hoppscotch-common/src/components/firebase/Logout.vue @@ -1,12 +1,12 @@ + + + Date: Mon, 17 Jul 2023 14:39:32 +0530 Subject: [PATCH 22/30] chore: update generateSitemap usage (#3182) --- packages/hoppscotch-common/package.json | 2 +- packages/hoppscotch-selfhost-web/vite.config.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/hoppscotch-common/package.json b/packages/hoppscotch-common/package.json index 5986de983..bd998020e 100644 --- a/packages/hoppscotch-common/package.json +++ b/packages/hoppscotch-common/package.json @@ -148,7 +148,7 @@ "vite-plugin-html-config": "^1.0.10", "vite-plugin-inspect": "^0.7.4", "vite-plugin-pages": "^0.26.0", - "vite-plugin-pages-sitemap": "^1.4.0", + "vite-plugin-pages-sitemap": "^1.4.5", "vite-plugin-pwa": "^0.13.1", "vite-plugin-vue-layouts": "^0.7.0", "vite-plugin-windicss": "^1.8.8", diff --git a/packages/hoppscotch-selfhost-web/vite.config.ts b/packages/hoppscotch-selfhost-web/vite.config.ts index 1ab48152c..14c1a50a4 100644 --- a/packages/hoppscotch-selfhost-web/vite.config.ts +++ b/packages/hoppscotch-selfhost-web/vite.config.ts @@ -79,8 +79,7 @@ export default defineConfig({ dirs: "../hoppscotch-common/src/pages", importMode: "async", onRoutesGenerated(routes) { - // HACK: See: https://github.com/jbaubree/vite-plugin-pages-sitemap/issues/173 - return ((generateSitemap as any).default as typeof generateSitemap)({ + return generateSitemap({ routes, nuxtStyle: true, allowRobots: true, From 8e153b38dc3dd0bfc87389a371d9acfac720e8cb Mon Sep 17 00:00:00 2001 From: Liyas Thomas Date: Mon, 17 Jul 2023 14:40:14 +0530 Subject: [PATCH 23/30] redesigned search button (#3187) --- packages/hoppscotch-common/package.json | 1 - .../hoppscotch-common/src/components.d.ts | 2 + .../src/components/app/Header.vue | 26 +- pnpm-lock.yaml | 601 +++++++++++++++++- 4 files changed, 589 insertions(+), 41 deletions(-) diff --git a/packages/hoppscotch-common/package.json b/packages/hoppscotch-common/package.json index bd998020e..fa8c73d27 100644 --- a/packages/hoppscotch-common/package.json +++ b/packages/hoppscotch-common/package.json @@ -89,7 +89,6 @@ "util": "^0.12.4", "uuid": "^8.3.2", "vue": "^3.2.25", - "vue-github-button": "^3.0.3", "vue-i18n": "^9.2.2", "vue-pdf-embed": "^1.1.4", "vue-router": "^4.0.16", diff --git a/packages/hoppscotch-common/src/components.d.ts b/packages/hoppscotch-common/src/components.d.ts index f9eef9d75..a3e0fbf33 100644 --- a/packages/hoppscotch-common/src/components.d.ts +++ b/packages/hoppscotch-common/src/components.d.ts @@ -94,6 +94,7 @@ declare module '@vue/runtime-core' { HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner'] HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab'] HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs'] + HoppSmartToggle: typeof import('@hoppscotch/ui')['HoppSmartToggle'] HoppSmartWindow: typeof import('@hoppscotch/ui')['HoppSmartWindow'] HoppSmartWindows: typeof import('@hoppscotch/ui')['HoppSmartWindows'] HttpAuthorization: typeof import('./components/http/Authorization.vue')['default'] @@ -132,6 +133,7 @@ declare module '@vue/runtime-core' { 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'] LensesHeadersRenderer: typeof import('./components/lenses/HeadersRenderer.vue')['default'] diff --git a/packages/hoppscotch-common/src/components/app/Header.vue b/packages/hoppscotch-common/src/components/app/Header.vue index 116b511ad..90c567d9a 100644 --- a/packages/hoppscotch-common/src/components/app/Header.vue +++ b/packages/hoppscotch-common/src/components/app/Header.vue @@ -15,18 +15,21 @@ :label="t('app.name')" to="/" /> -
-
- + +
+
= 14.6'} deprecated: This plugin support until Vite 3. If you would like to use on Vite 4, please use @intlify/unplugin-vue-i18n @@ -5997,6 +6020,33 @@ packages: fast-glob: 3.2.12 source-map: 0.6.1 vite: 3.2.4(@types/node@17.0.45)(sass@1.53.0)(terser@5.14.1) + transitivePeerDependencies: + - supports-color + dev: true + + /@intlify/vite-plugin-vue-i18n@7.0.0(vite@3.2.4)(vue-i18n@9.2.2): + resolution: {integrity: sha512-2TbDOQ8XD+vkc0s5OFmr+IY/k4mYMC7pzvx0xGQn+cU/ev314+yi7Z7N7rWcBgiYk1WOUalbGSo3d4nJDxOOyw==} + engines: {node: '>= 14.6'} + deprecated: This plugin support until Vite 3. If you would like to use on Vite 4, please use @intlify/unplugin-vue-i18n + peerDependencies: + petite-vue-i18n: '*' + vite: ^2.9.0 || ^3.0.0 + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vite: + optional: true + vue-i18n: + optional: true + dependencies: + '@intlify/bundle-utils': 3.4.0(vue-i18n@9.2.2) + '@intlify/shared': 9.3.0-beta.24 + '@rollup/pluginutils': 4.2.1 + debug: 4.3.4(supports-color@9.2.2) + fast-glob: 3.2.12 + source-map: 0.6.1 + vite: 3.2.4(@types/node@18.16.17)(sass@1.58.0) vue-i18n: 9.2.2(vue@3.2.45) transitivePeerDependencies: - supports-color @@ -6754,7 +6804,7 @@ packages: tsconfig-paths: 4.1.0 tsconfig-paths-webpack-plugin: 4.0.0 typescript: 4.8.4 - webpack: 5.74.0(esbuild@0.17.19) + webpack: 5.74.0 webpack-node-externals: 3.0.0 transitivePeerDependencies: - '@swc/core' @@ -6926,6 +6976,21 @@ packages: - chokidar dev: true + /@nestjs/schematics@9.0.3(typescript@4.9.3): + resolution: {integrity: sha512-kZrU/lrpVd2cnK8I3ibDb3Wi1ppl3wX3U3lVWoL+DzRRoezWKkh8upEL4q0koKmuXnsmLiu3UPxFeMOrJV7TSA==} + peerDependencies: + typescript: ^4.3.5 + dependencies: + '@angular-devkit/core': 14.2.1(chokidar@3.5.3) + '@angular-devkit/schematics': 14.2.1(chokidar@3.5.3) + fs-extra: 10.1.0 + jsonc-parser: 3.2.0 + pluralize: 8.0.0 + typescript: 4.9.3 + transitivePeerDependencies: + - chokidar + dev: true + /@nestjs/testing@9.2.1(@nestjs/common@9.2.1)(@nestjs/core@9.2.1)(@nestjs/platform-express@9.2.1): resolution: {integrity: sha512-lemXZdRSuqoZ87l0orCrS/c7gqwxeduIFOd21g9g2RUeQ4qlWPegbQDKASzbfC28klPyrgJLW4MNq7uv2JwV8w==} peerDependencies: @@ -7685,6 +7750,7 @@ 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==} @@ -8838,6 +8904,7 @@ packages: '@vue/shared': 3.2.39 estree-walker: 2.0.2 source-map: 0.6.1 + dev: true /@vue/compiler-core@3.2.45: resolution: {integrity: sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==} @@ -8867,6 +8934,7 @@ packages: dependencies: '@vue/compiler-core': 3.2.39 '@vue/shared': 3.2.39 + dev: true /@vue/compiler-dom@3.2.45: resolution: {integrity: sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==} @@ -8916,6 +8984,7 @@ packages: magic-string: 0.25.9 postcss: 8.4.16 source-map: 0.6.1 + dev: true /@vue/compiler-sfc@3.2.45: resolution: {integrity: sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==} @@ -8942,6 +9011,7 @@ packages: dependencies: '@vue/compiler-dom': 3.2.39 '@vue/shared': 3.2.39 + dev: true /@vue/compiler-ssr@3.2.45: resolution: {integrity: sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==} @@ -9011,6 +9081,7 @@ packages: '@vue/shared': 3.2.39 estree-walker: 2.0.2 magic-string: 0.25.9 + dev: true /@vue/reactivity-transform@3.2.45: resolution: {integrity: sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==} @@ -9093,6 +9164,7 @@ packages: /@vue/shared@3.2.39: resolution: {integrity: sha512-D3dl2ZB9qE6mTuWPk9RlhDeP1dgNRUKC3NJxji74A4yL8M2MwlhLKUC/49WHjrNzSPug58fWx/yFbaTzGAQSBw==} + dev: true /@vue/shared@3.2.45: resolution: {integrity: sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==} @@ -9221,15 +9293,19 @@ packages: dependencies: '@webassemblyjs/helper-numbers': 1.11.1 '@webassemblyjs/helper-wasm-bytecode': 1.11.1 + dev: true /@webassemblyjs/floating-point-hex-parser@1.11.1: resolution: {integrity: sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==} + dev: true /@webassemblyjs/helper-api-error@1.11.1: resolution: {integrity: sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==} + dev: true /@webassemblyjs/helper-buffer@1.11.1: resolution: {integrity: sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==} + dev: true /@webassemblyjs/helper-numbers@1.11.1: resolution: {integrity: sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==} @@ -9237,9 +9313,11 @@ packages: '@webassemblyjs/floating-point-hex-parser': 1.11.1 '@webassemblyjs/helper-api-error': 1.11.1 '@xtuc/long': 4.2.2 + dev: true /@webassemblyjs/helper-wasm-bytecode@1.11.1: resolution: {integrity: sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==} + dev: true /@webassemblyjs/helper-wasm-section@1.11.1: resolution: {integrity: sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==} @@ -9248,19 +9326,23 @@ packages: '@webassemblyjs/helper-buffer': 1.11.1 '@webassemblyjs/helper-wasm-bytecode': 1.11.1 '@webassemblyjs/wasm-gen': 1.11.1 + dev: true /@webassemblyjs/ieee754@1.11.1: resolution: {integrity: sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==} dependencies: '@xtuc/ieee754': 1.2.0 + dev: true /@webassemblyjs/leb128@1.11.1: resolution: {integrity: sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==} dependencies: '@xtuc/long': 4.2.2 + dev: true /@webassemblyjs/utf8@1.11.1: resolution: {integrity: sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==} + dev: true /@webassemblyjs/wasm-edit@1.11.1: resolution: {integrity: sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==} @@ -9273,6 +9355,7 @@ packages: '@webassemblyjs/wasm-opt': 1.11.1 '@webassemblyjs/wasm-parser': 1.11.1 '@webassemblyjs/wast-printer': 1.11.1 + dev: true /@webassemblyjs/wasm-gen@1.11.1: resolution: {integrity: sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==} @@ -9282,6 +9365,7 @@ packages: '@webassemblyjs/ieee754': 1.11.1 '@webassemblyjs/leb128': 1.11.1 '@webassemblyjs/utf8': 1.11.1 + dev: true /@webassemblyjs/wasm-opt@1.11.1: resolution: {integrity: sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==} @@ -9290,6 +9374,7 @@ packages: '@webassemblyjs/helper-buffer': 1.11.1 '@webassemblyjs/wasm-gen': 1.11.1 '@webassemblyjs/wasm-parser': 1.11.1 + dev: true /@webassemblyjs/wasm-parser@1.11.1: resolution: {integrity: sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==} @@ -9300,12 +9385,14 @@ packages: '@webassemblyjs/ieee754': 1.11.1 '@webassemblyjs/leb128': 1.11.1 '@webassemblyjs/utf8': 1.11.1 + dev: true /@webassemblyjs/wast-printer@1.11.1: resolution: {integrity: sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==} dependencies: '@webassemblyjs/ast': 1.11.1 '@xtuc/long': 4.2.2 + dev: true /@wessberg/stringutil@1.0.19: resolution: {integrity: sha512-9AZHVXWlpN8Cn9k5BC/O0Dzb9E9xfEMXzYrNunwvkUTvuK7xgQPVRZpLo+jWCOZ5r8oBa8NIrHuPEu1hzbb6bg==} @@ -9390,9 +9477,11 @@ 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==} @@ -9442,6 +9531,7 @@ packages: acorn: ^8 dependencies: acorn: 8.8.0 + dev: true /acorn-jsx@5.3.2(acorn@7.4.1): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -9509,7 +9599,7 @@ packages: hasBin: true /after@0.8.2: - resolution: {integrity: sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=} + resolution: {integrity: sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA==} dev: false /agent-base@6.0.2: @@ -9555,6 +9645,7 @@ packages: ajv: ^6.9.1 dependencies: ajv: 6.12.6 + dev: true /ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -10121,7 +10212,7 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} /base64-arraybuffer@0.1.4: - resolution: {integrity: sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=} + resolution: {integrity: sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg==} engines: {node: '>= 0.6.0'} dev: false @@ -10268,6 +10359,7 @@ packages: electron-to-chromium: 1.4.178 node-releases: 2.0.5 update-browserslist-db: 1.0.4(browserslist@4.21.1) + dev: true /bs-logger@0.2.6: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} @@ -10386,6 +10478,7 @@ packages: /caniuse-lite@1.0.30001363: resolution: {integrity: sha512-HpQhpzTGGPVMnCjIomjt+jvyUu8vNFo3TaDiZ/RcoTrlOq/5+tC8zHdsbgFB6MxmaY+jCpsH09aD80Bb4Ow3Sg==} + dev: true /capital-case@1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} @@ -10582,6 +10675,7 @@ 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==} @@ -10772,14 +10866,14 @@ packages: dev: true /component-bind@1.0.0: - resolution: {integrity: sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=} + resolution: {integrity: sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw==} dev: false /component-emitter@1.3.0: resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} /component-inherit@0.0.3: - resolution: {integrity: sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=} + resolution: {integrity: sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA==} dev: false /concat-map@0.0.1: @@ -11594,6 +11688,7 @@ packages: /electron-to-chromium@1.4.178: resolution: {integrity: sha512-aWuhJXkwIdoQzGR8p2QvR3N0OzdUKZSP8+P/hzuMzNQIPZoEa8HiCGM75bQBHjyz+eKT5PB9dVCzkK/tyQ4B5Q==} + dev: true /emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -11729,6 +11824,7 @@ packages: dependencies: graceful-fs: 4.2.10 tapable: 2.2.1 + dev: true /entities@2.1.0: resolution: {integrity: sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==} @@ -11792,6 +11888,7 @@ packages: /es-module-lexer@0.9.3: resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==} + dev: true /es-to-primitive@1.2.1: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} @@ -12423,6 +12520,7 @@ packages: '@esbuild/win32-arm64': 0.17.19 '@esbuild/win32-ia32': 0.17.19 '@esbuild/win32-x64': 0.17.19 + dev: true /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} @@ -12527,6 +12625,22 @@ packages: prettier-linter-helpers: 1.0.0 dev: true + /eslint-plugin-prettier@4.2.1(eslint@8.24.0)(prettier@2.8.4): + resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + eslint: '>=7.28.0' + eslint-config-prettier: '*' + prettier: '>=2.0.0' + peerDependenciesMeta: + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.24.0 + prettier: 2.8.4 + prettier-linter-helpers: 1.0.0 + dev: true + /eslint-plugin-vue@9.5.1(eslint@8.24.0): resolution: {integrity: sha512-Y0sL2RY7Xc9S8kNih9lbwHIDmewUg9bfas6WSzsOWRgDXhIHKxRBZYNAnVcXBFfE+bMWHUA5GLChl7TcTYUI8w==} engines: {node: ^14.17.0 || >=16.0.0} @@ -12569,6 +12683,7 @@ packages: dependencies: esrecurse: 4.3.0 estraverse: 4.3.0 + dev: true /eslint-scope@7.1.1: resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} @@ -13360,7 +13475,7 @@ packages: semver: 7.3.8 tapable: 2.2.1 typescript: 4.8.4 - webpack: 5.74.0(esbuild@0.17.19) + webpack: 5.74.0 dev: true /form-data-encoder@1.7.2: @@ -13644,6 +13759,7 @@ packages: /glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + dev: true /glob@7.1.6: resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} @@ -14055,7 +14171,7 @@ packages: dev: false /has-cors@1.1.0: - resolution: {integrity: sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=} + resolution: {integrity: sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA==} dev: false /has-flag@3.0.0: @@ -14448,7 +14564,7 @@ packages: engines: {node: '>=8'} /indexof@0.0.1: - resolution: {integrity: sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=} + resolution: {integrity: sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==} dev: false /inflight@1.0.6: @@ -15994,6 +16110,7 @@ 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==} @@ -16533,6 +16650,7 @@ packages: /loader-runner@4.3.0: resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} engines: {node: '>=6.11.5'} + dev: true /loader-utils@2.0.4: resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} @@ -17611,6 +17729,7 @@ packages: /node-releases@2.0.5: resolution: {integrity: sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==} + dev: true /nodemailer@6.9.1: resolution: {integrity: sha512-qHw7dOiU5UKNnQpXktdgQ1d3OFgRAekuvbJLcdG5dnEo/GtcTHRYM7+UfJARdOFU9WUQO8OiIamgWPmiSFHYAA==} @@ -18713,6 +18832,7 @@ 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==} @@ -19227,7 +19347,6 @@ packages: chokidar: 3.5.3 immutable: 4.1.0 source-map-js: 1.0.2 - dev: true /sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} @@ -19254,6 +19373,7 @@ packages: '@types/json-schema': 7.0.9 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==} @@ -19353,6 +19473,7 @@ packages: resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} dependencies: randombytes: 2.1.0 + dev: true /serve-static@1.15.0: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} @@ -20014,6 +20135,7 @@ packages: engines: {node: '>=10'} dependencies: has-flag: 4.0.0 + dev: true /supports-color@9.2.2: resolution: {integrity: sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==} @@ -20087,6 +20209,7 @@ packages: /tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} + dev: true /tar@6.1.13: resolution: {integrity: sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==} @@ -20158,6 +20281,31 @@ packages: serialize-javascript: 6.0.0 terser: 5.14.1 webpack: 5.74.0(esbuild@0.17.19) + dev: true + + /terser-webpack-plugin@5.3.6(webpack@5.74.0): + resolution: {integrity: sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + dependencies: + '@jridgewell/trace-mapping': 0.3.14 + jest-worker: 27.5.1 + schema-utils: 3.1.1 + serialize-javascript: 6.0.0 + terser: 5.14.1 + webpack: 5.74.0 + dev: true /terser@5.14.1: resolution: {integrity: sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ==} @@ -20292,7 +20440,7 @@ packages: dev: true /to-array@0.1.4: - resolution: {integrity: sha1-F+bBH3PdTz10zaek/zI46a2b+JA=} + resolution: {integrity: sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A==} dev: false /to-fast-properties@2.0.0: @@ -20466,7 +20614,7 @@ packages: micromatch: 4.0.5 semver: 7.3.8 typescript: 4.9.3 - webpack: 5.74.0(esbuild@0.17.19) + webpack: 5.74.0 dev: true /ts-log@2.2.4: @@ -20963,6 +21111,72 @@ packages: - supports-color - vite - webpack + dev: true + + /unplugin-icons@0.14.9(@vue/compiler-sfc@3.2.45)(vite@3.2.4): + resolution: {integrity: sha512-vPyVfNREH88dP6gszdaoGkAEFPpiScXj1A8eWN905jQgT53A3tsiPEiqJjCHOUVcsUaREt2JSudzumFOsCA78A==} + peerDependencies: + '@svgr/core': '>=5.5.0' + '@vue/compiler-sfc': ^3.0.2 + vue-template-compiler: ^2.6.12 + vue-template-es2015-compiler: ^1.9.0 + peerDependenciesMeta: + '@svgr/core': + optional: true + '@vue/compiler-sfc': + optional: true + vue-template-compiler: + optional: true + vue-template-es2015-compiler: + optional: true + dependencies: + '@antfu/install-pkg': 0.1.0 + '@antfu/utils': 0.5.2 + '@iconify/utils': 1.0.33 + '@vue/compiler-sfc': 3.2.45 + debug: 4.3.4(supports-color@9.2.2) + kolorist: 1.5.1 + local-pkg: 0.4.2 + unplugin: 0.9.5(vite@3.2.4) + transitivePeerDependencies: + - esbuild + - rollup + - supports-color + - vite + - webpack + dev: false + + /unplugin-icons@0.14.9(rollup@2.79.1)(vite@3.2.4): + resolution: {integrity: sha512-vPyVfNREH88dP6gszdaoGkAEFPpiScXj1A8eWN905jQgT53A3tsiPEiqJjCHOUVcsUaREt2JSudzumFOsCA78A==} + peerDependencies: + '@svgr/core': '>=5.5.0' + '@vue/compiler-sfc': ^3.0.2 + vue-template-compiler: ^2.6.12 + vue-template-es2015-compiler: ^1.9.0 + peerDependenciesMeta: + '@svgr/core': + optional: true + '@vue/compiler-sfc': + optional: true + vue-template-compiler: + optional: true + vue-template-es2015-compiler: + optional: true + dependencies: + '@antfu/install-pkg': 0.1.0 + '@antfu/utils': 0.5.2 + '@iconify/utils': 1.0.33 + debug: 4.3.4(supports-color@9.2.2) + kolorist: 1.5.1 + local-pkg: 0.4.2 + unplugin: 0.9.5(rollup@2.79.1)(vite@3.2.4) + transitivePeerDependencies: + - esbuild + - rollup + - supports-color + - vite + - webpack + dev: true /unplugin-icons@0.15.3(@vue/compiler-sfc@3.2.45): resolution: {integrity: sha512-YWgJqv5AahrokeOnta8uX/m1damZA6Rf6zPClgHg2Fa/45iyOe3Lj+Wn/Ba+CSsq9yBffn17YfKfJNyWCNZPvw==} @@ -20993,6 +21207,35 @@ packages: - supports-color dev: true + /unplugin-vue-components@0.21.0(esbuild@0.17.19)(rollup@2.79.1)(vite@3.1.4)(vue@3.2.37): + resolution: {integrity: sha512-U7uOMNmRJ2eAv9CNjP8QRvxs6nAe3FVQUEIUphC1FGguBp3BWSLgGAcSHaX2nQy0gFoDY2mLF2M52W/t/eDaKg==} + engines: {node: '>=14'} + peerDependencies: + '@babel/parser': ^7.15.8 + vue: 2 || 3 + peerDependenciesMeta: + '@babel/parser': + optional: true + dependencies: + '@antfu/utils': 0.5.2 + '@rollup/pluginutils': 4.2.1 + chokidar: 3.5.3 + debug: 4.3.4(supports-color@9.2.2) + fast-glob: 3.2.11 + local-pkg: 0.4.2 + magic-string: 0.26.7 + minimatch: 5.1.0 + resolve: 1.22.1 + unplugin: 0.7.1(esbuild@0.17.19)(rollup@2.79.1)(vite@3.1.4) + vue: 3.2.37 + transitivePeerDependencies: + - esbuild + - rollup + - supports-color + - vite + - webpack + dev: true + /unplugin-vue-components@0.21.0(esbuild@0.17.19)(rollup@2.79.1)(vite@3.2.4)(vue@3.2.45)(webpack@5.74.0): resolution: {integrity: sha512-U7uOMNmRJ2eAv9CNjP8QRvxs6nAe3FVQUEIUphC1FGguBp3BWSLgGAcSHaX2nQy0gFoDY2mLF2M52W/t/eDaKg==} engines: {node: '>=14'} @@ -21020,6 +21263,62 @@ packages: - supports-color - vite - webpack + dev: true + + /unplugin-vue-components@0.21.0(vite@3.2.4)(vue@3.2.45): + resolution: {integrity: sha512-U7uOMNmRJ2eAv9CNjP8QRvxs6nAe3FVQUEIUphC1FGguBp3BWSLgGAcSHaX2nQy0gFoDY2mLF2M52W/t/eDaKg==} + engines: {node: '>=14'} + peerDependencies: + '@babel/parser': ^7.15.8 + vue: 2 || 3 + peerDependenciesMeta: + '@babel/parser': + optional: true + dependencies: + '@antfu/utils': 0.5.2 + '@rollup/pluginutils': 4.2.1 + chokidar: 3.5.3 + debug: 4.3.4(supports-color@9.2.2) + fast-glob: 3.2.11 + local-pkg: 0.4.2 + magic-string: 0.26.7 + minimatch: 5.1.0 + resolve: 1.22.1 + unplugin: 0.7.1(vite@3.2.4) + vue: 3.2.45 + transitivePeerDependencies: + - esbuild + - rollup + - supports-color + - vite + - webpack + dev: false + + /unplugin@0.7.1(esbuild@0.17.19)(rollup@2.79.1)(vite@3.1.4): + resolution: {integrity: sha512-Z6hNDXDNh9aimMkPU1mEjtk+2ova8gh0y7rJeJdGH1vWZOHwF2lLQiQ/R97rv9ymmzEQXsR2fyMet72T8jy6ew==} + peerDependencies: + esbuild: '>=0.13' + rollup: ^2.50.0 + vite: ^2.3.0 || ^3.0.0-0 + webpack: 4 || 5 + peerDependenciesMeta: + esbuild: + optional: true + rollup: + optional: true + vite: + optional: true + webpack: + optional: true + dependencies: + acorn: 8.8.2 + chokidar: 3.5.3 + esbuild: 0.17.19 + rollup: 2.79.1 + vite: 3.1.4(sass@1.53.0)(terser@5.14.1) + webpack-sources: 3.2.3 + webpack-virtual-modules: 0.4.4 + dev: true /unplugin@0.7.1(esbuild@0.17.19)(rollup@2.79.1)(vite@3.2.4)(webpack@5.74.0): resolution: {integrity: sha512-Z6hNDXDNh9aimMkPU1mEjtk+2ova8gh0y7rJeJdGH1vWZOHwF2lLQiQ/R97rv9ymmzEQXsR2fyMet72T8jy6ew==} @@ -21046,6 +21345,31 @@ packages: webpack: 5.74.0(esbuild@0.17.19) webpack-sources: 3.2.3 webpack-virtual-modules: 0.4.4 + dev: true + + /unplugin@0.7.1(vite@3.2.4): + resolution: {integrity: sha512-Z6hNDXDNh9aimMkPU1mEjtk+2ova8gh0y7rJeJdGH1vWZOHwF2lLQiQ/R97rv9ymmzEQXsR2fyMet72T8jy6ew==} + peerDependencies: + esbuild: '>=0.13' + rollup: ^2.50.0 + vite: ^2.3.0 || ^3.0.0-0 + webpack: 4 || 5 + peerDependenciesMeta: + esbuild: + optional: true + rollup: + optional: true + vite: + optional: true + webpack: + optional: true + dependencies: + acorn: 8.8.2 + chokidar: 3.5.3 + vite: 3.2.4(@types/node@18.16.17)(sass@1.58.0) + webpack-sources: 3.2.3 + webpack-virtual-modules: 0.4.4 + dev: false /unplugin@0.9.5(esbuild@0.17.19)(rollup@2.79.1)(vite@3.1.4): resolution: {integrity: sha512-luraheyfxwtvkvHpsOvMNv7IjLdORTWKZp0gWYNHGLi2ImON3iIZOj464qEyyEwLA/EMt12fC415HW9zRpOfTg==} @@ -21071,6 +21395,56 @@ packages: vite: 3.1.4(sass@1.53.0)(terser@5.14.1) webpack-sources: 3.2.3 webpack-virtual-modules: 0.4.4 + dev: true + + /unplugin@0.9.5(rollup@2.79.1)(vite@3.2.4): + resolution: {integrity: sha512-luraheyfxwtvkvHpsOvMNv7IjLdORTWKZp0gWYNHGLi2ImON3iIZOj464qEyyEwLA/EMt12fC415HW9zRpOfTg==} + peerDependencies: + esbuild: '>=0.13' + rollup: ^2.50.0 + vite: ^2.3.0 || ^3.0.0-0 + webpack: 4 || 5 + peerDependenciesMeta: + esbuild: + optional: true + rollup: + optional: true + vite: + optional: true + webpack: + optional: true + dependencies: + acorn: 8.8.2 + chokidar: 3.5.3 + rollup: 2.79.1 + vite: 3.2.4(@types/node@17.0.45)(sass@1.53.0)(terser@5.14.1) + webpack-sources: 3.2.3 + webpack-virtual-modules: 0.4.4 + dev: true + + /unplugin@0.9.5(vite@3.2.4): + resolution: {integrity: sha512-luraheyfxwtvkvHpsOvMNv7IjLdORTWKZp0gWYNHGLi2ImON3iIZOj464qEyyEwLA/EMt12fC415HW9zRpOfTg==} + peerDependencies: + esbuild: '>=0.13' + rollup: ^2.50.0 + vite: ^2.3.0 || ^3.0.0-0 + webpack: 4 || 5 + peerDependenciesMeta: + esbuild: + optional: true + rollup: + optional: true + vite: + optional: true + webpack: + optional: true + dependencies: + acorn: 8.8.2 + chokidar: 3.5.3 + vite: 3.2.4(@types/node@18.16.17)(sass@1.58.0) + webpack-sources: 3.2.3 + webpack-virtual-modules: 0.4.4 + dev: false /unplugin@1.1.0: resolution: {integrity: sha512-I8obQ8Rs/hnkxokRV6g8JKOQFgYNnTd9DL58vcSt5IJ9AkK8wbrtsnzD5hi4BJlvcY536JzfEXj9L6h7j559/A==} @@ -21100,6 +21474,7 @@ packages: browserslist: 4.21.1 escalade: 3.1.1 picocolors: 1.0.0 + dev: true /upper-case-first@2.0.2: resolution: {integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==} @@ -21262,7 +21637,7 @@ packages: mlly: 1.2.0 pathe: 1.1.0 picocolors: 1.0.0 - vite: 4.3.9(@types/node@17.0.45)(sass@1.53.0)(terser@5.14.1) + vite: 4.3.9(@types/node@18.11.10)(terser@5.14.1) transitivePeerDependencies: - '@types/node' - less @@ -21524,6 +21899,29 @@ packages: json5: 2.2.1 local-pkg: 0.4.2 picocolors: 1.0.0 + vite: 3.2.4(@types/node@18.16.17)(sass@1.58.0) + yaml: 2.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /vite-plugin-pages@0.26.0(vite@3.2.4): + resolution: {integrity: sha512-yJZvwHEt7puYIf19S89IvkDsWPjWleSied4H8hmdW6i8buCA93z1UAU1ipW1d8fNKrC4FzXsUHHbPm6+kl1p9w==} + peerDependencies: + '@vue/compiler-sfc': ^2.7.0 || ^3.0.0 + vite: ^2.0.0 || ^3.0.0-0 + peerDependenciesMeta: + '@vue/compiler-sfc': + optional: true + dependencies: + '@types/debug': 4.1.7 + debug: 4.3.4(supports-color@9.2.2) + deep-equal: 2.0.5 + extract-comments: 1.1.0 + fast-glob: 3.2.11 + json5: 2.2.1 + local-pkg: 0.4.2 + picocolors: 1.0.0 vite: 3.2.4(@types/node@17.0.45)(sass@1.53.0)(terser@5.14.1) yaml: 2.1.1 transitivePeerDependencies: @@ -21704,6 +22102,75 @@ packages: optionalDependencies: fsevents: 2.3.2 + /vite@3.2.4(@types/node@18.11.10)(terser@5.14.1): + resolution: {integrity: sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.11.10 + esbuild: 0.15.15 + postcss: 8.4.21 + resolve: 1.22.1 + rollup: 2.79.1 + terser: 5.14.1 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /vite@3.2.4(@types/node@18.16.17)(sass@1.58.0): + resolution: {integrity: sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.16.17 + esbuild: 0.15.15 + postcss: 8.4.21 + resolve: 1.22.1 + rollup: 2.79.1 + sass: 1.58.0 + optionalDependencies: + fsevents: 2.3.2 + /vite@4.0.4(@types/node@17.0.45): resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==} engines: {node: ^14.18.0 || >=16.0.0} @@ -21773,6 +22240,40 @@ packages: fsevents: 2.3.2 dev: true + /vite@4.3.9(@types/node@18.11.10)(terser@5.14.1): + resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.11.10 + esbuild: 0.17.19 + postcss: 8.4.24 + rollup: 3.24.0 + terser: 5.14.1 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /vite@4.3.9(@types/node@18.16.17)(sass@1.53.0)(terser@5.14.1): resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} engines: {node: ^14.18.0 || >=16.0.0} @@ -21860,7 +22361,7 @@ packages: tinybench: 2.4.0 tinypool: 0.4.0 tinyspy: 1.1.1 - vite: 3.2.4(@types/node@17.0.45)(sass@1.53.0)(terser@5.14.1) + vite: 3.2.4(@types/node@18.11.10)(terser@5.14.1) vite-node: 0.29.8(@types/node@18.11.10)(terser@5.14.1) why-is-node-running: 2.2.2 transitivePeerDependencies: @@ -22385,6 +22886,7 @@ 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==} @@ -22461,6 +22963,46 @@ packages: resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==} dev: true + /webpack@5.74.0: + resolution: {integrity: sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + dependencies: + '@types/eslint-scope': 3.7.4 + '@types/estree': 0.0.51 + '@webassemblyjs/ast': 1.11.1 + '@webassemblyjs/wasm-edit': 1.11.1 + '@webassemblyjs/wasm-parser': 1.11.1 + acorn: 8.8.0 + acorn-import-assertions: 1.8.0(acorn@8.8.0) + browserslist: 4.21.1 + chrome-trace-event: 1.0.3 + enhanced-resolve: 5.12.0 + es-module-lexer: 0.9.3 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.10 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.1.1 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.6(webpack@5.74.0) + watchpack: 2.4.0 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + dev: true + /webpack@5.74.0(esbuild@0.17.19): resolution: {integrity: sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==} engines: {node: '>=10.13.0'} @@ -22499,6 +23041,7 @@ packages: - '@swc/core' - esbuild - uglify-js + dev: true /websocket-driver@0.7.4: resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} @@ -23115,7 +23658,7 @@ packages: dev: false /yeast@0.1.2: - resolution: {integrity: sha1-AI4G2AlDIMNy28L47XagymyKxBk=} + resolution: {integrity: sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg==} dev: false /yn@3.1.1: From 3b217d78e7b5a54e9a9a1e41b9d0f7d0a6b74abe Mon Sep 17 00:00:00 2001 From: Liyas Thomas Date: Mon, 17 Jul 2023 19:26:43 +0530 Subject: [PATCH 24/30] fix: deps mismatch for vite-plugin-pages-sitemap (#3191) --- packages/hoppscotch-selfhost-web/package.json | 2 +- packages/hoppscotch-selfhost-web/vite.config.ts | 7 +++---- packages/hoppscotch-ui/package.json | 2 +- pnpm-lock.yaml | 16 ++++++++-------- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/hoppscotch-selfhost-web/package.json b/packages/hoppscotch-selfhost-web/package.json index e63437ca3..36b72811e 100644 --- a/packages/hoppscotch-selfhost-web/package.json +++ b/packages/hoppscotch-selfhost-web/package.json @@ -66,7 +66,7 @@ "vite-plugin-html-config": "^1.0.10", "vite-plugin-inspect": "^0.7.4", "vite-plugin-pages": "^0.26.0", - "vite-plugin-pages-sitemap": "^1.4.0", + "vite-plugin-pages-sitemap": "^1.4.5", "vite-plugin-pwa": "^0.13.1", "vite-plugin-static-copy": "^0.12.0", "vite-plugin-vue-layouts": "^0.7.0", diff --git a/packages/hoppscotch-selfhost-web/vite.config.ts b/packages/hoppscotch-selfhost-web/vite.config.ts index 14c1a50a4..fb5268a53 100644 --- a/packages/hoppscotch-selfhost-web/vite.config.ts +++ b/packages/hoppscotch-selfhost-web/vite.config.ts @@ -78,15 +78,14 @@ export default defineConfig({ routeStyle: "nuxt", dirs: "../hoppscotch-common/src/pages", importMode: "async", - onRoutesGenerated(routes) { - return generateSitemap({ + onRoutesGenerated: (routes) => + generateSitemap({ routes, nuxtStyle: true, allowRobots: true, dest: ".sitemap-gen", hostname: ENV.VITE_BASE_URL, - }) - }, + }), }), StaticCopy({ targets: [ diff --git a/packages/hoppscotch-ui/package.json b/packages/hoppscotch-ui/package.json index 36c6a920d..79c5afeff 100644 --- a/packages/hoppscotch-ui/package.json +++ b/packages/hoppscotch-ui/package.json @@ -76,7 +76,7 @@ "vite-plugin-html-config": "^1.0.10", "vite-plugin-inspect": "^0.7.4", "vite-plugin-pages": "^0.26.0", - "vite-plugin-pages-sitemap": "^1.4.0", + "vite-plugin-pages-sitemap": "^1.4.5", "vite-plugin-pwa": "^0.13.1", "vite-plugin-vue-layouts": "^0.7.0", "vite-plugin-windicss": "^1.8.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e098d4b88..3919b203a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -749,8 +749,8 @@ importers: specifier: ^0.26.0 version: 0.26.0(@vue/compiler-sfc@3.2.39)(vite@3.1.4) vite-plugin-pages-sitemap: - specifier: ^1.4.0 - version: 1.4.0 + specifier: ^1.4.5 + version: 1.4.5 vite-plugin-pwa: specifier: ^0.13.1 version: 0.13.1(vite@3.1.4)(workbox-build@6.5.4)(workbox-window@6.5.4) @@ -986,8 +986,8 @@ importers: specifier: ^0.26.0 version: 0.26.0(vite@3.2.4) vite-plugin-pages-sitemap: - specifier: ^1.4.0 - version: 1.4.0 + specifier: ^1.4.5 + version: 1.4.5 vite-plugin-pwa: specifier: ^0.13.1 version: 0.13.1(vite@3.2.4)(workbox-build@6.5.4)(workbox-window@6.5.4) @@ -1327,8 +1327,8 @@ importers: specifier: ^0.26.0 version: 0.26.0(@vue/compiler-sfc@3.2.45)(vite@3.2.4) vite-plugin-pages-sitemap: - specifier: ^1.4.0 - version: 1.4.0 + specifier: ^1.4.5 + version: 1.4.5 vite-plugin-pwa: specifier: ^0.13.1 version: 0.13.1(vite@3.2.4)(workbox-build@6.5.4)(workbox-window@6.5.4) @@ -21853,8 +21853,8 @@ packages: - supports-color dev: true - /vite-plugin-pages-sitemap@1.4.0: - resolution: {integrity: sha512-8GlmNSeObvDVUdmgutfWnGBZGsn3Zb8IXCjRODFB0tOhQqAQPZRwXrFha6ZLjvF4DgVJI3sW2FfiXa1wFxJvmg==} + /vite-plugin-pages-sitemap@1.4.5: + resolution: {integrity: sha512-AcEoJ+0D0P1CwR1LjzBznHs6yNQVP7ha7l6cl/VHrHFcQXbVyKc+QOmLi1a3eONy+aDA3K01pZVK8TzTIufq/w==} dev: true /vite-plugin-pages@0.26.0(@vue/compiler-sfc@3.2.39)(vite@3.1.4): From 5a516f72422d0762eb1635a13bd53c325ad135e3 Mon Sep 17 00:00:00 2001 From: Liyas Thomas Date: Mon, 17 Jul 2023 19:27:49 +0530 Subject: [PATCH 25/30] docs: fixed shortcut keys for spotlight and shortcuts menu (#3192) --- .../hoppscotch-common/src/components/app/ShortcutsPrompt.vue | 5 +++-- packages/hoppscotch-common/src/helpers/shortcuts.ts | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/hoppscotch-common/src/components/app/ShortcutsPrompt.vue b/packages/hoppscotch-common/src/components/app/ShortcutsPrompt.vue index 45f788935..001b4b98d 100644 --- a/packages/hoppscotch-common/src/components/app/ShortcutsPrompt.vue +++ b/packages/hoppscotch-common/src/components/app/ShortcutsPrompt.vue @@ -22,10 +22,11 @@
{{ getSpecialKey() }} - K + /
- / + {{ getSpecialKey() }} + K
? diff --git a/packages/hoppscotch-common/src/helpers/shortcuts.ts b/packages/hoppscotch-common/src/helpers/shortcuts.ts index 73af38270..b94178c6f 100644 --- a/packages/hoppscotch-common/src/helpers/shortcuts.ts +++ b/packages/hoppscotch-common/src/helpers/shortcuts.ts @@ -16,12 +16,12 @@ export function getShortcuts(t: (x: string) => string): ShortcutDef[] { }, { label: t("shortcut.general.command_menu"), - keys: ["/"], + keys: [getPlatformSpecialKey(), "K"], section: t("shortcut.general.title"), }, { label: t("shortcut.general.show_all"), - keys: [getPlatformSpecialKey(), "K"], + keys: [getPlatformSpecialKey(), "/"], section: t("shortcut.general.title"), }, { From 8bb1d19c078032e82491eabdfbb0da2aa86c0acd Mon Sep 17 00:00:00 2001 From: Anwarul Islam Date: Tue, 1 Aug 2023 13:50:17 +0600 Subject: [PATCH 26/30] fix: firefox browser scrollbar issue (#3201) --- packages/hoppscotch-common/src/components.d.ts | 3 +++ packages/hoppscotch-ui/src/components/smart/Windows.vue | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/hoppscotch-common/src/components.d.ts b/packages/hoppscotch-common/src/components.d.ts index a3e0fbf33..914ec6108 100644 --- a/packages/hoppscotch-common/src/components.d.ts +++ b/packages/hoppscotch-common/src/components.d.ts @@ -133,7 +133,10 @@ declare module '@vue/runtime-core' { IconLucideLayers: typeof import('~icons/lucide/layers')['default'] IconLucideListEnd: typeof import('~icons/lucide/list-end')['default'] IconLucideMinus: typeof import('~icons/lucide/minus')['default'] +<<<<<<< HEAD IconLucideRss: typeof import('~icons/lucide/rss')['default'] +======= +>>>>>>> 6db825779 (fix: firefox browser scrollbar issue) IconLucideSearch: typeof import('~icons/lucide/search')['default'] IconLucideUsers: typeof import('~icons/lucide/users')['default'] LensesHeadersRenderer: typeof import('./components/lenses/HeadersRenderer.vue')['default'] diff --git a/packages/hoppscotch-ui/src/components/smart/Windows.vue b/packages/hoppscotch-ui/src/components/smart/Windows.vue index e4f8378c7..af7dc2a6e 100644 --- a/packages/hoppscotch-ui/src/components/smart/Windows.vue +++ b/packages/hoppscotch-ui/src/components/smart/Windows.vue @@ -4,7 +4,7 @@ class="relative sticky top-0 z-10 flex-shrink-0 overflow-x-auto divide-x divide-dividerLight bg-primaryLight tabs group-tabs" >
Date: Wed, 2 Aug 2023 20:47:54 +0530 Subject: [PATCH 27/30] fix: elastic overscroll on safari (#3221) --- packages/hoppscotch-common/assets/scss/styles.scss | 1 + packages/hoppscotch-sh-admin/assets/scss/styles.scss | 1 + packages/hoppscotch-ui/src/assets/scss/styles.scss | 1 + 3 files changed, 3 insertions(+) diff --git a/packages/hoppscotch-common/assets/scss/styles.scss b/packages/hoppscotch-common/assets/scss/styles.scss index 64bfe65aa..da5e37ca3 100644 --- a/packages/hoppscotch-common/assets/scss/styles.scss +++ b/packages/hoppscotch-common/assets/scss/styles.scss @@ -4,6 +4,7 @@ @apply after:backface-hidden; @apply selection:bg-accentDark; @apply selection:text-accentContrast; + @apply overscroll-none; } :root { diff --git a/packages/hoppscotch-sh-admin/assets/scss/styles.scss b/packages/hoppscotch-sh-admin/assets/scss/styles.scss index 64bfe65aa..da5e37ca3 100644 --- a/packages/hoppscotch-sh-admin/assets/scss/styles.scss +++ b/packages/hoppscotch-sh-admin/assets/scss/styles.scss @@ -4,6 +4,7 @@ @apply after:backface-hidden; @apply selection:bg-accentDark; @apply selection:text-accentContrast; + @apply overscroll-none; } :root { diff --git a/packages/hoppscotch-ui/src/assets/scss/styles.scss b/packages/hoppscotch-ui/src/assets/scss/styles.scss index 64bfe65aa..da5e37ca3 100644 --- a/packages/hoppscotch-ui/src/assets/scss/styles.scss +++ b/packages/hoppscotch-ui/src/assets/scss/styles.scss @@ -4,6 +4,7 @@ @apply after:backface-hidden; @apply selection:bg-accentDark; @apply selection:text-accentContrast; + @apply overscroll-none; } :root { From 8970ff5c68c07cb3da7ff15cb4d535f97c006e15 Mon Sep 17 00:00:00 2001 From: Nivedin <53208152+nivedin@users.noreply.github.com> Date: Wed, 2 Aug 2023 20:52:16 +0530 Subject: [PATCH 28/30] feat: context menu (#3180) Co-authored-by: Liyas Thomas --- packages/hoppscotch-common/locales/en.json | 12 + packages/hoppscotch-common/package.json | 2 +- .../hoppscotch-common/src/components.d.ts | 6 +- .../src/components/app/ContextMenu.vue | 76 +++++ .../src/components/app/Shortcuts.vue | 5 +- .../src/components/environments/Add.vue | 208 +++++++++++++ .../src/components/environments/Selector.vue | 286 ++++++++++++++---- .../src/components/environments/index.vue | 21 ++ .../src/components/smart/EnvInput.vue | 48 ++- .../src/composables/codemirror.ts | 37 +++ .../hoppscotch-common/src/helpers/actions.ts | 15 + .../hoppscotch-common/src/pages/index.vue | 41 +++ .../context-menu/__tests__/index.spec.ts | 114 +++++++ .../src/services/context-menu/index.ts | 109 +++++++ .../menu/__tests__/environment.menu.spec.ts | 70 +++++ .../menu/__tests__/parameter.menu.spec.ts | 94 ++++++ .../menu/__tests__/url.menu.spec.ts | 86 ++++++ .../context-menu/menu/environment.menu.ts | 56 ++++ .../context-menu/menu/parameter.menu.ts | 133 ++++++++ .../services/context-menu/menu/url.menu.ts | 89 ++++++ packages/hoppscotch-ui/package.json | 2 +- pnpm-lock.yaml | 14 +- 22 files changed, 1447 insertions(+), 77 deletions(-) create mode 100644 packages/hoppscotch-common/src/components/app/ContextMenu.vue create mode 100644 packages/hoppscotch-common/src/components/environments/Add.vue create mode 100644 packages/hoppscotch-common/src/services/context-menu/__tests__/index.spec.ts create mode 100644 packages/hoppscotch-common/src/services/context-menu/index.ts create mode 100644 packages/hoppscotch-common/src/services/context-menu/menu/__tests__/environment.menu.spec.ts create mode 100644 packages/hoppscotch-common/src/services/context-menu/menu/__tests__/parameter.menu.spec.ts create mode 100644 packages/hoppscotch-common/src/services/context-menu/menu/__tests__/url.menu.spec.ts create mode 100644 packages/hoppscotch-common/src/services/context-menu/menu/environment.menu.ts create mode 100644 packages/hoppscotch-common/src/services/context-menu/menu/parameter.menu.ts create mode 100644 packages/hoppscotch-common/src/services/context-menu/menu/url.menu.ts diff --git a/packages/hoppscotch-common/locales/en.json b/packages/hoppscotch-common/locales/en.json index 6a84716dc..cb30e4ebe 100644 --- a/packages/hoppscotch-common/locales/en.json +++ b/packages/hoppscotch-common/locales/en.json @@ -151,6 +151,11 @@ "save_unsaved_tab": "Do you want to save changes made in this tab?", "sync": "Would you like to restore your workspace from cloud? This will discard your local progress." }, + "context_menu": { + "set_environment_variable": "Set as variable", + "add_parameter": "Add to parameter", + "open_link_in_new_tab": "Open link in new tab" + }, "count": { "header": "Header {count}", "message": "Message {count}", @@ -195,16 +200,23 @@ "created": "Environment created", "deleted": "Environment deletion", "edit": "Edit Environment", + "global": "Global", "invalid_name": "Please provide a name for the environment", "my_environments": "My Environments", + "name": "Name", "nested_overflow": "nested environment variables are limited to 10 levels", "new": "New Environment", "no_environment": "No environment", "no_environment_description": "No environments were selected. Choose what to do with the following variables.", + "replace_with_variable": "Replace with variable", + "scope": "Scope", "select": "Select environment", + "set_as_environment": "Set as environment", "team_environments": "Team Environments", "title": "Environments", "updated": "Environment updated", + "value": "Value", + "variable": "Variable", "variable_list": "Variable List" }, "error": { diff --git a/packages/hoppscotch-common/package.json b/packages/hoppscotch-common/package.json index fa8c73d27..ab8d2ad12 100644 --- a/packages/hoppscotch-common/package.json +++ b/packages/hoppscotch-common/package.json @@ -110,7 +110,7 @@ "@graphql-codegen/typescript-urql-graphcache": "^2.3.1", "@graphql-codegen/urql-introspection": "^2.2.0", "@graphql-typed-document-node/core": "^3.1.1", - "@iconify-json/lucide": "^1.1.40", + "@iconify-json/lucide": "^1.1.109", "@intlify/vite-plugin-vue-i18n": "^7.0.0", "@relmify/jest-fp-ts": "^2.1.1", "@rushstack/eslint-patch": "^1.1.4", diff --git a/packages/hoppscotch-common/src/components.d.ts b/packages/hoppscotch-common/src/components.d.ts index 914ec6108..849ec7515 100644 --- a/packages/hoppscotch-common/src/components.d.ts +++ b/packages/hoppscotch-common/src/components.d.ts @@ -9,6 +9,7 @@ declare module '@vue/runtime-core' { 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'] @@ -54,6 +55,7 @@ declare module '@vue/runtime-core' { 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'] @@ -94,7 +96,6 @@ declare module '@vue/runtime-core' { HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner'] HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab'] HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs'] - HoppSmartToggle: typeof import('@hoppscotch/ui')['HoppSmartToggle'] HoppSmartWindow: typeof import('@hoppscotch/ui')['HoppSmartWindow'] HoppSmartWindows: typeof import('@hoppscotch/ui')['HoppSmartWindows'] HttpAuthorization: typeof import('./components/http/Authorization.vue')['default'] @@ -133,10 +134,7 @@ declare module '@vue/runtime-core' { IconLucideLayers: typeof import('~icons/lucide/layers')['default'] IconLucideListEnd: typeof import('~icons/lucide/list-end')['default'] IconLucideMinus: typeof import('~icons/lucide/minus')['default'] -<<<<<<< HEAD IconLucideRss: typeof import('~icons/lucide/rss')['default'] -======= ->>>>>>> 6db825779 (fix: firefox browser scrollbar issue) IconLucideSearch: typeof import('~icons/lucide/search')['default'] IconLucideUsers: typeof import('~icons/lucide/users')['default'] LensesHeadersRenderer: typeof import('./components/lenses/HeadersRenderer.vue')['default'] diff --git a/packages/hoppscotch-common/src/components/app/ContextMenu.vue b/packages/hoppscotch-common/src/components/app/ContextMenu.vue new file mode 100644 index 000000000..123d09202 --- /dev/null +++ b/packages/hoppscotch-common/src/components/app/ContextMenu.vue @@ -0,0 +1,76 @@ + + + diff --git a/packages/hoppscotch-common/src/components/app/Shortcuts.vue b/packages/hoppscotch-common/src/components/app/Shortcuts.vue index dc5d0e608..ec73ed19c 100644 --- a/packages/hoppscotch-common/src/components/app/Shortcuts.vue +++ b/packages/hoppscotch-common/src/components/app/Shortcuts.vue @@ -15,7 +15,10 @@
- +
+ + + + + + + diff --git a/packages/hoppscotch-common/src/components/environments/Selector.vue b/packages/hoppscotch-common/src/components/environments/Selector.vue index e960cf41a..4e25c7195 100644 --- a/packages/hoppscotch-common/src/components/environments/Selector.vue +++ b/packages/hoppscotch-common/src/components/environments/Selector.vue @@ -8,7 +8,7 @@ + @@ -138,6 +145,24 @@ const reqName = ref("") const t = useI18n() const toast = useToast() +type PopupDetails = { + show: boolean + position: { + top: number + left: number + } + text: string | null +} + +const contextMenu = ref({ + show: false, + position: { + top: 0, + left: 0, + }, + text: null, +}) + const tabs = getActiveTabs() const confirmSync = useReadonlyStream(currentSyncingStatus$, { @@ -365,6 +390,22 @@ function oAuthURL() { }) } +defineActionHandler("contextmenu.open", ({ position, text }) => { + if (text) { + contextMenu.value = { + show: true, + position, + text, + } + } else { + contextMenu.value = { + show: false, + position, + text, + } + } +}) + setupTabStateSync() bindRequestToURLParams() oAuthURL() diff --git a/packages/hoppscotch-common/src/services/context-menu/__tests__/index.spec.ts b/packages/hoppscotch-common/src/services/context-menu/__tests__/index.spec.ts new file mode 100644 index 000000000..627775d08 --- /dev/null +++ b/packages/hoppscotch-common/src/services/context-menu/__tests__/index.spec.ts @@ -0,0 +1,114 @@ +import { describe, it, expect, vi } from "vitest" +import { ContextMenu, ContextMenuResult, ContextMenuService } from "../" +import { TestContainer } from "dioc/testing" + +const contextMenuResult: ContextMenuResult[] = [ + { + id: "result1", + text: { type: "text", text: "Sample Text" }, + icon: {}, + // eslint-disable-next-line @typescript-eslint/no-empty-function + action: () => {}, + }, +] + +const testMenu: ContextMenu = { + menuID: "menu1", + getMenuFor: () => { + return { + results: contextMenuResult, + } + }, +} + +describe("ContextMenuService", () => { + describe("registerMenu", () => { + it("should register a menu", () => { + const container = new TestContainer() + const service = container.bind(ContextMenuService) + + service.registerMenu(testMenu) + + const result = service.getMenuFor("text") + + expect(result).toContainEqual(expect.objectContaining({ id: "result1" })) + }) + + it("should not register a menu twice", () => { + const container = new TestContainer() + const service = container.bind(ContextMenuService) + + service.registerMenu(testMenu) + service.registerMenu(testMenu) + + const result = service.getMenuFor("text") + + expect(result).toHaveLength(1) + }) + + it("should register multiple menus", () => { + const container = new TestContainer() + const service = container.bind(ContextMenuService) + + const testMenu2: ContextMenu = { + menuID: "menu2", + getMenuFor: () => { + return { + results: contextMenuResult, + } + }, + } + + service.registerMenu(testMenu) + service.registerMenu(testMenu2) + + const result = service.getMenuFor("text") + + expect(result).toHaveLength(2) + }) + }) + + describe("getMenuFor", () => { + it("should get the menu", () => { + const sampleMenus = { + results: contextMenuResult, + } + + const container = new TestContainer() + const service = container.bind(ContextMenuService) + + service.registerMenu(testMenu) + + const results = service.getMenuFor("sometext") + + expect(results).toEqual(sampleMenus.results) + }) + + it("calls registered menus with correct value", () => { + const container = new TestContainer() + const service = container.bind(ContextMenuService) + + const testMenu2: ContextMenu = { + menuID: "some-id", + getMenuFor: vi.fn(() => ({ + results: contextMenuResult, + })), + } + + service.registerMenu(testMenu2) + + service.getMenuFor("sometext") + + expect(testMenu2.getMenuFor).toHaveBeenCalledWith("sometext") + }) + + it("should return empty array if no menus are registered", () => { + const container = new TestContainer() + const service = container.bind(ContextMenuService) + + const results = service.getMenuFor("sometext") + + expect(results).toEqual([]) + }) + }) +}) diff --git a/packages/hoppscotch-common/src/services/context-menu/index.ts b/packages/hoppscotch-common/src/services/context-menu/index.ts new file mode 100644 index 000000000..609ca7b64 --- /dev/null +++ b/packages/hoppscotch-common/src/services/context-menu/index.ts @@ -0,0 +1,109 @@ +import { Service } from "dioc" +import { Component } from "vue" + +/** + * Defines how to render the text in a Context Menu Search Result + */ +export type ContextMenuTextType = + | { + type: "text" + text: string + } + | { + type: "custom" + /** + * The component to render in place of the text + */ + component: T + + /** + * The props to pass to the component + */ + componentProps: T extends Component ? Props : never + } + +/** + * Defines info about a context menu result so the UI can render it + */ +export interface ContextMenuResult { + /** + * The unique ID of the result + */ + id: string + /** + * The text to render in the result + */ + text: ContextMenuTextType + /** + * The icon to render as the signifier of the result + */ + icon: object | Component + /** + * The action to perform when the result is selected + */ + action: () => void + /** + * Additional metadata about the result + */ + meta?: { + /** + * The keyboard shortcut to trigger the result + */ + keyboardShortcut?: string[] + } +} + +/** + * Defines the state of a context menu + */ +export type ContextMenuState = { + results: ContextMenuResult[] +} + +/** + * Defines a context menu + */ +export interface ContextMenu { + /** + * The unique ID of the context menu + * This is used to identify the context menu + */ + menuID: string + /** + * Gets the context menu for the given text + * @param text The text to get the context menu for + * @returns The context menu state + */ + getMenuFor: (text: string) => ContextMenuState +} + +/** + * Defines the context menu service + * This service is used to register context menus and get context menus for text + * This service is used by the context menu UI + */ +export class ContextMenuService extends Service { + public static readonly ID = "CONTEXT_MENU_SERVICE" + + private menus: Map = new Map() + + /** + * Registers a menu with the context menu service + * @param menu The menu to register + */ + public registerMenu(menu: ContextMenu) { + this.menus.set(menu.menuID, menu) + } + + /** + * Gets the context menu for the given text + * @param text The text to get the context menu for + */ + public getMenuFor(text: string): ContextMenuResult[] { + const menus = Array.from(this.menus.values()).map((x) => x.getMenuFor(text)) + + const result = menus.flatMap((x) => x.results) + + return result + } +} diff --git a/packages/hoppscotch-common/src/services/context-menu/menu/__tests__/environment.menu.spec.ts b/packages/hoppscotch-common/src/services/context-menu/menu/__tests__/environment.menu.spec.ts new file mode 100644 index 000000000..0f9a35923 --- /dev/null +++ b/packages/hoppscotch-common/src/services/context-menu/menu/__tests__/environment.menu.spec.ts @@ -0,0 +1,70 @@ +import { TestContainer } from "dioc/testing" +import { describe, expect, it, vi } from "vitest" +import { EnvironmentMenuService } from "../environment.menu" +import { ContextMenuService } from "../.." + +vi.mock("~/modules/i18n", () => ({ + __esModule: true, + getI18n: () => (x: string) => x, +})) + +const actionsMock = vi.hoisted(() => ({ + invokeAction: vi.fn(), +})) + +vi.mock("~/helpers/actions", async () => { + return { + __esModule: true, + invokeAction: actionsMock.invokeAction, + } +}) + +describe("EnvironmentMenuService", () => { + it("registers with the contextmenu service upon initialization", () => { + const container = new TestContainer() + + const registerContextMenuFn = vi.fn() + + container.bindMock(ContextMenuService, { + registerMenu: registerContextMenuFn, + }) + + const environment = container.bind(EnvironmentMenuService) + + expect(registerContextMenuFn).toHaveBeenCalledOnce() + expect(registerContextMenuFn).toHaveBeenCalledWith(environment) + }) + + describe("getMenuFor", () => { + it("should return a menu for adding environment", () => { + const container = new TestContainer() + const environment = container.bind(EnvironmentMenuService) + + const test = "some-text" + const result = environment.getMenuFor(test) + + expect(result.results).toContainEqual( + expect.objectContaining({ id: "environment" }) + ) + }) + + it("should invoke the add environment modal", () => { + const container = new TestContainer() + const environment = container.bind(EnvironmentMenuService) + + const test = "some-text" + const result = environment.getMenuFor(test) + + const action = result.results[0].action + action() + expect(actionsMock.invokeAction).toHaveBeenCalledOnce() + expect(actionsMock.invokeAction).toHaveBeenCalledWith( + "modals.environment.add", + { + envName: "test", + variableName: test, + } + ) + }) + }) +}) diff --git a/packages/hoppscotch-common/src/services/context-menu/menu/__tests__/parameter.menu.spec.ts b/packages/hoppscotch-common/src/services/context-menu/menu/__tests__/parameter.menu.spec.ts new file mode 100644 index 000000000..bfd0fc70e --- /dev/null +++ b/packages/hoppscotch-common/src/services/context-menu/menu/__tests__/parameter.menu.spec.ts @@ -0,0 +1,94 @@ +import { TestContainer } from "dioc/testing" +import { describe, expect, it, vi } from "vitest" +import { ContextMenuService } from "../.." +import { ParameterMenuService } from "../parameter.menu" + +//regex containing both url and parameter +const urlAndParameterRegex = new RegExp("[^&?]*?=[^&?]*") + +vi.mock("~/modules/i18n", () => ({ + __esModule: true, + getI18n: () => (x: string) => x, +})) + +const tabMock = vi.hoisted(() => ({ + currentActiveTab: vi.fn(), +})) + +vi.mock("~/helpers/rest/tab", () => ({ + __esModule: true, + currentActiveTab: tabMock.currentActiveTab, +})) + +describe("ParameterMenuService", () => { + it("registers with the contextmenu service upon initialization", () => { + const container = new TestContainer() + + const registerContextMenuFn = vi.fn() + + container.bindMock(ContextMenuService, { + registerMenu: registerContextMenuFn, + }) + + const parameter = container.bind(ParameterMenuService) + + expect(registerContextMenuFn).toHaveBeenCalledOnce() + expect(registerContextMenuFn).toHaveBeenCalledWith(parameter) + + describe("getMenuFor", () => { + it("validating if the text passes the regex and return the menu", () => { + const container = new TestContainer() + const parameter = container.bind(ParameterMenuService) + + const test = "https://hoppscotch.io?id=some-text" + const result = parameter.getMenuFor(test) + + if (test.match(urlAndParameterRegex)) { + expect(result.results).toContainEqual( + expect.objectContaining({ id: "parameter" }) + ) + } else { + expect(result.results).not.toContainEqual( + expect.objectContaining({ id: "parameter" }) + ) + } + }) + + it("should call the addParameter function when action is called", () => { + const addParameterFn = vi.fn() + + const container = new TestContainer() + const environment = container.bind(ParameterMenuService) + + const test = "https://hoppscotch.io" + + const result = environment.getMenuFor(test) + + const action = result.results[0].action + + action() + + expect(addParameterFn).toHaveBeenCalledOnce() + expect(addParameterFn).toHaveBeenCalledWith(action) + }) + + it("should call the extractParams function when addParameter function is called", () => { + const extractParamsFn = vi.fn() + + const container = new TestContainer() + const environment = container.bind(ParameterMenuService) + + const test = "https://hoppscotch.io" + + const result = environment.getMenuFor(test) + + const action = result.results[0].action + + action() + + expect(extractParamsFn).toHaveBeenCalledOnce() + expect(extractParamsFn).toHaveBeenCalledWith(action) + }) + }) + }) +}) diff --git a/packages/hoppscotch-common/src/services/context-menu/menu/__tests__/url.menu.spec.ts b/packages/hoppscotch-common/src/services/context-menu/menu/__tests__/url.menu.spec.ts new file mode 100644 index 000000000..5b4b0d31e --- /dev/null +++ b/packages/hoppscotch-common/src/services/context-menu/menu/__tests__/url.menu.spec.ts @@ -0,0 +1,86 @@ +import { TestContainer } from "dioc/testing" +import { describe, expect, it, vi } from "vitest" +import { ContextMenuService } from "../.." +import { URLMenuService } from "../url.menu" +import { getDefaultRESTRequest } from "~/helpers/rest/default" + +vi.mock("~/modules/i18n", () => ({ + __esModule: true, + getI18n: () => (x: string) => x, +})) + +const tabMock = vi.hoisted(() => ({ + createNewTab: vi.fn(), +})) + +vi.mock("~/helpers/rest/tab", () => ({ + __esModule: true, + createNewTab: tabMock.createNewTab, +})) + +describe("URLMenuService", () => { + it("registers with the contextmenu service upon initialization", () => { + const container = new TestContainer() + + const registerContextMenuFn = vi.fn() + + container.bindMock(ContextMenuService, { + registerMenu: registerContextMenuFn, + }) + + const environment = container.bind(URLMenuService) + + expect(registerContextMenuFn).toHaveBeenCalledOnce() + expect(registerContextMenuFn).toHaveBeenCalledWith(environment) + }) + + describe("getMenuFor", () => { + it("validating if the text passes the regex and return the menu", () => { + function isValidURL(url: string) { + try { + new URL(url) + return true + } catch (error) { + // Fallback to regular expression check + const pattern = /^(https?:\/\/)?([\w.-]+)(\.[\w.-]+)+([/?].*)?$/ + return pattern.test(url) + } + } + + const container = new TestContainer() + const url = container.bind(URLMenuService) + + const test = "" + const result = url.getMenuFor(test) + + if (isValidURL(test)) { + expect(result.results).toContainEqual( + expect.objectContaining({ id: "link-tab" }) + ) + } else { + expect(result).toEqual({ results: [] }) + } + }) + + it("should call the openNewTab function when action is called and a new hoppscotch tab is opened", () => { + const container = new TestContainer() + const url = container.bind(URLMenuService) + + const test = "https://hoppscotch.io" + const result = url.getMenuFor(test) + + result.results[0].action() + + const request = { + ...getDefaultRESTRequest(), + endpoint: test, + } + + expect(tabMock.createNewTab).toHaveBeenCalledOnce() + expect(tabMock.createNewTab).toHaveBeenCalledWith({ + request: request, + isDirty: false, + }) + }) + }) +}) diff --git a/packages/hoppscotch-common/src/services/context-menu/menu/environment.menu.ts b/packages/hoppscotch-common/src/services/context-menu/menu/environment.menu.ts new file mode 100644 index 000000000..1882bda4b --- /dev/null +++ b/packages/hoppscotch-common/src/services/context-menu/menu/environment.menu.ts @@ -0,0 +1,56 @@ +import { Service } from "dioc" +import { + ContextMenu, + ContextMenuResult, + ContextMenuService, + ContextMenuState, +} from "../" +import { markRaw, ref } from "vue" +import { invokeAction } from "~/helpers/actions" +import IconPlusCircle from "~icons/lucide/plus-circle" +import { getI18n } from "~/modules/i18n" + +/** + * This menu returns a single result that allows the user + * to add the selected text as an environment variable + * This menus is shown on all text selections + */ +export class EnvironmentMenuService extends Service implements ContextMenu { + public static readonly ID = "ENVIRONMENT_CONTEXT_MENU_SERVICE" + + private t = getI18n() + + public readonly menuID = "environment" + + private readonly contextMenu = this.bind(ContextMenuService) + + constructor() { + super() + + this.contextMenu.registerMenu(this) + } + + getMenuFor(text: Readonly): ContextMenuState { + const results = ref([]) + results.value = [ + { + id: "environment", + text: { + type: "text", + text: this.t("context_menu.set_environment_variable"), + }, + icon: markRaw(IconPlusCircle), + action: () => { + invokeAction("modals.environment.add", { + envName: "test", + variableName: text, + }) + }, + }, + ] + const resultObj = { + results: results.value, + } + return resultObj + } +} diff --git a/packages/hoppscotch-common/src/services/context-menu/menu/parameter.menu.ts b/packages/hoppscotch-common/src/services/context-menu/menu/parameter.menu.ts new file mode 100644 index 000000000..ecb468ced --- /dev/null +++ b/packages/hoppscotch-common/src/services/context-menu/menu/parameter.menu.ts @@ -0,0 +1,133 @@ +import { Service } from "dioc" +import { + ContextMenu, + ContextMenuResult, + ContextMenuService, + ContextMenuState, +} from "../" +import { markRaw, ref } from "vue" +import IconArrowDownRight from "~icons/lucide/arrow-down-right" +import { currentActiveTab } from "~/helpers/rest/tab" +import { getI18n } from "~/modules/i18n" + +//regex containing both url and parameter +const urlAndParameterRegex = new RegExp("[^&?]*?=[^&?]*") + +interface Param { + [key: string]: string +} + +/** + * The extracted parameters from the input + * with the new URL if it was provided + */ +interface ExtractedParams { + params: Param + newURL?: string +} + +/** + * This menu returns a single result that allows the user + * to add the selected text as a parameter + * if the selected text is a valid URL + */ +export class ParameterMenuService extends Service implements ContextMenu { + public static readonly ID = "PARAMETER_CONTEXT_MENU_SERVICE" + + private t = getI18n() + + public readonly menuID = "parameter" + + private readonly contextMenu = this.bind(ContextMenuService) + + constructor() { + super() + + this.contextMenu.registerMenu(this) + } + + /** + * + * @param input The input to extract the parameters from + * @returns The extracted parameters and the new URL if it was provided + */ + private extractParams(input: string): ExtractedParams { + let text = input + let newURL: string | undefined + + // if the input is a URL, extract the parameters + if (text.startsWith("http")) { + const url = new URL(text) + newURL = url.origin + url.pathname + text = url.search.slice(1) + } + + const regex = /(\w+)=(\w+)/g + const matches = text.matchAll(regex) + const params: Param = {} + + // extract the parameters from the input + for (const match of matches) { + const [, key, value] = match + params[key] = value + } + + return { params, newURL } + } + + /** + * Adds the parameters from the input to the current request + * parameters and updates the endpoint if a new URL was provided + * @param text The input to extract the parameters from + */ + private addParameter(text: string) { + const { params, newURL } = this.extractParams(text) + + const queryParams = [] + for (const [key, value] of Object.entries(params)) { + queryParams.push({ key, value, active: true }) + } + + // add the parameters to the current request parameters + currentActiveTab.value.document.request.params = [ + ...currentActiveTab.value.document.request.params, + ...queryParams, + ] + + if (newURL) { + currentActiveTab.value.document.request.endpoint = newURL + } else { + // remove the parameter from the URL + const textRegex = new RegExp(`\\b${text.replace(/\?/g, "")}\\b`, "gi") + const sanitizedWord = currentActiveTab.value.document.request.endpoint + const newURL = sanitizedWord.replace(textRegex, "") + currentActiveTab.value.document.request.endpoint = newURL + } + } + + getMenuFor(text: Readonly): ContextMenuState { + const results = ref([]) + + if (urlAndParameterRegex.test(text)) { + results.value = [ + { + id: "environment", + text: { + type: "text", + text: this.t("context_menu.add_parameter"), + }, + icon: markRaw(IconArrowDownRight), + action: () => { + this.addParameter(text) + }, + }, + ] + } + + const resultObj = { + results: results.value, + } + + return resultObj + } +} diff --git a/packages/hoppscotch-common/src/services/context-menu/menu/url.menu.ts b/packages/hoppscotch-common/src/services/context-menu/menu/url.menu.ts new file mode 100644 index 000000000..fcec234ce --- /dev/null +++ b/packages/hoppscotch-common/src/services/context-menu/menu/url.menu.ts @@ -0,0 +1,89 @@ +import { Service } from "dioc" +import { + ContextMenu, + ContextMenuResult, + ContextMenuService, + ContextMenuState, +} from ".." +import { markRaw, ref } from "vue" +import IconCopyPlus from "~icons/lucide/copy-plus" +import { createNewTab } from "~/helpers/rest/tab" +import { getDefaultRESTRequest } from "~/helpers/rest/default" +import { getI18n } from "~/modules/i18n" + +/** + * Used to check if a string is a valid URL + * @param url The string to check + * @returns Whether the string is a valid URL + */ +function isValidURL(url: string) { + try { + // Try to create a URL object + // this will fail for endpoints like "localhost:3000", ie without a protocol + new URL(url) + return true + } catch (error) { + // Fallback to regular expression check + const pattern = /^(https?:\/\/)?([\w.-]+)(\.[\w.-]+)+([/?].*)?$/ + return pattern.test(url) + } +} + +export class URLMenuService extends Service implements ContextMenu { + public static readonly ID = "URL_CONTEXT_MENU_SERVICE" + + private t = getI18n() + + public readonly menuID = "url" + + private readonly contextMenu = this.bind(ContextMenuService) + + constructor() { + super() + + this.contextMenu.registerMenu(this) + } + + /** + * Opens a new tab with the provided URL + * @param url The URL to open + */ + private openNewTab(url: string) { + //create a new request object + const request = { + ...getDefaultRESTRequest(), + endpoint: url, + } + + createNewTab({ + request: request, + isDirty: false, + }) + } + + getMenuFor(text: Readonly): ContextMenuState { + const results = ref([]) + + if (isValidURL(text)) { + results.value = [ + { + id: "link-tab", + text: { + type: "text", + text: this.t("context_menu.open_link_in_new_tab"), + }, + icon: markRaw(IconCopyPlus), + action: () => { + this.openNewTab(text) + }, + }, + ] + } + + const resultObj = { + results: results.value, + } + + return resultObj + } +} diff --git a/packages/hoppscotch-ui/package.json b/packages/hoppscotch-ui/package.json index 79c5afeff..1c1a04929 100644 --- a/packages/hoppscotch-ui/package.json +++ b/packages/hoppscotch-ui/package.json @@ -47,7 +47,7 @@ "@esbuild-plugins/node-globals-polyfill": "^0.1.1", "@esbuild-plugins/node-modules-polyfill": "^0.1.4", "@histoire/plugin-vue": "^0.12.4", - "@iconify-json/lucide": "^1.1.40", + "@iconify-json/lucide": "^1.1.109", "@intlify/vite-plugin-vue-i18n": "^6.0.1", "@rushstack/eslint-patch": "^1.1.4", "@types/lodash-es": "^4.17.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3919b203a..35c19d7ee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -638,8 +638,8 @@ importers: specifier: ^3.1.1 version: 3.1.1(graphql@15.8.0) '@iconify-json/lucide': - specifier: ^1.1.40 - version: 1.1.40 + specifier: ^1.1.109 + version: 1.1.109 '@intlify/vite-plugin-vue-i18n': specifier: ^7.0.0 version: 7.0.0(vite@3.1.4)(vue-i18n@9.2.2) @@ -1240,8 +1240,8 @@ importers: specifier: ^0.12.4 version: 0.12.4(histoire@0.12.4)(vite@3.2.4)(vue@3.2.45) '@iconify-json/lucide': - specifier: ^1.1.40 - version: 1.1.40 + specifier: ^1.1.109 + version: 1.1.109 '@intlify/vite-plugin-vue-i18n': specifier: ^6.0.1 version: 6.0.1(vite@3.2.4) @@ -5809,10 +5809,10 @@ packages: /@iarna/toml@2.2.5: resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} - /@iconify-json/lucide@1.1.40: - resolution: {integrity: sha512-4GeQtaiv3mJ+b0sn/c2KL8Tgf4XQvsX1AHDOseuGRhgoLCWG+ZdNRFxF5sp1I6T/VcQccegLPOp5XHn3NC1mmA==} + /@iconify-json/lucide@1.1.109: + resolution: {integrity: sha512-1+zYieiKUAjN1x66kvcRmmtgBJaDbD7i4To8mhB6+3bEm/i61un76nspJ45LOSGovzBMvYZFIJpqJrGMipWPzw==} dependencies: - '@iconify/types': 1.1.0 + '@iconify/types': 2.0.0 dev: true /@iconify/types@1.1.0: From 610538ca02666b0250adbeef3b7bebbf8f7d8330 Mon Sep 17 00:00:00 2001 From: Anwarul Islam Date: Wed, 2 Aug 2023 21:24:02 +0600 Subject: [PATCH 29/30] chore: type and ux improvement for SmartTree (#3126) --- .../src/components/collections/Collection.vue | 102 +++++++----------- .../src/components/smart/Tree.vue | 1 + .../src/components/smart/TreeBranch.vue | 12 ++- 3 files changed, 48 insertions(+), 67 deletions(-) diff --git a/packages/hoppscotch-common/src/components/collections/Collection.vue b/packages/hoppscotch-common/src/components/collections/Collection.vue index 0c069a545..44caea729 100644 --- a/packages/hoppscotch-common/src/components/collections/Collection.vue +++ b/packages/hoppscotch-common/src/components/collections/Collection.vue @@ -193,7 +193,7 @@ import IconTrash2 from "~icons/lucide/trash-2" import IconEdit from "~icons/lucide/edit" import IconFolder from "~icons/lucide/folder" import IconFolderOpen from "~icons/lucide/folder-open" -import { PropType, ref, computed, watch } from "vue" +import { ref, computed, watch } from "vue" import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data" import { useI18n } from "@composables/i18n" import { TippyComponent } from "vue-tippy" @@ -209,67 +209,36 @@ type FolderType = "collection" | "folder" const t = useI18n() -const props = defineProps({ - id: { - type: String, - default: "", - required: true, - }, - parentID: { - type: String as PropType, - default: null, - required: false, - }, - data: { - type: Object as PropType | TeamCollection>, - default: () => ({}), - required: true, - }, - collectionsType: { - type: String as PropType, - default: "my-collections", - required: true, - }, - /** - * Collection component can be used for both collections and folders. - * folderType is used to determine which one it is. - */ - folderType: { - type: String as PropType, - default: "collection", - required: true, - }, - isOpen: { - type: Boolean, - default: false, - required: true, - }, - isSelected: { - type: Boolean as PropType, - default: false, - required: false, - }, - exportLoading: { - type: Boolean, - default: false, - required: false, - }, - hasNoTeamAccess: { - type: Boolean, - default: false, - required: false, - }, - collectionMoveLoading: { - type: Array as PropType, - default: () => [], - required: false, - }, - isLastItem: { - type: Boolean, - default: false, - required: false, - }, -}) +const props = withDefaults( + defineProps<{ + id: string + parentID?: string | null + data: HoppCollection | TeamCollection + /** + * Collection component can be used for both collections and folders. + * folderType is used to determine which one it is. + */ + collectionsType: CollectionType + folderType: FolderType + isOpen: boolean + isSelected?: boolean | null + exportLoading?: boolean + hasNoTeamAccess?: boolean + collectionMoveLoading?: string[] + isLastItem?: boolean + }>(), + { + id: "", + parentID: null, + collectionsType: "my-collections", + folderType: "collection", + isOpen: false, + isSelected: false, + exportLoading: false, + hasNoTeamAccess: false, + isLastItem: false, + } +) const emit = defineEmits<{ (event: "toggle-children"): void @@ -448,8 +417,13 @@ const notSameDestination = computed(() => { }) const isCollLoading = computed(() => { - if (props.collectionMoveLoading.length > 0 && props.data.id) { - return props.collectionMoveLoading.includes(props.data.id) + const { collectionMoveLoading } = props + if ( + collectionMoveLoading && + collectionMoveLoading.length > 0 && + props.data.id + ) { + return collectionMoveLoading.includes(props.data.id) } else { return false } diff --git a/packages/hoppscotch-common/src/components/smart/Tree.vue b/packages/hoppscotch-common/src/components/smart/Tree.vue index 3c9a07459..781459254 100644 --- a/packages/hoppscotch-common/src/components/smart/Tree.vue +++ b/packages/hoppscotch-common/src/components/smart/Tree.vue @@ -10,6 +10,7 @@ class="flex flex-col flex-1" > diff --git a/packages/hoppscotch-common/src/components/smart/TreeBranch.vue b/packages/hoppscotch-common/src/components/smart/TreeBranch.vue index 45366755a..9fcac990e 100644 --- a/packages/hoppscotch-common/src/components/smart/TreeBranch.vue +++ b/packages/hoppscotch-common/src/components/smart/TreeBranch.vue @@ -85,19 +85,25 @@ const props = defineProps<{ * The node item that will be used to render the tree branch content */ nodeItem: TreeNode + /** + * Total number of rootNode + */ + rootNodesLength?: number }>() const CHILD_SLOT_NAME = "default" const t = useI18n() +const isOnlyRootChild = computed(() => props.rootNodesLength === 1) + /** * Marks whether the children on this branch were ever rendered * See the usage inside '