You see, on today's standards, having a simple logo is essential. Our logo aligns perfectly with these design principles but it can always be improved.
We were chilling, you know, cooking sern handler v3, sern gui, npm create @sern/bot and serncord when we thought about changing the logo to a sleek design with less colors.
And here we are!
Ropox!
Bro's the GOAT. This website is maintained by him, the domain costs are funded by him and also he started brainstorming how the logo would be on paper:
And there it all clicked:
seren tried by the way!

Pretty nice!
By the way, we have animations and variations on the way, so make sure to stay updated in the discord server!
Service API (recommended to use this over useContainer hooks)Initializable type when making your Dependencies type!class DatabaseService implements Init {
//some hypothetical database
_pgsql : database()
async init() {
await _pgsql.load()
}
}
await makeDependencies({
build: root => root.add({
db: new DatabaseService() //will be init'ed automatically
})
})
modulesLoaded , which allows users to customize behavior after all modules are loaded!
export default eventModule({
name: 'modulesLoaded',
type: EventType.Sern,
execute: () => {
console.log('All modules loaded')
}
})
faster module loading
better error handling
Less boilerplate
class modules devex got upgraded and work better than before
automatically ignore any files not ending in (mts, cts, mjs, cjs, ts, js)
! prefix on filename or directory (ie: !filename.ts or !directory will be ignored by sern)Service API (recommended to use this over useContainer hooks)
Less boilerplate
new methods on ModuleManager
automatically ignore any files not ending in (mts, cts, mjs, cjs, ts, js)
! prefix on filename or directory (ie: !filename.ts or !directory)new SernEmitter event modulesLoaded , which allows users to customize behavior after all modules are loaded!
Init Hooks
file optionSern.init('file');
- Sern.makeDependencies({ build: () => {} })
+ await makeDependencies({ build: () => {} })
+0.3.9 Join our discord!
Wow! We're finally increasing our semantic versioning by +1.7.9.
What does this mean?
You can now use plugins for event modules. Previous version would throw an error if the
plugins field was populated.
export function commandPlTest() : SernEmitterPlugin {
return {
type: PluginType.Command,
execute: ({ mod}, controller) => {
if(mod.name === 'module.register') {
console.log('Event Module created correctly')
return controller.next()
}
console.log('event name is wrong')
return controller.stop()
}
}
}
Applying this plugin to some eventModule:
export default eventModule({
name: 'error',
type: EventType.Sern,
plugins: [commandPlTest()],
execute(m) {
console.log(m)
},
})
The powerful npm package iti decouples sern even more.
Decoupling data structures with the Inversion of Control pattern separates data from logic, which will help speed production
and make sern even more customizable than before.
//With typescript, you can customize / augment your typings.
interface MyDependencies extends Dependencies {
'@sern/client' : Singleton<Client>;
'@sern/logger' : Singleton<DefaultLogging>
}
export const useContainer = Sern.makeDependencies<MyDependencies>({
// exclude: new Set(['@sern/logger']), don't autofill optional dependencies
build: root => root
.add({ '@sern/client': single(client) })
.add({ '@sern/logger': single(new DefaultLogging()) })
});
Sern.init({
defaultPrefix: '!', // removing defaultPrefix will shut down text commands
commands: 'src/commands',
// events: 'src/events' (optional),
containerConfig : {
get: useContainer //pass in your dependency getter here
}
});
Using the Sern#makeDependencies function, inject your dependencies.
We'll use specific dependencies that are created with the @sern/keyword
key.
Using typescript to display all keywords and what they represent:
export interface Dependencies {
'@sern/client': Singleton<EventEmitter>; //Discord Client
'@sern/logger'?: Singleton<Logging>; //Logger
'@sern/emitter': Singleton<SernEmitter>; //SernEmitter
'@sern/store' : Singleton<ModuleStore>; //Stores all Command Modules
'@sern/modules' : Singleton<ModuleManager>; //Manages Modules
'@sern/errors': Singleton<ErrorHandling>; //A Lifetime / Crash Handler
}
Sern#addExternal has been deprecated and removed in favor of Sern#makeDependencies
At the moment, one optional dependency, @sern/logger, exists. If not added explicitly,
we'll autofill with a DefaultLogger.
If you don't want a logger, add it to the
exclude field while composing dependencies.
Use your generated dependency getter useContainer (use whatever name you want), access them
from anywhere.
the function useContainerRaw is provided for direct access to dependencies and the internal data structure. Use this wisely as no guarantees are made and crashes can happen.
The build field follows createContainer function call.
2.0 includes all the new role select menus. CommandType.MenuSelect has been renamed into
CommandType.StringSelect. The remaining SelectMenus are
CommandType.RoleSelect, CommandType.ChannelSelect, CommandType.UserSelect, CommandType.MentionableSelect
export default commandModule({
type: CommandType.RoleSelect,
execute(ctx) {
ctx.reply('role select')
},
})
In addition, commandModules with ContextMenus have been renamed.
- CommandType.MenuUser, CommandType.MenuMsg
+ CommandType.CtxUser, CommandType.CtxMsg
Pre 2.0:


