From 8f477eef8eb725952e65741222a866062c63dd38 Mon Sep 17 00:00:00 2001 From: Florian Zia Date: Wed, 12 Feb 2025 14:35:59 +0100 Subject: [PATCH 01/13] chore: Run ESLint on src/app/global-error.js --- eslint.config.js | 1 - src/app/global-error.js | 9 +++++++-- tsconfig.json | 9 +++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 3ca4b9e3ffc..84cd44277d7 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -172,7 +172,6 @@ const config = [ // Ignore the following files for now. { files: [ - "src/app/global-error.js", "src/db/migrations/*.js", "src/scripts/build/*.js", "src/scripts/loadtest/*.js", diff --git a/src/app/global-error.js b/src/app/global-error.js index 58fd89e85de..e05b6c7b290 100644 --- a/src/app/global-error.js +++ b/src/app/global-error.js @@ -8,7 +8,12 @@ import * as Sentry from "@sentry/nextjs"; import NextError from "next/error"; import { useEffect } from "react"; -export default function GlobalError({ error }) { +/** + * @param {Object} props + * @param {Error} props.error - The error object + * @param {number} props.statusCode - HTTP error status code (default: 500) + */ +export default function GlobalError({ error, statusCode = 500 }) { useEffect(() => { Sentry.captureException(error); }, [error]); @@ -17,7 +22,7 @@ export default function GlobalError({ error }) { {/* This is the default Next.js error component. */} - + ); diff --git a/tsconfig.json b/tsconfig.json index c7cdf9e58d7..9ffe89fc47f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,10 @@ { "include": [ - "next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", - "jest.setup.ts" + // JS files that are not yet migrated to TS. + "src/app/global-error.js" ], "compilerOptions": { /* Visit https://aka.ms/tsconfig to read more about this file */ @@ -99,8 +99,5 @@ "skipLibCheck": true /* Skip type checking all .d.ts files. */, "lib": ["dom", "dom.iterable", "esnext"] }, - "exclude": [ - "node_modules/", - "sentry.*.config.ts", - ] + "exclude": ["node_modules"] } From 041dca8009a9a4512bb02a320dd7efa05e8dc4f1 Mon Sep 17 00:00:00 2001 From: Florian Zia Date: Wed, 12 Feb 2025 16:10:35 +0100 Subject: [PATCH 02/13] chore: Address typing issues in src/scripts/build/uploadAutoCompleteLocations.js --- eslint.config.js | 1 - .../build/uploadAutoCompleteLocations.js | 104 ++++++++++++++---- tsconfig.json | 3 +- 3 files changed, 86 insertions(+), 22 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 84cd44277d7..af2ee620165 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -173,7 +173,6 @@ const config = [ { files: [ "src/db/migrations/*.js", - "src/scripts/build/*.js", "src/scripts/loadtest/*.js", // Next is not running ESLint on root files by default. The only way to // include those would be to explicitly add them one by one. Instead, we diff --git a/src/scripts/build/uploadAutoCompleteLocations.js b/src/scripts/build/uploadAutoCompleteLocations.js index ad215599064..2c388bbf97e 100644 --- a/src/scripts/build/uploadAutoCompleteLocations.js +++ b/src/scripts/build/uploadAutoCompleteLocations.js @@ -21,11 +21,13 @@ import { readFileSync, rmSync, writeFileSync, + // WriteStream is used as type in this file. + // eslint-disable-next-line @typescript-eslint/no-unused-vars + WriteStream, } from "fs"; import Sentry from "@sentry/nextjs"; import os from "os"; import path from "path"; -import fs from "fs"; import AdmZip from "adm-zip"; import { uploadToS3 } from "../../utils/s3.js"; @@ -61,6 +63,12 @@ const allowedFeatureCodes = [ "PPLL", ]; +/** + * Logs the progress of a task. + * + * @param {number} currentCount - The current count. + * @param {number} totalCount - The total count. + */ function logProgress(currentCount, totalCount) { const progress = Math.round(((currentCount + 1) / totalCount) * 100); process.stdout.write( @@ -68,6 +76,14 @@ function logProgress(currentCount, totalCount) { ); } +/** + * Writes the content of a remote file to a local write stream. + * + * @param {Object} param - The parameters for the function. + * @param {string} param.url - The URL of the remote file. + * @param {WriteStream} param.writeStream - The write stream the file content is written to. + * @returns {Promise} Resolves when the file has been written. + */ function writeFromRemoteFile({ url, writeStream }) { return new Promise((resolve, reject) => { https.get(url, (res) => { @@ -82,6 +98,15 @@ function writeFromRemoteFile({ url, writeStream }) { }); } +/** + * Fetches the remote archive. + * + * @param {Object} param - The parameters for the function. + * @param {string} param.remoteArchiveUrl - The URL of the remote archive. + * @param {string} param.localDownloadPath - The local path where the file will be downloaded. + * @param {string} param.localExtractionPath - The local path where the archive will be extracted. + * @returns {Promise} Resolves when the extraction is complete. + */ async function fetchRemoteArchive({ remoteArchiveUrl, localDownloadPath, @@ -100,7 +125,7 @@ async function fetchRemoteArchive({ const zip = new AdmZip(localDownloadPath); await new Promise((resolve, reject) => { zip.extractAllToAsync(localExtractionPath, true, false, (error) => - error ? reject(error) : resolve(), + error ? reject(error) : resolve(localExtractionPath), ); }); } @@ -211,7 +236,12 @@ try { const locationDataRows = locationData.split("\n"); const locationRowCount = locationDataRows.length; const locationDataPopulated = locationDataRows.reduce( - (relevantLocations, location, rowIndex) => { + ( + /** @type {Array} */ + relevantLocations, + location, + rowIndex, + ) => { logProgress(rowIndex, locationRowCount); const [ @@ -242,21 +272,35 @@ try { if (isPopulatedPlaceOfInterest) { const alternateNames = parsedAlternateNames.filter( - ({ alternateOf, name: alternateName }) => - alternateOf === geonameId && alternateName !== name, + (parsedAlternateName) => { + if (!parsedAlternateName) { + return; + } + const { alternateOf, name: alternateName } = parsedAlternateName; + return alternateOf === geonameId && alternateName !== name; + }, ); - const preferredName = alternateNames.find( - ({ isPreferredName }) => isPreferredName === "1", - ); - const alternateNamesFinal = alternateNames.map((alternateName) => { - // Include the original name as an alternative name if we’ll use an - // alternate name that is the preferred name. - if (preferredName && preferredName.name === alternateName.name) { - return name; + const preferredName = alternateNames.find((alternateName) => { + if (!alternateName) { + return; } - - return alternateName.name; + const { isPreferredName } = alternateName; + return isPreferredName === "1"; }); + const alternateNamesFinal = alternateNames + .map((alternateName) => { + if (!alternateName) { + return ""; + } + // Include the original name as an alternative name if we’ll use an + // alternate name that is the preferred name. + if (preferredName && preferredName.name === alternateName.name) { + return name; + } + + return alternateName.name; + }) + .filter((alternateNameFinal) => alternateNameFinal); // NOTE: Using short keys and only including entries when available // keeps the resulting JSON significantly smaller. @@ -293,10 +337,30 @@ try { return false; } - return locationDataPopulated.some((location) => { + return locationDataRows.some((location) => { + const [ + geonameId, + _name, + _asciiname, + _alternatenames, + _latitude, + _longitude, + featureClass, + _featureCode, + _countryCode, + _cc2, + _admin1Code, + _admin2Code, + _admin3Code, + _admin4Code, + _population, + _elevation, + _dem, + _timezone, + _modificationDate, + ] = location.split("\t"); // lines are tab delimited return ( - location.id === parentId && - location.featureClass === allowedFeatureClass + geonameId === parentId && featureClass === allowedFeatureClass ); }); }, @@ -324,12 +388,12 @@ try { }; writeFileSync(LOCATIONS_DATA_FILE, JSON.stringify(locationDataFinal)); - let readStream = fs.createReadStream(LOCATIONS_DATA_FILE); + const fileBuffer = readFileSync(LOCATIONS_DATA_FILE); if (process.argv.includes("--skip-upload")) { console.debug("Skipping S3 upload"); } else { - await uploadToS3(`autocomplete/${LOCATIONS_DATA_FILE}`, readStream); + await uploadToS3(`autocomplete/${LOCATIONS_DATA_FILE}`, fileBuffer); } if (CLEANUP_TMP_DATA_AFTER_FINISHED) { diff --git a/tsconfig.json b/tsconfig.json index 9ffe89fc47f..2990016efae 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,8 @@ "**/*.tsx", ".next/types/**/*.ts", // JS files that are not yet migrated to TS. - "src/app/global-error.js" + "src/app/global-error.js", + "src/scripts/build/*.js" ], "compilerOptions": { /* Visit https://aka.ms/tsconfig to read more about this file */ From 104e90bfc17643d625500edb3c54cda215917f9c Mon Sep 17 00:00:00 2001 From: Florian Zia Date: Wed, 12 Feb 2025 16:27:56 +0100 Subject: [PATCH 03/13] chore: Address typing issues in src/scripts/build/getAutoCompleteLocations.js --- src/scripts/build/getAutoCompleteLocations.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/scripts/build/getAutoCompleteLocations.js b/src/scripts/build/getAutoCompleteLocations.js index efc19301bfe..7c4e36498ab 100644 --- a/src/scripts/build/getAutoCompleteLocations.js +++ b/src/scripts/build/getAutoCompleteLocations.js @@ -15,6 +15,10 @@ if (!existsSync(dataPath)) { const fetchUrl = `https://s3.amazonaws.com/${process.env.S3_BUCKET}/autocomplete/locationAutocompleteData.json`; console.debug({ fetchUrl }); const { body } = await fetch(fetchUrl); + if (!body) { + throw new Error(`Failed to fetch: ${fetchUrl}`); + } + // @ts-ignore If body is `null` we are already throwing an error above. await finished(Readable.fromWeb(body).pipe(stream)); } catch (e) { console.error(e); From 1f85a240dc95056af0a9cda41ca6b31e4b3e2ae9 Mon Sep 17 00:00:00 2001 From: Florian Zia Date: Wed, 12 Feb 2025 16:50:52 +0100 Subject: [PATCH 04/13] chore: Address typing issues in src/scripts/build/nimbusTypes.js --- src/scripts/build/nimbusTypes.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/scripts/build/nimbusTypes.js b/src/scripts/build/nimbusTypes.js index ef920bd783b..58b59974f78 100644 --- a/src/scripts/build/nimbusTypes.js +++ b/src/scripts/build/nimbusTypes.js @@ -11,7 +11,8 @@ run(); /** * See https://experimenter.info/fml-spec/#additional-types * - * @typedef {"String" | "Boolean" | "Int" | "Text" | "Image" | `Option<${Type}>` | `List<${Type}>` | `Map<${Type}, ${Type}>`} Type + * @typedef { "String" | "Boolean" | "Int" | "Text" | "Image" } BaseType + * @typedef { BaseType | `Option<${BaseType}>` | `List<${BaseType}>` | `Map<${BaseType}, ${BaseType}>` } Type */ /** * @typedef {"local" | "staging" | "production"} Channel @@ -23,7 +24,7 @@ run(); * description: string; * type: Type; * default: unknown; - * string-alias?: string; + * "string-alias"?: string; * } * >} Variables */ @@ -126,7 +127,7 @@ function getLocalOverrides(nimbusConfig) { const overriddenValuesDef = typeof localOverrides === "undefined" ? "" - : ` ...${JSON.stringify(localOverrides.value, null, 2).replaceAll("\n", "\n ")}\n`; + : ` ...${JSON.stringify(localOverrides.values, null, 2).replaceAll("\n", "\n ")}\n`; return ` "${featureId}": {\n ...defaultExperimentData["Features"]["${featureId}"],\n${overriddenValuesDef} },\n`; }, ); @@ -159,7 +160,7 @@ function getFeaturesTypeDef(nimbusConfig) { nimbusConfig.features[featureId].variables, ); const variableDefs = variableNames.map((variableName) => { - return ` "${variableName}": ${getType(nimbusConfig.features[featureId].variables[variableName].type)};\n`; + return ` "${variableName}": ${getTypeScriptType(nimbusConfig.features[featureId].variables[variableName].type)};\n`; }); return ` "${featureId}": {\n${variableDefs.join("")} };\n`; }); @@ -208,15 +209,15 @@ function getStringAliases(nimbusConfig) { /** * @param {NimbusConfig} nimbusConfig - * @returns string + * @returns {string} */ function getTypeAliases(nimbusConfig) { const objects = nimbusConfig.objects ?? {}; const objectDefs = Object.keys(objects).map((typeAlias) => { - const propertyNames = Object.keys(nimbusConfig.objects[typeAlias].fields); + const propertyNames = Object.keys(objects[typeAlias].fields); const propertyDefs = propertyNames.map((propertyName) => { // TODO: Add descriptions as TSDoc comment? - return ` "${propertyName}": ${getType(nimbusConfig.objects[typeAlias].fields[propertyName].type)};\n`; + return ` "${propertyName}": ${getTypeScriptType(objects[typeAlias].fields[propertyName].type)};\n`; }); // TODO: Add description as TSDoc comment? return `type ${typeAlias} = {\n${propertyDefs.join("")}};\n`; @@ -225,7 +226,7 @@ function getTypeAliases(nimbusConfig) { const enums = nimbusConfig.enums ?? {}; const enumDefs = Object.keys(enums).map((typeAlias) => { // TODO: Add values as TSDoc comment? - const unionOfStrings = Object.keys(nimbusConfig.enums[typeAlias].variants) + const unionOfStrings = Object.keys(enums[typeAlias].variants) .map((variant) => `"${variant}"`) .join(" | "); return `type ${typeAlias} = ${unionOfStrings};`; @@ -241,9 +242,9 @@ function getTypeAliases(nimbusConfig) { /** * @param {string} type - * @returns string + * @returns {string} type */ -function getType(type) { +function getTypeScriptType(type) { if (type === "String") { return "string"; } @@ -258,16 +259,16 @@ function getType(type) { } if (type.startsWith("Option<")) { const t = type.substring("Option<".length, type.length - 1).trim(); - return `null | ${getType(t)}`; + return `null | ${getTypeScriptType(t)}`; } if (type.startsWith("List<")) { const t = type.substring("List<".length, type.length - 1).trim(); - return `Array<${getType(t)}>`; + return `Array<${getTypeScriptType(t)}>`; } if (type.startsWith("Map<")) { const kv = type.substring("Map<".length, type.length - 1).trim(); const [k, v] = kv.split(",").map((part) => part.trim()); - return `Record<${getType(k)}, ${getType(v)}>`; + return `Record<${getTypeScriptType(k)}, ${getTypeScriptType(v)}>`; } return type; From 8898122ccb5e9e5ddde6eed2c78322bedf1b1ddc Mon Sep 17 00:00:00 2001 From: Florian Zia Date: Wed, 12 Feb 2025 17:12:31 +0100 Subject: [PATCH 05/13] chore: Address typing issues in src/scripts/build/gleanTypes.js --- src/scripts/build/gleanTypes.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/scripts/build/gleanTypes.js b/src/scripts/build/gleanTypes.js index 8b46a9e43d2..5a7db3d7102 100644 --- a/src/scripts/build/gleanTypes.js +++ b/src/scripts/build/gleanTypes.js @@ -27,6 +27,7 @@ async function generateTypeMap() { (filename) => filename.endsWith(".ts") && filename !== MAP_FILE_NAME, ) .map((filename) => filename.replace(".ts", "")); + /** @type {Array<[string, Array<[string, string]>]>} */ const typeMapByModule = await Promise.all( moduleNames.map(async (moduleName) => [ moduleName, @@ -64,9 +65,9 @@ async function generateTypeMap() { * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // AUTOGENERATED BY the \`build-glean-types\` script. DO NOT EDIT. DO NOT COMMIT. - + ${eventTypeDefsString} - + export type GleanMetricMap = {\n${typeMapString}\n}; `; @@ -75,13 +76,13 @@ export type GleanMetricMap = {\n${typeMapString}\n}; /** * @param {string} moduleName + * @return {Promise>} */ async function getTypeMap(moduleName) { const filePath = resolve(GLEAN_GENERATED_DIR, `${moduleName}.ts`); const fileContents = await fs.readFile(filePath, "utf-8"); const eventNameAndTypeRegex = /export const (.+?) = new EventMetricType<(.+?)>/gs; - /** @type {Array<[string, string, string]>} */ const matches = Array.from(fileContents.matchAll(eventNameAndTypeRegex)); return matches.map(([_match, eventName, eventTypeDef]) => [ eventName, From e37990d9bf3f71818ede1e50a2b1ce1da4bb77a9 Mon Sep 17 00:00:00 2001 From: Florian Zia Date: Wed, 12 Feb 2025 18:10:10 +0100 Subject: [PATCH 06/13] chore: Address typing issues in src/scripts/build/checkNodeVersionAlignment.js --- .../build/checkNodeVersionAlignment.js | 43 ++++++++++++++----- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/src/scripts/build/checkNodeVersionAlignment.js b/src/scripts/build/checkNodeVersionAlignment.js index e41237796a2..5a106e6d543 100644 --- a/src/scripts/build/checkNodeVersionAlignment.js +++ b/src/scripts/build/checkNodeVersionAlignment.js @@ -17,26 +17,38 @@ import { fileURLToPath } from "node:url"; */ const __dirname = dirname(fileURLToPath(import.meta.url)); +/** + * @param {string} path - File path + * @returns {Promise} File content + */ const load = (path) => readFile(resolve(__dirname, "../../../", path), "utf-8"); const packageJson = JSON.parse(await load("package.json")); const lockFile = JSON.parse(await load("package-lock.json")); -const dockerFileNodeVersion = (await load("Dockerfile")) - .split("\n") - .find((line) => line.startsWith("FROM node:")) - .split("FROM node:")[1] - .split("-alpine")[0]; +const dockerFileNodeVersion = + ( + (await load("Dockerfile")) + .split("\n") + .find((line) => line.startsWith("FROM node:")) ?? "" + ) + .split("FROM node:")[1] + ?.split("-alpine")[0] ?? "default-value"; // Provide a safe fallback value + // Yes, we're assuming `netlify.toml` to contain a line in exactly this format: // environment = { NODE_VERSION = "20.12.0", NPM_VERSION = "10.2.4" } -const netlifyNodeVersion = (await load("netlify.toml")) - .split("\n") - .find((line) => line.includes('NODE_VERSION = "')) +const netlifyNodeVersion = ( + (await load("netlify.toml")) + .split("\n") + .find((line) => line.includes('NODE_VERSION = "')) ?? "" +) .split('NODE_VERSION = "')[1] .split('", NPM_VERSION')[0]; -const esbuildVersion = (await load("esbuild.cronjobs.js")) - .split("\n") - .find((line) => line.includes("target:")) +const esbuildVersion = ( + (await load("esbuild.cronjobs.js")) + .split("\n") + .find((line) => line.includes("target:")) ?? "" +) .split('"node')[1] .split('",')[0]; @@ -59,10 +71,19 @@ const ghaVersions = ghaWorkflows.flatMap(([filename, contents]) => { ]); }); +/** + * @param {string} version - The full version string + * @returns {string} The minor version + */ const getMinorOnly = (version) => { const versionParts = version.split("."); return `${versionParts[0]}.${versionParts[1]}`; }; + +/** + * @param {string} source - The file to check. + * @param {string} version - The reference Node version. + */ const checkVersion = (source, version) => { const voltaVersion = packageJson.volta.node; if (getMinorOnly(voltaVersion) !== getMinorOnly(version)) { From b48133816cb36e516669e864e7481d0b20737d98 Mon Sep 17 00:00:00 2001 From: Florian Zia Date: Wed, 12 Feb 2025 18:26:25 +0100 Subject: [PATCH 07/13] chore: Address typing issues in src/scripts/loadtest/hibp.js --- eslint.config.js | 1 - src/scripts/loadtest/hibp.js | 9 +++++---- tsconfig.json | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index af2ee620165..382547d1088 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -173,7 +173,6 @@ const config = [ { files: [ "src/db/migrations/*.js", - "src/scripts/loadtest/*.js", // Next is not running ESLint on root files by default. The only way to // include those would be to explicitly add them one by one. Instead, we // run ESLint directly in addition to next lint on just the root files. diff --git a/src/scripts/loadtest/hibp.js b/src/scripts/loadtest/hibp.js index 98e65c09b7a..337b9e20b36 100644 --- a/src/scripts/loadtest/hibp.js +++ b/src/scripts/loadtest/hibp.js @@ -14,9 +14,8 @@ import { post } from "k6/http"; import crypto from "k6/crypto"; -// k6 exposes environment variables via the __ENV variable: +// @ts-ignore: k6 exposes environment variables via the __ENV variable: // https://grafana.com/docs/k6/latest/using-k6/environment-variables/ -/** @type {typeof process.env} */ const envVars = __ENV; const HIBP_NOTIFY_TOKEN = envVars.HIBP_NOTIFY_TOKEN; @@ -48,7 +47,7 @@ const mockedBreachedEmailHash = export const run = () => { /** @type {import("../../app/api/v1/hibp/notify/route").PostHibpNotificationRequestBody} */ - let data = { + const data = { breachName: "ApexSMS", // NOTE: modify this hash range if you want to receive email to specific test account(s). // This example should only email an address that is a sha1 hash for 1c48923da9f6f17165711712d11bc104087444cc. @@ -58,7 +57,7 @@ export const run = () => { }; // Using a JSON string as body - let res = post(url, JSON.stringify(data), { + const res = post(url, JSON.stringify(data), { headers: { "Content-Type": "application/json", Authorization: `Bearer ${HIBP_NOTIFY_TOKEN}`, @@ -67,10 +66,12 @@ export const run = () => { try { const result = res.json(); + // @ts-ignore TODO: Add type for result. if (result.success !== true) { throw new Error(`Non-success result: ${JSON.stringify(result)}`); } } catch { + // @ts-ignore TODO: Add type for result. throw new Error(`Failed to parse result: ${res.status}, ${res.text}`); } }; diff --git a/tsconfig.json b/tsconfig.json index 2990016efae..f497b97323a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,8 @@ ".next/types/**/*.ts", // JS files that are not yet migrated to TS. "src/app/global-error.js", - "src/scripts/build/*.js" + "src/scripts/build/*.js", + "src/scripts/loadtest/hibp.js" ], "compilerOptions": { /* Visit https://aka.ms/tsconfig to read more about this file */ From 858b5ebb557d4208fa78e2697a3cf132a288ad8f Mon Sep 17 00:00:00 2001 From: Florian Zia Date: Wed, 12 Feb 2025 18:52:12 +0100 Subject: [PATCH 08/13] chore: Address typing issues in src/db/migrations/*.js --- eslint.config.js | 16 +++--- .../20180418090800_initial_schema.js | 8 +++ ...826102013_add_timestamps_to_subscribers.js | 8 +++ ...20180829161115_add_fx_newsletter_column.js | 8 +++ .../20180930071926_add_signup_language.js | 8 +++ .../20181007085241_add_sha1_index.js | 9 ++++ .../20181108151941_add_created_at_index.js | 8 +++ .../20181129152508_add_email_index.js | 8 +++ .../20181227100332_add_fxa_columns.js | 8 +++ .../20190117150910_add_verified_index.js | 8 +++ .../20190219154519_add_fxa_uid_column.js | 8 +++ ...0190328111900_add_email_addresses_table.js | 8 +++ ...422140308_add_subscriber_breaches_shown.js | 8 +++ ...52733_add_timestamps_to_email_addresses.js | 8 +++ ...170106_add_all_emails_to_primary_column.js | 8 +++ ...919_add_fxa_access_token_to_subscribers.js | 8 +++ .../20190713193852_add_email_sha1_index.js | 9 ++++ .../20191118100718_add-fxa-uid-index.js | 8 +++ ...8170713_add-email_addresses-email-index.js | 8 +++ ...1202161125_add_breaches_resolved_column.js | 8 +++ .../20200220143251_add-waitlists-column.js | 8 +++ ...d-subscribers-breaches_last_shown-index.js | 8 +++ ...0810144851_add_signup_language_index.js.js | 8 +++ .../20210823152654_add_kid_to_subscribers.js | 8 +++ ...20211001120114_user_enroll_and_pay_null.js | 8 +++ .../20211001135448_removal_pilot_table.js | 8 +++ ...20103145534_initial_removal_pilot_group.js | 8 +++ ...4095854_add-unenroll-col-to-subscribers.js | 8 +++ ...20607154058_rollback-data-removal-pilot.js | 8 +++ .../20220818212858_add-breach-stats.js | 8 +++ ...20220826220021_add-monthly-email-column.js | 8 +++ ...20220828233844_add-monthly-email-optout.js | 8 +++ ...1026215921_add-breach-resolution-column.js | 8 +++ .../20221026215921_add-breaches-table.js | 18 +++++++ ...30322233844_add-db-migration-processing.js | 8 +++ .../20230618104332_feature_flags.js | 11 +++- ...add_unique_constraint_onerep_profile_id.js | 2 + ...0230703010356_add-onerep-profiles-table.js | 12 +++++ ...0230711034311_remove_temp_migration_col.js | 5 +- .../20230811154502_add_onerep_scan_reason.js | 4 ++ ...0907143204_add_onerep_scanresults_table.js | 50 +++++++++++-------- .../20230921132056_add_status_column.js | 1 + ...624_add_unique_index_to_scan_results_id.js | 2 + .../migrations/20231220015816_onerep_stats.js | 4 ++ .../20240108008813_marketing_attributions.js | 4 ++ ...0240408161125_add_monthly_report_column.js | 8 +++ ...and_middle_name_to_onerep_profile_table.js | 8 +++ .../20240604053111_subscriber_coupons.js | 4 ++ ...0606111238_add_subscriber_sign_in_count.js | 8 +++ ...t_attempts_to_onerep_scan_results_table.js | 4 ++ ...702102931_add_fxa_expiry_to_subscribers.js | 4 ++ .../20240715115031_qa_custom_breaches.js | 4 ++ ...0822032133_subscriber_email_preferences.js | 4 ++ .../20241105115743_add_data_brokers.js | 8 +++ .../20250115032133_subscriber_churns.js | 4 ++ .../20250204102945_add_last_optout_at.js | 4 ++ tsconfig.json | 1 + 57 files changed, 422 insertions(+), 34 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 382547d1088..6f5eae6dd42 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -169,17 +169,13 @@ const config = [ "@typescript-eslint/no-unsafe-call": "off", }, }, - // Ignore the following files for now. + // Next is not running ESLint on root files by default. The only way to + // include those would be to explicitly add them one by one. Instead, we + // run ESLint directly in addition to next lint on just the root files. + // For more info see: + // https://nextjs.org/docs/app/api-reference/config/eslint#linting-custom-directories-and-files { - files: [ - "src/db/migrations/*.js", - // Next is not running ESLint on root files by default. The only way to - // include those would be to explicitly add them one by one. Instead, we - // run ESLint directly in addition to next lint on just the root files. - // For more info see: - // https://nextjs.org/docs/app/api-reference/config/eslint#linting-custom-directories-and-files - "*.{js,cjs,ts}", - ], + files: ["*.{js,cjs,ts}"], languageOptions: { parserOptions: { project: null }, }, diff --git a/src/db/migrations/20180418090800_initial_schema.js b/src/db/migrations/20180418090800_initial_schema.js index 2220e43d80a..4c92dad4d9e 100644 --- a/src/db/migrations/20180418090800_initial_schema.js +++ b/src/db/migrations/20180418090800_initial_schema.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.createTable("subscribers", (table) => { table.increments("id").primary(); @@ -12,6 +16,10 @@ export function up(knex) { }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.dropTableIfExists("subscribers"); } diff --git a/src/db/migrations/20180826102013_add_timestamps_to_subscribers.js b/src/db/migrations/20180826102013_add_timestamps_to_subscribers.js index 22a892f9050..f9ae802cf76 100644 --- a/src/db/migrations/20180826102013_add_timestamps_to_subscribers.js +++ b/src/db/migrations/20180826102013_add_timestamps_to_subscribers.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.timestamps(false, true); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropTimestamps(); diff --git a/src/db/migrations/20180829161115_add_fx_newsletter_column.js b/src/db/migrations/20180829161115_add_fx_newsletter_column.js index 20984121a38..f5cba4e3664 100644 --- a/src/db/migrations/20180829161115_add_fx_newsletter_column.js +++ b/src/db/migrations/20180829161115_add_fx_newsletter_column.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.boolean("fx_newsletter").defaultTo(false); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("fx_newsletter"); diff --git a/src/db/migrations/20180930071926_add_signup_language.js b/src/db/migrations/20180930071926_add_signup_language.js index 7e58823b394..dc98f1c9272 100644 --- a/src/db/migrations/20180930071926_add_signup_language.js +++ b/src/db/migrations/20180930071926_add_signup_language.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.string("signup_language"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("signup_language"); diff --git a/src/db/migrations/20181007085241_add_sha1_index.js b/src/db/migrations/20181007085241_add_sha1_index.js index e809870030e..f8d024e7f2e 100644 --- a/src/db/migrations/20181007085241_add_sha1_index.js +++ b/src/db/migrations/20181007085241_add_sha1_index.js @@ -5,12 +5,21 @@ // Note: this index was created on heroku, stage, and prod by hand // Use this statement to "fake" the migration: // INSERT INTO knex_migrations (name, batch, migration_time) values ('20181007085241_add_sha1_index.js', 4, '2018-10-07 08:52:42.000-05'); + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.index("sha1", "subscribers_sha1_idx"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropIndex("sha1", "subscribers_sha1_idx"); diff --git a/src/db/migrations/20181108151941_add_created_at_index.js b/src/db/migrations/20181108151941_add_created_at_index.js index 938c77cb973..79d876605f9 100644 --- a/src/db/migrations/20181108151941_add_created_at_index.js +++ b/src/db/migrations/20181108151941_add_created_at_index.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.index("created_at"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropIndex("created_at"); diff --git a/src/db/migrations/20181129152508_add_email_index.js b/src/db/migrations/20181129152508_add_email_index.js index 3458eceb157..19b272e9290 100644 --- a/src/db/migrations/20181129152508_add_email_index.js +++ b/src/db/migrations/20181129152508_add_email_index.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.index("email", "subscribers_email_idx"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropIndex("email", "subscribers_email_idx"); diff --git a/src/db/migrations/20181227100332_add_fxa_columns.js b/src/db/migrations/20181227100332_add_fxa_columns.js index 2eb428660f1..05a6007bb30 100644 --- a/src/db/migrations/20181227100332_add_fxa_columns.js +++ b/src/db/migrations/20181227100332_add_fxa_columns.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.string("fxa_refresh_token"); @@ -9,6 +13,10 @@ export function up(knex) { }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("fxa_refresh_token"); diff --git a/src/db/migrations/20190117150910_add_verified_index.js b/src/db/migrations/20190117150910_add_verified_index.js index 86f95e50c59..695b6995657 100644 --- a/src/db/migrations/20190117150910_add_verified_index.js +++ b/src/db/migrations/20190117150910_add_verified_index.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.index("verified", "subscribers_verified_idx"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropIndex("verified", "subscribers_verified_idx"); diff --git a/src/db/migrations/20190219154519_add_fxa_uid_column.js b/src/db/migrations/20190219154519_add_fxa_uid_column.js index e27739bfdbb..7f1853e35c4 100644 --- a/src/db/migrations/20190219154519_add_fxa_uid_column.js +++ b/src/db/migrations/20190219154519_add_fxa_uid_column.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.string("fxa_uid"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("fxa_uid"); diff --git a/src/db/migrations/20190328111900_add_email_addresses_table.js b/src/db/migrations/20190328111900_add_email_addresses_table.js index 0c7e310fa44..e78cc9dc039 100644 --- a/src/db/migrations/20190328111900_add_email_addresses_table.js +++ b/src/db/migrations/20190328111900_add_email_addresses_table.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise<[void, void]> } + */ export function up(knex) { return Promise.all([ knex.schema.createTable("email_addresses", (table) => { @@ -22,6 +26,10 @@ export function up(knex) { ]); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise<[void, void]> } + */ export function down(knex) { return Promise.all([ knex.schema.dropTableIfExists("email_addresses"), diff --git a/src/db/migrations/20190422140308_add_subscriber_breaches_shown.js b/src/db/migrations/20190422140308_add_subscriber_breaches_shown.js index 23ceaa2d403..5f0807d65ab 100644 --- a/src/db/migrations/20190422140308_add_subscriber_breaches_shown.js +++ b/src/db/migrations/20190422140308_add_subscriber_breaches_shown.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.timestamp("breaches_last_shown"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("breaches_last_shown"); diff --git a/src/db/migrations/20190510152733_add_timestamps_to_email_addresses.js b/src/db/migrations/20190510152733_add_timestamps_to_email_addresses.js index 0c57076d17c..359d1e74986 100644 --- a/src/db/migrations/20190510152733_add_timestamps_to_email_addresses.js +++ b/src/db/migrations/20190510152733_add_timestamps_to_email_addresses.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("email_addresses", (table) => { table.timestamps(false); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("email_addresses", (table) => { table.dropTimestamps(); diff --git a/src/db/migrations/20190512170106_add_all_emails_to_primary_column.js b/src/db/migrations/20190512170106_add_all_emails_to_primary_column.js index 362bb6fcb35..70969c20eb9 100644 --- a/src/db/migrations/20190512170106_add_all_emails_to_primary_column.js +++ b/src/db/migrations/20190512170106_add_all_emails_to_primary_column.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.boolean("all_emails_to_primary").defaultTo(false); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("all_emails_to_primary"); diff --git a/src/db/migrations/20190523152919_add_fxa_access_token_to_subscribers.js b/src/db/migrations/20190523152919_add_fxa_access_token_to_subscribers.js index 014dcb308a7..b46c4da8d25 100644 --- a/src/db/migrations/20190523152919_add_fxa_access_token_to_subscribers.js +++ b/src/db/migrations/20190523152919_add_fxa_access_token_to_subscribers.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.string("fxa_access_token"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("fxa_access_token"); diff --git a/src/db/migrations/20190713193852_add_email_sha1_index.js b/src/db/migrations/20190713193852_add_email_sha1_index.js index 37581b429ff..90ccdf91d11 100644 --- a/src/db/migrations/20190713193852_add_email_sha1_index.js +++ b/src/db/migrations/20190713193852_add_email_sha1_index.js @@ -5,12 +5,21 @@ // Note: this index was created on heroku, stage, and prod by hand // Use this statement to "fake" the migration: // INSERT INTO knex_migrations (name, batch, migration_time) values ('20190713193852_add_email_sha1_index.js', (SELECT max(batch) + 1 FROM knex_migrations), '2019-07-13 19:52:42.000-05'); + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("email_addresses", (table) => { table.index("sha1", "email_addresses_sha1_idx"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("email_addresses", (table) => { table.dropIndex("sha1", "email_addresses_sha1_idx"); diff --git a/src/db/migrations/20191118100718_add-fxa-uid-index.js b/src/db/migrations/20191118100718_add-fxa-uid-index.js index 56bec8bb9c4..35fe648acdb 100644 --- a/src/db/migrations/20191118100718_add-fxa-uid-index.js +++ b/src/db/migrations/20191118100718_add-fxa-uid-index.js @@ -6,12 +6,20 @@ // Use this statement to "fake" the migration: // INSERT INTO knex_migrations (name, batch, migration_time) values ('20191118100718_add-fxa-uid-index.js', (SELECT max(batch) + 1 FROM knex_migrations), '2019-11-18 11:00:00.000-05'); +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.index("fxa_uid", "subscribers_fxa_uid_idx"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropIndex("fxa_uid", "subscribers_fxa_uid_idx"); diff --git a/src/db/migrations/20191118170713_add-email_addresses-email-index.js b/src/db/migrations/20191118170713_add-email_addresses-email-index.js index 597f76190d3..5cf0e06eae4 100644 --- a/src/db/migrations/20191118170713_add-email_addresses-email-index.js +++ b/src/db/migrations/20191118170713_add-email_addresses-email-index.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("email_addresses", (table) => { table.index("email", "email_addresses_email_idx"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("email_addresses", (table) => { table.dropIndex("email", "email_addresses_email_idx"); diff --git a/src/db/migrations/20191202161125_add_breaches_resolved_column.js b/src/db/migrations/20191202161125_add_breaches_resolved_column.js index f2bf666c2bd..2c00ad48ee4 100644 --- a/src/db/migrations/20191202161125_add_breaches_resolved_column.js +++ b/src/db/migrations/20191202161125_add_breaches_resolved_column.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.jsonb("breaches_resolved"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("breaches_resolved"); diff --git a/src/db/migrations/20200220143251_add-waitlists-column.js b/src/db/migrations/20200220143251_add-waitlists-column.js index 82a623bae45..d97d3cf0e68 100644 --- a/src/db/migrations/20200220143251_add-waitlists-column.js +++ b/src/db/migrations/20200220143251_add-waitlists-column.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.jsonb("waitlists_joined"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("waitlists_joined"); diff --git a/src/db/migrations/20200708123351_add-subscribers-breaches_last_shown-index.js b/src/db/migrations/20200708123351_add-subscribers-breaches_last_shown-index.js index f8fda52079f..e5e9e3567a3 100644 --- a/src/db/migrations/20200708123351_add-subscribers-breaches_last_shown-index.js +++ b/src/db/migrations/20200708123351_add-subscribers-breaches_last_shown-index.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.index("breaches_last_shown", "subscribers_breaches_last_shown_idx"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("email_addresses", (table) => { table.dropIndex( diff --git a/src/db/migrations/20200810144851_add_signup_language_index.js.js b/src/db/migrations/20200810144851_add_signup_language_index.js.js index 5a13d4d3939..d94953a5244 100644 --- a/src/db/migrations/20200810144851_add_signup_language_index.js.js +++ b/src/db/migrations/20200810144851_add_signup_language_index.js.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.index("signup_language", "subscribers_signup_language_idx"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("email_addresses", (table) => { table.dropIndex("signup_language", "subscribers_signup_language_idx"); diff --git a/src/db/migrations/20210823152654_add_kid_to_subscribers.js b/src/db/migrations/20210823152654_add_kid_to_subscribers.js index f18cb5d34a2..d4d61d222e5 100644 --- a/src/db/migrations/20210823152654_add_kid_to_subscribers.js +++ b/src/db/migrations/20210823152654_add_kid_to_subscribers.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.integer("kid"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("kid"); diff --git a/src/db/migrations/20211001120114_user_enroll_and_pay_null.js b/src/db/migrations/20211001120114_user_enroll_and_pay_null.js index 1b170697d43..1bedf4e6c04 100644 --- a/src/db/migrations/20211001120114_user_enroll_and_pay_null.js +++ b/src/db/migrations/20211001120114_user_enroll_and_pay_null.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.boolean("removal_would_pay"); @@ -9,6 +13,10 @@ export function up(knex) { }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("removal_would_pay"); diff --git a/src/db/migrations/20211001135448_removal_pilot_table.js b/src/db/migrations/20211001135448_removal_pilot_table.js index a35d3964cda..c35243467ac 100644 --- a/src/db/migrations/20211001135448_removal_pilot_table.js +++ b/src/db/migrations/20211001135448_removal_pilot_table.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.createTable("removal_pilot", (table) => { table.increments("id").primary(); @@ -10,6 +14,10 @@ export function up(knex) { }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.dropTableIfExists("removal_pilot"); } diff --git a/src/db/migrations/20220103145534_initial_removal_pilot_group.js b/src/db/migrations/20220103145534_initial_removal_pilot_group.js index 2f301789f6c..7c7882cda3b 100644 --- a/src/db/migrations/20220103145534_initial_removal_pilot_group.js +++ b/src/db/migrations/20220103145534_initial_removal_pilot_group.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex("removal_pilot") .del() @@ -13,6 +17,10 @@ export function up(knex) { }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex("removal_pilot").del(); } diff --git a/src/db/migrations/20220104095854_add-unenroll-col-to-subscribers.js b/src/db/migrations/20220104095854_add-unenroll-col-to-subscribers.js index e6444f7982b..393befb0883 100644 --- a/src/db/migrations/20220104095854_add-unenroll-col-to-subscribers.js +++ b/src/db/migrations/20220104095854_add-unenroll-col-to-subscribers.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.boolean("removal_optout").defaultTo(false); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("removal_optout"); diff --git a/src/db/migrations/20220607154058_rollback-data-removal-pilot.js b/src/db/migrations/20220607154058_rollback-data-removal-pilot.js index 4a8b242be28..ba6a460abd5 100644 --- a/src/db/migrations/20220607154058_rollback-data-removal-pilot.js +++ b/src/db/migrations/20220607154058_rollback-data-removal-pilot.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export async function up(knex) { await knex.schema.table("subscribers", (table) => { table.dropColumns( @@ -15,6 +19,10 @@ export async function up(knex) { await knex.schema.dropTableIfExists("removal_pilot"); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export async function down(knex) { const hasTable = await knex.schema.hasTable("removal_pilot"); diff --git a/src/db/migrations/20220818212858_add-breach-stats.js b/src/db/migrations/20220818212858_add-breach-stats.js index 60d206a8e94..568a0349758 100644 --- a/src/db/migrations/20220818212858_add-breach-stats.js +++ b/src/db/migrations/20220818212858_add-breach-stats.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.jsonb("breach_stats").defaultTo(null); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("breach_stats"); diff --git a/src/db/migrations/20220826220021_add-monthly-email-column.js b/src/db/migrations/20220826220021_add-monthly-email-column.js index 12f295584fd..3fccfd54147 100644 --- a/src/db/migrations/20220826220021_add-monthly-email-column.js +++ b/src/db/migrations/20220826220021_add-monthly-email-column.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.timestamp("monthly_email_at"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("monthly_email_at"); diff --git a/src/db/migrations/20220828233844_add-monthly-email-optout.js b/src/db/migrations/20220828233844_add-monthly-email-optout.js index efc1dbefa49..2b8b92df524 100644 --- a/src/db/migrations/20220828233844_add-monthly-email-optout.js +++ b/src/db/migrations/20220828233844_add-monthly-email-optout.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.boolean("monthly_email_optout"); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("monthly_email_optout"); diff --git a/src/db/migrations/20221026215921_add-breach-resolution-column.js b/src/db/migrations/20221026215921_add-breach-resolution-column.js index de2836281aa..53c55e69bb8 100644 --- a/src/db/migrations/20221026215921_add-breach-resolution-column.js +++ b/src/db/migrations/20221026215921_add-breach-resolution-column.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.jsonb("breach_resolution").defaultTo(null); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("breach_resolution"); diff --git a/src/db/migrations/20221026215921_add-breaches-table.js b/src/db/migrations/20221026215921_add-breaches-table.js index 65699b2d73b..2fddc754001 100644 --- a/src/db/migrations/20221026215921_add-breaches-table.js +++ b/src/db/migrations/20221026215921_add-breaches-table.js @@ -2,17 +2,31 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.createTable("breaches", (table) => { table.increments("id").primary(); + // @ts-ignore TODO: Determine if the following line should be changed to + // `defaultTo` or remain unchanged as initially deployed. table.string("name").default("").unique(); + // @ts-ignore TODO: Determine if the following line should be changed to + // `defaultTo` or remain unchanged as initially deployed. table.string("title").default(""); + // @ts-ignore TODO: Determine if the following line should be changed to + // `defaultTo` or remain unchanged as initially deployed. table.string("domain").default(""); table.date("breach_date").notNullable(); table.timestamp("added_date"); table.timestamp("modified_date").defaultTo(knex.fn.now()); table.integer("pwn_count").defaultTo(0); + // @ts-ignore TODO: Determine if the following line should be changed to + // `defaultTo` or remain unchanged as initially deployed. table.text("description").default(""); + // @ts-ignore TODO: Determine if the following line should be changed to + // `defaultTo` or remain unchanged as initially deployed. table.string("logo_path").default(""); table.specificType("data_classes", "character varying(255)[]"); table.boolean("is_verified").defaultTo(false); @@ -24,6 +38,10 @@ export function up(knex) { }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.dropTableIfExists("breaches"); } diff --git a/src/db/migrations/20230322233844_add-db-migration-processing.js b/src/db/migrations/20230322233844_add-db-migration-processing.js index b1a1d0bd5f0..ddf3eae13d0 100644 --- a/src/db/migrations/20230322233844_add-db-migration-processing.js +++ b/src/db/migrations/20230322233844_add-db-migration-processing.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.boolean("db_migration_1"); @@ -9,6 +13,10 @@ export function up(knex) { }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("db_migration_1"); diff --git a/src/db/migrations/20230618104332_feature_flags.js b/src/db/migrations/20230618104332_feature_flags.js index 28f1cc9ba56..fcd33dc20a1 100644 --- a/src/db/migrations/20230618104332_feature_flags.js +++ b/src/db/migrations/20230618104332_feature_flags.js @@ -2,10 +2,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.createTable("feature_flags", (table) => { table.string("name").primary().unique(); table.boolean("is_enabled").defaultTo(false); + // @ts-ignore TODO: Determine if the following line should be changed to + // `defaultTo` or remain unchanged as initially deployed. table.text("description").default(""); table.specificType("dependencies", "character varying(255)[]"); table.specificType("allow_list", "character varying(255)[]"); @@ -17,7 +23,10 @@ export function up(knex) { table.string("owner"); }); } - +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.dropTableIfExists("feature_flags"); } diff --git a/src/db/migrations/20230623072741_add_unique_constraint_onerep_profile_id.js b/src/db/migrations/20230623072741_add_unique_constraint_onerep_profile_id.js index ece57f0121c..8aad0a1db2e 100644 --- a/src/db/migrations/20230623072741_add_unique_constraint_onerep_profile_id.js +++ b/src/db/migrations/20230623072741_add_unique_constraint_onerep_profile_id.js @@ -18,6 +18,8 @@ export function up(knex) { */ export function down(knex) { return knex.schema.table("subscribers", (table) => { + // @ts-ignore TODO: Determine if the following line should be changed to + // `defaultTo` or remain unchanged as initially deployed. table.dropUnique("onerep_profile_id"); }); } diff --git a/src/db/migrations/20230703010356_add-onerep-profiles-table.js b/src/db/migrations/20230703010356_add-onerep-profiles-table.js index 50c779cbeb2..6f84ff1b3b4 100644 --- a/src/db/migrations/20230703010356_add-onerep-profiles-table.js +++ b/src/db/migrations/20230703010356_add-onerep-profiles-table.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.createTable("onerep_profiles", (table) => { table.increments("id").primary(); @@ -9,9 +13,17 @@ export function up(knex) { .integer("onerep_profile_id") .references("subscribers.onerep_profile_id") .nullable(); + // @ts-ignore TODO: Determine if the following line should be changed + // or remain unchanged as initially deployed. table.varchar("first_name"); + // @ts-ignore TODO: Determine if the following line should be changed + // or remain unchanged as initially deployed. table.varchar("last_name"); + // @ts-ignore TODO: Determine if the following line should be changed + // or remain unchanged as initially deployed. table.varchar("city_name"); + // @ts-ignore TODO: Determine if the following line should be changed + // or remain unchanged as initially deployed. table.varchar("state_code"); table.date("date_of_birth"); table.timestamp("created_at").defaultTo(knex.fn.now()); diff --git a/src/db/migrations/20230711034311_remove_temp_migration_col.js b/src/db/migrations/20230711034311_remove_temp_migration_col.js index cda60093b7c..1b34fb6fdec 100644 --- a/src/db/migrations/20230711034311_remove_temp_migration_col.js +++ b/src/db/migrations/20230711034311_remove_temp_migration_col.js @@ -6,7 +6,6 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ - export async function up(knex) { if (await knex.schema.hasColumn("subscribers", "db_migration_1")) { await knex.schema.alterTable("subscribers", (table) => { @@ -21,6 +20,10 @@ export async function up(knex) { } } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export async function down(knex) { if (true === (await knex.schema.hasColumn("subscribers", "db_migration_1"))) { console.log("do nothing for db_migration_1"); diff --git a/src/db/migrations/20230811154502_add_onerep_scan_reason.js b/src/db/migrations/20230811154502_add_onerep_scan_reason.js index 5f3d99c455a..2f11f0d5dc7 100644 --- a/src/db/migrations/20230811154502_add_onerep_scan_reason.js +++ b/src/db/migrations/20230811154502_add_onerep_scan_reason.js @@ -12,6 +12,10 @@ export function up(knex) { }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("onerep_scans", (table) => { table.dropColumn("onerep_scan_reason"); diff --git a/src/db/migrations/20230907143204_add_onerep_scanresults_table.js b/src/db/migrations/20230907143204_add_onerep_scanresults_table.js index 8c7c4acd67d..3fcf1db19fb 100644 --- a/src/db/migrations/20230907143204_add_onerep_scanresults_table.js +++ b/src/db/migrations/20230907143204_add_onerep_scanresults_table.js @@ -55,28 +55,32 @@ export async function up(knex) { const scanResultRows = scanRows .map((scan) => { return ( - scan.onerep_scan_results?.data.map((scanResult) => { - const rowToInsert = { - onerep_scan_result_id: scanResult.id, - onerep_scan_id: scan.onerep_scan_id, - link: scanResult.link, - age: - typeof scanResult.age === "string" - ? Number.parseInt(scanResult.age, 10) - : null, - data_broker: scanResult.data_broker, - data_broker_id: scanResult.data_broker_id, - emails: JSON.stringify(scanResult.emails), - phones: JSON.stringify(scanResult.phones), - addresses: JSON.stringify(scanResult.addresses), - relatives: JSON.stringify(scanResult.relatives), - first_name: scanResult.first_name, - middle_name: scanResult.middle_name, - last_name: scanResult.last_name, - status: scanResult.status, - }; - return rowToInsert; - }) ?? [] + scan.onerep_scan_results?.data.map( + ( + /** @type {import("knex/types/tables").OnerepScanResultRow} */ scanResult, + ) => { + const rowToInsert = { + onerep_scan_result_id: scanResult.id, + onerep_scan_id: scan.onerep_scan_id, + link: scanResult.link, + age: + typeof scanResult.age === "string" + ? Number.parseInt(scanResult.age, 10) + : null, + data_broker: scanResult.data_broker, + data_broker_id: scanResult.data_broker_id, + emails: JSON.stringify(scanResult.emails), + phones: JSON.stringify(scanResult.phones), + addresses: JSON.stringify(scanResult.addresses), + relatives: JSON.stringify(scanResult.relatives), + first_name: scanResult.first_name, + middle_name: scanResult.middle_name, + last_name: scanResult.last_name, + status: scanResult.status, + }; + return rowToInsert; + }, + ) ?? [] ); }) .flat(); @@ -100,6 +104,8 @@ export async function down(knex) { }); await knex.schema.dropTable("onerep_scan_results"); await knex.schema.alterTable("onerep_scans", (table) => { + // @ts-ignore TODO: Determine if the following line should be changed to + // `defaultTo` or remain unchanged as initially deployed. table.dropUnique("onerep_scan_id"); }); } diff --git a/src/db/migrations/20230921132056_add_status_column.js b/src/db/migrations/20230921132056_add_status_column.js index 773b3e85fc4..58e80b72237 100644 --- a/src/db/migrations/20230921132056_add_status_column.js +++ b/src/db/migrations/20230921132056_add_status_column.js @@ -10,6 +10,7 @@ export async function up(knex) { await knex.schema.alterTable("onerep_scans", (table) => { table.string("onerep_scan_status"); }); + // @ts-ignore TODO: Fix type error. await knex("onerep_scans").update({ onerep_scan_status: "finished" }); await knex.schema.alterTable("onerep_scans", (table) => { table.dropNullable("onerep_scan_status"); diff --git a/src/db/migrations/20231102024624_add_unique_index_to_scan_results_id.js b/src/db/migrations/20231102024624_add_unique_index_to_scan_results_id.js index 513ae82c739..e18510aec86 100644 --- a/src/db/migrations/20231102024624_add_unique_index_to_scan_results_id.js +++ b/src/db/migrations/20231102024624_add_unique_index_to_scan_results_id.js @@ -24,6 +24,8 @@ export async function up(knex) { */ export function down(knex) { return knex.schema.table("onerep_scan_results", (table) => { + // @ts-ignore TODO: Determine if the following line should be changed to + // `defaultTo` or remain unchanged as initially deployed. table.dropUnique("onerep_scan_result_id"); }); } diff --git a/src/db/migrations/20231220015816_onerep_stats.js b/src/db/migrations/20231220015816_onerep_stats.js index 65261c454a0..a7c57885ec3 100644 --- a/src/db/migrations/20231220015816_onerep_stats.js +++ b/src/db/migrations/20231220015816_onerep_stats.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export async function up(knex) { return knex.schema.createTable("stats", (table) => { table.increments("id").primary(); diff --git a/src/db/migrations/20240108008813_marketing_attributions.js b/src/db/migrations/20240108008813_marketing_attributions.js index e4d6bfc321f..a7f0c474985 100644 --- a/src/db/migrations/20240108008813_marketing_attributions.js +++ b/src/db/migrations/20240108008813_marketing_attributions.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export async function up(knex) { return knex.schema.createTable("attributions", (table) => { table.increments("id").primary(); diff --git a/src/db/migrations/20240408161125_add_monthly_report_column.js b/src/db/migrations/20240408161125_add_monthly_report_column.js index a07cfe85bc2..a7e7d6997b8 100644 --- a/src/db/migrations/20240408161125_add_monthly_report_column.js +++ b/src/db/migrations/20240408161125_add_monthly_report_column.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("monthly_email_optout"); @@ -11,6 +15,10 @@ export function up(knex) { }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.boolean("monthly_email_optout"); diff --git a/src/db/migrations/20240423150332_add_suffix_and_middle_name_to_onerep_profile_table.js b/src/db/migrations/20240423150332_add_suffix_and_middle_name_to_onerep_profile_table.js index 1560a51c864..8dbab9b793f 100644 --- a/src/db/migrations/20240423150332_add_suffix_and_middle_name_to_onerep_profile_table.js +++ b/src/db/migrations/20240423150332_add_suffix_and_middle_name_to_onerep_profile_table.js @@ -2,9 +2,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("onerep_profiles", (table) => { + // @ts-ignore TODO: Determine if the following line should be changed + // or remain unchanged as initially deployed. table.varchar("name_suffix").after("onerep_profile_id"); + // @ts-ignore TODO: Determine if the following line should be changed + // or remain unchanged as initially deployed. table.varchar("middle_name").after("first_name"); }); } diff --git a/src/db/migrations/20240604053111_subscriber_coupons.js b/src/db/migrations/20240604053111_subscriber_coupons.js index f83918d7444..90ed098a153 100644 --- a/src/db/migrations/20240604053111_subscriber_coupons.js +++ b/src/db/migrations/20240604053111_subscriber_coupons.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export async function up(knex) { return knex.schema.createTable("subscriber_coupons", (table) => { table.increments("id").primary(); diff --git a/src/db/migrations/20240606111238_add_subscriber_sign_in_count.js b/src/db/migrations/20240606111238_add_subscriber_sign_in_count.js index be17be36e1a..57ffbbc151d 100644 --- a/src/db/migrations/20240606111238_add_subscriber_sign_in_count.js +++ b/src/db/migrations/20240606111238_add_subscriber_sign_in_count.js @@ -2,12 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.integer("sign_in_count").defaultTo(0); }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function down(knex) { return knex.schema.table("subscribers", (table) => { table.dropColumn("sign_in_count"); diff --git a/src/db/migrations/20240610150332_add_optout_attempts_to_onerep_scan_results_table.js b/src/db/migrations/20240610150332_add_optout_attempts_to_onerep_scan_results_table.js index 1e5302329c2..cb2757e9368 100644 --- a/src/db/migrations/20240610150332_add_optout_attempts_to_onerep_scan_results_table.js +++ b/src/db/migrations/20240610150332_add_optout_attempts_to_onerep_scan_results_table.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("onerep_scan_results", (table) => { table.integer("optout_attempts").after("status").nullable(); diff --git a/src/db/migrations/20240702102931_add_fxa_expiry_to_subscribers.js b/src/db/migrations/20240702102931_add_fxa_expiry_to_subscribers.js index 780e7b5cb62..48bd044546b 100644 --- a/src/db/migrations/20240702102931_add_fxa_expiry_to_subscribers.js +++ b/src/db/migrations/20240702102931_add_fxa_expiry_to_subscribers.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { return knex.schema.table("subscribers", (table) => { table.timestamp("fxa_session_expiry").after("fxa_access_token").nullable(); diff --git a/src/db/migrations/20240715115031_qa_custom_breaches.js b/src/db/migrations/20240715115031_qa_custom_breaches.js index c097b280bff..2bf419ac9c1 100644 --- a/src/db/migrations/20240715115031_qa_custom_breaches.js +++ b/src/db/migrations/20240715115031_qa_custom_breaches.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export function up(knex) { // camel case for easier insertion into table return knex.schema.createTable("qa_custom_breaches", (table) => { diff --git a/src/db/migrations/20240822032133_subscriber_email_preferences.js b/src/db/migrations/20240822032133_subscriber_email_preferences.js index 4c575c2684d..3d14bff92d2 100644 --- a/src/db/migrations/20240822032133_subscriber_email_preferences.js +++ b/src/db/migrations/20240822032133_subscriber_email_preferences.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export async function up(knex) { await knex.schema.createTable( "subscriber_email_preferences", diff --git a/src/db/migrations/20241105115743_add_data_brokers.js b/src/db/migrations/20241105115743_add_data_brokers.js index 0b5d050b262..28105e24da3 100644 --- a/src/db/migrations/20241105115743_add_data_brokers.js +++ b/src/db/migrations/20241105115743_add_data_brokers.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export async function up(knex) { return knex.schema.createTable("onerep_data_brokers", (table) => { table.increments("id").primary(); @@ -13,6 +17,10 @@ export async function up(knex) { }); } +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export async function down(knex) { return knex.schema.dropTable("onerep_data_brokers"); } diff --git a/src/db/migrations/20250115032133_subscriber_churns.js b/src/db/migrations/20250115032133_subscriber_churns.js index 561314f4e88..3914d2736ff 100644 --- a/src/db/migrations/20250115032133_subscriber_churns.js +++ b/src/db/migrations/20250115032133_subscriber_churns.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export async function up(knex) { await knex.schema.createTable("subscriber_churns", function (table) { table.increments("id").primary(); diff --git a/src/db/migrations/20250204102945_add_last_optout_at.js b/src/db/migrations/20250204102945_add_last_optout_at.js index 8429cc2f5ee..5e64e0611ae 100644 --- a/src/db/migrations/20250204102945_add_last_optout_at.js +++ b/src/db/migrations/20250204102945_add_last_optout_at.js @@ -2,6 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ export async function up(knex) { await knex.schema.table("onerep_scan_results", (table) => { table.timestamp("last_optout_at").after("optout_attempts").nullable(); diff --git a/tsconfig.json b/tsconfig.json index f497b97323a..0fa2f058886 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ ".next/types/**/*.ts", // JS files that are not yet migrated to TS. "src/app/global-error.js", + "src/db/migrations/*.js", "src/scripts/build/*.js", "src/scripts/loadtest/hibp.js" ], From 9afb5269077ad28c9ad9e9f4062f73bf6f8b255d Mon Sep 17 00:00:00 2001 From: Florian Zia Date: Wed, 12 Feb 2025 19:13:31 +0100 Subject: [PATCH 09/13] fix: Wrong type in nimbusTypes.js --- src/scripts/build/nimbusTypes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scripts/build/nimbusTypes.js b/src/scripts/build/nimbusTypes.js index 58b59974f78..0e8f1918d71 100644 --- a/src/scripts/build/nimbusTypes.js +++ b/src/scripts/build/nimbusTypes.js @@ -37,7 +37,7 @@ run(); * variables: Variables; * defaults?: Array<{ * channel: Channel; - * values: Record; + * value: Record; * }>; * }>; * enums?: Record Date: Thu, 13 Feb 2025 11:14:40 +0100 Subject: [PATCH 10/13] fix: Knex methods and args --- .../20221026215921_add-breaches-table.js | 20 +++++-------------- .../20230618104332_feature_flags.js | 4 +--- ...add_unique_constraint_onerep_profile_id.js | 4 +--- ...0907143204_add_onerep_scanresults_table.js | 4 +--- ...624_add_unique_index_to_scan_results_id.js | 4 +--- 5 files changed, 9 insertions(+), 27 deletions(-) diff --git a/src/db/migrations/20221026215921_add-breaches-table.js b/src/db/migrations/20221026215921_add-breaches-table.js index 2fddc754001..9b933363971 100644 --- a/src/db/migrations/20221026215921_add-breaches-table.js +++ b/src/db/migrations/20221026215921_add-breaches-table.js @@ -9,25 +9,15 @@ export function up(knex) { return knex.schema.createTable("breaches", (table) => { table.increments("id").primary(); - // @ts-ignore TODO: Determine if the following line should be changed to - // `defaultTo` or remain unchanged as initially deployed. - table.string("name").default("").unique(); - // @ts-ignore TODO: Determine if the following line should be changed to - // `defaultTo` or remain unchanged as initially deployed. - table.string("title").default(""); - // @ts-ignore TODO: Determine if the following line should be changed to - // `defaultTo` or remain unchanged as initially deployed. - table.string("domain").default(""); + table.string("name").defaultTo("").unique(); + table.string("title").defaultTo(""); + table.string("domain").defaultTo(""); table.date("breach_date").notNullable(); table.timestamp("added_date"); table.timestamp("modified_date").defaultTo(knex.fn.now()); table.integer("pwn_count").defaultTo(0); - // @ts-ignore TODO: Determine if the following line should be changed to - // `defaultTo` or remain unchanged as initially deployed. - table.text("description").default(""); - // @ts-ignore TODO: Determine if the following line should be changed to - // `defaultTo` or remain unchanged as initially deployed. - table.string("logo_path").default(""); + table.text("description").defaultTo(""); + table.string("logo_path").defaultTo(""); table.specificType("data_classes", "character varying(255)[]"); table.boolean("is_verified").defaultTo(false); table.boolean("is_fabricated").defaultTo(false); diff --git a/src/db/migrations/20230618104332_feature_flags.js b/src/db/migrations/20230618104332_feature_flags.js index fcd33dc20a1..fc413ebe635 100644 --- a/src/db/migrations/20230618104332_feature_flags.js +++ b/src/db/migrations/20230618104332_feature_flags.js @@ -10,9 +10,7 @@ export function up(knex) { return knex.schema.createTable("feature_flags", (table) => { table.string("name").primary().unique(); table.boolean("is_enabled").defaultTo(false); - // @ts-ignore TODO: Determine if the following line should be changed to - // `defaultTo` or remain unchanged as initially deployed. - table.text("description").default(""); + table.text("description").defaultTo(""); table.specificType("dependencies", "character varying(255)[]"); table.specificType("allow_list", "character varying(255)[]"); table.specificType("wait_list", "character varying(255)[]"); diff --git a/src/db/migrations/20230623072741_add_unique_constraint_onerep_profile_id.js b/src/db/migrations/20230623072741_add_unique_constraint_onerep_profile_id.js index 8aad0a1db2e..018b6461aff 100644 --- a/src/db/migrations/20230623072741_add_unique_constraint_onerep_profile_id.js +++ b/src/db/migrations/20230623072741_add_unique_constraint_onerep_profile_id.js @@ -18,8 +18,6 @@ export function up(knex) { */ export function down(knex) { return knex.schema.table("subscribers", (table) => { - // @ts-ignore TODO: Determine if the following line should be changed to - // `defaultTo` or remain unchanged as initially deployed. - table.dropUnique("onerep_profile_id"); + table.dropUnique(["onerep_profile_id"]); }); } diff --git a/src/db/migrations/20230907143204_add_onerep_scanresults_table.js b/src/db/migrations/20230907143204_add_onerep_scanresults_table.js index 3fcf1db19fb..93e3fceb125 100644 --- a/src/db/migrations/20230907143204_add_onerep_scanresults_table.js +++ b/src/db/migrations/20230907143204_add_onerep_scanresults_table.js @@ -104,8 +104,6 @@ export async function down(knex) { }); await knex.schema.dropTable("onerep_scan_results"); await knex.schema.alterTable("onerep_scans", (table) => { - // @ts-ignore TODO: Determine if the following line should be changed to - // `defaultTo` or remain unchanged as initially deployed. - table.dropUnique("onerep_scan_id"); + table.dropUnique(["onerep_scan_id"]); }); } diff --git a/src/db/migrations/20231102024624_add_unique_index_to_scan_results_id.js b/src/db/migrations/20231102024624_add_unique_index_to_scan_results_id.js index e18510aec86..be5a115b3e9 100644 --- a/src/db/migrations/20231102024624_add_unique_index_to_scan_results_id.js +++ b/src/db/migrations/20231102024624_add_unique_index_to_scan_results_id.js @@ -24,8 +24,6 @@ export async function up(knex) { */ export function down(knex) { return knex.schema.table("onerep_scan_results", (table) => { - // @ts-ignore TODO: Determine if the following line should be changed to - // `defaultTo` or remain unchanged as initially deployed. - table.dropUnique("onerep_scan_result_id"); + table.dropUnique(["onerep_scan_result_id"]); }); } From e189fb12af61c4ab9c7fbb41365ae0fff40f56d7 Mon Sep 17 00:00:00 2001 From: Florian Zia Date: Thu, 13 Feb 2025 16:10:10 +0100 Subject: [PATCH 11/13] chore: Infer that parsedAlternateNames does not contain null --- .../build/uploadAutoCompleteLocations.js | 45 ++++++------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/src/scripts/build/uploadAutoCompleteLocations.js b/src/scripts/build/uploadAutoCompleteLocations.js index 2c388bbf97e..76508fd98e2 100644 --- a/src/scripts/build/uploadAutoCompleteLocations.js +++ b/src/scripts/build/uploadAutoCompleteLocations.js @@ -21,9 +21,6 @@ import { readFileSync, rmSync, writeFileSync, - // WriteStream is used as type in this file. - // eslint-disable-next-line @typescript-eslint/no-unused-vars - WriteStream, } from "fs"; import Sentry from "@sentry/nextjs"; import os from "os"; @@ -81,7 +78,7 @@ function logProgress(currentCount, totalCount) { * * @param {Object} param - The parameters for the function. * @param {string} param.url - The URL of the remote file. - * @param {WriteStream} param.writeStream - The write stream the file content is written to. + * @param {import("fs").WriteStream} param.writeStream - The write stream the file content is written to. * @returns {Promise} Resolves when the file has been written. */ function writeFromRemoteFile({ url, writeStream }) { @@ -210,7 +207,7 @@ try { return null; }) - .filter((alternateName) => alternateName); + .filter((alternateName) => alternateName !== null); console.info("Reading file: Hierarchy"); const hierachyData = readFileSync( @@ -272,35 +269,21 @@ try { if (isPopulatedPlaceOfInterest) { const alternateNames = parsedAlternateNames.filter( - (parsedAlternateName) => { - if (!parsedAlternateName) { - return; - } - const { alternateOf, name: alternateName } = parsedAlternateName; - return alternateOf === geonameId && alternateName !== name; - }, + ({ alternateOf, name: alternateName }) => + alternateOf === geonameId && alternateName !== name, ); - const preferredName = alternateNames.find((alternateName) => { - if (!alternateName) { - return; + const preferredName = alternateNames.find( + ({ isPreferredName }) => isPreferredName === "1", + ); + const alternateNamesFinal = alternateNames.map((alternateName) => { + // Include the original name as an alternative name if we’ll use an + // alternate name that is the preferred name. + if (preferredName && preferredName.name === alternateName.name) { + return name; } - const { isPreferredName } = alternateName; - return isPreferredName === "1"; + + return alternateName.name; }); - const alternateNamesFinal = alternateNames - .map((alternateName) => { - if (!alternateName) { - return ""; - } - // Include the original name as an alternative name if we’ll use an - // alternate name that is the preferred name. - if (preferredName && preferredName.name === alternateName.name) { - return name; - } - - return alternateName.name; - }) - .filter((alternateNameFinal) => alternateNameFinal); // NOTE: Using short keys and only including entries when available // keeps the resulting JSON significantly smaller. From 77fdb3f4142b17f45b52984fa9d4bd864d86c090 Mon Sep 17 00:00:00 2001 From: Florian Zia Date: Thu, 13 Feb 2025 16:14:25 +0100 Subject: [PATCH 12/13] chore: Update varchar comments --- ...0230703010356_add-onerep-profiles-table.js | 20 +++++++++++-------- ...and_middle_name_to_onerep_profile_table.js | 10 ++++++---- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/db/migrations/20230703010356_add-onerep-profiles-table.js b/src/db/migrations/20230703010356_add-onerep-profiles-table.js index 6f84ff1b3b4..41486da7b0f 100644 --- a/src/db/migrations/20230703010356_add-onerep-profiles-table.js +++ b/src/db/migrations/20230703010356_add-onerep-profiles-table.js @@ -13,17 +13,21 @@ export function up(knex) { .integer("onerep_profile_id") .references("subscribers.onerep_profile_id") .nullable(); - // @ts-ignore TODO: Determine if the following line should be changed - // or remain unchanged as initially deployed. + // @ts-ignore TODO: The type Knex.CreateTableBuilder is missing `varchar`: + // https://github.com/knex/knex/blob/master/types/index.d.ts#L2420 + // The method is implemented, but the method `string` is recommended. table.varchar("first_name"); - // @ts-ignore TODO: Determine if the following line should be changed - // or remain unchanged as initially deployed. + // @ts-ignore TODO: The type Knex.CreateTableBuilder is missing `varchar`: + // https://github.com/knex/knex/blob/master/types/index.d.ts#L2420 + // The method is implemented, but the method `string` is recommended. table.varchar("last_name"); - // @ts-ignore TODO: Determine if the following line should be changed - // or remain unchanged as initially deployed. + // @ts-ignore TODO: The type Knex.CreateTableBuilder is missing `varchar`: + // https://github.com/knex/knex/blob/master/types/index.d.ts#L2420 + // The method is implemented, but the method `string` is recommended. table.varchar("city_name"); - // @ts-ignore TODO: Determine if the following line should be changed - // or remain unchanged as initially deployed. + // @ts-ignore TODO: The type Knex.CreateTableBuilder is missing `varchar`: + // https://github.com/knex/knex/blob/master/types/index.d.ts#L2420 + // The method is implemented, but the method `string` is recommended. table.varchar("state_code"); table.date("date_of_birth"); table.timestamp("created_at").defaultTo(knex.fn.now()); diff --git a/src/db/migrations/20240423150332_add_suffix_and_middle_name_to_onerep_profile_table.js b/src/db/migrations/20240423150332_add_suffix_and_middle_name_to_onerep_profile_table.js index 8dbab9b793f..276af364f50 100644 --- a/src/db/migrations/20240423150332_add_suffix_and_middle_name_to_onerep_profile_table.js +++ b/src/db/migrations/20240423150332_add_suffix_and_middle_name_to_onerep_profile_table.js @@ -8,11 +8,13 @@ */ export function up(knex) { return knex.schema.table("onerep_profiles", (table) => { - // @ts-ignore TODO: Determine if the following line should be changed - // or remain unchanged as initially deployed. + // @ts-ignore TODO: The type Knex.CreateTableBuilder is missing `varchar`: + // https://github.com/knex/knex/blob/master/types/index.d.ts#L2420 + // The method is implemented, but the method `string` is recommended. table.varchar("name_suffix").after("onerep_profile_id"); - // @ts-ignore TODO: Determine if the following line should be changed - // or remain unchanged as initially deployed. + // @ts-ignore TODO: The type Knex.CreateTableBuilder is missing `varchar`: + // https://github.com/knex/knex/blob/master/types/index.d.ts#L2420 + // The method is implemented, but the method `string` is recommended. table.varchar("middle_name").after("first_name"); }); } From e26b088252410b8b7cd9ed01fbb4de1007709a17 Mon Sep 17 00:00:00 2001 From: Florian Zia Date: Thu, 13 Feb 2025 16:21:08 +0100 Subject: [PATCH 13/13] chore: Update ts-ignore comment in src/scripts/loadtest/hibp.js --- src/scripts/loadtest/hibp.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scripts/loadtest/hibp.js b/src/scripts/loadtest/hibp.js index 337b9e20b36..4ed7be27f9c 100644 --- a/src/scripts/loadtest/hibp.js +++ b/src/scripts/loadtest/hibp.js @@ -66,12 +66,12 @@ export const run = () => { try { const result = res.json(); - // @ts-ignore TODO: Add type for result. + // @ts-ignore TODO: Add `PostHibpNotificationResponseBody` type to `src/app/api/v1/hibp/notify/route`, and use it. if (result.success !== true) { throw new Error(`Non-success result: ${JSON.stringify(result)}`); } } catch { - // @ts-ignore TODO: Add type for result. + // @ts-ignore TODO: Add type correct type for res. throw new Error(`Failed to parse result: ${res.status}, ${res.text}`); } };