@databricks-solutions/lakebase-scm-utils 0.2.51 → 0.3.1

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 (79) hide show
  1. package/README.md +10 -4
  2. package/dist/scripts/index.cjs +2813 -2244
  3. package/dist/scripts/index.d.cts +1 -1
  4. package/dist/scripts/index.d.ts +1 -1
  5. package/dist/scripts/index.js +857 -298
  6. package/dist/scripts/lakebase/ci-resolve-branch.cli.cjs +2460 -2030
  7. package/dist/scripts/lakebase/ci-resolve-branch.cli.js +508 -81
  8. package/dist/scripts/lakebase/collapse-heads.cli.cjs +2429 -2007
  9. package/dist/scripts/lakebase/collapse-heads.cli.js +477 -58
  10. package/dist/scripts/lakebase/detect-language.cli.cjs +2441 -2016
  11. package/dist/scripts/lakebase/detect-language.cli.js +489 -67
  12. package/dist/scripts/lakebase/doctor.cli.cjs +2484 -2040
  13. package/dist/scripts/lakebase/doctor.cli.js +532 -91
  14. package/dist/scripts/lakebase/get-connection.cli.cjs +2 -2
  15. package/dist/scripts/lakebase/get-connection.cli.js +2 -2
  16. package/dist/scripts/lakebase/index.cjs +2810 -2205
  17. package/dist/scripts/lakebase/index.d.cts +59 -10
  18. package/dist/scripts/lakebase/index.d.ts +59 -10
  19. package/dist/scripts/lakebase/index.js +851 -256
  20. package/dist/scripts/lakebase/infra-runner.cli.cjs +2435 -2013
  21. package/dist/scripts/lakebase/infra-runner.cli.js +483 -64
  22. package/dist/scripts/lakebase/new-migration.cli.cjs +2429 -2007
  23. package/dist/scripts/lakebase/new-migration.cli.js +477 -58
  24. package/dist/scripts/lakebase/schema-diff.cli.cjs +2 -2
  25. package/dist/scripts/lakebase/schema-diff.cli.js +2 -2
  26. package/dist/scripts/lakebase/schema-migrate.cli.cjs +2461 -2029
  27. package/dist/scripts/lakebase/schema-migrate.cli.js +509 -80
  28. package/dist/scripts/lakebase/scm-claim-feature.cli.cjs +2460 -2030
  29. package/dist/scripts/lakebase/scm-claim-feature.cli.js +508 -81
  30. package/dist/scripts/lakebase/scm-doctor.cli.cjs +2486 -2056
  31. package/dist/scripts/lakebase/scm-doctor.cli.js +537 -107
  32. package/dist/scripts/lakebase/scm-merge.cli.cjs +2443 -2021
  33. package/dist/scripts/lakebase/scm-merge.cli.js +494 -72
  34. package/dist/scripts/lakebase/scm-reconcile-tier.cli.cjs +2464 -2034
  35. package/dist/scripts/lakebase/scm-reconcile-tier.cli.js +511 -84
  36. package/dist/scripts/util/index.cjs +2 -2
  37. package/dist/scripts/util/index.js +2 -2
  38. package/package.json +1 -1
  39. package/scripts/lakebase/adapters/efcore-adapter.ts +158 -0
  40. package/scripts/lakebase/adapters/flyway-adapter.ts +8 -5
  41. package/scripts/lakebase/client-lockfile.ts +102 -0
  42. package/scripts/lakebase/collapse-heads.cli.ts +1 -1
  43. package/scripts/lakebase/create-project.ts +25 -2
  44. package/scripts/lakebase/detect-language.cli.ts +2 -2
  45. package/scripts/lakebase/doctor.ts +29 -0
  46. package/scripts/lakebase/dotnet-project.ts +112 -0
  47. package/scripts/lakebase/index.ts +1 -0
  48. package/scripts/lakebase/migration-layout.ts +43 -2
  49. package/scripts/lakebase/new-migration.cli.ts +1 -1
  50. package/scripts/lakebase/scaffold-language.ts +23 -2
  51. package/scripts/lakebase/scaffold.ts +4 -1
  52. package/scripts/lakebase/schema-migrate-runners/efcore.ts +399 -0
  53. package/scripts/lakebase/schema-migrate-runners/flyway.ts +6 -4
  54. package/scripts/lakebase/schema-migrate.cli.ts +3 -1
  55. package/scripts/lakebase/schema-migrate.ts +24 -10
  56. package/templates/project/common/.github/workflows/merge.yml +13 -1
  57. package/templates/project/common/.github/workflows/pr.yml +25 -5
  58. package/templates/project/common/scripts/flyway-migrate.sh +54 -1
  59. package/templates/project/common/scripts/prepare-schema-diff.sh +5 -0
  60. package/templates/project/common/scripts/run-dev.sh +10 -1
  61. package/templates/project/common/scripts/run-tests.sh +6 -1
  62. package/templates/project/dotnet/.gitignore.extra +8 -0
  63. package/templates/project/dotnet/App.sln +56 -0
  64. package/templates/project/dotnet/Makefile +13 -0
  65. package/templates/project/dotnet/global.json +6 -0
  66. package/templates/project/dotnet/src/App/Data/AppDbContext.cs +12 -0
  67. package/templates/project/dotnet/src/App/Lakebase/DotEnv.cs +48 -0
  68. package/templates/project/dotnet/src/App/Lakebase/LakebaseConnection.cs +225 -0
  69. package/templates/project/dotnet/src/App/Program.cs +34 -0
  70. package/templates/project/dotnet/src/App/appsettings.json +9 -0
  71. package/templates/project/dotnet/tests/App.Tests/App.Tests.csproj +25 -0
  72. package/templates/project/dotnet/tests/App.Tests/HealthTests.cs +18 -0
  73. package/templates/project/dotnet/tests/App.Tests/OpenApiTests.cs +23 -0
  74. package/templates/project/dotnet-efcore/.config/dotnet-tools.json +13 -0
  75. package/templates/project/dotnet-efcore/src/App/App.csproj +22 -0
  76. package/templates/project/dotnet-efcore/src/App/Data/DesignTimeDbContextFactory.cs +24 -0
  77. package/templates/project/dotnet-efcore/src/App/Migrations/README.md +23 -0
  78. package/templates/project/dotnet-flyway/db/migrations/README.md +23 -0
  79. package/templates/project/dotnet-flyway/src/App/App.csproj +17 -0
@@ -2581,7 +2581,7 @@ paginateRest.VERSION = VERSION5;
2581
2581
 
2582
2582
  // node_modules/@octokit/plugin-paginate-graphql/dist-bundle/index.js
2583
2583
  init_esm_shims();
2584
- var generateMessage = (path34, cursorValue) => `The cursor at "${path34.join(
2584
+ var generateMessage = (path38, cursorValue) => `The cursor at "${path38.join(
2585
2585
  ","
2586
2586
  )}" did not change its value "${cursorValue}" after a page transition. Please make sure your that your query is set up correctly.`;
2587
2587
  var MissingCursorChange = class extends Error {
@@ -2622,9 +2622,9 @@ function findPaginatedResourcePath(responseData) {
2622
2622
  }
2623
2623
  return paginatedResourcePath;
2624
2624
  }
