@ambiten/core 1.0.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.
- package/LICENSE +21 -0
- package/README.md +247 -0
- package/bin/bootstrap.js +26 -0
- package/dist/ambiten-cache/ambitenCache.d.ts +64 -0
- package/dist/ambiten-cache/ambitenCache.d.ts.map +1 -0
- package/dist/ambiten-cache/ambitenCache.js +127 -0
- package/dist/ambiten-cache/index.d.ts +2 -0
- package/dist/ambiten-cache/index.d.ts.map +1 -0
- package/dist/ambiten-cache/index.js +4 -0
- package/dist/browser.d.ts +32 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +37 -0
- package/dist/cli/ambiten-core-cli.d.ts +4 -0
- package/dist/cli/ambiten-core-cli.d.ts.map +1 -0
- package/dist/cli/ambiten-core-cli.js +41 -0
- package/dist/cli/buildInteractiveConfig.d.ts +4 -0
- package/dist/cli/buildInteractiveConfig.d.ts.map +1 -0
- package/dist/cli/buildInteractiveConfig.js +198 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli/prompt.d.ts +10 -0
- package/dist/cli/prompt.d.ts.map +1 -0
- package/dist/cli/prompt.js +48 -0
- package/dist/cli/types.d.ts +11 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +2 -0
- package/dist/config/ambiten.config.schema.json +278 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/config/loadAmbitenConfig.d.ts +3 -0
- package/dist/config/loadAmbitenConfig.d.ts.map +1 -0
- package/dist/config/loadAmbitenConfig.js +131 -0
- package/dist/context/ambitenContext.d.ts +35 -0
- package/dist/context/ambitenContext.d.ts.map +1 -0
- package/dist/context/ambitenContext.js +147 -0
- package/dist/context/helpers/runManualTransaction.d.ts +3 -0
- package/dist/context/helpers/runManualTransaction.d.ts.map +1 -0
- package/dist/context/helpers/runManualTransaction.js +22 -0
- package/dist/context/helpers/transactionSession.d.ts +9 -0
- package/dist/context/helpers/transactionSession.d.ts.map +1 -0
- package/dist/context/helpers/transactionSession.js +8 -0
- package/dist/context/index.d.ts +4 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +6 -0
- package/dist/debug/debugLog.d.ts +2 -0
- package/dist/debug/debugLog.d.ts.map +1 -0
- package/dist/debug/debugLog.js +23 -0
- package/dist/debug/index.d.ts +2 -0
- package/dist/debug/index.d.ts.map +1 -0
- package/dist/debug/index.js +4 -0
- package/dist/gc/ambitenGC.d.ts +49 -0
- package/dist/gc/ambitenGC.d.ts.map +1 -0
- package/dist/gc/ambitenGC.js +138 -0
- package/dist/gc/gcCron.browser.d.ts +2 -0
- package/dist/gc/gcCron.browser.d.ts.map +1 -0
- package/dist/gc/gcCron.browser.js +9 -0
- package/dist/gc/gcCron.node.d.ts +19 -0
- package/dist/gc/gcCron.node.d.ts.map +1 -0
- package/dist/gc/gcCron.node.js +47 -0
- package/dist/gc/gcManager.d.ts +37 -0
- package/dist/gc/gcManager.d.ts.map +1 -0
- package/dist/gc/gcManager.js +62 -0
- package/dist/gc/index.d.ts +5 -0
- package/dist/gc/index.d.ts.map +1 -0
- package/dist/gc/index.js +7 -0
- package/dist/graphql/ambitenGraphQL.d.ts +24 -0
- package/dist/graphql/ambitenGraphQL.d.ts.map +1 -0
- package/dist/graphql/ambitenGraphQL.js +215 -0
- package/dist/graphql/index.d.ts +2 -0
- package/dist/graphql/index.d.ts.map +1 -0
- package/dist/graphql/index.js +4 -0
- package/dist/index-browser.js +25 -0
- package/dist/index-browser.js.map +1 -0
- package/dist/index-cli.js +26 -0
- package/dist/index-cli.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/init-cli/generate.project.d.ts +9 -0
- package/dist/init-cli/generate.project.d.ts.map +1 -0
- package/dist/init-cli/generate.project.js +38 -0
- package/dist/instrumentation/index.d.ts +3 -0
- package/dist/instrumentation/index.d.ts.map +1 -0
- package/dist/instrumentation/index.js +5 -0
- package/dist/instrumentation/measureQuery.browser.d.ts +3 -0
- package/dist/instrumentation/measureQuery.browser.d.ts.map +1 -0
- package/dist/instrumentation/measureQuery.browser.js +6 -0
- package/dist/instrumentation/measureQueryWithErrors.d.ts +11 -0
- package/dist/instrumentation/measureQueryWithErrors.d.ts.map +1 -0
- package/dist/instrumentation/measureQueryWithErrors.js +90 -0
- package/dist/lib-core/ambitenClient.d.ts +115 -0
- package/dist/lib-core/ambitenClient.d.ts.map +1 -0
- package/dist/lib-core/ambitenClient.js +537 -0
- package/dist/lib-core/ambitenModelFactory.d.ts +979 -0
- package/dist/lib-core/ambitenModelFactory.d.ts.map +1 -0
- package/dist/lib-core/ambitenModelFactory.js +3984 -0
- package/dist/lib-core/ambitenSchema.d.ts +126 -0
- package/dist/lib-core/ambitenSchema.d.ts.map +1 -0
- package/dist/lib-core/ambitenSchema.js +247 -0
- package/dist/lib-core/bootstrap/ambitenBootstrap.d.ts +38 -0
- package/dist/lib-core/bootstrap/ambitenBootstrap.d.ts.map +1 -0
- package/dist/lib-core/bootstrap/ambitenBootstrap.js +381 -0
- package/dist/lib-core/bootstrap/index.d.ts +2 -0
- package/dist/lib-core/bootstrap/index.d.ts.map +1 -0
- package/dist/lib-core/bootstrap/index.js +4 -0
- package/dist/lib-core/index.d.ts +5 -0
- package/dist/lib-core/index.d.ts.map +1 -0
- package/dist/lib-core/index.js +7 -0
- package/dist/middleware/authorization/auth.d.ts +22 -0
- package/dist/middleware/authorization/auth.d.ts.map +1 -0
- package/dist/middleware/authorization/auth.js +37 -0
- package/dist/middleware/index.d.ts +4 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +6 -0
- package/dist/middleware/rbac/rbacMiddleware.d.ts +103 -0
- package/dist/middleware/rbac/rbacMiddleware.d.ts.map +1 -0
- package/dist/middleware/rbac/rbacMiddleware.js +246 -0
- package/dist/middleware/rbac/rbacTypes.d.ts +4 -0
- package/dist/middleware/rbac/rbacTypes.d.ts.map +1 -0
- package/dist/middleware/rbac/rbacTypes.js +8 -0
- package/dist/plugins/index.d.ts +3 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +5 -0
- package/dist/plugins/softDelete/applySoftDelete.d.ts +5 -0
- package/dist/plugins/softDelete/applySoftDelete.d.ts.map +1 -0
- package/dist/plugins/softDelete/applySoftDelete.js +139 -0
- package/dist/plugins/softDelete/types.d.ts +14 -0
- package/dist/plugins/softDelete/types.d.ts.map +1 -0
- package/dist/plugins/softDelete/types.js +2 -0
- package/dist/redis-manager/index.d.ts +2 -0
- package/dist/redis-manager/index.d.ts.map +1 -0
- package/dist/redis-manager/index.js +4 -0
- package/dist/redis-manager/redisClient.d.ts +32 -0
- package/dist/redis-manager/redisClient.d.ts.map +1 -0
- package/dist/redis-manager/redisClient.js +146 -0
- package/dist/tanancy/MultiTenantManager.d.ts +98 -0
- package/dist/tanancy/MultiTenantManager.d.ts.map +1 -0
- package/dist/tanancy/MultiTenantManager.js +190 -0
- package/dist/tanancy/TenantContext.d.ts +31 -0
- package/dist/tanancy/TenantContext.d.ts.map +1 -0
- package/dist/tanancy/TenantContext.js +59 -0
- package/dist/tanancy/TenantModelResolver.d.ts +10 -0
- package/dist/tanancy/TenantModelResolver.d.ts.map +1 -0
- package/dist/tanancy/TenantModelResolver.js +114 -0
- package/dist/tanancy/index.d.ts +5 -0
- package/dist/tanancy/index.d.ts.map +1 -0
- package/dist/tanancy/index.js +7 -0
- package/dist/tanancy/init/initMultiTenancy.d.ts +29 -0
- package/dist/tanancy/init/initMultiTenancy.d.ts.map +1 -0
- package/dist/tanancy/init/initMultiTenancy.js +62 -0
- package/dist/templates/generateDefaultConfigContent.d.ts +10 -0
- package/dist/templates/generateDefaultConfigContent.d.ts.map +1 -0
- package/dist/templates/generateDefaultConfigContent.js +108 -0
- package/dist/templates/generateGCManagerFile.d.ts +2 -0
- package/dist/templates/generateGCManagerFile.d.ts.map +1 -0
- package/dist/templates/generateGCManagerFile.js +39 -0
- package/dist/templates/generateGCRunnerFile.d.ts +2 -0
- package/dist/templates/generateGCRunnerFile.d.ts.map +1 -0
- package/dist/templates/generateGCRunnerFile.js +25 -0
- package/dist/templates/generateMainEntry.d.ts +3 -0
- package/dist/templates/generateMainEntry.d.ts.map +1 -0
- package/dist/templates/generateMainEntry.js +24 -0
- package/dist/templates/index.d.ts +6 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +8 -0
- package/dist/templates/scaffold/structureGenerator.d.ts +4 -0
- package/dist/templates/scaffold/structureGenerator.d.ts.map +1 -0
- package/dist/templates/scaffold/structureGenerator.js +193 -0
- package/dist/types/GCConfig.d.ts +7 -0
- package/dist/types/GCConfig.d.ts.map +1 -0
- package/dist/types/GCConfig.js +2 -0
- package/dist/types/ambiten-runtime-type.d.ts +24 -0
- package/dist/types/ambiten-runtime-type.d.ts.map +1 -0
- package/dist/types/ambiten-runtime-type.js +2 -0
- package/dist/types/ambiten.client.type.d.ts +136 -0
- package/dist/types/ambiten.client.type.d.ts.map +1 -0
- package/dist/types/ambiten.client.type.js +3 -0
- package/dist/types/ambiten.model.type.d.ts +135 -0
- package/dist/types/ambiten.model.type.d.ts.map +1 -0
- package/dist/types/ambiten.model.type.js +4 -0
- package/dist/types/ambitenConfig.d.ts +103 -0
- package/dist/types/ambitenConfig.d.ts.map +1 -0
- package/dist/types/ambitenConfig.js +3 -0
- package/dist/types/bootstrapClient.type.d.ts +11 -0
- package/dist/types/bootstrapClient.type.d.ts.map +1 -0
- package/dist/types/bootstrapClient.type.js +2 -0
- package/dist/types/db.provider.d.ts +66 -0
- package/dist/types/db.provider.d.ts.map +1 -0
- package/dist/types/db.provider.js +2 -0
- package/dist/types/document.d.ts +7 -0
- package/dist/types/document.d.ts.map +1 -0
- package/dist/types/document.js +2 -0
- package/dist/types/event.type.d.ts +6 -0
- package/dist/types/event.type.d.ts.map +1 -0
- package/dist/types/event.type.js +27 -0
- package/dist/types/graphql.type.d.ts +22 -0
- package/dist/types/graphql.type.d.ts.map +1 -0
- package/dist/types/graphql.type.js +2 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +17 -0
- package/dist/types/logger.types.d.ts +15 -0
- package/dist/types/logger.types.d.ts.map +1 -0
- package/dist/types/logger.types.js +3 -0
- package/dist/types/middleware/types.d.ts +28 -0
- package/dist/types/middleware/types.d.ts.map +1 -0
- package/dist/types/middleware/types.js +2 -0
- package/dist/types/result.utils.types.d.ts +7 -0
- package/dist/types/result.utils.types.d.ts.map +1 -0
- package/dist/types/result.utils.types.js +2 -0
- package/dist/types/schema.type.d.ts +23 -0
- package/dist/types/schema.type.d.ts.map +1 -0
- package/dist/types/schema.type.js +10 -0
- package/dist/types/user.type.d.ts +11 -0
- package/dist/types/user.type.d.ts.map +1 -0
- package/dist/types/user.type.js +2 -0
- package/dist/utils/AuthUtils.d.ts +15 -0
- package/dist/utils/AuthUtils.d.ts.map +1 -0
- package/dist/utils/AuthUtils.js +26 -0
- package/dist/utils/ModelRegistry.d.ts +15 -0
- package/dist/utils/ModelRegistry.d.ts.map +1 -0
- package/dist/utils/ModelRegistry.js +35 -0
- package/dist/utils/banner.d.ts +2 -0
- package/dist/utils/banner.d.ts.map +1 -0
- package/dist/utils/banner.js +20 -0
- package/dist/utils/builders/createModel.d.ts +37 -0
- package/dist/utils/builders/createModel.d.ts.map +1 -0
- package/dist/utils/builders/createModel.js +38 -0
- package/dist/utils/builders/getTenantDb.d.ts +12 -0
- package/dist/utils/builders/getTenantDb.d.ts.map +1 -0
- package/dist/utils/builders/getTenantDb.js +25 -0
- package/dist/utils/builders/index.d.ts +4 -0
- package/dist/utils/builders/index.d.ts.map +1 -0
- package/dist/utils/builders/index.js +6 -0
- package/dist/utils/builders/schema.d.ts +22 -0
- package/dist/utils/builders/schema.d.ts.map +1 -0
- package/dist/utils/builders/schema.js +26 -0
- package/dist/utils/cacheWithRedis.d.ts +27 -0
- package/dist/utils/cacheWithRedis.d.ts.map +1 -0
- package/dist/utils/cacheWithRedis.js +22 -0
- package/dist/utils/castId.d.ts +5 -0
- package/dist/utils/castId.d.ts.map +1 -0
- package/dist/utils/castId.js +52 -0
- package/dist/utils/color-palatte.d.ts +4 -0
- package/dist/utils/color-palatte.d.ts.map +1 -0
- package/dist/utils/color-palatte.js +24 -0
- package/dist/utils/configureAmbitenContext.d.ts +13 -0
- package/dist/utils/configureAmbitenContext.d.ts.map +1 -0
- package/dist/utils/configureAmbitenContext.js +35 -0
- package/dist/utils/detect-package-manager.d.ts +5 -0
- package/dist/utils/detect-package-manager.d.ts.map +1 -0
- package/dist/utils/detect-package-manager.js +59 -0
- package/dist/utils/ensureModelNameSafe.d.ts +12 -0
- package/dist/utils/ensureModelNameSafe.d.ts.map +1 -0
- package/dist/utils/ensureModelNameSafe.js +28 -0
- package/dist/utils/ensureRedis.d.ts +7 -0
- package/dist/utils/ensureRedis.d.ts.map +1 -0
- package/dist/utils/ensureRedis.js +40 -0
- package/dist/utils/error/ambitenError-handler.d.ts +24 -0
- package/dist/utils/error/ambitenError-handler.d.ts.map +1 -0
- package/dist/utils/error/ambitenError-handler.js +39 -0
- package/dist/utils/error/errorTypes.d.ts +27 -0
- package/dist/utils/error/errorTypes.d.ts.map +1 -0
- package/dist/utils/error/errorTypes.js +30 -0
- package/dist/utils/error/index.d.ts +3 -0
- package/dist/utils/error/index.d.ts.map +1 -0
- package/dist/utils/error/index.js +5 -0
- package/dist/utils/eventOptions.d.ts +38 -0
- package/dist/utils/eventOptions.d.ts.map +1 -0
- package/dist/utils/eventOptions.js +130 -0
- package/dist/utils/events.d.ts +5 -0
- package/dist/utils/events.d.ts.map +1 -0
- package/dist/utils/events.js +7 -0
- package/dist/utils/index.d.ts +23 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +25 -0
- package/dist/utils/invalidateTenantCache.d.ts +18 -0
- package/dist/utils/invalidateTenantCache.d.ts.map +1 -0
- package/dist/utils/invalidateTenantCache.js +27 -0
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +27 -0
- package/dist/utils/modelLoader.d.ts +3 -0
- package/dist/utils/modelLoader.d.ts.map +1 -0
- package/dist/utils/modelLoader.js +41 -0
- package/dist/utils/resolveAmbitenOption.d.ts +2 -0
- package/dist/utils/resolveAmbitenOption.d.ts.map +1 -0
- package/dist/utils/resolveAmbitenOption.js +10 -0
- package/dist/utils/resolveRuntimeConfig.d.ts +22 -0
- package/dist/utils/resolveRuntimeConfig.d.ts.map +1 -0
- package/dist/utils/resolveRuntimeConfig.js +33 -0
- package/dist/utils/scaffoldLoger.d.ts +11 -0
- package/dist/utils/scaffoldLoger.d.ts.map +1 -0
- package/dist/utils/scaffoldLoger.js +68 -0
- package/dist/utils/symbols.d.ts +4 -0
- package/dist/utils/symbols.d.ts.map +1 -0
- package/dist/utils/symbols.js +6 -0
- package/dist/utils/time.d.ts +5 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +39 -0
- package/package.json +180 -0
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AmbitenClient = void 0;
|
|
4
|
+
exports.createAmbitenClientModule = createAmbitenClientModule;
|
|
5
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
6
|
+
const mongodb_1 = require("mongodb");
|
|
7
|
+
require("dotenv/config");
|
|
8
|
+
const utils_1 = require("../utils");
|
|
9
|
+
const tanancy_1 = require("../tanancy");
|
|
10
|
+
const context_1 = require("../context");
|
|
11
|
+
const AmbitenSymbol = Symbol.for('Ambiten:default');
|
|
12
|
+
/**
|
|
13
|
+
* AmbitenClient is a MongoDB client wrapper that provides a simplified interface
|
|
14
|
+
* for connecting to and interacting with MongoDB databases.
|
|
15
|
+
*/
|
|
16
|
+
class AmbitenClient {
|
|
17
|
+
_opts;
|
|
18
|
+
_uri;
|
|
19
|
+
_client;
|
|
20
|
+
_db;
|
|
21
|
+
collectionRef;
|
|
22
|
+
_connected = false;
|
|
23
|
+
_defaultDbName;
|
|
24
|
+
_overrideDbName;
|
|
25
|
+
static instances = new Map();
|
|
26
|
+
static defaultUri = 'mongodb://127.0.0.1:27017';
|
|
27
|
+
constructor(_opts) {
|
|
28
|
+
this._opts = _opts;
|
|
29
|
+
this._uri = _opts.uri || process.env.MONGO_URI || AmbitenClient.defaultUri;
|
|
30
|
+
this._defaultDbName =
|
|
31
|
+
_opts?.options?.dbName ||
|
|
32
|
+
process.env.DB_NAME ||
|
|
33
|
+
'Ambiten_default_db';
|
|
34
|
+
this._client = new mongodb_1.MongoClient(this._uri, {
|
|
35
|
+
directConnection: true,
|
|
36
|
+
minPoolSize: 5,
|
|
37
|
+
maxPoolSize: 50,
|
|
38
|
+
serverSelectionTimeoutMS: 5000
|
|
39
|
+
});
|
|
40
|
+
this._db = this._client.db(this._opts.options?.dbName);
|
|
41
|
+
this.ensureMongoDependency();
|
|
42
|
+
}
|
|
43
|
+
static init(opts) {
|
|
44
|
+
const key = String(AmbitenSymbol);
|
|
45
|
+
const existing = this.instances.get(key);
|
|
46
|
+
if (existing) {
|
|
47
|
+
return existing;
|
|
48
|
+
}
|
|
49
|
+
const client = createAmbitenClientModule({
|
|
50
|
+
uri: opts?.uri ?? this.defaultUri ?? process.env.MONGO_URI,
|
|
51
|
+
options: { dbName: opts?.options?.dbName },
|
|
52
|
+
tenantResolver: opts?.tenantResolver
|
|
53
|
+
});
|
|
54
|
+
this.instances.set(key, client);
|
|
55
|
+
return client;
|
|
56
|
+
}
|
|
57
|
+
ensureMongoDependency() {
|
|
58
|
+
try {
|
|
59
|
+
const { MongoClient } = require('mongodb');
|
|
60
|
+
void MongoClient;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
console.error('\n❌ Missing peer dependency: "mongodb".\n' +
|
|
64
|
+
'Please install it in your project before continuing:\n\n' +
|
|
65
|
+
' npm i mongodb\n');
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Resolution order:
|
|
71
|
+
* 1. explicit ctx.db
|
|
72
|
+
* 2. tenant-aware db resolution
|
|
73
|
+
* 3. explicit ctx.dbName on base client
|
|
74
|
+
* 4. mutable override dbName
|
|
75
|
+
* 5. default dbName
|
|
76
|
+
*/
|
|
77
|
+
async db(ctx) {
|
|
78
|
+
if (ctx?.db) {
|
|
79
|
+
return ctx.db;
|
|
80
|
+
}
|
|
81
|
+
if (ctx?.tenantId) {
|
|
82
|
+
const resolver = this._opts.tenantResolver;
|
|
83
|
+
if (!resolver) {
|
|
84
|
+
throw new Error('Multi-tenancy not enabled. Provide tenantResolver to AmbitenClient or avoid passing tenantId.');
|
|
85
|
+
}
|
|
86
|
+
const tenantClient = await resolver.getClient(ctx.tenantId);
|
|
87
|
+
if (!tenantClient) {
|
|
88
|
+
throw new Error(`Tenant "${ctx.tenantId}" is not registered.`);
|
|
89
|
+
}
|
|
90
|
+
const resolvedTenantDbName = ctx.dbName ??
|
|
91
|
+
tanancy_1.MultiTenantManager.getTenantDbName(ctx.tenantId) ??
|
|
92
|
+
this._defaultDbName;
|
|
93
|
+
return tenantClient.db(resolvedTenantDbName);
|
|
94
|
+
}
|
|
95
|
+
if (!this._client || !this._connected) {
|
|
96
|
+
const message = 'AmbitenClient not connected. call connect() first.';
|
|
97
|
+
throw (0, utils_1.createAmbitenError)(utils_1.ErrorType.CONNECTION_ERROR, message, {
|
|
98
|
+
details: {
|
|
99
|
+
operation: 'db'
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (ctx?.dbName) {
|
|
104
|
+
return this._client.db(ctx.dbName);
|
|
105
|
+
}
|
|
106
|
+
if (this._overrideDbName) {
|
|
107
|
+
return this._client.db(this._overrideDbName);
|
|
108
|
+
}
|
|
109
|
+
return this._client.db(this._defaultDbName);
|
|
110
|
+
}
|
|
111
|
+
static async db(ctx) {
|
|
112
|
+
return this.init().db(ctx);
|
|
113
|
+
}
|
|
114
|
+
validateUri(uri) {
|
|
115
|
+
if (!uri || typeof uri !== 'string') {
|
|
116
|
+
const message = 'Missing MongoDB URI. Set MONGODB_URI (or pass uri explicitly).';
|
|
117
|
+
throw (0, utils_1.createAmbitenError)(utils_1.ErrorType.VALIDATION_ERROR, message, {
|
|
118
|
+
details: {
|
|
119
|
+
operation: 'validateUri',
|
|
120
|
+
providedUri: uri,
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (!/^mongodb(\+srv)?:\/\//.test(uri)) {
|
|
125
|
+
const message = `Invalid MongoDB URI "${uri}". It must start with "mongodb://" or "mongodb+srv://".`;
|
|
126
|
+
throw (0, utils_1.createAmbitenError)(utils_1.ErrorType.VALIDATION_ERROR, message, {
|
|
127
|
+
details: {
|
|
128
|
+
operation: 'validateUri',
|
|
129
|
+
providedUri: uri,
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async connect() {
|
|
135
|
+
this.validateUri(this._opts.uri);
|
|
136
|
+
if (!this._client) {
|
|
137
|
+
this._client = new mongodb_1.MongoClient(this._opts.uri);
|
|
138
|
+
}
|
|
139
|
+
if (!this._connected) {
|
|
140
|
+
await this._client.connect();
|
|
141
|
+
this._connected = true;
|
|
142
|
+
}
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
async client(ctx) {
|
|
146
|
+
if (ctx?.tenantId) {
|
|
147
|
+
const resolver = this._opts.tenantResolver;
|
|
148
|
+
if (!resolver) {
|
|
149
|
+
throw new Error('Multi-tenancy not enabled. Provide tenantResolver to AmbitenClient or avoid passing tenantId.');
|
|
150
|
+
}
|
|
151
|
+
const tenantClient = await resolver.getClient(ctx.tenantId);
|
|
152
|
+
if (!tenantClient) {
|
|
153
|
+
throw new Error(`Tenant "${ctx.tenantId}" is not registered.`);
|
|
154
|
+
}
|
|
155
|
+
return tenantClient;
|
|
156
|
+
}
|
|
157
|
+
if (!this._client) {
|
|
158
|
+
throw new Error('Client not initialized. Call `connect()` first.');
|
|
159
|
+
}
|
|
160
|
+
return this._client;
|
|
161
|
+
}
|
|
162
|
+
async useCollection(collectionName) {
|
|
163
|
+
if (!collectionName || typeof collectionName !== 'string' || !collectionName.trim()) {
|
|
164
|
+
throw new Error('Collection name is required.');
|
|
165
|
+
}
|
|
166
|
+
if (!this._client) {
|
|
167
|
+
throw new Error('Client not initialized. Call `connect()` first.');
|
|
168
|
+
}
|
|
169
|
+
const scope = this.resolveClientScope({ collectionName });
|
|
170
|
+
const db = this._client.db(scope.dbName);
|
|
171
|
+
if (!db || typeof db.collection !== 'function') {
|
|
172
|
+
throw new Error('Database name is required in client options.');
|
|
173
|
+
}
|
|
174
|
+
const collection = db.collection(scope.collectionName);
|
|
175
|
+
this.collectionRef = collection;
|
|
176
|
+
return collection;
|
|
177
|
+
}
|
|
178
|
+
async collection(collectionName, ctx) {
|
|
179
|
+
const db = await this.db(ctx);
|
|
180
|
+
return db.collection(collectionName);
|
|
181
|
+
}
|
|
182
|
+
async getCollection(collectionName, ctx) {
|
|
183
|
+
if (!this._client || typeof this._client.db !== 'function') {
|
|
184
|
+
return {
|
|
185
|
+
toString: () => collectionName
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
return this.collection(collectionName, ctx);
|
|
189
|
+
}
|
|
190
|
+
async getClusterInfo() {
|
|
191
|
+
if (!this._client) {
|
|
192
|
+
throw new Error('Client not initialized. Call `connect()` first.');
|
|
193
|
+
}
|
|
194
|
+
const dbName = this._opts?.options?.dbName;
|
|
195
|
+
if (!dbName) {
|
|
196
|
+
throw new Error('Database name is required in client options.');
|
|
197
|
+
}
|
|
198
|
+
const helloResult = await this._client
|
|
199
|
+
.db(dbName)
|
|
200
|
+
.admin()
|
|
201
|
+
.command({ hello: 1 });
|
|
202
|
+
if (helloResult?.msg === 'isdbgrid') {
|
|
203
|
+
console.log('MongoDB is running in a sharded cluster.');
|
|
204
|
+
return { type: 'sharded' };
|
|
205
|
+
}
|
|
206
|
+
if (helloResult?.setName || helloResult?.hosts) {
|
|
207
|
+
console.log('MongoDB is running as a replica set.');
|
|
208
|
+
return {
|
|
209
|
+
type: 'replicaSet',
|
|
210
|
+
setName: helloResult.setName,
|
|
211
|
+
hosts: helloResult.hosts
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
console.log('MongoDB is running as a standalone instance.');
|
|
215
|
+
return { type: 'standalone' };
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Resolves database + session + runtime binding
|
|
219
|
+
*/
|
|
220
|
+
static async resolveRuntime() {
|
|
221
|
+
const ctx = context_1.AmbitenContext.get();
|
|
222
|
+
if (!ctx?.tenantId) {
|
|
223
|
+
throw new Error("Tenant not resolved in context.");
|
|
224
|
+
}
|
|
225
|
+
const tenant = tanancy_1.MultiTenantManager.getTenant(ctx.tenantId);
|
|
226
|
+
if (!tenant) {
|
|
227
|
+
throw new Error(`Tenant not found: ${ctx.tenantId}`);
|
|
228
|
+
}
|
|
229
|
+
const db = this.resolveDbFromTenant(tenant, ctx);
|
|
230
|
+
return {
|
|
231
|
+
db,
|
|
232
|
+
session: ctx?.session,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Mutable legacy helper. Prefer withDatabase() or withScope() in request-safe flows.
|
|
237
|
+
*/
|
|
238
|
+
async useDatabase(dbName) {
|
|
239
|
+
if (!dbName) {
|
|
240
|
+
throw new Error('Database name is required.');
|
|
241
|
+
}
|
|
242
|
+
const client = await this.client();
|
|
243
|
+
this._overrideDbName = dbName;
|
|
244
|
+
return {
|
|
245
|
+
db: client.db(dbName),
|
|
246
|
+
client
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
withDatabase(dbName) {
|
|
250
|
+
return {
|
|
251
|
+
db: async (ctx) => {
|
|
252
|
+
const merged = { ...ctx, dbName: ctx?.dbName ?? dbName };
|
|
253
|
+
return this.db(merged);
|
|
254
|
+
},
|
|
255
|
+
connect: async () => {
|
|
256
|
+
await this.connect();
|
|
257
|
+
return this;
|
|
258
|
+
},
|
|
259
|
+
client: async (ctx) => {
|
|
260
|
+
const merged = { ...ctx, dbName: ctx?.dbName ?? dbName };
|
|
261
|
+
return this.client(merged);
|
|
262
|
+
},
|
|
263
|
+
collection: async (collectionName, ctx) => {
|
|
264
|
+
const merged = { ...ctx, dbName: ctx?.dbName ?? dbName };
|
|
265
|
+
return this.collection(collectionName, merged);
|
|
266
|
+
},
|
|
267
|
+
startSession: async (ctx) => {
|
|
268
|
+
const merged = {
|
|
269
|
+
...ctx, dbName: ctx?.dbName ?? dbName, session: ctx?.session
|
|
270
|
+
};
|
|
271
|
+
return this.startSession(merged);
|
|
272
|
+
},
|
|
273
|
+
close: async () => {
|
|
274
|
+
await this.close();
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Resolves the active database name.
|
|
280
|
+
*
|
|
281
|
+
* @param overrideDbName - Optional database override.
|
|
282
|
+
* @returns The resolved database name.
|
|
283
|
+
* @throws {Error} When no database name can be resolved.
|
|
284
|
+
*/
|
|
285
|
+
resolveDbName(overrideDbName) {
|
|
286
|
+
return this.resolveClientScope({ dbName: overrideDbName }).dbName;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Resolves the active client execution scope by combining explicit method
|
|
290
|
+
* arguments, active Ambiten runtime context, and client-level defaults.
|
|
291
|
+
*
|
|
292
|
+
* Resolution priority:
|
|
293
|
+
* 1. Explicit method arguments
|
|
294
|
+
* 2. Active Ambiten runtime context
|
|
295
|
+
* 3. Client configuration
|
|
296
|
+
* 4. Client defaults
|
|
297
|
+
*
|
|
298
|
+
* This method does not mutate runtime context or client configuration.
|
|
299
|
+
*
|
|
300
|
+
* @param input - Optional scope overrides for database, collection, and session.
|
|
301
|
+
* @returns The resolved execution scope for client-level operations.
|
|
302
|
+
* @throws {Error} When no database name can be resolved.
|
|
303
|
+
*/
|
|
304
|
+
resolveClientScope(input = {}) {
|
|
305
|
+
const ctx = context_1.AmbitenContext.get();
|
|
306
|
+
const dbName = input.dbName ??
|
|
307
|
+
ctx.dbName ??
|
|
308
|
+
this._opts?.options?.dbName ??
|
|
309
|
+
this._defaultDbName;
|
|
310
|
+
if (!dbName) {
|
|
311
|
+
throw new Error('Database name is required in client options.');
|
|
312
|
+
}
|
|
313
|
+
const collectionName = input.collectionName ??
|
|
314
|
+
ctx.collectionName;
|
|
315
|
+
return {
|
|
316
|
+
tenantId: ctx.tenantId,
|
|
317
|
+
requestId: ctx.requestId,
|
|
318
|
+
dbName,
|
|
319
|
+
collectionName: collectionName?.trim() || undefined,
|
|
320
|
+
session: input.session ?? ctx.session
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
static resolveDbFromTenant(tenant, ctx) {
|
|
324
|
+
if (!tenant?.client) {
|
|
325
|
+
throw new Error(`Tenant "${ctx?.tenantId}" has no MongoDB client.`);
|
|
326
|
+
}
|
|
327
|
+
const dbName = ctx?.dbName ?? tenant.dbName;
|
|
328
|
+
if (!dbName) {
|
|
329
|
+
throw new Error(`Database name not resolved for tenant "${ctx?.tenantId}".`);
|
|
330
|
+
}
|
|
331
|
+
return tenant.client.db(dbName);
|
|
332
|
+
}
|
|
333
|
+
;
|
|
334
|
+
async withContext(context, callback) {
|
|
335
|
+
return context_1.AmbitenContext.run({
|
|
336
|
+
tenantId: context.tenantId,
|
|
337
|
+
requestId: context.requestId,
|
|
338
|
+
dbName: context.dbName,
|
|
339
|
+
collectionName: context.collectionName,
|
|
340
|
+
session: context.session
|
|
341
|
+
}, callback);
|
|
342
|
+
}
|
|
343
|
+
logWithContext(level, message, meta = {}) {
|
|
344
|
+
const ctx = context_1.AmbitenContext.get();
|
|
345
|
+
const enrichedMeta = {
|
|
346
|
+
requestId: ctx.requestId,
|
|
347
|
+
tenantId: ctx.tenantId,
|
|
348
|
+
...ctx.loggerMeta,
|
|
349
|
+
...meta
|
|
350
|
+
};
|
|
351
|
+
this._opts.options?.config?.logger?.[level]?.(message, enrichedMeta);
|
|
352
|
+
}
|
|
353
|
+
withTenant(tenantId) {
|
|
354
|
+
return {
|
|
355
|
+
db: async (ctx) => {
|
|
356
|
+
const merged = { ...ctx, tenantId: ctx?.tenantId ?? tenantId };
|
|
357
|
+
return this.db(merged);
|
|
358
|
+
},
|
|
359
|
+
connect: async () => {
|
|
360
|
+
await this.connect();
|
|
361
|
+
return this;
|
|
362
|
+
},
|
|
363
|
+
client: async (ctx) => {
|
|
364
|
+
const merged = { ...ctx, tenantId: ctx?.tenantId ?? tenantId };
|
|
365
|
+
return this.client(merged);
|
|
366
|
+
},
|
|
367
|
+
collection: async (collectionName, ctx) => {
|
|
368
|
+
const merged = { ...ctx, tenantId: ctx?.tenantId ?? tenantId };
|
|
369
|
+
return this.collection(collectionName, merged);
|
|
370
|
+
},
|
|
371
|
+
startSession: async (ctx) => {
|
|
372
|
+
const merged = { ...ctx, tenantId: ctx.tenantId ?? tenantId };
|
|
373
|
+
return this.startSession(merged);
|
|
374
|
+
},
|
|
375
|
+
close: async () => {
|
|
376
|
+
await this.close();
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
withScope(scope) {
|
|
381
|
+
return {
|
|
382
|
+
db: async (ctx) => {
|
|
383
|
+
const merged = {
|
|
384
|
+
...ctx,
|
|
385
|
+
tenantId: ctx?.tenantId ?? scope.tenantId,
|
|
386
|
+
dbName: ctx?.dbName ?? scope.dbName
|
|
387
|
+
};
|
|
388
|
+
return this.db(merged);
|
|
389
|
+
},
|
|
390
|
+
connect: async () => {
|
|
391
|
+
await this.connect();
|
|
392
|
+
return this;
|
|
393
|
+
},
|
|
394
|
+
client: async (ctx) => {
|
|
395
|
+
const merged = {
|
|
396
|
+
...ctx,
|
|
397
|
+
tenantId: ctx?.tenantId ?? scope.tenantId,
|
|
398
|
+
dbName: ctx?.dbName ?? scope.dbName
|
|
399
|
+
};
|
|
400
|
+
return this.client(merged);
|
|
401
|
+
},
|
|
402
|
+
collection: async (collectionName, ctx) => {
|
|
403
|
+
const merged = {
|
|
404
|
+
...ctx,
|
|
405
|
+
tenantId: ctx?.tenantId ?? scope.tenantId,
|
|
406
|
+
dbName: ctx?.dbName ?? scope.dbName
|
|
407
|
+
};
|
|
408
|
+
return this.collection(collectionName, merged);
|
|
409
|
+
},
|
|
410
|
+
startSession: async (ctx) => {
|
|
411
|
+
const merged = {
|
|
412
|
+
...ctx,
|
|
413
|
+
tenantId: ctx?.tenantId ?? scope.tenantId,
|
|
414
|
+
dbName: ctx?.dbName ?? scope.dbName
|
|
415
|
+
};
|
|
416
|
+
return this.startSession(merged);
|
|
417
|
+
},
|
|
418
|
+
close: async () => {
|
|
419
|
+
await this.close();
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
resetDatabase() {
|
|
424
|
+
this._overrideDbName = undefined;
|
|
425
|
+
}
|
|
426
|
+
async startSession(ctx) {
|
|
427
|
+
const client = await this.client(ctx);
|
|
428
|
+
return client.startSession();
|
|
429
|
+
}
|
|
430
|
+
async dropCollection(collectionName, ctx) {
|
|
431
|
+
if (!this._client && !ctx?.db) {
|
|
432
|
+
throw (0, utils_1.createAmbitenError)(utils_1.ErrorType.CONNECTION_ERROR, 'Client not initialized. Call `connect()` first.', {
|
|
433
|
+
details: {
|
|
434
|
+
operation: 'dropCollection',
|
|
435
|
+
providedCollectionName: collectionName
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
const resolvedCollectionName = collectionName?.trim() ||
|
|
440
|
+
this.collectionRef?.collectionName;
|
|
441
|
+
if (!resolvedCollectionName) {
|
|
442
|
+
throw (0, utils_1.createAmbitenError)(utils_1.ErrorType.VALIDATION_ERROR, 'Collection name is required.', {
|
|
443
|
+
details: {
|
|
444
|
+
operation: 'dropCollection',
|
|
445
|
+
providedCollectionName: collectionName
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
const db = ctx?.db ?? this._client.db(this.resolveDbName(ctx?.dbName));
|
|
450
|
+
await db.collection(resolvedCollectionName).drop();
|
|
451
|
+
}
|
|
452
|
+
async dropDatabase(ctx) {
|
|
453
|
+
if (!this._client && !ctx?.db) {
|
|
454
|
+
throw (0, utils_1.createAmbitenError)(utils_1.ErrorType.CONNECTION_ERROR, 'Client not initialized. Call `connect()` first.', {
|
|
455
|
+
details: {
|
|
456
|
+
operation: 'dropDatabase'
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
const db = ctx?.db ??
|
|
461
|
+
this._client.db(this.resolveDbName(ctx?.dbName));
|
|
462
|
+
const result = await db.dropDatabase();
|
|
463
|
+
return result === undefined ? true : result;
|
|
464
|
+
}
|
|
465
|
+
async close() {
|
|
466
|
+
if (this._client) {
|
|
467
|
+
await this._client.close?.();
|
|
468
|
+
}
|
|
469
|
+
this._client = null;
|
|
470
|
+
this._db = null;
|
|
471
|
+
this._connected = false;
|
|
472
|
+
console.log('[info]: Disconnected from MongoDB');
|
|
473
|
+
}
|
|
474
|
+
async disconnect() {
|
|
475
|
+
await this.close();
|
|
476
|
+
}
|
|
477
|
+
isConnected() {
|
|
478
|
+
return this._connected;
|
|
479
|
+
}
|
|
480
|
+
static handleTopologyEvent(event) {
|
|
481
|
+
if (event instanceof mongodb_1.TopologyOpeningEvent) {
|
|
482
|
+
console.log(`Topology opened: ${event.topologyId}`);
|
|
483
|
+
}
|
|
484
|
+
else if (event instanceof mongodb_1.TopologyClosedEvent) {
|
|
485
|
+
console.log(`Topology closed: ${event.topologyId}`);
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
console.warn(`[warning]: Unknown topology event: ${event}`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
;
|
|
492
|
+
static async handleLogBatch(batch, transporter) {
|
|
493
|
+
if (!Array.isArray(batch) || batch.length === 0) {
|
|
494
|
+
console.warn('[warning]: Received an empty log batch or invalid format.');
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const ctx = context_1.AmbitenContext.get();
|
|
498
|
+
this.handleTopologyEvent(batch[0]);
|
|
499
|
+
const remaining = batch.slice(1);
|
|
500
|
+
if (remaining.length === 0)
|
|
501
|
+
return;
|
|
502
|
+
if (!transporter) {
|
|
503
|
+
console.warn(`[warning]: No transporter provided; ${remaining.length} topology event(s) will not be processed.`);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
const writes = remaining.map((event) => {
|
|
507
|
+
const eventName = event.constructor?.name ?? 'TopologyEvent';
|
|
508
|
+
const eventType = eventName.toLowerCase().includes('opening')
|
|
509
|
+
? 'opening'
|
|
510
|
+
: eventName.toLowerCase().includes('closed')
|
|
511
|
+
? 'closed'
|
|
512
|
+
: 'unknown';
|
|
513
|
+
const message = `Topology event: ${eventName}`;
|
|
514
|
+
return transporter.write({
|
|
515
|
+
level: 'info',
|
|
516
|
+
timestamp: new Date().toISOString(),
|
|
517
|
+
message,
|
|
518
|
+
source: 'AmbitenTopology',
|
|
519
|
+
context: {
|
|
520
|
+
requestId: ctx?.requestId,
|
|
521
|
+
tenantId: ctx?.tenantId,
|
|
522
|
+
collectionName: ctx?.collectionName,
|
|
523
|
+
dbName: ctx?.dbName,
|
|
524
|
+
},
|
|
525
|
+
meta: {
|
|
526
|
+
eventType,
|
|
527
|
+
topologyId: event.topologyId,
|
|
528
|
+
},
|
|
529
|
+
}, message);
|
|
530
|
+
});
|
|
531
|
+
await Promise.all(writes);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
exports.AmbitenClient = AmbitenClient;
|
|
535
|
+
function createAmbitenClientModule(opts) {
|
|
536
|
+
return new AmbitenClient(opts);
|
|
537
|
+
}
|