@gugananuvem/aws-local-simulator 1.0.17 → 1.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gugananuvem/aws-local-simulator",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.19",
|
|
4
4
|
"description": "Simulador local completo para serviços AWS",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -120,6 +120,6 @@
|
|
|
120
120
|
"optional": true
|
|
121
121
|
}
|
|
122
122
|
},
|
|
123
|
-
"buildDate": "2026-04-
|
|
123
|
+
"buildDate": "2026-04-22T01:57:09.515Z",
|
|
124
124
|
"published": true
|
|
125
125
|
}
|
|
@@ -223,7 +223,7 @@ class CognitoSimulator {
|
|
|
223
223
|
Enabled: u.Enabled,
|
|
224
224
|
UserCreateDate: u.CreatedDate,
|
|
225
225
|
UserLastModifiedDate: u.LastModifiedDate,
|
|
226
|
-
Attributes: this.
|
|
226
|
+
Attributes: this._formatUserAttributesWithSub(u),
|
|
227
227
|
})),
|
|
228
228
|
PaginationToken: nextToken,
|
|
229
229
|
};
|
|
@@ -570,9 +570,10 @@ class CognitoSimulator {
|
|
|
570
570
|
if (!this.sessions.has(session.Id)) throw new Error("Token has been revoked");
|
|
571
571
|
const user = this.users.get(session.UserId);
|
|
572
572
|
if (!user) throw new Error("User not found");
|
|
573
|
+
const attributes = this._formatUserAttributesWithSub(user);
|
|
573
574
|
return {
|
|
574
575
|
Username: user.Username,
|
|
575
|
-
UserAttributes:
|
|
576
|
+
UserAttributes: attributes,
|
|
576
577
|
UserStatus: user.UserStatus,
|
|
577
578
|
};
|
|
578
579
|
}
|
|
@@ -1155,7 +1156,7 @@ class CognitoSimulator {
|
|
|
1155
1156
|
|
|
1156
1157
|
return {
|
|
1157
1158
|
Username: user.Username,
|
|
1158
|
-
UserAttributes: this.
|
|
1159
|
+
UserAttributes: this._formatUserAttributesWithSub(user),
|
|
1159
1160
|
UserCreateDate: user.CreatedDate,
|
|
1160
1161
|
UserLastModifiedDate: user.LastModifiedDate,
|
|
1161
1162
|
Enabled: user.Enabled,
|
|
@@ -1214,7 +1215,7 @@ class CognitoSimulator {
|
|
|
1214
1215
|
return {
|
|
1215
1216
|
User: {
|
|
1216
1217
|
Username: user.Username,
|
|
1217
|
-
UserAttributes: this.
|
|
1218
|
+
UserAttributes: this._formatUserAttributesWithSub(user),
|
|
1218
1219
|
UserCreateDate: user.CreatedDate,
|
|
1219
1220
|
UserLastModifiedDate: user.LastModifiedDate,
|
|
1220
1221
|
Enabled: user.Enabled,
|
|
@@ -1321,6 +1322,14 @@ class CognitoSimulator {
|
|
|
1321
1322
|
return Object.entries(attributes).map(([Name, Value]) => ({ Name, Value }));
|
|
1322
1323
|
}
|
|
1323
1324
|
|
|
1325
|
+
_formatUserAttributesWithSub(user) {
|
|
1326
|
+
const attrs = this.formatUserAttributes(user.Attributes);
|
|
1327
|
+
if (!attrs.find(a => a.Name === 'sub')) {
|
|
1328
|
+
attrs.unshift({ Name: 'sub', Value: user.UserId });
|
|
1329
|
+
}
|
|
1330
|
+
return attrs;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1324
1333
|
hashPassword(password) {
|
|
1325
1334
|
// Simulação de hash (não usar em produção real)
|
|
1326
1335
|
return crypto.createHash("sha256").update(password).digest("hex");
|
|
@@ -20,7 +20,10 @@ class DynamoDBServer {
|
|
|
20
20
|
setupMiddlewares() {
|
|
21
21
|
this.app.use(cors());
|
|
22
22
|
this.app.use(express.json({
|
|
23
|
-
type:
|
|
23
|
+
type: (req) => {
|
|
24
|
+
const ct = req.headers['content-type'] || '';
|
|
25
|
+
return ct.includes('application/x-amz-json-1.0') || ct.includes('application/json');
|
|
26
|
+
}
|
|
24
27
|
}));
|
|
25
28
|
|
|
26
29
|
// Logging de requisições
|
|
@@ -53,6 +56,8 @@ class DynamoDBServer {
|
|
|
53
56
|
return res.status(400).json({ message: 'Missing X-Amz-Target header' });
|
|
54
57
|
}
|
|
55
58
|
|
|
59
|
+
logger.debug(`DynamoDB target=${target} content-type=${req.headers['content-type']} body=${JSON.stringify(req.body)}`);
|
|
60
|
+
|
|
56
61
|
try {
|
|
57
62
|
const result = await this.simulator.handleRequest(target, req.body);
|
|
58
63
|
res.json(result);
|
|
@@ -278,13 +278,27 @@ class DynamoDBSimulator {
|
|
|
278
278
|
throw new Error(`Table ${TableName} does not exist`);
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
// Busca o item atual
|
|
281
|
+
// Busca o item atual (upsert: cria se não existir, como o DynamoDB real)
|
|
282
282
|
const items = this.store.read(TableName);
|
|
283
283
|
const itemKey = this.getItemKeyFromKeys(Key, table);
|
|
284
284
|
const index = items.findIndex((item) => this.getItemKey(item, table) === itemKey);
|
|
285
285
|
|
|
286
|
+
// Se não existe, cria um novo item com as chaves fornecidas
|
|
286
287
|
if (index === -1) {
|
|
287
|
-
|
|
288
|
+
const newItem = this.normalizeItem(Key, table);
|
|
289
|
+
newItem._createdAt = new Date().toISOString();
|
|
290
|
+
newItem._updatedAt = new Date().toISOString();
|
|
291
|
+
if (UpdateExpression) {
|
|
292
|
+
this.processUpdateExpression(newItem, UpdateExpression, ExpressionAttributeNames, ExpressionAttributeValues, table);
|
|
293
|
+
}
|
|
294
|
+
items.push(newItem);
|
|
295
|
+
this.store.write(TableName, items);
|
|
296
|
+
logger.verboso(`UpdateItem (upsert): ${TableName}/${itemKey}`);
|
|
297
|
+
const response = {};
|
|
298
|
+
if (ReturnValues === "ALL_NEW" || ReturnValues === "UPDATED_NEW") {
|
|
299
|
+
response.Attributes = this.marshallItem(newItem, table);
|
|
300
|
+
}
|
|
301
|
+
return response;
|
|
288
302
|
}
|
|
289
303
|
|
|
290
304
|
const currentItem = items[index];
|
|
@@ -354,6 +368,11 @@ class DynamoDBSimulator {
|
|
|
354
368
|
const { RequestItems } = params;
|
|
355
369
|
const responses = {};
|
|
356
370
|
|
|
371
|
+
if (!RequestItems) {
|
|
372
|
+
logger.debug(`[DEBUG batchWriteItem] params recebido: ${JSON.stringify(params)}`);
|
|
373
|
+
throw new Error(`RequestItems is required for BatchWriteItem. Params received: ${JSON.stringify(params)}`);
|
|
374
|
+
}
|
|
375
|
+
|
|
357
376
|
for (const [tableName, operations] of Object.entries(RequestItems)) {
|
|
358
377
|
const table = this.tables.get(tableName);
|
|
359
378
|
if (!table) continue;
|
|
@@ -585,21 +604,46 @@ class DynamoDBSimulator {
|
|
|
585
604
|
}
|
|
586
605
|
|
|
587
606
|
processUpdateExpression(item, expression, nameMap, valueMap, table) {
|
|
588
|
-
//
|
|
589
|
-
const setMatch = expression.match(/SET\s+([^]+?)(?=\s+(?:REMOVE|ADD|DELETE)|\s*$)/i);
|
|
590
|
-
|
|
607
|
+
// SET clause
|
|
608
|
+
const setMatch = expression.match(/SET\s+([^]+?)(?=\s+(?:REMOVE|ADD|DELETE)\s|\s*$)/i);
|
|
591
609
|
if (setMatch) {
|
|
592
610
|
const assignments = setMatch[1].split(",").map((a) => a.trim());
|
|
593
|
-
|
|
594
611
|
for (const assignment of assignments) {
|
|
595
612
|
const [path, valueExpr] = assignment.split("=").map((s) => s.trim());
|
|
596
|
-
const attributeName = path.replace(/#/g, "");
|
|
613
|
+
const attributeName = nameMap[path] || path.replace(/#/g, "");
|
|
597
614
|
const rawValue = valueMap[valueExpr];
|
|
598
615
|
const value = rawValue && typeof rawValue === 'object' ? Object.values(rawValue)[0] : rawValue;
|
|
599
|
-
|
|
600
616
|
item[attributeName] = value;
|
|
601
617
|
}
|
|
602
618
|
}
|
|
619
|
+
|
|
620
|
+
// ADD clause — incrementa números ou adiciona a sets (upsert-friendly)
|
|
621
|
+
const addMatch = expression.match(/ADD\s+([^]+?)(?=\s+(?:SET|REMOVE|DELETE)\s|\s*$)/i);
|
|
622
|
+
if (addMatch) {
|
|
623
|
+
const assignments = addMatch[1].split(",").map((a) => a.trim());
|
|
624
|
+
for (const assignment of assignments) {
|
|
625
|
+
const parts = assignment.split(/\s+/);
|
|
626
|
+
const attributeName = nameMap[parts[0]] || parts[0].replace(/#/g, "");
|
|
627
|
+
const rawValue = valueMap[parts[1]];
|
|
628
|
+
const delta = rawValue && typeof rawValue === 'object' ? Object.values(rawValue)[0] : rawValue;
|
|
629
|
+
const current = item[attributeName];
|
|
630
|
+
if (current === undefined || current === null) {
|
|
631
|
+
item[attributeName] = typeof delta === 'number' ? delta : parseFloat(delta) || 0;
|
|
632
|
+
} else {
|
|
633
|
+
item[attributeName] = (parseFloat(current) || 0) + (parseFloat(delta) || 0);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// REMOVE clause
|
|
639
|
+
const removeMatch = expression.match(/REMOVE\s+([^]+?)(?=\s+(?:SET|ADD|DELETE)\s|\s*$)/i);
|
|
640
|
+
if (removeMatch) {
|
|
641
|
+
const attributes = removeMatch[1].split(",").map((a) => a.trim());
|
|
642
|
+
for (const attr of attributes) {
|
|
643
|
+
const attributeName = nameMap[attr] || attr.replace(/#/g, "");
|
|
644
|
+
delete item[attributeName];
|
|
645
|
+
}
|
|
646
|
+
}
|
|
603
647
|
}
|
|
604
648
|
|
|
605
649
|
applyFilter(items, expression, values, table) {
|