first commit

This commit is contained in:
Ted Werbel 2025-02-10 04:21:21 -05:00
commit d5ed2ef2d7
11 changed files with 2067 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules
dist
.port

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>DevTools Logs</title>
</head>
<body>
<!-- DevTools extension script -->
<script src="devtools.js"></script>
</body>
</html>

View File

@ -0,0 +1,384 @@
// devtools.js
// Store settings with defaults
let settings = {
logLimit: 50,
queryLimit: 30000,
stringSizeLimit: 500,
maxLogSize: 20000,
showRequestHeaders: false,
showResponseHeaders: false,
};
// Keep track of debugger state
let isDebuggerAttached = false;
const currentTabId = chrome.devtools.inspectedWindow.tabId;
// Load saved settings on startup
chrome.storage.local.get(["browserConnectorSettings"], (result) => {
if (result.browserConnectorSettings) {
settings = { ...settings, ...result.browserConnectorSettings };
}
});
// Listen for settings updates
chrome.runtime.onMessage.addListener((message) => {
if (message.type === "SETTINGS_UPDATED") {
settings = message.settings;
}
});
// Utility to recursively truncate strings in any data structure
function truncateStringsInData(data, maxLength, depth = 0, path = "") {
// Add depth limit to prevent circular references
if (depth > 100) {
console.warn("Max depth exceeded at path:", path);
return "[MAX_DEPTH_EXCEEDED]";
}
console.log(`Processing at path: ${path}, type:`, typeof data);
if (typeof data === "string") {
if (data.length > maxLength) {
console.log(
`Truncating string at path ${path} from ${data.length} to ${maxLength}`
);
return data.substring(0, maxLength) + "... (truncated)";
}
return data;
}
if (Array.isArray(data)) {
console.log(`Processing array at path ${path} with length:`, data.length);
return data.map((item, index) =>
truncateStringsInData(item, maxLength, depth + 1, `${path}[${index}]`)
);
}
if (typeof data === "object" && data !== null) {
console.log(
`Processing object at path ${path} with keys:`,
Object.keys(data)
);
const result = {};
for (const [key, value] of Object.entries(data)) {
try {
result[key] = truncateStringsInData(
value,
maxLength,
depth + 1,
path ? `${path}.${key}` : key
);
} catch (e) {
console.error(`Error processing key ${key} at path ${path}:`, e);
result[key] = "[ERROR_PROCESSING]";
}
}
return result;
}
return data;
}
// Helper to calculate the size of an object
function calculateObjectSize(obj) {
return JSON.stringify(obj).length;
}
// Helper to process array of objects with size limit
function processArrayWithSizeLimit(array, maxTotalSize, processFunc) {
let currentSize = 0;
const result = [];
for (const item of array) {
// Process the item first
const processedItem = processFunc(item);
const itemSize = calculateObjectSize(processedItem);
// Check if adding this item would exceed the limit
if (currentSize + itemSize > maxTotalSize) {
console.log(
`Reached size limit (${currentSize}/${maxTotalSize}), truncating array`
);
break;
}
// Add item and update size
result.push(processedItem);
currentSize += itemSize;
console.log(
`Added item of size ${itemSize}, total size now: ${currentSize}`
);
}
return result;
}
// Modified processJsonString to handle arrays with size limit
function processJsonString(jsonString, maxLength) {
console.log("Processing string of length:", jsonString?.length);
try {
let parsed;
try {
parsed = JSON.parse(jsonString);
console.log(
"Successfully parsed as JSON, structure:",
JSON.stringify(Object.keys(parsed))
);
} catch (e) {
console.log("Not valid JSON, treating as string");
return truncateStringsInData(jsonString, maxLength, 0, "root");
}
// If it's an array, process with size limit
if (Array.isArray(parsed)) {
console.log("Processing array of objects with size limit");
const processed = processArrayWithSizeLimit(
parsed,
settings.maxLogSize,
(item) => truncateStringsInData(item, maxLength, 0, "root")
);
const result = JSON.stringify(processed);
console.log(
`Processed array: ${parsed.length} -> ${processed.length} items`
);
return result;
}
// Otherwise process as before
const processed = truncateStringsInData(parsed, maxLength, 0, "root");
const result = JSON.stringify(processed);
console.log("Processed JSON string length:", result.length);
return result;
} catch (e) {
console.error("Error in processJsonString:", e);
return jsonString.substring(0, maxLength) + "... (truncated)";
}
}
// Utility to send logs to browser-connector
function sendToBrowserConnector(logData) {
console.log("Original log data size:", JSON.stringify(logData).length);
// Process any string fields that might contain JSON
const processedData = { ...logData };
if (logData.type === "network-request") {
console.log("Processing network request");
if (processedData.requestBody) {
console.log(
"Request body size before:",
processedData.requestBody.length
);
processedData.requestBody = processJsonString(
processedData.requestBody,
settings.stringSizeLimit
);
console.log("Request body size after:", processedData.requestBody.length);
}
if (processedData.responseBody) {
console.log(
"Response body size before:",
processedData.responseBody.length
);
processedData.responseBody = processJsonString(
processedData.responseBody,
settings.stringSizeLimit
);
console.log(
"Response body size after:",
processedData.responseBody.length
);
}
} else if (
logData.type === "console-log" ||
logData.type === "console-error"
) {
console.log("Processing console message");
if (processedData.message) {
console.log("Message size before:", processedData.message.length);
processedData.message = processJsonString(
processedData.message,
settings.stringSizeLimit
);
console.log("Message size after:", processedData.message.length);
}
}
// Add settings to the request
const payload = {
data: {
...processedData,
timestamp: Date.now(),
},
settings: {
logLimit: settings.logLimit,
queryLimit: settings.queryLimit,
showRequestHeaders: settings.showRequestHeaders,
showResponseHeaders: settings.showResponseHeaders,
},
};
const finalPayloadSize = JSON.stringify(payload).length;
console.log("Final payload size:", finalPayloadSize);
if (finalPayloadSize > 1000000) {
// 1MB warning threshold
console.warn("Warning: Large payload detected:", finalPayloadSize);
console.warn(
"Payload preview:",
JSON.stringify(payload).substring(0, 1000) + "..."
);
}
fetch("http://127.0.0.1:3025/extension-log", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
}).catch((error) => {
console.error("Failed to send log to browser-connector:", error);
});
}
// 1) Listen for network requests
chrome.devtools.network.onRequestFinished.addListener((request) => {
if (request._resourceType === "xhr" || request._resourceType === "fetch") {
request.getContent((responseBody) => {
const entry = {
type: "network-request",
url: request.request.url,
method: request.request.method,
status: request.response.status,
requestHeaders: request.request.headers,
responseHeaders: request.response.headers,
requestBody: request.request.postData?.text ?? "",
responseBody: responseBody ?? "",
};
sendToBrowserConnector(entry);
});
}
});
// Move the console message listener outside the panel creation
const consoleMessageListener = (source, method, params) => {
console.log("Debugger event:", method, source.tabId, currentTabId);
// Only process events for our tab
if (source.tabId !== currentTabId) {
console.log("Ignoring event from different tab");
return;
}
if (method === "Console.messageAdded") {
console.log("Console message received:", params.message);
const entry = {
type: params.message.level === "error" ? "console-error" : "console-log",
level: params.message.level,
message: params.message.text,
timestamp: Date.now(),
};
console.log("Sending console entry:", entry);
sendToBrowserConnector(entry);
} else {
console.log("Unhandled debugger method:", method);
}
};
// Helper function to attach debugger
function attachDebugger() {
if (isDebuggerAttached) {
console.log("Debugger already attached");
return;
}
// Check if the tab still exists
chrome.tabs.get(currentTabId, (tab) => {
if (chrome.runtime.lastError) {
console.log("Tab no longer exists:", chrome.runtime.lastError);
isDebuggerAttached = false;
return;
}
console.log("Attaching debugger to tab:", currentTabId);
chrome.debugger.attach({ tabId: currentTabId }, "1.3", () => {
if (chrome.runtime.lastError) {
console.error("Failed to attach debugger:", chrome.runtime.lastError);
isDebuggerAttached = false;
return;
}
isDebuggerAttached = true;
console.log("Debugger successfully attached");
// Add the event listener when attaching
chrome.debugger.onEvent.addListener(consoleMessageListener);
chrome.debugger.sendCommand(
{ tabId: currentTabId },
"Console.enable",
{},
() => {
if (chrome.runtime.lastError) {
console.error(
"Failed to enable console:",
chrome.runtime.lastError
);
return;
}
console.log("Console API successfully enabled");
}
);
});
});
}
// Helper function to detach debugger
function detachDebugger() {
if (!isDebuggerAttached) return;
// Remove the event listener when detaching
chrome.debugger.onEvent.removeListener(consoleMessageListener);
// Check if debugger is actually attached before trying to detach
chrome.debugger.getTargets((targets) => {
const isStillAttached = targets.some(
(target) => target.tabId === currentTabId && target.attached
);
if (!isStillAttached) {
console.log("Debugger already detached");
isDebuggerAttached = false;
return;
}
chrome.debugger.detach({ tabId: currentTabId }, () => {
if (chrome.runtime.lastError) {
console.error("Failed to detach debugger:", chrome.runtime.lastError);
isDebuggerAttached = false;
return;
}
isDebuggerAttached = false;
console.log("Debugger detached");
});
});
}
// 2) Use DevTools Protocol to capture console logs
chrome.devtools.panels.create("Browser Logs", "", "panel.html", (panel) => {
// Initial attach - we'll keep the debugger attached as long as DevTools is open
attachDebugger();
// Remove the panel show/hide listeners since we want to stay attached
// panel.onShown.addListener(() => {
// attachDebugger();
// });
//
// panel.onHidden.addListener(() => {
// detachDebugger();
// });
});
// Clean up only when the entire DevTools window is closed
window.addEventListener("unload", () => {
detachDebugger();
});

