@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.cjs
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
|
|
4
4
|
const require_discover = require("./discover-CroTIm0K.cjs");
|
|
5
|
-
const require_config = require("./config-
|
|
6
|
-
const require_workspace = require("./workspace-
|
|
5
|
+
const require_config = require("./config-DMpzmsQL.cjs");
|
|
6
|
+
const require_workspace = require("./workspace-CAJk6UmC.cjs");
|
|
7
7
|
const require_storage = require("./storage-C2JC31V9.cjs");
|
|
8
|
-
const require_workspace$1 = require("./workspace-
|
|
8
|
+
const require_workspace$1 = require("./workspace-C8TbIWxO.cjs");
|
|
9
9
|
const require_OpenApiTsGenerator = require("./OpenApiTsGenerator-D1nhN98m.cjs");
|
|
10
10
|
const require_openapi = require("./openapi.cjs");
|
|
11
|
-
const require_ready = require("./ready-
|
|
11
|
+
const require_ready = require("./ready-CXhoutix.cjs");
|
|
12
12
|
const require_emit = require("./emit-BdoPFjZf.cjs");
|
|
13
13
|
const require_reconcile = require("./reconcile-BxZTV2vl.cjs");
|
|
14
14
|
let commander = require("commander");
|
|
@@ -42,7 +42,7 @@ let _geekmidas_constructs_subscribers = require("@geekmidas/constructs/subscribe
|
|
|
42
42
|
let _geekmidas_constructs_topic = require("@geekmidas/constructs/topic");
|
|
43
43
|
let node_dns_promises = require("node:dns/promises");
|
|
44
44
|
//#region package.json
|
|
45
|
-
var version = "10.0.0-alpha.
|
|
45
|
+
var version = "10.0.0-alpha.33";
|
|
46
46
|
//#endregion
|
|
47
47
|
//#region src/auth/credentials.ts
|
|
48
48
|
/**
|
|
@@ -1091,7 +1091,10 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1091
1091
|
...rawSecrets,
|
|
1092
1092
|
...reconciled.env
|
|
1093
1093
|
};
|
|
1094
|
-
|
|
1094
|
+
if (options.migrate || options.seed) {
|
|
1095
|
+
const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-BkytlJlU.cjs"));
|
|
1096
|
+
await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
|
|
1097
|
+
} else await Promise.resolve().then(() => require("./migrate-BkytlJlU.cjs")).then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
|
|
1095
1098
|
logger$10.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
|
|
1096
1099
|
});
|
|
1097
1100
|
const appUrls = {};
|
|
@@ -8304,7 +8307,7 @@ function layoutBlock(options, template) {
|
|
|
8304
8307
|
const { database } = options.constructs;
|
|
8305
8308
|
const isWorker = template.name === "worker";
|
|
8306
8309
|
if (monorepo) return `constructs/ # every construct: the surface, the database, topics
|
|
8307
|
-
${database ? "db/<construct>/ # migrations
|
|
8310
|
+
${database ? "db/<construct>/ # migrations/ and seeds/, one folder per database construct\n" : ""}${apiPath}/ # the API app
|
|
8308
8311
|
gkm.config.ts # name, constructs glob, secrets`;
|
|
8309
8312
|
const lines = ["src/constructs/ # the surface, and anything it declares", "src/config/ # env parser, logger"];
|
|
8310
8313
|
if (!isWorker) lines.push("src/endpoints/ # HTTP handlers");
|
|
@@ -8312,7 +8315,7 @@ gkm.config.ts # name, constructs glob, secrets`;
|
|
|
8312
8315
|
lines.push("src/crons/ # scheduled work");
|
|
8313
8316
|
lines.push("src/subscribers/ # topic consumers");
|
|
8314
8317
|
}
|
|
8315
|
-
if (database) lines.push("db/<construct>/ # migrations
|
|
8318
|
+
if (database) lines.push("db/<construct>/ # migrations/ and seeds/, one folder per database construct");
|
|
8316
8319
|
lines.push("gkm.config.ts # name, constructs glob, secrets");
|
|
8317
8320
|
return lines.join("\n");
|
|
8318
8321
|
}
|
|
@@ -8441,32 +8444,52 @@ Queries go through Kysely. Never open your own connection pool — the one the
|
|
|
8441
8444
|
construct provides is the one that gets credentials, pooling and shutdown.`;
|
|
8442
8445
|
}
|
|
8443
8446
|
function migrationsSection() {
|
|
8444
|
-
return `## Migrations
|
|
8447
|
+
return `## Migrations and seeds
|
|
8445
8448
|
|
|
8446
8449
|
Each database construct has its own folder, named after the construct:
|
|
8447
8450
|
\`Database\` is \`db/database/\`, a tenant \`AuthDatabase\` is
|
|
8448
|
-
\`db/auth-database/\`.
|
|
8449
|
-
|
|
8451
|
+
\`db/auth-database/\`. It holds two folders and nothing else:
|
|
8452
|
+
|
|
8453
|
+
\`\`\`
|
|
8454
|
+
db/database/
|
|
8455
|
+
migrations/ # the schema's history, applied once each
|
|
8456
|
+
seeds/ # the reference data the app needs, run on every pass
|
|
8457
|
+
\`\`\`
|
|
8458
|
+
|
|
8459
|
+
Each construct keeps its own history, in its own schema, and is migrated and
|
|
8460
|
+
seeded as its own **owner** role — the role a handler runs as cannot create a
|
|
8450
8461
|
table.
|
|
8451
8462
|
|
|
8452
8463
|
\`\`\`bash
|
|
8453
|
-
gkm migration database add_workouts # db/database/20260930143012_add_workouts.ts
|
|
8464
|
+
gkm migration database add_workouts # db/database/migrations/20260930143012_add_workouts.ts
|
|
8454
8465
|
gkm migration auth # Better Auth's schema change, as SQL
|
|
8455
|
-
gkm migrate # apply every
|
|
8466
|
+
gkm migrate # apply every construct's migrations, parents first
|
|
8456
8467
|
gkm migrate database # apply one construct's
|
|
8468
|
+
gkm seed # migrate, then run every seed
|
|
8469
|
+
gkm dev --migrate # migrate before the apps start
|
|
8470
|
+
gkm dev --seed # migrate and seed before the apps start
|
|
8457
8471
|
\`\`\`
|
|
8458
8472
|
|
|
8459
|
-
-
|
|
8460
|
-
\`.sql\`, which runs whole. Never rename or edit one that has
|
|
8461
|
-
it is recorded by name. Change the schema with a new
|
|
8473
|
+
- Migrations are \`<UTC timestamp>_<name>.ts\` exporting \`up\` (and
|
|
8474
|
+
\`down\`), or \`.sql\`, which runs whole. Never rename or edit one that has
|
|
8475
|
+
been applied — it is recorded by name. Change the schema with a new
|
|
8476
|
+
migration.
|
|
8462
8477
|
- Write migrations with \`Kysely<unknown>\` and snake_case names: they are the
|
|
8463
8478
|
SQL, and the camelCase plugin is the app's, not theirs.
|
|
8464
8479
|
- \`gkm migration auth\` compares Better Auth's schema to its tenant and writes
|
|
8465
8480
|
only the difference. Run it after adding a Better Auth plugin, and commit the
|
|
8466
8481
|
file.
|
|
8467
|
-
-
|
|
8468
|
-
\`
|
|
8469
|
-
|
|
8482
|
+
- Seeds always run after migrations, never instead of them. Each is a
|
|
8483
|
+
\`.ts\` exporting \`seed(db, { stage })\`, or \`.sql\`, run in name
|
|
8484
|
+
order, in a transaction of its own.
|
|
8485
|
+
- **Seeds run on every stage, production included.** Write them to be safe
|
|
8486
|
+
there. A seed that belongs only somewhere checks the stage it is handed:
|
|
8487
|
+
\`if (stage === 'production') return;\`. A \`.sql\` seed has no stage, so
|
|
8488
|
+
it must be right everywhere.
|
|
8489
|
+
- Tests migrate and seed themselves: the root vitest config's
|
|
8490
|
+
\`globalSetup: ['@geekmidas/cli/vitest']\` does both for the test stage
|
|
8491
|
+
before any test runs. \`gkm dev\` without a flag only reports what is
|
|
8492
|
+
pending.
|
|
8470
8493
|
- A deploy migrates before the new version goes live, so the old version runs
|
|
8471
8494
|
against the new schema until it is replaced. Keep each migration compatible
|
|
8472
8495
|
with the code before it: add a column in one release, stop using the old one,
|
|
@@ -8475,29 +8498,26 @@ gkm migrate database # apply one construct's
|
|
|
8475
8498
|
**Migrations hold schema, never data.** Tables, columns, constraints, indexes.
|
|
8476
8499
|
No inserts, updates or deletes against the application's rows.
|
|
8477
8500
|
|
|
8478
|
-
- **
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8501
|
+
- **Reference data is a seed.** A permission catalogue, the roles and what
|
|
8502
|
+
they grant, a list of statuses — rows the app needs in order to run — are
|
|
8503
|
+
written by a seed in \`db/<construct>/seeds/\`, never by a migration.
|
|
8504
|
+
- **A seed is an upsert, run every time.** There is no history: every seed
|
|
8505
|
+
runs on every \`gkm seed\`, so changing what it writes and running it again
|
|
8506
|
+
is how the change is applied. Write \`onConflict(…).doUpdateSet(…)\` (or
|
|
8507
|
+
\`doNothing()\`), never a bare insert.
|
|
8508
|
+
- **Define it once, typed, where every app can import it.** A catalogue
|
|
8509
|
+
belongs in code, declared \`as const\` so its keys are a union type, and the
|
|
8510
|
+
seed writes the table from that constant — so the two cannot drift, and no
|
|
8511
|
+
test is needed to keep them in step. The API's guards, the web app's gates
|
|
8512
|
+
and a mobile app's gates all name the same keys, and a typo — or a removed
|
|
8487
8513
|
permission still checked somewhere — fails to compile instead of failing
|
|
8488
8514
|
closed at runtime.
|
|
8489
|
-
- **
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
- **A backfill is the one exception.** When a schema change needs existing rows
|
|
8496
|
-
transformed — a new non-null column filled from an old one — do it in the
|
|
8497
|
-
same migration, keyed on the rows it transforms. It never inserts rows that
|
|
8498
|
-
were not there.
|
|
8499
|
-
- **A test that only keeps two copies in step is the smell.** Delete a copy,
|
|
8500
|
-
and the test with it.`;
|
|
8515
|
+
- **Sample data is factories'.** Demo users, a tournament for a test — those
|
|
8516
|
+
come from test factories, never a seed and never a migration.
|
|
8517
|
+
- **A backfill is the one data change a migration makes.** When a schema
|
|
8518
|
+
change needs existing rows transformed — a new non-null column filled from
|
|
8519
|
+
an old one — do it in the same migration, keyed on the rows it transforms.
|
|
8520
|
+
It never inserts rows that were not there.`;
|
|
8501
8521
|
}
|
|
8502
8522
|
function queriesSection() {
|
|
8503
8523
|
return `## Writing queries and relations
|
|
@@ -8626,7 +8646,27 @@ functions it called on the way.
|
|
|
8626
8646
|
${database ? "gkm test # starts services, injects secrets, then runs vitest" : "gkm test"}
|
|
8627
8647
|
\`\`\`
|
|
8628
8648
|
|
|
8629
|
-
Run tests through \`gkm test\`, not \`vitest\` directly${database ? " — it is what starts the database and injects the secrets the suite needs" : ""}
|
|
8649
|
+
Run tests through \`gkm test\`, not \`vitest\` directly${database ? " — it is what starts the database and injects the secrets the suite needs" : ""}.${database ? `
|
|
8650
|
+
|
|
8651
|
+
**Factories, one per database.** \`test/factories/<construct>.ts\` at the
|
|
8652
|
+
project root — \`database.ts\` for \`Database\` — exports
|
|
8653
|
+
\`createFactory(db)\`. A feature test gets each by service name, on that
|
|
8654
|
+
test's transaction — the one \`db.get\` returns and its endpoints use — so
|
|
8655
|
+
ask for it rather than importing and building one. Sign in the way a person
|
|
8656
|
+
does, through \`browser.signIn\`:
|
|
8657
|
+
|
|
8658
|
+
\`\`\`typescript
|
|
8659
|
+
it('reads a user', async ({ browser, db, factories }) => {
|
|
8660
|
+
const factory = await factories.get('database');
|
|
8661
|
+
const ada = await factory.insert('users', { email: 'ada@example.com' });
|
|
8662
|
+
|
|
8663
|
+
await browser.signIn(ada.email);
|
|
8664
|
+
// … act through browser.api …
|
|
8665
|
+
|
|
8666
|
+
const app = await db.get('database');
|
|
8667
|
+
// … assert on what was written …
|
|
8668
|
+
});
|
|
8669
|
+
\`\`\`` : ""}`;
|
|
8630
8670
|
}
|
|
8631
8671
|
//#endregion
|
|
8632
8672
|
//#region src/init/dependencies.ts
|
|
@@ -8754,26 +8794,26 @@ function loadPackageJson() {
|
|
|
8754
8794
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
8755
8795
|
*/
|
|
8756
8796
|
const GEEKMIDAS_VERSIONS = {
|
|
8757
|
-
"@geekmidas/audit": "~10.0.0-alpha.
|
|
8758
|
-
"@geekmidas/auth": "~10.0.0-alpha.
|
|
8759
|
-
"@geekmidas/cache": "~10.0.0-alpha.
|
|
8760
|
-
"@geekmidas/client": "~10.0.0-alpha.
|
|
8761
|
-
"@geekmidas/cloud": "~10.0.0-alpha.
|
|
8762
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
8763
|
-
"@geekmidas/db": "~10.0.0-alpha.
|
|
8764
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
8765
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
8766
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
8767
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
8768
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
8769
|
-
"@geekmidas/manifest": "~10.0.0-alpha.
|
|
8770
|
-
"@geekmidas/rate-limit": "~10.0.0-alpha.
|
|
8771
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
8772
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
8773
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
8774
|
-
"@geekmidas/studio": "~10.0.0-alpha.
|
|
8775
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
8776
|
-
"@geekmidas/testkit": "~10.0.0-alpha.
|
|
8797
|
+
"@geekmidas/audit": "~10.0.0-alpha.33",
|
|
8798
|
+
"@geekmidas/auth": "~10.0.0-alpha.33",
|
|
8799
|
+
"@geekmidas/cache": "~10.0.0-alpha.33",
|
|
8800
|
+
"@geekmidas/client": "~10.0.0-alpha.33",
|
|
8801
|
+
"@geekmidas/cloud": "~10.0.0-alpha.33",
|
|
8802
|
+
"@geekmidas/constructs": "~10.0.0-alpha.33",
|
|
8803
|
+
"@geekmidas/db": "~10.0.0-alpha.33",
|
|
8804
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.33",
|
|
8805
|
+
"@geekmidas/envkit": "~10.0.0-alpha.33",
|
|
8806
|
+
"@geekmidas/errors": "~10.0.0-alpha.33",
|
|
8807
|
+
"@geekmidas/events": "~10.0.0-alpha.33",
|
|
8808
|
+
"@geekmidas/logger": "~10.0.0-alpha.33",
|
|
8809
|
+
"@geekmidas/manifest": "~10.0.0-alpha.33",
|
|
8810
|
+
"@geekmidas/rate-limit": "~10.0.0-alpha.33",
|
|
8811
|
+
"@geekmidas/schema": "~10.0.0-alpha.33",
|
|
8812
|
+
"@geekmidas/services": "~10.0.0-alpha.33",
|
|
8813
|
+
"@geekmidas/storage": "~10.0.0-alpha.33",
|
|
8814
|
+
"@geekmidas/studio": "~10.0.0-alpha.33",
|
|
8815
|
+
"@geekmidas/telescope": "~10.0.0-alpha.33",
|
|
8816
|
+
"@geekmidas/testkit": "~10.0.0-alpha.33",
|
|
8777
8817
|
"@geekmidas/cli": `~${loadPackageJson().version}`
|
|
8778
8818
|
};
|
|
8779
8819
|
//#endregion
|
|
@@ -11967,9 +12007,9 @@ function generateSourceFiles(options, template) {
|
|
|
11967
12007
|
//#endregion
|
|
11968
12008
|
//#region src/init/generators/test.ts
|
|
11969
12009
|
/**
|
|
11970
|
-
* Generate test infrastructure files when database is enabled
|
|
11971
|
-
*
|
|
11972
|
-
* factory
|
|
12010
|
+
* Generate test infrastructure files when database is enabled: the
|
|
12011
|
+
* transaction-isolated test config and an example spec, in the app. The
|
|
12012
|
+
* factory is the project's — see {@link generateTestFactoryFiles}.
|
|
11973
12013
|
*/
|
|
11974
12014
|
function generateTestFiles(options, _template) {
|
|
11975
12015
|
if (!options.constructs.database) return [];
|
|
@@ -11977,10 +12017,9 @@ function generateTestFiles(options, _template) {
|
|
|
11977
12017
|
const schema = !options.monorepo ? "../src/constructs/database.ts" : `@${options.name}/constructs/database.ts`;
|
|
11978
12018
|
db.urlKey;
|
|
11979
12019
|
db.ownerUrlKey;
|
|
11980
|
-
return [
|
|
11981
|
-
|
|
11982
|
-
|
|
11983
|
-
content: `import { it as itVitest } from 'vitest';
|
|
12020
|
+
return [{
|
|
12021
|
+
path: "test/config.ts",
|
|
12022
|
+
content: `import { it as itVitest } from 'vitest';
|
|
11984
12023
|
import { wrapVitestKyselyTransaction } from '@geekmidas/testkit/kysely';
|
|
11985
12024
|
import { type Database, database } from '${schema}';
|
|
11986
12025
|
|
|
@@ -11990,17 +12029,56 @@ export const it = wrapVitestKyselyTransaction<Database>(itVitest, {
|
|
|
11990
12029
|
connection: database,
|
|
11991
12030
|
});
|
|
11992
12031
|
`
|
|
11993
|
-
|
|
11994
|
-
|
|
11995
|
-
|
|
11996
|
-
|
|
12032
|
+
}, {
|
|
12033
|
+
path: "test/example.spec.ts",
|
|
12034
|
+
content: `import { describe, expect } from 'vitest';
|
|
12035
|
+
import { it } from './config.ts';
|
|
12036
|
+
|
|
12037
|
+
describe('example', () => {
|
|
12038
|
+
it('should have a working test setup', async ({ trx }) => {
|
|
12039
|
+
// trx is a Kysely transaction; everything in it is rolled back after the
|
|
12040
|
+
// test
|
|
12041
|
+
expect(trx).toBeDefined();
|
|
12042
|
+
});
|
|
12043
|
+
});
|
|
12044
|
+
`
|
|
12045
|
+
}];
|
|
12046
|
+
}
|
|
12047
|
+
/**
|
|
12048
|
+
* The database's test factory, at the project root:
|
|
12049
|
+
* `test/factories/<construct>.ts`.
|
|
12050
|
+
*
|
|
12051
|
+
* A factory belongs to a database, not to an app, so there is one per database
|
|
12052
|
+
* for the whole project. `gkm test` finds it by that name and hands every
|
|
12053
|
+
* feature test \`factories.get('database')\`, built on the test's transaction.
|
|
12054
|
+
*/
|
|
12055
|
+
function generateTestFactoryFiles(options) {
|
|
12056
|
+
if (!options.constructs.database) return [];
|
|
12057
|
+
const db = databaseFor();
|
|
12058
|
+
const schema = options.monorepo ? `@${options.name}/constructs/database.ts` : "../../src/constructs/database.ts";
|
|
12059
|
+
return [{
|
|
12060
|
+
path: `test/factories/${(0, _geekmidas_manifest.kebabCase)(db.id)}.ts`,
|
|
12061
|
+
content: `import type { Kysely } from 'kysely';
|
|
11997
12062
|
import { KyselyFactory } from '@geekmidas/testkit/kysely';
|
|
11998
12063
|
import type { Database } from '${schema}';
|
|
11999
|
-
|
|
12064
|
+
|
|
12065
|
+
const usersBuilder = KyselyFactory.createBuilder<Database, 'users'>(
|
|
12066
|
+
'users',
|
|
12067
|
+
({ faker }) => ({
|
|
12068
|
+
id: faker.string.uuid(),
|
|
12069
|
+
name: faker.person.fullName(),
|
|
12070
|
+
email: faker.internet.email(),
|
|
12071
|
+
createdAt: new Date(),
|
|
12072
|
+
}),
|
|
12073
|
+
);
|
|
12000
12074
|
|
|
12001
12075
|
const builders = { users: usersBuilder };
|
|
12002
12076
|
const seeds = {};
|
|
12003
12077
|
|
|
12078
|
+
/**
|
|
12079
|
+
* This database's factory. \`gkm test\` builds it on each feature test's
|
|
12080
|
+
* transaction and hands it over as \`factories.get('${(0, _geekmidas_manifest.serviceKey)(db.id)}')\`.
|
|
12081
|
+
*/
|
|
12004
12082
|
export function createFactory(db: Kysely<Database>) {
|
|
12005
12083
|
return new KyselyFactory<Database, typeof builders, typeof seeds>(
|
|
12006
12084
|
builders,
|
|
@@ -12011,38 +12089,7 @@ export function createFactory(db: Kysely<Database>) {
|
|
|
12011
12089
|
|
|
12012
12090
|
export type Factory = ReturnType<typeof createFactory>;
|
|
12013
12091
|
`
|
|
12014
|
-
|
|
12015
|
-
{
|
|
12016
|
-
path: "test/factory/users.ts",
|
|
12017
|
-
content: `import { KyselyFactory } from '@geekmidas/testkit/kysely';
|
|
12018
|
-
import type { Database } from '${schema}';
|
|
12019
|
-
|
|
12020
|
-
export const usersBuilder = KyselyFactory.createBuilder<Database, 'users'>(
|
|
12021
|
-
'users',
|
|
12022
|
-
({ faker }) => ({
|
|
12023
|
-
id: faker.string.uuid(),
|
|
12024
|
-
name: faker.person.fullName(),
|
|
12025
|
-
email: faker.internet.email(),
|
|
12026
|
-
createdAt: new Date(),
|
|
12027
|
-
}),
|
|
12028
|
-
);
|
|
12029
|
-
`
|
|
12030
|
-
},
|
|
12031
|
-
{
|
|
12032
|
-
path: "test/example.spec.ts",
|
|
12033
|
-
content: `import { describe, expect } from 'vitest';
|
|
12034
|
-
import { it } from './config.ts';
|
|
12035
|
-
|
|
12036
|
-
describe('example', () => {
|
|
12037
|
-
it('should have a working test setup', async ({ trx }) => {
|
|
12038
|
-
// trx is a Kysely transaction; everything in it is rolled back after the
|
|
12039
|
-
// test
|
|
12040
|
-
expect(trx).toBeDefined();
|
|
12041
|
-
});
|
|
12042
|
-
});
|
|
12043
|
-
`
|
|
12044
|
-
}
|
|
12045
|
-
];
|
|
12092
|
+
}];
|
|
12046
12093
|
}
|
|
12047
12094
|
//#endregion
|
|
12048
12095
|
//#region src/init/generators/ui.ts
|
|
@@ -14512,7 +14559,8 @@ async function initCommand(projectName, options = {}) {
|
|
|
14512
14559
|
...generateGithubFiles(templateOptions),
|
|
14513
14560
|
...generateMonorepoFiles(templateOptions, baseTemplate),
|
|
14514
14561
|
...generateRootConstructs(templateOptions),
|
|
14515
|
-
...generateModelsPackage(templateOptions)
|
|
14562
|
+
...generateModelsPackage(templateOptions),
|
|
14563
|
+
...generateTestFactoryFiles(templateOptions)
|
|
14516
14564
|
] : [];
|
|
14517
14565
|
const webAppFiles = isFullstack ? frontendFramework === "tanstack-start" ? generateTanStackWebFiles(templateOptions) : frontendFramework === "expo" ? generateExpoAppFiles(templateOptions) : generateWebAppFiles(templateOptions) : [];
|
|
14518
14566
|
const authAppFiles = isFullstack ? generateAuthAppFiles(templateOptions) : [];
|
|
@@ -14975,7 +15023,7 @@ var NoStoredSecrets = class extends Error {
|
|
|
14975
15023
|
}
|
|
14976
15024
|
};
|
|
14977
15025
|
async function reconcile(secrets, workspace, stage) {
|
|
14978
|
-
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-
|
|
15026
|
+
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-C8TbIWxO.cjs")).then((n) => n.workspace_exports);
|
|
14979
15027
|
const result = require_reconcile.reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
14980
15028
|
return result ? {
|
|
14981
15029
|
secrets: result.secrets,
|
|
@@ -15319,7 +15367,8 @@ async function testCommand(options = {}) {
|
|
|
15319
15367
|
patterns: require_workspace$1.constructGlobs(workspace),
|
|
15320
15368
|
cacheBackend: require_workspace$1.backendsOf(workspace).cache,
|
|
15321
15369
|
stage: require_workspace.TEST_STAGE,
|
|
15322
|
-
env: finalCredentials
|
|
15370
|
+
env: finalCredentials,
|
|
15371
|
+
factories: (0, node_path.join)(workspace.root, workspace.test.factories ?? "test/factories")
|
|
15323
15372
|
}) : void 0;
|
|
15324
15373
|
if (options.prepare) {
|
|
15325
15374
|
console.log(manifestPath ? ` 🧪 Test harness written beside ${manifestPath}` : " 🧪 Nothing to prepare: this project declares no constructs");
|
|
@@ -15368,9 +15417,12 @@ async function testCommand(options = {}) {
|
|
|
15368
15417
|
});
|
|
15369
15418
|
});
|
|
15370
15419
|
}
|
|
15371
|
-
/**
|
|
15420
|
+
/**
|
|
15421
|
+
* Migrate the test stage just reconciled, then seed it: a test run never
|
|
15422
|
+
* starts without the reference data the app needs.
|
|
15423
|
+
*/
|
|
15372
15424
|
async function migrateTestStage(workspace, env) {
|
|
15373
|
-
const {
|
|
15425
|
+
const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-BkytlJlU.cjs"));
|
|
15374
15426
|
const { discover } = await Promise.resolve().then(() => require("./discover-CroTIm0K.cjs")).then((n) => n.discover_exports);
|
|
15375
15427
|
const sources = {};
|
|
15376
15428
|
const manifest = await discover({
|
|
@@ -15378,13 +15430,15 @@ async function migrateTestStage(workspace, env) {
|
|
|
15378
15430
|
cwd: workspace.root,
|
|
15379
15431
|
sources
|
|
15380
15432
|
});
|
|
15381
|
-
const
|
|
15433
|
+
const { migrations, seeds } = await migrateAndSeed({
|
|
15382
15434
|
root: workspace.root,
|
|
15383
15435
|
manifest,
|
|
15384
15436
|
sources,
|
|
15385
|
-
env
|
|
15437
|
+
env,
|
|
15438
|
+
stage: require_workspace.TEST_STAGE
|
|
15386
15439
|
});
|
|
15387
|
-
for (const { target, applied } of
|
|
15440
|
+
for (const { target, applied } of migrations) if (applied.length > 0) console.log(` 🗄️ ${target.migrations}: applied ${applied.join(", ")}`);
|
|
15441
|
+
for (const { target, seeded } of seeds) if (seeded.length > 0) console.log(` 🌱 ${target.seeds}: ran ${seeded.join(", ")}`);
|
|
15388
15442
|
}
|
|
15389
15443
|
/** `gkm test` outside a project that declares stages, with none named. */
|
|
15390
15444
|
var NoStageToTest = class extends Error {
|
|
@@ -15768,7 +15822,7 @@ program.command("build").description("Build handlers and the manifest from decla
|
|
|
15768
15822
|
process.exit(1);
|
|
15769
15823
|
}
|
|
15770
15824
|
});
|
|
15771
|
-
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) => {
|
|
15825
|
+
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) => {
|
|
15772
15826
|
try {
|
|
15773
15827
|
const globalOptions = program.opts();
|
|
15774
15828
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
@@ -15777,7 +15831,9 @@ program.command("dev").description("Reconcile constructs and start the dev serve
|
|
|
15777
15831
|
portExplicit: !!options.port,
|
|
15778
15832
|
enableOpenApi: options.enableOpenapi ?? true,
|
|
15779
15833
|
entry: options.entry,
|
|
15780
|
-
watch: options.watch
|
|
15834
|
+
watch: options.watch,
|
|
15835
|
+
migrate: options.migrate,
|
|
15836
|
+
seed: options.seed
|
|
15781
15837
|
});
|
|
15782
15838
|
} catch (error) {
|
|
15783
15839
|
console.error(formatError(error));
|
|
@@ -15794,7 +15850,7 @@ program.command("exec").description("Run a command with secrets injected into Cr
|
|
|
15794
15850
|
process.exit(1);
|
|
15795
15851
|
}
|
|
15796
15852
|
});
|
|
15797
|
-
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
|
|
15853
|
+
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) => {
|
|
15798
15854
|
try {
|
|
15799
15855
|
const globalOptions = program.opts();
|
|
15800
15856
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
@@ -15807,11 +15863,11 @@ program.command("test").description("Run tests with secrets loaded from environm
|
|
|
15807
15863
|
process.exit(1);
|
|
15808
15864
|
}
|
|
15809
15865
|
});
|
|
15810
|
-
program.command("migrate").description("Apply each database construct's migrations from db/<construct
|
|
15866
|
+
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) => {
|
|
15811
15867
|
try {
|
|
15812
15868
|
const globalOptions = program.opts();
|
|
15813
15869
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15814
|
-
const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
15870
|
+
const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-BkytlJlU.cjs"));
|
|
15815
15871
|
await migrateCommand({
|
|
15816
15872
|
...options,
|
|
15817
15873
|
...construct ? { construct } : {}
|
|
@@ -15821,11 +15877,25 @@ program.command("migrate").description("Apply each database construct's migratio
|
|
|
15821
15877
|
process.exit(1);
|
|
15822
15878
|
}
|
|
15823
15879
|
});
|
|
15880
|
+
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) => {
|
|
15881
|
+
try {
|
|
15882
|
+
const globalOptions = program.opts();
|
|
15883
|
+
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15884
|
+
const { seedCommand } = await Promise.resolve().then(() => require("./migrate-BkytlJlU.cjs"));
|
|
15885
|
+
await seedCommand({
|
|
15886
|
+
...options,
|
|
15887
|
+
...construct ? { construct } : {}
|
|
15888
|
+
});
|
|
15889
|
+
} catch (error) {
|
|
15890
|
+
console.error(formatError(error));
|
|
15891
|
+
process.exit(1);
|
|
15892
|
+
}
|
|
15893
|
+
});
|
|
15824
15894
|
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) => {
|
|
15825
15895
|
try {
|
|
15826
15896
|
const globalOptions = program.opts();
|
|
15827
15897
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15828
|
-
const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
15898
|
+
const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-BkytlJlU.cjs"));
|
|
15829
15899
|
await migrationCommand({
|
|
15830
15900
|
construct,
|
|
15831
15901
|
...name ? { name } : {}
|
|
@@ -15993,7 +16063,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
15993
16063
|
console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
15994
16064
|
process.exit(1);
|
|
15995
16065
|
}
|
|
15996
|
-
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-
|
|
16066
|
+
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-C8TbIWxO.cjs")).then((n) => n.workspace_exports);
|
|
15997
16067
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
15998
16068
|
if (!result) {
|
|
15999
16069
|
console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
|
|
@@ -16052,7 +16122,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
|
|
|
16052
16122
|
try {
|
|
16053
16123
|
const globalOptions = program.opts();
|
|
16054
16124
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16055
|
-
const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-
|
|
16125
|
+
const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-JhvrmjQp.cjs"));
|
|
16056
16126
|
await deployGithubCommand(options);
|
|
16057
16127
|
} catch (error) {
|
|
16058
16128
|
console.error(formatError(error));
|