@databricks-solutions/lakebase-scm-utils 0.3.8 → 0.3.10

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 (56) hide show
  1. package/dist/scripts/index.cjs +59 -24
  2. package/dist/scripts/index.js +59 -24
  3. package/dist/scripts/lakebase/branch.cli.cjs +55 -10
  4. package/dist/scripts/lakebase/branch.cli.js +55 -10
  5. package/dist/scripts/lakebase/ci-resolve-branch.cli.cjs +59 -24
  6. package/dist/scripts/lakebase/ci-resolve-branch.cli.js +59 -24
  7. package/dist/scripts/lakebase/collapse-heads.cli.cjs +16 -14
  8. package/dist/scripts/lakebase/collapse-heads.cli.js +16 -14
  9. package/dist/scripts/lakebase/cut-backup.cli.cjs +43 -10
  10. package/dist/scripts/lakebase/cut-backup.cli.js +43 -10
  11. package/dist/scripts/lakebase/cut-tier.cli.cjs +43 -10
  12. package/dist/scripts/lakebase/cut-tier.cli.js +43 -10
  13. package/dist/scripts/lakebase/detect-language.cli.cjs +16 -14
  14. package/dist/scripts/lakebase/detect-language.cli.js +16 -14
  15. package/dist/scripts/lakebase/doctor.cli.cjs +16 -14
  16. package/dist/scripts/lakebase/doctor.cli.js +16 -14
  17. package/dist/scripts/lakebase/get-connection.cli.cjs +16 -14
  18. package/dist/scripts/lakebase/get-connection.cli.js +16 -14
  19. package/dist/scripts/lakebase/index.cjs +59 -24
  20. package/dist/scripts/lakebase/index.d.cts +23 -0
  21. package/dist/scripts/lakebase/index.d.ts +23 -0
  22. package/dist/scripts/lakebase/index.js +59 -24
  23. package/dist/scripts/lakebase/infra-runner.cli.cjs +16 -14
  24. package/dist/scripts/lakebase/infra-runner.cli.js +16 -14
  25. package/dist/scripts/lakebase/new-migration.cli.cjs +16 -14
  26. package/dist/scripts/lakebase/new-migration.cli.js +16 -14
  27. package/dist/scripts/lakebase/schema-diff.cli.cjs +16 -14
  28. package/dist/scripts/lakebase/schema-diff.cli.js +16 -14
  29. package/dist/scripts/lakebase/schema-migrate.cli.cjs +16 -14
  30. package/dist/scripts/lakebase/schema-migrate.cli.js +16 -14
  31. package/dist/scripts/lakebase/scm-claim-feature.cli.cjs +59 -24
  32. package/dist/scripts/lakebase/scm-claim-feature.cli.js +59 -24
  33. package/dist/scripts/lakebase/scm-doctor.cli.cjs +59 -24
  34. package/dist/scripts/lakebase/scm-doctor.cli.js +59 -24
  35. package/dist/scripts/lakebase/scm-merge.cli.cjs +16 -14
  36. package/dist/scripts/lakebase/scm-merge.cli.js +16 -14
  37. package/dist/scripts/lakebase/scm-reconcile-tier.cli.cjs +16 -14
  38. package/dist/scripts/lakebase/scm-reconcile-tier.cli.js +16 -14
  39. package/dist/scripts/lakebase/scm-recover-orphans.cli.cjs +43 -10
  40. package/dist/scripts/lakebase/scm-recover-orphans.cli.js +43 -10
  41. package/dist/scripts/lakebase/snapshot.cli.cjs +418 -0
  42. package/dist/scripts/lakebase/snapshot.cli.d.cts +1 -0
  43. package/dist/scripts/lakebase/snapshot.cli.d.ts +1 -0
  44. package/dist/scripts/lakebase/snapshot.cli.js +395 -0
  45. package/dist/scripts/util/index.cjs +2 -2
  46. package/dist/scripts/util/index.js +2 -2
  47. package/package.json +2 -1
  48. package/scripts/lakebase/branch-create.ts +93 -14
  49. package/scripts/lakebase/branch.cli.ts +15 -0
  50. package/scripts/lakebase/get-connection.ts +29 -12
  51. package/scripts/lakebase/snapshot.cli.ts +117 -0
  52. package/scripts/lakebase/snapshot.ts +85 -0
  53. package/templates/project/common/scripts/run-tests.sh +43 -2
  54. package/templates/project/common/tests/e2e/conftest.py +5 -1
  55. package/templates/project/python/tests/conftest.py +60 -4
  56. package/templates/project/python/tests/__pycache__/conftest.cpython-314.pyc +0 -0