View File

@ -0,0 +1,18 @@
{
"name": "AI Browser Connector",
"version": "1.0.0",
"description": "Captures console and network logs then sends them to a local aggregator for MCP usage",
"manifest_version": 3,
"devtools_page": "devtools.html",
"permissions": [
"activeTab",
"scripting",
"debugger",
"storage",
"tabs"
],
"host_permissions": [
"<all_urls>"
]
}

108
chrome-extension/panel.html Normal file
View File

@ -0,0 +1,108 @@
<!DOCTYPE html>
<html>
<head>
<style>
body {
padding: 16px;
font-family: system-ui, -apple-system, sans-serif;
background-color: #282828;
color: #fff;
}
.endpoint-list {
margin: 16px 0;
}
.endpoint-item {
display: flex;
gap: 8px;
margin-bottom: 8px;
align-items: center;
}
.endpoint-form {
display: flex;
gap: 8px;
margin-bottom: 16px;
align-items: center;
}
button {
padding: 4px 8px;
}
input {
padding: 4px;
}
.status-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
}
.status-connected {
background: #4caf50;
}
.status-disconnected {
background: #f44336;
}
.form-group {
margin-bottom: 16px;
}
.form-group label {
display: block;
margin-bottom: 4px;
}
.checkbox-group {
margin-bottom: 8px;
}
input[type="number"],
input[type="text"] {
padding: 4px;
width: 200px;
}
.settings-section {
border: 1px solid #ccc;
padding: 16px;
margin-bottom: 16px;
border-radius: 4px;
}
</style>
</head>
<body>
<div class="settings-section">
<h3>Log Settings</h3>
<div class="form-group">
<label for="log-limit">Log Limit (number of logs)</label>
<input type="number" id="log-limit" min="1" value="50">
</div>
<div class="form-group">
<label for="query-limit">Query Limit (characters)</label>
<input type="number" id="query-limit" min="1" value="30000">
</div>
<div class="form-group">
<label for="string-size-limit">String Size Limit (characters)</label>
<input type="number" id="string-size-limit" min="1" value="500">
</div>
<div class="form-group">
<label for="max-log-size">Max Log Size (characters)</label>
<input type="number" id="max-log-size" min="1000" value="20000">
</div>
<div class="checkbox-group">
<label>
<input type="checkbox" id="show-request-headers">
Include Request Headers
</label>
</div>
<div class="checkbox-group">
<label>
<input type="checkbox" id="show-response-headers">
Include Response Headers
</label>
</div>
</div>
<script src="panel.js"></script>
</body>
</html>

