@carllee1983/dbcli 1.2.1 → 1.4.1

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
@@ -6124,14 +6124,8 @@ var init_validation = __esm(() => {
6124
6124
  EnvRefSchema = exports_external.object({
6125
6125
  $env: exports_external.string()
6126
6126
  }).strict();
6127
- StringOrEnvRef = exports_external.union([
6128
- exports_external.string().min(1),
6129
- EnvRefSchema
6130
- ]);
6131
- NumberOrEnvRef = exports_external.union([
6132
- exports_external.number().int().min(1).max(65535),
6133
- EnvRefSchema
6134
- ]);
6127
+ StringOrEnvRef = exports_external.union([exports_external.string().min(1), EnvRefSchema]);
6128
+ NumberOrEnvRef = exports_external.union([exports_external.number().int().min(1).max(65535), EnvRefSchema]);
6135
6129
  ConnectionConfigSchema = exports_external.object({
6136
6130
  system: exports_external.enum(["postgresql", "mysql", "mariadb"]),
6137
6131
  host: StringOrEnvRef,
@@ -6143,7 +6137,9 @@ var init_validation = __esm(() => {
6143
6137
  PermissionSchema = exports_external.enum(["query-only", "read-write", "data-admin", "admin"]).default("query-only");
6144
6138
  MetadataSchema = exports_external.object({
6145
6139
  createdAt: exports_external.string().datetime().optional(),
6146
- version: exports_external.string().default("1.0")
6140
+ version: exports_external.string().default("1.0"),
6141
+ schemaLastUpdated: exports_external.string().datetime().optional(),
6142
+ schemaTableCount: exports_external.number().int().nonnegative().optional()
6147
6143
  }).optional().default({});
6148
6144
  BlacklistConfigSchema = exports_external.object({
6149
6145
  tables: exports_external.array(exports_external.string()).default([]),
@@ -6163,11 +6159,1458 @@ var init_validation = __esm(() => {
6163
6159
  DbcliConfigV2Schema = exports_external.object({
6164
6160
  version: exports_external.literal(2),
6165
6161
  default: exports_external.string().min(1),
6166
- connections: exports_external.record(NamedConnectionSchema).refine((conns) => Object.keys(conns).length > 0, { message: "At least one connection is required" }),
6162
+ connections: exports_external.record(NamedConnectionSchema).refine((conns) => Object.keys(conns).length > 0, {
6163
+ message: "At least one connection is required"
6164
+ }),
6167
6165
  schema: exports_external.record(exports_external.any()).optional().default({}),
6166
+ schemas: exports_external.record(exports_external.record(exports_external.any())).optional().default({}),
6168
6167
  metadata: MetadataSchema,
6169
6168
  blacklist: BlacklistConfigSchema
6170
- }).refine((config) => (config.default in config.connections), { message: "Default connection must exist in connections", path: ["default"] });
6169
+ }).refine((config) => (config.default in config.connections), {
6170
+ message: "Default connection must exist in connections",
6171
+ path: ["default"]
6172
+ });
6173
+ });
6174
+
6175
+ // node_modules/lru-cache/dist/esm/index.js
6176
+ class Stack {
6177
+ heap;
6178
+ length;
6179
+ static #constructing = false;
6180
+ static create(max) {
6181
+ const HeapCls = getUintArray(max);
6182
+ if (!HeapCls)
6183
+ return [];
6184
+ Stack.#constructing = true;
6185
+ const s = new Stack(max, HeapCls);
6186
+ Stack.#constructing = false;
6187
+ return s;
6188
+ }
6189
+ constructor(max, HeapCls) {
6190
+ if (!Stack.#constructing) {
6191
+ throw new TypeError("instantiate Stack using Stack.create(n)");
6192
+ }
6193
+ this.heap = new HeapCls(max);
6194
+ this.length = 0;
6195
+ }
6196
+ push(n) {
6197
+ this.heap[this.length++] = n;
6198
+ }
6199
+ pop() {
6200
+ return this.heap[--this.length];
6201
+ }
6202
+ }
6203
+ var perf, warned, PROCESS, emitWarning = (msg, type, code, fn) => {
6204
+ typeof PROCESS.emitWarning === "function" ? PROCESS.emitWarning(msg, type, code, fn) : console.error(`[${code}] ${type}: ${msg}`);
6205
+ }, AC, AS, shouldWarn = (code) => !warned.has(code), TYPE, isPosInt = (n) => n && n === Math.floor(n) && n > 0 && isFinite(n), getUintArray = (max) => !isPosInt(max) ? null : max <= Math.pow(2, 8) ? Uint8Array : max <= Math.pow(2, 16) ? Uint16Array : max <= Math.pow(2, 32) ? Uint32Array : max <= Number.MAX_SAFE_INTEGER ? ZeroArray : null, ZeroArray, LRUCache;
6206
+ var init_esm = __esm(() => {
6207
+ perf = typeof performance === "object" && performance && typeof performance.now === "function" ? performance : Date;
6208
+ warned = new Set;
6209
+ PROCESS = typeof process === "object" && !!process ? process : {};
6210
+ AC = globalThis.AbortController;
6211
+ AS = globalThis.AbortSignal;
6212
+ if (typeof AC === "undefined") {
6213
+ AS = class AbortSignal2 {
6214
+ onabort;
6215
+ _onabort = [];
6216
+ reason;
6217
+ aborted = false;
6218
+ addEventListener(_, fn) {
6219
+ this._onabort.push(fn);
6220
+ }
6221
+ };
6222
+ AC = class AbortController2 {
6223
+ constructor() {
6224
+ warnACPolyfill();
6225
+ }
6226
+ signal = new AS;
6227
+ abort(reason) {
6228
+ if (this.signal.aborted)
6229
+ return;
6230
+ this.signal.reason = reason;
6231
+ this.signal.aborted = true;
6232
+ for (const fn of this.signal._onabort) {
6233
+ fn(reason);
6234
+ }
6235
+ this.signal.onabort?.(reason);
6236
+ }
6237
+ };
6238
+ let printACPolyfillWarning = PROCESS.env?.LRU_CACHE_IGNORE_AC_WARNING !== "1";
6239
+ const warnACPolyfill = () => {
6240
+ if (!printACPolyfillWarning)
6241
+ return;
6242
+ printACPolyfillWarning = false;
6243
+ emitWarning("AbortController is not defined. If using lru-cache in " + "node 14, load an AbortController polyfill from the " + "`node-abort-controller` package. A minimal polyfill is " + "provided for use by LRUCache.fetch(), but it should not be " + "relied upon in other contexts (eg, passing it to other APIs that " + "use AbortController/AbortSignal might have undesirable effects). " + "You may disable this with LRU_CACHE_IGNORE_AC_WARNING=1 in the env.", "NO_ABORT_CONTROLLER", "ENOTSUP", warnACPolyfill);
6244
+ };
6245
+ }
6246
+ TYPE = Symbol("type");
6247
+ ZeroArray = class ZeroArray extends Array {
6248
+ constructor(size) {
6249
+ super(size);
6250
+ this.fill(0);
6251
+ }
6252
+ };
6253
+ LRUCache = class LRUCache {
6254
+ #max;
6255
+ #maxSize;
6256
+ #dispose;
6257
+ #disposeAfter;
6258
+ #fetchMethod;
6259
+ #memoMethod;
6260
+ ttl;
6261
+ ttlResolution;
6262
+ ttlAutopurge;
6263
+ updateAgeOnGet;
6264
+ updateAgeOnHas;
6265
+ allowStale;
6266
+ noDisposeOnSet;
6267
+ noUpdateTTL;
6268
+ maxEntrySize;
6269
+ sizeCalculation;
6270
+ noDeleteOnFetchRejection;
6271
+ noDeleteOnStaleGet;
6272
+ allowStaleOnFetchAbort;
6273
+ allowStaleOnFetchRejection;
6274
+ ignoreFetchAbort;
6275
+ #size;
6276
+ #calculatedSize;
6277
+ #keyMap;
6278
+ #keyList;
6279
+ #valList;
6280
+ #next;
6281
+ #prev;
6282
+ #head;
6283
+ #tail;
6284
+ #free;
6285
+ #disposed;
6286
+ #sizes;
6287
+ #starts;
6288
+ #ttls;
6289
+ #hasDispose;
6290
+ #hasFetchMethod;
6291
+ #hasDisposeAfter;
6292
+ static unsafeExposeInternals(c) {
6293
+ return {
6294
+ starts: c.#starts,
6295
+ ttls: c.#ttls,
6296
+ sizes: c.#sizes,
6297
+ keyMap: c.#keyMap,
6298
+ keyList: c.#keyList,
6299
+ valList: c.#valList,
6300
+ next: c.#next,
6301
+ prev: c.#prev,
6302
+ get head() {
6303
+ return c.#head;
6304
+ },
6305
+ get tail() {
6306
+ return c.#tail;
6307
+ },
6308
+ free: c.#free,
6309
+ isBackgroundFetch: (p) => c.#isBackgroundFetch(p),
6310
+ backgroundFetch: (k, index, options, context) => c.#backgroundFetch(k, index, options, context),
6311
+ moveToTail: (index) => c.#moveToTail(index),
6312
+ indexes: (options) => c.#indexes(options),
6313
+ rindexes: (options) => c.#rindexes(options),
6314
+ isStale: (index) => c.#isStale(index)
6315
+ };
6316
+ }
6317
+ get max() {
6318
+ return this.#max;
6319
+ }
6320
+ get maxSize() {
6321
+ return this.#maxSize;
6322
+ }
6323
+ get calculatedSize() {
6324
+ return this.#calculatedSize;
6325
+ }
6326
+ get size() {
6327
+ return this.#size;
6328
+ }
6329
+ get fetchMethod() {
6330
+ return this.#fetchMethod;
6331
+ }
6332
+ get memoMethod() {
6333
+ return this.#memoMethod;
6334
+ }
6335
+ get dispose() {
6336
+ return this.#dispose;
6337
+ }
6338
+ get disposeAfter() {
6339
+ return this.#disposeAfter;
6340
+ }
6341
+ constructor(options) {
6342
+ const { max = 0, ttl, ttlResolution = 1, ttlAutopurge, updateAgeOnGet, updateAgeOnHas, allowStale, dispose, disposeAfter, noDisposeOnSet, noUpdateTTL, maxSize = 0, maxEntrySize = 0, sizeCalculation, fetchMethod, memoMethod, noDeleteOnFetchRejection, noDeleteOnStaleGet, allowStaleOnFetchRejection, allowStaleOnFetchAbort, ignoreFetchAbort } = options;
6343
+ if (max !== 0 && !isPosInt(max)) {
6344
+ throw new TypeError("max option must be a nonnegative integer");
6345
+ }
6346
+ const UintArray = max ? getUintArray(max) : Array;
6347
+ if (!UintArray) {
6348
+ throw new Error("invalid max value: " + max);
6349
+ }
6350
+ this.#max = max;
6351
+ this.#maxSize = maxSize;
6352
+ this.maxEntrySize = maxEntrySize || this.#maxSize;
6353
+ this.sizeCalculation = sizeCalculation;
6354
+ if (this.sizeCalculation) {
6355
+ if (!this.#maxSize && !this.maxEntrySize) {
6356
+ throw new TypeError("cannot set sizeCalculation without setting maxSize or maxEntrySize");
6357
+ }
6358
+ if (typeof this.sizeCalculation !== "function") {
6359
+ throw new TypeError("sizeCalculation set to non-function");
6360
+ }
6361
+ }
6362
+ if (memoMethod !== undefined && typeof memoMethod !== "function") {
6363
+ throw new TypeError("memoMethod must be a function if defined");
6364
+ }
6365
+ this.#memoMethod = memoMethod;
6366
+ if (fetchMethod !== undefined && typeof fetchMethod !== "function") {
6367
+ throw new TypeError("fetchMethod must be a function if specified");
6368
+ }
6369
+ this.#fetchMethod = fetchMethod;
6370
+ this.#hasFetchMethod = !!fetchMethod;
6371
+ this.#keyMap = new Map;
6372
+ this.#keyList = new Array(max).fill(undefined);
6373
+ this.#valList = new Array(max).fill(undefined);
6374
+ this.#next = new UintArray(max);
6375
+ this.#prev = new UintArray(max);
6376
+ this.#head = 0;
6377
+ this.#tail = 0;
6378
+ this.#free = Stack.create(max);
6379
+ this.#size = 0;
6380
+ this.#calculatedSize = 0;
6381
+ if (typeof dispose === "function") {
6382
+ this.#dispose = dispose;
6383
+ }
6384
+ if (typeof disposeAfter === "function") {
6385
+ this.#disposeAfter = disposeAfter;
6386
+ this.#disposed = [];
6387
+ } else {
6388
+ this.#disposeAfter = undefined;
6389
+ this.#disposed = undefined;
6390
+ }
6391
+ this.#hasDispose = !!this.#dispose;
6392
+ this.#hasDisposeAfter = !!this.#disposeAfter;
6393
+ this.noDisposeOnSet = !!noDisposeOnSet;
6394
+ this.noUpdateTTL = !!noUpdateTTL;
6395
+ this.noDeleteOnFetchRejection = !!noDeleteOnFetchRejection;
6396
+ this.allowStaleOnFetchRejection = !!allowStaleOnFetchRejection;
6397
+ this.allowStaleOnFetchAbort = !!allowStaleOnFetchAbort;
6398
+ this.ignoreFetchAbort = !!ignoreFetchAbort;
6399
+ if (this.maxEntrySize !== 0) {
6400
+ if (this.#maxSize !== 0) {
6401
+ if (!isPosInt(this.#maxSize)) {
6402
+ throw new TypeError("maxSize must be a positive integer if specified");
6403
+ }
6404
+ }
6405
+ if (!isPosInt(this.maxEntrySize)) {
6406
+ throw new TypeError("maxEntrySize must be a positive integer if specified");
6407
+ }
6408
+ this.#initializeSizeTracking();
6409
+ }
6410
+ this.allowStale = !!allowStale;
6411
+ this.noDeleteOnStaleGet = !!noDeleteOnStaleGet;
6412
+ this.updateAgeOnGet = !!updateAgeOnGet;
6413
+ this.updateAgeOnHas = !!updateAgeOnHas;
6414
+ this.ttlResolution = isPosInt(ttlResolution) || ttlResolution === 0 ? ttlResolution : 1;
6415
+ this.ttlAutopurge = !!ttlAutopurge;
6416
+ this.ttl = ttl || 0;
6417
+ if (this.ttl) {
6418
+ if (!isPosInt(this.ttl)) {
6419
+ throw new TypeError("ttl must be a positive integer if specified");
6420
+ }
6421
+ this.#initializeTTLTracking();
6422
+ }
6423
+ if (this.#max === 0 && this.ttl === 0 && this.#maxSize === 0) {
6424
+ throw new TypeError("At least one of max, maxSize, or ttl is required");
6425
+ }
6426
+ if (!this.ttlAutopurge && !this.#max && !this.#maxSize) {
6427
+ const code = "LRU_CACHE_UNBOUNDED";
6428
+ if (shouldWarn(code)) {
6429
+ warned.add(code);
6430
+ const msg = "TTL caching without ttlAutopurge, max, or maxSize can " + "result in unbounded memory consumption.";
6431
+ emitWarning(msg, "UnboundedCacheWarning", code, LRUCache);
6432
+ }
6433
+ }
6434
+ }
6435
+ getRemainingTTL(key) {
6436
+ return this.#keyMap.has(key) ? Infinity : 0;
6437
+ }
6438
+ #initializeTTLTracking() {
6439
+ const ttls = new ZeroArray(this.#max);
6440
+ const starts = new ZeroArray(this.#max);
6441
+ this.#ttls = ttls;
6442
+ this.#starts = starts;
6443
+ this.#setItemTTL = (index, ttl, start = perf.now()) => {
6444
+ starts[index] = ttl !== 0 ? start : 0;
6445
+ ttls[index] = ttl;
6446
+ if (ttl !== 0 && this.ttlAutopurge) {
6447
+ const t2 = setTimeout(() => {
6448
+ if (this.#isStale(index)) {
6449
+ this.#delete(this.#keyList[index], "expire");
6450
+ }
6451
+ }, ttl + 1);
6452
+ if (t2.unref) {
6453
+ t2.unref();
6454
+ }
6455
+ }
6456
+ };
6457
+ this.#updateItemAge = (index) => {
6458
+ starts[index] = ttls[index] !== 0 ? perf.now() : 0;
6459
+ };
6460
+ this.#statusTTL = (status, index) => {
6461
+ if (ttls[index]) {
6462
+ const ttl = ttls[index];
6463
+ const start = starts[index];
6464
+ if (!ttl || !start)
6465
+ return;
6466
+ status.ttl = ttl;
6467
+ status.start = start;
6468
+ status.now = cachedNow || getNow();
6469
+ const age = status.now - start;
6470
+ status.remainingTTL = ttl - age;
6471
+ }
6472
+ };
6473
+ let cachedNow = 0;
6474
+ const getNow = () => {
6475
+ const n = perf.now();
6476
+ if (this.ttlResolution > 0) {
6477
+ cachedNow = n;
6478
+ const t2 = setTimeout(() => cachedNow = 0, this.ttlResolution);
6479
+ if (t2.unref) {
6480
+ t2.unref();
6481
+ }
6482
+ }
6483
+ return n;
6484
+ };
6485
+ this.getRemainingTTL = (key) => {
6486
+ const index = this.#keyMap.get(key);
6487
+ if (index === undefined) {
6488
+ return 0;
6489
+ }
6490
+ const ttl = ttls[index];
6491
+ const start = starts[index];
6492
+ if (!ttl || !start) {
6493
+ return Infinity;
6494
+ }
6495
+ const age = (cachedNow || getNow()) - start;
6496
+ return ttl - age;
6497
+ };
6498
+ this.#isStale = (index) => {
6499
+ const s = starts[index];
6500
+ const t2 = ttls[index];
6501
+ return !!t2 && !!s && (cachedNow || getNow()) - s > t2;
6502
+ };
6503
+ }
6504
+ #updateItemAge = () => {};
6505
+ #statusTTL = () => {};
6506
+ #setItemTTL = () => {};
6507
+ #isStale = () => false;
6508
+ #initializeSizeTracking() {
6509
+ const sizes = new ZeroArray(this.#max);
6510
+ this.#calculatedSize = 0;
6511
+ this.#sizes = sizes;
6512
+ this.#removeItemSize = (index) => {
6513
+ this.#calculatedSize -= sizes[index];
6514
+ sizes[index] = 0;
6515
+ };
6516
+ this.#requireSize = (k, v, size, sizeCalculation) => {
6517
+ if (this.#isBackgroundFetch(v)) {
6518
+ return 0;
6519
+ }
6520
+ if (!isPosInt(size)) {
6521
+ if (sizeCalculation) {
6522
+ if (typeof sizeCalculation !== "function") {
6523
+ throw new TypeError("sizeCalculation must be a function");
6524
+ }
6525
+ size = sizeCalculation(v, k);
6526
+ if (!isPosInt(size)) {
6527
+ throw new TypeError("sizeCalculation return invalid (expect positive integer)");
6528
+ }
6529
+ } else {
6530
+ throw new TypeError("invalid size value (must be positive integer). " + "When maxSize or maxEntrySize is used, sizeCalculation " + "or size must be set.");
6531
+ }
6532
+ }
6533
+ return size;
6534
+ };
6535
+ this.#addItemSize = (index, size, status) => {
6536
+ sizes[index] = size;
6537
+ if (this.#maxSize) {
6538
+ const maxSize = this.#maxSize - sizes[index];
6539
+ while (this.#calculatedSize > maxSize) {
6540
+ this.#evict(true);
6541
+ }
6542
+ }
6543
+ this.#calculatedSize += sizes[index];
6544
+ if (status) {
6545
+ status.entrySize = size;
6546
+ status.totalCalculatedSize = this.#calculatedSize;
6547
+ }
6548
+ };
6549
+ }
6550
+ #removeItemSize = (_i) => {};
6551
+ #addItemSize = (_i, _s, _st) => {};
6552
+ #requireSize = (_k, _v, size, sizeCalculation) => {
6553
+ if (size || sizeCalculation) {
6554
+ throw new TypeError("cannot set size without setting maxSize or maxEntrySize on cache");
6555
+ }
6556
+ return 0;
6557
+ };
6558
+ *#indexes({ allowStale = this.allowStale } = {}) {
6559
+ if (this.#size) {
6560
+ for (let i = this.#tail;; ) {
6561
+ if (!this.#isValidIndex(i)) {
6562
+ break;
6563
+ }
6564
+ if (allowStale || !this.#isStale(i)) {
6565
+ yield i;
6566
+ }
6567
+ if (i === this.#head) {
6568
+ break;
6569
+ } else {
6570
+ i = this.#prev[i];
6571
+ }
6572
+ }
6573
+ }
6574
+ }
6575
+ *#rindexes({ allowStale = this.allowStale } = {}) {
6576
+ if (this.#size) {
6577
+ for (let i = this.#head;; ) {
6578
+ if (!this.#isValidIndex(i)) {
6579
+ break;
6580
+ }
6581
+ if (allowStale || !this.#isStale(i)) {
6582
+ yield i;
6583
+ }
6584
+ if (i === this.#tail) {
6585
+ break;
6586
+ } else {
6587
+ i = this.#next[i];
6588
+ }
6589
+ }
6590
+ }
6591
+ }
6592
+ #isValidIndex(index) {
6593
+ return index !== undefined && this.#keyMap.get(this.#keyList[index]) === index;
6594
+ }
6595
+ *entries() {
6596
+ for (const i of this.#indexes()) {
6597
+ if (this.#valList[i] !== undefined && this.#keyList[i] !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
6598
+ yield [this.#keyList[i], this.#valList[i]];
6599
+ }
6600
+ }
6601
+ }
6602
+ *rentries() {
6603
+ for (const i of this.#rindexes()) {
6604
+ if (this.#valList[i] !== undefined && this.#keyList[i] !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
6605
+ yield [this.#keyList[i], this.#valList[i]];
6606
+ }
6607
+ }
6608
+ }
6609
+ *keys() {
6610
+ for (const i of this.#indexes()) {
6611
+ const k = this.#keyList[i];
6612
+ if (k !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
6613
+ yield k;
6614
+ }
6615
+ }
6616
+ }
6617
+ *rkeys() {
6618
+ for (const i of this.#rindexes()) {
6619
+ const k = this.#keyList[i];
6620
+ if (k !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
6621
+ yield k;
6622
+ }
6623
+ }
6624
+ }
6625
+ *values() {
6626
+ for (const i of this.#indexes()) {
6627
+ const v = this.#valList[i];
6628
+ if (v !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
6629
+ yield this.#valList[i];
6630
+ }
6631
+ }
6632
+ }
6633
+ *rvalues() {
6634
+ for (const i of this.#rindexes()) {
6635
+ const v = this.#valList[i];
6636
+ if (v !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
6637
+ yield this.#valList[i];
6638
+ }
6639
+ }
6640
+ }
6641
+ [Symbol.iterator]() {
6642
+ return this.entries();
6643
+ }
6644
+ [Symbol.toStringTag] = "LRUCache";
6645
+ find(fn, getOptions = {}) {
6646
+ for (const i of this.#indexes()) {
6647
+ const v = this.#valList[i];
6648
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
6649
+ if (value === undefined)
6650
+ continue;
6651
+ if (fn(value, this.#keyList[i], this)) {
6652
+ return this.get(this.#keyList[i], getOptions);
6653
+ }
6654
+ }
6655
+ }
6656
+ forEach(fn, thisp = this) {
6657
+ for (const i of this.#indexes()) {
6658
+ const v = this.#valList[i];
6659
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
6660
+ if (value === undefined)
6661
+ continue;
6662
+ fn.call(thisp, value, this.#keyList[i], this);
6663
+ }
6664
+ }
6665
+ rforEach(fn, thisp = this) {
6666
+ for (const i of this.#rindexes()) {
6667
+ const v = this.#valList[i];
6668
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
6669
+ if (value === undefined)
6670
+ continue;
6671
+ fn.call(thisp, value, this.#keyList[i], this);
6672
+ }
6673
+ }
6674
+ purgeStale() {
6675
+ let deleted = false;
6676
+ for (const i of this.#rindexes({ allowStale: true })) {
6677
+ if (this.#isStale(i)) {
6678
+ this.#delete(this.#keyList[i], "expire");
6679
+ deleted = true;
6680
+ }
6681
+ }
6682
+ return deleted;
6683
+ }
6684
+ info(key) {
6685
+ const i = this.#keyMap.get(key);
6686
+ if (i === undefined)
6687
+ return;
6688
+ const v = this.#valList[i];
6689
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
6690
+ if (value === undefined)
6691
+ return;
6692
+ const entry = { value };
6693
+ if (this.#ttls && this.#starts) {
6694
+ const ttl = this.#ttls[i];
6695
+ const start = this.#starts[i];
6696
+ if (ttl && start) {
6697
+ const remain = ttl - (perf.now() - start);
6698
+ entry.ttl = remain;
6699
+ entry.start = Date.now();
6700
+ }
6701
+ }
6702
+ if (this.#sizes) {
6703
+ entry.size = this.#sizes[i];
6704
+ }
6705
+ return entry;
6706
+ }
6707
+ dump() {
6708
+ const arr = [];
6709
+ for (const i of this.#indexes({ allowStale: true })) {
6710
+ const key = this.#keyList[i];
6711
+ const v = this.#valList[i];
6712
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
6713
+ if (value === undefined || key === undefined)
6714
+ continue;
6715
+ const entry = { value };
6716
+ if (this.#ttls && this.#starts) {
6717
+ entry.ttl = this.#ttls[i];
6718
+ const age = perf.now() - this.#starts[i];
6719
+ entry.start = Math.floor(Date.now() - age);
6720
+ }
6721
+ if (this.#sizes) {
6722
+ entry.size = this.#sizes[i];
6723
+ }
6724
+ arr.unshift([key, entry]);
6725
+ }
6726
+ return arr;
6727
+ }
6728
+ load(arr) {
6729
+ this.clear();
6730
+ for (const [key, entry] of arr) {
6731
+ if (entry.start) {
6732
+ const age = Date.now() - entry.start;
6733
+ entry.start = perf.now() - age;
6734
+ }
6735
+ this.set(key, entry.value, entry);
6736
+ }
6737
+ }
6738
+ set(k, v, setOptions = {}) {
6739
+ if (v === undefined) {
6740
+ this.delete(k);
6741
+ return this;
6742
+ }
6743
+ const { ttl = this.ttl, start, noDisposeOnSet = this.noDisposeOnSet, sizeCalculation = this.sizeCalculation, status } = setOptions;
6744
+ let { noUpdateTTL = this.noUpdateTTL } = setOptions;
6745
+ const size = this.#requireSize(k, v, setOptions.size || 0, sizeCalculation);
6746
+ if (this.maxEntrySize && size > this.maxEntrySize) {
6747
+ if (status) {
6748
+ status.set = "miss";
6749
+ status.maxEntrySizeExceeded = true;
6750
+ }
6751
+ this.#delete(k, "set");
6752
+ return this;
6753
+ }
6754
+ let index = this.#size === 0 ? undefined : this.#keyMap.get(k);
6755
+ if (index === undefined) {
6756
+ index = this.#size === 0 ? this.#tail : this.#free.length !== 0 ? this.#free.pop() : this.#size === this.#max ? this.#evict(false) : this.#size;
6757
+ this.#keyList[index] = k;
6758
+ this.#valList[index] = v;
6759
+ this.#keyMap.set(k, index);
6760
+ this.#next[this.#tail] = index;
6761
+ this.#prev[index] = this.#tail;
6762
+ this.#tail = index;
6763
+ this.#size++;
6764
+ this.#addItemSize(index, size, status);
6765
+ if (status)
6766
+ status.set = "add";
6767
+ noUpdateTTL = false;
6768
+ } else {
6769
+ this.#moveToTail(index);
6770
+ const oldVal = this.#valList[index];
6771
+ if (v !== oldVal) {
6772
+ if (this.#hasFetchMethod && this.#isBackgroundFetch(oldVal)) {
6773
+ oldVal.__abortController.abort(new Error("replaced"));
6774
+ const { __staleWhileFetching: s } = oldVal;
6775
+ if (s !== undefined && !noDisposeOnSet) {
6776
+ if (this.#hasDispose) {
6777
+ this.#dispose?.(s, k, "set");
6778
+ }
6779
+ if (this.#hasDisposeAfter) {
6780
+ this.#disposed?.push([s, k, "set"]);
6781
+ }
6782
+ }
6783
+ } else if (!noDisposeOnSet) {
6784
+ if (this.#hasDispose) {
6785
+ this.#dispose?.(oldVal, k, "set");
6786
+ }
6787
+ if (this.#hasDisposeAfter) {
6788
+ this.#disposed?.push([oldVal, k, "set"]);
6789
+ }
6790
+ }
6791
+ this.#removeItemSize(index);
6792
+ this.#addItemSize(index, size, status);
6793
+ this.#valList[index] = v;
6794
+ if (status) {
6795
+ status.set = "replace";
6796
+ const oldValue = oldVal && this.#isBackgroundFetch(oldVal) ? oldVal.__staleWhileFetching : oldVal;
6797
+ if (oldValue !== undefined)
6798
+ status.oldValue = oldValue;
6799
+ }
6800
+ } else if (status) {
6801
+ status.set = "update";
6802
+ }
6803
+ }
6804
+ if (ttl !== 0 && !this.#ttls) {
6805
+ this.#initializeTTLTracking();
6806
+ }
6807
+ if (this.#ttls) {
6808
+ if (!noUpdateTTL) {
6809
+ this.#setItemTTL(index, ttl, start);
6810
+ }
6811
+ if (status)
6812
+ this.#statusTTL(status, index);
6813
+ }
6814
+ if (!noDisposeOnSet && this.#hasDisposeAfter && this.#disposed) {
6815
+ const dt = this.#disposed;
6816
+ let task;
6817
+ while (task = dt?.shift()) {
6818
+ this.#disposeAfter?.(...task);
6819
+ }
6820
+ }
6821
+ return this;
6822
+ }
6823
+ pop() {
6824
+ try {
6825
+ while (this.#size) {
6826
+ const val = this.#valList[this.#head];
6827
+ this.#evict(true);
6828
+ if (this.#isBackgroundFetch(val)) {
6829
+ if (val.__staleWhileFetching) {
6830
+ return val.__staleWhileFetching;
6831
+ }
6832
+ } else if (val !== undefined) {
6833
+ return val;
6834
+ }
6835
+ }
6836
+ } finally {
6837
+ if (this.#hasDisposeAfter && this.#disposed) {
6838
+ const dt = this.#disposed;
6839
+ let task;
6840
+ while (task = dt?.shift()) {
6841
+ this.#disposeAfter?.(...task);
6842
+ }
6843
+ }
6844
+ }
6845
+ }
6846
+ #evict(free) {
6847
+ const head = this.#head;
6848
+ const k = this.#keyList[head];
6849
+ const v = this.#valList[head];
6850
+ if (this.#hasFetchMethod && this.#isBackgroundFetch(v)) {
6851
+ v.__abortController.abort(new Error("evicted"));
6852
+ } else if (this.#hasDispose || this.#hasDisposeAfter) {
6853
+ if (this.#hasDispose) {
6854
+ this.#dispose?.(v, k, "evict");
6855
+ }
6856
+ if (this.#hasDisposeAfter) {
6857
+ this.#disposed?.push([v, k, "evict"]);
6858
+ }
6859
+ }
6860
+ this.#removeItemSize(head);
6861
+ if (free) {
6862
+ this.#keyList[head] = undefined;
6863
+ this.#valList[head] = undefined;
6864
+ this.#free.push(head);
6865
+ }
6866
+ if (this.#size === 1) {
6867
+ this.#head = this.#tail = 0;
6868
+ this.#free.length = 0;
6869
+ } else {
6870
+ this.#head = this.#next[head];
6871
+ }
6872
+ this.#keyMap.delete(k);
6873
+ this.#size--;
6874
+ return head;
6875
+ }
6876
+ has(k, hasOptions = {}) {
6877
+ const { updateAgeOnHas = this.updateAgeOnHas, status } = hasOptions;
6878
+ const index = this.#keyMap.get(k);
6879
+ if (index !== undefined) {
6880
+ const v = this.#valList[index];
6881
+ if (this.#isBackgroundFetch(v) && v.__staleWhileFetching === undefined) {
6882
+ return false;
6883
+ }
6884
+ if (!this.#isStale(index)) {
6885
+ if (updateAgeOnHas) {
6886
+ this.#updateItemAge(index);
6887
+ }
6888
+ if (status) {
6889
+ status.has = "hit";
6890
+ this.#statusTTL(status, index);
6891
+ }
6892
+ return true;
6893
+ } else if (status) {
6894
+ status.has = "stale";
6895
+ this.#statusTTL(status, index);
6896
+ }
6897
+ } else if (status) {
6898
+ status.has = "miss";
6899
+ }
6900
+ return false;
6901
+ }
6902
+ peek(k, peekOptions = {}) {
6903
+ const { allowStale = this.allowStale } = peekOptions;
6904
+ const index = this.#keyMap.get(k);
6905
+ if (index === undefined || !allowStale && this.#isStale(index)) {
6906
+ return;
6907
+ }
6908
+ const v = this.#valList[index];
6909
+ return this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
6910
+ }
6911
+ #backgroundFetch(k, index, options, context) {
6912
+ const v = index === undefined ? undefined : this.#valList[index];
6913
+ if (this.#isBackgroundFetch(v)) {
6914
+ return v;
6915
+ }
6916
+ const ac = new AC;
6917
+ const { signal } = options;
6918
+ signal?.addEventListener("abort", () => ac.abort(signal.reason), {
6919
+ signal: ac.signal
6920
+ });
6921
+ const fetchOpts = {
6922
+ signal: ac.signal,
6923
+ options,
6924
+ context
6925
+ };
6926
+ const cb = (v2, updateCache = false) => {
6927
+ const { aborted } = ac.signal;
6928
+ const ignoreAbort = options.ignoreFetchAbort && v2 !== undefined;
6929
+ if (options.status) {
6930
+ if (aborted && !updateCache) {
6931
+ options.status.fetchAborted = true;
6932
+ options.status.fetchError = ac.signal.reason;
6933
+ if (ignoreAbort)
6934
+ options.status.fetchAbortIgnored = true;
6935
+ } else {
6936
+ options.status.fetchResolved = true;
6937
+ }
6938
+ }
6939
+ if (aborted && !ignoreAbort && !updateCache) {
6940
+ return fetchFail(ac.signal.reason);
6941
+ }
6942
+ const bf2 = p;
6943
+ if (this.#valList[index] === p) {
6944
+ if (v2 === undefined) {
6945
+ if (bf2.__staleWhileFetching) {
6946
+ this.#valList[index] = bf2.__staleWhileFetching;
6947
+ } else {
6948
+ this.#delete(k, "fetch");
6949
+ }
6950
+ } else {
6951
+ if (options.status)
6952
+ options.status.fetchUpdated = true;
6953
+ this.set(k, v2, fetchOpts.options);
6954
+ }
6955
+ }
6956
+ return v2;
6957
+ };
6958
+ const eb = (er) => {
6959
+ if (options.status) {
6960
+ options.status.fetchRejected = true;
6961
+ options.status.fetchError = er;
6962
+ }
6963
+ return fetchFail(er);
6964
+ };
6965
+ const fetchFail = (er) => {
6966
+ const { aborted } = ac.signal;
6967
+ const allowStaleAborted = aborted && options.allowStaleOnFetchAbort;
6968
+ const allowStale = allowStaleAborted || options.allowStaleOnFetchRejection;
6969
+ const noDelete = allowStale || options.noDeleteOnFetchRejection;
6970
+ const bf2 = p;
6971
+ if (this.#valList[index] === p) {
6972
+ const del = !noDelete || bf2.__staleWhileFetching === undefined;
6973
+ if (del) {
6974
+ this.#delete(k, "fetch");
6975
+ } else if (!allowStaleAborted) {
6976
+ this.#valList[index] = bf2.__staleWhileFetching;
6977
+ }
6978
+ }
6979
+ if (allowStale) {
6980
+ if (options.status && bf2.__staleWhileFetching !== undefined) {
6981
+ options.status.returnedStale = true;
6982
+ }
6983
+ return bf2.__staleWhileFetching;
6984
+ } else if (bf2.__returned === bf2) {
6985
+ throw er;
6986
+ }
6987
+ };
6988
+ const pcall = (res, rej) => {
6989
+ const fmp = this.#fetchMethod?.(k, v, fetchOpts);
6990
+ if (fmp && fmp instanceof Promise) {
6991
+ fmp.then((v2) => res(v2 === undefined ? undefined : v2), rej);
6992
+ }
6993
+ ac.signal.addEventListener("abort", () => {
6994
+ if (!options.ignoreFetchAbort || options.allowStaleOnFetchAbort) {
6995
+ res(undefined);
6996
+ if (options.allowStaleOnFetchAbort) {
6997
+ res = (v2) => cb(v2, true);
6998
+ }
6999
+ }
7000
+ });
7001
+ };
7002
+ if (options.status)
7003
+ options.status.fetchDispatched = true;
7004
+ const p = new Promise(pcall).then(cb, eb);
7005
+ const bf = Object.assign(p, {
7006
+ __abortController: ac,
7007
+ __staleWhileFetching: v,
7008
+ __returned: undefined
7009
+ });
7010
+ if (index === undefined) {
7011
+ this.set(k, bf, { ...fetchOpts.options, status: undefined });
7012
+ index = this.#keyMap.get(k);
7013
+ } else {
7014
+ this.#valList[index] = bf;
7015
+ }
7016
+ return bf;
7017
+ }
7018
+ #isBackgroundFetch(p) {
7019
+ if (!this.#hasFetchMethod)
7020
+ return false;
7021
+ const b = p;
7022
+ return !!b && b instanceof Promise && b.hasOwnProperty("__staleWhileFetching") && b.__abortController instanceof AC;
7023
+ }
7024
+ async fetch(k, fetchOptions = {}) {
7025
+ const {
7026
+ allowStale = this.allowStale,
7027
+ updateAgeOnGet = this.updateAgeOnGet,
7028
+ noDeleteOnStaleGet = this.noDeleteOnStaleGet,
7029
+ ttl = this.ttl,
7030
+ noDisposeOnSet = this.noDisposeOnSet,
7031
+ size = 0,
7032
+ sizeCalculation = this.sizeCalculation,
7033
+ noUpdateTTL = this.noUpdateTTL,
7034
+ noDeleteOnFetchRejection = this.noDeleteOnFetchRejection,
7035
+ allowStaleOnFetchRejection = this.allowStaleOnFetchRejection,
7036
+ ignoreFetchAbort = this.ignoreFetchAbort,
7037
+ allowStaleOnFetchAbort = this.allowStaleOnFetchAbort,
7038
+ context,
7039
+ forceRefresh = false,
7040
+ status,
7041
+ signal
7042
+ } = fetchOptions;
7043
+ if (!this.#hasFetchMethod) {
7044
+ if (status)
7045
+ status.fetch = "get";
7046
+ return this.get(k, {
7047
+ allowStale,
7048
+ updateAgeOnGet,
7049
+ noDeleteOnStaleGet,
7050
+ status
7051
+ });
7052
+ }
7053
+ const options = {
7054
+ allowStale,
7055
+ updateAgeOnGet,
7056
+ noDeleteOnStaleGet,
7057
+ ttl,
7058
+ noDisposeOnSet,
7059
+ size,
7060
+ sizeCalculation,
7061
+ noUpdateTTL,
7062
+ noDeleteOnFetchRejection,
7063
+ allowStaleOnFetchRejection,
7064
+ allowStaleOnFetchAbort,
7065
+ ignoreFetchAbort,
7066
+ status,
7067
+ signal
7068
+ };
7069
+ let index = this.#keyMap.get(k);
7070
+ if (index === undefined) {
7071
+ if (status)
7072
+ status.fetch = "miss";
7073
+ const p = this.#backgroundFetch(k, index, options, context);
7074
+ return p.__returned = p;
7075
+ } else {
7076
+ const v = this.#valList[index];
7077
+ if (this.#isBackgroundFetch(v)) {
7078
+ const stale = allowStale && v.__staleWhileFetching !== undefined;
7079
+ if (status) {
7080
+ status.fetch = "inflight";
7081
+ if (stale)
7082
+ status.returnedStale = true;
7083
+ }
7084
+ return stale ? v.__staleWhileFetching : v.__returned = v;
7085
+ }
7086
+ const isStale = this.#isStale(index);
7087
+ if (!forceRefresh && !isStale) {
7088
+ if (status)
7089
+ status.fetch = "hit";
7090
+ this.#moveToTail(index);
7091
+ if (updateAgeOnGet) {
7092
+ this.#updateItemAge(index);
7093
+ }
7094
+ if (status)
7095
+ this.#statusTTL(status, index);
7096
+ return v;
7097
+ }
7098
+ const p = this.#backgroundFetch(k, index, options, context);
7099
+ const hasStale = p.__staleWhileFetching !== undefined;
7100
+ const staleVal = hasStale && allowStale;
7101
+ if (status) {
7102
+ status.fetch = isStale ? "stale" : "refresh";
7103
+ if (staleVal && isStale)
7104
+ status.returnedStale = true;
7105
+ }
7106
+ return staleVal ? p.__staleWhileFetching : p.__returned = p;
7107
+ }
7108
+ }
7109
+ async forceFetch(k, fetchOptions = {}) {
7110
+ const v = await this.fetch(k, fetchOptions);
7111
+ if (v === undefined)
7112
+ throw new Error("fetch() returned undefined");
7113
+ return v;
7114
+ }
7115
+ memo(k, memoOptions = {}) {
7116
+ const memoMethod = this.#memoMethod;
7117
+ if (!memoMethod) {
7118
+ throw new Error("no memoMethod provided to constructor");
7119
+ }
7120
+ const { context, forceRefresh, ...options } = memoOptions;
7121
+ const v = this.get(k, options);
7122
+ if (!forceRefresh && v !== undefined)
7123
+ return v;
7124
+ const vv = memoMethod(k, v, {
7125
+ options,
7126
+ context
7127
+ });
7128
+ this.set(k, vv, options);
7129
+ return vv;
7130
+ }
7131
+ get(k, getOptions = {}) {
7132
+ const { allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet, status } = getOptions;
7133
+ const index = this.#keyMap.get(k);
7134
+ if (index !== undefined) {
7135
+ const value = this.#valList[index];
7136
+ const fetching = this.#isBackgroundFetch(value);
7137
+ if (status)
7138
+ this.#statusTTL(status, index);
7139
+ if (this.#isStale(index)) {
7140
+ if (status)
7141
+ status.get = "stale";
7142
+ if (!fetching) {
7143
+ if (!noDeleteOnStaleGet) {
7144
+ this.#delete(k, "expire");
7145
+ }
7146
+ if (status && allowStale)
7147
+ status.returnedStale = true;
7148
+ return allowStale ? value : undefined;
7149
+ } else {
7150
+ if (status && allowStale && value.__staleWhileFetching !== undefined) {
7151
+ status.returnedStale = true;
7152
+ }
7153
+ return allowStale ? value.__staleWhileFetching : undefined;
7154
+ }
7155
+ } else {
7156
+ if (status)
7157
+ status.get = "hit";
7158
+ if (fetching) {
7159
+ return value.__staleWhileFetching;
7160
+ }
7161
+ this.#moveToTail(index);
7162
+ if (updateAgeOnGet) {
7163
+ this.#updateItemAge(index);
7164
+ }
7165
+ return value;
7166
+ }
7167
+ } else if (status) {
7168
+ status.get = "miss";
7169
+ }
7170
+ }
7171
+ #connect(p, n) {
7172
+ this.#prev[n] = p;
7173
+ this.#next[p] = n;
7174
+ }
7175
+ #moveToTail(index) {
7176
+ if (index !== this.#tail) {
7177
+ if (index === this.#head) {
7178
+ this.#head = this.#next[index];
7179
+ } else {
7180
+ this.#connect(this.#prev[index], this.#next[index]);
7181
+ }
7182
+ this.#connect(this.#tail, index);
7183
+ this.#tail = index;
7184
+ }
7185
+ }
7186
+ delete(k) {
7187
+ return this.#delete(k, "delete");
7188
+ }
7189
+ #delete(k, reason) {
7190
+ let deleted = false;
7191
+ if (this.#size !== 0) {
7192
+ const index = this.#keyMap.get(k);
7193
+ if (index !== undefined) {
7194
+ deleted = true;
7195
+ if (this.#size === 1) {
7196
+ this.#clear(reason);
7197
+ } else {
7198
+ this.#removeItemSize(index);
7199
+ const v = this.#valList[index];
7200
+ if (this.#isBackgroundFetch(v)) {
7201
+ v.__abortController.abort(new Error("deleted"));
7202
+ } else if (this.#hasDispose || this.#hasDisposeAfter) {
7203
+ if (this.#hasDispose) {
7204
+ this.#dispose?.(v, k, reason);
7205
+ }
7206
+ if (this.#hasDisposeAfter) {
7207
+ this.#disposed?.push([v, k, reason]);
7208
+ }
7209
+ }
7210
+ this.#keyMap.delete(k);
7211
+ this.#keyList[index] = undefined;
7212
+ this.#valList[index] = undefined;
7213
+ if (index === this.#tail) {
7214
+ this.#tail = this.#prev[index];
7215
+ } else if (index === this.#head) {
7216
+ this.#head = this.#next[index];
7217
+ } else {
7218
+ const pi = this.#prev[index];
7219
+ this.#next[pi] = this.#next[index];
7220
+ const ni = this.#next[index];
7221
+ this.#prev[ni] = this.#prev[index];
7222
+ }
7223
+ this.#size--;
7224
+ this.#free.push(index);
7225
+ }
7226
+ }
7227
+ }
7228
+ if (this.#hasDisposeAfter && this.#disposed?.length) {
7229
+ const dt = this.#disposed;
7230
+ let task;
7231
+ while (task = dt?.shift()) {
7232
+ this.#disposeAfter?.(...task);
7233
+ }
7234
+ }
7235
+ return deleted;
7236
+ }
7237
+ clear() {
7238
+ return this.#clear("delete");
7239
+ }
7240
+ #clear(reason) {
7241
+ for (const index of this.#rindexes({ allowStale: true })) {
7242
+ const v = this.#valList[index];
7243
+ if (this.#isBackgroundFetch(v)) {
7244
+ v.__abortController.abort(new Error("deleted"));
7245
+ } else {
7246
+ const k = this.#keyList[index];
7247
+ if (this.#hasDispose) {
7248
+ this.#dispose?.(v, k, reason);
7249
+ }
7250
+ if (this.#hasDisposeAfter) {
7251
+ this.#disposed?.push([v, k, reason]);
7252
+ }
7253
+ }
7254
+ }
7255
+ this.#keyMap.clear();
7256
+ this.#valList.fill(undefined);
7257
+ this.#keyList.fill(undefined);
7258
+ if (this.#ttls && this.#starts) {
7259
+ this.#ttls.fill(0);
7260
+ this.#starts.fill(0);
7261
+ }
7262
+ if (this.#sizes) {
7263
+ this.#sizes.fill(0);
7264
+ }
7265
+ this.#head = 0;
7266
+ this.#tail = 0;
7267
+ this.#free.length = 0;
7268
+ this.#calculatedSize = 0;
7269
+ this.#size = 0;
7270
+ if (this.#hasDisposeAfter && this.#disposed) {
7271
+ const dt = this.#disposed;
7272
+ let task;
7273
+ while (task = dt?.shift()) {
7274
+ this.#disposeAfter?.(...task);
7275
+ }
7276
+ }
7277
+ }
7278
+ };
7279
+ });
7280
+
7281
+ // src/utils/schema-path.ts
7282
+ import { join as join2 } from "path";
7283
+ function resolveSchemaPath(dbcliPath, connectionName) {
7284
+ if (!connectionName)
7285
+ return join2(dbcliPath, "schemas");
7286
+ return join2(dbcliPath, "schemas", connectionName);
7287
+ }
7288
+ var init_schema_path = () => {};
7289
+
7290
+ // src/core/schema-cache.ts
7291
+ import { join as join3 } from "path";
7292
+
7293
+ class SchemaCacheManager {
7294
+ cache;
7295
+ index = null;
7296
+ hotSchemas = new Map;
7297
+ dbcliPath;
7298
+ schemaRoot;
7299
+ maxItems;
7300
+ maxSize;
7301
+ constructor(dbcliPath, options) {
7302
+ this.dbcliPath = dbcliPath;
7303
+ this.schemaRoot = resolveSchemaPath(dbcliPath, options?.connectionName);
7304
+ this.maxItems = options?.maxCacheItems || 100;
7305
+ this.maxSize = options?.maxCacheSize || 52428800;
7306
+ this.cache = new LRUCache({
7307
+ max: this.maxItems,
7308
+ maxSize: this.maxSize,
7309
+ sizeCalculation: (schema) => JSON.stringify(schema).length,
7310
+ allowStale: false,
7311
+ updateAgeOnGet: true,
7312
+ updateAgeOnHas: false
7313
+ });
7314
+ }
7315
+ async initialize() {
7316
+ try {
7317
+ const indexPath = join3(this.schemaRoot, "index.json");
7318
+ const indexFile = Bun.file(indexPath);
7319
+ if (await indexFile.exists()) {
7320
+ const indexContent = await indexFile.text();
7321
+ this.index = JSON.parse(indexContent);
7322
+ }
7323
+ const hotPath = join3(this.schemaRoot, "hot-schemas.json");
7324
+ const hotFile = Bun.file(hotPath);
7325
+ if (await hotFile.exists()) {
7326
+ const hotContent = await hotFile.text();
7327
+ const hotData = JSON.parse(hotContent);
7328
+ const schemasObj = hotData.schemas || hotData;
7329
+ for (const [tableName, schema] of Object.entries(schemasObj)) {
7330
+ const tableSchema = schema;
7331
+ this.hotSchemas.set(tableName, tableSchema);
7332
+ this.cache.set(tableName, tableSchema);
7333
+ }
7334
+ }
7335
+ } catch (error) {
7336
+ console.error("Failed to load schema index/hot-schemas:", error);
7337
+ this.index = null;
7338
+ }
7339
+ }
7340
+ async getTableSchema(tableName) {
7341
+ if (this.hotSchemas.has(tableName)) {
7342
+ return this.hotSchemas.get(tableName);
7343
+ }
7344
+ const cached = this.cache.get(tableName);
7345
+ if (cached) {
7346
+ return cached;
7347
+ }
7348
+ if (!this.index) {
7349
+ return null;
7350
+ }
7351
+ const tableInfo = this.index.tables[tableName];
7352
+ if (!tableInfo) {
7353
+ return null;
7354
+ }
7355
+ try {
7356
+ const filePath = join3(this.schemaRoot, tableInfo.file);
7357
+ const file = Bun.file(filePath);
7358
+ if (!await file.exists()) {
7359
+ console.error(`Cold table file not found: ${tableInfo.file} for table ${tableName}`);
7360
+ return null;
7361
+ }
7362
+ const content = await file.text();
7363
+ const data = JSON.parse(content);
7364
+ const schema = data.schemas?.[tableName] || data[tableName];
7365
+ if (schema) {
7366
+ this.cache.set(tableName, schema);
7367
+ }
7368
+ return schema || null;
7369
+ } catch (error) {
7370
+ console.error(`Failed to load cold schema for table ${tableName}:`, error);
7371
+ return null;
7372
+ }
7373
+ }
7374
+ async findFieldsByName(fieldName) {
7375
+ const results = [];
7376
+ for (const [tableName, schema] of this.hotSchemas.entries()) {
7377
+ const column = schema.columns.find((c) => c.name === fieldName);
7378
+ if (column) {
7379
+ results.push({ table: tableName, column });
7380
+ }
7381
+ }
7382
+ return results;
7383
+ }
7384
+ invalidateTable(tableName) {
7385
+ this.hotSchemas.delete(tableName);
7386
+ this.cache.delete(tableName);
7387
+ }
7388
+ refreshTable(tableName, schema) {
7389
+ this.hotSchemas.set(tableName, schema);
7390
+ this.cache.set(tableName, schema);
7391
+ }
7392
+ getStats() {
7393
+ const cacheSize = this.cache.calculatedSize || 0;
7394
+ const cacheHitRate = this.cache.max && this.cache.max > 0 ? Math.round(this.cache.size / this.cache.max * 100) : 0;
7395
+ return {
7396
+ hotTables: this.hotSchemas.size,
7397
+ cachedTables: this.cache.size,
7398
+ cacheSize,
7399
+ cacheHitRate: `${cacheHitRate}%`,
7400
+ maxItems: this.maxItems,
7401
+ maxSize: this.maxSize
7402
+ };
7403
+ }
7404
+ }
7405
+ var init_schema_cache = __esm(() => {
7406
+ init_esm();
7407
+ init_schema_path();
7408
+ });
7409
+
7410
+ // src/core/schema-index.ts
7411
+ import { join as join4 } from "path";
7412
+
7413
+ class SchemaIndexBuilder {
7414
+ static async loadIndex(dbcliPath, connectionName) {
7415
+ try {
7416
+ const indexPath = join4(resolveSchemaPath(dbcliPath, connectionName), "index.json");
7417
+ const file = Bun.file(indexPath);
7418
+ if (!await file.exists()) {
7419
+ return null;
7420
+ }
7421
+ const content = await file.text();
7422
+ return JSON.parse(content);
7423
+ } catch (error) {
7424
+ console.error("Failed to load schema index:", error);
7425
+ return null;
7426
+ }
7427
+ }
7428
+ static async buildIndex(config, options) {
7429
+ const hotTableThreshold = options?.hotTableThreshold || 20;
7430
+ const schema = config.schema || {};
7431
+ const tableEntries = Object.entries(schema).map(([tableName, tableData]) => {
7432
+ const size = JSON.stringify(tableData).length;
7433
+ return { tableName, size, data: tableData };
7434
+ });
7435
+ tableEntries.sort((a, b) => b.size - a.size);
7436
+ const hotCount = Math.max(1, Math.ceil(tableEntries.length * hotTableThreshold / 100));
7437
+ const hotTableNames = tableEntries.slice(0, hotCount).map((e) => e.tableName);
7438
+ const index = {
7439
+ tables: {},
7440
+ hotTables: hotTableNames,
7441
+ metadata: {
7442
+ version: "1.0",
7443
+ lastRefreshed: new Date().toISOString(),
7444
+ totalTables: tableEntries.length
7445
+ }
7446
+ };
7447
+ for (const { tableName, size } of tableEntries) {
7448
+ const location = hotTableNames.includes(tableName) ? "hot" : "cold";
7449
+ const file = location === "hot" ? "hot-schemas.json" : `cold/${this.getFileForTable(tableName)}`;
7450
+ index.tables[tableName] = {
7451
+ location,
7452
+ file,
7453
+ estimatedSize: size,
7454
+ lastModified: new Date().toISOString()
7455
+ };
7456
+ }
7457
+ return index;
7458
+ }
7459
+ static async saveIndex(dbcliPath, index, connectionName) {
7460
+ try {
7461
+ const schemasDir = resolveSchemaPath(dbcliPath, connectionName);
7462
+ await this.ensureDir(schemasDir);
7463
+ const indexPath = join4(schemasDir, "index.json");
7464
+ const indexFile = Bun.file(indexPath);
7465
+ await indexFile.write(JSON.stringify(index, null, 2));
7466
+ } catch (error) {
7467
+ throw new Error(`Failed to save schema index: ${error}`);
7468
+ }
7469
+ }
7470
+ static calculateFileMapping(index) {
7471
+ const mapping = {
7472
+ hot: [],
7473
+ cold: []
7474
+ };
7475
+ for (const [tableName, tableInfo] of Object.entries(index.tables)) {
7476
+ mapping[tableInfo.location].push({
7477
+ table: tableName,
7478
+ file: tableInfo.file
7479
+ });
7480
+ }
7481
+ return mapping;
7482
+ }
7483
+ static async ensureDir(dirPath) {
7484
+ const { mkdirSync } = await import("fs");
7485
+ try {
7486
+ mkdirSync(dirPath, { recursive: true });
7487
+ } catch (error) {
7488
+ if (error.code !== "EEXIST") {
7489
+ throw new Error(`Failed to ensure directory ${dirPath}: ${error}`);
7490
+ }
7491
+ }
7492
+ }
7493
+ static getFileForTable(tableName) {
7494
+ if (/^(old_|legacy_|archive_)/.test(tableName)) {
7495
+ return "legacy.json";
7496
+ }
7497
+ return "infrequent.json";
7498
+ }
7499
+ }
7500
+ var init_schema_index = __esm(() => {
7501
+ init_schema_path();
7502
+ });
7503
+
7504
+ // src/core/schema-loader.ts
7505
+ var exports_schema_loader = {};
7506
+ __export(exports_schema_loader, {
7507
+ SchemaLayeredLoader: () => SchemaLayeredLoader2
7508
+ });
7509
+ import { join as join5 } from "path";
7510
+
7511
+ class SchemaLayeredLoader2 {
7512
+ dbcliPath;
7513
+ connectionName;
7514
+ options;
7515
+ cache = null;
7516
+ index = null;
7517
+ loadTime = 0;
7518
+ constructor(dbcliPath, options) {
7519
+ this.dbcliPath = dbcliPath;
7520
+ this.connectionName = options?.connectionName;
7521
+ this.options = {
7522
+ maxCacheItems: options?.maxCacheItems || 100,
7523
+ maxCacheSize: options?.maxCacheSize || 52428800,
7524
+ hotTableThreshold: options?.hotTableThreshold || 20,
7525
+ enableStreaming: options?.enableStreaming || false,
7526
+ streamingTimeout: options?.streamingTimeout || 30000
7527
+ };
7528
+ }
7529
+ async initialize() {
7530
+ const startTime = performance.now();
7531
+ try {
7532
+ await this.ensureDirectories();
7533
+ this.index = await SchemaIndexBuilder.loadIndex(this.dbcliPath, this.connectionName);
7534
+ this.cache = new SchemaCacheManager(this.dbcliPath, {
7535
+ maxCacheItems: this.options.maxCacheItems,
7536
+ maxCacheSize: this.options.maxCacheSize,
7537
+ connectionName: this.connectionName
7538
+ });
7539
+ await this.cache.initialize();
7540
+ this.loadTime = performance.now() - startTime;
7541
+ if (this.loadTime > 50) {
7542
+ console.warn(`Schema initialization took ${this.loadTime.toFixed(2)}ms (target: < 100ms)`);
7543
+ }
7544
+ return {
7545
+ cache: this.cache,
7546
+ index: this.index,
7547
+ loadTime: this.loadTime
7548
+ };
7549
+ } catch (error) {
7550
+ console.error("Failed to initialize schema loader:", error);
7551
+ this.loadTime = performance.now() - startTime;
7552
+ if (!this.cache) {
7553
+ this.cache = new SchemaCacheManager(this.dbcliPath, {
7554
+ maxCacheItems: this.options.maxCacheItems,
7555
+ maxCacheSize: this.options.maxCacheSize,
7556
+ connectionName: this.connectionName
7557
+ });
7558
+ }
7559
+ return {
7560
+ cache: this.cache,
7561
+ index: this.index,
7562
+ loadTime: this.loadTime
7563
+ };
7564
+ }
7565
+ }
7566
+ async loadColdTable(tableName, cache) {
7567
+ try {
7568
+ return await cache.getTableSchema(tableName);
7569
+ } catch (error) {
7570
+ console.error(`Failed to load cold table ${tableName}:`, error);
7571
+ return null;
7572
+ }
7573
+ }
7574
+ async ensureDirectories() {
7575
+ const base = resolveSchemaPath(this.dbcliPath, this.connectionName);
7576
+ const dirs = [base, join5(base, "cold")];
7577
+ for (const dir of dirs) {
7578
+ try {
7579
+ const dirFile = Bun.file(dir);
7580
+ if (!await dirFile.exists()) {
7581
+ const proc = Bun.spawn(["mkdir", "-p", dir]);
7582
+ const exitCode = await proc.exited;
7583
+ if (exitCode !== 0) {
7584
+ console.error(`Failed to create directory: ${dir}`);
7585
+ }
7586
+ }
7587
+ } catch (error) {
7588
+ console.error(`Error ensuring directory ${dir}:`, error);
7589
+ }
7590
+ }
7591
+ }
7592
+ getBenchmark() {
7593
+ if (!this.cache || !this.index) {
7594
+ return {
7595
+ initTime: this.loadTime,
7596
+ hotTables: 0,
7597
+ totalTables: 0,
7598
+ estimatedSize: 0
7599
+ };
7600
+ }
7601
+ const estimatedSize = Object.values(this.index.tables).reduce((sum, table) => sum + (table.estimatedSize || 0), 0);
7602
+ return {
7603
+ initTime: this.loadTime,
7604
+ hotTables: this.index.hotTables.length,
7605
+ totalTables: this.index.metadata.totalTables,
7606
+ estimatedSize
7607
+ };
7608
+ }
7609
+ }
7610
+ var init_schema_loader = __esm(() => {
7611
+ init_schema_cache();
7612
+ init_schema_index();
7613
+ init_schema_path();
6171
7614
  });
