@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.js CHANGED
@@ -143,6 +143,9 @@ import * as jwt from "jsonwebtoken";
143
143
 
144
144
  // src/database/services/primary-database.service.ts
145
145
  import { Inject as Inject2, Injectable as Injectable2, InternalServerErrorException, Logger } from "@nestjs/common";
146
+ import { Pool } from "pg";
147
+ import { drizzle } from "drizzle-orm/node-postgres";
148
+ import { eq, or } from "drizzle-orm";
146
149
 
147
150
  // src/database/constants.ts
148
151
  var DATABASE_MODULE_OPTIONS = Symbol("DATABASE_MODULE_OPTIONS");
@@ -171,8 +174,10 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
171
174
  }
172
175
  options;
173
176
  logger = new Logger(_PrimaryDatabaseService.name);
174
- /** Primary database client for querying tenant registry */
175
- primaryDbClient;
177
+ /** PostgreSQL connection pool */
178
+ pool = null;
179
+ /** Drizzle database instance */
180
+ db = null;
176
181
  /** In-memory cache: Map<tenantIdentifier, TenantConfig> */
177
182
  tenantConfigCache = /* @__PURE__ */ new Map();
178
183
  /** Cache TTL in milliseconds */
@@ -183,28 +188,28 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
183
188
  }
184
189
  async onModuleInit() {
185
190
  if (this.options.primaryDb) {
186
- await this.initializePrimaryDbClient();
191
+ await this.initializeDrizzleClient();
187
192
  }
188
193
  }
189
194
  /**
190
- * Initialize connection to primary database
195
+ * Initialize connection to primary database using Drizzle
191
196
  */
