@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,88 @@
1
+ /**
2
+ * @file transaction-manager.ts
3
+ * @description Transaction simulation and rollback system for DynamoDB
4
+ * @author Miguel Alejandro
5
+ * @fecha 2025-08-31
6
+ */
7
+ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
8
+ interface TransactionOperation {
9
+ type: 'create' | 'update' | 'delete';
10
+ tableName: string;
11
+ key: Record<string, any>;
12
+ item?: Record<string, any>;
13
+ conditionExpression?: string;
14
+ rollbackData?: Record<string, any>;
15
+ }
16
+ interface TransactionSnapshot {
17
+ id: string;
18
+ operations: TransactionOperation[];
19
+ timestamp: number;
20
+ status: 'pending' | 'committed' | 'rolled_back' | 'failed';
21
+ }
22
+ interface TransactionConfig {
23
+ maxOperationsPerTransaction: number;
24
+ snapshotTTL: number;
25
+ enableOptimisticLocking: boolean;
26
+ maxRetries: number;
27
+ }
28
+ declare class TransactionManager {
29
+ private static readonly DEFAULT_CONFIG;
30
+ private client;
31
+ private config;
32
+ private activeTransactions;
33
+ private snapshots;
34
+ constructor(client: DynamoDBClient, config?: Partial<TransactionConfig>);
35
+ /**
36
+ * Iniciar nueva transacción
37
+ */
38
+ beginTransaction(operations: TransactionOperation[]): Promise<string>;
39
+ /**
40
+ * Ejecutar transacción usando DynamoDB TransactWrite
41
+ */
42
+ commitTransaction(transactionId: string): Promise<void>;
43
+ /**
44
+ * Rollback manual de transacción
45
+ */
46
+ rollbackTransaction(transactionId: string): Promise<void>;
47
+ /**
48
+ * Crear snapshot del estado actual para rollback
49
+ */
50
+ private createSnapshot;
51
+ /**
52
+ * Obtener estado actual de un item
53
+ */
54
+ private getCurrentItemState;
55
+ /**
56
+ * Crear operaciones inversas para rollback
57
+ */
58
+ private createRollbackOperations;
59
+ /**
60
+ * Ejecutar lote de operaciones usando TransactWrite
61
+ */
62
+ private executeBatch;
63
+ /**
64
+ * Dividir operaciones en lotes según límites DynamoDB
65
+ */
66
+ private splitIntoBatches;
67
+ /**
68
+ * Validar operaciones antes de transacción
69
+ */
70
+ private validateOperations;
71
+ /**
72
+ * Limpiar snapshots expirados
73
+ */
74
+ private cleanupExpiredSnapshots;
75
+ /**
76
+ * Obtener estado de transacción
77
+ */
78
+ getTransactionStatus(transactionId: string): TransactionSnapshot | null;
79
+ /**
80
+ * Listar transacciones activas
81
+ */
82
+ getActiveTransactions(): TransactionSnapshot[];
83
+ private generateTransactionId;
84
+ }
85
+ declare class TransactionError extends Error {
86
+ constructor(message: string);
87
+ }
88
+ export { TransactionManager, TransactionOperation, TransactionSnapshot, TransactionError, TransactionConfig };
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+ /**
3
+ * @file transaction-manager.ts
4
+ * @description Transaction simulation and rollback system for DynamoDB
5
+ * @author Miguel Alejandro
6
+ * @fecha 2025-08-31
7
+ */
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.TransactionError = exports.TransactionManager = void 0;
13
+ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
14
+ const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
15
+ const throttle_manager_1 = __importDefault(require("./throttle-manager"));
16
+ const security_validator_1 = __importDefault(require("./security-validator"));
17
+ class TransactionManager {
18
+ static { this.DEFAULT_CONFIG = {
19
+ maxOperationsPerTransaction: 25, // DynamoDB limit
20
+ snapshotTTL: 3600000, // 1 hour
21
+ enableOptimisticLocking: true,
22
+ maxRetries: 3,
23
+ }; }
24
+ constructor(client, config) {
25
+ this.activeTransactions = new Map();
26
+ this.snapshots = new Map();
27
+ this.client = client;
28
+ this.config = { ...TransactionManager.DEFAULT_CONFIG, ...config };
29
+ // Cleanup expired snapshots
30
+ setInterval(() => this.cleanupExpiredSnapshots(), 300000); // 5 minutes
31
+ }
32
+ /**
33
+ * Iniciar nueva transacción
34
+ */
35
+ async beginTransaction(operations) {
36
+ const transactionId = this.generateTransactionId();
37
+ // Validar operaciones
38
+ this.validateOperations(operations);
39
+ // Crear snapshot del estado actual para rollback
40
+ const snapshot = await this.createSnapshot(transactionId, operations);
41
+ this.activeTransactions.set(transactionId, snapshot);
42
+ this.snapshots.set(transactionId, snapshot);
43
+ return transactionId;
44
+ }
45
+ /**
46
+ * Ejecutar transacción usando DynamoDB TransactWrite
47
+ */
48
+ async commitTransaction(transactionId) {
49
+ const snapshot = this.activeTransactions.get(transactionId);
50
+ if (!snapshot) {
51
+ throw new TransactionError(`Transaction ${transactionId} not found`);
52
+ }
53
+ try {
54
+ snapshot.status = 'pending';
55
+ // Agrupar operaciones en lotes de 25 (límite DynamoDB)
56
+ const batches = this.splitIntoBatches(snapshot.operations);
57
+ for (const batch of batches) {
58
+ await this.executeBatch(batch);
59
+ }
60
+ snapshot.status = 'committed';
61
+ this.activeTransactions.delete(transactionId);
62
+ }
63
+ catch (error) {
64
+ snapshot.status = 'failed';
65
+ // Intentar rollback automático
66
+ try {
67
+ await this.rollbackTransaction(transactionId);
68
+ }
69
+ catch (rollbackError) {
70
+ console.error('Rollback failed after commit error:', rollbackError);
71
+ }
72
+ throw new TransactionError(`Transaction commit failed: ${error.message}`);
73
+ }
74
+ }
75
+ /**
76
+ * Rollback manual de transacción
77
+ */
78
+ async rollbackTransaction(transactionId) {
79
+ const snapshot = this.snapshots.get(transactionId);
80
+ if (!snapshot) {
81
+ throw new TransactionError(`Transaction snapshot ${transactionId} not found`);
82
+ }
83
+ try {
84
+ // Crear operaciones inversas para rollback
85
+ const rollbackOps = this.createRollbackOperations(snapshot.operations);
86
+ if (rollbackOps.length > 0) {
87
+ const batches = this.splitIntoBatches(rollbackOps);
88
+ for (const batch of batches) {
89
+ await this.executeBatch(batch);
90
+ }
91
+ }
92
+ snapshot.status = 'rolled_back';
93
+ this.activeTransactions.delete(transactionId);
94
+ }
95
+ catch (error) {
96
+ throw new TransactionError(`Rollback failed: ${error.message}`);
97
+ }
98
+ }
99
+ /**
100
+ * Crear snapshot del estado actual para rollback
101
+ */
102
+ async createSnapshot(transactionId, operations) {
103
+ const snapshot = {
104
+ id: transactionId,
105
+ operations: [],
106
+ timestamp: Date.now(),
107
+ status: 'pending',
108
+ };
109
+ // Para cada operación, guardar el estado actual del item
110
+ for (const op of operations) {
111
+ try {
112
+ const currentItem = await this.getCurrentItemState(op.tableName, op.key);
113
+ const snapshotOp = {
114
+ ...op,
115
+ rollbackData: currentItem || undefined, // Estado actual para rollback
116
+ };
117
+ snapshot.operations.push(snapshotOp);
118
+ }
119
+ catch (error) {
120
+ if (error.name !== 'ResourceNotFoundException') {
121
+ throw error;
122
+ }
123
+ // Item no existe, sin datos de rollback necesarios
124
+ snapshot.operations.push(op);
125
+ }
126
+ }
127
+ return snapshot;
128
+ }
129
+ /**
130
+ * Obtener estado actual de un item
131
+ */
132
+ async getCurrentItemState(tableName, key) {
133
+ const params = {
134
+ TransactItems: [{
135
+ Get: {
136
+ TableName: tableName,
137
+ Key: (0, util_dynamodb_1.marshall)(key),
138
+ }
139
+ }]
140
+ };
141
+ const result = await throttle_manager_1.default.executeWithRetry(() => this.client.send(new client_dynamodb_1.TransactGetItemsCommand(params)), `GetItem-${tableName}`);
142
+ return result.Responses?.[0]?.Item
143
+ ? (0, util_dynamodb_1.unmarshall)(result.Responses[0].Item)
144
+ : null;
145
+ }
146
+ /**
147
+ * Crear operaciones inversas para rollback
148
+ */
149
+ createRollbackOperations(operations) {
150
+ const rollbackOps = [];
151
+ // Procesar en orden inverso
152
+ for (let i = operations.length - 1; i >= 0; i--) {
153
+ const op = operations[i];
154
+ switch (op.type) {
155
+ case 'create':
156
+ // Para rollback de create: delete el item
157
+ rollbackOps.push({
158
+ type: 'delete',
159
+ tableName: op.tableName,
160
+ key: op.key,
161
+ });
162
+ break;
163
+ case 'update':
164
+ if (op.rollbackData) {
165
+ // Para rollback de update: restore estado anterior
166
+ rollbackOps.push({
167
+ type: 'update',
168
+ tableName: op.tableName,
169
+ key: op.key,
170
+ item: op.rollbackData,
171
+ });
172
+ }
173
+ break;
174
+ case 'delete':
175
+ if (op.rollbackData) {
176
+ // Para rollback de delete: recrear el item
177
+ rollbackOps.push({
178
+ type: 'create',
179
+ tableName: op.tableName,
180
+ key: op.key,
181
+ item: op.rollbackData,
182
+ });
183
+ }
184
+ break;
185
+ }
186
+ }
187
+ return rollbackOps;
188
+ }
189
+ /**
190
+ * Ejecutar lote de operaciones usando TransactWrite
191
+ */
192
+ async executeBatch(operations) {
193
+ const transactItems = operations.map(op => {
194
+ security_validator_1.default.validateItemSize(op.item || op.key);
195
+ switch (op.type) {
196
+ case 'create':
197
+ case 'update':
198
+ return {
199
+ Put: {
200
+ TableName: op.tableName,
201
+ Item: (0, util_dynamodb_1.marshall)(op.item, { removeUndefinedValues: true }),
202
+ ConditionExpression: op.conditionExpression,
203
+ }
204
+ };
205
+ case 'delete':
206
+ return {
207
+ Delete: {
208
+ TableName: op.tableName,
209
+ Key: (0, util_dynamodb_1.marshall)(op.key),
210
+ ConditionExpression: op.conditionExpression,
211
+ }
212
+ };
213
+ default:
214
+ throw new TransactionError(`Unknown operation type: ${op.type}`);
215
+ }
216
+ });
217
+ const command = new client_dynamodb_1.TransactWriteItemsCommand({
218
+ TransactItems: transactItems,
219
+ });
220
+ await throttle_manager_1.default.executeWithRetry(() => this.client.send(command), 'TransactWrite');
221
+ }
222
+ /**
223
+ * Dividir operaciones en lotes según límites DynamoDB
224
+ */
225
+ splitIntoBatches(operations) {
226
+ const batches = [];
227
+ const batchSize = this.config.maxOperationsPerTransaction;
228
+ for (let i = 0; i < operations.length; i += batchSize) {
229
+ batches.push(operations.slice(i, i + batchSize));
230
+ }
231
+ return batches;
232
+ }
233
+ /**
234
+ * Validar operaciones antes de transacción
235
+ */
236
+ validateOperations(operations) {
237
+ if (operations.length === 0) {
238
+ throw new TransactionError('Transaction must contain at least one operation');
239
+ }
240
+ if (operations.length > this.config.maxOperationsPerTransaction * 10) {
241
+ throw new TransactionError(`Too many operations: ${operations.length}`);
242
+ }
243
+ for (const op of operations) {
244
+ // Validar estructura de la operación
245
+ if (!op.tableName || !op.key) {
246
+ throw new TransactionError('Invalid operation: missing tableName or key');
247
+ }
248
+ // Validar datos de seguridad
249
+ security_validator_1.default.validateQueryFilters(op.key);
250
+ if (op.item) {
251
+ security_validator_1.default.validateValue(op.item);
252
+ }
253
+ }
254
+ }
255
+ /**
256
+ * Limpiar snapshots expirados
257
+ */
258
+ cleanupExpiredSnapshots() {
259
+ const now = Date.now();
260
+ const expired = [];
261
+ for (const [id, snapshot] of this.snapshots.entries()) {
262
+ if (now - snapshot.timestamp > this.config.snapshotTTL) {
263
+ expired.push(id);
264
+ }
265
+ }
266
+ expired.forEach(id => {
267
+ this.snapshots.delete(id);
268
+ this.activeTransactions.delete(id);
269
+ });
270
+ if (expired.length > 0) {
271
+ console.log(`Cleaned up ${expired.length} expired transaction snapshots`);
272
+ }
273
+ }
274
+ /**
275
+ * Obtener estado de transacción
276
+ */
277
+ getTransactionStatus(transactionId) {
278
+ return this.snapshots.get(transactionId) || null;
279
+ }
280
+ /**
281
+ * Listar transacciones activas
282
+ */
283
+ getActiveTransactions() {
284
+ return Array.from(this.activeTransactions.values());
285
+ }
286
+ generateTransactionId() {
287
+ return `txn_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
288
+ }
289
+ }
290
+ exports.TransactionManager = TransactionManager;
291
+ class TransactionError extends Error {
292
+ constructor(message) {
293
+ super(message);
294
+ this.name = 'TransactionError';
295
+ }
296
+ }
297
+ exports.TransactionError = TransactionError;
298
+ //# sourceMappingURL=transaction-manager.js.map
@@ -1,138 +0,0 @@
1
- import { DynamoDBClientConfig } from "@aws-sdk/client-dynamodb";
2
- import { InferAttributes, STORE, WrapperEntry } from "./wrapper";
3
- export declare function connect(cfg: DynamoDBClientConfig): void;
4
- export default class Table<T extends {} = any> {
5
- protected [STORE]: {
6
- [K in keyof T]?: T[K];
7
- };
8
- constructor(data: InferAttributes<T>);
9
- save(): Promise<this>;
10
- update(patch: InferAttributes<T>): Promise<this>;
11
- destroy(): Promise<void>;
12
- static create<M extends Table>(this: {
13
- new (data: InferAttributes<M>): M;
14
- prototype: M;
15
- }, data: InferAttributes<M>): Promise<M>;
16
- static update<M extends Table>(this: {
17
- new (data: InferAttributes<M>): M;
18
- prototype: M;
19
- }, id: string, record: InferAttributes<M>): Promise<void>;
20
- static destroy<M extends Table>(this: {
21
- new (data: InferAttributes<M>): M;
22
- prototype: M;
23
- }, id: string): Promise<null>;
24
- /**
25
- * Realiza una consulta sobre la tabla, devolviendo una lista de resultados
26
- * según condiciones y opciones de paginación/orden. Utiliza `QueryCommand`
27
- * si la condición es por PK/SK con igualdad, o `ScanCommand` como fallback.
28
- *
29
- * @template M Instancia de la clase Table.
30
- * @template K Clave de atributos inferida.
31
- *
32
- * @param key - Clave a filtrar (ej. "id").
33
- * @param value - Valor a comparar (equivalente a '=' implícito).
34
- * @param options - (Opcional) Opciones de paginación: { limit, skip, order }.
35
- *
36
- * @example
37
- * await User.where('id', 'u1');
38
- * await User.where('email', '=', 'a@b.com', { limit: 5, order: 'DESC' });
39
- * await User.where({ email: 'x@y.com' }, { skip: 10, limit: 1 });
40
- *
41
- * @param filters - Objeto con pares clave/valor para consulta tipo AND.
42
- * @param options - (Opcional) Opciones de paginación: { limit, skip, order }.
43
- *
44
- * @returns - Array de instancias de la tabla que cumplen la condición.
45
- */
46
- static where<M extends Table, K extends keyof InferAttributes<M>>(this: {
47
- new (data: InferAttributes<M>): M;
48
- prototype: M;
49
- }, key: K, value: InferAttributes<M>[K], options?: {
50
- limit?: number;
51
- skip?: number;
52
- order?: "ASC" | "DESC";
53
- }): Promise<M[]>;
54
- static where<M extends Table, K extends keyof InferAttributes<M>>(this: {
55
- new (data: InferAttributes<M>): M;
56
- prototype: M;
57
- }, key: K, op: "=" | "!=" | "<" | "<=" | ">" | ">=" | "in" | "not-in", value: InferAttributes<M>[K] | InferAttributes<M>[K][], options?: {
58
- limit?: number;
59
- skip?: number;
60
- order?: "ASC" | "DESC";
61
- }): Promise<M[]>;
62
- static where<M extends Table>(this: {
63
- new (data: InferAttributes<M>): M;
64
- prototype: M;
65
- }, filters: Partial<{
66
- [K in keyof InferAttributes<M>]: InferAttributes<M>[K] | InferAttributes<M>[K][];
67
- }>, options?: {
68
- limit?: number;
69
- skip?: number;
70
- order?: "ASC" | "DESC";
71
- }): Promise<M[]>;
72
- /**
73
- * Obtiene el primer elemento que coincida con la condición especificada.
74
- * Equivalente a where() con { limit: 1, skip: 0, order: "ASC" }.
75
- *
76
- * @template M Instancia de la clase Table.
77
- * @template K Clave de atributos inferida.
78
- *
79
- * @param key - Clave a filtrar (ej. "id").
80
- * @param value - Valor a comparar (equivalente a '=' implícito).
81
- *
82
- * @example
83
- * const user = await User.first('id', 'u1');
84
- * const firstActive = await User.first('status', '=', 'active');
85
- * const firstMatch = await User.first({ email: 'test@example.com' });
86
- *
87
- * @returns - El primer elemento encontrado o undefined si no hay coincidencias.
88
- */
89
- static first<M extends Table, K extends keyof InferAttributes<M>>(this: {
90
- new (data: InferAttributes<M>): M;
91
- prototype: M;
92
- }, key: K, value: InferAttributes<M>[K]): Promise<M | undefined>;
93
- static first<M extends Table, K extends keyof InferAttributes<M>>(this: {
94
- new (data: InferAttributes<M>): M;
95
- prototype: M;
96
- }, key: K, op: "=" | "!=" | "<" | "<=" | ">" | ">=" | "in" | "not-in", value: InferAttributes<M>[K] | InferAttributes<M>[K][]): Promise<M | undefined>;
97
- static first<M extends Table>(this: {
98
- new (data: InferAttributes<M>): M;
99
- prototype: M;
100
- }, filters: Partial<{
101
- [K in keyof InferAttributes<M>]: InferAttributes<M>[K] | InferAttributes<M>[K][];
102
- }>): Promise<M | undefined>;
103
- /**
104
- * Obtiene el último elemento que coincida con la condición especificada.
105
- * Equivalente a where() con { limit: 1, skip: 0, order: "DESC" }.
106
- *
107
- * @template M Instancia de la clase Table.
108
- * @template K Clave de atributos inferida.
109
- *
110
- * @param key - Clave a filtrar (ej. "id").
111
- * @param value - Valor a comparar (equivalente a '=' implícito).
112
- *
113
- * @example
114
- * const user = await User.last('id', 'u1');
115
- * const lastActive = await User.last('status', '=', 'active');
116
- * const lastMatch = await User.last({ email: 'test@example.com' });
117
- *
118
- * @returns - El último elemento encontrado o undefined si no hay coincidencias.
119
- */
120
- static last<M extends Table, K extends keyof InferAttributes<M>>(this: {
121
- new (data: InferAttributes<M>): M;
122
- prototype: M;
123
- }, key: K, value: InferAttributes<M>[K]): Promise<M | undefined>;
124
- static last<M extends Table, K extends keyof InferAttributes<M>>(this: {
125
- new (data: InferAttributes<M>): M;
126
- prototype: M;
127
- }, key: K, op: "=" | "!=" | "<" | "<=" | ">" | ">=" | "in" | "not-in", value: InferAttributes<M>[K] | InferAttributes<M>[K][]): Promise<M | undefined>;
128
- static last<M extends Table>(this: {
129
- new (data: InferAttributes<M>): M;
130
- prototype: M;
131
- }, filters: Partial<{
132
- [K in keyof InferAttributes<M>]: InferAttributes<M>[K] | InferAttributes<M>[K][];
133
- }>): Promise<M | undefined>;
134
- toJSON(): Record<string, unknown>;
135
- toString(): string;
136
- }
137
- export { STORE };
138
- export type { WrapperEntry };