@gugananuvem/aws-local-simulator 1.0.34 → 1.0.39

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 (150) hide show
  1. package/README.md +662 -658
  2. package/package.json +11 -4
  3. package/src/config/default-config.js +71 -0
  4. package/src/config/env-loader.js +4 -2
  5. package/src/middleware/authorization.js +271 -0
  6. package/src/middleware/cloudtrail-logger.js +333 -0
  7. package/src/middleware/error-handler.js +138 -0
  8. package/src/middleware/index.js +79 -0
  9. package/src/middleware/policy-enforcement.js +407 -0
  10. package/src/server.js +74 -4
  11. package/src/services/acm/index.js +66 -0
  12. package/src/services/acm/models/certificate.js +202 -0
  13. package/src/services/acm/server.js +224 -0
  14. package/src/services/acm/simulator.js +293 -0
  15. package/src/services/alb/index.js +104 -0
  16. package/src/services/alb/models/loadBalancer.js +88 -0
  17. package/src/services/alb/models/targetGroup.js +128 -0
  18. package/src/services/alb/server.js +160 -0
  19. package/src/services/alb/simulator.js +262 -0
  20. package/src/services/apigateway/index.js +22 -0
  21. package/src/services/apigateway/server.js +247 -35
  22. package/src/services/apigateway/simulator.js +780 -26
  23. package/src/services/asg/index.js +100 -0
  24. package/src/services/asg/models/autoScalingGroup.js +110 -0
  25. package/src/services/asg/models/launchTemplate.js +41 -0
  26. package/src/services/asg/models/scalingPolicy.js +51 -0
  27. package/src/services/asg/server.js +236 -0
  28. package/src/services/asg/simulator.js +410 -0
  29. package/src/services/aurora/cluster-manager.js +731 -0
  30. package/src/services/aurora/data-api.js +514 -0
  31. package/src/services/aurora/db-engine.js +270 -0
  32. package/src/services/aurora/index.js +159 -0
  33. package/src/services/aurora/rds-api.js +189 -0
  34. package/src/services/aurora/server.js +213 -0
  35. package/src/services/aurora/sql-translator.js +334 -0
  36. package/src/services/cloudformation/index.js +2 -2
  37. package/src/services/cloudformation/server.js +1 -0
  38. package/src/services/cloudfront/cache-engine.js +225 -0
  39. package/src/services/cloudfront/geo-restrictions.js +377 -0
  40. package/src/services/cloudfront/index.js +220 -0
  41. package/src/services/cloudfront/models/behavior.js +158 -0
  42. package/src/services/cloudfront/models/distribution.js +284 -0
  43. package/src/services/cloudfront/models/index.js +99 -0
  44. package/src/services/cloudfront/models/invalidation.js +109 -0
  45. package/src/services/cloudfront/models/origin.js +155 -0
  46. package/src/services/cloudfront/origin-router.js +552 -0
  47. package/src/services/cloudfront/origin-validator.js +398 -0
  48. package/src/services/cloudfront/server.js +512 -0
  49. package/src/services/cloudfront/signed-url-generator.js +377 -0
  50. package/src/services/cloudfront/simulator.js +425 -0
  51. package/src/services/cloudfront/store.js +326 -0
  52. package/src/services/cloudshell/env-builder.js +27 -0
  53. package/src/services/cloudshell/index.js +48 -0
  54. package/src/services/cloudshell/routes.js +60 -0
  55. package/src/services/cloudshell/session-manager.js +226 -0
  56. package/src/services/cloudshell/static/.gitkeep +0 -0
  57. package/src/services/cloudshell/static/index.html +406 -0
  58. package/src/services/cloudshell/websocket-handler.js +107 -0
  59. package/src/services/cloudtrail/index.js +2 -3
  60. package/src/services/cloudtrail/server.js +68 -0
  61. package/src/services/cognito/server.js +4 -1
  62. package/src/services/dynamodb/server.js +4 -1
  63. package/src/services/dynamodb/simulator.js +2 -1
  64. package/src/services/ec2/index.js +101 -0
  65. package/src/services/ec2/instanceServer.js +311 -0
  66. package/src/services/ec2/models/ami.js +39 -0
  67. package/src/services/ec2/models/instance.js +98 -0
  68. package/src/services/ec2/models/networkInterface.js +74 -0
  69. package/src/services/ec2/models/securityGroup.js +71 -0
  70. package/src/services/ec2/server.js +350 -0
  71. package/src/services/ec2/simulator.js +436 -0
  72. package/src/services/eventbridge/server.js +36 -0
  73. package/src/services/eventbridge/simulator.js +230 -45
  74. package/src/services/iam/index.js +65 -0
  75. package/src/services/iam/middleware.js +99 -0
  76. package/src/services/iam/server.js +331 -0
  77. package/src/services/iam/simulator.js +1432 -0
  78. package/src/services/index.js +28 -0
  79. package/src/services/kinesis-analytics/index.js +105 -0
  80. package/src/services/kinesis-analytics/models/application.js +325 -0
  81. package/src/services/kinesis-analytics/models/index.js +12 -0
  82. package/src/services/kinesis-analytics/server.js +250 -0
  83. package/src/services/kinesis-analytics/simulator.js +407 -0
  84. package/src/services/kinesis-analytics/store.js +176 -0
  85. package/src/services/kinesis-data-streams/index.js +106 -0
  86. package/src/services/kinesis-data-streams/models/index.js +12 -0
  87. package/src/services/kinesis-data-streams/models/record.js +70 -0
  88. package/src/services/kinesis-data-streams/models/stream.js +280 -0
  89. package/src/services/kinesis-data-streams/server.js +255 -0
  90. package/src/services/kinesis-data-streams/simulator.js +514 -0
  91. package/src/services/kinesis-data-streams/store.js +163 -0
  92. package/src/services/kinesis-firehose/index.js +106 -0
  93. package/src/services/kinesis-firehose/models/delivery-stream.js +265 -0
  94. package/src/services/kinesis-firehose/models/index.js +10 -0
  95. package/src/services/kinesis-firehose/server.js +215 -0
  96. package/src/services/kinesis-firehose/simulator.js +388 -0
  97. package/src/services/kinesis-firehose/store.js +163 -0
  98. package/src/services/kms/server.js +4 -1
  99. package/src/services/lambda/handler-loader.js +50 -5
  100. package/src/services/lambda/index.js +26 -0
  101. package/src/services/lambda/server.js +18 -12
  102. package/src/services/lambda/simulator.js +73 -17
  103. package/src/services/opensearch/index.js +105 -0
  104. package/src/services/opensearch/models/document.js +115 -0
  105. package/src/services/opensearch/models/index.js +73 -0
  106. package/src/services/opensearch/server.js +310 -0
  107. package/src/services/opensearch/simulator.js +605 -0
  108. package/src/services/opensearch/store.js +162 -0
  109. package/src/services/organizations/control-tower-simulator.js +321 -0
  110. package/src/services/organizations/control-tower-store.js +182 -0
  111. package/src/services/organizations/iam-integration.js +413 -0
  112. package/src/services/organizations/index.js +172 -0
  113. package/src/services/organizations/models/index.js +13 -0
  114. package/src/services/organizations/models/invitation.js +48 -0
  115. package/src/services/organizations/models/organization.js +566 -0
  116. package/src/services/organizations/models/role.js +244 -0
  117. package/src/services/organizations/models/scp.js +131 -0
  118. package/src/services/organizations/server.js +736 -0
  119. package/src/services/organizations/simulator.js +593 -0
  120. package/src/services/organizations/store.js +235 -0
  121. package/src/services/organizations/vpc-integration.js +453 -0
  122. package/src/services/route53/index.js +67 -0
  123. package/src/services/route53/models/healthCheck.js +101 -0
  124. package/src/services/route53/models/hostedZone.js +37 -0
  125. package/src/services/route53/models/resourceRecord.js +106 -0
  126. package/src/services/route53/server.js +841 -0
  127. package/src/services/route53/simulator.js +350 -0
  128. package/src/services/s3/index.js +62 -0
  129. package/src/services/s3/presigned-url-generator.js +337 -0
  130. package/src/services/s3/server.js +231 -0
  131. package/src/services/s3/simulator.js +551 -4
  132. package/src/services/sns/index.js +31 -0
  133. package/src/services/sns/server.js +9 -0
  134. package/src/services/sqs/index.js +38 -0
  135. package/src/services/sqs/server.js +57 -4
  136. package/src/services/sqs/simulator.js +423 -33
  137. package/src/services/sts/simulator.js +209 -17
  138. package/src/services/vpc/README.md +459 -0
  139. package/src/services/vpc/ec2ResponseFormatter.js +431 -0
  140. package/src/services/vpc/index.js +90 -0
  141. package/src/services/vpc/models/internetGateway.js +71 -0
  142. package/src/services/vpc/models/routeTable.js +160 -0
  143. package/src/services/vpc/models/subnet.js +48 -0
  144. package/src/services/vpc/models/vpc.js +37 -0
  145. package/src/services/vpc/models/vpcEndpoint.js +100 -0
  146. package/src/services/vpc/models/vpcGateway.js +134 -0
  147. package/src/services/vpc/server.js +836 -0
  148. package/src/services/vpc/simulator.js +613 -0
  149. package/src/utils/policy-cache.js +215 -0
  150. package/aws-config +0 -154
