@clef-sh/core 0.1.28 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +1 -2
  2. package/dist/artifact/packer.d.ts +4 -3
  3. package/dist/artifact/packer.d.ts.map +1 -1
  4. package/dist/artifact/resolve.d.ts +3 -2
  5. package/dist/artifact/resolve.d.ts.map +1 -1
  6. package/dist/compliance/run.d.ts.map +1 -1
  7. package/dist/diff/engine.d.ts +18 -8
  8. package/dist/diff/engine.d.ts.map +1 -1
  9. package/dist/import/index.d.ts +5 -5
  10. package/dist/import/index.d.ts.map +1 -1
  11. package/dist/index.d.mts +13 -10
  12. package/dist/index.d.ts +13 -10
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1080 -832
  15. package/dist/index.js.map +4 -4
  16. package/dist/index.mjs +1049 -791
  17. package/dist/index.mjs.map +4 -4
  18. package/dist/lint/runner.d.ts +7 -7
  19. package/dist/lint/runner.d.ts.map +1 -1
  20. package/dist/matrix/manager.d.ts +4 -16
  21. package/dist/matrix/manager.d.ts.map +1 -1
  22. package/dist/merge/driver.d.ts +2 -2
  23. package/dist/merge/driver.d.ts.map +1 -1
  24. package/dist/merge/metadata-driver.d.ts +5 -4
  25. package/dist/merge/metadata-driver.d.ts.map +1 -1
  26. package/dist/migration/backend.d.ts +10 -7
  27. package/dist/migration/backend.d.ts.map +1 -1
  28. package/dist/pack/backends/json-envelope.d.ts.map +1 -1
  29. package/dist/pack/types.d.ts +9 -3
  30. package/dist/pack/types.d.ts.map +1 -1
  31. package/dist/pending/metadata.d.ts +1 -3
  32. package/dist/pending/metadata.d.ts.map +1 -1
  33. package/dist/recipients/index.d.ts +4 -3
  34. package/dist/recipients/index.d.ts.map +1 -1
  35. package/dist/report/generator.d.ts +4 -3
  36. package/dist/report/generator.d.ts.map +1 -1
  37. package/dist/reset/manager.d.ts +21 -3
  38. package/dist/reset/manager.d.ts.map +1 -1
  39. package/dist/service-identity/manager.d.ts +6 -3
  40. package/dist/service-identity/manager.d.ts.map +1 -1
  41. package/dist/sops/client.d.ts +80 -55
  42. package/dist/sops/client.d.ts.map +1 -1
  43. package/dist/sops/linux-stdin-fifo.d.ts +31 -0
  44. package/dist/sops/linux-stdin-fifo.d.ts.map +1 -0
  45. package/dist/source/compose.d.ts +10 -0
  46. package/dist/source/compose.d.ts.map +1 -0
  47. package/dist/source/default-bulk.d.ts +12 -0
  48. package/dist/source/default-bulk.d.ts.map +1 -0
  49. package/dist/source/encryption-backend.d.ts +85 -0
  50. package/dist/source/encryption-backend.d.ts.map +1 -0
  51. package/dist/source/errors.d.ts +19 -0
  52. package/dist/source/errors.d.ts.map +1 -0
  53. package/dist/source/filesystem-storage-backend.d.ts +26 -0
  54. package/dist/source/filesystem-storage-backend.d.ts.map +1 -0
  55. package/dist/source/guards.d.ts +14 -0
  56. package/dist/source/guards.d.ts.map +1 -0
  57. package/dist/source/index.d.ts +10 -0
  58. package/dist/source/index.d.ts.map +1 -0
  59. package/dist/source/mock-source.d.ts +89 -0
  60. package/dist/source/mock-source.d.ts.map +1 -0
  61. package/dist/source/storage-backend.d.ts +61 -0
  62. package/dist/source/storage-backend.d.ts.map +1 -0
  63. package/dist/source/types.d.ts +212 -0
  64. package/dist/source/types.d.ts.map +1 -0
  65. package/dist/structure/manager.d.ts +17 -3
  66. package/dist/structure/manager.d.ts.map +1 -1
  67. package/dist/sync/manager.d.ts +7 -6
  68. package/dist/sync/manager.d.ts.map +1 -1
  69. package/dist/types/index.d.ts +10 -23
  70. package/dist/types/index.d.ts.map +1 -1
  71. package/package.json +3 -3
  72. package/dist/bulk/ops.d.ts +0 -57
  73. package/dist/bulk/ops.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -301,13 +301,13 @@ 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 = writeFileSync7;
304
+ module2.exports.sync = writeFileSync8;
305
305
  module2.exports._getTmpname = getTmpname;
306
306
  module2.exports._cleanupOnExit = cleanupOnExit;
307
- var fs22 = require("fs");
307
+ var fs23 = require("fs");
308
308
  var crypto7 = require("node:crypto");
309
309
  var { onExit } = require_cjs();
310
- var path29 = require("path");
310
+ var path28 = require("path");
311
311
  var { promisify } = require("util");
312
312
  var activeFiles = {};