CommandPlugin<T> and EventPlugin<T> typings have also been static'ified, transformed from types to interfaces

- type Module = EventModule | CommandModule
+ type AnyModule = EventModule | CommandModule
-export type SpreadParams<T extends (...args: never) => unknown> = (
- args: Parameters<T>[number],
- ) => unknown;
Override type has been removed due to redundancy
- discord.js : 14.5
+ discord.js : 14.7
-typescript: 4.7
+ typescript: 4.9
+ interface Wrapper {
+ readonly defaultPrefix?: string;
+ readonly commands: string;
+ readonly events?: string;
+ readonly containerConfig : {
+ get: (...keys: (keyof Dependencies)[]) => unknown[];
+ }
+}
- interface Wrapper {
- readonly client: Client;
- readonly sernEmitter?: SernEmitter;
- readonly defaultPrefix?: string;
- readonly commands: string;
- readonly events?:
- | string
- | { mod: EventModule; absPath: string }[]
- | (() => { mod: EventModule; absPath: string }[]);
-}
+ DefaultLogger
+ DefaultModuleManager
+ SernEmitter
+ DefaultErrorHandling
+ type Singleton<T> = () => T
+ type Transient<T> = () => () => T;
+ type LogPayload<T = unknown> = { message: T }
+ export const single = <T>() => T
+ export const many = <T>() => () => T
Including the previous section, some names to symbols and data structures were altered to be better represented. view changelog
The context data structure has been internally altered to represent its dynamics better.
]]>I'm Sr Izan, your fellow user and contributor.
Today I'm going to show you how to get started with sern and all its cool features.
Install the CLI:
npm i -g @sern/cli
and then run
sern init
You can also run sern init -y if you want to use the default options.
The CLI is written in Typescript and open-sourced on Github. (thanks evo!)
Normally you'd need a way to store secrets, and the best way to do that is by installing another package: dotenv
just npm i dotenv in the project folder and add require('dotenv').config() to your import section. Then, when you login, process.env.TOKEN (or however you have it named on your .env file) should do the trick.
If you're using ESM, configure dotenv with import 'dotenv/config' instead of require('dotenv').config().
Yes, that's it. Here's a little FAQ to get you started. You can also join the Discord for any problems.
Q: How do I publish a slash command?
A: Install the publish extension. Little video:
Q: Any snippet VSCode extension?
A: Yeah, just search sern Snippets made by a verified publisher called Sr Izan (haha yeah me funny!)
Q: HEEEELLLPPPP!!!!
A: Hey, don't panic! We're here to help so, join the Discord. We're trying to get to 100 members!
Today we're announcing the ability to create class based modules! To get started, install
npm install @sern/handler@latest
Quick List of changes!
Incorporate class based modules into your project instead of the traditional commandModule or eventModule
Extend the new CommandExecutable or EventExecutable
import { CommandType, CommandExecutable, type Args, type Context } from '@sern/handler';
import { publish } from '../plugins/publish.js';
import { serendipityOnly } from '../plugins/serendipityOnly.js';
export default class extends CommandExecutable<CommandType.Both> {
type = CommandType.Both as const;
description = 'What is the meaning of life?'
override onEvent = [
serendipityOnly()
];
override plugins = [
publish(),
];
execute = async (ctx: Context, args: Args) => {
await ctx.reply('42')
};
}
execute must not be a method of the class. It should be as above, a property on the class!
import { CommandType, EventExecutable, type EventType } from '@sern/handler';
import type { GuildMember } from 'discord.js'
export default class extends EventExecutable<EventType.Discord> {
type = EventType.Discord as const;
execute = (member: GuildMember) => {
console.log(member)
};
}
Now, you might ask why this feature was added.
Simply put, to give flexibility to the developers.
I believe that you should build your own structures however you might like and customize to your liking.
In addition, decorators now unofficially work with modules!
Feel free to use TypeScript experimental decorators to augment and customize your classes.
The next update will bring sern v2 with some important features. Here are some things to watch out for.