@vritti/api-sdk 0.1.6 → 0.1.8

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.d.ts CHANGED
@@ -511,6 +511,52 @@ declare class VrittiAuthGuard implements CanActivate {
511
511
  * @throws UnauthorizedException if token binding validation fails
512
512
  */
513
513
  private validateRefreshTokenBinding;
514
+ /**
515
+ * Validate CSRF token for state-changing requests
516
+ * Uses Fastify's csrf-protection plugin for token validation
517
+ *
518
+ * @param request - Fastify request object
519
+ * @param reply - Fastify reply object
520
+ * @throws ForbiddenException if CSRF validation fails
521
+ */
522
+ private validateCsrf;
523
+ }
524
+
525
+ /**
526
+ * SSE Authentication Guard - For Server-Sent Events endpoints
527
+ *
528
+ * This guard is specifically designed for SSE endpoints where:
529
+ * 1. Browser's EventSource API cannot send custom headers
530
+ * 2. Token must be passed via query parameter
531
+ * 3. CORS headers must be set before any response (including errors)
532
+ *
533
+ * Validation Flow:
534
+ * 1. Set CORS headers FIRST (ensures error responses include CORS)
535
+ * 2. Extract token from query param (?token=<jwt>)
536
+ * 3. Validate token is type='onboarding'
537
+ * 4. Attach user data to request.user
538
+ *
539
+ * Usage:
540
+ * ```typescript
541
+ * @Sse('events')
542
+ * @Public() // Bypass global VrittiAuthGuard
543
+ * @UseGuards(SseAuthGuard)
544
+ * async subscribeToEvents(@UserId() userId: string) { ... }
545
+ * ```
546
+ *
547
+ * Note: Must be used with @Public() to bypass the global VrittiAuthGuard
548
+ * since EventSource cannot send Authorization headers.
549
+ */
550
+ declare class SseAuthGuard implements CanActivate {
551
+ private readonly jwtService;
552
+ private readonly logger;
553
+ constructor(jwtService: JwtService);
554
+ canActivate(context: ExecutionContext): Promise<boolean>;
555
+ /**
556
+ * Set CORS headers for SSE responses
557
+ * Must be called before any potential exceptions
558
+ */
559
+ private setCorsHeaders;
514
560
  }
515
561
 
516
562
  /**
@@ -1614,21 +1660,87 @@ declare abstract class TenantBaseRepository<TTable extends PgTable, TInsert = In
1614
1660
  exists(where: SQL): Promise<boolean>;
1615
1661
  }
1616
1662
 
1663
+ /**
1664
+ * RFC 9457 Problem Details field-specific error structure.
1665
+ *
1666
+ * Used for validation errors or other field-specific issues.
1667
+ * The `field` property is required to ensure clear association.
1668
+ */
1617
1669
  interface FieldError {
1618
- field?: string;
1670
+ /** The field name (e.g., 'email', 'password') - REQUIRED */
1671
+ field: string;
1672
+ /** The error message for this field */
1619
1673
  message: string;
1620
1674
  }
1675
+ /**
1676
+ * RFC 9457 Problem Details standard fields.
1677
+ *
1678
+ * @see https://www.rfc-editor.org/rfc/rfc9457.html
1679
+ */
1621
1680
  interface ProblemDetails {
1681
+ /** Problem type URI (default: "about:blank") */
1682
+ type: string;
1683
+ /** HTTP status phrase (e.g., "Unauthorized", "Not Found") */
1622
1684
  title: string;
1685
+ /** HTTP status code */
1623
1686
  status: number;
1687
+ /** Root error heading (extension member, maps to AlertTitle in frontend) */
1688
+ label?: string;
1689
+ /** Detailed error description (maps to AlertDescription in frontend) */
1624
1690
  detail: string;
1691
+ /** Request path where the error occurred */
1692
+ instance?: string;
1625
1693
  }
1694
+ /**
1695
+ * Complete API error response following RFC 9457 Problem Details format.
1696
+ *
1697
+ * Extends ProblemDetails with field-specific errors.
1698
+ */
1626
1699
  interface ApiErrorResponse extends ProblemDetails {
1700
+ /** Field-specific errors (field is required in each FieldError) */
1627
1701
  errors: FieldError[];
1628
1702
  }
1629
1703
 
1630
- declare abstract class BaseFieldException extends HttpException {
1631
- constructor(statusOrMessageOrErrors: HttpStatus | string | FieldError[], messageOrStatus?: string | HttpStatus, statusOrDetail?: HttpStatus | string, detail?: string);
1704
+ /**
1705
+ * Options for creating RFC 9457 Problem Details exceptions.
1706
+ *
1707
+ * @example
1708
+ * throw new UnauthorizedException({
1709
+ * label: 'Invalid Credentials',
1710
+ * detail: 'The email or password is incorrect',
1711
+ * });
1712
+ *
1713
+ * @example
1714
+ * throw new BadRequestException({
1715
+ * detail: 'Validation failed',
1716
+ * errors: [
1717
+ * { field: 'email', message: 'Invalid email format' },
1718
+ * { field: 'password', message: 'Password too short' },
1719
+ * ],
1720
+ * });
1721
+ */
1722
+ interface ProblemOptions {
1723
+ /** Problem type URI (default: "about:blank") */
1724
+ type?: string;
1725
+ /** Root error heading (maps to AlertTitle in frontend) */
1726
+ label?: string;
1727
+ /** Root error description (maps to AlertDescription in frontend) */
1728
+ detail?: string;
1729
+ /** Field-specific errors only (field is required) */
1730
+ errors?: FieldError[];
1731
+ }
1732
+ /**
1733
+ * Base exception class that follows RFC 9457 Problem Details format.
1734
+ *
1735
+ * Provides a clean interface for creating HTTP exceptions with:
1736
+ * - RFC 9457 standard fields (type, title, status, detail, instance)
1737
+ * - Extension members (label for root error heading, errors for field-specific errors)
1738
+ *
1739
+ * The `title` field is always set to the HTTP status phrase (e.g., "Unauthorized")
1740
+ * by the HttpExceptionFilter, not by this class.
1741
+ */
1742
+ declare abstract class HttpProblemException extends HttpException {
1743
+ constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus);
1632
1744
  }
