@vritti/api-sdk 0.0.9 → 0.1.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.
package/dist/index.cjs CHANGED
@@ -222,6 +222,9 @@ var jwt = __toESM(require("jsonwebtoken"), 1);
222
222
 
223
223
  // src/database/services/primary-database.service.ts
224
224
  var import_common3 = require("@nestjs/common");
225
+ var import_pg = require("pg");
226
+ var import_node_postgres = require("drizzle-orm/node-postgres");
227
+ var import_drizzle_orm = require("drizzle-orm");
225
228
 
226
229
  // src/database/constants.ts
227
230
  var DATABASE_MODULE_OPTIONS = Symbol("DATABASE_MODULE_OPTIONS");
@@ -250,8 +253,10 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
250
253
  }
251
254
  options;
252
255
  logger = new import_common3.Logger(_PrimaryDatabaseService.name);
253
- /** Primary database client for querying tenant registry */
254
- primaryDbClient;
256
+ /** PostgreSQL connection pool */
257
+ pool = null;
258
+ /** Drizzle database instance */
259
+ db = null;
255
260
  /** In-memory cache: Map<tenantIdentifier, TenantConfig> */
256
261
  tenantConfigCache = /* @__PURE__ */ new Map();
257
262
  /** Cache TTL in milliseconds */
@@ -262,28 +267,28 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
262
267
  }
263
268
  async onModuleInit() {
264
269
  if (this.options.primaryDb) {
265
- await this.initializePrimaryDbClient();
270
+ await this.initializeDrizzleClient();
266
271
  }
267
272
  }
268
273
  /**
269
- * Initialize connection to primary database
274
+ * Initialize connection to primary database using Drizzle
270
275
  */
