Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: cleanup #348

Merged
merged 25 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/npm-publish-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Continuous Delivery

on:
push:
branches:
- main
paths:
- 'src/**'
- 'package.json'

jobs:
Publish:
name: Publishing Dev
runs-on: ubuntu-latest

steps:
- name: Check out Git repository
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3

- name: Set up Node.js
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3
with:
node-version: 18
registry-url: 'https://registry.npmjs.org'

- name: Install Node.js dependencies
run: npm i && npm run build:dev

- name: Publish to npm
run: |
npm version premajor --preid "dev.$(git rev-parse --verify --short HEAD)" --git-tag-version=false
npm publish --tag dev
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
"@typescript-eslint/eslint-plugin": "5.58.0",
"@typescript-eslint/parser": "5.59.1",
"discord.js": "^14.11.0",
"esbuild": "^0.17.0",
"eslint": "8.39.0",
"prettier": "2.8.8",
"tsup": "^6.7.0",
Expand Down Expand Up @@ -95,5 +94,8 @@
"type": "git",
"url": "git+https://github.com/sern-handler/handler.git"
},
"engines": {
"node": ">= 18.16.x"
},
"homepage": "https://sern.dev"
}
2 changes: 1 addition & 1 deletion src/core/_internal.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
export * as Id from './id';
export * from './operators';
export * from './predicates';
export * as Files from './module-loading';
export * from './functions';
export type { VoidResult } from '../types/core-plugin';
export { SernError } from './structures/enums';
export { ModuleStore } from './structures/module-store';
export * as DefaultServices from './structures/services';
export { useContainerRaw } from './ioc/base'

9 changes: 0 additions & 9 deletions src/core/contracts/disposable.ts

This file was deleted.

2 changes: 2 additions & 0 deletions src/core/contracts/emitter.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//i deleted it, hmm so how should we allow users to enable localization?
// a
import type { AnyFunction } from '../../types/utility';

export interface Emitter {
Expand Down
2 changes: 0 additions & 2 deletions src/core/contracts/error-handling.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { CommandModule,Processed, EventModule } from "../../types/core-modules";

/**
* @since 2.0.0
*/
Expand Down
16 changes: 16 additions & 0 deletions src/core/contracts/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

/**
* Represents an initialization contract.
* Let dependencies implement this to initiate some logic.
*/
export interface Init {
init(): unknown;
}

/**
* Represents a Disposable contract.
* Let dependencies implement this to dispose and cleanup.
*/
export interface Disposable {
dispose(): unknown;
}
3 changes: 1 addition & 2 deletions src/core/contracts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ export * from './error-handling';
export * from './logging';
export * from './module-manager';
export * from './module-store';
export * from './init';
export * from './hooks';
export * from './emitter';
export * from './disposable'
9 changes: 0 additions & 9 deletions src/core/contracts/init.ts

This file was deleted.

46 changes: 43 additions & 3 deletions src/core/functions.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import { Err, Ok } from 'ts-results-es';
import { ApplicationCommandOptionType, AutocompleteInteraction } from 'discord.js';
import type { SernAutocompleteData, SernOptionsData } from '../types/core-modules';
import type { Module, SernAutocompleteData, SernOptionsData } from '../types/core-modules';
import type { AnyCommandPlugin, AnyEventPlugin, Plugin } from '../types/core-plugin';
import { PluginType } from './structures';
import type {
AnySelectMenuInteraction,
ButtonInteraction,
ChatInputCommandInteraction,
MessageContextMenuCommandInteraction,
ModalSubmitInteraction,
UserContextMenuCommandInteraction,
AutocompleteInteraction
} from 'discord.js';
import { ApplicationCommandOptionType, InteractionType } from 'discord.js'
import { PayloadType, PluginType } from './structures';
import assert from 'assert';
import { Payload } from '../types/utility';

//function wrappers for empty ok / err
export const ok = /* @__PURE__*/ () => Ok.EMPTY;
Expand Down Expand Up @@ -81,3 +91,33 @@ export function treeSearch(
}
}
}


interface InteractionTypable {
type: InteractionType;
}
//discord.js pls fix ur typings or i will >:(
type AnyMessageComponentInteraction = AnySelectMenuInteraction | ButtonInteraction;
type AnyCommandInteraction =
| ChatInputCommandInteraction
| MessageContextMenuCommandInteraction
| UserContextMenuCommandInteraction;

export function isMessageComponent(i: InteractionTypable): i is AnyMessageComponentInteraction {
return i.type === InteractionType.MessageComponent;
}
export function isCommand(i: InteractionTypable): i is AnyCommandInteraction {
return i.type === InteractionType.ApplicationCommand;
}
export function isAutocomplete(i: InteractionTypable): i is AutocompleteInteraction {
return i.type === InteractionType.ApplicationCommandAutocomplete;
}

export function isModal(i: InteractionTypable): i is ModalSubmitInteraction {
return i.type === InteractionType.ModalSubmit;
}

export function resultPayload<T extends PayloadType>
(type: T, module?: Module, reason?: unknown) {
return { type, module, reason } as Payload & { type : T };
}
63 changes: 45 additions & 18 deletions src/core/ioc/base.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import * as assert from 'assert';
import { composeRoot, useContainer } from './dependency-injection';
import type { DependencyConfiguration } from '../../types/ioc';
import { useContainer } from './dependency-injection';
import type { CoreDependencies, DependencyConfiguration } from '../../types/ioc';
import { CoreContainer } from './container';
import { Result } from 'ts-results-es'
import { Result } from 'ts-results-es';
import { DefaultServices } from '../_internal';
import { AnyFunction } from '../../types/utility';
import type { Logging } from '../contracts/logging';

