@seedcord/plugins 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mongo/decorators/DatabaseModel.ts","../src/mongo/decorators/DatabaseService.ts","../src/mongo/BaseService.ts","../src/mongo/Mongo.ts","../src/mongo/decorators/DBCatchable.ts"],"names":["ModelMetadataKey","Symbol","DatabaseModel","collection","target","propertyKey","schema","name","String","model","mongoose","Reflect","defineMetadata","ServiceMetadataKey","DatabaseService","key","ctor","BaseService","db","core","getMetadata","Error","_register","Mongo","Plugin","logger","Logger","isInitialised","uri","services","options","shutdown","addTask","ShutdownPhase","ExternalResources","stop","init","connect","loadServices","disconnect","dbName","Envapter","isProduction","tls","ssl","then","i","info","chalk","bold","magenta","connection","catch","err","red","error","message","servicesDir","dir","traverseDirectory","_full","rel","mod","Service","Object","values","isServiceClass","instance","italic","yellow","gray","green","keys","length","obj","prototype","hasMetadata","DBCatchable","errorMessage","_target","_propertyKey","descriptor","originalMethod","value","args","apply","CustomError","throwCustomError","DatabaseError"],"mappings":"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","file":"index.cjs","sourcesContent":["import mongoose from 'mongoose';\n\nimport type { ServiceKeys } from '../types/Services';\n\nexport const ModelMetadataKey = Symbol('db:model');\n\n/**\n * Associates a Mongoose model with a database service\n *\n * Creates a Mongoose model from the decorated static schema property and stores it\n * for service registration. The model becomes available as `this.model` in the service.\n * Must be applied to a `public static schema` property in the service class.\n *\n * @param collection - Collection name for the Mongoose model\n * @decorator\n * @example\n * ```typescript\n * \\@DatabaseService('users')\n * export class Users extends BaseService<IUser> {\n * \\@DatabaseModel('users')\n * public static schema = new mongoose.Schema<IUser>({\n * username: { type: String, required: true, unique: true }\n * });\n * }\n * ```\n */\nexport function DatabaseModel<TService extends ServiceKeys>(collection: TService) {\n return <\n SchemaObj extends Record<KeyOfSchema, mongoose.Schema>,\n KeyOfSchema extends keyof SchemaObj & (string | symbol)\n >(\n target: SchemaObj,\n propertyKey: KeyOfSchema\n ): void => {\n const schema = target[propertyKey];\n const name = String(collection);\n const model = mongoose.model(name, schema);\n Reflect.defineMetadata(ModelMetadataKey, model, target);\n };\n}\n","import type { BaseService } from '../BaseService';\nimport type { ServiceKeys } from '../types/Services';\nimport type { ConstructorFunction } from '@seedcord/types';\n\nexport const ServiceMetadataKey = Symbol('db:serviceKey');\n\n/**\n * Registers a database service with a typed key\n *\n * Associates a service class with a key for dependency injection.\n * The service becomes available via `core.db.services[key]`.\n *\n * @param key - Service key for registration and type-safe access\n * @decorator\n * @example\n * ```typescript\n * \\@DatabaseService('users')\n * export class Users<Doc extends IUser = IUser> extends BaseService<Doc> {\n * // Some code\n * }\n * ```\n */\nexport function DatabaseService<TService extends ServiceKeys>(key: TService) {\n return <DatabaseCtor extends ConstructorFunction & { prototype: BaseService }>(ctor: DatabaseCtor): void => {\n Reflect.defineMetadata(ServiceMetadataKey, key, ctor);\n };\n}\n","import { ModelMetadataKey } from './decorators/DatabaseModel';\nimport { ServiceMetadataKey } from './decorators/DatabaseService';\n\nimport type { Mongo } from './Mongo';\nimport type { Services } from './types/Services';\nimport type { IDocument, TypedConstructor } from '@seedcord/types';\nimport type mongoose from 'mongoose';\nimport type { Core } from 'seedcord';\n\n/**\n * Base class for MongoDB service layers\n *\n * Provides typed access to MongoDB collections through Mongoose models.\n * Services are automatically registered with the Mongo plugin when instantiated.\n *\n * @typeParam Doc - The document type this service manages\n * @example\n * ```typescript\n * \\@DatabaseService('users')\n * export class Users extends BaseService<IUser> {\n * \\@DatabaseModel('users')\n * public static schema = new mongoose.Schema<IUser>({\n * username: { type: String, required: true, unique: true }\n * });\n *\n * // Custom methods here\n * public async findByUsername(username: string) {\n * return this.model.findOne({ username });\n * }\n * }\n * ```\n */\nexport abstract class BaseService<Doc extends IDocument = IDocument> {\n public readonly model: mongoose.Model<Doc>;\n\n public constructor(\n protected readonly db: Mongo,\n protected readonly core: Core\n ) {\n const ctor = this.constructor;\n\n const key = Reflect.getMetadata(ServiceMetadataKey, ctor) as string | undefined;\n if (!key) throw new Error(`Missing @DatabaseService on ${ctor.name}`);\n\n const model = Reflect.getMetadata(ModelMetadataKey, ctor) as mongoose.Model<Doc> | undefined;\n if (!model) throw new Error(`Missing @DatabaseModel on ${ctor.name}`);\n\n this.model = model;\n\n db._register(key as keyof Services, this as unknown as Services[keyof Services]);\n }\n}\n\n/** Constructor type for BaseService classes */\nexport type BaseServiceConstructor = TypedConstructor<typeof BaseService>;\n","import 'reflect-metadata';\n\nimport chalk from 'chalk';\nimport { Envapter } from 'envapt';\nimport mongoose from 'mongoose';\nimport { Logger, Plugin, ShutdownPhase, traverseDirectory } from 'seedcord';\n\nimport { BaseService } from './BaseService';\nimport { ServiceMetadataKey } from './decorators/DatabaseService';\n\nimport type { BaseServiceConstructor } from './BaseService';\nimport type { Services } from './types/Services';\nimport type { Core } from 'seedcord';\n\n/**\n * Configuration options for MongoDB connection and service loading.\n */\ninterface MongoOptions {\n /** Directory path containing database service classes */\n dir: string;\n /** MongoDB connection URI */\n uri: string;\n /** Database name to use */\n name: string;\n}\n\n/**\n * MongoDB integration plugin for Seedcord.\n *\n * Manages MongoDB connections, service loading, and provides type-safe\n * access to database services through service registration decorators.\n */\nexport class Mongo extends Plugin {\n public readonly logger = new Logger('MongoDB');\n private isInitialised = false;\n private readonly uri: string;\n\n /**\n * Map of all loaded services.\n * Keys come from `@DatabaseService('key')`\n */\n public readonly services: Services = {} as Services;\n\n constructor(\n public readonly core: Core,\n private readonly options: MongoOptions\n ) {\n super(core);\n this.uri = options.uri;\n\n this.core.shutdown.addTask(ShutdownPhase.ExternalResources, 'stop-database', async () => await this.stop());\n }\n\n public async init(): Promise<void> {\n if (this.isInitialised) return;\n this.isInitialised = true;\n\n await this.connect();\n await this.loadServices();\n }\n\n public async stop(): Promise<void> {\n await this.disconnect();\n }\n\n private async connect(): Promise<void> {\n await mongoose\n .connect(this.uri, {\n dbName: this.options.name,\n ...(Envapter.isProduction && { tls: true, ssl: true })\n })\n .then((i) => this.logger.info(`Connected to MongoDB: ${chalk.bold.magenta(i.connection.name)}`))\n .catch((err) => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n throw new Error(`Could not connect to MongoDB`, err);\n });\n }\n\n private async disconnect(): Promise<void> {\n await mongoose\n .disconnect()\n .then(() => this.logger.info(chalk.red.bold('Disconnected from MongoDB')))\n .catch((err) => this.logger.error(`Could not disconnect from MongoDB: ${(err as Error).message}`));\n }\n\n private async loadServices(): Promise<void> {\n const servicesDir = this.options.dir;\n this.logger.info(chalk.bold(servicesDir));\n\n await traverseDirectory(\n servicesDir,\n (_full, rel, mod) => {\n for (const Service of Object.values(mod)) {\n if (this.isServiceClass(Service)) {\n const instance = new Service(this, this.core);\n this.logger.info(\n `${chalk.italic('Registered')} ${chalk.bold.yellow(instance.constructor.name)} from ${chalk.gray(rel)}`\n );\n }\n }\n },\n this.logger\n );\n\n this.logger.info(`${chalk.bold.green('Loaded')}: ${chalk.magenta(Object.keys(this.services).length)} services`);\n }\n\n private isServiceClass(obj: unknown): obj is BaseServiceConstructor {\n return (\n typeof obj === 'function' && obj.prototype instanceof BaseService && Reflect.hasMetadata(ServiceMetadataKey, obj)\n );\n }\n\n _register<SKey extends keyof Services>(key: SKey, instance: Services[SKey]): void {\n this.services[key] = instance;\n }\n}\n","import { CustomError, throwCustomError, DatabaseError } from 'seedcord';\n\n/**\n * Catches and wraps database operation errors.\n *\n * Automatically wraps non-CustomError exceptions in DatabaseError instances\n * with UUID tracking. Should be applied to database service methods.\n *\n * @param errorMessage - Message to include when wrapping errors\n * @decorator\n * @example\n * ```typescript\n * class UserService extends BaseService {\n * \\@DBCatchable('Failed to find user')\n * async findById(id: string) {\n * return this.model.findById(id);\n * }\n * }\n * ```\n */\nexport function DBCatchable<TypeReturn>(errorMessage: string) {\n return function (\n _target: unknown,\n _propertyKey: string,\n descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<TypeReturn>>\n ): void {\n const originalMethod = descriptor.value;\n\n descriptor.value = async function (...args: any[]): Promise<TypeReturn> {\n if (!originalMethod) {\n throw new Error('Method not found');\n }\n\n try {\n return await originalMethod.apply(this, args);\n } catch (error) {\n if (!(error instanceof CustomError)) {\n throwCustomError(error, errorMessage, DatabaseError);\n } else {\n throw error;\n }\n }\n };\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/mongo/decorators/DatabaseModel.ts","../src/mongo/decorators/DatabaseService.ts","../src/mongo/BaseService.ts","../src/mongo/Mongo.ts","../src/mongo/decorators/DBCatchable.ts"],"names":["ModelMetadataKey","Symbol","DatabaseModel","collection","target","propertyKey","schema","name","String","model","mongoose","Reflect","defineMetadata","ServiceMetadataKey","DatabaseService","key","ctor","BaseService","db","core","getMetadata","Error","_register","Mongo","Plugin","logger","Logger","isInitialised","uri","services","options","shutdown","addTask","ShutdownPhase","ExternalResources","stop","init","connect","loadServices","disconnect","dbName","Envapter","isProduction","tls","ssl","then","i","info","chalk","bold","magenta","connection","catch","err","red","error","message","servicesDir","dir","traverseDirectory","_full","rel","mod","Service","Object","values","isServiceClass","instance","italic","yellow","gray","green","keys","length","obj","prototype","hasMetadata","DBCatchable","errorMessage","_target","_propertyKey","descriptor","originalMethod","value","args","apply","CustomError","throwCustomError","DatabaseError"],"mappings":"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","file":"index.cjs","sourcesContent":["import mongoose from 'mongoose';\n\nimport type { ServiceKeys } from '../types/Services';\n\nexport const ModelMetadataKey = Symbol('db:model');\n\n/**\n * Associates a Mongoose model with a database service\n *\n * Creates a Mongoose model from the decorated static schema property and stores it\n * for service registration. The model becomes available as `this.model` in the service.\n * Must be applied to a `public static schema` property in the service class.\n *\n * @param collection - Collection name for the Mongoose model\n * @decorator\n * @example\n * ```typescript\n * \\@DatabaseService('users')\n * export class Users extends BaseService<IUser> {\n * \\@DatabaseModel('users')\n * public static schema = new mongoose.Schema<IUser>({\n * username: { type: String, required: true, unique: true }\n * });\n * }\n * ```\n */\nexport function DatabaseModel<TService extends ServiceKeys>(collection: TService) {\n return <\n SchemaObj extends Record<KeyOfSchema, mongoose.Schema>,\n KeyOfSchema extends keyof SchemaObj & (string | symbol)\n >(\n target: SchemaObj,\n propertyKey: KeyOfSchema\n ): void => {\n const schema = target[propertyKey];\n const name = String(collection);\n const model = mongoose.model(name, schema);\n Reflect.defineMetadata(ModelMetadataKey, model, target);\n };\n}\n","import type { BaseService } from '../BaseService';\nimport type { ServiceKeys } from '../types/Services';\nimport type { ConstructorFunction } from '@seedcord/types';\n\nexport const ServiceMetadataKey = Symbol('db:serviceKey');\n\n/**\n * Registers a database service with a typed key\n *\n * Associates a service class with a key for dependency injection.\n * The service becomes available via `core.db.services[key]`.\n *\n * @param key - Service key for registration and type-safe access\n * @decorator\n * @example\n * ```typescript\n * \\@DatabaseService('users')\n * export class Users<Doc extends IUser = IUser> extends BaseService<Doc> {\n * // Some code\n * }\n * ```\n */\nexport function DatabaseService<TService extends ServiceKeys>(key: TService) {\n return <DatabaseCtor extends ConstructorFunction & { prototype: BaseService }>(ctor: DatabaseCtor): void => {\n Reflect.defineMetadata(ServiceMetadataKey, key, ctor);\n };\n}\n","import { ModelMetadataKey } from './decorators/DatabaseModel';\nimport { ServiceMetadataKey } from './decorators/DatabaseService';\n\nimport type { Mongo } from './Mongo';\nimport type { IDocument } from './types/Document';\nimport type { Services } from './types/Services';\nimport type { TypedConstructor } from '@seedcord/types';\nimport type mongoose from 'mongoose';\nimport type { Core } from 'seedcord';\n\n/**\n * Base class for MongoDB service layers\n *\n * Provides typed access to MongoDB collections through Mongoose models.\n * Services are automatically registered with the Mongo plugin when instantiated.\n *\n * @typeParam Doc - The document type this service manages\n * @example\n * ```typescript\n * \\@DatabaseService('users')\n * export class Users extends BaseService<IUser> {\n * \\@DatabaseModel('users')\n * public static schema = new mongoose.Schema<IUser>({\n * username: { type: String, required: true, unique: true }\n * });\n *\n * // Custom methods here\n * public async findByUsername(username: string) {\n * return this.model.findOne({ username });\n * }\n * }\n * ```\n */\nexport abstract class BaseService<Doc extends IDocument = IDocument> {\n public readonly model: mongoose.Model<Doc>;\n\n public constructor(\n protected readonly db: Mongo,\n protected readonly core: Core\n ) {\n const ctor = this.constructor;\n\n const key = Reflect.getMetadata(ServiceMetadataKey, ctor) as string | undefined;\n if (!key) throw new Error(`Missing @DatabaseService on ${ctor.name}`);\n\n const model = Reflect.getMetadata(ModelMetadataKey, ctor) as mongoose.Model<Doc> | undefined;\n if (!model) throw new Error(`Missing @DatabaseModel on ${ctor.name}`);\n\n this.model = model;\n\n db._register(key as keyof Services, this as unknown as Services[keyof Services]);\n }\n}\n\n/** Constructor type for BaseService classes */\nexport type BaseServiceConstructor = TypedConstructor<typeof BaseService>;\n","import 'reflect-metadata';\n\nimport chalk from 'chalk';\nimport { Envapter } from 'envapt';\nimport mongoose from 'mongoose';\nimport { Logger, Plugin, ShutdownPhase, traverseDirectory } from 'seedcord';\n\nimport { BaseService } from './BaseService';\nimport { ServiceMetadataKey } from './decorators/DatabaseService';\n\nimport type { BaseServiceConstructor } from './BaseService';\nimport type { Services } from './types/Services';\nimport type { Core } from 'seedcord';\n\n/**\n * Configuration options for MongoDB connection and service loading.\n */\ninterface MongoOptions {\n /** Directory path containing database service classes */\n dir: string;\n /** MongoDB connection URI */\n uri: string;\n /** Database name to use */\n name: string;\n}\n\n/**\n * MongoDB integration plugin for Seedcord.\n *\n * Manages MongoDB connections, service loading, and provides type-safe\n * access to database services through service registration decorators.\n */\nexport class Mongo extends Plugin {\n public readonly logger = new Logger('MongoDB');\n private isInitialised = false;\n private readonly uri: string;\n\n /**\n * Map of all loaded services.\n * Keys come from `@DatabaseService('key')`\n */\n public readonly services: Services = {} as Services;\n\n constructor(\n public readonly core: Core,\n private readonly options: MongoOptions\n ) {\n super(core);\n this.uri = options.uri;\n\n this.core.shutdown.addTask(ShutdownPhase.ExternalResources, 'stop-database', async () => await this.stop());\n }\n\n public async init(): Promise<void> {\n if (this.isInitialised) return;\n this.isInitialised = true;\n\n await this.connect();\n await this.loadServices();\n }\n\n public async stop(): Promise<void> {\n await this.disconnect();\n }\n\n private async connect(): Promise<void> {\n await mongoose\n .connect(this.uri, {\n dbName: this.options.name,\n ...(Envapter.isProduction && { tls: true, ssl: true })\n })\n .then((i) => this.logger.info(`Connected to MongoDB: ${chalk.bold.magenta(i.connection.name)}`))\n .catch((err) => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n throw new Error(`Could not connect to MongoDB`, err);\n });\n }\n\n private async disconnect(): Promise<void> {\n await mongoose\n .disconnect()\n .then(() => this.logger.info(chalk.red.bold('Disconnected from MongoDB')))\n .catch((err) => this.logger.error(`Could not disconnect from MongoDB: ${(err as Error).message}`));\n }\n\n private async loadServices(): Promise<void> {\n const servicesDir = this.options.dir;\n this.logger.info(chalk.bold(servicesDir));\n\n await traverseDirectory(\n servicesDir,\n (_full, rel, mod) => {\n for (const Service of Object.values(mod)) {\n if (this.isServiceClass(Service)) {\n const instance = new Service(this, this.core);\n this.logger.info(\n `${chalk.italic('Registered')} ${chalk.bold.yellow(instance.constructor.name)} from ${chalk.gray(rel)}`\n );\n }\n }\n },\n this.logger\n );\n\n this.logger.info(`${chalk.bold.green('Loaded')}: ${chalk.magenta(Object.keys(this.services).length)} services`);\n }\n\n private isServiceClass(obj: unknown): obj is BaseServiceConstructor {\n return (\n typeof obj === 'function' && obj.prototype instanceof BaseService && Reflect.hasMetadata(ServiceMetadataKey, obj)\n );\n }\n\n _register<SKey extends keyof Services>(key: SKey, instance: Services[SKey]): void {\n this.services[key] = instance;\n }\n}\n","import { CustomError, throwCustomError, DatabaseError } from 'seedcord';\n\n/**\n * Catches and wraps database operation errors.\n *\n * Automatically wraps non-CustomError exceptions in DatabaseError instances\n * with UUID tracking. Should be applied to database service methods.\n *\n * @param errorMessage - Message to include when wrapping errors\n * @decorator\n * @example\n * ```typescript\n * class UserService extends BaseService {\n * \\@DBCatchable('Failed to find user')\n * async findById(id: string) {\n * return this.model.findById(id);\n * }\n * }\n * ```\n */\nexport function DBCatchable<TypeReturn>(errorMessage: string) {\n return function (\n _target: unknown,\n _propertyKey: string,\n descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<TypeReturn>>\n ): void {\n const originalMethod = descriptor.value;\n\n descriptor.value = async function (...args: any[]): Promise<TypeReturn> {\n if (!originalMethod) {\n throw new Error('Method not found');\n }\n\n try {\n return await originalMethod.apply(this, args);\n } catch (error) {\n if (!(error instanceof CustomError)) {\n throwCustomError(error, errorMessage, DatabaseError);\n } else {\n throw error;\n }\n }\n };\n };\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Plugin, Core, Logger } from 'seedcord';
2
- import { IDocument, TypedConstructor, ConstructorFunction } from '@seedcord/types';
2
+ import { TypedConstructor, ConstructorFunction } from '@seedcord/types';
3
3
  import mongoose from 'mongoose';
4
4
 
5
5
  /**
@@ -25,6 +25,23 @@ interface Services {
25
25
  */
26
26
  type ServiceKeys = keyof Services;
27
27
 
28
+ /**
29
+ * Basic document interface with MongoDB ObjectId field.
30
+ *
31
+ * Represents the minimal structure of a MongoDB document
32
+ * with the required `_id` field.
33
+ */
34
+ interface IDocument {
35
+ /** MongoDB document identifier */
36
+ _id: string;
37
+ }
38
+ /**
39
+ * Helper type to extract the type of a document that extends IDocument.
40
+ *
41
+ * @typeParam Doc - The document type extending IDocument
42
+ */
43
+ type TypeOfIDocument<Doc extends IDocument = IDocument> = Doc;
44
+
28
45
  /**
29
46
  * Configuration options for MongoDB connection and service loading.
30
47
  */
@@ -159,4 +176,4 @@ declare function DatabaseService<TService extends ServiceKeys>(key: TService): <
159
176
  prototype: BaseService;
160
177
  }>(ctor: DatabaseCtor) => void;
