@equinor/fusion-framework-module
Advanced tools
Comparing version 0.1.1 to 0.2.0
@@ -6,2 +6,13 @@ # Change Log | ||
# 0.2.0 (2022-06-10) | ||
### Features | ||
* **module:** allow modules to have deps ([#128](https://github.com/equinor/fusion-framework/issues/128)) ([2466b1a](https://github.com/equinor/fusion-framework/commit/2466b1ad9d43aa472da9daf8c59b350844c0dae9)) | ||
## 0.1.1 (2022-02-09) | ||
@@ -8,0 +19,0 @@ |
@@ -0,7 +1,62 @@ | ||
import { BehaviorSubject, delayWhen, filter, from, lastValueFrom, map, mergeMap, of } from 'rxjs'; | ||
class ConsoleLogger { | ||
domain; | ||
constructor(domain) { | ||
this.domain = domain; | ||
} | ||
_createMessage(msg) { | ||
return [ | ||
`%c FUSION FRAMEWORK %c ${this.domain} %c %s`, | ||
'background: rgb(179, 13, 47); color: white; padding: 1px;', | ||
'background: rgb(244, 244, 244); color: rgb(36, 55, 70); padding: 1px;', | ||
'background: none; color: inherit', | ||
...msg.reduce((c, n) => [...c, n, '\n'], []), | ||
]; | ||
} | ||
debug(...msg) { | ||
process.env.NODE_ENV === 'development' && console.debug(...this._createMessage(msg)); | ||
} | ||
info(...msg) { | ||
console.info(...this._createMessage(msg)); | ||
} | ||
warn(...msg) { | ||
console.warn(...this._createMessage(msg)); | ||
} | ||
error(...msg) { | ||
console.error(...this._createMessage(msg)); | ||
} | ||
} | ||
const logModuleName = (module) => `\u001b[1;32m${module.name.replace(/([A-Z])/g, ' $1').toUpperCase()}\x1b[0m`; | ||
const logger = new ConsoleLogger('MODULES'); | ||
const _initializeModules = async (modules, config) => { | ||
const instance$ = new BehaviorSubject({}); | ||
from(modules) | ||
.pipe(delayWhen((module) => { | ||
const hasDeps = !!module.deps; | ||
if (hasDeps) { | ||
logger.debug(`module ${logModuleName(module)} requires dependencies`, module.deps); | ||
return instance$.pipe(filter((instance) => !!module.deps?.every((dep) => Object.keys(instance).includes(String(dep))))); | ||
} | ||
return of(0); | ||
}), mergeMap((module) => from(Promise.resolve(module.initialize(config, instance$.value))).pipe(map((instance) => { | ||
logger.debug(`initialized ${logModuleName(module)}`); | ||
return [module.name, instance]; | ||
})))) | ||
.subscribe({ | ||
next: ([name, module]) => { | ||
instance$.next(Object.assign(instance$.value, { [name]: module })); | ||
}, | ||
complete: () => instance$.complete(), | ||
}); | ||
return lastValueFrom(instance$); | ||
}; | ||
export const initializeModules = async (configure, modules, ref) => { | ||
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 }); | ||
@@ -17,11 +72,12 @@ }, Promise.resolve({ | ||
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 instance = await modules.reduce(async (acc, module) => { | ||
const obj = await acc; | ||
const res = await Promise.resolve(module.initialize(config, obj)); | ||
return Object.assign(obj, { [module.name]: res }); | ||
}, Promise.resolve({})); | ||
const instance = await _initializeModules(modules, config); | ||
logger.debug('✅ initialized'); | ||
Object.seal(ref); | ||
await Promise.all([...modules.map((x) => x.postInitialize), ...afterInit].map((x) => Promise.resolve(x?.(instance)))); | ||
const postInitialize = [...modules.map((x) => x.postInitialize), ...afterInit]; | ||
await Promise.all(postInitialize.map((x) => Promise.resolve(x?.(instance)))); | ||
logger.debug('✅ post initialized'); | ||
logger.info(`🚀 ${!ref ? 'modules' : 'sub-modules'} ready`, process.env.NODE_ENV === 'development' && instance); | ||
return instance; | ||
@@ -28,0 +84,0 @@ }; |
export interface Module<TKey extends string, TType, TConfig, TDeps extends Array<AnyModule> = []> { | ||
name: TKey; | ||
configure?: (ref?: any) => TConfig | Promise<TConfig>; | ||
deps?: Array<keyof ModulesInstanceType<ModulesType<TDeps>>>; | ||
postConfigure?: (config: Record<TKey, TConfig> & ModulesConfigType<ModulesType<TDeps>>) => void | Promise<void>; | ||
@@ -5,0 +6,0 @@ initialize: (config: Record<TKey, TConfig> & ModulesConfigType<ModulesType<TDeps>>, instance: Record<TKey, TType> & ModulesInstanceType<ModulesType<TDeps>>) => TType; |
{ | ||
"name": "@equinor/fusion-framework-module", | ||
"version": "0.1.1", | ||
"version": "0.2.0", | ||
"description": "", | ||
@@ -31,3 +31,6 @@ "main": "./dist/esm/index.js", | ||
}, | ||
"gitHead": "ca79b01fcc32c2c4e4aeea2f7c251fe9d91a2d04" | ||
"dependencies": { | ||
"rxjs": "^7.5.5" | ||
}, | ||
"gitHead": "7b2e61bf8bffa9307238b32397b1ce3c5fc5380d" | ||
} |
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
import { BehaviorSubject, delayWhen, filter, from, lastValueFrom, map, mergeMap, of } from 'rxjs'; | ||
import type { | ||
@@ -10,2 +12,86 @@ AnyModule, | ||
// TODO - move to own lib ;) | ||
class ConsoleLogger { | ||
constructor(protected domain: string) {} | ||
/** @inheritdoc */ | ||
protected _createMessage(msg: unknown[]): unknown[] { | ||
return [ | ||
`%c FUSION FRAMEWORK %c ${this.domain} %c %s`, | ||
'background: rgb(179, 13, 47); color: white; padding: 1px;', | ||
'background: rgb(244, 244, 244); color: rgb(36, 55, 70); padding: 1px;', | ||
'background: none; color: inherit', | ||
...msg.reduce((c: unknown[], n: unknown) => [...c, n, '\n'], []), | ||
]; | ||
} | ||
debug(...msg: unknown[]) { | ||
process.env.NODE_ENV === 'development' && console.debug(...this._createMessage(msg)); | ||
} | ||
info(...msg: unknown[]) { | ||
console.info(...this._createMessage(msg)); | ||
} | ||
warn(...msg: unknown[]) { | ||
console.warn(...this._createMessage(msg)); | ||
} | ||
error(...msg: unknown[]) { | ||
console.error(...this._createMessage(msg)); | ||
} | ||
} | ||
const logModuleName = (module: AnyModule) => | ||
`\u001b[1;32m${module.name.replace(/([A-Z])/g, ' $1').toUpperCase()}\x1b[0m`; | ||
const logger = new ConsoleLogger('MODULES'); | ||
// TODO - create a class for initializing | ||
const _initializeModules = async <TModules extends Array<AnyModule>>( | ||
modules: TModules, | ||
config: ModulesConfigType<TModules> | ||
) => { | ||
const instance$ = new BehaviorSubject<ModulesInstanceType<TModules>>( | ||
{} as ModulesInstanceType<TModules> | ||
); | ||
from(modules) | ||
.pipe( | ||
delayWhen((module) => { | ||
const hasDeps = !!module.deps; | ||
if (hasDeps) { | ||
logger.debug( | ||
`module ${logModuleName(module)} requires dependencies`, | ||
module.deps | ||
); | ||
return instance$.pipe( | ||
filter( | ||
(instance) => | ||
/** check that all dependencies are created */ | ||
!!module.deps?.every((dep) => | ||
Object.keys(instance).includes(String(dep)) | ||
) | ||
) | ||
); | ||
} | ||
return of(0); | ||
}), | ||
mergeMap((module) => | ||
/** assign module to modules object */ | ||
from(Promise.resolve(module.initialize(config, instance$.value))).pipe( | ||
map((instance) => { | ||
logger.debug(`initialized ${logModuleName(module)}`); | ||
return [module.name, instance]; | ||
}) | ||
) | ||
) | ||
) | ||
.subscribe({ | ||
next: ([name, module]) => { | ||
instance$.next(Object.assign(instance$.value, { [name]: module })); | ||
}, | ||
complete: () => instance$.complete(), | ||
}); | ||
return lastValueFrom(instance$); | ||
}; | ||
/** | ||
@@ -26,7 +112,15 @@ * Create an instances of provided instances | ||
logger.debug( | ||
`🛠 initializing ${!ref ? 'modules' : 'sub-modules'} ${modules.map(logModuleName)}`, | ||
modules, | ||
ref | ||
); | ||
/** initialize config providers for all modules */ | ||
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 }); | ||
@@ -47,2 +141,4 @@ }, | ||
logger.debug(`✅ Configured ${modules.map(logModuleName)}`, config); | ||
/** allow callback to configure */ | ||
@@ -58,10 +154,4 @@ configure && (await configure(config as any, ref)); | ||
/** call module initializers */ | ||
const instance = await modules.reduce(async (acc, module) => { | ||
const obj = await acc; | ||
const res = await Promise.resolve( | ||
module.initialize(config as unknown as ModulesConfigType<TModules>, obj) | ||
); | ||
return Object.assign(obj, { [module.name]: res }); | ||
}, Promise.resolve({} as ModulesInstanceType<TModules>)); | ||
const instance = await _initializeModules(modules, config); | ||
logger.debug('✅ initialized'); | ||
@@ -72,6 +162,9 @@ /** Protected instances */ | ||
/** call all added post config hooks */ | ||
await Promise.all( | ||
[...modules.map((x) => x.postInitialize), ...afterInit].map((x) => | ||
Promise.resolve(x?.(instance)) | ||
) | ||
const postInitialize = [...modules.map((x) => x.postInitialize), ...afterInit]; | ||
await Promise.all(postInitialize.map((x) => Promise.resolve(x?.(instance)))); | ||
logger.debug('✅ post initialized'); | ||
logger.info( | ||
`🚀 ${!ref ? 'modules' : 'sub-modules'} ready`, | ||
process.env.NODE_ENV === 'development' && instance | ||
); | ||
@@ -78,0 +171,0 @@ |
@@ -6,2 +6,3 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ | ||
configure?: (ref?: any) => TConfig | Promise<TConfig>; | ||
deps?: Array<keyof ModulesInstanceType<ModulesType<TDeps>>>; | ||
postConfigure?: ( | ||
@@ -8,0 +9,0 @@ config: Record<TKey, TConfig> & ModulesConfigType<ModulesType<TDeps>> |
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
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
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
82700
356
1
2
+ Addedrxjs@^7.5.5
+ Addedrxjs@7.8.1(transitive)
+ Addedtslib@2.8.1(transitive)