@geekmidas/cli 10.0.0-alpha.60 → 10.0.0-alpha.61

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 (73) hide show
  1. package/dist/{HostingerProvider-BTZd2u0f.cjs → HostingerProvider-CgDaD8VR.cjs} +2 -2
  2. package/dist/{HostingerProvider-BTZd2u0f.cjs.map → HostingerProvider-CgDaD8VR.cjs.map} +1 -1
  3. package/dist/{HostingerProvider-DgzKa_Tj.mjs → HostingerProvider-DI_Pr2VE.mjs} +2 -2
  4. package/dist/{HostingerProvider-DgzKa_Tj.mjs.map → HostingerProvider-DI_Pr2VE.mjs.map} +1 -1
  5. package/dist/{LegacyStateStore-CToa2hga.mjs → LegacyStateStore-C7vFAxbu.mjs} +2 -2
  6. package/dist/{LegacyStateStore-CToa2hga.mjs.map → LegacyStateStore-C7vFAxbu.mjs.map} +1 -1
  7. package/dist/{LegacyStateStore-CidlPT0N.cjs → LegacyStateStore-JbBVFp_2.cjs} +2 -2
  8. package/dist/{LegacyStateStore-CidlPT0N.cjs.map → LegacyStateStore-JbBVFp_2.cjs.map} +1 -1
  9. package/dist/{LocalStateStore-Dpuqka1N.cjs → LocalStateStore-DHj_-CQB.cjs} +2 -2
  10. package/dist/{LocalStateStore-Dpuqka1N.cjs.map → LocalStateStore-DHj_-CQB.cjs.map} +1 -1
  11. package/dist/{LocalStateStore-BFLiOocU.mjs → LocalStateStore-DfUwQsL8.mjs} +2 -2
  12. package/dist/{LocalStateStore-BFLiOocU.mjs.map → LocalStateStore-DfUwQsL8.mjs.map} +1 -1
  13. package/dist/{OpenApiTsGenerator-BTuUumsV.cjs → OpenApiTsGenerator-BSrgw7B0.cjs} +31 -2
  14. package/dist/OpenApiTsGenerator-BSrgw7B0.cjs.map +1 -0
  15. package/dist/{OpenApiTsGenerator-CvVr1Ilz.mjs → OpenApiTsGenerator-q5B0z8Xg.mjs} +31 -2
  16. package/dist/OpenApiTsGenerator-q5B0z8Xg.mjs.map +1 -0
  17. package/dist/{S3StateStore-aaGkjcxO.mjs → S3StateStore-Dq6hHtzY.mjs} +2 -2
  18. package/dist/{S3StateStore-aaGkjcxO.mjs.map → S3StateStore-Dq6hHtzY.mjs.map} +1 -1
  19. package/dist/{S3StateStore-cnm813Xn.cjs → S3StateStore-xqnpUrzy.cjs} +2 -2
  20. package/dist/{S3StateStore-cnm813Xn.cjs.map → S3StateStore-xqnpUrzy.cjs.map} +1 -1
  21. package/dist/{SSMStateStore-DwaGprVX.cjs → SSMStateStore-5dBRwVlJ.cjs} +2 -2
  22. package/dist/{SSMStateStore-DwaGprVX.cjs.map → SSMStateStore-5dBRwVlJ.cjs.map} +1 -1
  23. package/dist/{SSMStateStore-BllaJKGo.mjs → SSMStateStore-SUmvAbLi.mjs} +2 -2
  24. package/dist/{SSMStateStore-BllaJKGo.mjs.map → SSMStateStore-SUmvAbLi.mjs.map} +1 -1
  25. package/dist/{bundler-DJVR4QlW.mjs → bundler-B5VsKW-_.mjs} +2 -2
  26. package/dist/{bundler-DJVR4QlW.mjs.map → bundler-B5VsKW-_.mjs.map} +1 -1
  27. package/dist/{bundler-CzXZPSBJ.cjs → bundler-DXATpO1T.cjs} +2 -2
  28. package/dist/{bundler-CzXZPSBJ.cjs.map → bundler-DXATpO1T.cjs.map} +1 -1
  29. package/dist/{cli-DzR1hpum.mjs → cli-DEA_J3Sa.mjs} +2 -2
  30. package/dist/{cli-DzR1hpum.mjs.map → cli-DEA_J3Sa.mjs.map} +1 -1
  31. package/dist/{cli-D_qTl20Z.cjs → cli-DQdrVkj2.cjs} +2 -2
  32. package/dist/{cli-D_qTl20Z.cjs.map → cli-DQdrVkj2.cjs.map} +1 -1
  33. package/dist/deploy/public.cjs +2 -2
  34. package/dist/deploy/public.mjs +2 -2
  35. package/dist/{deploy-D6Q09OkA.mjs → deploy-BJcLmVTc.mjs} +6 -153
  36. package/dist/deploy-BJcLmVTc.mjs.map +1 -0
  37. package/dist/{deploy-0fBVeWO-.cjs → deploy-BbJdgQ2b.cjs} +17 -170
  38. package/dist/deploy-BbJdgQ2b.cjs.map +1 -0
  39. package/dist/{encryption-aDA2AzVx.mjs → encryption-DaA9gRXr.mjs} +2 -2
  40. package/dist/{encryption-aDA2AzVx.mjs.map → encryption-DaA9gRXr.mjs.map} +1 -1
  41. package/dist/{encryption-CML4UaPi.cjs → encryption-ojzUo7-z.cjs} +2 -2
  42. package/dist/{encryption-CML4UaPi.cjs.map → encryption-ojzUo7-z.cjs.map} +1 -1
  43. package/dist/index.cjs +1720 -139
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.mjs +1712 -147
  46. package/dist/index.mjs.map +1 -1
  47. package/dist/migrate-5_J5JySj.mjs +265 -0
  48. package/dist/migrate-5_J5JySj.mjs.map +1 -0
  49. package/dist/migrate-BScRpCEx.cjs +270 -0
  50. package/dist/migrate-BScRpCEx.cjs.map +1 -0
  51. package/dist/openapi.cjs +1 -1
  52. package/dist/openapi.mjs +1 -1
  53. package/dist/{ready-CIHpnUc5.mjs → ready-DQIy21W5.mjs} +2 -2
  54. package/dist/{ready-CIHpnUc5.mjs.map → ready-DQIy21W5.mjs.map} +1 -1
  55. package/dist/{ready-CCLIAkwR.cjs → ready-XlNx-Gbo.cjs} +2 -2
  56. package/dist/{ready-CCLIAkwR.cjs.map → ready-XlNx-Gbo.cjs.map} +1 -1
  57. package/dist/{sniffer-DIv12ncU.cjs → sniffer-CQmJnNJM.cjs} +2569 -2161
  58. package/dist/sniffer-CQmJnNJM.cjs.map +1 -0
  59. package/dist/{sniffer-BM8fpCas.mjs → sniffer-bdleks5l.mjs} +2416 -2176
  60. package/dist/sniffer-bdleks5l.mjs.map +1 -0
  61. package/dist/vitest.cjs +1 -1
  62. package/dist/vitest.mjs +1 -1
  63. package/package.json +13 -13
  64. package/dist/OpenApiTsGenerator-BTuUumsV.cjs.map +0 -1
  65. package/dist/OpenApiTsGenerator-CvVr1Ilz.mjs.map +0 -1
  66. package/dist/deploy-0fBVeWO-.cjs.map +0 -1
  67. package/dist/deploy-D6Q09OkA.mjs.map +0 -1
  68. package/dist/migrate-BTqIflwc.cjs +0 -743
  69. package/dist/migrate-BTqIflwc.cjs.map +0 -1
  70. package/dist/migrate-B_6KofUo.mjs +0 -736
  71. package/dist/migrate-B_6KofUo.mjs.map +0 -1
  72. package/dist/sniffer-BM8fpCas.mjs.map +0 -1
  73. package/dist/sniffer-DIv12ncU.cjs.map +0 -1
