@abloatai/ablo 0.30.2 → 0.31.0

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.
package/dist/cli.cjs CHANGED
@@ -1755,9 +1755,9 @@ var init_query = __esm({
1755
1755
  CLOSE = {};
1756
1756
  Query = class extends Promise {
1757
1757
  constructor(strings, args, handler, canceller, options = {}) {
1758
- let resolve7, reject;
1758
+ let resolve6, reject;
1759
1759
  super((a, b4) => {
1760
- resolve7 = a;
1760
+ resolve6 = a;
1761
1761
  reject = b4;
1762
1762
  });
1763
1763
  this.tagged = Array.isArray(strings.raw);
@@ -1768,7 +1768,7 @@ var init_query = __esm({
1768
1768
  this.options = options;
1769
1769
  this.state = null;
1770
1770
  this.statement = null;
1771
- this.resolve = (x2) => (this.active = false, resolve7(x2));
1771
+ this.resolve = (x2) => (this.active = false, resolve6(x2));
1772
1772
  this.reject = (x2) => (this.active = false, reject(x2));
1773
1773
  this.active = false;
1774
1774
  this.cancelled = null;
@@ -1816,12 +1816,12 @@ var init_query = __esm({
1816
1816
  if (this.executed && !this.active)
1817
1817
  return { done: true };
1818
1818
  prev && prev();
1819
- const promise = new Promise((resolve7, reject) => {
1819
+ const promise = new Promise((resolve6, reject) => {
1820
1820
  this.cursorFn = (value) => {
1821
- resolve7({ value, done: false });
1821
+ resolve6({ value, done: false });
1822
1822
  return new Promise((r2) => prev = r2);
1823
1823
  };
1824
- this.resolve = () => (this.active = false, resolve7({ done: true }));
1824
+ this.resolve = () => (this.active = false, resolve6({ done: true }));
1825
1825
  this.reject = (x2) => (this.active = false, reject(x2));
1826
1826
  });
1827
1827
  this.execute();
@@ -2419,12 +2419,12 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
2419
2419
  x2.on("drain", drain);
2420
2420
  return x2;
2421
2421
  }
2422
- async function cancel({ pid, secret }, resolve7, reject) {
2422
+ async function cancel({ pid, secret }, resolve6, reject) {
2423
2423
  try {
2424
2424
  cancelMessage = bytes_default().i32(16).i32(80877102).i32(pid).i32(secret).end(16);
2425
2425
  await connect2();
2426
2426
  socket.once("error", reject);
2427
- socket.once("close", resolve7);
2427
+ socket.once("close", resolve6);
2428
2428
  } catch (error2) {
2429
2429
  reject(error2);
2430
2430
  }
@@ -3447,7 +3447,7 @@ var init_subscribe = __esm({
3447
3447
 
3448
3448
  // node_modules/postgres/src/large.js
3449
3449
  function largeObject(sql, oid, mode2 = 131072 | 262144) {
3450
- return new Promise(async (resolve7, reject) => {
3450
+ return new Promise(async (resolve6, reject) => {
3451
3451
  await sql.begin(async (sql2) => {
3452
3452
  let finish;
3453
3453
  !oid && ([{ oid }] = await sql2`select lo_creat(-1) as oid`);
@@ -3473,7 +3473,7 @@ function largeObject(sql, oid, mode2 = 131072 | 262144) {
3473
3473
  ) seek
3474
3474
  `
3475
3475
  };
3476
- resolve7(lo);
3476
+ resolve6(lo);
3477
3477
  return new Promise(async (r2) => finish = r2);
3478
3478
  async function readable({
3479
3479
  highWaterMark = 2048 * 8,
@@ -3635,8 +3635,8 @@ function Postgres(a, b4) {
3635
3635
  }
3636
3636
  async function reserve() {
3637
3637
  const queue = queue_default();
3638
- const c = open.length ? open.shift() : await new Promise((resolve7, reject) => {
3639
- const query = { reserve: resolve7, reject };
3638
+ const c = open.length ? open.shift() : await new Promise((resolve6, reject) => {
3639
+ const query = { reserve: resolve6, reject };
3640
3640
  queries.push(query);
3641
3641
  closed.length && connect2(closed.shift(), query);
3642
3642
  });
@@ -3673,9 +3673,9 @@ function Postgres(a, b4) {
3673
3673
  let uncaughtError, result;
3674
3674
  name && await sql2`savepoint ${sql2(name)}`;
3675
3675
  try {
3676
- result = await new Promise((resolve7, reject) => {
3676
+ result = await new Promise((resolve6, reject) => {
3677
3677
  const x2 = fn2(sql2);
3678
- Promise.resolve(Array.isArray(x2) ? Promise.all(x2) : x2).then(resolve7, reject);
3678
+ Promise.resolve(Array.isArray(x2) ? Promise.all(x2) : x2).then(resolve6, reject);
3679
3679
  });
3680
3680
  if (uncaughtError)
3681
3681
  throw uncaughtError;
@@ -3732,8 +3732,8 @@ function Postgres(a, b4) {
3732
3732
  return c.execute(query) ? move(c, busy) : move(c, full);
3733
3733
  }
3734
3734
  function cancel(query) {
3735
- return new Promise((resolve7, reject) => {
3736
- query.state ? query.active ? connection_default(options).cancel(query.state, resolve7, reject) : query.cancelled = { resolve: resolve7, reject } : (queries.remove(query), query.cancelled = true, query.reject(Errors.generic("57014", "canceling statement due to user request")), resolve7());
3735
+ return new Promise((resolve6, reject) => {
3736
+ query.state ? query.active ? connection_default(options).cancel(query.state, resolve6, reject) : query.cancelled = { resolve: resolve6, reject } : (queries.remove(query), query.cancelled = true, query.reject(Errors.generic("57014", "canceling statement due to user request")), resolve6());
3737
3737
  });
3738
3738
  }
3739
3739
  async function end({ timeout = null } = {}) {
@@ -3752,11 +3752,11 @@ function Postgres(a, b4) {
3752
3752
  async function close() {
3753
3753
  await Promise.all(connections.map((c) => c.end()));
3754
3754
  }
3755
- async function destroy(resolve7) {
3755
+ async function destroy(resolve6) {
3756
3756
  await Promise.all(connections.map((c) => c.terminate()));
3757
3757
  while (queries.length)
3758
3758
  queries.shift().reject(Errors.connection("CONNECTION_DESTROYED", options));
3759
- resolve7();
3759
+ resolve6();
3760
3760
  }
3761
3761
  function connect2(c, query) {
3762
3762
  move(c, connecting);
@@ -3955,10 +3955,22 @@ function rewriteDatabaseUrl(ownerUrl, role, password) {
3955
3955
  url.password = password;
3956
3956
  return url.toString();
3957
3957
  }
3958
+ function readProjectReplicationUrlWithSource(cwd = process.cwd()) {
3959
+ for (const variable of REPLICATION_URL_VARS) {
3960
+ const value = process.env[variable];
3961
+ if (value) return { url: value, variable };
3962
+ }
3963
+ for (const variable of REPLICATION_URL_VARS) {
3964
+ const value = readProjectEnvValue(variable, cwd);
3965
+ if (value) return { url: value, variable };
3966
+ }
3967
+ return null;
3968
+ }
3958
3969
  function readProjectDatabaseUrl(cwd = process.cwd()) {
3959
- const fromEnv = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
3960
- if (fromEnv) return fromEnv;
3961
- return readProjectEnvValue("DATABASE_URL", cwd);
3970
+ return readProjectReplicationUrlWithSource(cwd)?.url ?? null;
3971
+ }
3972
+ function readProjectAdminDatabaseUrl(cwd = process.cwd()) {
3973
+ return process.env.DATABASE_URL ?? readProjectEnvValue("DATABASE_URL", cwd);
3962
3974
  }
3963
3975
  function readProjectWriteDatabaseUrl(cwd = process.cwd()) {
3964
3976
  const fromEnv = process.env.ABLO_WRITE_DATABASE_URL;
@@ -3984,7 +3996,7 @@ function readProjectApiKey(cwd = process.cwd()) {
3984
3996
  }
3985
3997
  return null;
3986
3998
  }
3987
- var import_crypto2, import_fs2, import_path;
3999
+ var import_crypto2, import_fs2, import_path, REPLICATION_URL_VARS, DATABASE_URL_REMOVAL_VERSION;
3988
4000
  var init_dbRole = __esm({
3989
4001
  "src/cli/dbRole.ts"() {
3990
4002
  "use strict";
@@ -3994,6 +4006,8 @@ var init_dbRole = __esm({
3994
4006
  init_dist2();
3995
4007
  import_fs2 = require("fs");
3996
4008
  import_path = require("path");
4009
+ REPLICATION_URL_VARS = ["ABLO_REPLICATION_DATABASE_URL", "DATABASE_URL"];
4010
+ DATABASE_URL_REMOVAL_VERSION = "0.32.0";
3997
4011
  }
3998
4012
  });
3999
4013
 
@@ -5426,8 +5440,17 @@ var connectApply_exports = {};
5426
5440
  __export(connectApply_exports, {
5427
5441
  connectApplyPlan: () => connectApplyPlan,
5428
5442
  passwordClause: () => passwordClause,
5443
+ postRegistrationOutcome: () => postRegistrationOutcome,
5429
5444
  runConnectApply: () => runConnectApply
5430
5445
  });
5446
+ function postRegistrationOutcome(input) {
5447
+ if (input.registered) return { exitCode: 0, notice: null };
5448
+ if (!input.rotating) return { exitCode: 1, notice: null };
5449
+ return {
5450
+ exitCode: 1,
5451
+ notice: "The new passwords are set in your database, but Ablo could not be updated with them.\nAblo still holds the previous password, which no longer works \u2014 writes will fail until\nyou re-run `ablo connect --rotate` (each run is idempotent and rotates a fresh password)."
5452
+ };
5453
+ }
5431
5454
  function passwordClause(password, mode2) {
5432
5455
  return mode2 === "scram-verifier" ? scramSha256Verifier(password) : password.replace(/'/g, "''");
5433
5456
  }
@@ -5496,38 +5519,6 @@ function isPlaintextRefusal(err) {
5496
5519
  const message = err instanceof Error ? err.message : String(err);
5497
5520
  return /plaintext password/i.test(message);
5498
5521
  }
5499
- function persistScopedUrls(vars, cwd = process.cwd()) {
5500
- const targets = [".env.local", ".env"];
5501
- const existing = targets.find((name) => (0, import_fs6.existsSync)((0, import_path4.resolve)(cwd, name)));
5502
- const file = existing ?? ".env.local";
5503
- const path = (0, import_path4.resolve)(cwd, file);
5504
- let content = (0, import_fs6.existsSync)(path) ? (0, import_fs6.readFileSync)(path, "utf8") : "";
5505
- for (const [name, value] of Object.entries(vars)) {
5506
- const line = `${name}=${value}`;
5507
- const pattern = new RegExp(`^${name}=.*$`, "m");
5508
- if (pattern.test(content)) {
5509
- content = content.replace(pattern, line);
5510
- } else {
5511
- content = `${content}${content.endsWith("\n") || content.length === 0 ? "" : "\n"}${line}
5512
- `;
5513
- }
5514
- }
5515
- if (!(0, import_fs6.existsSync)(path)) {
5516
- (0, import_fs6.writeFileSync)(path, content, { mode: 384 });
5517
- } else {
5518
- (0, import_fs6.writeFileSync)(path, content);
5519
- }
5520
- const gitignorePath = (0, import_path4.resolve)(cwd, ".gitignore");
5521
- const gitignore = (0, import_fs6.existsSync)(gitignorePath) ? (0, import_fs6.readFileSync)(gitignorePath, "utf8") : "";
5522
- if (!/^\.env(\.local|\*|\.\*)?$/m.test(gitignore)) {
5523
- (0, import_fs6.appendFileSync)(
5524
- gitignorePath,
5525
- `${gitignore.endsWith("\n") || gitignore.length === 0 ? "" : "\n"}.env.local
5526
- `
5527
- );
5528
- }
5529
- return file;
5530
- }
5531
5522
  function printPlan2(steps, showSql) {
5532
5523
  console.log(` This sets up your database for Ablo:
5533
5524
  `);
@@ -5578,16 +5569,38 @@ async function executePlan(sql, steps, rebuildPlaintext) {
5578
5569
  return { walDeferred };
5579
5570
  }
5580
5571
  async function runConnectApply(args) {
5581
- const adminUrl = readProjectDatabaseUrl();
5572
+ const rotating = args.rotate;
5573
+ const verb = rotating ? "connect --rotate" : "connect --apply";
5574
+ const adminUrl = args.url ?? readProjectAdminDatabaseUrl();
5582
5575
  if (!adminUrl) {
5583
5576
  console.error(
5584
- import_picocolors6.default.red(" No DATABASE_URL found.") + import_picocolors6.default.dim(` Point it at your admin connection and re-run.`)
5577
+ import_picocolors6.default.red(" No admin connection string.") + import_picocolors6.default.dim(` Pass ${import_picocolors6.default.bold("--url <admin-conn>")} (or set ${import_picocolors6.default.bold("DATABASE_URL")}) and re-run.`)
5578
+ );
5579
+ process.exit(1);
5580
+ }
5581
+ const adminSource = args.url ? "--url" : "DATABASE_URL";
5582
+ let target = "your database";
5583
+ try {
5584
+ const parsed = new URL(adminUrl);
5585
+ target = `${parsed.host}${parsed.pathname === "/" ? "" : parsed.pathname}`;
5586
+ } catch {
5587
+ }
5588
+ const apiKey = resolveApiKey();
5589
+ if (!apiKey) {
5590
+ console.error(
5591
+ import_picocolors6.default.red(" Not logged in.") + import_picocolors6.default.dim(
5592
+ ` Run ${import_picocolors6.default.bold("ablo login")} (or set ${import_picocolors6.default.bold("ABLO_API_KEY")}) so Ablo knows which project to register this database for.`
5593
+ )
5585
5594
  );
5586
5595
  process.exit(1);
5587
5596
  }
5588
5597
  console.log(
5589
5598
  `
5590
- ${brand("ablo")} ${import_picocolors6.default.dim("connect --apply")} ${import_picocolors6.default.dim("set up your database for Ablo")}
5599
+ ${brand("ablo")} ${import_picocolors6.default.dim(verb)} ${import_picocolors6.default.dim(rotating ? "re-key the scoped roles" : "set up your database for Ablo")}
5600
+ `
5601
+ );
5602
+ console.log(
5603
+ ` ${import_picocolors6.default.dim("\u2192")} ${import_picocolors6.default.bold(target)}${adminSource === "DATABASE_URL" ? import_picocolors6.default.dim(" (admin via DATABASE_URL)") : ""}
5591
5604
  `
5592
5605
  );
5593
5606
  const admin = src_default(adminUrl, { max: 1, prepare: false, connect_timeout: 10, onnotice: () => {
@@ -5604,7 +5617,7 @@ async function runConnectApply(args) {
5604
5617
  if (!capability || !(capability.rolsuper || capability.rolcreaterole)) {
5605
5618
  await admin.end({ timeout: 2 });
5606
5619
  console.error(
5607
- import_picocolors6.default.red(` ${capability?.rolname ?? "This role"} can't create roles.`) + import_picocolors6.default.dim(` Point DATABASE_URL at your owner/admin connection and re-run.`)
5620
+ import_picocolors6.default.red(` ${capability?.rolname ?? "This role"} can't create roles.`) + import_picocolors6.default.dim(` Point ${import_picocolors6.default.bold("--url")} at your owner/admin connection and re-run.`)
5608
5621
  );
5609
5622
  process.exit(1);
5610
5623
  }
@@ -5631,7 +5644,7 @@ async function runConnectApply(args) {
5631
5644
  process.exit(1);
5632
5645
  }
5633
5646
  const proceed = await ye({
5634
- message: `Set this up in ${capability.rolname === "postgres" ? "your database" : `your database as ${capability.rolname}`}?`,
5647
+ message: rotating ? `Re-key Ablo's roles on ${target}?` : `Provision Ablo on ${target}?`,
5635
5648
  initialValue: true
5636
5649
  });
5637
5650
  if (pD(proceed) || !proceed) {
@@ -5659,49 +5672,55 @@ async function runConnectApply(args) {
5659
5672
  const writeRole = args.writeRole && args.writeRole.length > 0 ? args.writeRole : ABLO_WRITE_ROLE;
5660
5673
  const replicationUrl = rewriteDatabaseUrl(adminUrl, role, replicationPassword);
5661
5674
  const writeUrl = rewriteDatabaseUrl(adminUrl, writeRole, writePassword);
5662
- const where = persistScopedUrls({
5663
- DATABASE_URL: replicationUrl,
5664
- ABLO_WRITE_DATABASE_URL: writeUrl
5665
- });
5666
- console.log(
5667
- `
5668
- ${import_picocolors6.default.green("\u2713")} Roles created \u2014 ${import_picocolors6.default.bold("DATABASE_URL")} + ${import_picocolors6.default.bold("ABLO_WRITE_DATABASE_URL")} updated in ${import_picocolors6.default.bold(where)}.
5669
- `
5670
- );
5671
- const verifier = src_default(replicationUrl, { max: 1, prepare: false, connect_timeout: 10, onnotice: () => {
5672
- } });
5673
- let items;
5674
- try {
5675
- items = await probeReadiness(verifier);
5676
- } catch {
5677
- await verifier.end({ timeout: 2 }).catch(() => void 0);
5678
- console.log(import_picocolors6.default.dim(` Verify when reachable: `) + import_picocolors6.default.cyan("npx ablo connect --check") + "\n");
5679
- process.exit(0);
5680
- }
5681
- await verifier.end({ timeout: 2 });
5682
- const failed = items.filter((i) => !i.ok);
5675
+ console.log(`
5676
+ ${import_picocolors6.default.green("\u2713")} Roles ${rotating ? "re-keyed" : "created"}.
5677
+ `);
5683
5678
  if (walDeferred) {
5684
5679
  console.log(
5685
5680
  ` ${import_picocolors6.default.yellow("!")} One step left \u2014 logical replication needs a restart.
5686
5681
  ` + import_picocolors6.default.dim(` RDS/Aurora: set rds.logical_replication=1, reboot. Neon: enable it in settings.
5687
5682
  `) + `
5688
- Then: ${import_picocolors6.default.cyan("npx ablo connect --register")}
5683
+ Then re-run: ${import_picocolors6.default.cyan(`npx ablo ${verb}`)}
5689
5684
  `
5690
5685
  );
5691
5686
  process.exit(0);
5692
5687
  }
5693
- if (failed.length === 0) {
5694
- console.log(` ${import_picocolors6.default.green("\u2713")} Verified. Next: ${import_picocolors6.default.cyan("npx ablo connect --register")}
5695
- `);
5696
- } else {
5688
+ const verifier = src_default(replicationUrl, { max: 1, prepare: false, connect_timeout: 10, onnotice: () => {
5689
+ } });
5690
+ let items;
5691
+ try {
5692
+ items = await probeReadiness(verifier);
5693
+ } catch {
5694
+ items = null;
5695
+ console.log(import_picocolors6.default.dim(` Couldn't verify from here; Ablo will validate from its own network.
5696
+ `));
5697
+ }
5698
+ await verifier.end({ timeout: 2 }).catch(() => void 0);
5699
+ const failed = items?.filter((i) => !i.ok) ?? [];
5700
+ if (failed.length > 0) {
5697
5701
  for (const item of failed) console.log(` ${import_picocolors6.default.yellow("!")} ${item.label}`);
5698
5702
  console.log(`
5699
- ${import_picocolors6.default.dim("Resolve, then")} ${import_picocolors6.default.cyan("npx ablo connect --register")}
5703
+ ${import_picocolors6.default.dim("Resolve, then re-run")} ${import_picocolors6.default.cyan(`npx ablo ${verb}`)}
5700
5704
  `);
5705
+ process.exit(1);
5701
5706
  }
5702
- process.exit(0);
5707
+ const apiUrl2 = (process.env.ABLO_API_URL ?? DEFAULT_URL).replace(/\/+$/, "");
5708
+ const registered = await registerDirectDataSource({
5709
+ apiUrl: apiUrl2,
5710
+ apiKey,
5711
+ replicationUrl,
5712
+ writeUrl,
5713
+ route: args.route
5714
+ });
5715
+ const outcome = postRegistrationOutcome({ rotating, registered });
5716
+ if (outcome.notice) {
5717
+ console.error(`
5718
+ ${import_picocolors6.default.red(outcome.notice.split("\n").join("\n "))}
5719
+ `);
5720
+ }
5721
+ process.exit(outcome.exitCode);
5703
5722
  }
5704
- var import_picocolors6, import_fs6, import_path4;
5723
+ var import_picocolors6;
5705
5724
  var init_connectApply = __esm({
5706
5725
  "src/cli/connectApply.ts"() {
5707
5726
  "use strict";
@@ -5709,10 +5728,10 @@ var init_connectApply = __esm({
5709
5728
  import_picocolors6 = __toESM(require_picocolors(), 1);
5710
5729
  init_src();
5711
5730
  init_dist2();
5712
- import_fs6 = require("fs");
5713
- import_path4 = require("path");
5714
5731
  init_connect();
5715
5732
  init_dbRole();
5733
+ init_config();
5734
+ init_push();
5716
5735
  init_theme();
5717
5736
  }
5718
5737
  });
@@ -5722,6 +5741,8 @@ function parseConnectArgs(argv) {
5722
5741
  let check2 = false;
5723
5742
  let register = false;
5724
5743
  let apply2 = false;
5744
+ let rotate = false;
5745
+ let url;
5725
5746
  let yes = false;
5726
5747
  let showSql = false;
5727
5748
  let auditInfra = false;
@@ -5741,6 +5762,12 @@ function parseConnectArgs(argv) {
5741
5762
  case "--apply":
5742
5763
  apply2 = true;
5743
5764
  break;
5765
+ case "--rotate":
5766
+ rotate = true;
5767
+ break;
5768
+ case "--url":
5769
+ url = argv[++i] ?? url;
5770
+ break;
5744
5771
  case "--yes":
5745
5772
  case "-y":
5746
5773
  yes = true;
@@ -5782,7 +5809,7 @@ function parseConnectArgs(argv) {
5782
5809
  code: "cli_invalid_arguments"
5783
5810
  });
5784
5811
  }
5785
- return { check: check2, register, apply: apply2, yes, showSql, auditInfra, tables, role, writeRole, route };
5812
+ return { check: check2, register, apply: apply2, rotate, url, yes, showSql, auditInfra, tables, role, writeRole, route };
5786
5813
  }
5787
5814
  function quoteIdent(id) {
5788
5815
  return `"${id.replace(/"/g, '""')}"`;
@@ -6086,15 +6113,36 @@ async function auditTenantSyncInfra(sql) {
6086
6113
  }
6087
6114
  return artifacts;
6088
6115
  }
6089
- function requireDatabaseUrl(verb) {
6090
- const dbUrl = readProjectDatabaseUrl();
6091
- if (!dbUrl) {
6116
+ function requireDatabaseUrl(verb, opts) {
6117
+ const resolved = readProjectReplicationUrlWithSource();
6118
+ if (!resolved) {
6092
6119
  console.error(
6093
- import_picocolors7.default.red(" No DATABASE_URL found (checked process env, .env.local, .env).") + import_picocolors7.default.dim(` Set it to the Postgres you want Ablo to read, then re-run ${import_picocolors7.default.bold(`ablo connect ${verb}`)}.`)
6120
+ import_picocolors7.default.red(" No replication connection found (checked process env, .env.local, .env).") + import_picocolors7.default.dim(
6121
+ ` Set ${import_picocolors7.default.bold("ABLO_REPLICATION_DATABASE_URL")} to the ${ABLO_REPLICATION_ROLE} connection printed by ${import_picocolors7.default.bold("ablo connect")}, then re-run ${import_picocolors7.default.bold(`ablo connect ${verb}`)}.`
6122
+ )
6094
6123
  );
6095
6124
  process.exit(1);
6096
6125
  }
6097
- return dbUrl;
6126
+ if (resolved.variable === "DATABASE_URL") {
6127
+ console.warn(
6128
+ import_picocolors7.default.yellow(` ! Falling back to ${import_picocolors7.default.bold("DATABASE_URL")} for the replication role \u2014 deprecated,`) + import_picocolors7.default.dim(
6129
+ ` removed in ${DATABASE_URL_REMOVAL_VERSION}.
6130
+ If this is your app's own connection string, this is NOT validating the ${import_picocolors7.default.bold(ABLO_REPLICATION_ROLE)} role.
6131
+ Set ${import_picocolors7.default.bold("ABLO_REPLICATION_DATABASE_URL")} to the connection ${import_picocolors7.default.bold("ablo connect")} printed.
6132
+ `
6133
+ )
6134
+ );
6135
+ if (!(opts?.allowDeprecatedFallback ?? false)) {
6136
+ console.error(
6137
+ import_picocolors7.default.red(` Refusing to ${verb.replace(/^--/, "")} against a ${import_picocolors7.default.bold("DATABASE_URL")} fallback.`) + import_picocolors7.default.dim(
6138
+ ` Set ${import_picocolors7.default.bold("ABLO_REPLICATION_DATABASE_URL")} explicitly and re-run. (${import_picocolors7.default.bold("ablo connect --check")} still accepts the fallback so you can inspect it.)
6139
+ `
6140
+ )
6141
+ );
6142
+ process.exit(1);
6143
+ }
6144
+ }
6145
+ return resolved.url;
6098
6146
  }
6099
6147
  function requireWriteDatabaseUrl(verb) {
6100
6148
  const dbUrl = readProjectWriteDatabaseUrl();
@@ -6192,7 +6240,7 @@ async function runRemoteCheck(dbUrl, writeDbUrl, localReason) {
6192
6240
  process.exit(1);
6193
6241
  }
6194
6242
  async function runCheck() {
6195
- const dbUrl = requireDatabaseUrl("--check");
6243
+ const dbUrl = requireDatabaseUrl("--check", { allowDeprecatedFallback: true });
6196
6244
  const writeDbUrl = requireWriteDatabaseUrl("--check");
6197
6245
  console.log(`
6198
6246
  ${brand("ablo")} ${import_picocolors7.default.dim("connect --check")} ${import_picocolors7.default.dim("direct-write + WAL readiness")}
@@ -6269,34 +6317,44 @@ async function runRegister(args) {
6269
6317
  process.exit(1);
6270
6318
  }
6271
6319
  const apiUrl2 = (process.env.ABLO_API_URL ?? DEFAULT_URL).replace(/\/+$/, "");
6320
+ const registered = await registerDirectDataSource({
6321
+ apiUrl: apiUrl2,
6322
+ apiKey,
6323
+ replicationUrl: dbUrl,
6324
+ writeUrl: writeDbUrl,
6325
+ route: args.route
6326
+ });
6327
+ process.exit(registered ? 0 : 1);
6328
+ }
6329
+ async function registerDirectDataSource(opts) {
6272
6330
  let res;
6273
6331
  try {
6274
- res = await fetch(registerEndpoint(apiUrl2), {
6332
+ res = await fetch(registerEndpoint(opts.apiUrl), {
6275
6333
  method: "POST",
6276
- headers: { "content-type": "application/json", authorization: `Bearer ${apiKey}` },
6334
+ headers: { "content-type": "application/json", authorization: `Bearer ${opts.apiKey}` },
6277
6335
  body: JSON.stringify({
6278
6336
  connection: "direct",
6279
- connectionString: dbUrl,
6280
- writeConnectionString: writeDbUrl,
6281
- route: args.route
6337
+ connectionString: opts.replicationUrl,
6338
+ writeConnectionString: opts.writeUrl,
6339
+ route: opts.route
6282
6340
  })
6283
6341
  });
6284
6342
  } catch (err) {
6285
6343
  console.error(import_picocolors7.default.red(`
6286
- Couldn't reach ${apiUrl2}: ${err instanceof Error ? err.message : String(err)}
6344
+ Couldn't reach ${opts.apiUrl}: ${err instanceof Error ? err.message : String(err)}
6287
6345
  `));
6288
- process.exit(1);
6346
+ return false;
6289
6347
  }
6290
6348
  if (res.ok) {
6291
6349
  const body2 = await res.json().catch(() => ({}));
6292
- const statusNote = body2.status === "active" ? `${args.route}, active` : args.route;
6350
+ const statusNote = body2.status === "active" ? `${opts.route}, active` : opts.route;
6293
6351
  console.log(
6294
6352
  `
6295
6353
  ${import_picocolors7.default.green("\u2713")} Registered${body2.host ? ` ${import_picocolors7.default.dim(body2.host)}` : ""}${body2.id ? ` ${import_picocolors7.default.dim(`(${body2.id})`)}` : ""} as a direct DataSource (${statusNote}).
6296
6354
  Customer COMMIT is durable acceptance; correlated WAL promotes queued writes to confirmed.
6297
6355
  `
6298
6356
  );
6299
- process.exit(0);
6357
+ return true;
6300
6358
  }
6301
6359
  const body = await res.json().catch(() => ({}));
6302
6360
  const code = body.code ?? body.error?.code;
@@ -6327,10 +6385,10 @@ async function runRegister(args) {
6327
6385
  );
6328
6386
  }
6329
6387
  console.error();
6330
- process.exit(1);
6388
+ return false;
6331
6389
  }
6332
6390
  async function runAuditInfra() {
6333
- const dbUrl = requireDatabaseUrl("--audit-infra");
6391
+ const dbUrl = requireDatabaseUrl("--audit-infra", { allowDeprecatedFallback: true });
6334
6392
  const sql = src_default(dbUrl, { max: 1, prepare: false, onnotice: () => {
6335
6393
  } });
6336
6394
  let artifacts;
@@ -6370,7 +6428,7 @@ async function connect(argv) {
6370
6428
  console.error(import_picocolors7.default.red(` ${err instanceof Error ? err.message : String(err)}`));
6371
6429
  process.exit(1);
6372
6430
  }
6373
- if (args.apply) {
6431
+ if (args.apply || args.rotate) {
6374
6432
  const { runConnectApply: runConnectApply2 } = await Promise.resolve().then(() => (init_connectApply(), connectApply_exports));
6375
6433
  await runConnectApply2(args);
6376
6434
  return;
@@ -6429,16 +6487,21 @@ var init_connect = __esm({
6429
6487
  orders it with external changes, and confirms it. Your Postgres remains authoritative.
6430
6488
 
6431
6489
  Usage:
6432
- npx ablo connect --apply Set it up for you: create the roles, publish, verify
6490
+ npx ablo connect --apply Set it up end to end: create the roles, publish, register
6491
+ npx ablo connect --url <admin-conn> Admin connection for --apply/--rotate (else DATABASE_URL)
6492
+ npx ablo connect --rotate New passwords for both scoped roles, then re-register
6433
6493
  npx ablo connect Print the exact setup SQL instead of running it
6434
6494
  npx ablo connect --tables a,b,c Publish only these tables (default: all tables)
6435
6495
  npx ablo connect --role <name> Name the replication role (default: ablo_replicator)
6436
6496
  npx ablo connect --write-role <name> Name the DML role (default: ablo_writer)
6437
6497
  npx ablo connect --route <route> public-allowlist | privatelink | peering | vpn
6438
- npx ablo connect --check Validate DATABASE_URL + ABLO_WRITE_DATABASE_URL
6498
+ npx ablo connect --check Validate the scoped roles (ABLO_REPLICATION_DATABASE_URL + ABLO_WRITE_DATABASE_URL)
6439
6499
  npx ablo connect --register Register both scoped credentials as one direct DataSource
6440
6500
  npx ablo connect --audit-infra Read-only Stage 5 audit for deprecated Ablo sync tables/types
6441
6501
 
6502
+ --apply registers with Ablo directly; the admin credential is used only on this
6503
+ machine and never persisted. Your app holds only ABLO_API_KEY.
6504
+
6442
6505
  --apply also takes --yes (skip the confirm) and --show-sql (show the exact statements).`;
6443
6506
  }
6444
6507
  });
@@ -131336,7 +131399,7 @@ ${lanes.join("\n")}
131336
131399
  }
131337
131400
  }
131338
131401
  function createImportCallExpressionAMD(arg, containsLexicalThis) {
131339
- const resolve7 = factory2.createUniqueName("resolve");
131402
+ const resolve6 = factory2.createUniqueName("resolve");
131340
131403
  const reject = factory2.createUniqueName("reject");
131341
131404
  const parameters = [
131342
131405
  factory2.createParameterDeclaration(
@@ -131345,7 +131408,7 @@ ${lanes.join("\n")}
131345
131408
  /*dotDotDotToken*/
131346
131409
  void 0,
131347
131410
  /*name*/
131348
- resolve7
131411
+ resolve6
131349
131412
  ),
131350
131413
  factory2.createParameterDeclaration(
131351
131414
  /*modifiers*/
@@ -131362,7 +131425,7 @@ ${lanes.join("\n")}
131362
131425
  factory2.createIdentifier("require"),
131363
131426
  /*typeArguments*/
131364
131427
  void 0,
131365
- [factory2.createArrayLiteralExpression([arg || factory2.createOmittedExpression()]), resolve7, reject]
131428
+ [factory2.createArrayLiteralExpression([arg || factory2.createOmittedExpression()]), resolve6, reject]
131366
131429
  )
131367
131430
  )
131368
131431
  ]);
@@ -217971,8 +218034,8 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
217971
218034
  installPackage(options) {
217972
218035
  this.packageInstallId++;
217973
218036
  const request2 = { kind: "installPackage", ...options, id: this.packageInstallId };
217974
- const promise = new Promise((resolve7, reject) => {
217975
- (this.packageInstalledPromise ?? (this.packageInstalledPromise = /* @__PURE__ */ new Map())).set(this.packageInstallId, { resolve: resolve7, reject });
218037
+ const promise = new Promise((resolve6, reject) => {
218038
+ (this.packageInstalledPromise ?? (this.packageInstalledPromise = /* @__PURE__ */ new Map())).set(this.packageInstallId, { resolve: resolve6, reject });
217976
218039
  });
217977
218040
  this.installer.send(request2);
217978
218041
  return promise;
@@ -220245,7 +220308,7 @@ var require_path_browserify = __commonJS({
220245
220308
  }
220246
220309
  var posix = {
220247
220310
  // path.resolve([from ...], to)
220248
- resolve: function resolve7() {
220311
+ resolve: function resolve6() {
220249
220312
  var resolvedPath = "";
220250
220313
  var resolvedAbsolute = false;
220251
220314
  var cwd;
@@ -225145,41 +225208,41 @@ var require_queue = __commonJS({
225145
225208
  queue.drained = drained;
225146
225209
  return queue;
225147
225210
  function push2(value) {
225148
- var p2 = new Promise(function(resolve7, reject) {
225211
+ var p2 = new Promise(function(resolve6, reject) {
225149
225212
  pushCb(value, function(err, result) {
225150
225213
  if (err) {
225151
225214
  reject(err);
225152
225215
  return;
225153
225216
  }
225154
- resolve7(result);
225217
+ resolve6(result);
225155
225218
  });
225156
225219
  });
225157
225220
  p2.catch(noop3);
225158
225221
  return p2;
225159
225222
  }
225160
225223
  function unshift(value) {
225161
- var p2 = new Promise(function(resolve7, reject) {
225224
+ var p2 = new Promise(function(resolve6, reject) {
225162
225225
  unshiftCb(value, function(err, result) {
225163
225226
  if (err) {
225164
225227
  reject(err);
225165
225228
  return;
225166
225229
  }
225167
- resolve7(result);
225230
+ resolve6(result);
225168
225231
  });
225169
225232
  });
225170
225233
  p2.catch(noop3);
225171
225234
  return p2;
225172
225235
  }
225173
225236
  function drained() {
225174
- var p2 = new Promise(function(resolve7) {
225237
+ var p2 = new Promise(function(resolve6) {
225175
225238
  process.nextTick(function() {
225176
225239
  if (queue.idle()) {
225177
- resolve7();
225240
+ resolve6();
225178
225241
  } else {
225179
225242
  var previousDrain = queue.drain;
225180
225243
  queue.drain = function() {
225181
225244
  if (typeof previousDrain === "function") previousDrain();
225182
- resolve7();
225245
+ resolve6();
225183
225246
  queue.drain = previousDrain;
225184
225247
  };
225185
225248
  }
@@ -225676,9 +225739,9 @@ var require_stream3 = __commonJS({
225676
225739
  });
225677
225740
  }
225678
225741
  _getStat(filepath) {
225679
- return new Promise((resolve7, reject) => {
225742
+ return new Promise((resolve6, reject) => {
225680
225743
  this._stat(filepath, this._fsStatSettings, (error, stats) => {
225681
- return error === null ? resolve7(stats) : reject(error);
225744
+ return error === null ? resolve6(stats) : reject(error);
225682
225745
  });
225683
225746
  });
225684
225747
  }
@@ -225703,10 +225766,10 @@ var require_async5 = __commonJS({
225703
225766
  this._readerStream = new stream_1.default(this._settings);
225704
225767
  }
225705
225768
  dynamic(root, options) {
225706
- return new Promise((resolve7, reject) => {
225769
+ return new Promise((resolve6, reject) => {
225707
225770
  this._walkAsync(root, options, (error, entries) => {
225708
225771
  if (error === null) {
225709
- resolve7(entries);
225772
+ resolve6(entries);
225710
225773
  } else {
225711
225774
  reject(error);
225712
225775
  }
@@ -225716,10 +225779,10 @@ var require_async5 = __commonJS({
225716
225779
  async static(patterns, options) {
225717
225780
  const entries = [];
225718
225781
  const stream = this._readerStream.static(patterns, options);
225719
- return new Promise((resolve7, reject) => {
225782
+ return new Promise((resolve6, reject) => {
225720
225783
  stream.once("error", reject);
225721
225784
  stream.on("data", (entry) => entries.push(entry));
225722
- stream.once("end", () => resolve7(entries));
225785
+ stream.once("end", () => resolve6(entries));
225723
225786
  });
225724
225787
  }
225725
225788
  };
@@ -258625,12 +258688,12 @@ type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "js
258625
258688
  };
258626
258689
  var NodeRuntimeFileSystem = class {
258627
258690
  delete(path2) {
258628
- return new Promise((resolve7, reject) => {
258691
+ return new Promise((resolve6, reject) => {
258629
258692
  fs__namespace.rm(path2, { recursive: true }, (err) => {
258630
258693
  if (err)
258631
258694
  reject(err);
258632
258695
  else
258633
- resolve7();
258696
+ resolve6();
258634
258697
  });
258635
258698
  });
258636
258699
  }
@@ -258649,12 +258712,12 @@ type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "js
258649
258712
  }));
258650
258713
  }
258651
258714
  readFile(filePath, encoding = "utf-8") {
258652
- return new Promise((resolve7, reject) => {
258715
+ return new Promise((resolve6, reject) => {
258653
258716
  fs__namespace.readFile(filePath, encoding, (err, data) => {
258654
258717
  if (err)
258655
258718
  reject(err);
258656
258719
  else
258657
- resolve7(data);
258720
+ resolve6(data);
258658
258721
  });
258659
258722
  });
258660
258723
  }
@@ -258662,12 +258725,12 @@ type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "js
258662
258725
  return fs__namespace.readFileSync(filePath, encoding);
258663
258726
  }
258664
258727
  async writeFile(filePath, fileText) {
258665
- await new Promise((resolve7, reject) => {
258728
+ await new Promise((resolve6, reject) => {
258666
258729
  fs__namespace.writeFile(filePath, fileText, (err) => {
258667
258730
  if (err)
258668
258731
  reject(err);
258669
258732
  else
258670
- resolve7();
258733
+ resolve6();
258671
258734
  });
258672
258735
  });
258673
258736
  }
@@ -258681,12 +258744,12 @@ type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "js
258681
258744
  fs__namespace.mkdirSync(dirPath, { recursive: true });
258682
258745
  }
258683
258746
  move(srcPath, destPath) {
258684
- return new Promise((resolve7, reject) => {
258747
+ return new Promise((resolve6, reject) => {
258685
258748
  fs__namespace.rename(srcPath, destPath, (err) => {
258686
258749
  if (err)
258687
258750
  reject(err);
258688
258751
  else
258689
- resolve7();
258752
+ resolve6();
258690
258753
  });
258691
258754
  });
258692
258755
  }
@@ -258694,12 +258757,12 @@ type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "js
258694
258757
  fs__namespace.renameSync(srcPath, destPath);
258695
258758
  }
258696
258759
  copy(srcPath, destPath) {
258697
- return new Promise((resolve7, reject) => {
258760
+ return new Promise((resolve6, reject) => {
258698
258761
  fs__namespace.copyFile(srcPath, destPath, (err) => {
258699
258762
  if (err)
258700
258763
  reject(err);
258701
258764
  else
258702
- resolve7();
258765
+ resolve6();
258703
258766
  });
258704
258767
  });
258705
258768
  }
@@ -258707,15 +258770,15 @@ type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "js
258707
258770
  fs__namespace.copyFileSync(srcPath, destPath);
258708
258771
  }
258709
258772
  stat(path2) {
258710
- return new Promise((resolve7, reject) => {
258773
+ return new Promise((resolve6, reject) => {
258711
258774
  fs__namespace.stat(path2, (err, stat) => {
258712
258775
  if (err) {
258713
258776
  if (err.code === "ENOENT" || err.code === "ENOTDIR")
258714
- resolve7(void 0);
258777
+ resolve6(void 0);
258715
258778
  else
258716
258779
  reject(err);
258717
258780
  } else {
258718
- resolve7(stat);
258781
+ resolve6(stat);
258719
258782
  }
258720
258783
  });
258721
258784
  });
@@ -282394,8 +282457,8 @@ Node text: ${this.#forgottenText}`;
282394
282457
  init_cjs_shims();
282395
282458
  init_dist2();
282396
282459
  var import_picocolors21 = __toESM(require_picocolors(), 1);
282397
- var import_fs13 = require("fs");
282398
- var import_path8 = require("path");
282460
+ var import_fs12 = require("fs");
282461
+ var import_path7 = require("path");
282399
282462
  var import_child_process3 = require("child_process");
282400
282463
 
282401
282464
  // src/cli/migrate.ts
@@ -282511,7 +282574,7 @@ async function applyStatements(dbUrl, targetSchema, statements, concurrent = [])
282511
282574
  if (pg.code === import_schema3.PG_LOCK_NOT_AVAILABLE && attempt < maxLockAttempts) {
282512
282575
  const backoffMs = (0, import_schema3.ddlLockRetryBackoffMs)(attempt);
282513
282576
  log.warn("schema change blocked by a lock; backing off and retrying", { targetSchema, attempt, backoffMs });
282514
- await new Promise((resolve7) => setTimeout(resolve7, backoffMs));
282577
+ await new Promise((resolve6) => setTimeout(resolve6, backoffMs));
282515
282578
  continue;
282516
282579
  }
282517
282580
  throw err;
@@ -282588,8 +282651,8 @@ init_push();
282588
282651
  // src/cli/generate.ts
282589
282652
  init_cjs_shims();
282590
282653
  init_errors();
282591
- var import_fs7 = require("fs");
282592
- var import_path5 = require("path");
282654
+ var import_fs6 = require("fs");
282655
+ var import_path4 = require("path");
282593
282656
  var import_picocolors8 = __toESM(require_picocolors(), 1);
282594
282657
  var import_schema4 = require("@abloatai/ablo/schema");
282595
282658
  init_push();
@@ -282635,9 +282698,9 @@ async function generate(argv) {
282635
282698
  console.error(import_picocolors8.default.red(` ${err instanceof Error ? err.message : String(err)}`));
282636
282699
  process.exit(1);
282637
282700
  }
282638
- const abs = (0, import_path5.resolve)(process.cwd(), args.out);
282639
- (0, import_fs7.mkdirSync)((0, import_path5.dirname)(abs), { recursive: true });
282640
- (0, import_fs7.writeFileSync)(abs, source);
282701
+ const abs = (0, import_path4.resolve)(process.cwd(), args.out);
282702
+ (0, import_fs6.mkdirSync)((0, import_path4.dirname)(abs), { recursive: true });
282703
+ (0, import_fs6.writeFileSync)(abs, source);
282641
282704
  console.log(` ${import_picocolors8.default.green("\u2713")} Generated types \u2192 ${import_picocolors8.default.bold(args.out)}`);
282642
282705
  }
282643
282706
 
@@ -282647,8 +282710,8 @@ init_errors();
282647
282710
  init_credentialPolicy();
282648
282711
  var import_picocolors9 = __toESM(require_picocolors(), 1);
282649
282712
  init_dist2();
282650
- var import_fs8 = require("fs");
282651
- var import_path6 = require("path");
282713
+ var import_fs7 = require("fs");
282714
+ var import_path5 = require("path");
282652
282715
  var import_schema5 = require("@abloatai/ablo/schema");
282653
282716
  init_push();
282654
282717
  init_config();
@@ -282709,34 +282772,34 @@ function classifyKey(apiKey, activeMode) {
282709
282772
  };
282710
282773
  }
282711
282774
  function wireEnvLocal(apiKey, cwd = process.cwd()) {
282712
- const envPath = (0, import_path6.resolve)(cwd, ".env.local");
282775
+ const envPath = (0, import_path5.resolve)(cwd, ".env.local");
282713
282776
  const line = `ABLO_API_KEY=${apiKey}`;
282714
282777
  let action;
282715
- if (!(0, import_fs8.existsSync)(envPath)) {
282716
- (0, import_fs8.writeFileSync)(envPath, `${line}
282778
+ if (!(0, import_fs7.existsSync)(envPath)) {
282779
+ (0, import_fs7.writeFileSync)(envPath, `${line}
282717
282780
  `, { mode: 384 });
282718
282781
  action = `Created ${import_picocolors9.default.bold(".env.local")} with ${import_picocolors9.default.bold("ABLO_API_KEY")}`;
282719
282782
  } else {
282720
- const content = (0, import_fs8.readFileSync)(envPath, "utf8");
282783
+ const content = (0, import_fs7.readFileSync)(envPath, "utf8");
282721
282784
  const match = /^ABLO_API_KEY=(.*)$/m.exec(content);
282722
282785
  const existing = match?.[1] ?? "";
282723
282786
  if (!match) {
282724
- (0, import_fs8.appendFileSync)(envPath, `${content.endsWith("\n") || content.length === 0 ? "" : "\n"}${line}
282787
+ (0, import_fs7.appendFileSync)(envPath, `${content.endsWith("\n") || content.length === 0 ? "" : "\n"}${line}
282725
282788
  `);
282726
282789
  action = `Added ${import_picocolors9.default.bold("ABLO_API_KEY")} to ${import_picocolors9.default.bold(".env.local")}`;
282727
282790
  } else if (existing === apiKey) {
282728
282791
  action = `${import_picocolors9.default.bold(".env.local")} already has this key`;
282729
282792
  } else {
282730
- (0, import_fs8.writeFileSync)(envPath, content.replace(/^ABLO_API_KEY=.*$/m, line));
282793
+ (0, import_fs7.writeFileSync)(envPath, content.replace(/^ABLO_API_KEY=.*$/m, line));
282731
282794
  action = `Updated ${import_picocolors9.default.bold("ABLO_API_KEY")} in ${import_picocolors9.default.bold(".env.local")} ${import_picocolors9.default.dim(`(was ${existing.slice(0, 12)}\u2026)`)}`;
282732
282795
  }
282733
282796
  }
282734
- const gitignorePath = (0, import_path6.resolve)(cwd, ".gitignore");
282735
- const gitignore = (0, import_fs8.existsSync)(gitignorePath) ? (0, import_fs8.readFileSync)(gitignorePath, "utf8") : "";
282797
+ const gitignorePath = (0, import_path5.resolve)(cwd, ".gitignore");
282798
+ const gitignore = (0, import_fs7.existsSync)(gitignorePath) ? (0, import_fs7.readFileSync)(gitignorePath, "utf8") : "";
282736
282799
  const ignored = /^(\.env\.local|\.env\*|\.env\.\*|\.env.*)$/m.test(gitignore);
282737
282800
  let gitignoreNote = "";
282738
282801
  if (!ignored) {
282739
- (0, import_fs8.writeFileSync)(
282802
+ (0, import_fs7.writeFileSync)(
282740
282803
  gitignorePath,
282741
282804
  `${gitignore.endsWith("\n") || gitignore.length === 0 ? gitignore : `${gitignore}
282742
282805
  `}.env.local
@@ -282838,12 +282901,12 @@ async function dev(argv) {
282838
282901
  }
282839
282902
  console.log(` Your app is wired for the sandbox.`);
282840
282903
  if (!args.watch) return;
282841
- const abs = (0, import_path6.resolve)(process.cwd(), args.schemaPath);
282904
+ const abs = (0, import_path5.resolve)(process.cwd(), args.schemaPath);
282842
282905
  console.log(` ${import_picocolors9.default.dim(`watching ${args.schemaPath} \u2026 (Ctrl-C to stop)`)}
282843
282906
  `);
282844
282907
  let timer2 = null;
282845
282908
  let pushing = false;
282846
- const watcher = (0, import_fs8.watch)(abs, () => {
282909
+ const watcher = (0, import_fs7.watch)(abs, () => {
282847
282910
  if (timer2) clearTimeout(timer2);
282848
282911
  timer2 = setTimeout(() => {
282849
282912
  void rePush();
@@ -283534,7 +283597,7 @@ async function logs(argv) {
283534
283597
 
283535
283598
  // src/cli/webhooks.ts
283536
283599
  init_cjs_shims();
283537
- var import_fs9 = require("fs");
283600
+ var import_fs8 = require("fs");
283538
283601
  var import_picocolors14 = __toESM(require_picocolors(), 1);
283539
283602
  init_credentialPolicy();
283540
283603
  init_config();
@@ -283600,11 +283663,11 @@ async function api(apiKey, method, path, body) {
283600
283663
  return await res.json();
283601
283664
  }
283602
283665
  function writeSecretToEnv(secret) {
283603
- const file = (0, import_fs9.existsSync)(".env.local") ? ".env.local" : (0, import_fs9.existsSync)(".env") ? ".env" : ".env.local";
283666
+ const file = (0, import_fs8.existsSync)(".env.local") ? ".env.local" : (0, import_fs8.existsSync)(".env") ? ".env" : ".env.local";
283604
283667
  const line = `${ENV_KEY}=${secret}`;
283605
283668
  let next;
283606
- if ((0, import_fs9.existsSync)(file)) {
283607
- const existing = (0, import_fs9.readFileSync)(file, "utf-8");
283669
+ if ((0, import_fs8.existsSync)(file)) {
283670
+ const existing = (0, import_fs8.readFileSync)(file, "utf-8");
283608
283671
  next = new RegExp(`^${ENV_KEY}=.*$`, "m").test(existing) ? existing.replace(new RegExp(`^${ENV_KEY}=.*$`, "m"), line) : `${existing.replace(/\n*$/, "")}
283609
283672
  ${line}
283610
283673
  `;
@@ -283612,7 +283675,7 @@ ${line}
283612
283675
  next = `${line}
283613
283676
  `;
283614
283677
  }
283615
- (0, import_fs9.writeFileSync)(file, next);
283678
+ (0, import_fs8.writeFileSync)(file, next);
283616
283679
  return file;
283617
283680
  }
283618
283681
  function printEndpoint(e2) {
@@ -284028,7 +284091,7 @@ init_cjs_shims();
284028
284091
  init_errors();
284029
284092
  var import_picocolors17 = __toESM(require_picocolors(), 1);
284030
284093
  init_src();
284031
- var import_fs10 = require("fs");
284094
+ var import_fs9 = require("fs");
284032
284095
  init_theme();
284033
284096
  var DEFAULT_OUT2 = "ablo/schema.ts";
284034
284097
  var DEFAULT_IMPORT = "@abloatai/ablo/schema";
@@ -284148,7 +284211,7 @@ async function pull(argv) {
284148
284211
  console.error(import_picocolors17.default.red(` No database.`) + import_picocolors17.default.dim(` Set ${import_picocolors17.default.bold("DATABASE_URL")} to the Postgres to pull from.`));
284149
284212
  process.exit(1);
284150
284213
  }
284151
- if ((0, import_fs10.existsSync)(args.out) && !args.force) {
284214
+ if ((0, import_fs9.existsSync)(args.out) && !args.force) {
284152
284215
  console.error(
284153
284216
  import_picocolors17.default.red(` ${args.out} already exists.`) + import_picocolors17.default.dim(` Re-run with ${import_picocolors17.default.bold("--force")} to overwrite.`)
284154
284217
  );
@@ -284170,7 +284233,7 @@ async function pull(argv) {
284170
284233
  );
284171
284234
  process.exit(1);
284172
284235
  }
284173
- (0, import_fs10.writeFileSync)(args.out, result.source);
284236
+ (0, import_fs9.writeFileSync)(args.out, result.source);
284174
284237
  console.log(` ${import_picocolors17.default.green("\u2713")} wrote ${import_picocolors17.default.bold(args.out)} ${import_picocolors17.default.dim(`(${result.models.length} models)`)}`);
284175
284238
  console.log(` ${import_picocolors17.default.dim(`models: ${result.models.join(", ")}`)}`);
284176
284239
  if (result.skipped > 0) {
@@ -284187,7 +284250,7 @@ async function pull(argv) {
284187
284250
  init_cjs_shims();
284188
284251
  init_errors();
284189
284252
  var import_picocolors18 = __toESM(require_picocolors(), 1);
284190
- var import_fs11 = require("fs");
284253
+ var import_fs10 = require("fs");
284191
284254
  init_theme();
284192
284255
 
284193
284256
  // src/cli/schemaIr.ts
@@ -284474,13 +284537,13 @@ async function prismaPull(argv) {
284474
284537
  console.error(import_picocolors18.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284475
284538
  process.exit(1);
284476
284539
  }
284477
- if (!(0, import_fs11.existsSync)(args.schema)) {
284540
+ if (!(0, import_fs10.existsSync)(args.schema)) {
284478
284541
  console.error(
284479
284542
  import_picocolors18.default.red(` No Prisma schema at ${import_picocolors18.default.bold(args.schema)}.`) + import_picocolors18.default.dim(` Pass a path: ${import_picocolors18.default.bold("ablo pull prisma <path>")}.`)
284480
284543
  );
284481
284544
  process.exit(1);
284482
284545
  }
284483
- if ((0, import_fs11.existsSync)(args.out) && !args.force) {
284546
+ if ((0, import_fs10.existsSync)(args.out) && !args.force) {
284484
284547
  console.error(
284485
284548
  import_picocolors18.default.red(` ${args.out} already exists.`) + import_picocolors18.default.dim(` Re-run with ${import_picocolors18.default.bold("--force")} to overwrite.`)
284486
284549
  );
@@ -284491,7 +284554,7 @@ async function prismaPull(argv) {
284491
284554
  `);
284492
284555
  let result;
284493
284556
  try {
284494
- const src = (0, import_fs11.readFileSync)(args.schema, "utf8");
284557
+ const src = (0, import_fs10.readFileSync)(args.schema, "utf8");
284495
284558
  result = buildSchemaSourceFromPrisma({ src, importPath: args.importPath });
284496
284559
  } catch (err) {
284497
284560
  console.error(import_picocolors18.default.red(` Couldn't parse the schema: ${err instanceof Error ? err.message : String(err)}`));
@@ -284503,7 +284566,7 @@ async function prismaPull(argv) {
284503
284566
  );
284504
284567
  process.exit(1);
284505
284568
  }
284506
- (0, import_fs11.writeFileSync)(args.out, result.source);
284569
+ (0, import_fs10.writeFileSync)(args.out, result.source);
284507
284570
  console.log(` ${import_picocolors18.default.green("\u2713")} wrote ${import_picocolors18.default.bold(args.out)} ${import_picocolors18.default.dim(`(${result.models.length} models)`)}`);
284508
284571
  console.log(` ${import_picocolors18.default.dim(`models: ${result.models.join(", ")}`)}`);
284509
284572
  if (result.skipped.length > 0) {
@@ -284521,8 +284584,8 @@ async function prismaPull(argv) {
284521
284584
  init_cjs_shims();
284522
284585
  var import_picocolors19 = __toESM(require_picocolors(), 1);
284523
284586
  init_errors();
284524
- var import_fs12 = require("fs");
284525
- var import_path7 = require("path");
284587
+ var import_fs11 = require("fs");
284588
+ var import_path6 = require("path");
284526
284589
  init_theme();
284527
284590
  var DEFAULT_OUT4 = "ablo/schema.ts";
284528
284591
  var DEFAULT_IMPORT3 = "@abloatai/ablo/schema";
@@ -284650,7 +284713,7 @@ function parseDrizzlePullArgs(argv) {
284650
284713
  async function loadModule(path) {
284651
284714
  const { createJiti } = await import("jiti");
284652
284715
  const jiti = createJiti(process.cwd());
284653
- const mod = await jiti.import((0, import_path7.resolve)(path));
284716
+ const mod = await jiti.import((0, import_path6.resolve)(path));
284654
284717
  return mod;
284655
284718
  }
284656
284719
  async function drizzlePull(argv) {
@@ -284667,11 +284730,11 @@ async function drizzlePull(argv) {
284667
284730
  );
284668
284731
  process.exit(1);
284669
284732
  }
284670
- if (!(0, import_fs12.existsSync)(args.schema)) {
284733
+ if (!(0, import_fs11.existsSync)(args.schema)) {
284671
284734
  console.error(import_picocolors19.default.red(` No file at ${import_picocolors19.default.bold(args.schema)}.`));
284672
284735
  process.exit(1);
284673
284736
  }
284674
- if ((0, import_fs12.existsSync)(args.out) && !args.force) {
284737
+ if ((0, import_fs11.existsSync)(args.out) && !args.force) {
284675
284738
  console.error(
284676
284739
  import_picocolors19.default.red(` ${args.out} already exists.`) + import_picocolors19.default.dim(` Re-run with ${import_picocolors19.default.bold("--force")} to overwrite.`)
284677
284740
  );
@@ -284696,7 +284759,7 @@ async function drizzlePull(argv) {
284696
284759
  );
284697
284760
  process.exit(1);
284698
284761
  }
284699
- (0, import_fs12.writeFileSync)(args.out, result.source);
284762
+ (0, import_fs11.writeFileSync)(args.out, result.source);
284700
284763
  console.log(` ${import_picocolors19.default.green("\u2713")} wrote ${import_picocolors19.default.bold(args.out)} ${import_picocolors19.default.dim(`(${result.models.length} models)`)}`);
284701
284764
  console.log(` ${import_picocolors19.default.dim(`models: ${result.models.join(", ")}`)}`);
284702
284765
  if (result.skipped.length > 0) {
@@ -284971,7 +285034,7 @@ async function ensureInitProject(opts) {
284971
285034
  const slug = opts.project ?? projectSlugFromPackageName(
284972
285035
  (() => {
284973
285036
  try {
284974
- return JSON.parse((0, import_fs13.readFileSync)("package.json", "utf-8")).name;
285037
+ return JSON.parse((0, import_fs12.readFileSync)("package.json", "utf-8")).name;
284975
285038
  } catch {
284976
285039
  return void 0;
284977
285040
  }
@@ -284988,7 +285051,7 @@ async function ensureInitProject(opts) {
284988
285051
  function detectOrm(override) {
284989
285052
  if (override === "prisma" || override === "drizzle" || override === "none") return override;
284990
285053
  try {
284991
- const pkg = JSON.parse((0, import_fs13.readFileSync)("package.json", "utf-8"));
285054
+ const pkg = JSON.parse((0, import_fs12.readFileSync)("package.json", "utf-8"));
284992
285055
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
284993
285056
  if (deps["@prisma/client"] || deps.prisma) return "prisma";
284994
285057
  if (deps["drizzle-orm"]) return "drizzle";
@@ -284997,8 +285060,8 @@ function detectOrm(override) {
284997
285060
  return "none";
284998
285061
  }
284999
285062
  function detectNextLayout() {
285000
- const useSrc = (0, import_fs13.existsSync)((0, import_path8.join)("src", "app")) || !(0, import_fs13.existsSync)("app") && (0, import_fs13.existsSync)("src");
285001
- return useSrc ? { appBase: (0, import_path8.join)("src", "app"), aliasBase: "src" } : { appBase: "app", aliasBase: "." };
285063
+ const useSrc = (0, import_fs12.existsSync)((0, import_path7.join)("src", "app")) || !(0, import_fs12.existsSync)("app") && (0, import_fs12.existsSync)("src");
285064
+ return useSrc ? { appBase: (0, import_path7.join)("src", "app"), aliasBase: "src" } : { appBase: "app", aliasBase: "." };
285002
285065
  }
285003
285066
  async function chooseOption(name, flagValue, fallback, allowed, interactive, prompt) {
285004
285067
  if (flagValue !== void 0) {
@@ -285024,7 +285087,7 @@ async function init(args = []) {
285024
285087
  const opts = parseInitArgs(args);
285025
285088
  const interactive = Boolean(process.stdin.isTTY) && !opts.yes && !process.env.CI;
285026
285089
  Ie(`${brand("ablo")} ${import_picocolors21.default.dim("sync engine")}`);
285027
- if (!(0, import_fs13.existsSync)("package.json")) {
285090
+ if (!(0, import_fs12.existsSync)("package.json")) {
285028
285091
  xe("No package.json found. Run this from your project root.");
285029
285092
  process.exit(1);
285030
285093
  }
@@ -285093,8 +285156,8 @@ async function init(args = []) {
285093
285156
  );
285094
285157
  }
285095
285158
  const layout = framework === "nextjs" ? detectNextLayout() : { appBase: "app", aliasBase: "." };
285096
- const abloDir = (0, import_path8.join)(layout.aliasBase, "ablo");
285097
- (0, import_fs13.mkdirSync)(abloDir, { recursive: true });
285159
+ const abloDir = (0, import_path7.join)(layout.aliasBase, "ablo");
285160
+ (0, import_fs12.mkdirSync)(abloDir, { recursive: true });
285098
285161
  const created = [];
285099
285162
  let schemaSource = generateSchema();
285100
285163
  let schemaNote = "";
@@ -285120,28 +285183,28 @@ async function init(args = []) {
285120
285183
  }
285121
285184
  }
285122
285185
  }
285123
- (0, import_fs13.writeFileSync)((0, import_path8.join)(abloDir, "schema.ts"), schemaSource);
285186
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "schema.ts"), schemaSource);
285124
285187
  created.push(`${abloDir}/schema.ts${schemaNote}`);
285125
- (0, import_fs13.writeFileSync)((0, import_path8.join)(abloDir, "index.ts"), generateSyncConfig(auth));
285188
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "index.ts"), generateSyncConfig(auth));
285126
285189
  created.push(`${abloDir}/index.ts`);
285127
- (0, import_fs13.writeFileSync)((0, import_path8.join)(abloDir, "register.ts"), generateRegister());
285190
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "register.ts"), generateRegister());
285128
285191
  created.push(`${abloDir}/register.ts`);
285129
285192
  const orm = detectOrm(opts.orm);
285130
285193
  if (storage === "endpoint") {
285131
- (0, import_fs13.writeFileSync)((0, import_path8.join)(abloDir, "data-source.ts"), generateDataSource(orm));
285194
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "data-source.ts"), generateDataSource(orm));
285132
285195
  created.push(`${abloDir}/data-source.ts${orm === "drizzle" ? " (Drizzle)" : " (Prisma)"}`);
285133
285196
  }
285134
285197
  const envFile = framework === "nextjs" ? ".env.local" : ".env";
285135
285198
  const resolvedKey = process.env.ABLO_API_KEY ? void 0 : resolveApiKey("sandbox");
285136
285199
  const wireRealKey = envFile === ".env.local" && Boolean(resolvedKey);
285137
285200
  const envBody = generateEnv(storage, { includeApiKey: !wireRealKey });
285138
- if (!(0, import_fs13.existsSync)(envFile)) {
285139
- (0, import_fs13.writeFileSync)(envFile, envBody);
285201
+ if (!(0, import_fs12.existsSync)(envFile)) {
285202
+ (0, import_fs12.writeFileSync)(envFile, envBody);
285140
285203
  created.push(envFile);
285141
285204
  } else {
285142
- const existing = (0, import_fs13.readFileSync)(envFile, "utf-8");
285205
+ const existing = (0, import_fs12.readFileSync)(envFile, "utf-8");
285143
285206
  if (!existing.includes("ABLO_")) {
285144
- (0, import_fs13.writeFileSync)(envFile, existing + "\n" + envBody);
285207
+ (0, import_fs12.writeFileSync)(envFile, existing + "\n" + envBody);
285145
285208
  created.push(`${envFile} ${import_picocolors21.default.dim("(appended)")}`);
285146
285209
  } else {
285147
285210
  created.push(`${envFile} ${import_picocolors21.default.dim("(already configured)")}`);
@@ -285152,26 +285215,26 @@ async function init(args = []) {
285152
285215
  created.push(`.env.local ${import_picocolors21.default.dim("(ABLO_API_KEY set from your login)")}`);
285153
285216
  }
285154
285217
  if (agent) {
285155
- (0, import_fs13.writeFileSync)((0, import_path8.join)(abloDir, "agent.ts"), generateAgent());
285218
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "agent.ts"), generateAgent());
285156
285219
  created.push(`${abloDir}/agent.ts`);
285157
285220
  }
285158
285221
  if (framework === "nextjs") {
285159
285222
  if (storage === "endpoint") {
285160
- const webhookDir = (0, import_path8.join)(layout.appBase, "api", "ablo", "webhooks");
285161
- (0, import_fs13.mkdirSync)(webhookDir, { recursive: true });
285162
- (0, import_fs13.writeFileSync)((0, import_path8.join)(webhookDir, "route.ts"), generateWebhookRoute(orm));
285223
+ const webhookDir = (0, import_path7.join)(layout.appBase, "api", "ablo", "webhooks");
285224
+ (0, import_fs12.mkdirSync)(webhookDir, { recursive: true });
285225
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(webhookDir, "route.ts"), generateWebhookRoute(orm));
285163
285226
  created.push(`${webhookDir}/route.ts${orm === "prisma" ? " (Prisma mirror)" : " (add your database write)"}`);
285164
285227
  }
285165
- const providersPath = (0, import_path8.join)(layout.appBase, "providers.tsx");
285166
- (0, import_fs13.writeFileSync)(providersPath, generateProviders());
285167
- created.push(`${providersPath} ${import_picocolors21.default.dim(`(wrap ${(0, import_path8.join)(layout.appBase, "layout.tsx")} in <Providers>)`)}`);
285168
- const sessionDir = (0, import_path8.join)(layout.appBase, "api", "ablo-session");
285169
- (0, import_fs13.mkdirSync)(sessionDir, { recursive: true });
285170
- (0, import_fs13.writeFileSync)((0, import_path8.join)(sessionDir, "route.ts"), generateSessionRoute());
285171
- created.push(`${(0, import_path8.join)(sessionDir, "route.ts")} ${import_picocolors21.default.dim("(wire your auth)")}`);
285228
+ const providersPath = (0, import_path7.join)(layout.appBase, "providers.tsx");
285229
+ (0, import_fs12.writeFileSync)(providersPath, generateProviders());
285230
+ created.push(`${providersPath} ${import_picocolors21.default.dim(`(wrap ${(0, import_path7.join)(layout.appBase, "layout.tsx")} in <Providers>)`)}`);
285231
+ const sessionDir = (0, import_path7.join)(layout.appBase, "api", "ablo-session");
285232
+ (0, import_fs12.mkdirSync)(sessionDir, { recursive: true });
285233
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(sessionDir, "route.ts"), generateSessionRoute());
285234
+ created.push(`${(0, import_path7.join)(sessionDir, "route.ts")} ${import_picocolors21.default.dim("(wire your auth)")}`);
285172
285235
  }
285173
285236
  if (framework !== "vanilla") {
285174
- (0, import_fs13.writeFileSync)((0, import_path8.join)(abloDir, "TaskList.tsx"), generateComponent());
285237
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "TaskList.tsx"), generateComponent());
285175
285238
  created.push(`${abloDir}/TaskList.tsx`);
285176
285239
  }
285177
285240
  Me(created.map((f) => `${import_picocolors21.default.green("\u2713")} ${f}`).join("\n"), "Created");
@@ -285199,7 +285262,7 @@ async function init(args = []) {
285199
285262
  `Provision your DB: ${import_picocolors21.default.bold("npx ablo migrate")} (creates your Ablo-model tables + the adapter tables; keep your own migrations for everything else), then mount ${import_picocolors21.default.bold(`${abloDir}/data-source.ts`)} at ${import_picocolors21.default.bold("/api/ablo/source")}`
285200
285263
  ],
285201
285264
  ...framework === "nextjs" ? [
285202
- `Wrap ${import_picocolors21.default.bold((0, import_path8.join)(layout.appBase, "layout.tsx"))} in ${import_picocolors21.default.bold("<Providers>")} (${(0, import_path8.join)(layout.appBase, "providers.tsx")}) and add your auth to ${import_picocolors21.default.bold((0, import_path8.join)(layout.appBase, "api", "ablo-session", "route.ts"))}`
285265
+ `Wrap ${import_picocolors21.default.bold((0, import_path7.join)(layout.appBase, "layout.tsx"))} in ${import_picocolors21.default.bold("<Providers>")} (${(0, import_path7.join)(layout.appBase, "providers.tsx")}) and add your auth to ${import_picocolors21.default.bold((0, import_path7.join)(layout.appBase, "api", "ablo-session", "route.ts"))}`
285203
285266
  ] : [],
285204
285267
  `Run ${import_picocolors21.default.bold(`${pm} run dev`)} and open two browser tabs \u2014 changes sync in real-time`,
285205
285268
  ...agent ? [
@@ -285607,9 +285670,9 @@ async function getCurrentUser(): Promise<{ id: string } | null> {
285607
285670
  `;
285608
285671
  }
285609
285672
  function detectPackageManager() {
285610
- if ((0, import_fs13.existsSync)("pnpm-lock.yaml")) return "pnpm";
285611
- if ((0, import_fs13.existsSync)("yarn.lock")) return "yarn";
285612
- if ((0, import_fs13.existsSync)("bun.lockb")) return "bun";
285673
+ if ((0, import_fs12.existsSync)("pnpm-lock.yaml")) return "pnpm";
285674
+ if ((0, import_fs12.existsSync)("yarn.lock")) return "yarn";
285675
+ if ((0, import_fs12.existsSync)("bun.lockb")) return "bun";
285613
285676
  return "npm";
285614
285677
  }
285615
285678
  main().catch((err) => {