1633
1745
 
1634
1746
  /**
@@ -1637,21 +1749,17 @@ declare abstract class BaseFieldException extends HttpException {
1637
1749
  *
1638
1750
  * @example
1639
1751
  * // Simple message
1640
- * throw new BadGatewayException('Bad gateway');
1641
- *
1642
- * // Field-specific error
1643
- * throw new BadGatewayException('upstream', 'Upstream service returned invalid response');
1644
- *
1645
- * // With detail
1646
- * throw new BadGatewayException('proxy', 'Gateway error', 'Payment service is not responding correctly');
1752
+ * throw new BadGatewayException('Upstream service returned invalid response');
1647
1753
  *
1648
- * // Multiple field errors
1649
- * throw new BadGatewayException([
1650
- * { field: 'gateway', message: 'Invalid response from upstream server' }
1651
- * ]);
1754
+ * // With options
1755
+ * throw new BadGatewayException({
1756
+ * title: 'Upstream Service Error',
1757
+ * detail: 'The payment service is not responding correctly',
1758
+ * instance: '/api/payments/process',
1759
+ * });
1652
1760
  */
1653
- declare class BadGatewayException extends BaseFieldException {
1654
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1761
+ declare class BadGatewayException extends HttpProblemException {
1762
+ constructor(detailOrOptions?: string | ProblemOptions);
1655
1763
  }
1656
1764
 
1657
1765
  /**
@@ -1661,20 +1769,24 @@ declare class BadGatewayException extends BaseFieldException {
1661
1769
  * // Simple message
1662
1770
  * throw new BadRequestException('Invalid request data');
1663
1771
  *
1664
- * // Field-specific error
1665
- * throw new BadRequestException('email', 'Invalid email format');
1666
- *
1667
- * // With detail
1668
- * throw new BadRequestException('email', 'Invalid email format', 'Email must be in valid format');
1772
+ * // With field errors
1773
+ * throw new BadRequestException({
1774
+ * detail: 'Validation failed',
1775
+ * errors: [
1776
+ * { field: 'email', message: 'Invalid email format' },
1777
+ * { field: 'password', message: 'Password too short' }
1778
+ * ]
1779
+ * });
1669
1780
  *
1670
- * // Multiple field errors
1671
- * throw new BadRequestException([
1672
- * { field: 'email', message: 'Invalid email' },
1673
- * { field: 'password', message: 'Password too short' }
1674
- * ]);
1781
+ * // With custom label and type
1782
+ * throw new BadRequestException({
1783
+ * label: 'Invalid Form Data',
1784
+ * detail: 'Please check your input',
1785
+ * type: 'validation-error'
1786
+ * });
1675
1787
  */
1676
- declare class BadRequestException extends BaseFieldException {
1677
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1788
+ declare class BadRequestException extends HttpProblemException {
1789
+ constructor(detailOrOptions?: string | ProblemOptions);
1678
1790
  }
1679
1791
 
1680
1792
  /**
@@ -1682,44 +1794,56 @@ declare class BadRequestException extends BaseFieldException {
1682
1794
  * Commonly used for duplicate resources or concurrent modification issues.
1683
1795
  *
1684
1796
  * @example
1685
- * // Simple message
1797
+ * // Simple detail message
1686
1798
  * throw new ConflictException('Resource already exists');
1687
1799
  *
1688
- * // Field-specific error
1689
- * throw new ConflictException('email', 'Email already registered');
1800
+ * // With custom label and detail
1801
+ * throw new ConflictException({
1802
+ * label: 'Duplicate Entry',
1803
+ * detail: 'Email already exists',
1804
+ * });
1690
1805
  *
1691
- * // With detail
1692
- * throw new ConflictException('email', 'Email already exists', 'Try logging in instead');
1806
+ * // With field-specific errors
1807
+ * throw new ConflictException({
1808
+ * detail: 'Duplicate data detected',
1809
+ * errors: [
1810
+ * { field: 'email', message: 'Email already registered' }
1811
+ * ],
1812
+ * });
1693
1813
  *
1694
- * // Multiple field errors
1695
- * throw new ConflictException([
1696
- * { field: 'email', message: 'Email already in use' }
1697
- * ]);
1814
+ * // With custom label and field errors
1815
+ * throw new ConflictException({
1816
+ * label: 'Resource Conflict',
1817
+ * detail: 'Try logging in instead or use a different email',
1818
+ * errors: [{ field: 'email', message: 'Email already in use' }],
1819
+ * });
1698
1820
  */
1699
- declare class ConflictException extends BaseFieldException {
1700
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1821
+ declare class ConflictException extends HttpProblemException {
1822
+ constructor(detailOrOptions?: string | ProblemOptions);
1701
1823
  }
1702
1824
 
1703
1825
  /**
1704
1826
  * Exception thrown when the user does not have permission to access a resource (HTTP 403).
1705
1827
  *
1706
1828
  * @example
1707
- * // Simple message
1829
+ * // Simple detail message
1708
1830
  * throw new ForbiddenException('Access denied');
1709
1831
  *
1710
- * // Field-specific error
1711
- * throw new ForbiddenException('resource', 'You do not have permission');
1712
- *
1713
- * // With detail
1714
- * throw new ForbiddenException('resource', 'Access denied', 'Admin role required');
1832
+ * // With custom label
1833
+ * throw new ForbiddenException({
1834
+ * label: 'Access Denied',
1835
+ * detail: 'You do not have permission to perform this action',
1836
+ * });
1715
1837
  *
1716
- * // Multiple field errors
1717
- * throw new ForbiddenException([
1718
- * { field: 'action', message: 'Insufficient permissions' }
1719
- * ]);
1838
+ * // With field-specific errors
1839
+ * throw new ForbiddenException({
1840
+ * label: 'Permission Denied',
1841
+ * detail: 'Contact your administrator for access',
1842
+ * errors: [{ field: 'role', message: 'Admin role required' }],
1843
+ * });
1720
1844
  */
1721
- declare class ForbiddenException extends BaseFieldException {
1722
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1845
+ declare class ForbiddenException extends HttpProblemException {
1846
+ constructor(detailOrOptions?: string | ProblemOptions);
1723
1847
  }
1724
1848
 
1725
1849
  /**
@@ -1730,19 +1854,21 @@ declare class ForbiddenException extends BaseFieldException {
1730
1854
  * // Simple message
1731
1855
  * throw new GoneException('Resource permanently deleted');
1732
1856
  *
1733
- * // Field-specific error
1734
- * throw new GoneException('account', 'Account has been permanently deleted');
1735
- *
1736
- * // With detail
1737
- * throw new GoneException('account', 'Deleted', 'This account was removed on user request');
1857
+ * // With label and detail
1858
+ * throw new GoneException({
1859
+ * label: 'Account Deleted',
1860
+ * detail: 'This account has been permanently removed',
1861
+ * });
1738
1862
  *
1739
- * // Multiple field errors
1740
- * throw new GoneException([
1741
- * { field: 'resource', message: 'This content has been permanently removed' }
1742
- * ]);
1863
+ * // With field errors
1864
+ * throw new GoneException({
1865
+ * label: 'Resource Removed',
1866
+ * detail: 'The resource was removed due to policy violation',
1867
+ * errors: [{ field: 'resource', message: 'This content has been permanently deleted' }],
1868
+ * });
1743
1869
  */
1744
- declare class GoneException extends BaseFieldException {
1745
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1870
+ declare class GoneException extends HttpProblemException {
1871
+ constructor(detailOrOptions?: string | ProblemOptions);
1746
1872
  }
1747
1873
 
1748
1874
  /**
@@ -1752,19 +1878,15 @@ declare class GoneException extends BaseFieldException {
1752
1878
  * // Simple message
1753
1879
  * throw new InternalServerErrorException('An unexpected error occurred');
1754
1880
  *
1755
- * // Field-specific error
1756
- * throw new InternalServerErrorException('database', 'Database connection failed');
1757
- *
1758
- * // With detail
1759
- * throw new InternalServerErrorException('database', 'Connection failed', 'Please try again later');
1760
- *
1761
- * // Multiple field errors
1762
- * throw new InternalServerErrorException([
1763
- * { field: 'system', message: 'Internal error' }
1764
- * ]);
1881
+ * // With options object
1882
+ * throw new InternalServerErrorException({
1883
+ * title: 'Server Error',
1884
+ * detail: 'Something went wrong',
1885
+ * instance: '/api/users',
1886
+ * });
1765
1887
  */
1766
- declare class InternalServerErrorException extends BaseFieldException {
1767
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1888
+ declare class InternalServerErrorException extends HttpProblemException {
1889
+ constructor(detailOrOptions?: string | ProblemOptions);
1768
1890
  }
1769
1891
 
1770
1892
  /**
@@ -1775,19 +1897,28 @@ declare class InternalServerErrorException extends BaseFieldException {
1775
1897
  * // Simple message
1776
1898
  * throw new MethodNotAllowedException('Method not allowed');
1777
1899
  *
1778
- * // Field-specific error
1779
- * throw new MethodNotAllowedException('method', 'POST method not allowed on this endpoint');
1780
- *
1781
1900
  * // With detail
1782
- * throw new MethodNotAllowedException('method', 'Not allowed', 'Only GET and PUT are supported');
1901
+ * throw new MethodNotAllowedException({
1902
+ * detail: 'Method not allowed',
1903
+ * instance: '/api/resource/123'
1904
+ * });
1905
+ *
1906
+ * // With custom title
1907
+ * throw new MethodNotAllowedException({
1908
+ * title: 'Invalid HTTP Method',
1909
+ * detail: 'This endpoint only supports GET requests',
1910
+ * });
1783
1911
  *
1784
- * // Multiple field errors
1785
- * throw new MethodNotAllowedException([
1786
- * { field: 'method', message: 'DELETE is not allowed on this resource' }
1787
- * ]);
1912
+ * // With additional context
1913
+ * throw new MethodNotAllowedException({
1914
+ * title: 'Unsupported Operation',
1915
+ * detail: 'PATCH is not supported for this resource',
1916
+ * instance: '/api/users/456',
1917
+ * extensions: { allowedMethods: ['GET', 'PUT', 'DELETE'] }
1918
+ * });
1788
1919
  */
1789
- declare class MethodNotAllowedException extends BaseFieldException {
1790
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1920
+ declare class MethodNotAllowedException extends HttpProblemException {
1921
+ constructor(detailOrOptions?: string | ProblemOptions);
1791
1922
  }
1792
1923
 
1793
1924
  /**
@@ -1795,22 +1926,34 @@ declare class MethodNotAllowedException extends BaseFieldException {
1795
1926
  * Used when the server cannot produce a response matching the Accept headers.
1796
1927
  *
1797
1928
  * @example
1798
- * // Simple message
1929
+ * // Simple detail message
1799
1930
  * throw new NotAcceptableException('Requested format not available');
1800
1931
  *
1801
- * // Field-specific error
1802
- * throw new NotAcceptableException('accept', 'Cannot produce response in requested format');
1932
+ * // With custom label
1933
+ * throw new NotAcceptableException({
1934
+ * label: 'Content Negotiation Failed',
1935
+ * detail: 'Cannot produce response in the requested format',
1936
+ * });
1803
1937
  *
1804
- * // With detail
1805
- * throw new NotAcceptableException('accept', 'Format not supported', 'Only JSON is available');
1938
+ * // With field-specific errors
1939
+ * throw new NotAcceptableException({
1940
+ * detail: 'Requested format is not supported',
1941
+ * errors: [
1942
+ * { field: 'accept', message: 'XML format is not available' },
1943
+ * { field: 'contentType', message: 'Only JSON is supported' },
1944
+ * ],
1945
+ * });
1806
1946
  *
1807
- * // Multiple field errors
1808
- * throw new NotAcceptableException([
1809
- * { field: 'contentType', message: 'XML format is not supported' }
1810
- * ]);
1947
+ * // With all options
1948
+ * throw new NotAcceptableException({
1949
+ * type: 'https://api.example.com/errors/format-not-supported',
1950
+ * label: 'Unsupported Media Type',
1951
+ * detail: 'This API only supports JSON responses',
1952
+ * errors: [{ field: 'accept', message: 'XML format is not available' }],
1953
+ * });
1811
1954
  */
1812
- declare class NotAcceptableException extends BaseFieldException {
1813
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1955
+ declare class NotAcceptableException extends HttpProblemException {
1956
+ constructor(detailOrOptions?: string | ProblemOptions);
1814
1957
  }
1815
1958
 
1816
1959
  /**
@@ -1820,19 +1963,20 @@ declare class NotAcceptableException extends BaseFieldException {
1820
1963
  * // Simple message
1821
1964
  * throw new NotFoundException('Resource not found');
1822
1965
  *
1823
- * // Field-specific error
1824
- * throw new NotFoundException('userId', 'User not found');
1825
- *
1826
- * // With detail
1827
- * throw new NotFoundException('userId', 'User not found', 'No user exists with the provided ID');
1966
+ * // With custom label and detail
1967
+ * throw new NotFoundException({
1968
+ * label: 'User Not Found',
1969
+ * detail: 'The requested user does not exist',
1970
+ * });
1828
1971
  *
1829
- * // Multiple field errors
1830
- * throw new NotFoundException([
1831
- * { field: 'userId', message: 'User does not exist' }
1832
- * ]);
1972
+ * // With field errors
1973
+ * throw new NotFoundException({
1974
+ * detail: 'The requested resource could not be located',
1975
+ * errors: [{ field: 'userId', message: 'User does not exist' }],
1976
+ * });
1833
1977
  */
1834
- declare class NotFoundException extends BaseFieldException {
1835
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1978
+ declare class NotFoundException extends HttpProblemException {
1979
+ constructor(detailOrOptions?: string | ProblemOptions);
1836
1980
  }
1837
1981
 
1838
1982
  /**
@@ -1843,19 +1987,14 @@ declare class NotFoundException extends BaseFieldException {
1843
1987
  * // Simple message
1844
1988
  * throw new NotImplementedException('Feature not yet implemented');
1845
1989
  *
1846
- * // Field-specific error
1847
- * throw new NotImplementedException('feature', 'This feature is coming soon');
1848
- *
1849
- * // With detail
1850
- * throw new NotImplementedException('export', 'Not implemented', 'PDF export will be available in v2.0');
1851
- *
1852
- * // Multiple field errors
1853
- * throw new NotImplementedException([
1854
- * { field: 'functionality', message: 'This functionality is not available yet' }
1855
- * ]);
1990
+ * // With options
1991
+ * throw new NotImplementedException({
1992
+ * detail: 'This feature is coming soon',
1993
+ * instance: '/api/v1/export',
1994
+ * });
1856
1995
  */
1857
- declare class NotImplementedException extends BaseFieldException {
1858
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
1996
+ declare class NotImplementedException extends HttpProblemException {
1997
+ constructor(detailOrOptions?: string | ProblemOptions);
1859
1998
  }
1860
1999
 
1861
2000
  /**
@@ -1866,19 +2005,21 @@ declare class NotImplementedException extends BaseFieldException {
1866
2005
  * // Simple message
1867
2006
  * throw new PayloadTooLargeException('Request payload too large');
1868
2007
  *
1869
- * // Field-specific error
1870
- * throw new PayloadTooLargeException('file', 'File size exceeds maximum allowed');
1871
- *
1872
2008
  * // With detail
1873
- * throw new PayloadTooLargeException('file', 'File too large', 'Maximum size is 10MB');
2009
+ * throw new PayloadTooLargeException({
2010
+ * detail: 'Request payload too large',
2011
+ * instance: '/api/upload',
2012
+ * });
1874
2013
  *
1875
- * // Multiple field errors
1876
- * throw new PayloadTooLargeException([
1877
- * { field: 'upload', message: 'File exceeds 10MB limit' }
1878
- * ]);
2014
+ * // With custom title
2015
+ * throw new PayloadTooLargeException({
2016
+ * title: 'File Size Limit Exceeded',
2017
+ * detail: 'The uploaded file is too large. Maximum size is 10MB',
2018
+ * instance: '/api/files/upload',
2019
+ * });
1879
2020
  */
1880
- declare class PayloadTooLargeException extends BaseFieldException {
1881
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
2021
+ declare class PayloadTooLargeException extends HttpProblemException {
2022
+ constructor(detailOrOptions?: string | ProblemOptions);
1882
2023
  }
1883
2024
 
1884
2025
  /**
@@ -1889,19 +2030,21 @@ declare class PayloadTooLargeException extends BaseFieldException {
1889
2030
  * // Simple message
1890
2031
  * throw new RequestTimeoutException('Request timeout');
1891
2032
  *
1892
- * // Field-specific error
1893
- * throw new RequestTimeoutException('operation', 'Operation timed out');
1894
- *
1895
- * // With detail
1896
- * throw new RequestTimeoutException('query', 'Database query timeout', 'Try with fewer filters');
2033
+ * // With custom title and detail
2034
+ * throw new RequestTimeoutException({
2035
+ * title: 'Operation Timeout',
2036
+ * detail: 'The request took too long to complete',
2037
+ * });
1897
2038
  *
1898
- * // Multiple field errors
1899
- * throw new RequestTimeoutException([
1900
- * { field: 'processing', message: 'Request took too long to complete' }
1901
- * ]);
2039
+ * // With instance for tracking
2040
+ * throw new RequestTimeoutException({
2041
+ * title: 'Database Timeout',
2042
+ * detail: 'Query execution exceeded time limit',
2043
+ * instance: '/api/queries/123',
2044
+ * });
1902
2045
  */
1903
- declare class RequestTimeoutException extends BaseFieldException {
1904
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
2046
+ declare class RequestTimeoutException extends HttpProblemException {
2047
+ constructor(detailOrOptions?: string | ProblemOptions);
1905
2048
  }
1906
2049
 
1907
2050
  /**
@@ -1912,19 +2055,21 @@ declare class RequestTimeoutException extends BaseFieldException {
1912
2055
  * // Simple message
1913
2056
  * throw new ServiceUnavailableException('Service temporarily unavailable');
1914
2057
  *
1915
- * // Field-specific error
1916
- * throw new ServiceUnavailableException('service', 'Scheduled maintenance in progress');
1917
- *
1918
- * // With detail
1919
- * throw new ServiceUnavailableException('service', 'Maintenance', 'Service will be back at 2 PM EST');
2058
+ * // With custom title and detail
2059
+ * throw new ServiceUnavailableException({
2060
+ * title: 'Scheduled Maintenance',
2061
+ * detail: 'Expected completion: 2 PM EST',
2062
+ * });
1920
2063
  *
1921
- * // Multiple field errors
1922
- * throw new ServiceUnavailableException([
1923
- * { field: 'database', message: 'Database is temporarily unavailable' }
1924
- * ]);
2064
+ * // With field errors
2065
+ * throw new ServiceUnavailableException({
2066
+ * title: 'External Service Unavailable',
2067
+ * detail: 'Payment service is down',
2068
+ * errors: [{ field: 'paymentGateway', message: 'Payment gateway unavailable' }],
2069
+ * });
1925
2070
  */
1926
- declare class ServiceUnavailableException extends BaseFieldException {
1927
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
2071
+ declare class ServiceUnavailableException extends HttpProblemException {
2072
+ constructor(detailOrOptions?: string | ProblemOptions);
1928
2073
  }
1929
2074
 
1930
2075
  /**
@@ -1935,19 +2080,30 @@ declare class ServiceUnavailableException extends BaseFieldException {
1935
2080
  * // Simple message
1936
2081
  * throw new TooManyRequestsException('Too many requests');
1937
2082
  *
1938
- * // Field-specific error
1939
- * throw new TooManyRequestsException('api', 'Rate limit exceeded');
2083
+ * // With custom title and detail
2084
+ * throw new TooManyRequestsException({
2085
+ * title: 'Rate Limit Exceeded',
2086
+ * detail: 'You have exceeded the allowed number of requests',
2087
+ * });
1940
2088
  *
1941
- * // With detail
1942
- * throw new TooManyRequestsException('api', 'Rate limit exceeded', 'Try again in 60 seconds');
2089
+ * // With field errors
2090
+ * throw new TooManyRequestsException({
2091
+ * title: 'API Throttled',
2092
+ * detail: 'Too many requests to this endpoint',
2093
+ * errors: [{ field: 'requests', message: 'Rate limit exceeded' }],
2094
+ * });
1943
2095
  *
1944
- * // Multiple field errors
1945
- * throw new TooManyRequestsException([
1946
- * { field: 'requests', message: 'Rate limit exceeded for this endpoint' }
1947
- * ]);
2096
+ * // With instance and additional metadata
2097
+ * throw new TooManyRequestsException({
2098
+ * detail: 'Rate limit exceeded',
2099
+ * instance: '/api/v1/users',
2100
+ * retryAfter: 60,
2101
+ * limit: 100,
2102
+ * remaining: 0,
2103
+ * });
1948
2104
  */
1949
- declare class TooManyRequestsException extends BaseFieldException {
1950
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
2105
+ declare class TooManyRequestsException extends HttpProblemException {
2106
+ constructor(detailOrOptions?: string | ProblemOptions);
1951
2107
  }
1952
2108
 
1953
2109
  /**
@@ -1957,19 +2113,14 @@ declare class TooManyRequestsException extends BaseFieldException {
1957
2113
  * // Simple message
1958
2114
  * throw new UnauthorizedException('Authentication required');
1959
2115
  *
1960
- * // Field-specific error
1961
- * throw new UnauthorizedException('token', 'Invalid or expired token');
1962
- *
1963
- * // With detail
1964
- * throw new UnauthorizedException('token', 'Invalid token', 'Please login again');
1965
- *
1966
- * // Multiple field errors
1967
- * throw new UnauthorizedException([
1968
- * { field: 'token', message: 'Token expired' }
1969
- * ]);
2116
+ * // With problem details
2117
+ * throw new UnauthorizedException({
2118
+ * detail: 'Invalid or expired token',
2119
+ * instance: '/api/auth/verify'
2120
+ * });
1970
2121
  */
1971
- declare class UnauthorizedException extends BaseFieldException {
1972
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
2122
+ declare class UnauthorizedException extends HttpProblemException {
2123
+ constructor(detailOrOptions?: string | ProblemOptions);
1973
2124
  }
1974
2125
 
1975
2126
  /**
@@ -1980,20 +2131,32 @@ declare class UnauthorizedException extends BaseFieldException {
1980
2131
  * // Simple message
1981
2132
  * throw new UnprocessableEntityException('Cannot process the request');
1982
2133
  *
1983
- * // Field-specific error
1984
- * throw new UnprocessableEntityException('age', 'Age must be 18 or older');
1985
- *
1986
2134
  * // With detail
1987
- * throw new UnprocessableEntityException('quantity', 'Insufficient stock', 'Only 5 items available');
2135
+ * throw new UnprocessableEntityException({
2136
+ * detail: 'Cannot process the order due to stock limitations',
2137
+ * });
2138
+ *
2139
+ * // With custom title
2140
+ * throw new UnprocessableEntityException({
2141
+ * title: 'Business Rule Violation',
2142
+ * detail: 'Cannot process the order due to stock limitations',
2143
+ * });
1988
2144
  *
1989
- * // Multiple field errors
1990
- * throw new UnprocessableEntityException([
1991
- * { field: 'startDate', message: 'Start date must be before end date' },
1992
- * { field: 'endDate', message: 'End date cannot be in the past' }
1993
- * ]);
2145
+ * // With field errors
2146
+ * throw new UnprocessableEntityException({
2147
+ * detail: 'One or more items exceed available inventory',
2148
+ * errors: [{ field: 'quantity', message: 'Insufficient stock available' }],
2149
+ * });
2150
+ *
2151
+ * // With custom title and field errors
2152
+ * throw new UnprocessableEntityException({
2153
+ * title: 'Validation Failed',
2154
+ * detail: 'One or more items exceed available inventory',
2155
+ * errors: [{ field: 'quantity', message: 'Insufficient stock available' }],
2156
+ * });
1994
2157
  */
1995
- declare class UnprocessableEntityException extends BaseFieldException {
1996
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
2158
+ declare class UnprocessableEntityException extends HttpProblemException {
2159
+ constructor(detailOrOptions?: string | ProblemOptions);
1997
2160
  }
1998
2161
 
1999
2162
  /**
@@ -2004,19 +2167,21 @@ declare class UnprocessableEntityException extends BaseFieldException {
2004
2167
  * // Simple message
2005
2168
  * throw new UnsupportedMediaTypeException('Unsupported media type');
2006
2169
  *
2007
- * // Field-specific error
2008
- * throw new UnsupportedMediaTypeException('contentType', 'XML is not supported');
2009
- *
2010
- * // With detail
2011
- * throw new UnsupportedMediaTypeException('contentType', 'Not supported', 'Only JSON and form-data are accepted');
2170
+ * // With label and detail
2171
+ * throw new UnsupportedMediaTypeException({
2172
+ * label: 'Invalid Content Type',
2173
+ * detail: 'The content type is not supported',
2174
+ * });
2012
2175
  *
2013
- * // Multiple field errors
2014
- * throw new UnsupportedMediaTypeException([
2015
- * { field: 'contentType', message: 'application/xml is not supported' }
2016
- * ]);
2176
+ * // With field errors
2177
+ * throw new UnsupportedMediaTypeException({
2178
+ * label: 'Unsupported File Format',
2179
+ * detail: 'Accepted formats: JPEG, PNG, GIF',
2180
+ * errors: [{ field: 'file', message: 'PDF format is not accepted for this upload' }],
2181
+ * });
2017
2182
  */
2018
- declare class UnsupportedMediaTypeException extends BaseFieldException {
2019
- constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
2183
+ declare class UnsupportedMediaTypeException extends HttpProblemException {
2184
+ constructor(detailOrOptions?: string | ProblemOptions);
2020
2185
  }
2021
2186
 
2022
2187
  /**
@@ -2024,20 +2189,36 @@ declare class UnsupportedMediaTypeException extends BaseFieldException {
2024
2189
  * Typically used for form validation or DTO validation errors.
2025
2190
  *
2026
2191
  * @example
2027
- * // Multiple validation errors
2028
- * throw new ValidationException([
2029
- * { field: 'email', message: 'Invalid email format' },
2030
- * { field: 'password', message: 'Password must be at least 8 characters' }
2031
- * ]);
2192
+ * // Simple message
2193
+ * throw new ValidationException('Validation failed');
2032
2194
  *
2033
- * // With detail
2034
- * throw new ValidationException(
2035
- * [{ field: 'email', message: 'Invalid format' }],
2036
- * 'Please correct the errors and try again'
2037
- * );
2195
+ * // With custom label and detail
2196
+ * throw new ValidationException({
2197
+ * label: 'Invalid Input',
2198
+ * detail: 'The provided data is invalid',
2199
+ * });
2200
+ *
2201
+ * // With field-specific errors
2202
+ * throw new ValidationException({
2203
+ * detail: 'Please correct the highlighted fields',
2204
+ * errors: [
2205
+ * { field: 'email', message: 'Invalid email format' },
2206
+ * { field: 'password', message: 'Password must be at least 8 characters' }
2207
+ * ],
2208
+ * });
2209
+ *
2210
+ * // With custom label and field errors
2211
+ * throw new ValidationException({
2212
+ * label: 'Form Validation Failed',
2213
+ * detail: 'Please correct the highlighted fields',
2214
+ * errors: [
2215
+ * { field: 'email', message: 'Invalid email format' },
2216
+ * { field: 'password', message: 'Password too weak' }
2217
+ * ],
2218
+ * });
2038
2219
  */
2039
- declare class ValidationException extends BaseFieldException {
2040
- constructor(errors: FieldError[], detail?: string);
2220
+ declare class ValidationException extends HttpProblemException {
2221
+ constructor(detailOrOptions?: string | ProblemOptions);
2041
2222
  }
2042
2223
 
2043
2224
  /**
@@ -2054,18 +2235,21 @@ declare class ValidationException extends BaseFieldException {
2054
2235
  */
2055
2236
  declare function getHttpStatusTitle(status: number): string;
2056
2237
  /**
2057
- * Global HTTP Exception Filter implementing RFC 7807 Problem Details
2238
+ * Global HTTP Exception Filter implementing RFC 9457 Problem Details
2058
2239
  *
2059
- * Transforms all exceptions into a standardized RFC 7807 format:
2240
+ * Transforms all exceptions into a standardized RFC 9457 format:
2060
2241
  * {
2061
- * title: string, // Human-readable status title
2242
+ * type: string, // Problem type URI (default: "about:blank")
2243
+ * title: string, // HTTP status phrase (e.g., "Unauthorized")
2062
2244
  * status: number, // HTTP status code
2063
- * detail: string, // Detailed error description
2064
- * errors: FieldError[] // Field-specific error messages
2245
+ * label?: string, // Root error heading (maps to AlertTitle)
2246
+ * detail: string, // Root error description (maps to AlertDescription)
2247
+ * instance: string, // Request path
2248
+ * errors: FieldError[] // Field-specific errors (field is required)
2065
2249
  * }
2066
2250
  *
2067
2251
  * Handles:
2068
- * - Custom field exceptions from @vritti/api-sdk (BaseFieldException)
2252
+ * - Custom HttpProblemException from @vritti/api-sdk
2069
2253
  * - Class-validator DTO validation errors
2070
2254
  * - Standard NestJS HTTP exceptions
2071
2255
  * - Unknown errors
@@ -2075,44 +2259,38 @@ declare class HttpExceptionFilter implements ExceptionFilter {
2075
2259
  catch(exception: unknown, host: ArgumentsHost): void;
2076
2260
  }
2077
2261
 
2262
+ declare const SKIP_CSRF_KEY = "skipCsrf";
2078
2263
  /**
2079
- * CSRF Guard
2080
- *
2081
- * Global guard that automatically protects all state-changing requests (POST, PUT, PATCH, DELETE)
2082
- * from CSRF attacks using Fastify's csrf-protection plugin.
2083
- *
2084
- * Flow:
2085
- * 1. Skip safe methods (GET, HEAD, OPTIONS)
2086
- * 2. Skip endpoints marked with @Public()
2087
- * 3. Validate CSRF token for all other requests
2264
+ * Decorator to skip CSRF validation for specific routes or controllers.
2265
+ * Use this for webhook endpoints that receive requests from external services
2266
+ * (e.g., WhatsApp, Twilio) which cannot include CSRF tokens.
2088
2267
  *
2089
- * Token Sources (in priority order by @fastify/csrf-protection):
2090
- * 1. req.headers['csrf-token']
2091
- * 2. req.headers['xsrf-token']
2092
- * 3. req.headers['x-csrf-token']
2093
- * 4. req.headers['x-xsrf-token']
2094
- * 5. req.body._csrf
2268
+ * @example
2269
+ * // Skip CSRF for entire controller
2270
+ * @Controller('webhooks')
2271
+ * @SkipCsrf()
2272
+ * export class WebhookController { ... }
2095
2273
  *
2096
- * This guard should be registered globally in main.ts after CSRF plugin registration.
2274
+ * @example
2275
+ * // Skip CSRF for specific route
2276
+ * @Post()
2277
+ * @SkipCsrf()
2278
+ * async handleWebhook() { ... }
2097
2279
  */
2098
- declare class CsrfGuard implements CanActivate {
2099
- private readonly logger;
2100
- canActivate(context: ExecutionContext): Promise<boolean>;
2101
- }
2280
+ declare const SkipCsrf: () => _nestjs_common.CustomDecorator<string>;
2102
2281
 
2103
2282
  /**
2104
- * HTTP Module
2105
- *
2106
- * Provides HTTP utilities including:
2107
- * - CSRF Guard for request protection
2108
- * - HTTP Exception Filter for standardized error responses
2109
- *
2110
- * Usage:
2111
- * Import this module to access HTTP guards and filters.
2112
- * Guards and filters are registered globally in the main application.
2283
+ * Extract ISO country code from E.164 phone number
2284
+ * @param phone Phone number in E.164 format (e.g., +919876543210)
2285
+ * @returns ISO 3166-1 alpha-2 country code (e.g., "IN") or undefined
2113
2286
  */
2114
- declare class HttpModule {
2115
- }
2287
+ declare function extractCountryFromPhone(phone: string): string | undefined;
2288
+ /**
2289
+ * Normalize phone number to E.164 format with + prefix
2290
+ * @param phone Phone number (with or without + prefix)
2291
+ * @returns Phone number in E.164 format
2292
+ */
2293
+ declare function normalizePhoneNumber(phone: string): string;
2116
2294
 
2117
2295
  /**
2118
2296
  * Supported log levels for the logging system.
@@ -2515,4 +2693,4 @@ declare function generateCorrelationId(): string;
2515
2693
  */
2516
2694
  declare function addCorrelationIdToResponse(reply: FastifyReply, correlationId: string, headerName?: string): void;
2517
2695
 
2518
- export { type ApiErrorResponse, type ApiSdkConfig, AuthConfigModule, BadGatewayException, BadRequestException, BaseFieldException, ConflictException, type CookieConfig, type CorrelationContext, CorrelationIdMiddleware, CsrfGuard, DEFAULT_CORRELATION_HEADER, DatabaseModule, type DatabaseModuleOptions, type FieldError, ForbiddenException, GoneException, type GuardConfig, HttpExceptionFilter, HttpLoggerInterceptor, type HttpLoggerOptions, HttpModule, InternalServerErrorException, type JwtConfig, LOGGER_MODULE_OPTIONS, type LogFormat, type LogLevel, type LogMetadata, LoggerModule, type LoggerModuleAsyncOptions, type LoggerModuleOptions, type LoggerOptionsFactory, LoggerService, MethodNotAllowedException, NotAcceptableException, NotFoundException, NotImplementedException, Onboarding, PayloadTooLargeException, PrimaryBaseRepository, PrimaryDatabaseService, type PrimaryDbConfig, type ProblemDetails, Public, type RegisteredSchema, RequestTimeoutException, ServiceUnavailableException, Tenant, TenantBaseRepository, TenantContextService, TenantDatabaseService, type TenantInfo, TooManyRequestsException, type TypedDrizzleClient, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, UserId, ValidationException, VrittiAuthGuard, addCorrelationIdToResponse, configureApiSdk, correlationStorage, defineConfig, generateCorrelationId, getConfig, getCorrelationContext, getHttpStatusTitle, getJwtExpiry, getRefreshCookieOptions, hashToken, resetConfig, runWithCorrelationContext, updateCorrelationContext, verifyTokenHash };
2696
+ export { type ApiErrorResponse, type ApiSdkConfig, AuthConfigModule, BadGatewayException, BadRequestException, ConflictException, type CookieConfig, type CorrelationContext, CorrelationIdMiddleware, DEFAULT_CORRELATION_HEADER, DatabaseModule, type DatabaseModuleOptions, type FieldError, ForbiddenException, GoneException, type GuardConfig, HttpExceptionFilter, HttpLoggerInterceptor, type HttpLoggerOptions, HttpProblemException, InternalServerErrorException, type JwtConfig, LOGGER_MODULE_OPTIONS, type LogFormat, type LogLevel, type LogMetadata, LoggerModule, type LoggerModuleAsyncOptions, type LoggerModuleOptions, type LoggerOptionsFactory, LoggerService, MethodNotAllowedException, NotAcceptableException, NotFoundException, NotImplementedException, Onboarding, PayloadTooLargeException, PrimaryBaseRepository, PrimaryDatabaseService, type PrimaryDbConfig, type ProblemDetails, type ProblemOptions, Public, type RegisteredSchema, RequestTimeoutException, SKIP_CSRF_KEY, ServiceUnavailableException, SkipCsrf, SseAuthGuard, Tenant, TenantBaseRepository, TenantContextService, TenantDatabaseService, type TenantInfo, TooManyRequestsException, type TypedDrizzleClient, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, UserId, ValidationException, VrittiAuthGuard, addCorrelationIdToResponse, configureApiSdk, correlationStorage, defineConfig, extractCountryFromPhone, generateCorrelationId, getConfig, getCorrelationContext, getHttpStatusTitle, getJwtExpiry, getRefreshCookieOptions, hashToken, normalizePhoneNumber, resetConfig, runWithCorrelationContext, updateCorrelationContext, verifyTokenHash };