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