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

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 (135) hide show
  1. package/dist/{HostingerProvider-DI_Pr2VE.mjs → HostingerProvider-B92qH4dC.mjs} +2 -2
  2. package/dist/{HostingerProvider-DI_Pr2VE.mjs.map → HostingerProvider-B92qH4dC.mjs.map} +1 -1
  3. package/dist/{HostingerProvider-CgDaD8VR.cjs → HostingerProvider-DyD1Hin8.cjs} +2 -2
  4. package/dist/{HostingerProvider-CgDaD8VR.cjs.map → HostingerProvider-DyD1Hin8.cjs.map} +1 -1
  5. package/dist/{LegacyStateStore-C7vFAxbu.mjs → LegacyStateStore-A8d2GdT7.mjs} +2 -2
  6. package/dist/{LegacyStateStore-C7vFAxbu.mjs.map → LegacyStateStore-A8d2GdT7.mjs.map} +1 -1
  7. package/dist/{LegacyStateStore-JbBVFp_2.cjs → LegacyStateStore-UHJEBLdn.cjs} +2 -2
  8. package/dist/{LegacyStateStore-JbBVFp_2.cjs.map → LegacyStateStore-UHJEBLdn.cjs.map} +1 -1
  9. package/dist/{LocalStateStore-DfUwQsL8.mjs → LocalStateStore-B51rnfgn.mjs} +2 -2
  10. package/dist/{LocalStateStore-DfUwQsL8.mjs.map → LocalStateStore-B51rnfgn.mjs.map} +1 -1
  11. package/dist/{LocalStateStore-DHj_-CQB.cjs → LocalStateStore-COW3Ldwa.cjs} +2 -2
  12. package/dist/{LocalStateStore-DHj_-CQB.cjs.map → LocalStateStore-COW3Ldwa.cjs.map} +1 -1
  13. package/dist/{S3StateStore-Dq6hHtzY.mjs → S3StateStore-CJewPhgz.mjs} +2 -2
  14. package/dist/{S3StateStore-Dq6hHtzY.mjs.map → S3StateStore-CJewPhgz.mjs.map} +1 -1
  15. package/dist/{S3StateStore-xqnpUrzy.cjs → S3StateStore-WUJGdRNL.cjs} +2 -2
  16. package/dist/{S3StateStore-xqnpUrzy.cjs.map → S3StateStore-WUJGdRNL.cjs.map} +1 -1
  17. package/dist/{SSMStateStore-5dBRwVlJ.cjs → SSMStateStore-Zejt42V8.cjs} +2 -2
  18. package/dist/{SSMStateStore-5dBRwVlJ.cjs.map → SSMStateStore-Zejt42V8.cjs.map} +1 -1
  19. package/dist/{SSMStateStore-SUmvAbLi.mjs → SSMStateStore-iK5UKp9d.mjs} +2 -2
  20. package/dist/{SSMStateStore-SUmvAbLi.mjs.map → SSMStateStore-iK5UKp9d.mjs.map} +1 -1
  21. package/dist/{bundler-DXATpO1T.cjs → bundler-CfH9frOS.cjs} +2 -2
  22. package/dist/{bundler-DXATpO1T.cjs.map → bundler-CfH9frOS.cjs.map} +1 -1
  23. package/dist/{bundler-B5VsKW-_.mjs → bundler-DN5r4x5b.mjs} +2 -2
  24. package/dist/{bundler-B5VsKW-_.mjs.map → bundler-DN5r4x5b.mjs.map} +1 -1
  25. package/dist/{cli-DQdrVkj2.cjs → cli-DOAUp80L.cjs} +12 -16
  26. package/dist/cli-DOAUp80L.cjs.map +1 -0
  27. package/dist/{cli-DEA_J3Sa.mjs → cli-DYwxmfsW.mjs} +10 -14
  28. package/dist/cli-DYwxmfsW.mjs.map +1 -0
  29. package/dist/{config-DXnkCEq0.cjs → config-BYh9S0cZ.cjs} +2 -2
  30. package/dist/config-BYh9S0cZ.cjs.map +1 -0
  31. package/dist/{config-C5-EEdmr.mjs → config-Bf4-bROQ.mjs} +2 -2
  32. package/dist/config-Bf4-bROQ.mjs.map +1 -0
  33. package/dist/config.cjs +2 -2
  34. package/dist/config.d.cts +2 -2
  35. package/dist/config.d.mts +2 -2
  36. package/dist/config.mjs +2 -2
  37. package/dist/define-CHBIEc5j.mjs +94 -0
  38. package/dist/define-CHBIEc5j.mjs.map +1 -0
  39. package/dist/define-Cv_QvDrg.cjs +141 -0
  40. package/dist/define-Cv_QvDrg.cjs.map +1 -0
  41. package/dist/deploy/public.cjs +28 -13
  42. package/dist/deploy/public.d.cts +38 -315
  43. package/dist/deploy/public.d.cts.map +1 -1
  44. package/dist/deploy/public.d.mts +38 -315
  45. package/dist/deploy/public.d.mts.map +1 -1
  46. package/dist/deploy/public.mjs +8 -3
  47. package/dist/{deploy-BJcLmVTc.mjs → deploy-B7jvUORo.mjs} +152 -532
  48. package/dist/deploy-B7jvUORo.mjs.map +1 -0
  49. package/dist/deploy-Cg9fae7a.mjs +133 -0
  50. package/dist/deploy-Cg9fae7a.mjs.map +1 -0
  51. package/dist/deploy-D8Qr-Xto.cjs +138 -0
  52. package/dist/deploy-D8Qr-Xto.cjs.map +1 -0
  53. package/dist/{deploy-BbJdgQ2b.cjs → deploy-DSaCYvcO.cjs} +168 -560
  54. package/dist/deploy-DSaCYvcO.cjs.map +1 -0
  55. package/dist/dokploy-CD9PY-3D.mjs +32 -0
  56. package/dist/dokploy-CD9PY-3D.mjs.map +1 -0
  57. package/dist/dokploy-Ey2foJLv.cjs +32 -0
  58. package/dist/dokploy-Ey2foJLv.cjs.map +1 -0
  59. package/dist/{encryption-DaA9gRXr.mjs → encryption-CVwvGbsL.mjs} +4 -4
  60. package/dist/{encryption-DaA9gRXr.mjs.map → encryption-CVwvGbsL.mjs.map} +1 -1
  61. package/dist/{encryption-ojzUo7-z.cjs → encryption-ChBk3hTj.cjs} +4 -4
  62. package/dist/{encryption-ojzUo7-z.cjs.map → encryption-ChBk3hTj.cjs.map} +1 -1
  63. package/dist/{github-DmCj2u95.mjs → github-BCB3_LBX.mjs} +3 -3
  64. package/dist/{github-DmCj2u95.mjs.map → github-BCB3_LBX.mjs.map} +1 -1
  65. package/dist/{github-Cq57poPj.cjs → github-OUgPDlqg.cjs} +3 -3
  66. package/dist/{github-Cq57poPj.cjs.map → github-OUgPDlqg.cjs.map} +1 -1
  67. package/dist/{index-UiWCaGzU.d.cts → index-BtXDZQuF.d.cts} +2 -2
  68. package/dist/{index-Da1-oa9W.d.mts.map → index-BtXDZQuF.d.cts.map} +1 -1
  69. package/dist/{index-Da1-oa9W.d.mts → index-DrKzpHl-.d.mts} +2 -2
  70. package/dist/{index-UiWCaGzU.d.cts.map → index-DrKzpHl-.d.mts.map} +1 -1
  71. package/dist/index.cjs +42 -41
  72. package/dist/index.cjs.map +1 -1
  73. package/dist/index.mjs +42 -41
  74. package/dist/index.mjs.map +1 -1
  75. package/dist/{migrate-5_J5JySj.mjs → migrate-BoHMqdCu.mjs} +4 -4
  76. package/dist/{migrate-5_J5JySj.mjs.map → migrate-BoHMqdCu.mjs.map} +1 -1
  77. package/dist/{migrate-BScRpCEx.cjs → migrate-dnaFMvOm.cjs} +4 -4
  78. package/dist/{migrate-BScRpCEx.cjs.map → migrate-dnaFMvOm.cjs.map} +1 -1
  79. package/dist/openapi.cjs +1 -1
  80. package/dist/openapi.d.cts +1 -1
  81. package/dist/openapi.d.mts +1 -1
  82. package/dist/openapi.mjs +1 -1
  83. package/dist/reconcile/public.d.cts +1 -1
  84. package/dist/reconcile/public.d.mts +1 -1
  85. package/dist/resolve-CFCIkm7m.mjs +170 -0
  86. package/dist/resolve-CFCIkm7m.mjs.map +1 -0
  87. package/dist/resolve-wbv66HjD.cjs +217 -0
  88. package/dist/resolve-wbv66HjD.cjs.map +1 -0
  89. package/dist/runtime-CMwtq0AL.cjs +163 -0
  90. package/dist/runtime-CMwtq0AL.cjs.map +1 -0
  91. package/dist/runtime-CXPlkZzs.d.mts +90 -0
  92. package/dist/runtime-CXPlkZzs.d.mts.map +1 -0
  93. package/dist/runtime-DbakJS8u.mjs +134 -0
  94. package/dist/runtime-DbakJS8u.mjs.map +1 -0
  95. package/dist/runtime-Dn_n-29S.d.cts +90 -0
  96. package/dist/runtime-Dn_n-29S.d.cts.map +1 -0
  97. package/dist/{sniffer-CQmJnNJM.cjs → sniffer-BsfPa-GA.cjs} +17 -14
  98. package/dist/sniffer-BsfPa-GA.cjs.map +1 -0
  99. package/dist/{sniffer-bdleks5l.mjs → sniffer-DHE2Nafd.mjs} +17 -14
  100. package/dist/sniffer-DHE2Nafd.mjs.map +1 -0
  101. package/dist/target/public.cjs +15 -0
  102. package/dist/target/public.d.cts +28 -0
  103. package/dist/target/public.d.cts.map +1 -0
  104. package/dist/target/public.d.mts +28 -0
  105. package/dist/target/public.d.mts.map +1 -0
  106. package/dist/target/public.mjs +4 -0
  107. package/dist/terminal-6oL0vANs.cjs +576 -0
  108. package/dist/terminal-6oL0vANs.cjs.map +1 -0
  109. package/dist/terminal-ByZNjuig.mjs +517 -0
  110. package/dist/terminal-ByZNjuig.mjs.map +1 -0
  111. package/dist/{types-efNLu9xu.d.cts → types-CfLKGfC_.d.mts} +624 -34
  112. package/dist/types-CfLKGfC_.d.mts.map +1 -0
  113. package/dist/{types-Bp1mVxG0.d.mts → types-DIA7Li-J.d.cts} +626 -34
  114. package/dist/types-DIA7Li-J.d.cts.map +1 -0
  115. package/dist/workspace/index.cjs +1 -1
  116. package/dist/workspace/index.d.cts +3 -3
  117. package/dist/workspace/index.d.mts +3 -3
  118. package/dist/workspace/index.mjs +1 -1
  119. package/dist/{workspace-B64Xk1Mb.cjs → workspace-B6zQO6RY.cjs} +229 -23
  120. package/dist/workspace-B6zQO6RY.cjs.map +1 -0
  121. package/dist/{workspace-Bp1xVaOz.mjs → workspace-BFe57es7.mjs} +229 -23
  122. package/dist/workspace-BFe57es7.mjs.map +1 -0
  123. package/package.json +23 -13
  124. package/dist/cli-DEA_J3Sa.mjs.map +0 -1
  125. package/dist/cli-DQdrVkj2.cjs.map +0 -1
  126. package/dist/config-C5-EEdmr.mjs.map +0 -1
  127. package/dist/config-DXnkCEq0.cjs.map +0 -1
  128. package/dist/deploy-BJcLmVTc.mjs.map +0 -1
  129. package/dist/deploy-BbJdgQ2b.cjs.map +0 -1
  130. package/dist/sniffer-CQmJnNJM.cjs.map +0 -1
  131. package/dist/sniffer-bdleks5l.mjs.map +0 -1
  132. package/dist/types-Bp1mVxG0.d.mts.map +0 -1
  133. package/dist/types-efNLu9xu.d.cts.map +0 -1
  134. package/dist/workspace-B64Xk1Mb.cjs.map +0 -1
  135. package/dist/workspace-Bp1xVaOz.mjs.map +0 -1