@@ -87388,9 +87388,46 @@ function dbcli3(args, host, timeout = KIT_TIMEOUTS.cliDefault) {
87388
87388
  }
87389
87389
 
87390
87390
  // scripts/lakebase/branch-create.ts
87391
+ function snapshotPath(instance, snapshot) {
87392
+ return snapshot.includes("/") ? snapshot : `${projectPath(instance)}/snapshots/${snapshot}`;
87393
+ }
87394
+ function applyExpiration(spec, ttl, noExpiry) {
87395
+ if (ttl && noExpiry === true) {
87396
+ throw new LakebaseBranchError(
87397
+ `Cannot set both ttl ("${ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
87398
+ );
87399
+ }
87400
+ if (ttl) spec.ttl = ttl;
87401
+ else if (noExpiry ?? true) spec.no_expiry = true;
87402
+ }
87391
87403
  async function createBranch(args) {
87392
87404
  const sanitized = sanitizeBranchName(args.branch);
87393
87405
  const lookup = { instance: args.instance, host: args.host };
87406
+ if (args.fromSnapshot) {
87407
+ if (args.parentBranch || args.sourceBranchTime || args.sourceBranchLsn) {
87408
+ throw new LakebaseBranchError(
87409
+ `fromSnapshot is mutually exclusive with parentBranch / sourceBranchTime / sourceBranchLsn (the Lakebase API rejects source_snapshot combined with any source_branch field).`
87410
+ );
87411
+ }
87412
+ const existing2 = await getBranchByName(sanitized, lookup);
87413
+ if (existing2) return existing2;
87414
+ const spec = { source_snapshot: snapshotPath(args.instance, args.fromSnapshot) };
87415
+ applyExpiration(spec, args.ttl, args.noExpiry);
87416
+ try {
87417
+ await createWithTtlRecovery(args.instance, sanitized, spec, args.host);
87418
+ } catch (err) {
87419
+ if (err instanceof LakebaseBranchTtlTooLongError) throw err;
87420
+ const landed = await getBranchByName(sanitized, lookup);
87421
+ if (!landed) throw err;
87422
+ }
87423
+ return waitForBranchReady({
87424
+ instance: args.instance,
87425
+ host: args.host,
87426
+ branch: sanitized,
87427
+ timeoutMs: args.readyTimeoutMs ?? KIT_TIMEOUTS.readyWait,
87428
+ pollIntervalMs: args.pollIntervalMs ?? KIT_TIMEOUTS.readyPoll
87429
+ });
87430
+ }
87394
87431
  let sourceBranchPath;
87395
87432
  if (args.parentBranch) {
87396
87433
  if (looksLikeBranchUid(args.parentBranch)) {
@@ -87438,19 +87475,15 @@ async function createBranch(args) {
87438
87475
  assertSourceMatches(existing, sourceBranchPath, sanitized);
87439
87476
  return existing;
87440
87477
  }
87441
- if (args.ttl && args.noExpiry === true) {
87478
+ if (args.sourceBranchTime && args.sourceBranchLsn) {
87442
87479
  throw new LakebaseBranchError(
87443
- `Cannot set both ttl ("${args.ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
87480
+ `sourceBranchTime and sourceBranchLsn are mutually exclusive \u2013 pass at most one point-in-time.`
87444
87481
  );
87445
87482
  }
87446
- const specObj = {
87447
- source_branch: sourceBranchPath
87448
- };
87449
- if (args.ttl) {
87450
- specObj.ttl = args.ttl;
87451
- } else if (args.noExpiry ?? true) {
87452
- specObj.no_expiry = true;
87453
- }
87483
+ const specObj = { source_branch: sourceBranchPath };
87484
+ if (args.sourceBranchTime) specObj.source_branch_time = args.sourceBranchTime;
87485
+ else if (args.sourceBranchLsn) specObj.source_branch_lsn = args.sourceBranchLsn;
87486
+ applyExpiration(specObj, args.ttl, args.noExpiry);
87454
87487
  try {
87455
87488
  await createWithTtlRecovery(args.instance, sanitized, specObj, args.host);
87456
87489
  } catch (err) {
@@ -88959,8 +88992,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
88959
88992
  var cached;
88960
88993
  function substrateSelfVersion() {
88961
88994
  if (cached !== void 0) return cached;
88962
- if ("0.3.8".length > 0) {
88963
- cached = "0.3.8";
88995
+ if ("0.3.10".length > 0) {
88996
+ cached = "0.3.10";
88964
88997
  return cached;
88965
88998
  }
88966
88999
  cached = "unknown";
@@ -89007,22 +89040,24 @@ async function getConnection(args) {
89007
89040
  const endpointPath2 = `projects/${args.instance}/branches/${branchId}/endpoints/${endpointName}`;
89008
89041
  if (args.output === "dsn") {
89009
89042
  const host2 = await resolveEndpointHost(args.instance, branchId);
89010
- const { token, email: email2 } = await mintCredential(endpointPath2);
89011
- const url = buildPostgresUrl({ host: host2, port: POSTGRES_PORT, database, user: email2, password: token });
89043
+ const { token: token2, email: email2 } = await mintCredential(endpointPath2);
89044
+ const url = buildPostgresUrl({ host: host2, port: POSTGRES_PORT, database, user: email2, password: token2 });
89012
89045
  return { url, host: host2, port: POSTGRES_PORT, database, user: email2, endpointPath: endpointPath2 };
89013
89046
  }
89014
89047
  const host = await resolveEndpointHost(args.instance, branchId);
89015
- const email = await resolveCurrentUser();
89016
89048
  process.env.PGAPPNAME = connectionApplicationName();
89017
- return createLakebasePool({
89018
- endpoint: endpointPath2,
89019
- host,
89020
- database,
89021
- user: email,
89022
- // workspaceClient is passed through verbatim. createLakebasePool falls
89023
- // back to environment / ServiceContext when omitted.
89024
- ...args.workspaceClient !== void 0 ? { workspaceClient: args.workspaceClient } : {}
89025
- });
89049
+ if (args.workspaceClient !== void 0) {
89050
+ const email2 = await resolveCurrentUser();
89051
+ return createLakebasePool({
89052
+ endpoint: endpointPath2,
89053
+ host,
89054
+ database,
89055
+ user: email2,
89056
+ workspaceClient: args.workspaceClient
89057
+ });
89058
+ }
89059
+ const { token, email } = await mintCredential(endpointPath2);
89060
+ return createLakebasePool({ host, database, user: email, password: token });
89026
89061
  }
89027
89062
  async function resolveEndpointHost(instance, branch) {
89028
89063
  const branchId = await resolveBranchId({ instance, branch });
@@ -9535,9 +9535,46 @@ function dbcli3(args, host, timeout = KIT_TIMEOUTS.cliDefault) {
9535
9535
  }
9536
9536
 
9537
9537
  // scripts/lakebase/branch-create.ts
9538
+ function snapshotPath(instance, snapshot) {
9539
+ return snapshot.includes("/") ? snapshot : `${projectPath(instance)}/snapshots/${snapshot}`;
9540
+ }
9541
+ function applyExpiration(spec, ttl, noExpiry) {
9542
+ if (ttl && noExpiry === true) {
9543
+ throw new LakebaseBranchError(
9544
+ `Cannot set both ttl ("${ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
9545
+ );
9546
+ }
9547
+ if (ttl) spec.ttl = ttl;
9548
+ else if (noExpiry ?? true) spec.no_expiry = true;
9549
+ }
9538
9550
  async function createBranch(args) {
9539
9551
  const sanitized = sanitizeBranchName(args.branch);
9540
9552
  const lookup = { instance: args.instance, host: args.host };
9553
+ if (args.fromSnapshot) {
9554
+ if (args.parentBranch || args.sourceBranchTime || args.sourceBranchLsn) {
9555
+ throw new LakebaseBranchError(
9556
+ `fromSnapshot is mutually exclusive with parentBranch / sourceBranchTime / sourceBranchLsn (the Lakebase API rejects source_snapshot combined with any source_branch field).`
9557
+ );
9558
+ }
9559
+ const existing2 = await getBranchByName(sanitized, lookup);
9560
+ if (existing2) return existing2;
9561
+ const spec = { source_snapshot: snapshotPath(args.instance, args.fromSnapshot) };
9562
+ applyExpiration(spec, args.ttl, args.noExpiry);
9563
+ try {
9564
+ await createWithTtlRecovery(args.instance, sanitized, spec, args.host);
9565
+ } catch (err) {
9566
+ if (err instanceof LakebaseBranchTtlTooLongError) throw err;
9567
+ const landed = await getBranchByName(sanitized, lookup);
9568
+ if (!landed) throw err;
9569
+ }
9570
+ return waitForBranchReady({
9571
+ instance: args.instance,
9572
+ host: args.host,
9573
+ branch: sanitized,
9574
+ timeoutMs: args.readyTimeoutMs ?? KIT_TIMEOUTS.readyWait,
9575
+ pollIntervalMs: args.pollIntervalMs ?? KIT_TIMEOUTS.readyPoll
9576
+ });
9577
+ }
9541
9578
  let sourceBranchPath;
9542
9579
  if (args.parentBranch) {
9543
9580
  if (looksLikeBranchUid(args.parentBranch)) {
@@ -9585,19 +9622,15 @@ async function createBranch(args) {
9585
9622
  assertSourceMatches(existing, sourceBranchPath, sanitized);
9586
9623
  return existing;
9587
9624
  }
9588
- if (args.ttl && args.noExpiry === true) {
9625
+ if (args.sourceBranchTime && args.sourceBranchLsn) {
9589
9626
  throw new LakebaseBranchError(
9590
- `Cannot set both ttl ("${args.ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
9627
+ `sourceBranchTime and sourceBranchLsn are mutually exclusive \u2013 pass at most one point-in-time.`
9591
9628
  );
9592
9629
  }
9593
- const specObj = {
9594
- source_branch: sourceBranchPath
9595
- };
9596
- if (args.ttl) {
9597
- specObj.ttl = args.ttl;
9598
- } else if (args.noExpiry ?? true) {
9599
- specObj.no_expiry = true;
9600
- }
9630
+ const specObj = { source_branch: sourceBranchPath };
9631
+ if (args.sourceBranchTime) specObj.source_branch_time = args.sourceBranchTime;
9632
+ else if (args.sourceBranchLsn) specObj.source_branch_lsn = args.sourceBranchLsn;
9633
+ applyExpiration(specObj, args.ttl, args.noExpiry);
9601
9634
  try {
9602
9635
  await createWithTtlRecovery(args.instance, sanitized, specObj, args.host);
9603
9636
  } catch (err) {
@@ -9737,8 +9770,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
9737
9770
  var cached;
9738
9771
  function substrateSelfVersion() {
9739
9772
  if (cached !== void 0) return cached;
9740
- if ("0.3.8".length > 0) {
9741
- cached = "0.3.8";
9773
+ if ("0.3.10".length > 0) {
9774
+ cached = "0.3.10";
9742
9775
  return cached;
9743
9776
  }
9744
9777
  cached = "unknown";
@@ -9785,22 +9818,24 @@ async function getConnection(args) {
9785
9818
  const endpointPath2 = `projects/${args.instance}/branches/${branchId}/endpoints/${endpointName}`;
9786
9819
  if (args.output === "dsn") {
9787
9820
  const host2 = await resolveEndpointHost(args.instance, branchId);
9788
- const { token, email: email2 } = await mintCredential(endpointPath2);
9789
- const url = buildPostgresUrl({ host: host2, port: POSTGRES_PORT, database, user: email2, password: token });
9821
+ const { token: token2, email: email2 } = await mintCredential(endpointPath2);
9822
+ const url = buildPostgresUrl({ host: host2, port: POSTGRES_PORT, database, user: email2, password: token2 });
9790
9823
  return { url, host: host2, port: POSTGRES_PORT, database, user: email2, endpointPath: endpointPath2 };
9791
9824
  }
9792
9825
  const host = await resolveEndpointHost(args.instance, branchId);
9793
- const email = await resolveCurrentUser();
9794
9826
  process.env.PGAPPNAME = connectionApplicationName();
9795
- return createLakebasePool({
9796
- endpoint: endpointPath2,
9797
- host,
9798
- database,
9799
- user: email,
9800
- // workspaceClient is passed through verbatim. createLakebasePool falls
9801
- // back to environment / ServiceContext when omitted.
9802
- ...args.workspaceClient !== void 0 ? { workspaceClient: args.workspaceClient } : {}
9803
- });
9827
+ if (args.workspaceClient !== void 0) {
9828
+ const email2 = await resolveCurrentUser();
9829
+ return createLakebasePool({
9830
+ endpoint: endpointPath2,
9831
+ host,
9832
+ database,
9833
+ user: email2,
9834
+ workspaceClient: args.workspaceClient
9835
+ });
9836
+ }
9837
+ const { token, email } = await mintCredential(endpointPath2);
9838
+ return createLakebasePool({ host, database, user: email, password: token });
9804
9839
  }
9805
9840
  async function resolveEndpointHost(instance, branch) {
9806
9841
  const branchId = await resolveBranchId({ instance, branch });
@@ -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.8".length > 0) {
79633
- cached = "0.3.8";
79665
+ if ("0.3.10".length > 0) {
79666
+ cached = "0.3.10";
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 });