@@ -0,0 +1,265 @@
1
+ import { K as reconcileWorkspace, W as constructGlobs, wt as loadSecretsForApp } from "./sniffer-bdleks5l.mjs";
2
+ import { t as discover } from "./discover-FrYvzkeC.mjs";
3
+ import { l as loadWorkspaceConfig } from "./config-C5-EEdmr.mjs";
4
+ import { E as TEST_STAGE } from "./workspace-Bp1xVaOz.mjs";
5
+ import { n as secretsStoreFor } from "./store-BS8A7NcA.mjs";
6
+ import { h as pendingMigrations, l as NoSuchMigrationTarget, m as migrateDatabases, n as slug, p as migrateAndSeed, r as stamp } from "./index.mjs";
7
+ import { join } from "node:path";
8
+ import { mkdir, writeFile } from "node:fs/promises";
9
+ import { canonicalId, migrationTargets } from "@geekmidas/manifest";
10
+ import { EnvironmentParser } from "@geekmidas/envkit";
11
+ import { serviceContext } from "@geekmidas/services";
12
+ //#region src/migrate/index.ts
13
+ /**
14
+ * `gkm migrate`, `gkm seed` and `gkm migration` — applying a construct's
15
+ * migrations, running its seeds, and writing its next migration.
16
+ *
17
+ * Both work on a stage this machine reconciles: its containers up, its roles
18
+ * provisioned, its owner URLs resolved. A deployed stage is migrated by the
19
+ * deploy that ships it, which is the one thing that can reach its database.
20
+ */
21
+ const logger = console;
22
+ /** `gkm migrate --stage` named a stage only a deploy can reach. */
23
+ var MigrateDeployedStage = class extends Error {
24
+ stage;
25
+ local;
26
+ constructor(stage, local) {
27
+ super(`'${stage}' is a deployed stage, and its database is migrated by \`gkm deploy --stage ${stage}\`, which is what can reach it. \`gkm migrate\` runs against '${local}' or '${TEST_STAGE}'.`);
28
+ this.stage = stage;
29
+ this.local = local;
30
+ this.name = "MigrateDeployedStage";
31
+ }
32
+ };
33
+ /** `gkm migration` for a Kysely database, with no name for the file. */
34
+ var MigrationNeedsName = class extends Error {
35
+ construct;
36
+ constructor(construct) {
37
+ super(`Name the migration: \`gkm migration ${construct} add_workouts\`. The name is what the file — and the history — calls it.`);
38
+ this.construct = construct;
39
+ this.name = "MigrationNeedsName";
40
+ }
41
+ };
42
+ /**
43
+ * Reconcile a stage so it can be migrated: containers up, databases, schemas,
44
+ * roles and grants in place, owner URLs resolved.
45
+ *
46
+ * Migrating assumes the roles exist, and this is what makes that true rather
47
+ * than something a developer has to have run first.
48
+ */
49
+ async function readyStage(cwd, stage) {
50
+ const { workspace } = await loadWorkspaceConfig(cwd);
51
+ const local = workspace.stages.local;
52
+ const target = stage ?? local;
53
+ if (target !== local && target !== "test") throw new MigrateDeployedStage(target, local);
54
+ const sources = {};
55
+ const manifest = await discover({
56
+ patterns: constructGlobs(workspace),
57
+ cwd: workspace.root,
58
+ sources
59
+ });
60
+ return {
61
+ workspace,
62
+ manifest,
63
+ sources,
64
+ env: { ...(await reconcileWorkspace(workspace, {
65
+ stage: target,
66
+ manifest,
67
+ start: true
68
+ })).env },
69
+ stage: target
70
+ };
71
+ }
72
+ /** `gkm migrate [construct]`. */
73
+ async function migrateCommand(options = {}) {
74
+ const ready = await readyStage(process.cwd(), options.stage);
75
+ const runs = await migrateDatabases({
76
+ root: ready.workspace.root,
77
+ manifest: ready.manifest,
78
+ sources: ready.sources,
79
+ env: ready.env,
80
+ ...options.construct ? { only: options.construct } : {}
81
+ });
82
+ if (runs.length === 0) {
83
+ logger.log("🗄️ No database constructs to migrate.");
84
+ return;
85
+ }
86
+ logMigrations(runs);
87
+ }
88
+ /**
89
+ * `gkm seed [construct]` — migrate, then run the seeds.
90
+ *
91
+ * Always migrates first: a seed is written against the schema its migrations
92
+ * build, and against an older one it fails on a table that is not there yet.
93
+ */
94
+ async function seedCommand(options = {}) {
95
+ const ready = await readyStage(process.cwd(), options.stage);
96
+ const { migrations, seeds } = await migrateAndSeed({
97
+ root: ready.workspace.root,
98
+ manifest: ready.manifest,
99
+ sources: ready.sources,
100
+ env: ready.env,
101
+ stage: ready.stage,
102
+ ...options.construct ? { only: options.construct } : {}
103
+ });
104
+ if (migrations.length === 0) {
105
+ logger.log("🗄️ No database constructs to seed.");
106
+ return;
107
+ }
108
+ logMigrations(migrations);
109
+ logSeeds(seeds);
110
+ }
111
+ /** One line per construct: up to date, or what was applied. */
112
+ function logMigrations(runs, log = logger.log) {
113
+ for (const { target, applied } of runs) {
114
+ if (applied.length === 0) {
115
+ log(`🗄️ ${target.migrations}: up to date`);
116
+ continue;
117
+ }
118
+ log(`🗄️ ${target.migrations}: applied ${applied.length}`);
119
+ for (const name of applied) log(` ✓ ${name}`);
120
+ }
121
+ }
122
+ /** One line per construct that has seeds, and each seed it ran. */
123
+ function logSeeds(runs, log = logger.log) {
124
+ for (const { target, seeded } of runs) {
125
+ if (seeded.length === 0) continue;
126
+ log(`🌱 ${target.seeds}: ran ${seeded.length}`);
127
+ for (const name of seeded) log(` ✓ ${name}`);
128
+ }
129
+ }
130
+ /**
131
+ * What `gkm dev` prints: one line per construct whose folder is ahead of its
132
+ * history, and nothing when every folder is applied.
133
+ *
134
+ * Reported, never applied. A migration being written would otherwise run the
135
+ * moment its file is saved, half-finished, against the database the developer
136
+ * is working in.
137
+ */
138
+ async function reportPendingMigrations(workspace, env) {
139
+ const sources = {};
140
+ const manifest = await discover({
141
+ patterns: constructGlobs(workspace),
142
+ cwd: workspace.root,
143
+ sources
144
+ });
145
+ const pending = await pendingMigrations({
146
+ root: workspace.root,
147
+ manifest,
148
+ sources,
149
+ env
150
+ });
151
+ for (const { target, pending: names } of pending) logger.log(`⚠️ ${target.migrations}: ${names.length} pending (${names.join(", ")}) — run gkm migrate`);
152
+ }
153
+ /**
154
+ * What `gkm dev --migrate` and `gkm dev --seed` do before the apps start:
155
+ * migrate, and with `seed`, run the seeds after — once, at startup, never on a
156
+ * save, for the reason {@link reportPendingMigrations} gives.
157
+ *
158
+ * A failure here is thrown, unlike the report's: the developer asked for the
159
+ * database to be brought up to date, and apps started against one in an
160
+ * unknown state would fail somewhere less obvious.
161
+ */
162
+ async function prepareDevDatabases(workspace, env, { seed = false } = {}) {
163
+ const sources = {};
164
+ const manifest = await discover({
165
+ patterns: constructGlobs(workspace),
166
+ cwd: workspace.root,
167
+ sources
168
+ });
169
+ const options = {
170
+ root: workspace.root,
171
+ manifest,
172
+ sources,
173
+ env
174
+ };
175
+ if (!seed) {
176
+ logMigrations(await migrateDatabases(options));
177
+ return;
178
+ }
179
+ const { migrations, seeds } = await migrateAndSeed({
180
+ ...options,
181
+ stage: workspace.stages.local
182
+ });
183
+ logMigrations(migrations);
184
+ logSeeds(seeds);
185
+ }
186
+ /**
187
+ * `gkm migration <construct> [name]` — write the construct's next migration.
188
+ *
189
+ * For a database, an empty `up`/`down` to fill in. For an auth server, what
190
+ * its schema is missing, computed against its tenant once every committed
191
+ * migration has been applied — so the file is only the change, and is
192
+ * reviewed and committed like any other.
193
+ */
194
+ async function migrationCommand(options) {
195
+ const ready = await readyStage(process.cwd());
196
+ const targets = migrationTargets(ready.manifest);
197
+ const id = canonicalId(options.construct);
198
+ const auth = authConstruct(ready.sources, id);
199
+ if (auth) {
200
+ const target = targets.find((t) => t.id === auth.databaseId);
201
+ if (!target) throw new NoSuchMigrationTarget(auth.databaseId, ids(targets));
202
+ return writeAuthMigration(ready, auth, target, options);
203
+ }
204
+ const target = targets.find((t) => t.id === id);
205
+ if (!target) throw new NoSuchMigrationTarget(options.construct, [...ids(targets), ...authIds(ready.sources)]);
206
+ if (!options.name) throw new MigrationNeedsName(options.construct);
207
+ const file = await write(ready.workspace.root, target, `${stamp(options.now)}_${slug(options.name)}.ts`, STUB);
208
+ logger.log(`📝 ${file}`);
209
+ return file;
210
+ }
211
+ function authConstruct(sources, id) {
212
+ const construct = sources[id]?.construct;
213
+ return construct && typeof construct.pendingMigration === "function" && typeof construct.databaseId === "string" ? construct : void 0;
214
+ }
215
+ function authIds(sources) {
216
+ return Object.keys(sources).filter((id) => authConstruct(sources, id));
217
+ }
218
+ async function writeAuthMigration(ready, auth, target, options) {
219
+ await migrateDatabases({
220
+ root: ready.workspace.root,
221
+ manifest: ready.manifest,
222
+ sources: ready.sources,
223
+ env: ready.env,
224
+ only: target.id
225
+ });
226
+ const stage = ready.workspace.stages.local;
227
+ const secrets = await loadSecretsForApp(await secretsStoreFor(ready.workspace, stage), stage);
228
+ const pending = await auth.pendingMigration({
229
+ envParser: new EnvironmentParser({
230
+ ...secrets,
231
+ ...ready.env
232
+ }),
233
+ context: serviceContext
234
+ });
235
+ if (!pending) {
236
+ logger.log(`✓ ${target.migrations}: ${auth.id} is up to date`);
237
+ return;
238
+ }
239
+ const file = await write(ready.workspace.root, target, `${stamp(options.now)}_${slug(options.name ?? "better_auth")}.sql`, `${pending.trim()}\n`);
240
+ logger.log(`📝 ${file}`);
241
+ return file;
242
+ }
243
+ const STUB = `import type { Kysely } from 'kysely';
244
+
245
+ export async function up(db: Kysely<unknown>): Promise<void> {
246
+ // await db.schema.createTable('…').addColumn(…).execute();
247
+ }
248
+
249
+ export async function down(db: Kysely<unknown>): Promise<void> {
250
+ // await db.schema.dropTable('…').execute();
251
+ }
252
+ `;
253
+ async function write(root, target, file, content) {
254
+ const folder = join(root, target.migrations);
255
+ await mkdir(folder, { recursive: true });
256
+ await writeFile(join(folder, file), content, { flag: "wx" });
257
+ return `${target.migrations}/${file}`;
258
+ }
259
+ function ids(targets) {
260
+ return targets.map((target) => target.id);
261
+ }
262
+ //#endregion
263
+ export { migrateAndSeed, migrateCommand, migrationCommand, prepareDevDatabases, reportPendingMigrations, seedCommand };
264
+
265
+ //# sourceMappingURL=migrate-5_J5JySj.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate-5_J5JySj.mjs","names":[],"sources":["../src/migrate/index.ts"],"sourcesContent":["/**\n * `gkm migrate`, `gkm seed` and `gkm migration` — applying a construct's\n * migrations, running its seeds, and writing its next migration.\n *\n * Both work on a stage this machine reconciles: its containers up, its roles\n * provisioned, its owner URLs resolved. A deployed stage is migrated by the\n * deploy that ships it, which is the one thing that can reach its database.\n */\n\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { EnvironmentParser } from '@geekmidas/envkit';\nimport {\n\ttype ConstructManifest,\n\tcanonicalId,\n\ttype MigrationTarget,\n\tmigrationTargets,\n} from '@geekmidas/manifest';\nimport {\n\ttype ServiceRegisterOptions,\n\tserviceContext,\n} from '@geekmidas/services';\nimport { loadWorkspaceConfig } from '../config';\nimport { loadSecretsForApp } from '../credentials';\nimport { type ConstructSource, discover } from '../reconcile/discover';\nimport { constructGlobs, reconcileWorkspace } from '../reconcile/workspace.js';\nimport { secretsStoreFor } from '../secrets/store.js';\nimport { TEST_STAGE } from '../workspace/stages';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport {\n\ttype MigrationRun,\n\tmigrateAndSeed,\n\tmigrateDatabases,\n\tNoSuchMigrationTarget,\n\tpendingMigrations,\n\ttype SeedRun,\n} from './databases';\nimport { slug, stamp } from './names';\n\nexport {\n\tKyselyNotFound,\n\tMigrationFailed,\n\ttype MigrationRun,\n\tMigrationsOutsideFolder,\n\tmigrateAndSeed,\n\tmigrateDatabases,\n\tNoOwnerCredential,\n\tNoSuchMigrationTarget,\n\ttype PendingMigrations,\n\tpendingMigrations,\n\tSeedFailed,\n\ttype SeedOptions,\n\ttype SeedRun,\n\tseedDatabases,\n\tUnknownDatabaseFolder,\n\tUnknownMigrationFolder,\n} from './databases';\nexport { slug, stamp } from './names';\nexport {\n\tDuplicateMigration,\n\tMigrationHasNoUp,\n\tSeedHasNoSeed,\n} from './provider';\n\nconst logger = console;\n\n/** `gkm migrate --stage` named a stage only a deploy can reach. */\nexport class MigrateDeployedStage extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly local: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' is a deployed stage, and its database is migrated by ` +\n\t\t\t\t`\\`gkm deploy --stage ${stage}\\`, which is what can reach it. ` +\n\t\t\t\t`\\`gkm migrate\\` runs against '${local}' or '${TEST_STAGE}'.`,\n\t\t);\n\t\tthis.name = 'MigrateDeployedStage';\n\t}\n}\n\n/** `gkm migration` for a Kysely database, with no name for the file. */\nexport class MigrationNeedsName extends Error {\n\tconstructor(readonly construct: string) {\n\t\tsuper(\n\t\t\t`Name the migration: \\`gkm migration ${construct} add_workouts\\`. ` +\n\t\t\t\t`The name is what the file — and the history — calls it.`,\n\t\t);\n\t\tthis.name = 'MigrationNeedsName';\n\t}\n}\n\nexport interface MigrateOptions {\n\t/** The stage to migrate: the project's local one, or `test`. */\n\tstage?: string;\n\t/** One construct, by name. All of them when absent. */\n\tconstruct?: string;\n}\n\n/** A workspace's stage, reconciled and ready to migrate. */\nexport interface ReadyStage {\n\tworkspace: NormalizedWorkspace;\n\tmanifest: ConstructManifest;\n\tsources: Record<string, ConstructSource>;\n\t/** Every URL the stage resolved — the owner URLs among them. */\n\tenv: Record<string, string>;\n\t/** The stage it is: the local one, or `test`. */\n\tstage: string;\n}\n\n/**\n * Reconcile a stage so it can be migrated: containers up, databases, schemas,\n * roles and grants in place, owner URLs resolved.\n *\n * Migrating assumes the roles exist, and this is what makes that true rather\n * than something a developer has to have run first.\n */\nexport async function readyStage(\n\tcwd: string,\n\tstage?: string,\n): Promise<ReadyStage> {\n\tconst { workspace } = await loadWorkspaceConfig(cwd);\n\tconst local = workspace.stages.local;\n\tconst target = stage ?? local;\n\tif (target !== local && target !== TEST_STAGE) {\n\t\tthrow new MigrateDeployedStage(target, local);\n\t}\n\n\tconst sources: Record<string, ConstructSource> = {};\n\tconst manifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\tsources,\n\t});\n\n\tconst reconciled = await reconcileWorkspace(workspace, {\n\t\tstage: target,\n\t\tmanifest,\n\t\tstart: true,\n\t});\n\n\treturn {\n\t\tworkspace,\n\t\tmanifest,\n\t\tsources,\n\t\tenv: { ...reconciled.env },\n\t\tstage: target,\n\t};\n}\n\n/** `gkm migrate [construct]`. */\nexport async function migrateCommand(\n\toptions: MigrateOptions = {},\n): Promise<void> {\n\tconst ready = await readyStage(process.cwd(), options.stage);\n\n\tconst runs = await migrateDatabases({\n\t\troot: ready.workspace.root,\n\t\tmanifest: ready.manifest,\n\t\tsources: ready.sources,\n\t\tenv: ready.env,\n\t\t...(options.construct ? { only: options.construct } : {}),\n\t});\n\n\tif (runs.length === 0) {\n\t\tlogger.log('🗄️ No database constructs to migrate.');\n\t\treturn;\n\t}\n\n\tlogMigrations(runs);\n}\n\n/**\n * `gkm seed [construct]` — migrate, then run the seeds.\n *\n * Always migrates first: a seed is written against the schema its migrations\n * build, and against an older one it fails on a table that is not there yet.\n */\nexport async function seedCommand(options: MigrateOptions = {}): Promise<void> {\n\tconst ready = await readyStage(process.cwd(), options.stage);\n\n\tconst { migrations, seeds } = await migrateAndSeed({\n\t\troot: ready.workspace.root,\n\t\tmanifest: ready.manifest,\n\t\tsources: ready.sources,\n\t\tenv: ready.env,\n\t\tstage: ready.stage,\n\t\t...(options.construct ? { only: options.construct } : {}),\n\t});\n\n\tif (migrations.length === 0) {\n\t\tlogger.log('🗄️ No database constructs to seed.');\n\t\treturn;\n\t}\n\n\tlogMigrations(migrations);\n\tlogSeeds(seeds);\n}\n\n/** One line per construct: up to date, or what was applied. */\nexport function logMigrations(\n\truns: readonly MigrationRun[],\n\tlog: (line: string) => void = logger.log,\n): void {\n\tfor (const { target, applied } of runs) {\n\t\tif (applied.length === 0) {\n\t\t\tlog(`🗄️ ${target.migrations}: up to date`);\n\t\t\tcontinue;\n\t\t}\n\t\tlog(`🗄️ ${target.migrations}: applied ${applied.length}`);\n\t\tfor (const name of applied) log(` ✓ ${name}`);\n\t}\n}\n\n/** One line per construct that has seeds, and each seed it ran. */\nexport function logSeeds(\n\truns: readonly SeedRun[],\n\tlog: (line: string) => void = logger.log,\n): void {\n\tfor (const { target, seeded } of runs) {\n\t\tif (seeded.length === 0) continue;\n\t\tlog(`🌱 ${target.seeds}: ran ${seeded.length}`);\n\t\tfor (const name of seeded) log(` ✓ ${name}`);\n\t}\n}\n\n/**\n * What `gkm dev` prints: one line per construct whose folder is ahead of its\n * history, and nothing when every folder is applied.\n *\n * Reported, never applied. A migration being written would otherwise run the\n * moment its file is saved, half-finished, against the database the developer\n * is working in.\n */\nexport async function reportPendingMigrations(\n\tworkspace: NormalizedWorkspace,\n\tenv: Readonly<Record<string, string>>,\n): Promise<void> {\n\tconst sources: Record<string, ConstructSource> = {};\n\tconst manifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\tsources,\n\t});\n\n\tconst pending = await pendingMigrations({\n\t\troot: workspace.root,\n\t\tmanifest,\n\t\tsources,\n\t\tenv,\n\t});\n\n\tfor (const { target, pending: names } of pending) {\n\t\tlogger.log(\n\t\t\t`⚠️ ${target.migrations}: ${names.length} pending (${names.join(', ')}) — run gkm migrate`,\n\t\t);\n\t}\n}\n\n/**\n * What `gkm dev --migrate` and `gkm dev --seed` do before the apps start:\n * migrate, and with `seed`, run the seeds after — once, at startup, never on a\n * save, for the reason {@link reportPendingMigrations} gives.\n *\n * A failure here is thrown, unlike the report's: the developer asked for the\n * database to be brought up to date, and apps started against one in an\n * unknown state would fail somewhere less obvious.\n */\nexport async function prepareDevDatabases(\n\tworkspace: NormalizedWorkspace,\n\tenv: Readonly<Record<string, string>>,\n\t{ seed = false }: { seed?: boolean } = {},\n): Promise<void> {\n\tconst sources: Record<string, ConstructSource> = {};\n\tconst manifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\tsources,\n\t});\n\tconst options = { root: workspace.root, manifest, sources, env };\n\n\tif (!seed) {\n\t\tlogMigrations(await migrateDatabases(options));\n\t\treturn;\n\t}\n\n\tconst { migrations, seeds } = await migrateAndSeed({\n\t\t...options,\n\t\tstage: workspace.stages.local,\n\t});\n\tlogMigrations(migrations);\n\tlogSeeds(seeds);\n}\n\nexport interface MigrationOptions {\n\t/** The construct the migration is for, by name. */\n\tconstruct: string;\n\t/** What the file is called, after its timestamp. */\n\tname?: string;\n\t/** The file's timestamp; the current time when absent. */\n\tnow?: Date;\n}\n\n/**\n * `gkm migration <construct> [name]` — write the construct's next migration.\n *\n * For a database, an empty `up`/`down` to fill in. For an auth server, what\n * its schema is missing, computed against its tenant once every committed\n * migration has been applied — so the file is only the change, and is\n * reviewed and committed like any other.\n */\nexport async function migrationCommand(\n\toptions: MigrationOptions,\n): Promise<string | undefined> {\n\tconst ready = await readyStage(process.cwd());\n\tconst targets = migrationTargets(ready.manifest);\n\tconst id = canonicalId(options.construct);\n\n\tconst auth = authConstruct(ready.sources, id);\n\tif (auth) {\n\t\tconst target = targets.find((t) => t.id === auth.databaseId);\n\t\tif (!target) {\n\t\t\tthrow new NoSuchMigrationTarget(auth.databaseId, ids(targets));\n\t\t}\n\t\treturn writeAuthMigration(ready, auth, target, options);\n\t}\n\n\tconst target = targets.find((t) => t.id === id);\n\tif (!target) {\n\t\tthrow new NoSuchMigrationTarget(options.construct, [\n\t\t\t...ids(targets),\n\t\t\t...authIds(ready.sources),\n\t\t]);\n\t}\n\tif (!options.name) throw new MigrationNeedsName(options.construct);\n\n\tconst file = await write(\n\t\tready.workspace.root,\n\t\ttarget,\n\t\t`${stamp(options.now)}_${slug(options.name)}.ts`,\n\t\tSTUB,\n\t);\n\tlogger.log(`📝 ${file}`);\n\treturn file;\n}\n\n/** What an auth construct offers `gkm migration`, duck-typed across copies. */\ninterface AuthConstruct {\n\tid: string;\n\tdatabaseId: string;\n\tpendingMigration(\n\t\toptions: ServiceRegisterOptions,\n\t): Promise<string | undefined>;\n}\n\nfunction authConstruct(\n\tsources: Readonly<Record<string, ConstructSource>>,\n\tid: string,\n): AuthConstruct | undefined {\n\tconst construct = sources[id]?.construct as\n\t\t| Partial<AuthConstruct>\n\t\t| undefined;\n\treturn construct &&\n\t\ttypeof construct.pendingMigration === 'function' &&\n\t\ttypeof construct.databaseId === 'string'\n\t\t? (construct as AuthConstruct)\n\t\t: undefined;\n}\n\nfunction authIds(sources: Readonly<Record<string, ConstructSource>>): string[] {\n\treturn Object.keys(sources).filter((id) => authConstruct(sources, id));\n}\n\nasync function writeAuthMigration(\n\tready: ReadyStage,\n\tauth: AuthConstruct,\n\ttarget: MigrationTarget,\n\toptions: MigrationOptions,\n): Promise<string | undefined> {\n\t// Against a tenant already at its committed migrations, so the comparison\n\t// finds only what the files do not yet say.\n\tawait migrateDatabases({\n\t\troot: ready.workspace.root,\n\t\tmanifest: ready.manifest,\n\t\tsources: ready.sources,\n\t\tenv: ready.env,\n\t\tonly: target.id,\n\t});\n\n\t// Its secret and URL come from the stage's secrets; the tenant's owner URL\n\t// from the reconcile above, which wins.\n\tconst stage = ready.workspace.stages.local;\n\tconst secrets = await loadSecretsForApp(\n\t\tawait secretsStoreFor(ready.workspace, stage),\n\t\tstage,\n\t);\n\tconst pending = await auth.pendingMigration({\n\t\tenvParser: new EnvironmentParser({ ...secrets, ...ready.env }),\n\t\tcontext: serviceContext,\n\t});\n\n\tif (!pending) {\n\t\tlogger.log(`✓ ${target.migrations}: ${auth.id} is up to date`);\n\t\treturn undefined;\n\t}\n\n\tconst file = await write(\n\t\tready.workspace.root,\n\t\ttarget,\n\t\t`${stamp(options.now)}_${slug(options.name ?? 'better_auth')}.sql`,\n\t\t`${pending.trim()}\\n`,\n\t);\n\tlogger.log(`📝 ${file}`);\n\treturn file;\n}\n\nconst STUB = `import type { Kysely } from 'kysely';\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n // await db.schema.createTable('…').addColumn(…).execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n // await db.schema.dropTable('…').execute();\n}\n`;\n\nasync function write(\n\troot: string,\n\ttarget: MigrationTarget,\n\tfile: string,\n\tcontent: string,\n): Promise<string> {\n\tconst folder = join(root, target.migrations);\n\tawait mkdir(folder, { recursive: true });\n\tawait writeFile(join(folder, file), content, { flag: 'wx' });\n\treturn `${target.migrations}/${file}`;\n}\n\nfunction ids(targets: readonly MigrationTarget[]): string[] {\n\treturn targets.map((target) => target.id);\n}\n"],"mappings":"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"}
@@ -0,0 +1,270 @@
1
+ const require_sniffer = require("./sniffer-CQmJnNJM.cjs");
2
+ const require_discover = require("./discover-Bc-4eA_p.cjs");
3
+ const require_config = require("./config-DXnkCEq0.cjs");
4
+ const require_workspace = require("./workspace-B64Xk1Mb.cjs");
5
+ const require_store = require("./store-KvNQwYaB.cjs");
6
+ const require_index = require("./index.cjs");
7
+ let node_path = require("node:path");
8
+ let node_fs_promises = require("node:fs/promises");
9
+ let _geekmidas_manifest = require("@geekmidas/manifest");
10
+ let _geekmidas_envkit = require("@geekmidas/envkit");
11
+ let _geekmidas_services = require("@geekmidas/services");
12
+ //#region src/migrate/index.ts
13
+ /**
14
+ * `gkm migrate`, `gkm seed` and `gkm migration` — applying a construct's
15
+ * migrations, running its seeds, and writing its next migration.
16
+ *
17
+ * Both work on a stage this machine reconciles: its containers up, its roles
18
+ * provisioned, its owner URLs resolved. A deployed stage is migrated by the
19
+ * deploy that ships it, which is the one thing that can reach its database.
20
+ */
21
+ const logger = console;
22
+ /** `gkm migrate --stage` named a stage only a deploy can reach. */
23
+ var MigrateDeployedStage = class extends Error {
24
+ stage;
25
+ local;
26
+ constructor(stage, local) {
27
+ super(`'${stage}' is a deployed stage, and its database is migrated by \`gkm deploy --stage ${stage}\`, which is what can reach it. \`gkm migrate\` runs against '${local}' or '${require_workspace.TEST_STAGE}'.`);
28
+ this.stage = stage;
29
+ this.local = local;
30
+ this.name = "MigrateDeployedStage";
31
+ }
32
+ };
33
+ /** `gkm migration` for a Kysely database, with no name for the file. */
34
+ var MigrationNeedsName = class extends Error {
35
+ construct;
36
+ constructor(construct) {
37
+ super(`Name the migration: \`gkm migration ${construct} add_workouts\`. The name is what the file — and the history — calls it.`);
38
+ this.construct = construct;
39
+ this.name = "MigrationNeedsName";
40
+ }
41
+ };
42
+ /**
43
+ * Reconcile a stage so it can be migrated: containers up, databases, schemas,
44
+ * roles and grants in place, owner URLs resolved.
45
+ *
46
+ * Migrating assumes the roles exist, and this is what makes that true rather
47
+ * than something a developer has to have run first.
48
+ */
49
+ async function readyStage(cwd, stage) {
50
+ const { workspace } = await require_config.loadWorkspaceConfig(cwd);
51
+ const local = workspace.stages.local;
52
+ const target = stage ?? local;
53
+ if (target !== local && target !== "test") throw new MigrateDeployedStage(target, local);
54
+ const sources = {};
55
+ const manifest = await require_discover.discover({
56
+ patterns: require_sniffer.constructGlobs(workspace),
57
+ cwd: workspace.root,
58
+ sources
59
+ });
60
+ return {
61
+ workspace,
62
+ manifest,
63
+ sources,
64
+ env: { ...(await require_sniffer.reconcileWorkspace(workspace, {
65
+ stage: target,
66
+ manifest,
67
+ start: true
68
+ })).env },
69
+ stage: target
70
+ };
71
+ }
72
+ /** `gkm migrate [construct]`. */
73
+ async function migrateCommand(options = {}) {
74
+ const ready = await readyStage(process.cwd(), options.stage);
75
+ const runs = await require_index.migrateDatabases({
76
+ root: ready.workspace.root,
77
+ manifest: ready.manifest,
78
+ sources: ready.sources,
79
+ env: ready.env,
80
+ ...options.construct ? { only: options.construct } : {}
81
+ });
82
+ if (runs.length === 0) {
83
+ logger.log("🗄️ No database constructs to migrate.");
84
+ return;
85
+ }
86
+ logMigrations(runs);
87
+ }
88
+ /**
89
+ * `gkm seed [construct]` — migrate, then run the seeds.
90
+ *
91
+ * Always migrates first: a seed is written against the schema its migrations
92
+ * build, and against an older one it fails on a table that is not there yet.
93
+ */
94
+ async function seedCommand(options = {}) {
95
+ const ready = await readyStage(process.cwd(), options.stage);
96
+ const { migrations, seeds } = await require_index.migrateAndSeed({
97
+ root: ready.workspace.root,
98
+ manifest: ready.manifest,
99
+ sources: ready.sources,
100
+ env: ready.env,
101
+ stage: ready.stage,
102
+ ...options.construct ? { only: options.construct } : {}
103
+ });
104
+ if (migrations.length === 0) {
105
+ logger.log("🗄️ No database constructs to seed.");
106
+ return;
107
+ }
108
+ logMigrations(migrations);
109
+ logSeeds(seeds);
110
+ }
111
+ /** One line per construct: up to date, or what was applied. */
112
+ function logMigrations(runs, log = logger.log) {
113
+ for (const { target, applied } of runs) {
114
+ if (applied.length === 0) {
115
+ log(`🗄️ ${target.migrations}: up to date`);
116
+ continue;
117
+ }
118
+ log(`🗄️ ${target.migrations}: applied ${applied.length}`);
119
+ for (const name of applied) log(` ✓ ${name}`);
120
+ }
121
+ }
122
+ /** One line per construct that has seeds, and each seed it ran. */
123
+ function logSeeds(runs, log = logger.log) {
124
+ for (const { target, seeded } of runs) {
125
+ if (seeded.length === 0) continue;
126
+ log(`🌱 ${target.seeds}: ran ${seeded.length}`);
127
+ for (const name of seeded) log(` ✓ ${name}`);
128
+ }
129
+ }
130
+ /**
131
+ * What `gkm dev` prints: one line per construct whose folder is ahead of its
132
+ * history, and nothing when every folder is applied.
133
+ *
134
+ * Reported, never applied. A migration being written would otherwise run the
135
+ * moment its file is saved, half-finished, against the database the developer
136
+ * is working in.
137
+ */
138
+ async function reportPendingMigrations(workspace, env) {
139
+ const sources = {};
140
+ const manifest = await require_discover.discover({
141
+ patterns: require_sniffer.constructGlobs(workspace),
142
+ cwd: workspace.root,
143
+ sources
144
+ });
145
+ const pending = await require_index.pendingMigrations({
146
+ root: workspace.root,
147
+ manifest,
148
+ sources,
149
+ env
150
+ });
151
+ for (const { target, pending: names } of pending) logger.log(`⚠️ ${target.migrations}: ${names.length} pending (${names.join(", ")}) — run gkm migrate`);
152
+ }
153
+ /**
154
+ * What `gkm dev --migrate` and `gkm dev --seed` do before the apps start:
155
+ * migrate, and with `seed`, run the seeds after — once, at startup, never on a
156
+ * save, for the reason {@link reportPendingMigrations} gives.
157
+ *
158
+ * A failure here is thrown, unlike the report's: the developer asked for the
159
+ * database to be brought up to date, and apps started against one in an
160
+ * unknown state would fail somewhere less obvious.
161
+ */
162
+ async function prepareDevDatabases(workspace, env, { seed = false } = {}) {
163
+ const sources = {};
164
+ const manifest = await require_discover.discover({
165
+ patterns: require_sniffer.constructGlobs(workspace),
166
+ cwd: workspace.root,
167
+ sources
168
+ });
169
+ const options = {
170
+ root: workspace.root,
171
+ manifest,
172
+ sources,
173
+ env
174
+ };
175
+ if (!seed) {
176
+ logMigrations(await require_index.migrateDatabases(options));
177
+ return;
178
+ }
179
+ const { migrations, seeds } = await require_index.migrateAndSeed({
180
+ ...options,
181
+ stage: workspace.stages.local
182
+ });
183
+ logMigrations(migrations);
184
+ logSeeds(seeds);
185
+ }
186
+ /**
187
+ * `gkm migration <construct> [name]` — write the construct's next migration.
188
+ *
189
+ * For a database, an empty `up`/`down` to fill in. For an auth server, what
190
+ * its schema is missing, computed against its tenant once every committed
191
+ * migration has been applied — so the file is only the change, and is
192
+ * reviewed and committed like any other.
193
+ */
194
+ async function migrationCommand(options) {
195
+ const ready = await readyStage(process.cwd());
196
+ const targets = (0, _geekmidas_manifest.migrationTargets)(ready.manifest);
197
+ const id = (0, _geekmidas_manifest.canonicalId)(options.construct);
198
+ const auth = authConstruct(ready.sources, id);
199
+ if (auth) {
200
+ const target = targets.find((t) => t.id === auth.databaseId);
201
+ if (!target) throw new require_index.NoSuchMigrationTarget(auth.databaseId, ids(targets));
202
+ return writeAuthMigration(ready, auth, target, options);
203
+ }
204
+ const target = targets.find((t) => t.id === id);
205
+ if (!target) throw new require_index.NoSuchMigrationTarget(options.construct, [...ids(targets), ...authIds(ready.sources)]);
206
+ if (!options.name) throw new MigrationNeedsName(options.construct);
207
+ const file = await write(ready.workspace.root, target, `${require_index.stamp(options.now)}_${require_index.slug(options.name)}.ts`, STUB);
208
+ logger.log(`📝 ${file}`);
209
+ return file;
210
+ }
211
+ function authConstruct(sources, id) {
212
+ const construct = sources[id]?.construct;
213
+ return construct && typeof construct.pendingMigration === "function" && typeof construct.databaseId === "string" ? construct : void 0;
214
+ }
215
+ function authIds(sources) {
216
+ return Object.keys(sources).filter((id) => authConstruct(sources, id));
217
+ }
218
+ async function writeAuthMigration(ready, auth, target, options) {
219
+ await require_index.migrateDatabases({
220
+ root: ready.workspace.root,
221
+ manifest: ready.manifest,
222
+ sources: ready.sources,
223
+ env: ready.env,
224
+ only: target.id
225
+ });
226
+ const stage = ready.workspace.stages.local;
227
+ const secrets = await require_sniffer.loadSecretsForApp(await require_store.secretsStoreFor(ready.workspace, stage), stage);
228
+ const pending = await auth.pendingMigration({
229
+ envParser: new _geekmidas_envkit.EnvironmentParser({
230
+ ...secrets,
231
+ ...ready.env
232
+ }),
233
+ context: _geekmidas_services.serviceContext
234
+ });
235
+ if (!pending) {
236
+ logger.log(`✓ ${target.migrations}: ${auth.id} is up to date`);
237
+ return;
238
+ }
239
+ const file = await write(ready.workspace.root, target, `${require_index.stamp(options.now)}_${require_index.slug(options.name ?? "better_auth")}.sql`, `${pending.trim()}\n`);
240
+ logger.log(`📝 ${file}`);
241
+ return file;
242
+ }
243
+ const STUB = `import type { Kysely } from 'kysely';
244
+
245
+ export async function up(db: Kysely<unknown>): Promise<void> {
246
+ // await db.schema.createTable('…').addColumn(…).execute();
247
+ }
248
+
249
+ export async function down(db: Kysely<unknown>): Promise<void> {
250
+ // await db.schema.dropTable('…').execute();
251
+ }
252
+ `;
253
+ async function write(root, target, file, content) {
254
+ const folder = (0, node_path.join)(root, target.migrations);
255
+ await (0, node_fs_promises.mkdir)(folder, { recursive: true });
256
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(folder, file), content, { flag: "wx" });
257
+ return `${target.migrations}/${file}`;
258
+ }
259
+ function ids(targets) {
260
+ return targets.map((target) => target.id);
261
+ }
262
+ //#endregion
263
+ exports.migrateAndSeed = require_index.migrateAndSeed;
264
+ exports.migrateCommand = migrateCommand;
265
+ exports.migrationCommand = migrationCommand;
266
+ exports.prepareDevDatabases = prepareDevDatabases;
267
+ exports.reportPendingMigrations = reportPendingMigrations;
268
+ exports.seedCommand = seedCommand;
269
+
270
+ //# sourceMappingURL=migrate-BScRpCEx.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate-BScRpCEx.cjs","names":["TEST_STAGE","loadWorkspaceConfig","discover","constructGlobs","reconcileWorkspace","migrateDatabases","migrateAndSeed","pendingMigrations","migrationTargets","canonicalId","NoSuchMigrationTarget","stamp","slug","loadSecretsForApp","secretsStoreFor","EnvironmentParser","serviceContext","join","mkdir","writeFile"],"sources":["../src/migrate/index.ts"],"sourcesContent":["/**\n * `gkm migrate`, `gkm seed` and `gkm migration` — applying a construct's\n * migrations, running its seeds, and writing its next migration.\n *\n * Both work on a stage this machine reconciles: its containers up, its roles\n * provisioned, its owner URLs resolved. A deployed stage is migrated by the\n * deploy that ships it, which is the one thing that can reach its database.\n */\n\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { EnvironmentParser } from '@geekmidas/envkit';\nimport {\n\ttype ConstructManifest,\n\tcanonicalId,\n\ttype MigrationTarget,\n\tmigrationTargets,\n} from '@geekmidas/manifest';\nimport {\n\ttype ServiceRegisterOptions,\n\tserviceContext,\n} from '@geekmidas/services';\nimport { loadWorkspaceConfig } from '../config';\nimport { loadSecretsForApp } from '../credentials';\nimport { type ConstructSource, discover } from '../reconcile/discover';\nimport { constructGlobs, reconcileWorkspace } from '../reconcile/workspace.js';\nimport { secretsStoreFor } from '../secrets/store.js';\nimport { TEST_STAGE } from '../workspace/stages';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport {\n\ttype MigrationRun,\n\tmigrateAndSeed,\n\tmigrateDatabases,\n\tNoSuchMigrationTarget,\n\tpendingMigrations,\n\ttype SeedRun,\n} from './databases';\nimport { slug, stamp } from './names';\n\nexport {\n\tKyselyNotFound,\n\tMigrationFailed,\n\ttype MigrationRun,\n\tMigrationsOutsideFolder,\n\tmigrateAndSeed,\n\tmigrateDatabases,\n\tNoOwnerCredential,\n\tNoSuchMigrationTarget,\n\ttype PendingMigrations,\n\tpendingMigrations,\n\tSeedFailed,\n\ttype SeedOptions,\n\ttype SeedRun,\n\tseedDatabases,\n\tUnknownDatabaseFolder,\n\tUnknownMigrationFolder,\n} from './databases';\nexport { slug, stamp } from './names';\nexport {\n\tDuplicateMigration,\n\tMigrationHasNoUp,\n\tSeedHasNoSeed,\n} from './provider';\n\nconst logger = console;\n\n/** `gkm migrate --stage` named a stage only a deploy can reach. */\nexport class MigrateDeployedStage extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly local: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' is a deployed stage, and its database is migrated by ` +\n\t\t\t\t`\\`gkm deploy --stage ${stage}\\`, which is what can reach it. ` +\n\t\t\t\t`\\`gkm migrate\\` runs against '${local}' or '${TEST_STAGE}'.`,\n\t\t);\n\t\tthis.name = 'MigrateDeployedStage';\n\t}\n}\n\n/** `gkm migration` for a Kysely database, with no name for the file. */\nexport class MigrationNeedsName extends Error {\n\tconstructor(readonly construct: string) {\n\t\tsuper(\n\t\t\t`Name the migration: \\`gkm migration ${construct} add_workouts\\`. ` +\n\t\t\t\t`The name is what the file — and the history — calls it.`,\n\t\t);\n\t\tthis.name = 'MigrationNeedsName';\n\t}\n}\n\nexport interface MigrateOptions {\n\t/** The stage to migrate: the project's local one, or `test`. */\n\tstage?: string;\n\t/** One construct, by name. All of them when absent. */\n\tconstruct?: string;\n}\n\n/** A workspace's stage, reconciled and ready to migrate. */\nexport interface ReadyStage {\n\tworkspace: NormalizedWorkspace;\n\tmanifest: ConstructManifest;\n\tsources: Record<string, ConstructSource>;\n\t/** Every URL the stage resolved — the owner URLs among them. */\n\tenv: Record<string, string>;\n\t/** The stage it is: the local one, or `test`. */\n\tstage: string;\n}\n\n/**\n * Reconcile a stage so it can be migrated: containers up, databases, schemas,\n * roles and grants in place, owner URLs resolved.\n *\n * Migrating assumes the roles exist, and this is what makes that true rather\n * than something a developer has to have run first.\n */\nexport async function readyStage(\n\tcwd: string,\n\tstage?: string,\n): Promise<ReadyStage> {\n\tconst { workspace } = await loadWorkspaceConfig(cwd);\n\tconst local = workspace.stages.local;\n\tconst target = stage ?? local;\n\tif (target !== local && target !== TEST_STAGE) {\n\t\tthrow new MigrateDeployedStage(target, local);\n\t}\n\n\tconst sources: Record<string, ConstructSource> = {};\n\tconst manifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\tsources,\n\t});\n\n\tconst reconciled = await reconcileWorkspace(workspace, {\n\t\tstage: target,\n\t\tmanifest,\n\t\tstart: true,\n\t});\n\n\treturn {\n\t\tworkspace,\n\t\tmanifest,\n\t\tsources,\n\t\tenv: { ...reconciled.env },\n\t\tstage: target,\n\t};\n}\n\n/** `gkm migrate [construct]`. */\nexport async function migrateCommand(\n\toptions: MigrateOptions = {},\n): Promise<void> {\n\tconst ready = await readyStage(process.cwd(), options.stage);\n\n\tconst runs = await migrateDatabases({\n\t\troot: ready.workspace.root,\n\t\tmanifest: ready.manifest,\n\t\tsources: ready.sources,\n\t\tenv: ready.env,\n\t\t...(options.construct ? { only: options.construct } : {}),\n\t});\n\n\tif (runs.length === 0) {\n\t\tlogger.log('🗄️ No database constructs to migrate.');\n\t\treturn;\n\t}\n\n\tlogMigrations(runs);\n}\n\n/**\n * `gkm seed [construct]` — migrate, then run the seeds.\n *\n * Always migrates first: a seed is written against the schema its migrations\n * build, and against an older one it fails on a table that is not there yet.\n */\nexport async function seedCommand(options: MigrateOptions = {}): Promise<void> {\n\tconst ready = await readyStage(process.cwd(), options.stage);\n\n\tconst { migrations, seeds } = await migrateAndSeed({\n\t\troot: ready.workspace.root,\n\t\tmanifest: ready.manifest,\n\t\tsources: ready.sources,\n\t\tenv: ready.env,\n\t\tstage: ready.stage,\n\t\t...(options.construct ? { only: options.construct } : {}),\n\t});\n\n\tif (migrations.length === 0) {\n\t\tlogger.log('🗄️ No database constructs to seed.');\n\t\treturn;\n\t}\n\n\tlogMigrations(migrations);\n\tlogSeeds(seeds);\n}\n\n/** One line per construct: up to date, or what was applied. */\nexport function logMigrations(\n\truns: readonly MigrationRun[],\n\tlog: (line: string) => void = logger.log,\n): void {\n\tfor (const { target, applied } of runs) {\n\t\tif (applied.length === 0) {\n\t\t\tlog(`🗄️ ${target.migrations}: up to date`);\n\t\t\tcontinue;\n\t\t}\n\t\tlog(`🗄️ ${target.migrations}: applied ${applied.length}`);\n\t\tfor (const name of applied) log(` ✓ ${name}`);\n\t}\n}\n\n/** One line per construct that has seeds, and each seed it ran. */\nexport function logSeeds(\n\truns: readonly SeedRun[],\n\tlog: (line: string) => void = logger.log,\n): void {\n\tfor (const { target, seeded } of runs) {\n\t\tif (seeded.length === 0) continue;\n\t\tlog(`🌱 ${target.seeds}: ran ${seeded.length}`);\n\t\tfor (const name of seeded) log(` ✓ ${name}`);\n\t}\n}\n\n/**\n * What `gkm dev` prints: one line per construct whose folder is ahead of its\n * history, and nothing when every folder is applied.\n *\n * Reported, never applied. A migration being written would otherwise run the\n * moment its file is saved, half-finished, against the database the developer\n * is working in.\n */\nexport async function reportPendingMigrations(\n\tworkspace: NormalizedWorkspace,\n\tenv: Readonly<Record<string, string>>,\n): Promise<void> {\n\tconst sources: Record<string, ConstructSource> = {};\n\tconst manifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\tsources,\n\t});\n\n\tconst pending = await pendingMigrations({\n\t\troot: workspace.root,\n\t\tmanifest,\n\t\tsources,\n\t\tenv,\n\t});\n\n\tfor (const { target, pending: names } of pending) {\n\t\tlogger.log(\n\t\t\t`⚠️ ${target.migrations}: ${names.length} pending (${names.join(', ')}) — run gkm migrate`,\n\t\t);\n\t}\n}\n\n/**\n * What `gkm dev --migrate` and `gkm dev --seed` do before the apps start:\n * migrate, and with `seed`, run the seeds after — once, at startup, never on a\n * save, for the reason {@link reportPendingMigrations} gives.\n *\n * A failure here is thrown, unlike the report's: the developer asked for the\n * database to be brought up to date, and apps started against one in an\n * unknown state would fail somewhere less obvious.\n */\nexport async function prepareDevDatabases(\n\tworkspace: NormalizedWorkspace,\n\tenv: Readonly<Record<string, string>>,\n\t{ seed = false }: { seed?: boolean } = {},\n): Promise<void> {\n\tconst sources: Record<string, ConstructSource> = {};\n\tconst manifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\tsources,\n\t});\n\tconst options = { root: workspace.root, manifest, sources, env };\n\n\tif (!seed) {\n\t\tlogMigrations(await migrateDatabases(options));\n\t\treturn;\n\t}\n\n\tconst { migrations, seeds } = await migrateAndSeed({\n\t\t...options,\n\t\tstage: workspace.stages.local,\n\t});\n\tlogMigrations(migrations);\n\tlogSeeds(seeds);\n}\n\nexport interface MigrationOptions {\n\t/** The construct the migration is for, by name. */\n\tconstruct: string;\n\t/** What the file is called, after its timestamp. */\n\tname?: string;\n\t/** The file's timestamp; the current time when absent. */\n\tnow?: Date;\n}\n\n/**\n * `gkm migration <construct> [name]` — write the construct's next migration.\n *\n * For a database, an empty `up`/`down` to fill in. For an auth server, what\n * its schema is missing, computed against its tenant once every committed\n * migration has been applied — so the file is only the change, and is\n * reviewed and committed like any other.\n */\nexport async function migrationCommand(\n\toptions: MigrationOptions,\n): Promise<string | undefined> {\n\tconst ready = await readyStage(process.cwd());\n\tconst targets = migrationTargets(ready.manifest);\n\tconst id = canonicalId(options.construct);\n\n\tconst auth = authConstruct(ready.sources, id);\n\tif (auth) {\n\t\tconst target = targets.find((t) => t.id === auth.databaseId);\n\t\tif (!target) {\n\t\t\tthrow new NoSuchMigrationTarget(auth.databaseId, ids(targets));\n\t\t}\n\t\treturn writeAuthMigration(ready, auth, target, options);\n\t}\n\n\tconst target = targets.find((t) => t.id === id);\n\tif (!target) {\n\t\tthrow new NoSuchMigrationTarget(options.construct, [\n\t\t\t...ids(targets),\n\t\t\t...authIds(ready.sources),\n\t\t]);\n\t}\n\tif (!options.name) throw new MigrationNeedsName(options.construct);\n\n\tconst file = await write(\n\t\tready.workspace.root,\n\t\ttarget,\n\t\t`${stamp(options.now)}_${slug(options.name)}.ts`,\n\t\tSTUB,\n\t);\n\tlogger.log(`📝 ${file}`);\n\treturn file;\n}\n\n/** What an auth construct offers `gkm migration`, duck-typed across copies. */\ninterface AuthConstruct {\n\tid: string;\n\tdatabaseId: string;\n\tpendingMigration(\n\t\toptions: ServiceRegisterOptions,\n\t): Promise<string | undefined>;\n}\n\nfunction authConstruct(\n\tsources: Readonly<Record<string, ConstructSource>>,\n\tid: string,\n): AuthConstruct | undefined {\n\tconst construct = sources[id]?.construct as\n\t\t| Partial<AuthConstruct>\n\t\t| undefined;\n\treturn construct &&\n\t\ttypeof construct.pendingMigration === 'function' &&\n\t\ttypeof construct.databaseId === 'string'\n\t\t? (construct as AuthConstruct)\n\t\t: undefined;\n}\n\nfunction authIds(sources: Readonly<Record<string, ConstructSource>>): string[] {\n\treturn Object.keys(sources).filter((id) => authConstruct(sources, id));\n}\n\nasync function writeAuthMigration(\n\tready: ReadyStage,\n\tauth: AuthConstruct,\n\ttarget: MigrationTarget,\n\toptions: MigrationOptions,\n): Promise<string | undefined> {\n\t// Against a tenant already at its committed migrations, so the comparison\n\t// finds only what the files do not yet say.\n\tawait migrateDatabases({\n\t\troot: ready.workspace.root,\n\t\tmanifest: ready.manifest,\n\t\tsources: ready.sources,\n\t\tenv: ready.env,\n\t\tonly: target.id,\n\t});\n\n\t// Its secret and URL come from the stage's secrets; the tenant's owner URL\n\t// from the reconcile above, which wins.\n\tconst stage = ready.workspace.stages.local;\n\tconst secrets = await loadSecretsForApp(\n\t\tawait secretsStoreFor(ready.workspace, stage),\n\t\tstage,\n\t);\n\tconst pending = await auth.pendingMigration({\n\t\tenvParser: new EnvironmentParser({ ...secrets, ...ready.env }),\n\t\tcontext: serviceContext,\n\t});\n\n\tif (!pending) {\n\t\tlogger.log(`✓ ${target.migrations}: ${auth.id} is up to date`);\n\t\treturn undefined;\n\t}\n\n\tconst file = await write(\n\t\tready.workspace.root,\n\t\ttarget,\n\t\t`${stamp(options.now)}_${slug(options.name ?? 'better_auth')}.sql`,\n\t\t`${pending.trim()}\\n`,\n\t);\n\tlogger.log(`📝 ${file}`);\n\treturn file;\n}\n\nconst STUB = `import type { Kysely } from 'kysely';\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n // await db.schema.createTable('…').addColumn(…).execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n // await db.schema.dropTable('…').execute();\n}\n`;\n\nasync function write(\n\troot: string,\n\ttarget: MigrationTarget,\n\tfile: string,\n\tcontent: string,\n): Promise<string> {\n\tconst folder = join(root, target.migrations);\n\tawait mkdir(folder, { recursive: true });\n\tawait writeFile(join(folder, file), content, { flag: 'wx' });\n\treturn `${target.migrations}/${file}`;\n}\n\nfunction ids(targets: readonly MigrationTarget[]): string[] {\n\treturn targets.map((target) => target.id);\n}\n"],"mappings":"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"}
package/dist/openapi.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_config = require("./config-DXnkCEq0.cjs");
4
- const require_OpenApiTsGenerator = require("./OpenApiTsGenerator-BTuUumsV.cjs");
4
+ const require_OpenApiTsGenerator = require("./OpenApiTsGenerator-BSrgw7B0.cjs");
5
5
  let node_path = require("node:path");
