diff --git a/package.json b/package.json index ab50b3c6b..1aca4bee3 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "socket-cli-monorepo", "version": "0.0.0", - "packageManager": "pnpm@11.0.0-rc.2", + "packageManager": "pnpm@11.0.0-rc.3", "private": true, "engines": { "node": ">=25.9.0", - "pnpm": ">=11.0.0-rc.2" + "pnpm": ">=11.0.0-rc.3" }, "scripts": { "// Build": "", diff --git a/packages/cli/src/utils/debug.mts b/packages/cli/src/utils/debug.mts index 4687e6c17..9a2f9a917 100644 --- a/packages/cli/src/utils/debug.mts +++ b/packages/cli/src/utils/debug.mts @@ -29,12 +29,24 @@ import { } from '@socketsecurity/lib/debug' export type ApiRequestDebugInfo = { + durationMs?: number | undefined + headers?: Record | undefined method?: string | undefined + // ISO-8601 timestamp of when the request was initiated. Useful when + // correlating failures with server-side logs. + requestedAt?: string | undefined + // Response body string; truncated by the helper to a safe length so + // logs don't balloon on megabyte payloads. + responseBody?: string | undefined + // Response headers from the failed request. The helper extracts the + // cf-ray trace id as a first-class field so support can look it up in + // the Cloudflare dashboard without eyeballing the whole header dump. + responseHeaders?: Record | undefined url?: string | undefined - headers?: Record | undefined - durationMs?: number | undefined } +const RESPONSE_BODY_TRUNCATE_LENGTH = 2_000 + /** * Sanitize headers to remove sensitive information. * Redacts Authorization and API key headers. @@ -77,15 +89,66 @@ export function debugApiRequest( } /** - * Debug an API response with detailed request information. - * Logs essential info without exposing sensitive data. + * Build the structured debug payload shared by the error + failure-status + * branches of `debugApiResponse`. Extracted so both paths log the same + * shape. + */ +function buildApiDebugDetails( + base: Record, + requestInfo?: ApiRequestDebugInfo | undefined, +): Record { + // `__proto__: null` keeps the payload free of prototype-chain keys + // when callers iterate over the debug output. + const details: Record = { __proto__: null, ...base } as Record + if (!requestInfo) { + return details + } + if (requestInfo.requestedAt) { + details['requestedAt'] = requestInfo.requestedAt + } + if (requestInfo.method) { + details['method'] = requestInfo.method + } + if (requestInfo.url) { + details['url'] = requestInfo.url + } + if (requestInfo.durationMs !== undefined) { + details['durationMs'] = requestInfo.durationMs + } + if (requestInfo.headers) { + details['headers'] = sanitizeHeaders(requestInfo.headers) + } + if (requestInfo.responseHeaders) { + const cfRay = + requestInfo.responseHeaders['cf-ray'] ?? + requestInfo.responseHeaders['CF-Ray'] + if (cfRay) { + // First-class field so it's obvious when filing a support ticket + // that points at a Cloudflare trace. + details['cfRay'] = cfRay + } + details['responseHeaders'] = sanitizeHeaders(requestInfo.responseHeaders) + } + if (requestInfo.responseBody !== undefined) { + const body = requestInfo.responseBody + // `.length` / `.slice` operate on UTF-16 code units, not bytes, so + // the counter and truncation are both reported in "chars" to stay + // consistent with what we actually measured. + details['responseBody'] = + body.length > RESPONSE_BODY_TRUNCATE_LENGTH + ? `${body.slice(0, RESPONSE_BODY_TRUNCATE_LENGTH)}… (truncated, ${body.length} chars)` + : body + } + return details +} + +/** + * Debug an API response. Failed requests (error or status >= 400) log + * under the `error` namespace; successful responses optionally log a + * one-liner under `notice`. * - * For failed requests (status >= 400 or error), logs: - * - HTTP method (GET, POST, etc.) - * - Full URL - * - Response status code - * - Sanitized headers (Authorization redacted) - * - Request duration in milliseconds + * Request and response headers are sanitized via `sanitizeHeaders` so + * Authorization and `*api-key*` values are redacted. */ export function debugApiResponse( endpoint: string, @@ -94,39 +157,21 @@ export function debugApiResponse( requestInfo?: ApiRequestDebugInfo | undefined, ): void { if (error) { - const errorDetails = { - __proto__: null, - endpoint, - error: error instanceof Error ? error.message : UNKNOWN_ERROR, - ...(requestInfo?.method ? { method: requestInfo.method } : {}), - ...(requestInfo?.url ? { url: requestInfo.url } : {}), - ...(requestInfo?.durationMs !== undefined - ? { durationMs: requestInfo.durationMs } - : {}), - ...(requestInfo?.headers - ? { headers: sanitizeHeaders(requestInfo.headers) } - : {}), - } - debugDir(errorDetails) + debugDirNs( + 'error', + buildApiDebugDetails( + { + endpoint, + error: error instanceof Error ? error.message : UNKNOWN_ERROR, + }, + requestInfo, + ), + ) } else if (status && status >= 400) { - // For failed requests, log detailed information. if (requestInfo) { - const failureDetails = { - __proto__: null, - endpoint, - status, - ...(requestInfo.method ? { method: requestInfo.method } : {}), - ...(requestInfo.url ? { url: requestInfo.url } : {}), - ...(requestInfo.durationMs !== undefined - ? { durationMs: requestInfo.durationMs } - : {}), - ...(requestInfo.headers - ? { headers: sanitizeHeaders(requestInfo.headers) } - : {}), - } - debugDir(failureDetails) + debugDirNs('error', buildApiDebugDetails({ endpoint, status }, requestInfo)) } else { - debug(`API ${endpoint}: HTTP ${status}`) + debugNs('error', `API ${endpoint}: HTTP ${status}`) } /* c8 ignore next 3 */ } else if (isDebugNs('notice')) { diff --git a/packages/cli/src/utils/socket/api.mts b/packages/cli/src/utils/socket/api.mts index 53acd468c..51a88babc 100644 --- a/packages/cli/src/utils/socket/api.mts +++ b/packages/cli/src/utils/socket/api.mts @@ -82,6 +82,18 @@ export async function socketHttpRequest( return await httpRequest(url, options) } +// Safe wrapper for `response.text()` in error-handling code paths. +// `text()` can throw (e.g. already consumed, malformed body), which +// would blow past the `ok: false` CResult return and break the +// error-handling contract of callers like `queryApiSafeText`. +function tryReadResponseText(result: HttpResponse): string | undefined { + try { + return result.text?.() + } catch { + return undefined + } +} + export type CommandRequirements = { permissions?: string[] | undefined quota?: number | undefined @@ -428,6 +440,7 @@ export async function queryApiSafeText( const baseUrl = getDefaultApiBaseUrl() const fullUrl = `${baseUrl}${baseUrl?.endsWith('/') ? '' : '/'}${path}` const startTime = Date.now() + const requestedAt = new Date(startTime).toISOString() let result: any try { @@ -443,6 +456,7 @@ export async function queryApiSafeText( method: 'GET', url: fullUrl, durationMs, + requestedAt, headers: { Authorization: '[REDACTED]' }, }) } catch (e) { @@ -458,6 +472,7 @@ export async function queryApiSafeText( method: 'GET', url: fullUrl, durationMs, + requestedAt, headers: { Authorization: '[REDACTED]' }, }) @@ -475,12 +490,17 @@ export async function queryApiSafeText( if (!result.ok) { const { status } = result const durationMs = Date.now() - startTime - // Log detailed error information. + // Include response headers (for cf-ray) and a truncated body so + // support tickets have everything needed to file against Cloudflare + // or backend teams. debugApiResponse(description || 'Query API', status, undefined, { method: 'GET', url: fullUrl, durationMs, + requestedAt, headers: { Authorization: '[REDACTED]' }, + responseHeaders: result.headers, + responseBody: tryReadResponseText(result), }) // Log required permissions for 403 errors when in a command context. if (commandPath && status === 403) { @@ -587,6 +607,7 @@ export async function sendApiRequest( const fullUrl = `${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}${path}` const startTime = Date.now() + const requestedAt = new Date(startTime).toISOString() let result: any try { @@ -614,6 +635,7 @@ export async function sendApiRequest( method, url: fullUrl, durationMs, + requestedAt, headers: { Authorization: '[REDACTED]', 'Content-Type': 'application/json', @@ -633,6 +655,7 @@ export async function sendApiRequest( method, url: fullUrl, durationMs, + requestedAt, headers: { Authorization: '[REDACTED]', 'Content-Type': 'application/json', @@ -653,15 +676,20 @@ export async function sendApiRequest( if (!result.ok) { const { status } = result const durationMs = Date.now() - startTime - // Log detailed error information. + // Include response headers (for cf-ray) and a truncated body so + // support tickets have everything needed to file against Cloudflare + // or backend teams. debugApiResponse(description || 'Send API Request', status, undefined, { method, url: fullUrl, durationMs, + requestedAt, headers: { Authorization: '[REDACTED]', 'Content-Type': 'application/json', }, + responseHeaders: result.headers, + responseBody: tryReadResponseText(result), }) // Log required permissions for 403 errors when in a command context. if (commandPath && status === 403) { diff --git a/packages/cli/test/unit/utils/debug.test.mts b/packages/cli/test/unit/utils/debug.test.mts index 0e01d86ca..f5d711062 100644 --- a/packages/cli/test/unit/utils/debug.test.mts +++ b/packages/cli/test/unit/utils/debug.test.mts @@ -97,16 +97,16 @@ describe('debug utilities', () => { debugApiResponse('/api/test', undefined, error) - expect(debugDir).toHaveBeenCalledWith({ + expect(mockDebugDirNs).toHaveBeenCalledWith('error', { endpoint: '/api/test', error: 'API failed', }) }) - it('logs warning for HTTP error status codes', () => { + it('logs under error namespace for HTTP error status codes', () => { debugApiResponse('/api/test', 404) - expect(debug).toHaveBeenCalledWith('API /api/test: HTTP 404') + expect(debugNs).toHaveBeenCalledWith('error', 'API /api/test: HTTP 404') }) it('logs notice for successful responses when debug is enabled', () => { @@ -128,7 +128,7 @@ describe('debug utilities', () => { it('handles non-Error objects in error parameter', () => { debugApiResponse('/api/test', undefined, 'String error') - expect(debugDir).toHaveBeenCalledWith({ + expect(mockDebugDirNs).toHaveBeenCalledWith('error', { endpoint: '/api/test', error: 'Unknown error', }) @@ -148,7 +148,7 @@ describe('debug utilities', () => { debugApiResponse('/api/test', undefined, error, requestInfo) - const calledWith = mockDebugDir.mock.calls[0]?.[0] + const calledWith = mockDebugDirNs.mock.calls[0]?.[1] as any expect(calledWith.method).toBe('POST') expect(calledWith.url).toBe('https://api.socket.dev/test') expect(calledWith.durationMs).toBe(1500) @@ -169,7 +169,7 @@ describe('debug utilities', () => { debugApiResponse('/api/resource', 500, undefined, requestInfo) - const calledWith = mockDebugDir.mock.calls[0]?.[0] + const calledWith = mockDebugDirNs.mock.calls[0]?.[1] as any expect(calledWith.status).toBe(500) expect(calledWith.method).toBe('GET') // API key should be redacted. @@ -183,11 +183,86 @@ describe('debug utilities', () => { debugApiResponse('/api/update', 400, undefined, requestInfo) - const calledWith = mockDebugDir.mock.calls[0]?.[0] + const calledWith = mockDebugDirNs.mock.calls[0]?.[1] as any expect(calledWith.method).toBe('PUT') expect(calledWith.url).toBeUndefined() expect(calledWith.headers).toBeUndefined() }) + + it('includes requestedAt timestamp when provided', () => { + const requestInfo = { + method: 'POST', + url: 'https://api.socket.dev/x', + requestedAt: '2026-04-18T00:00:00.000Z', + } + + debugApiResponse('/api/x', 500, undefined, requestInfo) + + const calledWith = mockDebugDirNs.mock.calls[0]?.[1] as any + expect(calledWith.requestedAt).toBe('2026-04-18T00:00:00.000Z') + }) + + it('extracts cf-ray as a top-level field and keeps responseHeaders', () => { + const requestInfo = { + method: 'GET', + url: 'https://api.socket.dev/y', + responseHeaders: { + 'cf-ray': 'abc123-IAD', + 'content-type': 'application/json', + }, + } + + debugApiResponse('/api/y', 500, undefined, requestInfo) + + const calledWith = mockDebugDirNs.mock.calls[0]?.[1] as any + expect(calledWith.cfRay).toBe('abc123-IAD') + expect(calledWith.responseHeaders?.['cf-ray']).toBe('abc123-IAD') + }) + + it('tolerates CF-Ray header casing', () => { + const requestInfo = { + method: 'GET', + url: 'https://api.socket.dev/z', + responseHeaders: { + 'CF-Ray': 'xyz789-SJC', + }, + } + + debugApiResponse('/api/z', 500, undefined, requestInfo) + + const calledWith = mockDebugDirNs.mock.calls[0]?.[1] as any + expect(calledWith.cfRay).toBe('xyz789-SJC') + }) + + it('includes response body on error', () => { + const requestInfo = { + method: 'GET', + url: 'https://api.socket.dev/body', + responseBody: '{"error":"bad"}', + } + + debugApiResponse('/api/body', 400, undefined, requestInfo) + + const calledWith = mockDebugDirNs.mock.calls[0]?.[1] as any + expect(calledWith.responseBody).toBe('{"error":"bad"}') + }) + + it('truncates oversized response bodies', () => { + const bigBody = 'x'.repeat(5000) + const requestInfo = { + method: 'GET', + url: 'https://api.socket.dev/big', + responseBody: bigBody, + } + + debugApiResponse('/api/big', 500, undefined, requestInfo) + + const calledWith = mockDebugDirNs.mock.calls[0]?.[1] as any + expect(calledWith.responseBody).toMatch(/… \(truncated, 5000 chars\)$/) + expect((calledWith.responseBody as string).length).toBeLessThan( + bigBody.length, + ) + }) }) describe('debugFileOp', () => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index de1cecf27..a2e59ee11 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,184 +1,3 @@ ---- -lockfileVersion: '9.0' - -importers: - - .: - configDependencies: {} - packageManagerDependencies: - '@pnpm/exe': - specifier: 11.0.0-rc.0 - version: 11.0.0-rc.0 - pnpm: - specifier: 11.0.0-rc.0 - version: 11.0.0-rc.0 - -packages: - - '@pnpm/exe@11.0.0-rc.0': - resolution: {integrity: sha512-lpa3BGQaCvH5BGS256VTyJ4+Ib2PiA5gipTfTs7MTL02utSYXcWarP0OeDhw++Cg/tgrCVRDYWcUjHOy/KNTtA==} - hasBin: true - - '@pnpm/linux-arm64@11.0.0-rc.0': - resolution: {integrity: sha512-6JIbPFu8y7RevLIpOH/rhML9JtnLgAa9VVVGl8A02+sRdF415Q3cldz+N9Oh3ZNLi2JZWtvHRa5UE2FRFELOJQ==} - cpu: [arm64] - os: [linux] - - '@pnpm/linux-x64@11.0.0-rc.0': - resolution: {integrity: sha512-5nSOBz07hmznMKJ88LaO/mk6BXCOMs3cA7VkwAz7ehWvtxeT1Dqez2Rnf5nK//BgEF1jQ8cgjff6MWaSmiYY8A==} - cpu: [x64] - os: [linux] - - '@pnpm/macos-arm64@11.0.0-rc.0': - resolution: {integrity: sha512-X1KgttzXrspprRU4JV3y1rxraX/H8AzXhuO3tDJj01nbUhps0kkjdfJziLJFFYN74bwSO8DgFWmJ5w5V+Hp0Cg==} - cpu: [arm64] - os: [darwin] - - '@pnpm/macos-x64@11.0.0-rc.0': - resolution: {integrity: sha512-Ra/CuHN7hrqScrl9w3zPDcMbY5AjAZMqTDKXL/1qP/GlY4lOJp24sQrH19y3pQGoUKoxlvVo0S4I29ZX2Wsf7A==} - cpu: [x64] - os: [darwin] - - '@pnpm/win-arm64@11.0.0-rc.0': - resolution: {integrity: sha512-vum6DgUMO6hxYdhJBUkdNpnXW0TU/iKRUuZca6qgn/uckhaobENsuaN0pG1ga49G26I+jL5C8GfVBmdnRenm6w==} - cpu: [arm64] - os: [win32] - - '@pnpm/win-x64@11.0.0-rc.0': - resolution: {integrity: sha512-avY9Gz97pvcWO7nRL1AoJToVwljZIybX9A09buGpgrxTSTGjfs6bbFE+d+576ro02MHqhTn6qUnkCbPyKPcWrA==} - cpu: [x64] - os: [win32] - - '@reflink/reflink-darwin-arm64@0.1.19': - resolution: {integrity: sha512-ruy44Lpepdk1FqDz38vExBY/PVUsjxZA+chd9wozjUH9JjuDT/HEaQYA6wYN9mf041l0yLVar6BCZuWABJvHSA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@reflink/reflink-darwin-x64@0.1.19': - resolution: {integrity: sha512-By85MSWrMZa+c26TcnAy8SDk0sTUkYlNnwknSchkhHpGXOtjNDUOxJE9oByBnGbeuIE1PiQsxDG3Ud+IVV9yuA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@reflink/reflink-linux-arm64-gnu@0.1.19': - resolution: {integrity: sha512-7P+er8+rP9iNeN+bfmccM4hTAaLP6PQJPKWSA4iSk2bNvo6KU6RyPgYeHxXmzNKzPVRcypZQTpFgstHam6maVg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@reflink/reflink-linux-arm64-musl@0.1.19': - resolution: {integrity: sha512-37iO/Dp6m5DDaC2sf3zPtx/hl9FV3Xze4xoYidrxxS9bgP3S8ALroxRK6xBG/1TtfXKTvolvp+IjrUU6ujIGmA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@reflink/reflink-linux-x64-gnu@0.1.19': - resolution: {integrity: sha512-jbI8jvuYCaA3MVUdu8vLoLAFqC+iNMpiSuLbxlAgg7x3K5bsS8nOpTRnkLF7vISJ+rVR8W+7ThXlXlUQ93ulkw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@reflink/reflink-linux-x64-musl@0.1.19': - resolution: {integrity: sha512-e9FBWDe+lv7QKAwtKOt6A2W/fyy/aEEfr0g6j/hWzvQcrzHCsz07BNQYlNOjTfeytrtLU7k449H1PI95jA4OjQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [musl] - - '@reflink/reflink-win32-arm64-msvc@0.1.19': - resolution: {integrity: sha512-09PxnVIQcd+UOn4WAW73WU6PXL7DwGS6wPlkMhMg2zlHHG65F3vHepOw06HFCq+N42qkaNAc8AKIabWvtk6cIQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@reflink/reflink-win32-x64-msvc@0.1.19': - resolution: {integrity: sha512-E//yT4ni2SyhwP8JRjVGWr3cbnhWDiPLgnQ66qqaanjjnMiu3O/2tjCPQXlcGc/DEYofpDc9fvhv6tALQsMV9w==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - - '@reflink/reflink@0.1.19': - resolution: {integrity: sha512-DmCG8GzysnCZ15bres3N5AHCmwBwYgp0As6xjhQ47rAUTUXxJiK+lLUxaGsX3hd/30qUpVElh05PbGuxRPgJwA==} - engines: {node: '>= 10'} - - pnpm@11.0.0-rc.0: - resolution: {integrity: sha512-Hwjq3uoCXpFEjebV3uQqbJR2QlcADAQ6nja4/xKEmnLry5xl/BiFCUdHJJ5S9T2Lc62hGBRGu6gYZoEMik0/bA==} - engines: {node: '>=22.13'} - hasBin: true - -snapshots: - - '@pnpm/exe@11.0.0-rc.0': - dependencies: - '@reflink/reflink': 0.1.19 - optionalDependencies: - '@pnpm/linux-arm64': 11.0.0-rc.0 - '@pnpm/linux-x64': 11.0.0-rc.0 - '@pnpm/macos-arm64': 11.0.0-rc.0 - '@pnpm/macos-x64': 11.0.0-rc.0 - '@pnpm/win-arm64': 11.0.0-rc.0 - '@pnpm/win-x64': 11.0.0-rc.0 - - '@pnpm/linux-arm64@11.0.0-rc.0': - optional: true - - '@pnpm/linux-x64@11.0.0-rc.0': - optional: true - - '@pnpm/macos-arm64@11.0.0-rc.0': - optional: true - - '@pnpm/macos-x64@11.0.0-rc.0': - optional: true - - '@pnpm/win-arm64@11.0.0-rc.0': - optional: true - - '@pnpm/win-x64@11.0.0-rc.0': - optional: true - - '@reflink/reflink-darwin-arm64@0.1.19': - optional: true - - '@reflink/reflink-darwin-x64@0.1.19': - optional: true - - '@reflink/reflink-linux-arm64-gnu@0.1.19': - optional: true - - '@reflink/reflink-linux-arm64-musl@0.1.19': - optional: true - - '@reflink/reflink-linux-x64-gnu@0.1.19': - optional: true - - '@reflink/reflink-linux-x64-musl@0.1.19': - optional: true - - '@reflink/reflink-win32-arm64-msvc@0.1.19': - optional: true - - '@reflink/reflink-win32-x64-msvc@0.1.19': - optional: true - - '@reflink/reflink@0.1.19': - optionalDependencies: - '@reflink/reflink-darwin-arm64': 0.1.19 - '@reflink/reflink-darwin-x64': 0.1.19 - '@reflink/reflink-linux-arm64-gnu': 0.1.19 - '@reflink/reflink-linux-arm64-musl': 0.1.19 - '@reflink/reflink-linux-x64-gnu': 0.1.19 - '@reflink/reflink-linux-x64-musl': 0.1.19 - '@reflink/reflink-win32-arm64-msvc': 0.1.19 - '@reflink/reflink-win32-x64-msvc': 0.1.19 - - pnpm@11.0.0-rc.0: {} - ---- lockfileVersion: '9.0' settings: @@ -2727,8 +2546,8 @@ packages: bare-abort-controller: optional: true - bare-fs@4.6.0: - resolution: {integrity: sha512-2YkS7NuiJceSEbyEOdSNLE9tsGd+f4+f7C+Nik/MCk27SYdwIMPT/yRKvg++FZhQXgk0KWJKJyXX9RhVV0RGqA==} + bare-fs@4.7.0: + resolution: {integrity: sha512-xzqKsCFxAek9aezYhjJuJRXBIaYlg/0OGDTZp+T8eYmYMlm66cs6cYko02drIyjN2CBbi+I6L7YfXyqpqtKRXA==} engines: {bare: '>=1.16.0'} peerDependencies: bare-buffer: '*' @@ -2743,8 +2562,8 @@ packages: bare-path@3.0.0: resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} - bare-stream@2.12.0: - resolution: {integrity: sha512-w28i8lkBgREV3rPXGbgK+BO66q+ZpKqRWrZLiCdmmUlLPrQ45CzkvRhN+7lnv00Gpi2zy5naRxnUFAxCECDm9g==} + bare-stream@2.13.0: + resolution: {integrity: sha512-3zAJRZMDFGjdn+RVnNpF9kuELw+0Fl3lpndM4NcEOhb9zwtSo/deETfuIwMSE5BXanA0FrN1qVjffGwAg2Y7EA==} peerDependencies: bare-abort-controller: '*' bare-buffer: '*' @@ -2763,8 +2582,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.16: - resolution: {integrity: sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==} + baseline-browser-mapping@2.10.19: + resolution: {integrity: sha512-qCkNLi2sfBOn8XhZQ0FXsT1Ki/Yo5P90hrkRamVFRS7/KV9hpfA4HkoWNU152+8w0zPjnxo5psx5NL3PSGgv5g==} engines: {node: '>=6.0.0'} hasBin: true @@ -2825,8 +2644,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001786: - resolution: {integrity: sha512-4oxTZEvqmLLrERwxO76yfKM7acZo310U+v4kqexI2TL1DkkUEMT8UijrxxcnVdxR3qkVf5awGRX+4Z6aPHVKrA==} + caniuse-lite@1.0.30001788: + resolution: {integrity: sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -3031,8 +2850,8 @@ packages: resolution: {integrity: sha512-/RInbCy1d4P6Zdfa+TMVsf/ufZVotat5hCw3QXmWtjU+3pFEOvOQ7ibo3aIxyCJw2leIeAMjmPj+1SLJiCpdrQ==} engines: {node: '>=12.0.0'} - electron-to-chromium@1.5.332: - resolution: {integrity: sha512-7OOtytmh/rINMLwaFTbcMVvYXO3AUm029X0LcyfYk0B557RlPkdpTpnH9+htMlfu5dKwOmT0+Zs2Aw+lnn6TeQ==} + electron-to-chromium@1.5.336: + resolution: {integrity: sha512-AbH9q9J455r/nLmdNZes0G0ZKcRX73FicwowalLs6ijwOmCJSRRrLX63lcAlzy9ux3dWK1w1+1nsBJEWN11hcQ==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -3054,6 +2873,10 @@ packages: error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} @@ -4060,8 +3883,8 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} hasBin: true @@ -4691,7 +4514,7 @@ snapshots: '@babel/generator@7.28.5': dependencies: '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 + '@babel/types': 7.28.5 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 @@ -4736,7 +4559,7 @@ snapshots: '@babel/helper-plugin-utils': 7.28.6 debug: 4.4.3 lodash.debounce: 4.0.8 - resolve: 1.22.11 + resolve: 1.22.12 transitivePeerDependencies: - supports-color @@ -6110,7 +5933,7 @@ snapshots: picocolors: 1.1.1 picomatch: 4.0.4 prebuild-install: 7.1.3 - resolve: 1.22.11 + resolve: 1.22.12 stream-meter: 1.0.4 tar: 7.5.13 tinyglobby: 0.2.16 @@ -6229,11 +6052,11 @@ snapshots: bare-events@2.8.2: {} - bare-fs@4.6.0: + bare-fs@4.7.0: dependencies: bare-events: 2.8.2 bare-path: 3.0.0 - bare-stream: 2.12.0(bare-events@2.8.2) + bare-stream: 2.13.0(bare-events@2.8.2) bare-url: 2.4.0 fast-fifo: 1.3.2 transitivePeerDependencies: @@ -6246,7 +6069,7 @@ snapshots: dependencies: bare-os: 3.8.7 - bare-stream@2.12.0(bare-events@2.8.2): + bare-stream@2.13.0(bare-events@2.8.2): dependencies: streamx: 2.25.0 teex: 1.0.1 @@ -6261,7 +6084,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.10.16: {} + baseline-browser-mapping@2.10.19: {} before-after-hook@4.0.0: {} @@ -6296,16 +6119,16 @@ snapshots: browserslist@4.25.4: dependencies: - caniuse-lite: 1.0.30001786 - electron-to-chromium: 1.5.332 + caniuse-lite: 1.0.30001788 + electron-to-chromium: 1.5.336 node-releases: 2.0.37 update-browserslist-db: 1.2.3(browserslist@4.25.4) browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.16 - caniuse-lite: 1.0.30001786 - electron-to-chromium: 1.5.332 + baseline-browser-mapping: 2.10.19 + caniuse-lite: 1.0.30001788 + electron-to-chromium: 1.5.336 node-releases: 2.0.37 update-browserslist-db: 1.2.3(browserslist@4.28.2) @@ -6342,7 +6165,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001786: {} + caniuse-lite@1.0.30001788: {} chai@6.2.2: {} @@ -6524,7 +6347,7 @@ snapshots: efrt@2.7.0: {} - electron-to-chromium@1.5.332: {} + electron-to-chromium@1.5.336: {} emoji-regex@10.6.0: {} @@ -6542,6 +6365,8 @@ snapshots: dependencies: is-arrayish: 0.2.1 + es-errors@1.3.0: {} + es-module-lexer@1.7.0: {} esbuild@0.25.11: @@ -7614,8 +7439,9 @@ snapshots: require-from-string@2.0.2: {} - resolve@1.22.11: + resolve@1.22.12: dependencies: + es-errors: 1.3.0 is-core-module: '@socketregistry/is-core-module@1.0.11' path-parse: '@socketregistry/path-parse@1.0.8' supports-preserve-symlinks-flag: 1.0.0 @@ -7846,7 +7672,7 @@ snapshots: pump: 3.0.4 tar-stream: 3.1.8 optionalDependencies: - bare-fs: 4.6.0 + bare-fs: 4.7.0 bare-path: 3.0.0 transitivePeerDependencies: - bare-abort-controller @@ -7873,7 +7699,7 @@ snapshots: tar-stream@3.1.8: dependencies: b4a: 1.8.0 - bare-fs: 4.6.0 + bare-fs: 4.7.0 fast-fifo: 1.3.2 streamx: 2.25.0 transitivePeerDependencies: