@gugananuvem/aws-local-simulator 1.0.35 → 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 (149) hide show
  1. package/README.md +662 -658
  2. package/package.json +10 -3
  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/index.js +26 -0
  100. package/src/services/lambda/server.js +12 -1
  101. package/src/services/lambda/simulator.js +4 -0
  102. package/src/services/opensearch/index.js +105 -0
  103. package/src/services/opensearch/models/document.js +115 -0
  104. package/src/services/opensearch/models/index.js +73 -0
  105. package/src/services/opensearch/server.js +310 -0
  106. package/src/services/opensearch/simulator.js +605 -0
  107. package/src/services/opensearch/store.js +162 -0
  108. package/src/services/organizations/control-tower-simulator.js +321 -0
  109. package/src/services/organizations/control-tower-store.js +182 -0
  110. package/src/services/organizations/iam-integration.js +413 -0
  111. package/src/services/organizations/index.js +172 -0
  112. package/src/services/organizations/models/index.js +13 -0
  113. package/src/services/organizations/models/invitation.js +48 -0
  114. package/src/services/organizations/models/organization.js +566 -0
  115. package/src/services/organizations/models/role.js +244 -0
  116. package/src/services/organizations/models/scp.js +131 -0
  117. package/src/services/organizations/server.js +736 -0
  118. package/src/services/organizations/simulator.js +593 -0
  119. package/src/services/organizations/store.js +235 -0
  120. package/src/services/organizations/vpc-integration.js +453 -0
  121. package/src/services/route53/index.js +67 -0
  122. package/src/services/route53/models/healthCheck.js +101 -0
  123. package/src/services/route53/models/hostedZone.js +37 -0
  124. package/src/services/route53/models/resourceRecord.js +106 -0
  125. package/src/services/route53/server.js +841 -0
  126. package/src/services/route53/simulator.js +350 -0
  127. package/src/services/s3/index.js +62 -0
  128. package/src/services/s3/presigned-url-generator.js +337 -0
  129. package/src/services/s3/server.js +231 -0
  130. package/src/services/s3/simulator.js +551 -4
  131. package/src/services/sns/index.js +31 -0
  132. package/src/services/sns/server.js +9 -0
  133. package/src/services/sqs/index.js +38 -0
  134. package/src/services/sqs/server.js +57 -4
  135. package/src/services/sqs/simulator.js +423 -33
  136. package/src/services/sts/simulator.js +209 -17
  137. package/src/services/vpc/README.md +459 -0
  138. package/src/services/vpc/ec2ResponseFormatter.js +431 -0
  139. package/src/services/vpc/index.js +90 -0
  140. package/src/services/vpc/models/internetGateway.js +71 -0
  141. package/src/services/vpc/models/routeTable.js +160 -0
  142. package/src/services/vpc/models/subnet.js +48 -0
  143. package/src/services/vpc/models/vpc.js +37 -0
  144. package/src/services/vpc/models/vpcEndpoint.js +100 -0
  145. package/src/services/vpc/models/vpcGateway.js +134 -0
  146. package/src/services/vpc/server.js +836 -0
  147. package/src/services/vpc/simulator.js +613 -0
  148. package/src/utils/policy-cache.js +215 -0
  149. package/aws-config +0 -154
