@arcaelas/dynamite 1.0.10 → 1.0.13

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 (105) hide show
  1. package/LICENSE.txt +32 -0
  2. package/README.txt +206 -0
  3. package/package.json +5 -4
  4. package/src/@types/index.d.ts +96 -0
  5. package/src/@types/index.js +9 -0
  6. package/{build/src → src}/core/client.d.ts +4 -0
  7. package/{build/src → src}/core/client.js +14 -2
  8. package/src/core/table.d.ts +98 -0
  9. package/src/core/table.js +459 -0
  10. package/src/core/wrapper.d.ts +17 -0
  11. package/src/core/wrapper.js +46 -0
  12. package/src/decorators/belongs_to.d.ts +1 -0
  13. package/src/decorators/belongs_to.js +24 -0
  14. package/src/decorators/created_at.d.ts +1 -0
  15. package/{build/src → src}/decorators/created_at.js +0 -7
  16. package/src/decorators/default.d.ts +1 -0
  17. package/{build/src → src}/decorators/default.js +2 -12
  18. package/src/decorators/has_many.d.ts +1 -0
  19. package/src/decorators/has_many.js +24 -0
  20. package/{build/src → src}/decorators/index.d.ts +4 -1
  21. package/src/decorators/index.js +36 -0
  22. package/src/decorators/index_sort.d.ts +12 -0
  23. package/src/decorators/index_sort.js +43 -0
  24. package/src/decorators/mutate.d.ts +2 -0
  25. package/{build/src → src}/decorators/mutate.js +2 -11
  26. package/src/decorators/name.d.ts +1 -0
  27. package/src/decorators/name.js +28 -0
  28. package/src/decorators/not_null.d.ts +1 -0
  29. package/{build/src → src}/decorators/not_null.js +0 -7
  30. package/src/decorators/primary_key.d.ts +6 -0
  31. package/src/decorators/primary_key.js +30 -0
  32. package/src/decorators/updated_at.d.ts +12 -0
  33. package/src/decorators/updated_at.js +26 -0
  34. package/src/decorators/validate.d.ts +1 -0
  35. package/{build/src → src}/decorators/validate.js +0 -7
  36. package/{build/src → src}/index.d.ts +9 -0
  37. package/{build/src → src}/index.js +14 -5
  38. package/{build/src → src}/utils/batch-relations.js +2 -1
  39. package/src/utils/circular-detector.d.ts +82 -0
  40. package/src/utils/circular-detector.js +209 -0
  41. package/src/utils/memory-manager.d.ts +42 -0
  42. package/src/utils/memory-manager.js +108 -0
  43. package/{build/src → src}/utils/projection.js +3 -2
  44. package/src/utils/relations.d.ts +17 -0
  45. package/src/utils/relations.js +166 -0
  46. package/src/utils/security-validator.d.ts +49 -0
  47. package/src/utils/security-validator.js +152 -0
  48. package/src/utils/throttle-manager.d.ts +78 -0
  49. package/src/utils/throttle-manager.js +196 -0
  50. package/src/utils/transaction-manager.d.ts +88 -0
  51. package/src/utils/transaction-manager.js +298 -0
  52. package/build/__tests__/crud.spec.d.ts +0 -7
  53. package/build/__tests__/crud.spec.js +0 -287
  54. package/build/__tests__/crud.spec.js.map +0 -1
  55. package/build/__tests__/debug-decorators.spec.d.ts +0 -7
  56. package/build/__tests__/debug-decorators.spec.js +0 -143
  57. package/build/__tests__/debug-decorators.spec.js.map +0 -1
  58. package/build/__tests__/decorators.spec.d.ts +0 -7
  59. package/build/__tests__/decorators.spec.js +0 -203
  60. package/build/__tests__/decorators.spec.js.map +0 -1
  61. package/build/__tests__/instance-crud.spec.d.ts +0 -7
  62. package/build/__tests__/instance-crud.spec.js +0 -184
  63. package/build/__tests__/instance-crud.spec.js.map +0 -1
  64. package/build/src/core/client.js.map +0 -1
  65. package/build/src/core/table.d.ts +0 -164
  66. package/build/src/core/table.js +0 -406
  67. package/build/src/core/table.js.map +0 -1
  68. package/build/src/core/wrapper.d.ts +0 -54
  69. package/build/src/core/wrapper.js +0 -27
  70. package/build/src/core/wrapper.js.map +0 -1
  71. package/build/src/decorators/created_at.d.ts +0 -8
  72. package/build/src/decorators/created_at.js.map +0 -1
  73. package/build/src/decorators/default.d.ts +0 -8
  74. package/build/src/decorators/default.js.map +0 -1
  75. package/build/src/decorators/index.js +0 -26
  76. package/build/src/decorators/index.js.map +0 -1
  77. package/build/src/decorators/index_sort.d.ts +0 -8
  78. package/build/src/decorators/index_sort.js +0 -30
  79. package/build/src/decorators/index_sort.js.map +0 -1
  80. package/build/src/decorators/mutate.d.ts +0 -9
  81. package/build/src/decorators/mutate.js.map +0 -1
  82. package/build/src/decorators/name.d.ts +0 -8
  83. package/build/src/decorators/name.js +0 -42
  84. package/build/src/decorators/name.js.map +0 -1
  85. package/build/src/decorators/not_null.d.ts +0 -8
  86. package/build/src/decorators/not_null.js.map +0 -1
  87. package/build/src/decorators/primary_key.d.ts +0 -8
  88. package/build/src/decorators/primary_key.js +0 -26
  89. package/build/src/decorators/primary_key.js.map +0 -1
  90. package/build/src/decorators/updated_at.d.ts +0 -8
  91. package/build/src/decorators/updated_at.js +0 -18
  92. package/build/src/decorators/updated_at.js.map +0 -1
  93. package/build/src/decorators/validate.d.ts +0 -8
  94. package/build/src/decorators/validate.js.map +0 -1
  95. package/build/src/index.js.map +0 -1
  96. package/build/src/utils/batch-relations.js.map +0 -1
  97. package/build/src/utils/naming.js.map +0 -1
  98. package/build/src/utils/projection.js.map +0 -1
  99. package/build/src/utils/relations.d.ts +0 -23
  100. package/build/src/utils/relations.js +0 -205
  101. package/build/src/utils/relations.js.map +0 -1
  102. /package/{build/src → src}/utils/batch-relations.d.ts +0 -0
  103. /package/{build/src → src}/utils/naming.d.ts +0 -0
  104. /package/{build/src → src}/utils/naming.js +0 -0
  105. /package/{build/src → src}/utils/projection.d.ts +0 -0
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @file memory-manager.ts
3
+ * @description Memory management and leak prevention
4
+ * @author Miguel Alejandro
5
+ * @fecha 2025-08-31
6
+ */
7
+ declare class MemoryManager {
8
+ private static instance;
9
+ private cleanupTasks;
10
+ private maxCacheSize;
11
+ private maxCacheAge;
12
+ static getInstance(): MemoryManager;
13
+ /**
14
+ * Registrar una tarea de limpieza
15
+ */
16
+ registerCleanup(id: string, cleanup: () => void, intervalMs?: number): void;
17
+ /**
18
+ * Desregistrar y limpiar tarea
19
+ */
20
+ unregisterCleanup(id: string): void;
21
+ /**
22
+ * Limpiar cache con límites de tamaño y edad
23
+ */
24
+ cleanCache<T>(cache: Map<string, {
25
+ data: T;
26
+ expires: number;
27
+ created: number;
28
+ }>, maxSize?: number): void;
29
+ /**
30
+ * Monitorear uso de memoria
31
+ */
32
+ getMemoryUsage(): NodeJS.MemoryUsage;
33
+ /**
34
+ * Forzar garbage collection si está disponible
35
+ */
36
+ forceGC(): void;
37
+ /**
38
+ * Cleanup completo al shutdown
39
+ */
40
+ shutdown(): void;
41
+ }
42
+ export default MemoryManager;
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ /**
3
+ * @file memory-manager.ts
4
+ * @description Memory management and leak prevention
5
+ * @author Miguel Alejandro
6
+ * @fecha 2025-08-31
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ class MemoryManager {
10
+ constructor() {
11
+ this.cleanupTasks = new Map();
12
+ this.maxCacheSize = 1000;
13
+ this.maxCacheAge = 300000; // 5 minutos
14
+ }
15
+ static getInstance() {
16
+ if (!MemoryManager.instance) {
17
+ MemoryManager.instance = new MemoryManager();
18
+ }
19
+ return MemoryManager.instance;
20
+ }
21
+ /**
22
+ * Registrar una tarea de limpieza
23
+ */
24
+ registerCleanup(id, cleanup, intervalMs) {
25
+ // Limpiar tarea previa si existe
26
+ this.unregisterCleanup(id);
27
+ const task = { id, cleanup };
28
+ if (intervalMs) {
29
+ task.interval = setInterval(() => {
30
+ try {
31
+ cleanup();
32
+ }
33
+ catch (error) {
34
+ console.warn(`Cleanup task ${id} failed:`, error);
35
+ }
36
+ }, intervalMs);
37
+ }
38
+ this.cleanupTasks.set(id, task);
39
+ }
40
+ /**
41
+ * Desregistrar y limpiar tarea
42
+ */
43
+ unregisterCleanup(id) {
44
+ const task = this.cleanupTasks.get(id);
45
+ if (task) {
46
+ if (task.interval) {
47
+ clearInterval(task.interval);
48
+ }
49
+ task.cleanup();
50
+ this.cleanupTasks.delete(id);
51
+ }
52
+ }
53
+ /**
54
+ * Limpiar cache con límites de tamaño y edad
55
+ */
56
+ cleanCache(cache, maxSize = this.maxCacheSize) {
57
+ const now = Date.now();
58
+ // Remover entradas expiradas
59
+ for (const [key, entry] of cache.entries()) {
60
+ if (entry.expires <= now || (now - entry.created) > this.maxCacheAge) {
61
+ cache.delete(key);
62
+ }
63
+ }
64
+ // Si aún excede el tamaño, remover las más antiguas
65
+ if (cache.size > maxSize) {
66
+ const entries = Array.from(cache.entries())
67
+ .sort((a, b) => a[1].created - b[1].created);
68
+ const toRemove = entries.slice(0, cache.size - maxSize);
69
+ toRemove.forEach(([key]) => cache.delete(key));
70
+ }
71
+ }
72
+ /**
73
+ * Monitorear uso de memoria
74
+ */
75
+ getMemoryUsage() {
76
+ return process.memoryUsage();
77
+ }
78
+ /**
79
+ * Forzar garbage collection si está disponible
80
+ */
81
+ forceGC() {
82
+ if (global.gc) {
83
+ global.gc();
84
+ }
85
+ }
86
+ /**
87
+ * Cleanup completo al shutdown
88
+ */
89
+ shutdown() {
90
+ console.log('MemoryManager: Iniciando cleanup completo...');
91
+ for (const [id, task] of this.cleanupTasks.entries()) {
92
+ try {
93
+ this.unregisterCleanup(id);
94
+ }
95
+ catch (error) {
96
+ console.warn(`Error cleaning up task ${id}:`, error);
97
+ }
98
+ }
99
+ this.cleanupTasks.clear();
100
+ this.forceGC();
101
+ }
102
+ }
103
+ // Cleanup automático en shutdown
104
+ process.on('SIGTERM', () => MemoryManager.getInstance().shutdown());
105
+ process.on('SIGINT', () => MemoryManager.getInstance().shutdown());
106
+ process.on('uncaughtException', () => MemoryManager.getInstance().shutdown());
107
+ exports.default = MemoryManager;
108
+ //# sourceMappingURL=memory-manager.js.map
@@ -7,6 +7,7 @@
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.getSmartAttributes = exports.optimizeRelationAttributes = exports.buildProjection = void 0;
10
+ const wrapper_1 = require("../core/wrapper");
10
11
  /** Build DynamoDB ProjectionExpression */
