From 36b45543d1c14e790c7878e3a067737f86aed591 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Sat, 11 Apr 2026 00:03:32 +0000 Subject: [PATCH 1/7] fix: show friendly error when repo is not found Catch `NotFoundRequestError` in all commands that call repo-scoped API endpoints and display "Repository not found: " instead of an unhandled stack trace. Closes #118 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/commands/locale-add.ts | 5 ++- src/commands/locale-list.ts | 17 ++++++++- src/commands/locale-remove.ts | 5 ++- src/commands/locale-set-master.ts | 27 ++++++++------ src/commands/preview-add.ts | 5 ++- src/commands/preview-list.ts | 24 +++++++++--- src/commands/preview-remove.ts | 17 +++++---- src/commands/preview-set-simulator.ts | 5 ++- src/commands/repo-create.ts | 5 ++- src/commands/repo-set-api-access.ts | 5 ++- src/commands/repo-set-name.ts | 5 ++- src/commands/repo-view.ts | 5 ++- src/commands/sync.ts | 30 ++++++++++----- src/commands/token-create.ts | 34 +++++++++++------ src/commands/token-delete.ts | 54 ++++++++++++++++----------- src/commands/token-list.ts | 24 +++++++++--- src/commands/webhook-create.ts | 5 ++- src/commands/webhook-disable.ts | 31 ++++++++------- src/commands/webhook-enable.ts | 31 ++++++++------- src/commands/webhook-list.ts | 17 ++++++++- src/commands/webhook-remove.ts | 19 ++++++---- src/commands/webhook-set-triggers.ts | 19 ++++++---- src/commands/webhook-view.ts | 15 +++++++- 23 files changed, 276 insertions(+), 128 deletions(-) diff --git a/src/commands/locale-add.ts b/src/commands/locale-add.ts index 3220aac..d094316 100644 --- a/src/commands/locale-add.ts +++ b/src/commands/locale-add.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { upsertLocale } from "../clients/locale"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -32,6 +32,9 @@ export default createCommand(config, async ({ positionals, values }) => { try { await upsertLocale({ id: code, isMaster: master, customName: name }, { repo, token, host }); } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to add locale: ${message}`); diff --git a/src/commands/locale-list.ts b/src/commands/locale-list.ts index 64b63c8..9248dd4 100644 --- a/src/commands/locale-list.ts +++ b/src/commands/locale-list.ts @@ -1,7 +1,8 @@ import { getHost, getToken } from "../auth"; import { getLocales } from "../clients/locale"; -import { createCommand, type CommandConfig } from "../lib/command"; +import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -24,7 +25,19 @@ export default createCommand(config, async ({ values }) => { const token = await getToken(); const host = await getHost(); - const locales = await getLocales({ repo, token, host }); + let locales; + try { + locales = await getLocales({ repo, token, host }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to list locales: ${message}`); + } + throw error; + } if (json) { console.info(stringify(locales)); diff --git a/src/commands/locale-remove.ts b/src/commands/locale-remove.ts index 2f45626..16c9697 100644 --- a/src/commands/locale-remove.ts +++ b/src/commands/locale-remove.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { removeLocale } from "../clients/locale"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -30,6 +30,9 @@ export default createCommand(config, async ({ positionals, values }) => { try { await removeLocale(code, { repo, token, host }); } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to remove locale: ${message}`); diff --git a/src/commands/locale-set-master.ts b/src/commands/locale-set-master.ts index 49af790..0357cd8 100644 --- a/src/commands/locale-set-master.ts +++ b/src/commands/locale-set-master.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { getLocales, upsertLocale } from "../clients/locale"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -27,25 +27,28 @@ export default createCommand(config, async ({ positionals, values }) => { const token = await getToken(); const host = await getHost(); - const locales = await getLocales({ repo, token, host }); - const locale = locales.find((l) => l.id === code); + try { + const locales = await getLocales({ repo, token, host }); + const locale = locales.find((l) => l.id === code); - if (!locale) { - throw new CommandError( - `Locale "${code}" not found. Available locales: ${locales.map((l) => l.id).join(", ")}`, - ); - } + if (!locale) { + throw new CommandError( + `Locale "${code}" not found. Available locales: ${locales.map((l) => l.id).join(", ")}`, + ); + } - if (locale.isMaster) { - throw new CommandError(`Locale "${code}" is already the master.`); - } + if (locale.isMaster) { + throw new CommandError(`Locale "${code}" is already the master.`); + } - try { await upsertLocale( { id: locale.id, isMaster: true, customName: locale.customName ?? undefined }, { repo, token, host }, ); } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to set master locale: ${message}`); diff --git a/src/commands/preview-add.ts b/src/commands/preview-add.ts index e4cf7bd..5878298 100644 --- a/src/commands/preview-add.ts +++ b/src/commands/preview-add.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { addPreview } from "../clients/core"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -42,6 +42,9 @@ export default createCommand(config, async ({ positionals, values }) => { try { await addPreview({ name: displayName, websiteURL, resolverPath }, { repo, token, host }); } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to add preview: ${message}`); diff --git a/src/commands/preview-list.ts b/src/commands/preview-list.ts index 7744489..190e850 100644 --- a/src/commands/preview-list.ts +++ b/src/commands/preview-list.ts @@ -1,7 +1,8 @@ import { getHost, getToken } from "../auth"; import { getPreviews, getSimulatorUrl } from "../clients/core"; -import { createCommand, type CommandConfig } from "../lib/command"; +import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -24,10 +25,23 @@ export default createCommand(config, async ({ values }) => { const token = await getToken(); const host = await getHost(); - const [previews, simulatorUrl] = await Promise.all([ - getPreviews({ repo, token, host }), - getSimulatorUrl({ repo, token, host }), - ]); + let previews; + let simulatorUrl; + try { + [previews, simulatorUrl] = await Promise.all([ + getPreviews({ repo, token, host }), + getSimulatorUrl({ repo, token, host }), + ]); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to list previews: ${message}`); + } + throw error; + } if (json) { console.info( diff --git a/src/commands/preview-remove.ts b/src/commands/preview-remove.ts index 7b95c6a..bea2e18 100644 --- a/src/commands/preview-remove.ts +++ b/src/commands/preview-remove.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { getPreviews, removePreview } from "../clients/core"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -27,15 +27,18 @@ export default createCommand(config, async ({ positionals, values }) => { const token = await getToken(); const host = await getHost(); - const previews = await getPreviews({ repo, token, host }); - const preview = previews.find((p) => p.url === previewUrl); - if (!preview) { - throw new CommandError(`Preview not found: ${previewUrl}`); - } - try { + const previews = await getPreviews({ repo, token, host }); + const preview = previews.find((p) => p.url === previewUrl); + if (!preview) { + throw new CommandError(`Preview not found: ${previewUrl}`); + } + await removePreview(preview.id, { repo, token, host }); } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to remove preview: ${message}`); diff --git a/src/commands/preview-set-simulator.ts b/src/commands/preview-set-simulator.ts index f61eb15..6e4bc9e 100644 --- a/src/commands/preview-set-simulator.ts +++ b/src/commands/preview-set-simulator.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { setSimulatorUrl } from "../clients/core"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -45,6 +45,9 @@ export default createCommand(config, async ({ positionals, values }) => { try { await setSimulatorUrl(simulatorUrl, { repo, token, host }); } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to set simulator URL: ${message}`); diff --git a/src/commands/repo-create.ts b/src/commands/repo-create.ts index d0ea8f8..9f63028 100644 --- a/src/commands/repo-create.ts +++ b/src/commands/repo-create.ts @@ -2,7 +2,7 @@ import { getAdapter } from "../adapters"; import { getHost, getToken } from "../auth"; import { checkIsDomainAvailable, createRepository } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; const MAX_DOMAIN_TRIES = 5; @@ -31,6 +31,9 @@ export default createCommand(config, async ({ values }) => { try { await createRepository({ domain, name: name ?? domain, framework, token, host }); } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${domain}`); + } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to create repository: ${message}`); diff --git a/src/commands/repo-set-api-access.ts b/src/commands/repo-set-api-access.ts index afe8103..30bc271 100644 --- a/src/commands/repo-set-api-access.ts +++ b/src/commands/repo-set-api-access.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { type RepositoryAccessLevel, setRepositoryAccess } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const VALID_LEVELS: RepositoryAccessLevel[] = ["private", "public", "open"]; @@ -38,6 +38,9 @@ export default createCommand(config, async ({ positionals, values }) => { try { await setRepositoryAccess(level as RepositoryAccessLevel, { repo, token, host }); } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to set repository access: ${message}`); diff --git a/src/commands/repo-set-name.ts b/src/commands/repo-set-name.ts index 1987b53..d2e5928 100644 --- a/src/commands/repo-set-name.ts +++ b/src/commands/repo-set-name.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { setRepositoryName } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -31,6 +31,9 @@ export default createCommand(config, async ({ positionals, values }) => { try { confirmedName = await setRepositoryName(displayName, { repo, token, host }); } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to set repository name: ${message}`); diff --git a/src/commands/repo-view.ts b/src/commands/repo-view.ts index 85051e1..b4fbf52 100644 --- a/src/commands/repo-view.ts +++ b/src/commands/repo-view.ts @@ -4,7 +4,7 @@ import { getProfile } from "../clients/user"; import { getRepositoryAccess } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; -import { UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -43,6 +43,9 @@ export default createCommand(config, async ({ values }) => { getRepositoryAccess({ repo, token, host }), ]); } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to fetch repository details: ${message}`); diff --git a/src/commands/sync.ts b/src/commands/sync.ts index 4f9df13..43caa3f 100644 --- a/src/commands/sync.ts +++ b/src/commands/sync.ts @@ -5,7 +5,8 @@ import { getAdapter, type Adapter } from "../adapters"; import { getHost, getToken } from "../auth"; import { getCustomTypes, getSlices } from "../clients/custom-types"; import { env } from "../env"; -import { createCommand, type CommandConfig } from "../lib/command"; +import { CommandError, createCommand, type CommandConfig } from "../lib/command"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { segmentTrackEnd, segmentTrackStart } from "../lib/segment"; import { dedent } from "../lib/string"; import { checkIsTypeBuilderEnabled, getRepositoryName, TypeBuilderRequiredError } from "../project"; @@ -45,15 +46,26 @@ export default createCommand(config, async ({ values }) => { segmentTrackStart("sync", { watch }); - if (watch) { - await watchForChanges(repo, adapter); - } else { - await syncSlices(repo, adapter); - await syncCustomTypes(repo, adapter); - await adapter.generateTypes(); - segmentTrackEnd("sync", { watch }); + try { + if (watch) { + await watchForChanges(repo, adapter); + } else { + await syncSlices(repo, adapter); + await syncCustomTypes(repo, adapter); + await adapter.generateTypes(); + segmentTrackEnd("sync", { watch }); - console.info("Sync complete"); + console.info("Sync complete"); + } + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to sync: ${message}`); + } + throw error; } }); diff --git a/src/commands/token-create.ts b/src/commands/token-create.ts index 6c33a00..8a6f2d2 100644 --- a/src/commands/token-create.ts +++ b/src/commands/token-create.ts @@ -6,6 +6,7 @@ import { getOAuthApps, } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const CLI_APP_NAME = "Prismic CLI"; @@ -38,18 +39,29 @@ export default createCommand(config, async ({ values }) => { const token = await getToken(); const host = await getHost(); - if (write) { - const writeToken = await createWriteToken(CLI_APP_NAME, { repo, token, host }); - console.info(`Token created: ${writeToken.token}`); - } else { - const scope = allowReleases ? "master+releases" : "master"; + try { + if (write) { + const writeToken = await createWriteToken(CLI_APP_NAME, { repo, token, host }); + console.info(`Token created: ${writeToken.token}`); + } else { + const scope = allowReleases ? "master+releases" : "master"; - // Find or create the CLI OAuth app. - const apps = await getOAuthApps({ repo, token, host }); - let app = apps.find((a) => a.name === CLI_APP_NAME); - if (!app) app = await createOAuthApp(CLI_APP_NAME, { repo, token, host }); + // Find or create the CLI OAuth app. + const apps = await getOAuthApps({ repo, token, host }); + let app = apps.find((a) => a.name === CLI_APP_NAME); + if (!app) app = await createOAuthApp(CLI_APP_NAME, { repo, token, host }); - const accessToken = await createOAuthAuthorization(app.id, scope, { repo, token, host }); - console.info(`Token created: ${accessToken.token}`); + const accessToken = await createOAuthAuthorization(app.id, scope, { repo, token, host }); + console.info(`Token created: ${accessToken.token}`); + } + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to create token: ${message}`); + } + throw error; } }); diff --git a/src/commands/token-delete.ts b/src/commands/token-delete.ts index 20f8682..7782a7a 100644 --- a/src/commands/token-delete.ts +++ b/src/commands/token-delete.ts @@ -6,6 +6,7 @@ import { getWriteTokens, } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -31,27 +32,38 @@ export default createCommand(config, async ({ positionals, values }) => { const token = await getToken(); const host = await getHost(); - const [apps, writeTokensInfo] = await Promise.all([ - getOAuthApps({ repo, token, host }), - getWriteTokens({ repo, token, host }), - ]); - - // Search access tokens - const accessTokenAuths = apps.flatMap((app) => app.wroom_auths); - const accessToken = accessTokenAuths.find((auth) => auth.token === tokenValue); - if (accessToken) { - await deleteOAuthAuthorization(accessToken.id, { repo, token, host }); - console.info("Token deleted"); - return; - } + try { + const [apps, writeTokensInfo] = await Promise.all([ + getOAuthApps({ repo, token, host }), + getWriteTokens({ repo, token, host }), + ]); - // Search write tokens - const writeToken = writeTokensInfo.tokens.find((t) => t.token === tokenValue); - if (writeToken) { - await deleteWriteToken(writeToken.token, { repo, token, host }); - console.info("Token deleted"); - return; - } + // Search access tokens + const accessTokenAuths = apps.flatMap((app) => app.wroom_auths); + const accessToken = accessTokenAuths.find((auth) => auth.token === tokenValue); + if (accessToken) { + await deleteOAuthAuthorization(accessToken.id, { repo, token, host }); + console.info("Token deleted"); + return; + } - throw new CommandError(`Token not found: ${tokenValue}`); + // Search write tokens + const writeToken = writeTokensInfo.tokens.find((t) => t.token === tokenValue); + if (writeToken) { + await deleteWriteToken(writeToken.token, { repo, token, host }); + console.info("Token deleted"); + return; + } + + throw new CommandError(`Token not found: ${tokenValue}`); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to delete token: ${message}`); + } + throw error; + } }); diff --git a/src/commands/token-list.ts b/src/commands/token-list.ts index 102dd11..f18c0f9 100644 --- a/src/commands/token-list.ts +++ b/src/commands/token-list.ts @@ -1,7 +1,8 @@ import { getHost, getToken } from "../auth"; import { getOAuthApps, getWriteTokens } from "../clients/wroom"; -import { createCommand, type CommandConfig } from "../lib/command"; +import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -24,10 +25,23 @@ export default createCommand(config, async ({ values }) => { const token = await getToken(); const host = await getHost(); - const [apps, writeTokensInfo] = await Promise.all([ - getOAuthApps({ repo, token, host }), - getWriteTokens({ repo, token, host }), - ]); + let apps; + let writeTokensInfo; + try { + [apps, writeTokensInfo] = await Promise.all([ + getOAuthApps({ repo, token, host }), + getWriteTokens({ repo, token, host }), + ]); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to list tokens: ${message}`); + } + throw error; + } const accessTokens = apps.flatMap((app) => app.wroom_auths.map((auth) => ({ diff --git a/src/commands/webhook-create.ts b/src/commands/webhook-create.ts index 19c15f8..29f657c 100644 --- a/src/commands/webhook-create.ts +++ b/src/commands/webhook-create.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { createWebhook, WEBHOOK_TRIGGERS } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -74,6 +74,9 @@ export default createCommand(config, async ({ positionals, values }) => { { repo, token, host }, ); } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to create webhook: ${message}`); diff --git a/src/commands/webhook-disable.ts b/src/commands/webhook-disable.ts index 060c882..140b68e 100644 --- a/src/commands/webhook-disable.ts +++ b/src/commands/webhook-disable.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { getWebhooks, updateWebhook } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -26,25 +26,28 @@ export default createCommand(config, async ({ positionals, values }) => { const token = await getToken(); const host = await getHost(); - const webhooks = await getWebhooks({ repo, token, host }); - const webhook = webhooks.find((w) => w.config.url === webhookUrl); - if (!webhook) { - throw new CommandError(`Webhook not found: ${webhookUrl}`); - } + try { + const webhooks = await getWebhooks({ repo, token, host }); + const webhook = webhooks.find((w) => w.config.url === webhookUrl); + if (!webhook) { + throw new CommandError(`Webhook not found: ${webhookUrl}`); + } - if (!webhook.config.active) { - console.info(`Webhook already disabled: ${webhookUrl}`); - return; - } + if (!webhook.config.active) { + console.info(`Webhook already disabled: ${webhookUrl}`); + return; + } - const id = webhook.config._id; + const id = webhook.config._id; - const updatedConfig = structuredClone(webhook.config); - updatedConfig.active = false; + const updatedConfig = structuredClone(webhook.config); + updatedConfig.active = false; - try { await updateWebhook(id, updatedConfig, { repo, token, host }); } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to disable webhook: ${message}`); diff --git a/src/commands/webhook-enable.ts b/src/commands/webhook-enable.ts index e14a77e..4b1150b 100644 --- a/src/commands/webhook-enable.ts +++ b/src/commands/webhook-enable.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { getWebhooks, updateWebhook } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -26,25 +26,28 @@ export default createCommand(config, async ({ positionals, values }) => { const token = await getToken(); const host = await getHost(); - const webhooks = await getWebhooks({ repo, token, host }); - const webhook = webhooks.find((w) => w.config.url === webhookUrl); - if (!webhook) { - throw new CommandError(`Webhook not found: ${webhookUrl}`); - } + try { + const webhooks = await getWebhooks({ repo, token, host }); + const webhook = webhooks.find((w) => w.config.url === webhookUrl); + if (!webhook) { + throw new CommandError(`Webhook not found: ${webhookUrl}`); + } - if (webhook.config.active) { - console.info(`Webhook already enabled: ${webhookUrl}`); - return; - } + if (webhook.config.active) { + console.info(`Webhook already enabled: ${webhookUrl}`); + return; + } - const id = webhook.config._id; + const id = webhook.config._id; - const updatedConfig = structuredClone(webhook.config); - updatedConfig.active = true; + const updatedConfig = structuredClone(webhook.config); + updatedConfig.active = true; - try { await updateWebhook(id, updatedConfig, { repo, token, host }); } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to enable webhook: ${message}`); diff --git a/src/commands/webhook-list.ts b/src/commands/webhook-list.ts index 7f6266e..7b99202 100644 --- a/src/commands/webhook-list.ts +++ b/src/commands/webhook-list.ts @@ -1,7 +1,8 @@ import { getHost, getToken } from "../auth"; import { getWebhooks } from "../clients/wroom"; -import { createCommand, type CommandConfig } from "../lib/command"; +import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -23,7 +24,19 @@ export default createCommand(config, async ({ values }) => { const token = await getToken(); const host = await getHost(); - const webhooks = await getWebhooks({ repo, token, host }); + let webhooks; + try { + webhooks = await getWebhooks({ repo, token, host }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to list webhooks: ${message}`); + } + throw error; + } if (json) { console.info(stringify(webhooks.map((webhook) => webhook.config))); diff --git a/src/commands/webhook-remove.ts b/src/commands/webhook-remove.ts index 22c1b56..5e4bbf8 100644 --- a/src/commands/webhook-remove.ts +++ b/src/commands/webhook-remove.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { deleteWebhook, getWebhooks } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -26,17 +26,20 @@ export default createCommand(config, async ({ positionals, values }) => { const token = await getToken(); const host = await getHost(); - const webhooks = await getWebhooks({ repo, token, host }); - const webhook = webhooks.find((w) => w.config.url === webhookUrl); - if (!webhook) { - throw new CommandError(`Webhook not found: ${webhookUrl}`); - } + try { + const webhooks = await getWebhooks({ repo, token, host }); + const webhook = webhooks.find((w) => w.config.url === webhookUrl); + if (!webhook) { + throw new CommandError(`Webhook not found: ${webhookUrl}`); + } - const id = webhook.config._id; + const id = webhook.config._id; - try { await deleteWebhook(id, { repo, token, host }); } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to remove webhook: ${message}`); diff --git a/src/commands/webhook-set-triggers.ts b/src/commands/webhook-set-triggers.ts index bb278b0..f706c31 100644 --- a/src/commands/webhook-set-triggers.ts +++ b/src/commands/webhook-set-triggers.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { getWebhooks, updateWebhook, WEBHOOK_TRIGGERS } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -52,15 +52,15 @@ export default createCommand(config, async ({ positionals, values }) => { const token = await getToken(); const host = await getHost(); - const webhooks = await getWebhooks({ repo, token, host }); - const webhook = webhooks.find((w) => w.config.url === webhookUrl); - if (!webhook) { - throw new CommandError(`Webhook not found: ${webhookUrl}`); - } + try { + const webhooks = await getWebhooks({ repo, token, host }); + const webhook = webhooks.find((w) => w.config.url === webhookUrl); + if (!webhook) { + throw new CommandError(`Webhook not found: ${webhookUrl}`); + } - const id = webhook.config._id; + const id = webhook.config._id; - try { await updateWebhook( id, { @@ -75,6 +75,9 @@ export default createCommand(config, async ({ positionals, values }) => { { repo, token, host }, ); } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to update webhook triggers: ${message}`); diff --git a/src/commands/webhook-view.ts b/src/commands/webhook-view.ts index ec38e16..3aa15b4 100644 --- a/src/commands/webhook-view.ts +++ b/src/commands/webhook-view.ts @@ -1,6 +1,7 @@ import { getHost, getToken } from "../auth"; import { getWebhooks, WEBHOOK_TRIGGERS } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; +import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -25,7 +26,19 @@ export default createCommand(config, async ({ positionals, values }) => { const token = await getToken(); const host = await getHost(); - const webhooks = await getWebhooks({ repo, token, host }); + let webhooks; + try { + webhooks = await getWebhooks({ repo, token, host }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to fetch webhook details: ${message}`); + } + throw error; + } const webhook = webhooks.find((webhook) => webhook.config.url === webhookUrl); if (!webhook) { From 1bd77cf9307064960ee95996c85f61c2673eaf57 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Sat, 11 Apr 2026 00:08:08 +0000 Subject: [PATCH 2/7] fix: handle 404 from checkIsTypeBuilderEnabled in sync and init The `checkIsTypeBuilderEnabled` call and `syncSlices`/`syncCustomTypes` in `init.ts` were outside the try/catch, so a non-existent repo would still crash with a stack trace. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/commands/init.ts | 27 +++++++++++++++++++++++---- src/commands/sync.ts | 10 +++++++++- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/commands/init.ts b/src/commands/init.ts index 4a184bc..639d76b 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -16,7 +16,11 @@ import { DEFAULT_PRISMIC_HOST } from "../env"; import { openBrowser } from "../lib/browser"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { installDependencies } from "../lib/packageJson"; -import { ForbiddenRequestError, UnauthorizedRequestError } from "../lib/request"; +import { + ForbiddenRequestError, + NotFoundRequestError, + UnauthorizedRequestError, +} from "../lib/request"; import { checkIsTypeBuilderEnabled, TypeBuilderRequiredError } from "../project"; import { syncCustomTypes, syncSlices } from "./sync"; @@ -104,7 +108,15 @@ export default createCommand(config, async ({ values }) => { ); } - const isTypeBuilderEnabled = await checkIsTypeBuilderEnabled(repo, { token, host }); + let isTypeBuilderEnabled; + try { + isTypeBuilderEnabled = await checkIsTypeBuilderEnabled(repo, { token, host }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } + throw error; + } if (!isTypeBuilderEnabled) { throw new TypeBuilderRequiredError(); } @@ -151,8 +163,15 @@ export default createCommand(config, async ({ values }) => { } // Sync models from remote - await syncSlices(repo, adapter); - await syncCustomTypes(repo, adapter); + try { + await syncSlices(repo, adapter); + await syncCustomTypes(repo, adapter); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } + throw error; + } // Generate TypeScript types from synced models await adapter.generateTypes(); diff --git a/src/commands/sync.ts b/src/commands/sync.ts index 43caa3f..774000c 100644 --- a/src/commands/sync.ts +++ b/src/commands/sync.ts @@ -35,7 +35,15 @@ export default createCommand(config, async ({ values }) => { const token = await getToken(); const host = await getHost(); - const isTypeBuilderEnabled = await checkIsTypeBuilderEnabled(repo, { token, host }); + let isTypeBuilderEnabled; + try { + isTypeBuilderEnabled = await checkIsTypeBuilderEnabled(repo, { token, host }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } + throw error; + } if (!isTypeBuilderEnabled) { throw new TypeBuilderRequiredError(); } From a8741dad2028c66557658894f0ed546b338fa63f Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Sat, 11 Apr 2026 00:11:54 +0000 Subject: [PATCH 3/7] fix: don't assume 404 from syncSlices/syncCustomTypes means repo not found The checkIsTypeBuilderEnabled call already validates the repo exists before syncing. A 404 during sync would mean something else, so remove the incorrect NotFoundRequestError catch around those calls. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/commands/init.ts | 11 ++--------- src/commands/sync.ts | 31 ++++++++++--------------------- 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/src/commands/init.ts b/src/commands/init.ts index 639d76b..e93d834 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -163,15 +163,8 @@ export default createCommand(config, async ({ values }) => { } // Sync models from remote - try { - await syncSlices(repo, adapter); - await syncCustomTypes(repo, adapter); - } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } - throw error; - } + await syncSlices(repo, adapter); + await syncCustomTypes(repo, adapter); // Generate TypeScript types from synced models await adapter.generateTypes(); diff --git a/src/commands/sync.ts b/src/commands/sync.ts index 774000c..c39871e 100644 --- a/src/commands/sync.ts +++ b/src/commands/sync.ts @@ -6,7 +6,7 @@ import { getHost, getToken } from "../auth"; import { getCustomTypes, getSlices } from "../clients/custom-types"; import { env } from "../env"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { NotFoundRequestError } from "../lib/request"; import { segmentTrackEnd, segmentTrackStart } from "../lib/segment"; import { dedent } from "../lib/string"; import { checkIsTypeBuilderEnabled, getRepositoryName, TypeBuilderRequiredError } from "../project"; @@ -54,26 +54,15 @@ export default createCommand(config, async ({ values }) => { segmentTrackStart("sync", { watch }); - try { - if (watch) { - await watchForChanges(repo, adapter); - } else { - await syncSlices(repo, adapter); - await syncCustomTypes(repo, adapter); - await adapter.generateTypes(); - segmentTrackEnd("sync", { watch }); - - console.info("Sync complete"); - } - } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } - if (error instanceof UnknownRequestError) { - const message = await error.text(); - throw new CommandError(`Failed to sync: ${message}`); - } - throw error; + if (watch) { + await watchForChanges(repo, adapter); + } else { + await syncSlices(repo, adapter); + await syncCustomTypes(repo, adapter); + await adapter.generateTypes(); + segmentTrackEnd("sync", { watch }); + + console.info("Sync complete"); } }); From 1770965e72acf40a19398f48949a76ad72cc2b3e Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Sat, 11 Apr 2026 00:43:14 +0000 Subject: [PATCH 4/7] fix: use contextual error messages for 404s instead of always assuming repo not found Split try/catch blocks in list-then-mutate commands so list-endpoint 404s say "Repository not found" and mutation-endpoint 404s say the entity-specific message (e.g. "Webhook not found"). Remove explicit 404 catches where ambiguous (locale-remove, repo-create) and add a generic NotFoundRequestError handler in index.ts as a fallback. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/commands/locale-remove.ts | 5 +-- src/commands/locale-set-master.ts | 35 ++++++++++----- src/commands/preview-remove.ts | 23 +++++++--- src/commands/repo-create.ts | 5 +-- src/commands/token-delete.ts | 64 +++++++++++++++++++--------- src/commands/webhook-disable.ts | 37 ++++++++++------ src/commands/webhook-enable.ts | 37 ++++++++++------ src/commands/webhook-remove.ts | 25 ++++++++--- src/commands/webhook-set-triggers.ts | 25 ++++++++--- src/index.ts | 11 ++++- 10 files changed, 186 insertions(+), 81 deletions(-) diff --git a/src/commands/locale-remove.ts b/src/commands/locale-remove.ts index 16c9697..2f45626 100644 --- a/src/commands/locale-remove.ts +++ b/src/commands/locale-remove.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { removeLocale } from "../clients/locale"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -30,9 +30,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { await removeLocale(code, { repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to remove locale: ${message}`); diff --git a/src/commands/locale-set-master.ts b/src/commands/locale-set-master.ts index 0357cd8..c7b2fd8 100644 --- a/src/commands/locale-set-master.ts +++ b/src/commands/locale-set-master.ts @@ -27,27 +27,40 @@ export default createCommand(config, async ({ positionals, values }) => { const token = await getToken(); const host = await getHost(); + let locales; try { - const locales = await getLocales({ repo, token, host }); - const locale = locales.find((l) => l.id === code); - - if (!locale) { - throw new CommandError( - `Locale "${code}" not found. Available locales: ${locales.map((l) => l.id).join(", ")}`, - ); + locales = await getLocales({ repo, token, host }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); } - - if (locale.isMaster) { - throw new CommandError(`Locale "${code}" is already the master.`); + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to set master locale: ${message}`); } + throw error; + } + + const locale = locales.find((l) => l.id === code); + + if (!locale) { + throw new CommandError( + `Locale "${code}" not found. Available locales: ${locales.map((l) => l.id).join(", ")}`, + ); + } + if (locale.isMaster) { + throw new CommandError(`Locale "${code}" is already the master.`); + } + + try { await upsertLocale( { id: locale.id, isMaster: true, customName: locale.customName ?? undefined }, { repo, token, host }, ); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); + throw new CommandError(`Locale not found: ${code}`); } if (error instanceof UnknownRequestError) { const message = await error.text(); diff --git a/src/commands/preview-remove.ts b/src/commands/preview-remove.ts index bea2e18..f907845 100644 --- a/src/commands/preview-remove.ts +++ b/src/commands/preview-remove.ts @@ -27,17 +27,30 @@ export default createCommand(config, async ({ positionals, values }) => { const token = await getToken(); const host = await getHost(); + let previews; try { - const previews = await getPreviews({ repo, token, host }); - const preview = previews.find((p) => p.url === previewUrl); - if (!preview) { - throw new CommandError(`Preview not found: ${previewUrl}`); + previews = await getPreviews({ repo, token, host }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to remove preview: ${message}`); } + throw error; + } + + const preview = previews.find((p) => p.url === previewUrl); + if (!preview) { + throw new CommandError(`Preview not found: ${previewUrl}`); + } + try { await removePreview(preview.id, { repo, token, host }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); + throw new CommandError(`Preview not found: ${previewUrl}`); } if (error instanceof UnknownRequestError) { const message = await error.text(); diff --git a/src/commands/repo-create.ts b/src/commands/repo-create.ts index 9f63028..d0ea8f8 100644 --- a/src/commands/repo-create.ts +++ b/src/commands/repo-create.ts @@ -2,7 +2,7 @@ import { getAdapter } from "../adapters"; import { getHost, getToken } from "../auth"; import { checkIsDomainAvailable, createRepository } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; const MAX_DOMAIN_TRIES = 5; @@ -31,9 +31,6 @@ export default createCommand(config, async ({ values }) => { try { await createRepository({ domain, name: name ?? domain, framework, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${domain}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to create repository: ${message}`); diff --git a/src/commands/token-delete.ts b/src/commands/token-delete.ts index 7782a7a..bfd6ba1 100644 --- a/src/commands/token-delete.ts +++ b/src/commands/token-delete.ts @@ -32,30 +32,13 @@ export default createCommand(config, async ({ positionals, values }) => { const token = await getToken(); const host = await getHost(); + let apps; + let writeTokensInfo; try { - const [apps, writeTokensInfo] = await Promise.all([ + [apps, writeTokensInfo] = await Promise.all([ getOAuthApps({ repo, token, host }), getWriteTokens({ repo, token, host }), ]); - - // Search access tokens - const accessTokenAuths = apps.flatMap((app) => app.wroom_auths); - const accessToken = accessTokenAuths.find((auth) => auth.token === tokenValue); - if (accessToken) { - await deleteOAuthAuthorization(accessToken.id, { repo, token, host }); - console.info("Token deleted"); - return; - } - - // Search write tokens - const writeToken = writeTokensInfo.tokens.find((t) => t.token === tokenValue); - if (writeToken) { - await deleteWriteToken(writeToken.token, { repo, token, host }); - console.info("Token deleted"); - return; - } - - throw new CommandError(`Token not found: ${tokenValue}`); } catch (error) { if (error instanceof NotFoundRequestError) { throw new CommandError(`Repository not found: ${repo}`); @@ -66,4 +49,45 @@ export default createCommand(config, async ({ positionals, values }) => { } throw error; } + + // Search access tokens + const accessTokenAuths = apps.flatMap((app) => app.wroom_auths); + const accessToken = accessTokenAuths.find((auth) => auth.token === tokenValue); + if (accessToken) { + try { + await deleteOAuthAuthorization(accessToken.id, { repo, token, host }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Token not found: ${tokenValue}`); + } + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to delete token: ${message}`); + } + throw error; + } + console.info("Token deleted"); + return; + } + + // Search write tokens + const writeToken = writeTokensInfo.tokens.find((t) => t.token === tokenValue); + if (writeToken) { + try { + await deleteWriteToken(writeToken.token, { repo, token, host }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Token not found: ${tokenValue}`); + } + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to delete token: ${message}`); + } + throw error; + } + console.info("Token deleted"); + return; + } + + throw new CommandError(`Token not found: ${tokenValue}`); }); diff --git a/src/commands/webhook-disable.ts b/src/commands/webhook-disable.ts index 140b68e..72ced5e 100644 --- a/src/commands/webhook-disable.ts +++ b/src/commands/webhook-disable.ts @@ -26,27 +26,40 @@ export default createCommand(config, async ({ positionals, values }) => { const token = await getToken(); const host = await getHost(); + let webhooks; try { - const webhooks = await getWebhooks({ repo, token, host }); - const webhook = webhooks.find((w) => w.config.url === webhookUrl); - if (!webhook) { - throw new CommandError(`Webhook not found: ${webhookUrl}`); + webhooks = await getWebhooks({ repo, token, host }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); } - - if (!webhook.config.active) { - console.info(`Webhook already disabled: ${webhookUrl}`); - return; + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to disable webhook: ${message}`); } + throw error; + } + + const webhook = webhooks.find((w) => w.config.url === webhookUrl); + if (!webhook) { + throw new CommandError(`Webhook not found: ${webhookUrl}`); + } + + if (!webhook.config.active) { + console.info(`Webhook already disabled: ${webhookUrl}`); + return; + } - const id = webhook.config._id; + const id = webhook.config._id; - const updatedConfig = structuredClone(webhook.config); - updatedConfig.active = false; + const updatedConfig = structuredClone(webhook.config); + updatedConfig.active = false; + try { await updateWebhook(id, updatedConfig, { repo, token, host }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); + throw new CommandError(`Webhook not found: ${webhookUrl}`); } if (error instanceof UnknownRequestError) { const message = await error.text(); diff --git a/src/commands/webhook-enable.ts b/src/commands/webhook-enable.ts index 4b1150b..482c03e 100644 --- a/src/commands/webhook-enable.ts +++ b/src/commands/webhook-enable.ts @@ -26,27 +26,40 @@ export default createCommand(config, async ({ positionals, values }) => { const token = await getToken(); const host = await getHost(); + let webhooks; try { - const webhooks = await getWebhooks({ repo, token, host }); - const webhook = webhooks.find((w) => w.config.url === webhookUrl); - if (!webhook) { - throw new CommandError(`Webhook not found: ${webhookUrl}`); + webhooks = await getWebhooks({ repo, token, host }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); } - - if (webhook.config.active) { - console.info(`Webhook already enabled: ${webhookUrl}`); - return; + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to enable webhook: ${message}`); } + throw error; + } + + const webhook = webhooks.find((w) => w.config.url === webhookUrl); + if (!webhook) { + throw new CommandError(`Webhook not found: ${webhookUrl}`); + } + + if (webhook.config.active) { + console.info(`Webhook already enabled: ${webhookUrl}`); + return; + } - const id = webhook.config._id; + const id = webhook.config._id; - const updatedConfig = structuredClone(webhook.config); - updatedConfig.active = true; + const updatedConfig = structuredClone(webhook.config); + updatedConfig.active = true; + try { await updateWebhook(id, updatedConfig, { repo, token, host }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); + throw new CommandError(`Webhook not found: ${webhookUrl}`); } if (error instanceof UnknownRequestError) { const message = await error.text(); diff --git a/src/commands/webhook-remove.ts b/src/commands/webhook-remove.ts index 5e4bbf8..818d1c2 100644 --- a/src/commands/webhook-remove.ts +++ b/src/commands/webhook-remove.ts @@ -26,19 +26,32 @@ export default createCommand(config, async ({ positionals, values }) => { const token = await getToken(); const host = await getHost(); + let webhooks; try { - const webhooks = await getWebhooks({ repo, token, host }); - const webhook = webhooks.find((w) => w.config.url === webhookUrl); - if (!webhook) { - throw new CommandError(`Webhook not found: ${webhookUrl}`); + webhooks = await getWebhooks({ repo, token, host }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to remove webhook: ${message}`); } + throw error; + } + + const webhook = webhooks.find((w) => w.config.url === webhookUrl); + if (!webhook) { + throw new CommandError(`Webhook not found: ${webhookUrl}`); + } - const id = webhook.config._id; + const id = webhook.config._id; + try { await deleteWebhook(id, { repo, token, host }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); + throw new CommandError(`Webhook not found: ${webhookUrl}`); } if (error instanceof UnknownRequestError) { const message = await error.text(); diff --git a/src/commands/webhook-set-triggers.ts b/src/commands/webhook-set-triggers.ts index f706c31..a56f05f 100644 --- a/src/commands/webhook-set-triggers.ts +++ b/src/commands/webhook-set-triggers.ts @@ -52,15 +52,28 @@ export default createCommand(config, async ({ positionals, values }) => { const token = await getToken(); const host = await getHost(); + let webhooks; try { - const webhooks = await getWebhooks({ repo, token, host }); - const webhook = webhooks.find((w) => w.config.url === webhookUrl); - if (!webhook) { - throw new CommandError(`Webhook not found: ${webhookUrl}`); + webhooks = await getWebhooks({ repo, token, host }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new CommandError(`Repository not found: ${repo}`); + } + if (error instanceof UnknownRequestError) { + const message = await error.text(); + throw new CommandError(`Failed to update webhook triggers: ${message}`); } + throw error; + } + + const webhook = webhooks.find((w) => w.config.url === webhookUrl); + if (!webhook) { + throw new CommandError(`Webhook not found: ${webhookUrl}`); + } - const id = webhook.config._id; + const id = webhook.config._id; + try { await updateWebhook( id, { @@ -76,7 +89,7 @@ export default createCommand(config, async ({ positionals, values }) => { ); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); + throw new CommandError(`Webhook not found: ${webhookUrl}`); } if (error instanceof UnknownRequestError) { const message = await error.text(); diff --git a/src/index.ts b/src/index.ts index e7b022c..fdb87a9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,7 +20,11 @@ import webhook from "./commands/webhook"; import whoami from "./commands/whoami"; import { InvalidPrismicConfig, MissingPrismicConfig } from "./config"; import { CommandError, createCommandRouter } from "./lib/command"; -import { ForbiddenRequestError, UnauthorizedRequestError } from "./lib/request"; +import { + ForbiddenRequestError, + NotFoundRequestError, + UnauthorizedRequestError, +} from "./lib/request"; import { initSegment, segmentIdentify, @@ -183,6 +187,11 @@ async function main(): Promise { return; } + if (error instanceof NotFoundRequestError) { + console.error("Not found. Verify the repository and any specified identifiers exist."); + return; + } + if (error instanceof InvalidPrismicConfig) { console.error(`${error.message} Run \`prismic init\` to re-create a config.`); return; From 99bf1ebb63a09c9c680578bcc0ff7e99d41f690d Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Mon, 13 Apr 2026 20:04:14 +0000 Subject: [PATCH 5/7] refactor: move repo-not-found detection into client layer Client functions that call repo-scoped endpoints (list/create operations) now catch 404s and rethrow with a "Repository not found" message on NotFoundRequestError. The central handler in index.ts prints the message when set, or falls back to the generic not-found text. This eliminates ~20 per-command try/catch blocks that were converting NotFoundRequestError into CommandError with the same repo-not-found message. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/clients/core.ts | 78 ++++++++---- src/clients/custom-types.ts | 32 +++-- src/clients/locale.ts | 19 ++- src/clients/repository.ts | 25 ++-- src/clients/wroom.ts | 177 ++++++++++++++++++-------- src/commands/init.ts | 11 +- src/commands/locale-add.ts | 2 +- src/commands/locale-list.ts | 5 +- src/commands/locale-set-master.ts | 3 - src/commands/preview-add.ts | 5 +- src/commands/preview-list.ts | 5 +- src/commands/preview-remove.ts | 3 - src/commands/preview-set-simulator.ts | 5 +- src/commands/repo-set-api-access.ts | 5 +- src/commands/repo-set-name.ts | 5 +- src/commands/repo-view.ts | 5 +- src/commands/sync.ts | 13 +- src/commands/token-create.ts | 5 +- src/commands/token-delete.ts | 3 - src/commands/token-list.ts | 5 +- src/commands/webhook-create.ts | 5 +- src/commands/webhook-disable.ts | 3 - src/commands/webhook-enable.ts | 3 - src/commands/webhook-list.ts | 5 +- src/commands/webhook-remove.ts | 3 - src/commands/webhook-set-triggers.ts | 3 - src/commands/webhook-view.ts | 5 +- src/index.ts | 2 +- src/lib/request.ts | 5 + 29 files changed, 250 insertions(+), 195 deletions(-) diff --git a/src/clients/core.ts b/src/clients/core.ts index 4c35557..baeb483 100644 --- a/src/clients/core.ts +++ b/src/clients/core.ts @@ -1,6 +1,6 @@ import * as z from "zod/mini"; -import { request } from "../lib/request"; +import { NotFoundRequestError, request } from "../lib/request"; const PreviewSchema = z.object({ id: z.string(), @@ -24,11 +24,18 @@ export async function getPreviews(config: { "core/repository/preview_configs", getCoreBaseUrl(repo, host), ); - const response = await request(url, { - credentials: { "prismic-auth": token }, - schema: GetPreviewsResponseSchema, - }); - return response.results; + try { + const response = await request(url, { + credentials: { "prismic-auth": token }, + schema: GetPreviewsResponseSchema, + }); + return response.results; + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + } + throw error; + } } export async function addPreview( @@ -41,15 +48,22 @@ export async function addPreview( ): Promise { const { repo, token, host } = config; const url = new URL("previews/new", getCoreBaseUrl(repo, host)); - await request(url, { - method: "POST", - body: { - name: previewConfig.name, - websiteURL: previewConfig.websiteURL, - resolverPath: previewConfig.resolverPath, - }, - credentials: { "prismic-auth": token }, - }); + try { + await request(url, { + method: "POST", + body: { + name: previewConfig.name, + websiteURL: previewConfig.websiteURL, + resolverPath: previewConfig.resolverPath, + }, + credentials: { "prismic-auth": token }, + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + } + throw error; + } } export async function removePreview( @@ -79,11 +93,18 @@ export async function getSimulatorUrl(config: { }): Promise { const { repo, token, host } = config; const url = new URL("core/repository", getCoreBaseUrl(repo, host)); - const response = await request(url, { - credentials: { "prismic-auth": token }, - schema: RepositoryResponseSchema, - }); - return response.simulator_url; + try { + const response = await request(url, { + credentials: { "prismic-auth": token }, + schema: RepositoryResponseSchema, + }); + return response.simulator_url; + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + } + throw error; + } } export async function setSimulatorUrl( @@ -92,11 +113,18 @@ export async function setSimulatorUrl( ): Promise { const { repo, token, host } = config; const url = new URL("core/repository", getCoreBaseUrl(repo, host)); - await request(url, { - method: "PATCH", - body: { simulator_url: simulatorUrl }, - credentials: { "prismic-auth": token }, - }); + try { + await request(url, { + method: "PATCH", + body: { simulator_url: simulatorUrl }, + credentials: { "prismic-auth": token }, + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + } + throw error; + } } function getCoreBaseUrl(repo: string, host: string): URL { diff --git a/src/clients/custom-types.ts b/src/clients/custom-types.ts index 54db858..fc0af5b 100644 --- a/src/clients/custom-types.ts +++ b/src/clients/custom-types.ts @@ -1,6 +1,6 @@ import type { CustomType, SharedSlice } from "@prismicio/types-internal/lib/customtypes"; -import { request } from "../lib/request"; +import { NotFoundRequestError, request } from "../lib/request"; export async function getCustomTypes(config: { repo: string; @@ -10,10 +10,17 @@ export async function getCustomTypes(config: { const { repo, token, host } = config; const customTypesServiceUrl = getCustomTypesServiceUrl(host); const url = new URL("customtypes", customTypesServiceUrl); - const response = await request(url, { - headers: { repository: repo, Authorization: `Bearer ${token}` }, - }); - return response; + try { + const response = await request(url, { + headers: { repository: repo, Authorization: `Bearer ${token}` }, + }); + return response; + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + } + throw error; + } } export async function getSlices(config: { @@ -24,10 +31,17 @@ export async function getSlices(config: { const { repo, token, host } = config; const customTypesServiceUrl = getCustomTypesServiceUrl(host); const url = new URL("slices", customTypesServiceUrl); - const response = await request(url, { - headers: { repository: repo, Authorization: `Bearer ${token}` }, - }); - return response; + try { + const response = await request(url, { + headers: { repository: repo, Authorization: `Bearer ${token}` }, + }); + return response; + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + } + throw error; + } } function getCustomTypesServiceUrl(host: string): URL { diff --git a/src/clients/locale.ts b/src/clients/locale.ts index 41cf212..3eec483 100644 --- a/src/clients/locale.ts +++ b/src/clients/locale.ts @@ -1,6 +1,6 @@ import * as z from "zod/mini"; -import { request } from "../lib/request"; +import { NotFoundRequestError, request } from "../lib/request"; const LocaleSchema = z.object({ id: z.string(), @@ -19,11 +19,18 @@ export async function getLocales(config: { const { repo, token, host } = config; const url = new URL("repository/locales", getLocaleServiceUrl(host)); url.searchParams.set("repository", repo); - const response = await request(url, { - headers: { Authorization: `Bearer ${token}` }, - schema: z.object({ results: z.array(LocaleSchema) }), - }); - return response.results; + try { + const response = await request(url, { + headers: { Authorization: `Bearer ${token}` }, + schema: z.object({ results: z.array(LocaleSchema) }), + }); + return response.results; + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + } + throw error; + } } export async function upsertLocale( diff --git a/src/clients/repository.ts b/src/clients/repository.ts index 9036818..84ce2c5 100644 --- a/src/clients/repository.ts +++ b/src/clients/repository.ts @@ -1,6 +1,6 @@ import * as z from "zod/mini"; -import { request } from "../lib/request"; +import { NotFoundRequestError, request } from "../lib/request"; const RepositorySchema = z.object({ quotas: z.optional( @@ -20,14 +20,21 @@ export async function getRepository(config: { const { repo, token, host } = config; const url = getRepositoryServiceUrl(host); url.searchParams.set("repository", repo); - const response = await request(url, { - headers: { - Authorization: `Bearer ${token}`, - repository: repo, - }, - schema: RepositorySchema, - }); - return response; + try { + const response = await request(url, { + headers: { + Authorization: `Bearer ${token}`, + repository: repo, + }, + schema: RepositorySchema, + }); + return response; + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + } + throw error; + } } function getRepositoryServiceUrl(host: string): URL { diff --git a/src/clients/wroom.ts b/src/clients/wroom.ts index 69ab897..63dbdb7 100644 --- a/src/clients/wroom.ts +++ b/src/clients/wroom.ts @@ -1,6 +1,6 @@ import * as z from "zod/mini"; -import { request } from "../lib/request"; +import { NotFoundRequestError, request } from "../lib/request"; const WebhookTriggersSchema = z.object({ documentsPublished: z.boolean(), @@ -33,11 +33,17 @@ export async function getWebhooks(config: { const { repo, token, host } = config; const wroomUrl = getWroomUrl(repo, host); const url = new URL("app/settings/webhooks", wroomUrl); - const response = await request(url, { - credentials: { "prismic-auth": token }, - schema: z.array(WebhookSchema), - }); - return response; + try { + return await request(url, { + credentials: { "prismic-auth": token }, + schema: z.array(WebhookSchema), + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + } + throw error; + } } export async function createWebhook( @@ -59,11 +65,18 @@ export async function createWebhook( body.set("releasesUpdated", webhookConfig.releasesUpdated.toString()); body.set("tagsCreated", webhookConfig.tagsCreated.toString()); body.set("tagsDeleted", webhookConfig.tagsDeleted.toString()); - await request(url, { - method: "POST", - body, - credentials: { "prismic-auth": token }, - }); + try { + await request(url, { + method: "POST", + body, + credentials: { "prismic-auth": token }, + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + } + throw error; + } } export async function updateWebhook( @@ -140,10 +153,17 @@ export async function getOAuthApps(config: { host: string; }): Promise { const url = new URL("settings/security/contentapi", getWroomUrl(config.repo, config.host)); - return await request(url, { - credentials: { "prismic-auth": config.token }, - schema: z.array(OAuthAppSchema), - }); + try { + return await request(url, { + credentials: { "prismic-auth": config.token }, + schema: z.array(OAuthAppSchema), + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${config.repo}`); + } + throw error; + } } export async function createOAuthApp( @@ -151,12 +171,19 @@ export async function createOAuthApp( config: { repo: string; token: string | undefined; host: string }, ): Promise { const url = new URL("settings/security/oauthapp", getWroomUrl(config.repo, config.host)); - return await request(url, { - method: "POST", - body: { app_name: name }, - credentials: { "prismic-auth": config.token }, - schema: OAuthAppSchema, - }); + try { + return await request(url, { + method: "POST", + body: { app_name: name }, + credentials: { "prismic-auth": config.token }, + schema: OAuthAppSchema, + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${config.repo}`); + } + throw error; + } } export async function createOAuthAuthorization( @@ -165,12 +192,19 @@ export async function createOAuthAuthorization( config: { repo: string; token: string | undefined; host: string }, ): Promise { const url = new URL("settings/security/authorizations", getWroomUrl(config.repo, config.host)); - return await request(url, { - method: "POST", - body: { app: appId, scope }, - credentials: { "prismic-auth": config.token }, - schema: AccessTokenSchema, - }); + try { + return await request(url, { + method: "POST", + body: { app: appId, scope }, + credentials: { "prismic-auth": config.token }, + schema: AccessTokenSchema, + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${config.repo}`); + } + throw error; + } } export async function deleteOAuthAuthorization( @@ -193,10 +227,17 @@ export async function getWriteTokens(config: { host: string; }): Promise { const url = new URL("settings/security/customtypesapi", getWroomUrl(config.repo, config.host)); - return await request(url, { - credentials: { "prismic-auth": config.token }, - schema: WriteTokensInfoSchema, - }); + try { + return await request(url, { + credentials: { "prismic-auth": config.token }, + schema: WriteTokensInfoSchema, + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${config.repo}`); + } + throw error; + } } export async function createWriteToken( @@ -204,12 +245,19 @@ export async function createWriteToken( config: { repo: string; token: string | undefined; host: string }, ): Promise { const url = new URL("settings/security/token", getWroomUrl(config.repo, config.host)); - return await request(url, { - method: "POST", - body: { app_name: name }, - credentials: { "prismic-auth": config.token }, - schema: WriteTokenSchema, - }); + try { + return await request(url, { + method: "POST", + body: { app_name: name }, + credentials: { "prismic-auth": config.token }, + schema: WriteTokenSchema, + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${config.repo}`); + } + throw error; + } } export async function deleteWriteToken( @@ -269,11 +317,18 @@ export async function getRepositoryAccess(config: { }): Promise { const { repo, token, host } = config; const url = new URL("syncState", getWroomUrl(repo, host)); - const response = await request(url, { - credentials: { "prismic-auth": token }, - schema: SyncStateSchema, - }); - return response.repository.api_access; + try { + const response = await request(url, { + credentials: { "prismic-auth": token }, + schema: SyncStateSchema, + }); + return response.repository.api_access; + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + } + throw error; + } } export type RepositoryAccessLevel = "private" | "public" | "open"; @@ -284,11 +339,18 @@ export async function setRepositoryAccess( ): Promise { const { repo, token, host } = config; const url = new URL("settings/security/apiaccess", getWroomUrl(repo, host)); - await request(url, { - method: "POST", - body: { api_access: level }, - credentials: { "prismic-auth": token }, - }); + try { + await request(url, { + method: "POST", + body: { api_access: level }, + credentials: { "prismic-auth": token }, + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + } + throw error; + } } const SetNameResponseSchema = z.object({ @@ -305,13 +367,20 @@ export async function setRepositoryName( const url = new URL("app/settings/repository", getWroomUrl(repo, host)); const formData = new FormData(); formData.set("displayname", name); - const response = await request(url, { - method: "POST", - body: formData, - credentials: { "prismic-auth": token }, - schema: SetNameResponseSchema, - }); - return response.repository.name; + try { + const response = await request(url, { + method: "POST", + body: formData, + credentials: { "prismic-auth": token }, + schema: SetNameResponseSchema, + }); + return response.repository.name; + } catch (error) { + if (error instanceof NotFoundRequestError) { + throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + } + throw error; + } } function getDashboardUrl(host: string): URL { diff --git a/src/commands/init.ts b/src/commands/init.ts index e93d834..510ca22 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -18,7 +18,6 @@ import { CommandError, createCommand, type CommandConfig } from "../lib/command" import { installDependencies } from "../lib/packageJson"; import { ForbiddenRequestError, - NotFoundRequestError, UnauthorizedRequestError, } from "../lib/request"; import { checkIsTypeBuilderEnabled, TypeBuilderRequiredError } from "../project"; @@ -108,15 +107,7 @@ export default createCommand(config, async ({ values }) => { ); } - let isTypeBuilderEnabled; - try { - isTypeBuilderEnabled = await checkIsTypeBuilderEnabled(repo, { token, host }); - } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } - throw error; - } + const isTypeBuilderEnabled = await checkIsTypeBuilderEnabled(repo, { token, host }); if (!isTypeBuilderEnabled) { throw new TypeBuilderRequiredError(); } diff --git a/src/commands/locale-add.ts b/src/commands/locale-add.ts index d094316..f844564 100644 --- a/src/commands/locale-add.ts +++ b/src/commands/locale-add.ts @@ -33,7 +33,7 @@ export default createCommand(config, async ({ positionals, values }) => { await upsertLocale({ id: code, isMaster: master, customName: name }, { repo, token, host }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); + throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); } if (error instanceof UnknownRequestError) { const message = await error.text(); diff --git a/src/commands/locale-list.ts b/src/commands/locale-list.ts index 9248dd4..bf7aa41 100644 --- a/src/commands/locale-list.ts +++ b/src/commands/locale-list.ts @@ -2,7 +2,7 @@ import { getHost, getToken } from "../auth"; import { getLocales } from "../clients/locale"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -29,9 +29,6 @@ export default createCommand(config, async ({ values }) => { try { locales = await getLocales({ repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to list locales: ${message}`); diff --git a/src/commands/locale-set-master.ts b/src/commands/locale-set-master.ts index c7b2fd8..eeb8664 100644 --- a/src/commands/locale-set-master.ts +++ b/src/commands/locale-set-master.ts @@ -31,9 +31,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { locales = await getLocales({ repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to set master locale: ${message}`); diff --git a/src/commands/preview-add.ts b/src/commands/preview-add.ts index 5878298..e4cf7bd 100644 --- a/src/commands/preview-add.ts +++ b/src/commands/preview-add.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { addPreview } from "../clients/core"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -42,9 +42,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { await addPreview({ name: displayName, websiteURL, resolverPath }, { repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to add preview: ${message}`); diff --git a/src/commands/preview-list.ts b/src/commands/preview-list.ts index 190e850..1b4d7b2 100644 --- a/src/commands/preview-list.ts +++ b/src/commands/preview-list.ts @@ -2,7 +2,7 @@ import { getHost, getToken } from "../auth"; import { getPreviews, getSimulatorUrl } from "../clients/core"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -33,9 +33,6 @@ export default createCommand(config, async ({ values }) => { getSimulatorUrl({ repo, token, host }), ]); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to list previews: ${message}`); diff --git a/src/commands/preview-remove.ts b/src/commands/preview-remove.ts index f907845..8e07c97 100644 --- a/src/commands/preview-remove.ts +++ b/src/commands/preview-remove.ts @@ -31,9 +31,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { previews = await getPreviews({ repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to remove preview: ${message}`); diff --git a/src/commands/preview-set-simulator.ts b/src/commands/preview-set-simulator.ts index 6e4bc9e..f61eb15 100644 --- a/src/commands/preview-set-simulator.ts +++ b/src/commands/preview-set-simulator.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { setSimulatorUrl } from "../clients/core"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -45,9 +45,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { await setSimulatorUrl(simulatorUrl, { repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to set simulator URL: ${message}`); diff --git a/src/commands/repo-set-api-access.ts b/src/commands/repo-set-api-access.ts index 30bc271..afe8103 100644 --- a/src/commands/repo-set-api-access.ts +++ b/src/commands/repo-set-api-access.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { type RepositoryAccessLevel, setRepositoryAccess } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const VALID_LEVELS: RepositoryAccessLevel[] = ["private", "public", "open"]; @@ -38,9 +38,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { await setRepositoryAccess(level as RepositoryAccessLevel, { repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to set repository access: ${message}`); diff --git a/src/commands/repo-set-name.ts b/src/commands/repo-set-name.ts index d2e5928..1987b53 100644 --- a/src/commands/repo-set-name.ts +++ b/src/commands/repo-set-name.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { setRepositoryName } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -31,9 +31,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { confirmedName = await setRepositoryName(displayName, { repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to set repository name: ${message}`); diff --git a/src/commands/repo-view.ts b/src/commands/repo-view.ts index b4fbf52..85051e1 100644 --- a/src/commands/repo-view.ts +++ b/src/commands/repo-view.ts @@ -4,7 +4,7 @@ import { getProfile } from "../clients/user"; import { getRepositoryAccess } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -43,9 +43,6 @@ export default createCommand(config, async ({ values }) => { getRepositoryAccess({ repo, token, host }), ]); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to fetch repository details: ${message}`); diff --git a/src/commands/sync.ts b/src/commands/sync.ts index c39871e..4f9df13 100644 --- a/src/commands/sync.ts +++ b/src/commands/sync.ts @@ -5,8 +5,7 @@ import { getAdapter, type Adapter } from "../adapters"; import { getHost, getToken } from "../auth"; import { getCustomTypes, getSlices } from "../clients/custom-types"; import { env } from "../env"; -import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError } from "../lib/request"; +import { createCommand, type CommandConfig } from "../lib/command"; import { segmentTrackEnd, segmentTrackStart } from "../lib/segment"; import { dedent } from "../lib/string"; import { checkIsTypeBuilderEnabled, getRepositoryName, TypeBuilderRequiredError } from "../project"; @@ -35,15 +34,7 @@ export default createCommand(config, async ({ values }) => { const token = await getToken(); const host = await getHost(); - let isTypeBuilderEnabled; - try { - isTypeBuilderEnabled = await checkIsTypeBuilderEnabled(repo, { token, host }); - } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } - throw error; - } + const isTypeBuilderEnabled = await checkIsTypeBuilderEnabled(repo, { token, host }); if (!isTypeBuilderEnabled) { throw new TypeBuilderRequiredError(); } diff --git a/src/commands/token-create.ts b/src/commands/token-create.ts index 8a6f2d2..73e25c0 100644 --- a/src/commands/token-create.ts +++ b/src/commands/token-create.ts @@ -6,7 +6,7 @@ import { getOAuthApps, } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const CLI_APP_NAME = "Prismic CLI"; @@ -55,9 +55,6 @@ export default createCommand(config, async ({ values }) => { console.info(`Token created: ${accessToken.token}`); } } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to create token: ${message}`); diff --git a/src/commands/token-delete.ts b/src/commands/token-delete.ts index bfd6ba1..9ba3ed4 100644 --- a/src/commands/token-delete.ts +++ b/src/commands/token-delete.ts @@ -40,9 +40,6 @@ export default createCommand(config, async ({ positionals, values }) => { getWriteTokens({ repo, token, host }), ]); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to delete token: ${message}`); diff --git a/src/commands/token-list.ts b/src/commands/token-list.ts index f18c0f9..3a7311f 100644 --- a/src/commands/token-list.ts +++ b/src/commands/token-list.ts @@ -2,7 +2,7 @@ import { getHost, getToken } from "../auth"; import { getOAuthApps, getWriteTokens } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -33,9 +33,6 @@ export default createCommand(config, async ({ values }) => { getWriteTokens({ repo, token, host }), ]); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to list tokens: ${message}`); diff --git a/src/commands/webhook-create.ts b/src/commands/webhook-create.ts index 29f657c..19c15f8 100644 --- a/src/commands/webhook-create.ts +++ b/src/commands/webhook-create.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { createWebhook, WEBHOOK_TRIGGERS } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -74,9 +74,6 @@ export default createCommand(config, async ({ positionals, values }) => { { repo, token, host }, ); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to create webhook: ${message}`); diff --git a/src/commands/webhook-disable.ts b/src/commands/webhook-disable.ts index 72ced5e..ad5dec1 100644 --- a/src/commands/webhook-disable.ts +++ b/src/commands/webhook-disable.ts @@ -30,9 +30,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { webhooks = await getWebhooks({ repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to disable webhook: ${message}`); diff --git a/src/commands/webhook-enable.ts b/src/commands/webhook-enable.ts index 482c03e..5a14251 100644 --- a/src/commands/webhook-enable.ts +++ b/src/commands/webhook-enable.ts @@ -30,9 +30,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { webhooks = await getWebhooks({ repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to enable webhook: ${message}`); diff --git a/src/commands/webhook-list.ts b/src/commands/webhook-list.ts index 7b99202..bf230fb 100644 --- a/src/commands/webhook-list.ts +++ b/src/commands/webhook-list.ts @@ -2,7 +2,7 @@ import { getHost, getToken } from "../auth"; import { getWebhooks } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -28,9 +28,6 @@ export default createCommand(config, async ({ values }) => { try { webhooks = await getWebhooks({ repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to list webhooks: ${message}`); diff --git a/src/commands/webhook-remove.ts b/src/commands/webhook-remove.ts index 818d1c2..71a53c8 100644 --- a/src/commands/webhook-remove.ts +++ b/src/commands/webhook-remove.ts @@ -30,9 +30,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { webhooks = await getWebhooks({ repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to remove webhook: ${message}`); diff --git a/src/commands/webhook-set-triggers.ts b/src/commands/webhook-set-triggers.ts index a56f05f..abb6dad 100644 --- a/src/commands/webhook-set-triggers.ts +++ b/src/commands/webhook-set-triggers.ts @@ -56,9 +56,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { webhooks = await getWebhooks({ repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to update webhook triggers: ${message}`); diff --git a/src/commands/webhook-view.ts b/src/commands/webhook-view.ts index 3aa15b4..99769f7 100644 --- a/src/commands/webhook-view.ts +++ b/src/commands/webhook-view.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { getWebhooks, WEBHOOK_TRIGGERS } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -30,9 +30,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { webhooks = await getWebhooks({ repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Repository not found: ${repo}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to fetch webhook details: ${message}`); diff --git a/src/index.ts b/src/index.ts index fdb87a9..af1c3d2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -188,7 +188,7 @@ async function main(): Promise { } if (error instanceof NotFoundRequestError) { - console.error("Not found. Verify the repository and any specified identifiers exist."); + console.error(error.message || "Not found. Verify the repository and any specified identifiers exist."); return; } diff --git a/src/lib/request.ts b/src/lib/request.ts index 04ba67a..a0cb36e 100644 --- a/src/lib/request.ts +++ b/src/lib/request.ts @@ -91,6 +91,11 @@ export class UnknownRequestError extends RequestError { } export class NotFoundRequestError extends RequestError { name = "NotFoundRequestError"; + + constructor(response: Response, message?: string) { + super(response); + this.message = message ?? ""; + } } export class ForbiddenRequestError extends RequestError { name = "ForbiddenRequestError"; From 77c98a5d526afc27abc1ca7b532d0339839be2c4 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Mon, 13 Apr 2026 20:20:26 +0000 Subject: [PATCH 6/7] refactor: mutate and rethrow NotFoundRequestError instead of creating new Simpler pattern: set message on the existing error and rethrow to preserve the original stack trace and response. Remove redundant throw statements where the catch block already ends with throw error. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/clients/core.ts | 8 ++++---- src/clients/custom-types.ts | 4 ++-- src/clients/locale.ts | 2 +- src/clients/repository.ts | 2 +- src/clients/wroom.ts | 20 ++++++++++---------- src/commands/locale-add.ts | 3 ++- src/lib/request.ts | 4 ++-- 7 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/clients/core.ts b/src/clients/core.ts index baeb483..707663b 100644 --- a/src/clients/core.ts +++ b/src/clients/core.ts @@ -32,7 +32,7 @@ export async function getPreviews(config: { return response.results; } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + error.message = `Repository not found: ${repo}`; } throw error; } @@ -60,7 +60,7 @@ export async function addPreview( }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + error.message = `Repository not found: ${repo}`; } throw error; } @@ -101,7 +101,7 @@ export async function getSimulatorUrl(config: { return response.simulator_url; } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + error.message = `Repository not found: ${repo}`; } throw error; } @@ -121,7 +121,7 @@ export async function setSimulatorUrl( }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + error.message = `Repository not found: ${repo}`; } throw error; } diff --git a/src/clients/custom-types.ts b/src/clients/custom-types.ts index fc0af5b..7be87d4 100644 --- a/src/clients/custom-types.ts +++ b/src/clients/custom-types.ts @@ -17,7 +17,7 @@ export async function getCustomTypes(config: { return response; } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + error.message = `Repository not found: ${repo}`; } throw error; } @@ -38,7 +38,7 @@ export async function getSlices(config: { return response; } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + error.message = `Repository not found: ${repo}`; } throw error; } diff --git a/src/clients/locale.ts b/src/clients/locale.ts index 3eec483..f84fa04 100644 --- a/src/clients/locale.ts +++ b/src/clients/locale.ts @@ -27,7 +27,7 @@ export async function getLocales(config: { return response.results; } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + error.message = `Repository not found: ${repo}`; } throw error; } diff --git a/src/clients/repository.ts b/src/clients/repository.ts index 84ce2c5..351f465 100644 --- a/src/clients/repository.ts +++ b/src/clients/repository.ts @@ -31,7 +31,7 @@ export async function getRepository(config: { return response; } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + error.message = `Repository not found: ${repo}`; } throw error; } diff --git a/src/clients/wroom.ts b/src/clients/wroom.ts index 63dbdb7..e9ec7af 100644 --- a/src/clients/wroom.ts +++ b/src/clients/wroom.ts @@ -40,7 +40,7 @@ export async function getWebhooks(config: { }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + error.message = `Repository not found: ${repo}`; } throw error; } @@ -73,7 +73,7 @@ export async function createWebhook( }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + error.message = `Repository not found: ${repo}`; } throw error; } @@ -160,7 +160,7 @@ export async function getOAuthApps(config: { }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${config.repo}`); + error.message = `Repository not found: ${config.repo}`; } throw error; } @@ -180,7 +180,7 @@ export async function createOAuthApp( }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${config.repo}`); + error.message = `Repository not found: ${config.repo}`; } throw error; } @@ -201,7 +201,7 @@ export async function createOAuthAuthorization( }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${config.repo}`); + error.message = `Repository not found: ${config.repo}`; } throw error; } @@ -234,7 +234,7 @@ export async function getWriteTokens(config: { }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${config.repo}`); + error.message = `Repository not found: ${config.repo}`; } throw error; } @@ -254,7 +254,7 @@ export async function createWriteToken( }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${config.repo}`); + error.message = `Repository not found: ${config.repo}`; } throw error; } @@ -325,7 +325,7 @@ export async function getRepositoryAccess(config: { return response.repository.api_access; } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + error.message = `Repository not found: ${repo}`; } throw error; } @@ -347,7 +347,7 @@ export async function setRepositoryAccess( }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + error.message = `Repository not found: ${repo}`; } throw error; } @@ -377,7 +377,7 @@ export async function setRepositoryName( return response.repository.name; } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + error.message = `Repository not found: ${repo}`; } throw error; } diff --git a/src/commands/locale-add.ts b/src/commands/locale-add.ts index f844564..c846d6d 100644 --- a/src/commands/locale-add.ts +++ b/src/commands/locale-add.ts @@ -33,7 +33,8 @@ export default createCommand(config, async ({ positionals, values }) => { await upsertLocale({ id: code, isMaster: master, customName: name }, { repo, token, host }); } catch (error) { if (error instanceof NotFoundRequestError) { - throw new NotFoundRequestError(error.response, `Repository not found: ${repo}`); + error.message = `Repository not found: ${repo}`; + throw error; } if (error instanceof UnknownRequestError) { const message = await error.text(); diff --git a/src/lib/request.ts b/src/lib/request.ts index a0cb36e..f9c63ad 100644 --- a/src/lib/request.ts +++ b/src/lib/request.ts @@ -92,9 +92,9 @@ export class UnknownRequestError extends RequestError { export class NotFoundRequestError extends RequestError { name = "NotFoundRequestError"; - constructor(response: Response, message?: string) { + constructor(response: Response) { super(response); - this.message = message ?? ""; + this.message = ""; } } export class ForbiddenRequestError extends RequestError { From 13773c1acfa262d59599e75430ae39b4aed52344 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Tue, 14 Apr 2026 18:56:04 +0000 Subject: [PATCH 7/7] refactor: move remaining NotFoundRequestError handling into client layer Co-Authored-By: Claude Opus 4.6 (1M context) --- src/clients/core.ts | 17 +++++--- src/clients/docs.ts | 26 +++++++++--- src/clients/locale.ts | 29 ++++++++----- src/clients/wroom.ts | 62 ++++++++++++++++++++-------- src/commands/docs-list.ts | 5 +-- src/commands/docs-view.ts | 5 +-- src/commands/locale-add.ts | 6 +-- src/commands/locale-set-master.ts | 5 +-- src/commands/preview-remove.ts | 5 +-- src/commands/token-delete.ts | 8 +--- src/commands/webhook-disable.ts | 5 +-- src/commands/webhook-enable.ts | 5 +-- src/commands/webhook-remove.ts | 5 +-- src/commands/webhook-set-triggers.ts | 5 +-- 14 files changed, 105 insertions(+), 83 deletions(-) diff --git a/src/clients/core.ts b/src/clients/core.ts index 707663b..0a15359 100644 --- a/src/clients/core.ts +++ b/src/clients/core.ts @@ -75,11 +75,18 @@ export async function removePreview( `previews/delete/${id}`, getCoreBaseUrl(repo, host), ); - await request(url, { - method: "POST", - body: {}, - credentials: { "prismic-auth": token }, - }); + try { + await request(url, { + method: "POST", + body: {}, + credentials: { "prismic-auth": token }, + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + error.message = "Preview not found"; + } + throw error; + } } const RepositoryResponseSchema = z.object({ diff --git a/src/clients/docs.ts b/src/clients/docs.ts index 5f7d3da..6b7eb58 100644 --- a/src/clients/docs.ts +++ b/src/clients/docs.ts @@ -1,7 +1,7 @@ import * as z from "zod/mini"; import { DEFAULT_PRISMIC_HOST, env } from "../env"; -import { request } from "../lib/request"; +import { NotFoundRequestError, request } from "../lib/request"; const DocsIndexEntrySchema = z.object({ path: z.string(), @@ -30,15 +30,29 @@ export async function getDocsIndex(): Promise { export async function getDocsPageIndex(path: string): Promise { const url = new URL(`api/index/${path}`, getDocsBaseUrl()); - return await request(url, { schema: DocsPageSchema }); + try { + return await request(url, { schema: DocsPageSchema }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + error.message = `Documentation page not found: ${path}`; + } + throw error; + } } export async function getDocsPageContent(path: string): Promise { const url = new URL(path, getDocsBaseUrl()); - return await request(url, { - headers: { Accept: "text/markdown" }, - schema: z.string(), - }); + try { + return await request(url, { + headers: { Accept: "text/markdown" }, + schema: z.string(), + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + error.message = `Page not found: ${path}`; + } + throw error; + } } function getDocsBaseUrl(): URL { diff --git a/src/clients/locale.ts b/src/clients/locale.ts index f84fa04..fb79ebb 100644 --- a/src/clients/locale.ts +++ b/src/clients/locale.ts @@ -40,17 +40,24 @@ export async function upsertLocale( const { repo, token, host } = config; const url = new URL("repository/locales", getLocaleServiceUrl(host)); url.searchParams.set("repository", repo); - const response = await request(url, { - method: "POST", - body: { - id: locale.id, - isMaster: locale.isMaster ?? false, - ...(locale.customName ? { customName: locale.customName } : {}), - }, - headers: { Authorization: `Bearer ${token}` }, - schema: LocaleSchema, - }); - return response; + try { + const response = await request(url, { + method: "POST", + body: { + id: locale.id, + isMaster: locale.isMaster ?? false, + ...(locale.customName ? { customName: locale.customName } : {}), + }, + headers: { Authorization: `Bearer ${token}` }, + schema: LocaleSchema, + }); + return response; + } catch (error) { + if (error instanceof NotFoundRequestError) { + error.message = `Repository not found: ${repo}`; + } + throw error; + } } export async function removeLocale( diff --git a/src/clients/wroom.ts b/src/clients/wroom.ts index e9ec7af..f148cdd 100644 --- a/src/clients/wroom.ts +++ b/src/clients/wroom.ts @@ -99,11 +99,18 @@ export async function updateWebhook( body.set("releasesUpdated", webhookConfig.releasesUpdated.toString()); body.set("tagsCreated", webhookConfig.tagsCreated.toString()); body.set("tagsDeleted", webhookConfig.tagsDeleted.toString()); - await request(url, { - method: "POST", - body, - credentials: { "prismic-auth": token }, - }); + try { + await request(url, { + method: "POST", + body, + credentials: { "prismic-auth": token }, + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + error.message = "Webhook not found"; + } + throw error; + } } export async function deleteWebhook( @@ -113,10 +120,17 @@ export async function deleteWebhook( const { repo, token, host } = config; const wroomUrl = getWroomUrl(repo, host); const url = new URL(`app/settings/webhooks/${id}/delete`, wroomUrl); - await request(url, { - method: "POST", - credentials: { "prismic-auth": token }, - }); + try { + await request(url, { + method: "POST", + credentials: { "prismic-auth": token }, + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + error.message = "Webhook not found"; + } + throw error; + } } const AccessTokenSchema = z.object({ @@ -215,10 +229,17 @@ export async function deleteOAuthAuthorization( `settings/security/authorizations/${encodeURIComponent(authId)}`, getWroomUrl(config.repo, config.host), ); - await request(url, { - method: "DELETE", - credentials: { "prismic-auth": config.token }, - }); + try { + await request(url, { + method: "DELETE", + credentials: { "prismic-auth": config.token }, + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + error.message = "Token not found"; + } + throw error; + } } export async function getWriteTokens(config: { @@ -268,10 +289,17 @@ export async function deleteWriteToken( `settings/security/token/${encodeURIComponent(tokenValue)}`, getWroomUrl(config.repo, config.host), ); - await request(url, { - method: "DELETE", - credentials: { "prismic-auth": config.token }, - }); + try { + await request(url, { + method: "DELETE", + credentials: { "prismic-auth": config.token }, + }); + } catch (error) { + if (error instanceof NotFoundRequestError) { + error.message = `Token not found: ${tokenValue}`; + } + throw error; + } } export async function checkIsDomainAvailable(config: { diff --git a/src/commands/docs-list.ts b/src/commands/docs-list.ts index d968ffa..69d2a2f 100644 --- a/src/commands/docs-list.ts +++ b/src/commands/docs-list.ts @@ -1,7 +1,7 @@ import { getDocsIndex, getDocsPageIndex } from "../clients/docs"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; const config = { name: "prismic docs list", @@ -30,9 +30,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { entry = await getDocsPageIndex(path); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Documentation page not found: ${path}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to fetch documentation index: ${message}`); diff --git a/src/commands/docs-view.ts b/src/commands/docs-view.ts index c1b7a8a..ec22268 100644 --- a/src/commands/docs-view.ts +++ b/src/commands/docs-view.ts @@ -3,7 +3,7 @@ import GithubSlugger from "github-slugger"; import { getDocsPageContent } from "../clients/docs"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; const config = { name: "prismic docs view", @@ -35,9 +35,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { markdown = await getDocsPageContent(path); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Page not found: ${path}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to fetch page: ${message}`); diff --git a/src/commands/locale-add.ts b/src/commands/locale-add.ts index c846d6d..3220aac 100644 --- a/src/commands/locale-add.ts +++ b/src/commands/locale-add.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { upsertLocale } from "../clients/locale"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -32,10 +32,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { await upsertLocale({ id: code, isMaster: master, customName: name }, { repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - error.message = `Repository not found: ${repo}`; - throw error; - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to add locale: ${message}`); diff --git a/src/commands/locale-set-master.ts b/src/commands/locale-set-master.ts index eeb8664..1eb7380 100644 --- a/src/commands/locale-set-master.ts +++ b/src/commands/locale-set-master.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { getLocales, upsertLocale } from "../clients/locale"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -56,9 +56,6 @@ export default createCommand(config, async ({ positionals, values }) => { { repo, token, host }, ); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Locale not found: ${code}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to set master locale: ${message}`); diff --git a/src/commands/preview-remove.ts b/src/commands/preview-remove.ts index 8e07c97..6943c66 100644 --- a/src/commands/preview-remove.ts +++ b/src/commands/preview-remove.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { getPreviews, removePreview } from "../clients/core"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -46,9 +46,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { await removePreview(preview.id, { repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Preview not found: ${previewUrl}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to remove preview: ${message}`); diff --git a/src/commands/token-delete.ts b/src/commands/token-delete.ts index 9ba3ed4..72b4a68 100644 --- a/src/commands/token-delete.ts +++ b/src/commands/token-delete.ts @@ -6,7 +6,7 @@ import { getWriteTokens, } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -54,9 +54,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { await deleteOAuthAuthorization(accessToken.id, { repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Token not found: ${tokenValue}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to delete token: ${message}`); @@ -73,9 +70,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { await deleteWriteToken(writeToken.token, { repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Token not found: ${tokenValue}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to delete token: ${message}`); diff --git a/src/commands/webhook-disable.ts b/src/commands/webhook-disable.ts index ad5dec1..ae7268a 100644 --- a/src/commands/webhook-disable.ts +++ b/src/commands/webhook-disable.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { getWebhooks, updateWebhook } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -55,9 +55,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { await updateWebhook(id, updatedConfig, { repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Webhook not found: ${webhookUrl}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to disable webhook: ${message}`); diff --git a/src/commands/webhook-enable.ts b/src/commands/webhook-enable.ts index 5a14251..c2e58ff 100644 --- a/src/commands/webhook-enable.ts +++ b/src/commands/webhook-enable.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { getWebhooks, updateWebhook } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -55,9 +55,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { await updateWebhook(id, updatedConfig, { repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Webhook not found: ${webhookUrl}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to enable webhook: ${message}`); diff --git a/src/commands/webhook-remove.ts b/src/commands/webhook-remove.ts index 71a53c8..6dd4323 100644 --- a/src/commands/webhook-remove.ts +++ b/src/commands/webhook-remove.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { deleteWebhook, getWebhooks } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -47,9 +47,6 @@ export default createCommand(config, async ({ positionals, values }) => { try { await deleteWebhook(id, { repo, token, host }); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Webhook not found: ${webhookUrl}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to remove webhook: ${message}`); diff --git a/src/commands/webhook-set-triggers.ts b/src/commands/webhook-set-triggers.ts index abb6dad..9f7111e 100644 --- a/src/commands/webhook-set-triggers.ts +++ b/src/commands/webhook-set-triggers.ts @@ -1,7 +1,7 @@ import { getHost, getToken } from "../auth"; import { getWebhooks, updateWebhook, WEBHOOK_TRIGGERS } from "../clients/wroom"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; -import { NotFoundRequestError, UnknownRequestError } from "../lib/request"; +import { UnknownRequestError } from "../lib/request"; import { getRepositoryName } from "../project"; const config = { @@ -85,9 +85,6 @@ export default createCommand(config, async ({ positionals, values }) => { { repo, token, host }, ); } catch (error) { - if (error instanceof NotFoundRequestError) { - throw new CommandError(`Webhook not found: ${webhookUrl}`); - } if (error instanceof UnknownRequestError) { const message = await error.text(); throw new CommandError(`Failed to update webhook triggers: ${message}`);