@geekmidas/cli 10.0.0-alpha.31 → 10.0.0-alpha.33
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/{config-URVD4bTj.cjs → config-DMpzmsQL.cjs} +2 -2
- package/dist/{config-URVD4bTj.cjs.map → config-DMpzmsQL.cjs.map} +1 -1
- package/dist/{config-BH3Ki69F.mjs → config-Du8AQJRK.mjs} +2 -2
- package/dist/{config-BH3Ki69F.mjs.map → config-Du8AQJRK.mjs.map} +1 -1
- package/dist/config.cjs +2 -2
- package/dist/config.d.cts +2 -2
- package/dist/config.d.mts +2 -2
- package/dist/config.mjs +2 -2
- package/dist/discover-CMxesskG.mjs.map +1 -1
- package/dist/discover-CroTIm0K.cjs.map +1 -1
- package/dist/{github-BrDmlm1x.mjs → github-DN-92LWP.mjs} +3 -3
- package/dist/{github-BrDmlm1x.mjs.map → github-DN-92LWP.mjs.map} +1 -1
- package/dist/{github-B2fis34P.cjs → github-JhvrmjQp.cjs} +3 -3
- package/dist/{github-B2fis34P.cjs.map → github-JhvrmjQp.cjs.map} +1 -1
- package/dist/{index-p89EzILa.d.mts → index-CAp22ul5.d.cts} +2 -2
- package/dist/{index-p89EzILa.d.mts.map → index-CAp22ul5.d.cts.map} +1 -1
- package/dist/{index-rU9u761a.d.cts → index-DxXMtQNT.d.mts} +2 -2
- package/dist/{index-rU9u761a.d.cts.map → index-DxXMtQNT.d.mts.map} +1 -1
- package/dist/index.cjs +191 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +192 -122
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-BuCx7tIv.cjs → migrate-BkytlJlU.cjs} +272 -29
- package/dist/migrate-BkytlJlU.cjs.map +1 -0
- package/dist/{migrate-CBICVMAi.mjs → migrate-D2rtGKYi.mjs} +272 -31
- package/dist/migrate-D2rtGKYi.mjs.map +1 -0
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/{ready-DXtNfk7v.cjs → ready-CXhoutix.cjs} +108 -25
- package/dist/ready-CXhoutix.cjs.map +1 -0
- package/dist/{ready-CvP3JVEw.mjs → ready-D1BCiaYU.mjs} +106 -29
- package/dist/ready-D1BCiaYU.mjs.map +1 -0
- package/dist/reconcile/public.d.cts +1 -1
- package/dist/reconcile/public.d.mts +1 -1
- package/dist/{types-CzK6hsXP.d.mts → types-BTiu5kwo.d.mts} +26 -1
- package/dist/{types-CzK6hsXP.d.mts.map → types-BTiu5kwo.d.mts.map} +1 -1
- package/dist/{types-DOOAevjG.d.cts → types-Bj_FpeTc.d.cts} +26 -1
- package/dist/{types-DOOAevjG.d.cts.map → types-Bj_FpeTc.d.cts.map} +1 -1
- package/dist/vitest.cjs +6 -6
- package/dist/vitest.cjs.map +1 -1
- package/dist/vitest.d.cts +4 -4
- package/dist/vitest.d.mts +4 -4
- package/dist/vitest.mjs +6 -6
- package/dist/vitest.mjs.map +1 -1
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +2 -2
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-C97QUCIf.mjs → workspace-BNurJoFx.mjs} +3 -3
- package/dist/{workspace-C97QUCIf.mjs.map → workspace-BNurJoFx.mjs.map} +1 -1
- package/dist/{workspace-BhiMkRuA.cjs → workspace-C8TbIWxO.cjs} +3 -3
- package/dist/{workspace-BhiMkRuA.cjs.map → workspace-C8TbIWxO.cjs.map} +1 -1
- package/dist/{workspace-Bav3TVrP.cjs → workspace-CAJk6UmC.cjs} +11 -4
- package/dist/{workspace-Bav3TVrP.cjs.map → workspace-CAJk6UmC.cjs.map} +1 -1
- package/dist/{workspace-T4IVGSoB.mjs → workspace-CsL76tpc.mjs} +11 -4
- package/dist/{workspace-T4IVGSoB.mjs.map → workspace-CsL76tpc.mjs.map} +1 -1
- package/package.json +14 -14
- package/dist/migrate-BuCx7tIv.cjs.map +0 -1
- package/dist/migrate-CBICVMAi.mjs.map +0 -1
- package/dist/ready-CvP3JVEw.mjs.map +0 -1
- package/dist/ready-DXtNfk7v.cjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
import { n as __require, t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
|
|
3
3
|
import { c as isPartitionedRoutes, i as ConstructGenerator, t as discover } from "./discover-CMxesskG.mjs";
|
|
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-
|
|
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-
|
|
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-Du8AQJRK.mjs";
|
|
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-
|
|
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-BNurJoFx.mjs";
|
|
8
8
|
import { n as EndpointGenerator, r as runtimeFor } from "./OpenApiTsGenerator-hDINScnz.mjs";
|
|
9
9
|
import { generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi.mjs";
|
|
10
|
-
import { a as
|
|
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";
|
|
11
11
|
import { i as withRoutes, r as withCompute } from "./emit-Bzq7JsMP.mjs";
|
|
12
12
|
import { a as createStageSecrets, c as generateServiceCredentials, i as writeDockerEnvFromSecrets, l as rotateServicePassword, o as generateConnectionUrls, r as generateFullstackCustomSecrets, s as generateSecurePassword, t as reconcileMissingSecrets } from "./reconcile-yNq6Shau.mjs";
|
|
13
13
|
import { createRequire } from "node:module";
|
|
@@ -19,7 +19,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
19
19
|
import { homedir } from "node:os";
|
|
20
20
|
import { basename, dirname, isAbsolute, join, parse, relative, resolve } from "node:path";
|
|
21
21
|
import { execFile, execSync, spawn } from "node:child_process";
|
|
22
|
-
import { cacheTable, canonicalId, cloudName, cookieDomain, dependentsOf, migrationFolder, provideKey, provisionOrder, scopedName, serviceKey } from "@geekmidas/manifest";
|
|
22
|
+
import { cacheTable, canonicalId, cloudName, cookieDomain, dependentsOf, kebabCase, migrationFolder, provideKey, provisionOrder, scopedName, serviceKey } from "@geekmidas/manifest";
|
|
23
23
|
import fg from "fast-glob";
|
|
24
24
|
import chokidar from "chokidar";
|
|
25
25
|
import { createCipheriv, createHash, randomBytes } from "node:crypto";
|
|
@@ -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.
|
|
40
|
+
var version = "10.0.0-alpha.33";
|
|
41
41
|
//#endregion
|
|
42
42
|
//#region src/auth/credentials.ts
|
|
43
43
|
/**
|
|
@@ -1086,7 +1086,10 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1086
1086
|
...rawSecrets,
|
|
1087
1087
|
...reconciled.env
|
|
1088
1088
|
};
|
|
1089
|
-
|
|
1089
|
+
if (options.migrate || options.seed) {
|
|
1090
|
+
const { prepareDevDatabases } = await import("./migrate-D2rtGKYi.mjs");
|
|
1091
|
+
await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
|
|
1092
|
+
} else await import("./migrate-D2rtGKYi.mjs").then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
|
|
1090
1093
|
logger$10.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
|
|
1091
1094
|
});
|
|
1092
1095
|
const appUrls = {};
|
|
@@ -8293,7 +8296,7 @@ function layoutBlock(options, template) {
|
|
|
8293
8296
|
const { database } = options.constructs;
|
|
8294
8297
|
const isWorker = template.name === "worker";
|
|
8295
8298
|
if (monorepo) return `constructs/ # every construct: the surface, the database, topics
|
|
8296
|
-
${database ? "db/<construct>/ # migrations
|
|
8299
|
+
${database ? "db/<construct>/ # migrations/ and seeds/, one folder per database construct\n" : ""}${apiPath}/ # the API app
|
|
8297
8300
|
gkm.config.ts # name, constructs glob, secrets`;
|
|
8298
8301
|
const lines = ["src/constructs/ # the surface, and anything it declares", "src/config/ # env parser, logger"];
|
|
8299
8302
|
if (!isWorker) lines.push("src/endpoints/ # HTTP handlers");
|
|
@@ -8301,7 +8304,7 @@ gkm.config.ts # name, constructs glob, secrets`;
|
|
|
8301
8304
|
lines.push("src/crons/ # scheduled work");
|
|
8302
8305
|
lines.push("src/subscribers/ # topic consumers");
|
|
8303
8306
|
}
|
|
8304
|
-
if (database) lines.push("db/<construct>/ # migrations
|
|
8307
|
+
if (database) lines.push("db/<construct>/ # migrations/ and seeds/, one folder per database construct");
|
|
8305
8308
|
lines.push("gkm.config.ts # name, constructs glob, secrets");
|
|
8306
8309
|
return lines.join("\n");
|
|
8307
8310
|
}
|
|
@@ -8430,32 +8433,52 @@ Queries go through Kysely. Never open your own connection pool — the one the
|
|
|
8430
8433
|
construct provides is the one that gets credentials, pooling and shutdown.`;
|
|
8431
8434
|
}
|
|
8432
8435
|
function migrationsSection() {
|
|
8433
|
-
return `## Migrations
|
|
8436
|
+
return `## Migrations and seeds
|
|
8434
8437
|
|
|
8435
8438
|
Each database construct has its own folder, named after the construct:
|
|
8436
8439
|
\`Database\` is \`db/database/\`, a tenant \`AuthDatabase\` is
|
|
8437
|
-
\`db/auth-database/\`.
|
|
8438
|
-
|
|
8440
|
+
\`db/auth-database/\`. It holds two folders and nothing else:
|
|
8441
|
+
|
|
8442
|
+
\`\`\`
|
|
8443
|
+
db/database/
|
|
8444
|
+
migrations/ # the schema's history, applied once each
|
|
8445
|
+
seeds/ # the reference data the app needs, run on every pass
|
|
8446
|
+
\`\`\`
|
|
8447
|
+
|
|
8448
|
+
Each construct keeps its own history, in its own schema, and is migrated and
|
|
8449
|
+
seeded as its own **owner** role — the role a handler runs as cannot create a
|
|
8439
8450
|
table.
|
|
8440
8451
|
|
|
8441
8452
|
\`\`\`bash
|
|
8442
|
-
gkm migration database add_workouts # db/database/20260930143012_add_workouts.ts
|
|
8453
|
+
gkm migration database add_workouts # db/database/migrations/20260930143012_add_workouts.ts
|
|
8443
8454
|
gkm migration auth # Better Auth's schema change, as SQL
|
|
8444
|
-
gkm migrate # apply every
|
|
8455
|
+
gkm migrate # apply every construct's migrations, parents first
|
|
8445
8456
|
gkm migrate database # apply one construct's
|
|
8457
|
+
gkm seed # migrate, then run every seed
|
|
8458
|
+
gkm dev --migrate # migrate before the apps start
|
|
8459
|
+
gkm dev --seed # migrate and seed before the apps start
|
|
8446
8460
|
\`\`\`
|
|
8447
8461
|
|
|
8448
|
-
-
|
|
8449
|
-
\`.sql\`, which runs whole. Never rename or edit one that has
|
|
8450
|
-
it is recorded by name. Change the schema with a new
|
|
8462
|
+
- Migrations are \`<UTC timestamp>_<name>.ts\` exporting \`up\` (and
|
|
8463
|
+
\`down\`), or \`.sql\`, which runs whole. Never rename or edit one that has
|
|
8464
|
+
been applied — it is recorded by name. Change the schema with a new
|
|
8465
|
+
migration.
|
|
8451
8466
|
- Write migrations with \`Kysely<unknown>\` and snake_case names: they are the
|
|
8452
8467
|
SQL, and the camelCase plugin is the app's, not theirs.
|
|
8453
8468
|
- \`gkm migration auth\` compares Better Auth's schema to its tenant and writes
|
|
8454
8469
|
only the difference. Run it after adding a Better Auth plugin, and commit the
|
|
8455
8470
|
file.
|
|
8456
|
-
-
|
|
8457
|
-
\`
|
|
8458
|
-
|
|
8471
|
+
- Seeds always run after migrations, never instead of them. Each is a
|
|
8472
|
+
\`.ts\` exporting \`seed(db, { stage })\`, or \`.sql\`, run in name
|
|
8473
|
+
order, in a transaction of its own.
|
|
8474
|
+
- **Seeds run on every stage, production included.** Write them to be safe
|
|
8475
|
+
there. A seed that belongs only somewhere checks the stage it is handed:
|
|
8476
|
+
\`if (stage === 'production') return;\`. A \`.sql\` seed has no stage, so
|
|
8477
|
+
it must be right everywhere.
|
|
8478
|
+
- Tests migrate and seed themselves: the root vitest config's
|
|
8479
|
+
\`globalSetup: ['@geekmidas/cli/vitest']\` does both for the test stage
|
|
8480
|
+
before any test runs. \`gkm dev\` without a flag only reports what is
|
|
8481
|
+
pending.
|
|
8459
8482
|
- A deploy migrates before the new version goes live, so the old version runs
|
|
8460
8483
|
against the new schema until it is replaced. Keep each migration compatible
|
|
8461
8484
|
with the code before it: add a column in one release, stop using the old one,
|
|
@@ -8464,29 +8487,26 @@ gkm migrate database # apply one construct's
|
|
|
8464
8487
|
**Migrations hold schema, never data.** Tables, columns, constraints, indexes.
|
|
8465
8488
|
No inserts, updates or deletes against the application's rows.
|
|
8466
8489
|
|
|
8467
|
-
- **
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8490
|
+
- **Reference data is a seed.** A permission catalogue, the roles and what
|
|
8491
|
+
they grant, a list of statuses — rows the app needs in order to run — are
|
|
8492
|
+
written by a seed in \`db/<construct>/seeds/\`, never by a migration.
|
|
8493
|
+
- **A seed is an upsert, run every time.** There is no history: every seed
|
|
8494
|
+
runs on every \`gkm seed\`, so changing what it writes and running it again
|
|
8495
|
+
is how the change is applied. Write \`onConflict(…).doUpdateSet(…)\` (or
|
|
8496
|
+
\`doNothing()\`), never a bare insert.
|
|
8497
|
+
- **Define it once, typed, where every app can import it.** A catalogue
|
|
8498
|
+
belongs in code, declared \`as const\` so its keys are a union type, and the
|
|
8499
|
+
seed writes the table from that constant — so the two cannot drift, and no
|
|
8500
|
+
test is needed to keep them in step. The API's guards, the web app's gates
|
|
8501
|
+
and a mobile app's gates all name the same keys, and a typo — or a removed
|
|
8476
8502
|
permission still checked somewhere — fails to compile instead of failing
|
|
8477
8503
|
closed at runtime.
|
|
8478
|
-
- **
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
- **A backfill is the one exception.** When a schema change needs existing rows
|
|
8485
|
-
transformed — a new non-null column filled from an old one — do it in the
|
|
8486
|
-
same migration, keyed on the rows it transforms. It never inserts rows that
|
|
8487
|
-
were not there.
|
|
8488
|
-
- **A test that only keeps two copies in step is the smell.** Delete a copy,
|
|
8489
|
-
and the test with it.`;
|
|
8504
|
+
- **Sample data is factories'.** Demo users, a tournament for a test — those
|
|
8505
|
+
come from test factories, never a seed and never a migration.
|
|
8506
|
+
- **A backfill is the one data change a migration makes.** When a schema
|
|
8507
|
+
change needs existing rows transformed — a new non-null column filled from
|
|
8508
|
+
an old one — do it in the same migration, keyed on the rows it transforms.
|
|
8509
|
+
It never inserts rows that were not there.`;
|
|
8490
8510
|
}
|
|
8491
8511
|
function queriesSection() {
|
|
8492
8512
|
return `## Writing queries and relations
|
|
@@ -8615,7 +8635,27 @@ functions it called on the way.
|
|
|
8615
8635
|
${database ? "gkm test # starts services, injects secrets, then runs vitest" : "gkm test"}
|
|
8616
8636
|
\`\`\`
|
|
8617
8637
|
|
|
8618
|
-
Run tests through \`gkm test\`, not \`vitest\` directly${database ? " — it is what starts the database and injects the secrets the suite needs" : ""}
|
|
8638
|
+
Run tests through \`gkm test\`, not \`vitest\` directly${database ? " — it is what starts the database and injects the secrets the suite needs" : ""}.${database ? `
|
|
8639
|
+
|
|
8640
|
+
**Factories, one per database.** \`test/factories/<construct>.ts\` at the
|
|
8641
|
+
project root — \`database.ts\` for \`Database\` — exports
|
|
8642
|
+
\`createFactory(db)\`. A feature test gets each by service name, on that
|
|
8643
|
+
test's transaction — the one \`db.get\` returns and its endpoints use — so
|
|
8644
|
+
ask for it rather than importing and building one. Sign in the way a person
|
|
8645
|
+
does, through \`browser.signIn\`:
|
|
8646
|
+
|
|
8647
|
+
\`\`\`typescript
|
|
8648
|
+
it('reads a user', async ({ browser, db, factories }) => {
|
|
8649
|
+
const factory = await factories.get('database');
|
|
8650
|
+
const ada = await factory.insert('users', { email: 'ada@example.com' });
|
|
8651
|
+
|
|
8652
|
+
await browser.signIn(ada.email);
|
|
8653
|
+
// … act through browser.api …
|
|
8654
|
+
|
|
8655
|
+
const app = await db.get('database');
|
|
8656
|
+
// … assert on what was written …
|
|
8657
|
+
});
|
|
8658
|
+
\`\`\`` : ""}`;
|
|
8619
8659
|
}
|
|
8620
8660
|
//#endregion
|
|
8621
8661
|
//#region src/init/dependencies.ts
|
|
@@ -8743,26 +8783,26 @@ function loadPackageJson() {
|
|
|
8743
8783
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
8744
8784
|
*/
|
|
8745
8785
|
const GEEKMIDAS_VERSIONS = {
|
|
8746
|
-
"@geekmidas/audit": "~10.0.0-alpha.
|
|
8747
|
-
"@geekmidas/auth": "~10.0.0-alpha.
|
|
8748
|
-
"@geekmidas/cache": "~10.0.0-alpha.
|
|
8749
|
-
"@geekmidas/client": "~10.0.0-alpha.
|
|
8750
|
-
"@geekmidas/cloud": "~10.0.0-alpha.
|
|
8751
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
8752
|
-
"@geekmidas/db": "~10.0.0-alpha.
|
|
8753
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
8754
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
8755
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
8756
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
8757
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
8758
|
-
"@geekmidas/manifest": "~10.0.0-alpha.
|
|
8759
|
-
"@geekmidas/rate-limit": "~10.0.0-alpha.
|
|
8760
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
8761
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
8762
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
8763
|
-
"@geekmidas/studio": "~10.0.0-alpha.
|
|
8764
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
8765
|
-
"@geekmidas/testkit": "~10.0.0-alpha.
|
|
8786
|
+
"@geekmidas/audit": "~10.0.0-alpha.33",
|
|
8787
|
+
"@geekmidas/auth": "~10.0.0-alpha.33",
|
|
8788
|
+
"@geekmidas/cache": "~10.0.0-alpha.33",
|
|
8789
|
+
"@geekmidas/client": "~10.0.0-alpha.33",
|
|
8790
|
+
"@geekmidas/cloud": "~10.0.0-alpha.33",
|
|
8791
|
+
"@geekmidas/constructs": "~10.0.0-alpha.33",
|
|
8792
|
+
"@geekmidas/db": "~10.0.0-alpha.33",
|
|
8793
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.33",
|
|
8794
|
+
"@geekmidas/envkit": "~10.0.0-alpha.33",
|
|
8795
|
+
"@geekmidas/errors": "~10.0.0-alpha.33",
|
|
8796
|
+
"@geekmidas/events": "~10.0.0-alpha.33",
|
|
8797
|
+
"@geekmidas/logger": "~10.0.0-alpha.33",
|
|
8798
|
+
"@geekmidas/manifest": "~10.0.0-alpha.33",
|
|
8799
|
+
"@geekmidas/rate-limit": "~10.0.0-alpha.33",
|
|
8800
|
+
"@geekmidas/schema": "~10.0.0-alpha.33",
|
|
8801
|
+
"@geekmidas/services": "~10.0.0-alpha.33",
|
|
8802
|
+
"@geekmidas/storage": "~10.0.0-alpha.33",
|
|
8803
|
+
"@geekmidas/studio": "~10.0.0-alpha.33",
|
|
8804
|
+
"@geekmidas/telescope": "~10.0.0-alpha.33",
|
|
8805
|
+
"@geekmidas/testkit": "~10.0.0-alpha.33",
|
|
8766
8806
|
"@geekmidas/cli": `~${loadPackageJson().version}`
|
|
8767
8807
|
};
|
|
8768
8808
|
//#endregion
|
|
@@ -11956,9 +11996,9 @@ function generateSourceFiles(options, template) {
|
|
|
11956
11996
|
//#endregion
|
|
11957
11997
|
//#region src/init/generators/test.ts
|
|
11958
11998
|
/**
|
|
11959
|
-
* Generate test infrastructure files when database is enabled
|
|
11960
|
-
*
|
|
11961
|
-
* factory
|
|
11999
|
+
* Generate test infrastructure files when database is enabled: the
|
|
12000
|
+
* transaction-isolated test config and an example spec, in the app. The
|
|
12001
|
+
* factory is the project's — see {@link generateTestFactoryFiles}.
|
|
11962
12002
|
*/
|
|
11963
12003
|
function generateTestFiles(options, _template) {
|
|
11964
12004
|
if (!options.constructs.database) return [];
|
|
@@ -11966,10 +12006,9 @@ function generateTestFiles(options, _template) {
|
|
|
11966
12006
|
const schema = !options.monorepo ? "../src/constructs/database.ts" : `@${options.name}/constructs/database.ts`;
|
|
11967
12007
|
db.urlKey;
|
|
11968
12008
|
db.ownerUrlKey;
|
|
11969
|
-
return [
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
content: `import { it as itVitest } from 'vitest';
|
|
12009
|
+
return [{
|
|
12010
|
+
path: "test/config.ts",
|
|
12011
|
+
content: `import { it as itVitest } from 'vitest';
|
|
11973
12012
|
import { wrapVitestKyselyTransaction } from '@geekmidas/testkit/kysely';
|
|
11974
12013
|
import { type Database, database } from '${schema}';
|
|
11975
12014
|
|
|
@@ -11979,17 +12018,56 @@ export const it = wrapVitestKyselyTransaction<Database>(itVitest, {
|
|
|
11979
12018
|
connection: database,
|
|
11980
12019
|
});
|
|
11981
12020
|
`
|
|
11982
|
-
|
|
11983
|
-
|
|
11984
|
-
|
|
11985
|
-
|
|
12021
|
+
}, {
|
|
12022
|
+
path: "test/example.spec.ts",
|
|
12023
|
+
content: `import { describe, expect } from 'vitest';
|
|
12024
|
+
import { it } from './config.ts';
|
|
12025
|
+
|
|
12026
|
+
describe('example', () => {
|
|
12027
|
+
it('should have a working test setup', async ({ trx }) => {
|
|
12028
|
+
// trx is a Kysely transaction; everything in it is rolled back after the
|
|
12029
|
+
// test
|
|
12030
|
+
expect(trx).toBeDefined();
|
|
12031
|
+
});
|
|
12032
|
+
});
|
|
12033
|
+
`
|
|
12034
|
+
}];
|
|
12035
|
+
}
|
|
12036
|
+
/**
|
|
12037
|
+
* The database's test factory, at the project root:
|
|
12038
|
+
* `test/factories/<construct>.ts`.
|
|
12039
|
+
*
|
|
12040
|
+
* A factory belongs to a database, not to an app, so there is one per database
|
|
12041
|
+
* for the whole project. `gkm test` finds it by that name and hands every
|
|
12042
|
+
* feature test \`factories.get('database')\`, built on the test's transaction.
|
|
12043
|
+
*/
|
|
12044
|
+
function generateTestFactoryFiles(options) {
|
|
12045
|
+
if (!options.constructs.database) return [];
|
|
12046
|
+
const db = databaseFor();
|
|
12047
|
+
const schema = options.monorepo ? `@${options.name}/constructs/database.ts` : "../../src/constructs/database.ts";
|
|
12048
|
+
return [{
|
|
12049
|
+
path: `test/factories/${kebabCase(db.id)}.ts`,
|
|
12050
|
+
content: `import type { Kysely } from 'kysely';
|
|
11986
12051
|
import { KyselyFactory } from '@geekmidas/testkit/kysely';
|
|
11987
12052
|
import type { Database } from '${schema}';
|
|
11988
|
-
|
|
12053
|
+
|
|
12054
|
+
const usersBuilder = KyselyFactory.createBuilder<Database, 'users'>(
|
|
12055
|
+
'users',
|
|
12056
|
+
({ faker }) => ({
|
|
12057
|
+
id: faker.string.uuid(),
|
|
12058
|
+
name: faker.person.fullName(),
|
|
12059
|
+
email: faker.internet.email(),
|
|
12060
|
+
createdAt: new Date(),
|
|
12061
|
+
}),
|
|
12062
|
+
);
|
|
11989
12063
|
|
|
11990
12064
|
const builders = { users: usersBuilder };
|
|
11991
12065
|
const seeds = {};
|
|
11992
12066
|
|
|
12067
|
+
/**
|
|
12068
|
+
* This database's factory. \`gkm test\` builds it on each feature test's
|
|
12069
|
+
* transaction and hands it over as \`factories.get('${serviceKey(db.id)}')\`.
|
|
12070
|
+
*/
|
|
11993
12071
|
export function createFactory(db: Kysely<Database>) {
|
|
11994
12072
|
return new KyselyFactory<Database, typeof builders, typeof seeds>(
|
|
11995
12073
|
builders,
|
|
@@ -12000,38 +12078,7 @@ export function createFactory(db: Kysely<Database>) {
|
|
|
12000
12078
|
|
|
12001
12079
|
export type Factory = ReturnType<typeof createFactory>;
|
|
12002
12080
|
`
|
|
12003
|
-
|
|
12004
|
-
{
|
|
12005
|
-
path: "test/factory/users.ts",
|
|
12006
|
-
content: `import { KyselyFactory } from '@geekmidas/testkit/kysely';
|
|
12007
|
-
import type { Database } from '${schema}';
|
|
12008
|
-
|
|
12009
|
-
export const usersBuilder = KyselyFactory.createBuilder<Database, 'users'>(
|
|
12010
|
-
'users',
|
|
12011
|
-
({ faker }) => ({
|
|
12012
|
-
id: faker.string.uuid(),
|
|
12013
|
-
name: faker.person.fullName(),
|
|
12014
|
-
email: faker.internet.email(),
|
|
12015
|
-
createdAt: new Date(),
|
|
12016
|
-
}),
|
|
12017
|
-
);
|
|
12018
|
-
`
|
|
12019
|
-
},
|
|
12020
|
-
{
|
|
12021
|
-
path: "test/example.spec.ts",
|
|
12022
|
-
content: `import { describe, expect } from 'vitest';
|
|
12023
|
-
import { it } from './config.ts';
|
|
12024
|
-
|
|
12025
|
-
describe('example', () => {
|
|
12026
|
-
it('should have a working test setup', async ({ trx }) => {
|
|
12027
|
-
// trx is a Kysely transaction; everything in it is rolled back after the
|
|
12028
|
-
// test
|
|
12029
|
-
expect(trx).toBeDefined();
|
|
12030
|
-
});
|
|
12031
|
-
});
|
|
12032
|
-
`
|
|
12033
|
-
}
|
|
12034
|
-
];
|
|
12081
|
+
}];
|
|
12035
12082
|
}
|
|
12036
12083
|
//#endregion
|
|
12037
12084
|
//#region src/init/generators/ui.ts
|
|
@@ -14501,7 +14548,8 @@ async function initCommand(projectName, options = {}) {
|
|
|
14501
14548
|
...generateGithubFiles(templateOptions),
|
|
14502
14549
|
...generateMonorepoFiles(templateOptions, baseTemplate),
|
|
14503
14550
|
...generateRootConstructs(templateOptions),
|
|
14504
|
-
...generateModelsPackage(templateOptions)
|
|
14551
|
+
...generateModelsPackage(templateOptions),
|
|
14552
|
+
...generateTestFactoryFiles(templateOptions)
|
|
14505
14553
|
] : [];
|
|
14506
14554
|
const webAppFiles = isFullstack ? frontendFramework === "tanstack-start" ? generateTanStackWebFiles(templateOptions) : frontendFramework === "expo" ? generateExpoAppFiles(templateOptions) : generateWebAppFiles(templateOptions) : [];
|
|
14507
14555
|
const authAppFiles = isFullstack ? generateAuthAppFiles(templateOptions) : [];
|
|
@@ -14964,7 +15012,7 @@ var NoStoredSecrets = class extends Error {
|
|
|
14964
15012
|
}
|
|
14965
15013
|
};
|
|
14966
15014
|
async function reconcile(secrets, workspace, stage) {
|
|
14967
|
-
const { derivedContainers } = await import("./workspace-
|
|
15015
|
+
const { derivedContainers } = await import("./workspace-BNurJoFx.mjs").then((n) => n.a);
|
|
14968
15016
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
14969
15017
|
return result ? {
|
|
14970
15018
|
secrets: result.secrets,
|
|
@@ -15308,7 +15356,8 @@ async function testCommand(options = {}) {
|
|
|
15308
15356
|
patterns: constructGlobs(workspace),
|
|
15309
15357
|
cacheBackend: backendsOf(workspace).cache,
|
|
15310
15358
|
stage: TEST_STAGE,
|
|
15311
|
-
env: finalCredentials
|
|
15359
|
+
env: finalCredentials,
|
|
15360
|
+
factories: join(workspace.root, workspace.test.factories ?? "test/factories")
|
|
15312
15361
|
}) : void 0;
|
|
15313
15362
|
if (options.prepare) {
|
|
15314
15363
|
console.log(manifestPath ? ` 🧪 Test harness written beside ${manifestPath}` : " 🧪 Nothing to prepare: this project declares no constructs");
|
|
@@ -15357,9 +15406,12 @@ async function testCommand(options = {}) {
|
|
|
15357
15406
|
});
|
|
15358
15407
|
});
|
|
15359
15408
|
}
|
|
15360
|
-
/**
|
|
15409
|
+
/**
|
|
15410
|
+
* Migrate the test stage just reconciled, then seed it: a test run never
|
|
15411
|
+
* starts without the reference data the app needs.
|
|
15412
|
+
*/
|
|
15361
15413
|
async function migrateTestStage(workspace, env) {
|
|
15362
|
-
const {
|
|
15414
|
+
const { migrateAndSeed } = await import("./migrate-D2rtGKYi.mjs");
|
|
15363
15415
|
const { discover } = await import("./discover-CMxesskG.mjs").then((n) => n.n);
|
|
15364
15416
|
const sources = {};
|
|
15365
15417
|
const manifest = await discover({
|
|
@@ -15367,13 +15419,15 @@ async function migrateTestStage(workspace, env) {
|
|
|
15367
15419
|
cwd: workspace.root,
|
|
15368
15420
|
sources
|
|
15369
15421
|
});
|
|
15370
|
-
const
|
|
15422
|
+
const { migrations, seeds } = await migrateAndSeed({
|
|
15371
15423
|
root: workspace.root,
|
|
15372
15424
|
manifest,
|
|
15373
15425
|
sources,
|
|
15374
|
-
env
|
|
15426
|
+
env,
|
|
15427
|
+
stage: TEST_STAGE
|
|
15375
15428
|
});
|
|
15376
|
-
for (const { target, applied } of
|
|
15429
|
+
for (const { target, applied } of migrations) if (applied.length > 0) console.log(` 🗄️ ${target.migrations}: applied ${applied.join(", ")}`);
|
|
15430
|
+
for (const { target, seeded } of seeds) if (seeded.length > 0) console.log(` 🌱 ${target.seeds}: ran ${seeded.join(", ")}`);
|
|
15377
15431
|
}
|
|
15378
15432
|
/** `gkm test` outside a project that declares stages, with none named. */
|
|
15379
15433
|
var NoStageToTest = class extends Error {
|
|
@@ -15757,7 +15811,7 @@ program.command("build").description("Build handlers and the manifest from decla
|
|
|
15757
15811
|
process.exit(1);
|
|
15758
15812
|
}
|
|
15759
15813
|
});
|
|
15760
|
-
program.command("dev").description("Reconcile constructs and start the dev server with automatic reload").option("-p, --port <port>", "Port to run the development server on").option("--entry <file>", "Entry file to run (bypasses gkm config)").option("--watch", "Watch for file changes (default: true with --entry)").option("--no-watch", "Disable file watching").option("--enable-openapi", "Enable OpenAPI documentation for development server", true).action(async (options) => {
|
|
15814
|
+
program.command("dev").description("Reconcile constructs and start the dev server with automatic reload").option("-p, --port <port>", "Port to run the development server on").option("--entry <file>", "Entry file to run (bypasses gkm config)").option("--watch", "Watch for file changes (default: true with --entry)").option("--no-watch", "Disable file watching").option("--enable-openapi", "Enable OpenAPI documentation for development server", true).option("--migrate", "Apply pending migrations before the apps start").option("--seed", "Migrate, then run the seeds, before the apps start").action(async (options) => {
|
|
15761
15815
|
try {
|
|
15762
15816
|
const globalOptions = program.opts();
|
|
15763
15817
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
@@ -15766,7 +15820,9 @@ program.command("dev").description("Reconcile constructs and start the dev serve
|
|
|
15766
15820
|
portExplicit: !!options.port,
|
|
15767
15821
|
enableOpenApi: options.enableOpenapi ?? true,
|
|
15768
15822
|
entry: options.entry,
|
|
15769
|
-
watch: options.watch
|
|
15823
|
+
watch: options.watch,
|
|
15824
|
+
migrate: options.migrate,
|
|
15825
|
+
seed: options.seed
|
|
15770
15826
|
});
|
|
15771
15827
|
} catch (error) {
|
|
15772
15828
|
console.error(formatError(error));
|
|
@@ -15783,7 +15839,7 @@ program.command("exec").description("Run a command with secrets injected into Cr
|
|
|
15783
15839
|
process.exit(1);
|
|
15784
15840
|
}
|
|
15785
15841
|
});
|
|
15786
|
-
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 and
|
|
15842
|
+
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) => {
|
|
15787
15843
|
try {
|
|
15788
15844
|
const globalOptions = program.opts();
|
|
15789
15845
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
@@ -15796,11 +15852,11 @@ program.command("test").description("Run tests with secrets loaded from environm
|
|
|
15796
15852
|
process.exit(1);
|
|
15797
15853
|
}
|
|
15798
15854
|
});
|
|
15799
|
-
program.command("migrate").description("Apply each database construct's migrations from db/<construct
|
|
15855
|
+
program.command("migrate").description("Apply each database construct's migrations from db/<construct>/migrations/, as its owner").argument("[construct]", "Only this construct, by name").option("--stage <stage>", "The local stage (default) or test — a deploy migrates its own stage").action(async (construct, options) => {
|
|
15800
15856
|
try {
|
|
15801
15857
|
const globalOptions = program.opts();
|
|
15802
15858
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15803
|
-
const { migrateCommand } = await import("./migrate-
|
|
15859
|
+
const { migrateCommand } = await import("./migrate-D2rtGKYi.mjs");
|
|
15804
15860
|
await migrateCommand({
|
|
15805
15861
|
...options,
|
|
15806
15862
|
...construct ? { construct } : {}
|
|
@@ -15810,11 +15866,25 @@ program.command("migrate").description("Apply each database construct's migratio
|
|
|
15810
15866
|
process.exit(1);
|
|
15811
15867
|
}
|
|
15812
15868
|
});
|
|
15869
|
+
program.command("seed").description("Migrate, then run each database construct's seeds from db/<construct>/seeds/, as its owner").argument("[construct]", "Only this construct, by name").option("--stage <stage>", "The local stage (default) or test — a deploy seeds its own stage").action(async (construct, options) => {
|
|
15870
|
+
try {
|
|
15871
|
+
const globalOptions = program.opts();
|
|
15872
|
+
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15873
|
+
const { seedCommand } = await import("./migrate-D2rtGKYi.mjs");
|
|
15874
|
+
await seedCommand({
|
|
15875
|
+
...options,
|
|
15876
|
+
...construct ? { construct } : {}
|
|
15877
|
+
});
|
|
15878
|
+
} catch (error) {
|
|
15879
|
+
console.error(formatError(error));
|
|
15880
|
+
process.exit(1);
|
|
15881
|
+
}
|
|
15882
|
+
});
|
|
15813
15883
|
program.command("migration").description("Write a construct's next migration: an empty one for a database, the schema change for an auth server").argument("<construct>", "The construct, by name").argument("[name]", "What the migration is called").action(async (construct, name) => {
|
|
15814
15884
|
try {
|
|
15815
15885
|
const globalOptions = program.opts();
|
|
15816
15886
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15817
|
-
const { migrationCommand } = await import("./migrate-
|
|
15887
|
+
const { migrationCommand } = await import("./migrate-D2rtGKYi.mjs");
|
|
15818
15888
|
await migrationCommand({
|
|
15819
15889
|
construct,
|
|
15820
15890
|
...name ? { name } : {}
|
|
@@ -15982,7 +16052,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
15982
16052
|
console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
15983
16053
|
process.exit(1);
|
|
15984
16054
|
}
|
|
15985
|
-
const { derivedContainers } = await import("./workspace-
|
|
16055
|
+
const { derivedContainers } = await import("./workspace-BNurJoFx.mjs").then((n) => n.a);
|
|
15986
16056
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
15987
16057
|
if (!result) {
|
|
15988
16058
|
console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
|
|
@@ -16041,7 +16111,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
|
|
|
16041
16111
|
try {
|
|
16042
16112
|
const globalOptions = program.opts();
|
|
16043
16113
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16044
|
-
const { deployGithubCommand } = await import("./github-
|
|
16114
|
+
const { deployGithubCommand } = await import("./github-DN-92LWP.mjs");
|
|
16045
16115
|
await deployGithubCommand(options);
|
|
16046
16116
|
} catch (error) {
|
|
16047
16117
|
console.error(formatError(error));
|