local-serverless-stack 0.0.1

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 (49) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/LICENSE +21 -0
  3. package/README.md +653 -0
  4. package/bin/cli.js +204 -0
  5. package/dist/server/dev/dynamo-proxy.d.ts +3 -0
  6. package/dist/server/dev/dynamo-proxy.d.ts.map +1 -0
  7. package/dist/server/dev/dynamo-proxy.js +35 -0
  8. package/dist/server/dev/dynamo-proxy.js.map +1 -0
  9. package/dist/server/index.d.ts +2 -0
  10. package/dist/server/index.d.ts.map +1 -0
  11. package/dist/server/index.js +67 -0
  12. package/dist/server/index.js.map +1 -0
  13. package/dist/server/routes/resources.d.ts +3 -0
  14. package/dist/server/routes/resources.d.ts.map +1 -0
  15. package/dist/server/routes/resources.js +16 -0
  16. package/dist/server/routes/resources.js.map +1 -0
  17. package/dist/server/routes/services.d.ts +5 -0
  18. package/dist/server/routes/services.d.ts.map +1 -0
  19. package/dist/server/routes/services.js +217 -0
  20. package/dist/server/routes/services.js.map +1 -0
  21. package/dist/server/services/cache-manager.d.ts +21 -0
  22. package/dist/server/services/cache-manager.d.ts.map +1 -0
  23. package/dist/server/services/cache-manager.js +70 -0
  24. package/dist/server/services/cache-manager.js.map +1 -0
  25. package/dist/server/services/cloudformation-parser.d.ts +88 -0
  26. package/dist/server/services/cloudformation-parser.d.ts.map +1 -0
  27. package/dist/server/services/cloudformation-parser.js +112 -0
  28. package/dist/server/services/cloudformation-parser.js.map +1 -0
  29. package/dist/server/services/localstack-manager.d.ts +23 -0
  30. package/dist/server/services/localstack-manager.d.ts.map +1 -0
  31. package/dist/server/services/localstack-manager.js +142 -0
  32. package/dist/server/services/localstack-manager.js.map +1 -0
  33. package/dist/server/services/process-manager.d.ts +41 -0
  34. package/dist/server/services/process-manager.d.ts.map +1 -0
  35. package/dist/server/services/process-manager.js +119 -0
  36. package/dist/server/services/process-manager.js.map +1 -0
  37. package/dist/server/services/resource-provisioner.d.ts +37 -0
  38. package/dist/server/services/resource-provisioner.d.ts.map +1 -0
  39. package/dist/server/services/resource-provisioner.js +539 -0
  40. package/dist/server/services/resource-provisioner.js.map +1 -0
  41. package/dist/ui/assets/index-7WT0rkfU.css +1 -0
  42. package/dist/ui/assets/index-M0lXeUj-.js +19 -0
  43. package/dist/ui/index.html +16 -0
  44. package/package.json +89 -0
  45. package/packages/serverless-plugin/README.md +95 -0
  46. package/packages/serverless-plugin/dist/index.d.ts +2 -0
  47. package/packages/serverless-plugin/dist/index.d.ts.map +1 -0
  48. package/packages/serverless-plugin/dist/index.js +88 -0
  49. package/packages/serverless-plugin/dist/index.js.map +1 -0
