mirror of
https://github.com/sern-handler/handler
synced 2026-06-06 01:16:55 +00:00
update locals api, docs, tests
This commit is contained in:
@@ -35,8 +35,9 @@
|
||||
"author": "SernDevs",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sern/ioc": "^1.0.3",
|
||||
"@sern/ioc": "^1.0.4",
|
||||
"callsites": "^3.1.0",
|
||||
"deepmerge": "^4.3.1",
|
||||
"node-cron": "^3.0.3",
|
||||
"rxjs": "^7.8.0",
|
||||
"ts-results-es": "^4.1.0"
|
||||
|
||||
@@ -14,11 +14,10 @@ import type { Awaitable } from '../types/utility';
|
||||
*/
|
||||
export function commandModule(mod: InputCommand): Module {
|
||||
const [onEvent, plugins] = partitionPlugins(mod.plugins);
|
||||
return {
|
||||
...mod,
|
||||
onEvent,
|
||||
plugins,
|
||||
} as Module;
|
||||
return { ...mod,
|
||||
onEvent,
|
||||
plugins,
|
||||
locals: {} } as Module;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -29,7 +28,9 @@ export function commandModule(mod: InputCommand): Module {
|
||||
export function eventModule(mod: InputEvent): Module {
|
||||
const [onEvent, plugins] = partitionPlugins(mod.plugins);
|
||||
if(onEvent.length !== 0) throw Error("Event modules cannot have ControlPlugins");
|
||||
return { ...mod, plugins } as Module;
|
||||
return { ...mod,
|
||||
plugins,
|
||||
locals: {} } as Module;
|
||||
}
|
||||
|
||||
/** Create event modules from discord.js client events,
|
||||
|
||||
@@ -17,6 +17,7 @@ import { CommandType } from '../core/structures/enums'
|
||||
import { inspect } from 'node:util'
|
||||
import { disposeAll } from '../core/ioc';
|
||||
import { resultPayload, isAutocomplete, treeSearch, fmt } from '../core/functions'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
function handleError<C>(crashHandler: ErrorHandling, emitter: Emitter, logging?: Logging) {
|
||||
return (pload: unknown, caught: Observable<C>) => {
|
||||
@@ -213,25 +214,26 @@ export function createResultResolver<Output>(config: {
|
||||
};
|
||||
};
|
||||
|
||||
export async function callInitPlugins(module: Module, deps: Dependencies, emit?: boolean) {
|
||||
let _module = module;
|
||||
function isObject(item: unknown) {
|
||||
return (item && typeof item === 'object' && !Array.isArray(item));
|
||||
}
|
||||
|
||||
//_module is frozen, preventing from mutations
|
||||
export async function callInitPlugins(_module: Module, deps: Dependencies, emit?: boolean) {
|
||||
let module = _module;
|
||||
const emitter = deps['@sern/emitter'];
|
||||
for(const plugin of _module.plugins ?? []) {
|
||||
const res = await plugin.execute({
|
||||
module: _module,
|
||||
absPath: _module.meta.absPath,
|
||||
deps
|
||||
});
|
||||
if (!res) throw Error("Plugin did not return anything. " + inspect(plugin, false, Infinity, true));
|
||||
if(res.isErr()) {
|
||||
for(const plugin of module.plugins ?? []) {
|
||||
const result = await plugin.execute({ module, absPath: module.meta.absPath, deps });
|
||||
if (!result) throw Error("Plugin did not return anything. " + inspect(plugin, false, Infinity, true));
|
||||
if(result.isErr()) {
|
||||
if(emit) {
|
||||
emitter?.emit('module.register',
|
||||
resultPayload('failure', module, res.error ?? SernError.PluginFailure));
|
||||
resultPayload('failure', module, result.error ?? SernError.PluginFailure));
|
||||
}
|
||||
throw Error(res.error ?? SernError.PluginFailure);
|
||||
throw Error(result.error ?? SernError.PluginFailure);
|
||||
}
|
||||
}
|
||||
return _module
|
||||
return module
|
||||
}
|
||||
|
||||
async function callPlugins({ args, module, deps, params }: ExecutePayload) {
|
||||
@@ -241,8 +243,8 @@ async function callPlugins({ args, module, deps, params }: ExecutePayload) {
|
||||
if(result.isErr()) {
|
||||
return result;
|
||||
}
|
||||
if(typeof result.value === 'object' && result.value !== null) {
|
||||
state = { ...state, ...result.value, };
|
||||
if(isObject(result.value)) {
|
||||
state = merge(state, result.value!);
|
||||
}
|
||||
}
|
||||
return Ok(state);
|
||||
|
||||
@@ -23,8 +23,8 @@ export default async function(dir: string, deps : UnpackedDependencies) {
|
||||
if(!validType) {
|
||||
throw Error(`Found ${module.name} at ${module.meta.absPath}, which has incorrect \`type\``);
|
||||
}
|
||||
const resultModule = await callInitPlugins(module, deps, true);
|
||||
// FREEZE! no more writing!!
|
||||
const resultModule = await callInitPlugins(module, deps, true); // FREEZE! no more writing!!
|
||||
|
||||
commands.set(resultModule.meta.id, Object.freeze(resultModule));
|
||||
sEmitter.emit('module.register', resultPayload('success', resultModule));
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import type {
|
||||
} from 'discord.js';
|
||||
import type { CommandType, EventType } from '../core/structures/enums';
|
||||
import { Context } from '../core/structures/context'
|
||||
import { AnyPlugin, ControlPlugin, InitPlugin } from './core-plugin';
|
||||
import { ControlPlugin, InitPlugin, Plugin } from './core-plugin';
|
||||
import { Awaitable, SernEventsMapping } from './utility';
|
||||
|
||||
//state, deps, type (very original)
|
||||
@@ -41,6 +41,7 @@ export interface Module {
|
||||
id: string;
|
||||
absPath: string;
|
||||
}
|
||||
locals: Record<string,unknown>
|
||||
execute(...args: any[]): Awaitable<any>;
|
||||
}
|
||||
|
||||
@@ -191,10 +192,10 @@ export interface SernAutocompleteData
|
||||
}
|
||||
|
||||
type CommandModuleNoPlugins = {
|
||||
[T in CommandType]: Omit<CommandModuleDefs[T], 'plugins' | 'onEvent' | 'meta'>;
|
||||
[T in CommandType]: Omit<CommandModuleDefs[T], 'plugins' | 'onEvent' | 'meta' | 'locals'>;
|
||||
};
|
||||
type EventModulesNoPlugins = {
|
||||
[T in EventType]: Omit<EventModuleDefs[T], 'plugins' | 'onEvent' | 'meta'> ;
|
||||
[T in EventType]: Omit<EventModuleDefs[T], 'plugins' | 'onEvent' | 'meta' | 'locals'> ;
|
||||
};
|
||||
|
||||
export type InputEvent = {
|
||||
@@ -206,7 +207,7 @@ export type InputEvent = {
|
||||
|
||||
export type InputCommand = {
|
||||
[T in CommandType]: CommandModuleNoPlugins[T] & {
|
||||
plugins?: AnyPlugin[];
|
||||
plugins?: Plugin[];
|
||||
};
|
||||
}[CommandType];
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ import type {
|
||||
} from 'discord.js';
|
||||
|
||||
export type PluginResult = Awaitable<Result<Record<string,unknown>|undefined, string|undefined>>;
|
||||
|
||||
export interface InitArgs<T extends Processed<Module> = Processed<Module>> {
|
||||
module: T;
|
||||
absPath: string;
|
||||
@@ -46,6 +45,7 @@ export interface Plugin<Args extends any[] = any[]> {
|
||||
|
||||
export interface InitPlugin<Args extends any[] = any[]> extends Plugin<Args> {
|
||||
type: PluginType.Init;
|
||||
execute: (...args: Args) => PluginResult;
|
||||
}
|
||||
export interface ControlPlugin<Args extends any[] = any[]> extends Plugin<Args> {
|
||||
type: PluginType.Control;
|
||||
|
||||
15
src/types/dependencies.d.ts
vendored
15
src/types/dependencies.d.ts
vendored
@@ -7,6 +7,21 @@
|
||||
import { CoreDependencies } from './ioc';
|
||||
|
||||
declare global {
|
||||
/**
|
||||
* discord.js client.
|
||||
* '@sern/client': Client
|
||||
* sern emitter listens to events that happen throughout
|
||||
* the handler. some include module.register, module.activate.
|
||||
* '@sern/emitter': Contracts.Emitter;
|
||||
* An error handler which is the final step before
|
||||
* the sern process actually crashes.
|
||||
'@sern/errors': Contracts.ErrorHandling;
|
||||
* Optional logger. Performs ... logging
|
||||
* '@sern/logger'?: Contracts.Logging;
|
||||
* Readonly module store. sern stores these
|
||||
* by module.meta.id -> Module
|
||||
* '@sern/modules': Map<string, Module>;
|
||||
*/
|
||||
interface Dependencies extends CoreDependencies {}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,28 @@ import { Module } from './core-modules';
|
||||
|
||||
|
||||
export interface CoreDependencies {
|
||||
/**
|
||||
* discord.js client.
|
||||
*/
|
||||
'@sern/client': Client;
|
||||
/**
|
||||
* sern emitter listens to events that happen throughout
|
||||
* the handler. some include module.register, module.activate.
|
||||
*/
|
||||
'@sern/emitter': Contracts.Emitter;
|
||||
/**
|
||||
* An error handler which is the final step before
|
||||
* the sern process actually crashes.
|
||||
*/
|
||||
'@sern/errors': Contracts.ErrorHandling;
|
||||
/**
|
||||
* Optional logger. Performs ... logging
|
||||
*/
|
||||
'@sern/logger'?: Contracts.Logging;
|
||||
/**
|
||||
* Readonly module store. sern stores these
|
||||
* by module.meta.id -> Module
|
||||
*/
|
||||
'@sern/modules': Map<string, Module>;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,13 +57,8 @@ vi.mock('discord.js', async (importOriginal) => {
|
||||
|
||||
function createRandomPlugin (s: 'go', mut?: Partial<Module>) {
|
||||
return CommandInitPlugin(({ module }) => {
|
||||
if(mut) {
|
||||
for(const [k,v] of Object.entries(mut)) {
|
||||
module[k] = v
|
||||
}
|
||||
}
|
||||
return s == 'go'
|
||||
? controller.next()
|
||||
? controller.next(mut)
|
||||
: controller.stop()
|
||||
})
|
||||
}
|
||||
@@ -103,16 +98,22 @@ describe('eventDispatcher standard', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test ('mutate with init plugins', async () => {
|
||||
test ('call init plugins', async () => {
|
||||
const deps = mockDeps()
|
||||
const plugins = createRandomPlugin('go', { name: "abc" })
|
||||
const mod = createRandomModule([plugins])
|
||||
console.log(mod)
|
||||
const s = await callInitPlugins(mod, deps, false)
|
||||
console.log(s)
|
||||
expect("abc").equal(s.name)
|
||||
})
|
||||
|
||||
test('init plugins replace array', async () => {
|
||||
const deps = mockDeps()
|
||||
const plugins = createRandomPlugin('go', { opts: [] })
|
||||
const plugins2 = createRandomPlugin('go', { opts: ['a'] })
|
||||
const mod = createRandomModule([plugins, plugins2])
|
||||
const s = await callInitPlugins(mod, deps, false)
|
||||
expect(['a']).deep.equal(s.opts)
|
||||
})
|
||||
|
||||
test('call control plugin ', async () => {
|
||||
const plugin = CommandControlPlugin<CommandType.Slash>((ctx,sdt) => {
|
||||
|
||||
18
yarn.lock
18
yarn.lock
@@ -553,12 +553,13 @@ __metadata:
|
||||
resolution: "@sern/handler@workspace:."
|
||||
dependencies:
|
||||
"@faker-js/faker": ^8.0.1
|
||||
"@sern/ioc": ^1.0.3
|
||||
"@sern/ioc": ^1.0.4
|
||||
"@types/node": ^20.0.0
|
||||
"@types/node-cron": ^3.0.11
|
||||
"@typescript-eslint/eslint-plugin": 5.58.0
|
||||
"@typescript-eslint/parser": 5.59.1
|
||||
callsites: ^3.1.0
|
||||
deepmerge: ^4.3.1
|
||||
discord.js: ^14.x.x
|
||||
eslint: 8.39.0
|
||||
node-cron: ^3.0.3
|
||||
@@ -569,10 +570,10 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@sern/ioc@npm:^1.0.3":
|
||||
version: 1.0.3
|
||||
resolution: "@sern/ioc@npm:1.0.3"
|
||||
checksum: 2c640cabbf3927d923b57233e660be1d6d4da1cd376a4ba77b118fd9aa5ef81c465d287357a15a7cd7827306dd614c73bde68751978d2030c3a4877dcbb0d02d
|
||||
"@sern/ioc@npm:^1.0.4":
|
||||
version: 1.0.4
|
||||
resolution: "@sern/ioc@npm:1.0.4"
|
||||
checksum: 3d1a63099b3e8ff0d44bb73007b1d66c3f3b27cf7a193c2d9122e021cb72be1ced535ea98efaf72602f371a489177e3b144ed72da8d7de80887c0408ce79cce2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1172,6 +1173,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"deepmerge@npm:^4.3.1":
|
||||
version: 4.3.1
|
||||
resolution: "deepmerge@npm:4.3.1"
|
||||
checksum: 2024c6a980a1b7128084170c4cf56b0fd58a63f2da1660dcfe977415f27b17dbe5888668b59d0b063753f3220719d5e400b7f113609489c90160bb9a5518d052
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"diff-sequences@npm:^29.6.3":
|
||||
version: 29.6.3
|
||||
resolution: "diff-sequences@npm:29.6.3"
|
||||
|
||||
Reference in New Issue
Block a user