@arcaelas/dynamite 1.0.17 → 1.0.19

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 (67) hide show
  1. package/package.json +40 -2
  2. package/src/@types/index.d.ts +116 -75
  3. package/src/core/client.d.ts +36 -0
  4. package/src/core/client.js +80 -27
  5. package/src/core/decorator.d.ts +44 -0
  6. package/src/core/decorator.js +133 -0
  7. package/src/core/method.d.ts +73 -0
  8. package/src/core/method.js +140 -0
  9. package/src/core/table.d.ts +44 -86
  10. package/src/core/table.js +510 -310
  11. package/src/decorators/indexes.d.ts +38 -0
  12. package/src/decorators/indexes.js +67 -0
  13. package/src/decorators/relations.d.ts +55 -0
  14. package/src/decorators/relations.js +84 -0
  15. package/src/decorators/timestamps.d.ts +54 -0
  16. package/src/decorators/timestamps.js +67 -0
  17. package/src/decorators/transforms.d.ts +86 -0
  18. package/src/decorators/transforms.js +154 -0
  19. package/src/index.d.ts +10 -16
  20. package/src/index.js +50 -32
  21. package/src/index.test.d.ts +13 -0
  22. package/src/index.test.js +1992 -0
  23. package/src/utils/relations.d.ts +34 -12
  24. package/src/utils/relations.js +109 -133
  25. package/src/@types/index.js +0 -9
  26. package/src/core/wrapper.d.ts +0 -17
  27. package/src/core/wrapper.js +0 -46
  28. package/src/decorators/belongs_to.d.ts +0 -1
  29. package/src/decorators/belongs_to.js +0 -24
  30. package/src/decorators/created_at.d.ts +0 -1
  31. package/src/decorators/created_at.js +0 -11
  32. package/src/decorators/default.d.ts +0 -1
  33. package/src/decorators/default.js +0 -47
  34. package/src/decorators/has_many.d.ts +0 -1
  35. package/src/decorators/has_many.js +0 -24
  36. package/src/decorators/index.d.ts +0 -11
  37. package/src/decorators/index.js +0 -36
  38. package/src/decorators/index_sort.d.ts +0 -12
  39. package/src/decorators/index_sort.js +0 -43
  40. package/src/decorators/mutate.d.ts +0 -2
  41. package/src/decorators/mutate.js +0 -51
  42. package/src/decorators/name.d.ts +0 -1
  43. package/src/decorators/name.js +0 -28
  44. package/src/decorators/not_null.d.ts +0 -1
  45. package/src/decorators/not_null.js +0 -13
  46. package/src/decorators/primary_key.d.ts +0 -6
  47. package/src/decorators/primary_key.js +0 -30
  48. package/src/decorators/updated_at.d.ts +0 -12
  49. package/src/decorators/updated_at.js +0 -26
  50. package/src/decorators/validate.d.ts +0 -1
  51. package/src/decorators/validate.js +0 -53
  52. package/src/utils/batch-relations.d.ts +0 -14
  53. package/src/utils/batch-relations.js +0 -131
  54. package/src/utils/circular-detector.d.ts +0 -82
  55. package/src/utils/circular-detector.js +0 -212
  56. package/src/utils/memory-manager.d.ts +0 -42
  57. package/src/utils/memory-manager.js +0 -107
  58. package/src/utils/naming.d.ts +0 -8
  59. package/src/utils/naming.js +0 -18
  60. package/src/utils/projection.d.ts +0 -12
  61. package/src/utils/projection.js +0 -51
  62. package/src/utils/security-validator.d.ts +0 -49
  63. package/src/utils/security-validator.js +0 -163
  64. package/src/utils/throttle-manager.d.ts +0 -78
  65. package/src/utils/throttle-manager.js +0 -201
  66. package/src/utils/transaction-manager.d.ts +0 -88
  67. package/src/utils/transaction-manager.js +0 -300
