diff --git a/src/commands/pull.ts b/src/commands/pull.ts index 46c0196..54349cb 100644 --- a/src/commands/pull.ts +++ b/src/commands/pull.ts @@ -8,7 +8,6 @@ import { fetchCloudApp, FirestoreClientError, type CloudApp, - type FirestoreClientOptions, } from '../cloud/firestoreClient.js'; import { collectAppFiles } from '../core/appCollector.js'; import { ArtifactProps, type ArtifactProp } from '../core/artifacts.js'; @@ -18,7 +17,7 @@ import { getValidAuthSession } from '../auth/session.js'; import { withSpinner } from '../lib/spinner.js'; import { applyCloudStateToFs } from '../core/applyToFs.js'; import { type RootManifest } from '../core/manifest.js'; -import { writeVerboseJson } from '../core/debugFiles.js'; +import { createFirestoreDebugOptions, writeVerboseJson } from '../core/debugFiles.js'; import { computePullPlan, type PullSummary } from '../core/sync.js'; import { applyCloudAssetsToFs, buildEnvConfigForCloudAssets } from '../core/pullAssets.js'; import { ui } from '../core/ui.js'; @@ -164,45 +163,7 @@ export async function pullCommand(options: PullOptions = {}): Promise { } const { idToken, userId } = session; - const debugEnabled = verbose; - const firestoreOptions: FirestoreClientOptions | undefined = debugEnabled - ? { - debug: (event) => { - // eslint-disable-next-line no-console - console.log( - `[debug:firestore] ${event.kind}`, - JSON.stringify( - { - ...(event.kind === 'request' && { - method: event.method, - url: event.url, - context: event.context, - }), - ...(event.kind === 'response' && { - method: event.method, - url: event.url, - status: event.status, - context: event.context, - }), - ...(event.kind === 'list_documents' && { - collection: event.collection, - parentPath: event.parentPath, - count: event.count, - }), - ...(event.kind === 'push_operation' && { - appId: event.appId, - operation: event.operation, - artifactKind: event.artifactKind, - documentId: event.documentId, - }), - }, - null, - 2 - ) - ); - }, - } - : undefined; + const firestoreOptions = verbose ? createFirestoreDebugOptions() : undefined; const manifestPath = path.join(projectRoot, '.manifest.json'); const readManifest = (): Promise => diff --git a/src/commands/push.ts b/src/commands/push.ts index 5127f6d..4305fa6 100644 --- a/src/commands/push.ts +++ b/src/commands/push.ts @@ -7,7 +7,6 @@ import { fetchCloudApp, submitCliPush, FirestoreClientError, - type FirestoreClientOptions, type CloudApp, } from '../cloud/firestoreClient.js'; import { buildDocumentsFromParsed } from '../core/buildDocuments.js'; @@ -18,7 +17,7 @@ import { resolveVerboseFlag } from '../core/cliError.js'; import { resolveAppContext } from '../config/projectConfig.js'; import { getValidAuthSession } from '../auth/session.js'; import { withSpinner } from '../lib/spinner.js'; -import { writeVerboseJson } from '../core/debugFiles.js'; +import { createFirestoreDebugOptions, writeVerboseJson } from '../core/debugFiles.js'; import { computePushPlan, type PushSummary, type PushCounts } from '../core/sync.js'; import { buildAndWriteManifest } from '../core/manifest.js'; import { ui } from '../core/ui.js'; @@ -207,45 +206,7 @@ export async function pushCommand(options: PushOptions = {}): Promise { } const { idToken, userId } = session; - const debugEnabled = verbose; - const firestoreOptions: FirestoreClientOptions | undefined = debugEnabled - ? { - debug: (event) => { - // eslint-disable-next-line no-console - console.log( - `[debug:firestore] ${event.kind}`, - JSON.stringify( - { - ...(event.kind === 'request' && { - method: event.method, - url: event.url, - context: event.context, - }), - ...(event.kind === 'response' && { - method: event.method, - url: event.url, - status: event.status, - context: event.context, - }), - ...(event.kind === 'list_documents' && { - collection: event.collection, - parentPath: event.parentPath, - count: event.count, - }), - ...(event.kind === 'push_operation' && { - appId: event.appId, - operation: event.operation, - artifactKind: event.artifactKind, - documentId: event.documentId, - }), - }, - null, - 2 - ) - ); - }, - } - : undefined; + const firestoreOptions = verbose ? createFirestoreDebugOptions() : undefined; const [access, dataWithLang, cloudAppResult] = await withSpinner( 'Preparing app for push...', diff --git a/src/core/debugFiles.ts b/src/core/debugFiles.ts index 8bbb686..19c4b9d 100644 --- a/src/core/debugFiles.ts +++ b/src/core/debugFiles.ts @@ -1,6 +1,19 @@ import fs from 'fs/promises'; import path from 'path'; +import type { FirestoreClientOptions, FirestoreDebugEvent } from '../cloud/firestoreClient.js'; + +export function createFirestoreDebugOptions(): FirestoreClientOptions { + return { debug: logFirestoreDebugEvent }; +} + +function logFirestoreDebugEvent(event: FirestoreDebugEvent): void { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { kind, url: _omit, ...payload } = event as FirestoreDebugEvent & { url: never }; + // eslint-disable-next-line no-console + console.log(`[debug:firestore] ${kind}`, JSON.stringify(payload, null, 2)); +} + interface WriteVerboseJsonOptions { verbose: boolean; truncateLargeContent?: boolean; diff --git a/tests/core/debugFiles.test.ts b/tests/core/debugFiles.test.ts new file mode 100644 index 0000000..ff29351 --- /dev/null +++ b/tests/core/debugFiles.test.ts @@ -0,0 +1,43 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; + +import { createFirestoreDebugOptions } from '../../src/core/debugFiles.js'; + +describe('createFirestoreDebugOptions', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('logs firestore debug events without api urls', () => { + const lines: string[] = []; + vi.spyOn(console, 'log').mockImplementation((...args: unknown[]) => { + lines.push(args.map(String).join(' ')); + }); + + const { debug } = createFirestoreDebugOptions(); + debug?.({ + kind: 'request', + method: 'GET', + url: 'https://firestore.googleapis.com/v1/projects/p/databases/(default)/documents/apps/app1/artifacts?pageToken=secret', + context: 'listCollectionDocuments', + }); + debug?.({ + kind: 'response', + method: 'GET', + url: 'https://firestore.googleapis.com/v1/projects/p/databases/(default)/documents/apps/app1/artifacts', + status: 200, + context: 'listCollectionDocuments', + }); + debug?.({ + kind: 'list_documents', + collection: 'artifacts', + parentPath: 'apps/app1', + count: 97, + }); + + const output = lines.join('\n'); + expect(output).toContain('listCollectionDocuments'); + expect(output).toContain('"count": 97'); + expect(output).not.toContain('firestore.googleapis.com'); + expect(output).not.toContain('pageToken'); + }); +});