spfn 0.2.0-beta.65 → 0.2.0-beta.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +114 -5
  2. package/dist/index.js +513 -393
  3. package/package.json +6 -5
package/dist/index.js CHANGED
@@ -922,7 +922,7 @@ var init_deployment_config = __esm({
922
922
 
923
923
  // src/utils/version.ts
924
924
  function getCliVersion() {
925
- return "0.2.0-beta.65";
925
+ return "0.2.0-beta.67";
926
926
  }
927
927
  function getTagFromVersion(version) {
928
928
  const match = version.match(/-([a-z]+)\./i);
@@ -1860,7 +1860,7 @@ var init_migration_status = __esm({
1860
1860
  });
1861
1861
 
1862
1862
  // src/index.ts
1863
- import { Command as Command14 } from "commander";
1863
+ import { Command as Command15 } from "commander";
1864
1864
 
1865
1865
  // src/commands/create.ts
1866
1866
  init_logger();
@@ -2647,13 +2647,18 @@ async function buildProject(options) {
2647
2647
  const orchestrator = new CodegenOrchestrator({
2648
2648
  generators,
2649
2649
  cwd,
2650
- debug: true
2650
+ debug: true,
2651
+ // A generator that refuses at build time — a broken route contract,
2652
+ // a router that will not load — must reach the exit code rather
2653
+ // than scroll past while the build ships anyway.
2654
+ throwOnError: true
2651
2655
  });
2652
- await orchestrator.generateAll();
2656
+ await orchestrator.generateAll("build");
2653
2657
  spinner.succeed("API client generated");
2654
2658
  } catch (error) {
2655
- spinner.warn("API client generation failed (non-critical)");
2656
- logger.warn(String(error));
2659
+ spinner.fail("Code generation failed");
2660
+ logger.error(error instanceof Error ? error.message : String(error));
2661
+ process.exit(1);
2657
2662
  }
2658
2663
  }
2659
2664
  if (hasNext && !options.serverOnly) {
@@ -3054,11 +3059,123 @@ codegenCommand.command("init").description("Initialize .spfnrc.ts with codegen c
3054
3059
  codegenCommand.command("list").alias("ls").description("List registered code generators").action(listGenerators);
3055
3060
  codegenCommand.command("run").description("Run code generators once (no watch mode)").action(runGenerators);
3056
3061
 
3057
- // src/commands/key.ts
3062
+ // src/commands/contract.ts
3058
3063
  init_logger();
3059
3064
  import { Command as Command9 } from "commander";
3060
- import { execSync } from "child_process";
3065
+ import { join as join22 } from "path";
3061
3066
  import chalk11 from "chalk";
3067
+ async function resolveContractsDir(cwd, override) {
3068
+ if (override) {
3069
+ return join22(cwd, override);
3070
+ }
3071
+ const { loadCodegenConfig } = await import("@spfn/core/codegen");
3072
+ const config = loadCodegenConfig(cwd);
3073
+ const generator = config.generators?.find(
3074
+ (entry) => "name" in entry && entry.name === "@spfn/core:contract"
3075
+ );
3076
+ return join22(cwd, generator?.outputDir ?? "./contracts");
3077
+ }
3078
+ async function regenerate(cwd) {
3079
+ const { loadCodegenConfig, createGeneratorsFromConfig, CodegenOrchestrator } = await import("@spfn/core/codegen");
3080
+ const config = loadCodegenConfig(cwd);
3081
+ const generators = (await createGeneratorsFromConfig(config, cwd)).filter((generator) => generator.name === "@spfn/core:contract");
3082
+ if (generators.length === 0) {
3083
+ throw new Error(
3084
+ "No @spfn/core:contract generator is configured in .spfnrc.ts.\nAdd one with the router path before using contract commands."
3085
+ );
3086
+ }
3087
+ const orchestrator = new CodegenOrchestrator({ generators, cwd, throwOnError: true });
3088
+ await orchestrator.generateAll("manual");
3089
+ }
3090
+ async function checkContractCommand(options) {
3091
+ const cwd = process.cwd();
3092
+ try {
3093
+ await regenerate(cwd);
3094
+ const { checkContract, formatViolations, readCurrentDocument } = await import("@spfn/core/contract");
3095
+ const contractsDir = await resolveContractsDir(cwd, options.dir);
3096
+ const result = checkContract(contractsDir, readCurrentDocument(contractsDir));
3097
+ for (const warning of result.warnings) {
3098
+ logger.warn(warning);
3099
+ }
3100
+ if (result.violations.length > 0) {
3101
+ console.log("\n" + chalk11.red.bold(`\u2717 Breaks the contract released as ${result.baselineVersion}
3102
+ `));
3103
+ console.log(formatViolations(result.violations));
3104
+ console.log("");
3105
+ process.exit(1);
3106
+ }
3107
+ const against = result.baselineVersion ? `backward compatible with released ${result.baselineVersion}` : "generated (nothing released to compare against yet)";
3108
+ console.log("\n" + chalk11.green.bold(`\u2713 Contract ${against}
3109
+ `));
3110
+ } catch (error) {
3111
+ logger.error(error instanceof Error ? error.message : String(error));
3112
+ process.exit(1);
3113
+ }
3114
+ }
3115
+ async function releaseContractCommand(version, options) {
3116
+ const cwd = process.cwd();
3117
+ try {
3118
+ await regenerate(cwd);
3119
+ const {
3120
+ checkContract,
3121
+ formatViolations,
3122
+ readCurrentDocument,
3123
+ writeSnapshot
3124
+ } = await import("@spfn/core/contract");
3125
+ const contractsDir = await resolveContractsDir(cwd, options.dir);
3126
+ const document = readCurrentDocument(contractsDir);
3127
+ const result = checkContract(contractsDir, document);
3128
+ if (result.violations.length > 0) {
3129
+ console.log("\n" + chalk11.red.bold(`\u2717 Cannot release: breaks the contract released as ${result.baselineVersion}
3130
+ `));
3131
+ console.log(formatViolations(result.violations));
3132
+ console.log("");
3133
+ process.exit(1);
3134
+ }
3135
+ for (const warning of result.warnings) {
3136
+ logger.warn(warning);
3137
+ }
3138
+ const file = writeSnapshot(contractsDir, version, document);
3139
+ console.log("\n" + chalk11.green.bold(`\u2713 Released contract ${version}
3140
+ `));
3141
+ console.log(chalk11.gray(` ${file}`));
3142
+ console.log(chalk11.gray(` ${document.operations.length} operation(s)`));
3143
+ console.log(chalk11.yellow("\n Commit this snapshot. The gate compares every later build against it.\n"));
3144
+ } catch (error) {
3145
+ logger.error(error instanceof Error ? error.message : String(error));
3146
+ process.exit(1);
3147
+ }
3148
+ }
3149
+ async function listContractCommand(options) {
3150
+ const cwd = process.cwd();
3151
+ try {
3152
+ const { listSnapshots } = await import("@spfn/core/contract");
3153
+ const contractsDir = await resolveContractsDir(cwd, options.dir);
3154
+ const snapshots = listSnapshots(contractsDir);
3155
+ if (snapshots.length === 0) {
3156
+ logger.info("No released contract snapshot yet");
3157
+ return;
3158
+ }
3159
+ console.log("\n" + chalk11.bold("Released contracts:"));
3160
+ for (const snapshot of snapshots) {
3161
+ console.log(` ${chalk11.cyan(snapshot.version)} ${chalk11.gray(snapshot.file)}`);
3162
+ }
3163
+ console.log("");
3164
+ } catch (error) {
3165
+ logger.error(error instanceof Error ? error.message : String(error));
3166
+ process.exit(1);
3167
+ }
3168
+ }
3169
+ var contractCommand = new Command9("contract").description("Route contract management (what separately deployed clients are promised)");
3170
+ contractCommand.command("check").description("Regenerate the contract and compare it against the newest released snapshot").option("--dir <path>", "Contracts directory (default: from .spfnrc.ts)").action(checkContractCommand);
3171
+ contractCommand.command("release <version>").description("Write contracts/released/<version>.json \u2014 required for every release").option("--dir <path>", "Contracts directory (default: from .spfnrc.ts)").action(releaseContractCommand);
3172
+ contractCommand.command("list").alias("ls").description("List released contract snapshots").option("--dir <path>", "Contracts directory (default: from .spfnrc.ts)").action(listContractCommand);
3173
+
3174
+ // src/commands/key.ts
3175
+ init_logger();
3176
+ import { Command as Command10 } from "commander";
3177
+ import { execSync } from "child_process";
3178
+ import chalk12 from "chalk";
3062
3179
 
3063
3180
  // src/utils/secret-gen.ts
3064
3181
  import { randomBytes as randomBytes2, randomUUID } from "crypto";
@@ -3133,44 +3250,44 @@ function copyToClipboard(text) {
3133
3250
  function generateSecret(bytes, preset, envVarName, copy) {
3134
3251
  const key = randomBase64Url(bytes);
3135
3252
  const config = preset ? PRESETS[preset] : null;
3136
- console.log("\n" + chalk11.green.bold("\u2713 Generated secret key:"));
3253
+ console.log("\n" + chalk12.green.bold("\u2713 Generated secret key:"));
3137
3254
  if (config) {
3138
- console.log(chalk11.dim(` ${config.description} (${bytes * 8} bits)`));
3255
+ console.log(chalk12.dim(` ${config.description} (${bytes * 8} bits)`));
3139
3256
  } else {
3140
- console.log(chalk11.dim(` ${bytes * 8}-bit secret`));
3257
+ console.log(chalk12.dim(` ${bytes * 8}-bit secret`));
3141
3258
  }
3142
- console.log("\n" + chalk11.cyan(key) + "\n");
3259
+ console.log("\n" + chalk12.cyan(key) + "\n");
3143
3260
  const varName = envVarName || config?.envVar || "SECRET_KEY";
3144
- console.log(chalk11.dim("Add to your .env file:"));
3145
- console.log(chalk11.yellow(`${varName}=${key}
3261
+ console.log(chalk12.dim("Add to your .env file:"));
3262
+ console.log(chalk12.yellow(`${varName}=${key}
3146
3263
  `));
3147
3264
  if (config?.usage) {
3148
- console.log(chalk11.dim("Usage:"));
3149
- console.log(chalk11.gray(` ${config.usage}
3265
+ console.log(chalk12.dim("Usage:"));
3266
+ console.log(chalk12.gray(` ${config.usage}
3150
3267
  `));
3151
3268
  }
3152
3269
  if (copy) {
3153
3270
  if (copyToClipboard(key)) {
3154
- console.log(chalk11.green("\u2713 Copied to clipboard!\n"));
3271
+ console.log(chalk12.green("\u2713 Copied to clipboard!\n"));
3155
3272
  } else {
3156
3273
  logger.warn("Could not copy to clipboard");
3157
3274
  }
3158
3275
  }
3159
3276
  }
3160
3277
  function listPresets() {
3161
- console.log("\n" + chalk11.bold("Available presets:"));
3278
+ console.log("\n" + chalk12.bold("Available presets:"));
3162
3279
  console.log();
3163
3280
  Object.entries(PRESETS).forEach(([name, config]) => {
3164
- console.log(` ${chalk11.cyan(name.padEnd(20))} ${chalk11.dim(config.description)}`);
3165
- console.log(` ${" ".repeat(20)} ${chalk11.gray(`\u2192 ${config.envVar} (${config.bytes * 8} bits)`)}`);
3281
+ console.log(` ${chalk12.cyan(name.padEnd(20))} ${chalk12.dim(config.description)}`);
3282
+ console.log(` ${" ".repeat(20)} ${chalk12.gray(`\u2192 ${config.envVar} (${config.bytes * 8} bits)`)}`);
3166
3283
  console.log();
3167
3284
  });
3168
- console.log(chalk11.dim("Usage:"));
3169
- console.log(chalk11.gray(" spfn key <preset>"));
3170
- console.log(chalk11.gray(" spfn key auth-encryption --copy"));
3285
+ console.log(chalk12.dim("Usage:"));
3286
+ console.log(chalk12.gray(" spfn key <preset>"));
3287
+ console.log(chalk12.gray(" spfn key auth-encryption --copy"));
3171
3288
  console.log();
3172
3289
  }
3173
- var generateValueCommand = new Command9("generate").alias("gen").description("Generate random value (simple output, no metadata)").option("-b, --bytes <number>", "Number of random bytes", "32").option("-c, --copy", "Copy to clipboard").action((options) => {
3290
+ var generateValueCommand = new Command10("generate").alias("gen").description("Generate random value (simple output, no metadata)").option("-b, --bytes <number>", "Number of random bytes", "32").option("-c, --copy", "Copy to clipboard").action((options) => {
3174
3291
  const bytes = parseInt(options.bytes, 10);
3175
3292
  if (isNaN(bytes) || bytes < 1 || bytes > 128) {
3176
3293
  logger.error("Invalid bytes value. Must be between 1 and 128.");
@@ -3180,13 +3297,13 @@ var generateValueCommand = new Command9("generate").alias("gen").description("Ge
3180
3297
  console.log(value);
3181
3298
  if (options.copy) {
3182
3299
  if (copyToClipboard(value)) {
3183
- console.error(chalk11.green("\u2713 Copied to clipboard"));
3300
+ console.error(chalk12.green("\u2713 Copied to clipboard"));
3184
3301
  } else {
3185
- console.error(chalk11.yellow("\u26A0 Could not copy to clipboard"));
3302
+ console.error(chalk12.yellow("\u26A0 Could not copy to clipboard"));
3186
3303
  }
3187
3304
  }
3188
3305
  });
3189
- var keyCommand = new Command9("key").alias("k").description("Generate secure random keys and secrets").argument("[preset]", `Preset type (use --list to see all)`).option("-l, --list", "List all available presets").option("-b, --bytes <number>", "Number of random bytes to generate", "32").option("-e, --env <name>", "Environment variable name").option("-c, --copy", "Copy to clipboard").action((preset, options) => {
3306
+ var keyCommand = new Command10("key").alias("k").description("Generate secure random keys and secrets").argument("[preset]", `Preset type (use --list to see all)`).option("-l, --list", "List all available presets").option("-b, --bytes <number>", "Number of random bytes to generate", "32").option("-e, --env <name>", "Environment variable name").option("-c, --copy", "Copy to clipboard").action((preset, options) => {
3190
3307
  if (options.list) {
3191
3308
  listPresets();
3192
3309
  return;
@@ -3200,9 +3317,9 @@ var keyCommand = new Command9("key").alias("k").description("Generate secure ran
3200
3317
  logger.error(`Unknown preset: ${preset}`);
3201
3318
  console.log("\nAvailable presets:");
3202
3319
  Object.entries(PRESETS).forEach(([name, config]) => {
3203
- console.log(` ${chalk11.cyan(name)}: ${config.description}`);
3320
+ console.log(` ${chalk12.cyan(name)}: ${config.description}`);
3204
3321
  });
3205
- console.log("\nUse " + chalk11.cyan("--list") + " to see detailed information");
3322
+ console.log("\nUse " + chalk12.cyan("--list") + " to see detailed information");
3206
3323
  process.exit(1);
3207
3324
  }
3208
3325
  generateSecret(
@@ -3218,16 +3335,16 @@ keyCommand.addCommand(generateValueCommand);
3218
3335
  init_setup();
3219
3336
 
3220
3337
  // src/commands/db/index.ts
3221
- import { Command as Command10 } from "commander";
3338
+ import { Command as Command11 } from "commander";
3222
3339
 
3223
3340
  // src/commands/db/generate.ts
3224
- import chalk13 from "chalk";
3341
+ import chalk14 from "chalk";
3225
3342
 
3226
3343
  // src/commands/db/utils/drizzle.ts
3227
3344
  import { existsSync as existsSync21, writeFileSync as writeFileSync14, unlinkSync as unlinkSync2 } from "fs";
3228
3345
  import { spawn } from "child_process";
3229
3346
  import { pathToFileURL as pathToFileURL2 } from "url";
3230
- import chalk12 from "chalk";
3347
+ import chalk13 from "chalk";
3231
3348
  import ora6 from "ora";
3232
3349
  import { env as env3 } from "@spfn/core/config";
3233
3350
  import { loadEnv as loadEnv3 } from "@spfn/core/server";
@@ -3285,8 +3402,8 @@ function resolvePushConnectionConfig(databaseUrl) {
3285
3402
  function validateDatabasePrerequisites() {
3286
3403
  loadEnv3();
3287
3404
  if (!env3.DATABASE_URL) {
3288
- console.error(chalk12.red("\u274C DATABASE_URL not found in environment"));
3289
- console.log(chalk12.yellow("\n\u{1F4A1} Tip: Add DATABASE_URL to your .env file"));
3405
+ console.error(chalk13.red("\u274C DATABASE_URL not found in environment"));
3406
+ console.log(chalk13.yellow("\n\u{1F4A1} Tip: Add DATABASE_URL to your .env file"));
3290
3407
  throw new Error("DATABASE_URL is required for database operations");
3291
3408
  }
3292
3409
  }
@@ -3297,8 +3414,8 @@ async function runDrizzleCommand(command) {
3297
3414
  if (!hasUserConfig) {
3298
3415
  loadEnv3();
3299
3416
  if (!env3.DATABASE_URL) {
3300
- console.error(chalk12.red("\u274C DATABASE_URL not found in environment"));
3301
- console.log(chalk12.yellow("\n\u{1F4A1} Tip: Add DATABASE_URL to your .env file"));
3417
+ console.error(chalk13.red("\u274C DATABASE_URL not found in environment"));
3418
+ console.log(chalk13.yellow("\n\u{1F4A1} Tip: Add DATABASE_URL to your .env file"));
3302
3419
  process.exit(1);
3303
3420
  }
3304
3421
  const { generateDrizzleConfigFile } = await import("@spfn/core/db");
@@ -3311,7 +3428,7 @@ async function runDrizzleCommand(command) {
3311
3428
  autoDetectSchemas: true
3312
3429
  });
3313
3430
  writeFileSync14(tempConfigPath, configContent);
3314
- console.log(chalk12.dim("Using auto-generated Drizzle config\n"));
3431
+ console.log(chalk13.dim("Using auto-generated Drizzle config\n"));
3315
3432
  }
3316
3433
  const args = command.split(" ");
3317
3434
  args.push(`--config=${configPath}`);
@@ -3346,10 +3463,10 @@ async function runWithSpinner(spinnerText, command, successMessage, failMessage)
3346
3463
  try {
3347
3464
  spinner.stop();
3348
3465
  await runDrizzleCommand(command);
3349
- console.log(chalk12.green(`\u2705 ${successMessage}`));
3466
+ console.log(chalk13.green(`\u2705 ${successMessage}`));
3350
3467
  } catch (error) {
3351
3468
  spinner.fail(failMessage);
3352
- console.error(chalk12.red(error instanceof Error ? error.message : "Unknown error"));
3469
+ console.error(chalk13.red(error instanceof Error ? error.message : "Unknown error"));
3353
3470
  process.exit(1);
3354
3471
  }
3355
3472
  }
@@ -3395,16 +3512,16 @@ async function createPushConnection() {
3395
3512
  async function dbGenerate() {
3396
3513
  try {
3397
3514
  await runDrizzleCommand("generate");
3398
- console.log(chalk13.green("\n\u2705 Migrations generated successfully"));
3515
+ console.log(chalk14.green("\n\u2705 Migrations generated successfully"));
3399
3516
  } catch (error) {
3400
- console.error(chalk13.red("\n\u274C Failed to generate migrations"));
3401
- console.error(chalk13.red(error instanceof Error ? error.message : "Unknown error"));
3517
+ console.error(chalk14.red("\n\u274C Failed to generate migrations"));
3518
+ console.error(chalk14.red(error instanceof Error ? error.message : "Unknown error"));
3402
3519
  process.exit(1);
3403
3520
  }
3404
3521
  }
3405
3522
 
3406
3523
  // src/commands/db/push.ts
3407
- import chalk15 from "chalk";
3524
+ import chalk16 from "chalk";
3408
3525
  import prompts4 from "prompts";
3409
3526
  import "@spfn/core/config";
3410
3527
  import { loadEnv as loadEnv4 } from "@spfn/core/server";
@@ -3451,7 +3568,7 @@ function classifyStatements(statements) {
3451
3568
  }
3452
3569
 
3453
3570
  // src/commands/db/utils/push-display.ts
3454
- import chalk14 from "chalk";
3571
+ import chalk15 from "chalk";
3455
3572
  function formatSql(sql2) {
3456
3573
  return sql2.replace(/\s+/g, " ").trim();
3457
3574
  }
@@ -3459,30 +3576,30 @@ function printStatements(statements, color) {
3459
3576
  for (const stmt of statements) {
3460
3577
  console.log(color(` ${formatSql(stmt.sql)}`));
3461
3578
  if (stmt.reason) {
3462
- console.log(chalk14.dim(` \u2192 ${stmt.reason}`));
3579
+ console.log(chalk15.dim(` \u2192 ${stmt.reason}`));
3463
3580
  }
3464
3581
  }
3465
3582
  }
3466
3583
  function displayClassifiedStatements(result) {
3467
3584
  if (result.safe.length > 0) {
3468
- console.log(chalk14.green(`
3585
+ console.log(chalk15.green(`
3469
3586
  \u2705 Safe changes (${result.safe.length}):`));
3470
- printStatements(result.safe, chalk14.green);
3587
+ printStatements(result.safe, chalk15.green);
3471
3588
  }
3472
3589
  if (result.warning.length > 0) {
3473
- console.log(chalk14.yellow(`
3590
+ console.log(chalk15.yellow(`
3474
3591
  \u26A0\uFE0F Warnings (${result.warning.length}):`));
3475
- printStatements(result.warning, chalk14.yellow);
3592
+ printStatements(result.warning, chalk15.yellow);
3476
3593
  }
3477
3594
  if (result.destructive.length > 0) {
3478
- console.log(chalk14.red(`
3595
+ console.log(chalk15.red(`
3479
3596
  \u274C Destructive changes (${result.destructive.length}):`));
3480
- printStatements(result.destructive, chalk14.red);
3597
+ printStatements(result.destructive, chalk15.red);
3481
3598
  }
3482
3599
  }
3483
3600
  function displayDryRunSummary(result) {
3484
3601
  const total = result.safe.length + result.warning.length + result.destructive.length;
3485
- console.log(chalk14.cyan(`
3602
+ console.log(chalk15.cyan(`
3486
3603
  \u{1F50D} Dry-run: ${total} statement(s) detected
3487
3604
  `));
3488
3605
  displayClassifiedStatements(result);
@@ -3490,11 +3607,11 @@ function displayDryRunSummary(result) {
3490
3607
  }
3491
3608
  function displayApplySummary(applied, skipped) {
3492
3609
  if (applied > 0) {
3493
- console.log(chalk14.green(`
3610
+ console.log(chalk15.green(`
3494
3611
  \u2705 Applied ${applied} statement(s)`));
3495
3612
  }
3496
3613
  if (skipped > 0) {
3497
- console.log(chalk14.yellow(`\u23ED\uFE0F Skipped ${skipped} destructive statement(s)`));
3614
+ console.log(chalk15.yellow(`\u23ED\uFE0F Skipped ${skipped} destructive statement(s)`));
3498
3615
  }
3499
3616
  console.log("");
3500
3617
  }
@@ -3539,10 +3656,10 @@ async function dbPush(options = {}) {
3539
3656
  });
3540
3657
  const schemaFiles = Array.isArray(config.schema) ? config.schema : [config.schema];
3541
3658
  if (schemaFiles.length === 0) {
3542
- console.log(chalk15.yellow("No schema files found."));
3659
+ console.log(chalk16.yellow("No schema files found."));
3543
3660
  return;
3544
3661
  }
3545
- console.log(chalk15.dim(`Found ${schemaFiles.length} schema file(s)
3662
+ console.log(chalk16.dim(`Found ${schemaFiles.length} schema file(s)
3546
3663
  `));
3547
3664
  const imports = await loadSchemaImports(schemaFiles);
3548
3665
  const detectedSchemas = new Set(config.schemaFilter ?? ["public"]);
@@ -3561,10 +3678,10 @@ async function dbPush(options = {}) {
3561
3678
  try {
3562
3679
  const { statements, hints } = await resolvePushPlan(imports, db, schemaFilter);
3563
3680
  for (const hint of hints) {
3564
- console.log(chalk15.yellow(`\u26A0\uFE0F ${hint.hint}`));
3681
+ console.log(chalk16.yellow(`\u26A0\uFE0F ${hint.hint}`));
3565
3682
  }
3566
3683
  if (statements.length === 0) {
3567
- console.log(chalk15.green("\u2705 No changes detected \u2014 database is up to date\n"));
3684
+ console.log(chalk16.green("\u2705 No changes detected \u2014 database is up to date\n"));
3568
3685
  await applyFunctionMigrations(functionPlans);
3569
3686
  return;
3570
3687
  }
@@ -3575,7 +3692,7 @@ async function dbPush(options = {}) {
3575
3692
  }
3576
3693
  displayClassifiedStatements(result);
3577
3694
  if (options.force) {
3578
- console.log(chalk15.dim("\n--force: applying all changes..."));
3695
+ console.log(chalk16.dim("\n--force: applying all changes..."));
3579
3696
  await applyStatements(db, statements);
3580
3697
  displayApplySummary(statements.length, 0);
3581
3698
  } else if (result.destructive.length === 0) {
@@ -3584,11 +3701,11 @@ async function dbPush(options = {}) {
3584
3701
  } else {
3585
3702
  const destructiveSet = new Set(result.destructive.map((s) => s.sql));
3586
3703
  const nonDestructive = statements.filter((s) => !destructiveSet.has(s));
3587
- console.log(chalk15.red(`
3704
+ console.log(chalk16.red(`
3588
3705
  \u274C ${result.destructive.length} destructive change(s) require confirmation:`));
3589
3706
  for (const stmt of result.destructive) {
3590
- console.log(chalk15.red(` ${stmt.sql.replace(/\s+/g, " ").trim()}`));
3591
- console.log(chalk15.dim(` \u2192 ${stmt.reason}`));
3707
+ console.log(chalk16.red(` ${stmt.sql.replace(/\s+/g, " ").trim()}`));
3708
+ console.log(chalk16.dim(` \u2192 ${stmt.reason}`));
3592
3709
  }
3593
3710
  const { confirm } = await prompts4({
3594
3711
  type: "confirm",
@@ -3604,7 +3721,7 @@ async function dbPush(options = {}) {
3604
3721
  await applyStatements(db, nonDestructive);
3605
3722
  }
3606
3723
  displayApplySummary(nonDestructive.length, result.destructive.length);
3607
- console.log(chalk15.dim("Tip: Use --force to apply all changes without prompting.\n"));
3724
+ console.log(chalk16.dim("Tip: Use --force to apply all changes without prompting.\n"));
3608
3725
  }
3609
3726
  }
3610
3727
  await applyFunctionMigrations(functionPlans);
@@ -3617,8 +3734,8 @@ function loadFunctionPlansOrExit() {
3617
3734
  try {
3618
3735
  return loadFunctionMigrationPlans(functions);
3619
3736
  } catch (error) {
3620
- console.error(chalk15.red("\n\u274C Invalid function package migrations \u2014 nothing was applied"));
3621
- console.error(chalk15.red(error instanceof Error ? error.message : "Unknown error"));
3737
+ console.error(chalk16.red("\n\u274C Invalid function package migrations \u2014 nothing was applied"));
3738
+ console.error(chalk16.red(error instanceof Error ? error.message : "Unknown error"));
3622
3739
  process.exit(1);
3623
3740
  }
3624
3741
  }
@@ -3626,36 +3743,36 @@ async function applyFunctionMigrations(plans) {
3626
3743
  if (plans.length === 0) {
3627
3744
  return;
3628
3745
  }
3629
- console.log(chalk15.blue("\n\u{1F4E6} Applying function package migrations:"));
3746
+ console.log(chalk16.blue("\n\u{1F4E6} Applying function package migrations:"));
3630
3747
  plans.forEach((plan) => {
3631
- console.log(chalk15.dim(` - ${plan.packageName}`));
3748
+ console.log(chalk16.dim(` - ${plan.packageName}`));
3632
3749
  });
3633
3750
  try {
3634
3751
  await executeFunctionMigrations(plans);
3635
- console.log(chalk15.green("\n\u2705 All function migrations applied\n"));
3752
+ console.log(chalk16.green("\n\u2705 All function migrations applied\n"));
3636
3753
  } catch (error) {
3637
- console.error(chalk15.red("\n\u274C Failed to apply function package migrations"));
3638
- console.error(chalk15.red(error instanceof Error ? error.message : "Unknown error"));
3639
- console.error(chalk15.yellow("Project schema changes (if any) were already applied \u2014 only function package migrations failed."));
3754
+ console.error(chalk16.red("\n\u274C Failed to apply function package migrations"));
3755
+ console.error(chalk16.red(error instanceof Error ? error.message : "Unknown error"));
3756
+ console.error(chalk16.yellow("Project schema changes (if any) were already applied \u2014 only function package migrations failed."));
3640
3757
  process.exit(1);
3641
3758
  }
3642
3759
  }
3643
3760
 
3644
3761
  // src/commands/db/migrate.ts
3645
- import chalk20 from "chalk";
3646
- import { join as join22 } from "path";
3762
+ import chalk21 from "chalk";
3763
+ import { join as join23 } from "path";
3647
3764
  import { existsSync as existsSync23 } from "fs";
3648
3765
 
3649
3766
  // src/commands/db/backup.ts
3650
3767
  import { promises as fs3 } from "fs";
3651
3768
  import path3 from "path";
3652
3769
  import { spawn as spawn2 } from "child_process";
3653
- import chalk19 from "chalk";
3770
+ import chalk20 from "chalk";
3654
3771
  import ora7 from "ora";
3655
3772
 
3656
3773
  // src/commands/db/utils/database.ts
3657
3774
  import net from "net";
3658
- import chalk16 from "chalk";
3775
+ import chalk17 from "chalk";
3659
3776
  import prompts5 from "prompts";
3660
3777
  function parseDatabaseUrl2(dbUrl) {
3661
3778
  try {
@@ -3686,7 +3803,7 @@ async function confirmDangerousTarget(dbInfo) {
3686
3803
  message: `Remote/production database detected. Type the database name "${dbInfo.database}" to continue:`
3687
3804
  });
3688
3805
  if (typed !== dbInfo.database) {
3689
- console.log(chalk16.gray("Cancelled (database name mismatch)."));
3806
+ console.log(chalk17.gray("Cancelled (database name mismatch)."));
3690
3807
  process.exit(0);
3691
3808
  }
3692
3809
  }
@@ -3739,14 +3856,14 @@ function formatTimestamp() {
3739
3856
  import { promises as fs2 } from "fs";
3740
3857
  import { existsSync as existsSync22 } from "fs";
3741
3858
  import path2 from "path";
3742
- import chalk18 from "chalk";
3859
+ import chalk19 from "chalk";
3743
3860
 
3744
3861
  // src/commands/db/utils/metadata.ts
3745
3862
  import { promises as fs } from "fs";
3746
3863
  import path from "path";
3747
3864
  import { promisify } from "util";
3748
3865
  import { exec } from "child_process";
3749
- import chalk17 from "chalk";
3866
+ import chalk18 from "chalk";
3750
3867
  var execAsync = promisify(exec);
3751
3868
  async function collectGitInfo() {
3752
3869
  try {
@@ -3809,7 +3926,7 @@ async function collectMigrationInfo(dbUrl) {
3809
3926
  await pool.end();
3810
3927
  }
3811
3928
  } catch (error) {
3812
- console.log(chalk17.dim("\u26A0\uFE0F Could not fetch migration info"));
3929
+ console.log(chalk18.dim("\u26A0\uFE0F Could not fetch migration info"));
3813
3930
  return void 0;
3814
3931
  }
3815
3932
  }
@@ -3817,9 +3934,9 @@ async function saveBackupMetadata(metadata, backupFilename) {
3817
3934
  const metadataPath = backupFilename.replace(/\.(sql|dump)$/, ".meta.json");
3818
3935
  try {
3819
3936
  await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
3820
- console.log(chalk17.dim(`\u2713 Metadata saved: ${path.basename(metadataPath)}`));
3937
+ console.log(chalk18.dim(`\u2713 Metadata saved: ${path.basename(metadataPath)}`));
3821
3938
  } catch (error) {
3822
- console.log(chalk17.dim("\u26A0\uFE0F Could not save metadata"));
3939
+ console.log(chalk18.dim("\u26A0\uFE0F Could not save metadata"));
3823
3940
  }
3824
3941
  }
3825
3942
  async function loadBackupMetadata(backupFilename) {
@@ -3848,10 +3965,10 @@ async function ensureBackupInGitignore() {
3848
3965
  if (!hasBackupsIgnore) {
3849
3966
  const entry = exists && content && !content.endsWith("\n") ? "\n\n# Database backups\nbackups/\n" : "# Database backups\nbackups/\n";
3850
3967
  await fs2.appendFile(gitignorePath, entry);
3851
- console.log(chalk18.dim("\u2713 Added backups/ to .gitignore"));
3968
+ console.log(chalk19.dim("\u2713 Added backups/ to .gitignore"));
3852
3969
  }
3853
3970
  } catch (error) {
3854
- console.log(chalk18.dim("\u26A0\uFE0F Could not update .gitignore"));
3971
+ console.log(chalk19.dim("\u26A0\uFE0F Could not update .gitignore"));
3855
3972
  }
3856
3973
  }
3857
3974
  async function ensureBackupDir() {
@@ -3901,12 +4018,12 @@ async function listBackupFiles() {
3901
4018
  import { env as env4 } from "@spfn/core/config";
3902
4019
  import { loadEnv as loadEnv5 } from "@spfn/core/server";
3903
4020
  async function dbBackup(options) {
3904
- console.log(chalk19.blue("\u{1F4BE} Creating database backup...\n"));
4021
+ console.log(chalk20.blue("\u{1F4BE} Creating database backup...\n"));
3905
4022
  loadEnv5();
3906
4023
  const dbUrl = env4.DATABASE_URL;
3907
4024
  if (!dbUrl) {
3908
- console.error(chalk19.red("\u274C DATABASE_URL not found in environment"));
3909
- console.log(chalk19.yellow("\n\u{1F4A1} Tip: Add DATABASE_URL to your .env file"));
4025
+ console.error(chalk20.red("\u274C DATABASE_URL not found in environment"));
4026
+ console.log(chalk20.yellow("\n\u{1F4A1} Tip: Add DATABASE_URL to your .env file"));
3910
4027
  process.exit(1);
3911
4028
  }
3912
4029
  const dbInfo = parseDatabaseUrl2(dbUrl);
@@ -3916,7 +4033,7 @@ async function dbBackup(options) {
3916
4033
  const ext = format === "sql" ? "sql" : "dump";
3917
4034
  const filename = options.output || path3.join(backupDir, `${dbInfo.database}_${timestamp}.${ext}`);
3918
4035
  if (options.dataOnly && options.schemaOnly) {
3919
- console.error(chalk19.red("\u274C Cannot use --data-only and --schema-only together"));
4036
+ console.error(chalk20.red("\u274C Cannot use --data-only and --schema-only together"));
3920
4037
  process.exit(1);
3921
4038
  }
3922
4039
  const args = [
@@ -3962,11 +4079,11 @@ async function dbBackup(options) {
3962
4079
  const stats = await fs3.stat(filename);
3963
4080
  const size = formatBytes(stats.size);
3964
4081
  spinner.succeed("Backup created");
3965
- console.log(chalk19.green(`
4082
+ console.log(chalk20.green(`
3966
4083
  \u2705 Backup created successfully`));
3967
- console.log(chalk19.gray(` File: ${filename}`));
3968
- console.log(chalk19.gray(` Size: ${size}`));
3969
- console.log(chalk19.dim("\n\u{1F4CB} Collecting metadata..."));
4084
+ console.log(chalk20.gray(` File: ${filename}`));
4085
+ console.log(chalk20.gray(` Size: ${size}`));
4086
+ console.log(chalk20.dim("\n\u{1F4CB} Collecting metadata..."));
3970
4087
  const [gitInfo, migrationInfo] = await Promise.all([
3971
4088
  collectGitInfo(),
3972
4089
  collectMigrationInfo(dbUrl)
@@ -4006,11 +4123,11 @@ async function dbBackup(options) {
4006
4123
  reject(error);
4007
4124
  });
4008
4125
  }).catch((error) => {
4009
- console.error(chalk19.red("\n\u274C Failed to create backup"));
4126
+ console.error(chalk20.red("\n\u274C Failed to create backup"));
4010
4127
  if (errorOutput.includes("pg_dump: command not found") || errorOutput.includes("not found")) {
4011
- console.error(chalk19.yellow("\n\u{1F4A1} pg_dump is not installed. Please install PostgreSQL client tools."));
4128
+ console.error(chalk20.yellow("\n\u{1F4A1} pg_dump is not installed. Please install PostgreSQL client tools."));
4012
4129
  } else {
4013
- console.error(chalk19.red(error instanceof Error ? error.message : "Unknown error"));
4130
+ console.error(chalk20.red(error instanceof Error ? error.message : "Unknown error"));
4014
4131
  }
4015
4132
  process.exit(1);
4016
4133
  });
@@ -4027,7 +4144,7 @@ async function dbMigrate(options = {}) {
4027
4144
  process.exit(1);
4028
4145
  }
4029
4146
  if (options.withBackup) {
4030
- console.log(chalk20.blue("\u{1F4E6} Creating pre-migration backup...\n"));
4147
+ console.log(chalk21.blue("\u{1F4E6} Creating pre-migration backup...\n"));
4031
4148
  await dbBackup({
4032
4149
  format: "custom",
4033
4150
  tag: "pre-migration",
@@ -4040,40 +4157,40 @@ async function dbMigrate(options = {}) {
4040
4157
  const postgres = await import("postgres");
4041
4158
  loadEnv6();
4042
4159
  if (!env5.DATABASE_URL) {
4043
- console.error(chalk20.red("\u274C DATABASE_URL not found in environment"));
4160
+ console.error(chalk21.red("\u274C DATABASE_URL not found in environment"));
4044
4161
  process.exit(1);
4045
4162
  }
4046
4163
  const { discoverFunctionMigrations: discoverFunctionMigrations2, loadFunctionMigrationPlans: loadFunctionMigrationPlans2, executeFunctionMigrations: executeFunctionMigrations2 } = await Promise.resolve().then(() => (init_function_migrations(), function_migrations_exports));
4047
4164
  const functions = discoverFunctionMigrations2(process.cwd());
4048
4165
  if (functions.length > 0) {
4049
- console.log(chalk20.blue("\u{1F4E6} Applying function package migrations:"));
4166
+ console.log(chalk21.blue("\u{1F4E6} Applying function package migrations:"));
4050
4167
  functions.forEach((func) => {
4051
- console.log(chalk20.dim(` - ${func.packageName}`));
4168
+ console.log(chalk21.dim(` - ${func.packageName}`));
4052
4169
  });
4053
4170
  await executeFunctionMigrations2(loadFunctionMigrationPlans2(functions));
4054
- console.log(chalk20.green("\u2705 Function migrations applied\n"));
4171
+ console.log(chalk21.green("\u2705 Function migrations applied\n"));
4055
4172
  }
4056
- const projectMigrationsDir = join22(process.cwd(), "src/server/drizzle");
4173
+ const projectMigrationsDir = join23(process.cwd(), "src/server/drizzle");
4057
4174
  if (existsSync23(projectMigrationsDir)) {
4058
4175
  const projConn = postgres.default(env5.DATABASE_URL, { max: 1 });
4059
4176
  const projDb = drizzle({ client: projConn });
4060
4177
  try {
4061
- console.log(chalk20.blue("\u{1F4E6} Running project migrations..."));
4178
+ console.log(chalk21.blue("\u{1F4E6} Running project migrations..."));
4062
4179
  await migrate(projDb, {
4063
4180
  migrationsFolder: projectMigrationsDir,
4064
4181
  migrationsTable: PROJECT_MIGRATIONS_TABLE
4065
4182
  });
4066
- console.log(chalk20.green("\u2705 Project migrations applied successfully"));
4183
+ console.log(chalk21.green("\u2705 Project migrations applied successfully"));
4067
4184
  } finally {
4068
4185
  await projConn.end();
4069
4186
  }
4070
4187
  } else {
4071
- console.log(chalk20.dim("No project migrations found (src/server/drizzle)"));
4188
+ console.log(chalk21.dim("No project migrations found (src/server/drizzle)"));
4072
4189
  }
4073
4190
  }
4074
4191
 
4075
4192
  // src/commands/db/status.ts
4076
- import chalk21 from "chalk";
4193
+ import chalk22 from "chalk";
4077
4194
  init_migration_status();
4078
4195
  async function dbStatus() {
4079
4196
  try {
@@ -4085,51 +4202,51 @@ async function dbStatus() {
4085
4202
  try {
4086
4203
  status = await getMigrationStatus(process.cwd());
4087
4204
  } catch (error) {
4088
- console.error(chalk21.red("\u274C Failed to read migration status"));
4089
- console.error(chalk21.red(error instanceof Error ? error.message : "Unknown error"));
4205
+ console.error(chalk22.red("\u274C Failed to read migration status"));
4206
+ console.error(chalk22.red(error instanceof Error ? error.message : "Unknown error"));
4090
4207
  process.exit(1);
4091
4208
  }
4092
4209
  const targets = [...status.packages, ...status.project ? [status.project] : []];
4093
4210
  if (targets.length === 0) {
4094
- console.log(chalk21.dim("No migrations found (no @spfn packages with migrations, no src/server/drizzle)."));
4211
+ console.log(chalk22.dim("No migrations found (no @spfn packages with migrations, no src/server/drizzle)."));
4095
4212
  return;
4096
4213
  }
4097
- console.log(chalk21.blue("\u{1F4E6} Migration status:\n"));
4214
+ console.log(chalk22.blue("\u{1F4E6} Migration status:\n"));
4098
4215
  for (const target of targets) {
4099
- const state = target.pending === 0 ? chalk21.green(`${target.applied}/${target.total} applied`) : chalk21.yellow(`${target.applied}/${target.total} applied, ${target.pending} pending`);
4216
+ const state = target.pending === 0 ? chalk22.green(`${target.applied}/${target.total} applied`) : chalk22.yellow(`${target.applied}/${target.total} applied, ${target.pending} pending`);
4100
4217
  console.log(` ${target.name.padEnd(32)} ${state}`);
4101
4218
  for (const tag of target.pendingTags) {
4102
- console.log(chalk21.dim(` - ${tag}`));
4219
+ console.log(chalk22.dim(` - ${tag}`));
4103
4220
  }
4104
4221
  }
4105
4222
  const pendingTotal = targets.reduce((sum, t) => sum + t.pending, 0);
4106
4223
  if (pendingTotal === 0) {
4107
- console.log(chalk21.green("\n\u2705 Database is up to date\n"));
4224
+ console.log(chalk22.green("\n\u2705 Database is up to date\n"));
4108
4225
  } else {
4109
- console.log(chalk21.yellow(`
4226
+ console.log(chalk22.yellow(`
4110
4227
  \u26A0\uFE0F ${pendingTotal} pending migration(s) \u2014 run: pnpm spfn db migrate
4111
4228
  `));
4112
4229
  }
4113
4230
  }
4114
4231
 
4115
4232
  // src/commands/db/studio.ts
4116
- import chalk22 from "chalk";
4233
+ import chalk23 from "chalk";
4117
4234
  import { existsSync as existsSync24, writeFileSync as writeFileSync15, unlinkSync as unlinkSync3 } from "fs";
4118
4235
  import { spawn as spawn3 } from "child_process";
4119
4236
  import { env as env6 } from "@spfn/core/config";
4120
4237
  async function dbStudio(requestedPort) {
4121
- console.log(chalk22.blue("\u{1F3A8} Opening Drizzle Studio...\n"));
4238
+ console.log(chalk23.blue("\u{1F3A8} Opening Drizzle Studio...\n"));
4122
4239
  const defaultPort = 4983;
4123
4240
  const startPort = requestedPort || defaultPort;
4124
4241
  let port;
4125
4242
  try {
4126
4243
  port = await findAvailablePort(startPort);
4127
4244
  if (port !== startPort) {
4128
- console.log(chalk22.yellow(`\u26A0\uFE0F Port ${startPort} is in use, using port ${port} instead
4245
+ console.log(chalk23.yellow(`\u26A0\uFE0F Port ${startPort} is in use, using port ${port} instead
4129
4246
  `));
4130
4247
  }
4131
4248
  } catch (error) {
4132
- console.error(chalk22.red(error instanceof Error ? error.message : "Failed to find available port"));
4249
+ console.error(chalk23.red(error instanceof Error ? error.message : "Failed to find available port"));
4133
4250
  process.exit(1);
4134
4251
  }
4135
4252
  const hasUserConfig = existsSync24("./drizzle.config.ts");
@@ -4138,8 +4255,8 @@ async function dbStudio(requestedPort) {
4138
4255
  const configPath = hasUserConfig ? "./drizzle.config.ts" : tempConfigPath;
4139
4256
  if (!hasUserConfig) {
4140
4257
  if (!env6.DATABASE_URL) {
4141
- console.error(chalk22.red("\u274C DATABASE_URL not found in environment"));
4142
- console.log(chalk22.yellow("\n\u{1F4A1} Tip: Add DATABASE_URL to your .env file"));
4258
+ console.error(chalk23.red("\u274C DATABASE_URL not found in environment"));
4259
+ console.log(chalk23.yellow("\n\u{1F4A1} Tip: Add DATABASE_URL to your .env file"));
4143
4260
  process.exit(1);
4144
4261
  }
4145
4262
  const { generateDrizzleConfigFile } = await import("@spfn/core/db");
@@ -4150,7 +4267,7 @@ async function dbStudio(requestedPort) {
4150
4267
  // Expand glob patterns for Studio compatibility
4151
4268
  });
4152
4269
  writeFileSync15(tempConfigPath, configContent);
4153
- console.log(chalk22.dim("Using auto-generated Drizzle config\n"));
4270
+ console.log(chalk23.dim("Using auto-generated Drizzle config\n"));
4154
4271
  }
4155
4272
  const studioProcess = spawn3("drizzle-kit", ["studio", `--port=${port}`, `--config=${configPath}`], {
4156
4273
  stdio: "inherit",
@@ -4165,19 +4282,19 @@ async function dbStudio(requestedPort) {
4165
4282
  studioProcess.on("exit", (code) => {
4166
4283
  cleanup();
4167
4284
  if (code !== 0 && code !== null) {
4168
- console.error(chalk22.red(`
4285
+ console.error(chalk23.red(`
4169
4286
  \u274C Drizzle Studio exited with code ${code}`));
4170
4287
  process.exit(code);
4171
4288
  }
4172
4289
  });
4173
4290
  studioProcess.on("error", (error) => {
4174
4291
  cleanup();
4175
- console.error(chalk22.red("\u274C Failed to start Drizzle Studio"));
4176
- console.error(chalk22.red(error.message));
4292
+ console.error(chalk23.red("\u274C Failed to start Drizzle Studio"));
4293
+ console.error(chalk23.red(error.message));
4177
4294
  process.exit(1);
4178
4295
  });
4179
4296
  process.on("SIGINT", () => {
4180
- console.log(chalk22.yellow("\n\n\u{1F44B} Shutting down Drizzle Studio..."));
4297
+ console.log(chalk23.yellow("\n\n\u{1F44B} Shutting down Drizzle Studio..."));
4181
4298
  studioProcess.kill("SIGTERM");
4182
4299
  cleanup();
4183
4300
  process.exit(0);
@@ -4191,27 +4308,27 @@ async function dbStudio(requestedPort) {
4191
4308
  if (!hasUserConfig && existsSync24(tempConfigPath)) {
4192
4309
  unlinkSync3(tempConfigPath);
4193
4310
  }
4194
- console.error(chalk22.red("\u274C Failed to start Drizzle Studio"));
4195
- console.error(chalk22.red(error instanceof Error ? error.message : "Unknown error"));
4311
+ console.error(chalk23.red("\u274C Failed to start Drizzle Studio"));
4312
+ console.error(chalk23.red(error instanceof Error ? error.message : "Unknown error"));
4196
4313
  process.exit(1);
4197
4314
  }
4198
4315
  }
4199
4316
 
4200
4317
  // src/commands/db/drop.ts
4201
- import chalk23 from "chalk";
4318
+ import chalk24 from "chalk";
4202
4319
  import prompts6 from "prompts";
4203
4320
  import { env as env7 } from "@spfn/core/config";
4204
4321
  import { loadEnv as loadEnv7 } from "@spfn/core/server";
4205
4322
  async function dbDrop() {
4206
4323
  loadEnv7();
4207
4324
  if (!env7.DATABASE_URL) {
4208
- console.error(chalk23.red("\u274C DATABASE_URL not found in environment"));
4209
- console.log(chalk23.yellow("\n\u{1F4A1} Tip: Add DATABASE_URL to your .env file"));
4325
+ console.error(chalk24.red("\u274C DATABASE_URL not found in environment"));
4326
+ console.log(chalk24.yellow("\n\u{1F4A1} Tip: Add DATABASE_URL to your .env file"));
4210
4327
  process.exit(1);
4211
4328
  }
4212
4329
  const dbInfo = parseDatabaseUrl2(env7.DATABASE_URL);
4213
- console.log(chalk23.yellow("\u26A0\uFE0F WARNING: This will drop all tables in your database!"));
4214
- console.log(chalk23.dim(` Target: ${dbInfo.database} @ ${dbInfo.host}:${dbInfo.port}
4330
+ console.log(chalk24.yellow("\u26A0\uFE0F WARNING: This will drop all tables in your database!"));
4331
+ console.log(chalk24.dim(` Target: ${dbInfo.database} @ ${dbInfo.host}:${dbInfo.port}
4215
4332
  `));
4216
4333
  const { confirm } = await prompts6({
4217
4334
  type: "confirm",
@@ -4220,7 +4337,7 @@ async function dbDrop() {
4220
4337
  initial: false
4221
4338
  });
4222
4339
  if (!confirm) {
4223
- console.log(chalk23.gray("Cancelled."));
4340
+ console.log(chalk24.gray("Cancelled."));
4224
4341
  process.exit(0);
4225
4342
  }
4226
4343
  await confirmDangerousTarget(dbInfo);
@@ -4233,7 +4350,7 @@ async function dbDrop() {
4233
4350
  }
4234
4351
 
4235
4352
  // src/commands/db/check.ts
4236
- import chalk24 from "chalk";
4353
+ import chalk25 from "chalk";
4237
4354
  import ora8 from "ora";
4238
4355
  async function dbCheck() {
4239
4356
  const spinner = ora8("Checking migration files...").start();
@@ -4242,7 +4359,7 @@ async function dbCheck() {
4242
4359
  spinner.succeed("Migration files consistent");
4243
4360
  } catch (error) {
4244
4361
  spinner.fail("Migration file check failed");
4245
- console.error(chalk24.red(error instanceof Error ? error.message : "Unknown error"));
4362
+ console.error(chalk25.red(error instanceof Error ? error.message : "Unknown error"));
4246
4363
  process.exit(1);
4247
4364
  }
4248
4365
  }
@@ -4250,25 +4367,25 @@ async function dbCheck() {
4250
4367
  // src/commands/db/restore.ts
4251
4368
  import path4 from "path";
4252
4369
  import { spawn as spawn4 } from "child_process";
4253
- import chalk25 from "chalk";
4370
+ import chalk26 from "chalk";
4254
4371
  import ora9 from "ora";
4255
4372
  import prompts7 from "prompts";
4256
4373
  import { env as env8 } from "@spfn/core/config";
4257
4374
  import { loadEnv as loadEnv8 } from "@spfn/core/server";
4258
4375
  async function dbRestore(backupFile, options = {}) {
4259
- console.log(chalk25.blue("\u267B\uFE0F Restoring database from backup...\n"));
4376
+ console.log(chalk26.blue("\u267B\uFE0F Restoring database from backup...\n"));
4260
4377
  loadEnv8();
4261
4378
  const dbUrl = env8.DATABASE_URL;
4262
4379
  if (!dbUrl) {
4263
- console.error(chalk25.red("\u274C DATABASE_URL not found in environment"));
4264
- console.log(chalk25.yellow("\n\u{1F4A1} Tip: Add DATABASE_URL to your .env file"));
4380
+ console.error(chalk26.red("\u274C DATABASE_URL not found in environment"));
4381
+ console.log(chalk26.yellow("\n\u{1F4A1} Tip: Add DATABASE_URL to your .env file"));
4265
4382
  process.exit(1);
4266
4383
  }
4267
4384
  let file = backupFile;
4268
4385
  if (!file) {
4269
4386
  const backups = await listBackupFiles();
4270
4387
  if (backups.length === 0) {
4271
- console.log(chalk25.yellow("No backups found in ./backups directory"));
4388
+ console.log(chalk26.yellow("No backups found in ./backups directory"));
4272
4389
  process.exit(0);
4273
4390
  }
4274
4391
  const { selected } = await prompts7({
@@ -4281,31 +4398,31 @@ async function dbRestore(backupFile, options = {}) {
4281
4398
  }))
4282
4399
  });
4283
4400
  if (!selected) {
4284
- console.log(chalk25.gray("Cancelled"));
4401
+ console.log(chalk26.gray("Cancelled"));
4285
4402
  process.exit(0);
4286
4403
  }
4287
4404
  file = selected;
4288
4405
  }
4289
4406
  if (!file) {
4290
- console.error(chalk25.red("\u274C No backup file selected"));
4407
+ console.error(chalk26.red("\u274C No backup file selected"));
4291
4408
  process.exit(1);
4292
4409
  }
4293
4410
  const metadata = await loadBackupMetadata(file);
4294
4411
  if (metadata) {
4295
- console.log(chalk25.blue("\n\u{1F4CB} Backup Information:\n"));
4296
- console.log(chalk25.dim(` Database: ${metadata.database}`));
4297
- console.log(chalk25.dim(` Created: ${new Date(metadata.timestamp).toLocaleString()}`));
4412
+ console.log(chalk26.blue("\n\u{1F4CB} Backup Information:\n"));
4413
+ console.log(chalk26.dim(` Database: ${metadata.database}`));
4414
+ console.log(chalk26.dim(` Created: ${new Date(metadata.timestamp).toLocaleString()}`));
4298
4415
  if (metadata.environment) {
4299
- console.log(chalk25.dim(` Environment: ${metadata.environment}`));
4416
+ console.log(chalk26.dim(` Environment: ${metadata.environment}`));
4300
4417
  }
4301
4418
  if (metadata.tags && metadata.tags.length > 0) {
4302
- console.log(chalk25.dim(` Tags: ${metadata.tags.join(", ")}`));
4419
+ console.log(chalk26.dim(` Tags: ${metadata.tags.join(", ")}`));
4303
4420
  }
4304
4421
  if (metadata.backup.dataOnly) {
4305
- console.log(chalk25.yellow(" \u26A0\uFE0F Data-only backup (no schema)"));
4422
+ console.log(chalk26.yellow(" \u26A0\uFE0F Data-only backup (no schema)"));
4306
4423
  }
4307
4424
  if (metadata.backup.schemaOnly) {
4308
- console.log(chalk25.yellow(" \u26A0\uFE0F Schema-only backup (no data)"));
4425
+ console.log(chalk26.yellow(" \u26A0\uFE0F Schema-only backup (no data)"));
4309
4426
  }
4310
4427
  const warnings2 = [];
4311
4428
  const [currentGitInfo, currentMigrationInfo] = await Promise.all([
@@ -4328,8 +4445,8 @@ async function dbRestore(backupFile, options = {}) {
4328
4445
  }
4329
4446
  }
4330
4447
  if (warnings2.length > 0) {
4331
- console.log(chalk25.yellow("\n\u26A0\uFE0F Version Warnings:\n"));
4332
- warnings2.forEach((warning) => console.log(chalk25.yellow(` - ${warning}`)));
4448
+ console.log(chalk26.yellow("\n\u26A0\uFE0F Version Warnings:\n"));
4449
+ warnings2.forEach((warning) => console.log(chalk26.yellow(` - ${warning}`)));
4333
4450
  console.log("");
4334
4451
  }
4335
4452
  }
@@ -4337,16 +4454,16 @@ async function dbRestore(backupFile, options = {}) {
4337
4454
  const { confirm } = await prompts7({
4338
4455
  type: "confirm",
4339
4456
  name: "confirm",
4340
- message: chalk25.yellow(`\u26A0\uFE0F This will replace all data in "${dbInfo.database}" @ ${dbInfo.host}:${dbInfo.port}. Continue?`),
4457
+ message: chalk26.yellow(`\u26A0\uFE0F This will replace all data in "${dbInfo.database}" @ ${dbInfo.host}:${dbInfo.port}. Continue?`),
4341
4458
  initial: false
4342
4459
  });
4343
4460
  if (!confirm) {
4344
- console.log(chalk25.gray("Cancelled"));
4461
+ console.log(chalk26.gray("Cancelled"));
4345
4462
  process.exit(0);
4346
4463
  }
4347
4464
  await confirmDangerousTarget(dbInfo);
4348
4465
  if (options.dataOnly && options.schemaOnly) {
4349
- console.error(chalk25.red("\u274C Cannot use --data-only and --schema-only together"));
4466
+ console.error(chalk26.red("\u274C Cannot use --data-only and --schema-only together"));
4350
4467
  process.exit(1);
4351
4468
  }
4352
4469
  const ext = path4.extname(file);
@@ -4374,11 +4491,11 @@ async function dbRestore(backupFile, options = {}) {
4374
4491
  args.push(file);
4375
4492
  } else {
4376
4493
  if (options.drop) {
4377
- console.log(chalk25.yellow("\u26A0\uFE0F Note: --drop only works with custom format backups (.dump) and was ignored."));
4494
+ console.log(chalk26.yellow("\u26A0\uFE0F Note: --drop only works with custom format backups (.dump) and was ignored."));
4378
4495
  }
4379
4496
  if (options.dataOnly || options.schemaOnly) {
4380
- console.log(chalk25.yellow("\u26A0\uFE0F Note: --data-only and --schema-only options only work with custom format backups (.dump)"));
4381
- console.log(chalk25.yellow(" For SQL files, the backup must have been created with the desired option.\n"));
4497
+ console.log(chalk26.yellow("\u26A0\uFE0F Note: --data-only and --schema-only options only work with custom format backups (.dump)"));
4498
+ console.log(chalk26.yellow(" For SQL files, the backup must have been created with the desired option.\n"));
4382
4499
  }
4383
4500
  args.push("-h", dbInfo.host);
4384
4501
  args.push("-p", dbInfo.port);
@@ -4421,7 +4538,7 @@ async function dbRestore(backupFile, options = {}) {
4421
4538
  }
4422
4539
  if (verbose) {
4423
4540
  spinner.stop();
4424
- console.log(chalk25.dim(` ${line.trim()}`));
4541
+ console.log(chalk26.dim(` ${line.trim()}`));
4425
4542
  spinner.start();
4426
4543
  }
4427
4544
  }
@@ -4429,7 +4546,7 @@ async function dbRestore(backupFile, options = {}) {
4429
4546
  restoreProcess.stdout?.on("data", (data) => {
4430
4547
  if (verbose) {
4431
4548
  spinner.stop();
4432
- console.log(chalk25.dim(` ${data.toString().trim()}`));
4549
+ console.log(chalk26.dim(` ${data.toString().trim()}`));
4433
4550
  spinner.start();
4434
4551
  }
4435
4552
  });
@@ -4439,31 +4556,31 @@ async function dbRestore(backupFile, options = {}) {
4439
4556
  const summary = objectCount > 0 ? ` (${objectCount} objects)` : "";
4440
4557
  spinner.succeed(`Restore completed${summary}`);
4441
4558
  if (warnings.length > 0) {
4442
- console.log(chalk25.yellow(`
4559
+ console.log(chalk26.yellow(`
4443
4560
  \u26A0\uFE0F Warnings during restore (${warnings.length}):
4444
4561
  `));
4445
4562
  for (const w of warnings) {
4446
- console.log(chalk25.yellow(` - ${w}`));
4563
+ console.log(chalk26.yellow(` - ${w}`));
4447
4564
  }
4448
4565
  }
4449
- console.log(chalk25.green("\n\u2705 Database restored successfully"));
4566
+ console.log(chalk26.green("\n\u2705 Database restored successfully"));
4450
4567
  resolve3();
4451
4568
  } else {
4452
4569
  spinner.fail("Restore failed");
4453
4570
  if (errors.length > 0) {
4454
- console.error(chalk25.red(`
4571
+ console.error(chalk26.red(`
4455
4572
  \u274C Errors (${errors.length}):
4456
4573
  `));
4457
4574
  for (const e of errors) {
4458
- console.error(chalk25.red(` - ${e}`));
4575
+ console.error(chalk26.red(` - ${e}`));
4459
4576
  }
4460
4577
  }
4461
4578
  if (warnings.length > 0) {
4462
- console.log(chalk25.yellow(`
4579
+ console.log(chalk26.yellow(`
4463
4580
  \u26A0\uFE0F Warnings (${warnings.length}):
4464
4581
  `));
4465
4582
  for (const w of warnings) {
4466
- console.log(chalk25.yellow(` - ${w}`));
4583
+ console.log(chalk26.yellow(` - ${w}`));
4467
4584
  }
4468
4585
  }
4469
4586
  const fallback = errors.length === 0 && warnings.length === 0 ? "Restore failed with no output" : "";
@@ -4477,7 +4594,7 @@ async function dbRestore(backupFile, options = {}) {
4477
4594
  }).catch((error) => {
4478
4595
  const msg = error instanceof Error ? error.message : "Unknown error";
4479
4596
  if (msg) {
4480
- console.error(chalk25.red(`
4597
+ console.error(chalk26.red(`
4481
4598
  \u274C ${msg}`));
4482
4599
  }
4483
4600
  process.exit(1);
@@ -4485,17 +4602,17 @@ async function dbRestore(backupFile, options = {}) {
4485
4602
  }
4486
4603
 
4487
4604
  // src/commands/db/list.ts
4488
- import chalk26 from "chalk";
4605
+ import chalk27 from "chalk";
4489
4606
  async function dbBackupList() {
4490
- console.log(chalk26.blue("\u{1F4CB} Database backups:\n"));
4607
+ console.log(chalk27.blue("\u{1F4CB} Database backups:\n"));
4491
4608
  const backups = await listBackupFiles();
4492
4609
  if (backups.length === 0) {
4493
- console.log(chalk26.yellow("No backups found in ./backups directory"));
4494
- console.log(chalk26.gray("\n\u{1F4A1} Create a backup with: pnpm spfn db backup\n"));
4610
+ console.log(chalk27.yellow("No backups found in ./backups directory"));
4611
+ console.log(chalk27.gray("\n\u{1F4A1} Create a backup with: pnpm spfn db backup\n"));
4495
4612
  return;
4496
4613
  }
4497
- console.log(chalk26.bold(" Date Size File"));
4498
- console.log(chalk26.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
4614
+ console.log(chalk27.bold(" Date Size File"));
4615
+ console.log(chalk27.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
4499
4616
  backups.forEach((backup) => {
4500
4617
  const date = backup.date.toLocaleString("en-US", {
4501
4618
  year: "numeric",
@@ -4506,23 +4623,23 @@ async function dbBackupList() {
4506
4623
  second: "2-digit"
4507
4624
  });
4508
4625
  const sizeStr = backup.size.padEnd(10);
4509
- console.log(chalk26.white(` ${date} ${sizeStr} ${backup.name}`));
4626
+ console.log(chalk27.white(` ${date} ${sizeStr} ${backup.name}`));
4510
4627
  });
4511
- console.log(chalk26.gray(`
4628
+ console.log(chalk27.gray(`
4512
4629
  Total: ${backups.length} backup(s)
4513
4630
  `));
4514
4631
  }
4515
4632
 
4516
4633
  // src/commands/db/clean.ts
4517
4634
  import { promises as fs4 } from "fs";
4518
- import chalk27 from "chalk";
4635
+ import chalk28 from "chalk";
4519
4636
  import ora10 from "ora";
4520
4637
  import prompts8 from "prompts";
4521
4638
  async function dbBackupClean(options) {
4522
- console.log(chalk27.blue("\u{1F9F9} Cleaning old backups...\n"));
4639
+ console.log(chalk28.blue("\u{1F9F9} Cleaning old backups...\n"));
4523
4640
  const backups = await listBackupFiles();
4524
4641
  if (backups.length === 0) {
4525
- console.log(chalk27.yellow("No backups found"));
4642
+ console.log(chalk28.yellow("No backups found"));
4526
4643
  return;
4527
4644
  }
4528
4645
  let toDelete = [];
@@ -4539,13 +4656,13 @@ async function dbBackupClean(options) {
4539
4656
  toDelete = backups.slice(defaultKeep);
4540
4657
  }
4541
4658
  if (toDelete.length === 0) {
4542
- console.log(chalk27.green("\u2705 No backups to clean"));
4659
+ console.log(chalk28.green("\u2705 No backups to clean"));
4543
4660
  return;
4544
4661
  }
4545
- console.log(chalk27.yellow(`The following ${toDelete.length} backup(s) will be deleted:
4662
+ console.log(chalk28.yellow(`The following ${toDelete.length} backup(s) will be deleted:
4546
4663
  `));
4547
4664
  toDelete.forEach((backup) => {
4548
- console.log(chalk27.gray(` - ${backup.name} (${backup.size})`));
4665
+ console.log(chalk28.gray(` - ${backup.name} (${backup.size})`));
4549
4666
  });
4550
4667
  const { confirm } = await prompts8({
4551
4668
  type: "confirm",
@@ -4554,7 +4671,7 @@ async function dbBackupClean(options) {
4554
4671
  initial: false
4555
4672
  });
4556
4673
  if (!confirm) {
4557
- console.log(chalk27.gray("Cancelled"));
4674
+ console.log(chalk28.gray("Cancelled"));
4558
4675
  return;
4559
4676
  }
4560
4677
  const spinner = ora10("Deleting backups...").start();
@@ -4565,19 +4682,19 @@ async function dbBackupClean(options) {
4565
4682
  fs4.rm(backup.path.replace(/\.(sql|dump)$/, ".meta.json"), { force: true })
4566
4683
  ]));
4567
4684
  spinner.succeed("Backups deleted");
4568
- console.log(chalk27.green(`
4685
+ console.log(chalk28.green(`
4569
4686
  \u2705 Deleted ${toDelete.length} backup(s)`));
4570
4687
  } catch (error) {
4571
4688
  spinner.fail("Failed to delete backups");
4572
- console.error(chalk27.red(error instanceof Error ? error.message : "Unknown error"));
4689
+ console.error(chalk28.red(error instanceof Error ? error.message : "Unknown error"));
4573
4690
  process.exit(1);
4574
4691
  }
4575
4692
  }
4576
4693
 
4577
4694
  // src/commands/db/reindex.ts
4578
4695
  import { existsSync as existsSync25, readFileSync as readFileSync10, writeFileSync as writeFileSync16, renameSync, copyFileSync } from "fs";
4579
- import { join as join23 } from "path";
4580
- import chalk28 from "chalk";
4696
+ import { join as join24 } from "path";
4697
+ import chalk29 from "chalk";
4581
4698
  import { loadEnv as loadEnv9 } from "@spfn/core/server";
4582
4699
  function isTimestampPrefix(tag) {
4583
4700
  const prefix = tag.split("_")[0];
@@ -4598,15 +4715,15 @@ async function dbReindex(options = {}) {
4598
4715
  const { getDrizzleConfig } = await import("@spfn/core/db");
4599
4716
  const config = getDrizzleConfig({ disablePackageDiscovery: true });
4600
4717
  const outDir = config.out;
4601
- const journalPath = join23(outDir, "meta", "_journal.json");
4718
+ const journalPath = join24(outDir, "meta", "_journal.json");
4602
4719
  if (!existsSync25(journalPath)) {
4603
- console.error(chalk28.red("\u274C No _journal.json found at:"), journalPath);
4604
- console.log(chalk28.yellow("\u{1F4A1} Run `spfn db generate` first to create migrations"));
4720
+ console.error(chalk29.red("\u274C No _journal.json found at:"), journalPath);
4721
+ console.log(chalk29.yellow("\u{1F4A1} Run `spfn db generate` first to create migrations"));
4605
4722
  process.exit(1);
4606
4723
  }
4607
4724
  const journal = JSON.parse(readFileSync10(journalPath, "utf-8"));
4608
4725
  if (journal.entries.length === 0) {
4609
- console.log(chalk28.yellow("No migration entries found \u2014 nothing to reindex."));
4726
+ console.log(chalk29.yellow("No migration entries found \u2014 nothing to reindex."));
4610
4727
  return;
4611
4728
  }
4612
4729
  const renames = [];
@@ -4620,46 +4737,46 @@ async function dbReindex(options = {}) {
4620
4737
  const { prefix: oldPrefix, suffix } = parseTag(entry.tag);
4621
4738
  const newPrefix = String(entry.when);
4622
4739
  const newTag = suffix ? `${newPrefix}_${suffix}` : newPrefix;
4623
- const oldSql = join23(outDir, `${entry.tag}.sql`);
4624
- const newSql = join23(outDir, `${newTag}.sql`);
4740
+ const oldSql = join24(outDir, `${entry.tag}.sql`);
4741
+ const newSql = join24(outDir, `${newTag}.sql`);
4625
4742
  if (existsSync25(oldSql)) {
4626
4743
  renames.push({ type: "sql", from: oldSql, to: newSql });
4627
4744
  }
4628
- const oldSnapshot = join23(outDir, "meta", `${oldPrefix}_snapshot.json`);
4629
- const newSnapshot = join23(outDir, "meta", `${newPrefix}_snapshot.json`);
4745
+ const oldSnapshot = join24(outDir, "meta", `${oldPrefix}_snapshot.json`);
4746
+ const newSnapshot = join24(outDir, "meta", `${newPrefix}_snapshot.json`);
4630
4747
  if (existsSync25(oldSnapshot)) {
4631
4748
  renames.push({ type: "snapshot", from: oldSnapshot, to: newSnapshot });
4632
4749
  }
4633
4750
  tagUpdates.push({ idx: entry.idx, oldTag: entry.tag, newTag });
4634
4751
  }
4635
4752
  if (tagUpdates.length === 0) {
4636
- console.log(chalk28.green("\u2705 All migrations already use timestamp prefix \u2014 nothing to do."));
4753
+ console.log(chalk29.green("\u2705 All migrations already use timestamp prefix \u2014 nothing to do."));
4637
4754
  if (skipped > 0) {
4638
- console.log(chalk28.dim(` (${skipped} entries already timestamp-prefixed)`));
4755
+ console.log(chalk29.dim(` (${skipped} entries already timestamp-prefixed)`));
4639
4756
  }
4640
4757
  return;
4641
4758
  }
4642
- console.log(chalk28.bold("\n\u{1F4CB} Reindex plan:\n"));
4759
+ console.log(chalk29.bold("\n\u{1F4CB} Reindex plan:\n"));
4643
4760
  for (const update of tagUpdates) {
4644
4761
  console.log(
4645
- chalk28.dim(` [${update.idx}]`),
4646
- chalk28.red(update.oldTag),
4647
- chalk28.dim("\u2192"),
4648
- chalk28.green(update.newTag)
4762
+ chalk29.dim(` [${update.idx}]`),
4763
+ chalk29.red(update.oldTag),
4764
+ chalk29.dim("\u2192"),
4765
+ chalk29.green(update.newTag)
4649
4766
  );
4650
4767
  }
4651
- console.log(chalk28.dim(`
4768
+ console.log(chalk29.dim(`
4652
4769
  ${renames.length} file(s) to rename, ${tagUpdates.length} journal tag(s) to update`));
4653
4770
  if (skipped > 0) {
4654
- console.log(chalk28.dim(` ${skipped} entry/entries already timestamp-prefixed (skipped)`));
4771
+ console.log(chalk29.dim(` ${skipped} entry/entries already timestamp-prefixed (skipped)`));
4655
4772
  }
4656
4773
  if (options.dryRun) {
4657
- console.log(chalk28.yellow("\n\u{1F50D} Dry run \u2014 no changes applied."));
4774
+ console.log(chalk29.yellow("\n\u{1F50D} Dry run \u2014 no changes applied."));
4658
4775
  return;
4659
4776
  }
4660
4777
  const backupPath = journalPath + ".bak";
4661
4778
  copyFileSync(journalPath, backupPath);
4662
- console.log(chalk28.dim(`
4779
+ console.log(chalk29.dim(`
4663
4780
  Backed up journal \u2192 ${backupPath}`));
4664
4781
  for (const rename of renames) {
4665
4782
  renameSync(rename.from, rename.to);
@@ -4671,12 +4788,12 @@ async function dbReindex(options = {}) {
4671
4788
  }
4672
4789
  }
4673
4790
  writeFileSync16(journalPath, JSON.stringify(journal, null, 2) + "\n");
4674
- console.log(chalk28.green(`
4791
+ console.log(chalk29.green(`
4675
4792
  \u2705 Reindex complete \u2014 ${tagUpdates.length} migration(s) converted to timestamp prefix.`));
4676
4793
  }
4677
4794
 
4678
4795
  // src/commands/db/index.ts
4679
- var dbCommand = new Command10("db").description("Database management commands (wraps Drizzle Kit)");
4796
+ var dbCommand = new Command11("db").description("Database management commands (wraps Drizzle Kit)");
4680
4797
  dbCommand.command("generate").alias("g").description("Generate database migrations from schema changes").action(dbGenerate);
4681
4798
  dbCommand.command("push").description("Push schema changes to database (safe mode by default)").option("--force", "Apply all changes including destructive ones").option("--dry-run", "Show changes without applying").action((options) => dbPush(options));
4682
4799
  dbCommand.command("migrate").alias("m").description("Run pending migrations").option("--with-backup", "Create backup before running migrations").action((options) => dbMigrate(options));
@@ -4692,22 +4809,22 @@ dbCommand.command("reindex").description("Convert migration files from sequentia
4692
4809
 
4693
4810
  // src/commands/add.ts
4694
4811
  init_package_manager();
4695
- import { Command as Command11 } from "commander";
4812
+ import { Command as Command12 } from "commander";
4696
4813
  import { existsSync as existsSync27, readFileSync as readFileSync11 } from "fs";
4697
- import { join as join25 } from "path";
4814
+ import { join as join26 } from "path";
4698
4815
  import { execa as execa10 } from "execa";
4699
- import chalk30 from "chalk";
4816
+ import chalk31 from "chalk";
4700
4817
  import ora11 from "ora";
4701
4818
 
4702
4819
  // src/commands/add-vercel.ts
4703
4820
  init_templates();
4704
4821
  import { copyFileSync as copyFileSync2, existsSync as existsSync26, mkdirSync as mkdirSync4 } from "fs";
4705
- import { dirname as dirname3, join as join24 } from "path";
4706
- import chalk29 from "chalk";
4822
+ import { dirname as dirname3, join as join25 } from "path";
4823
+ import chalk30 from "chalk";
4707
4824
  var FILES = [
4708
4825
  {
4709
4826
  template: "route.ts",
4710
- target: join24("src", "app", "api", "backend", "[[...route]]", "route.ts"),
4827
+ target: join25("src", "app", "api", "backend", "[[...route]]", "route.ts"),
4711
4828
  description: "hono/vercel adapter (mounts the SPFN app under /api/backend)"
4712
4829
  },
4713
4830
  {
@@ -4722,35 +4839,35 @@ var FILES = [
4722
4839
  }
4723
4840
  ];
4724
4841
  async function addVercel() {
4725
- console.log(chalk29.blue("\n\u{1F4E6} Setting up the Vercel serverless target...\n"));
4842
+ console.log(chalk30.blue("\n\u{1F4E6} Setting up the Vercel serverless target...\n"));
4726
4843
  const cwd = process.cwd();
4727
- const templatesDir = join24(findTemplatesPath(), "vercel");
4844
+ const templatesDir = join25(findTemplatesPath(), "vercel");
4728
4845
  for (const file of FILES) {
4729
- const targetPath = join24(cwd, file.target);
4846
+ const targetPath = join25(cwd, file.target);
4730
4847
  if (existsSync26(targetPath)) {
4731
- console.log(chalk29.yellow(`\u23ED\uFE0F ${file.target} already exists \u2014 skipped (not overwritten)`));
4848
+ console.log(chalk30.yellow(`\u23ED\uFE0F ${file.target} already exists \u2014 skipped (not overwritten)`));
4732
4849
  continue;
4733
4850
  }
4734
4851
  mkdirSync4(dirname3(targetPath), { recursive: true });
4735
- copyFileSync2(join24(templatesDir, file.template), targetPath);
4736
- console.log(chalk29.green(`\u2705 ${file.target}`) + chalk29.gray(` \u2014 ${file.description}`));
4737
- }
4738
- console.log(chalk29.green("\n\u2705 Vercel target ready!\n"));
4739
- console.log(chalk29.cyan("\u{1F4DA} Next steps:"));
4740
- console.log(chalk29.gray(" 1. Make sure `hono` is a direct dependency (hono/vercel must resolve from the app):"));
4741
- console.log(chalk29.gray(" pnpm add hono"));
4742
- console.log(chalk29.gray(" 2. Set Vercel project env vars:"));
4743
- console.log(chalk29.gray(" GITEA_NPM_TOKEN \u2014 @spfn registry token (install step)"));
4744
- console.log(chalk29.gray(" SPFN_AUTH_* \u2014 auth secrets (same as local .env.server)"));
4745
- console.log(chalk29.gray(" SPFN_API_URL \u2014 https://<your-domain>/api/backend (RPC proxy origin)"));
4746
- console.log(chalk29.gray(" DATABASE_URL \u2014 set automatically from POSTGRES_URL if you use the"));
4747
- console.log(chalk29.gray(" Vercel Supabase integration (mapped in route.ts)"));
4748
- console.log(chalk29.gray(" 3. Run migrations against the DIRECT (non-pooler) connection before deploying:"));
4749
- console.log(chalk29.gray(" DATABASE_URL=<direct-url> pnpm spfn db migrate"));
4750
- console.log(chalk29.gray(" 4. Seed/RBAC provisioning runs once per deploy, not per cold start:"));
4751
- console.log(chalk29.gray(" pnpm spfn provision (e.g. from the build step or a deploy hook)"));
4752
- console.log(chalk29.gray(" 5. Jobs (config.jobs) are NOT processed on serverless \u2014 drain the queue from a"));
4753
- console.log(chalk29.gray(" scheduled endpoint (Vercel Cron) or run workers on an always-on target.\n"));
4852
+ copyFileSync2(join25(templatesDir, file.template), targetPath);
4853
+ console.log(chalk30.green(`\u2705 ${file.target}`) + chalk30.gray(` \u2014 ${file.description}`));
4854
+ }
4855
+ console.log(chalk30.green("\n\u2705 Vercel target ready!\n"));
4856
+ console.log(chalk30.cyan("\u{1F4DA} Next steps:"));
4857
+ console.log(chalk30.gray(" 1. Make sure `hono` is a direct dependency (hono/vercel must resolve from the app):"));
4858
+ console.log(chalk30.gray(" pnpm add hono"));
4859
+ console.log(chalk30.gray(" 2. Set Vercel project env vars:"));
4860
+ console.log(chalk30.gray(" GITEA_NPM_TOKEN \u2014 @spfn registry token (install step)"));
4861
+ console.log(chalk30.gray(" SPFN_AUTH_* \u2014 auth secrets (same as local .env.server)"));
4862
+ console.log(chalk30.gray(" SPFN_API_URL \u2014 https://<your-domain>/api/backend (RPC proxy origin)"));
4863
+ console.log(chalk30.gray(" DATABASE_URL \u2014 set automatically from POSTGRES_URL if you use the"));
4864
+ console.log(chalk30.gray(" Vercel Supabase integration (mapped in route.ts)"));
4865
+ console.log(chalk30.gray(" 3. Run migrations against the DIRECT (non-pooler) connection before deploying:"));
4866
+ console.log(chalk30.gray(" DATABASE_URL=<direct-url> pnpm spfn db migrate"));
4867
+ console.log(chalk30.gray(" 4. Seed/RBAC provisioning runs once per deploy, not per cold start:"));
4868
+ console.log(chalk30.gray(" pnpm spfn provision (e.g. from the build step or a deploy hook)"));
4869
+ console.log(chalk30.gray(" 5. Jobs (config.jobs) are NOT processed on serverless \u2014 drain the queue from a"));
4870
+ console.log(chalk30.gray(" scheduled endpoint (Vercel Cron) or run workers on an always-on target.\n"));
4754
4871
  }
4755
4872
 
4756
4873
  // src/commands/add.ts
@@ -4760,17 +4877,19 @@ async function addPackage(packageName) {
4760
4877
  return;
4761
4878
  }
4762
4879
  if (!packageName.includes("/")) {
4763
- console.error(chalk30.red("\u274C Please specify full package name"));
4764
- console.log(chalk30.yellow("\n\u{1F4A1} Examples:"));
4765
- console.log(chalk30.gray(" pnpm spfn add @spfn/cms"));
4766
- console.log(chalk30.gray(" pnpm spfn add @mycompany/spfn-analytics"));
4880
+ console.error(chalk31.red("\u274C Please specify full package name"));
4881
+ console.log(chalk31.yellow("\n\u{1F4A1} Examples:"));
4882
+ console.log(chalk31.gray(" pnpm spfn add @spfn/cms"));
4883
+ console.log(chalk31.gray(" pnpm spfn add @mycompany/spfn-analytics"));
4884
+ console.log(chalk31.gray("\n Deploy targets are unscoped. The only one today is:"));
4885
+ console.log(chalk31.gray(" pnpm spfn add vercel"));
4767
4886
  process.exit(1);
4768
4887
  }
4769
- console.log(chalk30.blue(`
4888
+ console.log(chalk31.blue(`
4770
4889
  \u{1F4E6} Setting up ${packageName}...
4771
4890
  `));
4772
- const pkgPath = join25(process.cwd(), "node_modules", ...packageName.split("/"));
4773
- const pkgJsonPath = join25(pkgPath, "package.json");
4891
+ const pkgPath = join26(process.cwd(), "node_modules", ...packageName.split("/"));
4892
+ const pkgJsonPath = join26(pkgPath, "package.json");
4774
4893
  if (!existsSync27(pkgJsonPath)) {
4775
4894
  const pm = detectPackageManager(process.cwd());
4776
4895
  const installSpinner = ora11("Installing package...").start();
@@ -4779,27 +4898,27 @@ async function addPackage(packageName) {
4779
4898
  installSpinner.succeed("Package installed");
4780
4899
  } catch (error) {
4781
4900
  installSpinner.fail("Failed to install package");
4782
- console.error(chalk30.red(error instanceof Error ? error.message : "Unknown error"));
4901
+ console.error(chalk31.red(error instanceof Error ? error.message : "Unknown error"));
4783
4902
  process.exit(1);
4784
4903
  }
4785
4904
  } else {
4786
- console.log(chalk30.gray("\u2713 Package already installed (using local version)\n"));
4905
+ console.log(chalk31.gray("\u2713 Package already installed (using local version)\n"));
4787
4906
  }
4788
4907
  if (!existsSync27(pkgJsonPath)) {
4789
- console.error(chalk30.red(`\u274C Package ${packageName} not found after installation`));
4908
+ console.error(chalk31.red(`\u274C Package ${packageName} not found after installation`));
4790
4909
  process.exit(1);
4791
4910
  }
4792
4911
  const pkgJson = JSON.parse(readFileSync11(pkgJsonPath, "utf-8"));
4793
4912
  if (pkgJson.spfn?.migrations) {
4794
- console.log(chalk30.blue(`
4913
+ console.log(chalk31.blue(`
4795
4914
  \u{1F5C4}\uFE0F Setting up database for ${packageName}...
4796
4915
  `));
4797
4916
  const { env: env9 } = await import("@spfn/core/config");
4798
4917
  if (!env9.DATABASE_URL) {
4799
- console.log(chalk30.yellow("\u26A0\uFE0F DATABASE_URL not found \u2014 skipping database setup."));
4800
- console.log(chalk30.yellow(` ${packageName} tables are created by its bundled migrations, not by schema push.`));
4801
- console.log(chalk30.cyan(" Once DATABASE_URL is set, run: pnpm spfn db migrate"));
4802
- console.log(chalk30.gray(" (check state anytime with: pnpm spfn db status)\n"));
4918
+ console.log(chalk31.yellow("\u26A0\uFE0F DATABASE_URL not found \u2014 skipping database setup."));
4919
+ console.log(chalk31.yellow(` ${packageName} tables are created by its bundled migrations, not by schema push.`));
4920
+ console.log(chalk31.cyan(" Once DATABASE_URL is set, run: pnpm spfn db migrate"));
4921
+ console.log(chalk31.gray(" (check state anytime with: pnpm spfn db status)\n"));
4803
4922
  } else {
4804
4923
  const { discoverFunctionMigrations: discoverFunctionMigrations2, loadFunctionMigrationPlans: loadFunctionMigrationPlans2, executeFunctionMigrations: executeFunctionMigrations2 } = await Promise.resolve().then(() => (init_function_migrations(), function_migrations_exports));
4805
4924
  const functions = discoverFunctionMigrations2(process.cwd());
@@ -4811,30 +4930,30 @@ async function addPackage(packageName) {
4811
4930
  spinner.succeed("Migrations applied");
4812
4931
  } catch (error) {
4813
4932
  spinner.fail("Failed to apply migrations");
4814
- console.error(chalk30.red(error instanceof Error ? error.message : "Unknown error"));
4933
+ console.error(chalk31.red(error instanceof Error ? error.message : "Unknown error"));
4815
4934
  process.exit(1);
4816
4935
  }
4817
4936
  } else {
4818
- console.log(chalk30.gray("\u2139\uFE0F No migrations found for this package"));
4937
+ console.log(chalk31.gray("\u2139\uFE0F No migrations found for this package"));
4819
4938
  }
4820
4939
  }
4821
4940
  } else {
4822
- console.log(chalk30.gray("\n\u2139\uFE0F No database migrations to apply"));
4941
+ console.log(chalk31.gray("\n\u2139\uFE0F No database migrations to apply"));
4823
4942
  }
4824
- console.log(chalk30.green(`
4943
+ console.log(chalk31.green(`
4825
4944
  \u2705 ${packageName} installed successfully!
4826
4945
  `));
4827
4946
  if (pkgJson.spfn?.setupMessage) {
4828
- console.log(chalk30.cyan("\u{1F4DA} Setup Guide:"));
4947
+ console.log(chalk31.cyan("\u{1F4DA} Setup Guide:"));
4829
4948
  console.log(pkgJson.spfn.setupMessage);
4830
4949
  console.log();
4831
4950
  }
4832
4951
  }
4833
- var addCommand = new Command11("add").description("Install and set up SPFN ecosystem packages, or scaffold a deploy target (vercel)").argument("<package>", 'Package name (e.g., @spfn/cms, @mycompany/spfn-analytics) or "vercel"').action(addPackage);
4952
+ var addCommand = new Command12("add").description("Add a capability to this project: an SPFN ecosystem package, or a deploy target").argument("<package|target>", "Scoped package name (e.g., @spfn/cms, @mycompany/spfn-analytics), or a deploy target: vercel").action(addPackage);
4834
4953
 
4835
4954
  // src/commands/env.ts
4836
- import { Command as Command12 } from "commander";
4837
- import chalk31 from "chalk";
4955
+ import { Command as Command13 } from "commander";
4956
+ import chalk32 from "chalk";
4838
4957
  import { existsSync as existsSync28, readFileSync as readFileSync12, writeFileSync as writeFileSync17 } from "fs";
4839
4958
  import { resolve as resolve2 } from "path";
4840
4959
  import { parse as parse2 } from "dotenv";
@@ -4891,26 +5010,26 @@ function getEnvFilesForEnvironment(nodeEnv) {
4891
5010
  }
4892
5011
  function formatType(type) {
4893
5012
  const typeColors = {
4894
- string: chalk31.green,
4895
- number: chalk31.blue,
4896
- boolean: chalk31.yellow,
4897
- url: chalk31.cyan,
4898
- enum: chalk31.magenta,
4899
- json: chalk31.red
5013
+ string: chalk32.green,
5014
+ number: chalk32.blue,
5015
+ boolean: chalk32.yellow,
5016
+ url: chalk32.cyan,
5017
+ enum: chalk32.magenta,
5018
+ json: chalk32.red
4900
5019
  };
4901
- return (typeColors[type] || chalk31.white)(type);
5020
+ return (typeColors[type] || chalk32.white)(type);
4902
5021
  }
4903
5022
  function formatDefault(value, type) {
4904
5023
  if (value === void 0) {
4905
- return chalk31.dim("(none)");
5024
+ return chalk32.dim("(none)");
4906
5025
  }
4907
5026
  if (type === "string" || type === "url") {
4908
- return chalk31.green(`"${value}"`);
5027
+ return chalk32.green(`"${value}"`);
4909
5028
  }
4910
5029
  if (type === "boolean") {
4911
- return value ? chalk31.green("true") : chalk31.red("false");
5030
+ return value ? chalk32.green("true") : chalk32.red("false");
4912
5031
  }
4913
- return chalk31.cyan(String(value));
5032
+ return chalk32.cyan(String(value));
4914
5033
  }
4915
5034
  async function listEnvVars(options) {
4916
5035
  const packageName = options.package || "@spfn/core";
@@ -4924,28 +5043,28 @@ async function listEnvVars(options) {
4924
5043
  acc[target].push([key, schema]);
4925
5044
  return acc;
4926
5045
  }, {});
4927
- console.log(chalk31.blue.bold(`
5046
+ console.log(chalk32.blue.bold(`
4928
5047
  \u{1F4CB} Environment Variables by File (${packageName})
4929
5048
  `));
4930
5049
  for (const [file, vars] of Object.entries(grouped)) {
4931
- console.log(chalk31.bold.magenta(`
5050
+ console.log(chalk32.bold.magenta(`
4932
5051
  ${file}`));
4933
- console.log(chalk31.dim("\u2500".repeat(50)));
5052
+ console.log(chalk32.dim("\u2500".repeat(50)));
4934
5053
  for (const [key, schema] of vars) {
4935
5054
  printEnvVar(key, schema);
4936
5055
  }
4937
5056
  }
4938
5057
  } else {
4939
- console.log(chalk31.blue.bold(`
5058
+ console.log(chalk32.blue.bold(`
4940
5059
  \u{1F4CB} Environment Variables (${packageName})
4941
5060
  `));
4942
5061
  for (const [key, schema] of allVars) {
4943
5062
  printEnvVar(key, schema, true);
4944
5063
  }
4945
5064
  }
4946
- console.log(chalk31.dim("\n\u{1F4A1} Tip: Use `spfn env init` to generate .env template files\n"));
5065
+ console.log(chalk32.dim("\n\u{1F4A1} Tip: Use `spfn env init` to generate .env template files\n"));
4947
5066
  } catch (error) {
4948
- console.error(chalk31.red(`
5067
+ console.error(chalk32.red(`
4949
5068
  \u274C ${error instanceof Error ? error.message : "Unknown error"}
4950
5069
  `));
4951
5070
  process.exit(1);
@@ -4953,17 +5072,17 @@ ${file}`));
4953
5072
  }
4954
5073
  function printEnvVar(key, schema, showFile = false) {
4955
5074
  const typeStr = formatType(schema.type);
4956
- const requiredStr = schema.required || schema.default !== void 0 ? chalk31.red("[required]") : chalk31.dim("[optional]");
4957
- const sensitiveStr = schema.sensitive ? chalk31.yellow(" [sensitive]") : "";
4958
- const fileStr = showFile ? chalk31.dim(` \u2192 ${getTargetFile(schema)}`) : "";
4959
- console.log(`${chalk31.bold.cyan(key)} ${chalk31.dim("(")}${typeStr}${chalk31.dim(")")} ${requiredStr}${sensitiveStr}${fileStr}`);
4960
- console.log(` ${chalk31.dim(schema.description)}`);
5075
+ const requiredStr = schema.required || schema.default !== void 0 ? chalk32.red("[required]") : chalk32.dim("[optional]");
5076
+ const sensitiveStr = schema.sensitive ? chalk32.yellow(" [sensitive]") : "";
5077
+ const fileStr = showFile ? chalk32.dim(` \u2192 ${getTargetFile(schema)}`) : "";
5078
+ console.log(`${chalk32.bold.cyan(key)} ${chalk32.dim("(")}${typeStr}${chalk32.dim(")")} ${requiredStr}${sensitiveStr}${fileStr}`);
5079
+ console.log(` ${chalk32.dim(schema.description)}`);
4961
5080
  if (schema.default !== void 0) {
4962
- console.log(` ${chalk31.dim("Default:")} ${formatDefault(schema.default, schema.type)}`);
5081
+ console.log(` ${chalk32.dim("Default:")} ${formatDefault(schema.default, schema.type)}`);
4963
5082
  }
4964
5083
  if (schema.examples && schema.examples.length > 0) {
4965
5084
  const exampleStr = schema.examples.map((ex) => formatDefault(ex, schema.type)).join(", ");
4966
- console.log(` ${chalk31.dim("Examples:")} ${exampleStr}`);
5085
+ console.log(` ${chalk32.dim("Examples:")} ${exampleStr}`);
4967
5086
  }
4968
5087
  console.log();
4969
5088
  }
@@ -4971,7 +5090,7 @@ async function showEnvStats(options) {
4971
5090
  const packageName = options.package || "@spfn/core";
4972
5091
  try {
4973
5092
  const envSchema = await loadEnvSchema(packageName);
4974
- console.log(chalk31.blue.bold(`
5093
+ console.log(chalk32.blue.bold(`
4975
5094
  \u{1F4CA} Environment Variable Statistics (${packageName})
4976
5095
  `));
4977
5096
  const allVars = Object.entries(envSchema);
@@ -4993,24 +5112,24 @@ async function showEnvStats(options) {
4993
5112
  acc[file] = (acc[file] || 0) + 1;
4994
5113
  return acc;
4995
5114
  }, {});
4996
- console.log(`${chalk31.bold("Total variables:")} ${chalk31.cyan(allVars.length)}`);
4997
- console.log(`${chalk31.bold("Required:")} ${chalk31.red(required.length)}`);
4998
- console.log(`${chalk31.bold("Optional:")} ${chalk31.dim(optional.length)}`);
4999
- console.log(`${chalk31.bold("Sensitive:")} ${chalk31.yellow(sensitive.length)}`);
5000
- console.log(chalk31.bold("\nBy Target:"));
5001
- console.log(` ${chalk31.blue("Next.js accessible:")} ${chalk31.cyan(nextjsVars.length)}`);
5002
- console.log(` ${chalk31.magenta("SPFN server only:")} ${chalk31.cyan(serverOnlyVars.length)}`);
5003
- console.log(chalk31.bold("\nBy File:"));
5115
+ console.log(`${chalk32.bold("Total variables:")} ${chalk32.cyan(allVars.length)}`);
5116
+ console.log(`${chalk32.bold("Required:")} ${chalk32.red(required.length)}`);
5117
+ console.log(`${chalk32.bold("Optional:")} ${chalk32.dim(optional.length)}`);
5118
+ console.log(`${chalk32.bold("Sensitive:")} ${chalk32.yellow(sensitive.length)}`);
5119
+ console.log(chalk32.bold("\nBy Target:"));
5120
+ console.log(` ${chalk32.blue("Next.js accessible:")} ${chalk32.cyan(nextjsVars.length)}`);
5121
+ console.log(` ${chalk32.magenta("SPFN server only:")} ${chalk32.cyan(serverOnlyVars.length)}`);
5122
+ console.log(chalk32.bold("\nBy File:"));
5004
5123
  for (const [file, count] of Object.entries(fileCount)) {
5005
- console.log(` ${chalk31.dim(file)}: ${chalk31.cyan(count)}`);
5124
+ console.log(` ${chalk32.dim(file)}: ${chalk32.cyan(count)}`);
5006
5125
  }
5007
- console.log(chalk31.bold("\nBy Type:"));
5126
+ console.log(chalk32.bold("\nBy Type:"));
5008
5127
  for (const [type, count] of Object.entries(typeCount)) {
5009
- console.log(` ${formatType(type)}: ${chalk31.cyan(count)}`);
5128
+ console.log(` ${formatType(type)}: ${chalk32.cyan(count)}`);
5010
5129
  }
5011
5130
  console.log();
5012
5131
  } catch (error) {
5013
- console.error(chalk31.red(`
5132
+ console.error(chalk32.red(`
5014
5133
  \u274C ${error instanceof Error ? error.message : "Unknown error"}
5015
5134
  `));
5016
5135
  process.exit(1);
@@ -5030,40 +5149,40 @@ async function searchEnvVars(query, options) {
5030
5149
  }
5031
5150
  }
5032
5151
  if (results.length === 0) {
5033
- console.log(chalk31.yellow(`
5152
+ console.log(chalk32.yellow(`
5034
5153
  \u26A0\uFE0F No environment variables found matching "${query}"
5035
5154
  `));
5036
5155
  return;
5037
5156
  }
5038
- console.log(chalk31.blue.bold(`
5157
+ console.log(chalk32.blue.bold(`
5039
5158
  \u{1F50D} Found ${results.length} environment variable(s) matching "${query}"
5040
5159
  `));
5041
5160
  for (const [key, schema] of results) {
5042
5161
  const typeStr = formatType(schema.type);
5043
- const requiredStr = schema.required || schema.default !== void 0 ? chalk31.red("[required]") : chalk31.dim("[optional]");
5044
- console.log(`${chalk31.bold.cyan(key)} ${chalk31.dim("(")}${typeStr}${chalk31.dim(")")} ${requiredStr}`);
5045
- console.log(` ${chalk31.dim(schema.description)}`);
5162
+ const requiredStr = schema.required || schema.default !== void 0 ? chalk32.red("[required]") : chalk32.dim("[optional]");
5163
+ console.log(`${chalk32.bold.cyan(key)} ${chalk32.dim("(")}${typeStr}${chalk32.dim(")")} ${requiredStr}`);
5164
+ console.log(` ${chalk32.dim(schema.description)}`);
5046
5165
  if (schema.default !== void 0) {
5047
- console.log(` ${chalk31.dim("Default:")} ${formatDefault(schema.default, schema.type)}`);
5166
+ console.log(` ${chalk32.dim("Default:")} ${formatDefault(schema.default, schema.type)}`);
5048
5167
  }
5049
5168
  console.log();
5050
5169
  }
5051
5170
  } catch (error) {
5052
- console.error(chalk31.red(`
5171
+ console.error(chalk32.red(`
5053
5172
  \u274C ${error instanceof Error ? error.message : "Unknown error"}
5054
5173
  `));
5055
5174
  process.exit(1);
5056
5175
  }
5057
5176
  }
5058
- var envCommand = new Command12("env").description("Manage environment variables");
5177
+ var envCommand = new Command13("env").description("Manage environment variables");
5059
5178
  envCommand.command("list").description("List all environment variables from schema").option("-p, --package <package>", "Package name to read env schema from", "@spfn/core").option("-g, --group", "Group variables by target file").action(listEnvVars);
5060
5179
  envCommand.command("stats").description("Show environment variable statistics").option("-p, --package <package>", "Package name to read env schema from", "@spfn/core").action(showEnvStats);
5061
5180
  envCommand.command("search").description("Search environment variables").argument("<query>", "Search query (matches key or description)").option("-p, --package <package>", "Package name to read env schema from", "@spfn/core").action(searchEnvVars);
5062
5181
  function validateEnvOption(envValue) {
5063
5182
  if (!VALID_ENVS.includes(envValue)) {
5064
- console.error(chalk31.red(`
5183
+ console.error(chalk32.red(`
5065
5184
  \u274C Invalid environment: "${envValue}"`));
5066
- console.log(chalk31.dim(` Valid values: ${VALID_ENVS.join(", ")}
5185
+ console.log(chalk32.dim(` Valid values: ${VALID_ENVS.join(", ")}
5067
5186
  `));
5068
5187
  process.exit(1);
5069
5188
  }
@@ -5084,8 +5203,8 @@ async function initEnvFiles(options) {
5084
5203
  return acc;
5085
5204
  }, {});
5086
5205
  if (targetEnv) {
5087
- console.log(chalk31.blue.bold(`
5088
- \u{1F680} Generating .env template files for ${chalk31.cyan(targetEnv)} environment
5206
+ console.log(chalk32.blue.bold(`
5207
+ \u{1F680} Generating .env template files for ${chalk32.cyan(targetEnv)} environment
5089
5208
  `));
5090
5209
  const envSpecificFiles = {};
5091
5210
  const committedVars = allVars.filter(([_, schema]) => !schema.sensitive);
@@ -5101,24 +5220,24 @@ async function initEnvFiles(options) {
5101
5220
  writeEnvTemplate(cwd, file, vars, options.force ?? false);
5102
5221
  }
5103
5222
  } else {
5104
- console.log(chalk31.blue.bold(`
5223
+ console.log(chalk32.blue.bold(`
5105
5224
  \u{1F680} Generating .env template files
5106
5225
  `));
5107
5226
  for (const [file, vars] of Object.entries(grouped)) {
5108
5227
  writeEnvTemplate(cwd, file, vars, options.force ?? false);
5109
5228
  }
5110
5229
  }
5111
- console.log(chalk31.dim("\n\u{1F4A1} Copy .example files to create your actual .env files:"));
5112
- console.log(chalk31.dim(" cp .env.example .env"));
5113
- console.log(chalk31.dim(" cp .env.local.example .env.local"));
5114
- console.log(chalk31.dim(" cp .env.server.example .env.server"));
5230
+ console.log(chalk32.dim("\n\u{1F4A1} Copy .example files to create your actual .env files:"));
5231
+ console.log(chalk32.dim(" cp .env.example .env"));
5232
+ console.log(chalk32.dim(" cp .env.local.example .env.local"));
5233
+ console.log(chalk32.dim(" cp .env.server.example .env.server"));
5115
5234
  if (targetEnv) {
5116
- console.log(chalk31.dim(` cp .env.${targetEnv}.example .env.${targetEnv}`));
5117
- console.log(chalk31.dim(` cp .env.${targetEnv}.local.example .env.${targetEnv}.local`));
5235
+ console.log(chalk32.dim(` cp .env.${targetEnv}.example .env.${targetEnv}`));
5236
+ console.log(chalk32.dim(` cp .env.${targetEnv}.local.example .env.${targetEnv}.local`));
5118
5237
  }
5119
5238
  console.log("");
5120
5239
  } catch (error) {
5121
- console.error(chalk31.red(`
5240
+ console.error(chalk32.red(`
5122
5241
  \u274C ${error instanceof Error ? error.message : "Unknown error"}
5123
5242
  `));
5124
5243
  process.exit(1);
@@ -5127,11 +5246,11 @@ async function initEnvFiles(options) {
5127
5246
  function writeEnvTemplate(cwd, file, vars, force) {
5128
5247
  const filePath = resolve2(cwd, file);
5129
5248
  if (existsSync28(filePath) && !force) {
5130
- console.log(chalk31.yellow(` \u23ED\uFE0F ${file} already exists (use --force to overwrite)`));
5249
+ console.log(chalk32.yellow(` \u23ED\uFE0F ${file} already exists (use --force to overwrite)`));
5131
5250
  return;
5132
5251
  }
5133
5252
  writeFileSync17(filePath, generateEnvFileContent(vars), "utf-8");
5134
- console.log(chalk31.green(` \u2705 ${file} (${vars.length} variables)`));
5253
+ console.log(chalk32.green(` \u2705 ${file} (${vars.length} variables)`));
5135
5254
  }
5136
5255
  function generateEnvFileContent(vars) {
5137
5256
  const lines = [
@@ -5166,7 +5285,7 @@ async function checkEnvFiles(options) {
5166
5285
  const envSchema = await loadEnvSchema(packageName);
5167
5286
  const allVars = Object.entries(envSchema);
5168
5287
  const envLabel = targetEnv ? ` (${targetEnv})` : "";
5169
- console.log(chalk31.blue.bold(`
5288
+ console.log(chalk32.blue.bold(`
5170
5289
  \u{1F50D} Checking .env files against schema${envLabel}
5171
5290
  `));
5172
5291
  const filesToCheck = targetEnv ? getEnvFilesForEnvironment(targetEnv) : [...BASE_ENV_FILES.nextjs, ...BASE_ENV_FILES.server];
@@ -5183,7 +5302,7 @@ async function checkEnvFiles(options) {
5183
5302
  for (const [key, value] of Object.entries(parsed)) {
5184
5303
  loadedEnv[key] = { value: value || "", file };
5185
5304
  }
5186
- console.log(chalk31.dim(` \u{1F4C4} ${file} loaded`));
5305
+ console.log(chalk32.dim(` \u{1F4C4} ${file} loaded`));
5187
5306
  }
5188
5307
  console.log("");
5189
5308
  for (const [key, schema] of allVars) {
@@ -5191,7 +5310,7 @@ async function checkEnvFiles(options) {
5191
5310
  const found = loadedEnv[key];
5192
5311
  if (!found) {
5193
5312
  if (schema.required && schema.default === void 0) {
5194
- issues.push(`${chalk31.red("\u2717")} ${chalk31.cyan(key)} is required but not found in any .env file`);
5313
+ issues.push(`${chalk32.red("\u2717")} ${chalk32.cyan(key)} is required but not found in any .env file`);
5195
5314
  }
5196
5315
  continue;
5197
5316
  }
@@ -5201,11 +5320,11 @@ async function checkEnvFiles(options) {
5201
5320
  if (!shouldBeNextjs && isNextjsFile && !isServerFile) {
5202
5321
  if (schema.sensitive) {
5203
5322
  issues.push(
5204
- `${chalk31.red("\u2717")} ${chalk31.cyan(key)} is sensitive and should be in ${chalk31.magenta(expectedFile)}, but found in ${chalk31.yellow(found.file)} (security risk!)`
5323
+ `${chalk32.red("\u2717")} ${chalk32.cyan(key)} is sensitive and should be in ${chalk32.magenta(expectedFile)}, but found in ${chalk32.yellow(found.file)} (security risk!)`
5205
5324
  );
5206
5325
  } else {
5207
5326
  warnings.push(
5208
- `${chalk31.yellow("\u26A0")} ${chalk31.cyan(key)} should be in ${chalk31.magenta(expectedFile)}, but found in ${chalk31.dim(found.file)}`
5327
+ `${chalk32.yellow("\u26A0")} ${chalk32.cyan(key)} should be in ${chalk32.magenta(expectedFile)}, but found in ${chalk32.dim(found.file)}`
5209
5328
  );
5210
5329
  }
5211
5330
  }
@@ -5213,34 +5332,34 @@ async function checkEnvFiles(options) {
5213
5332
  for (const [key, { file }] of Object.entries(loadedEnv)) {
5214
5333
  const inSchema = allVars.some(([k]) => k === key);
5215
5334
  if (!inSchema) {
5216
- warnings.push(`${chalk31.yellow("\u26A0")} ${chalk31.cyan(key)} in ${chalk31.dim(file)} is not in schema`);
5335
+ warnings.push(`${chalk32.yellow("\u26A0")} ${chalk32.cyan(key)} in ${chalk32.dim(file)} is not in schema`);
5217
5336
  }
5218
5337
  }
5219
5338
  if (issues.length > 0) {
5220
- console.log(chalk31.red.bold("Issues:"));
5339
+ console.log(chalk32.red.bold("Issues:"));
5221
5340
  for (const issue of issues) {
5222
5341
  console.log(` ${issue}`);
5223
5342
  }
5224
5343
  console.log("");
5225
5344
  }
5226
5345
  if (warnings.length > 0) {
5227
- console.log(chalk31.yellow.bold("Warnings:"));
5346
+ console.log(chalk32.yellow.bold("Warnings:"));
5228
5347
  for (const warning of warnings) {
5229
5348
  console.log(` ${warning}`);
5230
5349
  }
5231
5350
  console.log("");
5232
5351
  }
5233
5352
  if (issues.length === 0 && warnings.length === 0) {
5234
- console.log(chalk31.green("\u2705 All environment variables are correctly configured!\n"));
5353
+ console.log(chalk32.green("\u2705 All environment variables are correctly configured!\n"));
5235
5354
  } else {
5236
- console.log(chalk31.dim(`Found ${issues.length} issue(s) and ${warnings.length} warning(s)
5355
+ console.log(chalk32.dim(`Found ${issues.length} issue(s) and ${warnings.length} warning(s)
5237
5356
  `));
5238
5357
  if (issues.length > 0) {
5239
5358
  process.exit(1);
5240
5359
  }
5241
5360
  }
5242
5361
  } catch (error) {
5243
- console.error(chalk31.red(`
5362
+ console.error(chalk32.red(`
5244
5363
  \u274C ${error instanceof Error ? error.message : "Unknown error"}
5245
5364
  `));
5246
5365
  process.exit(1);
@@ -5254,15 +5373,15 @@ async function validateEnvVars(options) {
5254
5373
  if (targetEnv) {
5255
5374
  const { loadEnv: loadEnv10 } = await import("@spfn/core/env/loader");
5256
5375
  const result = loadEnv10({ nodeEnv: targetEnv });
5257
- console.log(chalk31.blue.bold(`
5258
- \u{1F50D} Validating environment variables for ${chalk31.cyan(targetEnv)}
5376
+ console.log(chalk32.blue.bold(`
5377
+ \u{1F50D} Validating environment variables for ${chalk32.cyan(targetEnv)}
5259
5378
  `));
5260
5379
  if (result.loadedFiles.length > 0) {
5261
- console.log(chalk31.dim(` Loaded: ${result.loadedFiles.join(", ")}`));
5380
+ console.log(chalk32.dim(` Loaded: ${result.loadedFiles.join(", ")}`));
5262
5381
  }
5263
5382
  console.log("");
5264
5383
  } else {
5265
- console.log(chalk31.blue.bold(`
5384
+ console.log(chalk32.blue.bold(`
5266
5385
  \u{1F50D} Validating environment variables
5267
5386
  `));
5268
5387
  }
@@ -5270,7 +5389,7 @@ async function validateEnvVars(options) {
5270
5389
  const allWarnings = [];
5271
5390
  for (const packageName of packages) {
5272
5391
  try {
5273
- console.log(chalk31.dim(` \u{1F4E6} ${packageName}`));
5392
+ console.log(chalk32.dim(` \u{1F4E6} ${packageName}`));
5274
5393
  const envSchema = await loadEnvSchema(packageName);
5275
5394
  const { createEnvRegistry } = await import("@spfn/core/env");
5276
5395
  const registry = createEnvRegistry(envSchema);
@@ -5283,10 +5402,10 @@ async function validateEnvVars(options) {
5283
5402
  }
5284
5403
  } catch (error) {
5285
5404
  if (error instanceof Error && error.message.includes("does not export envSchema")) {
5286
- console.log(chalk31.dim(` \u23ED\uFE0F No envSchema exported, skipping`));
5405
+ console.log(chalk32.dim(` \u23ED\uFE0F No envSchema exported, skipping`));
5287
5406
  continue;
5288
5407
  }
5289
- console.error(chalk31.red(` \u274C Failed to load: ${error instanceof Error ? error.message : String(error)}`));
5408
+ console.error(chalk32.red(` \u274C Failed to load: ${error instanceof Error ? error.message : String(error)}`));
5290
5409
  if (options.strict) {
5291
5410
  process.exit(1);
5292
5411
  }
@@ -5294,32 +5413,32 @@ async function validateEnvVars(options) {
5294
5413
  }
5295
5414
  console.log("");
5296
5415
  if (allErrors.length > 0) {
5297
- console.log(chalk31.red.bold(`\u274C Validation Errors (${allErrors.length}):
5416
+ console.log(chalk32.red.bold(`\u274C Validation Errors (${allErrors.length}):
5298
5417
  `));
5299
5418
  for (const error of allErrors) {
5300
- console.log(` ${chalk31.red("\u2717")} ${chalk31.cyan(error.key)}`);
5301
- console.log(` ${chalk31.dim(error.message)}`);
5302
- console.log(` ${chalk31.dim(`from ${error.package}`)}`);
5419
+ console.log(` ${chalk32.red("\u2717")} ${chalk32.cyan(error.key)}`);
5420
+ console.log(` ${chalk32.dim(error.message)}`);
5421
+ console.log(` ${chalk32.dim(`from ${error.package}`)}`);
5303
5422
  console.log("");
5304
5423
  }
5305
5424
  }
5306
5425
  if (allWarnings.length > 0) {
5307
- console.log(chalk31.yellow.bold(`\u26A0\uFE0F Warnings (${allWarnings.length}):
5426
+ console.log(chalk32.yellow.bold(`\u26A0\uFE0F Warnings (${allWarnings.length}):
5308
5427
  `));
5309
5428
  for (const warning of allWarnings) {
5310
- console.log(` ${chalk31.yellow("\u26A0")} ${chalk31.cyan(warning.key)}`);
5311
- console.log(` ${chalk31.dim(warning.message)}`);
5429
+ console.log(` ${chalk32.yellow("\u26A0")} ${chalk32.cyan(warning.key)}`);
5430
+ console.log(` ${chalk32.dim(warning.message)}`);
5312
5431
  console.log("");
5313
5432
  }
5314
5433
  }
5315
5434
  if (allErrors.length === 0 && allWarnings.length === 0) {
5316
- console.log(chalk31.green.bold("\u2705 All environment variables are valid!\n"));
5435
+ console.log(chalk32.green.bold("\u2705 All environment variables are valid!\n"));
5317
5436
  } else if (allErrors.length === 0) {
5318
- console.log(chalk31.green("\u2705 No errors found."));
5319
- console.log(chalk31.yellow(`\u26A0\uFE0F ${allWarnings.length} warning(s) found.
5437
+ console.log(chalk32.green("\u2705 No errors found."));
5438
+ console.log(chalk32.yellow(`\u26A0\uFE0F ${allWarnings.length} warning(s) found.
5320
5439
  `));
5321
5440
  } else {
5322
- console.log(chalk31.red(`
5441
+ console.log(chalk32.red(`
5323
5442
  \u274C Validation failed with ${allErrors.length} error(s)
5324
5443
  `));
5325
5444
  process.exit(1);
@@ -5328,12 +5447,12 @@ async function validateEnvVars(options) {
5328
5447
  envCommand.command("validate").description("Validate environment variables against schema (for CI/CD)").option("-p, --packages <packages...>", "Packages to validate", ["@spfn/core"]).option("-e, --env <environment>", "Load env files for specific environment before validating").option("-s, --strict", "Exit on any error (including load failures)").action(validateEnvVars);
5329
5448
 
5330
5449
  // src/commands/secret/index.ts
5331
- import { Command as Command13 } from "commander";
5450
+ import { Command as Command14 } from "commander";
5332
5451
 
5333
5452
  // src/commands/secret/set.ts
5334
5453
  init_logger();
5335
5454
  import prompts9 from "prompts";
5336
- import chalk32 from "chalk";
5455
+ import chalk33 from "chalk";
5337
5456
 
5338
5457
  // src/commands/secret/options.ts
5339
5458
  init_logger();
@@ -5348,7 +5467,7 @@ function resolveEnv(env9) {
5348
5467
 
5349
5468
  // src/commands/secret/store-value.ts
5350
5469
  init_logger();
5351
- import { join as join27 } from "path";
5470
+ import { join as join28 } from "path";
5352
5471
  init_env_file();
5353
5472
 
5354
5473
  // src/utils/sops.ts
@@ -5397,19 +5516,19 @@ async function sopsUpdateKeys(absFile) {
5397
5516
 
5398
5517
  // src/utils/secret-config.ts
5399
5518
  import { existsSync as existsSync30, readdirSync as readdirSync3 } from "fs";
5400
- import { join as join26 } from "path";
5519
+ import { join as join27 } from "path";
5401
5520
  var SECRETS_DIR = "secrets";
5402
5521
  function getSopsFile(cwd, env9) {
5403
5522
  const relFile = `${SECRETS_DIR}/${env9}.enc.json`;
5404
- return { absFile: join26(cwd, relFile), relFile };
5523
+ return { absFile: join27(cwd, relFile), relFile };
5405
5524
  }
5406
5525
  function findUp(cwd, filename, maxDepth = 6) {
5407
5526
  let dir = cwd;
5408
5527
  for (let depth = 0; depth < maxDepth; depth++) {
5409
- if (existsSync30(join26(dir, filename))) {
5528
+ if (existsSync30(join27(dir, filename))) {
5410
5529
  return dir;
5411
5530
  }
5412
- const parent = join26(dir, "..");
5531
+ const parent = join27(dir, "..");
5413
5532
  if (parent === dir) {
5414
5533
  break;
5415
5534
  }
@@ -5419,17 +5538,17 @@ function findUp(cwd, filename, maxDepth = 6) {
5419
5538
  }
5420
5539
  function findSopsConfig(cwd) {
5421
5540
  const dir = findUp(cwd, ".sops.yaml");
5422
- return dir ? join26(dir, ".sops.yaml") : null;
5541
+ return dir ? join27(dir, ".sops.yaml") : null;
5423
5542
  }
5424
5543
  function hasSopsConfig(cwd) {
5425
5544
  return findSopsConfig(cwd) !== null;
5426
5545
  }
5427
5546
  function listSopsFiles(cwd) {
5428
- const dir = join26(cwd, SECRETS_DIR);
5547
+ const dir = join27(cwd, SECRETS_DIR);
5429
5548
  if (!existsSync30(dir)) {
5430
5549
  return [];
5431
5550
  }
5432
- return readdirSync3(dir).filter((name) => name.endsWith(".enc.json")).map((name) => join26(dir, name));
5551
+ return readdirSync3(dir).filter((name) => name.endsWith(".enc.json")).map((name) => join27(dir, name));
5433
5552
  }
5434
5553
 
5435
5554
  // src/commands/secret/store-value.ts
@@ -5458,7 +5577,7 @@ async function storeSecret(cwd, env9, key, value) {
5458
5577
  );
5459
5578
  }
5460
5579
  await store.set(keychainName(key), value);
5461
- const serverEnvPath = join27(cwd, ".env.server");
5580
+ const serverEnvPath = join28(cwd, ".env.server");
5462
5581
  const result = upsertEnvVar(serverEnvPath, key, keychainRef(key));
5463
5582
  restrictEnvFilePerms(serverEnvPath);
5464
5583
  ensureGitignored(cwd, [{ pattern: ".env.server", comment: "spfn server env (secrets)" }]);
@@ -5489,7 +5608,7 @@ async function secretSet(key, options) {
5489
5608
  const { value } = await prompts9({
5490
5609
  type: "password",
5491
5610
  name: "value",
5492
- message: `Value for ${chalk32.cyan(resolvedKey)} (${env9})`
5611
+ message: `Value for ${chalk33.cyan(resolvedKey)} (${env9})`
5493
5612
  });
5494
5613
  if (!value) {
5495
5614
  logger.warn("Cancelled \u2014 no value entered.");
@@ -5538,7 +5657,7 @@ async function warnIfNotSecret(pkg, key) {
5538
5657
 
5539
5658
  // src/commands/secret/list.ts
5540
5659
  init_logger();
5541
- import chalk33 from "chalk";
5660
+ import chalk34 from "chalk";
5542
5661
  async function secretList(options) {
5543
5662
  const env9 = resolveEnv(options.env);
5544
5663
  const pkg = options.package ?? "@spfn/core";
@@ -5554,12 +5673,12 @@ async function secretList(options) {
5554
5673
  return;
5555
5674
  }
5556
5675
  const present = await loadPresence(env9, entries);
5557
- console.log(chalk33.blue.bold(`
5676
+ console.log(chalk34.blue.bold(`
5558
5677
  \u{1F511} Secrets (${pkg}) \u2014 ${env9}
5559
5678
  `));
5560
5679
  for (const entry of entries) {
5561
5680
  const status = statusOf(entry, present.has(entry.key));
5562
- console.log(` ${badge(status)} ${chalk33.cyan(entry.key)}${entry.generate ? chalk33.dim(" (generatable)") : ""}`);
5681
+ console.log(` ${badge(status)} ${chalk34.cyan(entry.key)}${entry.generate ? chalk34.dim(" (generatable)") : ""}`);
5563
5682
  }
5564
5683
  console.log();
5565
5684
  }
@@ -5592,17 +5711,17 @@ function statusOf(entry, present) {
5592
5711
  function badge(status) {
5593
5712
  switch (status) {
5594
5713
  case "set":
5595
- return chalk33.green("\u25CF");
5714
+ return chalk34.green("\u25CF");
5596
5715
  case "missing":
5597
- return chalk33.yellow("\u25CB");
5716
+ return chalk34.yellow("\u25CB");
5598
5717
  case "awaiting-input":
5599
- return chalk33.red("\u25CB");
5718
+ return chalk34.red("\u25CB");
5600
5719
  }
5601
5720
  }
5602
5721
 
5603
5722
  // src/commands/secret/generate.ts
5604
5723
  init_logger();
5605
- import chalk34 from "chalk";
5724
+ import chalk35 from "chalk";
5606
5725
  async function secretGenerate(key, options) {
5607
5726
  const env9 = resolveEnv(options.env);
5608
5727
  const pkg = options.package ?? "@spfn/core";
@@ -5640,7 +5759,7 @@ function requireGeneratable(schema, key) {
5640
5759
  process.exit(1);
5641
5760
  }
5642
5761
  if (!entry.generate) {
5643
- logger.error(`${chalk34.cyan(key)} has no generate strategy \u2014 it's an external value. Use \`spfn secret set ${key}\`.`);
5762
+ logger.error(`${chalk35.cyan(key)} has no generate strategy \u2014 it's an external value. Use \`spfn secret set ${key}\`.`);
5644
5763
  process.exit(1);
5645
5764
  }
5646
5765
  return entry;
@@ -5648,7 +5767,7 @@ function requireGeneratable(schema, key) {
5648
5767
 
5649
5768
  // src/commands/secret/rotate.ts
5650
5769
  init_logger();
5651
- import chalk35 from "chalk";
5770
+ import chalk36 from "chalk";
5652
5771
  async function secretRotate(key, options) {
5653
5772
  const env9 = resolveEnv(options.env);
5654
5773
  const pkg = options.package ?? "@spfn/core";
@@ -5674,7 +5793,7 @@ async function secretRotate(key, options) {
5674
5793
  }
5675
5794
  }
5676
5795
  for (const entry of external) {
5677
- logger.warn(`${chalk35.cyan(entry.key)} is external \u2014 reissue it at the provider, then \`spfn secret set ${entry.key} --env ${env9}\`.`);
5796
+ logger.warn(`${chalk36.cyan(entry.key)} is external \u2014 reissue it at the provider, then \`spfn secret set ${entry.key} --env ${env9}\`.`);
5678
5797
  }
5679
5798
  if (generatable.length > 0) {
5680
5799
  logger.info("Rotation updates the source only \u2014 commit and deploy to apply.");
@@ -5698,10 +5817,10 @@ init_logger();
5698
5817
  import { execa as execa12 } from "execa";
5699
5818
  import { existsSync as existsSync31, mkdirSync as mkdirSync6 } from "fs";
5700
5819
  import { homedir } from "os";
5701
- import { dirname as dirname5, join as join28 } from "path";
5702
- import chalk36 from "chalk";
5820
+ import { dirname as dirname5, join as join29 } from "path";
5821
+ import chalk37 from "chalk";
5703
5822
  function ageKeyFile() {
5704
- return process.env.SOPS_AGE_KEY_FILE ?? join28(homedir(), ".config", "sops", "age", "keys.txt");
5823
+ return process.env.SOPS_AGE_KEY_FILE ?? join29(homedir(), ".config", "sops", "age", "keys.txt");
5705
5824
  }
5706
5825
  async function ensureAgeInstalled() {
5707
5826
  try {
@@ -5737,27 +5856,27 @@ function printPublicKeys(keys) {
5737
5856
  logger.warn("Could not read the public key \u2014 run `age-keygen -y <file>` manually.");
5738
5857
  return;
5739
5858
  }
5740
- console.log(chalk36.bold("\nPublic key(s):"));
5859
+ console.log(chalk37.bold("\nPublic key(s):"));
5741
5860
  for (const key of keys) {
5742
- console.log(` ${chalk36.cyan(key)}`);
5861
+ console.log(` ${chalk37.cyan(key)}`);
5743
5862
  }
5744
- console.log(chalk36.dim("\nRegister it as a recipient:"));
5745
- console.log(chalk36.dim(` spfn secret recipients add ${keys[0]}
5863
+ console.log(chalk37.dim("\nRegister it as a recipient:"));
5864
+ console.log(chalk37.dim(` spfn secret recipients add ${keys[0]}
5746
5865
  `));
5747
5866
  }
5748
5867
 
5749
5868
  // src/commands/secret/recipients.ts
5750
5869
  init_logger();
5751
5870
  import { existsSync as existsSync32, readFileSync as readFileSync13, writeFileSync as writeFileSync19 } from "fs";
5752
- import { join as join29 } from "path";
5871
+ import { join as join30 } from "path";
5753
5872
  import { parse as parse3, stringify } from "yaml";
5754
- import chalk37 from "chalk";
5873
+ import chalk38 from "chalk";
5755
5874
  var SOPS_CONFIG = ".sops.yaml";
5756
5875
  var DEFAULT_PATH_REGEX = "secrets/.*\\.enc\\.json$";
5757
5876
  var AGE_RECIPIENT = /^age1[0-9a-z]+$/;
5758
5877
  async function secretRecipients(action, key, _options) {
5759
5878
  const cwd = process.cwd();
5760
- const configPath = join29(cwd, SOPS_CONFIG);
5879
+ const configPath = join30(cwd, SOPS_CONFIG);
5761
5880
  if (action === "list") {
5762
5881
  listRecipients(configPath);
5763
5882
  return;
@@ -5795,13 +5914,13 @@ function listRecipients(configPath) {
5795
5914
  }
5796
5915
  const config = loadConfig(configPath);
5797
5916
  const rules = config.creation_rules ?? [];
5798
- console.log(chalk37.blue.bold(`
5917
+ console.log(chalk38.blue.bold(`
5799
5918
  \u{1F465} Recipients (${SOPS_CONFIG})
5800
5919
  `));
5801
5920
  for (const rule of rules) {
5802
- console.log(chalk37.dim(` ${rule.path_regex ?? "(any path)"}`));
5921
+ console.log(chalk38.dim(` ${rule.path_regex ?? "(any path)"}`));
5803
5922
  for (const recipient of parseRecipients(rule.age)) {
5804
- console.log(` ${chalk37.cyan(recipient)}`);
5923
+ console.log(` ${chalk38.cyan(recipient)}`);
5805
5924
  }
5806
5925
  }
5807
5926
  console.log();
@@ -5842,8 +5961,8 @@ async function reencrypt(cwd) {
5842
5961
 
5843
5962
  // src/commands/secret/check.ts
5844
5963
  init_logger();
5845
- import { join as join30 } from "path";
5846
- import chalk38 from "chalk";
5964
+ import { join as join31 } from "path";
5965
+ import chalk39 from "chalk";
5847
5966
  init_env_file();
5848
5967
  var COMMITTED_FILES = [".env", ".env.example"];
5849
5968
  var PLACEHOLDER = /(your-|changeme|placeholder|example|<.*>)/i;
@@ -5861,35 +5980,35 @@ async function secretCheck(options) {
5861
5980
  const issues = [];
5862
5981
  const warnings = [];
5863
5982
  for (const file of COMMITTED_FILES) {
5864
- const parsed = parseEnvFile(join30(cwd, file));
5983
+ const parsed = parseEnvFile(join31(cwd, file));
5865
5984
  for (const [key, value] of Object.entries(parsed)) {
5866
5985
  if (secretKeys.has(key) && value.length > 0 && !PLACEHOLDER.test(value)) {
5867
- issues.push(`${chalk38.cyan(key)} has a real value in committed ${chalk38.yellow(file)} \u2014 move it to the keychain/SOPS.`);
5986
+ issues.push(`${chalk39.cyan(key)} has a real value in committed ${chalk39.yellow(file)} \u2014 move it to the keychain/SOPS.`);
5868
5987
  }
5869
5988
  }
5870
5989
  }
5871
- const serverEnv = parseEnvFile(join30(cwd, ".env.server"));
5990
+ const serverEnv = parseEnvFile(join31(cwd, ".env.server"));
5872
5991
  for (const key of secretKeys) {
5873
5992
  const value = serverEnv[key];
5874
5993
  if (value && !value.startsWith(KEYCHAIN_REF_PREFIX)) {
5875
- warnings.push(`${chalk38.cyan(key)} is plaintext in .env.server \u2014 run \`spfn secret set ${key}\` to move it to the keychain.`);
5994
+ warnings.push(`${chalk39.cyan(key)} is plaintext in .env.server \u2014 run \`spfn secret set ${key}\` to move it to the keychain.`);
5876
5995
  }
5877
5996
  }
5878
5997
  report(issues, warnings, cwd);
5879
5998
  }
5880
5999
  function report(issues, warnings, cwd) {
5881
- console.log(chalk38.blue.bold("\n\u{1F50D} Secret hygiene check\n"));
6000
+ console.log(chalk39.blue.bold("\n\u{1F50D} Secret hygiene check\n"));
5882
6001
  for (const issue of issues) {
5883
- console.log(` ${chalk38.red("\u2717")} ${issue}`);
6002
+ console.log(` ${chalk39.red("\u2717")} ${issue}`);
5884
6003
  }
5885
6004
  for (const warning of warnings) {
5886
- console.log(` ${chalk38.yellow("\u26A0")} ${warning}`);
6005
+ console.log(` ${chalk39.yellow("\u26A0")} ${warning}`);
5887
6006
  }
5888
6007
  if (!hasSopsConfig(cwd)) {
5889
- console.log(` ${chalk38.dim("\u2139 no .sops.yaml \u2014 prod secrets need a backend (spfn secret keygen, or add a KMS rule)")}`);
6008
+ console.log(` ${chalk39.dim("\u2139 no .sops.yaml \u2014 prod secrets need a backend (spfn secret keygen, or add a KMS rule)")}`);
5890
6009
  }
5891
6010
  if (issues.length === 0 && warnings.length === 0) {
5892
- console.log(chalk38.green(" \u2713 No plaintext secret leaks found."));
6011
+ console.log(chalk39.green(" \u2713 No plaintext secret leaks found."));
5893
6012
  }
5894
6013
  console.log();
5895
6014
  if (issues.length > 0) {
@@ -5900,7 +6019,7 @@ function report(issues, warnings, cwd) {
5900
6019
  // src/commands/secret/index.ts
5901
6020
  var ENV_OPTION = ["-e, --env <env>", "Target environment (local | development | staging | production)", "local"];
5902
6021
  var PKG_OPTION = ["-p, --package <package>", "Package whose env schema to read", "@spfn/core"];
5903
- var secretCommand = new Command13("secret").description("Manage secrets: keychain locally, SOPS (age / GCP KMS / AWS KMS) for deployed environments");
6022
+ var secretCommand = new Command14("secret").description("Manage secrets: keychain locally, SOPS (age / GCP KMS / AWS KMS) for deployed environments");
5904
6023
  secretCommand.command("set [key]").description("Store a secret value (prompts for the value, masked)").option(...ENV_OPTION).option(...PKG_OPTION).action(secretSet);
5905
6024
  secretCommand.command("list").description("List declared secrets and their status (never prints values)").option(...ENV_OPTION).option(...PKG_OPTION).action(secretList);
5906
6025
  secretCommand.command("generate [key]").description("Generate value(s) for schema secrets that declare a generate strategy").option("-a, --all", "Generate every generatable secret").option(...ENV_OPTION).option(...PKG_OPTION).action(secretGenerate);
@@ -5911,7 +6030,7 @@ secretCommand.command("check").description("Static hygiene lint \u2014 flag plai
5911
6030
 
5912
6031
  // src/index.ts
5913
6032
  init_version();
5914
- var program = new Command14();
6033
+ var program = new Command15();
5915
6034
  program.name("spfn").description("SPFN CLI - The Missing Backend for Next.js").version(getCliVersion());
5916
6035
  program.addCommand(createCommand);
5917
6036
  program.addCommand(initCommand);
@@ -5921,6 +6040,7 @@ program.addCommand(buildCommand);
5921
6040
  program.addCommand(startCommand);
5922
6041
  program.addCommand(provisionCommand);
5923
6042
  program.addCommand(codegenCommand);
6043
+ program.addCommand(contractCommand);
5924
6044
  program.addCommand(keyCommand);
5925
6045
  program.addCommand(setupCommand);
5926
6046
  program.addCommand(dbCommand);