@databricks-solutions/lakebase-scm-utils 0.3.9 → 0.3.11

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 (69) hide show
  1. package/dist/{pr-e-E4aP4j.d.cts → pr-D-eayWyJ.d.cts} +59 -1
  2. package/dist/{pr-e-E4aP4j.d.ts → pr-D-eayWyJ.d.ts} +59 -1
  3. package/dist/scripts/git/index.cjs +30 -14
  4. package/dist/scripts/git/index.d.cts +32 -1
  5. package/dist/scripts/git/index.d.ts +32 -1
  6. package/dist/scripts/git/index.js +28 -14
  7. package/dist/scripts/github/index.d.cts +2 -60
  8. package/dist/scripts/github/index.d.ts +2 -60
  9. package/dist/scripts/index.cjs +301 -98
  10. package/dist/scripts/index.d.cts +4 -4
  11. package/dist/scripts/index.d.ts +4 -4
  12. package/dist/scripts/index.js +296 -98
  13. package/dist/scripts/lakebase/branch.cli.cjs +55 -10
  14. package/dist/scripts/lakebase/branch.cli.js +55 -10
  15. package/dist/scripts/lakebase/ci-resolve-branch.cli.cjs +59 -24
  16. package/dist/scripts/lakebase/ci-resolve-branch.cli.js +59 -24
  17. package/dist/scripts/lakebase/collapse-heads.cli.cjs +16 -14
  18. package/dist/scripts/lakebase/collapse-heads.cli.js +16 -14
  19. package/dist/scripts/lakebase/cut-backup.cli.cjs +43 -10
  20. package/dist/scripts/lakebase/cut-backup.cli.js +43 -10
  21. package/dist/scripts/lakebase/cut-tier.cli.cjs +43 -10
  22. package/dist/scripts/lakebase/cut-tier.cli.js +43 -10
  23. package/dist/scripts/lakebase/detect-language.cli.cjs +16 -14
  24. package/dist/scripts/lakebase/detect-language.cli.js +16 -14
  25. package/dist/scripts/lakebase/doctor.cli.cjs +16 -14
  26. package/dist/scripts/lakebase/doctor.cli.js +16 -14
  27. package/dist/scripts/lakebase/get-connection.cli.cjs +16 -14
  28. package/dist/scripts/lakebase/get-connection.cli.js +16 -14
  29. package/dist/scripts/lakebase/index.cjs +313 -99
  30. package/dist/scripts/lakebase/index.d.cts +97 -2
  31. package/dist/scripts/lakebase/index.d.ts +97 -2
  32. package/dist/scripts/lakebase/index.js +310 -99
  33. package/dist/scripts/lakebase/infra-runner.cli.cjs +16 -14
  34. package/dist/scripts/lakebase/infra-runner.cli.js +16 -14
  35. package/dist/scripts/lakebase/new-migration.cli.cjs +16 -14
  36. package/dist/scripts/lakebase/new-migration.cli.js +16 -14
  37. package/dist/scripts/lakebase/schema-diff.cli.cjs +16 -14
  38. package/dist/scripts/lakebase/schema-diff.cli.js +16 -14
  39. package/dist/scripts/lakebase/schema-migrate.cli.cjs +16 -14
  40. package/dist/scripts/lakebase/schema-migrate.cli.js +16 -14
  41. package/dist/scripts/lakebase/scm-claim-feature.cli.cjs +59 -24
  42. package/dist/scripts/lakebase/scm-claim-feature.cli.js +59 -24
  43. package/dist/scripts/lakebase/scm-doctor.cli.cjs +59 -24
  44. package/dist/scripts/lakebase/scm-doctor.cli.js +59 -24
  45. package/dist/scripts/lakebase/scm-merge.cli.cjs +16 -14
  46. package/dist/scripts/lakebase/scm-merge.cli.js +16 -14
  47. package/dist/scripts/lakebase/scm-reconcile-tier.cli.cjs +16 -14
  48. package/dist/scripts/lakebase/scm-reconcile-tier.cli.js +16 -14
  49. package/dist/scripts/lakebase/scm-recover-orphans.cli.cjs +43 -10
  50. package/dist/scripts/lakebase/scm-recover-orphans.cli.js +43 -10
  51. package/dist/scripts/lakebase/snapshot.cli.cjs +418 -0
  52. package/dist/scripts/lakebase/snapshot.cli.d.cts +1 -0
  53. package/dist/scripts/lakebase/snapshot.cli.d.ts +1 -0
  54. package/dist/scripts/lakebase/snapshot.cli.js +395 -0
  55. package/dist/scripts/util/index.cjs +2 -2
  56. package/dist/scripts/util/index.js +2 -2
  57. package/package.json +2 -1
  58. package/scripts/git/commit-push.ts +60 -14
  59. package/scripts/lakebase/branch-create.ts +93 -14
  60. package/scripts/lakebase/branch.cli.ts +15 -0
  61. package/scripts/lakebase/create-preflight.ts +213 -1
  62. package/scripts/lakebase/create-project.ts +98 -30
  63. package/scripts/lakebase/get-connection.ts +29 -12
  64. package/scripts/lakebase/snapshot.cli.ts +117 -0
  65. package/scripts/lakebase/snapshot.ts +85 -0
  66. package/templates/project/common/scripts/run-tests.sh +43 -2
  67. package/templates/project/python/tests/conftest.py +22 -0
  68. package/templates/project/common/tests/e2e/__pycache__/conftest.cpython-314.pyc +0 -0
  69. package/templates/project/python/tests/__pycache__/conftest.cpython-314.pyc +0 -0