//SIDE EFFECT: GLOBAL DI
let containerSubject: CoreContainer<Partial<Dependencies>>;

Expand All @@ -29,19 +30,18 @@ export function disposeAll(logger: Logging|undefined) {
.then(() => logger?.info({ message: 'Cleaning container and crashing' }));
}

const dependencyBuilder = (container: any, excluded: string[]) => {
const dependencyBuilder = (container: any, excluded: string[] ) => {
type Insertable =
| ((container: CoreContainer<Dependencies>) => unknown )
| Record<PropertyKey, unknown>
| object
return {
/**
* Insert a dependency into your container.
* Supply the correct key and dependency
*/
add(key: keyof Dependencies, v: Insertable) {
Result
.wrap(() => container.add({ [key]: v}))
.expect("Failed to add " + key);
Result.wrap(() => container.add({ [key]: v}))
.expect("Failed to add " + key);
},
/**
* Exclude any dependencies from being added.
Expand All @@ -50,15 +50,15 @@ const dependencyBuilder = (container: any, excluded: string[]) => {
exclude(...keys: (keyof Dependencies)[]) {
keys.forEach(key => excluded.push(key));
},

/**
* @param key the key of the dependency
* @param v The dependency to swap out.
* Swap out a preexisting dependency.
*/
swap(key: keyof Dependencies, v: Insertable) {
Result
.wrap(() => container.upsert({ [key]: v }))
.expect("Failed to update " + key);
Result.wrap(() => container.upsert({ [key]: v }))
.expect("Failed to update " + key);
},
/**
* @param key the key of the dependency
Expand All @@ -70,9 +70,8 @@ const dependencyBuilder = (container: any, excluded: string[]) => {
* Swap out a preexisting dependency.
*/
addDisposer(key: keyof Dependencies, cleanup: AnyFunction) {
Result
.wrap(() => container.addDisposer({ [key] : cleanup }))
.expect("Failed to addDisposer for" + key);
Result.wrap(() => container.addDisposer({ [key] : cleanup }))
.expect("Failed to addDisposer for" + key);
}
};
};
Expand All @@ -87,15 +86,45 @@ export const insertLogger = (containerSubject: CoreContainer<any>) => {
containerSubject
.upsert({'@sern/logger': () => new DefaultServices.DefaultLogging});
}


/**
* Given the user's conf, check for any excluded/included dependency keys.
* Then, call conf.build to get the rest of the users' dependencies.
* Finally, update the containerSubject with the new container state
* @param conf
*/
function composeRoot(
container: CoreContainer<Partial<Dependencies>>,
conf: DependencyConfiguration,
) {
//container should have no client or logger yet.
const hasLogger = conf.exclude?.has('@sern/logger');
if (!hasLogger) {
insertLogger(container);
}
//Build the container based on the callback provided by the user
conf.build(container as CoreContainer<Omit<CoreDependencies, '@sern/client'>>);

if (!hasLogger) {
container.get('@sern/logger')?.info({ message: 'All dependencies loaded successfully.' });
}

container.ready();
}

export async function makeDependencies<const T extends Dependencies>
(conf: ValidDependencyConfig) {
containerSubject = new CoreContainer();
if(typeof conf === 'function') {
const excluded: string[] = [];
conf(dependencyBuilder(containerSubject, excluded));

const includeLogger =
!excluded.includes('@sern/logger')
&& !containerSubject.getTokens()['@sern/logger'];

if(!excluded.includes('@sern/logger')
&& !containerSubject.getTokens()['@sern/logger']) {
if(includeLogger) {
insertLogger(containerSubject);
}

Expand All @@ -107,5 +136,3 @@ export async function makeDependencies<const T extends Dependencies>
return useContainer<T>();
}



26 changes: 17 additions & 9 deletions src/core/ioc/container.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Container } from 'iti';
import { Disposable, SernEmitter } from '../';
import { Disposable } from '../';
import * as assert from 'node:assert';
import { Subject } from 'rxjs';
import { DefaultServices, ModuleStore } from '../_internal';
import * as Hooks from './hooks'
import * as Hooks from './hooks';
import { EventEmitter } from 'node:events';


/**
Expand All @@ -17,12 +18,13 @@ export class CoreContainer<T extends Partial<Dependencies>> extends Container<T,
assert.ok(!this.isReady(), 'Listening for dispose & init should occur prior to sern being ready.');

const { unsubscribe } = Hooks.createInitListener(this);

this.ready$
.subscribe({ complete: unsubscribe });

(this as Container<{}, {}>)
.add({ '@sern/errors': () => new DefaultServices.DefaultErrorHandling(),
'@sern/emitter': () => new SernEmitter,
.add({ '@sern/errors': () => new DefaultServices.DefaultErrorHandling,
'@sern/emitter': () => new EventEmitter({ captureRejections: true }),
'@sern/store': () => new ModuleStore })
.add(ctx => {
return { '@sern/modules': () =>
Expand All @@ -33,19 +35,25 @@ export class CoreContainer<T extends Partial<Dependencies>> extends Container<T,
isReady() {
return this.ready$.closed;
}

hasKey(key: string): boolean {
return Boolean((this as Container<any,any>)._context[key]);
}

override async disposeAll() {

const otherDisposables = Object
.entries(this._context)
.flatMap(([key, value]) =>
'dispose' in value ? [key] : []);

for(const key of otherDisposables) {
otherDisposables.forEach(key => {
//possible source of bug: dispose is a property.
this.addDisposer({ [key]: (dep: Disposable) => dep.dispose() } as never);
}
await super.disposeAll()
})
await super.disposeAll();
}



ready() {
this.ready$.complete();
this.ready$.unsubscribe();
Expand Down
Loading
Loading