diff --git a/services/platform-service/scripts/gen-module.ts b/services/platform-service/scripts/gen-module.ts index 147e4cc3..9ffd234f 100644 --- a/services/platform-service/scripts/gen-module.ts +++ b/services/platform-service/scripts/gen-module.ts @@ -16,6 +16,8 @@ * @module platform-service/scripts/gen-module */ +/* eslint-disable no-console -- This generator is a CLI; console output is its user interface. */ + import { promises as fs } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; diff --git a/services/platform-service/scripts/migrate-referrals.ts b/services/platform-service/scripts/migrate-referrals.ts index 9a60f70e..03cb5307 100644 --- a/services/platform-service/scripts/migrate-referrals.ts +++ b/services/platform-service/scripts/migrate-referrals.ts @@ -18,10 +18,15 @@ * npx tsx scripts/migrate-referrals.ts --mode new-only */ +/* eslint-disable no-console -- This migration script is a CLI; console output is its progress and summary UI. */ + import { CosmosClient, type Container } from '@azure/cosmos'; import { config } from '../src/lib/config.js'; import type { ReferralDoc } from '../src/modules/referrals/types.js'; +// TODO(migration-cli): Defer service config loading so `--help` can run without +// requiring COSMOS_ENDPOINT, COSMOS_KEY, and JWT_SECRET in the environment. + interface MigrationOptions { productId?: string; batchSize: number; @@ -30,6 +35,16 @@ interface MigrationOptions { dryRun: boolean; } +function getErrorCode(error: unknown): number | undefined { + return typeof error === 'object' && error !== null && 'code' in error + ? Number((error as { code: unknown }).code) + : undefined; +} + +function getErrorMessage(error: unknown): string { + return error instanceof Error ? error.message : String(error); +} + function parseArgs(): MigrationOptions { const args = process.argv.slice(2); const options: MigrationOptions = { @@ -150,11 +165,11 @@ async function backfillProduct( }) .fetchAll(); - const existingIds = new Set(existingDocs.map((d) => d.id)); + const existingIds = new Set(existingDocs.map(d => d.id)); console.log(`[${productId}] ${existingIds.size} documents already in new container`); // Filter docs needing migration - const toMigrate = oldDocs.filter((d) => !existingIds.has(d.id)); + const toMigrate = oldDocs.filter(d => !existingIds.has(d.id)); console.log(`[${productId}] ${toMigrate.length} documents need migration`); if (toMigrate.length === 0) { @@ -172,7 +187,7 @@ async function backfillProduct( ); await Promise.all( - batch.map(async (doc) => { + batch.map(async doc => { try { if (!doc.referrerId) { result.errors.push(`Doc ${doc.id}: missing referrerId (required for new PK)`); @@ -181,11 +196,11 @@ async function backfillProduct( await newContainer.items.create(doc); result.migrated++; - } catch (err: any) { - if (err.code === 409) { + } catch (err: unknown) { + if (getErrorCode(err) === 409) { result.skipped++; } else { - result.errors.push(`Doc ${doc.id}: ${err.message}`); + result.errors.push(`Doc ${doc.id}: ${getErrorMessage(err)}`); } } }) @@ -217,8 +232,8 @@ async function verifyConsistency( .fetchAll(), ]); - const oldMap = new Map(oldDocs.map((d) => [d.id, d])); - const newMap = new Map(newDocs.map((d) => [d.id, d])); + const oldMap = new Map(oldDocs.map(d => [d.id, d])); + const newMap = new Map(newDocs.map(d => [d.id, d])); // Check docs in both containers for consistency for (const [id, newDoc] of newMap) { @@ -298,7 +313,7 @@ async function main(): Promise { console.log(` Skipped: ${result.skipped}`); if (result.errors.length > 0) { console.log(` Errors: ${result.errors.length}`); - result.errors.slice(0, 5).forEach((e) => console.log(` - ${e}`)); + result.errors.slice(0, 5).forEach(e => console.log(` - ${e}`)); if (result.errors.length > 5) { console.log(` ... and ${result.errors.length - 5} more`); } @@ -313,14 +328,16 @@ async function main(): Promise { console.log(`\n Verifying consistency...`); const verifyResult = await verifyConsistency(oldContainer, newContainer, productId); const realInconsistencies = verifyResult.inconsistencies.filter( - (i) => !i.issue.includes('pending backfill') + i => !i.issue.includes('pending backfill') ); if (realInconsistencies.length === 0) { - console.log(` Consistency check passed (pending: ${verifyResult.inconsistencies.filter(i => i.issue.includes('pending backfill')).length})`); + console.log( + ` Consistency check passed (pending: ${verifyResult.inconsistencies.filter(i => i.issue.includes('pending backfill')).length})` + ); } else { console.log(` WARNING: ${realInconsistencies.length} inconsistencies found:`); - realInconsistencies.slice(0, 5).forEach((i) => console.log(` - ${i.id}: ${i.issue}`)); + realInconsistencies.slice(0, 5).forEach(i => console.log(` - ${i.id}: ${i.issue}`)); } } } @@ -345,7 +362,7 @@ async function main(): Promise { console.log('3. Monitor for issues, then delete old container when confident'); } -main().catch((err) => { +main().catch(err => { console.error('Migration failed:', err); process.exit(1); });