diff --git a/packages/core/src/artifacts/artifacts-lock.ts b/packages/core/src/artifacts/artifacts-lock.ts index d9dba3e0..726ca9d4 100644 --- a/packages/core/src/artifacts/artifacts-lock.ts +++ b/packages/core/src/artifacts/artifacts-lock.ts @@ -1,6 +1,6 @@ import { open, readFile, rename, unlink } from "node:fs/promises"; import path from "node:path"; -import { CaatingaError, CaatingaErrorCode } from "../errors/CaatingaError.js"; +import { CaatingaError, CaatingaErrorCode, toCaatingaError } from "../errors/CaatingaError.js"; const LOCK_RETRY_DELAY_MS = 50; const LOCK_TIMEOUT_MS = 15_000; @@ -113,7 +113,7 @@ async function acquireLock(lockPath: string, deadline: number): Promise { return; } catch (error) { if ((error as NodeJS.ErrnoException).code !== "EEXIST") { - throw error; + throw toCaatingaError(error); } owner = await readLockOwner(lockPath); diff --git a/packages/core/src/artifacts/read-artifacts.ts b/packages/core/src/artifacts/read-artifacts.ts index eb893fe1..a4fc2cec 100644 --- a/packages/core/src/artifacts/read-artifacts.ts +++ b/packages/core/src/artifacts/read-artifacts.ts @@ -1,7 +1,7 @@ import { readFile } from "node:fs/promises"; import path from "node:path"; import { z } from "zod"; -import { CaatingaError, CaatingaErrorCode } from "../errors/CaatingaError.js"; +import { CaatingaError, CaatingaErrorCode, toCaatingaError } from "../errors/CaatingaError.js"; import { CaatingaArtifactsSchema, CURRENT_ARTIFACTS_SCHEMA_VERSION, @@ -44,6 +44,6 @@ export async function readArtifacts(cwd = process.cwd()): Promise undefined); - throw error; + throw toCaatingaError(error); } return artifactsPath; diff --git a/packages/core/src/config/load-config.ts b/packages/core/src/config/load-config.ts index d4f713ed..45870f61 100644 --- a/packages/core/src/config/load-config.ts +++ b/packages/core/src/config/load-config.ts @@ -2,7 +2,7 @@ import { access } from "node:fs/promises"; import path from "node:path"; import { createJiti } from "jiti"; import { z } from "zod"; -import { CaatingaError, CaatingaErrorCode } from "../errors/CaatingaError.js"; +import { CaatingaError, CaatingaErrorCode, toCaatingaError } from "../errors/CaatingaError.js"; import { isDependenciesNotInstalledError } from "./is-dependencies-not-installed-error.js"; import { CaatingaConfigSchema, type CaatingaConfig } from "./config.schema.js"; @@ -47,6 +47,6 @@ export async function loadConfig(options: LoadConfigOptions = {}): Promise { + let tmpDir: string; + + afterEach(async () => { + if (tmpDir) { + await rm(tmpDir, { recursive: true, force: true }); + } + }); + + it("writeArtifacts wraps a raw rename failure", async () => { + tmpDir = await mkdtemp(path.join(os.tmpdir(), "caatinga-wrap-")); + // A directory at the artifacts path makes the atomic rename fail with a + // raw fs error (EISDIR/ENOTEMPTY), not an ENOENT the code handles. + await mkdir(path.join(tmpDir, "caatinga.artifacts.json")); + + await expect(writeArtifacts(createInitialArtifacts("app"), tmpDir)).rejects.toBeInstanceOf( + CaatingaError + ); + }); + + it("readArtifacts wraps a raw read failure", async () => { + tmpDir = await mkdtemp(path.join(os.tmpdir(), "caatinga-wrap-")); + // Reading the artifacts path when it is a directory throws EISDIR — a raw + // error distinct from the ENOENT (missing file) the code returns empty for. + await mkdir(path.join(tmpDir, "caatinga.artifacts.json")); + + await expect(readArtifacts(tmpDir)).rejects.toMatchObject({ + code: CaatingaErrorCode.UNEXPECTED_ERROR, + }); + }); +}); diff --git a/packages/core/src/frontend/sync-frontend-env.ts b/packages/core/src/frontend/sync-frontend-env.ts index d4a78610..6e5950d8 100644 --- a/packages/core/src/frontend/sync-frontend-env.ts +++ b/packages/core/src/frontend/sync-frontend-env.ts @@ -2,7 +2,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises"; import path from "node:path"; import { readArtifacts } from "../artifacts/read-artifacts.js"; import type { CaatingaConfig } from "../config/config.schema.js"; -import { CaatingaError, CaatingaErrorCode } from "../errors/CaatingaError.js"; +import { CaatingaError, CaatingaErrorCode, toCaatingaError } from "../errors/CaatingaError.js"; import { resolveNetwork } from "../networks/resolve-network.js"; export type SyncFrontendEnvOptions = { @@ -35,7 +35,7 @@ async function readExistingEnv(envFile: string): Promise { if ((error as NodeJS.ErrnoException).code === "ENOENT") { return undefined; } - throw error; + throw toCaatingaError(error); } } diff --git a/packages/core/src/stellar-cli/check-stellar-cli-version.ts b/packages/core/src/stellar-cli/check-stellar-cli-version.ts index 9f3ae244..29c90bca 100644 --- a/packages/core/src/stellar-cli/check-stellar-cli-version.ts +++ b/packages/core/src/stellar-cli/check-stellar-cli-version.ts @@ -1,4 +1,4 @@ -import { CaatingaError, CaatingaErrorCode } from "../errors/CaatingaError.js"; +import { CaatingaError, CaatingaErrorCode, toCaatingaError } from "../errors/CaatingaError.js"; import { runCommand } from "../shell/run-command.js"; import { evaluateStellarCliCompatibility, @@ -36,7 +36,7 @@ export async function checkStellarCliVersion( ); } - throw error; + throw toCaatingaError(error); } const version = parseStellarCliVersion(rawOutput); diff --git a/packages/core/src/templates/create-project-from-template.ts b/packages/core/src/templates/create-project-from-template.ts index 9143254c..941ec8e0 100644 --- a/packages/core/src/templates/create-project-from-template.ts +++ b/packages/core/src/templates/create-project-from-template.ts @@ -1,7 +1,7 @@ import { cp, lstat, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises"; import path from "node:path"; import { z } from "zod"; -import { CaatingaError, CaatingaErrorCode } from "../errors/CaatingaError.js"; +import { CaatingaError, CaatingaErrorCode, toCaatingaError } from "../errors/CaatingaError.js"; import { readArtifacts } from "../artifacts/read-artifacts.js"; import { createInitialArtifacts, writeArtifacts } from "../artifacts/write-artifacts.js"; import { @@ -67,7 +67,7 @@ async function ensureArtifacts(targetDir: string, projectName: string): Promise< return; } - throw error; + throw toCaatingaError(error); } } @@ -109,7 +109,7 @@ async function readTemplateManifest(templateDir: string): Promise