import { inject, type InjectionKey } from 'vue'; import type { useCollectionState } from '@/presentation/components/Shared/Hooks/UseCollectionState'; import type { useApplication } from '@/presentation/components/Shared/Hooks/UseApplication'; import type { useRuntimeEnvironment } from '@/presentation/components/Shared/Hooks/UseRuntimeEnvironment'; import type { useClipboard } from '@/presentation/components/Shared/Hooks/Clipboard/UseClipboard'; import type { useCurrentCode } from '@/presentation/components/Shared/Hooks/UseCurrentCode'; import type { useAutoUnsubscribedEvents } from '@/presentation/components/Shared/Hooks/UseAutoUnsubscribedEvents'; import type { useUserSelectionState } from '@/presentation/components/Shared/Hooks/UseUserSelectionState'; import type { useLogger } from '@/presentation/components/Shared/Hooks/Log/UseLogger'; import type { useCodeRunner } from '@/presentation/components/Shared/Hooks/UseCodeRunner'; import type { useDialog } from '@/presentation/components/Shared/Hooks/Dialog/UseDialog'; import type { useScriptDiagnosticsCollector } from '@/presentation/components/Shared/Hooks/UseScriptDiagnosticsCollector'; import type { useAutoUnsubscribedEventListener } from '@/presentation/components/Shared/Hooks/UseAutoUnsubscribedEventListener'; export const InjectionKeys = { useCollectionState: defineTransientKey>('useCollectionState'), useApplication: defineSingletonKey>('useApplication'), useRuntimeEnvironment: defineSingletonKey>('useRuntimeEnvironment'), useAutoUnsubscribedEvents: defineTransientKey>('useAutoUnsubscribedEvents'), useClipboard: defineTransientKey>('useClipboard'), useCurrentCode: defineTransientKey>('useCurrentCode'), useUserSelectionState: defineTransientKey>('useUserSelectionState'), useLogger: defineTransientKey>('useLogger'), useCodeRunner: defineTransientKey>('useCodeRunner'), useDialog: defineTransientKey>('useDialog'), useScriptDiagnosticsCollector: defineTransientKey>('useScriptDiagnostics'), useAutoUnsubscribedEventListener: defineTransientKey>('useAutoUnsubscribedEventListener'), }; export interface InjectionKeyWithLifetime { readonly lifetime: InjectionKeyLifetime; readonly key: InjectionKey & symbol; } export interface SingletonKey extends InjectionKeyWithLifetime { readonly lifetime: InjectionKeyLifetime.Singleton; readonly key: InjectionKey & symbol; } export interface TransientKey extends InjectionKeyWithLifetime<() => T> { readonly lifetime: InjectionKeyLifetime.Transient; readonly key: InjectionKey<() => T> & symbol; } export type AnyLifetimeInjectionKey = InjectionKeyWithLifetime | TransientKey; export type InjectionKeySelector = (keys: typeof InjectionKeys) => AnyLifetimeInjectionKey; export function injectKey( keySelector: InjectionKeySelector, vueInjector = inject, ): T { const key = keySelector(InjectionKeys); const injectedValue = injectRequired(key.key, vueInjector); if (key.lifetime === InjectionKeyLifetime.Transient) { const factory = injectedValue as () => T; const value = factory(); return value; } return injectedValue as T; } export enum InjectionKeyLifetime { Singleton, Transient, } function defineSingletonKey(key: string): SingletonKey { return { lifetime: InjectionKeyLifetime.Singleton, key: Symbol(key), }; } function defineTransientKey(key: string): TransientKey { return { lifetime: InjectionKeyLifetime.Transient, key: Symbol(key), }; } function injectRequired( key: InjectionKey, vueInjector = inject, ): T { const injectedValue = vueInjector(key); if (injectedValue === undefined) { throw new Error(`Failed to inject value for key: ${key.description}`); } return injectedValue; }