6172
7615
 
6173
7616
  // node_modules/@inquirer/core/dist/esm/lib/key.mjs
@@ -6427,7 +7870,7 @@ function isUnicodeSupported() {
6427
7870
  return Boolean(process2.env["WT_SESSION"]) || Boolean(process2.env["TERMINUS_SUBLIME"]) || process2.env["ConEmuTask"] === "{cmd::Cmder}" || process2.env["TERM_PROGRAM"] === "Terminus-Sublime" || process2.env["TERM_PROGRAM"] === "vscode" || process2.env["TERM"] === "xterm-256color" || process2.env["TERM"] === "alacritty" || process2.env["TERMINAL_EMULATOR"] === "JetBrains-JediTerm";
6428
7871
  }
6429
7872
  var common, specialMainSymbols, specialFallbackSymbols, mainSymbols, fallbackSymbols, shouldUseMain, figures, esm_default, replacements;
6430
- var init_esm = __esm(() => {
7873
+ var init_esm2 = __esm(() => {
6431
7874
  common = {
6432
7875
  circleQuestionMark: "(?)",
6433
7876
  questionMarkPrefix: "(?)",
@@ -6713,7 +8156,7 @@ var init_esm = __esm(() => {
6713
8156
  // node_modules/@inquirer/core/dist/esm/lib/theme.mjs
6714
8157
  var import_yoctocolors_cjs, defaultTheme;
6715
8158
  var init_theme = __esm(() => {
6716
- init_esm();
8159
+ init_esm2();
6717
8160
  import_yoctocolors_cjs = __toESM(require_yoctocolors_cjs(), 1);
6718
8161
  defaultTheme = {
6719
8162
  prefix: {
@@ -8925,12 +10368,12 @@ class Separator {
8925
10368
  }
8926
10369
  var import_yoctocolors_cjs2;
8927
10370
  var init_Separator = __esm(() => {
8928
- init_esm();
10371
+ init_esm2();
8929
10372
  import_yoctocolors_cjs2 = __toESM(require_yoctocolors_cjs(), 1);
8930
10373
  });
8931
10374
 
8932
10375
  // node_modules/@inquirer/core/dist/esm/index.mjs
8933
- var init_esm2 = __esm(() => {
10376
+ var init_esm3 = __esm(() => {
8934
10377
  init_use_prefix();
8935
10378
  init_use_state();
8936
10379
  init_use_effect();
@@ -8984,10 +10427,10 @@ function normalizeChoices(choices) {
8984
10427
  });
8985
10428
  }
8986
10429
  var import_yoctocolors_cjs3, import_ansi_escapes2, checkboxTheme, esm_default2;
8987
- var init_esm3 = __esm(() => {
8988
- init_esm2();
8989
- init_esm();
10430
+ var init_esm4 = __esm(() => {
10431
+ init_esm3();
8990
10432
  init_esm2();
10433
+ init_esm3();
8991
10434
  import_yoctocolors_cjs3 = __toESM(require_yoctocolors_cjs(), 1);
8992
10435
  import_ansi_escapes2 = __toESM(require_ansi_escapes(), 1);
8993
10436
  checkboxTheme = {
@@ -18489,8 +19932,8 @@ var require_main = __commonJS((exports) => {
18489
19932
  // node_modules/@inquirer/editor/dist/esm/index.mjs
18490
19933
  import { AsyncResource as AsyncResource4 } from "async_hooks";
18491
19934
  var import_external_editor, esm_default3;
18492
- var init_esm4 = __esm(() => {
18493
- init_esm2();
19935
+ var init_esm5 = __esm(() => {
19936
+ init_esm3();
18494
19937
  import_external_editor = __toESM(require_main(), 1);
18495
19938
  esm_default3 = createPrompt((config, done) => {
18496
19939
  const { waitForUseInput = true, postfix = ".txt", validate = () => true } = config;
@@ -18553,8 +19996,8 @@ var init_esm4 = __esm(() => {
18553
19996
 
18554
19997
  // node_modules/@inquirer/confirm/dist/esm/index.mjs
18555
19998
  var esm_default4;
18556
- var init_esm5 = __esm(() => {
18557
- init_esm2();
19999
+ var init_esm6 = __esm(() => {
20000
+ init_esm3();
18558
20001
  esm_default4 = createPrompt((config, done) => {
18559
20002
  const { transformer = (answer) => answer ? "yes" : "no" } = config;
18560
20003
  const [status, setStatus] = useState("pending");
@@ -18589,8 +20032,8 @@ var init_esm5 = __esm(() => {
18589
20032
 
18590
20033
  // node_modules/@inquirer/input/dist/esm/index.mjs
18591
20034
  var esm_default5;
18592
- var init_esm6 = __esm(() => {
18593
- init_esm2();
20035
+ var init_esm7 = __esm(() => {
20036
+ init_esm3();
18594
20037
  esm_default5 = createPrompt((config, done) => {
18595
20038
  const { required, validate = () => true } = config;
18596
20039
  const theme = makeTheme(config.theme);
@@ -18669,8 +20112,8 @@ function validateNumber(value, { min, max, step }) {
18669
20112
  return true;
18670
20113
  }
18671
20114
  var esm_default6;
18672
- var init_esm7 = __esm(() => {
18673
- init_esm2();
20115
+ var init_esm8 = __esm(() => {
20116
+ init_esm3();
18674
20117
  esm_default6 = createPrompt((config, done) => {
18675
20118
  const { validate = () => true, min = -Infinity, max = Infinity, step = 1, required = false } = config;
18676
20119
  const theme = makeTheme(config.theme);
@@ -18753,8 +20196,8 @@ function normalizeChoices2(choices) {
18753
20196
  });
18754
20197
  }
18755
20198
  var import_yoctocolors_cjs4, helpChoice, esm_default7;
18756
- var init_esm8 = __esm(() => {
18757
- init_esm2();
20199
+ var init_esm9 = __esm(() => {
20200
+ init_esm3();
18758
20201
  import_yoctocolors_cjs4 = __toESM(require_yoctocolors_cjs(), 1);
18759
20202
  helpChoice = {
18760
20203
  key: "h",
@@ -18867,8 +20310,8 @@ function normalizeChoices3(choices) {
18867
20310
  });
18868
20311
  }
18869
20312
  var import_yoctocolors_cjs5, numberRegex, esm_default8;
18870
- var init_esm9 = __esm(() => {
18871
- init_esm2();
20313
+ var init_esm10 = __esm(() => {
20314
+ init_esm3();
18872
20315
  import_yoctocolors_cjs5 = __toESM(require_yoctocolors_cjs(), 1);
18873
20316
  numberRegex = /\d+/;
18874
20317
  esm_default8 = createPrompt((config, done) => {
@@ -18930,8 +20373,8 @@ var init_esm9 = __esm(() => {
18930
20373
 
18931
20374
  // node_modules/@inquirer/password/dist/esm/index.mjs
18932
20375
  var import_ansi_escapes3, esm_default9;
18933
- var init_esm10 = __esm(() => {
18934
- init_esm2();
20376
+ var init_esm11 = __esm(() => {
20377
+ init_esm3();
18935
20378
  import_ansi_escapes3 = __toESM(require_ansi_escapes(), 1);
18936
20379
  esm_default9 = createPrompt((config, done) => {
18937
20380
  const { validate = () => true } = config;
@@ -19010,9 +20453,9 @@ function normalizeChoices4(choices) {
19010
20453
  });
19011
20454
  }
19012
20455
  var import_yoctocolors_cjs6, searchTheme, esm_default10;
19013
- var init_esm11 = __esm(() => {
20456
+ var init_esm12 = __esm(() => {
20457
+ init_esm3();
19014
20458
  init_esm2();
19015
- init_esm();
19016
20459
  import_yoctocolors_cjs6 = __toESM(require_yoctocolors_cjs(), 1);
19017
20460
  searchTheme = {
19018
20461
  icon: { cursor: esm_default.pointer },
@@ -19179,9 +20622,9 @@ function normalizeChoices5(choices) {
19179
20622
  });
19180
20623
  }
19181
20624
  var import_yoctocolors_cjs7, import_ansi_escapes4, selectTheme, esm_default11;
19182
- var init_esm12 = __esm(() => {
20625
+ var init_esm13 = __esm(() => {
20626
+ init_esm3();
19183
20627
  init_esm2();
19184
- init_esm();
19185
20628
  import_yoctocolors_cjs7 = __toESM(require_yoctocolors_cjs(), 1);
19186
20629
  import_ansi_escapes4 = __toESM(require_ansi_escapes(), 1);
19187
20630
  selectTheme = {
@@ -19312,8 +20755,7 @@ __export(exports_esm, {
19312
20755
  checkbox: () => esm_default2,
19313
20756
  Separator: () => Separator
19314
20757
  });
19315
- var init_esm13 = __esm(() => {
19316
- init_esm3();
20758
+ var init_esm14 = __esm(() => {
19317
20759
  init_esm4();
19318
20760
  init_esm5();
19319
20761
  init_esm6();
@@ -19323,6 +20765,7 @@ var init_esm13 = __esm(() => {
19323
20765
  init_esm10();
19324
20766
  init_esm11();
19325
20767
  init_esm12();
20768
+ init_esm13();
19326
20769
  });
19327
20770
 
19328
20771
  // node_modules/postgres-array/index.js
@@ -43012,7 +44455,7 @@ var require_named_placeholders = __commonJS((exports, module) => {
43012
44455
  }
43013
44456
  return s;
43014
44457
  }
43015
- function join3(tree) {
44458
+ function join7(tree) {
43016
44459
  if (tree.length === 1) {
43017
44460
  return tree;
43018
44461
  }
@@ -43038,7 +44481,7 @@ var require_named_placeholders = __commonJS((exports, module) => {
43038
44481
  if (cache && (tree = cache.get(query))) {
43039
44482
  return toArrayParams(tree, paramsObj);
43040
44483
  }
43041
- tree = join3(parse(query));
44484
+ tree = join7(parse(query));
43042
44485
  if (cache) {
43043
44486
  cache.set(query, tree);
43044
44487
  }
@@ -46864,7 +48307,7 @@ var {
46864
48307
  // package.json
46865
48308
  var package_default = {
46866
48309
  name: "@carllee1983/dbcli",
46867
- version: "1.2.1",
48310
+ version: "1.4.1",
46868
48311
  description: "Database CLI for AI agents",
46869
48312
  type: "module",
46870
48313
  publishConfig: {
@@ -47066,9 +48509,27 @@ Hint: run 'export {envKey}=<value>' and retry`
47066
48509
  formats: "Supported formats: json, csv",
47067
48510
  exported: "Exported {count} rows to {file}"
47068
48511
  },
48512
+ upgrade: {
48513
+ description: "Check for updates and upgrade dbcli to the latest version",
48514
+ checking: "Checking for updates...",
48515
+ already_up_to_date: "\u2713 Already up to date (v{version})",
48516
+ new_version_available: "New version available: v{version}",
48517
+ current_version: "Current version : v{version}",
48518
+ latest_version: "Latest version : v{version}",
48519
+ update_hint: '[INFO] dbcli v{version} available. Run "dbcli upgrade" to upgrade.',
48520
+ install_hint: 'Run "dbcli upgrade" to install the update.',
48521
+ upgrading: "Upgrading...",
48522
+ success: "\u2713 Successfully upgraded to v{version}",
48523
+ skill_recheck_hint: 'Hint: If you have installed AI skills, run "dbcli skill --install <platform>" to ensure they are up to date.',
48524
+ failed: "\u2717 Upgrade failed. Try running manually:",
48525
+ manual_hint: "bun add -g @carllee1983/dbcli@latest",
48526
+ network_error: "\u26A0 Could not check for updates (network error or registry unavailable)"
48527
+ },
47069
48528
  skill: {
47070
48529
  description: "Generate AI skill documentation",
47071
- installed: "Skill installed to {path}"
48530
+ installed: "Skill installed to {path}",
48531
+ update_available: "Skill updates available for the following platforms:",
48532
+ update_hint: 'Run "dbcli skill --install <platform>" to update.'
47072
48533
  },
47073
48534
  blacklist: {
47074
48535
  description: "Manage sensitive data blacklist to prevent AI access",
@@ -47238,9 +48699,27 @@ var messages_default2 = {
47238
48699
  formats: "\u652F\u63F4\u7684\u683C\u5F0F\uFF1Ajson\u3001csv",
47239
48700
  exported: "\u5DF2\u5C07 {count} \u5217\u532F\u51FA\u81F3 {file}"
47240
48701
  },
48702
+ upgrade: {
48703
+ description: "\u6AA2\u67E5\u66F4\u65B0\u4E26\u5C07 dbcli \u5347\u7D1A\u81F3\u6700\u65B0\u7248\u672C",
48704
+ checking: "\u6B63\u5728\u6AA2\u67E5\u66F4\u65B0...",
48705
+ already_up_to_date: "\u2713 \u5DF2\u7D93\u662F\u6700\u65B0\u7248\u672C (v{version})",
48706
+ new_version_available: "\u767C\u73FE\u65B0\u7248\u672C\uFF1Av{version}",
48707
+ current_version: "\u76EE\u524D\u7248\u672C\uFF1Av{version}",
48708
+ latest_version: "\u6700\u65B0\u7248\u672C\uFF1Av{version}",
48709
+ update_hint: '[\u63D0\u793A] dbcli v{version} \u5DF2\u767C\u5E03\u3002\u57F7\u884C "dbcli upgrade" \u9032\u884C\u5347\u7D1A\u3002',
48710
+ install_hint: '\u57F7\u884C "dbcli upgrade" \u5B89\u88DD\u66F4\u65B0\u3002',
48711
+ upgrading: "\u6B63\u5728\u5347\u7D1A...",
48712
+ success: "\u2713 \u6210\u529F\u5347\u7D1A\u81F3 v{version}",
48713
+ skill_recheck_hint: '\u63D0\u793A\uFF1A\u82E5\u5DF2\u5B89\u88DD AI \u6280\u80FD\uFF0C\u8ACB\u57F7\u884C "dbcli skill --install <platform>" \u78BA\u8A8D\u6280\u80FD\u70BA\u6700\u65B0\u7248\u672C\u3002',
48714
+ failed: "\u2717 \u5347\u7D1A\u5931\u6557\u3002\u8ACB\u5617\u8A66\u624B\u52D5\u57F7\u884C\uFF1A",
48715
+ manual_hint: "bun add -g @carllee1983/dbcli@latest",
48716
+ network_error: "\u26A0 \u7121\u6CD5\u6AA2\u67E5\u66F4\u65B0\uFF08\u7DB2\u8DEF\u932F\u8AA4\u6216\u8A3B\u518A\u8868\u4E0D\u53EF\u7528\uFF09"
48717
+ },
47241
48718
  skill: {
47242
48719
  description: "\u751F\u6210 AI \u6280\u80FD\u6587\u6A94",
47243
- installed: "\u6280\u80FD\u5DF2\u5B89\u88DD\u81F3 {path}"
48720
+ installed: "\u6280\u80FD\u5DF2\u5B89\u88DD\u81F3 {path}",
48721
+ update_available: "\u4EE5\u4E0B\u5E73\u53F0\u7684\u6280\u80FD\u9700\u8981\u66F4\u65B0\uFF1A",
48722
+ update_hint: '\u57F7\u884C "dbcli skill --install <platform>" \u9032\u884C\u66F4\u65B0\u3002'
47244
48723
  },
47245
48724
  blacklist: {
47246
48725
  description: "\u7BA1\u7406\u654F\u611F\u8CC7\u6599\u9ED1\u540D\u55AE\u4EE5\u9632\u6B62 AI \u5B58\u53D6",
@@ -47444,7 +48923,7 @@ function getLogger() {
47444
48923
  }
47445
48924
 
47446
48925
  // src/commands/init.ts
47447
- import { join as join3 } from "path";
48926
+ import { join as join7 } from "path";
47448
48927
 
47449
48928
  // src/utils/errors.ts
47450
48929
  class EnvParseError extends Error {
@@ -47631,13 +49110,51 @@ async function writeV2Config(path, config) {
47631
49110
  const json = JSON.stringify(config, null, 2);
47632
49111
  await Bun.write(configPath, json);
47633
49112
  }
49113
+ async function patchConnectionSchema(dbcliPath, connectionName, schema, metadataUpdate) {
49114
+ const v2Config = await readV2Config(dbcliPath);
49115
+ const updated = {
49116
+ ...v2Config,
49117
+ schemas: {
49118
+ ...v2Config.schemas,
49119
+ [connectionName]: schema
49120
+ },
49121
+ metadata: {
49122
+ ...v2Config.metadata,
49123
+ ...metadataUpdate ?? {}
49124
+ }
49125
+ };
49126
+ await writeV2Config(dbcliPath, updated);
49127
+ }
47634
49128
 
47635
49129
  // src/core/config.ts
47636
- import { join as join2 } from "path";
49130
+ import { join as join6 } from "path";
47637
49131
  var _globalConnectionName;
47638
49132
  function setGlobalConnectionName(name) {
47639
49133
  _globalConnectionName = name;
47640
49134
  }
49135
+ function getGlobalConnectionName() {
49136
+ return _globalConnectionName;
49137
+ }
49138
+ async function getSchemaIsolationConnectionName(dbcliPath) {
49139
+ const effectiveName = getGlobalConnectionName();
49140
+ try {
49141
+ const stat = await Bun.file(dbcliPath).stat();
49142
+ const isDirectory = stat?.isDirectory() ?? false;
49143
+ if (!isDirectory)
49144
+ return;
49145
+ const configJsonPath = join6(dbcliPath, "config.json");
49146
+ const configFile = Bun.file(configJsonPath);
49147
+ if (!await configFile.exists())
49148
+ return;
49149
+ const raw = JSON.parse(await configFile.text());
49150
+ if (detectConfigVersion(raw) !== 2)
49151
+ return;
49152
+ const v2Config = DbcliConfigV2Schema.parse(raw);
49153
+ return resolveConnection(v2Config, effectiveName).name;
49154
+ } catch {
49155
+ return;
49156
+ }
49157
+ }
47641
49158
  var DEFAULT_CONFIG = {
47642
49159
  connection: {
47643
49160
  system: "postgresql",
@@ -47705,7 +49222,7 @@ var configModule = {
47705
49222
  isDirectory = false;
47706
49223
  }
47707
49224
  if (isDirectory) {
47708
- const configPath = join2(path, "config.json");
49225
+ const configPath = join6(path, "config.json");
47709
49226
  const configFile = Bun.file(configPath);
47710
49227
  const configExists = await configFile.exists();
47711
49228
  if (configExists) {
@@ -47715,7 +49232,7 @@ var configModule = {
47715
49232
  const v2Config = DbcliConfigV2Schema.parse(config);
47716
49233
  const resolved = resolveConnection(v2Config, effectiveConnectionName);
47717
49234
  await loadConnectionEnv(resolved, path);
47718
- const envLocalPath = join2(path, ".env.local");
49235
+ const envLocalPath = join6(path, ".env.local");
47719
49236
  const envLocalFile = Bun.file(envLocalPath);
47720
49237
  let legacyPassword = null;
47721
49238
  if (await envLocalFile.exists()) {
@@ -47729,16 +49246,35 @@ var configModule = {
47729
49246
  if (!resolvedConnection.password && legacyPassword) {
47730
49247
  resolvedConnection.password = legacyPassword;
47731
49248
  }
49249
+ let schema = (v2Config.schemas ?? {})[resolved.name] ?? v2Config.schema;
49250
+ try {
49251
+ const { SchemaLayeredLoader: SchemaLayeredLoader3 } = await Promise.resolve().then(() => (init_schema_loader(), exports_schema_loader));
49252
+ const loader = new SchemaLayeredLoader3(path, { connectionName: resolved.name });
49253
+ const { cache, index } = await loader.initialize();
49254
+ if (index && Object.keys(index.tables).length > 0) {
49255
+ const layeredSchema = {};
49256
+ for (const tableName of Object.keys(index.tables)) {
49257
+ const s = await cache.getTableSchema(tableName);
49258
+ if (s)
49259
+ layeredSchema[tableName] = s;
49260
+ }
49261
+ if (Object.keys(layeredSchema).length > 0) {
49262
+ schema = layeredSchema;
49263
+ }
49264
+ }
49265
+ } catch (error) {
49266
+ console.warn("Warning: Failed to load layered schema cache, falling back to config.json");
49267
+ }
47732
49268
  return DbcliConfigSchema.parse({
47733
49269
  connection: resolvedConnection,
47734
49270
  permission: resolved.permission,
47735
- schema: v2Config.schema,
49271
+ schema,
47736
49272
  metadata: v2Config.metadata,
47737
49273
  blacklist: v2Config.blacklist
47738
49274
  });
47739
49275
  }
47740
49276
  const resolvedConfig = resolveEnvReferences(config, process.env, undefined, false);
47741
- const envPath = join2(path, ".env.local");
49277
+ const envPath = join6(path, ".env.local");
47742
49278
  const envFile = Bun.file(envPath);
47743
49279
  if (await envFile.exists()) {
47744
49280
  const envContent = await envFile.text();
@@ -47805,7 +49341,7 @@ var configModule = {
47805
49341
  if (isDirectory || path.endsWith(".dbcli")) {
47806
49342
  const hasEnvReferences = isEnvReference(config.connection.password);
47807
49343
  if (hasEnvReferences) {
47808
- const configPath = join2(path, "config.json");
49344
+ const configPath = join6(path, "config.json");
47809
49345
  const configJson = JSON.stringify(config, null, 2);
47810
49346
  await Bun.file(configPath).write(configJson);
47811
49347
  } else {
@@ -47818,11 +49354,11 @@ var configModule = {
47818
49354
  }
47819
49355
  };
47820
49356
  delete configWithoutPassword.connection.password;
47821
- const configPath = join2(path, "config.json");
49357
+ const configPath = join6(path, "config.json");
47822
49358
  const configJson = JSON.stringify(configWithoutPassword, null, 2);
47823
49359
  await Bun.file(configPath).write(configJson);
47824
49360
  if (password) {
47825
- const envPath = join2(path, ".env.local");
49361
+ const envPath = join6(path, ".env.local");
47826
49362
  const envContent = `# Database Credentials - DO NOT commit to git
47827
49363
 
47828
49364
  DBCLI_PASSWORD=${password}
@@ -47879,7 +49415,7 @@ async function text(message, defaultValue) {
47879
49415
  return answer.trim() || defaultValue || "";
47880
49416
  }
47881
49417
  try {
47882
- const { text: inquirerText } = await Promise.resolve().then(() => (init_esm13(), exports_esm));
49418
+ const { text: inquirerText } = await Promise.resolve().then(() => (init_esm14(), exports_esm));
47883
49419
  return await inquirerText({ message, default: defaultValue });
47884
49420
  } catch {
47885
49421
  const displayMessage = defaultValue ? `${message} [${defaultValue}]: ` : `${message}: `;
@@ -47901,7 +49437,7 @@ async function select(message, choices) {
47901
49437
  return choices[0];
47902
49438
  }
47903
49439
  try {
47904
- const { select: inquirerSelect } = await Promise.resolve().then(() => (init_esm13(), exports_esm));
49440
+ const { select: inquirerSelect } = await Promise.resolve().then(() => (init_esm14(), exports_esm));
47905
49441
  return await inquirerSelect({ message, choices });
47906
49442
  } catch {
47907
49443
  console.log(message);
@@ -47922,7 +49458,7 @@ async function confirm(message) {
47922
49458
  return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
47923
49459
  }
47924
49460
  try {
47925
- const { confirm: inquirerConfirm } = await Promise.resolve().then(() => (init_esm13(), exports_esm));
49461
+ const { confirm: inquirerConfirm } = await Promise.resolve().then(() => (init_esm14(), exports_esm));
47926
49462
  return await inquirerConfirm({ message });
47927
49463
  } catch {
47928
49464
  const answer = await readLineFromStdin(`${message} (y/n): `);
@@ -48618,7 +50154,7 @@ class AdapterFactory {
48618
50154
  var VALID_PERMISSIONS = ["query-only", "read-write", "data-admin", "admin"];
48619
50155
  async function checkOverwrite(configPath, shouldPrompt, force) {
48620
50156
  const fileExists = await Bun.file(configPath).exists();
48621
- const dirConfigExists = await Bun.file(join3(configPath, "config.json")).exists();
50157
+ const dirConfigExists = await Bun.file(join7(configPath, "config.json")).exists();
48622
50158
  if (!fileExists && !dirConfigExists || force)
48623
50159
  return true;
48624
50160
  if (shouldPrompt) {
@@ -48632,7 +50168,7 @@ async function checkOverwrite(configPath, shouldPrompt, force) {
48632
50168
  throw new Error(t("init.config_exists_use_force"));
48633
50169
  }
48634
50170
  async function handleRemove(configPath, name) {
48635
- const configFile = Bun.file(join3(configPath, "config.json"));
50171
+ const configFile = Bun.file(join7(configPath, "config.json"));
48636
50172
  if (!await configFile.exists()) {
48637
50173
  throw new Error(t("init.config_not_found"));
48638
50174
  }
@@ -48667,7 +50203,7 @@ async function handleRename(configPath, renameArg) {
48667
50203
  if (!oldName || !newName) {
48668
50204
  throw new Error(t("init.rename_invalid_format"));
48669
50205
  }
48670
- const configFile = Bun.file(join3(configPath, "config.json"));
50206
+ const configFile = Bun.file(join7(configPath, "config.json"));
48671
50207
  if (!await configFile.exists()) {
48672
50208
  throw new Error(t("init.config_not_found"));
48673
50209
  }
@@ -48692,7 +50228,7 @@ async function handleRename(configPath, renameArg) {
48692
50228
  console.log(t_vars("init.connection_renamed", { oldName, newName }));
48693
50229
  }
48694
50230
  async function writeV2InitConfig(configPath, connectionName, connection, permission, envFile) {
48695
- const configJsonPath = join3(configPath, "config.json");
50231
+ const configJsonPath = join7(configPath, "config.json");
48696
50232
  const configFile = Bun.file(configJsonPath);
48697
50233
  let existingV2 = null;
48698
50234
  if (await configFile.exists()) {
@@ -49266,6 +50802,582 @@ class SchemaDiffEngine {
49266
50802
  }
49267
50803
  }
49268
50804
 
50805
+ // src/core/index.ts
50806
+ init_schema_loader();
50807
+ init_schema_index();
50808
+ init_schema_cache();
50809
+
50810
+ // src/core/schema-writer.ts
50811
+ init_schema_index();
50812
+ import { join as join8 } from "path";
50813
+
50814
+ // src/core/atomic-writer.ts
50815
+ class AtomicFileWriter {
50816
+ async write(filePath, content, options) {
50817
+ const startTime = Date.now();
50818
+ const timeout = options?.timeout || 5000;
50819
+ const createBackup = options?.createBackup ?? true;
50820
+ const timestamp = Date.now();
50821
+ const tempPath = `${filePath}.${timestamp}.tmp`;
50822
+ const backupPath = `${filePath}.backup.${timestamp}`;
50823
+ let backupCreated = false;
50824
+ let tempFileCreated = false;
50825
+ try {
50826
+ this.checkTimeout(startTime, timeout, "write initialization");
50827
+ const buffer = typeof content === "string" ? Buffer.from(content, "utf-8") : content;
50828
+ const tempFile = Bun.file(tempPath);
50829
+ await Bun.write(tempFile, buffer);
50830
+ tempFileCreated = true;
50831
+ this.checkTimeout(startTime, timeout, "temp file write");
50832
+ if (createBackup) {
50833
+ const originalFile = Bun.file(filePath);
50834
+ if (await originalFile.exists()) {
50835
+ const originalContent = await originalFile.arrayBuffer();
50836
+ const backupFile = Bun.file(backupPath);
50837
+ await Bun.write(backupFile, originalContent);
50838
+ backupCreated = true;
50839
+ }
50840
+ }
50841
+ this.checkTimeout(startTime, timeout, "backup creation");
50842
+ const moveResult = await Bun.spawn(["mv", tempPath, filePath]).exited;
50843
+ if (moveResult !== 0) {
50844
+ throw new Error(`Atomic rename failed with exit code ${moveResult}`);
50845
+ }
50846
+ tempFileCreated = false;
50847
+ const writtenFile = Bun.file(filePath);
50848
+ const written = await writtenFile.exists();
50849
+ if (!written) {
50850
+ throw new Error("Verification failed: file not found after write");
50851
+ }
50852
+ const sizeBytes = writtenFile.size || buffer.length;
50853
+ return {
50854
+ filePath,
50855
+ sizeBytes,
50856
+ timestamp: new Date().toISOString(),
50857
+ backupCreated,
50858
+ backupPath: backupCreated ? backupPath : undefined
50859
+ };
50860
+ } catch (error) {
50861
+ await this.cleanup(tempPath, tempFileCreated);
50862
+ throw new Error(`Atomic write failed: ${error instanceof Error ? error.message : String(error)}`);
50863
+ }
50864
+ }
50865
+ async writeJSON(filePath, data, options) {
50866
+ const jsonContent = JSON.stringify(data, null, 2);
50867
+ return this.write(filePath, jsonContent, options);
50868
+ }
50869
+ async read(filePath, timeout = 5000) {
50870
+ const startTime = Date.now();
50871
+ this.checkTimeout(startTime, timeout, "read start");
50872
+ const file = Bun.file(filePath);
50873
+ if (!await file.exists()) {
50874
+ throw new Error(`File not found: ${filePath}`);
50875
+ }
50876
+ this.checkTimeout(startTime, timeout, "file existence check");
50877
+ return await file.text();
50878
+ }
50879
+ async backup(filePath) {
50880
+ const file = Bun.file(filePath);
50881
+ if (!await file.exists()) {
50882
+ return null;
50883
+ }
50884
+ const timestamp = Date.now();
50885
+ const backupPath = `${filePath}.backup.${timestamp}`;
50886
+ const content = await file.arrayBuffer();
50887
+ const backupFile = Bun.file(backupPath);
50888
+ await Bun.write(backupFile, content);
50889
+ return backupPath;
50890
+ }
50891
+ async restore(backupPath, targetPath) {
50892
+ try {
50893
+ const backupFile = Bun.file(backupPath);
50894
+ if (!await backupFile.exists()) {
50895
+ throw new Error(`Backup file not found: ${backupPath}`);
50896
+ }
50897
+ const content = await backupFile.arrayBuffer();
50898
+ const result = await this.write(targetPath, Buffer.from(content), {
50899
+ createBackup: false
50900
+ });
50901
+ return !!result;
50902
+ } catch (error) {
50903
+ throw new Error(`File restore failed: ${error instanceof Error ? error.message : String(error)}`);
50904
+ }
50905
+ }
50906
+ checkTimeout(startTime, timeout, stage) {
50907
+ const elapsed = Date.now() - startTime;
50908
+ if (elapsed > timeout) {
50909
+ throw new Error(`Operation timeout exceeded at ${stage}: ${elapsed}ms > ${timeout}ms`);
50910
+ }
50911
+ }
50912
+ async cleanup(tempPath, tempExists) {
50913
+ if (!tempExists)
50914
+ return;
50915
+ try {
50916
+ const tempFile = Bun.file(tempPath);
50917
+ if (await tempFile.exists()) {
50918
+ await Bun.spawn(["rm", "-f", tempPath]).exited;
50919
+ }
50920
+ } catch (error) {
50921
+ console.error(`Cleanup failed for temp file ${tempPath}:`, error);
50922
+ }
50923
+ }
50924
+ }
50925
+
50926
+ // src/core/schema-writer.ts
50927
+ init_schema_path();
50928
+
50929
+ class SchemaWriter {
50930
+ dbcliPath;
50931
+ writer;
50932
+ constructor(dbcliPath) {
50933
+ this.dbcliPath = dbcliPath;
50934
+ this.writer = new AtomicFileWriter;
50935
+ }
50936
+ async save(schema, connectionName) {
50937
+ const schemaRoot = resolveSchemaPath(this.dbcliPath, connectionName);
50938
+ const index = await SchemaIndexBuilder.buildIndex({ schema });
50939
+ await SchemaIndexBuilder.saveIndex(this.dbcliPath, index, connectionName);
50940
+ const mapping = SchemaIndexBuilder.calculateFileMapping(index);
50941
+ const hotSchemas = {};
50942
+ for (const item of mapping.hot) {
50943
+ hotSchemas[item.table] = schema[item.table];
50944
+ }
50945
+ await this.writer.writeJSON(join8(schemaRoot, "hot-schemas.json"), hotSchemas);
50946
+ const coldGroups = {};
50947
+ for (const item of mapping.cold) {
50948
+ if (!coldGroups[item.file]) {
50949
+ coldGroups[item.file] = {};
50950
+ }
50951
+ coldGroups[item.file][item.table] = schema[item.table];
50952
+ }
50953
+ const coldDir = join8(schemaRoot, "cold");
50954
+ await this.ensureDir(coldDir);
50955
+ for (const [fileName, tables] of Object.entries(coldGroups)) {
50956
+ const filePath = join8(schemaRoot, fileName);
50957
+ await this.writer.writeJSON(filePath, tables);
50958
+ }
50959
+ }
50960
+ async clear(connectionName) {
50961
+ const schemaRoot = resolveSchemaPath(this.dbcliPath, connectionName);
50962
+ try {
50963
+ const dir = Bun.file(schemaRoot);
50964
+ const s = await dir.stat();
50965
+ if (s.isDirectory()) {
50966
+ await Bun.spawn(["rm", "-rf", schemaRoot]).exited;
50967
+ }
50968
+ } catch {}
50969
+ }
50970
+ async ensureDir(path) {
50971
+ const { mkdirSync } = await import("fs");
50972
+ try {
50973
+ mkdirSync(path, { recursive: true });
50974
+ } catch (error) {
50975
+ if (error.code !== "EEXIST") {
50976
+ throw new Error(`Error ensuring directory ${path}: ${error}`);
50977
+ }
50978
+ }
50979
+ }
50980
+ }
50981
+ // src/core/column-index.ts
50982
+ class ColumnIndexBuilder {
50983
+ index = {
50984
+ columns: new Map,
50985
+ totalColumns: 0,
50986
+ totalTables: 0,
50987
+ timestamp: new Date().toISOString()
50988
+ };
50989
+ build(schemas) {
50990
+ this.index = {
50991
+ columns: new Map,
50992
+ totalColumns: 0,
50993
+ totalTables: Object.keys(schemas).length,
50994
+ timestamp: new Date().toISOString()
50995
+ };
50996
+ for (const [tableName, table] of Object.entries(schemas)) {
50997
+ for (const column of table.columns) {
50998
+ this.addColumnEntry(tableName, column);
50999
+ }
51000
+ }
51001
+ return this.index;
51002
+ }
51003
+ addColumnEntry(tableName, column) {
51004
+ const colName = column.name.toLowerCase();
51005
+ if (!this.index.columns.has(colName)) {
51006
+ this.index.columns.set(colName, {
51007
+ name: column.name,
51008
+ tables: []
51009
+ });
51010
+ this.index.totalColumns++;
51011
+ }
51012
+ const entry = this.index.columns.get(colName);
51013
+ entry.tables.push({
51014
+ tableName,
51015
+ column
51016
+ });
51017
+ }
51018
+ findColumn(columnName) {
51019
+ const entry = this.index.columns.get(columnName.toLowerCase());
51020
+ return entry?.tables || [];
51021
+ }
51022
+ findColumnsMatching(pattern) {
51023
+ const regex = typeof pattern === "string" ? new RegExp(pattern, "i") : pattern;
51024
+ const results = [];
51025
+ for (const [colName, entry] of this.index.columns.entries()) {
51026
+ if (regex.test(entry.name)) {
51027
+ results.push({
51028
+ columnName: entry.name,
51029
+ tables: entry.tables
51030
+ });
51031
+ }
51032
+ }
51033
+ return results;
51034
+ }
51035
+ getTableColumns(tableName) {
51036
+ const results = [];
51037
+ for (const entry of this.index.columns.values()) {
51038
+ const tableEntry = entry.tables.find((t2) => t2.tableName === tableName);
51039
+ if (tableEntry) {
51040
+ results.push(tableEntry.column);
51041
+ }
51042
+ }
51043
+ return results;
51044
+ }
51045
+ findColumnsByType(type) {
51046
+ const results = [];
51047
+ const typePattern = new RegExp(type, "i");
51048
+ for (const entry of this.index.columns.values()) {
51049
+ for (const tableEntry of entry.tables) {
51050
+ if (typePattern.test(tableEntry.column.type)) {
51051
+ results.push(tableEntry);
51052
+ }
51053
+ }
51054
+ }
51055
+ return results;
51056
+ }
51057
+ findPrimaryKeys() {
51058
+ const results = [];
51059
+ for (const entry of this.index.columns.values()) {
51060
+ for (const tableEntry of entry.tables) {
51061
+ if (tableEntry.column.primaryKey) {
51062
+ results.push(tableEntry);
51063
+ }
51064
+ }
51065
+ }
51066
+ return results;
51067
+ }
51068
+ findNullableColumns() {
51069
+ const results = [];
51070
+ for (const entry of this.index.columns.values()) {
51071
+ for (const tableEntry of entry.tables) {
51072
+ if (tableEntry.column.nullable) {
51073
+ results.push(tableEntry);
51074
+ }
51075
+ }
51076
+ }
51077
+ return results;
51078
+ }
51079
+ getStats() {
51080
+ return {
51081
+ totalColumns: this.index.totalColumns,
51082
+ totalTables: this.index.totalTables,
51083
+ uniqueColumnNames: this.index.columns.size,
51084
+ averageTablesPerColumn: this.index.columns.size > 0 ? Array.from(this.index.columns.values()).reduce((sum, entry) => sum + entry.tables.length, 0) / this.index.columns.size : 0,
51085
+ timestamp: this.index.timestamp
51086
+ };
51087
+ }
51088
+ export() {
51089
+ const serialized = {
51090
+ columns: Array.from(this.index.columns.entries()).reduce((acc, [key2, value]) => {
51091
+ acc[key2] = value;
51092
+ return acc;
51093
+ }, {}),
51094
+ totalColumns: this.index.totalColumns,
51095
+ totalTables: this.index.totalTables,
51096
+ timestamp: this.index.timestamp
51097
+ };
51098
+ return serialized;
51099
+ }
51100
+ import(exported) {
51101
+ this.index = {
51102
+ columns: new Map(Object.entries(exported.columns)),
51103
+ totalColumns: exported.totalColumns,
51104
+ totalTables: exported.totalTables,
51105
+ timestamp: exported.timestamp
51106
+ };
51107
+ }
51108
+ }
51109
+ // src/core/blacklist-manager.ts
51110
+ class BlacklistManager {
51111
+ config;
51112
+ state;
51113
+ overrideEnabled;
51114
+ constructor(config, overrideEnvValue) {
51115
+ this.config = config;
51116
+ this.overrideEnabled = (overrideEnvValue ?? Bun.env.DBCLI_OVERRIDE_BLACKLIST ?? "") === "true";
51117
+ this.state = this.loadBlacklist();
51118
+ }
51119
+ loadBlacklist() {
51120
+ const tables = new Set;
51121
+ const columns = new Map;
51122
+ const blacklistConfig = this.config.blacklist;
51123
+ if (!blacklistConfig) {
51124
+ return { tables, columns };
51125
+ }
51126
+ if (Array.isArray(blacklistConfig.tables)) {
51127
+ for (const tableName of blacklistConfig.tables) {
51128
+ if (typeof tableName === "string") {
51129
+ tables.add(tableName.toLowerCase());
51130
+ } else {
51131
+ console.warn(`[BlacklistManager] Invalid table name in blacklist config: ${JSON.stringify(tableName)}`);
51132
+ }
51133
+ }
51134
+ } else if (blacklistConfig.tables !== undefined) {
51135
+ console.warn("[BlacklistManager] blacklist.tables must be an array, ignoring");
51136
+ }
51137
+ if (blacklistConfig.columns && typeof blacklistConfig.columns === "object" && !Array.isArray(blacklistConfig.columns)) {
51138
+ for (const [tableName, cols] of Object.entries(blacklistConfig.columns)) {
51139
+ if (typeof tableName !== "string") {
51140
+ console.warn(`[BlacklistManager] Invalid table name key in columns config: ${JSON.stringify(tableName)}`);
51141
+ continue;
51142
+ }
51143
+ if (!Array.isArray(cols)) {
51144
+ console.warn(`[BlacklistManager] blacklist.columns["${tableName}"] must be an array, ignoring`);
51145
+ continue;
51146
+ }
51147
+ const columnSet = new Set;
51148
+ for (const col of cols) {
51149
+ if (typeof col === "string") {
51150
+ columnSet.add(col);
51151
+ } else {
51152
+ console.warn(`[BlacklistManager] Invalid column name in blacklist.columns["${tableName}"]: ${JSON.stringify(col)}`);
51153
+ }
51154
+ }
51155
+ if (columnSet.size > 0) {
51156
+ columns.set(tableName, columnSet);
51157
+ }
51158
+ }
51159
+ } else if (blacklistConfig.columns !== undefined) {
51160
+ console.warn("[BlacklistManager] blacklist.columns must be an object, ignoring");
51161
+ }
51162
+ return { tables, columns };
51163
+ }
51164
+ isTableBlacklisted(tableName) {
51165
+ return this.state.tables.has(tableName.toLowerCase());
51166
+ }
51167
+ isColumnBlacklisted(tableName, columnName) {
51168
+ const columnSet = this.state.columns.get(tableName);
51169
+ if (!columnSet) {
51170
+ return false;
51171
+ }
51172
+ return columnSet.has(columnName);
51173
+ }
51174
+ getBlacklistedColumns(tableName) {
51175
+ const columnSet = this.state.columns.get(tableName);
51176
+ if (!columnSet) {
51177
+ return [];
51178
+ }
51179
+ return Array.from(columnSet);
51180
+ }
51181
+ canOverrideBlacklist() {
51182
+ return this.overrideEnabled;
51183
+ }
51184
+ getState() {
51185
+ return this.state;
51186
+ }
51187
+ }
51188
+ // src/types/blacklist.ts
51189
+ class BlacklistError extends Error {
51190
+ tableName;
51191
+ operation;
51192
+ constructor(message, tableName, operation) {
51193
+ super(message);
51194
+ this.tableName = tableName;
51195
+ this.operation = operation;
51196
+ this.name = "BlacklistError";
51197
+ }
51198
+ }
51199
+
51200
+ // src/core/blacklist-validator.ts
51201
+ class BlacklistValidator {
51202
+ manager;
51203
+ constructor(manager) {
51204
+ this.manager = manager;
51205
+ }
51206
+ checkTableBlacklist(operation, tableName, _tableList = []) {
51207
+ if (this.manager.canOverrideBlacklist()) {
51208
+ const message = t_vars("warnings.blacklist_override_used", {
51209
+ operation,
51210
+ table: tableName
51211
+ });
51212
+ console.error(message);
51213
+ return;
51214
+ }
51215
+ if (this.manager.isTableBlacklisted(tableName)) {
51216
+ const message = t_vars("errors.table_blacklisted", {
51217
+ table: tableName,
51218
+ operation
51219
+ });
51220
+ throw new BlacklistError(message, tableName, operation);
51221
+ }
51222
+ }
51223
+ filterColumns(tableName, rows, columnList) {
51224
+ const blacklistedColumns = this.manager.getBlacklistedColumns(tableName);
51225
+ if (blacklistedColumns.length === 0) {
51226
+ return { filteredRows: rows, omittedColumns: [] };
51227
+ }
51228
+ const omittedColumns = columnList.filter((col) => blacklistedColumns.includes(col));
51229
+ if (omittedColumns.length === 0) {
51230
+ return { filteredRows: rows, omittedColumns: [] };
51231
+ }
51232
+ const filteredRows = rows.map((row) => {
51233
+ const newRow = {};
51234
+ for (const [key2, value] of Object.entries(row)) {
51235
+ if (!omittedColumns.includes(key2)) {
51236
+ newRow[key2] = value;
51237
+ }
51238
+ }
51239
+ return newRow;
51240
+ });
51241
+ return { filteredRows, omittedColumns };
51242
+ }
51243
+ buildSecurityNotification(_tableName, omittedColumns) {
51244
+ if (omittedColumns.length === 0) {
51245
+ return "";
51246
+ }
51247
+ return t_vars("security.columns_omitted", {
51248
+ count: omittedColumns.length
51249
+ });
51250
+ }
51251
+ }
51252
+
51253
+ // src/core/index.ts
51254
+ init_size_category();
51255
+
51256
+ // src/core/health-checker.ts
51257
+ init_size_category();
51258
+
51259
+ class HealthChecker {
51260
+ adapter;
51261
+ constructor(adapter) {
51262
+ this.adapter = adapter;
51263
+ }
51264
+ async check(schema, options = {}) {
51265
+ const checks = options.checks || ["nulls", "duplicates", "orphans", "emptyStrings", "rowCount"];
51266
+ const sample2 = options.sample || 1e4;
51267
+ const blacklisted = options.blacklistedColumns || new Set;
51268
+ const visibleColumns = schema.columns.filter((c) => !blacklisted.has(`${schema.name}.${c.name}`));
51269
+ const countResult = await this.adapter.execute(`SELECT COUNT(*) as count FROM \`${schema.name}\``);
51270
+ const rowCount = countResult[0]?.count || 0;
51271
+ const nulls = checks.includes("nulls") ? await this.checkNulls(schema.name, visibleColumns, rowCount, sample2) : [];
51272
+ const orphans = checks.includes("orphans") ? await this.checkOrphans(schema.name, visibleColumns) : [];
51273
+ const duplicates = checks.includes("duplicates") ? await this.checkDuplicates(schema.name, schema.indexes || []) : [];
51274
+ const emptyStrings = checks.includes("emptyStrings") ? await this.checkEmptyStrings(schema.name, visibleColumns) : [];
51275
+ const issues = orphans.length + duplicates.length;
51276
+ const warnings = nulls.filter((n) => n.nullPercent > 50).length + emptyStrings.length;
51277
+ const clean = Math.max(0, checks.length - (issues > 0 ? 1 : 0) - (warnings > 0 ? 1 : 0));
51278
+ return {
51279
+ table: schema.name,
51280
+ rowCount,
51281
+ sizeCategory: getSizeCategory(schema.estimatedRowCount),
51282
+ checks: { nulls, orphans, duplicates, emptyStrings },
51283
+ summary: { issues, warnings, clean },
51284
+ skippedColumns: blacklisted.size > 0 ? Array.from(blacklisted).filter((c) => c.startsWith(`${schema.name}.`)) : undefined
51285
+ };
51286
+ }
51287
+ async checkNulls(tableName, columns, totalRows, sample2) {
51288
+ if (totalRows === 0)
51289
+ return [];
51290
+ const nullableColumns = columns.filter((c) => c.nullable);
51291
+ const results = [];
51292
+ for (const col of nullableColumns) {
51293
+ try {
51294
+ const useSample = totalRows > sample2;
51295
+ const sql = useSample ? `SELECT COUNT(*) - COUNT(\`${col.name}\`) as null_count FROM (SELECT \`${col.name}\` FROM \`${tableName}\` LIMIT ${sample2}) sub` : `SELECT COUNT(*) - COUNT(\`${col.name}\`) as null_count FROM \`${tableName}\``;
51296
+ const result = await this.adapter.execute(sql);
51297
+ const nullCount = result[0]?.null_count || 0;
51298
+ if (nullCount > 0) {
51299
+ const sampleSize = Math.min(totalRows, sample2);
51300
+ results.push({
51301
+ column: col.name,
51302
+ nullCount,
51303
+ nullPercent: Number((nullCount / sampleSize * 100).toFixed(1))
51304
+ });
51305
+ }
51306
+ } catch {}
51307
+ }
51308
+ return results;
51309
+ }
51310
+ async checkOrphans(tableName, columns) {
51311
+ const fkColumns = columns.filter((c) => c.foreignKey);
51312
+ const results = [];
51313
+ for (const col of fkColumns) {
51314
+ if (!col.foreignKey)
51315
+ continue;
51316
+ try {
51317
+ const sql = `
51318
+ SELECT COUNT(*) as orphan_count
51319
+ FROM \`${tableName}\` child
51320
+ LEFT JOIN \`${col.foreignKey.table}\` parent
51321
+ ON child.\`${col.name}\` = parent.\`${col.foreignKey.column}\`
51322
+ WHERE child.\`${col.name}\` IS NOT NULL
51323
+ AND parent.\`${col.foreignKey.column}\` IS NULL
51324
+ `;
51325
+ const result = await this.adapter.execute(sql);
51326
+ const orphanCount = result[0]?.orphan_count || 0;
51327
+ if (orphanCount > 0) {
51328
+ results.push({
51329
+ column: col.name,
51330
+ references: `${col.foreignKey.table}.${col.foreignKey.column}`,
51331
+ orphanCount
51332
+ });
51333
+ }
51334
+ } catch {}
51335
+ }
51336
+ return results;
51337
+ }
51338
+ async checkDuplicates(tableName, indexes) {
51339
+ const uniqueIndexes = indexes.filter((idx) => idx.unique);
51340
+ const results = [];
51341
+ for (const idx of uniqueIndexes) {
51342
+ try {
51343
+ const colList = idx.columns.map((c) => `\`${c}\``).join(", ");
51344
+ const sql = `
51345
+ SELECT COUNT(*) as dup_count FROM (
51346
+ SELECT ${colList}
51347
+ FROM \`${tableName}\`
51348
+ GROUP BY ${colList}
51349
+ HAVING COUNT(*) > 1
51350
+ ) dups
51351
+ `;
51352
+ const result = await this.adapter.execute(sql);
51353
+ const dupCount = result[0]?.dup_count || 0;
51354
+ if (dupCount > 0) {
51355
+ results.push({
51356
+ columns: idx.columns,
51357
+ indexName: idx.name,
51358
+ duplicateCount: dupCount
51359
+ });
51360
+ }
51361
+ } catch {}
51362
+ }
51363
+ return results;
51364
+ }
51365
+ async checkEmptyStrings(tableName, columns) {
51366
+ const stringColumns = columns.filter((c) => /varchar|text|char/i.test(c.type));
51367
+ const results = [];
51368
+ for (const col of stringColumns) {
51369
+ try {
51370
+ const sql = `SELECT COUNT(*) as empty_count FROM \`${tableName}\` WHERE \`${col.name}\` = ''`;
51371
+ const result = await this.adapter.execute(sql);
51372
+ const count = result[0]?.empty_count || 0;
51373
+ if (count > 0) {
51374
+ results.push({ column: col.name, count });
51375
+ }
51376
+ } catch {}
51377
+ }
51378
+ return results;
51379
+ }
51380
+ }
49269
51381
  // src/commands/schema.ts
49270
51382
  init_validation();
49271
51383
  var ALLOWED_FORMATS2 = ["table", "json"];
@@ -49278,17 +51390,29 @@ async function schemaAction(table, options) {
49278
51390
  console.error("Database not configured. Run: dbcli init");
49279
51391
  process.exit(1);
49280
51392
  }
51393
+ const connectionName = await getSchemaIsolationConnectionName(options.config);
51394
+ let existingSchemaCount;
51395
+ if (connectionName !== undefined) {
51396
+ try {
51397
+ const v2Raw = await readV2Config(options.config);
51398
+ existingSchemaCount = Object.keys(v2Raw.schemas?.[connectionName] ?? {}).length;
51399
+ } catch {
51400
+ existingSchemaCount = 0;
51401
+ }
51402
+ } else {
51403
+ existingSchemaCount = Object.keys(config.schema ?? {}).length;
51404
+ }
49281
51405
  const adapter = AdapterFactory.createAdapter(config.connection);
49282
51406
  await adapter.connect();
49283
51407
  try {
49284
51408
  if (options.reset) {
49285
- await handleSchemaReset(adapter, config, options);
51409
+ await handleSchemaReset(adapter, config, options, connectionName, existingSchemaCount);
49286
51410
  } else if (options.refresh) {
49287
- await handleSchemaRefresh(adapter, config, options);
51411
+ await handleSchemaRefresh(adapter, config, options, connectionName);
49288
51412
  } else if (table) {
49289
51413
  await handleSingleTableSchema(adapter, table, options.format);
49290
51414
  } else {
49291
- await handleFullDatabaseScan(adapter, config, options);
51415
+ await handleFullDatabaseScan(adapter, config, options, connectionName, existingSchemaCount);
49292
51416
  }
49293
51417
  } finally {
49294
51418
  await adapter.disconnect();
@@ -49348,10 +51472,18 @@ Indexes:`);
49348
51472
  }
49349
51473
  }
49350
51474
  }
49351
- async function handleSchemaRefresh(adapter, config, options) {
51475
+ async function handleSchemaRefresh(adapter, config, options, connectionName) {
49352
51476
  const diffEngine = new SchemaDiffEngine(adapter, config);
49353
51477
  const report = await diffEngine.diff();
49354
51478
  if (report.tablesAdded.length === 0 && report.tablesRemoved.length === 0 && Object.keys(report.tablesModified).length === 0) {
51479
+ const updatedConfig2 = configModule.merge(config, {
51480
+ metadata: {
51481
+ ...config.metadata,
51482
+ schemaLastUpdated: new Date().toISOString(),
51483
+ schemaTableCount: Object.keys(config.schema || {}).length
51484
+ }
51485
+ });
51486
+ await writeSchema(options.config, updatedConfig2, connectionName);
49355
51487
  console.log("\u2705 Schema is up-to-date (no changes detected)");
49356
51488
  return;
49357
51489
  }
@@ -49375,27 +51507,37 @@ async function handleSchemaRefresh(adapter, config, options) {
49375
51507
  schemaTableCount: Object.keys(newSchema).length
49376
51508
  }
49377
51509
  });
49378
- await configModule.write(options.config, updatedConfig);
51510
+ const writer = new SchemaWriter(options.config);
51511
+ await writer.save(newSchema, connectionName);
51512
+ console.log(`\u2705 Schema persisted to layered storage (.dbcli/schemas/${connectionName || ""})`);
51513
+ await writeSchema(options.config, updatedConfig, connectionName);
49379
51514
  console.log(`\u2705 Schema updated in .dbcli`);
49380
51515
  }
49381
- async function handleSchemaReset(adapter, config, options) {
49382
- const existingCount = config.schema ? Object.keys(config.schema).length : 0;
51516
+ async function handleSchemaReset(adapter, config, options, connectionName, existingCount) {
49383
51517
  if (existingCount > 0 && !options.force) {
49384
- console.log(`\u26A0 This will clear ${existingCount} existing table schemas and re-fetch from database.`);
49385
- console.log(" Use --force to confirm.");
51518
+ const { SchemaLayeredLoader: SchemaLayeredLoader3 } = await Promise.resolve().then(() => (init_schema_loader(), exports_schema_loader));
51519
+ const loader = new SchemaLayeredLoader3(options.config, { connectionName });
51520
+ const { index } = await loader.initialize();
51521
+ if (!index || Object.keys(index.tables).length === 0) {
51522
+ console.log(`\u26A0 This will clear ${existingCount} existing table schemas and re-fetch from database.`);
51523
+ console.log("\uD83D\uDCA1 Hint: Schema found in config.json but layered cache files are missing.");
51524
+ console.log(" Use --force to migrate to optimized layered storage.");
51525
+ } else {
51526
+ console.log(`\u26A0 This will clear ${existingCount} existing table schemas and re-fetch from database.`);
51527
+ console.log(" Use --force to confirm.");
51528
+ }
49386
51529
  return;
49387
51530
  }
49388
51531
  console.log("\uD83D\uDDD1 Clearing existing schema data...");
51532
+ const emptyMeta = { schemaLastUpdated: undefined, schemaTableCount: 0 };
49389
51533
  const configWithoutSchema = {
49390
51534
  ...config,
49391
51535
  schema: {},
49392
- metadata: {
49393
- ...config.metadata,
49394
- schemaLastUpdated: undefined,
49395
- schemaTableCount: 0
49396
- }
51536
+ metadata: { ...config.metadata, ...emptyMeta }
49397
51537
  };
49398
- await configModule.write(options.config, configWithoutSchema);
51538
+ await writeSchema(options.config, configWithoutSchema, connectionName);
51539
+ const writer = new SchemaWriter(options.config);
51540
+ await writer.clear(connectionName);
49399
51541
  console.log(t("schema.scanning_database"));
49400
51542
  const tables = await adapter.listTables();
49401
51543
  console.log(t_vars("schema.tables_found", { count: tables.length }));
@@ -49428,7 +51570,9 @@ async function handleSchemaReset(adapter, config, options) {
49428
51570
  schemaTableCount: tables.length
49429
51571
  }
49430
51572
  };
49431
- await configModule.write(options.config, updatedConfig);
51573
+ await writer.save(schemaData, connectionName);
51574
+ console.log(`\u2705 Schema persisted to layered storage (.dbcli/schemas/${connectionName || ""})`);
51575
+ await writeSchema(options.config, updatedConfig, connectionName);
49432
51576
  if (existingCount > 0) {
49433
51577
  console.log(`
49434
51578
  \u2705 Schema reset complete \u2014 cleared ${existingCount} old tables, fetched ${tables.length} tables from database`);
@@ -49437,7 +51581,7 @@ async function handleSchemaReset(adapter, config, options) {
49437
51581
  \u2705 Schema fetched \u2014 ${tables.length} tables from database`);
49438
51582
  }
49439
51583
  }
49440
- async function handleFullDatabaseScan(adapter, config, options) {
51584
+ async function handleFullDatabaseScan(adapter, config, options, connectionName, existingSchemaCount) {
49441
51585
  console.log(t("schema.scanning_database"));
49442
51586
  const tables = await adapter.listTables();
49443
51587
  console.log(t_vars("schema.tables_found", { count: tables.length }));
@@ -49461,26 +51605,50 @@ async function handleFullDatabaseScan(adapter, config, options) {
49461
51605
  console.log(t_vars("schema.processing_tables", { processed, total: tables.length }));
49462
51606
  }
49463
51607
  }
49464
- if (config.schema && Object.keys(config.schema).length > 0 && !options.force) {
49465
- console.log(`
51608
+ if (existingSchemaCount > 0 && !options.force) {
51609
+ const { SchemaLayeredLoader: SchemaLayeredLoader3 } = await Promise.resolve().then(() => (init_schema_loader(), exports_schema_loader));
51610
+ const loader = new SchemaLayeredLoader3(options.config, { connectionName });
51611
+ const { index } = await loader.initialize();
51612
+ if (!index || Object.keys(index.tables).length === 0) {
51613
+ console.log(`
51614
+ ` + t("schema.schema_exists_warning"));
51615
+ console.log("\uD83D\uDCA1 Hint: Schema found in config.json but layered cache files are missing.");
51616
+ console.log(" Run with --force to migrate your schema to optimized layered storage.");
51617
+ } else {
51618
+ console.log(`
49466
51619
  ` + t("schema.schema_exists_warning"));
49467
- console.log(t("schema.use_force_to_override"));
51620
+ console.log(t("schema.use_force_to_override"));
51621
+ }
49468
51622
  process.exit(0);
49469
51623
  }
51624
+ const now = new Date().toISOString();
49470
51625
  const updatedConfig = {
49471
51626
  ...config,
49472
51627
  schema: schemaData,
49473
51628
  metadata: {
49474
51629
  ...config.metadata,
49475
- schemaLastUpdated: new Date().toISOString(),
51630
+ schemaLastUpdated: now,
49476
51631
  schemaTableCount: tables.length
49477
51632
  }
49478
51633
  };
49479
- await configModule.write(options.config, updatedConfig);
51634
+ const writer = new SchemaWriter(options.config);
51635
+ await writer.save(schemaData, connectionName);
51636
+ console.log(`\u2705 Schema persisted to layered storage (.dbcli/schemas/${connectionName || ""})`);
51637
+ await writeSchema(options.config, updatedConfig, connectionName);
49480
51638
  console.log(`
49481
51639
  \u2705 Schema updated in .dbcli`);
49482
51640
  console.log(` ${tables.length} tables with full column details and relationships`);
49483
- console.log(` Timestamp: ${updatedConfig.metadata.schemaLastUpdated}`);
51641
+ console.log(` Timestamp: ${now}`);
51642
+ }
51643
+ async function writeSchema(configPath, config, connectionName) {
51644
+ if (connectionName !== undefined) {
51645
+ await patchConnectionSchema(configPath, connectionName, config.schema ?? {}, {
51646
+ schemaLastUpdated: config.metadata?.schemaLastUpdated,
51647
+ schemaTableCount: config.metadata?.schemaTableCount
51648
+ });
51649
+ } else {
51650
+ await configModule.write(configPath, config);
51651
+ }
49484
51652
  }
49485
51653
 
49486
51654
  // src/core/permission-guard.ts
@@ -50007,151 +52175,6 @@ function inferColumnType(value) {
50007
52175
  return "unknown";
50008
52176
  }
50009
52177
 
50010
- // src/core/blacklist-manager.ts
50011
- class BlacklistManager {
50012
- config;
50013
- state;
50014
- overrideEnabled;
50015
- constructor(config, overrideEnvValue) {
50016
- this.config = config;
50017
- this.overrideEnabled = (overrideEnvValue ?? Bun.env.DBCLI_OVERRIDE_BLACKLIST ?? "") === "true";
50018
- this.state = this.loadBlacklist();
50019
- }
50020
- loadBlacklist() {
50021
- const tables = new Set;
50022
- const columns = new Map;
50023
- const blacklistConfig = this.config.blacklist;
50024
- if (!blacklistConfig) {
50025
- return { tables, columns };
50026
- }
50027
- if (Array.isArray(blacklistConfig.tables)) {
50028
- for (const tableName of blacklistConfig.tables) {
50029
- if (typeof tableName === "string") {
50030
- tables.add(tableName.toLowerCase());
50031
- } else {
50032
- console.warn(`[BlacklistManager] Invalid table name in blacklist config: ${JSON.stringify(tableName)}`);
50033
- }
50034
- }
50035
- } else if (blacklistConfig.tables !== undefined) {
50036
- console.warn("[BlacklistManager] blacklist.tables must be an array, ignoring");
50037
- }
50038
- if (blacklistConfig.columns && typeof blacklistConfig.columns === "object" && !Array.isArray(blacklistConfig.columns)) {
50039
- for (const [tableName, cols] of Object.entries(blacklistConfig.columns)) {
50040
- if (typeof tableName !== "string") {
50041
- console.warn(`[BlacklistManager] Invalid table name key in columns config: ${JSON.stringify(tableName)}`);
50042
- continue;
50043
- }
50044
- if (!Array.isArray(cols)) {
50045
- console.warn(`[BlacklistManager] blacklist.columns["${tableName}"] must be an array, ignoring`);
50046
- continue;
50047
- }
50048
- const columnSet = new Set;
50049
- for (const col of cols) {
50050
- if (typeof col === "string") {
50051
- columnSet.add(col);
50052
- } else {
50053
- console.warn(`[BlacklistManager] Invalid column name in blacklist.columns["${tableName}"]: ${JSON.stringify(col)}`);
50054
- }
50055
- }
50056
- if (columnSet.size > 0) {
50057
- columns.set(tableName, columnSet);
50058
- }
50059
- }
50060
- } else if (blacklistConfig.columns !== undefined) {
50061
- console.warn("[BlacklistManager] blacklist.columns must be an object, ignoring");
50062
- }
50063
- return { tables, columns };
50064
- }
50065
- isTableBlacklisted(tableName) {
50066
- return this.state.tables.has(tableName.toLowerCase());
50067
- }
50068
- isColumnBlacklisted(tableName, columnName) {
50069
- const columnSet = this.state.columns.get(tableName);
50070
- if (!columnSet) {
50071
- return false;
50072
- }
50073
- return columnSet.has(columnName);
50074
- }
50075
- getBlacklistedColumns(tableName) {
50076
- const columnSet = this.state.columns.get(tableName);
50077
- if (!columnSet) {
50078
- return [];
50079
- }
50080
- return Array.from(columnSet);
50081
- }
50082
- canOverrideBlacklist() {
50083
- return this.overrideEnabled;
50084
- }
50085
- getState() {
50086
- return this.state;
50087
- }
50088
- }
50089
-
50090
- // src/types/blacklist.ts
50091
- class BlacklistError extends Error {
50092
- tableName;
50093
- operation;
50094
- constructor(message, tableName, operation) {
50095
- super(message);
50096
- this.tableName = tableName;
50097
- this.operation = operation;
50098
- this.name = "BlacklistError";
50099
- }
50100
- }
50101
-
50102
- // src/core/blacklist-validator.ts
50103
- class BlacklistValidator {
50104
- manager;
50105
- constructor(manager) {
50106
- this.manager = manager;
50107
- }
50108
- checkTableBlacklist(operation, tableName, _tableList = []) {
50109
- if (this.manager.canOverrideBlacklist()) {
50110
- const message = t_vars("warnings.blacklist_override_used", {
50111
- operation,
50112
- table: tableName
50113
- });
50114
- console.error(message);
50115
- return;
50116
- }
50117
- if (this.manager.isTableBlacklisted(tableName)) {
50118
- const message = t_vars("errors.table_blacklisted", {
50119
- table: tableName,
50120
- operation
50121
- });
50122
- throw new BlacklistError(message, tableName, operation);
50123
- }
50124
- }
50125
- filterColumns(tableName, rows, columnList) {
50126
- const blacklistedColumns = this.manager.getBlacklistedColumns(tableName);
50127
- if (blacklistedColumns.length === 0) {
50128
- return { filteredRows: rows, omittedColumns: [] };
50129
- }
50130
- const omittedColumns = columnList.filter((col) => blacklistedColumns.includes(col));
50131
- if (omittedColumns.length === 0) {
50132
- return { filteredRows: rows, omittedColumns: [] };
50133
- }
50134
- const filteredRows = rows.map((row) => {
50135
- const newRow = {};
50136
- for (const [key2, value] of Object.entries(row)) {
50137
- if (!omittedColumns.includes(key2)) {
50138
- newRow[key2] = value;
50139
- }
50140
- }
50141
- return newRow;
50142
- });
50143
- return { filteredRows, omittedColumns };
50144
- }
50145
- buildSecurityNotification(_tableName, omittedColumns) {
50146
- if (omittedColumns.length === 0) {
50147
- return "";
50148
- }
50149
- return t_vars("security.columns_omitted", {
50150
- count: omittedColumns.length
50151
- });
50152
- }
50153
- }
50154
-
50155
52178
  // src/commands/query.ts
50156
52179
  init_validation();
50157
52180
  var ALLOWED_FORMATS3 = ["table", "json", "csv"];
@@ -50904,6 +52927,7 @@ async function exportCommand(sql, options) {
50904
52927
  }
50905
52928
 
50906
52929
  // src/commands/skill.ts
52930
+ var {$ } = globalThis.Bun;
50907
52931
  import * as path from "path";
50908
52932
  import { homedir } from "os";
50909
52933
  function findPackageRoot() {
@@ -50917,6 +52941,7 @@ function findPackageRoot() {
50917
52941
  return path.resolve(import.meta.dir, "../..");
50918
52942
  }
50919
52943
  var SKILL_SOURCE_PATH = path.join(findPackageRoot(), "assets", "SKILL.md");
52944
+ var SUPPORTED_PLATFORMS = ["claude", "gemini", "copilot", "cursor"];
50920
52945
  async function skillCommand(_program, options) {
50921
52946
  try {
50922
52947
  const skillFile = Bun.file(SKILL_SOURCE_PATH);
@@ -50943,6 +52968,28 @@ async function skillCommand(_program, options) {
50943
52968
  process.exit(1);
50944
52969
  }
50945
52970
  }
52971
+ async function checkSkillUpdates() {
52972
+ const outdated = [];
52973
+ try {
52974
+ const sourceFile = Bun.file(SKILL_SOURCE_PATH);
52975
+ if (!await sourceFile.exists())
52976
+ return [];
52977
+ const sourceContent = await sourceFile.text();
52978
+ for (const platform of SUPPORTED_PLATFORMS) {
52979
+ try {
52980
+ const installPath = getInstallPath(platform);
52981
+ const installedFile = Bun.file(installPath);
52982
+ if (await installedFile.exists()) {
52983
+ const installedContent = await installedFile.text();
52984
+ if (installedContent !== sourceContent) {
52985
+ outdated.push(platform);
52986
+ }
52987
+ }
52988
+ } catch {}
52989
+ }
52990
+ } catch {}
52991
+ return outdated;
52992
+ }
50946
52993
  function getInstallPath(platform) {
50947
52994
  const home = process.env.HOME || homedir();
50948
52995
  const platformLower = platform.toLowerCase();
@@ -50956,7 +53003,7 @@ function getInstallPath(platform) {
50956
53003
  case "cursor":
50957
53004
  return path.join(process.cwd(), ".cursor", "rules", "dbcli.mdc");
50958
53005
  default:
50959
- throw new Error(`Unknown platform: ${platform}. Supported platforms: claude, gemini, copilot, cursor`);
53006
+ throw new Error(`Unknown platform: ${platform}. Supported platforms: ${SUPPORTED_PLATFORMS.join(", ")}`);
50960
53007
  }
50961
53008
  }
50962
53009
  async function ensureDir(dirPath) {
@@ -51148,132 +53195,6 @@ columnCmd.command("remove <table.column>").description("Remove column from black
51148
53195
  }
51149
53196
  });
51150
53197
 
51151
- // src/core/health-checker.ts
51152
- init_size_category();
51153
-
51154
- class HealthChecker {
51155
- adapter;
51156
- constructor(adapter) {
51157
- this.adapter = adapter;
51158
- }
51159
- async check(schema, options = {}) {
51160
- const checks = options.checks || ["nulls", "duplicates", "orphans", "emptyStrings", "rowCount"];
51161
- const sample2 = options.sample || 1e4;
51162
- const blacklisted = options.blacklistedColumns || new Set;
51163
- const visibleColumns = schema.columns.filter((c) => !blacklisted.has(`${schema.name}.${c.name}`));
51164
- const countResult = await this.adapter.execute(`SELECT COUNT(*) as count FROM \`${schema.name}\``);
51165
- const rowCount = countResult[0]?.count || 0;
51166
- const nulls = checks.includes("nulls") ? await this.checkNulls(schema.name, visibleColumns, rowCount, sample2) : [];
51167
- const orphans = checks.includes("orphans") ? await this.checkOrphans(schema.name, visibleColumns) : [];
51168
- const duplicates = checks.includes("duplicates") ? await this.checkDuplicates(schema.name, schema.indexes || []) : [];
51169
- const emptyStrings = checks.includes("emptyStrings") ? await this.checkEmptyStrings(schema.name, visibleColumns) : [];
51170
- const issues = orphans.length + duplicates.length;
51171
- const warnings = nulls.filter((n) => n.nullPercent > 50).length + emptyStrings.length;
51172
- const clean = Math.max(0, checks.length - (issues > 0 ? 1 : 0) - (warnings > 0 ? 1 : 0));
51173
- return {
51174
- table: schema.name,
51175
- rowCount,
51176
- sizeCategory: getSizeCategory(schema.estimatedRowCount),
51177
- checks: { nulls, orphans, duplicates, emptyStrings },
51178
- summary: { issues, warnings, clean },
51179
- skippedColumns: blacklisted.size > 0 ? Array.from(blacklisted).filter((c) => c.startsWith(`${schema.name}.`)) : undefined
51180
- };
51181
- }
51182
- async checkNulls(tableName, columns, totalRows, sample2) {
51183
- if (totalRows === 0)
51184
- return [];
51185
- const nullableColumns = columns.filter((c) => c.nullable);
51186
- const results = [];
51187
- for (const col of nullableColumns) {
51188
- try {
51189
- const useSample = totalRows > sample2;
51190
- const sql = useSample ? `SELECT COUNT(*) - COUNT(\`${col.name}\`) as null_count FROM (SELECT \`${col.name}\` FROM \`${tableName}\` LIMIT ${sample2}) sub` : `SELECT COUNT(*) - COUNT(\`${col.name}\`) as null_count FROM \`${tableName}\``;
51191
- const result = await this.adapter.execute(sql);
51192
- const nullCount = result[0]?.null_count || 0;
51193
- if (nullCount > 0) {
51194
- const sampleSize = Math.min(totalRows, sample2);
51195
- results.push({
51196
- column: col.name,
51197
- nullCount,
51198
- nullPercent: Number((nullCount / sampleSize * 100).toFixed(1))
51199
- });
51200
- }
51201
- } catch {}
51202
- }
51203
- return results;
51204
- }
51205
- async checkOrphans(tableName, columns) {
51206
- const fkColumns = columns.filter((c) => c.foreignKey);
51207
- const results = [];
51208
- for (const col of fkColumns) {
51209
- if (!col.foreignKey)
51210
- continue;
51211
- try {
51212
- const sql = `
51213
- SELECT COUNT(*) as orphan_count
51214
- FROM \`${tableName}\` child
51215
- LEFT JOIN \`${col.foreignKey.table}\` parent
51216
- ON child.\`${col.name}\` = parent.\`${col.foreignKey.column}\`
51217
- WHERE child.\`${col.name}\` IS NOT NULL
51218
- AND parent.\`${col.foreignKey.column}\` IS NULL
51219
- `;
51220
- const result = await this.adapter.execute(sql);
51221
- const orphanCount = result[0]?.orphan_count || 0;
51222
- if (orphanCount > 0) {
51223
- results.push({
51224
- column: col.name,
51225
- references: `${col.foreignKey.table}.${col.foreignKey.column}`,
51226
- orphanCount
51227
- });
51228
- }
51229
- } catch {}
51230
- }
51231
- return results;
51232
- }
51233
- async checkDuplicates(tableName, indexes) {
51234
- const uniqueIndexes = indexes.filter((idx) => idx.unique);
51235
- const results = [];
51236
- for (const idx of uniqueIndexes) {
51237
- try {
51238
- const colList = idx.columns.map((c) => `\`${c}\``).join(", ");
51239
- const sql = `
51240
- SELECT COUNT(*) as dup_count FROM (
51241
- SELECT ${colList}
51242
- FROM \`${tableName}\`
51243
- GROUP BY ${colList}
51244
- HAVING COUNT(*) > 1
51245
- ) dups
51246
- `;
51247
- const result = await this.adapter.execute(sql);
51248
- const dupCount = result[0]?.dup_count || 0;
51249
- if (dupCount > 0) {
51250
- results.push({
51251
- columns: idx.columns,
51252
- indexName: idx.name,
51253
- duplicateCount: dupCount
51254
- });
51255
- }
51256
- } catch {}
51257
- }
51258
- return results;
51259
- }
51260
- async checkEmptyStrings(tableName, columns) {
51261
- const stringColumns = columns.filter((c) => /varchar|text|char/i.test(c.type));
51262
- const results = [];
51263
- for (const col of stringColumns) {
51264
- try {
51265
- const sql = `SELECT COUNT(*) as empty_count FROM \`${tableName}\` WHERE \`${col.name}\` = ''`;
51266
- const result = await this.adapter.execute(sql);
51267
- const count = result[0]?.empty_count || 0;
51268
- if (count > 0) {
51269
- results.push({ column: col.name, count });
51270
- }
51271
- } catch {}
51272
- }
51273
- return results;
51274
- }
51275
- }
51276
-
51277
53198
  // src/commands/check.ts
51278
53199
  init_size_category();
51279
53200
  init_validation();
@@ -51615,7 +53536,8 @@ var statusCommand = new Command("status").description("Show current configuratio
51615
53536
 
51616
53537
  // src/commands/doctor.ts
51617
53538
  init_validation();
51618
- import { join as join5 } from "path";
53539
+ init_schema_path();
53540
+ import { join as join10 } from "path";
51619
53541
  var ALLOWED_FORMATS7 = ["text", "json"];
51620
53542
  var SENSITIVE_PATTERNS = [
51621
53543
  "password",
@@ -51633,6 +53555,15 @@ var SENSITIVE_PATTERNS = [
51633
53555
  "ssn",
51634
53556
  "credit_card"
51635
53557
  ];
53558
+ function resolveSchemaLastUpdated(indexJson, configMetadata) {
53559
+ if (indexJson && typeof indexJson === "object") {
53560
+ const idx = indexJson;
53561
+ const fromIndex = idx.metadata?.lastRefreshed ?? idx.updatedAt;
53562
+ if (fromIndex)
53563
+ return fromIndex;
53564
+ }
53565
+ return configMetadata?.schemaLastUpdated ?? null;
53566
+ }
51636
53567
  function compareSemver(a, b) {
51637
53568
  const pa = a.split(".").map(Number);
51638
53569
  const pb = b.split(".").map(Number);
@@ -51677,7 +53608,7 @@ var runDoctorChecks = {
51677
53608
  }
51678
53609
  },
51679
53610
  async checkConfigExists(configPath, existsFn) {
51680
- const exists = existsFn ? await existsFn(configPath) : await Bun.file(configPath).exists() || await Bun.file(join5(configPath, "config.json")).exists();
53611
+ const exists = existsFn ? await existsFn(configPath) : await Bun.file(configPath).exists() || await Bun.file(join10(configPath, "config.json")).exists();
51681
53612
  return {
51682
53613
  group: "Configuration",
51683
53614
  label: "Config exists",
@@ -51771,7 +53702,7 @@ var runDoctorChecks = {
51771
53702
  },
51772
53703
  async checkV2Config(configPath) {
51773
53704
  const results = [];
51774
- const configFile = Bun.file(join5(configPath, "config.json"));
53705
+ const configFile = Bun.file(join10(configPath, "config.json"));
51775
53706
  if (!await configFile.exists())
51776
53707
  return results;
51777
53708
  let raw;
@@ -51811,7 +53742,7 @@ var runDoctorChecks = {
51811
53742
  }
51812
53743
  for (const [name, conn] of Object.entries(config.connections)) {
51813
53744
  if (conn.envFile) {
51814
- const envPath = join5(configPath, "..", conn.envFile);
53745
+ const envPath = join10(configPath, "..", conn.envFile);
51815
53746
  const exists = await Bun.file(envPath).exists();
51816
53747
  results.push({
51817
53748
  group: "Configuration",
@@ -51907,17 +53838,17 @@ var doctorCommand = new Command("doctor").description("Run diagnostic checks on
51907
53838
  logger.debug("Could not list tables for blacklist/large table check");
51908
53839
  }
51909
53840
  try {
51910
- const indexPath = join5(configPath, "schemas", "index.json");
53841
+ const schemaConnName = await getSchemaIsolationConnectionName(configPath);
53842
+ const indexPath = join10(resolveSchemaPath(configPath, schemaConnName), "index.json");
51911
53843
  const indexFile = Bun.file(indexPath);
53844
+ let indexParsed = null;
51912
53845
  if (await indexFile.exists()) {
51913
- const indexContent = await indexFile.text();
51914
- const index = JSON.parse(indexContent);
51915
- results.push(runDoctorChecks.checkSchemaCacheFreshness(index.updatedAt ?? null));
51916
- } else {
51917
- results.push(runDoctorChecks.checkSchemaCacheFreshness(null));
53846
+ indexParsed = JSON.parse(await indexFile.text());
51918
53847
  }
53848
+ const lastUpdated = resolveSchemaLastUpdated(indexParsed, config.metadata);
53849
+ results.push(runDoctorChecks.checkSchemaCacheFreshness(lastUpdated));
51919
53850
  } catch {
51920
- results.push(runDoctorChecks.checkSchemaCacheFreshness(null));
53851
+ results.push(runDoctorChecks.checkSchemaCacheFreshness(config.metadata?.schemaLastUpdated ?? null));
51921
53852
  }
51922
53853
  await adapter.disconnect();
51923
53854
  } catch (error) {
@@ -51949,7 +53880,7 @@ var doctorCommand = new Command("doctor").description("Run diagnostic checks on
51949
53880
  });
51950
53881
 
51951
53882
  // src/commands/completion.ts
51952
- import { join as join6 } from "path";
53883
+ import { join as join11 } from "path";
51953
53884
  import { homedir as homedir2 } from "os";
51954
53885
  function extractCommands(program2) {
51955
53886
  return program2.commands.map((cmd) => ({
@@ -52054,11 +53985,11 @@ function getInstallPath2(shell) {
52054
53985
  const home = homedir2();
52055
53986
  switch (shell) {
52056
53987
  case "bash":
52057
- return join6(home, ".bashrc");
53988
+ return join11(home, ".bashrc");
52058
53989
  case "zsh":
52059
- return join6(home, ".zshrc");
53990
+ return join11(home, ".zshrc");
52060
53991
  case "fish":
52061
- return join6(home, ".config", "fish", "completions", "dbcli.fish");
53992
+ return join11(home, ".config", "fish", "completions", "dbcli.fish");
52062
53993
  default:
52063
53994
  throw new Error(`Unsupported shell: ${shell}. Supported: bash, zsh, fish`);
52064
53995
  }
@@ -52078,7 +54009,7 @@ var MARKER_END = "# <<< dbcli completion <<<";
52078
54009
  async function installCompletion(shell, script) {
52079
54010
  const targetPath = getInstallPath2(shell);
52080
54011
  if (shell === "fish") {
52081
- const dir = join6(homedir2(), ".config", "fish", "completions");
54012
+ const dir = join11(homedir2(), ".config", "fish", "completions");
52082
54013
  await Bun.$`mkdir -p ${dir}`.quiet();
52083
54014
  await Bun.file(targetPath).write(script);
52084
54015
  console.log(colors.success(`\u2713 Fish completion installed to ${targetPath}`));
@@ -52220,22 +54151,34 @@ async function checkForUpdate(currentVersion, cachePath, existingCache) {
52220
54151
 
52221
54152
  // src/commands/upgrade.ts
52222
54153
  function formatAlreadyUpToDate(version) {
52223
- return colors.success(`\u2713 Already up to date (v${version})`);
54154
+ return colors.success(t_vars("upgrade.already_up_to_date", { version }));
52224
54155
  }
52225
54156
  function formatUpgradeMessage(currentVersion, latestVersion) {
52226
54157
  return [
52227
- colors.info(` Current version : v${currentVersion}`),
52228
- colors.success(` Latest version : v${latestVersion}`)
54158
+ colors.info(` ${t_vars("upgrade.current_version", { version: currentVersion })}`),
54159
+ colors.success(` ${t_vars("upgrade.latest_version", { version: latestVersion })}`)
52229
54160
  ].join(`
52230
54161
  `);
52231
54162
  }
52232
54163
  function formatUpdateHint(latestVersion) {
52233
- return colors.warn(`[INFO] dbcli v${latestVersion} available. Run "dbcli upgrade" to upgrade.`);
54164
+ return colors.warn(t_vars("upgrade.update_hint", { version: latestVersion }));
54165
+ }
54166
+ function formatSkillUpdateReminder(platforms) {
54167
+ if (platforms.length === 0)
54168
+ return "";
54169
+ return [
54170
+ colors.warn(`
54171
+ ${t("skill.update_available")}`),
54172
+ ...platforms.map((p) => colors.info(` - ${p}`)),
54173
+ colors.dim(`
54174
+ ${t("skill.update_hint")}`)
54175
+ ].join(`
54176
+ `);
52234
54177
  }
52235
- var upgradeCommand = new Command("upgrade").description("Check for updates and upgrade dbcli to the latest version").option("--check", "Only check for updates, do not upgrade").action(async (options) => {
54178
+ var upgradeCommand = new Command("upgrade").description(t("upgrade.description")).option("--check", "Only check for updates, do not upgrade").action(async (options) => {
52236
54179
  const configPath = upgradeCommand.parent?.opts().config ?? ".dbcli";
52237
54180
  const currentVersion = package_default.version;
52238
- console.log(colors.bold("Checking for updates..."));
54181
+ console.log(colors.bold(t("upgrade.checking")));
52239
54182
  let cachePath = null;
52240
54183
  try {
52241
54184
  const file = Bun.file(configPath);
@@ -52247,41 +54190,53 @@ var upgradeCommand = new Command("upgrade").description("Check for updates and u
52247
54190
  cachePath = null;
52248
54191
  }
52249
54192
  const result = await checkForUpdate(currentVersion, cachePath);
54193
+ const outdatedSkills = await checkSkillUpdates();
52250
54194
  if (!result) {
52251
- console.error(colors.warn("\u26A0 Could not check for updates (network error or registry unavailable)"));
54195
+ console.error(colors.warn(t("upgrade.network_error")));
54196
+ if (outdatedSkills.length > 0) {
54197
+ console.log(formatSkillUpdateReminder(outdatedSkills));
54198
+ }
52252
54199
  process.exit(0);
52253
54200
  }
52254
54201
  if (!result.hasUpdate) {
52255
54202
  console.log(formatAlreadyUpToDate(currentVersion));
54203
+ if (outdatedSkills.length > 0) {
54204
+ console.log(formatSkillUpdateReminder(outdatedSkills));
54205
+ }
52256
54206
  process.exit(0);
52257
54207
  }
52258
54208
  console.log(colors.warn(`
52259
- New version available: v${result.latestVersion}`));
54209
+ ${t_vars("upgrade.new_version_available", { version: result.latestVersion })}`));
52260
54210
  console.log(formatUpgradeMessage(currentVersion, result.latestVersion));
52261
54211
  console.log();
52262
54212
  if (options.check) {
52263
- console.log(colors.dim(' Run "dbcli upgrade" to install the update.'));
54213
+ console.log(colors.dim(` ${t("upgrade.install_hint")}`));
54214
+ if (outdatedSkills.length > 0) {
54215
+ console.log(formatSkillUpdateReminder(outdatedSkills));
54216
+ }
52264
54217
  process.exit(0);
52265
54218
  }
52266
- console.log(colors.bold("Upgrading..."));
52267
- console.log(colors.dim(` bun add -g @carllee1983/dbcli@latest`));
54219
+ console.log(colors.bold(t("upgrade.upgrading")));
54220
+ console.log(colors.dim(` ${t("upgrade.manual_hint")}`));
52268
54221
  console.log();
52269
54222
  const proc = Bun.$`bun add -g @carllee1983/dbcli@latest`.nothrow();
52270
54223
  const result2 = await proc;
52271
54224
  if (result2.exitCode === 0) {
52272
54225
  console.log(colors.success(`
52273
- \u2713 Successfully upgraded to v${result.latestVersion}`));
54226
+ ${t_vars("upgrade.success", { version: result.latestVersion })}`));
54227
+ console.log(colors.dim(`
54228
+ ${t("upgrade.skill_recheck_hint")}`));
52274
54229
  } else {
52275
54230
  console.error(colors.error(`
52276
- \u2717 Upgrade failed. Try running manually:`));
52277
- console.error(colors.dim(" bun add -g @carllee1983/dbcli@latest"));
54231
+ ${t("upgrade.failed")}`));
54232
+ console.error(colors.dim(` ${t("upgrade.manual_hint")}`));
52278
54233
  process.exit(1);
52279
54234
  }
52280
54235
  });
52281
54236
 
52282
54237
  // src/commands/shell.ts
52283
54238
  import { createInterface as createInterface2 } from "readline";
52284
- import { join as join7 } from "path";
54239
+ import { join as join12 } from "path";
52285
54240
  import { homedir as homedir3 } from "os";
52286
54241
 
52287
54242
  // src/core/repl/types.ts
@@ -52969,7 +54924,7 @@ function matchWithSuffix(candidates, prefix) {
52969
54924
 
52970
54925
  // src/commands/shell.ts
52971
54926
  var import_picocolors4 = __toESM(require_picocolors(), 1);
52972
- var HISTORY_PATH = join7(homedir3(), ".dbcli_history");
54927
+ var HISTORY_PATH = join12(homedir3(), ".dbcli_history");
52973
54928
  var shellCommand = new Command("shell").description("Interactive database shell with auto-completion and syntax highlighting").option("--sql", "SQL-only mode (skip dbcli command parsing)").action(async (options) => {
52974
54929
  const globalOpts = shellCommand.optsWithGlobals?.() ?? {};
52975
54930
  const configPath = globalOpts.config ?? ".dbcli";
@@ -53868,7 +55823,7 @@ addExecOpts(migrateCommand.command("drop-enum <name>").description(t("migrate.dr
53868
55823
  });
53869
55824
 
53870
55825
  // src/commands/use.ts
53871
- import { join as join8 } from "path";
55826
+ import { join as join13 } from "path";
53872
55827
  async function switchDefault(configPath, name, config) {
53873
55828
  if (!config.connections[name]) {
53874
55829
  const available = Object.keys(config.connections).join(", ");
@@ -53890,7 +55845,7 @@ function listConnectionsForDisplay(config) {
53890
55845
  });
53891
55846
  }
53892
55847
  async function ensureV2Config(configPath) {
53893
- const configFile = Bun.file(join8(configPath, "config.json"));
55848
+ const configFile = Bun.file(join13(configPath, "config.json"));
53894
55849
  if (!await configFile.exists()) {
53895
55850
  throw new ConfigError(t("init.config_not_found"));
53896
55851
  }
@@ -53927,7 +55882,7 @@ var useCommand = new Command("use").description("Switch or display the default d
53927
55882
  });
53928
55883
 
53929
55884
  // src/cli.ts
53930
- import { join as join9 } from "path";
55885
+ import { join as join14 } from "path";
53931
55886
  var _bgVersionCheckResult;
53932
55887
  var program2 = new Command().name("dbcli").description("Database CLI for AI agents").version(package_default.version).option("--no-color", "Disable colored output").option("-v, --verbose", "Increase verbosity (-v verbose, -vv debug)", (_, prev) => prev + 1, 0).option("-q, --quiet", "Suppress non-essential output").option("--config <path>", "Path to .dbcli config file", ".dbcli").option("--use <connection>", "Use a specific named connection (v2 config)");
53933
55888
  program2.hook("preAction", (thisCommand, actionCommand) => {
@@ -53953,7 +55908,7 @@ program2.hook("preAction", (thisCommand, actionCommand) => {
53953
55908
  try {
53954
55909
  let cache = null;
53955
55910
  try {
53956
- const cacheFile = Bun.file(join9(configPath, "version-check.json"));
55911
+ const cacheFile = Bun.file(join14(configPath, "version-check.json"));
53957
55912
  if (await cacheFile.exists()) {
53958
55913
  cache = await cacheFile.json();
53959
55914
  }
@@ -53966,16 +55921,24 @@ program2.hook("preAction", (thisCommand, actionCommand) => {
53966
55921
  })();
53967
55922
  }
53968
55923
  });
53969
- program2.hook("postAction", () => {
55924
+ program2.hook("postAction", async (thisCommand, actionCommand) => {
53970
55925
  if (_bgVersionCheckResult?.hasUpdate) {
53971
55926
  process.stderr.write(formatUpdateHint(_bgVersionCheckResult.latestVersion) + `
53972
55927
  `);
53973
55928
  }
55929
+ const isUpgradeOrSkill = ["upgrade", "skill"].includes(actionCommand.name());
55930
+ if (!thisCommand.opts().quiet && !isUpgradeOrSkill) {
55931
+ const outdatedSkills = await checkSkillUpdates();
55932
+ if (outdatedSkills.length > 0) {
55933
+ process.stderr.write(formatSkillUpdateReminder(outdatedSkills) + `
55934
+ `);
55935
+ }
55936
+ }
53974
55937
  });
53975
55938
  program2.addCommand(initCommand);
53976
55939
  program2.addCommand(listCommand);
53977
55940
  program2.addCommand(schemaCommand);
53978
- program2.command("query <sql>").description(t("query.description")).option("--format <type>", "Output format: table, json, csv", "table").option("--limit <number>", "Limit result rows (overrides auto-limit)", undefined, parseInt).option("--no-limit", "Disable auto-limit in query-only mode").action(async (sql, options) => {
55941
+ program2.command("query <sql>").description(t("query.description")).option("--format <type>", "Output format: table, json, csv", "table").option("--limit <number>", "Limit result rows (overrides auto-limit)", (val) => parseInt(val, 10)).option("--no-limit", "Disable auto-limit in query-only mode").action(async (sql, options) => {
53979
55942
  try {
53980
55943
  await queryCommand(sql, options);
53981
55944
  } catch (error) {