@vritti/api-sdk 0.0.3 → 0.0.6

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
@@ -32,11 +32,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
32
  var index_exports = {};
33
33
  __export(index_exports, {
34
34
  AuthConfigModule: () => AuthConfigModule,
35
+ CsrfGuard: () => CsrfGuard,
35
36
  DatabaseModule: () => DatabaseModule,
37
+ HttpExceptionFilter: () => HttpExceptionFilter,
38
+ HttpModule: () => HttpModule,
36
39
  Onboarding: () => Onboarding,
40
+ PrimaryBaseRepository: () => PrimaryBaseRepository,
37
41
  PrimaryDatabaseService: () => PrimaryDatabaseService,
38
42
  Public: () => Public,
39
43
  Tenant: () => Tenant,
44
+ TenantBaseRepository: () => TenantBaseRepository,
40
45
  TenantContextService: () => TenantContextService,
41
46
  TenantDatabaseService: () => TenantDatabaseService,
42
47
  VrittiAuthGuard: () => VrittiAuthGuard
@@ -376,16 +381,15 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
376
381
  this.logger.log(`Cleared ${size} cached tenant configs`);
377
382
  }
378
383
  /**
379
- * Get primary database client for direct database access
380
- *
381
- * This is useful for platform admin operations (creating tenants, billing, etc.)
384
+ * Get the Prisma client for the primary database.
385
+ * This is a synchronous property that returns the initialized Prisma client.
382
386
  *
383
387
  * @returns Primary database client instance
384
388
  * @throws Error if primary database client is not initialized
385
389
  */
386
- getPrimaryDbClient() {
390
+ get prismaClient() {
387
391
  if (!this.primaryDbClient) {
388
- throw new Error("Primary database client not initialized. Are you in gateway mode?");
392
+ throw new Error("Primary database client not initialized");
389
393
  }
390
394
  return this.primaryDbClient;
391
395
  }
@@ -698,7 +702,7 @@ AuthConfigModule = _ts_decorate5([
698
702
 
699
703
  // src/database/database.module.ts
700
704
  var import_common10 = require("@nestjs/common");
701
- var import_core4 = require("@nestjs/core");
705
+ var import_core5 = require("@nestjs/core");
702
706
 
703
707
  // src/database/interceptors/message-tenant-context.interceptor.ts
704
708
  var import_common7 = require("@nestjs/common");
@@ -864,6 +868,7 @@ MessageTenantContextInterceptor = _ts_decorate7([
864
868
 
865
869
  // src/database/interceptors/tenant-context.interceptor.ts
866
870
  var import_common8 = require("@nestjs/common");
871
+ var import_core4 = require("@nestjs/core");
867
872
  function _ts_decorate8(decorators, target, key, desc) {
868
873
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
869
874
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -879,11 +884,13 @@ var TenantContextInterceptor = class _TenantContextInterceptor {
879
884
  static {
880
885
  __name(this, "TenantContextInterceptor");
881
886
  }
887
+ reflector;
882
888
  tenantContext;
883
889
  primaryDatabase;
884
890
  requestService;
885
891
  logger = new import_common8.Logger(_TenantContextInterceptor.name);
886
- constructor(tenantContext, primaryDatabase, requestService) {
892
+ constructor(reflector, tenantContext, primaryDatabase, requestService) {
893
+ this.reflector = reflector;
887
894
  this.tenantContext = tenantContext;
888
895
  this.primaryDatabase = primaryDatabase;
889
896
  this.requestService = requestService;
@@ -891,8 +898,16 @@ var TenantContextInterceptor = class _TenantContextInterceptor {
891
898
  async intercept(context, next) {
892
899
  const request = context.switchToHttp().getRequest();
893
900
  this.logger.debug(`Processing request: ${request.method} ${request.url}`);
901
+ const isPublic = this.reflector.getAllAndOverride("isPublic", [
902
+ context.getHandler(),
903
+ context.getClass()
904
+ ]);
894
905
  try {
895
906
  const tenantIdentifier = this.requestService.getTenantIdentifier();
907
+ if (isPublic && !tenantIdentifier) {
908
+ this.logger.debug("Public endpoint without tenant identifier, skipping tenant context setup");
909
+ return next.handle();
910
+ }
896
911
  if (!tenantIdentifier) {
897
912
  throw new import_common8.UnauthorizedException("Tenant identifier not found in request");
898
913
  }
@@ -927,6 +942,7 @@ TenantContextInterceptor = _ts_decorate8([
927
942
  }),
928
943
  _ts_metadata5("design:type", Function),
929
944
  _ts_metadata5("design:paramtypes", [
945
+ typeof import_core4.Reflector === "undefined" ? Object : import_core4.Reflector,
930
946
  typeof TenantContextService === "undefined" ? Object : TenantContextService,
931
947
  typeof PrimaryDatabaseService === "undefined" ? Object : PrimaryDatabaseService,
932
948
  typeof RequestService === "undefined" ? Object : RequestService
@@ -971,6 +987,17 @@ var TenantDatabaseService = class _TenantDatabaseService {
971
987
  this.startConnectionCleaner();
972
988
  }
973
989
  /**
990
+ * Get the Prisma client for the current tenant's database.
991
+ * This returns the tenant-scoped database client.
992
+ *
993
+ * @returns Tenant-scoped database client instance
994
+ * @throws UnauthorizedException if tenant context not set
995
+ * @throws InternalServerErrorException if connection fails
996
+ */
997
+ get prismaClient() {
998
+ return this.getDbClient();
999
+ }
1000
+ /**
974
1001
  * Get tenant-scoped database client for the current request/message
975
1002
  *
976
1003
  * This method:
@@ -1164,7 +1191,7 @@ var DatabaseModule = class _DatabaseModule {
1164
1191
  * })
1165
1192
  */
1166
1193
  static forServer(options) {
1167
- return this.createDynamicModule(options, "gateway");
1194
+ return this.createDynamicModule(options, "server");
1168
1195
  }
1169
1196
  /**
1170
1197
  * Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)
@@ -1207,14 +1234,14 @@ var DatabaseModule = class _DatabaseModule {
1207
1234
  PrimaryDatabaseService,
1208
1235
  TenantDatabaseService
1209
1236
  ];
1210
- if (mode === "gateway") {
1237
+ if (mode === "server") {
1211
1238
  providers.push({
1212
- provide: import_core4.APP_INTERCEPTOR,
1239
+ provide: import_core5.APP_INTERCEPTOR,
1213
1240
  useClass: TenantContextInterceptor
1214
1241
  });
1215
1242
  } else {
1216
1243
  providers.push({
1217
- provide: import_core4.APP_INTERCEPTOR,
1244
+ provide: import_core5.APP_INTERCEPTOR,
1218
1245
  useClass: MessageTenantContextInterceptor
1219
1246
  });
1220
1247
  }
@@ -1238,9 +1265,548 @@ DatabaseModule = _ts_decorate10([
1238
1265
  (0, import_common10.Module)({})
1239
1266
  ], DatabaseModule);
1240
1267
 
1241
- // src/database/decorators/tenant.decorator.ts
1268
+ // src/database/repositories/primary-base.repository.ts
1242
1269
  var import_common11 = require("@nestjs/common");
1243
- var Tenant = (0, import_common11.createParamDecorator)((data, ctx) => {
1270
+ var PrimaryBaseRepository = class {
1271
+ static {
1272
+ __name(this, "PrimaryBaseRepository");
1273
+ }
1274
+ database;
1275
+ logger;
1276
+ modelGetter;
1277
+ /**
1278
+ * Lazy getter for Prisma client.
1279
+ * Accesses the client from the database service only when needed,
1280
+ * avoiding initialization timing issues with NestJS lifecycle.
1281
+ */
1282
+ get prisma() {
1283
+ return this.database.prismaClient;
1284
+ }
1285
+ /**
1286
+ * Lazy getter for the Prisma model delegate.
1287
+ * Returns the specific model (e.g., prisma.user, prisma.tenant) for this repository.
1288
+ */
1289
+ get model() {
1290
+ return this.modelGetter(this.prisma);
1291
+ }
1292
+ /**
1293
+ * Create a new repository instance
1294
+ *
1295
+ * @param database - The primary database service
1296
+ * @param getModel - Function that returns the Prisma model delegate from the client
1297
+ *
1298
+ * @example
1299
+ * ```typescript
1300
+ * // Standard usage with full parameter name
1301
+ * constructor(database: PrimaryDatabaseService) {
1302
+ * super(database, (prisma) => prisma.user);
1303
+ * }
1304
+ *
1305
+ * // Short syntax
1306
+ * constructor(database: PrimaryDatabaseService) {
1307
+ * super(database, (p) => p.user);
1308
+ * }
1309
+ *
1310
+ * // Complex model names
1311
+ * constructor(database: PrimaryDatabaseService) {
1312
+ * super(database, (p) => p.emailVerification);
1313
+ * }
1314
+ * ```
1315
+ */
1316
+ constructor(database, getModel) {
1317
+ this.database = database;
1318
+ this.logger = new import_common11.Logger(this.constructor.name);
1319
+ this.modelGetter = getModel;
1320
+ this.logger.debug(`Initialized ${this.constructor.name}`);
1321
+ }
1322
+ /**
1323
+ * Create a new record
1324
+ *
1325
+ * @param data - The data to create the record with
1326
+ * @returns Promise resolving to the created record
1327
+ *
1328
+ * @example
1329
+ * ```typescript
1330
+ * const user = await userRepository.create({
1331
+ * email: 'user@example.com',
1332
+ * name: 'John Doe'
1333
+ * });
1334
+ * ```
1335
+ */
1336
+ async create(data) {
1337
+ this.logger.log("Creating record");
1338
+ return await this.model.create({
1339
+ data
1340
+ });
1341
+ }
1342
+ /**
1343
+ * Find a single record by ID
1344
+ *
1345
+ * @param id - The record ID
1346
+ * @returns Promise resolving to the record or null if not found
1347
+ *
1348
+ * @example
1349
+ * ```typescript
1350
+ * const user = await userRepository.findById('user-id-123');
1351
+ * ```
1352
+ */
1353
+ async findById(id) {
1354
+ this.logger.debug(`Finding record by ID: ${id}`);
1355
+ return await this.model.findUnique({
1356
+ where: {
1357
+ id
1358
+ }
1359
+ });
1360
+ }
1361
+ /**
1362
+ * Find a single record with custom where clause
1363
+ *
1364
+ * @param where - The where clause or findUnique args
1365
+ * @returns Promise resolving to the record or null if not found
1366
+ *
1367
+ * @example
1368
+ * ```typescript
1369
+ * // Simple where clause
1370
+ * const user = await userRepository.findOne({ email: 'user@example.com' });
1371
+ *
1372
+ * // With include
1373
+ * const user = await userRepository.findOne({
1374
+ * where: { email: 'user@example.com' },
1375
+ * include: { posts: true }
1376
+ * });
1377
+ * ```
1378
+ */
1379
+ async findOne(where) {
1380
+ this.logger.debug("Finding record with custom query");
1381
+ return await this.model.findUnique(typeof where === "object" && "where" in where ? where : {
1382
+ where
1383
+ });
1384
+ }
1385
+ /**
1386
+ * Find multiple records
1387
+ *
1388
+ * @param args - Prisma findMany arguments (where, orderBy, take, skip, etc.)
1389
+ * @returns Promise resolving to an array of records
1390
+ *
1391
+ * @example
1392
+ * ```typescript
1393
+ * // Find all users
1394
+ * const users = await userRepository.findMany();
1395
+ *
1396
+ * // Find with filtering and pagination
1397
+ * const users = await userRepository.findMany({
1398
+ * where: { status: 'ACTIVE' },
1399
+ * orderBy: { createdAt: 'desc' },
1400
+ * take: 10,
1401
+ * skip: 0
1402
+ * });
1403
+ * ```
1404
+ */
1405
+ async findMany(args) {
1406
+ this.logger.debug("Finding multiple records");
1407
+ return await this.model.findMany(args);
1408
+ }
1409
+ /**
1410
+ * Update a record by ID
1411
+ *
1412
+ * @param id - The record ID
1413
+ * @param data - The data to update
1414
+ * @returns Promise resolving to the updated record
1415
+ *
1416
+ * @example
1417
+ * ```typescript
1418
+ * const user = await userRepository.update('user-id-123', {
1419
+ * name: 'Jane Doe'
1420
+ * });
1421
+ * ```
1422
+ */
1423
+ async update(id, data) {
1424
+ this.logger.log(`Updating record with ID: ${id}`);
1425
+ return await this.model.update({
1426
+ where: {
1427
+ id
1428
+ },
1429
+ data
1430
+ });
1431
+ }
1432
+ /**
1433
+ * Update multiple records
1434
+ *
1435
+ * @param where - The where clause to match records
1436
+ * @param data - The data to update
1437
+ * @returns Promise resolving to the count of updated records
1438
+ *
1439
+ * @example
1440
+ * ```typescript
1441
+ * const result = await userRepository.updateMany(
1442
+ * { status: 'PENDING' },
1443
+ * { status: 'ACTIVE' }
1444
+ * );
1445
+ * console.log(`Updated ${result.count} users`);
1446
+ * ```
1447
+ */
1448
+ async updateMany(where, data) {
1449
+ this.logger.log("Updating multiple records");
1450
+ return await this.model.updateMany({
1451
+ where,
1452
+ data
1453
+ });
1454
+ }
1455
+ /**
1456
+ * Delete a record by ID
1457
+ *
1458
+ * @param id - The record ID
1459
+ * @returns Promise resolving to the deleted record
1460
+ *
1461
+ * @example
1462
+ * ```typescript
1463
+ * const user = await userRepository.delete('user-id-123');
1464
+ * ```
1465
+ */
1466
+ async delete(id) {
1467
+ this.logger.log(`Deleting record with ID: ${id}`);
1468
+ return await this.model.delete({
1469
+ where: {
1470
+ id
1471
+ }
1472
+ });
1473
+ }
1474
+ /**
1475
+ * Delete multiple records
1476
+ *
1477
+ * @param where - The where clause to match records
1478
+ * @returns Promise resolving to the count of deleted records
1479
+ *
1480
+ * @example
1481
+ * ```typescript
1482
+ * const result = await userRepository.deleteMany({
1483
+ * status: 'INACTIVE',
1484
+ * createdAt: { lt: new Date('2020-01-01') }
1485
+ * });
1486
+ * console.log(`Deleted ${result.count} users`);
1487
+ * ```
1488
+ */
1489
+ async deleteMany(where) {
1490
+ this.logger.log("Deleting multiple records");
1491
+ return await this.model.deleteMany({
1492
+ where
1493
+ });
1494
+ }
1495
+ /**
1496
+ * Count records
1497
+ *
1498
+ * @param where - Optional where clause to filter records
1499
+ * @returns Promise resolving to the count of records
1500
+ *
1501
+ * @example
1502
+ * ```typescript
1503
+ * // Count all users
1504
+ * const total = await userRepository.count();
1505
+ *
1506
+ * // Count active users
1507
+ * const activeCount = await userRepository.count({ status: 'ACTIVE' });
1508
+ * ```
1509
+ */
1510
+ async count(where) {
1511
+ this.logger.debug("Counting records");
1512
+ return await this.model.count({
1513
+ where
1514
+ });
1515
+ }
1516
+ /**
1517
+ * Check if a record exists
1518
+ *
1519
+ * @param where - The where clause to match records
1520
+ * @returns Promise resolving to true if at least one record exists, false otherwise
1521
+ *
1522
+ * @example
1523
+ * ```typescript
1524
+ * const emailExists = await userRepository.exists({
1525
+ * email: 'user@example.com'
1526
+ * });
1527
+ * ```
1528
+ */
1529
+ async exists(where) {
1530
+ const count = await this.model.count({
1531
+ where
1532
+ });
1533
+ return count > 0;
1534
+ }
1535
+ };
1536
+
1537
+ // src/database/repositories/tenant-base.repository.ts
1538
+ var import_common12 = require("@nestjs/common");
1539
+ var TenantBaseRepository = class {
1540
+ static {
1541
+ __name(this, "TenantBaseRepository");
1542
+ }
1543
+ database;
1544
+ logger;
1545
+ modelGetter;
1546
+ /**
1547
+ * Lazy getter for Prisma client.
1548
+ * Accesses the client from the database service only when needed,
1549
+ * avoiding initialization timing issues with NestJS lifecycle.
1550
+ */
1551
+ get prisma() {
1552
+ return this.database.prismaClient;
1553
+ }
1554
+ /**
1555
+ * Lazy getter for the Prisma model delegate.
1556
+ * Returns the specific model (e.g., prisma.product, prisma.order) for this repository.
1557
+ */
1558
+ get model() {
1559
+ return this.modelGetter(this.prisma);
1560
+ }
1561
+ /**
1562
+ * Create a new repository instance
1563
+ *
1564
+ * @param database - The tenant database service
1565
+ * @param getModel - Function that returns the Prisma model delegate from the client
1566
+ *
1567
+ * @example
1568
+ * ```typescript
1569
+ * // Standard usage with full parameter name
1570
+ * constructor(database: TenantDatabaseService) {
1571
+ * super(database, (prisma) => prisma.product);
1572
+ * }
1573
+ *
1574
+ * // Short syntax
1575
+ * constructor(database: TenantDatabaseService) {
1576
+ * super(database, (p) => p.product);
1577
+ * }
1578
+ *
1579
+ * // Complex model names
1580
+ * constructor(database: TenantDatabaseService) {
1581
+ * super(database, (p) => p.inventoryItem);
1582
+ * }
1583
+ * ```
1584
+ */
1585
+ constructor(database, getModel) {
1586
+ this.database = database;
1587
+ this.logger = new import_common12.Logger(this.constructor.name);
1588
+ this.modelGetter = getModel;
1589
+ this.logger.debug(`Initialized ${this.constructor.name}`);
1590
+ }
1591
+ /**
1592
+ * Create a new record
1593
+ *
1594
+ * @param data - The data to create the record with
1595
+ * @returns Promise resolving to the created record
1596
+ *
1597
+ * @example
1598
+ * ```typescript
1599
+ * const product = await productRepository.create({
1600
+ * name: 'Widget',
1601
+ * sku: 'WDG-001',
1602
+ * price: 9.99
1603
+ * });
1604
+ * ```
1605
+ */
1606
+ async create(data) {
1607
+ this.logger.log("Creating record");
1608
+ return await this.model.create({
1609
+ data
1610
+ });
1611
+ }
1612
+ /**
1613
+ * Find a single record by ID
1614
+ *
1615
+ * @param id - The record ID
1616
+ * @returns Promise resolving to the record or null if not found
1617
+ *
1618
+ * @example
1619
+ * ```typescript
1620
+ * const product = await productRepository.findById('product-id-123');
1621
+ * ```
1622
+ */
1623
+ async findById(id) {
1624
+ this.logger.debug(`Finding record by ID: ${id}`);
1625
+ return await this.model.findUnique({
1626
+ where: {
1627
+ id
1628
+ }
1629
+ });
1630
+ }
1631
+ /**
1632
+ * Find a single record with custom where clause
1633
+ *
1634
+ * @param where - The where clause or findUnique args
1635
+ * @returns Promise resolving to the record or null if not found
1636
+ *
1637
+ * @example
1638
+ * ```typescript
1639
+ * // Simple where clause
1640
+ * const product = await productRepository.findOne({ sku: 'WDG-001' });
1641
+ *
1642
+ * // With include
1643
+ * const product = await productRepository.findOne({
1644
+ * where: { sku: 'WDG-001' },
1645
+ * include: { category: true }
1646
+ * });
1647
+ * ```
1648
+ */
1649
+ async findOne(where) {
1650
+ this.logger.debug("Finding record with custom query");
1651
+ return await this.model.findUnique(typeof where === "object" && "where" in where ? where : {
1652
+ where
1653
+ });
1654
+ }
1655
+ /**
1656
+ * Find multiple records
1657
+ *
1658
+ * @param args - Prisma findMany arguments (where, orderBy, take, skip, etc.)
1659
+ * @returns Promise resolving to an array of records
1660
+ *
1661
+ * @example
1662
+ * ```typescript
1663
+ * // Find all products
1664
+ * const products = await productRepository.findMany();
1665
+ *
1666
+ * // Find with filtering and pagination
1667
+ * const products = await productRepository.findMany({
1668
+ * where: { status: 'ACTIVE' },
1669
+ * orderBy: { createdAt: 'desc' },
1670
+ * take: 10,
1671
+ * skip: 0
1672
+ * });
1673
+ * ```
1674
+ */
1675
+ async findMany(args) {
1676
+ this.logger.debug("Finding multiple records");
1677
+ return await this.model.findMany(args);
1678
+ }
1679
+ /**
1680
+ * Update a record by ID
1681
+ *
1682
+ * @param id - The record ID
1683
+ * @param data - The data to update
1684
+ * @returns Promise resolving to the updated record
1685
+ *
1686
+ * @example
1687
+ * ```typescript
1688
+ * const product = await productRepository.update('product-id-123', {
1689
+ * price: 12.99
1690
+ * });
1691
+ * ```
1692
+ */
1693
+ async update(id, data) {
1694
+ this.logger.log(`Updating record with ID: ${id}`);
1695
+ return await this.model.update({
1696
+ where: {
1697
+ id
1698
+ },
1699
+ data
1700
+ });
1701
+ }
1702
+ /**
1703
+ * Update multiple records
1704
+ *
1705
+ * @param where - The where clause to match records
1706
+ * @param data - The data to update
1707
+ * @returns Promise resolving to the count of updated records
1708
+ *
1709
+ * @example
1710
+ * ```typescript
1711
+ * const result = await productRepository.updateMany(
1712
+ * { status: 'PENDING' },
1713
+ * { status: 'ACTIVE' }
1714
+ * );
1715
+ * console.log(`Updated ${result.count} products`);
1716
+ * ```
1717
+ */
1718
+ async updateMany(where, data) {
1719
+ this.logger.log("Updating multiple records");
1720
+ return await this.model.updateMany({
1721
+ where,
1722
+ data
1723
+ });
1724
+ }
1725
+ /**
1726
+ * Delete a record by ID
1727
+ *
1728
+ * @param id - The record ID
1729
+ * @returns Promise resolving to the deleted record
1730
+ *
1731
+ * @example
1732
+ * ```typescript
1733
+ * const product = await productRepository.delete('product-id-123');
1734
+ * ```
1735
+ */
1736
+ async delete(id) {
1737
+ this.logger.log(`Deleting record with ID: ${id}`);
1738
+ return await this.model.delete({
1739
+ where: {
1740
+ id
1741
+ }
1742
+ });
1743
+ }
1744
+ /**
1745
+ * Delete multiple records
1746
+ *
1747
+ * @param where - The where clause to match records
1748
+ * @returns Promise resolving to the count of deleted records
1749
+ *
1750
+ * @example
1751
+ * ```typescript
1752
+ * const result = await productRepository.deleteMany({
1753
+ * status: 'INACTIVE',
1754
+ * createdAt: { lt: new Date('2020-01-01') }
1755
+ * });
1756
+ * console.log(`Deleted ${result.count} products`);
1757
+ * ```
1758
+ */
1759
+ async deleteMany(where) {
1760
+ this.logger.log("Deleting multiple records");
1761
+ return await this.model.deleteMany({
1762
+ where
1763
+ });
1764
+ }
1765
+ /**
1766
+ * Count records
1767
+ *
1768
+ * @param where - Optional where clause to filter records
1769
+ * @returns Promise resolving to the count of records
1770
+ *
1771
+ * @example
1772
+ * ```typescript
1773
+ * // Count all products
1774
+ * const total = await productRepository.count();
1775
+ *
1776
+ * // Count active products
1777
+ * const activeCount = await productRepository.count({ status: 'ACTIVE' });
1778
+ * ```
1779
+ */
1780
+ async count(where) {
1781
+ this.logger.debug("Counting records");
1782
+ return await this.model.count({
1783
+ where
1784
+ });
1785
+ }
1786
+ /**
1787
+ * Check if a record exists
1788
+ *
1789
+ * @param where - The where clause to match records
1790
+ * @returns Promise resolving to true if at least one record exists, false otherwise
1791
+ *
1792
+ * @example
1793
+ * ```typescript
1794
+ * const skuExists = await productRepository.exists({
1795
+ * sku: 'WDG-001'
1796
+ * });
1797
+ * ```
1798
+ */
1799
+ async exists(where) {
1800
+ const count = await this.model.count({
1801
+ where
1802
+ });
1803
+ return count > 0;
1804
+ }
1805
+ };
1806
+
1807
+ // src/database/decorators/tenant.decorator.ts
1808
+ var import_common13 = require("@nestjs/common");
1809
+ var Tenant = (0, import_common13.createParamDecorator)((data, ctx) => {
1244
1810
  const request = ctx.switchToHttp().getRequest();
1245
1811
  const tenantContext = request.app?.get?.(TenantContextService);
1246
1812
  if (!tenantContext) {
@@ -1250,20 +1816,239 @@ var Tenant = (0, import_common11.createParamDecorator)((data, ctx) => {
1250
1816
  });
1251
1817
 
1252
1818
  // src/auth/decorators/onboarding.decorator.ts
1253
- var import_common12 = require("@nestjs/common");
1254
- var Onboarding = /* @__PURE__ */ __name(() => (0, import_common12.SetMetadata)("isOnboarding", true), "Onboarding");
1819
+ var import_common14 = require("@nestjs/common");
1820
+ var Onboarding = /* @__PURE__ */ __name(() => (0, import_common14.SetMetadata)("isOnboarding", true), "Onboarding");
1255
1821
 
1256
1822
  // src/auth/decorators/public.decorator.ts
1257
- var import_common13 = require("@nestjs/common");
1258
- var Public = /* @__PURE__ */ __name(() => (0, import_common13.SetMetadata)("isPublic", true), "Public");
1823
+ var import_common15 = require("@nestjs/common");
1824
+ var Public = /* @__PURE__ */ __name(() => (0, import_common15.SetMetadata)("isPublic", true), "Public");
1825
+
1826
+ // src/http/http.module.ts
1827
+ var import_common17 = require("@nestjs/common");
1828
+
1829
+ // src/http/guards/csrf.guard.ts
1830
+ var import_common16 = require("@nestjs/common");
1831
+ var import_core6 = require("@nestjs/core");
1832
+ function _ts_decorate11(decorators, target, key, desc) {
1833
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1834
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1835
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1836
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1837
+ }
1838
+ __name(_ts_decorate11, "_ts_decorate");
1839
+ function _ts_metadata7(k, v) {
1840
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1841
+ }
1842
+ __name(_ts_metadata7, "_ts_metadata");
1843
+ var CsrfGuard = class _CsrfGuard {
1844
+ static {
1845
+ __name(this, "CsrfGuard");
1846
+ }
1847
+ reflector;
1848
+ logger = new import_common16.Logger(_CsrfGuard.name);
1849
+ constructor(reflector) {
1850
+ this.reflector = reflector;
1851
+ }
1852
+ async canActivate(context) {
1853
+ const request = context.switchToHttp().getRequest();
1854
+ const reply = context.switchToHttp().getResponse();
1855
+ const safeMethods = [
1856
+ "GET",
1857
+ "HEAD",
1858
+ "OPTIONS"
1859
+ ];
1860
+ if (safeMethods.includes(request.method)) {
1861
+ return true;
1862
+ }
1863
+ try {
1864
+ const fastifyInstance = request.server;
1865
+ if (!fastifyInstance.csrfProtection) {
1866
+ this.logger.error("CSRF protection plugin not found. Ensure @fastify/csrf-protection is registered.");
1867
+ throw new import_common16.ForbiddenException("CSRF protection not configured");
1868
+ }
1869
+ await new Promise((resolve, reject) => {
1870
+ fastifyInstance.csrfProtection(request, reply, (err) => {
1871
+ if (err) {
1872
+ reject(err);
1873
+ } else {
1874
+ resolve();
1875
+ }
1876
+ });
1877
+ });
1878
+ this.logger.debug(`CSRF validation successful for ${request.method} ${request.url}`);
1879
+ return true;
1880
+ } catch (error) {
1881
+ this.logger.warn(`CSRF validation failed for ${request.method} ${request.url}: ${error instanceof Error ? error.message : "Unknown error"}`);
1882
+ throw new import_common16.ForbiddenException({
1883
+ errors: [
1884
+ {
1885
+ field: "csrf",
1886
+ message: "Invalid or missing CSRF token"
1887
+ }
1888
+ ],
1889
+ message: "CSRF validation failed"
1890
+ });
1891
+ }
1892
+ }
1893
+ };
1894
+ CsrfGuard = _ts_decorate11([
1895
+ (0, import_common16.Injectable)(),
1896
+ _ts_metadata7("design:type", Function),
1897
+ _ts_metadata7("design:paramtypes", [
1898
+ typeof import_core6.Reflector === "undefined" ? Object : import_core6.Reflector
1899
+ ])
1900
+ ], CsrfGuard);
1901
+
1902
+ // src/http/http.module.ts
1903
+ function _ts_decorate12(decorators, target, key, desc) {
1904
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1905
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1906
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1907
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1908
+ }
1909
+ __name(_ts_decorate12, "_ts_decorate");
1910
+ var HttpModule = class {
1911
+ static {
1912
+ __name(this, "HttpModule");
1913
+ }
1914
+ };
1915
+ HttpModule = _ts_decorate12([
1916
+ (0, import_common17.Module)({
1917
+ providers: [
1918
+ CsrfGuard
1919
+ ],
1920
+ exports: [
1921
+ CsrfGuard
1922
+ ]
1923
+ })
1924
+ ], HttpModule);
1925
+
1926
+ // src/http/filters/http-exception.filter.ts
1927
+ var import_common18 = require("@nestjs/common");
1928
+ function _ts_decorate13(decorators, target, key, desc) {
1929
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1930
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1931
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1932
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1933
+ }
1934
+ __name(_ts_decorate13, "_ts_decorate");
1935
+ var HttpExceptionFilter = class _HttpExceptionFilter {
1936
+ static {
1937
+ __name(this, "HttpExceptionFilter");
1938
+ }
1939
+ logger = new import_common18.Logger(_HttpExceptionFilter.name);
1940
+ catch(exception, host) {
1941
+ const ctx = host.switchToHttp();
1942
+ const reply = ctx.getResponse();
1943
+ const request = ctx.getRequest();
1944
+ let status = import_common18.HttpStatus.INTERNAL_SERVER_ERROR;
1945
+ let errors = [];
1946
+ let message;
1947
+ if (exception instanceof import_common18.HttpException) {
1948
+ status = exception.getStatus();
1949
+ const exceptionResponse = exception.getResponse();
1950
+ if (typeof exceptionResponse === "object" && exceptionResponse !== null) {
1951
+ const responseObj = exceptionResponse;
1952
+ if (Array.isArray(responseObj.message)) {
1953
+ errors = this.parseValidationErrors(responseObj.message);
1954
+ message = "Validation failed";
1955
+ } else if (responseObj.message) {
1956
+ errors = [
1957
+ {
1958
+ field: "general",
1959
+ message: responseObj.message
1960
+ }
1961
+ ];
1962
+ message = responseObj.message;
1963
+ }
1964
+ } else if (typeof exceptionResponse === "string") {
1965
+ errors = [
1966
+ {
1967
+ field: "general",
1968
+ message: exceptionResponse
1969
+ }
1970
+ ];
1971
+ message = exceptionResponse;
1972
+ }
1973
+ } else if (exception instanceof Error) {
1974
+ this.logger.error(`Unhandled error: ${exception.message}`, exception.stack);
1975
+ errors = [
1976
+ {
1977
+ field: "general",
1978
+ message: "Internal server error"
1979
+ }
1980
+ ];
1981
+ message = "An unexpected error occurred";
1982
+ } else {
1983
+ this.logger.error("Unknown exception type", exception);
1984
+ errors = [
1985
+ {
1986
+ field: "general",
1987
+ message: "Internal server error"
1988
+ }
1989
+ ];
1990
+ message = "An unexpected error occurred";
1991
+ }
1992
+ const errorResponse = {
1993
+ errors,
1994
+ message,
1995
+ statusCode: status,
1996
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1997
+ path: request.url
1998
+ };
1999
+ if (status >= 500) {
2000
+ this.logger.error(`HTTP ${status} Error: ${JSON.stringify(errorResponse)}`, exception instanceof Error ? exception.stack : void 0);
2001
+ } else {
2002
+ this.logger.warn(`HTTP ${status} Error: ${JSON.stringify(errorResponse)}`);
2003
+ }
2004
+ reply.status(status).send(errorResponse);
2005
+ }
2006
+ /**
2007
+ * Parse class-validator error messages into field-specific errors
2008
+ */
2009
+ parseValidationErrors(messages) {
2010
+ const errors = [];
2011
+ for (const msg of messages) {
2012
+ if (typeof msg === "string") {
2013
+ errors.push({
2014
+ field: "general",
2015
+ message: msg
2016
+ });
2017
+ } else if (typeof msg === "object" && msg.property && msg.constraints) {
2018
+ const field = msg.property;
2019
+ const constraintMessages = Object.values(msg.constraints);
2020
+ for (const constraintMsg of constraintMessages) {
2021
+ errors.push({
2022
+ field,
2023
+ message: constraintMsg
2024
+ });
2025
+ }
2026
+ }
2027
+ }
2028
+ return errors.length > 0 ? errors : [
2029
+ {
2030
+ field: "general",
2031
+ message: "Validation failed"
2032
+ }
2033
+ ];
2034
+ }
2035
+ };
2036
+ HttpExceptionFilter = _ts_decorate13([
2037
+ (0, import_common18.Catch)()
2038
+ ], HttpExceptionFilter);
1259
2039
  // Annotate the CommonJS export names for ESM import in node:
1260
2040
  0 && (module.exports = {
1261
2041
  AuthConfigModule,
2042
+ CsrfGuard,
1262
2043
  DatabaseModule,
2044
+ HttpExceptionFilter,
2045
+ HttpModule,
1263
2046
  Onboarding,
2047
+ PrimaryBaseRepository,
1264
2048
  PrimaryDatabaseService,
1265
2049
  Public,
1266
2050
  Tenant,
2051
+ TenantBaseRepository,
1267
2052
  TenantContextService,
1268
2053
  TenantDatabaseService,
1269
2054
  VrittiAuthGuard