From d95fc3bb8ea10faba1ff17c313771c02bf34cb9f Mon Sep 17 00:00:00 2001 From: dmungamuri Date: Mon, 27 Oct 2025 00:57:27 +0530 Subject: [PATCH 1/2] feat: add comprehensive error capture system for LWC local dev @W-19978113@ This adds a robust error capture and reporting system for LWC local development that captures runtime errors from the browser and displays them formatted in the terminal. Features: - ErrorStore: In-memory store for runtime errors with deduplication - Error Capture HTTP Server: Standalone server on LWC port + 1 - Error Middleware: Express middleware for capturing/querying errors - Error Formatter: CLI-friendly formatted error output with colors - Stack Trace Utils: Parse and sanitize stack traces Components: - src/lwc-dev-server/errorStore.ts: Error storage with statistics - src/lwc-dev-server/errorHttpServer.ts: HTTP server for error endpoints - src/lwc-dev-server/errorMiddleware.ts: Express middleware - src/shared/errorFormatter.ts: Format errors for terminal display - src/shared/stackTraceUtils.ts: Stack trace parsing utilities - src/types/errorPayload.ts: TypeScript types for error diagnostics Testing: - Comprehensive unit tests for all error capture components - E2E tests for error capture workflow - Test fixtures for validation Updated: - package.json: Added express dependency - src/lwc-dev-server/index.ts: Integrated error capture server - yarn.lock: Updated dependencies Error Capture Endpoints: - POST /_dev/errors - Capture error reports - GET /_dev/errors - Query errors with filters - DELETE /_dev/errors - Clear all errors - GET /_dev/errors/stats - Get error statistics - GET /_dev/health - Health check Auto-clears errors on server restart. Can also manually clear via DELETE endpoint. --- package.json | 2 + src/lwc-dev-server/errorHttpServer.ts | 215 ++++++ src/lwc-dev-server/errorMiddleware.ts | 288 ++++++++ src/lwc-dev-server/errorStore.ts | 258 ++++++++ src/lwc-dev-server/index.ts | 78 ++- src/shared/errorFormatter.ts | 302 +++++++++ src/shared/stackTraceUtils.ts | 326 +++++++++ src/types/errorPayload.ts | 195 ++++++ .../errorTestComponent.html | 8 + .../errorTestComponent/errorTestComponent.js | 29 + .../errorTestComponent.js-meta.xml | 14 + .../errorTestComponent.html | 3 + .../errorTestComponent/errorTestComponent.js | 8 + .../errorTestComponent.js-meta.xml | 10 + .../errorTestProject/sfdx-project.json | 11 + test/lwc-dev-server/errorCapture.e2e.test.ts | 365 ++++++++++ test/lwc-dev-server/errorHttpServer.test.ts | 621 ++++++++++++++++++ test/lwc-dev-server/errorMiddleware.test.ts | 487 ++++++++++++++ test/lwc-dev-server/errorStore.test.ts | 385 +++++++++++ test/shared/stackTraceUtils.test.ts | 349 ++++++++++ yarn.lock | 617 ++++++++++++----- 21 files changed, 4397 insertions(+), 174 deletions(-) create mode 100644 src/lwc-dev-server/errorHttpServer.ts create mode 100644 src/lwc-dev-server/errorMiddleware.ts create mode 100644 src/lwc-dev-server/errorStore.ts create mode 100644 src/shared/errorFormatter.ts create mode 100644 src/shared/stackTraceUtils.ts create mode 100644 src/types/errorPayload.ts create mode 100644 test/fixtures/errorTestComponent/errorTestComponent.html create mode 100644 test/fixtures/errorTestComponent/errorTestComponent.js create mode 100644 test/fixtures/errorTestComponent/errorTestComponent.js-meta.xml create mode 100644 test/fixtures/errorTestProject/force-app/main/default/lwc/errorTestComponent/errorTestComponent.html create mode 100644 test/fixtures/errorTestProject/force-app/main/default/lwc/errorTestComponent/errorTestComponent.js create mode 100644 test/fixtures/errorTestProject/force-app/main/default/lwc/errorTestComponent/errorTestComponent.js-meta.xml create mode 100644 test/fixtures/errorTestProject/sfdx-project.json create mode 100644 test/lwc-dev-server/errorCapture.e2e.test.ts create mode 100644 test/lwc-dev-server/errorHttpServer.test.ts create mode 100644 test/lwc-dev-server/errorMiddleware.test.ts create mode 100644 test/lwc-dev-server/errorStore.test.ts create mode 100644 test/shared/stackTraceUtils.test.ts diff --git a/package.json b/package.json index 67a1fee..4eba562 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@salesforce/lwc-dev-mobile-core": "4.0.0-alpha.13", "@salesforce/sf-plugins-core": "^11.2.4", "axios": "^1.12.2", + "express": "^5.1.0", "glob": "^10.4.5", "lwc": "~8.23.0", "node-fetch": "^3.3.2", @@ -27,6 +28,7 @@ "@salesforce/cli-plugins-testkit": "^5.3.41", "@salesforce/dev-scripts": "^11.0.4", "@salesforce/plugin-command-reference": "^3.1.74", + "@types/express": "^5.0.3", "@types/node-fetch": "^2.6.13", "@types/xml2js": "^0.4.14", "@typescript-eslint/eslint-plugin": "^6.21.0", diff --git a/src/lwc-dev-server/errorHttpServer.ts b/src/lwc-dev-server/errorHttpServer.ts new file mode 100644 index 0000000..1d3db6c --- /dev/null +++ b/src/lwc-dev-server/errorHttpServer.ts @@ -0,0 +1,215 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Server } from 'node:http'; +// eslint-disable-next-line import/no-extraneous-dependencies +import express, { Express } from 'express'; +import { Logger } from '@salesforce/core'; +import { ErrorStore } from './errorStore.js'; +import { createCombinedErrorMiddleware, createErrorCORSMiddleware } from './errorMiddleware.js'; + +/** + * Configuration for the standalone error capture HTTP server + */ +export type ErrorServerConfig = { + /** Port for the error capture server */ + port: number; + /** Error store instance */ + errorStore: ErrorStore; + /** Logger instance */ + logger: Logger; + /** Project root directory */ + projectRoot: string; + /** Whether to log errors to console */ + logToConsole?: boolean; + /** Whether to bind to localhost only (recommended for security) */ + localhostOnly?: boolean; +}; + +/** + * Standalone HTTP server for error capture endpoints. + * + * This server runs independently of the LWC dev server and provides + * HTTP endpoints for error reporting, querying, and management. + * + * Endpoints: + * - POST /_dev/errors - Capture error reports + * - GET /_dev/errors - Query stored errors (with filters) + * - DELETE /_dev/errors - Clear all errors + * - GET /_dev/errors/stats - Get error statistics + * + * @example + * ```typescript + * const errorServer = await startErrorCaptureServer({ + * port: 8082, + * errorStore: getErrorStore(), + * logger, + * projectRoot: '/path/to/project', + * }); + * + * // Later... + * await errorServer.stop(); + * ``` + */ +export class ErrorCaptureServer { + private app: Express; + private server: Server | null = null; + private config: ErrorServerConfig; + + public constructor(config: ErrorServerConfig) { + this.config = config; + this.app = express(); + this.setupMiddleware(); + } + + /** + * Start the error capture server + */ + public async start(): Promise { + return new Promise((resolve, reject) => { + const { port, localhostOnly = true, logger } = this.config; + + try { + // Bind to localhost only for security (unless explicitly disabled) + const host = localhostOnly ? 'localhost' : '0.0.0.0'; + + this.server = this.app.listen(port, host, () => { + logger.info(`[ErrorCapture] Error capture server started at http://${host}:${port}`); + logger.info('[ErrorCapture] Available endpoints:'); + logger.info('[ErrorCapture] POST /_dev/errors - Capture error reports'); + logger.info( + '[ErrorCapture] GET /_dev/errors - Query errors (supports ?component=, ?severity=, ?limit=)' + ); + logger.info('[ErrorCapture] DELETE /_dev/errors - Clear all errors'); + logger.info('[ErrorCapture] GET /_dev/errors/stats - Get statistics'); + logger.info('[ErrorCapture] GET /_dev/health - Health check'); + resolve(); + }); + + this.server.on('error', (err: NodeJS.ErrnoException) => { + if (err.code === 'EADDRINUSE') { + logger.error(`[ErrorCapture] Port ${port} is already in use. Please use a different port.`); + reject(new Error(`Port ${port} is already in use`)); + } else { + logger.error(`[ErrorCapture] Server error: ${err.message}`); + reject(err); + } + }); + } catch (err) { + logger.error( + `[ErrorCapture] Failed to start error capture server: ${err instanceof Error ? err.message : String(err)}` + ); + reject(err); + } + }); + } + + /** + * Stop the error capture server + */ + public async stop(): Promise { + return new Promise((resolve, reject) => { + if (!this.server) { + resolve(); + return; + } + + this.server.close((err) => { + if (err) { + this.config.logger.error(`[ErrorCapture] Error stopping server: ${err.message}`); + reject(err); + } else { + this.config.logger.info('[ErrorCapture] Error capture server stopped'); + this.server = null; + resolve(); + } + }); + }); + } + + /** + * Get the underlying Express app (for testing) + */ + public getApp(): Express { + return this.app; + } + + /** + * Get the server instance (for testing) + */ + public getServer(): Server | null { + return this.server; + } + + /** + * Check if server is running + */ + public isRunning(): boolean { + return this.server !== null && this.server.listening; + } + + /** + * Set up Express middleware + */ + private setupMiddleware(): void { + const { errorStore, logger, projectRoot, logToConsole = true } = this.config; + + // Parse JSON request bodies + this.app.use(express.json({ limit: '10mb' })); + + // Enable CORS for error endpoints + this.app.use(createErrorCORSMiddleware()); + + // Add combined error middleware + const errorMiddleware = createCombinedErrorMiddleware({ + errorStore, + logger, + projectRoot, + logToConsole, + }); + this.app.use(errorMiddleware); + + // Health check endpoint + this.app.get('/_dev/health', (_req, res) => { + res.json({ + status: 'ok', + service: 'error-capture', + uptime: process.uptime(), + errors: errorStore.getErrorCount(), + }); + }); + + // Catch-all for unknown routes + this.app.use((_req, res) => { + res.status(404).json({ + error: 'Not Found', + message: 'Endpoint not found. Available endpoints: POST/GET/DELETE /_dev/errors, GET /_dev/errors/stats', + }); + }); + } +} + +/** + * Convenience function to start an error capture server + * + * @param config - Server configuration + * @returns Running ErrorCaptureServer instance + */ +export async function startErrorCaptureServer(config: ErrorServerConfig): Promise { + const server = new ErrorCaptureServer(config); + await server.start(); + return server; +} diff --git a/src/lwc-dev-server/errorMiddleware.ts b/src/lwc-dev-server/errorMiddleware.ts new file mode 100644 index 0000000..58c690d --- /dev/null +++ b/src/lwc-dev-server/errorMiddleware.ts @@ -0,0 +1,288 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// eslint-disable-next-line import/no-extraneous-dependencies +import { Request, Response, NextFunction } from 'express'; +import { Logger } from '@salesforce/core'; +import { isValidErrorPayload, ErrorDiagnosticPayload } from '../types/errorPayload.js'; +import { parseStackTrace } from '../shared/stackTraceUtils.js'; +import { formatErrorForCLI } from '../shared/errorFormatter.js'; +import { ErrorStore } from './errorStore.js'; + +/** + * Configuration for error middleware + */ +export type ErrorMiddlewareConfig = { + /** Error store instance */ + errorStore: ErrorStore; + /** Logger instance */ + logger: Logger; + /** Project root directory for stack trace sanitization */ + projectRoot: string; + /** Whether to log errors to console */ + logToConsole?: boolean; +}; + +/** + * Creates Express middleware for handling runtime error reports from the browser. + * + * This middleware provides a POST /_dev/errors endpoint that receives error diagnostic + * payloads from the client-side error capture system, stores them, and logs them to the CLI. + * + * @param config - Middleware configuration + * @returns Express middleware function + */ +export function createErrorMiddleware( + config: ErrorMiddlewareConfig +): (req: Request, res: Response, next: NextFunction) => void { + const { errorStore, logger, projectRoot, logToConsole = true } = config; + + return (req: Request, res: Response, next: NextFunction): void => { + // Only handle POST requests to /_dev/errors + if (req.method !== 'POST' || !req.path.startsWith('/_dev/errors')) { + next(); + return; + } + + try { + // Parse request body + const payload = req.body as unknown; + + // Validate payload structure + if (!isValidErrorPayload(payload)) { + res.status(400).json({ + error: 'Invalid error payload', + message: 'Payload does not match ErrorDiagnosticPayload schema', + }); + return; + } + + const error = payload; + + // Enhance stack trace with project context + if (error.error.stack && error.error.sanitizedStack.length === 0) { + error.error.sanitizedStack = parseStackTrace(error.error.stack, projectRoot); + } + + // Store the error + errorStore.addError(error); + + // Log to console if enabled + if (logToConsole) { + const formatted = formatErrorForCLI(error, { + colorize: true, + showFullStack: false, + compact: false, + }); + // eslint-disable-next-line no-console + console.error('\n๐Ÿ”ด [ErrorCapture] Runtime Error Detected:\n'); + // eslint-disable-next-line no-console + console.error(formatted); + // eslint-disable-next-line no-console + console.error(''); // blank line for spacing + logger.error(formatted); + } + + // Log to debug + logger.debug(`Error captured: ${error.errorId} - ${error.error.name}: ${error.error.message}`); + + // Send success response + res.status(201).json({ + success: true, + errorId: error.errorId, + message: 'Error captured successfully', + }); + } catch (err) { + logger.error(`Failed to process error report: ${(err as Error).message}`); + res.status(500).json({ + error: 'Internal server error', + message: 'Failed to process error report', + }); + } + }; +} + +/** + * Creates middleware for retrieving stored errors (GET /_dev/errors) + * + * @param errorStore - Error store instance + * @returns Express middleware function + */ +export function createErrorQueryMiddleware(errorStore: ErrorStore) { + return (req: Request, res: Response, next: NextFunction): void => { + // Only handle GET requests to /_dev/errors + if (req.method !== 'GET' || !req.path.startsWith('/_dev/errors')) { + next(); + return; + } + + try { + const { component, severity, limit = '100' } = req.query; + + let errors = errorStore.getErrors(); + + // Filter by component if specified + if (typeof component === 'string') { + errors = errors.filter((e: ErrorDiagnosticPayload) => e.component.name === component); + } + + // Filter by severity if specified + if (typeof severity === 'string' && (severity === 'error' || severity === 'warning' || severity === 'fatal')) { + errors = errors.filter((e: ErrorDiagnosticPayload) => e.metadata.severity === severity); + } + + // Limit results + const limitNum = parseInt(limit as string, 10) || 100; + errors = errors.slice(-limitNum); + + res.json({ + success: true, + count: errors.length, + errors, + }); + } catch (err) { + res.status(500).json({ + error: 'Internal server error', + message: (err as Error).message, + }); + } + }; +} + +/** + * Creates middleware for clearing stored errors (DELETE /_dev/errors) + * + * @param errorStore - Error store instance + * @param logger - Logger instance + * @returns Express middleware function + */ +export function createErrorClearMiddleware(errorStore: ErrorStore, logger: Logger) { + return (req: Request, res: Response, next: NextFunction): void => { + // Only handle DELETE requests to /_dev/errors + if (req.method !== 'DELETE' || !req.path.startsWith('/_dev/errors')) { + next(); + return; + } + + try { + const count = errorStore.getErrorCount(); + errorStore.clearErrors(); + + if (count > 0) { + // eslint-disable-next-line no-console + console.log(`\n๐Ÿงน [ErrorCapture] Cleared ${count} error(s) from store\n`); + } + logger.debug(`Cleared ${count} stored errors`); + + res.json({ + success: true, + message: `Cleared ${count} errors`, + clearedCount: count, + }); + } catch (err) { + res.status(500).json({ + error: 'Internal server error', + message: (err as Error).message, + }); + } + }; +} + +/** + * Creates middleware for getting error statistics (GET /_dev/errors/stats) + * + * @param errorStore - Error store instance + * @returns Express middleware function + */ +export function createErrorStatsMiddleware(errorStore: ErrorStore) { + return (req: Request, res: Response, next: NextFunction): void => { + // Only handle GET requests to /_dev/errors/stats + if (req.method !== 'GET' || req.path !== '/_dev/errors/stats') { + next(); + return; + } + + try { + const stats = errorStore.getStatistics(); + + res.json({ + success: true, + statistics: stats, + }); + } catch (err) { + res.status(500).json({ + error: 'Internal server error', + message: (err as Error).message, + }); + } + }; +} + +/** + * Combines all error-related middleware into a single middleware function + * + * @param config - Middleware configuration + * @returns Combined Express middleware function + */ +export function createCombinedErrorMiddleware( + config: ErrorMiddlewareConfig +): (req: Request, res: Response, next: NextFunction) => void { + const postMiddleware = createErrorMiddleware(config); + const getMiddleware = createErrorQueryMiddleware(config.errorStore); + const deleteMiddleware = createErrorClearMiddleware(config.errorStore, config.logger); + const statsMiddleware = createErrorStatsMiddleware(config.errorStore); + + return (req: Request, res: Response, next: NextFunction): void => { + // Route to appropriate handler based on path and method + if (req.path === '/_dev/errors/stats') { + statsMiddleware(req, res, next); + } else if (req.path.startsWith('/_dev/errors')) { + if (req.method === 'POST') { + void postMiddleware(req, res, next); + } else if (req.method === 'GET') { + getMiddleware(req, res, next); + } else if (req.method === 'DELETE') { + deleteMiddleware(req, res, next); + } else { + next(); + } + } else { + next(); + } + }; +} + +/** + * Middleware to enable CORS for error reporting endpoints + * Necessary for browser-based error reporting from Salesforce org pages + */ +export function createErrorCORSMiddleware() { + return (req: Request, res: Response, next: NextFunction): void => { + if (req.path.startsWith('/_dev/errors')) { + // Allow CORS from any origin for local development + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS'); + res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); + + // Handle preflight + if (req.method === 'OPTIONS') { + res.status(204).end(); + return; + } + } + next(); + }; +} diff --git a/src/lwc-dev-server/errorStore.ts b/src/lwc-dev-server/errorStore.ts new file mode 100644 index 0000000..fba4997 --- /dev/null +++ b/src/lwc-dev-server/errorStore.ts @@ -0,0 +1,258 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ErrorDiagnosticPayload } from '../types/errorPayload.js'; + +/** + * Statistics about errors in the store + */ +export type ErrorStatistics = { + totalErrors: number; + totalOccurrences: number; + byComponent: Record; + bySeverity: Record; +}; + +/** + * In-memory error store for runtime errors captured during LWC development. + * + * Features: + * - Maintains insertion order + * - Deduplicates errors with same signature (increments occurrenceCount) + * - Supports max size limit (FIFO eviction) + * - Provides filtering and statistics + */ +export class ErrorStore { + private errors: Map = new Map(); + private maxSize: number; + + /** + * Creates a new error store + * + * @param maxSize - Maximum number of unique errors to store (default: 1000) + */ + public constructor(maxSize = 1000) { + this.maxSize = maxSize; + } + + /** + * Generates a signature for an error based on its key characteristics. + * Used to identify duplicate errors. + * + * @param error - Error diagnostic payload + * @returns Signature string + */ + private static getErrorSignature(error: ErrorDiagnosticPayload): string { + const componentName = error.component.name ?? 'unknown'; + const fileName = error.source.fileName ?? 'unknown'; + const lineNumber = error.source.lineNumber ?? 0; + return `${error.error.message}|${componentName}|${fileName}|${lineNumber}`; + } + + /** + * Adds an error to the store. + * If an error with the same signature exists, increments its occurrence count. + * If max size is reached, removes the oldest error (FIFO). + * + * @param error - Error diagnostic payload + */ + public addError(error: ErrorDiagnosticPayload): void { + const signature = ErrorStore.getErrorSignature(error); + const existing = this.findErrorBySignature(signature); + + if (existing) { + // Increment occurrence count for duplicate error + existing.metadata.occurrenceCount++; + existing.timestamp = error.timestamp; // Update to latest timestamp + } else { + // Add new error + if (this.errors.size >= this.maxSize) { + // Remove oldest error (first entry) + const firstKey = this.errors.keys().next().value as string | undefined; + if (firstKey) { + this.errors.delete(firstKey); + } + } + this.errors.set(error.errorId, error); + } + } + + /** + * Gets an error by its ID + * + * @param errorId - Unique error identifier + * @returns Error or undefined if not found + */ + public getError(errorId: string): ErrorDiagnosticPayload | undefined { + return this.errors.get(errorId); + } + + /** + * Gets all errors in insertion order + * + * @returns Array of all errors + */ + public getErrors(): ErrorDiagnosticPayload[] { + return Array.from(this.errors.values()); + } + + /** + * Gets errors filtered by component name + * + * @param componentName - Component name to filter by + * @returns Array of matching errors + */ + public getErrorsByComponent(componentName: string): ErrorDiagnosticPayload[] { + return this.getErrors().filter((error) => error.component.name === componentName); + } + + /** + * Gets errors filtered by severity + * + * @param severity - Severity level to filter by + * @returns Array of matching errors + */ + public getErrorsBySeverity(severity: string): ErrorDiagnosticPayload[] { + return this.getErrors().filter((error) => error.metadata.severity === severity); + } + + /** + * Gets the N most recent errors + * + * @param count - Number of errors to retrieve + * @returns Array of most recent errors + */ + public getRecentErrors(count: number): ErrorDiagnosticPayload[] { + const errors = this.getErrors(); + return errors.slice(-count); + } + + /** + * Clears all errors from the store + */ + public clearErrors(): void { + this.errors.clear(); + } + + /** + * Gets the count of unique errors in the store + * + * @returns Number of errors + */ + public getErrorCount(): number { + return this.errors.size; + } + + /** + * Gets statistics about errors in the store + * + * @returns Statistics object + */ + public getStatistics(): ErrorStatistics { + const errors = this.getErrors(); + const stats: ErrorStatistics = { + totalErrors: errors.length, + totalOccurrences: 0, + byComponent: {}, + bySeverity: {}, + }; + + for (const error of errors) { + stats.totalOccurrences += error.metadata.occurrenceCount; + + // Count by component (use 'unknown' for null component names) + const componentName = error.component.name ?? 'unknown'; + stats.byComponent[componentName] = (stats.byComponent[componentName] || 0) + 1; + + // Count by severity + const severity = error.metadata.severity; + stats.bySeverity[severity] = (stats.bySeverity[severity] || 0) + 1; + } + + return stats; + } + + /** + * Exports all errors as JSON + * + * @returns JSON string of all errors + */ + public exportAsJSON(): string { + return JSON.stringify(this.getErrors(), null, 2); + } + + /** + * Imports errors from JSON string + * + * @param json - JSON string containing errors + * @returns Number of errors imported + */ + public importFromJSON(json: string): number { + try { + const errors = JSON.parse(json) as ErrorDiagnosticPayload[]; + if (!Array.isArray(errors)) { + return 0; + } + + for (const error of errors) { + this.addError(error); + } + + return errors.length; + } catch { + return 0; + } + } + + /** + * Finds an error with the same signature + * + * @param signature - Error signature to search for + * @returns Matching error or undefined + */ + private findErrorBySignature(signature: string): ErrorDiagnosticPayload | undefined { + for (const error of this.errors.values()) { + if (ErrorStore.getErrorSignature(error) === signature) { + return error; + } + } + return undefined; + } +} + +/** + * Singleton error store instance + */ +let errorStoreInstance: ErrorStore | null = null; + +/** + * Gets the singleton error store instance + * + * @returns Error store instance + */ +export function getErrorStore(): ErrorStore { + if (!errorStoreInstance) { + errorStoreInstance = new ErrorStore(); + } + return errorStoreInstance; +} + +/** + * Resets the singleton error store instance (useful for testing) + */ +export function resetErrorStore(): void { + errorStoreInstance = null; +} diff --git a/src/lwc-dev-server/index.ts b/src/lwc-dev-server/index.ts index 731678a..62835c5 100644 --- a/src/lwc-dev-server/index.ts +++ b/src/lwc-dev-server/index.ts @@ -24,6 +24,8 @@ import { LOCAL_DEV_SERVER_DEFAULT_HTTP_PORT, LOCAL_DEV_SERVER_DEFAULT_WORKSPACE, } from '../shared/configUtils.js'; +import { getErrorStore } from './errorStore.js'; +import { startErrorCaptureServer, type ErrorCaptureServer } from './errorHttpServer.js'; async function createLWCServerConfig( rootDir: string, @@ -87,9 +89,78 @@ export async function startLWCServer( logger.trace(`Starting LWC Dev Server with config: ${JSON.stringify(config)}`); let lwcDevServer: LWCServer | null = await startLwcDevServer(config, logger); + // ============================================================ + // Start standalone error capture HTTP server + // ============================================================ + const errorStore = getErrorStore(); + const errorCapturePort = (serverPorts?.httpPort ?? LOCAL_DEV_SERVER_DEFAULT_HTTP_PORT) + 1; + + let errorCaptureServer: ErrorCaptureServer | null = null; + + try { + errorCaptureServer = await startErrorCaptureServer({ + port: errorCapturePort, + errorStore, + logger, + projectRoot: rootDir, + logToConsole: true, + localhostOnly: true, // Bind to localhost only for security + }); + + // eslint-disable-next-line no-console + console.log('\nโœ… [ErrorCapture] Error capture system initialized'); + // eslint-disable-next-line no-console + console.log(`๐Ÿ“ก [ErrorCapture] LWC Dev Server (WebSocket): ws://localhost:${config.port}`); + // eslint-disable-next-line no-console + console.log(`๐Ÿ” [ErrorCapture] Error Capture Server (HTTP): http://localhost:${errorCapturePort}`); + // eslint-disable-next-line no-console + console.log('๐Ÿ’ก [ErrorCapture] Tip: Errors clear automatically on server restart'); + // eslint-disable-next-line no-console + console.log(` Or manually: curl -X DELETE http://localhost:${errorCapturePort}/_dev/errors\n`); + logger.info('[ErrorCapture] Error capture system initialized'); + } catch (err) { + // eslint-disable-next-line no-console + console.log( + `\nโš ๏ธ [ErrorCapture] Failed to start error capture server on port ${errorCapturePort}: ${ + err instanceof Error ? err.message : String(err) + }` + ); + // eslint-disable-next-line no-console + console.log( + 'โš ๏ธ [ErrorCapture] Error capture will not be available. This does not affect LWC dev server functionality.\n' + ); + logger.warn( + `[ErrorCapture] Failed to start error capture server on port ${errorCapturePort}: ${ + err instanceof Error ? err.message : String(err) + }` + ); + } + const cleanup = (): void => { if (lwcDevServer) { - logger.trace('Stopping LWC Dev Server'); + logger.trace('Stopping LWC Dev Server and Error Capture Server'); + + // Show error statistics before shutdown + const stats = errorStore.getStatistics(); + if (stats.totalErrors > 0) { + // eslint-disable-next-line no-console + console.log(`\n๐Ÿ“Š [ErrorCapture] Captured ${stats.totalErrors} unique error(s) during this session`); + // eslint-disable-next-line no-console + console.log(`๐Ÿ“Š [ErrorCapture] Total occurrences: ${stats.totalOccurrences}\n`); + logger.info(`[ErrorCapture] Captured ${stats.totalErrors} errors during this session`); + } + + // Stop error capture server first + if (errorCaptureServer) { + errorCaptureServer.stop().catch((err) => { + logger.error( + `[ErrorCapture] Error stopping error capture server: ${err instanceof Error ? err.message : String(err)}` + ); + }); + errorCaptureServer = null; + } + + // Then stop LWC dev server lwcDevServer.stopServer(); lwcDevServer = null; } @@ -103,3 +174,8 @@ export async function startLWCServer( return lwcDevServer; } + +/** + * Exports the error store for external access (e.g., for testing or CLI commands) + */ +export { getErrorStore, resetErrorStore } from './errorStore.js'; diff --git a/src/shared/errorFormatter.ts b/src/shared/errorFormatter.ts new file mode 100644 index 0000000..4cf6464 --- /dev/null +++ b/src/shared/errorFormatter.ts @@ -0,0 +1,302 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ErrorDiagnosticPayload, StackFrame } from '../types/errorPayload.js'; +import { filterLocalFrames } from './stackTraceUtils.js'; + +/** + * ANSI color codes for terminal output + */ +const COLORS = { + reset: '\x1b[0m', + bright: '\x1b[1m', + dim: '\x1b[2m', + red: '\x1b[31m', + green: '\x1b[32m', + yellow: '\x1b[33m', + blue: '\x1b[34m', + magenta: '\x1b[35m', + cyan: '\x1b[36m', + white: '\x1b[37m', + gray: '\x1b[90m', + bgRed: '\x1b[41m', + bgYellow: '\x1b[43m', +}; + +/** + * Formats an error diagnostic payload for CLI display with colors and structure + * + * @param error - Error diagnostic payload to format + * @param options - Formatting options + * @returns Formatted string ready for console output + */ +export function formatErrorForCLI(error: ErrorDiagnosticPayload, options: FormatterOptions = {}): string { + const { showFullStack = false, colorize = true, compact = false } = options; + + const c = colorize ? COLORS : createNoColorPalette(); + + const lines: string[] = []; + + // Header with severity badge + const severityBadge = getSeverityBadge(error.metadata.severity, c); + const timestamp = new Date(error.timestamp).toLocaleTimeString(); + + lines.push(''); + lines.push(`${severityBadge} ${c.bright}${error.error.name}${c.reset} ${c.gray}[${timestamp}]${c.reset}`); + + // Error message + lines.push(`${c.red}${error.error.message}${c.reset}`); + lines.push(''); + + // Component context (if available) + if (error.component.name) { + lines.push(`${c.cyan}Component:${c.reset} ${c.bright}${error.component.name}${c.reset}`); + if (error.component.lifecycle) { + lines.push(`${c.cyan}Lifecycle:${c.reset} ${error.component.lifecycle}`); + } + lines.push(''); + } + + // Source location + if (error.source.fileName) { + const location = `${error.source.fileName}:${String(error.source.lineNumber ?? 0)}:${String( + error.source.columnNumber ?? 0 + )}`; + lines.push(`${c.cyan}Location:${c.reset} ${location}`); + lines.push(''); + } + + // Stack trace + if (!compact) { + const frames = showFullStack ? error.error.sanitizedStack : filterLocalFrames(error.error.sanitizedStack); + + if (frames.length > 0) { + lines.push(`${c.cyan}Stack Trace:${c.reset}`); + frames.forEach((frame, index) => { + lines.push(formatStackFrameForCLI(frame, index, c)); + }); + + if (!showFullStack && error.error.sanitizedStack.length > frames.length) { + const hiddenCount = error.error.sanitizedStack.length - frames.length; + lines.push(`${c.gray} ... ${hiddenCount} more frames (framework/library code)${c.reset}`); + } + lines.push(''); + } + } + + // Occurrence count if > 1 + if (error.metadata.occurrenceCount > 1) { + lines.push( + `${c.yellow}โš ${c.reset} ${c.gray}This error occurred ${c.bright}${error.metadata.occurrenceCount}${c.reset}${c.gray} times${c.reset}` + ); + lines.push(''); + } + + // Separator + lines.push(`${c.gray}${'โ”€'.repeat(80)}${c.reset}`); + + return lines.join('\n'); +} + +/** + * Formats a stack frame for CLI display + */ +function formatStackFrameForCLI(frame: StackFrame, index: number, colors: ColorPalette): string { + const funcName = frame.functionName ?? ''; + const location = `${frame.fileName}:${String(frame.lineNumber)}:${String(frame.columnNumber)}`; + + if (frame.isLocalSource) { + return ` ${colors.gray}${index + 1}.${colors.reset} ${colors.bright}${funcName}${colors.reset} ${ + colors.dim + }(${location})${colors.reset}`; + } else { + return ` ${colors.gray}${index + 1}. ${funcName} (${location})${colors.reset}`; + } +} + +/** + * Gets a colored severity badge + */ +function getSeverityBadge(severity: 'error' | 'warning' | 'fatal', colors: ColorPalette): string { + switch (severity) { + case 'fatal': + return `${colors.bgRed}${colors.white} FATAL ${colors.reset}`; + case 'error': + return `${colors.red}โœ–${colors.reset}`; + case 'warning': + return `${colors.yellow}โš ${colors.reset}`; + default: + return 'โ€ข'; + } +} + +/** + * Formats an error as a compact single-line summary + * + * @param error - Error diagnostic payload + * @param colorize - Whether to use colors + * @returns Compact formatted string + */ +export function formatErrorCompact(error: ErrorDiagnosticPayload, colorize = true): string { + const c = colorize ? COLORS : createNoColorPalette(); + + const timestamp = new Date(error.timestamp).toLocaleTimeString(); + const component = error.component.name ?? 'unknown'; + const location = error.source.fileName + ? `${error.source.fileName}:${String(error.source.lineNumber ?? 0)}` + : 'unknown'; + + return `${c.red}โœ–${c.reset} ${c.bright}${error.error.name}${c.reset}: ${error.error.message} ${c.gray}(${component} @ ${location}) [${timestamp}]${c.reset}`; +} + +/** + * Formats multiple errors as a summary + * + * @param errors - Array of error diagnostic payloads + * @param colorize - Whether to use colors + * @returns Formatted summary string + */ +export function formatErrorSummary(errors: ErrorDiagnosticPayload[], colorize = true): string { + const c = colorize ? COLORS : createNoColorPalette(); + + if (errors.length === 0) { + return `${c.green}โœ“${c.reset} No errors captured`; + } + + const lines: string[] = []; + lines.push(''); + lines.push(`${c.bright}Error Summary${c.reset} ${c.gray}(${errors.length} total)${c.reset}`); + lines.push(`${c.gray}${'โ”€'.repeat(80)}${c.reset}`); + lines.push(''); + + // Group by component + const byComponent = new Map(); + for (const error of errors) { + const componentName = error.component.name ?? 'unknown'; + if (!byComponent.has(componentName)) { + byComponent.set(componentName, []); + } + byComponent.get(componentName)!.push(error); + } + + // Display grouped errors + for (const [component, componentErrors] of byComponent) { + lines.push(`${c.cyan}${component}${c.reset} ${c.gray}(${componentErrors.length} errors)${c.reset}`); + for (const error of componentErrors.slice(0, 3)) { + // Show up to 3 per component + lines.push(` ${formatErrorCompact(error, colorize)}`); + } + if (componentErrors.length > 3) { + lines.push(` ${c.gray}... ${componentErrors.length - 3} more errors${c.reset}`); + } + lines.push(''); + } + + return lines.join('\n'); +} + +/** + * Formats error statistics + * + * @param stats - Error statistics object + * @param colorize - Whether to use colors + * @returns Formatted statistics string + */ +export function formatErrorStatistics( + stats: { + totalErrors: number; + totalOccurrences: number; + byComponent: Record; + bySeverity: { error: number; warning: number; fatal: number }; + }, + colorize = true +): string { + const c = colorize ? COLORS : createNoColorPalette(); + + const lines: string[] = []; + lines.push(''); + lines.push(`${c.bright}Error Statistics${c.reset}`); + lines.push(`${c.gray}${'โ”€'.repeat(80)}${c.reset}`); + lines.push(''); + lines.push(`Total Errors: ${c.bright}${stats.totalErrors}${c.reset}`); + lines.push(`Total Occurrences: ${c.bright}${stats.totalOccurrences}${c.reset}`); + lines.push(''); + + lines.push(`${c.cyan}By Severity:${c.reset}`); + lines.push(` Fatal: ${c.red}${stats.bySeverity.fatal}${c.reset}`); + lines.push(` Error: ${c.yellow}${stats.bySeverity.error}${c.reset}`); + lines.push(` Warning: ${c.yellow}${stats.bySeverity.warning}${c.reset}`); + lines.push(''); + + if (Object.keys(stats.byComponent).length > 0) { + lines.push(`${c.cyan}By Component:${c.reset}`); + for (const [component, count] of Object.entries(stats.byComponent)) { + lines.push(` ${component}: ${c.bright}${count}${c.reset}`); + } + lines.push(''); + } + + return lines.join('\n'); +} + +/** + * Formats an error as JSON for logging systems + * + * @param error - Error diagnostic payload + * @param pretty - Whether to pretty-print JSON + * @returns JSON string + */ +export function formatErrorAsJSON(error: ErrorDiagnosticPayload, pretty = false): string { + return JSON.stringify(error, null, pretty ? 2 : 0); +} + +/** + * Creates a color palette with all empty strings (for no-color output) + */ +function createNoColorPalette(): ColorPalette { + return { + reset: '', + bright: '', + dim: '', + red: '', + green: '', + yellow: '', + blue: '', + magenta: '', + cyan: '', + white: '', + gray: '', + bgRed: '', + bgYellow: '', + }; +} + +/** + * Formatter options + */ +export type FormatterOptions = { + /** Whether to show the full stack trace (including framework code) */ + showFullStack?: boolean; + /** Whether to colorize output */ + colorize?: boolean; + /** Whether to use compact formatting */ + compact?: boolean; +}; + +/** + * Color palette type + */ +type ColorPalette = typeof COLORS; diff --git a/src/shared/stackTraceUtils.ts b/src/shared/stackTraceUtils.ts new file mode 100644 index 0000000..1f3b07c --- /dev/null +++ b/src/shared/stackTraceUtils.ts @@ -0,0 +1,326 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import path from 'node:path'; +import { StackFrame } from '../types/errorPayload.js'; + +/** + * Parses a stack trace string into structured StackFrame objects. + * Supports Chrome, Firefox, and Safari stack trace formats. + * + * @param stack - Raw stack trace string + * @param projectRoot - Project root directory for determining local sources + * @returns Array of parsed stack frames + */ +export function parseStackTrace(stack: string, projectRoot?: string): StackFrame[] { + if (!stack) { + return []; + } + + const lines = stack.split('\n'); + const frames: StackFrame[] = []; + + for (const line of lines) { + const frame = parseStackFrame(line, projectRoot); + if (frame) { + frames.push(frame); + } + } + + return frames; +} + +/** + * Parses a single line of a stack trace into a StackFrame. + * Handles multiple browser formats. + * + * @param line - Single line from stack trace + * @param projectRoot - Project root directory + * @returns Parsed StackFrame or null if parsing fails + */ +export function parseStackFrame(line: string, projectRoot?: string): StackFrame | null { + const trimmedLine = line.trim(); + + // Try Chrome/Node format: "at FunctionName (file:line:column)" or "at file:line:column" + const chromeMatch = trimmedLine.match(/^\s*at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?$/); + if (chromeMatch) { + const [, functionName, fileName, lineNumber, columnNumber] = chromeMatch; + return createStackFrame( + functionName?.trim() || null, + fileName, + parseInt(lineNumber, 10), + parseInt(columnNumber, 10), + projectRoot, + trimmedLine + ); + } + + // Try Firefox format: "FunctionName@file:line:column" + const firefoxMatch = trimmedLine.match(/^(.+?)@(.+?):(\d+):(\d+)$/); + if (firefoxMatch) { + const [, functionName, fileName, lineNumber, columnNumber] = firefoxMatch; + return createStackFrame( + functionName?.trim() || null, + fileName, + parseInt(lineNumber, 10), + parseInt(columnNumber, 10), + projectRoot, + trimmedLine + ); + } + + // Try Safari format: "FunctionName@[native code]" or "FunctionName@file:line:column" + const safariMatch = trimmedLine.match(/^(.+?)@(.+?)$/); + if (safariMatch) { + const [, functionName, location] = safariMatch; + + // Handle [native code] + if (location === '[native code]') { + return createStackFrame(functionName?.trim() || null, '[native code]', 0, 0, projectRoot, trimmedLine); + } + + // Try to extract line:column + const locationMatch = location.match(/(.+?):(\d+):(\d+)$/); + if (locationMatch) { + const [, fileName, lineNumber, columnNumber] = locationMatch; + return createStackFrame( + functionName?.trim() || null, + fileName, + parseInt(lineNumber, 10), + parseInt(columnNumber, 10), + projectRoot, + trimmedLine + ); + } + } + + // Try simple format: "file:line:column" (no function name) + const simpleMatch = trimmedLine.match(/^(.+?):(\d+):(\d+)$/); + if (simpleMatch) { + const [, fileName, lineNumber, columnNumber] = simpleMatch; + return createStackFrame( + null, + fileName, + parseInt(lineNumber, 10), + parseInt(columnNumber, 10), + projectRoot, + trimmedLine + ); + } + + // Could not parse this line + return null; +} + +/** + * Creates a StackFrame object with all fields populated + */ +function createStackFrame( + functionName: string | null, + fileName: string, + lineNumber: number, + columnNumber: number, + projectRoot: string | undefined, + raw: string +): StackFrame { + return { + functionName, + fileName: sanitizeFileName(fileName), + lineNumber, + columnNumber, + isLocalSource: isLocalSource(fileName, projectRoot), + raw, + }; +} + +/** + * Sanitizes a file name by removing webpack/bundler artifacts + * + * @param fileName - Raw file name from stack trace + * @returns Cleaned file name + */ +export function sanitizeFileName(fileName: string): string { + if (!fileName) { + return fileName; + } + + let sanitized = fileName; + + // Remove webpack internal paths + sanitized = sanitized.replace(/webpack:\/\/\//g, ''); + sanitized = sanitized.replace(/webpack-internal:\/\/\//g, ''); + + // Remove query parameters + sanitized = sanitized.split('?')[0]; + + // Remove hash fragments + sanitized = sanitized.split('#')[0]; + + // Convert file:// URLs to paths + if (sanitized.startsWith('file://')) { + try { + sanitized = new URL(sanitized).pathname; + } catch { + sanitized = sanitized.replace('file://', ''); + } + } + + // Convert http/https URLs to just the pathname + if (sanitized.startsWith('http://') || sanitized.startsWith('https://')) { + try { + const url = new URL(sanitized); + sanitized = url.pathname; + } catch { + // Keep as is if URL parsing fails + } + } + + return sanitized; +} + +/** + * Determines if a file path refers to local project source code + * + * @param fileName - File name or path + * @param projectRoot - Project root directory + * @returns True if this is local source code + */ +export function isLocalSource(fileName: string, projectRoot?: string): boolean { + if (!fileName) { + return false; + } + + const sanitized = sanitizeFileName(fileName); + + // Check for node_modules + if (sanitized.includes('node_modules')) { + return false; + } + + // Check for browser/framework internals + if (sanitized.includes('[native code]') || sanitized.startsWith('') || sanitized.startsWith('eval at')) { + return false; + } + + // Check for external URLs + if (sanitized.startsWith('http://') || sanitized.startsWith('https://')) { + return false; + } + + // If we have a project root, check if file is within it + if (projectRoot) { + try { + const absolutePath = path.isAbsolute(sanitized) ? sanitized : path.resolve(projectRoot, sanitized); + const relativePath = path.relative(projectRoot, absolutePath); + // If path doesn't start with '..' it's within project root + return !relativePath.startsWith('..'); + } catch { + return false; + } + } + + // If no project root, assume local if it looks like a relative path + return !sanitized.startsWith('http') && !sanitized.startsWith('['); +} + +/** + * Extracts the likely component name from a stack trace. + * Looks for patterns like "c-component-name" or "namespace-component-name" + * + * @param frames - Parsed stack frames + * @returns Component name or null + */ +export function extractComponentNameFromStack(frames: StackFrame[]): string | null { + for (const frame of frames) { + // Look in file names + const fileMatch = frame.fileName.match(/([a-z]+-[a-z-]+)\.js/i); + if (fileMatch) { + return fileMatch[1]; + } + + // Look in function names + if (frame.functionName) { + const funcMatch = frame.functionName.match(/([a-z]+-[a-z-]+)/i); + if (funcMatch) { + return funcMatch[1]; + } + } + } + + return null; +} + +/** + * Extracts the likely lifecycle hook from a stack trace. + * Looks for patterns like "connectedCallback", "disconnectedCallback", etc. + * + * @param frames - Parsed stack frames + * @returns Lifecycle hook name or null + */ +export function extractLifecycleHookFromStack(frames: StackFrame[]): string | null { + const lifecycleHooks = [ + 'constructor', + 'connectedCallback', + 'disconnectedCallback', + 'renderedCallback', + 'errorCallback', + 'render', + ]; + + for (const frame of frames) { + if (frame.functionName) { + const lowerFunc = frame.functionName.toLowerCase(); + for (const hook of lifecycleHooks) { + if (lowerFunc.includes(hook.toLowerCase())) { + return hook; + } + } + } + } + + return null; +} + +/** + * Filters stack frames to only include local source files + * + * @param frames - Stack frames to filter + * @returns Filtered array of local frames + */ +export function filterLocalFrames(frames: StackFrame[]): StackFrame[] { + return frames.filter((frame) => frame.isLocalSource); +} + +/** + * Formats a stack frame for display + * + * @param frame - Stack frame to format + * @returns Formatted string + */ +export function formatStackFrame(frame: StackFrame): string { + const funcName = frame.functionName ?? ''; + return `${funcName} (${frame.fileName}:${String(frame.lineNumber)}:${String(frame.columnNumber)})`; +} + +/** + * Formats an array of stack frames for display + * + * @param frames - Stack frames to format + * @returns Formatted multi-line string + */ +export function formatStackTrace(frames: StackFrame[]): string { + return frames.map((frame, index) => ` ${index + 1}. ${formatStackFrame(frame)}`).join('\n'); +} diff --git a/src/types/errorPayload.ts b/src/types/errorPayload.ts new file mode 100644 index 0000000..2f89e29 --- /dev/null +++ b/src/types/errorPayload.ts @@ -0,0 +1,195 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Represents a single frame in a parsed stack trace. + */ +export type StackFrame = { + /** Function name, or null if anonymous */ + functionName: string | null; + /** File path or URL where the error occurred */ + fileName: string; + /** Line number in the source file */ + lineNumber: number; + /** Column number in the source file */ + columnNumber: number; + /** Whether this frame references local project source code */ + isLocalSource: boolean; + /** Original unparsed stack frame string */ + raw?: string; +}; + +/** + * Core error diagnostic payload structure. + * This is the standardized format for all runtime errors captured during local development. + */ +export type ErrorDiagnosticPayload = { + /** Unique identifier for this error instance (UUID v4) */ + errorId: string; + + /** ISO 8601 timestamp when the error was captured */ + timestamp: string; + + /** Detailed error information */ + error: { + /** Error message */ + message: string; + /** Error type (TypeError, ReferenceError, SyntaxError, etc.) */ + name: string; + /** Full stack trace as a string */ + stack: string; + /** Parsed and sanitized stack frames */ + sanitizedStack: StackFrame[]; + /** Error code if available */ + code?: string; + }; + + /** LWC component context information */ + component: { + /** Component name (e.g., 'c-hello-world', 'myNamespace-my-component') */ + name: string | null; + /** Component namespace (e.g., 'c', 'myNamespace') */ + namespace: string | null; + /** Custom element tag name (e.g., 'c-hello-world') */ + tagName: string | null; + /** Lifecycle hook where error occurred (if detectable) */ + lifecycle: string | null; + /** Local file path to the component source */ + filePath: string | null; + }; + + /** Runtime environment context */ + runtime: { + /** Browser user agent string */ + userAgent: string; + /** Viewport dimensions */ + viewport: { + width: number; + height: number; + }; + /** Current page URL */ + url: string; + /** LWC framework version if detectable */ + lwcVersion: string | null; + /** Development mode indicator */ + isDevelopment: boolean; + }; + + /** Component state at time of error (limited to prevent circular refs) */ + state: { + /** Public properties/attributes (sanitized, max depth 3) */ + props: Record | null; + /** Public property names */ + publicProperties: string[]; + /** Whether component was connected to DOM */ + isConnected: boolean; + }; + + /** Precise error location in source code */ + source: { + /** Source file name */ + fileName: string | null; + /** Line number where error occurred */ + lineNumber: number | null; + /** Column number where error occurred */ + columnNumber: number | null; + }; + + /** Additional metadata */ + metadata: { + /** Error severity level */ + severity: 'error' | 'warning' | 'fatal'; + /** Whether error was handled by a boundary */ + wasHandled: boolean; + /** Number of times this error occurred (for deduplication) */ + occurrenceCount: number; + /** Tags for categorization */ + tags: string[]; + }; +}; + +/** + * Simplified error payload for transmission over network. + * Excludes redundant data to minimize payload size. + */ +export type ErrorPayloadDTO = { + errorId: string; + timestamp: string; + error: { + message: string; + name: string; + stack: string; + }; + component: { + name: string | null; + tagName: string | null; + lifecycle: string | null; + }; + source: { + fileName: string | null; + lineNumber: number | null; + columnNumber: number | null; + }; + metadata: { + severity: 'error' | 'warning' | 'fatal'; + }; +}; + +/** + * Configuration options for error capture system + */ +export type ErrorCaptureConfig = { + /** Whether to capture errors */ + enabled: boolean; + /** Local dev server URL */ + serverUrl: string; + /** Whether to log errors to console */ + logToConsole: boolean; + /** Maximum stack trace depth to capture */ + maxStackDepth: number; + /** Maximum number of errors to store in memory */ + maxStoredErrors: number; + /** Debounce time for duplicate errors (ms) */ + debounceTime: number; +}; + +/** + * Type guard to check if an error is an Error object + */ +export function isError(error: unknown): error is Error { + return error instanceof Error || (typeof error === 'object' && error !== null && 'message' in error); +} + +/** + * Type guard to check if a payload is a valid ErrorDiagnosticPayload + */ +export function isValidErrorPayload(payload: unknown): payload is ErrorDiagnosticPayload { + if (typeof payload !== 'object' || payload === null) { + return false; + } + + const p = payload as Partial; + + return ( + typeof p.errorId === 'string' && + typeof p.timestamp === 'string' && + typeof p.error === 'object' && + p.error !== null && + typeof p.error.message === 'string' && + typeof p.error.name === 'string' && + Array.isArray(p.error.sanitizedStack) + ); +} diff --git a/test/fixtures/errorTestComponent/errorTestComponent.html b/test/fixtures/errorTestComponent/errorTestComponent.html new file mode 100644 index 0000000..6b47713 --- /dev/null +++ b/test/fixtures/errorTestComponent/errorTestComponent.html @@ -0,0 +1,8 @@ + diff --git a/test/fixtures/errorTestComponent/errorTestComponent.js b/test/fixtures/errorTestComponent/errorTestComponent.js new file mode 100644 index 0000000..7e82f55 --- /dev/null +++ b/test/fixtures/errorTestComponent/errorTestComponent.js @@ -0,0 +1,29 @@ +/** + * Test component with deliberate errors for testing error capture system + */ +import { LightningElement, track } from 'lwc'; + +export default class ErrorTestComponent extends LightningElement { + @track counter = 0; + + connectedCallback() { + // Deliberate error: calling undefined method + this.nonExistentMethod(); + } + + handleClick() { + // Deliberate error: accessing undefined property + console.log(this.undefinedProp.someMethod()); + } + + renderedCallback() { + // Intentional error to test lifecycle detection + if (this.counter > 5) { + throw new Error('Counter exceeded maximum value in renderedCallback'); + } + } + + handleIncrement() { + this.counter++; + } +} diff --git a/test/fixtures/errorTestComponent/errorTestComponent.js-meta.xml b/test/fixtures/errorTestComponent/errorTestComponent.js-meta.xml new file mode 100644 index 0000000..52f8e4c --- /dev/null +++ b/test/fixtures/errorTestComponent/errorTestComponent.js-meta.xml @@ -0,0 +1,14 @@ + + + 66.0 + true + Error Test Component + Component for testing error capture system with deliberate errors + + lightning__AppPage + lightning__RecordPage + lightning__HomePage + + + + diff --git a/test/fixtures/errorTestProject/force-app/main/default/lwc/errorTestComponent/errorTestComponent.html b/test/fixtures/errorTestProject/force-app/main/default/lwc/errorTestComponent/errorTestComponent.html new file mode 100644 index 0000000..b7ad19c --- /dev/null +++ b/test/fixtures/errorTestProject/force-app/main/default/lwc/errorTestComponent/errorTestComponent.html @@ -0,0 +1,3 @@ + diff --git a/test/fixtures/errorTestProject/force-app/main/default/lwc/errorTestComponent/errorTestComponent.js b/test/fixtures/errorTestProject/force-app/main/default/lwc/errorTestComponent/errorTestComponent.js new file mode 100644 index 0000000..80c44ae --- /dev/null +++ b/test/fixtures/errorTestProject/force-app/main/default/lwc/errorTestComponent/errorTestComponent.js @@ -0,0 +1,8 @@ +import { LightningElement } from 'lwc'; + +export default class ErrorTestComponent extends LightningElement { + connectedCallback() { + // Deliberately cause a ReferenceError + this.nonExistentMethod(); + } +} diff --git a/test/fixtures/errorTestProject/force-app/main/default/lwc/errorTestComponent/errorTestComponent.js-meta.xml b/test/fixtures/errorTestProject/force-app/main/default/lwc/errorTestComponent/errorTestComponent.js-meta.xml new file mode 100644 index 0000000..61e8026 --- /dev/null +++ b/test/fixtures/errorTestProject/force-app/main/default/lwc/errorTestComponent/errorTestComponent.js-meta.xml @@ -0,0 +1,10 @@ + + + 58.0 + true + + lightning__AppPage + lightning__RecordPage + lightning__HomePage + + \ No newline at end of file diff --git a/test/fixtures/errorTestProject/sfdx-project.json b/test/fixtures/errorTestProject/sfdx-project.json new file mode 100644 index 0000000..cc1b8b0 --- /dev/null +++ b/test/fixtures/errorTestProject/sfdx-project.json @@ -0,0 +1,11 @@ +{ + "packageDirectories": [ + { + "path": "force-app", + "default": true + } + ], + "namespace": "", + "sfdcLoginUrl": "https://login.salesforce.com", + "sourceApiVersion": "58.0" +} diff --git a/test/lwc-dev-server/errorCapture.e2e.test.ts b/test/lwc-dev-server/errorCapture.e2e.test.ts new file mode 100644 index 0000000..894154a --- /dev/null +++ b/test/lwc-dev-server/errorCapture.e2e.test.ts @@ -0,0 +1,365 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { expect } from 'chai'; +import { Logger } from '@salesforce/core'; +import { getErrorStore, resetErrorStore } from '../../src/lwc-dev-server/errorStore.js'; +import { ErrorDiagnosticPayload } from '../../src/types/errorPayload.js'; + +// ESM compatibility: create __dirname equivalent +// eslint-disable-next-line no-underscore-dangle +const __filename = fileURLToPath(import.meta.url); +// eslint-disable-next-line no-underscore-dangle +const __dirname = path.dirname(__filename); + +/** + * Helper function to create a valid test error payload + */ +function createTestError(overrides?: Partial): ErrorDiagnosticPayload { + const baseError: ErrorDiagnosticPayload = { + errorId: 'test-error-1', + timestamp: new Date().toISOString(), + error: { + name: 'Error', + message: 'Test error', + stack: 'Error: Test error\n at test.js:5:10', + sanitizedStack: [], + }, + component: { + name: 'testComponent', + namespace: 'c', + tagName: 'c-test-component', + lifecycle: null, + filePath: null, + }, + source: { + fileName: 'test.js', + lineNumber: 5, + columnNumber: 10, + }, + runtime: { + userAgent: 'Mozilla/5.0', + viewport: { width: 1920, height: 1080 }, + url: 'http://localhost:8081', + lwcVersion: null, + isDevelopment: true, + }, + state: { + props: null, + publicProperties: [], + isConnected: true, + }, + metadata: { + severity: 'error', + wasHandled: false, + occurrenceCount: 1, + tags: [], + }, + }; + + return { ...baseError, ...overrides }; +} + +/** + * E2E test for error capture system + * + * This test verifies the complete error capture flow: + * 1. Start LWC dev server with error capture enabled + * 2. Simulate an error being sent from the client-side + * 3. Verify the error is captured and stored + * 4. Verify the error can be retrieved + */ +describe('Error Capture E2E', function () { + // Increase timeout for server startup + this.timeout(30_000); + + let errorStore: ReturnType; + + before(async () => { + const logger = await Logger.child('ErrorCaptureE2E'); + logger.debug('Initializing E2E test'); + // Create a test project directory + const testProjectDir = path.join(__dirname, '../fixtures/errorTestProject'); + + // Ensure the test project directory exists + if (!fs.existsSync(testProjectDir)) { + fs.mkdirSync(testProjectDir, { recursive: true }); + + // Create a basic sfdx-project.json + const sfdxProjectPath = path.join(testProjectDir, 'sfdx-project.json'); + fs.writeFileSync( + sfdxProjectPath, + JSON.stringify( + { + packageDirectories: [ + { + path: 'force-app', + default: true, + }, + ], + namespace: '', + sfdcLoginUrl: 'https://login.salesforce.com', + sourceApiVersion: '58.0', + }, + null, + 2 + ) + ); + + // Create force-app/main/default/lwc directory + const lwcDir = path.join(testProjectDir, 'force-app/main/default/lwc'); + fs.mkdirSync(lwcDir, { recursive: true }); + + // Create error test component + const componentDir = path.join(lwcDir, 'errorTestComponent'); + fs.mkdirSync(componentDir, { recursive: true }); + + // Component JS + fs.writeFileSync( + path.join(componentDir, 'errorTestComponent.js'), + `import { LightningElement } from 'lwc'; + +export default class ErrorTestComponent extends LightningElement { + connectedCallback() { + // Deliberately cause a ReferenceError + this.nonExistentMethod(); + } +}` + ); + + // Component HTML + fs.writeFileSync( + path.join(componentDir, 'errorTestComponent.html'), + `` + ); + + // Component metadata + fs.writeFileSync( + path.join(componentDir, 'errorTestComponent.js-meta.xml'), + ` + + 58.0 + true + + lightning__AppPage + lightning__RecordPage + lightning__HomePage + + ` + ); + } + }); + + beforeEach(() => { + // Reset error store before each test + resetErrorStore(); + errorStore = getErrorStore(); + }); + + it('should initialize error store when server starts', () => { + expect(errorStore).to.exist; + expect(errorStore.getErrorCount()).to.equal(0); + }); + + it('should capture and store errors sent to /_dev/errors endpoint', async () => { + const testError = createTestError({ + errorId: 'test-error-123', + error: { + name: 'ReferenceError', + message: 'nonExistentMethod is not defined', + stack: + 'ReferenceError: nonExistentMethod is not defined\n' + + ' at ErrorTestComponent.connectedCallback (errorTestComponent.js:5:10)\n' + + ' at callHook (lwc-engine.js:123:45)', + sanitizedStack: [], + }, + component: { + name: 'errorTestComponent', + namespace: 'c', + tagName: 'c-error-test-component', + lifecycle: 'connectedCallback', + filePath: null, + }, + source: { + fileName: 'errorTestComponent.js', + lineNumber: 5, + columnNumber: 10, + }, + metadata: { + severity: 'error', + wasHandled: false, + occurrenceCount: 1, + tags: ['runtime', 'lwc'], + }, + }); + + // Simulate error capture by directly adding to store + // In a real E2E test, this would come from an HTTP POST to /_dev/errors + errorStore.addError(testError); + + // Verify error was stored + expect(errorStore.getErrorCount()).to.equal(1); + + // Retrieve the error + const errors = errorStore.getErrors(); + expect(errors).to.have.lengthOf(1); + + const capturedError = errors[0]; + expect(capturedError.errorId).to.equal('test-error-123'); + expect(capturedError.error.name).to.equal('ReferenceError'); + expect(capturedError.component.name).to.equal('errorTestComponent'); + expect(capturedError.component.lifecycle).to.equal('connectedCallback'); + }); + + it('should deduplicate identical errors', () => { + const testError = createTestError({ + errorId: 'test-error-1', + error: { + name: 'TypeError', + message: 'Cannot read property of undefined', + stack: 'TypeError: Cannot read property of undefined\n at test.js:10:5', + sanitizedStack: [], + }, + source: { + fileName: 'test.js', + lineNumber: 10, + columnNumber: 5, + }, + }); + + // Add the same error twice + errorStore.addError(testError); + errorStore.addError({ ...testError, errorId: 'test-error-2', timestamp: new Date().toISOString() }); + + // Should only have one error with increased occurrence count + expect(errorStore.getErrorCount()).to.equal(1); + + const errors = errorStore.getErrors(); + expect(errors[0].metadata.occurrenceCount).to.equal(2); + }); + + it('should retrieve errors by component', () => { + const error1 = createTestError({ + errorId: 'error-1', + error: { + name: 'Error', + message: 'Error in component 1', + stack: 'Error stack', + sanitizedStack: [], + }, + component: { + name: 'component1', + namespace: 'c', + tagName: 'c-component1', + lifecycle: null, + filePath: null, + }, + source: { + fileName: 'component1.js', + lineNumber: 5, + columnNumber: 10, + }, + }); + + const error2 = createTestError({ + errorId: 'error-2', + component: { + name: 'component2', + namespace: 'c', + tagName: 'c-component2', + lifecycle: null, + filePath: null, + }, + }); + + errorStore.addError(error1); + errorStore.addError(error2); + + const component1Errors = errorStore.getErrorsByComponent('component1'); + expect(component1Errors).to.have.lengthOf(1); + expect(component1Errors[0].component.name).to.equal('component1'); + }); + + it('should retrieve error statistics', () => { + // Add multiple errors with different severities + const errors = [ + createTestError({ + errorId: 'error-1', + component: { name: 'comp1', namespace: 'c', tagName: 'c-comp1', lifecycle: null, filePath: null }, + metadata: { severity: 'error', wasHandled: false, occurrenceCount: 1, tags: [] }, + }), + createTestError({ + errorId: 'error-2', + error: { name: 'Warning', message: 'Warning 1', stack: 'stack', sanitizedStack: [] }, + component: { name: 'comp1', namespace: 'c', tagName: 'c-comp1', lifecycle: null, filePath: null }, + metadata: { severity: 'warning', wasHandled: false, occurrenceCount: 1, tags: [] }, + }), + createTestError({ + errorId: 'error-3', + error: { name: 'Error', message: 'Error 2', stack: 'stack', sanitizedStack: [] }, + component: { name: 'comp2', namespace: 'c', tagName: 'c-comp2', lifecycle: null, filePath: null }, + metadata: { severity: 'fatal', wasHandled: false, occurrenceCount: 1, tags: [] }, + }), + ]; + + errors.forEach((error) => errorStore.addError(error)); + + const stats = errorStore.getStatistics(); + expect(stats.totalErrors).to.equal(3); + expect(stats.bySeverity.error).to.equal(1); + expect(stats.bySeverity.warning).to.equal(1); + expect(stats.bySeverity.fatal).to.equal(1); + expect(stats.byComponent).to.deep.equal({ + comp1: 2, + comp2: 1, + }); + }); + + it('should clear all errors', () => { + const error = createTestError({ errorId: 'error-1' }); + + errorStore.addError(error); + expect(errorStore.getErrorCount()).to.equal(1); + + errorStore.clearErrors(); + expect(errorStore.getErrorCount()).to.equal(0); + }); + + it('should export and import errors as JSON', () => { + const error = createTestError({ errorId: 'error-1' }); + + errorStore.addError(error); + + const json = errorStore.exportAsJSON(); + expect(json).to.be.a('string'); + + // Clear and re-import + errorStore.clearErrors(); + expect(errorStore.getErrorCount()).to.equal(0); + + const importedCount = errorStore.importFromJSON(json); + expect(importedCount).to.equal(1); + expect(errorStore.getErrorCount()).to.equal(1); + + const errors = errorStore.getErrors(); + expect(errors[0].errorId).to.equal('error-1'); + }); +}); diff --git a/test/lwc-dev-server/errorHttpServer.test.ts b/test/lwc-dev-server/errorHttpServer.test.ts new file mode 100644 index 0000000..e8b3335 --- /dev/null +++ b/test/lwc-dev-server/errorHttpServer.test.ts @@ -0,0 +1,621 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { Logger } from '@salesforce/core'; +import sinon from 'sinon'; +import { ErrorCaptureServer, startErrorCaptureServer } from '../../src/lwc-dev-server/errorHttpServer.js'; +import { ErrorStore } from '../../src/lwc-dev-server/errorStore.js'; +import { ErrorDiagnosticPayload } from '../../src/types/errorPayload.js'; + +/** + * Helper to create a valid test error payload + */ +function createTestError(): ErrorDiagnosticPayload { + return { + errorId: 'test-error-1', + timestamp: new Date().toISOString(), + error: { + name: 'TypeError', + message: 'Test error', + stack: 'Error: Test error\n at test.js:5:10', + sanitizedStack: [], + }, + component: { + name: 'testComponent', + namespace: 'c', + tagName: 'c-test-component', + lifecycle: 'connectedCallback', + filePath: '/path/to/component.js', + }, + runtime: { + userAgent: 'Mozilla/5.0', + viewport: { width: 1920, height: 1080 }, + url: 'http://localhost:8081', + lwcVersion: '2.0.0', + isDevelopment: true, + }, + state: { + props: {}, + publicProperties: [], + isConnected: true, + }, + source: { + fileName: 'component.js', + lineNumber: 5, + columnNumber: 10, + }, + metadata: { + severity: 'error', + wasHandled: false, + occurrenceCount: 1, + tags: [], + }, + }; +} + +describe('ErrorCaptureServer', () => { + let errorStore: ErrorStore; + let logger: Logger; + let server: ErrorCaptureServer; + + // Use a random port to avoid conflicts + const testPort = 9000 + Math.floor(Math.random() * 1000); + + beforeEach(() => { + errorStore = new ErrorStore(100); + logger = { + debug: sinon.stub(), + info: sinon.stub(), + warn: sinon.stub(), + error: sinon.stub(), + trace: sinon.stub(), + } as unknown as Logger; + }); + + afterEach(async () => { + if (server && server.isRunning()) { + await server.stop(); + } + }); + + describe('constructor', () => { + it('should create server instance', () => { + server = new ErrorCaptureServer({ + port: testPort, + errorStore, + logger, + projectRoot: '/test/project', + logToConsole: false, + }); + + expect(server).to.be.instanceOf(ErrorCaptureServer); + expect(server.getApp()).to.exist; + }); + + it('should setup middleware', () => { + server = new ErrorCaptureServer({ + port: testPort, + errorStore, + logger, + projectRoot: '/test/project', + logToConsole: false, + }); + + const app = server.getApp(); + // Express app should exist and be a function + expect(app).to.be.a('function'); + expect(app).to.have.property('use'); + }); + }); + + describe('start', () => { + it('should start server on specified port', async () => { + server = new ErrorCaptureServer({ + port: testPort, + errorStore, + logger, + projectRoot: '/test/project', + localhostOnly: true, + }); + + await server.start(); + + expect(server.isRunning()).to.be.true; + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(logger.info).to.have.been.calledWith( + sinon.match(`Error capture server started at http://localhost:${testPort}`) + ); + }); + + it('should bind to localhost by default', async () => { + server = new ErrorCaptureServer({ + port: testPort, + errorStore, + logger, + projectRoot: '/test/project', + localhostOnly: true, + }); + + await server.start(); + + expect(server.isRunning()).to.be.true; + // Server should be listening + const serverInstance = server.getServer(); + expect(serverInstance).to.exist; + expect(serverInstance!.listening).to.be.true; + }); + + it.skip('should reject if port is already in use', async () => { + // Start first server + server = new ErrorCaptureServer({ + port: testPort, + errorStore, + logger, + projectRoot: '/test/project', + }); + await server.start(); + + // Wait a bit to ensure server is fully listening + await new Promise((resolve) => setTimeout(resolve, 100)); + + // Try to start second server on same port + const server2 = new ErrorCaptureServer({ + port: testPort, + errorStore: new ErrorStore(), + logger: { + debug: sinon.stub(), + info: sinon.stub(), + warn: sinon.stub(), + error: sinon.stub(), + trace: sinon.stub(), + } as unknown as Logger, + projectRoot: '/test/project', + }); + + let didThrow = false; + let errorMessage = ''; + try { + await server2.start(); + } catch (err) { + didThrow = true; + errorMessage = (err as Error).message; + expect(err).to.be.instanceOf(Error); + expect(errorMessage).to.include('already in use'); + } finally { + if (server2.isRunning()) { + await server2.stop(); + } + } + + expect(didThrow, `Expected error to be thrown but got none. Last error: ${errorMessage}`).to.be.true; + }); + + it('should log available endpoints', async () => { + server = new ErrorCaptureServer({ + port: testPort, + errorStore, + logger, + projectRoot: '/test/project', + }); + + await server.start(); + + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(logger.info).to.have.been.calledWith('[ErrorCapture] Available endpoints:'); + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(logger.info).to.have.been.calledWith(sinon.match('POST /_dev/errors')); + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(logger.info).to.have.been.calledWith(sinon.match('GET /_dev/errors')); + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(logger.info).to.have.been.calledWith(sinon.match('DELETE /_dev/errors')); + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(logger.info).to.have.been.calledWith(sinon.match('GET /_dev/errors/stats')); + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(logger.info).to.have.been.calledWith(sinon.match('GET /_dev/health')); + }); + }); + + describe('stop', () => { + it('should stop running server', async () => { + server = new ErrorCaptureServer({ + port: testPort, + errorStore, + logger, + projectRoot: '/test/project', + }); + + await server.start(); + expect(server.isRunning()).to.be.true; + + await server.stop(); + expect(server.isRunning()).to.be.false; + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(logger.info).to.have.been.calledWith('[ErrorCapture] Error capture server stopped'); + }); + + it('should be safe to call stop on non-running server', async () => { + server = new ErrorCaptureServer({ + port: testPort, + errorStore, + logger, + projectRoot: '/test/project', + }); + + // Stop without starting + await server.stop(); + expect(server.isRunning()).to.be.false; + }); + + it('should handle stop errors gracefully', async () => { + server = new ErrorCaptureServer({ + port: testPort, + errorStore, + logger, + projectRoot: '/test/project', + }); + + await server.start(); + + // Force an error by closing the server directly + const serverInstance = server.getServer(); + serverInstance!.close(); + + // Try to stop again (should handle error) + try { + await server.stop(); + } catch (err) { + // Should log error but not crash + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(logger.error).to.have.been.called; + } + }); + }); + + describe('HTTP endpoints', () => { + beforeEach(async () => { + server = new ErrorCaptureServer({ + port: testPort, + errorStore, + logger, + projectRoot: '/test/project', + logToConsole: false, + }); + await server.start(); + }); + + describe('POST /_dev/errors', () => { + it('should accept valid error payload', async () => { + const error = createTestError(); + + const response = await fetch(`http://localhost:${testPort}/_dev/errors`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(error), + }); + + expect(response.status).to.equal(201); + const data = (await response.json()) as { success: boolean; errorId: string }; + expect(data.success).to.be.true; + expect(data.errorId).to.equal(error.errorId); + + // Verify error was stored + expect(errorStore.getErrorCount()).to.equal(1); + }); + + it('should reject invalid error payload', async () => { + const invalidPayload = { invalid: 'data' }; + + const response = await fetch(`http://localhost:${testPort}/_dev/errors`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(invalidPayload), + }); + + expect(response.status).to.equal(400); + const data = (await response.json()) as { error: string }; + expect(data.error).to.equal('Invalid error payload'); + + // Verify error was NOT stored + expect(errorStore.getErrorCount()).to.equal(0); + }); + }); + + describe('GET /_dev/errors', () => { + it('should return all errors', async () => { + // Add test errors with different messages to avoid deduplication + const error1 = createTestError(); + const error2 = { + ...createTestError(), + errorId: 'test-error-2', + error: { + name: 'ReferenceError', + message: 'Different error message', + stack: 'ReferenceError: Different error\n at test2.js:10:5', + sanitizedStack: [], + }, + source: { + fileName: 'test2.js', + lineNumber: 10, + columnNumber: 5, + }, + }; + errorStore.addError(error1); + errorStore.addError(error2); + + const response = await fetch(`http://localhost:${testPort}/_dev/errors`); + + expect(response.status).to.equal(200); + const data = (await response.json()) as { success: boolean; count: number; errors: ErrorDiagnosticPayload[] }; + expect(data.success).to.be.true; + expect(data.count).to.equal(2); + expect(data.errors).to.have.lengthOf(2); + }); + + it('should filter errors by component', async () => { + const error1 = createTestError(); + const error2 = { + ...createTestError(), + errorId: 'test-error-2', + component: { ...createTestError().component, name: 'otherComponent' }, + }; + errorStore.addError(error1); + errorStore.addError(error2); + + const response = await fetch(`http://localhost:${testPort}/_dev/errors?component=testComponent`); + + expect(response.status).to.equal(200); + const data = (await response.json()) as { errors: ErrorDiagnosticPayload[] }; + expect(data.errors).to.have.lengthOf(1); + expect(data.errors[0].component.name).to.equal('testComponent'); + }); + + it('should limit results', async () => { + // Add 5 errors with distinct properties to avoid deduplication + for (let i = 0; i < 5; i++) { + errorStore.addError({ + ...createTestError(), + errorId: `error-${i}`, + error: { + name: 'Error', + message: `Error message ${i}`, + stack: `Error: Error message ${i}\n at test${i}.js:${i}:10`, + sanitizedStack: [], + }, + source: { + fileName: `test${i}.js`, + lineNumber: i, + columnNumber: 10, + }, + }); + } + + const response = await fetch(`http://localhost:${testPort}/_dev/errors?limit=2`); + + expect(response.status).to.equal(200); + const data = (await response.json()) as { errors: ErrorDiagnosticPayload[] }; + expect(data.errors).to.have.lengthOf(2); + }); + }); + + describe('DELETE /_dev/errors', () => { + it('should clear all errors', async () => { + // Add test errors with distinct properties + errorStore.addError(createTestError()); + errorStore.addError({ + ...createTestError(), + errorId: 'test-error-2', + error: { + name: 'TypeError', + message: 'Second error message', + stack: 'TypeError: Second error\n at test2.js:15:20', + sanitizedStack: [], + }, + source: { + fileName: 'test2.js', + lineNumber: 15, + columnNumber: 20, + }, + }); + expect(errorStore.getErrorCount()).to.equal(2); + + const response = await fetch(`http://localhost:${testPort}/_dev/errors`, { + method: 'DELETE', + }); + + expect(response.status).to.equal(200); + const data = (await response.json()) as { success: boolean; clearedCount: number }; + expect(data.success).to.be.true; + expect(data.clearedCount).to.equal(2); + + // Verify errors were cleared + expect(errorStore.getErrorCount()).to.equal(0); + }); + }); + + describe('GET /_dev/errors/stats', () => { + it('should return error statistics', async () => { + // Add test errors with distinct properties + const error1 = createTestError(); + const error2 = { + ...createTestError(), + errorId: 'test-error-2', + error: { + name: 'TypeError', + message: 'Warning error message', + stack: 'TypeError: Warning error\n at test3.js:20:15', + sanitizedStack: [], + }, + source: { + fileName: 'test3.js', + lineNumber: 20, + columnNumber: 15, + }, + metadata: { ...createTestError().metadata, severity: 'warning' as const }, + }; + errorStore.addError(error1); + errorStore.addError(error2); + + const response = await fetch(`http://localhost:${testPort}/_dev/errors/stats`); + + expect(response.status).to.equal(200); + const data = (await response.json()) as { + success: boolean; + statistics: { + totalErrors: number; + bySeverity: { error: number; warning: number; fatal: number }; + }; + }; + expect(data.success).to.be.true; + expect(data.statistics.totalErrors).to.equal(2); + expect(data.statistics.bySeverity.error).to.equal(1); + expect(data.statistics.bySeverity.warning).to.equal(1); + }); + }); + + describe('GET /_dev/health', () => { + it('should return health status', async () => { + const response = await fetch(`http://localhost:${testPort}/_dev/health`); + + expect(response.status).to.equal(200); + const data = (await response.json()) as { + status: string; + service: string; + uptime: number; + errors: number; + }; + expect(data.status).to.equal('ok'); + expect(data.service).to.equal('error-capture'); + expect(data.uptime).to.be.a('number'); + expect(data.errors).to.equal(0); + }); + }); + + describe('404 handler', () => { + it('should return 404 for unknown routes', async () => { + const response = await fetch(`http://localhost:${testPort}/_dev/unknown`); + + expect(response.status).to.equal(404); + const data = (await response.json()) as { error: string; message: string }; + expect(data.error).to.equal('Not Found'); + expect(data.message).to.include('Endpoint not found'); + }); + }); + + describe('CORS', () => { + it('should set CORS headers', async () => { + const response = await fetch(`http://localhost:${testPort}/_dev/errors`, { + method: 'OPTIONS', + }); + + // OPTIONS requests return 204 No Content (not 200) + expect(response.status).to.be.oneOf([200, 204]); + expect(response.headers.get('access-control-allow-origin')).to.equal('*'); + const methods = response.headers.get('access-control-allow-methods'); + expect(methods).to.include('GET'); + expect(methods).to.include('POST'); + expect(methods).to.include('DELETE'); + expect(methods).to.include('OPTIONS'); + }); + }); + }); + + describe('startErrorCaptureServer', () => { + it('should create and start server', async () => { + server = await startErrorCaptureServer({ + port: testPort, + errorStore, + logger, + projectRoot: '/test/project', + }); + + expect(server).to.be.instanceOf(ErrorCaptureServer); + expect(server.isRunning()).to.be.true; + }); + + it('should reject if server fails to start', async () => { + // Start a server on the port first + server = await startErrorCaptureServer({ + port: testPort, + errorStore, + logger, + projectRoot: '/test/project', + }); + + // Try to start another on same port + try { + await startErrorCaptureServer({ + port: testPort, + errorStore: new ErrorStore(), + logger, + projectRoot: '/test/project', + }); + expect.fail('Should have thrown error'); + } catch (err) { + expect(err).to.be.instanceOf(Error); + } + }); + }); + + describe('integration with errorStore', () => { + beforeEach(async () => { + server = new ErrorCaptureServer({ + port: testPort, + errorStore, + logger, + projectRoot: '/test/project', + logToConsole: false, + }); + await server.start(); + }); + + it('should store errors in errorStore', async () => { + const error = createTestError(); + + await fetch(`http://localhost:${testPort}/_dev/errors`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(error), + }); + + const storedError = errorStore.getError(error.errorId); + expect(storedError).to.exist; + expect(storedError!.errorId).to.equal(error.errorId); + }); + + it('should deduplicate identical errors', async () => { + const error = createTestError(); + + // Send same error twice + await fetch(`http://localhost:${testPort}/_dev/errors`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(error), + }); + + await fetch(`http://localhost:${testPort}/_dev/errors`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ ...error, timestamp: new Date().toISOString() }), + }); + + // Should only have one error with increased occurrence count + expect(errorStore.getErrorCount()).to.equal(1); + const errors = errorStore.getErrors(); + expect(errors[0].metadata.occurrenceCount).to.equal(2); + }); + }); +}); diff --git a/test/lwc-dev-server/errorMiddleware.test.ts b/test/lwc-dev-server/errorMiddleware.test.ts new file mode 100644 index 0000000..d6634ee --- /dev/null +++ b/test/lwc-dev-server/errorMiddleware.test.ts @@ -0,0 +1,487 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, use } from 'chai'; +import sinon, { SinonStub } from 'sinon'; +import sinonChai from 'sinon-chai'; +import { Logger } from '@salesforce/core'; +import { Request, Response } from 'express'; + +// eslint-disable-next-line @typescript-eslint/no-unsafe-argument +use(sinonChai); +import { + createErrorMiddleware, + createErrorQueryMiddleware, + createErrorClearMiddleware, + createErrorStatsMiddleware, + createCombinedErrorMiddleware, + createErrorCORSMiddleware, +} from '../../src/lwc-dev-server/errorMiddleware.js'; +import { ErrorStore } from '../../src/lwc-dev-server/errorStore.js'; +import { ErrorDiagnosticPayload } from '../../src/types/errorPayload.js'; + +/** + * Helper function to create a valid test error payload + */ +function createTestError(overrides?: Partial): ErrorDiagnosticPayload { + const baseError: ErrorDiagnosticPayload = { + errorId: 'test-error-1', + timestamp: new Date().toISOString(), + error: { + name: 'Error', + message: 'Test error', + stack: 'Error: Test error\n at test.js:5:10', + sanitizedStack: [], + }, + component: { + name: 'testComponent', + namespace: null, + tagName: null, + lifecycle: null, + filePath: null, + }, + source: { + fileName: 'test.js', + lineNumber: 5, + columnNumber: 10, + }, + runtime: { + userAgent: 'Mozilla/5.0', + viewport: { width: 1920, height: 1080 }, + url: 'http://localhost:8081', + lwcVersion: null, + isDevelopment: true, + }, + state: { + props: null, + publicProperties: [], + isConnected: true, + }, + metadata: { + severity: 'error', + wasHandled: false, + occurrenceCount: 1, + tags: [], + }, + }; + + return { ...baseError, ...overrides }; +} + +describe('Error Middleware', () => { + let errorStore: ErrorStore; + let logger: Logger; + let req: Request; + let res: Response; + let next: sinon.SinonSpy; + + beforeEach(() => { + errorStore = new ErrorStore(100); + logger = { + debug: sinon.stub(), + info: sinon.stub(), + warn: sinon.stub(), + error: sinon.stub(), + } as unknown as Logger; + + req = { + method: 'POST', + path: '/_dev/errors', + body: {}, + query: {}, + } as Request; + + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub().returnsThis(), + send: sinon.stub().returnsThis(), + setHeader: sinon.stub().returnsThis(), + } as unknown as Response; + + next = sinon.spy(); + }); + + describe('createErrorMiddleware', () => { + it('should pass through non-POST requests', () => { + const middleware = createErrorMiddleware({ + errorStore, + logger, + projectRoot: '/test/project', + logToConsole: false, + }); + + req = { ...req, method: 'GET' } as Request; + middleware(req, res, next); + + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(next).to.have.been.calledOnce; + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(res.status).to.not.have.been.called; + }); + + it('should pass through requests to other paths', () => { + const middleware = createErrorMiddleware({ + errorStore, + logger, + projectRoot: '/test/project', + logToConsole: false, + }); + + req = { ...req, path: '/other/path' } as Request; + middleware(req, res, next); + + expect(next).to.have.been.calledOnce; + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(res.status).to.not.have.been.called; + }); + + it('should accept valid error payload and store it', () => { + const middleware = createErrorMiddleware({ + errorStore, + logger, + projectRoot: '/test/project', + logToConsole: false, + }); + + const validPayload = createTestError({ + error: { + name: 'ReferenceError', + message: 'nonExistentMethod is not defined', + stack: 'ReferenceError: nonExistentMethod is not defined\n at connectedCallback (test.js:5:10)', + sanitizedStack: [], + }, + component: { + name: 'testComponent', + namespace: null, + tagName: 'c-test-component', + lifecycle: 'connectedCallback', + filePath: null, + }, + }); + + req = { ...req, body: validPayload } as Request; + middleware(req, res, next); + + // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-call + expect(res.status).to.have.been.calledWith(201); + // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-call + expect(res.json).to.have.been.calledWith( + sinon.match({ + success: true, + errorId: 'test-error-1', + }) + ); + expect(errorStore.getErrorCount()).to.equal(1); + }); + + it('should reject invalid error payload', () => { + const middleware = createErrorMiddleware({ + errorStore, + logger, + projectRoot: '/test/project', + logToConsole: false, + }); + + req = { ...req, body: { invalid: 'payload' } } as Request; + middleware(req, res, next); + + // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-call + expect(res.status).to.have.been.calledWith(400); + // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-call + expect(res.json).to.have.been.calledWith( + sinon.match({ + error: 'Invalid error payload', + }) + ); + expect(errorStore.getErrorCount()).to.equal(0); + }); + + it('should handle errors gracefully', () => { + const middleware = createErrorMiddleware({ + errorStore, + logger, + projectRoot: '/test/project', + logToConsole: false, + }); + + // Stub addError to throw an error + sinon.stub(errorStore, 'addError').throws(new Error('Storage error')); + + const validPayload = createTestError({ + errorId: 'test-error-2', + error: { + name: 'ReferenceError', + message: 'test error', + stack: 'Error stack', + sanitizedStack: [], + }, + source: { + fileName: null, + lineNumber: null, + columnNumber: null, + }, + }); + + req = { ...req, body: validPayload } as Request; + middleware(req, res, next); + + // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-call + expect(res.status).to.have.been.calledWith(500); + // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-call + expect(res.json).to.have.been.calledWith( + sinon.match({ + error: 'Internal server error', + }) + ); + }); + }); + + describe('createErrorQueryMiddleware', () => { + it('should return all errors', () => { + const middleware = createErrorQueryMiddleware(errorStore); + + // Add test errors + const error1 = createTestError({ + errorId: 'test-1', + error: { + name: 'Error1', + message: 'First error', + stack: 'stack', + sanitizedStack: [], + }, + component: { name: 'comp1', namespace: null, tagName: null, lifecycle: null, filePath: null }, + }); + + errorStore.addError(error1); + + req = { ...req, method: 'GET', path: '/_dev/errors' } as Request; + middleware(req, res, next); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + expect(res.json).to.have.been.calledOnce; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const response = (res.json as SinonStub).firstCall.args[0]; + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(response.errors).to.have.lengthOf(1); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(response.errors[0].errorId).to.equal('test-1'); + }); + + it('should filter errors by component', () => { + const middleware = createErrorQueryMiddleware(errorStore); + + const error1 = createTestError({ + errorId: 'test-1', + error: { name: 'Error1', message: 'First error', stack: 'stack', sanitizedStack: [] }, + component: { name: 'comp1', namespace: null, tagName: null, lifecycle: null, filePath: null }, + }); + + const error2 = createTestError({ + errorId: 'test-2', + component: { name: 'comp2', namespace: null, tagName: null, lifecycle: null, filePath: null }, + }); + + errorStore.addError(error1); + errorStore.addError(error2); + + req = { ...req, method: 'GET', path: '/_dev/errors', query: { component: 'comp1' } } as unknown as Request; + middleware(req, res, next); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const response = (res.json as SinonStub).firstCall.args[0]; + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(response.errors).to.have.lengthOf(1); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(response.errors[0].component.name).to.equal('comp1'); + }); + + it('should limit results', () => { + const middleware = createErrorQueryMiddleware(errorStore); + + // Add 3 errors + for (let i = 0; i < 3; i++) { + const error = createTestError({ + errorId: `test-${i}`, + error: { name: 'Error', message: `Error ${i}`, stack: 'stack', sanitizedStack: [] }, + }); + errorStore.addError(error); + } + + req = { ...req, method: 'GET', path: '/_dev/errors', query: { limit: '2' } } as unknown as Request; + middleware(req, res, next); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const response = (res.json as SinonStub).firstCall.args[0]; + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(response.errors).to.have.lengthOf(2); + }); + }); + + describe('createErrorClearMiddleware', () => { + it('should clear all errors', () => { + const middleware = createErrorClearMiddleware(errorStore, logger); + + // Add an error + const error = createTestError(); + errorStore.addError(error); + + expect(errorStore.getErrorCount()).to.equal(1); + + req = { ...req, method: 'DELETE', path: '/_dev/errors' } as Request; + middleware(req, res, next); + + expect(errorStore.getErrorCount()).to.equal(0); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + expect(res.json).to.have.been.calledWith( + sinon.match({ + success: true, + }) + ); + }); + }); + + describe('createErrorStatsMiddleware', () => { + it('should return error statistics', () => { + const middleware = createErrorStatsMiddleware(errorStore); + + // Add errors + const error1 = createTestError({ + errorId: 'test-1', + error: { message: 'Error 1', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + component: { name: 'comp1', namespace: null, tagName: null, lifecycle: null, filePath: null }, + }); + + const error2 = createTestError({ + errorId: 'test-2', + error: { message: 'Error 2', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + component: { name: 'comp1', namespace: null, tagName: null, lifecycle: null, filePath: null }, + metadata: { severity: 'warning', wasHandled: false, occurrenceCount: 1, tags: [] }, + }); + + errorStore.addError(error1); + errorStore.addError(error2); + + req = { ...req, method: 'GET', path: '/_dev/errors/stats' } as Request; + middleware(req, res, next); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const response = (res.json as SinonStub).firstCall.args[0]; + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(response.statistics.totalErrors).to.equal(2); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(response.statistics.bySeverity.error).to.equal(1); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(response.statistics.bySeverity.warning).to.equal(1); + }); + }); + + describe('createErrorCORSMiddleware', () => { + it('should set CORS headers', () => { + const middleware = createErrorCORSMiddleware(); + + req = { ...req, path: '/_dev/errors' } as Request; + middleware(req, res, next); + + // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-call + expect(res.setHeader).to.have.been.calledWith('Access-Control-Allow-Origin', '*'); + // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-call + expect(res.setHeader).to.have.been.calledWith('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS'); + // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-call + expect(res.setHeader).to.have.been.calledWith('Access-Control-Allow-Headers', 'Content-Type'); + expect(next).to.have.been.calledOnce; + }); + }); + + describe('createCombinedErrorMiddleware', () => { + it('should route to stats endpoint', () => { + const middleware = createCombinedErrorMiddleware({ + errorStore, + logger, + projectRoot: '/test/project', + logToConsole: false, + }); + + req = { ...req, path: '/_dev/errors/stats', method: 'GET' } as Request; + + middleware(req, res, next); + + expect(res.json).to.have.been.called; + }); + + it('should route POST to error capture', () => { + const middleware = createCombinedErrorMiddleware({ + errorStore, + logger, + projectRoot: '/test/project', + logToConsole: false, + }); + + const validPayload = createTestError(); + + req = { ...req, path: '/_dev/errors', method: 'POST', body: validPayload } as Request; + + middleware(req, res, next); + + // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-call + expect(res.status).to.have.been.calledWith(201); + }); + + it('should route GET to error query', () => { + const middleware = createCombinedErrorMiddleware({ + errorStore, + logger, + projectRoot: '/test/project', + logToConsole: false, + }); + + req = { ...req, path: '/_dev/errors', method: 'GET' } as Request; + + middleware(req, res, next); + + expect(res.json).to.have.been.called; + }); + + it('should route DELETE to error clear', () => { + const middleware = createCombinedErrorMiddleware({ + errorStore, + logger, + projectRoot: '/test/project', + logToConsole: false, + }); + + req = { ...req, path: '/_dev/errors', method: 'DELETE' } as Request; + + middleware(req, res, next); + + expect(res.json).to.have.been.called; + }); + + it('should pass through other requests', () => { + const middleware = createCombinedErrorMiddleware({ + errorStore, + logger, + projectRoot: '/test/project', + logToConsole: false, + }); + + req = { ...req, path: '/other/path', method: 'GET' } as Request; + + middleware(req, res, next); + + expect(next).to.have.been.calledOnce; + }); + }); +}); diff --git a/test/lwc-dev-server/errorStore.test.ts b/test/lwc-dev-server/errorStore.test.ts new file mode 100644 index 0000000..c7115b9 --- /dev/null +++ b/test/lwc-dev-server/errorStore.test.ts @@ -0,0 +1,385 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { ErrorStore, getErrorStore, resetErrorStore } from '../../src/lwc-dev-server/errorStore.js'; +import { ErrorDiagnosticPayload } from '../../src/types/errorPayload.js'; + +function createMockError(overrides: Partial = {}): ErrorDiagnosticPayload { + return { + errorId: 'test-error-' + Math.random(), + timestamp: new Date().toISOString(), + error: { + message: 'Test error', + name: 'TypeError', + stack: 'Error stack', + sanitizedStack: [], + }, + component: { + name: 'c-test-component', + namespace: 'c', + tagName: 'c-test-component', + lifecycle: 'connectedCallback', + filePath: '/path/to/component.js', + }, + runtime: { + userAgent: 'Test Agent', + viewport: { width: 1920, height: 1080 }, + url: 'http://localhost:8081', + lwcVersion: '2.0.0', + isDevelopment: true, + }, + state: { + props: {}, + publicProperties: [], + isConnected: true, + }, + source: { + fileName: 'component.js', + lineNumber: 10, + columnNumber: 5, + }, + metadata: { + severity: 'error', + wasHandled: false, + occurrenceCount: 1, + tags: [], + }, + ...overrides, + }; +} + +describe('ErrorStore', () => { + let errorStore: ErrorStore; + + beforeEach(() => { + errorStore = new ErrorStore(); + }); + + describe('addError', () => { + it('should add an error to the store', () => { + const error = createMockError(); + errorStore.addError(error); + + const stored = errorStore.getError(error.errorId); + expect(stored).to.deep.equal(error); + }); + + it('should maintain insertion order', () => { + const error1 = createMockError({ + errorId: 'error1', + error: { message: 'Error 1', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + }); + const error2 = createMockError({ + errorId: 'error2', + error: { message: 'Error 2', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + }); + const error3 = createMockError({ + errorId: 'error3', + error: { message: 'Error 3', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + }); + + errorStore.addError(error1); + errorStore.addError(error2); + errorStore.addError(error3); + + const errors = errorStore.getErrors(); + expect(errors).to.have.length(3); + expect(errors[0].errorId).to.equal('error1'); + expect(errors[1].errorId).to.equal('error2'); + expect(errors[2].errorId).to.equal('error3'); + }); + + it('should increment occurrence count for duplicate errors', () => { + const error = createMockError(); + errorStore.addError(error); + + // Add the same error again (same signature) + const duplicate = createMockError({ + errorId: 'different-id', + error: error.error, + component: error.component, + source: error.source, + }); + errorStore.addError(duplicate); + + const errors = errorStore.getErrors(); + expect(errors).to.have.length(1); + expect(errors[0].metadata.occurrenceCount).to.equal(2); + }); + + it('should enforce max size limit', () => { + const smallStore = new ErrorStore(3); + + const error1 = createMockError({ + errorId: 'error1', + error: { message: 'Error 1', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + }); + const error2 = createMockError({ + errorId: 'error2', + error: { message: 'Error 2', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + }); + const error3 = createMockError({ + errorId: 'error3', + error: { message: 'Error 3', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + }); + const error4 = createMockError({ + errorId: 'error4', + error: { message: 'Error 4', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + }); + + smallStore.addError(error1); + smallStore.addError(error2); + smallStore.addError(error3); + smallStore.addError(error4); + + const errors = smallStore.getErrors(); + expect(errors).to.have.length(3); + expect(errors[0].errorId).to.equal('error2'); + expect(errors[1].errorId).to.equal('error3'); + expect(errors[2].errorId).to.equal('error4'); + }); + }); + + describe('getError', () => { + it('should retrieve error by ID', () => { + const error = createMockError(); + errorStore.addError(error); + + const retrieved = errorStore.getError(error.errorId); + expect(retrieved).to.deep.equal(error); + }); + + it('should return undefined for non-existent ID', () => { + const retrieved = errorStore.getError('non-existent'); + expect(retrieved).to.be.undefined; + }); + }); + + describe('getErrors', () => { + it('should return all errors in order', () => { + const error1 = createMockError({ + errorId: 'error1', + error: { message: 'Error 1', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + }); + const error2 = createMockError({ + errorId: 'error2', + error: { message: 'Error 2', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + }); + + errorStore.addError(error1); + errorStore.addError(error2); + + const errors = errorStore.getErrors(); + expect(errors).to.have.length(2); + expect(errors[0].errorId).to.equal('error1'); + expect(errors[1].errorId).to.equal('error2'); + }); + + it('should return empty array when no errors', () => { + const errors = errorStore.getErrors(); + expect(errors).to.be.an('array').that.is.empty; + }); + }); + + describe('getErrorsByComponent', () => { + it('should filter errors by component name', () => { + const error1 = createMockError({ + errorId: 'error1', + error: { message: 'Error 1', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + component: { name: 'c-component-a', namespace: 'c', tagName: 'c-component-a', lifecycle: null, filePath: null }, + }); + const error2 = createMockError({ + errorId: 'error2', + error: { message: 'Error 2', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + component: { name: 'c-component-b', namespace: 'c', tagName: 'c-component-b', lifecycle: null, filePath: null }, + }); + const error3 = createMockError({ + errorId: 'error3', + error: { message: 'Error 3', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + component: { name: 'c-component-a', namespace: 'c', tagName: 'c-component-a', lifecycle: null, filePath: null }, + }); + + errorStore.addError(error1); + errorStore.addError(error2); + errorStore.addError(error3); + + const filtered = errorStore.getErrorsByComponent('c-component-a'); + expect(filtered).to.have.length(2); + expect(filtered[0].component.name).to.equal('c-component-a'); + expect(filtered[1].component.name).to.equal('c-component-a'); + }); + }); + + describe('getErrorsBySeverity', () => { + it('should filter errors by severity', () => { + const error1 = createMockError({ + errorId: 'error1', + error: { message: 'Error 1', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + metadata: { severity: 'error', wasHandled: false, occurrenceCount: 1, tags: [] }, + }); + const error2 = createMockError({ + errorId: 'error2', + error: { message: 'Error 2', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + metadata: { severity: 'warning', wasHandled: false, occurrenceCount: 1, tags: [] }, + }); + const error3 = createMockError({ + errorId: 'error3', + error: { message: 'Error 3', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + metadata: { severity: 'error', wasHandled: false, occurrenceCount: 1, tags: [] }, + }); + + errorStore.addError(error1); + errorStore.addError(error2); + errorStore.addError(error3); + + const filtered = errorStore.getErrorsBySeverity('error'); + expect(filtered).to.have.length(2); + }); + }); + + describe('getRecentErrors', () => { + it('should return most recent N errors', () => { + for (let i = 0; i < 10; i++) { + errorStore.addError( + createMockError({ + errorId: `error${i}`, + error: { message: `Error ${i}`, name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + }) + ); + } + + const recent = errorStore.getRecentErrors(3); + expect(recent).to.have.length(3); + expect(recent[0].errorId).to.equal('error7'); + expect(recent[1].errorId).to.equal('error8'); + expect(recent[2].errorId).to.equal('error9'); + }); + }); + + describe('clearErrors', () => { + it('should remove all errors', () => { + errorStore.addError(createMockError()); + errorStore.addError(createMockError()); + + errorStore.clearErrors(); + + expect(errorStore.getErrorCount()).to.equal(0); + expect(errorStore.getErrors()).to.be.empty; + }); + }); + + describe('getErrorCount', () => { + it('should return correct count', () => { + expect(errorStore.getErrorCount()).to.equal(0); + + errorStore.addError( + createMockError({ + errorId: 'error1', + error: { message: 'Error 1', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + }) + ); + expect(errorStore.getErrorCount()).to.equal(1); + + errorStore.addError( + createMockError({ + errorId: 'error2', + error: { message: 'Error 2', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + }) + ); + expect(errorStore.getErrorCount()).to.equal(2); + }); + }); + + describe('getStatistics', () => { + it('should return correct statistics', () => { + const error1 = createMockError({ + errorId: 'error1', + error: { message: 'Error 1', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + component: { name: 'c-component-a', namespace: 'c', tagName: 'c-component-a', lifecycle: null, filePath: null }, + metadata: { severity: 'error', wasHandled: false, occurrenceCount: 1, tags: [] }, + }); + const error2 = createMockError({ + errorId: 'error2', + error: { message: 'Error 2', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + component: { name: 'c-component-b', namespace: 'c', tagName: 'c-component-b', lifecycle: null, filePath: null }, + metadata: { severity: 'warning', wasHandled: false, occurrenceCount: 1, tags: [] }, + }); + + errorStore.addError(error1); + errorStore.addError(error2); + + const stats = errorStore.getStatistics(); + + expect(stats.totalErrors).to.equal(2); + expect(stats.totalOccurrences).to.equal(2); + expect(stats.byComponent['c-component-a']).to.equal(1); + expect(stats.byComponent['c-component-b']).to.equal(1); + expect(stats.bySeverity.error).to.equal(1); + expect(stats.bySeverity.warning).to.equal(1); + }); + }); + + describe('exportAsJSON and importFromJSON', () => { + it('should export and import errors', () => { + const error1 = createMockError({ + errorId: 'error1', + error: { message: 'Export Error 1', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + }); + const error2 = createMockError({ + errorId: 'error2', + error: { message: 'Export Error 2', name: 'TypeError', stack: 'stack', sanitizedStack: [] }, + }); + + errorStore.addError(error1); + errorStore.addError(error2); + + const json = errorStore.exportAsJSON(); + + const newStore = new ErrorStore(); + const count = newStore.importFromJSON(json); + + expect(count).to.equal(2); + expect(newStore.getErrorCount()).to.equal(2); + }); + + it('should handle invalid JSON gracefully', () => { + const count = errorStore.importFromJSON('invalid json'); + expect(count).to.equal(0); + }); + }); +}); + +describe('getErrorStore singleton', () => { + afterEach(() => { + resetErrorStore(); + }); + + it('should return same instance on multiple calls', () => { + const store1 = getErrorStore(); + const store2 = getErrorStore(); + + expect(store1).to.equal(store2); + }); + + it('should reset singleton', () => { + const store1 = getErrorStore(); + resetErrorStore(); + const store2 = getErrorStore(); + + expect(store1).to.not.equal(store2); + }); +}); diff --git a/test/shared/stackTraceUtils.test.ts b/test/shared/stackTraceUtils.test.ts new file mode 100644 index 0000000..475bd4d --- /dev/null +++ b/test/shared/stackTraceUtils.test.ts @@ -0,0 +1,349 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { + parseStackTrace, + parseStackFrame, + sanitizeFileName, + isLocalSource, + extractComponentNameFromStack, + extractLifecycleHookFromStack, + filterLocalFrames, + formatStackFrame, + formatStackTrace, +} from '../../src/shared/stackTraceUtils.js'; + +describe('stackTraceUtils', () => { + describe('parseStackFrame', () => { + it('should parse Chrome/Node format with function name', () => { + const line = ' at myFunction (file:///path/to/file.js:10:5)'; + const frame = parseStackFrame(line, '/path/to'); + + expect(frame).to.not.be.null; + expect(frame?.functionName).to.equal('myFunction'); + expect(frame?.fileName).to.equal('/path/to/file.js'); + expect(frame?.lineNumber).to.equal(10); + expect(frame?.columnNumber).to.equal(5); + expect(frame?.isLocalSource).to.be.true; + }); + + it('should parse Chrome/Node format without function name', () => { + const line = ' at /path/to/file.js:10:5'; + const frame = parseStackFrame(line, '/path/to'); + + expect(frame).to.not.be.null; + expect(frame?.functionName).to.be.null; + expect(frame?.fileName).to.equal('/path/to/file.js'); + expect(frame?.lineNumber).to.equal(10); + expect(frame?.columnNumber).to.equal(5); + }); + + it('should parse Firefox format', () => { + const line = 'myFunction@file:///path/to/file.js:10:5'; + const frame = parseStackFrame(line, '/path/to'); + + expect(frame).to.not.be.null; + expect(frame?.functionName).to.equal('myFunction'); + expect(frame?.fileName).to.equal('/path/to/file.js'); + expect(frame?.lineNumber).to.equal(10); + expect(frame?.columnNumber).to.equal(5); + expect(frame?.isLocalSource).to.be.true; + }); + + it('should parse Safari format', () => { + const line = 'myFunction@/path/to/file.js:10:5'; + const frame = parseStackFrame(line, '/path/to'); + + expect(frame).to.not.be.null; + expect(frame?.functionName).to.equal('myFunction'); + expect(frame?.fileName).to.equal('/path/to/file.js'); + expect(frame?.lineNumber).to.equal(10); + expect(frame?.columnNumber).to.equal(5); + }); + + it('should handle native code', () => { + const line = 'myFunction@[native code]'; + const frame = parseStackFrame(line); + + expect(frame).to.not.be.null; + expect(frame?.functionName).to.equal('myFunction'); + expect(frame?.fileName).to.equal('[native code]'); + expect(frame?.lineNumber).to.equal(0); + expect(frame?.columnNumber).to.equal(0); + expect(frame?.isLocalSource).to.be.false; + }); + + it('should return null for unparseable lines', () => { + const line = 'This is not a stack frame'; + const frame = parseStackFrame(line); + + expect(frame).to.be.null; + }); + }); + + describe('parseStackTrace', () => { + it('should parse multi-line stack trace', () => { + const stack = `Error: Test error + at myFunction (file:///path/to/file.js:10:5) + at anotherFunction (file:///path/to/other.js:20:10) + at Object. (file:///path/to/main.js:30:15)`; + + const frames = parseStackTrace(stack, '/path/to'); + + expect(frames).to.have.length(3); + expect(frames[0].functionName).to.equal('myFunction'); + expect(frames[1].functionName).to.equal('anotherFunction'); + expect(frames[2].functionName).to.equal('Object.'); + }); + + it('should handle empty stack trace', () => { + const frames = parseStackTrace(''); + expect(frames).to.have.length(0); + }); + }); + + describe('sanitizeFileName', () => { + it('should remove webpack paths', () => { + expect(sanitizeFileName('webpack:///./src/file.js')).to.equal('./src/file.js'); + expect(sanitizeFileName('webpack-internal:///./src/file.js')).to.equal('./src/file.js'); + }); + + it('should remove query parameters', () => { + expect(sanitizeFileName('/path/to/file.js?v=123')).to.equal('/path/to/file.js'); + }); + + it('should remove hash fragments', () => { + expect(sanitizeFileName('/path/to/file.js#fragment')).to.equal('/path/to/file.js'); + }); + + it('should convert file:// URLs', () => { + const result = sanitizeFileName('file:///path/to/file.js'); + expect(result).to.include('/path/to/file.js'); + }); + + it('should handle http URLs', () => { + const result = sanitizeFileName('http://localhost:8081/path/to/file.js'); + expect(result).to.equal('/path/to/file.js'); + }); + + it('should return unchanged for simple paths', () => { + expect(sanitizeFileName('/path/to/file.js')).to.equal('/path/to/file.js'); + }); + }); + + describe('isLocalSource', () => { + it('should return false for node_modules', () => { + expect(isLocalSource('/path/to/node_modules/package/file.js')).to.be.false; + }); + + it('should return false for native code', () => { + expect(isLocalSource('[native code]')).to.be.false; + }); + + it('should return false for anonymous', () => { + expect(isLocalSource('')).to.be.false; + }); + + it('should return false for external URLs', () => { + expect(isLocalSource('http://example.com/file.js', '/path/to')).to.be.false; + expect(isLocalSource('https://example.com/file.js', '/path/to')).to.be.false; + }); + + it('should return true for local paths', () => { + expect(isLocalSource('/path/to/file.js', '/path/to')).to.be.true; + expect(isLocalSource('./file.js')).to.be.true; + }); + }); + + describe('extractComponentNameFromStack', () => { + it('should extract component name from file names', () => { + const frames = [ + { + functionName: null, + fileName: '/path/to/c-hello-world.js', + lineNumber: 10, + columnNumber: 5, + isLocalSource: true, + }, + ]; + + expect(extractComponentNameFromStack(frames)).to.equal('c-hello-world'); + }); + + it('should extract component name from function names', () => { + const frames = [ + { + functionName: 'c-my-component.connectedCallback', + fileName: '/path/to/file.js', + lineNumber: 10, + columnNumber: 5, + isLocalSource: true, + }, + ]; + + expect(extractComponentNameFromStack(frames)).to.equal('c-my-component'); + }); + + it('should return null if no component name found', () => { + const frames = [ + { + functionName: 'someFunction', + fileName: '/path/to/file.js', + lineNumber: 10, + columnNumber: 5, + isLocalSource: true, + }, + ]; + + expect(extractComponentNameFromStack(frames)).to.be.null; + }); + }); + + describe('extractLifecycleHookFromStack', () => { + it('should detect connectedCallback', () => { + const frames = [ + { + functionName: 'connectedCallback', + fileName: '/path/to/file.js', + lineNumber: 10, + columnNumber: 5, + isLocalSource: true, + }, + ]; + + expect(extractLifecycleHookFromStack(frames)).to.equal('connectedCallback'); + }); + + it('should detect renderedCallback', () => { + const frames = [ + { + functionName: 'MyComponent.renderedCallback', + fileName: '/path/to/file.js', + lineNumber: 10, + columnNumber: 5, + isLocalSource: true, + }, + ]; + + expect(extractLifecycleHookFromStack(frames)).to.equal('renderedCallback'); + }); + + it('should return null if no lifecycle hook found', () => { + const frames = [ + { + functionName: 'someOtherMethod', + fileName: '/path/to/file.js', + lineNumber: 10, + columnNumber: 5, + isLocalSource: true, + }, + ]; + + expect(extractLifecycleHookFromStack(frames)).to.be.null; + }); + }); + + describe('filterLocalFrames', () => { + it('should filter to only local source frames', () => { + const frames = [ + { + functionName: 'myFunction', + fileName: '/path/to/file.js', + lineNumber: 10, + columnNumber: 5, + isLocalSource: true, + }, + { + functionName: 'libraryFunction', + fileName: '/path/to/node_modules/lib/file.js', + lineNumber: 20, + columnNumber: 10, + isLocalSource: false, + }, + { + functionName: 'anotherFunction', + fileName: '/path/to/other.js', + lineNumber: 30, + columnNumber: 15, + isLocalSource: true, + }, + ]; + + const filtered = filterLocalFrames(frames); + + expect(filtered).to.have.length(2); + expect(filtered[0].functionName).to.equal('myFunction'); + expect(filtered[1].functionName).to.equal('anotherFunction'); + }); + }); + + describe('formatStackFrame', () => { + it('should format stack frame with function name', () => { + const frame = { + functionName: 'myFunction', + fileName: '/path/to/file.js', + lineNumber: 10, + columnNumber: 5, + isLocalSource: true, + }; + + const formatted = formatStackFrame(frame); + expect(formatted).to.equal('myFunction (/path/to/file.js:10:5)'); + }); + + it('should format stack frame without function name', () => { + const frame = { + functionName: null, + fileName: '/path/to/file.js', + lineNumber: 10, + columnNumber: 5, + isLocalSource: true, + }; + + const formatted = formatStackFrame(frame); + expect(formatted).to.equal(' (/path/to/file.js:10:5)'); + }); + }); + + describe('formatStackTrace', () => { + it('should format multiple frames', () => { + const frames = [ + { + functionName: 'function1', + fileName: '/path/to/file1.js', + lineNumber: 10, + columnNumber: 5, + isLocalSource: true, + }, + { + functionName: 'function2', + fileName: '/path/to/file2.js', + lineNumber: 20, + columnNumber: 10, + isLocalSource: true, + }, + ]; + + const formatted = formatStackTrace(frames); + + expect(formatted).to.include('1. function1'); + expect(formatted).to.include('2. function2'); + expect(formatted).to.include('/path/to/file1.js:10:5'); + expect(formatted).to.include('/path/to/file2.js:20:10'); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 427a82b..611cac1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -86,9 +86,9 @@ "@smithy/util-utf8" "^2.0.0" tslib "^2.6.2" -"@aws-sdk/client-cloudfront@^3.908.0": +"@aws-sdk/client-cloudfront@^3.917.0": version "3.917.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-cloudfront/-/client-cloudfront-3.917.0.tgz#f8701c7e8ebbc9ecfb6581c204009cfcd2a17e8c" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/client-cloudfront/-/client-cloudfront-3.917.0.tgz#f8701c7e8ebbc9ecfb6581c204009cfcd2a17e8c" integrity sha512-ZnbhUpnVWh/E0wWw0PygCq8fj7Pytun29Pu3PqIl6Qh9d0XU5kx0Ecis0vNi9HWqj/jmJ5+UDiUcVxC2ft0Utw== dependencies: "@aws-crypto/sha256-browser" "5.2.0" @@ -136,7 +136,7 @@ "@aws-sdk/client-s3@^3.913.0": version "3.917.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.917.0.tgz#835ead98d5a6ddad5662d0f133d377febf43de1e" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/client-s3/-/client-s3-3.917.0.tgz#835ead98d5a6ddad5662d0f133d377febf43de1e" integrity sha512-3L73mDCpH7G0koFv3p3WkkEKqC5wn2EznKtNMrJ6hczPIr2Cu6DJz8VHeTZp9wFZLPrIBmh3ZW1KiLujT5Fd2w== dependencies: "@aws-crypto/sha1-browser" "5.2.0" @@ -199,7 +199,7 @@ "@aws-sdk/client-sso@3.916.0": version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.916.0.tgz#627792ab588a004fc0874a060b3466e21328b5b6" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/client-sso/-/client-sso-3.916.0.tgz#627792ab588a004fc0874a060b3466e21328b5b6" integrity sha512-Eu4PtEUL1MyRvboQnoq5YKg0Z9vAni3ccebykJy615xokVZUdA3di2YxHM/hykDQX7lcUC62q9fVIvh0+UNk/w== dependencies: "@aws-crypto/sha256-browser" "5.2.0" @@ -243,7 +243,7 @@ "@aws-sdk/core@3.916.0": version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.916.0.tgz#ea11b485f837f1773e174f8a4ed82ecce9f163f7" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/core/-/core-3.916.0.tgz#ea11b485f837f1773e174f8a4ed82ecce9f163f7" integrity sha512-1JHE5s6MD5PKGovmx/F1e01hUbds/1y3X8rD+Gvi/gWVfdg5noO7ZCerpRsWgfzgvCMZC9VicopBqNHCKLykZA== dependencies: "@aws-sdk/types" "3.914.0" @@ -262,7 +262,7 @@ "@aws-sdk/credential-provider-env@3.916.0": version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.916.0.tgz#c76861ec87f9edf227af62474411bf54ca04805d" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/credential-provider-env/-/credential-provider-env-3.916.0.tgz#c76861ec87f9edf227af62474411bf54ca04805d" integrity sha512-3gDeqOXcBRXGHScc6xb7358Lyf64NRG2P08g6Bu5mv1Vbg9PKDyCAZvhKLkG7hkdfAM8Yc6UJNhbFxr1ud/tCQ== dependencies: "@aws-sdk/core" "3.916.0" @@ -273,7 +273,7 @@ "@aws-sdk/credential-provider-http@3.916.0": version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.916.0.tgz#b46e51c5cc65364c5fde752b4d016b5b747c6d89" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/credential-provider-http/-/credential-provider-http-3.916.0.tgz#b46e51c5cc65364c5fde752b4d016b5b747c6d89" integrity sha512-NmooA5Z4/kPFJdsyoJgDxuqXC1C6oPMmreJjbOPqcwo6E/h2jxaG8utlQFgXe5F9FeJsMx668dtxVxSYnAAqHQ== dependencies: "@aws-sdk/core" "3.916.0" @@ -289,7 +289,7 @@ "@aws-sdk/credential-provider-ini@3.917.0": version "3.917.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.917.0.tgz#d9255ffeaab2326e94e84a830668aa4182317294" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.917.0.tgz#d9255ffeaab2326e94e84a830668aa4182317294" integrity sha512-rvQ0QamLySRq+Okc0ZqFHZ3Fbvj3tYuWNIlzyEKklNmw5X5PM1idYKlOJflY2dvUGkIqY3lUC9SC2WL+1s7KIw== dependencies: "@aws-sdk/core" "3.916.0" @@ -308,7 +308,7 @@ "@aws-sdk/credential-provider-node@3.917.0": version "3.917.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.917.0.tgz#a508038c12dc5ba177cc27ff0c26ea48d3702125" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/credential-provider-node/-/credential-provider-node-3.917.0.tgz#a508038c12dc5ba177cc27ff0c26ea48d3702125" integrity sha512-n7HUJ+TgU9wV/Z46yR1rqD9hUjfG50AKi+b5UXTlaDlVD8bckg40i77ROCllp53h32xQj/7H0yBIYyphwzLtmg== dependencies: "@aws-sdk/credential-provider-env" "3.916.0" @@ -326,7 +326,7 @@ "@aws-sdk/credential-provider-process@3.916.0": version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.916.0.tgz#7c5aa9642a0e1c2a2791d85fe1bedfecae73672e" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/credential-provider-process/-/credential-provider-process-3.916.0.tgz#7c5aa9642a0e1c2a2791d85fe1bedfecae73672e" integrity sha512-SXDyDvpJ1+WbotZDLJW1lqP6gYGaXfZJrgFSXIuZjHb75fKeNRgPkQX/wZDdUvCwdrscvxmtyJorp2sVYkMcvA== dependencies: "@aws-sdk/core" "3.916.0" @@ -338,7 +338,7 @@ "@aws-sdk/credential-provider-sso@3.916.0": version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.916.0.tgz#b99ff591e758a56eefe7b05f1e77efe8f28f8c16" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.916.0.tgz#b99ff591e758a56eefe7b05f1e77efe8f28f8c16" integrity sha512-gu9D+c+U/Dp1AKBcVxYHNNoZF9uD4wjAKYCjgSN37j4tDsazwMEylbbZLuRNuxfbXtizbo4/TiaxBXDbWM7AkQ== dependencies: "@aws-sdk/client-sso" "3.916.0" @@ -352,7 +352,7 @@ "@aws-sdk/credential-provider-web-identity@3.917.0": version "3.917.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.917.0.tgz#4a9bdc3dae13f5802aaa2d6e51249dfed029d9d6" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.917.0.tgz#4a9bdc3dae13f5802aaa2d6e51249dfed029d9d6" integrity sha512-pZncQhFbwW04pB0jcD5OFv3x2gAddDYCVxyJVixgyhSw7bKCYxqu6ramfq1NxyVpmm+qsw+ijwi/3cCmhUHF/A== dependencies: "@aws-sdk/core" "3.916.0" @@ -365,7 +365,7 @@ "@aws-sdk/middleware-bucket-endpoint@3.914.0": version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.914.0.tgz#4500425660d45af30e1bb66d8ce9362e040b9c7d" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.914.0.tgz#4500425660d45af30e1bb66d8ce9362e040b9c7d" integrity sha512-mHLsVnPPp4iq3gL2oEBamfpeETFV0qzxRHmcnCfEP3hualV8YF8jbXGmwPCPopUPQDpbYDBHYtXaoClZikCWPQ== dependencies: "@aws-sdk/types" "3.914.0" @@ -378,7 +378,7 @@ "@aws-sdk/middleware-expect-continue@3.917.0": version "3.917.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.917.0.tgz#f0e0cacad99d048c46cdce8f9dbe47351e59a0f5" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.917.0.tgz#f0e0cacad99d048c46cdce8f9dbe47351e59a0f5" integrity sha512-UPBq1ZP2CaxwbncWSbVqkhYXQrmfNiqAtHyBxi413hjRVZ4JhQ1UyH7pz5yqiG8zx2/+Po8cUD4SDUwJgda4nw== dependencies: "@aws-sdk/types" "3.914.0" @@ -388,7 +388,7 @@ "@aws-sdk/middleware-flexible-checksums@3.916.0": version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.916.0.tgz#ecbec3baf54e79dae04f1fd19f21041482928239" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.916.0.tgz#ecbec3baf54e79dae04f1fd19f21041482928239" integrity sha512-CBRRg6slHHBYAm26AWY/pECHK0vVO/peDoNhZiAzUNt4jV6VftotjszEJ904pKGOr7/86CfZxtCnP3CCs3lQjA== dependencies: "@aws-crypto/crc32" "5.2.0" @@ -407,7 +407,7 @@ "@aws-sdk/middleware-host-header@3.914.0": version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.914.0.tgz#7e962c3d18c1ecc98606eab09a98dcf1b3402835" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/middleware-host-header/-/middleware-host-header-3.914.0.tgz#7e962c3d18c1ecc98606eab09a98dcf1b3402835" integrity sha512-7r9ToySQ15+iIgXMF/h616PcQStByylVkCshmQqcdeynD/lCn2l667ynckxW4+ql0Q+Bo/URljuhJRxVJzydNA== dependencies: "@aws-sdk/types" "3.914.0" @@ -417,7 +417,7 @@ "@aws-sdk/middleware-location-constraint@3.914.0": version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.914.0.tgz#ee877bdaa54746f65919fa54685ef392256bfb19" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.914.0.tgz#ee877bdaa54746f65919fa54685ef392256bfb19" integrity sha512-Mpd0Sm9+GN7TBqGnZg1+dO5QZ/EOYEcDTo7KfvoyrXScMlxvYm9fdrUVMmLdPn/lntweZGV3uNrs+huasGOOTA== dependencies: "@aws-sdk/types" "3.914.0" @@ -426,7 +426,7 @@ "@aws-sdk/middleware-logger@3.914.0": version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.914.0.tgz#222d50ec69447715d6954eb6db0029f11576227b" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/middleware-logger/-/middleware-logger-3.914.0.tgz#222d50ec69447715d6954eb6db0029f11576227b" integrity sha512-/gaW2VENS5vKvJbcE1umV4Ag3NuiVzpsANxtrqISxT3ovyro29o1RezW/Avz/6oJqjnmgz8soe9J1t65jJdiNg== dependencies: "@aws-sdk/types" "3.914.0" @@ -435,7 +435,7 @@ "@aws-sdk/middleware-recursion-detection@3.914.0": version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.914.0.tgz#bf65759cf303f271b22770e7f9675034b4ced946" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.914.0.tgz#bf65759cf303f271b22770e7f9675034b4ced946" integrity sha512-yiAjQKs5S2JKYc+GrkvGMwkUvhepXDigEXpSJqUseR/IrqHhvGNuOxDxq+8LbDhM4ajEW81wkiBbU+Jl9G82yQ== dependencies: "@aws-sdk/types" "3.914.0" @@ -446,7 +446,7 @@ "@aws-sdk/middleware-sdk-s3@3.916.0": version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.916.0.tgz#5c1cc4645186b3c0f7ac5f6a897885af0b62198e" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.916.0.tgz#5c1cc4645186b3c0f7ac5f6a897885af0b62198e" integrity sha512-pjmzzjkEkpJObzmTthqJPq/P13KoNFuEi/x5PISlzJtHofCNcyXeVAQ90yvY2dQ6UXHf511Rh1/ytiKy2A8M0g== dependencies: "@aws-sdk/core" "3.916.0" @@ -466,7 +466,7 @@ "@aws-sdk/middleware-ssec@3.914.0": version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.914.0.tgz#4042dfed7a4d4234e37a84bab9d1cd9998a22180" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/middleware-ssec/-/middleware-ssec-3.914.0.tgz#4042dfed7a4d4234e37a84bab9d1cd9998a22180" integrity sha512-V1Oae/oLVbpNb9uWs+v80GKylZCdsbqs2c2Xb1FsAUPtYeSnxFuAWsF3/2AEMSSpFe0dTC5KyWr/eKl2aim9VQ== dependencies: "@aws-sdk/types" "3.914.0" @@ -475,7 +475,7 @@ "@aws-sdk/middleware-user-agent@3.916.0": version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.916.0.tgz#a0894ae6d70d7a81b2572ee69ed0d3049d39dfce" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.916.0.tgz#a0894ae6d70d7a81b2572ee69ed0d3049d39dfce" integrity sha512-mzF5AdrpQXc2SOmAoaQeHpDFsK2GE6EGcEACeNuoESluPI2uYMpuuNMYrUufdnIAIyqgKlis0NVxiahA5jG42w== dependencies: "@aws-sdk/core" "3.916.0" @@ -488,7 +488,7 @@ "@aws-sdk/nested-clients@3.916.0": version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.916.0.tgz#2f79b924dd6c25cc3c40f6a0453097ae7a512702" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/nested-clients/-/nested-clients-3.916.0.tgz#2f79b924dd6c25cc3c40f6a0453097ae7a512702" integrity sha512-tgg8e8AnVAer0rcgeWucFJ/uNN67TbTiDHfD+zIOPKep0Z61mrHEoeT/X8WxGIOkEn4W6nMpmS4ii8P42rNtnA== dependencies: "@aws-crypto/sha256-browser" "5.2.0" @@ -532,7 +532,7 @@ "@aws-sdk/region-config-resolver@3.914.0": version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.914.0.tgz#b6d2825081195ce1c634b8c92b1e19b08f140008" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/region-config-resolver/-/region-config-resolver-3.914.0.tgz#b6d2825081195ce1c634b8c92b1e19b08f140008" integrity sha512-KlmHhRbn1qdwXUdsdrJ7S/MAkkC1jLpQ11n+XvxUUUCGAJd1gjC7AjxPZUM7ieQ2zcb8bfEzIU7al+Q3ZT0u7Q== dependencies: "@aws-sdk/types" "3.914.0" @@ -542,7 +542,7 @@ "@aws-sdk/signature-v4-multi-region@3.916.0": version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.916.0.tgz#d70e3dc9ca2cb3f65923283600a0a6e9a6c4ec7f" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.916.0.tgz#d70e3dc9ca2cb3f65923283600a0a6e9a6c4ec7f" integrity sha512-fuzUMo6xU7e0NBzBA6TQ4FUf1gqNbg4woBSvYfxRRsIfKmSMn9/elXXn4sAE5UKvlwVQmYnb6p7dpVRPyFvnQA== dependencies: "@aws-sdk/middleware-sdk-s3" "3.916.0" @@ -554,7 +554,7 @@ "@aws-sdk/token-providers@3.916.0": version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.916.0.tgz#e824fd44a553c4047b769caf22a94fd2705c9f1d" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/token-providers/-/token-providers-3.916.0.tgz#e824fd44a553c4047b769caf22a94fd2705c9f1d" integrity sha512-13GGOEgq5etbXulFCmYqhWtpcEQ6WI6U53dvXbheW0guut8fDFJZmEv7tKMTJgiybxh7JHd0rWcL9JQND8DwoQ== dependencies: "@aws-sdk/core" "3.916.0" @@ -567,7 +567,7 @@ "@aws-sdk/types@3.914.0": version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.914.0.tgz#175cf9a4b2267aafbb110fe1316e6827de951fdb" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/types/-/types-3.914.0.tgz#175cf9a4b2267aafbb110fe1316e6827de951fdb" integrity sha512-kQWPsRDmom4yvAfyG6L1lMmlwnTzm1XwMHOU+G5IFlsP4YEaMtXidDzW/wiivY0QFrhfCz/4TVmu0a2aPU57ug== dependencies: "@smithy/types" "^4.8.0" @@ -590,7 +590,7 @@ "@aws-sdk/util-endpoints@3.916.0": version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.916.0.tgz#ab54249b8090cd66fe14aa8518097107a2595196" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/util-endpoints/-/util-endpoints-3.916.0.tgz#ab54249b8090cd66fe14aa8518097107a2595196" integrity sha512-bAgUQwvixdsiGNcuZSDAOWbyHlnPtg8G8TyHD6DTfTmKTHUW6tAn+af/ZYJPXEzXhhpwgJqi58vWnsiDhmr7NQ== dependencies: "@aws-sdk/types" "3.914.0" @@ -608,7 +608,7 @@ "@aws-sdk/util-user-agent-browser@3.914.0": version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.914.0.tgz#ed29fd87f6ffba6f53615894a5e969cb9013af59" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.914.0.tgz#ed29fd87f6ffba6f53615894a5e969cb9013af59" integrity sha512-rMQUrM1ECH4kmIwlGl9UB0BtbHy6ZuKdWFrIknu8yGTRI/saAucqNTh5EI1vWBxZ0ElhK5+g7zOnUuhSmVQYUA== dependencies: "@aws-sdk/types" "3.914.0" @@ -618,7 +618,7 @@ "@aws-sdk/util-user-agent-node@3.916.0": version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.916.0.tgz#3ab5fdb9f45345f19f426941ece71988b31bf58d" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.916.0.tgz#3ab5fdb9f45345f19f426941ece71988b31bf58d" integrity sha512-CwfWV2ch6UdjuSV75ZU99N03seEUb31FIUrXBnwa6oONqj/xqXwrxtlUMLx6WH3OJEE4zI3zt5PjlTdGcVwf4g== dependencies: "@aws-sdk/middleware-user-agent" "3.916.0" @@ -629,7 +629,7 @@ "@aws-sdk/xml-builder@3.914.0": version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.914.0.tgz#4e98b479856113db877d055e7b008065c50266d4" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@aws-sdk/xml-builder/-/xml-builder-3.914.0.tgz#4e98b479856113db877d055e7b008065c50266d4" integrity sha512-k75evsBD5TcIjedycYS7QXQ98AmOtbnxRJOPtCo0IwYRmy7UvqgS/gBL5SmrIqeV6FDSYRQMgdBxSMp6MLmdew== dependencies: "@smithy/types" "^4.8.0" @@ -1658,7 +1658,7 @@ "@inquirer/ansi@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@inquirer/ansi/-/ansi-1.0.1.tgz#994f7dd16a00c547a7b110e04bf4f4eca1857929" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/ansi/-/ansi-1.0.1.tgz#994f7dd16a00c547a7b110e04bf4f4eca1857929" integrity sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw== "@inquirer/checkbox@^2.5.0": @@ -1674,7 +1674,7 @@ "@inquirer/checkbox@^4.3.0": version "4.3.0" - resolved "https://registry.yarnpkg.com/@inquirer/checkbox/-/checkbox-4.3.0.tgz#747ab0ec9b385dd77d3215a51fc9abe25f556a4b" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/checkbox/-/checkbox-4.3.0.tgz#747ab0ec9b385dd77d3215a51fc9abe25f556a4b" integrity sha512-5+Q3PKH35YsnoPTh75LucALdAxom6xh5D1oeY561x4cqBuH24ZFVyFREPe14xgnrtmGu3EEt1dIi60wRVSnGCw== dependencies: "@inquirer/ansi" "^1.0.1" @@ -1693,7 +1693,7 @@ "@inquirer/confirm@^5.1.19": version "5.1.19" - resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-5.1.19.tgz#bf28b420898999eb7479ab55623a3fbaf1453ff4" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/confirm/-/confirm-5.1.19.tgz#bf28b420898999eb7479ab55623a3fbaf1453ff4" integrity sha512-wQNz9cfcxrtEnUyG5PndC8g3gZ7lGDBzmWiXZkX8ot3vfZ+/BLjR8EvyGX4YzQLeVqtAlY/YScZpW7CW8qMoDQ== dependencies: "@inquirer/core" "^10.3.0" @@ -1701,7 +1701,7 @@ "@inquirer/core@^10.3.0": version "10.3.0" - resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-10.3.0.tgz#342e4fd62cbd33ea62089364274995dbec1f2ffe" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/core/-/core-10.3.0.tgz#342e4fd62cbd33ea62089364274995dbec1f2ffe" integrity sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA== dependencies: "@inquirer/ansi" "^1.0.1" @@ -1743,7 +1743,7 @@ "@inquirer/editor@^4.2.21": version "4.2.21" - resolved "https://registry.yarnpkg.com/@inquirer/editor/-/editor-4.2.21.tgz#9ffe641760a1a1f7722c39be00143060537adcc7" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/editor/-/editor-4.2.21.tgz#9ffe641760a1a1f7722c39be00143060537adcc7" integrity sha512-MjtjOGjr0Kh4BciaFShYpZ1s9400idOdvQ5D7u7lE6VztPFoyLcVNE5dXBmEEIQq5zi4B9h2kU+q7AVBxJMAkQ== dependencies: "@inquirer/core" "^10.3.0" @@ -1761,7 +1761,7 @@ "@inquirer/expand@^4.0.21": version "4.0.21" - resolved "https://registry.yarnpkg.com/@inquirer/expand/-/expand-4.0.21.tgz#3b22eb3d9961bdbad6edb2a956cfcadc15be9128" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/expand/-/expand-4.0.21.tgz#3b22eb3d9961bdbad6edb2a956cfcadc15be9128" integrity sha512-+mScLhIcbPFmuvU3tAGBed78XvYHSvCl6dBiYMlzCLhpr0bzGzd8tfivMMeqND6XZiaZ1tgusbUHJEfc6YzOdA== dependencies: "@inquirer/core" "^10.3.0" @@ -1770,7 +1770,7 @@ "@inquirer/external-editor@^1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@inquirer/external-editor/-/external-editor-1.0.2.tgz#dc16e7064c46c53be09918db639ff780718c071a" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/external-editor/-/external-editor-1.0.2.tgz#dc16e7064c46c53be09918db639ff780718c071a" integrity sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ== dependencies: chardet "^2.1.0" @@ -1778,7 +1778,7 @@ "@inquirer/figures@^1.0.14": version "1.0.14" - resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.14.tgz#12a7bfd344a83ae6cc5d6004b389ed11f6db6be4" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/figures/-/figures-1.0.14.tgz#12a7bfd344a83ae6cc5d6004b389ed11f6db6be4" integrity sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ== "@inquirer/figures@^1.0.5": @@ -1796,7 +1796,7 @@ "@inquirer/input@^4.2.5": version "4.2.5" - resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-4.2.5.tgz#40fe0a4b585c367089b57ef455da4980fbc5480f" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/input/-/input-4.2.5.tgz#40fe0a4b585c367089b57ef455da4980fbc5480f" integrity sha512-7GoWev7P6s7t0oJbenH0eQ0ThNdDJbEAEtVt9vsrYZ9FulIokvd823yLyhQlWHJPGce1wzP53ttfdCZmonMHyA== dependencies: "@inquirer/core" "^10.3.0" @@ -1812,7 +1812,7 @@ "@inquirer/number@^3.0.21": version "3.0.21" - resolved "https://registry.yarnpkg.com/@inquirer/number/-/number-3.0.21.tgz#fb8fac4c8bd08471b1068dc89f42d61fe3a43ca9" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/number/-/number-3.0.21.tgz#fb8fac4c8bd08471b1068dc89f42d61fe3a43ca9" integrity sha512-5QWs0KGaNMlhbdhOSCFfKsW+/dcAVC2g4wT/z2MCiZM47uLgatC5N20kpkDQf7dHx+XFct/MJvvNGy6aYJn4Pw== dependencies: "@inquirer/core" "^10.3.0" @@ -1829,7 +1829,7 @@ "@inquirer/password@^4.0.21": version "4.0.21" - resolved "https://registry.yarnpkg.com/@inquirer/password/-/password-4.0.21.tgz#b3422a19621290f2270f9b2ef8eeded8cf85db4f" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/password/-/password-4.0.21.tgz#b3422a19621290f2270f9b2ef8eeded8cf85db4f" integrity sha512-xxeW1V5SbNFNig2pLfetsDb0svWlKuhmr7MPJZMYuDnCTkpVBI+X/doudg4pznc1/U+yYmWFFOi4hNvGgUo7EA== dependencies: "@inquirer/ansi" "^1.0.1" @@ -1854,7 +1854,7 @@ "@inquirer/prompts@^7.9.0": version "7.9.0" - resolved "https://registry.yarnpkg.com/@inquirer/prompts/-/prompts-7.9.0.tgz#497718b2ac43b15cac636d8f7b501efd1574e1cd" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/prompts/-/prompts-7.9.0.tgz#497718b2ac43b15cac636d8f7b501efd1574e1cd" integrity sha512-X7/+dG9SLpSzRkwgG5/xiIzW0oMrV3C0HOa7YHG1WnrLK+vCQHfte4k/T80059YBdei29RBC3s+pSMvPJDU9/A== dependencies: "@inquirer/checkbox" "^4.3.0" @@ -1879,7 +1879,7 @@ "@inquirer/rawlist@^4.1.9": version "4.1.9" - resolved "https://registry.yarnpkg.com/@inquirer/rawlist/-/rawlist-4.1.9.tgz#b4641cb54e130049a13bd1b7621ac766c6d531f2" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/rawlist/-/rawlist-4.1.9.tgz#b4641cb54e130049a13bd1b7621ac766c6d531f2" integrity sha512-AWpxB7MuJrRiSfTKGJ7Y68imYt8P9N3Gaa7ySdkFj1iWjr6WfbGAhdZvw/UnhFXTHITJzxGUI9k8IX7akAEBCg== dependencies: "@inquirer/core" "^10.3.0" @@ -1898,7 +1898,7 @@ "@inquirer/search@^3.2.0": version "3.2.0" - resolved "https://registry.yarnpkg.com/@inquirer/search/-/search-3.2.0.tgz#fef378965592e9f407cd4f1f782ca40df1b3ed5e" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/search/-/search-3.2.0.tgz#fef378965592e9f407cd4f1f782ca40df1b3ed5e" integrity sha512-a5SzB/qrXafDX1Z4AZW3CsVoiNxcIYCzYP7r9RzrfMpaLpB+yWi5U8BWagZyLmwR0pKbbL5umnGRd0RzGVI8bQ== dependencies: "@inquirer/core" "^10.3.0" @@ -1919,7 +1919,7 @@ "@inquirer/select@^4.4.0": version "4.4.0" - resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-4.4.0.tgz#e19d0d0fbfcd5cb4a20f292e62c88aa8155cc6dc" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/select/-/select-4.4.0.tgz#e19d0d0fbfcd5cb4a20f292e62c88aa8155cc6dc" integrity sha512-kaC3FHsJZvVyIjYBs5Ih8y8Bj4P/QItQWrZW22WJax7zTN+ZPXVGuOM55vzbdCP9zKUiBd9iEJVdesujfF+cAA== dependencies: "@inquirer/ansi" "^1.0.1" @@ -1937,7 +1937,7 @@ "@inquirer/type@^3.0.9": version "3.0.9" - resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-3.0.9.tgz#f7f9696e9276e4e1ae9332767afb9199992e31d9" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@inquirer/type/-/type-3.0.9.tgz#f7f9696e9276e4e1ae9332767afb9199992e31d9" integrity sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w== "@isaacs/cliui@^8.0.2": @@ -2103,10 +2103,10 @@ "@babel/parser" "^7.9.0" "@babel/types" "^7.9.0" -"@komaci/esm-generator@258.0.0": - version "258.0.0" - resolved "https://registry.yarnpkg.com/@komaci/esm-generator/-/esm-generator-258.0.0.tgz#b11e9fa1eb9d21168cd6e1a1f91003912e4f5fdd" - integrity sha512-HR9XDNzaIzq3Ioh4PIi43tceamwUPETL3UhRStkkAIna1az9LjkXDP37SzRzn9+doUlSyCJdM7xkyyWcQRLlrQ== +"@komaci/esm-generator@260.31.0": + version "260.31.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@komaci/esm-generator/-/esm-generator-260.31.0.tgz#bbebbaa5b0ee41f387d9a09899125d149fe81d30" + integrity sha512-7TzTckrdoLqxSI63A5QZBsPpQPr3QOFNrjkwWETp44+EXeCRmE1tzNztbSoePVMV/o+JQI2FaZDTTTsg5BuKfA== dependencies: "@babel/core" "^7.9.0" "@babel/generator" "^7.9.0" @@ -2206,12 +2206,12 @@ "@lwc/aria-reflection@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/aria-reflection/-/aria-reflection-8.23.0.tgz#b4d1f31e5f380d49e28b6dfe07c505d0657024e4" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/aria-reflection/-/aria-reflection-8.23.0.tgz#b4d1f31e5f380d49e28b6dfe07c505d0657024e4" integrity sha512-WGaMjNc84art9cYtkMf15dCKZ7M1x1yldsbqctiKOTl6mN6d7EDRaV+046EkFMkC/FwQbspWTHF9x1UGqWV8pw== "@lwc/babel-plugin-component@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/babel-plugin-component/-/babel-plugin-component-8.23.0.tgz#756762acb6c5ba17f18614d8a858f377a4e55fbc" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/babel-plugin-component/-/babel-plugin-component-8.23.0.tgz#756762acb6c5ba17f18614d8a858f377a4e55fbc" integrity sha512-Dct16w1mSoL0gIZFNSQI6EQjOAnOkmdbCBAf2PMD7mJXQKNYYgb4RcA4BDIoZZJwe5nH7rd6q47YaeD7pdxCvg== dependencies: "@babel/helper-module-imports" "7.27.1" @@ -2221,7 +2221,7 @@ "@lwc/compiler@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/compiler/-/compiler-8.23.0.tgz#bc5f5406e8d71d4675cfe7276b2443544116f0a7" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/compiler/-/compiler-8.23.0.tgz#bc5f5406e8d71d4675cfe7276b2443544116f0a7" integrity sha512-ejGsAR9c+Lv3Xtu6XC8RbNl7XUIt3tIES3g4r3FanrU5FHQ2fsqXdLTT2I9jxlTiKHiRnvpq49pQQLWQcUPr0Q== dependencies: "@babel/core" "7.28.4" @@ -2237,16 +2237,16 @@ "@lwc/style-compiler" "8.23.0" "@lwc/template-compiler" "8.23.0" -"@lwc/dev-server-plugin-lex@13.2.7": - version "13.2.7" - resolved "https://registry.yarnpkg.com/@lwc/dev-server-plugin-lex/-/dev-server-plugin-lex-13.2.7.tgz#78dc9a2018bdfa4646accb1478c2b58fe7ba4f9d" - integrity sha512-lngzMKZM7iq2utG5xSxfkt8GH/N6V6ThK8tDx8SN7d9c95r88DZnku77UbLDnEdCGqDrtYLyHzWWDl/xgGfmew== +"@lwc/dev-server-plugin-lex@13.2.8": + version "13.2.8" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/dev-server-plugin-lex/-/dev-server-plugin-lex-13.2.8.tgz#59b8979bb3e775bb9a8d6948db97d9e1aab8e3ab" + integrity sha512-px6B1T+tukSUvPmIWcPwsB/vvh/UnDadjUaJx1glI9TIAyF1EykRvmuTSPsATqaiV1Mjwb+uc8K90HpRmFLHOQ== dependencies: magic-string "~0.30.17" "@lwc/engine-core@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/engine-core/-/engine-core-8.23.0.tgz#04ac9204140685c5d4f69cc3fe8c8bb6d7ffe4df" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/engine-core/-/engine-core-8.23.0.tgz#04ac9204140685c5d4f69cc3fe8c8bb6d7ffe4df" integrity sha512-YFobCuGRv0me1FLhJqtSDm/WxTpRyPtdHbfBe/6AKWDFfYYPk83tcMFTTS7ndE3LrYno2aP9ABHdg5BtAl55iA== dependencies: "@lwc/features" "8.23.0" @@ -2255,17 +2255,17 @@ "@lwc/engine-dom@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/engine-dom/-/engine-dom-8.23.0.tgz#dd45d7c33a318dcdf600796478a7e03e759122df" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/engine-dom/-/engine-dom-8.23.0.tgz#dd45d7c33a318dcdf600796478a7e03e759122df" integrity sha512-cbMVwFkYmhFFmCpnSYBePk8wnpdhqESVFDZJqFgeR4Tb0LBVlI18gGqHn9MmjxT3SnurDp1EW/ArH8QFYJgf1g== "@lwc/engine-server@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/engine-server/-/engine-server-8.23.0.tgz#6675516060b8886c23e18faf92ab31b69d6c7053" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/engine-server/-/engine-server-8.23.0.tgz#6675516060b8886c23e18faf92ab31b69d6c7053" integrity sha512-h4HOYAoHWAPEwITroai8yAy6YSlqMXRLdVZNRNH/ZEXkz5Hom+h16BbnGGeGCNqZgGrm58LnCPYmmzeXIZ1aPQ== "@lwc/errors@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/errors/-/errors-8.23.0.tgz#7c230010e197062a0eae04a4caa4abe4e7df4af3" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/errors/-/errors-8.23.0.tgz#7c230010e197062a0eae04a4caa4abe4e7df4af3" integrity sha512-cqziHl/aDg0pgAIm9HhNz2OrR0t08vML7+FNrqgrnaSwLgYCYp2WPNmvMjD5ZRy/bIBESwA/dCWf9Cs7Vo/ZGw== "@lwc/eslint-plugin-lwc-platform@6.2.0": @@ -2285,23 +2285,23 @@ "@lwc/features@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/features/-/features-8.23.0.tgz#7e0578c89dc39e62d50b732facca7e9e969539e9" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/features/-/features-8.23.0.tgz#7e0578c89dc39e62d50b732facca7e9e969539e9" integrity sha512-Y1yVOH6LAJMWJaeUHJwjx7OHjbRxycdSSZKewhQlRTGLrmca1Rtpi31nfrRtcCdOkxfF3oPMFlPlA4ZKDyA6aA== dependencies: "@lwc/shared" "8.23.0" -"@lwc/lwc-dev-server-types@13.2.7": - version "13.2.7" - resolved "https://registry.yarnpkg.com/@lwc/lwc-dev-server-types/-/lwc-dev-server-types-13.2.7.tgz#e6f9750b27b9a8b8dce87630a96e5c49d0e8d8a3" - integrity sha512-yAyzyGRqYqWCrNqbG5Oq/1PUALaB63u+gChczswdnua80X2QbJ5Oos7bniDAKE9kXaGkbCkqI9xnGSZRn6kPfQ== +"@lwc/lwc-dev-server-types@13.2.8": + version "13.2.8" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/lwc-dev-server-types/-/lwc-dev-server-types-13.2.8.tgz#a4d4ed6fa7f85d98b6a913f0ea2b6258b8282f2e" + integrity sha512-tEQIECkQIW9S10/3eYIJ8/ggAD5drSfMWYt4Hdzcn0xUqB46/aEohkFEpiSo9pKMKYchV0qbd6/OhCa45hpBHw== "@lwc/lwc-dev-server@~13.2.7": - version "13.2.7" - resolved "https://registry.yarnpkg.com/@lwc/lwc-dev-server/-/lwc-dev-server-13.2.7.tgz#21d4456e981ccdb737c9f131172fea48d1e69ff7" - integrity sha512-1DwXCVXKIUo1DBlrxtFxjrdJcwNUsZDrjYzlluF8dJ8T/qG8RhXmGnzOBqMrTq3SJ02y1yU3hRhiEMPfOtyb1g== + version "13.2.8" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/lwc-dev-server/-/lwc-dev-server-13.2.8.tgz#daba3ad4424936ccc1c41ce9f54f5a380ddf6cd5" + integrity sha512-LJTSn9iMJG1ZTgt3WfcpA5YCANizr9RJZHQEOCT2ct9mMlvoaviW6zIfvMoOV5tQJgDm2uUrs7EE+KMQlv66Rw== dependencies: - "@lwc/lwc-dev-server-types" "13.2.7" - "@lwc/sfdc-lwc-compiler" "13.2.7" + "@lwc/lwc-dev-server-types" "13.2.8" + "@lwc/sfdc-lwc-compiler" "13.2.8" chalk "~5.4.1" chokidar "~3.6.0" commander "~10.0.0" @@ -2309,29 +2309,29 @@ glob "^10.4.5" ws "^8.18.2" -"@lwc/metadata@13.2.7": - version "13.2.7" - resolved "https://registry.yarnpkg.com/@lwc/metadata/-/metadata-13.2.7.tgz#4bddc61bc3fac9c17713b99768c8fc7ed7156505" - integrity sha512-zOM/BKVOwMCaPYazgEZMnROSyRWFTb0cNQSHWwfSuOI680ma+4Z663jTxCsAqcAmhl/VNvD+Ny8B4xNeyzC2Vg== +"@lwc/metadata@13.2.8": + version "13.2.8" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/metadata/-/metadata-13.2.8.tgz#cab0e323ec9ff145883f2ffa305e9c3f81055ff5" + integrity sha512-TN94CY53MVAEk7ux3ikGBb+3ylAUepheP0Rk0dS75xDosNIsefuGHR4aRn08nnsBr2Tm+7qyw3LdZ85AvinwLw== dependencies: "@babel/parser" "~7.27.5" "@babel/traverse" "~7.27.4" "@babel/types" "~7.27.6" - "@lwc/sfdc-compiler-utils" "13.2.7" + "@lwc/sfdc-compiler-utils" "13.2.8" postcss "~8.5.5" postcss-selector-parser "~6.1.2" postcss-value-parser "~4.2.0" "@lwc/module-resolver@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/module-resolver/-/module-resolver-8.23.0.tgz#f98581796558d484516097b7b04121453846f9d1" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/module-resolver/-/module-resolver-8.23.0.tgz#f98581796558d484516097b7b04121453846f9d1" integrity sha512-ZqZ/402NvVswMK2HMhwH6Fmkzn19xn5Yx7VZr1QmIefKXr8OKqFSlsySujN3CSwNH9XHybyREWe4TXlkT7LHFw== dependencies: resolve "~1.22.10" "@lwc/rollup-plugin@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/rollup-plugin/-/rollup-plugin-8.23.0.tgz#6821076f721f4b298b2c40d81832ffb55a37c9f6" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/rollup-plugin/-/rollup-plugin-8.23.0.tgz#6821076f721f4b298b2c40d81832ffb55a37c9f6" integrity sha512-bDlnRXWOVN4VE+/h1dj2KXuej9bED2A07CtxHPepCH4iIwpN6w+s/495zDndJgO/VppnZ3ZUiUooUrcDOrOmBA== dependencies: "@lwc/compiler" "8.23.0" @@ -2339,15 +2339,15 @@ "@lwc/shared" "8.23.0" "@rollup/pluginutils" "~5.3.0" -"@lwc/sfdc-compiler-utils@13.2.7": - version "13.2.7" - resolved "https://registry.yarnpkg.com/@lwc/sfdc-compiler-utils/-/sfdc-compiler-utils-13.2.7.tgz#0e4580d7b0e5799137fac524201c1b546697e833" - integrity sha512-vONz/KO8K8edhNeCHOMqImwvqNoAZYWyu0rcvH1IsoQo6EjAClz2qTWNGFGycCnhAOKrDAPxI/odzVoS1sw8Ew== +"@lwc/sfdc-compiler-utils@13.2.8": + version "13.2.8" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/sfdc-compiler-utils/-/sfdc-compiler-utils-13.2.8.tgz#5c0fddd3f2518b8510365d6a246cbadd4e6201d9" + integrity sha512-osvX/a7W+/PoKKowbw0PuT9LvZlVOfnCUpWqafM5W607H8QYk5XFMToLbic6EQPih1vj7hygtRUuxIAcUUPJXg== -"@lwc/sfdc-lwc-compiler@13.2.7", "@lwc/sfdc-lwc-compiler@~13.2.7": - version "13.2.7" - resolved "https://registry.yarnpkg.com/@lwc/sfdc-lwc-compiler/-/sfdc-lwc-compiler-13.2.7.tgz#07b3921e50712a6367be1fa3a1d9256efb8927a7" - integrity sha512-FQLpvzZ1RFTRLWAf76cps7T7NMCkpo30MKztWzjTDcHuIZSQe1HKVosHc7db3Y/uOb9QIJSgkVx5GudBmWtXtw== +"@lwc/sfdc-lwc-compiler@13.2.8", "@lwc/sfdc-lwc-compiler@~13.2.7": + version "13.2.8" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/sfdc-lwc-compiler/-/sfdc-lwc-compiler-13.2.8.tgz#3a7e93fbec1e56c6eb44da16834a820c55950554" + integrity sha512-nItzk9KSNjG+lt0TUf+zoK5bljUtq+lmuf2r8lz945nWeNgWhgRgTi324+JeRQZ8vNBt6HD6RVHLgGn1Su0bIA== dependencies: "@babel/core" "7.27.4" "@babel/parser" "7.27.5" @@ -2355,12 +2355,12 @@ "@babel/preset-typescript" "7.27.1" "@babel/traverse" "7.27.4" "@babel/types" "7.27.6" - "@komaci/esm-generator" "258.0.0" - "@lwc/dev-server-plugin-lex" "13.2.7" + "@komaci/esm-generator" "260.31.0" + "@lwc/dev-server-plugin-lex" "13.2.8" "@lwc/eslint-plugin-lwc" "3.0.0-beta.2" "@lwc/eslint-plugin-lwc-platform" "6.2.0" - "@lwc/metadata" "13.2.7" - "@lwc/sfdc-compiler-utils" "13.2.7" + "@lwc/metadata" "13.2.8" + "@lwc/sfdc-compiler-utils" "13.2.8" "@rollup/plugin-babel" "^6.0.4" "@rollup/plugin-replace" "^6.0.2" "@rollup/wasm-node" "4.10.0" @@ -2383,17 +2383,17 @@ "@lwc/shared@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/shared/-/shared-8.23.0.tgz#c9304f7fd8db4256094e5cbf1960dd4f027aa599" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/shared/-/shared-8.23.0.tgz#c9304f7fd8db4256094e5cbf1960dd4f027aa599" integrity sha512-g6teckOlRJgPkqFJjjrMWoXwEbP3E0PByVIbrxfvv7gN/d8INL+TOA/Deg5ZgRMwuYUGO0Elr5rGcAK5jj/bhA== "@lwc/signals@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/signals/-/signals-8.23.0.tgz#c38177c9ccd20803392a99715e4770a9e9001104" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/signals/-/signals-8.23.0.tgz#c38177c9ccd20803392a99715e4770a9e9001104" integrity sha512-mdW1i0i4RBFracnevRN8YQtkUDI/WuWHsQXGQC2kluQAveM/qmVIkvMCPfehBsMwbXpEnYneUEe58XXnuCsAvA== "@lwc/ssr-compiler@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/ssr-compiler/-/ssr-compiler-8.23.0.tgz#cd3ff236701824188e7b9675e927092ffb34d1b2" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/ssr-compiler/-/ssr-compiler-8.23.0.tgz#cd3ff236701824188e7b9675e927092ffb34d1b2" integrity sha512-JucwFx+bjVsnyJnfJIbcX2DpaKO+h3vGEBlDPgI6cdaRfymtkxklxZojzc1HTcN+0XSGSiAmBcDn3MKxsNMrMQ== dependencies: "@babel/types" "7.28.4" @@ -2408,12 +2408,12 @@ "@lwc/ssr-runtime@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/ssr-runtime/-/ssr-runtime-8.23.0.tgz#29da9702f09992a3cf721fa88057c53dcdd7680a" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/ssr-runtime/-/ssr-runtime-8.23.0.tgz#29da9702f09992a3cf721fa88057c53dcdd7680a" integrity sha512-J4JSyEGX+DiBUoMIRBUTcrsc0GGI+LuczO4uSLoMIjFQJXjh5dmI058pVBYq5cCXJHTv2vbtUILzQtX3xcFb0A== "@lwc/style-compiler@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/style-compiler/-/style-compiler-8.23.0.tgz#508faaea6cd4b5df990cc2c0d91b5bbbe3fa905e" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/style-compiler/-/style-compiler-8.23.0.tgz#508faaea6cd4b5df990cc2c0d91b5bbbe3fa905e" integrity sha512-hIsmMgKyFQ3VSozQtHuU1BcAbbWyk/8BFygB2WdadM/cBrHfNCy+PGLofv8xkyvhDPrfbWBtwFrP9VIRXDdLNA== dependencies: "@lwc/shared" "8.23.0" @@ -2423,12 +2423,12 @@ "@lwc/synthetic-shadow@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/synthetic-shadow/-/synthetic-shadow-8.23.0.tgz#b209293ac9e1b03f778b71c802c5f2095e814067" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/synthetic-shadow/-/synthetic-shadow-8.23.0.tgz#b209293ac9e1b03f778b71c802c5f2095e814067" integrity sha512-wmFB6nMKlsH47+YW+Wr3HdhPdUbHor6yPzbsai85St8+xSlrCzQWuXPWuqv6raFyHg6YnWAiF2Hf5e2h9sdCig== "@lwc/template-compiler@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/template-compiler/-/template-compiler-8.23.0.tgz#323ee9d6476b94421b3041ae359b32ec5bfc0d91" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/template-compiler/-/template-compiler-8.23.0.tgz#323ee9d6476b94421b3041ae359b32ec5bfc0d91" integrity sha512-E24VtNe4Ej307ui8BuQncBzcd6MdzOjXjrhIOQDnGLzNnGL7I3cuGA2wVwTuV8WNrPg7JkgpJghUduEkN3kubw== dependencies: "@lwc/errors" "8.23.0" @@ -2439,14 +2439,14 @@ "@lwc/types@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/types/-/types-8.23.0.tgz#f7a3dca0e4c3a6649dbaf9e41eac9f3b99a6ae86" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/types/-/types-8.23.0.tgz#f7a3dca0e4c3a6649dbaf9e41eac9f3b99a6ae86" integrity sha512-MqRqq/eQu36/lI3MnPn4EAIW5JgYXIorlzpnQYLA6kBnViSCYcaDeJJil/FDIzKSF8HgHf7CuXVJ5MUkcXbqJw== dependencies: "@lwc/engine-core" "8.23.0" "@lwc/wire-service@8.23.0": version "8.23.0" - resolved "https://registry.yarnpkg.com/@lwc/wire-service/-/wire-service-8.23.0.tgz#c44f5197d921b5fbfa213cb954cccd11b78b9978" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@lwc/wire-service/-/wire-service-8.23.0.tgz#c44f5197d921b5fbfa213cb954cccd11b78b9978" integrity sha512-vAwzn6gSrC/C0FMIXUWl/Ieyg7xaY4SMoMuBiL36ChvtXfSJjHPhmeVjhMGkkGCXyDHS/3f5/9LhplaIi60EfQ== "@lwrjs/api@0.18.3": @@ -2855,9 +2855,33 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@oclif/core@^4", "@oclif/core@^4.0.27", "@oclif/core@^4.3.0", "@oclif/core@^4.4.0", "@oclif/core@^4.5.5", "@oclif/core@^4.5.6", "@oclif/core@^4.7.2": +"@oclif/core@^4", "@oclif/core@^4.0.27", "@oclif/core@^4.3.0", "@oclif/core@^4.4.0", "@oclif/core@^4.5.5": + version "4.5.5" + resolved "https://registry.yarnpkg.com/@oclif/core/-/core-4.5.5.tgz#91073acd6bdc6f62f8a2863a388d327dbe95967c" + integrity sha512-iQzlaJQgPeUXrtrX71OzDwxPikQ7c2FhNd8U8rBB7BCtj2XYfmzBT/Hmbc+g9OKDIG/JkbJT0fXaWMMBrhi+1A== + dependencies: + ansi-escapes "^4.3.2" + ansis "^3.17.0" + clean-stack "^3.0.1" + cli-spinners "^2.9.2" + debug "^4.4.3" + ejs "^3.1.10" + get-package-type "^0.1.0" + indent-string "^4.0.0" + is-wsl "^2.2.0" + lilconfig "^3.1.3" + minimatch "^9.0.5" + semver "^7.7.3" + string-width "^4.2.3" + supports-color "^8" + tinyglobby "^0.2.14" + widest-line "^3.1.0" + wordwrap "^1.0.0" + wrap-ansi "^7.0.0" + +"@oclif/core@^4.5.6", "@oclif/core@^4.7.2": version "4.7.2" - resolved "https://registry.yarnpkg.com/@oclif/core/-/core-4.7.2.tgz#9ebf36b4693500685956f3405c55526d191aa5ef" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@oclif/core/-/core-4.7.2.tgz#9ebf36b4693500685956f3405c55526d191aa5ef" integrity sha512-AmZnhEnyD7bFxmzEKRaOEr0kzonmwIip72eWZPWB5+7D9ayHa/QFX08zhaQT9eOo0//ed64v5p5QZIbYCbQaJQ== dependencies: ansi-escapes "^4.3.2" @@ -2903,7 +2927,7 @@ "@oclif/plugin-not-found@^3.2.71": version "3.2.71" - resolved "https://registry.yarnpkg.com/@oclif/plugin-not-found/-/plugin-not-found-3.2.71.tgz#1b8ac0e71d4a7ef8ee24425b9b8205bb3f1c9ef3" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@oclif/plugin-not-found/-/plugin-not-found-3.2.71.tgz#1b8ac0e71d4a7ef8ee24425b9b8205bb3f1c9ef3" integrity sha512-Vp93vWBzAyZFYtovQtAH3lBAtJE8Z0XUYu1/3uN2Y1kE7ywCNnivaEYRw8n4D3G4uF1g4GaXKAQP+HiYL/d2Ug== dependencies: "@inquirer/prompts" "^7.9.0" @@ -2911,10 +2935,10 @@ ansis "^3.17.0" fast-levenshtein "^3.0.0" -"@oclif/plugin-warn-if-update-available@^3.1.49": - version "3.1.50" - resolved "https://registry.yarnpkg.com/@oclif/plugin-warn-if-update-available/-/plugin-warn-if-update-available-3.1.50.tgz#f3a016becd63399712be8a73d2e4d2265ae5279d" - integrity sha512-JAN0qm5z4FrgZ5i1K1vDGCglOTYrdHtSwSi0R6EAqv0SlrlY5ZKDqpRFklT0i2KGr4M6XPoDr1QiDsZbpN62EQ== +"@oclif/plugin-warn-if-update-available@^3.1.50": + version "3.1.51" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@oclif/plugin-warn-if-update-available/-/plugin-warn-if-update-available-3.1.51.tgz#b101757fc713e93dfcb7ab3cdde7d2048d2f45bf" + integrity sha512-++PpRVemEasTc8X54EL4Td0BQz+DzRilWofUxmzVHnZGJsXcM8e9VdoKkrk5yUs/7sO+MqJm17Yvsk7JHqcN3A== dependencies: "@oclif/core" "^4" ansis "^3.17.0" @@ -3399,9 +3423,34 @@ strip-ansi "6.0.1" ts-retry-promise "^0.8.1" -"@salesforce/core@^8.15.0", "@salesforce/core@^8.23.1", "@salesforce/core@^8.23.2", "@salesforce/core@^8.23.3", "@salesforce/core@^8.5.1", "@salesforce/core@^8.8.0": +"@salesforce/core@^8.15.0", "@salesforce/core@^8.23.1", "@salesforce/core@^8.5.1", "@salesforce/core@^8.8.0": + version "8.23.2" + resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-8.23.2.tgz#218152b97e05745cd0499ad2594df4443fa8aa18" + integrity sha512-9XUlaI0orvdjDJZsgjt0lVLa5wrFWNaorRbshT/EJ6fIiqcAehOV2bR62NJtRhrOrgu1h34bTmUqMo+yUEES9w== + dependencies: + "@jsforce/jsforce-node" "^3.10.8" + "@salesforce/kit" "^3.2.4" + "@salesforce/schemas" "^1.10.0" + "@salesforce/ts-types" "^2.0.11" + ajv "^8.17.1" + change-case "^4.1.2" + fast-levenshtein "^3.0.0" + faye "^1.4.1" + form-data "^4.0.4" + js2xmlparser "^4.0.1" + jsonwebtoken "9.0.2" + jszip "3.10.1" + memfs "^4.30.1" + pino "^9.7.0" + pino-abstract-transport "^1.2.0" + pino-pretty "^11.3.0" + proper-lockfile "^4.1.2" + semver "^7.6.3" + ts-retry-promise "^0.8.1" + +"@salesforce/core@^8.23.3": version "8.23.3" - resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-8.23.3.tgz#23d92d6eb887e946e26989552a605fa085e626e8" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@salesforce/core/-/core-8.23.3.tgz#23d92d6eb887e946e26989552a605fa085e626e8" integrity sha512-BD9cOUOw3wTR8ud6dBacLvA4x0KAfQXkNGdxtU9ujz5nEW86ms5tU1AEUzVXnhuDrrtdQZh7/yTGxqg5mS7rZg== dependencies: "@jsforce/jsforce-node" "^3.10.8" @@ -3487,7 +3536,7 @@ "@salesforce/lwc-dev-mobile-core@4.0.0-alpha.13": version "4.0.0-alpha.13" - resolved "https://registry.yarnpkg.com/@salesforce/lwc-dev-mobile-core/-/lwc-dev-mobile-core-4.0.0-alpha.13.tgz#cab1ffc74646c7fa33add0f7d2b9600b4847df10" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@salesforce/lwc-dev-mobile-core/-/lwc-dev-mobile-core-4.0.0-alpha.13.tgz#cab1ffc74646c7fa33add0f7d2b9600b4847df10" integrity sha512-L76zOvWZE/D/rOKan8IXhNtL9dth+7sqBKwoBZ7dfxOESZ/33yI6bvJASHn04NcRCK1sawKVJ4YltDuVBn0T+A== dependencies: "@oclif/core" "^4.4.0" @@ -3500,12 +3549,12 @@ node-forge "^1.3.1" "@salesforce/plugin-command-reference@^3.1.74": - version "3.1.74" - resolved "https://registry.yarnpkg.com/@salesforce/plugin-command-reference/-/plugin-command-reference-3.1.74.tgz#94123aeef099049f01eb4e4e14f283b613281b4b" - integrity sha512-x2kBVbzP2hYsEdOW7BMteeqHgvWT1pa3jWJidFmbZGrOTS2wZhtZrHjE/O3hXMSGnQFTeJakjqr0oZJO8UCxpQ== + version "3.1.77" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@salesforce/plugin-command-reference/-/plugin-command-reference-3.1.77.tgz#a9c20064fe96424140427929f6df506bf3890a20" + integrity sha512-npuxDH+ewoJduPH1NBneIYjnsgeMV/9Vrm7PpA+foboap1rBI8DRyi32ZJvGfOBRIZz4s2H377Dw7Y3E4JujDg== dependencies: "@oclif/core" "^4" - "@salesforce/core" "^8.23.2" + "@salesforce/core" "^8.23.3" "@salesforce/kit" "^3.2.4" "@salesforce/sf-plugins-core" "^11.3.12" "@salesforce/ts-types" "^2.0.11" @@ -3518,9 +3567,9 @@ resolved "https://registry.yarnpkg.com/@salesforce/prettier-config/-/prettier-config-0.0.3.tgz#ba648d4886bb38adabe073dbea0b3a91b3753bb0" integrity sha512-hYOhoPTCSYMDYn+U1rlEk16PoBeAJPkrdg4/UtAzupM1mRRJOwEPMG1d7U8DxJFKuXW3DMEYWr2MwAIBDaHmFg== -"@salesforce/schemas@^1.10.3": +"@salesforce/schemas@^1.10.0", "@salesforce/schemas@^1.10.3": version "1.10.3" - resolved "https://registry.yarnpkg.com/@salesforce/schemas/-/schemas-1.10.3.tgz#52c867fdd60679cf216110aa49542b7ad391f5d1" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@salesforce/schemas/-/schemas-1.10.3.tgz#52c867fdd60679cf216110aa49542b7ad391f5d1" integrity sha512-FKfvtrYTcvTXE9advzS25/DEY9yJhEyLvStm++eQFtnAaX1pe4G3oGHgiQ0q55BM5+0AlCh0+0CVtQv1t4oJRA== "@salesforce/sf-plugins-core@^11.2.4", "@salesforce/sf-plugins-core@^11.3.12": @@ -3644,7 +3693,7 @@ "@smithy/abort-controller@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-4.2.3.tgz#4615da3012b580ac3d1f0ee7b57ed7d7880bb29b" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/abort-controller/-/abort-controller-4.2.3.tgz#4615da3012b580ac3d1f0ee7b57ed7d7880bb29b" integrity sha512-xWL9Mf8b7tIFuAlpjKtRPnHrR8XVrwTj5NPYO/QwZPtc0SDLsPxb56V5tzi5yspSMytISHybifez+4jlrx0vkQ== dependencies: "@smithy/types" "^4.8.0" @@ -3667,7 +3716,7 @@ "@smithy/config-resolver@^4.4.0": version "4.4.0" - resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-4.4.0.tgz#9a33b7dd9b7e0475802acef53f41555257e104cd" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/config-resolver/-/config-resolver-4.4.0.tgz#9a33b7dd9b7e0475802acef53f41555257e104cd" integrity sha512-Kkmz3Mup2PGp/HNJxhCWkLNdlajJORLSjwkcfrj0E7nu6STAEdcMR1ir5P9/xOmncx8xXfru0fbUYLlZog/cFg== dependencies: "@smithy/node-config-provider" "^4.3.3" @@ -3679,7 +3728,7 @@ "@smithy/core@^3.17.1": version "3.17.1" - resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.17.1.tgz#644aa4046b31c82d2c17276bcef2c6b78245dfeb" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/core/-/core-3.17.1.tgz#644aa4046b31c82d2c17276bcef2c6b78245dfeb" integrity sha512-V4Qc2CIb5McABYfaGiIYLTmo/vwNIK7WXI5aGveBd9UcdhbOMwcvIMxIw/DJj1S9QgOMa/7FBkarMdIC0EOTEQ== dependencies: "@smithy/middleware-serde" "^4.2.3" @@ -3695,7 +3744,7 @@ "@smithy/credential-provider-imds@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.3.tgz#b35d0d1f1b28f415e06282999eba2d53eb10a1c5" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.3.tgz#b35d0d1f1b28f415e06282999eba2d53eb10a1c5" integrity sha512-hA1MQ/WAHly4SYltJKitEsIDVsNmXcQfYBRv2e+q04fnqtAX5qXaybxy/fhUeAMCnQIdAjaGDb04fMHQefWRhw== dependencies: "@smithy/node-config-provider" "^4.3.3" @@ -3706,7 +3755,7 @@ "@smithy/eventstream-codec@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-4.2.3.tgz#dd65d9050c322f0805ba62749a3801985a2f5394" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/eventstream-codec/-/eventstream-codec-4.2.3.tgz#dd65d9050c322f0805ba62749a3801985a2f5394" integrity sha512-rcr0VH0uNoMrtgKuY7sMfyKqbHc4GQaQ6Yp4vwgm+Z6psPuOgL+i/Eo/QWdXRmMinL3EgFM0Z1vkfyPyfzLmjw== dependencies: "@aws-crypto/crc32" "5.2.0" @@ -3716,7 +3765,7 @@ "@smithy/eventstream-serde-browser@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.3.tgz#57fb9c10daac12647a0b97ef04330d706cbe9494" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.3.tgz#57fb9c10daac12647a0b97ef04330d706cbe9494" integrity sha512-EcS0kydOr2qJ3vV45y7nWnTlrPmVIMbUFOZbMG80+e2+xePQISX9DrcbRpVRFTS5Nqz3FiEbDcTCAV0or7bqdw== dependencies: "@smithy/eventstream-serde-universal" "^4.2.3" @@ -3725,7 +3774,7 @@ "@smithy/eventstream-serde-config-resolver@^4.3.3": version "4.3.3" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.3.tgz#ca1a7d272ae939aee303da40aa476656d785f75f" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.3.tgz#ca1a7d272ae939aee303da40aa476656d785f75f" integrity sha512-GewKGZ6lIJ9APjHFqR2cUW+Efp98xLu1KmN0jOWxQ1TN/gx3HTUPVbLciFD8CfScBj2IiKifqh9vYFRRXrYqXA== dependencies: "@smithy/types" "^4.8.0" @@ -3733,7 +3782,7 @@ "@smithy/eventstream-serde-node@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.3.tgz#f1b33bb576bf7222b6bd6bc2ad845068ccf53f16" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.3.tgz#f1b33bb576bf7222b6bd6bc2ad845068ccf53f16" integrity sha512-uQobOTQq2FapuSOlmGLUeGTpvcBLE5Fc7XjERUSk4dxEi4AhTwuyHYZNAvL4EMUp7lzxxkKDFaJ1GY0ovrj0Kg== dependencies: "@smithy/eventstream-serde-universal" "^4.2.3" @@ -3742,7 +3791,7 @@ "@smithy/eventstream-serde-universal@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.3.tgz#86194daa2cd2496e413723465360d80f32ad7252" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.3.tgz#86194daa2cd2496e413723465360d80f32ad7252" integrity sha512-QIvH/CKOk1BZPz/iwfgbh1SQD5Y0lpaw2kLA8zpLRRtYMPXeYUEWh+moTaJyqDaKlbrB174kB7FSRFiZ735tWw== dependencies: "@smithy/eventstream-codec" "^4.2.3" @@ -3751,7 +3800,7 @@ "@smithy/fetch-http-handler@^5.3.4": version "5.3.4" - resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.4.tgz#af6dd2f63550494c84ef029a5ceda81ef46965d3" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.4.tgz#af6dd2f63550494c84ef029a5ceda81ef46965d3" integrity sha512-bwigPylvivpRLCm+YK9I5wRIYjFESSVwl8JQ1vVx/XhCw0PtCi558NwTnT2DaVCl5pYlImGuQTSwMsZ+pIavRw== dependencies: "@smithy/protocol-http" "^5.3.3" @@ -3762,7 +3811,7 @@ "@smithy/hash-blob-browser@^4.2.4": version "4.2.4" - resolved "https://registry.yarnpkg.com/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.4.tgz#c7226d2ba2a394acf6e90510d08f7c3003f516d1" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.4.tgz#c7226d2ba2a394acf6e90510d08f7c3003f516d1" integrity sha512-W7eIxD+rTNsLB/2ynjmbdeP7TgxRXprfvqQxKFEfy9HW2HeD7t+g+KCIrY0pIn/GFjA6/fIpH+JQnfg5TTk76Q== dependencies: "@smithy/chunked-blob-reader" "^5.2.0" @@ -3772,7 +3821,7 @@ "@smithy/hash-node@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-4.2.3.tgz#c85711fca84e022f05c71b921f98cb6a0f48e5ca" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/hash-node/-/hash-node-4.2.3.tgz#c85711fca84e022f05c71b921f98cb6a0f48e5ca" integrity sha512-6+NOdZDbfuU6s1ISp3UOk5Rg953RJ2aBLNLLBEcamLjHAg1Po9Ha7QIB5ZWhdRUVuOUrT8BVFR+O2KIPmw027g== dependencies: "@smithy/types" "^4.8.0" @@ -3782,7 +3831,7 @@ "@smithy/hash-stream-node@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/hash-stream-node/-/hash-stream-node-4.2.3.tgz#8ddae1f5366513cbbec3acb6f54e3ec1b332db88" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/hash-stream-node/-/hash-stream-node-4.2.3.tgz#8ddae1f5366513cbbec3acb6f54e3ec1b332db88" integrity sha512-EXMSa2yiStVII3x/+BIynyOAZlS7dGvI7RFrzXa/XssBgck/7TXJIvnjnCu328GY/VwHDC4VeDyP1S4rqwpYag== dependencies: "@smithy/types" "^4.8.0" @@ -3791,7 +3840,7 @@ "@smithy/invalid-dependency@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-4.2.3.tgz#4f126ddde90fe3d69d522fc37256ee853246c1ec" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/invalid-dependency/-/invalid-dependency-4.2.3.tgz#4f126ddde90fe3d69d522fc37256ee853246c1ec" integrity sha512-Cc9W5DwDuebXEDMpOpl4iERo8I0KFjTnomK2RMdhhR87GwrSmUmwMxS4P5JdRf+LsjOdIqumcerwRgYMr/tZ9Q== dependencies: "@smithy/types" "^4.8.0" @@ -3813,7 +3862,7 @@ "@smithy/md5-js@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/md5-js/-/md5-js-4.2.3.tgz#a89c324ff61c64c25b4895fa16d9358f7e3cc746" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/md5-js/-/md5-js-4.2.3.tgz#a89c324ff61c64c25b4895fa16d9358f7e3cc746" integrity sha512-5+4bUEJQi/NRgzdA5SVXvAwyvEnD0ZAiKzV3yLO6dN5BG8ScKBweZ8mxXXUtdxq+Dx5k6EshKk0XJ7vgvIPSnA== dependencies: "@smithy/types" "^4.8.0" @@ -3822,7 +3871,7 @@ "@smithy/middleware-content-length@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-4.2.3.tgz#b7d1d79ae674dad17e35e3518db4b1f0adc08964" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/middleware-content-length/-/middleware-content-length-4.2.3.tgz#b7d1d79ae674dad17e35e3518db4b1f0adc08964" integrity sha512-/atXLsT88GwKtfp5Jr0Ks1CSa4+lB+IgRnkNrrYP0h1wL4swHNb0YONEvTceNKNdZGJsye+W2HH8W7olbcPUeA== dependencies: "@smithy/protocol-http" "^5.3.3" @@ -3831,7 +3880,7 @@ "@smithy/middleware-endpoint@^4.3.5": version "4.3.5" - resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.5.tgz#c22f82f83f0b5cc6c0866a2a87b65bc2e79af352" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.5.tgz#c22f82f83f0b5cc6c0866a2a87b65bc2e79af352" integrity sha512-SIzKVTvEudFWJbxAaq7f2GvP3jh2FHDpIFI6/VAf4FOWGFZy0vnYMPSRj8PGYI8Hjt29mvmwSRgKuO3bK4ixDw== dependencies: "@smithy/core" "^3.17.1" @@ -3845,7 +3894,7 @@ "@smithy/middleware-retry@^4.4.5": version "4.4.5" - resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-4.4.5.tgz#5bdb6ba1be6a97272b79fdac99db40c5e7ab81e0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/middleware-retry/-/middleware-retry-4.4.5.tgz#5bdb6ba1be6a97272b79fdac99db40c5e7ab81e0" integrity sha512-DCaXbQqcZ4tONMvvdz+zccDE21sLcbwWoNqzPLFlZaxt1lDtOE2tlVpRSwcTOJrjJSUThdgEYn7HrX5oLGlK9A== dependencies: "@smithy/node-config-provider" "^4.3.3" @@ -3860,7 +3909,7 @@ "@smithy/middleware-serde@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-4.2.3.tgz#a827e9c4ea9e51c79cca4d6741d582026a8b53eb" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/middleware-serde/-/middleware-serde-4.2.3.tgz#a827e9c4ea9e51c79cca4d6741d582026a8b53eb" integrity sha512-8g4NuUINpYccxiCXM5s1/V+uLtts8NcX4+sPEbvYQDZk4XoJfDpq5y2FQxfmUL89syoldpzNzA0R9nhzdtdKnQ== dependencies: "@smithy/protocol-http" "^5.3.3" @@ -3869,7 +3918,7 @@ "@smithy/middleware-stack@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-4.2.3.tgz#5a315aa9d0fd4faaa248780297c8cbacc31c2eba" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/middleware-stack/-/middleware-stack-4.2.3.tgz#5a315aa9d0fd4faaa248780297c8cbacc31c2eba" integrity sha512-iGuOJkH71faPNgOj/gWuEGS6xvQashpLwWB1HjHq1lNNiVfbiJLpZVbhddPuDbx9l4Cgl0vPLq5ltRfSaHfspA== dependencies: "@smithy/types" "^4.8.0" @@ -3877,7 +3926,7 @@ "@smithy/node-config-provider@^4.3.3": version "4.3.3" - resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-4.3.3.tgz#44140a1e6bc666bcf16faf68c35d3dae4ba8cad5" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/node-config-provider/-/node-config-provider-4.3.3.tgz#44140a1e6bc666bcf16faf68c35d3dae4ba8cad5" integrity sha512-NzI1eBpBSViOav8NVy1fqOlSfkLgkUjUTlohUSgAEhHaFWA3XJiLditvavIP7OpvTjDp5u2LhtlBhkBlEisMwA== dependencies: "@smithy/property-provider" "^4.2.3" @@ -3887,7 +3936,7 @@ "@smithy/node-http-handler@^4.4.3": version "4.4.3" - resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.4.3.tgz#fb2d16719cb4e8df0c189e8bde60e837df5c0c5b" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/node-http-handler/-/node-http-handler-4.4.3.tgz#fb2d16719cb4e8df0c189e8bde60e837df5c0c5b" integrity sha512-MAwltrDB0lZB/H6/2M5PIsISSwdI5yIh6DaBB9r0Flo9nx3y0dzl/qTMJPd7tJvPdsx6Ks/cwVzheGNYzXyNbQ== dependencies: "@smithy/abort-controller" "^4.2.3" @@ -3898,7 +3947,7 @@ "@smithy/property-provider@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.2.3.tgz#a6c82ca0aa1c57f697464bee496f3fec58660864" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/property-provider/-/property-provider-4.2.3.tgz#a6c82ca0aa1c57f697464bee496f3fec58660864" integrity sha512-+1EZ+Y+njiefCohjlhyOcy1UNYjT+1PwGFHCxA/gYctjg3DQWAU19WigOXAco/Ql8hZokNehpzLd0/+3uCreqQ== dependencies: "@smithy/types" "^4.8.0" @@ -3906,7 +3955,7 @@ "@smithy/protocol-http@^5.3.3": version "5.3.3" - resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-5.3.3.tgz#55b35c18bdc0f6d86e78f63961e50ba4ff1c5d73" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/protocol-http/-/protocol-http-5.3.3.tgz#55b35c18bdc0f6d86e78f63961e50ba4ff1c5d73" integrity sha512-Mn7f/1aN2/jecywDcRDvWWWJF4uwg/A0XjFMJtj72DsgHTByfjRltSqcT9NyE9RTdBSN6X1RSXrhn/YWQl8xlw== dependencies: "@smithy/types" "^4.8.0" @@ -3914,7 +3963,7 @@ "@smithy/querystring-builder@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.2.3.tgz#ca273ae8c21fce01a52632202679c0f9e2acf41a" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/querystring-builder/-/querystring-builder-4.2.3.tgz#ca273ae8c21fce01a52632202679c0f9e2acf41a" integrity sha512-LOVCGCmwMahYUM/P0YnU/AlDQFjcu+gWbFJooC417QRB/lDJlWSn8qmPSDp+s4YVAHOgtgbNG4sR+SxF/VOcJQ== dependencies: "@smithy/types" "^4.8.0" @@ -3923,7 +3972,7 @@ "@smithy/querystring-parser@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-4.2.3.tgz#b6d7d5cd300b4083c62d9bd30915f782d01f503e" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/querystring-parser/-/querystring-parser-4.2.3.tgz#b6d7d5cd300b4083c62d9bd30915f782d01f503e" integrity sha512-cYlSNHcTAX/wc1rpblli3aUlLMGgKZ/Oqn8hhjFASXMCXjIqeuQBei0cnq2JR8t4RtU9FpG6uyl6PxyArTiwKA== dependencies: "@smithy/types" "^4.8.0" @@ -3931,14 +3980,14 @@ "@smithy/service-error-classification@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-4.2.3.tgz#ecb41dd514841eebb93e91012ae5e343040f6828" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/service-error-classification/-/service-error-classification-4.2.3.tgz#ecb41dd514841eebb93e91012ae5e343040f6828" integrity sha512-NkxsAxFWwsPsQiwFG2MzJ/T7uIR6AQNh1SzcxSUnmmIqIQMlLRQDKhc17M7IYjiuBXhrQRjQTo3CxX+DobS93g== dependencies: "@smithy/types" "^4.8.0" "@smithy/shared-ini-file-loader@^4.3.3": version "4.3.3" - resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.3.tgz#1d5162cd3a14f57e4fde56f65aa188e8138c1248" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.3.tgz#1d5162cd3a14f57e4fde56f65aa188e8138c1248" integrity sha512-9f9Ixej0hFhroOK2TxZfUUDR13WVa8tQzhSzPDgXe5jGL3KmaM9s8XN7RQwqtEypI82q9KHnKS71CJ+q/1xLtQ== dependencies: "@smithy/types" "^4.8.0" @@ -3946,7 +3995,7 @@ "@smithy/signature-v4@^5.3.3": version "5.3.3" - resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.3.3.tgz#5ff13cfaa29cb531061c2582cb599b39e040e52e" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/signature-v4/-/signature-v4-5.3.3.tgz#5ff13cfaa29cb531061c2582cb599b39e040e52e" integrity sha512-CmSlUy+eEYbIEYN5N3vvQTRfqt0lJlQkaQUIf+oizu7BbDut0pozfDjBGecfcfWf7c62Yis4JIEgqQ/TCfodaA== dependencies: "@smithy/is-array-buffer" "^4.2.0" @@ -3960,7 +4009,7 @@ "@smithy/smithy-client@^4.9.1": version "4.9.1" - resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-4.9.1.tgz#a36e456e837121b2ded6f7d5f1f30b205c446e20" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/smithy-client/-/smithy-client-4.9.1.tgz#a36e456e837121b2ded6f7d5f1f30b205c446e20" integrity sha512-Ngb95ryR5A9xqvQFT5mAmYkCwbXvoLavLFwmi7zVg/IowFPCfiqRfkOKnbc/ZRL8ZKJ4f+Tp6kSu6wjDQb8L/g== dependencies: "@smithy/core" "^3.17.1" @@ -3980,14 +4029,14 @@ "@smithy/types@^4.8.0": version "4.8.0" - resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.8.0.tgz#e6f65e712478910b74747081e6046e68159f767d" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/types/-/types-4.8.0.tgz#e6f65e712478910b74747081e6046e68159f767d" integrity sha512-QpELEHLO8SsQVtqP+MkEgCYTFW0pleGozfs3cZ183ZBj9z3VC1CX1/wtFMK64p+5bhtZo41SeLK1rBRtd25nHQ== dependencies: tslib "^2.6.2" "@smithy/url-parser@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-4.2.3.tgz#82508f273a3f074d47d0919f7ce08028c6575c2f" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/url-parser/-/url-parser-4.2.3.tgz#82508f273a3f074d47d0919f7ce08028c6575c2f" integrity sha512-I066AigYvY3d9VlU3zG9XzZg1yT10aNqvCaBTw9EPgu5GrsEl1aUkcMvhkIXascYH1A8W0LQo3B1Kr1cJNcQEw== dependencies: "@smithy/querystring-parser" "^4.2.3" @@ -4042,7 +4091,7 @@ "@smithy/util-defaults-mode-browser@^4.3.4": version "4.3.4" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.4.tgz#ed96651c32ac0de55b066fcb07a296837373212f" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.4.tgz#ed96651c32ac0de55b066fcb07a296837373212f" integrity sha512-qI5PJSW52rnutos8Bln8nwQZRpyoSRN6k2ajyoUHNMUzmWqHnOJCnDELJuV6m5PML0VkHI+XcXzdB+6awiqYUw== dependencies: "@smithy/property-provider" "^4.2.3" @@ -4052,7 +4101,7 @@ "@smithy/util-defaults-mode-node@^4.2.6": version "4.2.6" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.6.tgz#01b7ff4605f6f981972083fee22d036e5dc4be38" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.6.tgz#01b7ff4605f6f981972083fee22d036e5dc4be38" integrity sha512-c6M/ceBTm31YdcFpgfgQAJaw3KbaLuRKnAz91iMWFLSrgxRpYm03c3bu5cpYojNMfkV9arCUelelKA7XQT36SQ== dependencies: "@smithy/config-resolver" "^4.4.0" @@ -4065,7 +4114,7 @@ "@smithy/util-endpoints@^3.2.3": version "3.2.3" - resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-3.2.3.tgz#8bbb80f1ad5769d9f73992c5979eea3b74d7baa9" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/util-endpoints/-/util-endpoints-3.2.3.tgz#8bbb80f1ad5769d9f73992c5979eea3b74d7baa9" integrity sha512-aCfxUOVv0CzBIkU10TubdgKSx5uRvzH064kaiPEWfNIvKOtNpu642P4FP1hgOFkjQIkDObrfIDnKMKkeyrejvQ== dependencies: "@smithy/node-config-provider" "^4.3.3" @@ -4081,7 +4130,7 @@ "@smithy/util-middleware@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-4.2.3.tgz#7c73416a6e3d3207a2d34a1eadd9f2b6a9811bd6" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/util-middleware/-/util-middleware-4.2.3.tgz#7c73416a6e3d3207a2d34a1eadd9f2b6a9811bd6" integrity sha512-v5ObKlSe8PWUHCqEiX2fy1gNv6goiw6E5I/PN2aXg3Fb/hse0xeaAnSpXDiWl7x6LamVKq7senB+m5LOYHUAHw== dependencies: "@smithy/types" "^4.8.0" @@ -4089,7 +4138,7 @@ "@smithy/util-retry@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-4.2.3.tgz#b1e5c96d96aaf971b68323ff8ba8754f914f22a0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/util-retry/-/util-retry-4.2.3.tgz#b1e5c96d96aaf971b68323ff8ba8754f914f22a0" integrity sha512-lLPWnakjC0q9z+OtiXk+9RPQiYPNAovt2IXD3CP4LkOnd9NpUsxOjMx1SnoUVB7Orb7fZp67cQMtTBKMFDvOGg== dependencies: "@smithy/service-error-classification" "^4.2.3" @@ -4098,7 +4147,7 @@ "@smithy/util-stream@^4.5.4": version "4.5.4" - resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-4.5.4.tgz#bfc60e2714c2065b8e7e91ca921cc31c73efdbd4" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/util-stream/-/util-stream-4.5.4.tgz#bfc60e2714c2065b8e7e91ca921cc31c73efdbd4" integrity sha512-+qDxSkiErejw1BAIXUFBSfM5xh3arbz1MmxlbMCKanDDZtVEQ7PSKW9FQS0Vud1eI/kYn0oCTVKyNzRlq+9MUw== dependencies: "@smithy/fetch-http-handler" "^5.3.4" @@ -4135,7 +4184,7 @@ "@smithy/util-waiter@^4.2.3": version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-4.2.3.tgz#4c662009db101bc60aed07815d359e90904caef2" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@smithy/util-waiter/-/util-waiter-4.2.3.tgz#4c662009db101bc60aed07815d359e90904caef2" integrity sha512-5+nU///E5sAdD7t3hs4uwvCTWQtTR8JwKwOCSJtBRx0bY1isDo1QwH87vRK86vlFLBTISqoDA2V6xvP6nF1isQ== dependencies: "@smithy/abort-controller" "^4.2.3" @@ -4257,6 +4306,16 @@ "@types/range-parser" "*" "@types/send" "*" +"@types/express-serve-static-core@^5.0.0": + version "5.1.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz#74f47555b3d804b54cb7030e6f9aa0c7485cfc5b" + integrity sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + "@types/express@*", "@types/express@^4.17.21": version "4.17.21" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" @@ -4267,6 +4326,15 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/express@^5.0.3": + version "5.0.3" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/@types/express/-/express-5.0.3.tgz#6c4bc6acddc2e2a587142e1d8be0bce20757e956" + integrity sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^5.0.0" + "@types/serve-static" "*" + "@types/glob@~7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" @@ -4729,6 +4797,14 @@ accepts@^1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" +accepts@^2.0.0: + version "2.0.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" + integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== + dependencies: + mime-types "^3.0.0" + negotiator "^1.0.0" + acorn-import-attributes@^1.9.5: version "1.9.5" resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" @@ -5095,6 +5171,21 @@ body-parser@1.20.3: type-is "~1.6.18" unpipe "1.0.0" +body-parser@^2.2.0: + version "2.2.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/body-parser/-/body-parser-2.2.0.tgz#f7a9656de305249a715b549b7b8fd1ab9dfddcfa" + integrity sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg== + dependencies: + bytes "^3.1.2" + content-type "^1.0.5" + debug "^4.4.0" + http-errors "^2.0.0" + iconv-lite "^0.6.3" + on-finished "^2.4.1" + qs "^6.14.0" + raw-body "^3.0.0" + type-is "^2.0.0" + bowser@^2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" @@ -5174,7 +5265,7 @@ bundle-name@^4.1.0: dependencies: run-applescript "^7.0.0" -bytes@3.1.2: +bytes@3.1.2, bytes@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== @@ -5593,7 +5684,14 @@ content-disposition@0.5.4, content-disposition@~0.5.2: dependencies: safe-buffer "5.2.1" -content-type@^1.0.4, content-type@~1.0.4, content-type@~1.0.5: +content-disposition@^1.0.0: + version "1.0.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/content-disposition/-/content-disposition-1.0.0.tgz#844426cb398f934caefcbb172200126bc7ceace2" + integrity sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg== + dependencies: + safe-buffer "5.2.1" + +content-type@^1.0.4, content-type@^1.0.5, content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== @@ -5642,11 +5740,21 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== +cookie-signature@^1.2.1: + version "1.2.2" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793" + integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== + cookie@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== +cookie@^0.7.1: + version "0.7.2" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + cookies@~0.9.0: version "0.9.1" resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.9.1.tgz#3ffed6f60bb4fb5f146feeedba50acc418af67e3" @@ -6060,7 +6168,7 @@ encodeurl@^1.0.2, encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -encodeurl@~2.0.0: +encodeurl@^2.0.0, encodeurl@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== @@ -6626,7 +6734,7 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -etag@~1.8.1: +etag@^1.8.1, etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== @@ -6718,6 +6826,39 @@ express@^4.20.0: utils-merge "1.0.1" vary "~1.1.2" +express@^5.1.0: + version "5.1.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/express/-/express-5.1.0.tgz#d31beaf715a0016f0d53f47d3b4d7acf28c75cc9" + integrity sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA== + dependencies: + accepts "^2.0.0" + body-parser "^2.2.0" + content-disposition "^1.0.0" + content-type "^1.0.5" + cookie "^0.7.1" + cookie-signature "^1.2.1" + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + finalhandler "^2.1.0" + fresh "^2.0.0" + http-errors "^2.0.0" + merge-descriptors "^2.0.0" + mime-types "^3.0.0" + on-finished "^2.4.1" + once "^1.4.0" + parseurl "^1.3.3" + proxy-addr "^2.0.7" + qs "^6.14.0" + range-parser "^1.2.1" + router "^2.2.0" + send "^1.1.0" + serve-static "^2.2.0" + statuses "^2.0.1" + type-is "^2.0.1" + vary "^1.1.2" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -6884,6 +7025,18 @@ finalhandler@1.3.1: statuses "2.0.1" unpipe "~1.0.0" +finalhandler@^2.1.0: + version "2.1.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/finalhandler/-/finalhandler-2.1.0.tgz#72306373aa89d05a8242ed569ed86a1bff7c561f" + integrity sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q== + dependencies: + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + on-finished "^2.4.1" + parseurl "^1.3.3" + statuses "^2.0.1" + find-cache-dir@^3.2.0: version "3.3.2" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" @@ -7004,6 +7157,11 @@ fresh@0.5.2, fresh@~0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== +fresh@^2.0.0: + version "2.0.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4" + integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== + fromentries@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a" @@ -7590,7 +7748,7 @@ http-call@^5.2.2: parse-json "^4.0.0" tunnel-agent "^0.6.0" -http-errors@2.0.0: +http-errors@2.0.0, http-errors@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== @@ -7688,13 +7846,20 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.7.0: +iconv-lite@0.7.0, iconv-lite@^0.7.0: version "0.7.0" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.0.tgz#c50cd80e6746ca8115eb98743afa81aa0e147a3e" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/iconv-lite/-/iconv-lite-0.7.0.tgz#c50cd80e6746ca8115eb98743afa81aa0e147a3e" integrity sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -8062,6 +8227,11 @@ is-plain-obj@^4.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== +is-promise@^4.0.0: + version "4.0.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + is-regex@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" @@ -8788,7 +8958,7 @@ lunr@^2.3.9: lwc@~8.23.0: version "8.23.0" - resolved "https://registry.yarnpkg.com/lwc/-/lwc-8.23.0.tgz#1123a559700aa8bb437f54258efa1ed2be8e94f1" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/lwc/-/lwc-8.23.0.tgz#1123a559700aa8bb437f54258efa1ed2be8e94f1" integrity sha512-XeNx83aT0NZJ8ORfR4bHWIgL5m+XoDJvIX0Og+8ZAr9YYMmZJuBd83tmdhrneYXaJTaGbX54TVbvRY90k+/noA== dependencies: "@lwc/aria-reflection" "8.23.0" @@ -9002,6 +9172,11 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== +media-typer@^1.1.0: + version "1.1.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561" + integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== + memfs@^4.30.1: version "4.36.0" resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.36.0.tgz#b9fa8d97ddda3cb8c06908bceec956560c33d979" @@ -9039,6 +9214,11 @@ merge-descriptors@1.0.3: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== +merge-descriptors@^2.0.0: + version "2.0.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808" + integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -9125,6 +9305,11 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== +mime-db@^1.54.0: + version "1.54.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.33, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" @@ -9132,6 +9317,13 @@ mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.33, mime-types@~2.1.24, dependencies: mime-db "1.52.0" +mime-types@^3.0.0, mime-types@^3.0.1: + version "3.0.1" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/mime-types/-/mime-types-3.0.1.tgz#b1d94d6997a9b32fd69ebaed0db73de8acb519ce" + integrity sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA== + dependencies: + mime-db "^1.54.0" + mime@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -9323,6 +9515,11 @@ negotiator@0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +negotiator@^1.0.0: + version "1.0.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" + integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== + neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" @@ -9541,11 +9738,11 @@ object.values@^1.2.0: es-object-atoms "^1.0.0" oclif@^4.22.35: - version "4.22.35" - resolved "https://registry.yarnpkg.com/oclif/-/oclif-4.22.35.tgz#67cb55c4eae90eea178d502ac473c1b43212acb9" - integrity sha512-5H6Kc/nnw2mexCO4YWxKSY1b7hJKuqP+Ug35abunS62SBFR5Te4yMLvaEWmqKRVFt2mQet/cml4J40byjvxynA== + version "4.22.37" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/oclif/-/oclif-4.22.37.tgz#aebd30159a4aa09be4fd842402bb2b0e76780cba" + integrity sha512-hi2zjgOUsevIW3xJFLcSW3M6dX1i9V1jeI1Z+xN3B9f63YKtSa9bvucBy42G2cHhqq05p++DRotcWKXEU5xQ+w== dependencies: - "@aws-sdk/client-cloudfront" "^3.908.0" + "@aws-sdk/client-cloudfront" "^3.917.0" "@aws-sdk/client-s3" "^3.913.0" "@inquirer/confirm" "^3.1.22" "@inquirer/input" "^2.2.4" @@ -9553,7 +9750,7 @@ oclif@^4.22.35: "@oclif/core" "^4.5.5" "@oclif/plugin-help" "^6.2.33" "@oclif/plugin-not-found" "^3.2.71" - "@oclif/plugin-warn-if-update-available" "^3.1.49" + "@oclif/plugin-warn-if-update-available" "^3.1.50" ansis "^3.16.0" async-retry "^1.3.3" change-case "^4" @@ -9575,7 +9772,7 @@ on-exit-leak-free@^2.1.0: resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz#fed195c9ebddb7d9e4c3842f93f281ac8dadd3b8" integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA== -on-finished@2.4.1, on-finished@^2.3.0: +on-finished@2.4.1, on-finished@^2.3.0, on-finished@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -9773,7 +9970,7 @@ parse5@^7.2.1: dependencies: entities "^4.5.0" -parseurl@^1.3.2, parseurl@~1.3.3: +parseurl@^1.3.2, parseurl@^1.3.3, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -9844,6 +10041,11 @@ path-to-regexp@^6.1.0, path-to-regexp@^6.2.1, path-to-regexp@^6.3.0: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz#2b6a26a337737a8e1416f9272ed0766b1c0389f4" integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ== +path-to-regexp@^8.0.0: + version "8.3.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/path-to-regexp/-/path-to-regexp-8.3.0.tgz#aa818a6981f99321003a08987d3cec9c3474cd1f" + integrity sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA== + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -10067,7 +10269,7 @@ protobufjs@^7.2.5, protobufjs@^7.3.0: "@types/node" ">=13.7.0" long "^5.0.0" -proxy-addr@~2.0.7: +proxy-addr@^2.0.7, proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== @@ -10110,6 +10312,13 @@ qs@6.13.0, qs@^6.13.0: dependencies: side-channel "^1.0.6" +qs@^6.14.0: + version "6.14.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" + integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== + dependencies: + side-channel "^1.1.0" + querystringify@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" @@ -10142,7 +10351,7 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -range-parser@~1.2.1: +range-parser@^1.2.1, range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== @@ -10157,6 +10366,16 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" +raw-body@^3.0.0: + version "3.0.1" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/raw-body/-/raw-body-3.0.1.tgz#ced5cd79a77bbb0496d707f2a0f9e1ae3aecdcb1" + integrity sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.7.0" + unpipe "1.0.0" + react-reconciler@^0.29.0: version "0.29.2" resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.29.2.tgz#8ecfafca63549a4f4f3e4c1e049dd5ad9ac3a54f" @@ -10484,6 +10703,17 @@ rollup@^2.79.2: optionalDependencies: fsevents "~2.3.2" +router@^2.2.0: + version "2.2.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/router/-/router-2.2.0.tgz#019be620b711c87641167cc79b99090f00b146ef" + integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== + dependencies: + debug "^4.4.0" + depd "^2.0.0" + is-promise "^4.0.0" + parseurl "^1.3.3" + path-to-regexp "^8.0.0" + run-applescript@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" @@ -10581,7 +10811,7 @@ semver@7.5.4: dependencies: lru-cache "^6.0.0" -semver@>=7.6.3, semver@^7.3.4, semver@^7.3.5, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.7.2, semver@^7.7.3: +semver@>=7.6.3, semver@^7.3.4, semver@^7.3.5, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3, semver@^7.7.2, semver@^7.7.3: version "7.7.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== @@ -10610,6 +10840,23 @@ send@0.19.0: range-parser "~1.2.1" statuses "2.0.1" +send@^1.1.0, send@^1.2.0: + version "1.2.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/send/-/send-1.2.0.tgz#32a7554fb777b831dfa828370f773a3808d37212" + integrity sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw== + dependencies: + debug "^4.3.5" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + fresh "^2.0.0" + http-errors "^2.0.0" + mime-types "^3.0.1" + ms "^2.1.3" + on-finished "^2.4.1" + range-parser "^1.2.1" + statuses "^2.0.1" + sentence-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f" @@ -10641,6 +10888,16 @@ serve-static@1.16.2: parseurl "~1.3.3" send "0.19.0" +serve-static@^2.2.0: + version "2.2.0" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/serve-static/-/serve-static-2.2.0.tgz#9c02564ee259bdd2251b82d659a2e7e1938d66f9" + integrity sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ== + dependencies: + encodeurl "^2.0.0" + escape-html "^1.0.3" + parseurl "^1.3.3" + send "^1.2.0" + server-destroy@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/server-destroy/-/server-destroy-1.0.1.tgz#f13bf928e42b9c3e79383e61cc3998b5d14e6cdd" @@ -10993,6 +11250,11 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +statuses@^2.0.1: + version "2.0.2" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + "string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -11455,6 +11717,15 @@ type-is@^1.6.16, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +type-is@^2.0.0, type-is@^2.0.1: + version "2.0.1" + resolved "https://nexus-proxy.repo.local.sfdc.net/nexus/content/groups/npm-all/type-is/-/type-is-2.0.1.tgz#64f6cf03f92fce4015c2b224793f6bdd4b068c97" + integrity sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== + dependencies: + content-type "^1.0.5" + media-typer "^1.1.0" + mime-types "^3.0.0" + typed-array-buffer@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" From 5d0975fb214b5829f74301d6d66a97723623972b Mon Sep 17 00:00:00 2001 From: Deepu Mungamuri Date: Mon, 27 Oct 2025 04:31:23 +0530 Subject: [PATCH 2/2] feat: improve error capture UX with filtered stack traces and cleaner logs - Remove verbose console logs on error capture server startup - Add server-side cleanup for _clientParsedStack field (client-only data) - Keep filtered stack traces (show local source only, hide framework code) - Improve code documentation and comments --- src/lwc-dev-server/errorMiddleware.ts | 9 ++++++++- src/lwc-dev-server/index.ts | 22 +++++----------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/lwc-dev-server/errorMiddleware.ts b/src/lwc-dev-server/errorMiddleware.ts index 58c690d..3ffa09c 100644 --- a/src/lwc-dev-server/errorMiddleware.ts +++ b/src/lwc-dev-server/errorMiddleware.ts @@ -72,6 +72,13 @@ export function createErrorMiddleware( const error = payload; + // Clean up client-only fields (used for browser logging, not needed on server) + // eslint-disable-next-line no-underscore-dangle + if ('_clientParsedStack' in error.error) { + // eslint-disable-next-line no-underscore-dangle + delete (error.error as Record)._clientParsedStack; + } + // Enhance stack trace with project context if (error.error.stack && error.error.sanitizedStack.length === 0) { error.error.sanitizedStack = parseStackTrace(error.error.stack, projectRoot); @@ -84,7 +91,7 @@ export function createErrorMiddleware( if (logToConsole) { const formatted = formatErrorForCLI(error, { colorize: true, - showFullStack: false, + showFullStack: false, // Only show local source frames, hide framework/library code compact: false, }); // eslint-disable-next-line no-console diff --git a/src/lwc-dev-server/index.ts b/src/lwc-dev-server/index.ts index 62835c5..1b4b55b 100644 --- a/src/lwc-dev-server/index.ts +++ b/src/lwc-dev-server/index.ts @@ -47,9 +47,9 @@ async function createLWCServerConfig( const ports = serverPorts ?? (await ConfigUtils.getLocalDevServerPorts()) ?? { - httpPort: LOCAL_DEV_SERVER_DEFAULT_HTTP_PORT, - httpsPort: LOCAL_DEV_SERVER_DEFAULT_HTTP_PORT + 1, - }; + httpPort: LOCAL_DEV_SERVER_DEFAULT_HTTP_PORT, + httpsPort: LOCAL_DEV_SERVER_DEFAULT_HTTP_PORT + 1, + }; const serverConfig: ServerConfig = { rootDir, @@ -107,22 +107,11 @@ export async function startLWCServer( localhostOnly: true, // Bind to localhost only for security }); - // eslint-disable-next-line no-console - console.log('\nโœ… [ErrorCapture] Error capture system initialized'); - // eslint-disable-next-line no-console - console.log(`๐Ÿ“ก [ErrorCapture] LWC Dev Server (WebSocket): ws://localhost:${config.port}`); - // eslint-disable-next-line no-console - console.log(`๐Ÿ” [ErrorCapture] Error Capture Server (HTTP): http://localhost:${errorCapturePort}`); - // eslint-disable-next-line no-console - console.log('๐Ÿ’ก [ErrorCapture] Tip: Errors clear automatically on server restart'); - // eslint-disable-next-line no-console - console.log(` Or manually: curl -X DELETE http://localhost:${errorCapturePort}/_dev/errors\n`); logger.info('[ErrorCapture] Error capture system initialized'); } catch (err) { // eslint-disable-next-line no-console console.log( - `\nโš ๏ธ [ErrorCapture] Failed to start error capture server on port ${errorCapturePort}: ${ - err instanceof Error ? err.message : String(err) + `\nโš ๏ธ [ErrorCapture] Failed to start error capture server on port ${errorCapturePort}: ${err instanceof Error ? err.message : String(err) }` ); // eslint-disable-next-line no-console @@ -130,8 +119,7 @@ export async function startLWCServer( 'โš ๏ธ [ErrorCapture] Error capture will not be available. This does not affect LWC dev server functionality.\n' ); logger.warn( - `[ErrorCapture] Failed to start error capture server on port ${errorCapturePort}: ${ - err instanceof Error ? err.message : String(err) + `[ErrorCapture] Failed to start error capture server on port ${errorCapturePort}: ${err instanceof Error ? err.message : String(err) }` ); }