161
178
 
162
- export { BaseService, type BaseServiceConstructor, DBCatchable, DatabaseModel, DatabaseService, ModelMetadataKey, Mongo, type ServiceKeys, ServiceMetadataKey, type Services };
179
+ export { BaseService, type BaseServiceConstructor, DBCatchable, DatabaseModel, DatabaseService, type IDocument, ModelMetadataKey, Mongo, type ServiceKeys, ServiceMetadataKey, type Services, type TypeOfIDocument };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Plugin, Core, Logger } from 'seedcord';
2
- import { IDocument, TypedConstructor, ConstructorFunction } from '@seedcord/types';
2
+ import { TypedConstructor, ConstructorFunction } from '@seedcord/types';
3
3
  import mongoose from 'mongoose';
4
4
 
5
5
  /**
@@ -25,6 +25,23 @@ interface Services {
25
25
  */
26
26
  type ServiceKeys = keyof Services;
27
27
 
28
+ /**
29
+ * Basic document interface with MongoDB ObjectId field.
30
+ *
31
+ * Represents the minimal structure of a MongoDB document
32
+ * with the required `_id` field.
33
+ */
34
+ interface IDocument {
35
+ /** MongoDB document identifier */
36
+ _id: string;
37
+ }
38
+ /**
39
+ * Helper type to extract the type of a document that extends IDocument.
40
+ *
41
+ * @typeParam Doc - The document type extending IDocument
42
+ */
43
+ type TypeOfIDocument<Doc extends IDocument = IDocument> = Doc;
44
+
28
45
  /**
29
46
  * Configuration options for MongoDB connection and service loading.
30
47
  */
