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