80
chrome-extension/panel.js Normal file
View File

@ -0,0 +1,80 @@
// Store settings
let settings = {
logLimit: 50,
queryLimit: 30000,
stringSizeLimit: 500,
showRequestHeaders: false,
showResponseHeaders: false,
maxLogSize: 20000,
};
// Load saved settings on startup
chrome.storage.local.get(["browserConnectorSettings"], (result) => {
if (result.browserConnectorSettings) {
settings = { ...settings, ...result.browserConnectorSettings };
updateUIFromSettings();
}
});
// Initialize UI elements
const logLimitInput = document.getElementById("log-limit");
const queryLimitInput = document.getElementById("query-limit");
const stringSizeLimitInput = document.getElementById("string-size-limit");
const showRequestHeadersCheckbox = document.getElementById(
"show-request-headers"
);
const showResponseHeadersCheckbox = document.getElementById(
"show-response-headers"
);
const maxLogSizeInput = document.getElementById("max-log-size");
// Update UI from settings
function updateUIFromSettings() {
logLimitInput.value = settings.logLimit;
queryLimitInput.value = settings.queryLimit;
stringSizeLimitInput.value = settings.stringSizeLimit;
showRequestHeadersCheckbox.checked = settings.showRequestHeaders;
showResponseHeadersCheckbox.checked = settings.showResponseHeaders;
maxLogSizeInput.value = settings.maxLogSize;
}
// Save settings
function saveSettings() {
chrome.storage.local.set({ browserConnectorSettings: settings });
// Notify devtools.js about settings change
chrome.runtime.sendMessage({
type: "SETTINGS_UPDATED",
settings,
});
}
// Add event listeners for all inputs
logLimitInput.addEventListener("change", (e) => {
settings.logLimit = parseInt(e.target.value, 10);
saveSettings();
});
queryLimitInput.addEventListener("change", (e) => {
settings.queryLimit = parseInt(e.target.value, 10);
saveSettings();
});
stringSizeLimitInput.addEventListener("change", (e) => {
settings.stringSizeLimit = parseInt(e.target.value, 10);
saveSettings();
});
showRequestHeadersCheckbox.addEventListener("change", (e) => {
settings.showRequestHeaders = e.target.checked;
saveSettings();
});
showResponseHeadersCheckbox.addEventListener("change", (e) => {
settings.showResponseHeaders = e.target.checked;
saveSettings();
});
maxLogSizeInput.addEventListener("change", (e) => {
settings.maxLogSize = parseInt(e.target.value, 10);
saveSettings();
});

