diff --git a/src/core/ioc/base.ts b/src/core/ioc/base.ts index aaad603..1f67926 100644 --- a/src/core/ioc/base.ts +++ b/src/core/ioc/base.ts @@ -1,6 +1,6 @@ import * as assert from "assert"; import { composeRoot, useContainer } from "./dependency-injection"; -import { DependencyConfiguration, Dependencies } from "./types"; +import { DependencyConfiguration } from "./types"; import { CoreContainer } from "../structures/container"; @@ -24,16 +24,13 @@ export function useContainerRaw() { * @param conf a configuration for creating your project dependencies */ export async function makeDependencies( - conf: DependencyConfiguration, + conf: DependencyConfiguration, ) { //Until there are more optional dependencies, just check if the logger exists //SIDE EFFECT containerSubject = new CoreContainer() - await composeRoot(conf); + await composeRoot(containerSubject, conf); - //SIDE EFFECT - containerSubject.ready(); - return useContainer(); } diff --git a/src/core/ioc/dependency-injection.ts b/src/core/ioc/dependency-injection.ts index 783e0ce..dde7a66 100644 --- a/src/core/ioc/dependency-injection.ts +++ b/src/core/ioc/dependency-injection.ts @@ -1,4 +1,4 @@ -import type { CoreDependencies, Dependencies, DependencyConfiguration, MapDeps, IntoDependencies } from './types'; +import type { DependencyConfiguration, MapDeps, IntoDependencies } from './types'; import { DefaultLogging } from '../structures'; import { SernError } from '../structures/errors'; import { useContainerRaw } from './base'; @@ -24,8 +24,7 @@ export function single(cb: () => T) { export function transient(cb: () => () => T) { return cb; } - -export function Service(key: string): unknown +export function Service(key: string) : unknown export function Service(key: T) { return useContainerRaw().get(key)! } @@ -41,31 +40,35 @@ export function Services(...keys: [...T] * Finally, update the containerSubject with the new container state * @param conf */ -export async function composeRoot(conf: DependencyConfiguration) { +export async function composeRoot( + container: CoreContainer>, + conf: DependencyConfiguration +) { //container should have no client or logger yet. - const excludeLogger = conf.exclude?.has('@sern/logger'); - const container = useContainerRaw(); - if (!excludeLogger) { + const hasLogger = conf.exclude?.has('@sern/logger'); + if (!hasLogger) { container.upsert({ '@sern/logger': () => new DefaultLogging(), }); } //Build the container based on the callback provided by the user - const updatedContainer = await conf.build(container as CoreContainer); + conf.build(container as CoreContainer); try { - updatedContainer.get('@sern/client'); + container.get('@sern/client'); } catch { throw new Error(SernError.MissingRequired + " No client was provided") } - if (!excludeLogger) { - updatedContainer.get('@sern/logger')?.info({ message: 'All dependencies loaded successfully.' }); + if (!hasLogger) { + container.get('@sern/logger')?.info({ message: 'All dependencies loaded successfully.' }); } + + container.ready(); } export function useContainer() { console.warn(` - Warning: using a container hook is not recommended. + Warning: using a container hook (useContainer) is not recommended. Could lead to many unwanted side effects. Use the new Service(s) api function instead. ` diff --git a/src/core/ioc/ioc.d.ts b/src/core/ioc/ioc.d.ts new file mode 100644 index 0000000..166bc5e --- /dev/null +++ b/src/core/ioc/ioc.d.ts @@ -0,0 +1,16 @@ + +type Singleton = () => T; +type Transient = () => () => T; + +interface CoreDependencies { + '@sern/logger'?: Singleton; + '@sern/emitter': Singleton; + '@sern/store': Singleton; + '@sern/modules': Singleton; + '@sern/errors': Singleton; +} + +interface Dependencies extends CoreDependencies { + '@sern/client': Singleton; +} + diff --git a/src/core/ioc/types.ts b/src/core/ioc/types.ts index bd8f810..3f58295 100644 --- a/src/core/ioc/types.ts +++ b/src/core/ioc/types.ts @@ -1,35 +1,22 @@ import { Container, UnpackFunction } from "iti"; -import { Awaitable, ModuleStore } from "../../shared"; -import { ErrorHandling, Logging, ModuleManager } from "../contracts"; -import { SernEmitter } from "../"; -import EventEmitter from "node:events"; export type Singleton = () => T; export type Transient = () => () => T; -export interface CoreDependencies { - '@sern/logger'?: Singleton; - '@sern/emitter': Singleton; - '@sern/store': Singleton; - '@sern/modules': Singleton; - '@sern/errors': Singleton; -} -export interface Dependencies extends CoreDependencies { - '@sern/client': Singleton; -} export type DependencyFromKey = Dependencies[T]; + export type IntoDependencies = { [Index in keyof Tuple]: UnpackFunction&{}>; //Unpack and make NonNullable } & { length: Tuple['length'] }; -export interface DependencyConfiguration { +export interface DependencyConfiguration { //@deprecated. Loggers will always be included in the future exclude?: Set<'@sern/logger'>; - build: (root: Container) => Awaitable>; + build: (root: Container) => Container; } //To be removed in future diff --git a/src/handler/id.ts b/src/handler/id.ts new file mode 100644 index 0000000..2ba752d --- /dev/null +++ b/src/handler/id.ts @@ -0,0 +1,46 @@ +import { Interaction, InteractionType } from "discord.js"; +import { CommandType, EventType } from "../core"; + +/** + * Creates a unique ID for a given interaction object. + * @param event The interaction object for which to create an ID. + * @returns A unique string ID based on the type and properties of the interaction object. + */ +export function createId(event: T) { + switch (event.type) { + case InteractionType.MessageComponent: { + return `${event.customId}_C${event.componentType}`; + } + case InteractionType.ApplicationCommand: + case InteractionType.ApplicationCommandAutocomplete: { + return `${event.commandName}_A${event.commandType}`; + } + case InteractionType.ModalSubmit: { + return `${event.customId}_C1`; + } + } +} + +const appBitField = 0b000000011111; +/* + * Generates a number based on CommandType. + * This corresponds to an ApplicationCommandType or ComponentType + * TextCommands are 0 as they aren't either or. + */ +function apiType(t: CommandType|EventType) { + if (t === CommandType.Both || t === CommandType.Modal) return 1; + const log = Math.log2(t); + return (appBitField & t) !== 0 ? log : log - 2; +} + +/* + * Generates an id based on CommandType. + * A is for any ApplicationCommand. C is for any ComponentCommand + * Then, another number generated by apiType function is appended + */ +export function uniqueId(t: CommandType|EventType) { + const am = (appBitField & t) !== 0 ? 'A' : 'C'; + return am + apiType(t); +} + +