@@ -1,7 +1,7 @@
1
- import { K as reconcileWorkspace, W as constructGlobs, wt as loadSecretsForApp } from "./sniffer-bdleks5l.mjs";
1
+ import { K as reconcileWorkspace, W as constructGlobs, wt as loadSecretsForApp } from "./sniffer-DHE2Nafd.mjs";
2
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";
3
+ import { l as loadWorkspaceConfig } from "./config-Bf4-bROQ.mjs";
4
+ import { E as TEST_STAGE } from "./workspace-BFe57es7.mjs";
5
5
  import { n as secretsStoreFor } from "./store-BS8A7NcA.mjs";
6
6
  import { h as pendingMigrations, l as NoSuchMigrationTarget, m as migrateDatabases, n as slug, p as migrateAndSeed, r as stamp } from "./index.mjs";
7
7
  import { join } from "node:path";
@@ -262,4 +262,4 @@ function ids(targets) {
262
262
  //#endregion
263
263
  export { migrateAndSeed, migrateCommand, migrationCommand, prepareDevDatabases, reportPendingMigrations, seedCommand };
264
264
 
265
- //# sourceMappingURL=migrate-5_J5JySj.mjs.map
265
+ //# sourceMappingURL=migrate-BoHMqdCu.mjs.map
@@ -1 +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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"}
1
+ {"version":3,"file":"migrate-BoHMqdCu.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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"}
@@ -1,7 +1,7 @@
1
- const require_sniffer = require("./sniffer-CQmJnNJM.cjs");
1
+ const require_sniffer = require("./sniffer-BsfPa-GA.cjs");
2
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");
3
+ const require_config = require("./config-BYh9S0cZ.cjs");
4
+ const require_workspace = require("./workspace-B6zQO6RY.cjs");
5
5
  const require_store = require("./store-KvNQwYaB.cjs");
6
6
  const require_index = require("./index.cjs");
7
7
  let node_path = require("node:path");
@@ -267,4 +267,4 @@ exports.prepareDevDatabases = prepareDevDatabases;
267
267
  exports.reportPendingMigrations = reportPendingMigrations;
268
268
  exports.seedCommand = seedCommand;
269
269
 
270
- //# sourceMappingURL=migrate-BScRpCEx.cjs.map
270
+ //# sourceMappingURL=migrate-dnaFMvOm.cjs.map
@@ -1 +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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"}
1
+ {"version":3,"file":"migrate-dnaFMvOm.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,6 +1,6 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const require_config = require("./config-DXnkCEq0.cjs");
3
+ const require_config = require("./config-BYh9S0cZ.cjs");
4
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");
@@ -1,4 +1,4 @@
1
- import { a as Routes, i as OpenApiConfig } from "./types-efNLu9xu.cjs";
1
+ import { a as Routes, i as OpenApiConfig } from "./types-DIA7Li-J.cjs";
2
2
  import { Endpoint } from "@geekmidas/constructs/endpoints";
3
3
  //#region src/openapi.d.ts
4
4
  interface OpenAPIOptions {
@@ -1,4 +1,4 @@
1
- import { a as Routes, i as OpenApiConfig } from "./types-Bp1mVxG0.mjs";
1
+ import { a as Routes, i as OpenApiConfig } from "./types-CfLKGfC_.mjs";
2
2
  import { Endpoint } from "@geekmidas/constructs/endpoints";
3
3
  //#region src/openapi.d.ts
4
4
  interface OpenAPIOptions {
package/dist/openapi.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env -S npx tsx
2
- import { l as loadWorkspaceConfig } from "./config-C5-EEdmr.mjs";
2
+ import { l as loadWorkspaceConfig } from "./config-Bf4-bROQ.mjs";
3
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";
@@ -1,4 +1,4 @@
1
- import { n as EmailBackend, t as CacheBackend } from "../types-efNLu9xu.cjs";
1
+ import { n as EmailBackend, t as CacheBackend } from "../types-DIA7Li-J.cjs";
2
2
  import { ConstructManifest, Declaration, RouteInfo } from "@geekmidas/manifest";
3
3
  //#region src/reconcile/discover.d.ts
4
4
  /** The construct face discovery needs: an id, and the ability to declare. */
@@ -1,4 +1,4 @@
1
- import { n as EmailBackend, t as CacheBackend } from "../types-Bp1mVxG0.mjs";
1
+ import { n as EmailBackend, t as CacheBackend } from "../types-CfLKGfC_.mjs";
2
2
  import { ConstructManifest, Declaration, RouteInfo } from "@geekmidas/manifest";
3
3
  //#region src/reconcile/discover.d.ts
4
4
  /** The construct face discovery needs: an id, and the ability to declare. */
@@ -0,0 +1,170 @@
1
+ import { a as UnknownDeployTarget, c as configuredTarget, i as DeployTargetNotYetSupported, n as isDeployTarget, o as builtinTarget, s as configurableBuiltins } from "./define-CHBIEc5j.mjs";
2
+ import { a as importPackage, n as declaredRuntime } from "./runtime-DbakJS8u.mjs";
3
+ //#region src/target/provider.ts
4
+ /**
5
+ * `gkm deploy --provider`, from before there were targets.
6
+ *
7
+ * It named three things, and only one of them ever deployed a workspace:
8
+ * `dokploy` is now the target of that name; `docker` and `aws-lambda` were
9
+ * accepted and always failed, so they are gone with a pointer to what does
10
+ * the job.
11
+ */
12
+ /** What each removed provider's job is done with now. */
13
+ const REMOVED = {
14
+ docker: "Build images with `gkm docker`, or run the stage as one Docker Compose stack with `gkm compose`",
15
+ "aws-lambda": "Deploy to AWS with SST: set deploy: { default: 'sst' } and run `gkm build && sst deploy --stage <stage>`"
16
+ };
17
+ /** A `--provider` that no longer exists. */
18
+ var ProviderRemoved = class extends Error {
19
+ provider;
20
+ instead;
21
+ constructor(provider, instead) {
22
+ super(`--provider ${provider} has been removed. ${instead}.`);
23
+ this.provider = provider;
24
+ this.instead = instead;
25
+ this.name = "ProviderRemoved";
26
+ }
27
+ };
28
+ /** The deprecation `--provider` prints when it still means something. */
29
+ function providerDeprecation(provider) {
30
+ return `--provider is deprecated; use --target ${provider}.`;
31
+ }
32
+ /**
33
+ * The target a `--provider` value means. `warn` hears the deprecation; a
34
+ * provider that is gone, or a target not deployable yet, throws.
35
+ */
36
+ function targetForProvider(provider, warn) {
37
+ const removed = REMOVED[provider];
38
+ if (removed) throw new ProviderRemoved(provider, removed);
39
+ if (provider === "vercel" || provider === "cloudflare") throw new DeployTargetNotYetSupported(provider);
40
+ warn(providerDeprecation(provider));
41
+ return provider;
42
+ }
43
+ //#endregion
44
+ //#region src/target/resolve.ts
45
+ /**
46
+ * From a target's name to the target.
47
+ *
48
+ * In this order, the first that has the name wins:
49
+ *
50
+ * 1. the host's own targets — what a program calling `deploy()` passes
51
+ * 2. the built-ins — `dokploy`
52
+ * 3. `deploy.targets` in gkm.config.ts — a package, an object, or either
53
+ * with options
54
+ *
55
+ * Built-ins come before the config so that a dependency cannot take over
56
+ * `dokploy` by being listed under its name. A name nothing has is
57
+ * `UnknownDeployTarget`. There is deliberately no fourth step that guesses a
58
+ * package from the name (`acme` → `@acme/gkm-target`): an npm scope nobody
59
+ * has claimed can be claimed by anyone, and a deploy would then run their
60
+ * code with the stage's credentials. A target is installed and named.
61
+ */
62
+ /** A target package whose default export is not a target. */
63
+ var TargetPackageInvalid = class extends Error {
64
+ target;
65
+ packageName;
66
+ constructor(target, packageName) {
67
+ super(`deploy.targets.${target} names "${packageName}", whose default export is not a deploy target. A target package exports one as default: export default defineTarget({ … }) from '@geekmidas/cli/target'.`);
68
+ this.target = target;
69
+ this.packageName = packageName;
70
+ this.name = "TargetPackageInvalid";
71
+ }
72
+ };
73
+ /** A `deploy.targets` entry that is not a target, a package name or a pair. */
74
+ var TargetEntryInvalid = class extends Error {
75
+ target;
76
+ constructor(target) {
77
+ super(`deploy.targets.${target} must be a package name, a target object (defineTarget), or [package or target, options].`);
78
+ this.target = target;
79
+ this.name = "TargetEntryInvalid";
80
+ }
81
+ };
82
+ /** A target whose `runtime` is not the one its package declares. */
83
+ var TargetRuntimeMismatch = class extends Error {
84
+ target;
85
+ packageName;
86
+ declared;
87
+ actual;
88
+ constructor(target, packageName, declared, actual) {
89
+ super(`The deploy target "${target}" runs on "${actual}", but ${packageName}'s package.json declares gkm.runtime "${declared}". gkm dev and gkm build chose backends for "${declared}", so the two must agree — fix the package.`);
90
+ this.target = target;
91
+ this.packageName = packageName;
92
+ this.declared = declared;
93
+ this.actual = actual;
94
+ this.name = "TargetRuntimeMismatch";
95
+ }
96
+ };
97
+ /** Options a target's schema rejected. */
98
+ var InvalidTargetOptions = class extends Error {
99
+ target;
100
+ issues;
101
+ constructor(target, issues) {
102
+ const lines = issues.map((issue) => {
103
+ const path = (issue.path ?? []).map((segment) => typeof segment === "object" ? String(segment.key) : String(segment)).join(".");
104
+ return ` - ${path ? `${path}: ` : ""}${issue.message}`;
105
+ });
106
+ super(`The options for deploy target "${target}" are invalid — fix them in deploy.targets.${target} in gkm.config.ts:\n${lines.join("\n")}`);
107
+ this.target = target;
108
+ this.issues = issues;
109
+ this.name = "InvalidTargetOptions";
110
+ }
111
+ };
112
+ /** The built-ins `gkm deploy` deploys through, loaded only when used. */
113
+ const BUILTIN_LOADERS = { dokploy: async () => (await import("./dokploy-CD9PY-3D.mjs")).dokployTarget };
114
+ /** The target `name` means, in this workspace, for this host. */
115
+ async function resolveTarget(name, { workspace, host, stage }) {
116
+ if (host && Object.hasOwn(host, name)) return {
117
+ name,
118
+ target: host[name],
119
+ source: "host",
120
+ options: void 0
121
+ };
122
+ const builtin = builtinTarget(name);
123
+ if (builtin) {
124
+ const load = BUILTIN_LOADERS[name];
125
+ if (builtin.status !== "available" || !load) throw new DeployTargetNotYetSupported(name, builtin.instead?.(stage));
126
+ return {
127
+ name,
128
+ target: await load(),
129
+ source: "builtin",
130
+ options: void 0
131
+ };
132
+ }
133
+ const entry = configuredTarget(workspace.deploy, name);
134
+ if (entry === void 0) throw new UnknownDeployTarget(name, [...configurableBuiltins(), ...Object.keys(workspace.deploy?.targets ?? {})]);
135
+ const [implementation, options] = Array.isArray(entry) ? entry : [entry, void 0];
136
+ if (typeof implementation === "string") {
137
+ const declared = declaredRuntime(name, implementation, workspace.root);
138
+ const target = (await importPackage(name, implementation, workspace.root)).default;
139
+ if (!isDeployTarget(target)) throw new TargetPackageInvalid(name, implementation);
140
+ if (target.runtime !== declared) throw new TargetRuntimeMismatch(name, implementation, declared, target.runtime);
141
+ return {
142
+ name,
143
+ target,
144
+ source: "config",
145
+ options
146
+ };
147
+ }
148
+ if (!isDeployTarget(implementation)) throw new TargetEntryInvalid(name);
149
+ return {
150
+ name,
151
+ target: implementation,
152
+ source: "config",
153
+ options
154
+ };
155
+ }
156
+ /** The target's options, parsed by its schema. */
157
+ async function parseTargetOptions(resolved) {
158
+ const { name, target, options } = resolved;
159
+ if (!target.options) {
160
+ if (options !== void 0) throw new InvalidTargetOptions(name, [{ message: "this target takes no options" }]);
161
+ return;
162
+ }
163
+ const result = await target.options["~standard"].validate(options);
164
+ if (result.issues) throw new InvalidTargetOptions(name, result.issues);
165
+ return result.value;
166
+ }
167
+ //#endregion
168
+ export { parseTargetOptions as a, targetForProvider as c, TargetRuntimeMismatch as i, TargetEntryInvalid as n, resolveTarget as o, TargetPackageInvalid as r, ProviderRemoved as s, InvalidTargetOptions as t };
169
+
170
+ //# sourceMappingURL=resolve-CFCIkm7m.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-CFCIkm7m.mjs","names":[],"sources":["../src/target/provider.ts","../src/target/resolve.ts"],"sourcesContent":["/**\n * `gkm deploy --provider`, from before there were targets.\n *\n * It named three things, and only one of them ever deployed a workspace:\n * `dokploy` is now the target of that name; `docker` and `aws-lambda` were\n * accepted and always failed, so they are gone with a pointer to what does\n * the job.\n */\n\nimport { DeployTargetNotYetSupported } from './builtins';\n\n/** What each removed provider's job is done with now. */\nconst REMOVED: Record<string, string> = {\n\tdocker:\n\t\t'Build images with `gkm docker`, or run the stage as one Docker Compose stack with `gkm compose`',\n\t'aws-lambda':\n\t\t\"Deploy to AWS with SST: set deploy: { default: 'sst' } and run `gkm build && sst deploy --stage <stage>`\",\n};\n\n/** A `--provider` that no longer exists. */\nexport class ProviderRemoved extends Error {\n\tconstructor(\n\t\treadonly provider: string,\n\t\t/** What to use instead. */\n\t\treadonly instead: string,\n\t) {\n\t\tsuper(`--provider ${provider} has been removed. ${instead}.`);\n\t\tthis.name = 'ProviderRemoved';\n\t}\n}\n\n/** The deprecation `--provider` prints when it still means something. */\nexport function providerDeprecation(provider: string): string {\n\treturn `--provider is deprecated; use --target ${provider}.`;\n}\n\n/**\n * The target a `--provider` value means. `warn` hears the deprecation; a\n * provider that is gone, or a target not deployable yet, throws.\n */\nexport function targetForProvider(\n\tprovider: string,\n\twarn: (message: string) => void,\n): string {\n\tconst removed = REMOVED[provider];\n\tif (removed) throw new ProviderRemoved(provider, removed);\n\tif (provider === 'vercel' || provider === 'cloudflare') {\n\t\tthrow new DeployTargetNotYetSupported(provider);\n\t}\n\twarn(providerDeprecation(provider));\n\treturn provider;\n}\n","/**\n * From a target's name to the target.\n *\n * In this order, the first that has the name wins:\n *\n * 1. the host's own targets — what a program calling `deploy()` passes\n * 2. the built-ins — `dokploy`\n * 3. `deploy.targets` in gkm.config.ts — a package, an object, or either\n * with options\n *\n * Built-ins come before the config so that a dependency cannot take over\n * `dokploy` by being listed under its name. A name nothing has is\n * `UnknownDeployTarget`. There is deliberately no fourth step that guesses a\n * package from the name (`acme` → `@acme/gkm-target`): an npm scope nobody\n * has claimed can be claimed by anyone, and a deploy would then run their\n * code with the stage's credentials. A target is installed and named.\n */\n\nimport {\n\tbuiltinTarget,\n\tconfigurableBuiltins,\n\tconfiguredTarget,\n\tDeployTargetNotYetSupported,\n\tUnknownDeployTarget,\n} from './builtins';\nimport { isDeployTarget } from './define';\nimport { importPackage } from './package';\nimport { declaredRuntime } from './runtime';\nimport type {\n\tAnyDeployTarget,\n\tDeployRuntime,\n\tDeployTargetEntry,\n\tTargetOptionsIssue,\n} from './types';\n\n/** Where a target came from. */\nexport type TargetSourceKind = 'host' | 'builtin' | 'config';\n\nexport interface ResolvedTarget {\n\t/** The name it was asked for by. */\n\tname: string;\n\ttarget: AnyDeployTarget;\n\tsource: TargetSourceKind;\n\t/** Its options as written in the config, before its schema parsed them. */\n\toptions: unknown;\n}\n\nexport interface ResolveTargetOptions {\n\t/** The workspace whose `deploy.targets` is consulted. */\n\tworkspace: {\n\t\troot: string;\n\t\tdeploy?: { targets?: Record<string, DeployTargetEntry> } | undefined;\n\t};\n\t/** The host's targets, consulted first. */\n\thost?: Record<string, AnyDeployTarget>;\n\t/** The stage being deployed, for what to do instead of a refused target. */\n\tstage: string;\n}\n\n/** A target package whose default export is not a target. */\nexport class TargetPackageInvalid extends Error {\n\tconstructor(\n\t\treadonly target: string,\n\t\treadonly packageName: string,\n\t) {\n\t\tsuper(\n\t\t\t`deploy.targets.${target} names \"${packageName}\", whose default export is not a deploy target. A target package exports one as default: export default defineTarget({ … }) from '@geekmidas/cli/target'.`,\n\t\t);\n\t\tthis.name = 'TargetPackageInvalid';\n\t}\n}\n\n/** A `deploy.targets` entry that is not a target, a package name or a pair. */\nexport class TargetEntryInvalid extends Error {\n\tconstructor(readonly target: string) {\n\t\tsuper(\n\t\t\t`deploy.targets.${target} must be a package name, a target object (defineTarget), or [package or target, options].`,\n\t\t);\n\t\tthis.name = 'TargetEntryInvalid';\n\t}\n}\n\n/** A target whose `runtime` is not the one its package declares. */\nexport class TargetRuntimeMismatch extends Error {\n\tconstructor(\n\t\treadonly target: string,\n\t\treadonly packageName: string,\n\t\treadonly declared: DeployRuntime,\n\t\treadonly actual: DeployRuntime,\n\t) {\n\t\tsuper(\n\t\t\t`The deploy target \"${target}\" runs on \"${actual}\", but ${packageName}'s package.json declares gkm.runtime \"${declared}\". gkm dev and gkm build chose backends for \"${declared}\", so the two must agree — fix the package.`,\n\t\t);\n\t\tthis.name = 'TargetRuntimeMismatch';\n\t}\n}\n\n/** Options a target's schema rejected. */\nexport class InvalidTargetOptions extends Error {\n\tconstructor(\n\t\treadonly target: string,\n\t\treadonly issues: readonly TargetOptionsIssue[],\n\t) {\n\t\tconst lines = issues.map((issue) => {\n\t\t\tconst path = (issue.path ?? [])\n\t\t\t\t.map((segment) =>\n\t\t\t\t\ttypeof segment === 'object' ? String(segment.key) : String(segment),\n\t\t\t\t)\n\t\t\t\t.join('.');\n\t\t\treturn ` - ${path ? `${path}: ` : ''}${issue.message}`;\n\t\t});\n\t\tsuper(\n\t\t\t`The options for deploy target \"${target}\" are invalid — fix them in deploy.targets.${target} in gkm.config.ts:\\n${lines.join('\\n')}`,\n\t\t);\n\t\tthis.name = 'InvalidTargetOptions';\n\t}\n}\n\n/** The built-ins `gkm deploy` deploys through, loaded only when used. */\nconst BUILTIN_LOADERS: Record<string, () => Promise<AnyDeployTarget>> = {\n\tdokploy: async () => (await import('./dokploy')).dokployTarget,\n};\n\n/** The target `name` means, in this workspace, for this host. */\nexport async function resolveTarget(\n\tname: string,\n\t{ workspace, host, stage }: ResolveTargetOptions,\n): Promise<ResolvedTarget> {\n\tif (host && Object.hasOwn(host, name)) {\n\t\treturn { name, target: host[name]!, source: 'host', options: undefined };\n\t}\n\n\tconst builtin = builtinTarget(name);\n\tif (builtin) {\n\t\tconst load = BUILTIN_LOADERS[name];\n\t\tif (builtin.status !== 'available' || !load) {\n\t\t\tthrow new DeployTargetNotYetSupported(name, builtin.instead?.(stage));\n\t\t}\n\t\treturn {\n\t\t\tname,\n\t\t\ttarget: await load(),\n\t\t\tsource: 'builtin',\n\t\t\toptions: undefined,\n\t\t};\n\t}\n\n\tconst entry = configuredTarget(workspace.deploy, name);\n\tif (entry === undefined) {\n\t\tthrow new UnknownDeployTarget(name, [\n\t\t\t...configurableBuiltins(),\n\t\t\t...Object.keys(workspace.deploy?.targets ?? {}),\n\t\t]);\n\t}\n\n\tconst [implementation, options] = Array.isArray(entry)\n\t\t? (entry as readonly [unknown, unknown])\n\t\t: [entry, undefined];\n\n\tif (typeof implementation === 'string') {\n\t\t// Before the import: a package that does not say where it runs is\n\t\t// refused without running any of its code.\n\t\tconst declared = declaredRuntime(name, implementation, workspace.root);\n\t\tconst loaded = await importPackage(name, implementation, workspace.root);\n\t\tconst target = loaded.default;\n\t\tif (!isDeployTarget(target)) {\n\t\t\tthrow new TargetPackageInvalid(name, implementation);\n\t\t}\n\t\tif (target.runtime !== declared) {\n\t\t\tthrow new TargetRuntimeMismatch(\n\t\t\t\tname,\n\t\t\t\timplementation,\n\t\t\t\tdeclared,\n\t\t\t\ttarget.runtime,\n\t\t\t);\n\t\t}\n\t\treturn { name, target, source: 'config', options };\n\t}\n\n\tif (!isDeployTarget(implementation)) throw new TargetEntryInvalid(name);\n\treturn { name, target: implementation, source: 'config', options };\n}\n\n/** The target's options, parsed by its schema. */\nexport async function parseTargetOptions(\n\tresolved: ResolvedTarget,\n): Promise<unknown> {\n\tconst { name, target, options } = resolved;\n\tif (!target.options) {\n\t\tif (options !== undefined) {\n\t\t\tthrow new InvalidTargetOptions(name, [\n\t\t\t\t{ message: 'this target takes no options' },\n\t\t\t]);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tconst result = await target.options['~standard'].validate(options);\n\tif (result.issues) throw new InvalidTargetOptions(name, result.issues);\n\treturn result.value;\n}\n"],"mappings":"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"}