1008
mcp-server/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

29
mcp-server/package.json Normal file
View File

@ -0,0 +1,29 @@
{
"name": "mcp-server",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "tsc && node dist/browser-connector.js",
"mcp": "tsc && node dist/mcp-server.js",
"inspect": "tsc && npx @modelcontextprotocol/inspector node -- dist/mcp-server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.4.1",
"body-parser": "^1.20.3",
"cors": "^2.8.5",
"express": "^4.21.2",
"llm-cost": "^1.0.5"
},
"devDependencies": {
"@types/body-parser": "^1.19.5",
"@types/cors": "^2.8.17",
"@types/express": "^5.0.0",
"@types/node": "^22.13.1",
"typescript": "^5.7.3"
}
}

View File

@ -0,0 +1,210 @@
import express from "express";
import cors from "cors";
import bodyParser from "body-parser";
import { tokenizeAndEstimateCost } from "llm-cost";
// We store logs in memory
const consoleLogs: any[] = [];
const consoleErrors: any[] = [];
const networkErrors: any[] = [];
const networkSuccess: any[] = [];
const allXhr: any[] = [];
// Add settings state
let currentSettings = {
logLimit: 50,
queryLimit: 30000,
showRequestHeaders: false,
showResponseHeaders: false,
model: "claude-3-sonnet",
stringSizeLimit: 500,
maxLogSize: 20000,
};
const app = express();
const PORT = 3025;
app.use(cors());
app.use(bodyParser.json());
// Helper to recursively truncate strings in any data structure
function truncateStringsInData(data: any, maxLength: number): any {
if (typeof data === "string") {
return data.length > maxLength
? data.substring(0, maxLength) + "... (truncated)"
: data;
}
if (Array.isArray(data)) {
return data.map((item) => truncateStringsInData(item, maxLength));
}
if (typeof data === "object" && data !== null) {
const result: any = {};
for (const [key, value] of Object.entries(data)) {
result[key] = truncateStringsInData(value, maxLength);
}
return result;
}
return data;
}
// Helper to safely parse and process JSON strings
function processJsonString(jsonString: string, maxLength: number): string {
try {
// Try to parse the string as JSON
const parsed = JSON.parse(jsonString);
// Process any strings within the parsed JSON
const processed = truncateStringsInData(parsed, maxLength);
// Stringify the processed data
return JSON.stringify(processed);
} catch (e) {
// If it's not valid JSON, treat it as a regular string
return truncateStringsInData(jsonString, maxLength);
}
}
// Helper to process logs based on settings
function processLogsWithSettings(logs: any[]) {
return logs.map((log) => {
const processedLog = { ...log };
if (log.type === "network-request") {
// Handle headers visibility
if (!currentSettings.showRequestHeaders) {
delete processedLog.requestHeaders;
}
if (!currentSettings.showResponseHeaders) {
delete processedLog.responseHeaders;
}
}
return processedLog;
});
}
// Helper to calculate size of a log entry
function calculateLogSize(log: any): number {
return JSON.stringify(log).length;
}
// Helper to truncate logs based on character limit
function truncateLogsToQueryLimit(logs: any[]): any[] {
if (logs.length === 0) return logs;
// First process logs according to current settings
const processedLogs = processLogsWithSettings(logs);
let currentSize = 0;
const result = [];
for (const log of processedLogs) {
const logSize = calculateLogSize(log);
// Check if adding this log would exceed the limit
if (currentSize + logSize > currentSettings.queryLimit) {
console.log(
`Reached query limit (${currentSize}/${currentSettings.queryLimit}), truncating logs`
);
break;
}
// Add log and update size
result.push(log);
currentSize += logSize;
console.log(`Added log of size ${logSize}, total size now: ${currentSize}`);
}
return result;
}
// Endpoint for the extension to POST data
app.post("/extension-log", (req, res) => {
const { data, settings } = req.body;
// Update settings if provided
if (settings) {
currentSettings = {
...currentSettings,
...settings,
};
}
console.log("Received log:", data);
switch (data.type) {
case "console-log":
consoleLogs.push(data);
if (consoleLogs.length > currentSettings.logLimit) consoleLogs.shift();
break;
case "console-error":
consoleErrors.push(data);
if (consoleErrors.length > currentSettings.logLimit)
consoleErrors.shift();
break;
case "network-request":
// Route network requests based on status code
if (data.status >= 400) {
networkErrors.push(data);
if (networkErrors.length > currentSettings.logLimit)
networkErrors.shift();
} else {
networkSuccess.push(data);
if (networkSuccess.length > currentSettings.logLimit)
networkSuccess.shift();
}
break;
}
res.json({ status: "ok" });
});
// Update GET endpoints to use the new function
app.get("/console-logs", (req, res) => {
const truncatedLogs = truncateLogsToQueryLimit(consoleLogs);
res.json(truncatedLogs);
});
app.get("/console-errors", (req, res) => {
const truncatedLogs = truncateLogsToQueryLimit(consoleErrors);
res.json(truncatedLogs);
});
app.get("/network-errors", (req, res) => {
const truncatedLogs = truncateLogsToQueryLimit(networkErrors);
res.json(truncatedLogs);
});
app.get("/network-success", (req, res) => {
const truncatedLogs = truncateLogsToQueryLimit(networkSuccess);
res.json(truncatedLogs);
});
app.get("/all-xhr", (req, res) => {
// Merge and sort network success and error logs by timestamp
const mergedLogs = [...networkSuccess, ...networkErrors].sort(
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
);
const truncatedLogs = truncateLogsToQueryLimit(mergedLogs);
res.json(truncatedLogs);
});
app.get("/.port", (req, res) => {
res.send(PORT.toString());
});
// Start the server
const server = app.listen(PORT, () => {
console.log(`Aggregator listening on http://127.0.0.1:${PORT}`);
// Write the port to a file so mcp-server can read it
const fs = require("fs");
fs.writeFileSync(".port", PORT.toString());
});
// Handle shutdown gracefully
process.on("SIGINT", () => {
server.close(() => {
console.log("Server shut down");
process.exit(0);
});
});

