@ductape/sdk 0.0.4-v42 → 0.0.4-v43

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 (76) hide show
  1. package/dist/apps/services/app.service.d.ts +10 -0
  2. package/dist/apps/services/app.service.js +22 -0
  3. package/dist/apps/services/app.service.js.map +1 -1
  4. package/dist/database/adapters/base.adapter.d.ts +176 -0
  5. package/dist/database/adapters/base.adapter.js +31 -0
  6. package/dist/database/adapters/base.adapter.js.map +1 -0
  7. package/dist/database/adapters/dynamodb.adapter.d.ts +83 -0
  8. package/dist/database/adapters/dynamodb.adapter.js +1237 -0
  9. package/dist/database/adapters/dynamodb.adapter.js.map +1 -0
  10. package/dist/database/adapters/mongodb.adapter.d.ts +70 -0
  11. package/dist/database/adapters/mongodb.adapter.js +717 -0
  12. package/dist/database/adapters/mongodb.adapter.js.map +1 -0
  13. package/dist/database/adapters/mysql.adapter.d.ts +141 -0
  14. package/dist/database/adapters/mysql.adapter.js +1221 -0
  15. package/dist/database/adapters/mysql.adapter.js.map +1 -0
  16. package/dist/database/adapters/postgresql.adapter.d.ts +142 -0
  17. package/dist/database/adapters/postgresql.adapter.js +1288 -0
  18. package/dist/database/adapters/postgresql.adapter.js.map +1 -0
  19. package/dist/database/database.service.d.ts +190 -0
  20. package/dist/database/database.service.js +552 -0
  21. package/dist/database/database.service.js.map +1 -0
  22. package/dist/database/index.d.ts +18 -0
  23. package/dist/database/index.js +98 -0
  24. package/dist/database/index.js.map +1 -0
  25. package/dist/database/types/aggregation.types.d.ts +202 -0
  26. package/dist/database/types/aggregation.types.js +21 -0
  27. package/dist/database/types/aggregation.types.js.map +1 -0
  28. package/dist/database/types/connection.types.d.ts +132 -0
  29. package/dist/database/types/connection.types.js +6 -0
  30. package/dist/database/types/connection.types.js.map +1 -0
  31. package/dist/database/types/database.types.d.ts +173 -0
  32. package/dist/database/types/database.types.js +73 -0
  33. package/dist/database/types/database.types.js.map +1 -0
  34. package/dist/database/types/index.d.ts +12 -0
  35. package/dist/database/types/index.js +37 -0
  36. package/dist/database/types/index.js.map +1 -0
  37. package/dist/database/types/index.types.d.ts +220 -0
  38. package/dist/database/types/index.types.js +27 -0
  39. package/dist/database/types/index.types.js.map +1 -0
  40. package/dist/database/types/migration.types.d.ts +205 -0
  41. package/dist/database/types/migration.types.js +44 -0
  42. package/dist/database/types/migration.types.js.map +1 -0
  43. package/dist/database/types/query.types.d.ts +274 -0
  44. package/dist/database/types/query.types.js +57 -0
  45. package/dist/database/types/query.types.js.map +1 -0
  46. package/dist/database/types/result.types.d.ts +218 -0
  47. package/dist/database/types/result.types.js +6 -0
  48. package/dist/database/types/result.types.js.map +1 -0
  49. package/dist/database/types/schema.types.d.ts +190 -0
  50. package/dist/database/types/schema.types.js +69 -0
  51. package/dist/database/types/schema.types.js.map +1 -0
  52. package/dist/database/utils/helpers.d.ts +66 -0
  53. package/dist/database/utils/helpers.js +501 -0
  54. package/dist/database/utils/helpers.js.map +1 -0
  55. package/dist/database/utils/migration.utils.d.ts +151 -0
  56. package/dist/database/utils/migration.utils.js +476 -0
  57. package/dist/database/utils/migration.utils.js.map +1 -0
  58. package/dist/database/utils/transaction.d.ts +64 -0
  59. package/dist/database/utils/transaction.js +130 -0
  60. package/dist/database/utils/transaction.js.map +1 -0
  61. package/dist/database/validators/connection.validator.d.ts +20 -0
  62. package/dist/database/validators/connection.validator.js +267 -0
  63. package/dist/database/validators/connection.validator.js.map +1 -0
  64. package/dist/database/validators/query.validator.d.ts +31 -0
  65. package/dist/database/validators/query.validator.js +305 -0
  66. package/dist/database/validators/query.validator.js.map +1 -0
  67. package/dist/database/validators/schema.validator.d.ts +31 -0
  68. package/dist/database/validators/schema.validator.js +334 -0
  69. package/dist/database/validators/schema.validator.js.map +1 -0
  70. package/dist/index.d.ts +25 -4
  71. package/dist/index.js +36 -4
  72. package/dist/index.js.map +1 -1
  73. package/dist/processor/services/processor.service.js +10 -8
  74. package/dist/processor/services/processor.service.js.map +1 -1
  75. package/dist/types/processor.types.d.ts +2 -2
  76. package/package.json +3 -1
