@geekmidas/cli 10.0.0-alpha.36 → 10.0.0-alpha.37

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.
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import { c as isPartitionedRoutes, i as ConstructGenerator, t as discover } from
4
4
  import { a as loadConfig, c as parseModuleConfig, i as loadAppConfig, l as appKey, r as getAppNameFromCwd, s as loadWorkspaceConfig, u as derivedApps } from "./config-Dss1WwtH.mjs";
5
5
  import { D as assertDeployedStage, E as TEST_STAGE, O as deployedProblems, T as InvalidStages, a as getAppBuildOrder, b as getDeployTargetError, k as stageProblems, m as stripPublicPrefix, o as getAppGkmConfig, p as getPublicEnvPrefix, t as allConstructGlobs, x as isDeployTargetSupported } from "./workspace-CsL76tpc.mjs";
6
6
  import { c as getKeyPath, i as setCustomSecret, n as readStageSecrets, o as toEmbeddableSecrets, r as secretsExist, s as writeStageSecrets, t as maskPassword } from "./storage-BjTLjDDp.mjs";
7
- import { _ as findAvailablePort, b as loadSecretsForApp, c as applyPostgres, d as cacheBackendFor, f as eventsBackendFor, g as createEntryWrapper, h as createCredentialsPreload, i as reconcileWorkspace, l as resourceName, m as storageBackendFor, n as constructGlobs, o as COMPOSE_PATH, p as providerOf, r as derivedContainers, s as LOCAL_CA_PATH, t as backendsOf, u as rootSite, v as isPortAvailable, x as prepareEntryCredentials, y as loadEnvFiles } from "./workspace-EmXwO2qM.mjs";
7
+ import { C as loadEnvFiles, S as isPortAvailable, T as prepareEntryCredentials, _ as providerOf, b as createEntryWrapper, c as forgetState, d as pgClient, f as resourceName, g as eventsBackendFor, h as cacheBackendFor, i as reconcileWorkspace, l as applyPostgres, m as primaryPortKey, n as constructGlobs, o as COMPOSE_PATH, p as rootSite, r as derivedContainers, s as LOCAL_CA_PATH, t as backendsOf, u as quoteIdentifier, v as storageBackendFor, w as loadSecretsForApp, x as findAvailablePort, y as createCredentialsPreload } from "./workspace-BtXCL1C8.mjs";
8
8
  import { n as EndpointGenerator, r as runtimeFor } from "./OpenApiTsGenerator-hDINScnz.mjs";
