refactor: rename, format, move things

This commit is contained in:
Jacob Nguyen
2023-05-08 11:36:25 -05:00
parent 9718360530
commit cd1568ff69
7 changed files with 105 additions and 61 deletions

View File

@@ -1,6 +1,3 @@
import type { Observable } from 'rxjs';
import type { Logging } from './logging';
import util from 'node:util';
/**
* @since 2.0.0
*/
@@ -35,16 +32,4 @@ export class DefaultErrorHandling implements ErrorHandling {
}
}
export function handleError<C>(crashHandler: ErrorHandling, logging?: Logging) {
return (pload: unknown, caught: Observable<C>) => {
// This is done to fit the ErrorHandling contract
const err = pload instanceof Error ? pload : Error(util.format(pload));
if (crashHandler.keepAlive == 0) {
crashHandler.crash(err);
}
//formatted payload
logging?.error({ message: util.format(pload) });
crashHandler.updateAlive(err);
return caught;
};
}

View File

@@ -8,12 +8,18 @@ export interface ModuleManager {
get(id: string): string | undefined;
set(id: string, path: string): void;
getPublishableCommands(): Promise<CommandModule[]>;
remove(id: string) : boolean
}
/**
* @since 2.0.0
*/
export class DefaultModuleManager implements ModuleManager {
constructor(private moduleStore: ModuleStore) {}
remove(id: string): boolean {
throw new Error('Method not implemented.');
}
get(id: string) {
return this.moduleStore.get(id);
}

View File

@@ -1,5 +1,5 @@
import { CommandType, EventType, PluginType } from '../structures';
import type { Plugin, PluginResult, EventArgs, CommandArgs } from '../../types/plugin';
import { CommandType, EventType, PluginType } from './structures';
import type { Plugin, PluginResult, EventArgs, CommandArgs } from '../types/plugin';
import type { ClientEvents } from 'discord.js';
export function makePlugin<V extends unknown[]>(

View File

@@ -1,11 +1,12 @@
import { Container } from 'iti';
import type { Dependencies, DependencyConfiguration, MapDeps, Wrapper } from '../types/core';
import { DefaultErrorHandling, DefaultLogging, DefaultModuleManager } from './contracts';
import { Result } from 'ts-results-es';
import { createContainer } from 'iti';
import { SernEmitter } from './structures';
import { SernError } from './structures/errors';
export let containerSubject: Container<{}, {}>
import * as assert from 'node:assert'
import * as types from 'node:util/types'
import { Awaitable } from '../types/handler';
export let containerSubject: Container<{}, {}>;
const requiredDependencyKeys = ['@sern/emitter', '@sern/errors', '@sern/logger'] as const;
/**
* @__PURE__
@@ -33,8 +34,8 @@ export function transient<T>(cb: () => () => T) {
* Finally, update the containerSubject with the new container state
* @param conf
*/
export function composeRoot<T extends Dependencies>(conf: DependencyConfiguration<T>) {
//This should have no client or logger yet.
export async function composeRoot<T extends Dependencies>(conf: DependencyConfiguration<T>) {
//container should have no client or logger yet.
const excludeLogger = conf.exclude?.has('@sern/logger');
if (!excludeLogger) {
containerSubject.add({
@@ -42,52 +43,97 @@ export function composeRoot<T extends Dependencies>(conf: DependencyConfiguratio
});
}
//Build the container based on the callback provided by the user
const container = conf.build(containerSubject as Container<Omit<Dependencies, '@sern/client'>, {}>);
const updatedContainer = await conf.build(containerSubject as Container<Omit<Dependencies, '@sern/client'>, {}>);
try {
container.get('@sern/client');
updatedContainer.get('@sern/client');
} catch {
throw new Error(SernError.MissingRequired + " No client was provided")
}
if (!excludeLogger) {
container.get('@sern/logger')?.info({ message: 'All dependencies loaded successfully.' });
updatedContainer.get('@sern/logger')?.info({ message: 'All dependencies loaded successfully.' });
}
}
export function useContainer<const T extends Dependencies>() {
const container = containerSubject as Container<T, {}>;
console.warn(`Warning: using a container hook is not recommended. Could lead to many unwanted side effects`);
return <V extends (keyof T)[]>(...keys: [...V]) =>
keys.map(key => Result.wrap(() => container.get(key)).expect(`Unregistered dependency: ${String(key)}`)) as MapDeps<T, V>;
keys.map(key => (containerSubject as Container<T, {}>).get(key)) as MapDeps<T, V>;
}
/**
* Returns the underlying data structure holding all dependencies.
* Please be careful as this only gets the client's current state.
* Exposes some methods from iti
* Exposes methods from iti
*/
export function useContainerRaw<T extends Dependencies>() {
if(!containerSubject) {
throw Error("Could not find container. Did you call makeDependencies?")
}
return containerSubject as Container<T, {}>;
export function useContainerRaw() {
assert.ok(
containerSubject && (containerSubject as CoreContainer).isReady(),
"Could not find container or container wasn't ready. Did you call makeDependencies?"
);
return containerSubject;
}
/**
* @since 2.0.0
* @param conf a configuration for creating your project dependencies
*/
export async function makeDependencies<const T extends Dependencies>(
conf: DependencyConfiguration<T>,
) {
containerSubject = new CoreContainer();
//Until there are more optional dependencies, just check if the logger exists
await composeRoot(conf);
(containerSubject as CoreContainer).ready();
return useContainer<T>();
}
export interface Init {
init() : Awaitable<unknown>
}
/**
* Provides all the defaults for sern to function properly.
* The only user provided dependency needs to be @sern/client
*/
function defaultContainer() {
return createContainer()
.add({
'@sern/errors': () => new DefaultErrorHandling(),
'@sern/store': () => new Map<string, string>(),
'@sern/emitter': () => new SernEmitter()
})
.add(ctx => {
return {
'@sern/modules': () => new DefaultModuleManager(ctx['@sern/store']),
};
})
class CoreContainer extends Container<Dependencies, {}> {
private _ready = false;
constructor() {
super();
(this as Container<{}, {}>)
.add({
'@sern/errors': () => new DefaultErrorHandling(),
'@sern/store': () => new Map<string, string>(),
'@sern/emitter': () => new SernEmitter()
})
.add(ctx => {
return { '@sern/modules': () => new DefaultModuleManager(ctx['@sern/store']) };
})
}
async withInit<const Keys extends keyof Dependencies>(...keys: Keys[]) {
if(this.isReady()) {
throw Error("You cannot call this method after sern has started");
}
for await (const k of keys) {
const dep = this.get(k);
assert.ok(dep !== undefined);
if('init' in dep && typeof dep.init === 'function') {
types.isAsyncFunction(dep.init)
? await dep.init()
: dep.init()
} else {
throw Error(`called withInit with key ${k} but found nothing to init`)
}
}
return this;
}
isReady() {
return this._ready;
}
ready() {
this._ready = true;
}
}
@@ -104,16 +150,3 @@ export function makeFetcher<Dep extends Dependencies>(
...(otherKeys as (keyof Dependencies)[]),
) as MapDeps<Dep, [...typeof requiredDependencyKeys, ...Keys]>;
}
/**
* @since 2.0.0
* @param conf a configuration for creating your project dependencies
*/
export function makeDependencies<const T extends Dependencies>(
conf: DependencyConfiguration<T>,
) {
containerSubject = defaultContainer()
//Until there are more optional dependencies, just check if the logger exists
composeRoot(conf);
return useContainer<T>();
}

View File

@@ -32,6 +32,7 @@ function checkIsProcessed<T extends Module>(m: T): asserts m is Processed<T> {
}
export const fmtFileName = (n: string) => n.substring(0, n.length - 3);
/**
* a directory string is converted into a stream of modules.
* starts the stream of modules that sern needs to process on init

View File

@@ -1,7 +1,7 @@
/**
* This file holds sern's rxjs operators used for processing data.
* Each function should be modular and testable, not bound to discord / sern
* and independent of each other
* and independent of each other.
*/
import {
concatMap,
@@ -21,6 +21,8 @@ import type { PluginResult, VoidResult } from '../types/plugin';
import { Result } from 'ts-results-es';
import { Awaitable } from '../types/handler';
import { EventEmitter } from 'node:events';
import { ErrorHandling, Logging } from './contracts';
import util from 'node:util'
/**
* if {src} is true, mapTo V, else ignore
* @param item
@@ -91,3 +93,19 @@ export const everyPluginOk: OperatorFunction<VoidResult, boolean> = pipe(
export const sharedObservable = <T>(e: EventEmitter, eventName: string) => {
return (fromEvent(e, eventName) as Observable<T>).pipe(share());
};
export function handleError<C>(crashHandler: ErrorHandling, logging?: Logging) {
return (pload: unknown, caught: Observable<C>) => {
// This is done to fit the ErrorHandling contract
const err = pload instanceof Error
? pload
: Error(util.inspect(pload, { colors: true }));
if (crashHandler.keepAlive == 0) {
crashHandler.crash(err);
}
//formatted payload
logging?.error({ message: util.inspect(pload) });
crashHandler.updateAlive(err);
return caught;
};
}

View File

@@ -18,6 +18,7 @@ type AnyCommandInteraction =
| ChatInputCommandInteraction
| MessageContextMenuCommandInteraction
| UserContextMenuCommandInteraction;
export function isMessageComponent(i: InteractionTypable): i is AnyMessageComponentInteraction {
return i.type === InteractionType.MessageComponent;
}