import { type InjectionKey, provide, inject } from 'vue'; import { useCollectionState } from '@/presentation/components/Shared/Hooks/UseCollectionState'; import { useApplication } from '@/presentation/components/Shared/Hooks/UseApplication'; import { useAutoUnsubscribedEvents } from '@/presentation/components/Shared/Hooks/UseAutoUnsubscribedEvents'; import { useClipboard } from '@/presentation/components/Shared/Hooks/Clipboard/UseClipboard'; import { useCurrentCode } from '@/presentation/components/Shared/Hooks/UseCurrentCode'; import type { IApplicationContext } from '@/application/Context/IApplicationContext'; import { type AnyLifetimeInjectionKey, type InjectionKeySelector, InjectionKeys, type SingletonKey, type TransientKey, injectKey, } from '@/presentation/injectionSymbols'; import type { PropertyKeys } from '@/TypeHelpers'; import { useUserSelectionState } from '@/presentation/components/Shared/Hooks/UseUserSelectionState'; import { useLogger } from '@/presentation/components/Shared/Hooks/Log/UseLogger'; import { useCodeRunner } from '@/presentation/components/Shared/Hooks/UseCodeRunner'; import { CurrentEnvironment } from '@/infrastructure/RuntimeEnvironment/RuntimeEnvironmentFactory'; import { useDialog } from '@/presentation/components/Shared/Hooks/Dialog/UseDialog'; import { useScriptDiagnosticsCollector } from '@/presentation/components/Shared/Hooks/UseScriptDiagnosticsCollector'; import { useAutoUnsubscribedEventListener } from '@/presentation/components/Shared/Hooks/UseAutoUnsubscribedEventListener'; export function provideDependencies( context: IApplicationContext, api: VueDependencyInjectionApi = { provide, inject }, ) { const resolvers: Record, (di: DependencyRegistrar) => void> = { useCollectionState: (di) => di.provide( InjectionKeys.useCollectionState, () => { const { events } = di.injectKey((keys) => keys.useAutoUnsubscribedEvents); return useCollectionState(context, events); }, ), useApplication: (di) => di.provide( InjectionKeys.useApplication, useApplication(context.app), ), useRuntimeEnvironment: (di) => di.provide( InjectionKeys.useRuntimeEnvironment, CurrentEnvironment, ), useAutoUnsubscribedEvents: (di) => di.provide( InjectionKeys.useAutoUnsubscribedEvents, useAutoUnsubscribedEvents, ), useClipboard: (di) => di.provide( InjectionKeys.useClipboard, useClipboard, ), useCurrentCode: (di) => di.provide( InjectionKeys.useCurrentCode, () => { const { events } = di.injectKey((keys) => keys.useAutoUnsubscribedEvents); const state = di.injectKey((keys) => keys.useCollectionState); return useCurrentCode(state, events); }, ), useUserSelectionState: (di) => di.provide( InjectionKeys.useUserSelectionState, () => { const events = di.injectKey((keys) => keys.useAutoUnsubscribedEvents); const state = di.injectKey((keys) => keys.useCollectionState); return useUserSelectionState(state, events); }, ), useLogger: (di) => di.provide( InjectionKeys.useLogger, useLogger, ), useCodeRunner: (di) => di.provide( InjectionKeys.useCodeRunner, useCodeRunner, ), useDialog: (di) => di.provide( InjectionKeys.useDialog, useDialog, ), useScriptDiagnosticsCollector: (di) => di.provide( InjectionKeys.useScriptDiagnosticsCollector, useScriptDiagnosticsCollector, ), useAutoUnsubscribedEventListener: (di) => di.provide( InjectionKeys.useAutoUnsubscribedEventListener, useAutoUnsubscribedEventListener, ), }; registerAll(Object.values(resolvers), api); } function registerAll( registrations: ReadonlyArray<(di: DependencyRegistrar) => void>, api: VueDependencyInjectionApi, ) { const registrar = new DependencyRegistrar(api); Object.values(registrations).forEach((register) => register(registrar)); } export interface VueDependencyInjectionApi { provide(key: InjectionKey, value: T): void; inject(key: InjectionKey): T; } class DependencyRegistrar { constructor(private api: VueDependencyInjectionApi) {} public provide( key: TransientKey, resolver: () => T, ): void; public provide( key: SingletonKey, resolver: T, ): void; public provide( key: AnyLifetimeInjectionKey, resolver: T | (() => T), ): void { this.api.provide(key.key, resolver); } public injectKey(key: InjectionKeySelector): T { const injector = this.api.inject.bind(this.api); return injectKey(key, injector); } }