@gugananuvem/aws-local-simulator 1.0.34 → 1.0.35
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.35",
|
|
4
4
|
"description": "Simulador local completo para serviços AWS",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
"start": "node bin/aws-local-simulator.js start"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"better-sqlite3": "^11.0.0",
|
|
14
13
|
"@aws-sdk/client-api-gateway": "^3.0.0",
|
|
15
14
|
"@aws-sdk/client-cognito-identity-provider": "^3.0.0",
|
|
16
15
|
"@aws-sdk/client-dynamodb": "^3.0.0",
|
|
@@ -19,6 +18,7 @@
|
|
|
19
18
|
"@aws-sdk/client-s3": "^3.0.0",
|
|
20
19
|
"@aws-sdk/client-sqs": "^3.0.0",
|
|
21
20
|
"@aws-sdk/lib-dynamodb": "^3.0.0",
|
|
21
|
+
"better-sqlite3": "11.10.0",
|
|
22
22
|
"child_process": "1.0.2",
|
|
23
23
|
"cors": "^2.8.5",
|
|
24
24
|
"crypto": "1.0.1",
|
|
@@ -67,6 +67,6 @@
|
|
|
67
67
|
"publishConfig": {
|
|
68
68
|
"directory": "dist"
|
|
69
69
|
},
|
|
70
|
-
"buildDate": "2026-06-
|
|
70
|
+
"buildDate": "2026-06-26T03:40:01.988Z",
|
|
71
71
|
"published": true
|
|
72
72
|
}
|
|
@@ -146,9 +146,25 @@ class HandlerLoader {
|
|
|
146
146
|
* Recarrega um handler (útil para hot reload)
|
|
147
147
|
*/
|
|
148
148
|
static async reload(handlerPath, type = 'auto') {
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
// Resolve path: try cwd first, then data dir (same logic as load())
|
|
150
|
+
let fullPath = path.resolve(process.cwd(), handlerPath);
|
|
151
|
+
|
|
152
|
+
if (!fs.existsSync(fullPath)) {
|
|
153
|
+
const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR;
|
|
154
|
+
if (dataDir) {
|
|
155
|
+
const dataPath = path.resolve(dataDir, 'lambda', handlerPath.replace(/^\.\//, ''));
|
|
156
|
+
if (fs.existsSync(dataPath)) {
|
|
157
|
+
fullPath = dataPath;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (!fs.existsSync(fullPath)) {
|
|
163
|
+
throw new Error(`Handler não encontrado: ${fullPath}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Limpa cache do require
|
|
167
|
+
delete require.cache[fullPath];
|
|
152
168
|
|
|
153
169
|
// Recarrega
|
|
154
170
|
return this.load(handlerPath, type);
|
|
@@ -158,7 +174,20 @@ class HandlerLoader {
|
|
|
158
174
|
* Valida se um caminho de handler é válido
|
|
159
175
|
*/
|
|
160
176
|
static isValid(handlerPath) {
|
|
161
|
-
|
|
177
|
+
// Tenta resolver de cwd primeiro
|
|
178
|
+
let fullPath = path.resolve(process.cwd(), handlerPath);
|
|
179
|
+
|
|
180
|
+
if (!fs.existsSync(fullPath)) {
|
|
181
|
+
// Tenta resolver do data dir
|
|
182
|
+
const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR;
|
|
183
|
+
if (dataDir) {
|
|
184
|
+
const dataPath = path.resolve(dataDir, 'lambda', handlerPath.replace(/^\.\//, ''));
|
|
185
|
+
if (fs.existsSync(dataPath)) {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
162
191
|
return fs.existsSync(fullPath);
|
|
163
192
|
}
|
|
164
193
|
|
|
@@ -166,7 +195,23 @@ class HandlerLoader {
|
|
|
166
195
|
* Retorna informações sobre o handler
|
|
167
196
|
*/
|
|
168
197
|
static async getInfo(handlerPath) {
|
|
169
|
-
|
|
198
|
+
// Resolve path: try cwd first, then data dir (same logic as load())
|
|
199
|
+
let fullPath = path.resolve(process.cwd(), handlerPath);
|
|
200
|
+
|
|
201
|
+
if (!fs.existsSync(fullPath)) {
|
|
202
|
+
const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR;
|
|
203
|
+
if (dataDir) {
|
|
204
|
+
const dataPath = path.resolve(dataDir, 'lambda', handlerPath.replace(/^\.\//, ''));
|
|
205
|
+
if (fs.existsSync(dataPath)) {
|
|
206
|
+
fullPath = dataPath;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!fs.existsSync(fullPath)) {
|
|
212
|
+
throw new Error(`Handler não encontrado: ${fullPath}`);
|
|
213
|
+
}
|
|
214
|
+
|
|
170
215
|
const type = this.detectType(fullPath);
|
|
171
216
|
const stats = fs.statSync(fullPath);
|
|
172
217
|
|
|
@@ -18,6 +18,7 @@ class LambdaServer {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
setupMiddlewares() {
|
|
21
|
+
this.app.use(express.json()); // Adiciona suporte para JSON
|
|
21
22
|
this.app.use(express.urlencoded({ extended: true }));
|
|
22
23
|
this.app.use(cors());
|
|
23
24
|
|
|
@@ -91,20 +92,13 @@ class LambdaServer {
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
setupAdminRoutes() {
|
|
94
|
-
// Helper: parse Buffer body as JSON for admin routes
|
|
95
|
-
const parseJson = (req, res, next) => {
|
|
96
|
-
if (Buffer.isBuffer(req.body) && req.body.length > 0) {
|
|
97
|
-
try { req.body = JSON.parse(req.body.toString('utf8')); } catch { req.body = {}; }
|
|
98
|
-
}
|
|
99
|
-
next();
|
|
100
|
-
};
|
|
101
|
-
|
|
102
95
|
this.app.get('/__admin/functions', (req, res) => {
|
|
103
96
|
res.json(this.simulator.listLambdas());
|
|
104
97
|
});
|
|
105
98
|
|
|
106
|
-
this.app.post('/__admin/functions',
|
|
99
|
+
this.app.post('/__admin/functions', async (req, res) => {
|
|
107
100
|
try {
|
|
101
|
+
logger.debug(`📥 POST /__admin/functions:`, req.body);
|
|
108
102
|
const lambda = await this.simulator.createFunction(req.body);
|
|
109
103
|
res.status(201).json(lambda);
|
|
110
104
|
} catch (err) {
|
|
@@ -112,8 +106,9 @@ class LambdaServer {
|
|
|
112
106
|
}
|
|
113
107
|
});
|
|
114
108
|
|
|
115
|
-
this.app.put('/__admin/functions/:name',
|
|
109
|
+
this.app.put('/__admin/functions/:name', async (req, res) => {
|
|
116
110
|
try {
|
|
111
|
+
logger.debug(`📥 PUT /__admin/functions/${req.params.name}:`, req.body);
|
|
117
112
|
const lambda = await this.simulator.updateFunction(req.params.name, req.body);
|
|
118
113
|
res.json(lambda);
|
|
119
114
|
} catch (err) {
|
|
@@ -132,7 +127,7 @@ class LambdaServer {
|
|
|
132
127
|
res.json({ message: 'Lambdas recarregadas', count: this.simulator.getLambdasCount() });
|
|
133
128
|
});
|
|
134
129
|
|
|
135
|
-
this.app.post('/__admin/env',
|
|
130
|
+
this.app.post('/__admin/env', (req, res) => {
|
|
136
131
|
const { key, value } = req.body;
|
|
137
132
|
if (key && value !== undefined) {
|
|
138
133
|
this.simulator.setEnvironmentVariable(key, value);
|
|
@@ -48,13 +48,24 @@ class LambdaSimulator {
|
|
|
48
48
|
|
|
49
49
|
async registerLambda(lambdaConfig) {
|
|
50
50
|
try {
|
|
51
|
-
const { name, handler: handlerPath, type = "auto" } = lambdaConfig;
|
|
51
|
+
const { name, handler: handlerPath, type = "auto", runtime = "nodejs18.x" } = lambdaConfig;
|
|
52
52
|
|
|
53
53
|
if (!name) {
|
|
54
54
|
logger.warn(`Lambda sem nome ignorada: ${JSON.stringify(lambdaConfig)}`);
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
// Resolve the handler path to absolute path
|
|
59
|
+
let resolvedHandlerPath = path.resolve(process.cwd(), handlerPath);
|
|
60
|
+
|
|
61
|
+
// Se não existir no cwd, tenta no data dir
|
|
62
|
+
if (!fs.existsSync(resolvedHandlerPath)) {
|
|
63
|
+
const dataPath = path.resolve(this.dataDir, handlerPath.replace(/^\.\//, ''));
|
|
64
|
+
if (fs.existsSync(dataPath)) {
|
|
65
|
+
resolvedHandlerPath = dataPath;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
58
69
|
// Merge: global env as base, lambda-specific env overrides
|
|
59
70
|
const env = {
|
|
60
71
|
...(this.globalEnv || {}),
|
|
@@ -76,8 +87,9 @@ class LambdaSimulator {
|
|
|
76
87
|
if (handler != undefined) {
|
|
77
88
|
this.lambdas.set(name, {
|
|
78
89
|
name,
|
|
90
|
+
runtime,
|
|
79
91
|
handler,
|
|
80
|
-
handlerPath,
|
|
92
|
+
handlerPath: resolvedHandlerPath,
|
|
81
93
|
handlerName: handler.name || "anonymous",
|
|
82
94
|
env,
|
|
83
95
|
timeout,
|
|
@@ -87,7 +99,7 @@ class LambdaSimulator {
|
|
|
87
99
|
registeredAt: new Date().toISOString(),
|
|
88
100
|
});
|
|
89
101
|
}
|
|
90
|
-
logger.debug(`✅ Lambda registrada: ${name} -> ${
|
|
102
|
+
logger.debug(`✅ Lambda registrada: ${name} -> ${resolvedHandlerPath}`);
|
|
91
103
|
} catch (error) {
|
|
92
104
|
if (error.message.indexOf("Handler não encontrado") == -1){
|
|
93
105
|
logger.error(`Erro ao registrar Lambda ${lambdaConfig.name}:`, error);
|
|
@@ -222,6 +234,7 @@ class LambdaSimulator {
|
|
|
222
234
|
|
|
223
235
|
return {
|
|
224
236
|
name: l.name,
|
|
237
|
+
runtime: l.runtime,
|
|
225
238
|
handlerName: l.handlerName,
|
|
226
239
|
handlerPath: l.handlerPath,
|
|
227
240
|
type: l.type,
|
|
@@ -297,25 +310,64 @@ class LambdaSimulator {
|
|
|
297
310
|
const lambda = this.lambdas.get(name);
|
|
298
311
|
if (!lambda) throw new Error(`Function not found: ${name}`);
|
|
299
312
|
|
|
300
|
-
|
|
313
|
+
// Normaliza 'environment' para 'env' se necessário
|
|
314
|
+
const config = {
|
|
315
|
+
...lambdaConfig,
|
|
316
|
+
env: lambdaConfig.environment || lambdaConfig.env
|
|
317
|
+
};
|
|
318
|
+
delete config.environment; // Remove para evitar confusão
|
|
319
|
+
|
|
320
|
+
const { code, runtime, handler, timeout, memorySize, env } = config;
|
|
321
|
+
|
|
322
|
+
logger.debug(`📝 Updating function ${name}:`, {
|
|
323
|
+
hasCode: !!code,
|
|
324
|
+
code: code ? `(${code.length} chars)` : 'NULL/UNDEFINED',
|
|
325
|
+
codeLength: code?.length,
|
|
326
|
+
runtime,
|
|
327
|
+
timeout,
|
|
328
|
+
memorySize,
|
|
329
|
+
handlerPath: lambda.handlerPath,
|
|
330
|
+
configKeys: Object.keys(config)
|
|
331
|
+
});
|
|
301
332
|
|
|
302
333
|
// Se houver código novo, sobrescreve o arquivo
|
|
303
|
-
if (code !== undefined) {
|
|
304
|
-
|
|
334
|
+
if (code !== undefined && code !== null && code.trim() !== '') {
|
|
335
|
+
logger.debug(`💾 Escrevendo código no arquivo: ${lambda.handlerPath}`);
|
|
336
|
+
try {
|
|
337
|
+
fs.writeFileSync(lambda.handlerPath, code, 'utf8');
|
|
338
|
+
logger.debug(`✅ Arquivo atualizado com sucesso`);
|
|
339
|
+
} catch (err) {
|
|
340
|
+
logger.error(`❌ Erro ao escrever arquivo:`, err);
|
|
341
|
+
throw err;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Reload the handler from the updated file
|
|
345
|
+
try {
|
|
346
|
+
const newHandler = await HandlerLoader.reload(lambda.handlerPath, lambda.type);
|
|
347
|
+
lambda.handler = newHandler;
|
|
348
|
+
lambda.handlerName = newHandler.name || "anonymous";
|
|
349
|
+
|
|
350
|
+
// Update code size
|
|
351
|
+
try {
|
|
352
|
+
const info = await HandlerLoader.getInfo(lambda.handlerPath);
|
|
353
|
+
lambda.codeSize = info.size;
|
|
354
|
+
} catch (err) {
|
|
355
|
+
// Ignore size errors
|
|
356
|
+
}
|
|
357
|
+
} catch (error) {
|
|
358
|
+
logger.error(`❌ Erro ao recarregar Lambda ${name}:`, error);
|
|
359
|
+
throw error;
|
|
360
|
+
}
|
|
361
|
+
} else {
|
|
362
|
+
logger.warn(`⚠️ Nenhum código fornecido para atualização. code=${JSON.stringify(code)}`);
|
|
305
363
|
}
|
|
306
364
|
|
|
307
|
-
// Atualiza
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
timeout: timeout || lambda.timeout,
|
|
313
|
-
memorySize: memorySize || lambda.memorySize,
|
|
314
|
-
env: environment || lambda.env,
|
|
315
|
-
type: lambda.type
|
|
316
|
-
};
|
|
365
|
+
// Atualiza outras configurações
|
|
366
|
+
if (runtime !== undefined) lambda.runtime = runtime;
|
|
367
|
+
if (timeout !== undefined) lambda.timeout = timeout;
|
|
368
|
+
if (memorySize !== undefined) lambda.memorySize = memorySize;
|
|
369
|
+
if (env !== undefined) lambda.env = env;
|
|
317
370
|
|
|
318
|
-
await this.registerLambda(updatedConfig);
|
|
319
371
|
this.persistLambdas();
|
|
320
372
|
|
|
321
373
|
return this.lambdas.get(name);
|