@@ -159,4 +176,4 @@ declare function DatabaseService<TService extends ServiceKeys>(key: TService): <
159
176
  prototype: BaseService;
160
177
  }>(ctor: DatabaseCtor) => void;
161
178
 
162
- export { BaseService, type BaseServiceConstructor, DBCatchable, DatabaseModel, DatabaseService, ModelMetadataKey, Mongo, type ServiceKeys, ServiceMetadataKey, type Services };
179
+ export { BaseService, type BaseServiceConstructor, DBCatchable, DatabaseModel, DatabaseService, type IDocument, ModelMetadataKey, Mongo, type ServiceKeys, ServiceMetadataKey, type Services, type TypeOfIDocument };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Plugin, Core, Logger } from 'seedcord';
2
- import { IDocument, TypedConstructor, ConstructorFunction } from '@seedcord/types';
2
+ import { TypedConstructor, ConstructorFunction } from '@seedcord/types';
3
3
  import mongoose from 'mongoose';
4
4
 
5
5
  /**
@@ -25,6 +25,23 @@ interface Services {
25
25
  */
26
26
  type ServiceKeys = keyof Services;
27
27
 
28
+ /**
29
+ * Basic document interface with MongoDB ObjectId field.
30
+ *
31
+ * Represents the minimal structure of a MongoDB document
32
+ * with the required `_id` field.
33
+ */
34
+ interface IDocument {
35
+ /** MongoDB document identifier */
36
+ _id: string;
37
+ }
38
+ /**
39
+ * Helper type to extract the type of a document that extends IDocument.
40
+ *
41
+ * @typeParam Doc - The document type extending IDocument
42
+ */
43
+ type TypeOfIDocument<Doc extends IDocument = IDocument> = Doc;
44
+
28
45
  /**
29
46
  * Configuration options for MongoDB connection and service loading.
30
47
  */
