diff --git a/CLAUDE.md b/CLAUDE.md index d5fe768..3ab8037 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -56,6 +56,7 @@ This project is structured as follows: - **Type Safety**: Never use non-null assertions (`!`). Always write type-safe code. Non-null assertions are only allowed in test files. - **Zod Coercion for CLI Options**: Always use `z.coerce.number()` (not `z.number()`) for numeric CLI options, since CLI arguments are parsed as strings. - **`--json` Output**: Commands that return data take a `json` option (`z.boolean().optional().describe('Output in JSON format.')`) and print `console.log(JSON.stringify(data, null, 2))` with the resource's own field names (`{ id }`). Commands with progress output (spinners, step logs) keep that output and just append the JSON, since progress is useful in CI/CD. Commands without progress output use `if (json) { ...JSON... } else { ...consola... }` so `--json` stays clean. +- **URL Field Naming**: JSON output fields that point to a page in a web UI (e.g. the Capawesome Cloud Console) are named `webUrl` — never `url` or `consoleUrl`. Nested resources use the full entity name as the key (e.g. `appBuildShare`, not `share`). ## Philosophy diff --git a/src/commands/apps/builds/create.test.ts b/src/commands/apps/builds/create.test.ts index b139f09..664313d 100644 --- a/src/commands/apps/builds/create.test.ts +++ b/src/commands/apps/builds/create.test.ts @@ -85,14 +85,14 @@ describe('apps-builds-create', () => { expect(findScope.isDone()).toBe(true); expect(shareScope.isDone()).toBe(true); - const url = `${DEFAULT_CONSOLE_BASE_URL}/app-build-shares/${shareId}`; - const qrCodeUrl = `${DEFAULT_API_BASE_URL}/v1/qrcodes?content=${encodeURIComponent(url)}&format=png`; + const webUrl = `${DEFAULT_CONSOLE_BASE_URL}/app-build-shares/${shareId}`; + const qrCodeUrl = `${DEFAULT_API_BASE_URL}/v1/qrcodes?content=${encodeURIComponent(webUrl)}&format=png`; expect(console.log).toHaveBeenCalledWith( JSON.stringify( { id: buildId, numberAsString: '42', - share: { id: shareId, url, qrCodeUrl, expiresAt: null }, + appBuildShare: { id: shareId, qrCodeUrl, webUrl, expiresAt: null }, }, null, 2, diff --git a/src/commands/apps/builds/create.ts b/src/commands/apps/builds/create.ts index 4581fa4..8287fa0 100644 --- a/src/commands/apps/builds/create.ts +++ b/src/commands/apps/builds/create.ts @@ -455,7 +455,7 @@ export default defineCommand({ }); } // Create a public share link if requested - let share: { id: string; url: string; qrCodeUrl: string; expiresAt: string | null } | undefined; + let appBuildShare: { id: string; qrCodeUrl: string; webUrl: string; expiresAt: string | null } | undefined; if (options.share) { const expiresAt = options.shareExpiresInDays !== undefined @@ -468,15 +468,15 @@ export default defineCommand({ expiresAt, }); const shareUrls = await getAppBuildShareUrls(createdShare.id); - share = { + appBuildShare = { id: createdShare.id, - url: shareUrls.url, qrCodeUrl: shareUrls.qrCodeUrl, + webUrl: shareUrls.webUrl, expiresAt: createdShare.expiresAt, }; if (!json) { consola.success('Build shared successfully.'); - consola.info(`Share URL: ${shareUrls.url}`); + consola.info(`Share URL: ${shareUrls.webUrl}`); } } @@ -497,7 +497,7 @@ export default defineCommand({ { id: response.id, numberAsString: response.numberAsString, - ...(share ? { share } : {}), + ...(appBuildShare ? { appBuildShare } : {}), }, null, 2, diff --git a/src/commands/apps/builds/share.test.ts b/src/commands/apps/builds/share.test.ts index de0db48..d5535bc 100644 --- a/src/commands/apps/builds/share.test.ts +++ b/src/commands/apps/builds/share.test.ts @@ -72,9 +72,11 @@ describe('apps-builds-share', () => { expect(buildScope.isDone()).toBe(true); expect(shareScope.isDone()).toBe(true); - const url = `${DEFAULT_CONSOLE_BASE_URL}/app-build-shares/${shareId}`; - const qrCodeUrl = `${DEFAULT_API_BASE_URL}/v1/qrcodes?content=${encodeURIComponent(url)}&format=png`; - expect(console.log).toHaveBeenCalledWith(JSON.stringify({ id: shareId, url, qrCodeUrl, expiresAt: null }, null, 2)); + const webUrl = `${DEFAULT_CONSOLE_BASE_URL}/app-build-shares/${shareId}`; + const qrCodeUrl = `${DEFAULT_API_BASE_URL}/v1/qrcodes?content=${encodeURIComponent(webUrl)}&format=png`; + expect(console.log).toHaveBeenCalledWith( + JSON.stringify({ id: shareId, qrCodeUrl, webUrl, expiresAt: null }, null, 2), + ); expect(mockConsola.success).not.toHaveBeenCalled(); }); diff --git a/src/commands/apps/builds/share.ts b/src/commands/apps/builds/share.ts index 3f742b5..e323aba 100644 --- a/src/commands/apps/builds/share.ts +++ b/src/commands/apps/builds/share.ts @@ -99,13 +99,13 @@ export default defineCommand({ : undefined; const share = await appBuildsService.createShare({ appId, appBuildId: buildId, description, expiresAt }); - const { url, qrCodeUrl } = await getAppBuildShareUrls(share.id); + const { qrCodeUrl, webUrl } = await getAppBuildShareUrls(share.id); if (json) { - console.log(JSON.stringify({ id: share.id, url, qrCodeUrl, expiresAt: share.expiresAt }, null, 2)); + console.log(JSON.stringify({ id: share.id, qrCodeUrl, webUrl, expiresAt: share.expiresAt }, null, 2)); } else { consola.success('Build shared successfully.'); - consola.info(`Share URL: ${url}`); + consola.info(`Share URL: ${webUrl}`); consola.info(`QR Code URL: ${qrCodeUrl}`); if (share.expiresAt) { consola.info(`Expires At: ${share.expiresAt}`); diff --git a/src/utils/app-build-shares.ts b/src/utils/app-build-shares.ts index a2fd711..3998dd7 100644 --- a/src/utils/app-build-shares.ts +++ b/src/utils/app-build-shares.ts @@ -1,8 +1,8 @@ import configService from '@/services/config.js'; export interface AppBuildShareUrls { - url: string; qrCodeUrl: string; + webUrl: string; } /** @@ -11,7 +11,7 @@ export interface AppBuildShareUrls { export const getAppBuildShareUrls = async (shareId: string): Promise => { const consoleBaseUrl = await configService.getValueForKey('CONSOLE_BASE_URL'); const apiBaseUrl = await configService.getValueForKey('API_BASE_URL'); - const url = `${consoleBaseUrl}/app-build-shares/${shareId}`; - const qrCodeUrl = `${apiBaseUrl}/v1/qrcodes?content=${encodeURIComponent(url)}&format=png`; - return { url, qrCodeUrl }; + const webUrl = `${consoleBaseUrl}/app-build-shares/${shareId}`; + const qrCodeUrl = `${apiBaseUrl}/v1/qrcodes?content=${encodeURIComponent(webUrl)}&format=png`; + return { qrCodeUrl, webUrl }; };