diff --git a/.changeset/four-wolves-greet.md b/.changeset/four-wolves-greet.md new file mode 100644 index 00000000..68646764 --- /dev/null +++ b/.changeset/four-wolves-greet.md @@ -0,0 +1,5 @@ +--- +"@cipherstash/cli": minor +--- + +Fixed peer dependency by lazy loading commands requiring @cipherstash/stack. diff --git a/packages/cli/package.json b/packages/cli/package.json index ce6e61d7..386b2eb4 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -50,6 +50,14 @@ "posthog-node": "^5.28.9", "zod": "^4.3.6" }, + "peerDependencies": { + "@cipherstash/stack": ">=0.6.0" + }, + "peerDependenciesMeta": { + "@cipherstash/stack": { + "optional": true + } + }, "devDependencies": { "@cipherstash/stack": "workspace:*", "@types/pg": "^8.11.11", diff --git a/packages/cli/src/bin/stash.ts b/packages/cli/src/bin/stash.ts index 6f0b8046..3f280088 100644 --- a/packages/cli/src/bin/stash.ts +++ b/packages/cli/src/bin/stash.ts @@ -5,20 +5,41 @@ import { readFileSync } from 'node:fs' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' import * as p from '@clack/prompts' +// Commands that depend on @cipherstash/stack are lazy-loaded in the switch below. import { authCommand, - builderCommand, initCommand, installCommand, - pushCommand, - secretsCommand, setupCommand, statusCommand, testConnectionCommand, upgradeCommand, - validateCommand, } from '../commands/index.js' +function isModuleNotFound(err: unknown): boolean { + return ( + err instanceof Error && + 'code' in err && + (err as { code: string }).code === 'ERR_MODULE_NOT_FOUND' + ) +} + +async function requireStack(importFn: () => Promise): Promise { + try { + return await importFn() + } catch (err: unknown) { + if (isModuleNotFound(err)) { + p.log.error( + '@cipherstash/stack is required for this command.\n' + + ' Install it with: npm install @cipherstash/stack\n' + + ' Or run: npx @cipherstash/cli init', + ) + process.exit(1) as never + } + throw err + } +} + const __dirname = dirname(fileURLToPath(import.meta.url)) const pkg = JSON.parse( readFileSync(join(__dirname, '../../package.json'), 'utf-8'), @@ -148,15 +169,19 @@ async function runDbCommand( out: values.out, }) break - case 'push': + case 'push': { + const { pushCommand } = await requireStack(() => import('../commands/db/push.js')) await pushCommand({ dryRun: flags['dry-run'] }) break - case 'validate': + } + case 'validate': { + const { validateCommand } = await requireStack(() => import('../commands/db/validate.js')) await validateCommand({ supabase: flags.supabase, excludeOperatorFamily: flags['exclude-operator-family'], }) break + } case 'status': await statusCommand() break @@ -179,9 +204,11 @@ async function runSchemaCommand( flags: Record, ) { switch (sub) { - case 'build': + case 'build': { + const { builderCommand } = await requireStack(() => import('../commands/schema/build.js')) await builderCommand({ supabase: flags.supabase }) break + } default: p.log.error(`Unknown schema subcommand: ${sub ?? '(none)'}`) console.log() @@ -215,6 +242,7 @@ async function main() { break } case 'secrets': { + const { secretsCommand } = await requireStack(() => import('../commands/secrets/index.js')) const secretsArgs = subcommand ? [subcommand, ...commandArgs] : commandArgs diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 0a898d0f..a64b53ed 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -1,15 +1,7 @@ export { setupCommand } from './db/setup.js' export { installCommand } from './db/install.js' -export { pushCommand } from './db/push.js' export { statusCommand } from './db/status.js' export { testConnectionCommand } from './db/test-connection.js' export { upgradeCommand } from './db/upgrade.js' -export { - validateCommand, - validateEncryptConfig, - reportIssues, -} from './db/validate.js' -export { builderCommand } from './schema/build.js' export { authCommand } from './auth/index.js' export { initCommand } from './init/index.js' -export { secretsCommand } from './secrets/index.js'