View File

@ -0,0 +1,105 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import fs from "fs";
// Create the MCP server
const server = new McpServer({
name: "AI Browser Connector",
version: "1.0.0",
});
// Function to get the port from the .port file
function getPort(): number {
try {
const port = parseInt(fs.readFileSync(".port", "utf8"));
return port;
} catch (err) {
console.error("Could not read port file, defaulting to 3000");
return 3025;
}
}
// const PORT = getPort();
const PORT = 3025;
// We'll define four "tools" that retrieve data from the aggregator at localhost:3000
server.tool("getConsoleLogs", "Check our browser logs", async () => {
const response = await fetch(`http://127.0.0.1:${PORT}/console-logs`);
const json = await response.json();
return {
content: [
{
type: "text",
text: JSON.stringify(json, null, 2),
},
],
};
});
// server.tool(
// "getConsoleErrors",
// "Check our browsers console errors",
// async () => {
// const response = await fetch(`http://127.0.0.1:${PORT}/console-errors`);
// const json = await response.json();
// const last50 = json.slice(-50);
// return {
// content: [
// {
// type: "text",
// text: JSON.stringify(last50, null, 2),
// },
// ],
// };
// }
// );
// Return all HTTP errors (4xx/5xx)
server.tool("getNetworkErrors", "Check our network ERROR logs", async () => {
const response = await fetch(`http://127.0.0.1:${PORT}/network-errors`);
const json = await response.json();
return {
content: [
{
type: "text",
text: JSON.stringify(json, null, 2),
},
],
};
});
// Return all XHR/fetch requests
server.tool("getNetworkSuccess", "Check our network SUCCESS logs", async () => {
const response = await fetch(`http://127.0.0.1:${PORT}/all-xhr`);
const json = await response.json();
return {
content: [
{
type: "text",
text: JSON.stringify(json, null, 2),
},
],
};
});
// Return all XHR/fetch requests
server.tool("getNetworkLogs", "Check ALL our network logs", async () => {
const response = await fetch(`http://127.0.0.1:${PORT}/all-xhr`);
const json = await response.json();
return {
content: [
{
type: "text",
text: JSON.stringify(json, null, 2),
},
],
};
});
// Start receiving messages on stdio
(async () => {
const transport = new StdioServerTransport();
await server.connect(transport);
})();

111
mcp-server/tsconfig.json Normal file
View File

@ -0,0 +1,111 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./dist", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}