271
- async initializePrimaryDbClient() {
276
+ async initializeDrizzleClient() {
272
277
  try {
273
- const PrimaryDbClient = this.options.prismaClientConstructor;
274
278
  const databaseUrl = this.buildPrimaryDbUrl();
275
- this.primaryDbClient = new PrimaryDbClient({
276
- datasources: {
277
- db: {
278
- url: databaseUrl
279
- }
280
- },
281
- log: [
282
- "error",
283
- "warn"
284
- ]
279
+ this.pool = new import_pg.Pool({
280
+ connectionString: databaseUrl,
281
+ max: this.options.maxConnections || 10
285
282
  });
286
- await this.primaryDbClient.$connect();
283
+ this.logger.debug(`Schema keys passed to drizzle: [${Object.keys(this.options.drizzleSchema || {}).join(", ")}]`);
284
+ this.logger.debug(`Relations keys passed to drizzle: [${Object.keys(this.options.drizzleRelations || {}).join(", ")}]`);
285
+ this.db = (0, import_node_postgres.drizzle)({
286
+ client: this.pool,
287
+ schema: this.options.drizzleSchema,
288
+ relations: this.options.drizzleRelations
289
+ });
290
+ this.logger.debug(`Drizzle query keys after init: [${Object.keys(this.db.query || {}).join(", ")}]`);
291
+ await this.pool.query("SELECT 1");
287
292
  this.logger.log("Connected to primary database (tenant registry)");
288
293
  } catch (error) {
289
294
  this.logger.error("Failed to connect to primary database", error);
@@ -298,7 +303,7 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
298
303
  throw new Error("Primary database configuration not provided");
299
304
  }
300
305
  const { host, port = 5432, username, password, database, schema = "public", sslMode = "require" } = this.options.primaryDb;
301
- let url = `postgresql://${username}:${password}@${host}:${port}/${database}`;
306
+ let url = `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;
302
307
  const params = new URLSearchParams();
303
308
  if (schema) {
304
309
  params.set("schema", schema);
@@ -318,9 +323,9 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
318
323
  return url.replace(/:([^@]+)@/, ":****@");
319
324
  }
320
325
  /**
321
- * Get tenant configuration by identifier (ID or slug)
326
+ * Get tenant configuration by identifier (ID or subdomain)
322
327
  *
323
- * @param tenantIdentifier Tenant ID or slug
328
+ * @param tenantIdentifier Tenant ID or subdomain
324
329
  * @returns Tenant configuration or null if not found
325
330
  */
326
331
  async getTenantInfo(tenantIdentifier) {
@@ -330,45 +335,39 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
330
335
  return cached;
331
336
  }
332
337
  try {
333
- if (!this.primaryDbClient) {
338
+ if (!this.db) {
334
339
  throw new Error("Primary database client not initialized");
335
340
  }
336
341
  this.logger.debug(`Querying primary database for tenant: ${tenantIdentifier}`);
337
- const tenant = await this.primaryDbClient.tenant.findFirst({
338
- where: {
339
- OR: [
340
- {
341
- id: tenantIdentifier
342
- },
343
- {
344
- subdomain: tenantIdentifier
345
- }
346
- ],
347
- status: "ACTIVE"
348
- },
349
- include: {
350
- databaseConfig: true
351
- }
352
- });
353
- if (!tenant) {
342
+ const schema = this.options.drizzleSchema;
343
+ const { tenants, tenantDatabaseConfigs } = schema;
344
+ const result = await this.db.select().from(tenants).leftJoin(tenantDatabaseConfigs, (0, import_drizzle_orm.eq)(tenants.id, tenantDatabaseConfigs.tenantId)).where((0, import_drizzle_orm.or)((0, import_drizzle_orm.eq)(tenants.id, tenantIdentifier), (0, import_drizzle_orm.eq)(tenants.subdomain, tenantIdentifier))).limit(1);
345
+ if (!result.length) {
354
346
  this.logger.warn(`Tenant not found: ${tenantIdentifier}`);
355
347
  return null;
356
348
  }
349
+ const row = result[0];
350
+ const tenant = row.tenants;
351
+ const config = row.tenant_database_configs;
352
+ if (tenant.status !== "ACTIVE") {
353
+ this.logger.warn(`Tenant not active: ${tenantIdentifier}`);
354
+ return null;
355
+ }
357
356
  const info = {
358
357
  id: tenant.id,
359
358
  subdomain: tenant.subdomain,
360
359
  type: tenant.dbType,
361
360
  status: tenant.status,
362
361
  // For SHARED tenants: schema name
363
- schemaName: tenant.databaseConfig?.dbSchema || void 0,
362
+ schemaName: config?.dbSchema || void 0,
364
363
  // For DEDICATED tenants: database configuration from TenantDatabaseConfig table
365
- databaseName: tenant.databaseConfig?.dbName || void 0,
366
- databaseHost: tenant.databaseConfig?.dbHost || void 0,
367
- databasePort: tenant.databaseConfig?.dbPort || void 0,
368
- databaseUsername: tenant.databaseConfig?.dbUsername ? this.decrypt(tenant.databaseConfig.dbUsername) : void 0,
369
- databasePassword: tenant.databaseConfig?.dbPassword ? this.decrypt(tenant.databaseConfig.dbPassword) : void 0,
370
- databaseSslMode: tenant.databaseConfig?.dbSslMode || void 0,
371
- connectionPoolSize: tenant.databaseConfig?.connectionPoolSize || void 0
364
+ databaseName: config?.dbName || void 0,
365
+ databaseHost: config?.dbHost || void 0,
366
+ databasePort: config?.dbPort || void 0,
367
+ databaseUsername: config?.dbUsername ? this.decrypt(config.dbUsername) : void 0,
368
+ databasePassword: config?.dbPassword ? this.decrypt(config.dbPassword) : void 0,
369
+ databaseSslMode: config?.dbSslMode || void 0,
370
+ connectionPoolSize: config?.connectionPoolSize || void 0
372
371
  };
373
372
  this.cacheInfo(info);
374
373
  return info;
@@ -394,7 +393,7 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
394
393
  *
395
394
  * Useful when tenant settings are updated and cache needs to be invalidated
396
395
  *
397
- * @param tenantIdentifier Tenant ID or slug
396
+ * @param tenantIdentifier Tenant ID or subdomain
398
397
  */
399
398
  clearTenantCache(tenantIdentifier) {
400
399
  const config = this.tenantConfigCache.get(tenantIdentifier);
@@ -413,17 +412,23 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
413
412
  this.logger.log(`Cleared ${size} cached tenant configs`);
414
413
  }
415
414
  /**
416
- * Get the Prisma client for the primary database.
417
- * This is a synchronous property that returns the initialized Prisma client.
415
+ * Get the Drizzle database instance for the primary database.
416
+ * This is a synchronous property that returns the initialized Drizzle client.
418
417
  *
419
- * @returns Primary database client instance
418
+ * @returns Primary database Drizzle instance
420
419
  * @throws Error if primary database client is not initialized
421
420
  */
422
- get prismaClient() {
423
- if (!this.primaryDbClient) {
421
+ get drizzleClient() {
422
+ if (!this.db) {
424
423
  throw new Error("Primary database client not initialized");
425
424
  }
426
- return this.primaryDbClient;
425
+ return this.db;
426
+ }
427
+ /**
428
+ * Get the Drizzle schema
429
+ */
430
+ get schema() {
431
+ return this.options.drizzleSchema;
427
432
  }
428
433
  /**
429
434
  * Decrypt database credentials
@@ -437,8 +442,8 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
437
442
  return encrypted;
438
443
  }
439
444
  async onModuleDestroy() {
440
- if (this.primaryDbClient) {
441
- await this.primaryDbClient.$disconnect();
445
+ if (this.pool) {
446
+ await this.pool.end();
442
447
  this.logger.log("Disconnected from primary database");
443
448
  }
444
449
  }
@@ -983,6 +988,8 @@ TenantContextInterceptor = _ts_decorate8([
983
988
 
984
989
  // src/database/services/tenant-database.service.ts
985
990
  var import_common9 = require("@nestjs/common");
991
+ var import_pg2 = require("pg");
992
+ var import_node_postgres2 = require("drizzle-orm/node-postgres");
986
993
  function _ts_decorate9(decorators, target, key, desc) {
987
994
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
988
995
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1007,7 +1014,7 @@ var TenantDatabaseService = class _TenantDatabaseService {
1007
1014
  options;
1008
1015
  tenantContext;
1009
1016
  logger = new import_common9.Logger(_TenantDatabaseService.name);
1010
- /** Connection pool: Map<cacheKey, DbClient> */
1017
+ /** Connection pool: Map<cacheKey, TenantConnection> */
1011
1018
  clients = /* @__PURE__ */ new Map();
1012
1019
  /** Track last usage time for idle connection cleanup */
1013
1020
  clientLastUsed = /* @__PURE__ */ new Map();
@@ -1019,17 +1026,23 @@ var TenantDatabaseService = class _TenantDatabaseService {
1019
1026
  this.startConnectionCleaner();
1020
1027
  }
1021
1028
  /**
1022
- * Get the Prisma client for the current tenant's database.
1029
+ * Get the Drizzle client for the current tenant's database.
1023
1030
  * This returns the tenant-scoped database client.
1024
1031
  *
1025
- * @returns Tenant-scoped database client instance
1032
+ * @returns Tenant-scoped Drizzle database instance
1026
1033
  * @throws UnauthorizedException if tenant context not set
1027
1034
  * @throws InternalServerErrorException if connection fails
1028
1035
  */
1029
- get prismaClient() {
1036
+ get drizzleClient() {
1030
1037
  return this.getDbClient();
1031
1038
  }
1032
1039
  /**
1040
+ * Get the Drizzle schema
1041
+ */
1042
+ get schema() {
1043
+ return this.options.drizzleSchema;
1044
+ }
1045
+ /**
1033
1046
  * Get tenant-scoped database client for the current request/message
1034
1047
  *
1035
1048
  * This method:
@@ -1037,66 +1050,61 @@ var TenantDatabaseService = class _TenantDatabaseService {
1037
1050
  * 2. Builds a connection URL based on tenant type
1038
1051
  * 3. Returns cached client if exists, otherwise creates new one
1039
1052
  *
1040
- * @returns Promise<Database client instance>
1053
+ * @returns Drizzle database instance
1041
1054
  * @throws UnauthorizedException if tenant context not set
1042
1055
  * @throws InternalServerErrorException if connection fails
1043
- *
1044
- * @example
1045
- * const dbClient = await tenantDatabase.getDbClient<PrismaClient>();
1046
- * const users = await dbClient.user.findMany();
1047
1056
  */
1048
- async getDbClient() {
1057
+ getDbClient() {
1049
1058
  const tenant = this.tenantContext.getTenant();
1050
1059
  const cacheKey = this.buildCacheKey(tenant);
1051
- if (this.clients.has(cacheKey)) {
1060
+ const existing = this.clients.get(cacheKey);
1061
+ if (existing) {
1052
1062
  this.clientLastUsed.set(cacheKey, Date.now());
1053
1063
  this.logger.debug(`Reusing cached connection: ${cacheKey}`);
1054
- return this.clients.get(cacheKey);
1064
+ return existing.db;
1055
1065
  }
1056
1066
  this.logger.log(`Creating new database connection: ${cacheKey}`);
1057
- const client = await this.createDbClient(tenant);
1058
- this.clients.set(cacheKey, client);
1067
+ const connection = this.createDbClientSync(tenant);
1068
+ this.clients.set(cacheKey, connection);
1059
1069
  this.clientLastUsed.set(cacheKey, Date.now());
1060
- return client;
1070
+ return connection.db;
1061
1071
  }
1062
1072
  /**
1063
- * Create a new database client for the given tenant
1073
+ * Create a new database client for the given tenant (synchronous)
1064
1074
  */
1065
- async createDbClient(tenant) {
1075
+ createDbClientSync(tenant) {
1066
1076
  try {
1067
1077
  const databaseUrl = this.buildTenantDbUrl(tenant);
1068
- const PrismaClient = await this.options.prismaClientConstructor;
1069
- const client = new PrismaClient({
1070
- datasources: {
1071
- db: {
1072
- url: databaseUrl
1073
- }
1074
- },
1075
- log: [
1076
- "error",
1077
- "warn"
1078
- ]
1078
+ const pool = new import_pg2.Pool({
1079
+ connectionString: databaseUrl,
1080
+ max: tenant.connectionPoolSize || this.options.maxConnections || 10
1081
+ });
1082
+ const db = (0, import_node_postgres2.drizzle)({
1083
+ client: pool,
1084
+ schema: this.options.drizzleSchema
1079
1085
  });
1080
- await client.$connect();
1081
1086
  this.logger.log(`Connected to database for tenant: ${tenant.subdomain}`);
1082
- return client;
1087
+ return {
1088
+ pool,
1089
+ db
1090
+ };
1083
1091
  } catch (error) {
1084
1092
  this.logger.error(`Failed to create database connection for tenant: ${tenant.subdomain}`, error);
1085
1093
  throw new import_common9.InternalServerErrorException("Failed to connect to tenant database");
1086
1094
  }
1087
1095
  }
1088
1096
  /**
1089
- * Build connection URL for enterprise tenant (dedicated database)
1097
+ * Build connection URL for tenant (dedicated database)
1090
1098
  */
1091
1099
  buildTenantDbUrl(tenant) {
1092
1100
  const { databaseHost, databasePort, databaseName, databaseUsername, databasePassword, databaseSslMode } = tenant;
1093
1101
  if (!databaseHost || !databaseName || !databaseUsername) {
1094
- throw new Error(`Enterprise tenant ${tenant.subdomain} missing database configuration`);
1102
+ throw new Error(`Tenant ${tenant.subdomain} missing database configuration`);
1095
1103
  }
1096
1104
  const port = databasePort || 5432;
1097
1105
  const sslMode = databaseSslMode || "require";
1098
- const connectionUrl = `postgresql://${databaseUsername}:${databasePassword}@${databaseHost}:${port}/${databaseName}?sslmode=${sslMode}`;
1099
- this.logger.debug(`Enterprise connection URL: ${this.maskPassword(connectionUrl)}`);
1106
+ const connectionUrl = `postgresql://${databaseUsername}:${encodeURIComponent(databasePassword || "")}@${databaseHost}:${port}/${databaseName}?sslmode=${sslMode}`;
1107
+ this.logger.debug(`Tenant connection URL: ${this.maskPassword(connectionUrl)}`);
1100
1108
  return connectionUrl;
1101
1109
  }
1102
1110
  /**
@@ -1118,19 +1126,20 @@ var TenantDatabaseService = class _TenantDatabaseService {
1118
1126
  /**
1119
1127
  * Clean up idle connections that haven't been used recently
1120
1128
  */
1121
- cleanupIdleConnections() {
1129
+ async cleanupIdleConnections() {
1122
1130
  const now = Date.now();
1123
1131
  const maxIdle = this.options.connectionCacheTTL || 3e5;
1124
1132
  let cleaned = 0;
1125
1133
  for (const [key, lastUsed] of this.clientLastUsed.entries()) {
1126
1134
  if (now - lastUsed > maxIdle) {
1127
- const client = this.clients.get(key);
1128
- if (client) {
1129
- client.$disconnect().then(() => {
1135
+ const connection = this.clients.get(key);
1136
+ if (connection) {
1137
+ try {
1138
+ await connection.pool.end();
1130
1139
  this.logger.debug(`Cleaned up idle connection: ${key}`);
1131
- }).catch((error) => {
1140
+ } catch (error) {
1132
1141
  this.logger.error(`Error disconnecting idle client: ${key}`, error);
1133
- });
1142
+ }
1134
1143
  this.clients.delete(key);
1135
1144
  this.clientLastUsed.delete(key);
1136
1145
  cleaned++;
@@ -1161,9 +1170,9 @@ var TenantDatabaseService = class _TenantDatabaseService {
1161
1170
  clearInterval(this.cleanupInterval);
1162
1171
  }
1163
1172
  this.logger.log(`Disconnecting ${this.clients.size} database connections`);
1164
- const disconnectPromises = Array.from(this.clients.entries()).map(async ([key, client]) => {
1173
+ const disconnectPromises = Array.from(this.clients.entries()).map(async ([key, connection]) => {
1165
1174
  try {
1166
- await client.$disconnect();
1175
+ await connection.pool.end();
1167
1176
  this.logger.debug(`Disconnected: ${key}`);
1168
1177
  } catch (error) {
1169
1178
  this.logger.error(`Error disconnecting client: ${key}`, error);
@@ -1299,57 +1308,73 @@ DatabaseModule = _ts_decorate10([
1299
1308
 
1300
1309
  // src/database/repositories/primary-base.repository.ts
1301
1310
  var import_common11 = require("@nestjs/common");
1311
+ var import_drizzle_orm2 = require("drizzle-orm");
1302
1312
  var PrimaryBaseRepository = class {
1303
1313
  static {
1304
1314
  __name(this, "PrimaryBaseRepository");
1305
1315
  }
1306
1316
  database;
1317
+ table;
1307
1318
  logger;
1308
- modelGetter;
1309
1319
  /**
1310
- * Lazy getter for Prisma client.
1320
+ * The table name extracted from the Drizzle table at runtime.
1321
+ * Used to access the query API for this repository's table.
1322
+ */
1323
+ tableName;
1324
+ /**
1325
+ * Lazy getter for Drizzle client.
1311
1326
  * Accesses the client from the database service only when needed,
1312
1327
  * avoiding initialization timing issues with NestJS lifecycle.
1313
1328
  */
1314
- get prisma() {
1315
- return this.database.prismaClient;
1329
+ get db() {
1330
+ return this.database.drizzleClient;
1316
1331
  }
1317
1332
  /**
1318
- * Lazy getter for the Prisma model delegate.
1319
- * Returns the specific model (e.g., prisma.user, prisma.tenant) for this repository.
1333
+ * Model query API for THIS repository's table (Drizzle v2 relational queries)
1334
+ * Scoped to only the table this repository manages.
1335
+ * Returns a type-safe wrapper around Drizzle's RelationalQueryBuilder.
1336
+ *
1337
+ * @example
1338
+ * ```typescript
1339
+ * // Use relational queries with v2 object-based where syntax
1340
+ * const user = await this.model.findFirst({
1341
+ * where: { id },
1342
+ * with: { posts: true, profile: true }
1343
+ * });
1344
+ * ```
1320
1345
  */
1321
1346
  get model() {
1322
- return this.modelGetter(this.prisma);
1347
+ const query = this.database.drizzleClient.query;
1348
+ const queryKeys = Object.keys(query || {});
1349
+ this.logger.debug(`Looking for '${this.tableName}' in query keys: [${queryKeys.join(", ")}]`);
1350
+ const model = query[this.tableName];
1351
+ if (!model) {
1352
+ this.logger.error(`Table '${this.tableName}' not found in query object. Available: [${queryKeys.join(", ")}]`);
1353
+ }
1354
+ return model;
1323
1355
  }
1324
1356
  /**
1325
1357
  * Create a new repository instance
1326
1358
  *
1327
1359
  * @param database - The primary database service
1328
- * @param getModel - Function that returns the Prisma model delegate from the client
1360
+ * @param table - The Drizzle table schema object
1329
1361
  *
1330
1362
  * @example
1331
1363
  * ```typescript
1332
- * // Standard usage with full parameter name
1333
- * constructor(database: PrimaryDatabaseService) {
1334
- * super(database, (prisma) => prisma.user);
1335
- * }
1364
+ * import { users } from '@/db/schema';
1336
1365
  *
1337
- * // Short syntax
1338
1366
  * constructor(database: PrimaryDatabaseService) {
1339
- * super(database, (p) => p.user);
1340
- * }
1341
- *
1342
- * // Complex model names
1343
- * constructor(database: PrimaryDatabaseService) {
1344
- * super(database, (p) => p.emailVerification);
1367
+ * super(database, users);
1345
1368
  * }
1346
1369
  * ```
1347
1370
  */
1348
- constructor(database, getModel) {
1371
+ constructor(database, table) {
1349
1372
  this.database = database;
1373
+ this.table = table;
1374
+ this.tableName = (0, import_drizzle_orm2.getTableName)(table);
1350
1375
  this.logger = new import_common11.Logger(this.constructor.name);
1351
- this.modelGetter = getModel;
1352
1376
  this.logger.debug(`Initialized ${this.constructor.name}`);
1377
+ this.logger.debug(`Table name from getTableName: '${this.tableName}'`);
1353
1378
  }
1354
1379
  /**
1355
1380
  * Create a new record
@@ -1361,21 +1386,20 @@ var PrimaryBaseRepository = class {
1361
1386
  * ```typescript
1362
1387
  * const user = await userRepository.create({
1363
1388
  * email: 'user@example.com',
1364
- * name: 'John Doe'
1389
+ * firstName: 'John'
1365
1390
  * });
1366
1391
  * ```
1367
1392
  */
1368
1393
  async create(data) {
1369
1394
  this.logger.log("Creating record");
1370
- return await this.model.create({
1371
- data
1372
- });
1395
+ const results = await this.db.insert(this.table).values(data).returning();
1396
+ return results[0];
1373
1397
  }
1374
1398
  /**
1375
1399
  * Find a single record by ID
1376
1400
  *
1377
1401
  * @param id - The record ID
1378
- * @returns Promise resolving to the record or null if not found
1402
+ * @returns Promise resolving to the record or undefined if not found
1379
1403
  *
1380
1404
  * @example
1381
1405
  * ```typescript
@@ -1384,40 +1408,43 @@ var PrimaryBaseRepository = class {
1384
1408
  */
1385
1409
  async findById(id) {
1386
1410
  this.logger.debug(`Finding record by ID: ${id}`);
1387
- return await this.model.findUnique({
1411
+ return this.model.findFirst({
1388
1412
  where: {
1389
1413
  id
1390
1414
  }
1391
1415
  });
1392
1416
  }
1393
1417
  /**
1394
- * Find a single record with custom where clause
1418
+ * Find a single record with custom where clause (Drizzle v2 object-based syntax)
1395
1419
  *
1396
- * @param where - The where clause or findUnique args
1397
- * @returns Promise resolving to the record or null if not found
1420
+ * @param where - Object-based filter condition
1421
+ * @returns Promise resolving to the record or undefined if not found
1398
1422
  *
1399
1423
  * @example
1400
1424
  * ```typescript
1401
- * // Simple where clause
1425
+ * // Simple equality
1402
1426
  * const user = await userRepository.findOne({ email: 'user@example.com' });
1403
1427
  *
1404
- * // With include
1428
+ * // With operators
1429
+ * const user = await userRepository.findOne({ age: { gte: 18 } });
1430
+ *
1431
+ * // Multiple conditions (AND)
1405
1432
  * const user = await userRepository.findOne({
1406
- * where: { email: 'user@example.com' },
1407
- * include: { posts: true }
1433
+ * email: 'user@example.com',
1434
+ * status: 'ACTIVE'
1408
1435
  * });
1409
1436
  * ```
1410
1437
  */
1411
1438
  async findOne(where) {
1412
1439
  this.logger.debug("Finding record with custom query");
1413
- return await this.model.findUnique(typeof where === "object" && "where" in where ? where : {
1440
+ return this.model.findFirst({
1414
1441
  where
1415
1442
  });
1416
1443
  }
1417
1444
  /**
1418
- * Find multiple records
1445
+ * Find multiple records (Drizzle v2 object-based syntax)
1419
1446
  *
1420
- * @param args - Prisma findMany arguments (where, orderBy, take, skip, etc.)
1447
+ * @param options - Query options (where, orderBy, limit, offset)
1421
1448
  * @returns Promise resolving to an array of records
1422
1449
  *
1423
1450
  * @example
@@ -1425,18 +1452,28 @@ var PrimaryBaseRepository = class {
1425
1452
  * // Find all users
1426
1453
  * const users = await userRepository.findMany();
1427
1454
  *
1428
- * // Find with filtering and pagination
1455
+ * // Find with filtering and pagination (v2 object syntax)
1429
1456
  * const users = await userRepository.findMany({
1430
- * where: { status: 'ACTIVE' },
1457
+ * where: { accountStatus: 'ACTIVE' },
1431
1458
  * orderBy: { createdAt: 'desc' },
1432
- * take: 10,
1433
- * skip: 0
1459
+ * limit: 10,
1460
+ * offset: 0
1461
+ * });
1462
+ *
1463
+ * // Multiple conditions
1464
+ * const users = await userRepository.findMany({
1465
+ * where: {
1466
+ * AND: [
1467
+ * { status: 'ACTIVE' },
1468
+ * { age: { gte: 18 } }
1469
+ * ]
1470
+ * }
1434
1471
  * });
1435
1472
  * ```
1436
1473
  */
1437
- async findMany(args) {
1474
+ async findMany(options) {
1438
1475
  this.logger.debug("Finding multiple records");
1439
- return await this.model.findMany(args);
1476
+ return this.model.findMany(options);
1440
1477
  }
1441
1478
  /**
1442
1479
  * Update a record by ID
@@ -1448,41 +1485,40 @@ var PrimaryBaseRepository = class {
1448
1485
  * @example
1449
1486
  * ```typescript
1450
1487
  * const user = await userRepository.update('user-id-123', {
1451
- * name: 'Jane Doe'
1488
+ * firstName: 'Jane'
1452
1489
  * });
1453
1490
  * ```
1454
1491
  */
1455
1492
  async update(id, data) {
1456
1493
  this.logger.log(`Updating record with ID: ${id}`);
1457
- return await this.model.update({
1458
- where: {
1459
- id
1460
- },
1461
- data
1462
- });
1494
+ const idColumn = this.table.id;
1495
+ const results = await this.db.update(this.table).set(data).where((0, import_drizzle_orm2.eq)(idColumn, id)).returning();
1496
+ return results[0];
1463
1497
  }
1464
1498
  /**
1465
1499
  * Update multiple records
1466
1500
  *
1467
- * @param where - The where clause to match records
1501
+ * @param where - SQL condition to match records
1468
1502
  * @param data - The data to update
1469
1503
  * @returns Promise resolving to the count of updated records
1470
1504
  *
1471
1505
  * @example
1472
1506
  * ```typescript
1507
+ * import { eq } from 'drizzle-orm';
1508
+ *
1473
1509
  * const result = await userRepository.updateMany(
1474
- * { status: 'PENDING' },
1475
- * { status: 'ACTIVE' }
1510
+ * eq(users.accountStatus, 'PENDING'),
1511
+ * { accountStatus: 'ACTIVE' }
1476
1512
  * );
1477
1513
  * console.log(`Updated ${result.count} users`);
1478
1514
  * ```
1479
1515
  */
1480
1516
  async updateMany(where, data) {
1481
1517
  this.logger.log("Updating multiple records");
1482
- return await this.model.updateMany({
1483
- where,
1484
- data
1485
- });
1518
+ const result = await this.db.update(this.table).set(data).where(where);
1519
+ return {
1520
+ count: result.rowCount ?? 0
1521
+ };
1486
1522
  }
1487
1523
  /**
1488
1524
  * Delete a record by ID
@@ -1497,127 +1533,143 @@ var PrimaryBaseRepository = class {
1497
1533
  */
1498
1534
  async delete(id) {
1499
1535
  this.logger.log(`Deleting record with ID: ${id}`);
1500
- return await this.model.delete({
1501
- where: {
1502
- id
1503
- }
1504
- });
1536
+ const idColumn = this.table.id;
1537
+ const results = await this.db.delete(this.table).where((0, import_drizzle_orm2.eq)(idColumn, id)).returning();
1538
+ return results[0];
1505
1539
  }
1506
1540
  /**
1507
1541
  * Delete multiple records
1508
1542
  *
1509
- * @param where - The where clause to match records
1543
+ * @param where - SQL condition to match records
1510
1544
  * @returns Promise resolving to the count of deleted records
1511
1545
  *
1512
1546
  * @example
1513
1547
  * ```typescript
1514
- * const result = await userRepository.deleteMany({
1515
- * status: 'INACTIVE',
1516
- * createdAt: { lt: new Date('2020-01-01') }
1517
- * });
1548
+ * import { lt } from 'drizzle-orm';
1549
+ *
1550
+ * const result = await userRepository.deleteMany(
1551
+ * lt(users.createdAt, new Date('2020-01-01'))
1552
+ * );
1518
1553
  * console.log(`Deleted ${result.count} users`);
1519
1554
  * ```
1520
1555
  */
1521
1556
  async deleteMany(where) {
1522
1557
  this.logger.log("Deleting multiple records");
1523
- return await this.model.deleteMany({
1524
- where
1525
- });
1558
+ const result = await this.db.delete(this.table).where(where);
1559
+ return {
1560
+ count: result.rowCount ?? 0
1561
+ };
1526
1562
  }
1527
1563
  /**
1528
1564
  * Count records
1529
1565
  *
1530
- * @param where - Optional where clause to filter records
1566
+ * @param where - Optional SQL condition to filter records
1531
1567
  * @returns Promise resolving to the count of records
1532
1568
  *
1533
1569
  * @example
1534
1570
  * ```typescript
1571
+ * import { eq } from 'drizzle-orm';
1572
+ *
1535
1573
  * // Count all users
1536
1574
  * const total = await userRepository.count();
1537
1575
  *
1538
1576
  * // Count active users
1539
- * const activeCount = await userRepository.count({ status: 'ACTIVE' });
1577
+ * const activeCount = await userRepository.count(
1578
+ * eq(users.accountStatus, 'ACTIVE')
1579
+ * );
1540
1580
  * ```
1541
1581
  */
1542
1582
  async count(where) {
1543
1583
  this.logger.debug("Counting records");
1544
- return await this.model.count({
1545
- where
1546
- });
1584
+ let query = this.db.select({
1585
+ count: import_drizzle_orm2.sql`count(*)::int`
1586
+ }).from(this.table).$dynamic();
1587
+ if (where) {
1588
+ query = query.where(where);
1589
+ }
1590
+ const results = await query;
1591
+ return results[0].count;
1547
1592
  }
1548
1593
  /**
1549
1594
  * Check if a record exists
1550
1595
  *
1551
- * @param where - The where clause to match records
1596
+ * @param where - SQL condition to match records
1552
1597
  * @returns Promise resolving to true if at least one record exists, false otherwise
1553
1598
  *
1554
1599
  * @example
1555
1600
  * ```typescript
1556
- * const emailExists = await userRepository.exists({
1557
- * email: 'user@example.com'
1558
- * });
1601
+ * import { eq } from 'drizzle-orm';
1602
+ *
1603
+ * const emailExists = await userRepository.exists(
1604
+ * eq(users.email, 'user@example.com')
1605
+ * );
1559
1606
  * ```
1560
1607
  */
1561
1608
  async exists(where) {
1562
- const count = await this.model.count({
1563
- where
1564
- });
1609
+ const count = await this.count(where);
1565
1610
  return count > 0;
1566
1611
  }
1567
1612
  };
1568
1613
 
1569
1614
  // src/database/repositories/tenant-base.repository.ts
1570
1615
  var import_common12 = require("@nestjs/common");
1616
+ var import_drizzle_orm3 = require("drizzle-orm");
1571
1617
  var TenantBaseRepository = class {
1572
1618
  static {
1573
1619
  __name(this, "TenantBaseRepository");
1574
1620
  }
1575
1621
  database;
1622
+ table;
1576
1623
  logger;
1577
- modelGetter;
1578
1624
  /**
1579
- * Lazy getter for Prisma client.
1625
+ * The table name extracted from the Drizzle table at runtime.
1626
+ * Used to access the query API for this repository's table.
1627
+ */
1628
+ tableName;
1629
+ /**
1630
+ * Lazy getter for Drizzle client.
1580
1631
  * Accesses the client from the database service only when needed,
1581
1632
  * avoiding initialization timing issues with NestJS lifecycle.
1582
1633
  */
1583
- get prisma() {
1584
- return this.database.prismaClient;
1634
+ get db() {
1635
+ return this.database.drizzleClient;
1585
1636
  }
1586
1637
  /**
1587
- * Lazy getter for the Prisma model delegate.
1588
- * Returns the specific model (e.g., prisma.product, prisma.order) for this repository.
1638
+ * Model query API for THIS repository's table (Prisma-like syntax)
1639
+ * Scoped to only the table this repository manages
1640
+ *
1641
+ * @example
1642
+ * ```typescript
1643
+ * // Use relational queries with type safety
1644
+ * const product = await this.model.findFirst({
1645
+ * where: eq(products.id, id),
1646
+ * with: { category: true, variants: true }
1647
+ * });
1648
+ * ```
1589
1649
  */
1590
1650
  get model() {
1591
- return this.modelGetter(this.prisma);
1651
+ return this.database.drizzleClient.query[this.tableName];
1592
1652
  }
1593
1653
  /**
1594
1654
  * Create a new repository instance
1595
1655
  *
1596
1656
  * @param database - The tenant database service
1597
- * @param getModel - Function that returns the Prisma model delegate from the client
1657
+ * @param table - The Drizzle table schema object
1598
1658
  *
1599
1659
  * @example
1600
1660
  * ```typescript
1601
- * // Standard usage with full parameter name
1602
- * constructor(database: TenantDatabaseService) {
1603
- * super(database, (prisma) => prisma.product);
1604
- * }
1661
+ * import { products } from '@/db/schema';
1605
1662
  *
1606
- * // Short syntax
1607
1663
  * constructor(database: TenantDatabaseService) {
1608
- * super(database, (p) => p.product);
1609
- * }
1610
- *
1611
- * // Complex model names
1612
- * constructor(database: TenantDatabaseService) {
1613
- * super(database, (p) => p.inventoryItem);
1664
+ * super(database, products);
1614
1665
  * }
1615
1666
  * ```
1616
1667
  */
1617
- constructor(database, getModel) {
1668
+ constructor(database, table) {
1618
1669
  this.database = database;
1670
+ this.table = table;
1671
+ this.tableName = (0, import_drizzle_orm3.getTableName)(table);
1619
1672
  this.logger = new import_common12.Logger(this.constructor.name);
1620
- this.modelGetter = getModel;
1621
1673
  this.logger.debug(`Initialized ${this.constructor.name}`);
1622
1674
  }
1623
1675
  /**
@@ -1637,9 +1689,8 @@ var TenantBaseRepository = class {
1637
1689
  */
1638
1690
  async create(data) {
1639
1691
  this.logger.log("Creating record");
1640
- return await this.model.create({
1641
- data
1642
- });
1692
+ const results = await this.db.insert(this.table).values(data).returning();
1693
+ return results[0];
1643
1694
  }
1644
1695
  /**
1645
1696
  * Find a single record by ID
@@ -1654,59 +1705,65 @@ var TenantBaseRepository = class {
1654
1705
  */
1655
1706
  async findById(id) {
1656
1707
  this.logger.debug(`Finding record by ID: ${id}`);
1657
- return await this.model.findUnique({
1658
- where: {
1659
- id
1660
- }
1661
- });
1708
+ const idColumn = this.table.id;
1709
+ const results = await this.db.select().from(this.table).where((0, import_drizzle_orm3.eq)(idColumn, id)).limit(1);
1710
+ return results[0] ?? null;
1662
1711
  }
1663
1712
  /**
1664
1713
  * Find a single record with custom where clause
1665
1714
  *
1666
- * @param where - The where clause or findUnique args
1715
+ * @param where - SQL condition
1667
1716
  * @returns Promise resolving to the record or null if not found
1668
1717
  *
1669
1718
  * @example
1670
1719
  * ```typescript
1671
- * // Simple where clause
1672
- * const product = await productRepository.findOne({ sku: 'WDG-001' });
1673
- *
1674
- * // With include
1675
- * const product = await productRepository.findOne({
1676
- * where: { sku: 'WDG-001' },
1677
- * include: { category: true }
1678
- * });
1720
+ * import { eq } from 'drizzle-orm';
1721
+ * const product = await productRepository.findOne(eq(products.sku, 'WDG-001'));
1679
1722
  * ```
1680
1723
  */
1681
1724
  async findOne(where) {
1682
1725
  this.logger.debug("Finding record with custom query");
1683
- return await this.model.findUnique(typeof where === "object" && "where" in where ? where : {
1684
- where
1685
- });
1726
+ const results = await this.db.select().from(this.table).where(where).limit(1);
1727
+ return results[0] ?? null;
1686
1728
  }
1687
1729
  /**
1688
1730
  * Find multiple records
1689
1731
  *
1690
- * @param args - Prisma findMany arguments (where, orderBy, take, skip, etc.)
1732
+ * @param options - Query options (where, orderBy, limit, offset)
1691
1733
  * @returns Promise resolving to an array of records
1692
1734
  *
1693
1735
  * @example
1694
1736
  * ```typescript
1737
+ * import { eq, desc } from 'drizzle-orm';
1738
+ *
1695
1739
  * // Find all products
1696
1740
  * const products = await productRepository.findMany();
1697
1741
  *
1698
1742
  * // Find with filtering and pagination
1699
1743
  * const products = await productRepository.findMany({
1700
- * where: { status: 'ACTIVE' },
1701
- * orderBy: { createdAt: 'desc' },
1702
- * take: 10,
1703
- * skip: 0
1744
+ * where: eq(products.status, 'ACTIVE'),
1745
+ * orderBy: desc(products.createdAt),
1746
+ * limit: 10,
1747
+ * offset: 0
1704
1748
  * });
1705
1749
  * ```
1706
1750
  */
1707
- async findMany(args) {
1751
+ async findMany(options) {
1708
1752
  this.logger.debug("Finding multiple records");
1709
- return await this.model.findMany(args);
1753
+ let query = this.db.select().from(this.table).$dynamic();
1754
+ if (options?.where) {
1755
+ query = query.where(options.where);
1756
+ }
1757
+ if (options?.orderBy) {
1758
+ query = query.orderBy(options.orderBy);
1759
+ }
1760
+ if (options?.limit) {
1761
+ query = query.limit(options.limit);
1762
+ }
1763
+ if (options?.offset) {
1764
+ query = query.offset(options.offset);
1765
+ }
1766
+ return await query;
1710
1767
  }
1711
1768
  /**
1712
1769
  * Update a record by ID
@@ -1724,24 +1781,23 @@ var TenantBaseRepository = class {
1724
1781
  */
1725
1782
  async update(id, data) {
1726
1783
  this.logger.log(`Updating record with ID: ${id}`);
1727
- return await this.model.update({
1728
- where: {
1729
- id
1730
- },
1731
- data
1732
- });
1784
+ const idColumn = this.table.id;
1785
+ const results = await this.db.update(this.table).set(data).where((0, import_drizzle_orm3.eq)(idColumn, id)).returning();
1786
+ return results[0];
1733
1787
  }
1734
1788
  /**
1735
1789
  * Update multiple records
1736
1790
  *
1737
- * @param where - The where clause to match records
1791
+ * @param where - SQL condition to match records
1738
1792
  * @param data - The data to update
1739
1793
  * @returns Promise resolving to the count of updated records
1740
1794
  *
1741
1795
  * @example
1742
1796
  * ```typescript
1797
+ * import { eq } from 'drizzle-orm';
1798
+ *
1743
1799
  * const result = await productRepository.updateMany(
1744
- * { status: 'PENDING' },
1800
+ * eq(products.status, 'PENDING'),
1745
1801
  * { status: 'ACTIVE' }
1746
1802
  * );
1747
1803
  * console.log(`Updated ${result.count} products`);
@@ -1749,10 +1805,10 @@ var TenantBaseRepository = class {
1749
1805
  */
1750
1806
  async updateMany(where, data) {
1751
1807
  this.logger.log("Updating multiple records");
1752
- return await this.model.updateMany({
1753
- where,
1754
- data
1755
- });
1808
+ const result = await this.db.update(this.table).set(data).where(where);
1809
+ return {
1810
+ count: result.rowCount ?? 0
1811
+ };
1756
1812
  }
1757
1813
  /**
1758
1814
  * Delete a record by ID
@@ -1767,71 +1823,80 @@ var TenantBaseRepository = class {
1767
1823
  */
1768
1824
  async delete(id) {
1769
1825
  this.logger.log(`Deleting record with ID: ${id}`);
1770
- return await this.model.delete({
1771
- where: {
1772
- id
1773
- }
1774
- });
1826
+ const idColumn = this.table.id;
1827
+ const results = await this.db.delete(this.table).where((0, import_drizzle_orm3.eq)(idColumn, id)).returning();
1828
+ return results[0];
1775
1829
  }
1776
1830
  /**
1777
1831
  * Delete multiple records
1778
1832
  *
1779
- * @param where - The where clause to match records
1833
+ * @param where - SQL condition to match records
1780
1834
  * @returns Promise resolving to the count of deleted records
1781
1835
  *
1782
1836
  * @example
1783
1837
  * ```typescript
1784
- * const result = await productRepository.deleteMany({
1785
- * status: 'INACTIVE',
1786
- * createdAt: { lt: new Date('2020-01-01') }
1787
- * });
1838
+ * import { lt } from 'drizzle-orm';
1839
+ *
1840
+ * const result = await productRepository.deleteMany(
1841
+ * lt(products.createdAt, new Date('2020-01-01'))
1842
+ * );
1788
1843
  * console.log(`Deleted ${result.count} products`);
1789
1844
  * ```
1790
1845
  */
1791
1846
  async deleteMany(where) {
1792
1847
  this.logger.log("Deleting multiple records");
1793
- return await this.model.deleteMany({
1794
- where
1795
- });
1848
+ const result = await this.db.delete(this.table).where(where);
1849
+ return {
1850
+ count: result.rowCount ?? 0
1851
+ };
1796
1852
  }
1797
1853
  /**
1798
1854
  * Count records
1799
1855
  *
1800
- * @param where - Optional where clause to filter records
1856
+ * @param where - Optional SQL condition to filter records
1801
1857
  * @returns Promise resolving to the count of records
1802
1858
  *
1803
1859
  * @example
1804
1860
  * ```typescript
1861
+ * import { eq } from 'drizzle-orm';
1862
+ *
1805
1863
  * // Count all products
1806
1864
  * const total = await productRepository.count();
1807
1865
  *
1808
1866
  * // Count active products
1809
- * const activeCount = await productRepository.count({ status: 'ACTIVE' });
1867
+ * const activeCount = await productRepository.count(
1868
+ * eq(products.status, 'ACTIVE')
1869
+ * );
1810
1870
  * ```
1811
1871
  */
1812
1872
  async count(where) {
1813
1873
  this.logger.debug("Counting records");
1814
- return await this.model.count({
1815
- where
1816
- });
1874
+ let query = this.db.select({
1875
+ count: import_drizzle_orm3.sql`count(*)::int`
1876
+ }).from(this.table).$dynamic();
1877
+ if (where) {
1878
+ query = query.where(where);
1879
+ }
1880
+ const results = await query;
1881
+ return results[0].count;
1817
1882
  }
1818
1883
  /**
1819
1884
  * Check if a record exists
1820
1885
  *
1821
- * @param where - The where clause to match records
1886
+ * @param where - SQL condition to match records
1822
1887
  * @returns Promise resolving to true if at least one record exists, false otherwise
1823
1888
  *
1824
1889
  * @example
1825
1890
  * ```typescript
1826
- * const skuExists = await productRepository.exists({
1827
- * sku: 'WDG-001'
1828
- * });
1891
+ * import { eq } from 'drizzle-orm';
1892
+ *
1893
+ * const skuExists = await productRepository.exists(
1894
+ * eq(products.sku, 'WDG-001')
1895
+ * );
1829
1896
  * ```
1830
1897
  */
1831
1898
  async exists(where) {
1832
- const count = await this.model.count({
1833
- where
1834
- });
1899
+ const count = await this.count(where);
1835
1900
  return count > 0;
1836
1901
  }
1837
1902
  };
@@ -1988,15 +2053,16 @@ var HttpExceptionFilter = class _HttpExceptionFilter {
1988
2053
  const exceptionResponse = exception.getResponse();
1989
2054
  if (typeof exceptionResponse === "object" && exceptionResponse !== null) {
1990
2055
  const responseObj = exceptionResponse;
1991
- if (responseObj.errors && Array.isArray(responseObj.errors)) {
2056
+ if ("errors" in responseObj && Array.isArray(responseObj.errors)) {
1992
2057
  errors = responseObj.errors;
1993
- detail = responseObj.detail || exception.message;
1994
- } else if (responseObj.message && Array.isArray(responseObj.message)) {
2058
+ detail = ("detail" in responseObj ? responseObj.detail : void 0) || exception.message;
2059
+ } else if ("message" in responseObj && Array.isArray(responseObj.message)) {
1995
2060
  errors = responseObj.message.map((msg) => {
1996
- if (typeof msg === "object" && msg.property && msg.constraints) {
2061
+ if (typeof msg === "object" && "property" in msg && "constraints" in msg) {
2062
+ const constraintValues = Object.values(msg.constraints);
1997
2063
  return {
1998
2064
  field: msg.property,
1999
- message: Object.values(msg.constraints)[0]
2065
+ message: constraintValues[0] ?? "Validation failed"
2000
2066
  };
2001
2067
  }
2002
2068
  return {
@@ -2004,13 +2070,14 @@ var HttpExceptionFilter = class _HttpExceptionFilter {
2004
2070
  };
2005
2071
  });
2006
2072
  detail = "Validation failed";
2007
- } else if (responseObj.message) {
2073
+ } else if ("message" in responseObj) {
2074
+ const message = responseObj.message;
2008
2075
  errors = [
2009
2076
  {
2010
- message: Array.isArray(responseObj.message) ? responseObj.message.join(", ") : responseObj.message
2077
+ message: Array.isArray(message) ? message.join(", ") : message
2011
2078
  }
2012
2079
  ];
2013
- detail = responseObj.error || exception.message;
2080
+ detail = ("error" in responseObj ? responseObj.error : void 0) || exception.message;
2014
2081
  }
2015
2082
  } else if (typeof exceptionResponse === "string") {
2016
2083
  errors = [
@@ -3058,14 +3125,15 @@ function getLevelsUpTo(level) {
3058
3125
  "debug",
3059
3126
  "verbose"
3060
3127
  ];
3061
- const levelIndex = allLevels.indexOf(level);
3062
- if (levelIndex === -1) {
3128
+ const isValidLevel = /* @__PURE__ */ __name((l) => allLevels.includes(l), "isValidLevel");
3129
+ if (!isValidLevel(level)) {
3063
3130
  return [
3064
3131
  "error",
3065
3132
  "warn",
3066
3133
  "log"
3067
3134
  ];
3068
3135
  }
3136
+ const levelIndex = allLevels.indexOf(level);
3069
3137
  return allLevels.slice(0, levelIndex + 1);
3070
3138
  }
3071
3139
  __name(getLevelsUpTo, "getLevelsUpTo");