@plyaz/db 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -0
- package/dist/adapters/drizzle/DrizzleAdapter.d.ts +269 -0
- package/dist/adapters/drizzle/DrizzleAdapter.d.ts.map +1 -0
- package/dist/adapters/index.d.ts +20 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/sql/SQLAdapter.d.ts +282 -0
- package/dist/adapters/sql/SQLAdapter.d.ts.map +1 -0
- package/dist/adapters/supabase/SupabaseAdapter.d.ts +305 -0
- package/dist/adapters/supabase/SupabaseAdapter.d.ts.map +1 -0
- package/dist/advanced/backup/BackupService.d.ts +159 -0
- package/dist/advanced/backup/BackupService.d.ts.map +1 -0
- package/dist/advanced/backup/index.d.ts +2 -0
- package/dist/advanced/backup/index.d.ts.map +1 -0
- package/dist/advanced/caching/CacheEvict.decorator.d.ts +3 -0
- package/dist/advanced/caching/CacheEvict.decorator.d.ts.map +1 -0
- package/dist/advanced/caching/Cacheable.decorator.d.ts +99 -0
- package/dist/advanced/caching/Cacheable.decorator.d.ts.map +1 -0
- package/dist/advanced/caching/RedisCache.d.ts +417 -0
- package/dist/advanced/caching/RedisCache.d.ts.map +1 -0
- package/dist/advanced/caching/index.d.ts +4 -0
- package/dist/advanced/caching/index.d.ts.map +1 -0
- package/dist/advanced/connection-pool/DynamicPool.d.ts +234 -0
- package/dist/advanced/connection-pool/DynamicPool.d.ts.map +1 -0
- package/dist/advanced/connection-pool/index.d.ts +2 -0
- package/dist/advanced/connection-pool/index.d.ts.map +1 -0
- package/dist/advanced/index.d.ts +8 -0
- package/dist/advanced/index.d.ts.map +1 -0
- package/dist/advanced/monitoring/AlertManager.d.ts +72 -0
- package/dist/advanced/monitoring/AlertManager.d.ts.map +1 -0
- package/dist/advanced/monitoring/MetricsCollector.d.ts +81 -0
- package/dist/advanced/monitoring/MetricsCollector.d.ts.map +1 -0
- package/dist/advanced/monitoring/index.d.ts +3 -0
- package/dist/advanced/monitoring/index.d.ts.map +1 -0
- package/dist/advanced/multi-tenancy/TenantContext.d.ts +52 -0
- package/dist/advanced/multi-tenancy/TenantContext.d.ts.map +1 -0
- package/dist/advanced/multi-tenancy/TenantRepository.d.ts +292 -0
- package/dist/advanced/multi-tenancy/TenantRepository.d.ts.map +1 -0
- package/dist/advanced/multi-tenancy/index.d.ts +3 -0
- package/dist/advanced/multi-tenancy/index.d.ts.map +1 -0
- package/dist/advanced/read-replica/ReadReplicaAdapter.d.ts +516 -0
- package/dist/advanced/read-replica/ReadReplicaAdapter.d.ts.map +1 -0
- package/dist/advanced/read-replica/ReadReplicaManager.d.ts +68 -0
- package/dist/advanced/read-replica/ReadReplicaManager.d.ts.map +1 -0
- package/dist/advanced/read-replica/UseReplica.decorator.d.ts +24 -0
- package/dist/advanced/read-replica/UseReplica.decorator.d.ts.map +1 -0
- package/dist/advanced/read-replica/index.d.ts +3 -0
- package/dist/advanced/read-replica/index.d.ts.map +1 -0
- package/dist/advanced/sharding/ShardKey.d.ts +80 -0
- package/dist/advanced/sharding/ShardKey.d.ts.map +1 -0
- package/dist/advanced/sharding/ShardRouter.d.ts +66 -0
- package/dist/advanced/sharding/ShardRouter.d.ts.map +1 -0
- package/dist/advanced/sharding/index.d.ts +3 -0
- package/dist/advanced/sharding/index.d.ts.map +1 -0
- package/dist/builder/query/index.d.ts +7 -0
- package/dist/builder/query/index.d.ts.map +1 -0
- package/dist/builder/query/orm.d.ts +22 -0
- package/dist/builder/query/orm.d.ts.map +1 -0
- package/dist/builder/query/sql.d.ts +29 -0
- package/dist/builder/query/sql.d.ts.map +1 -0
- package/dist/extensions/AuditExtension.d.ts +468 -0
- package/dist/extensions/AuditExtension.d.ts.map +1 -0
- package/dist/extensions/CachingAdapter.d.ts +451 -0
- package/dist/extensions/CachingAdapter.d.ts.map +1 -0
- package/dist/extensions/EncryptionExtension.d.ts +95 -0
- package/dist/extensions/EncryptionExtension.d.ts.map +1 -0
- package/dist/extensions/ReadReplicaAdapter.d.ts +32 -0
- package/dist/extensions/ReadReplicaAdapter.d.ts.map +1 -0
- package/dist/extensions/SoftDeleteExtension.d.ts +430 -0
- package/dist/extensions/SoftDeleteExtension.d.ts.map +1 -0
- package/dist/extensions/index.d.ts +79 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/factory/AdapterFactory.d.ts +111 -0
- package/dist/factory/AdapterFactory.d.ts.map +1 -0
- package/dist/factory/createDatabaseService.d.ts +121 -0
- package/dist/factory/createDatabaseService.d.ts.map +1 -0
- package/dist/index.cjs +8518 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +8480 -0
- package/dist/index.mjs.map +1 -0
- package/dist/repository/BaseRepository.d.ts +209 -0
- package/dist/repository/BaseRepository.d.ts.map +1 -0
- package/dist/repository/index.d.ts +80 -0
- package/dist/repository/index.d.ts.map +1 -0
- package/dist/security/index.cjs +118 -0
- package/dist/security/index.cjs.map +1 -0
- package/dist/security/index.d.ts +3 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.mjs +114 -0
- package/dist/security/index.mjs.map +1 -0
- package/dist/security/sanitizers/html.sanitizer.d.ts +31 -0
- package/dist/security/sanitizers/html.sanitizer.d.ts.map +1 -0
- package/dist/security/serializers/DataValidation.d.ts +34 -0
- package/dist/security/serializers/DataValidation.d.ts.map +1 -0
- package/dist/service/DatabaseService.d.ts +136 -0
- package/dist/service/DatabaseService.d.ts.map +1 -0
- package/dist/service/EventEmitter.d.ts +110 -0
- package/dist/service/EventEmitter.d.ts.map +1 -0
- package/dist/service/HealthManager.d.ts +166 -0
- package/dist/service/HealthManager.d.ts.map +1 -0
- package/dist/utils/ConfigMerger.d.ts +227 -0
- package/dist/utils/ConfigMerger.d.ts.map +1 -0
- package/dist/utils/databaseResultHelpers.d.ts +98 -0
- package/dist/utils/databaseResultHelpers.d.ts.map +1 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/normalizeDetails.d.ts +111 -0
- package/dist/utils/normalizeDetails.d.ts.map +1 -0
- package/dist/utils/pagination.d.ts +77 -0
- package/dist/utils/pagination.d.ts.map +1 -0
- package/dist/utils/regex.d.ts +199 -0
- package/dist/utils/regex.d.ts.map +1 -0
- package/dist/utils/typeGuards.d.ts +57 -0
- package/dist/utils/typeGuards.d.ts.map +1 -0
- package/dist/utils/validation.d.ts +146 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/package.json +156 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Base Repository for @plyaz/db package
|
|
3
|
+
*
|
|
4
|
+
* This module provides the BaseRepository abstract class that serves as the foundation
|
|
5
|
+
* for all domain-specific repositories in the @plyaz/db package. It provides type-safe
|
|
6
|
+
* CRUD operations and consistent interface patterns.
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
import type { DatabaseResult, PaginatedResult, QueryOptions, Filter, DatabaseServiceType } from "@plyaz/types/db";
|
|
10
|
+
/**
|
|
11
|
+
* BASE REPOSITORY - Repository Layer Foundation
|
|
12
|
+
*
|
|
13
|
+
* Base repository providing common CRUD operations for domain entities.
|
|
14
|
+
* All domain-specific repositories extend this class for type-safe database operations.
|
|
15
|
+
*
|
|
16
|
+
* **Application Flow Position:**
|
|
17
|
+
* Service Layer � **Repository Layer** � DatabaseService � Adapter Chain
|
|
18
|
+
*
|
|
19
|
+
* **What this class provides:**
|
|
20
|
+
* - Type-safe CRUD operations for domain entities
|
|
21
|
+
* - Consistent interface across all repositories
|
|
22
|
+
* - Delegation to DatabaseService with proper table mapping
|
|
23
|
+
* - Foundation for domain-specific repository methods
|
|
24
|
+
*
|
|
25
|
+
* **Called by:** Service layer (UserService, OrderService, etc.)
|
|
26
|
+
* **Calls:** DatabaseService methods (get, create, update, delete, etc.)
|
|
27
|
+
* **Extended by:** Domain repositories (UserRepository, OrderRepository, etc.)
|
|
28
|
+
*
|
|
29
|
+
* @template T The entity type this repository manages
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ### Creating a Domain Repository
|
|
33
|
+
* ```typescript
|
|
34
|
+
* interface User {
|
|
35
|
+
* id: string;
|
|
36
|
+
* name: string;
|
|
37
|
+
* email: string;
|
|
38
|
+
* createdAt: Date;
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* class UserRepository extends BaseRepository<User> {
|
|
42
|
+
* constructor(db: IDatabaseService) {
|
|
43
|
+
* super(db, Tables.USERS);
|
|
44
|
+
* }
|
|
45
|
+
*
|
|
46
|
+
* // Domain-specific methods
|
|
47
|
+
* async findByEmail(email: string) {
|
|
48
|
+
* return this.db.query(this.tableName, {
|
|
49
|
+
* filter: { field: 'email', operator: 'eq', value: email }
|
|
50
|
+
* });
|
|
51
|
+
* }
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ### Usage in Service Layer
|
|
57
|
+
* ```typescript
|
|
58
|
+
* class UserService {
|
|
59
|
+
* constructor(private userRepo: UserRepository) {}
|
|
60
|
+
*
|
|
61
|
+
* async getUserById(id: string) {
|
|
62
|
+
* // Calls BaseRepository.findById() � DatabaseService.get()
|
|
63
|
+
* return this.userRepo.findById(id);
|
|
64
|
+
* }
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare abstract class BaseRepository<T> {
|
|
69
|
+
protected readonly db: DatabaseServiceType;
|
|
70
|
+
protected readonly tableName: string;
|
|
71
|
+
constructor(db: DatabaseServiceType, tableName: string);
|
|
72
|
+
/**
|
|
73
|
+
* Find a single entity by its primary key ID
|
|
74
|
+
*
|
|
75
|
+
* @param {string} id - The primary key ID of the entity to retrieve
|
|
76
|
+
* @returns {Promise<DatabaseResult<T | null>>} Promise resolving to the entity or null if not found
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const result = await userRepository.findById('user-123');
|
|
81
|
+
* if (result.success && result.value) {
|
|
82
|
+
* console.log('Found user:', result.value.name);
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
findById(id: string): Promise<DatabaseResult<T | null>>;
|
|
87
|
+
/**
|
|
88
|
+
* Find multiple entities with optional filtering, sorting, and pagination
|
|
89
|
+
*
|
|
90
|
+
* @param {QueryOptions} [options] - Optional query configuration
|
|
91
|
+
* @returns {Promise<DatabaseResult<PaginatedResult<T>>>} Promise resolving to paginated results
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* const result = await userRepository.findMany({
|
|
96
|
+
* filter: { field: 'status', operator: 'eq', value: 'active' },
|
|
97
|
+
* sort: [{ field: 'createdAt', direction: 'desc' }],
|
|
98
|
+
* pagination: { limit: 20, offset: 0 }
|
|
99
|
+
* });
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
findMany(options?: QueryOptions): Promise<DatabaseResult<PaginatedResult<T>>>;
|
|
103
|
+
/**
|
|
104
|
+
* Create a new entity in the database
|
|
105
|
+
*
|
|
106
|
+
* @param {T} data - The entity data to create
|
|
107
|
+
* @returns {Promise<DatabaseResult<T>>} Promise resolving to the created entity
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* const result = await userRepository.create({
|
|
112
|
+
* name: 'John Doe',
|
|
113
|
+
* email: 'john@example.com'
|
|
114
|
+
* });
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
create(data: T): Promise<DatabaseResult<T>>;
|
|
118
|
+
/**
|
|
119
|
+
* Update an existing entity by ID
|
|
120
|
+
*
|
|
121
|
+
* @param {string} id - The primary key ID of the entity to update
|
|
122
|
+
* @param {Partial<T>} data - Partial entity data containing fields to update
|
|
123
|
+
* @returns {Promise<DatabaseResult<T>>} Promise resolving to the updated entity
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* const result = await userRepository.update('user-123', {
|
|
128
|
+
* email: 'newemail@example.com',
|
|
129
|
+
* updatedAt: new Date()
|
|
130
|
+
* });
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
update(id: string, data: Partial<T>): Promise<DatabaseResult<T>>;
|
|
134
|
+
/**
|
|
135
|
+
* Delete an entity by ID (hard delete)
|
|
136
|
+
*
|
|
137
|
+
* @param {string} id - The primary key ID of the entity to delete
|
|
138
|
+
* @returns {Promise<DatabaseResult<void>>} Promise resolving when deletion is complete
|
|
139
|
+
*
|
|
140
|
+
* @warning This is a permanent operation that cannot be undone
|
|
141
|
+
* @see {@link softDelete} For recoverable deletion
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* const result = await userRepository.delete('user-123');
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
delete(id: string): Promise<DatabaseResult<void>>;
|
|
149
|
+
/**
|
|
150
|
+
* Count entities matching optional filter criteria
|
|
151
|
+
*
|
|
152
|
+
* @param {Filter} [filter] - Optional filter conditions
|
|
153
|
+
* @returns {Promise<DatabaseResult<number>>} Promise resolving to the count
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* const totalResult = await userRepository.count();
|
|
158
|
+
* const activeResult = await userRepository.count({
|
|
159
|
+
* field: 'status', operator: 'eq', value: 'active'
|
|
160
|
+
* });
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
count(filter?: Filter): Promise<DatabaseResult<number>>;
|
|
164
|
+
/**
|
|
165
|
+
* Check if an entity exists by ID
|
|
166
|
+
*
|
|
167
|
+
* @param {string} id - The primary key ID to check
|
|
168
|
+
* @returns {Promise<DatabaseResult<boolean>>} Promise resolving to existence status
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```typescript
|
|
172
|
+
* const existsResult = await userRepository.exists('user-123');
|
|
173
|
+
* if (existsResult.success && existsResult.value) {
|
|
174
|
+
* console.log('User exists');
|
|
175
|
+
* }
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
exists(id: string): Promise<DatabaseResult<boolean>>;
|
|
179
|
+
/**
|
|
180
|
+
* Find the first entity matching filter criteria
|
|
181
|
+
*
|
|
182
|
+
* @param {Filter} filter - Filter conditions to match
|
|
183
|
+
* @returns {Promise<DatabaseResult<T | null>>} Promise resolving to first match or null
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```typescript
|
|
187
|
+
* const result = await userRepository.findOne({
|
|
188
|
+
* field: 'email', operator: 'eq', value: 'john@example.com'
|
|
189
|
+
* });
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
findOne(filter: Filter): Promise<DatabaseResult<T | null>>;
|
|
193
|
+
/**
|
|
194
|
+
* Soft delete an entity by ID (recoverable deletion)
|
|
195
|
+
*
|
|
196
|
+
* @param {string} id - The primary key ID of the entity to soft delete
|
|
197
|
+
* @returns {Promise<DatabaseResult<void>>} Promise resolving when soft deletion is complete
|
|
198
|
+
*
|
|
199
|
+
* @see {@link delete} For permanent deletion
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```typescript
|
|
203
|
+
* const result = await userRepository.softDelete('user-123');
|
|
204
|
+
* // User is hidden but can be recovered
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
softDelete(id: string): Promise<DatabaseResult<void>>;
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=BaseRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseRepository.d.ts","sourceRoot":"","sources":["../../src/repository/BaseRepository.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,YAAY,EACZ,MAAM,EACN,mBAAmB,EACpB,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,8BAAsB,cAAc,CAAC,CAAC;IAIlC,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,mBAAmB;IAC1C,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM;gBADjB,EAAE,EAAE,mBAAmB,EACvB,SAAS,EAAE,MAAM;IAGtC;;;;;;;;;;;;;OAaG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAI7D;;;;;;;;;;;;;;OAcG;IACG,QAAQ,CACZ,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;IAI9C;;;;;;;;;;;;;OAaG;IACG,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IAIjD;;;;;;;;;;;;;;OAcG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IAItE;;;;;;;;;;;;;OAaG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IAIvD;;;;;;;;;;;;;OAaG;IACG,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAI7D;;;;;;;;;;;;;OAaG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAI1D;;;;;;;;;;;;OAYG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAIhE;;;;;;;;;;;;;OAaG;IACG,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;CAG5D"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Repository Pattern Implementation for Database Operations
|
|
3
|
+
*
|
|
4
|
+
* Provides the `BaseRepository` class, which implements the Repository pattern
|
|
5
|
+
* to achieve a clean separation between domain logic and data access.
|
|
6
|
+
* This abstraction ensures consistent, testable, and maintainable database interaction
|
|
7
|
+
* across all entities and services.
|
|
8
|
+
*
|
|
9
|
+
* ---
|
|
10
|
+
*
|
|
11
|
+
* **Repository Pattern Benefits:**
|
|
12
|
+
* - **Separation of Concerns** → Domain logic isolated from persistence layer
|
|
13
|
+
* - **Testability** → Easy to mock repositories for unit testing
|
|
14
|
+
* - **Consistency** → Unified CRUD interface for all entities
|
|
15
|
+
* - **Type Safety** → Full TypeScript support with generics
|
|
16
|
+
* - **Extensibility** → Easily extendable for entity-specific operations
|
|
17
|
+
*
|
|
18
|
+
* ---
|
|
19
|
+
*
|
|
20
|
+
* **Application Flow Context:**
|
|
21
|
+
* ```
|
|
22
|
+
* Domain Layer → Repository Layer → Service Layer → Database
|
|
23
|
+
* ↓ ↓ ↓ ↓
|
|
24
|
+
* UserService → UserRepository → DatabaseService → Adapters
|
|
25
|
+
* OrderService → OrderRepository → CRUD Operations → PostgreSQL
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* ---
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // Define an entity-specific repository
|
|
33
|
+
* interface User {
|
|
34
|
+
* id: string;
|
|
35
|
+
* name: string;
|
|
36
|
+
* email: string;
|
|
37
|
+
* status: 'active' | 'inactive';
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* class UserRepository extends BaseRepository<User> {
|
|
41
|
+
* constructor(db: IDatabaseService) {
|
|
42
|
+
* super(db, Tables.USERS);
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* // Add entity-specific methods
|
|
46
|
+
* async findByEmail(email: string): Promise<DatabaseResult<User | null>> {
|
|
47
|
+
* const result = await this.findMany({
|
|
48
|
+
* filter: { field: 'email', operator: 'eq', value: email }
|
|
49
|
+
* });
|
|
50
|
+
*
|
|
51
|
+
* if (result.success && result.value.data.length > 0) {
|
|
52
|
+
* return success(result.value.data[0]);
|
|
53
|
+
* }
|
|
54
|
+
* return success();
|
|
55
|
+
* }
|
|
56
|
+
*
|
|
57
|
+
* async findActiveUsers(): Promise<DatabaseResult<PaginatedResult<User>>> {
|
|
58
|
+
* return this.findMany({
|
|
59
|
+
* filter: { field: 'status', operator: 'eq', value: 'active' }
|
|
60
|
+
* });
|
|
61
|
+
* }
|
|
62
|
+
* }
|
|
63
|
+
*
|
|
64
|
+
* // Example usage in a service layer
|
|
65
|
+
* class UserService {
|
|
66
|
+
* constructor(private userRepo: UserRepository) {}
|
|
67
|
+
*
|
|
68
|
+
* async createUser(userData: CreateInput<User>): Promise<User> {
|
|
69
|
+
* const result = await this.userRepo.create(userData);
|
|
70
|
+
* if (!result.success) {
|
|
71
|
+
* throw new DatabaseError(`Failed to create user: ${result.error?.message}`, DATABASE_ERROR_CODES.INIT_FAILED);
|
|
72
|
+
* }
|
|
73
|
+
* return result.value;
|
|
74
|
+
* }
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
/** Base repository class implementing the Repository pattern */
|
|
79
|
+
export { BaseRepository } from "./BaseRepository";
|
|
80
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/repository/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4EG;AAEH,gEAAgE;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var common = require('@nestjs/common');
|
|
4
|
+
var sanitizeHtml = require('sanitize-html');
|
|
5
|
+
|
|
6
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var sanitizeHtml__default = /*#__PURE__*/_interopDefault(sanitizeHtml);
|
|
9
|
+
|
|
10
|
+
// @plyaz package - Built with tsup
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
13
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
14
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
15
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
16
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
17
|
+
if (decorator = decorators[i])
|
|
18
|
+
result = (decorator(result)) || result;
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// src/utils/typeGuards.ts
|
|
23
|
+
function isString(value) {
|
|
24
|
+
return typeof value === "string";
|
|
25
|
+
}
|
|
26
|
+
__name(isString, "isString");
|
|
27
|
+
function isObject(value) {
|
|
28
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
29
|
+
}
|
|
30
|
+
__name(isObject, "isObject");
|
|
31
|
+
|
|
32
|
+
// src/security/sanitizers/html.sanitizer.ts
|
|
33
|
+
exports.SanitizeHtmlPipe = class SanitizeHtmlPipe {
|
|
34
|
+
/**
|
|
35
|
+
* Transforms and sanitizes the provided value.
|
|
36
|
+
* - Strings are sanitized using `sanitize-html`.
|
|
37
|
+
* - Arrays and objects are recursively sanitized.
|
|
38
|
+
* - Other primitive types (e.g., numbers, booleans) are returned as-is.
|
|
39
|
+
*
|
|
40
|
+
* @template T
|
|
41
|
+
* @param {T} value - The value to sanitize.
|
|
42
|
+
* @returns {string | Record<string, unknown> | unknown[]} - The sanitized value.
|
|
43
|
+
*/
|
|
44
|
+
transform(value) {
|
|
45
|
+
if (isString(value)) {
|
|
46
|
+
return this.sanitizeString(value);
|
|
47
|
+
}
|
|
48
|
+
if (Array.isArray(value)) {
|
|
49
|
+
return value.map((v) => this.transform(v));
|
|
50
|
+
}
|
|
51
|
+
if (isObject(value)) {
|
|
52
|
+
const result = {};
|
|
53
|
+
for (const [key, val] of Object.entries(value)) {
|
|
54
|
+
result[key] = this.transform(val);
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Sanitizes a single string by removing all HTML tags and attributes,
|
|
62
|
+
* allowing only safe URL schemes like `http`, `https`, and `mailto`.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} str - The string to sanitize.
|
|
65
|
+
* @returns {string} - The sanitized string.
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
68
|
+
sanitizeString(str) {
|
|
69
|
+
return sanitizeHtml__default.default(str, {
|
|
70
|
+
allowedTags: [],
|
|
71
|
+
allowedAttributes: {},
|
|
72
|
+
allowedSchemes: ["http", "https", "mailto"]
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
__name(exports.SanitizeHtmlPipe, "SanitizeHtmlPipe");
|
|
77
|
+
exports.SanitizeHtmlPipe = __decorateClass([
|
|
78
|
+
common.Injectable()
|
|
79
|
+
], exports.SanitizeHtmlPipe);
|
|
80
|
+
exports.DataValidationPipe = class DataValidationPipe {
|
|
81
|
+
/**
|
|
82
|
+
* Creates a new DataValidationPipe instance with the provided Zod schema.
|
|
83
|
+
*
|
|
84
|
+
* @param {ZodType} schema - The Zod schema used to validate sanitized data.
|
|
85
|
+
*/
|
|
86
|
+
constructor(schema) {
|
|
87
|
+
this.schema = schema;
|
|
88
|
+
}
|
|
89
|
+
sanitizeHtmlPipe = new exports.SanitizeHtmlPipe();
|
|
90
|
+
/**
|
|
91
|
+
* Sanitizes and validates incoming data.
|
|
92
|
+
* - First, all string values are sanitized to remove unsafe HTML.
|
|
93
|
+
* - Then, the sanitized data is validated against the provided Zod schema.
|
|
94
|
+
*
|
|
95
|
+
* Throws a `BadRequestException` if validation fails.
|
|
96
|
+
*
|
|
97
|
+
* @param {unknown} value - The incoming value to sanitize and validate.
|
|
98
|
+
* @returns {unknown} - The sanitized and validated data.
|
|
99
|
+
* @throws {BadRequestException} - If validation fails.
|
|
100
|
+
*/
|
|
101
|
+
transform(value) {
|
|
102
|
+
const sanitizedValue = this.sanitizeHtmlPipe.transform(value);
|
|
103
|
+
try {
|
|
104
|
+
return this.schema.parse(sanitizedValue);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (error instanceof Error) {
|
|
107
|
+
throw new common.BadRequestException(`Validation failed: ${error.message}`);
|
|
108
|
+
}
|
|
109
|
+
throw new common.BadRequestException(`Invalid request data.`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
__name(exports.DataValidationPipe, "DataValidationPipe");
|
|
114
|
+
exports.DataValidationPipe = __decorateClass([
|
|
115
|
+
common.Injectable()
|
|
116
|
+
], exports.DataValidationPipe);
|
|
117
|
+
//# sourceMappingURL=index.cjs.map
|
|
118
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/typeGuards.ts","../../src/security/sanitizers/html.sanitizer.ts","../../src/security/serializers/DataValidation.ts"],"names":["SanitizeHtmlPipe","sanitizeHtml","Injectable","DataValidationPipe","BadRequestException"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAyBO,SAAS,SAAS,KAAA,EAAiC;AACxD,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA;AAC1B;AAFgB,MAAA,CAAA,QAAA,EAAA,UAAA,CAAA;AAwCT,SAAS,SAAS,KAAA,EAAiC;AACxD,EAAA,OAAO,KAAA,KAAU,QAAQ,OAAO,KAAA,KAAU,YAAY,CAAC,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5E;AAFgB,MAAA,CAAA,QAAA,EAAA,UAAA,CAAA;;;ACrDHA,2BAAN,sBAAA,CAAgD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWrD,UAAa,KAAA,EAAwD;AACnE,IAAA,IAAI,QAAA,CAAS,KAAK,CAAA,EAAG;AACnB,MAAA,OAAO,IAAA,CAAK,eAAe,KAAK,CAAA;AAAA,IAClC;AAEA,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxB,MAAA,OAAO,MAAM,GAAA,CAAI,CAAC,MAAM,IAAA,CAAK,SAAA,CAAU,CAAC,CAAC,CAAA;AAAA,IAC3C;AAEA,IAAA,IAAI,QAAA,CAAS,KAAK,CAAA,EAAG;AACnB,MAAA,MAAM,SAAkC,EAAC;AACzC,MAAA,KAAA,MAAW,CAAC,GAAA,EAAK,GAAG,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAC9C,QAAA,MAAA,CAAO,GAAG,CAAA,GAAI,IAAA,CAAK,SAAA,CAAU,GAAG,CAAA;AAAA,MAClC;AACA,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,OAAO,KAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,eAAe,GAAA,EAAqB;AAC1C,IAAA,OAAOC,8BAAa,GAAA,EAAK;AAAA,MACvB,aAAa,EAAC;AAAA,MACd,mBAAmB,EAAC;AAAA,MACpB,cAAA,EAAgB,CAAC,MAAA,EAAQ,OAAA,EAAS,QAAQ;AAAA,KAC3C,CAAA;AAAA,EACH;AACF;AA9CuD,MAAA,CAAAD,wBAAA,EAAA,kBAAA,CAAA;AAA1CA,wBAAA,GAAN,eAAA,CAAA;AAAA,EADNE,iBAAA;AAAW,CAAA,EACCF,wBAAA,CAAA;ACEAG,6BAAN,wBAAA,CAAkD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQvD,YAAoB,MAAA,EAAiB;AAAjB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAAkB;AAAA,EAPrB,gBAAA,GAAmB,IAAIH,wBAAA,EAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBzD,UAAU,KAAA,EAAyB;AACjC,IAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,gBAAA,CAAiB,SAAA,CAAU,KAAK,CAAA;AAE5D,IAAA,IAAI;AACF,MAAA,OAAO,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,cAAc,CAAA;AAAA,IACzC,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,QAAA,MAAM,IAAII,0BAAA,CAAoB,CAAA,mBAAA,EAAsB,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA,MACrE;AACA,MAAA,MAAM,IAAIA,2BAAoB,CAAA,qBAAA,CAAuB,CAAA;AAAA,IACvD;AAAA,EACF;AACF;AAjCyD,MAAA,CAAAD,0BAAA,EAAA,oBAAA,CAAA;AAA5CA,0BAAA,GAAN,eAAA,CAAA;AAAA,EADND,iBAAAA;AAAW,CAAA,EACCC,0BAAA,CAAA","file":"index.cjs","sourcesContent":["/**\n * @fileoverview Type Guard Utilities for @plyaz/db package\n *\n * This module provides type guard functions for runtime type checking throughout\n * the @plyaz/db package. These utilities replace direct typeof checks with\n * consistent, reusable type validation functions.\n *\n */\n\n/**\n * Type guard to check if a value is a string\n *\n * @param {unknown} value - The value to check\n * @returns {value is string} True if value is a string, false otherwise\n *\n * @example\n * ```typescript\n * import { isString } from '@plyaz/db/utils';\n *\n * if (isString(userInput)) {\n * // userInput is now typed as string\n * console.log(userInput.toLowerCase());\n * }\n * ```\n */\nexport function isString(value: unknown): value is string {\n return typeof value === \"string\";\n}\n\n/**\n * Type guard to check if a value is a non-empty string\n *\n * @param {unknown} value - The value to check\n * @returns {value is string} True if value is a non-empty string, false otherwise\n *\n * @example\n * ```typescript\n * import { isNonEmptyString } from '@plyaz/db/utils';\n *\n * if (isNonEmptyString(tableName)) {\n * // tableName is guaranteed to be a non-empty string\n * const query = `SELECT * FROM ${tableName}`;\n * }\n * ```\n */\nexport function isNonEmptyString(value: unknown): value is string {\n return isString(value) && value.length > 0;\n}\n\n/**\n * Type guard to check if a value is a number\n *\n * @param {unknown} value - The value to check\n * @returns {value is number} True if value is a number, false otherwise\n */\nexport function isNumber(value: unknown): value is number {\n return typeof value === \"number\";\n}\n\n/**\n * Type guard to check if a value is an object (not null, not array)\n *\n * @param {unknown} value - The value to check\n * @returns {value is object} True if value is an object, false otherwise\n */\nexport function isObject(value: unknown): value is object {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n","import { PipeTransform, Injectable } from \"@nestjs/common\";\nimport sanitizeHtml from \"sanitize-html\";\nimport { isString, isObject } from \"@utils/typeGuards\";\n\n/**\n * A NestJS pipe that recursively sanitizes input data to remove any potentially unsafe HTML.\n *\n * This pipe can handle strings, arrays, and objects. It ensures that\n * all string values within the provided data are sanitized using the `sanitize-html` library.\n *\n */\n@Injectable()\nexport class SanitizeHtmlPipe implements PipeTransform {\n /**\n * Transforms and sanitizes the provided value.\n * - Strings are sanitized using `sanitize-html`.\n * - Arrays and objects are recursively sanitized.\n * - Other primitive types (e.g., numbers, booleans) are returned as-is.\n *\n * @template T\n * @param {T} value - The value to sanitize.\n * @returns {string | Record<string, unknown> | unknown[]} - The sanitized value.\n */\n transform<T>(value: T): string | Record<string, unknown> | unknown[] {\n if (isString(value)) {\n return this.sanitizeString(value);\n }\n\n if (Array.isArray(value)) {\n return value.map((v) => this.transform(v));\n }\n\n if (isObject(value)) {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value)) {\n result[key] = this.transform(val);\n }\n return result;\n }\n\n return value as unknown[];\n }\n\n /**\n * Sanitizes a single string by removing all HTML tags and attributes,\n * allowing only safe URL schemes like `http`, `https`, and `mailto`.\n *\n * @param {string} str - The string to sanitize.\n * @returns {string} - The sanitized string.\n * @private\n */\n private sanitizeString(str: string): string {\n return sanitizeHtml(str, {\n allowedTags: [],\n allowedAttributes: {},\n allowedSchemes: [\"http\", \"https\", \"mailto\"],\n });\n }\n}\n","import { BadRequestException, Injectable, PipeTransform } from \"@nestjs/common\";\nimport { ZodType } from \"zod\";\nimport { SanitizeHtmlPipe } from \"../sanitizers/html.sanitizer\";\n\n/**\n * A NestJS pipe that combines input sanitization and validation.\n *\n * This pipe first sanitizes incoming data using {@link SanitizeHtmlPipe}\n * to remove any potentially unsafe HTML, and then validates the sanitized\n * data using a provided Zod schema.\n *\n * If validation fails, a `BadRequestException` is thrown.\n */\n@Injectable()\nexport class DataValidationPipe implements PipeTransform {\n private readonly sanitizeHtmlPipe = new SanitizeHtmlPipe();\n\n /**\n * Creates a new DataValidationPipe instance with the provided Zod schema.\n *\n * @param {ZodType} schema - The Zod schema used to validate sanitized data.\n */\n constructor(private schema: ZodType) {}\n\n /**\n * Sanitizes and validates incoming data.\n * - First, all string values are sanitized to remove unsafe HTML.\n * - Then, the sanitized data is validated against the provided Zod schema.\n *\n * Throws a `BadRequestException` if validation fails.\n *\n * @param {unknown} value - The incoming value to sanitize and validate.\n * @returns {unknown} - The sanitized and validated data.\n * @throws {BadRequestException} - If validation fails.\n */\n transform(value: unknown): unknown {\n const sanitizedValue = this.sanitizeHtmlPipe.transform(value);\n\n try {\n return this.schema.parse(sanitizedValue);\n } catch (error) {\n if (error instanceof Error) {\n throw new BadRequestException(`Validation failed: ${error.message}`);\n }\n throw new BadRequestException(`Invalid request data.`);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/security/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { Injectable, BadRequestException } from '@nestjs/common';
|
|
2
|
+
import sanitizeHtml from 'sanitize-html';
|
|
3
|
+
|
|
4
|
+
// @plyaz package - Built with tsup
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
9
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
10
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
11
|
+
if (decorator = decorators[i])
|
|
12
|
+
result = (decorator(result)) || result;
|
|
13
|
+
return result;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// src/utils/typeGuards.ts
|
|
17
|
+
function isString(value) {
|
|
18
|
+
return typeof value === "string";
|
|
19
|
+
}
|
|
20
|
+
__name(isString, "isString");
|
|
21
|
+
function isObject(value) {
|
|
22
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
23
|
+
}
|
|
24
|
+
__name(isObject, "isObject");
|
|
25
|
+
|
|
26
|
+
// src/security/sanitizers/html.sanitizer.ts
|
|
27
|
+
var SanitizeHtmlPipe = class {
|
|
28
|
+
/**
|
|
29
|
+
* Transforms and sanitizes the provided value.
|
|
30
|
+
* - Strings are sanitized using `sanitize-html`.
|
|
31
|
+
* - Arrays and objects are recursively sanitized.
|
|
32
|
+
* - Other primitive types (e.g., numbers, booleans) are returned as-is.
|
|
33
|
+
*
|
|
34
|
+
* @template T
|
|
35
|
+
* @param {T} value - The value to sanitize.
|
|
36
|
+
* @returns {string | Record<string, unknown> | unknown[]} - The sanitized value.
|
|
37
|
+
*/
|
|
38
|
+
transform(value) {
|
|
39
|
+
if (isString(value)) {
|
|
40
|
+
return this.sanitizeString(value);
|
|
41
|
+
}
|
|
42
|
+
if (Array.isArray(value)) {
|
|
43
|
+
return value.map((v) => this.transform(v));
|
|
44
|
+
}
|
|
45
|
+
if (isObject(value)) {
|
|
46
|
+
const result = {};
|
|
47
|
+
for (const [key, val] of Object.entries(value)) {
|
|
48
|
+
result[key] = this.transform(val);
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Sanitizes a single string by removing all HTML tags and attributes,
|
|
56
|
+
* allowing only safe URL schemes like `http`, `https`, and `mailto`.
|
|
57
|
+
*
|
|
58
|
+
* @param {string} str - The string to sanitize.
|
|
59
|
+
* @returns {string} - The sanitized string.
|
|
60
|
+
* @private
|
|
61
|
+
*/
|
|
62
|
+
sanitizeString(str) {
|
|
63
|
+
return sanitizeHtml(str, {
|
|
64
|
+
allowedTags: [],
|
|
65
|
+
allowedAttributes: {},
|
|
66
|
+
allowedSchemes: ["http", "https", "mailto"]
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
__name(SanitizeHtmlPipe, "SanitizeHtmlPipe");
|
|
71
|
+
SanitizeHtmlPipe = __decorateClass([
|
|
72
|
+
Injectable()
|
|
73
|
+
], SanitizeHtmlPipe);
|
|
74
|
+
var DataValidationPipe = class {
|
|
75
|
+
/**
|
|
76
|
+
* Creates a new DataValidationPipe instance with the provided Zod schema.
|
|
77
|
+
*
|
|
78
|
+
* @param {ZodType} schema - The Zod schema used to validate sanitized data.
|
|
79
|
+
*/
|
|
80
|
+
constructor(schema) {
|
|
81
|
+
this.schema = schema;
|
|
82
|
+
}
|
|
83
|
+
sanitizeHtmlPipe = new SanitizeHtmlPipe();
|
|
84
|
+
/**
|
|
85
|
+
* Sanitizes and validates incoming data.
|
|
86
|
+
* - First, all string values are sanitized to remove unsafe HTML.
|
|
87
|
+
* - Then, the sanitized data is validated against the provided Zod schema.
|
|
88
|
+
*
|
|
89
|
+
* Throws a `BadRequestException` if validation fails.
|
|
90
|
+
*
|
|
91
|
+
* @param {unknown} value - The incoming value to sanitize and validate.
|
|
92
|
+
* @returns {unknown} - The sanitized and validated data.
|
|
93
|
+
* @throws {BadRequestException} - If validation fails.
|
|
94
|
+
*/
|
|
95
|
+
transform(value) {
|
|
96
|
+
const sanitizedValue = this.sanitizeHtmlPipe.transform(value);
|
|
97
|
+
try {
|
|
98
|
+
return this.schema.parse(sanitizedValue);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (error instanceof Error) {
|
|
101
|
+
throw new BadRequestException(`Validation failed: ${error.message}`);
|
|
102
|
+
}
|
|
103
|
+
throw new BadRequestException(`Invalid request data.`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
__name(DataValidationPipe, "DataValidationPipe");
|
|
108
|
+
DataValidationPipe = __decorateClass([
|
|
109
|
+
Injectable()
|
|
110
|
+
], DataValidationPipe);
|
|
111
|
+
|
|
112
|
+
export { DataValidationPipe, SanitizeHtmlPipe };
|
|
113
|
+
//# sourceMappingURL=index.mjs.map
|
|
114
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/typeGuards.ts","../../src/security/sanitizers/html.sanitizer.ts","../../src/security/serializers/DataValidation.ts"],"names":["Injectable"],"mappings":";;;;;;;;;;;;;;;;AAyBO,SAAS,SAAS,KAAA,EAAiC;AACxD,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA;AAC1B;AAFgB,MAAA,CAAA,QAAA,EAAA,UAAA,CAAA;AAwCT,SAAS,SAAS,KAAA,EAAiC;AACxD,EAAA,OAAO,KAAA,KAAU,QAAQ,OAAO,KAAA,KAAU,YAAY,CAAC,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5E;AAFgB,MAAA,CAAA,QAAA,EAAA,UAAA,CAAA;;;ACrDT,IAAM,mBAAN,MAAgD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWrD,UAAa,KAAA,EAAwD;AACnE,IAAA,IAAI,QAAA,CAAS,KAAK,CAAA,EAAG;AACnB,MAAA,OAAO,IAAA,CAAK,eAAe,KAAK,CAAA;AAAA,IAClC;AAEA,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxB,MAAA,OAAO,MAAM,GAAA,CAAI,CAAC,MAAM,IAAA,CAAK,SAAA,CAAU,CAAC,CAAC,CAAA;AAAA,IAC3C;AAEA,IAAA,IAAI,QAAA,CAAS,KAAK,CAAA,EAAG;AACnB,MAAA,MAAM,SAAkC,EAAC;AACzC,MAAA,KAAA,MAAW,CAAC,GAAA,EAAK,GAAG,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAC9C,QAAA,MAAA,CAAO,GAAG,CAAA,GAAI,IAAA,CAAK,SAAA,CAAU,GAAG,CAAA;AAAA,MAClC;AACA,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,OAAO,KAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,eAAe,GAAA,EAAqB;AAC1C,IAAA,OAAO,aAAa,GAAA,EAAK;AAAA,MACvB,aAAa,EAAC;AAAA,MACd,mBAAmB,EAAC;AAAA,MACpB,cAAA,EAAgB,CAAC,MAAA,EAAQ,OAAA,EAAS,QAAQ;AAAA,KAC3C,CAAA;AAAA,EACH;AACF;AA9CuD,MAAA,CAAA,gBAAA,EAAA,kBAAA,CAAA;AAA1C,gBAAA,GAAN,eAAA,CAAA;AAAA,EADN,UAAA;AAAW,CAAA,EACC,gBAAA,CAAA;ACEN,IAAM,qBAAN,MAAkD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQvD,YAAoB,MAAA,EAAiB;AAAjB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAAkB;AAAA,EAPrB,gBAAA,GAAmB,IAAI,gBAAA,EAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBzD,UAAU,KAAA,EAAyB;AACjC,IAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,gBAAA,CAAiB,SAAA,CAAU,KAAK,CAAA;AAE5D,IAAA,IAAI;AACF,MAAA,OAAO,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,cAAc,CAAA;AAAA,IACzC,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,QAAA,MAAM,IAAI,mBAAA,CAAoB,CAAA,mBAAA,EAAsB,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA,MACrE;AACA,MAAA,MAAM,IAAI,oBAAoB,CAAA,qBAAA,CAAuB,CAAA;AAAA,IACvD;AAAA,EACF;AACF;AAjCyD,MAAA,CAAA,kBAAA,EAAA,oBAAA,CAAA;AAA5C,kBAAA,GAAN,eAAA,CAAA;AAAA,EADNA,UAAAA;AAAW,CAAA,EACC,kBAAA,CAAA","file":"index.mjs","sourcesContent":["/**\n * @fileoverview Type Guard Utilities for @plyaz/db package\n *\n * This module provides type guard functions for runtime type checking throughout\n * the @plyaz/db package. These utilities replace direct typeof checks with\n * consistent, reusable type validation functions.\n *\n */\n\n/**\n * Type guard to check if a value is a string\n *\n * @param {unknown} value - The value to check\n * @returns {value is string} True if value is a string, false otherwise\n *\n * @example\n * ```typescript\n * import { isString } from '@plyaz/db/utils';\n *\n * if (isString(userInput)) {\n * // userInput is now typed as string\n * console.log(userInput.toLowerCase());\n * }\n * ```\n */\nexport function isString(value: unknown): value is string {\n return typeof value === \"string\";\n}\n\n/**\n * Type guard to check if a value is a non-empty string\n *\n * @param {unknown} value - The value to check\n * @returns {value is string} True if value is a non-empty string, false otherwise\n *\n * @example\n * ```typescript\n * import { isNonEmptyString } from '@plyaz/db/utils';\n *\n * if (isNonEmptyString(tableName)) {\n * // tableName is guaranteed to be a non-empty string\n * const query = `SELECT * FROM ${tableName}`;\n * }\n * ```\n */\nexport function isNonEmptyString(value: unknown): value is string {\n return isString(value) && value.length > 0;\n}\n\n/**\n * Type guard to check if a value is a number\n *\n * @param {unknown} value - The value to check\n * @returns {value is number} True if value is a number, false otherwise\n */\nexport function isNumber(value: unknown): value is number {\n return typeof value === \"number\";\n}\n\n/**\n * Type guard to check if a value is an object (not null, not array)\n *\n * @param {unknown} value - The value to check\n * @returns {value is object} True if value is an object, false otherwise\n */\nexport function isObject(value: unknown): value is object {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n","import { PipeTransform, Injectable } from \"@nestjs/common\";\nimport sanitizeHtml from \"sanitize-html\";\nimport { isString, isObject } from \"@utils/typeGuards\";\n\n/**\n * A NestJS pipe that recursively sanitizes input data to remove any potentially unsafe HTML.\n *\n * This pipe can handle strings, arrays, and objects. It ensures that\n * all string values within the provided data are sanitized using the `sanitize-html` library.\n *\n */\n@Injectable()\nexport class SanitizeHtmlPipe implements PipeTransform {\n /**\n * Transforms and sanitizes the provided value.\n * - Strings are sanitized using `sanitize-html`.\n * - Arrays and objects are recursively sanitized.\n * - Other primitive types (e.g., numbers, booleans) are returned as-is.\n *\n * @template T\n * @param {T} value - The value to sanitize.\n * @returns {string | Record<string, unknown> | unknown[]} - The sanitized value.\n */\n transform<T>(value: T): string | Record<string, unknown> | unknown[] {\n if (isString(value)) {\n return this.sanitizeString(value);\n }\n\n if (Array.isArray(value)) {\n return value.map((v) => this.transform(v));\n }\n\n if (isObject(value)) {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value)) {\n result[key] = this.transform(val);\n }\n return result;\n }\n\n return value as unknown[];\n }\n\n /**\n * Sanitizes a single string by removing all HTML tags and attributes,\n * allowing only safe URL schemes like `http`, `https`, and `mailto`.\n *\n * @param {string} str - The string to sanitize.\n * @returns {string} - The sanitized string.\n * @private\n */\n private sanitizeString(str: string): string {\n return sanitizeHtml(str, {\n allowedTags: [],\n allowedAttributes: {},\n allowedSchemes: [\"http\", \"https\", \"mailto\"],\n });\n }\n}\n","import { BadRequestException, Injectable, PipeTransform } from \"@nestjs/common\";\nimport { ZodType } from \"zod\";\nimport { SanitizeHtmlPipe } from \"../sanitizers/html.sanitizer\";\n\n/**\n * A NestJS pipe that combines input sanitization and validation.\n *\n * This pipe first sanitizes incoming data using {@link SanitizeHtmlPipe}\n * to remove any potentially unsafe HTML, and then validates the sanitized\n * data using a provided Zod schema.\n *\n * If validation fails, a `BadRequestException` is thrown.\n */\n@Injectable()\nexport class DataValidationPipe implements PipeTransform {\n private readonly sanitizeHtmlPipe = new SanitizeHtmlPipe();\n\n /**\n * Creates a new DataValidationPipe instance with the provided Zod schema.\n *\n * @param {ZodType} schema - The Zod schema used to validate sanitized data.\n */\n constructor(private schema: ZodType) {}\n\n /**\n * Sanitizes and validates incoming data.\n * - First, all string values are sanitized to remove unsafe HTML.\n * - Then, the sanitized data is validated against the provided Zod schema.\n *\n * Throws a `BadRequestException` if validation fails.\n *\n * @param {unknown} value - The incoming value to sanitize and validate.\n * @returns {unknown} - The sanitized and validated data.\n * @throws {BadRequestException} - If validation fails.\n */\n transform(value: unknown): unknown {\n const sanitizedValue = this.sanitizeHtmlPipe.transform(value);\n\n try {\n return this.schema.parse(sanitizedValue);\n } catch (error) {\n if (error instanceof Error) {\n throw new BadRequestException(`Validation failed: ${error.message}`);\n }\n throw new BadRequestException(`Invalid request data.`);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { PipeTransform } from "@nestjs/common";
|
|
2
|
+
/**
|
|
3
|
+
* A NestJS pipe that recursively sanitizes input data to remove any potentially unsafe HTML.
|
|
4
|
+
*
|
|
5
|
+
* This pipe can handle strings, arrays, and objects. It ensures that
|
|
6
|
+
* all string values within the provided data are sanitized using the `sanitize-html` library.
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
export declare class SanitizeHtmlPipe implements PipeTransform {
|
|
10
|
+
/**
|
|
11
|
+
* Transforms and sanitizes the provided value.
|
|
12
|
+
* - Strings are sanitized using `sanitize-html`.
|
|
13
|
+
* - Arrays and objects are recursively sanitized.
|
|
14
|
+
* - Other primitive types (e.g., numbers, booleans) are returned as-is.
|
|
15
|
+
*
|
|
16
|
+
* @template T
|
|
17
|
+
* @param {T} value - The value to sanitize.
|
|
18
|
+
* @returns {string | Record<string, unknown> | unknown[]} - The sanitized value.
|
|
19
|
+
*/
|
|
20
|
+
transform<T>(value: T): string | Record<string, unknown> | unknown[];
|
|
21
|
+
/**
|
|
22
|
+
* Sanitizes a single string by removing all HTML tags and attributes,
|
|
23
|
+
* allowing only safe URL schemes like `http`, `https`, and `mailto`.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} str - The string to sanitize.
|
|
26
|
+
* @returns {string} - The sanitized string.
|
|
27
|
+
* @private
|
|
28
|
+
*/
|
|
29
|
+
private sanitizeString;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=html.sanitizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.sanitizer.d.ts","sourceRoot":"","sources":["../../../src/security/sanitizers/html.sanitizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAc,MAAM,gBAAgB,CAAC;AAI3D;;;;;;GAMG;AACH,qBACa,gBAAiB,YAAW,aAAa;IACpD;;;;;;;;;OASG;IACH,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE;IAoBpE;;;;;;;OAOG;IACH,OAAO,CAAC,cAAc;CAOvB"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { PipeTransform } from "@nestjs/common";
|
|
2
|
+
import { ZodType } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* A NestJS pipe that combines input sanitization and validation.
|
|
5
|
+
*
|
|
6
|
+
* This pipe first sanitizes incoming data using {@link SanitizeHtmlPipe}
|
|
7
|
+
* to remove any potentially unsafe HTML, and then validates the sanitized
|
|
8
|
+
* data using a provided Zod schema.
|
|
9
|
+
*
|
|
10
|
+
* If validation fails, a `BadRequestException` is thrown.
|
|
11
|
+
*/
|
|
12
|
+
export declare class DataValidationPipe implements PipeTransform {
|
|
13
|
+
private schema;
|
|
14
|
+
private readonly sanitizeHtmlPipe;
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new DataValidationPipe instance with the provided Zod schema.
|
|
17
|
+
*
|
|
18
|
+
* @param {ZodType} schema - The Zod schema used to validate sanitized data.
|
|
19
|
+
*/
|
|
20
|
+
constructor(schema: ZodType);
|
|
21
|
+
/**
|
|
22
|
+
* Sanitizes and validates incoming data.
|
|
23
|
+
* - First, all string values are sanitized to remove unsafe HTML.
|
|
24
|
+
* - Then, the sanitized data is validated against the provided Zod schema.
|
|
25
|
+
*
|
|
26
|
+
* Throws a `BadRequestException` if validation fails.
|
|
27
|
+
*
|
|
28
|
+
* @param {unknown} value - The incoming value to sanitize and validate.
|
|
29
|
+
* @returns {unknown} - The sanitized and validated data.
|
|
30
|
+
* @throws {BadRequestException} - If validation fails.
|
|
31
|
+
*/
|
|
32
|
+
transform(value: unknown): unknown;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=DataValidation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataValidation.d.ts","sourceRoot":"","sources":["../../../src/security/serializers/DataValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAG9B;;;;;;;;GAQG;AACH,qBACa,kBAAmB,YAAW,aAAa;IAQ1C,OAAO,CAAC,MAAM;IAP1B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA0B;IAE3D;;;;OAIG;gBACiB,MAAM,EAAE,OAAO;IAEnC;;;;;;;;;;OAUG;IACH,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO;CAYnC"}
|