@equinor/fusion-framework-module
Advanced tools
Comparing version 0.4.4 to 1.0.0-alpha.0
@@ -6,2 +6,21 @@ # Change Log | ||
## [1.0.0-alpha.0](https://github.com/equinor/fusion-framework/compare/@equinor/fusion-framework-module@0.4.4...@equinor/fusion-framework-module@1.0.0-alpha.0) (2022-09-12) | ||
### ⚠ BREAKING CHANGES | ||
* **module:** initialize modules now takes configurator object as argument. | ||
### Features | ||
* **module:** rewrite config to object ([74566f3](https://github.com/equinor/fusion-framework/commit/74566f36eb73c63e1e25df05d89f6f6490dc8272)) | ||
### Bug Fixes | ||
* **module:** await all creation of configs ([25649a4](https://github.com/equinor/fusion-framework/commit/25649a4a6bc4249f2fe996c0bdf735a7ebd42186)) | ||
* **module:** expose logger ([c88574a](https://github.com/equinor/fusion-framework/commit/c88574a61d368841dd648c511d80cad2e5efd7c6)) | ||
## 0.4.4 (2022-09-05) | ||
@@ -8,0 +27,0 @@ |
export * from './types'; | ||
export { initializeModules } from './initialize-modules'; | ||
export { ModuleConsoleLogger } from './logger'; | ||
export { ModulesConfigurator } from './configurator'; | ||
//# sourceMappingURL=index.js.map |
@@ -1,180 +0,3 @@ | ||
import { BehaviorSubject, filter, firstValueFrom, from, lastValueFrom, map, mergeMap, scan, throwError, timeout, } 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 = (moduleOrName) => { | ||
const name = typeof moduleOrName === 'string' ? moduleOrName : moduleOrName.name; | ||
return `📦\u001b[1;32m${name.replace(/([A-Z])/g, ' $1').toUpperCase()}\x1b[0m`; | ||
}; | ||
const logger = new ConsoleLogger('initialize-modules'); | ||
class RequiredModuleTimeoutError extends Error { | ||
constructor() { | ||
super('It was too slow'); | ||
this.name = 'RequiredModuleTimeoutError'; | ||
} | ||
} | ||
export const initializeModules = async (configure, modules, ref) => { | ||
const moduleNames = modules.map((m) => m.name); | ||
const instance$ = new BehaviorSubject({}); | ||
const afterConfiguration = []; | ||
const afterInit = []; | ||
logger.info(`🔵 Configuring modules`); | ||
logger.debug(`🛠 start configuration ${modules.map(logModuleName)}`, modules, ref); | ||
const config = await lastValueFrom(from(modules).pipe(mergeMap(async (module) => { | ||
logger.debug(`🛠 creating configurator ${logModuleName(module)}`); | ||
try { | ||
const configurator = await module.configure?.(ref); | ||
logger.debug(`🛠 created configurator for ${logModuleName(module)}`, configurator); | ||
return { [module.name]: configurator }; | ||
} | ||
catch (err) { | ||
logger.error(`🛠 Failed to created configurator for ${logModuleName(module)}`, err); | ||
throw err; | ||
} | ||
}), scan((acc, module) => Object.assign(acc, module), { | ||
onAfterConfiguration(cb) { | ||
afterConfiguration.push(cb); | ||
}, | ||
onAfterInit(cb) { | ||
afterInit.push(cb); | ||
}, | ||
}))); | ||
Object.seal(config); | ||
logger.info(`🟢 Config created`); | ||
logger.debug(`🛠 Config created ${modules.map(logModuleName)}`, config); | ||
if (configure) { | ||
await new Promise((resolve, reject) => { | ||
try { | ||
resolve(configure(config, ref)); | ||
logger.debug(`🏗 Configured`, config); | ||
} | ||
catch (err) { | ||
logger.error(`🏗 Failed to configure, please check provider configurator`); | ||
reject(err); | ||
} | ||
}); | ||
} | ||
else { | ||
logger.debug(`🏗 no configurator provided [skipping]`, config); | ||
} | ||
await Promise.allSettled(modules | ||
.filter((module) => !!module.postConfigure) | ||
.map(async (module) => { | ||
try { | ||
await module.postConfigure?.(config); | ||
logger.debug(`🏗📌 post configured ${logModuleName(module)}`, module); | ||
} | ||
catch (err) { | ||
logger.warn(`🏗📌 post configure failed ${logModuleName(module)}`); | ||
} | ||
})); | ||
if (afterConfiguration.length) { | ||
try { | ||
logger.debug(`🏗📌 post configure hooks [${afterConfiguration.length}]`); | ||
await Promise.allSettled(afterConfiguration.map((x) => Promise.resolve(x(config)))); | ||
logger.debug(`🏗📌 post configure hooks complete`); | ||
} | ||
catch (err) { | ||
logger.warn(`🏗📌 post configure hook failed`, err); | ||
} | ||
} | ||
logger.info(`🟢 Configured`); | ||
const requireInstance = (name, wait = 60) => { | ||
if (!moduleNames.includes(name)) { | ||
logger.error(`🚀⌛️ Cannot not require ${logModuleName(String(name))} since module is not defined!`); | ||
throw Error(`cannot not require [${String(name)}] since module is not defined!`); | ||
} | ||
if (instance$.value[name]) { | ||
logger.debug(`🚀⌛️ ${logModuleName(String(name))} is initiated, skipping queue`); | ||
return Promise.resolve(instance$.value[name]); | ||
} | ||
logger.debug(`🚀⌛️ Awaiting init ${logModuleName(String(name))}, timeout ${wait}s`); | ||
return firstValueFrom(instance$.pipe(filter((x) => !!x[name]), map((x) => x[name]), timeout({ | ||
each: wait, | ||
with: () => throwError(() => new RequiredModuleTimeoutError()), | ||
}))); | ||
}; | ||
from(modules) | ||
.pipe(mergeMap((module) => { | ||
const key = module.name; | ||
logger.debug(`🚀 initializing ${logModuleName(module)}`); | ||
return from(Promise.resolve(module.initialize({ ref, config: config[key], requireInstance }))).pipe(map((instance) => { | ||
logger.debug(`🚀 initialized ${logModuleName(module)}`); | ||
return [key, instance]; | ||
})); | ||
})) | ||
.subscribe({ | ||
next: ([name, module]) => { | ||
instance$.next(Object.assign(instance$.value, { [name]: module })); | ||
}, | ||
complete: () => instance$.complete(), | ||
}); | ||
const instance = await lastValueFrom(instance$); | ||
Object.seal(instance); | ||
await Promise.allSettled(modules | ||
.filter((x) => !!x.postInitialize) | ||
.map(async (module) => { | ||
try { | ||
logger.debug(`🚀📌 post initializing moule ${logModuleName(module)}`); | ||
await module.postInitialize?.({ | ||
ref, | ||
modules: instance, | ||
instance: instance[module.name], | ||
}); | ||
logger.debug(`🚀📌 post initialized moule ${logModuleName(module)}`); | ||
} | ||
catch (err) { | ||
logger.warn(`🚀📌 post initialize failed moule ${logModuleName(module)}`); | ||
} | ||
})); | ||
if (afterInit.length) { | ||
try { | ||
logger.debug(`🚀📌 post configure hooks [${afterConfiguration.length}]`); | ||
await Promise.allSettled(afterInit.map((x) => Promise.resolve(x(instance)))); | ||
logger.debug(`🚀📌 post configure hooks complete`); | ||
} | ||
catch (err) { | ||
logger.warn(`🚀📌 post configure hook failed`, err); | ||
} | ||
} | ||
logger.debug(`🎉 Modules initialized ${modules.map(logModuleName)}`, instance); | ||
logger.info('🟢 Modules initialized'); | ||
const dispose = async () => { | ||
await Promise.allSettled(modules | ||
.filter((module) => !!module.dispose) | ||
.map(async (module) => { | ||
await module.dispose?.({ | ||
ref, | ||
modules: instance, | ||
instance: modules[module.name], | ||
}); | ||
})); | ||
}; | ||
return Object.seal(Object.assign({}, instance, { dispose })); | ||
}; | ||
export const initializeModules = async (configurator, ref) => configurator.initialize(ref); | ||
export default initializeModules; | ||
//# sourceMappingURL=initialize-modules.js.map |
export * from './types'; | ||
export { initializeModules } from './initialize-modules'; | ||
export { ModuleConsoleLogger } from './logger'; | ||
export { ModulesConfigurator } from './configurator'; | ||
export type { IModuleConfigurator, IModulesConfigurator } from './configurator'; |
@@ -1,5 +0,6 @@ | ||
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> & { | ||
import { IModulesConfigurator } from './configurator'; | ||
import type { AnyModule, ModulesInstanceType } from './types'; | ||
export declare const initializeModules: <TModules extends AnyModule[], TInstance = any>(configurator: IModulesConfigurator<TModules, TInstance>, ref?: TInstance | undefined) => Promise<ModulesInstanceType<TModules> & { | ||
dispose: VoidFunction; | ||
}>; | ||
export default initializeModules; |
@@ -22,5 +22,9 @@ export interface Module<TKey extends string, TType, TConfig, TDeps extends Array<AnyModule> = []> { | ||
export declare type AnyModule = Module<any, any, any, any>; | ||
export declare type AnyModuleInstance = Record<string, AnyModule>; | ||
export declare type ModuleKey<M> = M extends Module<infer TKey, any, any, any> ? TKey : never; | ||
export declare type ModuleType<M> = M extends Module<any, infer TType, any, any> ? TType : never; | ||
export declare type ModuleConfigType<M> = M extends Module<any, any, infer TType, any> ? TType : never; | ||
export declare type ModulesInstance<TModules extends Array<AnyModule> | Record<string, AnyModule>> = ModulesInstanceType<TModules> & { | ||
dispose: VoidFunction; | ||
}; | ||
export interface Modules { | ||
@@ -32,5 +36,2 @@ [Key: string]: AnyModule; | ||
} : never; | ||
export interface ModulesConfigurator<TModules extends Array<AnyModule>, TRef = ModuleInstance> { | ||
(config: ModulesConfig<TModules>, ref?: TRef): void | Promise<void>; | ||
} | ||
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; | ||
@@ -50,2 +51,8 @@ export declare type ModulesInstanceType<TModules extends Array<AnyModule> | Record<string, AnyModule>> = TModules extends Array<AnyModule> ? ModulesObjectInstanceType<ModulesType<TModules>> : TModules extends Record<string, AnyModule> ? ModulesObjectInstanceType<TModules> : never; | ||
export declare type ModuleInstance = ModulesInstanceType<Modules>; | ||
export interface ILogger { | ||
debug: (...msg: unknown[]) => void; | ||
info: (...msg: unknown[]) => void; | ||
warn: (...msg: unknown[]) => void; | ||
error: (...msg: unknown[]) => void; | ||
} | ||
export {}; |
{ | ||
"name": "@equinor/fusion-framework-module", | ||
"version": "0.4.4", | ||
"version": "1.0.0-alpha.0", | ||
"description": "", | ||
@@ -31,3 +31,3 @@ "main": "dist/esm/index.js", | ||
}, | ||
"gitHead": "512e05eab757da7a9836199dd28a1bc0dbf5b492" | ||
"gitHead": "c077f3a9bda113b739faca7bb3fc1e96c2b8dc77" | ||
} |
export * from './types'; | ||
export { initializeModules } from './initialize-modules'; | ||
export { ModuleConsoleLogger } from './logger'; | ||
export { ModulesConfigurator } from './configurator'; | ||
export type { IModuleConfigurator, IModulesConfigurator } from './configurator'; |
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
import { | ||
BehaviorSubject, | ||
filter, | ||
firstValueFrom, | ||
from, | ||
lastValueFrom, | ||
map, | ||
mergeMap, | ||
scan, | ||
throwError, | ||
timeout, | ||
} from 'rxjs'; | ||
import { IModulesConfigurator } from './configurator'; | ||
import type { | ||
AnyModule, | ||
ModulesConfig, | ||
ModulesConfigType, | ||
ModulesConfigurator, | ||
ModulesInstanceType, | ||
} from './types'; | ||
import type { AnyModule, ModulesInstanceType } from './types'; | ||
// 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 = (moduleOrName: string | AnyModule) => { | ||
const name = typeof moduleOrName === 'string' ? moduleOrName : moduleOrName.name; | ||
return `📦\u001b[1;32m${name.replace(/([A-Z])/g, ' $1').toUpperCase()}\x1b[0m`; | ||
}; | ||
const logger = new ConsoleLogger('initialize-modules'); | ||
class RequiredModuleTimeoutError extends Error { | ||
constructor() { | ||
super('It was too slow'); | ||
this.name = 'RequiredModuleTimeoutError'; | ||
} | ||
} | ||
/** | ||
@@ -74,207 +14,7 @@ * Create an instances of provided instances | ||
export const initializeModules = async <TModules extends Array<AnyModule>, TInstance = any>( | ||
configure: ModulesConfigurator<TModules, TInstance>, | ||
modules: TModules, | ||
configurator: IModulesConfigurator<TModules, TInstance>, | ||
ref?: TInstance | ||
): Promise<ModulesInstanceType<TModules> & { dispose: VoidFunction }> => { | ||
/** extract module names from provided modules */ | ||
const moduleNames = modules.map((m) => m.name); | ||
): Promise<ModulesInstanceType<TModules> & { dispose: VoidFunction }> => | ||
configurator.initialize(ref); | ||
const instance$ = new BehaviorSubject<ModulesInstanceType<TModules>>( | ||
{} as ModulesInstanceType<TModules> | ||
); | ||
const afterConfiguration: Array<(config: ModulesConfigType<TModules>) => void> = []; | ||
const afterInit: Array<(instance: ModulesInstanceType<TModules>) => void> = []; | ||
logger.info(`🔵 Configuring modules`); | ||
logger.debug(`🛠 start configuration ${modules.map(logModuleName)}`, modules, ref); | ||
const config = await lastValueFrom( | ||
from(modules).pipe( | ||
// TODO - handle config creation errors | ||
mergeMap(async (module) => { | ||
logger.debug(`🛠 creating configurator ${logModuleName(module)}`); | ||
try { | ||
const configurator = await module.configure?.(ref); | ||
logger.debug( | ||
`🛠 created configurator for ${logModuleName(module)}`, | ||
configurator | ||
); | ||
return { [module.name]: configurator }; | ||
} catch (err) { | ||
logger.error( | ||
`🛠 Failed to created configurator for ${logModuleName(module)}`, | ||
err | ||
); | ||
throw err; | ||
} | ||
}), | ||
scan((acc, module) => Object.assign(acc, module), { | ||
onAfterConfiguration(cb) { | ||
afterConfiguration.push(cb); | ||
}, | ||
onAfterInit(cb) { | ||
afterInit.push(cb); | ||
}, | ||
} as ModulesConfig<TModules>) | ||
) | ||
); | ||
/** protected config instance */ | ||
Object.seal(config); | ||
logger.info(`🟢 Config created`); | ||
logger.debug(`🛠 Config created ${modules.map(logModuleName)}`, config); | ||
/** allow callback to configure */ | ||
if (configure) { | ||
await new Promise((resolve, reject) => { | ||
try { | ||
resolve(configure(config, ref)); | ||
logger.debug(`🏗 Configured`, config); | ||
} catch (err) { | ||
logger.error(`🏗 Failed to configure, please check provider configurator`); | ||
reject(err); | ||
} | ||
}); | ||
} else { | ||
logger.debug(`🏗 no configurator provided [skipping]`, config); | ||
} | ||
await Promise.allSettled( | ||
modules | ||
.filter((module) => !!module.postConfigure) | ||
.map(async (module) => { | ||
try { | ||
await module.postConfigure?.(config); | ||
logger.debug(`🏗📌 post configured ${logModuleName(module)}`, module); | ||
} catch (err) { | ||
logger.warn(`🏗📌 post configure failed ${logModuleName(module)}`); | ||
} | ||
}) | ||
); | ||
/** call all added post config hooks */ | ||
if (afterConfiguration.length) { | ||
try { | ||
logger.debug(`🏗📌 post configure hooks [${afterConfiguration.length}]`); | ||
await Promise.allSettled(afterConfiguration.map((x) => Promise.resolve(x(config)))); | ||
logger.debug(`🏗📌 post configure hooks complete`); | ||
} catch (err) { | ||
logger.warn(`🏗📌 post configure hook failed`, err); | ||
} | ||
} | ||
logger.info(`🟢 Configured`); | ||
const requireInstance = <TKey extends keyof ModulesInstanceType<TModules>>( | ||
name: TKey, | ||
wait = 60 | ||
): Promise<ModulesInstanceType<TModules>[TKey]> => { | ||
if (!moduleNames.includes(name)) { | ||
logger.error( | ||
`🚀⌛️ Cannot not require ${logModuleName( | ||
String(name) | ||
)} since module is not defined!` | ||
); | ||
throw Error(`cannot not require [${String(name)}] since module is not defined!`); | ||
} | ||
if (instance$.value[name]) { | ||
logger.debug(`🚀⌛️ ${logModuleName(String(name))} is initiated, skipping queue`); | ||
return Promise.resolve(instance$.value[name]); | ||
} | ||
logger.debug(`🚀⌛️ Awaiting init ${logModuleName(String(name))}, timeout ${wait}s`); | ||
return firstValueFrom( | ||
instance$.pipe( | ||
filter((x) => !!x[name]), | ||
map((x) => x[name]), | ||
timeout({ | ||
each: wait, | ||
with: () => throwError(() => new RequiredModuleTimeoutError()), | ||
}) | ||
) | ||
); | ||
}; | ||
from(modules) | ||
.pipe( | ||
/** assign module to modules object */ | ||
mergeMap((module) => { | ||
const key = module.name; | ||
logger.debug(`🚀 initializing ${logModuleName(module)}`); | ||
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 [key, instance]; | ||
}) | ||
); | ||
}) | ||
) | ||
.subscribe({ | ||
next: ([name, module]) => { | ||
/** push instance */ | ||
instance$.next(Object.assign(instance$.value, { [name]: module })); | ||
}, | ||
complete: () => instance$.complete(), | ||
}); | ||
/** await creation of all instances */ | ||
const instance = await lastValueFrom(instance$); | ||
Object.seal(instance); | ||
/** call all added post config hooks */ | ||
await Promise.allSettled( | ||
modules | ||
.filter((x) => !!x.postInitialize) | ||
.map(async (module) => { | ||
try { | ||
logger.debug(`🚀📌 post initializing moule ${logModuleName(module)}`); | ||
await module.postInitialize?.({ | ||
ref, | ||
modules: instance, | ||
instance: instance[module.name as keyof ModulesInstanceType<TModules>], | ||
}); | ||
logger.debug(`🚀📌 post initialized moule ${logModuleName(module)}`); | ||
} catch (err) { | ||
logger.warn(`🚀📌 post initialize failed moule ${logModuleName(module)}`); | ||
} | ||
}) | ||
); | ||
if (afterInit.length) { | ||
try { | ||
logger.debug(`🚀📌 post configure hooks [${afterConfiguration.length}]`); | ||
await Promise.allSettled(afterInit.map((x) => Promise.resolve(x(instance)))); | ||
logger.debug(`🚀📌 post configure hooks complete`); | ||
} catch (err) { | ||
logger.warn(`🚀📌 post configure hook failed`, err); | ||
} | ||
} | ||
logger.debug(`🎉 Modules initialized ${modules.map(logModuleName)}`, instance); | ||
logger.info('🟢 Modules initialized'); | ||
const dispose = async () => { | ||
await Promise.allSettled( | ||
modules | ||
.filter((module) => !!module.dispose) | ||
.map(async (module) => { | ||
await module.dispose?.({ | ||
ref, | ||
modules: instance, | ||
instance: modules[module.name], | ||
}); | ||
}) | ||
); | ||
}; | ||
return Object.seal(Object.assign({}, instance, { dispose })); | ||
}; | ||
export default initializeModules; |
@@ -30,5 +30,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ | ||
export type AnyModule = Module<any, any, any, any>; | ||
export type AnyModuleInstance = Record<string, AnyModule>; | ||
export type ModuleKey<M> = M extends Module<infer TKey, any, any, any> ? TKey : never; | ||
export type ModuleType<M> = M extends Module<any, infer TType, any, any> ? TType : never; | ||
export type ModuleConfigType<M> = M extends Module<any, any, infer TType, any> ? TType : never; | ||
export type ModulesInstance<TModules extends Array<AnyModule> | Record<string, AnyModule>> = | ||
ModulesInstanceType<TModules> & { dispose: VoidFunction }; | ||
@@ -48,6 +51,2 @@ export interface Modules { | ||
export interface ModulesConfigurator<TModules extends Array<AnyModule>, TRef = ModuleInstance> { | ||
(config: ModulesConfig<TModules>, ref?: TRef): void | Promise<void>; | ||
} | ||
/** Extract configs from modules */ | ||
@@ -89,1 +88,8 @@ export type ModulesConfigType<TModules extends Array<AnyModule> | Record<string, AnyModule>> = | ||
export type ModuleInstance = ModulesInstanceType<Modules>; | ||
export interface ILogger { | ||
debug: (...msg: unknown[]) => void; | ||
info: (...msg: unknown[]) => void; | ||
warn: (...msg: unknown[]) => void; | ||
error: (...msg: unknown[]) => void; | ||
} |
Sorry, the diff of this file is not supported yet
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
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
113080
24
770
0
1