diff --git a/_reference/localEmailViewer/README.md b/_reference/localEmailViewer/README.md
index 6054686fd..238c2bf22 100644
--- a/_reference/localEmailViewer/README.md
+++ b/_reference/localEmailViewer/README.md
@@ -1,5 +1,7 @@
-This app connects to your Docker LocalStack SES endpoint and gives you a local inbox-style viewer
-for generated emails.
+This app connects to your Docker LocalStack endpoints and gives you a compact inspector for:
+
+- SES generated emails
+- CloudWatch log groups, streams, and recent events
```shell
npm start
@@ -15,13 +17,11 @@ Open: http://localhost:3334
Features:
-- Manual refresh
-- Live refresh with adjustable polling interval
-- Search across subject, addresses, preview text, and attachment names
-- Expand/collapse all messages
-- Rendered HTML, plain-text, and raw MIME views
-- Copy raw MIME source
-- New-message highlighting plus fetch timing and parse-error stats
+- SES email workspace with manual refresh, live refresh, search, HTML/text/raw views,
+ attachment downloads, and new-message highlighting
+- CloudWatch Logs workspace with log group selection, stream filtering, adjustable time window,
+ adjustable event limit, live refresh, and in-browser log search
+- Compact single-page UI for switching between the local stack tools you use most
Optional environment variables:
@@ -30,4 +30,9 @@ PORT=3334
SES_VIEWER_ENDPOINT=http://localhost:4566/_aws/ses
SES_VIEWER_REFRESH_MS=10000
SES_VIEWER_FETCH_TIMEOUT_MS=5000
+CLOUDWATCH_VIEWER_ENDPOINT=http://localhost:4566
+CLOUDWATCH_VIEWER_REGION=ca-central-1
+CLOUDWATCH_VIEWER_LOG_GROUP=development
+CLOUDWATCH_VIEWER_WINDOW_MS=900000
+CLOUDWATCH_VIEWER_LIMIT=200
```
diff --git a/_reference/localEmailViewer/index.js b/_reference/localEmailViewer/index.js
index 7f35fa393..4d935135a 100644
--- a/_reference/localEmailViewer/index.js
+++ b/_reference/localEmailViewer/index.js
@@ -1,5 +1,11 @@
import express from "express";
import fetch from "node-fetch";
+import {
+ CloudWatchLogsClient,
+ DescribeLogGroupsCommand,
+ DescribeLogStreamsCommand,
+ FilterLogEventsCommand
+} from "@aws-sdk/client-cloudwatch-logs";
import { simpleParser } from "mailparser";
const app = express();
@@ -8,6 +14,20 @@ const PORT = Number(process.env.PORT || 3334);
const SES_ENDPOINT = process.env.SES_VIEWER_ENDPOINT || "http://localhost:4566/_aws/ses";
const FETCH_TIMEOUT_MS = Number(process.env.SES_VIEWER_FETCH_TIMEOUT_MS || 5000);
const DEFAULT_REFRESH_MS = Number(process.env.SES_VIEWER_REFRESH_MS || 10000);
+const CLOUDWATCH_ENDPOINT = process.env.CLOUDWATCH_VIEWER_ENDPOINT || "http://localhost:4566";
+const CLOUDWATCH_REGION = process.env.CLOUDWATCH_VIEWER_REGION || process.env.AWS_DEFAULT_REGION || "ca-central-1";
+const CLOUDWATCH_DEFAULT_GROUP = process.env.CLOUDWATCH_VIEWER_LOG_GROUP || "development";
+const CLOUDWATCH_DEFAULT_WINDOW_MS = Number(process.env.CLOUDWATCH_VIEWER_WINDOW_MS || 15 * 60 * 1000);
+const CLOUDWATCH_DEFAULT_LIMIT = Number(process.env.CLOUDWATCH_VIEWER_LIMIT || 200);
+const LOCALSTACK_CREDENTIALS = {
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID || "test",
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || "test"
+};
+const cloudWatchLogsClient = new CloudWatchLogsClient({
+ region: CLOUDWATCH_REGION,
+ endpoint: CLOUDWATCH_ENDPOINT,
+ credentials: LOCALSTACK_CREDENTIALS
+});
app.use((req, res, next) => {
res.set("Cache-Control", "no-store");
@@ -26,6 +46,10 @@ app.get("/health", (req, res) => {
res.json({
ok: true,
endpoint: SES_ENDPOINT,
+ endpoints: {
+ ses: SES_ENDPOINT,
+ cloudWatchLogs: CLOUDWATCH_ENDPOINT
+ },
port: PORT,
defaultRefreshMs: DEFAULT_REFRESH_MS
});
@@ -96,6 +120,71 @@ app.get("/api/messages/:id/attachments/:index", async (req, res) => {
}
});
+app.get("/api/logs/groups", async (req, res) => {
+ try {
+ const groups = await loadLogGroups();
+ res.json({
+ endpoint: CLOUDWATCH_ENDPOINT,
+ region: CLOUDWATCH_REGION,
+ defaultGroup: CLOUDWATCH_DEFAULT_GROUP,
+ groups
+ });
+ } catch (error) {
+ console.error("Error fetching log groups:", error);
+ res.status(502).json({
+ error: "Unable to fetch CloudWatch log groups from LocalStack",
+ details: error.message,
+ endpoint: CLOUDWATCH_ENDPOINT
+ });
+ }
+});
+
+app.get("/api/logs/streams", async (req, res) => {
+ try {
+ const logGroupName = String(req.query.group || "");
+
+ if (!logGroupName) {
+ res.status(400).json({ error: "Query parameter 'group' is required" });
+ return;
+ }
+
+ res.json({
+ logGroupName,
+ streams: await loadLogStreams(logGroupName)
+ });
+ } catch (error) {
+ console.error("Error fetching log streams:", error);
+ res.status(502).json({
+ error: "Unable to fetch CloudWatch log streams from LocalStack",
+ details: error.message,
+ endpoint: CLOUDWATCH_ENDPOINT
+ });
+ }
+});
+
+app.get("/api/logs/events", async (req, res) => {
+ try {
+ const logGroupName = String(req.query.group || "");
+ const logStreamName = String(req.query.stream || "");
+ const windowMs = clampNumber(req.query.windowMs, CLOUDWATCH_DEFAULT_WINDOW_MS, 60 * 1000, 24 * 60 * 60 * 1000);
+ const limit = clampNumber(req.query.limit, CLOUDWATCH_DEFAULT_LIMIT, 25, 500);
+
+ if (!logGroupName) {
+ res.status(400).json({ error: "Query parameter 'group' is required" });
+ return;
+ }
+
+ res.json(await loadLogEvents({ logGroupName, logStreamName, windowMs, limit }));
+ } catch (error) {
+ console.error("Error fetching log events:", error);
+ res.status(502).json({
+ error: "Unable to fetch CloudWatch log events from LocalStack",
+ details: error.message,
+ endpoint: CLOUDWATCH_ENDPOINT
+ });
+ }
+});
+
async function loadMessages() {
const startedAt = Date.now();
const sesMessages = await fetchSesMessages();
@@ -133,6 +222,136 @@ async function fetchSesMessages() {
return Array.isArray(data.messages) ? data.messages : [];
}
+async function loadLogGroups() {
+ const groups = [];
+ let nextToken;
+ let pageCount = 0;
+
+ do {
+ const response = await cloudWatchLogsClient.send(
+ new DescribeLogGroupsCommand({
+ nextToken,
+ limit: 50
+ })
+ );
+
+ groups.push(
+ ...(response.logGroups || []).map((group) => ({
+ name: group.logGroupName || "",
+ arn: group.arn || "",
+ storedBytes: group.storedBytes || 0,
+ retentionInDays: group.retentionInDays || 0,
+ creationTime: group.creationTime || 0
+ }))
+ );
+
+ nextToken = response.nextToken;
+ pageCount += 1;
+ } while (nextToken && pageCount < 10);
+
+ return groups.sort((left, right) => left.name.localeCompare(right.name));
+}
+
+async function loadLogStreams(logGroupName) {
+ const streams = [];
+ let nextToken;
+ let pageCount = 0;
+
+ do {
+ const response = await cloudWatchLogsClient.send(
+ new DescribeLogStreamsCommand({
+ logGroupName,
+ descending: true,
+ orderBy: "LastEventTime",
+ nextToken,
+ limit: 50
+ })
+ );
+
+ streams.push(
+ ...(response.logStreams || []).map((stream) => ({
+ name: stream.logStreamName || "",
+ arn: stream.arn || "",
+ lastEventTimestamp: stream.lastEventTimestamp || 0,
+ lastIngestionTime: stream.lastIngestionTime || 0,
+ storedBytes: stream.storedBytes || 0
+ }))
+ );
+
+ nextToken = response.nextToken;
+ pageCount += 1;
+ } while (nextToken && pageCount < 6 && streams.length < 250);
+
+ return streams;
+}
+
+async function loadLogEvents({ logGroupName, logStreamName, windowMs, limit }) {
+ const startedAt = Date.now();
+ const eventMap = new Map();
+ const startTime = Date.now() - windowMs;
+ let nextToken;
+ let previousToken = "";
+ let pageCount = 0;
+ let searchedLogStreams = 0;
+
+ do {
+ const response = await cloudWatchLogsClient.send(
+ new FilterLogEventsCommand({
+ logGroupName,
+ logStreamNames: logStreamName ? [logStreamName] : undefined,
+ startTime,
+ endTime: Date.now(),
+ limit,
+ nextToken
+ })
+ );
+
+ for (const event of response.events || []) {
+ const id =
+ event.eventId || `${event.logStreamName || "stream"}-${event.timestamp || 0}-${event.ingestionTime || 0}`;
+
+ if (!eventMap.has(id)) {
+ const message = String(event.message || "").trim();
+ eventMap.set(id, {
+ id,
+ timestamp: event.timestamp || 0,
+ ingestionTime: event.ingestionTime || 0,
+ logStreamName: event.logStreamName || "",
+ message,
+ preview: buildLogPreview(message)
+ });
+ }
+ }
+
+ searchedLogStreams = Math.max(searchedLogStreams, (response.searchedLogStreams || []).length);
+ previousToken = nextToken || "";
+ nextToken = response.nextToken;
+ pageCount += 1;
+ } while (nextToken && nextToken !== previousToken && pageCount < 10 && eventMap.size < limit);
+
+ const events = [...eventMap.values()]
+ .sort((left, right) => {
+ if ((right.timestamp || 0) !== (left.timestamp || 0)) {
+ return (right.timestamp || 0) - (left.timestamp || 0);
+ }
+
+ return left.logStreamName.localeCompare(right.logStreamName);
+ })
+ .slice(0, limit);
+
+ return {
+ endpoint: CLOUDWATCH_ENDPOINT,
+ region: CLOUDWATCH_REGION,
+ logGroupName,
+ logStreamName,
+ fetchDurationMs: Date.now() - startedAt,
+ latestTimestamp: events[0]?.timestamp || 0,
+ searchedLogStreams,
+ totalEvents: events.length,
+ events
+ };
+}
+
async function findSesMessageById(id) {
const messages = await fetchSesMessages();
return messages.find((message, index) => resolveMessageId(message, index) === id) || null;
@@ -274,6 +493,28 @@ function buildPreview(textContent, renderedHtml) {
return source.length > 220 ? `${source.slice(0, 217)}...` : source;
}
+function buildLogPreview(message) {
+ const source = String(message || "")
+ .replace(/\s+/g, " ")
+ .trim();
+
+ if (!source) {
+ return "No log preview available.";
+ }
+
+ return source.length > 220 ? `${source.slice(0, 217)}...` : source;
+}
+
+function clampNumber(value, fallback, min, max) {
+ const parsed = Number(value);
+
+ if (!Number.isFinite(parsed)) {
+ return fallback;
+ }
+
+ return Math.min(Math.max(parsed, min), max);
+}
+
function buildRenderedHtml(html) {
if (!html) {
return "";
@@ -325,7 +566,12 @@ function formatAddressList(addresses) {
function getClientConfig() {
return {
defaultRefreshMs: DEFAULT_REFRESH_MS,
- endpoint: SES_ENDPOINT
+ endpoint: SES_ENDPOINT,
+ cloudWatchEndpoint: CLOUDWATCH_ENDPOINT,
+ cloudWatchRegion: CLOUDWATCH_REGION,
+ defaultLogGroup: CLOUDWATCH_DEFAULT_GROUP,
+ defaultLogWindowMs: CLOUDWATCH_DEFAULT_WINDOW_MS,
+ defaultLogLimit: CLOUDWATCH_DEFAULT_LIMIT
};
}
@@ -335,18 +581,28 @@ function renderHtml() {
- LocalStack Email Viewer
+ LocalStack Inspector
-
LocalStack SES
-
Email Viewer
-
Compact inbox for generated emails with live polling, search, and raw MIME inspection.
+
LocalStack Toolbox
+
Inspector
+
Inspect generated SES mail and CloudWatch logs from the same local stack console.
+
+
+
+
+
Keep outbound email inspection and CloudWatch tailing in one local viewer without leaving the browser.
+
+
+
+
+
+
+
+ Total00 visible
+ New0New since last refresh
+ NewestNo messagesNot refreshed yet
+ FetchIdleEndpoint: ${escapeHtml(SES_ENDPOINT)}
+
+
+
-
-
-
- Total00 visible
- New0New since last refresh
- NewestNo messagesNot refreshed yet
- FetchIdleEndpoint: ${escapeHtml(SES_ENDPOINT)}
-
-
-
+
+
+
+
+ Events00 visible
+ Streams0Streams in selected group
+ LatestNo eventsNot refreshed yet
+ FetchIdleEndpoint: ${escapeHtml(CLOUDWATCH_ENDPOINT)} (${escapeHtml(CLOUDWATCH_REGION)})
+
+
+
+
@@ -391,23 +712,32 @@ function renderHtml() {
function renderStyles() {
return `
- :root{--panel:rgba(255,255,255,.82);--panel-strong:#fff;--card-shell:linear-gradient(180deg,rgba(255,246,236,.98),rgba(255,252,247,.99));--card-body:#fffdf9;--ink:#1f2933;--muted:#607080;--line:rgba(31,41,51,.12);--card-line:rgba(207,109,60,.24);--accent:#cf6d3c;--accent-soft:rgba(207,109,60,.1);--ok:#1f8f65;--warn:#9d5f00;--bad:#b33a3a;--shadow:0 12px 28px rgba(35,43,53,.08);--card-shadow:0 18px 34px rgba(122,78,34,.12);}
+ :root{--panel:rgba(255,255,255,.82);--panel-strong:#fff;--card-shell:linear-gradient(180deg,rgba(255,246,236,.98),rgba(255,252,247,.99));--card-body:#fffdf9;--log-shell:linear-gradient(180deg,rgba(239,246,255,.98),rgba(248,251,255,.99));--log-body:#f8fbff;--ink:#1f2933;--muted:#607080;--line:rgba(31,41,51,.12);--card-line:rgba(207,109,60,.24);--log-line:rgba(48,113,169,.22);--accent:#cf6d3c;--accent-soft:rgba(207,109,60,.1);--info:#3071a9;--info-soft:rgba(48,113,169,.1);--ok:#1f8f65;--warn:#9d5f00;--bad:#b33a3a;--shadow:0 12px 28px rgba(35,43,53,.08);--card-shadow:0 18px 34px rgba(122,78,34,.12);--log-shadow:0 16px 32px rgba(48,113,169,.12);}
*{box-sizing:border-box}
html,body{margin:0;min-height:100%}
body{background:radial-gradient(circle at top left,rgba(207,109,60,.18),transparent 28%),radial-gradient(circle at top right,rgba(31,143,101,.12),transparent 24%),linear-gradient(180deg,#f8f5ef,#efe7da);color:var(--ink);font:15px/1.45 "Aptos","Segoe UI Variable Display","Segoe UI",system-ui,sans-serif}
button,input,select,textarea{font:inherit}
button{cursor:pointer}
- .page{max-width:1360px;margin:0 auto;padding:18px}
- .hero{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(320px,.95fr);gap:14px;margin-bottom:14px}
- .hero-copy,.hero-controls,.stat{background:var(--panel);backdrop-filter:blur(14px);border:1px solid var(--line);box-shadow:var(--shadow)}
+ .page{display:grid;gap:14px;max-width:1360px;min-height:100vh;margin:0 auto;padding:18px}
+ .hero{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(320px,.95fr);gap:14px;margin-bottom:0}
+ .hero-copy,.hero-controls,.toolControls,.stat{background:var(--panel);backdrop-filter:blur(14px);border:1px solid var(--line);box-shadow:var(--shadow)}
.card{background:var(--card-shell);border:1px solid var(--card-line);box-shadow:var(--card-shadow)}
- .hero-copy,.hero-controls{border-radius:18px;padding:18px}
+ .hero-copy,.hero-controls,.toolControls{border-radius:18px;padding:18px}
.eyebrow{margin:0 0 6px;color:var(--accent);font-size:.76rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase}
h1{margin:0;font-size:clamp(1.9rem,4vw,3.1rem);line-height:.98;letter-spacing:-.05em}
.lede{margin:10px 0 0;max-width:54ch;color:var(--muted);font-size:.96rem}
.hero-controls{display:grid;gap:10px}
+ .helper{margin:0;color:var(--muted);font-size:.95rem}
+ .workspaceTabs{display:flex;flex-wrap:wrap;gap:8px;padding:6px;border-radius:999px;background:rgba(31,41,51,.05)}
+ .workspaceTab,.primary,.ghost,.mini,.tab{border-radius:999px;border:1px solid transparent;transition:transform .12s ease,background-color .12s ease,border-color .12s ease}
+ .workspaceTab{min-height:34px;padding:0 14px;background:transparent;color:var(--muted);font-weight:700}
+ .workspaceTab.active{background:#fff;border-color:rgba(31,41,51,.08);color:var(--ink)}
+ .workspacePanel{display:grid;gap:12px}
+ .workspacePanel[hidden]{display:none}
+ .toolControls{display:grid;gap:10px}
+ .contentPane{height:clamp(360px,50vh,720px);overflow:auto;padding-right:4px}
+ .contentStack{display:grid;gap:12px;min-width:min-content}
.row{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
- .primary,.ghost,.mini,.tab{border-radius:999px;border:1px solid transparent;transition:transform .12s ease,background-color .12s ease,border-color .12s ease}
.primary,.ghost{min-height:38px;padding:0 14px;font-weight:700}
.mini,.tab{min-height:30px;padding:0 10px;font-weight:600}
.primary{background:var(--accent);color:#fff7f2}
@@ -429,9 +759,10 @@ function renderStyles() {
.stat strong{display:block;font-size:clamp(1.6rem,3vw,2rem);line-height:1;letter-spacing:-.05em}
.stat strong.small{font-size:1.1rem;line-height:1.3;letter-spacing:-.02em}
.stat small{display:block;margin-top:8px;color:var(--muted);font-size:.85rem}
- .banner,.empty{margin:0 0 12px;padding:12px 14px;border-radius:14px;border:1px solid var(--line);background:rgba(255,255,255,.82)}
+ .banner,.empty{margin:0;padding:12px 14px;border-radius:14px;border:1px solid var(--line);background:rgba(255,255,255,.82)}
.banner{color:var(--bad);border-color:rgba(179,58,58,.24);background:rgba(179,58,58,.08)}
- .list{display:grid;gap:12px}
+ .list{display:grid;gap:12px;align-content:start}
+ .logList{display:grid;gap:10px;align-content:start}
.card{overflow:hidden;border-radius:16px}
.card.new{border-color:rgba(31,143,101,.3);box-shadow:var(--card-shadow),0 0 0 1px rgba(31,143,101,.12)}
.summary{list-style:none;display:grid;gap:8px;padding:14px 16px;cursor:pointer;background:linear-gradient(180deg,rgba(255,250,244,.88),rgba(255,246,238,.96))}
@@ -459,12 +790,22 @@ function renderStyles() {
.attachmentLink{color:#8d5632;text-decoration:none;transition:transform .12s ease,background-color .12s ease,border-color .12s ease}
.attachmentLink:hover{transform:translateY(-1px);background:#fff;border-color:rgba(207,109,60,.28)}
.panel{overflow:hidden;border-radius:12px;border:1px solid rgba(207,109,60,.14);background:#fff}
+ .logEvent{overflow:hidden;border-radius:16px;border:1px solid var(--log-line);background:var(--log-shell);box-shadow:var(--log-shadow)}
+ .logSummary{list-style:none;display:grid;gap:8px;padding:12px 14px;cursor:pointer}
+ .logSummary::-webkit-details-marker{display:none}
+ .logSummaryTop{display:flex;flex-wrap:wrap;gap:8px;justify-content:space-between;align-items:center}
+ .logMeta{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
+ .logTag{background:var(--info-soft);color:var(--info);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
+ .logPreview{margin:0;color:#324150;font-size:.9rem}
+ .logBody{padding:0 14px 14px;border-top:1px solid rgba(48,113,169,.14);background:var(--log-body)}
+ .logActions{display:flex;justify-content:flex-end;padding:12px 0 0}
+ .logBody pre{background:linear-gradient(180deg,rgba(48,113,169,.04),transparent 140px),#fff}
iframe{width:100%;min-height:560px;border:none;background:#fff}
pre{margin:0;padding:12px;white-space:pre-wrap;word-break:break-word;overflow:auto;font:12.5px/1.45 "Cascadia Code","Consolas",monospace;color:#102030;background:linear-gradient(180deg,rgba(207,109,60,.04),transparent 140px),#fff}
.placeholder,.inlineError{padding:12px}
.inlineError{color:var(--bad)}
@media (max-width:1080px){.hero{grid-template-columns:1fr}.stats{grid-template-columns:repeat(2,minmax(0,1fr))}}
- @media (max-width:720px){.page{padding:12px}.stats{grid-template-columns:1fr}.primary,.ghost,.chip{width:100%;justify-content:center}.toolbar,.row{align-items:stretch}iframe{min-height:420px}}
+ @media (max-width:720px){.page{padding:12px}.stats{grid-template-columns:1fr}.workspaceTab,.primary,.ghost,.chip{width:100%;justify-content:center}.toolbar,.row{align-items:stretch}.logSummaryTop{align-items:flex-start}.contentPane{height:clamp(300px,48vh,560px)}iframe{min-height:420px}}
`;
}
@@ -478,6 +819,11 @@ function escapeHtml(value) {
}
function clientApp(config) {
+ const appState = {
+ panel: "emails",
+ logsReady: false
+ };
+
const state = {
messages: [],
filtered: [],
@@ -499,7 +845,34 @@ function clientApp(config) {
listSignature: ""
};
+ const logsState = {
+ groups: [],
+ streams: [],
+ events: [],
+ filtered: [],
+ group: config.defaultLogGroup || "",
+ stream: "",
+ search: "",
+ auto: true,
+ interval: config.defaultRefreshMs,
+ windowMs: config.defaultLogWindowMs,
+ limit: config.defaultLogLimit,
+ loading: false,
+ error: "",
+ updatedAt: 0,
+ source: "initial",
+ duration: 0,
+ newest: 0,
+ searchedLogStreams: 0,
+ openIds: new Set(),
+ hasInteracted: false,
+ listSignature: ""
+ };
+
const el = {
+ workspaceTabs: document.getElementById("workspaceTabs"),
+ emailsPanel: document.getElementById("emailsPanel"),
+ logsPanel: document.getElementById("logsPanel"),
refreshButton: document.getElementById("refreshButton"),
autoToggle: document.getElementById("autoToggle"),
intervalSelect: document.getElementById("intervalSelect"),
@@ -517,16 +890,54 @@ function clientApp(config) {
fetchDetail: document.getElementById("fetchDetail"),
banner: document.getElementById("banner"),
empty: document.getElementById("empty"),
- list: document.getElementById("list")
+ list: document.getElementById("list"),
+ logsRefreshButton: document.getElementById("logsRefreshButton"),
+ logsAutoToggle: document.getElementById("logsAutoToggle"),
+ logsIntervalSelect: document.getElementById("logsIntervalSelect"),
+ logsGroupSelect: document.getElementById("logsGroupSelect"),
+ logsStreamSelect: document.getElementById("logsStreamSelect"),
+ logsWindowSelect: document.getElementById("logsWindowSelect"),
+ logsLimitSelect: document.getElementById("logsLimitSelect"),
+ logsSearchInput: document.getElementById("logsSearchInput"),
+ logsClearSearchButton: document.getElementById("logsClearSearchButton"),
+ logsStatusChip: document.getElementById("logsStatusChip"),
+ logsTotalStat: document.getElementById("logsTotalStat"),
+ logsVisibleStat: document.getElementById("logsVisibleStat"),
+ logsStreamsStat: document.getElementById("logsStreamsStat"),
+ logsNewestStat: document.getElementById("logsNewestStat"),
+ logsUpdatedStat: document.getElementById("logsUpdatedStat"),
+ logsFetchStat: document.getElementById("logsFetchStat"),
+ logsFetchDetail: document.getElementById("logsFetchDetail"),
+ logsBanner: document.getElementById("logsBanner"),
+ logsEmpty: document.getElementById("logsEmpty"),
+ logsList: document.getElementById("logsList")
};
el.intervalSelect.value = String(config.defaultRefreshMs);
+ el.logsIntervalSelect.value = String(config.defaultRefreshMs);
+ el.logsWindowSelect.value = String(config.defaultLogWindowMs);
+ el.logsLimitSelect.value = String(config.defaultLogLimit);
wire();
+ renderWorkspace();
renderAll();
+ renderLogsAll();
refreshMessages("initial");
- window.setInterval(renderLiveClock, 1000);
+ window.setInterval(() => {
+ renderLiveClock();
+ renderLogsLiveClock();
+ }, 1000);
function wire() {
+ el.workspaceTabs.addEventListener("click", async (event) => {
+ const button = event.target.closest("button[data-panel]");
+
+ if (!button) {
+ return;
+ }
+
+ await setPanel(button.dataset.panel);
+ });
+
el.refreshButton.addEventListener("click", () => refreshMessages("manual"));
el.autoToggle.addEventListener("change", () => {
@@ -598,10 +1009,88 @@ function clientApp(config) {
}
});
+ el.logsRefreshButton.addEventListener("click", () => refreshLogs("manual"));
+
+ el.logsAutoToggle.addEventListener("change", () => {
+ logsState.auto = el.logsAutoToggle.checked;
+ scheduleLogsRefresh();
+ renderLogsStatus();
+ });
+
+ el.logsIntervalSelect.addEventListener("change", () => {
+ logsState.interval = Number(el.logsIntervalSelect.value) || config.defaultRefreshMs;
+ scheduleLogsRefresh();
+ renderLogsStatus();
+ });
+
+ el.logsWindowSelect.addEventListener("change", () => {
+ logsState.windowMs = Number(el.logsWindowSelect.value) || config.defaultLogWindowMs;
+ refreshLogs("window");
+ });
+
+ el.logsLimitSelect.addEventListener("change", () => {
+ logsState.limit = Number(el.logsLimitSelect.value) || config.defaultLogLimit;
+ refreshLogs("limit");
+ });
+
+ el.logsSearchInput.addEventListener("input", (event) => {
+ logsState.search = event.target.value;
+ applyLogsFilter();
+ });
+
+ el.logsClearSearchButton.addEventListener("click", () => {
+ logsState.search = "";
+ el.logsSearchInput.value = "";
+ applyLogsFilter();
+ });
+
+ el.logsGroupSelect.addEventListener("change", async () => {
+ logsState.group = el.logsGroupSelect.value;
+ logsState.stream = "";
+ await refreshLogStreams();
+ await refreshLogs("group");
+ });
+
+ el.logsStreamSelect.addEventListener("change", () => {
+ logsState.stream = el.logsStreamSelect.value;
+ refreshLogs("stream");
+ });
+
+ el.logsList.addEventListener("click", async (event) => {
+ const button = event.target.closest("button[data-log-action]");
+
+ if (!button) {
+ return;
+ }
+
+ const logEvent = getLogEvent(button.dataset.id);
+
+ if (!logEvent) {
+ return;
+ }
+
+ if (button.dataset.logAction === "copy") {
+ await copyText(formatLogMessage(logEvent.message));
+ setLogsStatus("Log payload copied to the clipboard.", "ok");
+ }
+ });
+
document.addEventListener("visibilitychange", () => {
window.clearTimeout(state.timer);
+ window.clearTimeout(logsState.timer);
- if (!document.hidden && state.auto) {
+ if (document.hidden) {
+ renderStatus();
+ renderLogsStatus();
+ return;
+ }
+
+ if (appState.panel === "logs" && logsState.auto) {
+ refreshLogs("visibility");
+ return;
+ }
+
+ if (state.auto) {
refreshMessages("visibility");
} else {
renderStatus();
@@ -615,11 +1104,64 @@ function clientApp(config) {
if (!isField && event.key.toLowerCase() === "r") {
event.preventDefault();
+
+ if (appState.panel === "logs") {
+ refreshLogs("keyboard");
+ return;
+ }
+
refreshMessages("keyboard");
}
});
}
+ async function setPanel(panel) {
+ if (!panel || panel === appState.panel) {
+ if (panel === "logs") {
+ await ensureLogsReady();
+ }
+
+ renderWorkspace();
+ return;
+ }
+
+ appState.panel = panel;
+ renderWorkspace();
+
+ if (panel === "logs") {
+ await ensureLogsReady();
+ }
+
+ scheduleRefresh();
+ scheduleLogsRefresh();
+ renderStatus();
+ renderLogsStatus();
+ }
+
+ function renderWorkspace() {
+ el.emailsPanel.hidden = appState.panel !== "emails";
+ el.logsPanel.hidden = appState.panel !== "logs";
+
+ el.workspaceTabs.querySelectorAll("button[data-panel]").forEach((button) => {
+ const active = button.dataset.panel === appState.panel;
+ button.classList.toggle("active", active);
+ button.setAttribute("aria-pressed", active ? "true" : "false");
+ });
+ }
+
+ async function ensureLogsReady() {
+ if (appState.logsReady) {
+ return;
+ }
+
+ await refreshLogGroups();
+ appState.logsReady = !logsState.error;
+
+ if (logsState.group) {
+ await refreshLogs("initial");
+ }
+ }
+
async function refreshMessages(source) {
if (state.loading) {
return;
@@ -672,6 +1214,144 @@ function clientApp(config) {
}
}
+ async function refreshLogGroups() {
+ try {
+ const response = await fetch("/api/logs/groups", { cache: "no-store" });
+
+ if (!response.ok) {
+ const payload = await safeJson(response);
+ throw new Error(payload?.details || payload?.error || `Request failed with ${response.status}`);
+ }
+
+ const payload = await response.json();
+ logsState.groups = Array.isArray(payload.groups) ? payload.groups : [];
+
+ const availableGroups = logsState.groups.map((group) => group.name).filter(Boolean);
+
+ if (!availableGroups.includes(logsState.group)) {
+ logsState.group = availableGroups.includes(config.defaultLogGroup)
+ ? config.defaultLogGroup
+ : availableGroups[0] || "";
+ }
+
+ logsState.error = "";
+ await refreshLogStreams();
+ } catch (error) {
+ logsState.error = error.message || "Unknown log group refresh error";
+ } finally {
+ renderLogsAll();
+ }
+ }
+
+ async function refreshLogStreams() {
+ if (!logsState.group) {
+ logsState.streams = [];
+ logsState.stream = "";
+ renderLogsAll();
+ return;
+ }
+
+ try {
+ const response = await fetch(`/api/logs/streams?group=${encodeURIComponent(logsState.group)}`, {
+ cache: "no-store"
+ });
+
+ if (!response.ok) {
+ const payload = await safeJson(response);
+ throw new Error(payload?.details || payload?.error || `Request failed with ${response.status}`);
+ }
+
+ const payload = await response.json();
+ logsState.streams = Array.isArray(payload.streams) ? payload.streams : [];
+
+ if (!logsState.streams.some((stream) => stream.name === logsState.stream)) {
+ logsState.stream = "";
+ }
+
+ logsState.error = "";
+ } catch (error) {
+ logsState.streams = [];
+ logsState.stream = "";
+ logsState.error = error.message || "Unknown log stream refresh error";
+ } finally {
+ renderLogsAll();
+ }
+ }
+
+ async function refreshLogs(source) {
+ if (logsState.loading) {
+ return;
+ }
+
+ if (!appState.logsReady) {
+ await ensureLogsReady();
+ return;
+ }
+
+ if (!logsState.group) {
+ logsState.error = logsState.groups.length ? "Choose a log group to load events." : "No log groups found.";
+ renderLogsAll();
+ return;
+ }
+
+ let shouldRenderList = false;
+
+ logsState.loading = true;
+ logsState.source = source;
+ logsState.error = "";
+ renderLogsStatus();
+ renderLogsFetch();
+
+ try {
+ const params = new URLSearchParams({
+ group: logsState.group,
+ windowMs: String(logsState.windowMs),
+ limit: String(logsState.limit)
+ });
+
+ if (logsState.stream) {
+ params.set("stream", logsState.stream);
+ }
+
+ const response = await fetch(`/api/logs/events?${params.toString()}`, { cache: "no-store" });
+
+ if (!response.ok) {
+ const payload = await safeJson(response);
+ throw new Error(payload?.details || payload?.error || `Request failed with ${response.status}`);
+ }
+
+ const payload = await response.json();
+ const events = Array.isArray(payload.events) ? payload.events : [];
+ const nextSignature = computeLogListSignature(events);
+ shouldRenderList = nextSignature !== logsState.listSignature;
+ logsState.events = events;
+ logsState.duration = payload.fetchDurationMs || 0;
+ logsState.newest = payload.latestTimestamp || 0;
+ logsState.updatedAt = Date.now();
+ logsState.searchedLogStreams = payload.searchedLogStreams || (logsState.stream ? 1 : logsState.streams.length);
+ logsState.listSignature = nextSignature;
+
+ pruneLogsState();
+ applyLogsFilter(shouldRenderList);
+ setLogsStatus(`Updated ${logsState.events.length} log event${logsState.events.length === 1 ? "" : "s"}.`, "ok");
+ } catch (error) {
+ logsState.error = error.message || "Unknown log refresh error";
+ setLogsStatus(`Refresh failed: ${logsState.error}`, "bad");
+ } finally {
+ logsState.loading = false;
+ scheduleLogsRefresh();
+ renderLogsAll({ renderList: shouldRenderList });
+ }
+ }
+
+ function applyLogsFilter(shouldRenderList = true) {
+ const search = logsState.search.trim().toLowerCase();
+ logsState.filtered = !search
+ ? [...logsState.events]
+ : logsState.events.filter((event) => logHaystack(event).includes(search));
+ renderLogsAll({ renderList: shouldRenderList });
+ }
+
function pruneState() {
const ids = new Set(state.messages.map((message) => message.id));
state.openIds = new Set([...state.openIds].filter((id) => ids.has(id)));
@@ -690,6 +1370,11 @@ function clientApp(config) {
});
}
+ function pruneLogsState() {
+ const ids = new Set(logsState.events.map((event) => event.id));
+ logsState.openIds = new Set([...logsState.openIds].filter((id) => ids.has(id)));
+ }
+
function applyFilter(shouldRenderList = true) {
const search = state.search.trim().toLowerCase();
state.filtered = !search
@@ -714,6 +1399,16 @@ function clientApp(config) {
.join("|");
}
+ function computeLogListSignature(events) {
+ return events
+ .map((event) =>
+ [event.id, event.timestamp || 0, event.ingestionTime || 0, event.logStreamName || "", event.message || ""].join(
+ "::"
+ )
+ )
+ .join("|");
+ }
+
function haystack(message) {
return [
message.subject,
@@ -730,16 +1425,30 @@ function clientApp(config) {
.toLowerCase();
}
+ function logHaystack(event) {
+ return [event.logStreamName, event.message, event.preview].filter(Boolean).join(" ").toLowerCase();
+ }
+
function scheduleRefresh() {
window.clearTimeout(state.timer);
- if (!state.auto || document.hidden || state.loading) {
+ if (appState.panel !== "emails" || !state.auto || document.hidden || state.loading) {
return;
}
state.timer = window.setTimeout(() => refreshMessages("auto"), state.interval);
}
+ function scheduleLogsRefresh() {
+ window.clearTimeout(logsState.timer);
+
+ if (appState.panel !== "logs" || !logsState.auto || document.hidden || logsState.loading) {
+ return;
+ }
+
+ logsState.timer = window.setTimeout(() => refreshLogs("auto"), logsState.interval);
+ }
+
function renderAll(options = {}) {
const { renderList: shouldRenderList = true } = options;
renderStats();
@@ -751,6 +1460,18 @@ function clientApp(config) {
renderLiveClock();
}
+ function renderLogsAll(options = {}) {
+ const { renderList: shouldRenderList = true } = options;
+ renderLogsFilters();
+ renderLogsStats();
+ renderLogsFetch();
+ renderLogsStatus();
+ if (shouldRenderList) {
+ renderLogsList();
+ }
+ renderLogsLiveClock();
+ }
+
function renderStats() {
el.totalStat.textContent = String(state.messages.length);
el.visibleStat.textContent = `${state.filtered.length} visible${state.search ? " after search" : ""}`;
@@ -758,6 +1479,34 @@ function clientApp(config) {
el.newestStat.textContent = state.newest ? formatDateTime(state.newest) : "No messages";
}
+ function renderLogsFilters() {
+ const groups = logsState.groups.length
+ ? logsState.groups.map((group) => ``)
+ : [''];
+ const streams = [
+ '',
+ ...logsState.streams.map(
+ (stream) => ``
+ )
+ ];
+
+ el.logsGroupSelect.innerHTML = groups.join("");
+ el.logsStreamSelect.innerHTML = streams.join("");
+
+ if (logsState.group) {
+ el.logsGroupSelect.value = logsState.group;
+ }
+
+ el.logsStreamSelect.value = logsState.stream;
+ }
+
+ function renderLogsStats() {
+ el.logsTotalStat.textContent = String(logsState.events.length);
+ el.logsVisibleStat.textContent = `${logsState.filtered.length} visible${logsState.search ? " after search" : ""}`;
+ el.logsStreamsStat.textContent = String(logsState.streams.length || logsState.searchedLogStreams || 0);
+ el.logsNewestStat.textContent = logsState.newest ? formatDateTime(logsState.newest) : "No events";
+ }
+
function renderFetch() {
if (state.loading) {
el.fetchStat.textContent = "Refreshing...";
@@ -781,6 +1530,29 @@ function clientApp(config) {
el.fetchDetail.textContent = `${state.parseErrors} parse error${state.parseErrors === 1 ? "" : "s"}. Endpoint: ${config.endpoint}`;
}
+ function renderLogsFetch() {
+ if (logsState.loading) {
+ el.logsFetchStat.textContent = "Refreshing...";
+ el.logsFetchDetail.textContent = `Endpoint: ${config.cloudWatchEndpoint} (${config.cloudWatchRegion})`;
+ return;
+ }
+
+ if (logsState.error) {
+ el.logsFetchStat.textContent = "Needs attention";
+ el.logsFetchDetail.textContent = logsState.error;
+ return;
+ }
+
+ if (!logsState.updatedAt) {
+ el.logsFetchStat.textContent = "Idle";
+ el.logsFetchDetail.textContent = `Endpoint: ${config.cloudWatchEndpoint} (${config.cloudWatchRegion})`;
+ return;
+ }
+
+ el.logsFetchStat.textContent = `${logsState.duration}ms`;
+ el.logsFetchDetail.textContent = `${logsState.group || "No group selected"}. Endpoint: ${config.cloudWatchEndpoint}`;
+ }
+
function renderStatus() {
el.statusChip.className = "status";
@@ -817,6 +1589,42 @@ function clientApp(config) {
el.statusChip.textContent = `Live refresh on, next check in ${seconds}s`;
}
+ function renderLogsStatus() {
+ el.logsStatusChip.className = "status";
+
+ if (logsState.loading) {
+ el.logsStatusChip.classList.add("warn");
+ el.logsStatusChip.textContent = "Refreshing logs...";
+ return;
+ }
+
+ if (logsState.error) {
+ el.logsStatusChip.classList.add("bad");
+ el.logsStatusChip.textContent = `Refresh failed: ${logsState.error}`;
+ return;
+ }
+
+ if (!logsState.auto) {
+ el.logsStatusChip.textContent = "Live refresh paused";
+ return;
+ }
+
+ if (document.hidden) {
+ el.logsStatusChip.classList.add("warn");
+ el.logsStatusChip.textContent = "Tab hidden, live refresh paused";
+ return;
+ }
+
+ if (!logsState.updatedAt) {
+ el.logsStatusChip.textContent = "Waiting for first refresh...";
+ return;
+ }
+
+ const seconds = Math.max(0, Math.ceil((logsState.updatedAt + logsState.interval - Date.now()) / 1000));
+ el.logsStatusChip.classList.add("ok");
+ el.logsStatusChip.textContent = `Live refresh on, next check in ${seconds}s`;
+ }
+
function renderLiveClock() {
if (!state.updatedAt) {
el.updatedStat.textContent = "Not refreshed yet";
@@ -827,6 +1635,16 @@ function clientApp(config) {
renderStatus();
}
+ function renderLogsLiveClock() {
+ if (!logsState.updatedAt) {
+ el.logsUpdatedStat.textContent = "Not refreshed yet";
+ return;
+ }
+
+ el.logsUpdatedStat.textContent = `Updated ${formatRelative(logsState.updatedAt)} via ${logsState.source}`;
+ renderLogsStatus();
+ }
+
function renderList() {
el.banner.hidden = !state.error;
el.banner.textContent = state.error ? `Refresh failed: ${state.error}` : "";
@@ -846,6 +1664,76 @@ function clientApp(config) {
el.list.querySelectorAll(".card[open]").forEach((details) => hydrate(details, getMessage(details.dataset.id)));
}
+ function renderLogsList() {
+ el.logsBanner.hidden = !logsState.error;
+ el.logsBanner.textContent = logsState.error ? `Refresh failed: ${logsState.error}` : "";
+
+ if (!logsState.group && !logsState.groups.length) {
+ el.logsList.innerHTML = "";
+ el.logsEmpty.hidden = false;
+ el.logsEmpty.textContent = "No CloudWatch log groups found in LocalStack yet.";
+ return;
+ }
+
+ if (!logsState.filtered.length) {
+ el.logsList.innerHTML = "";
+ el.logsEmpty.hidden = false;
+ el.logsEmpty.textContent = logsState.events.length
+ ? "No log events match the current search."
+ : "No log events found for the selected group, stream, and time window.";
+ return;
+ }
+
+ el.logsEmpty.hidden = true;
+ el.logsList.innerHTML = logsState.filtered.map((event, index) => renderLogEvent(event, index)).join("");
+ bindLogToggles();
+ }
+
+ function renderLogEvent(event, index) {
+ const open = logsState.openIds.has(event.id) || (!logsState.hasInteracted && index === 0) ? "open" : "";
+
+ return `
+
+
+
+
+ ${escapeHtml(formatDateTime(event.timestamp))}
+ ${escapeHtml(event.logStreamName || "Unknown stream")}
+
+
${escapeHtml(formatRelative(event.timestamp || Date.now()))}
+
+ ${escapeHtml(event.preview || "No preview available.")}
+
+
+
+
+
+
${escapeHtml(formatLogMessage(event.message))}
+
+
+ `;
+ }
+
+ function bindLogToggles() {
+ el.logsList.querySelectorAll(".logEvent").forEach((details) => {
+ details.addEventListener("toggle", () => {
+ const id = details.dataset.id;
+
+ if (!id) {
+ return;
+ }
+
+ logsState.hasInteracted = true;
+
+ if (details.open) {
+ logsState.openIds.add(id);
+ } else {
+ logsState.openIds.delete(id);
+ }
+ });
+ });
+ }
+
function bindCardToggles() {
el.list.querySelectorAll(".card").forEach((details) => {
details.addEventListener("toggle", () => {
@@ -999,6 +1887,10 @@ function clientApp(config) {
return state.messages.find((message) => message.id === id);
}
+ function getLogEvent(id) {
+ return logsState.events.find((event) => event.id === id);
+ }
+
async function loadRaw(id) {
if (state.raw[id]?.status === "loaded") {
return state.raw[id].value;
@@ -1056,6 +1948,16 @@ function clientApp(config) {
el.statusChip.textContent = message;
}
+ function setLogsStatus(message, tone) {
+ el.logsStatusChip.className = "status";
+
+ if (tone) {
+ el.logsStatusChip.classList.add(tone);
+ }
+
+ el.logsStatusChip.textContent = message;
+ }
+
function formatDateTime(value) {
if (!value) {
return "Unknown time";
@@ -1106,6 +2008,20 @@ function clientApp(config) {
return `${index === 0 ? size : size.toFixed(1)} ${units[index]}`;
}
+ function formatLogMessage(message) {
+ const value = String(message || "").trim();
+
+ if (!value) {
+ return "No log payload.";
+ }
+
+ try {
+ return JSON.stringify(JSON.parse(value), null, 2);
+ } catch {
+ return value;
+ }
+ }
+
function escapeHtml(value) {
return String(value ?? "")
.replace(/&/g, "&")
@@ -1125,6 +2041,7 @@ function clientApp(config) {
}
app.listen(PORT, () => {
- console.log(`Local email viewer is running on http://localhost:${PORT}`);
+ console.log(`LocalStack inspector is running on http://localhost:${PORT}`);
console.log(`Watching LocalStack SES endpoint at ${SES_ENDPOINT}`);
+ console.log(`Watching LocalStack CloudWatch Logs endpoint at ${CLOUDWATCH_ENDPOINT} (${CLOUDWATCH_REGION})`);
});
diff --git a/_reference/localEmailViewer/package-lock.json b/_reference/localEmailViewer/package-lock.json
index 2c7ecad93..2dec2be60 100644
--- a/_reference/localEmailViewer/package-lock.json
+++ b/_reference/localEmailViewer/package-lock.json
@@ -9,11 +9,634 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
+ "@aws-sdk/client-cloudwatch-logs": "^3.1012.0",
"express": "^5.1.0",
"mailparser": "^3.7.4",
"node-fetch": "^3.3.2"
}
},
+ "node_modules/@aws-crypto/crc32": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz",
+ "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-browser": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz",
+ "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-js": "^5.2.0",
+ "@aws-crypto/supports-web-crypto": "^5.2.0",
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "@aws-sdk/util-locate-window": "^3.0.0",
+ "@smithy/util-utf8": "^2.0.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz",
+ "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz",
+ "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/is-array-buffer": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz",
+ "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-buffer-from": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-js": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz",
+ "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/supports-web-crypto": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz",
+ "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/util": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz",
+ "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.222.0",
+ "@smithy/util-utf8": "^2.0.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz",
+ "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz",
+ "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/is-array-buffer": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz",
+ "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-buffer-from": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-cloudwatch-logs": {
+ "version": "3.1012.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.1012.0.tgz",
+ "integrity": "sha512-nkMczmeCG9a1YD48zDKZLX7wLGb5ycXMJXVFnXF8xVxQ2v2uo2xFz1ZZganWHSQxR6ZfBPG7R9OTkDrVx79ZPA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/credential-provider-node": "^3.972.22",
+ "@aws-sdk/middleware-host-header": "^3.972.8",
+ "@aws-sdk/middleware-logger": "^3.972.8",
+ "@aws-sdk/middleware-recursion-detection": "^3.972.8",
+ "@aws-sdk/middleware-user-agent": "^3.972.22",
+ "@aws-sdk/region-config-resolver": "^3.972.8",
+ "@aws-sdk/types": "^3.973.6",
+ "@aws-sdk/util-endpoints": "^3.996.5",
+ "@aws-sdk/util-user-agent-browser": "^3.972.8",
+ "@aws-sdk/util-user-agent-node": "^3.973.8",
+ "@smithy/config-resolver": "^4.4.11",
+ "@smithy/core": "^3.23.12",
+ "@smithy/eventstream-serde-browser": "^4.2.12",
+ "@smithy/eventstream-serde-config-resolver": "^4.3.12",
+ "@smithy/eventstream-serde-node": "^4.2.12",
+ "@smithy/fetch-http-handler": "^5.3.15",
+ "@smithy/hash-node": "^4.2.12",
+ "@smithy/invalid-dependency": "^4.2.12",
+ "@smithy/middleware-content-length": "^4.2.12",
+ "@smithy/middleware-endpoint": "^4.4.26",
+ "@smithy/middleware-retry": "^4.4.43",
+ "@smithy/middleware-serde": "^4.2.15",
+ "@smithy/middleware-stack": "^4.2.12",
+ "@smithy/node-config-provider": "^4.3.12",
+ "@smithy/node-http-handler": "^4.5.0",
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/smithy-client": "^4.12.6",
+ "@smithy/types": "^4.13.1",
+ "@smithy/url-parser": "^4.2.12",
+ "@smithy/util-base64": "^4.3.2",
+ "@smithy/util-body-length-browser": "^4.2.2",
+ "@smithy/util-body-length-node": "^4.2.3",
+ "@smithy/util-defaults-mode-browser": "^4.3.42",
+ "@smithy/util-defaults-mode-node": "^4.2.45",
+ "@smithy/util-endpoints": "^3.3.3",
+ "@smithy/util-middleware": "^4.2.12",
+ "@smithy/util-retry": "^4.2.12",
+ "@smithy/util-utf8": "^4.2.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/core": {
+ "version": "3.973.21",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.21.tgz",
+ "integrity": "sha512-OTUcDX9Yfz/FLKbHjiMaP9D4Hs44lYJzN7zBcrK2nDmBt0Wr8D6nYt12QoBkZsW0nVMFsTIGaZCrsU9zCcIMXQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.973.6",
+ "@aws-sdk/xml-builder": "^3.972.12",
+ "@smithy/core": "^3.23.12",
+ "@smithy/node-config-provider": "^4.3.12",
+ "@smithy/property-provider": "^4.2.12",
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/signature-v4": "^5.3.12",
+ "@smithy/smithy-client": "^4.12.6",
+ "@smithy/types": "^4.13.1",
+ "@smithy/util-base64": "^4.3.2",
+ "@smithy/util-middleware": "^4.2.12",
+ "@smithy/util-utf8": "^4.2.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-env": {
+ "version": "3.972.19",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.19.tgz",
+ "integrity": "sha512-33NpkQtmnsjLr9QdZvL3w8bjy+WoBJ+jY8JwuzxIq38rDNi1kwpBWW7Yjh+8bMlksd+ZAWW0fH4S/6OeoAdU5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/types": "^3.973.6",
+ "@smithy/property-provider": "^4.2.12",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-http": {
+ "version": "3.972.21",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.21.tgz",
+ "integrity": "sha512-xFke7yjbON4unNOG0TApQwz+o1LH5VhVLgWlUuiLRWNDyBfeHIFje2ck8qHybvJ8Fkm5m3SsN+pvHtVo6PGWlQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/types": "^3.973.6",
+ "@smithy/fetch-http-handler": "^5.3.15",
+ "@smithy/node-http-handler": "^4.5.0",
+ "@smithy/property-provider": "^4.2.12",
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/smithy-client": "^4.12.6",
+ "@smithy/types": "^4.13.1",
+ "@smithy/util-stream": "^4.5.20",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-ini": {
+ "version": "3.972.21",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.21.tgz",
+ "integrity": "sha512-fmJN7KhB7CoG65w9fC2LVOd2wZbR2d1yJIpZNe2J5CeDPu7nUHSmavuJAeGEoE3OL5UIBVPNhmK/fV/NQrs3Hw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/credential-provider-env": "^3.972.19",
+ "@aws-sdk/credential-provider-http": "^3.972.21",
+ "@aws-sdk/credential-provider-login": "^3.972.21",
+ "@aws-sdk/credential-provider-process": "^3.972.19",
+ "@aws-sdk/credential-provider-sso": "^3.972.21",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.21",
+ "@aws-sdk/nested-clients": "^3.996.11",
+ "@aws-sdk/types": "^3.973.6",
+ "@smithy/credential-provider-imds": "^4.2.12",
+ "@smithy/property-provider": "^4.2.12",
+ "@smithy/shared-ini-file-loader": "^4.4.7",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-login": {
+ "version": "3.972.21",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.21.tgz",
+ "integrity": "sha512-ENU+YCiuQocQjfIf9bPxZ+ZY0wIBkl3SMH22optBQwy8UFpSfonHynXzGT27xQxer4cYTNOpwDqbfo57BusbpQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/nested-clients": "^3.996.11",
+ "@aws-sdk/types": "^3.973.6",
+ "@smithy/property-provider": "^4.2.12",
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/shared-ini-file-loader": "^4.4.7",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-node": {
+ "version": "3.972.22",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.22.tgz",
+ "integrity": "sha512-VE6i8nkmrRyhKut7nnfCWRbdDf+CfyRr8ixSwdaPDguYlgvkAO2pHu9oK11XzbSuatB0io1ozI/vpYhelXn8Pg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/credential-provider-env": "^3.972.19",
+ "@aws-sdk/credential-provider-http": "^3.972.21",
+ "@aws-sdk/credential-provider-ini": "^3.972.21",
+ "@aws-sdk/credential-provider-process": "^3.972.19",
+ "@aws-sdk/credential-provider-sso": "^3.972.21",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.21",
+ "@aws-sdk/types": "^3.973.6",
+ "@smithy/credential-provider-imds": "^4.2.12",
+ "@smithy/property-provider": "^4.2.12",
+ "@smithy/shared-ini-file-loader": "^4.4.7",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-process": {
+ "version": "3.972.19",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.19.tgz",
+ "integrity": "sha512-hjj5bFo4kf5/WzAMjDEFByVOMbq5gZiagIpJexf7Kp9nIDaGzhCphMsx03NCA8s9zUJzHlD1lXazd7MS+e03Lg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/types": "^3.973.6",
+ "@smithy/property-provider": "^4.2.12",
+ "@smithy/shared-ini-file-loader": "^4.4.7",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-sso": {
+ "version": "3.972.21",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.21.tgz",
+ "integrity": "sha512-9jWRCuMZpZKlqCZ46bvievqdfswsyB2yPAr9rOiN+FxaGgf8jrR5iYDqJgscvk1jrbAxiK4cIjHv3XjIAWAhzQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/nested-clients": "^3.996.11",
+ "@aws-sdk/token-providers": "3.1012.0",
+ "@aws-sdk/types": "^3.973.6",
+ "@smithy/property-provider": "^4.2.12",
+ "@smithy/shared-ini-file-loader": "^4.4.7",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-web-identity": {
+ "version": "3.972.21",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.21.tgz",
+ "integrity": "sha512-ShWQO/cQVZ+j3zUDK7Kj+m7grPzQCVA2iaZdJ+hJTGvVH5lR32Ip/rgZZ+zBdH6D6wczP9Upa4NMXoqJdGpK1g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/nested-clients": "^3.996.11",
+ "@aws-sdk/types": "^3.973.6",
+ "@smithy/property-provider": "^4.2.12",
+ "@smithy/shared-ini-file-loader": "^4.4.7",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-host-header": {
+ "version": "3.972.8",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.8.tgz",
+ "integrity": "sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.973.6",
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-logger": {
+ "version": "3.972.8",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.8.tgz",
+ "integrity": "sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.973.6",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-recursion-detection": {
+ "version": "3.972.8",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.8.tgz",
+ "integrity": "sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.973.6",
+ "@aws/lambda-invoke-store": "^0.2.2",
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-user-agent": {
+ "version": "3.972.22",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.22.tgz",
+ "integrity": "sha512-pZPNGWZVQvgUIO/P9PXZNz7ciq9mLYb/wQEurg3phKTa3DiBIunIRcgA0eBNwmog6S3oy0KR1bv4EJ4ld9A5sQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/types": "^3.973.6",
+ "@aws-sdk/util-endpoints": "^3.996.5",
+ "@smithy/core": "^3.23.12",
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/types": "^4.13.1",
+ "@smithy/util-retry": "^4.2.12",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/nested-clients": {
+ "version": "3.996.11",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.11.tgz",
+ "integrity": "sha512-i7SwoSR4JB/79JoGDUACnFUQOZwXGLWNX35lIb1Pq72nUGlVV+RFZp+BLa8S+mog2pbXU9+6Kc5YwGiMi5bKhQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/middleware-host-header": "^3.972.8",
+ "@aws-sdk/middleware-logger": "^3.972.8",
+ "@aws-sdk/middleware-recursion-detection": "^3.972.8",
+ "@aws-sdk/middleware-user-agent": "^3.972.22",
+ "@aws-sdk/region-config-resolver": "^3.972.8",
+ "@aws-sdk/types": "^3.973.6",
+ "@aws-sdk/util-endpoints": "^3.996.5",
+ "@aws-sdk/util-user-agent-browser": "^3.972.8",
+ "@aws-sdk/util-user-agent-node": "^3.973.8",
+ "@smithy/config-resolver": "^4.4.11",
+ "@smithy/core": "^3.23.12",
+ "@smithy/fetch-http-handler": "^5.3.15",
+ "@smithy/hash-node": "^4.2.12",
+ "@smithy/invalid-dependency": "^4.2.12",
+ "@smithy/middleware-content-length": "^4.2.12",
+ "@smithy/middleware-endpoint": "^4.4.26",
+ "@smithy/middleware-retry": "^4.4.43",
+ "@smithy/middleware-serde": "^4.2.15",
+ "@smithy/middleware-stack": "^4.2.12",
+ "@smithy/node-config-provider": "^4.3.12",
+ "@smithy/node-http-handler": "^4.5.0",
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/smithy-client": "^4.12.6",
+ "@smithy/types": "^4.13.1",
+ "@smithy/url-parser": "^4.2.12",
+ "@smithy/util-base64": "^4.3.2",
+ "@smithy/util-body-length-browser": "^4.2.2",
+ "@smithy/util-body-length-node": "^4.2.3",
+ "@smithy/util-defaults-mode-browser": "^4.3.42",
+ "@smithy/util-defaults-mode-node": "^4.2.45",
+ "@smithy/util-endpoints": "^3.3.3",
+ "@smithy/util-middleware": "^4.2.12",
+ "@smithy/util-retry": "^4.2.12",
+ "@smithy/util-utf8": "^4.2.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/region-config-resolver": {
+ "version": "3.972.8",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.8.tgz",
+ "integrity": "sha512-1eD4uhTDeambO/PNIDVG19A6+v4NdD7xzwLHDutHsUqz0B+i661MwQB2eYO4/crcCvCiQG4SRm1k81k54FEIvw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.973.6",
+ "@smithy/config-resolver": "^4.4.11",
+ "@smithy/node-config-provider": "^4.3.12",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/token-providers": {
+ "version": "3.1012.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1012.0.tgz",
+ "integrity": "sha512-vzKwy020zjuiF4WTJzejx5nYcXJnRhHpb6i3lyZHIwfFwXG1yX4bzBVNMWYWF+bz1i2Pp2VhJbPyzpqj4VuJXQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/nested-clients": "^3.996.11",
+ "@aws-sdk/types": "^3.973.6",
+ "@smithy/property-provider": "^4.2.12",
+ "@smithy/shared-ini-file-loader": "^4.4.7",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/types": {
+ "version": "3.973.6",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.6.tgz",
+ "integrity": "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/util-endpoints": {
+ "version": "3.996.5",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.5.tgz",
+ "integrity": "sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.973.6",
+ "@smithy/types": "^4.13.1",
+ "@smithy/url-parser": "^4.2.12",
+ "@smithy/util-endpoints": "^3.3.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/util-locate-window": {
+ "version": "3.965.5",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz",
+ "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/util-user-agent-browser": {
+ "version": "3.972.8",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.8.tgz",
+ "integrity": "sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.973.6",
+ "@smithy/types": "^4.13.1",
+ "bowser": "^2.11.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-sdk/util-user-agent-node": {
+ "version": "3.973.8",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.8.tgz",
+ "integrity": "sha512-Kvb96TafGPLYo4Z2GRCzQTne77epXgiZEo0DDXwavzkWmgDV/1XD1tMA766gzRcHHFUraWsE+4T8DKtPTZUxgQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/middleware-user-agent": "^3.972.22",
+ "@aws-sdk/types": "^3.973.6",
+ "@smithy/node-config-provider": "^4.3.12",
+ "@smithy/types": "^4.13.1",
+ "@smithy/util-config-provider": "^4.2.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "aws-crt": ">=1.0.0"
+ },
+ "peerDependenciesMeta": {
+ "aws-crt": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@aws-sdk/xml-builder": {
+ "version": "3.972.13",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.13.tgz",
+ "integrity": "sha512-I/+BMxM4WE/6xL0tyV7tAUDOAXmyw/va1oGr/eSly43HmLUcD1G+v96vEKAA8VoLcZ03ZQo/PWzjmN9zQErqPQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.13.1",
+ "fast-xml-parser": "5.5.6",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws/lambda-invoke-store": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz",
+ "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
"node_modules/@selderee/plugin-htmlparser2": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz",
@@ -27,6 +650,657 @@
"url": "https://ko-fi.com/killymxi"
}
},
+ "node_modules/@smithy/abort-controller": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.12.tgz",
+ "integrity": "sha512-xolrFw6b+2iYGl6EcOL7IJY71vvyZ0DJ3mcKtpykqPe2uscwtzDZJa1uVQXyP7w9Dd+kGwYnPbMsJrGISKiY/Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/config-resolver": {
+ "version": "4.4.13",
+ "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.13.tgz",
+ "integrity": "sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/node-config-provider": "^4.3.12",
+ "@smithy/types": "^4.13.1",
+ "@smithy/util-config-provider": "^4.2.2",
+ "@smithy/util-endpoints": "^3.3.3",
+ "@smithy/util-middleware": "^4.2.12",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/core": {
+ "version": "3.23.12",
+ "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.12.tgz",
+ "integrity": "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/types": "^4.13.1",
+ "@smithy/url-parser": "^4.2.12",
+ "@smithy/util-base64": "^4.3.2",
+ "@smithy/util-body-length-browser": "^4.2.2",
+ "@smithy/util-middleware": "^4.2.12",
+ "@smithy/util-stream": "^4.5.20",
+ "@smithy/util-utf8": "^4.2.2",
+ "@smithy/uuid": "^1.1.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/credential-provider-imds": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.12.tgz",
+ "integrity": "sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/node-config-provider": "^4.3.12",
+ "@smithy/property-provider": "^4.2.12",
+ "@smithy/types": "^4.13.1",
+ "@smithy/url-parser": "^4.2.12",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/eventstream-codec": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.12.tgz",
+ "integrity": "sha512-FE3bZdEl62ojmy8x4FHqxq2+BuOHlcxiH5vaZ6aqHJr3AIZzwF5jfx8dEiU/X0a8RboyNDjmXjlbr8AdEyLgiA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/crc32": "5.2.0",
+ "@smithy/types": "^4.13.1",
+ "@smithy/util-hex-encoding": "^4.2.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/eventstream-serde-browser": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.12.tgz",
+ "integrity": "sha512-XUSuMxlTxV5pp4VpqZf6Sa3vT/Q75FVkLSpSSE3KkWBvAQWeuWt1msTv8fJfgA4/jcJhrbrbMzN1AC/hvPmm5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/eventstream-serde-universal": "^4.2.12",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/eventstream-serde-config-resolver": {
+ "version": "4.3.12",
+ "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.12.tgz",
+ "integrity": "sha512-7epsAZ3QvfHkngz6RXQYseyZYHlmWXSTPOfPmXkiS+zA6TBNo1awUaMFL9vxyXlGdoELmCZyZe1nQE+imbmV+Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/eventstream-serde-node": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.12.tgz",
+ "integrity": "sha512-D1pFuExo31854eAvg89KMn9Oab/wEeJR6Buy32B49A9Ogdtx5fwZPqBHUlDzaCDpycTFk2+fSQgX689Qsk7UGA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/eventstream-serde-universal": "^4.2.12",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/eventstream-serde-universal": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.12.tgz",
+ "integrity": "sha512-+yNuTiyBACxOJUTvbsNsSOfH9G9oKbaJE1lNL3YHpGcuucl6rPZMi3nrpehpVOVR2E07YqFFmtwpImtpzlouHQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/eventstream-codec": "^4.2.12",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/fetch-http-handler": {
+ "version": "5.3.15",
+ "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.15.tgz",
+ "integrity": "sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/querystring-builder": "^4.2.12",
+ "@smithy/types": "^4.13.1",
+ "@smithy/util-base64": "^4.3.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/hash-node": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.12.tgz",
+ "integrity": "sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.13.1",
+ "@smithy/util-buffer-from": "^4.2.2",
+ "@smithy/util-utf8": "^4.2.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/invalid-dependency": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.12.tgz",
+ "integrity": "sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/is-array-buffer": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.2.tgz",
+ "integrity": "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-content-length": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.12.tgz",
+ "integrity": "sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-endpoint": {
+ "version": "4.4.27",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.27.tgz",
+ "integrity": "sha512-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.23.12",
+ "@smithy/middleware-serde": "^4.2.15",
+ "@smithy/node-config-provider": "^4.3.12",
+ "@smithy/shared-ini-file-loader": "^4.4.7",
+ "@smithy/types": "^4.13.1",
+ "@smithy/url-parser": "^4.2.12",
+ "@smithy/util-middleware": "^4.2.12",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-retry": {
+ "version": "4.4.44",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.44.tgz",
+ "integrity": "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/node-config-provider": "^4.3.12",
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/service-error-classification": "^4.2.12",
+ "@smithy/smithy-client": "^4.12.7",
+ "@smithy/types": "^4.13.1",
+ "@smithy/util-middleware": "^4.2.12",
+ "@smithy/util-retry": "^4.2.12",
+ "@smithy/uuid": "^1.1.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-serde": {
+ "version": "4.2.15",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.15.tgz",
+ "integrity": "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.23.12",
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-stack": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.12.tgz",
+ "integrity": "sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/node-config-provider": {
+ "version": "4.3.12",
+ "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.12.tgz",
+ "integrity": "sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/property-provider": "^4.2.12",
+ "@smithy/shared-ini-file-loader": "^4.4.7",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/node-http-handler": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.5.0.tgz",
+ "integrity": "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/abort-controller": "^4.2.12",
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/querystring-builder": "^4.2.12",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/property-provider": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.12.tgz",
+ "integrity": "sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/protocol-http": {
+ "version": "5.3.12",
+ "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.12.tgz",
+ "integrity": "sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/querystring-builder": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.12.tgz",
+ "integrity": "sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.13.1",
+ "@smithy/util-uri-escape": "^4.2.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/querystring-parser": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.12.tgz",
+ "integrity": "sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/service-error-classification": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.12.tgz",
+ "integrity": "sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.13.1"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/shared-ini-file-loader": {
+ "version": "4.4.7",
+ "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.7.tgz",
+ "integrity": "sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/signature-v4": {
+ "version": "5.3.12",
+ "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.12.tgz",
+ "integrity": "sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/is-array-buffer": "^4.2.2",
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/types": "^4.13.1",
+ "@smithy/util-hex-encoding": "^4.2.2",
+ "@smithy/util-middleware": "^4.2.12",
+ "@smithy/util-uri-escape": "^4.2.2",
+ "@smithy/util-utf8": "^4.2.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/smithy-client": {
+ "version": "4.12.7",
+ "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.7.tgz",
+ "integrity": "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.23.12",
+ "@smithy/middleware-endpoint": "^4.4.27",
+ "@smithy/middleware-stack": "^4.2.12",
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/types": "^4.13.1",
+ "@smithy/util-stream": "^4.5.20",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/types": {
+ "version": "4.13.1",
+ "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.13.1.tgz",
+ "integrity": "sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/url-parser": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.12.tgz",
+ "integrity": "sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/querystring-parser": "^4.2.12",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-base64": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.2.tgz",
+ "integrity": "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-buffer-from": "^4.2.2",
+ "@smithy/util-utf8": "^4.2.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-body-length-browser": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.2.tgz",
+ "integrity": "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-body-length-node": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.3.tgz",
+ "integrity": "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-buffer-from": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.2.tgz",
+ "integrity": "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/is-array-buffer": "^4.2.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-config-provider": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.2.tgz",
+ "integrity": "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-defaults-mode-browser": {
+ "version": "4.3.43",
+ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.43.tgz",
+ "integrity": "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/property-provider": "^4.2.12",
+ "@smithy/smithy-client": "^4.12.7",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-defaults-mode-node": {
+ "version": "4.2.47",
+ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.47.tgz",
+ "integrity": "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/config-resolver": "^4.4.13",
+ "@smithy/credential-provider-imds": "^4.2.12",
+ "@smithy/node-config-provider": "^4.3.12",
+ "@smithy/property-provider": "^4.2.12",
+ "@smithy/smithy-client": "^4.12.7",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-endpoints": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.3.3.tgz",
+ "integrity": "sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/node-config-provider": "^4.3.12",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-hex-encoding": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.2.tgz",
+ "integrity": "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-middleware": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.12.tgz",
+ "integrity": "sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-retry": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.12.tgz",
+ "integrity": "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/service-error-classification": "^4.2.12",
+ "@smithy/types": "^4.13.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-stream": {
+ "version": "4.5.20",
+ "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.20.tgz",
+ "integrity": "sha512-4yXLm5n/B5SRBR2p8cZ90Sbv4zL4NKsgxdzCzp/83cXw2KxLEumt5p+GAVyRNZgQOSrzXn9ARpO0lUe8XSlSDw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/fetch-http-handler": "^5.3.15",
+ "@smithy/node-http-handler": "^4.5.0",
+ "@smithy/types": "^4.13.1",
+ "@smithy/util-base64": "^4.3.2",
+ "@smithy/util-buffer-from": "^4.2.2",
+ "@smithy/util-hex-encoding": "^4.2.2",
+ "@smithy/util-utf8": "^4.2.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-uri-escape": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.2.tgz",
+ "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-utf8": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz",
+ "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-buffer-from": "^4.2.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/uuid": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.2.tgz",
+ "integrity": "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
"node_modules/accepts": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
@@ -60,6 +1334,12 @@
"node": ">=18"
}
},
+ "node_modules/bowser": {
+ "version": "2.14.1",
+ "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz",
+ "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==",
+ "license": "MIT"
+ },
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -373,6 +1653,41 @@
"url": "https://opencollective.com/express"
}
},
+ "node_modules/fast-xml-builder": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz",
+ "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "path-expression-matcher": "^1.1.3"
+ }
+ },
+ "node_modules/fast-xml-parser": {
+ "version": "5.5.6",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.6.tgz",
+ "integrity": "sha512-3+fdZyBRVg29n4rXP0joHthhcHdPUHaIC16cuyyd1iLsuaO6Vea36MPrxgAzbZna8lhvZeRL8Bc9GP56/J9xEw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "fast-xml-builder": "^1.1.4",
+ "path-expression-matcher": "^1.1.3",
+ "strnum": "^2.1.2"
+ },
+ "bin": {
+ "fxparser": "src/cli/cli.js"
+ }
+ },
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
@@ -856,6 +2171,21 @@
"node": ">= 0.8"
}
},
+ "node_modules/path-expression-matcher": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz",
+ "integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/path-to-regexp": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
@@ -1113,6 +2443,18 @@
"node": ">= 0.8"
}
},
+ "node_modules/strnum": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.1.tgz",
+ "integrity": "sha512-BwRvNd5/QoAtyW1na1y1LsJGQNvRlkde6Q/ipqqEaivoMdV+B1OMOTVdwR+N/cwVUcIt9PYyHmV8HyexCZSupg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/tlds": {
"version": "1.259.0",
"resolved": "https://registry.npmjs.org/tlds/-/tlds-1.259.0.tgz",
@@ -1131,6 +2473,12 @@
"node": ">=0.6"
}
},
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
"node_modules/type-is": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
diff --git a/_reference/localEmailViewer/package.json b/_reference/localEmailViewer/package.json
index a6b522e0e..377708878 100644
--- a/_reference/localEmailViewer/package.json
+++ b/_reference/localEmailViewer/package.json
@@ -10,8 +10,9 @@
"keywords": [],
"author": "",
"license": "ISC",
- "description": "LocalStack SES email viewer for inspecting local outbound mail",
+ "description": "LocalStack inspector for SES emails and CloudWatch logs",
"dependencies": {
+ "@aws-sdk/client-cloudwatch-logs": "^3.1012.0",
"express": "^5.1.0",
"mailparser": "^3.7.4",
"node-fetch": "^3.3.2"