9
9
  import { generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi.mjs";
10
10
  import { a as writeTestHarness, i as TEST_MANIFEST_ENV, n as TEST_READY_FILE, o as cacheBackendsIn, s as driversFor, t as TEST_READY_ENV } from "./ready-D1BCiaYU.mjs";
@@ -37,7 +37,7 @@ import { Subscriber } from "@geekmidas/constructs/subscribers";
37
37
  import { Topic } from "@geekmidas/constructs/topic";
38
38
  import { lookup } from "node:dns/promises";
39
39
  //#region package.json
40
- var version = "10.0.0-alpha.36";
40
+ var version = "10.0.0-alpha.37";
41
41
  //#endregion
42
42
  //#region src/auth/credentials.ts
43
43
  /**
@@ -1087,9 +1087,9 @@ async function workspaceDevCommand(workspace, options) {
1087
1087
  ...reconciled.env
1088
1088
  };
1089
1089
  if (options.migrate || options.seed) {
1090
- const { prepareDevDatabases } = await import("./migrate-CT8b3nt_.mjs");
1090
+ const { prepareDevDatabases } = await import("./migrate-_2J3lFT5.mjs");
1091
1091
  await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
1092
- } else await import("./migrate-CT8b3nt_.mjs").then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
1092
+ } else await import("./migrate-_2J3lFT5.mjs").then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
1093
1093
  logger$10.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
1094
1094
  });
1095
1095
  const appUrls = {};
@@ -8798,26 +8798,26 @@ function loadPackageJson() {
8798
8798
  * Run: pnpm --filter @geekmidas/cli sync-versions
8799
8799
  */
8800
8800
  const GEEKMIDAS_VERSIONS = {
8801
- "@geekmidas/audit": "~10.0.0-alpha.36",
8802
- "@geekmidas/auth": "~10.0.0-alpha.36",
8803
- "@geekmidas/cache": "~10.0.0-alpha.36",
8804
- "@geekmidas/client": "~10.0.0-alpha.36",
8805
- "@geekmidas/cloud": "~10.0.0-alpha.36",
8806
- "@geekmidas/constructs": "~10.0.0-alpha.36",
8807
- "@geekmidas/db": "~10.0.0-alpha.36",
8808
- "@geekmidas/emailkit": "~10.0.0-alpha.36",
8809
- "@geekmidas/envkit": "~10.0.0-alpha.36",
8810
- "@geekmidas/errors": "~10.0.0-alpha.36",
8811
- "@geekmidas/events": "~10.0.0-alpha.36",
8812
- "@geekmidas/logger": "~10.0.0-alpha.36",
8813
- "@geekmidas/manifest": "~10.0.0-alpha.36",
8814
- "@geekmidas/rate-limit": "~10.0.0-alpha.36",
8815
- "@geekmidas/schema": "~10.0.0-alpha.36",
8816
- "@geekmidas/services": "~10.0.0-alpha.36",
8817
- "@geekmidas/storage": "~10.0.0-alpha.36",
8818
- "@geekmidas/studio": "~10.0.0-alpha.36",
8819
- "@geekmidas/telescope": "~10.0.0-alpha.36",
8820
- "@geekmidas/testkit": "~10.0.0-alpha.36",
8801
+ "@geekmidas/audit": "~10.0.0-alpha.37",
8802
+ "@geekmidas/auth": "~10.0.0-alpha.37",
8803
+ "@geekmidas/cache": "~10.0.0-alpha.37",
8804
+ "@geekmidas/client": "~10.0.0-alpha.37",
8805
+ "@geekmidas/cloud": "~10.0.0-alpha.37",
8806
+ "@geekmidas/constructs": "~10.0.0-alpha.37",
8807
+ "@geekmidas/db": "~10.0.0-alpha.37",
8808
+ "@geekmidas/emailkit": "~10.0.0-alpha.37",
8809
+ "@geekmidas/envkit": "~10.0.0-alpha.37",
8810
+ "@geekmidas/errors": "~10.0.0-alpha.37",
8811
+ "@geekmidas/events": "~10.0.0-alpha.37",
8812
+ "@geekmidas/logger": "~10.0.0-alpha.37",
8813
+ "@geekmidas/manifest": "~10.0.0-alpha.37",
8814
+ "@geekmidas/rate-limit": "~10.0.0-alpha.37",
8815
+ "@geekmidas/schema": "~10.0.0-alpha.37",
8816
+ "@geekmidas/services": "~10.0.0-alpha.37",
8817
+ "@geekmidas/storage": "~10.0.0-alpha.37",
8818
+ "@geekmidas/studio": "~10.0.0-alpha.37",
8819
+ "@geekmidas/telescope": "~10.0.0-alpha.37",
8820
+ "@geekmidas/testkit": "~10.0.0-alpha.37",
8821
8821
  "@geekmidas/cli": `~${loadPackageJson().version}`
8822
8822
  };
8823
8823
  //#endregion
@@ -15149,7 +15149,7 @@ var NoStoredSecrets = class extends Error {
15149
15149
  }
15150
15150
  };