@@ -0,0 +1,1237 @@
1
+ "use strict";
2
+ /**
3
+ * DynamoDB Database Adapter
4
+ * Implements database operations for Amazon DynamoDB
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.DynamoDBAdapter = void 0;
41
+ const base_adapter_1 = require("./base.adapter");
42
+ const database_types_1 = require("../types/database.types");
43
+ const schema_types_1 = require("../types/schema.types");
44
+ const migration_types_1 = require("../types/migration.types");
45
+ /**
46
+ * DynamoDB Connection wrapper
47
+ */
48
+ class DynamoDBConnection {
49
+ constructor(id, client, docClient) {
50
+ this.id = id;
51
+ this.type = database_types_1.DatabaseType.DYNAMODB;
52
+ this.status = database_types_1.ConnectionStatus.CONNECTED;
53
+ this.client = client;
54
+ this.docClient = docClient;
55
+ }
56
+ async connect() {
57
+ this.status = database_types_1.ConnectionStatus.CONNECTED;
58
+ }
59
+ async disconnect() {
60
+ this.client.destroy();
61
+ this.status = database_types_1.ConnectionStatus.DISCONNECTED;
62
+ }
63
+ isConnected() {
64
+ return this.status === database_types_1.ConnectionStatus.CONNECTED;
65
+ }
66
+ getClient() {
67
+ return this.docClient;
68
+ }
69
+ getRawClient() {
70
+ return this.client;
71
+ }
72
+ getType() {
73
+ return this.type;
74
+ }
75
+ getConfig() {
76
+ throw new Error('Config not stored in connection');
77
+ }
78
+ }
79
+ /**
80
+ * DynamoDB Database Adapter
81
+ */
82
+ class DynamoDBAdapter extends base_adapter_1.BaseDatabaseAdapter {
83
+ constructor() {
84
+ super(...arguments);
85
+ this.type = database_types_1.DatabaseType.DYNAMODB;
86
+ }
87
+ // ==================== Connection Methods ====================
88
+ async connect(config) {
89
+ var _a, _b, _c, _d;
90
+ try {
91
+ // Dynamic import of AWS SDK v3
92
+ const { DynamoDBClient } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
93
+ const { DynamoDBDocumentClient } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
94
+ const clientConfig = {
95
+ region: ((_a = config.options) === null || _a === void 0 ? void 0 : _a.region) || 'us-east-1',
96
+ };
97
+ // Add credentials if provided
98
+ if (((_b = config.options) === null || _b === void 0 ? void 0 : _b.accessKeyId) && ((_c = config.options) === null || _c === void 0 ? void 0 : _c.secretAccessKey)) {
99
+ clientConfig.credentials = {
100
+ accessKeyId: config.options.accessKeyId,
101
+ secretAccessKey: config.options.secretAccessKey,
102
+ sessionToken: config.options.sessionToken,
103
+ };
104
+ }
105
+ // Add endpoint for local development
106
+ if ((_d = config.options) === null || _d === void 0 ? void 0 : _d.endpoint) {
107
+ clientConfig.endpoint = config.options.endpoint;
108
+ }
109
+ const client = new DynamoDBClient(clientConfig);
110
+ const docClient = DynamoDBDocumentClient.from(client);
111
+ return new DynamoDBConnection(`dynamodb-${Date.now()}`, client, docClient);
112
+ }
113
+ catch (error) {
114
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.CONNECTION_ERROR, `Failed to connect to DynamoDB: ${error.message}`, error);
115
+ }
116
+ }
117
+ async disconnect(connection) {
118
+ await connection.disconnect();
119
+ }
120
+ async testConnection(connection) {
121
+ const startTime = Date.now();
122
+ try {
123
+ const { ListTablesCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
124
+ const client = connection.getRawClient();
125
+ await client.send(new ListTablesCommand({ Limit: 1 }));
126
+ return {
127
+ connected: true,
128
+ message: 'DynamoDB connection successful',
129
+ databaseType: 'DynamoDB',
130
+ responseTime: Date.now() - startTime,
131
+ };
132
+ }
133
+ catch (error) {
134
+ return {
135
+ connected: false,
136
+ message: `DynamoDB connection failed: ${error.message}`,
137
+ databaseType: 'DynamoDB',
138
+ responseTime: Date.now() - startTime,
139
+ error: error.message,
140
+ };
141
+ }
142
+ }
143
+ // ==================== Transaction Methods ====================
144
+ async beginTransaction(connection, options) {
145
+ // DynamoDB transactions are handled differently - store items for batch execution
146
+ return {
147
+ items: [],
148
+ connection,
149
+ };
150
+ }
151
+ async commitTransaction(connection, transaction) {
152
+ // Execute transactional write
153
+ if (transaction.items.length === 0) {
154
+ return;
155
+ }
156
+ const { TransactWriteCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
157
+ const docClient = connection.getClient();
158
+ await docClient.send(new TransactWriteCommand({
159
+ TransactItems: transaction.items,
160
+ }));
161
+ }
162
+ async rollbackTransaction(connection, transaction) {
163
+ // DynamoDB transactions are atomic - no explicit rollback needed
164
+ // Just clear the items
165
+ transaction.items = [];
166
+ }
167
+ async createSavepoint(connection, transaction, savepointName) {
168
+ // DynamoDB doesn't support savepoints
169
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, 'DynamoDB does not support savepoints');
170
+ }
171
+ async rollbackToSavepoint(connection, transaction, savepointName) {
172
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, 'DynamoDB does not support savepoints');
173
+ }
174
+ async releaseSavepoint(connection, transaction, savepointName) {
175
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, 'DynamoDB does not support savepoints');
176
+ }
177
+ // ==================== Query Methods ====================
178
+ async query(connection, options) {
179
+ const startTime = Date.now();
180
+ try {
181
+ const { ScanCommand, QueryCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
182
+ const docClient = connection.getClient();
183
+ let result;
184
+ // If there's a partition key in where clause, use Query, otherwise use Scan
185
+ const hasPartitionKey = options.where && this.hasPartitionKeyInWhere(options.where);
186
+ if (hasPartitionKey) {
187
+ const params = this.buildQueryParams(options);
188
+ result = await docClient.send(new QueryCommand(params));
189
+ }
190
+ else {
191
+ const params = this.buildScanParams(options);
192
+ result = await docClient.send(new ScanCommand(params));
193
+ }
194
+ const executionTime = Date.now() - startTime;
195
+ return {
196
+ data: (result.Items || []),
197
+ count: result.Count || 0,
198
+ executionTime,
199
+ };
200
+ }
201
+ catch (error) {
202
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB query failed: ${error.message}`, error);
203
+ }
204
+ }
205
+ async insert(connection, options) {
206
+ const startTime = Date.now();
207
+ try {
208
+ const { PutCommand, BatchWriteCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
209
+ const docClient = connection.getClient();
210
+ const dataArray = Array.isArray(options.data) ? options.data : [options.data];
211
+ if (dataArray.length === 1) {
212
+ // Single item insert
213
+ await docClient.send(new PutCommand({
214
+ TableName: options.table,
215
+ Item: dataArray[0],
216
+ }));
217
+ }
218
+ else {
219
+ // Batch insert (max 25 items per batch)
220
+ const batches = this.chunkArray(dataArray, 25);
221
+ for (const batch of batches) {
222
+ await docClient.send(new BatchWriteCommand({
223
+ RequestItems: {
224
+ [options.table]: batch.map((item) => ({
225
+ PutRequest: { Item: item },
226
+ })),
227
+ },
228
+ }));
229
+ }
230
+ }
231
+ const executionTime = Date.now() - startTime;
232
+ return {
233
+ insertedCount: dataArray.length,
234
+ insertedIds: [], // DynamoDB doesn't auto-generate IDs
235
+ data: undefined,
236
+ executionTime,
237
+ success: true,
238
+ };
239
+ }
240
+ catch (error) {
241
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB insert failed: ${error.message}`, error);
242
+ }
243
+ }
244
+ async update(connection, options) {
245
+ const startTime = Date.now();
246
+ try {
247
+ const { UpdateCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
248
+ const docClient = connection.getClient();
249
+ // DynamoDB requires a key to update - extract from where clause
250
+ const key = this.extractKeyFromWhere(options.where);
251
+ // Build update expression
252
+ const { updateExpression, expressionAttributeNames, expressionAttributeValues } = this.buildUpdateExpression(options.data);
253
+ await docClient.send(new UpdateCommand({
254
+ TableName: options.table,
255
+ Key: key,
256
+ UpdateExpression: updateExpression,
257
+ ExpressionAttributeNames: expressionAttributeNames,
258
+ ExpressionAttributeValues: expressionAttributeValues,
259
+ }));
260
+ const executionTime = Date.now() - startTime;
261
+ return {
262
+ updatedCount: 1, // DynamoDB updates one item at a time
263
+ matchedCount: 1,
264
+ data: undefined,
265
+ executionTime,
266
+ success: true,
267
+ };
268
+ }
269
+ catch (error) {
270
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB update failed: ${error.message}`, error);
271
+ }
272
+ }
273
+ async delete(connection, options) {
274
+ const startTime = Date.now();
275
+ try {
276
+ const { DeleteCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
277
+ const docClient = connection.getClient();
278
+ // DynamoDB requires a key to delete - extract from where clause
279
+ const key = this.extractKeyFromWhere(options.where);
280
+ await docClient.send(new DeleteCommand({
281
+ TableName: options.table,
282
+ Key: key,
283
+ }));
284
+ const executionTime = Date.now() - startTime;
285
+ return {
286
+ deletedCount: 1,
287
+ data: undefined,
288
+ executionTime,
289
+ success: true,
290
+ };
291
+ }
292
+ catch (error) {
293
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB delete failed: ${error.message}`, error);
294
+ }
295
+ }
296
+ async upsert(connection, options) {
297
+ const startTime = Date.now();
298
+ try {
299
+ const { PutCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
300
+ const docClient = connection.getClient();
301
+ const data = Array.isArray(options.data) ? options.data[0] : options.data;
302
+ // PutItem automatically does upsert in DynamoDB
303
+ await docClient.send(new PutCommand({
304
+ TableName: options.table,
305
+ Item: data,
306
+ }));
307
+ const executionTime = Date.now() - startTime;
308
+ return {
309
+ insertedCount: 0, // Can't distinguish in DynamoDB
310
+ updatedCount: 0,
311
+ affectedCount: 1,
312
+ data: undefined,
313
+ executionTime,
314
+ success: true,
315
+ };
316
+ }
317
+ catch (error) {
318
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB upsert failed: ${error.message}`, error);
319
+ }
320
+ }
321
+ async executeRaw(connection, options) {
322
+ var _a;
323
+ const startTime = Date.now();
324
+ try {
325
+ const { ExecuteStatementCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
326
+ const client = connection.getRawClient();
327
+ // DynamoDB PartiQL support
328
+ const result = await client.send(new ExecuteStatementCommand({
329
+ Statement: options.query,
330
+ Parameters: options.params,
331
+ }));
332
+ const executionTime = Date.now() - startTime;
333
+ return {
334
+ rows: (result.Items || []),
335
+ rowCount: ((_a = result.Items) === null || _a === void 0 ? void 0 : _a.length) || 0,
336
+ executionTime,
337
+ };
338
+ }
339
+ catch (error) {
340
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB raw query failed: ${error.message}`, error);
341
+ }
342
+ }
343
+ // ==================== Aggregation Methods ====================
344
+ async count(connection, options) {
345
+ try {
346
+ const { ScanCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
347
+ const docClient = connection.getClient();
348
+ const params = {
349
+ TableName: options.table,
350
+ Select: 'COUNT',
351
+ };
352
+ if (options.where) {
353
+ const { filterExpression, expressionAttributeNames, expressionAttributeValues } = this.buildFilterExpression(options.where);
354
+ params.FilterExpression = filterExpression;
355
+ params.ExpressionAttributeNames = expressionAttributeNames;
356
+ params.ExpressionAttributeValues = expressionAttributeValues;
357
+ }
358
+ const result = await docClient.send(new ScanCommand(params));
359
+ return result.Count || 0;
360
+ }
361
+ catch (error) {
362
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB count failed: ${error.message}`, error);
363
+ }
364
+ }
365
+ async sum(connection, options) {
366
+ // DynamoDB doesn't support aggregations natively - need to scan and calculate
367
+ try {
368
+ const result = await this.query(connection, {
369
+ table: options.table,
370
+ where: options.where,
371
+ env: options.env,
372
+ product: options.product,
373
+ database: options.database,
374
+ });
375
+ return result.data.reduce((sum, item) => sum + (item[options.column] || 0), 0);
376
+ }
377
+ catch (error) {
378
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB sum failed: ${error.message}`, error);
379
+ }
380
+ }
381
+ async avg(connection, options) {
382
+ try {
383
+ const result = await this.query(connection, {
384
+ table: options.table,
385
+ where: options.where,
386
+ env: options.env,
387
+ product: options.product,
388
+ database: options.database,
389
+ });
390
+ const sum = result.data.reduce((total, item) => total + (item[options.column] || 0), 0);
391
+ return result.data.length > 0 ? sum / result.data.length : 0;
392
+ }
393
+ catch (error) {
394
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB avg failed: ${error.message}`, error);
395
+ }
396
+ }
397
+ async min(connection, options) {
398
+ try {
399
+ const result = await this.query(connection, {
400
+ table: options.table,
401
+ where: options.where,
402
+ env: options.env,
403
+ product: options.product,
404
+ database: options.database,
405
+ });
406
+ if (result.data.length === 0)
407
+ return 0;
408
+ return result.data.reduce((min, item) => {
409
+ const value = item[options.column];
410
+ return value < min ? value : min;
411
+ }, result.data[0][options.column]);
412
+ }
413
+ catch (error) {
414
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB min failed: ${error.message}`, error);
415
+ }
416
+ }
417
+ async max(connection, options) {
418
+ try {
419
+ const result = await this.query(connection, {
420
+ table: options.table,
421
+ where: options.where,
422
+ env: options.env,
423
+ product: options.product,
424
+ database: options.database,
425
+ });
426
+ if (result.data.length === 0)
427
+ return 0;
428
+ return result.data.reduce((max, item) => {
429
+ const value = item[options.column];
430
+ return value > max ? value : max;
431
+ }, result.data[0][options.column]);
432
+ }
433
+ catch (error) {
434
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB max failed: ${error.message}`, error);
435
+ }
436
+ }
437
+ async groupBy(connection, options) {
438
+ try {
439
+ // DynamoDB doesn't support GROUP BY - need to scan and group manually
440
+ const result = await this.query(connection, {
441
+ table: options.table,
442
+ where: options.where,
443
+ env: options.env,
444
+ product: options.product,
445
+ database: options.database,
446
+ });
447
+ const groups = new Map();
448
+ result.data.forEach((item) => {
449
+ // Create group key
450
+ const groupKey = options.groupBy.map((col) => item[col]).join('|');
451
+ if (!groups.has(groupKey)) {
452
+ const groupData = {};
453
+ options.groupBy.forEach((col) => {
454
+ groupData[col] = item[col];
455
+ });
456
+ groups.set(groupKey, groupData);
457
+ }
458
+ // Accumulate aggregations
459
+ if (options.aggregate) {
460
+ const groupData = groups.get(groupKey);
461
+ Object.entries(options.aggregate).forEach(([alias, agg]) => {
462
+ const value = item[agg.column];
463
+ if (!groupData[alias]) {
464
+ groupData[alias] = agg.function === 'count' ? 0 : 0;
465
+ }
466
+ switch (agg.function.toLowerCase()) {
467
+ case 'count':
468
+ groupData[alias]++;
469
+ break;
470
+ case 'sum':
471
+ groupData[alias] += value || 0;
472
+ break;
473
+ case 'avg':
474
+ if (!groupData[`${alias}_sum`])
475
+ groupData[`${alias}_sum`] = 0;
476
+ if (!groupData[`${alias}_count`])
477
+ groupData[`${alias}_count`] = 0;
478
+ groupData[`${alias}_sum`] += value || 0;
479
+ groupData[`${alias}_count`]++;
480
+ groupData[alias] = groupData[`${alias}_sum`] / groupData[`${alias}_count`];
481
+ break;
482
+ }
483
+ });
484
+ }
485
+ });
486
+ return Array.from(groups.values());
487
+ }
488
+ catch (error) {
489
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB groupBy failed: ${error.message}`, error);
490
+ }
491
+ }
492
+ async aggregate(connection, options) {
493
+ try {
494
+ const result = await this.query(connection, {
495
+ table: options.table,
496
+ where: options.where,
497
+ env: options.env,
498
+ product: options.product,
499
+ database: options.database,
500
+ });
501
+ const aggregateResult = {};
502
+ Object.entries(options.operations).forEach(([alias, agg]) => {
503
+ const values = result.data.map((item) => item[agg.column]);
504
+ switch (agg.function.toLowerCase()) {
505
+ case 'count':
506
+ aggregateResult[alias] = result.data.length;
507
+ break;
508
+ case 'sum':
509
+ aggregateResult[alias] = values.reduce((sum, val) => sum + (val || 0), 0);
510
+ break;
511
+ case 'avg':
512
+ const sum = values.reduce((s, val) => s + (val || 0), 0);
513
+ aggregateResult[alias] = values.length > 0 ? sum / values.length : 0;
514
+ break;
515
+ case 'min':
516
+ aggregateResult[alias] = Math.min(...values.filter((v) => v !== undefined));
517
+ break;
518
+ case 'max':
519
+ aggregateResult[alias] = Math.max(...values.filter((v) => v !== undefined));
520
+ break;
521
+ }
522
+ });
523
+ return aggregateResult;
524
+ }
525
+ catch (error) {
526
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB aggregate failed: ${error.message}`, error);
527
+ }
528
+ }
529
+ // ==================== Schema Methods ====================
530
+ async createTable(connection, schema, options) {
531
+ const startTime = Date.now();
532
+ try {
533
+ const { CreateTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
534
+ const client = connection.getRawClient();
535
+ // Extract key schema from columns
536
+ const keySchema = [];
537
+ const attributeDefinitions = [];
538
+ schema.columns.forEach((col) => {
539
+ if (col.primaryKey) {
540
+ keySchema.push({
541
+ AttributeName: col.name,
542
+ KeyType: 'HASH', // Partition key
543
+ });
544
+ attributeDefinitions.push({
545
+ AttributeName: col.name,
546
+ AttributeType: this.mapColumnTypeToDynamoDB(col.type),
547
+ });
548
+ }
549
+ });
550
+ // If no sort key specified, DynamoDB only needs partition key
551
+ await client.send(new CreateTableCommand({
552
+ TableName: schema.name,
553
+ KeySchema: keySchema,
554
+ AttributeDefinitions: attributeDefinitions,
555
+ BillingMode: 'PAY_PER_REQUEST', // On-demand billing
556
+ }));
557
+ const executionTime = Date.now() - startTime;
558
+ return {
559
+ success: true,
560
+ operation: 'create',
561
+ table: schema.name,
562
+ executionTime,
563
+ };
564
+ }
565
+ catch (error) {
566
+ return {
567
+ success: false,
568
+ operation: 'create',
569
+ table: schema.name,
570
+ error: error.message,
571
+ executionTime: Date.now() - startTime,
572
+ };
573
+ }
574
+ }
575
+ async dropTable(connection, tableName) {
576
+ const startTime = Date.now();
577
+ try {
578
+ const { DeleteTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
579
+ const client = connection.getRawClient();
580
+ await client.send(new DeleteTableCommand({
581
+ TableName: tableName,
582
+ }));
583
+ return {
584
+ success: true,
585
+ operation: 'drop',
586
+ table: tableName,
587
+ executionTime: Date.now() - startTime,
588
+ };
589
+ }
590
+ catch (error) {
591
+ return {
592
+ success: false,
593
+ operation: 'drop',
594
+ table: tableName,
595
+ error: error.message,
596
+ executionTime: Date.now() - startTime,
597
+ };
598
+ }
599
+ }
600
+ async alterTable(connection, tableName, alterations, options) {
601
+ // DynamoDB doesn't support schema alterations in the traditional sense
602
+ // Can only modify throughput, streams, TTL, etc.
603
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.SCHEMA_ERROR, 'DynamoDB does not support traditional table alterations. You can only modify table settings.');
604
+ }
605
+ async getTableSchema(connection, tableName) {
606
+ try {
607
+ const { DescribeTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
608
+ const client = connection.getRawClient();
609
+ const result = await client.send(new DescribeTableCommand({
610
+ TableName: tableName,
611
+ }));
612
+ const table = result.Table;
613
+ const columns = table.KeySchema.map((key) => {
614
+ const attr = table.AttributeDefinitions.find((a) => a.AttributeName === key.AttributeName);
615
+ return {
616
+ name: key.AttributeName,
617
+ type: this.mapDynamoDBTypeToColumnType((attr === null || attr === void 0 ? void 0 : attr.AttributeType) || 'S'),
618
+ primaryKey: key.KeyType === 'HASH',
619
+ nullable: false,
620
+ };
621
+ });
622
+ return {
623
+ name: tableName,
624
+ columns,
625
+ };
626
+ }
627
+ catch (error) {
628
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.SCHEMA_ERROR, `Failed to get DynamoDB table schema: ${error.message}`, error);
629
+ }
630
+ }
631
+ async listTables(connection) {
632
+ try {
633
+ const { ListTablesCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
634
+ const client = connection.getRawClient();
635
+ const result = await client.send(new ListTablesCommand({}));
636
+ return result.TableNames || [];
637
+ }
638
+ catch (error) {
639
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.SCHEMA_ERROR, `Failed to list DynamoDB tables: ${error.message}`, error);
640
+ }
641
+ }
642
+ async tableExists(connection, tableName) {
643
+ try {
644
+ const { DescribeTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
645
+ const client = connection.getRawClient();
646
+ await client.send(new DescribeTableCommand({
647
+ TableName: tableName,
648
+ }));
649
+ return true;
650
+ }
651
+ catch (error) {
652
+ if (error.name === 'ResourceNotFoundException') {
653
+ return false;
654
+ }
655
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.SCHEMA_ERROR, `Failed to check DynamoDB table existence: ${error.message}`, error);
656
+ }
657
+ }
658
+ // ==================== Index Methods ====================
659
+ async createIndex(connection, options) {
660
+ const startTime = Date.now();
661
+ try {
662
+ const { UpdateTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
663
+ const client = connection.getRawClient();
664
+ // Build GSI definition
665
+ const keySchema = options.index.columns.map((col, idx) => ({
666
+ AttributeName: col.name,
667
+ KeyType: idx === 0 ? 'HASH' : 'RANGE',
668
+ }));
669
+ const attributeDefinitions = options.index.columns.map((col) => ({
670
+ AttributeName: col.name,
671
+ AttributeType: 'S', // Default to String
672
+ }));
673
+ await client.send(new UpdateTableCommand({
674
+ TableName: options.table,
675
+ AttributeDefinitions: attributeDefinitions,
676
+ GlobalSecondaryIndexUpdates: [
677
+ {
678
+ Create: {
679
+ IndexName: options.index.name,
680
+ KeySchema: keySchema,
681
+ Projection: {
682
+ ProjectionType: 'ALL',
683
+ },
684
+ },
685
+ },
686
+ ],
687
+ }));
688
+ return {
689
+ success: true,
690
+ operation: 'create',
691
+ indexName: options.index.name,
692
+ table: options.table,
693
+ executionTime: Date.now() - startTime,
694
+ };
695
+ }
696
+ catch (error) {
697
+ return {
698
+ success: false,
699
+ operation: 'create',
700
+ indexName: options.index.name,
701
+ table: options.table,
702
+ error: error.message,
703
+ executionTime: Date.now() - startTime,
704
+ };
705
+ }
706
+ }
707
+ async dropIndex(connection, options) {
708
+ const startTime = Date.now();
709
+ try {
710
+ const { UpdateTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
711
+ const client = connection.getRawClient();
712
+ await client.send(new UpdateTableCommand({
713
+ TableName: options.table,
714
+ GlobalSecondaryIndexUpdates: [
715
+ {
716
+ Delete: {
717
+ IndexName: options.indexName,
718
+ },
719
+ },
720
+ ],
721
+ }));
722
+ return {
723
+ success: true,
724
+ operation: 'drop',
725
+ indexName: options.indexName,
726
+ table: options.table,
727
+ executionTime: Date.now() - startTime,
728
+ };
729
+ }
730
+ catch (error) {
731
+ return {
732
+ success: false,
733
+ operation: 'drop',
734
+ indexName: options.indexName,
735
+ table: options.table,
736
+ error: error.message,
737
+ executionTime: Date.now() - startTime,
738
+ };
739
+ }
740
+ }
741
+ async listIndexes(connection, options) {
742
+ var _a, _b;
743
+ try {
744
+ const { DescribeTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
745
+ const client = connection.getRawClient();
746
+ const result = await client.send(new DescribeTableCommand({
747
+ TableName: options.table,
748
+ }));
749
+ const indexes = [];
750
+ // Add GSIs
751
+ if ((_a = result.Table) === null || _a === void 0 ? void 0 : _a.GlobalSecondaryIndexes) {
752
+ result.Table.GlobalSecondaryIndexes.forEach((gsi) => {
753
+ indexes.push({
754
+ name: gsi.IndexName,
755
+ table: options.table,
756
+ columns: gsi.KeySchema.map((key) => key.AttributeName),
757
+ columnDetails: gsi.KeySchema.map((key) => ({
758
+ name: key.AttributeName,
759
+ order: 'ASC',
760
+ })),
761
+ unique: false,
762
+ primaryKey: false,
763
+ type: 'GSI',
764
+ });
765
+ });
766
+ }
767
+ // Add LSIs
768
+ if ((_b = result.Table) === null || _b === void 0 ? void 0 : _b.LocalSecondaryIndexes) {
769
+ result.Table.LocalSecondaryIndexes.forEach((lsi) => {
770
+ indexes.push({
771
+ name: lsi.IndexName,
772
+ table: options.table,
773
+ columns: lsi.KeySchema.map((key) => key.AttributeName),
774
+ columnDetails: lsi.KeySchema.map((key) => ({
775
+ name: key.AttributeName,
776
+ order: 'ASC',
777
+ })),
778
+ unique: false,
779
+ primaryKey: false,
780
+ type: 'LSI',
781
+ });
782
+ });
783
+ }
784
+ return indexes;
785
+ }
786
+ catch (error) {
787
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.INDEX_ERROR, `Failed to list DynamoDB indexes: ${error.message}`, error);
788
+ }
789
+ }
790
+ async getIndexStatistics(connection, tableName, indexName) {
791
+ var _a;
792
+ try {
793
+ const { DescribeTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
794
+ const client = connection.getRawClient();
795
+ const result = await client.send(new DescribeTableCommand({
796
+ TableName: tableName,
797
+ }));
798
+ const statistics = [];
799
+ if ((_a = result.Table) === null || _a === void 0 ? void 0 : _a.GlobalSecondaryIndexes) {
800
+ result.Table.GlobalSecondaryIndexes.forEach((gsi) => {
801
+ if (!indexName || gsi.IndexName === indexName) {
802
+ statistics.push({
803
+ indexName: gsi.IndexName,
804
+ table: tableName,
805
+ size: gsi.IndexSizeBytes || 0,
806
+ tuplesRead: gsi.ItemCount || 0,
807
+ });
808
+ }
809
+ });
810
+ }
811
+ return statistics;
812
+ }
813
+ catch (error) {
814
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.INDEX_ERROR, `Failed to get DynamoDB index statistics: ${error.message}`, error);
815
+ }
816
+ }
817
+ // ==================== Migration Methods ====================
818
+ /**
819
+ * Execute a migration operation
820
+ * Note: DynamoDB is schema-less, so column operations are skipped
821
+ */
822
+ async executeMigrationOperation(connection, operation) {
823
+ var _a, _b, _c, _d;
824
+ const { CreateTableCommand, DeleteTableCommand, UpdateTableCommand, ExecuteStatementCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
825
+ const client = connection.getRawClient();
826
+ switch (operation.type) {
827
+ case 'create_table':
828
+ if ((_a = operation.params) === null || _a === void 0 ? void 0 : _a.schema) {
829
+ const schema = operation.params.schema;
830
+ const keySchema = [];
831
+ const attributeDefinitions = [];
832
+ // Extract partition and sort keys from schema
833
+ schema.columns.forEach((col) => {
834
+ if (col.primaryKey) {
835
+ keySchema.push({
836
+ AttributeName: col.name,
837
+ KeyType: 'HASH',
838
+ });
839
+ attributeDefinitions.push({
840
+ AttributeName: col.name,
841
+ AttributeType: this.mapColumnTypeToDynamoDB(col.type),
842
+ });
843
+ }
844
+ });
845
+ await client.send(new CreateTableCommand({
846
+ TableName: schema.name,
847
+ KeySchema: keySchema,
848
+ AttributeDefinitions: attributeDefinitions,
849
+ BillingMode: 'PAY_PER_REQUEST',
850
+ }));
851
+ return { executed: true, command: `CreateTable: ${schema.name}` };
852
+ }
853
+ return { executed: false };
854
+ case 'drop_table':
855
+ if (operation.table) {
856
+ await client.send(new DeleteTableCommand({
857
+ TableName: operation.table,
858
+ }));
859
+ return { executed: true, command: `DropTable: ${operation.table}` };
860
+ }
861
+ return { executed: false };
862
+ case 'add_index':
863
+ if (operation.table && ((_b = operation.params) === null || _b === void 0 ? void 0 : _b.indexName) && ((_c = operation.params) === null || _c === void 0 ? void 0 : _c.columns)) {
864
+ const keySchema = operation.params.columns.map((col, idx) => ({
865
+ AttributeName: col,
866
+ KeyType: idx === 0 ? 'HASH' : 'RANGE',
867
+ }));
868
+ const attributeDefinitions = operation.params.columns.map((col) => ({
869
+ AttributeName: col,
870
+ AttributeType: 'S', // Default to String
871
+ }));
872
+ await client.send(new UpdateTableCommand({
873
+ TableName: operation.table,
874
+ AttributeDefinitions: attributeDefinitions,
875
+ GlobalSecondaryIndexUpdates: [
876
+ {
877
+ Create: {
878
+ IndexName: operation.params.indexName,
879
+ KeySchema: keySchema,
880
+ Projection: {
881
+ ProjectionType: 'ALL',
882
+ },
883
+ },
884
+ },
885
+ ],
886
+ }));
887
+ return { executed: true, command: `AddIndex: ${operation.params.indexName}` };
888
+ }
889
+ return { executed: false };
890
+ case 'drop_index':
891
+ if (operation.table && ((_d = operation.params) === null || _d === void 0 ? void 0 : _d.indexName)) {
892
+ await client.send(new UpdateTableCommand({
893
+ TableName: operation.table,
894
+ GlobalSecondaryIndexUpdates: [
895
+ {
896
+ Delete: {
897
+ IndexName: operation.params.indexName,
898
+ },
899
+ },
900
+ ],
901
+ }));
902
+ return { executed: true, command: `DropIndex: ${operation.params.indexName}` };
903
+ }
904
+ return { executed: false };
905
+ case 'raw_sql':
906
+ if (operation.sql) {
907
+ // Use PartiQL for raw queries
908
+ await client.send(new ExecuteStatementCommand({
909
+ Statement: operation.sql,
910
+ }));
911
+ return { executed: true, command: `PartiQL: ${operation.sql.substring(0, 50)}...` };
912
+ }
913
+ return { executed: false };
914
+ // Schema-less operations - skip silently for DynamoDB
915
+ case 'add_column':
916
+ case 'drop_column':
917
+ case 'modify_column':
918
+ case 'rename_column':
919
+ case 'alter_table':
920
+ return { executed: false, command: `Skipped (schema-less): ${operation.type}` };
921
+ // Constraint operations - not applicable to DynamoDB
922
+ case 'add_constraint':
923
+ case 'drop_constraint':
924
+ return { executed: false, command: `Skipped (no constraints): ${operation.type}` };
925
+ default:
926
+ return { executed: false };
927
+ }
928
+ }
929
+ async runMigration(connection, migration, options) {
930
+ const startTime = Date.now();
931
+ const statements = [];
932
+ try {
933
+ // DynamoDB doesn't support transactions for schema changes
934
+ // Execute operations sequentially
935
+ for (const operation of migration.up) {
936
+ const result = await this.executeMigrationOperation(connection, operation);
937
+ if (result.command) {
938
+ statements.push(result.command);
939
+ }
940
+ }
941
+ // Record migration in history table
942
+ await this.recordMigration(connection, migration);
943
+ return {
944
+ tag: migration.tag,
945
+ status: migration_types_1.MigrationStatus.COMPLETED,
946
+ direction: migration_types_1.MigrationDirection.UP,
947
+ executedAt: new Date(),
948
+ duration: Date.now() - startTime,
949
+ statements,
950
+ };
951
+ }
952
+ catch (error) {
953
+ return {
954
+ tag: migration.tag,
955
+ status: migration_types_1.MigrationStatus.FAILED,
956
+ direction: migration_types_1.MigrationDirection.UP,
957
+ executedAt: new Date(),
958
+ duration: Date.now() - startTime,
959
+ error: error.message,
960
+ statements,
961
+ };
962
+ }
963
+ }
964
+ async rollbackMigration(connection, migration, options) {
965
+ const startTime = Date.now();
966
+ const statements = [];
967
+ try {
968
+ // Execute rollback operations
969
+ for (const operation of migration.down) {
970
+ const result = await this.executeMigrationOperation(connection, operation);
971
+ if (result.command) {
972
+ statements.push(result.command);
973
+ }
974
+ }
975
+ // Remove migration from history table
976
+ await this.removeMigration(connection, migration.tag);
977
+ return {
978
+ tag: migration.tag,
979
+ status: migration_types_1.MigrationStatus.ROLLED_BACK,
980
+ direction: migration_types_1.MigrationDirection.DOWN,
981
+ executedAt: new Date(),
982
+ duration: Date.now() - startTime,
983
+ statements,
984
+ };
985
+ }
986
+ catch (error) {
987
+ return {
988
+ tag: migration.tag,
989
+ status: migration_types_1.MigrationStatus.FAILED,
990
+ direction: migration_types_1.MigrationDirection.DOWN,
991
+ executedAt: new Date(),
992
+ duration: Date.now() - startTime,
993
+ error: error.message,
994
+ statements,
995
+ };
996
+ }
997
+ }
998
+ async getMigrationHistory(connection, options) {
999
+ try {
1000
+ const { ScanCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
1001
+ const docClient = connection.getClient();
1002
+ // Ensure migration table exists
1003
+ await this.ensureMigrationTable(connection);
1004
+ const result = await docClient.send(new ScanCommand({
1005
+ TableName: '_ductape_migrations',
1006
+ }));
1007
+ return (result.Items || []).map((item) => ({
1008
+ tag: item.tag,
1009
+ name: item.name,
1010
+ env: options.env,
1011
+ product: options.product,
1012
+ database: options.database,
1013
+ status: migration_types_1.MigrationStatus.COMPLETED,
1014
+ direction: migration_types_1.MigrationDirection.UP,
1015
+ appliedAt: new Date(item.executed_at),
1016
+ }));
1017
+ }
1018
+ catch (error) {
1019
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.MIGRATION_ERROR, `Failed to get DynamoDB migration history: ${error.message}`, error);
1020
+ }
1021
+ }
1022
+ /**
1023
+ * Ensure migration tracking table exists
1024
+ */
1025
+ async ensureMigrationTable(connection) {
1026
+ try {
1027
+ const { CreateTableCommand, DescribeTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
1028
+ const client = connection.getRawClient();
1029
+ // Check if table exists
1030
+ try {
1031
+ await client.send(new DescribeTableCommand({
1032
+ TableName: '_ductape_migrations',
1033
+ }));
1034
+ return; // Table exists
1035
+ }
1036
+ catch (error) {
1037
+ if (error.name !== 'ResourceNotFoundException') {
1038
+ throw error;
1039
+ }
1040
+ }
1041
+ // Create migration table
1042
+ await client.send(new CreateTableCommand({
1043
+ TableName: '_ductape_migrations',
1044
+ KeySchema: [
1045
+ {
1046
+ AttributeName: 'tag',
1047
+ KeyType: 'HASH',
1048
+ },
1049
+ ],
1050
+ AttributeDefinitions: [
1051
+ {
1052
+ AttributeName: 'tag',
1053
+ AttributeType: 'S',
1054
+ },
1055
+ ],
1056
+ BillingMode: 'PAY_PER_REQUEST',
1057
+ }));
1058
+ // Wait for table to be active
1059
+ await new Promise((resolve) => setTimeout(resolve, 5000));
1060
+ }
1061
+ catch (error) {
1062
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.MIGRATION_ERROR, `Failed to ensure migration table: ${error.message}`, error);
1063
+ }
1064
+ }
1065
+ /**
1066
+ * Record migration execution
1067
+ */
1068
+ async recordMigration(connection, migration) {
1069
+ try {
1070
+ const { PutCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
1071
+ const docClient = connection.getClient();
1072
+ await this.ensureMigrationTable(connection);
1073
+ await docClient.send(new PutCommand({
1074
+ TableName: '_ductape_migrations',
1075
+ Item: {
1076
+ tag: migration.tag,
1077
+ name: migration.name,
1078
+ executed_at: new Date().toISOString(),
1079
+ },
1080
+ }));
1081
+ }
1082
+ catch (error) {
1083
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.MIGRATION_ERROR, `Failed to record migration: ${error.message}`, error);
1084
+ }
1085
+ }
1086
+ /**
1087
+ * Remove migration from history
1088
+ */
1089
+ async removeMigration(connection, tag) {
1090
+ try {
1091
+ const { DeleteCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
1092
+ const docClient = connection.getClient();
1093
+ await docClient.send(new DeleteCommand({
1094
+ TableName: '_ductape_migrations',
1095
+ Key: { tag },
1096
+ }));
1097
+ }
1098
+ catch (error) {
1099
+ throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.MIGRATION_ERROR, `Failed to remove migration: ${error.message}`, error);
1100
+ }
1101
+ }
1102
+ // ==================== Helper Methods ====================
1103
+ hasPartitionKeyInWhere(where) {
1104
+ // Simple check - in production would need table metadata
1105
+ return where && typeof where === 'object' && Object.keys(where).length > 0;
1106
+ }
1107
+ buildQueryParams(options) {
1108
+ const params = {
1109
+ TableName: options.table,
1110
+ };
1111
+ if (options.where) {
1112
+ const { keyConditionExpression, expressionAttributeNames, expressionAttributeValues } = this.buildKeyConditionExpression(options.where);
1113
+ params.KeyConditionExpression = keyConditionExpression;
1114
+ params.ExpressionAttributeNames = expressionAttributeNames;
1115
+ params.ExpressionAttributeValues = expressionAttributeValues;
1116
+ }
1117
+ if (options.limit) {
1118
+ params.Limit = options.limit;
1119
+ }
1120
+ return params;
1121
+ }
1122
+ buildScanParams(options) {
1123
+ const params = {
1124
+ TableName: options.table,
1125
+ };
1126
+ if (options.where) {
1127
+ const { filterExpression, expressionAttributeNames, expressionAttributeValues } = this.buildFilterExpression(options.where);
1128
+ params.FilterExpression = filterExpression;
1129
+ params.ExpressionAttributeNames = expressionAttributeNames;
1130
+ params.ExpressionAttributeValues = expressionAttributeValues;
1131
+ }
1132
+ if (options.limit) {
1133
+ params.Limit = options.limit;
1134
+ }
1135
+ return params;
1136
+ }
1137
+ buildKeyConditionExpression(where) {
1138
+ const names = {};
1139
+ const values = {};
1140
+ const conditions = [];
1141
+ Object.entries(where).forEach(([key, value], idx) => {
1142
+ names[`#${key}`] = key;
1143
+ values[`:val${idx}`] = value;
1144
+ conditions.push(`#${key} = :val${idx}`);
1145
+ });
1146
+ return {
1147
+ keyConditionExpression: conditions.join(' AND '),
1148
+ expressionAttributeNames: names,
1149
+ expressionAttributeValues: values,
1150
+ };
1151
+ }
1152
+ buildFilterExpression(where) {
1153
+ const { keyConditionExpression, expressionAttributeNames, expressionAttributeValues, } = this.buildKeyConditionExpression(where);
1154
+ return {
1155
+ filterExpression: keyConditionExpression,
1156
+ expressionAttributeNames,
1157
+ expressionAttributeValues,
1158
+ };
1159
+ }
1160
+ buildUpdateExpression(data) {
1161
+ const names = {};
1162
+ const values = {};
1163
+ const updates = [];
1164
+ Object.entries(data).forEach(([key, value], idx) => {
1165
+ names[`#${key}`] = key;
1166
+ values[`:val${idx}`] = value;
1167
+ updates.push(`#${key} = :val${idx}`);
1168
+ });
1169
+ return {
1170
+ updateExpression: `SET ${updates.join(', ')}`,
1171
+ expressionAttributeNames: names,
1172
+ expressionAttributeValues: values,
1173
+ };
1174
+ }
1175
+ extractKeyFromWhere(where) {
1176
+ // Simple implementation - extract key fields from where clause
1177
+ return where || {};
1178
+ }
1179
+ chunkArray(array, size) {
1180
+ const chunks = [];
1181
+ for (let i = 0; i < array.length; i += size) {
1182
+ chunks.push(array.slice(i, i + size));
1183
+ }
1184
+ return chunks;
1185
+ }
1186
+ mapColumnTypeToDynamoDB(type) {
1187
+ // DynamoDB only supports S (String), N (Number), and B (Binary) for keys
1188
+ switch (type) {
1189
+ case schema_types_1.ColumnType.INTEGER:
1190
+ case schema_types_1.ColumnType.BIGINT:
1191
+ case schema_types_1.ColumnType.SMALLINT:
1192
+ case schema_types_1.ColumnType.DECIMAL:
1193
+ case schema_types_1.ColumnType.NUMERIC:
1194
+ case schema_types_1.ColumnType.FLOAT:
1195
+ case schema_types_1.ColumnType.DOUBLE:
1196
+ case schema_types_1.ColumnType.REAL:
1197
+ return 'N';
1198
+ case schema_types_1.ColumnType.BLOB:
1199
+ case schema_types_1.ColumnType.BINARY:
1200
+ return 'B';
1201
+ default:
1202
+ return 'S';
1203
+ }
1204
+ }
1205
+ mapDynamoDBTypeToColumnType(dynamoType) {
1206
+ switch (dynamoType) {
1207
+ case 'N':
1208
+ return schema_types_1.ColumnType.DECIMAL;
1209
+ case 'B':
1210
+ return schema_types_1.ColumnType.BINARY;
1211
+ default:
1212
+ return schema_types_1.ColumnType.STRING;
1213
+ }
1214
+ }
1215
+ // ==================== Utility Methods ====================
1216
+ escapeIdentifier(identifier) {
1217
+ // DynamoDB doesn't require escaping identifiers
1218
+ return identifier;
1219
+ }
1220
+ escapeValue(value) {
1221
+ if (value === null || value === undefined) {
1222
+ return 'null';
1223
+ }
1224
+ if (typeof value === 'string') {
1225
+ return `"${value.replace(/"/g, '\\"')}"`;
1226
+ }
1227
+ if (typeof value === 'object') {
1228
+ return JSON.stringify(value);
1229
+ }
1230
+ return String(value);
1231
+ }
1232
+ async getDatabaseVersion(connection) {
1233
+ return 'DynamoDB (AWS Managed)';
1234
+ }
1235
+ }
1236
+ exports.DynamoDBAdapter = DynamoDBAdapter;
1237
+ //# sourceMappingURL=dynamodb.adapter.js.map