@@ -78171,9 +78171,46 @@ function dbcli2(args, host, timeout = KIT_TIMEOUTS.cliDefault) {
78171
78171
  }
78172
78172
 
78173
78173
  // scripts/lakebase/branch-create.ts
78174
+ function snapshotPath(instance, snapshot) {
78175
+ return snapshot.includes("/") ? snapshot : `${projectPath(instance)}/snapshots/${snapshot}`;
78176
+ }
78177
+ function applyExpiration(spec, ttl, noExpiry) {
78178
+ if (ttl && noExpiry === true) {
78179
+ throw new LakebaseBranchError(
78180
+ `Cannot set both ttl ("${ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
78181
+ );
78182
+ }
78183
+ if (ttl) spec.ttl = ttl;
78184
+ else if (noExpiry ?? true) spec.no_expiry = true;
78185
+ }
78174
78186
  async function createBranch(args) {
78175
78187
  const sanitized = sanitizeBranchName(args.branch);
78176
78188
  const lookup = { instance: args.instance, host: args.host };
78189
+ if (args.fromSnapshot) {
78190
+ if (args.parentBranch || args.sourceBranchTime || args.sourceBranchLsn) {
78191
+ throw new LakebaseBranchError(
78192
+ `fromSnapshot is mutually exclusive with parentBranch / sourceBranchTime / sourceBranchLsn (the Lakebase API rejects source_snapshot combined with any source_branch field).`
78193
+ );
78194
+ }
78195
+ const existing2 = await getBranchByName(sanitized, lookup);
78196
+ if (existing2) return existing2;
78197
+ const spec = { source_snapshot: snapshotPath(args.instance, args.fromSnapshot) };
78198
+ applyExpiration(spec, args.ttl, args.noExpiry);
78199
+ try {
78200
+ await createWithTtlRecovery(args.instance, sanitized, spec, args.host);
78201
+ } catch (err) {
78202
+ if (err instanceof LakebaseBranchTtlTooLongError) throw err;
78203
+ const landed = await getBranchByName(sanitized, lookup);
78204
+ if (!landed) throw err;
78205
+ }
78206
+ return waitForBranchReady({
78207
+ instance: args.instance,
78208
+ host: args.host,
78209
+ branch: sanitized,
78210
+ timeoutMs: args.readyTimeoutMs ?? KIT_TIMEOUTS.readyWait,
78211
+ pollIntervalMs: args.pollIntervalMs ?? KIT_TIMEOUTS.readyPoll
78212
+ });
78213
+ }
78177
78214
  let sourceBranchPath;
78178
78215
  if (args.parentBranch) {
78179
78216
  if (looksLikeBranchUid(args.parentBranch)) {
@@ -78221,19 +78258,15 @@ async function createBranch(args) {
78221
78258
  assertSourceMatches(existing, sourceBranchPath, sanitized);
78222
78259
  return existing;
78223
78260
  }
78224
- if (args.ttl && args.noExpiry === true) {
78261
+ if (args.sourceBranchTime && args.sourceBranchLsn) {
78225
78262
  throw new LakebaseBranchError(
78226
- `Cannot set both ttl ("${args.ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
78263
+ `sourceBranchTime and sourceBranchLsn are mutually exclusive \u2013 pass at most one point-in-time.`
78227
78264
  );
78228
78265
  }
78229
- const specObj = {
78230
- source_branch: sourceBranchPath
78231
- };
78232
- if (args.ttl) {
78233
- specObj.ttl = args.ttl;
78234
- } else if (args.noExpiry ?? true) {
78235
- specObj.no_expiry = true;
78236
- }
78266
+ const specObj = { source_branch: sourceBranchPath };
78267
+ if (args.sourceBranchTime) specObj.source_branch_time = args.sourceBranchTime;
78268
+ else if (args.sourceBranchLsn) specObj.source_branch_lsn = args.sourceBranchLsn;
78269
+ applyExpiration(specObj, args.ttl, args.noExpiry);
78237
78270
  try {
78238
78271
  await createWithTtlRecovery(args.instance, sanitized, specObj, args.host);
78239
78272
  } catch (err) {
@@ -79056,6 +79089,15 @@ function parseArgs(argv) {
79056
79089
  case "--parent-branch":
79057
79090
  out.parentBranch = argv[++i2];
79058
79091
  break;
79092
+ case "--from-snapshot":
79093
+ out.fromSnapshot = argv[++i2];
79094
+ break;
79095
+ case "--at-time":
79096
+ out.atTime = argv[++i2];
79097
+ break;
79098
+ case "--at-lsn":
79099
+ out.atLsn = argv[++i2];
79100
+ break;
79059
79101
  case "--ttl":
79060
79102
  out.ttl = argv[++i2];
79061
79103
  break;
@@ -79218,6 +79260,9 @@ async function main() {
79218
79260
  host: args.host,
79219
79261
  branch: args.branch,
79220
79262
  parentBranch: args.parentBranch,
79263
+ fromSnapshot: args.fromSnapshot,
79264
+ sourceBranchTime: args.atTime,
79265
+ sourceBranchLsn: args.atLsn,
79221
79266
  ttl: args.ttl,
79222
79267
  noExpiry: args.noExpiry
79223
79268
  });
@@ -641,9 +641,46 @@ function dbcli2(args, host, timeout = KIT_TIMEOUTS.cliDefault) {
641
641
  }
642
642
 
643
643
  // scripts/lakebase/branch-create.ts
644
+ function snapshotPath(instance, snapshot) {
645
+ return snapshot.includes("/") ? snapshot : `${projectPath(instance)}/snapshots/${snapshot}`;
646
+ }
647
+ function applyExpiration(spec, ttl, noExpiry) {
648
+ if (ttl && noExpiry === true) {
649
+ throw new LakebaseBranchError(
650
+ `Cannot set both ttl ("${ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
651
+ );
652
+ }
653
+ if (ttl) spec.ttl = ttl;
654
+ else if (noExpiry ?? true) spec.no_expiry = true;
655
+ }
644
656
  async function createBranch(args) {
645
657
  const sanitized = sanitizeBranchName(args.branch);
646
658
  const lookup = { instance: args.instance, host: args.host };
659
+ if (args.fromSnapshot) {
660
+ if (args.parentBranch || args.sourceBranchTime || args.sourceBranchLsn) {
661
+ throw new LakebaseBranchError(
662
+ `fromSnapshot is mutually exclusive with parentBranch / sourceBranchTime / sourceBranchLsn (the Lakebase API rejects source_snapshot combined with any source_branch field).`
663
+ );
664
+ }
665
+ const existing2 = await getBranchByName(sanitized, lookup);
666
+ if (existing2) return existing2;
667
+ const spec = { source_snapshot: snapshotPath(args.instance, args.fromSnapshot) };
668
+ applyExpiration(spec, args.ttl, args.noExpiry);
669
+ try {
670
+ await createWithTtlRecovery(args.instance, sanitized, spec, args.host);
671
+ } catch (err) {
672
+ if (err instanceof LakebaseBranchTtlTooLongError) throw err;
673
+ const landed = await getBranchByName(sanitized, lookup);
674
+ if (!landed) throw err;
675
+ }
676
+ return waitForBranchReady({
677
+ instance: args.instance,
678
+ host: args.host,
679
+ branch: sanitized,
680
+ timeoutMs: args.readyTimeoutMs ?? KIT_TIMEOUTS.readyWait,
681
+ pollIntervalMs: args.pollIntervalMs ?? KIT_TIMEOUTS.readyPoll
682
+ });
683
+ }
647
684
  let sourceBranchPath;
648
685
  if (args.parentBranch) {
649
686
  if (looksLikeBranchUid(args.parentBranch)) {
@@ -691,19 +728,15 @@ async function createBranch(args) {
691
728
  assertSourceMatches(existing, sourceBranchPath, sanitized);
692
729
  return existing;
693
730
  }
694
- if (args.ttl && args.noExpiry === true) {
731
+ if (args.sourceBranchTime && args.sourceBranchLsn) {
695
732
  throw new LakebaseBranchError(
696
- `Cannot set both ttl ("${args.ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
733
+ `sourceBranchTime and sourceBranchLsn are mutually exclusive \u2013 pass at most one point-in-time.`
697
734
  );
698
735
  }
699
- const specObj = {
700
- source_branch: sourceBranchPath
701
- };
702
- if (args.ttl) {
703
- specObj.ttl = args.ttl;
704
- } else if (args.noExpiry ?? true) {
705
- specObj.no_expiry = true;
706
- }
736
+ const specObj = { source_branch: sourceBranchPath };
737
+ if (args.sourceBranchTime) specObj.source_branch_time = args.sourceBranchTime;
738
+ else if (args.sourceBranchLsn) specObj.source_branch_lsn = args.sourceBranchLsn;
739
+ applyExpiration(specObj, args.ttl, args.noExpiry);
707
740
  try {
708
741
  await createWithTtlRecovery(args.instance, sanitized, specObj, args.host);
709
742
  } catch (err) {
@@ -1474,6 +1507,15 @@ function parseArgs(argv) {
1474
1507
  case "--parent-branch":
1475
1508
  out.parentBranch = argv[++i];
1476
1509
  break;
1510
+ case "--from-snapshot":
1511
+ out.fromSnapshot = argv[++i];
1512
+ break;
1513
+ case "--at-time":
1514
+ out.atTime = argv[++i];
1515
+ break;
1516
+ case "--at-lsn":
1517
+ out.atLsn = argv[++i];
1518
+ break;
1477
1519
  case "--ttl":
1478
1520
  out.ttl = argv[++i];
1479
1521
  break;
@@ -1636,6 +1678,9 @@ async function main() {
1636
1678
  host: args.host,
1637
1679
  branch: args.branch,
1638
1680
  parentBranch: args.parentBranch,
1681
+ fromSnapshot: args.fromSnapshot,
1682
+ sourceBranchTime: args.atTime,
1683
+ sourceBranchLsn: args.atLsn,
1639
1684
  ttl: args.ttl,
1640
1685
  noExpiry: args.noExpiry
1641
1686
  });
@@ -78044,9 +78044,46 @@ function dbcli2(args, host, timeout = KIT_TIMEOUTS.cliDefault) {
78044
78044
  }
78045
78045
 
78046
78046
  // scripts/lakebase/branch-create.ts
78047
+ function snapshotPath(instance, snapshot) {
78048
+ return snapshot.includes("/") ? snapshot : `${projectPath(instance)}/snapshots/${snapshot}`;
78049
+ }
78050
+ function applyExpiration(spec, ttl, noExpiry) {
78051
+ if (ttl && noExpiry === true) {
78052
+ throw new LakebaseBranchError(
78053
+ `Cannot set both ttl ("${ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
78054
+ );
78055
+ }
78056
+ if (ttl) spec.ttl = ttl;
78057
+ else if (noExpiry ?? true) spec.no_expiry = true;
78058
+ }
78047
78059
  async function createBranch(args) {
78048
78060
  const sanitized = sanitizeBranchName(args.branch);
78049
78061
  const lookup = { instance: args.instance, host: args.host };
78062
+ if (args.fromSnapshot) {
78063
+ if (args.parentBranch || args.sourceBranchTime || args.sourceBranchLsn) {
78064
+ throw new LakebaseBranchError(
78065
+ `fromSnapshot is mutually exclusive with parentBranch / sourceBranchTime / sourceBranchLsn (the Lakebase API rejects source_snapshot combined with any source_branch field).`
78066
+ );
78067
+ }
78068
+ const existing2 = await getBranchByName(sanitized, lookup);
78069
+ if (existing2) return existing2;
78070
+ const spec = { source_snapshot: snapshotPath(args.instance, args.fromSnapshot) };
78071
+ applyExpiration(spec, args.ttl, args.noExpiry);
78072
+ try {
78073
+ await createWithTtlRecovery(args.instance, sanitized, spec, args.host);
78074
+ } catch (err) {
78075
+ if (err instanceof LakebaseBranchTtlTooLongError) throw err;
78076
+ const landed = await getBranchByName(sanitized, lookup);
78077
+ if (!landed) throw err;
78078
+ }
78079
+ return waitForBranchReady({
78080
+ instance: args.instance,
78081
+ host: args.host,
78082
+ branch: sanitized,
78083
+ timeoutMs: args.readyTimeoutMs ?? KIT_TIMEOUTS.readyWait,
78084
+ pollIntervalMs: args.pollIntervalMs ?? KIT_TIMEOUTS.readyPoll
78085
+ });
78086
+ }
78050
78087
  let sourceBranchPath;
78051
78088
  if (args.parentBranch) {
78052
78089
  if (looksLikeBranchUid(args.parentBranch)) {
@@ -78094,19 +78131,15 @@ async function createBranch(args) {
78094
78131
  assertSourceMatches(existing, sourceBranchPath, sanitized);
78095
78132
  return existing;
78096
78133
  }
78097
- if (args.ttl && args.noExpiry === true) {
78134
+ if (args.sourceBranchTime && args.sourceBranchLsn) {
78098
78135
  throw new LakebaseBranchError(
78099
- `Cannot set both ttl ("${args.ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
78136
+ `sourceBranchTime and sourceBranchLsn are mutually exclusive \u2013 pass at most one point-in-time.`
78100
78137
  );
78101
78138
  }
78102
- const specObj = {
78103
- source_branch: sourceBranchPath
78104
- };
78105
- if (args.ttl) {
78106
- specObj.ttl = args.ttl;
78107
- } else if (args.noExpiry ?? true) {
78108
- specObj.no_expiry = true;
78109
- }
78139
+ const specObj = { source_branch: sourceBranchPath };
78140
+ if (args.sourceBranchTime) specObj.source_branch_time = args.sourceBranchTime;
78141
+ else if (args.sourceBranchLsn) specObj.source_branch_lsn = args.sourceBranchLsn;
78142
+ applyExpiration(specObj, args.ttl, args.noExpiry);
78110
78143
  try {
78111
78144
  await createWithTtlRecovery(args.instance, sanitized, specObj, args.host);
78112
78145
  } catch (err) {
@@ -79629,8 +79662,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
79629
79662
  var cached;
79630
79663
  function substrateSelfVersion() {
79631
79664
  if (cached !== void 0) return cached;
79632
- if ("0.3.9".length > 0) {
79633
- cached = "0.3.9";
79665
+ if ("0.3.11".length > 0) {
79666
+ cached = "0.3.11";
79634
79667
  return cached;
79635
79668
  }
79636
79669
  cached = "unknown";
@@ -79677,22 +79710,24 @@ async function getConnection(args) {
79677
79710
  const endpointPath = `projects/${args.instance}/branches/${branchId}/endpoints/${endpointName}`;
79678
79711
  if (args.output === "dsn") {
79679
79712
  const host2 = await resolveEndpointHost(args.instance, branchId);
79680
- const { token, email: email2 } = await mintCredential(endpointPath);
79681
- const url = buildPostgresUrl({ host: host2, port: POSTGRES_PORT, database, user: email2, password: token });
79713
+ const { token: token2, email: email2 } = await mintCredential(endpointPath);
79714
+ const url = buildPostgresUrl({ host: host2, port: POSTGRES_PORT, database, user: email2, password: token2 });
79682
79715
  return { url, host: host2, port: POSTGRES_PORT, database, user: email2, endpointPath };
79683
79716
  }
79684
79717
  const host = await resolveEndpointHost(args.instance, branchId);
79685
- const email = await resolveCurrentUser();
79686
79718
  process.env.PGAPPNAME = connectionApplicationName();
79687
- return createLakebasePool({
79688
- endpoint: endpointPath,
79689
- host,
79690
- database,
79691
- user: email,
79692
- // workspaceClient is passed through verbatim. createLakebasePool falls
79693
- // back to environment / ServiceContext when omitted.
79694
- ...args.workspaceClient !== void 0 ? { workspaceClient: args.workspaceClient } : {}
79695
- });
79719
+ if (args.workspaceClient !== void 0) {
79720
+ const email2 = await resolveCurrentUser();
79721
+ return createLakebasePool({
79722
+ endpoint: endpointPath,
79723
+ host,
79724
+ database,
79725
+ user: email2,
79726
+ workspaceClient: args.workspaceClient
79727
+ });
79728
+ }
79729
+ const { token, email } = await mintCredential(endpointPath);
79730
+ return createLakebasePool({ host, database, user: email, password: token });
79696
79731
  }
79697
79732
  async function resolveEndpointHost(instance, branch) {
79698
79733
  const branchId = await resolveBranchId({ instance, branch });
@@ -510,9 +510,46 @@ function dbcli2(args, host, timeout = KIT_TIMEOUTS.cliDefault) {
510
510
  }
511
511
 
512
512
  // scripts/lakebase/branch-create.ts
513
+ function snapshotPath(instance, snapshot) {
514
+ return snapshot.includes("/") ? snapshot : `${projectPath(instance)}/snapshots/${snapshot}`;
515
+ }
516
+ function applyExpiration(spec, ttl, noExpiry) {
517
+ if (ttl && noExpiry === true) {
518
+ throw new LakebaseBranchError(
519
+ `Cannot set both ttl ("${ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
520
+ );
521
+ }
522
+ if (ttl) spec.ttl = ttl;
523
+ else if (noExpiry ?? true) spec.no_expiry = true;
524
+ }
513
525
  async function createBranch(args) {
514
526
  const sanitized = sanitizeBranchName(args.branch);
515
527
  const lookup = { instance: args.instance, host: args.host };
528
+ if (args.fromSnapshot) {
529
+ if (args.parentBranch || args.sourceBranchTime || args.sourceBranchLsn) {
530
+ throw new LakebaseBranchError(
531
+ `fromSnapshot is mutually exclusive with parentBranch / sourceBranchTime / sourceBranchLsn (the Lakebase API rejects source_snapshot combined with any source_branch field).`
532
+ );
533
+ }
534
+ const existing2 = await getBranchByName(sanitized, lookup);
535
+ if (existing2) return existing2;
536
+ const spec = { source_snapshot: snapshotPath(args.instance, args.fromSnapshot) };
537
+ applyExpiration(spec, args.ttl, args.noExpiry);
538
+ try {
539
+ await createWithTtlRecovery(args.instance, sanitized, spec, args.host);
540
+ } catch (err) {
541
+ if (err instanceof LakebaseBranchTtlTooLongError) throw err;
542
+ const landed = await getBranchByName(sanitized, lookup);
543
+ if (!landed) throw err;
544
+ }
545
+ return waitForBranchReady({
546
+ instance: args.instance,
547
+ host: args.host,
548
+ branch: sanitized,
549
+ timeoutMs: args.readyTimeoutMs ?? KIT_TIMEOUTS.readyWait,
550
+ pollIntervalMs: args.pollIntervalMs ?? KIT_TIMEOUTS.readyPoll
551
+ });
552
+ }
516
553
  let sourceBranchPath;
517
554
  if (args.parentBranch) {
518
555
  if (looksLikeBranchUid(args.parentBranch)) {
@@ -560,19 +597,15 @@ async function createBranch(args) {
560
597
  assertSourceMatches(existing, sourceBranchPath, sanitized);
561
598
  return existing;
562
599
  }
563
- if (args.ttl && args.noExpiry === true) {
600
+ if (args.sourceBranchTime && args.sourceBranchLsn) {
564
601
  throw new LakebaseBranchError(
565
- `Cannot set both ttl ("${args.ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
602
+ `sourceBranchTime and sourceBranchLsn are mutually exclusive \u2013 pass at most one point-in-time.`
566
603
  );
567
604
  }
568
- const specObj = {
569
- source_branch: sourceBranchPath
570
- };
571
- if (args.ttl) {
572
- specObj.ttl = args.ttl;
573
- } else if (args.noExpiry ?? true) {
574
- specObj.no_expiry = true;
575
- }
605
+ const specObj = { source_branch: sourceBranchPath };
606
+ if (args.sourceBranchTime) specObj.source_branch_time = args.sourceBranchTime;
607
+ else if (args.sourceBranchLsn) specObj.source_branch_lsn = args.sourceBranchLsn;
608
+ applyExpiration(specObj, args.ttl, args.noExpiry);
576
609
  try {
577
610
  await createWithTtlRecovery(args.instance, sanitized, specObj, args.host);
578
611
  } catch (err) {
@@ -721,8 +754,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
721
754
  var cached;
722
755
  function substrateSelfVersion() {
723
756
  if (cached !== void 0) return cached;
724
- if ("0.3.9".length > 0) {
725
- cached = "0.3.9";
757
+ if ("0.3.11".length > 0) {
758
+ cached = "0.3.11";
726
759
  return cached;
727
760
  }
728
761
  cached = "unknown";
@@ -769,22 +802,24 @@ async function getConnection(args) {
769
802
  const endpointPath = `projects/${args.instance}/branches/${branchId}/endpoints/${endpointName}`;
770
803
  if (args.output === "dsn") {
771
804
  const host2 = await resolveEndpointHost(args.instance, branchId);
772
- const { token, email: email2 } = await mintCredential(endpointPath);
773
- const url = buildPostgresUrl({ host: host2, port: POSTGRES_PORT, database, user: email2, password: token });
805
+ const { token: token2, email: email2 } = await mintCredential(endpointPath);
806
+ const url = buildPostgresUrl({ host: host2, port: POSTGRES_PORT, database, user: email2, password: token2 });
774
807
  return { url, host: host2, port: POSTGRES_PORT, database, user: email2, endpointPath };
775
808
  }
776
809
  const host = await resolveEndpointHost(args.instance, branchId);
777
- const email = await resolveCurrentUser();
778
810
  process.env.PGAPPNAME = connectionApplicationName();
779
- return createLakebasePool({
780
- endpoint: endpointPath,
781
- host,
782
- database,
783
- user: email,
784
- // workspaceClient is passed through verbatim. createLakebasePool falls
785
- // back to environment / ServiceContext when omitted.
786
- ...args.workspaceClient !== void 0 ? { workspaceClient: args.workspaceClient } : {}
787
- });
811
+ if (args.workspaceClient !== void 0) {
812
+ const email2 = await resolveCurrentUser();
813
+ return createLakebasePool({
814
+ endpoint: endpointPath,
815
+ host,
816
+ database,
817
+ user: email2,
818
+ workspaceClient: args.workspaceClient
819
+ });
820
+ }
821
+ const { token, email } = await mintCredential(endpointPath);
822
+ return createLakebasePool({ host, database, user: email, password: token });
788
823
  }
789
824
  async function resolveEndpointHost(instance, branch) {
790
825
  const branchId = await resolveBranchId({ instance, branch });
@@ -79290,8 +79290,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
79290
79290
  var cached;
79291
79291
  function substrateSelfVersion() {
79292
79292
  if (cached !== void 0) return cached;
79293
- if ("0.3.9".length > 0) {
79294
- cached = "0.3.9";
79293
+ if ("0.3.11".length > 0) {
79294
+ cached = "0.3.11";
79295
79295
  return cached;
79296
79296
  }
79297
79297
  cached = "unknown";
@@ -79338,22 +79338,24 @@ async function getConnection(args) {
79338
79338
  const endpointPath = `projects/${args.instance}/branches/${branchId}/endpoints/${endpointName}`;
79339
79339
  if (args.output === "dsn") {
79340
79340
  const host2 = await resolveEndpointHost(args.instance, branchId);
79341
- const { token, email: email2 } = await mintCredential(endpointPath);
79342
- const url = buildPostgresUrl({ host: host2, port: POSTGRES_PORT, database, user: email2, password: token });
79341
+ const { token: token2, email: email2 } = await mintCredential(endpointPath);
79342
+ const url = buildPostgresUrl({ host: host2, port: POSTGRES_PORT, database, user: email2, password: token2 });
79343
79343
  return { url, host: host2, port: POSTGRES_PORT, database, user: email2, endpointPath };
79344
79344
  }
79345
79345
  const host = await resolveEndpointHost(args.instance, branchId);
79346
- const email = await resolveCurrentUser();
79347
79346
  process.env.PGAPPNAME = connectionApplicationName();
79348
- return createLakebasePool({
79349
- endpoint: endpointPath,
79350
- host,
79351
- database,
79352
- user: email,
79353
- // workspaceClient is passed through verbatim. createLakebasePool falls
79354
- // back to environment / ServiceContext when omitted.
79355
- ...args.workspaceClient !== void 0 ? { workspaceClient: args.workspaceClient } : {}
79356
- });
79347
+ if (args.workspaceClient !== void 0) {
79348
+ const email2 = await resolveCurrentUser();
79349
+ return createLakebasePool({
79350
+ endpoint: endpointPath,
79351
+ host,
79352
+ database,
79353
+ user: email2,
79354
+ workspaceClient: args.workspaceClient
79355
+ });
79356
+ }
79357
+ const { token, email } = await mintCredential(endpointPath);
79358
+ return createLakebasePool({ host, database, user: email, password: token });
79357
79359
  }
79358
79360
  async function resolveEndpointHost(instance, branch) {
79359
79361
  const branchId = await resolveBranchId({ instance, branch });
@@ -393,8 +393,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
393
393
  var cached;
394
394
  function substrateSelfVersion() {
395
395
  if (cached !== void 0) return cached;
396
- if ("0.3.9".length > 0) {
397
- cached = "0.3.9";
396
+ if ("0.3.11".length > 0) {
397
+ cached = "0.3.11";
398
398
  return cached;
399
399
  }
400
400
  cached = "unknown";
@@ -441,22 +441,24 @@ async function getConnection(args) {
441
441
  const endpointPath = `projects/${args.instance}/branches/${branchId}/endpoints/${endpointName}`;
442
442
  if (args.output === "dsn") {
443
443
  const host2 = await resolveEndpointHost(args.instance, branchId);
444
- const { token, email: email2 } = await mintCredential(endpointPath);
445
- const url = buildPostgresUrl({ host: host2, port: POSTGRES_PORT, database, user: email2, password: token });
444
+ const { token: token2, email: email2 } = await mintCredential(endpointPath);
445
+ const url = buildPostgresUrl({ host: host2, port: POSTGRES_PORT, database, user: email2, password: token2 });
446
446
  return { url, host: host2, port: POSTGRES_PORT, database, user: email2, endpointPath };
447
447
  }
448
448
  const host = await resolveEndpointHost(args.instance, branchId);
449
- const email = await resolveCurrentUser();
450
449
  process.env.PGAPPNAME = connectionApplicationName();
451
- return createLakebasePool({
452
- endpoint: endpointPath,
453
- host,
454
- database,
455
- user: email,
456
- // workspaceClient is passed through verbatim. createLakebasePool falls
457
- // back to environment / ServiceContext when omitted.
458
- ...args.workspaceClient !== void 0 ? { workspaceClient: args.workspaceClient } : {}
459
- });
450
+ if (args.workspaceClient !== void 0) {
451
+ const email2 = await resolveCurrentUser();
452
+ return createLakebasePool({
453
+ endpoint: endpointPath,
454
+ host,
455
+ database,
456
+ user: email2,
457
+ workspaceClient: args.workspaceClient
458
+ });
459
+ }
460
+ const { token, email } = await mintCredential(endpointPath);
461
+ return createLakebasePool({ host, database, user: email, password: token });
460
462
  }
461
463
  async function resolveEndpointHost(instance, branch) {
462
464
  const branchId = await resolveBranchId({ instance, branch });
@@ -487,9 +487,46 @@ function dbcli2(args, host, timeout = KIT_TIMEOUTS.cliDefault) {
487
487
  }
488
488
 
489
489
  // scripts/lakebase/branch-create.ts
490
+ function snapshotPath(instance, snapshot) {
491
+ return snapshot.includes("/") ? snapshot : `${projectPath(instance)}/snapshots/${snapshot}`;
492
+ }
493
+ function applyExpiration(spec, ttl, noExpiry) {
494
+ if (ttl && noExpiry === true) {
495
+ throw new LakebaseBranchError(
496
+ `Cannot set both ttl ("${ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
497
+ );
498
+ }
499
+ if (ttl) spec.ttl = ttl;
500
+ else if (noExpiry ?? true) spec.no_expiry = true;
501
+ }
490
502
  async function createBranch(args) {
491
503
  const sanitized = sanitizeBranchName(args.branch);
492
504
  const lookup = { instance: args.instance, host: args.host };
505
+ if (args.fromSnapshot) {
506
+ if (args.parentBranch || args.sourceBranchTime || args.sourceBranchLsn) {
507
+ throw new LakebaseBranchError(
508
+ `fromSnapshot is mutually exclusive with parentBranch / sourceBranchTime / sourceBranchLsn (the Lakebase API rejects source_snapshot combined with any source_branch field).`
509
+ );
510
+ }
511
+ const existing2 = await getBranchByName(sanitized, lookup);
512
+ if (existing2) return existing2;
513
+ const spec = { source_snapshot: snapshotPath(args.instance, args.fromSnapshot) };
514
+ applyExpiration(spec, args.ttl, args.noExpiry);
515
+ try {
516
+ await createWithTtlRecovery(args.instance, sanitized, spec, args.host);
517
+ } catch (err) {
518
+ if (err instanceof LakebaseBranchTtlTooLongError) throw err;
519
+ const landed = await getBranchByName(sanitized, lookup);
520
+ if (!landed) throw err;
521
+ }
522
+ return waitForBranchReady({
523
+ instance: args.instance,
524
+ host: args.host,
525
+ branch: sanitized,
526
+ timeoutMs: args.readyTimeoutMs ?? KIT_TIMEOUTS.readyWait,
527
+ pollIntervalMs: args.pollIntervalMs ?? KIT_TIMEOUTS.readyPoll
528
+ });
529
+ }
493
530
  let sourceBranchPath;
494
531
  if (args.parentBranch) {
495
532
  if (looksLikeBranchUid(args.parentBranch)) {
@@ -537,19 +574,15 @@ async function createBranch(args) {
537
574
  assertSourceMatches(existing, sourceBranchPath, sanitized);
538
575
  return existing;
539
576
  }
540
- if (args.ttl && args.noExpiry === true) {
577
+ if (args.sourceBranchTime && args.sourceBranchLsn) {
541
578
  throw new LakebaseBranchError(
542
- `Cannot set both ttl ("${args.ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
579
+ `sourceBranchTime and sourceBranchLsn are mutually exclusive \u2013 pass at most one point-in-time.`
543
580
  );
544
581
  }
545
- const specObj = {
546
- source_branch: sourceBranchPath
547
- };
548
- if (args.ttl) {
549
- specObj.ttl = args.ttl;
550
- } else if (args.noExpiry ?? true) {
551
- specObj.no_expiry = true;
552
- }
582
+ const specObj = { source_branch: sourceBranchPath };
583
+ if (args.sourceBranchTime) specObj.source_branch_time = args.sourceBranchTime;
584
+ else if (args.sourceBranchLsn) specObj.source_branch_lsn = args.sourceBranchLsn;
585
+ applyExpiration(specObj, args.ttl, args.noExpiry);
553
586
  try {
554
587
  await createWithTtlRecovery(args.instance, sanitized, specObj, args.host);
555
588
  } catch (err) {