diff --git a/.eslintrc.js b/.eslintrc.js index 04ba63980ee..73c7d4a11cd 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -65,9 +65,7 @@ module.exports = { "@typescript-eslint/no-unsafe-return": "warn", // TODO(bkendall): remove, allow to error. "@typescript-eslint/no-use-before-define": ["warn", { functions: false, typedefs: false }], // TODO(bkendall): change to error. "@typescript-eslint/no-var-requires": "warn", // TODO(bkendall): remove, allow to error. - "@typescript-eslint/prefer-includes": "warn", // TODO(bkendall): remove, allow to error. "@typescript-eslint/prefer-regexp-exec": "warn", // TODO(bkendall): remove, allow to error. - "@typescript-eslint/prefer-string-starts-ends-with": "warn", // TODO(bkendall): remove, allow to error. "@typescript-eslint/restrict-plus-operands": "warn", // TODO(bkendall): remove, allow to error. "@typescript-eslint/restrict-template-expressions": "warn", // TODO(bkendall): remove, allow to error. "no-constant-condition": "warn", // TODO(bkendall): remove, allow to error. diff --git a/src/apphosting/backend.ts b/src/apphosting/backend.ts index 2dccef1f1d1..2a298a0ef4e 100644 --- a/src/apphosting/backend.ts +++ b/src/apphosting/backend.ts @@ -50,7 +50,7 @@ async function tlsReady(url: string): Promise { // SSL. const maybeNodeError = err as { cause: { code: string }; code: string }; if ( - /HANDSHAKE_FAILURE/.test(maybeNodeError?.cause?.code) || + maybeNodeError?.cause?.code?.includes("HANDSHAKE_FAILURE") || "EPROTO" === maybeNodeError?.code ) { return false; diff --git a/src/deploy/firestore/prepare.ts b/src/deploy/firestore/prepare.ts index d562749bec1..8dee38dea71 100644 --- a/src/deploy/firestore/prepare.ts +++ b/src/deploy/firestore/prepare.ts @@ -156,15 +156,15 @@ export default async function (context: any, options: DeployOptions): Promise= 0; - const excludeIndexes = targets.indexOf("firestore:rules") >= 0; + const excludeRules = targets.includes("firestore:indexes"); + const excludeIndexes = targets.includes("firestore:rules"); // Used for edge case when deploying --only firestore:rules,firestore:indexes // https://github.com/firebase/firebase-tools/issues/6857 - const includeRules = targets.indexOf("firestore:rules") >= 0; - const includeIndexes = targets.indexOf("firestore:indexes") >= 0; + const includeRules = targets.includes("firestore:rules"); + const includeIndexes = targets.includes("firestore:indexes"); - const onlyFirestore = targets.indexOf("firestore") >= 0; + const onlyFirestore = targets.includes("firestore"); context.firestoreIndexes = !excludeIndexes || includeIndexes || onlyFirestore; context.firestoreRules = !excludeRules || includeRules || onlyFirestore; diff --git a/src/emulator/apphosting/serve.ts b/src/emulator/apphosting/serve.ts index 8f4a66d91fa..0fa41b74646 100644 --- a/src/emulator/apphosting/serve.ts +++ b/src/emulator/apphosting/serve.ts @@ -173,7 +173,7 @@ async function tripFirebasePostinstall( if ( dependency.name === "@firebase/util" && semverGte(dependency.version, "1.11.0") && - firebaseUtilPaths.indexOf(dependency.path) === -1 + !firebaseUtilPaths.includes(dependency.path) ) { firebaseUtilPaths.push(dependency.path); } diff --git a/src/emulator/auth/utils.ts b/src/emulator/auth/utils.ts index ae535d23349..fe1e26ecfda 100644 --- a/src/emulator/auth/utils.ts +++ b/src/emulator/auth/utils.ts @@ -57,7 +57,7 @@ export function isValidPhoneNumber(phoneNumber: string): boolean { // is not worth the effort and bloat (500+ kB). libphonenumber-js is not used // either since it has different behaviors and may block numbers that are // valid in production. - return /^\+/.test(phoneNumber); + return phoneNumber.startsWith("+"); } /** diff --git a/src/emulator/dataconnect/pgliteServer.ts b/src/emulator/dataconnect/pgliteServer.ts index ebc2bf729ec..1e8daf0a0fc 100644 --- a/src/emulator/dataconnect/pgliteServer.ts +++ b/src/emulator/dataconnect/pgliteServer.ts @@ -235,7 +235,7 @@ export class PostgresServer { await db.waitReady; return db; } catch (err: unknown) { - if (pg17Dir && hasMessage(err) && /Database already exists/.test(err.message)) { + if (pg17Dir && hasMessage(err) && err.message.includes("Database already exists")) { // Clear out the current pglite data fs.rmSync(pg17Dir, { force: true, recursive: true }); const db = new PGlite({ ...baseArgs, dataDir: pg17Dir }); diff --git a/src/emulator/download.ts b/src/emulator/download.ts index f8132a3e186..eb5b09bb255 100644 --- a/src/emulator/download.ts +++ b/src/emulator/download.ts @@ -112,7 +112,7 @@ function removeOldFiles( for (const file of files) { const fullFilePath = path.join(emulator.opts.cacheDir, file); - if (file.indexOf(emulator.opts.namePrefix) < 0) { + if (!file.includes(emulator.opts.namePrefix)) { // This file is not related to this emulator, could be a JAR // from a different emulator or just a random file. continue; diff --git a/src/emulator/downloadableEmulators.ts b/src/emulator/downloadableEmulators.ts index f36524999f1..5fc9ae5db41 100755 --- a/src/emulator/downloadableEmulators.ts +++ b/src/emulator/downloadableEmulators.ts @@ -50,7 +50,7 @@ const EMULATOR_UPDATE_DETAILS: { * generate download details for a single emulator, based on the host environment. * pulls data from `downloadableEmulatorInfo.json`. * @param emulator The name of the downloadable emulator to get details for. - * @returns The download details for the specified emulator. + * @return The download details for the specified emulator. */ function generateDownloadDetails(emulator: DownloadableEmulators): EmulatorDownloadDetails { const emulatorUiDetails = experiments.isEnabled("emulatoruisnapshot") @@ -653,14 +653,18 @@ export async function start( return _runBinary(emulator, command, extraEnv); } +/** + * + */ export function isIncomaptibleArchError(err: unknown): boolean { return ( - hasMessage(err) && - /Unknown system error/.test(err.message ?? "") && - process.platform === "darwin" + hasMessage(err) && err.message.includes("Unknown system error") && process.platform === "darwin" ); } +/** + * + */ export function emulatorVersionOverride(emulator: DownloadableEmulators) { return process.env[`${emulator.toUpperCase()}_EMULATOR_VERSION`]; } diff --git a/src/firestore/validator.ts b/src/firestore/validator.ts index b3d4a7a43a2..98d11a2494b 100644 --- a/src/firestore/validator.ts +++ b/src/firestore/validator.ts @@ -35,7 +35,7 @@ export function assertHasOneOf(obj: any, props: string[]): void { */ export function assertEnum(obj: any, prop: string, valid: any[]): void { const objString = clc.cyan(JSON.stringify(obj)); - if (valid.indexOf(obj[prop]) < 0) { + if (!valid.includes(obj[prop])) { throw new FirebaseError(`Field "${prop}" must be one of ${valid.join(", ")}: ${objString}`); } } diff --git a/src/profileReport.spec.ts b/src/profileReport.spec.ts index e3e3b0d7379..26cbf160d48 100644 --- a/src/profileReport.spec.ts +++ b/src/profileReport.spec.ts @@ -1,7 +1,7 @@ import { expect } from "chai"; - import * as stream from "stream"; -import { extractReadableIndex, formatNumber, ProfileReport } from "./profileReport"; + +import { extractJSON, extractReadableIndex, formatNumber, ProfileReport } from "./profileReport"; import { SAMPLE_INPUT_PATH, SAMPLE_OUTPUT_PATH } from "./test/fixtures/profiler-data"; function combinerFunc(obj1: any, obj2: any): any { @@ -100,4 +100,30 @@ describe("profilerReport", () => { const result = extractReadableIndex(query); expect(result).to.eq(".value"); }); + + describe("extractJSON", () => { + it("should return parsed JSON when line starts with data: and input is false", () => { + const line = 'data: {"path":["public"],"name":"rest-read"}'; + const result = extractJSON(line, false); + expect(result).to.deep.eq({ path: ["public"], name: "rest-read" }); + }); + + it("should return null when line does not start with data: and input is false", () => { + const line = "event: log"; + const result = extractJSON(line, false); + expect(result).to.be.null; + }); + + it("should return parsed JSON directly when input is true", () => { + const line = '{"path":["public"],"name":"rest-read"}'; + const result = extractJSON(line, true); + expect(result).to.deep.eq({ path: ["public"], name: "rest-read" }); + }); + + it("should return null for malformed JSON", () => { + const line = "data: {invalid-json}"; + const result = extractJSON(line, false); + expect(result).to.be.null; + }); + }); }); diff --git a/src/profileReport.ts b/src/profileReport.ts index 13a5bed11f4..ff658406ded 100644 --- a/src/profileReport.ts +++ b/src/profileReport.ts @@ -7,8 +7,6 @@ import * as readline from "readline"; import { FirebaseError } from "./error"; import { logger } from "./logger"; -const DATA_LINE_REGEX = /^data: /; - const BANDWIDTH_NOTE = "NOTE: The numbers reported here are only estimates of the data" + " payloads from read operations. They are NOT a valid measure of your bandwidth bill."; @@ -25,11 +23,14 @@ const COLLAPSE_WILDCARD = ["$wildcard"]; // 'static' helper methods +/** + * + */ export function extractJSON(line: string, input: any): string | null { - if (!input && !DATA_LINE_REGEX.test(line)) { + if (!input && !line.startsWith("data: ")) { return null; } else if (!input) { - line = line.substring(5); + line = line.substring(6); } try { return JSON.parse(line); @@ -38,10 +39,16 @@ export function extractJSON(line: string, input: any): string | null { } } +/** + * + */ export function pathString(path: string[]): string { return `/${path ? path.join("/") : ""}`; } +/** + * + */ export function formatNumber(num: number) { const parts = num.toFixed(2).split("."); parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); @@ -51,6 +58,9 @@ export function formatNumber(num: number) { return parts.join("."); } +/** + * + */ export function formatBytes(bytes: number) { const threshold = 1000; if (Math.round(bytes) < threshold) { @@ -66,6 +76,9 @@ export function formatBytes(bytes: number) { return formatNumber(formattedBytes) + " " + units[u]; } +/** + * + */ export function extractReadableIndex(query: Record): string { if (query.orderBy) { return query.orderBy;