@arcaelas/dynamite 1.0.9 → 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 (88) 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/src/core/client.d.ts +69 -0
  7. package/src/core/client.js +164 -0
  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}/decorators/created_at.js +0 -11
  16. package/src/decorators/default.d.ts +1 -0
  17. package/{build → src}/decorators/default.js +7 -12
  18. package/src/decorators/has_many.d.ts +1 -0
  19. package/src/decorators/has_many.js +24 -0
  20. package/src/decorators/index.d.ts +11 -0
  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/{build → src}/decorators/mutate.js +12 -14
  25. package/{build → src}/decorators/name.js +1 -14
  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/{build → src}/decorators/validate.js +12 -13
  33. package/src/index.d.ts +22 -0
  34. package/{build → src}/index.js +22 -6
  35. package/src/utils/batch-relations.d.ts +14 -0
  36. package/src/utils/batch-relations.js +131 -0
  37. package/src/utils/circular-detector.d.ts +82 -0
  38. package/src/utils/circular-detector.js +209 -0
  39. package/src/utils/memory-manager.d.ts +42 -0
  40. package/src/utils/memory-manager.js +108 -0
  41. package/src/utils/naming.d.ts +8 -0
  42. package/src/utils/naming.js +18 -0
  43. package/src/utils/projection.d.ts +12 -0
  44. package/src/utils/projection.js +51 -0
  45. package/src/utils/relations.d.ts +17 -0
  46. package/src/utils/relations.js +166 -0
  47. package/src/utils/security-validator.d.ts +49 -0
  48. package/src/utils/security-validator.js +152 -0
  49. package/src/utils/throttle-manager.d.ts +78 -0
  50. package/src/utils/throttle-manager.js +196 -0
  51. package/src/utils/transaction-manager.d.ts +88 -0
  52. package/src/utils/transaction-manager.js +298 -0
  53. package/build/core/table.d.ts +0 -138
  54. package/build/core/table.js +0 -400
  55. package/build/core/table.js.map +0 -1
  56. package/build/core/wrapper.d.ts +0 -44
  57. package/build/core/wrapper.js +0 -26
  58. package/build/core/wrapper.js.map +0 -1
  59. package/build/decorators/created_at.d.ts +0 -4
  60. package/build/decorators/created_at.js.map +0 -1
  61. package/build/decorators/default.d.ts +0 -2
  62. package/build/decorators/default.js.map +0 -1
  63. package/build/decorators/index.d.ts +0 -1
  64. package/build/decorators/index.js +0 -24
  65. package/build/decorators/index.js.map +0 -1
  66. package/build/decorators/index_sort.d.ts +0 -1
  67. package/build/decorators/index_sort.js +0 -27
  68. package/build/decorators/index_sort.js.map +0 -1
  69. package/build/decorators/mutate.js.map +0 -1
  70. package/build/decorators/name.js.map +0 -1
  71. package/build/decorators/not_null.d.ts +0 -4
  72. package/build/decorators/not_null.js +0 -28
  73. package/build/decorators/not_null.js.map +0 -1
  74. package/build/decorators/primary_key.d.ts +0 -7
  75. package/build/decorators/primary_key.js +0 -30
  76. package/build/decorators/primary_key.js.map +0 -1
  77. package/build/decorators/updated_at.d.ts +0 -4
  78. package/build/decorators/updated_at.js +0 -23
  79. package/build/decorators/updated_at.js.map +0 -1
  80. package/build/decorators/validate.js.map +0 -1
  81. package/build/index.d.ts +0 -11
  82. package/build/index.js.map +0 -1
  83. package/build/utils/naming.d.ts +0 -1
  84. package/build/utils/naming.js +0 -18
  85. package/build/utils/naming.js.map +0 -1
  86. /package/{build → src}/decorators/mutate.d.ts +0 -0
  87. /package/{build → src}/decorators/name.d.ts +0 -0
  88. /package/{build → src}/decorators/validate.d.ts +0 -0
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @file table.ts
3
+ * @descripcion Clase Table rediseñada con API completa y tipado estricto
4
+ * @autor Miguel Alejandro
5
+ * @fecha 2025-07-30
6
+ */
7
+ import type { InferAttributes, QueryOperator, WrapperEntry, WhereQueryOptions } from "../@types/index";
8
+ import { STORE } from "./wrapper";
9
+ export default class Table<T = any> {
10
+ protected [STORE]: {
11
+ [K in keyof T]?: T[K];
12
+ };
13
+ constructor(data: InferAttributes<T>);
14
+ /** Serializar instancia a JSON plano */
15
+ toJSON(): Record<string, any>;
16
+ /** Guardar instancia (crear o actualizar) */
17
+ save(): Promise<this>;
18
+ /** Actualizar instancia */
19
+ update(patch: Partial<InferAttributes<T>>): Promise<this>;
20
+ /** Eliminar instancia */
21
+ destroy(): Promise<null>;
22
+ /**
23
+ * Crear un nuevo registro en la base de datos
24
+ */
25
+ static create<M extends Table>(this: {
26
+ new (data: InferAttributes<M>): M;
27
+ prototype: M;
28
+ }, data: InferAttributes<M>): Promise<M>;
29
+ /**
30
+ * Actualizar registros en la base de datos
31
+ * @param updates - Campos a actualizar. Los campos con valor `undefined` se ignoran.
32
+ * @param filters - Filtros para seleccionar los registros a actualizar
33
+ * @returns Número de registros actualizados
34
+ */
35
+ static update<M extends Table>(this: {
36
+ new (data: InferAttributes<M>): M;
37
+ prototype: M;
38
+ }, updates: Partial<InferAttributes<M>>, filters: Partial<InferAttributes<M>>): Promise<number>;
39
+ /**
40
+ * Eliminar registros de la base de datos
41
+ */
42
+ static delete<M extends Table>(this: {
43
+ new (data: InferAttributes<M>): M;
44
+ prototype: M;
45
+ }, filters: Partial<InferAttributes<M>>): Promise<number>;
46
+ /** Filtrar registros por campo igual a valor */
47
+ static where<M extends Table, K extends keyof InferAttributes<M>>(this: {
48
+ new (data: InferAttributes<M>): M;
49
+ prototype: M;
50
+ }, field: K, value: InferAttributes<M>[K] | InferAttributes<M>[K][]): Promise<M[]>;
51
+ /** Filtrar registros por campo con operador específico */
52
+ static where<M extends Table, K extends keyof InferAttributes<M>>(this: {
53
+ new (data: InferAttributes<M>): M;
54
+ prototype: M;
55
+ }, field: K, operator: QueryOperator, value: InferAttributes<M>[K] | InferAttributes<M>[K][]): Promise<M[]>;
56
+ /** Filtrar registros por múltiples campos (AND) */
57
+ static where<M extends Table>(this: {
58
+ new (data: InferAttributes<M>): M;
59
+ prototype: M;
60
+ }, filters: Partial<InferAttributes<M>>): Promise<M[]>;
61
+ /** Filtrar registros con opciones avanzadas */
62
+ static where<M extends Table>(this: {
63
+ new (data: InferAttributes<M>): M;
64
+ prototype: M;
65
+ }, filters: Partial<InferAttributes<M>>, options: WhereQueryOptions<M>): Promise<M[]>;
66
+ /**
67
+ * Obtener el primer registro que coincida con los filtros
68
+ */
69
+ static first<M extends Table, K extends keyof InferAttributes<M>>(this: {
70
+ new (data: InferAttributes<M>): M;
71
+ prototype: M;
72
+ }, field: K, value: InferAttributes<M>[K]): Promise<M | undefined>;
73
+ static first<M extends Table, K extends keyof InferAttributes<M>>(this: {
74
+ new (data: InferAttributes<M>): M;
75
+ prototype: M;
76
+ }, field: K, operator: QueryOperator, value: InferAttributes<M>[K] | InferAttributes<M>[K][]): Promise<M | undefined>;
77
+ static first<M extends Table>(this: {
78
+ new (data: InferAttributes<M>): M;
79
+ prototype: M;
80
+ }, filters: Partial<InferAttributes<M>>): Promise<M | undefined>;
81
+ /**
82
+ * Obtener el último registro que coincida con los filtros
83
+ */
84
+ static last<M extends Table, K extends keyof InferAttributes<M>>(this: {
85
+ new (data: InferAttributes<M>): M;
86
+ prototype: M;
87
+ }, field: K, value: InferAttributes<M>[K]): Promise<M | undefined>;
88
+ static last<M extends Table, K extends keyof InferAttributes<M>>(this: {
89
+ new (data: InferAttributes<M>): M;
90
+ prototype: M;
91
+ }, field: K, operator: QueryOperator, value: InferAttributes<M>[K] | InferAttributes<M>[K][]): Promise<M | undefined>;
92
+ static last<M extends Table>(this: {
93
+ new (data: InferAttributes<M>): M;
94
+ prototype: M;
95
+ }, filters: Partial<InferAttributes<M>>): Promise<M | undefined>;
96
+ }
97
+ export { STORE };
98
+ export type { WrapperEntry };
@@ -0,0 +1,459 @@
1
+ "use strict";
2
+ /**
3
+ * @file table.ts
4
+ * @descripcion Clase Table rediseñada con API completa y tipado estricto
5
+ * @autor Miguel Alejandro
6
+ * @fecha 2025-07-30
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.STORE = void 0;
10
+ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
11
+ const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
12
+ const relations_1 = require("../utils/relations");
13
+ const client_1 = require("./client");
14
+ const wrapper_1 = require("./wrapper");
15
+ Object.defineProperty(exports, "STORE", { enumerable: true, get: function () { return wrapper_1.STORE; } });
16
+ /** Tipos importados desde @types */
17
+ // =============================================================================
18
+ // FUNCIONES UTILITARIAS
19
+ // =============================================================================
20
+ function validateOperator(operator) {
21
+ const validOps = [
22
+ "=",
23
+ "!=",
24
+ "<",
25
+ "<=",
26
+ ">",
27
+ ">=",
28
+ "in",
29
+ "not-in",
30
+ "contains",
31
+ "begins-with",
32
+ ];
33
+ if (!validOps.includes(operator)) {
34
+ throw new Error(`Operador inválido: ${operator}. Válidos: ${validOps.join(", ")}`);
35
+ }
36
+ return operator;
37
+ }
38
+ function buildConditionExpression(filters, operator = "=") {
39
+ const expressions = [];
40
+ const names = {};
41
+ const values = {};
42
+ Object.entries(filters).forEach(([key, value], index) => {
43
+ const nameKey = `#attr${index}`;
44
+ const valueKey = `:val${index}`;
45
+ names[nameKey] = key;
46
+ if (operator === "in" && Array.isArray(value)) {
47
+ const inValues = value.map((v, i) => {
48
+ const inValueKey = `:val${index}_${i}`;
49
+ values[inValueKey] = v;
50
+ return inValueKey;
51
+ });
52
+ expressions.push(`${nameKey} IN (${inValues.join(", ")})`);
53
+ }
54
+ else if (operator === "not-in" && Array.isArray(value)) {
55
+ const notInValues = value.map((v, i) => {
56
+ const notInValueKey = `:val${index}_${i}`;
57
+ values[notInValueKey] = v;
58
+ return notInValueKey;
59
+ });
60
+ expressions.push(`NOT ${nameKey} IN (${notInValues.join(", ")})`);
61
+ }
62
+ else if (operator === "contains") {
63
+ values[valueKey] = value;
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}`);
74
+ }
75
+ else {
76
+ values[valueKey] = value;
77
+ expressions.push(`${nameKey} ${operator} ${valueKey}`);
78
+ }
79
+ });
80
+ return {
81
+ expression: expressions.join(" AND "),
82
+ names,
83
+ values,
84
+ };
85
+ }
86
+ // =============================================================================
87
+ // CLASE TABLE REDISEÑADA
88
+ // =============================================================================
89
+ class Table {
90
+ constructor(data) {
91
+ (0, client_1.requireClient)();
92
+ const meta = (0, wrapper_1.mustMeta)(Object.getPrototypeOf(this).constructor);
93
+ // Inicializar propiedades con valores por defecto
94
+ meta.columns.forEach((col, key) => {
95
+ if (typeof key === "string" && !(key in data)) {
96
+ // Forzar aplicación de valor por defecto mediante setter
97
+ if (col.default !== undefined) {
98
+ const defaultValue = typeof col.default === "function" ? col.default() : col.default;
99
+ this[key] = defaultValue;
100
+ }
101
+ else {
102
+ this[key] = undefined;
103
+ }
104
+ }
105
+ });
106
+ Object.assign(this, data);
107
+ }
108
+ /** Serializar instancia a JSON plano */
109
+ toJSON() {
110
+ const meta = (0, wrapper_1.mustMeta)(Object.getPrototypeOf(this).constructor);
111
+ const result = {};
112
+ meta.columns.forEach((column, key) => {
113
+ if (typeof key === "string") {
114
+ // Acceder a la propiedad directamente para activar getters virtuales
115
+ const value = this[key];
116
+ if (value !== undefined) {
117
+ result[key] = value;
118
+ }
119
+ }
120
+ });
121
+ // Incluir propiedades enumerables ad-hoc no registradas como relaciones
122
+ // Esto permite persistir campos opcionales sin decoradores (p.ej. 'notes')
123
+ for (const key of Object.keys(this)) {
124
+ if (result[key] !== undefined)
125
+ continue;
126
+ if (meta.relations.has && meta.relations.has(key))
127
+ continue;
128
+ const val = this[key];
129
+ if (val === undefined)
130
+ continue;
131
+ if (typeof val === "function")
132
+ continue;
133
+ result[key] = val;
134
+ }
135
+ return result;
136
+ }
137
+ /** Guardar instancia (crear o actualizar) */
138
+ async save() {
139
+ const id = this.id;
140
+ const Ctor = this.constructor;
141
+ const meta = (0, wrapper_1.mustMeta)(Ctor);
142
+ const now = new Date().toISOString();
143
+ const isNew = id === undefined || id === null;
144
+ // Actualizar campos de timestamp
145
+ meta.columns.forEach((col, key) => {
146
+ if (col.createdAt && isNew) {
147
+ // Solo establecer createdAt si es un nuevo registro
148
+ this[key] = now;
149
+ }
150
+ else if (col.updatedAt) {
151
+ // Actualizar updatedAt en cada guardado
152
+ this[key] = now;
153
+ }
154
+ });
155
+ const payload = this.toJSON();
156
+ const client = (0, client_1.requireClient)();
157
+ if (isNew) {
158
+ // Crear nuevo registro
159
+ const created = await Ctor.create(payload);
160
+ Object.assign(this, created);
161
+ }
162
+ else {
163
+ // Actualizar registro existente
164
+ await client.send(new client_dynamodb_1.PutItemCommand({
165
+ TableName: meta.name,
166
+ Item: (0, util_dynamodb_1.marshall)(payload, { removeUndefinedValues: true }),
167
+ }));
168
+ }
169
+ return this;
170
+ }
171
+ /** Actualizar instancia */
172
+ async update(patch) {
173
+ Object.assign(this, patch);
174
+ return await this.save();
175
+ }
176
+ /** Eliminar instancia */
177
+ async destroy() {
178
+ const id = this.id;
179
+ if (id === undefined || id === null) {
180
+ throw new Error("destroy() requiere que la instancia tenga un id");
181
+ }
182
+ const Ctor = this.constructor;
183
+ return await Ctor.delete({ id: String(id) });
184
+ }
185
+ // ===========================================================================
186
+ // MÉTODOS ESTÁTICOS SEGÚN ESPECIFICACIONES
187
+ // ===========================================================================
188
+ /**
189
+ * Crear un nuevo registro en la base de datos
190
+ */
191
+ static async create(data) {
192
+ const client = (0, client_1.requireClient)();
193
+ const meta = (0, wrapper_1.mustMeta)(this);
194
+ const instance = new this(data);
195
+ // Establecer timestamps si corresponde
196
+ const now = new Date().toISOString();
197
+ meta.columns.forEach((col, key) => {
198
+ if (col.createdAt && instance[key] === undefined) {
199
+ instance[key] = now;
200
+ }
201
+ if (col.updatedAt) {
202
+ instance[key] = now;
203
+ }
204
+ });
205
+ const payload = instance.toJSON();
206
+ await client.send(new client_dynamodb_1.PutItemCommand({
207
+ TableName: meta.name,
208
+ Item: (0, util_dynamodb_1.marshall)(payload, { removeUndefinedValues: true }),
209
+ }));
210
+ return instance;
211
+ }
212
+ /**
213
+ * Actualizar registros en la base de datos
214
+ * @param updates - Campos a actualizar. Los campos con valor `undefined` se ignoran.
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) {
225
+ return 0;
226
+ }
227
+ // No filtrar campos undefined aquí para permitir establecer valores nulos/explicitos
228
+ const cleanUpdates = { ...updates };
229
+ // Verificar si hay campos para actualizar
230
+ if (Object.keys(cleanUpdates).length === 0) {
231
+ return recordsToUpdate.length; // No hay cambios que hacer
232
+ }
233
+ // Actualizar cada registro
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
+ }
244
+ }
245
+ // Actualizar timestamp updated_at si está configurado
246
+ let updatedAtKey = undefined;
247
+ for (const [k, col] of meta.columns.entries()) {
248
+ if (col.updatedAt === true) {
249
+ updatedAtKey = k;
250
+ break;
251
+ }
252
+ }
253
+ if (updatedAtKey !== undefined) {
254
+ updatedData[String(updatedAtKey)] = new Date().toISOString();
255
+ }
256
+ // Actualizar en la base de datos
257
+ await client.send(new client_dynamodb_1.PutItemCommand({
258
+ TableName: meta.name,
259
+ Item: (0, util_dynamodb_1.marshall)(updatedData, { removeUndefinedValues: true }),
260
+ }));
261
+ });
262
+ await Promise.all(updatePromises);
263
+ return recordsToUpdate.length;
264
+ }
265
+ /**
266
+ * Eliminar registros de la base de datos
267
+ */
268
+ static async delete(filters) {
269
+ const recordsToDelete = await this.where(filters);
270
+ if (recordsToDelete.length === 0) {
271
+ return 0;
272
+ }
273
+ const client = (0, client_1.requireClient)();
274
+ const meta = (0, wrapper_1.mustMeta)(this);
275
+ const deletePromises = recordsToDelete.map(async (record) => {
276
+ const id = record.id;
277
+ if (id) {
278
+ await client.send(new client_dynamodb_1.DeleteItemCommand({
279
+ TableName: meta.name,
280
+ Key: (0, util_dynamodb_1.marshall)({ id: String(id) }),
281
+ }));
282
+ }
283
+ });
284
+ await Promise.all(deletePromises);
285
+ return recordsToDelete.length;
286
+ }
287
+ /** Implementación del método where */
288
+ static async where(...args) {
289
+ const client = (0, client_1.requireClient)();
290
+ const meta = (0, wrapper_1.mustMeta)(this);
291
+ let filters;
292
+ let options = {};
293
+ let operator = "=";
294
+ // Parsear argumentos según la sobrecarga utilizada
295
+ if (args.length === 2) {
296
+ if (typeof args[0] === "string") {
297
+ // where(field, value) - check if value is array for IN operation
298
+ const value = args[1];
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
+ }
309
+ }
310
+ else if (args.length === 3) {
311
+ // where(field, operator, value)
312
+ const [field, op, value] = args;
313
+ operator = validateOperator(op);
314
+ filters = { [field]: value };
315
+ }
316
+ else if (args.length === 1) {
317
+ // where(filters)
318
+ filters = args[0];
319
+ }
320
+ else {
321
+ throw new Error("Argumentos inválidos para where()");
322
+ }
323
+ // Validar límites y opciones
324
+ if (options.limit && options.limit < 0) {
325
+ throw new Error("limit debe ser mayor o igual a 0");
326
+ }
327
+ if (options.skip && options.skip < 0) {
328
+ throw new Error("skip debe ser mayor o igual a 0");
329
+ }
330
+ if (options.order && !["ASC", "DESC"].includes(options.order)) {
331
+ throw new Error('order debe ser "ASC" o "DESC"');
332
+ }
333
+ // Construir la consulta DynamoDB
334
+ const { expression, names, values } = buildConditionExpression(filters, operator);
335
+ const scanParams = {
336
+ TableName: meta.name,
337
+ };
338
+ // Initialize ExpressionAttributeNames to avoid conflicts
339
+ if (!scanParams.ExpressionAttributeNames) {
340
+ scanParams.ExpressionAttributeNames = {};
341
+ }
342
+ if (expression) {
343
+ scanParams.FilterExpression = expression;
344
+ // Merge filter attribute names
345
+ Object.assign(scanParams.ExpressionAttributeNames, names);
346
+ scanParams.ExpressionAttributeValues = (0, util_dynamodb_1.marshall)(values);
347
+ }
348
+ if (options.attributes) {
349
+ // Handle projection attributes with proper aliases
350
+ const projectionExpressions = options.attributes.map((attr, index) => {
351
+ const aliasKey = `#proj${index}`;
352
+ scanParams.ExpressionAttributeNames[aliasKey] = attr;
353
+ return aliasKey;
354
+ });
355
+ scanParams.ProjectionExpression = projectionExpressions.join(", ");
356
+ }
357
+ // Ejecutar consulta con paginación
358
+ let allItems = [];
359
+ let lastEvaluatedKey = undefined;
360
+ let scannedCount = 0;
361
+ const targetSkip = options.skip || 0;
362
+ const targetLimit = options.limit || 100;
363
+ do {
364
+ if (lastEvaluatedKey) {
365
+ scanParams.ExclusiveStartKey = lastEvaluatedKey;
366
+ }
367
+ const result = await client.send(new client_dynamodb_1.ScanCommand(scanParams));
368
+ if (result.Items) {
369
+ const items = result.Items.map((item) => (0, util_dynamodb_1.unmarshall)(item));
370
+ for (const item of items) {
371
+ if (scannedCount < targetSkip) {
372
+ scannedCount++;
373
+ continue;
374
+ }
375
+ if (allItems.length >= targetLimit) {
376
+ break;
377
+ }
378
+ allItems.push(item);
379
+ scannedCount++;
380
+ }
381
+ }
382
+ lastEvaluatedKey = result.LastEvaluatedKey;
383
+ } while (lastEvaluatedKey && allItems.length < targetLimit);
384
+ // Aplicar ordenamiento
385
+ if (options.order) {
386
+ allItems.sort((a, b) => {
387
+ const sortField = Object.keys(filters)[0] || "id";
388
+ const aVal = a[sortField];
389
+ const bVal = b[sortField];
390
+ if (aVal < bVal)
391
+ return options.order === "ASC" ? -1 : 1;
392
+ if (aVal > bVal)
393
+ return options.order === "ASC" ? 1 : -1;
394
+ return 0;
395
+ });
396
+ }
397
+ // Convertir a instancias del modelo
398
+ const instances = allItems.map((item) => {
399
+ if (options.attributes) {
400
+ // When using attribute selection, create minimal instances
401
+ // only with the requested fields to avoid default value population
402
+ const instance = Object.create(this.prototype);
403
+ Object.assign(instance, item);
404
+ return instance;
405
+ }
406
+ else {
407
+ // Normal instantiation with all defaults
408
+ return new this(item);
409
+ }
410
+ });
411
+ // Procesar includes si están presentes
412
+ if (options.include) {
413
+ return await (0, relations_1.processIncludes)(this, instances, options.include);
414
+ }
415
+ return instances;
416
+ }
417
+ static async first(...args) {
418
+ const results = await this.where(...args);
419
+ return results[0] || undefined;
420
+ }
421
+ static async last(...args) {
422
+ // Soporte de firmas sin generar una cuarta sobrecarga inválida para where()
423
+ if (args.length === 0) {
424
+ const results = await this.where({}, { order: "DESC", limit: 1 });
425
+ return results[0] || undefined;
426
+ }
427
+ if (args.length === 1) {
428
+ if (typeof args[0] === "object" && !Array.isArray(args[0])) {
429
+ const results = await this.where(args[0], {
430
+ order: "DESC",
431
+ limit: 1,
432
+ });
433
+ return results[0] || undefined;
434
+ }
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
+ }
455
+ throw new Error("Argumentos no válidos para last()");
456
+ }
457
+ }
458
+ exports.default = Table;
459
+ //# sourceMappingURL=table.js.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @file wrapper.ts
3
+ * @descripcion Sistema de tipos y wrapper optimizado
4
+ * @autor Miguel Alejandro
5
+ * @fecha 2025-01-28
6
+ */
7
+ import type { Column, WrapperEntry } from "../@types/index";
8
+ export type { Column, WrapperEntry, HasMany, BelongsTo, NonAttribute, CreationOptional, InferAttributes, FilterableAttributes, QueryResult, WhereOptions, WhereOptionsWithoutWhere, QueryOperator, IncludeOptions, IncludeRelationOptions, WhereQueryOptions, Mutate, Validate, } from "../@types/index";
9
+ export declare const STORE: unique symbol;
10
+ declare const wrapper: Map<Function, WrapperEntry>;
11
+ /** Obtener o crear entrada wrapper para constructor */
12
+ export declare const ensureConfig: (ctor: Function, table_name: string) => WrapperEntry;
13
+ /** Obtener o crear configuración de columna para propiedad */
14
+ export declare const ensureColumn: (entry: WrapperEntry, prop: string | symbol, column_name: string) => Column;
15
+ /** Obtener metadatos requeridos (throws si no existen) */
16
+ export declare const mustMeta: (ctor: Function) => WrapperEntry;
17
+ export default wrapper;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ /**
3
+ * @file wrapper.ts
4
+ * @descripcion Sistema de tipos y wrapper optimizado
5
+ * @autor Miguel Alejandro
6
+ * @fecha 2025-01-28
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.mustMeta = exports.ensureColumn = exports.ensureConfig = exports.STORE = void 0;
10
+ exports.STORE = Symbol("dynamite:values");
11
+ const wrapper = new Map();
12
+ /** Obtener o crear entrada wrapper para constructor */
13
+ const ensureConfig = (ctor, table_name) => {
14
+ const existing = wrapper.get(ctor);
15
+ if (existing)
16
+ return existing;
17
+ const entry = {
18
+ name: table_name,
19
+ columns: new Map(),
20
+ relations: new Map(),
21
+ };
22
+ wrapper.set(ctor, entry);
23
+ return entry;
24
+ };
25
+ exports.ensureConfig = ensureConfig;
26
+ /** Obtener o crear configuración de columna para propiedad */
27
+ const ensureColumn = (entry, prop, column_name) => {
28
+ const existing = entry.columns.get(prop);
29
+ if (existing)
30
+ return existing;
31
+ const column = { name: column_name, mutate: [], validate: [] };
32
+ entry.columns.set(prop, column);
33
+ return column;
34
+ };
35
+ exports.ensureColumn = ensureColumn;
36
+ /** Obtener metadatos requeridos (throws si no existen) */
37
+ const mustMeta = (ctor) => {
38
+ const meta = wrapper.get(ctor);
39
+ if (!meta) {
40
+ throw new Error(`Metadatos no encontrados para ${ctor.name}. ¿Usaste decoradores @Index, @PrimaryKey, etc.?`);
41
+ }
42
+ return meta;
43
+ };
44
+ exports.mustMeta = mustMeta;
45
+ exports.default = wrapper;
46
+ //# sourceMappingURL=wrapper.js.map
@@ -0,0 +1 @@
1
+ export default function BelongsTo(targetModel: () => any, localKey: string, foreignKey?: string): PropertyDecorator;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = BelongsTo;
4
+ const wrapper_1 = require("../core/wrapper");
5
+ function BelongsTo(targetModel, localKey, foreignKey) {
6
+ return (target, prop) => {
7
+ const entry = (0, wrapper_1.ensureConfig)(target.constructor, target.constructor.name);
8
+ entry.relations.set(prop, {
9
+ type: "belongsTo",
10
+ targetModel,
11
+ foreignKey: foreignKey || "id",
12
+ localKey,
13
+ });
14
+ // Crear getter dinámico para la relación
15
+ Object.defineProperty(target, prop, {
16
+ get() {
17
+ return this[`_${prop.toString()}`] || null;
18
+ },
19
+ enumerable: true,
20
+ configurable: true,
21
+ });
22
+ };
23
+ }
24
+ //# sourceMappingURL=belongs_to.js.map
@@ -0,0 +1 @@
1
+ export default function CreatedAt(): PropertyDecorator;
@@ -1,21 +1,10 @@
1
1
  "use strict";
2
- /*
3
- * Dinamite ORM — @CreatedAt Decorator (wrapper)
4
- * -------------------------------------------
5
- * Establece un valor por defecto con la fecha/hora actual (ISO‑string)
6
- * usando @Default.
7
- *
8
- * © 2025 Miguel Alejandro
9
- */
10
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
11
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
12
4
  };
13
5
  Object.defineProperty(exports, "__esModule", { value: true });
14
6
  exports.default = CreatedAt;
15
7
  const default_1 = __importDefault(require("./default"));
16
- /**
17
- * Asigna automáticamente la fecha de creación en nuevas instancias.
18
- */
19
8
  function CreatedAt() {
20
9
  return (0, default_1.default)(() => new Date().toISOString());
21
10
  }
@@ -0,0 +1 @@
1
+ export default function Default(factory: any): PropertyDecorator;