@@ -159,4 +176,4 @@ declare function DatabaseService<TService extends ServiceKeys>(key: TService): <
159
176
  prototype: BaseService;
160
177
  }>(ctor: DatabaseCtor) => void;
161
178
 
162
- export { BaseService, type BaseServiceConstructor, DBCatchable, DatabaseModel, DatabaseService, ModelMetadataKey, Mongo, type ServiceKeys, ServiceMetadataKey, type Services };
179
+ export { BaseService, type BaseServiceConstructor, DBCatchable, DatabaseModel, DatabaseService, type IDocument, ModelMetadataKey, Mongo, type ServiceKeys, ServiceMetadataKey, type Services, type TypeOfIDocument };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mongo/decorators/DatabaseModel.ts","../src/mongo/decorators/DatabaseService.ts","../src/mongo/BaseService.ts","../src/mongo/Mongo.ts","../src/mongo/decorators/DBCatchable.ts"],"names":["ModelMetadataKey","Symbol","DatabaseModel","collection","target","propertyKey","schema","name","String","model","mongoose","Reflect","defineMetadata","ServiceMetadataKey","DatabaseService","key","ctor","BaseService","db","core","getMetadata","Error","_register","Mongo","Plugin","logger","Logger","isInitialised","uri","services","options","shutdown","addTask","ShutdownPhase","ExternalResources","stop","init","connect","loadServices","disconnect","dbName","Envapter","isProduction","tls","ssl","then","i","info","chalk","bold","magenta","connection","catch","err","red","error","message","servicesDir","dir","traverseDirectory","_full","rel","mod","Service","Object","values","isServiceClass","instance","italic","yellow","gray","green","keys","length","obj","prototype","hasMetadata","DBCatchable","errorMessage","_target","_propertyKey","descriptor","originalMethod","value","args","apply","CustomError","throwCustomError","DatabaseError"],"mappings":"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","file":"index.mjs","sourcesContent":["import mongoose from 'mongoose';\n\nimport type { ServiceKeys } from '../types/Services';\n\nexport const ModelMetadataKey = Symbol('db:model');\n\n/**\n * Associates a Mongoose model with a database service\n *\n * Creates a Mongoose model from the decorated static schema property and stores it\n * for service registration. The model becomes available as `this.model` in the service.\n * Must be applied to a `public static schema` property in the service class.\n *\n * @param collection - Collection name for the Mongoose model\n * @decorator\n * @example\n * ```typescript\n * \\@DatabaseService('users')\n * export class Users extends BaseService<IUser> {\n * \\@DatabaseModel('users')\n * public static schema = new mongoose.Schema<IUser>({\n * username: { type: String, required: true, unique: true }\n * });\n * }\n * ```\n */\nexport function DatabaseModel<TService extends ServiceKeys>(collection: TService) {\n return <\n SchemaObj extends Record<KeyOfSchema, mongoose.Schema>,\n KeyOfSchema extends keyof SchemaObj & (string | symbol)\n >(\n target: SchemaObj,\n propertyKey: KeyOfSchema\n ): void => {\n const schema = target[propertyKey];\n const name = String(collection);\n const model = mongoose.model(name, schema);\n Reflect.defineMetadata(ModelMetadataKey, model, target);\n };\n}\n","import type { BaseService } from '../BaseService';\nimport type { ServiceKeys } from '../types/Services';\nimport type { ConstructorFunction } from '@seedcord/types';\n\nexport const ServiceMetadataKey = Symbol('db:serviceKey');\n\n/**\n * Registers a database service with a typed key\n *\n * Associates a service class with a key for dependency injection.\n * The service becomes available via `core.db.services[key]`.\n *\n * @param key - Service key for registration and type-safe access\n * @decorator\n * @example\n * ```typescript\n * \\@DatabaseService('users')\n * export class Users<Doc extends IUser = IUser> extends BaseService<Doc> {\n * // Some code\n * }\n * ```\n */\nexport function DatabaseService<TService extends ServiceKeys>(key: TService) {\n return <DatabaseCtor extends ConstructorFunction & { prototype: BaseService }>(ctor: DatabaseCtor): void => {\n Reflect.defineMetadata(ServiceMetadataKey, key, ctor);\n };\n}\n","import { ModelMetadataKey } from './decorators/DatabaseModel';\nimport { ServiceMetadataKey } from './decorators/DatabaseService';\n\nimport type { Mongo } from './Mongo';\nimport type { Services } from './types/Services';\nimport type { IDocument, TypedConstructor } from '@seedcord/types';\nimport type mongoose from 'mongoose';\nimport type { Core } from 'seedcord';\n\n/**\n * Base class for MongoDB service layers\n *\n * Provides typed access to MongoDB collections through Mongoose models.\n * Services are automatically registered with the Mongo plugin when instantiated.\n *\n * @typeParam Doc - The document type this service manages\n * @example\n * ```typescript\n * \\@DatabaseService('users')\n * export class Users extends BaseService<IUser> {\n * \\@DatabaseModel('users')\n * public static schema = new mongoose.Schema<IUser>({\n * username: { type: String, required: true, unique: true }\n * });\n *\n * // Custom methods here\n * public async findByUsername(username: string) {\n * return this.model.findOne({ username });\n * }\n * }\n * ```\n */\nexport abstract class BaseService<Doc extends IDocument = IDocument> {\n public readonly model: mongoose.Model<Doc>;\n\n public constructor(\n protected readonly db: Mongo,\n protected readonly core: Core\n ) {\n const ctor = this.constructor;\n\n const key = Reflect.getMetadata(ServiceMetadataKey, ctor) as string | undefined;\n if (!key) throw new Error(`Missing @DatabaseService on ${ctor.name}`);\n\n const model = Reflect.getMetadata(ModelMetadataKey, ctor) as mongoose.Model<Doc> | undefined;\n if (!model) throw new Error(`Missing @DatabaseModel on ${ctor.name}`);\n\n this.model = model;\n\n db._register(key as keyof Services, this as unknown as Services[keyof Services]);\n }\n}\n\n/** Constructor type for BaseService classes */\nexport type BaseServiceConstructor = TypedConstructor<typeof BaseService>;\n","import 'reflect-metadata';\n\nimport chalk from 'chalk';\nimport { Envapter } from 'envapt';\nimport mongoose from 'mongoose';\nimport { Logger, Plugin, ShutdownPhase, traverseDirectory } from 'seedcord';\n\nimport { BaseService } from './BaseService';\nimport { ServiceMetadataKey } from './decorators/DatabaseService';\n\nimport type { BaseServiceConstructor } from './BaseService';\nimport type { Services } from './types/Services';\nimport type { Core } from 'seedcord';\n\n/**\n * Configuration options for MongoDB connection and service loading.\n */\ninterface MongoOptions {\n /** Directory path containing database service classes */\n dir: string;\n /** MongoDB connection URI */\n uri: string;\n /** Database name to use */\n name: string;\n}\n\n/**\n * MongoDB integration plugin for Seedcord.\n *\n * Manages MongoDB connections, service loading, and provides type-safe\n * access to database services through service registration decorators.\n */\nexport class Mongo extends Plugin {\n public readonly logger = new Logger('MongoDB');\n private isInitialised = false;\n private readonly uri: string;\n\n /**\n * Map of all loaded services.\n * Keys come from `@DatabaseService('key')`\n */\n public readonly services: Services = {} as Services;\n\n constructor(\n public readonly core: Core,\n private readonly options: MongoOptions\n ) {\n super(core);\n this.uri = options.uri;\n\n this.core.shutdown.addTask(ShutdownPhase.ExternalResources, 'stop-database', async () => await this.stop());\n }\n\n public async init(): Promise<void> {\n if (this.isInitialised) return;\n this.isInitialised = true;\n\n await this.connect();\n await this.loadServices();\n }\n\n public async stop(): Promise<void> {\n await this.disconnect();\n }\n\n private async connect(): Promise<void> {\n await mongoose\n .connect(this.uri, {\n dbName: this.options.name,\n ...(Envapter.isProduction && { tls: true, ssl: true })\n })\n .then((i) => this.logger.info(`Connected to MongoDB: ${chalk.bold.magenta(i.connection.name)}`))\n .catch((err) => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n throw new Error(`Could not connect to MongoDB`, err);\n });\n }\n\n private async disconnect(): Promise<void> {\n await mongoose\n .disconnect()\n .then(() => this.logger.info(chalk.red.bold('Disconnected from MongoDB')))\n .catch((err) => this.logger.error(`Could not disconnect from MongoDB: ${(err as Error).message}`));\n }\n\n private async loadServices(): Promise<void> {\n const servicesDir = this.options.dir;\n this.logger.info(chalk.bold(servicesDir));\n\n await traverseDirectory(\n servicesDir,\n (_full, rel, mod) => {\n for (const Service of Object.values(mod)) {\n if (this.isServiceClass(Service)) {\n const instance = new Service(this, this.core);\n this.logger.info(\n `${chalk.italic('Registered')} ${chalk.bold.yellow(instance.constructor.name)} from ${chalk.gray(rel)}`\n );\n }\n }\n },\n this.logger\n );\n\n this.logger.info(`${chalk.bold.green('Loaded')}: ${chalk.magenta(Object.keys(this.services).length)} services`);\n }\n\n private isServiceClass(obj: unknown): obj is BaseServiceConstructor {\n return (\n typeof obj === 'function' && obj.prototype instanceof BaseService && Reflect.hasMetadata(ServiceMetadataKey, obj)\n );\n }\n\n _register<SKey extends keyof Services>(key: SKey, instance: Services[SKey]): void {\n this.services[key] = instance;\n }\n}\n","import { CustomError, throwCustomError, DatabaseError } from 'seedcord';\n\n/**\n * Catches and wraps database operation errors.\n *\n * Automatically wraps non-CustomError exceptions in DatabaseError instances\n * with UUID tracking. Should be applied to database service methods.\n *\n * @param errorMessage - Message to include when wrapping errors\n * @decorator\n * @example\n * ```typescript\n * class UserService extends BaseService {\n * \\@DBCatchable('Failed to find user')\n * async findById(id: string) {\n * return this.model.findById(id);\n * }\n * }\n * ```\n */\nexport function DBCatchable<TypeReturn>(errorMessage: string) {\n return function (\n _target: unknown,\n _propertyKey: string,\n descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<TypeReturn>>\n ): void {\n const originalMethod = descriptor.value;\n\n descriptor.value = async function (...args: any[]): Promise<TypeReturn> {\n if (!originalMethod) {\n throw new Error('Method not found');\n }\n\n try {\n return await originalMethod.apply(this, args);\n } catch (error) {\n if (!(error instanceof CustomError)) {\n throwCustomError(error, errorMessage, DatabaseError);\n } else {\n throw error;\n }\n }\n };\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/mongo/decorators/DatabaseModel.ts","../src/mongo/decorators/DatabaseService.ts","../src/mongo/BaseService.ts","../src/mongo/Mongo.ts","../src/mongo/decorators/DBCatchable.ts"],"names":["ModelMetadataKey","Symbol","DatabaseModel","collection","target","propertyKey","schema","name","String","model","mongoose","Reflect","defineMetadata","ServiceMetadataKey","DatabaseService","key","ctor","BaseService","db","core","getMetadata","Error","_register","Mongo","Plugin","logger","Logger","isInitialised","uri","services","options","shutdown","addTask","ShutdownPhase","ExternalResources","stop","init","connect","loadServices","disconnect","dbName","Envapter","isProduction","tls","ssl","then","i","info","chalk","bold","magenta","connection","catch","err","red","error","message","servicesDir","dir","traverseDirectory","_full","rel","mod","Service","Object","values","isServiceClass","instance","italic","yellow","gray","green","keys","length","obj","prototype","hasMetadata","DBCatchable","errorMessage","_target","_propertyKey","descriptor","originalMethod","value","args","apply","CustomError","throwCustomError","DatabaseError"],"mappings":"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","file":"index.mjs","sourcesContent":["import mongoose from 'mongoose';\n\nimport type { ServiceKeys } from '../types/Services';\n\nexport const ModelMetadataKey = Symbol('db:model');\n\n/**\n * Associates a Mongoose model with a database service\n *\n * Creates a Mongoose model from the decorated static schema property and stores it\n * for service registration. The model becomes available as `this.model` in the service.\n * Must be applied to a `public static schema` property in the service class.\n *\n * @param collection - Collection name for the Mongoose model\n * @decorator\n * @example\n * ```typescript\n * \\@DatabaseService('users')\n * export class Users extends BaseService<IUser> {\n * \\@DatabaseModel('users')\n * public static schema = new mongoose.Schema<IUser>({\n * username: { type: String, required: true, unique: true }\n * });\n * }\n * ```\n */\nexport function DatabaseModel<TService extends ServiceKeys>(collection: TService) {\n return <\n SchemaObj extends Record<KeyOfSchema, mongoose.Schema>,\n KeyOfSchema extends keyof SchemaObj & (string | symbol)\n >(\n target: SchemaObj,\n propertyKey: KeyOfSchema\n ): void => {\n const schema = target[propertyKey];\n const name = String(collection);\n const model = mongoose.model(name, schema);\n Reflect.defineMetadata(ModelMetadataKey, model, target);\n };\n}\n","import type { BaseService } from '../BaseService';\nimport type { ServiceKeys } from '../types/Services';\nimport type { ConstructorFunction } from '@seedcord/types';\n\nexport const ServiceMetadataKey = Symbol('db:serviceKey');\n\n/**\n * Registers a database service with a typed key\n *\n * Associates a service class with a key for dependency injection.\n * The service becomes available via `core.db.services[key]`.\n *\n * @param key - Service key for registration and type-safe access\n * @decorator\n * @example\n * ```typescript\n * \\@DatabaseService('users')\n * export class Users<Doc extends IUser = IUser> extends BaseService<Doc> {\n * // Some code\n * }\n * ```\n */\nexport function DatabaseService<TService extends ServiceKeys>(key: TService) {\n return <DatabaseCtor extends ConstructorFunction & { prototype: BaseService }>(ctor: DatabaseCtor): void => {\n Reflect.defineMetadata(ServiceMetadataKey, key, ctor);\n };\n}\n","import { ModelMetadataKey } from './decorators/DatabaseModel';\nimport { ServiceMetadataKey } from './decorators/DatabaseService';\n\nimport type { Mongo } from './Mongo';\nimport type { IDocument } from './types/Document';\nimport type { Services } from './types/Services';\nimport type { TypedConstructor } from '@seedcord/types';\nimport type mongoose from 'mongoose';\nimport type { Core } from 'seedcord';\n\n/**\n * Base class for MongoDB service layers\n *\n * Provides typed access to MongoDB collections through Mongoose models.\n * Services are automatically registered with the Mongo plugin when instantiated.\n *\n * @typeParam Doc - The document type this service manages\n * @example\n * ```typescript\n * \\@DatabaseService('users')\n * export class Users extends BaseService<IUser> {\n * \\@DatabaseModel('users')\n * public static schema = new mongoose.Schema<IUser>({\n * username: { type: String, required: true, unique: true }\n * });\n *\n * // Custom methods here\n * public async findByUsername(username: string) {\n * return this.model.findOne({ username });\n * }\n * }\n * ```\n */\nexport abstract class BaseService<Doc extends IDocument = IDocument> {\n public readonly model: mongoose.Model<Doc>;\n\n public constructor(\n protected readonly db: Mongo,\n protected readonly core: Core\n ) {\n const ctor = this.constructor;\n\n const key = Reflect.getMetadata(ServiceMetadataKey, ctor) as string | undefined;\n if (!key) throw new Error(`Missing @DatabaseService on ${ctor.name}`);\n\n const model = Reflect.getMetadata(ModelMetadataKey, ctor) as mongoose.Model<Doc> | undefined;\n if (!model) throw new Error(`Missing @DatabaseModel on ${ctor.name}`);\n\n this.model = model;\n\n db._register(key as keyof Services, this as unknown as Services[keyof Services]);\n }\n}\n\n/** Constructor type for BaseService classes */\nexport type BaseServiceConstructor = TypedConstructor<typeof BaseService>;\n","import 'reflect-metadata';\n\nimport chalk from 'chalk';\nimport { Envapter } from 'envapt';\nimport mongoose from 'mongoose';\nimport { Logger, Plugin, ShutdownPhase, traverseDirectory } from 'seedcord';\n\nimport { BaseService } from './BaseService';\nimport { ServiceMetadataKey } from './decorators/DatabaseService';\n\nimport type { BaseServiceConstructor } from './BaseService';\nimport type { Services } from './types/Services';\nimport type { Core } from 'seedcord';\n\n/**\n * Configuration options for MongoDB connection and service loading.\n */\ninterface MongoOptions {\n /** Directory path containing database service classes */\n dir: string;\n /** MongoDB connection URI */\n uri: string;\n /** Database name to use */\n name: string;\n}\n\n/**\n * MongoDB integration plugin for Seedcord.\n *\n * Manages MongoDB connections, service loading, and provides type-safe\n * access to database services through service registration decorators.\n */\nexport class Mongo extends Plugin {\n public readonly logger = new Logger('MongoDB');\n private isInitialised = false;\n private readonly uri: string;\n\n /**\n * Map of all loaded services.\n * Keys come from `@DatabaseService('key')`\n */\n public readonly services: Services = {} as Services;\n\n constructor(\n public readonly core: Core,\n private readonly options: MongoOptions\n ) {\n super(core);\n this.uri = options.uri;\n\n this.core.shutdown.addTask(ShutdownPhase.ExternalResources, 'stop-database', async () => await this.stop());\n }\n\n public async init(): Promise<void> {\n if (this.isInitialised) return;\n this.isInitialised = true;\n\n await this.connect();\n await this.loadServices();\n }\n\n public async stop(): Promise<void> {\n await this.disconnect();\n }\n\n private async connect(): Promise<void> {\n await mongoose\n .connect(this.uri, {\n dbName: this.options.name,\n ...(Envapter.isProduction && { tls: true, ssl: true })\n })\n .then((i) => this.logger.info(`Connected to MongoDB: ${chalk.bold.magenta(i.connection.name)}`))\n .catch((err) => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n throw new Error(`Could not connect to MongoDB`, err);\n });\n }\n\n private async disconnect(): Promise<void> {\n await mongoose\n .disconnect()\n .then(() => this.logger.info(chalk.red.bold('Disconnected from MongoDB')))\n .catch((err) => this.logger.error(`Could not disconnect from MongoDB: ${(err as Error).message}`));\n }\n\n private async loadServices(): Promise<void> {\n const servicesDir = this.options.dir;\n this.logger.info(chalk.bold(servicesDir));\n\n await traverseDirectory(\n servicesDir,\n (_full, rel, mod) => {\n for (const Service of Object.values(mod)) {\n if (this.isServiceClass(Service)) {\n const instance = new Service(this, this.core);\n this.logger.info(\n `${chalk.italic('Registered')} ${chalk.bold.yellow(instance.constructor.name)} from ${chalk.gray(rel)}`\n );\n }\n }\n },\n this.logger\n );\n\n this.logger.info(`${chalk.bold.green('Loaded')}: ${chalk.magenta(Object.keys(this.services).length)} services`);\n }\n\n private isServiceClass(obj: unknown): obj is BaseServiceConstructor {\n return (\n typeof obj === 'function' && obj.prototype instanceof BaseService && Reflect.hasMetadata(ServiceMetadataKey, obj)\n );\n }\n\n _register<SKey extends keyof Services>(key: SKey, instance: Services[SKey]): void {\n this.services[key] = instance;\n }\n}\n","import { CustomError, throwCustomError, DatabaseError } from 'seedcord';\n\n/**\n * Catches and wraps database operation errors.\n *\n * Automatically wraps non-CustomError exceptions in DatabaseError instances\n * with UUID tracking. Should be applied to database service methods.\n *\n * @param errorMessage - Message to include when wrapping errors\n * @decorator\n * @example\n * ```typescript\n * class UserService extends BaseService {\n * \\@DBCatchable('Failed to find user')\n * async findById(id: string) {\n * return this.model.findById(id);\n * }\n * }\n * ```\n */\nexport function DBCatchable<TypeReturn>(errorMessage: string) {\n return function (\n _target: unknown,\n _propertyKey: string,\n descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<TypeReturn>>\n ): void {\n const originalMethod = descriptor.value;\n\n descriptor.value = async function (...args: any[]): Promise<TypeReturn> {\n if (!originalMethod) {\n throw new Error('Method not found');\n }\n\n try {\n return await originalMethod.apply(this, args);\n } catch (error) {\n if (!(error instanceof CustomError)) {\n throwCustomError(error, errorMessage, DatabaseError);\n } else {\n throw error;\n }\n }\n };\n };\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@seedcord/plugins",
3
3
  "type": "module",
4
- "version": "0.2.0",
4
+ "version": "0.2.1",
5
5
  "description": "Official plugins for Seedcord Discord bot framework",
6
6
  "repository": {
7
7
  "type": "git",
@@ -34,13 +34,13 @@
34
34
  "mongoose": "8.18.0",
35
35
  "envapt": "3.0.0",
36
36
  "reflect-metadata": "0.2.2",
37
- "seedcord": "^0.2.0",
38
- "@seedcord/types": "^0.1.2"
37
+ "@seedcord/types": "^0.1.3",
38
+ "seedcord": "^0.2.1"
39
39
  },
40
40
  "devDependencies": {
41
+ "@seedcord/tsconfig": "^1.0.2",
41
42
  "@seedcord/eslint-config": "^1.1.1",
42
- "@seedcord/tsup-config": "^1.0.2",
43
- "@seedcord/tsconfig": "^1.0.2"
43
+ "@seedcord/tsup-config": "^1.0.2"
44
44
  },
45
45
  "scripts": {
46
46
  "build": "tsup && cp dist/index.d.cts dist/index.d.mts",