mirror of
https://github.com/sern-handler/handler
synced 2026-06-28 02:32:15 +00:00
refactor: cleanup (#348)
* some wip code Co-authored-by: Jacob Nguyen <jacoobes@users.noreply.github.com> * general idea * style * making shrimple truly optional * got optional localizer working * proposing api notation? * prepare for localization map * add localsFor * merge some internals * boss call * add test for init functionality * add documentation * inline and cleanup * feat: logging for experimental json loading * loosen typings * dev workflow and cleaning up comments * cleaning up a bit more * rename Localizer -> Localization * more documentation, change dir for default localizer * some tests * " * move stuff, refactor, deprecate * yarnb * Update index.ts --------- Co-authored-by: Jacob Nguyen <jacoobes@users.noreply.github.com> Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com> Co-authored-by: jacob <jacoobes@sern.dev>
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
import * as assert from 'assert';
|
||||
import { composeRoot, useContainer } from './dependency-injection';
|
||||
import type { DependencyConfiguration } from '../../types/ioc';
|
||||
import { useContainer } from './dependency-injection';
|
||||
import type { CoreDependencies, DependencyConfiguration } from '../../types/ioc';
|
||||
import { CoreContainer } from './container';
|
||||
import { Result } from 'ts-results-es'
|
||||
import { Result } from 'ts-results-es';
|
||||
import { DefaultServices } from '../_internal';
|
||||
import { AnyFunction } from '../../types/utility';
|
||||
import type { Logging } from '../contracts/logging';
|
||||
|
||||
//SIDE EFFECT: GLOBAL DI
|
||||
let containerSubject: CoreContainer<Partial<Dependencies>>;
|
||||
|
||||
@@ -29,19 +30,18 @@ export function disposeAll(logger: Logging|undefined) {
|
||||
.then(() => logger?.info({ message: 'Cleaning container and crashing' }));
|
||||
}
|
||||
|
||||
const dependencyBuilder = (container: any, excluded: string[]) => {
|
||||
const dependencyBuilder = (container: any, excluded: string[] ) => {
|
||||
type Insertable =
|
||||
| ((container: CoreContainer<Dependencies>) => unknown )
|
||||
| Record<PropertyKey, unknown>
|
||||
| object
|
||||
return {
|
||||
/**
|
||||
* Insert a dependency into your container.
|
||||
* Supply the correct key and dependency
|
||||
*/
|
||||
add(key: keyof Dependencies, v: Insertable) {
|
||||
Result
|
||||
.wrap(() => container.add({ [key]: v}))
|
||||
.expect("Failed to add " + key);
|
||||
Result.wrap(() => container.add({ [key]: v}))
|
||||
.expect("Failed to add " + key);
|
||||
},
|
||||
/**
|
||||
* Exclude any dependencies from being added.
|
||||
@@ -50,15 +50,15 @@ const dependencyBuilder = (container: any, excluded: string[]) => {
|
||||
exclude(...keys: (keyof Dependencies)[]) {
|
||||
keys.forEach(key => excluded.push(key));
|
||||
},
|
||||
|
||||
/**
|
||||
* @param key the key of the dependency
|
||||
* @param v The dependency to swap out.
|
||||
* Swap out a preexisting dependency.
|
||||
*/
|
||||
swap(key: keyof Dependencies, v: Insertable) {
|
||||
Result
|
||||
.wrap(() => container.upsert({ [key]: v }))
|
||||
.expect("Failed to update " + key);
|
||||
Result.wrap(() => container.upsert({ [key]: v }))
|
||||
.expect("Failed to update " + key);
|
||||
},
|
||||
/**
|
||||
* @param key the key of the dependency
|
||||
@@ -70,9 +70,8 @@ const dependencyBuilder = (container: any, excluded: string[]) => {
|
||||
* Swap out a preexisting dependency.
|
||||
*/
|
||||
addDisposer(key: keyof Dependencies, cleanup: AnyFunction) {
|
||||
Result
|
||||
.wrap(() => container.addDisposer({ [key] : cleanup }))
|
||||
.expect("Failed to addDisposer for" + key);
|
||||
Result.wrap(() => container.addDisposer({ [key] : cleanup }))
|
||||
.expect("Failed to addDisposer for" + key);
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -87,15 +86,45 @@ export const insertLogger = (containerSubject: CoreContainer<any>) => {
|
||||
containerSubject
|
||||
.upsert({'@sern/logger': () => new DefaultServices.DefaultLogging});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given the user's conf, check for any excluded/included dependency keys.
|
||||
* Then, call conf.build to get the rest of the users' dependencies.
|
||||
* Finally, update the containerSubject with the new container state
|
||||
* @param conf
|
||||
*/
|
||||
function composeRoot(
|
||||
container: CoreContainer<Partial<Dependencies>>,
|
||||
conf: DependencyConfiguration,
|
||||
) {
|
||||
//container should have no client or logger yet.
|
||||
const hasLogger = conf.exclude?.has('@sern/logger');
|
||||
if (!hasLogger) {
|
||||
insertLogger(container);
|
||||
}
|
||||
//Build the container based on the callback provided by the user
|
||||
conf.build(container as CoreContainer<Omit<CoreDependencies, '@sern/client'>>);
|
||||
|
||||
if (!hasLogger) {
|
||||
container.get('@sern/logger')?.info({ message: 'All dependencies loaded successfully.' });
|
||||
}
|
||||
|
||||
container.ready();
|
||||
}
|
||||
|
||||
export async function makeDependencies<const T extends Dependencies>
|
||||
(conf: ValidDependencyConfig) {
|
||||
containerSubject = new CoreContainer();
|
||||
if(typeof conf === 'function') {
|
||||
const excluded: string[] = [];
|
||||
conf(dependencyBuilder(containerSubject, excluded));
|
||||
|
||||
const includeLogger =
|
||||
!excluded.includes('@sern/logger')
|
||||
&& !containerSubject.getTokens()['@sern/logger'];
|
||||
|
||||
if(!excluded.includes('@sern/logger')
|
||||
&& !containerSubject.getTokens()['@sern/logger']) {
|
||||
if(includeLogger) {
|
||||
insertLogger(containerSubject);
|
||||
}
|
||||
|
||||
@@ -107,5 +136,3 @@ export async function makeDependencies<const T extends Dependencies>
|
||||
return useContainer<T>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Container } from 'iti';
|
||||
import { Disposable, SernEmitter } from '../';
|
||||
import { Disposable } from '../';
|
||||
import * as assert from 'node:assert';
|
||||
import { Subject } from 'rxjs';
|
||||
import { DefaultServices, ModuleStore } from '../_internal';
|
||||
import * as Hooks from './hooks'
|
||||
import * as Hooks from './hooks';
|
||||
import { EventEmitter } from 'node:events';
|
||||
|
||||
|
||||
/**
|
||||
@@ -17,12 +18,13 @@ export class CoreContainer<T extends Partial<Dependencies>> extends Container<T,
|
||||
assert.ok(!this.isReady(), 'Listening for dispose & init should occur prior to sern being ready.');
|
||||
|
||||
const { unsubscribe } = Hooks.createInitListener(this);
|
||||
|
||||
this.ready$
|
||||
.subscribe({ complete: unsubscribe });
|
||||
|
||||
(this as Container<{}, {}>)
|
||||
.add({ '@sern/errors': () => new DefaultServices.DefaultErrorHandling(),
|
||||
'@sern/emitter': () => new SernEmitter,
|
||||
.add({ '@sern/errors': () => new DefaultServices.DefaultErrorHandling,
|
||||
'@sern/emitter': () => new EventEmitter({ captureRejections: true }),
|
||||
'@sern/store': () => new ModuleStore })
|
||||
.add(ctx => {
|
||||
return { '@sern/modules': () =>
|
||||
@@ -33,19 +35,25 @@ export class CoreContainer<T extends Partial<Dependencies>> extends Container<T,
|
||||
isReady() {
|
||||
return this.ready$.closed;
|
||||
}
|
||||
|
||||
hasKey(key: string): boolean {
|
||||
return Boolean((this as Container<any,any>)._context[key]);
|
||||
}
|
||||
|
||||
override async disposeAll() {
|
||||
|
||||
const otherDisposables = Object
|
||||
.entries(this._context)
|
||||
.flatMap(([key, value]) =>
|
||||
'dispose' in value ? [key] : []);
|
||||
|
||||
for(const key of otherDisposables) {
|
||||
otherDisposables.forEach(key => {
|
||||
//possible source of bug: dispose is a property.
|
||||
this.addDisposer({ [key]: (dep: Disposable) => dep.dispose() } as never);
|
||||
}
|
||||
await super.disposeAll()
|
||||
})
|
||||
await super.disposeAll();
|
||||
}
|
||||
|
||||
|
||||
|
||||
ready() {
|
||||
this.ready$.complete();
|
||||
this.ready$.unsubscribe();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CoreDependencies, DependencyConfiguration, IntoDependencies } from '../../types/ioc';
|
||||
import { insertLogger, useContainerRaw } from './base';
|
||||
import { CoreContainer } from './container';
|
||||
import assert from 'node:assert';
|
||||
import type { IntoDependencies } from '../../types/ioc';
|
||||
import { useContainerRaw } from './base';
|
||||
|
||||
/**
|
||||
* @__PURE__
|
||||
@@ -25,6 +25,7 @@ export function transient<T>(cb: () => () => T) {
|
||||
* The new Service api, a cleaner alternative to useContainer
|
||||
* To obtain intellisense, ensure a .d.ts file exists in the root of compilation.
|
||||
* Usually our scaffolding tool takes care of this.
|
||||
* Note: this method only works AFTER your container has been initiated
|
||||
* @since 3.0.0
|
||||
* @example
|
||||
* ```ts
|
||||
@@ -34,7 +35,9 @@ export function transient<T>(cb: () => () => T) {
|
||||
*
|
||||
*/
|
||||
export function Service<const T extends keyof Dependencies>(key: T) {
|
||||
return useContainerRaw().get(key)!;
|
||||
const dep = useContainerRaw().get(key)!;
|
||||
assert(dep, "Requested key " + key + " returned undefined");
|
||||
return dep;
|
||||
}
|
||||
/**
|
||||
* @since 3.0.0
|
||||
@@ -46,32 +49,9 @@ export function Services<const T extends (keyof Dependencies)[]>(...keys: [...T]
|
||||
return keys.map(k => container.get(k)!) as IntoDependencies<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the user's conf, check for any excluded dependency keys.
|
||||
* Then, call conf.build to get the rest of the users' dependencies.
|
||||
* Finally, update the containerSubject with the new container state
|
||||
* @param conf
|
||||
*/
|
||||
export function composeRoot(
|
||||
container: CoreContainer<Partial<Dependencies>>,
|
||||
conf: DependencyConfiguration,
|
||||
) {
|
||||
//container should have no client or logger yet.
|
||||
const hasLogger = conf.exclude?.has('@sern/logger');
|
||||
if (!hasLogger) {
|
||||
insertLogger(container);
|
||||
}
|
||||
//Build the container based on the callback provided by the user
|
||||
conf.build(container as CoreContainer<Omit<CoreDependencies, '@sern/client'>>);
|
||||
|
||||
if (!hasLogger) {
|
||||
container.get('@sern/logger')?.info({ message: 'All dependencies loaded successfully.' });
|
||||
}
|
||||
|
||||
container.ready();
|
||||
}
|
||||
|
||||
export function useContainer<const T extends Dependencies>() {
|
||||
return <V extends (keyof T)[]>(...keys: [...V]) =>
|
||||
keys.map(key => useContainerRaw().get(key as keyof Dependencies)) as IntoDependencies<V>;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ type HookName = 'init';
|
||||
export const createInitListener = (coreContainer : CoreContainer<any>) => {
|
||||
const initCalled = new Set<PropertyKey>();
|
||||
const hasCallableMethod = createPredicate(initCalled);
|
||||
const unsubscribe = coreContainer.on('containerUpserted', async (event) => {
|
||||
const unsubscribe = coreContainer.on('containerUpserted', async event => {
|
||||
|
||||
if(isNotHookable(event)) {
|
||||
return;
|
||||
@@ -21,6 +21,7 @@ export const createInitListener = (coreContainer : CoreContainer<any>) => {
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return { unsubscribe };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user