@arcaelas/dynamite 1.0.15 → 1.0.17

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.
Files changed (53) hide show
  1. package/package.json +1 -1
  2. package/src/@types/index.d.ts +96 -0
  3. package/src/@types/index.js +9 -0
  4. package/src/core/client.d.ts +69 -0
  5. package/src/core/client.js +164 -0
  6. package/src/core/table.d.ts +98 -0
  7. package/src/core/table.js +459 -0
  8. package/src/core/wrapper.d.ts +17 -0
  9. package/src/core/wrapper.js +46 -0
  10. package/src/decorators/belongs_to.d.ts +1 -0
  11. package/src/decorators/belongs_to.js +24 -0
  12. package/src/decorators/created_at.d.ts +1 -0
  13. package/src/decorators/created_at.js +11 -0
  14. package/src/decorators/default.d.ts +1 -0
  15. package/src/decorators/default.js +47 -0
  16. package/src/decorators/has_many.d.ts +1 -0
  17. package/src/decorators/has_many.js +24 -0
  18. package/src/decorators/index.d.ts +11 -0
  19. package/src/decorators/index.js +36 -0
  20. package/src/decorators/index_sort.d.ts +12 -0
  21. package/src/decorators/index_sort.js +43 -0
  22. package/src/decorators/mutate.d.ts +2 -0
  23. package/src/decorators/mutate.js +51 -0
  24. package/src/decorators/name.d.ts +1 -0
  25. package/src/decorators/name.js +28 -0
  26. package/src/decorators/not_null.d.ts +1 -0
  27. package/src/decorators/not_null.js +13 -0
  28. package/src/decorators/primary_key.d.ts +6 -0
  29. package/src/decorators/primary_key.js +30 -0
  30. package/src/decorators/updated_at.d.ts +12 -0
  31. package/src/decorators/updated_at.js +26 -0
  32. package/src/decorators/validate.d.ts +1 -0
  33. package/src/decorators/validate.js +53 -0
  34. package/src/index.d.ts +22 -0
  35. package/src/index.js +47 -0
  36. package/src/utils/batch-relations.d.ts +14 -0
  37. package/src/utils/batch-relations.js +131 -0
  38. package/src/utils/circular-detector.d.ts +82 -0
  39. package/src/utils/circular-detector.js +212 -0
  40. package/src/utils/memory-manager.d.ts +42 -0
  41. package/src/utils/memory-manager.js +107 -0
  42. package/src/utils/naming.d.ts +8 -0
  43. package/src/utils/naming.js +18 -0
  44. package/src/utils/projection.d.ts +12 -0
  45. package/src/utils/projection.js +51 -0
  46. package/src/utils/relations.d.ts +17 -0
  47. package/src/utils/relations.js +165 -0
  48. package/src/utils/security-validator.d.ts +49 -0
  49. package/src/utils/security-validator.js +163 -0
  50. package/src/utils/throttle-manager.d.ts +78 -0
  51. package/src/utils/throttle-manager.js +201 -0
  52. package/src/utils/transaction-manager.d.ts +88 -0
  53. package/src/utils/transaction-manager.js +300 -0
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ /**
3
+ * @file security-validator.ts
4
+ * @description Security validation and NoSQL injection prevention
5
+ * @author Miguel Alejandro
6
+ * @fecha 2025-08-31
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.securityValidator = exports.SecurityValidator = exports.SecurityError = void 0;
10
+ class SecurityValidator {
11
+ static { this.DEFAULT_CONFIG = {
12
+ maxStringLength: 10000,
13
+ maxArrayLength: 1000,
14
+ maxNestedDepth: 10,
15
+ allowedOperators: [
16
+ "=",
17
+ "!=",
18
+ "<",
19
+ "<=",
20
+ ">",
21
+ ">=",
22
+ "in",
23
+ "not-in",
24
+ "contains",
25
+ "begins-with",
26
+ ],
27
+ blockedPatterns: [
28
+ /\$\w+/g, // MongoDB operators
29
+ /javascript:/gi, // JavaScript injection
30
+ /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, // Script tags
31
+ /eval\s*\(/gi, // eval() calls
32
+ /function\s*\(/gi, // function declarations
33
+ /\{\s*\$\w+/g, // NoSQL operators
34
+ /\.\.\//g, // Path traversal
35
+ /union\s+select/gi, // SQL injection patterns
36
+ /drop\s+table/gi, // Destructive SQL
37
+ ],
38
+ }; }
39
+ constructor(config) {
40
+ this.config = { ...SecurityValidator.DEFAULT_CONFIG, ...config };
41
+ }
42
+ /**
43
+ * Validar nombre de atributo/tabla
44
+ */
45
+ validateAttributeName(name) {
46
+ if (!name || typeof name !== "string") {
47
+ throw new SecurityError("Nombre de atributo inválido");
48
+ }
49
+ if (name.length > 255) {
50
+ throw new SecurityError("Nombre de atributo muy largo");
51
+ }
52
+ // Solo permitir caracteres alfanuméricos, guiones y guiones bajos
53
+ if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name)) {
54
+ throw new SecurityError("Nombre de atributo contiene caracteres inválidos");
55
+ }
56
+ // Verificar patrones bloqueados
57
+ for (const pattern of this.config.blockedPatterns) {
58
+ if (pattern.test(name)) {
59
+ throw new SecurityError("Nombre de atributo contiene patrones peligrosos");
60
+ }
61
+ }
62
+ }
63
+ /**
64
+ * Validar operador de query
65
+ */
66
+ validateOperator(operator) {
67
+ if (!this.config.allowedOperators.includes(operator)) {
68
+ throw new SecurityError(`Operador no permitido: ${operator}`);
69
+ }
70
+ return operator;
71
+ }
72
+ /**
73
+ * Validar valor de campo
74
+ */
75
+ validateValue(value, depth = 0) {
76
+ if (depth > this.config.maxNestedDepth) {
77
+ throw new SecurityError("Estructura de datos anidada muy profunda");
78
+ }
79
+ // Valores null/undefined son válidos
80
+ if (value === null || value === undefined) {
81
+ return value;
82
+ }
83
+ // Validar strings
84
+ if (typeof value === "string") {
85
+ if (value.length > this.config.maxStringLength) {
86
+ throw new SecurityError("String muy largo");
87
+ }
88
+ for (const pattern of this.config.blockedPatterns) {
89
+ if (pattern.test(value)) {
90
+ throw new SecurityError("Valor contiene patrones peligrosos");
91
+ }
92
+ }
93
+ return value;
94
+ }
95
+ // Validar arrays
96
+ if (Array.isArray(value)) {
97
+ if (value.length > this.config.maxArrayLength) {
98
+ throw new SecurityError("Array muy largo");
99
+ }
100
+ return value.map((item) => this.validateValue(item, depth + 1));
101
+ }
102
+ // Validar objetos
103
+ if (typeof value === "object") {
104
+ const validated = {};
105
+ for (const [key, val] of Object.entries(value)) {
106
+ this.validateAttributeName(key);
107
+ validated[key] = this.validateValue(val, depth + 1);
108
+ }
109
+ return validated;
110
+ }
111
+ // Números, booleans son válidos
112
+ if (typeof value === "number" || typeof value === "boolean") {
113
+ return value;
114
+ }
115
+ throw new SecurityError(`Tipo de valor no permitido: ${typeof value}`);
116
+ }
117
+ /**
118
+ * Validar filtros de query completos
119
+ */
120
+ validateQueryFilters(filters) {
121
+ const validated = {};
122
+ for (const [key, value] of Object.entries(filters)) {
123
+ this.validateAttributeName(key);
124
+ validated[key] = this.validateValue(value);
125
+ }
126
+ return validated;
127
+ }
128
+ /**
129
+ * Sanitizar string para DynamoDB
130
+ */
131
+ sanitizeString(input) {
132
+ if (typeof input !== "string") {
133
+ return String(input);
134
+ }
135
+ return input
136
+ .replace(/[\x00-\x1F\x7F]/g, "") // Remover caracteres de control
137
+ .trim()
138
+ .slice(0, this.config.maxStringLength);
139
+ }
140
+ /**
141
+ * Validar tamaño de item para DynamoDB (límite 400KB)
142
+ */
143
+ validateItemSize(item) {
144
+ const size = JSON.stringify(item).length;
145
+ const maxSize = 400 * 1024; // 400KB en bytes
146
+ if (size > maxSize) {
147
+ throw new SecurityError(`Item muy grande: ${size} bytes (máximo: ${maxSize} bytes)`);
148
+ }
149
+ }
150
+ }
151
+ exports.SecurityValidator = SecurityValidator;
152
+ class SecurityError extends Error {
153
+ constructor(message) {
154
+ super(message);
155
+ this.name = "SecurityError";
156
+ }
157
+ }
158
+ exports.SecurityError = SecurityError;
159
+ // Instancia singleton
160
+ const securityValidator = new SecurityValidator();
161
+ exports.securityValidator = securityValidator;
162
+ exports.default = securityValidator;
163
+ //# sourceMappingURL=security-validator.js.map
@@ -0,0 +1,78 @@
1
+ /**
2
+ * @file throttle-manager.ts
3
+ * @description AWS DynamoDB throttling and retry management
4
+ * @author Miguel Alejandro
5
+ * @fecha 2025-08-31
6
+ */
7
+ interface RetryConfig {
8
+ maxRetries: number;
9
+ baseDelay: number;
10
+ maxDelay: number;
11
+ backoffMultiplier: number;
12
+ jitterFactor: number;
13
+ }
14
+ interface ThrottleStats {
15
+ totalRequests: number;
16
+ throttledRequests: number;
17
+ retriedRequests: number;
18
+ averageLatency: number;
19
+ }
20
+ declare class ThrottleManager {
21
+ private static readonly DEFAULT_CONFIG;
22
+ private config;
23
+ private stats;
24
+ private requestQueue;
25
+ private isProcessingQueue;
26
+ private concurrentRequests;
27
+ private maxConcurrentRequests;
28
+ constructor(config?: Partial<RetryConfig>);
29
+ /**
30
+ * Ejecutar operación con retry automático
31
+ */
32
+ executeWithRetry<T>(operation: () => Promise<T>, operationName?: string): Promise<T>;
33
+ /**
34
+ * Verificar si el error es reintentable
35
+ */
36
+ private isRetryableError;
37
+ /**
38
+ * Calcular delay con exponential backoff + jitter
39
+ */
40
+ private calculateDelay;
41
+ /**
42
+ * Controlar concurrencia de requests
43
+ */
44
+ private acquireConcurrencySlot;
45
+ private releaseConcurrencySlot;
46
+ /**
47
+ * Queue para operaciones batch con rate limiting
48
+ */
49
+ queueOperation<T>(operation: () => Promise<T>): Promise<T>;
50
+ private processQueue;
51
+ /**
52
+ * Obtener estadísticas de throttling
53
+ */
54
+ getStats(): ThrottleStats;
55
+ /**
56
+ * Resetear estadísticas
57
+ */
58
+ resetStats(): void;
59
+ private updateLatencyStats;
60
+ private sleep;
61
+ /**
62
+ * Configurar límites de concurrencia dinámicamente
63
+ */
64
+ setConcurrencyLimit(limit: number): void;
65
+ /**
66
+ * Obtener métricas de salud del throttling
67
+ */
68
+ getHealthMetrics(): {
69
+ throttleRate: number;
70
+ retryRate: number;
71
+ avgLatency: number;
72
+ concurrentRequests: number;
73
+ queueSize: number;
74
+ };
75
+ }
76
+ declare const throttleManager: ThrottleManager;
77
+ export { RetryConfig, ThrottleManager, ThrottleStats, throttleManager };
78
+ export default throttleManager;
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ /**
3
+ * @file throttle-manager.ts
4
+ * @description AWS DynamoDB throttling and retry management
5
+ * @author Miguel Alejandro
6
+ * @fecha 2025-08-31
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.throttleManager = exports.ThrottleManager = void 0;
10
+ class ThrottleManager {
11
+ static { this.DEFAULT_CONFIG = {
12
+ maxRetries: 10,
13
+ baseDelay: 100, // 100ms base delay
14
+ maxDelay: 30000, // 30s max delay
15
+ backoffMultiplier: 2, // Exponential backoff
16
+ jitterFactor: 0.1, // 10% jitter
17
+ }; }
18
+ constructor(config) {
19
+ this.requestQueue = [];
20
+ this.isProcessingQueue = false;
21
+ this.concurrentRequests = 0;
22
+ this.maxConcurrentRequests = 25; // DynamoDB default
23
+ this.config = { ...ThrottleManager.DEFAULT_CONFIG, ...config };
24
+ this.stats = {
25
+ totalRequests: 0,
26
+ throttledRequests: 0,
27
+ retriedRequests: 0,
28
+ averageLatency: 0,
29
+ };
30
+ }
31
+ /**
32
+ * Ejecutar operación con retry automático
33
+ */
34
+ async executeWithRetry(operation, operationName = "DynamoDB Operation") {
35
+ const startTime = Date.now();
36
+ let lastError = null;
37
+ this.stats.totalRequests++;
38
+ for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
39
+ try {
40
+ // Control de concurrencia
41
+ await this.acquireConcurrencySlot();
42
+ const result = await operation();
43
+ this.releaseConcurrencySlot();
44
+ this.updateLatencyStats(Date.now() - startTime);
45
+ if (attempt > 0) {
46
+ console.log(`✅ ${operationName} succeeded after ${attempt} retries`);
47
+ }
48
+ return result;
49
+ }
50
+ catch (error) {
51
+ this.releaseConcurrencySlot();
52
+ lastError = error;
53
+ // Verificar si es un error que podemos reintentar
54
+ if (!this.isRetryableError(error)) {
55
+ throw error;
56
+ }
57
+ this.stats.throttledRequests++;
58
+ if (attempt === this.config.maxRetries) {
59
+ console.error(`❌ ${operationName} failed after ${this.config.maxRetries} retries:`, error.message);
60
+ throw new Error(`Max retries exceeded for ${operationName}: ${error.message}`);
61
+ }
62
+ this.stats.retriedRequests++;
63
+ const delay = this.calculateDelay(attempt);
64
+ console.warn(`⚠️ ${operationName} throttled, retrying in ${delay}ms (attempt ${attempt + 1}/${this.config.maxRetries})`);
65
+ await this.sleep(delay);
66
+ }
67
+ }
68
+ throw lastError;
69
+ }
70
+ /**
71
+ * Verificar si el error es reintentable
72
+ */
73
+ isRetryableError(error) {
74
+ if (!error.name)
75
+ return false;
76
+ const retryableErrors = [
77
+ "ProvisionedThroughputExceededException",
78
+ "ThrottlingException",
79
+ "RequestLimitExceeded",
80
+ "ServiceUnavailableException",
81
+ "InternalServerError",
82
+ "NetworkingError",
83
+ "TimeoutError",
84
+ ];
85
+ return retryableErrors.includes(error.name) || error.retryable === true;
86
+ }
87
+ /**
88
+ * Calcular delay con exponential backoff + jitter
89
+ */
90
+ calculateDelay(attempt) {
91
+ const exponentialDelay = this.config.baseDelay * Math.pow(this.config.backoffMultiplier, attempt);
92
+ const cappedDelay = Math.min(exponentialDelay, this.config.maxDelay);
93
+ // Agregar jitter para evitar "thundering herd"
94
+ const jitter = cappedDelay * this.config.jitterFactor * Math.random();
95
+ return Math.floor(cappedDelay + jitter);
96
+ }
97
+ /**
98
+ * Controlar concurrencia de requests
99
+ */
100
+ async acquireConcurrencySlot() {
101
+ while (this.concurrentRequests >= this.maxConcurrentRequests) {
102
+ await this.sleep(10); // Esperar 10ms
103
+ }
104
+ this.concurrentRequests++;
105
+ }
106
+ releaseConcurrencySlot() {
107
+ this.concurrentRequests = Math.max(0, this.concurrentRequests - 1);
108
+ }
109
+ /**
110
+ * Queue para operaciones batch con rate limiting
111
+ */
112
+ async queueOperation(operation) {
113
+ return new Promise((resolve, reject) => {
114
+ this.requestQueue.push(async () => {
115
+ try {
116
+ const result = await this.executeWithRetry(operation);
117
+ resolve(result);
118
+ }
119
+ catch (error) {
120
+ reject(error);
121
+ }
122
+ });
123
+ this.processQueue();
124
+ });
125
+ }
126
+ async processQueue() {
127
+ if (this.isProcessingQueue || this.requestQueue.length === 0) {
128
+ return;
129
+ }
130
+ this.isProcessingQueue = true;
131
+ while (this.requestQueue.length > 0) {
132
+ const operation = this.requestQueue.shift();
133
+ try {
134
+ await operation();
135
+ }
136
+ catch (error) {
137
+ console.error("Queue operation failed:", error);
138
+ }
139
+ // Rate limiting: pequeña pausa entre operaciones
140
+ if (this.requestQueue.length > 0) {
141
+ await this.sleep(50); // 50ms entre requests
142
+ }
143
+ }
144
+ this.isProcessingQueue = false;
145
+ }
146
+ /**
147
+ * Obtener estadísticas de throttling
148
+ */
149
+ getStats() {
150
+ return { ...this.stats };
151
+ }
152
+ /**
153
+ * Resetear estadísticas
154
+ */
155
+ resetStats() {
156
+ this.stats = {
157
+ totalRequests: 0,
158
+ throttledRequests: 0,
159
+ retriedRequests: 0,
160
+ averageLatency: 0,
161
+ };
162
+ }
163
+ updateLatencyStats(latency) {
164
+ const totalRequests = this.stats.totalRequests;
165
+ this.stats.averageLatency =
166
+ (this.stats.averageLatency * (totalRequests - 1) + latency) /
167
+ totalRequests;
168
+ }
169
+ sleep(ms) {
170
+ return new Promise((resolve) => setTimeout(resolve, ms));
171
+ }
172
+ /**
173
+ * Configurar límites de concurrencia dinámicamente
174
+ */
175
+ setConcurrencyLimit(limit) {
176
+ this.maxConcurrentRequests = Math.max(1, Math.min(limit, 100));
177
+ }
178
+ /**
179
+ * Obtener métricas de salud del throttling
180
+ */
181
+ getHealthMetrics() {
182
+ const stats = this.getStats();
183
+ return {
184
+ throttleRate: stats.totalRequests > 0
185
+ ? stats.throttledRequests / stats.totalRequests
186
+ : 0,
187
+ retryRate: stats.totalRequests > 0
188
+ ? stats.retriedRequests / stats.totalRequests
189
+ : 0,
190
+ avgLatency: stats.averageLatency,
191
+ concurrentRequests: this.concurrentRequests,
192
+ queueSize: this.requestQueue.length,
193
+ };
194
+ }
195
+ }
196
+ exports.ThrottleManager = ThrottleManager;
197
+ // Instancia singleton
198
+ const throttleManager = new ThrottleManager();
199
+ exports.throttleManager = throttleManager;
200
+ exports.default = throttleManager;
201
+ //# sourceMappingURL=throttle-manager.js.map
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @file transaction-manager.ts
3
+ * @description Transaction simulation and rollback system for DynamoDB
4
+ * @author Miguel Alejandro
5
+ * @fecha 2025-08-31
6
+ */
7
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
8
+ interface TransactionOperation {
9
+ type: "create" | "update" | "delete";
10
+ tableName: string;
11
+ key: Record<string, any>;
12
+ item?: Record<string, any>;
13
+ conditionExpression?: string;
14
+ rollbackData?: Record<string, any>;
15
+ }
16
+ interface TransactionSnapshot {
17
+ id: string;
18
+ operations: TransactionOperation[];
19
+ timestamp: number;
20
+ status: "pending" | "committed" | "rolled_back" | "failed";
21
+ }
22
+ interface TransactionConfig {
23
+ maxOperationsPerTransaction: number;
24
+ snapshotTTL: number;
25
+ enableOptimisticLocking: boolean;
26
+ maxRetries: number;
27
+ }
28
+ declare class TransactionManager {
29
+ private static readonly DEFAULT_CONFIG;
30
+ private client;
31
+ private config;
32
+ private activeTransactions;
33
+ private snapshots;
34
+ constructor(client: DynamoDBClient, config?: Partial<TransactionConfig>);
35
+ /**
36
+ * Iniciar nueva transacción
37
+ */
38
+ beginTransaction(operations: TransactionOperation[]): Promise<string>;
39
+ /**
40
+ * Ejecutar transacción usando DynamoDB TransactWrite
41
+ */
42
+ commitTransaction(transactionId: string): Promise<void>;
43
+ /**
44
+ * Rollback manual de transacción
45
+ */
46
+ rollbackTransaction(transactionId: string): Promise<void>;
47
+ /**
48
+ * Crear snapshot del estado actual para rollback
49
+ */
50
+ private createSnapshot;
51
+ /**
52
+ * Obtener estado actual de un item
53
+ */
54
+ private getCurrentItemState;
55
+ /**
56
+ * Crear operaciones inversas para rollback
57
+ */
58
+ private createRollbackOperations;
59
+ /**
60
+ * Ejecutar lote de operaciones usando TransactWrite
61
+ */
62
+ private executeBatch;
63
+ /**
64
+ * Dividir operaciones en lotes según límites DynamoDB
65
+ */
66
+ private splitIntoBatches;
67
+ /**
68
+ * Validar operaciones antes de transacción
69
+ */
70
+ private validateOperations;
71
+ /**
72
+ * Limpiar snapshots expirados
73
+ */
74
+ private cleanupExpiredSnapshots;
75
+ /**
76
+ * Obtener estado de transacción
77
+ */
78
+ getTransactionStatus(transactionId: string): TransactionSnapshot | null;
79
+ /**
80
+ * Listar transacciones activas
81
+ */
82
+ getActiveTransactions(): TransactionSnapshot[];
83
+ private generateTransactionId;
84
+ }
85
+ declare class TransactionError extends Error {
86
+ constructor(message: string);
87
+ }
88
+ export { TransactionConfig, TransactionError, TransactionManager, TransactionOperation, TransactionSnapshot, };