mirror of
https://github.com/sern-handler/handler
synced 2026-06-06 01:16:55 +00:00
refactor: rename, format, move things
This commit is contained in:
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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[]>(
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ type AnyCommandInteraction =
|
||||
| ChatInputCommandInteraction
|
||||
| MessageContextMenuCommandInteraction
|
||||
| UserContextMenuCommandInteraction;
|
||||
|
||||
export function isMessageComponent(i: InteractionTypable): i is AnyMessageComponentInteraction {
|
||||
return i.type === InteractionType.MessageComponent;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user