From e9d5528e42bbdb0e037f78977d879d5426a3059d Mon Sep 17 00:00:00 2001 From: Niraj Kumar Date: Sun, 8 Feb 2026 00:52:06 +0530 Subject: [PATCH 1/5] fix: harden line count refresh pipeline and add configurable LineSight settings - replace shared debounce behavior with queued URI-set flushing to prevent dropped updates - listen to file change/edit/save events so badges update reliably on content edits - switch cache invalidation from size-only to {size, mtimeMs} to avoid stale same-size edits - fix empty-file stream counting edge case - normalize skip-path matching for cross-platform folder patterns - track and dispose timers/watchers/provider state cleanly across refresh/deactivate - remove intrusive startup popups and use status bar updates - add linesight.* configuration schema (size limit, debounce, batch, excludes, include extensions, etc.) - fix broken test script by making test run compile+lint - update README settings section - upgrade eslint/typescript-eslint/typescript dev toolchain to remove TS version warning --- README.md | 11 +- package-lock.json | 46 +- package.json | 69 ++- src/extension.ts | 1081 ++++++++++++++++++++++++++------------------- 4 files changed, 713 insertions(+), 494 deletions(-) diff --git a/README.md b/README.md index 4be1778..541c070 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,16 @@ Once installed, LineSight will automatically display line counts next to your fi ## Extension Settings -This extension has no configurable settings at this time. +LineSight supports the following settings: + +- `linesight.sizeLimit`: max file size (bytes) before estimated counts are used +- `linesight.batchSize`: files processed per initialization batch +- `linesight.debounceDelay`: debounce delay (ms) for file/update events +- `linesight.initialScanDelay`: delay (ms) before initial workspace scan +- `linesight.estimationFactor`: bytes-per-line factor for large-file estimates +- `linesight.excludeFolders`: extra folder paths to skip +- `linesight.includeExtensions`: optional extensions to include (empty uses built-in defaults) +- `linesight.showStartupNotifications`: show initialization status notifications ## License diff --git a/package-lock.json b/package-lock.json index e24faf9..39940c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { "name": "linesight", - "version": "0.0.1", + "version": "0.0.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "linesight", - "version": "0.0.1", - "license": "ISC", + "version": "0.0.5", + "license": "MIT", "devDependencies": { "@types/node": "^16.18.34", "@types/vscode": "^1.80.0", @@ -74,9 +74,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -124,9 +124,9 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -277,6 +277,7 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -446,6 +447,7 @@ "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -531,9 +533,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -693,6 +695,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -774,9 +777,9 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -1030,9 +1033,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -1214,9 +1217,9 @@ "license": "ISC" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -1727,6 +1730,7 @@ "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index 41e13d8..fafd277 100644 --- a/package.json +++ b/package.json @@ -34,24 +34,79 @@ "group": "navigation" } ] + }, + "configuration": { + "title": "LineSight", + "properties": { + "linesight.sizeLimit": { + "type": "number", + "default": 5000000, + "minimum": 1000, + "description": "Maximum file size in bytes before LineSight switches to an estimated line count." + }, + "linesight.batchSize": { + "type": "number", + "default": 200, + "minimum": 1, + "description": "Number of files refreshed per batch during initialization." + }, + "linesight.debounceDelay": { + "type": "number", + "default": 300, + "minimum": 50, + "description": "Debounce delay in milliseconds for file update events." + }, + "linesight.initialScanDelay": { + "type": "number", + "default": 2000, + "minimum": 0, + "description": "Delay in milliseconds before the initial workspace scan starts." + }, + "linesight.estimationFactor": { + "type": "number", + "default": 50, + "minimum": 1, + "description": "Bytes-per-line factor used to estimate counts for oversized files." + }, + "linesight.excludeFolders": { + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "description": "Additional folder paths to exclude from line counting." + }, + "linesight.includeExtensions": { + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "description": "Optional file extensions to include (e.g. .ts, .py). If empty, LineSight uses the built-in defaults." + }, + "linesight.showStartupNotifications": { + "type": "boolean", + "default": false, + "description": "Show status notifications while LineSight initializes." + } + } } }, "scripts": { "vscode:prepublish": "npm run compile", "compile": "tsc -p ./", "watch": "tsc -watch -p ./", - "pretest": "npm run compile && npm run lint", "lint": "eslint src --ext ts", - "test": "node ./out/test/runTest.js", + "test": "npm run compile && npm run lint", "package": "vsce package" }, "devDependencies": { "@types/vscode": "^1.80.0", - "@types/node": "^16.18.34", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "eslint": "^8.44.0", - "typescript": "^5.1.6" + "@types/node": "^20.17.0", + "@typescript-eslint/eslint-plugin": "^8.15.0", + "@typescript-eslint/parser": "^8.15.0", + "eslint": "^8.57.1", + "typescript": "^5.8.2" }, "keywords": ["line count", "file explorer", "productivity"], "author": "Karan Singh", diff --git a/src/extension.ts b/src/extension.ts index 6307326..0775973 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2,65 +2,220 @@ import * as vscode from 'vscode'; import * as fs from 'fs'; import * as path from 'path'; +interface FileCacheMetadata { + size: number; + mtimeMs: number; +} + +interface LineSightConfig { + sizeLimit: number; + batchSize: number; + debounceDelay: number; + initialScanDelay: number; + estimationFactor: number; + excludeFolders: string[]; + includeExtensions: Set; + includeFileNames: Set; + showStartupNotifications: boolean; +} + // Store line counts to avoid recounting const lineCountCache = new Map(); -// Store decorations to update badges +// Store decorations to avoid recreating objects repeatedly const fileDecorations = new Map(); -// Store watcher to avoid multiple instances -let fileWatcher: { dispose: () => void } | undefined; -// Store file sizes to use for estimation and change detection -const fileSizeCache = new Map(); -// Debounce timer for batch updates -let debounceTimer: NodeJS.Timeout | undefined; -// Flag to track initialization state +// Store stat metadata for cache invalidation +const fileMetadataCache = new Map(); + +let fileWatcher: vscode.Disposable | undefined; let isInitializing = false; +let initializationRunId = 0; +let initializationPromise: Promise | undefined; + +// Track timers so they can be cleaned up on deactivate +const activeTimers = new Set(); + +// Queue state for watcher/document driven updates +const pendingWatcherUpdates = new Set(); +let watcherQueueTimer: NodeJS.Timeout | undefined; +let watcherQueueDelayMs = 0; + +const DEFAULT_EXCLUDED_FOLDERS = [ + 'node_modules', '.git', 'dist', 'build', 'out', 'bin', 'obj', + '.vscode', '.idea', '.vs', 'vendor', 'coverage', '.next', '.nuxt', + 'public/assets', 'static/assets', 'target', '.sass-cache', '.cache' +]; + +const BINARY_EXTENSIONS = new Set([ + '.exe', '.dll', '.obj', '.bin', '.jpg', '.jpeg', '.png', '.gif', + '.mp3', '.mp4', '.zip', '.gz', '.tar', '.pdf', '.class', '.pyc', + '.pyd', '.so', '.dylib', '.o', '.a', '.lib', '.woff', '.woff2', + '.ttf', '.eot', '.svg', '.ico', '.bmp', '.tiff', '.webp' +]); + +const DEFAULT_INCLUDED_EXTENSIONS = new Set([ + '.js', '.jsx', '.ts', '.tsx', '.html', '.css', '.scss', '.less', '.vue', '.svelte', + '.go', '.py', '.java', '.c', '.cpp', '.cc', '.cxx', '.h', '.hpp', '.cs', '.php', '.rb', + '.rs', '.kt', '.swift', '.sh', '.bash', '.zsh', '.sql', '.prisma', '.graphql', '.gql', + '.json', '.yaml', '.yml', '.xml', '.toml', '.ini', '.md', '.txt' +]); + +const DEFAULT_INCLUDED_FILE_NAMES = new Set([ + 'dockerfile', + 'makefile', + '.env', + '.gitignore', + '.gitattributes', + '.npmrc', + '.editorconfig' +]); -// Configuration defaults const DEFAULT_CONFIG = { - sizeLimit: 5000000, // 5MB limit to prevent performance issues - batchSize: 200, // Files to process per batch - debounceDelay: 300, // ms to wait before updating after changes - initialScanDelay: 5000, // ms to wait before initial scan after activation - estimationFactor: 50 // bytes per line for estimation + sizeLimit: 5_000_000, + batchSize: 200, + debounceDelay: 300, + initialScanDelay: 2_000, + estimationFactor: 50, + showStartupNotifications: false, }; -// Function to count lines in a file -async function countLines(filePath: string): Promise { - try { - // Check if file exists - const stats = await fs.promises.stat(filePath); - - // Store file size for later change detection - fileSizeCache.set(filePath, stats.size); - - // If file is too large, estimate instead - if (stats.size > DEFAULT_CONFIG.sizeLimit) { - return Math.floor(stats.size / DEFAULT_CONFIG.estimationFactor); +let currentConfig: LineSightConfig = loadConfiguration(); + +function scheduleTimeout(callback: () => void, delayMs: number): NodeJS.Timeout { + const safeDelay = Math.max(0, delayMs); + const timer = setTimeout(() => { + activeTimers.delete(timer); + callback(); + }, safeDelay); + activeTimers.add(timer); + return timer; +} + +function clearTrackedTimer(timer: NodeJS.Timeout | undefined): void { + if (!timer) { + return; + } + clearTimeout(timer); + activeTimers.delete(timer); +} + +function wait(delayMs: number): Promise { + return new Promise((resolve) => { + scheduleTimeout(resolve, delayMs); + }); +} + +function clearAllTrackedTimers(): void { + for (const timer of activeTimers) { + clearTimeout(timer); + } + activeTimers.clear(); + watcherQueueTimer = undefined; +} + +function toPositiveInteger(value: number | undefined, fallback: number, minimum = 1): number { + if (typeof value !== 'number' || !Number.isFinite(value)) { + return fallback; + } + return Math.max(minimum, Math.floor(value)); +} + +function normalizeFolderPath(folder: string): string { + return folder.replace(/\\/g, '/').replace(/^\/+|\/+$/g, ''); +} + +function normalizeExtension(ext: string): string | undefined { + const trimmed = ext.trim().toLowerCase(); + if (!trimmed || trimmed.includes('/') || trimmed.includes('\\')) { + return undefined; + } + if (trimmed.startsWith('.')) { + return trimmed; + } + if (/^[a-z0-9_+-]+$/i.test(trimmed)) { + return `.${trimmed}`; + } + return undefined; +} + +function loadConfiguration(): LineSightConfig { + const cfg = vscode.workspace.getConfiguration('linesight'); + + const configuredExcludes = cfg + .get('excludeFolders', []) + .map(normalizeFolderPath) + .filter(Boolean); + + const excludeFolders = Array.from(new Set([ + ...DEFAULT_EXCLUDED_FOLDERS.map(normalizeFolderPath), + ...configuredExcludes, + ])); + + const configuredExtensions = cfg.get('includeExtensions', []); + const includeExtensions = new Set(); + + for (const value of configuredExtensions) { + const normalized = normalizeExtension(value); + if (normalized) { + includeExtensions.add(normalized); } + } - // Skip files with no extension or known binary extensions - const ext = path.extname(filePath).toLowerCase(); - const binaryExtensions = ['.exe', '.dll', '.obj', '.bin', '.jpg', '.jpeg', '.png', '.gif', '.mp3', '.mp4', - '.zip', '.gz', '.tar', '.pdf', '.class', '.pyc', '.pyd', '.so', '.dylib']; - if (binaryExtensions.includes(ext)) { - return 0; + const effectiveExtensions = includeExtensions.size > 0 + ? includeExtensions + : new Set(DEFAULT_INCLUDED_EXTENSIONS); + + return { + sizeLimit: toPositiveInteger(cfg.get('sizeLimit'), DEFAULT_CONFIG.sizeLimit), + batchSize: toPositiveInteger(cfg.get('batchSize'), DEFAULT_CONFIG.batchSize), + debounceDelay: toPositiveInteger(cfg.get('debounceDelay'), DEFAULT_CONFIG.debounceDelay, 50), + initialScanDelay: toPositiveInteger(cfg.get('initialScanDelay'), DEFAULT_CONFIG.initialScanDelay, 0), + estimationFactor: toPositiveInteger(cfg.get('estimationFactor'), DEFAULT_CONFIG.estimationFactor), + excludeFolders, + includeExtensions: effectiveExtensions, + includeFileNames: new Set(DEFAULT_INCLUDED_FILE_NAMES), + showStartupNotifications: cfg.get('showStartupNotifications', DEFAULT_CONFIG.showStartupNotifications), + }; +} + +function updateConfiguration(): void { + currentConfig = loadConfiguration(); +} + +function clearAllCaches(): void { + lineCountCache.clear(); + fileDecorations.clear(); + fileMetadataCache.clear(); +} + +function shouldSkipPath(filePath: string): boolean { + const normalizedPath = `/${filePath.replace(/\\/g, '/')}`; + + for (const folder of currentConfig.excludeFolders) { + const normalizedFolder = normalizeFolderPath(folder); + if (!normalizedFolder) { + continue; } - try { - // More efficient line counting using stream reading - return await countLinesWithReadStream(filePath); - } catch (error) { - // If we can't read as UTF-8, it might be binary or have encoding issues - console.error(`Error reading file ${filePath}:`, error); - return 0; + const folderPattern = `/${normalizedFolder}/`; + const folderSuffix = `/${normalizedFolder}`; + + if (normalizedPath.includes(folderPattern) || normalizedPath.endsWith(folderSuffix)) { + return true; } - } catch (error) { - // Remove from caches if file is inaccessible - lineCountCache.delete(filePath); - fileSizeCache.delete(filePath); - console.error(`Error accessing ${filePath}:`, error); - return 0; } + + const fileName = path.basename(filePath).toLowerCase(); + const ext = path.extname(fileName).toLowerCase(); + + if (ext && BINARY_EXTENSIONS.has(ext)) { + return true; + } + + if (!ext) { + return !currentConfig.includeFileNames.has(fileName); + } + + return !currentConfig.includeExtensions.has(ext); } // More efficient line counting using read stream instead of loading entire file @@ -68,32 +223,30 @@ async function countLinesWithReadStream(filePath: string): Promise { return new Promise((resolve, reject) => { const readStream = fs.createReadStream(filePath, { encoding: 'utf8', - highWaterMark: 128 * 1024, // Increase to 128KB chunks for better throughput + highWaterMark: 128 * 1024, }); let lineCount = 0; - let incompleteChunk = ''; + let sawAnyContent = false; + let lastCharWasNewline = true; readStream.on('data', (chunk: string) => { - // Count newlines directly for better performance + if (chunk.length > 0) { + sawAnyContent = true; + } + for (let i = 0; i < chunk.length; i++) { if (chunk[i] === '\n') { lineCount++; + lastCharWasNewline = true; + } else { + lastCharWasNewline = false; } } - - // Check if last character was not a newline - // This handles the case where the file doesn't end with a newline - if (chunk.length > 0 && chunk[chunk.length - 1] !== '\n') { - incompleteChunk = chunk[chunk.length - 1]; - } else { - incompleteChunk = ''; - } }); readStream.on('end', () => { - // If the file doesn't end with a newline and has content, count the last line - if (incompleteChunk || lineCount === 0) { + if (sawAnyContent && !lastCharWasNewline) { lineCount++; } resolve(lineCount); @@ -105,509 +258,507 @@ async function countLinesWithReadStream(filePath: string): Promise { }); } +// Function to count lines in a file +async function countLines(filePath: string, stats?: fs.Stats): Promise { + try { + const fileStats = stats ?? await fs.promises.stat(filePath); + + if (!fileStats.isFile()) { + return 0; + } + + fileMetadataCache.set(filePath, { + size: fileStats.size, + mtimeMs: fileStats.mtimeMs, + }); + + const ext = path.extname(filePath).toLowerCase(); + if (BINARY_EXTENSIONS.has(ext)) { + return 0; + } + + if (fileStats.size > currentConfig.sizeLimit) { + return Math.floor(fileStats.size / currentConfig.estimationFactor); + } + + return await countLinesWithReadStream(filePath); + } catch (error) { + lineCountCache.delete(filePath); + fileDecorations.delete(filePath); + fileMetadataCache.delete(filePath); + console.error(`LineSight: Error counting lines for ${filePath}:`, error); + return 0; + } +} + // Format line count for display function formatLineCount(count: number): string { - if (count >= 1000000) { - return `${Math.floor(count / 1000000)}M`; // e.g., "1M" - } else if (count >= 1000) { - return `${Math.floor(count / 1000)}K`; // e.g., "1K" - } else if (count >= 100) { - return `${Math.floor(count / 100)}H`; // e.g., "1H" for hundreds - } else { - return count.toString(); // exact count for <100 + if (count >= 1_000_000) { + return `${Math.floor(count / 1_000_000)}M`; } -} -// Skip directories that should be ignored -function shouldSkipPath(filePath: string): boolean { - // Skip directories that are commonly large or not relevant - const skippedFolders = [ - 'node_modules', '.git', 'dist', 'build', 'out', 'bin', 'obj', - '.vscode', '.idea', '.vs', 'vendor', 'coverage', '.next', '.nuxt', - 'public/assets', 'static/assets', 'target', '.sass-cache', '.cache' - ]; - - // Check if path contains any of the skipped folders - for (const folder of skippedFolders) { - const folderPattern = `${path.sep}${folder}${path.sep}`; - const endPattern = `${path.sep}${folder}`; - - if (filePath.includes(folderPattern) || filePath.endsWith(endPattern)) { - return true; - } + if (count >= 1_000) { + return `${Math.floor(count / 1_000)}K`; } - - // Skip files with large binary extensions - const skipExtensions = [ - '.exe', '.dll', '.obj', '.bin', '.jpg', '.jpeg', '.png', '.gif', - '.mp3', '.mp4', '.zip', '.gz', '.tar', '.pdf', '.class', '.pyc', - '.pyd', '.so', '.dylib', '.o', '.a', '.lib', '.woff', '.woff2', - '.ttf', '.eot', '.svg', '.ico', '.bmp', '.tiff', '.webp' - ]; - - const ext = path.extname(filePath).toLowerCase(); - if (skipExtensions.includes(ext)) { - return true; + + return count.toString(); +} + +function createLineDecoration(lineCount: number, estimated = false): vscode.FileDecoration { + const formattedCount = formatLineCount(lineCount); + const badge = estimated ? `~${formattedCount}` : formattedCount; + const tooltip = estimated + ? `~${lineCount} lines (estimated)` + : `${lineCount} lines`; + + return new vscode.FileDecoration(badge, tooltip); +} + +function buildExcludeGlob(): string | undefined { + if (currentConfig.excludeFolders.length === 0) { + return undefined; } - - // Focus on common code file extensions - const codeFileExtensions = [ - // Web/JavaScript - '.js', '.jsx', '.ts', '.tsx', '.html', '.css', '.scss', '.less', - // Backend - '.go', '.py', '.java', '.c', '.cpp', '.cs', '.php', '.rb', '.rs', - // Data/Config - '.json', '.yaml', '.yml', '.xml', '.md', '.txt' - ]; - - if (codeFileExtensions.includes(ext)) { - return false; + + const patterns = currentConfig.excludeFolders + .map((folder) => normalizeFolderPath(folder)) + .filter(Boolean) + .map((folder) => `**/${folder}/**`); + + if (patterns.length === 0) { + return undefined; } - - // If the file is very large, skip it - try { - const cachedSize = fileSizeCache.get(filePath); - if (cachedSize && cachedSize > DEFAULT_CONFIG.sizeLimit) { - return true; + + return `{${patterns.join(',')}}`; +} + +function cancelInitialization(): void { + initializationRunId++; + isInitializing = false; + initializationPromise = undefined; +} + +async function processBatchesWithDelay( + files: vscode.Uri[], + provider: LineCountDecorationProvider, + batchSize: number, + delayMs: number, + runId: number, +): Promise { + for (let i = 0; i < files.length; i += batchSize) { + if (runId !== initializationRunId) { + return; } - } catch (error) { - // If we can't check the size, continue with other checks + + const batch = files.slice(i, i + batchSize); + provider.refresh(batch); + + if (i > 0 && i % 1000 === 0) { + vscode.window.setStatusBarMessage(`LineSight: Processing files (${i}/${files.length})...`, 1500); + } + + await wait(delayMs); } - - // Skip files that aren't likely to be text/source code - return true; } // Initialize decorations for all visible files -async function initializeDecorations(provider: LineCountDecorationProvider) { - // Prevent multiple initializations - if (isInitializing) return; - isInitializing = true; - - // Get all workspace folders +async function initializeDecorations( + provider: LineCountDecorationProvider, + options: { force?: boolean } = {}, +): Promise { + if (options.force) { + cancelInitialization(); + } else if (isInitializing) { + return initializationPromise ?? Promise.resolve(); + } + const workspaceFolders = vscode.workspace.workspaceFolders; - if (!workspaceFolders) { - isInitializing = false; + if (!workspaceFolders || workspaceFolders.length === 0) { return; } - vscode.window.showInformationMessage('LineSight is counting lines in your files...'); - vscode.window.setStatusBarMessage('LineSight: Initializing...', 2000); - - // Process files in stages to reduce initial load - setTimeout(async () => { - // Force refresh on all files in workspace - for (const folder of workspaceFolders) { - try { - // Define file patterns to include - only the most common code files first - const highPriorityPattern = '**/*.{js,jsx,ts,tsx,py,java,c,cpp,cs,go}'; - const lowPriorityPattern = '**/*.{html,css,scss,less,php,rb,rs,json,yaml,yml,xml,md,txt}'; - - // Define patterns to exclude - const excludePattern = '{**/node_modules/**,**/.git/**,**/dist/**,**/build/**,**/out/**,**/.vscode/**,**/bin/**,**/obj/**,**/.idea/**,**/.vs/**,**/vendor/**,**/coverage/**}'; - - // First process high-priority files (most likely to be viewed) - const highPriorityFiles = await vscode.workspace.findFiles( - new vscode.RelativePattern(folder, highPriorityPattern), - excludePattern, - 1000 // Limit to 1000 most important files first + const runId = ++initializationRunId; + isInitializing = true; + + if (currentConfig.showStartupNotifications) { + vscode.window.setStatusBarMessage('LineSight: Initializing line counts...', 1500); + } + + initializationPromise = (async () => { + try { + await wait(currentConfig.initialScanDelay); + + if (runId !== initializationRunId) { + return; + } + + const excludeGlob = buildExcludeGlob(); + + for (const folder of workspaceFolders) { + if (runId !== initializationRunId) { + return; + } + + const allFiles = await vscode.workspace.findFiles( + new vscode.RelativePattern(folder, '**/*'), + excludeGlob, + 6000, ); - - console.log(`Found ${highPriorityFiles.length} high-priority files in ${folder.name}`); - - // Process high-priority files in small batches - await processBatchesWithDelay(highPriorityFiles, provider, 100, 50); // 100 files per batch, 50ms delay - - // Then process remaining files with lower priority - const lowPriorityFiles = await vscode.workspace.findFiles( - new vscode.RelativePattern(folder, lowPriorityPattern), - excludePattern, - 5000 // Limit total files to prevent excessive processing + + const candidateFiles = allFiles.filter((uri) => + uri.scheme === 'file' && !shouldSkipPath(uri.fsPath) ); - - console.log(`Found ${lowPriorityFiles.length} low-priority files in ${folder.name}`); - - // Process low-priority files in larger batches with more delay - await processBatchesWithDelay(lowPriorityFiles, provider, DEFAULT_CONFIG.batchSize, 100); - } catch (error) { - console.error(`Error initializing decorations for ${folder.uri.fsPath}:`, error); - } - } - isInitializing = false; - vscode.window.showInformationMessage('LineSight is ready!'); - }, DEFAULT_CONFIG.initialScanDelay); // Delay initial scan to let the editor load fully -} + await processBatchesWithDelay(candidateFiles, provider, currentConfig.batchSize, 60, runId); + } -// Helper function to process files in batches with delays -async function processBatchesWithDelay( - files: vscode.Uri[], - provider: LineCountDecorationProvider, - batchSize: number, - delayMs: number -): Promise { - for (let i = 0; i < files.length; i += batchSize) { - const batch = files.slice(i, i + batchSize); - - // Update progress periodically - if (i % 1000 === 0 && i > 0) { - vscode.window.setStatusBarMessage(`LineSight: Processing files (${i}/${files.length})...`, 2000); + if (runId === initializationRunId) { + provider.refresh(); + vscode.window.setStatusBarMessage('LineSight: Ready', 1200); + } + } catch (error) { + console.error('LineSight: Initialization failed:', error); + } finally { + if (runId === initializationRunId) { + isInitializing = false; + initializationPromise = undefined; + } } - - // Process batch - provider.refresh(batch); - - // Delay to prevent UI freezing - await new Promise(resolve => setTimeout(resolve, delayMs)); - } + })(); + + return initializationPromise; } // Main file decorator provider -class LineCountDecorationProvider implements vscode.FileDecorationProvider { - private _onDidChangeFileDecorations = new vscode.EventEmitter(); +class LineCountDecorationProvider implements vscode.FileDecorationProvider, vscode.Disposable { + private _onDidChangeFileDecorations = new vscode.EventEmitter(); readonly onDidChangeFileDecorations = this._onDidChangeFileDecorations.event; - // Add a queue system to prevent too many concurrent operations private processingQueue = new Map>(); - // Add a throttling mechanism - private lastUpdate = 0; private pendingUpdates = new Set(); + private refreshTimer: NodeJS.Timeout | undefined; + private fullRefreshPending = false; async provideFileDecoration(uri: vscode.Uri): Promise { try { - // Skip non-file schemes if (uri.scheme !== 'file') { return undefined; } const filePath = uri.fsPath; - - // Skip paths that should be ignored + if (shouldSkipPath(filePath)) { return undefined; } - - // Check if we already have a decoration for this file - const existingDecoration = fileDecorations.get(filePath); - if (existingDecoration) { - return existingDecoration; + + const stat = await fs.promises.stat(filePath); + if (!stat.isFile()) { + return undefined; } - - try { - // Check if file exists and is a regular file - const stat = await fs.promises.stat(filePath); - if (!stat.isFile()) { - return undefined; - } - // For empty files, return 0 immediately - if (stat.size === 0) { - const zeroDecoration = new vscode.FileDecoration('0', '0 lines'); - fileDecorations.set(filePath, zeroDecoration); - return zeroDecoration; - } + const cachedMetadata = fileMetadataCache.get(filePath); + const cachedCount = lineCountCache.get(filePath); - // Check for file size changes - if size matches cache, use cached line count - const cachedSize = fileSizeCache.get(filePath); - let lineCount = lineCountCache.get(filePath); - - if (cachedSize === stat.size && lineCount !== undefined) { - // Use cached value if file size hasn't changed - const formattedCount = formatLineCount(lineCount); - const decoration = new vscode.FileDecoration( - formattedCount, - `${lineCount} lines`, - ); - return decoration; - } - - // Update file size cache - fileSizeCache.set(filePath, stat.size); - - // If file is too large, use an estimation - if (stat.size > DEFAULT_CONFIG.sizeLimit) { - lineCount = Math.floor(stat.size / DEFAULT_CONFIG.estimationFactor); - lineCountCache.set(filePath, lineCount); - const formattedCount = formatLineCount(lineCount); - const decoration = new vscode.FileDecoration( - formattedCount, - `~${lineCount} lines (estimated)`, - ); - fileDecorations.set(filePath, decoration); - return decoration; - } - - // If not in cache or size changed, count lines - if (lineCount === undefined || lineCount === 0) { - // Check if this file is already being processed - let countPromise = this.processingQueue.get(filePath); - - if (!countPromise) { - // If not already processing, create a new counting promise - countPromise = countLines(filePath).then(count => { - // Store result in cache when done - if (count > 0) { - lineCountCache.set(filePath, count); - } - // Remove from processing queue when done - this.processingQueue.delete(filePath); - return count; - }).catch(err => { - console.error(`Error counting lines in ${filePath}:`, err); - this.processingQueue.delete(filePath); - return 0; - }); - - // Add to processing queue - this.processingQueue.set(filePath, countPromise); - } - - // Wait for counting to complete - lineCount = await countPromise; - } - - // Only create decoration if we have a positive line count - if (lineCount > 0) { - const formattedCount = formatLineCount(lineCount); - - // Create the decoration with the formatted count - const decoration = new vscode.FileDecoration( - formattedCount, - `${lineCount} lines`, - ); - - // Store decoration for later reference - fileDecorations.set(filePath, decoration); - return decoration; - } - - return undefined; - } catch (error) { - // File might not exist or be inaccessible - console.error(`Error providing decoration for ${filePath}:`, error); + if ( + cachedMetadata && + cachedCount !== undefined && + cachedMetadata.size === stat.size && + cachedMetadata.mtimeMs === stat.mtimeMs + ) { + const cachedDecoration = fileDecorations.get(filePath) ?? createLineDecoration(cachedCount, stat.size > currentConfig.sizeLimit); + fileDecorations.set(filePath, cachedDecoration); + return cachedDecoration; + } + + fileMetadataCache.set(filePath, { + size: stat.size, + mtimeMs: stat.mtimeMs, + }); + + if (stat.size === 0) { + lineCountCache.set(filePath, 0); + const zeroDecoration = createLineDecoration(0); + fileDecorations.set(filePath, zeroDecoration); + return zeroDecoration; + } + + if (stat.size > currentConfig.sizeLimit) { + const estimatedLineCount = Math.floor(stat.size / currentConfig.estimationFactor); + lineCountCache.set(filePath, estimatedLineCount); + const estimatedDecoration = createLineDecoration(estimatedLineCount, true); + fileDecorations.set(filePath, estimatedDecoration); + return estimatedDecoration; + } + + let lineCountPromise = this.processingQueue.get(filePath); + + if (!lineCountPromise) { + lineCountPromise = countLines(filePath, stat) + .then((lineCount) => { + this.processingQueue.delete(filePath); + return lineCount; + }) + .catch((error) => { + this.processingQueue.delete(filePath); + console.error(`LineSight: Failed to count lines for ${filePath}:`, error); + return 0; + }); + + this.processingQueue.set(filePath, lineCountPromise); + } + + const lineCount = await lineCountPromise; + + if (lineCount <= 0) { + fileDecorations.delete(filePath); return undefined; } - } catch (error) { - console.error(`Unexpected error for ${uri.fsPath}:`, error); + + lineCountCache.set(filePath, lineCount); + const decoration = createLineDecoration(lineCount); + fileDecorations.set(filePath, decoration); + return decoration; + } catch { return undefined; } } - // Notify VS Code to refresh decorations with debouncing refresh(resources?: vscode.Uri | vscode.Uri[]) { - // Clear any pending debounce - if (debounceTimer) { - clearTimeout(debounceTimer); - } - - // Add resources to pending updates - if (resources && !Array.isArray(resources)) { - this.pendingUpdates.add(resources.fsPath); - lineCountCache.delete(resources.fsPath); - fileDecorations.delete(resources.fsPath); + if (resources === undefined) { + this.fullRefreshPending = true; + this.pendingUpdates.clear(); } else if (Array.isArray(resources)) { - resources.forEach(uri => { + for (const uri of resources) { this.pendingUpdates.add(uri.fsPath); lineCountCache.delete(uri.fsPath); fileDecorations.delete(uri.fsPath); - }); - } - - // Throttle updates - const now = Date.now(); - const timeSinceLastUpdate = now - this.lastUpdate; - - // If we've updated recently, use a debounce - if (timeSinceLastUpdate < DEFAULT_CONFIG.debounceDelay && !isInitializing) { - debounceTimer = setTimeout(() => { - this.flushUpdates(); - }, DEFAULT_CONFIG.debounceDelay - timeSinceLastUpdate); + fileMetadataCache.delete(uri.fsPath); + } } else { - // Otherwise update immediately - this.flushUpdates(); + this.pendingUpdates.add(resources.fsPath); + lineCountCache.delete(resources.fsPath); + fileDecorations.delete(resources.fsPath); + fileMetadataCache.delete(resources.fsPath); } + + clearTrackedTimer(this.refreshTimer); + + const delayMs = isInitializing ? Math.max(100, currentConfig.debounceDelay) : currentConfig.debounceDelay; + this.refreshTimer = scheduleTimeout(() => { + this.flushUpdates(); + }, delayMs); } - - // Flush all pending updates + + dispose(): void { + clearTrackedTimer(this.refreshTimer); + this.refreshTimer = undefined; + this.pendingUpdates.clear(); + this.processingQueue.clear(); + this._onDidChangeFileDecorations.dispose(); + } + private flushUpdates() { + if (this.fullRefreshPending) { + this.fullRefreshPending = false; + this._onDidChangeFileDecorations.fire(undefined); + return; + } + if (this.pendingUpdates.size === 0) { - // No pending updates, just fire empty array - this._onDidChangeFileDecorations.fire([]); return; } - - // Convert pending updates to Uri array + const updates: vscode.Uri[] = []; - this.pendingUpdates.forEach(fsPath => { + this.pendingUpdates.forEach((fsPath) => { updates.push(vscode.Uri.file(fsPath)); }); - - // Clear pending updates + this.pendingUpdates.clear(); - - // Update last update time - this.lastUpdate = Date.now(); - - // Fire event this._onDidChangeFileDecorations.fire(updates); } } +function flushQueuedWatcherUpdates(provider: LineCountDecorationProvider): void { + clearTrackedTimer(watcherQueueTimer); + watcherQueueTimer = undefined; + watcherQueueDelayMs = 0; + + if (pendingWatcherUpdates.size === 0) { + return; + } + + const updates = Array.from(pendingWatcherUpdates).map((filePath) => vscode.Uri.file(filePath)); + pendingWatcherUpdates.clear(); + provider.refresh(updates); +} + +// Helper function to queue updates with debouncing without losing previous files +function queueUpdate( + uri: vscode.Uri, + provider: LineCountDecorationProvider, + delay: number = currentConfig.debounceDelay, +): void { + if (uri.scheme !== 'file') { + return; + } + + pendingWatcherUpdates.add(uri.fsPath); + + const normalizedDelay = Math.max(50, delay); + + if (!watcherQueueTimer) { + watcherQueueDelayMs = normalizedDelay; + watcherQueueTimer = scheduleTimeout(() => { + flushQueuedWatcherUpdates(provider); + }, watcherQueueDelayMs); + return; + } + + if (normalizedDelay < watcherQueueDelayMs) { + clearTrackedTimer(watcherQueueTimer); + watcherQueueDelayMs = normalizedDelay; + watcherQueueTimer = scheduleTimeout(() => { + flushQueuedWatcherUpdates(provider); + }, watcherQueueDelayMs); + } +} + // Set up file system watcher to track changes -function setupFileWatcher(context: vscode.ExtensionContext, provider: LineCountDecorationProvider) { - // Clear any existing watchers +function setupFileWatcher(provider: LineCountDecorationProvider): void { if (fileWatcher) { fileWatcher.dispose(); + fileWatcher = undefined; } - + const workspaceFolders = vscode.workspace.workspaceFolders; - if (!workspaceFolders) return; - - // Watch for file changes in all workspace folders - const watchers: vscode.FileSystemWatcher[] = []; - + if (!workspaceFolders || workspaceFolders.length === 0) { + return; + } + + const watchers: vscode.Disposable[] = []; + for (const folder of workspaceFolders) { - // Only watch the most common code file types to reduce overhead - const highPriorityPattern = new vscode.RelativePattern( - folder, - '**/*.{js,jsx,ts,tsx,py,java,c,cpp,cs,go}' + const watcher = vscode.workspace.createFileSystemWatcher( + new vscode.RelativePattern(folder, '**/*'), + false, + false, + false, ); - - const watcher = vscode.workspace.createFileSystemWatcher(highPriorityPattern, false, true, false); - - // Only listen for created and deleted events, not changed (reduces overhead) - // We'll use file size detection to handle changes - - // When a file is created, add to tracking + watcher.onDidCreate((uri: vscode.Uri) => { - if (uri.scheme !== 'file' || shouldSkipPath(uri.fsPath)) return; - - // Queue update with debouncing + if (shouldSkipPath(uri.fsPath)) { + return; + } queueUpdate(uri, provider); }); - - // When a file is deleted, remove from cache + + watcher.onDidChange((uri: vscode.Uri) => { + if (shouldSkipPath(uri.fsPath)) { + return; + } + queueUpdate(uri, provider, currentConfig.debounceDelay); + }); + watcher.onDidDelete((uri: vscode.Uri) => { lineCountCache.delete(uri.fsPath); fileDecorations.delete(uri.fsPath); - fileSizeCache.delete(uri.fsPath); + fileMetadataCache.delete(uri.fsPath); + provider.refresh(uri); }); - - context.subscriptions.push(watcher); + watchers.push(watcher); - - // Add a lower priority watcher for less commonly edited file types - const lowPriorityPattern = new vscode.RelativePattern( - folder, - '**/*.{html,css,scss,less,php,rb,rs,json,yaml,yml,xml,md,txt}' - ); - - const lowPriorityWatcher = vscode.workspace.createFileSystemWatcher(lowPriorityPattern, false, true, false); - - // Same event handlers but with more throttling - lowPriorityWatcher.onDidCreate((uri: vscode.Uri) => { - if (uri.scheme !== 'file' || shouldSkipPath(uri.fsPath)) return; - queueUpdate(uri, provider, 500); // longer delay for low priority files - }); - - lowPriorityWatcher.onDidDelete((uri: vscode.Uri) => { - lineCountCache.delete(uri.fsPath); - fileDecorations.delete(uri.fsPath); - fileSizeCache.delete(uri.fsPath); - }); - - context.subscriptions.push(lowPriorityWatcher); - watchers.push(lowPriorityWatcher); } - - // Also watch for visible editor changes to update counts for currently viewed files - context.subscriptions.push( - vscode.window.onDidChangeVisibleTextEditors((editors) => { - editors.forEach(editor => { - const uri = editor.document.uri; - if (uri.scheme === 'file' && !shouldSkipPath(uri.fsPath)) { - queueUpdate(uri, provider, 100); - } - }); - }) - ); - - // Store all watchers - fileWatcher = { - dispose: () => { - watchers.forEach(w => w.dispose()); - } - }; -} -// Helper function to queue updates with debouncing -function queueUpdate(uri: vscode.Uri, provider: LineCountDecorationProvider, delay: number = DEFAULT_CONFIG.debounceDelay) { - if (debounceTimer) { - clearTimeout(debounceTimer); - } - - debounceTimer = setTimeout(() => { - provider.refresh(uri); - }, delay); + fileWatcher = vscode.Disposable.from(...watchers); } -// Main extension activation -export function activate(context: vscode.ExtensionContext) { - console.log('LineSight extension activated'); - - // Create provider and register it +export function activate(context: vscode.ExtensionContext): void { + updateConfiguration(); + const provider = new LineCountDecorationProvider(); - context.subscriptions.push( - vscode.window.registerFileDecorationProvider(provider) - ); - - // Set up file system watcher with a delay to let the editor start up - setTimeout(() => { - setupFileWatcher(context, provider); - - // Initialize decorations with delay - initializeDecorations(provider); - }, 2000); - - // Register command to manually refresh all line counts + context.subscriptions.push(provider); + context.subscriptions.push(vscode.window.registerFileDecorationProvider(provider)); + + setupFileWatcher(provider); + void initializeDecorations(provider); + const refreshCommand = vscode.commands.registerCommand('linesight.refresh', async () => { - // Clear cache to force recounting - lineCountCache.clear(); - fileSizeCache.clear(); - fileDecorations.clear(); - - // Refresh all files - await initializeDecorations(provider); + cancelInitialization(); + clearAllCaches(); + provider.refresh(); + await initializeDecorations(provider, { force: true }); }); - + context.subscriptions.push(refreshCommand); - - // Watch for workspace folder changes + context.subscriptions.push( vscode.workspace.onDidChangeWorkspaceFolders(() => { - // Add delay to prevent immediate heavy processing when changing workspaces - setTimeout(() => { - setupFileWatcher(context, provider); - initializeDecorations(provider); - }, 5000); - }) + cancelInitialization(); + clearAllCaches(); + setupFileWatcher(provider); + void initializeDecorations(provider, { force: true }); + }), + ); + + context.subscriptions.push( + vscode.workspace.onDidChangeConfiguration((event) => { + if (!event.affectsConfiguration('linesight')) { + return; + } + + updateConfiguration(); + cancelInitialization(); + clearAllCaches(); + setupFileWatcher(provider); + provider.refresh(); + void initializeDecorations(provider, { force: true }); + }), + ); + + context.subscriptions.push( + vscode.workspace.onDidChangeTextDocument((event) => { + const uri = event.document.uri; + if (uri.scheme !== 'file' || shouldSkipPath(uri.fsPath)) { + return; + } + queueUpdate(uri, provider, currentConfig.debounceDelay); + }), + ); + + context.subscriptions.push( + vscode.workspace.onDidSaveTextDocument((document) => { + const uri = document.uri; + if (uri.scheme !== 'file' || shouldSkipPath(uri.fsPath)) { + return; + } + queueUpdate(uri, provider, 75); + }), + ); + + context.subscriptions.push( + vscode.window.onDidChangeVisibleTextEditors((editors) => { + for (const editor of editors) { + const uri = editor.document.uri; + if (uri.scheme !== 'file' || shouldSkipPath(uri.fsPath)) { + continue; + } + queueUpdate(uri, provider, 100); + } + }), ); } -// Called when extension is deactivated -export function deactivate() { - // Clear all caches - lineCountCache.clear(); - fileDecorations.clear(); - fileSizeCache.clear(); - - // Clear any pending timers - if (debounceTimer) { - clearTimeout(debounceTimer); - } - - // Dispose watchers +export function deactivate(): void { + cancelInitialization(); + clearAllCaches(); + + pendingWatcherUpdates.clear(); + clearTrackedTimer(watcherQueueTimer); + watcherQueueTimer = undefined; + + clearAllTrackedTimers(); + if (fileWatcher) { fileWatcher.dispose(); + fileWatcher = undefined; } -} \ No newline at end of file +} From 7d11183ba34575e86f7c7504ab7e0599f508633b Mon Sep 17 00:00:00 2001 From: Niraj Kumar Date: Sun, 8 Feb 2026 02:12:36 +0530 Subject: [PATCH 2/5] feat: Refactored extension modules and reliability hardening. Added core extension modules, comprehensive test suite, and changelog. --- .mocharc.yml | 3 + CHANGELOG.md | 288 +++++++++++ package-lock.json | 959 ++++++++++++++++++++++++++--------- package.json | 12 +- src/cache.ts | 60 +++ src/concurrency.ts | 75 +++ src/config.ts | 93 ++++ src/constants.ts | 43 ++ src/decorationProvider.ts | 224 ++++++++ src/extension.ts | 778 +++------------------------- src/fileFilter.ts | 65 +++ src/fileWatcher.ts | 131 +++++ src/initialization.ts | 128 +++++ src/lineCounter.ts | 144 ++++++ src/state.ts | 54 ++ src/test/cache.test.ts | 112 ++++ src/test/concurrency.test.ts | 112 ++++ src/test/config.test.ts | 98 ++++ src/test/extension.test.ts | 77 +++ src/test/fileFilter.test.ts | 84 +++ src/test/lineCounter.test.ts | 75 +++ src/test/setup.ts | 17 + src/test/timer.test.ts | 46 ++ src/test/vscode-mock.ts | 129 +++++ src/timer.ts | 44 ++ src/types.ts | 33 ++ 26 files changed, 2945 insertions(+), 939 deletions(-) create mode 100644 .mocharc.yml create mode 100644 CHANGELOG.md create mode 100644 src/cache.ts create mode 100644 src/concurrency.ts create mode 100644 src/config.ts create mode 100644 src/constants.ts create mode 100644 src/decorationProvider.ts create mode 100644 src/fileFilter.ts create mode 100644 src/fileWatcher.ts create mode 100644 src/initialization.ts create mode 100644 src/lineCounter.ts create mode 100644 src/state.ts create mode 100644 src/test/cache.test.ts create mode 100644 src/test/concurrency.test.ts create mode 100644 src/test/config.test.ts create mode 100644 src/test/extension.test.ts create mode 100644 src/test/fileFilter.test.ts create mode 100644 src/test/lineCounter.test.ts create mode 100644 src/test/setup.ts create mode 100644 src/test/timer.test.ts create mode 100644 src/test/vscode-mock.ts create mode 100644 src/timer.ts create mode 100644 src/types.ts diff --git a/.mocharc.yml b/.mocharc.yml new file mode 100644 index 0000000..04203d7 --- /dev/null +++ b/.mocharc.yml @@ -0,0 +1,3 @@ +require: + - out/test/setup.js +timeout: 10000 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5b77dc0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,288 @@ +# Changelog + +## v0.0.7 — Security Hardening (Unreleased) + +### Security Fixes + +#### Glob pattern injection via `excludeFolders` settings +- `normalizeFolderPath` now validates folder names against an allowlist of safe characters (`a-z`, `0-9`, `_`, `-`, `.`, `/`). +- Folder entries containing glob metacharacters (`*`, `?`, `{`, `}`, `[`, `]`, `,`, etc.) are silently rejected, preventing crafted workspace settings from manipulating exclude glob patterns. + +#### Unbounded queue growth under high-churn workloads +- `pendingWatcherUpdates` (file watcher) and `pendingUpdates` (decoration provider) are now capped at 500 entries. When exceeded, individual tracking is abandoned in favor of a single full refresh — preventing sustained memory growth in pathological repos. +- `ConcurrencyLimiter` queue is now capped (default 500). New work is rejected when the queue is full; existing error handling absorbs the rejection gracefully. + +#### Explicit workspace trust handling +- `package.json` now declares `capabilities.untrustedWorkspaces.supported: "limited"`. +- File scanning and watching are deferred until `vscode.workspace.isTrusted` is true. In untrusted workspaces, the extension registers for `onDidGrantWorkspaceTrust` and starts only when trust is granted. +- Configuration and workspace-folder change handlers are gated on trust. + +--- + +## v0.0.6 — Refactoring, Reliability & Bug Fixes (Unreleased) + +A structural overhaul of the LineSight codebase. This section serves as the single +source of truth for every issue discovered, every fix applied, and what +remains open. + +--- + +### Issue Tracker + +#### Round 1: First codebase review + +| # | Severity | Issue | Status | +|---|----------|-------|--------| +| R1-1 | CRITICAL | Shared `debounceTimer` race condition — two code paths mutate the same timer | **Fixed.** Provider owns `refreshTimer`; watcher owns `state.watcherQueueTimer`. | +| R1-2 | MEDIUM | Empty files return 1 line from `countLinesWithReadStream` | **Fixed.** `sawAnyContent` + `lastCharWasNewline` flags handle all edge cases. | +| R1-3 | HIGH | `isInitializing` flag blocks refresh command | **Fixed.** `initializeDecorations({ force: true })` cancels running init first. | +| R1-4 | MEDIUM | `isInitializing` stuck forever on unhandled rejection | **Fixed.** try/catch/finally with proper cleanup in `initializationPromise`. | +| R1-5 | HIGH | File change events silently ignored (watcher param `true`) | **Fixed.** `createFileSystemWatcher(pattern, false, false, false)`. | +| R1-6 | MEDIUM | Cross-platform path matching broken on Windows | **Fixed.** `shouldSkipPath` normalizes all paths to forward slashes. | +| R1-7 | HIGH | Module-level global state — untestable, tightly coupled | **Fixed.** `AppState` struct passed explicitly to all modules. | +| R1-8 | MEDIUM | Duplicated binary extension lists (19 vs 27 entries) | **Fixed.** Single `BINARY_EXTENSIONS` Set in `constants.ts`. | +| R1-9 | MEDIUM | Arrays recreated on every `shouldSkipPath` call | **Fixed.** Constants hoisted to module-level Sets. | +| R1-10 | MEDIUM | Dual `stat` calls per file (provider + countLines) | **Fixed.** `provideFileDecoration` passes its stat result into `countLines`. | +| R1-11 | LOW | EventEmitter never disposed | **Fixed.** `dispose()` on provider calls `_onDidChangeFileDecorations.dispose()`. | +| R1-12 | MEDIUM | "H" abbreviation for hundreds is non-standard | **Fixed.** Shows exact counts under 1000, K for thousands, M for millions. | +| R1-13 | CRITICAL | Zero test coverage | **Fixed.** 59 unit tests across 6 test files (see below). | +| R1-14 | HIGH | No user-configurable settings | **Fixed.** 8 settings in `contributes.configuration`. | +| R1-15 | HIGH | Intrusive `showInformationMessage` on every activation | **Fixed.** Uses `setStatusBarMessage` only, gated behind `showStartupNotifications`. | +| R1-16 | MEDIUM | No read timeout on file stream — hangs on FIFO/network mounts | **Fixed.** 10s timeout with `settled` flag and stream destruction. | +| R1-17 | MEDIUM | No cancellation support for initialization | **Fixed.** `initializationRunId` monotonic counter detects stale runs. | +| R1-18 | LOW | Symlink traversal (`stat` follows symlinks to FIFOs) | Open. Timeout limits blast radius but `stat` itself has no timeout. | +| R1-19 | MEDIUM | No CI/CD pipeline | Open. Tests exist but don't run automatically on PRs. | +| R1-20 | MEDIUM | Stream overhead for small files (ReadStream for every size) | Open. Low priority — current approach is correct, just suboptimal for <512KB. | + +--- + +#### Round 2: Post-refactor review + +Issues identified after the modularization refactor. + +| # | Severity | Issue | Status | +|---|----------|-------|--------| +| R2-1 | HIGH | Stale in-flight counts committed with fresh metadata — `processingQueue` race condition | **Fixed.** See fix details below. | +| R2-2 | HIGH | `ConcurrencyLimiter` deadlocks when task function throws synchronously | **Fixed.** See fix details below. | +| R2-3 | MEDIUM | `onDidChangeTextDocument` triggers pointless disk I/O on every keystroke | **Fixed.** See fix details below. | +| R2-4 | MEDIUM | `findFiles` silently truncates at 6,000 files per workspace folder | **Fixed.** See fix details below. | +| R2-5 | MEDIUM | `activeTimers` in `timer.ts` is the lone module-level global | Open. Works correctly but inconsistent with AppState pattern. | +| R2-6 | MEDIUM | File watcher `**/*` pattern is broad; excluded dirs still generate callbacks | Open. VS Code's watcher API doesn't support complex exclude globs; filtering in callbacks is the standard approach. | +| R2-7 | MEDIUM | Unbounded `stat()` calls bypass the concurrency limiter | Open. Only impacts network-mounted filesystems; local stats are ~0.1ms. | +| R2-8 | LOW | Redundant `updateConfiguration` call on activate | Open. Harmless — double-loads identical config. | +| R2-9 | LOW | Redundant double `cancelInitialization` in refresh command | Open. Defensive pattern — not worth removing. | +| R2-10 | LOW | Debounce is "first-event windowed" not trailing-edge | Open. Valid design choice for batch flushing. | +| R2-11 | LOW | Redundant double-normalization of exclude folders in hot paths | Open. Correct but wasteful. | +| R2-12 | LOW | `LRUCache.get()` can't distinguish stored `undefined` from miss | Open. Not a practical issue — cache value types are never `undefined`. | +| R2-13 | LOW | `FileDecoration.badge` limited to 2 chars; `~9K` and `999` are 3 | Open. Verify rendering for edge cases. | + +--- + +### Fix Details + +#### Fix 1: ConcurrencyLimiter sync throw deadlock (R2-2) + +**Problem:** `run()` calls `this.running++` then `fn().then(...)`. If `fn` +throws synchronously before returning a Promise, the `.then()` handlers never +execute, so `running--` and `dequeue()` never run. With `maxConcurrent=1` +this permanently stalls all future tasks. + +**Fix:** Wrapped `fn()` invocation in try/catch. On sync throw: decrement +`running`, call `dequeue()`, and reject the outer promise. Added a regression +test that verifies a sync-throwing task rejects cleanly without blocking the +queue. + +**Files:** `src/concurrency.ts`, `src/test/concurrency.test.ts` + +--- + +#### Fix 2: Stale in-flight counts race condition (R2-1) + +**Problem:** `processingQueue` deduplicates by file path only. If a file +changes while an old count is in-flight: + +1. `refresh()` clears caches but old promise stays in `processingQueue` +2. New `provideFileDecoration` reuses the stale promise +3. Stale count is written to caches under fresh metadata +4. Wrong badge persists until the next filesystem event + +**Fix (three parts):** + +1. `refresh()` now deletes `processingQueue` entries for invalidated files, + so new requests create fresh counting promises instead of reusing stale ones. +2. After `await lineCountPromise`, a metadata guard compares the current + `fileMetadataCache` entry against the `stat` captured at the start of + `provideFileDecoration`. If they diverge (because `refresh()` cleared or + updated the metadata), the stale result is silently dropped. +3. `flushUpdates()` clears `pendingUpdates` when firing a full refresh, + preventing stale file-specific entries from re-firing after a full refresh + already covers them. + +**File:** `src/decorationProvider.ts` + +--- + +#### Fix 3: `onDidChangeTextDocument` wasted disk I/O (R2-3) + +**Problem:** Every keystroke triggered `queueUpdate` -> `refresh` -> cache +invalidation -> `provideFileDecoration` -> `fs.stat()` + stream read on the +**saved disk file**. The count never changed until save, so all this I/O was +wasted. On a large workspace, rapid typing caused unnecessary stat + read +operations every ~300ms. + +**Fix:** Added `updateFromBuffer(uri, lineCount)` method to the decoration +provider. The `onDidChangeTextDocument` handler now calls it with +`event.document.lineCount` (VS Code's in-memory buffer count), which writes +directly to caches and schedules a debounced notification — zero disk I/O. +The disk-based count is reconciled on save via `onDidSaveTextDocument` and +the file watcher. + +**Bonus:** Users now get live line count updates as they type, rather than +waiting for save. + +**Files:** `src/decorationProvider.ts`, `src/extension.ts` + +--- + +#### Fix 4: `findFiles` silent 6,000 cap (R2-4) + +**Problem:** `findFiles(..., 6000)` imposes a hard per-folder limit with no +feedback. Large monorepos would silently have missing decorations for files +beyond the cap, with no indication to the user or developer. + +**Fix:** Added `console.warn` when the result count reaches the cap, so the +behavior is at least observable in the developer console / output channel. + +**File:** `src/initialization.ts` + +--- + +### Structural Changes + +#### Modular file structure + +The monolithic 765-line `src/extension.ts` was split into 13 focused modules +with an explicit, cycle-free dependency graph: + +``` +types constants cache concurrency timer + \ | | + \---> config <-+ | + | | | + fileFilter | | + | | | + lineCounter | | + \ | | + state <------+------------+ + | + decorationProvider <-- concurrency, fileFilter, lineCounter, timer + | + fileWatcher <-- fileFilter, timer + initialization <-- fileFilter, timer + | + extension.ts (thin entry point, ~100 lines) +``` + +**Key design decisions:** + +- **AppState as explicit parameter:** All mutable state lives in a single + `AppState` object created at activation and threaded through every function. + Makes data flow visible and modules testable without global setup/teardown. + +- **Config as function parameter:** `shouldSkipPath` and `buildExcludeGlob` + accept `LineSightConfig` explicitly. Tests pass hand-crafted config objects + without touching VS Code settings. + +- **Zero new runtime dependencies:** `LRUCache` and `ConcurrencyLimiter` are + implemented in-house. Mocha is devDependencies-only. + +#### Bounded caches via LRU eviction + +Replaced the three unbounded `Map`s with `LRUCache` (max 10,000 +entries each). The implementation exploits ES2015 Map insertion order: +`get()` promotes by delete + re-insert; `has()` is a non-promoting peek. +Drop-in replacement — same `.get/.set/.delete/.has/.clear` API. + +**File:** `src/cache.ts`, `src/state.ts` + +#### Bounded concurrency for file reads + +Introduced `ConcurrencyLimiter` — a promise-based concurrency gate with a +FIFO queue. The decoration provider wraps every `countLines` call in +`limiter.run(...)` with a cap of 20 concurrent reads. + +**File:** `src/concurrency.ts`, `src/decorationProvider.ts` + +#### Read timeout on file streams + +Added a configurable `timeoutMs` parameter (default 10,000 ms) to +`countLinesWithReadStream`. A `settled` flag ensures whichever of end / +error / timeout fires first wins. On timeout the stream is destroyed +immediately so the file handle is released. + +**File:** `src/lineCounter.ts` + +#### User-configurable settings + +Added 8 settings under `linesight.*` in `contributes.configuration`: +`sizeLimit`, `batchSize`, `debounceDelay`, `initialScanDelay`, +`estimationFactor`, `excludeFolders`, `includeExtensions`, +`showStartupNotifications`. + +**File:** `package.json`, `src/config.ts` + +--- + +### Test Coverage + +59 unit tests across 6 test files, running outside the VS Code extension host +via a lightweight mock (`test/vscode-mock.ts`): + +| File | What it tests | +|------|---------------| +| `test/cache.test.ts` | LRU eviction, recency promotion, peek, edge cases (capacity 0/1) | +| `test/concurrency.test.ts` | Parallel limits, queue ordering, error propagation, sync-throw handling | +| `test/config.test.ts` | `toPositiveInteger`, `normalizeFolderPath`, `normalizeExtension` | +| `test/fileFilter.test.ts` | `shouldSkipPath`, `buildExcludeGlob` with manual configs | +| `test/lineCounter.test.ts` | `formatLineCount`, `countLinesWithReadStream` (real temp files) | +| `test/timer.test.ts` | `scheduleTimeout`, `clearTrackedTimer`, `wait` | + +**Test gaps (known):** +- `decorationProvider.ts` — most complex module, requires richer VS Code mock +- `fileWatcher.ts` — debouncing and watcher setup +- `initialization.ts` — batch processing and cancellation +- `countLines` wrapper — depends on AppState +- `countLinesWithReadStream` timeout path — needs FIFO or mock stream + +--- + +### Open Items (not yet addressed) + +| # | Severity | Item | Notes | +|---|----------|------|-------| +| R1-18 | LOW | Symlink traversal | `stat()` follows symlinks; timeout limits blast radius | +| R1-19 | MEDIUM | No CI/CD pipeline | Tests exist but need GitHub Actions | +| R1-20 | LOW | Stream overhead for small files | Correct but suboptimal | +| R2-5 | MEDIUM | `activeTimers` module-level global | Works correctly, inconsistent with AppState | +| R2-6 | MEDIUM | Broad file watcher pattern | VS Code API limitation | +| R2-7 | MEDIUM | Unbounded stat calls | Only impacts network filesystems | +| R2-10 | LOW | First-event-windowed debounce | Valid design choice | +| R2-11 | LOW | Double-normalization of exclude folders | Correct but redundant | +| R2-13 | LOW | Badge 2-char limit edge cases | Needs manual verification | + +--- + +### How to verify + +```bash +npm install # picks up mocha + @types/mocha +npm run compile # should produce zero errors +npm run lint # should produce zero warnings/errors +npm run test:unit # 59 mocha tests, all passing +npm test # compile + lint + mocha in sequence +``` + +For manual verification: open a workspace in the VS Code extension +development host (`F5`), confirm line counts appear in the explorer, edit a +file and verify counts update live while typing, save and confirm the count +reconciles, and run the "Refresh Line Counts" command. diff --git a/package-lock.json b/package-lock.json index 39940c2..f3252b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,21 +9,23 @@ "version": "0.0.5", "license": "MIT", "devDependencies": { - "@types/node": "^16.18.34", + "@types/mocha": "^10.0.7", + "@types/node": "^20.17.0", "@types/vscode": "^1.80.0", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "eslint": "^8.44.0", - "typescript": "^5.1.6" + "@typescript-eslint/eslint-plugin": "^8.15.0", + "@typescript-eslint/parser": "^8.15.0", + "eslint": "^8.57.1", + "mocha": "^10.7.0", + "typescript": "^5.8.2" }, "engines": { "vscode": "^1.80.0" } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.0.tgz", - "integrity": "sha512-RoV8Xs9eNwiDvhv7M+xcL4PWyRyIXRY/FLp3buU4h1EYfdF7unWUy3dOjPqb3C7rMUewIcqwW850PgS8h1o1yg==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -40,9 +42,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { @@ -207,26 +209,22 @@ "node": ">= 8" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "node_modules/@types/mocha": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { - "version": "16.18.126", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.126.tgz", - "integrity": "sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==", + "version": "20.19.32", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.32.tgz", + "integrity": "sha512-Ez8QE4DMfhjjTsES9K2dwfV258qBui7qxUsoaixZDiTzbde4U12e1pXGNu/ECsUIOi5/zoCxAQxIhQnaUQ2VvA==", "dev": true, - "license": "MIT" - }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } }, "node_modules/@types/vscode": { "version": "1.98.0", @@ -236,125 +234,160 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", + "integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/type-utils": "8.54.0", + "@typescript-eslint/utils": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^2.4.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@typescript-eslint/parser": "^8.54.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" } }, "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz", + "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "debug": "^4.4.3" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", + "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.54.0", + "@typescript-eslint/types": "^8.54.0", + "debug": "^4.4.3" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", + "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", + "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz", + "integrity": "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/utils": "8.54.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", + "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", "dev": true, "license": "MIT", "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -362,78 +395,88 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", + "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "@typescript-eslint/project-service": "8.54.0", + "@typescript-eslint/tsconfig-utils": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz", + "integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", + "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "8.54.0", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", @@ -482,6 +525,16 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -508,6 +561,20 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -515,16 +582,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -532,6 +589,19 @@ "dev": true, "license": "MIT" }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -555,6 +625,13 @@ "node": ">=8" } }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -565,6 +642,19 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -582,6 +672,56 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -625,9 +765,9 @@ } }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { @@ -642,6 +782,19 @@ } } }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -649,17 +802,14 @@ "dev": true, "license": "MIT" }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">=8" + "node": ">=0.3.1" } }, "node_modules/doctrine": { @@ -675,6 +825,23 @@ "node": ">=6.0.0" } }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -871,36 +1038,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -968,6 +1105,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -997,6 +1144,31 @@ "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1072,27 +1244,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -1110,6 +1261,16 @@ "node": ">=8" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -1166,6 +1327,19 @@ "dev": true, "license": "ISC" }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1176,6 +1350,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1209,6 +1393,29 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1297,46 +1504,125 @@ "dev": true, "license": "MIT" }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, "engines": { - "node": ">= 8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "node_modules/mocha": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", "dev": true, "license": "MIT", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" }, "engines": { - "node": ">=8.6" + "node": ">= 14.0.0" } }, - "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1351,6 +1637,16 @@ "dev": true, "license": "MIT" }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1454,16 +1750,6 @@ "node": ">=8" } }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1518,6 +1804,39 @@ ], "license": "MIT" }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1580,10 +1899,31 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -1593,6 +1933,16 @@ "node": ">=10" } }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -1616,12 +1966,17 @@ "node": ">=8" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, "engines": { "node": ">=8" } @@ -1672,6 +2027,55 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1686,16 +2090,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/type-check": { @@ -1739,6 +2143,13 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -1775,6 +2186,31 @@ "node": ">=0.10.0" } }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -1782,6 +2218,61 @@ "dev": true, "license": "ISC" }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index fafd277..8116dd4 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,12 @@ "activationEvents": [ "onStartupFinished" ], + "capabilities": { + "untrustedWorkspaces": { + "supported": "limited", + "description": "LineSight disables file scanning and watching in untrusted workspaces." + } + }, "main": "./out/extension.js", "contributes": { "commands": [ @@ -97,15 +103,19 @@ "compile": "tsc -p ./", "watch": "tsc -watch -p ./", "lint": "eslint src --ext ts", - "test": "npm run compile && npm run lint", + "pretest": "npm run compile", + "test": "npm run compile && npm run lint && mocha 'out/test/**/*.test.js'", + "test:unit": "npm run compile && mocha 'out/test/**/*.test.js'", "package": "vsce package" }, "devDependencies": { + "@types/mocha": "^10.0.7", "@types/vscode": "^1.80.0", "@types/node": "^20.17.0", "@typescript-eslint/eslint-plugin": "^8.15.0", "@typescript-eslint/parser": "^8.15.0", "eslint": "^8.57.1", + "mocha": "^10.7.0", "typescript": "^5.8.2" }, "keywords": ["line count", "file explorer", "productivity"], diff --git a/src/cache.ts b/src/cache.ts new file mode 100644 index 0000000..d6d2258 --- /dev/null +++ b/src/cache.ts @@ -0,0 +1,60 @@ +/** + * Bounded least-recently-used cache backed by a plain Map. + * + * JS Maps iterate in insertion order, so the *first* key is always the oldest + * and the *last* key is the most recently used. get() promotes an entry by + * deleting and re-inserting it; has() is a non-promoting peek. + * + * Drop-in replacement for the unbounded Maps the extension used previously — + * only .get/.set/.delete/.has/.clear and .size are needed. + */ +export class LRUCache { + private readonly map = new Map(); + private readonly maxSize: number; + + constructor(maxSize: number) { + this.maxSize = Math.max(1, Math.floor(maxSize)); + } + + get(key: K): V | undefined { + const value = this.map.get(key); + if (value === undefined) { + return undefined; + } + // Move to MRU position by re-inserting + this.map.delete(key); + this.map.set(key, value); + return value; + } + + set(key: K, value: V): void { + // If key already exists, delete first so re-insert moves to end + if (this.map.has(key)) { + this.map.delete(key); + } else if (this.map.size >= this.maxSize) { + // Evict oldest (first) entry + const oldest = this.map.keys().next().value; + if (oldest !== undefined) { + this.map.delete(oldest); + } + } + this.map.set(key, value); + } + + delete(key: K): boolean { + return this.map.delete(key); + } + + has(key: K): boolean { + // Peek only — no recency update + return this.map.has(key); + } + + clear(): void { + this.map.clear(); + } + + get size(): number { + return this.map.size; + } +} diff --git a/src/concurrency.ts b/src/concurrency.ts new file mode 100644 index 0000000..ad98cba --- /dev/null +++ b/src/concurrency.ts @@ -0,0 +1,75 @@ +/** + * Promise-based concurrency limiter. + * + * Wraps async work in `run()`. Up to `maxConcurrent` tasks execute in + * parallel; extras are held in a FIFO queue and started as earlier tasks + * finish. Errors in one task do not block the queue. + * + * Used by the decoration provider to cap simultaneous file reads so the + * extension host isn't overwhelmed during large workspace scans. + */ +export class ConcurrencyLimiter { + private readonly maxConcurrent: number; + private readonly maxQueued: number; + private running = 0; + private readonly queue: Array<() => void> = []; + + constructor(maxConcurrent: number, maxQueued = 500) { + this.maxConcurrent = Math.max(1, Math.floor(maxConcurrent)); + this.maxQueued = Math.max(1, Math.floor(maxQueued)); + } + + get activeCount(): number { + return this.running; + } + + get pendingCount(): number { + return this.queue.length; + } + + run(fn: () => Promise): Promise { + return new Promise((resolve, reject) => { + const execute = () => { + this.running++; + let result: Promise; + try { + result = fn(); + } catch (err) { + this.running--; + this.dequeue(); + reject(err); + return; + } + result.then( + (value) => { + this.running--; + this.dequeue(); + resolve(value); + }, + (err) => { + this.running--; + this.dequeue(); + reject(err); + }, + ); + }; + + if (this.running < this.maxConcurrent) { + execute(); + } else if (this.queue.length < this.maxQueued) { + this.queue.push(execute); + } else { + reject(new Error('ConcurrencyLimiter: queue full')); + } + }); + } + + private dequeue(): void { + if (this.queue.length > 0 && this.running < this.maxConcurrent) { + const next = this.queue.shift(); + if (next) { + next(); + } + } + } +} diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..ac37872 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,93 @@ +/** + * Configuration helpers. + * + * Pure validation / normalization functions live here alongside + * `loadConfiguration`, which reads from `vscode.workspace` settings. + * The pure functions are safe to unit-test without a VS Code runtime. + */ + +import * as vscode from 'vscode'; +import { LineSightConfig } from './types'; +import { DEFAULT_EXCLUDED_FOLDERS, DEFAULT_INCLUDED_EXTENSIONS, DEFAULT_INCLUDED_FILE_NAMES, DEFAULT_CONFIG } from './constants'; + +/** Coerce a possibly-undefined number to a positive integer, applying a floor and minimum. */ +export function toPositiveInteger(value: number | undefined, fallback: number, minimum = 1): number { + if (typeof value !== 'number' || !Number.isFinite(value)) { + return fallback; + } + return Math.max(minimum, Math.floor(value)); +} + +/** Characters allowed in folder path tokens: alphanumeric, _, -, ., space, / (after normalization). */ +const SAFE_FOLDER_CHARS = /^[a-zA-Z0-9_\-. /]+$/; + +/** Convert backslashes to forward slashes, strip leading/trailing slashes, and reject glob metacharacters. */ +export function normalizeFolderPath(folder: string): string { + const normalized = folder.replace(/\\/g, '/').replace(/^\/+|\/+$/g, ''); + if (!normalized || !SAFE_FOLDER_CHARS.test(normalized)) { + return ''; + } + return normalized; +} + +/** Normalize a user-supplied extension to lowercase dot-prefixed form, or return undefined if invalid. */ +export function normalizeExtension(ext: string): string | undefined { + const trimmed = ext.trim().toLowerCase(); + if (!trimmed || trimmed.includes('/') || trimmed.includes('\\')) { + return undefined; + } + if (trimmed.startsWith('.')) { + return trimmed; + } + if (/^[a-z0-9_+-]+$/i.test(trimmed)) { + return `.${trimmed}`; + } + return undefined; +} + +/** + * Read the `linesight.*` settings from VS Code and merge them with built-in + * defaults. User-supplied extensions override the defaults entirely when + * non-empty; excluded folders are additive (defaults + user extras). + */ +export function loadConfiguration(): LineSightConfig { + const cfg = vscode.workspace.getConfiguration('linesight'); + + // Merge user-configured excluded folders on top of the built-in defaults. + const configuredExcludes = cfg + .get('excludeFolders', []) + .map(normalizeFolderPath) + .filter(Boolean); + + const excludeFolders = Array.from(new Set([ + ...DEFAULT_EXCLUDED_FOLDERS.map(normalizeFolderPath), + ...configuredExcludes, + ])); + + // If the user provides extensions, use them exclusively; otherwise fall back to defaults. + const configuredExtensions = cfg.get('includeExtensions', []); + const includeExtensions = new Set(); + + for (const value of configuredExtensions) { + const normalized = normalizeExtension(value); + if (normalized) { + includeExtensions.add(normalized); + } + } + + const effectiveExtensions = includeExtensions.size > 0 + ? includeExtensions + : new Set(DEFAULT_INCLUDED_EXTENSIONS); + + return { + sizeLimit: toPositiveInteger(cfg.get('sizeLimit'), DEFAULT_CONFIG.sizeLimit), + batchSize: toPositiveInteger(cfg.get('batchSize'), DEFAULT_CONFIG.batchSize), + debounceDelay: toPositiveInteger(cfg.get('debounceDelay'), DEFAULT_CONFIG.debounceDelay, 50), + initialScanDelay: toPositiveInteger(cfg.get('initialScanDelay'), DEFAULT_CONFIG.initialScanDelay, 0), + estimationFactor: toPositiveInteger(cfg.get('estimationFactor'), DEFAULT_CONFIG.estimationFactor), + excludeFolders, + includeExtensions: effectiveExtensions, + includeFileNames: new Set(DEFAULT_INCLUDED_FILE_NAMES), + showStartupNotifications: cfg.get('showStartupNotifications', DEFAULT_CONFIG.showStartupNotifications), + }; +} diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..a18f73e --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,43 @@ +/** Folders skipped by default — common build output, dependency, and tooling directories. */ +export const DEFAULT_EXCLUDED_FOLDERS = [ + 'node_modules', '.git', 'dist', 'build', 'out', 'bin', 'obj', + '.vscode', '.idea', '.vs', 'vendor', 'coverage', '.next', '.nuxt', + 'public/assets', 'static/assets', 'target', '.sass-cache', '.cache' +]; + +/** Extensions that represent non-text content — always skipped, never line-counted. */ +export const BINARY_EXTENSIONS = new Set([ + '.exe', '.dll', '.obj', '.bin', '.jpg', '.jpeg', '.png', '.gif', + '.mp3', '.mp4', '.zip', '.gz', '.tar', '.pdf', '.class', '.pyc', + '.pyd', '.so', '.dylib', '.o', '.a', '.lib', '.woff', '.woff2', + '.ttf', '.eot', '.svg', '.ico', '.bmp', '.tiff', '.webp' +]); + +/** Source / text extensions counted when the user hasn't overridden `includeExtensions`. */ +export const DEFAULT_INCLUDED_EXTENSIONS = new Set([ + '.js', '.jsx', '.ts', '.tsx', '.html', '.css', '.scss', '.less', '.vue', '.svelte', + '.go', '.py', '.java', '.c', '.cpp', '.cc', '.cxx', '.h', '.hpp', '.cs', '.php', '.rb', + '.rs', '.kt', '.swift', '.sh', '.bash', '.zsh', '.sql', '.prisma', '.graphql', '.gql', + '.json', '.yaml', '.yml', '.xml', '.toml', '.ini', '.md', '.txt' +]); + +/** Well-known extensionless filenames that should still be line-counted. */ +export const DEFAULT_INCLUDED_FILE_NAMES = new Set([ + 'dockerfile', + 'makefile', + '.env', + '.gitignore', + '.gitattributes', + '.npmrc', + '.editorconfig' +]); + +/** Numeric / boolean defaults used when VS Code settings are absent or invalid. */ +export const DEFAULT_CONFIG = { + sizeLimit: 5_000_000, + batchSize: 200, + debounceDelay: 300, + initialScanDelay: 2_000, + estimationFactor: 50, + showStartupNotifications: false, +}; diff --git a/src/decorationProvider.ts b/src/decorationProvider.ts new file mode 100644 index 0000000..e471b1b --- /dev/null +++ b/src/decorationProvider.ts @@ -0,0 +1,224 @@ +/** + * VS Code FileDecorationProvider that shows line counts in the file explorer. + * + * Each call to `provideFileDecoration` is initiated by VS Code whenever a + * file becomes visible. The provider: + * 1. Returns instantly from cache when size+mtime haven't changed. + * 2. Uses byte-based estimation for files above the configured size limit. + * 3. Delegates actual counting to `countLines`, gated through a + * `ConcurrencyLimiter` (default max 20) to avoid overwhelming the host. + * 4. De-duplicates in-flight counts via `processingQueue`. + * 5. Batches refresh notifications with a debounce timer. + */ + +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import { AppState } from './state'; +import { ConcurrencyLimiter } from './concurrency'; +import { shouldSkipPath } from './fileFilter'; +import { countLines, createLineDecoration } from './lineCounter'; +import { scheduleTimeout, clearTrackedTimer } from './timer'; + +/** When more unique pending updates queue up than this, switch to a full refresh. */ +const PENDING_UPDATES_CAP = 500; + +export class LineCountDecorationProvider implements vscode.FileDecorationProvider, vscode.Disposable { + private _onDidChangeFileDecorations = new vscode.EventEmitter(); + readonly onDidChangeFileDecorations = this._onDidChangeFileDecorations.event; + + /** Maps file path -> in-flight line-count promise to avoid duplicate work. */ + private processingQueue = new Map>(); + /** File paths whose decorations need to be re-emitted on the next flush. */ + private pendingUpdates = new Set(); + private refreshTimer: NodeJS.Timeout | undefined; + private fullRefreshPending = false; + + constructor( + private readonly state: AppState, + private readonly limiter: ConcurrencyLimiter, + ) {} + + async provideFileDecoration(uri: vscode.Uri): Promise { + try { + if (uri.scheme !== 'file') { + return undefined; + } + + const filePath = uri.fsPath; + + if (shouldSkipPath(filePath, this.state.config)) { + return undefined; + } + + const stat = await fs.promises.stat(filePath); + if (!stat.isFile()) { + return undefined; + } + + const cachedMetadata = this.state.fileMetadataCache.get(filePath); + const cachedCount = this.state.lineCountCache.get(filePath); + + if ( + cachedMetadata && + cachedCount !== undefined && + cachedMetadata.size === stat.size && + cachedMetadata.mtimeMs === stat.mtimeMs + ) { + const cachedDecoration = this.state.fileDecorations.get(filePath) ?? createLineDecoration(cachedCount, stat.size > this.state.config.sizeLimit); + this.state.fileDecorations.set(filePath, cachedDecoration); + return cachedDecoration; + } + + this.state.fileMetadataCache.set(filePath, { + size: stat.size, + mtimeMs: stat.mtimeMs, + }); + + if (stat.size === 0) { + this.state.lineCountCache.set(filePath, 0); + const zeroDecoration = createLineDecoration(0); + this.state.fileDecorations.set(filePath, zeroDecoration); + return zeroDecoration; + } + + if (stat.size > this.state.config.sizeLimit) { + const estimatedLineCount = Math.floor(stat.size / this.state.config.estimationFactor); + this.state.lineCountCache.set(filePath, estimatedLineCount); + const estimatedDecoration = createLineDecoration(estimatedLineCount, true); + this.state.fileDecorations.set(filePath, estimatedDecoration); + return estimatedDecoration; + } + + let lineCountPromise = this.processingQueue.get(filePath); + + if (!lineCountPromise) { + lineCountPromise = this.limiter.run(() => countLines(filePath, this.state, stat)) + .then((lineCount) => { + this.processingQueue.delete(filePath); + return lineCount; + }) + .catch((error) => { + this.processingQueue.delete(filePath); + console.error(`LineSight: Failed to count lines for ${filePath}:`, error); + return 0; + }); + + this.processingQueue.set(filePath, lineCountPromise); + } + + const lineCount = await lineCountPromise; + + if (lineCount <= 0) { + this.state.fileDecorations.delete(filePath); + return undefined; + } + + // Guard against stale counts: if the file was invalidated while + // counting (e.g. by a file-watcher refresh), the metadata will + // have been cleared or updated. Drop the result so the next + // provideFileDecoration call re-counts from scratch. + const currentMeta = this.state.fileMetadataCache.get(filePath); + if (!currentMeta || currentMeta.size !== stat.size || currentMeta.mtimeMs !== stat.mtimeMs) { + return undefined; + } + + this.state.lineCountCache.set(filePath, lineCount); + const decoration = createLineDecoration(lineCount); + this.state.fileDecorations.set(filePath, decoration); + return decoration; + } catch { + return undefined; + } + } + + /** + * Queue a decoration refresh. Pass specific URIs for targeted updates, + * or call with no arguments to trigger a full (undefined) fire. + * Stale cache entries are purged immediately so the next provideFileDecoration re-counts. + */ + refresh(resources?: vscode.Uri | vscode.Uri[]): void { + if (resources === undefined) { + this.fullRefreshPending = true; + this.pendingUpdates.clear(); + } else if (Array.isArray(resources)) { + for (const uri of resources) { + this.pendingUpdates.add(uri.fsPath); + this.processingQueue.delete(uri.fsPath); + this.state.lineCountCache.delete(uri.fsPath); + this.state.fileDecorations.delete(uri.fsPath); + this.state.fileMetadataCache.delete(uri.fsPath); + } + } else { + this.pendingUpdates.add(resources.fsPath); + this.processingQueue.delete(resources.fsPath); + this.state.lineCountCache.delete(resources.fsPath); + this.state.fileDecorations.delete(resources.fsPath); + this.state.fileMetadataCache.delete(resources.fsPath); + } + + // If too many individual paths queue up, coalesce into a single full refresh. + if (!this.fullRefreshPending && this.pendingUpdates.size > PENDING_UPDATES_CAP) { + this.fullRefreshPending = true; + this.pendingUpdates.clear(); + } + + clearTrackedTimer(this.refreshTimer); + + const delayMs = this.state.isInitializing ? Math.max(100, this.state.config.debounceDelay) : this.state.config.debounceDelay; + this.refreshTimer = scheduleTimeout(() => { + this.flushUpdates(); + }, delayMs); + } + + /** + * Update decoration from an in-memory line count (e.g. editor buffer). + * Writes directly to caches and schedules a debounced notification + * without invalidating — avoids disk I/O for unsaved changes. + * The disk-based count is reconciled on the next save or watcher event. + */ + updateFromBuffer(uri: vscode.Uri, lineCount: number): void { + const filePath = uri.fsPath; + if (lineCount <= 0) { + return; + } + this.state.lineCountCache.set(filePath, lineCount); + const decoration = createLineDecoration(lineCount); + this.state.fileDecorations.set(filePath, decoration); + + this.pendingUpdates.add(filePath); + clearTrackedTimer(this.refreshTimer); + this.refreshTimer = scheduleTimeout(() => { + this.flushUpdates(); + }, this.state.config.debounceDelay); + } + + dispose(): void { + clearTrackedTimer(this.refreshTimer); + this.refreshTimer = undefined; + this.pendingUpdates.clear(); + this.processingQueue.clear(); + this._onDidChangeFileDecorations.dispose(); + } + + /** Emit queued decoration changes to VS Code in a single batch. */ + private flushUpdates(): void { + if (this.fullRefreshPending) { + this.fullRefreshPending = false; + this.pendingUpdates.clear(); + this._onDidChangeFileDecorations.fire(undefined); + return; + } + + if (this.pendingUpdates.size === 0) { + return; + } + + const updates: vscode.Uri[] = []; + this.pendingUpdates.forEach((fsPath) => { + updates.push(vscode.Uri.file(fsPath)); + }); + + this.pendingUpdates.clear(); + this._onDidChangeFileDecorations.fire(updates); + } +} diff --git a/src/extension.ts b/src/extension.ts index 0775973..a52d28d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,701 +1,69 @@ -import * as vscode from 'vscode'; -import * as fs from 'fs'; -import * as path from 'path'; - -interface FileCacheMetadata { - size: number; - mtimeMs: number; -} - -interface LineSightConfig { - sizeLimit: number; - batchSize: number; - debounceDelay: number; - initialScanDelay: number; - estimationFactor: number; - excludeFolders: string[]; - includeExtensions: Set; - includeFileNames: Set; - showStartupNotifications: boolean; -} - -// Store line counts to avoid recounting -const lineCountCache = new Map(); -// Store decorations to avoid recreating objects repeatedly -const fileDecorations = new Map(); -// Store stat metadata for cache invalidation -const fileMetadataCache = new Map(); - -let fileWatcher: vscode.Disposable | undefined; -let isInitializing = false; -let initializationRunId = 0; -let initializationPromise: Promise | undefined; - -// Track timers so they can be cleaned up on deactivate -const activeTimers = new Set(); - -// Queue state for watcher/document driven updates -const pendingWatcherUpdates = new Set(); -let watcherQueueTimer: NodeJS.Timeout | undefined; -let watcherQueueDelayMs = 0; - -const DEFAULT_EXCLUDED_FOLDERS = [ - 'node_modules', '.git', 'dist', 'build', 'out', 'bin', 'obj', - '.vscode', '.idea', '.vs', 'vendor', 'coverage', '.next', '.nuxt', - 'public/assets', 'static/assets', 'target', '.sass-cache', '.cache' -]; - -const BINARY_EXTENSIONS = new Set([ - '.exe', '.dll', '.obj', '.bin', '.jpg', '.jpeg', '.png', '.gif', - '.mp3', '.mp4', '.zip', '.gz', '.tar', '.pdf', '.class', '.pyc', - '.pyd', '.so', '.dylib', '.o', '.a', '.lib', '.woff', '.woff2', - '.ttf', '.eot', '.svg', '.ico', '.bmp', '.tiff', '.webp' -]); - -const DEFAULT_INCLUDED_EXTENSIONS = new Set([ - '.js', '.jsx', '.ts', '.tsx', '.html', '.css', '.scss', '.less', '.vue', '.svelte', - '.go', '.py', '.java', '.c', '.cpp', '.cc', '.cxx', '.h', '.hpp', '.cs', '.php', '.rb', - '.rs', '.kt', '.swift', '.sh', '.bash', '.zsh', '.sql', '.prisma', '.graphql', '.gql', - '.json', '.yaml', '.yml', '.xml', '.toml', '.ini', '.md', '.txt' -]); - -const DEFAULT_INCLUDED_FILE_NAMES = new Set([ - 'dockerfile', - 'makefile', - '.env', - '.gitignore', - '.gitattributes', - '.npmrc', - '.editorconfig' -]); - -const DEFAULT_CONFIG = { - sizeLimit: 5_000_000, - batchSize: 200, - debounceDelay: 300, - initialScanDelay: 2_000, - estimationFactor: 50, - showStartupNotifications: false, -}; - -let currentConfig: LineSightConfig = loadConfiguration(); - -function scheduleTimeout(callback: () => void, delayMs: number): NodeJS.Timeout { - const safeDelay = Math.max(0, delayMs); - const timer = setTimeout(() => { - activeTimers.delete(timer); - callback(); - }, safeDelay); - activeTimers.add(timer); - return timer; -} - -function clearTrackedTimer(timer: NodeJS.Timeout | undefined): void { - if (!timer) { - return; - } - clearTimeout(timer); - activeTimers.delete(timer); -} - -function wait(delayMs: number): Promise { - return new Promise((resolve) => { - scheduleTimeout(resolve, delayMs); - }); -} - -function clearAllTrackedTimers(): void { - for (const timer of activeTimers) { - clearTimeout(timer); - } - activeTimers.clear(); - watcherQueueTimer = undefined; -} - -function toPositiveInteger(value: number | undefined, fallback: number, minimum = 1): number { - if (typeof value !== 'number' || !Number.isFinite(value)) { - return fallback; - } - return Math.max(minimum, Math.floor(value)); -} - -function normalizeFolderPath(folder: string): string { - return folder.replace(/\\/g, '/').replace(/^\/+|\/+$/g, ''); -} +/** + * Extension entry point. + * + * This thin wrapper creates the shared `AppState`, wires the decoration + * provider and file watcher together, and registers VS Code event handlers. + * All real logic lives in the focused modules it imports. + */ -function normalizeExtension(ext: string): string | undefined { - const trimmed = ext.trim().toLowerCase(); - if (!trimmed || trimmed.includes('/') || trimmed.includes('\\')) { - return undefined; - } - if (trimmed.startsWith('.')) { - return trimmed; - } - if (/^[a-z0-9_+-]+$/i.test(trimmed)) { - return `.${trimmed}`; - } - return undefined; -} - -function loadConfiguration(): LineSightConfig { - const cfg = vscode.workspace.getConfiguration('linesight'); - - const configuredExcludes = cfg - .get('excludeFolders', []) - .map(normalizeFolderPath) - .filter(Boolean); - - const excludeFolders = Array.from(new Set([ - ...DEFAULT_EXCLUDED_FOLDERS.map(normalizeFolderPath), - ...configuredExcludes, - ])); +import * as vscode from 'vscode'; +import { createAppState, clearAllCaches, updateConfiguration } from './state'; +import { ConcurrencyLimiter } from './concurrency'; +import { LineCountDecorationProvider } from './decorationProvider'; +import { setupFileWatcher } from './fileWatcher'; +import { queueUpdate } from './fileWatcher'; +import { cancelInitialization, initializeDecorations } from './initialization'; +import { shouldSkipPath } from './fileFilter'; +import { clearTrackedTimer, clearAllTrackedTimers } from './timer'; + +/** Shared state lives at module scope so both activate() and deactivate() can reach it. */ +const state = createAppState(); - const configuredExtensions = cfg.get('includeExtensions', []); - const includeExtensions = new Set(); +export function activate(context: vscode.ExtensionContext): void { + updateConfiguration(state); - for (const value of configuredExtensions) { - const normalized = normalizeExtension(value); - if (normalized) { - includeExtensions.add(normalized); - } - } + // Cap concurrent file reads so we don't overwhelm the extension host. + const limiter = new ConcurrencyLimiter(20); + const provider = new LineCountDecorationProvider(state, limiter); - const effectiveExtensions = includeExtensions.size > 0 - ? includeExtensions - : new Set(DEFAULT_INCLUDED_EXTENSIONS); + context.subscriptions.push(provider); + context.subscriptions.push(vscode.window.registerFileDecorationProvider(provider)); - return { - sizeLimit: toPositiveInteger(cfg.get('sizeLimit'), DEFAULT_CONFIG.sizeLimit), - batchSize: toPositiveInteger(cfg.get('batchSize'), DEFAULT_CONFIG.batchSize), - debounceDelay: toPositiveInteger(cfg.get('debounceDelay'), DEFAULT_CONFIG.debounceDelay, 50), - initialScanDelay: toPositiveInteger(cfg.get('initialScanDelay'), DEFAULT_CONFIG.initialScanDelay, 0), - estimationFactor: toPositiveInteger(cfg.get('estimationFactor'), DEFAULT_CONFIG.estimationFactor), - excludeFolders, - includeExtensions: effectiveExtensions, - includeFileNames: new Set(DEFAULT_INCLUDED_FILE_NAMES), - showStartupNotifications: cfg.get('showStartupNotifications', DEFAULT_CONFIG.showStartupNotifications), + // Defer scanning/watching until the workspace is trusted. + const startScanning = () => { + setupFileWatcher(state, provider); + void initializeDecorations(state, provider); }; -} - -function updateConfiguration(): void { - currentConfig = loadConfiguration(); -} - -function clearAllCaches(): void { - lineCountCache.clear(); - fileDecorations.clear(); - fileMetadataCache.clear(); -} - -function shouldSkipPath(filePath: string): boolean { - const normalizedPath = `/${filePath.replace(/\\/g, '/')}`; - - for (const folder of currentConfig.excludeFolders) { - const normalizedFolder = normalizeFolderPath(folder); - if (!normalizedFolder) { - continue; - } - - const folderPattern = `/${normalizedFolder}/`; - const folderSuffix = `/${normalizedFolder}`; - - if (normalizedPath.includes(folderPattern) || normalizedPath.endsWith(folderSuffix)) { - return true; - } - } - const fileName = path.basename(filePath).toLowerCase(); - const ext = path.extname(fileName).toLowerCase(); - - if (ext && BINARY_EXTENSIONS.has(ext)) { - return true; - } - - if (!ext) { - return !currentConfig.includeFileNames.has(fileName); - } - - return !currentConfig.includeExtensions.has(ext); -} - -// More efficient line counting using read stream instead of loading entire file -async function countLinesWithReadStream(filePath: string): Promise { - return new Promise((resolve, reject) => { - const readStream = fs.createReadStream(filePath, { - encoding: 'utf8', - highWaterMark: 128 * 1024, - }); - - let lineCount = 0; - let sawAnyContent = false; - let lastCharWasNewline = true; - - readStream.on('data', (chunk: string) => { - if (chunk.length > 0) { - sawAnyContent = true; - } - - for (let i = 0; i < chunk.length; i++) { - if (chunk[i] === '\n') { - lineCount++; - lastCharWasNewline = true; - } else { - lastCharWasNewline = false; - } - } - }); - - readStream.on('end', () => { - if (sawAnyContent && !lastCharWasNewline) { - lineCount++; - } - resolve(lineCount); - }); - - readStream.on('error', (err) => { - reject(err); - }); - }); -} - -// Function to count lines in a file -async function countLines(filePath: string, stats?: fs.Stats): Promise { - try { - const fileStats = stats ?? await fs.promises.stat(filePath); - - if (!fileStats.isFile()) { - return 0; - } - - fileMetadataCache.set(filePath, { - size: fileStats.size, - mtimeMs: fileStats.mtimeMs, - }); - - const ext = path.extname(filePath).toLowerCase(); - if (BINARY_EXTENSIONS.has(ext)) { - return 0; - } - - if (fileStats.size > currentConfig.sizeLimit) { - return Math.floor(fileStats.size / currentConfig.estimationFactor); - } - - return await countLinesWithReadStream(filePath); - } catch (error) { - lineCountCache.delete(filePath); - fileDecorations.delete(filePath); - fileMetadataCache.delete(filePath); - console.error(`LineSight: Error counting lines for ${filePath}:`, error); - return 0; - } -} - -// Format line count for display -function formatLineCount(count: number): string { - if (count >= 1_000_000) { - return `${Math.floor(count / 1_000_000)}M`; - } - - if (count >= 1_000) { - return `${Math.floor(count / 1_000)}K`; - } - - return count.toString(); -} - -function createLineDecoration(lineCount: number, estimated = false): vscode.FileDecoration { - const formattedCount = formatLineCount(lineCount); - const badge = estimated ? `~${formattedCount}` : formattedCount; - const tooltip = estimated - ? `~${lineCount} lines (estimated)` - : `${lineCount} lines`; - - return new vscode.FileDecoration(badge, tooltip); -} - -function buildExcludeGlob(): string | undefined { - if (currentConfig.excludeFolders.length === 0) { - return undefined; - } - - const patterns = currentConfig.excludeFolders - .map((folder) => normalizeFolderPath(folder)) - .filter(Boolean) - .map((folder) => `**/${folder}/**`); - - if (patterns.length === 0) { - return undefined; - } - - return `{${patterns.join(',')}}`; -} - -function cancelInitialization(): void { - initializationRunId++; - isInitializing = false; - initializationPromise = undefined; -} - -async function processBatchesWithDelay( - files: vscode.Uri[], - provider: LineCountDecorationProvider, - batchSize: number, - delayMs: number, - runId: number, -): Promise { - for (let i = 0; i < files.length; i += batchSize) { - if (runId !== initializationRunId) { - return; - } - - const batch = files.slice(i, i + batchSize); - provider.refresh(batch); - - if (i > 0 && i % 1000 === 0) { - vscode.window.setStatusBarMessage(`LineSight: Processing files (${i}/${files.length})...`, 1500); - } - - await wait(delayMs); - } -} - -// Initialize decorations for all visible files -async function initializeDecorations( - provider: LineCountDecorationProvider, - options: { force?: boolean } = {}, -): Promise { - if (options.force) { - cancelInitialization(); - } else if (isInitializing) { - return initializationPromise ?? Promise.resolve(); - } - - const workspaceFolders = vscode.workspace.workspaceFolders; - if (!workspaceFolders || workspaceFolders.length === 0) { - return; - } - - const runId = ++initializationRunId; - isInitializing = true; - - if (currentConfig.showStartupNotifications) { - vscode.window.setStatusBarMessage('LineSight: Initializing line counts...', 1500); - } - - initializationPromise = (async () => { - try { - await wait(currentConfig.initialScanDelay); - - if (runId !== initializationRunId) { - return; - } - - const excludeGlob = buildExcludeGlob(); - - for (const folder of workspaceFolders) { - if (runId !== initializationRunId) { - return; - } - - const allFiles = await vscode.workspace.findFiles( - new vscode.RelativePattern(folder, '**/*'), - excludeGlob, - 6000, - ); - - const candidateFiles = allFiles.filter((uri) => - uri.scheme === 'file' && !shouldSkipPath(uri.fsPath) - ); - - await processBatchesWithDelay(candidateFiles, provider, currentConfig.batchSize, 60, runId); - } - - if (runId === initializationRunId) { - provider.refresh(); - vscode.window.setStatusBarMessage('LineSight: Ready', 1200); - } - } catch (error) { - console.error('LineSight: Initialization failed:', error); - } finally { - if (runId === initializationRunId) { - isInitializing = false; - initializationPromise = undefined; - } - } - })(); - - return initializationPromise; -} - -// Main file decorator provider -class LineCountDecorationProvider implements vscode.FileDecorationProvider, vscode.Disposable { - private _onDidChangeFileDecorations = new vscode.EventEmitter(); - readonly onDidChangeFileDecorations = this._onDidChangeFileDecorations.event; - - private processingQueue = new Map>(); - private pendingUpdates = new Set(); - private refreshTimer: NodeJS.Timeout | undefined; - private fullRefreshPending = false; - - async provideFileDecoration(uri: vscode.Uri): Promise { - try { - if (uri.scheme !== 'file') { - return undefined; - } - - const filePath = uri.fsPath; - - if (shouldSkipPath(filePath)) { - return undefined; - } - - const stat = await fs.promises.stat(filePath); - if (!stat.isFile()) { - return undefined; - } - - const cachedMetadata = fileMetadataCache.get(filePath); - const cachedCount = lineCountCache.get(filePath); - - if ( - cachedMetadata && - cachedCount !== undefined && - cachedMetadata.size === stat.size && - cachedMetadata.mtimeMs === stat.mtimeMs - ) { - const cachedDecoration = fileDecorations.get(filePath) ?? createLineDecoration(cachedCount, stat.size > currentConfig.sizeLimit); - fileDecorations.set(filePath, cachedDecoration); - return cachedDecoration; - } - - fileMetadataCache.set(filePath, { - size: stat.size, - mtimeMs: stat.mtimeMs, - }); - - if (stat.size === 0) { - lineCountCache.set(filePath, 0); - const zeroDecoration = createLineDecoration(0); - fileDecorations.set(filePath, zeroDecoration); - return zeroDecoration; - } - - if (stat.size > currentConfig.sizeLimit) { - const estimatedLineCount = Math.floor(stat.size / currentConfig.estimationFactor); - lineCountCache.set(filePath, estimatedLineCount); - const estimatedDecoration = createLineDecoration(estimatedLineCount, true); - fileDecorations.set(filePath, estimatedDecoration); - return estimatedDecoration; - } - - let lineCountPromise = this.processingQueue.get(filePath); - - if (!lineCountPromise) { - lineCountPromise = countLines(filePath, stat) - .then((lineCount) => { - this.processingQueue.delete(filePath); - return lineCount; - }) - .catch((error) => { - this.processingQueue.delete(filePath); - console.error(`LineSight: Failed to count lines for ${filePath}:`, error); - return 0; - }); - - this.processingQueue.set(filePath, lineCountPromise); - } - - const lineCount = await lineCountPromise; - - if (lineCount <= 0) { - fileDecorations.delete(filePath); - return undefined; - } - - lineCountCache.set(filePath, lineCount); - const decoration = createLineDecoration(lineCount); - fileDecorations.set(filePath, decoration); - return decoration; - } catch { - return undefined; - } - } - - refresh(resources?: vscode.Uri | vscode.Uri[]) { - if (resources === undefined) { - this.fullRefreshPending = true; - this.pendingUpdates.clear(); - } else if (Array.isArray(resources)) { - for (const uri of resources) { - this.pendingUpdates.add(uri.fsPath); - lineCountCache.delete(uri.fsPath); - fileDecorations.delete(uri.fsPath); - fileMetadataCache.delete(uri.fsPath); - } - } else { - this.pendingUpdates.add(resources.fsPath); - lineCountCache.delete(resources.fsPath); - fileDecorations.delete(resources.fsPath); - fileMetadataCache.delete(resources.fsPath); - } - - clearTrackedTimer(this.refreshTimer); - - const delayMs = isInitializing ? Math.max(100, currentConfig.debounceDelay) : currentConfig.debounceDelay; - this.refreshTimer = scheduleTimeout(() => { - this.flushUpdates(); - }, delayMs); - } - - dispose(): void { - clearTrackedTimer(this.refreshTimer); - this.refreshTimer = undefined; - this.pendingUpdates.clear(); - this.processingQueue.clear(); - this._onDidChangeFileDecorations.dispose(); - } - - private flushUpdates() { - if (this.fullRefreshPending) { - this.fullRefreshPending = false; - this._onDidChangeFileDecorations.fire(undefined); - return; - } - - if (this.pendingUpdates.size === 0) { - return; - } - - const updates: vscode.Uri[] = []; - this.pendingUpdates.forEach((fsPath) => { - updates.push(vscode.Uri.file(fsPath)); - }); - - this.pendingUpdates.clear(); - this._onDidChangeFileDecorations.fire(updates); - } -} - -function flushQueuedWatcherUpdates(provider: LineCountDecorationProvider): void { - clearTrackedTimer(watcherQueueTimer); - watcherQueueTimer = undefined; - watcherQueueDelayMs = 0; - - if (pendingWatcherUpdates.size === 0) { - return; - } - - const updates = Array.from(pendingWatcherUpdates).map((filePath) => vscode.Uri.file(filePath)); - pendingWatcherUpdates.clear(); - provider.refresh(updates); -} - -// Helper function to queue updates with debouncing without losing previous files -function queueUpdate( - uri: vscode.Uri, - provider: LineCountDecorationProvider, - delay: number = currentConfig.debounceDelay, -): void { - if (uri.scheme !== 'file') { - return; - } - - pendingWatcherUpdates.add(uri.fsPath); - - const normalizedDelay = Math.max(50, delay); - - if (!watcherQueueTimer) { - watcherQueueDelayMs = normalizedDelay; - watcherQueueTimer = scheduleTimeout(() => { - flushQueuedWatcherUpdates(provider); - }, watcherQueueDelayMs); - return; - } - - if (normalizedDelay < watcherQueueDelayMs) { - clearTrackedTimer(watcherQueueTimer); - watcherQueueDelayMs = normalizedDelay; - watcherQueueTimer = scheduleTimeout(() => { - flushQueuedWatcherUpdates(provider); - }, watcherQueueDelayMs); - } -} - -// Set up file system watcher to track changes -function setupFileWatcher(provider: LineCountDecorationProvider): void { - if (fileWatcher) { - fileWatcher.dispose(); - fileWatcher = undefined; - } - - const workspaceFolders = vscode.workspace.workspaceFolders; - if (!workspaceFolders || workspaceFolders.length === 0) { - return; - } - - const watchers: vscode.Disposable[] = []; - - for (const folder of workspaceFolders) { - const watcher = vscode.workspace.createFileSystemWatcher( - new vscode.RelativePattern(folder, '**/*'), - false, - false, - false, + if (vscode.workspace.isTrusted) { + startScanning(); + } else { + context.subscriptions.push( + vscode.workspace.onDidGrantWorkspaceTrust(() => { + startScanning(); + }), ); + } - watcher.onDidCreate((uri: vscode.Uri) => { - if (shouldSkipPath(uri.fsPath)) { - return; - } - queueUpdate(uri, provider); - }); - - watcher.onDidChange((uri: vscode.Uri) => { - if (shouldSkipPath(uri.fsPath)) { + context.subscriptions.push( + vscode.commands.registerCommand('linesight.refresh', async () => { + if (!vscode.workspace.isTrusted) { return; } - queueUpdate(uri, provider, currentConfig.debounceDelay); - }); - - watcher.onDidDelete((uri: vscode.Uri) => { - lineCountCache.delete(uri.fsPath); - fileDecorations.delete(uri.fsPath); - fileMetadataCache.delete(uri.fsPath); - provider.refresh(uri); - }); - - watchers.push(watcher); - } - - fileWatcher = vscode.Disposable.from(...watchers); -} - -export function activate(context: vscode.ExtensionContext): void { - updateConfiguration(); - - const provider = new LineCountDecorationProvider(); - context.subscriptions.push(provider); - context.subscriptions.push(vscode.window.registerFileDecorationProvider(provider)); - - setupFileWatcher(provider); - void initializeDecorations(provider); - - const refreshCommand = vscode.commands.registerCommand('linesight.refresh', async () => { - cancelInitialization(); - clearAllCaches(); - provider.refresh(); - await initializeDecorations(provider, { force: true }); - }); - - context.subscriptions.push(refreshCommand); + cancelInitialization(state); + clearAllCaches(state); + provider.refresh(); + await initializeDecorations(state, provider, { force: true }); + }), + ); context.subscriptions.push( vscode.workspace.onDidChangeWorkspaceFolders(() => { - cancelInitialization(); - clearAllCaches(); - setupFileWatcher(provider); - void initializeDecorations(provider, { force: true }); + if (!vscode.workspace.isTrusted) { return; } + cancelInitialization(state); + clearAllCaches(state); + setupFileWatcher(state, provider); + void initializeDecorations(state, provider, { force: true }); }), ); @@ -704,33 +72,35 @@ export function activate(context: vscode.ExtensionContext): void { if (!event.affectsConfiguration('linesight')) { return; } - - updateConfiguration(); - cancelInitialization(); - clearAllCaches(); - setupFileWatcher(provider); + updateConfiguration(state); + if (!vscode.workspace.isTrusted) { return; } + cancelInitialization(state); + clearAllCaches(state); + setupFileWatcher(state, provider); provider.refresh(); - void initializeDecorations(provider, { force: true }); + void initializeDecorations(state, provider, { force: true }); }), ); + // Live updates: edits use the in-memory buffer line count (no disk I/O). + // Saves and editor visibility changes queue disk-based refreshes. context.subscriptions.push( vscode.workspace.onDidChangeTextDocument((event) => { const uri = event.document.uri; - if (uri.scheme !== 'file' || shouldSkipPath(uri.fsPath)) { + if (uri.scheme !== 'file' || shouldSkipPath(uri.fsPath, state.config)) { return; } - queueUpdate(uri, provider, currentConfig.debounceDelay); + provider.updateFromBuffer(uri, event.document.lineCount); }), ); context.subscriptions.push( vscode.workspace.onDidSaveTextDocument((document) => { const uri = document.uri; - if (uri.scheme !== 'file' || shouldSkipPath(uri.fsPath)) { + if (uri.scheme !== 'file' || shouldSkipPath(uri.fsPath, state.config)) { return; } - queueUpdate(uri, provider, 75); + queueUpdate(uri, state, provider, 75); }), ); @@ -738,27 +108,27 @@ export function activate(context: vscode.ExtensionContext): void { vscode.window.onDidChangeVisibleTextEditors((editors) => { for (const editor of editors) { const uri = editor.document.uri; - if (uri.scheme !== 'file' || shouldSkipPath(uri.fsPath)) { + if (uri.scheme !== 'file' || shouldSkipPath(uri.fsPath, state.config)) { continue; } - queueUpdate(uri, provider, 100); + queueUpdate(uri, state, provider, 100); } }), ); } export function deactivate(): void { - cancelInitialization(); - clearAllCaches(); + cancelInitialization(state); + clearAllCaches(state); - pendingWatcherUpdates.clear(); - clearTrackedTimer(watcherQueueTimer); - watcherQueueTimer = undefined; + state.pendingWatcherUpdates.clear(); + clearTrackedTimer(state.watcherQueueTimer); + state.watcherQueueTimer = undefined; clearAllTrackedTimers(); - if (fileWatcher) { - fileWatcher.dispose(); - fileWatcher = undefined; + if (state.fileWatcher) { + state.fileWatcher.dispose(); + state.fileWatcher = undefined; } } diff --git a/src/fileFilter.ts b/src/fileFilter.ts new file mode 100644 index 0000000..ecb86f0 --- /dev/null +++ b/src/fileFilter.ts @@ -0,0 +1,65 @@ +/** + * Path-level filtering logic. + * + * Decides whether a given file should be ignored (excluded folder, binary + * extension, or extension not in the allow-list). All functions accept an + * explicit `LineSightConfig` so they stay pure and testable. + */ + +import * as path from 'path'; +import { LineSightConfig } from './types'; +import { BINARY_EXTENSIONS } from './constants'; +import { normalizeFolderPath } from './config'; + +/** + * Return true if the file should be skipped — i.e. it lives inside an + * excluded folder, has a binary extension, or isn't in the include list. + */ +export function shouldSkipPath(filePath: string, config: LineSightConfig): boolean { + const normalizedPath = `/${filePath.replace(/\\/g, '/')}`; + + for (const folder of config.excludeFolders) { + const normalizedFolder = normalizeFolderPath(folder); + if (!normalizedFolder) { + continue; + } + + const folderPattern = `/${normalizedFolder}/`; + const folderSuffix = `/${normalizedFolder}`; + + if (normalizedPath.includes(folderPattern) || normalizedPath.endsWith(folderSuffix)) { + return true; + } + } + + const fileName = path.basename(filePath).toLowerCase(); + const ext = path.extname(fileName).toLowerCase(); + + if (ext && BINARY_EXTENSIONS.has(ext)) { + return true; + } + + if (!ext) { + return !config.includeFileNames.has(fileName); + } + + return !config.includeExtensions.has(ext); +} + +/** Build a VS Code glob pattern that excludes all configured folders (for `workspace.findFiles`). */ +export function buildExcludeGlob(config: LineSightConfig): string | undefined { + if (config.excludeFolders.length === 0) { + return undefined; + } + + const patterns = config.excludeFolders + .map((folder) => normalizeFolderPath(folder)) + .filter(Boolean) + .map((folder) => `**/${folder}/**`); + + if (patterns.length === 0) { + return undefined; + } + + return `{${patterns.join(',')}}`; +} diff --git a/src/fileWatcher.ts b/src/fileWatcher.ts new file mode 100644 index 0000000..28b7e95 --- /dev/null +++ b/src/fileWatcher.ts @@ -0,0 +1,131 @@ +/** + * File system watcher integration. + * + * Watches every workspace folder for create / change / delete events and + * funnels them through a debounced queue so rapid-fire saves don't flood + * the decoration provider. If a shorter delay arrives while a timer is + * already running, the timer is restarted with the shorter delay. + */ + +import * as vscode from 'vscode'; +import { AppState } from './state'; +import { LineCountDecorationProvider } from './decorationProvider'; +import { shouldSkipPath } from './fileFilter'; +import { scheduleTimeout, clearTrackedTimer } from './timer'; + +/** Flush all pending watcher updates to the decoration provider immediately. */ +export function flushQueuedWatcherUpdates(state: AppState, provider: LineCountDecorationProvider): void { + clearTrackedTimer(state.watcherQueueTimer); + state.watcherQueueTimer = undefined; + state.watcherQueueDelayMs = 0; + + if (state.pendingWatcherUpdates.size === 0) { + return; + } + + const updates = Array.from(state.pendingWatcherUpdates).map((filePath) => vscode.Uri.file(filePath)); + state.pendingWatcherUpdates.clear(); + provider.refresh(updates); +} + +/** + * Add a file to the pending-update queue and (re)start the debounce timer. + * Multiple files accumulate in the queue and are flushed together, which + * avoids per-file decoration refreshes during burst edits. + */ +/** When more unique files queue up than this, stop tracking individuals and do a full refresh. */ +const WATCHER_QUEUE_CAP = 500; + +export function queueUpdate( + uri: vscode.Uri, + state: AppState, + provider: LineCountDecorationProvider, + delay?: number, +): void { + if (uri.scheme !== 'file') { + return; + } + + state.pendingWatcherUpdates.add(uri.fsPath); + + // If the queue grows too large, coalesce into a full refresh. + if (state.pendingWatcherUpdates.size > WATCHER_QUEUE_CAP) { + clearTrackedTimer(state.watcherQueueTimer); + state.watcherQueueTimer = undefined; + state.watcherQueueDelayMs = 0; + state.pendingWatcherUpdates.clear(); + provider.refresh(); + return; + } + + const normalizedDelay = Math.max(50, delay ?? state.config.debounceDelay); + + if (!state.watcherQueueTimer) { + state.watcherQueueDelayMs = normalizedDelay; + state.watcherQueueTimer = scheduleTimeout(() => { + flushQueuedWatcherUpdates(state, provider); + }, state.watcherQueueDelayMs); + return; + } + + if (normalizedDelay < state.watcherQueueDelayMs) { + clearTrackedTimer(state.watcherQueueTimer); + state.watcherQueueDelayMs = normalizedDelay; + state.watcherQueueTimer = scheduleTimeout(() => { + flushQueuedWatcherUpdates(state, provider); + }, state.watcherQueueDelayMs); + } +} + +/** + * Create file system watchers for every workspace folder. + * Disposes any previous watcher first so re-calling is safe after + * workspace-folder or configuration changes. + */ +export function setupFileWatcher(state: AppState, provider: LineCountDecorationProvider): void { + if (state.fileWatcher) { + state.fileWatcher.dispose(); + state.fileWatcher = undefined; + } + + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders || workspaceFolders.length === 0) { + return; + } + + const watchers: vscode.Disposable[] = []; + + for (const folder of workspaceFolders) { + const watcher = vscode.workspace.createFileSystemWatcher( + new vscode.RelativePattern(folder, '**/*'), + false, + false, + false, + ); + + watcher.onDidCreate((uri: vscode.Uri) => { + if (shouldSkipPath(uri.fsPath, state.config)) { + return; + } + queueUpdate(uri, state, provider); + }); + + watcher.onDidChange((uri: vscode.Uri) => { + if (shouldSkipPath(uri.fsPath, state.config)) { + return; + } + queueUpdate(uri, state, provider, state.config.debounceDelay); + }); + + watcher.onDidDelete((uri: vscode.Uri) => { + state.lineCountCache.delete(uri.fsPath); + state.fileDecorations.delete(uri.fsPath); + state.fileMetadataCache.delete(uri.fsPath); + provider.refresh(uri); + }); + + watchers.push(watcher); + } + + state.fileWatcher = vscode.Disposable.from(...watchers); +} diff --git a/src/initialization.ts b/src/initialization.ts new file mode 100644 index 0000000..6452987 --- /dev/null +++ b/src/initialization.ts @@ -0,0 +1,128 @@ +/** + * Workspace initialization — the initial scan that decorates every visible file. + * + * Uses a monotonically increasing `runId` to detect stale runs: if a new + * initialization is requested (e.g. after a config change) before the current + * one finishes, the old run notices the ID mismatch and bails out early. + * + * Files are processed in batches with short delays between them so the + * extension host stays responsive during large workspace scans. + */ + +import * as vscode from 'vscode'; +import { AppState } from './state'; +import { LineCountDecorationProvider } from './decorationProvider'; +import { shouldSkipPath } from './fileFilter'; +import { buildExcludeGlob } from './fileFilter'; +import { wait } from './timer'; + +/** Bump the run ID so any in-flight initialization loop will stop on its next iteration. */ +export function cancelInitialization(state: AppState): void { + state.initializationRunId++; + state.isInitializing = false; + state.initializationPromise = undefined; +} + +/** Send files to the provider in chunks, yielding between batches to keep the UI responsive. */ +async function processBatchesWithDelay( + files: vscode.Uri[], + provider: LineCountDecorationProvider, + state: AppState, + batchSize: number, + delayMs: number, + runId: number, +): Promise { + for (let i = 0; i < files.length; i += batchSize) { + if (runId !== state.initializationRunId) { + return; + } + + const batch = files.slice(i, i + batchSize); + provider.refresh(batch); + + if (i > 0 && i % 1000 === 0) { + vscode.window.setStatusBarMessage(`LineSight: Processing files (${i}/${files.length})...`, 1500); + } + + await wait(delayMs); + } +} + +/** + * Scan all workspace folders, filter to countable files, and feed them to the + * decoration provider in batches. Pass `{ force: true }` to cancel any + * running initialization first (used after config / folder changes). + */ +export async function initializeDecorations( + state: AppState, + provider: LineCountDecorationProvider, + options: { force?: boolean } = {}, +): Promise { + if (options.force) { + cancelInitialization(state); + } else if (state.isInitializing) { + return state.initializationPromise ?? Promise.resolve(); + } + + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders || workspaceFolders.length === 0) { + return; + } + + const runId = ++state.initializationRunId; + state.isInitializing = true; + + if (state.config.showStartupNotifications) { + vscode.window.setStatusBarMessage('LineSight: Initializing line counts...', 1500); + } + + state.initializationPromise = (async () => { + try { + await wait(state.config.initialScanDelay); + + if (runId !== state.initializationRunId) { + return; + } + + const excludeGlob = buildExcludeGlob(state.config); + + for (const folder of workspaceFolders) { + if (runId !== state.initializationRunId) { + return; + } + + const allFiles = await vscode.workspace.findFiles( + new vscode.RelativePattern(folder, '**/*'), + excludeGlob, + 6000, + ); + + if (allFiles.length >= 6000) { + console.warn( + `LineSight: File discovery capped at 6,000 in "${folder.name}". Some files may only be decorated when visible in the explorer.`, + ); + } + + const candidateFiles = allFiles.filter((uri) => + uri.scheme === 'file' && !shouldSkipPath(uri.fsPath, state.config) + ); + + await processBatchesWithDelay(candidateFiles, provider, state, state.config.batchSize, 60, runId); + } + + if (runId === state.initializationRunId) { + provider.refresh(); + vscode.window.setStatusBarMessage('LineSight: Ready', 1200); + } + } catch (error) { + console.error('LineSight: Initialization failed:', error); + } finally { + if (runId === state.initializationRunId) { + state.isInitializing = false; + state.initializationPromise = undefined; + } + } + })(); + + return state.initializationPromise; +} diff --git a/src/lineCounter.ts b/src/lineCounter.ts new file mode 100644 index 0000000..9929ca2 --- /dev/null +++ b/src/lineCounter.ts @@ -0,0 +1,144 @@ +/** + * Line counting and display formatting. + * + * `countLinesWithReadStream` streams a file in 128 KB chunks and counts + * newline characters. A configurable read timeout (default 10 s) destroys + * the stream if it stalls — this prevents the extension from hanging on + * network-mounted or locked files. + * + * `countLines` is the higher-level wrapper that checks caches, skips binaries, + * and falls back to byte-based estimation for oversized files. + */ + +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as path from 'path'; +import { BINARY_EXTENSIONS } from './constants'; +import { AppState } from './state'; + +/** Format a number for the explorer badge: 1234 -> "1K", 2500000 -> "2M". */ +export function formatLineCount(count: number): string { + if (count >= 1_000_000) { + return `${Math.floor(count / 1_000_000)}M`; + } + + if (count >= 1_000) { + return `${Math.floor(count / 1_000)}K`; + } + + return count.toString(); +} + +/** Create a VS Code FileDecoration badge + tooltip from a line count. */ +export function createLineDecoration(lineCount: number, estimated = false): vscode.FileDecoration { + const formattedCount = formatLineCount(lineCount); + const badge = estimated ? `~${formattedCount}` : formattedCount; + const tooltip = estimated + ? `~${lineCount} lines (estimated)` + : `${lineCount} lines`; + + return new vscode.FileDecoration(badge, tooltip); +} + +/** + * Count newline characters in a file using a read stream. + * + * A `settled` flag guards against double-resolution: whichever of end / + * error / timeout fires first wins, and the stream is destroyed on timeout + * so the file handle is released promptly. + */ +export async function countLinesWithReadStream(filePath: string, timeoutMs = 10000): Promise { + return new Promise((resolve, reject) => { + let settled = false; + + const readStream = fs.createReadStream(filePath, { + encoding: 'utf8', + highWaterMark: 128 * 1024, // 128 KB chunks balance syscall overhead vs memory + }); + + // Abort hung reads (e.g. network mounts, locked files) after timeoutMs. + const timer = setTimeout(() => { + if (!settled) { + settled = true; + readStream.destroy(); + reject(new Error(`Read timeout after ${timeoutMs}ms for ${filePath}`)); + } + }, timeoutMs); + + let lineCount = 0; + let sawAnyContent = false; + let lastCharWasNewline = true; + + readStream.on('data', (chunk: string | Buffer) => { + const data = typeof chunk === 'string' ? chunk : chunk.toString('utf8'); + if (data.length > 0) { + sawAnyContent = true; + } + + for (let i = 0; i < data.length; i++) { + if (data[i] === '\n') { + lineCount++; + lastCharWasNewline = true; + } else { + lastCharWasNewline = false; + } + } + }); + + readStream.on('end', () => { + if (!settled) { + settled = true; + clearTimeout(timer); + if (sawAnyContent && !lastCharWasNewline) { + lineCount++; + } + resolve(lineCount); + } + }); + + readStream.on('error', (err) => { + if (!settled) { + settled = true; + clearTimeout(timer); + reject(err); + } + }); + }); +} + +/** + * High-level line counter: checks file metadata, skips binaries, estimates + * oversized files, and falls back to a streaming count. On error the + * relevant caches are purged so the next request retries from scratch. + */ +export async function countLines(filePath: string, state: AppState, stats?: fs.Stats): Promise { + try { + const fileStats = stats ?? await fs.promises.stat(filePath); + + if (!fileStats.isFile()) { + return 0; + } + + state.fileMetadataCache.set(filePath, { + size: fileStats.size, + mtimeMs: fileStats.mtimeMs, + }); + + const ext = path.extname(filePath).toLowerCase(); + if (BINARY_EXTENSIONS.has(ext)) { + return 0; + } + + if (fileStats.size > state.config.sizeLimit) { + return Math.floor(fileStats.size / state.config.estimationFactor); + } + + return await countLinesWithReadStream(filePath); + } catch (error) { + state.lineCountCache.delete(filePath); + state.fileDecorations.delete(filePath); + state.fileMetadataCache.delete(filePath); + console.error(`LineSight: Error counting lines for ${filePath}:`, error); + return 0; + } +} diff --git a/src/state.ts b/src/state.ts new file mode 100644 index 0000000..f7a7b1d --- /dev/null +++ b/src/state.ts @@ -0,0 +1,54 @@ +/** + * Centralised mutable state for the extension. + * + * A single `AppState` is created at activation and threaded through every + * module. This avoids module-level globals, makes dependencies explicit, + * and keeps individual modules easy to test. + * + * All three caches use LRUCache (max 10 000 entries) to bound memory usage. + */ + +import * as vscode from 'vscode'; +import { FileCacheMetadata, LineSightConfig } from './types'; +import { LRUCache } from './cache'; +import { loadConfiguration } from './config'; + +export interface AppState { + lineCountCache: LRUCache; + fileDecorations: LRUCache; + fileMetadataCache: LRUCache; + config: LineSightConfig; + fileWatcher: vscode.Disposable | undefined; + isInitializing: boolean; + initializationRunId: number; + initializationPromise: Promise | undefined; + pendingWatcherUpdates: Set; + watcherQueueTimer: NodeJS.Timeout | undefined; + watcherQueueDelayMs: number; +} + +export function createAppState(): AppState { + return { + lineCountCache: new LRUCache(10000), + fileDecorations: new LRUCache(10000), + fileMetadataCache: new LRUCache(10000), + config: loadConfiguration(), + fileWatcher: undefined, + isInitializing: false, + initializationRunId: 0, + initializationPromise: undefined, + pendingWatcherUpdates: new Set(), + watcherQueueTimer: undefined, + watcherQueueDelayMs: 0, + }; +} + +export function clearAllCaches(state: AppState): void { + state.lineCountCache.clear(); + state.fileDecorations.clear(); + state.fileMetadataCache.clear(); +} + +export function updateConfiguration(state: AppState): void { + state.config = loadConfiguration(); +} diff --git a/src/test/cache.test.ts b/src/test/cache.test.ts new file mode 100644 index 0000000..cfe1eab --- /dev/null +++ b/src/test/cache.test.ts @@ -0,0 +1,112 @@ +import * as assert from 'assert'; +import { LRUCache } from '../cache'; + +describe('LRUCache', () => { + it('stores and retrieves values', () => { + const cache = new LRUCache(3); + cache.set('a', 1); + cache.set('b', 2); + assert.strictEqual(cache.get('a'), 1); + assert.strictEqual(cache.get('b'), 2); + assert.strictEqual(cache.size, 2); + }); + + it('returns undefined for missing keys', () => { + const cache = new LRUCache(3); + assert.strictEqual(cache.get('missing'), undefined); + }); + + it('evicts oldest entry when at capacity', () => { + const cache = new LRUCache(3); + cache.set('a', 1); + cache.set('b', 2); + cache.set('c', 3); + cache.set('d', 4); // should evict 'a' + assert.strictEqual(cache.get('a'), undefined); + assert.strictEqual(cache.get('b'), 2); + assert.strictEqual(cache.get('d'), 4); + assert.strictEqual(cache.size, 3); + }); + + it('get() promotes entry to MRU position', () => { + const cache = new LRUCache(3); + cache.set('a', 1); + cache.set('b', 2); + cache.set('c', 3); + cache.get('a'); // promote 'a' — now 'b' is oldest + cache.set('d', 4); // should evict 'b' + assert.strictEqual(cache.get('a'), 1); + assert.strictEqual(cache.get('b'), undefined); + assert.strictEqual(cache.get('c'), 3); + assert.strictEqual(cache.get('d'), 4); + }); + + it('has() does not promote entry (peek)', () => { + const cache = new LRUCache(3); + cache.set('a', 1); + cache.set('b', 2); + cache.set('c', 3); + assert.strictEqual(cache.has('a'), true); // peek — does NOT promote 'a' + cache.set('d', 4); // should evict 'a' (still oldest) + assert.strictEqual(cache.has('a'), false); + assert.strictEqual(cache.get('a'), undefined); + }); + + it('set() overwrites existing key without growing', () => { + const cache = new LRUCache(3); + cache.set('a', 1); + cache.set('b', 2); + cache.set('c', 3); + cache.set('a', 10); // overwrite — should not evict, size stays 3 + assert.strictEqual(cache.get('a'), 10); + assert.strictEqual(cache.size, 3); + }); + + it('overwrite moves entry to MRU position', () => { + const cache = new LRUCache(3); + cache.set('a', 1); + cache.set('b', 2); + cache.set('c', 3); + cache.set('a', 10); // 'a' moves to MRU — 'b' is now oldest + cache.set('d', 4); // should evict 'b' + assert.strictEqual(cache.get('b'), undefined); + assert.strictEqual(cache.get('a'), 10); + assert.strictEqual(cache.size, 3); + }); + + it('delete() removes entry and reduces size', () => { + const cache = new LRUCache(3); + cache.set('a', 1); + cache.set('b', 2); + assert.strictEqual(cache.delete('a'), true); + assert.strictEqual(cache.get('a'), undefined); + assert.strictEqual(cache.size, 1); + assert.strictEqual(cache.delete('missing'), false); + }); + + it('clear() empties the cache', () => { + const cache = new LRUCache(3); + cache.set('a', 1); + cache.set('b', 2); + cache.clear(); + assert.strictEqual(cache.size, 0); + assert.strictEqual(cache.get('a'), undefined); + }); + + it('handles maxSize of 1', () => { + const cache = new LRUCache(1); + cache.set('a', 1); + assert.strictEqual(cache.get('a'), 1); + cache.set('b', 2); // evicts 'a' + assert.strictEqual(cache.get('a'), undefined); + assert.strictEqual(cache.get('b'), 2); + assert.strictEqual(cache.size, 1); + }); + + it('enforces minimum maxSize of 1', () => { + const cache = new LRUCache(0); + cache.set('a', 1); + assert.strictEqual(cache.size, 1); + assert.strictEqual(cache.get('a'), 1); + }); +}); diff --git a/src/test/concurrency.test.ts b/src/test/concurrency.test.ts new file mode 100644 index 0000000..cde48e7 --- /dev/null +++ b/src/test/concurrency.test.ts @@ -0,0 +1,112 @@ +import * as assert from 'assert'; +import { ConcurrencyLimiter } from '../concurrency'; + +function delay(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +describe('ConcurrencyLimiter', () => { + it('runs tasks immediately when under limit', async () => { + const limiter = new ConcurrencyLimiter(3); + const result = await limiter.run(() => Promise.resolve(42)); + assert.strictEqual(result, 42); + }); + + it('limits concurrent tasks', async () => { + const limiter = new ConcurrencyLimiter(2); + let maxConcurrent = 0; + let current = 0; + + const task = async () => { + current++; + if (current > maxConcurrent) { + maxConcurrent = current; + } + await delay(50); + current--; + }; + + await Promise.all([ + limiter.run(task), + limiter.run(task), + limiter.run(task), + limiter.run(task), + ]); + + assert.strictEqual(maxConcurrent, 2); + assert.strictEqual(limiter.activeCount, 0); + assert.strictEqual(limiter.pendingCount, 0); + }); + + it('drains the queue in order', async () => { + const limiter = new ConcurrencyLimiter(1); + const order: number[] = []; + + const makeTask = (n: number) => async () => { + order.push(n); + await delay(10); + }; + + await Promise.all([ + limiter.run(makeTask(1)), + limiter.run(makeTask(2)), + limiter.run(makeTask(3)), + ]); + + assert.deepStrictEqual(order, [1, 2, 3]); + }); + + it('propagates errors without blocking the queue', async () => { + const limiter = new ConcurrencyLimiter(1); + + const failingTask = () => Promise.reject(new Error('boom')); + const okTask = () => Promise.resolve('ok'); + + const results = await Promise.allSettled([ + limiter.run(failingTask), + limiter.run(okTask), + ]); + + assert.strictEqual(results[0].status, 'rejected'); + assert.strictEqual((results[1] as PromiseFulfilledResult).value, 'ok'); + assert.strictEqual(limiter.activeCount, 0); + assert.strictEqual(limiter.pendingCount, 0); + }); + + it('handles synchronously throwing tasks without deadlocking', async () => { + const limiter = new ConcurrencyLimiter(1); + + const syncThrow = (): Promise => { throw new Error('sync boom'); }; + const okTask = () => Promise.resolve('ok'); + + const results = await Promise.allSettled([ + limiter.run(syncThrow), + limiter.run(okTask), + ]); + + assert.strictEqual(results[0].status, 'rejected'); + assert.strictEqual((results[1] as PromiseFulfilledResult).value, 'ok'); + assert.strictEqual(limiter.activeCount, 0); + assert.strictEqual(limiter.pendingCount, 0); + }); + + it('reports activeCount and pendingCount', async () => { + const limiter = new ConcurrencyLimiter(1); + + let resolveFirst!: () => void; + const firstTask = () => new Promise((r) => { resolveFirst = r; }); + + const p1 = limiter.run(firstTask); + const p2 = limiter.run(() => Promise.resolve()); + + assert.strictEqual(limiter.activeCount, 1); + assert.strictEqual(limiter.pendingCount, 1); + + resolveFirst(); + await p1; + await p2; + + assert.strictEqual(limiter.activeCount, 0); + assert.strictEqual(limiter.pendingCount, 0); + }); +}); diff --git a/src/test/config.test.ts b/src/test/config.test.ts new file mode 100644 index 0000000..e1837bd --- /dev/null +++ b/src/test/config.test.ts @@ -0,0 +1,98 @@ +import * as assert from 'assert'; +import { toPositiveInteger, normalizeFolderPath, normalizeExtension } from '../config'; + +describe('toPositiveInteger', () => { + it('returns the value when valid', () => { + assert.strictEqual(toPositiveInteger(10, 5), 10); + }); + + it('returns fallback for undefined', () => { + assert.strictEqual(toPositiveInteger(undefined, 5), 5); + }); + + it('returns fallback for NaN', () => { + assert.strictEqual(toPositiveInteger(NaN, 5), 5); + }); + + it('returns fallback for Infinity', () => { + assert.strictEqual(toPositiveInteger(Infinity, 5), 5); + }); + + it('floors fractional values', () => { + assert.strictEqual(toPositiveInteger(3.9, 5), 3); + }); + + it('clamps to minimum', () => { + assert.strictEqual(toPositiveInteger(0, 5), 1); + assert.strictEqual(toPositiveInteger(-10, 5), 1); + }); + + it('uses custom minimum', () => { + assert.strictEqual(toPositiveInteger(30, 100, 50), 50); + assert.strictEqual(toPositiveInteger(100, 100, 50), 100); + }); +}); + +describe('normalizeFolderPath', () => { + it('normalizes backslashes to forward slashes', () => { + assert.strictEqual(normalizeFolderPath('foo\\bar\\baz'), 'foo/bar/baz'); + }); + + it('strips leading and trailing slashes', () => { + assert.strictEqual(normalizeFolderPath('/foo/bar/'), 'foo/bar'); + assert.strictEqual(normalizeFolderPath('///foo///'), 'foo'); + }); + + it('handles simple folder names', () => { + assert.strictEqual(normalizeFolderPath('node_modules'), 'node_modules'); + }); + + it('returns empty string for root-like input', () => { + assert.strictEqual(normalizeFolderPath('/'), ''); + assert.strictEqual(normalizeFolderPath(''), ''); + }); + + it('accepts folder names containing spaces', () => { + assert.strictEqual(normalizeFolderPath('Generated Files'), 'Generated Files'); + }); + + it('accepts paths with spaces in segments', () => { + assert.strictEqual(normalizeFolderPath('My Project/Generated Files'), 'My Project/Generated Files'); + }); + + it('rejects glob metacharacters', () => { + assert.strictEqual(normalizeFolderPath('foo*'), ''); + assert.strictEqual(normalizeFolderPath('foo?'), ''); + assert.strictEqual(normalizeFolderPath('{foo}'), ''); + assert.strictEqual(normalizeFolderPath('[foo]'), ''); + }); +}); + +describe('normalizeExtension', () => { + it('returns dotted extension as-is', () => { + assert.strictEqual(normalizeExtension('.ts'), '.ts'); + }); + + it('adds dot for bare extension', () => { + assert.strictEqual(normalizeExtension('ts'), '.ts'); + }); + + it('lowercases the extension', () => { + assert.strictEqual(normalizeExtension('.TS'), '.ts'); + assert.strictEqual(normalizeExtension('PY'), '.py'); + }); + + it('trims whitespace', () => { + assert.strictEqual(normalizeExtension(' .js '), '.js'); + }); + + it('rejects empty strings', () => { + assert.strictEqual(normalizeExtension(''), undefined); + assert.strictEqual(normalizeExtension(' '), undefined); + }); + + it('rejects paths with slashes', () => { + assert.strictEqual(normalizeExtension('foo/bar'), undefined); + assert.strictEqual(normalizeExtension('foo\\bar'), undefined); + }); +}); diff --git a/src/test/extension.test.ts b/src/test/extension.test.ts new file mode 100644 index 0000000..46bd7c7 --- /dev/null +++ b/src/test/extension.test.ts @@ -0,0 +1,77 @@ +import * as assert from 'assert'; + +/* eslint-disable @typescript-eslint/no-require-imports */ +// Dynamic require is necessary here: the vscode mock must be resolved at +// runtime through the setup.ts module-intercept, and the extension module +// must be loaded/unloaded per suite to reset module-scoped state. + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const vscode = require('vscode'); + +describe('extension – workspace trust', function () { + // Allow time for the async initialization path in the trusted test. + this.timeout(5000); + + let deactivate: () => void; + + before(() => { + vscode.__test.reset(); + vscode.workspace.isTrusted = false; + vscode.__test.findFilesCalled = false; + + // Require extension module (creates module-scoped state). + const ext = require('../extension'); + deactivate = ext.deactivate; + + // Activate in untrusted mode — startScanning should be skipped. + ext.activate(vscode.__test.createMockContext()); + }); + + after(() => { + deactivate(); + vscode.__test.reset(); + + // Purge compiled extension modules so other suites get a fresh state. + for (const key of Object.keys(require.cache)) { + if (key.includes('/out/') && !key.includes('/out/test/')) { + delete require.cache[key]; + } + } + }); + + it('does not scan workspace during activation when untrusted', () => { + assert.strictEqual(vscode.__test.findFilesCalled, false); + }); + + it('refresh command short-circuits in untrusted workspace', async () => { + vscode.__test.findFilesCalled = false; + vscode.workspace.isTrusted = false; + + const handler = vscode.__test.commandHandlers.get('linesight.refresh'); + assert.ok(handler, 'linesight.refresh command should be registered'); + + await handler(); + + assert.strictEqual( + vscode.__test.findFilesCalled, + false, + 'refresh must not trigger findFiles in an untrusted workspace', + ); + }); + + it('refresh command proceeds in trusted workspace', async () => { + vscode.__test.findFilesCalled = false; + vscode.workspace.isTrusted = true; + + const handler = vscode.__test.commandHandlers.get('linesight.refresh'); + assert.ok(handler); + + await handler(); + + assert.strictEqual( + vscode.__test.findFilesCalled, + true, + 'refresh should call findFiles when workspace is trusted', + ); + }); +}); diff --git a/src/test/fileFilter.test.ts b/src/test/fileFilter.test.ts new file mode 100644 index 0000000..23726d0 --- /dev/null +++ b/src/test/fileFilter.test.ts @@ -0,0 +1,84 @@ +import * as assert from 'assert'; +import { shouldSkipPath, buildExcludeGlob } from '../fileFilter'; +import { LineSightConfig } from '../types'; + +function makeConfig(overrides: Partial = {}): LineSightConfig { + return { + sizeLimit: 5_000_000, + batchSize: 200, + debounceDelay: 300, + initialScanDelay: 2000, + estimationFactor: 50, + excludeFolders: ['node_modules', '.git'], + includeExtensions: new Set(['.ts', '.js', '.py']), + includeFileNames: new Set(['dockerfile', 'makefile']), + showStartupNotifications: false, + ...overrides, + }; +} + +describe('shouldSkipPath', () => { + it('skips files in excluded folders', () => { + const config = makeConfig(); + assert.strictEqual(shouldSkipPath('/project/node_modules/foo.ts', config), true); + assert.strictEqual(shouldSkipPath('/project/.git/config', config), true); + }); + + it('does not skip files in non-excluded folders', () => { + const config = makeConfig(); + assert.strictEqual(shouldSkipPath('/project/src/index.ts', config), false); + }); + + it('skips binary extensions', () => { + const config = makeConfig(); + assert.strictEqual(shouldSkipPath('/project/image.png', config), true); + assert.strictEqual(shouldSkipPath('/project/file.exe', config), true); + }); + + it('skips files with non-included extensions', () => { + const config = makeConfig(); + assert.strictEqual(shouldSkipPath('/project/data.csv', config), true); + }); + + it('includes files with included extensions', () => { + const config = makeConfig(); + assert.strictEqual(shouldSkipPath('/project/main.py', config), false); + assert.strictEqual(shouldSkipPath('/project/index.js', config), false); + }); + + it('includes known extensionless file names', () => { + const config = makeConfig(); + assert.strictEqual(shouldSkipPath('/project/Dockerfile', config), false); + assert.strictEqual(shouldSkipPath('/project/Makefile', config), false); + }); + + it('skips unknown extensionless files', () => { + const config = makeConfig(); + assert.strictEqual(shouldSkipPath('/project/LICENSE', config), true); + }); + + it('handles Windows-style paths', () => { + const config = makeConfig(); + assert.strictEqual(shouldSkipPath('C:\\project\\node_modules\\foo.ts', config), true); + assert.strictEqual(shouldSkipPath('C:\\project\\src\\index.ts', config), false); + }); +}); + +describe('buildExcludeGlob', () => { + it('returns undefined for empty excludeFolders', () => { + const config = makeConfig({ excludeFolders: [] }); + assert.strictEqual(buildExcludeGlob(config), undefined); + }); + + it('builds glob from excludeFolders', () => { + const config = makeConfig({ excludeFolders: ['node_modules', 'dist'] }); + const result = buildExcludeGlob(config); + assert.strictEqual(result, '{**/node_modules/**,**/dist/**}'); + }); + + it('normalizes folder paths in glob', () => { + const config = makeConfig({ excludeFolders: ['/foo/bar/'] }); + const result = buildExcludeGlob(config); + assert.strictEqual(result, '{**/foo/bar/**}'); + }); +}); diff --git a/src/test/lineCounter.test.ts b/src/test/lineCounter.test.ts new file mode 100644 index 0000000..84139cd --- /dev/null +++ b/src/test/lineCounter.test.ts @@ -0,0 +1,75 @@ +import * as assert from 'assert'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import { formatLineCount, countLinesWithReadStream } from '../lineCounter'; + +describe('formatLineCount', () => { + it('formats millions', () => { + assert.strictEqual(formatLineCount(1_000_000), '1M'); + assert.strictEqual(formatLineCount(2_500_000), '2M'); + }); + + it('formats thousands', () => { + assert.strictEqual(formatLineCount(1_000), '1K'); + assert.strictEqual(formatLineCount(9_999), '9K'); + }); + + it('returns plain number under 1000', () => { + assert.strictEqual(formatLineCount(0), '0'); + assert.strictEqual(formatLineCount(1), '1'); + assert.strictEqual(formatLineCount(999), '999'); + }); +}); + +describe('countLinesWithReadStream', () => { + let tmpDir: string; + + beforeEach(() => { + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'linesight-test-')); + }); + + afterEach(() => { + fs.rmSync(tmpDir, { recursive: true, force: true }); + }); + + it('counts lines in a simple file', async () => { + const filePath = path.join(tmpDir, 'simple.txt'); + fs.writeFileSync(filePath, 'line1\nline2\nline3\n'); + const count = await countLinesWithReadStream(filePath); + assert.strictEqual(count, 3); + }); + + it('counts last line without trailing newline', async () => { + const filePath = path.join(tmpDir, 'no-trailing.txt'); + fs.writeFileSync(filePath, 'line1\nline2'); + const count = await countLinesWithReadStream(filePath); + assert.strictEqual(count, 2); + }); + + it('returns 0 for an empty file', async () => { + const filePath = path.join(tmpDir, 'empty.txt'); + fs.writeFileSync(filePath, ''); + const count = await countLinesWithReadStream(filePath); + assert.strictEqual(count, 0); + }); + + it('counts a single line without newline', async () => { + const filePath = path.join(tmpDir, 'single.txt'); + fs.writeFileSync(filePath, 'hello'); + const count = await countLinesWithReadStream(filePath); + assert.strictEqual(count, 1); + }); + + it('counts a single line with trailing newline', async () => { + const filePath = path.join(tmpDir, 'single-nl.txt'); + fs.writeFileSync(filePath, 'hello\n'); + const count = await countLinesWithReadStream(filePath); + assert.strictEqual(count, 1); + }); + + it('rejects on missing file', async () => { + const filePath = path.join(tmpDir, 'nonexistent.txt'); + await assert.rejects(() => countLinesWithReadStream(filePath)); + }); +}); diff --git a/src/test/setup.ts b/src/test/setup.ts new file mode 100644 index 0000000..b26ae49 --- /dev/null +++ b/src/test/setup.ts @@ -0,0 +1,17 @@ +// Register the vscode mock before any test modules load. +// Mocha's --require runs this before importing test files, +// so `require('vscode')` in production code resolves to our stub. + +import * as Module from 'module'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const M = Module as any; +const originalResolveFilename = M._resolveFilename; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +M._resolveFilename = function (request: string, ...args: any[]) { + if (request === 'vscode') { + return require.resolve('./vscode-mock'); + } + return originalResolveFilename.call(this, request, ...args); +}; diff --git a/src/test/timer.test.ts b/src/test/timer.test.ts new file mode 100644 index 0000000..2afd413 --- /dev/null +++ b/src/test/timer.test.ts @@ -0,0 +1,46 @@ +import * as assert from 'assert'; +import { scheduleTimeout, clearTrackedTimer, wait } from '../timer'; + +describe('scheduleTimeout', () => { + it('calls the callback after the delay', (done) => { + const start = Date.now(); + scheduleTimeout(() => { + const elapsed = Date.now() - start; + assert.ok(elapsed >= 40, `expected at least 40ms, got ${elapsed}ms`); + done(); + }, 50); + }); + + it('clamps negative delay to 0', (done) => { + scheduleTimeout(() => { + done(); + }, -100); + }); +}); + +describe('clearTrackedTimer', () => { + it('prevents callback from firing', (done) => { + let called = false; + const timer = scheduleTimeout(() => { + called = true; + }, 50); + clearTrackedTimer(timer); + setTimeout(() => { + assert.strictEqual(called, false); + done(); + }, 100); + }); + + it('handles undefined gracefully', () => { + clearTrackedTimer(undefined); // should not throw + }); +}); + +describe('wait', () => { + it('resolves after the specified delay', async () => { + const start = Date.now(); + await wait(50); + const elapsed = Date.now() - start; + assert.ok(elapsed >= 40, `expected at least 40ms, got ${elapsed}ms`); + }); +}); diff --git a/src/test/vscode-mock.ts b/src/test/vscode-mock.ts new file mode 100644 index 0000000..36a4f79 --- /dev/null +++ b/src/test/vscode-mock.ts @@ -0,0 +1,129 @@ +// Minimal vscode mock for unit tests that run outside the VS Code extension host. +// Only the APIs actually touched by production modules are stubbed here. +// Test-specific helpers live under the `__test` namespace. + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AnyFn = (...args: any[]) => any; + +const noopDisposable = { dispose: () => {} }; +const noopEvent = () => noopDisposable; + +// ─── Command tracking ─────────────────────────────────────────────── +const commandHandlers = new Map(); + +// ─── findFiles tracking ───────────────────────────────────────────── +let findFilesCalled = false; + +// ─── Config overrides (keep tests fast) ───────────────────────────── +const configOverrides: Record = { + initialScanDelay: 0, + debounceDelay: 50, +}; + +// ─── Core API stubs ───────────────────────────────────────────────── +const workspace: Record = { + isTrusted: true, + workspaceFolders: [{ uri: { fsPath: '/mock-workspace' }, name: 'mock', index: 0 }], + getConfiguration: () => ({ + get: (_key: string, defaultValue?: T): T | undefined => { + if (_key in configOverrides) { + return configOverrides[_key] as T; + } + return defaultValue; + }, + }), + findFiles: async () => { + findFilesCalled = true; + return []; + }, + createFileSystemWatcher: () => ({ + onDidCreate: noopEvent, + onDidChange: noopEvent, + onDidDelete: noopEvent, + dispose: () => {}, + }), + onDidGrantWorkspaceTrust: noopEvent, + onDidChangeWorkspaceFolders: noopEvent, + onDidChangeConfiguration: noopEvent, + onDidChangeTextDocument: noopEvent, + onDidSaveTextDocument: noopEvent, +}; + +const vscodeWindow = { + registerFileDecorationProvider: () => noopDisposable, + onDidChangeVisibleTextEditors: noopEvent, + setStatusBarMessage: () => noopDisposable, +}; + +const commands = { + registerCommand: (id: string, handler: AnyFn) => { + commandHandlers.set(id, handler); + return noopDisposable; + }, +}; + +// ─── VS Code classes ──────────────────────────────────────────────── +class EventEmitter { + event = noopEvent; + fire() {} + dispose() {} +} + +class FileDecoration { + badge?: string; + tooltip?: string; + constructor(badge?: string, tooltip?: string) { + this.badge = badge; + this.tooltip = tooltip; + } +} + +class Disposable { + static from(...disposables: { dispose: () => void }[]) { + return { dispose: () => disposables.forEach((d) => d.dispose()) }; + } + dispose() {} +} + +class Uri { + scheme: string; + fsPath: string; + private constructor(scheme: string, fsPath: string) { + this.scheme = scheme; + this.fsPath = fsPath; + } + static file(path: string): Uri { + return new Uri('file', path); + } +} + +class RelativePattern { + constructor(public base: unknown, public pattern: string) {} +} + +// ─── Test helpers ─────────────────────────────────────────────────── +const __test = { + commandHandlers, + get findFilesCalled() { return findFilesCalled; }, + set findFilesCalled(v: boolean) { findFilesCalled = v; }, + reset() { + workspace.isTrusted = true; + commandHandlers.clear(); + findFilesCalled = false; + }, + createMockContext() { + return { subscriptions: [] as { dispose: () => void }[] }; + }, +}; + +module.exports = { + workspace, + window: vscodeWindow, + commands, + EventEmitter, + FileDecoration, + Disposable, + Uri, + RelativePattern, + __test, +}; diff --git a/src/timer.ts b/src/timer.ts new file mode 100644 index 0000000..a5e19d6 --- /dev/null +++ b/src/timer.ts @@ -0,0 +1,44 @@ +/** + * Centralized timer management. + * + * Every setTimeout created through this module is tracked in `activeTimers` + * so the extension can cancel all pending timers on deactivation and avoid + * firing callbacks after the extension host has torn down. + */ + +const activeTimers = new Set(); + +/** Schedule a tracked timeout. Negative delays are clamped to 0. */ +export function scheduleTimeout(callback: () => void, delayMs: number): NodeJS.Timeout { + const safeDelay = Math.max(0, delayMs); + const timer = setTimeout(() => { + activeTimers.delete(timer); + callback(); + }, safeDelay); + activeTimers.add(timer); + return timer; +} + +/** Cancel a single tracked timer (safe to call with undefined). */ +export function clearTrackedTimer(timer: NodeJS.Timeout | undefined): void { + if (!timer) { + return; + } + clearTimeout(timer); + activeTimers.delete(timer); +} + +/** Promise-based delay that is also tracked for cleanup. */ +export function wait(delayMs: number): Promise { + return new Promise((resolve) => { + scheduleTimeout(resolve, delayMs); + }); +} + +/** Cancel every outstanding tracked timer — called during deactivation. */ +export function clearAllTrackedTimers(): void { + for (const timer of activeTimers) { + clearTimeout(timer); + } + activeTimers.clear(); +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..f1e2e44 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,33 @@ +/** + * Snapshot of a file's stat metadata used for cache invalidation. + * When size or mtime changes the cached line count is considered stale. + */ +export interface FileCacheMetadata { + size: number; + mtimeMs: number; +} + +/** + * Resolved user + default configuration for a LineSight session. + * Built once at activation and rebuilt whenever VS Code settings change. + */ +export interface LineSightConfig { + /** Files larger than this (bytes) use byte-based estimation instead of counting. */ + sizeLimit: number; + /** Number of files sent to the decoration provider per initialization batch. */ + batchSize: number; + /** Milliseconds to wait before flushing debounced file-change events. */ + debounceDelay: number; + /** Milliseconds to wait after activation before starting the initial workspace scan. */ + initialScanDelay: number; + /** Assumed average bytes-per-line, used to estimate counts for oversized files. */ + estimationFactor: number; + /** Folder paths (normalized, no leading/trailing slashes) to skip during scanning. */ + excludeFolders: string[]; + /** Allowed file extensions (lowercased, dot-prefixed). */ + includeExtensions: Set; + /** Extensionless filenames (lowercased) that should still be counted (e.g. "dockerfile"). */ + includeFileNames: Set; + /** Whether to show status-bar messages during initialization. */ + showStartupNotifications: boolean; +} From 16fbbe64830b2833b71e7d8db97b41927aae4ec1 Mon Sep 17 00:00:00 2001 From: Niraj Kumar Date: Sun, 8 Feb 2026 03:18:58 +0530 Subject: [PATCH 3/5] feat: Expand default file extensions and introduce 'H' suffix for line counts in the hundreds, along with updated tests and documentation. --- src/constants.ts | 26 +++++++++++++++++++++++--- src/lineCounter.ts | 16 +++++++++++++++- src/test/lineCounter.test.ts | 12 ++++++++++-- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index a18f73e..0239dd5 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -15,10 +15,30 @@ export const BINARY_EXTENSIONS = new Set([ /** Source / text extensions counted when the user hasn't overridden `includeExtensions`. */ export const DEFAULT_INCLUDED_EXTENSIONS = new Set([ - '.js', '.jsx', '.ts', '.tsx', '.html', '.css', '.scss', '.less', '.vue', '.svelte', + // Web + '.js', '.jsx', '.ts', '.tsx', '.html', '.css', '.scss', '.sass', '.less', + '.vue', '.svelte', '.astro', '.mdx', + // Templates + '.hbs', '.ejs', '.pug', + // Systems / compiled '.go', '.py', '.java', '.c', '.cpp', '.cc', '.cxx', '.h', '.hpp', '.cs', '.php', '.rb', - '.rs', '.kt', '.swift', '.sh', '.bash', '.zsh', '.sql', '.prisma', '.graphql', '.gql', - '.json', '.yaml', '.yml', '.xml', '.toml', '.ini', '.md', '.txt' + '.rs', '.kt', '.swift', '.scala', '.groovy', '.lua', '.r', '.m', '.zig', '.dart', + // Functional + '.hs', '.ex', '.exs', '.clj', '.cljs', '.fs', '.fsx', '.jl', + // Enterprise / legacy + '.vb', '.pl', '.pm', + // Shell / scripting + '.sh', '.bash', '.zsh', '.ps1', '.psm1', '.psd1', + // SQL dialects + '.sql', '.pgsql', '.plsql', + // Schema / query + '.prisma', '.graphql', '.gql', '.proto', + // Data / config + '.json', '.yaml', '.yml', '.xml', '.toml', '.ini', '.cfg', '.conf', '.csv', '.log', + // Infra + '.tf', '.tfvars', '.hcl', '.gradle', + // Docs + '.md', '.txt', '.rst', '.tex', ]); /** Well-known extensionless filenames that should still be line-counted. */ diff --git a/src/lineCounter.ts b/src/lineCounter.ts index 9929ca2..959c077 100644 --- a/src/lineCounter.ts +++ b/src/lineCounter.ts @@ -16,7 +16,17 @@ import * as path from 'path'; import { BINARY_EXTENSIONS } from './constants'; import { AppState } from './state'; -/** Format a number for the explorer badge: 1234 -> "1K", 2500000 -> "2M". */ +/** + * Format a number for the explorer badge. + * + * VS Code silently drops FileDecoration badges longer than 2 characters, + * so every tier must produce a 1–2 char string: + * 1–99 → "1" … "99" + * 100–999 → "1H" … "9H" (H = hundreds: 1H ≈ 100s, 2H ≈ 200s, …) + * 1 000–9 999 → "1K" … "9K" + * 10 000–999 999 → "10K" … "999K" — but 3+ chars; use "XK" floored + * 1 000 000+ → "1M" … + */ export function formatLineCount(count: number): string { if (count >= 1_000_000) { return `${Math.floor(count / 1_000_000)}M`; @@ -26,6 +36,10 @@ export function formatLineCount(count: number): string { return `${Math.floor(count / 1_000)}K`; } + if (count >= 100) { + return `${Math.floor(count / 100)}H`; + } + return count.toString(); } diff --git a/src/test/lineCounter.test.ts b/src/test/lineCounter.test.ts index 84139cd..f9a0e3b 100644 --- a/src/test/lineCounter.test.ts +++ b/src/test/lineCounter.test.ts @@ -15,10 +15,18 @@ describe('formatLineCount', () => { assert.strictEqual(formatLineCount(9_999), '9K'); }); - it('returns plain number under 1000', () => { + it('formats hundreds with H suffix', () => { + assert.strictEqual(formatLineCount(100), '1H'); + assert.strictEqual(formatLineCount(155), '1H'); + assert.strictEqual(formatLineCount(224), '2H'); + assert.strictEqual(formatLineCount(500), '5H'); + assert.strictEqual(formatLineCount(999), '9H'); + }); + + it('returns plain number under 100', () => { assert.strictEqual(formatLineCount(0), '0'); assert.strictEqual(formatLineCount(1), '1'); - assert.strictEqual(formatLineCount(999), '999'); + assert.strictEqual(formatLineCount(99), '99'); }); }); From fdf7158cfa53cf030f328d419758c7227bbd8e26 Mon Sep 17 00:00:00 2001 From: Niraj Kumar Date: Sun, 8 Feb 2026 03:37:44 +0530 Subject: [PATCH 4/5] feat: Implement esbuild for production bundling, establish GitHub Actions for CI/CD and publishing, and rewrite the README. --- .github/workflows/ci.yml | 50 + .github/workflows/publish.yml | 28 + .gitignore | 1 + .vscodeignore | 7 +- CHANGELOG.md | 51 +- README.md | 140 +- esbuild.js | 32 + package-lock.json | 4398 +++++++++++++++++++++++++++++++-- package.json | 10 +- 9 files changed, 4409 insertions(+), 308 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/publish.yml create mode 100644 esbuild.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5805844 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,50 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run lint + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run build + - run: npx @vscode/vsce package --no-dependencies + - uses: actions/upload-artifact@v4 + with: + name: linesight.vsix + path: "*.vsix" + + test: + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run compile + - run: npm run test:unit diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..a365d5f --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,28 @@ +name: Publish + +on: + push: + tags: + - "v*" + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run test:unit + - run: npm run build + - name: Publish to VS Code Marketplace + run: npx @vscode/vsce publish --no-dependencies + env: + VSCE_PAT: ${{ secrets.VSCE_PAT }} + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + files: "*.vsix" diff --git a/.gitignore b/.gitignore index 5fe00fe..0b60dfa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ out +dist node_modules .vscode-test/ *.vsix diff --git a/.vscodeignore b/.vscodeignore index fc6e656..fb7418a 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -1,9 +1,14 @@ .vscode/** .vscode-test/** +.github/** +.claude/** src/** +out/** +node_modules/** .gitignore .eslintrc.json +.mocharc.yml tsconfig.json +esbuild.js **/*.map **/*.ts -node_modules/** diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b77dc0..1817296 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,52 @@ # Changelog +## v0.0.8 — Build & CI Infrastructure (Unreleased) + +### esbuild bundling + +Replaced raw `tsc` output with esbuild for production builds. The extension now ships as a single minified `dist/extension.js` (14KB) instead of multiple files under `out/`. + +- Added `esbuild.js` build script with production minification and optional watch mode +- `vscode:prepublish` now runs `npm run build` (esbuild) instead of `npm run compile` (tsc) +- `tsc` is still used for type checking and compiling tests +- Added `@vscode/vsce` as a devDependency for packaging and publishing + +**Package.json script changes:** +- `build` — production esbuild bundle +- `watch` — esbuild watch mode for development +- `watch:tsc` — tsc watch with `--noEmit` for type checking during development +- `compile` — tsc compilation (unchanged, used for tests) + +### CI/CD via GitHub Actions + +Added two workflows under `.github/workflows/`: + +**ci.yml** — runs on every push and PR to `main`: +- `lint` — eslint on ubuntu +- `build` — esbuild production build + VSIX packaging with artifact upload +- `test` — unit tests on ubuntu, windows, and macos + +**publish.yml** — triggered by `v*` tags: +- Runs tests, builds production bundle +- Publishes to VS Code Marketplace via `VSCE_PAT` secret +- Creates a GitHub Release with auto-generated release notes + +### README rewrite + +Replaced the original README with a focused, clutter-free version: +- Install instructions for both Marketplace and building from source (clone, build, install VSIX) +- Architecture overview showing module structure +- Development setup commands +- Configuration reference table + +### Housekeeping + +- Updated `.vscodeignore` to exclude `out/`, `.github/`, build config files +- Updated `.gitignore` to exclude `dist/` +- Closed R1-19 (no CI/CD pipeline) from the v0.0.6 open items + +--- + ## v0.0.7 — Security Hardening (Unreleased) ### Security Fixes @@ -51,7 +98,7 @@ remains open. | R1-16 | MEDIUM | No read timeout on file stream — hangs on FIFO/network mounts | **Fixed.** 10s timeout with `settled` flag and stream destruction. | | R1-17 | MEDIUM | No cancellation support for initialization | **Fixed.** `initializationRunId` monotonic counter detects stale runs. | | R1-18 | LOW | Symlink traversal (`stat` follows symlinks to FIFOs) | Open. Timeout limits blast radius but `stat` itself has no timeout. | -| R1-19 | MEDIUM | No CI/CD pipeline | Open. Tests exist but don't run automatically on PRs. | +| R1-19 | MEDIUM | No CI/CD pipeline | **Fixed in v0.0.8.** GitHub Actions CI + publish workflows. | | R1-20 | MEDIUM | Stream overhead for small files (ReadStream for every size) | Open. Low priority — current approach is correct, just suboptimal for <512KB. | --- @@ -261,7 +308,7 @@ via a lightweight mock (`test/vscode-mock.ts`): | # | Severity | Item | Notes | |---|----------|------|-------| | R1-18 | LOW | Symlink traversal | `stat()` follows symlinks; timeout limits blast radius | -| R1-19 | MEDIUM | No CI/CD pipeline | Tests exist but need GitHub Actions | +| R1-19 | MEDIUM | No CI/CD pipeline | **Fixed in v0.0.8.** GitHub Actions CI + publish workflows. | | R1-20 | LOW | Stream overhead for small files | Correct but suboptimal | | R2-5 | MEDIUM | `activeTimers` module-level global | Works correctly, inconsistent with AppState | | R2-6 | MEDIUM | Broad file watcher pattern | VS Code API limitation | diff --git a/README.md b/README.md index 541c070..3e64b85 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,112 @@ # LineSight -LineSight is a VS Code extension that shows the number of lines next to each file in the file explorer, helping you quickly understand the size of files in your project. +[![CI](https://github.com/karansinghgit/LineSight/actions/workflows/ci.yml/badge.svg)](https://github.com/karansinghgit/LineSight/actions/workflows/ci.yml) -The extension is available on the VSCode Marketplace for installing: +A VS Code extension that displays line counts next to files in the explorer sidebar. -https://marketplace.cursorapi.com/items?itemName=2048Labs.linesight +Large files are a common friction point when working with LLMs — context windows have limits, and even modern IDEs with smart chunking perform better on smaller files. LineSight gives you an at-a-glance view of file sizes so you know when something needs to be broken up. + +LineSight Logo -## Why I built this? +## Install -A common issue with LLM's is the size of the context window, and even though modern IDE's like Cursor support better chunking and indexing for large files, it still performs much better when working with smaller files. +**From the Marketplace** — search for "LineSight" in the VS Code extensions panel, or install directly: -LineSight allows you to quickly glance at line counts to understand when you need to refactor a particular file. +https://marketplace.cursorapi.com/items?itemName=2048Labs.linesight -Efficient caching, progressive loading and prioritization make this extension very performant when running in the background. +**From source:** -Hope you find it useful :) +```sh +git clone https://github.com/karansinghgit/LineSight.git +cd LineSight +npm install +npm run build +npx @vscode/vsce package --no-dependencies +``` -LineSight Logo +This produces a `.vsix` file. Install it in VS Code: -## Features +``` +code --install-extension linesight-*.vsix +``` -- **Line Count Badges**: Shows the number of lines next to each file in the explorer -- **Auto-Updates**: Line counts automatically update when files are edited -- **Refresh Button**: Provides a refresh button in the explorer title bar to manually update counts -- **Abbreviated Display**: Shows abbreviated counts (like "2K" for 2000+ lines) as badges -- **Exact Counts in Tooltips**: Hover over a badge to see the exact line count -- **Skip Large Directories**: Ignores directories like node_modules and .git for better performance -- **Optimized Performance**: Minimal background overhead with smart caching and throttling +Or open VS Code, go to Extensions > `...` menu > "Install from VSIX..." and select the file. ## Screenshot ![LineSight in action](resources/images/screenshot.png) -## Usage - -Once installed, LineSight will automatically display line counts next to your files in the explorer panel. - -- **Refresh Counts**: Click the refresh icon in the explorer title bar to manually refresh line counts -- **View Exact Count**: Hover over a line count badge to see the exact number of lines in the tooltip - -## Performance Considerations - -- For very large files (over 5MB), line counts are estimated based on file size -- Certain directories are skipped by default to improve performance: node_modules, .git, dist, build, out -- The extension uses smart caching to minimize CPU usage -- File watchers are limited to common code file types to reduce overhead -- Updates are debounced and throttled to prevent performance impact - -## Installation - -1. Install the extension from the VS Code Marketplace -2. Reload VS Code Window -3. Line counts will automatically appear next to files in the explorer - -## Extension Settings - -LineSight supports the following settings: +## Features -- `linesight.sizeLimit`: max file size (bytes) before estimated counts are used -- `linesight.batchSize`: files processed per initialization batch -- `linesight.debounceDelay`: debounce delay (ms) for file/update events -- `linesight.initialScanDelay`: delay (ms) before initial workspace scan -- `linesight.estimationFactor`: bytes-per-line factor for large-file estimates -- `linesight.excludeFolders`: extra folder paths to skip -- `linesight.includeExtensions`: optional extensions to include (empty uses built-in defaults) -- `linesight.showStartupNotifications`: show initialization status notifications +- **Line count badges** in the explorer next to every tracked file +- **Live updates** as you type — uses the in-memory editor buffer, zero disk I/O +- **Abbreviated display** — `42`, `3H`, `2K`, `1M` as badges, exact counts in tooltips +- **Refresh command** — manual refresh button in the explorer title bar +- **Configurable** — control size limits, debounce timing, file types, excluded folders +- **Workspace trust aware** — defers file scanning in untrusted workspaces + +## How It Works + +LineSight registers a `FileDecorationProvider` that VS Code queries whenever it renders a file in the explorer. On activation, it walks the workspace in batches, streams each file to count newlines, and caches the result. From there: + +- **Edits** update the count from the in-memory buffer (no disk read) +- **Saves** trigger a disk-based recount +- **File system changes** (create, rename, delete) are picked up by a watcher and debounced + +Performance is managed through bounded LRU caches, a concurrency limiter on parallel file reads, stream timeouts for hung files, and queue caps that trigger a full refresh under update storms. + +## Configuration + +All settings live under the `linesight.*` namespace. + +| Setting | Default | Description | +|---|---|---| +| `sizeLimit` | `5000000` | Max file size (bytes) before switching to estimated counts | +| `batchSize` | `200` | Files per initialization batch | +| `debounceDelay` | `300` | Debounce delay (ms) for file change events | +| `initialScanDelay` | `2000` | Delay (ms) before initial workspace scan | +| `estimationFactor` | `50` | Bytes-per-line factor for large file estimates | +| `excludeFolders` | `[]` | Additional folders to skip (additive to built-in defaults) | +| `includeExtensions` | `[]` | File extensions to include (empty = built-in defaults) | +| `showStartupNotifications` | `false` | Show status bar messages during initialization | + +**Built-in excluded folders:** node_modules, .git, dist, build, out, bin, obj, .vscode, .idea, .vs, vendor, coverage, .next, .nuxt, target, .sass-cache, .cache, and others. + +**Built-in included extensions:** 60+ file types covering web (.js, .ts, .jsx, .tsx, .html, .css, .vue, .svelte), systems (.go, .rs, .c, .cpp, .java, .py, .rb), data (.json, .yaml, .xml, .sql, .graphql), and more. + +## Architecture + +The codebase is split into focused, cycle-free modules: + +``` +extension.ts thin entry point — wires everything together +types.ts shared TypeScript interfaces +constants.ts built-in file extension and folder lists +config.ts settings parsing and validation +cache.ts bounded LRU cache (ES2015 Map insertion order) +concurrency.ts promise-based concurrency limiter with FIFO queue +timer.ts tracked timeouts with clean disposal +state.ts central AppState threaded through all modules +fileFilter.ts path/extension filtering logic +lineCounter.ts stream-based line counting and formatting +decorationProvider.ts FileDecorationProvider with batched notifications +fileWatcher.ts file system watching with debounced updates +initialization.ts workspace scanning with batch processing +``` + +Tests live in `src/test/` with a lightweight VS Code mock that allows unit testing without the extension host. + +## Development + +```sh +npm install +npm run compile # type-check with tsc +npm run build # production bundle with esbuild +npm run watch # esbuild watch mode for development +npm run lint # eslint +npm run test:unit # compile + mocha unit tests +npm test # compile + lint + unit tests +``` ## License diff --git a/esbuild.js b/esbuild.js new file mode 100644 index 0000000..f76fa40 --- /dev/null +++ b/esbuild.js @@ -0,0 +1,32 @@ +const esbuild = require('esbuild'); + +const production = process.argv.includes('--production'); +const watch = process.argv.includes('--watch'); + +async function main() { + const ctx = await esbuild.context({ + entryPoints: ['src/extension.ts'], + bundle: true, + format: 'cjs', + minify: production, + sourcemap: !production, + sourcesContent: false, + platform: 'node', + outfile: 'dist/extension.js', + external: ['vscode'], + logLevel: 'silent', + }); + + if (watch) { + await ctx.watch(); + console.log('watching for changes...'); + } else { + await ctx.rebuild(); + await ctx.dispose(); + } +} + +main().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/package-lock.json b/package-lock.json index f3252b9..6214b14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,8 @@ "@types/vscode": "^1.80.0", "@typescript-eslint/eslint-plugin": "^8.15.0", "@typescript-eslint/parser": "^8.15.0", + "@vscode/vsce": "^3.2.2", + "esbuild": "^0.24.2", "eslint": "^8.57.1", "mocha": "^10.7.0", "typescript": "^5.8.2" @@ -22,6 +24,642 @@ "vscode": "^1.80.0" } }, + "node_modules/@azu/format-text": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.2.tgz", + "integrity": "sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@azu/style-format": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azu/style-format/-/style-format-1.0.1.tgz", + "integrity": "sha512-AHcTojlNBdD/3/KxIKlg8sxIWHfOtQszLvOpagLTO+bjC3u7SAszu1lf//u7JJC50aUSH+BVWDD/KvaA6Gfn5g==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "@azu/format-text": "^1.0.1" + } + }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz", + "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.2.tgz", + "integrity": "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", + "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.0.tgz", + "integrity": "sha512-uWC0fssc+hs1TGGVkkghiaFkkS7NkTxfnCH+Hdg+yTehTpMcehpok4PgUKKdyCH+9ldu6FhiHRv84Ntqj1vVcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^4.2.0", + "@azure/msal-node": "^3.5.0", + "open": "^10.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.28.1.tgz", + "integrity": "sha512-al2u2fTchbClq3L4C1NlqLm+vwKfhYCPtZN2LR/9xJVaQ4Mnrwf5vANvuyPSJHcGvw50UBmhuVmYUAhTEetTpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.14.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "15.14.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.14.1.tgz", + "integrity": "sha512-IkzF7Pywt6QKTS0kwdCv/XV8x8JXknZDvSjj/IccooxnP373T5jaadO3FnOrbWo3S0UqkfIDyZNTaQ/oAgRdXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "3.8.6", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.8.6.tgz", + "integrity": "sha512-XTmhdItcBckcVVTy65Xp+42xG4LX5GK+9AqAsXPXk4IqUNv+LyQo5TMwNjuFYBfAB2GTG9iSQGk+QLc03vhf3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.14.1", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -93,120 +731,454 @@ "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", + "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@secretlint/config-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-creator/-/config-creator-10.2.2.tgz", + "integrity": "sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-loader/-/config-loader-10.2.2.tgz", + "integrity": "sha512-ndjjQNgLg4DIcMJp4iaRD6xb9ijWQZVbd9694Ol2IszBIbGPPkwZHzJYKICbTBmh6AH/pLr0CiCaWdGJU7RbpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "ajv": "^8.17.1", + "debug": "^4.4.1", + "rc-config-loader": "^4.1.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@secretlint/config-loader/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/core": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz", + "integrity": "sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "structured-source": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/formatter/-/formatter-10.2.2.tgz", + "integrity": "sha512-10f/eKV+8YdGKNQmoDUD1QnYL7TzhI2kzyx95vsJKbEa8akzLAR5ZrWIZ3LbcMmBLzxlSQMMccRmi05yDQ5YDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "@textlint/linter-formatter": "^15.2.0", + "@textlint/module-interop": "^15.2.0", + "@textlint/types": "^15.2.0", + "chalk": "^5.4.1", + "debug": "^4.4.1", + "pluralize": "^8.0.0", + "strip-ansi": "^7.1.0", + "table": "^6.9.0", + "terminal-link": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@secretlint/formatter/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@secretlint/formatter/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@secretlint/node": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/node/-/node-10.2.2.tgz", + "integrity": "sha512-eZGJQgcg/3WRBwX1bRnss7RmHHK/YlP/l7zOQsrjexYt6l+JJa5YhUmHbuGXS94yW0++3YkEJp0kQGYhiw1DMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-loader": "^10.2.2", + "@secretlint/core": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "@secretlint/source-creator": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "p-map": "^7.0.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/profiler": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz", + "integrity": "sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/resolver": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/resolver/-/resolver-10.2.2.tgz", + "integrity": "sha512-3md0cp12e+Ae5V+crPQYGd6aaO7ahw95s28OlULGyclyyUtf861UoRGS2prnUrKh7MZb23kdDOyGCYb9br5e4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/secretlint-formatter-sarif": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-formatter-sarif/-/secretlint-formatter-sarif-10.2.2.tgz", + "integrity": "sha512-ojiF9TGRKJJw308DnYBucHxkpNovDNu1XvPh7IfUp0A12gzTtxuWDqdpuVezL7/IP8Ua7mp5/VkDMN9OLp1doQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-sarif-builder": "^3.2.0" + } + }, + "node_modules/@secretlint/secretlint-rule-no-dotenv": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz", + "integrity": "sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" }, "engines": { - "node": "*" + "node": ">=20.0.0" } }, - "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "node_modules/@secretlint/secretlint-rule-preset-recommend": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz", + "integrity": "sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==", "dev": true, "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=20.0.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", + "node_modules/@secretlint/source-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz", + "integrity": "sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "@secretlint/types": "^10.2.2", + "istextorbinary": "^9.5.0" }, "engines": { - "node": ">=10.10.0" + "node": ">=20.0.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/@secretlint/types": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz", + "integrity": "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==", "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, "engines": { - "node": "*" + "node": ">=20.0.0" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "engines": { - "node": ">=12.22" + "node": ">=18" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", + "node_modules/@textlint/ast-node-types": { + "version": "15.5.1", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.5.1.tgz", + "integrity": "sha512-2ABQSaQoM9u9fycXLJKcCv4XQulJWTUSwjo6F0i/ujjqOH8/AZ2A0RDKKbAddqxDhuabVB20lYoEsZZgzehccg==", "dev": true, - "license": "BSD-3-Clause" + "license": "MIT" }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@textlint/linter-formatter": { + "version": "15.5.1", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.5.1.tgz", + "integrity": "sha512-7wfzpcQtk7TZ3UJO2deTI71mJCm4VvPGUmSwE4iuH6FoaxpdWpwSBiMLcZtjYrt/oIFOtNz0uf5rI+xJiHTFww==", "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" + "@azu/format-text": "^1.0.2", + "@azu/style-format": "^1.0.1", + "@textlint/module-interop": "15.5.1", + "@textlint/resolver": "15.5.1", + "@textlint/types": "15.5.1", + "chalk": "^4.1.2", + "debug": "^4.4.3", + "js-yaml": "^4.1.1", + "lodash": "^4.17.21", + "pluralize": "^2.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "table": "^6.9.0", + "text-table": "^0.2.0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@textlint/linter-formatter/node_modules/pluralize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-2.0.0.tgz", + "integrity": "sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } + "license": "MIT" }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@textlint/module-interop": { + "version": "15.5.1", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.5.1.tgz", + "integrity": "sha512-Y1jcFGCKNSmHxwsLO3mshOfLYX4Wavq2+w5BG6x5lGgZv0XrF1xxURRhbnhns4LzCu0fAcx6W+3V8/1bkyTZCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/resolver": { + "version": "15.5.1", + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.5.1.tgz", + "integrity": "sha512-CVHxMIm8iNGccqM12CQ/ycvh+HjJId4RyC6as5ynCcp2E1Uy1TCe0jBWOpmLsbT4Nx15Ke29BmspyByawuIRyA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/types": { + "version": "15.5.1", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.5.1.tgz", + "integrity": "sha512-IY1OVZZk8LOOrbapYCsaeH7XSJT89HVukixDT8CoiWMrKGCTCZ3/Kzoa3DtMMbY8jtY777QmPOVCNnR+8fF6YQ==", "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" + "@textlint/ast-node-types": "15.5.1" } }, "node_modules/@types/mocha": { @@ -226,6 +1198,20 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/vscode": { "version": "1.98.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.98.0.tgz", @@ -380,110 +1366,382 @@ "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", - "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", + "node_modules/@typescript-eslint/types": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", + "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", + "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.54.0", + "@typescript-eslint/tsconfig-utils": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz", + "integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", + "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.54.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.3.tgz", + "integrity": "sha512-91fp6CAAJSRtH5ja95T1FHSKa8aPW9/Zw6cta81jlZTUw/+Vq8jM/AfF/14h2b71wwR84JUTW/3Y8QPhDAawFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vscode/vsce": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.7.1.tgz", + "integrity": "sha512-OTm2XdMt2YkpSn2Nx7z2EJtSuhRHsTPYsSK59hr3v8jRArK+2UEoju4Jumn1CmpgoBLGI6ReHLJ/czYltNUW3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/identity": "^4.1.0", + "@secretlint/node": "^10.1.2", + "@secretlint/secretlint-formatter-sarif": "^10.1.2", + "@secretlint/secretlint-rule-no-dotenv": "^10.1.2", + "@secretlint/secretlint-rule-preset-recommend": "^10.1.2", + "@vscode/vsce-sign": "^2.0.0", + "azure-devops-node-api": "^12.5.0", + "chalk": "^4.1.2", + "cheerio": "^1.0.0-rc.9", + "cockatiel": "^3.1.2", + "commander": "^12.1.0", + "form-data": "^4.0.0", + "glob": "^11.0.0", + "hosted-git-info": "^4.0.2", + "jsonc-parser": "^3.2.0", + "leven": "^3.1.0", + "markdown-it": "^14.1.0", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "secretlint": "^10.1.2", + "semver": "^7.5.2", + "tmp": "^0.2.3", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.5.0", + "yauzl": "^2.3.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "keytar": "^7.7.0" + } + }, + "node_modules/@vscode/vsce-sign": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.0.9.tgz", + "integrity": "sha512-8IvaRvtFyzUnGGl3f5+1Cnor3LqaUWvhaUjAYO8Y39OUYlOf3cRd+dowuQYLpZcP3uwSG+mURwjEBOSq4SOJ0g==", + "dev": true, + "hasInstallScript": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optionalDependencies": { + "@vscode/vsce-sign-alpine-arm64": "2.0.6", + "@vscode/vsce-sign-alpine-x64": "2.0.6", + "@vscode/vsce-sign-darwin-arm64": "2.0.6", + "@vscode/vsce-sign-darwin-x64": "2.0.6", + "@vscode/vsce-sign-linux-arm": "2.0.6", + "@vscode/vsce-sign-linux-arm64": "2.0.6", + "@vscode/vsce-sign-linux-x64": "2.0.6", + "@vscode/vsce-sign-win32-arm64": "2.0.6", + "@vscode/vsce-sign-win32-x64": "2.0.6" + } + }, + "node_modules/@vscode/vsce-sign-alpine-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-arm64/-/vsce-sign-alpine-arm64-2.0.6.tgz", + "integrity": "sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-alpine-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-x64/-/vsce-sign-alpine-x64-2.0.6.tgz", + "integrity": "sha512-YoAGlmdK39vKi9jA18i4ufBbd95OqGJxRvF3n6ZbCyziwy3O+JgOpIUPxv5tjeO6gQfx29qBivQ8ZZTUF2Ba0w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.6.tgz", + "integrity": "sha512-5HMHaJRIQuozm/XQIiJiA0W9uhdblwwl2ZNDSSAeXGO9YhB9MH5C4KIHOmvyjUnKy4UCuiP43VKpIxW1VWP4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.6.tgz", + "integrity": "sha512-25GsUbTAiNfHSuRItoQafXOIpxlYj+IXb4/qarrXu7kmbH94jlm5sdWSCKrrREs8+GsXF1b+l3OB7VJy5jsykw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm/-/vsce-sign-linux-arm-2.0.6.tgz", + "integrity": "sha512-UndEc2Xlq4HsuMPnwu7420uqceXjs4yb5W8E2/UkaHBB9OWCwMd3/bRe/1eLe3D8kPpxzcaeTyXiK3RdzS/1CA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.6.tgz", + "integrity": "sha512-cfb1qK7lygtMa4NUl2582nP7aliLYuDEVpAbXJMkDq1qE+olIw/es+C8j1LJwvcRq1I2yWGtSn3EkDp9Dq5FdA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", - "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", + "node_modules/@vscode/vsce-sign-linux-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.6.tgz", + "integrity": "sha512-/olerl1A4sOqdP+hjvJ1sbQjKN07Y3DVnxO4gnbn/ahtQvFrdhUi0G1VsZXDNjfqmXw57DmPi5ASnj/8PGZhAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-win32-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz", + "integrity": "sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vscode/vsce-sign-win32-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz", + "integrity": "sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vscode/vsce/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.54.0", - "@typescript-eslint/tsconfig-utils": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", - "debug": "^4.4.3", - "minimatch": "^9.0.5", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz", - "integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==", + "node_modules/@vscode/vsce/node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0" + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "20 || >=22" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", - "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", + "node_modules/@vscode/vsce/node_modules/glob/node_modules/minimatch": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", + "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "@typescript-eslint/types": "8.54.0", - "eslint-visitor-keys": "^4.2.1" + "@isaacs/brace-expansion": "^5.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "20 || >=22" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "node_modules/@vscode/vsce/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" }, - "funding": { - "url": "https://opencollective.com/eslint" + "engines": { + "node": "*" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC" - }, "node_modules/acorn": { "version": "8.14.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", @@ -508,6 +1766,16 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -535,6 +1803,22 @@ "node": ">=6" } }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -582,6 +1866,34 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/azure-devops-node-api": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz", + "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==", + "dev": true, + "license": "MIT", + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -589,6 +1901,28 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -602,6 +1936,49 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/binaryextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz", + "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/boundary": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz", + "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -609,29 +1986,119 @@ "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dev": true, "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true, - "license": "ISC" - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -672,6 +2139,50 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/cheerio": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", + "integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.1.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.19.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -710,6 +2221,14 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC", + "optional": true + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -722,6 +2241,16 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/cockatiel": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", + "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -742,6 +2271,29 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -764,6 +2316,36 @@ "node": ">= 8" } }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -795,42 +2377,376 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, - "license": "MIT" + "license": "MIT" + }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/editions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz", + "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "version-range": "^4.15.0" + }, + "engines": { + "ecmascript": ">= es5", + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "node_modules/diff": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", - "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, "engines": { - "node": ">=0.3.1" + "node": ">= 0.4" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "esutils": "^2.0.2" + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { - "node": ">=6.0.0" + "node": ">= 0.4" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "node_modules/esbuild": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", "dev": true, - "license": "MIT" + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" + } }, "node_modules/escalade": { "version": "3.2.0", @@ -1031,6 +2947,17 @@ "node": ">=0.10.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "license": "(MIT OR WTFPL)", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1038,6 +2965,36 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1052,6 +3009,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -1062,6 +3036,16 @@ "reusify": "^1.0.4" } }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -1137,6 +3121,63 @@ "dev": true, "license": "ISC" }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/fs-extra": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz", + "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1159,6 +3200,16 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1169,6 +3220,53 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1244,6 +3342,57 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -1261,6 +3410,48 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -1271,6 +3462,115 @@ "he": "bin/he" } }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause", + "optional": true + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -1308,6 +3608,19 @@ "node": ">=0.8.19" } }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1327,6 +3640,14 @@ "dev": true, "license": "ISC" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC", + "optional": true + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1340,6 +3661,22 @@ "node": ">=8" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1373,6 +3710,25 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1416,6 +3772,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1423,6 +3795,47 @@ "dev": true, "license": "ISC" }, + "node_modules/istextorbinary": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz", + "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "binaryextensions": "^6.11.0", + "editions": "^6.21.0", + "textextensions": "^6.11.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/jackspeak": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", @@ -1450,70 +3863,367 @@ "dev": true, "license": "MIT" }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "dev": true, "license": "MIT" }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" + "engines": { + "node": ">= 8" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">=8.6" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" + "bin": { + "mime": "cli.js" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "mime-db": "1.52.0" }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "optional": true, "engines": { "node": ">=10" }, @@ -1537,6 +4247,35 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/mocha": { "version": "10.8.2", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", @@ -1630,6 +4369,21 @@ "dev": true, "license": "MIT" }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true, + "license": "ISC" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -1637,6 +4391,77 @@ "dev": true, "license": "MIT" }, + "node_modules/node-abi": { + "version": "3.87.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", + "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-sarif-builder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.4.0.tgz", + "integrity": "sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1647,6 +4472,32 @@ "node": ">=0.10.0" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1657,6 +4508,25 @@ "wrappy": "1" } }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -1707,6 +4577,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -1720,6 +4610,110 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-semver": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", + "integrity": "sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.1.0" + } + }, + "node_modules/parse-semver/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -1750,6 +4744,60 @@ "node": ">=8" } }, + "node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.2.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", + "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1759,8 +4807,46 @@ "engines": { "node": ">=8.6" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/prelude-ls": { @@ -1773,6 +4859,18 @@ "node": ">= 0.8.0" } }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -1783,6 +4881,32 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -1814,6 +4938,122 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc-config-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.3.tgz", + "integrity": "sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "js-yaml": "^4.1.0", + "json5": "^2.2.2", + "require-from-string": "^2.0.2" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1837,6 +5077,16 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1875,6 +5125,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -1920,50 +5183,305 @@ ], "license": "MIT" }, - "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz", + "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/secretlint": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/secretlint/-/secretlint-10.2.2.tgz", + "integrity": "sha512-xVpkeHV/aoWe4vP4TansF622nBEImzCY73y/0042DuJ29iKIaqgoJ8fGxre3rVSHHbxar4FdJobmTnLp9AU0eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-creator": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/node": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "debug": "^4.4.1", + "globby": "^14.1.0", + "read-pkg": "^9.0.1" + }, + "bin": { + "secretlint": "bin/secretlint.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, - "license": "BSD-3-Clause", + "license": "Apache-2.0", "dependencies": { - "randombytes": "^2.1.0" + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "optional": true, + "dependencies": { + "safe-buffer": "~5.2.0" } }, "node_modules/string-width": { @@ -2007,6 +5525,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/structured-source": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", + "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boundary": "^2.0.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2020,6 +5548,113 @@ "node": ">=8" } }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terminal-link": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-4.0.0.tgz", + "integrity": "sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "supports-hyperlinks": "^3.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -2027,6 +5662,22 @@ "dev": true, "license": "MIT" }, + "node_modules/textextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz", + "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -2076,6 +5727,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2102,6 +5763,37 @@ "typescript": ">=4.8.4" } }, + "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==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -2128,6 +5820,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-rest-client": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", + "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, "node_modules/typescript": { "version": "5.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", @@ -2143,6 +5847,30 @@ "node": ">=14.17" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.21.0.tgz", + "integrity": "sha512-Hn2tCQpoDt1wv23a68Ctc8Cr/BHpUSfaPYrkajTXOS9IKpxVRx/X5m1K2YkbK2ipgZgxXSgsUinl3x+2YdSSfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", @@ -2150,6 +5878,29 @@ "dev": true, "license": "MIT" }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2160,6 +5911,79 @@ "punycode": "^2.1.0" } }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/version-range": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz", + "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==", + "dev": true, + "license": "Artistic-2.0", + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2218,6 +6042,46 @@ "dev": true, "license": "ISC" }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -2228,6 +6092,13 @@ "node": ">=10" } }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -2273,6 +6144,27 @@ "node": ">=10" } }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 8116dd4..7d7efaa 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "description": "LineSight disables file scanning and watching in untrusted workspaces." } }, - "main": "./out/extension.js", + "main": "./dist/extension.js", "contributes": { "commands": [ { @@ -99,9 +99,11 @@ } }, "scripts": { - "vscode:prepublish": "npm run compile", + "vscode:prepublish": "npm run build", "compile": "tsc -p ./", - "watch": "tsc -watch -p ./", + "build": "node esbuild.js --production", + "watch": "node esbuild.js --watch", + "watch:tsc": "tsc -watch -p ./ --noEmit", "lint": "eslint src --ext ts", "pretest": "npm run compile", "test": "npm run compile && npm run lint && mocha 'out/test/**/*.test.js'", @@ -114,6 +116,8 @@ "@types/node": "^20.17.0", "@typescript-eslint/eslint-plugin": "^8.15.0", "@typescript-eslint/parser": "^8.15.0", + "@vscode/vsce": "^3.2.2", + "esbuild": "^0.24.2", "eslint": "^8.57.1", "mocha": "^10.7.0", "typescript": "^5.8.2" From fcb362b2a03562562fdcfc63677cd3402a9d3723 Mon Sep 17 00:00:00 2001 From: Niraj Kumar Date: Sun, 8 Feb 2026 03:42:16 +0530 Subject: [PATCH 5/5] docs: Update CI badge URL in README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3e64b85..1e694c9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # LineSight -[![CI](https://github.com/karansinghgit/LineSight/actions/workflows/ci.yml/badge.svg)](https://github.com/karansinghgit/LineSight/actions/workflows/ci.yml) +[![CI](https://github.com/nirajkvinit/LineSight/actions/workflows/ci.yml/badge.svg)](https://github.com/nirajkvinit/LineSight/actions/workflows/ci.yml) A VS Code extension that displays line counts next to files in the explorer sidebar.