@@ -1,3 +1,4 @@
1
+ const { setupServiceMiddleware } = require('../../middleware');
1
2
  /**
2
3
  * Lambda Server - Servidor HTTP para Lambda (API Gateway style)
3
4
  */
@@ -8,7 +9,9 @@ const LambdaSimulator = require('./simulator');
8
9
  const logger = require('../../utils/logger');
9
10
 
10
11
  class LambdaServer {
11
- constructor(port, config) {
12
+ constructor(port, config) {
13
+ this.organizationsService = null;
14
+ this.stsService = null;
12
15
  this.port = port;
13
16
  this.config = config;
14
17
  this.app = express();
@@ -18,6 +21,7 @@ class LambdaServer {
18
21
  }
19
22
 
20
23
  setupMiddlewares() {
24
+ this.app.use(express.json()); // Adiciona suporte para JSON
21
25
  this.app.use(express.urlencoded({ extended: true }));
22
26
  this.app.use(cors());
23
27
 
@@ -41,6 +45,14 @@ class LambdaServer {
41
45
  }
42
46
 
43
47
  setupRoutes() {
48
+ // IAM enforcement (conditionally applied)
49
+ this.app.use((req, res, next) => {
50
+ if (this.iamMiddleware && !req.path.startsWith('/__admin') && !req.path.startsWith('/health')) {
51
+ return this.iamMiddleware(req, res, next);
52
+ }
53
+ next();
54
+ });
55
+
44
56
  // Health check
45
57
  this.app.get('/health', (req, res) => {
46
58
  res.json({ status: 'healthy', lambdas: this.simulator.getLambdasCount() });
@@ -91,20 +103,13 @@ class LambdaServer {
91
103
  }
92
104
 
93
105
  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
106
  this.app.get('/__admin/functions', (req, res) => {
103
107
  res.json(this.simulator.listLambdas());
104
108
  });
105
109
 
106
- this.app.post('/__admin/functions', parseJson, async (req, res) => {
110
+ this.app.post('/__admin/functions', async (req, res) => {
107
111
  try {
112
+ logger.debug(`📥 POST /__admin/functions:`, req.body);
108
113
  const lambda = await this.simulator.createFunction(req.body);
109
114
  res.status(201).json(lambda);
110
115
  } catch (err) {
@@ -112,8 +117,9 @@ class LambdaServer {
112
117
  }
113
118
  });
114
119
 
115
- this.app.put('/__admin/functions/:name', parseJson, async (req, res) => {
120
+ this.app.put('/__admin/functions/:name', async (req, res) => {
116
121
  try {
122
+ logger.debug(`📥 PUT /__admin/functions/${req.params.name}:`, req.body);
117
123
  const lambda = await this.simulator.updateFunction(req.params.name, req.body);
118
124
  res.json(lambda);
119
125
  } catch (err) {
@@ -132,7 +138,7 @@ class LambdaServer {
132
138
  res.json({ message: 'Lambdas recarregadas', count: this.simulator.getLambdasCount() });
133
139
  });
134
140
 
135
- this.app.post('/__admin/env', parseJson, (req, res) => {
141
+ this.app.post('/__admin/env', (req, res) => {
136
142
  const { key, value } = req.body;
137
143
  if (key && value !== undefined) {
138
144
  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} -> ${handlerPath}`);
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);
@@ -129,6 +141,10 @@ class LambdaSimulator {
129
141
  return { StatusCode: result.statusCode || 200, Payload: result };
130
142
  }
131
143
 
144
+ async invokeFunction(functionName, event) {
145
+ return this.invoke(functionName, event, "Event");
146
+ }
147
+
132
148
  async executeHandler(lambda, functionName, event) {
133
149
  const requestId = Math.random().toString(36).substring(2, 10) + Math.random().toString(36).substring(2, 10);
134
150
  const capturedLogs = [];
@@ -222,6 +238,7 @@ class LambdaSimulator {
222
238
 
223
239
  return {
224
240
  name: l.name,
241
+ runtime: l.runtime,
225
242
  handlerName: l.handlerName,
226
243
  handlerPath: l.handlerPath,
227
244
  type: l.type,
@@ -297,25 +314,64 @@ class LambdaSimulator {
297
314
  const lambda = this.lambdas.get(name);
298
315
  if (!lambda) throw new Error(`Function not found: ${name}`);
299
316
 
300
- const { code, runtime, handler, timeout, memorySize, environment } = lambdaConfig;
317
+ // Normaliza 'environment' para 'env' se necessário
318
+ const config = {
319
+ ...lambdaConfig,
320
+ env: lambdaConfig.environment || lambdaConfig.env
321
+ };
322
+ delete config.environment; // Remove para evitar confusão
323
+
324
+ const { code, runtime, handler, timeout, memorySize, env } = config;
325
+
326
+ logger.debug(`📝 Updating function ${name}:`, {
327
+ hasCode: !!code,
328
+ code: code ? `(${code.length} chars)` : 'NULL/UNDEFINED',
329
+ codeLength: code?.length,
330
+ runtime,
331
+ timeout,
332
+ memorySize,
333
+ handlerPath: lambda.handlerPath,
334
+ configKeys: Object.keys(config)
335
+ });
301
336
 
302
337
  // Se houver código novo, sobrescreve o arquivo
303
- if (code !== undefined) {
304
- fs.writeFileSync(lambda.handlerPath, code);
338
+ if (code !== undefined && code !== null && code.trim() !== '') {
339
+ logger.debug(`💾 Escrevendo código no arquivo: ${lambda.handlerPath}`);
340
+ try {
341
+ fs.writeFileSync(lambda.handlerPath, code, 'utf8');
342
+ logger.debug(`✅ Arquivo atualizado com sucesso`);
343
+ } catch (err) {
344
+ logger.error(`❌ Erro ao escrever arquivo:`, err);
345
+ throw err;
346
+ }
347
+
348
+ // Reload the handler from the updated file
349
+ try {
350
+ const newHandler = await HandlerLoader.reload(lambda.handlerPath, lambda.type);
351
+ lambda.handler = newHandler;
352
+ lambda.handlerName = newHandler.name || "anonymous";
353
+
354
+ // Update code size
355
+ try {
356
+ const info = await HandlerLoader.getInfo(lambda.handlerPath);
357
+ lambda.codeSize = info.size;
358
+ } catch (err) {
359
+ // Ignore size errors
360
+ }
361
+ } catch (error) {
362
+ logger.error(`❌ Erro ao recarregar Lambda ${name}:`, error);
363
+ throw error;
364
+ }
365
+ } else {
366
+ logger.warn(`⚠️ Nenhum código fornecido para atualização. code=${JSON.stringify(code)}`);
305
367
  }
306
368
 
307
- // Atualiza a configuração
308
- const updatedConfig = {
309
- name,
310
- handler: lambda.handlerPath,
311
- runtime: runtime || lambda.runtime,
312
- timeout: timeout || lambda.timeout,
313
- memorySize: memorySize || lambda.memorySize,
314
- env: environment || lambda.env,
315
- type: lambda.type
316
- };
369
+ // Atualiza outras configurações
370
+ if (runtime !== undefined) lambda.runtime = runtime;
371
+ if (timeout !== undefined) lambda.timeout = timeout;
372
+ if (memorySize !== undefined) lambda.memorySize = memorySize;
373
+ if (env !== undefined) lambda.env = env;
317
374
 
318
- await this.registerLambda(updatedConfig);
319
375
  this.persistLambdas();
320
376
 
321
377
  return this.lambdas.get(name);
@@ -0,0 +1,105 @@
1
+ /**
2
+ * OpenSearch Service - Ponto de entrada
3
+ */
4
+
5
+ const OpenSearchServer = require('./server');
6
+ const OpenSearchSimulator = require('./simulator');
7
+
8
+ class OpenSearchService {
9
+ constructor(config) {
10
+ this.config = config;
11
+ this.name = 'opensearch';
12
+ this.port = config.ports.opensearch || 9200;
13
+ this.server = null;
14
+ this.simulator = null;
15
+ this.isRunning = false;
16
+ }
17
+
18
+ async initialize() {
19
+ const logger = require('../../utils/logger');
20
+ logger.debug(`Inicializando OpenSearch Service na porta ${this.port}...`);
21
+
22
+ this.simulator = new OpenSearchSimulator(this.config);
23
+ await this.simulator.initialize();
24
+
25
+ this.server = new OpenSearchServer(this.port, this.config);
26
+ this.server.simulator = this.simulator;
27
+
28
+ await this.server.initialize();
29
+
30
+ logger.debug('OpenSearch Service inicializado');
31
+ }
32
+
33
+ injectDependencies(server) {
34
+ // Aqui você pode injetar dependências de outros serviços se necessário
35
+ }
36
+
37
+ async start() {
38
+ const logger = require('../../utils/logger');
39
+
40
+ if (this.isRunning) {
41
+ logger.warn('OpenSearch Service já está em execução');
42
+ return;
43
+ }
44
+
45
+ try {
46
+ await this.server.start();
47
+ this.isRunning = true;
48
+ logger.info('OpenSearch Service iniciado com sucesso');
49
+ } catch (error) {
50
+ logger.error(`Erro ao iniciar OpenSearch Service: ${error.message}`);
51
+ throw error;
52
+ }
53
+ }
54
+
55
+ async stop() {
56
+ const logger = require('../../utils/logger');
57
+
58
+ if (!this.isRunning) {
59
+ logger.warn('OpenSearch Service não está em execução');
60
+ return;
61
+ }
62
+
63
+ try {
64
+ await this.server.stop();
65
+ this.isRunning = false;
66
+ logger.info('OpenSearch Service parado com sucesso');
67
+ } catch (error) {
68
+ logger.error(`Erro ao parar OpenSearch Service: ${error.message}`);
69
+ throw error;
70
+ }
71
+ }
72
+
73
+ async reset() {
74
+ const logger = require('../../utils/logger');
75
+ try {
76
+ await this.simulator.reset();
77
+ logger.info('OpenSearch Service resetado');
78
+ } catch (error) {
79
+ logger.error(`Erro ao resetar OpenSearch Service: ${error.message}`);
80
+ throw error;
81
+ }
82
+ }
83
+
84
+ getSimulator() {
85
+ return this.simulator;
86
+ }
87
+
88
+ getStatus() {
89
+ const stats = this.simulator?.getClusterStats() || { indices: 0, docs: 0, size_mb: 0 };
90
+
91
+ return {
92
+ name: this.name,
93
+ running: this.isRunning,
94
+ port: this.port,
95
+ endpoint: `http://localhost:${this.port}`,
96
+ resources: {
97
+ indicesCount: stats.indices,
98
+ docsCount: stats.docs,
99
+ sizeMb: stats.size_mb
100
+ }
101
+ };
102
+ }
103
+ }
104
+
105
+ module.exports = OpenSearchService;
@@ -0,0 +1,115 @@
1
+ /**
2
+ * OpenSearch Document Model - Equivalente ao Item no DynamoDB
3
+ */
4
+
5
+ const { v4: uuidv4 } = require('uuid');
6
+
7
+ class Document {
8
+ constructor(data = {}, config = {}) {
9
+ this._id = config._id || uuidv4();
10
+ this._type = config._type || '_doc';
11
+ this._index = config._index || null;
12
+ this._version = config._version || 1;
13
+ this._seq_no = config._seq_no || 0;
14
+ this._primary_term = config._primary_term || 1;
15
+ this._score = config._score || null;
16
+
17
+ // Metadados
18
+ this._createdAt = config._createdAt || new Date().toISOString();
19
+ this._updatedAt = config._updatedAt || new Date().toISOString();
20
+
21
+ // Source - dados do documento
22
+ this._source = data;
23
+
24
+ // Highlights - para busca
25
+ this.highlight = {};
26
+ }
27
+
28
+ /**
29
+ * Atualiza documento
30
+ */
31
+ update(data = {}) {
32
+ this._source = { ...this._source, ...data };
33
+ this._version++;
34
+ this._updatedAt = new Date().toISOString();
35
+ }
36
+
37
+ /**
38
+ * Incrementa version
39
+ */
40
+ incrementVersion() {
41
+ this._version++;
42
+ this._updatedAt = new Date().toISOString();
43
+ }
44
+
45
+ /**
46
+ * Retorna documento em formato Elasticsearch
47
+ */
48
+ toJSON() {
49
+ return {
50
+ _id: this._id,
51
+ _type: this._type,
52
+ _index: this._index,
53
+ _version: this._version,
54
+ _seq_no: this._seq_no,
55
+ _primary_term: this._primary_term,
56
+ _score: this._score,
57
+ _source: this._source,
58
+ highlight: Object.keys(this.highlight).length > 0 ? this.highlight : undefined
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Retorna apenas _source
64
+ */
65
+ getSource() {
66
+ return this._source;
67
+ }
68
+
69
+ /**
70
+ * Retorna tamanho em bytes (aproximado)
71
+ */
72
+ getSize() {
73
+ return JSON.stringify(this._source).length;
74
+ }
75
+
76
+ /**
77
+ * Verifica se documento match em busca simples
78
+ */
79
+ matchesQuery(query = {}) {
80
+ if (Object.keys(query).length === 0) return true;
81
+
82
+ for (const [key, value] of Object.entries(query)) {
83
+ if (key === '_id' && this._id !== value) return false;
84
+ if (key === '_type' && this._type !== value) return false;
85
+ if (!(key in this._source)) return false;
86
+
87
+ const fieldValue = this._source[key];
88
+ if (typeof fieldValue === 'string') {
89
+ if (!fieldValue.toLowerCase().includes(String(value).toLowerCase())) return false;
90
+ } else if (fieldValue !== value) {
91
+ return false;
92
+ }
93
+ }
94
+
95
+ return true;
96
+ }
97
+
98
+ /**
99
+ * Busca full-text simples
100
+ */
101
+ matchesFullText(text = '') {
102
+ if (!text) return true;
103
+
104
+ const searchText = text.toLowerCase();
105
+ for (const value of Object.values(this._source)) {
106
+ if (typeof value === 'string' && value.toLowerCase().includes(searchText)) {
107
+ return true;
108
+ }
109
+ }
110
+
111
+ return false;
112
+ }
113
+ }
114
+
115
+ module.exports = Document;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * OpenSearch Index Model - Equivalente à Table no DynamoDB
3
+ */
4
+
5
+ const { v4: uuidv4 } = require('uuid');
6
+
7
+ class Index {
8
+ constructor(name, config = {}) {
9
+ this.name = name;
10
+ this.settings = {
11
+ number_of_shards: config.number_of_shards || 1,
12
+ number_of_replicas: config.number_of_replicas || 1,
13
+ analysis: config.analysis || {
14
+ analyzer: {
15
+ default: {
16
+ type: 'standard'
17
+ }
18
+ }
19
+ }
20
+ };
21
+
22
+ // Mappings - Define tipos de campos
23
+ this.mappings = {
24
+ properties: config.properties || {
25
+ _id: { type: 'keyword' },
26
+ _type: { type: 'keyword' },
27
+ _source: { type: 'text' }
28
+ }
29
+ };
30
+
31
+ this.aliases = config.aliases || {};
32
+ this.createdAt = new Date().toISOString();
33
+ this.status = 'open';
34
+ this.docsCount = 0;
35
+ this.storeSize = 0;
36
+ }
37
+
38
+ toJSON() {
39
+ return {
40
+ name: this.name,
41
+ settings: this.settings,
42
+ mappings: this.mappings,
43
+ aliases: this.aliases,
44
+ status: this.status,
45
+ createdAt: this.createdAt,
46
+ docsCount: this.docsCount,
47
+ storeSize: this.storeSize
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Adiciona documento - incrementa contador
53
+ */
54
+ addDocument() {
55
+ this.docsCount++;
56
+ }
57
+
58
+ /**
59
+ * Remove documento - decrementa contador
60
+ */
61
+ removeDocument() {
62
+ if (this.docsCount > 0) this.docsCount--;
63
+ }
64
+
65
+ /**
66
+ * Atualiza tamanho do índice
67
+ */
68
+ updateSize(totalSize) {
69
+ this.storeSize = totalSize;
70
+ }
71
+ }
72
+
73
+ module.exports = Index;