6
6
  let node_fs_promises = require("node:fs/promises");
7
7
  let _geekmidas_manifest = require("@geekmidas/manifest");
package/dist/openapi.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  import { l as loadWorkspaceConfig } from "./config-C5-EEdmr.mjs";
3
- import { n as EndpointGenerator, t as OpenApiTsGenerator } from "./OpenApiTsGenerator-CvVr1Ilz.mjs";
3
+ import { n as EndpointGenerator, t as OpenApiTsGenerator } from "./OpenApiTsGenerator-q5B0z8Xg.mjs";
4
4
  import { dirname, join, relative, resolve } from "node:path";
5
5
  import { mkdir, writeFile } from "node:fs/promises";
6
6
  import { kebabCase } from "@geekmidas/manifest";
@@ -1,5 +1,5 @@
1
1
  import { i as ConstructGenerator, t as discover } from "./discover-FrYvzkeC.mjs";
2
- import { n as EndpointGenerator, r as runtimeFor, t as OpenApiTsGenerator } from "./OpenApiTsGenerator-CvVr1Ilz.mjs";
2
+ import { n as EndpointGenerator, r as runtimeFor, t as OpenApiTsGenerator } from "./OpenApiTsGenerator-q5B0z8Xg.mjs";
3
3
  import { n as DEFAULT_CACHE, t as readFakes } from "./fakes-C9RiT6XG.mjs";
4
4
  import { dirname, extname, join, relative, resolve } from "node:path";
5
5
  import { existsSync, readFileSync } from "node:fs";
@@ -757,4 +757,4 @@ const TEST_READY_FILE = ".gkm/test-ready.json";
757
757
  //#endregion
758
758
  export { writeTestHarness as a, driversFor as c, TEST_MANIFEST_ENV as i, TEST_READY_FILE as n, SubscriberGenerator as o, DEFAULT_FACTORIES_DIR as r, cacheBackendsIn as s, TEST_READY_ENV as t };
759
759
 
760
- //# sourceMappingURL=ready-CIHpnUc5.mjs.map
760
+ //# sourceMappingURL=ready-DQIy21W5.mjs.map