@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
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Kinesis Data Analytics Store - Persistência de aplicações em JSON
3
+ */
4
+
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+ const logger = require('../../utils/logger');
8
+
9
+ class KinesisAnalyticsStore {
10
+ constructor(dataDir) {
11
+ this.dataDir = dataDir;
12
+ this.applicationsDir = path.join(dataDir, 'kinesis-analytics');
13
+ this.metadataFile = path.join(this.applicationsDir, '__applications__.json');
14
+
15
+ if (!fs.existsSync(this.applicationsDir)) {
16
+ fs.mkdirSync(this.applicationsDir, { recursive: true });
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Carrega metadados de todas as aplicações
22
+ */
23
+ loadMetadata() {
24
+ if (fs.existsSync(this.metadataFile)) {
25
+ try {
26
+ const data = fs.readFileSync(this.metadataFile, 'utf8');
27
+ return JSON.parse(data);
28
+ } catch (error) {
29
+ logger.warn(`Erro ao ler ${this.metadataFile}: ${error.message}`);
30
+ return {};
31
+ }
32
+ }
33
+ return {};
34
+ }
35
+
36
+ /**
37
+ * Salva metadados de todas as aplicações
38
+ */
39
+ saveMetadata(metadata) {
40
+ try {
41
+ fs.writeFileSync(this.metadataFile, JSON.stringify(metadata, null, 2), 'utf8');
42
+ logger.debug(`Metadados de aplicações salvos em ${this.metadataFile}`);
43
+ } catch (error) {
44
+ logger.error(`Erro ao salvar ${this.metadataFile}: ${error.message}`);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Obtém caminho do arquivo de aplicação
50
+ */
51
+ getApplicationPath(applicationName) {
52
+ return path.join(this.applicationsDir, `${applicationName}.json`);
53
+ }
54
+
55
+ /**
56
+ * Carrega código SQL de uma aplicação
57
+ */
58
+ loadApplicationCode(applicationName) {
59
+ const appPath = this.getApplicationPath(applicationName);
60
+
61
+ if (!fs.existsSync(appPath)) {
62
+ return '';
63
+ }
64
+
65
+ try {
66
+ const data = fs.readFileSync(appPath, 'utf8');
67
+ const appData = JSON.parse(data);
68
+ return appData.sqlCode || '';
69
+ } catch (error) {
70
+ logger.warn(`Erro ao ler aplicação ${applicationName}: ${error.message}`);
71
+ return '';
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Salva código SQL de uma aplicação
77
+ */
78
+ saveApplicationCode(applicationName, sqlCode) {
79
+ try {
80
+ const appPath = this.getApplicationPath(applicationName);
81
+ const appData = {
82
+ applicationName,
83
+ sqlCode,
84
+ lastModified: new Date().toISOString()
85
+ };
86
+ fs.writeFileSync(appPath, JSON.stringify(appData, null, 2), 'utf8');
87
+ logger.debug(`Código da aplicação ${applicationName} salvo`);
88
+ } catch (error) {
89
+ logger.error(`Erro ao salvar aplicação ${applicationName}: ${error.message}`);
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Carrega resultados de query
95
+ */
96
+ loadQueryResults(applicationName, queryId) {
97
+ const appPath = this.getApplicationPath(applicationName);
98
+
99
+ if (!fs.existsSync(appPath)) {
100
+ return [];
101
+ }
102
+
103
+ try {
104
+ const data = fs.readFileSync(appPath, 'utf8');
105
+ const appData = JSON.parse(data);
106
+ const queryResults = appData.queryResults || {};
107
+ return queryResults[queryId] || [];
108
+ } catch (error) {
109
+ logger.warn(`Erro ao ler resultados de query ${queryId}: ${error.message}`);
110
+ return [];
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Salva resultados de query
116
+ */
117
+ saveQueryResults(applicationName, queryId, results) {
118
+ try {
119
+ const appPath = this.getApplicationPath(applicationName);
120
+ let appData = {};
121
+
122
+ if (fs.existsSync(appPath)) {
123
+ const data = fs.readFileSync(appPath, 'utf8');
124
+ appData = JSON.parse(data);
125
+ }
126
+
127
+ if (!appData.queryResults) {
128
+ appData.queryResults = {};
129
+ }
130
+
131
+ appData.queryResults[queryId] = results;
132
+ appData.lastModified = new Date().toISOString();
133
+
134
+ fs.writeFileSync(appPath, JSON.stringify(appData, null, 2), 'utf8');
135
+ logger.debug(`Resultados da query ${queryId} salvos para aplicação ${applicationName}`);
136
+ } catch (error) {
137
+ logger.error(`Erro ao salvar resultados de query ${queryId}: ${error.message}`);
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Deleta aplicação completamente
143
+ */
144
+ deleteApplication(applicationName) {
145
+ try {
146
+ const appPath = this.getApplicationPath(applicationName);
147
+ if (fs.existsSync(appPath)) {
148
+ fs.unlinkSync(appPath);
149
+ logger.debug(`Aplicação ${applicationName} deletada`);
150
+ }
151
+ } catch (error) {
152
+ logger.error(`Erro ao deletar aplicação ${applicationName}: ${error.message}`);
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Limpa todas as aplicações
158
+ */
159
+ clear() {
160
+ try {
161
+ if (fs.existsSync(this.applicationsDir)) {
162
+ const files = fs.readdirSync(this.applicationsDir);
163
+ files.forEach(file => {
164
+ if (file.endsWith('.json')) {
165
+ fs.unlinkSync(path.join(this.applicationsDir, file));
166
+ }
167
+ });
168
+ logger.debug('Todas as aplicações foram limpas');
169
+ }
170
+ } catch (error) {
171
+ logger.error(`Erro ao limpar aplicações: ${error.message}`);
172
+ }
173
+ }
174
+ }
175
+
176
+ module.exports = KinesisAnalyticsStore;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Kinesis Data Streams Service - Ponto de entrada
3
+ */
4
+
5
+ const KinesisDataStreamsServer = require('./server');
6
+ const KinesisDataStreamsSimulator = require('./simulator');
7
+
8
+ class KinesisDataStreamsService {
9
+ constructor(config) {
10
+ this.config = config;
11
+ this.name = 'kinesis-data-streams';
12
+ this.port = config.ports['kinesis-data-streams'] || 4568;
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 Kinesis Data Streams Service na porta ${this.port}...`);
21
+
22
+ this.simulator = new KinesisDataStreamsSimulator(this.config);
23
+ await this.simulator.initialize();
24
+
25
+ this.server = new KinesisDataStreamsServer(this.port, this.config);
26
+ this.server.simulator = this.simulator;
27
+
28
+ await this.server.initialize();
29
+
30
+ logger.debug('Kinesis Data Streams 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('Kinesis Data Streams Service já está em execução');
42
+ return;
43
+ }
44
+
45
+ try {
46
+ await this.server.start();
47
+ this.isRunning = true;
48
+ logger.info('Kinesis Data Streams Service iniciado com sucesso');
49
+ } catch (error) {
50
+ logger.error(`Erro ao iniciar Kinesis Data Streams 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('Kinesis Data Streams 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('Kinesis Data Streams Service parado com sucesso');
67
+ } catch (error) {
68
+ logger.error(`Erro ao parar Kinesis Data Streams 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('Kinesis Data Streams Service resetado');
78
+ } catch (error) {
79
+ logger.error(`Erro ao resetar Kinesis Data Streams Service: ${error.message}`);
80
+ throw error;
81
+ }
82
+ }
83
+
84
+ getSimulator() {
85
+ return this.simulator;
86
+ }
87
+
88
+ getStatus() {
89
+ const stats = this.simulator?.getStats() || { streamsCount: 0, consumersCount: 0, totalRecords: 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
+ streamsCount: stats.streamsCount,
98
+ consumersCount: stats.consumersCount,
99
+ totalRecords: stats.totalRecords,
100
+ totalShards: stats.totalShards
101
+ }
102
+ };
103
+ }
104
+ }
105
+
106
+ module.exports = KinesisDataStreamsService;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Kinesis Data Streams Models
3
+ */
4
+
5
+ const { Stream, Shard } = require('./stream');
6
+ const Record = require('./record');
7
+
8
+ module.exports = {
9
+ Stream,
10
+ Shard,
11
+ Record
12
+ };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Kinesis Data Streams - Record Model
3
+ * Representa um record (mensagem) dentro de um shard
4
+ */
5
+
6
+ const { v4: uuidv4 } = require('uuid');
7
+
8
+ class Record {
9
+ constructor(data, partitionKey, config = {}) {
10
+ this.recordId = uuidv4();
11
+ this.data = data; // Base64 encoded por padrão
12
+ this.partitionKey = partitionKey;
13
+ this.explicitHashKey = config.explicitHashKey || null;
14
+ this.sequenceNumber = null; // Será atribuído pelo shard
15
+ this.timestamp = new Date().toISOString();
16
+ this.approximateArrivalTimestamp = new Date();
17
+
18
+ // Metadados
19
+ this.shardId = config.shardId || null;
20
+ this.encryptionType = config.encryptionType || 'NONE';
21
+
22
+ // Dados do consumidor (quando acessado via consumer)
23
+ this.consumerSequenceNumber = null;
24
+ this.offsetInMilliseconds = null;
25
+ }
26
+
27
+ /**
28
+ * Retorna record em formato Kinesis (sem dados internos)
29
+ */
30
+ toJSON() {
31
+ return {
32
+ Data: this.data,
33
+ PartitionKey: this.partitionKey,
34
+ ExplicitHashKey: this.explicitHashKey,
35
+ SequenceNumber: this.sequenceNumber,
36
+ ApproximateArrivalTimestamp: this.approximateArrivalTimestamp.toISOString(),
37
+ EncryptionType: this.encryptionType
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Retorna record em formato consumer
43
+ */
44
+ toConsumerJSON() {
45
+ return {
46
+ Data: this.data,
47
+ PartitionKey: this.partitionKey,
48
+ SequenceNumber: this.consumerSequenceNumber || this.sequenceNumber,
49
+ ApproximateArrivalTimestamp: this.approximateArrivalTimestamp.toISOString(),
50
+ EncryptionType: this.encryptionType
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Obtém tamanho do record em bytes
56
+ */
57
+ getSize() {
58
+ return Buffer.byteLength(this.data) + Buffer.byteLength(this.partitionKey);
59
+ }
60
+
61
+ /**
62
+ * Marca record como consumido
63
+ */
64
+ markAsConsumed(consumerArn, sequenceNumber) {
65
+ this.consumerSequenceNumber = sequenceNumber;
66
+ this.offsetInMilliseconds = Date.now();
67
+ }
68
+ }
69
+
70
+ module.exports = Record;
@@ -0,0 +1,280 @@
1
+ /**
2
+ * Kinesis Data Streams - Stream Model
3
+ * Representa um stream Kinesis com seus shards, registros e metadados
4
+ */
5
+
6
+ const { v4: uuidv4 } = require('uuid');
7
+
8
+ class Stream {
9
+ constructor(streamName, config = {}) {
10
+ this.streamName = streamName;
11
+ this.streamArn = `arn:aws:kinesis:us-east-1:123456789012:stream/${streamName}`;
12
+ this.streamStatus = 'ACTIVE';
13
+ this.streamModeDetails = {
14
+ streamMode: config.streamMode || 'PROVISIONED'
15
+ };
16
+
17
+ // Configuração de shards (só para PROVISIONED)
18
+ this.shardCount = config.shardCount || 1;
19
+ this.shards = [];
20
+ this.initializeShards();
21
+
22
+ // Metadados
23
+ this.creationTimestamp = new Date().toISOString();
24
+ this.lastUpdateTimestamp = new Date().toISOString();
25
+ this.retentionPeriod = config.retentionPeriod || 24; // horas
26
+ this.enhancedMonitoring = config.enhancedMonitoring || ['ALL'];
27
+
28
+ // Consumidores
29
+ this.consumers = new Map();
30
+ this.consumerCount = 0;
31
+
32
+ // Estatísticas
33
+ this.recordCount = 0;
34
+ this.incomingRecords = 0;
35
+ this.incomingBytes = 0;
36
+
37
+ // Tags
38
+ this.tags = config.tags || {};
39
+ }
40
+
41
+ /**
42
+ * Inicializa shards
43
+ */
44
+ initializeShards() {
45
+ this.shards = [];
46
+ for (let i = 0; i < this.shardCount; i++) {
47
+ this.shards.push(new Shard(i, this.streamName));
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Adiciona um shard
53
+ */
54
+ addShard() {
55
+ const newShard = new Shard(this.shards.length, this.streamName);
56
+ this.shards.push(newShard);
57
+ this.shardCount++;
58
+ this.lastUpdateTimestamp = new Date().toISOString();
59
+ return newShard;
60
+ }
61
+
62
+ /**
63
+ * Remove um shard (merge com outro)
64
+ */
65
+ removeShard(shardId) {
66
+ const index = this.shards.findIndex(s => s.shardId === shardId);
67
+ if (index !== -1) {
68
+ this.shards.splice(index, 1);
69
+ this.shardCount--;
70
+ this.lastUpdateTimestamp = new Date().toISOString();
71
+ return true;
72
+ }
73
+ return false;
74
+ }
75
+
76
+ /**
77
+ * Obtém shard por ID
78
+ */
79
+ getShard(shardId) {
80
+ return this.shards.find(s => s.shardId === shardId);
81
+ }
82
+
83
+ /**
84
+ * Obtém todos os shards
85
+ */
86
+ getAllShards() {
87
+ return this.shards;
88
+ }
89
+
90
+ /**
91
+ * Seleciona shard baseado em partition key
92
+ */
93
+ selectShardByPartitionKey(partitionKey) {
94
+ if (this.shards.length === 0) return null;
95
+
96
+ // Hash simples para distribuição uniforme
97
+ let hash = 0;
98
+ for (let i = 0; i < partitionKey.length; i++) {
99
+ const char = partitionKey.charCodeAt(i);
100
+ hash = ((hash << 5) - hash) + char;
101
+ hash = hash & hash; // Converte para 32-bit
102
+ }
103
+
104
+ const shardIndex = Math.abs(hash) % this.shards.length;
105
+ return this.shards[shardIndex];
106
+ }
107
+
108
+ /**
109
+ * Registra um consumidor
110
+ */
111
+ registerConsumer(consumerArn, consumerName) {
112
+ if (!this.consumers.has(consumerArn)) {
113
+ this.consumers.set(consumerArn, {
114
+ consumerArn,
115
+ consumerName,
116
+ consumerStatus: 'ACTIVE',
117
+ consumerCreationTimestamp: new Date().toISOString(),
118
+ shardConsumers: new Map() // Map de shardId -> lastSequenceNumber
119
+ });
120
+ this.consumerCount++;
121
+ }
122
+ return this.consumers.get(consumerArn);
123
+ }
124
+
125
+ /**
126
+ * Remove consumidor
127
+ */
128
+ removeConsumer(consumerArn) {
129
+ if (this.consumers.has(consumerArn)) {
130
+ this.consumers.delete(consumerArn);
131
+ this.consumerCount--;
132
+ return true;
133
+ }
134
+ return false;
135
+ }
136
+
137
+ /**
138
+ * Obtém consumidor
139
+ */
140
+ getConsumer(consumerArn) {
141
+ return this.consumers.get(consumerArn);
142
+ }
143
+
144
+ /**
145
+ * Atualiza estatísticas de incoming records
146
+ */
147
+ recordPutRecord(recordData) {
148
+ this.recordCount++;
149
+ this.incomingRecords++;
150
+ this.incomingBytes += Buffer.byteLength(JSON.stringify(recordData));
151
+ this.lastUpdateTimestamp = new Date().toISOString();
152
+ }
153
+
154
+ /**
155
+ * Retorna descrição do stream
156
+ */
157
+ describe() {
158
+ return {
159
+ StreamDescription: {
160
+ StreamName: this.streamName,
161
+ StreamArn: this.streamArn,
162
+ StreamStatus: this.streamStatus,
163
+ StreamModeDetails: this.streamModeDetails,
164
+ StreamCreationTimestamp: this.creationTimestamp,
165
+ Shards: this.shards.map(s => s.toJSON()),
166
+ HasMoreShards: false,
167
+ RetentionPeriod: this.retentionPeriod,
168
+ StreamLastUpdateTimestamp: this.lastUpdateTimestamp,
169
+ EnhancedMonitoring: this.enhancedMonitoring,
170
+ EncryptionType: 'NONE',
171
+ KeyId: null
172
+ }
173
+ };
174
+ }
175
+
176
+ toJSON() {
177
+ return {
178
+ streamName: this.streamName,
179
+ streamArn: this.streamArn,
180
+ streamStatus: this.streamStatus,
181
+ streamModeDetails: this.streamModeDetails,
182
+ creationTimestamp: this.creationTimestamp,
183
+ lastUpdateTimestamp: this.lastUpdateTimestamp,
184
+ shardCount: this.shardCount,
185
+ consumerCount: this.consumerCount,
186
+ retentionPeriod: this.retentionPeriod,
187
+ recordCount: this.recordCount,
188
+ incomingRecords: this.incomingRecords,
189
+ incomingBytes: this.incomingBytes
190
+ };
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Shard Model - Partição dentro de um stream
196
+ */
197
+ class Shard {
198
+ constructor(shardIndex, streamName) {
199
+ this.shardId = `shardId-${String(shardIndex).padStart(12, '0')}`;
200
+ this.shardName = `${streamName}-shard-${shardIndex}`;
201
+ this.shardStatus = 'ACTIVE';
202
+ this.parentShardId = null;
203
+ this.adjacentParentShardId = null;
204
+
205
+ // Range de partition keys que este shard cobre
206
+ this.hashKeyRange = {
207
+ startingHashKey: '0',
208
+ endingHashKey: '18446744073709551615' // uint64 max
209
+ };
210
+
211
+ // Seqüência de números
212
+ this.sequenceNumberRange = {
213
+ startingSequenceNumber: '0',
214
+ endingSequenceNumber: null
215
+ };
216
+
217
+ this.creationTimestamp = new Date().toISOString();
218
+
219
+ // Records armazenados neste shard
220
+ this.records = [];
221
+ this.recordCount = 0;
222
+ this.lastSequenceNumber = 0;
223
+ }
224
+
225
+ /**
226
+ * Adiciona um record ao shard
227
+ */
228
+ addRecord(record) {
229
+ record.sequenceNumber = (++this.lastSequenceNumber).toString();
230
+ this.records.push(record);
231
+ this.recordCount++;
232
+ return record;
233
+ }
234
+
235
+ /**
236
+ * Obtém records a partir de um sequence number
237
+ */
238
+ getRecords(startingSequenceNumber = null, limit = 100) {
239
+ let startIndex = 0;
240
+
241
+ if (startingSequenceNumber) {
242
+ const seqNum = parseInt(startingSequenceNumber);
243
+ startIndex = this.records.findIndex(r => parseInt(r.sequenceNumber) > seqNum);
244
+ if (startIndex === -1) {
245
+ startIndex = this.records.length;
246
+ }
247
+ }
248
+
249
+ return this.records.slice(startIndex, startIndex + limit);
250
+ }
251
+
252
+ /**
253
+ * Obtém último record
254
+ */
255
+ getLastSequenceNumber() {
256
+ return this.lastSequenceNumber.toString();
257
+ }
258
+
259
+ /**
260
+ * Retorna todos os records
261
+ */
262
+ getAllRecords() {
263
+ return this.records;
264
+ }
265
+
266
+ toJSON() {
267
+ return {
268
+ ShardId: this.shardId,
269
+ ShardStatus: this.shardStatus,
270
+ HashKeyRange: this.hashKeyRange,
271
+ SequenceNumberRange: this.sequenceNumberRange,
272
+ CreationTimestamp: this.creationTimestamp
273
+ };
274
+ }
275
+ }
276
+
277
+ module.exports = {
278
+ Stream,
279
+ Shard
280
+ };