@@ -1,201 +0,0 @@
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
@@ -1,88 +0,0 @@
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, };
@@ -1,300 +0,0 @@
1
- "use strict";
2
- /**
3
- * @file transaction-manager.ts
4
- * @description Transaction simulation and rollback system for DynamoDB
5
- * @author Miguel Alejandro
6
- * @fecha 2025-08-31
7
- */
8
- var __importDefault = (this && this.__importDefault) || function (mod) {
9
- return (mod && mod.__esModule) ? mod : { "default": mod };
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.TransactionManager = exports.TransactionError = void 0;
13
- const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
14
- const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
15
- const security_validator_1 = __importDefault(require("./security-validator"));
16
- const throttle_manager_1 = __importDefault(require("./throttle-manager"));
17
- class TransactionManager {
18
- static { this.DEFAULT_CONFIG = {
19
- maxOperationsPerTransaction: 25, // DynamoDB limit
20
- snapshotTTL: 3600000, // 1 hour
21
- enableOptimisticLocking: true,
22
- maxRetries: 3,
23
- }; }
24
- constructor(client, config) {
25
- this.activeTransactions = new Map();
26
- this.snapshots = new Map();
27
- this.client = client;
28
- this.config = { ...TransactionManager.DEFAULT_CONFIG, ...config };
29
- // Cleanup expired snapshots
30
- setInterval(() => this.cleanupExpiredSnapshots(), 300000); // 5 minutes
31
- }
32
- /**
33
- * Iniciar nueva transacción
34
- */
35
- async beginTransaction(operations) {
36
- const transactionId = this.generateTransactionId();
37
- // Validar operaciones
38
- this.validateOperations(operations);
39
- // Crear snapshot del estado actual para rollback
40
- const snapshot = await this.createSnapshot(transactionId, operations);
41
- this.activeTransactions.set(transactionId, snapshot);
42
- this.snapshots.set(transactionId, snapshot);
43
- return transactionId;
44
- }
45
- /**
46
- * Ejecutar transacción usando DynamoDB TransactWrite
47
- */
48
- async commitTransaction(transactionId) {
49
- const snapshot = this.activeTransactions.get(transactionId);
50
- if (!snapshot) {
51
- throw new TransactionError(`Transaction ${transactionId} not found`);
52
- }
53
- try {
54
- snapshot.status = "pending";
55
- // Agrupar operaciones en lotes de 25 (límite DynamoDB)
56
- const batches = this.splitIntoBatches(snapshot.operations);
57
- for (const batch of batches) {
58
- await this.executeBatch(batch);
59
- }
60
- snapshot.status = "committed";
61
- this.activeTransactions.delete(transactionId);
62
- }
63
- catch (error) {
64
- snapshot.status = "failed";
65
- // Intentar rollback automático
66
- try {
67
- await this.rollbackTransaction(transactionId);
68
- }
69
- catch (rollbackError) {
70
- console.error("Rollback failed after commit error:", rollbackError);
71
- }
72
- throw new TransactionError(`Transaction commit failed: ${error.message}`);
73
- }
74
- }
75
- /**
76
- * Rollback manual de transacción
77
- */
78
- async rollbackTransaction(transactionId) {
79
- const snapshot = this.snapshots.get(transactionId);
80
- if (!snapshot) {
81
- throw new TransactionError(`Transaction snapshot ${transactionId} not found`);
82
- }
83
- try {
84
- // Crear operaciones inversas para rollback
85
- const rollbackOps = this.createRollbackOperations(snapshot.operations);
86
- if (rollbackOps.length > 0) {
87
- const batches = this.splitIntoBatches(rollbackOps);
88
- for (const batch of batches) {
89
- await this.executeBatch(batch);
90
- }
91
- }
92
- snapshot.status = "rolled_back";
93
- this.activeTransactions.delete(transactionId);
94
- }
95
- catch (error) {
96
- throw new TransactionError(`Rollback failed: ${error.message}`);
97
- }
98
- }
99
- /**
100
- * Crear snapshot del estado actual para rollback
101
- */
102
- async createSnapshot(transactionId, operations) {
103
- const snapshot = {
104
- id: transactionId,
105
- operations: [],
106
- timestamp: Date.now(),
107
- status: "pending",
108
- };
109
- // Para cada operación, guardar el estado actual del item
110
- for (const op of operations) {
111
- try {
112
- const currentItem = await this.getCurrentItemState(op.tableName, op.key);
113
- const snapshotOp = {
114
- ...op,
115
- rollbackData: currentItem || undefined, // Estado actual para rollback
116
- };
117
- snapshot.operations.push(snapshotOp);
118
- }
119
- catch (error) {
120
- if (error.name !== "ResourceNotFoundException") {
121
- throw error;
122
- }
123
- // Item no existe, sin datos de rollback necesarios
124
- snapshot.operations.push(op);
125
- }
126
- }
127
- return snapshot;
128
- }
129
- /**
130
- * Obtener estado actual de un item
131
- */
132
- async getCurrentItemState(tableName, key) {
133
- const params = {
134
- TransactItems: [
135
- {
136
- Get: {
137
- TableName: tableName,
138
- Key: (0, util_dynamodb_1.marshall)(key),
139
- },
140
- },
141
- ],
142
- };
143
- const result = await throttle_manager_1.default.executeWithRetry(() => this.client.send(new client_dynamodb_1.TransactGetItemsCommand(params)), `GetItem-${tableName}`);
144
- return result.Responses?.[0]?.Item
145
- ? (0, util_dynamodb_1.unmarshall)(result.Responses[0].Item)
146
- : null;
147
- }
148
- /**
149
- * Crear operaciones inversas para rollback
150
- */
151
- createRollbackOperations(operations) {
152
- const rollbackOps = [];
153
- // Procesar en orden inverso
154
- for (let i = operations.length - 1; i >= 0; i--) {
155
- const op = operations[i];
156
- switch (op.type) {
157
- case "create":
158
- // Para rollback de create: delete el item
159
- rollbackOps.push({
160
- type: "delete",
161
- tableName: op.tableName,
162
- key: op.key,
163
- });
164
- break;
165
- case "update":
166
- if (op.rollbackData) {
167
- // Para rollback de update: restore estado anterior
168
- rollbackOps.push({
169
- type: "update",
170
- tableName: op.tableName,
171
- key: op.key,
172
- item: op.rollbackData,
173
- });
174
- }
175
- break;
176
- case "delete":
177
- if (op.rollbackData) {
178
- // Para rollback de delete: recrear el item
179
- rollbackOps.push({
180
- type: "create",
181
- tableName: op.tableName,
182
- key: op.key,
183
- item: op.rollbackData,
184
- });
185
- }
186
- break;
187
- }
188
- }
189
- return rollbackOps;
190
- }
191
- /**
192
- * Ejecutar lote de operaciones usando TransactWrite
193
- */
194
- async executeBatch(operations) {
195
- const transactItems = operations.map((op) => {
196
- security_validator_1.default.validateItemSize(op.item || op.key);
197
- switch (op.type) {
198
- case "create":
199
- case "update":
200
- return {
201
- Put: {
202
- TableName: op.tableName,
203
- Item: (0, util_dynamodb_1.marshall)(op.item, { removeUndefinedValues: true }),
204
- ConditionExpression: op.conditionExpression,
205
- },
206
- };
207
- case "delete":
208
- return {
209
- Delete: {
210
- TableName: op.tableName,
211
- Key: (0, util_dynamodb_1.marshall)(op.key),
212
- ConditionExpression: op.conditionExpression,
213
- },
214
- };
215
- default:
216
- throw new TransactionError(`Unknown operation type: ${op.type}`);
217
- }
218
- });
219
- const command = new client_dynamodb_1.TransactWriteItemsCommand({
220
- TransactItems: transactItems,
221
- });
222
- await throttle_manager_1.default.executeWithRetry(() => this.client.send(command), "TransactWrite");
223
- }
224
- /**
225
- * Dividir operaciones en lotes según límites DynamoDB
226
- */
227
- splitIntoBatches(operations) {
228
- const batches = [];
229
- const batchSize = this.config.maxOperationsPerTransaction;
230
- for (let i = 0; i < operations.length; i += batchSize) {
231
- batches.push(operations.slice(i, i + batchSize));
232
- }
233
- return batches;
234
- }
235
- /**
236
- * Validar operaciones antes de transacción
237
- */
238
- validateOperations(operations) {
239
- if (operations.length === 0) {
240
- throw new TransactionError("Transaction must contain at least one operation");
241
- }
242
- if (operations.length > this.config.maxOperationsPerTransaction * 10) {
243
- throw new TransactionError(`Too many operations: ${operations.length}`);
244
- }
245
- for (const op of operations) {
246
- // Validar estructura de la operación
247
- if (!op.tableName || !op.key) {
248
- throw new TransactionError("Invalid operation: missing tableName or key");
249
- }
250
- // Validar datos de seguridad
251
- security_validator_1.default.validateQueryFilters(op.key);
252
- if (op.item) {
253
- security_validator_1.default.validateValue(op.item);
254
- }
255
- }
256
- }
257
- /**
258
- * Limpiar snapshots expirados
259
- */
260
- cleanupExpiredSnapshots() {
261
- const now = Date.now();
262
- const expired = [];
263
- for (const [id, snapshot] of this.snapshots.entries()) {
264
- if (now - snapshot.timestamp > this.config.snapshotTTL) {
265
- expired.push(id);
266
- }
267
- }
268
- expired.forEach((id) => {
269
- this.snapshots.delete(id);
270
- this.activeTransactions.delete(id);
271
- });
272
- if (expired.length > 0) {
273
- console.log(`Cleaned up ${expired.length} expired transaction snapshots`);
274
- }
275
- }
276
- /**
277
- * Obtener estado de transacción
278
- */
279
- getTransactionStatus(transactionId) {
280
- return this.snapshots.get(transactionId) || null;
281
- }
282
- /**
283
- * Listar transacciones activas
284
- */
285
- getActiveTransactions() {
286
- return Array.from(this.activeTransactions.values());
287
- }
288
- generateTransactionId() {
289
- return `txn_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
290
- }
291
- }
292
- exports.TransactionManager = TransactionManager;
293
- class TransactionError extends Error {
294
- constructor(message) {
295
- super(message);
296
- this.name = "TransactionError";
297
- }
298
- }
299
- exports.TransactionError = TransactionError;
300
- //# sourceMappingURL=transaction-manager.js.map