2625
- var deepFindPathToProperty = (object, searchProp, path34 = []) => {
2625
+ var deepFindPathToProperty = (object, searchProp, path38 = []) => {
2626
2626
  for (const key of Object.keys(object)) {
2627
- const currentPath = [...path34, key];
2627
+ const currentPath = [...path38, key];
2628
2628
  const currentValue = object[key];
2629
2629
  if (isObject(currentValue)) {
2630
2630
  if (currentValue.hasOwnProperty(searchProp)) {
@@ -2642,12 +2642,12 @@ var deepFindPathToProperty = (object, searchProp, path34 = []) => {
2642
2642
  }
2643
2643
  return [];
2644
2644
  };
2645
- var get = (object, path34) => {
2646
- return path34.reduce((current, nextProperty) => current[nextProperty], object);
2645
+ var get = (object, path38) => {
2646
+ return path38.reduce((current, nextProperty) => current[nextProperty], object);
2647
2647
  };
2648
- var set = (object, path34, mutator) => {
2649
- const lastProperty = path34[path34.length - 1];
2650
- const parentPath = [...path34].slice(0, -1);
2648
+ var set = (object, path38, mutator) => {
2649
+ const lastProperty = path38[path38.length - 1];
2650
+ const parentPath = [...path38].slice(0, -1);
2651
2651
  const parent = get(object, parentPath);
2652
2652
  if (typeof mutator === "function") {
2653
2653
  parent[lastProperty] = mutator(parent[lastProperty]);
@@ -2699,22 +2699,22 @@ var mergeResponses = (response1, response2) => {
2699
2699
  if (Object.keys(response1).length === 0) {
2700
2700
  return Object.assign(response1, response2);
2701
2701
  }
2702
- const path34 = findPaginatedResourcePath(response1);
2703
- const nodesPath = [...path34, "nodes"];
2702
+ const path38 = findPaginatedResourcePath(response1);
2703
+ const nodesPath = [...path38, "nodes"];
2704
2704
  const newNodes = get(response2, nodesPath);
2705
2705
  if (newNodes) {
2706
2706
  set(response1, nodesPath, (values) => {
2707
2707
  return [...values, ...newNodes];
2708
2708
  });
2709
2709
  }
2710
- const edgesPath = [...path34, "edges"];
2710
+ const edgesPath = [...path38, "edges"];
2711
2711
  const newEdges = get(response2, edgesPath);
2712
2712
  if (newEdges) {
2713
2713
  set(response1, edgesPath, (values) => {
2714
2714
  return [...values, ...newEdges];
2715
2715
  });
2716
2716
  }
2717
- const pageInfoPath = [...path34, "pageInfo"];
2717
+ const pageInfoPath = [...path38, "pageInfo"];
2718
2718
  set(response1, pageInfoPath, get(response2, pageInfoPath));
2719
2719
  return response1;
2720
2720
  };
@@ -5135,7 +5135,7 @@ var triggers_notification_paths_default = [
5135
5135
  ];
5136
5136
  function routeMatcher(paths) {
5137
5137
  const regexes = paths.map(
5138
- (path34) => path34.split("/").map((c) => c.startsWith("{") ? "(?:.+?)" : c).join("/")
5138
+ (path38) => path38.split("/").map((c) => c.startsWith("{") ? "(?:.+?)" : c).join("/")
5139
5139
  );
5140
5140
  const regex2 = `^(?:${regexes.map((r) => `(?:${r})`).join("|")})[^/]*$`;
5141
5141
  return new RegExp(regex2, "i");
@@ -8715,9 +8715,9 @@ function asBranchUid(s) {
8715
8715
  }
8716
8716
  return s;
8717
8717
  }
8718
- function branchNameFromResourcePath(path34) {
8719
- if (!path34.includes("/branches/")) return null;
8720
- const leaf = path34.split("/branches/").pop();
8718
+ function branchNameFromResourcePath(path38) {
8719
+ if (!path38.includes("/branches/")) return null;
8720
+ const leaf = path38.split("/branches/").pop();
8721
8721
  if (!leaf) return null;
8722
8722
  try {
8723
8723
  return asBranchName(leaf);
@@ -9725,8 +9725,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
9725
9725
  var cached;
9726
9726
  function substrateSelfVersion() {
9727
9727
  if (cached !== void 0) return cached;
9728
- if ("0.2.51".length > 0) {
9729
- cached = "0.2.51";
9728
+ if ("0.3.1".length > 0) {
9729
+ cached = "0.3.1";
9730
9730
  return cached;
9731
9731
  }
9732
9732
  cached = "unknown";
@@ -11314,16 +11314,16 @@ function isAllSchemas(schema) {
11314
11314
  }
11315
11315
  function buildSchemaQuery(schema) {
11316
11316
  const cols = "c.table_schema, c.table_name, c.column_name, c.data_type";
11317
- const join36 = "FROM information_schema.columns c JOIN pg_tables t ON c.table_name = t.tablename AND c.table_schema = t.schemaname ";
11317
+ const join38 = "FROM information_schema.columns c JOIN pg_tables t ON c.table_name = t.tablename AND c.table_schema = t.schemaname ";
11318
11318
  if (isAllSchemas(schema)) {
11319
11319
  return {
11320
- text: `SELECT ${cols} ` + join36 + `WHERE ${SYSTEM_SCHEMA_FILTER} ORDER BY c.table_schema, c.table_name, c.ordinal_position`,
11320
+ text: `SELECT ${cols} ` + join38 + `WHERE ${SYSTEM_SCHEMA_FILTER} ORDER BY c.table_schema, c.table_name, c.ordinal_position`,
11321
11321
  values: []
11322
11322
  };
11323
11323
  }
11324
11324
  const one = (schema ?? "").trim() || "public";
11325
11325
  return {
11326
- text: `SELECT ${cols} ` + join36 + "WHERE c.table_schema = $1 ORDER BY c.table_name, c.ordinal_position",
11326
+ text: `SELECT ${cols} ` + join38 + "WHERE c.table_schema = $1 ORDER BY c.table_name, c.ordinal_position",
11327
11327
  values: [one]
11328
11328
  };
11329
11329
  }
@@ -11483,8 +11483,8 @@ async function withLakebaseRollback(opts, fn) {
11483
11483
 
11484
11484
  // scripts/lakebase/create-project.ts
11485
11485
  init_esm_shims();
11486
- import * as fs18 from "fs";
11487
- import * as path18 from "path";
11486
+ import * as fs19 from "fs";
11487
+ import * as path19 from "path";
11488
11488
 
11489
11489
  // scripts/lakebase/project-verify.ts
11490
11490
  init_esm_shims();
@@ -11988,6 +11988,20 @@ async function deployLanguageProject(args) {
11988
11988
  return;
11989
11989
  }
11990
11990
  const templatesDir = args.templatesDir ?? findTemplatesDir2();
11991
+ if (args.language === "dotnet") {
11992
+ const tool = args.migrationTool ?? "efcore";
11993
+ if (tool !== "efcore" && tool !== "flyway") {
11994
+ throw new Error(`Unsupported .NET migration tool: ${String(tool)} (expected efcore or flyway)`);
11995
+ }
11996
+ const base = path10.join(templatesDir, "dotnet");
11997
+ const overlay = path10.join(templatesDir, `dotnet-${tool}`);
11998
+ if (!fs11.existsSync(base) || !fs11.existsSync(overlay)) {
11999
+ throw new Error(`No template found for language: dotnet (${tool})`);
12000
+ }
12001
+ copyDirSubstituted(base, args.targetDir, { projectName: args.projectName });
12002
+ copyDirSubstituted(overlay, args.targetDir, { projectName: args.projectName });
12003
+ return;
12004
+ }
11991
12005
  const langSrc = path10.join(templatesDir, args.language);
11992
12006
  if (!fs11.existsSync(langSrc)) {
11993
12007
  throw new Error(`No template found for language: ${args.language}`);
@@ -12407,6 +12421,7 @@ async function scaffoldAll(args) {
12407
12421
  targetDir: args.targetDir,
12408
12422
  language,
12409
12423
  projectName,
12424
+ migrationTool: args.migrationTool,
12410
12425
  templatesDir: args.templatesDir,
12411
12426
  initializrClient: args.initializrClient,
12412
12427
  report
@@ -12739,18 +12754,100 @@ function enableE2eForProject(args) {
12739
12754
  };
12740
12755
  }
12741
12756
 
12742
- // scripts/lakebase/enable-infra.ts
12757
+ // scripts/lakebase/client-lockfile.ts
12743
12758
  init_esm_shims();
12744
12759
  import * as fs15 from "fs";
12745
12760
  import * as path14 from "path";
12761
+
12762
+ // scripts/util/proxy-env.ts
12763
+ init_esm_shims();
12764
+ var PROXY_ENV_KEYS = [
12765
+ "HTTP_PROXY",
12766
+ "HTTPS_PROXY",
12767
+ "NO_PROXY",
12768
+ "http_proxy",
12769
+ "https_proxy",
12770
+ "no_proxy",
12771
+ "npm_config_proxy",
12772
+ "npm_config_https_proxy",
12773
+ "npm_config_no_proxy",
12774
+ "npm_config_registry"
12775
+ ];
12776
+ function proxyEnvSubset(source = process.env) {
12777
+ const out = {};
12778
+ for (const key of PROXY_ENV_KEYS) {
12779
+ const v = source[key];
12780
+ if (v !== void 0 && v !== "") {
12781
+ out[key] = v;
12782
+ }
12783
+ }
12784
+ return out;
12785
+ }
12786
+ function withProxyEnv(base = {}) {
12787
+ const out = {};
12788
+ const inherited = proxyEnvSubset();
12789
+ for (const [k, v] of Object.entries(inherited)) {
12790
+ out[k] = v;
12791
+ }
12792
+ for (const [k, v] of Object.entries(base)) {
12793
+ if (v !== void 0) out[k] = v;
12794
+ }
12795
+ return out;
12796
+ }
12797
+
12798
+ // scripts/lakebase/client-lockfile.ts
12799
+ var PROXY_HOST = "npm-proxy.cloud.databricks.com";
12800
+ var PUBLIC_HOST = "registry.npmjs.org";
12801
+ function normalizeLockfileRegistry(lockText) {
12802
+ let swaps = 0;
12803
+ const text = lockText.replace(new RegExp(PROXY_HOST.replace(/\./g, "\\."), "g"), () => {
12804
+ swaps++;
12805
+ return PUBLIC_HOST;
12806
+ });
12807
+ return { text, swaps };
12808
+ }
12809
+ async function ensureClientLockfile(args) {
12810
+ const clientDir = path14.join(args.projectDir, args.clientDir ?? "client");
12811
+ const pkg = path14.join(clientDir, "package.json");
12812
+ if (!fs15.existsSync(pkg)) {
12813
+ return { generated: false, swaps: 0, reason: "no client package.json" };
12814
+ }
12815
+ const lockPath = path14.join(clientDir, "package-lock.json");
12816
+ try {
12817
+ await exec2("npm install --package-lock-only", {
12818
+ cwd: clientDir,
12819
+ env: withProxyEnv(),
12820
+ timeout: 18e4
12821
+ });
12822
+ } catch (err) {
12823
+ const msg = err instanceof Error ? err.message : String(err);
12824
+ return { generated: fs15.existsSync(lockPath), swaps: 0, reason: `npm resolve failed: ${msg}` };
12825
+ }
12826
+ if (!fs15.existsSync(lockPath)) {
12827
+ return { generated: false, swaps: 0, reason: "npm wrote no package-lock.json" };
12828
+ }
12829
+ try {
12830
+ const { text, swaps } = normalizeLockfileRegistry(fs15.readFileSync(lockPath, "utf8"));
12831
+ if (swaps > 0) fs15.writeFileSync(lockPath, text);
12832
+ return { generated: true, swaps };
12833
+ } catch (err) {
12834
+ const msg = err instanceof Error ? err.message : String(err);
12835
+ return { generated: true, swaps: 0, reason: `lock normalize failed: ${msg}` };
12836
+ }
12837
+ }
12838
+
12839
+ // scripts/lakebase/enable-infra.ts
12840
+ init_esm_shims();
12841
+ import * as fs16 from "fs";
12842
+ import * as path15 from "path";
12746
12843
  var RUN_TESTS_INFRA_MARKER = "# Run Lakebase [Infra]-tag suite when wired";
12747
12844
  function addInfraToPackageJson(args) {
12748
- const pkgPath = path14.join(args.projectDir, "package.json");
12749
- if (!fs15.existsSync(pkgPath)) {
12845
+ const pkgPath = path15.join(args.projectDir, "package.json");
12846
+ if (!fs16.existsSync(pkgPath)) {
12750
12847
  return { patched: false, scriptAdded: false };
12751
12848
  }
12752
12849
  const scriptValue = args.scriptValue ?? "npx --yes lakebase-infra-runner";
12753
- const raw = fs15.readFileSync(pkgPath, "utf8");
12850
+ const raw = fs16.readFileSync(pkgPath, "utf8");
12754
12851
  const pkg = JSON.parse(raw);
12755
12852
  const scripts = pkg.scripts ?? {};
12756
12853
  let scriptAdded = false;
@@ -12761,16 +12858,16 @@ function addInfraToPackageJson(args) {
12761
12858
  pkg.scripts = scripts;
12762
12859
  if (scriptAdded) {
12763
12860
  const trailing = raw.endsWith("\n") ? "\n" : "";
12764
- fs15.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + trailing, "utf8");
12861
+ fs16.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + trailing, "utf8");
12765
12862
  }
12766
12863
  return { patched: true, scriptAdded };
12767
12864
  }
12768
12865
  function addInfraToRunTestsScript(args) {
12769
- const scriptPath = path14.join(args.projectDir, "scripts", "run-tests.sh");
12770
- if (!fs15.existsSync(scriptPath)) {
12866
+ const scriptPath = path15.join(args.projectDir, "scripts", "run-tests.sh");
12867
+ if (!fs16.existsSync(scriptPath)) {
12771
12868
  return { patched: false, inserted: false };
12772
12869
  }
12773
- const original = fs15.readFileSync(scriptPath, "utf8");
12870
+ const original = fs16.readFileSync(scriptPath, "utf8");
12774
12871
  if (original.includes(RUN_TESTS_INFRA_MARKER)) {
12775
12872
  return { patched: true, inserted: false };
12776
12873
  }
@@ -12786,7 +12883,7 @@ function addInfraToRunTestsScript(args) {
12786
12883
  "fi",
12787
12884
  ""
12788
12885
  ].join("\n");
12789
- fs15.writeFileSync(scriptPath, trimmed + block, "utf8");
12886
+ fs16.writeFileSync(scriptPath, trimmed + block, "utf8");
12790
12887
  return { patched: true, inserted: true };
12791
12888
  }
12792
12889
  function enableInfraForProject(args) {
@@ -12800,9 +12897,9 @@ function enableInfraForProject(args) {
12800
12897
 
12801
12898
  // scripts/lakebase/runner-setup.ts
12802
12899
  init_esm_shims();
12803
- import * as fs16 from "fs";
12900
+ import * as fs17 from "fs";
12804
12901
  import * as os from "os";
12805
- import * as path15 from "path";
12902
+ import * as path16 from "path";
12806
12903
  import * as cp4 from "child_process";
12807
12904
  import * as tar from "tar";
12808
12905
  import findJavaHome from "find-java-home";
@@ -12813,28 +12910,28 @@ var RUNNER_OS = process.platform === "darwin" ? "osx" : "linux";
12813
12910
  var RUNNER_ARCHIVE = `actions-runner-${RUNNER_OS}-${RUNNER_ARCH}-${RUNNER_VERSION}.tar.gz`;
12814
12911
  var RUNNER_URL = `https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/${RUNNER_ARCHIVE}`;
12815
12912
  function cacheDir() {
12816
- return path15.join(os.homedir(), ".cache", "github-actions-runner");
12913
+ return path16.join(os.homedir(), ".cache", "github-actions-runner");
12817
12914
  }
12818
12915
  function runnersDir() {
12819
- return path15.join(os.homedir(), ".lakebase", "runners");
12916
+ return path16.join(os.homedir(), ".lakebase", "runners");
12820
12917
  }
12821
12918
  function runnerDir(projectName) {
12822
- return path15.join(runnersDir(), projectName);
12919
+ return path16.join(runnersDir(), projectName);
12823
12920
  }
12824
12921
  function runnerName(projectName) {
12825
12922
  return `lakebase-${projectName}`;
12826
12923
  }
12827
12924
  async function ensureCachedArchive() {
12828
12925
  const dir = cacheDir();
12829
- fs16.mkdirSync(dir, { recursive: true });
12830
- const cachedPath = path15.join(dir, RUNNER_ARCHIVE);
12831
- if (fs16.existsSync(cachedPath)) return cachedPath;
12926
+ fs17.mkdirSync(dir, { recursive: true });
12927
+ const cachedPath = path16.join(dir, RUNNER_ARCHIVE);
12928
+ if (fs17.existsSync(cachedPath)) return cachedPath;
12832
12929
  const response = await fetch(RUNNER_URL);
12833
12930
  if (!response.ok) {
12834
12931
  throw new Error(`Failed to download runner: HTTP ${response.status}`);
12835
12932
  }
12836
12933
  const buffer = Buffer.from(await response.arrayBuffer());
12837
- fs16.writeFileSync(cachedPath, buffer);
12934
+ fs17.writeFileSync(cachedPath, buffer);
12838
12935
  return cachedPath;
12839
12936
  }
12840
12937
  async function resolveJavaHome() {
@@ -12844,9 +12941,9 @@ async function resolveJavaHome() {
12844
12941
  });
12845
12942
  }
12846
12943
  function isRunning(projectName) {
12847
- const pidFile = path15.join(runnerDir(projectName), ".pid");
12848
- if (!fs16.existsSync(pidFile)) return false;
12849
- const pid = parseInt(fs16.readFileSync(pidFile, "utf-8").trim(), 10);
12944
+ const pidFile = path16.join(runnerDir(projectName), ".pid");
12945
+ if (!fs17.existsSync(pidFile)) return false;
12946
+ const pid = parseInt(fs17.readFileSync(pidFile, "utf-8").trim(), 10);
12850
12947
  if (!pid) return false;
12851
12948
  try {
12852
12949
  process.kill(pid, 0);
@@ -12857,23 +12954,23 @@ function isRunning(projectName) {
12857
12954
  }
12858
12955
  function getRunnerInfo(projectName) {
12859
12956
  const dir = runnerDir(projectName);
12860
- if (!fs16.existsSync(dir)) return void 0;
12861
- const pidFile = path15.join(dir, ".pid");
12957
+ if (!fs17.existsSync(dir)) return void 0;
12958
+ const pidFile = path16.join(dir, ".pid");
12862
12959
  let pid;
12863
- if (fs16.existsSync(pidFile)) {
12864
- pid = parseInt(fs16.readFileSync(pidFile, "utf-8").trim(), 10);
12960
+ if (fs17.existsSync(pidFile)) {
12961
+ pid = parseInt(fs17.readFileSync(pidFile, "utf-8").trim(), 10);
12865
12962
  }
12866
12963
  return { name: runnerName(projectName), dir, pid, online: isRunning(projectName) };
12867
12964
  }
12868
12965
  var lastRunnerPid;
12869
12966
  function stopRunner(projectName) {
12870
12967
  const dir = runnerDir(projectName);
12871
- const pidFile = path15.join(dir, ".pid");
12968
+ const pidFile = path16.join(dir, ".pid");
12872
12969
  let pid = lastRunnerPid;
12873
- if (fs16.existsSync(pidFile)) {
12874
- pid = parseInt(fs16.readFileSync(pidFile, "utf-8").trim(), 10);
12970
+ if (fs17.existsSync(pidFile)) {
12971
+ pid = parseInt(fs17.readFileSync(pidFile, "utf-8").trim(), 10);
12875
12972
  try {
12876
- fs16.unlinkSync(pidFile);
12973
+ fs17.unlinkSync(pidFile);
12877
12974
  } catch {
12878
12975
  }
12879
12976
  }
@@ -12886,7 +12983,7 @@ function stopRunner(projectName) {
12886
12983
  } catch {
12887
12984
  }
12888
12985
  }
12889
- } else if (fs16.existsSync(dir)) {
12986
+ } else if (fs17.existsSync(dir)) {
12890
12987
  try {
12891
12988
  cp4.execSync(`pkill -9 -f "${dir.replace(/\//g, "\\/")}.*Runner" 2>/dev/null || true`, {
12892
12989
  timeout: KIT_TIMEOUTS.cmdShort
@@ -12896,16 +12993,16 @@ function stopRunner(projectName) {
12896
12993
  }
12897
12994
  lastRunnerPid = void 0;
12898
12995
  for (const stale of ["_diag/pages", "_work/_temp", "_work/_actions"]) {
12899
- const full = path15.join(dir, stale);
12900
- if (fs16.existsSync(full)) {
12996
+ const full = path16.join(dir, stale);
12997
+ if (fs17.existsSync(full)) {
12901
12998
  try {
12902
- fs16.rmSync(full, { recursive: true, force: true });
12999
+ fs17.rmSync(full, { recursive: true, force: true });
12903
13000
  } catch {
12904
13001
  }
12905
13002
  }
12906
13003
  }
12907
13004
  try {
12908
- fs16.mkdirSync(path15.join(dir, "_diag", "pages"), { recursive: true });
13005
+ fs17.mkdirSync(path16.join(dir, "_diag", "pages"), { recursive: true });
12909
13006
  } catch {
12910
13007
  }
12911
13008
  }
@@ -12921,24 +13018,24 @@ function resetRunnerConfig(dir, projectName) {
12921
13018
  ];
12922
13019
  for (const f of stateFiles) {
12923
13020
  try {
12924
- fs16.unlinkSync(path15.join(dir, f));
13021
+ fs17.unlinkSync(path16.join(dir, f));
12925
13022
  } catch {
12926
13023
  }
12927
13024
  }
12928
13025
  if (process.platform === "darwin") {
12929
- const plist = path15.join(
13026
+ const plist = path16.join(
12930
13027
  os.homedir(),
12931
13028
  "Library",
12932
13029
  "LaunchAgents",
12933
13030
  `actions.runner.${projectName}.plist`
12934
13031
  );
12935
- if (fs16.existsSync(plist)) {
13032
+ if (fs17.existsSync(plist)) {
12936
13033
  try {
12937
13034
  cp4.execFileSync("launchctl", ["unload", plist], { stdio: "ignore" });
12938
13035
  } catch {
12939
13036
  }
12940
13037
  try {
12941
- fs16.unlinkSync(plist);
13038
+ fs17.unlinkSync(plist);
12942
13039
  } catch {
12943
13040
  }
12944
13041
  }
@@ -12952,24 +13049,24 @@ async function setupRunner(args) {
12952
13049
  stopRunner(args.projectName);
12953
13050
  report("Downloading runner binary...");
12954
13051
  const archive = await ensureCachedArchive();
12955
- fs16.mkdirSync(dir, { recursive: true });
12956
- if (!fs16.existsSync(path15.join(dir, "config.sh"))) {
13052
+ fs17.mkdirSync(dir, { recursive: true });
13053
+ if (!fs17.existsSync(path16.join(dir, "config.sh"))) {
12957
13054
  report("Extracting runner...");
12958
13055
  await tar.extract({ file: archive, cwd: dir });
12959
13056
  }
12960
- const diagPages = path15.join(dir, "_diag", "pages");
12961
- if (fs16.existsSync(diagPages)) {
12962
- fs16.rmSync(diagPages, { recursive: true, force: true });
12963
- fs16.mkdirSync(diagPages, { recursive: true });
13057
+ const diagPages = path16.join(dir, "_diag", "pages");
13058
+ if (fs17.existsSync(diagPages)) {
13059
+ fs17.rmSync(diagPages, { recursive: true, force: true });
13060
+ fs17.mkdirSync(diagPages, { recursive: true });
12964
13061
  }
12965
- const runnerFile = path15.join(dir, ".runner");
12966
- let needsConfig = !fs16.existsSync(runnerFile);
13062
+ const runnerFile = path16.join(dir, ".runner");
13063
+ let needsConfig = !fs17.existsSync(runnerFile);
12967
13064
  if (needsConfig) {
12968
13065
  resetRunnerConfig(dir, args.projectName);
12969
13066
  } else {
12970
13067
  let urlMismatch = false;
12971
13068
  try {
12972
- const runnerJson = JSON.parse(fs16.readFileSync(runnerFile, "utf-8"));
13069
+ const runnerJson = JSON.parse(fs17.readFileSync(runnerFile, "utf-8"));
12973
13070
  const configuredUrl = runnerJson.gitHubUrl || runnerJson.serverUrl || runnerJson.agentUrl || "";
12974
13071
  const expectedUrl = `https://github.com/${args.fullRepoName}`;
12975
13072
  urlMismatch = !!configuredUrl && !configuredUrl.startsWith(expectedUrl);
@@ -13030,7 +13127,7 @@ async function setupRunner(args) {
13030
13127
  child.unref();
13031
13128
  lastRunnerPid = child.pid;
13032
13129
  if (child.pid) {
13033
- fs16.writeFileSync(path15.join(dir, ".pid"), String(child.pid));
13130
+ fs17.writeFileSync(path16.join(dir, ".pid"), String(child.pid));
13034
13131
  }
13035
13132
  report("Waiting for runner to come online...");
13036
13133
  let online = false;
@@ -13062,14 +13159,14 @@ async function removeRunner(args) {
13062
13159
  } catch {
13063
13160
  }
13064
13161
  try {
13065
- fs16.rmSync(dir, { recursive: true, force: true });
13162
+ fs17.rmSync(dir, { recursive: true, force: true });
13066
13163
  } catch {
13067
13164
  }
13068
13165
  }
13069
13166
 
13070
13167
  // scripts/util/ci-secrets.ts
13071
13168
  init_esm_shims();
13072
- import * as path16 from "path";
13169
+ import * as path17 from "path";
13073
13170
 
13074
13171
  // scripts/git/remote.ts
13075
13172
  init_esm_shims();
@@ -13245,7 +13342,7 @@ async function ensureCiSecretsFresh(args) {
13245
13342
  return { action: "ok", reason: "CI token is current." };
13246
13343
  }
13247
13344
  async function ensureCiSecretsFreshFromEnv(projectDir, opts) {
13248
- const envPath = path16.join(projectDir, ".env");
13345
+ const envPath = path17.join(projectDir, ".env");
13249
13346
  const databricksHost = readEnvVar(envPath, "DATABRICKS_HOST");
13250
13347
  const lakebaseProjectId = readEnvVar(envPath, "LAKEBASE_PROJECT_ID");
13251
13348
  if (!databricksHost || !lakebaseProjectId) {
@@ -13272,8 +13369,8 @@ async function ensureCiSecretsFreshFromEnv(projectDir, opts) {
13272
13369
 
13273
13370
  // scripts/lakebase/scm-workflow-state.ts
13274
13371
  init_esm_shims();
13275
- import * as fs17 from "fs";
13276
- import * as path17 from "path";
13372
+ import * as fs18 from "fs";
13373
+ import * as path18 from "path";
13277
13374
  import { execFileSync as execFileSync6 } from "child_process";
13278
13375
  function isGitTracked(projectDir, rel) {
13279
13376
  try {
@@ -13292,11 +13389,11 @@ function ensureWorkflowStateUntracked(projectDir) {
13292
13389
  } catch {
13293
13390
  }
13294
13391
  try {
13295
- const gitignore = path17.join(projectDir, ".gitignore");
13296
- const existing = fs17.existsSync(gitignore) ? fs17.readFileSync(gitignore, "utf8") : "";
13392
+ const gitignore = path18.join(projectDir, ".gitignore");
13393
+ const existing = fs18.existsSync(gitignore) ? fs18.readFileSync(gitignore, "utf8") : "";
13297
13394
  if (!existing.split("\n").some((l) => l.trim() === rel)) {
13298
13395
  const sep4 = existing === "" || existing.endsWith("\n") ? "" : "\n";
13299
- fs17.appendFileSync(
13396
+ fs18.appendFileSync(
13300
13397
  gitignore,
13301
13398
  `${sep4}# Runtime SCM claim state (per working tree): a branch checkout must never restore
13302
13399
  # a stale committed claim over the live one (issue #203 / Finding 28).
@@ -13320,12 +13417,12 @@ var STATE_INDEX = SCM_STATES.reduce(
13320
13417
  );
13321
13418
  var STATE_FILE_REL = ".lakebase/workflow-state.json";
13322
13419
  function stateFilePath(projectDir) {
13323
- return path17.join(projectDir, STATE_FILE_REL);
13420
+ return path18.join(projectDir, STATE_FILE_REL);
13324
13421
  }
13325
13422
  function readWorkflowState(projectDir) {
13326
13423
  const p = stateFilePath(projectDir);
13327
- if (!fs17.existsSync(p)) return null;
13328
- const raw = fs17.readFileSync(p, "utf8");
13424
+ if (!fs18.existsSync(p)) return null;
13425
+ const raw = fs18.readFileSync(p, "utf8");
13329
13426
  let parsed;
13330
13427
  try {
13331
13428
  parsed = JSON.parse(raw);
@@ -13360,14 +13457,14 @@ function writeWorkflowState(projectDir, state) {
13360
13457
  ${summary}`);
13361
13458
  }
13362
13459
  ensureWorkflowStateUntracked(projectDir);
13363
- const dir = path17.join(projectDir, ".lakebase");
13364
- fs17.mkdirSync(dir, { recursive: true });
13460
+ const dir = path18.join(projectDir, ".lakebase");
13461
+ fs18.mkdirSync(dir, { recursive: true });
13365
13462
  const target = stateFilePath(projectDir);
13366
13463
  const tmp = `${target}.tmp`;
13367
13464
  const ordered = orderForOutput(result.value);
13368
- fs17.writeFileSync(tmp, `${JSON.stringify(ordered, null, 2)}
13465
+ fs18.writeFileSync(tmp, `${JSON.stringify(ordered, null, 2)}
13369
13466
  `, "utf8");
13370
- fs17.renameSync(tmp, target);
13467
+ fs18.renameSync(tmp, target);
13371
13468
  }
13372
13469
  function initWorkflowState(args) {
13373
13470
  return {
@@ -13591,7 +13688,7 @@ function orderForOutput(state) {
13591
13688
  async function createProject(input, progress) {
13592
13689
  const report = progress ?? (() => {
13593
13690
  });
13594
- const projectDir = path18.join(input.parentDir, input.projectName);
13691
+ const projectDir = path19.join(input.parentDir, input.projectName);
13595
13692
  const lakebaseProjectId = input.projectName;
13596
13693
  const host = input.databricksHost.replace(/\/+$/, "");
13597
13694
  const useGithub = input.createGithubRepo !== false;
@@ -13616,7 +13713,7 @@ async function createProject(input, progress) {
13616
13713
  useGithub,
13617
13714
  githubOwner: input.githubOwner,
13618
13715
  tiers,
13619
- dirExists: (p) => fs18.existsSync(p),
13716
+ dirExists: (p) => fs19.existsSync(p),
13620
13717
  dirIsEmpty
13621
13718
  });
13622
13719
  if (!v.ok) throw new Error(v.reason);
@@ -13685,7 +13782,7 @@ Last probe error:
13685
13782
  });
13686
13783
  } else {
13687
13784
  report("Creating local project directory...", projectDir);
13688
- fs18.mkdirSync(projectDir, { recursive: true });
13785
+ fs19.mkdirSync(projectDir, { recursive: true });
13689
13786
  await gitInit(projectDir);
13690
13787
  }
13691
13788
  report("Creating Lakebase database (provisioning Postgres)...", lakebaseProjectId);
@@ -13704,6 +13801,7 @@ Last probe error:
13704
13801
  databricksHost: host,
13705
13802
  lakebaseProjectId,
13706
13803
  language,
13804
+ migrationTool: input.migrationTool,
13707
13805
  runnerType,
13708
13806
  skipCommands,
13709
13807
  clientFramework,
@@ -13818,9 +13916,9 @@ Last probe error:
13818
13916
  const scmRef = envRef || (ver !== "unknown" ? `v${ver}` : "");
13819
13917
  if (scmRef) {
13820
13918
  try {
13821
- const dir = path18.join(projectDir, ".lakebase");
13822
- fs18.mkdirSync(dir, { recursive: true });
13823
- fs18.writeFileSync(path18.join(dir, "scm-utils-ref"), `${scmRef}
13919
+ const dir = path19.join(projectDir, ".lakebase");
13920
+ fs19.mkdirSync(dir, { recursive: true });
13921
+ fs19.writeFileSync(path19.join(dir, "scm-utils-ref"), `${scmRef}
13824
13922
  `, "utf8");
13825
13923
  } catch (err) {
13826
13924
  warnings.push(`Substrate ref pin failed (advisory): ${err instanceof Error ? err.message : String(err)}.`);
@@ -13831,9 +13929,9 @@ Last probe error:
13831
13929
  const kitRef = process.env.LAKEBASE_KIT_REF?.trim();
13832
13930
  if (kitRef) {
13833
13931
  try {
13834
- const dir = path18.join(projectDir, ".lakebase");
13835
- fs18.mkdirSync(dir, { recursive: true });
13836
- fs18.writeFileSync(path18.join(dir, "kit-ref"), `${kitRef}
13932
+ const dir = path19.join(projectDir, ".lakebase");
13933
+ fs19.mkdirSync(dir, { recursive: true });
13934
+ fs19.writeFileSync(path19.join(dir, "kit-ref"), `${kitRef}
13837
13935
  `, "utf8");
13838
13936
  } catch (err) {
13839
13937
  warnings.push(`Kit ref pin failed (advisory): ${err instanceof Error ? err.message : String(err)}.`);
@@ -13847,9 +13945,21 @@ Last probe error:
13847
13945
  java: "Java/Spring Boot",
13848
13946
  kotlin: "Kotlin/Spring Boot",
13849
13947
  python: "Python/FastAPI",
13850
- nodejs: "Node.js/Express"
13948
+ nodejs: "Node.js/Express",
13949
+ dotnet: `.NET/ASP.NET Core (${input.migrationTool === "flyway" ? "Flyway" : "EF Core"})`
13851
13950
  };
13852
13951
  const langLabel = langLabels[language] ?? language;
13952
+ if (clientFramework !== "none") {
13953
+ report("Generating client package-lock.json (registry-normalized)...");
13954
+ const lock = await ensureClientLockfile({ projectDir });
13955
+ if (lock.generated) {
13956
+ report(` wrote client/package-lock.json${lock.swaps > 0 ? ` (normalized ${lock.swaps} proxy URL(s) to the public registry)` : ""}`);
13957
+ } else {
13958
+ const m = `Could not generate client/package-lock.json (${lock.reason ?? "unknown"}). Generate it with: (cd client && npm install) \u2014 the lock is committed on first push.`;
13959
+ warnings.push(m);
13960
+ report(`WARNING: ${m}`);
13961
+ }
13962
+ }
13853
13963
  report("Creating initial commit...");
13854
13964
  await commitAndPush({
13855
13965
  projectDir,
@@ -13914,17 +14024,17 @@ Last probe error:
13914
14024
  // scripts/lakebase/adopt-lakebase-project.ts
13915
14025
  init_esm_shims();
13916
14026
  import * as cp5 from "child_process";
13917
- import * as fs19 from "fs";
13918
- import * as path19 from "path";
14027
+ import * as fs20 from "fs";
14028
+ import * as path20 from "path";
13919
14029
  async function adoptLakebaseProject(args) {
13920
14030
  const warnings = [];
13921
14031
  const filesWritten = [];
13922
14032
  const dryRun = args.dryRun === true;
13923
14033
  const preserveExistingEnv = args.preserveExistingEnv !== false;
13924
- if (!fs19.existsSync(args.projectDir)) {
14034
+ if (!fs20.existsSync(args.projectDir)) {
13925
14035
  throw new Error(`adoptLakebaseProject: project directory does not exist: ${args.projectDir}`);
13926
14036
  }
13927
- if (!fs19.existsSync(path19.join(args.projectDir, ".git"))) {
14037
+ if (!fs20.existsSync(path20.join(args.projectDir, ".git"))) {
13928
14038
  throw new Error(
13929
14039
  `adoptLakebaseProject: ${args.projectDir} is not a git repo. Run \`git init\` first, or pass an existing repo path.`
13930
14040
  );
@@ -13990,9 +14100,9 @@ async function adoptLakebaseProject(args) {
13990
14100
  };
13991
14101
  }
13992
14102
  function assertEnvCompatibility(projectDir, expectedProjectId) {
13993
- const envPath = path19.join(projectDir, ".env");
13994
- if (!fs19.existsSync(envPath)) return;
13995
- const content = fs19.readFileSync(envPath, "utf8");
14103
+ const envPath = path20.join(projectDir, ".env");
14104
+ if (!fs20.existsSync(envPath)) return;
14105
+ const content = fs20.readFileSync(envPath, "utf8");
13996
14106
  const match = content.match(/^LAKEBASE_PROJECT_ID\s*=\s*(.+?)\s*$/m);
13997
14107
  if (!match) return;
13998
14108
  const existing = match[1].trim().replace(/^['"]|['"]$/g, "");
@@ -14003,10 +14113,10 @@ function assertEnvCompatibility(projectDir, expectedProjectId) {
14003
14113
  }
14004
14114
  }
14005
14115
  function assertAdoptionPreflight(args) {
14006
- if (!fs19.existsSync(args.projectDir)) {
14116
+ if (!fs20.existsSync(args.projectDir)) {
14007
14117
  throw new Error(`assertAdoptionPreflight: project directory does not exist: ${args.projectDir}`);
14008
14118
  }
14009
- if (!fs19.existsSync(path19.join(args.projectDir, ".git"))) {
14119
+ if (!fs20.existsSync(path20.join(args.projectDir, ".git"))) {
14010
14120
  throw new Error(
14011
14121
  `assertAdoptionPreflight: ${args.projectDir} is not a git repo.`
14012
14122
  );
@@ -14016,11 +14126,11 @@ function assertAdoptionPreflight(args) {
14016
14126
  }
14017
14127
  }
14018
14128
  function _testMakeBrownfieldFixture(opts) {
14019
- fs19.mkdirSync(opts.dir, { recursive: true });
14129
+ fs20.mkdirSync(opts.dir, { recursive: true });
14020
14130
  cp5.execSync("git init --quiet", { cwd: opts.dir, stdio: "pipe" });
14021
14131
  if (opts.packageJson) {
14022
- fs19.writeFileSync(
14023
- path19.join(opts.dir, "package.json"),
14132
+ fs20.writeFileSync(
14133
+ path20.join(opts.dir, "package.json"),
14024
14134
  JSON.stringify(opts.packageJson, null, 2) + "\n"
14025
14135
  );
14026
14136
  }
@@ -14028,8 +14138,8 @@ function _testMakeBrownfieldFixture(opts) {
14028
14138
 
14029
14139
  // scripts/lakebase/infra-runner.ts
14030
14140
  init_esm_shims();
14031
- import * as fs27 from "fs";
14032
- import * as path28 from "path";
14141
+ import * as fs30 from "fs";
14142
+ import * as path32 from "path";
14033
14143
 
14034
14144
  // scripts/lakebase/schema-diff.ts
14035
14145
  init_esm_shims();
@@ -14225,18 +14335,84 @@ function dbcli6(args) {
14225
14335
 
14226
14336
  // scripts/lakebase/schema-migrate.ts
14227
14337
  init_esm_shims();
14228
- import * as fs26 from "fs";
14229
- import * as path27 from "path";
14338
+ import * as fs29 from "fs";
14339
+ import * as path31 from "path";
14230
14340
 
14231
14341
  // scripts/lakebase/migration-layout.ts
14232
14342
  init_esm_shims();
14233
- import * as fs20 from "fs";
14234
- import * as path20 from "path";
14343
+ import * as fs22 from "fs";
14344
+ import * as path22 from "path";
14345
+
14346
+ // scripts/lakebase/dotnet-project.ts
14347
+ init_esm_shims();
14348
+ import * as fs21 from "fs";
14349
+ import * as path21 from "path";
14350
+ var EF_DESIGN_PACKAGE = "Microsoft.EntityFrameworkCore.Design";
14351
+ var DOTNET_FLYWAY_DIR = path21.join("db", "migrations");
14352
+ var SKIP_DIRS = /* @__PURE__ */ new Set(["bin", "obj", "node_modules", ".git", "client", "tests", "test"]);
14353
+ function safeReaddir(dir) {
14354
+ try {
14355
+ return fs21.readdirSync(dir, { withFileTypes: true });
14356
+ } catch {
14357
+ return [];
14358
+ }
14359
+ }
14360
+ function hasDotnetMarker(dir) {
14361
+ return safeReaddir(dir).some(
14362
+ (e) => e.isFile() && /\.(sln|slnx|csproj)$/i.test(e.name)
14363
+ );
14364
+ }
14365
+ function findCsprojFiles(projectDir) {
14366
+ const found = [];
14367
+ const walk = (dir, depth) => {
14368
+ for (const e of safeReaddir(dir)) {
14369
+ const full = path21.join(dir, e.name);
14370
+ if (e.isFile() && e.name.toLowerCase().endsWith(".csproj")) {
14371
+ found.push(full);
14372
+ } else if (e.isDirectory() && depth < 2 && !SKIP_DIRS.has(e.name.toLowerCase()) && !e.name.startsWith(".")) {
14373
+ walk(full, depth + 1);
14374
+ }
14375
+ }
14376
+ };
14377
+ walk(projectDir, 0);
14378
+ return found.sort();
14379
+ }
14380
+ function findEfCoreProject(projectDir) {
14381
+ for (const csproj of findCsprojFiles(projectDir)) {
14382
+ let content = "";
14383
+ try {
14384
+ content = fs21.readFileSync(csproj, "utf-8");
14385
+ } catch {
14386
+ continue;
14387
+ }
14388
+ if (content.includes(EF_DESIGN_PACKAGE)) {
14389
+ const dir = path21.dirname(csproj);
14390
+ return { csproj, projectDir: dir, migrationsDir: path21.join(dir, "Migrations") };
14391
+ }
14392
+ }
14393
+ return null;
14394
+ }
14395
+ function isDotnetProject(projectDir) {
14396
+ return hasDotnetMarker(projectDir);
14397
+ }
14398
+ function isDotnetFlywayProject(projectDir) {
14399
+ return isDotnetProject(projectDir) && fs21.existsSync(path21.join(projectDir, DOTNET_FLYWAY_DIR));
14400
+ }
14401
+ var MAVEN_FLYWAY_DIR = path21.join("src", "main", "resources", "db", "migration");
14402
+ function flywayMigrationsDir(projectDir) {
14403
+ const maven = path21.join(projectDir, MAVEN_FLYWAY_DIR);
14404
+ if (fs21.existsSync(maven) || fs21.existsSync(path21.join(projectDir, "pom.xml"))) return maven;
14405
+ if (isDotnetFlywayProject(projectDir)) return path21.join(projectDir, DOTNET_FLYWAY_DIR);
14406
+ return maven;
14407
+ }
14408
+
14409
+ // scripts/lakebase/migration-layout.ts
14235
14410
  var MIGRATION_LANGUAGES = [
14236
14411
  "java",
14237
14412
  "kotlin",
14238
14413
  "python",
14239
14414
  "nodejs",
14415
+ "dotnet",
14240
14416
  "unknown"
14241
14417
  ];
14242
14418
  var MIGRATION_DEFAULTS = {
@@ -14244,16 +14420,19 @@ var MIGRATION_DEFAULTS = {
14244
14420
  kotlin: { path: "src/main/resources/db/migration", pattern: /^V\d+.*\.sql$/i, glob: "*.sql" },
14245
14421
  python: { path: "alembic/versions", pattern: /^[0-9a-f][\w]*.*\.py$/i, glob: "*.py" },
14246
14422
  nodejs: { path: "migrations", pattern: /^\d+.*\.(js|ts)$/i, glob: "*.{js,ts}" },
14423
+ // .NET + Flyway; EF Core projects get their Migrations/ dir from
14424
+ // resolveMigrationLayout (see DOTNET_EFCORE_DEFAULTS).
14425
+ dotnet: { path: "db/migrations", pattern: /^V\d+.*\.sql$/i, glob: "*.sql" },
14247
14426
  unknown: { path: "src/main/resources/db/migration", pattern: /^V\d+.*\.sql$/i, glob: "*.sql" }
14248
14427
  };
14249
14428
  function detectLanguageAt(dir) {
14250
- if (fs20.existsSync(path20.join(dir, "pom.xml"))) {
14251
- const kotlinDir = path20.join(dir, "src", "main", "kotlin");
14252
- if (fs20.existsSync(kotlinDir)) {
14429
+ if (fs22.existsSync(path22.join(dir, "pom.xml"))) {
14430
+ const kotlinDir = path22.join(dir, "src", "main", "kotlin");
14431
+ if (fs22.existsSync(kotlinDir)) {
14253
14432
  return "kotlin";
14254
14433
  }
14255
14434
  try {
14256
- const pom = fs20.readFileSync(path20.join(dir, "pom.xml"), "utf-8");
14435
+ const pom = fs22.readFileSync(path22.join(dir, "pom.xml"), "utf-8");
14257
14436
  if (pom.includes("kotlin-maven-plugin")) {
14258
14437
  return "kotlin";
14259
14438
  }
@@ -14261,10 +14440,13 @@ function detectLanguageAt(dir) {
14261
14440
  }
14262
14441
  return "java";
14263
14442
  }
14264
- if (fs20.existsSync(path20.join(dir, "pyproject.toml")) || fs20.existsSync(path20.join(dir, "requirements.txt")) || fs20.existsSync(path20.join(dir, "alembic.ini"))) {
14443
+ if (fs22.existsSync(path22.join(dir, "pyproject.toml")) || fs22.existsSync(path22.join(dir, "requirements.txt")) || fs22.existsSync(path22.join(dir, "alembic.ini"))) {
14265
14444
  return "python";
14266
14445
  }
14267
- if (fs20.existsSync(path20.join(dir, "package.json"))) {
14446
+ if (hasDotnetMarker(dir)) {
14447
+ return "dotnet";
14448
+ }
14449
+ if (fs22.existsSync(path22.join(dir, "package.json"))) {
14268
14450
  return "nodejs";
14269
14451
  }
14270
14452
  return "unknown";
@@ -14285,14 +14467,14 @@ function resolveMigrationLanguage(projectDir, configuredMigrationPath, override)
14285
14467
  if (!rel) {
14286
14468
  return "unknown";
14287
14469
  }
14288
- const rootResolved = path20.resolve(projectDir);
14289
- let dir = path20.resolve(projectDir, rel);
14290
- while (dir === rootResolved || dir.startsWith(rootResolved + path20.sep)) {
14470
+ const rootResolved = path22.resolve(projectDir);
14471
+ let dir = path22.resolve(projectDir, rel);
14472
+ while (dir === rootResolved || dir.startsWith(rootResolved + path22.sep)) {
14291
14473
  const lang = detectLanguageAt(dir);
14292
14474
  if (lang !== "unknown") {
14293
14475
  return lang;
14294
14476
  }
14295
- const parent = path20.dirname(dir);
14477
+ const parent = path22.dirname(dir);
14296
14478
  if (parent === dir) {
14297
14479
  break;
14298
14480
  }
@@ -14311,10 +14493,33 @@ function compileMigrationPattern(src) {
14311
14493
  return void 0;
14312
14494
  }
14313
14495
  }
14496
+ var DOTNET_EFCORE_DEFAULTS = {
14497
+ path: "Migrations",
14498
+ pattern: /^\d{14}_(?!.*\.Designer\.cs$).+\.cs$/i,
14499
+ glob: "*.cs"
14500
+ };
14501
+ var DOTNET_LAYOUT_TTL_MS = 5e3;
14502
+ var dotnetLayoutCache = /* @__PURE__ */ new Map();
14503
+ function dotnetDefaults(projectDir) {
14504
+ if (!projectDir) return MIGRATION_DEFAULTS.dotnet;
14505
+ const key = path22.resolve(projectDir);
14506
+ const hit = dotnetLayoutCache.get(key);
14507
+ if (hit && Date.now() - hit.at < DOTNET_LAYOUT_TTL_MS) return hit.value;
14508
+ let value = MIGRATION_DEFAULTS.dotnet;
14509
+ if (!isDotnetFlywayProject(projectDir)) {
14510
+ const ef = findEfCoreProject(projectDir);
14511
+ if (ef) {
14512
+ const rel = path22.relative(projectDir, ef.migrationsDir).split(path22.sep).join("/");
14513
+ value = { ...DOTNET_EFCORE_DEFAULTS, path: rel };
14514
+ }
14515
+ }
14516
+ dotnetLayoutCache.set(key, { at: Date.now(), value });
14517
+ return value;
14518
+ }
14314
14519
  function resolveMigrationLayout(args) {
14315
14520
  const configuredMigrationPath = (args.migrationPath ?? "").trim();
14316
14521
  const language = resolveMigrationLanguage(args.projectDir, configuredMigrationPath, args.language);
14317
- const defaults = MIGRATION_DEFAULTS[language];
14522
+ const defaults = language === "dotnet" ? dotnetDefaults(args.projectDir) : MIGRATION_DEFAULTS[language];
14318
14523
  return {
14319
14524
  language,
14320
14525
  migrationPath: configuredMigrationPath || defaults.path,
@@ -14325,22 +14530,22 @@ function resolveMigrationLayout(args) {
14325
14530
 
14326
14531
  // scripts/lakebase/adapters/alembic-adapter.ts
14327
14532
  init_esm_shims();
14328
- import * as fs22 from "fs";
14329
- import * as path22 from "path";
14533
+ import * as fs24 from "fs";
14534
+ import * as path24 from "path";
14330
14535
 
14331
14536
  // scripts/lakebase/schema-migrate-runners/alembic.ts
14332
14537
  init_esm_shims();
14333
14538
  import { spawn as spawn5 } from "child_process";
14334
- import * as fs21 from "fs";
14335
- import * as path21 from "path";
14539
+ import * as fs23 from "fs";
14540
+ import * as path23 from "path";
14336
14541
  function resolveAlembicBin(projectDir) {
14337
14542
  const candidates = [
14338
- path21.join(projectDir, ".venv", "bin", "alembic"),
14339
- path21.join(projectDir, "venv", "bin", "alembic")
14543
+ path23.join(projectDir, ".venv", "bin", "alembic"),
14544
+ path23.join(projectDir, "venv", "bin", "alembic")
14340
14545
  ];
14341
14546
  for (const candidate of candidates) {
14342
14547
  try {
14343
- if (fs21.existsSync(candidate)) return candidate;
14548
+ if (fs23.existsSync(candidate)) return candidate;
14344
14549
  } catch {
14345
14550
  }
14346
14551
  }
@@ -14350,7 +14555,7 @@ function spawnAlembic(projectDir, args, dsn) {
14350
14555
  return new Promise((resolve2, reject) => {
14351
14556
  const bin = resolveAlembicBin(projectDir);
14352
14557
  const env = { ...process.env };
14353
- env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(path21.delimiter);
14558
+ env.PYTHONPATH = [projectDir, process.env.PYTHONPATH].filter(Boolean).join(path23.delimiter);
14354
14559
  if (dsn) env.DATABASE_URL = dsn;
14355
14560
  const child = spawn5(bin, args, {
14356
14561
  cwd: projectDir,
@@ -14398,10 +14603,10 @@ async function createAlembicRevision(opts) {
14398
14603
  const m = stdout.match(/Generating\s+(\S+\.py)/);
14399
14604
  if (m) return m[1].trim();
14400
14605
  for (const rel of ["migrations/versions", "alembic/versions"]) {
14401
- const dir = path21.join(opts.projectDir, rel);
14402
- if (!fs21.existsSync(dir)) continue;
14403
- const hit = fs21.readdirSync(dir).find((f) => f.startsWith(`${opts.revId}_`) && f.endsWith(".py"));
14404
- if (hit) return path21.join(dir, hit);
14606
+ const dir = path23.join(opts.projectDir, rel);
14607
+ if (!fs23.existsSync(dir)) continue;
14608
+ const hit = fs23.readdirSync(dir).find((f) => f.startsWith(`${opts.revId}_`) && f.endsWith(".py"));
14609
+ if (hit) return path23.join(dir, hit);
14405
14610
  }
14406
14611
  throw new SchemaMigrationError(
14407
14612
  `alembic revision succeeded but the created file could not be located.
@@ -14536,15 +14741,15 @@ async function buildDsn2(args) {
14536
14741
  }
14537
14742
  function findVersionsDir(projectDir) {
14538
14743
  const candidates = [
14539
- path22.join(projectDir, "migrations", "versions"),
14540
- path22.join(projectDir, "alembic", "versions")
14744
+ path24.join(projectDir, "migrations", "versions"),
14745
+ path24.join(projectDir, "alembic", "versions")
14541
14746
  ];
14542
- return candidates.find((p) => fs22.existsSync(p));
14747
+ return candidates.find((p) => fs24.existsSync(p));
14543
14748
  }
14544
14749
  function listAlembicFiles(projectDir) {
14545
14750
  const dir = findVersionsDir(projectDir);
14546
14751
  if (!dir) return [];
14547
- const files = fs22.readdirSync(dir).filter((f) => f.endsWith(".py") && !f.startsWith("__"));
14752
+ const files = fs24.readdirSync(dir).filter((f) => f.endsWith(".py") && !f.startsWith("__"));
14548
14753
  return files.map((filename) => {
14549
14754
  const stem = filename.replace(/\.py$/, "");
14550
14755
  const sep4 = stem.indexOf("_");
@@ -14569,9 +14774,9 @@ var AlembicAdapter = {
14569
14774
  * here. Callers can still force-select via project.yaml#migration_tool.
14570
14775
  */
14571
14776
  detect(projectDir) {
14572
- if (fs22.existsSync(path22.join(projectDir, "alembic.ini"))) return true;
14573
- if (fs22.existsSync(path22.join(projectDir, "migrations", "env.py"))) return true;
14574
- if (fs22.existsSync(path22.join(projectDir, "alembic", "env.py"))) return true;
14777
+ if (fs24.existsSync(path24.join(projectDir, "alembic.ini"))) return true;
14778
+ if (fs24.existsSync(path24.join(projectDir, "migrations", "env.py"))) return true;
14779
+ if (fs24.existsSync(path24.join(projectDir, "alembic", "env.py"))) return true;
14575
14780
  return false;
14576
14781
  },
14577
14782
  async apply(args) {
@@ -14677,7 +14882,7 @@ var AlembicAdapter = {
14677
14882
  autogenerate: !!args.autogenerate,
14678
14883
  dsn
14679
14884
  });
14680
- return { status: "ok", version: revId, filename: path22.basename(created), path: created };
14885
+ return { status: "ok", version: revId, filename: path24.basename(created), path: created };
14681
14886
  } catch (err) {
14682
14887
  return {
14683
14888
  status: "error",
@@ -14694,7 +14899,7 @@ var AlembicAdapter = {
14694
14899
  if (heads.length <= 1) return { status: "noop", headsBefore: heads };
14695
14900
  if (args.dryRun) return { status: "ok", headsBefore: heads };
14696
14901
  const created = await mergeAlembicHeads(args.projectDir, args.message ?? "merge heads");
14697
- const mergeRevision = path22.basename(created).replace(/\.py$/, "").split("_")[0];
14902
+ const mergeRevision = path24.basename(created).replace(/\.py$/, "").split("_")[0];
14698
14903
  return { status: "ok", headsBefore: heads, mergeRevision, path: created };
14699
14904
  } catch (err) {
14700
14905
  return {
@@ -14709,13 +14914,13 @@ registerSchemaMigrationAdapter(AlembicAdapter);
14709
14914
 
14710
14915
  // scripts/lakebase/adapters/flyway-adapter.ts
14711
14916
  init_esm_shims();
14712
- import * as fs23 from "fs";
14713
- import * as path24 from "path";
14917
+ import * as fs25 from "fs";
14918
+ import * as path26 from "path";
14714
14919
 
14715
14920
  // scripts/lakebase/schema-migrate-runners/flyway.ts
14716
14921
  init_esm_shims();
14717
14922
  import { spawn as spawn6 } from "child_process";
14718
- import * as path23 from "path";
14923
+ import * as path25 from "path";
14719
14924
  function dsnToFlywayEnv(dsn) {
14720
14925
  const u = new URL(dsn);
14721
14926
  const user = decodeURIComponent(u.username);
@@ -14725,7 +14930,7 @@ function dsnToFlywayEnv(dsn) {
14725
14930
  return { url, user, password };
14726
14931
  }
14727
14932
  function migrationsLocation(projectDir) {
14728
- return `filesystem:${path23.join(projectDir, "src", "main", "resources", "db", "migration")}`;
14933
+ return `filesystem:${flywayMigrationsDir(projectDir)}`;
14729
14934
  }
14730
14935
  function runFlyway(ctx, args) {
14731
14936
  const { url, user, password } = dsnToFlywayEnv(ctx.dsn);
@@ -14826,7 +15031,7 @@ async function statusFlyway(ctx) {
14826
15031
  if (state === "SUCCESS" || state === "BASELINE") {
14827
15032
  current = m.version;
14828
15033
  } else if (state === "PENDING") {
14829
- const filename = m.filepath ? path23.basename(m.filepath) : `V${m.version}__migration.sql`;
15034
+ const filename = m.filepath ? path25.basename(m.filepath) : `V${m.version}__migration.sql`;
14830
15035
  pending.push({
14831
15036
  version: m.version,
14832
15037
  filename,
@@ -14849,9 +15054,9 @@ async function buildDsn3(args) {
14849
15054
  return result.url;
14850
15055
  }
14851
15056
  function listFlywayFiles(projectDir) {
14852
- const dir = path24.join(projectDir, "src", "main", "resources", "db", "migration");
14853
- if (!fs23.existsSync(dir)) return [];
14854
- const files = fs23.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
15057
+ const dir = flywayMigrationsDir(projectDir);
15058
+ if (!fs25.existsSync(dir)) return [];
15059
+ const files = fs25.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
14855
15060
  return files.map((filename) => {
14856
15061
  const m = filename.match(/^V(\d+(?:\.\d+)*)__(.+)\.sql$/);
14857
15062
  const version = m[1];
@@ -14872,9 +15077,9 @@ function versionCompare(a, b) {
14872
15077
  }
14873
15078
  var FlywayAdapter = {
14874
15079
  id: "flyway",
14875
- languages: ["java", "kotlin"],
15080
+ languages: ["java", "kotlin", "dotnet"],
14876
15081
  detect(projectDir) {
14877
- return fs23.existsSync(path24.join(projectDir, "pom.xml"));
15082
+ return fs25.existsSync(path26.join(projectDir, "pom.xml")) || isDotnetFlywayProject(projectDir);
14878
15083
  },
14879
15084
  async apply(args) {
14880
15085
  const dsn = await buildDsn3(args);
@@ -14932,14 +15137,14 @@ var FlywayAdapter = {
14932
15137
  // optional-protocol shape makes this additive.
14933
15138
  async newMigration(args) {
14934
15139
  try {
14935
- const dir = path24.join(args.projectDir, "src", "main", "resources", "db", "migration");
14936
- fs23.mkdirSync(dir, { recursive: true });
15140
+ const dir = flywayMigrationsDir(args.projectDir);
15141
+ fs25.mkdirSync(dir, { recursive: true });
14937
15142
  const version = migrationTimestamp();
14938
15143
  const slug = migrationSlug2(args.slug);
14939
15144
  const filename = `V${version}__${slug}.sql`;
14940
- const full = path24.join(dir, filename);
14941
- if (fs23.existsSync(full)) throw new Error(`${filename} already exists`);
14942
- fs23.writeFileSync(
15145
+ const full = path26.join(dir, filename);
15146
+ if (fs25.existsSync(full)) throw new Error(`${filename} already exists`);
15147
+ fs25.writeFileSync(
14943
15148
  full,
14944
15149
  `-- V${version}: ${args.slug}
14945
15150
  -- Flyway migration (write your DDL/DML below).
@@ -14962,19 +15167,19 @@ registerSchemaMigrationAdapter(FlywayAdapter);
14962
15167
 
14963
15168
  // scripts/lakebase/adapters/knex-adapter.ts
14964
15169
  init_esm_shims();
14965
- import * as fs25 from "fs";
14966
- import * as path26 from "path";
15170
+ import * as fs27 from "fs";
15171
+ import * as path28 from "path";
14967
15172
 
14968
15173
  // scripts/lakebase/schema-migrate-runners/knex.ts
14969
15174
  init_esm_shims();
14970
15175
  import { spawn as spawn7 } from "child_process";
14971
- import * as fs24 from "fs";
14972
- import * as path25 from "path";
15176
+ import * as fs26 from "fs";
15177
+ import * as path27 from "path";
14973
15178
  var KNEXFILE_VARIANTS = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
14974
15179
  function findKnexfile(projectDir) {
14975
15180
  for (const name of KNEXFILE_VARIANTS) {
14976
- const p = path25.join(projectDir, name);
14977
- if (fs24.existsSync(p)) return p;
15181
+ const p = path27.join(projectDir, name);
15182
+ if (fs26.existsSync(p)) return p;
14978
15183
  }
14979
15184
  return void 0;
14980
15185
  }
@@ -15126,9 +15331,9 @@ async function buildDsn4(args) {
15126
15331
  }
15127
15332
  var KNEXFILE_VARIANTS2 = ["knexfile.js", "knexfile.ts", "knexfile.mjs", "knexfile.cjs"];
15128
15333
  function listKnexFiles(projectDir) {
15129
- const dir = path26.join(projectDir, "migrations");
15130
- if (!fs25.existsSync(dir)) return [];
15131
- const files = fs25.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
15334
+ const dir = path28.join(projectDir, "migrations");
15335
+ if (!fs27.existsSync(dir)) return [];
15336
+ const files = fs27.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
15132
15337
  return files.map((filename) => {
15133
15338
  const stem = filename.replace(/\.(js|ts)$/, "");
15134
15339
  const m = stem.match(/^(\d{14})_(.+)$/);
@@ -15148,7 +15353,7 @@ var KnexAdapter = {
15148
15353
  * project.yaml#migration_tool.
15149
15354
  */
15150
15355
  detect(projectDir) {
15151
- return KNEXFILE_VARIANTS2.some((name) => fs25.existsSync(path26.join(projectDir, name)));
15356
+ return KNEXFILE_VARIANTS2.some((name) => fs27.existsSync(path28.join(projectDir, name)));
15152
15357
  },
15153
15358
  async apply(args) {
15154
15359
  const dsn = await buildDsn4(args);
@@ -15221,9 +15426,9 @@ var KnexAdapter = {
15221
15426
  async newMigration(args) {
15222
15427
  try {
15223
15428
  const created = await createKnexMigration({ projectDir: args.projectDir, slug: migrationSlug2(args.slug) });
15224
- const stem = path26.basename(created).replace(/\.(js|ts)$/, "");
15429
+ const stem = path28.basename(created).replace(/\.(js|ts)$/, "");
15225
15430
  const version = stem.match(/^(\d{14})_/)?.[1] ?? stem;
15226
- return { status: "ok", version, filename: path26.basename(created), path: created };
15431
+ return { status: "ok", version, filename: path28.basename(created), path: created };
15227
15432
  } catch (err) {
15228
15433
  return {
15229
15434
  status: "error",
@@ -15237,6 +15442,362 @@ var KnexAdapter = {
15237
15442
  };
15238
15443
  registerSchemaMigrationAdapter(KnexAdapter);
15239
15444
 
15445
+ // scripts/lakebase/adapters/efcore-adapter.ts
15446
+ init_esm_shims();
15447
+ import * as path30 from "path";
15448
+
15449
+ // scripts/lakebase/schema-migrate-runners/efcore.ts
15450
+ init_esm_shims();
15451
+ import { spawn as spawn8 } from "child_process";
15452
+ import * as fs28 from "fs";
15453
+ import * as path29 from "path";
15454
+ import { Client as Client3 } from "pg";
15455
+ var MIGRATION_ID = /^(\d{14})_(.+)$/;
15456
+ function efVersion(id) {
15457
+ return MIGRATION_ID.exec(id)?.[1] ?? id;
15458
+ }
15459
+ function efDescription(id) {
15460
+ const name = MIGRATION_ID.exec(id)?.[2] ?? id;
15461
+ return name.replace(/_/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2");
15462
+ }
15463
+ function dsnToNpgsql(dsn) {
15464
+ const u = new URL(dsn);
15465
+ const parts = [
15466
+ `Host=${u.hostname}`,
15467
+ `Port=${u.port || "5432"}`,
15468
+ `Database=${decodeURIComponent(u.pathname.replace(/^\//, "")) || "databricks_postgres"}`
15469
+ ];
15470
+ if (u.username) parts.push(`Username=${decodeURIComponent(u.username)}`);
15471
+ if (u.password) parts.push(`Password=${decodeURIComponent(u.password)}`);
15472
+ const sslmode = (u.searchParams.get("sslmode") ?? "require").toLowerCase();
15473
+ const ssl = {
15474
+ disable: "Disable",
15475
+ allow: "Allow",
15476
+ prefer: "Prefer",
15477
+ require: "Require",
15478
+ "verify-ca": "VerifyCA",
15479
+ "verify-full": "VerifyFull"
15480
+ };
15481
+ parts.push(`SSL Mode=${ssl[sslmode] ?? "Require"}`);
15482
+ const app = u.searchParams.get("application_name");
15483
+ if (app) parts.push(`Application Name=${app}`);
15484
+ return parts.map((p) => p.replace(/;/g, "")).join(";");
15485
+ }
15486
+ function parseEfJson(stdout) {
15487
+ const body = stdout.split(/\r?\n/).filter((l) => l.startsWith("data:")).map((l) => l.slice("data:".length)).join("\n").trim();
15488
+ if (!body) {
15489
+ throw new SchemaMigrationError(`dotnet ef returned no JSON payload: ${stdout.slice(0, 400)}`);
15490
+ }
15491
+ try {
15492
+ return JSON.parse(body);
15493
+ } catch (err) {
15494
+ throw new SchemaMigrationError(
15495
+ `dotnet ef JSON parse failed: ${err instanceof Error ? err.message : String(err)}.
15496
+ Body (first 400 chars): ${body.slice(0, 400)}`
15497
+ );
15498
+ }
15499
+ }
15500
+ function requireProject(projectDir) {
15501
+ const project = findEfCoreProject(projectDir);
15502
+ if (!project) {
15503
+ throw new SchemaMigrationError(
15504
+ `No EF Core project found under ${projectDir}. Expected a *.csproj that references ${EF_DESIGN_PACKAGE}.`
15505
+ );
15506
+ }
15507
+ return project;
15508
+ }
15509
+ function spawnDotnet(projectDir, args, env) {
15510
+ return new Promise((resolve2, reject) => {
15511
+ const child = spawn8("dotnet", args, {
15512
+ cwd: projectDir,
15513
+ env: { ...process.env, DOTNET_NOLOGO: "1", DOTNET_CLI_TELEMETRY_OPTOUT: "1", ...env },
15514
+ stdio: ["ignore", "pipe", "pipe"]
15515
+ });
15516
+ let stdout = "";
15517
+ let stderr = "";
15518
+ child.stdout.on("data", (chunk) => {
15519
+ stdout += chunk.toString("utf8");
15520
+ });
15521
+ child.stderr.on("data", (chunk) => {
15522
+ stderr += chunk.toString("utf8");
15523
+ });
15524
+ child.on("error", (err) => {
15525
+ reject(
15526
+ new SchemaMigrationError(
15527
+ `Could not spawn dotnet. Is the .NET SDK installed and on PATH? ${err.message}`,
15528
+ err
15529
+ )
15530
+ );
15531
+ });
15532
+ child.on("close", (code) => {
15533
+ if (code === 0) {
15534
+ resolve2({ stdout, stderr });
15535
+ } else {
15536
+ reject(
15537
+ new SchemaMigrationError(
15538
+ `dotnet ${args.join(" ")} exited with code ${code}.
15539
+ stdout: ${stdout}
15540
+ stderr: ${stderr}`
15541
+ )
15542
+ );
15543
+ }
15544
+ });
15545
+ });
15546
+ }
15547
+ var restored = /* @__PURE__ */ new Set();
15548
+ async function ensureRestored(projectDir, csproj) {
15549
+ if (restored.has(projectDir)) return;
15550
+ if (fs28.existsSync(path29.join(projectDir, ".config", "dotnet-tools.json"))) {
15551
+ await spawnDotnet(projectDir, ["tool", "restore"], {});
15552
+ }
15553
+ await spawnDotnet(projectDir, ["restore", csproj], {});
15554
+ restored.add(projectDir);
15555
+ }
15556
+ async function runEf(projectDir, args, dsn, opts = {}) {
15557
+ const project = requireProject(projectDir);
15558
+ const csproj = path29.relative(projectDir, project.csproj) || project.csproj;
15559
+ await ensureRestored(projectDir, csproj);
15560
+ const full = ["ef", ...args, "--project", csproj, "--prefix-output"];
15561
+ if (opts.noBuild) full.push("--no-build");
15562
+ const env = {};
15563
+ if (dsn) {
15564
+ env.DATABASE_URL = dsn;
15565
+ if (process.env.LAKEBASE_EFCORE_CONNECTION_ARG === "1") full.push("--connection", dsnToNpgsql(dsn));
15566
+ }
15567
+ return spawnDotnet(projectDir, full, env);
15568
+ }
15569
+ async function listEfMigrations(ctx, opts = {}) {
15570
+ const { stdout, stderr } = await runEf(ctx.projectDir, ["migrations", "list", "--json"], ctx.dsn, opts);
15571
+ const entries = parseEfJson(stdout);
15572
+ if (ctx.dsn && entries.some((e) => e.applied === null)) {
15573
+ const warning = stdout.split(/\r?\n/).filter((l) => /^(warn|error):/.test(l)).join("\n");
15574
+ throw new SchemaMigrationError(
15575
+ `dotnet ef could not read migration state from the database (check connectivity and credentials).
15576
+ ${warning || stderr}`.trim()
15577
+ );
15578
+ }
15579
+ return entries;
15580
+ }
15581
+ async function readEfHistory(dsn) {
15582
+ const client = new Client3({ connectionString: dsn, application_name: connectionApplicationName() });
15583
+ await client.connect();
15584
+ try {
15585
+ const exists = await client.query(`SELECT to_regclass('"__EFMigrationsHistory"') IS NOT NULL AS present`);
15586
+ if (!exists.rows[0]?.present) return null;
15587
+ const r = await client.query(`SELECT "MigrationId" FROM "__EFMigrationsHistory" ORDER BY "MigrationId"`);
15588
+ return r.rows.map((row) => row.MigrationId);
15589
+ } finally {
15590
+ await client.end();
15591
+ }
15592
+ }
15593
+ function toApplied(entries) {
15594
+ return entries.map((e) => ({ version: efVersion(e.id), description: efDescription(e.id) }));
15595
+ }
15596
+ async function statusEfcore(ctx) {
15597
+ const entries = await listEfMigrations(ctx);
15598
+ const history = await (ctx.readHistory ?? readEfHistory)(ctx.dsn);
15599
+ const current = history && history.length > 0 ? efVersion(history[history.length - 1]) : void 0;
15600
+ const pending = entries.filter((e) => e.applied !== true).map((e) => ({ version: efVersion(e.id), filename: `${e.id}.cs`, description: efDescription(e.id) }));
15601
+ return { current, pending, tool: "efcore" };
15602
+ }
15603
+ async function applyEfcore(ctx) {
15604
+ const before = new Set((await listEfMigrations(ctx)).filter((e) => e.applied).map((e) => e.id));
15605
+ await runEf(ctx.projectDir, ["database", "update"], ctx.dsn, { noBuild: true });
15606
+ const after = await listEfMigrations(ctx, { noBuild: true });
15607
+ const applied = toApplied(after.filter((e) => e.applied && !before.has(e.id)));
15608
+ return { applied, alreadyAtLatest: applied.length === 0, tool: "efcore" };
15609
+ }
15610
+ function resolveEfTarget(target, applied) {
15611
+ const t = target.trim();
15612
+ if (t === "0") return "0";
15613
+ const rel = /^-(\d+)$/.exec(t);
15614
+ if (rel) {
15615
+ const steps = Number(rel[1]);
15616
+ if (steps < 1) throw new SchemaMigrationError(`Invalid rollback step: ${target}`);
15617
+ const index = applied.length - 1 - steps;
15618
+ if (index < -1) {
15619
+ throw new SchemaMigrationError(`Cannot roll back ${steps} migration(s); only ${applied.length} applied.`);
15620
+ }
15621
+ return index === -1 ? "0" : applied[index].id;
15622
+ }
15623
+ const match = applied.find((e) => e.id === t || efVersion(e.id) === t || e.name === t);
15624
+ if (!match) throw new SchemaMigrationError(`Rollback target '${target}' is not an applied migration.`);
15625
+ return match.id;
15626
+ }
15627
+ async function rollbackEfcore(ctx) {
15628
+ const beforeEntries = (await listEfMigrations(ctx)).filter((e) => e.applied);
15629
+ const targetId = resolveEfTarget(ctx.target, beforeEntries);
15630
+ await runEf(ctx.projectDir, ["database", "update", targetId], ctx.dsn, { noBuild: true });
15631
+ const stillApplied = new Set((await listEfMigrations(ctx, { noBuild: true })).filter((e) => e.applied).map((e) => e.id));
15632
+ return { rolledBack: toApplied(beforeEntries.filter((e) => !stillApplied.has(e.id)).reverse()), tool: "efcore" };
15633
+ }
15634
+ function efMigrationName(slug) {
15635
+ const name = slug.split(/[^A-Za-z0-9]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join("");
15636
+ if (!name) throw new SchemaMigrationError(`Cannot derive a migration name from '${slug}'.`);
15637
+ return /^[0-9]/.test(name) ? `M${name}` : name;
15638
+ }
15639
+ async function createEfcoreMigration(opts) {
15640
+ const project = requireProject(opts.projectDir);
15641
+ const name = efMigrationName(opts.slug);
15642
+ const existing = new Set(fs28.existsSync(project.migrationsDir) ? fs28.readdirSync(project.migrationsDir) : []);
15643
+ await runEf(opts.projectDir, ["migrations", "add", name]);
15644
+ const created = fs28.readdirSync(project.migrationsDir).filter((f) => !existing.has(f) && MIGRATION_ID.test(f.replace(/\.cs$/, "")) && !f.endsWith(".Designer.cs"));
15645
+ if (created.length !== 1) {
15646
+ throw new SchemaMigrationError(`Expected one new migration file for ${name}, found: ${created.join(", ") || "none"}`);
15647
+ }
15648
+ return { id: created[0].replace(/\.cs$/, ""), path: path29.join(project.migrationsDir, created[0]) };
15649
+ }
15650
+ function localEfMigrationIds(projectDir) {
15651
+ const project = findEfCoreProject(projectDir);
15652
+ if (!project || !fs28.existsSync(project.migrationsDir)) return [];
15653
+ return fs28.readdirSync(project.migrationsDir).filter((f) => f.endsWith(".cs") && !f.endsWith(".Designer.cs") && MIGRATION_ID.test(f.slice(0, -3))).map((f) => f.slice(0, -3)).sort();
15654
+ }
15655
+ function efProductVersion(project) {
15656
+ try {
15657
+ const csproj = fs28.readFileSync(project.csproj, "utf-8");
15658
+ const m = new RegExp(`${EF_DESIGN_PACKAGE.replace(/\./g, "\\.")}"\\s+Version="([^"]+)"`).exec(csproj);
15659
+ if (m) return m[1];
15660
+ } catch {
15661
+ }
15662
+ return "unknown";
15663
+ }
15664
+ async function stampEfcore(ctx) {
15665
+ const project = requireProject(ctx.projectDir);
15666
+ const ids = localEfMigrationIds(ctx.projectDir);
15667
+ if (ids.length === 0) throw new SchemaMigrationError("No local EF Core migrations to stamp.");
15668
+ const rev = ctx.revision.trim();
15669
+ const end = rev === "head" ? ids.length - 1 : ids.findIndex((id) => id === rev || efVersion(id) === rev);
15670
+ if (end < 0) throw new SchemaMigrationError(`Stamp revision '${ctx.revision}' is not a local migration.`);
15671
+ const toStamp = ids.slice(0, end + 1);
15672
+ const productVersion = efProductVersion(project);
15673
+ const client = new Client3({ connectionString: ctx.dsn, application_name: connectionApplicationName() });
15674
+ await client.connect();
15675
+ try {
15676
+ await client.query("BEGIN");
15677
+ await client.query(
15678
+ `CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" (
15679
+ "MigrationId" character varying(150) NOT NULL,
15680
+ "ProductVersion" character varying(32) NOT NULL,
15681
+ CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId"))`
15682
+ );
15683
+ await client.query(`DELETE FROM "__EFMigrationsHistory" WHERE NOT ("MigrationId" = ANY($1::text[]))`, [toStamp]);
15684
+ for (const id of toStamp) {
15685
+ await client.query(
15686
+ `INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") VALUES ($1, $2)
15687
+ ON CONFLICT ("MigrationId") DO NOTHING`,
15688
+ [id, productVersion]
15689
+ );
15690
+ }
15691
+ await client.query("COMMIT");
15692
+ } catch (err) {
15693
+ await client.query("ROLLBACK").catch(() => void 0);
15694
+ throw err;
15695
+ } finally {
15696
+ await client.end();
15697
+ }
15698
+ return efVersion(toStamp[toStamp.length - 1]);
15699
+ }
15700
+
15701
+ // scripts/lakebase/adapters/efcore-adapter.ts
15702
+ async function buildDsn5(args) {
15703
+ const result = await getConnection({
15704
+ output: "dsn",
15705
+ instance: args.instance,
15706
+ branch: args.branch,
15707
+ database: args.database,
15708
+ endpointName: args.endpointName
15709
+ });
15710
+ return result.url;
15711
+ }
15712
+ function errMsg(err) {
15713
+ return err instanceof Error ? err.message : String(err);
15714
+ }
15715
+ function listEfCoreFiles(projectDir) {
15716
+ return localEfMigrationIds(projectDir).map((id) => ({
15717
+ version: efVersion(id),
15718
+ filename: `${id}.cs`,
15719
+ description: efDescription(id),
15720
+ type: "CSharp",
15721
+ tool: "efcore"
15722
+ }));
15723
+ }
15724
+ var EfCoreAdapter = {
15725
+ id: "efcore",
15726
+ languages: ["dotnet"],
15727
+ /** A .NET project (root marker) with a *.csproj that references Microsoft.EntityFrameworkCore.Design. */
15728
+ detect(projectDir) {
15729
+ return isDotnetProject(projectDir) && findEfCoreProject(projectDir) !== null;
15730
+ },
15731
+ async apply(args) {
15732
+ try {
15733
+ const dsn = await buildDsn5(args);
15734
+ const r = await applyEfcore({ projectDir: args.projectDir, dsn });
15735
+ return {
15736
+ applied_migrations: r.applied,
15737
+ status: r.alreadyAtLatest ? "noop" : "ok",
15738
+ tool_specific: { alreadyAtLatest: r.alreadyAtLatest, tool: r.tool }
15739
+ };
15740
+ } catch (err) {
15741
+ return { applied_migrations: [], status: "error", error: errMsg(err) };
15742
+ }
15743
+ },
15744
+ async rollback(args) {
15745
+ try {
15746
+ const dsn = await buildDsn5(args);
15747
+ const r = await rollbackEfcore({ projectDir: args.projectDir, dsn, target: args.target });
15748
+ return {
15749
+ rolled_back: r.rolledBack,
15750
+ status: r.rolledBack.length === 0 ? "noop" : "ok",
15751
+ tool_specific: { tool: r.tool }
15752
+ };
15753
+ } catch (err) {
15754
+ return { rolled_back: [], status: "error", error: errMsg(err) };
15755
+ }
15756
+ },
15757
+ async stamp(args) {
15758
+ try {
15759
+ const dsn = await buildDsn5(args);
15760
+ const stamped = await stampEfcore({ projectDir: args.projectDir, dsn, revision: args.revision });
15761
+ return { status: "ok", stamped_revision: stamped, tool_specific: { tool: "efcore" } };
15762
+ } catch (err) {
15763
+ return { status: "error", stamped_revision: null, error: errMsg(err) };
15764
+ }
15765
+ },
15766
+ async status(args) {
15767
+ try {
15768
+ const dsn = await buildDsn5(args);
15769
+ const r = await statusEfcore({ projectDir: args.projectDir, dsn });
15770
+ return {
15771
+ applied_version: r.current ?? null,
15772
+ pending: r.pending,
15773
+ applied: [],
15774
+ status: "ok",
15775
+ tool_specific: { tool: r.tool }
15776
+ };
15777
+ } catch (err) {
15778
+ return { applied_version: null, pending: [], applied: [], status: "error", error: errMsg(err) };
15779
+ }
15780
+ },
15781
+ async list(args) {
15782
+ return { files: listEfCoreFiles(args.projectDir) };
15783
+ },
15784
+ // baseline: use stamp (EF has no separate baseline concept).
15785
+ async newMigration(args) {
15786
+ try {
15787
+ const created = await createEfcoreMigration({ projectDir: args.projectDir, slug: args.slug });
15788
+ return {
15789
+ status: "ok",
15790
+ version: efVersion(created.id),
15791
+ filename: path30.basename(created.path),
15792
+ path: created.path
15793
+ };
15794
+ } catch (err) {
15795
+ return { status: "error", version: "", filename: "", path: "", error: errMsg(err) };
15796
+ }
15797
+ }
15798
+ };
15799
+ registerSchemaMigrationAdapter(EfCoreAdapter);
15800
+
15240
15801
  // scripts/lakebase/schema-migrate.ts
15241
15802
  var SchemaMigrationError = class extends Error {
15242
15803
  constructor(message, cause) {
@@ -15250,13 +15811,16 @@ function detectLanguage(projectDir) {
15250
15811
  const lang = resolveMigrationLanguage(projectDir);
15251
15812
  if (lang === "unknown") {
15252
15813
  throw new SchemaMigrationError(
15253
- `Could not detect project language in ${projectDir}. Expected one of: pom.xml (java/kotlin), pyproject.toml or alembic.ini (python), package.json (nodejs). Pass {language} explicitly to override.`
15814
+ `Could not detect project language in ${projectDir}. Expected one of: pom.xml (java/kotlin), pyproject.toml or alembic.ini (python), package.json (nodejs), *.sln or *.csproj (dotnet). Pass {language} explicitly to override.`
15254
15815
  );
15255
15816
  }
15256
15817
  return lang;
15257
15818
  }
15258
- function toolForLanguage(language) {
15819
+ function toolForLanguage(language, projectDir) {
15259
15820
  switch (language) {
15821
+ case "dotnet":
15822
+ if (projectDir && isDotnetFlywayProject(projectDir)) return "flyway";
15823
+ return "efcore";
15260
15824
  case "java":
15261
15825
  case "kotlin":
15262
15826
  return "flyway";
@@ -15269,8 +15833,10 @@ function toolForLanguage(language) {
15269
15833
  function listSchemaMigrations(args = {}) {
15270
15834
  const projectDir = args.projectDir ?? process.cwd();
15271
15835
  const language = args.language ?? detectLanguage(projectDir);
15272
- const tool = toolForLanguage(language);
15836
+ const tool = toolForLanguage(language, projectDir);
15273
15837
  switch (tool) {
15838
+ case "efcore":
15839
+ return listEfCoreFiles(projectDir);
15274
15840
  case "flyway":
15275
15841
  return listFlywayMigrations(projectDir);
15276
15842
  case "alembic":
@@ -15280,9 +15846,9 @@ function listSchemaMigrations(args = {}) {
15280
15846
  }
15281
15847
  }
15282
15848
  function listFlywayMigrations(projectDir) {
15283
- const dir = path27.join(projectDir, "src", "main", "resources", "db", "migration");
15284
- if (!fs26.existsSync(dir)) return [];
15285
- const files = fs26.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
15849
+ const dir = flywayMigrationsDir(projectDir);
15850
+ if (!fs29.existsSync(dir)) return [];
15851
+ const files = fs29.readdirSync(dir).filter((f) => /^V\d+(\.\d+)*__.+\.sql$/.test(f));
15286
15852
  return files.map((filename) => {
15287
15853
  const m = filename.match(/^V(\d+(?:\.\d+)*)__(.+)\.sql$/);
15288
15854
  const version = m[1];
@@ -15292,12 +15858,12 @@ function listFlywayMigrations(projectDir) {
15292
15858
  }
15293
15859
  function listAlembicMigrations(projectDir) {
15294
15860
  const candidates = [
15295
- path27.join(projectDir, "migrations", "versions"),
15296
- path27.join(projectDir, "alembic", "versions")
15861
+ path31.join(projectDir, "migrations", "versions"),
15862
+ path31.join(projectDir, "alembic", "versions")
15297
15863
  ];
15298
- const dir = candidates.find((p) => fs26.existsSync(p));
15864
+ const dir = candidates.find((p) => fs29.existsSync(p));
15299
15865
  if (!dir) return [];
15300
- const files = fs26.readdirSync(dir).filter((f) => f.endsWith(".py") && !f.startsWith("__"));
15866
+ const files = fs29.readdirSync(dir).filter((f) => f.endsWith(".py") && !f.startsWith("__"));
15301
15867
  return files.map((filename) => {
15302
15868
  const stem = filename.replace(/\.py$/, "");
15303
15869
  const sep4 = stem.indexOf("_");
@@ -15307,9 +15873,9 @@ function listAlembicMigrations(projectDir) {
15307
15873
  }).sort((a, b) => a.filename.localeCompare(b.filename));
15308
15874
  }
15309
15875
  function listKnexMigrations(projectDir) {
15310
- const dir = path27.join(projectDir, "migrations");
15311
- if (!fs26.existsSync(dir)) return [];
15312
- const files = fs26.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
15876
+ const dir = path31.join(projectDir, "migrations");
15877
+ if (!fs29.existsSync(dir)) return [];
15878
+ const files = fs29.readdirSync(dir).filter((f) => (f.endsWith(".js") || f.endsWith(".ts")) && !f.startsWith("."));
15313
15879
  return files.map((filename) => {
15314
15880
  const stem = filename.replace(/\.(js|ts)$/, "");
15315
15881
  const m = stem.match(/^(\d{14})_(.+)$/);
@@ -15331,7 +15897,7 @@ function versionCompare2(a, b) {
15331
15897
  return 0;
15332
15898
  }
15333
15899
  function adapterFor(projectDir, language) {
15334
- const override = language ? toolForLanguage(language) : void 0;
15900
+ const override = language ? toolForLanguage(language, projectDir) : void 0;
15335
15901
  return resolveSchemaMigrationAdapter(projectDir, override);
15336
15902
  }
15337
15903
  var TierMigrationRefusedError = class extends Error {
@@ -15485,8 +16051,8 @@ async function runInfraSuite(args) {
15485
16051
  duration_ms: Date.now() - start
15486
16052
  };
15487
16053
  if (args.junitOutput) {
15488
- fs27.mkdirSync(path28.dirname(args.junitOutput), { recursive: true });
15489
- fs27.writeFileSync(args.junitOutput, formatJUnit(result), "utf8");
16054
+ fs30.mkdirSync(path32.dirname(args.junitOutput), { recursive: true });
16055
+ fs30.writeFileSync(args.junitOutput, formatJUnit(result), "utf8");
15490
16056
  }
15491
16057
  return result;
15492
16058
  }
@@ -15532,8 +16098,8 @@ function escapeXml(s) {
15532
16098
 
15533
16099
  // scripts/lakebase/scm-claim-feature.ts
15534
16100
  init_esm_shims();
15535
- import * as fs28 from "fs";
15536
- import * as path29 from "path";
16101
+ import * as fs31 from "fs";
16102
+ import * as path33 from "path";
15537
16103
  var ScmClaimError = class extends Error {
15538
16104
  constructor(message, code) {
15539
16105
  super(message);
@@ -15590,7 +16156,7 @@ async function claimFeatureBranch(args) {
15590
16156
  const current = readWorkflowState(args.projectDir);
15591
16157
  if (!current) {
15592
16158
  throw new ScmClaimError(
15593
- `No SCM workflow state found at ${path29.join(args.projectDir, ".lakebase/workflow-state.json")}. Run lakebase-create-project to scaffold, or re-seed via the substrate.`,
16159
+ `No SCM workflow state found at ${path33.join(args.projectDir, ".lakebase/workflow-state.json")}. Run lakebase-create-project to scaffold, or re-seed via the substrate.`,
15594
16160
  "no-state-file"
15595
16161
  );
15596
16162
  }
@@ -15694,8 +16260,8 @@ function alreadyClaimedSentinel(state) {
15694
16260
  };
15695
16261
  }
15696
16262
  function workflowStateFileExists(projectDir) {
15697
- return fs28.existsSync(
15698
- path29.join(projectDir, ".lakebase/workflow-state.json")
16263
+ return fs31.existsSync(
16264
+ path33.join(projectDir, ".lakebase/workflow-state.json")
15699
16265
  );
15700
16266
  }
15701
16267
 
@@ -16217,12 +16783,12 @@ async function sync(args) {
16217
16783
 
16218
16784
  // scripts/lakebase/workflow-drift.ts
16219
16785
  init_esm_shims();
16220
- import * as fs29 from "fs";
16221
- import * as path30 from "path";
16786
+ import * as fs32 from "fs";
16787
+ import * as path34 from "path";
16222
16788
  function findKitTemplatesDir(start) {
16223
16789
  let dir = start;
16224
16790
  for (let i = 0; i < 6; i++) {
16225
- const candidate = path30.join(
16791
+ const candidate = path34.join(
16226
16792
  dir,
16227
16793
  "templates",
16228
16794
  "project",
@@ -16230,8 +16796,8 @@ function findKitTemplatesDir(start) {
16230
16796
  ".github",
16231
16797
  "workflows"
16232
16798
  );
16233
- if (fs29.existsSync(candidate)) return candidate;
16234
- const parent = path30.dirname(dir);
16799
+ if (fs32.existsSync(candidate)) return candidate;
16800
+ const parent = path34.dirname(dir);
16235
16801
  if (parent === dir) break;
16236
16802
  dir = parent;
16237
16803
  }
@@ -16255,13 +16821,13 @@ function unifiedDiff(name, projectContent, templateContent) {
16255
16821
  return out.join("\n");
16256
16822
  }
16257
16823
  function detectWorkflowDrift(args) {
16258
- const projectWorkflowsDir = path30.join(
16824
+ const projectWorkflowsDir = path34.join(
16259
16825
  args.projectDir,
16260
16826
  ".github",
16261
16827
  "workflows"
16262
16828
  );
16263
- const here = path30.dirname(new URL(import.meta.url).pathname);
16264
- const kitWorkflowsDir = args.kitDir ? path30.join(
16829
+ const here = path34.dirname(new URL(import.meta.url).pathname);
16830
+ const kitWorkflowsDir = args.kitDir ? path34.join(
16265
16831
  args.kitDir,
16266
16832
  "templates",
16267
16833
  "project",
@@ -16269,21 +16835,21 @@ function detectWorkflowDrift(args) {
16269
16835
  ".github",
16270
16836
  "workflows"
16271
16837
  ) : findKitTemplatesDir(here);
16272
- const templateFiles = fs29.existsSync(kitWorkflowsDir) ? fs29.readdirSync(kitWorkflowsDir).filter((f) => f.endsWith(".yml")) : [];
16273
- const projectFiles = fs29.existsSync(projectWorkflowsDir) ? fs29.readdirSync(projectWorkflowsDir).filter((f) => f.endsWith(".yml")) : [];
16838
+ const templateFiles = fs32.existsSync(kitWorkflowsDir) ? fs32.readdirSync(kitWorkflowsDir).filter((f) => f.endsWith(".yml")) : [];
16839
+ const projectFiles = fs32.existsSync(projectWorkflowsDir) ? fs32.readdirSync(projectWorkflowsDir).filter((f) => f.endsWith(".yml")) : [];
16274
16840
  const version = readKitVersion(kitWorkflowsDir);
16275
16841
  const seen = /* @__PURE__ */ new Set();
16276
16842
  const files = [];
16277
16843
  for (const name of templateFiles) {
16278
16844
  seen.add(name);
16279
- const projectPath2 = path30.join(projectWorkflowsDir, name);
16280
- const templatePath = path30.join(kitWorkflowsDir, name);
16281
- if (!fs29.existsSync(projectPath2)) {
16845
+ const projectPath2 = path34.join(projectWorkflowsDir, name);
16846
+ const templatePath = path34.join(kitWorkflowsDir, name);
16847
+ if (!fs32.existsSync(projectPath2)) {
16282
16848
  files.push({ name, status: "missing" });
16283
16849
  continue;
16284
16850
  }
16285
- const projectContent = fs29.readFileSync(projectPath2, "utf8");
16286
- const templateContent = applyPlaceholders(fs29.readFileSync(templatePath, "utf8"), version);
16851
+ const projectContent = fs32.readFileSync(projectPath2, "utf8");
16852
+ const templateContent = applyPlaceholders(fs32.readFileSync(templatePath, "utf8"), version);
16287
16853
  if (projectContent === templateContent) {
16288
16854
  files.push({ name, status: "unchanged" });
16289
16855
  } else {
@@ -16314,10 +16880,10 @@ function detectWorkflowDrift(args) {
16314
16880
  function readKitVersion(kitWorkflowsDir) {
16315
16881
  let dir = kitWorkflowsDir;
16316
16882
  for (let i = 0; i < 5; i++) {
16317
- dir = path30.dirname(dir);
16883
+ dir = path34.dirname(dir);
16318
16884
  }
16319
16885
  try {
16320
- const raw = fs29.readFileSync(path30.join(dir, "package.json"), "utf-8");
16886
+ const raw = fs32.readFileSync(path34.join(dir, "package.json"), "utf-8");
16321
16887
  const pkg = JSON.parse(raw);
16322
16888
  return typeof pkg.version === "string" ? pkg.version : "unknown";
16323
16889
  } catch {
@@ -16334,7 +16900,7 @@ var COMMAND_HOOK_FILE_PATTERN = /\.(pre|post)-hook\.md$/;
16334
16900
  function findKitCommandsDir(start) {
16335
16901
  let dir = start;
16336
16902
  for (let i = 0; i < 6; i++) {
16337
- const candidate = path30.join(
16903
+ const candidate = path34.join(
16338
16904
  dir,
16339
16905
  "templates",
16340
16906
  "project",
@@ -16342,8 +16908,8 @@ function findKitCommandsDir(start) {
16342
16908
  ".claude",
16343
16909
  "commands"
16344
16910
  );
16345
- if (fs29.existsSync(candidate)) return candidate;
16346
- const parent = path30.dirname(dir);
16911
+ if (fs32.existsSync(candidate)) return candidate;
16912
+ const parent = path34.dirname(dir);
16347
16913
  if (parent === dir) break;
16348
16914
  dir = parent;
16349
16915
  }
@@ -16356,24 +16922,24 @@ function parsePinnedVersion(content) {
16356
16922
  return m ? m[1] : void 0;
16357
16923
  }
16358
16924
  function detectCommandDrift(args) {
16359
- const projectCommandsDir = path30.join(args.projectDir, ".claude", "commands");
16360
- const here = path30.dirname(new URL(import.meta.url).pathname);
16361
- const kitCommandsDir = args.kitDir ? path30.join(args.kitDir, "templates", "project", "common", ".claude", "commands") : findKitCommandsDir(here);
16925
+ const projectCommandsDir = path34.join(args.projectDir, ".claude", "commands");
16926
+ const here = path34.dirname(new URL(import.meta.url).pathname);
16927
+ const kitCommandsDir = args.kitDir ? path34.join(args.kitDir, "templates", "project", "common", ".claude", "commands") : findKitCommandsDir(here);
16362
16928
  const kitVersion = readKitVersionFromCommandsDir(kitCommandsDir);
16363
- const templateFiles = fs29.existsSync(kitCommandsDir) ? fs29.readdirSync(kitCommandsDir).filter((f) => f.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f)) : [];
16364
- const projectFiles = fs29.existsSync(projectCommandsDir) ? fs29.readdirSync(projectCommandsDir).filter((f) => f.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f)) : [];
16929
+ const templateFiles = fs32.existsSync(kitCommandsDir) ? fs32.readdirSync(kitCommandsDir).filter((f) => f.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f)) : [];
16930
+ const projectFiles = fs32.existsSync(projectCommandsDir) ? fs32.readdirSync(projectCommandsDir).filter((f) => f.endsWith(".md") && !COMMAND_HOOK_FILE_PATTERN.test(f)) : [];
16365
16931
  const seen = /* @__PURE__ */ new Set();
16366
16932
  const files = [];
16367
16933
  for (const name of templateFiles) {
16368
16934
  seen.add(name);
16369
- const projectPath2 = path30.join(projectCommandsDir, name);
16370
- const templatePath = path30.join(kitCommandsDir, name);
16371
- const templateRaw = fs29.readFileSync(templatePath, "utf8");
16372
- if (!fs29.existsSync(projectPath2)) {
16935
+ const projectPath2 = path34.join(projectCommandsDir, name);
16936
+ const templatePath = path34.join(kitCommandsDir, name);
16937
+ const templateRaw = fs32.readFileSync(templatePath, "utf8");
16938
+ if (!fs32.existsSync(projectPath2)) {
16373
16939
  files.push({ name, status: "missing", kit_version: kitVersion });
16374
16940
  continue;
16375
16941
  }
16376
- const projectContent = fs29.readFileSync(projectPath2, "utf8");
16942
+ const projectContent = fs32.readFileSync(projectPath2, "utf8");
16377
16943
  const pinned = parsePinnedVersion(projectContent);
16378
16944
  const versionForCompare = pinned ?? kitVersion;
16379
16945
  const templateContent = applyCommandPlaceholders(templateRaw, versionForCompare);
@@ -16411,10 +16977,10 @@ function detectCommandDrift(args) {
16411
16977
  function readKitVersionFromCommandsDir(kitCommandsDir) {
16412
16978
  let dir = kitCommandsDir;
16413
16979
  for (let i = 0; i < 5; i++) {
16414
- dir = path30.dirname(dir);
16980
+ dir = path34.dirname(dir);
16415
16981
  }
16416
16982
  try {
16417
- const raw = fs29.readFileSync(path30.join(dir, "package.json"), "utf-8");
16983
+ const raw = fs32.readFileSync(path34.join(dir, "package.json"), "utf-8");
16418
16984
  const pkg = JSON.parse(raw);
16419
16985
  return typeof pkg.version === "string" ? pkg.version : "unknown";
16420
16986
  } catch {
@@ -16431,13 +16997,13 @@ function detectScaffoldedDrift(args) {
16431
16997
  };
16432
16998
  }
16433
16999
  function updateWorkflows(args) {
16434
- const projectWorkflowsDir = path30.join(
17000
+ const projectWorkflowsDir = path34.join(
16435
17001
  args.projectDir,
16436
17002
  ".github",
16437
17003
  "workflows"
16438
17004
  );
16439
- const here = path30.dirname(new URL(import.meta.url).pathname);
16440
- const kitWorkflowsDir = args.kitDir ? path30.join(
17005
+ const here = path34.dirname(new URL(import.meta.url).pathname);
17006
+ const kitWorkflowsDir = args.kitDir ? path34.join(
16441
17007
  args.kitDir,
16442
17008
  "templates",
16443
17009
  "project",
@@ -16448,22 +17014,22 @@ function updateWorkflows(args) {
16448
17014
  const substitute = args.substitute !== false;
16449
17015
  const dryRun = args.dryRun === true;
16450
17016
  const pruneExtras = args.pruneExtras === true;
16451
- const templateFiles = fs29.existsSync(kitWorkflowsDir) ? fs29.readdirSync(kitWorkflowsDir).filter((f) => f.endsWith(".yml")) : [];
16452
- const projectFiles = fs29.existsSync(projectWorkflowsDir) ? fs29.readdirSync(projectWorkflowsDir).filter((f) => f.endsWith(".yml")) : [];
16453
- if (!dryRun && templateFiles.length > 0 && !fs29.existsSync(projectWorkflowsDir)) {
16454
- fs29.mkdirSync(projectWorkflowsDir, { recursive: true });
17017
+ const templateFiles = fs32.existsSync(kitWorkflowsDir) ? fs32.readdirSync(kitWorkflowsDir).filter((f) => f.endsWith(".yml")) : [];
17018
+ const projectFiles = fs32.existsSync(projectWorkflowsDir) ? fs32.readdirSync(projectWorkflowsDir).filter((f) => f.endsWith(".yml")) : [];
17019
+ if (!dryRun && templateFiles.length > 0 && !fs32.existsSync(projectWorkflowsDir)) {
17020
+ fs32.mkdirSync(projectWorkflowsDir, { recursive: true });
16455
17021
  }
16456
17022
  const version = substitute ? readKitVersion(kitWorkflowsDir) : "";
16457
17023
  const seen = /* @__PURE__ */ new Set();
16458
17024
  const files = [];
16459
17025
  for (const name of templateFiles) {
16460
17026
  seen.add(name);
16461
- const projectPath2 = path30.join(projectWorkflowsDir, name);
16462
- const templatePath = path30.join(kitWorkflowsDir, name);
16463
- const templateRaw = fs29.readFileSync(templatePath, "utf-8");
17027
+ const projectPath2 = path34.join(projectWorkflowsDir, name);
17028
+ const templatePath = path34.join(kitWorkflowsDir, name);
17029
+ const templateRaw = fs32.readFileSync(templatePath, "utf-8");
16464
17030
  const desired = substitute ? applyPlaceholders(templateRaw, version) : templateRaw;
16465
- const existed = fs29.existsSync(projectPath2);
16466
- const current = existed ? fs29.readFileSync(projectPath2, "utf-8") : "";
17031
+ const existed = fs32.existsSync(projectPath2);
17032
+ const current = existed ? fs32.readFileSync(projectPath2, "utf-8") : "";
16467
17033
  let outcome;
16468
17034
  if (!existed) {
16469
17035
  outcome = "added";
@@ -16473,16 +17039,16 @@ function updateWorkflows(args) {
16473
17039
  outcome = "updated";
16474
17040
  }
16475
17041
  if (!dryRun && outcome !== "unchanged") {
16476
- fs29.writeFileSync(projectPath2, desired);
17042
+ fs32.writeFileSync(projectPath2, desired);
16477
17043
  }
16478
17044
  files.push({ name, outcome });
16479
17045
  }
16480
17046
  if (pruneExtras) {
16481
17047
  for (const name of projectFiles) {
16482
17048
  if (seen.has(name)) continue;
16483
- const projectPath2 = path30.join(projectWorkflowsDir, name);
17049
+ const projectPath2 = path34.join(projectWorkflowsDir, name);
16484
17050
  if (!dryRun) {
16485
- fs29.unlinkSync(projectPath2);
17051
+ fs32.unlinkSync(projectPath2);
16486
17052
  }
16487
17053
  files.push({ name, outcome: "removed" });
16488
17054
  }
@@ -17115,15 +17681,15 @@ function parentForTopology(t, defaultLeaf) {
17115
17681
 
17116
17682
  // scripts/lakebase/scm-doctor.ts
17117
17683
  init_esm_shims();
17118
- import * as fs30 from "fs";
17119
- import * as path31 from "path";
17684
+ import * as fs33 from "fs";
17685
+ import * as path35 from "path";
17120
17686
  var FEATURE_PREFIX = "feature/";
17121
17687
  var TIER_LEAFS2 = DEFAULT_PROTECTED_TIER_NAMES;
17122
17688
  function readEnv(projectDir) {
17123
- const envPath = path31.join(projectDir, ".env");
17689
+ const envPath = path35.join(projectDir, ".env");
17124
17690
  const out = /* @__PURE__ */ new Map();
17125
- if (!fs30.existsSync(envPath)) return out;
17126
- const lines = fs30.readFileSync(envPath, "utf8").split("\n");
17691
+ if (!fs33.existsSync(envPath)) return out;
17692
+ const lines = fs33.readFileSync(envPath, "utf8").split("\n");
17127
17693
  for (const line of lines) {
17128
17694
  const m = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.+?)\s*$/);
17129
17695
  if (m) out.set(m[1], m[2].replace(/^["']|["']$/g, ""));
@@ -17178,9 +17744,9 @@ async function runDoctor(args, deps = {}) {
17178
17744
  }
17179
17745
  for (const wf of ["pr.yml", "merge.yml"]) {
17180
17746
  try {
17181
- const p = path31.join(projectDir, ".github", "workflows", wf);
17182
- if (!fs30.existsSync(p)) continue;
17183
- const body = fs30.readFileSync(p, "utf8");
17747
+ const p = path35.join(projectDir, ".github", "workflows", wf);
17748
+ if (!fs33.existsSync(p)) continue;
17749
+ const body = fs33.readFileSync(p, "utf8");
17184
17750
  if (/lakebase-scm-utils#v\d/.test(body)) {
17185
17751
  findings.push({
17186
17752
  id: "ci-workflow-substrate-pin",
@@ -17395,7 +17961,7 @@ async function fixFinding(args) {
17395
17961
  );
17396
17962
  }
17397
17963
  const sanitized = sanitizeBranchName(branch);
17398
- const envFile = path31.join(args.projectDir, ".env");
17964
+ const envFile = path35.join(args.projectDir, ".env");
17399
17965
  updateEnvConnection({
17400
17966
  envPath: envFile,
17401
17967
  projectId: readEnvVar(envFile, "LAKEBASE_PROJECT_ID") ?? "",
@@ -17497,14 +18063,14 @@ function shellEscape3(s) {
17497
18063
 
17498
18064
  // scripts/lakebase/doctor.ts
17499
18065
  init_esm_shims();
17500
- import * as fs31 from "fs";
17501
- import * as path32 from "path";
18066
+ import * as fs34 from "fs";
18067
+ import * as path36 from "path";
17502
18068
  import * as cp6 from "child_process";
17503
18069
  function readEnvFile(projectDir) {
17504
- const envPath = path32.join(projectDir, ".env");
17505
- if (!fs31.existsSync(envPath)) return {};
18070
+ const envPath = path36.join(projectDir, ".env");
18071
+ if (!fs34.existsSync(envPath)) return {};
17506
18072
  const out = {};
17507
- for (const line of fs31.readFileSync(envPath, "utf8").split("\n")) {
18073
+ for (const line of fs34.readFileSync(envPath, "utf8").split("\n")) {
17508
18074
  const m = line.match(/^\s*([A-Z][A-Z0-9_]*)\s*=\s*(.*)\s*$/);
17509
18075
  if (!m) continue;
17510
18076
  let val = m[2];
@@ -17658,6 +18224,23 @@ async function checkPrereq(spec, run) {
17658
18224
  detail: { version: trimmed }
17659
18225
  };
17660
18226
  }
18227
+ var DOTNET_PREREQ = {
18228
+ name: "dotnet-sdk",
18229
+ cmd: "dotnet",
18230
+ versionArgs: ["--version"],
18231
+ minMajor: 10,
18232
+ label: ".NET SDK",
18233
+ hint: "Install the .NET 10 SDK (e.g. `brew install --cask dotnet-sdk` or https://dot.net). EF Core's dotnet-ef is restored per project from .config/dotnet-tools.json."
18234
+ };
18235
+ async function checkLanguagePrerequisites(projectDir, run = defaultVersionRunner) {
18236
+ let lang;
18237
+ try {
18238
+ lang = detectLanguage(projectDir);
18239
+ } catch {
18240
+ return [];
18241
+ }
18242
+ return lang === "dotnet" ? [await checkPrereq(DOTNET_PREREQ, run)] : [];
18243
+ }
17661
18244
  async function checkPrerequisites(run = defaultVersionRunner) {
17662
18245
  return Promise.all(PREREQS.map((spec) => checkPrereq(spec, run)));
17663
18246
  }
@@ -17759,7 +18342,7 @@ function checkEnv(projectDir) {
17759
18342
  name: "env-file",
17760
18343
  status: "warn",
17761
18344
  message: ".env not found",
17762
- detail: { projectDir, envPath: path32.join(projectDir, ".env") },
18345
+ detail: { projectDir, envPath: path36.join(projectDir, ".env") },
17763
18346
  hint: "Run `lakebase-branch sync-env`, or `git checkout <branch>` so the post-checkout hook writes .env (or copy .env.example). The kit stores connection METADATA only , no DB token (the app mints a short-lived credential at runtime)."
17764
18347
  };
17765
18348
  }
@@ -17989,6 +18572,7 @@ async function runDoctor2(args = {}) {
17989
18572
  const hooks = checkHooks(projectDir);
17990
18573
  const workflowDrift = checkWorkflowDrift(projectDir);
17991
18574
  const prereqs = await checkPrerequisites();
18575
+ const languagePrereqs = await checkLanguagePrerequisites(projectDir);
17992
18576
  const lakebaseEnabled = auth7.status === "ok" ? await checkLakebaseEnabled(profile) : {
17993
18577
  name: "lakebase-enabled",
17994
18578
  status: "skip",
@@ -17997,6 +18581,7 @@ async function runDoctor2(args = {}) {
17997
18581
  const checks = [
17998
18582
  cli,
17999
18583
  ...prereqs,
18584
+ ...languagePrereqs,
18000
18585
  auth7,
18001
18586
  identity,
18002
18587
  lakebaseEnabled,
@@ -18252,8 +18837,8 @@ async function listMigrationsOnBranch(args) {
18252
18837
 
18253
18838
  // scripts/git/commits.ts
18254
18839
  init_esm_shims();
18255
- import { existsSync as existsSync30 } from "fs";
18256
- import { join as join34 } from "path";
18840
+ import { existsSync as existsSync33 } from "fs";
18841
+ import { join as join36 } from "path";
18257
18842
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
18258
18843
  ".py",
18259
18844
  ".ts",
@@ -18356,7 +18941,7 @@ async function commitAllIfChanged(args) {
18356
18941
  await exec2("git add -A", { cwd: args.cwd });
18357
18942
  }
18358
18943
  for (const inc of args.include ?? []) {
18359
- if (existsSync30(join34(args.cwd, inc))) {
18944
+ if (existsSync33(join36(args.cwd, inc))) {
18360
18945
  await exec2(`git add -f -- ${shq(inc)}`, { cwd: args.cwd });
18361
18946
  }
18362
18947
  }
@@ -18498,14 +19083,14 @@ async function stashDropAll(args) {
18498
19083
 
18499
19084
  // scripts/git/rebase.ts
18500
19085
  init_esm_shims();
18501
- import * as fs32 from "fs";
18502
- import * as path33 from "path";
19086
+ import * as fs35 from "fs";
19087
+ import * as path37 from "path";
18503
19088
  async function abortRebase(args) {
18504
19089
  await exec2("git rebase --abort", { cwd: args.cwd });
18505
19090
  }
18506
19091
  async function isRebasing(args) {
18507
19092
  try {
18508
- return fs32.existsSync(path33.join(args.cwd, ".git/rebase-merge")) || fs32.existsSync(path33.join(args.cwd, ".git/rebase-apply"));
19093
+ return fs35.existsSync(path37.join(args.cwd, ".git/rebase-merge")) || fs35.existsSync(path37.join(args.cwd, ".git/rebase-apply"));
18509
19094
  } catch {
18510
19095
  return false;
18511
19096
  }
@@ -18684,47 +19269,14 @@ function isCliEntry(importMetaUrl) {
18684
19269
  }
18685
19270
  return invokedResolved === moduleResolved;
18686
19271
  }
18687
-
18688
- // scripts/util/proxy-env.ts
18689
- init_esm_shims();
18690
- var PROXY_ENV_KEYS = [
18691
- "HTTP_PROXY",
18692
- "HTTPS_PROXY",
18693
- "NO_PROXY",
18694
- "http_proxy",
18695
- "https_proxy",
18696
- "no_proxy",
18697
- "npm_config_proxy",
18698
- "npm_config_https_proxy",
18699
- "npm_config_no_proxy",
18700
- "npm_config_registry"
18701
- ];
18702
- function proxyEnvSubset(source = process.env) {
18703
- const out = {};
18704
- for (const key of PROXY_ENV_KEYS) {
18705
- const v = source[key];
18706
- if (v !== void 0 && v !== "") {
18707
- out[key] = v;
18708
- }
18709
- }
18710
- return out;
18711
- }
18712
- function withProxyEnv(base = {}) {
18713
- const out = {};
18714
- const inherited = proxyEnvSubset();
18715
- for (const [k, v] of Object.entries(inherited)) {
18716
- out[k] = v;
18717
- }
18718
- for (const [k, v] of Object.entries(base)) {
18719
- if (v !== void 0) out[k] = v;
18720
- }
18721
- return out;
18722
- }
18723
19272
  export {
18724
19273
  CI_TOKEN_LIFETIME_SECONDS,
18725
19274
  CI_TOKEN_REMINT_MARGIN_SECONDS,
18726
19275
  CONVENTION_TIER_DEFAULTS,
18727
19276
  DEFAULT_PROTECTED_TIER_NAMES,
19277
+ DOTNET_EFCORE_DEFAULTS,
19278
+ DOTNET_FLYWAY_DIR,
19279
+ EF_DESIGN_PACKAGE,
18728
19280
  FIXABLE_FINDING_IDS,
18729
19281
  GITHUB_SCOPES,
18730
19282
  GitHubPullRequestError,
@@ -18737,6 +19289,7 @@ export {
18737
19289
  LakebaseBranchError,
18738
19290
  LakebaseBranchTtlTooLongError,
18739
19291
  LakebaseProjectError,
19292
+ MAVEN_FLYWAY_DIR,
18740
19293
  MIGRATION_DEFAULTS,
18741
19294
  NODE_E2E_TEMPLATE_FILES,
18742
19295
  PLAYWRIGHT_TEMPLATE_FILES,
@@ -18873,9 +19426,12 @@ export {
18873
19426
  fastForwardBranch,
18874
19427
  featureBranchName,
18875
19428
  fetch2 as fetch,
19429
+ findCsprojFiles,
18876
19430
  findDefaultBranchName,
19431
+ findEfCoreProject,
18877
19432
  findHistoryRetentionDuration,
18878
19433
  fixFinding,
19434
+ flywayMigrationsDir,
18879
19435
  formatJUnit,
18880
19436
  formatOwnerRepo,
18881
19437
  formatSchemaDiffAsMarkdown,
@@ -18925,6 +19481,7 @@ export {
18925
19481
  gitInit,
18926
19482
  grantLakebasePermission,
18927
19483
  grantUcCatalogPermission,
19484
+ hasDotnetMarker,
18928
19485
  hasRemoteBranch,
18929
19486
  hasUpstream,
18930
19487
  inferTierTopology,
@@ -18934,6 +19491,8 @@ export {
18934
19491
  isAllSchemas,
18935
19492
  isCliEntry,
18936
19493
  isDirty,
19494
+ isDotnetFlywayProject,
19495
+ isDotnetProject,
18937
19496
  isForeignFeatureClaim,
18938
19497
  isGitTracked,
18939
19498
  isLongRunningTierBranch,