@@ -0,0 +1,37 @@
1
+ import type { Resource } from './cloudformation-parser.js';
2
+ export declare class ResourceProvisioner {
3
+ private dynamoClient;
4
+ private sqsClient;
5
+ private snsClient;
6
+ private lambdaClient;
7
+ private provisionedResources;
8
+ private serviceMetadata;
9
+ constructor();
10
+ provisionResources(serviceName: string, resources: Resource[], metadata?: {
11
+ invokePort?: number;
12
+ invokeUrl?: string;
13
+ }): Promise<void>;
14
+ private createDynamoDBTable;
15
+ private createSQSQueue;
16
+ private createSNSTopic;
17
+ private generateProxyLambdaCode;
18
+ private createEventSourceMapping;
19
+ private ensureLambdaProxyExists;
20
+ private resolveLambdaFunctionName;
21
+ private convertCamelCaseToKebab;
22
+ private resolveEventSourceArn;
23
+ listAllResources(): Promise<{
24
+ tables: string[];
25
+ queues: string[];
26
+ topics: string[];
27
+ }>;
28
+ private listDynamoDBTables;
29
+ private listSQSQueues;
30
+ private listSNSTopics;
31
+ cleanupResources(serviceName: string): Promise<void>;
32
+ private deleteDynamoDBTable;
33
+ private deleteSQSQueue;
34
+ private deleteSNSTopic;
35
+ private deleteEventSourceMapping;
36
+ }
37
+ //# sourceMappingURL=resource-provisioner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-provisioner.d.ts","sourceRoot":"","sources":["../../../src/server/services/resource-provisioner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,QAAQ,EAKT,MAAM,4BAA4B,CAAC;AAGpC,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,oBAAoB,CAAkC;IAC9D,OAAO,CAAC,eAAe,CAAgE;;IAUjF,kBAAkB,CACtB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,QAAQ,EAAE,EACrB,QAAQ,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GACrD,OAAO,CAAC,IAAI,CAAC;YAwDF,mBAAmB;YAyDnB,cAAc;YAiCd,cAAc;IAiB5B,OAAO,CAAC,uBAAuB;YAoEjB,wBAAwB;YA0CxB,uBAAuB;IAiDrC,OAAO,CAAC,yBAAyB;IAoBjC,OAAO,CAAC,uBAAuB;YAOjB,qBAAqB;IAqE7B,gBAAgB,IAAI,OAAO,CAAC;QAChC,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;YAUY,kBAAkB;YASlB,aAAa;YASb,aAAa;IAYrB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAyC5C,mBAAmB;YAYnB,cAAc;YAiBd,cAAc;YAkBd,wBAAwB;CA6BvC"}
@@ -0,0 +1,539 @@
1
+ import { DynamoDBClient, CreateTableCommand, ListTablesCommand, DeleteTableCommand } from '@aws-sdk/client-dynamodb';
2
+ import { SQSClient, CreateQueueCommand, ListQueuesCommand, GetQueueAttributesCommand, DeleteQueueCommand, GetQueueUrlCommand } from '@aws-sdk/client-sqs';
3
+ import { SNSClient, CreateTopicCommand, ListTopicsCommand, DeleteTopicCommand } from '@aws-sdk/client-sns';
4
+ import { LambdaClient, CreateFunctionCommand, GetFunctionCommand, DeleteFunctionCommand } from '@aws-sdk/client-lambda';
5
+ import { CreateEventSourceMappingCommand, ListEventSourceMappingsCommand, DeleteEventSourceMappingCommand } from '@aws-sdk/client-lambda';
6
+ import { LocalStackManager } from './localstack-manager.js';
7
+ import AdmZip from 'adm-zip';
8
+ export class ResourceProvisioner {
9
+ dynamoClient;
10
+ sqsClient;
11
+ snsClient;
12
+ lambdaClient;
13
+ provisionedResources = new Map();
14
+ serviceMetadata = new Map();
15
+ constructor() {
16
+ const config = LocalStackManager.getInstance().getConfig();
17
+ this.dynamoClient = new DynamoDBClient(config);
18
+ this.sqsClient = new SQSClient(config);
19
+ this.snsClient = new SNSClient(config);
20
+ this.lambdaClient = new LambdaClient(config);
21
+ }
22
+ async provisionResources(serviceName, resources, metadata) {
23
+ const provisioned = new Set();
24
+ // Store service metadata for Lambda proxy creation
25
+ if (metadata?.invokePort) {
26
+ this.serviceMetadata.set(serviceName, {
27
+ invokePort: metadata.invokePort,
28
+ invokeUrl: metadata.invokeUrl || `http://host.docker.internal:${metadata.invokePort}`,
29
+ });
30
+ }
31
+ // First pass: Create infrastructure resources (DynamoDB, SQS, SNS)
32
+ // Lambda functions are handled by Serverless Offline, not LocalStack
33
+ for (const resource of resources) {
34
+ try {
35
+ switch (resource.type) {
36
+ case 'dynamodb':
37
+ await this.createDynamoDBTable(resource);
38
+ provisioned.add(`dynamodb:${resource.name}`);
39
+ break;
40
+ case 'sqs':
41
+ await this.createSQSQueue(resource);
42
+ provisioned.add(`sqs:${resource.name}`);
43
+ break;
44
+ case 'sns':
45
+ await this.createSNSTopic(resource);
46
+ provisioned.add(`sns:${resource.name}`);
47
+ break;
48
+ }
49
+ }
50
+ catch (error) {
51
+ if (!error.message?.includes('already exists')) {
52
+ const resourceName = 'name' in resource ? resource.name : resource.functionName;
53
+ console.error(`Failed to provision ${resource.type}:${resourceName}:`, error.message);
54
+ }
55
+ }
56
+ }
57
+ // Second pass: Create event source mappings (which will create Lambda proxies if needed)
58
+ const eventSources = resources.filter(r => r.type === 'event-source');
59
+ for (const eventSource of eventSources) {
60
+ try {
61
+ await this.createEventSourceMapping(serviceName, eventSource);
62
+ provisioned.add(`event-source:${eventSource.functionName}`);
63
+ }
64
+ catch (error) {
65
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
66
+ const errorName = error instanceof Error && 'name' in error ? error.name : '';
67
+ if (!errorMessage.includes('already exists') && errorName !== 'ResourceConflictException') {
68
+ console.error(`Failed to provision event-source for ${eventSource.functionName}:`, errorMessage);
69
+ }
70
+ }
71
+ }
72
+ this.provisionedResources.set(serviceName, provisioned);
73
+ console.log(`✅ Provisioned ${provisioned.size} resources for ${serviceName}`);
74
+ }
75
+ async createDynamoDBTable(resource) {
76
+ try {
77
+ // Ensure attribute definitions include all attributes referenced by KeySchema and GSIs/LSIs
78
+ const attributeDefinitions = [...(resource.attributeDefinitions || [])];
79
+ const defined = new Set(attributeDefinitions.map(a => a.AttributeName));
80
+ const required = new Set();
81
+ resource.keySchema.forEach(k => required.add(k.AttributeName));
82
+ // Look for GSI/LSI keys inside the resource
83
+ const gsiList = resource.globalSecondaryIndexes || [];
84
+ const lsiList = resource.localSecondaryIndexes || [];
85
+ const collectIndexKeys = (indexes) => {
86
+ if (!indexes)
87
+ return;
88
+ indexes.forEach(idx => {
89
+ (idx.KeySchema || []).forEach(k => required.add(k.AttributeName));
90
+ });
91
+ };
92
+ collectIndexKeys(gsiList);
93
+ collectIndexKeys(lsiList);
94
+ for (const attr of required) {
95
+ if (!defined.has(attr)) {
96
+ attributeDefinitions.push({ AttributeName: attr, AttributeType: 'S' });
97
+ }
98
+ }
99
+ await this.dynamoClient.send(new CreateTableCommand({
100
+ TableName: resource.name,
101
+ KeySchema: resource.keySchema,
102
+ AttributeDefinitions: attributeDefinitions,
103
+ BillingMode: resource.billingMode,
104
+ GlobalSecondaryIndexes: (resource.globalSecondaryIndexes || []).length
105
+ ? resource.globalSecondaryIndexes
106
+ : undefined,
107
+ LocalSecondaryIndexes: (resource.localSecondaryIndexes || []).length
108
+ ? resource.localSecondaryIndexes
109
+ : undefined,
110
+ StreamSpecification: resource.streamEnabled
111
+ ? {
112
+ StreamEnabled: true,
113
+ StreamViewType: 'NEW_AND_OLD_IMAGES',
114
+ }
115
+ : undefined,
116
+ }));
117
+ console.log(` ✓ Created DynamoDB table: ${resource.name}`);
118
+ }
119
+ catch (error) {
120
+ const errorName = error instanceof Error && 'name' in error ? error.name : '';
121
+ if (errorName === 'ResourceInUseException') {
122
+ }
123
+ else {
124
+ throw error;
125
+ }
126
+ }
127
+ }
128
+ async createSQSQueue(resource) {
129
+ try {
130
+ const attributes = {};
131
+ if (resource.visibilityTimeout) {
132
+ attributes.VisibilityTimeout = resource.visibilityTimeout.toString();
133
+ }
134
+ if (resource.messageRetentionPeriod) {
135
+ attributes.MessageRetentionPeriod = resource.messageRetentionPeriod.toString();
136
+ }
137
+ if (resource.fifoQueue) {
138
+ attributes.FifoQueue = 'true';
139
+ if (resource.contentBasedDeduplication) {
140
+ attributes.ContentBasedDeduplication = 'true';
141
+ }
142
+ }
143
+ await this.sqsClient.send(new CreateQueueCommand({
144
+ QueueName: resource.name,
145
+ Attributes: Object.keys(attributes).length > 0 ? attributes : undefined,
146
+ }));
147
+ console.log(` ✓ Created SQS queue: ${resource.name}`);
148
+ }
149
+ catch (error) {
150
+ if (error.name === 'QueueAlreadyExists') {
151
+ console.log(` ⚠ SQS queue already exists: ${resource.name}`);
152
+ }
153
+ else {
154
+ throw error;
155
+ }
156
+ }
157
+ }
158
+ async createSNSTopic(resource) {
159
+ try {
160
+ await this.snsClient.send(new CreateTopicCommand({
161
+ Name: resource.name,
162
+ }));
163
+ console.log(` ✓ Created SNS topic: ${resource.name}`);
164
+ }
165
+ catch (error) {
166
+ if (error.message?.includes('already exists')) {
167
+ console.log(` ⚠ SNS topic already exists: ${resource.name}`);
168
+ }
169
+ else {
170
+ throw error;
171
+ }
172
+ }
173
+ }
174
+ generateProxyLambdaCode(functionName, invokeUrl) {
175
+ return `
176
+ export const handler = async (event, context) => {
177
+ const http = await import('http');
178
+ const url = await import('url');
179
+
180
+ const invokeUrl = process.env.INVOKE_URL || '${invokeUrl}';
181
+ const parsedUrl = url.parse(invokeUrl);
182
+
183
+ // Transform event based on source
184
+ // LocalStack sends events through different sources (SQS, DynamoDB, SNS, etc.)
185
+ // We need to pass them correctly to the Serverless Offline handler
186
+ let transformedEvent = event;
187
+
188
+ // If the event already has Records (SQS/DynamoDB/SNS event), use it as is
189
+ // Otherwise wrap it in a Records array
190
+ if (!event.Records) {
191
+ transformedEvent = {
192
+ Records: [event]
193
+ };
194
+ }
195
+
196
+ const payload = JSON.stringify(transformedEvent);
197
+
198
+ return new Promise((resolve, reject) => {
199
+ const options = {
200
+ hostname: parsedUrl.hostname,
201
+ port: parsedUrl.port,
202
+ path: '/2015-03-31/functions/${functionName}/invocations',
203
+ method: 'POST',
204
+ headers: {
205
+ 'Content-Type': 'application/json',
206
+ 'Content-Length': Buffer.byteLength(payload),
207
+ 'X-Amz-Invocation-Type': 'RequestResponse'
208
+ }
209
+ };
210
+
211
+ const req = http.request(options, (res) => {
212
+ let data = '';
213
+ res.on('data', chunk => data += chunk);
214
+ res.on('end', () => {
215
+ if (res.statusCode >= 200 && res.statusCode < 300) {
216
+ try {
217
+ // Try to parse as JSON
218
+ const response = JSON.parse(data);
219
+ resolve(response);
220
+ } catch (e) {
221
+ // If not JSON, return as string
222
+ resolve(data);
223
+ }
224
+ } else {
225
+ reject(new Error(\`Lambda invoke failed: \${res.statusCode} \${data}\`));
226
+ }
227
+ });
228
+ });
229
+
230
+ req.on('error', (err) => {
231
+ console.error('Request error:', err);
232
+ reject(err);
233
+ });
234
+
235
+ req.write(payload);
236
+ req.end();
237
+ });
238
+ };
239
+ `;
240
+ }
241
+ async createEventSourceMapping(serviceName, mapping) {
242
+ try {
243
+ // Convert CloudFormation function name to Serverless naming convention
244
+ // CloudFormation: ConsumerAppsQueueLambdaFunction -> Serverless: payment-reminder-api-consumerAppsQueue
245
+ const actualFunctionName = this.resolveLambdaFunctionName(serviceName, mapping.functionName);
246
+ // Resolve queue ARN from queue name
247
+ const eventSourceArn = await this.resolveEventSourceArn(mapping.eventSourceArn);
248
+ // Create Lambda proxy function if it doesn't exist
249
+ await this.ensureLambdaProxyExists(serviceName, actualFunctionName);
250
+ // Check if mapping already exists
251
+ const existingMappings = await this.lambdaClient.send(new ListEventSourceMappingsCommand({
252
+ FunctionName: actualFunctionName,
253
+ }));
254
+ if (existingMappings.EventSourceMappings?.some(m => m.EventSourceArn?.includes(mapping.eventSourceArn))) {
255
+ console.log(` ⚠ Event source mapping already exists: ${actualFunctionName} <- ${mapping.eventSourceArn}`);
256
+ return;
257
+ }
258
+ await this.lambdaClient.send(new CreateEventSourceMappingCommand({
259
+ FunctionName: actualFunctionName,
260
+ EventSourceArn: eventSourceArn,
261
+ BatchSize: mapping.batchSize || 10,
262
+ Enabled: mapping.enabled,
263
+ }));
264
+ console.log(` ✓ Created event source mapping: ${actualFunctionName} <- ${mapping.eventSourceArn}`);
265
+ }
266
+ catch (error) {
267
+ if (error.name === 'ResourceConflictException') {
268
+ console.log(` ⚠ Event source mapping already exists: ${mapping.functionName}`);
269
+ }
270
+ else {
271
+ throw error;
272
+ }
273
+ }
274
+ }
275
+ async ensureLambdaProxyExists(serviceName, functionName) {
276
+ try {
277
+ // Check if function already exists
278
+ await this.lambdaClient.send(new GetFunctionCommand({
279
+ FunctionName: functionName,
280
+ }));
281
+ // Function exists, no need to create
282
+ return;
283
+ }
284
+ catch (error) {
285
+ if (error.name !== 'ResourceNotFoundException') {
286
+ throw error;
287
+ }
288
+ }
289
+ // Function doesn't exist, create a Lambda proxy
290
+ const metadata = this.serviceMetadata.get(serviceName);
291
+ if (!metadata) {
292
+ throw new Error(`Cannot create Lambda proxy for ${functionName}: no invoke port configured for ${serviceName}`);
293
+ }
294
+ const proxyCode = this.generateProxyLambdaCode(functionName, metadata.invokeUrl);
295
+ const zip = new AdmZip();
296
+ zip.addFile('index.mjs', Buffer.from(proxyCode, 'utf-8'));
297
+ const zipBuffer = zip.toBuffer();
298
+ await this.lambdaClient.send(new CreateFunctionCommand({
299
+ FunctionName: functionName,
300
+ Runtime: 'nodejs20.x',
301
+ Role: 'arn:aws:iam::000000000000:role/lambda-role',
302
+ Handler: 'index.handler',
303
+ Code: {
304
+ ZipFile: zipBuffer,
305
+ },
306
+ Environment: {
307
+ Variables: {
308
+ INVOKE_URL: metadata.invokeUrl,
309
+ FUNCTION_NAME: functionName,
310
+ },
311
+ },
312
+ MemorySize: 256,
313
+ Timeout: 60,
314
+ }));
315
+ console.log(` ✓ Created Lambda proxy: ${functionName} -> ${metadata.invokeUrl}`);
316
+ }
317
+ resolveLambdaFunctionName(serviceName, cfnName) {
318
+ // CloudFormation names like "ConsumerAppsQueueLambdaFunction" are converted to Serverless naming
319
+ // by removing the "LambdaFunction" suffix, converting to camelCase, then to the serverless pattern
320
+ // Example: ConsumerAppsQueueLambdaFunction -> consumerAppsQueue -> payment-reminder-api-consumerAppsQueue
321
+ // Remove "LambdaFunction" suffix if present
322
+ let funcName = cfnName;
323
+ if (funcName.endsWith('LambdaFunction')) {
324
+ funcName = funcName.slice(0, -14); // Remove "LambdaFunction"
325
+ }
326
+ // Convert to camelCase (first letter lowercase)
327
+ funcName = funcName.charAt(0).toLowerCase() + funcName.slice(1);
328
+ // Serverless names follow pattern: {service}-{stage}-{functionName}
329
+ // We need to extract service and stage from the created Lambda names
330
+ // For now, assume standard naming: payment-reminder-api-{functionName}
331
+ return `${serviceName}-api-${funcName}`;
332
+ }
333
+ convertCamelCaseToKebab(camelCase) {
334
+ return camelCase
335
+ .replace(/([A-Z])/g, '-$1')
336
+ .toLowerCase()
337
+ .replace(/^-/, '');
338
+ }
339
+ async resolveEventSourceArn(arnRef) {
340
+ // If it's already a full ARN, return it
341
+ if (arnRef.startsWith('arn:aws:')) {
342
+ return arnRef;
343
+ }
344
+ // Extract resource name from Fn::GetAtt reference (e.g., "paymentReminderApps::Arn")
345
+ let resourceName = arnRef.split('::')[0];
346
+ // Convert camelCase resource names to kebab-case (serverless convention)
347
+ // e.g., "paymentReminderApps" -> "payment-reminder-apps"
348
+ resourceName = this.convertCamelCaseToKebab(resourceName);
349
+ // Try to resolve as SQS queue
350
+ try {
351
+ const queues = await this.sqsClient.send(new ListQueuesCommand({}));
352
+ const queueUrl = queues.QueueUrls?.find(url => url.includes(resourceName));
353
+ if (queueUrl) {
354
+ const attributes = await this.sqsClient.send(new GetQueueAttributesCommand({
355
+ QueueUrl: queueUrl,
356
+ AttributeNames: ['QueueArn'],
357
+ }));
358
+ const queueArn = attributes.Attributes?.QueueArn;
359
+ if (queueArn) {
360
+ return queueArn;
361
+ }
362
+ }
363
+ }
364
+ catch (error) {
365
+ // Continue to try other resource types
366
+ }
367
+ // Try to resolve as DynamoDB stream
368
+ try {
369
+ const tables = await this.dynamoClient.send(new ListTablesCommand({}));
370
+ const tableMatch = tables.TableNames?.find(name => this.convertCamelCaseToKebab(name) === resourceName);
371
+ if (tableMatch) {
372
+ // Return DynamoDB Streams ARN (format: arn:aws:dynamodb:region:account:table/name/stream/type)
373
+ // For now, we'll construct a generic stream ARN
374
+ return `arn:aws:dynamodb:us-east-1:000000000000:table/${tableMatch}/stream/NEW_AND_OLD_IMAGES`;
375
+ }
376
+ }
377
+ catch (error) {
378
+ // Continue to try other resource types
379
+ }
380
+ // Try to resolve as SNS topic
381
+ try {
382
+ const topics = await this.snsClient.send(new ListTopicsCommand({}));
383
+ const topicMatch = topics.Topics?.find(topic => {
384
+ const topicName = topic.TopicArn?.split(':').pop() || '';
385
+ return this.convertCamelCaseToKebab(topicName) === resourceName;
386
+ });
387
+ if (topicMatch?.TopicArn) {
388
+ return topicMatch.TopicArn;
389
+ }
390
+ }
391
+ catch (error) {
392
+ // Continue
393
+ }
394
+ throw new Error(`Event source not found: ${arnRef} (resolved as: ${resourceName})`);
395
+ }
396
+ async listAllResources() {
397
+ const [tables, queues, topics] = await Promise.all([
398
+ this.listDynamoDBTables(),
399
+ this.listSQSQueues(),
400
+ this.listSNSTopics(),
401
+ ]);
402
+ return { tables, queues, topics };
403
+ }
404
+ async listDynamoDBTables() {
405
+ try {
406
+ const response = await this.dynamoClient.send(new ListTablesCommand({}));
407
+ return response.TableNames || [];
408
+ }
409
+ catch {
410
+ return [];
411
+ }
412
+ }
413
+ async listSQSQueues() {
414
+ try {
415
+ const response = await this.sqsClient.send(new ListQueuesCommand({}));
416
+ return response.QueueUrls?.map(url => url.split('/').pop()) || [];
417
+ }
418
+ catch {
419
+ return [];
420
+ }
421
+ }
422
+ async listSNSTopics() {
423
+ try {
424
+ const response = await this.snsClient.send(new ListTopicsCommand({}));
425
+ return response.Topics?.map(t => {
426
+ const arn = t.TopicArn?.split(':').pop();
427
+ return arn || '';
428
+ }).filter(arn => arn !== '') || [];
429
+ }
430
+ catch {
431
+ return [];
432
+ }
433
+ }
434
+ async cleanupResources(serviceName) {
435
+ const resources = this.provisionedResources.get(serviceName);
436
+ if (!resources) {
437
+ console.log(`No resources found for service ${serviceName}`);
438
+ return;
439
+ }
440
+ let cleaned = 0;
441
+ for (const resource of resources) {
442
+ try {
443
+ const [type, name] = resource.split(':');
444
+ switch (type) {
445
+ case 'dynamodb':
446
+ await this.deleteDynamoDBTable(name);
447
+ cleaned++;
448
+ break;
449
+ case 'sqs':
450
+ await this.deleteSQSQueue(name);
451
+ cleaned++;
452
+ break;
453
+ case 'sns':
454
+ await this.deleteSNSTopic(name);
455
+ cleaned++;
456
+ break;
457
+ case 'event-source':
458
+ await this.deleteEventSourceMapping(serviceName, name);
459
+ cleaned++;
460
+ break;
461
+ }
462
+ }
463
+ catch (error) {
464
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
465
+ console.error(`Failed to cleanup ${resource}:`, errorMessage);
466
+ }
467
+ }
468
+ this.provisionedResources.delete(serviceName);
469
+ this.serviceMetadata.delete(serviceName);
470
+ console.log(`✅ Cleaned up ${cleaned} resources for ${serviceName}`);
471
+ }
472
+ async deleteDynamoDBTable(tableName) {
473
+ try {
474
+ await this.dynamoClient.send(new DeleteTableCommand({ TableName: tableName }));
475
+ console.log(`Deleted DynamoDB table: ${tableName}`);
476
+ }
477
+ catch (error) {
478
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
479
+ if (!errorMessage.includes('ResourceNotFoundException')) {
480
+ throw error;
481
+ }
482
+ }
483
+ }
484
+ async deleteSQSQueue(queueName) {
485
+ try {
486
+ const urlResponse = await this.sqsClient.send(new GetQueueUrlCommand({ QueueName: queueName }));
487
+ if (urlResponse.QueueUrl) {
488
+ await this.sqsClient.send(new DeleteQueueCommand({ QueueUrl: urlResponse.QueueUrl }));
489
+ console.log(`Deleted SQS queue: ${queueName}`);
490
+ }
491
+ }
492
+ catch (error) {
493
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
494
+ if (!errorMessage.includes('NonExistentQueue')) {
495
+ throw error;
496
+ }
497
+ }
498
+ }
499
+ async deleteSNSTopic(topicName) {
500
+ try {
501
+ // Find the topic ARN from list
502
+ const response = await this.snsClient.send(new ListTopicsCommand({}));
503
+ const topic = response.Topics?.find(t => t.TopicArn?.endsWith(`:${topicName}`));
504
+ if (topic?.TopicArn) {
505
+ await this.snsClient.send(new DeleteTopicCommand({ TopicArn: topic.TopicArn }));
506
+ console.log(`Deleted SNS topic: ${topicName}`);
507
+ }
508
+ }
509
+ catch (error) {
510
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
511
+ if (!errorMessage.includes('NotFound')) {
512
+ throw error;
513
+ }
514
+ }
515
+ }
516
+ async deleteEventSourceMapping(serviceName, functionName) {
517
+ try {
518
+ // List all event source mappings
519
+ const response = await this.lambdaClient.send(new ListEventSourceMappingsCommand({ FunctionName: `${serviceName}-${functionName}-proxy` }));
520
+ // Delete each mapping
521
+ for (const mapping of response.EventSourceMappings || []) {
522
+ if (mapping.UUID) {
523
+ await this.lambdaClient.send(new DeleteEventSourceMappingCommand({ UUID: mapping.UUID }));
524
+ console.log(`Deleted event source mapping: ${mapping.UUID}`);
525
+ }
526
+ }
527
+ // Delete the Lambda proxy function
528
+ await this.lambdaClient.send(new DeleteFunctionCommand({ FunctionName: `${serviceName}-${functionName}-proxy` }));
529
+ console.log(`Deleted Lambda proxy: ${serviceName}-${functionName}-proxy`);
530
+ }
531
+ catch (error) {
532
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
533
+ if (!errorMessage.includes('ResourceNotFoundException')) {
534
+ throw error;
535
+ }
536
+ }
537
+ }
538
+ }
539
+ //# sourceMappingURL=resource-provisioner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-provisioner.js","sourceRoot":"","sources":["../../../src/server/services/resource-provisioner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ .modal[data-v-af35b7d4]{position:fixed;top:0;right:0;bottom:0;left:0;background:#0000008c;display:flex;align-items:center;justify-content:center;z-index:50}.modal-content[data-v-af35b7d4]{background:var(--card);border:1px solid var(--border);border-radius:.5rem;width:720px;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 10px 30px #0000004d}.modal-header[data-v-af35b7d4]{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;border-bottom:1px solid var(--border)}.logs[data-v-af35b7d4]{flex:1;padding:1rem;margin:0;background:#0b0c10;color:#d1d5db;font-family:Menlo,Consolas,Monaco,monospace;font-size:.85rem;overflow:auto;min-height:300px;white-space:pre-wrap}.actions[data-v-af35b7d4]{display:flex;gap:.5rem;flex-wrap:wrap}*{margin:0;padding:0;box-sizing:border-box}:root{--primary: #3b82f6;--primary-hover: #2563eb;--success: #10b981;--warning: #f59e0b;--danger: #ef4444;--bg: #0f172a;--bg-card: #1e293b;--bg-hover: #334155;--text: #f1f5f9;--text-muted: #94a3b8;--border: #334155}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;background:var(--bg);color:var(--text);line-height:1.6}#app{min-height:100vh}.header{background:var(--bg-card);border-bottom:1px solid var(--border);padding:1rem 2rem}.header h1{font-size:1.5rem;font-weight:600}.header-subtitle{color:var(--text-muted);font-size:.875rem}.container{max-width:1400px;margin:0 auto;padding:2rem}.card{background:var(--bg-card);border:1px solid var(--border);border-radius:.5rem;padding:1.5rem;margin-bottom:1.5rem}.card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;padding-bottom:1rem;border-bottom:1px solid var(--border)}.card-title{font-size:1.25rem;font-weight:600}.btn{padding:.5rem 1rem;border:none;border-radius:.375rem;font-size:.875rem;font-weight:500;cursor:pointer;transition:all .2s}.btn-primary{background:var(--primary);color:#fff}.btn-primary:hover{background:var(--primary-hover)}.btn-success{background:var(--success);color:#fff}.btn-danger{background:var(--danger);color:#fff}.btn-sm{padding:.375rem .75rem;font-size:.8125rem}.table{width:100%;border-collapse:collapse}.table th,.table td{padding:.75rem;text-align:left;border-bottom:1px solid var(--border)}.table th{font-weight:600;color:var(--text-muted);font-size:.875rem;text-transform:uppercase;letter-spacing:.05em}.table tr:hover{background:var(--bg-hover)}.badge{display:inline-block;padding:.25rem .625rem;border-radius:9999px;font-size:.75rem;font-weight:500}.badge-success{background:#10b98133;color:var(--success)}.badge-warning{background:#f59e0b33;color:var(--warning)}.badge-danger{background:#ef444433;color:var(--danger)}.grid{display:grid;gap:1.5rem}.grid-cols-3{grid-template-columns:repeat(3,1fr)}.stat-card{background:var(--bg-card);border:1px solid var(--border);border-radius:.5rem;padding:1.5rem}.stat-label{color:var(--text-muted);font-size:.875rem;margin-bottom:.5rem}.stat-value{font-size:2rem;font-weight:600}.empty-state{text-align:center;padding:3rem 1rem;color:var(--text-muted)}.loading{text-align:center;padding:2rem;color:var(--text-muted)}.actions{display:flex;gap:.5rem}