11
12
  const buildProjection = (attributes, meta) => {
12
13
  if (!attributes?.length || !meta)
@@ -33,11 +34,11 @@ const optimizeRelationAttributes = (include, baseAttributes) => {
33
34
  exports.optimizeRelationAttributes = optimizeRelationAttributes;
34
35
  /** Smart attribute selection for common patterns */
35
36
  const getSmartAttributes = (Model, scenario) => {
36
- const meta = Model.getMeta();
37
+ const meta = (0, wrapper_1.mustMeta)(Model);
37
38
  const columns = Array.from(meta.columns.keys());
38
39
  switch (scenario) {
39
40
  case "minimal":
40
- return columns.filter((col) => meta.columns.get(col)?.isKey ||
41
+ return columns.filter((col) => meta.columns.get(col)?.primaryKey ||
41
42
  ["id", "name", "title", "email"].includes(col));
42
43
  case "list":
43
44
  return columns.filter((col) => !["description", "content", "body", "metadata"].includes(col));
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @file relations.ts
3
+ * @descripcion Sistema de relaciones optimizado
4
+ * @autor Miguel Alejandro
5
+ * @fecha 2025-01-28
6
+ */
7
+ /** Decorador @hasMany para relaciones 1:N */
8
+ export declare const hasMany: (targetModel: () => any, foreignKey: string, localKey?: string) => (target: any, propertyKey: string) => void;
9
+ /** Decorador @belongsTo para relaciones N:1 */
10
+ export declare const belongsTo: (targetModel: () => any, localKey: string, foreignKey?: string) => (target: any, propertyKey: string) => void;
11
+ /** Procesamiento optimizado de includes con batch loading transparente */
12
+ export declare const processIncludes: (Model: any, items: any[], include: Record<string, any>, depth?: number) => Promise<any[]>;
13
+ /** Separar opciones de query e include */
14
+ export declare const separateQueryOptions: (options: Record<string, any>) => {
15
+ queryOptions: Record<string, any>;
16
+ includeOptions: Record<string, any> | undefined;
17
+ };
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ /**
3
+ * @file relations.ts
4
+ * @descripcion Sistema de relaciones optimizado
5
+ * @autor Miguel Alejandro
6
+ * @fecha 2025-01-28
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.separateQueryOptions = exports.processIncludes = exports.belongsTo = exports.hasMany = void 0;
10
+ // =============================================================================
11
+ // IMPORTS
12
+ // =============================================================================
13
+ const wrapper_1 = require("../core/wrapper");
14
+ const naming_1 = require("./naming");
15
+ // =============================================================================
16
+ // DECORATORS
17
+ // =============================================================================
18
+ /** Decorador @hasMany para relaciones 1:N */
19
+ const hasMany = (targetModel, foreignKey, localKey = "id") => (target, propertyKey) => {
20
+ (0, wrapper_1.ensureConfig)(target.constructor, (0, naming_1.toSnakePlural)(target.constructor.name)).relations.set(propertyKey, {
21
+ type: "hasMany",
22
+ targetModel,
23
+ foreignKey,
24
+ localKey,
25
+ });
26
+ Object.defineProperty(target, propertyKey, {
27
+ get() {
28
+ const cached = this[`_${propertyKey}`];
29
+ return cached !== undefined
30
+ ? cached
31
+ : (console.warn(`Relación ${propertyKey} no cargada. Use includes en la consulta.`),
32
+ []);
33
+ },
34
+ configurable: true,
35
+ enumerable: false,
36
+ });
37
+ };
38
+ exports.hasMany = hasMany;
39
+ /** Decorador @belongsTo para relaciones N:1 */
40
+ const belongsTo = (targetModel, localKey, foreignKey = "id") => (target, propertyKey) => {
41
+ (0, wrapper_1.ensureConfig)(target.constructor, (0, naming_1.toSnakePlural)(target.constructor.name)).relations.set(propertyKey, {
42
+ type: "belongsTo",
43
+ targetModel,
44
+ localKey,
45
+ foreignKey,
46
+ });
47
+ Object.defineProperty(target, propertyKey, {
48
+ get() {
49
+ const cached = this[`_${propertyKey}`];
50
+ return cached !== undefined
51
+ ? cached
52
+ : (console.warn(`Relación ${propertyKey} no cargada. Use includes en la consulta.`),
53
+ null);
54
+ },
55
+ configurable: true,
56
+ enumerable: false,
57
+ });
58
+ };
59
+ exports.belongsTo = belongsTo;
60
+ // =============================================================================
61
+ // FUNCTIONS
62
+ // =============================================================================
63
+ /** Batch loading para relaciones hasMany */
64
+ const batchLoadHasMany = async (Model, items, relation, options = {}) => {
65
+ const { targetModel, foreignKey, localKey = "id" } = relation;
66
+ const parent_keys = items.map((item) => item[localKey]).filter(Boolean);
67
+ if (!parent_keys.length)
68
+ return new Map();
69
+ // Build query with relation options
70
+ let query = targetModel().where(foreignKey, "in", parent_keys);
71
+ // Apply additional filters if specified
72
+ if (options.where) {
73
+ const additionalFilters = Object.entries(options.where);
74
+ for (const [key, value] of additionalFilters) {
75
+ const currentResults = await query;
76
+ query = Promise.resolve(currentResults.filter((item) => item[key] === value));
77
+ }
78
+ }
79
+ const related_items = await query;
80
+ // TODO: Apply attributes selection
81
+ // For now, skip attributes filtering to ensure basic relations work
82
+ let processedItems = related_items;
83
+ // Apply other options like limit, order
84
+ let filteredItems = processedItems;
85
+ if (options.order === "DESC") {
86
+ filteredItems.sort((a, b) => b.id.localeCompare(a.id));
87
+ }
88
+ else if (options.order === "ASC") {
89
+ filteredItems.sort((a, b) => a.id.localeCompare(b.id));
90
+ }
91
+ if (options.limit) {
92
+ filteredItems = filteredItems.slice(0, options.limit);
93
+ }
94
+ const grouped = new Map();
95
+ filteredItems.forEach((item) => {
96
+ const key = item[foreignKey];
97
+ grouped.has(key) ? grouped.get(key).push(item) : grouped.set(key, [item]);
98
+ });
99
+ return grouped;
100
+ };
101
+ /** Batch loading para relaciones belongsTo */
102
+ const batchLoadBelongsTo = async (Model, items, relation, options = {}) => {
103
+ const { targetModel, localKey, foreignKey = "id" } = relation;
104
+ // Para BelongsTo: obtener valores de localKey (ej: category_id) de los items
105
+ const keys = items.map((item) => localKey ? item[localKey] : null).filter(Boolean);
106
+ if (!keys.length)
107
+ return new Map();
108
+ // Buscar en targetModel donde foreignKey (ej: id) esté en los keys
109
+ const fetched_items = await targetModel().where(foreignKey, "in", keys);
110
+ // TODO: Apply attributes selection
111
+ // For now, skip attributes filtering to ensure basic relations work
112
+ let processedItems = fetched_items;
113
+ const results = new Map();
114
+ // Mapear por foreignKey para que coincida con localKey de los items
115
+ processedItems.forEach((item) => results.set(item[foreignKey], item));
116
+ return results;
117
+ };
118
+ /** Procesamiento optimizado de includes con batch loading transparente */
119
+ const processIncludes = async (Model, items, include, depth = 0) => {
120
+ if (!include || depth > 10 || !items.length)
121
+ return items;
122
+ const meta = (0, wrapper_1.mustMeta)(Model);
123
+ const relation_promises = Object.entries(include).map(async ([relation_key, relation_options]) => {
124
+ const relation = meta.relations.get(relation_key);
125
+ if (!relation)
126
+ return;
127
+ const related_data = relation.type === "hasMany"
128
+ ? await batchLoadHasMany(Model, items, relation, relation_options)
129
+ : await batchLoadBelongsTo(Model, items, relation, relation_options);
130
+ items.forEach((item) => {
131
+ let key;
132
+ if (relation.type === "hasMany") {
133
+ // Para HasMany: usar localKey (ej: "id") del item actual
134
+ key = item[relation.localKey || "id"];
135
+ }
136
+ else {
137
+ // Para BelongsTo: usar localKey (ej: "category_id") del item actual
138
+ key = relation.localKey ? item[relation.localKey] : null;
139
+ }
140
+ const related = related_data.get(key);
141
+ // Usar una propiedad temporal para evitar conflictos con getters
142
+ Object.defineProperty(item, relation_key, {
143
+ value: relation.type === "hasMany" ? related || [] : related || null,
144
+ writable: true,
145
+ enumerable: true,
146
+ configurable: true
147
+ });
148
+ });
149
+ if (relation_options?.include && related_data.size) {
150
+ const all_related = Array.from(related_data.values())
151
+ .flat()
152
+ .filter(Boolean);
153
+ await (0, exports.processIncludes)(relation.targetModel(), all_related, relation_options.include, depth + 1);
154
+ }
155
+ });
156
+ await Promise.all(relation_promises);
157
+ return items;
158
+ };
159
+ exports.processIncludes = processIncludes;
160
+ /** Separar opciones de query e include */
161
+ const separateQueryOptions = (options) => {
162
+ const { include, ...queryOptions } = options;
163
+ return { queryOptions, includeOptions: include };
164
+ };
165
+ exports.separateQueryOptions = separateQueryOptions;
166
+ //# sourceMappingURL=relations.js.map
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @file security-validator.ts
3
+ * @description Security validation and NoSQL injection prevention
4
+ * @author Miguel Alejandro
5
+ * @fecha 2025-08-31
6
+ */
7
+ import { QueryOperator } from "../@types/index";
8
+ interface SecurityConfig {
9
+ maxStringLength: number;
10
+ maxArrayLength: number;
11
+ maxNestedDepth: number;
12
+ allowedOperators: QueryOperator[];
13
+ blockedPatterns: RegExp[];
14
+ }
15
+ declare class SecurityValidator {
16
+ private static readonly DEFAULT_CONFIG;
17
+ private config;
18
+ constructor(config?: Partial<SecurityConfig>);
19
+ /**
20
+ * Validar nombre de atributo/tabla
21
+ */
22
+ validateAttributeName(name: string): void;
23
+ /**
24
+ * Validar operador de query
25
+ */
26
+ validateOperator(operator: string): QueryOperator;
27
+ /**
28
+ * Validar valor de campo
29
+ */
30
+ validateValue(value: any, depth?: number): any;
31
+ /**
32
+ * Validar filtros de query completos
33
+ */
34
+ validateQueryFilters(filters: Record<string, any>): Record<string, any>;
35
+ /**
36
+ * Sanitizar string para DynamoDB
37
+ */
38
+ sanitizeString(input: string): string;
39
+ /**
40
+ * Validar tamaño de item para DynamoDB (límite 400KB)
41
+ */
42
+ validateItemSize(item: any): void;
43
+ }
44
+ declare class SecurityError extends Error {
45
+ constructor(message: string);
46
+ }
47
+ declare const securityValidator: SecurityValidator;
48
+ export { SecurityValidator, SecurityError, securityValidator };
49
+ export default securityValidator;
@@ -0,0 +1,152 @@
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.SecurityError = exports.SecurityValidator = void 0;
10
+ class SecurityValidator {
11
+ static { this.DEFAULT_CONFIG = {
12
+ maxStringLength: 10000,
13
+ maxArrayLength: 1000,
14
+ maxNestedDepth: 10,
15
+ allowedOperators: ["=", "!=", "<", "<=", ">", ">=", "in", "not-in", "contains", "begins-with"],
16
+ blockedPatterns: [
17
+ /\$\w+/g, // MongoDB operators
18
+ /javascript:/gi, // JavaScript injection
19
+ /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, // Script tags
20
+ /eval\s*\(/gi, // eval() calls
21
+ /function\s*\(/gi, // function declarations
22
+ /\{\s*\$\w+/g, // NoSQL operators
23
+ /\.\.\//g, // Path traversal
24
+ /union\s+select/gi, // SQL injection patterns
25
+ /drop\s+table/gi, // Destructive SQL
26
+ ]
27
+ }; }
28
+ constructor(config) {
29
+ this.config = { ...SecurityValidator.DEFAULT_CONFIG, ...config };
30
+ }
31
+ /**
32
+ * Validar nombre de atributo/tabla
33
+ */
34
+ validateAttributeName(name) {
35
+ if (!name || typeof name !== 'string') {
36
+ throw new SecurityError('Nombre de atributo inválido');
37
+ }
38
+ if (name.length > 255) {
39
+ throw new SecurityError('Nombre de atributo muy largo');
40
+ }
41
+ // Solo permitir caracteres alfanuméricos, guiones y guiones bajos
42
+ if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name)) {
43
+ throw new SecurityError('Nombre de atributo contiene caracteres inválidos');
44
+ }
45
+ // Verificar patrones bloqueados
46
+ for (const pattern of this.config.blockedPatterns) {
47
+ if (pattern.test(name)) {
48
+ throw new SecurityError('Nombre de atributo contiene patrones peligrosos');
49
+ }
50
+ }
51
+ }
52
+ /**
53
+ * Validar operador de query
54
+ */
55
+ validateOperator(operator) {
56
+ if (!this.config.allowedOperators.includes(operator)) {
57
+ throw new SecurityError(`Operador no permitido: ${operator}`);
58
+ }
59
+ return operator;
60
+ }
61
+ /**
62
+ * Validar valor de campo
63
+ */
64
+ validateValue(value, depth = 0) {
65
+ if (depth > this.config.maxNestedDepth) {
66
+ throw new SecurityError('Estructura de datos anidada muy profunda');
67
+ }
68
+ // Valores null/undefined son válidos
69
+ if (value === null || value === undefined) {
70
+ return value;
71
+ }
72
+ // Validar strings
73
+ if (typeof value === 'string') {
74
+ if (value.length > this.config.maxStringLength) {
75
+ throw new SecurityError('String muy largo');
76
+ }
77
+ for (const pattern of this.config.blockedPatterns) {
78
+ if (pattern.test(value)) {
79
+ throw new SecurityError('Valor contiene patrones peligrosos');
80
+ }
81
+ }
82
+ return value;
83
+ }
84
+ // Validar arrays
85
+ if (Array.isArray(value)) {
86
+ if (value.length > this.config.maxArrayLength) {
87
+ throw new SecurityError('Array muy largo');
88
+ }
89
+ return value.map(item => this.validateValue(item, depth + 1));
90
+ }
91
+ // Validar objetos
92
+ if (typeof value === 'object') {
93
+ const validated = {};
94
+ for (const [key, val] of Object.entries(value)) {
95
+ this.validateAttributeName(key);
96
+ validated[key] = this.validateValue(val, depth + 1);
97
+ }
98
+ return validated;
99
+ }
100
+ // Números, booleans son válidos
101
+ if (typeof value === 'number' || typeof value === 'boolean') {
102
+ return value;
103
+ }
104
+ throw new SecurityError(`Tipo de valor no permitido: ${typeof value}`);
105
+ }
106
+ /**
107
+ * Validar filtros de query completos
108
+ */
109
+ validateQueryFilters(filters) {
110
+ const validated = {};
111
+ for (const [key, value] of Object.entries(filters)) {
112
+ this.validateAttributeName(key);
113
+ validated[key] = this.validateValue(value);
114
+ }
115
+ return validated;
116
+ }
117
+ /**
118
+ * Sanitizar string para DynamoDB
119
+ */
120
+ sanitizeString(input) {
121
+ if (typeof input !== 'string') {
122
+ return String(input);
123
+ }
124
+ return input
125
+ .replace(/[\x00-\x1F\x7F]/g, '') // Remover caracteres de control
126
+ .trim()
127
+ .slice(0, this.config.maxStringLength);
128
+ }
129
+ /**
130
+ * Validar tamaño de item para DynamoDB (límite 400KB)
131
+ */
132
+ validateItemSize(item) {
133
+ const size = JSON.stringify(item).length;
134
+ const maxSize = 400 * 1024; // 400KB en bytes
135
+ if (size > maxSize) {
136
+ throw new SecurityError(`Item muy grande: ${size} bytes (máximo: ${maxSize} bytes)`);
137
+ }
138
+ }
139
+ }
140
+ exports.SecurityValidator = SecurityValidator;
141
+ class SecurityError extends Error {
142
+ constructor(message) {
143
+ super(message);
144
+ this.name = 'SecurityError';
145
+ }
146
+ }
147
+ exports.SecurityError = SecurityError;
148
+ // Instancia singleton
149
+ const securityValidator = new SecurityValidator();
150
+ exports.securityValidator = securityValidator;
151
+ exports.default = securityValidator;
152
+ //# 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 { ThrottleManager, RetryConfig, ThrottleStats, throttleManager };
78
+ export default throttleManager;