@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,225 @@
1
+ /**
2
+ * CloudFront Cache Engine
3
+ * Gerencia caching de objetos com TTL
4
+ */
5
+
6
+ const logger = require('../../utils/logger');
7
+
8
+ class CacheEngine {
9
+ constructor(config = {}) {
10
+ this.cache = new Map();
11
+ this.stats = {
12
+ hits: 0,
13
+ misses: 0,
14
+ evictions: 0,
15
+ bytesStored: 0
16
+ };
17
+ this.maxSize = config.maxSize || 100 * 1024 * 1024; // 100MB default
18
+ this.cleanupInterval = setInterval(() => this.cleanup(), 60000); // Cleanup every minute
19
+ }
20
+
21
+ /**
22
+ * Gerar chave de cache
23
+ */
24
+ generateKey(distributionId, path, queryString = '') {
25
+ return `${distributionId}:${path}${queryString ? '?' + queryString : ''}`;
26
+ }
27
+
28
+ /**
29
+ * Get com validação de TTL
30
+ */
31
+ get(key) {
32
+ const entry = this.cache.get(key);
33
+
34
+ if (!entry) {
35
+ this.stats.misses++;
36
+ logger.debug(`Cache MISS: ${key}`);
37
+ return null;
38
+ }
39
+
40
+ // Verificar se expirou
41
+ if (entry.expiresAt < Date.now()) {
42
+ this.cache.delete(key);
43
+ this.stats.misses++;
44
+ logger.debug(`Cache EXPIRED: ${key}`);
45
+ return null;
46
+ }
47
+
48
+ // Hit
49
+ this.stats.hits++;
50
+ entry.hits++;
51
+ entry.lastAccessed = Date.now();
52
+ logger.debug(`Cache HIT: ${key} (hits: ${entry.hits})`);
53
+
54
+ return {
55
+ data: entry.data,
56
+ headers: entry.headers,
57
+ statusCode: entry.statusCode,
58
+ cacheKey: key,
59
+ xCache: 'Hit from cloudfront'
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Set com TTL
65
+ */
66
+ set(key, data, headers = {}, statusCode = 200, ttl = 86400) {
67
+ // Calcular tamanho aproximado
68
+ const size = JSON.stringify({ data, headers }).length;
69
+
70
+ // Verificar espaço disponível
71
+ if (this.stats.bytesStored + size > this.maxSize) {
72
+ this.evictLRU();
73
+ }
74
+
75
+ const now = Date.now();
76
+ const entry = {
77
+ data,
78
+ headers,
79
+ statusCode,
80
+ createdAt: now,
81
+ expiresAt: now + (ttl * 1000),
82
+ lastAccessed: now,
83
+ hits: 0,
84
+ size
85
+ };
86
+
87
+ this.cache.set(key, entry);
88
+ this.stats.bytesStored += size;
89
+
90
+ logger.debug(`Cache SET: ${key} (TTL: ${ttl}s, size: ${size} bytes)`);
91
+ }
92
+
93
+ /**
94
+ * Invalidar cache por padrão
95
+ */
96
+ invalidate(pattern) {
97
+ let invalidated = 0;
98
+
99
+ for (const key of this.cache.keys()) {
100
+ if (this.pathMatches(key.split(':')[1], pattern)) {
101
+ const entry = this.cache.get(key);
102
+ this.stats.bytesStored -= entry.size;
103
+ this.cache.delete(key);
104
+ invalidated++;
105
+ }
106
+ }
107
+
108
+ logger.info(`Cache INVALIDATE: ${pattern} (${invalidated} entries)`);
109
+ return invalidated;
110
+ }
111
+
112
+ /**
113
+ * Invalidar tudo de uma distribuição
114
+ */
115
+ invalidateDistribution(distributionId) {
116
+ let invalidated = 0;
117
+
118
+ for (const key of this.cache.keys()) {
119
+ if (key.startsWith(distributionId + ':')) {
120
+ const entry = this.cache.get(key);
121
+ this.stats.bytesStored -= entry.size;
122
+ this.cache.delete(key);
123
+ invalidated++;
124
+ }
125
+ }
126
+
127
+ logger.info(`Cache INVALIDATE DISTRIBUTION: ${distributionId} (${invalidated} entries)`);
128
+ return invalidated;
129
+ }
130
+
131
+ /**
132
+ * Verificar se path corresponde ao padrão
133
+ */
134
+ pathMatches(path, pattern) {
135
+ if (pattern === '*') return true;
136
+
137
+ const regex = new RegExp(
138
+ '^' + pattern
139
+ .replace(/\./g, '\\.')
140
+ .replace(/\*/g, '.*')
141
+ .replace(/\?/g, '.') +
142
+ '$'
143
+ );
144
+
145
+ return regex.test(path);
146
+ }
147
+
148
+ /**
149
+ * Remover item menos recentemente acessado (LRU)
150
+ */
151
+ evictLRU() {
152
+ let lruKey = null;
153
+ let lruTime = Date.now();
154
+
155
+ for (const [key, entry] of this.cache.entries()) {
156
+ if (entry.lastAccessed < lruTime) {
157
+ lruTime = entry.lastAccessed;
158
+ lruKey = key;
159
+ }
160
+ }
161
+
162
+ if (lruKey) {
163
+ const entry = this.cache.get(lruKey);
164
+ this.stats.bytesStored -= entry.size;
165
+ this.cache.delete(lruKey);
166
+ this.stats.evictions++;
167
+ logger.debug(`Cache LRU EVICTION: ${lruKey}`);
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Cleanup de entries expiradas
173
+ */
174
+ cleanup() {
175
+ const now = Date.now();
176
+ let expired = 0;
177
+
178
+ for (const [key, entry] of this.cache.entries()) {
179
+ if (entry.expiresAt < now) {
180
+ this.stats.bytesStored -= entry.size;
181
+ this.cache.delete(key);
182
+ expired++;
183
+ }
184
+ }
185
+
186
+ if (expired > 0) {
187
+ logger.debug(`Cache CLEANUP: ${expired} expired entries removed`);
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Obter estatísticas do cache
193
+ */
194
+ getStats() {
195
+ const total = this.stats.hits + this.stats.misses;
196
+ const hitRate = total > 0 ? ((this.stats.hits / total) * 100).toFixed(2) : 0;
197
+
198
+ return {
199
+ ...this.stats,
200
+ hitRate: `${hitRate}%`,
201
+ cacheSize: this.cache.size,
202
+ bytesStored: this.stats.bytesStored,
203
+ maxSize: this.maxSize
204
+ };
205
+ }
206
+
207
+ /**
208
+ * Limpar cache completamente
209
+ */
210
+ clear() {
211
+ this.cache.clear();
212
+ this.stats.bytesStored = 0;
213
+ logger.debug('Cache CLEARED');
214
+ }
215
+
216
+ /**
217
+ * Destruir e parar cleanup
218
+ */
219
+ destroy() {
220
+ clearInterval(this.cleanupInterval);
221
+ this.clear();
222
+ }
223
+ }
224
+
225
+ module.exports = CacheEngine;
@@ -0,0 +1,377 @@
1
+
2
+
3
+ /**
4
+ * Geo-Restrictions Handler for CloudFront
5
+ * Manages geographic restrictions for distributions based on country codes
6
+ */
7
+ class GeoRestrictions {
8
+ constructor() {
9
+ // ISO 3166-1 alpha-2 country code validation
10
+ this.validCountryCodes = this._initializeCountryCodes();
11
+ this.requestCache = new Map();
12
+ this.cacheExpiry = 10 * 60 * 1000; // 10 minutes
13
+ }
14
+
15
+ /**
16
+ * Check if a request should be allowed based on geo-restrictions
17
+ * @param {object} geoRestriction - GeoRestriction object from Distribution
18
+ * @param {string} clientCountry - Country code of the client (ISO 3166-1 alpha-2)
19
+ * @param {object} [requestContext] - Additional context for logging/debugging
20
+ * @returns {object} Geo-restriction check result
21
+ */
22
+ checkGeoRestriction(geoRestriction, clientCountry, requestContext = {}) {
23
+ if (!geoRestriction || !geoRestriction.restrictionType) {
24
+ return {
25
+ isAllowed: true,
26
+ reason: 'NO_RESTRICTIONS'
27
+ };
28
+ }
29
+
30
+ const normalizedCountry = clientCountry?.toUpperCase();
31
+ const { restrictionType, locations = [] } = geoRestriction;
32
+
33
+ // Validate country code
34
+ if (!this._isValidCountryCode(normalizedCountry)) {
35
+ return {
36
+ isAllowed: false,
37
+ reason: 'INVALID_COUNTRY_CODE',
38
+ country: clientCountry,
39
+ error: `Invalid country code: ${clientCountry}`
40
+ };
41
+ }
42
+
43
+ let isAllowed = false;
44
+ let reason = '';
45
+
46
+ if (restrictionType === 'whitelist') {
47
+ isAllowed = locations.includes(normalizedCountry);
48
+ reason = isAllowed ? 'WHITELISTED_COUNTRY' : 'COUNTRY_NOT_WHITELISTED';
49
+ } else if (restrictionType === 'blacklist') {
50
+ isAllowed = !locations.includes(normalizedCountry);
51
+ reason = isAllowed ? 'COUNTRY_NOT_BLACKLISTED' : 'BLACKLISTED_COUNTRY';
52
+ } else {
53
+ return {
54
+ isAllowed: true,
55
+ reason: 'UNKNOWN_RESTRICTION_TYPE'
56
+ };
57
+ }
58
+
59
+ return {
60
+ isAllowed: isAllowed,
61
+ reason: reason,
62
+ country: normalizedCountry,
63
+ restrictionType: restrictionType,
64
+ timestamp: new Date().toISOString(),
65
+ requestContext: requestContext
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Validate geo-restriction configuration
71
+ * @param {object} geoRestriction - GeoRestriction object
72
+ * @returns {object} Validation result
73
+ */
74
+ validateGeoRestriction(geoRestriction) {
75
+ const errors = [];
76
+ const warnings = [];
77
+
78
+ if (!geoRestriction) {
79
+ return {
80
+ isValid: true,
81
+ message: 'No geo-restrictions configured'
82
+ };
83
+ }
84
+
85
+ // Validate restriction type
86
+ const validTypes = ['whitelist', 'blacklist'];
87
+ if (!validTypes.includes(geoRestriction.restrictionType)) {
88
+ errors.push(
89
+ `Invalid restriction type: ${geoRestriction.restrictionType}. Must be 'whitelist' or 'blacklist'`
90
+ );
91
+ }
92
+
93
+ // Validate locations
94
+ if (!Array.isArray(geoRestriction.locations)) {
95
+ errors.push('Locations must be an array');
96
+ } else {
97
+ const invalidCodes = [];
98
+ for (const code of geoRestriction.locations) {
99
+ if (!this._isValidCountryCode(code)) {
100
+ invalidCodes.push(code);
101
+ }
102
+ }
103
+
104
+ if (invalidCodes.length > 0) {
105
+ errors.push(
106
+ `Invalid country codes: ${invalidCodes.join(', ')}`
107
+ );
108
+ }
109
+
110
+ if (geoRestriction.locations.length === 0) {
111
+ warnings.push('No locations specified in geo-restriction');
112
+ }
113
+
114
+ // Warn if quantity doesn't match
115
+ if (
116
+ geoRestriction.quantity !== geoRestriction.locations.length
117
+ ) {
118
+ warnings.push(
119
+ `Quantity mismatch: specified ${geoRestriction.quantity} but has ${geoRestriction.locations.length} locations`
120
+ );
121
+ }
122
+ }
123
+
124
+ return {
125
+ isValid: errors.length === 0,
126
+ errors: errors,
127
+ warnings: warnings
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Get country information
133
+ * @param {string} countryCode - ISO 3166-1 alpha-2 country code
134
+ * @returns {object} Country information
135
+ */
136
+ getCountryInfo(countryCode) {
137
+ const code = countryCode?.toUpperCase();
138
+
139
+ if (!this._isValidCountryCode(code)) {
140
+ return null;
141
+ }
142
+
143
+ return {
144
+ code: code,
145
+ name: this._getCountryName(code),
146
+ region: this._getCountryRegion(code)
147
+ };
148
+ }
149
+
150
+ /**
151
+ * Get list of all valid country codes
152
+ * @returns {string[]} Array of ISO 3166-1 alpha-2 country codes
153
+ */
154
+ getAllCountryCodes() {
155
+ return Array.from(this.validCountryCodes);
156
+ }
157
+
158
+ /**
159
+ * Get countries by region
160
+ * @param {string} region - Region name (e.g., 'EU', 'ASIA', 'AMERICAS')
161
+ * @returns {string[]} Array of country codes
162
+ */
163
+ getCountriesByRegion(region) {
164
+ const regionCode = region?.toUpperCase();
165
+ const countries = [];
166
+
167
+ const regionMap = {
168
+ 'EU': ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE'],
169
+ 'ASIA': ['CN', 'JP', 'KR', 'IN', 'TH', 'VN', 'SG', 'MY', 'PH', 'ID', 'HK', 'TW', 'BD', 'PK', 'LK', 'KZ', 'UZ', 'TM', 'KG'],
170
+ 'AMERICAS': ['US', 'CA', 'MX', 'BR', 'AR', 'CL', 'CO', 'PE', 'VE', 'EC', 'CU', 'DO', 'JM', 'TT'],
171
+ 'AFRICA': ['NG', 'EG', 'ZA', 'KE', 'ET', 'GH', 'TZ', 'UG', 'DZ', 'MA', 'TN', 'SD', 'AO', 'ZM', 'ZW'],
172
+ 'OCEANIA': ['AU', 'NZ', 'FJ', 'PG', 'SB', 'VU']
173
+ };
174
+
175
+ return regionMap[regionCode] || [];
176
+ }
177
+
178
+ /**
179
+ * Generate a geo-restriction report
180
+ * @param {object} geoRestriction - GeoRestriction object
181
+ * @returns {object} Detailed report
182
+ */
183
+ generateReport(geoRestriction) {
184
+ if (!geoRestriction) {
185
+ return {
186
+ status: 'NO_RESTRICTIONS',
187
+ report: 'No geo-restrictions configured'
188
+ };
189
+ }
190
+
191
+ const validation = this.validateGeoRestriction(geoRestriction);
192
+ const locations = geoRestriction.locations || [];
193
+ const countryDetails = locations.map(code => ({
194
+ code: code,
195
+ name: this._getCountryName(code),
196
+ region: this._getCountryRegion(code)
197
+ }));
198
+
199
+ return {
200
+ status: 'ACTIVE',
201
+ restrictionType: geoRestriction.restrictionType,
202
+ totalCountries: locations.length,
203
+ countries: countryDetails,
204
+ validation: validation,
205
+ timestamp: new Date().toISOString()
206
+ };
207
+ }
208
+
209
+ /**
210
+ * Estimate impact of geo-restrictions (rough statistics)
211
+ * @param {object} geoRestriction - GeoRestriction object
212
+ * @returns {object} Impact estimate
213
+ */
214
+ estimateRestrictionImpact(geoRestriction) {
215
+ if (!geoRestriction || !geoRestriction.locations) {
216
+ return {
217
+ estimatedAffected: 0,
218
+ estimatedAccessible: 100
219
+ };
220
+ }
221
+
222
+ // Rough estimates for demonstration (these would come from real data in production)
223
+ const globalPopulation = 100; // Represents 100%
224
+ const locationCount = geoRestriction.locations.length;
225
+ const countryCount = this.getAllCountryCodes().length;
226
+
227
+ let estimatedAffected = (locationCount / countryCount) * globalPopulation;
228
+
229
+ if (geoRestriction.restrictionType === 'whitelist') {
230
+ // Whitelisting specific countries
231
+ estimatedAffected = (locationCount / countryCount) * globalPopulation;
232
+ } else if (geoRestriction.restrictionType === 'blacklist') {
233
+ // Blacklisting specific countries
234
+ estimatedAffected = (locationCount / countryCount) * globalPopulation;
235
+ }
236
+
237
+ return {
238
+ estimatedAffected: Math.round(estimatedAffected * 100) / 100,
239
+ estimatedAccessible: Math.round((globalPopulation - estimatedAffected) * 100) / 100,
240
+ locationCount: locationCount,
241
+ restrictionType: geoRestriction.restrictionType
242
+ };
243
+ }
244
+
245
+ /**
246
+ * Create a geo-restriction from country names (convenience method)
247
+ * @param {string} type - 'whitelist' or 'blacklist'
248
+ * @param {string[]} countryNames - Array of country names
249
+ * @returns {object} Geo-restriction object
250
+ */
251
+ createFromCountryNames(type, countryNames) {
252
+ const countryCodes = countryNames
253
+ .map(name => this._getCountryCodeByName(name))
254
+ .filter(code => code !== null);
255
+
256
+ return {
257
+ restrictionType: type,
258
+ quantity: countryCodes.length,
259
+ locations: countryCodes
260
+ };
261
+ }
262
+
263
+ /**
264
+ * Clear cache
265
+ */
266
+ clearCache() {
267
+ this.requestCache.clear();
268
+ }
269
+
270
+ /**
271
+ * Private Methods
272
+ */
273
+
274
+ _initializeCountryCodes() {
275
+ // ISO 3166-1 alpha-2 country codes
276
+ return new Set([
277
+ 'AD', 'AE', 'AF', 'AG', 'AI', 'AL', 'AM', 'AO', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AW', 'AX', 'AZ',
278
+ 'BA', 'BB', 'BD', 'BE', 'BF', 'BG', 'BH', 'BI', 'BJ', 'BL', 'BM', 'BN', 'BO', 'BQ', 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', 'BZ',
279
+ 'CA', 'CC', 'CD', 'CF', 'CG', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', 'CO', 'CR', 'CU', 'CV', 'CW', 'CX', 'CY', 'CZ',
280
+ 'DE', 'DJ', 'DK', 'DM', 'DO', 'DZ',
281
+ 'EC', 'EE', 'EG', 'EH', 'ER', 'ES', 'ET',
282
+ 'FI', 'FJ', 'FK', 'FM', 'FO', 'FR',
283
+ 'GA', 'GB', 'GD', 'GE', 'GF', 'GG', 'GH', 'GI', 'GL', 'GM', 'GN', 'GP', 'GQ', 'GR', 'GS', 'GT', 'GU', 'GW', 'GY',
284
+ 'HK', 'HM', 'HN', 'HR', 'HT', 'HU',
285
+ 'ID', 'IE', 'IL', 'IM', 'IN', 'IO', 'IQ', 'IR', 'IS', 'IT',
286
+ 'JE', 'JM', 'JO', 'JP',
287
+ 'KE', 'KG', 'KH', 'KI', 'KM', 'KN', 'KP', 'KR', 'KW', 'KY', 'KZ',
288
+ 'LA', 'LB', 'LC', 'LI', 'LK', 'LR', 'LS', 'LT', 'LU', 'LV', 'LY',
289
+ 'MA', 'MC', 'MD', 'ME', 'MF', 'MG', 'MH', 'MK', 'ML', 'MM', 'MN', 'MO', 'MP', 'MQ', 'MR', 'MS', 'MT', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ',
290
+ 'NA', 'NC', 'NE', 'NF', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NU', 'NZ',
291
+ 'OM',
292
+ 'PA', 'PE', 'PF', 'PG', 'PH', 'PK', 'PL', 'PM', 'PN', 'PR', 'PS', 'PT', 'PW', 'PY',
293
+ 'QA',
294
+ 'RE', 'RO', 'RS', 'RU', 'RW',
295
+ 'SA', 'SB', 'SC', 'SD', 'SE', 'SG', 'SH', 'SI', 'SJ', 'SK', 'SL', 'SM', 'SN', 'SO', 'SR', 'SS', 'ST', 'SV', 'SX', 'SY', 'SZ',
296
+ 'TC', 'TD', 'TF', 'TG', 'TH', 'TJ', 'TK', 'TL', 'TM', 'TN', 'TO', 'TR', 'TT', 'TV', 'TW', 'TZ',
297
+ 'UA', 'UG', 'UM', 'US', 'UY', 'UZ',
298
+ 'VA', 'VC', 'VE', 'VG', 'VI', 'VN', 'VU',
299
+ 'WF', 'WS',
300
+ 'YE', 'YT',
301
+ 'ZA', 'ZM', 'ZW'
302
+ ]);
303
+ }
304
+
305
+ _isValidCountryCode(code) {
306
+ if (!code || typeof code !== 'string') return false;
307
+ return this.validCountryCodes.has(code.toUpperCase());
308
+ }
309
+
310
+ _getCountryName(code) {
311
+ const countryNames = {
312
+ 'US': 'United States',
313
+ 'GB': 'United Kingdom',
314
+ 'CA': 'Canada',
315
+ 'AU': 'Australia',
316
+ 'JP': 'Japan',
317
+ 'DE': 'Germany',
318
+ 'FR': 'France',
319
+ 'BR': 'Brazil',
320
+ 'IN': 'India',
321
+ 'CN': 'China',
322
+ 'MX': 'Mexico',
323
+ 'ZA': 'South Africa',
324
+ 'SG': 'Singapore',
325
+ 'NZ': 'New Zealand',
326
+ 'NL': 'Netherlands',
327
+ 'SE': 'Sweden',
328
+ 'NO': 'Norway',
329
+ 'ES': 'Spain',
330
+ 'IT': 'Italy',
331
+ 'KR': 'South Korea',
332
+ // Add more as needed
333
+ };
334
+
335
+ return countryNames[code] || code;
336
+ }
337
+
338
+ _getCountryRegion(code) {
339
+ const regionMap = {
340
+ 'US': 'AMERICAS', 'CA': 'AMERICAS', 'MX': 'AMERICAS', 'BR': 'AMERICAS', 'AR': 'AMERICAS',
341
+ 'GB': 'EU', 'DE': 'EU', 'FR': 'EU', 'IT': 'EU', 'ES': 'EU', 'NL': 'EU', 'SE': 'EU', 'NO': 'EU',
342
+ 'JP': 'ASIA', 'CN': 'ASIA', 'IN': 'ASIA', 'SG': 'ASIA', 'KR': 'ASIA', 'AU': 'OCEANIA', 'NZ': 'OCEANIA',
343
+ 'ZA': 'AFRICA', 'EG': 'AFRICA',
344
+ };
345
+
346
+ return regionMap[code] || 'UNKNOWN';
347
+ }
348
+
349
+ _getCountryCodeByName(name) {
350
+ const nameToCode = {
351
+ 'United States': 'US',
352
+ 'United Kingdom': 'GB',
353
+ 'Canada': 'CA',
354
+ 'Australia': 'AU',
355
+ 'Japan': 'JP',
356
+ 'Germany': 'DE',
357
+ 'France': 'FR',
358
+ 'Brazil': 'BR',
359
+ 'India': 'IN',
360
+ 'China': 'CN',
361
+ 'Mexico': 'MX',
362
+ 'South Africa': 'ZA',
363
+ 'Singapore': 'SG',
364
+ 'New Zealand': 'NZ',
365
+ 'Netherlands': 'NL',
366
+ 'Sweden': 'SE',
367
+ 'Norway': 'NO',
368
+ 'Spain': 'ES',
369
+ 'Italy': 'IT',
370
+ 'South Korea': 'KR',
371
+ };
372
+
373
+ return nameToCode[name] || null;
374
+ }
375
+ }
376
+
377
+ module.exports = GeoRestrictions;