@carllee1983/dbcli 1.20.1 → 1.20.2

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.mjs CHANGED
@@ -7151,15 +7151,15 @@ var init_config_v2 = __esm(() => {
7151
7151
  });
7152
7152
 
7153
7153
  // node_modules/lru-cache/dist/esm/node/index.min.js
7154
- import { tracingChannel as I, channel as G } from "diagnostics_channel";
7155
- var S, W, C, D = () => S.hasSubscribers || W.hasSubscribers, U, L, P = (u, e, t2, i) => {
7154
+ import { tracingChannel as j, channel as G } from "diagnostics_channel";
7155
+ var w, W, C, D = () => w.hasSubscribers || W.hasSubscribers, I, L, P = (u, e, t2, i) => {
7156
7156
  typeof L.emitWarning == "function" ? L.emitWarning(u, e, t2, i) : console.error(`[${t2}] ${e}: ${u}`);
7157
- }, H = (u) => !U.has(u), X, F = (u) => !!u && u === Math.floor(u) && u > 0 && isFinite(u), j = (u) => F(u) ? u <= Math.pow(2, 8) ? Uint8Array : u <= Math.pow(2, 16) ? Uint16Array : u <= Math.pow(2, 32) ? Uint32Array : u <= Number.MAX_SAFE_INTEGER ? O : null : null, O, R = class u {
7157
+ }, H = (u) => !I.has(u), T = (u) => !!u && u === Math.floor(u) && u > 0 && isFinite(u), U = (u) => T(u) ? u <= Math.pow(2, 8) ? Uint8Array : u <= Math.pow(2, 16) ? Uint16Array : u <= Math.pow(2, 32) ? Uint32Array : u <= Number.MAX_SAFE_INTEGER ? O : null : null, O, R = class u {
7158
7158
  heap;
7159
7159
  length;
7160
7160
  static #o = false;
7161
7161
  static create(e) {
7162
- let t2 = j(e);
7162
+ let t2 = U(e);
7163
7163
  if (!t2)
7164
7164
  return [];
7165
7165
  u.#o = true;
@@ -7179,12 +7179,11 @@ var S, W, C, D = () => S.hasSubscribers || W.hasSubscribers, U, L, P = (u, e, t2
7179
7179
  }
7180
7180
  }, M;
7181
7181
  var init_index_min = __esm(() => {
7182
- S = G("lru-cache:metrics");
7183
- W = I("lru-cache");
7182
+ w = G("lru-cache:metrics");
7183
+ W = j("lru-cache");
7184
7184
  C = typeof performance == "object" && performance && typeof performance.now == "function" ? performance : Date;
7185
- U = new Set;
7185
+ I = new Set;
7186
7186
  L = typeof process == "object" && process ? process : {};
7187
- X = Symbol("type");
7188
7187
  O = class extends Array {
7189
7188
  constructor(e) {
7190
7189
  super(e), this.fill(0);
@@ -7192,12 +7191,12 @@ var init_index_min = __esm(() => {
7192
7191
  };
7193
7192
  M = class u2 {
7194
7193
  #o;
7195
- #u;
7194
+ #c;
7196
7195
  #w;
7197
- #x;
7196
+ #O;
7198
7197
  #S;
7199
7198
  #M;
7200
- #U;
7199
+ #I;
7201
7200
  #m;
7202
7201
  get perf() {
7203
7202
  return this.#m;
@@ -7222,9 +7221,9 @@ var init_index_min = __esm(() => {
7222
7221
  #s;
7223
7222
  #i;
7224
7223
  #t;
7225
- #a;
7226
- #c;
7227
7224
  #l;
7225
+ #u;
7226
+ #a;
7228
7227
  #h;
7229
7228
  #y;
7230
7229
  #r;
@@ -7233,12 +7232,12 @@ var init_index_min = __esm(() => {
7233
7232
  #d;
7234
7233
  #g;
7235
7234
  #T;
7236
- #W;
7235
+ #U;
7237
7236
  #f;
7238
- #j;
7237
+ #x;
7239
7238
  static unsafeExposeInternals(e) {
7240
- return { starts: e.#F, ttls: e.#d, autopurgeTimers: e.#g, sizes: e.#_, keyMap: e.#s, keyList: e.#i, valList: e.#t, next: e.#a, prev: e.#c, get head() {
7241
- return e.#l;
7239
+ return { starts: e.#F, ttls: e.#d, autopurgeTimers: e.#g, sizes: e.#_, keyMap: e.#s, keyList: e.#i, valList: e.#t, next: e.#l, prev: e.#u, get head() {
7240
+ return e.#a;
7242
7241
  }, get tail() {
7243
7242
  return e.#h;
7244
7243
  }, free: e.#y, isBackgroundFetch: (t2) => e.#e(t2), backgroundFetch: (t2, i, s, n) => e.#P(t2, i, s, n), moveToTail: (t2) => e.#L(t2), indexes: (t2) => e.#A(t2), rindexes: (t2) => e.#z(t2), isStale: (t2) => e.#p(t2) };
@@ -7247,7 +7246,7 @@ var init_index_min = __esm(() => {
7247
7246
  return this.#o;
7248
7247
  }
7249
7248
  get maxSize() {
7250
- return this.#u;
7249
+ return this.#c;
7251
7250
  }
7252
7251
  get calculatedSize() {
7253
7252
  return this.#b;
@@ -7259,53 +7258,53 @@ var init_index_min = __esm(() => {
7259
7258
  return this.#M;
7260
7259
  }
7261
7260
  get memoMethod() {
7262
- return this.#U;
7261
+ return this.#I;
7263
7262
  }
7264
7263
  get dispose() {
7265
7264
  return this.#w;
7266
7265
  }
7267
7266
  get onInsert() {
7268
- return this.#x;
7267
+ return this.#O;
7269
7268
  }
7270
7269
  get disposeAfter() {
7271
7270
  return this.#S;
7272
7271
  }
7273
7272
  constructor(e) {
7274
- let { max: t2 = 0, ttl: i, ttlResolution: s = 1, ttlAutopurge: n, updateAgeOnGet: o, updateAgeOnHas: r, allowStale: h, dispose: l, onInsert: c, disposeAfter: f, noDisposeOnSet: g, noUpdateTTL: p, maxSize: T = 0, maxEntrySize: w = 0, sizeCalculation: y, fetchMethod: a, memoMethod: m, noDeleteOnFetchRejection: _, noDeleteOnStaleGet: b, allowStaleOnFetchRejection: d, allowStaleOnFetchAbort: A, ignoreFetchAbort: z, perf: x } = e;
7273
+ let { max: t2 = 0, ttl: i, ttlResolution: s = 1, ttlAutopurge: n, updateAgeOnGet: r, updateAgeOnHas: h, allowStale: a, dispose: o, onInsert: p, disposeAfter: m, noDisposeOnSet: _, noUpdateTTL: c, maxSize: d = 0, maxEntrySize: f = 0, sizeCalculation: y, fetchMethod: l, memoMethod: S, noDeleteOnFetchRejection: F, noDeleteOnStaleGet: b, allowStaleOnFetchRejection: g, allowStaleOnFetchAbort: A, ignoreFetchAbort: z, perf: x } = e;
7275
7274
  if (x !== undefined && typeof x?.now != "function")
7276
7275
  throw new TypeError("perf option must have a now() method if specified");
7277
- if (this.#m = x ?? C, t2 !== 0 && !F(t2))
7276
+ if (this.#m = x ?? C, t2 !== 0 && !T(t2))
7278
7277
  throw new TypeError("max option must be a nonnegative integer");
7279
- let v = t2 ? j(t2) : Array;
7280
- if (!v)
7278
+ let E = t2 ? U(t2) : Array;
7279
+ if (!E)
7281
7280
  throw new Error("invalid max value: " + t2);
7282
- if (this.#o = t2, this.#u = T, this.maxEntrySize = w || this.#u, this.sizeCalculation = y, this.sizeCalculation) {
7283
- if (!this.#u && !this.maxEntrySize)
7281
+ if (this.#o = t2, this.#c = d, this.maxEntrySize = f || this.#c, this.sizeCalculation = y, this.sizeCalculation) {
7282
+ if (!this.#c && !this.maxEntrySize)
7284
7283
  throw new TypeError("cannot set sizeCalculation without setting maxSize or maxEntrySize");
7285
7284
  if (typeof this.sizeCalculation != "function")
7286
7285
  throw new TypeError("sizeCalculation set to non-function");
7287
7286
  }
7288
- if (m !== undefined && typeof m != "function")
7287
+ if (S !== undefined && typeof S != "function")
7289
7288
  throw new TypeError("memoMethod must be a function if defined");
7290
- if (this.#U = m, a !== undefined && typeof a != "function")
7289
+ if (this.#I = S, l !== undefined && typeof l != "function")
7291
7290
  throw new TypeError("fetchMethod must be a function if specified");
7292
- if (this.#M = a, this.#W = !!a, this.#s = new Map, this.#i = Array.from({ length: t2 }).fill(undefined), this.#t = Array.from({ length: t2 }).fill(undefined), this.#a = new v(t2), this.#c = new v(t2), this.#l = 0, this.#h = 0, this.#y = R.create(t2), this.#n = 0, this.#b = 0, typeof l == "function" && (this.#w = l), typeof c == "function" && (this.#x = c), typeof f == "function" ? (this.#S = f, this.#r = []) : (this.#S = undefined, this.#r = undefined), this.#T = !!this.#w, this.#j = !!this.#x, this.#f = !!this.#S, this.noDisposeOnSet = !!g, this.noUpdateTTL = !!p, this.noDeleteOnFetchRejection = !!_, this.allowStaleOnFetchRejection = !!d, this.allowStaleOnFetchAbort = !!A, this.ignoreFetchAbort = !!z, this.maxEntrySize !== 0) {
7293
- if (this.#u !== 0 && !F(this.#u))
7291
+ if (this.#M = l, this.#U = !!l, this.#s = new Map, this.#i = Array.from({ length: t2 }).fill(undefined), this.#t = Array.from({ length: t2 }).fill(undefined), this.#l = new E(t2), this.#u = new E(t2), this.#a = 0, this.#h = 0, this.#y = R.create(t2), this.#n = 0, this.#b = 0, typeof o == "function" && (this.#w = o), typeof p == "function" && (this.#O = p), typeof m == "function" ? (this.#S = m, this.#r = []) : (this.#S = undefined, this.#r = undefined), this.#T = !!this.#w, this.#x = !!this.#O, this.#f = !!this.#S, this.noDisposeOnSet = !!_, this.noUpdateTTL = !!c, this.noDeleteOnFetchRejection = !!F, this.allowStaleOnFetchRejection = !!g, this.allowStaleOnFetchAbort = !!A, this.ignoreFetchAbort = !!z, this.maxEntrySize !== 0) {
7292
+ if (this.#c !== 0 && !T(this.#c))
7294
7293
  throw new TypeError("maxSize must be a positive integer if specified");
7295
- if (!F(this.maxEntrySize))
7294
+ if (!T(this.maxEntrySize))
7296
7295
  throw new TypeError("maxEntrySize must be a positive integer if specified");
7297
7296
  this.#X();
7298
7297
  }
7299
- if (this.allowStale = !!h, this.noDeleteOnStaleGet = !!b, this.updateAgeOnGet = !!o, this.updateAgeOnHas = !!r, this.ttlResolution = F(s) || s === 0 ? s : 1, this.ttlAutopurge = !!n, this.ttl = i || 0, this.ttl) {
7300
- if (!F(this.ttl))
7298
+ if (this.allowStale = !!a, this.noDeleteOnStaleGet = !!b, this.updateAgeOnGet = !!r, this.updateAgeOnHas = !!h, this.ttlResolution = T(s) || s === 0 ? s : 1, this.ttlAutopurge = !!n, this.ttl = i || 0, this.ttl) {
7299
+ if (!T(this.ttl))
7301
7300
  throw new TypeError("ttl must be a positive integer if specified");
7302
7301
  this.#H();
7303
7302
  }
7304
- if (this.#o === 0 && this.ttl === 0 && this.#u === 0)
7303
+ if (this.#o === 0 && this.ttl === 0 && this.#c === 0)
7305
7304
  throw new TypeError("At least one of max, maxSize, or ttl is required");
7306
- if (!this.ttlAutopurge && !this.#o && !this.#u) {
7307
- let E = "LRU_CACHE_UNBOUNDED";
7308
- H(E) && (U.add(E), P("TTL caching without ttlAutopurge, max, or maxSize can result in unbounded memory consumption.", "UnboundedCacheWarning", E, u2));
7305
+ if (!this.ttlAutopurge && !this.#o && !this.#c) {
7306
+ let v = "LRU_CACHE_UNBOUNDED";
7307
+ H(v) && (I.add(v), P("TTL caching without ttlAutopurge, max, or maxSize can result in unbounded memory consumption.", "UnboundedCacheWarning", v, u2));
7309
7308
  }
7310
7309
  }
7311
7310
  getRemainingTTL(e) {
@@ -7315,75 +7314,75 @@ var init_index_min = __esm(() => {
7315
7314
  let e = new O(this.#o), t2 = new O(this.#o);
7316
7315
  this.#d = e, this.#F = t2;
7317
7316
  let i = this.ttlAutopurge ? Array.from({ length: this.#o }) : undefined;
7318
- this.#g = i, this.#N = (r, h, l = this.#m.now()) => {
7319
- t2[r] = h !== 0 ? l : 0, e[r] = h, s(r, h);
7320
- }, this.#D = (r) => {
7321
- t2[r] = e[r] !== 0 ? this.#m.now() : 0, s(r, e[r]);
7317
+ this.#g = i, this.#N = (h, a, o = this.#m.now()) => {
7318
+ t2[h] = a !== 0 ? o : 0, e[h] = a, s(h, a);
7319
+ }, this.#D = (h) => {
7320
+ t2[h] = e[h] !== 0 ? this.#m.now() : 0, s(h, e[h]);
7322
7321
  };
7323
- let s = this.ttlAutopurge ? (r, h) => {
7324
- if (i?.[r] && (clearTimeout(i[r]), i[r] = undefined), h && h !== 0 && i) {
7325
- let l = setTimeout(() => {
7326
- this.#p(r) && this.#v(this.#i[r], "expire");
7327
- }, h + 1);
7328
- l.unref && l.unref(), i[r] = l;
7322
+ let s = this.ttlAutopurge ? (h, a) => {
7323
+ if (i?.[h] && (clearTimeout(i[h]), i[h] = undefined), a && a !== 0 && i) {
7324
+ let o = setTimeout(() => {
7325
+ this.#p(h) && this.#E(this.#i[h], "expire");
7326
+ }, a + 1);
7327
+ o.unref && o.unref(), i[h] = o;
7329
7328
  }
7330
7329
  } : () => {};
7331
- this.#E = (r, h) => {
7332
- if (e[h]) {
7333
- let l = e[h], c = t2[h];
7334
- if (!l || !c)
7330
+ this.#v = (h, a) => {
7331
+ if (e[a]) {
7332
+ let o = e[a], p = t2[a];
7333
+ if (!o || !p)
7335
7334
  return;
7336
- r.ttl = l, r.start = c, r.now = n || o();
7337
- let f = r.now - c;
7338
- r.remainingTTL = l - f;
7335
+ h.ttl = o, h.start = p, h.now = n || r();
7336
+ let m = h.now - p;
7337
+ h.remainingTTL = o - m;
7339
7338
  }
7340
7339
  };
7341
- let n = 0, o = () => {
7342
- let r = this.#m.now();
7340
+ let n = 0, r = () => {
7341
+ let h = this.#m.now();
7343
7342
  if (this.ttlResolution > 0) {
7344
- n = r;
7345
- let h = setTimeout(() => n = 0, this.ttlResolution);
7346
- h.unref && h.unref();
7343
+ n = h;
7344
+ let a = setTimeout(() => n = 0, this.ttlResolution);
7345
+ a.unref && a.unref();
7347
7346
  }
7348
- return r;
7347
+ return h;
7349
7348
  };
7350
- this.getRemainingTTL = (r) => {
7351
- let h = this.#s.get(r);
7352
- if (h === undefined)
7349
+ this.getRemainingTTL = (h) => {
7350
+ let a = this.#s.get(h);
7351
+ if (a === undefined)
7353
7352
  return 0;
7354
- let l = e[h], c = t2[h];
7355
- if (!l || !c)
7353
+ let o = e[a], p = t2[a];
7354
+ if (!o || !p)
7356
7355
  return 1 / 0;
7357
- let f = (n || o()) - c;
7358
- return l - f;
7359
- }, this.#p = (r) => {
7360
- let h = t2[r], l = e[r];
7361
- return !!l && !!h && (n || o()) - h > l;
7356
+ let m = (n || r()) - p;
7357
+ return o - m;
7358
+ }, this.#p = (h) => {
7359
+ let a = t2[h], o = e[h];
7360
+ return !!o && !!a && (n || r()) - a > o;
7362
7361
  };
7363
7362
  }
7364
7363
  #D = () => {};
7365
- #E = () => {};
7364
+ #v = () => {};
7366
7365
  #N = () => {};
7367
7366
  #p = () => false;
7368
7367
  #X() {
7369
7368
  let e = new O(this.#o);
7370
7369
  this.#b = 0, this.#_ = e, this.#R = (t2) => {
7371
7370
  this.#b -= e[t2], e[t2] = 0;
7372
- }, this.#k = (t2, i, s, n) => {
7371
+ }, this.#V = (t2, i, s, n) => {
7373
7372
  if (this.#e(i))
7374
7373
  return 0;
7375
- if (!F(s))
7374
+ if (!T(s))
7376
7375
  if (n) {
7377
7376
  if (typeof n != "function")
7378
7377
  throw new TypeError("sizeCalculation must be a function");
7379
- if (s = n(i, t2), !F(s))
7378
+ if (s = n(i, t2), !T(s))
7380
7379
  throw new TypeError("sizeCalculation return invalid (expect positive integer)");
7381
7380
  } else
7382
7381
  throw new TypeError("invalid size value (must be positive integer). When maxSize or maxEntrySize is used, sizeCalculation or size must be set.");
7383
7382
  return s;
7384
- }, this.#I = (t2, i, s) => {
7385
- if (e[t2] = i, this.#u) {
7386
- let n = this.#u - e[t2];
7383
+ }, this.#j = (t2, i, s) => {
7384
+ if (e[t2] = i, this.#c) {
7385
+ let n = this.#c - e[t2];
7387
7386
  for (;this.#b > n; )
7388
7387
  this.#G(true);
7389
7388
  }
@@ -7391,23 +7390,23 @@ var init_index_min = __esm(() => {
7391
7390
  };
7392
7391
  }
7393
7392
  #R = (e) => {};
7394
- #I = (e, t2, i) => {};
7395
- #k = (e, t2, i, s) => {
7393
+ #j = (e, t2, i) => {};
7394
+ #V = (e, t2, i, s) => {
7396
7395
  if (i || s)
7397
7396
  throw new TypeError("cannot set size without setting maxSize or maxEntrySize on cache");
7398
7397
  return 0;
7399
7398
  };
7400
7399
  *#A({ allowStale: e = this.allowStale } = {}) {
7401
7400
  if (this.#n)
7402
- for (let t2 = this.#h;this.#V(t2) && ((e || !this.#p(t2)) && (yield t2), t2 !== this.#l); )
7403
- t2 = this.#c[t2];
7401
+ for (let t2 = this.#h;this.#B(t2) && ((e || !this.#p(t2)) && (yield t2), t2 !== this.#a); )
7402
+ t2 = this.#u[t2];
7404
7403
  }
7405
7404
  *#z({ allowStale: e = this.allowStale } = {}) {
7406
7405
  if (this.#n)
7407
- for (let t2 = this.#l;this.#V(t2) && ((e || !this.#p(t2)) && (yield t2), t2 !== this.#h); )
7408
- t2 = this.#a[t2];
7406
+ for (let t2 = this.#a;this.#B(t2) && ((e || !this.#p(t2)) && (yield t2), t2 !== this.#h); )
7407
+ t2 = this.#l[t2];
7409
7408
  }
7410
- #V(e) {
7409
+ #B(e) {
7411
7410
  return e !== undefined && this.#s.get(this.#i[e]) === e;
7412
7411
  }
7413
7412
  *entries() {
@@ -7464,7 +7463,7 @@ var init_index_min = __esm(() => {
7464
7463
  purgeStale() {
7465
7464
  let e = false;
7466
7465
  for (let t2 of this.#z({ allowStale: true }))
7467
- this.#p(t2) && (this.#v(this.#i[t2], "expire"), e = true);
7466
+ this.#p(t2) && (this.#E(this.#i[t2], "expire"), e = true);
7468
7467
  return e;
7469
7468
  }
7470
7469
  info(e) {
@@ -7476,10 +7475,10 @@ var init_index_min = __esm(() => {
7476
7475
  return;
7477
7476
  let n = { value: s };
7478
7477
  if (this.#d && this.#F) {
7479
- let o = this.#d[t2], r = this.#F[t2];
7480
- if (o && r) {
7481
- let h = o - (this.#m.now() - r);
7482
- n.ttl = h, n.start = Date.now();
7478
+ let r = this.#d[t2], h = this.#F[t2];
7479
+ if (r && h) {
7480
+ let a = r - (this.#m.now() - h);
7481
+ n.ttl = a, n.start = Date.now();
7483
7482
  }
7484
7483
  }
7485
7484
  return this.#_ && (n.size = this.#_[t2]), n;
@@ -7490,13 +7489,13 @@ var init_index_min = __esm(() => {
7490
7489
  let i = this.#i[t2], s = this.#t[t2], n = this.#e(s) ? s.__staleWhileFetching : s;
7491
7490
  if (n === undefined || i === undefined)
7492
7491
  continue;
7493
- let o = { value: n };
7492
+ let r = { value: n };
7494
7493
  if (this.#d && this.#F) {
7495
- o.ttl = this.#d[t2];
7496
- let r = this.#m.now() - this.#F[t2];
7497
- o.start = Math.floor(Date.now() - r);
7494
+ r.ttl = this.#d[t2];
7495
+ let h = this.#m.now() - this.#F[t2];
7496
+ r.start = Math.floor(Date.now() - h);
7498
7497
  }
7499
- this.#_ && (o.size = this.#_[t2]), e.unshift([i, o]);
7498
+ this.#_ && (r.size = this.#_[t2]), e.unshift([i, r]);
7500
7499
  }
7501
7500
  return e;
7502
7501
  }
@@ -7507,57 +7506,56 @@ var init_index_min = __esm(() => {
7507
7506
  let s = Date.now() - i.start;
7508
7507
  i.start = this.#m.now() - s;
7509
7508
  }
7510
- this.#O(t2, i.value, i);
7509
+ this.#W(t2, i.value, i);
7511
7510
  }
7512
7511
  }
7513
7512
  set(e, t2, i = {}) {
7514
- let { status: s = S.hasSubscribers ? {} : undefined } = i;
7515
- i.status = s, s && (s.op = "set", s.key = e, t2 !== undefined && (s.value = t2));
7516
- let n = this.#O(e, t2, i);
7517
- return s && S.hasSubscribers && S.publish(s), n;
7513
+ let { status: s = w.hasSubscribers ? {} : undefined } = i;
7514
+ i.status = s, s && (s.op = "set", s.key = e, t2 !== undefined && (s.value = t2), s.cache = this);
7515
+ let n = this.#W(e, t2, i);
7516
+ return s && w.hasSubscribers && w.publish(s), n;
7518
7517
  }
7519
- #O(e, t2, i = {}) {
7520
- let { ttl: s = this.ttl, start: n, noDisposeOnSet: o = this.noDisposeOnSet, sizeCalculation: r = this.sizeCalculation, status: h } = i;
7518
+ #W(e, t2, i, s) {
7519
+ let { ttl: n = this.ttl, start: r, noDisposeOnSet: h = this.noDisposeOnSet, sizeCalculation: a = this.sizeCalculation, status: o } = i;
7521
7520
  if (t2 === undefined)
7522
- return h && (h.set = "deleted"), this.delete(e), this;
7523
- let { noUpdateTTL: l = this.noUpdateTTL } = i;
7524
- h && !this.#e(t2) && (h.value = t2);
7525
- let c = this.#k(e, t2, i.size || 0, r, h);
7526
- if (this.maxEntrySize && c > this.maxEntrySize)
7527
- return this.#v(e, "set"), h && (h.set = "miss", h.maxEntrySizeExceeded = true), this;
7528
- let f = this.#n === 0 ? undefined : this.#s.get(e);
7529
- if (f === undefined)
7530
- f = this.#n === 0 ? this.#h : this.#y.length !== 0 ? this.#y.pop() : this.#n === this.#o ? this.#G(false) : this.#n, this.#i[f] = e, this.#t[f] = t2, this.#s.set(e, f), this.#a[this.#h] = f, this.#c[f] = this.#h, this.#h = f, this.#n++, this.#I(f, c, h), h && (h.set = "add"), l = false, this.#j && this.#x?.(t2, e, "add");
7521
+ return o && (o.set = "deleted"), this.delete(e), this;
7522
+ let { noUpdateTTL: p = this.noUpdateTTL } = i, m = this.#e(t2);
7523
+ o && !m && (o.value = t2);
7524
+ let _ = this.#V(e, t2, i.size || 0, a, o);
7525
+ if (this.maxEntrySize && _ > this.maxEntrySize)
7526
+ return this.#E(e, "set"), o && (o.set = "miss", o.maxEntrySizeExceeded = true), this;
7527
+ let c = this.#n === 0 ? undefined : this.#s.get(e);
7528
+ if (c === undefined)
7529
+ c = this.#n === 0 ? this.#h : this.#y.length !== 0 ? this.#y.pop() : this.#n === this.#o ? this.#G(false) : this.#n, this.#i[c] = e, this.#t[c] = t2, this.#s.set(e, c), this.#l[this.#h] = c, this.#u[c] = this.#h, this.#h = c, this.#n++, this.#j(c, _, o), o && (o.set = "add"), p = false, this.#x && !m && this.#O?.(t2, e, "add");
7531
7530
  else {
7532
- this.#L(f);
7533
- let g = this.#t[f];
7534
- if (t2 !== g) {
7535
- if (this.#W && this.#e(g)) {
7536
- g.__abortController.abort(new Error("replaced"));
7537
- let { __staleWhileFetching: p } = g;
7538
- p !== undefined && !o && (this.#T && this.#w?.(p, e, "set"), this.#f && this.#r?.push([p, e, "set"]));
7539
- } else
7540
- o || (this.#T && this.#w?.(g, e, "set"), this.#f && this.#r?.push([g, e, "set"]));
7541
- if (this.#R(f), this.#I(f, c, h), this.#t[f] = t2, h) {
7542
- h.set = "replace";
7543
- let p = g && this.#e(g) ? g.__staleWhileFetching : g;
7544
- p !== undefined && (h.oldValue = p);
7531
+ this.#L(c);
7532
+ let d = this.#t[c];
7533
+ if (t2 !== d) {
7534
+ if (!h)
7535
+ if (this.#e(d)) {
7536
+ d !== s && d.__abortController.abort(new Error("replaced"));
7537
+ let { __staleWhileFetching: f } = d;
7538
+ f !== undefined && f !== t2 && (this.#T && this.#w?.(f, e, "set"), this.#f && this.#r?.push([f, e, "set"]));
7539
+ } else
7540
+ this.#T && this.#w?.(d, e, "set"), this.#f && this.#r?.push([d, e, "set"]);
7541
+ if (this.#R(c), this.#j(c, _, o), this.#t[c] = t2, !m) {
7542
+ let f = d && this.#e(d) ? d.__staleWhileFetching : d, y = f === undefined ? "add" : t2 !== f ? "replace" : "update";
7543
+ o && (o.set = y, f !== undefined && (o.oldValue = f)), this.#x && this.onInsert?.(t2, e, y);
7545
7544
  }
7546
7545
  } else
7547
- h && (h.set = "update");
7548
- this.#j && this.onInsert?.(t2, e, t2 === g ? "update" : "replace");
7546
+ m || (o && (o.set = "update"), this.#x && this.onInsert?.(t2, e, "update"));
7549
7547
  }
7550
- if (s !== 0 && !this.#d && this.#H(), this.#d && (l || this.#N(f, s, n), h && this.#E(h, f)), !o && this.#f && this.#r) {
7551
- let g = this.#r, p;
7552
- for (;p = g?.shift(); )
7553
- this.#S?.(...p);
7548
+ if (n !== 0 && !this.#d && this.#H(), this.#d && (p || this.#N(c, n, r), o && this.#v(o, c)), !h && this.#f && this.#r) {
7549
+ let d = this.#r, f;
7550
+ for (;f = d?.shift(); )
7551
+ this.#S?.(...f);
7554
7552
  }
7555
7553
  return this;
7556
7554
  }
7557
7555
  pop() {
7558
7556
  try {
7559
7557
  for (;this.#n; ) {
7560
- let e = this.#t[this.#l];
7558
+ let e = this.#t[this.#a];
7561
7559
  if (this.#G(true), this.#e(e)) {
7562
7560
  if (e.__staleWhileFetching)
7563
7561
  return e.__staleWhileFetching;
@@ -7573,34 +7571,36 @@ var init_index_min = __esm(() => {
7573
7571
  }
7574
7572
  }
7575
7573
  #G(e) {
7576
- let t2 = this.#l, i = this.#i[t2], s = this.#t[t2];
7577
- return this.#W && this.#e(s) ? s.__abortController.abort(new Error("evicted")) : (this.#T || this.#f) && (this.#T && this.#w?.(s, i, "evict"), this.#f && this.#r?.push([s, i, "evict"])), this.#R(t2), this.#g?.[t2] && (clearTimeout(this.#g[t2]), this.#g[t2] = undefined), e && (this.#i[t2] = undefined, this.#t[t2] = undefined, this.#y.push(t2)), this.#n === 1 ? (this.#l = this.#h = 0, this.#y.length = 0) : this.#l = this.#a[t2], this.#s.delete(i), this.#n--, t2;
7574
+ let t2 = this.#a, i = this.#i[t2], s = this.#t[t2], n = this.#e(s);
7575
+ n && s.__abortController.abort(new Error("evicted"));
7576
+ let r = n ? s.__staleWhileFetching : s;
7577
+ return (this.#T || this.#f) && r !== undefined && (this.#T && this.#w?.(r, i, "evict"), this.#f && this.#r?.push([r, i, "evict"])), this.#R(t2), this.#g?.[t2] && (clearTimeout(this.#g[t2]), this.#g[t2] = undefined), e && (this.#i[t2] = undefined, this.#t[t2] = undefined, this.#y.push(t2)), this.#n === 1 ? (this.#a = this.#h = 0, this.#y.length = 0) : this.#a = this.#l[t2], this.#s.delete(i), this.#n--, t2;
7578
7578
  }
7579
7579
  has(e, t2 = {}) {
7580
- let { status: i = S.hasSubscribers ? {} : undefined } = t2;
7581
- t2.status = i, i && (i.op = "has", i.key = e);
7580
+ let { status: i = w.hasSubscribers ? {} : undefined } = t2;
7581
+ t2.status = i, i && (i.op = "has", i.key = e, i.cache = this);
7582
7582
  let s = this.#Y(e, t2);
7583
- return S.hasSubscribers && S.publish(i), s;
7583
+ return w.hasSubscribers && w.publish(i), s;
7584
7584
  }
7585
7585
  #Y(e, t2 = {}) {
7586
7586
  let { updateAgeOnHas: i = this.updateAgeOnHas, status: s } = t2, n = this.#s.get(e);
7587
7587
  if (n !== undefined) {
7588
- let o = this.#t[n];
7589
- if (this.#e(o) && o.__staleWhileFetching === undefined)
7588
+ let r = this.#t[n];
7589
+ if (this.#e(r) && r.__staleWhileFetching === undefined)
7590
7590
  return false;
7591
7591
  if (this.#p(n))
7592
- s && (s.has = "stale", this.#E(s, n));
7592
+ s && (s.has = "stale", this.#v(s, n));
7593
7593
  else
7594
- return i && this.#D(n), s && (s.has = "hit", this.#E(s, n)), true;
7594
+ return i && this.#D(n), s && (s.has = "hit", this.#v(s, n)), true;
7595
7595
  } else
7596
7596
  s && (s.has = "miss");
7597
7597
  return false;
7598
7598
  }
7599
7599
  peek(e, t2 = {}) {
7600
7600
  let { status: i = D() ? {} : undefined } = t2;
7601
- i && (i.op = "peek", i.key = e), t2.status = i;
7601
+ i && (i.op = "peek", i.key = e, i.cache = this), t2.status = i;
7602
7602
  let s = this.#J(e, t2);
7603
- return S.hasSubscribers && S.publish(i), s;
7603
+ return w.hasSubscribers && w.publish(i), s;
7604
7604
  }
7605
7605
  #J(e, t2) {
7606
7606
  let { status: i, allowStale: s = this.allowStale } = t2, n = this.#s.get(e);
@@ -7608,39 +7608,39 @@ var init_index_min = __esm(() => {
7608
7608
  i && (i.peek = n === undefined ? "miss" : "stale");
7609
7609
  return;
7610
7610
  }
7611
- let o = this.#t[n], r = this.#e(o) ? o.__staleWhileFetching : o;
7612
- return i && (r !== undefined ? (i.peek = "hit", i.value = r) : i.peek = "miss"), r;
7611
+ let r = this.#t[n], h = this.#e(r) ? r.__staleWhileFetching : r;
7612
+ return i && (h !== undefined ? (i.peek = "hit", i.value = h) : i.peek = "miss"), h;
7613
7613
  }
7614
7614
  #P(e, t2, i, s) {
7615
7615
  let n = t2 === undefined ? undefined : this.#t[t2];
7616
7616
  if (this.#e(n))
7617
7617
  return n;
7618
- let o = new AbortController, { signal: r } = i;
7619
- r?.addEventListener("abort", () => o.abort(r.reason), { signal: o.signal });
7620
- let h = { signal: o.signal, options: i, context: s }, l = (w, y = false) => {
7621
- let { aborted: a } = o.signal, m = i.ignoreFetchAbort && w !== undefined, _ = i.ignoreFetchAbort || !!(i.allowStaleOnFetchAbort && w !== undefined);
7622
- if (i.status && (a && !y ? (i.status.fetchAborted = true, i.status.fetchError = o.signal.reason, m && (i.status.fetchAbortIgnored = true)) : i.status.fetchResolved = true), a && !m && !y)
7623
- return f(o.signal.reason, _);
7624
- let b = p, d = this.#t[t2];
7625
- return (d === p || d === undefined && m && y) && (w === undefined ? b.__staleWhileFetching !== undefined ? this.#t[t2] = b.__staleWhileFetching : this.#v(e, "fetch") : (i.status && (i.status.fetchUpdated = true), this.#O(e, w, h.options))), w;
7626
- }, c = (w) => (i.status && (i.status.fetchRejected = true, i.status.fetchError = w), f(w, false)), f = (w, y) => {
7627
- let { aborted: a } = o.signal, m = a && i.allowStaleOnFetchAbort, _ = m || i.allowStaleOnFetchRejection, b = _ || i.noDeleteOnFetchRejection, d = p;
7628
- if (this.#t[t2] === p && (!b || !y && d.__staleWhileFetching === undefined ? this.#v(e, "fetch") : m || (this.#t[t2] = d.__staleWhileFetching)), _)
7629
- return i.status && d.__staleWhileFetching !== undefined && (i.status.returnedStale = true), d.__staleWhileFetching;
7630
- if (d.__returned === d)
7631
- throw w;
7632
- }, g = (w, y) => {
7633
- let a = this.#M?.(e, n, h);
7634
- a && a instanceof Promise && a.then((m) => w(m === undefined ? undefined : m), y), o.signal.addEventListener("abort", () => {
7635
- (!i.ignoreFetchAbort || i.allowStaleOnFetchAbort) && (w(undefined), i.allowStaleOnFetchAbort && (w = (m) => l(m, true)));
7636
- });
7618
+ let r = new AbortController, { signal: h } = i;
7619
+ h?.addEventListener("abort", () => r.abort(h.reason), { signal: r.signal });
7620
+ let a = { signal: r.signal, options: i, context: s }, o = (f, y = false) => {
7621
+ let { aborted: l } = r.signal, S = i.ignoreFetchAbort && f !== undefined, F = i.ignoreFetchAbort || !!(i.allowStaleOnFetchAbort && f !== undefined);
7622
+ if (i.status && (l && !y ? (i.status.fetchAborted = true, i.status.fetchError = r.signal.reason, S && (i.status.fetchAbortIgnored = true)) : i.status.fetchResolved = true), l && !S && !y)
7623
+ return m(r.signal.reason, F);
7624
+ let b = c, g = this.#t[t2];
7625
+ return (g === c || g === undefined && S && y) && (f === undefined ? b.__staleWhileFetching !== undefined ? this.#t[t2] = b.__staleWhileFetching : this.#E(e, "fetch") : (i.status && (i.status.fetchUpdated = true), this.#W(e, f, a.options, b))), f;
7626
+ }, p = (f) => (i.status && (i.status.fetchRejected = true, i.status.fetchError = f), m(f, false)), m = (f, y) => {
7627
+ let { aborted: l } = r.signal, S = l && i.allowStaleOnFetchAbort, F = S || i.allowStaleOnFetchRejection, b = F || i.noDeleteOnFetchRejection, g = c;
7628
+ if (this.#t[t2] === c && (!b || !y && g.__staleWhileFetching === undefined ? this.#E(e, "fetch") : S || (this.#t[t2] = g.__staleWhileFetching)), F)
7629
+ return i.status && g.__staleWhileFetching !== undefined && (i.status.returnedStale = true), g.__staleWhileFetching;
7630
+ if (g.__returned === g)
7631
+ throw f;
7632
+ }, _ = (f, y) => {
7633
+ let l = this.#M?.(e, n, a);
7634
+ r.signal.addEventListener("abort", () => {
7635
+ (!i.ignoreFetchAbort || i.allowStaleOnFetchAbort) && (f(undefined), i.allowStaleOnFetchAbort && (f = (S) => o(S, true)));
7636
+ }), l && l instanceof Promise ? l.then((S) => f(S === undefined ? undefined : S), y) : l !== undefined && f(l);
7637
7637
  };
7638
7638
  i.status && (i.status.fetchDispatched = true);
7639
- let p = new Promise(g).then(l, c), T = Object.assign(p, { __abortController: o, __staleWhileFetching: n, __returned: undefined });
7640
- return t2 === undefined ? (this.#O(e, T, { ...h.options, status: undefined }), t2 = this.#s.get(e)) : this.#t[t2] = T, T;
7639
+ let c = new Promise(_).then(o, p), d = Object.assign(c, { __abortController: r, __staleWhileFetching: n, __returned: undefined });
7640
+ return t2 === undefined ? (this.#W(e, d, { ...a.options, status: undefined }), t2 = this.#s.get(e)) : this.#t[t2] = d, d;
7641
7641
  }
7642
7642
  #e(e) {
7643
- if (!this.#W)
7643
+ if (!this.#U)
7644
7644
  return false;
7645
7645
  let t2 = e;
7646
7646
  return !!t2 && t2 instanceof Promise && t2.hasOwnProperty("__staleWhileFetching") && t2.__abortController instanceof AbortController;
@@ -7648,29 +7648,29 @@ var init_index_min = __esm(() => {
7648
7648
  fetch(e, t2 = {}) {
7649
7649
  let i = W.hasSubscribers, { status: s = D() ? {} : undefined } = t2;
7650
7650
  t2.status = s, s && t2.context && (s.context = t2.context);
7651
- let n = this.#B(e, t2);
7651
+ let n = this.#k(e, t2);
7652
7652
  return s && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
7653
7653
  }
7654
- async#B(e, t2 = {}) {
7655
- let { allowStale: i = this.allowStale, updateAgeOnGet: s = this.updateAgeOnGet, noDeleteOnStaleGet: n = this.noDeleteOnStaleGet, ttl: o = this.ttl, noDisposeOnSet: r = this.noDisposeOnSet, size: h = 0, sizeCalculation: l = this.sizeCalculation, noUpdateTTL: c = this.noUpdateTTL, noDeleteOnFetchRejection: f = this.noDeleteOnFetchRejection, allowStaleOnFetchRejection: g = this.allowStaleOnFetchRejection, ignoreFetchAbort: p = this.ignoreFetchAbort, allowStaleOnFetchAbort: T = this.allowStaleOnFetchAbort, context: w, forceRefresh: y = false, status: a, signal: m } = t2;
7656
- if (a && (a.op = "fetch", a.key = e, y && (a.forceRefresh = true)), !this.#W)
7657
- return a && (a.fetch = "get"), this.#C(e, { allowStale: i, updateAgeOnGet: s, noDeleteOnStaleGet: n, status: a });
7658
- let _ = { allowStale: i, updateAgeOnGet: s, noDeleteOnStaleGet: n, ttl: o, noDisposeOnSet: r, size: h, sizeCalculation: l, noUpdateTTL: c, noDeleteOnFetchRejection: f, allowStaleOnFetchRejection: g, allowStaleOnFetchAbort: T, ignoreFetchAbort: p, status: a, signal: m }, b = this.#s.get(e);
7654
+ async#k(e, t2 = {}) {
7655
+ let { allowStale: i = this.allowStale, updateAgeOnGet: s = this.updateAgeOnGet, noDeleteOnStaleGet: n = this.noDeleteOnStaleGet, ttl: r = this.ttl, noDisposeOnSet: h = this.noDisposeOnSet, size: a = 0, sizeCalculation: o = this.sizeCalculation, noUpdateTTL: p = this.noUpdateTTL, noDeleteOnFetchRejection: m = this.noDeleteOnFetchRejection, allowStaleOnFetchRejection: _ = this.allowStaleOnFetchRejection, ignoreFetchAbort: c = this.ignoreFetchAbort, allowStaleOnFetchAbort: d = this.allowStaleOnFetchAbort, context: f, forceRefresh: y = false, status: l, signal: S } = t2;
7656
+ if (l && (l.op = "fetch", l.key = e, y && (l.forceRefresh = true), l.cache = this), !this.#U)
7657
+ return l && (l.fetch = "get"), this.#C(e, { allowStale: i, updateAgeOnGet: s, noDeleteOnStaleGet: n, status: l });
7658
+ let F = { allowStale: i, updateAgeOnGet: s, noDeleteOnStaleGet: n, ttl: r, noDisposeOnSet: h, size: a, sizeCalculation: o, noUpdateTTL: p, noDeleteOnFetchRejection: m, allowStaleOnFetchRejection: _, allowStaleOnFetchAbort: d, ignoreFetchAbort: c, status: l, signal: S }, b = this.#s.get(e);
7659
7659
  if (b === undefined) {
7660
- a && (a.fetch = "miss");
7661
- let d = this.#P(e, b, _, w);
7662
- return d.__returned = d;
7660
+ l && (l.fetch = "miss");
7661
+ let g = this.#P(e, b, F, f);
7662
+ return g.__returned = g;
7663
7663
  } else {
7664
- let d = this.#t[b];
7665
- if (this.#e(d)) {
7666
- let E = i && d.__staleWhileFetching !== undefined;
7667
- return a && (a.fetch = "inflight", E && (a.returnedStale = true)), E ? d.__staleWhileFetching : d.__returned = d;
7664
+ let g = this.#t[b];
7665
+ if (this.#e(g)) {
7666
+ let v = i && g.__staleWhileFetching !== undefined;
7667
+ return l && (l.fetch = "inflight", v && (l.returnedStale = true)), v ? g.__staleWhileFetching : g.__returned = g;
7668
7668
  }
7669
7669
  let A = this.#p(b);
7670
7670
  if (!y && !A)
7671
- return a && (a.fetch = "hit"), this.#L(b), s && this.#D(b), a && this.#E(a, b), d;
7672
- let z = this.#P(e, b, _, w), v = z.__staleWhileFetching !== undefined && i;
7673
- return a && (a.fetch = A ? "stale" : "refresh", v && A && (a.returnedStale = true)), v ? z.__staleWhileFetching : z.__returned = z;
7671
+ return l && (l.fetch = "hit"), this.#L(b), s && this.#D(b), l && this.#v(l, b), g;
7672
+ let z = this.#P(e, b, F, f), E = z.__staleWhileFetching !== undefined && i;
7673
+ return l && (l.fetch = A ? "stale" : "refresh", E && A && (l.returnedStale = true)), E ? z.__staleWhileFetching : z.__returned = z;
7674
7674
  }
7675
7675
  }
7676
7676
  forceFetch(e, t2 = {}) {
@@ -7680,55 +7680,55 @@ var init_index_min = __esm(() => {
7680
7680
  return s && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
7681
7681
  }
7682
7682
  async#K(e, t2 = {}) {
7683
- let i = await this.#B(e, t2);
7683
+ let i = await this.#k(e, t2);
7684
7684
  if (i === undefined)
7685
7685
  throw new Error("fetch() returned undefined");
7686
7686
  return i;
7687
7687
  }
7688
7688
  memo(e, t2 = {}) {
7689
- let { status: i = S.hasSubscribers ? {} : undefined } = t2;
7690
- t2.status = i, i && (i.op = "memo", i.key = e, t2.context && (i.context = t2.context));
7689
+ let { status: i = w.hasSubscribers ? {} : undefined } = t2;
7690
+ t2.status = i, i && (i.op = "memo", i.key = e, t2.context && (i.context = t2.context), i.cache = this);
7691
7691
  let s = this.#Q(e, t2);
7692
- return i && (i.value = s), S.hasSubscribers && S.publish(i), s;
7692
+ return i && (i.value = s), w.hasSubscribers && w.publish(i), s;
7693
7693
  }
7694
7694
  #Q(e, t2 = {}) {
7695
- let i = this.#U;
7695
+ let i = this.#I;
7696
7696
  if (!i)
7697
7697
  throw new Error("no memoMethod provided to constructor");
7698
- let { context: s, status: n, forceRefresh: o, ...r } = t2;
7699
- n && o && (n.forceRefresh = true);
7700
- let h = this.#C(e, r), l = o || h === undefined;
7701
- if (n && (n.memo = l ? "miss" : "hit", l || (n.value = h)), !l)
7702
- return h;
7703
- let c = i(e, h, { options: r, context: s });
7704
- return n && (n.value = c), this.#O(e, c, r), c;
7698
+ let { context: s, status: n, forceRefresh: r, ...h } = t2;
7699
+ n && r && (n.forceRefresh = true);
7700
+ let a = this.#C(e, h), o = r || a === undefined;
7701
+ if (n && (n.memo = o ? "miss" : "hit", o || (n.value = a)), !o)
7702
+ return a;
7703
+ let p = i(e, a, { options: h, context: s });
7704
+ return n && (n.value = p), this.#W(e, p, h), p;
7705
7705
  }
7706
7706
  get(e, t2 = {}) {
7707
- let { status: i = S.hasSubscribers ? {} : undefined } = t2;
7708
- t2.status = i, i && (i.op = "get", i.key = e);
7707
+ let { status: i = w.hasSubscribers ? {} : undefined } = t2;
7708
+ t2.status = i, i && (i.op = "get", i.key = e, i.cache = this);
7709
7709
  let s = this.#C(e, t2);
7710
- return i && (s !== undefined && (i.value = s), S.hasSubscribers && S.publish(i)), s;
7710
+ return i && (s !== undefined && (i.value = s), w.hasSubscribers && w.publish(i)), s;
7711
7711
  }
7712
7712
  #C(e, t2 = {}) {
7713
- let { allowStale: i = this.allowStale, updateAgeOnGet: s = this.updateAgeOnGet, noDeleteOnStaleGet: n = this.noDeleteOnStaleGet, status: o } = t2, r = this.#s.get(e);
7714
- if (r === undefined) {
7715
- o && (o.get = "miss");
7713
+ let { allowStale: i = this.allowStale, updateAgeOnGet: s = this.updateAgeOnGet, noDeleteOnStaleGet: n = this.noDeleteOnStaleGet, status: r } = t2, h = this.#s.get(e);
7714
+ if (h === undefined) {
7715
+ r && (r.get = "miss");
7716
7716
  return;
7717
7717
  }
7718
- let h = this.#t[r], l = this.#e(h);
7719
- return o && this.#E(o, r), this.#p(r) ? l ? (o && (o.get = "stale-fetching"), i && h.__staleWhileFetching !== undefined ? (o && (o.returnedStale = true), h.__staleWhileFetching) : undefined) : (n || this.#v(e, "expire"), o && (o.get = "stale"), i ? (o && (o.returnedStale = true), h) : undefined) : (o && (o.get = l ? "fetching" : "hit"), this.#L(r), s && this.#D(r), l ? h.__staleWhileFetching : h);
7718
+ let a = this.#t[h], o = this.#e(a);
7719
+ return r && this.#v(r, h), this.#p(h) ? o ? (r && (r.get = "stale-fetching"), i && a.__staleWhileFetching !== undefined ? (r && (r.returnedStale = true), a.__staleWhileFetching) : undefined) : (n || this.#E(e, "expire"), r && (r.get = "stale"), i ? (r && (r.returnedStale = true), a) : undefined) : (r && (r.get = o ? "fetching" : "hit"), this.#L(h), s && this.#D(h), o ? a.__staleWhileFetching : a);
7720
7720
  }
7721
7721
  #$(e, t2) {
7722
- this.#c[t2] = e, this.#a[e] = t2;
7722
+ this.#u[t2] = e, this.#l[e] = t2;
7723
7723
  }
7724
7724
  #L(e) {
7725
- e !== this.#h && (e === this.#l ? this.#l = this.#a[e] : this.#$(this.#c[e], this.#a[e]), this.#$(this.#h, e), this.#h = e);
7725
+ e !== this.#h && (e === this.#a ? this.#a = this.#l[e] : this.#$(this.#u[e], this.#l[e]), this.#$(this.#h, e), this.#h = e);
7726
7726
  }
7727
7727
  delete(e) {
7728
- return this.#v(e, "delete");
7728
+ return this.#E(e, "delete");
7729
7729
  }
7730
- #v(e, t2) {
7731
- S.hasSubscribers && S.publish({ op: "delete", delete: t2, key: e });
7730
+ #E(e, t2) {
7731
+ w.hasSubscribers && w.publish({ op: "delete", delete: t2, key: e, cache: this });
7732
7732
  let i = false;
7733
7733
  if (this.#n !== 0) {
7734
7734
  let s = this.#s.get(e);
@@ -7739,14 +7739,14 @@ var init_index_min = __esm(() => {
7739
7739
  this.#R(s);
7740
7740
  let n = this.#t[s];
7741
7741
  if (this.#e(n) ? n.__abortController.abort(new Error("deleted")) : (this.#T || this.#f) && (this.#T && this.#w?.(n, e, t2), this.#f && this.#r?.push([n, e, t2])), this.#s.delete(e), this.#i[s] = undefined, this.#t[s] = undefined, s === this.#h)
7742
- this.#h = this.#c[s];
7743
- else if (s === this.#l)
7744
- this.#l = this.#a[s];
7742
+ this.#h = this.#u[s];
7743
+ else if (s === this.#a)
7744
+ this.#a = this.#l[s];
7745
7745
  else {
7746
- let o = this.#c[s];
7747
- this.#a[o] = this.#a[s];
7748
- let r = this.#a[s];
7749
- this.#c[r] = this.#c[s];
7746
+ let r = this.#u[s];
7747
+ this.#l[r] = this.#l[s];
7748
+ let h = this.#l[s];
7749
+ this.#u[h] = this.#u[s];
7750
7750
  }
7751
7751
  this.#n--, this.#y.push(s);
7752
7752
  }
@@ -7777,7 +7777,7 @@ var init_index_min = __esm(() => {
7777
7777
  t2 !== undefined && clearTimeout(t2);
7778
7778
  this.#g?.fill(undefined);
7779
7779
  }
7780
- if (this.#_ && this.#_.fill(0), this.#l = 0, this.#h = 0, this.#y.length = 0, this.#b = 0, this.#n = 0, this.#f && this.#r) {
7780
+ if (this.#_ && this.#_.fill(0), this.#a = 0, this.#h = 0, this.#y.length = 0, this.#b = 0, this.#n = 0, this.#f && this.#r) {
7781
7781
  let t2 = this.#r, i;
7782
7782
  for (;i = t2?.shift(); )
7783
7783
  this.#S?.(...i);
@@ -9887,6 +9887,7 @@ class ScreenManager {
9887
9887
  let output = cursorDown(this.extraLinesUnderPrompt);
9888
9888
  output += clearContent ? eraseLines(this.height) : `
9889
9889
  `;
9890
+ output += cursorLeft;
9890
9891
  output += cursorShow;
9891
9892
  this.write(output);
9892
9893
  this.rl.close();
@@ -9921,7 +9922,7 @@ import * as readline2 from "readline";
9921
9922
  import { AsyncResource as AsyncResource3 } from "async_hooks";
9922
9923
  import path from "path";
9923
9924
  function getCallSites() {
9924
- const _prepareStackTrace = Error.prepareStackTrace;
9925
+ const savedPrepareStackTrace = Error.prepareStackTrace;
9925
9926
  let result = [];
9926
9927
  try {
9927
9928
  Error.prepareStackTrace = (_, callSites) => {
@@ -9933,7 +9934,7 @@ function getCallSites() {
9933
9934
  } catch {
9934
9935
  return result;
9935
9936
  }
9936
- Error.prepareStackTrace = _prepareStackTrace;
9937
+ Error.prepareStackTrace = savedPrepareStackTrace;
9937
9938
  return result;
9938
9939
  }
9939
9940
  function createPrompt(view) {
@@ -20845,8 +20846,8 @@ var init_capabilities = __esm(() => {
20845
20846
  schemaFullScan: cap("limited", "readonly", "Full scan is sampled and document-oriented."),
20846
20847
  query: cap("limited", "readonly", "Uses JSON filter or aggregation syntax, not SQL."),
20847
20848
  queryLimitGuard: cap("limited", "readonly", "Applies result limits and collection size guard."),
20848
- queries: cap("limited", "local-write", "Snippet management works, but saved-query execution is not supported for MongoDB."),
20849
- q: cap("unsupported", "none", "MongoDB saved-query execution is not supported."),
20849
+ queries: cap("limited", "local-write", "Snippet management works with MongoDB-specific saved-query limitations."),
20850
+ q: cap("limited", "readonly", "Saved MongoDB snippets require collection frontmatter, accept JSON find/aggregate bodies, and JSON-encode parameter substitutions."),
20850
20851
  insert: cap("limited", "db-write", "Document insert is supported with Mongo-specific behavior."),
20851
20852
  update: cap("limited", "db-write", "Document update is supported with Mongo-specific behavior."),
20852
20853
  delete: cap("limited", "db-write", "Document delete is supported with Mongo-specific behavior."),
@@ -21776,22 +21777,29 @@ class MongoDBAdapter {
21776
21777
  const collection = db.collection(collectionName);
21777
21778
  const estimatedRowCount = await collection.estimatedDocumentCount();
21778
21779
  const requested = options?.sampleSize;
21779
- const desired = typeof requested === "number" && requested >= 1 ? requested : 50;
21780
+ const desired = typeof requested === "number" && requested >= 1 ? requested : 100;
21780
21781
  const sampleSize = Math.min(desired, 1000);
21781
- const samples = await collection.find().limit(sampleSize).toArray();
21782
- const columnMap = new Map;
21783
- for (const doc of samples) {
21784
- for (const [key, value] of Object.entries(doc)) {
21785
- if (!columnMap.has(key)) {
21786
- columnMap.set(key, new Set);
21787
- }
21788
- columnMap.get(key).add(typeof value);
21782
+ const method = options?.sampleMethod ?? "random";
21783
+ let samples = [];
21784
+ if (method === "random") {
21785
+ try {
21786
+ samples = await collection.aggregate([{ $sample: { size: sampleSize } }]).toArray();
21787
+ } catch (err) {
21788
+ console.error(`[mongo] $sample failed (${err.message}); falling back to natural order.`);
21789
+ samples = await collection.find().limit(sampleSize).toArray();
21789
21790
  }
21791
+ } else {
21792
+ samples = await collection.find().limit(sampleSize).toArray();
21790
21793
  }
21791
- const columns = Array.from(columnMap.entries()).map(([name, types2]) => ({
21794
+ const accumulator = new Map;
21795
+ for (const doc of samples)
21796
+ accumulateDocPaths(doc, "", accumulator);
21797
+ const denom = Math.max(samples.length, 1);
21798
+ const columns = Array.from(accumulator.entries()).map(([name, info]) => ({
21792
21799
  name,
21793
- type: Array.from(types2).join(" | "),
21794
- nullable: true
21800
+ type: Array.from(info.types).sort().join(" | "),
21801
+ nullable: info.types.has("null") || info.count < denom,
21802
+ presence: info.count / denom
21795
21803
  }));
21796
21804
  return {
21797
21805
  name: collectionName,
@@ -21836,6 +21844,44 @@ class MongoDBAdapter {
21836
21844
  };
21837
21845
  }
21838
21846
  }
21847
+ function detectMongoType(v) {
21848
+ if (v === null || v === undefined)
21849
+ return "null";
21850
+ if (Array.isArray(v))
21851
+ return "array";
21852
+ if (v instanceof Date)
21853
+ return "Date";
21854
+ if (typeof v === "object") {
21855
+ const ctor = v.constructor?.name;
21856
+ if (ctor === "ObjectId")
21857
+ return "ObjectId";
21858
+ if (ctor === "Binary")
21859
+ return "Binary";
21860
+ if (ctor === "Decimal128")
21861
+ return "Decimal128";
21862
+ return "object";
21863
+ }
21864
+ if (typeof v === "string")
21865
+ return "string";
21866
+ if (typeof v === "number")
21867
+ return "number";
21868
+ if (typeof v === "boolean")
21869
+ return "boolean";
21870
+ return "string";
21871
+ }
21872
+ function accumulateDocPaths(doc, prefix, acc) {
21873
+ for (const [k, v] of Object.entries(doc)) {
21874
+ const path3 = prefix === "" ? k : `${prefix}.${k}`;
21875
+ const t2 = detectMongoType(v);
21876
+ const slot = acc.get(path3) ?? { types: new Set, count: 0 };
21877
+ slot.types.add(t2);
21878
+ slot.count += 1;
21879
+ acc.set(path3, slot);
21880
+ if (t2 === "object" && v && typeof v === "object") {
21881
+ accumulateDocPaths(v, path3, acc);
21882
+ }
21883
+ }
21884
+ }
21839
21885
  var init_mongodb_adapter = __esm(() => {
21840
21886
  init_types2();
21841
21887
  });
@@ -24289,6 +24335,51 @@ var require_table = __commonJS((exports, module) => {
24289
24335
  module.exports = Table;
24290
24336
  });
24291
24337
 
24338
+ // src/formatters/table-formatter.ts
24339
+ class TableFormatter {
24340
+ format(columns) {
24341
+ const table = new Table({
24342
+ head: ["Column", "Type", "Nullable", "Default", "Key"].map((h) => colors.bold(h)),
24343
+ style: { compact: false, "padding-left": 1, "padding-right": 1 },
24344
+ colWidths: [25, 25, 10, 25, 20]
24345
+ });
24346
+ columns.forEach((col) => {
24347
+ let keyType = "";
24348
+ if (col.primaryKey) {
24349
+ keyType = "PK";
24350
+ } else if (col.foreignKey) {
24351
+ keyType = `FK \u2192 ${col.foreignKey.table}.${col.foreignKey.column}`;
24352
+ }
24353
+ table.push([col.name, col.type, col.nullable ? "YES" : "NO", col.default || "NULL", keyType]);
24354
+ });
24355
+ return table.toString();
24356
+ }
24357
+ }
24358
+
24359
+ class TableListFormatter {
24360
+ format(tables) {
24361
+ const table = new Table({
24362
+ head: ["Table", "Columns", "Rows", "Engine"].map((h) => colors.bold(h)),
24363
+ style: { compact: false, "padding-left": 1, "padding-right": 1 },
24364
+ colWidths: [30, 12, 15, 15]
24365
+ });
24366
+ tables.forEach((t2) => {
24367
+ table.push([
24368
+ t2.name,
24369
+ (t2.columnCount ?? t2.columns.length).toString(),
24370
+ (t2.rowCount ?? "?").toString(),
24371
+ t2.engine || "N/A"
24372
+ ]);
24373
+ });
24374
+ return table.toString();
24375
+ }
24376
+ }
24377
+ var Table;
24378
+ var init_table_formatter = __esm(() => {
24379
+ init_colors();
24380
+ Table = require_table();
24381
+ });
24382
+
24292
24383
  // src/core/size-category.ts
24293
24384
  var exports_size_category = {};
24294
24385
  __export(exports_size_category, {
@@ -24311,6 +24402,240 @@ var init_size_category = __esm(() => {
24311
24402
  ];
24312
24403
  });
24313
24404
 
24405
+ // src/formatters/json-formatter.ts
24406
+ class TableSchemaJSONFormatter {
24407
+ format(table, options) {
24408
+ const spacing = options?.compact ? undefined : 2;
24409
+ const rowCount = table.estimatedRowCount ?? table.rowCount;
24410
+ const output = {
24411
+ name: table.name,
24412
+ estimatedRowCount: rowCount ?? null,
24413
+ sizeCategory: rowCount !== undefined && rowCount !== null ? getSizeCategory(rowCount) : null,
24414
+ tableType: table.tableType ?? "table",
24415
+ columns: table.columns,
24416
+ primaryKey: table.primaryKey || [],
24417
+ foreignKeys: table.foreignKeys || [],
24418
+ indexes: table.indexes || [],
24419
+ metadata: {
24420
+ rowCount: table.rowCount,
24421
+ engine: table.engine
24422
+ }
24423
+ };
24424
+ return JSON.stringify(output, null, spacing);
24425
+ }
24426
+ }
24427
+ var init_json_formatter = __esm(() => {
24428
+ init_size_category();
24429
+ });
24430
+
24431
+ // src/formatters/query-result-formatter.ts
24432
+ class QueryResultFormatter {
24433
+ format(result, options) {
24434
+ const format = options?.format || "table";
24435
+ switch (format) {
24436
+ case "json":
24437
+ return this.formatJSON(result, options?.compact);
24438
+ case "csv":
24439
+ return this.formatCSV(result);
24440
+ case "table":
24441
+ default:
24442
+ return this.formatTable(result);
24443
+ }
24444
+ }
24445
+ formatTable(result) {
24446
+ if (result.rows.length === 0) {
24447
+ return this.formatEmptyTable(result);
24448
+ }
24449
+ const table = new Table2({
24450
+ head: result.columnNames,
24451
+ style: { compact: false, "padding-left": 1, "padding-right": 1 }
24452
+ });
24453
+ result.rows.forEach((row) => {
24454
+ table.push(result.columnNames.map((col) => this.cellToString(row[col])));
24455
+ });
24456
+ let output = table.toString();
24457
+ const footerLines = [];
24458
+ footerLines.push(`Rows: ${result.rowCount}`);
24459
+ if (result.executionTimeMs !== undefined) {
24460
+ footerLines.push(`Execution time: ${result.executionTimeMs}ms`);
24461
+ }
24462
+ if (footerLines.length > 0) {
24463
+ output += `
24464
+ ` + footerLines.join(" | ");
24465
+ }
24466
+ if (result.metadata?.securityNotification) {
24467
+ output += `
24468
+ ` + result.metadata.securityNotification;
24469
+ }
24470
+ return output;
24471
+ }
24472
+ formatEmptyTable(result) {
24473
+ const table = new Table2({
24474
+ head: result.columnNames,
24475
+ style: { compact: false, "padding-left": 1, "padding-right": 1 }
24476
+ });
24477
+ let output = table.toString();
24478
+ const footerLines = [];
24479
+ footerLines.push(`Rows: ${result.rowCount}`);
24480
+ if (result.executionTimeMs !== undefined) {
24481
+ footerLines.push(`Execution time: ${result.executionTimeMs}ms`);
24482
+ }
24483
+ if (footerLines.length > 0) {
24484
+ output += `
24485
+ ` + footerLines.join(" | ");
24486
+ }
24487
+ if (result.metadata?.securityNotification) {
24488
+ output += `
24489
+ ` + result.metadata.securityNotification;
24490
+ }
24491
+ return output;
24492
+ }
24493
+ formatJSON(result, compact) {
24494
+ const spacing = compact ? undefined : 2;
24495
+ const output = {
24496
+ rows: result.rows,
24497
+ rowCount: result.rowCount,
24498
+ columnNames: result.columnNames,
24499
+ columnTypes: result.columnTypes,
24500
+ executionTimeMs: result.executionTimeMs,
24501
+ metadata: result.metadata
24502
+ };
24503
+ return JSON.stringify(output, null, spacing);
24504
+ }
24505
+ formatCSV(result) {
24506
+ if (result.rows.length === 0) {
24507
+ let csvOutput = result.columnNames.map((name) => this.escapeCSVField(name)).join(",");
24508
+ if (result.metadata?.securityNotification) {
24509
+ csvOutput += `
24510
+ # ` + result.metadata.securityNotification;
24511
+ }
24512
+ return csvOutput;
24513
+ }
24514
+ const lines = [];
24515
+ lines.push(result.columnNames.map((name) => this.escapeCSVField(name)).join(","));
24516
+ result.rows.forEach((row) => {
24517
+ const csvRow = result.columnNames.map((col) => this.escapeCSVField(row[col])).join(",");
24518
+ lines.push(csvRow);
24519
+ });
24520
+ if (result.metadata?.securityNotification) {
24521
+ lines.push(`# ${result.metadata.securityNotification}`);
24522
+ }
24523
+ return lines.join(`
24524
+ `);
24525
+ }
24526
+ escapeCSVField(value) {
24527
+ if (value === null || value === undefined) {
24528
+ return "";
24529
+ }
24530
+ const str = String(value);
24531
+ if (str.includes(",") || str.includes('"') || str.includes(`
24532
+ `)) {
24533
+ return `"${str.replace(/"/g, '""')}"`;
24534
+ }
24535
+ return str;
24536
+ }
24537
+ cellToString(value) {
24538
+ if (value === null || value === undefined) {
24539
+ return "";
24540
+ }
24541
+ if (typeof value === "object") {
24542
+ return JSON.stringify(value);
24543
+ }
24544
+ return String(value);
24545
+ }
24546
+ }
24547
+ var Table2;
24548
+ var init_query_result_formatter = __esm(() => {
24549
+ Table2 = require_table();
24550
+ });
24551
+
24552
+ // src/formatters/index.ts
24553
+ var init_formatters = __esm(() => {
24554
+ init_table_formatter();
24555
+ init_json_formatter();
24556
+ init_query_result_formatter();
24557
+ });
24558
+
24559
+ // src/core/blacklist-manager.ts
24560
+ class BlacklistManager {
24561
+ config;
24562
+ state;
24563
+ overrideEnabled;
24564
+ constructor(config, overrideEnvValue) {
24565
+ this.config = config;
24566
+ this.overrideEnabled = (overrideEnvValue ?? Bun.env.DBCLI_OVERRIDE_BLACKLIST ?? "") === "true";
24567
+ this.state = this.loadBlacklist();
24568
+ }
24569
+ loadBlacklist() {
24570
+ const tables = new Set;
24571
+ const columns = new Map;
24572
+ const blacklistConfig = this.config.blacklist;
24573
+ if (!blacklistConfig) {
24574
+ return { tables, columns };
24575
+ }
24576
+ if (Array.isArray(blacklistConfig.tables)) {
24577
+ for (const tableName of blacklistConfig.tables) {
24578
+ if (typeof tableName === "string") {
24579
+ tables.add(tableName.toLowerCase());
24580
+ } else {
24581
+ console.warn(`[BlacklistManager] Invalid table name in blacklist config: ${JSON.stringify(tableName)}`);
24582
+ }
24583
+ }
24584
+ } else if (blacklistConfig.tables !== undefined) {
24585
+ console.warn("[BlacklistManager] blacklist.tables must be an array, ignoring");
24586
+ }
24587
+ if (blacklistConfig.columns && typeof blacklistConfig.columns === "object" && !Array.isArray(blacklistConfig.columns)) {
24588
+ for (const [tableName, cols] of Object.entries(blacklistConfig.columns)) {
24589
+ if (typeof tableName !== "string") {
24590
+ console.warn(`[BlacklistManager] Invalid table name key in columns config: ${JSON.stringify(tableName)}`);
24591
+ continue;
24592
+ }
24593
+ if (!Array.isArray(cols)) {
24594
+ console.warn(`[BlacklistManager] blacklist.columns["${tableName}"] must be an array, ignoring`);
24595
+ continue;
24596
+ }
24597
+ const columnSet = new Set;
24598
+ for (const col of cols) {
24599
+ if (typeof col === "string") {
24600
+ columnSet.add(col);
24601
+ } else {
24602
+ console.warn(`[BlacklistManager] Invalid column name in blacklist.columns["${tableName}"]: ${JSON.stringify(col)}`);
24603
+ }
24604
+ }
24605
+ if (columnSet.size > 0) {
24606
+ columns.set(tableName.toLowerCase(), columnSet);
24607
+ }
24608
+ }
24609
+ } else if (blacklistConfig.columns !== undefined) {
24610
+ console.warn("[BlacklistManager] blacklist.columns must be an object, ignoring");
24611
+ }
24612
+ return { tables, columns };
24613
+ }
24614
+ isTableBlacklisted(tableName) {
24615
+ return this.state.tables.has(tableName.toLowerCase());
24616
+ }
24617
+ isColumnBlacklisted(tableName, columnName) {
24618
+ const columnSet = this.state.columns.get(tableName.toLowerCase());
24619
+ if (!columnSet) {
24620
+ return false;
24621
+ }
24622
+ return columnSet.has(columnName);
24623
+ }
24624
+ getBlacklistedColumns(tableName) {
24625
+ const columnSet = this.state.columns.get(tableName.toLowerCase());
24626
+ if (!columnSet) {
24627
+ return [];
24628
+ }
24629
+ return Array.from(columnSet);
24630
+ }
24631
+ canOverrideBlacklist() {
24632
+ return this.overrideEnabled;
24633
+ }
24634
+ getState() {
24635
+ return this.state;
24636
+ }
24637
+ }
24638
+
24314
24639
  // src/types/blacklist.ts
24315
24640
  var BlacklistError;
24316
24641
  var init_blacklist = __esm(() => {
@@ -24326,6 +24651,87 @@ var init_blacklist = __esm(() => {
24326
24651
  };
24327
24652
  });
24328
24653
 
24654
+ // src/core/blacklist-validator.ts
24655
+ class BlacklistValidator {
24656
+ manager;
24657
+ constructor(manager) {
24658
+ this.manager = manager;
24659
+ }
24660
+ checkTableBlacklist(operation, tableName, _tableList = []) {
24661
+ if (this.manager.canOverrideBlacklist()) {
24662
+ const message = t_vars("warnings.blacklist_override_used", {
24663
+ operation,
24664
+ table: tableName
24665
+ });
24666
+ console.error(message);
24667
+ return;
24668
+ }
24669
+ if (this.manager.isTableBlacklisted(tableName)) {
24670
+ const message = t_vars("errors.table_blacklisted", {
24671
+ table: tableName,
24672
+ operation
24673
+ });
24674
+ throw new BlacklistError(message, tableName, operation);
24675
+ }
24676
+ }
24677
+ checkColumnBlacklistOnWrite(tableName, fields, operation = "WRITE") {
24678
+ const blacklisted = this.manager.getBlacklistedColumns(tableName);
24679
+ if (blacklisted.length === 0 || fields.length === 0) {
24680
+ return;
24681
+ }
24682
+ const conflicts = fields.filter((f) => blacklisted.includes(f));
24683
+ if (conflicts.length === 0) {
24684
+ return;
24685
+ }
24686
+ if (this.manager.canOverrideBlacklist()) {
24687
+ const warning = t_vars("warnings.blacklist_override_used", {
24688
+ operation,
24689
+ table: tableName
24690
+ });
24691
+ console.error(`${warning} (columns: ${conflicts.join(", ")})`);
24692
+ return;
24693
+ }
24694
+ const message = t_vars("errors.column_blacklisted_write", {
24695
+ table: tableName,
24696
+ operation,
24697
+ columns: conflicts.join(", ")
24698
+ });
24699
+ throw new BlacklistError(message, tableName, operation);
24700
+ }
24701
+ filterColumns(tableName, rows, columnList) {
24702
+ const blacklistedColumns = this.manager.getBlacklistedColumns(tableName);
24703
+ if (blacklistedColumns.length === 0) {
24704
+ return { filteredRows: rows, omittedColumns: [] };
24705
+ }
24706
+ const omittedColumns = columnList.filter((col) => blacklistedColumns.includes(col));
24707
+ if (omittedColumns.length === 0) {
24708
+ return { filteredRows: rows, omittedColumns: [] };
24709
+ }
24710
+ const filteredRows = rows.map((row) => {
24711
+ const newRow = {};
24712
+ for (const [key, value] of Object.entries(row)) {
24713
+ if (!omittedColumns.includes(key)) {
24714
+ newRow[key] = value;
24715
+ }
24716
+ }
24717
+ return newRow;
24718
+ });
24719
+ return { filteredRows, omittedColumns };
24720
+ }
24721
+ buildSecurityNotification(_tableName, omittedColumns) {
24722
+ if (omittedColumns.length === 0) {
24723
+ return "";
24724
+ }
24725
+ return t_vars("security.columns_omitted", {
24726
+ count: omittedColumns.length
24727
+ });
24728
+ }
24729
+ }
24730
+ var init_blacklist_validator = __esm(() => {
24731
+ init_blacklist();
24732
+ init_message_loader();
24733
+ });
24734
+
24329
24735
  // src/core/audit/lock.ts
24330
24736
  import { hostname } from "os";
24331
24737
  import { dirname } from "path";
@@ -24827,8 +25233,75 @@ var init_integration_helper = __esm(() => {
24827
25233
  _loggers = new Map;
24828
25234
  });
24829
25235
 
25236
+ // src/core/mongo/path-matcher.ts
25237
+ function compilePatterns(raw) {
25238
+ const patterns = [];
25239
+ const rejected = [];
25240
+ for (const entry of raw) {
25241
+ if (typeof entry !== "string" || entry.length === 0) {
25242
+ rejected.push({ raw: String(entry ?? ""), reason: "must be a non-empty string" });
25243
+ continue;
25244
+ }
25245
+ const segments = entry.split(".");
25246
+ if (segments.length === 0 || segments.some((s) => s.length === 0)) {
25247
+ rejected.push({ raw: entry, reason: "empty path segment" });
25248
+ continue;
25249
+ }
25250
+ const wildcardIndices = segments.map((s, i) => s.includes("*") ? i : -1).filter((i) => i >= 0);
25251
+ if (wildcardIndices.length === 0) {
25252
+ patterns.push({ raw: entry, segments, wildcardTail: false });
25253
+ continue;
25254
+ }
25255
+ const lastIndex = segments.length - 1;
25256
+ const onlyTail = wildcardIndices.length === 1 && wildcardIndices[0] === lastIndex && segments[lastIndex] === "*";
25257
+ if (!onlyTail) {
25258
+ rejected.push({ raw: entry, reason: "wildcard must be the final segment" });
25259
+ continue;
25260
+ }
25261
+ if (segments.length === 1) {
25262
+ rejected.push({ raw: entry, reason: "wildcard must have a parent path" });
25263
+ continue;
25264
+ }
25265
+ patterns.push({ raw: entry, segments: segments.slice(0, -1), wildcardTail: true });
25266
+ }
25267
+ return { patterns, rejected };
25268
+ }
25269
+ function matchAny(path3, patterns) {
25270
+ if (patterns.length === 0)
25271
+ return false;
25272
+ const pathSegments = path3.split(".");
25273
+ for (const pat of patterns) {
25274
+ if (pat.wildcardTail) {
25275
+ if (pathSegments.length < pat.segments.length)
25276
+ continue;
25277
+ let ok = true;
25278
+ for (let i = 0;i < pat.segments.length; i++) {
25279
+ if (pat.segments[i] !== pathSegments[i]) {
25280
+ ok = false;
25281
+ break;
25282
+ }
25283
+ }
25284
+ if (ok)
25285
+ return true;
25286
+ } else {
25287
+ if (pat.segments.length !== pathSegments.length)
25288
+ continue;
25289
+ let ok = true;
25290
+ for (let i = 0;i < pat.segments.length; i++) {
25291
+ if (pat.segments[i] !== pathSegments[i]) {
25292
+ ok = false;
25293
+ break;
25294
+ }
25295
+ }
25296
+ if (ok)
25297
+ return true;
25298
+ }
25299
+ }
25300
+ return false;
25301
+ }
25302
+
24830
25303
  // src/core/recovery/types.ts
24831
- var RECOVERY_SCHEMA_VERSION = 1, RECOVERY_CODES, SchemaCacheMissingError, RECOVERY_CODE_METADATA;
25304
+ var RECOVERY_SCHEMA_VERSION = 1, RECOVERY_CODES, MAX_BRANCH_STEPS = 6, MAX_BRANCH_COUNT = 8, SchemaCacheMissingError, RECOVERY_CODE_METADATA;
24832
25305
  var init_types3 = __esm(() => {
24833
25306
  RECOVERY_CODES = [
24834
25307
  "CONFIG_MISSING",
@@ -24929,7 +25402,7 @@ var init_types3 = __esm(() => {
24929
25402
  };
24930
25403
  });
24931
25404
 
24932
- // src/core/recovery/recovery-steps.ts
25405
+ // src/core/recovery/shell-quote.ts
24933
25406
  function shellQuote(value) {
24934
25407
  if (value.length === 0)
24935
25408
  return "''";
@@ -24937,6 +25410,8 @@ function shellQuote(value) {
24937
25410
  return value;
24938
25411
  return `'${value.replace(/'/g, `'\\''`)}'`;
24939
25412
  }
25413
+
25414
+ // src/core/recovery/recovery-steps.ts
24940
25415
  function dryRunStepForWrite(ctx, quotedTable, placeholders) {
24941
25416
  if (!ctx.writeOperation)
24942
25417
  return null;
@@ -25199,6 +25674,7 @@ function draftsForCode(code, ctx) {
25199
25674
  }
25200
25675
  }
25201
25676
  var MAX_RECOVERY_STEPS = 6;
25677
+ var init_recovery_steps = () => {};
25202
25678
 
25203
25679
  // src/core/permission-guard.ts
25204
25680
  var exports_permission_guard = {};
@@ -25821,19 +26297,222 @@ var init_verify_steps = __esm(() => {
25821
26297
  };
25822
26298
  });
25823
26299
 
26300
+ // src/core/recovery/connection-branches.ts
26301
+ function parseDoctorJson(stdoutSummary) {
26302
+ if (!stdoutSummary || stdoutSummary.length === 0)
26303
+ return null;
26304
+ let raw;
26305
+ try {
26306
+ raw = JSON.parse(stdoutSummary);
26307
+ } catch {
26308
+ return null;
26309
+ }
26310
+ if (typeof raw !== "object" || raw === null)
26311
+ return null;
26312
+ const obj = raw;
26313
+ if (!Array.isArray(obj.results))
26314
+ return null;
26315
+ const entries = [];
26316
+ for (const r of obj.results) {
26317
+ if (typeof r !== "object" || r === null)
26318
+ return null;
26319
+ const e = r;
26320
+ if (typeof e.group !== "string" || typeof e.label !== "string" || typeof e.status !== "string" || typeof e.message !== "string") {
26321
+ return null;
26322
+ }
26323
+ entries.push({ group: e.group, label: e.label, status: e.status, message: e.message });
26324
+ }
26325
+ return { results: entries, hasError: Boolean(obj.hasError) };
26326
+ }
26327
+ function anyKeyword(message, keywords) {
26328
+ const m = message.toLowerCase();
26329
+ return keywords.some((kw) => m.includes(kw));
26330
+ }
26331
+ function matchConnectionBranch(prev, opts = {}) {
26332
+ const doctor = parseDoctorJson(prev.stdoutSummary);
26333
+ if (doctor === null) {
26334
+ opts.trace?.({ outcome: "parse-fail", detail: "doctor JSON parse failed" });
26335
+ return null;
26336
+ }
26337
+ if (doctor.results.every((r) => r.status !== "error")) {
26338
+ opts.trace?.({ outcome: "matched", branchId: "doctor-clean" });
26339
+ return "doctor-clean";
26340
+ }
26341
+ if (doctor.results.some((r) => r.status === "error" && CONFIG_LABELS.has(r.label))) {
26342
+ opts.trace?.({ outcome: "matched", branchId: "doctor-config-missing" });
26343
+ return "doctor-config-missing";
26344
+ }
26345
+ const connectionErrors = doctor.results.filter((r) => r.status === "error" && r.label === "Connection");
26346
+ if (connectionErrors.some((r) => anyKeyword(r.message, AUTH_KEYWORDS))) {
26347
+ opts.trace?.({ outcome: "matched", branchId: "doctor-auth-error" });
26348
+ return "doctor-auth-error";
26349
+ }
26350
+ if (connectionErrors.some((r) => anyKeyword(r.message, NETWORK_KEYWORDS))) {
26351
+ opts.trace?.({ outcome: "matched", branchId: "doctor-network-error" });
26352
+ return "doctor-network-error";
26353
+ }
26354
+ opts.trace?.({ outcome: "no-match" });
26355
+ return null;
26356
+ }
26357
+ function renumber(branchId, drafts) {
26358
+ return drafts.map((d, i) => ({ ...d, order: i + 1, branchId }));
26359
+ }
26360
+ function planDoctorClean() {
26361
+ const drafts = [
26362
+ {
26363
+ command: "dbcli inspect --for-agent",
26364
+ rationale: "Re-anchor in the current context and confirm schemaCache.available; if stale, schema --refresh before retry.",
26365
+ risk: "readonly",
26366
+ expects: "JSON snapshot; check connection.online=true, schemaCache.available."
26367
+ }
26368
+ ];
26369
+ return {
26370
+ description: "Doctor reports no errors. Likely a transient failure \u2014 verify baseline state, then retry the original command.",
26371
+ steps: renumber("doctor-clean", drafts)
26372
+ };
26373
+ }
26374
+ function planDoctorConfigMissing() {
26375
+ const drafts = [
26376
+ {
26377
+ command: "dbcli init",
26378
+ rationale: "No usable config; run init to create it.",
26379
+ risk: "write",
26380
+ interactive: true,
26381
+ expects: "Init wizard prompts; new .dbcli written."
26382
+ },
26383
+ {
26384
+ command: "dbcli inspect --no-connect --format json",
26385
+ rationale: "Verify config shape after init.",
26386
+ risk: "readonly",
26387
+ expects: "JSON snapshot with system/permission/blacklist sections populated."
26388
+ }
26389
+ ];
26390
+ return {
26391
+ description: "Doctor flagged a config-level failure. Rebuild config before reattempting connection.",
26392
+ steps: renumber("doctor-config-missing", drafts)
26393
+ };
26394
+ }
26395
+ function planDoctorAuthError() {
26396
+ const drafts = [
26397
+ {
26398
+ command: "dbcli init --force",
26399
+ rationale: "Re-run init focused on credentials; --force overwrites the existing config in place.",
26400
+ risk: "write",
26401
+ interactive: true,
26402
+ expects: "Init wizard accepts new user/password; config rewritten."
26403
+ },
26404
+ {
26405
+ command: "dbcli inspect --no-connect --format json",
26406
+ rationale: "Confirm config now resolves credentials.",
26407
+ risk: "readonly",
26408
+ expects: "JSON snapshot reflecting updated credentials."
26409
+ }
26410
+ ];
26411
+ return {
26412
+ description: "Doctor confirms credentials were rejected. Re-init with --force to overwrite the credential fields.",
26413
+ steps: renumber("doctor-auth-error", drafts)
26414
+ };
26415
+ }
26416
+ function planDoctorNetworkError(ctx) {
26417
+ const drafts = [
26418
+ {
26419
+ command: "dbcli inspect --no-connect --format json",
26420
+ rationale: "Compare expected vs actual host/port without a live probe.",
26421
+ risk: "readonly",
26422
+ expects: "JSON snapshot with connection.name/host/port."
26423
+ }
26424
+ ];
26425
+ if (ctx.connectionName) {
26426
+ drafts.push({
26427
+ command: `dbcli use ${shellQuote(ctx.connectionName)}`,
26428
+ rationale: "Re-select the failing named connection so subsequent commands target it explicitly.",
26429
+ risk: "write",
26430
+ dbWrite: false,
26431
+ expects: "Active connection switched."
26432
+ });
26433
+ }
26434
+ drafts.push({
26435
+ command: "dbcli init --force",
26436
+ rationale: "If addressing is wrong, rewrite host/port via init.",
26437
+ risk: "write",
26438
+ interactive: true,
26439
+ expects: "Init wizard accepts new host/port; config rewritten."
26440
+ });
26441
+ return {
26442
+ description: "Doctor confirms a network-level failure (host / port / DNS / timeout). Inspect expected vs actual addressing, optionally re-select named connection, then re-init host/port.",
26443
+ steps: renumber("doctor-network-error", drafts)
26444
+ };
26445
+ }
26446
+ function buildConnectionBranches(ctx) {
26447
+ const branches = {
26448
+ "doctor-clean": planDoctorClean(),
26449
+ "doctor-config-missing": planDoctorConfigMissing(),
26450
+ "doctor-auth-error": planDoctorAuthError(),
26451
+ "doctor-network-error": planDoctorNetworkError(ctx)
26452
+ };
26453
+ return {
26454
+ branches,
26455
+ branchFork: { after: 1, branchIds: [...CONNECTION_BRANCH_IDS] }
26456
+ };
26457
+ }
26458
+ var CONNECTION_BRANCH_IDS, CONFIG_LABELS, AUTH_KEYWORDS, NETWORK_KEYWORDS, CONNECTION_RESOLVER_KEYWORDS;
26459
+ var init_connection_branches = __esm(() => {
26460
+ CONNECTION_BRANCH_IDS = [
26461
+ "doctor-clean",
26462
+ "doctor-config-missing",
26463
+ "doctor-auth-error",
26464
+ "doctor-network-error"
26465
+ ];
26466
+ CONFIG_LABELS = new Set([
26467
+ "Config exists",
26468
+ "Default connection",
26469
+ "V2 config validation",
26470
+ "Config valid"
26471
+ ]);
26472
+ AUTH_KEYWORDS = [
26473
+ "auth",
26474
+ "password",
26475
+ "credentials",
26476
+ "credential",
26477
+ "permission denied",
26478
+ "access denied",
26479
+ "login"
26480
+ ];
26481
+ NETWORK_KEYWORDS = [
26482
+ "host",
26483
+ "port",
26484
+ "refused",
26485
+ "timeout",
26486
+ "timed out",
26487
+ "unreachable",
26488
+ "enotfound",
26489
+ "econnrefused",
26490
+ "etimedout",
26491
+ "eai_again",
26492
+ "dns"
26493
+ ];
26494
+ CONNECTION_RESOLVER_KEYWORDS = {
26495
+ auth: AUTH_KEYWORDS,
26496
+ network: NETWORK_KEYWORDS,
26497
+ configLabels: [...CONFIG_LABELS]
26498
+ };
26499
+ });
26500
+
25824
26501
  // src/core/recovery/classify.ts
25825
26502
  function classifyError(error, ctx) {
25826
26503
  const recoveryError = errorToRecoveryError(error, ctx);
25827
26504
  const ctxWithDetails = applyDetailsToContext(ctx, recoveryError);
25828
26505
  const recovery = stepsForCode(recoveryError.code, ctxWithDetails);
25829
26506
  const verify = verifyForCode(recoveryError.code, ctxWithDetails);
26507
+ const branchExtras = recoveryError.category === "connection" ? buildConnectionBranches(ctxWithDetails) : null;
25830
26508
  return {
25831
26509
  schemaVersion: RECOVERY_SCHEMA_VERSION,
25832
26510
  generatedAt: new Date().toISOString(),
25833
26511
  ok: false,
25834
26512
  error: recoveryError,
25835
26513
  recovery,
25836
- ...verify !== null ? { verify } : {}
26514
+ ...verify !== null ? { verify } : {},
26515
+ ...branchExtras !== null ? { branches: branchExtras.branches, branchFork: branchExtras.branchFork } : {}
25837
26516
  };
25838
26517
  }
25839
26518
  function errorToRecoveryError(error, ctx) {
@@ -25924,7 +26603,9 @@ var init_classify = __esm(() => {
25924
26603
  init_permission_guard();
25925
26604
  init_blacklist();
25926
26605
  init_types4();
26606
+ init_recovery_steps();
25927
26607
  init_verify_steps();
26608
+ init_connection_branches();
25928
26609
  init_types3();
25929
26610
  });
25930
26611
 
@@ -26950,20 +27631,91 @@ var init_next_step_schema = __esm(() => {
26950
27631
  });
26951
27632
 
26952
27633
  // src/core/recovery/next-step.ts
26953
- function nextStepFromEnvelope(envelope, afterStep, _prevResult) {
27634
+ function resolverFor(code) {
27635
+ switch (code) {
27636
+ case "CONN_REFUSED":
27637
+ case "CONN_AUTH_FAILED":
27638
+ case "CONN_TIMEOUT":
27639
+ case "CONN_HOST_NOT_FOUND":
27640
+ case "CONN_UNKNOWN":
27641
+ return (prev, opts) => matchConnectionBranch(prev, opts);
27642
+ default:
27643
+ return null;
27644
+ }
27645
+ }
27646
+ function assertAfterStep(afterStep) {
26954
27647
  if (!Number.isInteger(afterStep)) {
26955
27648
  throw new RangeError(`afterStep must be an integer (got ${afterStep})`);
26956
27649
  }
26957
27650
  if (afterStep < 1) {
26958
27651
  throw new RangeError(`afterStep must be >= 1 (got ${afterStep})`);
26959
27652
  }
27653
+ }
27654
+ function nextStepFromEnvelope(envelope, afterStep, prevResult, options = {}) {
27655
+ assertAfterStep(afterStep);
27656
+ if (options.branchId !== undefined) {
27657
+ if (!envelope.branches || !envelope.branches[options.branchId]) {
27658
+ const valid = envelope.branches ? Object.keys(envelope.branches).join(", ") : "<none>";
27659
+ throw new RangeError(`Branch '${options.branchId}' not found in envelope (valid: ${valid}).`);
27660
+ }
27661
+ const plan = envelope.branches[options.branchId];
27662
+ if (afterStep > plan.steps.length) {
27663
+ throw new RangeError(`afterStep ${afterStep} exceeds branch '${options.branchId}' length ${plan.steps.length}`);
27664
+ }
27665
+ if (afterStep === plan.steps.length) {
27666
+ return {
27667
+ kind: "done",
27668
+ branchId: options.branchId,
27669
+ cursor: afterStep,
27670
+ totalSteps: plan.steps.length
27671
+ };
27672
+ }
27673
+ return {
27674
+ kind: "step",
27675
+ branchId: options.branchId,
27676
+ step: plan.steps[afterStep],
27677
+ cursor: afterStep + 1,
27678
+ totalSteps: plan.steps.length
27679
+ };
27680
+ }
26960
27681
  if (afterStep > envelope.recovery.length) {
26961
27682
  throw new RangeError(`afterStep ${afterStep} exceeds plan length ${envelope.recovery.length}`);
26962
27683
  }
26963
- if (afterStep === envelope.recovery.length)
26964
- return { kind: "done" };
26965
- return { kind: "step", step: envelope.recovery[afterStep] };
27684
+ if (envelope.branchFork?.after === afterStep && envelope.branches) {
27685
+ const resolver = resolverFor(envelope.error.code);
27686
+ if (resolver !== null) {
27687
+ const traceSink = options.trace ? (line) => {
27688
+ const tail = line.detail ?? line.branchId ?? "";
27689
+ options.trace(`connection-branches: ${line.outcome}${tail ? ` ${tail}` : ""}`);
27690
+ } : undefined;
27691
+ const branchId = resolver(prevResult, { trace: traceSink });
27692
+ if (branchId !== null) {
27693
+ const plan = envelope.branches[branchId];
27694
+ if (plan && plan.steps.length >= 1) {
27695
+ return {
27696
+ kind: "step",
27697
+ branchId,
27698
+ step: plan.steps[0],
27699
+ cursor: 1,
27700
+ totalSteps: plan.steps.length
27701
+ };
27702
+ }
27703
+ }
27704
+ }
27705
+ }
27706
+ if (afterStep === envelope.recovery.length) {
27707
+ return { kind: "done", cursor: afterStep, totalSteps: envelope.recovery.length };
27708
+ }
27709
+ return {
27710
+ kind: "step",
27711
+ step: envelope.recovery[afterStep],
27712
+ cursor: afterStep + 1,
27713
+ totalSteps: envelope.recovery.length
27714
+ };
26966
27715
  }
27716
+ var init_next_step = __esm(() => {
27717
+ init_connection_branches();
27718
+ });
26967
27719
 
26968
27720
  // src/core/recovery/next-render-json.ts
26969
27721
  function renderNextJson(result) {
@@ -26977,6 +27729,12 @@ function renderNextMarkdown(result) {
26977
27729
  lines.push("");
26978
27730
  lines.push(`*Source:* \`${result.source.kind}\` \u2192 \`${result.source.path}\``);
26979
27731
  lines.push(`errorCode: \`${result.errorCode}\``);
27732
+ if (result.branchId !== undefined) {
27733
+ lines.push(`**Branch:** \`${result.branchId}\``);
27734
+ if (result.branchDescription !== undefined) {
27735
+ lines.push(`**Branch description:** ${result.branchDescription}`);
27736
+ }
27737
+ }
26980
27738
  lines.push("");
26981
27739
  if (result.kind === "step" && result.step) {
26982
27740
  lines.push(`## Next step (${result.cursor} of ${result.totalSteps})`);
@@ -27035,12 +27793,15 @@ __export(exports_recovery, {
27035
27793
  RECOVERY_CODES: () => RECOVERY_CODES,
27036
27794
  NEXT_SCHEMA_VERSION: () => NEXT_SCHEMA_VERSION,
27037
27795
  MAX_RECOVERY_STEPS: () => MAX_RECOVERY_STEPS,
27796
+ MAX_BRANCH_STEPS: () => MAX_BRANCH_STEPS,
27797
+ MAX_BRANCH_COUNT: () => MAX_BRANCH_COUNT,
27038
27798
  LAST_ENVELOPE_PATH: () => LAST_ENVELOPE_PATH,
27039
27799
  EXIT_CODE: () => EXIT_CODE,
27040
27800
  APPLY_SCHEMA_VERSION: () => APPLY_SCHEMA_VERSION
27041
27801
  });
27042
27802
  var init_recovery = __esm(() => {
27043
27803
  init_types3();
27804
+ init_recovery_steps();
27044
27805
  init_classify();
27045
27806
  init_render_markdown();
27046
27807
  init_emit();
@@ -27050,6 +27811,7 @@ var init_recovery = __esm(() => {
27050
27811
  init_verify_steps();
27051
27812
  init_apply_verify();
27052
27813
  init_next_step_schema();
27814
+ init_next_step();
27053
27815
  });
27054
27816
 
27055
27817
  // src/utils/package-root.ts
@@ -27084,6 +27846,38 @@ var init_package_root = __esm(() => {
27084
27846
  HERE = import.meta.dir;
27085
27847
  });
27086
27848
 
27849
+ // src/formatters/html-formatter.ts
27850
+ async function generateHtmlReport(payload) {
27851
+ const templatePath = packageAssetPath("ui-template.html");
27852
+ const templateFile = Bun.file(templatePath);
27853
+ if (!await templateFile.exists()) {
27854
+ throw new Error(`UI template not found at ${templatePath}. Please run 'bun run build' first.`);
27855
+ }
27856
+ let html = await templateFile.text();
27857
+ const jsonPayload = JSON.stringify(payload).replace(/</g, "\\u003c");
27858
+ const injection = `window.__DBCLI_PAYLOAD__ = ${jsonPayload};`;
27859
+ html = html.replace("/*DBCLI_PAYLOAD*/", () => injection);
27860
+ return html;
27861
+ }
27862
+ var init_html_formatter = __esm(() => {
27863
+ init_package_root();
27864
+ });
27865
+
27866
+ // src/utils/opener.ts
27867
+ import open2 from "open";
27868
+ async function openInBrowser(target) {
27869
+ if (process.env.DBCLI_NO_OPEN === "1" || false) {
27870
+ console.log(`[Opener] DBCLI_NO_OPEN is set. Skipping browser launch for: ${target}`);
27871
+ return;
27872
+ }
27873
+ try {
27874
+ await open2(target);
27875
+ } catch (err) {
27876
+ console.error(`[Opener] Failed to open ${target}:`, err);
27877
+ }
27878
+ }
27879
+ var init_opener = () => {};
27880
+
27087
27881
  // src/utils/levenshtein-distance.ts
27088
27882
  function levenshteinDistance(a, b) {
27089
27883
  const lenA = a.length;
@@ -27127,6 +27921,59 @@ function levenshteinDistance(a, b) {
27127
27921
  return dp[lenB]?.[lenA] ?? 0;
27128
27922
  }
27129
27923
 
27924
+ // src/core/mongo/field-masker.ts
27925
+ function maskMongoRows(rows, collection, blacklist) {
27926
+ const columns = blacklist.columns ?? {};
27927
+ const raw = columns[collection] ?? findCaseInsensitive(columns, collection);
27928
+ if (!raw || raw.length === 0)
27929
+ return rows;
27930
+ const { patterns } = compilePatterns(raw);
27931
+ if (patterns.length === 0)
27932
+ return rows;
27933
+ const idAffected = patterns.some((p) => p.segments.length === 1 && p.segments[0] === "_id" && !p.wildcardTail);
27934
+ if (idAffected) {
27935
+ console.error(`[blacklist] collection '${collection}' blacklists '_id'; read paths still expose _id to preserve document references.`);
27936
+ }
27937
+ return rows.map((row) => maskRecord(row, "", patterns));
27938
+ }
27939
+ function maskRecord(obj, prefix, patterns) {
27940
+ const out = {};
27941
+ for (const [key, value] of Object.entries(obj)) {
27942
+ if (key === "_id" && prefix === "") {
27943
+ out[key] = value;
27944
+ continue;
27945
+ }
27946
+ const path4 = prefix === "" ? key : `${prefix}.${key}`;
27947
+ if (matchAny(path4, patterns)) {
27948
+ out[key] = REDACTED;
27949
+ continue;
27950
+ }
27951
+ out[key] = maskValue(value, path4, patterns);
27952
+ }
27953
+ return out;
27954
+ }
27955
+ function maskValue(value, path4, patterns) {
27956
+ if (value === null || value === undefined)
27957
+ return value;
27958
+ if (Array.isArray(value)) {
27959
+ return value.map((item) => item && typeof item === "object" && !Array.isArray(item) ? maskRecord(item, path4, patterns) : item);
27960
+ }
27961
+ if (typeof value === "object") {
27962
+ return maskRecord(value, path4, patterns);
27963
+ }
27964
+ return value;
27965
+ }
27966
+ function findCaseInsensitive(columns, name) {
27967
+ const target = name.toLowerCase();
27968
+ for (const [k, v] of Object.entries(columns)) {
27969
+ if (k.toLowerCase() === target)
27970
+ return v;
27971
+ }
27972
+ return;
27973
+ }
27974
+ var REDACTED = "[REDACTED]";
27975
+ var init_field_masker = () => {};
27976
+
27130
27977
  // src/commands/query-size-guard.ts
27131
27978
  var exports_query_size_guard = {};
27132
27979
  __export(exports_query_size_guard, {
@@ -27593,6 +28440,86 @@ var init_redis = __esm(() => {
27593
28440
  }
27594
28441
  };
27595
28442
  });
28443
+
28444
+ // src/core/saved-queries/strategies/mongodb.ts
28445
+ function interpolate(body, meta, params) {
28446
+ const declared = new Map(meta.params.map((p) => [p.name, p]));
28447
+ return body.replace(TEMPLATE_RE, (_match, name) => {
28448
+ const spec = declared.get(name);
28449
+ let value = params[name];
28450
+ if (value === undefined) {
28451
+ if (spec?.default !== undefined) {
28452
+ value = spec.default;
28453
+ } else if (spec?.required) {
28454
+ throw new SavedQueryError(`Missing required param '${name}' for snippet '${meta.key}'`, "PARAM_MISSING");
28455
+ } else {
28456
+ value = null;
28457
+ }
28458
+ }
28459
+ return JSON.stringify(value);
28460
+ });
28461
+ }
28462
+ function stripTemplatesForValidation(body) {
28463
+ return body.replace(TEMPLATE_RE, "0");
28464
+ }
28465
+ function describe(v) {
28466
+ if (v === null)
28467
+ return "null";
28468
+ if (Array.isArray(v))
28469
+ return "array";
28470
+ return typeof v;
28471
+ }
28472
+ var TEMPLATE_RE, mongoStrategy;
28473
+ var init_mongodb = __esm(() => {
28474
+ init_types4();
28475
+ TEMPLATE_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
28476
+ mongoStrategy = {
28477
+ family: "mongo",
28478
+ validateBody(body, meta, file) {
28479
+ if (!meta.operation) {
28480
+ throw new SavedQueryError(`MongoDB snippet '${meta.key}' must declare 'operation: find' or 'operation: aggregate'`, "MONGO_INVALID_BODY", file);
28481
+ }
28482
+ const probe = stripTemplatesForValidation(body.trim());
28483
+ let parsed;
28484
+ try {
28485
+ parsed = JSON.parse(probe);
28486
+ } catch (e) {
28487
+ throw new SavedQueryError(`MongoDB snippet '${meta.key}' body is not valid JSON: ${e.message}`, "MONGO_INVALID_BODY", file);
28488
+ }
28489
+ if (meta.operation === "find" && (Array.isArray(parsed) || typeof parsed !== "object" || parsed === null)) {
28490
+ throw new SavedQueryError(`MongoDB find snippet '${meta.key}' body must be a JSON object (got ${describe(parsed)})`, "MONGO_INVALID_BODY", file);
28491
+ }
28492
+ if (meta.operation === "aggregate" && !Array.isArray(parsed)) {
28493
+ throw new SavedQueryError(`MongoDB aggregate snippet '${meta.key}' body must be a JSON array (got ${describe(parsed)})`, "MONGO_INVALID_BODY", file);
28494
+ }
28495
+ },
28496
+ prepare(snippet, params, opts) {
28497
+ if (opts.engine !== "mongodb") {
28498
+ throw new SavedQueryError(`MongoDB snippet '${snippet.meta.key}' cannot run on ${opts.engine} connection`, "ENGINE_MISMATCH", snippet.file);
28499
+ }
28500
+ const collection = params.__collection ?? snippet.meta.target;
28501
+ if (!collection || collection.trim() === "") {
28502
+ throw new SavedQueryError(`MongoDB snippet '${snippet.meta.key}' needs a target collection (frontmatter 'target' or CLI --collection).`, "MONGO_MISSING_COLLECTION", snippet.file);
28503
+ }
28504
+ const interpolated = interpolate(snippet.sqlBody, snippet.meta, params);
28505
+ let parsed;
28506
+ try {
28507
+ parsed = JSON.parse(interpolated);
28508
+ } catch (e) {
28509
+ throw new SavedQueryError(`MongoDB snippet '${snippet.meta.key}' body became invalid JSON after parameter substitution: ${e.message}`, "MONGO_INVALID_BODY", snippet.file);
28510
+ }
28511
+ return {
28512
+ driver: { sql: JSON.stringify(parsed), values: [] },
28513
+ rewrittenBody: JSON.stringify(parsed, null, 2),
28514
+ warnings: [],
28515
+ execHints: {
28516
+ collection,
28517
+ mongoOperation: snippet.meta.operation
28518
+ }
28519
+ };
28520
+ }
28521
+ };
28522
+ });
27596
28523
  // src/core/saved-queries/strategies/index.ts
27597
28524
  function engineFamily(engine) {
27598
28525
  if (engine === "postgres" || engine === "mysql")
@@ -27601,6 +28528,8 @@ function engineFamily(engine) {
27601
28528
  return "es";
27602
28529
  if (engine === "redis")
27603
28530
  return "redis";
28531
+ if (engine === "mongodb")
28532
+ return "mongo";
27604
28533
  throw new Error(`Unknown engine: ${engine}`);
27605
28534
  }
27606
28535
  function getStrategy(family) {
@@ -27611,6 +28540,8 @@ function getStrategy(family) {
27611
28540
  return esStrategy;
27612
28541
  case "redis":
27613
28542
  return redisStrategy;
28543
+ case "mongo":
28544
+ return mongoStrategy;
27614
28545
  default:
27615
28546
  throw new Error(`No strategy registered for family: ${family}`);
27616
28547
  }
@@ -27619,6 +28550,7 @@ var init_strategies = __esm(() => {
27619
28550
  init_sql();
27620
28551
  init_elasticsearch();
27621
28552
  init_redis();
28553
+ init_mongodb();
27622
28554
  });
27623
28555
 
27624
28556
  // src/core/saved-queries/yaml-mini.ts
@@ -27753,6 +28685,8 @@ function familyOf(engine) {
27753
28685
  return "sql";
27754
28686
  if (engine === "elasticsearch")
27755
28687
  return "es";
28688
+ if (engine === "mongodb")
28689
+ return "mongo";
27756
28690
  return "redis";
27757
28691
  }
27758
28692
  function parseSavedQuery(input) {
@@ -27826,6 +28760,8 @@ function parseFrontmatter(yaml, input) {
27826
28760
  const index = typeof raw.index === "string" ? raw.index : undefined;
27827
28761
  const intent = normaliseIntent(raw.intent, input);
27828
28762
  const visual = normaliseVisual(raw.visual);
28763
+ const target = typeof raw.target === "string" ? raw.target : undefined;
28764
+ const operation = raw.operation === "find" || raw.operation === "aggregate" ? raw.operation : undefined;
27829
28765
  return {
27830
28766
  meta: {
27831
28767
  name: typeof raw.name === "string" ? raw.name : "",
@@ -27836,7 +28772,9 @@ function parseFrontmatter(yaml, input) {
27836
28772
  params,
27837
28773
  tags,
27838
28774
  intent,
27839
- visual
28775
+ visual,
28776
+ target,
28777
+ operation
27840
28778
  },
27841
28779
  warnings
27842
28780
  };
@@ -28033,7 +28971,7 @@ var init_parser = __esm(() => {
28033
28971
  init_types4();
28034
28972
  MAX_BYTES = 64 * 1024;
28035
28973
  VALID_TYPES = ["int", "string", "float", "bool", "date", "datetime"];
28036
- VALID_ENGINES = ["postgres", "mysql", "elasticsearch", "redis"];
28974
+ VALID_ENGINES = ["postgres", "mysql", "elasticsearch", "redis", "mongodb"];
28037
28975
  INTENT_RE = /^[a-z][a-z0-9.-]*$/;
28038
28976
  });
28039
28977
 
@@ -28119,7 +29057,13 @@ async function collectFiles(root) {
28119
29057
  var ENGINE_SUFFIXES;
28120
29058
  var init_loader = __esm(() => {
28121
29059
  init_parser();
28122
- ENGINE_SUFFIXES = ["postgres", "mysql", "elasticsearch", "redis"];
29060
+ ENGINE_SUFFIXES = [
29061
+ "postgres",
29062
+ "mysql",
29063
+ "elasticsearch",
29064
+ "redis",
29065
+ "mongodb"
29066
+ ];
28123
29067
  });
28124
29068
 
28125
29069
  // src/core/saved-queries/resolver.ts
@@ -28159,9 +29103,6 @@ var init_resolver = __esm(() => {
28159
29103
 
28160
29104
  // src/core/saved-queries/runner.ts
28161
29105
  function prepareExecution(snippet, opts, cliParams, fileParams) {
28162
- if (opts.engine === "mongodb") {
28163
- throw new SavedQueryError(`Saved queries do not support MongoDB connections`, "ENGINE_MISMATCH", snippet.query.file);
28164
- }
28165
29106
  const declared = snippet.query.meta.engine;
28166
29107
  if (declared && declared.length > 0) {
28167
29108
  const declaredFamily = engineFamily(declared[0]);
@@ -28239,6 +29180,77 @@ var init_saved_queries = __esm(() => {
28239
29180
  init_snippet_paths();
28240
29181
  });
28241
29182
 
29183
+ // src/commands/q-mongo.ts
29184
+ var exports_q_mongo = {};
29185
+ __export(exports_q_mongo, {
29186
+ qMongoBranch: () => qMongoBranch
29187
+ });
29188
+ import { tmpdir as tmpdir2 } from "os";
29189
+ import { join as join18 } from "path";
29190
+ async function qMongoBranch(snippet, prepared, options, config) {
29191
+ const collection = options.collection ?? prepared.execHints?.collection;
29192
+ if (!collection) {
29193
+ throw new Error(`MongoDB snippet '${snippet.query.meta.key}' resolved without a target collection`);
29194
+ }
29195
+ const blacklistManager = new BlacklistManager(config);
29196
+ const blacklistValidator = new BlacklistValidator(blacklistManager);
29197
+ blacklistValidator.checkTableBlacklist("SELECT", collection);
29198
+ if (options.dryRun) {
29199
+ console.log(`Dry-run preview (no execution):`);
29200
+ console.log(`Collection: ${collection}`);
29201
+ console.log(`Operation: ${prepared.execHints?.mongoOperation ?? "find"}`);
29202
+ console.log(prepared.driver.sql);
29203
+ return;
29204
+ }
29205
+ const adapter = AdapterFactory.createMongoDBAdapter(config.connection);
29206
+ await adapter.connect();
29207
+ try {
29208
+ const start = performance.now();
29209
+ const result = await adapter.execute(prepared.driver.sql, [collection]);
29210
+ const executionTimeMs = Math.round(performance.now() - start);
29211
+ const blacklistCfg = config.blacklist ?? { tables: [], columns: {} };
29212
+ const masked = maskMongoRows(result.rows, collection, blacklistCfg);
29213
+ if (options.ui || options.format === "html") {
29214
+ const html = await generateHtmlReport({ meta: snippet.query.meta, rows: masked });
29215
+ if (options.ui) {
29216
+ const tempPath = join18(tmpdir2(), `dbcli-report-${Date.now()}.html`);
29217
+ await Bun.write(tempPath, html);
29218
+ await openInBrowser(tempPath);
29219
+ } else {
29220
+ console.log(html);
29221
+ }
29222
+ } else {
29223
+ const columnNames = masked[0] ? Object.keys(masked[0]) : [];
29224
+ const formatter = new QueryResultFormatter;
29225
+ console.log(formatter.format({
29226
+ rows: masked,
29227
+ rowCount: masked.length,
29228
+ columnNames,
29229
+ columnTypes: [],
29230
+ executionTimeMs,
29231
+ metadata: { statement: "SELECT", affectedRows: 0 }
29232
+ }, { format: options.format ?? "table" }));
29233
+ }
29234
+ await writeAuditEntry(config, "q", options, {
29235
+ success: true,
29236
+ target: collection,
29237
+ sql: prepared.driver.sql,
29238
+ metadata: { rows_affected: masked.length, execution_ms: executionTimeMs }
29239
+ });
29240
+ } finally {
29241
+ await adapter.disconnect();
29242
+ }
29243
+ }
29244
+ var init_q_mongo = __esm(() => {
29245
+ init_adapters();
29246
+ init_blacklist_validator();
29247
+ init_field_masker();
29248
+ init_formatters();
29249
+ init_html_formatter();
29250
+ init_opener();
29251
+ init_integration_helper();
29252
+ });
29253
+
28242
29254
  // src/core/saved-queries/fold.ts
28243
29255
  function foldVariants(key, variants) {
28244
29256
  const sources = unique(variants.map((v) => v.query.source)).sort();
@@ -28394,7 +29406,7 @@ __export(exports_queries_import, {
28394
29406
  queriesImport: () => queriesImport
28395
29407
  });
28396
29408
  import { stat as stat5, mkdir as mkdir8, copyFile as copyFile2 } from "fs/promises";
28397
- import { basename as basename3, join as join19, extname } from "path";
29409
+ import { basename as basename3, join as join20, extname } from "path";
28398
29410
  async function queriesImport(filePath, options = {}) {
28399
29411
  const cwd = options.cwd ?? process.cwd();
28400
29412
  if (extname(filePath) !== ".sql") {
@@ -28405,9 +29417,9 @@ async function queriesImport(filePath, options = {}) {
28405
29417
  const baseName = options.as ? options.as.replace(/^@/, "") : basename3(filePath, ".sql").replace(/\.(postgres|mysql)$/, "");
28406
29418
  const key = "@" + baseName;
28407
29419
  parseSavedQuery({ key, file: filePath, source: "local", text: text2 });
28408
- const targetDir = join19(cwd, ".dbcli/queries");
29420
+ const targetDir = join20(cwd, ".dbcli/queries");
28409
29421
  await mkdir8(targetDir, { recursive: true });
28410
- const target = join19(targetDir, basename3(filePath));
29422
+ const target = join20(targetDir, basename3(filePath));
28411
29423
  if (await Bun.file(target).exists()) {
28412
29424
  if (!options.force) {
28413
29425
  const ok = await dist_default6({ message: `Overwrite ${target}?`, default: false });
@@ -28532,7 +29544,7 @@ var init_collect_snippets = __esm(() => {
28532
29544
  });
28533
29545
 
28534
29546
  // src/core/inspect/collect-schema-cache.ts
28535
- import { join as join23 } from "path";
29547
+ import { join as join24 } from "path";
28536
29548
  async function collectSchemaCache(opts) {
28537
29549
  const warnings = [];
28538
29550
  if (opts.system && !SQL_SYSTEMS2.includes(opts.system)) {
@@ -28542,7 +29554,7 @@ async function collectSchemaCache(opts) {
28542
29554
  };
28543
29555
  }
28544
29556
  const root = resolveSchemaPath(opts.dbcliPath, opts.connectionName);
28545
- const indexPath = join23(root, "index.json");
29557
+ const indexPath = join24(root, "index.json");
28546
29558
  const file = Bun.file(indexPath);
28547
29559
  if (!await file.exists()) {
28548
29560
  return { section: { available: false }, warnings };
@@ -28654,7 +29666,7 @@ var init_suggest_commands = __esm(() => {
28654
29666
  });
28655
29667
 
28656
29668
  // src/core/inspect/collector.ts
28657
- import { join as join24 } from "path";
29669
+ import { join as join25 } from "path";
28658
29670
  async function collectInspect(opts) {
28659
29671
  const warnings = [];
28660
29672
  let config = null;
@@ -28730,7 +29742,7 @@ async function collectInspect(opts) {
28730
29742
  return { ...snapWithoutSuggestions, suggestedCommands, warnings };
28731
29743
  }
28732
29744
  async function hasConfig(configPath) {
28733
- if (await Bun.file(join24(configPath, "config.json")).exists())
29745
+ if (await Bun.file(join25(configPath, "config.json")).exists())
28734
29746
  return true;
28735
29747
  if (await Bun.file(configPath).exists()) {
28736
29748
  const stat6 = await Bun.file(configPath).stat().catch(() => null);
@@ -28844,8 +29856,8 @@ function renderMarkdown2(snap, options = {}) {
28844
29856
  lines.push("");
28845
29857
  if (snap.warnings.length > 0) {
28846
29858
  lines.push("## Warnings");
28847
- for (const w of snap.warnings)
28848
- lines.push(`- ${w}`);
29859
+ for (const w2 of snap.warnings)
29860
+ lines.push(`- ${w2}`);
28849
29861
  lines.push("");
28850
29862
  }
28851
29863
  return lines.join(`
@@ -29019,7 +30031,7 @@ var {
29019
30031
  // package.json
29020
30032
  var package_default = {
29021
30033
  name: "@carllee1983/dbcli",
29022
- version: "1.20.1",
30034
+ version: "1.20.2",
29023
30035
  description: "Database CLI for AI agents",
29024
30036
  type: "module",
29025
30037
  publishConfig: {
@@ -29079,12 +30091,12 @@ var package_default = {
29079
30091
  dependencies: {
29080
30092
  "cli-table3": "^0.6.5",
29081
30093
  commander: "13.0.0",
29082
- "lru-cache": "^11.3.6",
29083
- "lucide-react": "^1.14.0",
30094
+ "lru-cache": "^11.4.0",
30095
+ "lucide-react": "^1.16.0",
29084
30096
  mongodb: "^7.2.0",
29085
30097
  mysql2: "^3.22.3",
29086
30098
  open: "^11.0.0",
29087
- pg: "^8.20.0",
30099
+ pg: "^8.21.0",
29088
30100
  picocolors: "^1.1.1",
29089
30101
  react: "^19.2.6",
29090
30102
  "react-dom": "^19.2.6",
@@ -29094,19 +30106,22 @@ var package_default = {
29094
30106
  devDependencies: {
29095
30107
  "@eslint/js": "^9.39.4",
29096
30108
  "@happy-dom/global-registrator": "^20.9.0",
29097
- "@inquirer/prompts": "^8.4.2",
30109
+ "@inquirer/prompts": "^8.4.3",
29098
30110
  "@testing-library/react": "^16.3.2",
29099
30111
  "@types/bun": "latest",
29100
30112
  "@types/react": "^19.2.14",
29101
30113
  "@types/react-dom": "^19.2.3",
29102
30114
  autoprefixer: "^10.5.0",
29103
- eslint: "^10.3.0",
30115
+ eslint: "^10.4.0",
29104
30116
  "happy-dom": "^20.9.0",
29105
30117
  postcss: "^8.5.14",
29106
30118
  prettier: "^3.8.3",
29107
30119
  tailwindcss: "3.4.1",
29108
30120
  typescript: "^5.9.3",
29109
- "typescript-eslint": "^8.59.2"
30121
+ "typescript-eslint": "^8.59.4"
30122
+ },
30123
+ overrides: {
30124
+ "brace-expansion": "^5.0.6"
29110
30125
  }
29111
30126
  };
29112
30127
 
@@ -29884,191 +30899,7 @@ async function handleMongoDBInit(ctx) {
29884
30899
  // src/commands/list.ts
29885
30900
  init_message_loader();
29886
30901
  init_adapters();
29887
-
29888
- // src/formatters/table-formatter.ts
29889
- init_colors();
29890
- var Table = require_table();
29891
-
29892
- class TableFormatter {
29893
- format(columns) {
29894
- const table = new Table({
29895
- head: ["Column", "Type", "Nullable", "Default", "Key"].map((h) => colors.bold(h)),
29896
- style: { compact: false, "padding-left": 1, "padding-right": 1 },
29897
- colWidths: [25, 25, 10, 25, 20]
29898
- });
29899
- columns.forEach((col) => {
29900
- let keyType = "";
29901
- if (col.primaryKey) {
29902
- keyType = "PK";
29903
- } else if (col.foreignKey) {
29904
- keyType = `FK \u2192 ${col.foreignKey.table}.${col.foreignKey.column}`;
29905
- }
29906
- table.push([col.name, col.type, col.nullable ? "YES" : "NO", col.default || "NULL", keyType]);
29907
- });
29908
- return table.toString();
29909
- }
29910
- }
29911
-
29912
- class TableListFormatter {
29913
- format(tables) {
29914
- const table = new Table({
29915
- head: ["Table", "Columns", "Rows", "Engine"].map((h) => colors.bold(h)),
29916
- style: { compact: false, "padding-left": 1, "padding-right": 1 },
29917
- colWidths: [30, 12, 15, 15]
29918
- });
29919
- tables.forEach((t2) => {
29920
- table.push([
29921
- t2.name,
29922
- (t2.columnCount ?? t2.columns.length).toString(),
29923
- (t2.rowCount ?? "?").toString(),
29924
- t2.engine || "N/A"
29925
- ]);
29926
- });
29927
- return table.toString();
29928
- }
29929
- }
29930
- // src/formatters/json-formatter.ts
29931
- init_size_category();
29932
- class TableSchemaJSONFormatter {
29933
- format(table, options) {
29934
- const spacing = options?.compact ? undefined : 2;
29935
- const rowCount = table.estimatedRowCount ?? table.rowCount;
29936
- const output = {
29937
- name: table.name,
29938
- estimatedRowCount: rowCount ?? null,
29939
- sizeCategory: rowCount !== undefined && rowCount !== null ? getSizeCategory(rowCount) : null,
29940
- tableType: table.tableType ?? "table",
29941
- columns: table.columns,
29942
- primaryKey: table.primaryKey || [],
29943
- foreignKeys: table.foreignKeys || [],
29944
- indexes: table.indexes || [],
29945
- metadata: {
29946
- rowCount: table.rowCount,
29947
- engine: table.engine
29948
- }
29949
- };
29950
- return JSON.stringify(output, null, spacing);
29951
- }
29952
- }
29953
- // src/formatters/query-result-formatter.ts
29954
- var Table2 = require_table();
29955
-
29956
- class QueryResultFormatter {
29957
- format(result, options) {
29958
- const format = options?.format || "table";
29959
- switch (format) {
29960
- case "json":
29961
- return this.formatJSON(result, options?.compact);
29962
- case "csv":
29963
- return this.formatCSV(result);
29964
- case "table":
29965
- default:
29966
- return this.formatTable(result);
29967
- }
29968
- }
29969
- formatTable(result) {
29970
- if (result.rows.length === 0) {
29971
- return this.formatEmptyTable(result);
29972
- }
29973
- const table = new Table2({
29974
- head: result.columnNames,
29975
- style: { compact: false, "padding-left": 1, "padding-right": 1 }
29976
- });
29977
- result.rows.forEach((row) => {
29978
- table.push(result.columnNames.map((col) => this.cellToString(row[col])));
29979
- });
29980
- let output = table.toString();
29981
- const footerLines = [];
29982
- footerLines.push(`Rows: ${result.rowCount}`);
29983
- if (result.executionTimeMs !== undefined) {
29984
- footerLines.push(`Execution time: ${result.executionTimeMs}ms`);
29985
- }
29986
- if (footerLines.length > 0) {
29987
- output += `
29988
- ` + footerLines.join(" | ");
29989
- }
29990
- if (result.metadata?.securityNotification) {
29991
- output += `
29992
- ` + result.metadata.securityNotification;
29993
- }
29994
- return output;
29995
- }
29996
- formatEmptyTable(result) {
29997
- const table = new Table2({
29998
- head: result.columnNames,
29999
- style: { compact: false, "padding-left": 1, "padding-right": 1 }
30000
- });
30001
- let output = table.toString();
30002
- const footerLines = [];
30003
- footerLines.push(`Rows: ${result.rowCount}`);
30004
- if (result.executionTimeMs !== undefined) {
30005
- footerLines.push(`Execution time: ${result.executionTimeMs}ms`);
30006
- }
30007
- if (footerLines.length > 0) {
30008
- output += `
30009
- ` + footerLines.join(" | ");
30010
- }
30011
- if (result.metadata?.securityNotification) {
30012
- output += `
30013
- ` + result.metadata.securityNotification;
30014
- }
30015
- return output;
30016
- }
30017
- formatJSON(result, compact) {
30018
- const spacing = compact ? undefined : 2;
30019
- const output = {
30020
- rows: result.rows,
30021
- rowCount: result.rowCount,
30022
- columnNames: result.columnNames,
30023
- columnTypes: result.columnTypes,
30024
- executionTimeMs: result.executionTimeMs,
30025
- metadata: result.metadata
30026
- };
30027
- return JSON.stringify(output, null, spacing);
30028
- }
30029
- formatCSV(result) {
30030
- if (result.rows.length === 0) {
30031
- let csvOutput = result.columnNames.map((name) => this.escapeCSVField(name)).join(",");
30032
- if (result.metadata?.securityNotification) {
30033
- csvOutput += `
30034
- # ` + result.metadata.securityNotification;
30035
- }
30036
- return csvOutput;
30037
- }
30038
- const lines = [];
30039
- lines.push(result.columnNames.map((name) => this.escapeCSVField(name)).join(","));
30040
- result.rows.forEach((row) => {
30041
- const csvRow = result.columnNames.map((col) => this.escapeCSVField(row[col])).join(",");
30042
- lines.push(csvRow);
30043
- });
30044
- if (result.metadata?.securityNotification) {
30045
- lines.push(`# ${result.metadata.securityNotification}`);
30046
- }
30047
- return lines.join(`
30048
- `);
30049
- }
30050
- escapeCSVField(value) {
30051
- if (value === null || value === undefined) {
30052
- return "";
30053
- }
30054
- const str = String(value);
30055
- if (str.includes(",") || str.includes('"') || str.includes(`
30056
- `)) {
30057
- return `"${str.replace(/"/g, '""')}"`;
30058
- }
30059
- return str;
30060
- }
30061
- cellToString(value) {
30062
- if (value === null || value === undefined) {
30063
- return "";
30064
- }
30065
- if (typeof value === "object") {
30066
- return JSON.stringify(value);
30067
- }
30068
- return String(value);
30069
- }
30070
- }
30071
- // src/commands/list.ts
30902
+ init_formatters();
30072
30903
  init_config();
30073
30904
  init_validation();
30074
30905
  function requireSqlConnection(connection) {
@@ -30222,6 +31053,7 @@ async function elasticsearchListBranch(config, format, includeSystem) {
30222
31053
  import crypto from "crypto";
30223
31054
  init_message_loader();
30224
31055
  init_adapters();
31056
+ init_formatters();
30225
31057
  init_config();
30226
31058
  init_config_v2();
30227
31059
  init_config_binding();
@@ -30598,166 +31430,9 @@ class ColumnIndexBuilder {
30598
31430
  };
30599
31431
  }
30600
31432
  }
30601
- // src/core/blacklist-manager.ts
30602
- class BlacklistManager {
30603
- config;
30604
- state;
30605
- overrideEnabled;
30606
- constructor(config, overrideEnvValue) {
30607
- this.config = config;
30608
- this.overrideEnabled = (overrideEnvValue ?? Bun.env.DBCLI_OVERRIDE_BLACKLIST ?? "") === "true";
30609
- this.state = this.loadBlacklist();
30610
- }
30611
- loadBlacklist() {
30612
- const tables = new Set;
30613
- const columns = new Map;
30614
- const blacklistConfig = this.config.blacklist;
30615
- if (!blacklistConfig) {
30616
- return { tables, columns };
30617
- }
30618
- if (Array.isArray(blacklistConfig.tables)) {
30619
- for (const tableName of blacklistConfig.tables) {
30620
- if (typeof tableName === "string") {
30621
- tables.add(tableName.toLowerCase());
30622
- } else {
30623
- console.warn(`[BlacklistManager] Invalid table name in blacklist config: ${JSON.stringify(tableName)}`);
30624
- }
30625
- }
30626
- } else if (blacklistConfig.tables !== undefined) {
30627
- console.warn("[BlacklistManager] blacklist.tables must be an array, ignoring");
30628
- }
30629
- if (blacklistConfig.columns && typeof blacklistConfig.columns === "object" && !Array.isArray(blacklistConfig.columns)) {
30630
- for (const [tableName, cols] of Object.entries(blacklistConfig.columns)) {
30631
- if (typeof tableName !== "string") {
30632
- console.warn(`[BlacklistManager] Invalid table name key in columns config: ${JSON.stringify(tableName)}`);
30633
- continue;
30634
- }
30635
- if (!Array.isArray(cols)) {
30636
- console.warn(`[BlacklistManager] blacklist.columns["${tableName}"] must be an array, ignoring`);
30637
- continue;
30638
- }
30639
- const columnSet = new Set;
30640
- for (const col of cols) {
30641
- if (typeof col === "string") {
30642
- columnSet.add(col);
30643
- } else {
30644
- console.warn(`[BlacklistManager] Invalid column name in blacklist.columns["${tableName}"]: ${JSON.stringify(col)}`);
30645
- }
30646
- }
30647
- if (columnSet.size > 0) {
30648
- columns.set(tableName.toLowerCase(), columnSet);
30649
- }
30650
- }
30651
- } else if (blacklistConfig.columns !== undefined) {
30652
- console.warn("[BlacklistManager] blacklist.columns must be an object, ignoring");
30653
- }
30654
- return { tables, columns };
30655
- }
30656
- isTableBlacklisted(tableName) {
30657
- return this.state.tables.has(tableName.toLowerCase());
30658
- }
30659
- isColumnBlacklisted(tableName, columnName) {
30660
- const columnSet = this.state.columns.get(tableName.toLowerCase());
30661
- if (!columnSet) {
30662
- return false;
30663
- }
30664
- return columnSet.has(columnName);
30665
- }
30666
- getBlacklistedColumns(tableName) {
30667
- const columnSet = this.state.columns.get(tableName.toLowerCase());
30668
- if (!columnSet) {
30669
- return [];
30670
- }
30671
- return Array.from(columnSet);
30672
- }
30673
- canOverrideBlacklist() {
30674
- return this.overrideEnabled;
30675
- }
30676
- getState() {
30677
- return this.state;
30678
- }
30679
- }
30680
- // src/core/blacklist-validator.ts
30681
- init_blacklist();
30682
- init_message_loader();
30683
-
30684
- class BlacklistValidator {
30685
- manager;
30686
- constructor(manager) {
30687
- this.manager = manager;
30688
- }
30689
- checkTableBlacklist(operation, tableName, _tableList = []) {
30690
- if (this.manager.canOverrideBlacklist()) {
30691
- const message = t_vars("warnings.blacklist_override_used", {
30692
- operation,
30693
- table: tableName
30694
- });
30695
- console.error(message);
30696
- return;
30697
- }
30698
- if (this.manager.isTableBlacklisted(tableName)) {
30699
- const message = t_vars("errors.table_blacklisted", {
30700
- table: tableName,
30701
- operation
30702
- });
30703
- throw new BlacklistError(message, tableName, operation);
30704
- }
30705
- }
30706
- checkColumnBlacklistOnWrite(tableName, fields, operation = "WRITE") {
30707
- const blacklisted = this.manager.getBlacklistedColumns(tableName);
30708
- if (blacklisted.length === 0 || fields.length === 0) {
30709
- return;
30710
- }
30711
- const conflicts = fields.filter((f) => blacklisted.includes(f));
30712
- if (conflicts.length === 0) {
30713
- return;
30714
- }
30715
- if (this.manager.canOverrideBlacklist()) {
30716
- const warning = t_vars("warnings.blacklist_override_used", {
30717
- operation,
30718
- table: tableName
30719
- });
30720
- console.error(`${warning} (columns: ${conflicts.join(", ")})`);
30721
- return;
30722
- }
30723
- const message = t_vars("errors.column_blacklisted_write", {
30724
- table: tableName,
30725
- operation,
30726
- columns: conflicts.join(", ")
30727
- });
30728
- throw new BlacklistError(message, tableName, operation);
30729
- }
30730
- filterColumns(tableName, rows, columnList) {
30731
- const blacklistedColumns = this.manager.getBlacklistedColumns(tableName);
30732
- if (blacklistedColumns.length === 0) {
30733
- return { filteredRows: rows, omittedColumns: [] };
30734
- }
30735
- const omittedColumns = columnList.filter((col) => blacklistedColumns.includes(col));
30736
- if (omittedColumns.length === 0) {
30737
- return { filteredRows: rows, omittedColumns: [] };
30738
- }
30739
- const filteredRows = rows.map((row) => {
30740
- const newRow = {};
30741
- for (const [key, value] of Object.entries(row)) {
30742
- if (!omittedColumns.includes(key)) {
30743
- newRow[key] = value;
30744
- }
30745
- }
30746
- return newRow;
30747
- });
30748
- return { filteredRows, omittedColumns };
30749
- }
30750
- buildSecurityNotification(_tableName, omittedColumns) {
30751
- if (omittedColumns.length === 0) {
30752
- return "";
30753
- }
30754
- return t_vars("security.columns_omitted", {
30755
- count: omittedColumns.length
30756
- });
30757
- }
30758
- }
30759
31433
 
30760
31434
  // src/core/index.ts
31435
+ init_blacklist_validator();
30761
31436
  init_size_category();
30762
31437
 
30763
31438
  // src/core/health-checker.ts
@@ -30895,8 +31570,25 @@ init_schema_loader();
30895
31570
  // src/commands/schema.ts
30896
31571
  init_integration_helper();
30897
31572
  init_validation();
31573
+ function markRedactedColumns(cols, collection, blacklist) {
31574
+ const raw = (blacklist.columns ?? {})[collection];
31575
+ if (!raw || raw.length === 0)
31576
+ return cols;
31577
+ const { patterns } = compilePatterns(raw);
31578
+ if (patterns.length === 0)
31579
+ return cols;
31580
+ return cols.map((c) => matchAny(c.name, patterns) ? { ...c, redacted: true } : c);
31581
+ }
31582
+ function decorateMongoSchema(schema, meta) {
31583
+ const columns = markRedactedColumns(schema.columns, schema.name, meta.blacklist);
31584
+ return {
31585
+ ...schema,
31586
+ columns,
31587
+ ...{ sampleMethod: meta.sampleMethod, sampleSize: meta.sampleSize }
31588
+ };
31589
+ }
30898
31590
  var ALLOWED_FORMATS2 = ["table", "json"];
30899
- var schemaCommand = new Command().name("schema").description("Display table schema, scan database schema, or refresh existing schema with detected changes").argument("[table]", "Optional: table name to inspect (if omitted, scans all tables)").option("--format <format>", "Output format: table (default) or json", "table").option("--config <path>", "Path to .dbcli config file", ".dbcli").option("--refresh", "Refresh schema by detecting changes from database", false).option("--reset", "Clear all existing schema data and re-fetch from database", false).option("--force", "Skip confirmation when updating schema data", false).option("--sample-size <n>", "MongoDB only: number of documents to sample for schema inference (default 50, max 1000). Ignored on SQL connections.").option("--recovery", "On failure, emit a structured recovery envelope to stdout (suppresses human stderr message)", false).action(schemaAction);
31591
+ var schemaCommand = new Command().name("schema").description("Display table schema, scan database schema, or refresh existing schema with detected changes").argument("[table]", "Optional: table name to inspect (if omitted, scans all tables)").option("--format <format>", "Output format: table (default) or json", "table").option("--config <path>", "Path to .dbcli config file", ".dbcli").option("--refresh", "Refresh schema by detecting changes from database", false).option("--reset", "Clear all existing schema data and re-fetch from database", false).option("--force", "Skip confirmation when updating schema data", false).option("--sample-size <n>", "MongoDB only: number of documents to sample for schema inference (default 50, max 1000). Ignored on SQL connections.").option("--sample-method <method>", 'MongoDB only: "random" (default, uses $sample) or "natural" (uses find().limit()). Ignored on SQL connections.', "random").option("--recovery", "On failure, emit a structured recovery envelope to stdout (suppresses human stderr message)", false).action(schemaAction);
30900
31592
  async function schemaAction(table, options, command) {
30901
31593
  let config;
30902
31594
  try {
@@ -30921,6 +31613,17 @@ async function schemaAction(table, options, command) {
30921
31613
  console.error("--sample-size is MongoDB-only and is being ignored for this connection.");
30922
31614
  }
30923
31615
  }
31616
+ if (options.sampleMethod && options.sampleMethod !== "random") {
31617
+ if (options.sampleMethod !== "natural") {
31618
+ console.error(`--sample-method must be 'random' or 'natural' (received '${options.sampleMethod}')`);
31619
+ process.exit(1);
31620
+ }
31621
+ if (config.connection.system === "mongodb") {
31622
+ inferenceOptions = { ...inferenceOptions ?? {}, sampleMethod: "natural" };
31623
+ } else {
31624
+ console.error("--sample-method is MongoDB-only and is being ignored for this connection.");
31625
+ }
31626
+ }
30924
31627
  const connectionName = await getSchemaIsolationConnectionName(configPath);
30925
31628
  let existingSchemaCount;
30926
31629
  if (connectionName !== undefined) {
@@ -30994,15 +31697,20 @@ Key: ${schema.name}`);
30994
31697
  }
30995
31698
  const adapter = AdapterFactory.createAdapter(config.connection);
30996
31699
  await adapter.connect();
31700
+ const mongoMeta = config.connection.system === "mongodb" ? {
31701
+ blacklist: config.blacklist ?? { tables: [], columns: {} },
31702
+ sampleMethod: inferenceOptions?.sampleMethod ?? "random",
31703
+ sampleSize: inferenceOptions?.sampleSize ?? 100
31704
+ } : undefined;
30997
31705
  try {
30998
31706
  if (options.reset) {
30999
- await handleSchemaReset(adapter, config, options, connectionName, existingSchemaCount, storagePath, inferenceOptions);
31707
+ await handleSchemaReset(adapter, config, options, connectionName, existingSchemaCount, storagePath, inferenceOptions, mongoMeta);
31000
31708
  } else if (options.refresh) {
31001
- await handleSchemaRefresh(adapter, config, options, connectionName, storagePath, inferenceOptions);
31709
+ await handleSchemaRefresh(adapter, config, options, connectionName, storagePath, inferenceOptions, mongoMeta);
31002
31710
  } else if (table) {
31003
- await handleSingleTableSchema(adapter, table, options.format, inferenceOptions);
31711
+ await handleSingleTableSchema(adapter, table, options.format, inferenceOptions, mongoMeta);
31004
31712
  } else {
31005
- await handleFullDatabaseScan(adapter, config, options, connectionName, existingSchemaCount, storagePath, inferenceOptions);
31713
+ await handleFullDatabaseScan(adapter, config, options, connectionName, existingSchemaCount, storagePath, inferenceOptions, mongoMeta);
31006
31714
  }
31007
31715
  await writeAuditEntry(config, "schema", options, {
31008
31716
  success: true,
@@ -31038,8 +31746,10 @@ Key: ${schema.name}`);
31038
31746
  process.exit(1);
31039
31747
  }
31040
31748
  }
31041
- async function handleSingleTableSchema(adapter, tableName, format, inferenceOptions) {
31042
- const schema = await adapter.getTableSchema(tableName, inferenceOptions);
31749
+ async function handleSingleTableSchema(adapter, tableName, format, inferenceOptions, mongoMeta) {
31750
+ let schema = await adapter.getTableSchema(tableName, inferenceOptions);
31751
+ if (mongoMeta)
31752
+ schema = decorateMongoSchema(schema, mongoMeta);
31043
31753
  if (format === "json") {
31044
31754
  const formatter = new TableSchemaJSONFormatter;
31045
31755
  console.log(formatter.format(schema));
@@ -31083,7 +31793,7 @@ Indexes:`);
31083
31793
  }
31084
31794
  }
31085
31795
  }
31086
- async function handleSchemaRefresh(adapter, config, options, connectionName, storagePath, inferenceOptions) {
31796
+ async function handleSchemaRefresh(adapter, config, options, connectionName, storagePath, inferenceOptions, mongoMeta) {
31087
31797
  const diffEngine = new SchemaDiffEngine(adapter, config);
31088
31798
  const report = await diffEngine.diff();
31089
31799
  if (report.tablesAdded.length === 0 && report.tablesRemoved.length === 0 && Object.keys(report.tablesModified).length === 0) {
@@ -31106,7 +31816,9 @@ async function handleSchemaRefresh(adapter, config, options, connectionName, sto
31106
31816
  }
31107
31817
  const newSchema = { ...config.schema };
31108
31818
  for (const tableName of report.tablesAdded.concat(Object.keys(report.tablesModified))) {
31109
- const fullSchema = await adapter.getTableSchema(tableName, inferenceOptions);
31819
+ let fullSchema = await adapter.getTableSchema(tableName, inferenceOptions);
31820
+ if (mongoMeta)
31821
+ fullSchema = decorateMongoSchema(fullSchema, mongoMeta);
31110
31822
  newSchema[tableName] = fullSchema;
31111
31823
  }
31112
31824
  report.tablesRemoved.forEach((t2) => delete newSchema[t2]);
@@ -31124,7 +31836,7 @@ async function handleSchemaRefresh(adapter, config, options, connectionName, sto
31124
31836
  await writeSchema(storagePath, updatedConfig, connectionName);
31125
31837
  console.log(`\u2705 Schema updated in .dbcli`);
31126
31838
  }
31127
- async function handleSchemaReset(adapter, config, options, connectionName, existingCount, storagePath, inferenceOptions) {
31839
+ async function handleSchemaReset(adapter, config, options, connectionName, existingCount, storagePath, inferenceOptions, mongoMeta) {
31128
31840
  if (existingCount > 0 && !options.force) {
31129
31841
  const { SchemaLayeredLoader: SchemaLayeredLoader2 } = await Promise.resolve().then(() => (init_schema_loader(), exports_schema_loader));
31130
31842
  const loader = new SchemaLayeredLoader2(storagePath, { connectionName });
@@ -31155,7 +31867,9 @@ async function handleSchemaReset(adapter, config, options, connectionName, exist
31155
31867
  const schemaData = {};
31156
31868
  let processed = 0;
31157
31869
  for (const table of tables) {
31158
- const fullSchema = await adapter.getTableSchema(table.name, inferenceOptions);
31870
+ let fullSchema = await adapter.getTableSchema(table.name, inferenceOptions);
31871
+ if (mongoMeta)
31872
+ fullSchema = decorateMongoSchema(fullSchema, mongoMeta);
31159
31873
  schemaData[table.name] = {
31160
31874
  name: fullSchema.name,
31161
31875
  columns: fullSchema.columns,
@@ -31165,7 +31879,8 @@ async function handleSchemaReset(adapter, config, options, connectionName, exist
31165
31879
  foreignKeys: fullSchema.foreignKeys || [],
31166
31880
  indexes: fullSchema.indexes || [],
31167
31881
  estimatedRowCount: fullSchema.estimatedRowCount || 0,
31168
- tableType: fullSchema.tableType || "table"
31882
+ tableType: fullSchema.tableType || "table",
31883
+ ...mongoMeta ? { sampleMethod: mongoMeta.sampleMethod, sampleSize: mongoMeta.sampleSize } : {}
31169
31884
  };
31170
31885
  processed++;
31171
31886
  if (processed % 10 === 0 || processed === tables.length) {
@@ -31192,14 +31907,16 @@ async function handleSchemaReset(adapter, config, options, connectionName, exist
31192
31907
  \u2705 Schema fetched \u2014 ${tables.length} tables from database`);
31193
31908
  }
31194
31909
  }
31195
- async function handleFullDatabaseScan(adapter, config, options, connectionName, existingSchemaCount, storagePath, inferenceOptions) {
31910
+ async function handleFullDatabaseScan(adapter, config, options, connectionName, existingSchemaCount, storagePath, inferenceOptions, mongoMeta) {
31196
31911
  console.log(t("schema.scanning_database"));
31197
31912
  const tables = await adapter.listTables();
31198
31913
  console.log(t_vars("schema.tables_found", { count: tables.length }));
31199
31914
  const schemaData = {};
31200
31915
  let processed = 0;
31201
31916
  for (const table of tables) {
31202
- const fullSchema = await adapter.getTableSchema(table.name, inferenceOptions);
31917
+ let fullSchema = await adapter.getTableSchema(table.name, inferenceOptions);
31918
+ if (mongoMeta)
31919
+ fullSchema = decorateMongoSchema(fullSchema, mongoMeta);
31203
31920
  schemaData[table.name] = {
31204
31921
  name: fullSchema.name,
31205
31922
  columns: fullSchema.columns,
@@ -31209,7 +31926,8 @@ async function handleFullDatabaseScan(adapter, config, options, connectionName,
31209
31926
  foreignKeys: fullSchema.foreignKeys || [],
31210
31927
  indexes: fullSchema.indexes || [],
31211
31928
  estimatedRowCount: fullSchema.estimatedRowCount || 0,
31212
- tableType: fullSchema.tableType || "table"
31929
+ tableType: fullSchema.tableType || "table",
31930
+ ...mongoMeta ? { sampleMethod: mongoMeta.sampleMethod, sampleSize: mongoMeta.sampleSize } : {}
31213
31931
  };
31214
31932
  processed++;
31215
31933
  if (processed % 10 === 0 || processed === tables.length) {
@@ -31265,38 +31983,10 @@ async function writeSchema(configPath, config, connectionName) {
31265
31983
  // src/commands/query.ts
31266
31984
  init_message_loader();
31267
31985
  init_adapters();
31986
+ init_formatters();
31987
+ init_html_formatter();
31988
+ init_opener();
31268
31989
  import crypto2 from "crypto";
31269
-
31270
- // src/formatters/html-formatter.ts
31271
- init_package_root();
31272
- async function generateHtmlReport(payload) {
31273
- const templatePath = packageAssetPath("ui-template.html");
31274
- const templateFile = Bun.file(templatePath);
31275
- if (!await templateFile.exists()) {
31276
- throw new Error(`UI template not found at ${templatePath}. Please run 'bun run build' first.`);
31277
- }
31278
- let html = await templateFile.text();
31279
- const jsonPayload = JSON.stringify(payload).replace(/</g, "\\u003c");
31280
- const injection = `window.__DBCLI_PAYLOAD__ = ${jsonPayload};`;
31281
- html = html.replace("/*DBCLI_PAYLOAD*/", () => injection);
31282
- return html;
31283
- }
31284
-
31285
- // src/utils/opener.ts
31286
- import open2 from "open";
31287
- async function openInBrowser(target) {
31288
- if (process.env.DBCLI_NO_OPEN === "1" || false) {
31289
- console.log(`[Opener] DBCLI_NO_OPEN is set. Skipping browser launch for: ${target}`);
31290
- return;
31291
- }
31292
- try {
31293
- await open2(target);
31294
- } catch (err) {
31295
- console.error(`[Opener] Failed to open ${target}:`, err);
31296
- }
31297
- }
31298
-
31299
- // src/commands/query.ts
31300
31990
  import { tmpdir } from "os";
31301
31991
  import { join as join15 } from "path";
31302
31992
 
@@ -31506,9 +32196,11 @@ function inferColumnType(value) {
31506
32196
  // src/commands/query.ts
31507
32197
  init_config();
31508
32198
  init_permission_guard();
32199
+ init_blacklist_validator();
31509
32200
  init_blacklist();
31510
32201
  init_validation();
31511
32202
  init_integration_helper();
32203
+ init_field_masker();
31512
32204
  function requireSqlConnection2(connection) {
31513
32205
  if (!["postgresql", "mysql", "mariadb"].includes(connection.system)) {
31514
32206
  throw new Error(`This command requires a SQL connection, got: ${connection.system}`);
@@ -31688,22 +32380,22 @@ async function mongoQueryBranch(queryStr, options, config) {
31688
32380
  execution_ms: executionTimeMs
31689
32381
  }
31690
32382
  });
31691
- const columnNames = result.rows[0] ? Object.keys(result.rows[0]) : [];
31692
- const filterResult = blacklistValidator.filterColumns(collection, result.rows, columnNames);
32383
+ const blacklistCfg = config.blacklist ?? { tables: [], columns: {} };
32384
+ const maskedRows = maskMongoRows(result.rows, collection, blacklistCfg);
32385
+ const columnNames = maskedRows[0] ? Object.keys(maskedRows[0]) : [];
31693
32386
  const queryResult = {
31694
- rows: filterResult.filteredRows,
31695
- rowCount: filterResult.filteredRows.length,
31696
- columnNames: columnNames.filter((col) => !filterResult.omittedColumns.includes(col))
32387
+ rows: maskedRows,
32388
+ rowCount: maskedRows.length,
32389
+ columnNames
31697
32390
  };
31698
32391
  const formatter = new QueryResultFormatter;
31699
32392
  const output = formatter.format(queryResult, {
31700
32393
  format
31701
32394
  });
31702
32395
  console.log(output);
31703
- const securityNote = blacklistValidator.buildSecurityNotification(collection, filterResult.omittedColumns);
31704
- if (securityNote) {
32396
+ if ((blacklistCfg.columns[collection] ?? []).length > 0) {
31705
32397
  console.log(`
31706
- \u2139 ${securityNote}`);
32398
+ \u2139 Some fields may have been redacted as [REDACTED] per .dbcli blacklist.`);
31707
32399
  }
31708
32400
  } finally {
31709
32401
  await mongoAdapter.disconnect();
@@ -32241,14 +32933,18 @@ init_message_loader();
32241
32933
  init_adapters();
32242
32934
  init_config();
32243
32935
  import crypto3 from "crypto";
32936
+ init_blacklist_validator();
32244
32937
  init_blacklist();
32245
32938
  init_permission_guard();
32246
32939
  init_engine_hints();
32940
+ init_formatters();
32941
+ init_html_formatter();
32942
+ init_opener();
32247
32943
  init_integration_helper();
32248
32944
  init_saved_queries();
32249
32945
  init_strategies();
32250
- import { tmpdir as tmpdir2 } from "os";
32251
- import { join as join18 } from "path";
32946
+ import { tmpdir as tmpdir3 } from "os";
32947
+ import { join as join19 } from "path";
32252
32948
  function formatDryRun(input) {
32253
32949
  const lines = ["Dry-run preview (no execution):"];
32254
32950
  if (input.family === "es") {
@@ -32284,17 +32980,14 @@ async function qCommand(name, options, command) {
32284
32980
  if (!config.connection)
32285
32981
  throw new Error('Run "dbcli init" first');
32286
32982
  const engine = mapSystemToEngine(config.connection.system);
32287
- if (engine === "mongodb") {
32288
- throw new SavedQueryError(`Saved queries do not support MongoDB connections`, "ENGINE_MISMATCH");
32289
- }
32290
32983
  const dirs = resolveSnippetDirs(process.cwd());
32291
32984
  const map = await loadSnippets(dirs);
32292
32985
  const snippet = resolveByName(map, name, engine);
32293
32986
  const cliParams = parseCliParams(options.param ?? []);
32294
32987
  const fileParams = await readParamFile(options.paramFile);
32295
32988
  const prepared = prepareExecution(snippet, { engine, noLimit: options.noLimit === true }, cliParams, fileParams);
32296
- for (const w of prepared.warnings)
32297
- console.error(`\u26A0 ${w}`);
32989
+ for (const w2 of prepared.warnings)
32990
+ console.error(`\u26A0 ${w2}`);
32298
32991
  if (prepared.warnings.length > 0)
32299
32992
  console.error("");
32300
32993
  if (options.dryRun) {
@@ -32312,6 +33005,11 @@ async function qCommand(name, options, command) {
32312
33005
  });
32313
33006
  return;
32314
33007
  }
33008
+ if (engine === "mongodb") {
33009
+ const { qMongoBranch: qMongoBranch2 } = await Promise.resolve().then(() => (init_q_mongo(), exports_q_mongo));
33010
+ await qMongoBranch2(snippet, prepared, options, config);
33011
+ return;
33012
+ }
32315
33013
  const blacklistManager = new BlacklistManager(config);
32316
33014
  const blacklistValidator = new BlacklistValidator(blacklistManager);
32317
33015
  const family = engineFamily(engine);
@@ -32338,7 +33036,7 @@ async function qCommand(name, options, command) {
32338
33036
  rows: filtered.filteredRows
32339
33037
  });
32340
33038
  if (options.ui) {
32341
- const tempPath = join18(tmpdir2(), `dbcli-report-${Date.now()}.html`);
33039
+ const tempPath = join19(tmpdir3(), `dbcli-report-${Date.now()}.html`);
32342
33040
  await Bun.write(tempPath, html);
32343
33041
  await openInBrowser(tempPath);
32344
33042
  } else {
@@ -32593,9 +33291,7 @@ async function deriveEngine() {
32593
33291
  try {
32594
33292
  const cfg = await configModule.read(resolveConfigPath(undefined, {}));
32595
33293
  if (cfg.connection) {
32596
- const engine = mapSystemToEngine(cfg.connection.system);
32597
- if (engine !== "mongodb")
32598
- return engine;
33294
+ return mapSystemToEngine(cfg.connection.system);
32599
33295
  }
32600
33296
  } catch {}
32601
33297
  return "postgres";
@@ -32766,7 +33462,7 @@ async function queriesSearch(keywords, options) {
32766
33462
  process.exit(2);
32767
33463
  return;
32768
33464
  }
32769
- const allowed = ["postgres", "mysql", "redis", "elasticsearch", "all"];
33465
+ const allowed = ["postgres", "mysql", "redis", "elasticsearch", "mongodb", "all"];
32770
33466
  if (options.engine && !allowed.includes(options.engine)) {
32771
33467
  console.error(`Unknown engine '${options.engine}'. Allowed: ${allowed.join(", ")}.`);
32772
33468
  process.exit(2);
@@ -32811,9 +33507,7 @@ async function deriveEngineOrNull() {
32811
33507
  try {
32812
33508
  const cfg = await configModule.read(resolveConfigPath(undefined, {}));
32813
33509
  if (cfg.connection) {
32814
- const e = mapSystemToEngine(cfg.connection.system);
32815
- if (e !== "mongodb")
32816
- return e;
33510
+ return mapSystemToEngine(cfg.connection.system);
32817
33511
  }
32818
33512
  } catch {}
32819
33513
  return null;
@@ -32861,7 +33555,7 @@ async function queriesSuggest(intent, options) {
32861
33555
  process.exit(2);
32862
33556
  return;
32863
33557
  }
32864
- const allowed = ["postgres", "mysql", "redis", "elasticsearch", "all"];
33558
+ const allowed = ["postgres", "mysql", "redis", "elasticsearch", "mongodb", "all"];
32865
33559
  if (options.engine && !allowed.includes(options.engine)) {
32866
33560
  console.error(`Unknown engine '${options.engine}'. Allowed: ${allowed.join(", ")}.`);
32867
33561
  process.exit(2);
@@ -33305,6 +33999,7 @@ Parameters:`);
33305
33999
  // src/commands/insert.ts
33306
34000
  init_config();
33307
34001
  init_permission_guard();
34002
+ init_blacklist_validator();
33308
34003
  init_blacklist();
33309
34004
 
33310
34005
  // src/core/mongo/dry-run-formatter.ts
@@ -33422,7 +34117,23 @@ function parseWhereClause(whereClause) {
33422
34117
 
33423
34118
  // src/core/mongo/dml-plan.ts
33424
34119
  init_permission_guard();
33425
- var MONGO_UPDATE_OPERATOR_ALLOWLIST = new Set(["$set", "$unset"]);
34120
+ var MONGO_OPERATOR_TIER = {
34121
+ $set: "SAFE",
34122
+ $unset: "SAFE",
34123
+ $rename: "RENAME",
34124
+ $inc: "ARITHMETIC",
34125
+ $mul: "ARITHMETIC",
34126
+ $min: "ARITHMETIC",
34127
+ $max: "ARITHMETIC",
34128
+ $currentDate: "ARITHMETIC",
34129
+ $push: "ARRAY",
34130
+ $pull: "ARRAY",
34131
+ $pullAll: "ARRAY",
34132
+ $pop: "ARRAY",
34133
+ $addToSet: "ARRAY",
34134
+ $bit: "BITWISE",
34135
+ $where: "BLOCK"
34136
+ };
33426
34137
  var MONGO_BROAD_FILTER_OPERATORS = new Set([
33427
34138
  "$regex",
33428
34139
  "$in",
@@ -33466,28 +34177,79 @@ function decide2(factors) {
33466
34177
  return "WARN";
33467
34178
  return "ALLOW";
33468
34179
  }
33469
- function extractMongoWrittenFields(setDoc) {
34180
+ function classifyMongoUpdate(setDoc) {
33470
34181
  const fields = new Set;
33471
- const operators = [];
33472
- let hasUnsupportedOperator = false;
34182
+ const tierFactors = [];
34183
+ let hasBlock = false;
33473
34184
  const hasAnyOperator = Object.keys(setDoc).some((k) => k.startsWith("$"));
33474
34185
  if (!hasAnyOperator) {
33475
34186
  for (const k of Object.keys(setDoc))
33476
34187
  fields.add(k);
33477
- return { fields: Array.from(fields), operators: [], hasUnsupportedOperator: false };
34188
+ return { fields: Array.from(fields), tierFactors, hasBlock };
33478
34189
  }
34190
+ const seenTiers = new Map;
33479
34191
  for (const [op, payload] of Object.entries(setDoc)) {
33480
- operators.push(op);
33481
- if (!MONGO_UPDATE_OPERATOR_ALLOWLIST.has(op)) {
33482
- hasUnsupportedOperator = true;
34192
+ if (!op.startsWith("$")) {
34193
+ fields.add(op);
33483
34194
  continue;
33484
34195
  }
33485
34196
  if (payload && typeof payload === "object" && !Array.isArray(payload)) {
33486
34197
  for (const k of Object.keys(payload))
33487
34198
  fields.add(k);
33488
34199
  }
34200
+ const tier = MONGO_OPERATOR_TIER[op];
34201
+ if (tier === undefined) {
34202
+ hasBlock = true;
34203
+ tierFactors.push({
34204
+ code: "mongo_unknown_operator",
34205
+ severity: "block",
34206
+ message: `Update uses unknown operator '${op}'. Reject by default; add to tier table if intentional.`
34207
+ });
34208
+ continue;
34209
+ }
34210
+ if (tier === "BLOCK") {
34211
+ hasBlock = true;
34212
+ tierFactors.push({
34213
+ code: "mongo_unknown_operator",
34214
+ severity: "block",
34215
+ message: `Update uses '${op}' which executes server-side code. Operation rejected.`
34216
+ });
34217
+ continue;
34218
+ }
34219
+ if (tier === "SAFE")
34220
+ continue;
34221
+ const bucket = seenTiers.get(tier) ?? [];
34222
+ bucket.push(op);
34223
+ seenTiers.set(tier, bucket);
34224
+ }
34225
+ for (const [tier, ops] of seenTiers) {
34226
+ if (tier === "RENAME") {
34227
+ tierFactors.push({
34228
+ code: "mongo_rename_operator",
34229
+ severity: "warn",
34230
+ message: `Update uses ${ops.join(", ")}; field rename does not exfiltrate data but can break readers.`
34231
+ });
34232
+ } else if (tier === "ARITHMETIC") {
34233
+ tierFactors.push({
34234
+ code: "mongo_arithmetic_operator",
34235
+ severity: "warn",
34236
+ message: `Update uses ${ops.join(", ")}; numeric mutation may compound silently.`
34237
+ });
34238
+ } else if (tier === "ARRAY") {
34239
+ tierFactors.push({
34240
+ code: "mongo_array_operator",
34241
+ severity: "warn",
34242
+ message: `Update uses ${ops.join(", ")}; array mutation can grow unboundedly without a size guard.`
34243
+ });
34244
+ } else if (tier === "BITWISE") {
34245
+ tierFactors.push({
34246
+ code: "mongo_bitwise_operator",
34247
+ severity: "warn",
34248
+ message: `Update uses ${ops.join(", ")}; bitwise updates skip type promotion checks.`
34249
+ });
34250
+ }
33489
34251
  }
33490
- return { fields: Array.from(fields), operators, hasUnsupportedOperator };
34252
+ return { fields: Array.from(fields), tierFactors, hasBlock };
33491
34253
  }
33492
34254
  function hasIdEquality(where) {
33493
34255
  if (!("_id" in where))
@@ -33541,18 +34303,21 @@ function applyTableBlacklist(target, context, factors) {
33541
34303
  function applyColumnBlacklist(target, fields, context, factors) {
33542
34304
  const columns = context.blacklist.columns ?? {};
33543
34305
  const lower = target.toLowerCase();
33544
- let blacklistedFields = [];
34306
+ let raw = [];
33545
34307
  for (const [t2, cols] of Object.entries(columns)) {
33546
34308
  if (t2.toLowerCase() === lower) {
33547
- blacklistedFields = cols;
34309
+ raw = cols;
33548
34310
  break;
33549
34311
  }
33550
34312
  }
33551
- if (blacklistedFields.length === 0)
34313
+ if (raw.length === 0)
34314
+ return;
34315
+ const { patterns } = compilePatterns(raw);
34316
+ if (patterns.length === 0)
33552
34317
  return;
33553
34318
  for (const field of fields) {
33554
- if (blacklistedFields.includes(field)) {
33555
- pushFactor2(factors, "blacklisted_column", "block", `MongoDB write would touch blacklisted field ${target}.${field}.`);
34319
+ if (matchAny(field, patterns)) {
34320
+ pushFactor2(factors, "blacklisted_column", "block", `MongoDB write would touch blacklisted path ${target}.${field}.`);
33556
34321
  }
33557
34322
  }
33558
34323
  }
@@ -33601,18 +34366,31 @@ function buildSuggestedCommands2(target, factors) {
33601
34366
  }
33602
34367
  return [];
33603
34368
  }
34369
+ function flattenInsertPaths(data, prefix = "") {
34370
+ const out = [];
34371
+ for (const [k, v] of Object.entries(data)) {
34372
+ const path4 = prefix === "" ? k : `${prefix}.${k}`;
34373
+ out.push(path4);
34374
+ if (v && typeof v === "object" && !Array.isArray(v) && !(v instanceof Date)) {
34375
+ const ctor = v.constructor?.name;
34376
+ if (ctor === "Object") {
34377
+ out.push(...flattenInsertPaths(v, path4));
34378
+ }
34379
+ }
34380
+ }
34381
+ return out;
34382
+ }
33604
34383
  var analyzeMongoDmlRisk = (intent, context) => {
33605
34384
  const factors = [];
33606
34385
  applyPermission(intent.operation, context, factors);
33607
34386
  applyTableBlacklist(intent.target, context, factors);
33608
34387
  if (intent.operation === "insert") {
33609
- applyColumnBlacklist(intent.target, Object.keys(intent.data), context, factors);
34388
+ applyColumnBlacklist(intent.target, flattenInsertPaths(intent.data), context, factors);
33610
34389
  } else if (intent.operation === "update") {
33611
- const { fields, hasUnsupportedOperator } = extractMongoWrittenFields(intent.set);
33612
- if (hasUnsupportedOperator) {
33613
- pushFactor2(factors, "nonsql_unsupported_operator", "block", "MongoDB update uses an operator outside the MVP allowlist ($set, $unset).");
33614
- }
33615
- applyColumnBlacklist(intent.target, fields, context, factors);
34390
+ const cls = classifyMongoUpdate(intent.set);
34391
+ for (const f of cls.tierFactors)
34392
+ pushFactor2(factors, f.code, f.severity, f.message);
34393
+ applyColumnBlacklist(intent.target, cls.fields, context, factors);
33616
34394
  const where = intent.where ?? {};
33617
34395
  if (Object.keys(where).length === 0) {
33618
34396
  pushFactor2(factors, "nonsql_filter_empty", "block", "MongoDB update filter is empty and would match every document.");
@@ -34278,6 +35056,7 @@ init_adapters();
34278
35056
  import crypto5 from "crypto";
34279
35057
  init_config();
34280
35058
  init_permission_guard();
35059
+ init_blacklist_validator();
34281
35060
  init_blacklist();
34282
35061
  init_integration_helper();
34283
35062
  function requireSqlConnection4(connection) {
@@ -34574,6 +35353,7 @@ init_adapters();
34574
35353
  import crypto6 from "crypto";
34575
35354
  init_config();
34576
35355
  init_permission_guard();
35356
+ init_blacklist_validator();
34577
35357
  init_blacklist();
34578
35358
  init_integration_helper();
34579
35359
  function requireSqlConnection5(connection) {
@@ -34851,12 +35631,16 @@ async function deleteCommand(table, options, command) {
34851
35631
  // src/commands/export.ts
34852
35632
  init_message_loader();
34853
35633
  init_adapters();
35634
+ init_formatters();
35635
+ init_html_formatter();
34854
35636
  import crypto7 from "crypto";
34855
35637
  init_config();
34856
35638
  init_permission_guard();
35639
+ init_blacklist_validator();
34857
35640
  init_blacklist();
34858
35641
  init_integration_helper();
34859
35642
  init_engine_hints();
35643
+ init_field_masker();
34860
35644
  function requireSqlConnection6(connection) {
34861
35645
  if (!["postgresql", "mysql", "mariadb"].includes(connection.system)) {
34862
35646
  throw new Error(`This command requires a SQL connection, got: ${connection.system}`);
@@ -35062,10 +35846,10 @@ async function mongoExportBranch(query, options, config) {
35062
35846
  await adapter.connect();
35063
35847
  try {
35064
35848
  const result = await adapter.execute(query, [collection], effectiveLimit !== undefined ? { limit: effectiveLimit } : undefined);
35065
- const columnNames = collectColumnUnion(result.rows);
35066
- const filterResult = blacklistValidator.filterColumns(collection, result.rows, columnNames);
35067
- const visibleColumns = columnNames.filter((col) => !filterResult.omittedColumns.includes(col));
35068
- const formatted = formatMongoRows(filterResult.filteredRows, visibleColumns, options.format);
35849
+ const blacklistCfg = config.blacklist ?? { tables: [], columns: {} };
35850
+ const maskedRows = maskMongoRows(result.rows, collection, blacklistCfg);
35851
+ const visibleColumns = collectColumnUnion(maskedRows);
35852
+ const formatted = formatMongoRows(maskedRows, visibleColumns, options.format);
35069
35853
  if (options.output) {
35070
35854
  const file = Bun.file(options.output);
35071
35855
  const exists = await file.exists();
@@ -35078,21 +35862,20 @@ async function mongoExportBranch(query, options, config) {
35078
35862
  }
35079
35863
  await file.write(formatted);
35080
35864
  console.error(t_vars("export.exported", {
35081
- count: filterResult.filteredRows.length,
35865
+ count: maskedRows.length,
35082
35866
  file: options.output
35083
35867
  }));
35084
35868
  } else {
35085
35869
  console.log(formatted);
35086
35870
  }
35087
- const securityNote = blacklistValidator.buildSecurityNotification(collection, filterResult.omittedColumns);
35088
- if (securityNote) {
35089
- console.error(`\u2139 ${securityNote}`);
35871
+ if ((blacklistCfg.columns[collection] ?? []).length > 0) {
35872
+ console.error(`\u2139 Some fields may have been redacted as [REDACTED] per .dbcli blacklist.`);
35090
35873
  }
35091
35874
  await writeAuditEntry(config, "export", options, {
35092
35875
  success: true,
35093
35876
  target: collection,
35094
35877
  metadata: {
35095
- rows_affected: filterResult.filteredRows.length,
35878
+ rows_affected: maskedRows.length,
35096
35879
  output_format: options.format,
35097
35880
  ...options.output && { output_file: options.output }
35098
35881
  }
@@ -35298,15 +36081,15 @@ class AgentTaskError extends Error {
35298
36081
  }
35299
36082
  // src/core/agent-tasks/task-paths.ts
35300
36083
  init_package_root();
35301
- import { join as join21 } from "path";
36084
+ import { join as join22 } from "path";
35302
36085
  function resolveBuiltinDir2() {
35303
36086
  return packageAssetPath("tasks");
35304
36087
  }
35305
36088
  function resolveAgentTaskDirs(workspaceRoot) {
35306
36089
  return {
35307
36090
  builtinDir: resolveBuiltinDir2(),
35308
- sharedDir: join21(workspaceRoot, ".dbcli-shared", "tasks"),
35309
- localDir: join21(workspaceRoot, ".dbcli", "tasks")
36091
+ sharedDir: join22(workspaceRoot, ".dbcli-shared", "tasks"),
36092
+ localDir: join22(workspaceRoot, ".dbcli", "tasks")
35310
36093
  };
35311
36094
  }
35312
36095
  // src/core/agent-tasks/parser.ts
@@ -35444,7 +36227,7 @@ function parseSteps(value, input) {
35444
36227
  }
35445
36228
  // src/core/agent-tasks/loader.ts
35446
36229
  import { readdir as readdir2 } from "fs/promises";
35447
- import { join as join22, relative as relative2, sep as sep2 } from "path";
36230
+ import { join as join23, relative as relative2, sep as sep2 } from "path";
35448
36231
  async function loadAgentTasks(opts, flags) {
35449
36232
  const errors3 = [];
35450
36233
  const builtin = await walkAndParse2(opts.builtinDir, "builtin", errors3);
@@ -35501,7 +36284,7 @@ async function safeCollectMd(root) {
35501
36284
  return;
35502
36285
  }
35503
36286
  for (const e of entries) {
35504
- const full = join22(dir, e.name);
36287
+ const full = join23(dir, e.name);
35505
36288
  if (e.isDirectory())
35506
36289
  await walk(full);
35507
36290
  else
@@ -35609,7 +36392,7 @@ function splitArgv(input) {
35609
36392
  }
35610
36393
 
35611
36394
  // src/core/agent-tasks/planner.ts
35612
- var TEMPLATE_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
36395
+ var TEMPLATE_RE2 = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
35613
36396
  function planAgentTask(input) {
35614
36397
  const warnings = [];
35615
36398
  const resolved = resolveParams(input.task.params, input.params, warnings);
@@ -35689,7 +36472,7 @@ function coerce2(p, raw) {
35689
36472
  return value;
35690
36473
  }
35691
36474
  function applyTemplate(command, values, task) {
35692
- return command.replace(TEMPLATE_RE, (_match, key) => {
36475
+ return command.replace(TEMPLATE_RE2, (_match, key) => {
35693
36476
  if (!(key in values)) {
35694
36477
  throw new AgentTaskError(`Template references unknown parameter '${key}' in task '${task.name}'`, "TEMPLATE_SYNTAX", task.file);
35695
36478
  }
@@ -35729,8 +36512,8 @@ function renderMarkdownPlan(plan) {
35729
36512
  });
35730
36513
  if (plan.warnings.length > 0) {
35731
36514
  lines.push("## Warnings");
35732
- for (const w of plan.warnings)
35733
- lines.push(`- ${w}`);
36515
+ for (const w2 of plan.warnings)
36516
+ lines.push(`- ${w2}`);
35734
36517
  }
35735
36518
  return lines.join(`
35736
36519
  `);
@@ -35903,6 +36686,15 @@ function parseParamPairs(pairs) {
35903
36686
  // src/commands/blacklist.ts
35904
36687
  init_message_loader();
35905
36688
  init_config();
36689
+ function auditBlacklistPatterns(cfg) {
36690
+ const warnings = [];
36691
+ for (const [collection, raw] of Object.entries(cfg.columns ?? {})) {
36692
+ const { rejected } = compilePatterns(raw);
36693
+ for (const r of rejected)
36694
+ warnings.push({ collection, raw: r.raw, reason: r.reason });
36695
+ }
36696
+ return { warnings };
36697
+ }
35906
36698
  var DEFAULT_CONFIG_PATH = ".dbcli";
35907
36699
  var VALID_TABLE_NAME = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
35908
36700
  var VALID_COLUMN_NAME = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
@@ -35953,6 +36745,10 @@ async function blacklistList(configPath) {
35953
36745
  } else {
35954
36746
  console.log(`${t("blacklist.columns_label")}: {}`);
35955
36747
  }
36748
+ const audit = auditBlacklistPatterns(blacklist);
36749
+ for (const w2 of audit.warnings) {
36750
+ console.error(`\u26A0 blacklist.columns["${w2.collection}"]: '${w2.raw}' is ignored on mongo connections (${w2.reason}).`);
36751
+ }
35956
36752
  }
35957
36753
  async function blacklistTableAdd(tableName, configPath) {
35958
36754
  if (!isValidTableName(tableName)) {
@@ -36702,8 +37498,8 @@ async function collectReport(opts) {
36702
37498
  brief: opts.brief,
36703
37499
  probeTimeoutMs: opts.probeTimeoutMs
36704
37500
  });
36705
- for (const w of context.warnings) {
36706
- warnings.push({ severity: "warn", message: w, source: "inspect" });
37501
+ for (const w2 of context.warnings) {
37502
+ warnings.push({ severity: "warn", message: w2, source: "inspect" });
36707
37503
  }
36708
37504
  if (opts.noConnect) {
36709
37505
  warnings.push({ severity: "info", message: "diagnostics skipped (no-connect mode)" });
@@ -36871,9 +37667,9 @@ function renderMarkdown3(snap, options = {}) {
36871
37667
  }
36872
37668
  if (snap.warnings.length > 0) {
36873
37669
  lines.push("## Warnings");
36874
- for (const w of snap.warnings) {
36875
- const src = w.source ? ` (${w.source})` : "";
36876
- lines.push(`- [${w.severity}]${src} ${w.message}`);
37670
+ for (const w2 of snap.warnings) {
37671
+ const src = w2.source ? ` (${w2.source})` : "";
37672
+ lines.push(`- [${w2.severity}]${src} ${w2.message}`);
36877
37673
  }
36878
37674
  lines.push("");
36879
37675
  }
@@ -37187,8 +37983,8 @@ async function collectGuide(opts) {
37187
37983
  brief: opts.brief,
37188
37984
  probeTimeoutMs: opts.probeTimeoutMs
37189
37985
  });
37190
- for (const w of context.warnings) {
37191
- warnings.push({ severity: "warn", message: w, source: "inspect" });
37986
+ for (const w2 of context.warnings) {
37987
+ warnings.push({ severity: "warn", message: w2, source: "inspect" });
37192
37988
  }
37193
37989
  let snippets = new Map;
37194
37990
  try {
@@ -37277,9 +38073,9 @@ function renderMarkdown4(snap, options = {}) {
37277
38073
  lines.push("");
37278
38074
  if (snap.warnings.length > 0) {
37279
38075
  lines.push("## Warnings");
37280
- for (const w of snap.warnings) {
37281
- const src = w.source ? ` (${w.source})` : "";
37282
- lines.push(`- [${w.severity}]${src} ${w.message}`);
38076
+ for (const w2 of snap.warnings) {
38077
+ const src = w2.source ? ` (${w2.source})` : "";
38078
+ lines.push(`- [${w2.severity}]${src} ${w2.message}`);
37283
38079
  }
37284
38080
  lines.push("");
37285
38081
  }
@@ -37479,6 +38275,7 @@ var recoveryCategorySchema = exports_external.enum([
37479
38275
  "unknown"
37480
38276
  ]);
37481
38277
  var guideRiskSchema = exports_external.enum(["readonly", "dry-run", "write", "unknown"]);
38278
+ var branchIdSchema = exports_external.string().min(1).max(64).regex(/^[a-z0-9-]+$/, "branchId must match /^[a-z0-9-]+$/");
37482
38279
  var guideStepSchema = exports_external.object({
37483
38280
  order: exports_external.number().int(),
37484
38281
  command: exports_external.string().min(1),
@@ -37489,7 +38286,35 @@ var guideStepSchema = exports_external.object({
37489
38286
  intent: exports_external.string().optional(),
37490
38287
  interactive: exports_external.boolean().optional(),
37491
38288
  dbWrite: exports_external.boolean().optional(),
37492
- placeholders: exports_external.array(exports_external.string()).optional()
38289
+ placeholders: exports_external.array(exports_external.string()).optional(),
38290
+ branchId: branchIdSchema.optional()
38291
+ }).strict();
38292
+ var branchPlanSchema = exports_external.object({
38293
+ description: exports_external.string().min(1),
38294
+ steps: exports_external.array(guideStepSchema).min(1).max(MAX_BRANCH_STEPS)
38295
+ }).strict();
38296
+ var branchesSchema = exports_external.record(branchIdSchema, branchPlanSchema).superRefine((map, ctx) => {
38297
+ const keys = Object.keys(map);
38298
+ if (keys.length > MAX_BRANCH_COUNT) {
38299
+ ctx.addIssue({
38300
+ code: exports_external.ZodIssueCode.custom,
38301
+ message: `branches has ${keys.length} entries; cap is ${MAX_BRANCH_COUNT}`
38302
+ });
38303
+ }
38304
+ for (const [id, plan] of Object.entries(map)) {
38305
+ for (const step of plan.steps) {
38306
+ if (step.branchId !== undefined && step.branchId !== id) {
38307
+ ctx.addIssue({
38308
+ code: exports_external.ZodIssueCode.custom,
38309
+ message: `step.branchId '${step.branchId}' does not match enclosing key '${id}'`
38310
+ });
38311
+ }
38312
+ }
38313
+ }
38314
+ });
38315
+ var branchForkSchema = exports_external.object({
38316
+ after: exports_external.number().int().min(1),
38317
+ branchIds: exports_external.array(branchIdSchema).min(1)
37493
38318
  }).strict();
37494
38319
  var recoveryEnvelopeSchema = exports_external.object({
37495
38320
  schemaVersion: exports_external.literal(1),
@@ -37510,8 +38335,30 @@ var recoveryEnvelopeSchema = exports_external.object({
37510
38335
  }).strict().optional()
37511
38336
  }).strict(),
37512
38337
  recovery: exports_external.array(guideStepSchema),
37513
- verify: guideStepSchema.optional()
37514
- }).strict();
38338
+ verify: guideStepSchema.optional(),
38339
+ branches: branchesSchema.optional(),
38340
+ branchFork: branchForkSchema.optional()
38341
+ }).strict().superRefine((env, ctx) => {
38342
+ const hasBranches = env.branches !== undefined;
38343
+ const hasFork = env.branchFork !== undefined;
38344
+ if (hasBranches !== hasFork) {
38345
+ ctx.addIssue({
38346
+ code: exports_external.ZodIssueCode.custom,
38347
+ message: "branches and branchFork must be set together (or both omitted)"
38348
+ });
38349
+ return;
38350
+ }
38351
+ if (hasBranches && hasFork) {
38352
+ const keys = Object.keys(env.branches).sort();
38353
+ const ids = [...env.branchFork.branchIds].sort();
38354
+ if (keys.length !== ids.length || keys.some((k, i) => k !== ids[i])) {
38355
+ ctx.addIssue({
38356
+ code: exports_external.ZodIssueCode.custom,
38357
+ message: "branchFork.branchIds must equal Object.keys(branches)"
38358
+ });
38359
+ }
38360
+ }
38361
+ });
37515
38362
  var savedRecoveryEnvelopeSchema = exports_external.object({
37516
38363
  schemaVersion: exports_external.literal(1),
37517
38364
  id: exports_external.string().optional(),
@@ -37544,6 +38391,7 @@ function looksLikeSavedEnvelope(x) {
37544
38391
  }
37545
38392
 
37546
38393
  // src/commands/recover.ts
38394
+ init_next_step();
37547
38395
  init_next_step_schema();
37548
38396
  init_validation();
37549
38397
  var ALLOWED_FORMATS13 = ["json", "markdown"];
@@ -37654,21 +38502,27 @@ async function runNext(options, source) {
37654
38502
  throw new RecoverCliError(parsed.reason ?? "--result invalid.", EXIT_CODE.malformed);
37655
38503
  }
37656
38504
  const env = source.envelope;
38505
+ const branchIdRaw = options.branch;
37657
38506
  let outcome;
37658
38507
  try {
37659
- outcome = nextStepFromEnvelope(env, afterStep, parsed.value);
38508
+ outcome = nextStepFromEnvelope(env, afterStep, parsed.value, {
38509
+ branchId: branchIdRaw
38510
+ });
37660
38511
  } catch (e) {
37661
38512
  throw new RecoverCliError(e.message, EXIT_CODE.malformed);
37662
38513
  }
37663
- const totalSteps = env.recovery.length;
37664
38514
  if (outcome.kind === "done") {
37665
38515
  return {
37666
38516
  schemaVersion: NEXT_SCHEMA_VERSION,
37667
38517
  kind: "done",
37668
38518
  source: { kind: source.kind, path: source.path },
37669
38519
  errorCode: env.error.code,
37670
- cursor: totalSteps,
37671
- totalSteps
38520
+ cursor: outcome.cursor,
38521
+ totalSteps: outcome.totalSteps,
38522
+ ...outcome.branchId !== undefined ? {
38523
+ branchId: outcome.branchId,
38524
+ branchDescription: env.branches?.[outcome.branchId]?.description
38525
+ } : {}
37672
38526
  };
37673
38527
  }
37674
38528
  return {
@@ -37676,12 +38530,16 @@ async function runNext(options, source) {
37676
38530
  kind: "step",
37677
38531
  source: { kind: source.kind, path: source.path },
37678
38532
  errorCode: env.error.code,
37679
- cursor: outcome.step.order,
37680
- totalSteps,
37681
- step: outcome.step
38533
+ cursor: outcome.cursor,
38534
+ totalSteps: outcome.totalSteps,
38535
+ step: outcome.step,
38536
+ ...outcome.branchId !== undefined ? {
38537
+ branchId: outcome.branchId,
38538
+ branchDescription: env.branches?.[outcome.branchId]?.description
38539
+ } : {}
37682
38540
  };
37683
38541
  }
37684
- var recoverCommand = new Command().name("recover").description("Inspect or apply the last recovery plan").option("--apply", "Execute the recovery plan under risk gating", false).option("--from <path>", "Read the envelope from a file instead of .dbcli/last-recovery.json").option("--allow-write <tier>", `Open the risk gate one tier; values: ${ALLOWED_TIERS.join(" | ")}`).option("--no-verify", "Skip the verify step appended after a successful --apply").option("--next", "Look up the single next step in the multi-turn protocol", false).option("--after-step <n>", "For --next: 1-based order of the step the agent just executed").option("--result <value>", "For --next: JSON StepResultSummary (or @<path> to read from a file)").option("--format <format>", "Output format: markdown | json (default: markdown for inspect, json for --apply / --next)").action(async (options) => {
38542
+ var recoverCommand = new Command().name("recover").description("Inspect or apply the last recovery plan").option("--apply", "Execute the recovery plan under risk gating", false).option("--from <path>", "Read the envelope from a file instead of .dbcli/last-recovery.json").option("--allow-write <tier>", `Open the risk gate one tier; values: ${ALLOWED_TIERS.join(" | ")}`).option("--no-verify", "Skip the verify step appended after a successful --apply").option("--next", "Look up the single next step in the multi-turn protocol", false).option("--after-step <n>", "For --next: 1-based order of the step the agent just executed").option("--result <value>", "For --next: JSON StepResultSummary (or @<path> to read from a file)").option("--branch <id>", "For --next: walk a specific branch by id").option("--format <format>", "Output format: markdown | json (default: markdown for inspect, json for --apply / --next)").action(async (options) => {
37685
38543
  try {
37686
38544
  if (options.next === true && options.apply === true) {
37687
38545
  throw new RecoverCliError("--next and --apply cannot be combined.", EXIT_CODE.malformed);
@@ -37755,7 +38613,7 @@ var recoverCommand = new Command().name("recover").description("Inspect or apply
37755
38613
 
37756
38614
  // src/commands/audit.ts
37757
38615
  import { rm as rm3, stat as stat7 } from "fs/promises";
37758
- import { join as join25 } from "path";
38616
+ import { join as join26 } from "path";
37759
38617
  init_message_loader();
37760
38618
  init_validation();
37761
38619
  init_config();
@@ -37771,8 +38629,8 @@ var PREFIX_MIN = 4;
37771
38629
  async function resolveAuditPaths(configPath, config) {
37772
38630
  const storagePath = await resolveConfigStoragePath(configPath);
37773
38631
  const connName = config.effectiveConnectionName || getGlobalConnectionName() || "default";
37774
- const auditDir = join25(storagePath, ".dbcli", "audit");
37775
- const auditFile = join25(auditDir, `${connName}.jsonl`);
38632
+ const auditDir = join26(storagePath, ".dbcli", "audit");
38633
+ const auditFile = join26(auditDir, `${connName}.jsonl`);
37776
38634
  return { auditDir, connectionName: connName, auditFile };
37777
38635
  }
37778
38636
  function isAuditDisabled(config) {
@@ -37814,7 +38672,7 @@ function renderTable(rows, headers) {
37814
38672
  const allRows = [headers, ...rows];
37815
38673
  const widths = headers.map((_, col) => Math.max(...allRows.map((r) => (r[col] ?? "").length)));
37816
38674
  const fmt = (r) => r.map((cell, i) => (cell ?? "").padEnd(widths[i] ?? 0)).join(" ").trimEnd();
37817
- const sep3 = widths.map((w) => "-".repeat(w)).join(" ").trimEnd();
38675
+ const sep3 = widths.map((w2) => "-".repeat(w2)).join(" ").trimEnd();
37818
38676
  return [fmt(headers), sep3, ...rows.map(fmt)].join(`
37819
38677
  `);
37820
38678
  }
@@ -38199,7 +39057,7 @@ init_config_binding();
38199
39057
  init_schema_path();
38200
39058
  init_config();
38201
39059
  init_integration_helper();
38202
- import { join as join26 } from "path";
39060
+ import { join as join27 } from "path";
38203
39061
  import { resolveSrv as resolveSrv2 } from "dns/promises";
38204
39062
  function requireSqlConnection9(connection) {
38205
39063
  if (!["postgresql", "mysql", "mariadb"].includes(connection.system)) {
@@ -38279,7 +39137,7 @@ var runDoctorChecks = {
38279
39137
  }
38280
39138
  },
38281
39139
  async checkConfigExists(configPath, existsFn) {
38282
- const exists = existsFn ? await existsFn(configPath) : await Bun.file(configPath).exists() || await Bun.file(join26(configPath, "config.json")).exists();
39140
+ const exists = existsFn ? await existsFn(configPath) : await Bun.file(configPath).exists() || await Bun.file(join27(configPath, "config.json")).exists();
38283
39141
  return {
38284
39142
  group: "Configuration",
38285
39143
  label: "Config exists",
@@ -38436,7 +39294,7 @@ var runDoctorChecks = {
38436
39294
  async checkV2Config(configPath) {
38437
39295
  const results = [];
38438
39296
  const storagePath = await resolveConfigStoragePath(configPath);
38439
- const configFile = Bun.file(join26(storagePath, "config.json"));
39297
+ const configFile = Bun.file(join27(storagePath, "config.json"));
38440
39298
  if (!await configFile.exists())
38441
39299
  return results;
38442
39300
  let raw;
@@ -38476,7 +39334,7 @@ var runDoctorChecks = {
38476
39334
  }
38477
39335
  for (const [name, conn] of Object.entries(config.connections)) {
38478
39336
  if (conn.envFile) {
38479
- const envPath = join26(storagePath, conn.envFile);
39337
+ const envPath = join27(storagePath, conn.envFile);
38480
39338
  const exists = await Bun.file(envPath).exists();
38481
39339
  results.push({
38482
39340
  group: "Configuration",
@@ -38695,7 +39553,7 @@ var doctorCommand = new Command("doctor").description("Run diagnostic checks on
38695
39553
  }
38696
39554
  try {
38697
39555
  const schemaConnName = await getSchemaIsolationConnectionName(configPath);
38698
- const indexPath = join26(resolveSchemaPath(storagePath, schemaConnName), "index.json");
39556
+ const indexPath = join27(resolveSchemaPath(storagePath, schemaConnName), "index.json");
38699
39557
  const indexFile = Bun.file(indexPath);
38700
39558
  let indexParsed = null;
38701
39559
  if (await indexFile.exists()) {
@@ -38749,7 +39607,7 @@ var doctorCommand = new Command("doctor").description("Run diagnostic checks on
38749
39607
 
38750
39608
  // src/commands/completion.ts
38751
39609
  init_colors();
38752
- import { join as join27 } from "path";
39610
+ import { join as join28 } from "path";
38753
39611
  import { homedir as homedir3 } from "os";
38754
39612
  function extractCommands(program2) {
38755
39613
  return program2.commands.map((cmd) => ({
@@ -38851,11 +39709,11 @@ function getInstallPath2(shell) {
38851
39709
  const home = homedir3();
38852
39710
  switch (shell) {
38853
39711
  case "bash":
38854
- return join27(home, ".bashrc");
39712
+ return join28(home, ".bashrc");
38855
39713
  case "zsh":
38856
- return join27(home, ".zshrc");
39714
+ return join28(home, ".zshrc");
38857
39715
  case "fish":
38858
- return join27(home, ".config", "fish", "completions", "dbcli.fish");
39716
+ return join28(home, ".config", "fish", "completions", "dbcli.fish");
38859
39717
  default:
38860
39718
  throw new Error(`Unsupported shell: ${shell}. Supported: bash, zsh, fish`);
38861
39719
  }
@@ -38875,7 +39733,7 @@ var MARKER_END = "# <<< dbcli completion <<<";
38875
39733
  async function installCompletion(shell, script) {
38876
39734
  const targetPath = getInstallPath2(shell);
38877
39735
  if (shell === "fish") {
38878
- const dir = join27(homedir3(), ".config", "fish", "completions");
39736
+ const dir = join28(homedir3(), ".config", "fish", "completions");
38879
39737
  await Bun.$`mkdir -p ${dir}`.quiet();
38880
39738
  await Bun.file(targetPath).write(script);
38881
39739
  console.log(colors.success(`\u2713 Fish completion installed to ${targetPath}`));
@@ -39108,7 +39966,7 @@ ${t("upgrade.failed")}`));
39108
39966
  init_config();
39109
39967
  init_adapters();
39110
39968
  import { createInterface as createInterface2 } from "readline";
39111
- import { join as join28 } from "path";
39969
+ import { join as join29 } from "path";
39112
39970
  import { homedir as homedir4 } from "os";
39113
39971
 
39114
39972
  // src/core/repl/types.ts
@@ -39505,6 +40363,7 @@ class HistoryManager {
39505
40363
 
39506
40364
  // src/core/repl/repl-engine.ts
39507
40365
  init_permission_guard();
40366
+ init_query_result_formatter();
39508
40367
  init_message_loader();
39509
40368
  var import_picocolors3 = __toESM(require_picocolors(), 1);
39510
40369
 
@@ -39839,7 +40698,7 @@ function requireSqlConnection10(connection) {
39839
40698
  }
39840
40699
  return connection;
39841
40700
  }
39842
- var HISTORY_PATH = join28(homedir4(), ".dbcli_history");
40701
+ var HISTORY_PATH = join29(homedir4(), ".dbcli_history");
39843
40702
  var MONGO_COMPLETION_EAGER_THRESHOLD = 20;
39844
40703
  async function populateMongoColumns(mongoAdapter, collectionNames, threshold = MONGO_COMPLETION_EAGER_THRESHOLD) {
39845
40704
  const columnsByTable = {};
@@ -40589,7 +41448,6 @@ class DDLExecutor {
40589
41448
  } catch {}
40590
41449
  }
40591
41450
  }
40592
-
40593
41451
  // src/commands/migrate.ts
40594
41452
  function requireSqlConnection11(connection) {
40595
41453
  if (!["postgresql", "mysql", "mariadb"].includes(connection.system)) {
@@ -40826,7 +41684,7 @@ init_config();
40826
41684
  init_errors();
40827
41685
  init_message_loader();
40828
41686
  init_config_binding();
40829
- import { join as join29 } from "path";
41687
+ import { join as join30 } from "path";
40830
41688
  async function switchDefault(configPath, name, config) {
40831
41689
  if (!config.connections[name]) {
40832
41690
  const available = Object.keys(config.connections).join(", ");
@@ -40849,7 +41707,7 @@ function listConnectionsForDisplay(config) {
40849
41707
  }
40850
41708
  async function ensureV2Config(configPath) {
40851
41709
  const storagePath = await resolveConfigStoragePath(configPath);
40852
- const configFile = Bun.file(join29(storagePath, "config.json"));
41710
+ const configFile = Bun.file(join30(storagePath, "config.json"));
40853
41711
  const legacyFile = Bun.file(configPath);
40854
41712
  if (!await configFile.exists() && !await legacyFile.exists()) {
40855
41713
  throw new ConfigError(t("init.config_not_found"));
@@ -40913,7 +41771,7 @@ var useCommand = new Command("use").description("Switch or display the default d
40913
41771
 
40914
41772
  // src/cli.ts
40915
41773
  init_config();
40916
- import { join as join30 } from "path";
41774
+ import { join as join31 } from "path";
40917
41775
  var _bgVersionCheckResult;
40918
41776
  function shouldSkipBackgroundChecks() {
40919
41777
  return process.env.DBCLI_NO_UPDATE_CHECK === "1" || process.env.DBCLI_NO_UPDATE_CHECK === "true" || false;
@@ -40942,7 +41800,7 @@ program2.hook("preAction", (thisCommand, actionCommand) => {
40942
41800
  try {
40943
41801
  let cache = null;
40944
41802
  try {
40945
- const cacheFile = Bun.file(join30(configPath, "version-check.json"));
41803
+ const cacheFile = Bun.file(join31(configPath, "version-check.json"));
40946
41804
  if (await cacheFile.exists()) {
40947
41805
  cache = await cacheFile.json();
40948
41806
  }