@onerjs/shared-ui-components 8.48.4 → 8.48.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fluent/hoc/buttonLine.js +2 -1
- package/fluent/hoc/buttonLine.js.map +1 -1
- package/fluent/primitives/contextMenu.js.map +1 -1
- package/fluent/primitives/popover.js.map +1 -1
- package/modularTool/components/errorBoundary.d.ts +31 -0
- package/modularTool/components/errorBoundary.js +91 -0
- package/modularTool/components/errorBoundary.js.map +1 -0
- package/modularTool/components/extensibleAccordion.d.ts +67 -0
- package/modularTool/components/extensibleAccordion.js +148 -0
- package/modularTool/components/extensibleAccordion.js.map +1 -0
- package/modularTool/components/pane.d.ts +4 -0
- package/modularTool/components/pane.js +20 -0
- package/modularTool/components/pane.js.map +1 -0
- package/modularTool/components/teachingMoment.d.ts +20 -0
- package/modularTool/components/teachingMoment.js +17 -0
- package/modularTool/components/teachingMoment.js.map +1 -0
- package/modularTool/components/theme.d.ts +10 -0
- package/modularTool/components/theme.js +24 -0
- package/modularTool/components/theme.js.map +1 -0
- package/modularTool/components/uxContextProvider.d.ts +2 -0
- package/modularTool/components/uxContextProvider.js +19 -0
- package/modularTool/components/uxContextProvider.js.map +1 -0
- package/modularTool/contexts/extensionManagerContext.d.ts +6 -0
- package/modularTool/contexts/extensionManagerContext.js +6 -0
- package/modularTool/contexts/extensionManagerContext.js.map +1 -0
- package/modularTool/contexts/settingsContext.d.ts +3 -0
- package/modularTool/contexts/settingsContext.js +6 -0
- package/modularTool/contexts/settingsContext.js.map +1 -0
- package/modularTool/extensibility/builtInsExtensionFeed.d.ts +21 -0
- package/modularTool/extensibility/builtInsExtensionFeed.js +26 -0
- package/modularTool/extensibility/builtInsExtensionFeed.js.map +1 -0
- package/modularTool/extensibility/extensionFeed.d.ts +113 -0
- package/modularTool/extensibility/extensionFeed.js +2 -0
- package/modularTool/extensibility/extensionFeed.js.map +1 -0
- package/modularTool/extensibility/extensionManager.d.ts +111 -0
- package/modularTool/extensibility/extensionManager.js +277 -0
- package/modularTool/extensibility/extensionManager.js.map +1 -0
- package/modularTool/hooks/observableHooks.d.ts +35 -0
- package/modularTool/hooks/observableHooks.js +84 -0
- package/modularTool/hooks/observableHooks.js.map +1 -0
- package/modularTool/hooks/resourceHooks.d.ts +20 -0
- package/modularTool/hooks/resourceHooks.js +101 -0
- package/modularTool/hooks/resourceHooks.js.map +1 -0
- package/modularTool/hooks/settingsHooks.d.ts +8 -0
- package/modularTool/hooks/settingsHooks.js +40 -0
- package/modularTool/hooks/settingsHooks.js.map +1 -0
- package/modularTool/hooks/teachingMomentHooks.d.ts +34 -0
- package/modularTool/hooks/teachingMomentHooks.js +89 -0
- package/modularTool/hooks/teachingMomentHooks.js.map +1 -0
- package/modularTool/hooks/themeHooks.d.ts +17 -0
- package/modularTool/hooks/themeHooks.js +38 -0
- package/modularTool/hooks/themeHooks.js.map +1 -0
- package/modularTool/hooks/useResizeHandle.d.ts +35 -0
- package/modularTool/hooks/useResizeHandle.js +75 -0
- package/modularTool/hooks/useResizeHandle.js.map +1 -0
- package/modularTool/misc/assert.d.ts +5 -0
- package/modularTool/misc/assert.js +10 -0
- package/modularTool/misc/assert.js.map +1 -0
- package/modularTool/misc/graphUtils.d.ts +44 -0
- package/modularTool/misc/graphUtils.js +90 -0
- package/modularTool/misc/graphUtils.js.map +1 -0
- package/modularTool/misc/observableCollection.d.ts +23 -0
- package/modularTool/misc/observableCollection.js +43 -0
- package/modularTool/misc/observableCollection.js.map +1 -0
- package/modularTool/modularTool.d.ts +42 -0
- package/modularTool/modularTool.js +223 -0
- package/modularTool/modularTool.js.map +1 -0
- package/modularTool/modularity/serviceContainer.d.ts +64 -0
- package/modularTool/modularity/serviceContainer.js +181 -0
- package/modularTool/modularity/serviceContainer.js.map +1 -0
- package/modularTool/modularity/serviceDefinition.d.ts +64 -0
- package/modularTool/modularity/serviceDefinition.js +11 -0
- package/modularTool/modularity/serviceDefinition.js.map +1 -0
- package/modularTool/services/extensionsListService.d.ts +3 -0
- package/modularTool/services/extensionsListService.js +202 -0
- package/modularTool/services/extensionsListService.js.map +1 -0
- package/modularTool/services/globalSettings.d.ts +3 -0
- package/modularTool/services/globalSettings.js +9 -0
- package/modularTool/services/globalSettings.js.map +1 -0
- package/modularTool/services/reactContextService.d.ts +18 -0
- package/modularTool/services/reactContextService.js +5 -0
- package/modularTool/services/reactContextService.js.map +1 -0
- package/modularTool/services/settingsService.d.ts +24 -0
- package/modularTool/services/settingsService.js +41 -0
- package/modularTool/services/settingsService.js.map +1 -0
- package/modularTool/services/settingsStore.d.ts +55 -0
- package/modularTool/services/settingsStore.js +35 -0
- package/modularTool/services/settingsStore.js.map +1 -0
- package/modularTool/services/shellService.d.ts +256 -0
- package/modularTool/services/shellService.js +729 -0
- package/modularTool/services/shellService.js.map +1 -0
- package/modularTool/services/shellSettingsService.d.ts +3 -0
- package/modularTool/services/shellSettingsService.js +35 -0
- package/modularTool/services/shellSettingsService.js.map +1 -0
- package/modularTool/services/themeSelectorService.d.ts +3 -0
- package/modularTool/services/themeSelectorService.js +42 -0
- package/modularTool/services/themeSelectorService.js.map +1 -0
- package/modularTool/services/themeService.d.ts +60 -0
- package/modularTool/services/themeService.js +69 -0
- package/modularTool/services/themeService.js.map +1 -0
- package/modularTool/themes/babylonTheme.d.ts +3 -0
- package/modularTool/themes/babylonTheme.js +36 -0
- package/modularTool/themes/babylonTheme.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modularTool.js","sourceRoot":"","sources":["../../../../dev/sharedUiComponents/src/modularTool/modularTool.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\r\n type ComponentType,\r\n type Context,\r\n type FunctionComponent,\r\n type PropsWithChildren,\r\n type ReactNode,\r\n createElement,\r\n Suspense,\r\n useCallback,\r\n useEffect,\r\n useReducer,\r\n useState,\r\n} from \"react\";\r\n\r\nimport { type IDisposable } from \"core/index\";\r\nimport { type IExtensionFeed } from \"./extensibility/extensionFeed\";\r\nimport { type IExtension, type InstallFailedInfo, ExtensionManager } from \"./extensibility/extensionManager\";\r\nimport { type WeaklyTypedServiceDefinition, ServiceContainer } from \"./modularity/serviceContainer\";\r\nimport { type ISettingsStore, SettingsStore, SettingsStoreIdentity } from \"./services/settingsStore\";\r\nimport { type IRootComponentService, type ShellServiceOptions, MakeShellServiceDefinition, RootComponentServiceIdentity } from \"./services/shellService\";\r\nimport { type ThemeMode, ThemeModeSettingDescriptor, ThemeServiceDefinition } from \"./services/themeService\";\r\n\r\nimport {\r\n Body1,\r\n Button,\r\n Dialog,\r\n DialogActions,\r\n DialogBody,\r\n DialogContent,\r\n DialogSurface,\r\n DialogTitle,\r\n List,\r\n ListItem,\r\n makeStyles,\r\n Spinner,\r\n tokens,\r\n} from \"@fluentui/react-components\";\r\nimport { ErrorCircleRegular } from \"@fluentui/react-icons\";\r\nimport { createRoot } from \"react-dom/client\";\r\n\r\nimport { Deferred } from \"core/Misc/deferred\";\r\nimport { Logger } from \"core/Misc/logger\";\r\nimport { ToastProvider } from \"shared-ui-components/fluent/primitives/toast\";\r\nimport { Theme } from \"./components/theme\";\r\nimport { ExtensionManagerContext } from \"./contexts/extensionManagerContext\";\r\nimport { SettingsStoreContext } from \"./contexts/settingsContext\";\r\nimport { type IReactContextService, type ReactContextHandle, ReactContextServiceIdentity } from \"./services/reactContextService\";\r\nimport { ThemeSelectorServiceDefinition } from \"./services/themeSelectorService\";\r\n\r\nconst useStyles = makeStyles({\r\n app: {\r\n colorScheme: \"light dark\",\r\n flexGrow: 1,\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n backgroundColor: tokens.colorTransparentBackground,\r\n },\r\n spinner: {\r\n flexGrow: 1,\r\n animationDuration: \"1s\",\r\n animationName: {\r\n from: { opacity: 0 },\r\n to: { opacity: 1 },\r\n },\r\n },\r\n extensionErrorTitleDiv: {\r\n display: \"flex\",\r\n flexDirection: \"row\",\r\n alignItems: \"center\",\r\n gap: tokens.spacingHorizontalS,\r\n },\r\n extensionErrorIcon: {\r\n color: tokens.colorPaletteRedForeground1,\r\n },\r\n});\r\n\r\ntype ReactContextEntry = {\r\n provider: Context<unknown>[\"Provider\"];\r\n value: unknown;\r\n order: number;\r\n};\r\n\r\ntype ReactContextAction =\r\n | { type: \"add\"; entry: ReactContextEntry }\r\n | { type: \"remove\"; provider: Context<unknown>[\"Provider\"] }\r\n | { type: \"update\"; provider: Context<unknown>[\"Provider\"]; value: unknown };\r\n\r\nconst ReactContextsWrapper: FunctionComponent<PropsWithChildren<{ contexts: readonly Readonly<ReactContextEntry>[] }>> = ({ contexts, children }) => {\r\n return <>{contexts.reduceRight<ReactNode>((acc, entry) => createElement(entry.provider, { value: entry.value }, acc), children)}</>;\r\n};\r\n\r\nexport type ModularToolOptions = {\r\n /**\r\n * The namespace for the tool, used for scoping persisted settings and other storage.\r\n */\r\n namespace: string;\r\n\r\n /**\r\n * The container element where the tool will be rendered.\r\n */\r\n containerElement: HTMLElement;\r\n\r\n /**\r\n * The service definitions to be registered with the tool.\r\n */\r\n serviceDefinitions: readonly WeaklyTypedServiceDefinition[];\r\n\r\n /**\r\n * The theme mode to use. If not specified, the default is \"system\", which uses the system/browser preference, and the last used mode is persisted.\r\n */\r\n themeMode?: ThemeMode;\r\n\r\n /**\r\n * Whether to show the theme selector in the toolbar. Default is true.\r\n */\r\n showThemeSelector?: boolean;\r\n\r\n /**\r\n * The extension feeds that provide optional extensions the user can install.\r\n */\r\n extensionFeeds?: readonly IExtensionFeed[];\r\n\r\n /**\r\n * An optional parent ServiceContainer. Dependencies not found in the tool's own container\r\n * will be resolved from this parent.\r\n */\r\n parentContainer?: ServiceContainer;\r\n} & ShellServiceOptions;\r\n\r\n/**\r\n * Creates a modular tool with a base set of common tool services, including the toolbar/side pane basic UI layout.\r\n * @param options The options for the tool.\r\n * @returns A token that can be used to dispose of the tool.\r\n */\r\nexport function MakeModularTool(options: ModularToolOptions): IDisposable {\r\n const { namespace, containerElement, serviceDefinitions, themeMode, showThemeSelector = true, extensionFeeds = [], parentContainer } = options;\r\n\r\n // Create the settings store immediately as it will be exposed to services and through React context.\r\n const settingsStore = new SettingsStore(namespace);\r\n\r\n // If a theme mode is provided, just write the setting so it is the active theme.\r\n if (themeMode) {\r\n settingsStore.writeSetting(ThemeModeSettingDescriptor, themeMode);\r\n }\r\n\r\n const modularToolRootComponent: FunctionComponent = () => {\r\n const classes = useStyles();\r\n const [extensionManagerContext, setExtensionManagerContext] = useState<ExtensionManagerContext>();\r\n const [requiredExtensions, setRequiredExtensions] = useState<string[]>();\r\n const [requiredExtensionsDeferred, setRequiredExtensionsDeferred] = useState<Deferred<boolean>>();\r\n const [extensionInstallError, setExtensionInstallError] = useState<InstallFailedInfo>();\r\n\r\n const [rootComponentService, setRootComponentService] = useState<IRootComponentService>();\r\n\r\n const [contexts, updateContexts] = useReducer((state: ReactContextEntry[], action: ReactContextAction): ReactContextEntry[] => {\r\n switch (action.type) {\r\n case \"add\":\r\n return [...state, action.entry].sort((a, b) => a.order - b.order);\r\n case \"remove\":\r\n return state.filter((e) => e.provider !== action.provider);\r\n case \"update\":\r\n return state.map((e) => (e.provider === action.provider ? { ...e, value: action.value } : e));\r\n }\r\n }, []);\r\n\r\n // This is the main async initialization.\r\n useEffect(() => {\r\n const initializeExtensionManagerAsync = async () => {\r\n const serviceContainer = new ServiceContainer(\"ModularToolContainer\", parentContainer);\r\n\r\n // Expose the settings store as a service so other services can read/write settings.\r\n await serviceContainer.addServiceAsync<[ISettingsStore], []>({\r\n friendlyName: \"Settings Store\",\r\n produces: [SettingsStoreIdentity],\r\n factory: () => settingsStore,\r\n });\r\n\r\n // Expose the react context service so other services can add React context providers.\r\n await serviceContainer.addServiceAsync<[IReactContextService], []>({\r\n friendlyName: \"React Context Service\",\r\n produces: [ReactContextServiceIdentity],\r\n factory: (): IReactContextService => ({\r\n addContext<T>(provider: Context<T>[\"Provider\"], initialValue: T, options?: { order?: number }): ReactContextHandle<T> {\r\n const typedProvider = provider as Context<unknown>[\"Provider\"];\r\n updateContexts({ type: \"add\", entry: { provider: typedProvider, value: initialValue, order: options?.order ?? 0 } });\r\n return {\r\n updateValue: (newValue: T) => {\r\n updateContexts({ type: \"update\", provider: typedProvider, value: newValue });\r\n },\r\n dispose: () => {\r\n updateContexts({ type: \"remove\", provider: typedProvider });\r\n },\r\n };\r\n },\r\n }),\r\n });\r\n\r\n // Register the shell service (top level toolbar/side pane UI layout).\r\n await serviceContainer.addServiceAsync(MakeShellServiceDefinition(options));\r\n\r\n // Register a service that simply consumes the services we need before first render.\r\n await serviceContainer.addServiceAsync<[], [IRootComponentService]>({\r\n friendlyName: \"Service Bootstrapper\",\r\n consumes: [RootComponentServiceIdentity],\r\n factory: (rootComponent) => {\r\n // Use function syntax for the state setter since the root component may be a function component.\r\n setRootComponentService(() => rootComponent);\r\n return {\r\n dispose: () => setRootComponentService(undefined),\r\n };\r\n },\r\n });\r\n\r\n // Register the theme service (exposes the current theme to other services).\r\n await serviceContainer.addServiceAsync(ThemeServiceDefinition);\r\n\r\n // Register the theme selector service (for selecting the theme) if theming is configured.\r\n if (showThemeSelector) {\r\n await serviceContainer.addServiceAsync(ThemeSelectorServiceDefinition);\r\n }\r\n\r\n // Register the extension list service (for browsing/installing extensions) if extension feeds are provided.\r\n if (extensionFeeds.length > 0) {\r\n const { ExtensionListServiceDefinition } = await import(\"./services/extensionsListService\");\r\n await serviceContainer.addServiceAsync(ExtensionListServiceDefinition);\r\n }\r\n\r\n // Register all external services (that make up a unique tool).\r\n await serviceContainer.addServicesAsync(...serviceDefinitions);\r\n\r\n // Create the extension manager, passing along the registry for runtime changes to the registered services.\r\n const extensionManager = await ExtensionManager.CreateAsync(namespace, serviceContainer, extensionFeeds, setExtensionInstallError);\r\n\r\n // Check query params for required extensions. This lets users share links with sets of extensions.\r\n const queryParams = new URLSearchParams(window.location.search);\r\n const requiredExtensions = queryParams.getAll(\"babylon.requiredExtension\");\r\n const uninstalledExtensions: IExtension[] = [];\r\n for (const requiredExtension of requiredExtensions) {\r\n // These could possibly be parallelized to speed things up, but it's more complex so let's wait and see if we need it.\r\n // eslint-disable-next-line no-await-in-loop\r\n const query = await extensionManager.queryExtensionsAsync(requiredExtension);\r\n // eslint-disable-next-line no-await-in-loop\r\n const extensions = await query.getExtensionsAsync(0, query.totalCount);\r\n for (const extension of extensions) {\r\n if (!extension.isInstalled) {\r\n uninstalledExtensions.push(extension);\r\n }\r\n }\r\n }\r\n\r\n // Check if any required extensions are uninstalled or disabled. If so, show a dialog to the user.\r\n if (uninstalledExtensions.length > 0) {\r\n setRequiredExtensions(uninstalledExtensions.map((extension) => extension.metadata.name));\r\n const deferred = new Deferred<boolean>();\r\n setRequiredExtensionsDeferred(deferred);\r\n if (await deferred.promise) {\r\n for (const extension of uninstalledExtensions) {\r\n // This could possibly be parallelized to speed things up, but it's more complex so let's wait and see if we need it.\r\n // eslint-disable-next-line no-await-in-loop\r\n await extension.installAsync();\r\n }\r\n }\r\n }\r\n\r\n // Set the contexts.\r\n setExtensionManagerContext({ extensionManager });\r\n\r\n return () => {\r\n extensionManager.dispose();\r\n serviceContainer.dispose();\r\n serviceContainer.dispose();\r\n };\r\n };\r\n\r\n const disposePromise = initializeExtensionManagerAsync();\r\n\r\n return () => {\r\n disposePromise\r\n // eslint-disable-next-line github/no-then\r\n .then((dispose) => dispose())\r\n // eslint-disable-next-line github/no-then\r\n .catch((error) => {\r\n Logger.Error(`Failed to dispose of the modular tool: ${error}`);\r\n });\r\n };\r\n }, []);\r\n\r\n const onAcceptRequiredExtensions = useCallback(() => {\r\n setRequiredExtensions(undefined);\r\n requiredExtensionsDeferred?.resolve(true);\r\n }, [setRequiredExtensions, requiredExtensionsDeferred]);\r\n\r\n const onRejectRequiredExtensions = useCallback(() => {\r\n setRequiredExtensions(undefined);\r\n requiredExtensionsDeferred?.resolve(false);\r\n }, [setRequiredExtensions, requiredExtensionsDeferred]);\r\n\r\n const onAcknowledgedExtensionInstallError = useCallback(() => {\r\n setExtensionInstallError(undefined);\r\n }, [setExtensionInstallError]);\r\n\r\n // Show a spinner until a main view has been set.\r\n if (!rootComponentService) {\r\n return (\r\n <ReactContextsWrapper contexts={contexts}>\r\n <SettingsStoreContext.Provider value={settingsStore}>\r\n <Theme className={classes.app}>\r\n <Spinner className={classes.spinner} />\r\n </Theme>\r\n </SettingsStoreContext.Provider>\r\n </ReactContextsWrapper>\r\n );\r\n } else {\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n const Content: ComponentType = rootComponentService.rootComponent;\r\n\r\n return (\r\n <ReactContextsWrapper contexts={contexts}>\r\n {/* Expose the settings store as a React context so that UI components can read/write\r\n settings without the ISettingsService needing to be explicitly passed around. */}\r\n <SettingsStoreContext.Provider value={settingsStore}>\r\n <ExtensionManagerContext.Provider value={extensionManagerContext}>\r\n <Theme className={classes.app}>\r\n <ToastProvider>\r\n <Dialog open={!!requiredExtensions} modalType=\"alert\">\r\n <DialogSurface>\r\n <DialogBody>\r\n <DialogTitle>Required Extensions</DialogTitle>\r\n <DialogContent>\r\n Opening this URL requires the following extensions to be installed and enabled:\r\n <ul>\r\n {requiredExtensions?.map((name) => (\r\n <li key={name}>{name}</li>\r\n ))}\r\n </ul>\r\n </DialogContent>\r\n <DialogActions>\r\n <Button appearance=\"primary\" onClick={onAcceptRequiredExtensions}>\r\n Install & Enable\r\n </Button>\r\n <Button appearance=\"secondary\" onClick={onRejectRequiredExtensions}>\r\n No Thanks\r\n </Button>\r\n </DialogActions>\r\n </DialogBody>\r\n </DialogSurface>\r\n </Dialog>\r\n <Dialog open={!!extensionInstallError} modalType=\"alert\">\r\n <DialogSurface>\r\n <DialogBody>\r\n <DialogTitle>\r\n <div className={classes.extensionErrorTitleDiv}>\r\n Extension Install Error\r\n <ErrorCircleRegular className={classes.extensionErrorIcon} />\r\n </div>\r\n </DialogTitle>\r\n <DialogContent>\r\n <List>\r\n <ListItem>\r\n <Body1>{`Extension \"${extensionInstallError?.extension.name}\" failed to install and was removed.`}</Body1>\r\n </ListItem>\r\n <ListItem>\r\n <Body1>{`${extensionInstallError?.error}`}</Body1>\r\n </ListItem>\r\n </List>\r\n </DialogContent>\r\n <DialogActions>\r\n <Button appearance=\"primary\" onClick={onAcknowledgedExtensionInstallError}>\r\n Close\r\n </Button>\r\n </DialogActions>\r\n </DialogBody>\r\n </DialogSurface>\r\n </Dialog>\r\n <Suspense fallback={<Spinner className={classes.spinner} />}>\r\n <Content />\r\n </Suspense>\r\n </ToastProvider>\r\n </Theme>\r\n </ExtensionManagerContext.Provider>\r\n </SettingsStoreContext.Provider>\r\n </ReactContextsWrapper>\r\n );\r\n }\r\n };\r\n\r\n // Set the container element to be a flex container so that the tool can be displayed properly.\r\n const originalContainerElementDisplay = containerElement.style.display;\r\n containerElement.style.display = \"flex\";\r\n\r\n // Create and render the react root component.\r\n const reactRoot = createRoot(containerElement);\r\n reactRoot.render(createElement(modularToolRootComponent));\r\n\r\n let disposed = false;\r\n return {\r\n dispose: () => {\r\n // Unmount and restore the original container element display.\r\n if (!disposed) {\r\n disposed = true;\r\n reactRoot.unmount();\r\n containerElement.style.display = originalContainerElementDisplay;\r\n }\r\n },\r\n };\r\n}\r\n"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type IDisposable } from "@onerjs/core/index.js";
|
|
2
|
+
import { type IService, type ServiceDefinition } from "./serviceDefinition.js";
|
|
3
|
+
export type WeaklyTypedServiceDefinition = Omit<ServiceDefinition<IService<symbol>[] | [], IService<symbol>[] | []>, "factory"> & {
|
|
4
|
+
/**
|
|
5
|
+
* A factory function responsible for creating a service instance.
|
|
6
|
+
*/
|
|
7
|
+
factory: (...args: any) => ReturnType<ServiceDefinition<IService<symbol>[] | [], IService<symbol>[] | []>["factory"]>;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* A service container manages the lifetimes of a set of services.
|
|
11
|
+
* It takes care of instantiating the services in the correct order based on their dependencies,
|
|
12
|
+
* passing dependencies through to services, and disposing of services when the container is disposed.
|
|
13
|
+
*/
|
|
14
|
+
export declare class ServiceContainer implements IDisposable {
|
|
15
|
+
private readonly _friendlyName;
|
|
16
|
+
private readonly _parent?;
|
|
17
|
+
private _isDisposed;
|
|
18
|
+
private readonly _serviceDefinitions;
|
|
19
|
+
private readonly _serviceDependents;
|
|
20
|
+
private readonly _serviceInstances;
|
|
21
|
+
private readonly _children;
|
|
22
|
+
/**
|
|
23
|
+
* Creates a new ServiceContainer.
|
|
24
|
+
* @param _friendlyName A human-readable name for debugging.
|
|
25
|
+
* @param _parent An optional parent container. Dependencies not found locally will be resolved from the parent.
|
|
26
|
+
*/
|
|
27
|
+
constructor(_friendlyName: string, _parent?: ServiceContainer | undefined);
|
|
28
|
+
/**
|
|
29
|
+
* Adds a set of service definitions in the service container.
|
|
30
|
+
* The services are sorted based on their dependencies.
|
|
31
|
+
* @param args The service definitions to register, and optionally an abort signal.
|
|
32
|
+
* @returns A disposable that will remove the service definition from the service container.
|
|
33
|
+
*/
|
|
34
|
+
addServicesAsync(...args: WeaklyTypedServiceDefinition[] | [...serviceDefinitions: WeaklyTypedServiceDefinition[], abortSignal: AbortSignal]): Promise<IDisposable>;
|
|
35
|
+
/**
|
|
36
|
+
* Registers a service definition in the service container.
|
|
37
|
+
* @param serviceDefinition The service definition to register.
|
|
38
|
+
* @param abortSignal An optional abort signal.
|
|
39
|
+
* @returns A disposable that will remove the service definition from the service container.
|
|
40
|
+
*/
|
|
41
|
+
addServiceAsync<Produces extends IService<symbol>[] = [], Consumes extends IService<symbol>[] = []>(serviceDefinition: ServiceDefinition<Produces, Consumes>, abortSignal?: AbortSignal): Promise<IDisposable>;
|
|
42
|
+
private _addServiceAsync;
|
|
43
|
+
/**
|
|
44
|
+
* Resolves a dependency by contract identity for a consuming service.
|
|
45
|
+
* Checks local services first, then walks up the parent chain.
|
|
46
|
+
* Registers the consumer as a dependent in whichever container owns the dependency.
|
|
47
|
+
* @param contract The contract identity to resolve.
|
|
48
|
+
* @param consumer The service definition that consumes this dependency.
|
|
49
|
+
* @returns The resolved service instance.
|
|
50
|
+
*/
|
|
51
|
+
private _resolveDependency;
|
|
52
|
+
/**
|
|
53
|
+
* Removes a consumer from the dependent set for a given contract, checking locally first then the parent chain.
|
|
54
|
+
* @param contract The contract identity.
|
|
55
|
+
* @param consumer The service definition to remove as a dependent.
|
|
56
|
+
*/
|
|
57
|
+
private _removeDependentFromChain;
|
|
58
|
+
private _removeService;
|
|
59
|
+
/**
|
|
60
|
+
* Disposes the service container and all contained services.
|
|
61
|
+
* Throws if this container is still a parent of any live child containers.
|
|
62
|
+
*/
|
|
63
|
+
dispose(): void;
|
|
64
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { SortGraph } from "../misc/graphUtils.js";
|
|
2
|
+
// This sorts a set of service definitions based on their dependencies (e.g. a topological sort).
|
|
3
|
+
function SortServiceDefinitions(serviceDefinitions) {
|
|
4
|
+
const sortedServiceDefinitions = [];
|
|
5
|
+
SortGraph(serviceDefinitions, function* (serviceDefinition) {
|
|
6
|
+
// Check each dependency.
|
|
7
|
+
for (const contractIdentity of serviceDefinition.consumes ?? []) {
|
|
8
|
+
// If another service definition produces the dependency contract, return it as an adjacent node for the purpose of sorting.
|
|
9
|
+
yield* serviceDefinitions.filter((otherServiceDefinition) => (otherServiceDefinition.produces ?? []).includes(contractIdentity));
|
|
10
|
+
}
|
|
11
|
+
}, sortedServiceDefinitions.push.bind(sortedServiceDefinitions));
|
|
12
|
+
return sortedServiceDefinitions;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A service container manages the lifetimes of a set of services.
|
|
16
|
+
* It takes care of instantiating the services in the correct order based on their dependencies,
|
|
17
|
+
* passing dependencies through to services, and disposing of services when the container is disposed.
|
|
18
|
+
*/
|
|
19
|
+
export class ServiceContainer {
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new ServiceContainer.
|
|
22
|
+
* @param _friendlyName A human-readable name for debugging.
|
|
23
|
+
* @param _parent An optional parent container. Dependencies not found locally will be resolved from the parent.
|
|
24
|
+
*/
|
|
25
|
+
constructor(_friendlyName, _parent) {
|
|
26
|
+
this._friendlyName = _friendlyName;
|
|
27
|
+
this._parent = _parent;
|
|
28
|
+
this._isDisposed = false;
|
|
29
|
+
this._serviceDefinitions = new Map();
|
|
30
|
+
this._serviceDependents = new Map();
|
|
31
|
+
this._serviceInstances = new Map();
|
|
32
|
+
this._children = new Set();
|
|
33
|
+
_parent?._children.add(this);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Adds a set of service definitions in the service container.
|
|
37
|
+
* The services are sorted based on their dependencies.
|
|
38
|
+
* @param args The service definitions to register, and optionally an abort signal.
|
|
39
|
+
* @returns A disposable that will remove the service definition from the service container.
|
|
40
|
+
*/
|
|
41
|
+
async addServicesAsync(...args) {
|
|
42
|
+
if (this._isDisposed) {
|
|
43
|
+
throw new Error("ServiceContainer is disposed.");
|
|
44
|
+
}
|
|
45
|
+
const abortSignal = args[args.length - 1] instanceof AbortSignal ? args.pop() : undefined;
|
|
46
|
+
const serviceDefinitions = args;
|
|
47
|
+
const sortedServiceDefinitions = SortServiceDefinitions(serviceDefinitions);
|
|
48
|
+
const dispose = () => {
|
|
49
|
+
for (const serviceDefinition of sortedServiceDefinitions.reverse()) {
|
|
50
|
+
this._removeService(serviceDefinition);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
try {
|
|
54
|
+
for (const serviceDefinition of sortedServiceDefinitions) {
|
|
55
|
+
// We could possibly optimize this by allowing some parallel initialization of services, but this would be way more complex, so let's wait and see if it's needed.
|
|
56
|
+
// eslint-disable-next-line no-await-in-loop
|
|
57
|
+
await this._addServiceAsync(serviceDefinition, abortSignal);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
dispose();
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
dispose,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Registers a service definition in the service container.
|
|
70
|
+
* @param serviceDefinition The service definition to register.
|
|
71
|
+
* @param abortSignal An optional abort signal.
|
|
72
|
+
* @returns A disposable that will remove the service definition from the service container.
|
|
73
|
+
*/
|
|
74
|
+
async addServiceAsync(serviceDefinition, abortSignal) {
|
|
75
|
+
if (abortSignal) {
|
|
76
|
+
return await this.addServicesAsync(serviceDefinition, abortSignal);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
return await this.addServicesAsync(serviceDefinition);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async _addServiceAsync(service, abortSignal) {
|
|
83
|
+
if (this._isDisposed) {
|
|
84
|
+
throw new Error(`'${this._friendlyName}' container is disposed.`);
|
|
85
|
+
}
|
|
86
|
+
service.produces?.forEach((contract) => {
|
|
87
|
+
if (this._serviceDefinitions.has(contract)) {
|
|
88
|
+
throw new Error(`A service producing the contract '${contract.toString()}' has already been added to this '${this._friendlyName}' container.`);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
service.produces?.forEach((contract) => {
|
|
92
|
+
this._serviceDefinitions.set(contract, service);
|
|
93
|
+
});
|
|
94
|
+
const dependencies = service.consumes?.map((contract) => this._resolveDependency(contract, service)) ?? [];
|
|
95
|
+
this._serviceInstances.set(service, await service.factory(...dependencies, abortSignal));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Resolves a dependency by contract identity for a consuming service.
|
|
99
|
+
* Checks local services first, then walks up the parent chain.
|
|
100
|
+
* Registers the consumer as a dependent in whichever container owns the dependency.
|
|
101
|
+
* @param contract The contract identity to resolve.
|
|
102
|
+
* @param consumer The service definition that consumes this dependency.
|
|
103
|
+
* @returns The resolved service instance.
|
|
104
|
+
*/
|
|
105
|
+
_resolveDependency(contract, consumer) {
|
|
106
|
+
const definition = this._serviceDefinitions.get(contract);
|
|
107
|
+
if (definition) {
|
|
108
|
+
let dependentDefinitions = this._serviceDependents.get(definition);
|
|
109
|
+
if (!dependentDefinitions) {
|
|
110
|
+
this._serviceDependents.set(definition, (dependentDefinitions = new Set()));
|
|
111
|
+
}
|
|
112
|
+
dependentDefinitions.add(consumer);
|
|
113
|
+
const instance = this._serviceInstances.get(definition);
|
|
114
|
+
if (!instance) {
|
|
115
|
+
throw new Error(`Service '${contract.toString()}' has not been instantiated in the '${this._friendlyName}' container.`);
|
|
116
|
+
}
|
|
117
|
+
return instance;
|
|
118
|
+
}
|
|
119
|
+
if (this._parent) {
|
|
120
|
+
return this._parent._resolveDependency(contract, consumer);
|
|
121
|
+
}
|
|
122
|
+
throw new Error(`Service '${contract.toString()}' has not been registered in the '${this._friendlyName}' container.`);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Removes a consumer from the dependent set for a given contract, checking locally first then the parent chain.
|
|
126
|
+
* @param contract The contract identity.
|
|
127
|
+
* @param consumer The service definition to remove as a dependent.
|
|
128
|
+
*/
|
|
129
|
+
_removeDependentFromChain(contract, consumer) {
|
|
130
|
+
const definition = this._serviceDefinitions.get(contract);
|
|
131
|
+
if (definition) {
|
|
132
|
+
const dependentDefinitions = this._serviceDependents.get(definition);
|
|
133
|
+
if (dependentDefinitions) {
|
|
134
|
+
dependentDefinitions.delete(consumer);
|
|
135
|
+
if (dependentDefinitions.size === 0) {
|
|
136
|
+
this._serviceDependents.delete(definition);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
this._parent?._removeDependentFromChain(contract, consumer);
|
|
142
|
+
}
|
|
143
|
+
_removeService(service) {
|
|
144
|
+
if (this._isDisposed) {
|
|
145
|
+
throw new Error(`'${this._friendlyName}' container is disposed.`);
|
|
146
|
+
}
|
|
147
|
+
const serviceDependents = this._serviceDependents.get(service);
|
|
148
|
+
if (serviceDependents && serviceDependents.size > 0) {
|
|
149
|
+
throw new Error(`Service '${service.friendlyName}' has dependents: ${Array.from(serviceDependents)
|
|
150
|
+
.map((dependent) => dependent.friendlyName)
|
|
151
|
+
.join(", ")}`);
|
|
152
|
+
}
|
|
153
|
+
// NOTE: The service instance could be undefined, as the service factory for a service that does not produce any contracts is not required to return an actual service instance.
|
|
154
|
+
const serviceInstance = this._serviceInstances.get(service);
|
|
155
|
+
this._serviceInstances.delete(service);
|
|
156
|
+
serviceInstance?.dispose?.();
|
|
157
|
+
service.produces?.forEach((contract) => {
|
|
158
|
+
this._serviceDefinitions.delete(contract);
|
|
159
|
+
});
|
|
160
|
+
// Remove this service as a dependent from each of its consumed dependencies (local or in parent chain).
|
|
161
|
+
service.consumes?.forEach((contract) => {
|
|
162
|
+
this._removeDependentFromChain(contract, service);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Disposes the service container and all contained services.
|
|
167
|
+
* Throws if this container is still a parent of any live child containers.
|
|
168
|
+
*/
|
|
169
|
+
dispose() {
|
|
170
|
+
if (this._children.size > 0) {
|
|
171
|
+
throw new Error(`'${this._friendlyName}' container cannot be disposed because it has ${this._children.size} active child container(s).`);
|
|
172
|
+
}
|
|
173
|
+
Array.from(this._serviceInstances.keys()).reverse().forEach(this._removeService.bind(this));
|
|
174
|
+
this._serviceInstances.clear();
|
|
175
|
+
this._serviceDependents.clear();
|
|
176
|
+
this._serviceDefinitions.clear();
|
|
177
|
+
this._parent?._children.delete(this);
|
|
178
|
+
this._isDisposed = true;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=serviceContainer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceContainer.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/modularity/serviceContainer.ts"],"names":[],"mappings":"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","sourcesContent":["import { type IDisposable } from \"core/index\";\r\n\r\nimport { type IService, type ServiceDefinition } from \"./serviceDefinition\";\r\n\r\nimport { SortGraph } from \"../misc/graphUtils\";\r\n\r\n// Service definitions should strongly typed when they are defined to avoid programming errors. However, they are tracked internally\r\n// in a weakly typed manner so they can be handled generically.\r\nexport type WeaklyTypedServiceDefinition = Omit<ServiceDefinition<IService<symbol>[] | [], IService<symbol>[] | []>, \"factory\"> & {\r\n /**\r\n * A factory function responsible for creating a service instance.\r\n */\r\n factory: (...args: any) => ReturnType<ServiceDefinition<IService<symbol>[] | [], IService<symbol>[] | []>[\"factory\"]>;\r\n};\r\n\r\n// This sorts a set of service definitions based on their dependencies (e.g. a topological sort).\r\nfunction SortServiceDefinitions(serviceDefinitions: WeaklyTypedServiceDefinition[]) {\r\n const sortedServiceDefinitions: typeof serviceDefinitions = [];\r\n SortGraph(\r\n serviceDefinitions,\r\n function* (serviceDefinition) {\r\n // Check each dependency.\r\n for (const contractIdentity of serviceDefinition.consumes ?? []) {\r\n // If another service definition produces the dependency contract, return it as an adjacent node for the purpose of sorting.\r\n yield* serviceDefinitions.filter((otherServiceDefinition) => (otherServiceDefinition.produces ?? []).includes(contractIdentity));\r\n }\r\n },\r\n sortedServiceDefinitions.push.bind(sortedServiceDefinitions)\r\n );\r\n return sortedServiceDefinitions;\r\n}\r\n\r\n/**\r\n * A service container manages the lifetimes of a set of services.\r\n * It takes care of instantiating the services in the correct order based on their dependencies,\r\n * passing dependencies through to services, and disposing of services when the container is disposed.\r\n */\r\nexport class ServiceContainer implements IDisposable {\r\n private _isDisposed = false;\r\n private readonly _serviceDefinitions = new Map<symbol, WeaklyTypedServiceDefinition>();\r\n private readonly _serviceDependents = new Map<WeaklyTypedServiceDefinition, Set<WeaklyTypedServiceDefinition>>();\r\n private readonly _serviceInstances = new Map<WeaklyTypedServiceDefinition, (IService<symbol> & Partial<IDisposable>) | void>();\r\n private readonly _children = new Set<ServiceContainer>();\r\n\r\n /**\r\n * Creates a new ServiceContainer.\r\n * @param _friendlyName A human-readable name for debugging.\r\n * @param _parent An optional parent container. Dependencies not found locally will be resolved from the parent.\r\n */\r\n public constructor(\r\n private readonly _friendlyName: string,\r\n private readonly _parent?: ServiceContainer\r\n ) {\r\n _parent?._children.add(this);\r\n }\r\n\r\n /**\r\n * Adds a set of service definitions in the service container.\r\n * The services are sorted based on their dependencies.\r\n * @param args The service definitions to register, and optionally an abort signal.\r\n * @returns A disposable that will remove the service definition from the service container.\r\n */\r\n public async addServicesAsync(\r\n ...args: WeaklyTypedServiceDefinition[] | [...serviceDefinitions: WeaklyTypedServiceDefinition[], abortSignal: AbortSignal]\r\n ): Promise<IDisposable> {\r\n if (this._isDisposed) {\r\n throw new Error(\"ServiceContainer is disposed.\");\r\n }\r\n\r\n const abortSignal = args[args.length - 1] instanceof AbortSignal ? (args.pop() as AbortSignal) : undefined;\r\n const serviceDefinitions = args as WeaklyTypedServiceDefinition[];\r\n\r\n const sortedServiceDefinitions = SortServiceDefinitions(serviceDefinitions);\r\n\r\n const dispose = () => {\r\n for (const serviceDefinition of sortedServiceDefinitions.reverse()) {\r\n this._removeService(serviceDefinition);\r\n }\r\n };\r\n\r\n try {\r\n for (const serviceDefinition of sortedServiceDefinitions) {\r\n // We could possibly optimize this by allowing some parallel initialization of services, but this would be way more complex, so let's wait and see if it's needed.\r\n // eslint-disable-next-line no-await-in-loop\r\n await this._addServiceAsync(serviceDefinition, abortSignal);\r\n }\r\n } catch (error: unknown) {\r\n dispose();\r\n throw error;\r\n }\r\n\r\n return {\r\n dispose,\r\n };\r\n }\r\n\r\n /**\r\n * Registers a service definition in the service container.\r\n * @param serviceDefinition The service definition to register.\r\n * @param abortSignal An optional abort signal.\r\n * @returns A disposable that will remove the service definition from the service container.\r\n */\r\n public async addServiceAsync<Produces extends IService<symbol>[] = [], Consumes extends IService<symbol>[] = []>(\r\n serviceDefinition: ServiceDefinition<Produces, Consumes>,\r\n abortSignal?: AbortSignal\r\n ): Promise<IDisposable> {\r\n if (abortSignal) {\r\n return await this.addServicesAsync(serviceDefinition, abortSignal);\r\n } else {\r\n return await this.addServicesAsync(serviceDefinition);\r\n }\r\n }\r\n\r\n private async _addServiceAsync(service: WeaklyTypedServiceDefinition, abortSignal?: AbortSignal) {\r\n if (this._isDisposed) {\r\n throw new Error(`'${this._friendlyName}' container is disposed.`);\r\n }\r\n\r\n service.produces?.forEach((contract) => {\r\n if (this._serviceDefinitions.has(contract)) {\r\n throw new Error(`A service producing the contract '${contract.toString()}' has already been added to this '${this._friendlyName}' container.`);\r\n }\r\n });\r\n\r\n service.produces?.forEach((contract) => {\r\n this._serviceDefinitions.set(contract, service);\r\n });\r\n\r\n const dependencies = service.consumes?.map((contract) => this._resolveDependency(contract, service)) ?? [];\r\n\r\n this._serviceInstances.set(service, await service.factory(...dependencies, abortSignal));\r\n }\r\n\r\n /**\r\n * Resolves a dependency by contract identity for a consuming service.\r\n * Checks local services first, then walks up the parent chain.\r\n * Registers the consumer as a dependent in whichever container owns the dependency.\r\n * @param contract The contract identity to resolve.\r\n * @param consumer The service definition that consumes this dependency.\r\n * @returns The resolved service instance.\r\n */\r\n private _resolveDependency(contract: symbol, consumer: WeaklyTypedServiceDefinition): IService<symbol> & Partial<IDisposable> {\r\n const definition = this._serviceDefinitions.get(contract);\r\n if (definition) {\r\n let dependentDefinitions = this._serviceDependents.get(definition);\r\n if (!dependentDefinitions) {\r\n this._serviceDependents.set(definition, (dependentDefinitions = new Set()));\r\n }\r\n dependentDefinitions.add(consumer);\r\n\r\n const instance = this._serviceInstances.get(definition);\r\n if (!instance) {\r\n throw new Error(`Service '${contract.toString()}' has not been instantiated in the '${this._friendlyName}' container.`);\r\n }\r\n return instance;\r\n }\r\n\r\n if (this._parent) {\r\n return this._parent._resolveDependency(contract, consumer);\r\n }\r\n\r\n throw new Error(`Service '${contract.toString()}' has not been registered in the '${this._friendlyName}' container.`);\r\n }\r\n\r\n /**\r\n * Removes a consumer from the dependent set for a given contract, checking locally first then the parent chain.\r\n * @param contract The contract identity.\r\n * @param consumer The service definition to remove as a dependent.\r\n */\r\n private _removeDependentFromChain(contract: symbol, consumer: WeaklyTypedServiceDefinition): void {\r\n const definition = this._serviceDefinitions.get(contract);\r\n if (definition) {\r\n const dependentDefinitions = this._serviceDependents.get(definition);\r\n if (dependentDefinitions) {\r\n dependentDefinitions.delete(consumer);\r\n if (dependentDefinitions.size === 0) {\r\n this._serviceDependents.delete(definition);\r\n }\r\n }\r\n return;\r\n }\r\n\r\n this._parent?._removeDependentFromChain(contract, consumer);\r\n }\r\n\r\n private _removeService(service: WeaklyTypedServiceDefinition) {\r\n if (this._isDisposed) {\r\n throw new Error(`'${this._friendlyName}' container is disposed.`);\r\n }\r\n\r\n const serviceDependents = this._serviceDependents.get(service);\r\n if (serviceDependents && serviceDependents.size > 0) {\r\n throw new Error(\r\n `Service '${service.friendlyName}' has dependents: ${Array.from(serviceDependents)\r\n .map((dependent) => dependent.friendlyName)\r\n .join(\", \")}`\r\n );\r\n }\r\n\r\n // NOTE: The service instance could be undefined, as the service factory for a service that does not produce any contracts is not required to return an actual service instance.\r\n const serviceInstance = this._serviceInstances.get(service);\r\n this._serviceInstances.delete(service);\r\n serviceInstance?.dispose?.();\r\n\r\n service.produces?.forEach((contract) => {\r\n this._serviceDefinitions.delete(contract);\r\n });\r\n\r\n // Remove this service as a dependent from each of its consumed dependencies (local or in parent chain).\r\n service.consumes?.forEach((contract) => {\r\n this._removeDependentFromChain(contract, service);\r\n });\r\n }\r\n\r\n /**\r\n * Disposes the service container and all contained services.\r\n * Throws if this container is still a parent of any live child containers.\r\n */\r\n public dispose() {\r\n if (this._children.size > 0) {\r\n throw new Error(`'${this._friendlyName}' container cannot be disposed because it has ${this._children.size} active child container(s).`);\r\n }\r\n\r\n Array.from(this._serviceInstances.keys()).reverse().forEach(this._removeService.bind(this));\r\n this._serviceInstances.clear();\r\n this._serviceDependents.clear();\r\n this._serviceDefinitions.clear();\r\n this._parent?._children.delete(this);\r\n this._isDisposed = true;\r\n }\r\n}\r\n"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type IDisposable } from "@onerjs/core/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* A helper to create a service factory function from a class constructor.
|
|
4
|
+
* @param constructor The class to create a factory function for.
|
|
5
|
+
* @returns A factory function that creates an instance of the class.
|
|
6
|
+
*/
|
|
7
|
+
export declare function ConstructorFactory<Class extends new (...args: any) => any>(constructor: Class): (...args: ConstructorParameters<Class>) => InstanceType<Class>;
|
|
8
|
+
declare const Contract: unique symbol;
|
|
9
|
+
/**
|
|
10
|
+
* This interface must be implemented by all service contracts.
|
|
11
|
+
*/
|
|
12
|
+
export interface IService<ContractIdentity extends symbol> {
|
|
13
|
+
/**
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
readonly [Contract]?: ContractIdentity;
|
|
17
|
+
}
|
|
18
|
+
type ExtractContractIdentity<ServiceContract extends IService<symbol>> = ServiceContract extends IService<infer ContractIdentity> ? ContractIdentity : never;
|
|
19
|
+
type ExtractContractIdentities<ServiceContracts extends IService<symbol>[]> = {
|
|
20
|
+
[Index in keyof ServiceContracts]: ExtractContractIdentity<ServiceContracts[Index]>;
|
|
21
|
+
};
|
|
22
|
+
type UnionToIntersection<Union> = (Union extends any ? (k: Union) => void : never) extends (k: infer Intersection) => void ? Intersection : never;
|
|
23
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
24
|
+
/**
|
|
25
|
+
* A factory function responsible for creating a service instance.
|
|
26
|
+
* Consumed services are passed as arguments to the factory function.
|
|
27
|
+
* The returned value must implement all produced services, and may IDisposable.
|
|
28
|
+
* If not services are produced, the returned value may implement IDisposable, otherwise it may return void.
|
|
29
|
+
*/
|
|
30
|
+
export type ServiceFactory<Produces extends IService<symbol>[], Consumes extends IService<symbol>[]> = (...dependencies: [...Consumes, abortSignal?: AbortSignal]) => MaybePromise<Produces extends [] ? Partial<IDisposable> | void : Partial<IDisposable> & UnionToIntersection<Produces[number]>>;
|
|
31
|
+
/**
|
|
32
|
+
* Defines a service, which is a logical unit that consumes other services (dependencies), and optionally produces services that can be consumed by other services (dependents).
|
|
33
|
+
*/
|
|
34
|
+
export type ServiceDefinition<Produces extends IService<symbol>[] = [], Consumes extends IService<symbol>[] = []> = {
|
|
35
|
+
/**
|
|
36
|
+
* A human readable name for the service to help with debugging.
|
|
37
|
+
*/
|
|
38
|
+
friendlyName: string;
|
|
39
|
+
/**
|
|
40
|
+
* A function that instantiates the service.
|
|
41
|
+
*/
|
|
42
|
+
factory: ServiceFactory<Produces, Consumes>;
|
|
43
|
+
} & (Produces extends [] ? {
|
|
44
|
+
/**
|
|
45
|
+
* An empty list or undefined, since the type specification has indicated no contracts are produced.
|
|
46
|
+
*/
|
|
47
|
+
produces?: [];
|
|
48
|
+
} : {
|
|
49
|
+
/**
|
|
50
|
+
* The list of contract identities that this service produces for consumption by other services.
|
|
51
|
+
*/
|
|
52
|
+
produces: ExtractContractIdentities<Produces>;
|
|
53
|
+
}) & (Consumes extends [] ? {
|
|
54
|
+
/**
|
|
55
|
+
* An empty list or undefined, since the type specification has indicated that no other services are consumed.
|
|
56
|
+
*/
|
|
57
|
+
consumes?: [];
|
|
58
|
+
} : {
|
|
59
|
+
/**
|
|
60
|
+
* The list of contract identities of other services that this service consumes.
|
|
61
|
+
*/
|
|
62
|
+
consumes: ExtractContractIdentities<Consumes>;
|
|
63
|
+
});
|
|
64
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A helper to create a service factory function from a class constructor.
|
|
3
|
+
* @param constructor The class to create a factory function for.
|
|
4
|
+
* @returns A factory function that creates an instance of the class.
|
|
5
|
+
*/
|
|
6
|
+
export function ConstructorFactory(constructor) {
|
|
7
|
+
return (...args) => new constructor(...args);
|
|
8
|
+
}
|
|
9
|
+
// This allows us to map from a service contract back to a contract identity.
|
|
10
|
+
const Contract = Symbol();
|
|
11
|
+
//# sourceMappingURL=serviceDefinition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceDefinition.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/modularity/serviceDefinition.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAA0C,WAAkB;IAC1F,OAAO,CAAC,GAAG,IAAkC,EAAE,EAAE,CAAC,IAAI,WAAW,CAAC,GAAG,IAAI,CAAC,CAAC;AAC/E,CAAC;AAED,6EAA6E;AAC7E,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC","sourcesContent":["import { type IDisposable } from \"core/index\";\r\n\r\n/**\r\n * A helper to create a service factory function from a class constructor.\r\n * @param constructor The class to create a factory function for.\r\n * @returns A factory function that creates an instance of the class.\r\n */\r\nexport function ConstructorFactory<Class extends new (...args: any) => any>(constructor: Class): (...args: ConstructorParameters<Class>) => InstanceType<Class> {\r\n return (...args: ConstructorParameters<Class>) => new constructor(...args);\r\n}\r\n\r\n// This allows us to map from a service contract back to a contract identity.\r\nconst Contract = Symbol();\r\n/**\r\n * This interface must be implemented by all service contracts.\r\n */\r\nexport interface IService<ContractIdentity extends symbol> {\r\n /**\r\n * @internal\r\n */\r\n readonly [Contract]?: ContractIdentity;\r\n}\r\n\r\n// This utility type extracts the service identity (unique symbol type) from a service contract. That is, it extracts T from IService<T>.\r\ntype ExtractContractIdentity<ServiceContract extends IService<symbol>> = ServiceContract extends IService<infer ContractIdentity> ? ContractIdentity : never;\r\n\r\n// This utility type extracts the contract identities from an array of service contracts. That is, it extracts [T1, T2, ...] from [IService<T1>, IService<T2>, ...].\r\ntype ExtractContractIdentities<ServiceContracts extends IService<symbol>[]> = {\r\n [Index in keyof ServiceContracts]: ExtractContractIdentity<ServiceContracts[Index]>;\r\n};\r\n\r\n// This utility type converts a union of types into an intersection of types. That is, it converts T1 | T2 | ... into T1 & T2 & ...\r\n// This is specifically used to determine the type that a service factory function returns when it produces multiple services.\r\ntype UnionToIntersection<Union> = (Union extends any ? (k: Union) => void : never) extends (k: infer Intersection) => void ? Intersection : never;\r\n\r\ntype MaybePromise<T> = T | Promise<T>;\r\n\r\n/**\r\n * A factory function responsible for creating a service instance.\r\n * Consumed services are passed as arguments to the factory function.\r\n * The returned value must implement all produced services, and may IDisposable.\r\n * If not services are produced, the returned value may implement IDisposable, otherwise it may return void.\r\n */\r\nexport type ServiceFactory<Produces extends IService<symbol>[], Consumes extends IService<symbol>[]> = (\r\n ...dependencies: [...Consumes, abortSignal?: AbortSignal]\r\n) => MaybePromise<Produces extends [] ? Partial<IDisposable> | void : Partial<IDisposable> & UnionToIntersection<Produces[number]>>;\r\n\r\n/**\r\n * Defines a service, which is a logical unit that consumes other services (dependencies), and optionally produces services that can be consumed by other services (dependents).\r\n */\r\nexport type ServiceDefinition<Produces extends IService<symbol>[] = [], Consumes extends IService<symbol>[] = []> = {\r\n /**\r\n * A human readable name for the service to help with debugging.\r\n */\r\n friendlyName: string;\r\n\r\n /**\r\n * A function that instantiates the service.\r\n */\r\n factory: ServiceFactory<Produces, Consumes>;\r\n} & (Produces extends []\r\n ? {\r\n /**\r\n * An empty list or undefined, since the type specification has indicated no contracts are produced.\r\n */\r\n produces?: [];\r\n }\r\n : {\r\n /**\r\n * The list of contract identities that this service produces for consumption by other services.\r\n */\r\n produces: ExtractContractIdentities<Produces>;\r\n }) &\r\n (Consumes extends []\r\n ? {\r\n /**\r\n * An empty list or undefined, since the type specification has indicated that no other services are consumed.\r\n */\r\n consumes?: [];\r\n }\r\n : {\r\n /**\r\n * The list of contract identities of other services that this service consumes.\r\n */\r\n consumes: ExtractContractIdentities<Consumes>;\r\n });\r\n"]}
|