@@ -0,0 +1,213 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Aurora Server - HTTP server que roteia requisições para Data API ou RDS Management API.
5
+ *
6
+ * Routing:
7
+ * - x-amz-target header starting with "RDSDataService" → Data API Handler
8
+ * - Body/query Action parameter → RDS Management API Handler
9
+ * - Otherwise → HTTP 400
10
+ *
11
+ * Admin endpoints:
12
+ * - GET /__admin/health
13
+ * - GET /__admin/clusters
14
+ * - GET /__admin/databases
15
+ * - POST /__admin/reset
16
+ */
17
+
18
+ const express = require('express');
19
+ const cors = require('cors');
20
+ const logger = require('../../utils/logger');
21
+
22
+ class AuroraServer {
23
+ constructor(port, config) {
24
+ this.port = port;
25
+ this.config = config;
26
+ this.app = express();
27
+ this.server = null;
28
+
29
+ // Handler/manager references set externally by AuroraService
30
+ this.dataApiHandler = null;
31
+ this.rdsApiHandler = null;
32
+ this.clusterManager = null;
33
+ this.dbEngine = null;
34
+
35
+ this.setupMiddlewares();
36
+ }
37
+
38
+ setupMiddlewares() {
39
+ this.app.use(cors());
40
+ this.app.use(express.json({
41
+ type: (req) => {
42
+ const ct = req.headers['content-type'] || '';
43
+ return ct.includes('application/x-amz-json-1.1') || ct.includes('application/json');
44
+ }
45
+ }));
46
+
47
+ // Request logging
48
+ if (logger.currentLogLevel === 'verboso') {
49
+ this.app.use((req, res, next) => {
50
+ const start = Date.now();
51
+ res.on('finish', () => {
52
+ const duration = Date.now() - start;
53
+ const target = req.headers['x-amz-target'] || '';
54
+ const action = req.body?.Action || '';
55
+ logger.verboso(`Aurora: ${target || action || req.method} - ${duration}ms`);
56
+ });
57
+ next();
58
+ });
59
+ }
60
+ }
61
+
62
+ async initialize() {
63
+ this.setupRoutes();
64
+ }
65
+
66
+ setupRoutes() {
67
+ // Main endpoint - route based on headers/body
68
+ this.app.post('/', async (req, res) => {
69
+ const target = req.headers['x-amz-target'] || '';
70
+ const body = req.body || {};
71
+ const action = body.Action || req.query?.Action || '';
72
+
73
+ try {
74
+ let result;
75
+
76
+ if (target.startsWith('RDSDataService')) {
77
+ // Data API routing
78
+ if (!this.dataApiHandler) {
79
+ return res.status(503).json({
80
+ __type: 'ServiceUnavailableException',
81
+ message: 'Data API handler not initialized'
82
+ });
83
+ }
84
+ result = await this.dataApiHandler.handleRequest(target, body);
85
+ } else if (action) {
86
+ // RDS Management API routing
87
+ if (!this.rdsApiHandler) {
88
+ return res.status(503).json({
89
+ __type: 'ServiceUnavailableException',
90
+ message: 'RDS API handler not initialized'
91
+ });
92
+ }
93
+ result = await this.rdsApiHandler.handleRequest(action, body);
94
+ } else {
95
+ // Unrecognized request
96
+ return res.status(400).json({
97
+ __type: 'BadRequestException',
98
+ message: 'Unrecognized request. Expected x-amz-target header starting with "RDSDataService" or an Action parameter.'
99
+ });
100
+ }
101
+
102
+ res.json(result || {});
103
+ } catch (error) {
104
+ logger.error(`Aurora Error: ${error.message}`);
105
+
106
+ // If error already has an AWS-style code, use it directly
107
+ if (error.code && error.statusCode) {
108
+ return res.status(error.statusCode).json({
109
+ __type: error.code,
110
+ message: error.message
111
+ });
112
+ }
113
+
114
+ // Wrap raw SQLite/better-sqlite3 errors as BadRequestException
115
+ // These come from db.prepare(), stmt.run(), stmt.all(), etc.
116
+ if (error.message && (
117
+ error.message.includes('SQLITE_') ||
118
+ error.message.includes('no such table') ||
119
+ error.message.includes('no such column') ||
120
+ error.message.includes('near "') ||
121
+ error.message.includes('syntax error') ||
122
+ error.constructor?.name === 'SqliteError'
123
+ )) {
124
+ return res.status(400).json({
125
+ __type: 'BadRequestException',
126
+ message: error.message
127
+ });
128
+ }
129
+
130
+ // Default: treat unstructured errors as BadRequestException (matching AWS behavior)
131
+ const statusCode = error.statusCode || 400;
132
+ res.status(statusCode).json({
133
+ __type: error.code || 'BadRequestException',
134
+ message: error.message
135
+ });
136
+ }
137
+ });
138
+
139
+ // Admin endpoints
140
+ this.setupAdminRoutes();
141
+ }
142
+
143
+ setupAdminRoutes() {
144
+ this.app.get('/__admin/health', (req, res) => {
145
+ res.json({
146
+ status: 'healthy',
147
+ service: 'aurora',
148
+ port: this.port,
149
+ running: !!this.server
150
+ });
151
+ });
152
+
153
+ this.app.get('/__admin/clusters', (req, res) => {
154
+ try {
155
+ const clusters = this.clusterManager ? this.clusterManager.listClusters() : [];
156
+ res.json({ clusters });
157
+ } catch (error) {
158
+ res.status(500).json({ error: error.message });
159
+ }
160
+ });
161
+
162
+ this.app.get('/__admin/databases', (req, res) => {
163
+ try {
164
+ const databases = this.dbEngine ? this.dbEngine.listDatabases() : [];
165
+ res.json({ databases });
166
+ } catch (error) {
167
+ res.status(500).json({ error: error.message });
168
+ }
169
+ });
170
+
171
+ this.app.post('/__admin/reset', async (req, res) => {
172
+ try {
173
+ if (this.clusterManager) await this.clusterManager.reset();
174
+ if (this.dbEngine) await this.dbEngine.reset();
175
+ res.json({ message: 'Aurora data reset complete' });
176
+ } catch (error) {
177
+ res.status(500).json({ error: error.message });
178
+ }
179
+ });
180
+ }
181
+
182
+ start() {
183
+ return new Promise((resolve) => {
184
+ this.server = this.app.listen(this.port, () => {
185
+ logger.info(`🌐 Aurora rodando em http://localhost:${this.port}`);
186
+ resolve();
187
+ });
188
+ });
189
+ }
190
+
191
+ stop() {
192
+ return new Promise((resolve) => {
193
+ if (this.server) {
194
+ this.server.close(() => {
195
+ this.server = null;
196
+ resolve();
197
+ });
198
+ } else {
199
+ resolve();
200
+ }
201
+ });
202
+ }
203
+
204
+ getStatus() {
205
+ return {
206
+ running: !!this.server,
207
+ port: this.port,
208
+ endpoint: `http://localhost:${this.port}`
209
+ };
210
+ }
211
+ }
212
+
213
+ module.exports = AuroraServer;
@@ -0,0 +1,334 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * SQL Translator - Converte SQL MySQL/PostgreSQL para SQLite compatível.
5
+ *
6
+ * Responsabilidades:
7
+ * - Conversão de tipos de dados (VARCHAR→TEXT, INT→INTEGER, DOUBLE→REAL, etc.)
8
+ * - Tradução de funções (NOW()→datetime('now'), UUID()→hex expression)
9
+ * - Remoção de cláusulas não-suportadas (ENGINE, CHARSET, COLLATE, COMMENT)
10
+ * - Substituição AUTO_INCREMENT→AUTOINCREMENT
11
+ * - Normalização de whitespace e remoção de comentários
12
+ *
13
+ * IMPORTANTE: O método translate() NÃO muta a string SQL original.
14
+ */
15
+
16
+ class SqlTranslator {
17
+ /**
18
+ * @param {string} engineMode - 'aurora-mysql' | 'aurora-postgresql'
19
+ */
20
+ constructor(engineMode) {
21
+ if (engineMode !== 'aurora-mysql' && engineMode !== 'aurora-postgresql') {
22
+ throw new Error(`Invalid engineMode: ${engineMode}. Must be 'aurora-mysql' or 'aurora-postgresql'`);
23
+ }
24
+ this.engineMode = engineMode;
25
+ }
26
+
27
+ /**
28
+ * Translates SQL from MySQL/PostgreSQL dialect to SQLite-compatible SQL.
29
+ * Does NOT mutate the original SQL string.
30
+ *
31
+ * @param {string} sql - Original SQL statement
32
+ * @returns {string} SQLite-compatible SQL
33
+ */
34
+ translate(sql) {
35
+ if (!sql || typeof sql !== 'string') {
36
+ return sql;
37
+ }
38
+
39
+ // Work on a copy - never mutate the original
40
+ let result = sql.trim();
41
+
42
+ // Step 1: Normalize whitespace and remove comments
43
+ result = this._removeComments(result);
44
+ result = this._normalizeWhitespace(result);
45
+
46
+ // Step 2: Translate data types (only in DDL context)
47
+ result = this._translateDataTypes(result);
48
+
49
+ // Step 3: Translate AUTO_INCREMENT
50
+ if (this.engineMode === 'aurora-mysql') {
51
+ result = result.replace(/\bAUTO_INCREMENT\b/gi, 'AUTOINCREMENT');
52
+ }
53
+
54
+ // Step 4: Remove engine-specific clauses
55
+ result = this._removeEngineClauses(result);
56
+
57
+ // Step 5: Translate functions (apply globally)
58
+ result = this._translateFunctions(result);
59
+
60
+ // Step 6: Translate ON CONFLICT (PostgreSQL only)
61
+ if (this.engineMode === 'aurora-postgresql') {
62
+ result = this._translateOnConflict(result);
63
+ }
64
+
65
+ return result;
66
+ }
67
+
68
+ /**
69
+ * Remove SQL comments (single-line -- and multi-line /* *\/)
70
+ * @private
71
+ */
72
+ _removeComments(sql) {
73
+ // Remove multi-line comments /* ... */
74
+ let result = sql.replace(/\/\*[\s\S]*?\*\//g, '');
75
+ // Remove single-line comments -- ...
76
+ result = result.replace(/--[^\n]*/g, '');
77
+ return result;
78
+ }
79
+
80
+ /**
81
+ * Normalize whitespace: collapse multiple spaces/tabs/newlines into single space
82
+ * @private
83
+ */
84
+ _normalizeWhitespace(sql) {
85
+ return sql.replace(/\s+/g, ' ').trim();
86
+ }
87
+
88
+ /**
89
+ * Translate MySQL/PostgreSQL data types to SQLite equivalents.
90
+ * Only applies within CREATE TABLE statements to avoid replacing data in DML.
91
+ * @private
92
+ */
93
+ _translateDataTypes(sql) {
94
+ if (this.engineMode === 'aurora-postgresql') {
95
+ return this._translatePostgresDataTypes(sql);
96
+ }
97
+
98
+ if (this.engineMode !== 'aurora-mysql') {
99
+ return sql;
100
+ }
101
+
102
+ // Check if this is a CREATE TABLE statement
103
+ if (!/\bCREATE\s+TABLE\b/i.test(sql)) {
104
+ return sql;
105
+ }
106
+
107
+ let result = sql;
108
+
109
+ // VARCHAR(N), CHAR(N) → TEXT
110
+ result = result.replace(/\bVARCHAR\s*\(\s*\d+\s*\)/gi, 'TEXT');
111
+ result = result.replace(/\bCHAR\s*\(\s*\d+\s*\)/gi, 'TEXT');
112
+
113
+ // TINYTEXT, MEDIUMTEXT, LONGTEXT → TEXT
114
+ result = result.replace(/\bTINYTEXT\b/gi, 'TEXT');
115
+ result = result.replace(/\bMEDIUMTEXT\b/gi, 'TEXT');
116
+ result = result.replace(/\bLONGTEXT\b/gi, 'TEXT');
117
+
118
+ // TINYINT, SMALLINT, MEDIUMINT → INTEGER (before INT to avoid partial match)
119
+ result = result.replace(/\bTINYINT\b/gi, 'INTEGER');
120
+ result = result.replace(/\bSMALLINT\b/gi, 'INTEGER');
121
+ result = result.replace(/\bMEDIUMINT\b/gi, 'INTEGER');
122
+
123
+ // BIGINT → INTEGER (before INT to avoid partial match)
124
+ result = result.replace(/\bBIGINT\b/gi, 'INTEGER');
125
+
126
+ // INT → INTEGER (word boundary prevents matching TINYINT, BIGINT, etc.)
127
+ result = result.replace(/\bINT\b/gi, 'INTEGER');
128
+
129
+ // FLOAT → REAL
130
+ result = result.replace(/\bFLOAT\b/gi, 'REAL');
131
+
132
+ // DOUBLE → REAL
133
+ result = result.replace(/\bDOUBLE\b/gi, 'REAL');
134
+
135
+ // DECIMAL(N,M) → REAL
136
+ result = result.replace(/\bDECIMAL\s*\(\s*\d+\s*,\s*\d+\s*\)/gi, 'REAL');
137
+
138
+ // DATETIME → TEXT
139
+ result = result.replace(/\bDATETIME\b/gi, 'TEXT');
140
+
141
+ // TIMESTAMP → TEXT
142
+ result = result.replace(/\bTIMESTAMP\b/gi, 'TEXT');
143
+
144
+ // DATE → TEXT (word boundary avoids matching CURDATE, DATETIME, etc.)
145
+ result = result.replace(/\bDATE\b/gi, 'TEXT');
146
+
147
+ // TIME → TEXT (word boundary avoids matching DATETIME, TIMESTAMP, etc.)
148
+ result = result.replace(/\bTIME\b/gi, 'TEXT');
149
+
150
+ // JSON → TEXT
151
+ result = result.replace(/\bJSON\b/gi, 'TEXT');
152
+
153
+ // TINYBLOB, MEDIUMBLOB, LONGBLOB → BLOB (before BLOB)
154
+ result = result.replace(/\bTINYBLOB\b/gi, 'BLOB');
155
+ result = result.replace(/\bMEDIUMBLOB\b/gi, 'BLOB');
156
+ result = result.replace(/\bLONGBLOB\b/gi, 'BLOB');
157
+
158
+ // BLOB → BLOB (already correct, but normalize case)
159
+ result = result.replace(/\bBLOB\b/gi, 'BLOB');
160
+
161
+ // ENUM(...) → TEXT
162
+ result = result.replace(/\bENUM\s*\([^)]*\)/gi, 'TEXT');
163
+
164
+ // SET(...) → TEXT
165
+ result = result.replace(/\bSET\s*\([^)]*\)/gi, 'TEXT');
166
+
167
+ return result;
168
+ }
169
+
170
+ /**
171
+ * Remove engine-specific clauses (ENGINE, CHARSET, COLLATE, CHARACTER SET, COMMENT)
172
+ * @private
173
+ */
174
+ _removeEngineClauses(sql) {
175
+ let result = sql;
176
+
177
+ // ENGINE=InnoDB (or any engine name)
178
+ result = result.replace(/\s*ENGINE\s*=\s*\w+/gi, '');
179
+
180
+ // DEFAULT CHARSET=utf8mb4 (or any charset)
181
+ result = result.replace(/\s*DEFAULT\s+CHARSET\s*=\s*\w+/gi, '');
182
+
183
+ // COLLATE = utf8mb4_unicode_ci or COLLATE utf8mb4_unicode_ci
184
+ result = result.replace(/\s*COLLATE\s*=?\s*\w+/gi, '');
185
+
186
+ // CHARACTER SET utf8
187
+ result = result.replace(/\s*CHARACTER\s+SET\s+\w+/gi, '');
188
+
189
+ // COMMENT 'text'
190
+ result = result.replace(/\s*COMMENT\s+'[^']*'/gi, '');
191
+
192
+ return result;
193
+ }
194
+
195
+ /**
196
+ * Translate MySQL/PostgreSQL functions to SQLite equivalents.
197
+ * Applied globally (not just in DDL).
198
+ * @private
199
+ */
200
+ _translateFunctions(sql) {
201
+ if (this.engineMode === 'aurora-postgresql') {
202
+ return this._translatePostgresFunctions(sql);
203
+ }
204
+
205
+ if (this.engineMode !== 'aurora-mysql') {
206
+ return sql;
207
+ }
208
+
209
+ let result = sql;
210
+
211
+ // NOW() → datetime('now')
212
+ result = result.replace(/\bNOW\s*\(\s*\)/gi, "datetime('now')");
213
+
214
+ // CURDATE() → date('now')
215
+ result = result.replace(/\bCURDATE\s*\(\s*\)/gi, "date('now')");
216
+
217
+ // CURTIME() → time('now')
218
+ result = result.replace(/\bCURTIME\s*\(\s*\)/gi, "time('now')");
219
+
220
+ // UUID() → lower(hex(randomblob(4))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(6)))
221
+ result = result.replace(
222
+ /\bUUID\s*\(\s*\)/gi,
223
+ "lower(hex(randomblob(4))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(6)))"
224
+ );
225
+
226
+ return result;
227
+ }
228
+
229
+ /**
230
+ * Translate PostgreSQL data types to SQLite equivalents.
231
+ * Only applies within CREATE TABLE statements to avoid replacing data in DML.
232
+ * @private
233
+ */
234
+ _translatePostgresDataTypes(sql) {
235
+ // Check if this is a CREATE TABLE statement
236
+ if (!/\bCREATE\s+TABLE\b/i.test(sql)) {
237
+ return sql;
238
+ }
239
+
240
+ let result = sql;
241
+
242
+ // BIGSERIAL → INTEGER PRIMARY KEY AUTOINCREMENT (before SERIAL to avoid partial match)
243
+ result = result.replace(/\bBIGSERIAL\b/gi, 'INTEGER PRIMARY KEY AUTOINCREMENT');
244
+
245
+ // SERIAL → INTEGER PRIMARY KEY AUTOINCREMENT
246
+ result = result.replace(/\bSERIAL\b/gi, 'INTEGER PRIMARY KEY AUTOINCREMENT');
247
+
248
+ // JSONB → TEXT (before JSON to avoid partial match)
249
+ result = result.replace(/\bJSONB\b/gi, 'TEXT');
250
+
251
+ // JSON → TEXT
252
+ result = result.replace(/\bJSON\b/gi, 'TEXT');
253
+
254
+ // BYTEA → BLOB
255
+ result = result.replace(/\bBYTEA\b/gi, 'BLOB');
256
+
257
+ // TIMESTAMPTZ → TEXT (before TIMESTAMP to avoid partial match)
258
+ result = result.replace(/\bTIMESTAMPTZ\b/gi, 'TEXT');
259
+
260
+ // TIMESTAMP → TEXT
261
+ result = result.replace(/\bTIMESTAMP\b/gi, 'TEXT');
262
+
263
+ // VARCHAR(N) → TEXT
264
+ result = result.replace(/\bVARCHAR\s*\(\s*\d+\s*\)/gi, 'TEXT');
265
+
266
+ // CHAR(N) → TEXT
267
+ result = result.replace(/\bCHAR\s*\(\s*\d+\s*\)/gi, 'TEXT');
268
+
269
+ // BOOLEAN → INTEGER
270
+ result = result.replace(/\bBOOLEAN\b/gi, 'INTEGER');
271
+
272
+ // NUMERIC(N,M) → REAL
273
+ result = result.replace(/\bNUMERIC\s*\(\s*\d+\s*,\s*\d+\s*\)/gi, 'REAL');
274
+
275
+ // DOUBLE PRECISION → REAL (before FLOAT to avoid conflicts)
276
+ result = result.replace(/\bDOUBLE\s+PRECISION\b/gi, 'REAL');
277
+
278
+ // FLOAT → REAL
279
+ result = result.replace(/\bFLOAT\b/gi, 'REAL');
280
+
281
+ // TEXT[] → TEXT (arrays stored as JSON text)
282
+ result = result.replace(/\bTEXT\s*\[\s*\]/gi, 'TEXT');
283
+
284
+ return result;
285
+ }
286
+
287
+ /**
288
+ * Translate PostgreSQL functions to SQLite equivalents.
289
+ * Applied globally (not just in DDL).
290
+ * @private
291
+ */
292
+ _translatePostgresFunctions(sql) {
293
+ let result = sql;
294
+
295
+ // NOW() → datetime('now')
296
+ result = result.replace(/\bNOW\s*\(\s*\)/gi, "datetime('now')");
297
+
298
+ // CURRENT_TIMESTAMP → datetime('now') (without parentheses - it's a keyword in PostgreSQL)
299
+ result = result.replace(/\bCURRENT_TIMESTAMP\b/gi, "datetime('now')");
300
+
301
+ // gen_random_uuid() → lower(hex(randomblob(4))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(6)))
302
+ result = result.replace(
303
+ /\bgen_random_uuid\s*\(\s*\)/gi,
304
+ "lower(hex(randomblob(4))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(6)))"
305
+ );
306
+
307
+ // COALESCE is already SQLite-compatible, no translation needed
308
+
309
+ return result;
310
+ }
311
+
312
+ /**
313
+ * Translate PostgreSQL ON CONFLICT clauses to SQLite INSERT OR REPLACE/IGNORE.
314
+ * This is a simplification for the local simulator.
315
+ * @private
316
+ */
317
+ _translateOnConflict(sql) {
318
+ // ON CONFLICT (...) DO NOTHING → INSERT OR IGNORE INTO ...
319
+ let result = sql.replace(
320
+ /\bINSERT\s+INTO\b(.*?)\bON\s+CONFLICT\s*\([^)]*\)\s*DO\s+NOTHING\b/gi,
321
+ 'INSERT OR IGNORE INTO$1'
322
+ );
323
+
324
+ // ON CONFLICT (...) DO UPDATE SET ... → INSERT OR REPLACE INTO ...
325
+ result = result.replace(
326
+ /\bINSERT\s+INTO\b(.*?)\bON\s+CONFLICT\s*\([^)]*\)\s*DO\s+UPDATE\s+SET\b[^;]*/gi,
327
+ 'INSERT OR REPLACE INTO$1'
328
+ );
329
+
330
+ return result;
331
+ }
332
+ }
333
+
334
+ module.exports = SqlTranslator;
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * @fileoverview CloudFormation Service
5
- * Porta padrão: 4580
5
+ * Porta padrão: 4582
6
6
  */
7
7
 
8
8
  const http = require('http');
@@ -16,7 +16,7 @@ class CloudFormationService {
16
16
  this.config = config;
17
17
  this.logger = require('../../utils/logger');
18
18
  this.name = 'cloudformation';
19
- this.port = config?.ports?.cloudformation || config?.services?.cloudformation?.port || 4580;
19
+ this.port = config?.ports?.cloudformation || config?.services?.cloudformation?.port || 4582;
20
20
  this.store = null;
21
21
  this.simulator = null;
22
22
  this._server = null;
@@ -1,3 +1,4 @@
1
+ const { setupServiceMiddleware } = require('../../middleware');
1
2
  'use strict';
2
3
 
3
4
  /**