local-serverless-stack 0.0.7 → 0.0.8

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.0.8] - 2026-05-18
9
+
10
+ ### Added
11
+ - **Queue inspection UI**: new "Queues" tab in the dashboard. For each SQS queue it shows available messages, in-flight messages, processed-since-orchestrator-start, delayed messages, and the Lambda consumers (event source mappings) attached to it. Click "Details" for full attributes (visibility timeout, retention, FIFO, creation time) plus a per-consumer panel.
12
+ - **`/api/queues` endpoints**: `GET /api/queues` (list), `GET /api/queues/:name` (details), `POST /api/queues/:name/reset-processed` (reset processed counter).
13
+ - **Queue metrics tracker**: `QueueInspector` polls LocalStack every 5s and derives a per-queue "processed" count from drops in the in-flight bucket that are not re-queued as retries.
14
+ - **TreeUI design system**: dashboard now uses [`@treeui/vue`](https://www.npmjs.com/package/@treeui/vue) components throughout — `TNavbar`, `TContainer`, `TTabs`, `TCard`, `TStat`, `TTable`, `TBadge`, `TTag`, `TButton`, `TInput`, `TFormField`, `TModal`, `TConfirmDialog`, `TEmptyState`, `TSpinner`, `TProgress`, `TAlert`, `TStack`, `TGrid`, `TDivider`, `TToastProvider` / `useToast()`. Toasts replace native `alert()` calls. Dark theme is applied by default with a light/dark toggle in the navbar.
15
+
16
+ ### Changed
17
+ - `ServicesList` and `ResourcesOverview` rewritten on top of TreeUI primitives.
18
+ - Delete service now goes through a `TConfirmDialog` instead of `window.confirm`.
19
+
8
20
  ## [0.0.6] - 2026-05-17
9
21
 
10
22
  ### Added
@@ -4,8 +4,10 @@ import path from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  import { servicesRouter, processManager } from './routes/services.js';
6
6
  import { resourcesRouter } from './routes/resources.js';
7
+ import { queuesRouter } from './routes/queues.js';
7
8
  import { LocalStackManager } from './services/localstack-manager.js';
8
9
  import { ConfigManager } from './services/config-manager.js';
10
+ import { QueueInspector } from './services/queue-inspector.js';
9
11
  import { startDynamoProxy } from './dev/dynamo-proxy.js';
10
12
  const __filename = fileURLToPath(import.meta.url);
11
13
  const __dirname = path.dirname(__filename);
@@ -18,6 +20,7 @@ app.use(express.json());
18
20
  // API routes
19
21
  app.use('/api/services', servicesRouter);
20
22
  app.use('/api/resources', resourcesRouter);
23
+ app.use('/api/queues', queuesRouter);
21
24
  // Health check
22
25
  app.get('/api/health', (_req, res) => {
23
26
  res.json({
@@ -43,6 +46,7 @@ async function start() {
43
46
  console.log(`✅ Server running on http://localhost:${PORT}`);
44
47
  console.log(`✅ LocalStack running on ${localstack.getEndpoint()}`);
45
48
  });
49
+ QueueInspector.getInstance().startPolling();
46
50
  // Optional DynamoDB proxy
47
51
  if (configManager.isEnableDynamoProxy()) {
48
52
  const proxyPort = configManager.getDynamoProxyPort();
@@ -59,6 +63,7 @@ async function start() {
59
63
  // Graceful shutdown
60
64
  process.on('SIGINT', async () => {
61
65
  console.log('\n🛑 Shutting down gracefully...');
66
+ QueueInspector.getInstance().stopPolling();
62
67
  processManager.stopAll();
63
68
  await processManager.cleanup();
64
69
  const localstack = LocalStackManager.getInstance();
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ declare const router: import("express-serve-static-core").Router;
2
+ export { router as queuesRouter };
3
+ //# sourceMappingURL=queues.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queues.d.ts","sourceRoot":"","sources":["../../../src/server/routes/queues.ts"],"names":[],"mappings":"AAGA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA4CxB,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { Router } from 'express';
2
+ import { QueueInspector } from '../services/queue-inspector.js';
3
+ const router = Router();
4
+ const inspector = QueueInspector.getInstance();
5
+ router.get('/', async (_req, res) => {
6
+ try {
7
+ const queues = await inspector.listQueues();
8
+ res.json(queues);
9
+ }
10
+ catch (error) {
11
+ const message = error instanceof Error ? error.message : 'Failed to list queues';
12
+ res.status(500).json({ error: message });
13
+ }
14
+ });
15
+ router.get('/:name', async (req, res) => {
16
+ try {
17
+ const name = req.params.name;
18
+ if (!name || typeof name !== 'string' || name.includes('/') || name.includes('..')) {
19
+ return res.status(400).json({ error: 'Invalid queue name' });
20
+ }
21
+ const queue = await inspector.getQueue(name);
22
+ if (!queue) {
23
+ return res.status(404).json({ error: 'Queue not found' });
24
+ }
25
+ return res.json(queue);
26
+ }
27
+ catch (error) {
28
+ const message = error instanceof Error ? error.message : 'Failed to fetch queue';
29
+ return res.status(500).json({ error: message });
30
+ }
31
+ });
32
+ router.post('/:name/reset-processed', async (req, res) => {
33
+ try {
34
+ const name = req.params.name;
35
+ if (!name || typeof name !== 'string' || name.includes('/') || name.includes('..')) {
36
+ return res.status(400).json({ error: 'Invalid queue name' });
37
+ }
38
+ inspector.resetProcessedCount(name);
39
+ return res.json({ success: true });
40
+ }
41
+ catch (error) {
42
+ const message = error instanceof Error ? error.message : 'Failed to reset counter';
43
+ return res.status(500).json({ error: message });
44
+ }
45
+ });
46
+ export { router as queuesRouter };
47
+ //# sourceMappingURL=queues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queues.js","sourceRoot":"","sources":["../../../src/server/routes/queues.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ export interface QueueConsumer {
2
+ functionName: string;
3
+ uuid?: string;
4
+ state?: string;
5
+ batchSize?: number;
6
+ enabled: boolean;
7
+ }
8
+ export interface QueueSnapshot {
9
+ name: string;
10
+ url: string;
11
+ arn?: string;
12
+ available: number;
13
+ inFlight: number;
14
+ delayed: number;
15
+ processed: number;
16
+ total: number;
17
+ fifo: boolean;
18
+ visibilityTimeout?: number;
19
+ messageRetentionPeriod?: number;
20
+ createdAt?: number;
21
+ lastModifiedAt?: number;
22
+ consumers: QueueConsumer[];
23
+ lastPolledAt: number;
24
+ }
25
+ export declare class QueueInspector {
26
+ private static instance;
27
+ private sqsClient;
28
+ private lambdaClient;
29
+ private metrics;
30
+ private pollInterval;
31
+ private readonly pollFrequencyMs;
32
+ private constructor();
33
+ static getInstance(): QueueInspector;
34
+ startPolling(): void;
35
+ stopPolling(): void;
36
+ listQueues(): Promise<QueueSnapshot[]>;
37
+ getQueue(queueName: string): Promise<QueueSnapshot | null>;
38
+ private refreshMetrics;
39
+ private fetchQueueUrls;
40
+ private fetchEventSourceMappingsByQueueArn;
41
+ private buildSnapshot;
42
+ private updateMetricsForQueue;
43
+ private updateMetricsState;
44
+ resetProcessedCount(queueName: string): void;
45
+ }
46
+ //# sourceMappingURL=queue-inspector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-inspector.d.ts","sourceRoot":"","sources":["../../../src/server/services/queue-inspector.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;CACtB;AAQD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAiB;IACxC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,OAAO,CAAwC;IACvD,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IAExC,OAAO;IAMP,MAAM,CAAC,WAAW,IAAI,cAAc;IAOpC,YAAY,IAAI,IAAI;IAOpB,WAAW,IAAI,IAAI;IAOb,UAAU,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAUtC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;YAclD,cAAc;YASd,cAAc;YASd,kCAAkC;YA0BlC,aAAa;YA4Cb,qBAAqB;IAmBnC,OAAO,CAAC,kBAAkB;IA2B1B,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAO7C"}
@@ -0,0 +1,182 @@
1
+ import { SQSClient, ListQueuesCommand, GetQueueAttributesCommand, GetQueueUrlCommand, } from '@aws-sdk/client-sqs';
2
+ import { LambdaClient, ListEventSourceMappingsCommand } from '@aws-sdk/client-lambda';
3
+ import { LocalStackManager } from './localstack-manager.js';
4
+ export class QueueInspector {
5
+ static instance;
6
+ sqsClient;
7
+ lambdaClient;
8
+ metrics = new Map();
9
+ pollInterval = null;
10
+ pollFrequencyMs = 5000;
11
+ constructor() {
12
+ const config = LocalStackManager.getInstance().getConfig();
13
+ this.sqsClient = new SQSClient(config);
14
+ this.lambdaClient = new LambdaClient(config);
15
+ }
16
+ static getInstance() {
17
+ if (!QueueInspector.instance) {
18
+ QueueInspector.instance = new QueueInspector();
19
+ }
20
+ return QueueInspector.instance;
21
+ }
22
+ startPolling() {
23
+ if (this.pollInterval)
24
+ return;
25
+ this.pollInterval = setInterval(() => {
26
+ this.refreshMetrics().catch(() => undefined);
27
+ }, this.pollFrequencyMs);
28
+ }
29
+ stopPolling() {
30
+ if (this.pollInterval) {
31
+ clearInterval(this.pollInterval);
32
+ this.pollInterval = null;
33
+ }
34
+ }
35
+ async listQueues() {
36
+ const queueUrls = await this.fetchQueueUrls();
37
+ const eventSourceMap = await this.fetchEventSourceMappingsByQueueArn();
38
+ const snapshots = await Promise.all(queueUrls.map(url => this.buildSnapshot(url, eventSourceMap)));
39
+ return snapshots.filter((s) => s !== null);
40
+ }
41
+ async getQueue(queueName) {
42
+ try {
43
+ const urlResponse = await this.sqsClient.send(new GetQueueUrlCommand({ QueueName: queueName }));
44
+ if (!urlResponse.QueueUrl)
45
+ return null;
46
+ const eventSourceMap = await this.fetchEventSourceMappingsByQueueArn();
47
+ return await this.buildSnapshot(urlResponse.QueueUrl, eventSourceMap);
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ }
53
+ async refreshMetrics() {
54
+ try {
55
+ const queueUrls = await this.fetchQueueUrls();
56
+ await Promise.all(queueUrls.map(url => this.updateMetricsForQueue(url)));
57
+ }
58
+ catch {
59
+ // Ignore polling errors — LocalStack may be unavailable briefly
60
+ }
61
+ }
62
+ async fetchQueueUrls() {
63
+ try {
64
+ const response = await this.sqsClient.send(new ListQueuesCommand({}));
65
+ return response.QueueUrls || [];
66
+ }
67
+ catch {
68
+ return [];
69
+ }
70
+ }
71
+ async fetchEventSourceMappingsByQueueArn() {
72
+ const result = new Map();
73
+ try {
74
+ const response = await this.lambdaClient.send(new ListEventSourceMappingsCommand({}));
75
+ for (const mapping of response.EventSourceMappings || []) {
76
+ if (!mapping.EventSourceArn || !mapping.FunctionArn)
77
+ continue;
78
+ if (!mapping.EventSourceArn.includes(':sqs:'))
79
+ continue;
80
+ const consumer = {
81
+ functionName: mapping.FunctionArn.split(':').pop() || mapping.FunctionArn,
82
+ uuid: mapping.UUID,
83
+ state: mapping.State,
84
+ batchSize: mapping.BatchSize,
85
+ enabled: mapping.State === 'Enabled' || mapping.State === 'Creating',
86
+ };
87
+ const consumers = result.get(mapping.EventSourceArn) || [];
88
+ consumers.push(consumer);
89
+ result.set(mapping.EventSourceArn, consumers);
90
+ }
91
+ }
92
+ catch {
93
+ // No mappings or LocalStack offline — return empty map
94
+ }
95
+ return result;
96
+ }
97
+ async buildSnapshot(queueUrl, consumersByArn) {
98
+ try {
99
+ const attrs = await this.sqsClient.send(new GetQueueAttributesCommand({
100
+ QueueUrl: queueUrl,
101
+ AttributeNames: ['All'],
102
+ }));
103
+ const a = attrs.Attributes || {};
104
+ const available = Number(a.ApproximateNumberOfMessages || 0);
105
+ const inFlight = Number(a.ApproximateNumberOfMessagesNotVisible || 0);
106
+ const delayed = Number(a.ApproximateNumberOfMessagesDelayed || 0);
107
+ const arn = a.QueueArn;
108
+ const name = queueUrl.split('/').pop() || queueUrl;
109
+ this.updateMetricsState(queueUrl, available, inFlight);
110
+ const processed = this.metrics.get(queueUrl)?.processed || 0;
111
+ return {
112
+ name,
113
+ url: queueUrl,
114
+ arn,
115
+ available,
116
+ inFlight,
117
+ delayed,
118
+ processed,
119
+ total: available + inFlight + delayed,
120
+ fifo: name.endsWith('.fifo'),
121
+ visibilityTimeout: a.VisibilityTimeout ? Number(a.VisibilityTimeout) : undefined,
122
+ messageRetentionPeriod: a.MessageRetentionPeriod ? Number(a.MessageRetentionPeriod) : undefined,
123
+ createdAt: a.CreatedTimestamp ? Number(a.CreatedTimestamp) * 1000 : undefined,
124
+ lastModifiedAt: a.LastModifiedTimestamp ? Number(a.LastModifiedTimestamp) * 1000 : undefined,
125
+ consumers: arn ? consumersByArn.get(arn) || [] : [],
126
+ lastPolledAt: Date.now(),
127
+ };
128
+ }
129
+ catch {
130
+ return null;
131
+ }
132
+ }
133
+ async updateMetricsForQueue(queueUrl) {
134
+ try {
135
+ const attrs = await this.sqsClient.send(new GetQueueAttributesCommand({
136
+ QueueUrl: queueUrl,
137
+ AttributeNames: [
138
+ 'ApproximateNumberOfMessages',
139
+ 'ApproximateNumberOfMessagesNotVisible',
140
+ ],
141
+ }));
142
+ const available = Number(attrs.Attributes?.ApproximateNumberOfMessages || 0);
143
+ const inFlight = Number(attrs.Attributes?.ApproximateNumberOfMessagesNotVisible || 0);
144
+ this.updateMetricsState(queueUrl, available, inFlight);
145
+ }
146
+ catch {
147
+ // Skip on transient failures
148
+ }
149
+ }
150
+ updateMetricsState(queueUrl, available, inFlight) {
151
+ const state = this.metrics.get(queueUrl);
152
+ if (!state) {
153
+ this.metrics.set(queueUrl, {
154
+ lastAvailable: available,
155
+ lastInFlight: inFlight,
156
+ processed: 0,
157
+ });
158
+ return;
159
+ }
160
+ // A message is considered "processed" when it leaves the in-flight bucket
161
+ // without re-appearing as available (i.e., the consumer deleted it).
162
+ const inFlightDelta = state.lastInFlight - inFlight;
163
+ const availableDelta = available - state.lastAvailable;
164
+ if (inFlightDelta > 0) {
165
+ // Some messages left in-flight. Messages re-added to available are retries,
166
+ // not new work — subtract them so retries don't inflate the processed count.
167
+ const reAppeared = Math.max(0, availableDelta);
168
+ const processedNow = Math.max(0, inFlightDelta - reAppeared);
169
+ state.processed += processedNow;
170
+ }
171
+ state.lastAvailable = available;
172
+ state.lastInFlight = inFlight;
173
+ }
174
+ resetProcessedCount(queueName) {
175
+ for (const [url, state] of this.metrics.entries()) {
176
+ if (url.endsWith(`/${queueName}`)) {
177
+ state.processed = 0;
178
+ }
179
+ }
180
+ }
181
+ }
182
+ //# sourceMappingURL=queue-inspector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-inspector.js","sourceRoot":"","sources":["../../../src/server/services/queue-inspector.ts"],"names":[],"mappings":"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"}