@databricks-solutions/lakebase-scm-utils 0.3.9 → 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/python/tests/conftest.py +22 -0
  55. package/templates/project/common/tests/e2e/__pycache__/conftest.cpython-314.pyc +0 -0
  56. package/templates/project/python/tests/__pycache__/conftest.cpython-314.pyc +0 -0
@@ -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.10".length > 0) {
758
+ cached = "0.3.10";
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.10".length > 0) {
79294
+ cached = "0.3.10";
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.10".length > 0) {
397
+ cached = "0.3.10";
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) {
@@ -464,9 +464,46 @@ function dbcli2(args, host, timeout = KIT_TIMEOUTS.cliDefault) {
464
464
  }
465
465
 
466
466
  // scripts/lakebase/branch-create.ts
467
+ function snapshotPath(instance, snapshot) {
468
+ return snapshot.includes("/") ? snapshot : `${projectPath(instance)}/snapshots/${snapshot}`;
469
+ }
470
+ function applyExpiration(spec, ttl, noExpiry) {
471
+ if (ttl && noExpiry === true) {
472
+ throw new LakebaseBranchError(
473
+ `Cannot set both ttl ("${ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
474
+ );
475
+ }
476
+ if (ttl) spec.ttl = ttl;
477
+ else if (noExpiry ?? true) spec.no_expiry = true;
478
+ }
467
479
  async function createBranch(args) {
468
480
  const sanitized = sanitizeBranchName(args.branch);
469
481
  const lookup = { instance: args.instance, host: args.host };
482
+ if (args.fromSnapshot) {
483
+ if (args.parentBranch || args.sourceBranchTime || args.sourceBranchLsn) {
484
+ throw new LakebaseBranchError(
485
+ `fromSnapshot is mutually exclusive with parentBranch / sourceBranchTime / sourceBranchLsn (the Lakebase API rejects source_snapshot combined with any source_branch field).`
486
+ );
487
+ }
488
+ const existing2 = await getBranchByName(sanitized, lookup);
489
+ if (existing2) return existing2;
490
+ const spec = { source_snapshot: snapshotPath(args.instance, args.fromSnapshot) };
491
+ applyExpiration(spec, args.ttl, args.noExpiry);
492
+ try {
493
+ await createWithTtlRecovery(args.instance, sanitized, spec, args.host);
494
+ } catch (err) {
495
+ if (err instanceof LakebaseBranchTtlTooLongError) throw err;
496
+ const landed = await getBranchByName(sanitized, lookup);
497
+ if (!landed) throw err;
498
+ }
499
+ return waitForBranchReady({
500
+ instance: args.instance,
501
+ host: args.host,
502
+ branch: sanitized,
503
+ timeoutMs: args.readyTimeoutMs ?? KIT_TIMEOUTS.readyWait,
504
+ pollIntervalMs: args.pollIntervalMs ?? KIT_TIMEOUTS.readyPoll
505
+ });
506
+ }
470
507
  let sourceBranchPath;
471
508
  if (args.parentBranch) {
472
509
  if (looksLikeBranchUid(args.parentBranch)) {
@@ -514,19 +551,15 @@ async function createBranch(args) {
514
551
  assertSourceMatches(existing, sourceBranchPath, sanitized);
515
552
  return existing;
516
553
  }
517
- if (args.ttl && args.noExpiry === true) {
554
+ if (args.sourceBranchTime && args.sourceBranchLsn) {
518
555
  throw new LakebaseBranchError(
519
- `Cannot set both ttl ("${args.ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
556
+ `sourceBranchTime and sourceBranchLsn are mutually exclusive \u2013 pass at most one point-in-time.`
520
557
  );
521
558
  }
522
- const specObj = {
523
- source_branch: sourceBranchPath
524
- };
525
- if (args.ttl) {
526
- specObj.ttl = args.ttl;
527
- } else if (args.noExpiry ?? true) {
528
- specObj.no_expiry = true;
529
- }
559
+ const specObj = { source_branch: sourceBranchPath };
560
+ if (args.sourceBranchTime) specObj.source_branch_time = args.sourceBranchTime;
561
+ else if (args.sourceBranchLsn) specObj.source_branch_lsn = args.sourceBranchLsn;
562
+ applyExpiration(specObj, args.ttl, args.noExpiry);
530
563
  try {
531
564
  await createWithTtlRecovery(args.instance, sanitized, specObj, args.host);
532
565
  } catch (err) {
@@ -531,9 +531,46 @@ function dbcli2(args, host, timeout = KIT_TIMEOUTS.cliDefault) {
531
531
  }
532
532
 
533
533
  // scripts/lakebase/branch-create.ts
534
+ function snapshotPath(instance, snapshot) {
535
+ return snapshot.includes("/") ? snapshot : `${projectPath(instance)}/snapshots/${snapshot}`;
536
+ }
537
+ function applyExpiration(spec, ttl, noExpiry) {
538
+ if (ttl && noExpiry === true) {
539
+ throw new LakebaseBranchError(
540
+ `Cannot set both ttl ("${ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
541
+ );
542
+ }
543
+ if (ttl) spec.ttl = ttl;
544
+ else if (noExpiry ?? true) spec.no_expiry = true;
545
+ }
534
546
  async function createBranch(args) {
535
547
  const sanitized = sanitizeBranchName(args.branch);
536
548
  const lookup = { instance: args.instance, host: args.host };
549
+ if (args.fromSnapshot) {
550
+ if (args.parentBranch || args.sourceBranchTime || args.sourceBranchLsn) {
551
+ throw new LakebaseBranchError(
552
+ `fromSnapshot is mutually exclusive with parentBranch / sourceBranchTime / sourceBranchLsn (the Lakebase API rejects source_snapshot combined with any source_branch field).`
553
+ );
554
+ }
555
+ const existing2 = await getBranchByName(sanitized, lookup);
556
+ if (existing2) return existing2;
557
+ const spec = { source_snapshot: snapshotPath(args.instance, args.fromSnapshot) };
558
+ applyExpiration(spec, args.ttl, args.noExpiry);
559
+ try {
560
+ await createWithTtlRecovery(args.instance, sanitized, spec, args.host);
561
+ } catch (err) {
562
+ if (err instanceof LakebaseBranchTtlTooLongError) throw err;
563
+ const landed = await getBranchByName(sanitized, lookup);
564
+ if (!landed) throw err;
565
+ }
566
+ return waitForBranchReady({
567
+ instance: args.instance,
568
+ host: args.host,
569
+ branch: sanitized,
570
+ timeoutMs: args.readyTimeoutMs ?? KIT_TIMEOUTS.readyWait,
571
+ pollIntervalMs: args.pollIntervalMs ?? KIT_TIMEOUTS.readyPoll
572
+ });
573
+ }
537
574
  let sourceBranchPath;
538
575
  if (args.parentBranch) {
539
576
  if (looksLikeBranchUid(args.parentBranch)) {
@@ -581,19 +618,15 @@ async function createBranch(args) {
581
618
  assertSourceMatches(existing, sourceBranchPath, sanitized);
582
619
  return existing;
583
620
  }
584
- if (args.ttl && args.noExpiry === true) {
621
+ if (args.sourceBranchTime && args.sourceBranchLsn) {
585
622
  throw new LakebaseBranchError(
586
- `Cannot set both ttl ("${args.ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
623
+ `sourceBranchTime and sourceBranchLsn are mutually exclusive \u2013 pass at most one point-in-time.`
587
624
  );
588
625
  }
589
- const specObj = {
590
- source_branch: sourceBranchPath
591
- };
592
- if (args.ttl) {
593
- specObj.ttl = args.ttl;
594
- } else if (args.noExpiry ?? true) {
595
- specObj.no_expiry = true;
596
- }
626
+ const specObj = { source_branch: sourceBranchPath };
627
+ if (args.sourceBranchTime) specObj.source_branch_time = args.sourceBranchTime;
628
+ else if (args.sourceBranchLsn) specObj.source_branch_lsn = args.sourceBranchLsn;
629
+ applyExpiration(specObj, args.ttl, args.noExpiry);
597
630
  try {
598
631
  await createWithTtlRecovery(args.instance, sanitized, specObj, args.host);
599
632
  } catch (err) {
@@ -491,9 +491,46 @@ function dbcli2(args, host, timeout = KIT_TIMEOUTS.cliDefault) {
491
491
  }
492
492
 
493
493
  // scripts/lakebase/branch-create.ts
494
+ function snapshotPath(instance, snapshot) {
495
+ return snapshot.includes("/") ? snapshot : `${projectPath(instance)}/snapshots/${snapshot}`;
496
+ }
497
+ function applyExpiration(spec, ttl, noExpiry) {
498
+ if (ttl && noExpiry === true) {
499
+ throw new LakebaseBranchError(
500
+ `Cannot set both ttl ("${ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
501
+ );
502
+ }
503
+ if (ttl) spec.ttl = ttl;
504
+ else if (noExpiry ?? true) spec.no_expiry = true;
505
+ }
494
506
  async function createBranch(args) {
495
507
  const sanitized = sanitizeBranchName(args.branch);
496
508
  const lookup = { instance: args.instance, host: args.host };
509
+ if (args.fromSnapshot) {
510
+ if (args.parentBranch || args.sourceBranchTime || args.sourceBranchLsn) {
511
+ throw new LakebaseBranchError(
512
+ `fromSnapshot is mutually exclusive with parentBranch / sourceBranchTime / sourceBranchLsn (the Lakebase API rejects source_snapshot combined with any source_branch field).`
513
+ );
514
+ }
515
+ const existing2 = await getBranchByName(sanitized, lookup);
516
+ if (existing2) return existing2;
517
+ const spec = { source_snapshot: snapshotPath(args.instance, args.fromSnapshot) };
518
+ applyExpiration(spec, args.ttl, args.noExpiry);
519
+ try {
520
+ await createWithTtlRecovery(args.instance, sanitized, spec, args.host);
521
+ } catch (err) {
522
+ if (err instanceof LakebaseBranchTtlTooLongError) throw err;
523
+ const landed = await getBranchByName(sanitized, lookup);
524
+ if (!landed) throw err;
525
+ }
526
+ return waitForBranchReady({
527
+ instance: args.instance,
528
+ host: args.host,
529
+ branch: sanitized,
530
+ timeoutMs: args.readyTimeoutMs ?? KIT_TIMEOUTS.readyWait,
531
+ pollIntervalMs: args.pollIntervalMs ?? KIT_TIMEOUTS.readyPoll
532
+ });
533
+ }
497
534
  let sourceBranchPath;
498
535
  if (args.parentBranch) {
499
536
  if (looksLikeBranchUid(args.parentBranch)) {
@@ -541,19 +578,15 @@ async function createBranch(args) {
541
578
  assertSourceMatches(existing, sourceBranchPath, sanitized);
542
579
  return existing;
543
580
  }
544
- if (args.ttl && args.noExpiry === true) {
581
+ if (args.sourceBranchTime && args.sourceBranchLsn) {
545
582
  throw new LakebaseBranchError(
546
- `Cannot set both ttl ("${args.ttl}") and noExpiry: true on the same branch \u2013 they are mutually exclusive. Pass one or the other.`
583
+ `sourceBranchTime and sourceBranchLsn are mutually exclusive \u2013 pass at most one point-in-time.`
547
584
  );
548
585
  }
549
- const specObj = {
550
- source_branch: sourceBranchPath
551
- };
552
- if (args.ttl) {
553
- specObj.ttl = args.ttl;
554
- } else if (args.noExpiry ?? true) {
555
- specObj.no_expiry = true;
556
- }
586
+ const specObj = { source_branch: sourceBranchPath };
587
+ if (args.sourceBranchTime) specObj.source_branch_time = args.sourceBranchTime;
588
+ else if (args.sourceBranchLsn) specObj.source_branch_lsn = args.sourceBranchLsn;
589
+ applyExpiration(specObj, args.ttl, args.noExpiry);
557
590
  try {
558
591
  await createWithTtlRecovery(args.instance, sanitized, specObj, args.host);
559
592
  } catch (err) {
@@ -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.10".length > 0) {
79294
+ cached = "0.3.10";
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.10".length > 0) {
397
+ cached = "0.3.10";
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 });
@@ -80800,8 +80800,8 @@ var PKG_NAME = "@databricks-solutions/lakebase-scm-utils";
80800
80800
  var cached;
80801
80801
  function substrateSelfVersion() {
80802
80802
  if (cached !== void 0) return cached;
80803
- if ("0.3.9".length > 0) {
80804
- cached = "0.3.9";
80803
+ if ("0.3.10".length > 0) {
80804
+ cached = "0.3.10";
80805
80805
  return cached;
80806
80806
  }
80807
80807
  cached = "unknown";
@@ -80848,22 +80848,24 @@ async function getConnection(args) {
80848
80848
  const endpointPath = `projects/${args.instance}/branches/${branchId}/endpoints/${endpointName}`;
80849
80849
  if (args.output === "dsn") {
80850
80850
  const host2 = await resolveEndpointHost(args.instance, branchId);
80851
- const { token, email: email2 } = await mintCredential(endpointPath);
80852
- const url = buildPostgresUrl({ host: host2, port: POSTGRES_PORT, database, user: email2, password: token });
80851
+ const { token: token2, email: email2 } = await mintCredential(endpointPath);
80852
+ const url = buildPostgresUrl({ host: host2, port: POSTGRES_PORT, database, user: email2, password: token2 });
80853
80853
  return { url, host: host2, port: POSTGRES_PORT, database, user: email2, endpointPath };
80854
80854
  }
80855
80855
  const host = await resolveEndpointHost(args.instance, branchId);
80856
- const email = await resolveCurrentUser();
80857
80856
  process.env.PGAPPNAME = connectionApplicationName();
80858
- return createLakebasePool({
80859
- endpoint: endpointPath,
80860
- host,
80861
- database,
80862
- user: email,
80863
- // workspaceClient is passed through verbatim. createLakebasePool falls
80864
- // back to environment / ServiceContext when omitted.
80865
- ...args.workspaceClient !== void 0 ? { workspaceClient: args.workspaceClient } : {}
80866
- });
80857
+ if (args.workspaceClient !== void 0) {
80858
+ const email2 = await resolveCurrentUser();
80859
+ return createLakebasePool({
80860
+ endpoint: endpointPath,
80861
+ host,
80862
+ database,
80863
+ user: email2,
80864
+ workspaceClient: args.workspaceClient
80865
+ });
80866
+ }
80867
+ const { token, email } = await mintCredential(endpointPath);
80868
+ return createLakebasePool({ host, database, user: email, password: token });
80867
80869
  }
80868
80870
  async function resolveEndpointHost(instance, branch) {
80869
80871
  const branchId = await resolveBranchId({ instance, branch });