313
313
  var threadId = (function getId() {
@@ -325,7 +325,7 @@ var require_lib = __commonJS({
325
325
  function cleanupOnExit(tmpfile) {
326
326
  return () => {
327
327
  try {
328
- fs22.unlinkSync(typeof tmpfile === "function" ? tmpfile() : tmpfile);
328
+ fs23.unlinkSync(typeof tmpfile === "function" ? tmpfile() : tmpfile);
329
329
  } catch {
330
330
  }
331
331
  };
@@ -360,13 +360,13 @@ var require_lib = __commonJS({
360
360
  let fd;
361
361
  let tmpfile;
362
362
  const removeOnExitHandler = onExit(cleanupOnExit(() => tmpfile));
363
- const absoluteName = path29.resolve(filename);
363
+ const absoluteName = path28.resolve(filename);
364
364
  try {
365
365
  await serializeActiveFile(absoluteName);
366
- const truename = await promisify(fs22.realpath)(filename).catch(() => filename);
366
+ const truename = await promisify(fs23.realpath)(filename).catch(() => filename);
367
367
  tmpfile = getTmpname(truename);
368
368
  if (!options.mode || !options.chown) {
369
- const stats = await promisify(fs22.stat)(truename).catch(() => {
369
+ const stats = await promisify(fs23.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(fs22.open)(tmpfile, "w", options.mode);
380
+ fd = await promisify(fs23.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(fs22.write)(fd, data, 0, data.length, 0);
385
+ await promisify(fs23.write)(fd, data, 0, data.length, 0);
386
386
  } else if (data != null) {
387
- await promisify(fs22.write)(fd, String(data), 0, String(options.encoding || "utf8"));
387
+ await promisify(fs23.write)(fd, String(data), 0, String(options.encoding || "utf8"));
388
388
  }
389
389
  if (options.fsync !== false) {
390
- await promisify(fs22.fsync)(fd);
390
+ await promisify(fs23.fsync)(fd);
391
391
  }
392
- await promisify(fs22.close)(fd);
392
+ await promisify(fs23.close)(fd);
393
393
  fd = null;
394
394
  if (options.chown) {
395
- await promisify(fs22.chown)(tmpfile, options.chown.uid, options.chown.gid).catch((err) => {
395
+ await promisify(fs23.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(fs22.chmod)(tmpfile, options.mode).catch((err) => {
402
+ await promisify(fs23.chmod)(tmpfile, options.mode).catch((err) => {
403
403
  if (!isChownErrOk(err)) {
404
404
  throw err;
405
405
  }
406
406
  });
407
407
  }
408
- await promisify(fs22.rename)(tmpfile, truename);
408
+ await promisify(fs23.rename)(tmpfile, truename);
409
409
  } finally {
410
410
  if (fd) {
411
- await promisify(fs22.close)(fd).catch(
411
+ await promisify(fs23.close)(fd).catch(
412
412
  /* istanbul ignore next */
413
413
  () => {
414
414
  }
415
415
  );
416
416
  }
417
417
  removeOnExitHandler();
418
- await promisify(fs22.unlink)(tmpfile).catch(() => {
418
+ await promisify(fs23.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 writeFileSync7(filename, data, options) {
444
+ function writeFileSync8(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 = fs22.realpathSync(filename);
451
+ filename = fs23.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 = fs22.statSync(filename);
457
+ const stats = fs23.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 = fs22.openSync(tmpfile, "w", options.mode || 438);
473
+ fd = fs23.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
- fs22.writeSync(fd, data, 0, data.length, 0);
478
+ fs23.writeSync(fd, data, 0, data.length, 0);
479
479
  } else if (data != null) {
480
- fs22.writeSync(fd, String(data), 0, String(options.encoding || "utf8"));
480
+ fs23.writeSync(fd, String(data), 0, String(options.encoding || "utf8"));
481
481
  }
482
482
  if (options.fsync !== false) {
483
- fs22.fsyncSync(fd);
483
+ fs23.fsyncSync(fd);
484
484
  }
485
- fs22.closeSync(fd);
485
+ fs23.closeSync(fd);
486
486
  fd = null;
487
487
  if (options.chown) {
488
488
  try {
489
- fs22.chownSync(tmpfile, options.chown.uid, options.chown.gid);
489
+ fs23.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
- fs22.chmodSync(tmpfile, options.mode);
498
+ fs23.chmodSync(tmpfile, options.mode);
499
499
  } catch (err) {
500
500
  if (!isChownErrOk(err)) {
501
501
  throw err;
502
502
  }
503
503
  }
504
504
  }
505
- fs22.renameSync(tmpfile, filename);
505
+ fs23.renameSync(tmpfile, filename);
506
506
  threw = false;
507
507
  } finally {
508
508
  if (fd) {
509
509
  try {
510
- fs22.closeSync(fd);
510
+ fs23.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(fs22) {
549
+ function patch(fs23) {
550
550
  if (constants.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
551
- patchLchmod(fs22);
552
- }
553
- if (!fs22.lutimes) {
554
- patchLutimes(fs22);
555
- }
556
- fs22.chown = chownFix(fs22.chown);
557
- fs22.fchown = chownFix(fs22.fchown);
558
- fs22.lchown = chownFix(fs22.lchown);
559
- fs22.chmod = chmodFix(fs22.chmod);
560
- fs22.fchmod = chmodFix(fs22.fchmod);
561
- fs22.lchmod = chmodFix(fs22.lchmod);
562
- fs22.chownSync = chownFixSync(fs22.chownSync);
563
- fs22.fchownSync = chownFixSync(fs22.fchownSync);
564
- fs22.lchownSync = chownFixSync(fs22.lchownSync);
565
- fs22.chmodSync = chmodFixSync(fs22.chmodSync);
566
- fs22.fchmodSync = chmodFixSync(fs22.fchmodSync);
567
- fs22.lchmodSync = chmodFixSync(fs22.lchmodSync);
568
- fs22.stat = statFix(fs22.stat);
569
- fs22.fstat = statFix(fs22.fstat);
570
- fs22.lstat = statFix(fs22.lstat);
571
- fs22.statSync = statFixSync(fs22.statSync);
572
- fs22.fstatSync = statFixSync(fs22.fstatSync);
573
- fs22.lstatSync = statFixSync(fs22.lstatSync);
574
- if (fs22.chmod && !fs22.lchmod) {
575
- fs22.lchmod = function(path29, mode, cb) {
551
+ patchLchmod(fs23);
552
+ }
553
+ if (!fs23.lutimes) {
554
+ patchLutimes(fs23);
555
+ }
556
+ fs23.chown = chownFix(fs23.chown);
557
+ fs23.fchown = chownFix(fs23.fchown);
558
+ fs23.lchown = chownFix(fs23.lchown);
559
+ fs23.chmod = chmodFix(fs23.chmod);
560
+ fs23.fchmod = chmodFix(fs23.fchmod);
561
+ fs23.lchmod = chmodFix(fs23.lchmod);
562
+ fs23.chownSync = chownFixSync(fs23.chownSync);
563
+ fs23.fchownSync = chownFixSync(fs23.fchownSync);
564
+ fs23.lchownSync = chownFixSync(fs23.lchownSync);
565
+ fs23.chmodSync = chmodFixSync(fs23.chmodSync);
566
+ fs23.fchmodSync = chmodFixSync(fs23.fchmodSync);
567
+ fs23.lchmodSync = chmodFixSync(fs23.lchmodSync);
568
+ fs23.stat = statFix(fs23.stat);
569
+ fs23.fstat = statFix(fs23.fstat);
570
+ fs23.lstat = statFix(fs23.lstat);
571
+ fs23.statSync = statFixSync(fs23.statSync);
572
+ fs23.fstatSync = statFixSync(fs23.fstatSync);
573
+ fs23.lstatSync = statFixSync(fs23.lstatSync);
574
+ if (fs23.chmod && !fs23.lchmod) {
575
+ fs23.lchmod = function(path28, mode, cb) {
576
576
  if (cb) process.nextTick(cb);
577
577
  };
578
- fs22.lchmodSync = function() {
578
+ fs23.lchmodSync = function() {
579
579
  };
580
580
  }
581
- if (fs22.chown && !fs22.lchown) {
582
- fs22.lchown = function(path29, uid, gid, cb) {
581
+ if (fs23.chown && !fs23.lchown) {
582
+ fs23.lchown = function(path28, uid, gid, cb) {
583
583
  if (cb) process.nextTick(cb);
584
584
  };
585
- fs22.lchownSync = function() {
585
+ fs23.lchownSync = function() {
586
586
  };
587
587
  }
588
588
  if (platform === "win32") {
589
- fs22.rename = typeof fs22.rename !== "function" ? fs22.rename : (function(fs$rename) {
589
+ fs23.rename = typeof fs23.rename !== "function" ? fs23.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
- fs22.stat(to, function(stater, st) {
596
+ fs23.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
- })(fs22.rename);
612
+ })(fs23.rename);
613
613
  }
614
- fs22.read = typeof fs22.read !== "function" ? fs22.read : (function(fs$read) {
614
+ fs23.read = typeof fs23.read !== "function" ? fs23.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(fs22, fd, buffer, offset, length, position, callback);
622
+ return fs$read.call(fs23, fd, buffer, offset, length, position, callback);
623
623
  }
624
624
  callback_.apply(this, arguments);
625
625
  };
626
626
  }
627
- return fs$read.call(fs22, fd, buffer, offset, length, position, callback);
627
+ return fs$read.call(fs23, fd, buffer, offset, length, position, callback);
628
628
  }
629
629
  if (Object.setPrototypeOf) Object.setPrototypeOf(read, fs$read);
630
630
  return read;
631
- })(fs22.read);
632
- fs22.readSync = typeof fs22.readSync !== "function" ? fs22.readSync : /* @__PURE__ */ (function(fs$readSync) {
631
+ })(fs23.read);
632
+ fs23.readSync = typeof fs23.readSync !== "function" ? fs23.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(fs22, fd, buffer, offset, length, position);
637
+ return fs$readSync.call(fs23, fd, buffer, offset, length, position);
638
638
  } catch (er) {
639
639
  if (er.code === "EAGAIN" && eagCounter < 10) {
640
640
  eagCounter++;
@@ -644,11 +644,11 @@ var require_polyfills = __commonJS({
644
644
  }
645
645
  }
646
646
  };
647
- })(fs22.readSync);
648
- function patchLchmod(fs23) {
649
- fs23.lchmod = function(path29, mode, callback) {
650
- fs23.open(
651
- path29,
647
+ })(fs23.readSync);
648
+ function patchLchmod(fs24) {
649
+ fs24.lchmod = function(path28, mode, callback) {
650
+ fs24.open(
651
+ path28,
652
652
  constants.O_WRONLY | constants.O_SYMLINK,
653
653
  mode,
654
654
  function(err, fd) {
@@ -656,80 +656,80 @@ var require_polyfills = __commonJS({
656
656
  if (callback) callback(err);
657
657
  return;
658
658
  }
659
- fs23.fchmod(fd, mode, function(err2) {
660
- fs23.close(fd, function(err22) {
659
+ fs24.fchmod(fd, mode, function(err2) {
660
+ fs24.close(fd, function(err22) {
661
661
  if (callback) callback(err2 || err22);
662
662
  });
663
663
  });
664
664
  }
665
665
  );
666
666
  };
667
- fs23.lchmodSync = function(path29, mode) {
668
- var fd = fs23.openSync(path29, constants.O_WRONLY | constants.O_SYMLINK, mode);
667
+ fs24.lchmodSync = function(path28, mode) {
668
+ var fd = fs24.openSync(path28, constants.O_WRONLY | constants.O_SYMLINK, mode);
669
669
  var threw = true;
670
670
  var ret;
671
671
  try {
672
- ret = fs23.fchmodSync(fd, mode);
672
+ ret = fs24.fchmodSync(fd, mode);
673
673
  threw = false;
674
674
  } finally {
675
675
  if (threw) {
676
676
  try {
677
- fs23.closeSync(fd);
677
+ fs24.closeSync(fd);
678
678
  } catch (er) {
679
679
  }
680
680
  } else {
681
- fs23.closeSync(fd);
681
+ fs24.closeSync(fd);
682
682
  }
683
683
  }
684
684
  return ret;
685
685
  };
686
686
  }
687
- function patchLutimes(fs23) {
688
- if (constants.hasOwnProperty("O_SYMLINK") && fs23.futimes) {
689
- fs23.lutimes = function(path29, at, mt, cb) {
690
- fs23.open(path29, constants.O_SYMLINK, function(er, fd) {
687
+ function patchLutimes(fs24) {
688
+ if (constants.hasOwnProperty("O_SYMLINK") && fs24.futimes) {
689
+ fs24.lutimes = function(path28, at, mt, cb) {
690
+ fs24.open(path28, constants.O_SYMLINK, function(er, fd) {
691
691
  if (er) {
692
692
  if (cb) cb(er);
693
693
  return;
694
694
  }
695
- fs23.futimes(fd, at, mt, function(er2) {
696
- fs23.close(fd, function(er22) {
695
+ fs24.futimes(fd, at, mt, function(er2) {
696
+ fs24.close(fd, function(er22) {
697
697
  if (cb) cb(er2 || er22);
698
698
  });
699
699
  });
700
700
  });
701
701
  };
702
- fs23.lutimesSync = function(path29, at, mt) {
703
- var fd = fs23.openSync(path29, constants.O_SYMLINK);
702
+ fs24.lutimesSync = function(path28, at, mt) {
703
+ var fd = fs24.openSync(path28, constants.O_SYMLINK);
704
704
  var ret;
705
705
  var threw = true;
706
706
  try {
707
- ret = fs23.futimesSync(fd, at, mt);
707
+ ret = fs24.futimesSync(fd, at, mt);
708
708
  threw = false;
709
709
  } finally {
710
710
  if (threw) {
711
711
  try {
712
- fs23.closeSync(fd);
712
+ fs24.closeSync(fd);
713
713
  } catch (er) {
714
714
  }
715
715
  } else {
716
- fs23.closeSync(fd);
716
+ fs24.closeSync(fd);
717
717
  }
718
718
  }
719
719
  return ret;
720
720
  };
721
- } else if (fs23.futimes) {
722
- fs23.lutimes = function(_a, _b, _c, cb) {
721
+ } else if (fs24.futimes) {
722
+ fs24.lutimes = function(_a, _b, _c, cb) {
723
723
  if (cb) process.nextTick(cb);
724
724
  };
725
- fs23.lutimesSync = function() {
725
+ fs24.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(fs22, target, mode, function(er) {
732
+ return orig.call(fs23, 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(fs22, target, mode);
742
+ return orig.call(fs23, 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(fs22, target, uid, gid, function(er) {
751
+ return orig.call(fs23, 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(fs22, target, uid, gid);
761
+ return orig.call(fs23, 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(fs22, target, options, callback) : orig.call(fs22, target, callback);
781
+ return options ? orig.call(fs23, target, options, callback) : orig.call(fs23, 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(fs22, target, options) : orig.call(fs22, target);
787
+ var stats = options ? orig.call(fs23, target, options) : orig.call(fs23, target);
788
788
  if (stats) {
789
789
  if (stats.uid < 0) stats.uid += 4294967296;
790
790
  if (stats.gid < 0) stats.gid += 4294967296;
@@ -813,16 +813,16 @@ 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(fs22) {
816
+ function legacy(fs23) {
817
817
  return {
818
818
  ReadStream,
819
819
  WriteStream
820
820
  };
821
- function ReadStream(path29, options) {
822
- if (!(this instanceof ReadStream)) return new ReadStream(path29, options);
821
+ function ReadStream(path28, options) {
822
+ if (!(this instanceof ReadStream)) return new ReadStream(path28, options);
823
823
  Stream.call(this);
824
824
  var self = this;
825
- this.path = path29;
825
+ this.path = path28;
826
826
  this.fd = null;
827
827
  this.readable = true;
828
828
  this.paused = false;
@@ -856,7 +856,7 @@ var require_legacy_streams = __commonJS({
856
856
  });
857
857
  return;
858
858
  }
859
- fs22.open(this.path, this.flags, this.mode, function(err, fd) {
859
+ fs23.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;
@@ -867,10 +867,10 @@ var require_legacy_streams = __commonJS({
867
867
  self._read();
868
868
  });
869
869
  }
870
- function WriteStream(path29, options) {
871
- if (!(this instanceof WriteStream)) return new WriteStream(path29, options);
870
+ function WriteStream(path28, options) {
871
+ if (!(this instanceof WriteStream)) return new WriteStream(path28, options);
872
872
  Stream.call(this);
873
- this.path = path29;
873
+ this.path = path28;
874
874
  this.fd = null;
875
875
  this.writable = true;
876
876
  this.flags = "w";
@@ -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 = fs22.open;
898
+ this._open = fs23.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 fs22 = require("fs");
933
+ var fs23 = 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 (!fs22[gracefulQueue]) {
965
+ if (!fs23[gracefulQueue]) {
966
966
  queue = global[gracefulQueue] || [];
967
- publishQueue(fs22, queue);
968
- fs22.close = (function(fs$close) {
967
+ publishQueue(fs23, queue);
968
+ fs23.close = (function(fs$close) {
969
969
  function close(fd, cb) {
970
- return fs$close.call(fs22, fd, function(err) {
970
+ return fs$close.call(fs23, fd, function(err) {
971
971
  if (!err) {
972
972
  resetQueue();
973
973
  }
@@ -979,48 +979,48 @@ var require_graceful_fs = __commonJS({
979
979
  value: fs$close
980
980
  });
981
981
  return close;
982
- })(fs22.close);
983
- fs22.closeSync = (function(fs$closeSync) {
984
- function closeSync2(fd) {
985
- fs$closeSync.apply(fs22, arguments);
982
+ })(fs23.close);
983
+ fs23.closeSync = (function(fs$closeSync) {
984
+ function closeSync3(fd) {
985
+ fs$closeSync.apply(fs23, arguments);
986
986
  resetQueue();
987
987
  }
988
- Object.defineProperty(closeSync2, previousSymbol, {
988
+ Object.defineProperty(closeSync3, previousSymbol, {
989
989
  value: fs$closeSync
990
990
  });
991
- return closeSync2;
992
- })(fs22.closeSync);
991
+ return closeSync3;
992
+ })(fs23.closeSync);
993
993
  if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) {
994
994
  process.on("exit", function() {
995
- debug(fs22[gracefulQueue]);
996
- require("assert").equal(fs22[gracefulQueue].length, 0);
995
+ debug(fs23[gracefulQueue]);
996
+ require("assert").equal(fs23[gracefulQueue].length, 0);
997
997
  });
998
998
  }
999
999
  }
1000
1000
  var queue;
1001
1001
  if (!global[gracefulQueue]) {
1002
- publishQueue(global, fs22[gracefulQueue]);
1003
- }
1004
- module2.exports = patch(clone(fs22));
1005
- if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs22.__patched) {
1006
- module2.exports = patch(fs22);
1007
- fs22.__patched = true;
1008
- }
1009
- function patch(fs23) {
1010
- polyfills(fs23);
1011
- fs23.gracefulify = patch;
1012
- fs23.createReadStream = createReadStream;
1013
- fs23.createWriteStream = createWriteStream;
1014
- var fs$readFile = fs23.readFile;
1015
- fs23.readFile = readFile;
1016
- function readFile(path29, options, cb) {
1002
+ publishQueue(global, fs23[gracefulQueue]);
1003
+ }
1004
+ module2.exports = patch(clone(fs23));
1005
+ if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs23.__patched) {
1006
+ module2.exports = patch(fs23);
1007
+ fs23.__patched = true;
1008
+ }
1009
+ function patch(fs24) {
1010
+ polyfills(fs24);
1011
+ fs24.gracefulify = patch;
1012
+ fs24.createReadStream = createReadStream;
1013
+ fs24.createWriteStream = createWriteStream;
1014
+ var fs$readFile = fs24.readFile;
1015
+ fs24.readFile = readFile;
1016
+ function readFile(path28, options, cb) {
1017
1017
  if (typeof options === "function")
1018
1018
  cb = options, options = null;
1019
- return go$readFile(path29, options, cb);
1020
- function go$readFile(path30, options2, cb2, startTime) {
1021
- return fs$readFile(path30, options2, function(err) {
1019
+ return go$readFile(path28, options, cb);
1020
+ function go$readFile(path29, options2, cb2, startTime) {
1021
+ return fs$readFile(path29, options2, function(err) {
1022
1022
  if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
1023
- enqueue([go$readFile, [path30, options2, cb2], err, startTime || Date.now(), Date.now()]);
1023
+ enqueue([go$readFile, [path29, options2, cb2], err, startTime || Date.now(), Date.now()]);
1024
1024
  else {
1025
1025
  if (typeof cb2 === "function")
1026
1026
  cb2.apply(this, arguments);
@@ -1028,16 +1028,16 @@ var require_graceful_fs = __commonJS({
1028
1028
  });
1029
1029
  }
1030
1030
  }
1031
- var fs$writeFile = fs23.writeFile;
1032
- fs23.writeFile = writeFile;
1033
- function writeFile(path29, data, options, cb) {
1031
+ var fs$writeFile = fs24.writeFile;
1032
+ fs24.writeFile = writeFile;
1033
+ function writeFile(path28, data, options, cb) {
1034
1034
  if (typeof options === "function")
1035
1035
  cb = options, options = null;
1036
- return go$writeFile(path29, data, options, cb);
1037
- function go$writeFile(path30, data2, options2, cb2, startTime) {
1038
- return fs$writeFile(path30, data2, options2, function(err) {
1036
+ return go$writeFile(path28, data, options, cb);
1037
+ function go$writeFile(path29, data2, options2, cb2, startTime) {
1038
+ return fs$writeFile(path29, data2, options2, function(err) {
1039
1039
  if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
1040
- enqueue([go$writeFile, [path30, data2, options2, cb2], err, startTime || Date.now(), Date.now()]);
1040
+ enqueue([go$writeFile, [path29, data2, options2, cb2], err, startTime || Date.now(), Date.now()]);
1041
1041
  else {
1042
1042
  if (typeof cb2 === "function")
1043
1043
  cb2.apply(this, arguments);
@@ -1045,17 +1045,17 @@ var require_graceful_fs = __commonJS({
1045
1045
  });
1046
1046
  }
1047
1047
  }
1048
- var fs$appendFile = fs23.appendFile;
1048
+ var fs$appendFile = fs24.appendFile;
1049
1049
  if (fs$appendFile)
1050
- fs23.appendFile = appendFile;
1051
- function appendFile(path29, data, options, cb) {
1050
+ fs24.appendFile = appendFile;
1051
+ function appendFile(path28, data, options, cb) {
1052
1052
  if (typeof options === "function")
1053
1053
  cb = options, options = null;
1054
- return go$appendFile(path29, data, options, cb);
1055
- function go$appendFile(path30, data2, options2, cb2, startTime) {
1056
- return fs$appendFile(path30, data2, options2, function(err) {
1054
+ return go$appendFile(path28, data, options, cb);
1055
+ function go$appendFile(path29, data2, options2, cb2, startTime) {
1056
+ return fs$appendFile(path29, data2, options2, function(err) {
1057
1057
  if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
1058
- enqueue([go$appendFile, [path30, data2, options2, cb2], err, startTime || Date.now(), Date.now()]);
1058
+ enqueue([go$appendFile, [path29, data2, options2, cb2], err, startTime || Date.now(), Date.now()]);
1059
1059
  else {
1060
1060
  if (typeof cb2 === "function")
1061
1061
  cb2.apply(this, arguments);
@@ -1063,9 +1063,9 @@ var require_graceful_fs = __commonJS({
1063
1063
  });
1064
1064
  }
1065
1065
  }
1066
- var fs$copyFile = fs23.copyFile;
1066
+ var fs$copyFile = fs24.copyFile;
1067
1067
  if (fs$copyFile)
1068
- fs23.copyFile = copyFile;
1068
+ fs24.copyFile = copyFile;
1069
1069
  function copyFile(src, dest, flags, cb) {
1070
1070
  if (typeof flags === "function") {
1071
1071
  cb = flags;
@@ -1083,34 +1083,34 @@ var require_graceful_fs = __commonJS({
1083
1083
  });
1084
1084
  }
1085
1085
  }
1086
- var fs$readdir = fs23.readdir;
1087
- fs23.readdir = readdir;
1086
+ var fs$readdir = fs24.readdir;
1087
+ fs24.readdir = readdir;
1088
1088
  var noReaddirOptionVersions = /^v[0-5]\./;
1089
- function readdir(path29, options, cb) {
1089
+ function readdir(path28, options, cb) {
1090
1090
  if (typeof options === "function")
1091
1091
  cb = options, options = null;
1092
- var go$readdir = noReaddirOptionVersions.test(process.version) ? function go$readdir2(path30, options2, cb2, startTime) {
1093
- return fs$readdir(path30, fs$readdirCallback(
1094
- path30,
1092
+ var go$readdir = noReaddirOptionVersions.test(process.version) ? function go$readdir2(path29, options2, cb2, startTime) {
1093
+ return fs$readdir(path29, fs$readdirCallback(
1094
+ path29,
1095
1095
  options2,
1096
1096
  cb2,
1097
1097
  startTime
1098
1098
  ));
1099
- } : function go$readdir2(path30, options2, cb2, startTime) {
1100
- return fs$readdir(path30, options2, fs$readdirCallback(
1101
- path30,
1099
+ } : function go$readdir2(path29, options2, cb2, startTime) {
1100
+ return fs$readdir(path29, options2, fs$readdirCallback(
1101
+ path29,
1102
1102
  options2,
1103
1103
  cb2,
1104
1104
  startTime
1105
1105
  ));
1106
1106
  };
1107
- return go$readdir(path29, options, cb);
1108
- function fs$readdirCallback(path30, options2, cb2, startTime) {
1107
+ return go$readdir(path28, options, cb);
1108
+ function fs$readdirCallback(path29, options2, cb2, startTime) {
1109
1109
  return function(err, files) {
1110
1110
  if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
1111
1111
  enqueue([
1112
1112
  go$readdir,
1113
- [path30, options2, cb2],
1113
+ [path29, options2, cb2],
1114
1114
  err,
1115
1115
  startTime || Date.now(),
1116
1116
  Date.now()
@@ -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(fs23);
1128
+ var legStreams = legacy(fs24);
1129
1129
  ReadStream = legStreams.ReadStream;
1130
1130
  WriteStream = legStreams.WriteStream;
1131
1131
  }
1132
- var fs$ReadStream = fs23.ReadStream;
1132
+ var fs$ReadStream = fs24.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 = fs23.WriteStream;
1137
+ var fs$WriteStream = fs24.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(fs23, "ReadStream", {
1142
+ Object.defineProperty(fs24, "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(fs23, "WriteStream", {
1152
+ Object.defineProperty(fs24, "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(fs23, "FileReadStream", {
1163
+ Object.defineProperty(fs24, "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(fs23, "FileWriteStream", {
1174
+ Object.defineProperty(fs24, "FileWriteStream", {
1175
1175
  get: function() {
1176
1176
  return FileWriteStream;
1177
1177
  },
@@ -1181,7 +1181,7 @@ var require_graceful_fs = __commonJS({
1181
1181
  enumerable: true,
1182
1182
  configurable: true
1183
1183
  });
1184
- function ReadStream(path29, options) {
1184
+ function ReadStream(path28, options) {
1185
1185
  if (this instanceof ReadStream)
1186
1186
  return fs$ReadStream.apply(this, arguments), this;
1187
1187
  else
@@ -1201,7 +1201,7 @@ var require_graceful_fs = __commonJS({
1201
1201
  }
1202
1202
  });
1203
1203
  }
1204
- function WriteStream(path29, options) {
1204
+ function WriteStream(path28, options) {
1205
1205
  if (this instanceof WriteStream)
1206
1206
  return fs$WriteStream.apply(this, arguments), this;
1207
1207
  else
@@ -1219,22 +1219,22 @@ var require_graceful_fs = __commonJS({
1219
1219
  }
1220
1220
  });
1221
1221
  }
1222
- function createReadStream(path29, options) {
1223
- return new fs23.ReadStream(path29, options);
1222
+ function createReadStream(path28, options) {
1223
+ return new fs24.ReadStream(path28, options);
1224
1224
  }
1225
- function createWriteStream(path29, options) {
1226
- return new fs23.WriteStream(path29, options);
1225
+ function createWriteStream(path28, options) {
1226
+ return new fs24.WriteStream(path28, options);
1227
1227
  }
1228
- var fs$open = fs23.open;
1229
- fs23.open = open;
1230
- function open(path29, flags, mode, cb) {
1228
+ var fs$open = fs24.open;
1229
+ fs24.open = open;
1230
+ function open(path28, flags, mode, cb) {
1231
1231
  if (typeof mode === "function")
1232
1232
  cb = mode, mode = null;
1233
- return go$open(path29, flags, mode, cb);
1234
- function go$open(path30, flags2, mode2, cb2, startTime) {
1235
- return fs$open(path30, flags2, mode2, function(err, fd) {
1233
+ return go$open(path28, flags, mode, cb);
1234
+ function go$open(path29, flags2, mode2, cb2, startTime) {
1235
+ return fs$open(path29, flags2, mode2, function(err, fd) {
1236
1236
  if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
1237
- enqueue([go$open, [path30, flags2, mode2, cb2], err, startTime || Date.now(), Date.now()]);
1237
+ enqueue([go$open, [path29, flags2, mode2, cb2], err, startTime || Date.now(), Date.now()]);
1238
1238
  else {
1239
1239
  if (typeof cb2 === "function")
1240
1240
  cb2.apply(this, arguments);
@@ -1242,20 +1242,20 @@ var require_graceful_fs = __commonJS({
1242
1242
  });
1243
1243
  }
1244
1244
  }
1245
- return fs23;
1245
+ return fs24;
1246
1246
  }
1247
1247
  function enqueue(elem) {
1248
1248
  debug("ENQUEUE", elem[0].name, elem[1]);
1249
- fs22[gracefulQueue].push(elem);
1249
+ fs23[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 < fs22[gracefulQueue].length; ++i) {
1256
- if (fs22[gracefulQueue][i].length > 2) {
1257
- fs22[gracefulQueue][i][3] = now;
1258
- fs22[gracefulQueue][i][4] = now;
1255
+ for (var i = 0; i < fs23[gracefulQueue].length; ++i) {
1256
+ if (fs23[gracefulQueue][i].length > 2) {
1257
+ fs23[gracefulQueue][i][3] = now;
1258
+ fs23[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 (fs22[gracefulQueue].length === 0)
1266
+ if (fs23[gracefulQueue].length === 0)
1267
1267
  return;
1268
- var elem = fs22[gracefulQueue].shift();
1268
+ var elem = fs23[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
- fs22[gracefulQueue].push(elem);
1290
+ fs23[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, fs22, callback) {
1726
- const cachedPrecision = fs22[cacheSymbol];
1725
+ function probe(file, fs23, callback) {
1726
+ const cachedPrecision = fs23[cacheSymbol];
1727
1727
  if (cachedPrecision) {
1728
- return fs22.stat(file, (err, stat) => {
1728
+ return fs23.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
- fs22.utimes(file, mtime, mtime, (err) => {
1736
+ fs23.utimes(file, mtime, mtime, (err) => {
1737
1737
  if (err) {
1738
1738
  return callback(err);
1739
1739
  }
1740
- fs22.stat(file, (err2, stat) => {
1740
+ fs23.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(fs22, cacheSymbol, { value: precision });
1745
+ Object.defineProperty(fs23, cacheSymbol, { value: precision });
1746
1746
  callback(null, stat.mtime, precision);
1747
1747
  });
1748
1748
  });
@@ -1763,8 +1763,8 @@ var require_mtime_precision = __commonJS({
1763
1763
  var require_lockfile = __commonJS({
1764
1764
  "../../node_modules/proper-lockfile/lib/lockfile.js"(exports2, module2) {
1765
1765
  "use strict";
1766
- var path29 = require("path");
1767
- var fs22 = require_graceful_fs();
1766
+ var path28 = require("path");
1767
+ var fs23 = require_graceful_fs();
1768
1768
  var retry = require_retry2();
1769
1769
  var onExit = require_signal_exit();
1770
1770
  var mtimePrecision = require_mtime_precision();
@@ -1774,7 +1774,7 @@ var require_lockfile = __commonJS({
1774
1774
  }
1775
1775
  function resolveCanonicalPath(file, options, callback) {
1776
1776
  if (!options.realpath) {
1777
- return callback(null, path29.resolve(file));
1777
+ return callback(null, path28.resolve(file));
1778
1778
  }
1779
1779
  options.fs.realpath(file, callback);
1780
1780
  }
@@ -1895,7 +1895,7 @@ var require_lockfile = __commonJS({
1895
1895
  update: null,
1896
1896
  realpath: true,
1897
1897
  retries: 0,
1898
- fs: fs22,
1898
+ fs: fs23,
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: fs22,
1942
+ fs: fs23,
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: fs22,
1964
+ fs: fs23,
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 fs22 = require_graceful_fs();
2004
- function createSyncFs(fs23) {
2003
+ var fs23 = require_graceful_fs();
2004
+ function createSyncFs(fs24) {
2005
2005
  const methods = ["mkdir", "realpath", "stat", "rmdir", "utimes"];
2006
- const newFs = { ...fs23 };
2006
+ const newFs = { ...fs24 };
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 = fs23[`${method}Sync`](...args);
2012
+ ret = fs24[`${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 || fs22);
2050
+ options.fs = createSyncFs(options.fs || fs23);
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
  }
@@ -2303,7 +2303,7 @@ var require_age_encryption = __commonJS({
2303
2303
  Object.assign(hashC, info);
2304
2304
  return Object.freeze(hashC);
2305
2305
  }
2306
- function randomBytes4(bytesLength = 32) {
2306
+ function randomBytes5(bytesLength = 32) {
2307
2307
  anumber(bytesLength, "bytesLength");
2308
2308
  const cr = typeof globalThis === "object" ? globalThis.crypto : null;
2309
2309
  if (typeof cr?.getRandomValues !== "function")
@@ -3030,7 +3030,7 @@ var require_age_encryption = __commonJS({
3030
3030
  };
3031
3031
  }
3032
3032
  // @__NO_SIDE_EFFECTS__
3033
- function join21(separator = "") {
3033
+ function join20(separator = "") {
3034
3034
  astr("join", separator);
3035
3035
  return {
3036
3036
  encode: (from) => {
@@ -3160,9 +3160,9 @@ var require_age_encryption = __commonJS({
3160
3160
  decode(s) {
3161
3161
  return decodeBase64Builtin(s, false);
3162
3162
  }
3163
- } : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ padding(6), /* @__PURE__ */ join21("")));
3164
- var base64nopad = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ join21("")));
3165
- var BECH_ALPHABET = /* @__PURE__ */ chain(/* @__PURE__ */ alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), /* @__PURE__ */ join21(""));
3163
+ } : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ padding(6), /* @__PURE__ */ join20("")));
3164
+ var base64nopad = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ join20("")));
3165
+ var BECH_ALPHABET = /* @__PURE__ */ chain(/* @__PURE__ */ alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), /* @__PURE__ */ join20(""));
3166
3166
  var POLYMOD_GENERATORS = [996825010, 642813549, 513874426, 1027748829, 705979059];
3167
3167
  function bech32Polymod(pre) {
3168
3168
  const b = pre >> 25;
@@ -7655,7 +7655,7 @@ var require_age_encryption = __commonJS({
7655
7655
  var concatBytes4 = (...arrays) => concatBytes(...arrays);
7656
7656
  var hexToBytes3 = (hex) => hexToBytes(hex);
7657
7657
  var isBytes5 = isBytes;
7658
- var randomBytes5 = (bytesLength) => randomBytes4(bytesLength);
7658
+ var randomBytes52 = (bytesLength) => randomBytes5(bytesLength);
7659
7659
  var _0n7 = /* @__PURE__ */ BigInt(0);
7660
7660
  var _1n8 = /* @__PURE__ */ BigInt(1);
7661
7661
  function abool3(value, title = "") {
@@ -9127,7 +9127,7 @@ var require_age_encryption = __commonJS({
9127
9127
  }
9128
9128
  function ecdh2(Point, ecdhOpts = {}) {
9129
9129
  const { Fn: Fn2 } = Point;
9130
- const randomBytes_ = ecdhOpts.randomBytes === void 0 ? randomBytes5 : ecdhOpts.randomBytes;
9130
+ const randomBytes_ = ecdhOpts.randomBytes === void 0 ? randomBytes52 : ecdhOpts.randomBytes;
9131
9131
  const lengths = Object.assign(getWLengths2(Point.Fp, Fn2), {
9132
9132
  seed: Math.max(getMinHashLength2(Fn2.ORDER), 16)
9133
9133
  });
@@ -9201,7 +9201,7 @@ var require_age_encryption = __commonJS({
9201
9201
  bits2int_modN: "function"
9202
9202
  });
9203
9203
  ecdsaOpts = Object.assign({}, ecdsaOpts);
9204
- const randomBytes6 = ecdsaOpts.randomBytes === void 0 ? randomBytes5 : ecdsaOpts.randomBytes;
9204
+ const randomBytes6 = ecdsaOpts.randomBytes === void 0 ? randomBytes52 : ecdsaOpts.randomBytes;
9205
9205
  const hmac3 = ecdsaOpts.hmac === void 0 ? (key, msg) => hmac(hash_, key, msg) : ecdsaOpts.hmac;
9206
9206
  const { Fp: Fp2, Fn: Fn2 } = Point;
9207
9207
  const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn2;
@@ -9815,7 +9815,7 @@ var require_age_encryption = __commonJS({
9815
9815
  const is25519 = type === "x25519";
9816
9816
  if (!is25519 && type !== "x448")
9817
9817
  throw new Error("invalid type");
9818
- const randomBytes_ = rand === void 0 ? randomBytes5 : rand;
9818
+ const randomBytes_ = rand === void 0 ? randomBytes52 : rand;
9819
9819
  const montgomeryBits = is25519 ? 255 : 448;
9820
9820
  const fieldLen = is25519 ? 32 : 56;
9821
9821
  const Gu = is25519 ? BigInt(9) : BigInt(5);
@@ -10449,12 +10449,12 @@ var require_age_encryption = __commonJS({
10449
10449
  return generateX25519Identity();
10450
10450
  }
10451
10451
  function generateX25519Identity() {
10452
- const scalar = randomBytes4(32);
10452
+ const scalar = randomBytes5(32);
10453
10453
  const identity = bech32.encodeFromBytes("AGE-SECRET-KEY-", scalar).toUpperCase();
10454
10454
  return Promise.resolve(identity);
10455
10455
  }
10456
10456
  function generateHybridIdentity() {
10457
- const scalar = randomBytes4(32);
10457
+ const scalar = randomBytes5(32);
10458
10458
  const identity = bech32.encodeFromBytes("AGE-SECRET-KEY-PQ-", scalar).toUpperCase();
10459
10459
  return Promise.resolve(identity);
10460
10460
  }
@@ -10662,7 +10662,7 @@ var require_age_encryption = __commonJS({
10662
10662
  this.recipient = res.bytes;
10663
10663
  }
10664
10664
  async wrapFileKey(fileKey) {
10665
- const ephemeral = randomBytes4(32);
10665
+ const ephemeral = randomBytes5(32);
10666
10666
  const share = await scalarMultBase(ephemeral);
10667
10667
  const secret = await scalarMult(ephemeral, this.recipient);
10668
10668
  const salt = new Uint8Array(share.length + this.recipient.length);
@@ -10723,7 +10723,7 @@ var require_age_encryption = __commonJS({
10723
10723
  this.logN = logN;
10724
10724
  }
10725
10725
  wrapFileKey(fileKey) {
10726
- const salt = randomBytes4(16);
10726
+ const salt = randomBytes5(16);
10727
10727
  const label2 = "age-encryption.org/v1/scrypt";
10728
10728
  const labelAndSalt = new Uint8Array(label2.length + 16);
10729
10729
  labelAndSalt.set(new TextEncoder().encode(label2));
@@ -11058,7 +11058,7 @@ var require_age_encryption = __commonJS({
11058
11058
  rp: { name: "", id: options.rpId },
11059
11059
  user: {
11060
11060
  name: options.keyName,
11061
- id: domBuffer2(randomBytes4(8)),
11061
+ id: domBuffer2(randomBytes5(8)),
11062
11062
  // avoid overwriting existing keys
11063
11063
  displayName: ""
11064
11064
  },
@@ -11128,7 +11128,7 @@ var require_age_encryption = __commonJS({
11128
11128
  transports: this.transports,
11129
11129
  type: "public-key"
11130
11130
  }] : [],
11131
- challenge: domBuffer2(randomBytes4(16)),
11131
+ challenge: domBuffer2(randomBytes5(16)),
11132
11132
  extensions: { prf: { eval: prfInputs(nonce) } },
11133
11133
  userVerification: "required",
11134
11134
  // prf requires UV
@@ -11147,7 +11147,7 @@ var require_age_encryption = __commonJS({
11147
11147
  * Implements {@link Recipient.wrapFileKey}.
11148
11148
  */
11149
11149
  async wrapFileKey(fileKey) {
11150
- const nonce = randomBytes4(16);
11150
+ const nonce = randomBytes5(16);
11151
11151
  const results = await this.getCredential(nonce);
11152
11152
  const key = deriveKey(results);
11153
11153
  return [new Stanza([label, base64nopad.encode(nonce)], encryptFileKey(fileKey, key))];
@@ -11270,7 +11270,7 @@ var require_age_encryption = __commonJS({
11270
11270
  }
11271
11271
  }
11272
11272
  async encrypt(file) {
11273
- const fileKey = randomBytes4(16);
11273
+ const fileKey = randomBytes5(16);
11274
11274
  const stanzas = [];
11275
11275
  let recipients = this.recipients;
11276
11276
  if (this.passphrase !== null) {
@@ -11283,7 +11283,7 @@ var require_age_encryption = __commonJS({
11283
11283
  const hmacKey = hkdf(sha256, fileKey, void 0, labelHeader, 32);
11284
11284
  const mac = hmac(sha256, hmacKey, encodeHeaderNoMAC(stanzas));
11285
11285
  const header = encodeHeader(stanzas, mac);
11286
- const nonce = randomBytes4(16);
11286
+ const nonce = randomBytes5(16);
11287
11287
  const labelPayload = new TextEncoder().encode("payload");
11288
11288
  const streamKey = hkdf(sha256, fileKey, nonce, labelPayload, 32);
11289
11289
  const encrypter = encryptSTREAM(streamKey);
@@ -11420,7 +11420,6 @@ var index_exports = {};
11420
11420
  __export(index_exports, {
11421
11421
  ArtifactPacker: () => ArtifactPacker,
11422
11422
  BackendMigrator: () => BackendMigrator,
11423
- BulkOps: () => BulkOps,
11424
11423
  CLEF_MANIFEST_FILENAME: () => CLEF_MANIFEST_FILENAME,
11425
11424
  CLEF_POLICY_FILENAME: () => CLEF_POLICY_FILENAME,
11426
11425
  CLEF_REPORT_SCHEMA_VERSION: () => CLEF_REPORT_SCHEMA_VERSION,
@@ -11434,6 +11433,7 @@ __export(index_exports, {
11434
11433
  DiffEngine: () => DiffEngine,
11435
11434
  DriftDetector: () => DriftDetector,
11436
11435
  FilePackOutput: () => FilePackOutput,
11436
+ FilesystemStorageBackend: () => FilesystemStorageBackend,
11437
11437
  GitIntegration: () => GitIntegration,
11438
11438
  GitOperationError: () => GitOperationError,
11439
11439
  ImportRunner: () => ImportRunner,
@@ -11450,7 +11450,6 @@ __export(index_exports, {
11450
11450
  PolicyValidationError: () => PolicyValidationError,
11451
11451
  REQUESTS_FILENAME: () => REQUESTS_FILENAME,
11452
11452
  REQUIREMENTS: () => REQUIREMENTS,
11453
- REVEAL_WARNING: () => REVEAL_WARNING,
11454
11453
  RecipientManager: () => RecipientManager,
11455
11454
  ReportGenerator: () => ReportGenerator,
11456
11455
  ReportSanitizer: () => ReportSanitizer,
@@ -11467,6 +11466,7 @@ __export(index_exports, {
11467
11466
  SopsMergeDriver: () => SopsMergeDriver,
11468
11467
  SopsMissingError: () => SopsMissingError,
11469
11468
  SopsVersionError: () => SopsVersionError,
11469
+ SourceCapabilityUnsupportedError: () => SourceCapabilityUnsupportedError,
11470
11470
  StructureManager: () => StructureManager,
11471
11471
  SyncManager: () => SyncManager,
11472
11472
  TransactionLockError: () => TransactionLockError,
@@ -11486,11 +11486,11 @@ __export(index_exports, {
11486
11486
  checkAll: () => checkAll,
11487
11487
  checkDependency: () => checkDependency,
11488
11488
  collectCIContext: () => collectCIContext,
11489
+ composeSecretSource: () => composeSecretSource,
11489
11490
  computeCiphertextHash: () => computeCiphertextHash,
11490
11491
  deriveAgePublicKey: () => deriveAgePublicKey,
11492
+ describeCapabilities: () => describeCapabilities,
11491
11493
  describeScope: () => describeScope,
11492
- detectAlgorithm: () => detectAlgorithm,
11493
- detectFormat: () => detectFormat,
11494
11494
  emptyTemplate: () => emptyTemplate,
11495
11495
  exampleTemplate: () => exampleTemplate,
11496
11496
  findRequest: () => findRequest,
@@ -11498,29 +11498,24 @@ __export(index_exports, {
11498
11498
  formatRevealWarning: () => formatRevealWarning,
11499
11499
  generateAgeIdentity: () => generateAgeIdentity,
11500
11500
  generateRandomValue: () => generateRandomValue,
11501
- generateSigningKeyPair: () => generateSigningKeyPair,
11502
- getPendingKeys: () => getPendingKeys,
11503
- getRotations: () => getRotations,
11501
+ isBulk: () => isBulk,
11504
11502
  isClefHsmArn: () => isClefHsmArn,
11505
11503
  isHighEntropy: () => isHighEntropy,
11506
11504
  isKmsEnvelope: () => isKmsEnvelope,
11505
+ isLintable: () => isLintable,
11506
+ isMergeAware: () => isMergeAware,
11507
+ isMigratable: () => isMigratable,
11507
11508
  isPackedArtifact: () => isPackedArtifact,
11508
- isPending: () => isPending,
11509
+ isRecipientManaged: () => isRecipientManaged,
11510
+ isRotatable: () => isRotatable,
11511
+ isStructural: () => isStructural,
11509
11512
  keyPreview: () => keyPreview,
11510
- loadIgnoreRules: () => loadIgnoreRules,
11511
- loadMetadata: () => loadMetadata,
11512
11513
  loadRequests: () => loadRequests,
11513
11514
  markPending: () => markPending,
11514
- markPendingWithRetry: () => markPendingWithRetry,
11515
11515
  markResolved: () => markResolved,
11516
11516
  matchPatterns: () => matchPatterns,
11517
- mergeMetadataContents: () => mergeMetadataContents,
11518
11517
  mergeMetadataFiles: () => mergeMetadataFiles,
11519
- metadataPath: () => metadataPath,
11520
11518
  parse: () => parse9,
11521
- parseDotenv: () => parseDotenv,
11522
- parseIgnoreContent: () => parseIgnoreContent,
11523
- parseJson: () => parseJson,
11524
11519
  parseSignerKey: () => parseSignerKey,
11525
11520
  parseYaml: () => parseYaml,
11526
11521
  pkcs11UriToSyntheticArn: () => pkcs11UriToSyntheticArn,
@@ -11538,14 +11533,8 @@ __export(index_exports, {
11538
11533
  resolveRecipientsForEnvironment: () => resolveRecipientsForEnvironment,
11539
11534
  resolveSopsPath: () => resolveSopsPath,
11540
11535
  runCompliance: () => runCompliance,
11541
- saveMetadata: () => saveMetadata,
11542
11536
  saveRequests: () => saveRequests,
11543
- serializeSchema: () => serializeSchema,
11544
- shannonEntropy: () => shannonEntropy,
11545
- shouldIgnoreFile: () => shouldIgnoreFile,
11546
- shouldIgnoreMatch: () => shouldIgnoreMatch,
11547
- signEd25519: () => signEd25519,
11548
- signKms: () => signKms,
11537
+ shouldUseLinuxStdinFifo: () => shouldUseLinuxStdinFifo,
11549
11538
  spawnKeyservice: () => spawnKeyservice,
11550
11539
  syntheticArnToPkcs11Uri: () => syntheticArnToPkcs11Uri,
11551
11540
  tryBundledKeyservice: () => tryBundledKeyservice,
@@ -11555,6 +11544,7 @@ __export(index_exports, {
11555
11544
  validatePackedArtifact: () => validatePackedArtifact,
11556
11545
  validateResetScope: () => validateResetScope,
11557
11546
  verifySignature: () => verifySignature,
11547
+ wrapWithLinuxStdinFifo: () => wrapWithLinuxStdinFifo,
11558
11548
  writeManifestYaml: () => writeManifestYaml,
11559
11549
  writeManifestYamlRaw: () => writeManifestYamlRaw,
11560
11550
  writeSchema: () => writeSchema,
@@ -12907,10 +12897,6 @@ async function getPendingKeys(filePath) {
12907
12897
  const metadata = await loadMetadata(filePath);
12908
12898
  return metadata.pending.map((p) => p.key);
12909
12899
  }
12910
- async function isPending(filePath, key) {
12911
- const metadata = await loadMetadata(filePath);
12912
- return metadata.pending.some((p) => p.key === key);
12913
- }
12914
12900
  async function recordRotation(filePath, keys, rotatedBy, now = /* @__PURE__ */ new Date()) {
12915
12901
  const metadata = await loadMetadata(filePath);
12916
12902
  for (const key of keys) {
@@ -12946,14 +12932,6 @@ async function getRotations(filePath) {
12946
12932
  function generateRandomValue() {
12947
12933
  return crypto2.randomBytes(32).toString("hex");
12948
12934
  }
12949
- async function markPendingWithRetry(filePath, keys, setBy, retryDelayMs = 200) {
12950
- try {
12951
- await markPending(filePath, keys, setBy);
12952
- } catch {
12953
- await new Promise((r) => setTimeout(r, retryDelayMs));
12954
- await markPending(filePath, keys, setBy);
12955
- }
12956
- }
12957
12935
 
12958
12936
  // src/sops/keys.ts
12959
12937
  var fs6 = __toESM(require("fs"));
@@ -13003,34 +12981,17 @@ var MatrixManager = class {
13003
12981
  detectMissingCells(manifest, repoRoot) {
13004
12982
  return this.resolveMatrix(manifest, repoRoot).filter((cell) => !cell.exists);
13005
12983
  }
13006
- /**
13007
- * Create an empty encrypted SOPS file for a missing matrix cell.
13008
- *
13009
- * @param cell - The cell to scaffold (must not already exist).
13010
- * @param sopsClient - SOPS client used to write the initial encrypted file.
13011
- * @param manifest - Parsed manifest used to determine the encryption backend.
13012
- */
13013
- async scaffoldCell(cell, sopsClient, manifest) {
13014
- const dir = path5.dirname(cell.filePath);
13015
- if (!fs7.existsSync(dir)) {
13016
- fs7.mkdirSync(dir, { recursive: true });
13017
- }
13018
- await sopsClient.encrypt(cell.filePath, {}, manifest, cell.environment);
13019
- }
13020
12984
  /**
13021
12985
  * Read each cell and return key counts, pending counts, and cross-environment issues.
13022
12986
  *
13023
- * The SOPS client parameter is currently unused keys are read from the
13024
- * plaintext YAML structure directly, no decryption needed. It is retained
13025
- * in the signature for back-compat with callers that may need to swap to a
13026
- * decrypt-based implementation later (e.g. for backends that don't expose
13027
- * key names without decryption).
12987
+ * Keys are read from the plaintext YAML structure directly no
12988
+ * decryption needed. A future backend that doesn't expose key names
12989
+ * without decryption would need its own implementation.
13028
12990
  *
13029
12991
  * @param manifest - Parsed manifest.
13030
12992
  * @param repoRoot - Absolute path to the repository root.
13031
- * @param _sopsClient - Reserved for future use; pass any `EncryptionBackend`.
13032
12993
  */
13033
- async getMatrixStatus(manifest, repoRoot, _sopsClient) {
12994
+ async getMatrixStatus(manifest, repoRoot) {
13034
12995
  const cells = this.resolveMatrix(manifest, repoRoot);
13035
12996
  const statuses = [];
13036
12997
  const cellKeys = /* @__PURE__ */ new Map();
@@ -13346,7 +13307,6 @@ function orderedKeys(keys) {
13346
13307
  }
13347
13308
 
13348
13309
  // src/diff/engine.ts
13349
- var path7 = __toESM(require("path"));
13350
13310
  var DiffEngine = class {
13351
13311
  /**
13352
13312
  * Compare two in-memory value maps and produce a sorted diff result.
@@ -13397,131 +13357,21 @@ var DiffEngine = class {
13397
13357
  * @param namespace - Namespace containing both cells.
13398
13358
  * @param envA - Name of environment A.
13399
13359
  * @param envB - Name of environment B.
13400
- * @param manifest - Parsed manifest used to resolve file paths.
13401
- * @param sopsClient - SOPS client used to decrypt both files.
13402
- * @param repoRoot - Absolute path to the repository root.
13403
- * @throws {@link SopsDecryptionError} If either file cannot be decrypted.
13360
+ * @param source - SecretSource that resolves both cells (substrate-agnostic).
13361
+ * @throws {@link SopsDecryptionError} If either cell cannot be decrypted.
13404
13362
  */
13405
- async diffFiles(namespace, envA, envB, manifest, sopsClient, repoRoot) {
13406
- const fileA = path7.join(
13407
- repoRoot,
13408
- manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", envA)
13409
- );
13410
- const fileB = path7.join(
13411
- repoRoot,
13412
- manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", envB)
13413
- );
13363
+ async diffCells(namespace, envA, envB, source) {
13414
13364
  const [decryptedA, decryptedB] = await Promise.all([
13415
- sopsClient.decrypt(fileA),
13416
- sopsClient.decrypt(fileB)
13365
+ source.readCell({ namespace, environment: envA }),
13366
+ source.readCell({ namespace, environment: envB })
13417
13367
  ]);
13418
13368
  return this.diff(decryptedA.values, decryptedB.values, envA, envB, namespace);
13419
13369
  }
13420
13370
  };
13421
13371
 
13422
- // src/bulk/ops.ts
13423
- var path8 = __toESM(require("path"));
13424
- var BulkOps = class {
13425
- constructor(tx) {
13426
- this.tx = tx;
13427
- }
13428
- tx;
13429
- /**
13430
- * Set a key to different values in multiple environments at once.
13431
- *
13432
- * @param namespace - Target namespace.
13433
- * @param key - Secret key name to set.
13434
- * @param values - Map of `{ environment: value }` pairs.
13435
- * @param manifest - Parsed manifest.
13436
- * @param sopsClient - SOPS client used to decrypt and re-encrypt each file.
13437
- * @param repoRoot - Absolute path to the repository root.
13438
- * @throws Whatever the underlying encrypt throws — the transaction rolls back.
13439
- */
13440
- async setAcrossEnvironments(namespace, key, values, manifest, sopsClient, repoRoot) {
13441
- const targets = manifest.environments.filter((env) => env.name in values).map((env) => ({
13442
- env: env.name,
13443
- filePath: path8.join(
13444
- repoRoot,
13445
- manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", env.name)
13446
- )
13447
- }));
13448
- if (targets.length === 0) return;
13449
- await this.tx.run(repoRoot, {
13450
- description: `clef set: ${namespace}/${key} across ${targets.length} env(s)`,
13451
- paths: targets.map((t) => path8.relative(repoRoot, t.filePath)),
13452
- mutate: async () => {
13453
- for (const target of targets) {
13454
- const decrypted = await sopsClient.decrypt(target.filePath);
13455
- decrypted.values[key] = values[target.env];
13456
- await sopsClient.encrypt(target.filePath, decrypted.values, manifest, target.env);
13457
- }
13458
- }
13459
- });
13460
- }
13461
- /**
13462
- * Delete a key from every environment in a namespace.
13463
- *
13464
- * @param namespace - Target namespace.
13465
- * @param key - Secret key name to delete.
13466
- * @param manifest - Parsed manifest.
13467
- * @param sopsClient - SOPS client.
13468
- * @param repoRoot - Absolute path to the repository root.
13469
- */
13470
- async deleteAcrossEnvironments(namespace, key, manifest, sopsClient, repoRoot) {
13471
- const targets = manifest.environments.map((env) => ({
13472
- env: env.name,
13473
- filePath: path8.join(
13474
- repoRoot,
13475
- manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", env.name)
13476
- )
13477
- }));
13478
- await this.tx.run(repoRoot, {
13479
- description: `clef delete: ${namespace}/${key} from ${targets.length} env(s)`,
13480
- paths: targets.map((t) => path8.relative(repoRoot, t.filePath)),
13481
- mutate: async () => {
13482
- for (const target of targets) {
13483
- const decrypted = await sopsClient.decrypt(target.filePath);
13484
- if (key in decrypted.values) {
13485
- delete decrypted.values[key];
13486
- await sopsClient.encrypt(target.filePath, decrypted.values, manifest, target.env);
13487
- }
13488
- }
13489
- }
13490
- });
13491
- }
13492
- /**
13493
- * Copy a single key's value from one matrix cell to another.
13494
- *
13495
- * @param key - Secret key name to copy.
13496
- * @param fromCell - Source matrix cell.
13497
- * @param toCell - Destination matrix cell.
13498
- * @param sopsClient - SOPS client.
13499
- * @param manifest - Parsed manifest.
13500
- * @param repoRoot - Absolute path to the repository root.
13501
- * @throws `Error` if the key does not exist in the source cell.
13502
- */
13503
- async copyValue(key, fromCell, toCell, sopsClient, manifest, repoRoot) {
13504
- const source = await sopsClient.decrypt(fromCell.filePath);
13505
- if (!(key in source.values)) {
13506
- throw new Error(
13507
- `Key '${key}' does not exist in ${fromCell.namespace}/${fromCell.environment}.`
13508
- );
13509
- }
13510
- await this.tx.run(repoRoot, {
13511
- description: `clef copy: ${key} from ${fromCell.namespace}/${fromCell.environment} to ${toCell.namespace}/${toCell.environment}`,
13512
- paths: [path8.relative(repoRoot, toCell.filePath)],
13513
- mutate: async () => {
13514
- const dest = await sopsClient.decrypt(toCell.filePath);
13515
- dest.values[key] = source.values[key];
13516
- await sopsClient.encrypt(toCell.filePath, dest.values, manifest, toCell.environment);
13517
- }
13518
- });
13519
- }
13520
- };
13521
-
13522
13372
  // src/git/integration.ts
13523
13373
  var fs10 = __toESM(require("fs"));
13524
- var path9 = __toESM(require("path"));
13374
+ var path7 = __toESM(require("path"));
13525
13375
  var PRE_COMMIT_HOOK = `#!/bin/sh
13526
13376
  # Clef pre-commit hook \u2014 blocks commits of files missing SOPS encryption metadata
13527
13377
  # and scans staged files for plaintext secrets.
@@ -13697,17 +13547,17 @@ var GitIntegration = class {
13697
13547
  * @returns The kind of operation in progress, or null if none.
13698
13548
  */
13699
13549
  async isMidOperation(repoRoot) {
13700
- const gitDir = path9.join(repoRoot, ".git");
13701
- if (fs10.existsSync(path9.join(gitDir, "MERGE_HEAD"))) {
13550
+ const gitDir = path7.join(repoRoot, ".git");
13551
+ if (fs10.existsSync(path7.join(gitDir, "MERGE_HEAD"))) {
13702
13552
  return { midOp: true, kind: "merge" };
13703
13553
  }
13704
- if (fs10.existsSync(path9.join(gitDir, "rebase-merge")) || fs10.existsSync(path9.join(gitDir, "rebase-apply"))) {
13554
+ if (fs10.existsSync(path7.join(gitDir, "rebase-merge")) || fs10.existsSync(path7.join(gitDir, "rebase-apply"))) {
13705
13555
  return { midOp: true, kind: "rebase" };
13706
13556
  }
13707
- if (fs10.existsSync(path9.join(gitDir, "CHERRY_PICK_HEAD"))) {
13557
+ if (fs10.existsSync(path7.join(gitDir, "CHERRY_PICK_HEAD"))) {
13708
13558
  return { midOp: true, kind: "cherry-pick" };
13709
13559
  }
13710
- if (fs10.existsSync(path9.join(gitDir, "REVERT_HEAD"))) {
13560
+ if (fs10.existsSync(path7.join(gitDir, "REVERT_HEAD"))) {
13711
13561
  return { midOp: true, kind: "revert" };
13712
13562
  }
13713
13563
  return { midOp: false };
@@ -13919,14 +13769,14 @@ var GitIntegration = class {
13919
13769
  { cwd: repoRoot }
13920
13770
  );
13921
13771
  const metadataGitConfig = metaConfig.exitCode === 0 && metaConfig.stdout.trim().length > 0;
13922
- const attrFilePath = path9.join(repoRoot, ".gitattributes");
13772
+ const attrFilePath = path7.join(repoRoot, ".gitattributes");
13923
13773
  const attrContent = fs10.existsSync(attrFilePath) ? fs10.readFileSync(attrFilePath, "utf-8") : "";
13924
13774
  const gitattributes = attrContent.includes("merge=sops");
13925
13775
  const metadataGitattributes = attrContent.includes("merge=clef-metadata");
13926
13776
  return { gitConfig, gitattributes, metadataGitConfig, metadataGitattributes };
13927
13777
  }
13928
13778
  async ensureGitattributes(repoRoot) {
13929
- const attrPath = path9.join(repoRoot, ".gitattributes");
13779
+ const attrPath = path7.join(repoRoot, ".gitattributes");
13930
13780
  const existing = fs10.existsSync(attrPath) ? fs10.readFileSync(attrPath, "utf-8") : "";
13931
13781
  let newContent = existing;
13932
13782
  if (!existing.includes("merge=sops")) {
@@ -13961,9 +13811,9 @@ ${block}` : block;
13961
13811
  * @throws {@link GitOperationError} On failure.
13962
13812
  */
13963
13813
  async installPreCommitHook(repoRoot) {
13964
- const hookPath = path9.join(repoRoot, ".git", "hooks", "pre-commit");
13814
+ const hookPath = path7.join(repoRoot, ".git", "hooks", "pre-commit");
13965
13815
  try {
13966
- const hooksDir = path9.dirname(hookPath);
13816
+ const hooksDir = path7.dirname(hookPath);
13967
13817
  if (!fs10.existsSync(hooksDir)) {
13968
13818
  fs10.mkdirSync(hooksDir, { recursive: true });
13969
13819
  }
@@ -13979,7 +13829,7 @@ ${block}` : block;
13979
13829
 
13980
13830
  // src/tx/transaction-manager.ts
13981
13831
  var fs11 = __toESM(require("fs"));
13982
- var path10 = __toESM(require("path"));
13832
+ var path8 = __toESM(require("path"));
13983
13833
  var lockfile = __toESM(require_proper_lockfile());
13984
13834
 
13985
13835
  // src/tx/errors.ts
@@ -14028,15 +13878,15 @@ var TransactionManager = class {
14028
13878
  async run(repoRoot, opts) {
14029
13879
  const shouldCommit = opts.commit !== false;
14030
13880
  const allowDirty = opts.allowDirty === true;
14031
- const clefDir = path10.join(repoRoot, CLEF_DIR);
13881
+ const clefDir = path8.join(repoRoot, CLEF_DIR);
14032
13882
  if (!fs11.existsSync(clefDir)) {
14033
13883
  fs11.mkdirSync(clefDir, { recursive: true });
14034
13884
  }
14035
- const clefGitignore = path10.join(clefDir, ".gitignore");
13885
+ const clefGitignore = path8.join(clefDir, ".gitignore");
14036
13886
  if (!fs11.existsSync(clefGitignore)) {
14037
13887
  fs11.writeFileSync(clefGitignore, "*\n");
14038
13888
  }
14039
- const lockPath = path10.join(clefDir, LOCK_FILE);
13889
+ const lockPath = path8.join(clefDir, LOCK_FILE);
14040
13890
  if (!fs11.existsSync(lockPath)) {
14041
13891
  fs11.writeFileSync(lockPath, "");
14042
13892
  }
@@ -14177,16 +14027,15 @@ var TransactionManager = class {
14177
14027
  var fs14 = __toESM(require("fs"));
14178
14028
  var net = __toESM(require("net"));
14179
14029
  var import_crypto = require("crypto");
14180
- var import_write_file_atomic3 = __toESM(require_lib());
14181
14030
  var YAML8 = __toESM(require("yaml"));
14182
14031
 
14183
14032
  // src/sops/resolver.ts
14184
14033
  var fs13 = __toESM(require("fs"));
14185
- var path12 = __toESM(require("path"));
14034
+ var path10 = __toESM(require("path"));
14186
14035
 
14187
14036
  // src/sops/bundled.ts
14188
14037
  var fs12 = __toESM(require("fs"));
14189
- var path11 = __toESM(require("path"));
14038
+ var path9 = __toESM(require("path"));
14190
14039
  function tryBundled() {
14191
14040
  const platform = process.platform;
14192
14041
  const arch = process.arch;
@@ -14198,8 +14047,8 @@ function tryBundled() {
14198
14047
  const binName = platform === "win32" ? "sops.exe" : "sops";
14199
14048
  try {
14200
14049
  const packageMain = require.resolve(`${packageName}/package.json`);
14201
- const packageDir = path11.dirname(packageMain);
14202
- const binPath = path11.join(packageDir, "bin", binName);
14050
+ const packageDir = path9.dirname(packageMain);
14051
+ const binPath = path9.join(packageDir, "bin", binName);
14203
14052
  return fs12.existsSync(binPath) ? binPath : null;
14204
14053
  } catch {
14205
14054
  return null;
@@ -14208,7 +14057,7 @@ function tryBundled() {
14208
14057
 
14209
14058
  // src/sops/resolver.ts
14210
14059
  function validateSopsPath(candidate) {
14211
- if (!path12.isAbsolute(candidate)) {
14060
+ if (!path10.isAbsolute(candidate)) {
14212
14061
  throw new Error(`CLEF_SOPS_PATH must be an absolute path, got '${candidate}'.`);
14213
14062
  }
14214
14063
  const segments = candidate.split(/[/\\]/);
@@ -14387,6 +14236,17 @@ function isClefHsmArn(arn) {
14387
14236
  function formatFromPath(filePath) {
14388
14237
  return filePath.endsWith(".json") ? "json" : "yaml";
14389
14238
  }
14239
+ async function openInputPipe(content) {
14240
+ if (process.platform === "win32") {
14241
+ const pipe = await openWindowsInputPipe(content);
14242
+ return { inputArg: pipe.inputArg, cleanup: pipe.cleanup };
14243
+ }
14244
+ return { inputArg: "/dev/stdin", cleanup: () => {
14245
+ }, runnerStdin: content };
14246
+ }
14247
+ function nullConfigPath() {
14248
+ return process.platform === "win32" ? "NUL" : "/dev/null";
14249
+ }
14390
14250
  function openWindowsInputPipe(content) {
14391
14251
  const pipeName = `\\\\.\\pipe\\clef-sops-${(0, import_crypto.randomBytes)(8).toString("hex")}`;
14392
14252
  return new Promise((resolve2, reject) => {
@@ -14434,6 +14294,10 @@ var SopsClient = class {
14434
14294
  runner;
14435
14295
  ageKeyFile;
14436
14296
  ageKey;
14297
+ /** {@link EncryptionBackend} identifier. */
14298
+ id = "sops";
14299
+ /** {@link EncryptionBackend} short description (used by `clef doctor`). */
14300
+ description = "SOPS-based encryption via the bundled `sops` binary";
14437
14301
  sopsCommand;
14438
14302
  keyserviceArgs;
14439
14303
  buildSopsEnv() {
@@ -14447,14 +14311,18 @@ var SopsClient = class {
14447
14311
  return Object.keys(env).length > 0 ? env : void 0;
14448
14312
  }
14449
14313
  /**
14450
- * Decrypt a SOPS-encrypted file and return its values and metadata.
14314
+ * Decrypt a SOPS-encrypted file by path. The only remaining file-path
14315
+ * entry point on this class — kept for the merge driver, which
14316
+ * receives temp filesystem paths from git that don't map onto a
14317
+ * `CellRef`. Production `SecretSource` consumers should call
14318
+ * `source.readCell` instead.
14451
14319
  *
14452
14320
  * @param filePath - Path to the `.enc.yaml` or `.enc.json` file.
14453
14321
  * @returns {@link DecryptedFile} with plaintext values in memory only.
14454
14322
  * @throws {@link SopsKeyNotFoundError} If no matching decryption key is available.
14455
14323
  * @throws {@link SopsDecryptionError} On any other decryption failure.
14456
14324
  */
14457
- async decrypt(filePath) {
14325
+ async decryptFile(filePath) {
14458
14326
  await assertSops(this.runner, this.sopsCommand);
14459
14327
  const fmt = formatFromPath(filePath);
14460
14328
  const env = this.buildSopsEnv();
@@ -14490,170 +14358,9 @@ var SopsClient = class {
14490
14358
  for (const [key, value] of Object.entries(parsed)) {
14491
14359
  values[key] = String(value);
14492
14360
  }
14493
- const metadata = await this.getMetadata(filePath);
14361
+ const metadata = this.parseMetadataFromFile(filePath);
14494
14362
  return { values, metadata };
14495
14363
  }
14496
- /**
14497
- * Encrypt a key/value map and write it to an encrypted SOPS file.
14498
- *
14499
- * @param filePath - Destination path for the encrypted file.
14500
- * @param values - Flat key/value map to encrypt.
14501
- * @param manifest - Manifest used to determine the encryption backend and key configuration.
14502
- * @param environment - Optional environment name. When provided, per-env backend overrides
14503
- * are resolved from the manifest. When omitted, the global `sops.default_backend` is used.
14504
- * @throws {@link SopsEncryptionError} On encryption or write failure.
14505
- */
14506
- async encrypt(filePath, values, manifest, environment) {
14507
- await assertSops(this.runner, this.sopsCommand);
14508
- const fmt = formatFromPath(filePath);
14509
- const content = fmt === "json" ? JSON.stringify(values, null, 2) : YAML8.stringify(values);
14510
- const args = this.buildEncryptArgs(filePath, manifest, environment);
14511
- const env = this.buildSopsEnv();
14512
- let inputArg;
14513
- let pipeCleanup;
14514
- if (process.platform === "win32") {
14515
- const pipe = await openWindowsInputPipe(content);
14516
- inputArg = pipe.inputArg;
14517
- pipeCleanup = pipe.cleanup;
14518
- } else {
14519
- inputArg = "/dev/stdin";
14520
- }
14521
- let result;
14522
- try {
14523
- const configPath = process.platform === "win32" ? "NUL" : "/dev/null";
14524
- result = await this.runner.run(
14525
- this.sopsCommand,
14526
- [
14527
- "--config",
14528
- configPath,
14529
- "encrypt",
14530
- ...this.keyserviceArgs,
14531
- ...args,
14532
- "--input-type",
14533
- fmt,
14534
- "--output-type",
14535
- fmt,
14536
- "--filename-override",
14537
- filePath,
14538
- inputArg
14539
- ],
14540
- {
14541
- // stdin is still piped on Unix (/dev/stdin reads from it);
14542
- // on Windows the named pipe server feeds content directly.
14543
- ...process.platform !== "win32" ? { stdin: content } : {},
14544
- ...env ? { env } : {}
14545
- }
14546
- );
14547
- } finally {
14548
- pipeCleanup?.();
14549
- }
14550
- if (result.exitCode !== 0) {
14551
- throw new SopsEncryptionError(
14552
- `Failed to encrypt '${filePath}': ${result.stderr.trim()}`,
14553
- filePath
14554
- );
14555
- }
14556
- try {
14557
- await (0, import_write_file_atomic3.default)(filePath, result.stdout);
14558
- } catch (err) {
14559
- throw new SopsEncryptionError(
14560
- `Failed to write encrypted data to '${filePath}': ${err.message}`,
14561
- filePath
14562
- );
14563
- }
14564
- }
14565
- /**
14566
- * Rotate encryption by adding a new age recipient key to an existing SOPS file.
14567
- *
14568
- * @param filePath - Path to the encrypted file to re-encrypt.
14569
- * @param newKey - New age public key to add as a recipient.
14570
- * @throws {@link SopsEncryptionError} On failure.
14571
- */
14572
- async reEncrypt(filePath, newKey) {
14573
- await this.addRecipient(filePath, newKey);
14574
- }
14575
- /**
14576
- * Add an age recipient to an existing SOPS file.
14577
- *
14578
- * @param filePath - Path to the encrypted file.
14579
- * @param key - age public key to add as a recipient.
14580
- * @throws {@link SopsEncryptionError} On failure.
14581
- */
14582
- async addRecipient(filePath, key) {
14583
- await assertSops(this.runner, this.sopsCommand);
14584
- const env = this.buildSopsEnv();
14585
- const result = await this.runner.run(
14586
- this.sopsCommand,
14587
- ["rotate", ...this.keyserviceArgs, "-i", "--add-age", key, filePath],
14588
- {
14589
- ...env ? { env } : {}
14590
- }
14591
- );
14592
- if (result.exitCode !== 0) {
14593
- throw new SopsEncryptionError(
14594
- `Failed to add recipient to '${filePath}': ${result.stderr.trim()}`,
14595
- filePath
14596
- );
14597
- }
14598
- }
14599
- /**
14600
- * Remove an age recipient from an existing SOPS file.
14601
- *
14602
- * @param filePath - Path to the encrypted file.
14603
- * @param key - age public key to remove.
14604
- * @throws {@link SopsEncryptionError} On failure.
14605
- */
14606
- async removeRecipient(filePath, key) {
14607
- await assertSops(this.runner, this.sopsCommand);
14608
- const env = this.buildSopsEnv();
14609
- const result = await this.runner.run(
14610
- this.sopsCommand,
14611
- ["rotate", ...this.keyserviceArgs, "-i", "--rm-age", key, filePath],
14612
- {
14613
- ...env ? { env } : {}
14614
- }
14615
- );
14616
- if (result.exitCode !== 0) {
14617
- throw new SopsEncryptionError(
14618
- `Failed to remove recipient from '${filePath}': ${result.stderr.trim()}`,
14619
- filePath
14620
- );
14621
- }
14622
- }
14623
- /**
14624
- * Check whether a file contains valid SOPS encryption metadata.
14625
- *
14626
- * @param filePath - Path to the file to check.
14627
- * @returns `true` if valid SOPS metadata is present; `false` otherwise. Never throws.
14628
- */
14629
- async validateEncryption(filePath) {
14630
- await assertSops(this.runner, this.sopsCommand);
14631
- try {
14632
- await this.getMetadata(filePath);
14633
- return true;
14634
- } catch {
14635
- return false;
14636
- }
14637
- }
14638
- /**
14639
- * Extract SOPS metadata (backend, recipients, last-modified timestamp) from an encrypted file
14640
- * without decrypting its values.
14641
- *
14642
- * @param filePath - Path to the encrypted file.
14643
- * @returns {@link SopsMetadata} parsed from the file's `sops:` block.
14644
- * @throws {@link SopsDecryptionError} If the file cannot be read or parsed.
14645
- */
14646
- async getMetadata(filePath) {
14647
- await assertSops(this.runner, this.sopsCommand);
14648
- const env = this.buildSopsEnv();
14649
- const result = await this.runner.run(this.sopsCommand, ["filestatus", filePath], {
14650
- ...env ? { env } : {}
14651
- });
14652
- if (result.exitCode !== 0) {
14653
- return this.parseMetadataFromFile(filePath);
14654
- }
14655
- return this.parseMetadataFromFile(filePath);
14656
- }
14657
14364
  /**
14658
14365
  * Determine whether a decrypt failure is caused by a missing/mismatched key (vs. some other
14659
14366
  * SOPS error) without relying on stderr message text.
@@ -14697,20 +14404,30 @@ var SopsClient = class {
14697
14404
  filePath
14698
14405
  );
14699
14406
  }
14407
+ return this.parseMetadataFromContent(content, filePath);
14408
+ }
14409
+ /**
14410
+ * Parse SOPS metadata from a string (no IO). Used by both
14411
+ * `parseMetadataFromFile` (after reading from disk) and the blob-shaped
14412
+ * `getMetadataFromBlob` (which receives ciphertext directly from a
14413
+ * BlobStore). The `label` is woven into error messages so callers can
14414
+ * include the file path or cell ref the content came from.
14415
+ */
14416
+ parseMetadataFromContent(content, label) {
14700
14417
  let parsed;
14701
14418
  try {
14702
14419
  parsed = YAML8.parse(content);
14703
14420
  } catch {
14704
14421
  throw new SopsDecryptionError(
14705
- `File '${filePath}' is not valid YAML. Cannot extract SOPS metadata.`,
14706
- filePath
14422
+ `${label} is not valid YAML. Cannot extract SOPS metadata.`,
14423
+ label
14707
14424
  );
14708
14425
  }
14709
14426
  const sops = parsed?.sops;
14710
14427
  if (!sops) {
14711
14428
  throw new SopsDecryptionError(
14712
- `File '${filePath}' does not contain SOPS metadata. It may not be encrypted.`,
14713
- filePath
14429
+ `${label} does not contain SOPS metadata. It may not be encrypted.`,
14430
+ label
14714
14431
  );
14715
14432
  }
14716
14433
  const backend = this.detectBackend(sops);
@@ -14773,7 +14490,7 @@ var SopsClient = class {
14773
14490
  }
14774
14491
  }
14775
14492
  }
14776
- buildEncryptArgs(filePath, manifest, environment) {
14493
+ buildEncryptArgs(manifest, environment) {
14777
14494
  const args = [];
14778
14495
  const config = environment ? resolveBackendConfig(manifest, environment) : {
14779
14496
  backend: manifest.sops.default_backend,
@@ -14821,11 +14538,255 @@ var SopsClient = class {
14821
14538
  }
14822
14539
  return args;
14823
14540
  }
14541
+ // ── Blob-shaped methods ─────────────────────────────────────────────────
14542
+ //
14543
+ // These mirror the file-path methods above but operate on opaque
14544
+ // ciphertext bytes via SOPS' stdin/stdout. They are the substrate-
14545
+ // agnostic primitives used by the `composeSecretSource` factory to
14546
+ // wrap any `BlobStore` (filesystem, postgres, etc.) into a full
14547
+ // `SecretSource`. Plaintext never leaves the SOPS subprocess.
14548
+ /**
14549
+ * {@link EncryptionBackend.decrypt} — decrypt SOPS-encrypted bytes (e.g.
14550
+ * read from a `StorageBackend`) and return plaintext values + metadata.
14551
+ * Plaintext lives only in memory.
14552
+ */
14553
+ async decrypt(blob, ctx) {
14554
+ await assertSops(this.runner, this.sopsCommand);
14555
+ const env = this.buildSopsEnv();
14556
+ const pipe = await openInputPipe(blob);
14557
+ let result;
14558
+ try {
14559
+ result = await this.runner.run(
14560
+ this.sopsCommand,
14561
+ [
14562
+ "decrypt",
14563
+ ...this.keyserviceArgs,
14564
+ "--input-type",
14565
+ ctx.format,
14566
+ "--output-type",
14567
+ ctx.format,
14568
+ pipe.inputArg
14569
+ ],
14570
+ {
14571
+ ...pipe.runnerStdin !== void 0 ? { stdin: pipe.runnerStdin } : {},
14572
+ ...env ? { env } : {}
14573
+ }
14574
+ );
14575
+ } finally {
14576
+ pipe.cleanup();
14577
+ }
14578
+ if (result.exitCode !== 0) {
14579
+ const errorType = await this.classifyDecryptErrorFromContent(blob);
14580
+ if (errorType === "key-not-found") {
14581
+ throw new SopsKeyNotFoundError(`No decryption key found for cell. ${result.stderr.trim()}`);
14582
+ }
14583
+ throw new SopsDecryptionError(`Failed to decrypt cell: ${result.stderr.trim()}`);
14584
+ }
14585
+ let parsed;
14586
+ try {
14587
+ parsed = YAML8.parse(result.stdout) ?? {};
14588
+ } catch {
14589
+ throw new SopsDecryptionError("Decrypted content is not valid YAML.");
14590
+ }
14591
+ const values = {};
14592
+ for (const [key, value] of Object.entries(parsed)) {
14593
+ values[key] = String(value);
14594
+ }
14595
+ const metadata = this.parseMetadataFromContent(blob, "<cell>");
14596
+ return { values, metadata };
14597
+ }
14598
+ /**
14599
+ * {@link EncryptionBackend.encrypt} — encrypt plaintext values into a
14600
+ * SOPS-formatted ciphertext blob. Returns the bytes as a string;
14601
+ * caller (typically a `StorageBackend`) decides where to put them.
14602
+ * Plaintext is piped via stdin only.
14603
+ */
14604
+ async encrypt(values, ctx) {
14605
+ await assertSops(this.runner, this.sopsCommand);
14606
+ const content = ctx.format === "json" ? JSON.stringify(values, null, 2) : YAML8.stringify(values);
14607
+ const args = this.buildEncryptArgs(ctx.manifest, ctx.environment);
14608
+ const env = this.buildSopsEnv();
14609
+ const pipe = await openInputPipe(content);
14610
+ let result;
14611
+ try {
14612
+ result = await this.runner.run(
14613
+ this.sopsCommand,
14614
+ [
14615
+ "--config",
14616
+ nullConfigPath(),
14617
+ "encrypt",
14618
+ ...this.keyserviceArgs,
14619
+ ...args,
14620
+ "--input-type",
14621
+ ctx.format,
14622
+ "--output-type",
14623
+ ctx.format,
14624
+ pipe.inputArg
14625
+ ],
14626
+ {
14627
+ ...pipe.runnerStdin !== void 0 ? { stdin: pipe.runnerStdin } : {},
14628
+ ...env ? { env } : {}
14629
+ }
14630
+ );
14631
+ } finally {
14632
+ pipe.cleanup();
14633
+ }
14634
+ if (result.exitCode !== 0) {
14635
+ throw new SopsEncryptionError(`Failed to encrypt cell: ${result.stderr.trim()}`);
14636
+ }
14637
+ return result.stdout;
14638
+ }
14639
+ /**
14640
+ * {@link EncryptionBackend.rotate} — add or remove recipients from an
14641
+ * encrypted SOPS blob via stdin/stdout. Drops the in-place `-i` flag
14642
+ * the deleted file-path-shaped methods used, so SOPS writes the
14643
+ * rotated ciphertext to stdout instead of back to a file. Plaintext
14644
+ * stays inside the SOPS subprocess; no plaintext window exists in
14645
+ * this Node process.
14646
+ *
14647
+ * Single SOPS invocation can both add and remove recipients
14648
+ * simultaneously (matches the CLI flag set).
14649
+ */
14650
+ async rotate(blob, opts, ctx) {
14651
+ await assertSops(this.runner, this.sopsCommand);
14652
+ const env = this.buildSopsEnv();
14653
+ const pipe = await openInputPipe(blob);
14654
+ const flagArgs = [];
14655
+ if (opts.addAge) flagArgs.push("--add-age", opts.addAge);
14656
+ if (opts.rmAge) flagArgs.push("--rm-age", opts.rmAge);
14657
+ if (opts.addKms) flagArgs.push("--add-kms", opts.addKms);
14658
+ if (opts.rmKms) flagArgs.push("--rm-kms", opts.rmKms);
14659
+ if (opts.addGcpKms) flagArgs.push("--add-gcp-kms", opts.addGcpKms);
14660
+ if (opts.rmGcpKms) flagArgs.push("--rm-gcp-kms", opts.rmGcpKms);
14661
+ if (opts.addAzureKv) flagArgs.push("--add-azure-kv", opts.addAzureKv);
14662
+ if (opts.rmAzureKv) flagArgs.push("--rm-azure-kv", opts.rmAzureKv);
14663
+ if (opts.addPgp) flagArgs.push("--add-pgp", opts.addPgp);
14664
+ if (opts.rmPgp) flagArgs.push("--rm-pgp", opts.rmPgp);
14665
+ let result;
14666
+ try {
14667
+ result = await this.runner.run(
14668
+ this.sopsCommand,
14669
+ [
14670
+ "--config",
14671
+ nullConfigPath(),
14672
+ "rotate",
14673
+ ...this.keyserviceArgs,
14674
+ ...flagArgs,
14675
+ "--input-type",
14676
+ ctx.format,
14677
+ "--output-type",
14678
+ ctx.format,
14679
+ pipe.inputArg
14680
+ ],
14681
+ {
14682
+ ...pipe.runnerStdin !== void 0 ? { stdin: pipe.runnerStdin } : {},
14683
+ ...env ? { env } : {}
14684
+ }
14685
+ );
14686
+ } finally {
14687
+ pipe.cleanup();
14688
+ }
14689
+ if (result.exitCode !== 0) {
14690
+ throw new SopsEncryptionError(`Failed to rotate cell: ${result.stderr.trim()}`);
14691
+ }
14692
+ return result.stdout;
14693
+ }
14694
+ /**
14695
+ * {@link EncryptionBackend.getMetadata} — extract SOPS metadata from a
14696
+ * ciphertext blob without decrypting. Pure parser, no IO, no
14697
+ * subprocess.
14698
+ */
14699
+ getMetadata(content) {
14700
+ return this.parseMetadataFromContent(content, "<cell>");
14701
+ }
14702
+ /**
14703
+ * {@link EncryptionBackend.validateEncryption} — whether `content` is a
14704
+ * valid SOPS-encrypted blob (parses + has the `sops:` metadata
14705
+ * block). Never throws.
14706
+ */
14707
+ validateEncryption(content) {
14708
+ try {
14709
+ this.parseMetadataFromContent(content, "<cell>");
14710
+ return true;
14711
+ } catch {
14712
+ return false;
14713
+ }
14714
+ }
14715
+ /**
14716
+ * Blob-shaped variant of `classifyDecryptError`. Same logic as the
14717
+ * file-path version but reads metadata from the in-memory ciphertext
14718
+ * instead of disk.
14719
+ */
14720
+ async classifyDecryptErrorFromContent(content) {
14721
+ let metadata;
14722
+ try {
14723
+ metadata = this.parseMetadataFromContent(content, "<cell>");
14724
+ } catch {
14725
+ return "other";
14726
+ }
14727
+ if (metadata.backend !== "age") return "other";
14728
+ if (!this.ageKey && !this.ageKeyFile) return "key-not-found";
14729
+ let keyContent;
14730
+ try {
14731
+ keyContent = this.ageKey ?? fs14.readFileSync(this.ageKeyFile, "utf-8");
14732
+ } catch {
14733
+ return "key-not-found";
14734
+ }
14735
+ const privateKeys = keyContent.split("\n").map((line) => line.trim()).filter((line) => line.startsWith("AGE-SECRET-KEY-"));
14736
+ if (privateKeys.length === 0) return "key-not-found";
14737
+ try {
14738
+ const publicKeys = await Promise.all(privateKeys.map((k) => deriveAgePublicKey(k)));
14739
+ const recipients = new Set(metadata.recipients);
14740
+ return publicKeys.some((pk) => recipients.has(pk)) ? "other" : "key-not-found";
14741
+ } catch {
14742
+ return "other";
14743
+ }
14744
+ }
14824
14745
  };
14825
14746
 
14747
+ // src/sops/linux-stdin-fifo.ts
14748
+ var os = __toESM(require("os"));
14749
+ var path11 = __toESM(require("path"));
14750
+ var import_child_process = require("child_process");
14751
+ function shouldUseLinuxStdinFifo() {
14752
+ return process.platform === "linux" && !process.env.JEST_WORKER_ID;
14753
+ }
14754
+ function wrapWithLinuxStdinFifo(runner) {
14755
+ if (!shouldUseLinuxStdinFifo()) return runner;
14756
+ return {
14757
+ run: (cmd, args, opts) => {
14758
+ const stdinIdx = args.indexOf("/dev/stdin");
14759
+ if (stdinIdx < 0 || opts?.stdin === void 0) {
14760
+ return runner.run(cmd, args, opts);
14761
+ }
14762
+ const fifoDir = (0, import_child_process.execFileSync)("mktemp", ["-d", path11.join(os.tmpdir(), "clef-fifo-XXXXXX")]).toString().trim();
14763
+ const fifoPath = path11.join(fifoDir, "input");
14764
+ (0, import_child_process.execFileSync)("mkfifo", [fifoPath]);
14765
+ const writer = (0, import_child_process.spawn)("dd", [`of=${fifoPath}`, "status=none"], {
14766
+ stdio: ["pipe", "ignore", "ignore"]
14767
+ });
14768
+ writer.stdin.write(opts.stdin);
14769
+ writer.stdin.end();
14770
+ const patchedArgs = [...args];
14771
+ patchedArgs[stdinIdx] = fifoPath;
14772
+ const { stdin: _stdin, ...restOpts } = opts;
14773
+ return runner.run(cmd, patchedArgs, restOpts).finally(() => {
14774
+ try {
14775
+ writer.kill();
14776
+ } catch {
14777
+ }
14778
+ try {
14779
+ (0, import_child_process.execFileSync)("rm", ["-rf", fifoDir]);
14780
+ } catch {
14781
+ }
14782
+ });
14783
+ }
14784
+ };
14785
+ }
14786
+
14826
14787
  // src/hsm/bundled.ts
14827
14788
  var fs15 = __toESM(require("fs"));
14828
- var path13 = __toESM(require("path"));
14789
+ var path12 = __toESM(require("path"));
14829
14790
  function tryBundledKeyservice() {
14830
14791
  const platform = process.platform;
14831
14792
  const arch = process.arch;
@@ -14837,8 +14798,8 @@ function tryBundledKeyservice() {
14837
14798
  const binName = "clef-keyservice";
14838
14799
  try {
14839
14800
  const packageMain = require.resolve(`${packageName}/package.json`);
14840
- const packageDir = path13.dirname(packageMain);
14841
- const binPath = path13.join(packageDir, "bin", binName);
14801
+ const packageDir = path12.dirname(packageMain);
14802
+ const binPath = path12.join(packageDir, "bin", binName);
14842
14803
  return fs15.existsSync(binPath) ? binPath : null;
14843
14804
  } catch {
14844
14805
  return null;
@@ -14847,9 +14808,9 @@ function tryBundledKeyservice() {
14847
14808
 
14848
14809
  // src/hsm/resolver.ts
14849
14810
  var fs16 = __toESM(require("fs"));
14850
- var path14 = __toESM(require("path"));
14811
+ var path13 = __toESM(require("path"));
14851
14812
  function validateKeyservicePath(candidate) {
14852
- if (!path14.isAbsolute(candidate)) {
14813
+ if (!path13.isAbsolute(candidate)) {
14853
14814
  throw new Error(`CLEF_KEYSERVICE_PATH must be an absolute path, got '${candidate}'.`);
14854
14815
  }
14855
14816
  const segments = candidate.split(/[/\\]/);
@@ -14884,7 +14845,7 @@ function resetKeyserviceResolution() {
14884
14845
  }
14885
14846
 
14886
14847
  // src/hsm/keyservice.ts
14887
- var import_child_process = require("child_process");
14848
+ var import_child_process2 = require("child_process");
14888
14849
  var readline = __toESM(require("readline"));
14889
14850
  var PORT_REGEX = /^PORT=(\d+)$/;
14890
14851
  var STARTUP_TIMEOUT_MS = 5e3;
@@ -14902,7 +14863,7 @@ async function spawnKeyservice(options) {
14902
14863
  ...options.pin ? { CLEF_PKCS11_PIN: options.pin } : {},
14903
14864
  ...options.pinFile ? { CLEF_PKCS11_PIN_FILE: options.pinFile } : {}
14904
14865
  };
14905
- const child = (0, import_child_process.spawn)(options.binaryPath, args, {
14866
+ const child = (0, import_child_process2.spawn)(options.binaryPath, args, {
14906
14867
  stdio: ["ignore", "pipe", "pipe"],
14907
14868
  env: childEnv
14908
14869
  });
@@ -14978,16 +14939,16 @@ function killGracefully(child) {
14978
14939
  }
14979
14940
 
14980
14941
  // src/lint/runner.ts
14981
- var path15 = __toESM(require("path"));
14942
+ var path14 = __toESM(require("path"));
14982
14943
  var LintRunner = class {
14983
- constructor(matrixManager, schemaValidator, sopsClient) {
14944
+ constructor(matrixManager, schemaValidator, source) {
14984
14945
  this.matrixManager = matrixManager;
14985
14946
  this.schemaValidator = schemaValidator;
14986
- this.sopsClient = sopsClient;
14947
+ this.source = source;
14987
14948
  }
14988
14949
  matrixManager;
14989
14950
  schemaValidator;
14990
- sopsClient;
14951
+ source;
14991
14952
  /**
14992
14953
  * Lint the entire matrix: check missing files, schema errors, SOPS integrity,
14993
14954
  * single-recipient warnings, and cross-environment key drift.
@@ -15014,8 +14975,9 @@ var LintRunner = class {
15014
14975
  fileCount = existingCells.length;
15015
14976
  const namespaceKeys = {};
15016
14977
  for (const cell of existingCells) {
14978
+ const ref = { namespace: cell.namespace, environment: cell.environment };
15017
14979
  try {
15018
- const isValid = await this.sopsClient.validateEncryption(cell.filePath);
14980
+ const isValid = await this.source.validateEncryption(ref);
15019
14981
  if (!isValid) {
15020
14982
  issues.push({
15021
14983
  severity: "error",
@@ -15036,7 +14998,7 @@ var LintRunner = class {
15036
14998
  continue;
15037
14999
  }
15038
15000
  try {
15039
- const decrypted = await this.sopsClient.decrypt(cell.filePath);
15001
+ const decrypted = await this.source.readCell(ref);
15040
15002
  const keys = Object.keys(decrypted.values);
15041
15003
  if (!namespaceKeys[cell.namespace]) {
15042
15004
  namespaceKeys[cell.namespace] = {};
@@ -15081,7 +15043,7 @@ var LintRunner = class {
15081
15043
  }
15082
15044
  const ns = manifest.namespaces.find((n) => n.name === cell.namespace);
15083
15045
  if (ns?.schema) {
15084
- const schemaPath = path15.join(repoRoot, ns.schema);
15046
+ const schemaPath = path14.join(repoRoot, ns.schema);
15085
15047
  try {
15086
15048
  const schema = this.schemaValidator.loadSchema(schemaPath);
15087
15049
  const result = this.schemaValidator.validate(decrypted.values, schema);
@@ -15124,7 +15086,8 @@ var LintRunner = class {
15124
15086
  }
15125
15087
  }
15126
15088
  try {
15127
- const pendingKeys = await getPendingKeys(cell.filePath);
15089
+ const meta = await this.source.getPendingMetadata(ref);
15090
+ const pendingKeys = meta.pending.map((p) => p.key);
15128
15091
  pendingCount += pendingKeys.length;
15129
15092
  for (const pendingKey of pendingKeys) {
15130
15093
  issues.push({
@@ -15177,7 +15140,6 @@ var LintRunner = class {
15177
15140
  const siIssues = await this.lintServiceIdentities(
15178
15141
  manifest.service_identities,
15179
15142
  manifest,
15180
- repoRoot,
15181
15143
  existingCells
15182
15144
  );
15183
15145
  issues.push(...siIssues);
@@ -15187,18 +15149,27 @@ var LintRunner = class {
15187
15149
  return { issues, fileCount: fileCount + missingCells.length, pendingCount };
15188
15150
  }
15189
15151
  /**
15190
- * Cross-reference `.clef-meta.yaml` against the cipher's plaintext key
15152
+ * Cross-reference cell metadata against the cipher's plaintext key
15191
15153
  * names for each existing cell. Reports orphan rotation records and
15192
- * dual-state (pending + rotation) inconsistencies. Uses
15193
- * {@link readSopsKeyNames} (plaintext YAML parse) — no decryption.
15154
+ * dual-state (pending + rotation) inconsistencies. Uses the source's
15155
+ * `listKeys` (no decryption).
15194
15156
  */
15195
15157
  async lintMetadataConsistency(cells) {
15196
15158
  const issues = [];
15197
15159
  for (const cell of cells) {
15198
- const keysInCipher = readSopsKeyNames(cell.filePath);
15199
- if (keysInCipher === null) continue;
15200
- const cipherKeys = new Set(keysInCipher);
15201
- const metadata = await loadMetadata(cell.filePath);
15160
+ const ref = { namespace: cell.namespace, environment: cell.environment };
15161
+ let cipherKeys;
15162
+ try {
15163
+ cipherKeys = new Set(await this.source.listKeys(ref));
15164
+ } catch {
15165
+ continue;
15166
+ }
15167
+ let metadata;
15168
+ try {
15169
+ metadata = await this.source.getPendingMetadata(ref);
15170
+ } catch {
15171
+ continue;
15172
+ }
15202
15173
  for (const record of metadata.rotations) {
15203
15174
  if (!cipherKeys.has(record.key)) {
15204
15175
  issues.push({
@@ -15229,7 +15200,7 @@ var LintRunner = class {
15229
15200
  /**
15230
15201
  * Lint service identity configurations for drift issues.
15231
15202
  */
15232
- async lintServiceIdentities(identities, manifest, repoRoot, existingCells) {
15203
+ async lintServiceIdentities(identities, manifest, existingCells) {
15233
15204
  const issues = [];
15234
15205
  const declaredEnvNames = new Set(manifest.environments.map((e) => e.name));
15235
15206
  const declaredNsNames = new Set(manifest.namespaces.map((ns) => ns.name));
@@ -15270,9 +15241,10 @@ var LintRunner = class {
15270
15241
  const envConfig = si.environments[cell.environment];
15271
15242
  if (!envConfig) continue;
15272
15243
  if (!envConfig.recipient) continue;
15244
+ const ref = { namespace: cell.namespace, environment: cell.environment };
15273
15245
  if (si.namespaces.includes(cell.namespace)) {
15274
15246
  try {
15275
- const metadata = await this.sopsClient.getMetadata(cell.filePath);
15247
+ const metadata = await this.source.getCellMetadata(ref);
15276
15248
  if (!metadata.recipients.includes(envConfig.recipient)) {
15277
15249
  issues.push({
15278
15250
  severity: "warning",
@@ -15286,7 +15258,7 @@ var LintRunner = class {
15286
15258
  }
15287
15259
  } else {
15288
15260
  try {
15289
- const metadata = await this.sopsClient.getMetadata(cell.filePath);
15261
+ const metadata = await this.source.getCellMetadata(ref);
15290
15262
  if (metadata.recipients.includes(envConfig.recipient)) {
15291
15263
  issues.push({
15292
15264
  severity: "warning",
@@ -15312,7 +15284,10 @@ var LintRunner = class {
15312
15284
  async fix(manifest, repoRoot) {
15313
15285
  const missingCells = this.matrixManager.detectMissingCells(manifest, repoRoot);
15314
15286
  for (const cell of missingCells) {
15315
- await this.matrixManager.scaffoldCell(cell, this.sopsClient, manifest);
15287
+ await this.source.scaffoldCell(
15288
+ { namespace: cell.namespace, environment: cell.environment },
15289
+ manifest
15290
+ );
15316
15291
  }
15317
15292
  return this.run(manifest, repoRoot);
15318
15293
  }
@@ -15367,15 +15342,12 @@ Use 'clef exec' to inject secrets directly into a process, or 'clef export --for
15367
15342
  }
15368
15343
  };
15369
15344
 
15370
- // src/import/index.ts
15371
- var path17 = __toESM(require("path"));
15372
-
15373
15345
  // src/import/parsers.ts
15374
- var path16 = __toESM(require("path"));
15346
+ var path15 = __toESM(require("path"));
15375
15347
  var YAML9 = __toESM(require("yaml"));
15376
15348
  function detectFormat(filePath, content) {
15377
- const base = path16.basename(filePath);
15378
- const ext = path16.extname(filePath).toLowerCase();
15349
+ const base = path15.basename(filePath);
15350
+ const ext = path15.extname(filePath).toLowerCase();
15379
15351
  if (base === ".env" || base.startsWith(".env.")) {
15380
15352
  return "dotenv";
15381
15353
  }
@@ -15525,11 +15497,11 @@ function parse9(content, format, filePath) {
15525
15497
 
15526
15498
  // src/import/index.ts
15527
15499
  var ImportRunner = class {
15528
- constructor(sopsClient, tx) {
15529
- this.sopsClient = sopsClient;
15500
+ constructor(source, tx) {
15501
+ this.source = source;
15530
15502
  this.tx = tx;
15531
15503
  }
15532
- sopsClient;
15504
+ source;
15533
15505
  tx;
15534
15506
  /**
15535
15507
  * Parse a source file and import its key/value pairs into a target `namespace/environment` cell.
@@ -15543,10 +15515,8 @@ var ImportRunner = class {
15543
15515
  */
15544
15516
  async import(target, sourcePath, content, manifest, repoRoot, options) {
15545
15517
  const [ns, env] = target.split("/");
15546
- const filePath = path17.join(
15547
- repoRoot,
15548
- manifest.file_pattern.replace("{namespace}", ns).replace("{environment}", env)
15549
- );
15518
+ const ref = { namespace: ns, environment: env };
15519
+ const relCellPath = manifest.file_pattern.replace("{namespace}", ns).replace("{environment}", env);
15550
15520
  const parsed = parse9(content, options.format ?? "auto", sourcePath ?? "");
15551
15521
  let candidates = Object.entries(parsed.pairs);
15552
15522
  if (options.prefix) {
@@ -15564,7 +15534,7 @@ var ImportRunner = class {
15564
15534
  if (options.dryRun) {
15565
15535
  let existingKeys;
15566
15536
  try {
15567
- const decrypted2 = await this.sopsClient.decrypt(filePath);
15537
+ const decrypted2 = await this.source.readCell(ref);
15568
15538
  existingKeys = new Set(Object.keys(decrypted2.values));
15569
15539
  } catch {
15570
15540
  existingKeys = /* @__PURE__ */ new Set();
@@ -15578,7 +15548,7 @@ var ImportRunner = class {
15578
15548
  }
15579
15549
  return { imported, skipped, failed, warnings, dryRun: true };
15580
15550
  }
15581
- const decrypted = await this.sopsClient.decrypt(filePath);
15551
+ const decrypted = await this.source.readCell(ref);
15582
15552
  const newValues = { ...decrypted.values };
15583
15553
  const rotatedKeys = [];
15584
15554
  for (const [key, value] of candidates) {
@@ -15595,7 +15565,6 @@ var ImportRunner = class {
15595
15565
  if (imported.length === 0) {
15596
15566
  return { imported, skipped, failed, warnings, dryRun: false };
15597
15567
  }
15598
- const relCellPath = path17.relative(repoRoot, filePath);
15599
15568
  const relMetaPath = relCellPath.replace(/\.enc\.(yaml|json)$/, ".clef-meta.yaml");
15600
15569
  await this.tx.run(repoRoot, {
15601
15570
  description: `clef import ${target}: ${imported.length} key(s)`,
@@ -15603,9 +15572,9 @@ var ImportRunner = class {
15603
15572
  // callback are staged and rolled back atomically with the ciphertext.
15604
15573
  paths: [relCellPath, relMetaPath],
15605
15574
  mutate: async () => {
15606
- await this.sopsClient.encrypt(filePath, newValues, manifest, env);
15575
+ await this.source.writeCell(ref, newValues);
15607
15576
  if (options.rotatedBy && rotatedKeys.length > 0) {
15608
- await recordRotation(filePath, rotatedKeys, options.rotatedBy);
15577
+ await this.source.recordRotation(ref, rotatedKeys, options.rotatedBy);
15609
15578
  }
15610
15579
  }
15611
15580
  });
@@ -15614,7 +15583,7 @@ var ImportRunner = class {
15614
15583
  };
15615
15584
 
15616
15585
  // src/recipients/index.ts
15617
- var path18 = __toESM(require("path"));
15586
+ var path16 = __toESM(require("path"));
15618
15587
  function parseRecipientEntry(entry) {
15619
15588
  if (typeof entry === "string") {
15620
15589
  return { key: entry };
@@ -15682,12 +15651,12 @@ function ensureEnvironmentRecipientsArray(doc, envName) {
15682
15651
  return env.recipients;
15683
15652
  }
15684
15653
  var RecipientManager = class {
15685
- constructor(encryption, matrixManager, tx) {
15686
- this.encryption = encryption;
15654
+ constructor(source, matrixManager, tx) {
15655
+ this.source = source;
15687
15656
  this.matrixManager = matrixManager;
15688
15657
  this.tx = tx;
15689
15658
  }
15690
- encryption;
15659
+ source;
15691
15660
  matrixManager;
15692
15661
  tx;
15693
15662
  /**
@@ -15742,7 +15711,7 @@ var RecipientManager = class {
15742
15711
  const reEncryptedFiles = [];
15743
15712
  await this.tx.run(repoRoot, {
15744
15713
  description: environment ? `clef recipients add ${keyPreview(normalizedKey)} -e ${environment}` : `clef recipients add ${keyPreview(normalizedKey)}`,
15745
- paths: [...cells.map((c) => path18.relative(repoRoot, c.filePath)), CLEF_MANIFEST_FILENAME],
15714
+ paths: [...cells.map((c) => path16.relative(repoRoot, c.filePath)), CLEF_MANIFEST_FILENAME],
15746
15715
  mutate: async () => {
15747
15716
  const doc = readManifestYaml(repoRoot);
15748
15717
  const recipients = environment ? ensureEnvironmentRecipientsArray(doc, environment) : ensureRecipientsArray(doc);
@@ -15753,7 +15722,8 @@ var RecipientManager = class {
15753
15722
  }
15754
15723
  writeManifestYaml(repoRoot, doc);
15755
15724
  for (const cell of cells) {
15756
- await this.encryption.addRecipient(cell.filePath, normalizedKey);
15725
+ const ref = { namespace: cell.namespace, environment: cell.environment };
15726
+ await this.source.rotate(ref, { addAge: normalizedKey });
15757
15727
  reEncryptedFiles.push(cell.filePath);
15758
15728
  }
15759
15729
  }
@@ -15801,7 +15771,7 @@ var RecipientManager = class {
15801
15771
  const reEncryptedFiles = [];
15802
15772
  await this.tx.run(repoRoot, {
15803
15773
  description: environment ? `clef recipients remove ${keyPreview(trimmedKey)} -e ${environment}` : `clef recipients remove ${keyPreview(trimmedKey)}`,
15804
- paths: [...cells.map((c) => path18.relative(repoRoot, c.filePath)), CLEF_MANIFEST_FILENAME],
15774
+ paths: [...cells.map((c) => path16.relative(repoRoot, c.filePath)), CLEF_MANIFEST_FILENAME],
15805
15775
  mutate: async () => {
15806
15776
  const doc = readManifestYaml(repoRoot);
15807
15777
  const recipients = environment ? ensureEnvironmentRecipientsArray(doc, environment) : ensureRecipientsArray(doc);
@@ -15809,7 +15779,8 @@ var RecipientManager = class {
15809
15779
  recipients.splice(idx, 1);
15810
15780
  writeManifestYaml(repoRoot, doc);
15811
15781
  for (const cell of cells) {
15812
- await this.encryption.removeRecipient(cell.filePath, trimmedKey);
15782
+ const ref = { namespace: cell.namespace, environment: cell.environment };
15783
+ await this.source.rotate(ref, { rmAge: trimmedKey });
15813
15784
  reEncryptedFiles.push(cell.filePath);
15814
15785
  }
15815
15786
  }
@@ -15831,12 +15802,12 @@ var RecipientManager = class {
15831
15802
 
15832
15803
  // src/recipients/requests.ts
15833
15804
  var fs17 = __toESM(require("fs"));
15834
- var path19 = __toESM(require("path"));
15805
+ var path17 = __toESM(require("path"));
15835
15806
  var YAML10 = __toESM(require("yaml"));
15836
15807
  var REQUESTS_FILENAME = ".clef-requests.yaml";
15837
15808
  var HEADER_COMMENT2 = "# Pending recipient access requests. Approve with: clef recipients approve <label>\n";
15838
15809
  function requestsFilePath(repoRoot) {
15839
- return path19.join(repoRoot, REQUESTS_FILENAME);
15810
+ return path17.join(repoRoot, REQUESTS_FILENAME);
15840
15811
  }
15841
15812
  function loadRequests(repoRoot) {
15842
15813
  const filePath = requestsFilePath(repoRoot);
@@ -15911,7 +15882,7 @@ function findInList(requests, identifier) {
15911
15882
  }
15912
15883
 
15913
15884
  // src/drift/detector.ts
15914
- var path20 = __toESM(require("path"));
15885
+ var path18 = __toESM(require("path"));
15915
15886
  var DriftDetector = class {
15916
15887
  parser = new ManifestParser();
15917
15888
  matrix = new MatrixManager();
@@ -15924,8 +15895,8 @@ var DriftDetector = class {
15924
15895
  * @returns Drift result with any issues found.
15925
15896
  */
15926
15897
  detect(localRoot, remoteRoot, namespaceFilter) {
15927
- const localManifest = this.parser.parse(path20.join(localRoot, CLEF_MANIFEST_FILENAME));
15928
- const remoteManifest = this.parser.parse(path20.join(remoteRoot, CLEF_MANIFEST_FILENAME));
15898
+ const localManifest = this.parser.parse(path18.join(localRoot, CLEF_MANIFEST_FILENAME));
15899
+ const remoteManifest = this.parser.parse(path18.join(remoteRoot, CLEF_MANIFEST_FILENAME));
15929
15900
  const localCells = this.matrix.resolveMatrix(localManifest, localRoot);
15930
15901
  const remoteCells = this.matrix.resolveMatrix(remoteManifest, remoteRoot);
15931
15902
  const localEnvNames = localManifest.environments.map((e) => e.name);
@@ -15989,7 +15960,7 @@ var DriftDetector = class {
15989
15960
  };
15990
15961
 
15991
15962
  // src/report/generator.ts
15992
- var path21 = __toESM(require("path"));
15963
+ var path19 = __toESM(require("path"));
15993
15964
 
15994
15965
  // src/report/sanitizer.ts
15995
15966
  var ReportSanitizer = class {
@@ -16126,14 +16097,14 @@ var ReportSanitizer = class {
16126
16097
 
16127
16098
  // src/report/generator.ts
16128
16099
  var ReportGenerator = class {
16129
- constructor(runner, sopsClient, matrixManager, schemaValidator) {
16100
+ constructor(runner, source, matrixManager, schemaValidator) {
16130
16101
  this.runner = runner;
16131
- this.sopsClient = sopsClient;
16102
+ this.source = source;
16132
16103
  this.matrixManager = matrixManager;
16133
16104
  this.schemaValidator = schemaValidator;
16134
16105
  }
16135
16106
  runner;
16136
- sopsClient;
16107
+ source;
16137
16108
  matrixManager;
16138
16109
  schemaValidator;
16139
16110
  /**
@@ -16149,7 +16120,7 @@ var ReportGenerator = class {
16149
16120
  let manifest = null;
16150
16121
  try {
16151
16122
  const parser = new ManifestParser();
16152
- manifest = parser.parse(path21.join(repoRoot, "clef.yaml"));
16123
+ manifest = parser.parse(path19.join(repoRoot, "clef.yaml"));
16153
16124
  } catch {
16154
16125
  const emptyManifest = {
16155
16126
  manifestVersion: 0,
@@ -16270,16 +16241,17 @@ var ReportGenerator = class {
16270
16241
  metadata: null
16271
16242
  };
16272
16243
  }
16244
+ const ref = { namespace: cell.namespace, environment: cell.environment };
16273
16245
  const keyCount = this.readKeyCount(cell.filePath);
16274
16246
  let pendingCount = 0;
16275
16247
  try {
16276
- const pending = await getPendingKeys(cell.filePath);
16277
- pendingCount = pending.length;
16248
+ const meta = await this.source.getPendingMetadata(ref);
16249
+ pendingCount = meta.pending.length;
16278
16250
  } catch {
16279
16251
  }
16280
16252
  let metadata = null;
16281
16253
  try {
16282
- const sopsMetadata = await this.sopsClient.getMetadata(cell.filePath);
16254
+ const sopsMetadata = await this.source.getCellMetadata(ref);
16283
16255
  metadata = {
16284
16256
  backend: sopsMetadata.backend,
16285
16257
  recipients: sopsMetadata.recipients,
@@ -16302,7 +16274,7 @@ var ReportGenerator = class {
16302
16274
  }
16303
16275
  async buildPolicy(manifest, repoRoot) {
16304
16276
  try {
16305
- const lintRunner = new LintRunner(this.matrixManager, this.schemaValidator, this.sopsClient);
16277
+ const lintRunner = new LintRunner(this.matrixManager, this.schemaValidator, this.source);
16306
16278
  const lintResult = await lintRunner.run(manifest, repoRoot);
16307
16279
  return new ReportSanitizer().sanitize(lintResult.issues);
16308
16280
  } catch {
@@ -16619,9 +16591,9 @@ var SopsMergeDriver = class {
16619
16591
  */
16620
16592
  async mergeFiles(basePath, oursPath, theirsPath) {
16621
16593
  const [baseDecrypted, oursDecrypted, theirsDecrypted] = await Promise.all([
16622
- this.sopsClient.decrypt(basePath),
16623
- this.sopsClient.decrypt(oursPath),
16624
- this.sopsClient.decrypt(theirsPath)
16594
+ this.sopsClient.decryptFile(basePath),
16595
+ this.sopsClient.decryptFile(oursPath),
16596
+ this.sopsClient.decryptFile(theirsPath)
16625
16597
  ]);
16626
16598
  return this.merge(baseDecrypted.values, oursDecrypted.values, theirsDecrypted.values);
16627
16599
  }
@@ -16738,22 +16710,26 @@ function mergeMetadataFiles(_basePath, oursPath, theirsPath) {
16738
16710
  }
16739
16711
 
16740
16712
  // src/service-identity/manager.ts
16741
- var path22 = __toESM(require("path"));
16713
+ var path20 = __toESM(require("path"));
16742
16714
  var ServiceIdentityManager = class {
16743
- constructor(encryption, matrixManager, tx) {
16744
- this.encryption = encryption;
16715
+ constructor(source, matrixManager, tx) {
16716
+ this.source = source;
16745
16717
  this.matrixManager = matrixManager;
16746
16718
  this.tx = tx;
16747
16719
  }
16748
- encryption;
16720
+ source;
16749
16721
  matrixManager;
16750
16722
  tx;
16723
+ /** Helper: cell → ref for the source seam. */
16724
+ ref(cell) {
16725
+ return { namespace: cell.namespace, environment: cell.environment };
16726
+ }
16751
16727
  /**
16752
16728
  * Compute repo-relative paths for a set of cells plus the manifest. Used
16753
16729
  * to seed TransactionManager.run's `paths` argument.
16754
16730
  */
16755
16731
  txPaths(repoRoot, cells) {
16756
- return [...cells.map((c) => path22.relative(repoRoot, c.filePath)), CLEF_MANIFEST_FILENAME];
16732
+ return [...cells.map((c) => path20.relative(repoRoot, c.filePath)), CLEF_MANIFEST_FILENAME];
16757
16733
  }
16758
16734
  /**
16759
16735
  * Create a new service identity with per-environment age key pairs or KMS envelope config.
@@ -16845,7 +16821,7 @@ var ServiceIdentityManager = class {
16845
16821
  if (!envConfig?.recipient) continue;
16846
16822
  if (isKmsEnvelope(envConfig)) continue;
16847
16823
  try {
16848
- await this.encryption.removeRecipient(cell.filePath, envConfig.recipient);
16824
+ await this.source.rotate(this.ref(cell), { rmAge: envConfig.recipient });
16849
16825
  } catch {
16850
16826
  }
16851
16827
  }
@@ -16895,7 +16871,7 @@ var ServiceIdentityManager = class {
16895
16871
  const scopedCells = cells.filter((c) => c.environment === envName);
16896
16872
  for (const cell of scopedCells) {
16897
16873
  try {
16898
- await this.encryption.removeRecipient(cell.filePath, oldConfig.recipient);
16874
+ await this.source.rotate(this.ref(cell), { rmAge: oldConfig.recipient });
16899
16875
  } catch {
16900
16876
  }
16901
16877
  }
@@ -16922,7 +16898,7 @@ var ServiceIdentityManager = class {
16922
16898
  if (isKmsEnvelope(envConfig)) continue;
16923
16899
  if (!envConfig.recipient) continue;
16924
16900
  try {
16925
- await this.encryption.addRecipient(cell.filePath, envConfig.recipient);
16901
+ await this.source.rotate(this.ref(cell), { addAge: envConfig.recipient });
16926
16902
  } catch (err) {
16927
16903
  const message = err instanceof Error ? err.message : String(err);
16928
16904
  if (!message.includes("already")) {
@@ -16970,7 +16946,7 @@ var ServiceIdentityManager = class {
16970
16946
  if (isKmsEnvelope(envConfig)) continue;
16971
16947
  if (!envConfig.recipient) continue;
16972
16948
  try {
16973
- await this.encryption.addRecipient(cell.filePath, envConfig.recipient);
16949
+ await this.source.rotate(this.ref(cell), { addAge: envConfig.recipient });
16974
16950
  affectedFiles.push(cell.filePath);
16975
16951
  } catch (err) {
16976
16952
  const message = err instanceof Error ? err.message : String(err);
@@ -17031,7 +17007,7 @@ var ServiceIdentityManager = class {
17031
17007
  if (isKmsEnvelope(envConfig)) continue;
17032
17008
  if (!envConfig.recipient) continue;
17033
17009
  try {
17034
- await this.encryption.removeRecipient(cell.filePath, envConfig.recipient);
17010
+ await this.source.rotate(this.ref(cell), { rmAge: envConfig.recipient });
17035
17011
  affectedFiles.push(cell.filePath);
17036
17012
  } catch {
17037
17013
  }
@@ -17100,7 +17076,7 @@ var ServiceIdentityManager = class {
17100
17076
  if (!identity.pack_only && !isKmsEnvelope(envConfig) && envConfig.recipient) {
17101
17077
  for (const cell of cells) {
17102
17078
  try {
17103
- await this.encryption.addRecipient(cell.filePath, envConfig.recipient);
17079
+ await this.source.rotate(this.ref(cell), { addAge: envConfig.recipient });
17104
17080
  } catch (err) {
17105
17081
  const message = err instanceof Error ? err.message : String(err);
17106
17082
  if (!message.includes("already")) {
@@ -17177,10 +17153,10 @@ var ServiceIdentityManager = class {
17177
17153
  const scopedCells = cells.filter((c) => c.environment === envName);
17178
17154
  for (const cell of scopedCells) {
17179
17155
  try {
17180
- await this.encryption.removeRecipient(cell.filePath, oldRecipient);
17156
+ await this.source.rotate(this.ref(cell), { rmAge: oldRecipient });
17181
17157
  } catch {
17182
17158
  }
17183
- await this.encryption.addRecipient(cell.filePath, newPublicKey);
17159
+ await this.source.rotate(this.ref(cell), { addAge: newPublicKey });
17184
17160
  }
17185
17161
  }
17186
17162
  }
@@ -17239,7 +17215,7 @@ var ServiceIdentityManager = class {
17239
17215
  if (!envConfig.recipient) continue;
17240
17216
  if (si.namespaces.includes(cell.namespace)) {
17241
17217
  try {
17242
- const metadata = await this.encryption.getMetadata(cell.filePath);
17218
+ const metadata = await this.source.getCellMetadata(this.ref(cell));
17243
17219
  if (!metadata.recipients.includes(envConfig.recipient)) {
17244
17220
  issues.push({
17245
17221
  identity: si.name,
@@ -17254,7 +17230,7 @@ var ServiceIdentityManager = class {
17254
17230
  }
17255
17231
  } else {
17256
17232
  try {
17257
- const metadata = await this.encryption.getMetadata(cell.filePath);
17233
+ const metadata = await this.source.getCellMetadata(this.ref(cell));
17258
17234
  if (metadata.recipients.includes(envConfig.recipient)) {
17259
17235
  issues.push({
17260
17236
  identity: si.name,
@@ -17276,15 +17252,15 @@ var ServiceIdentityManager = class {
17276
17252
 
17277
17253
  // src/structure/manager.ts
17278
17254
  var fs19 = __toESM(require("fs"));
17279
- var path23 = __toESM(require("path"));
17255
+ var path21 = __toESM(require("path"));
17280
17256
  var StructureManager = class {
17281
- constructor(matrixManager, encryption, tx) {
17257
+ constructor(matrixManager, buildSource, tx) {
17282
17258
  this.matrixManager = matrixManager;
17283
- this.encryption = encryption;
17259
+ this.buildSource = buildSource;
17284
17260
  this.tx = tx;
17285
17261
  }
17286
17262
  matrixManager;
17287
- encryption;
17263
+ buildSource;
17288
17264
  tx;
17289
17265
  // ── add ──────────────────────────────────────────────────────────────────
17290
17266
  /**
@@ -17300,7 +17276,7 @@ var StructureManager = class {
17300
17276
  this.assertValidIdentifier("namespace", name);
17301
17277
  const newCellPaths = manifest.environments.map((env) => ({
17302
17278
  environment: env.name,
17303
- filePath: path23.join(
17279
+ filePath: path21.join(
17304
17280
  repoRoot,
17305
17281
  manifest.file_pattern.replace("{namespace}", name).replace("{environment}", env.name)
17306
17282
  )
@@ -17308,7 +17284,7 @@ var StructureManager = class {
17308
17284
  for (const cell of newCellPaths) {
17309
17285
  if (fs19.existsSync(cell.filePath)) {
17310
17286
  throw new Error(
17311
- `Cannot add namespace '${name}': file '${path23.relative(repoRoot, cell.filePath)}' already exists.`
17287
+ `Cannot add namespace '${name}': file '${path21.relative(repoRoot, cell.filePath)}' already exists.`
17312
17288
  );
17313
17289
  }
17314
17290
  }
@@ -17327,21 +17303,14 @@ var StructureManager = class {
17327
17303
  await this.tx.run(repoRoot, {
17328
17304
  description: `clef namespace add ${name}`,
17329
17305
  paths: [
17330
- ...newCellPaths.map((c) => path23.relative(repoRoot, c.filePath)),
17306
+ ...newCellPaths.map((c) => path21.relative(repoRoot, c.filePath)),
17331
17307
  CLEF_MANIFEST_FILENAME
17332
17308
  ],
17333
17309
  mutate: async () => {
17310
+ const source = this.buildSource(updatedManifest);
17334
17311
  for (const cell of newCellPaths) {
17335
- await this.matrixManager.scaffoldCell(
17336
- {
17337
- namespace: name,
17338
- environment: cell.environment,
17339
- filePath: cell.filePath,
17340
- exists: false
17341
- },
17342
- this.encryption,
17343
- updatedManifest
17344
- );
17312
+ const ref = { namespace: name, environment: cell.environment };
17313
+ await source.scaffoldCell(ref, updatedManifest);
17345
17314
  }
17346
17315
  const doc = readManifestYaml(repoRoot);
17347
17316
  const namespaces = doc.namespaces;
@@ -17372,7 +17341,7 @@ var StructureManager = class {
17372
17341
  this.assertValidIdentifier("environment", name);
17373
17342
  const newCellPaths = manifest.namespaces.map((ns) => ({
17374
17343
  namespace: ns.name,
17375
- filePath: path23.join(
17344
+ filePath: path21.join(
17376
17345
  repoRoot,
17377
17346
  manifest.file_pattern.replace("{namespace}", ns.name).replace("{environment}", name)
17378
17347
  )
@@ -17380,7 +17349,7 @@ var StructureManager = class {
17380
17349
  for (const cell of newCellPaths) {
17381
17350
  if (fs19.existsSync(cell.filePath)) {
17382
17351
  throw new Error(
17383
- `Cannot add environment '${name}': file '${path23.relative(repoRoot, cell.filePath)}' already exists.`
17352
+ `Cannot add environment '${name}': file '${path21.relative(repoRoot, cell.filePath)}' already exists.`
17384
17353
  );
17385
17354
  }
17386
17355
  }
@@ -17399,21 +17368,14 @@ var StructureManager = class {
17399
17368
  await this.tx.run(repoRoot, {
17400
17369
  description: `clef env add ${name}`,
17401
17370
  paths: [
17402
- ...newCellPaths.map((c) => path23.relative(repoRoot, c.filePath)),
17371
+ ...newCellPaths.map((c) => path21.relative(repoRoot, c.filePath)),
17403
17372
  CLEF_MANIFEST_FILENAME
17404
17373
  ],
17405
17374
  mutate: async () => {
17375
+ const source = this.buildSource(updatedManifest);
17406
17376
  for (const cell of newCellPaths) {
17407
- await this.matrixManager.scaffoldCell(
17408
- {
17409
- namespace: cell.namespace,
17410
- environment: name,
17411
- filePath: cell.filePath,
17412
- exists: false
17413
- },
17414
- this.encryption,
17415
- updatedManifest
17416
- );
17377
+ const ref = { namespace: cell.namespace, environment: name };
17378
+ await source.scaffoldCell(ref, updatedManifest);
17417
17379
  }
17418
17380
  const doc = readManifestYaml(repoRoot);
17419
17381
  const environments = doc.environments;
@@ -17556,7 +17518,7 @@ var StructureManager = class {
17556
17518
  for (const pair of renamePairs) {
17557
17519
  if (fs19.existsSync(pair.to)) {
17558
17520
  throw new Error(
17559
- `Rename target '${path23.relative(repoRoot, pair.to)}' already exists. Move or remove it first.`
17521
+ `Rename target '${path21.relative(repoRoot, pair.to)}' already exists. Move or remove it first.`
17560
17522
  );
17561
17523
  }
17562
17524
  }
@@ -17599,7 +17561,7 @@ var StructureManager = class {
17599
17561
  for (const pair of renamePairs) {
17600
17562
  if (fs19.existsSync(pair.to)) {
17601
17563
  throw new Error(
17602
- `Rename target '${path23.relative(repoRoot, pair.to)}' already exists. Move or remove it first.`
17564
+ `Rename target '${path21.relative(repoRoot, pair.to)}' already exists. Move or remove it first.`
17603
17565
  );
17604
17566
  }
17605
17567
  }
@@ -17649,7 +17611,7 @@ var StructureManager = class {
17649
17611
  swapAxisInCellPath(repoRoot, manifest, cell, axis, newName) {
17650
17612
  const ns = axis === "namespace" ? newName : cell.namespace;
17651
17613
  const env = axis === "environment" ? newName : cell.environment;
17652
- return path23.join(
17614
+ return path21.join(
17653
17615
  repoRoot,
17654
17616
  manifest.file_pattern.replace("{namespace}", ns).replace("{environment}", env)
17655
17617
  );
@@ -17661,8 +17623,8 @@ var StructureManager = class {
17661
17623
  txPaths(repoRoot, renamePairs) {
17662
17624
  const paths = /* @__PURE__ */ new Set();
17663
17625
  for (const pair of renamePairs) {
17664
- paths.add(path23.relative(repoRoot, pair.from));
17665
- paths.add(path23.relative(repoRoot, pair.to));
17626
+ paths.add(path21.relative(repoRoot, pair.from));
17627
+ paths.add(path21.relative(repoRoot, pair.to));
17666
17628
  }
17667
17629
  paths.add(CLEF_MANIFEST_FILENAME);
17668
17630
  return [...paths];
@@ -17673,7 +17635,7 @@ var StructureManager = class {
17673
17635
  */
17674
17636
  applyRenames(pairs) {
17675
17637
  for (const pair of pairs) {
17676
- const targetDir = path23.dirname(pair.to);
17638
+ const targetDir = path21.dirname(pair.to);
17677
17639
  if (!fs19.existsSync(targetDir)) {
17678
17640
  fs19.mkdirSync(targetDir, { recursive: true });
17679
17641
  }
@@ -17688,10 +17650,10 @@ var StructureManager = class {
17688
17650
  deletePaths(repoRoot, cells) {
17689
17651
  const paths = /* @__PURE__ */ new Set();
17690
17652
  for (const cell of cells) {
17691
- paths.add(path23.relative(repoRoot, cell.filePath));
17653
+ paths.add(path21.relative(repoRoot, cell.filePath));
17692
17654
  const meta = cell.filePath.replace(/\.enc\.(yaml|json)$/, ".clef-meta.yaml");
17693
17655
  if (fs19.existsSync(meta)) {
17694
- paths.add(path23.relative(repoRoot, meta));
17656
+ paths.add(path21.relative(repoRoot, meta));
17695
17657
  }
17696
17658
  }
17697
17659
  paths.add(CLEF_MANIFEST_FILENAME);
@@ -17806,7 +17768,7 @@ function renameKeyPreservingOrder(obj, oldKey, newKey) {
17806
17768
  }
17807
17769
 
17808
17770
  // src/artifact/resolve.ts
17809
- async function resolveIdentitySecrets(identityName, environment, manifest, repoRoot, encryption, matrixManager) {
17771
+ async function resolveIdentitySecrets(identityName, environment, manifest, repoRoot, source, matrixManager) {
17810
17772
  const identity = manifest.service_identities?.find((si) => si.name === identityName);
17811
17773
  if (!identity) {
17812
17774
  throw new Error(`Service identity '${identityName}' not found in manifest.`);
@@ -17822,7 +17784,10 @@ async function resolveIdentitySecrets(identityName, environment, manifest, repoR
17822
17784
  (c) => c.exists && identity.namespaces.includes(c.namespace) && c.environment === environment
17823
17785
  );
17824
17786
  for (const cell of cells) {
17825
- const decrypted = await encryption.decrypt(cell.filePath);
17787
+ const decrypted = await source.readCell({
17788
+ namespace: cell.namespace,
17789
+ environment: cell.environment
17790
+ });
17826
17791
  const bucket = allValues[cell.namespace] ??= {};
17827
17792
  for (const [key, value] of Object.entries(decrypted.values)) {
17828
17793
  if (key in bucket && bucket[key] !== value) {
@@ -17846,14 +17811,14 @@ var crypto5 = __toESM(require("crypto"));
17846
17811
 
17847
17812
  // src/artifact/output.ts
17848
17813
  var fs20 = __toESM(require("fs"));
17849
- var path24 = __toESM(require("path"));
17814
+ var path22 = __toESM(require("path"));
17850
17815
  var FilePackOutput = class {
17851
17816
  constructor(outputPath) {
17852
17817
  this.outputPath = outputPath;
17853
17818
  }
17854
17819
  outputPath;
17855
17820
  async write(_artifact, json) {
17856
- const outputDir = path24.dirname(this.outputPath);
17821
+ const outputDir = path22.dirname(this.outputPath);
17857
17822
  if (!fs20.existsSync(outputDir)) {
17858
17823
  fs20.mkdirSync(outputDir, { recursive: true });
17859
17824
  }
@@ -17900,17 +17865,6 @@ function buildSigningPayload(artifact) {
17900
17865
  ];
17901
17866
  return Buffer.from(fields.join("\n"), "utf-8");
17902
17867
  }
17903
- function generateSigningKeyPair() {
17904
- const pair = crypto3.generateKeyPairSync("ed25519");
17905
- return {
17906
- publicKey: pair.publicKey.export({ type: "spki", format: "der" }).toString(
17907
- "base64"
17908
- ),
17909
- privateKey: pair.privateKey.export({ type: "pkcs8", format: "der" }).toString(
17910
- "base64"
17911
- )
17912
- };
17913
- }
17914
17868
  function signEd25519(payload, privateKeyBase64) {
17915
17869
  const keyObj = crypto3.createPrivateKey({
17916
17870
  key: Buffer.from(privateKeyBase64, "base64"),
@@ -17946,17 +17900,6 @@ function verifySignature(payload, signatureBase64, publicKeyBase64) {
17946
17900
  }
17947
17901
  throw new Error(`Unsupported key type for signature verification: ${keyType}`);
17948
17902
  }
17949
- function detectAlgorithm(publicKeyBase64) {
17950
- const keyObj = crypto3.createPublicKey({
17951
- key: Buffer.from(publicKeyBase64, "base64"),
17952
- format: "der",
17953
- type: "spki"
17954
- });
17955
- const keyType = keyObj.asymmetricKeyType;
17956
- if (keyType === "ed25519") return "Ed25519";
17957
- if (keyType === "ec") return "ECDSA_SHA256";
17958
- throw new Error(`Unsupported key type: ${keyType}`);
17959
- }
17960
17903
 
17961
17904
  // src/artifact/hash.ts
17962
17905
  var crypto4 = __toESM(require("crypto"));
@@ -17966,12 +17909,12 @@ function computeCiphertextHash(ciphertext) {
17966
17909
 
17967
17910
  // src/artifact/packer.ts
17968
17911
  var ArtifactPacker = class {
17969
- constructor(encryption, matrixManager, kms) {
17970
- this.encryption = encryption;
17912
+ constructor(source, matrixManager, kms) {
17913
+ this.source = source;
17971
17914
  this.matrixManager = matrixManager;
17972
17915
  this.kms = kms;
17973
17916
  }
17974
- encryption;
17917
+ source;
17975
17918
  matrixManager;
17976
17919
  kms;
17977
17920
  /**
@@ -17989,7 +17932,7 @@ var ArtifactPacker = class {
17989
17932
  config.environment,
17990
17933
  manifest,
17991
17934
  repoRoot,
17992
- this.encryption,
17935
+ this.source,
17993
17936
  this.matrixManager
17994
17937
  );
17995
17938
  const plaintext = JSON.stringify(resolved.values);
@@ -18374,11 +18317,7 @@ var JsonEnvelopeBackend = class {
18374
18317
  }
18375
18318
  async pack(req) {
18376
18319
  const opts = req.backendOptions;
18377
- const packer = new ArtifactPacker(
18378
- req.services.encryption,
18379
- new MatrixManager(),
18380
- req.services.kms
18381
- );
18320
+ const packer = new ArtifactPacker(req.services.source, new MatrixManager(), req.services.kms);
18382
18321
  const output = opts.output ?? (opts.outputPath ? new FilePackOutput(opts.outputPath) : void 0);
18383
18322
  const result = await packer.pack(
18384
18323
  {
@@ -18407,7 +18346,7 @@ var JsonEnvelopeBackend = class {
18407
18346
  var VALID_KMS_PROVIDERS = ["aws", "gcp", "azure"];
18408
18347
 
18409
18348
  // src/migration/backend.ts
18410
- var path25 = __toESM(require("path"));
18349
+ var path23 = __toESM(require("path"));
18411
18350
  var YAML12 = __toESM(require("yaml"));
18412
18351
  var BACKEND_KEY_FIELDS = {
18413
18352
  age: void 0,
@@ -18435,23 +18374,24 @@ function metadataMatchesTarget(meta, target) {
18435
18374
  }
18436
18375
  var BackendMigrator = class {
18437
18376
  /**
18438
- * @param encryption - Backend used for both decrypt and encrypt (standard case).
18377
+ * @param buildSource - Factory that builds a `SecretSource` bound to a
18378
+ * given manifest. Called twice during a real migration: once with the
18379
+ * pre-migration manifest (for classification + decrypt) and once with
18380
+ * the post-mutation manifest (for re-encrypt + verify). The factory
18381
+ * pattern is required because the encryption layer of a composed
18382
+ * source is bound to a manifest at construction.
18439
18383
  * @param matrixManager - Matrix resolver.
18440
18384
  * @param tx - Transaction manager that wraps the migration in a single git commit
18441
18385
  * so a partial failure rolls back ALL files + the manifest via `git reset --hard`.
18442
- * @param targetEncryption - Optional separate backend for encrypt. Use when migrating
18443
- * from cloud (decrypt via keyservice) to another backend (encrypt via local credentials).
18444
18386
  */
18445
- constructor(encryption, matrixManager, tx, targetEncryption) {
18387
+ constructor(buildSource, matrixManager, tx) {
18388
+ this.buildSource = buildSource;
18446
18389
  this.matrixManager = matrixManager;
18447
18390
  this.tx = tx;
18448
- this.decryptBackend = encryption;
18449
- this.encryptBackend = targetEncryption ?? encryption;
18450
18391
  }
18392
+ buildSource;
18451
18393
  matrixManager;
18452
18394
  tx;
18453
- decryptBackend;
18454
- encryptBackend;
18455
18395
  async migrate(manifest, repoRoot, options, onProgress) {
18456
18396
  const { target, environment, dryRun, skipVerify } = options;
18457
18397
  if (environment) {
@@ -18471,10 +18411,12 @@ var BackendMigrator = class {
18471
18411
  warnings: ["No encrypted files found to migrate."]
18472
18412
  };
18473
18413
  }
18414
+ const sourceBefore = this.buildSource(manifest);
18474
18415
  const toMigrate = [];
18475
18416
  const skippedFiles = [];
18476
18417
  for (const cell of targetCells) {
18477
- const meta = await this.decryptBackend.getMetadata(cell.filePath);
18418
+ const ref = { namespace: cell.namespace, environment: cell.environment };
18419
+ const meta = await sourceBefore.getCellMetadata(ref);
18478
18420
  if (metadataMatchesTarget(meta, target)) {
18479
18421
  skippedFiles.push(cell.filePath);
18480
18422
  onProgress?.({
@@ -18525,11 +18467,12 @@ var BackendMigrator = class {
18525
18467
  const migratedFiles = [];
18526
18468
  let migrationFailed = false;
18527
18469
  let migrationError;
18470
+ let sourceAfter;
18528
18471
  try {
18529
18472
  await this.tx.run(repoRoot, {
18530
18473
  description: environment ? `clef migrate-backend ${target.backend}: ${environment}` : `clef migrate-backend ${target.backend}`,
18531
18474
  paths: [
18532
- ...toMigrate.map((c) => path25.relative(repoRoot, c.filePath)),
18475
+ ...toMigrate.map((c) => path23.relative(repoRoot, c.filePath)),
18533
18476
  CLEF_MANIFEST_FILENAME
18534
18477
  ],
18535
18478
  mutate: async () => {
@@ -18537,19 +18480,16 @@ var BackendMigrator = class {
18537
18480
  this.updateManifestDoc(doc, target, environment);
18538
18481
  writeManifestYaml(repoRoot, doc);
18539
18482
  const updatedManifest = YAML12.parse(YAML12.stringify(doc));
18483
+ sourceAfter = this.buildSource(updatedManifest);
18540
18484
  for (const cell of toMigrate) {
18541
18485
  onProgress?.({
18542
18486
  type: "migrate",
18543
18487
  file: cell.filePath,
18544
18488
  message: `Migrating ${cell.namespace}/${cell.environment}...`
18545
18489
  });
18546
- const decrypted = await this.decryptBackend.decrypt(cell.filePath);
18547
- await this.encryptBackend.encrypt(
18548
- cell.filePath,
18549
- decrypted.values,
18550
- updatedManifest,
18551
- cell.environment
18552
- );
18490
+ const ref = { namespace: cell.namespace, environment: cell.environment };
18491
+ const decrypted = await sourceBefore.readCell(ref);
18492
+ await sourceAfter.writeCell(ref, decrypted.values);
18553
18493
  migratedFiles.push(cell.filePath);
18554
18494
  }
18555
18495
  }
@@ -18579,7 +18519,7 @@ var BackendMigrator = class {
18579
18519
  }
18580
18520
  const verifiedFiles = [];
18581
18521
  const warnings = [];
18582
- if (!skipVerify) {
18522
+ if (!skipVerify && sourceAfter) {
18583
18523
  for (const cell of toMigrate) {
18584
18524
  try {
18585
18525
  onProgress?.({
@@ -18587,7 +18527,8 @@ var BackendMigrator = class {
18587
18527
  file: cell.filePath,
18588
18528
  message: `Verifying ${cell.namespace}/${cell.environment}...`
18589
18529
  });
18590
- await this.encryptBackend.decrypt(cell.filePath);
18530
+ const ref = { namespace: cell.namespace, environment: cell.environment };
18531
+ await sourceAfter.readCell(ref);
18591
18532
  verifiedFiles.push(cell.filePath);
18592
18533
  } catch (err) {
18593
18534
  const errorMsg = err instanceof Error ? err.message : String(err);
@@ -18632,16 +18573,16 @@ var BackendMigrator = class {
18632
18573
  };
18633
18574
 
18634
18575
  // src/reset/manager.ts
18635
- var path26 = __toESM(require("path"));
18576
+ var path24 = __toESM(require("path"));
18636
18577
  var ResetManager = class {
18637
- constructor(matrixManager, encryption, schemaValidator, tx) {
18578
+ constructor(matrixManager, buildSource, schemaValidator, tx) {
18638
18579
  this.matrixManager = matrixManager;
18639
- this.encryption = encryption;
18580
+ this.buildSource = buildSource;
18640
18581
  this.schemaValidator = schemaValidator;
18641
18582
  this.tx = tx;
18642
18583
  }
18643
18584
  matrixManager;
18644
- encryption;
18585
+ buildSource;
18645
18586
  schemaValidator;
18646
18587
  tx;
18647
18588
  async reset(opts, manifest, repoRoot) {
@@ -18661,11 +18602,11 @@ var ResetManager = class {
18661
18602
  txPaths.push(CLEF_MANIFEST_FILENAME);
18662
18603
  }
18663
18604
  for (const cell of targetCells) {
18664
- txPaths.push(path26.relative(repoRoot, cell.filePath));
18605
+ txPaths.push(path24.relative(repoRoot, cell.filePath));
18665
18606
  const cellKeys = keyPlan.get(cell.namespace) ?? [];
18666
18607
  if (cellKeys.length > 0) {
18667
18608
  txPaths.push(
18668
- path26.relative(repoRoot, cell.filePath.replace(/\.enc\.(yaml|json)$/, ".clef-meta.yaml"))
18609
+ path24.relative(repoRoot, cell.filePath.replace(/\.enc\.(yaml|json)$/, ".clef-meta.yaml"))
18669
18610
  );
18670
18611
  }
18671
18612
  }
@@ -18682,17 +18623,14 @@ var ResetManager = class {
18682
18623
  writeManifestYaml(repoRoot, doc);
18683
18624
  effectiveManifest = withBackendOverride(manifest, affectedEnvs, opts.backend, opts.key);
18684
18625
  }
18626
+ const source = this.buildSource(effectiveManifest);
18685
18627
  for (const cell of targetCells) {
18686
18628
  const keys = keyPlan.get(cell.namespace) ?? [];
18687
18629
  const placeholders = this.buildPlaceholders(keys);
18688
- await this.encryption.encrypt(
18689
- cell.filePath,
18690
- placeholders,
18691
- effectiveManifest,
18692
- cell.environment
18693
- );
18630
+ const ref = { namespace: cell.namespace, environment: cell.environment };
18631
+ await source.writeCell(ref, placeholders);
18694
18632
  if (keys.length > 0) {
18695
- await markPendingWithRetry(cell.filePath, keys, "clef reset");
18633
+ await source.markPending(ref, keys, "clef reset");
18696
18634
  pendingKeysByCell[cell.filePath] = keys;
18697
18635
  }
18698
18636
  scaffoldedCells.push(cell.filePath);
@@ -18753,7 +18691,7 @@ var ResetManager = class {
18753
18691
  for (const namespace of namespaces) {
18754
18692
  const nsDef = manifest.namespaces.find((n) => n.name === namespace);
18755
18693
  if (nsDef?.schema) {
18756
- const schema = this.schemaValidator.loadSchema(path26.join(repoRoot, nsDef.schema));
18694
+ const schema = this.schemaValidator.loadSchema(path24.join(repoRoot, nsDef.schema));
18757
18695
  plan.set(namespace, Object.keys(schema.keys));
18758
18696
  continue;
18759
18697
  }
@@ -18832,15 +18770,15 @@ function withBackendOverride(manifest, envNames, backend, key) {
18832
18770
  }
18833
18771
 
18834
18772
  // src/sync/manager.ts
18835
- var path27 = __toESM(require("path"));
18773
+ var path25 = __toESM(require("path"));
18836
18774
  var SyncManager = class {
18837
- constructor(matrixManager, encryption, tx) {
18775
+ constructor(matrixManager, source, tx) {
18838
18776
  this.matrixManager = matrixManager;
18839
- this.encryption = encryption;
18777
+ this.source = source;
18840
18778
  this.tx = tx;
18841
18779
  }
18842
18780
  matrixManager;
18843
- encryption;
18781
+ source;
18844
18782
  tx;
18845
18783
  /**
18846
18784
  * Compute what sync would do without mutating anything.
@@ -18857,8 +18795,13 @@ var SyncManager = class {
18857
18795
  const targetCells = opts.namespace ? existingCells.filter((c) => c.namespace === opts.namespace) : existingCells;
18858
18796
  const keysByNsEnv = {};
18859
18797
  for (const cell of targetCells) {
18860
- const keys = readSopsKeyNames(cell.filePath);
18861
- if (!keys) continue;
18798
+ const ref = { namespace: cell.namespace, environment: cell.environment };
18799
+ let keys;
18800
+ try {
18801
+ keys = await this.source.listKeys(ref);
18802
+ } catch {
18803
+ continue;
18804
+ }
18862
18805
  if (!keysByNsEnv[cell.namespace]) keysByNsEnv[cell.namespace] = {};
18863
18806
  keysByNsEnv[cell.namespace][cell.environment] = new Set(keys);
18864
18807
  }
@@ -18904,7 +18847,7 @@ var SyncManager = class {
18904
18847
  }
18905
18848
  const txPaths = [];
18906
18849
  for (const cell of syncPlan.cells) {
18907
- const rel = path27.relative(repoRoot, cell.filePath);
18850
+ const rel = path25.relative(repoRoot, cell.filePath);
18908
18851
  txPaths.push(rel);
18909
18852
  txPaths.push(rel.replace(/\.enc\.(yaml|json)$/, ".clef-meta.yaml"));
18910
18853
  }
@@ -18917,17 +18860,13 @@ var SyncManager = class {
18917
18860
  paths: txPaths,
18918
18861
  mutate: async () => {
18919
18862
  for (const cell of syncPlan.cells) {
18920
- const decrypted = await this.encryption.decrypt(cell.filePath);
18863
+ const ref = { namespace: cell.namespace, environment: cell.environment };
18864
+ const decrypted = await this.source.readCell(ref);
18921
18865
  for (const key of cell.missingKeys) {
18922
18866
  decrypted.values[key] = generateRandomValue();
18923
18867
  }
18924
- await this.encryption.encrypt(
18925
- cell.filePath,
18926
- decrypted.values,
18927
- manifest,
18928
- cell.environment
18929
- );
18930
- await markPendingWithRetry(cell.filePath, cell.missingKeys, "clef sync");
18868
+ await this.source.writeCell(ref, decrypted.values);
18869
+ await this.source.markPending(ref, cell.missingKeys, "clef sync");
18931
18870
  const cellLabel = `${cell.namespace}/${cell.environment}`;
18932
18871
  modifiedCells.push(cellLabel);
18933
18872
  scaffoldedKeys[cellLabel] = cell.missingKeys;
@@ -19187,13 +19126,272 @@ var ComplianceGenerator = class {
19187
19126
  };
19188
19127
 
19189
19128
  // src/compliance/run.ts
19190
- var path28 = __toESM(require("path"));
19129
+ var path27 = __toESM(require("path"));
19130
+
19131
+ // src/source/compose.ts
19132
+ var YAML14 = __toESM(require("yaml"));
19133
+
19134
+ // src/source/default-bulk.ts
19135
+ function defaultBulk(source) {
19136
+ return {
19137
+ async bulkSet(namespace, key, valuesByEnv, _manifest) {
19138
+ for (const [environment, value] of Object.entries(valuesByEnv)) {
19139
+ const cell = { namespace, environment };
19140
+ const existing = await source.cellExists(cell) ? (await source.readCell(cell)).values : {};
19141
+ await source.writeCell(cell, { ...existing, [key]: value });
19142
+ }
19143
+ },
19144
+ async bulkDelete(namespace, key, manifest) {
19145
+ for (const env of manifest.environments) {
19146
+ const cell = { namespace, environment: env.name };
19147
+ if (!await source.cellExists(cell)) continue;
19148
+ const data = await source.readCell(cell);
19149
+ if (!(key in data.values)) continue;
19150
+ const next = { ...data.values };
19151
+ delete next[key];
19152
+ await source.writeCell(cell, next);
19153
+ }
19154
+ },
19155
+ async copyValue(key, from, to, _manifest) {
19156
+ const src = await source.readCell(from);
19157
+ if (!(key in src.values)) {
19158
+ throw new Error(
19159
+ `Cannot copy: key '${key}' not present in ${from.namespace}/${from.environment}`
19160
+ );
19161
+ }
19162
+ const dst = await source.cellExists(to) ? (await source.readCell(to)).values : {};
19163
+ await source.writeCell(to, { ...dst, [key]: src.values[key] });
19164
+ }
19165
+ };
19166
+ }
19167
+
19168
+ // src/source/compose.ts
19169
+ function composeSecretSource(storage, encryption, manifest) {
19170
+ return new ComposedSecretSource(storage, encryption, manifest);
19171
+ }
19172
+ var ComposedSecretSource = class {
19173
+ constructor(storage, encryption, manifest) {
19174
+ this.storage = storage;
19175
+ this.encryption = encryption;
19176
+ this.manifest = manifest;
19177
+ this.id = `${storage.id}+${encryption.id}`;
19178
+ this.description = `${storage.description} / ${encryption.description}`;
19179
+ }
19180
+ storage;
19181
+ encryption;
19182
+ manifest;
19183
+ id;
19184
+ description;
19185
+ context(cell) {
19186
+ return {
19187
+ manifest: this.manifest,
19188
+ environment: cell.environment,
19189
+ format: this.storage.blobFormat(cell)
19190
+ };
19191
+ }
19192
+ // ── Core SecretSource ──────────────────────────────────────────────────
19193
+ async readCell(cell) {
19194
+ const blob = await this.storage.readBlob(cell);
19195
+ return this.encryption.decrypt(blob, this.context(cell));
19196
+ }
19197
+ async writeCell(cell, values) {
19198
+ const blob = await this.encryption.encrypt(values, this.context(cell));
19199
+ await this.storage.writeBlob(cell, blob);
19200
+ }
19201
+ async deleteCell(cell) {
19202
+ await this.storage.deleteBlob(cell);
19203
+ }
19204
+ async cellExists(cell) {
19205
+ return this.storage.blobExists(cell);
19206
+ }
19207
+ /**
19208
+ * List cell keys WITHOUT decrypting. SOPS files store key names in
19209
+ * plaintext at the top level of the YAML/JSON document — we read the
19210
+ * blob and return everything except the `sops:` metadata block.
19211
+ *
19212
+ * NOTE: this is currently SOPS-shaped. A future non-SOPS
19213
+ * `EncryptionBackend` whose ciphertext doesn't expose key names in
19214
+ * the clear would need its own listing strategy — likely a
19215
+ * `listKeys(blob)` method on `EncryptionBackend`. Deferred until a
19216
+ * second backend exists.
19217
+ */
19218
+ async listKeys(cell) {
19219
+ if (!await this.storage.blobExists(cell)) return [];
19220
+ const blob = await this.storage.readBlob(cell);
19221
+ const parsed = YAML14.parse(blob);
19222
+ if (!parsed || typeof parsed !== "object") return [];
19223
+ return Object.keys(parsed).filter((k) => k !== "sops");
19224
+ }
19225
+ async getCellMetadata(cell) {
19226
+ const blob = await this.storage.readBlob(cell);
19227
+ return this.encryption.getMetadata(blob);
19228
+ }
19229
+ async scaffoldCell(cell, manifest) {
19230
+ if (await this.storage.blobExists(cell)) return;
19231
+ const blob = await this.encryption.encrypt(
19232
+ {},
19233
+ {
19234
+ manifest,
19235
+ environment: cell.environment,
19236
+ format: this.storage.blobFormat(cell)
19237
+ }
19238
+ );
19239
+ await this.storage.writeBlob(cell, blob);
19240
+ }
19241
+ // ── Pending / rotation metadata ────────────────────────────────────────
19242
+ async getPendingMetadata(cell) {
19243
+ return this.storage.readPendingMetadata(cell);
19244
+ }
19245
+ async markPending(cell, keys, setBy) {
19246
+ const meta = await this.storage.readPendingMetadata(cell);
19247
+ const now = /* @__PURE__ */ new Date();
19248
+ for (const key of keys) {
19249
+ if (!meta.pending.find((p) => p.key === key)) {
19250
+ meta.pending.push({ key, since: now, setBy });
19251
+ }
19252
+ }
19253
+ await this.storage.writePendingMetadata(cell, meta);
19254
+ }
19255
+ async markResolved(cell, keys) {
19256
+ const meta = await this.storage.readPendingMetadata(cell);
19257
+ meta.pending = meta.pending.filter((p) => !keys.includes(p.key));
19258
+ await this.storage.writePendingMetadata(cell, meta);
19259
+ }
19260
+ async recordRotation(cell, keys, rotatedBy) {
19261
+ const meta = await this.storage.readPendingMetadata(cell);
19262
+ const now = /* @__PURE__ */ new Date();
19263
+ for (const key of keys) {
19264
+ const existing = meta.rotations.find((r) => r.key === key);
19265
+ if (existing) {
19266
+ existing.lastRotatedAt = now;
19267
+ existing.rotatedBy = rotatedBy;
19268
+ existing.rotationCount += 1;
19269
+ } else {
19270
+ meta.rotations.push({ key, lastRotatedAt: now, rotatedBy, rotationCount: 1 });
19271
+ }
19272
+ }
19273
+ meta.pending = meta.pending.filter((p) => !keys.includes(p.key));
19274
+ await this.storage.writePendingMetadata(cell, meta);
19275
+ }
19276
+ async removeRotation(cell, keys) {
19277
+ const meta = await this.storage.readPendingMetadata(cell);
19278
+ meta.rotations = meta.rotations.filter((r) => !keys.includes(r.key));
19279
+ await this.storage.writePendingMetadata(cell, meta);
19280
+ }
19281
+ // ── Lintable ───────────────────────────────────────────────────────────
19282
+ async validateEncryption(cell) {
19283
+ if (!await this.storage.blobExists(cell)) return false;
19284
+ const blob = await this.storage.readBlob(cell);
19285
+ return this.encryption.validateEncryption(blob);
19286
+ }
19287
+ async checkRecipientDrift(cell, expected) {
19288
+ const blob = await this.storage.readBlob(cell);
19289
+ const meta = this.encryption.getMetadata(blob);
19290
+ const actual = new Set(meta.recipients);
19291
+ const expectedSet = new Set(expected);
19292
+ return {
19293
+ missing: expected.filter((r) => !actual.has(r)),
19294
+ unexpected: meta.recipients.filter((r) => !expectedSet.has(r))
19295
+ };
19296
+ }
19297
+ // ── Rotatable ──────────────────────────────────────────────────────────
19298
+ async rotate(cell, opts) {
19299
+ const blob = await this.storage.readBlob(cell);
19300
+ const rotated = await this.encryption.rotate(blob, opts, this.context(cell));
19301
+ await this.storage.writeBlob(cell, rotated);
19302
+ }
19303
+ // ── Bulk ───────────────────────────────────────────────────────────────
19304
+ //
19305
+ // Default looped implementation. A future StorageBackend that supports
19306
+ // batch operations (e.g. PostgresStorageBackend with row-level UPDATE
19307
+ // batching) can override these by wrapping `composeSecretSource`'s
19308
+ // output and replacing just the bulk methods.
19309
+ bulkSet = (namespace, key, valuesByEnv, manifest) => defaultBulk(this).bulkSet(namespace, key, valuesByEnv, manifest);
19310
+ bulkDelete = (namespace, key, manifest) => defaultBulk(this).bulkDelete(namespace, key, manifest);
19311
+ copyValue = (key, from, to, manifest) => defaultBulk(this).copyValue(key, from, to, manifest);
19312
+ };
19313
+
19314
+ // src/source/filesystem-storage-backend.ts
19315
+ var fs22 = __toESM(require("fs"));
19316
+ var path26 = __toESM(require("path"));
19317
+ var import_crypto3 = require("crypto");
19318
+ var FilesystemStorageBackend = class {
19319
+ constructor(manifest, repoRoot) {
19320
+ this.manifest = manifest;
19321
+ this.repoRoot = repoRoot;
19322
+ }
19323
+ manifest;
19324
+ repoRoot;
19325
+ id = "filesystem";
19326
+ description = "Filesystem-backed cell storage (default substrate)";
19327
+ /**
19328
+ * Resolve a cell reference to its absolute filesystem path. Public —
19329
+ * used by substrate-specific trait implementations.
19330
+ */
19331
+ cellPath(cell) {
19332
+ const relativePath = this.manifest.file_pattern.replace("{namespace}", cell.namespace).replace("{environment}", cell.environment);
19333
+ return path26.join(this.repoRoot, relativePath);
19334
+ }
19335
+ /** The repo root, exposed for filesystem-shaped trait implementations. */
19336
+ getRepoRoot() {
19337
+ return this.repoRoot;
19338
+ }
19339
+ blobFormat(cell) {
19340
+ return this.cellPath(cell).endsWith(".json") ? "json" : "yaml";
19341
+ }
19342
+ async readBlob(cell) {
19343
+ const filePath = this.cellPath(cell);
19344
+ return fs22.readFileSync(filePath, "utf-8");
19345
+ }
19346
+ async writeBlob(cell, blob) {
19347
+ const filePath = this.cellPath(cell);
19348
+ const dir = path26.dirname(filePath);
19349
+ if (!fs22.existsSync(dir)) {
19350
+ fs22.mkdirSync(dir, { recursive: true });
19351
+ }
19352
+ const tmpPath = `${filePath}.${Date.now()}.${(0, import_crypto3.randomBytes)(4).toString("hex")}.tmp`;
19353
+ const handle = fs22.openSync(tmpPath, "w");
19354
+ try {
19355
+ fs22.writeFileSync(handle, blob);
19356
+ fs22.fsyncSync(handle);
19357
+ } finally {
19358
+ fs22.closeSync(handle);
19359
+ }
19360
+ fs22.renameSync(tmpPath, filePath);
19361
+ }
19362
+ async deleteBlob(cell) {
19363
+ const filePath = this.cellPath(cell);
19364
+ if (fs22.existsSync(filePath)) {
19365
+ fs22.unlinkSync(filePath);
19366
+ }
19367
+ const sidecar = this.sidecarPath(filePath);
19368
+ if (fs22.existsSync(sidecar)) {
19369
+ fs22.unlinkSync(sidecar);
19370
+ }
19371
+ }
19372
+ async blobExists(cell) {
19373
+ return fs22.existsSync(this.cellPath(cell));
19374
+ }
19375
+ async readPendingMetadata(cell) {
19376
+ return loadMetadata(this.cellPath(cell));
19377
+ }
19378
+ async writePendingMetadata(cell, meta) {
19379
+ await saveMetadata(this.cellPath(cell), meta);
19380
+ }
19381
+ sidecarPath(filePath) {
19382
+ const dir = path26.dirname(filePath);
19383
+ const base = path26.basename(filePath).replace(/\.enc\.(yaml|json)$/, "");
19384
+ return path26.join(dir, `${base}.clef-meta.yaml`);
19385
+ }
19386
+ };
19387
+
19388
+ // src/compliance/run.ts
19191
19389
  var UNKNOWN = "unknown";
19192
19390
  async function runCompliance(opts) {
19193
19391
  const start = Date.now();
19194
19392
  const repoRoot = opts.repoRoot ?? process.cwd();
19195
- const manifestPath = opts.manifestPath ?? path28.join(repoRoot, "clef.yaml");
19196
- const policyPath = opts.policyPath ?? path28.join(repoRoot, CLEF_POLICY_FILENAME);
19393
+ const manifestPath = opts.manifestPath ?? path27.join(repoRoot, "clef.yaml");
19394
+ const policyPath = opts.policyPath ?? path27.join(repoRoot, CLEF_POLICY_FILENAME);
19197
19395
  const include = {
19198
19396
  scan: opts.include?.scan ?? true,
19199
19397
  lint: opts.include?.lint ?? true,
@@ -19205,6 +19403,11 @@ async function runCompliance(opts) {
19205
19403
  const sopsClient = new SopsClient(opts.runner, opts.ageKeyFile, opts.ageKey, opts.sopsPath);
19206
19404
  const matrixManager = new MatrixManager();
19207
19405
  const schemaValidator = new SchemaValidator();
19406
+ const lintSource = composeSecretSource(
19407
+ new FilesystemStorageBackend(manifest, repoRoot),
19408
+ sopsClient,
19409
+ manifest
19410
+ );
19208
19411
  const [sha, repo, files, scanResult, lintResult] = await Promise.all([
19209
19412
  opts.sha !== void 0 ? Promise.resolve(opts.sha) : detectSha(opts.runner, repoRoot),
19210
19413
  opts.repo !== void 0 ? Promise.resolve(opts.repo) : detectRepo(opts.runner, repoRoot),
@@ -19213,12 +19416,12 @@ async function runCompliance(opts) {
19213
19416
  repoRoot,
19214
19417
  policy,
19215
19418
  matrixManager,
19216
- sopsClient,
19419
+ source: lintSource,
19217
19420
  filter: opts.filter,
19218
19421
  now
19219
19422
  }) : Promise.resolve([]),
19220
19423
  include.scan ? new ScanRunner(opts.runner).scan(repoRoot, manifest) : Promise.resolve(emptyScan()),
19221
- include.lint ? new LintRunner(matrixManager, schemaValidator, sopsClient).run(manifest, repoRoot) : Promise.resolve(emptyLint())
19424
+ include.lint ? new LintRunner(matrixManager, schemaValidator, lintSource).run(manifest, repoRoot) : Promise.resolve(emptyLint())
19222
19425
  ]);
19223
19426
  const adjustedLint = downgradeDecryptIssues(lintResult);
19224
19427
  const document = new ComplianceGenerator().generate({
@@ -19238,8 +19441,11 @@ async function evaluateMatrix(args) {
19238
19441
  const cells = args.matrixManager.resolveMatrix(args.manifest, args.repoRoot).filter((c) => applyFilter(c.namespace, c.environment, args.filter)).filter((c) => c.exists);
19239
19442
  return Promise.all(
19240
19443
  cells.map(async (cell) => {
19241
- const metadata = await args.sopsClient.getMetadata(cell.filePath);
19242
- const relPath = path28.relative(args.repoRoot, cell.filePath).replace(/\\/g, "/");
19444
+ const metadata = await args.source.getCellMetadata({
19445
+ namespace: cell.namespace,
19446
+ environment: cell.environment
19447
+ });
19448
+ const relPath = path27.relative(args.repoRoot, cell.filePath).replace(/\\/g, "/");
19243
19449
  const keys = readSopsKeyNames(cell.filePath) ?? [];
19244
19450
  const rotations = await getRotations(cell.filePath);
19245
19451
  return evaluator.evaluateFile(relPath, cell.environment, metadata, keys, rotations, args.now);
@@ -19297,11 +19503,62 @@ async function detectRepo(runner, repoRoot) {
19297
19503
  const match = url.match(/[:/]([^/:]+)\/([^/]+?)(?:\.git)?\/?$/);
19298
19504
  return match ? `${match[1]}/${match[2]}` : UNKNOWN;
19299
19505
  }
19506
+
19507
+ // src/source/guards.ts
19508
+ function isFn(o, name) {
19509
+ return typeof o === "object" && o !== null && typeof o[name] === "function";
19510
+ }
19511
+ function isLintable(s) {
19512
+ return isFn(s, "validateEncryption") && isFn(s, "checkRecipientDrift");
19513
+ }
19514
+ function isRotatable(s) {
19515
+ return isFn(s, "rotate");
19516
+ }
19517
+ function isRecipientManaged(s) {
19518
+ return isFn(s, "listRecipients") && isFn(s, "addRecipient") && isFn(s, "removeRecipient");
19519
+ }
19520
+ function isMergeAware(s) {
19521
+ return isFn(s, "mergeCells") && isFn(s, "installMergeDriver");
19522
+ }
19523
+ function isMigratable(s) {
19524
+ return isFn(s, "migrateBackend");
19525
+ }
19526
+ function isBulk(s) {
19527
+ return isFn(s, "bulkSet") && isFn(s, "bulkDelete") && isFn(s, "copyValue");
19528
+ }
19529
+ function isStructural(s) {
19530
+ return isFn(s, "addNamespace") && isFn(s, "addEnvironment") && isFn(s, "renameNamespace") && isFn(s, "renameEnvironment");
19531
+ }
19532
+ function describeCapabilities(s) {
19533
+ return {
19534
+ lint: isLintable(s),
19535
+ rotate: isRotatable(s),
19536
+ recipients: isRecipientManaged(s),
19537
+ merge: isMergeAware(s),
19538
+ migrate: isMigratable(s),
19539
+ bulk: isBulk(s),
19540
+ structural: isStructural(s)
19541
+ };
19542
+ }
19543
+
19544
+ // src/source/errors.ts
19545
+ var SourceCapabilityUnsupportedError = class extends ClefError {
19546
+ constructor(capability, sourceId) {
19547
+ super(
19548
+ `'${capability}' is not supported by the '${sourceId}' source.`,
19549
+ `Switch to a source that implements ${capability}, or use a different command.`
19550
+ );
19551
+ this.capability = capability;
19552
+ this.sourceId = sourceId;
19553
+ this.name = "SourceCapabilityUnsupportedError";
19554
+ }
19555
+ capability;
19556
+ sourceId;
19557
+ };
19300
19558
  // Annotate the CommonJS export names for ESM import in node:
19301
19559
  0 && (module.exports = {
19302
19560
  ArtifactPacker,
19303
19561
  BackendMigrator,
19304
- BulkOps,
19305
19562
  CLEF_MANIFEST_FILENAME,
19306
19563
  CLEF_POLICY_FILENAME,
19307
19564
  CLEF_REPORT_SCHEMA_VERSION,
@@ -19315,6 +19572,7 @@ async function detectRepo(runner, repoRoot) {
19315
19572
  DiffEngine,
19316
19573
  DriftDetector,
19317
19574
  FilePackOutput,
19575
+ FilesystemStorageBackend,
19318
19576
  GitIntegration,
19319
19577
  GitOperationError,
19320
19578
  ImportRunner,
@@ -19331,7 +19589,6 @@ async function detectRepo(runner, repoRoot) {
19331
19589
  PolicyValidationError,
19332
19590
  REQUESTS_FILENAME,
19333
19591
  REQUIREMENTS,
19334
- REVEAL_WARNING,
19335
19592
  RecipientManager,
19336
19593
  ReportGenerator,
19337
19594
  ReportSanitizer,
@@ -19348,6 +19605,7 @@ async function detectRepo(runner, repoRoot) {
19348
19605
  SopsMergeDriver,
19349
19606
  SopsMissingError,
19350
19607
  SopsVersionError,
19608
+ SourceCapabilityUnsupportedError,
19351
19609
  StructureManager,
19352
19610
  SyncManager,
19353
19611
  TransactionLockError,
@@ -19367,11 +19625,11 @@ async function detectRepo(runner, repoRoot) {
19367
19625
  checkAll,
19368
19626
  checkDependency,
19369
19627
  collectCIContext,
19628
+ composeSecretSource,
19370
19629
  computeCiphertextHash,
19371
19630
  deriveAgePublicKey,
19631
+ describeCapabilities,
19372
19632
  describeScope,
19373
- detectAlgorithm,
19374
- detectFormat,
19375
19633
  emptyTemplate,
19376
19634
  exampleTemplate,
19377
19635
  findRequest,
@@ -19379,29 +19637,24 @@ async function detectRepo(runner, repoRoot) {
19379
19637
  formatRevealWarning,
19380
19638
  generateAgeIdentity,
19381
19639
  generateRandomValue,
19382
- generateSigningKeyPair,
19383
- getPendingKeys,
19384
- getRotations,
19640
+ isBulk,
19385
19641
  isClefHsmArn,
19386
19642
  isHighEntropy,
19387
19643
  isKmsEnvelope,
19644
+ isLintable,
19645
+ isMergeAware,
19646
+ isMigratable,
19388
19647
  isPackedArtifact,
19389
- isPending,
19648
+ isRecipientManaged,
19649
+ isRotatable,
19650
+ isStructural,
19390
19651
  keyPreview,
19391
- loadIgnoreRules,
19392
- loadMetadata,
19393
19652
  loadRequests,
19394
19653
  markPending,
19395
- markPendingWithRetry,
19396
19654
  markResolved,
19397
19655
  matchPatterns,
19398
- mergeMetadataContents,
19399
19656
  mergeMetadataFiles,
19400
- metadataPath,
19401
19657
  parse,
19402
- parseDotenv,
19403
- parseIgnoreContent,
19404
- parseJson,
19405
19658
  parseSignerKey,
19406
19659
  parseYaml,
19407
19660
  pkcs11UriToSyntheticArn,
@@ -19419,14 +19672,8 @@ async function detectRepo(runner, repoRoot) {
19419
19672
  resolveRecipientsForEnvironment,
19420
19673
  resolveSopsPath,
19421
19674
  runCompliance,
19422
- saveMetadata,
19423
19675
  saveRequests,
19424
- serializeSchema,
19425
- shannonEntropy,
19426
- shouldIgnoreFile,
19427
- shouldIgnoreMatch,
19428
- signEd25519,
19429
- signKms,
19676
+ shouldUseLinuxStdinFifo,
19430
19677
  spawnKeyservice,
19431
19678
  syntheticArnToPkcs11Uri,
19432
19679
  tryBundledKeyservice,
@@ -19436,6 +19683,7 @@ async function detectRepo(runner, repoRoot) {
19436
19683
  validatePackedArtifact,
19437
19684
  validateResetScope,
19438
19685
  verifySignature,
19686
+ wrapWithLinuxStdinFifo,
19439
19687
  writeManifestYaml,
19440
19688
  writeManifestYamlRaw,
19441
19689
  writeSchema,