15151
15151
  async function reconcile(secrets, workspace, stage) {
15152
- const { derivedContainers } = await import("./workspace-EmXwO2qM.mjs").then((n) => n.a);
15152
+ const { derivedContainers } = await import("./workspace-BtXCL1C8.mjs").then((n) => n.a);
15153
15153
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
15154
15154
  return result ? {
15155
15155
  secrets: result.secrets,
@@ -15448,6 +15448,56 @@ function printSummary(workspace, stage) {
15448
15448
  logger$1.log("");
15449
15449
  }
15450
15450
  //#endregion
15451
+ //#region src/test/teardown.ts
15452
+ /**
15453
+ * Dropping the test stage's databases when a suite ends.
15454
+ *
15455
+ * Reconcile never drops — the line it draws is at data. The test stage's data
15456
+ * is the exception: every test runs in a transaction rolled back after it, so
15457
+ * nothing in these databases is anyone's, and a database kept between runs is
15458
+ * one whose migrations already ran — an edited migration is never applied to
15459
+ * it again. So each run starts from a database it created, and drops it when
15460
+ * the suite ends: the Vitest global setup's teardown.
15461
+ */
15462
+ /**
15463
+ * Drop the databases the last setup recorded, and forget the test stage's
15464
+ * reconcile, so the next run provisions them from nothing.
15465
+ *
15466
+ * A no-op when nothing is recorded — no run yet, or one already torn down —
15467
+ * so running it twice, or before a setup, is harmless.
15468
+ */
15469
+ async function dropTestDatabases(cwd, root, sql = pgClient) {
15470
+ const readyPath = join(cwd, TEST_READY_FILE);
15471
+ const ready = await readReady(readyPath);
15472
+ const recorded = ready?.databases;
15473
+ if (!ready || !recorded) return [];
15474
+ const client = sql(recorded.port);
15475
+ for (const name of recorded.names) {
15476
+ if (!name.endsWith(`_test`)) throw new NotATestDatabase(name);
15477
+ await client.query(void 0, `DROP DATABASE IF EXISTS ${quoteIdentifier(name)} WITH (FORCE)`);
15478
+ }
15479
+ await forgetState(root, TEST_STAGE);
15480
+ const { databases: _dropped, ...rest } = ready;
15481
+ await writeFile(readyPath, JSON.stringify(rest, null, 2));
15482
+ return recorded.names;
15483
+ }
15484
+ async function readReady(path) {
15485
+ try {
15486
+ return JSON.parse(await readFile(path, "utf-8"));
15487
+ } catch {
15488
+ return;
15489
+ }
15490
+ }
15491
+ /** A name recorded for the test stage without the test stage's suffix. */
15492
+ var NotATestDatabase = class extends Error {
15493
+ database;
15494
+ constructor(database) {
15495
+ super(`Refusing to drop '${database}': only the test stage's databases (named '…_${TEST_STAGE}') are dropped after a suite. Delete ${TEST_READY_FILE} if it was edited by hand.`);
15496
+ this.database = database;
15497
+ this.name = "NotATestDatabase";
15498
+ }
15499
+ };
15500
+ //#endregion
15451
15501
  //#region src/test/index.ts
15452
15502
  /**
15453
15503
  * Run tests with secrets, dependency URLs, and .env files loaded.
@@ -15456,6 +15506,10 @@ function printSummary(workspace, stage) {
15456
15506
  async function testCommand(options = {}) {
15457
15507
  const cwd = process.cwd();
15458
15508
  const stage = options.stage ?? await loadWorkspaceConfig(cwd).then((loaded) => loaded.workspace.stages.local).catch(() => void 0);
15509
+ if (options.teardown) {
15510
+ await teardown(cwd);
15511
+ return;
15512
+ }
15459
15513
  if (!stage) throw new NoStageToTest();
15460
15514
  console.log(`\n🧪 Running tests with ${stage} environment...\n`);
15461
15515
  if (options.autoSetup || Boolean(process.env.GKM_AUTO_SETUP)) {
@@ -15464,6 +15518,7 @@ async function testCommand(options = {}) {
15464
15518
  }
15465
15519
  const defaultEnv = loadEnvFiles(".env");
15466
15520
  if (defaultEnv.loaded.length > 0) console.log(` 📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
15521
+ if (!options.prepare) await teardown(cwd);
15467
15522
  const result = await prepareEntryCredentials({
15468
15523
  stage,
15469
15524
  startDocker: !options.prepare,
@@ -15504,7 +15559,8 @@ async function testCommand(options = {}) {
15504
15559
  const readyPath = join(cwd, TEST_READY_FILE);
15505
15560
  await writeFile(readyPath, JSON.stringify({
15506
15561
  env: result.secretsJsonPath,
15507
- ...manifestPath ? { manifest: manifestPath } : {}
15562
+ ...manifestPath ? { manifest: manifestPath } : {},
15563
+ ...testDatabases(result.reconciled)
15508
15564
  }, null, 2));
15509
15565
  if (options.setup) {
15510
15566
  console.log(` 🧪 Test stage ready: ${TEST_READY_FILE}`);
@@ -15543,12 +15599,29 @@ async function testCommand(options = {}) {
15543
15599
  });
15544
15600
  });
15545
15601
  }
15602
+ /** Drop what the last setup created — see `dropTestDatabases`. */
15603
+ async function teardown(cwd) {
15604
+ const dropped = await dropTestDatabases(cwd, await loadWorkspaceConfig(cwd).then((loaded) => loaded.workspace.root).catch(() => cwd));
15605
+ if (dropped.length > 0) console.log(` 🧹 Dropped ${dropped.join(", ")}`);
15606
+ }
15607
+ /**
15608
+ * The test stage's Postgres databases, to drop when the suite ends — those
15609
+ * the plan provisions, on the port the shared container answers.
15610
+ */
15611
+ function testDatabases(reconciled) {
15612
+ const port = reconciled?.ports[primaryPortKey("postgres")];
15613
+ const names = (reconciled?.plan.resources ?? []).filter((resource) => resource.kind === "database" && resource.provisions && resource.container === "postgres").map((resource) => resource.name);
15614
+ return port !== void 0 && names.length > 0 ? { databases: {
15615
+ port,
15616
+ names
15617
+ } } : {};
15618
+ }
15546
15619
  /**
15547
15620
  * Migrate the test stage just reconciled, then seed it: a test run never
15548
15621
  * starts without the reference data the app needs.
15549
15622
  */
15550
15623
  async function migrateTestStage(workspace, env) {
15551
- const { migrateAndSeed } = await import("./migrate-CT8b3nt_.mjs");
15624
+ const { migrateAndSeed } = await import("./migrate-_2J3lFT5.mjs");
15552
15625
  const { discover } = await import("./discover-CMxesskG.mjs").then((n) => n.n);
15553
15626
  const sources = {};
15554
15627
  const manifest = await discover({
@@ -15976,7 +16049,7 @@ program.command("exec").description("Run a command with secrets injected into Cr
15976
16049
  process.exit(1);
15977
16050
  }
15978
16051
  });
15979
- program.command("test").description("Run tests with secrets loaded from environment").option("--stage <stage>", "Stage to load secrets from (default: stages.local)").option("--run", "Run tests once without watch mode").option("--watch", "Enable watch mode").option("--coverage", "Generate coverage report").option("--ui", "Open Vitest UI").option("--auto-setup", "Generate a fresh stage (secrets + key) from the workspace config when none exists (for CI; also via GKM_AUTO_SETUP)").option("--prepare", "Write the test manifest and #test harness, then stop — for a typecheck that runs before the suite").option("--setup", "Reconcile, migrate and seed the test stage and write the harness, then stop — what @geekmidas/cli/vitest runs").argument("[pattern]", "Pattern to filter tests").action(async (pattern, options) => {
16052
+ program.command("test").description("Run tests with secrets loaded from environment").option("--stage <stage>", "Stage to load secrets from (default: stages.local)").option("--run", "Run tests once without watch mode").option("--watch", "Enable watch mode").option("--coverage", "Generate coverage report").option("--ui", "Open Vitest UI").option("--auto-setup", "Generate a fresh stage (secrets + key) from the workspace config when none exists (for CI; also via GKM_AUTO_SETUP)").option("--prepare", "Write the test manifest and #test harness, then stop — for a typecheck that runs before the suite").option("--setup", "Reconcile, migrate and seed the test stage and write the harness, then stop — what @geekmidas/cli/vitest runs").option("--teardown", "Drop the test stage's databases the last setup created, then stop — what @geekmidas/cli/vitest runs when the suite ends").argument("[pattern]", "Pattern to filter tests").action(async (pattern, options) => {
15980
16053
  try {
15981
16054
  const globalOptions = program.opts();
15982
16055
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
@@ -15993,7 +16066,7 @@ program.command("migrate").description("Apply each database construct's migratio
15993
16066
  try {
15994
16067
  const globalOptions = program.opts();
15995
16068
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
15996
- const { migrateCommand } = await import("./migrate-CT8b3nt_.mjs");
16069
+ const { migrateCommand } = await import("./migrate-_2J3lFT5.mjs");
15997
16070
  await migrateCommand({
15998
16071
  ...options,
15999
16072
  ...construct ? { construct } : {}
@@ -16007,7 +16080,7 @@ program.command("seed").description("Migrate, then run each database construct's
16007
16080
  try {
16008
16081
  const globalOptions = program.opts();
16009
16082
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16010
- const { seedCommand } = await import("./migrate-CT8b3nt_.mjs");
16083
+ const { seedCommand } = await import("./migrate-_2J3lFT5.mjs");
16011
16084
  await seedCommand({
16012
16085
  ...options,
16013
16086
  ...construct ? { construct } : {}
@@ -16021,7 +16094,7 @@ program.command("migration").description("Write a construct's next migration: an
16021
16094
  try {
16022
16095
  const globalOptions = program.opts();
16023
16096
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16024
- const { migrationCommand } = await import("./migrate-CT8b3nt_.mjs");
16097
+ const { migrationCommand } = await import("./migrate-_2J3lFT5.mjs");
16025
16098
  await migrationCommand({
16026
16099
  construct,
16027
16100
  ...name ? { name } : {}
@@ -16189,7 +16262,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
16189
16262
  console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
16190
16263
  process.exit(1);
16191
16264
  }
16192
- const { derivedContainers } = await import("./workspace-EmXwO2qM.mjs").then((n) => n.a);
16265
+ const { derivedContainers } = await import("./workspace-BtXCL1C8.mjs").then((n) => n.a);
16193
16266
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
16194
16267
  if (!result) {
16195
16268
  console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);