@clef-sh/core 0.1.17 → 0.1.18

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/index.js CHANGED
@@ -301,10 +301,10 @@ var require_lib = __commonJS({
301
301
  "node_modules/write-file-atomic/lib/index.js"(exports2, module2) {
302
302
  "use strict";
303
303
  module2.exports = writeFile;
304
- module2.exports.sync = writeFileSync6;
304
+ module2.exports.sync = writeFileSync7;
305
305
  module2.exports._getTmpname = getTmpname;
306
306
  module2.exports._cleanupOnExit = cleanupOnExit;
307
- var fs18 = require("fs");
307
+ var fs19 = require("fs");
308
308
  var crypto5 = require("node:crypto");
309
309
  var { onExit } = require_cjs();
310
310
  var path26 = require("path");
@@ -325,7 +325,7 @@ var require_lib = __commonJS({
325
325
  function cleanupOnExit(tmpfile) {
326
326
  return () => {
327
327
  try {
328
- fs18.unlinkSync(typeof tmpfile === "function" ? tmpfile() : tmpfile);
328
+ fs19.unlinkSync(typeof tmpfile === "function" ? tmpfile() : tmpfile);
329
329
  } catch {
330
330
  }
331
331
  };
@@ -363,10 +363,10 @@ var require_lib = __commonJS({
363
363
  const absoluteName = path26.resolve(filename);
364
364
  try {
365
365
  await serializeActiveFile(absoluteName);
366
- const truename = await promisify(fs18.realpath)(filename).catch(() => filename);
366
+ const truename = await promisify(fs19.realpath)(filename).catch(() => filename);
367
367
  tmpfile = getTmpname(truename);
368
368
  if (!options.mode || !options.chown) {
369
- const stats = await promisify(fs18.stat)(truename).catch(() => {
369
+ const stats = await promisify(fs19.stat)(truename).catch(() => {
370
370
  });
371
371
  if (stats) {
372
372
  if (options.mode == null) {
@@ -377,45 +377,45 @@ var require_lib = __commonJS({
377
377
  }
378
378
  }
379
379
  }
380
- fd = await promisify(fs18.open)(tmpfile, "w", options.mode);
380
+ fd = await promisify(fs19.open)(tmpfile, "w", options.mode);
381
381
  if (options.tmpfileCreated) {
382
382
  await options.tmpfileCreated(tmpfile);
383
383
  }
384
384
  if (ArrayBuffer.isView(data)) {
385
- await promisify(fs18.write)(fd, data, 0, data.length, 0);
385
+ await promisify(fs19.write)(fd, data, 0, data.length, 0);
386
386
  } else if (data != null) {
387
- await promisify(fs18.write)(fd, String(data), 0, String(options.encoding || "utf8"));
387
+ await promisify(fs19.write)(fd, String(data), 0, String(options.encoding || "utf8"));
388
388
  }
389
389
  if (options.fsync !== false) {
390
- await promisify(fs18.fsync)(fd);
390
+ await promisify(fs19.fsync)(fd);
391
391
  }
392
- await promisify(fs18.close)(fd);
392
+ await promisify(fs19.close)(fd);
393
393
  fd = null;
394
394
  if (options.chown) {
395
- await promisify(fs18.chown)(tmpfile, options.chown.uid, options.chown.gid).catch((err) => {
395
+ await promisify(fs19.chown)(tmpfile, options.chown.uid, options.chown.gid).catch((err) => {
396
396
  if (!isChownErrOk(err)) {
397
397
  throw err;
398
398
  }
399
399
  });
400
400
  }
401
401
  if (options.mode) {
402
- await promisify(fs18.chmod)(tmpfile, options.mode).catch((err) => {
402
+ await promisify(fs19.chmod)(tmpfile, options.mode).catch((err) => {
403
403
  if (!isChownErrOk(err)) {
404
404
  throw err;
405
405
  }
406
406
  });
407
407
  }
408
- await promisify(fs18.rename)(tmpfile, truename);
408
+ await promisify(fs19.rename)(tmpfile, truename);
409
409
  } finally {
410
410
  if (fd) {
411
- await promisify(fs18.close)(fd).catch(
411
+ await promisify(fs19.close)(fd).catch(
412
412
  /* istanbul ignore next */
413
413
  () => {
414
414
  }
415
415
  );
416
416
  }
417
417
  removeOnExitHandler();
418
- await promisify(fs18.unlink)(tmpfile).catch(() => {
418
+ await promisify(fs19.unlink)(tmpfile).catch(() => {
419
419
  });
420
420
  activeFiles[absoluteName].shift();
421
421
  if (activeFiles[absoluteName].length > 0) {
@@ -441,20 +441,20 @@ var require_lib = __commonJS({
441
441
  }
442
442
  return promise;
443
443
  }
444
- function writeFileSync6(filename, data, options) {
444
+ function writeFileSync7(filename, data, options) {
445
445
  if (typeof options === "string") {
446
446
  options = { encoding: options };
447
447
  } else if (!options) {
448
448
  options = {};
449
449
  }
450
450
  try {
451
- filename = fs18.realpathSync(filename);
451
+ filename = fs19.realpathSync(filename);
452
452
  } catch (ex) {
453
453
  }
454
454
  const tmpfile = getTmpname(filename);
455
455
  if (!options.mode || !options.chown) {
456
456
  try {
457
- const stats = fs18.statSync(filename);
457
+ const stats = fs19.statSync(filename);
458
458
  options = Object.assign({}, options);
459
459
  if (!options.mode) {
460
460
  options.mode = stats.mode;
@@ -470,23 +470,23 @@ var require_lib = __commonJS({
470
470
  const removeOnExitHandler = onExit(cleanup);
471
471
  let threw = true;
472
472
  try {
473
- fd = fs18.openSync(tmpfile, "w", options.mode || 438);
473
+ fd = fs19.openSync(tmpfile, "w", options.mode || 438);
474
474
  if (options.tmpfileCreated) {
475
475
  options.tmpfileCreated(tmpfile);
476
476
  }
477
477
  if (ArrayBuffer.isView(data)) {
478
- fs18.writeSync(fd, data, 0, data.length, 0);
478
+ fs19.writeSync(fd, data, 0, data.length, 0);
479
479
  } else if (data != null) {
480
- fs18.writeSync(fd, String(data), 0, String(options.encoding || "utf8"));
480
+ fs19.writeSync(fd, String(data), 0, String(options.encoding || "utf8"));
481
481
  }
482
482
  if (options.fsync !== false) {
483
- fs18.fsyncSync(fd);
483
+ fs19.fsyncSync(fd);
484
484
  }
485
- fs18.closeSync(fd);
485
+ fs19.closeSync(fd);
486
486
  fd = null;
487
487
  if (options.chown) {
488
488
  try {
489
- fs18.chownSync(tmpfile, options.chown.uid, options.chown.gid);
489
+ fs19.chownSync(tmpfile, options.chown.uid, options.chown.gid);
490
490
  } catch (err) {
491
491
  if (!isChownErrOk(err)) {
492
492
  throw err;
@@ -495,19 +495,19 @@ var require_lib = __commonJS({
495
495
  }
496
496
  if (options.mode) {
497
497
  try {
498
- fs18.chmodSync(tmpfile, options.mode);
498
+ fs19.chmodSync(tmpfile, options.mode);
499
499
  } catch (err) {
500
500
  if (!isChownErrOk(err)) {
501
501
  throw err;
502
502
  }
503
503
  }
504
504
  }
505
- fs18.renameSync(tmpfile, filename);
505
+ fs19.renameSync(tmpfile, filename);
506
506
  threw = false;
507
507
  } finally {
508
508
  if (fd) {
509
509
  try {
510
- fs18.closeSync(fd);
510
+ fs19.closeSync(fd);
511
511
  } catch (ex) {
512
512
  }
513
513
  }
@@ -546,54 +546,54 @@ var require_polyfills = __commonJS({
546
546
  }
547
547
  var chdir;
548
548
  module2.exports = patch;
549
- function patch(fs18) {
549
+ function patch(fs19) {
550
550
  if (constants.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
551
- patchLchmod(fs18);
552
- }
553
- if (!fs18.lutimes) {
554
- patchLutimes(fs18);
555
- }
556
- fs18.chown = chownFix(fs18.chown);
557
- fs18.fchown = chownFix(fs18.fchown);
558
- fs18.lchown = chownFix(fs18.lchown);
559
- fs18.chmod = chmodFix(fs18.chmod);
560
- fs18.fchmod = chmodFix(fs18.fchmod);
561
- fs18.lchmod = chmodFix(fs18.lchmod);
562
- fs18.chownSync = chownFixSync(fs18.chownSync);
563
- fs18.fchownSync = chownFixSync(fs18.fchownSync);
564
- fs18.lchownSync = chownFixSync(fs18.lchownSync);
565
- fs18.chmodSync = chmodFixSync(fs18.chmodSync);
566
- fs18.fchmodSync = chmodFixSync(fs18.fchmodSync);
567
- fs18.lchmodSync = chmodFixSync(fs18.lchmodSync);
568
- fs18.stat = statFix(fs18.stat);
569
- fs18.fstat = statFix(fs18.fstat);
570
- fs18.lstat = statFix(fs18.lstat);
571
- fs18.statSync = statFixSync(fs18.statSync);
572
- fs18.fstatSync = statFixSync(fs18.fstatSync);
573
- fs18.lstatSync = statFixSync(fs18.lstatSync);
574
- if (fs18.chmod && !fs18.lchmod) {
575
- fs18.lchmod = function(path26, mode, cb) {
551
+ patchLchmod(fs19);
552
+ }
553
+ if (!fs19.lutimes) {
554
+ patchLutimes(fs19);
555
+ }
556
+ fs19.chown = chownFix(fs19.chown);
557
+ fs19.fchown = chownFix(fs19.fchown);
558
+ fs19.lchown = chownFix(fs19.lchown);
559
+ fs19.chmod = chmodFix(fs19.chmod);
560
+ fs19.fchmod = chmodFix(fs19.fchmod);
561
+ fs19.lchmod = chmodFix(fs19.lchmod);
562
+ fs19.chownSync = chownFixSync(fs19.chownSync);
563
+ fs19.fchownSync = chownFixSync(fs19.fchownSync);
564
+ fs19.lchownSync = chownFixSync(fs19.lchownSync);
565
+ fs19.chmodSync = chmodFixSync(fs19.chmodSync);
566
+ fs19.fchmodSync = chmodFixSync(fs19.fchmodSync);
567
+ fs19.lchmodSync = chmodFixSync(fs19.lchmodSync);
568
+ fs19.stat = statFix(fs19.stat);
569
+ fs19.fstat = statFix(fs19.fstat);
570
+ fs19.lstat = statFix(fs19.lstat);
571
+ fs19.statSync = statFixSync(fs19.statSync);
572
+ fs19.fstatSync = statFixSync(fs19.fstatSync);
573
+ fs19.lstatSync = statFixSync(fs19.lstatSync);
574
+ if (fs19.chmod && !fs19.lchmod) {
575
+ fs19.lchmod = function(path26, mode, cb) {
576
576
  if (cb) process.nextTick(cb);
577
577
  };
578
- fs18.lchmodSync = function() {
578
+ fs19.lchmodSync = function() {
579
579
  };
580
580
  }
581
- if (fs18.chown && !fs18.lchown) {
582
- fs18.lchown = function(path26, uid, gid, cb) {
581
+ if (fs19.chown && !fs19.lchown) {
582
+ fs19.lchown = function(path26, uid, gid, cb) {
583
583
  if (cb) process.nextTick(cb);
584
584
  };
585
- fs18.lchownSync = function() {
585
+ fs19.lchownSync = function() {
586
586
  };
587
587
  }
588
588
  if (platform === "win32") {
589
- fs18.rename = typeof fs18.rename !== "function" ? fs18.rename : (function(fs$rename) {
589
+ fs19.rename = typeof fs19.rename !== "function" ? fs19.rename : (function(fs$rename) {
590
590
  function rename(from, to, cb) {
591
591
  var start = Date.now();
592
592
  var backoff = 0;
593
593
  fs$rename(from, to, function CB(er) {
594
594
  if (er && (er.code === "EACCES" || er.code === "EPERM" || er.code === "EBUSY") && Date.now() - start < 6e4) {
595
595
  setTimeout(function() {
596
- fs18.stat(to, function(stater, st) {
596
+ fs19.stat(to, function(stater, st) {
597
597
  if (stater && stater.code === "ENOENT")
598
598
  fs$rename(from, to, CB);
599
599
  else
@@ -609,9 +609,9 @@ var require_polyfills = __commonJS({
609
609
  }
610
610
  if (Object.setPrototypeOf) Object.setPrototypeOf(rename, fs$rename);
611
611
  return rename;
612
- })(fs18.rename);
612
+ })(fs19.rename);
613
613
  }
614
- fs18.read = typeof fs18.read !== "function" ? fs18.read : (function(fs$read) {
614
+ fs19.read = typeof fs19.read !== "function" ? fs19.read : (function(fs$read) {
615
615
  function read(fd, buffer, offset, length, position, callback_) {
616
616
  var callback;
617
617
  if (callback_ && typeof callback_ === "function") {
@@ -619,22 +619,22 @@ var require_polyfills = __commonJS({
619
619
  callback = function(er, _, __) {
620
620
  if (er && er.code === "EAGAIN" && eagCounter < 10) {
621
621
  eagCounter++;
622
- return fs$read.call(fs18, fd, buffer, offset, length, position, callback);
622
+ return fs$read.call(fs19, fd, buffer, offset, length, position, callback);
623
623
  }
624
624
  callback_.apply(this, arguments);
625
625
  };
626
626
  }
627
- return fs$read.call(fs18, fd, buffer, offset, length, position, callback);
627
+ return fs$read.call(fs19, fd, buffer, offset, length, position, callback);
628
628
  }
629
629
  if (Object.setPrototypeOf) Object.setPrototypeOf(read, fs$read);
630
630
  return read;
631
- })(fs18.read);
632
- fs18.readSync = typeof fs18.readSync !== "function" ? fs18.readSync : /* @__PURE__ */ (function(fs$readSync) {
631
+ })(fs19.read);
632
+ fs19.readSync = typeof fs19.readSync !== "function" ? fs19.readSync : /* @__PURE__ */ (function(fs$readSync) {
633
633
  return function(fd, buffer, offset, length, position) {
634
634
  var eagCounter = 0;
635
635
  while (true) {
636
636
  try {
637
- return fs$readSync.call(fs18, fd, buffer, offset, length, position);
637
+ return fs$readSync.call(fs19, fd, buffer, offset, length, position);
638
638
  } catch (er) {
639
639
  if (er.code === "EAGAIN" && eagCounter < 10) {
640
640
  eagCounter++;
@@ -644,10 +644,10 @@ var require_polyfills = __commonJS({
644
644
  }
645
645
  }
646
646
  };
647
- })(fs18.readSync);
648
- function patchLchmod(fs19) {
649
- fs19.lchmod = function(path26, mode, callback) {
650
- fs19.open(
647
+ })(fs19.readSync);
648
+ function patchLchmod(fs20) {
649
+ fs20.lchmod = function(path26, mode, callback) {
650
+ fs20.open(
651
651
  path26,
652
652
  constants.O_WRONLY | constants.O_SYMLINK,
653
653
  mode,
@@ -656,80 +656,80 @@ var require_polyfills = __commonJS({
656
656
  if (callback) callback(err);
657
657
  return;
658
658
  }
659
- fs19.fchmod(fd, mode, function(err2) {
660
- fs19.close(fd, function(err22) {
659
+ fs20.fchmod(fd, mode, function(err2) {
660
+ fs20.close(fd, function(err22) {
661
661
  if (callback) callback(err2 || err22);
662
662
  });
663
663
  });
664
664
  }
665
665
  );
666
666
  };
667
- fs19.lchmodSync = function(path26, mode) {
668
- var fd = fs19.openSync(path26, constants.O_WRONLY | constants.O_SYMLINK, mode);
667
+ fs20.lchmodSync = function(path26, mode) {
668
+ var fd = fs20.openSync(path26, constants.O_WRONLY | constants.O_SYMLINK, mode);
669
669
  var threw = true;
670
670
  var ret;
671
671
  try {
672
- ret = fs19.fchmodSync(fd, mode);
672
+ ret = fs20.fchmodSync(fd, mode);
673
673
  threw = false;
674
674
  } finally {
675
675
  if (threw) {
676
676
  try {
677
- fs19.closeSync(fd);
677
+ fs20.closeSync(fd);
678
678
  } catch (er) {
679
679
  }
680
680
  } else {
681
- fs19.closeSync(fd);
681
+ fs20.closeSync(fd);
682
682
  }
683
683
  }
684
684
  return ret;
685
685
  };
686
686
  }
687
- function patchLutimes(fs19) {
688
- if (constants.hasOwnProperty("O_SYMLINK") && fs19.futimes) {
689
- fs19.lutimes = function(path26, at, mt, cb) {
690
- fs19.open(path26, constants.O_SYMLINK, function(er, fd) {
687
+ function patchLutimes(fs20) {
688
+ if (constants.hasOwnProperty("O_SYMLINK") && fs20.futimes) {
689
+ fs20.lutimes = function(path26, at, mt, cb) {
690
+ fs20.open(path26, constants.O_SYMLINK, function(er, fd) {
691
691
  if (er) {
692
692
  if (cb) cb(er);
693
693
  return;
694
694
  }
695
- fs19.futimes(fd, at, mt, function(er2) {
696
- fs19.close(fd, function(er22) {
695
+ fs20.futimes(fd, at, mt, function(er2) {
696
+ fs20.close(fd, function(er22) {
697
697
  if (cb) cb(er2 || er22);
698
698
  });
699
699
  });
700
700
  });
701
701
  };
702
- fs19.lutimesSync = function(path26, at, mt) {
703
- var fd = fs19.openSync(path26, constants.O_SYMLINK);
702
+ fs20.lutimesSync = function(path26, at, mt) {
703
+ var fd = fs20.openSync(path26, constants.O_SYMLINK);
704
704
  var ret;
705
705
  var threw = true;
706
706
  try {
707
- ret = fs19.futimesSync(fd, at, mt);
707
+ ret = fs20.futimesSync(fd, at, mt);
708
708
  threw = false;
709
709
  } finally {
710
710
  if (threw) {
711
711
  try {
712
- fs19.closeSync(fd);
712
+ fs20.closeSync(fd);
713
713
  } catch (er) {
714
714
  }
715
715
  } else {
716
- fs19.closeSync(fd);
716
+ fs20.closeSync(fd);
717
717
  }
718
718
  }
719
719
  return ret;
720
720
  };
721
- } else if (fs19.futimes) {
722
- fs19.lutimes = function(_a, _b, _c, cb) {
721
+ } else if (fs20.futimes) {
722
+ fs20.lutimes = function(_a, _b, _c, cb) {
723
723
  if (cb) process.nextTick(cb);
724
724
  };
725
- fs19.lutimesSync = function() {
725
+ fs20.lutimesSync = function() {
726
726
  };
727
727
  }
728
728
  }
729
729
  function chmodFix(orig) {
730
730
  if (!orig) return orig;
731
731
  return function(target, mode, cb) {
732
- return orig.call(fs18, target, mode, function(er) {
732
+ return orig.call(fs19, target, mode, function(er) {
733
733
  if (chownErOk(er)) er = null;
734
734
  if (cb) cb.apply(this, arguments);
735
735
  });
@@ -739,7 +739,7 @@ var require_polyfills = __commonJS({
739
739
  if (!orig) return orig;
740
740
  return function(target, mode) {
741
741
  try {
742
- return orig.call(fs18, target, mode);
742
+ return orig.call(fs19, target, mode);
743
743
  } catch (er) {
744
744
  if (!chownErOk(er)) throw er;
745
745
  }
@@ -748,7 +748,7 @@ var require_polyfills = __commonJS({
748
748
  function chownFix(orig) {
749
749
  if (!orig) return orig;
750
750
  return function(target, uid, gid, cb) {
751
- return orig.call(fs18, target, uid, gid, function(er) {
751
+ return orig.call(fs19, target, uid, gid, function(er) {
752
752
  if (chownErOk(er)) er = null;
753
753
  if (cb) cb.apply(this, arguments);
754
754
  });
@@ -758,7 +758,7 @@ var require_polyfills = __commonJS({
758
758
  if (!orig) return orig;
759
759
  return function(target, uid, gid) {
760
760
  try {
761
- return orig.call(fs18, target, uid, gid);
761
+ return orig.call(fs19, target, uid, gid);
762
762
  } catch (er) {
763
763
  if (!chownErOk(er)) throw er;
764
764
  }
@@ -778,13 +778,13 @@ var require_polyfills = __commonJS({
778
778
  }
779
779
  if (cb) cb.apply(this, arguments);
780
780
  }
781
- return options ? orig.call(fs18, target, options, callback) : orig.call(fs18, target, callback);
781
+ return options ? orig.call(fs19, target, options, callback) : orig.call(fs19, target, callback);
782
782
  };
783
783
  }
784
784
  function statFixSync(orig) {
785
785
  if (!orig) return orig;
786
786
  return function(target, options) {
787
- var stats = options ? orig.call(fs18, target, options) : orig.call(fs18, target);
787
+ var stats = options ? orig.call(fs19, target, options) : orig.call(fs19, target);
788
788
  if (stats) {
789
789
  if (stats.uid < 0) stats.uid += 4294967296;
790
790
  if (stats.gid < 0) stats.gid += 4294967296;
@@ -813,7 +813,7 @@ var require_legacy_streams = __commonJS({
813
813
  "../../node_modules/graceful-fs/legacy-streams.js"(exports2, module2) {
814
814
  var Stream = require("stream").Stream;
815
815
  module2.exports = legacy;
816
- function legacy(fs18) {
816
+ function legacy(fs19) {
817
817
  return {
818
818
  ReadStream,
819
819
  WriteStream
@@ -856,7 +856,7 @@ var require_legacy_streams = __commonJS({
856
856
  });
857
857
  return;
858
858
  }
859
- fs18.open(this.path, this.flags, this.mode, function(err, fd) {
859
+ fs19.open(this.path, this.flags, this.mode, function(err, fd) {
860
860
  if (err) {
861
861
  self.emit("error", err);
862
862
  self.readable = false;
@@ -895,7 +895,7 @@ var require_legacy_streams = __commonJS({
895
895
  this.busy = false;
896
896
  this._queue = [];
897
897
  if (this.fd === null) {
898
- this._open = fs18.open;
898
+ this._open = fs19.open;
899
899
  this._queue.push([this._open, this.path, this.flags, this.mode, void 0]);
900
900
  this.flush();
901
901
  }
@@ -930,7 +930,7 @@ var require_clone = __commonJS({
930
930
  // ../../node_modules/graceful-fs/graceful-fs.js
931
931
  var require_graceful_fs = __commonJS({
932
932
  "../../node_modules/graceful-fs/graceful-fs.js"(exports2, module2) {
933
- var fs18 = require("fs");
933
+ var fs19 = require("fs");
934
934
  var polyfills = require_polyfills();
935
935
  var legacy = require_legacy_streams();
936
936
  var clone = require_clone();
@@ -962,12 +962,12 @@ var require_graceful_fs = __commonJS({
962
962
  m = "GFS4: " + m.split(/\n/).join("\nGFS4: ");
963
963
  console.error(m);
964
964
  };
965
- if (!fs18[gracefulQueue]) {
965
+ if (!fs19[gracefulQueue]) {
966
966
  queue = global[gracefulQueue] || [];
967
- publishQueue(fs18, queue);
968
- fs18.close = (function(fs$close) {
967
+ publishQueue(fs19, queue);
968
+ fs19.close = (function(fs$close) {
969
969
  function close(fd, cb) {
970
- return fs$close.call(fs18, fd, function(err) {
970
+ return fs$close.call(fs19, fd, function(err) {
971
971
  if (!err) {
972
972
  resetQueue();
973
973
  }
@@ -979,40 +979,40 @@ var require_graceful_fs = __commonJS({
979
979
  value: fs$close
980
980
  });
981
981
  return close;
982
- })(fs18.close);
983
- fs18.closeSync = (function(fs$closeSync) {
982
+ })(fs19.close);
983
+ fs19.closeSync = (function(fs$closeSync) {
984
984
  function closeSync2(fd) {
985
- fs$closeSync.apply(fs18, arguments);
985
+ fs$closeSync.apply(fs19, arguments);
986
986
  resetQueue();
987
987
  }
988
988
  Object.defineProperty(closeSync2, previousSymbol, {
989
989
  value: fs$closeSync
990
990
  });
991
991
  return closeSync2;
992
- })(fs18.closeSync);
992
+ })(fs19.closeSync);
993
993
  if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) {
994
994
  process.on("exit", function() {
995
- debug(fs18[gracefulQueue]);
996
- require("assert").equal(fs18[gracefulQueue].length, 0);
995
+ debug(fs19[gracefulQueue]);
996
+ require("assert").equal(fs19[gracefulQueue].length, 0);
997
997
  });
998
998
  }
999
999
  }
1000
1000
  var queue;
1001
1001
  if (!global[gracefulQueue]) {
1002
- publishQueue(global, fs18[gracefulQueue]);
1003
- }
1004
- module2.exports = patch(clone(fs18));
1005
- if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs18.__patched) {
1006
- module2.exports = patch(fs18);
1007
- fs18.__patched = true;
1008
- }
1009
- function patch(fs19) {
1010
- polyfills(fs19);
1011
- fs19.gracefulify = patch;
1012
- fs19.createReadStream = createReadStream;
1013
- fs19.createWriteStream = createWriteStream;
1014
- var fs$readFile = fs19.readFile;
1015
- fs19.readFile = readFile;
1002
+ publishQueue(global, fs19[gracefulQueue]);
1003
+ }
1004
+ module2.exports = patch(clone(fs19));
1005
+ if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs19.__patched) {
1006
+ module2.exports = patch(fs19);
1007
+ fs19.__patched = true;
1008
+ }
1009
+ function patch(fs20) {
1010
+ polyfills(fs20);
1011
+ fs20.gracefulify = patch;
1012
+ fs20.createReadStream = createReadStream;
1013
+ fs20.createWriteStream = createWriteStream;
1014
+ var fs$readFile = fs20.readFile;
1015
+ fs20.readFile = readFile;
1016
1016
  function readFile(path26, options, cb) {
1017
1017
  if (typeof options === "function")
1018
1018
  cb = options, options = null;
@@ -1028,8 +1028,8 @@ var require_graceful_fs = __commonJS({
1028
1028
  });
1029
1029
  }
1030
1030
  }
1031
- var fs$writeFile = fs19.writeFile;
1032
- fs19.writeFile = writeFile;
1031
+ var fs$writeFile = fs20.writeFile;
1032
+ fs20.writeFile = writeFile;
1033
1033
  function writeFile(path26, data, options, cb) {
1034
1034
  if (typeof options === "function")
1035
1035
  cb = options, options = null;
@@ -1045,9 +1045,9 @@ var require_graceful_fs = __commonJS({
1045
1045
  });
1046
1046
  }
1047
1047
  }
1048
- var fs$appendFile = fs19.appendFile;
1048
+ var fs$appendFile = fs20.appendFile;
1049
1049
  if (fs$appendFile)
1050
- fs19.appendFile = appendFile;
1050
+ fs20.appendFile = appendFile;
1051
1051
  function appendFile(path26, data, options, cb) {
1052
1052
  if (typeof options === "function")
1053
1053
  cb = options, options = null;
@@ -1063,9 +1063,9 @@ var require_graceful_fs = __commonJS({
1063
1063
  });
1064
1064
  }
1065
1065
  }
1066
- var fs$copyFile = fs19.copyFile;
1066
+ var fs$copyFile = fs20.copyFile;
1067
1067
  if (fs$copyFile)
1068
- fs19.copyFile = copyFile;
1068
+ fs20.copyFile = copyFile;
1069
1069
  function copyFile(src, dest, flags, cb) {
1070
1070
  if (typeof flags === "function") {
1071
1071
  cb = flags;
@@ -1083,8 +1083,8 @@ var require_graceful_fs = __commonJS({
1083
1083
  });
1084
1084
  }
1085
1085
  }
1086
- var fs$readdir = fs19.readdir;
1087
- fs19.readdir = readdir;
1086
+ var fs$readdir = fs20.readdir;
1087
+ fs20.readdir = readdir;
1088
1088
  var noReaddirOptionVersions = /^v[0-5]\./;
1089
1089
  function readdir(path26, options, cb) {
1090
1090
  if (typeof options === "function")
@@ -1125,21 +1125,21 @@ var require_graceful_fs = __commonJS({
1125
1125
  }
1126
1126
  }
1127
1127
  if (process.version.substr(0, 4) === "v0.8") {
1128
- var legStreams = legacy(fs19);
1128
+ var legStreams = legacy(fs20);
1129
1129
  ReadStream = legStreams.ReadStream;
1130
1130
  WriteStream = legStreams.WriteStream;
1131
1131
  }
1132
- var fs$ReadStream = fs19.ReadStream;
1132
+ var fs$ReadStream = fs20.ReadStream;
1133
1133
  if (fs$ReadStream) {
1134
1134
  ReadStream.prototype = Object.create(fs$ReadStream.prototype);
1135
1135
  ReadStream.prototype.open = ReadStream$open;
1136
1136
  }
1137
- var fs$WriteStream = fs19.WriteStream;
1137
+ var fs$WriteStream = fs20.WriteStream;
1138
1138
  if (fs$WriteStream) {
1139
1139
  WriteStream.prototype = Object.create(fs$WriteStream.prototype);
1140
1140
  WriteStream.prototype.open = WriteStream$open;
1141
1141
  }
1142
- Object.defineProperty(fs19, "ReadStream", {
1142
+ Object.defineProperty(fs20, "ReadStream", {
1143
1143
  get: function() {
1144
1144
  return ReadStream;
1145
1145
  },
@@ -1149,7 +1149,7 @@ var require_graceful_fs = __commonJS({
1149
1149
  enumerable: true,
1150
1150
  configurable: true
1151
1151
  });
1152
- Object.defineProperty(fs19, "WriteStream", {
1152
+ Object.defineProperty(fs20, "WriteStream", {
1153
1153
  get: function() {
1154
1154
  return WriteStream;
1155
1155
  },
@@ -1160,7 +1160,7 @@ var require_graceful_fs = __commonJS({
1160
1160
  configurable: true
1161
1161
  });
1162
1162
  var FileReadStream = ReadStream;
1163
- Object.defineProperty(fs19, "FileReadStream", {
1163
+ Object.defineProperty(fs20, "FileReadStream", {
1164
1164
  get: function() {
1165
1165
  return FileReadStream;
1166
1166
  },
@@ -1171,7 +1171,7 @@ var require_graceful_fs = __commonJS({
1171
1171
  configurable: true
1172
1172
  });
1173
1173
  var FileWriteStream = WriteStream;
1174
- Object.defineProperty(fs19, "FileWriteStream", {
1174
+ Object.defineProperty(fs20, "FileWriteStream", {
1175
1175
  get: function() {
1176
1176
  return FileWriteStream;
1177
1177
  },
@@ -1220,13 +1220,13 @@ var require_graceful_fs = __commonJS({
1220
1220
  });
1221
1221
  }
1222
1222
  function createReadStream(path26, options) {
1223
- return new fs19.ReadStream(path26, options);
1223
+ return new fs20.ReadStream(path26, options);
1224
1224
  }
1225
1225
  function createWriteStream(path26, options) {
1226
- return new fs19.WriteStream(path26, options);
1226
+ return new fs20.WriteStream(path26, options);
1227
1227
  }
1228
- var fs$open = fs19.open;
1229
- fs19.open = open;
1228
+ var fs$open = fs20.open;
1229
+ fs20.open = open;
1230
1230
  function open(path26, flags, mode, cb) {
1231
1231
  if (typeof mode === "function")
1232
1232
  cb = mode, mode = null;
@@ -1242,20 +1242,20 @@ var require_graceful_fs = __commonJS({
1242
1242
  });
1243
1243
  }
1244
1244
  }
1245
- return fs19;
1245
+ return fs20;
1246
1246
  }
1247
1247
  function enqueue(elem) {
1248
1248
  debug("ENQUEUE", elem[0].name, elem[1]);
1249
- fs18[gracefulQueue].push(elem);
1249
+ fs19[gracefulQueue].push(elem);
1250
1250
  retry();
1251
1251
  }
1252
1252
  var retryTimer;
1253
1253
  function resetQueue() {
1254
1254
  var now = Date.now();
1255
- for (var i = 0; i < fs18[gracefulQueue].length; ++i) {
1256
- if (fs18[gracefulQueue][i].length > 2) {
1257
- fs18[gracefulQueue][i][3] = now;
1258
- fs18[gracefulQueue][i][4] = now;
1255
+ for (var i = 0; i < fs19[gracefulQueue].length; ++i) {
1256
+ if (fs19[gracefulQueue][i].length > 2) {
1257
+ fs19[gracefulQueue][i][3] = now;
1258
+ fs19[gracefulQueue][i][4] = now;
1259
1259
  }
1260
1260
  }
1261
1261
  retry();
@@ -1263,9 +1263,9 @@ var require_graceful_fs = __commonJS({
1263
1263
  function retry() {
1264
1264
  clearTimeout(retryTimer);
1265
1265
  retryTimer = void 0;
1266
- if (fs18[gracefulQueue].length === 0)
1266
+ if (fs19[gracefulQueue].length === 0)
1267
1267
  return;
1268
- var elem = fs18[gracefulQueue].shift();
1268
+ var elem = fs19[gracefulQueue].shift();
1269
1269
  var fn = elem[0];
1270
1270
  var args = elem[1];
1271
1271
  var err = elem[2];
@@ -1287,7 +1287,7 @@ var require_graceful_fs = __commonJS({
1287
1287
  debug("RETRY", fn.name, args);
1288
1288
  fn.apply(null, args.concat([startTime]));
1289
1289
  } else {
1290
- fs18[gracefulQueue].push(elem);
1290
+ fs19[gracefulQueue].push(elem);
1291
1291
  }
1292
1292
  }
1293
1293
  if (retryTimer === void 0) {
@@ -1722,10 +1722,10 @@ var require_mtime_precision = __commonJS({
1722
1722
  "../../node_modules/proper-lockfile/lib/mtime-precision.js"(exports2, module2) {
1723
1723
  "use strict";
1724
1724
  var cacheSymbol = /* @__PURE__ */ Symbol();
1725
- function probe(file, fs18, callback) {
1726
- const cachedPrecision = fs18[cacheSymbol];
1725
+ function probe(file, fs19, callback) {
1726
+ const cachedPrecision = fs19[cacheSymbol];
1727
1727
  if (cachedPrecision) {
1728
- return fs18.stat(file, (err, stat) => {
1728
+ return fs19.stat(file, (err, stat) => {
1729
1729
  if (err) {
1730
1730
  return callback(err);
1731
1731
  }
@@ -1733,16 +1733,16 @@ var require_mtime_precision = __commonJS({
1733
1733
  });
1734
1734
  }
1735
1735
  const mtime = new Date(Math.ceil(Date.now() / 1e3) * 1e3 + 5);
1736
- fs18.utimes(file, mtime, mtime, (err) => {
1736
+ fs19.utimes(file, mtime, mtime, (err) => {
1737
1737
  if (err) {
1738
1738
  return callback(err);
1739
1739
  }
1740
- fs18.stat(file, (err2, stat) => {
1740
+ fs19.stat(file, (err2, stat) => {
1741
1741
  if (err2) {
1742
1742
  return callback(err2);
1743
1743
  }
1744
1744
  const precision = stat.mtime.getTime() % 1e3 === 0 ? "s" : "ms";
1745
- Object.defineProperty(fs18, cacheSymbol, { value: precision });
1745
+ Object.defineProperty(fs19, cacheSymbol, { value: precision });
1746
1746
  callback(null, stat.mtime, precision);
1747
1747
  });
1748
1748
  });
@@ -1764,7 +1764,7 @@ var require_lockfile = __commonJS({
1764
1764
  "../../node_modules/proper-lockfile/lib/lockfile.js"(exports2, module2) {
1765
1765
  "use strict";
1766
1766
  var path26 = require("path");
1767
- var fs18 = require_graceful_fs();
1767
+ var fs19 = require_graceful_fs();
1768
1768
  var retry = require_retry2();
1769
1769
  var onExit = require_signal_exit();
1770
1770
  var mtimePrecision = require_mtime_precision();
@@ -1895,7 +1895,7 @@ var require_lockfile = __commonJS({
1895
1895
  update: null,
1896
1896
  realpath: true,
1897
1897
  retries: 0,
1898
- fs: fs18,
1898
+ fs: fs19,
1899
1899
  onCompromised: (err) => {
1900
1900
  throw err;
1901
1901
  },
@@ -1939,7 +1939,7 @@ var require_lockfile = __commonJS({
1939
1939
  }
1940
1940
  function unlock(file, options, callback) {
1941
1941
  options = {
1942
- fs: fs18,
1942
+ fs: fs19,
1943
1943
  realpath: true,
1944
1944
  ...options
1945
1945
  };
@@ -1961,7 +1961,7 @@ var require_lockfile = __commonJS({
1961
1961
  options = {
1962
1962
  stale: 1e4,
1963
1963
  realpath: true,
1964
- fs: fs18,
1964
+ fs: fs19,
1965
1965
  ...options
1966
1966
  };
1967
1967
  options.stale = Math.max(options.stale || 0, 2e3);
@@ -2000,16 +2000,16 @@ var require_lockfile = __commonJS({
2000
2000
  var require_adapter = __commonJS({
2001
2001
  "../../node_modules/proper-lockfile/lib/adapter.js"(exports2, module2) {
2002
2002
  "use strict";
2003
- var fs18 = require_graceful_fs();
2004
- function createSyncFs(fs19) {
2003
+ var fs19 = require_graceful_fs();
2004
+ function createSyncFs(fs20) {
2005
2005
  const methods = ["mkdir", "realpath", "stat", "rmdir", "utimes"];
2006
- const newFs = { ...fs19 };
2006
+ const newFs = { ...fs20 };
2007
2007
  methods.forEach((method) => {
2008
2008
  newFs[method] = (...args) => {
2009
2009
  const callback = args.pop();
2010
2010
  let ret;
2011
2011
  try {
2012
- ret = fs19[`${method}Sync`](...args);
2012
+ ret = fs20[`${method}Sync`](...args);
2013
2013
  } catch (err) {
2014
2014
  return callback(err);
2015
2015
  }
@@ -2047,7 +2047,7 @@ var require_adapter = __commonJS({
2047
2047
  }
2048
2048
  function toSyncOptions(options) {
2049
2049
  options = { ...options };
2050
- options.fs = createSyncFs(options.fs || fs18);
2050
+ options.fs = createSyncFs(options.fs || fs19);
2051
2051
  if (typeof options.retries === "number" && options.retries > 0 || options.retries && typeof options.retries.retries === "number" && options.retries.retries > 0) {
2052
2052
  throw Object.assign(new Error("Cannot use retries with the sync api"), { code: "ESYNC" });
2053
2053
  }
@@ -9078,6 +9078,7 @@ __export(index_exports, {
9078
9078
  generateRandomValue: () => generateRandomValue,
9079
9079
  generateSigningKeyPair: () => generateSigningKeyPair,
9080
9080
  getPendingKeys: () => getPendingKeys,
9081
+ getRotations: () => getRotations,
9081
9082
  isHighEntropy: () => isHighEntropy,
9082
9083
  isKmsEnvelope: () => isKmsEnvelope,
9083
9084
  isPending: () => isPending,
@@ -9089,6 +9090,8 @@ __export(index_exports, {
9089
9090
  markPendingWithRetry: () => markPendingWithRetry,
9090
9091
  markResolved: () => markResolved,
9091
9092
  matchPatterns: () => matchPatterns,
9093
+ mergeMetadataContents: () => mergeMetadataContents,
9094
+ mergeMetadataFiles: () => mergeMetadataFiles,
9092
9095
  metadataPath: () => metadataPath,
9093
9096
  parse: () => parse9,
9094
9097
  parseDotenv: () => parseDotenv,
@@ -9096,8 +9099,10 @@ __export(index_exports, {
9096
9099
  parseJson: () => parseJson,
9097
9100
  parseYaml: () => parseYaml,
9098
9101
  readManifestYaml: () => readManifestYaml,
9102
+ recordRotation: () => recordRotation,
9099
9103
  redactValue: () => redactValue,
9100
9104
  removeAccessRequest: () => removeRequest,
9105
+ removeRotation: () => removeRotation,
9101
9106
  requestsFilePath: () => requestsFilePath,
9102
9107
  resetSopsResolution: () => resetSopsResolution,
9103
9108
  resolveBackendConfig: () => resolveBackendConfig,
@@ -9866,6 +9871,20 @@ var PATTERNS = [
9866
9871
  },
9867
9872
  { name: "Database URL", regex: /(?:postgres|mysql|mongodb|redis):\/\/[^:]+:[^@]+@/ }
9868
9873
  ];
9874
+ var PUBLIC_PREFIX_PATTERNS = [
9875
+ // reCAPTCHA v2, v3, and Enterprise site keys are exactly 40 chars and
9876
+ // begin with 6L[c-f]. Site keys are designed to be embedded in HTML.
9877
+ // https://developers.google.com/recaptcha/docs/faq
9878
+ { name: "reCAPTCHA site key", regex: /^6L[c-f][0-9A-Za-z_-]{37}$/ },
9879
+ // Stripe publishable keys (client-side, distinct from sk_live_/sk_test_).
9880
+ { name: "Stripe publishable key", regex: /^pk_(?:live|test)_[0-9a-zA-Z]{24,}$/ }
9881
+ ];
9882
+ function matchPublicPrefix(value) {
9883
+ for (const def of PUBLIC_PREFIX_PATTERNS) {
9884
+ if (def.regex.test(value)) return { name: def.name };
9885
+ }
9886
+ return null;
9887
+ }
9869
9888
  function shannonEntropy(str) {
9870
9889
  if (str.length === 0) return 0;
9871
9890
  const freq = /* @__PURE__ */ new Map();
@@ -10054,7 +10073,8 @@ var ScanRunner = class {
10054
10073
  }
10055
10074
  }
10056
10075
  if (options.severity !== "high") {
10057
- const entropyHit = this.detectEntropy(line, lineNum, relPath);
10076
+ const allowPublic = options.publicAllowlist !== false;
10077
+ const entropyHit = this.detectEntropy(line, lineNum, relPath, allowPublic);
10058
10078
  if (entropyHit && !shouldIgnoreMatch(entropyHit, ignoreRules)) {
10059
10079
  matches.push(entropyHit);
10060
10080
  }
@@ -10095,13 +10115,14 @@ var ScanRunner = class {
10095
10115
  return false;
10096
10116
  }
10097
10117
  }
10098
- detectEntropy(line, lineNum, filePath) {
10118
+ detectEntropy(line, lineNum, filePath, allowPublic) {
10099
10119
  const valuePattern = /(?:=|:\s*)["']?([A-Za-z0-9+/=_-]{20,})["']?/;
10100
10120
  const match = valuePattern.exec(line);
10101
10121
  if (!match) return null;
10102
10122
  const value = match[1];
10103
10123
  const entropy = shannonEntropy(value);
10104
10124
  if (!isHighEntropy(value)) return null;
10125
+ if (allowPublic && matchPublicPrefix(value)) return null;
10105
10126
  const varMatch = /(\w+)\s*(?:=|:)/.exec(line);
10106
10127
  const varName = varMatch ? varMatch[1] : "";
10107
10128
  const preview = varName ? `${varName}=\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022` : redactValue(value);
@@ -10183,41 +10204,50 @@ function metadataPath(encryptedFilePath) {
10183
10204
  return path4.join(dir, `${base}.clef-meta.yaml`);
10184
10205
  }
10185
10206
  var HEADER_COMMENT = "# Managed by Clef. Do not edit manually.\n";
10207
+ function emptyMetadata() {
10208
+ return { version: 1, pending: [], rotations: [] };
10209
+ }
10186
10210
  async function loadMetadata(filePath) {
10187
10211
  const metaPath = metadataPath(filePath);
10188
10212
  try {
10189
- if (!fs5.existsSync(metaPath)) {
10190
- return { version: 1, pending: [] };
10191
- }
10213
+ if (!fs5.existsSync(metaPath)) return emptyMetadata();
10192
10214
  const content = fs5.readFileSync(metaPath, "utf-8");
10193
10215
  const parsed = YAML3.parse(content);
10194
- if (!parsed || !Array.isArray(parsed.pending)) {
10195
- return { version: 1, pending: [] };
10196
- }
10197
- return {
10198
- version: 1,
10199
- pending: parsed.pending.map((p) => ({
10200
- key: p.key,
10201
- since: new Date(p.since),
10202
- setBy: p.setBy
10203
- }))
10204
- };
10216
+ if (!parsed || typeof parsed !== "object") return emptyMetadata();
10217
+ const pendingRaw = Array.isArray(parsed.pending) ? parsed.pending : [];
10218
+ const pending = pendingRaw.filter(
10219
+ (p) => !!p && typeof p === "object" && typeof p.key === "string" && typeof p.since === "string" && typeof p.setBy === "string"
10220
+ ).map((p) => ({ key: p.key, since: new Date(p.since), setBy: p.setBy }));
10221
+ const rotationsRaw = Array.isArray(parsed.rotations) ? parsed.rotations : [];
10222
+ const rotations = rotationsRaw.filter(
10223
+ (r) => !!r && typeof r === "object" && typeof r.key === "string" && typeof r.last_rotated_at === "string" && typeof r.rotated_by === "string" && typeof r.rotation_count === "number"
10224
+ ).map((r) => ({
10225
+ key: r.key,
10226
+ lastRotatedAt: new Date(r.last_rotated_at),
10227
+ rotatedBy: r.rotated_by,
10228
+ rotationCount: r.rotation_count
10229
+ }));
10230
+ return { version: 1, pending, rotations };
10205
10231
  } catch {
10206
- return { version: 1, pending: [] };
10232
+ return emptyMetadata();
10207
10233
  }
10208
10234
  }
10209
10235
  async function saveMetadata(filePath, metadata) {
10210
10236
  const metaPath = metadataPath(filePath);
10211
10237
  const dir = path4.dirname(metaPath);
10212
- if (!fs5.existsSync(dir)) {
10213
- fs5.mkdirSync(dir, { recursive: true });
10214
- }
10238
+ if (!fs5.existsSync(dir)) fs5.mkdirSync(dir, { recursive: true });
10215
10239
  const data = {
10216
10240
  version: metadata.version,
10217
10241
  pending: metadata.pending.map((p) => ({
10218
10242
  key: p.key,
10219
10243
  since: p.since.toISOString(),
10220
10244
  setBy: p.setBy
10245
+ })),
10246
+ rotations: metadata.rotations.map((r) => ({
10247
+ key: r.key,
10248
+ last_rotated_at: r.lastRotatedAt.toISOString(),
10249
+ rotated_by: r.rotatedBy,
10250
+ rotation_count: r.rotationCount
10221
10251
  }))
10222
10252
  };
10223
10253
  fs5.writeFileSync(metaPath, HEADER_COMMENT + YAML3.stringify(data), "utf-8");
@@ -10248,6 +10278,38 @@ async function isPending(filePath, key) {
10248
10278
  const metadata = await loadMetadata(filePath);
10249
10279
  return metadata.pending.some((p) => p.key === key);
10250
10280
  }
10281
+ async function recordRotation(filePath, keys, rotatedBy, now = /* @__PURE__ */ new Date()) {
10282
+ const metadata = await loadMetadata(filePath);
10283
+ for (const key of keys) {
10284
+ const existing = metadata.rotations.findIndex((r) => r.key === key);
10285
+ if (existing >= 0) {
10286
+ metadata.rotations[existing] = {
10287
+ key,
10288
+ lastRotatedAt: now,
10289
+ rotatedBy,
10290
+ rotationCount: metadata.rotations[existing].rotationCount + 1
10291
+ };
10292
+ } else {
10293
+ metadata.rotations.push({
10294
+ key,
10295
+ lastRotatedAt: now,
10296
+ rotatedBy,
10297
+ rotationCount: 1
10298
+ });
10299
+ }
10300
+ }
10301
+ metadata.pending = metadata.pending.filter((p) => !keys.includes(p.key));
10302
+ await saveMetadata(filePath, metadata);
10303
+ }
10304
+ async function removeRotation(filePath, keys) {
10305
+ const metadata = await loadMetadata(filePath);
10306
+ metadata.rotations = metadata.rotations.filter((r) => !keys.includes(r.key));
10307
+ await saveMetadata(filePath, metadata);
10308
+ }
10309
+ async function getRotations(filePath) {
10310
+ const metadata = await loadMetadata(filePath);
10311
+ return metadata.rotations;
10312
+ }
10251
10313
  function generateRandomValue() {
10252
10314
  return crypto2.randomBytes(32).toString("hex");
10253
10315
  }
@@ -11049,61 +11111,83 @@ var GitIntegration = class {
11049
11111
  * @throws {@link GitOperationError} On failure.
11050
11112
  */
11051
11113
  async installMergeDriver(repoRoot) {
11052
- const configResult = await this.runner.run(
11053
- "git",
11054
- ["config", "merge.sops.name", "SOPS-aware merge driver"],
11055
- { cwd: repoRoot }
11056
- );
11057
- if (configResult.exitCode !== 0) {
11058
- throw new GitOperationError(
11059
- `Failed to configure merge driver name: ${configResult.stderr.trim()}`,
11060
- "Ensure you are inside a git repository."
11114
+ const drivers = [
11115
+ { config: "merge.sops", friendly: "SOPS-aware merge driver" },
11116
+ { config: "merge.clef-metadata", friendly: "Clef metadata merge driver" }
11117
+ ];
11118
+ for (const driver of drivers) {
11119
+ const nameResult = await this.runner.run(
11120
+ "git",
11121
+ ["config", `${driver.config}.name`, driver.friendly],
11122
+ { cwd: repoRoot }
11061
11123
  );
11062
- }
11063
- const driverResult = await this.runner.run(
11064
- "git",
11065
- ["config", "merge.sops.driver", "clef merge-driver %O %A %B"],
11066
- { cwd: repoRoot }
11067
- );
11068
- if (driverResult.exitCode !== 0) {
11069
- throw new GitOperationError(
11070
- `Failed to configure merge driver command: ${driverResult.stderr.trim()}`,
11071
- "Ensure you are inside a git repository."
11124
+ if (nameResult.exitCode !== 0) {
11125
+ throw new GitOperationError(
11126
+ `Failed to configure merge driver name: ${nameResult.stderr.trim()}`,
11127
+ "Ensure you are inside a git repository."
11128
+ );
11129
+ }
11130
+ const driverResult = await this.runner.run(
11131
+ "git",
11132
+ ["config", `${driver.config}.driver`, "clef merge-driver %O %A %B"],
11133
+ { cwd: repoRoot }
11072
11134
  );
11135
+ if (driverResult.exitCode !== 0) {
11136
+ throw new GitOperationError(
11137
+ `Failed to configure merge driver command: ${driverResult.stderr.trim()}`,
11138
+ "Ensure you are inside a git repository."
11139
+ );
11140
+ }
11073
11141
  }
11074
11142
  await this.ensureGitattributes(repoRoot);
11075
11143
  }
11076
11144
  /**
11077
- * Check whether the SOPS merge driver is configured in both
11078
- * `.git/config` and `.gitattributes`.
11079
- *
11080
- * @param repoRoot - Absolute path to the repository root.
11081
- * @returns An object indicating which parts are configured.
11145
+ * Check whether both Clef merge drivers are configured in `.git/config`
11146
+ * and `.gitattributes`. Reports separately on the SOPS driver
11147
+ * (`merge=sops` for `.enc.*`) and the metadata driver
11148
+ * (`merge=clef-metadata` for `.clef-meta.yaml`) so `clef doctor` can
11149
+ * prompt the user to run `clef hooks` when only the SOPS driver is
11150
+ * installed (older install, pre-metadata-merge).
11082
11151
  */
11083
11152
  async checkMergeDriver(repoRoot) {
11084
- const configResult = await this.runner.run("git", ["config", "--get", "merge.sops.driver"], {
11153
+ const sopsConfig = await this.runner.run("git", ["config", "--get", "merge.sops.driver"], {
11085
11154
  cwd: repoRoot
11086
11155
  });
11087
- const gitConfig = configResult.exitCode === 0 && configResult.stdout.trim().length > 0;
11156
+ const gitConfig = sopsConfig.exitCode === 0 && sopsConfig.stdout.trim().length > 0;
11157
+ const metaConfig = await this.runner.run(
11158
+ "git",
11159
+ ["config", "--get", "merge.clef-metadata.driver"],
11160
+ { cwd: repoRoot }
11161
+ );
11162
+ const metadataGitConfig = metaConfig.exitCode === 0 && metaConfig.stdout.trim().length > 0;
11088
11163
  const attrFilePath = path8.join(repoRoot, ".gitattributes");
11089
11164
  const attrContent = fs9.existsSync(attrFilePath) ? fs9.readFileSync(attrFilePath, "utf-8") : "";
11090
11165
  const gitattributes = attrContent.includes("merge=sops");
11091
- return { gitConfig, gitattributes };
11166
+ const metadataGitattributes = attrContent.includes("merge=clef-metadata");
11167
+ return { gitConfig, gitattributes, metadataGitConfig, metadataGitattributes };
11092
11168
  }
11093
11169
  async ensureGitattributes(repoRoot) {
11094
11170
  const attrPath = path8.join(repoRoot, ".gitattributes");
11095
- const mergeRule = "*.enc.yaml merge=sops\n*.enc.json merge=sops";
11096
11171
  const existing = fs9.existsSync(attrPath) ? fs9.readFileSync(attrPath, "utf-8") : "";
11097
- if (existing.includes("merge=sops")) {
11098
- return;
11099
- }
11100
- const newContent = existing.trimEnd() ? `${existing.trimEnd()}
11172
+ let newContent = existing;
11173
+ if (!existing.includes("merge=sops")) {
11174
+ const block = `# Clef: SOPS-aware merge driver for encrypted files
11175
+ *.enc.yaml merge=sops
11176
+ *.enc.json merge=sops
11177
+ `;
11178
+ newContent = newContent.trimEnd() ? `${newContent.trimEnd()}
11101
11179
 
11102
- # Clef: SOPS-aware merge driver for encrypted files
11103
- ${mergeRule}
11104
- ` : `# Clef: SOPS-aware merge driver for encrypted files
11105
- ${mergeRule}
11180
+ ${block}` : block;
11181
+ }
11182
+ if (!newContent.includes("merge=clef-metadata")) {
11183
+ const block = `# Clef: rotation-aware merge driver for metadata sidecars
11184
+ *.clef-meta.yaml merge=clef-metadata
11106
11185
  `;
11186
+ newContent = newContent.trimEnd() ? `${newContent.trimEnd()}
11187
+
11188
+ ${block}` : block;
11189
+ }
11190
+ if (newContent === existing) return;
11107
11191
  try {
11108
11192
  fs9.writeFileSync(attrPath, newContent, "utf-8");
11109
11193
  } catch (err) {
@@ -12100,8 +12184,50 @@ var LintRunner = class {
12100
12184
  );
12101
12185
  issues.push(...siIssues);
12102
12186
  }
12187
+ const metadataIssues = await this.lintMetadataConsistency(existingCells);
12188
+ issues.push(...metadataIssues);
12103
12189
  return { issues, fileCount: fileCount + missingCells.length, pendingCount };
12104
12190
  }
12191
+ /**
12192
+ * Cross-reference `.clef-meta.yaml` against the cipher's plaintext key
12193
+ * names for each existing cell. Reports orphan rotation records and
12194
+ * dual-state (pending + rotation) inconsistencies. Uses
12195
+ * {@link readSopsKeyNames} (plaintext YAML parse) — no decryption.
12196
+ */
12197
+ async lintMetadataConsistency(cells) {
12198
+ const issues = [];
12199
+ for (const cell of cells) {
12200
+ const keysInCipher = readSopsKeyNames(cell.filePath);
12201
+ if (keysInCipher === null) continue;
12202
+ const cipherKeys = new Set(keysInCipher);
12203
+ const metadata = await loadMetadata(cell.filePath);
12204
+ for (const record of metadata.rotations) {
12205
+ if (!cipherKeys.has(record.key)) {
12206
+ issues.push({
12207
+ severity: "warning",
12208
+ category: "metadata",
12209
+ file: cell.filePath,
12210
+ key: record.key,
12211
+ message: `Rotation record exists for key '${record.key}' but the key is not in this cell. Remove the orphan entry from .clef-meta.yaml or re-add the key via clef set.`
12212
+ });
12213
+ }
12214
+ }
12215
+ const pendingKeys = new Set(metadata.pending.map((p) => p.key));
12216
+ for (const record of metadata.rotations) {
12217
+ if (pendingKeys.has(record.key)) {
12218
+ issues.push({
12219
+ severity: "error",
12220
+ category: "metadata",
12221
+ file: cell.filePath,
12222
+ key: record.key,
12223
+ message: `Key '${record.key}' appears in both 'pending' and 'rotations' sections of .clef-meta.yaml. One of them is stale \u2014 likely a manual edit or a failed transaction. Re-run clef set to reconcile.`,
12224
+ fixCommand: `clef set ${cell.namespace}/${cell.environment} ${record.key}`
12225
+ });
12226
+ }
12227
+ }
12228
+ }
12229
+ return issues;
12230
+ }
12105
12231
  /**
12106
12232
  * Lint service identity configurations for drift issues.
12107
12233
  */
@@ -12454,22 +12580,33 @@ var ImportRunner = class {
12454
12580
  }
12455
12581
  const decrypted = await this.sopsClient.decrypt(filePath);
12456
12582
  const newValues = { ...decrypted.values };
12583
+ const rotatedKeys = [];
12457
12584
  for (const [key, value] of candidates) {
12458
- if (key in decrypted.values && !options.overwrite) {
12585
+ const existed = key in decrypted.values;
12586
+ if (existed && !options.overwrite) {
12459
12587
  skipped.push(key);
12460
12588
  continue;
12461
12589
  }
12590
+ const valueChanged = !existed || decrypted.values[key] !== value;
12462
12591
  newValues[key] = value;
12463
12592
  imported.push(key);
12593
+ if (valueChanged) rotatedKeys.push(key);
12464
12594
  }
12465
12595
  if (imported.length === 0) {
12466
12596
  return { imported, skipped, failed, warnings, dryRun: false };
12467
12597
  }
12598
+ const relCellPath = path14.relative(repoRoot, filePath);
12599
+ const relMetaPath = relCellPath.replace(/\.enc\.(yaml|json)$/, ".clef-meta.yaml");
12468
12600
  await this.tx.run(repoRoot, {
12469
12601
  description: `clef import ${target}: ${imported.length} key(s)`,
12470
- paths: [path14.relative(repoRoot, filePath)],
12602
+ // Include the metadata path so rotation records created in the mutate
12603
+ // callback are staged and rolled back atomically with the ciphertext.
12604
+ paths: [relCellPath, relMetaPath],
12471
12605
  mutate: async () => {
12472
12606
  await this.sopsClient.encrypt(filePath, newValues, manifest, env);
12607
+ if (options.rotatedBy && rotatedKeys.length > 0) {
12608
+ await recordRotation(filePath, rotatedKeys, options.rotatedBy);
12609
+ }
12473
12610
  }
12474
12611
  });
12475
12612
  return { imported, skipped, failed, warnings, dryRun: false };
@@ -13482,6 +13619,116 @@ var SopsMergeDriver = class {
13482
13619
  }
13483
13620
  };
13484
13621
 
13622
+ // src/merge/metadata-driver.ts
13623
+ var fs15 = __toESM(require("fs"));
13624
+ var YAML10 = __toESM(require("yaml"));
13625
+ var HEADER_COMMENT3 = "# Managed by Clef. Do not edit manually.\n";
13626
+ function parseMetadata(content) {
13627
+ try {
13628
+ const parsed = YAML10.parse(content);
13629
+ if (!parsed || typeof parsed !== "object") return emptyMetadata2();
13630
+ const pendingRaw = Array.isArray(parsed.pending) ? parsed.pending : [];
13631
+ const pending = pendingRaw.filter(
13632
+ (p) => !!p && typeof p === "object" && typeof p.key === "string" && typeof p.since === "string" && typeof p.setBy === "string"
13633
+ ).map((p) => ({ key: p.key, since: new Date(p.since), setBy: p.setBy }));
13634
+ const rotationsRaw = Array.isArray(parsed.rotations) ? parsed.rotations : [];
13635
+ const rotations = rotationsRaw.filter(
13636
+ (r) => !!r && typeof r === "object" && typeof r.key === "string" && typeof r.last_rotated_at === "string" && typeof r.rotated_by === "string" && typeof r.rotation_count === "number"
13637
+ ).map((r) => ({
13638
+ key: r.key,
13639
+ lastRotatedAt: new Date(r.last_rotated_at),
13640
+ rotatedBy: r.rotated_by,
13641
+ rotationCount: r.rotation_count
13642
+ }));
13643
+ return { version: 1, pending, rotations };
13644
+ } catch {
13645
+ return emptyMetadata2();
13646
+ }
13647
+ }
13648
+ function emptyMetadata2() {
13649
+ return { version: 1, pending: [], rotations: [] };
13650
+ }
13651
+ function serializeMetadata(m) {
13652
+ const data = {
13653
+ version: m.version,
13654
+ pending: m.pending.map((p) => ({
13655
+ key: p.key,
13656
+ since: p.since.toISOString(),
13657
+ setBy: p.setBy
13658
+ })),
13659
+ rotations: m.rotations.map((r) => ({
13660
+ key: r.key,
13661
+ last_rotated_at: r.lastRotatedAt.toISOString(),
13662
+ rotated_by: r.rotatedBy,
13663
+ rotation_count: r.rotationCount
13664
+ }))
13665
+ };
13666
+ return HEADER_COMMENT3 + YAML10.stringify(data);
13667
+ }
13668
+ function mergeRotations(ours, theirs) {
13669
+ const byKey = /* @__PURE__ */ new Map();
13670
+ const ourByKey = new Map(ours.map((r) => [r.key, r]));
13671
+ const theirByKey = new Map(theirs.map((r) => [r.key, r]));
13672
+ const allKeys = /* @__PURE__ */ new Set([...ourByKey.keys(), ...theirByKey.keys()]);
13673
+ for (const key of allKeys) {
13674
+ const o = ourByKey.get(key);
13675
+ const t = theirByKey.get(key);
13676
+ if (o && t) {
13677
+ const oTime = o.lastRotatedAt.getTime();
13678
+ const tTime = t.lastRotatedAt.getTime();
13679
+ const winner = tTime > oTime ? t : o;
13680
+ byKey.set(key, {
13681
+ key,
13682
+ lastRotatedAt: winner.lastRotatedAt,
13683
+ rotatedBy: winner.rotatedBy,
13684
+ rotationCount: Math.max(o.rotationCount, t.rotationCount) + 1
13685
+ });
13686
+ } else if (o) {
13687
+ byKey.set(key, o);
13688
+ } else if (t) {
13689
+ byKey.set(key, t);
13690
+ }
13691
+ }
13692
+ return Array.from(byKey.values());
13693
+ }
13694
+ function mergePending(oursPending, theirsPending, oursRotations, theirsRotations) {
13695
+ const ourByKey = new Map(oursPending.map((p) => [p.key, p]));
13696
+ const theirByKey = new Map(theirsPending.map((p) => [p.key, p]));
13697
+ const rotatedKeys = /* @__PURE__ */ new Set([
13698
+ ...oursRotations.map((r) => r.key),
13699
+ ...theirsRotations.map((r) => r.key)
13700
+ ]);
13701
+ const allKeys = /* @__PURE__ */ new Set([...ourByKey.keys(), ...theirByKey.keys()]);
13702
+ const out = [];
13703
+ for (const key of allKeys) {
13704
+ if (rotatedKeys.has(key)) continue;
13705
+ const o = ourByKey.get(key);
13706
+ const t = theirByKey.get(key);
13707
+ if (o && t) {
13708
+ const winner = t.since.getTime() > o.since.getTime() ? t : o;
13709
+ out.push({ key, since: winner.since, setBy: winner.setBy });
13710
+ } else if (o) {
13711
+ out.push(o);
13712
+ } else if (t) {
13713
+ out.push(t);
13714
+ }
13715
+ }
13716
+ return out;
13717
+ }
13718
+ function mergeMetadataContents(oursContent, theirsContent) {
13719
+ const ours = parseMetadata(oursContent);
13720
+ const theirs = parseMetadata(theirsContent);
13721
+ const rotations = mergeRotations(ours.rotations, theirs.rotations);
13722
+ const pending = mergePending(ours.pending, theirs.pending, ours.rotations, theirs.rotations);
13723
+ return serializeMetadata({ version: 1, pending, rotations });
13724
+ }
13725
+ function mergeMetadataFiles(_basePath, oursPath, theirsPath) {
13726
+ const oursContent = fs15.existsSync(oursPath) ? fs15.readFileSync(oursPath, "utf-8") : "";
13727
+ const theirsContent = fs15.existsSync(theirsPath) ? fs15.readFileSync(theirsPath, "utf-8") : "";
13728
+ const merged = mergeMetadataContents(oursContent, theirsContent);
13729
+ fs15.writeFileSync(oursPath, merged, "utf-8");
13730
+ }
13731
+
13485
13732
  // src/service-identity/manager.ts
13486
13733
  var path19 = __toESM(require("path"));
13487
13734
  var ServiceIdentityManager = class {
@@ -14017,7 +14264,7 @@ var ServiceIdentityManager = class {
14017
14264
  };
14018
14265
 
14019
14266
  // src/structure/manager.ts
14020
- var fs15 = __toESM(require("fs"));
14267
+ var fs16 = __toESM(require("fs"));
14021
14268
  var path20 = __toESM(require("path"));
14022
14269
  var StructureManager = class {
14023
14270
  constructor(matrixManager, encryption, tx) {
@@ -14045,7 +14292,7 @@ var StructureManager = class {
14045
14292
  )
14046
14293
  }));
14047
14294
  for (const cell of newCellPaths) {
14048
- if (fs15.existsSync(cell.filePath)) {
14295
+ if (fs16.existsSync(cell.filePath)) {
14049
14296
  throw new Error(
14050
14297
  `Cannot add namespace '${name}': file '${path20.relative(repoRoot, cell.filePath)}' already exists.`
14051
14298
  );
@@ -14117,7 +14364,7 @@ var StructureManager = class {
14117
14364
  )
14118
14365
  }));
14119
14366
  for (const cell of newCellPaths) {
14120
- if (fs15.existsSync(cell.filePath)) {
14367
+ if (fs16.existsSync(cell.filePath)) {
14121
14368
  throw new Error(
14122
14369
  `Cannot add environment '${name}': file '${path20.relative(repoRoot, cell.filePath)}' already exists.`
14123
14370
  );
@@ -14201,7 +14448,7 @@ var StructureManager = class {
14201
14448
  paths: this.deletePaths(repoRoot, cellsToDelete),
14202
14449
  mutate: async () => {
14203
14450
  for (const cell of cellsToDelete) {
14204
- fs15.unlinkSync(cell.filePath);
14451
+ fs16.unlinkSync(cell.filePath);
14205
14452
  this.unlinkMetaSibling(cell.filePath);
14206
14453
  }
14207
14454
  const doc = readManifestYaml(repoRoot);
@@ -14251,7 +14498,7 @@ var StructureManager = class {
14251
14498
  paths: this.deletePaths(repoRoot, cellsToDelete),
14252
14499
  mutate: async () => {
14253
14500
  for (const cell of cellsToDelete) {
14254
- fs15.unlinkSync(cell.filePath);
14501
+ fs16.unlinkSync(cell.filePath);
14255
14502
  this.unlinkMetaSibling(cell.filePath);
14256
14503
  }
14257
14504
  const doc = readManifestYaml(repoRoot);
@@ -14293,7 +14540,7 @@ var StructureManager = class {
14293
14540
  const renamePairs = isRename ? this.collectRenamePairs(manifest, repoRoot, name, opts.rename, "namespace") : [];
14294
14541
  if (isRename) {
14295
14542
  for (const pair of renamePairs) {
14296
- if (fs15.existsSync(pair.to)) {
14543
+ if (fs16.existsSync(pair.to)) {
14297
14544
  throw new Error(
14298
14545
  `Rename target '${path20.relative(repoRoot, pair.to)}' already exists. Move or remove it first.`
14299
14546
  );
@@ -14336,7 +14583,7 @@ var StructureManager = class {
14336
14583
  const renamePairs = isRename ? this.collectRenamePairs(manifest, repoRoot, name, opts.rename, "environment") : [];
14337
14584
  if (isRename) {
14338
14585
  for (const pair of renamePairs) {
14339
- if (fs15.existsSync(pair.to)) {
14586
+ if (fs16.existsSync(pair.to)) {
14340
14587
  throw new Error(
14341
14588
  `Rename target '${path20.relative(repoRoot, pair.to)}' already exists. Move or remove it first.`
14342
14589
  );
@@ -14373,7 +14620,7 @@ var StructureManager = class {
14373
14620
  const newCellPath = this.swapAxisInCellPath(repoRoot, manifest, cell, axis, newName);
14374
14621
  pairs.push({ from: cell.filePath, to: newCellPath });
14375
14622
  const oldMeta = cell.filePath.replace(/\.enc\.(yaml|json)$/, ".clef-meta.yaml");
14376
- if (fs15.existsSync(oldMeta)) {
14623
+ if (fs16.existsSync(oldMeta)) {
14377
14624
  const newMeta = newCellPath.replace(/\.enc\.(yaml|json)$/, ".clef-meta.yaml");
14378
14625
  pairs.push({ from: oldMeta, to: newMeta });
14379
14626
  }
@@ -14413,10 +14660,10 @@ var StructureManager = class {
14413
14660
  applyRenames(pairs) {
14414
14661
  for (const pair of pairs) {
14415
14662
  const targetDir = path20.dirname(pair.to);
14416
- if (!fs15.existsSync(targetDir)) {
14417
- fs15.mkdirSync(targetDir, { recursive: true });
14663
+ if (!fs16.existsSync(targetDir)) {
14664
+ fs16.mkdirSync(targetDir, { recursive: true });
14418
14665
  }
14419
- fs15.renameSync(pair.from, pair.to);
14666
+ fs16.renameSync(pair.from, pair.to);
14420
14667
  }
14421
14668
  }
14422
14669
  /**
@@ -14429,7 +14676,7 @@ var StructureManager = class {
14429
14676
  for (const cell of cells) {
14430
14677
  paths.add(path20.relative(repoRoot, cell.filePath));
14431
14678
  const meta = cell.filePath.replace(/\.enc\.(yaml|json)$/, ".clef-meta.yaml");
14432
- if (fs15.existsSync(meta)) {
14679
+ if (fs16.existsSync(meta)) {
14433
14680
  paths.add(path20.relative(repoRoot, meta));
14434
14681
  }
14435
14682
  }
@@ -14443,8 +14690,8 @@ var StructureManager = class {
14443
14690
  */
14444
14691
  unlinkMetaSibling(cellPath) {
14445
14692
  const meta = cellPath.replace(/\.enc\.(yaml|json)$/, ".clef-meta.yaml");
14446
- if (fs15.existsSync(meta)) {
14447
- fs15.unlinkSync(meta);
14693
+ if (fs16.existsSync(meta)) {
14694
+ fs16.unlinkSync(meta);
14448
14695
  }
14449
14696
  }
14450
14697
  /**
@@ -14589,7 +14836,7 @@ async function resolveIdentitySecrets(identityName, environment, manifest, repoR
14589
14836
  var crypto4 = __toESM(require("crypto"));
14590
14837
 
14591
14838
  // src/artifact/output.ts
14592
- var fs16 = __toESM(require("fs"));
14839
+ var fs17 = __toESM(require("fs"));
14593
14840
  var path21 = __toESM(require("path"));
14594
14841
  var FilePackOutput = class {
14595
14842
  constructor(outputPath) {
@@ -14597,12 +14844,12 @@ var FilePackOutput = class {
14597
14844
  }
14598
14845
  async write(_artifact, json) {
14599
14846
  const outputDir = path21.dirname(this.outputPath);
14600
- if (!fs16.existsSync(outputDir)) {
14601
- fs16.mkdirSync(outputDir, { recursive: true });
14847
+ if (!fs17.existsSync(outputDir)) {
14848
+ fs17.mkdirSync(outputDir, { recursive: true });
14602
14849
  }
14603
14850
  const tmpOutput = `${this.outputPath}.tmp.${process.pid}`;
14604
- fs16.writeFileSync(tmpOutput, json, "utf-8");
14605
- fs16.renameSync(tmpOutput, this.outputPath);
14851
+ fs17.writeFileSync(tmpOutput, json, "utf-8");
14852
+ fs17.renameSync(tmpOutput, this.outputPath);
14606
14853
  }
14607
14854
  };
14608
14855
  var MemoryPackOutput = class {
@@ -14824,7 +15071,7 @@ var VALID_KMS_PROVIDERS = ["aws", "gcp", "azure"];
14824
15071
 
14825
15072
  // src/migration/backend.ts
14826
15073
  var path22 = __toESM(require("path"));
14827
- var YAML10 = __toESM(require("yaml"));
15074
+ var YAML11 = __toESM(require("yaml"));
14828
15075
  var BACKEND_KEY_FIELDS = {
14829
15076
  age: void 0,
14830
15077
  awskms: "aws_kms_arn",
@@ -14947,7 +15194,7 @@ var BackendMigrator = class {
14947
15194
  const doc = readManifestYaml(repoRoot);
14948
15195
  this.updateManifestDoc(doc, target, environment);
14949
15196
  writeManifestYaml(repoRoot, doc);
14950
- const updatedManifest = YAML10.parse(YAML10.stringify(doc));
15197
+ const updatedManifest = YAML11.parse(YAML11.stringify(doc));
14951
15198
  for (const cell of toMigrate) {
14952
15199
  onProgress?.({
14953
15200
  type: "migrate",
@@ -15342,8 +15589,8 @@ var SyncManager = class {
15342
15589
  };
15343
15590
 
15344
15591
  // src/policy/parser.ts
15345
- var fs17 = __toESM(require("fs"));
15346
- var YAML11 = __toESM(require("yaml"));
15592
+ var fs18 = __toESM(require("fs"));
15593
+ var YAML12 = __toESM(require("yaml"));
15347
15594
 
15348
15595
  // src/policy/types.ts
15349
15596
  var DEFAULT_POLICY = Object.freeze({
@@ -15364,7 +15611,7 @@ var PolicyParser = class {
15364
15611
  parse(filePath) {
15365
15612
  let raw;
15366
15613
  try {
15367
- raw = fs17.readFileSync(filePath, "utf-8");
15614
+ raw = fs18.readFileSync(filePath, "utf-8");
15368
15615
  } catch {
15369
15616
  throw new PolicyValidationError(`Could not read policy file at '${filePath}'.`);
15370
15617
  }
@@ -15379,7 +15626,7 @@ var PolicyParser = class {
15379
15626
  parseContent(content) {
15380
15627
  let parsed;
15381
15628
  try {
15382
- parsed = YAML11.parse(content);
15629
+ parsed = YAML12.parse(content);
15383
15630
  } catch {
15384
15631
  throw new PolicyValidationError(
15385
15632
  "Policy file contains invalid YAML. Check for syntax errors."
@@ -15392,7 +15639,7 @@ var PolicyParser = class {
15392
15639
  * not exist. Any other read or validation error throws.
15393
15640
  */
15394
15641
  load(filePath) {
15395
- if (!fs17.existsSync(filePath)) return DEFAULT_POLICY;
15642
+ if (!fs18.existsSync(filePath)) return DEFAULT_POLICY;
15396
15643
  return this.parse(filePath);
15397
15644
  }
15398
15645
  validate(raw) {
@@ -15460,31 +15707,64 @@ var PolicyEvaluator = class {
15460
15707
  this.policy = policy;
15461
15708
  }
15462
15709
  /**
15463
- * Evaluate a single encrypted file's rotation state.
15710
+ * Evaluate a single encrypted file's per-key rotation state.
15464
15711
  *
15465
15712
  * @param filePath Repo-relative or absolute path to the encrypted file.
15466
- * @param environment Environment name from the matrix; selects per-env
15467
- * overrides if present in the policy.
15468
- * @param metadata Result of `SopsClient.getMetadata()` for the file.
15469
- * @param now Reference time (defaults to `new Date()`). Inject for
15470
- * deterministic tests and reproducible audits.
15713
+ * @param environment Environment name; selects per-env overrides.
15714
+ * @param metadata SOPS metadata for the file (carries last_modified,
15715
+ * backend, recipients). The evaluator does not read
15716
+ * `last_modified` for the policy gate it is echoed
15717
+ * into the output for audit consumers only.
15718
+ * @param keys Plaintext key names present in the cipher, enumerated
15719
+ * from the unencrypted YAML top-level keys (no decrypt
15720
+ * required since SOPS stores key names in plaintext).
15721
+ * @param rotations Rotation records from `.clef-meta.yaml`. Records for
15722
+ * keys not in `keys` are ignored (those are orphans;
15723
+ * lint surfaces them as a warning).
15724
+ * @param now Reference time. Inject for deterministic tests.
15471
15725
  */
15472
- evaluateFile(filePath, environment, metadata, now = /* @__PURE__ */ new Date()) {
15726
+ evaluateFile(filePath, environment, metadata, keys, rotations, now = /* @__PURE__ */ new Date()) {
15473
15727
  const maxAgeDays = this.resolveMaxAgeDays(environment);
15474
- const rotationDue = new Date(metadata.lastModified.getTime() + maxAgeDays * MS_PER_DAY);
15475
- const rotationOverdue = now.getTime() > rotationDue.getTime();
15476
- const daysOverdue = rotationOverdue ? Math.floor((now.getTime() - rotationDue.getTime()) / MS_PER_DAY) : 0;
15728
+ const byKey = new Map(rotations.map((r) => [r.key, r]));
15729
+ const keyStatuses = keys.map(
15730
+ (key) => this.evaluateKey(key, byKey.get(key), maxAgeDays, now)
15731
+ );
15477
15732
  return {
15478
15733
  path: filePath,
15479
15734
  environment,
15480
15735
  backend: metadata.backend,
15481
15736
  recipients: metadata.recipients,
15482
15737
  last_modified: metadata.lastModified.toISOString(),
15483
- // Treat a missing `lastModifiedPresent` as `true` — the field is
15484
- // optional on SopsMetadata and only `parseMetadataFromFile` knows
15485
- // authoritatively whether the underlying file carried `sops.lastmodified`.
15486
- // Hand-constructed metadata is assumed trustworthy.
15487
15738
  last_modified_known: metadata.lastModifiedPresent !== false,
15739
+ keys: keyStatuses,
15740
+ // Cell-level compliance is the AND of per-key verdicts. An empty
15741
+ // `keys` array (cell with no secrets) is vacuously compliant.
15742
+ compliant: keyStatuses.every((k) => k.compliant)
15743
+ };
15744
+ }
15745
+ evaluateKey(key, record, maxAgeDays, now) {
15746
+ if (!record) {
15747
+ return {
15748
+ key,
15749
+ last_rotated_at: null,
15750
+ last_rotated_known: false,
15751
+ rotated_by: null,
15752
+ rotation_count: 0,
15753
+ rotation_due: null,
15754
+ rotation_overdue: false,
15755
+ days_overdue: 0,
15756
+ compliant: false
15757
+ };
15758
+ }
15759
+ const rotationDue = new Date(record.lastRotatedAt.getTime() + maxAgeDays * MS_PER_DAY);
15760
+ const rotationOverdue = now.getTime() > rotationDue.getTime();
15761
+ const daysOverdue = rotationOverdue ? Math.floor((now.getTime() - rotationDue.getTime()) / MS_PER_DAY) : 0;
15762
+ return {
15763
+ key,
15764
+ last_rotated_at: record.lastRotatedAt.toISOString(),
15765
+ last_rotated_known: true,
15766
+ rotated_by: record.rotatedBy,
15767
+ rotation_count: record.rotationCount,
15488
15768
  rotation_due: rotationDue.toISOString(),
15489
15769
  rotation_overdue: rotationOverdue,
15490
15770
  days_overdue: daysOverdue,
@@ -15540,10 +15820,11 @@ var ComplianceGenerator = class {
15540
15820
  return `sha256:${(0, import_crypto2.createHash)("sha256").update(canonicalJson(policy)).digest("hex")}`;
15541
15821
  }
15542
15822
  buildSummary(scan, lint, files) {
15823
+ const rotationOverdue = files.filter((f) => !f.compliant).length;
15543
15824
  return {
15544
15825
  total_files: files.length,
15545
15826
  compliant: files.filter((f) => f.compliant).length,
15546
- rotation_overdue: files.filter((f) => f.rotation_overdue).length,
15827
+ rotation_overdue: rotationOverdue,
15547
15828
  scan_violations: scan.matches.length,
15548
15829
  lint_errors: lint.issues.filter((i) => i.severity === "error").length
15549
15830
  };
@@ -15566,7 +15847,7 @@ async function runCompliance(opts) {
15566
15847
  const now = opts.now ?? /* @__PURE__ */ new Date();
15567
15848
  const manifest = new ManifestParser().parse(manifestPath);
15568
15849
  const policy = opts.policy ?? new PolicyParser().load(policyPath);
15569
- const sopsClient = new SopsClient(opts.runner);
15850
+ const sopsClient = new SopsClient(opts.runner, opts.ageKeyFile, opts.ageKey, opts.sopsPath);
15570
15851
  const matrixManager = new MatrixManager();
15571
15852
  const schemaValidator = new SchemaValidator();
15572
15853
  const [sha, repo, files, scanResult, lintResult] = await Promise.all([
@@ -15584,12 +15865,13 @@ async function runCompliance(opts) {
15584
15865
  include.scan ? new ScanRunner(opts.runner).scan(repoRoot, manifest) : Promise.resolve(emptyScan()),
15585
15866
  include.lint ? new LintRunner(matrixManager, schemaValidator, sopsClient).run(manifest, repoRoot) : Promise.resolve(emptyLint())
15586
15867
  ]);
15868
+ const adjustedLint = downgradeDecryptIssues(lintResult);
15587
15869
  const document = new ComplianceGenerator().generate({
15588
15870
  sha,
15589
15871
  repo,
15590
15872
  policy,
15591
15873
  scanResult,
15592
- lintResult,
15874
+ lintResult: adjustedLint,
15593
15875
  files,
15594
15876
  now
15595
15877
  });
@@ -15603,7 +15885,9 @@ async function evaluateMatrix(args) {
15603
15885
  cells.map(async (cell) => {
15604
15886
  const metadata = await args.sopsClient.getMetadata(cell.filePath);
15605
15887
  const relPath = path25.relative(args.repoRoot, cell.filePath).replace(/\\/g, "/");
15606
- return evaluator.evaluateFile(relPath, cell.environment, metadata, args.now);
15888
+ const keys = readSopsKeyNames(cell.filePath) ?? [];
15889
+ const rotations = await getRotations(cell.filePath);
15890
+ return evaluator.evaluateFile(relPath, cell.environment, metadata, keys, rotations, args.now);
15607
15891
  })
15608
15892
  );
15609
15893
  }
@@ -15624,6 +15908,21 @@ function emptyScan() {
15624
15908
  function emptyLint() {
15625
15909
  return { issues: [], fileCount: 0, pendingCount: 0 };
15626
15910
  }
15911
+ function downgradeDecryptIssues(result) {
15912
+ return {
15913
+ ...result,
15914
+ issues: result.issues.map((issue) => {
15915
+ if (issue.category === "sops" && issue.message.startsWith("Failed to decrypt")) {
15916
+ return {
15917
+ ...issue,
15918
+ severity: "info",
15919
+ message: `File not decryptable in this environment (compliance runs without keys). Original check: ${issue.message}`
15920
+ };
15921
+ }
15922
+ return issue;
15923
+ })
15924
+ };
15925
+ }
15627
15926
  async function detectSha(runner, repoRoot) {
15628
15927
  const env = process.env;
15629
15928
  const fromEnv = env.GITHUB_SHA ?? env.CI_COMMIT_SHA ?? env.BITBUCKET_COMMIT ?? env.CIRCLE_SHA1 ?? env.BUILD_VCS_NUMBER;
@@ -15712,6 +16011,7 @@ async function detectRepo(runner, repoRoot) {
15712
16011
  generateRandomValue,
15713
16012
  generateSigningKeyPair,
15714
16013
  getPendingKeys,
16014
+ getRotations,
15715
16015
  isHighEntropy,
15716
16016
  isKmsEnvelope,
15717
16017
  isPending,
@@ -15723,6 +16023,8 @@ async function detectRepo(runner, repoRoot) {
15723
16023
  markPendingWithRetry,
15724
16024
  markResolved,
15725
16025
  matchPatterns,
16026
+ mergeMetadataContents,
16027
+ mergeMetadataFiles,
15726
16028
  metadataPath,
15727
16029
  parse,
15728
16030
  parseDotenv,
@@ -15730,8 +16032,10 @@ async function detectRepo(runner, repoRoot) {
15730
16032
  parseJson,
15731
16033
  parseYaml,
15732
16034
  readManifestYaml,
16035
+ recordRotation,
15733
16036
  redactValue,
15734
16037
  removeAccessRequest,
16038
+ removeRotation,
15735
16039
  requestsFilePath,
15736
16040
  resetSopsResolution,
15737
16041
  resolveBackendConfig,