@equinor/fusion-framework-module
Advanced tools
Comparing version 0.3.0 to 0.4.0
@@ -6,2 +6,16 @@ # Change Log | ||
# 0.4.0 (2022-08-11) | ||
* feat!: allow modules to displose ([32b69fb](https://github.com/equinor/fusion-framework/commit/32b69fb7cc61e78e503e67d0e77f21fb44b600b9)) | ||
### BREAKING CHANGES | ||
* module.initialize now has object as arg | ||
# [0.3.0](https://github.com/equinor/fusion-framework/compare/@equinor/fusion-framework-module@0.2.8...@equinor/fusion-framework-module@0.3.0) (2022-08-04) | ||
@@ -8,0 +22,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { BehaviorSubject, filter, firstValueFrom, from, lastValueFrom, map, mergeMap, throwError, timeout, } from 'rxjs'; | ||
import { BehaviorSubject, filter, firstValueFrom, from, lastValueFrom, map, mergeMap, scan, throwError, timeout, } from 'rxjs'; | ||
class ConsoleLogger { | ||
@@ -38,14 +38,11 @@ domain; | ||
export const initializeModules = async (configure, modules, ref) => { | ||
const moduleNames = modules.map((m) => m.name); | ||
const instance$ = new BehaviorSubject({}); | ||
const moduleNames = modules.map((m) => m.name); | ||
const afterConfiguration = []; | ||
const afterInit = []; | ||
logger.debug(`🛠 initializing ${!ref ? 'modules' : 'sub-modules'} ${modules.map(logModuleName)}`, modules, ref); | ||
const config = await Object.values(modules).reduce(async (acc, module) => { | ||
logger.debug(`configuring ${logModuleName(module)}`); | ||
const obj = await acc; | ||
const res = await Promise.resolve(module.configure?.(ref)); | ||
logger.debug(`configured ${logModuleName(module)}`); | ||
return Object.assign(obj, { [module.name]: res }); | ||
}, Promise.resolve({ | ||
const config = await lastValueFrom(from(modules).pipe(mergeMap(async (module) => { | ||
const configurator = await module.configure?.(ref); | ||
return { [module.name]: configurator }; | ||
}), scan((acc, module) => Object.assign(acc, module), { | ||
onAfterConfiguration(cb) { | ||
@@ -57,4 +54,8 @@ afterConfiguration.push(cb); | ||
}, | ||
})); | ||
config.requireInstance = (name, wait = 60) => { | ||
}))); | ||
Object.seal(config); | ||
logger.debug(`✅ Configured ${modules.map(logModuleName)}`, config); | ||
configure && (await configure(config, ref)); | ||
await Promise.all([...modules.map((x) => x.postConfigure), ...afterConfiguration].map((x) => Promise.resolve(x?.(config)))); | ||
const requireInstance = (name, wait = 60) => { | ||
if (!moduleNames.includes(name)) { | ||
@@ -73,11 +74,10 @@ throw Error(`cannot not require [${String(name)}] since module is not defined!`); | ||
}; | ||
Object.seal(config); | ||
logger.debug(`✅ Configured ${modules.map(logModuleName)}`, config); | ||
configure && (await configure(config, ref)); | ||
await Promise.all([...modules.map((x) => x.postConfigure), ...afterConfiguration].map((x) => Promise.resolve(x?.(config)))); | ||
from(modules) | ||
.pipe(mergeMap((module) => from(Promise.resolve(module.initialize(config, instance$.value))).pipe(map((instance) => { | ||
logger.debug(`initialized ${logModuleName(module)}`); | ||
return [module.name, instance]; | ||
})))) | ||
.pipe(mergeMap((module) => { | ||
const key = module.name; | ||
return from(Promise.resolve(module.initialize({ ref, config: config[key], requireInstance }))).pipe(map((instance) => { | ||
logger.debug(`initialized ${logModuleName(module)}`); | ||
return [key, instance]; | ||
})); | ||
})) | ||
.subscribe({ | ||
@@ -91,3 +91,3 @@ next: ([name, module]) => { | ||
logger.debug('✅ initialized'); | ||
Object.seal(ref); | ||
Object.seal(instance); | ||
const postInitialize = [...modules.map((x) => x.postInitialize), ...afterInit]; | ||
@@ -97,5 +97,14 @@ await Promise.all(postInitialize.map((x) => Promise.resolve(x?.(instance)))); | ||
logger.info(`🚀 ${!ref ? 'modules' : 'sub-modules'} ready`, process.env.NODE_ENV === 'development' && instance); | ||
return instance; | ||
const dispose = async () => { | ||
await Promise.allSettled(modules.map((module) => { | ||
Promise.resolve(module.dispose?.({ | ||
ref, | ||
modules: instance, | ||
instance: modules[module.name], | ||
})); | ||
})); | ||
}; | ||
return Object.seal(Object.assign({}, instance, { dispose })); | ||
}; | ||
export default initializeModules; | ||
//# sourceMappingURL=initialize-modules.js.map |
import type { AnyModule, ModulesConfigurator, ModulesInstanceType } from './types'; | ||
export declare const initializeModules: <TModules extends AnyModule[], TInstance = any>(configure: ModulesConfigurator<TModules, TInstance>, modules: TModules, ref?: TInstance | undefined) => Promise<ModulesInstanceType<TModules>>; | ||
export declare const initializeModules: <TModules extends AnyModule[], TInstance = any>(configure: ModulesConfigurator<TModules, TInstance>, modules: TModules, ref?: TInstance | undefined) => Promise<ModulesInstanceType<TModules> & { | ||
dispose: VoidFunction; | ||
}>; | ||
export default initializeModules; |
@@ -1,4 +0,1 @@ | ||
export declare type ModuleInitializeConfig<TKey extends string, TConfig, TDeps extends Array<AnyModule> = []> = Record<TKey, TConfig> & ModulesConfigType<ModulesType<TDeps>> & { | ||
requireInstance: <TKey extends keyof ModulesInstanceType<TDeps>>(name: TKey, wait?: number) => Promise<ModulesInstanceType<TDeps>[TKey]>; | ||
}; | ||
export interface Module<TKey extends string, TType, TConfig, TDeps extends Array<AnyModule> = []> { | ||
@@ -8,4 +5,13 @@ name: TKey; | ||
postConfigure?: (config: Record<TKey, TConfig> & ModulesConfigType<ModulesType<TDeps>>) => void | Promise<void>; | ||
initialize: (config: ModuleInitializeConfig<TKey, TConfig, TDeps>, instance: Record<TKey, TType> & ModulesInstanceType<ModulesType<TDeps>>) => TType | Promise<TType>; | ||
initialize: (args: { | ||
ref?: any; | ||
config: TConfig; | ||
requireInstance: <TKey extends keyof ModulesInstanceType<TDeps>>(name: TKey, wait?: number) => Promise<ModulesInstanceType<TDeps>[TKey]>; | ||
}) => TType | Promise<TType>; | ||
postInitialize?: (modules: Record<TKey, TType> & ModulesInstanceType<ModulesType<TDeps>>) => void | Promise<void>; | ||
dispose?: (args: { | ||
ref?: any; | ||
instance: TType; | ||
modules: Record<TKey, TType> & ModulesInstanceType<ModulesType<TDeps>>; | ||
}) => void | Promise<void>; | ||
} | ||
@@ -23,3 +29,3 @@ export declare type AnyModule = Module<any, any, any, any>; | ||
export interface ModulesConfigurator<TModules extends Array<AnyModule>, TRef = ModuleInstance> { | ||
(config: ModulesConfig<ModulesType<TModules>>, ref?: TRef): void | Promise<void>; | ||
(config: ModulesConfig<TModules>, ref?: TRef): void | Promise<void>; | ||
} | ||
@@ -26,0 +32,0 @@ export declare type ModulesConfigType<TModules extends Array<AnyModule> | Record<string, AnyModule>> = TModules extends Array<AnyModule> ? ModulesObjectConfigType<ModulesType<TModules>> : TModules extends Record<string, AnyModule> ? ModulesObjectConfigType<TModules> : never; |
{ | ||
"name": "@equinor/fusion-framework-module", | ||
"version": "0.3.0", | ||
"version": "0.4.0", | ||
"description": "", | ||
@@ -31,3 +31,3 @@ "main": "dist/esm/index.js", | ||
}, | ||
"gitHead": "65221b1abaf4e12d55349ddde5492c8facada168" | ||
"gitHead": "b578e1812564df3aac4313c1b38e7252e7c9dd83" | ||
} |
@@ -10,2 +10,3 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ | ||
mergeMap, | ||
scan, | ||
throwError, | ||
@@ -75,3 +76,6 @@ timeout, | ||
ref?: TInstance | ||
): Promise<ModulesInstanceType<TModules>> => { | ||
): Promise<ModulesInstanceType<TModules> & { dispose: VoidFunction }> => { | ||
/** extract module names from provided modules */ | ||
const moduleNames = modules.map((m) => m.name); | ||
const instance$ = new BehaviorSubject<ModulesInstanceType<TModules>>( | ||
@@ -81,5 +85,2 @@ {} as ModulesInstanceType<TModules> | ||
/** extract module names from provided modules */ | ||
const moduleNames = modules.map((m) => m.name); | ||
const afterConfiguration: Array<(config: ModulesConfigType<TModules>) => void> = []; | ||
@@ -94,29 +95,36 @@ const afterInit: Array<(instance: ModulesInstanceType<TModules>) => void> = []; | ||
/** initialize config providers for all modules */ | ||
// TODO: make configs init in parallel | ||
const config: ModulesConfig<TModules> = await Object.values(modules).reduce( | ||
async (acc, module) => { | ||
logger.debug(`configuring ${logModuleName(module)}`); | ||
const obj = await acc; | ||
const res = await Promise.resolve(module.configure?.(ref)); | ||
logger.debug(`configured ${logModuleName(module)}`); | ||
return Object.assign(obj, { [module.name]: res }); | ||
}, | ||
Promise.resolve({ | ||
onAfterConfiguration(cb: (config: ModulesConfigType<TModules>) => void) { | ||
afterConfiguration.push(cb); | ||
}, | ||
onAfterInit(cb: (instance: ModulesInstanceType<TModules>) => void) { | ||
afterInit.push(cb); | ||
}, | ||
} as ModulesConfig<TModules>) | ||
const config = await lastValueFrom( | ||
from(modules).pipe( | ||
// TODO - handle config creation errors | ||
mergeMap(async (module) => { | ||
const configurator = await module.configure?.(ref); | ||
return { [module.name]: configurator }; | ||
}), | ||
scan((acc, module) => Object.assign(acc, module), { | ||
onAfterConfiguration(cb) { | ||
afterConfiguration.push(cb); | ||
}, | ||
onAfterInit(cb) { | ||
afterInit.push(cb); | ||
}, | ||
} as ModulesConfig<TModules>) | ||
) | ||
); | ||
/** | ||
* Add method for allowing modules to await other module instance when initiating | ||
* WARNING: this might create stall-lock if developer does not implement correctly! | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
config.requireInstance = <TKey extends keyof ModulesInstanceType<TModules>>( | ||
/** protected config instance */ | ||
Object.seal(config); | ||
logger.debug(`✅ Configured ${modules.map(logModuleName)}`, config); | ||
/** allow callback to configure */ | ||
configure && (await configure(config, ref)); | ||
/** call all added post config hooks */ | ||
await Promise.all( | ||
[...modules.map((x) => x.postConfigure), ...afterConfiguration].map((x) => | ||
Promise.resolve(x?.(config)) | ||
) | ||
); | ||
const requireInstance = <TKey extends keyof ModulesInstanceType<TModules>>( | ||
name: TKey, | ||
@@ -145,30 +153,20 @@ wait = 60 | ||
/** protected config instance */ | ||
Object.seal(config); | ||
logger.debug(`✅ Configured ${modules.map(logModuleName)}`, config); | ||
/** allow callback to configure */ | ||
configure && (await configure(config as any, ref)); | ||
/** call all added post config hooks */ | ||
await Promise.all( | ||
[...modules.map((x) => x.postConfigure), ...afterConfiguration].map((x) => | ||
Promise.resolve(x?.(config)) | ||
) | ||
); | ||
from(modules) | ||
.pipe( | ||
/** assign module to modules object */ | ||
mergeMap((module) => | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
from(Promise.resolve(module.initialize(config, instance$.value))).pipe( | ||
mergeMap((module) => { | ||
const key = module.name; | ||
return from( | ||
Promise.resolve( | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
module.initialize({ ref, config: config[key], requireInstance }) | ||
) | ||
).pipe( | ||
map((instance) => { | ||
logger.debug(`initialized ${logModuleName(module)}`); | ||
return [module.name, instance]; | ||
return [key, instance]; | ||
}) | ||
) | ||
) | ||
); | ||
}) | ||
) | ||
@@ -187,4 +185,3 @@ .subscribe({ | ||
/** Protected instances */ | ||
Object.seal(ref); | ||
Object.seal(instance); | ||
@@ -201,5 +198,19 @@ /** call all added post config hooks */ | ||
return instance; | ||
const dispose = async () => { | ||
await Promise.allSettled( | ||
modules.map((module) => { | ||
Promise.resolve( | ||
module.dispose?.({ | ||
ref, | ||
modules: instance, | ||
instance: modules[module.name], | ||
}) | ||
); | ||
}) | ||
); | ||
}; | ||
return Object.seal(Object.assign({}, instance, { dispose })); | ||
}; | ||
export default initializeModules; |
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
/** config object that are provided to modules when initiating */ | ||
// TODO - create a own class for config (ConfigureConfig and InitializeConfig) | ||
export type ModuleInitializeConfig< | ||
TKey extends string, | ||
TConfig, | ||
TDeps extends Array<AnyModule> = [] | ||
> = Record<TKey, TConfig> & | ||
ModulesConfigType<ModulesType<TDeps>> & { | ||
requireInstance: <TKey extends keyof ModulesInstanceType<TDeps>>( | ||
name: TKey, | ||
wait?: number | ||
) => Promise<ModulesInstanceType<TDeps>[TKey]>; | ||
}; | ||
export interface Module<TKey extends string, TType, TConfig, TDeps extends Array<AnyModule> = []> { | ||
@@ -23,9 +9,18 @@ name: TKey; | ||
) => void | Promise<void>; | ||
initialize: ( | ||
config: ModuleInitializeConfig<TKey, TConfig, TDeps>, | ||
instance: Record<TKey, TType> & ModulesInstanceType<ModulesType<TDeps>> | ||
) => TType | Promise<TType>; | ||
initialize: (args: { | ||
ref?: any; | ||
config: TConfig; | ||
requireInstance: <TKey extends keyof ModulesInstanceType<TDeps>>( | ||
name: TKey, | ||
wait?: number | ||
) => Promise<ModulesInstanceType<TDeps>[TKey]>; | ||
}) => TType | Promise<TType>; | ||
postInitialize?: ( | ||
modules: Record<TKey, TType> & ModulesInstanceType<ModulesType<TDeps>> | ||
) => void | Promise<void>; | ||
dispose?: (args: { | ||
ref?: any; | ||
instance: TType; | ||
modules: Record<TKey, TType> & ModulesInstanceType<ModulesType<TDeps>>; | ||
}) => void | Promise<void>; | ||
} | ||
@@ -52,3 +47,3 @@ | ||
export interface ModulesConfigurator<TModules extends Array<AnyModule>, TRef = ModuleInstance> { | ||
(config: ModulesConfig<ModulesType<TModules>>, ref?: TRef): void | Promise<void>; | ||
(config: ModulesConfig<TModules>, ref?: TRef): void | Promise<void>; | ||
} | ||
@@ -55,0 +50,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
89516
430