192
- async initializePrimaryDbClient() {
197
+ async initializeDrizzleClient() {
193
198
  try {
194
- const PrimaryDbClient = this.options.prismaClientConstructor;
195
199
  const databaseUrl = this.buildPrimaryDbUrl();
196
- this.primaryDbClient = new PrimaryDbClient({
197
- datasources: {
198
- db: {
199
- url: databaseUrl
200
- }
201
- },
202
- log: [
203
- "error",
204
- "warn"
205
- ]
200
+ this.pool = new Pool({
201
+ connectionString: databaseUrl,
202
+ max: this.options.maxConnections || 10
206
203
  });
207
- await this.primaryDbClient.$connect();
204
+ this.logger.debug(`Schema keys passed to drizzle: [${Object.keys(this.options.drizzleSchema || {}).join(", ")}]`);
205
+ this.logger.debug(`Relations keys passed to drizzle: [${Object.keys(this.options.drizzleRelations || {}).join(", ")}]`);
206
+ this.db = drizzle({
207
+ client: this.pool,
208
+ schema: this.options.drizzleSchema,
209
+ relations: this.options.drizzleRelations
210
+ });
211
+ this.logger.debug(`Drizzle query keys after init: [${Object.keys(this.db.query || {}).join(", ")}]`);
212
+ await this.pool.query("SELECT 1");
208
213
  this.logger.log("Connected to primary database (tenant registry)");
209
214
  } catch (error) {
210
215
  this.logger.error("Failed to connect to primary database", error);
@@ -219,7 +224,7 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
219
224
  throw new Error("Primary database configuration not provided");
220
225
  }
221
226
  const { host, port = 5432, username, password, database, schema = "public", sslMode = "require" } = this.options.primaryDb;
222
- let url = `postgresql://${username}:${password}@${host}:${port}/${database}`;
227
+ let url = `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;
223
228
  const params = new URLSearchParams();
224
229
  if (schema) {
225
230
  params.set("schema", schema);
@@ -239,9 +244,9 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
239
244
  return url.replace(/:([^@]+)@/, ":****@");
240
245
  }
241
246
  /**
242
- * Get tenant configuration by identifier (ID or slug)
247
+ * Get tenant configuration by identifier (ID or subdomain)
243
248
  *
244
- * @param tenantIdentifier Tenant ID or slug
249
+ * @param tenantIdentifier Tenant ID or subdomain
245
250
  * @returns Tenant configuration or null if not found
246
251
  */
247
252
  async getTenantInfo(tenantIdentifier) {
@@ -251,45 +256,39 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
251
256
  return cached;
252
257
  }
253
258
  try {
254
- if (!this.primaryDbClient) {
259
+ if (!this.db) {
255
260
  throw new Error("Primary database client not initialized");
256
261
  }
257
262
  this.logger.debug(`Querying primary database for tenant: ${tenantIdentifier}`);
258
- const tenant = await this.primaryDbClient.tenant.findFirst({
259
- where: {
260
- OR: [
261
- {
262
- id: tenantIdentifier
263
- },
264
- {
265
- subdomain: tenantIdentifier
266
- }
267
- ],
268
- status: "ACTIVE"
269
- },
270
- include: {
271
- databaseConfig: true
272
- }
273
- });
274
- if (!tenant) {
263
+ const schema = this.options.drizzleSchema;
264
+ const { tenants, tenantDatabaseConfigs } = schema;
265
+ const result = await this.db.select().from(tenants).leftJoin(tenantDatabaseConfigs, eq(tenants.id, tenantDatabaseConfigs.tenantId)).where(or(eq(tenants.id, tenantIdentifier), eq(tenants.subdomain, tenantIdentifier))).limit(1);
266
+ if (!result.length) {
275
267
  this.logger.warn(`Tenant not found: ${tenantIdentifier}`);
276
268
  return null;
277
269
  }
270
+ const row = result[0];
271
+ const tenant = row.tenants;
272
+ const config = row.tenant_database_configs;
273
+ if (tenant.status !== "ACTIVE") {
274
+ this.logger.warn(`Tenant not active: ${tenantIdentifier}`);
275
+ return null;
276
+ }
278
277
  const info = {
279
278
  id: tenant.id,
280
279
  subdomain: tenant.subdomain,
281
280
  type: tenant.dbType,
282
281
  status: tenant.status,
283
282
  // For SHARED tenants: schema name
284
- schemaName: tenant.databaseConfig?.dbSchema || void 0,
283
+ schemaName: config?.dbSchema || void 0,
285
284
  // For DEDICATED tenants: database configuration from TenantDatabaseConfig table
286
- databaseName: tenant.databaseConfig?.dbName || void 0,
287
- databaseHost: tenant.databaseConfig?.dbHost || void 0,
288
- databasePort: tenant.databaseConfig?.dbPort || void 0,
289
- databaseUsername: tenant.databaseConfig?.dbUsername ? this.decrypt(tenant.databaseConfig.dbUsername) : void 0,
290
- databasePassword: tenant.databaseConfig?.dbPassword ? this.decrypt(tenant.databaseConfig.dbPassword) : void 0,
291
- databaseSslMode: tenant.databaseConfig?.dbSslMode || void 0,
292
- connectionPoolSize: tenant.databaseConfig?.connectionPoolSize || void 0
285
+ databaseName: config?.dbName || void 0,
286
+ databaseHost: config?.dbHost || void 0,
287
+ databasePort: config?.dbPort || void 0,
288
+ databaseUsername: config?.dbUsername ? this.decrypt(config.dbUsername) : void 0,
289
+ databasePassword: config?.dbPassword ? this.decrypt(config.dbPassword) : void 0,
290
+ databaseSslMode: config?.dbSslMode || void 0,
291
+ connectionPoolSize: config?.connectionPoolSize || void 0
293
292
  };
294
293
  this.cacheInfo(info);
295
294
  return info;
@@ -315,7 +314,7 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
315
314
  *
316
315
  * Useful when tenant settings are updated and cache needs to be invalidated
317
316
  *
318
- * @param tenantIdentifier Tenant ID or slug
317
+ * @param tenantIdentifier Tenant ID or subdomain
319
318
  */
320
319
  clearTenantCache(tenantIdentifier) {
321
320
  const config = this.tenantConfigCache.get(tenantIdentifier);
@@ -334,17 +333,23 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
334
333
  this.logger.log(`Cleared ${size} cached tenant configs`);
335
334
  }
336
335
  /**
337
- * Get the Prisma client for the primary database.
338
- * This is a synchronous property that returns the initialized Prisma client.
336
+ * Get the Drizzle database instance for the primary database.
337
+ * This is a synchronous property that returns the initialized Drizzle client.
339
338
  *
340
- * @returns Primary database client instance
339
+ * @returns Primary database Drizzle instance
341
340
  * @throws Error if primary database client is not initialized
342
341
  */
343
- get prismaClient() {
344
- if (!this.primaryDbClient) {
342
+ get drizzleClient() {
343
+ if (!this.db) {
345
344
  throw new Error("Primary database client not initialized");
346
345
  }
347
- return this.primaryDbClient;
346
+ return this.db;
347
+ }
348
+ /**
349
+ * Get the Drizzle schema
350
+ */
351
+ get schema() {
352
+ return this.options.drizzleSchema;
348
353
  }
349
354
  /**
350
355
  * Decrypt database credentials
@@ -358,8 +363,8 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
358
363
  return encrypted;
359
364
  }
360
365
  async onModuleDestroy() {
361
- if (this.primaryDbClient) {
362
- await this.primaryDbClient.$disconnect();
366
+ if (this.pool) {
367
+ await this.pool.end();
363
368
  this.logger.log("Disconnected from primary database");
364
369
  }
365
370
  }
@@ -904,6 +909,8 @@ TenantContextInterceptor = _ts_decorate8([
904
909
 
905
910
  // src/database/services/tenant-database.service.ts
906
911
  import { Inject as Inject3, Injectable as Injectable7, InternalServerErrorException as InternalServerErrorException2, Logger as Logger5 } from "@nestjs/common";
912
+ import { Pool as Pool2 } from "pg";
913
+ import { drizzle as drizzle2 } from "drizzle-orm/node-postgres";
907
914
  function _ts_decorate9(decorators, target, key, desc) {
908
915
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
909
916
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -928,7 +935,7 @@ var TenantDatabaseService = class _TenantDatabaseService {
928
935
  options;
929
936
  tenantContext;
930
937
  logger = new Logger5(_TenantDatabaseService.name);
931
- /** Connection pool: Map<cacheKey, DbClient> */
938
+ /** Connection pool: Map<cacheKey, TenantConnection> */
932
939
  clients = /* @__PURE__ */ new Map();
933
940
  /** Track last usage time for idle connection cleanup */
934
941
  clientLastUsed = /* @__PURE__ */ new Map();
@@ -940,17 +947,23 @@ var TenantDatabaseService = class _TenantDatabaseService {
940
947
  this.startConnectionCleaner();
941
948
  }
942
949
  /**
943
- * Get the Prisma client for the current tenant's database.
950
+ * Get the Drizzle client for the current tenant's database.
944
951
  * This returns the tenant-scoped database client.
945
952
  *
946
- * @returns Tenant-scoped database client instance
953
+ * @returns Tenant-scoped Drizzle database instance
947
954
  * @throws UnauthorizedException if tenant context not set
948
955
  * @throws InternalServerErrorException if connection fails
949
956
  */
950
- get prismaClient() {
957
+ get drizzleClient() {
951
958
  return this.getDbClient();
952
959
  }
953
960
  /**
961
+ * Get the Drizzle schema
962
+ */
963
+ get schema() {
964
+ return this.options.drizzleSchema;
965
+ }
966
+ /**
954
967
  * Get tenant-scoped database client for the current request/message
955
968
  *
956
969
  * This method:
@@ -958,66 +971,61 @@ var TenantDatabaseService = class _TenantDatabaseService {
958
971
  * 2. Builds a connection URL based on tenant type
959
972
  * 3. Returns cached client if exists, otherwise creates new one
960
973
  *
961
- * @returns Promise<Database client instance>
974
+ * @returns Drizzle database instance
962
975
  * @throws UnauthorizedException if tenant context not set
963
976
  * @throws InternalServerErrorException if connection fails
964
- *
965
- * @example
966
- * const dbClient = await tenantDatabase.getDbClient<PrismaClient>();
967
- * const users = await dbClient.user.findMany();
968
977
  */
969
- async getDbClient() {
978
+ getDbClient() {
970
979
  const tenant = this.tenantContext.getTenant();
971
980
  const cacheKey = this.buildCacheKey(tenant);
972
- if (this.clients.has(cacheKey)) {
981
+ const existing = this.clients.get(cacheKey);
982
+ if (existing) {
973
983
  this.clientLastUsed.set(cacheKey, Date.now());
974
984
  this.logger.debug(`Reusing cached connection: ${cacheKey}`);
975
- return this.clients.get(cacheKey);
985
+ return existing.db;
976
986
  }
977
987
  this.logger.log(`Creating new database connection: ${cacheKey}`);
978
- const client = await this.createDbClient(tenant);
979
- this.clients.set(cacheKey, client);
988
+ const connection = this.createDbClientSync(tenant);
989
+ this.clients.set(cacheKey, connection);
980
990
  this.clientLastUsed.set(cacheKey, Date.now());
981
- return client;
991
+ return connection.db;
982
992
  }
983
993
  /**
984
- * Create a new database client for the given tenant
994
+ * Create a new database client for the given tenant (synchronous)
985
995
  */
986
- async createDbClient(tenant) {
996
+ createDbClientSync(tenant) {
987
997
  try {
988
998
  const databaseUrl = this.buildTenantDbUrl(tenant);
989
- const PrismaClient = await this.options.prismaClientConstructor;
990
- const client = new PrismaClient({
991
- datasources: {
992
- db: {
993
- url: databaseUrl
994
- }
995
- },
996
- log: [
997
- "error",
998
- "warn"
999
- ]
999
+ const pool = new Pool2({
1000
+ connectionString: databaseUrl,
1001
+ max: tenant.connectionPoolSize || this.options.maxConnections || 10
1002
+ });
1003
+ const db = drizzle2({
1004
+ client: pool,
1005
+ schema: this.options.drizzleSchema
1000
1006
  });
1001
- await client.$connect();
1002
1007
  this.logger.log(`Connected to database for tenant: ${tenant.subdomain}`);
1003
- return client;
1008
+ return {
1009
+ pool,
1010
+ db
1011
+ };
1004
1012
  } catch (error) {
1005
1013
  this.logger.error(`Failed to create database connection for tenant: ${tenant.subdomain}`, error);
1006
1014
  throw new InternalServerErrorException2("Failed to connect to tenant database");
1007
1015
  }
1008
1016
  }
1009
1017
  /**
1010
- * Build connection URL for enterprise tenant (dedicated database)
1018
+ * Build connection URL for tenant (dedicated database)
1011
1019
  */
1012
1020
  buildTenantDbUrl(tenant) {
1013
1021
  const { databaseHost, databasePort, databaseName, databaseUsername, databasePassword, databaseSslMode } = tenant;
1014
1022
  if (!databaseHost || !databaseName || !databaseUsername) {
1015
- throw new Error(`Enterprise tenant ${tenant.subdomain} missing database configuration`);
1023
+ throw new Error(`Tenant ${tenant.subdomain} missing database configuration`);
1016
1024
  }
1017
1025
  const port = databasePort || 5432;
1018
1026
  const sslMode = databaseSslMode || "require";
1019
- const connectionUrl = `postgresql://${databaseUsername}:${databasePassword}@${databaseHost}:${port}/${databaseName}?sslmode=${sslMode}`;
1020
- this.logger.debug(`Enterprise connection URL: ${this.maskPassword(connectionUrl)}`);
1027
+ const connectionUrl = `postgresql://${databaseUsername}:${encodeURIComponent(databasePassword || "")}@${databaseHost}:${port}/${databaseName}?sslmode=${sslMode}`;
1028
+ this.logger.debug(`Tenant connection URL: ${this.maskPassword(connectionUrl)}`);
1021
1029
  return connectionUrl;
1022
1030
  }
1023
1031
  /**
@@ -1039,19 +1047,20 @@ var TenantDatabaseService = class _TenantDatabaseService {
1039
1047
  /**
1040
1048
  * Clean up idle connections that haven't been used recently
1041
1049
  */
1042
- cleanupIdleConnections() {
1050
+ async cleanupIdleConnections() {
1043
1051
  const now = Date.now();
1044
1052
  const maxIdle = this.options.connectionCacheTTL || 3e5;
1045
1053
  let cleaned = 0;
1046
1054
  for (const [key, lastUsed] of this.clientLastUsed.entries()) {
1047
1055
  if (now - lastUsed > maxIdle) {
1048
- const client = this.clients.get(key);
1049
- if (client) {
1050
- client.$disconnect().then(() => {
1056
+ const connection = this.clients.get(key);
1057
+ if (connection) {
1058
+ try {
1059
+ await connection.pool.end();
1051
1060
  this.logger.debug(`Cleaned up idle connection: ${key}`);
1052
- }).catch((error) => {
1061
+ } catch (error) {
1053
1062
  this.logger.error(`Error disconnecting idle client: ${key}`, error);
1054
- });
1063
+ }
1055
1064
  this.clients.delete(key);
1056
1065
  this.clientLastUsed.delete(key);
1057
1066
  cleaned++;
@@ -1082,9 +1091,9 @@ var TenantDatabaseService = class _TenantDatabaseService {
1082
1091
  clearInterval(this.cleanupInterval);
1083
1092
  }
1084
1093
  this.logger.log(`Disconnecting ${this.clients.size} database connections`);
1085
- const disconnectPromises = Array.from(this.clients.entries()).map(async ([key, client]) => {
1094
+ const disconnectPromises = Array.from(this.clients.entries()).map(async ([key, connection]) => {
1086
1095
  try {
1087
- await client.$disconnect();
1096
+ await connection.pool.end();
1088
1097
  this.logger.debug(`Disconnected: ${key}`);
1089
1098
  } catch (error) {
1090
1099
  this.logger.error(`Error disconnecting client: ${key}`, error);
@@ -1220,57 +1229,73 @@ DatabaseModule = _ts_decorate10([
1220
1229
 
1221
1230
  // src/database/repositories/primary-base.repository.ts
1222
1231
  import { Logger as Logger6 } from "@nestjs/common";
1232
+ import { eq as eq2, sql, getTableName } from "drizzle-orm";
1223
1233
  var PrimaryBaseRepository = class {
1224
1234
  static {
1225
1235
  __name(this, "PrimaryBaseRepository");
1226
1236
  }
1227
1237
  database;
1238
+ table;
1228
1239
  logger;
1229
- modelGetter;
1230
1240
  /**
1231
- * Lazy getter for Prisma client.
1241
+ * The table name extracted from the Drizzle table at runtime.
1242
+ * Used to access the query API for this repository's table.
1243
+ */
1244
+ tableName;
1245
+ /**
1246
+ * Lazy getter for Drizzle client.
1232
1247
  * Accesses the client from the database service only when needed,
1233
1248
  * avoiding initialization timing issues with NestJS lifecycle.
1234
1249
  */
1235
- get prisma() {
1236
- return this.database.prismaClient;
1250
+ get db() {
1251
+ return this.database.drizzleClient;
1237
1252
  }
1238
1253
  /**
1239
- * Lazy getter for the Prisma model delegate.
1240
- * Returns the specific model (e.g., prisma.user, prisma.tenant) for this repository.
1254
+ * Model query API for THIS repository's table (Drizzle v2 relational queries)
1255
+ * Scoped to only the table this repository manages.
1256
+ * Returns a type-safe wrapper around Drizzle's RelationalQueryBuilder.
1257
+ *
1258
+ * @example
1259
+ * ```typescript
1260
+ * // Use relational queries with v2 object-based where syntax
1261
+ * const user = await this.model.findFirst({
1262
+ * where: { id },
1263
+ * with: { posts: true, profile: true }
1264
+ * });
1265
+ * ```
1241
1266
  */
1242
1267
  get model() {
1243
- return this.modelGetter(this.prisma);
1268
+ const query = this.database.drizzleClient.query;
1269
+ const queryKeys = Object.keys(query || {});
1270
+ this.logger.debug(`Looking for '${this.tableName}' in query keys: [${queryKeys.join(", ")}]`);
1271
+ const model = query[this.tableName];
1272
+ if (!model) {
1273
+ this.logger.error(`Table '${this.tableName}' not found in query object. Available: [${queryKeys.join(", ")}]`);
1274
+ }
1275
+ return model;
1244
1276
  }
1245
1277
  /**
1246
1278
  * Create a new repository instance
1247
1279
  *
1248
1280
  * @param database - The primary database service
1249
- * @param getModel - Function that returns the Prisma model delegate from the client
1281
+ * @param table - The Drizzle table schema object
1250
1282
  *
1251
1283
  * @example
1252
1284
  * ```typescript
1253
- * // Standard usage with full parameter name
1254
- * constructor(database: PrimaryDatabaseService) {
1255
- * super(database, (prisma) => prisma.user);
1256
- * }
1285
+ * import { users } from '@/db/schema';
1257
1286
  *
1258
- * // Short syntax
1259
1287
  * constructor(database: PrimaryDatabaseService) {
1260
- * super(database, (p) => p.user);
1261
- * }
1262
- *
1263
- * // Complex model names
1264
- * constructor(database: PrimaryDatabaseService) {
1265
- * super(database, (p) => p.emailVerification);
1288
+ * super(database, users);
1266
1289
  * }
1267
1290
  * ```
1268
1291
  */
1269
- constructor(database, getModel) {
1292
+ constructor(database, table) {
1270
1293
  this.database = database;
1294
+ this.table = table;
1295
+ this.tableName = getTableName(table);
1271
1296
  this.logger = new Logger6(this.constructor.name);
1272
- this.modelGetter = getModel;
1273
1297
  this.logger.debug(`Initialized ${this.constructor.name}`);
1298
+ this.logger.debug(`Table name from getTableName: '${this.tableName}'`);
1274
1299
  }
1275
1300
  /**
1276
1301
  * Create a new record
@@ -1282,21 +1307,20 @@ var PrimaryBaseRepository = class {
1282
1307
  * ```typescript
1283
1308
  * const user = await userRepository.create({
1284
1309
  * email: 'user@example.com',
1285
- * name: 'John Doe'
1310
+ * firstName: 'John'
1286
1311
  * });
1287
1312
  * ```
1288
1313
  */
1289
1314
  async create(data) {
1290
1315
  this.logger.log("Creating record");
1291
- return await this.model.create({
1292
- data
1293
- });
1316
+ const results = await this.db.insert(this.table).values(data).returning();
1317
+ return results[0];
1294
1318
  }
1295
1319
  /**
1296
1320
  * Find a single record by ID
1297
1321
  *
1298
1322
  * @param id - The record ID
1299
- * @returns Promise resolving to the record or null if not found
1323
+ * @returns Promise resolving to the record or undefined if not found
1300
1324
  *
1301
1325
  * @example
1302
1326
  * ```typescript
@@ -1305,40 +1329,43 @@ var PrimaryBaseRepository = class {
1305
1329
  */
1306
1330
  async findById(id) {
1307
1331
  this.logger.debug(`Finding record by ID: ${id}`);
1308
- return await this.model.findUnique({
1332
+ return this.model.findFirst({
1309
1333
  where: {
1310
1334
  id
1311
1335
  }
1312
1336
  });
1313
1337
  }
1314
1338
  /**
1315
- * Find a single record with custom where clause
1339
+ * Find a single record with custom where clause (Drizzle v2 object-based syntax)
1316
1340
  *
1317
- * @param where - The where clause or findUnique args
1318
- * @returns Promise resolving to the record or null if not found
1341
+ * @param where - Object-based filter condition
1342
+ * @returns Promise resolving to the record or undefined if not found
1319
1343
  *
1320
1344
  * @example
1321
1345
  * ```typescript
1322
- * // Simple where clause
1346
+ * // Simple equality
1323
1347
  * const user = await userRepository.findOne({ email: 'user@example.com' });
1324
1348
  *
1325
- * // With include
1349
+ * // With operators
1350
+ * const user = await userRepository.findOne({ age: { gte: 18 } });
1351
+ *
1352
+ * // Multiple conditions (AND)
1326
1353
  * const user = await userRepository.findOne({
1327
- * where: { email: 'user@example.com' },
1328
- * include: { posts: true }
1354
+ * email: 'user@example.com',
1355
+ * status: 'ACTIVE'
1329
1356
  * });
1330
1357
  * ```
1331
1358
  */
1332
1359
  async findOne(where) {
1333
1360
  this.logger.debug("Finding record with custom query");
1334
- return await this.model.findUnique(typeof where === "object" && "where" in where ? where : {
1361
+ return this.model.findFirst({
1335
1362
  where
1336
1363
  });
1337
1364
  }
1338
1365
  /**
1339
- * Find multiple records
1366
+ * Find multiple records (Drizzle v2 object-based syntax)
1340
1367
  *
1341
- * @param args - Prisma findMany arguments (where, orderBy, take, skip, etc.)
1368
+ * @param options - Query options (where, orderBy, limit, offset)
1342
1369
  * @returns Promise resolving to an array of records
1343
1370
  *
1344
1371
  * @example
@@ -1346,18 +1373,28 @@ var PrimaryBaseRepository = class {
1346
1373
  * // Find all users
1347
1374
  * const users = await userRepository.findMany();
1348
1375
  *
1349
- * // Find with filtering and pagination
1376
+ * // Find with filtering and pagination (v2 object syntax)
1350
1377
  * const users = await userRepository.findMany({
1351
- * where: { status: 'ACTIVE' },
1378
+ * where: { accountStatus: 'ACTIVE' },
1352
1379
  * orderBy: { createdAt: 'desc' },
1353
- * take: 10,
1354
- * skip: 0
1380
+ * limit: 10,
1381
+ * offset: 0
1382
+ * });
1383
+ *
1384
+ * // Multiple conditions
1385
+ * const users = await userRepository.findMany({
1386
+ * where: {
1387
+ * AND: [
1388
+ * { status: 'ACTIVE' },
1389
+ * { age: { gte: 18 } }
1390
+ * ]
1391
+ * }
1355
1392
  * });
1356
1393
  * ```
1357
1394
  */
1358
- async findMany(args) {
1395
+ async findMany(options) {
1359
1396
  this.logger.debug("Finding multiple records");
1360
- return await this.model.findMany(args);
1397
+ return this.model.findMany(options);
1361
1398
  }
1362
1399
  /**
1363
1400
  * Update a record by ID
@@ -1369,41 +1406,40 @@ var PrimaryBaseRepository = class {
1369
1406
  * @example
1370
1407
  * ```typescript
1371
1408
  * const user = await userRepository.update('user-id-123', {
1372
- * name: 'Jane Doe'
1409
+ * firstName: 'Jane'
1373
1410
  * });
1374
1411
  * ```
1375
1412
  */
1376
1413
  async update(id, data) {
1377
1414
  this.logger.log(`Updating record with ID: ${id}`);
1378
- return await this.model.update({
1379
- where: {
1380
- id
1381
- },
1382
- data
1383
- });
1415
+ const idColumn = this.table.id;
1416
+ const results = await this.db.update(this.table).set(data).where(eq2(idColumn, id)).returning();
1417
+ return results[0];
1384
1418
  }
1385
1419
  /**
1386
1420
  * Update multiple records
1387
1421
  *
1388
- * @param where - The where clause to match records
1422
+ * @param where - SQL condition to match records
1389
1423
  * @param data - The data to update
1390
1424
  * @returns Promise resolving to the count of updated records
1391
1425
  *
1392
1426
  * @example
1393
1427
  * ```typescript
1428
+ * import { eq } from 'drizzle-orm';
1429
+ *
1394
1430
  * const result = await userRepository.updateMany(
1395
- * { status: 'PENDING' },
1396
- * { status: 'ACTIVE' }
1431
+ * eq(users.accountStatus, 'PENDING'),
1432
+ * { accountStatus: 'ACTIVE' }
1397
1433
  * );
1398
1434
  * console.log(`Updated ${result.count} users`);
1399
1435
  * ```
1400
1436
  */
1401
1437
  async updateMany(where, data) {
1402
1438
  this.logger.log("Updating multiple records");
1403
- return await this.model.updateMany({
1404
- where,
1405
- data
1406
- });
1439
+ const result = await this.db.update(this.table).set(data).where(where);
1440
+ return {
1441
+ count: result.rowCount ?? 0
1442
+ };
1407
1443
  }
1408
1444
  /**
1409
1445
  * Delete a record by ID
@@ -1418,127 +1454,143 @@ var PrimaryBaseRepository = class {
1418
1454
  */
1419
1455
  async delete(id) {
1420
1456
  this.logger.log(`Deleting record with ID: ${id}`);
1421
- return await this.model.delete({
1422
- where: {
1423
- id
1424
- }
1425
- });
1457
+ const idColumn = this.table.id;
1458
+ const results = await this.db.delete(this.table).where(eq2(idColumn, id)).returning();
1459
+ return results[0];
1426
1460
  }
1427
1461
  /**
1428
1462
  * Delete multiple records
1429
1463
  *
1430
- * @param where - The where clause to match records
1464
+ * @param where - SQL condition to match records
1431
1465
  * @returns Promise resolving to the count of deleted records
1432
1466
  *
1433
1467
  * @example
1434
1468
  * ```typescript
1435
- * const result = await userRepository.deleteMany({
1436
- * status: 'INACTIVE',
1437
- * createdAt: { lt: new Date('2020-01-01') }
1438
- * });
1469
+ * import { lt } from 'drizzle-orm';
1470
+ *
1471
+ * const result = await userRepository.deleteMany(
1472
+ * lt(users.createdAt, new Date('2020-01-01'))
1473
+ * );
1439
1474
  * console.log(`Deleted ${result.count} users`);
1440
1475
  * ```
1441
1476
  */
1442
1477
  async deleteMany(where) {
1443
1478
  this.logger.log("Deleting multiple records");
1444
- return await this.model.deleteMany({
1445
- where
1446
- });
1479
+ const result = await this.db.delete(this.table).where(where);
1480
+ return {
1481
+ count: result.rowCount ?? 0
1482
+ };
1447
1483
  }
1448
1484
  /**
1449
1485
  * Count records
1450
1486
  *
1451
- * @param where - Optional where clause to filter records
1487
+ * @param where - Optional SQL condition to filter records
1452
1488
  * @returns Promise resolving to the count of records
1453
1489
  *
1454
1490
  * @example
1455
1491
  * ```typescript
1492
+ * import { eq } from 'drizzle-orm';
1493
+ *
1456
1494
  * // Count all users
1457
1495
  * const total = await userRepository.count();
1458
1496
  *
1459
1497
  * // Count active users
1460
- * const activeCount = await userRepository.count({ status: 'ACTIVE' });
1498
+ * const activeCount = await userRepository.count(
1499
+ * eq(users.accountStatus, 'ACTIVE')
1500
+ * );
1461
1501
  * ```
1462
1502
  */
1463
1503
  async count(where) {
1464
1504
  this.logger.debug("Counting records");
1465
- return await this.model.count({
1466
- where
1467
- });
1505
+ let query = this.db.select({
1506
+ count: sql`count(*)::int`
1507
+ }).from(this.table).$dynamic();
1508
+ if (where) {
1509
+ query = query.where(where);
1510
+ }
1511
+ const results = await query;
1512
+ return results[0].count;
1468
1513
  }
1469
1514
  /**
1470
1515
  * Check if a record exists
1471
1516
  *
1472
- * @param where - The where clause to match records
1517
+ * @param where - SQL condition to match records
1473
1518
  * @returns Promise resolving to true if at least one record exists, false otherwise
1474
1519
  *
1475
1520
  * @example
1476
1521
  * ```typescript
1477
- * const emailExists = await userRepository.exists({
1478
- * email: 'user@example.com'
1479
- * });
1522
+ * import { eq } from 'drizzle-orm';
1523
+ *
1524
+ * const emailExists = await userRepository.exists(
1525
+ * eq(users.email, 'user@example.com')
1526
+ * );
1480
1527
  * ```
1481
1528
  */
1482
1529
  async exists(where) {
1483
- const count = await this.model.count({
1484
- where
1485
- });
1530
+ const count = await this.count(where);
1486
1531
  return count > 0;
1487
1532
  }
1488
1533
  };
1489
1534
 
1490
1535
  // src/database/repositories/tenant-base.repository.ts
1491
1536
  import { Logger as Logger7 } from "@nestjs/common";
1537
+ import { eq as eq3, sql as sql2, getTableName as getTableName2 } from "drizzle-orm";
1492
1538
  var TenantBaseRepository = class {
1493
1539
  static {
1494
1540
  __name(this, "TenantBaseRepository");
1495
1541
  }
1496
1542
  database;
1543
+ table;
1497
1544
  logger;
1498
- modelGetter;
1499
1545
  /**
1500
- * Lazy getter for Prisma client.
1546
+ * The table name extracted from the Drizzle table at runtime.
1547
+ * Used to access the query API for this repository's table.
1548
+ */
1549
+ tableName;
1550
+ /**
1551
+ * Lazy getter for Drizzle client.
1501
1552
  * Accesses the client from the database service only when needed,
1502
1553
  * avoiding initialization timing issues with NestJS lifecycle.
1503
1554
  */
1504
- get prisma() {
1505
- return this.database.prismaClient;
1555
+ get db() {
1556
+ return this.database.drizzleClient;
1506
1557
  }
1507
1558
  /**
1508
- * Lazy getter for the Prisma model delegate.
1509
- * Returns the specific model (e.g., prisma.product, prisma.order) for this repository.
1559
+ * Model query API for THIS repository's table (Prisma-like syntax)
1560
+ * Scoped to only the table this repository manages
1561
+ *
1562
+ * @example
1563
+ * ```typescript
1564
+ * // Use relational queries with type safety
1565
+ * const product = await this.model.findFirst({
1566
+ * where: eq(products.id, id),
1567
+ * with: { category: true, variants: true }
1568
+ * });
1569
+ * ```
1510
1570
  */
1511
1571
  get model() {
1512
- return this.modelGetter(this.prisma);
1572
+ return this.database.drizzleClient.query[this.tableName];
1513
1573
  }
1514
1574
  /**
1515
1575
  * Create a new repository instance
1516
1576
  *
1517
1577
  * @param database - The tenant database service
1518
- * @param getModel - Function that returns the Prisma model delegate from the client
1578
+ * @param table - The Drizzle table schema object
1519
1579
  *
1520
1580
  * @example
1521
1581
  * ```typescript
1522
- * // Standard usage with full parameter name
1523
- * constructor(database: TenantDatabaseService) {
1524
- * super(database, (prisma) => prisma.product);
1525
- * }
1582
+ * import { products } from '@/db/schema';
1526
1583
  *
1527
- * // Short syntax
1528
1584
  * constructor(database: TenantDatabaseService) {
1529
- * super(database, (p) => p.product);
1530
- * }
1531
- *
1532
- * // Complex model names
1533
- * constructor(database: TenantDatabaseService) {
1534
- * super(database, (p) => p.inventoryItem);
1585
+ * super(database, products);
1535
1586
  * }
1536
1587
  * ```
1537
1588
  */
1538
- constructor(database, getModel) {
1589
+ constructor(database, table) {
1539
1590
  this.database = database;
1591
+ this.table = table;
1592
+ this.tableName = getTableName2(table);
1540
1593
  this.logger = new Logger7(this.constructor.name);
1541
- this.modelGetter = getModel;
1542
1594
  this.logger.debug(`Initialized ${this.constructor.name}`);
1543
1595
  }
1544
1596
  /**
@@ -1558,9 +1610,8 @@ var TenantBaseRepository = class {
1558
1610
  */
1559
1611
  async create(data) {
1560
1612
  this.logger.log("Creating record");
1561
- return await this.model.create({
1562
- data
1563
- });
1613
+ const results = await this.db.insert(this.table).values(data).returning();
1614
+ return results[0];
1564
1615
  }
1565
1616
  /**
1566
1617
  * Find a single record by ID
@@ -1575,59 +1626,65 @@ var TenantBaseRepository = class {
1575
1626
  */
1576
1627
  async findById(id) {
1577
1628
  this.logger.debug(`Finding record by ID: ${id}`);
1578
- return await this.model.findUnique({
1579
- where: {
1580
- id
1581
- }
1582
- });
1629
+ const idColumn = this.table.id;
1630
+ const results = await this.db.select().from(this.table).where(eq3(idColumn, id)).limit(1);
1631
+ return results[0] ?? null;
1583
1632
  }
1584
1633
  /**
1585
1634
  * Find a single record with custom where clause
1586
1635
  *
1587
- * @param where - The where clause or findUnique args
1636
+ * @param where - SQL condition
1588
1637
  * @returns Promise resolving to the record or null if not found
1589
1638
  *
1590
1639
  * @example
1591
1640
  * ```typescript
1592
- * // Simple where clause
1593
- * const product = await productRepository.findOne({ sku: 'WDG-001' });
1594
- *
1595
- * // With include
1596
- * const product = await productRepository.findOne({
1597
- * where: { sku: 'WDG-001' },
1598
- * include: { category: true }
1599
- * });
1641
+ * import { eq } from 'drizzle-orm';
1642
+ * const product = await productRepository.findOne(eq(products.sku, 'WDG-001'));
1600
1643
  * ```
1601
1644
  */
1602
1645
  async findOne(where) {
1603
1646
  this.logger.debug("Finding record with custom query");
1604
- return await this.model.findUnique(typeof where === "object" && "where" in where ? where : {
1605
- where
1606
- });
1647
+ const results = await this.db.select().from(this.table).where(where).limit(1);
1648
+ return results[0] ?? null;
1607
1649
  }
1608
1650
  /**
1609
1651
  * Find multiple records
1610
1652
  *
1611
- * @param args - Prisma findMany arguments (where, orderBy, take, skip, etc.)
1653
+ * @param options - Query options (where, orderBy, limit, offset)
1612
1654
  * @returns Promise resolving to an array of records
1613
1655
  *
1614
1656
  * @example
1615
1657
  * ```typescript
1658
+ * import { eq, desc } from 'drizzle-orm';
1659
+ *
1616
1660
  * // Find all products
1617
1661
  * const products = await productRepository.findMany();
1618
1662
  *
1619
1663
  * // Find with filtering and pagination
1620
1664
  * const products = await productRepository.findMany({
1621
- * where: { status: 'ACTIVE' },
1622
- * orderBy: { createdAt: 'desc' },
1623
- * take: 10,
1624
- * skip: 0
1665
+ * where: eq(products.status, 'ACTIVE'),
1666
+ * orderBy: desc(products.createdAt),
1667
+ * limit: 10,
1668
+ * offset: 0
1625
1669
  * });
1626
1670
  * ```
1627
1671
  */
1628
- async findMany(args) {
1672
+ async findMany(options) {
1629
1673
  this.logger.debug("Finding multiple records");
1630
- return await this.model.findMany(args);
1674
+ let query = this.db.select().from(this.table).$dynamic();
1675
+ if (options?.where) {
1676
+ query = query.where(options.where);
1677
+ }
1678
+ if (options?.orderBy) {
1679
+ query = query.orderBy(options.orderBy);
1680
+ }
1681
+ if (options?.limit) {
1682
+ query = query.limit(options.limit);
1683
+ }
1684
+ if (options?.offset) {
1685
+ query = query.offset(options.offset);
1686
+ }
1687
+ return await query;
1631
1688
  }
1632
1689
  /**
1633
1690
  * Update a record by ID
@@ -1645,24 +1702,23 @@ var TenantBaseRepository = class {
1645
1702
  */
1646
1703
  async update(id, data) {
1647
1704
  this.logger.log(`Updating record with ID: ${id}`);
1648
- return await this.model.update({
1649
- where: {
1650
- id
1651
- },
1652
- data
1653
- });
1705
+ const idColumn = this.table.id;
1706
+ const results = await this.db.update(this.table).set(data).where(eq3(idColumn, id)).returning();
1707
+ return results[0];
1654
1708
  }
1655
1709
  /**
1656
1710
  * Update multiple records
1657
1711
  *
1658
- * @param where - The where clause to match records
1712
+ * @param where - SQL condition to match records
1659
1713
  * @param data - The data to update
1660
1714
  * @returns Promise resolving to the count of updated records
1661
1715
  *
1662
1716
  * @example
1663
1717
  * ```typescript
1718
+ * import { eq } from 'drizzle-orm';
1719
+ *
1664
1720
  * const result = await productRepository.updateMany(
1665
- * { status: 'PENDING' },
1721
+ * eq(products.status, 'PENDING'),
1666
1722
  * { status: 'ACTIVE' }
1667
1723
  * );
1668
1724
  * console.log(`Updated ${result.count} products`);
@@ -1670,10 +1726,10 @@ var TenantBaseRepository = class {
1670
1726
  */
1671
1727
  async updateMany(where, data) {
1672
1728
  this.logger.log("Updating multiple records");
1673
- return await this.model.updateMany({
1674
- where,
1675
- data
1676
- });
1729
+ const result = await this.db.update(this.table).set(data).where(where);
1730
+ return {
1731
+ count: result.rowCount ?? 0
1732
+ };
1677
1733
  }
1678
1734
  /**
1679
1735
  * Delete a record by ID
@@ -1688,71 +1744,80 @@ var TenantBaseRepository = class {
1688
1744
  */
1689
1745
  async delete(id) {
1690
1746
  this.logger.log(`Deleting record with ID: ${id}`);
1691
- return await this.model.delete({
1692
- where: {
1693
- id
1694
- }
1695
- });
1747
+ const idColumn = this.table.id;
1748
+ const results = await this.db.delete(this.table).where(eq3(idColumn, id)).returning();
1749
+ return results[0];
1696
1750
  }
1697
1751
  /**
1698
1752
  * Delete multiple records
1699
1753
  *
1700
- * @param where - The where clause to match records
1754
+ * @param where - SQL condition to match records
1701
1755
  * @returns Promise resolving to the count of deleted records
1702
1756
  *
1703
1757
  * @example
1704
1758
  * ```typescript
1705
- * const result = await productRepository.deleteMany({
1706
- * status: 'INACTIVE',
1707
- * createdAt: { lt: new Date('2020-01-01') }
1708
- * });
1759
+ * import { lt } from 'drizzle-orm';
1760
+ *
1761
+ * const result = await productRepository.deleteMany(
1762
+ * lt(products.createdAt, new Date('2020-01-01'))
1763
+ * );
1709
1764
  * console.log(`Deleted ${result.count} products`);
1710
1765
  * ```
1711
1766
  */
1712
1767
  async deleteMany(where) {
1713
1768
  this.logger.log("Deleting multiple records");
1714
- return await this.model.deleteMany({
1715
- where
1716
- });
1769
+ const result = await this.db.delete(this.table).where(where);
1770
+ return {
1771
+ count: result.rowCount ?? 0
1772
+ };
1717
1773
  }
1718
1774
  /**
1719
1775
  * Count records
1720
1776
  *
1721
- * @param where - Optional where clause to filter records
1777
+ * @param where - Optional SQL condition to filter records
1722
1778
  * @returns Promise resolving to the count of records
1723
1779
  *
1724
1780
  * @example
1725
1781
  * ```typescript
1782
+ * import { eq } from 'drizzle-orm';
1783
+ *
1726
1784
  * // Count all products
1727
1785
  * const total = await productRepository.count();
1728
1786
  *
1729
1787
  * // Count active products
1730
- * const activeCount = await productRepository.count({ status: 'ACTIVE' });
1788
+ * const activeCount = await productRepository.count(
1789
+ * eq(products.status, 'ACTIVE')
1790
+ * );
1731
1791
  * ```
1732
1792
  */
1733
1793
  async count(where) {
1734
1794
  this.logger.debug("Counting records");
1735
- return await this.model.count({
1736
- where
1737
- });
1795
+ let query = this.db.select({
1796
+ count: sql2`count(*)::int`
1797
+ }).from(this.table).$dynamic();
1798
+ if (where) {
1799
+ query = query.where(where);
1800
+ }
1801
+ const results = await query;
1802
+ return results[0].count;
1738
1803
  }
1739
1804
  /**
1740
1805
  * Check if a record exists
1741
1806
  *
1742
- * @param where - The where clause to match records
1807
+ * @param where - SQL condition to match records
1743
1808
  * @returns Promise resolving to true if at least one record exists, false otherwise
1744
1809
  *
1745
1810
  * @example
1746
1811
  * ```typescript
1747
- * const skuExists = await productRepository.exists({
1748
- * sku: 'WDG-001'
1749
- * });
1812
+ * import { eq } from 'drizzle-orm';
1813
+ *
1814
+ * const skuExists = await productRepository.exists(
1815
+ * eq(products.sku, 'WDG-001')
1816
+ * );
1750
1817
  * ```
1751
1818
  */
1752
1819
  async exists(where) {
1753
- const count = await this.model.count({
1754
- where
1755
- });
1820
+ const count = await this.count(where);
1756
1821
  return count > 0;
1757
1822
  }
1758
1823
  };
@@ -1909,15 +1974,16 @@ var HttpExceptionFilter = class _HttpExceptionFilter {
1909
1974
  const exceptionResponse = exception.getResponse();
1910
1975
  if (typeof exceptionResponse === "object" && exceptionResponse !== null) {
1911
1976
  const responseObj = exceptionResponse;
1912
- if (responseObj.errors && Array.isArray(responseObj.errors)) {
1977
+ if ("errors" in responseObj && Array.isArray(responseObj.errors)) {
1913
1978
  errors = responseObj.errors;
1914
- detail = responseObj.detail || exception.message;
1915
- } else if (responseObj.message && Array.isArray(responseObj.message)) {
1979
+ detail = ("detail" in responseObj ? responseObj.detail : void 0) || exception.message;
1980
+ } else if ("message" in responseObj && Array.isArray(responseObj.message)) {
1916
1981
  errors = responseObj.message.map((msg) => {
1917
- if (typeof msg === "object" && msg.property && msg.constraints) {
1982
+ if (typeof msg === "object" && "property" in msg && "constraints" in msg) {
1983
+ const constraintValues = Object.values(msg.constraints);
1918
1984
  return {
1919
1985
  field: msg.property,
1920
- message: Object.values(msg.constraints)[0]
1986
+ message: constraintValues[0] ?? "Validation failed"
1921
1987
  };
1922
1988
  }
1923
1989
  return {
@@ -1925,13 +1991,14 @@ var HttpExceptionFilter = class _HttpExceptionFilter {
1925
1991
  };
1926
1992
  });
1927
1993
  detail = "Validation failed";
1928
- } else if (responseObj.message) {
1994
+ } else if ("message" in responseObj) {
1995
+ const message = responseObj.message;
1929
1996
  errors = [
1930
1997
  {
1931
- message: Array.isArray(responseObj.message) ? responseObj.message.join(", ") : responseObj.message
1998
+ message: Array.isArray(message) ? message.join(", ") : message
1932
1999
  }
1933
2000
  ];
1934
- detail = responseObj.error || exception.message;
2001
+ detail = ("error" in responseObj ? responseObj.error : void 0) || exception.message;
1935
2002
  }
1936
2003
  } else if (typeof exceptionResponse === "string") {
1937
2004
  errors = [
@@ -2979,14 +3046,15 @@ function getLevelsUpTo(level) {
2979
3046
  "debug",
2980
3047
  "verbose"
2981
3048
  ];
2982
- const levelIndex = allLevels.indexOf(level);
2983
- if (levelIndex === -1) {
3049
+ const isValidLevel = /* @__PURE__ */ __name((l) => allLevels.includes(l), "isValidLevel");
3050
+ if (!isValidLevel(level)) {
2984
3051
  return [
2985
3052
  "error",
2986
3053
  "warn",
2987
3054
  "log"
2988
3055
  ];
2989
3056
  }
3057
+ const levelIndex = allLevels.indexOf(level);
2990
3058
  return allLevels.slice(0, levelIndex + 1);
2991
3059
  }
2992
3060
  __name(getLevelsUpTo, "getLevelsUpTo");