@monque/tsed 0.1.0

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.
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @monque/tsed - Type Guards
3
+ *
4
+ * Utilities for duck-typing Mongoose and MongoDB related objects
5
+ * to avoid hard dependencies on @tsed/mongoose.
6
+ */
7
+
8
+ import type { Db } from 'mongodb';
9
+
10
+ /**
11
+ * Interface representing a Mongoose Connection object.
12
+ * We only care that it has a `db` property which is a MongoDB Db instance.
13
+ */
14
+ export interface MongooseConnection {
15
+ db: Db;
16
+ }
17
+
18
+ /**
19
+ * Interface representing the @tsed/mongoose MongooseService.
20
+ * It acts as a registry/factory for connections.
21
+ */
22
+ export interface MongooseService {
23
+ /**
24
+ * Get a connection by its ID (configuration key).
25
+ * @param id The connection ID (default: "default")
26
+ */
27
+ get(id?: string): MongooseConnection | undefined;
28
+ }
29
+
30
+ /**
31
+ * Type guard to check if an object acts like a Mongoose Service.
32
+ *
33
+ * Checks if the object has a `get` method.
34
+ *
35
+ * @param value The value to check
36
+ */
37
+ export function isMongooseService(value: unknown): value is MongooseService {
38
+ return (
39
+ typeof value === 'object' &&
40
+ value !== null &&
41
+ 'get' in value &&
42
+ typeof (value as MongooseService).get === 'function'
43
+ );
44
+ }
45
+
46
+ /**
47
+ * Type guard to check if an object acts like a Mongoose Connection.
48
+ *
49
+ * Checks if the object has a `db` property.
50
+ *
51
+ * @param value The value to check
52
+ */
53
+ export function isMongooseConnection(value: unknown): value is MongooseConnection {
54
+ return (
55
+ typeof value === 'object' &&
56
+ value !== null &&
57
+ 'db' in value &&
58
+ typeof (value as MongooseConnection).db === 'object' &&
59
+ (value as MongooseConnection).db !== null &&
60
+ typeof (value as MongooseConnection).db.collection === 'function'
61
+ );
62
+ }
@@ -0,0 +1,13 @@
1
+ export { buildJobName } from './build-job-name.js';
2
+ export {
3
+ type CollectedWorkerMetadata,
4
+ collectWorkerMetadata,
5
+ } from './collect-worker-metadata.js';
6
+ export { getWorkerToken } from './get-worker-token.js';
7
+ export {
8
+ isMongooseConnection,
9
+ isMongooseService,
10
+ type MongooseConnection,
11
+ type MongooseService,
12
+ } from './guards.js';
13
+ export { type InjectorFn, resolveDatabase } from './resolve-database.js';
@@ -0,0 +1,119 @@
1
+ /**
2
+ * @monque/tsed - Database Resolution Utility
3
+ *
4
+ * Multi-strategy database resolution for flexible MongoDB connection handling.
5
+ */
6
+
7
+ import type { TokenProvider } from '@tsed/di';
8
+ import type { Db } from 'mongodb';
9
+
10
+ import type { MonqueTsedConfig } from '@/config';
11
+
12
+ import { isMongooseConnection, isMongooseService } from './guards.js';
13
+
14
+ /**
15
+ * Type for the injector function used to resolve DI tokens.
16
+ */
17
+ export type InjectorFn = <T>(token: TokenProvider<T>) => T | undefined;
18
+
19
+ /**
20
+ * Resolve the MongoDB database instance from the configuration.
21
+ *
22
+ * Supports three resolution strategies:
23
+ * 1. **Direct `db`**: Returns the provided Db instance directly
24
+ * 2. **Factory `dbFactory`**: Calls the factory function (supports async)
25
+ * 3. **DI Token `dbToken`**: Resolves the Db from the DI container
26
+ *
27
+ * @param config - The Monque configuration containing database settings
28
+ * @param injectorFn - Optional function to resolve DI tokens (required for dbToken strategy)
29
+ * @returns The resolved MongoDB Db instance
30
+ * @throws Error if no database strategy is provided or if DI resolution fails
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * // Direct Db instance
35
+ * const db = await resolveDatabase({ db: mongoDb });
36
+ *
37
+ * // Factory function
38
+ * const db = await resolveDatabase({
39
+ * dbFactory: async () => {
40
+ * const client = await MongoClient.connect(uri);
41
+ * return client.db("myapp");
42
+ * }
43
+ * });
44
+ *
45
+ * // DI token
46
+ * const db = await resolveDatabase(
47
+ * { dbToken: "MONGODB_DATABASE" },
48
+ * (token) => injector.get(token)
49
+ * );
50
+ * ```
51
+ */
52
+ export async function resolveDatabase(
53
+ config: MonqueTsedConfig,
54
+ injectorFn?: InjectorFn,
55
+ ): Promise<Db> {
56
+ // Strategy 1: Direct Db instance
57
+ if (config.db) {
58
+ return config.db;
59
+ }
60
+
61
+ // Strategy 2: Factory function (sync or async)
62
+ if (config.dbFactory) {
63
+ return config.dbFactory();
64
+ }
65
+
66
+ // Strategy 3: DI token resolution
67
+ if (config.dbToken) {
68
+ if (!injectorFn) {
69
+ throw new Error(
70
+ 'MonqueTsedConfig.dbToken requires an injector function to resolve the database',
71
+ );
72
+ }
73
+
74
+ const resolved = injectorFn(config.dbToken);
75
+
76
+ if (!resolved) {
77
+ throw new Error(
78
+ `Could not resolve database from token: ${String(config.dbToken)}. ` +
79
+ 'Make sure the provider is registered in the DI container.',
80
+ );
81
+ }
82
+
83
+ if (isMongooseService(resolved)) {
84
+ // Check for Mongoose Service (duck typing)
85
+ // It has a get() method that returns a connection
86
+ const connectionId = config.mongooseConnectionId || 'default';
87
+ const connection = resolved.get(connectionId);
88
+
89
+ if (!connection) {
90
+ throw new Error(
91
+ `MongooseService resolved from token "${String(config.dbToken)}" returned no connection for ID "${connectionId}". ` +
92
+ 'Ensure the connection ID is correct and the connection is established.',
93
+ );
94
+ }
95
+
96
+ if ('db' in connection && connection.db) {
97
+ return connection.db as Db;
98
+ }
99
+ }
100
+
101
+ if (isMongooseConnection(resolved)) {
102
+ // Check for Mongoose Connection (duck typing)
103
+ // It has a db property that is the native Db instance
104
+ return resolved.db as Db;
105
+ }
106
+
107
+ // Default: Assume it is a native Db instance
108
+ if (typeof resolved !== 'object' || resolved === null || !('collection' in resolved)) {
109
+ throw new Error(
110
+ `Resolved value from token "${String(config.dbToken)}" does not appear to be a valid MongoDB Db instance.`,
111
+ );
112
+ }
113
+
114
+ return resolved as Db;
115
+ }
116
+
117
+ // No strategy provided
118
+ throw new Error("MonqueTsedConfig requires 'db', 'dbFactory', or 'dbToken' to be set");
119
+ }