diff --git a/src/appdistribution/distribution.spec.ts b/src/appdistribution/distribution.spec.ts index a8f73f63b16..9b898737b9d 100644 --- a/src/appdistribution/distribution.spec.ts +++ b/src/appdistribution/distribution.spec.ts @@ -68,7 +68,16 @@ describe("appdistribution/distribution", () => { mockClient.uploadRelease.resolves("operations/123"); mockClient.pollUploadStatus.resolves({ result: UploadReleaseResult.RELEASE_CREATED, - release: { displayVersion: "1.0", buildVersion: "1", name: "test-rel" } as unknown as any, + release: { + displayVersion: "1.0", + buildVersion: "1", + name: "test-rel", + releaseNotes: { text: "test-notes" }, + createTime: new Date(), + firebaseConsoleUri: "http://console.firebase.google.com", + testingUri: "http://testing.firebase.google.com", + binaryDownloadUri: "http://download.firebase.google.com", + }, }); const res = await upload( @@ -94,11 +103,16 @@ describe("appdistribution/distribution", () => { describe("awaitTestResults", () => { it("should succeed when all tests pass on first poll", async () => { - const releaseTests: ReleaseTest[] = [{ name: "tests/1", deviceExecutions: [] } as any]; + const releaseTests: ReleaseTest[] = [{ name: "tests/1", deviceExecutions: [] }]; mockClient.getReleaseTest.resolves({ name: "tests/1", - deviceExecutions: [{ state: "PASSED", device: { model: "Pixel" } as unknown as any }], - } as unknown as ReleaseTest); + deviceExecutions: [ + { + state: "PASSED", + device: { model: "Pixel", version: "14", locale: "en_US", orientation: "PORTRAIT" }, + }, + ], + }); const setTimeoutStub = sinon.stub(global, "setTimeout").callsFake((fn) => fn() as any); @@ -109,11 +123,15 @@ describe("appdistribution/distribution", () => { }); it("should fail immediately when a test execution fails", async () => { - const releaseTests: ReleaseTest[] = [{ name: "tests/1", deviceExecutions: [] } as any]; + const releaseTests: ReleaseTest[] = [{ name: "tests/1", deviceExecutions: [] }]; mockClient.getReleaseTest.resolves({ name: "tests/1", deviceExecutions: [ - { state: "FAILED", failedReason: "Crash", device: { model: "Pixel" } as any }, + { + state: "FAILED", + failedReason: "Crash", + device: { model: "Pixel", version: "14", locale: "en_US", orientation: "PORTRAIT" }, + }, ], } as any); diff --git a/src/apphosting/secrets/index.ts b/src/apphosting/secrets/index.ts index b9c8f64d8af..c85f0645abc 100644 --- a/src/apphosting/secrets/index.ts +++ b/src/apphosting/secrets/index.ts @@ -282,13 +282,21 @@ export async function loadSecret(project: string | undefined, name: string): Pro } try { return await gcsm.accessSecretVersion(projectId, secretId, version); - } catch (err: any) { - if (err?.original?.code === 403 || err?.original?.context?.response?.statusCode === 403) { - utils.logLabeledError( - "apphosting", - `Permission denied to access secret ${secretId}. Use ` + - `${clc.bold("firebase apphosting:secrets:grantaccess")} to get permissions.`, - ); + } catch (err: unknown) { + if (err instanceof FirebaseError) { + const original = err.original; + if (typeof original === "object" && original !== null) { + if ( + (original as any).code === 403 || + (original as any).context?.response?.statusCode === 403 + ) { + utils.logLabeledError( + "apphosting", + `Permission denied to access secret ${secretId}. Use ` + + `${clc.bold("firebase apphosting:secrets:grantaccess")} to get permissions.`, + ); + } + } } throw err; } diff --git a/src/emulator/adminSdkConfig.ts b/src/emulator/adminSdkConfig.ts index e85c7dcac57..96d4a194e05 100644 --- a/src/emulator/adminSdkConfig.ts +++ b/src/emulator/adminSdkConfig.ts @@ -2,6 +2,7 @@ import { firebaseApiOrigin } from "../api"; import * as apiv2 from "../apiv2"; import { configstore } from "../configstore"; import { FirebaseError } from "../error"; +import { getError } from "../error"; import { logger } from "../logger"; import { Constants } from "./constants"; @@ -43,7 +44,7 @@ export async function getProjectAdminSdkConfigOrCached( const config = await getProjectAdminSdkConfig(projectId); setCacheAdminSdkConfig(projectId, config); return config; - } catch (e: any) { + } catch (e: unknown) { logger.debug(`Failed to get Admin SDK config for ${projectId}, falling back to cache`, e); return getCachedAdminSdkConfig(projectId); } @@ -71,11 +72,11 @@ async function getProjectAdminSdkConfig(projectId: string): Promise(`projects/${projectId}/adminSdkConfig`); return res.body; - } catch (err: any) { + } catch (err: unknown) { throw new FirebaseError( `Failed to get Admin SDK for Firebase project ${projectId}. ` + "Please make sure the project exists and your account has permission to access it.", - { exit: 2, original: err }, + { exit: 2, original: getError(err) }, ); } } diff --git a/src/emulator/auth/operations.ts b/src/emulator/auth/operations.ts index 22f8e0c1847..bb77fbf8312 100644 --- a/src/emulator/auth/operations.ts +++ b/src/emulator/auth/operations.ts @@ -1642,7 +1642,7 @@ async function signInWithIdp( userMatchingProvider, )); } - } catch (err: any) { + } catch (err: unknown) { if (reqBody.returnIdpCredential && err instanceof BadRequestError) { response.errorMessage = err.message; return response; diff --git a/src/emulator/commandUtils.ts b/src/emulator/commandUtils.ts index 843ae373d5e..4306ca0fa69 100644 --- a/src/emulator/commandUtils.ts +++ b/src/emulator/commandUtils.ts @@ -11,6 +11,7 @@ import { requireAuth } from "../requireAuth"; import { requireConfig } from "../requireConfig"; import { Emulators, ALL_SERVICE_EMULATORS } from "./types"; import { FirebaseError } from "../error"; +import { getError } from "../error"; import { EmulatorRegistry } from "./registry"; import { getProjectId } from "../projectUtils"; import { confirm } from "../prompt"; @@ -172,8 +173,8 @@ export async function beforeEmulatorCommand(options: any): Promise { ) { try { await requireAuth(options); - } catch (e: any) { - logger.debug(e); + } catch (e: unknown) { + logger.debug(e as any); utils.logLabeledWarning( "emulators", `You are not currently authenticated so some features may not work correctly. Please run ${clc.bold( @@ -333,8 +334,8 @@ function processKillSignal( } } res(); - } catch (e: any) { - logger.debug(e); + } catch (e: unknown) { + logger.debug(e as any); rej(); } }; @@ -512,9 +513,11 @@ export async function checkJavaMajorVersion(): Promise { stdio: ["inherit", "pipe", "pipe"], }, ); - } catch (err: any) { + } catch (err: unknown) { return reject( - new FirebaseError(`Could not spawn \`java -version\`. ${JAVA_HINT}`, { original: err }), + new FirebaseError(`Could not spawn \`java -version\`. ${JAVA_HINT}`, { + original: getError(err), + }), ); } diff --git a/src/emulator/controller.ts b/src/emulator/controller.ts index d02aac363c0..493dc0834db 100755 --- a/src/emulator/controller.ts +++ b/src/emulator/controller.ts @@ -20,6 +20,7 @@ import { import { Constants, FIND_AVAILBLE_PORT_BY_DEFAULT } from "./constants"; import { EmulatableBackend, FunctionsEmulator } from "./functionsEmulator"; import { FirebaseError } from "../error"; +import { getErrMsg, getError } from "../error"; import { getProjectId, getAliases, needProjectNumber } from "../projectUtils"; import * as commandUtils from "./commandUtils"; import { EmulatorHub } from "./hub"; @@ -174,11 +175,11 @@ export function shouldStart(options: Options, name: Emulators): boolean { try { normalizeAndValidate(options.config.src.functions); return true; - } catch (err: any) { + } catch (err: unknown) { EmulatorLogger.forEmulator(Emulators.FUNCTIONS).logLabeled( "ERROR", "functions", - `Failed to start Functions emulator: ${err.message}`, + `Failed to start Functions emulator: ${getErrMsg(err)}`, ); return false; } @@ -357,7 +358,7 @@ export async function startAll( if (!isDemoProject) { try { projectNumber = await needProjectNumber(options); - } catch (err: any) { + } catch (err: unknown) { EmulatorLogger.forEmulator(Emulators.EXTENSIONS).logLabeled( "ERROR", Emulators.EXTENSIONS, @@ -781,11 +782,8 @@ export async function startAll( if (!options.instance) { options.instance = await getDefaultDatabaseInstance(projectId); } - } catch (e: any) { - databaseLogger.log( - "DEBUG", - `Failed to retrieve default database instance: ${JSON.stringify(e)}`, - ); + } catch (e: unknown) { + databaseLogger.log("DEBUG", `Failed to retrieve default database instance: ${getErrMsg(e)}`); } const rc = dbRulesConfig.normalizeRulesConfig( @@ -1117,7 +1115,7 @@ export async function exportEmulatorData(exportPath: string, options: any, initi let origin; try { origin = await hubClient.getStatus(); - } catch (e: any) { + } catch (e: unknown) { const filePath = EmulatorHub.getLocatorFilePath(projectId); throw new FirebaseError( `The emulator hub for ${projectId} did not respond to a status check. If this error continues try shutting down all running emulators and deleting the file ${filePath}`, @@ -1161,10 +1159,10 @@ export async function exportEmulatorData(exportPath: string, options: any, initi try { const targets = filterEmulatorTargets(options); await hubClient.postExport({ path: exportAbsPath, initiatedBy, targets }); - } catch (e: any) { + } catch (e: unknown) { throw new FirebaseError("Export request failed, see emulator logs for more information.", { exit: 1, - original: e, + original: getError(e), }); } diff --git a/src/emulator/dataconnectEmulator.ts b/src/emulator/dataconnectEmulator.ts index 3283e4aa8bf..a0f129604ea 100644 --- a/src/emulator/dataconnectEmulator.ts +++ b/src/emulator/dataconnectEmulator.ts @@ -16,6 +16,7 @@ import { } from "./downloadableEmulators"; import { EmulatorInfo, EmulatorInstance, Emulators, ListenSpec } from "./types"; import { FirebaseError } from "../error"; +import { getErrMsg, getErrStatus } from "../error"; import { EmulatorLogger } from "./emulatorLogger"; import { BuildResult, mainSchemaYaml, requiresVector } from "../dataconnect/types"; import { listenSpecsToString } from "./portUtils"; @@ -95,8 +96,8 @@ export class DataConnectEmulator implements EmulatorInstance { ); } } - } catch (err: any) { - this.logger.log("DEBUG", `'fdc build' failed with error: ${err.message}`); + } catch (err: unknown) { + this.logger.log("DEBUG", `'fdc build' failed with error: ${getErrMsg(err)}`); } const env = await DataConnectEmulator.getEnv(this.args.account, this.args.extraEnv); await start( @@ -259,8 +260,8 @@ export class DataConnectEmulator implements EmulatorInstance { // Handle errors like command not found reject(err); }); - } catch (e: any) { - if (isIncomaptibleArchError(e)) { + } catch (e: unknown) { + if (isIncomaptibleArchError(e as Error)) { reject( new FirebaseError( `Unknown system error when running the SQL Connect toolkit. ` + @@ -343,14 +344,14 @@ export class DataConnectEmulator implements EmulatorInstance { `Successfully connected to ${connectionString}}`, ); return true; - } catch (err: any) { + } catch (err: unknown) { if (i === MAX_RETRIES) { throw err; } this.logger.logLabeled( "DEBUG", "SQL Connect", - `Retrying connectToPostgress call (${i} of ${MAX_RETRIES} attempts): ${err}`, + `Retrying connectToPostgress call (${i} of ${MAX_RETRIES} attempts): ${getErrMsg(err)}`, ); await new Promise((resolve) => setTimeout(resolve, 2000)); } @@ -413,9 +414,11 @@ export class DataConnectEmulatorClient { body, ); return res; - } catch (err: any) { - if (err.status === 500) { - throw new FirebaseError(`SQL Connect emulator: ${err?.context?.body?.message}`); + } catch (err: unknown) { + const status = getErrStatus(err); + if (status === 500) { + const message = (err as any)?.context?.body?.message; + throw new FirebaseError(`SQL Connect emulator: ${message || String(err)}`); } throw err; } diff --git a/src/emulator/download.ts b/src/emulator/download.ts index 062f827a930..b26b38ec9eb 100644 --- a/src/emulator/download.ts +++ b/src/emulator/download.ts @@ -40,7 +40,7 @@ export async function downloadEmulator(name: DownloadableEmulators): Promise { try { res(require(require.resolve(moduleName, opts))); // eslint-disable-line @typescript-eslint/no-var-requires - } catch (e: any) { + } catch (e: unknown) { rej(e); } }); @@ -51,7 +52,7 @@ function requireResolveAsync(moduleName: string, opts?: { paths: string[] }): Pr return new Promise((res, rej) => { try { res(require.resolve(moduleName, opts)); - } catch (e: any) { + } catch (e: unknown) { rej(e); } }); @@ -289,7 +290,7 @@ function requirePackageJson(): PackageJSON | undefined { devDependencies: pkg.devDependencies || {}, }; return developerPkgJSON; - } catch (err: any) { + } catch (err: unknown) { return; } } @@ -335,7 +336,7 @@ function initializeNetworkFiltering(): void { try { new URL(arg); return arg; - } catch (err: any) { + } catch (err: unknown) { return; } } else if (typeof arg === "object") { @@ -406,7 +407,7 @@ async function initializeFirebaseFunctionsStubs(): Promise { let httpsProvider: any; try { httpsProvider = require(httpsProviderV1Resolution); - } catch (e: any) { + } catch (e: unknown) { httpsProvider = require(httpsProviderResolution); } @@ -644,7 +645,7 @@ async function runFunction(func: () => Promise): Promise { let caughtErr; try { await func(); - } catch (err: any) { + } catch (err: unknown) { caughtErr = err; } if (caughtErr) { @@ -757,10 +758,15 @@ async function loadTriggers(): Promise { let triggerModule; try { triggerModule = require(process.cwd()); - } catch (err: any) { - if (err.code !== "ERR_REQUIRE_ESM") { - // Try to run diagnostics to see what could've gone wrong before rethrowing the error. - await moduleResolutionDetective(err); + } catch (err: unknown) { + if (typeof err === "object" && err !== null && "code" in err) { + const code = (err as { code: unknown }).code; + if (code !== "ERR_REQUIRE_ESM") { + // Try to run diagnostics to see what could've gone wrong before rethrowing the error. + await moduleResolutionDetective(err as unknown as Error); + throw err; + } + } else { throw err; } const modulePath = require.resolve(process.cwd()); @@ -780,7 +786,7 @@ async function handleMessage(message: string) { let debug: FunctionsRuntimeBundle["debug"]; try { debug = JSON.parse(message) as FunctionsRuntimeBundle["debug"]; - } catch (e: any) { + } catch (e: unknown) { new EmulatorLog("FATAL", "runtime-error", `Got unexpected message body: ${message}`).log(); await flushAndExit(1); return; @@ -819,11 +825,11 @@ async function main(): Promise { await initializeRuntime(); try { functionModule = await loadTriggers(); - } catch (e: any) { + } catch (e: unknown) { new EmulatorLog( "FATAL", "runtime-status", - `Failed to initialize and load triggers. This shouldn't happen: ${e.message}`, + `Failed to initialize and load triggers. This shouldn't happen: ${getErrMsg(e)}`, ).log(); await flushAndExit(1); } @@ -889,9 +895,9 @@ async function main(): Promise { case "http": await runHTTPS(trigger, [req, res]); } - } catch (err: any) { - new EmulatorLog("FATAL", "runtime-error", err.stack ? err.stack : err).log(); - res.status(500).send(err.message); + } catch (err: unknown) { + new EmulatorLog("FATAL", "runtime-error", getErrStack(err)).log(); + res.status(500).send(getErrMsg(err)); } }); app.listen(process.env.PORT, () => { diff --git a/src/emulator/functionsEmulatorShared.ts b/src/emulator/functionsEmulatorShared.ts index 852b8d23857..5a9a5009ed2 100644 --- a/src/emulator/functionsEmulatorShared.ts +++ b/src/emulator/functionsEmulatorShared.ts @@ -440,7 +440,7 @@ export function findModuleRoot(moduleName: string, filepath: string): string { return chunks.join("/"); } break; - } catch (err: any) { + } catch (err: unknown) { /**/ } } diff --git a/src/emulator/functionsRuntimeWorker.ts b/src/emulator/functionsRuntimeWorker.ts index a8391d1ccd2..a238223ea81 100644 --- a/src/emulator/functionsRuntimeWorker.ts +++ b/src/emulator/functionsRuntimeWorker.ts @@ -272,11 +272,14 @@ export class RuntimeWorker { try { await Promise.race([this.isSocketReady(), timeout]); break; - } catch (err: any) { + } catch (err: unknown) { // Allow us to wait until the server is listening. - if (["ECONNREFUSED", "ENOENT"].includes(err?.code)) { - await sleep(100); - continue; + if (typeof err === "object" && err !== null && "code" in err) { + const code = (err as { code: unknown }).code; + if (typeof code === "string" && ["ECONNREFUSED", "ENOENT"].includes(code)) { + await sleep(100); + continue; + } } throw err; } diff --git a/src/emulator/hubExport.ts b/src/emulator/hubExport.ts index 3c3fc629dec..481553c1dc9 100644 --- a/src/emulator/hubExport.ts +++ b/src/emulator/hubExport.ts @@ -85,7 +85,7 @@ export class HubExport { try { mdString = fs.readFileSync(metadataPath, "utf8").toString(); return JSON.parse(mdString) as ExportMetadata; - } catch (err: any) { + } catch (err: unknown) { // JSON parse errors are unreadable. Throw the original. throw new FirebaseError(`Unable to parse metadata file ${metadataPath}: ${mdString}`); } diff --git a/src/emulator/loggingEmulator.ts b/src/emulator/loggingEmulator.ts index c77e7c8bcf2..45f90e1bca1 100644 --- a/src/emulator/loggingEmulator.ts +++ b/src/emulator/loggingEmulator.ts @@ -123,7 +123,7 @@ class WebSocketTransport extends TransportStream { try { bundle.data = { ...bundle.data, ...JSON.parse(value) }; return null; - } catch (err: any) { + } catch (err: unknown) { // If the value isn't JSONable, just treat it like a string return value; } diff --git a/src/emulator/storage/persistence.ts b/src/emulator/storage/persistence.ts index fb02bd360e1..1e11d85d431 100644 --- a/src/emulator/storage/persistence.ts +++ b/src/emulator/storage/persistence.ts @@ -57,7 +57,7 @@ export class Persistence { deleteFile(fileName: string, failSilently = false): void { try { unlinkSync(this.getDiskPath(fileName)); - } catch (err: any) { + } catch (err: unknown) { if (!failSilently) { throw err; } diff --git a/src/emulator/storage/rules/runtime.ts b/src/emulator/storage/rules/runtime.ts index 7a359b5167f..67c8ffdfce5 100644 --- a/src/emulator/storage/rules/runtime.ts +++ b/src/emulator/storage/rules/runtime.ts @@ -193,7 +193,7 @@ export class StorageRulesRuntime { let rap; try { rap = JSON.parse(serializedRuntimeActionResponse) as RuntimeActionResponse; - } catch (err: any) { + } catch (err: unknown) { EmulatorLogger.forEmulator(Emulators.STORAGE).log( "INFO", serializedRuntimeActionResponse, diff --git a/src/emulator/types.ts b/src/emulator/types.ts index f33ba83868f..601ce0d6e43 100644 --- a/src/emulator/types.ts +++ b/src/emulator/types.ts @@ -284,7 +284,7 @@ export class EmulatorLog { let isNotJSON = false; try { parsedLog = JSON.parse(json); - } catch (err: any) { + } catch (err: unknown) { isNotJSON = true; } diff --git a/src/mcp/apps/update_environment/mcp-app.ts b/src/mcp/apps/update_environment/mcp-app.ts index ab878f8a635..02d601ac58d 100644 --- a/src/mcp/apps/update_environment/mcp-app.ts +++ b/src/mcp/apps/update_environment/mcp-app.ts @@ -12,8 +12,13 @@ const searchInput = document.getElementById("search-input") as HTMLInputElement; const submitBtn = document.getElementById("submit-btn") as HTMLButtonElement; const statusBox = document.getElementById("status-box") as HTMLDivElement; -let projects: any[] = []; -let filteredProjects: any[] = []; +interface Project { + projectId: string; + displayName?: string; +} + +let projects: Project[] = []; +let filteredProjects: Project[] = []; let selectedProjectId: string | null = null; const envProjectIdEl = document.getElementById("env-project-id") as HTMLSpanElement; diff --git a/src/mcp/resources/update_environment_ui.ts b/src/mcp/resources/update_environment_ui.ts index 76f10d3265e..f78d7c79a12 100644 --- a/src/mcp/resources/update_environment_ui.ts +++ b/src/mcp/resources/update_environment_ui.ts @@ -25,8 +25,9 @@ export const update_environment_ui = resource( }, ], }; - } catch (e: any) { - throw new Error(`Failed to load Update Environment UI: ${e.message}`); + } catch (e: unknown) { + const message = e instanceof Error ? e.message : String(e); + throw new Error(`Failed to load Update Environment UI: ${message}`); } }, ); diff --git a/src/mcp/util.ts b/src/mcp/util.ts index fe3c198b1ca..cda84f67583 100644 --- a/src/mcp/util.ts +++ b/src/mcp/util.ts @@ -23,7 +23,7 @@ import { timeoutFallback } from "../timeout"; * Converts data to a CallToolResult. */ export function toContent( - data: any, + data: unknown, options?: { format?: "json" | "yaml"; contentPrefix?: string; contentSuffix?: string }, ): CallToolResult { if (typeof data === "string") return { content: [{ type: "text", text: data }] }; @@ -42,8 +42,8 @@ export function toContent( const suffix = options?.contentSuffix || ""; return { content: [{ type: "text", text: `${prefix}${text}${suffix}` }], - structuredContent: data, - } as CallToolResult & { structuredContent: any }; + structuredContent: data as { [key: string]: unknown }, + }; } /**