Skip to main content

4 posts tagged with "release"

View All Tags

· 3 min read

3.0 Release

Join our discord


Dependency Management

  • Service API (recommended to use this over useContainer hooks)
    • Dependencies type must be globally augmented in order for Services api to function properly
  • new methods on ModuleManager
    • getPublishableCommands()
  • Init Hooks
    • implement starting behavior for dependencies
    • To enforce and type check this, use the Initializable type when making your Dependencies type!
  • Emitter interface
    • More generic interface to define any event emitter
    • You can now swap out the SernEmitter with whatever emitter now.
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

  • new SernEmitter event 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')

Quality of Life

  • faster module loading

    • I utilize async generators for reading files now. A lot faster than the first iteration.
  • better error handling

  • Less boilerplate

    • Services api cleans up v2 boilerplate
  • class modules devex got upgraded and work better than before

  • automatically ignore any files not ending in (mts, cts, mjs, cjs, ts, js)

    • ignore commands and events with ! prefix on filename or directory (ie: !filename.ts or !directory will be ignored by sern)
  • Service API (recommended to use this over useContainer hooks)

    • Dependencies type must be globally augmented in order for Services api to function properly
  • Less boilerplate

  • new methods on ModuleManager

  • automatically ignore any files not ending in (mts, cts, mjs, cjs, ts, js)

    • ignore commands / events with ! 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

    • implement starting behavior for dependencies


  • Experimental things may be subject to removal, need feedback and are not guaranteed stable
  • dev / prod mode
    • sern will behave differently depending on mode set
  • init sern from file option
    • reads from local sern.config.json

Breaking changes

  • Sern.makeDependencies -> makeDependencies
    • it is asynchronous and top level function now. Make sure to await it before initing for proper synchronization.
  • module store and manager internally changed, so those using them may recieve breaking changes
  • BaseOptions type removed
- Sern.makeDependencies({ build: () => {} })
+ await makeDependencies({ build: () => {} })


  • Removed all previous marked deprecations in v3
  • ModuleStore will be removed as a dependency in v4. The only way to access modules should be through ModuleManager
  • Default Dependencies will be made internal in the v4. Users should only have access to its interface / contract

· One min read

2.5 Release

Join our discord

  • Thank you for using sern in your projects. It's only going to get better!
  • I apologize for the sudden small breaking change. After this update, there won't be any for a while. Wow! We're increasing our semantic versioning by +0.3.9
    What does this mean?

Breaking changes



  • The naming of plugins was getting too complex. For example, plugin naming evolved into CommandModuleCommandPlugin, CommandModuleEventPlugin and more.
  • I realize that this affects all plugins. I have updated all plugins to match these changes
  • The old way of declaring plugins has been deprecated in favor of newer functions that facilitate and ease typings

· 4 min read

2.0 Release

Join our discord!
Wow! We're finally increasing our semantic versioning by +1.7.9.
What does this mean?

Plugin Support for Event Modules

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( === 'module.register') {
console.log('Event Module created correctly')
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) {

Dependency Injection and Decoupling

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.

How do I start?

//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()) })

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.

New CommandTypes

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

Typings Simplification

Pre 2.0:

Post 2.0:

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

Breaking Changes

All deprecation warnings from previous versions have taken effect, and are removed in 2.0.
- 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

Context refactoring

The context data structure has been internally altered to represent its dynamics better.

· 2 min read

Class-based modules

Today we're announcing the ability to create class based modules! To get started, install

npm install @sern/handler@latest

Quick List of changes!

Class based modules

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 = [
override plugins = [
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) => {

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.

Deprecation Warnings

The next update will bring sern v2 with some important features. Here are some things to watch out for.

Dependencies Update

  • TypeScript has been updated to 4.8.3
  • Discord.js has been upgraded to 14.5