mirror of
https://github.com/sern-handler/handler
synced 2026-06-26 09:42:15 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
395f75bcda | ||
|
|
215aca2f46 | ||
|
|
a7f5ea269f | ||
|
|
52d6368440 | ||
|
|
1e723a4154 | ||
|
|
5fe13f43d2 | ||
|
|
ab9d39306a | ||
|
|
d429f3adbf | ||
|
|
5e011b471e | ||
|
|
41344608c6 |
39
.github/workflows/codeql-analysis.yml
vendored
39
.github/workflows/codeql-analysis.yml
vendored
@@ -1,39 +0,0 @@
|
|||||||
name: "CodeQL"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
paths: ["src/**/*"]
|
|
||||||
pull_request:
|
|
||||||
branches: [ main ]
|
|
||||||
paths: ["src/**/*"]
|
|
||||||
schedule:
|
|
||||||
- cron: '37 20 * * 4'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
language: [ 'javascript' ]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v2
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v2
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v2
|
|
||||||
4
.github/workflows/npm-publish.yml
vendored
4
.github/workflows/npm-publish.yml
vendored
@@ -2,6 +2,10 @@ name: NPM / Publish
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
# We only publish if the version of sern handler is different. workflow automatically cancels if verson is the same
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'main'
|
||||||
jobs:
|
jobs:
|
||||||
test-and-publish:
|
test-and-publish:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
29
.github/workflows/test.yml
vendored
Normal file
29
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||||
|
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
||||||
|
|
||||||
|
name: Node.js CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [18.x, 19.x, 20.x]
|
||||||
|
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'npm'
|
||||||
|
- run: npm install -g yarn
|
||||||
|
- run: yarn install
|
||||||
|
- run: yarn test
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -91,3 +91,7 @@ dist
|
|||||||
# Yarn files
|
# Yarn files
|
||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.yarn/build-state.yml
|
.yarn/build-state.yml
|
||||||
|
|
||||||
|
.yalc
|
||||||
|
|
||||||
|
yalc.lock
|
||||||
|
|||||||
14
CHANGELOG.md
14
CHANGELOG.md
@@ -1,5 +1,19 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [3.0.2](https://github.com/sern-handler/handler/compare/v3.0.1...v3.0.2) (2023-08-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* invalid id for cts, mts, cjs, mjs files, node paths ([#318](https://github.com/sern-handler/handler/issues/318)) ([a7f5ea2](https://github.com/sern-handler/handler/commit/a7f5ea269fb344e221d10dbdc26a1611ffc8138f))
|
||||||
|
|
||||||
|
## [3.0.1](https://github.com/sern-handler/handler/compare/v3.0.0...v3.0.1) (2023-08-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* collectors ([4134460](https://github.com/sern-handler/handler/commit/41344608c677b6069c46412f5f16e4337182ca7d))
|
||||||
|
|
||||||
## [3.0.0](https://github.com/sern-handler/handler/compare/v2.6.3...v3.0.0) (2023-07-29)
|
## [3.0.0](https://github.com/sern-handler/handler/compare/v2.6.3...v3.0.0) (2023-07-29)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "@sern/handler",
|
"name": "@sern/handler",
|
||||||
"packageManager": "yarn@3.5.0",
|
"packageManager": "yarn@3.5.0",
|
||||||
"version": "3.0.0",
|
"version": "3.0.2",
|
||||||
"description": "A complete, customizable, typesafe, & reactive framework for discord bots.",
|
"description": "A complete, customizable, typesafe, & reactive framework for discord bots.",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/cjs/index.cjs",
|
"module": "./dist/index.mjs",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"iti": "^0.6.0",
|
"iti": "^0.6.0",
|
||||||
"rxjs": "^7.8.0",
|
"rxjs": "^7.8.0",
|
||||||
"ts-results-es": "^3.6.0"
|
"ts-results-es": "^3.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@faker-js/faker": "^8.0.1",
|
"@faker-js/faker": "^8.0.1",
|
||||||
|
|||||||
@@ -80,11 +80,6 @@ export async function composeRoot(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useContainer<const T extends Dependencies>() {
|
export function useContainer<const T extends Dependencies>() {
|
||||||
console.warn(`
|
|
||||||
Warning: using a container hook (useContainer) is not recommended.
|
|
||||||
Could lead to many unwanted side effects.
|
|
||||||
Use the new Service(s) api function instead.
|
|
||||||
`);
|
|
||||||
return <V extends (keyof T)[]>(...keys: [...V]) =>
|
return <V extends (keyof T)[]>(...keys: [...V]) =>
|
||||||
keys.map(key => useContainerRaw().get(key as keyof Dependencies)) as IntoDependencies<V>;
|
keys.map(key => useContainerRaw().get(key as keyof Dependencies)) as IntoDependencies<V>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Result } from 'ts-results-es';
|
import { Result } from 'ts-results-es';
|
||||||
import { type Observable, from, mergeMap, ObservableInput } from 'rxjs';
|
import { type Observable, from, mergeMap, ObservableInput } from 'rxjs';
|
||||||
import { readdir, stat } from 'fs/promises';
|
import { readdir, stat } from 'fs/promises';
|
||||||
import { basename, extname, join, resolve } from 'path';
|
import { basename, extname, join, resolve, parse } from 'path';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import { createRequire } from 'node:module';
|
import { createRequire } from 'node:module';
|
||||||
import type { ImportPayload, Wrapper } from '../types/core';
|
import type { ImportPayload, Wrapper } from '../types/core';
|
||||||
@@ -25,27 +25,21 @@ export type ModuleResult<T> = Promise<ImportPayload<T>>;
|
|||||||
export async function importModule<T>(absPath: string) {
|
export async function importModule<T>(absPath: string) {
|
||||||
let module = await import(absPath).then(esm => esm.default);
|
let module = await import(absPath).then(esm => esm.default);
|
||||||
|
|
||||||
assert(
|
assert(module, `Found no export for module at ${absPath}. Forgot to ignore with "!"? (!${basename(absPath)})?`);
|
||||||
module,
|
|
||||||
'Found no default export for command module at ' +
|
|
||||||
absPath +
|
|
||||||
'Forgot to ignore with "!"? (!filename.ts)?',
|
|
||||||
);
|
|
||||||
if ('default' in module) {
|
if ('default' in module) {
|
||||||
module = module.default;
|
module = module.default;
|
||||||
}
|
}
|
||||||
return Result.wrap(() => module.getInstance()).unwrapOr(module) as T;
|
return Result
|
||||||
|
.wrap(() => module.getInstance())
|
||||||
|
.unwrapOr(module) as T;
|
||||||
}
|
}
|
||||||
export async function defaultModuleLoader<T extends Module>(absPath: string): ModuleResult<T> {
|
export async function defaultModuleLoader<T extends Module>(absPath: string): ModuleResult<T> {
|
||||||
let module = await importModule<T>(absPath);
|
let module = await importModule<T>(absPath);
|
||||||
assert.ok(
|
assert(module, `Found an undefined module: ${absPath}`);
|
||||||
module,
|
|
||||||
"Found an undefined module. Forgot to ignore it with a '!' ie (!filename.ts)?",
|
|
||||||
);
|
|
||||||
return { module, absPath };
|
return { module, absPath };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fmtFileName = (n: string) => n.substring(0, n.length - 3);
|
export const fmtFileName = (fileName: string) => parse(fileName).name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a directory string is converted into a stream of modules.
|
* a directory string is converted into a stream of modules.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
import { Emitter, ErrorHandling, Logging } from './contracts';
|
import { Emitter, ErrorHandling, Logging } from './contracts';
|
||||||
import util from 'node:util';
|
import util from 'node:util';
|
||||||
import type { PluginResult, VoidResult } from '../types/core-plugin';
|
import type { PluginResult, VoidResult } from '../types/core-plugin';
|
||||||
|
import type { Result } from 'ts-results-es'
|
||||||
/**
|
/**
|
||||||
* if {src} is true, mapTo V, else ignore
|
* if {src} is true, mapTo V, else ignore
|
||||||
* @param item
|
* @param item
|
||||||
@@ -69,3 +70,17 @@ export function handleError<C>(crashHandler: ErrorHandling, logging?: Logging) {
|
|||||||
return caught;
|
return caught;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// Temporary until i get rxjs operators working on ts-results-es
|
||||||
|
export const filterTap = <K, R>(onErr: (e: R) => void): OperatorFunction<Result<K, R>, K> =>
|
||||||
|
pipe(
|
||||||
|
concatMap(result => {
|
||||||
|
if(result.ok) {
|
||||||
|
return of(result.val)
|
||||||
|
}
|
||||||
|
onErr(result.val);
|
||||||
|
return EMPTY
|
||||||
|
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,9 +26,8 @@ import { Emitter, ErrorHandling, Logging, ModuleManager, useContainerRaw } from
|
|||||||
import { contextArgs, createDispatcher, dispatchMessage } from './dispatchers';
|
import { contextArgs, createDispatcher, dispatchMessage } from './dispatchers';
|
||||||
import { ObservableInput, pipe } from 'rxjs';
|
import { ObservableInput, pipe } from 'rxjs';
|
||||||
import { SernEmitter } from '../core';
|
import { SernEmitter } from '../core';
|
||||||
import { Result } from 'ts-results-es';
|
import { Err, Ok, Result } from 'ts-results-es';
|
||||||
import type { Awaitable } from '../types/utility';
|
import type { Awaitable } from '../types/utility';
|
||||||
import assert from 'node:assert';
|
|
||||||
import type { ControlPlugin } from '../types/core-plugin';
|
import type { ControlPlugin } from '../types/core-plugin';
|
||||||
import type { AnyModule, CommandModule, Module, Processed } from '../types/core-modules';
|
import type { AnyModule, CommandModule, Module, Processed } from '../types/core-modules';
|
||||||
import type { ImportPayload } from '../types/core';
|
import type { ImportPayload } from '../types/core';
|
||||||
@@ -37,7 +36,10 @@ function createGenericHandler<Source, Narrowed extends Source, Output>(
|
|||||||
source: Observable<Source>,
|
source: Observable<Source>,
|
||||||
makeModule: (event: Narrowed) => Promise<Output>,
|
makeModule: (event: Narrowed) => Promise<Output>,
|
||||||
) {
|
) {
|
||||||
return (pred: (i: Source) => i is Narrowed) => source.pipe(filter(pred), concatMap(makeModule));
|
return (pred: (i: Source) => i is Narrowed) =>
|
||||||
|
source.pipe(
|
||||||
|
filter(pred),
|
||||||
|
concatMap(makeModule));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,14 +67,18 @@ export function createInteractionHandler<T extends Interaction>(
|
|||||||
source: Observable<Interaction>,
|
source: Observable<Interaction>,
|
||||||
mg: ModuleManager,
|
mg: ModuleManager,
|
||||||
) {
|
) {
|
||||||
return createGenericHandler<Interaction, T, ReturnType<typeof createDispatcher>>(
|
return createGenericHandler<Interaction, T, Result<ReturnType<typeof createDispatcher>, void>>(
|
||||||
source,
|
source,
|
||||||
async event => {
|
async event => {
|
||||||
const fullPath = mg.get(Id.reconstruct(event));
|
const fullPath = mg.get(Id.reconstruct(event));
|
||||||
assert(fullPath, SernError.UndefinedModule + ' No full path found in module store');
|
if(!fullPath) {
|
||||||
return Files.defaultModuleLoader<Processed<CommandModule>>(fullPath).then(payload =>
|
return Err.EMPTY
|
||||||
createDispatcher({ module: payload.module, event }),
|
}
|
||||||
);
|
return Files
|
||||||
|
.defaultModuleLoader<Processed<CommandModule>>(fullPath)
|
||||||
|
.then(payload =>
|
||||||
|
Ok(createDispatcher({ module: payload.module, event }))
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -86,11 +92,15 @@ export function createMessageHandler(
|
|||||||
const [prefix, ...rest] = fmt(event.content, defaultPrefix);
|
const [prefix, ...rest] = fmt(event.content, defaultPrefix);
|
||||||
const fullPath = mg.get(`${prefix}_A1`);
|
const fullPath = mg.get(`${prefix}_A1`);
|
||||||
|
|
||||||
assert(fullPath, SernError.UndefinedModule + ' No full path found in module store');
|
if(!fullPath) {
|
||||||
return Files.defaultModuleLoader<Processed<CommandModule>>(fullPath).then(payload => {
|
return Err('Possibly undefined behavior: could not find a static id to resolve ')
|
||||||
const args = contextArgs(event, rest);
|
}
|
||||||
return dispatchMessage(payload.module, args);
|
return Files
|
||||||
});
|
.defaultModuleLoader<Processed<CommandModule>>(fullPath)
|
||||||
|
.then(payload => {
|
||||||
|
const args = contextArgs(event, rest);
|
||||||
|
return Ok(dispatchMessage(payload.module, args));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
isModal,
|
isModal,
|
||||||
sharedEventStream,
|
sharedEventStream,
|
||||||
SernError,
|
SernError,
|
||||||
|
filterTap,
|
||||||
} from '../core/_internal';
|
} from '../core/_internal';
|
||||||
import { createInteractionHandler, executeModule, makeModuleExecutor } from './_internal';
|
import { createInteractionHandler, executeModule, makeModuleExecutor } from './_internal';
|
||||||
import type { DependencyList } from '../types/ioc';
|
import type { DependencyList } from '../types/ioc';
|
||||||
@@ -22,10 +23,11 @@ export function interactionHandler([emitter, , , modules, client]: DependencyLis
|
|||||||
handle(isCommand),
|
handle(isCommand),
|
||||||
handle(isModal),
|
handle(isModal),
|
||||||
);
|
);
|
||||||
return interactionHandler$.pipe(
|
return interactionHandler$
|
||||||
makeModuleExecutor(module => {
|
.pipe(
|
||||||
emitter.emit('module.activate', SernEmitter.failure(module, SernError.PluginFailure));
|
filterTap(e => emitter.emit('warning', SernEmitter.warning(e))),
|
||||||
}),
|
makeModuleExecutor(module =>
|
||||||
concatMap(payload => executeModule(emitter, payload)),
|
emitter.emit('module.activate', SernEmitter.failure(module, SernError.PluginFailure))),
|
||||||
|
concatMap(payload => executeModule(emitter, payload)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { concatMap, EMPTY } from 'rxjs';
|
import { concatMap, EMPTY } from 'rxjs';
|
||||||
import type { Message } from 'discord.js';
|
import type { Message } from 'discord.js';
|
||||||
import { SernEmitter } from '../core';
|
import { SernEmitter } from '../core';
|
||||||
import { sharedEventStream, SernError } from '../core/_internal';
|
import { sharedEventStream, SernError, filterTap } from '../core/_internal';
|
||||||
import { createMessageHandler, executeModule, makeModuleExecutor } from './_internal';
|
import { createMessageHandler, executeModule, makeModuleExecutor } from './_internal';
|
||||||
import type { DependencyList } from '../types/ioc';
|
import type { DependencyList } from '../types/ioc';
|
||||||
|
|
||||||
@@ -38,6 +38,7 @@ export function messageHandler(
|
|||||||
const msgCommands$ = handle(isNonBot(defaultPrefix));
|
const msgCommands$ = handle(isNonBot(defaultPrefix));
|
||||||
|
|
||||||
return msgCommands$.pipe(
|
return msgCommands$.pipe(
|
||||||
|
filterTap((e) => emitter.emit('warning', SernEmitter.warning(e))),
|
||||||
makeModuleExecutor(module => {
|
makeModuleExecutor(module => {
|
||||||
emitter.emit('module.activate', SernEmitter.failure(module, SernError.PluginFailure));
|
emitter.emit('module.activate', SernEmitter.failure(module, SernError.PluginFailure));
|
||||||
}),
|
}),
|
||||||
|
|||||||
23
test/core/module-loading.test.ts
Normal file
23
test/core/module-loading.test.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
import { faker } from '@faker-js/faker'
|
||||||
|
import * as Files from '../../src/core/module-loading'
|
||||||
|
describe('module-loading', () => {
|
||||||
|
it('should properly extract filename from file, nested once', () => {
|
||||||
|
const extension = faker.system.fileExt()
|
||||||
|
const name = faker.system.fileName({ extensionCount: 0 })
|
||||||
|
const filename = Files.fmtFileName(name+'.'+extension);
|
||||||
|
expect(filename).toBe(name)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// todo: handle commands with multiple extensions
|
||||||
|
// it('should properly extract filename from file, nested multiple', () => {
|
||||||
|
// const extension = faker.system.fileExt()
|
||||||
|
// const extension2 = faker.system.fileExt()
|
||||||
|
// const name = faker.system.fileName({ extensionCount: 0 })
|
||||||
|
// const filename = Files.fmtFileName(name+'.'+extension+'.'+extension2);
|
||||||
|
// console.log(filename, name)
|
||||||
|
// expect(filename).toBe(name)
|
||||||
|
//
|
||||||
|
// })
|
||||||
|
})
|
||||||
10
yarn.lock
10
yarn.lock
@@ -619,7 +619,7 @@ __metadata:
|
|||||||
iti: ^0.6.0
|
iti: ^0.6.0
|
||||||
prettier: 2.8.8
|
prettier: 2.8.8
|
||||||
rxjs: ^7.8.0
|
rxjs: ^7.8.0
|
||||||
ts-results-es: ^3.6.0
|
ts-results-es: ^3.6.1
|
||||||
tsup: ^6.7.0
|
tsup: ^6.7.0
|
||||||
typescript: 5.0.2
|
typescript: 5.0.2
|
||||||
vitest: latest
|
vitest: latest
|
||||||
@@ -3844,10 +3844,10 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"ts-results-es@npm:^3.6.0":
|
"ts-results-es@npm:^3.6.1":
|
||||||
version: 3.6.0
|
version: 3.6.1
|
||||||
resolution: "ts-results-es@npm:3.6.0"
|
resolution: "ts-results-es@npm:3.6.1"
|
||||||
checksum: 28593545cad764efce2de64b09037f855c3bbd499e3f5e2683863c86abc9b4453e5aa7d651edff93d42340a2c24f8bbabcf92467fcec767e09bbeeac01e97396
|
checksum: af0d93ee4d3bd9e99a5fd4ac4b0ad090aef0a61e1f38ee596cfebe8d47090b34a2557d3778e00b4aae7c74962133805275ffffe56716e4d747fa559a926d9ced
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user