@arcaelas/dynamite 1.0.17 → 1.0.18
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/package.json +40 -2
- package/src/@types/index.d.ts +27 -21
- package/src/core/client.d.ts +36 -0
- package/src/core/client.js +80 -27
- package/src/core/decorator.d.ts +44 -0
- package/src/core/decorator.js +133 -0
- package/src/core/method.d.ts +73 -0
- package/src/core/method.js +140 -0
- package/src/core/table.d.ts +44 -86
- package/src/core/table.js +510 -310
- package/src/decorators/indexes.d.ts +38 -0
- package/src/decorators/indexes.js +67 -0
- package/src/decorators/relations.d.ts +55 -0
- package/src/decorators/relations.js +84 -0
- package/src/decorators/timestamps.d.ts +54 -0
- package/src/decorators/timestamps.js +67 -0
- package/src/decorators/transforms.d.ts +86 -0
- package/src/decorators/transforms.js +154 -0
- package/src/index.d.ts +9 -16
- package/src/index.js +35 -32
- package/src/index.test.d.ts +13 -0
- package/src/index.test.js +1992 -0
- package/src/utils/relations.d.ts +34 -12
- package/src/utils/relations.js +109 -133
- package/src/core/wrapper.d.ts +0 -17
- package/src/core/wrapper.js +0 -46
- package/src/decorators/belongs_to.d.ts +0 -1
- package/src/decorators/belongs_to.js +0 -24
- package/src/decorators/created_at.d.ts +0 -1
- package/src/decorators/created_at.js +0 -11
- package/src/decorators/default.d.ts +0 -1
- package/src/decorators/default.js +0 -47
- package/src/decorators/has_many.d.ts +0 -1
- package/src/decorators/has_many.js +0 -24
- package/src/decorators/index.d.ts +0 -11
- package/src/decorators/index.js +0 -36
- package/src/decorators/index_sort.d.ts +0 -12
- package/src/decorators/index_sort.js +0 -43
- package/src/decorators/mutate.d.ts +0 -2
- package/src/decorators/mutate.js +0 -51
- package/src/decorators/name.d.ts +0 -1
- package/src/decorators/name.js +0 -28
- package/src/decorators/not_null.d.ts +0 -1
- package/src/decorators/not_null.js +0 -13
- package/src/decorators/primary_key.d.ts +0 -6
- package/src/decorators/primary_key.js +0 -30
- package/src/decorators/updated_at.d.ts +0 -12
- package/src/decorators/updated_at.js +0 -26
- package/src/decorators/validate.d.ts +0 -1
- package/src/decorators/validate.js +0 -53
- package/src/utils/batch-relations.d.ts +0 -14
- package/src/utils/batch-relations.js +0 -131
- package/src/utils/circular-detector.d.ts +0 -82
- package/src/utils/circular-detector.js +0 -212
- package/src/utils/memory-manager.d.ts +0 -42
- package/src/utils/memory-manager.js +0 -107
- package/src/utils/naming.d.ts +0 -8
- package/src/utils/naming.js +0 -18
- package/src/utils/projection.d.ts +0 -12
- package/src/utils/projection.js +0 -51
- package/src/utils/security-validator.d.ts +0 -49
- package/src/utils/security-validator.js +0 -163
- package/src/utils/throttle-manager.d.ts +0 -78
- package/src/utils/throttle-manager.js +0 -201
- package/src/utils/transaction-manager.d.ts +0 -88
- package/src/utils/transaction-manager.js +0 -300
package/src/core/table.js
CHANGED
|
@@ -1,82 +1,144 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* @file table.ts
|
|
4
|
-
* @
|
|
4
|
+
* @description Tabla autocontenida con arquitectura minimalista y Symbol storage
|
|
5
5
|
* @autor Miguel Alejandro
|
|
6
|
-
* @fecha 2025-
|
|
6
|
+
* @fecha 2025-01-28
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.STORE = void 0;
|
|
10
9
|
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
11
10
|
const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
|
|
12
|
-
const relations_1 = require("../utils/relations");
|
|
13
11
|
const client_1 = require("./client");
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
const decorator_1 = require("./decorator");
|
|
13
|
+
const relations_1 = require("../utils/relations");
|
|
14
|
+
// Alias de operadores para sintaxis objeto
|
|
15
|
+
const OPERATOR_ALIASES = {
|
|
16
|
+
'$eq': '=',
|
|
17
|
+
'$ne': '!=',
|
|
18
|
+
'$lt': '<',
|
|
19
|
+
'$lte': '<=',
|
|
20
|
+
'$gt': '>',
|
|
21
|
+
'$gte': '>=',
|
|
22
|
+
'$in': 'in',
|
|
23
|
+
'$nin': 'not-in',
|
|
24
|
+
'contains': 'contains',
|
|
25
|
+
'begins-with': 'begins-with',
|
|
26
|
+
'beginsWith': 'begins-with',
|
|
27
|
+
// Operadores directos también soportados
|
|
28
|
+
'=': '=',
|
|
29
|
+
'!=': '!=',
|
|
30
|
+
'<': '<',
|
|
31
|
+
'<=': '<=',
|
|
32
|
+
'>': '>',
|
|
33
|
+
'>=': '>=',
|
|
34
|
+
'in': 'in',
|
|
35
|
+
'not-in': 'not-in',
|
|
36
|
+
};
|
|
17
37
|
// =============================================================================
|
|
18
|
-
// FUNCIONES UTILITARIAS
|
|
38
|
+
// FUNCIONES UTILITARIAS SIMPLIFICADAS
|
|
19
39
|
// =============================================================================
|
|
20
40
|
function validateOperator(operator) {
|
|
21
|
-
const validOps = [
|
|
22
|
-
"=",
|
|
23
|
-
"!=",
|
|
24
|
-
"<",
|
|
25
|
-
"<=",
|
|
26
|
-
">",
|
|
27
|
-
">=",
|
|
28
|
-
"in",
|
|
29
|
-
"not-in",
|
|
30
|
-
"contains",
|
|
31
|
-
"begins-with",
|
|
32
|
-
];
|
|
41
|
+
const validOps = ["=", "!=", "<", "<=", ">", ">=", "in", "not-in", "contains", "begins-with"];
|
|
33
42
|
if (!validOps.includes(operator)) {
|
|
34
43
|
throw new Error(`Operador inválido: ${operator}. Válidos: ${validOps.join(", ")}`);
|
|
35
44
|
}
|
|
36
45
|
return operator;
|
|
37
46
|
}
|
|
38
|
-
|
|
47
|
+
/**
|
|
48
|
+
* @description Detecta si un valor es un objeto con operadores
|
|
49
|
+
*/
|
|
50
|
+
function isOperatorObject(value) {
|
|
51
|
+
if (value === null || value === undefined || typeof value !== 'object' || Array.isArray(value)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
// Es objeto con operadores si alguna key es un operador conocido
|
|
55
|
+
return Object.keys(value).some(key => key in OPERATOR_ALIASES);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* @description Genera expresión para un operador y valor específico
|
|
59
|
+
*/
|
|
60
|
+
function buildSingleExpression(nameKey, valueKeyPrefix, op, val, names, values, fieldName) {
|
|
61
|
+
// Manejar arrays vacíos
|
|
62
|
+
if (op === "in" && Array.isArray(val) && val.length === 0) {
|
|
63
|
+
return "1 = 0"; // Ningún resultado coincide
|
|
64
|
+
}
|
|
65
|
+
if (op === "not-in" && Array.isArray(val) && val.length === 0) {
|
|
66
|
+
return null; // Excluir nada = todo pasa
|
|
67
|
+
}
|
|
68
|
+
names[nameKey] = fieldName;
|
|
69
|
+
if (op === "in" && Array.isArray(val)) {
|
|
70
|
+
const inValues = val.map((v, i) => {
|
|
71
|
+
const inValueKey = `${valueKeyPrefix}_${i}`;
|
|
72
|
+
values[inValueKey] = v;
|
|
73
|
+
return inValueKey;
|
|
74
|
+
});
|
|
75
|
+
return `${nameKey} IN (${inValues.join(", ")})`;
|
|
76
|
+
}
|
|
77
|
+
else if (op === "not-in" && Array.isArray(val)) {
|
|
78
|
+
const notInValues = val.map((v, i) => {
|
|
79
|
+
const notInValueKey = `${valueKeyPrefix}_${i}`;
|
|
80
|
+
values[notInValueKey] = v;
|
|
81
|
+
return notInValueKey;
|
|
82
|
+
});
|
|
83
|
+
return `NOT ${nameKey} IN (${notInValues.join(", ")})`;
|
|
84
|
+
}
|
|
85
|
+
else if (op === "contains") {
|
|
86
|
+
values[valueKeyPrefix] = val;
|
|
87
|
+
return `contains(${nameKey}, ${valueKeyPrefix})`;
|
|
88
|
+
}
|
|
89
|
+
else if (op === "begins-with") {
|
|
90
|
+
values[valueKeyPrefix] = val;
|
|
91
|
+
return `begins_with(${nameKey}, ${valueKeyPrefix})`;
|
|
92
|
+
}
|
|
93
|
+
else if (op === "!=") {
|
|
94
|
+
values[valueKeyPrefix] = val;
|
|
95
|
+
return `${nameKey} <> ${valueKeyPrefix}`;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
values[valueKeyPrefix] = val;
|
|
99
|
+
return `${nameKey} ${op} ${valueKeyPrefix}`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function buildConditionExpression(filters, defaultOperator = "=") {
|
|
39
103
|
const expressions = [];
|
|
40
104
|
const names = {};
|
|
41
105
|
const values = {};
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
expressions.push(`contains(${nameKey}, ${valueKey})`);
|
|
65
|
-
}
|
|
66
|
-
else if (operator === "begins-with") {
|
|
67
|
-
values[valueKey] = value;
|
|
68
|
-
expressions.push(`begins_with(${nameKey}, ${valueKey})`);
|
|
69
|
-
}
|
|
70
|
-
else if (operator === "!=") {
|
|
71
|
-
// DynamoDB uses <> for not equal
|
|
72
|
-
values[valueKey] = value;
|
|
73
|
-
expressions.push(`${nameKey} <> ${valueKey}`);
|
|
106
|
+
let index = 0;
|
|
107
|
+
for (const [fieldName, filterValue] of Object.entries(filters)) {
|
|
108
|
+
// Ignorar valores undefined
|
|
109
|
+
if (filterValue === undefined)
|
|
110
|
+
continue;
|
|
111
|
+
// Detectar sintaxis objeto con operadores: { campo: { operador: valor } }
|
|
112
|
+
if (isOperatorObject(filterValue)) {
|
|
113
|
+
// Procesar cada operador en el objeto
|
|
114
|
+
for (const [opKey, opValue] of Object.entries(filterValue)) {
|
|
115
|
+
// Ignorar valores undefined dentro del objeto
|
|
116
|
+
if (opValue === undefined)
|
|
117
|
+
continue;
|
|
118
|
+
const resolvedOp = OPERATOR_ALIASES[opKey];
|
|
119
|
+
if (!resolvedOp)
|
|
120
|
+
continue; // Ignorar keys desconocidas
|
|
121
|
+
const nameKey = `#attr${index}`;
|
|
122
|
+
const valueKey = `:val${index}`;
|
|
123
|
+
const expr = buildSingleExpression(nameKey, valueKey, resolvedOp, opValue, names, values, fieldName);
|
|
124
|
+
if (expr)
|
|
125
|
+
expressions.push(expr);
|
|
126
|
+
index++;
|
|
127
|
+
}
|
|
74
128
|
}
|
|
75
129
|
else {
|
|
76
|
-
|
|
77
|
-
|
|
130
|
+
// Valor simple: usar operador default
|
|
131
|
+
// Ignorar null solo si no hay operador explícito
|
|
132
|
+
if (filterValue === null)
|
|
133
|
+
continue;
|
|
134
|
+
const nameKey = `#attr${index}`;
|
|
135
|
+
const valueKey = `:val${index}`;
|
|
136
|
+
const expr = buildSingleExpression(nameKey, valueKey, defaultOperator, filterValue, names, values, fieldName);
|
|
137
|
+
if (expr)
|
|
138
|
+
expressions.push(expr);
|
|
139
|
+
index++;
|
|
78
140
|
}
|
|
79
|
-
}
|
|
141
|
+
}
|
|
80
142
|
return {
|
|
81
143
|
expression: expressions.join(" AND "),
|
|
82
144
|
names,
|
|
@@ -84,297 +146,398 @@ function buildConditionExpression(filters, operator = "=") {
|
|
|
84
146
|
};
|
|
85
147
|
}
|
|
86
148
|
// =============================================================================
|
|
87
|
-
// CLASE TABLE
|
|
149
|
+
// CLASE TABLE AUTOCONTENIDA
|
|
88
150
|
// =============================================================================
|
|
89
151
|
class Table {
|
|
90
|
-
constructor(
|
|
152
|
+
constructor(props = {}) {
|
|
91
153
|
(0, client_1.requireClient)();
|
|
92
|
-
const
|
|
93
|
-
// Inicializar
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
154
|
+
const schema = this.constructor[decorator_1.SCHEMA];
|
|
155
|
+
// Inicializar VALUES
|
|
156
|
+
this[decorator_1.VALUES] = {};
|
|
157
|
+
// Procesar cada columna del schema
|
|
158
|
+
for (const column_name in schema.columns) {
|
|
159
|
+
const column = schema.columns[column_name];
|
|
160
|
+
if (column.store?.relation) {
|
|
161
|
+
// ═══════════════════════════════════════════════════════════════
|
|
162
|
+
// RELACIONES: getter lazy que convierte a instancias, setter bloqueado
|
|
163
|
+
// ═══════════════════════════════════════════════════════════════
|
|
164
|
+
let relation_data = props[column_name] ?? undefined;
|
|
165
|
+
Object.defineProperty(this, column_name, {
|
|
166
|
+
enumerable: true,
|
|
167
|
+
configurable: true, // Permite reconfigurar cuando processIncludes carga datos
|
|
168
|
+
set: () => undefined, // Relaciones son read-only
|
|
169
|
+
get: () => {
|
|
170
|
+
if (relation_data === undefined)
|
|
171
|
+
return undefined;
|
|
172
|
+
const RelatedModel = column.store.relation.model();
|
|
173
|
+
const type = column.store.relation.type;
|
|
174
|
+
if (type === 'HasMany') {
|
|
175
|
+
// HasMany: Array de instancias
|
|
176
|
+
return [].concat(relation_data ?? [])
|
|
177
|
+
.filter(Boolean)
|
|
178
|
+
.map((item) => item instanceof RelatedModel ? item : new RelatedModel(item));
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// HasOne / BelongsTo: Instancia única o null
|
|
182
|
+
if (relation_data === null)
|
|
183
|
+
return null;
|
|
184
|
+
return relation_data instanceof RelatedModel
|
|
185
|
+
? relation_data
|
|
186
|
+
: new RelatedModel(relation_data);
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
// ═══════════════════════════════════════════════════════════════
|
|
193
|
+
// COLUMNAS REGULARES: getter/setter con pipelines via reduce
|
|
194
|
+
// ═══════════════════════════════════════════════════════════════
|
|
195
|
+
const has_initial_value = column_name in props;
|
|
196
|
+
let value = has_initial_value ? props[column_name] : null;
|
|
197
|
+
// Aplicar set pipeline en inicialización (solo si usuario proporciona valor)
|
|
198
|
+
if (has_initial_value) {
|
|
199
|
+
value = column.set.reduce((v, fn) => fn(v), value);
|
|
103
200
|
}
|
|
201
|
+
// Almacenar en VALUES para compatibilidad con métodos existentes
|
|
202
|
+
this[decorator_1.VALUES][column_name] = value;
|
|
203
|
+
Object.defineProperty(this, column_name, {
|
|
204
|
+
enumerable: true,
|
|
205
|
+
configurable: true,
|
|
206
|
+
get: () => {
|
|
207
|
+
// Aplicar get pipeline
|
|
208
|
+
let v = column.get.reduce((val, fn) => fn(val), value);
|
|
209
|
+
// Almacenar si se generó default (para consistencia)
|
|
210
|
+
if (value === null && v !== null) {
|
|
211
|
+
value = v;
|
|
212
|
+
this[decorator_1.VALUES][column_name] = v;
|
|
213
|
+
}
|
|
214
|
+
return v;
|
|
215
|
+
},
|
|
216
|
+
set: (new_value) => {
|
|
217
|
+
// Aplicar set pipeline
|
|
218
|
+
value = column.set.reduce((v, fn) => fn(v), new_value ?? null);
|
|
219
|
+
this[decorator_1.VALUES][column_name] = value;
|
|
220
|
+
},
|
|
221
|
+
});
|
|
104
222
|
}
|
|
105
|
-
}
|
|
106
|
-
Object.assign(this, data);
|
|
223
|
+
}
|
|
107
224
|
}
|
|
108
|
-
|
|
225
|
+
// **Métodos de instancia con API preservada**
|
|
226
|
+
/**
|
|
227
|
+
* @description Serializa la instancia a JSON incluyendo relaciones recursivamente
|
|
228
|
+
* @returns Objeto JSON con todas las propiedades y relaciones
|
|
229
|
+
*/
|
|
109
230
|
toJSON() {
|
|
110
|
-
const
|
|
231
|
+
const schema = this.constructor[decorator_1.SCHEMA];
|
|
111
232
|
const result = {};
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
233
|
+
for (const column_name in schema.columns) {
|
|
234
|
+
const column = schema.columns[column_name];
|
|
235
|
+
const value = this[column_name];
|
|
236
|
+
if (value === null || value === undefined)
|
|
237
|
+
continue;
|
|
238
|
+
if (column.store?.relation) {
|
|
239
|
+
// Serializar relaciones recursivamente
|
|
240
|
+
if (Array.isArray(value)) {
|
|
241
|
+
result[column_name] = value.map(item => item?.toJSON ? item.toJSON() : item);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
result[column_name] = value?.toJSON ? value.toJSON() : value;
|
|
118
245
|
}
|
|
119
246
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
247
|
+
else {
|
|
248
|
+
result[column_name] = value;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* @description Serializa la instancia a string JSON
|
|
255
|
+
* @returns String JSON con todas las propiedades y relaciones
|
|
256
|
+
*/
|
|
257
|
+
toString() {
|
|
258
|
+
return JSON.stringify(this);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* @description Genera payload para operaciones de DB (excluye relaciones)
|
|
262
|
+
*/
|
|
263
|
+
_toDBPayload() {
|
|
264
|
+
const schema = this.constructor[decorator_1.SCHEMA];
|
|
265
|
+
const result = {};
|
|
266
|
+
for (const column_name in schema.columns) {
|
|
267
|
+
const column = schema.columns[column_name];
|
|
268
|
+
if (column.store?.relation)
|
|
132
269
|
continue;
|
|
133
|
-
|
|
270
|
+
const value = this[column_name];
|
|
271
|
+
if (value !== null && value !== undefined) {
|
|
272
|
+
result[column_name] = value;
|
|
273
|
+
}
|
|
134
274
|
}
|
|
135
275
|
return result;
|
|
136
276
|
}
|
|
137
|
-
/** Guardar instancia (crear o actualizar) */
|
|
138
277
|
async save() {
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
278
|
+
const schema = this.constructor[decorator_1.SCHEMA];
|
|
279
|
+
const id = this[decorator_1.VALUES][schema.primary_key];
|
|
280
|
+
const is_new = id === undefined || id === null;
|
|
281
|
+
// Validación lazy
|
|
282
|
+
for (const column_name in schema.columns) {
|
|
283
|
+
const column = schema.columns[column_name];
|
|
284
|
+
if (column.store?.lazy_validators && !column.store?.relation) {
|
|
285
|
+
const value = this[decorator_1.VALUES][column_name];
|
|
286
|
+
for (const validator of column.store.lazy_validators) {
|
|
287
|
+
const result = validator(value);
|
|
288
|
+
if (result !== true) {
|
|
289
|
+
throw new Error(typeof result === "string" ? result : "Validación fallida");
|
|
290
|
+
}
|
|
291
|
+
}
|
|
149
292
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
293
|
+
}
|
|
294
|
+
// Timestamps automáticos (updatedAt solamente, createdAt se genera via pipeline)
|
|
295
|
+
const now = new Date().toISOString();
|
|
296
|
+
for (const column_name in schema.columns) {
|
|
297
|
+
const column = schema.columns[column_name];
|
|
298
|
+
if (column.store?.updatedAt) {
|
|
299
|
+
// Usar setter para sincronizar closure y VALUES
|
|
300
|
+
this[column_name] = now;
|
|
153
301
|
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const
|
|
157
|
-
if (
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
Object.assign(this, created);
|
|
302
|
+
}
|
|
303
|
+
// Preparar datos para DB (omitir relaciones)
|
|
304
|
+
const db_data = this._toDBPayload();
|
|
305
|
+
if (is_new) {
|
|
306
|
+
// Insertar nuevo registro
|
|
307
|
+
await this.insertIntoDynamoDB(db_data);
|
|
161
308
|
}
|
|
162
309
|
else {
|
|
163
310
|
// Actualizar registro existente
|
|
164
|
-
await
|
|
165
|
-
TableName: meta.name,
|
|
166
|
-
Item: (0, util_dynamodb_1.marshall)(payload, { removeUndefinedValues: true }),
|
|
167
|
-
}));
|
|
311
|
+
await this.updateInDynamoDB(db_data);
|
|
168
312
|
}
|
|
169
313
|
return this;
|
|
170
314
|
}
|
|
171
|
-
/** Actualizar instancia */
|
|
172
|
-
async update(patch) {
|
|
173
|
-
Object.assign(this, patch);
|
|
174
|
-
return await this.save();
|
|
175
|
-
}
|
|
176
|
-
/** Eliminar instancia */
|
|
177
315
|
async destroy() {
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
316
|
+
const schema = this.constructor[decorator_1.SCHEMA];
|
|
317
|
+
const id = this[decorator_1.VALUES][schema.primary_key];
|
|
318
|
+
if (!id) {
|
|
319
|
+
throw new Error('Cannot destroy record without ID');
|
|
181
320
|
}
|
|
182
|
-
|
|
183
|
-
|
|
321
|
+
// Buscar si hay soft delete configurado
|
|
322
|
+
let softDeleteColumn = null;
|
|
323
|
+
for (const column_name in schema.columns) {
|
|
324
|
+
const column = schema.columns[column_name];
|
|
325
|
+
if (column.store?.softDelete) {
|
|
326
|
+
softDeleteColumn = column_name;
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (softDeleteColumn) {
|
|
331
|
+
// Soft delete
|
|
332
|
+
this[decorator_1.VALUES][softDeleteColumn] = new Date().toISOString();
|
|
333
|
+
await this.save();
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
// Hard delete
|
|
337
|
+
await this.deleteFromDynamoDB(id);
|
|
338
|
+
}
|
|
339
|
+
return null;
|
|
184
340
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
341
|
+
async forceDestroy() {
|
|
342
|
+
const schema = this.constructor[decorator_1.SCHEMA];
|
|
343
|
+
const id = this[decorator_1.VALUES][schema.primary_key];
|
|
344
|
+
if (!id) {
|
|
345
|
+
throw new Error('Cannot destroy record without ID');
|
|
346
|
+
}
|
|
347
|
+
await this.deleteFromDynamoDB(id);
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
// **Métodos estáticos con API preservada**
|
|
351
|
+
static async create(data, tx) {
|
|
194
352
|
const instance = new this(data);
|
|
195
353
|
// Establecer timestamps si corresponde
|
|
354
|
+
const schema = this[decorator_1.SCHEMA];
|
|
196
355
|
const now = new Date().toISOString();
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
instance[
|
|
356
|
+
// Solo updatedAt, createdAt se genera via pipeline
|
|
357
|
+
for (const column_name in schema.columns) {
|
|
358
|
+
const column = schema.columns[column_name];
|
|
359
|
+
if (column.store?.updatedAt) {
|
|
360
|
+
// Usar setter para sincronizar closure y VALUES
|
|
361
|
+
instance[column_name] = now;
|
|
203
362
|
}
|
|
204
|
-
}
|
|
205
|
-
const payload = instance.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
363
|
+
}
|
|
364
|
+
const payload = instance._toDBPayload();
|
|
365
|
+
if (tx) {
|
|
366
|
+
tx.addPut(schema.name, payload);
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
const client = (0, client_1.requireClient)();
|
|
370
|
+
await client.send(new client_dynamodb_1.PutItemCommand({
|
|
371
|
+
TableName: schema.name,
|
|
372
|
+
Item: (0, util_dynamodb_1.marshall)(payload, { removeUndefinedValues: true }),
|
|
373
|
+
}));
|
|
374
|
+
}
|
|
210
375
|
return instance;
|
|
211
376
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
* @param filters - Filtros para seleccionar los registros a actualizar
|
|
216
|
-
* @returns Número de registros actualizados
|
|
217
|
-
*/
|
|
218
|
-
static async update(updates, filters) {
|
|
219
|
-
// Obtener metadatos del modelo para manejar timestamps
|
|
220
|
-
const meta = (0, wrapper_1.mustMeta)(this);
|
|
221
|
-
const client = (0, client_1.requireClient)();
|
|
222
|
-
// Buscar registros que coincidan con los filtros
|
|
223
|
-
const recordsToUpdate = await this.where(filters);
|
|
224
|
-
if (recordsToUpdate.length === 0) {
|
|
377
|
+
static async update(updates, filters, tx) {
|
|
378
|
+
const records = await this.where(filters);
|
|
379
|
+
if (records.length === 0) {
|
|
225
380
|
return 0;
|
|
226
381
|
}
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const updatePromises = recordsToUpdate.map(async (record) => {
|
|
235
|
-
// Obtener datos actuales
|
|
236
|
-
const currentData = record.toJSON();
|
|
237
|
-
// Aplicar actualizaciones, preservando valores existentes
|
|
238
|
-
const updatedData = { ...currentData };
|
|
239
|
-
// Aplicar solo los campos que no son undefined
|
|
240
|
-
for (const [key, value] of Object.entries(cleanUpdates)) {
|
|
241
|
-
if (value !== undefined) {
|
|
242
|
-
updatedData[key] = value;
|
|
243
|
-
}
|
|
382
|
+
let updatedCount = 0;
|
|
383
|
+
const schema = this[decorator_1.SCHEMA];
|
|
384
|
+
const client = tx ? null : (0, client_1.requireClient)();
|
|
385
|
+
for (const record of records) {
|
|
386
|
+
// Aplicar actualizaciones
|
|
387
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
388
|
+
record[key] = value;
|
|
244
389
|
}
|
|
245
|
-
// Actualizar timestamp
|
|
246
|
-
|
|
247
|
-
for (const
|
|
248
|
-
|
|
249
|
-
|
|
390
|
+
// Actualizar timestamp
|
|
391
|
+
const now = new Date().toISOString();
|
|
392
|
+
for (const column_name in schema.columns) {
|
|
393
|
+
const column = schema.columns[column_name];
|
|
394
|
+
if (column.store?.updatedAt) {
|
|
395
|
+
// Usar setter para sincronizar closure y VALUES
|
|
396
|
+
record[column_name] = now;
|
|
250
397
|
break;
|
|
251
398
|
}
|
|
252
399
|
}
|
|
253
|
-
|
|
254
|
-
|
|
400
|
+
const updated_data = record._toDBPayload();
|
|
401
|
+
if (tx) {
|
|
402
|
+
tx.addPut(schema.name, updated_data);
|
|
255
403
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
404
|
+
else {
|
|
405
|
+
await client.send(new client_dynamodb_1.PutItemCommand({
|
|
406
|
+
TableName: schema.name,
|
|
407
|
+
Item: (0, util_dynamodb_1.marshall)(updated_data, { removeUndefinedValues: true }),
|
|
408
|
+
}));
|
|
409
|
+
}
|
|
410
|
+
updatedCount++;
|
|
411
|
+
}
|
|
412
|
+
return updatedCount;
|
|
264
413
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
static async delete(filters) {
|
|
269
|
-
const recordsToDelete = await this.where(filters);
|
|
270
|
-
if (recordsToDelete.length === 0) {
|
|
414
|
+
static async delete(filters, tx) {
|
|
415
|
+
const records = await this.where(filters);
|
|
416
|
+
if (records.length === 0) {
|
|
271
417
|
return 0;
|
|
272
418
|
}
|
|
273
|
-
const
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
419
|
+
const schema = this[decorator_1.SCHEMA];
|
|
420
|
+
const client = tx ? null : (0, client_1.requireClient)();
|
|
421
|
+
let deletedCount = 0;
|
|
422
|
+
for (const record of records) {
|
|
423
|
+
const id = record[decorator_1.VALUES][schema.primary_key];
|
|
277
424
|
if (id) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
425
|
+
if (tx) {
|
|
426
|
+
tx.addDelete(schema.name, { [schema.primary_key]: id });
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
await client.send(new client_dynamodb_1.DeleteItemCommand({
|
|
430
|
+
TableName: schema.name,
|
|
431
|
+
Key: (0, util_dynamodb_1.marshall)({ [schema.primary_key]: id }),
|
|
432
|
+
}));
|
|
433
|
+
}
|
|
434
|
+
deletedCount++;
|
|
282
435
|
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return recordsToDelete.length;
|
|
436
|
+
}
|
|
437
|
+
return deletedCount;
|
|
286
438
|
}
|
|
287
|
-
|
|
288
|
-
static async where(...args) {
|
|
439
|
+
static async where(field_or_filters, operator_or_value, value, options) {
|
|
289
440
|
const client = (0, client_1.requireClient)();
|
|
290
|
-
const
|
|
441
|
+
const schema = this[decorator_1.SCHEMA];
|
|
442
|
+
// Parsear argumentos dinámicamente (como el original)
|
|
291
443
|
let filters;
|
|
292
|
-
let
|
|
444
|
+
let queryOptions;
|
|
293
445
|
let operator = "=";
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (Array.isArray(value)) {
|
|
300
|
-
operator = "in";
|
|
301
|
-
}
|
|
302
|
-
filters = { [args[0]]: value };
|
|
303
|
-
}
|
|
304
|
-
else {
|
|
305
|
-
// where(filters, options)
|
|
306
|
-
filters = args[0];
|
|
307
|
-
options = args[1] || {};
|
|
308
|
-
}
|
|
446
|
+
if (typeof operator_or_value === 'string') {
|
|
447
|
+
// where(field, operator, value, options?)
|
|
448
|
+
operator = validateOperator(operator_or_value);
|
|
449
|
+
filters = { [field_or_filters]: value };
|
|
450
|
+
queryOptions = options || {};
|
|
309
451
|
}
|
|
310
|
-
else if (
|
|
311
|
-
// where(field,
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
filters = { [field]: value };
|
|
452
|
+
else if (value !== undefined) {
|
|
453
|
+
// where(field, value, options?)
|
|
454
|
+
filters = { [field_or_filters]: Array.isArray(operator_or_value) ? { in: operator_or_value } : operator_or_value };
|
|
455
|
+
queryOptions = value || {};
|
|
315
456
|
}
|
|
316
|
-
else if (
|
|
317
|
-
// where(filters)
|
|
318
|
-
filters =
|
|
457
|
+
else if (operator_or_value !== undefined && typeof operator_or_value === 'object') {
|
|
458
|
+
// where(filters, options?)
|
|
459
|
+
filters = field_or_filters;
|
|
460
|
+
queryOptions = operator_or_value;
|
|
319
461
|
}
|
|
320
462
|
else {
|
|
321
|
-
|
|
463
|
+
// where(filters?)
|
|
464
|
+
filters = field_or_filters;
|
|
465
|
+
queryOptions = {};
|
|
322
466
|
}
|
|
323
|
-
// Validar
|
|
324
|
-
if (
|
|
467
|
+
// Validar opciones
|
|
468
|
+
if (queryOptions.limit === 0)
|
|
469
|
+
return [];
|
|
470
|
+
if (queryOptions.limit && queryOptions.limit < 0) {
|
|
325
471
|
throw new Error("limit debe ser mayor o igual a 0");
|
|
326
472
|
}
|
|
327
|
-
if (
|
|
473
|
+
if (queryOptions.skip && queryOptions.skip < 0) {
|
|
328
474
|
throw new Error("skip debe ser mayor o igual a 0");
|
|
329
475
|
}
|
|
330
|
-
if (
|
|
476
|
+
if (queryOptions.order && !["ASC", "DESC"].includes(queryOptions.order)) {
|
|
331
477
|
throw new Error('order debe ser "ASC" o "DESC"');
|
|
332
478
|
}
|
|
333
|
-
//
|
|
479
|
+
// Auto-excluir soft deleted
|
|
480
|
+
if (!queryOptions._includeTrashed) {
|
|
481
|
+
for (const column_name in schema.columns) {
|
|
482
|
+
const column = schema.columns[column_name];
|
|
483
|
+
if (column.store?.softDelete && !(column_name in filters)) {
|
|
484
|
+
filters[column_name] = null;
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// Construir consulta DynamoDB
|
|
334
490
|
const { expression, names, values } = buildConditionExpression(filters, operator);
|
|
335
491
|
const scanParams = {
|
|
336
|
-
TableName:
|
|
492
|
+
TableName: schema.name,
|
|
493
|
+
ExpressionAttributeNames: names || {},
|
|
337
494
|
};
|
|
338
|
-
// Initialize ExpressionAttributeNames to avoid conflicts
|
|
339
|
-
if (!scanParams.ExpressionAttributeNames) {
|
|
340
|
-
scanParams.ExpressionAttributeNames = {};
|
|
341
|
-
}
|
|
342
495
|
if (expression) {
|
|
343
496
|
scanParams.FilterExpression = expression;
|
|
344
|
-
|
|
345
|
-
Object.assign(scanParams.ExpressionAttributeNames, names);
|
|
346
|
-
scanParams.ExpressionAttributeValues = (0, util_dynamodb_1.marshall)(values);
|
|
497
|
+
scanParams.ExpressionAttributeValues = (0, util_dynamodb_1.marshall)(values || {}, { removeUndefinedValues: true });
|
|
347
498
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const projectionExpressions =
|
|
499
|
+
// Proyección solo si hay atributos específicos (ignorar array vacío)
|
|
500
|
+
if (queryOptions.attributes && queryOptions.attributes.length > 0) {
|
|
501
|
+
const projectionExpressions = queryOptions.attributes.map((attr, index) => {
|
|
351
502
|
const aliasKey = `#proj${index}`;
|
|
352
|
-
scanParams.ExpressionAttributeNames[aliasKey] = attr;
|
|
503
|
+
scanParams.ExpressionAttributeNames[aliasKey] = String(attr);
|
|
353
504
|
return aliasKey;
|
|
354
505
|
});
|
|
355
506
|
scanParams.ProjectionExpression = projectionExpressions.join(", ");
|
|
356
507
|
}
|
|
508
|
+
// Limpiar ExpressionAttributeNames si está vacío (DynamoDB lo rechaza)
|
|
509
|
+
if (Object.keys(scanParams.ExpressionAttributeNames).length === 0) {
|
|
510
|
+
delete scanParams.ExpressionAttributeNames;
|
|
511
|
+
}
|
|
357
512
|
// Ejecutar consulta con paginación
|
|
358
513
|
let allItems = [];
|
|
359
514
|
let lastEvaluatedKey = undefined;
|
|
360
515
|
let scannedCount = 0;
|
|
361
|
-
const targetSkip =
|
|
362
|
-
const targetLimit =
|
|
516
|
+
const targetSkip = queryOptions.skip || 0;
|
|
517
|
+
const targetLimit = queryOptions.limit ?? 100;
|
|
363
518
|
do {
|
|
364
519
|
if (lastEvaluatedKey) {
|
|
365
520
|
scanParams.ExclusiveStartKey = lastEvaluatedKey;
|
|
366
521
|
}
|
|
367
522
|
const result = await client.send(new client_dynamodb_1.ScanCommand(scanParams));
|
|
368
523
|
if (result.Items) {
|
|
369
|
-
const items = result.Items.map((item) =>
|
|
524
|
+
const items = result.Items.map((item) => {
|
|
525
|
+
const raw = (0, util_dynamodb_1.unmarshall)(item);
|
|
526
|
+
// Limpiar null/undefined
|
|
527
|
+
for (const k of Object.keys(raw)) {
|
|
528
|
+
if (raw[k] === null || raw[k] === undefined) {
|
|
529
|
+
delete raw[k];
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
return raw;
|
|
533
|
+
});
|
|
370
534
|
for (const item of items) {
|
|
371
535
|
if (scannedCount < targetSkip) {
|
|
372
536
|
scannedCount++;
|
|
373
537
|
continue;
|
|
374
538
|
}
|
|
375
|
-
if (allItems.length >= targetLimit)
|
|
539
|
+
if (allItems.length >= targetLimit)
|
|
376
540
|
break;
|
|
377
|
-
}
|
|
378
541
|
allItems.push(item);
|
|
379
542
|
scannedCount++;
|
|
380
543
|
}
|
|
@@ -382,77 +545,114 @@ class Table {
|
|
|
382
545
|
lastEvaluatedKey = result.LastEvaluatedKey;
|
|
383
546
|
} while (lastEvaluatedKey && allItems.length < targetLimit);
|
|
384
547
|
// Aplicar ordenamiento
|
|
385
|
-
if (
|
|
548
|
+
if (queryOptions.order) {
|
|
386
549
|
allItems.sort((a, b) => {
|
|
387
|
-
const sortField = Object.keys(filters)[0] ||
|
|
550
|
+
const sortField = Object.keys(filters)[0] || schema.primary_key;
|
|
388
551
|
const aVal = a[sortField];
|
|
389
552
|
const bVal = b[sortField];
|
|
390
553
|
if (aVal < bVal)
|
|
391
|
-
return
|
|
554
|
+
return queryOptions.order === "ASC" ? -1 : 1;
|
|
392
555
|
if (aVal > bVal)
|
|
393
|
-
return
|
|
556
|
+
return queryOptions.order === "ASC" ? 1 : -1;
|
|
394
557
|
return 0;
|
|
395
558
|
});
|
|
396
559
|
}
|
|
397
|
-
// Convertir a instancias
|
|
560
|
+
// Convertir a instancias (schema ya definido arriba)
|
|
398
561
|
const instances = allItems.map((item) => {
|
|
399
|
-
if (
|
|
400
|
-
//
|
|
401
|
-
// only with the requested fields to avoid default value population
|
|
562
|
+
if (queryOptions.attributes) {
|
|
563
|
+
// Proyección parcial: crear instancia con solo los atributos solicitados
|
|
402
564
|
const instance = Object.create(this.prototype);
|
|
403
|
-
|
|
565
|
+
instance[decorator_1.VALUES] = item;
|
|
566
|
+
// Definir getters para los atributos proyectados
|
|
567
|
+
for (const attr of queryOptions.attributes) {
|
|
568
|
+
const column = schema.columns[attr];
|
|
569
|
+
if (column) {
|
|
570
|
+
Object.defineProperty(instance, attr, {
|
|
571
|
+
enumerable: true,
|
|
572
|
+
configurable: true,
|
|
573
|
+
get: () => {
|
|
574
|
+
let value = instance[decorator_1.VALUES][attr] ?? null;
|
|
575
|
+
// Aplicar get pipeline
|
|
576
|
+
for (const fn of column.get) {
|
|
577
|
+
value = fn(value);
|
|
578
|
+
}
|
|
579
|
+
return value;
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
}
|
|
404
584
|
return instance;
|
|
405
585
|
}
|
|
406
586
|
else {
|
|
407
|
-
// Normal instantiation with all defaults
|
|
408
587
|
return new this(item);
|
|
409
588
|
}
|
|
410
589
|
});
|
|
411
|
-
// Procesar includes
|
|
412
|
-
if (
|
|
413
|
-
|
|
590
|
+
// Procesar includes para cargar relaciones
|
|
591
|
+
if (queryOptions.include) {
|
|
592
|
+
await (0, relations_1.processIncludes)(instances, queryOptions.include, this);
|
|
414
593
|
}
|
|
415
594
|
return instances;
|
|
416
595
|
}
|
|
417
|
-
static async first(
|
|
418
|
-
const results = await this.where(
|
|
419
|
-
return results[0]
|
|
596
|
+
static async first(field_or_filters, operator_or_value, value_or_options) {
|
|
597
|
+
const results = await this.where(field_or_filters, operator_or_value, value_or_options, { limit: 1 });
|
|
598
|
+
return results[0];
|
|
420
599
|
}
|
|
421
|
-
static async last(
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
600
|
+
static async last(field_or_filters, operator_or_value) {
|
|
601
|
+
if (field_or_filters === undefined) {
|
|
602
|
+
const results = await this.where({}, { order: 'DESC', limit: 1 });
|
|
603
|
+
return results[0];
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
const results = await this.where(field_or_filters, operator_or_value, undefined, { order: 'DESC', limit: 1 });
|
|
607
|
+
return results[0];
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
static async withTrashed(filters, options) {
|
|
611
|
+
return await this.where(filters ?? {}, { ...options, _includeTrashed: true });
|
|
612
|
+
}
|
|
613
|
+
static async onlyTrashed(filters, options) {
|
|
614
|
+
const schema = this[decorator_1.SCHEMA];
|
|
615
|
+
let softDeleteColumn = null;
|
|
616
|
+
for (const column_name in schema.columns) {
|
|
617
|
+
const column = schema.columns[column_name];
|
|
618
|
+
if (column.store?.softDelete) {
|
|
619
|
+
softDeleteColumn = column_name;
|
|
620
|
+
break;
|
|
434
621
|
}
|
|
435
|
-
throw new Error("Se requiere un valor para el campo de filtro");
|
|
436
|
-
}
|
|
437
|
-
if (args.length === 2) {
|
|
438
|
-
// field, value
|
|
439
|
-
const results = await this.where(args[0], args[1]);
|
|
440
|
-
return results[results.length - 1];
|
|
441
|
-
}
|
|
442
|
-
if (args.length === 3) {
|
|
443
|
-
// field, operator, value
|
|
444
|
-
const results = await this.where(args[0], args[1], args[2]);
|
|
445
|
-
return results[results.length - 1];
|
|
446
|
-
}
|
|
447
|
-
if (args.length === 2 && typeof args[1] === "object") {
|
|
448
|
-
const results = await this.where(args[0], {
|
|
449
|
-
...(args[1] || {}),
|
|
450
|
-
order: "DESC",
|
|
451
|
-
limit: 1,
|
|
452
|
-
});
|
|
453
|
-
return results[0] || undefined;
|
|
454
622
|
}
|
|
455
|
-
|
|
623
|
+
if (!softDeleteColumn) {
|
|
624
|
+
throw new Error("onlyTrashed() requiere un campo @SoftDelete configurado");
|
|
625
|
+
}
|
|
626
|
+
const merged_filters = {
|
|
627
|
+
...filters,
|
|
628
|
+
[softDeleteColumn]: { "!=": null },
|
|
629
|
+
};
|
|
630
|
+
return await this.where(merged_filters, { ...options, _includeTrashed: true });
|
|
631
|
+
}
|
|
632
|
+
// **Métodos privados para DynamoDB**
|
|
633
|
+
async insertIntoDynamoDB(data) {
|
|
634
|
+
const client = (0, client_1.requireClient)();
|
|
635
|
+
const schema = this.constructor[decorator_1.SCHEMA];
|
|
636
|
+
await client.send(new client_dynamodb_1.PutItemCommand({
|
|
637
|
+
TableName: schema.name,
|
|
638
|
+
Item: (0, util_dynamodb_1.marshall)(data, { removeUndefinedValues: true }),
|
|
639
|
+
}));
|
|
640
|
+
}
|
|
641
|
+
async updateInDynamoDB(data) {
|
|
642
|
+
const client = (0, client_1.requireClient)();
|
|
643
|
+
const schema = this.constructor[decorator_1.SCHEMA];
|
|
644
|
+
await client.send(new client_dynamodb_1.PutItemCommand({
|
|
645
|
+
TableName: schema.name,
|
|
646
|
+
Item: (0, util_dynamodb_1.marshall)(data, { removeUndefinedValues: true }),
|
|
647
|
+
}));
|
|
648
|
+
}
|
|
649
|
+
async deleteFromDynamoDB(id) {
|
|
650
|
+
const client = (0, client_1.requireClient)();
|
|
651
|
+
const schema = this.constructor[decorator_1.SCHEMA];
|
|
652
|
+
await client.send(new client_dynamodb_1.DeleteItemCommand({
|
|
653
|
+
TableName: schema.name,
|
|
654
|
+
Key: (0, util_dynamodb_1.marshall)({ [schema.primary_key]: id }),
|
|
655
|
+
}));
|
|
456
656
|
}
|
|
457
657
|
}
|
|
458
658
|
exports.default = Table;
|