@hawkeyexl/manni 4.4.1 → 4.6.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 (226) hide show
  1. package/README.md +1 -1
  2. package/dist/batch-OZOACN4G.js +41 -0
  3. package/dist/build-Z5VOVADH.js +32 -0
  4. package/dist/calibrate-TZYT42DR.js +36 -0
  5. package/dist/capture-SL4MNDDP.js +34 -0
  6. package/dist/check-5OMA42Y6.js +26 -0
  7. package/dist/{check-HYNGXYJV.js → check-OAJWC3WD.js} +7 -5
  8. package/dist/{check-ASPIHA6B.js → check-PKY7KTQE.js} +13 -14
  9. package/dist/check-WB2QDHJQ.js +880 -0
  10. package/dist/check-WB2QDHJQ.js.map +1 -0
  11. package/dist/{chunk-MWJLZMBL.js → chunk-2JFPBZJK.js} +11 -11
  12. package/dist/{chunk-QQUKGCAJ.js → chunk-2KRB3EG7.js} +2 -2
  13. package/dist/{chunk-DXNZS72W.js → chunk-2M6DVNI5.js} +10 -6
  14. package/dist/{chunk-DXNZS72W.js.map → chunk-2M6DVNI5.js.map} +1 -1
  15. package/dist/{chunk-QZ6LRSNV.js → chunk-3NWX34ZI.js} +19 -19
  16. package/dist/{chunk-IDV6HJCB.js → chunk-5G53XATW.js} +4 -4
  17. package/dist/{chunk-M2JCK5F6.js → chunk-6KKZXB2O.js} +19 -6
  18. package/dist/chunk-6KKZXB2O.js.map +1 -0
  19. package/dist/{chunk-PNPQTHBT.js → chunk-733Y2STG.js} +2 -2
  20. package/dist/{chunk-WB3QM6PP.js → chunk-73TC4DYH.js} +332 -2522
  21. package/dist/chunk-73TC4DYH.js.map +1 -0
  22. package/dist/{chunk-EPH54CAY.js → chunk-7WRP5FFP.js} +4 -4
  23. package/dist/{chunk-NXV7WE2D.js → chunk-APKH2T6E.js} +29 -591
  24. package/dist/chunk-APKH2T6E.js.map +1 -0
  25. package/dist/{chunk-I2AH3CJJ.js → chunk-B3TS4WRM.js} +17 -15
  26. package/dist/{chunk-I2AH3CJJ.js.map → chunk-B3TS4WRM.js.map} +1 -1
  27. package/dist/chunk-BWCWTHYS.js +146 -0
  28. package/dist/chunk-BWCWTHYS.js.map +1 -0
  29. package/dist/{chunk-MMTSSTG7.js → chunk-C3B2VJNC.js} +2 -2
  30. package/dist/{chunk-PU6DFJMY.js → chunk-C73RSUIC.js} +4 -4
  31. package/dist/chunk-CU57HT2E.js +116 -0
  32. package/dist/chunk-CU57HT2E.js.map +1 -0
  33. package/dist/chunk-CWXZ2MRT.js +222 -0
  34. package/dist/chunk-CWXZ2MRT.js.map +1 -0
  35. package/dist/{chunk-GCQRPUSB.js → chunk-DC5DDJVE.js} +6 -6
  36. package/dist/{chunk-6OYEI2VV.js → chunk-ECVQNGTH.js} +3 -3
  37. package/dist/chunk-FLAO4ZS3.js +502 -0
  38. package/dist/chunk-FLAO4ZS3.js.map +1 -0
  39. package/dist/chunk-FQGLRJGX.js +174 -0
  40. package/dist/chunk-FQGLRJGX.js.map +1 -0
  41. package/dist/{chunk-7UKO3ASZ.js → chunk-GAJIJKET.js} +3 -3
  42. package/dist/{chunk-6JSSEWBG.js → chunk-GREQKO6M.js} +202 -54
  43. package/dist/chunk-GREQKO6M.js.map +1 -0
  44. package/dist/chunk-HX44GB4Z.js +30 -0
  45. package/dist/chunk-HX44GB4Z.js.map +1 -0
  46. package/dist/{chunk-4IUC2MNL.js → chunk-IY7NYNPZ.js} +9 -11
  47. package/dist/{chunk-4IUC2MNL.js.map → chunk-IY7NYNPZ.js.map} +1 -1
  48. package/dist/{chunk-57NEQRVO.js → chunk-KCOFEWV5.js} +8 -6
  49. package/dist/{chunk-57NEQRVO.js.map → chunk-KCOFEWV5.js.map} +1 -1
  50. package/dist/chunk-M2RLHZ4J.js +849 -0
  51. package/dist/chunk-M2RLHZ4J.js.map +1 -0
  52. package/dist/{chunk-NLXTXE5Y.js → chunk-M6GSS72T.js} +185 -33
  53. package/dist/chunk-M6GSS72T.js.map +1 -0
  54. package/dist/{chunk-YTURO7SI.js → chunk-MHM4QEBV.js} +4 -4
  55. package/dist/{chunk-RL5XWQOL.js → chunk-MOWMCEOJ.js} +143 -54
  56. package/dist/chunk-MOWMCEOJ.js.map +1 -0
  57. package/dist/{chunk-C4SQM22E.js → chunk-MWBJ3KTF.js} +13 -11
  58. package/dist/chunk-MWBJ3KTF.js.map +1 -0
  59. package/dist/chunk-N7JSEDSC.js +233 -0
  60. package/dist/chunk-N7JSEDSC.js.map +1 -0
  61. package/dist/{chunk-JCDXE76J.js → chunk-NBMPHD4I.js} +2 -2
  62. package/dist/{chunk-37LP5HMZ.js → chunk-NHHOPZRA.js} +12 -10
  63. package/dist/{chunk-37LP5HMZ.js.map → chunk-NHHOPZRA.js.map} +1 -1
  64. package/dist/{chunk-YB4IBW3M.js → chunk-NTWW7J76.js} +2 -2
  65. package/dist/{chunk-UJCVAWP7.js → chunk-PCFG7E6B.js} +3 -3
  66. package/dist/{chunk-UJCVAWP7.js.map → chunk-PCFG7E6B.js.map} +1 -1
  67. package/dist/{chunk-FBEQCK47.js → chunk-PL4UAF4S.js} +73 -1304
  68. package/dist/chunk-PL4UAF4S.js.map +1 -0
  69. package/dist/chunk-QCHVR4ZM.js +286 -0
  70. package/dist/chunk-QCHVR4ZM.js.map +1 -0
  71. package/dist/{chunk-PIKOVN6W.js → chunk-QW77LCOU.js} +10 -10
  72. package/dist/{chunk-2B7ZNTPV.js → chunk-SM6NOMJS.js} +2 -2
  73. package/dist/chunk-SWSTHZBI.js +2464 -0
  74. package/dist/chunk-SWSTHZBI.js.map +1 -0
  75. package/dist/{chunk-JXLTXMJ7.js → chunk-T6SYGLWR.js} +3 -3
  76. package/dist/{chunk-CK7SKVHR.js → chunk-TBAMBLF3.js} +3 -12
  77. package/dist/chunk-TBAMBLF3.js.map +1 -0
  78. package/dist/chunk-TH4JVQPF.js +133 -0
  79. package/dist/chunk-TH4JVQPF.js.map +1 -0
  80. package/dist/{chunk-QRWGPQKO.js → chunk-TLXE7LYK.js} +4 -4
  81. package/dist/{chunk-M3W7J6MU.js → chunk-TUAAR3MM.js} +3280 -283
  82. package/dist/chunk-TUAAR3MM.js.map +1 -0
  83. package/dist/{chunk-SKSDRUUK.js → chunk-UAKNZVCD.js} +4 -4
  84. package/dist/{chunk-YJOGUXGP.js → chunk-UJSGS2Y5.js} +8 -6
  85. package/dist/{chunk-YJOGUXGP.js.map → chunk-UJSGS2Y5.js.map} +1 -1
  86. package/dist/{chunk-UQISLDAV.js → chunk-UWQ2UVHE.js} +8 -6
  87. package/dist/{chunk-UQISLDAV.js.map → chunk-UWQ2UVHE.js.map} +1 -1
  88. package/dist/chunk-WQWKYAAK.js +86 -0
  89. package/dist/chunk-WQWKYAAK.js.map +1 -0
  90. package/dist/{chunk-LJ2RDH3F.js → chunk-XG6PVB5S.js} +1 -23
  91. package/dist/chunk-XG6PVB5S.js.map +1 -0
  92. package/dist/{chunk-Q35YIDBV.js → chunk-XKBEHKWO.js} +29 -264
  93. package/dist/chunk-XKBEHKWO.js.map +1 -0
  94. package/dist/chunk-XPEWUK7J.js +532 -0
  95. package/dist/chunk-XPEWUK7J.js.map +1 -0
  96. package/dist/{chunk-MYMBCTDE.js → chunk-XXDXIIQ4.js} +2 -2
  97. package/dist/chunk-YGFIQ6Y5.js +17 -0
  98. package/dist/chunk-YGFIQ6Y5.js.map +1 -0
  99. package/dist/chunk-YLK2J5YK.js +610 -0
  100. package/dist/chunk-YLK2J5YK.js.map +1 -0
  101. package/dist/{chunk-3VK37OR2.js → chunk-Z4IZGEM3.js} +16 -12
  102. package/dist/{chunk-3VK37OR2.js.map → chunk-Z4IZGEM3.js.map} +1 -1
  103. package/dist/{cli-6HIDIFVT.js → cli-6EWVMEN6.js} +5 -3
  104. package/dist/{cli-6HIDIFVT.js.map → cli-6EWVMEN6.js.map} +1 -1
  105. package/dist/{cli-VI7VHWNZ.js → cli-6H7HVDMQ.js} +113 -20
  106. package/dist/{cli-VI7VHWNZ.js.map → cli-6H7HVDMQ.js.map} +1 -1
  107. package/dist/{cli-RPM5PELX.js → cli-EPVZHAZS.js} +24 -20
  108. package/dist/cli-EPVZHAZS.js.map +1 -0
  109. package/dist/{cli-MN7PYMBI.js → cli-LUCSGTVI.js} +17 -19
  110. package/dist/{cli-MN7PYMBI.js.map → cli-LUCSGTVI.js.map} +1 -1
  111. package/dist/{cli-LFP5ZGYS.js → cli-NIG4N3XM.js} +16 -15
  112. package/dist/{cli-LFP5ZGYS.js.map → cli-NIG4N3XM.js.map} +1 -1
  113. package/dist/{cli-PYPX6O6E.js → cli-PSKNB66T.js} +225 -58
  114. package/dist/cli-PSKNB66T.js.map +1 -0
  115. package/dist/{cli-3BIBYHFK.js → cli-UO4NHIB3.js} +17 -17
  116. package/dist/{cli-6UB5N6CU.js → cli-UQ7A3YA2.js} +17 -18
  117. package/dist/{cli-6UB5N6CU.js.map → cli-UQ7A3YA2.js.map} +1 -1
  118. package/dist/{cli-673OTRGE.js → cli-YHYDLQAF.js} +10 -10
  119. package/dist/cli.js +11 -11
  120. package/dist/config-P4HDNTYD.js +26 -0
  121. package/dist/conformance-ZFCMKM6M.js +18 -0
  122. package/dist/docmeta.js +9 -9
  123. package/dist/github-7ZCH5PSO.js +26 -0
  124. package/dist/github-POT6724K.js +31 -0
  125. package/dist/host-6GURH5HE.js +23 -0
  126. package/dist/index.d.ts +102 -12
  127. package/dist/index.js +185 -171
  128. package/dist/index.js.map +1 -1
  129. package/dist/{json-SY7KLNC7.js → json-KT2K62L5.js} +12 -13
  130. package/dist/lint-5AQ5C2BV.js +29 -0
  131. package/dist/list-5JZKX2NK.js +28 -0
  132. package/dist/list-SWPFXQMU.js +30 -0
  133. package/dist/{page-KSGV5RD4.js → page-UPSXRP75.js} +11 -12
  134. package/dist/prepare-6GNEWCNH.js +155 -0
  135. package/dist/prepare-6GNEWCNH.js.map +1 -0
  136. package/dist/{pretty-CXSDXNAS.js → pretty-Q6FQAOK4.js} +13 -14
  137. package/dist/{pretty-A5EGTN35.js → pretty-SQRPFPSQ.js} +12 -13
  138. package/dist/provider-AMGRDY6V.js +34 -0
  139. package/dist/release-IECYCLTD.js +36 -0
  140. package/dist/release-IECYCLTD.js.map +1 -0
  141. package/dist/{reporters-HUY7UXFS.js → reporters-45MMC2ID.js} +13 -13
  142. package/dist/{reporters-K2VUKSAK.js → reporters-XUV4MM64.js} +7 -5
  143. package/dist/reporters-XUV4MM64.js.map +1 -0
  144. package/dist/run-KMWY2IAN.js +32 -0
  145. package/dist/run-KMWY2IAN.js.map +1 -0
  146. package/dist/{term-BIQUPMOR.js → term-PCJUCWNF.js} +16 -17
  147. package/dist/{term-BIQUPMOR.js.map → term-PCJUCWNF.js.map} +1 -1
  148. package/dist/validate-IMNCZNWG.js +17 -0
  149. package/dist/validate-IMNCZNWG.js.map +1 -0
  150. package/package.json +2 -2
  151. package/plugin/manni/.claude-plugin/plugin.json +1 -1
  152. package/plugin/manni/hooks/hooks.json +47 -0
  153. package/dist/batch-GFNLTGOH.js +0 -36
  154. package/dist/build-COYIQ25L.js +0 -31
  155. package/dist/calibrate-MDBLJKEE.js +0 -31
  156. package/dist/capture-LRML2PKV.js +0 -32
  157. package/dist/check-EDLBYL5N.js +0 -27
  158. package/dist/chunk-6JSSEWBG.js.map +0 -1
  159. package/dist/chunk-A5ULMVHQ.js +0 -164
  160. package/dist/chunk-A5ULMVHQ.js.map +0 -1
  161. package/dist/chunk-C4SQM22E.js.map +0 -1
  162. package/dist/chunk-CK7SKVHR.js.map +0 -1
  163. package/dist/chunk-FBEQCK47.js.map +0 -1
  164. package/dist/chunk-LJ2RDH3F.js.map +0 -1
  165. package/dist/chunk-M2JCK5F6.js.map +0 -1
  166. package/dist/chunk-M3W7J6MU.js.map +0 -1
  167. package/dist/chunk-NLXTXE5Y.js.map +0 -1
  168. package/dist/chunk-NXV7WE2D.js.map +0 -1
  169. package/dist/chunk-Q35YIDBV.js.map +0 -1
  170. package/dist/chunk-RL5XWQOL.js.map +0 -1
  171. package/dist/chunk-TK3FC4RF.js +0 -3090
  172. package/dist/chunk-TK3FC4RF.js.map +0 -1
  173. package/dist/chunk-TU2WUBVZ.js +0 -2
  174. package/dist/chunk-WB3QM6PP.js.map +0 -1
  175. package/dist/cli-PYPX6O6E.js.map +0 -1
  176. package/dist/cli-RPM5PELX.js.map +0 -1
  177. package/dist/github-6QMYOHC3.js +0 -27
  178. package/dist/github-SXPGP5DB.js +0 -32
  179. package/dist/lint-ASMJLLPA.js +0 -28
  180. package/dist/list-KZNVTNVC.js +0 -31
  181. package/dist/list-Q57YTFUQ.js +0 -29
  182. package/dist/run-3DYJNRIG.js +0 -33
  183. package/dist/validate-53RKM2XG.js +0 -16
  184. /package/dist/{batch-GFNLTGOH.js.map → batch-OZOACN4G.js.map} +0 -0
  185. /package/dist/{build-COYIQ25L.js.map → build-Z5VOVADH.js.map} +0 -0
  186. /package/dist/{calibrate-MDBLJKEE.js.map → calibrate-TZYT42DR.js.map} +0 -0
  187. /package/dist/{capture-LRML2PKV.js.map → capture-SL4MNDDP.js.map} +0 -0
  188. /package/dist/{check-ASPIHA6B.js.map → check-5OMA42Y6.js.map} +0 -0
  189. /package/dist/{check-EDLBYL5N.js.map → check-OAJWC3WD.js.map} +0 -0
  190. /package/dist/{check-HYNGXYJV.js.map → check-PKY7KTQE.js.map} +0 -0
  191. /package/dist/{chunk-MWJLZMBL.js.map → chunk-2JFPBZJK.js.map} +0 -0
  192. /package/dist/{chunk-QQUKGCAJ.js.map → chunk-2KRB3EG7.js.map} +0 -0
  193. /package/dist/{chunk-QZ6LRSNV.js.map → chunk-3NWX34ZI.js.map} +0 -0
  194. /package/dist/{chunk-IDV6HJCB.js.map → chunk-5G53XATW.js.map} +0 -0
  195. /package/dist/{chunk-PNPQTHBT.js.map → chunk-733Y2STG.js.map} +0 -0
  196. /package/dist/{chunk-EPH54CAY.js.map → chunk-7WRP5FFP.js.map} +0 -0
  197. /package/dist/{chunk-MMTSSTG7.js.map → chunk-C3B2VJNC.js.map} +0 -0
  198. /package/dist/{chunk-PU6DFJMY.js.map → chunk-C73RSUIC.js.map} +0 -0
  199. /package/dist/{chunk-GCQRPUSB.js.map → chunk-DC5DDJVE.js.map} +0 -0
  200. /package/dist/{chunk-6OYEI2VV.js.map → chunk-ECVQNGTH.js.map} +0 -0
  201. /package/dist/{chunk-7UKO3ASZ.js.map → chunk-GAJIJKET.js.map} +0 -0
  202. /package/dist/{chunk-YTURO7SI.js.map → chunk-MHM4QEBV.js.map} +0 -0
  203. /package/dist/{chunk-JCDXE76J.js.map → chunk-NBMPHD4I.js.map} +0 -0
  204. /package/dist/{chunk-YB4IBW3M.js.map → chunk-NTWW7J76.js.map} +0 -0
  205. /package/dist/{chunk-PIKOVN6W.js.map → chunk-QW77LCOU.js.map} +0 -0
  206. /package/dist/{chunk-2B7ZNTPV.js.map → chunk-SM6NOMJS.js.map} +0 -0
  207. /package/dist/{chunk-JXLTXMJ7.js.map → chunk-T6SYGLWR.js.map} +0 -0
  208. /package/dist/{chunk-QRWGPQKO.js.map → chunk-TLXE7LYK.js.map} +0 -0
  209. /package/dist/{chunk-SKSDRUUK.js.map → chunk-UAKNZVCD.js.map} +0 -0
  210. /package/dist/{chunk-MYMBCTDE.js.map → chunk-XXDXIIQ4.js.map} +0 -0
  211. /package/dist/{cli-3BIBYHFK.js.map → cli-UO4NHIB3.js.map} +0 -0
  212. /package/dist/{chunk-TU2WUBVZ.js.map → cli-YHYDLQAF.js.map} +0 -0
  213. /package/dist/{cli-673OTRGE.js.map → config-P4HDNTYD.js.map} +0 -0
  214. /package/dist/{github-6QMYOHC3.js.map → conformance-ZFCMKM6M.js.map} +0 -0
  215. /package/dist/{github-SXPGP5DB.js.map → github-7ZCH5PSO.js.map} +0 -0
  216. /package/dist/{json-SY7KLNC7.js.map → github-POT6724K.js.map} +0 -0
  217. /package/dist/{lint-ASMJLLPA.js.map → host-6GURH5HE.js.map} +0 -0
  218. /package/dist/{list-KZNVTNVC.js.map → json-KT2K62L5.js.map} +0 -0
  219. /package/dist/{list-Q57YTFUQ.js.map → lint-5AQ5C2BV.js.map} +0 -0
  220. /package/dist/{page-KSGV5RD4.js.map → list-5JZKX2NK.js.map} +0 -0
  221. /package/dist/{pretty-A5EGTN35.js.map → list-SWPFXQMU.js.map} +0 -0
  222. /package/dist/{pretty-CXSDXNAS.js.map → page-UPSXRP75.js.map} +0 -0
  223. /package/dist/{reporters-HUY7UXFS.js.map → pretty-Q6FQAOK4.js.map} +0 -0
  224. /package/dist/{reporters-K2VUKSAK.js.map → pretty-SQRPFPSQ.js.map} +0 -0
  225. /package/dist/{run-3DYJNRIG.js.map → provider-AMGRDY6V.js.map} +0 -0
  226. /package/dist/{validate-53RKM2XG.js.map → reporters-45MMC2ID.js.map} +0 -0
@@ -1,19 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ CHECK_NAME,
4
+ DEFAULT_BASELINE_PATH,
3
5
  DERIVED_KEYWORD,
4
6
  DERIVED_STALE_SCHEMA,
7
+ DERIVE_SOURCES,
5
8
  DocmetaError,
6
9
  FILE_SCHEMA_KEY,
10
+ LEGACY_BASELINE_PATH,
7
11
  ManifestCache,
12
+ PROVENANCE_FIELD,
13
+ STAMPED_DATE_FIELDS,
8
14
  STDIN_LABEL,
9
15
  STDIN_TOKEN,
16
+ assertNonEmpty,
10
17
  blockLines,
18
+ canonicalSchemaRef,
11
19
  classifyRef,
12
20
  colOf,
13
21
  collectSchemaPins,
14
22
  colonEnd,
23
+ compareDerived,
15
24
  contentEnd,
16
25
  deepEqual,
26
+ derivableFields,
27
+ deriveCovers,
17
28
  detectEol,
18
29
  detectStyle,
19
30
  emptySource,
@@ -24,25 +35,34 @@ import {
24
35
  gitignoreOptions,
25
36
  insertKey,
26
37
  integrityOf,
38
+ isBuiltinField,
39
+ isErrorSeverity,
27
40
  keyLines,
28
41
  keyString,
29
42
  lineEnd,
30
43
  loadConfig,
31
44
  loadSchema,
32
45
  locateFrontmatter,
46
+ managedFields,
33
47
  positionForFactory,
34
48
  publishedBuiltins,
35
49
  rangeOf,
36
50
  rebaseConfigSchemaRefs,
37
51
  replaceBlockValue,
38
52
  replaceFlowValue,
53
+ resolveBaselineRequest,
39
54
  resolveElements,
55
+ resolveRunConfig,
40
56
  resolveSchemaSetWithSource,
41
57
  resolveTargetSet,
42
58
  schemaLoadOptions,
43
59
  schemaTrustRoot,
60
+ settleBaseline,
61
+ staleFindings,
62
+ supportedExtensions,
63
+ toJsonText,
44
64
  writeFileAtomic
45
- } from "./chunk-57NEQRVO.js";
65
+ } from "./chunk-KCOFEWV5.js";
46
66
  import {
47
67
  STDIN_LINES_MARKER
48
68
  } from "./chunk-YEUYKR62.js";
@@ -67,12 +87,106 @@ import {
67
87
  pageManifestPath,
68
88
  parseCollections,
69
89
  sameFile
70
- } from "./chunk-Q35YIDBV.js";
90
+ } from "./chunk-XKBEHKWO.js";
71
91
  import {
72
92
  ToolError,
73
93
  errorMessage
74
94
  } from "./chunk-G3T47CFV.js";
75
95
 
96
+ // src/meta/commands/validate.ts
97
+ import { readFile as readFile6 } from "node:fs/promises";
98
+
99
+ // src/meta/core/external-metadata.ts
100
+ import { readFile as readFile3 } from "node:fs/promises";
101
+ import { isAbsolute, relative as relative2, resolve as resolve2, sep } from "node:path";
102
+ import {
103
+ LineCounter,
104
+ isMap as isMap2,
105
+ isNode,
106
+ isScalar,
107
+ isSeq,
108
+ parseDocument as parseDocument2
109
+ } from "yaml";
110
+
111
+ // src/shared/manifest-cas.ts
112
+ import { readFile } from "node:fs/promises";
113
+ import { posix } from "node:path";
114
+ var COMMIT_ATTEMPTS = 5;
115
+ function conflictRefusal(file) {
116
+ return `${file} changed under the command while it was being written. Nothing was written to it. Re-run the command.`;
117
+ }
118
+ function heldManifest(path, file, before, absent = false) {
119
+ return { path, file, before, text: before, ops: [], bases: /* @__PURE__ */ new Map(), absent };
120
+ }
121
+ function encodeValue(value) {
122
+ return value === void 0 ? "absent" : JSON.stringify(value);
123
+ }
124
+ function baseKey(at2) {
125
+ const entry = at2.join === "path" ? posix.normalize(at2.entry) : at2.entry;
126
+ return `${entry}\0${at2.key}`;
127
+ }
128
+ function rebaseable(held, op, codec) {
129
+ const at2 = baseKey(op);
130
+ if (held.bases.has(at2)) return;
131
+ held.bases.set(at2, {
132
+ entry: op.entry,
133
+ key: op.key,
134
+ join: op.join,
135
+ value: encodeValue(codec.read(held.before, op))
136
+ });
137
+ }
138
+ function rebase(held, current, codec) {
139
+ for (const base of held.bases.values()) {
140
+ if (encodeValue(codec.read(current, base)) !== base.value) return void 0;
141
+ }
142
+ let text2 = current;
143
+ for (const op of held.ops) text2 = codec.apply(text2, op);
144
+ return text2;
145
+ }
146
+ function rebaseline(held) {
147
+ held.before = held.text;
148
+ held.ops = [];
149
+ held.bases.clear();
150
+ held.absent = false;
151
+ }
152
+ async function settle(held, io) {
153
+ for (let attempt = 1; attempt <= COMMIT_ATTEMPTS; attempt++) {
154
+ let current;
155
+ try {
156
+ current = await io.read(held.path);
157
+ } catch (error) {
158
+ if (held.absent && isMissing(error)) {
159
+ await io.write(held.path, held.text);
160
+ held.absent = false;
161
+ return;
162
+ }
163
+ const reason = error instanceof Error ? error.message : String(error);
164
+ throw io.toError(`Manifest ${held.file} could not be read: ${reason}`);
165
+ }
166
+ if (!held.absent && current === held.before) {
167
+ await io.write(held.path, held.text);
168
+ return;
169
+ }
170
+ const replayed = rebase(held, current, io.codec);
171
+ if (replayed === void 0) throw io.toError(conflictRefusal(held.file));
172
+ held.before = current;
173
+ held.text = replayed;
174
+ held.absent = false;
175
+ }
176
+ throw io.toError(conflictRefusal(held.file));
177
+ }
178
+ function isMissing(error) {
179
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
180
+ }
181
+ async function readManifestOrEmpty(absPath, perPage) {
182
+ try {
183
+ return await readFile(absPath, "utf8");
184
+ } catch (e) {
185
+ if (perPage && isMissing(e)) return "";
186
+ throw e;
187
+ }
188
+ }
189
+
76
190
  // src/meta/core/external-metadata-fetch.ts
77
191
  var EXTERNAL_METADATA_FETCH_TIMEOUT_MS = 1e4;
78
192
  var EXTERNAL_METADATA_FETCH_MAX_BYTES = 5 * 1024 * 1024;
@@ -166,7 +280,7 @@ async function readCapped(url, res, maxBytes) {
166
280
 
167
281
  // src/shared/encryption-key.ts
168
282
  import { spawnSync } from "node:child_process";
169
- import { readFile } from "node:fs/promises";
283
+ import { readFile as readFile2 } from "node:fs/promises";
170
284
  import { basename as basename2, dirname as dirname2, join as join2, relative, resolve } from "node:path";
171
285
  import { isDeepStrictEqual } from "node:util";
172
286
  import { isMap, parseDocument, parse as parseYaml, stringify } from "yaml";
@@ -179,16 +293,16 @@ var RENAME_ATTEMPTS = 3;
179
293
  function errorCode(err) {
180
294
  return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
181
295
  }
182
- var wait = (ms) => new Promise((resolve4) => {
183
- setTimeout(resolve4, ms);
296
+ var wait = (ms) => new Promise((resolve8) => {
297
+ setTimeout(resolve8, ms);
184
298
  });
185
- async function writeTextAtomic(path, text) {
299
+ async function writeTextAtomic(path, text2) {
186
300
  const tmp = join(
187
301
  dirname(path),
188
302
  `.${basename(path)}.manni-${process.pid}-${Math.random().toString(36).slice(2, 8)}.tmp`
189
303
  );
190
304
  try {
191
- await writeFile(tmp, text, "utf8");
305
+ await writeFile(tmp, text2, "utf8");
192
306
  try {
193
307
  const { mode } = await stat(path);
194
308
  await chmod(tmp, mode);
@@ -236,7 +350,7 @@ function errorCode2(err) {
236
350
  }
237
351
  async function readIfPresent(path) {
238
352
  try {
239
- return await readFile(path, "utf8");
353
+ return await readFile2(path, "utf8");
240
354
  } catch (err) {
241
355
  if (errorCode2(err) === "ENOENT") return null;
242
356
  throw err;
@@ -248,21 +362,21 @@ async function fileTarget(file, toError) {
248
362
  `${file.source} is a single-tool config file, which cannot hold the family ${ENCRYPTION_KEY_FIELD}. Move its keys under the tool's section in ${FAMILY_CONFIG_NAMES[0] ?? "manni.config.yaml"}, or set ${ENCRYPTION_KEY_ENV}.`
249
363
  );
250
364
  }
251
- const text = await readIfPresent(file.path);
252
- return { path: file.path, source: file.source, text: text ?? file.text };
365
+ const text2 = await readIfPresent(file.path);
366
+ return { path: file.path, source: file.source, text: text2 ?? file.text };
253
367
  }
254
368
  async function defaultTarget(cwd) {
255
369
  const root = findGitRoot(cwd) ?? resolve(cwd);
256
370
  for (const name of FAMILY_CONFIG_NAMES) {
257
371
  const path2 = join2(root, name);
258
- const text = await readIfPresent(path2);
259
- if (text !== null) return { path: path2, source: relativeSource(cwd, path2), text };
372
+ const text2 = await readIfPresent(path2);
373
+ if (text2 !== null) return { path: path2, source: relativeSource(cwd, path2), text: text2 };
260
374
  }
261
375
  const path = join2(root, FAMILY_CONFIG_NAMES[0] ?? "manni.config.yaml");
262
376
  return { path, source: relativeSource(cwd, path), text: null };
263
377
  }
264
- function withKey(text, key, previous, source, toError) {
265
- const doc = parseDocument(text);
378
+ function withKey(text2, key, previous, source, toError) {
379
+ const doc = parseDocument(text2);
266
380
  const [first] = doc.errors;
267
381
  if (first !== void 0) {
268
382
  throw toError(`${source}: invalid YAML: ${first.message}`);
@@ -307,12 +421,12 @@ async function writeEncryptionKey(opts) {
307
421
  );
308
422
  }
309
423
  const target = file === null ? await defaultTarget(opts.cwd) : await fileTarget(file, toError);
310
- const text = target.text === null ? stringify({
424
+ const text2 = target.text === null ? stringify({
311
425
  [ENCRYPTION_KEY_FIELD]: key,
312
426
  ...typeof previous === "string" ? { [ENCRYPTION_KEY_PREVIOUS_FIELD]: previous } : {}
313
427
  }) : withKey(target.text, key, previous, target.source, toError);
314
- verify(target.text, text, key, previous, target.source, toError);
315
- if (opts.dryRun !== true) await writeTextAtomic(target.path, text);
428
+ verify(target.text, text2, key, previous, target.source, toError);
429
+ if (opts.dryRun !== true) await writeTextAtomic(target.path, text2);
316
430
  return { path: target.path, source: target.source, created: target.text === null };
317
431
  }
318
432
  function isIgnoredByGit(path) {
@@ -377,8 +491,8 @@ function valueAt(data, pointer) {
377
491
  let node = data;
378
492
  for (const seg of segments(pointer)) {
379
493
  if (Array.isArray(node)) {
380
- const items = node;
381
- node = /^\d+$/.test(seg) ? items[Number(seg)] : void 0;
494
+ const items2 = node;
495
+ node = /^\d+$/.test(seg) ? items2[Number(seg)] : void 0;
382
496
  } else if (isRecord(node)) {
383
497
  node = Object.hasOwn(node, seg) ? node[seg] : void 0;
384
498
  } else {
@@ -393,10 +507,10 @@ function withValueAt(root, pointer, value) {
393
507
  const seg = parts[i];
394
508
  if (seg === void 0) return value;
395
509
  if (Array.isArray(node)) {
396
- const items = [...node];
397
- if (!/^\d+$/.test(seg) || Number(seg) >= items.length) return node;
398
- items[Number(seg)] = set(items[Number(seg)], i + 1);
399
- return items;
510
+ const items2 = [...node];
511
+ if (!/^\d+$/.test(seg) || Number(seg) >= items2.length) return node;
512
+ items2[Number(seg)] = set(items2[Number(seg)], i + 1);
513
+ return items2;
400
514
  }
401
515
  if (isRecord(node) && Object.hasOwn(node, seg)) {
402
516
  return { ...node, [seg]: set(node[seg], i + 1) };
@@ -533,97 +647,6 @@ function redactedDerived(f) {
533
647
  };
534
648
  }
535
649
 
536
- // src/meta/core/external-metadata.ts
537
- import { readFile as readFile3 } from "node:fs/promises";
538
- import { isAbsolute, relative as relative2, resolve as resolve2, sep } from "node:path";
539
- import {
540
- LineCounter,
541
- isMap as isMap2,
542
- isNode,
543
- isScalar,
544
- isSeq,
545
- parseDocument as parseDocument2
546
- } from "yaml";
547
-
548
- // src/shared/manifest-cas.ts
549
- import { readFile as readFile2 } from "node:fs/promises";
550
- import { posix } from "node:path";
551
- var COMMIT_ATTEMPTS = 5;
552
- function conflictRefusal(file) {
553
- return `${file} changed under the command while it was being written. Nothing was written to it. Re-run the command.`;
554
- }
555
- function heldManifest(path, file, before, absent = false) {
556
- return { path, file, before, text: before, ops: [], bases: /* @__PURE__ */ new Map(), absent };
557
- }
558
- function encodeValue(value) {
559
- return value === void 0 ? "absent" : JSON.stringify(value);
560
- }
561
- function baseKey(at2) {
562
- const entry = at2.join === "path" ? posix.normalize(at2.entry) : at2.entry;
563
- return `${entry}\0${at2.key}`;
564
- }
565
- function rebaseable(held, op, codec) {
566
- const at2 = baseKey(op);
567
- if (held.bases.has(at2)) return;
568
- held.bases.set(at2, {
569
- entry: op.entry,
570
- key: op.key,
571
- join: op.join,
572
- value: encodeValue(codec.read(held.before, op))
573
- });
574
- }
575
- function rebase(held, current, codec) {
576
- for (const base of held.bases.values()) {
577
- if (encodeValue(codec.read(current, base)) !== base.value) return void 0;
578
- }
579
- let text = current;
580
- for (const op of held.ops) text = codec.apply(text, op);
581
- return text;
582
- }
583
- function rebaseline(held) {
584
- held.before = held.text;
585
- held.ops = [];
586
- held.bases.clear();
587
- held.absent = false;
588
- }
589
- async function settle(held, io) {
590
- for (let attempt = 1; attempt <= COMMIT_ATTEMPTS; attempt++) {
591
- let current;
592
- try {
593
- current = await io.read(held.path);
594
- } catch (error) {
595
- if (held.absent && isMissing(error)) {
596
- await io.write(held.path, held.text);
597
- held.absent = false;
598
- return;
599
- }
600
- const reason = error instanceof Error ? error.message : String(error);
601
- throw io.toError(`Manifest ${held.file} could not be read: ${reason}`);
602
- }
603
- if (!held.absent && current === held.before) {
604
- await io.write(held.path, held.text);
605
- return;
606
- }
607
- const replayed = rebase(held, current, io.codec);
608
- if (replayed === void 0) throw io.toError(conflictRefusal(held.file));
609
- held.before = current;
610
- held.text = replayed;
611
- held.absent = false;
612
- }
613
- throw io.toError(conflictRefusal(held.file));
614
- }
615
- function isMissing(error) {
616
- return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
617
- }
618
- async function readManifestOrEmpty(absPath, perPage) {
619
- try {
620
- return await readFile2(absPath, "utf8");
621
- } catch (e) {
622
- if (perPage && isMissing(e)) return "";
623
- throw e;
624
- }
625
- }
626
-
627
650
  // src/meta/core/external-metadata.ts
628
651
  var EXTERNAL_OWNED_SCHEMA = "external:owned";
629
652
  var EXTERNAL_DUPLICATE_SCHEMA = "external:duplicate";
@@ -666,18 +689,18 @@ async function loadExternalMetadata(collections, opts) {
666
689
  rules.push({ collection: declared, manifest, configDir: opts.configDir });
667
690
  continue;
668
691
  }
669
- const join4 = externalMetadataJoin(manifest);
692
+ const join5 = externalMetadataJoin(manifest);
670
693
  let parsed;
671
694
  let file;
672
695
  if (classifyRef(manifest.file).kind === "url") {
673
696
  file = manifest.file;
674
- const text = await fetchExternalMetadata(manifest.file, {
697
+ const text2 = await fetchExternalMetadata(manifest.file, {
675
698
  ...manifest.tokenEnv !== void 0 ? { tokenEnv: manifest.tokenEnv } : {},
676
699
  ...opts.offline !== void 0 ? { offline: opts.offline } : {},
677
700
  ...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
678
701
  ...opts.env !== void 0 ? { env: opts.env } : {}
679
702
  });
680
- parsed = parseManifest(manifest, collection, text, file, opts.configDir, join4, ownership(declared, manifest));
703
+ parsed = parseManifest(manifest, collection, text2, file, opts.configDir, join5, ownership(declared, manifest));
681
704
  } else {
682
705
  const abs = isAbsolute(manifest.file) ? manifest.file : resolve2(opts.configDir, manifest.file);
683
706
  const label = reportedPath(abs, opts.base);
@@ -693,7 +716,7 @@ async function loadExternalMetadata(collections, opts) {
693
716
  // this run's spelling onto every value and entry instead.
694
717
  JSON.stringify([
695
718
  collection,
696
- join4,
719
+ join5,
697
720
  opts.configDir,
698
721
  variantOf(declared, manifest)
699
722
  ]),
@@ -703,7 +726,7 @@ async function loadExternalMetadata(collections, opts) {
703
726
  await readManifest(abs, label),
704
727
  label,
705
728
  opts.configDir,
706
- join4,
729
+ join5,
707
730
  ownership(declared, manifest)
708
731
  )
709
732
  );
@@ -717,14 +740,14 @@ async function loadExternalMetadata(collections, opts) {
717
740
  else owners.set(key, [{ collection, file }]);
718
741
  }
719
742
  let target;
720
- if (join4 === PATH_JOIN) {
743
+ if (join5 === PATH_JOIN) {
721
744
  target = byPath;
722
745
  } else {
723
- const existing = byField.get(join4);
746
+ const existing = byField.get(join5);
724
747
  if (existing) target = existing;
725
748
  else {
726
749
  target = /* @__PURE__ */ new Map();
727
- byField.set(join4, target);
750
+ byField.set(join5, target);
728
751
  }
729
752
  }
730
753
  for (const [indexKey, values] of parsed.values) {
@@ -759,14 +782,14 @@ function variantOf(declared, manifest) {
759
782
  }
760
783
  async function loadPerPage(declared, manifest, opts, resolvedBy, into) {
761
784
  const collection = declared.name;
762
- const join4 = externalMetadataJoin(manifest);
785
+ const join5 = externalMetadataJoin(manifest);
763
786
  const implied = manifest.keys === void 0 ? { collection: declared, manifest, file: manifest.file, files: /* @__PURE__ */ new Set() } : void 0;
764
787
  for (const key of manifest.keys ?? []) {
765
788
  const list2 = into.owners.get(key);
766
789
  if (list2) list2.push({ collection, file: manifest.file });
767
790
  else into.owners.set(key, [{ collection, file: manifest.file }]);
768
791
  }
769
- const target = indexFor(join4, into);
792
+ const target = indexFor(join5, into);
770
793
  for (const pageAbs of opts.pages ?? []) {
771
794
  const resolved = pageManifestPath(manifest.file, opts.configDir, pageAbs);
772
795
  if ("outside" in resolved) {
@@ -788,14 +811,14 @@ async function loadPerPage(declared, manifest, opts, resolvedBy, into) {
788
811
  const label = reportedPath(resolved.abs, opts.base);
789
812
  const parsed = await parsedManifests.parse(
790
813
  resolved.abs,
791
- JSON.stringify([collection, join4, opts.configDir, variantOf(declared, manifest)]),
814
+ JSON.stringify([collection, join5, opts.configDir, variantOf(declared, manifest)]),
792
815
  async () => parseManifest(
793
816
  manifest,
794
817
  collection,
795
818
  await readPageManifest(resolved.abs, label),
796
819
  label,
797
820
  opts.configDir,
798
- join4,
821
+ join5,
799
822
  ownership(declared, manifest)
800
823
  )
801
824
  );
@@ -809,7 +832,7 @@ async function loadPerPage(declared, manifest, opts, resolvedBy, into) {
809
832
  );
810
833
  }
811
834
  for (const [indexKey, values] of parsed.values) {
812
- const key = join4 === PATH_JOIN ? pageAbs : indexKey;
835
+ const key = join5 === PATH_JOIN ? pageAbs : indexKey;
813
836
  const merged = target.get(key) ?? /* @__PURE__ */ new Map();
814
837
  for (const [owned, value] of values) merged.set(owned, reported(value, label));
815
838
  target.set(key, merged);
@@ -823,12 +846,12 @@ async function loadPerPage(declared, manifest, opts, resolvedBy, into) {
823
846
  }
824
847
  return implied;
825
848
  }
826
- function indexFor(join4, into) {
827
- if (join4 === PATH_JOIN) return into.byPath;
828
- const existing = into.byField.get(join4);
849
+ function indexFor(join5, into) {
850
+ if (join5 === PATH_JOIN) return into.byPath;
851
+ const existing = into.byField.get(join5);
829
852
  if (existing) return existing;
830
853
  const created = /* @__PURE__ */ new Map();
831
- into.byField.set(join4, created);
854
+ into.byField.set(join5, created);
832
855
  return created;
833
856
  }
834
857
  function outsideRefusal(manifest, collection, pageRel) {
@@ -858,11 +881,11 @@ function unreadable(file, err) {
858
881
  const reason = err instanceof Error ? err.message : String(err);
859
882
  return new DocmetaError(`Manifest ${file} could not be read: ${reason}`);
860
883
  }
861
- function parseManifest(manifest, collection, text, file, configDir, join4, owns) {
884
+ function parseManifest(manifest, collection, text2, file, configDir, join5, owns) {
862
885
  const target = /* @__PURE__ */ new Map();
863
886
  const entries = [];
864
887
  const lc = new LineCounter();
865
- const doc = parseDocument2(text, { lineCounter: lc, uniqueKeys: false });
888
+ const doc = parseDocument2(text2, { lineCounter: lc, uniqueKeys: false });
866
889
  const problem = doc.errors[0];
867
890
  if (problem) {
868
891
  throw new DocmetaError(
@@ -874,7 +897,7 @@ function parseManifest(manifest, collection, text, file, configDir, join4, owns)
874
897
  return { values: target, entries };
875
898
  if (!isMap2(root)) {
876
899
  throw new DocmetaError(
877
- `Manifest ${file}: the manifest must be a mapping from document ${join4 === PATH_JOIN ? "path" : `"${join4}"`} to owned keys.`
900
+ `Manifest ${file}: the manifest must be a mapping from document ${join5 === PATH_JOIN ? "path" : `"${join5}"`} to owned keys.`
878
901
  );
879
902
  }
880
903
  const lineAt = (node) => {
@@ -898,7 +921,7 @@ function parseManifest(manifest, collection, text, file, configDir, join4, owns)
898
921
  `Manifest ${where}: "${spelled}" must be a mapping of owned keys to values.`
899
922
  );
900
923
  }
901
- const indexKey = join4 === PATH_JOIN ? resolve2(configDir, spelled) : spelled;
924
+ const indexKey = join5 === PATH_JOIN ? resolve2(configDir, spelled) : spelled;
902
925
  const values = target.get(indexKey) ?? /* @__PURE__ */ new Map();
903
926
  for (const kv of pair.value.items) {
904
927
  const key = isScalar(kv.key) ? String(kv.key.value) : String(kv.key);
@@ -924,8 +947,8 @@ function parseManifest(manifest, collection, text, file, configDir, join4, owns)
924
947
  target.set(indexKey, values);
925
948
  entries.push({
926
949
  collection,
927
- join: join4,
928
- ...join4 === PATH_JOIN ? { abs: indexKey } : {},
950
+ join: join5,
951
+ ...join5 === PATH_JOIN ? { abs: indexKey } : {},
929
952
  spelled,
930
953
  file,
931
954
  ...entryLine === void 0 ? {} : { line: entryLine }
@@ -1186,9 +1209,6 @@ function isFieldLocation(value) {
1186
1209
  return value === "page" || value === "external";
1187
1210
  }
1188
1211
 
1189
- // src/meta/core/graph-output.ts
1190
- var GRAPH_OUTPUT_KEYWORD = "x-manni-graph-output";
1191
-
1192
1212
  // src/meta/core/validator.ts
1193
1213
  import * as AjvDraft07Ns from "ajv";
1194
1214
  import * as Ajv2019Ns from "ajv/dist/2019.js";
@@ -1196,6 +1216,11 @@ import * as Ajv2020Ns from "ajv/dist/2020.js";
1196
1216
  import * as AjvDraft04Ns from "ajv-draft-04";
1197
1217
  import * as addFormatsNs from "ajv-formats";
1198
1218
  import { createRequire } from "node:module";
1219
+
1220
+ // src/meta/core/graph-output.ts
1221
+ var GRAPH_OUTPUT_KEYWORD = "x-manni-graph-output";
1222
+
1223
+ // src/meta/core/validator.ts
1199
1224
  var draft06MetaSchema = createRequire(import.meta.url)(
1200
1225
  "ajv/dist/refs/json-schema-draft-06.json"
1201
1226
  );
@@ -1810,6 +1835,23 @@ function familyMarks(opts) {
1810
1835
  })();
1811
1836
  }
1812
1837
 
1838
+ // src/meta/core/relocation.ts
1839
+ import { access, readFile as readFile4, stat as stat2, unlink } from "node:fs/promises";
1840
+ import { basename as basename3, extname, isAbsolute as isAbsolute3, join as join3, relative as relative4, resolve as resolve3, sep as sep3 } from "node:path";
1841
+ import picomatch from "picomatch";
1842
+ import {
1843
+ LineCounter as LineCounter3,
1844
+ isMap as isMap4,
1845
+ isNode as isNode3,
1846
+ isScalar as isScalar3,
1847
+ isSeq as isSeq2,
1848
+ parse as parseYaml2,
1849
+ parseDocument as parseDocument4
1850
+ } from "yaml";
1851
+
1852
+ // src/meta/core/collections.ts
1853
+ import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolvePath, sep as sep2 } from "node:path";
1854
+
1813
1855
  // src/meta/core/projection.ts
1814
1856
  var SYSTEM_COLUMNS = ["_path", "_format", "_present", "_data"];
1815
1857
  var RESERVED = new Set(SYSTEM_COLUMNS);
@@ -1940,7 +1982,7 @@ function isParamName(name) {
1940
1982
  return PARAM_NAME_HEAD.exec(name)?.[0] === name;
1941
1983
  }
1942
1984
  function collectNamedParameters(sql) {
1943
- const tokens = [];
1985
+ const tokens2 = [];
1944
1986
  const seen = /* @__PURE__ */ new Set();
1945
1987
  const prefixOf = /* @__PURE__ */ new Map();
1946
1988
  let i = 0;
@@ -1970,14 +2012,14 @@ function collectNamedParameters(sql) {
1970
2012
  prefixOf.set(m[0], ch);
1971
2013
  if (!seen.has(token)) {
1972
2014
  seen.add(token);
1973
- tokens.push(token);
2015
+ tokens2.push(token);
1974
2016
  }
1975
2017
  i += 1 + m[0].length;
1976
2018
  } else {
1977
2019
  i++;
1978
2020
  }
1979
2021
  }
1980
- return tokens;
2022
+ return tokens2;
1981
2023
  }
1982
2024
  function stripLeadingTrivia(sql) {
1983
2025
  let i = 0;
@@ -1990,16 +2032,16 @@ function stripLeadingTrivia(sql) {
1990
2032
  return sql.slice(i);
1991
2033
  }
1992
2034
  }
1993
- function isTrivia(text) {
2035
+ function isTrivia(text2) {
1994
2036
  let i = 0;
1995
- while (i < text.length) {
1996
- const ch = text[i];
2037
+ while (i < text2.length) {
2038
+ const ch = text2[i];
1997
2039
  if (ch === void 0 || /\s/.test(ch) || ch === ";") {
1998
2040
  i++;
1999
2041
  continue;
2000
2042
  }
2001
- if (startsComment(text, i)) {
2002
- i = skipComment(text, i);
2043
+ if (startsComment(text2, i)) {
2044
+ i = skipComment(text2, i);
2003
2045
  continue;
2004
2046
  }
2005
2047
  return false;
@@ -2011,8 +2053,8 @@ function loadSqlite() {
2011
2053
  sqliteModule ??= (async () => {
2012
2054
  const original = process.emitWarning.bind(process);
2013
2055
  const filtered = (warning, ...rest) => {
2014
- const text = typeof warning === "string" ? warning : warning.message;
2015
- if (text.includes("SQLite is an experimental feature")) return;
2056
+ const text2 = typeof warning === "string" ? warning : warning.message;
2057
+ if (text2.includes("SQLite is an experimental feature")) return;
2016
2058
  Reflect.apply(original, process, [warning, ...rest]);
2017
2059
  };
2018
2060
  process.emitWarning = filtered;
@@ -2026,7 +2068,6 @@ function loadSqlite() {
2026
2068
  }
2027
2069
 
2028
2070
  // src/meta/core/collections.ts
2029
- import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolvePath, sep as sep2 } from "node:path";
2030
2071
  function memberOf(collections, configDir, base, label) {
2031
2072
  if (collections.length === 0) return [];
2032
2073
  if (label === STDIN_LABEL) return [];
@@ -2067,20 +2108,6 @@ function createCollectionViews(db, collections) {
2067
2108
  }
2068
2109
  }
2069
2110
 
2070
- // src/meta/core/relocation.ts
2071
- import { access, readFile as readFile4, stat as stat2, unlink } from "node:fs/promises";
2072
- import { basename as basename3, extname, isAbsolute as isAbsolute3, join as join3, relative as relative4, resolve as resolve3, sep as sep3 } from "node:path";
2073
- import picomatch from "picomatch";
2074
- import {
2075
- LineCounter as LineCounter3,
2076
- isMap as isMap4,
2077
- isNode as isNode3,
2078
- isScalar as isScalar3,
2079
- isSeq as isSeq2,
2080
- parse as parseYaml2,
2081
- parseDocument as parseDocument4
2082
- } from "yaml";
2083
-
2084
2111
  // src/meta/core/external-metadata-write.ts
2085
2112
  import { posix as posix3 } from "node:path";
2086
2113
  import {
@@ -2091,9 +2118,9 @@ import {
2091
2118
  parseDocument as parseDocument3,
2092
2119
  stringify as stringify2
2093
2120
  } from "yaml";
2094
- function spliceManifestValue(text, options) {
2121
+ function spliceManifestValue(text2, options) {
2095
2122
  const { entry, key, value, file } = options;
2096
- const join4 = options.join ?? PATH_JOIN;
2123
+ const join5 = options.join ?? PATH_JOIN;
2097
2124
  const at2 = (line2) => {
2098
2125
  const name = file === void 0 ? "Manifest" : `Manifest ${file}`;
2099
2126
  if (line2 === void 0) return name;
@@ -2107,9 +2134,9 @@ function spliceManifestValue(text, options) {
2107
2134
  if (value === void 0) {
2108
2135
  throw new DocmetaError(`${at2()}: refusing to write "${key}" for "${entry}" with no value.`);
2109
2136
  }
2110
- const eol = detectEol(text);
2137
+ const eol = detectEol(text2);
2111
2138
  const lc = new LineCounter2();
2112
- const doc = parseDocument3(text, { lineCounter: lc, uniqueKeys: false });
2139
+ const doc = parseDocument3(text2, { lineCounter: lc, uniqueKeys: false });
2113
2140
  const problem = doc.errors[0];
2114
2141
  if (problem) {
2115
2142
  throw new DocmetaError(`${at2()} is not valid YAML: ${problem.message}`);
@@ -2118,18 +2145,18 @@ function spliceManifestValue(text, options) {
2118
2145
  const r = rangeOf(node);
2119
2146
  return r ? lc.linePos(r[0]).line : void 0;
2120
2147
  };
2121
- const same = join4 === PATH_JOIN ? (a, b) => posix3.normalize(a) === posix3.normalize(b) : (a, b) => a === b;
2148
+ const same = join5 === PATH_JOIN ? (a, b) => posix3.normalize(a) === posix3.normalize(b) : (a, b) => a === b;
2122
2149
  const root = doc.contents;
2123
2150
  let spelled = entry;
2124
2151
  let edit;
2125
- if (root === null || isScalar2(root) && root.value === null && emptySource(text, root)) {
2126
- edit = startManifest(text, entry, key, value, eol);
2152
+ if (root === null || isScalar2(root) && root.value === null && emptySource(text2, root)) {
2153
+ edit = startManifest(text2, entry, key, value, eol);
2127
2154
  } else if (!isMap3(root)) {
2128
2155
  throw new DocmetaError(
2129
- `${at2()}: the manifest must be a mapping from document ${join4 === PATH_JOIN ? "path" : `"${join4}"`} to owned keys.`
2156
+ `${at2()}: the manifest must be a mapping from document ${join5 === PATH_JOIN ? "path" : `"${join5}"`} to owned keys.`
2130
2157
  );
2131
2158
  } else {
2132
- const style = detectStyle(text, root, eol);
2159
+ const style = detectStyle(text2, root, eol);
2133
2160
  const matches = root.items.filter((p) => same(keyString(p.key), entry));
2134
2161
  const [first, second] = matches;
2135
2162
  if (first && second) {
@@ -2143,7 +2170,7 @@ function spliceManifestValue(text, options) {
2143
2170
  `${at2()}: the manifest is a flow mapping ({ \u2026 }), which this writer does not add entries to. Rewrite it in block style.`
2144
2171
  );
2145
2172
  }
2146
- edit = appendEntry(text, root, entry, key, value, style);
2173
+ edit = appendEntry(text2, root, entry, key, value, style);
2147
2174
  } else {
2148
2175
  spelled = keyString(first.key);
2149
2176
  const where = at2(keyLine(first.key));
@@ -2166,18 +2193,18 @@ function spliceManifestValue(text, options) {
2166
2193
  );
2167
2194
  let made;
2168
2195
  if (v.flow === true) {
2169
- if (hit) made = replaceFlowValue(text, hit, value);
2170
- else if (v.items.length === 0) made = fillEmptyEntry(text, first, key, value, style);
2196
+ if (hit) made = replaceFlowValue(text2, hit, value);
2197
+ else if (v.items.length === 0) made = fillEmptyEntry(text2, first, key, value, style);
2171
2198
  else throw flowRefusal();
2172
2199
  if (!made) throw hit ? flowRefusal() : formRefusal();
2173
2200
  } else {
2174
- made = hit ? replaceBlockValue(text, hit, value, style) : insertKey(text, v, key, value, style);
2201
+ made = hit ? replaceBlockValue(text2, hit, value, style) : insertKey(text2, v, key, value, style);
2175
2202
  if (!made) throw formRefusal();
2176
2203
  }
2177
2204
  edit = made;
2178
2205
  }
2179
2206
  }
2180
- const out = text.slice(0, edit.start) + edit.insert + text.slice(edit.end);
2207
+ const out = text2.slice(0, edit.start) + edit.insert + text2.slice(edit.end);
2181
2208
  const line = readBack(
2182
2209
  doc,
2183
2210
  out,
@@ -2190,13 +2217,13 @@ function spliceManifestValue(text, options) {
2190
2217
  );
2191
2218
  return { text: out, line };
2192
2219
  }
2193
- function readManifestValue(text, options) {
2194
- const join4 = options.join ?? PATH_JOIN;
2195
- const doc = parseDocument3(text, { uniqueKeys: false });
2220
+ function readManifestValue(text2, options) {
2221
+ const join5 = options.join ?? PATH_JOIN;
2222
+ const doc = parseDocument3(text2, { uniqueKeys: false });
2196
2223
  if (doc.errors.length > 0) return void 0;
2197
2224
  const root = doc.contents;
2198
2225
  if (!isMap3(root)) return void 0;
2199
- const same = join4 === PATH_JOIN ? (a, b) => posix3.normalize(a) === posix3.normalize(b) : (a, b) => a === b;
2226
+ const same = join5 === PATH_JOIN ? (a, b) => posix3.normalize(a) === posix3.normalize(b) : (a, b) => a === b;
2200
2227
  const pair = root.items.find((p) => same(keyString(p.key), options.entry));
2201
2228
  const held = pair?.value;
2202
2229
  if (!isMap3(held)) return void 0;
@@ -2204,17 +2231,17 @@ function readManifestValue(text, options) {
2204
2231
  const node = kv?.value;
2205
2232
  return isNode2(node) ? node.toJSON() : void 0;
2206
2233
  }
2207
- function removeManifestKey(text, options) {
2234
+ function removeManifestKey(text2, options) {
2208
2235
  const { entry, key, file } = options;
2209
- const join4 = options.join ?? PATH_JOIN;
2236
+ const join5 = options.join ?? PATH_JOIN;
2210
2237
  const at2 = (line) => {
2211
2238
  const name = file === void 0 ? "Manifest" : `Manifest ${file}`;
2212
2239
  if (line === void 0) return name;
2213
2240
  return file === void 0 ? `${name} line ${String(line)}` : `${name}:${String(line)}`;
2214
2241
  };
2215
- const eol = detectEol(text);
2242
+ const eol = detectEol(text2);
2216
2243
  const lc = new LineCounter2();
2217
- const doc = parseDocument3(text, { lineCounter: lc, uniqueKeys: false });
2244
+ const doc = parseDocument3(text2, { lineCounter: lc, uniqueKeys: false });
2218
2245
  const problem = doc.errors[0];
2219
2246
  if (problem) {
2220
2247
  throw new DocmetaError(`${at2()} is not valid YAML: ${problem.message}`);
@@ -2224,13 +2251,13 @@ function removeManifestKey(text, options) {
2224
2251
  return r ? lc.linePos(r[0]).line : void 0;
2225
2252
  };
2226
2253
  const root = doc.contents;
2227
- if (root === null || isScalar2(root) && root.value === null) return { text };
2254
+ if (root === null || isScalar2(root) && root.value === null) return { text: text2 };
2228
2255
  if (!isMap3(root)) {
2229
2256
  throw new DocmetaError(
2230
- `${at2()}: the manifest must be a mapping from document ${join4 === PATH_JOIN ? "path" : `"${join4}"`} to owned keys.`
2257
+ `${at2()}: the manifest must be a mapping from document ${join5 === PATH_JOIN ? "path" : `"${join5}"`} to owned keys.`
2231
2258
  );
2232
2259
  }
2233
- const same = join4 === PATH_JOIN ? (a, b) => posix3.normalize(a) === posix3.normalize(b) : (a, b) => a === b;
2260
+ const same = join5 === PATH_JOIN ? (a, b) => posix3.normalize(a) === posix3.normalize(b) : (a, b) => a === b;
2234
2261
  const matches = root.items.filter((p) => same(keyString(p.key), entry));
2235
2262
  const [first, second] = matches;
2236
2263
  if (first && second) {
@@ -2238,7 +2265,7 @@ function removeManifestKey(text, options) {
2238
2265
  `${at2(keyLine(second.key))}: "${keyString(second.key)}" is named twice (first at line ${String(keyLine(first.key) ?? "?")}). Merge the two entries into one.`
2239
2266
  );
2240
2267
  }
2241
- if (first === void 0) return { text };
2268
+ if (first === void 0) return { text: text2 };
2242
2269
  const spelled = keyString(first.key);
2243
2270
  const where = at2(keyLine(first.key));
2244
2271
  const v = first.value;
@@ -2252,7 +2279,7 @@ function removeManifestKey(text, options) {
2252
2279
  `${at2(keyLine(again.key))}: "${spelled}" sets "${key}" twice (first at line ${String(keyLine(hit.key) ?? "?")}). Remove one.`
2253
2280
  );
2254
2281
  }
2255
- if (hit === void 0) return { text };
2282
+ if (hit === void 0) return { text: text2 };
2256
2283
  const whole = v.items.length === 1;
2257
2284
  if (!whole && v.flow === true) {
2258
2285
  throw new DocmetaError(
@@ -2260,18 +2287,18 @@ function removeManifestKey(text, options) {
2260
2287
  );
2261
2288
  }
2262
2289
  const from = rangeOf(whole ? first.key : hit.key);
2263
- const to = whole ? contentEnd(text, v) : contentEnd(text, hit.value) ?? contentEnd(text, hit.key);
2290
+ const to = whole ? contentEnd(text2, v) : contentEnd(text2, hit.value) ?? contentEnd(text2, hit.key);
2264
2291
  if (!from || to === void 0) {
2265
2292
  throw new DocmetaError(
2266
2293
  `${where}: "${spelled}" writes a key in a form this writer does not edit. Write it as "${key}: \u2026".`
2267
2294
  );
2268
2295
  }
2269
- let { start, end } = keyLines(text, from[0], to, eol);
2296
+ let { start, end } = keyLines(text2, from[0], to, eol);
2270
2297
  if (whole) {
2271
- if (text.slice(start - 2 * eol.length, start) === eol + eol) start -= eol.length;
2272
- else if (start === 0 && text.startsWith(eol, end)) end += eol.length;
2298
+ if (text2.slice(start - 2 * eol.length, start) === eol + eol) start -= eol.length;
2299
+ else if (start === 0 && text2.startsWith(eol, end)) end += eol.length;
2273
2300
  }
2274
- const out = text.slice(0, start) + text.slice(end);
2301
+ const out = text2.slice(0, start) + text2.slice(end);
2275
2302
  const after = parseDocument3(out, { uniqueKeys: false });
2276
2303
  const fail = (why) => new DocmetaError(
2277
2304
  `${at2()}: removing "${key}" for "${entry}" did not read back as removed, so nothing was written. ${why}`
@@ -2290,16 +2317,16 @@ function removeManifestKey(text, options) {
2290
2317
  if (!deepEqual(now ?? null, expected)) throw fail("Another value in the manifest would have changed.");
2291
2318
  return { text: out };
2292
2319
  }
2293
- function renameManifestEntry(text, options) {
2320
+ function renameManifestEntry(text2, options) {
2294
2321
  const { entry, to, file } = options;
2295
- const join4 = options.join ?? PATH_JOIN;
2322
+ const join5 = options.join ?? PATH_JOIN;
2296
2323
  const at2 = (line) => {
2297
2324
  const name = file === void 0 ? "Manifest" : `Manifest ${file}`;
2298
2325
  if (line === void 0) return name;
2299
2326
  return file === void 0 ? `${name} line ${String(line)}` : `${name}:${String(line)}`;
2300
2327
  };
2301
2328
  const lc = new LineCounter2();
2302
- const doc = parseDocument3(text, { lineCounter: lc, uniqueKeys: false });
2329
+ const doc = parseDocument3(text2, { lineCounter: lc, uniqueKeys: false });
2303
2330
  const problem = doc.errors[0];
2304
2331
  if (problem) {
2305
2332
  throw new DocmetaError(`${at2()} is not valid YAML: ${problem.message}`);
@@ -2309,20 +2336,20 @@ function renameManifestEntry(text, options) {
2309
2336
  return r2 ? lc.linePos(r2[0]).line : void 0;
2310
2337
  };
2311
2338
  const root = doc.contents;
2312
- if (root === null || isScalar2(root) && root.value === null) return { text };
2339
+ if (root === null || isScalar2(root) && root.value === null) return { text: text2 };
2313
2340
  if (!isMap3(root)) {
2314
2341
  throw new DocmetaError(
2315
- `${at2()}: the manifest must be a mapping from document ${join4 === PATH_JOIN ? "path" : `"${join4}"`} to owned keys.`
2342
+ `${at2()}: the manifest must be a mapping from document ${join5 === PATH_JOIN ? "path" : `"${join5}"`} to owned keys.`
2316
2343
  );
2317
2344
  }
2318
- const same = join4 === PATH_JOIN ? (a, b) => posix3.normalize(a) === posix3.normalize(b) : (a, b) => a === b;
2345
+ const same = join5 === PATH_JOIN ? (a, b) => posix3.normalize(a) === posix3.normalize(b) : (a, b) => a === b;
2319
2346
  const [first, second] = root.items.filter((p) => same(keyString(p.key), entry));
2320
2347
  if (first && second) {
2321
2348
  throw new DocmetaError(
2322
2349
  `${at2(keyLine(second.key))}: "${keyString(second.key)}" is named twice (first at line ${String(keyLine(first.key) ?? "?")}). Merge the two entries into one.`
2323
2350
  );
2324
2351
  }
2325
- if (first === void 0) return { text };
2352
+ if (first === void 0) return { text: text2 };
2326
2353
  const taken = root.items.find((p) => same(keyString(p.key), to));
2327
2354
  if (taken !== void 0) {
2328
2355
  throw new DocmetaError(
@@ -2331,8 +2358,8 @@ function renameManifestEntry(text, options) {
2331
2358
  }
2332
2359
  const spelled = keyString(first.key);
2333
2360
  const r = rangeOf(first.key);
2334
- const end = contentEnd(text, first.key);
2335
- if (!r || end === void 0 || colonEnd(text, first) === void 0) {
2361
+ const end = contentEnd(text2, first.key);
2362
+ if (!r || end === void 0 || colonEnd(text2, first) === void 0) {
2336
2363
  throw new DocmetaError(
2337
2364
  `${at2(keyLine(first.key))}: "${spelled}" is written in a form this writer does not edit. Write it as "${spelled}: \u2026".`
2338
2365
  );
@@ -2340,7 +2367,7 @@ function renameManifestEntry(text, options) {
2340
2367
  const quoted = isScalar2(first.key) && (first.key.type === "QUOTE_DOUBLE" || first.key.type === "QUOTE_SINGLE");
2341
2368
  const plain = stringify2(to, { lineWidth: 0 }).replace(/\n$/, "");
2342
2369
  const insert = quoted || plain.includes("\n") ? JSON.stringify(to) : plain;
2343
- const out = text.slice(0, r[0]) + insert + text.slice(end);
2370
+ const out = text2.slice(0, r[0]) + insert + text2.slice(end);
2344
2371
  const after = parseDocument3(out, { uniqueKeys: false });
2345
2372
  const fail = (why) => new DocmetaError(
2346
2373
  `${at2()}: renaming "${spelled}" to "${to}" did not read back as renamed, so nothing was written. ${why}`
@@ -2355,25 +2382,25 @@ function renameManifestEntry(text, options) {
2355
2382
  if (!deepEqual(now, expected)) throw fail("Another value in the manifest would have changed.");
2356
2383
  return { text: out };
2357
2384
  }
2358
- function startManifest(text, entry, key, value, eol) {
2385
+ function startManifest(text2, entry, key, value, eol) {
2359
2386
  const style = { step: 2, indentSeq: true, eol };
2360
2387
  const block = blockLines({ [entry]: { [key]: value } }, 0, style);
2361
2388
  let insert;
2362
- if (text === "" || text.endsWith("\n")) insert = block + eol;
2389
+ if (text2 === "" || text2.endsWith("\n")) insert = block + eol;
2363
2390
  else insert = eol + block;
2364
- return { start: text.length, end: text.length, insert };
2391
+ return { start: text2.length, end: text2.length, insert };
2365
2392
  }
2366
- function appendEntry(text, root, entry, key, value, style) {
2393
+ function appendEntry(text2, root, entry, key, value, style) {
2367
2394
  const body = { [entry]: { [key]: value } };
2368
2395
  if (root.flow === true) {
2369
2396
  const r = rangeOf(root);
2370
- const start = r ? r[0] : text.length;
2371
- const end = contentEnd(text, root) ?? start;
2372
- return { start, end, insert: blockLines(body, colOf(text, start), style) };
2397
+ const start = r ? r[0] : text2.length;
2398
+ const end = contentEnd(text2, root) ?? start;
2399
+ return { start, end, insert: blockLines(body, colOf(text2, start), style) };
2373
2400
  }
2374
2401
  const firstKey = rangeOf(root.items[0]?.key);
2375
- const col = firstKey ? colOf(text, firstKey[0]) : 0;
2376
- const pos = lineEnd(text, contentEnd(text, root) ?? text.length);
2402
+ const col = firstKey ? colOf(text2, firstKey[0]) : 0;
2403
+ const pos = lineEnd(text2, contentEnd(text2, root) ?? text2.length);
2377
2404
  const last = root.items.at(-1);
2378
2405
  const spaced = root.items.length > 1 && isNode2(last?.key) && last.key.spaceBefore === true;
2379
2406
  return {
@@ -2382,12 +2409,12 @@ function appendEntry(text, root, entry, key, value, style) {
2382
2409
  insert: style.eol + (spaced ? style.eol : "") + blockLines(body, col, style)
2383
2410
  };
2384
2411
  }
2385
- function fillEmptyEntry(text, entryPair, key, value, style) {
2386
- const start = colonEnd(text, entryPair);
2412
+ function fillEmptyEntry(text2, entryPair, key, value, style) {
2413
+ const start = colonEnd(text2, entryPair);
2387
2414
  const keyRange = rangeOf(entryPair.key);
2388
- const end = contentEnd(text, entryPair.value);
2415
+ const end = contentEnd(text2, entryPair.value);
2389
2416
  if (start === void 0 || !keyRange || end === void 0) return void 0;
2390
- const col = colOf(text, keyRange[0]) + style.step;
2417
+ const col = colOf(text2, keyRange[0]) + style.step;
2391
2418
  return { start, end, insert: style.eol + blockLines({ [key]: value }, col, style) };
2392
2419
  }
2393
2420
  function readBack(before, out, spelled, key, value, fail) {
@@ -2428,17 +2455,17 @@ function isRecord3(x) {
2428
2455
  function relocateFailed(result) {
2429
2456
  return result.summary.stayed > 0;
2430
2457
  }
2431
- function relocationContext(run, extras) {
2458
+ function relocationContext(run2, extras) {
2432
2459
  return {
2433
2460
  cwd: extras.cwd,
2434
- base: run.base,
2461
+ base: run2.base,
2435
2462
  noConfig: extras.noConfig ?? false,
2436
- config: run.config,
2437
- ...run.configDir !== void 0 ? { configDir: run.configDir } : {},
2438
- ...run.configPath !== void 0 ? { configPath: run.configPath } : {},
2439
- ...run.configFile !== void 0 ? { configFile: run.configFile } : {},
2440
- collections: run.collections,
2441
- declaredCollections: run.declaredCollections,
2463
+ config: run2.config,
2464
+ ...run2.configDir !== void 0 ? { configDir: run2.configDir } : {},
2465
+ ...run2.configPath !== void 0 ? { configPath: run2.configPath } : {},
2466
+ ...run2.configFile !== void 0 ? { configFile: run2.configFile } : {},
2467
+ collections: run2.collections,
2468
+ declaredCollections: run2.declaredCollections,
2442
2469
  targets: extras.targets.filter((t) => t !== STDIN_TOKEN),
2443
2470
  ...extras.cliSchemas !== void 0 ? { cliSchemas: extras.cliSchemas } : {},
2444
2471
  validator: extras.validator,
@@ -2677,17 +2704,17 @@ function bare(path) {
2677
2704
  }
2678
2705
  async function collectionPath(target, cwd, configDir) {
2679
2706
  if (target === STDIN_TOKEN) return void 0;
2680
- const posix4 = target.replace(/\\/g, "/");
2681
- const scanned = picomatch.scan(posix4);
2707
+ const posix5 = target.replace(/\\/g, "/");
2708
+ const scanned = picomatch.scan(posix5);
2682
2709
  if (scanned.isGlob) {
2683
2710
  const rel2 = toPosix(relative4(configDir, resolve3(cwd, scanned.base === "" ? "." : scanned.base)));
2684
2711
  if (isAbsolute3(rel2) || rel2 === ".." || rel2.startsWith("../")) return void 0;
2685
2712
  return rel2 === "" ? scanned.glob : `${rel2}/${scanned.glob}`;
2686
2713
  }
2687
- const abs = resolve3(cwd, posix4);
2714
+ const abs = resolve3(cwd, posix5);
2688
2715
  const rel = toPosix(relative4(configDir, abs));
2689
2716
  if (isAbsolute3(rel) || rel.startsWith("..")) return void 0;
2690
- let directory = posix4.endsWith("/");
2717
+ let directory = posix5.endsWith("/");
2691
2718
  if (!directory) {
2692
2719
  try {
2693
2720
  directory = (await stat2(abs)).isDirectory();
@@ -3062,9 +3089,9 @@ async function planRelocation(ctx, request) {
3062
3089
  }
3063
3090
  const orderedPages = () => {
3064
3091
  const all = [...pages.values()];
3065
- const run = all.filter((p) => !p.beyond).sort((a, b) => compare(a.label, b.label));
3092
+ const run2 = all.filter((p) => !p.beyond).sort((a, b) => compare(a.label, b.label));
3066
3093
  const beyond = all.filter((p) => p.beyond).sort((a, b) => compare(a.label, b.label));
3067
- return [...run, ...beyond];
3094
+ return [...run2, ...beyond];
3068
3095
  };
3069
3096
  let dirty = new Set(pages.values());
3070
3097
  while (dirty.size > 0) {
@@ -3126,20 +3153,20 @@ async function planRelocation(ctx, request) {
3126
3153
  for (const [path, { file, outs, ins }] of files) {
3127
3154
  if (!m.created && outs.length === 0 && ins.length === 0) continue;
3128
3155
  const disk = m.created ? null : await readManifestText(path, file, m.perPage);
3129
- let text = disk ?? "";
3130
- const before = text;
3156
+ let text2 = disk ?? "";
3157
+ const before = text2;
3131
3158
  const where = { join: m.join, file };
3132
3159
  for (const o of outs) {
3133
- if (!o.drop) text = spliceManifestValue(text, { ...where, entry: o.entry, key: o.key, value: o.value }).text;
3160
+ if (!o.drop) text2 = spliceManifestValue(text2, { ...where, entry: o.entry, key: o.key, value: o.value }).text;
3134
3161
  }
3135
- for (const i of ins) text = removeManifestKey(text, { ...where, entry: i.entry, key: i.key }).text;
3136
- const found = keyLines2(text, m.join);
3162
+ for (const i of ins) text2 = removeManifestKey(text2, { ...where, entry: i.entry, key: i.key }).text;
3163
+ const found = keyLines2(text2, m.join);
3137
3164
  for (const o of outs) {
3138
3165
  const line = found.get(entryId(o.entry, m.join))?.get(o.key);
3139
3166
  if (line !== void 0) lines.set(lineId(o.page, file, o.key), line);
3140
3167
  }
3141
- if (m.created || text !== before) {
3142
- writes.push({ path, text, kind: "manifest", before: disk });
3168
+ if (m.created || text2 !== before) {
3169
+ writes.push({ path, text: text2, kind: "manifest", before: disk });
3143
3170
  }
3144
3171
  }
3145
3172
  }
@@ -3153,9 +3180,9 @@ async function planRelocation(ctx, request) {
3153
3180
  `${ctx.configFile.source} is a single-tool config file, which cannot hold collections:. Move its keys under meta: in ${FAMILY_CONFIG_NAMES[0] ?? "manni.config.yaml"}.`
3154
3181
  );
3155
3182
  }
3156
- const text = editConfig(target.text, model, configSource(target.path, ctx.cwd));
3157
- verifyConfig(target.text, text, finalCollections, configSource(target.path, ctx.cwd));
3158
- writes.push({ path: target.path, text, kind: "config", before: target.disk });
3183
+ const text2 = editConfig(target.text, model, configSource(target.path, ctx.cwd));
3184
+ verifyConfig(target.text, text2, finalCollections, configSource(target.path, ctx.cwd));
3185
+ writes.push({ path: target.path, text: text2, kind: "config", before: target.disk });
3159
3186
  configCreated = target.text === null;
3160
3187
  }
3161
3188
  for (const page of orderedPages()) {
@@ -3270,12 +3297,12 @@ async function readManifestText(path, file, perPage) {
3270
3297
  throw new DocmetaError(`Manifest ${file} could not be read: ${errorMessage(err)}`);
3271
3298
  }
3272
3299
  }
3273
- var entryId = (entry, join4) => join4 === PATH_JOIN ? toPosix(entry).replace(/^\.\//, "").replace(/\/+/g, "/") : entry;
3300
+ var entryId = (entry, join5) => join5 === PATH_JOIN ? toPosix(entry).replace(/^\.\//, "").replace(/\/+/g, "/") : entry;
3274
3301
  var lineId = (page, manifest, key) => `${page}\0${manifest}\0${key}`;
3275
- function keyLines2(text, join4) {
3302
+ function keyLines2(text2, join5) {
3276
3303
  const out = /* @__PURE__ */ new Map();
3277
3304
  const lc = new LineCounter3();
3278
- const doc = parseDocument4(text, { lineCounter: lc, uniqueKeys: false });
3305
+ const doc = parseDocument4(text2, { lineCounter: lc, uniqueKeys: false });
3279
3306
  const root = doc.contents;
3280
3307
  if (!isMap4(root)) return out;
3281
3308
  for (const pair of root.items) {
@@ -3287,15 +3314,15 @@ function keyLines2(text, join4) {
3287
3314
  const range = isNode3(kv.key) ? kv.key.range : void 0;
3288
3315
  if (range) keys.set(name, lc.linePos(range[0]).line);
3289
3316
  }
3290
- out.set(entryId(spelled, join4), keys);
3317
+ out.set(entryId(spelled, join5), keys);
3291
3318
  }
3292
3319
  return out;
3293
3320
  }
3294
3321
  async function configTarget(ctx, configDir) {
3295
3322
  if (ctx.configPath !== void 0) {
3296
3323
  try {
3297
- const text = await readFile4(ctx.configPath, "utf8");
3298
- return { path: ctx.configPath, text, disk: text };
3324
+ const text2 = await readFile4(ctx.configPath, "utf8");
3325
+ return { path: ctx.configPath, text: text2, disk: text2 };
3299
3326
  } catch (err) {
3300
3327
  if (err.code !== "ENOENT") {
3301
3328
  throw new DocmetaError(
@@ -3308,15 +3335,15 @@ async function configTarget(ctx, configDir) {
3308
3335
  for (const name of FAMILY_CONFIG_NAMES) {
3309
3336
  const path = join3(configDir, name);
3310
3337
  try {
3311
- const text = await readFile4(path, "utf8");
3312
- return { path, text, disk: text };
3338
+ const text2 = await readFile4(path, "utf8");
3339
+ return { path, text: text2, disk: text2 };
3313
3340
  } catch {
3314
3341
  }
3315
3342
  }
3316
3343
  return { path: join3(configDir, FAMILY_CONFIG_NAMES[0] ?? "manni.config.yaml"), text: null, disk: null };
3317
3344
  }
3318
- function editConfig(text, model, source) {
3319
- const doc = parseDocument4(text ?? "");
3345
+ function editConfig(text2, model, source) {
3346
+ const doc = parseDocument4(text2 ?? "");
3320
3347
  const [problem] = doc.errors;
3321
3348
  if (problem !== void 0) throw new DocmetaError(`${source}: invalid YAML: ${problem.message}`);
3322
3349
  if (doc.contents !== null && !isMap4(doc.contents)) {
@@ -3488,7 +3515,7 @@ function buildResult(input) {
3488
3515
  };
3489
3516
  }
3490
3517
  async function applyRelocation(ctx, plan, io = {}) {
3491
- const write = io.write ?? ((path, text) => writeFileAtomic(path, text, { createParents: true }));
3518
+ const write = io.write ?? ((path, text2) => writeFileAtomic(path, text2, { createParents: true }));
3492
3519
  const remove = io.remove ?? ((path) => unlink(path));
3493
3520
  const label = (w) => w.kind === "config" ? configSource(w.path, ctx.cwd) : reported2(w.path, ctx.base);
3494
3521
  for (const w of plan.writes) {
@@ -3660,6 +3687,12 @@ function relocationOffers(plan) {
3660
3687
  return plan.offers;
3661
3688
  }
3662
3689
 
3690
+ // src/meta/commands/validate.ts
3691
+ import { resolve as resolve7, extname as extname3 } from "node:path";
3692
+
3693
+ // src/meta/core/derive/provenance.ts
3694
+ import picomatch2 from "picomatch";
3695
+
3663
3696
  // src/shared/pin.ts
3664
3697
  var LineRangeError = class extends ToolError {
3665
3698
  constructor(message) {
@@ -3667,12 +3700,12 @@ var LineRangeError = class extends ToolError {
3667
3700
  this.name = "LineRangeError";
3668
3701
  }
3669
3702
  };
3670
- function normalizeText(text) {
3671
- const body = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
3703
+ function normalizeText(text2) {
3704
+ const body = text2.charCodeAt(0) === 65279 ? text2.slice(1) : text2;
3672
3705
  return body.replace(/\r\n/g, "\n");
3673
3706
  }
3674
- function splitLines(text) {
3675
- const parts = normalizeText(text).split("\n");
3707
+ function splitLines(text2) {
3708
+ const parts = normalizeText(text2).split("\n");
3676
3709
  if (parts.length > 0 && parts[parts.length - 1] === "") parts.pop();
3677
3710
  return parts;
3678
3711
  }
@@ -3689,8 +3722,8 @@ function sliceLines(lines, range, label) {
3689
3722
  }
3690
3723
  return lines.slice(start - 1, end).join("\n");
3691
3724
  }
3692
- function hashRange(text, range, key) {
3693
- return hashLines(sliceLines(splitLines(text), range), key);
3725
+ function hashRange(text2, range, key) {
3726
+ return hashLines(sliceLines(splitLines(text2), range), key);
3694
3727
  }
3695
3728
  function hashLines(joined, key) {
3696
3729
  return key === void 0 ? integrityOf(Buffer.from(joined, "utf8")) : keyedPin(joined, key);
@@ -3737,14 +3770,14 @@ function findWindows(lines, length, pin, key, opts) {
3737
3770
  for (let start = bandEnd + 1; start <= lastStart; start++) order.push(start);
3738
3771
  let spent = 0;
3739
3772
  for (const start of order) {
3740
- const text = joined(start);
3741
- const bytes = Buffer.byteLength(text, "utf8");
3773
+ const text2 = joined(start);
3774
+ const bytes = Buffer.byteLength(text2, "utf8");
3742
3775
  if (spent + bytes > budget) {
3743
3776
  if (counter !== void 0) counter.left = budget - spent;
3744
3777
  return { starts, truncated: true };
3745
3778
  }
3746
3779
  spent += bytes;
3747
- if (hashLines(text, key) === pin) starts.push(start);
3780
+ if (hashLines(text2, key) === pin) starts.push(start);
3748
3781
  }
3749
3782
  if (counter !== void 0) counter.left = budget - spent;
3750
3783
  return { starts, truncated: false };
@@ -3775,9 +3808,9 @@ function toBodyLines(lines, bodyLine) {
3775
3808
  return { start: lines.start - bodyLine + 1, end: lines.end - bodyLine + 1 };
3776
3809
  }
3777
3810
  function splitPageArgument(arg) {
3778
- const text = arg.startsWith(STDIN_LINES_MARKER) ? `-${arg.slice(STDIN_LINES_MARKER.length)}` : arg;
3779
- const m = /:([1-9][0-9]*)(?:-([1-9][0-9]*))?$/.exec(text);
3780
- const head = m === null ? text : text.slice(0, m.index);
3811
+ const text2 = arg.startsWith(STDIN_LINES_MARKER) ? `-${arg.slice(STDIN_LINES_MARKER.length)}` : arg;
3812
+ const m = /:([1-9][0-9]*)(?:-([1-9][0-9]*))?$/.exec(text2);
3813
+ const head = m === null ? text2 : text2.slice(0, m.index);
3781
3814
  if (m === null || head === "") return { page: arg };
3782
3815
  const start = Number(m[1]);
3783
3816
  const end = m[2] === void 0 ? start : Number(m[2]);
@@ -3788,7 +3821,6 @@ function splitPageArgument(arg) {
3788
3821
  }
3789
3822
 
3790
3823
  // src/meta/core/derive/provenance.ts
3791
- import picomatch2 from "picomatch";
3792
3824
  var ZERO_SHA = "0000000000000000000000000000000000000000";
3793
3825
  var DEFAULT_MACHINES = ["*[bot]"];
3794
3826
  var PROVENANCE_POINTER = "/provenance";
@@ -3843,8 +3875,8 @@ function readProvenancePage(content, opts) {
3843
3875
  }
3844
3876
  var HEADER = /^([0-9a-f]{40}(?:[0-9a-f]{24})?) ([1-9][0-9]*) ([1-9][0-9]*)(?: ([1-9][0-9]*))?$/;
3845
3877
  var ALL_ZERO = /^0+$/;
3846
- function parseLinePorcelain(text) {
3847
- const rows = text.split("\n");
3878
+ function parseLinePorcelain(text2) {
3879
+ const rows = text2.split("\n");
3848
3880
  if (rows.length > 0 && rows[rows.length - 1] === "") rows.pop();
3849
3881
  const out = [];
3850
3882
  let i = 0;
@@ -4267,7 +4299,2940 @@ function attributeRange(input) {
4267
4299
  return { "generated-by": input.generatedBy, lines: lineSpec(span), integrity: pinOf(page.body, span) };
4268
4300
  }
4269
4301
 
4302
+ // src/meta/core/derive/config.ts
4303
+ var DEFAULT_COMMAND_TIMEOUT_MS = 6e4;
4304
+ function commandsOf(config) {
4305
+ if (config?.commands === void 0) return void 0;
4306
+ const out = {};
4307
+ for (const [field, c] of Object.entries(config.commands)) {
4308
+ out[field] = {
4309
+ run: c.run,
4310
+ timeoutMs: c.timeout === void 0 ? DEFAULT_COMMAND_TIMEOUT_MS : Math.round(c.timeout * 1e3)
4311
+ };
4312
+ }
4313
+ return out;
4314
+ }
4315
+ function machinesOf(config) {
4316
+ return config?.machines ?? DEFAULT_MACHINES;
4317
+ }
4318
+
4319
+ // src/meta/core/derive/index.ts
4320
+ import { dirname as dirname5, resolve as resolve5 } from "node:path";
4321
+
4322
+ // src/meta/core/derive/cache.ts
4323
+ import { createHash } from "node:crypto";
4324
+ import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
4325
+ import { join as join4 } from "node:path";
4326
+ var REVIEW_CACHE_DIR = ".manni/meta/review-cache";
4327
+ var REVIEW_CACHE_VERSION = 1;
4328
+ var ReviewCache = class {
4329
+ constructor(dir, enabled) {
4330
+ this.dir = dir;
4331
+ this.enabled = enabled;
4332
+ }
4333
+ dir;
4334
+ enabled;
4335
+ entryPath(key) {
4336
+ const digest = createHash("sha256").update(key).digest("hex");
4337
+ return join4(this.dir, `${digest}.json`);
4338
+ }
4339
+ /** The cached change, or `undefined` for a miss. Never `null`: nulls are not stored. */
4340
+ get(key) {
4341
+ if (!this.enabled) return void 0;
4342
+ let raw;
4343
+ try {
4344
+ raw = JSON.parse(readFileSync(this.entryPath(key), "utf8"));
4345
+ } catch {
4346
+ return void 0;
4347
+ }
4348
+ if (!isRecord4(raw)) return void 0;
4349
+ if (raw.version !== REVIEW_CACHE_VERSION || raw.key !== key) return void 0;
4350
+ const change = readChange(raw.change);
4351
+ return change ?? void 0;
4352
+ }
4353
+ set(key, value) {
4354
+ if (!this.enabled) return;
4355
+ const entry = { version: REVIEW_CACHE_VERSION, key, change: value };
4356
+ const path = this.entryPath(key);
4357
+ const tmp = `${path}.${process.pid}-${Math.random().toString(36).slice(2, 8)}.tmp`;
4358
+ try {
4359
+ mkdirSync(this.dir, { recursive: true });
4360
+ writeFileSync(tmp, `${JSON.stringify(entry, null, 2)}
4361
+ `, "utf8");
4362
+ renameSync(tmp, path);
4363
+ } catch {
4364
+ try {
4365
+ rmSync(tmp, { force: true });
4366
+ } catch {
4367
+ }
4368
+ }
4369
+ }
4370
+ };
4371
+ function cachedClient(client, cache, identity) {
4372
+ return {
4373
+ detect: () => client.detect(),
4374
+ status: () => client.status(),
4375
+ async mergedChangeFor(sha) {
4376
+ if (identity === null) return client.mergedChangeFor(sha);
4377
+ const key = `${identity.host}/${identity.project}/${sha}`;
4378
+ const hit = cache.get(key);
4379
+ if (hit !== void 0) return hit;
4380
+ const change = await client.mergedChangeFor(sha);
4381
+ if (change !== null) cache.set(key, change);
4382
+ return change;
4383
+ }
4384
+ };
4385
+ }
4386
+ function isRecord4(v) {
4387
+ return typeof v === "object" && v !== null && !Array.isArray(v);
4388
+ }
4389
+ function readChange(v) {
4390
+ if (!isRecord4(v)) return null;
4391
+ const { id, mergedAt, approvals } = v;
4392
+ if (typeof id !== "number" || typeof mergedAt !== "string") return null;
4393
+ if (!Array.isArray(approvals)) return null;
4394
+ const read = [];
4395
+ for (const a of approvals) {
4396
+ if (!isRecord4(a)) return null;
4397
+ if (typeof a.login !== "string" || typeof a.submittedAt !== "string") return null;
4398
+ read.push({ login: a.login, submittedAt: a.submittedAt });
4399
+ }
4400
+ return { id, mergedAt, approvals: read };
4401
+ }
4402
+
4403
+ // src/meta/core/derive/codeowners.ts
4404
+ import { existsSync } from "node:fs";
4405
+ import { readFile as readFile5 } from "node:fs/promises";
4406
+ import { dirname as dirname3, relative as relative5, resolve as resolve4, sep as sep4 } from "node:path";
4407
+ import picomatch3 from "picomatch";
4408
+ var CODEOWNERS_LOCATIONS = [
4409
+ ".github/CODEOWNERS",
4410
+ "CODEOWNERS",
4411
+ "docs/CODEOWNERS",
4412
+ ".gitlab/CODEOWNERS"
4413
+ ];
4414
+ function findCodeowners(root, explicit) {
4415
+ if (explicit !== void 0) {
4416
+ const path = resolve4(root, explicit);
4417
+ return existsSync(path) ? path : null;
4418
+ }
4419
+ for (const loc of CODEOWNERS_LOCATIONS) {
4420
+ const path = resolve4(root, ...loc.split("/"));
4421
+ if (existsSync(path)) return path;
4422
+ }
4423
+ return null;
4424
+ }
4425
+ var SECTION_HEADER = /^(\^?)\[([^\]]+)\](?:\[(\d+)\])?(.*)$/;
4426
+ var TOKEN = /(?:\\.|[^\s\\])+/g;
4427
+ function parseCodeowners(text2, path, label) {
4428
+ const rules = [];
4429
+ const sections = [];
4430
+ let current;
4431
+ const lines = text2.split(/\r?\n/);
4432
+ for (let i = 0; i < lines.length; i += 1) {
4433
+ const raw = lines[i] ?? "";
4434
+ const line = raw.trim();
4435
+ if (line === "" || line.startsWith("#")) continue;
4436
+ if (line.startsWith("[") || line.startsWith("^[")) {
4437
+ const m = SECTION_HEADER.exec(line);
4438
+ if (!m) continue;
4439
+ const [, caret, rawName, approvals, rest] = m;
4440
+ const name = (rawName ?? "").trim();
4441
+ if (name === "") continue;
4442
+ const owners2 = tokens(rest ?? "");
4443
+ const existing = sections.find(
4444
+ (s) => s.name.toLowerCase() === name.toLowerCase()
4445
+ );
4446
+ if (existing) {
4447
+ if (owners2.length > 0) existing.defaultOwners = owners2;
4448
+ current = existing;
4449
+ continue;
4450
+ }
4451
+ const section = {
4452
+ name,
4453
+ optional: caret === "^",
4454
+ ...approvals !== void 0 ? { approvals: Number(approvals) } : {},
4455
+ defaultOwners: owners2
4456
+ };
4457
+ sections.push(section);
4458
+ current = section;
4459
+ continue;
4460
+ }
4461
+ const [first, ...owners] = tokens(line);
4462
+ if (first === void 0) continue;
4463
+ const pattern = unescapePattern(first);
4464
+ if (pattern === "" || pattern === "/") continue;
4465
+ rules.push({
4466
+ pattern,
4467
+ owners,
4468
+ line: i + 1,
4469
+ ...current ? { section: current.name } : {}
4470
+ });
4471
+ }
4472
+ return { path, label, rules, sections };
4473
+ }
4474
+ function tokens(s) {
4475
+ return s.match(TOKEN) ?? [];
4476
+ }
4477
+ function unescapePattern(token) {
4478
+ return token.replace(/\\ /g, " ").replace(/^\\#/, "#");
4479
+ }
4480
+ function ownersFor(file, relPath) {
4481
+ const matchers = matchersFor(file);
4482
+ const defaults = new Map(file.sections.map((s) => [s.name, s.defaultOwners]));
4483
+ const order = [void 0, ...file.sections.map((s) => s.name)];
4484
+ const winners = /* @__PURE__ */ new Map();
4485
+ file.rules.forEach((rule, i) => {
4486
+ if (matchers[i]?.(relPath)) winners.set(rule.section, rule);
4487
+ });
4488
+ if (winners.size === 0) return null;
4489
+ const owners = [];
4490
+ let line = 0;
4491
+ let lineFromOwner = false;
4492
+ for (const section of order) {
4493
+ const rule = winners.get(section);
4494
+ if (!rule) continue;
4495
+ const chosen = rule.owners.length > 0 ? rule.owners : (section === void 0 ? void 0 : defaults.get(section)) ?? [];
4496
+ if (chosen.length > 0) {
4497
+ line = rule.line;
4498
+ lineFromOwner = true;
4499
+ } else if (!lineFromOwner) {
4500
+ line = rule.line;
4501
+ }
4502
+ for (const o of chosen) if (!owners.includes(o)) owners.push(o);
4503
+ }
4504
+ return { owners, line };
4505
+ }
4506
+ var matcherCache = /* @__PURE__ */ new WeakMap();
4507
+ function matchersFor(file) {
4508
+ let matchers = matcherCache.get(file);
4509
+ if (!matchers) {
4510
+ matchers = file.rules.map((r) => picomatch3(toGlobs(r.pattern), PICOMATCH));
4511
+ matcherCache.set(file, matchers);
4512
+ }
4513
+ return matchers;
4514
+ }
4515
+ var PICOMATCH = { dot: true, windows: false };
4516
+ function toGlobs(pattern) {
4517
+ let p = pattern;
4518
+ const dirOnly = p.endsWith("/");
4519
+ if (dirOnly) p = p.slice(0, -1);
4520
+ let anchored = false;
4521
+ if (p.startsWith("/")) {
4522
+ anchored = true;
4523
+ p = p.slice(1);
4524
+ } else if (p.includes("/") && !p.startsWith("**/")) {
4525
+ anchored = true;
4526
+ }
4527
+ const base = anchored || p.startsWith("**/") ? p : `**/${p}`;
4528
+ if (dirOnly) return [`${base}/**`];
4529
+ const last = p.slice(p.lastIndexOf("/") + 1);
4530
+ return /[*?[]/.test(last) ? [base] : [base, `${base}/**`];
4531
+ }
4532
+ var NOT_FOUND_REASON = `no CODEOWNERS file found (looked for ${CODEOWNERS_LOCATIONS.join(", ")})`;
4533
+ async function deriveFromCodeowners(inputs, opts) {
4534
+ const records = /* @__PURE__ */ new Map();
4535
+ const explicitPath = opts.explicit === void 0 ? void 0 : resolve4(opts.configDir ?? process.cwd(), opts.explicit);
4536
+ if (explicitPath !== void 0 && !existsSync(explicitPath)) {
4537
+ return {
4538
+ status: { available: false, reason: `CODEOWNERS not found at ${explicitPath}` },
4539
+ records
4540
+ };
4541
+ }
4542
+ const byRoot = /* @__PURE__ */ new Map();
4543
+ const rootCache = /* @__PURE__ */ new Map();
4544
+ for (const input of inputs) {
4545
+ const dir = dirname3(input.absPath);
4546
+ let root = rootCache.get(dir);
4547
+ if (root === void 0) {
4548
+ root = opts.rootOf(input.absPath);
4549
+ rootCache.set(dir, root);
4550
+ }
4551
+ if (root === null) continue;
4552
+ const bucket = byRoot.get(root);
4553
+ if (bucket) bucket.push(input);
4554
+ else byRoot.set(root, [input]);
4555
+ }
4556
+ let found = 0;
4557
+ const parsed = /* @__PURE__ */ new Map();
4558
+ for (const [root, bucket] of byRoot) {
4559
+ const path = explicitPath ?? findCodeowners(root);
4560
+ if (path === null) {
4561
+ for (const input of bucket) records.set(input.label, null);
4562
+ continue;
4563
+ }
4564
+ found += 1;
4565
+ const label = toPosix2(relative5(opts.configDir ?? root, path));
4566
+ const key = `${path}\0${label}`;
4567
+ let file = parsed.get(key);
4568
+ if (!file) {
4569
+ file = readFile5(path, "utf8").then(
4570
+ (text2) => parseCodeowners(text2, path, label)
4571
+ );
4572
+ parsed.set(key, file);
4573
+ }
4574
+ let owners;
4575
+ try {
4576
+ owners = await file;
4577
+ } catch (err) {
4578
+ return {
4579
+ status: {
4580
+ available: false,
4581
+ reason: `CODEOWNERS at ${label} could not be read: ${errorMessage(err)}`
4582
+ },
4583
+ records
4584
+ };
4585
+ }
4586
+ for (const input of bucket) {
4587
+ const rel = toPosix2(relative5(root, input.absPath));
4588
+ const hit = ownersFor(owners, rel);
4589
+ records.set(
4590
+ input.label,
4591
+ hit === null ? null : {
4592
+ value: hit.owners,
4593
+ source: "codeowners",
4594
+ evidence: `${label}:${hit.line}`
4595
+ }
4596
+ );
4597
+ }
4598
+ }
4599
+ if (byRoot.size === 0) {
4600
+ return {
4601
+ status: { available: false, reason: "no git repository contains the documents" },
4602
+ records
4603
+ };
4604
+ }
4605
+ if (found === 0 && explicitPath === void 0) {
4606
+ return { status: { available: true, reason: NOT_FOUND_REASON }, records };
4607
+ }
4608
+ return { status: { available: true }, records };
4609
+ }
4610
+ function toPosix2(p) {
4611
+ return p.split(sep4).join("/");
4612
+ }
4613
+
4614
+ // src/meta/core/derive/spawn.ts
4615
+ import { spawn } from "node:child_process";
4616
+ var DEFAULT_TIMEOUT_MS = 3e4;
4617
+ var MAX_OUTPUT_BYTES = 8 * 1024 * 1024;
4618
+ var BinMissing = class extends Error {
4619
+ };
4620
+ function run(bin, args, opts) {
4621
+ return new Promise((settle2, reject) => {
4622
+ let done = false;
4623
+ const finish = (r) => {
4624
+ if (done) return;
4625
+ done = true;
4626
+ settle2(r);
4627
+ };
4628
+ const fail = (err) => {
4629
+ if (done) return;
4630
+ done = true;
4631
+ reject(err);
4632
+ };
4633
+ let child;
4634
+ try {
4635
+ child = spawn(bin, [...opts.prefixArgs ?? [], ...args], {
4636
+ cwd: opts.cwd,
4637
+ windowsHide: true
4638
+ });
4639
+ } catch (err) {
4640
+ fail(err instanceof Error ? err : new Error(String(err)));
4641
+ return;
4642
+ }
4643
+ let stdout = "";
4644
+ let stderr = "";
4645
+ let timedOut = false;
4646
+ let tooLarge = false;
4647
+ const stop = (r) => {
4648
+ clearTimeout(timer);
4649
+ child.kill();
4650
+ setTimeout(() => child.kill("SIGKILL"), 1e3).unref();
4651
+ finish({ code: null, ...r });
4652
+ };
4653
+ child.stdout.setEncoding("utf8");
4654
+ child.stdout.on("data", (chunk) => {
4655
+ stdout += chunk;
4656
+ if (stdout.length > MAX_OUTPUT_BYTES) {
4657
+ tooLarge = true;
4658
+ stop({ stdout, stderr, timedOut, tooLarge });
4659
+ }
4660
+ });
4661
+ child.stderr.setEncoding("utf8");
4662
+ child.stderr.on("data", (chunk) => {
4663
+ stderr += chunk;
4664
+ if (stderr.length > MAX_OUTPUT_BYTES) {
4665
+ tooLarge = true;
4666
+ stop({ stdout, stderr, timedOut, tooLarge });
4667
+ }
4668
+ });
4669
+ child.stdin.end();
4670
+ const timer = setTimeout(() => {
4671
+ timedOut = true;
4672
+ stop({ stdout, stderr, timedOut, tooLarge });
4673
+ }, opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
4674
+ child.on("error", (err) => {
4675
+ clearTimeout(timer);
4676
+ fail(err.code === "ENOENT" ? new BinMissing(err.message) : err);
4677
+ });
4678
+ child.on("close", (code) => {
4679
+ clearTimeout(timer);
4680
+ finish({ code, stdout, stderr, timedOut, tooLarge });
4681
+ });
4682
+ });
4683
+ }
4684
+ function commandLine(bin, args) {
4685
+ return [bin, ...args].join(" ");
4686
+ }
4687
+ function lastLine(text2) {
4688
+ const lines = text2.split(/\r?\n/).filter((l) => l.trim() !== "");
4689
+ return lines.at(-1) ?? "";
4690
+ }
4691
+
4692
+ // src/meta/core/derive/command.ts
4693
+ var PATH_PLACEHOLDER = "{path}";
4694
+ function isPerFile(command) {
4695
+ return command.run.some((arg) => arg.includes(PATH_PLACEHOLDER));
4696
+ }
4697
+ function argvFor(command, label) {
4698
+ return command.run.map((arg) => arg.split(PATH_PLACEHOLDER).join(label));
4699
+ }
4700
+ function valueOf(stdout) {
4701
+ const text2 = stdout.trim();
4702
+ if (text2 === "") return null;
4703
+ if (!/^[{["]/.test(text2)) return text2;
4704
+ try {
4705
+ return JSON.parse(text2);
4706
+ } catch {
4707
+ return text2;
4708
+ }
4709
+ }
4710
+ async function deriveFromCommands(inputs, commands, opts) {
4711
+ const records = /* @__PURE__ */ new Map();
4712
+ const fields = Object.keys(commands);
4713
+ if (fields.length === 0 || inputs.length === 0) {
4714
+ return { status: { available: true }, records };
4715
+ }
4716
+ for (const input of inputs) records.set(input.label, {});
4717
+ for (const field of fields) {
4718
+ const command = commands[field];
4719
+ if (command === void 0) continue;
4720
+ if (isPerFile(command)) {
4721
+ for (const [label, record] of records) {
4722
+ const outcome = await runOne(field, argvFor(command, label), command.timeoutMs, opts);
4723
+ if ("reason" in outcome) return unavailable(outcome.reason);
4724
+ record[field] = outcome.value;
4725
+ }
4726
+ } else {
4727
+ const outcome = await runOne(field, command.run, command.timeoutMs, opts);
4728
+ if ("reason" in outcome) return unavailable(outcome.reason);
4729
+ for (const record of records.values()) record[field] = outcome.value;
4730
+ }
4731
+ }
4732
+ return { status: { available: true }, records };
4733
+ }
4734
+ function unavailable(reason) {
4735
+ return { status: { available: false, reason }, records: /* @__PURE__ */ new Map() };
4736
+ }
4737
+ async function runOne(field, argv, timeoutMs, opts) {
4738
+ const [program, ...args] = argv;
4739
+ const where = `(derive.commands.${field})`;
4740
+ if (program === void 0) {
4741
+ return { reason: `no program to run ${where}` };
4742
+ }
4743
+ const line = argv.join(" ");
4744
+ let result;
4745
+ try {
4746
+ result = await run(opts.spawn?.bin ?? program, args, {
4747
+ ...opts.spawn,
4748
+ cwd: opts.spawn?.cwd ?? opts.cwd,
4749
+ timeoutMs
4750
+ });
4751
+ } catch (err) {
4752
+ if (err instanceof BinMissing) {
4753
+ return { reason: `\`${program}\` is not on PATH ${where}` };
4754
+ }
4755
+ return { reason: `\`${line}\` could not be run: ${errorMessage(err)} ${where}` };
4756
+ }
4757
+ if (result.timedOut) {
4758
+ return { reason: `\`${line}\` timed out after ${String(timeoutMs / 1e3)}s ${where}` };
4759
+ }
4760
+ if (result.tooLarge) {
4761
+ return {
4762
+ reason: `\`${line}\` wrote more than ${String(MAX_OUTPUT_BYTES / (1024 * 1024))} MiB; a command reports one field's value ${where}`
4763
+ };
4764
+ }
4765
+ if (result.code !== 0) {
4766
+ const last = lastLine(result.stderr);
4767
+ const detail = last === "" ? "no output on stderr" : last;
4768
+ const exit = result.code === null ? "killed by a signal" : `exit ${String(result.code)}`;
4769
+ return { reason: `\`${line}\` failed (${exit}): ${detail} ${where}` };
4770
+ }
4771
+ const value = valueOf(result.stdout);
4772
+ return { value: value === null ? null : { value, source: "command", evidence: line } };
4773
+ }
4774
+
4775
+ // src/meta/core/derive/reviews.ts
4776
+ import { resolve as resolvePath2 } from "node:path";
4777
+ function parseOriginUrl(url) {
4778
+ const trimmed = url.trim();
4779
+ if (trimmed === "") return null;
4780
+ if (isLocalPath(trimmed)) return null;
4781
+ const scp = /^(?:[^@/:]+@)?([^/:]+):(?!\/\/)(.+)$/.exec(trimmed);
4782
+ if (scp) {
4783
+ const host = scp[1];
4784
+ const path = scp[2];
4785
+ if (host === void 0 || path === void 0) return null;
4786
+ if (/^[A-Za-z]$/.test(host)) return null;
4787
+ return project(host, path);
4788
+ }
4789
+ let parsed;
4790
+ try {
4791
+ parsed = new URL(trimmed);
4792
+ } catch {
4793
+ return null;
4794
+ }
4795
+ if (!/^(https?|ssh|git):$/.test(parsed.protocol)) return null;
4796
+ if (parsed.hostname === "") return null;
4797
+ return project(parsed.hostname, parsed.pathname);
4798
+ }
4799
+ function isLocalPath(s) {
4800
+ return s.startsWith("/") || s.startsWith("./") || s.startsWith("../") || s.startsWith("\\\\");
4801
+ }
4802
+ function project(host, path) {
4803
+ const segments2 = path.replace(/\.git$/, "").split("/").filter((s) => s !== "");
4804
+ if (segments2.length < 2) return null;
4805
+ return { host, project: segments2.join("/") };
4806
+ }
4807
+ function identityFromOrigin(host, project2, requested) {
4808
+ const name = host.toLowerCase();
4809
+ if (name.includes("github")) return { kind: "github", host, project: project2 };
4810
+ if (name.includes("gitlab")) return { kind: "gitlab", host, project: project2 };
4811
+ const only = requested !== void 0 && requested.length === 1 ? requested[0] : void 0;
4812
+ return only === void 0 ? null : { kind: only, host, project: project2 };
4813
+ }
4814
+ var UnknownHostClient = class {
4815
+ constructor(host) {
4816
+ this.host = host;
4817
+ }
4818
+ host;
4819
+ detect() {
4820
+ return Promise.resolve(null);
4821
+ }
4822
+ status() {
4823
+ return Promise.resolve({
4824
+ available: false,
4825
+ reason: `the origin remote is ${this.host}, which names neither GitHub nor GitLab; list exactly one of github, gitlab in derive.sources to say which it is`
4826
+ });
4827
+ }
4828
+ mergedChangeFor() {
4829
+ return Promise.resolve(null);
4830
+ }
4831
+ };
4832
+ function httpStatus(stderr) {
4833
+ const m = /\b(?:HTTP\s+(\d{3})|(\d{3})\s+Not\s+Found)\b/i.exec(stderr);
4834
+ const code = m?.[1] ?? m?.[2];
4835
+ return code === void 0 ? null : Number(code);
4836
+ }
4837
+ function isNotFound(r) {
4838
+ const status = httpStatus(r.stderr);
4839
+ return status === 404 || status === 422;
4840
+ }
4841
+ var CliClient = class {
4842
+ constructor(identity, spawnOpts) {
4843
+ this.identity = identity;
4844
+ this.spawnOpts = spawnOpts;
4845
+ }
4846
+ identity;
4847
+ spawnOpts;
4848
+ detect() {
4849
+ return Promise.resolve(this.identity);
4850
+ }
4851
+ async status() {
4852
+ if (this.identity === null) {
4853
+ return { available: false, reason: "no origin remote to tell GitHub from GitLab" };
4854
+ }
4855
+ const { host } = this.identity;
4856
+ let r;
4857
+ try {
4858
+ r = await this.run(["auth", "status", "--hostname", host]);
4859
+ } catch (err) {
4860
+ if (err instanceof BinMissing) {
4861
+ return {
4862
+ available: false,
4863
+ reason: `${this.tool} is not on PATH (origin is ${host}); install ${this.tool} and run ${this.tool} auth login`
4864
+ };
4865
+ }
4866
+ throw err;
4867
+ }
4868
+ if (r.timedOut) throw this.timeout(["auth", "status", "--hostname", host]);
4869
+ if (r.code !== 0) {
4870
+ return {
4871
+ available: false,
4872
+ reason: `${this.tool} is not authenticated for ${host}; run ${this.tool} auth login`
4873
+ };
4874
+ }
4875
+ return { available: true };
4876
+ }
4877
+ async mergedChangeFor(sha) {
4878
+ if (this.identity === null) return null;
4879
+ return this.lookup(this.identity, sha);
4880
+ }
4881
+ get bin() {
4882
+ return this.spawnOpts.bin ?? this.tool;
4883
+ }
4884
+ run(args) {
4885
+ return run(this.bin, args, this.spawnOpts);
4886
+ }
4887
+ timeout(args) {
4888
+ const ms = this.spawnOpts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
4889
+ return new DocmetaError(
4890
+ `${commandLine(this.tool, args)} timed out after ${ms}ms`
4891
+ );
4892
+ }
4893
+ /**
4894
+ * Run a call whose stdout is JSON. A not-found answer is `null`; any other
4895
+ * failure throws, naming the command the user can re-run by hand. The
4896
+ * message quotes the tool's own name rather than `bin`, which under test
4897
+ * is `node`.
4898
+ */
4899
+ async json(args) {
4900
+ let r;
4901
+ try {
4902
+ r = await this.run(args);
4903
+ } catch (err) {
4904
+ if (err instanceof BinMissing) {
4905
+ throw new DocmetaError(
4906
+ `${commandLine(this.tool, args)} failed: ${this.tool} is not on PATH`
4907
+ );
4908
+ }
4909
+ throw err;
4910
+ }
4911
+ const cmd = commandLine(this.tool, args);
4912
+ if (r.timedOut) throw this.timeout(args);
4913
+ if (r.tooLarge) {
4914
+ throw new DocmetaError(`${cmd} wrote more output than manni will read`);
4915
+ }
4916
+ if (r.code !== 0) {
4917
+ if (isNotFound(r)) return null;
4918
+ const detail = lastLine(r.stderr) || lastLine(r.stdout);
4919
+ const exit = r.code === null ? "signal" : `exit ${r.code}`;
4920
+ throw new DocmetaError(
4921
+ `${cmd} failed (${exit})${detail === "" ? "" : `: ${detail}`}`
4922
+ );
4923
+ }
4924
+ try {
4925
+ return JSON.parse(r.stdout);
4926
+ } catch {
4927
+ throw new DocmetaError(`${cmd} did not return JSON`);
4928
+ }
4929
+ }
4930
+ };
4931
+ function isRecord5(v) {
4932
+ return typeof v === "object" && v !== null && !Array.isArray(v);
4933
+ }
4934
+ function items(v) {
4935
+ return Array.isArray(v) ? v.filter(isRecord5) : [];
4936
+ }
4937
+ function numberOf(v) {
4938
+ return typeof v === "number" && Number.isInteger(v) ? v : null;
4939
+ }
4940
+ function stringOf(v) {
4941
+ return typeof v === "string" && v !== "" ? v : null;
4942
+ }
4943
+ function timestampOf(v) {
4944
+ if (v === void 0) return void 0;
4945
+ return stringOf(v);
4946
+ }
4947
+ function normalizeLogin(login) {
4948
+ return login.replace(/\[bot\]$/, "");
4949
+ }
4950
+ function latestApprovals(reviews) {
4951
+ const latest = /* @__PURE__ */ new Map();
4952
+ for (const r of reviews) {
4953
+ if (r.state !== "APPROVED") continue;
4954
+ const key = normalizeLogin(r.login);
4955
+ const seen = latest.get(key);
4956
+ if (seen === void 0 || r.submittedAt > seen.submittedAt) {
4957
+ latest.set(key, { login: r.login, submittedAt: r.submittedAt });
4958
+ }
4959
+ }
4960
+ return byTime([...latest.values()]);
4961
+ }
4962
+ function byTime(approvals) {
4963
+ return [...approvals].sort(
4964
+ (a, b) => a.submittedAt < b.submittedAt ? -1 : a.submittedAt > b.submittedAt ? 1 : 0
4965
+ );
4966
+ }
4967
+ var GitHubClient = class extends CliClient {
4968
+ tool = "gh";
4969
+ async lookup(identity, sha) {
4970
+ const { host, project: project2 } = identity;
4971
+ const api = (endpoint) => this.json(["api", "--hostname", host, endpoint]);
4972
+ let candidates = items(await api(`repos/${project2}/commits/${sha}/pulls`)).map(
4973
+ (p) => ({ number: numberOf(p.number), mergedAt: timestampOf(p.merged_at) })
4974
+ );
4975
+ if (candidates.length === 0) {
4976
+ const found = await this.json([
4977
+ "pr",
4978
+ "list",
4979
+ "--search",
4980
+ sha,
4981
+ "--state",
4982
+ "merged",
4983
+ "--json",
4984
+ "number,mergedAt",
4985
+ "-R",
4986
+ `${host}/${project2}`
4987
+ ]);
4988
+ candidates = items(found).map((p) => ({
4989
+ number: numberOf(p.number),
4990
+ mergedAt: timestampOf(p.mergedAt)
4991
+ }));
4992
+ }
4993
+ for (const c of candidates) {
4994
+ if (c.number === null) continue;
4995
+ let mergedAt = c.mergedAt;
4996
+ if (mergedAt === void 0) {
4997
+ const pr = await api(`repos/${project2}/pulls/${c.number}`);
4998
+ mergedAt = isRecord5(pr) ? stringOf(pr.merged_at) ?? null : null;
4999
+ }
5000
+ if (mergedAt === null) continue;
5001
+ const raw = [];
5002
+ for (let page = 1; ; page += 1) {
5003
+ const batch = items(
5004
+ await api(`repos/${project2}/pulls/${c.number}/reviews?per_page=100&page=${page}`)
5005
+ );
5006
+ raw.push(...batch);
5007
+ if (batch.length < 100) break;
5008
+ }
5009
+ const reviews = raw.map((r) => ({
5010
+ login: isRecord5(r.user) ? stringOf(r.user.login) : null,
5011
+ state: stringOf(r.state),
5012
+ submittedAt: stringOf(r.submitted_at)
5013
+ })).filter(
5014
+ (r) => r.login !== null && r.state !== null && r.submittedAt !== null
5015
+ );
5016
+ return { id: c.number, mergedAt, approvals: latestApprovals(reviews) };
5017
+ }
5018
+ return null;
5019
+ }
5020
+ };
5021
+ var GitLabClient = class extends CliClient {
5022
+ tool = "glab";
5023
+ async lookup(identity, sha) {
5024
+ const { host } = identity;
5025
+ const api = (endpoint) => this.json(["api", "--hostname", host, endpoint]);
5026
+ const merged = items(
5027
+ await api(`projects/:fullpath/repository/commits/${sha}/merge_requests`)
5028
+ ).map((mr) => ({ iid: numberOf(mr.iid), mergedAt: stringOf(mr.merged_at) })).find(
5029
+ (mr) => mr.iid !== null && mr.mergedAt !== null
5030
+ );
5031
+ if (merged === void 0) return null;
5032
+ const answer = await api(`projects/:fullpath/merge_requests/${merged.iid}/approvals`);
5033
+ const approvedBy = isRecord5(answer) ? items(answer.approved_by) : [];
5034
+ const approvals = [];
5035
+ const seen = /* @__PURE__ */ new Set();
5036
+ for (const entry of approvedBy) {
5037
+ const login = isRecord5(entry.user) ? stringOf(entry.user.username) : null;
5038
+ if (login === null) continue;
5039
+ const key = normalizeLogin(login);
5040
+ if (seen.has(key)) continue;
5041
+ seen.add(key);
5042
+ approvals.push({ login, submittedAt: merged.mergedAt });
5043
+ }
5044
+ return { id: merged.iid, mergedAt: merged.mergedAt, approvals };
5045
+ }
5046
+ };
5047
+ async function createReviewClient(root, opts) {
5048
+ const spawnOpts = { ...opts.spawn, cwd: opts.spawn?.cwd ?? root };
5049
+ const origin = await originUrl(root);
5050
+ const parsed = origin === null ? null : parseOriginUrl(origin);
5051
+ const identity = parsed === null ? null : identityFromOrigin(parsed.host, parsed.project, opts.requested);
5052
+ if (parsed !== null && identity === null) return new UnknownHostClient(parsed.host);
5053
+ const client = identity?.kind === "gitlab" ? new GitLabClient(identity, spawnOpts) : new GitHubClient(identity, spawnOpts);
5054
+ const cache = new ReviewCache(resolvePath2(root, opts.cacheDir), opts.cache);
5055
+ return cachedClient(client, cache, identity);
5056
+ }
5057
+ async function originUrl(root) {
5058
+ try {
5059
+ const r = await run("git", ["remote", "get-url", "origin"], { cwd: root });
5060
+ if (r.code !== 0 || r.timedOut || r.tooLarge) return null;
5061
+ return stringOf(r.stdout.trim());
5062
+ } catch {
5063
+ return null;
5064
+ }
5065
+ }
5066
+ var NOTHING = { "reviewed-by": null, "last-reviewed": null };
5067
+ async function deriveFromReviews(inputs, client) {
5068
+ const records = /* @__PURE__ */ new Map();
5069
+ const status = await client.status();
5070
+ if (!status.available) return { status, records };
5071
+ const identity = await client.detect();
5072
+ const kind = identity?.kind ?? "github";
5073
+ const bySha = /* @__PURE__ */ new Map();
5074
+ const changeFor = (sha) => {
5075
+ let pending = bySha.get(sha);
5076
+ if (pending === void 0) {
5077
+ pending = client.mergedChangeFor(sha);
5078
+ bySha.set(sha, pending);
5079
+ }
5080
+ return pending;
5081
+ };
5082
+ for (const input of inputs) {
5083
+ if (input.sha === null) {
5084
+ records.set(input.label, NOTHING);
5085
+ continue;
5086
+ }
5087
+ const change = await changeFor(input.sha);
5088
+ records.set(input.label, factsFrom(change, kind));
5089
+ }
5090
+ return { status, records };
5091
+ }
5092
+ function factsFrom(change, kind) {
5093
+ if (change === null || change.approvals.length === 0) return NOTHING;
5094
+ const ordered = byTime(change.approvals);
5095
+ const latest = ordered.at(-1);
5096
+ if (latest === void 0) return NOTHING;
5097
+ const evidence = kind === "gitlab" ? `gitlab MR !${change.id}` : `github PR #${change.id}`;
5098
+ const dateEvidence = kind === "gitlab" ? `${evidence} (merge date; GitLab records no approval time)` : evidence;
5099
+ return {
5100
+ "reviewed-by": {
5101
+ value: ordered.map((a) => a.login),
5102
+ source: kind,
5103
+ evidence
5104
+ },
5105
+ "last-reviewed": {
5106
+ value: utcDate(latest.submittedAt),
5107
+ source: kind,
5108
+ evidence: dateEvidence
5109
+ }
5110
+ };
5111
+ }
5112
+ function utcDate(iso) {
5113
+ const ms = Date.parse(iso);
5114
+ if (Number.isNaN(ms)) return iso;
5115
+ return new Date(ms).toISOString().slice(0, 10);
5116
+ }
5117
+
5118
+ // src/meta/core/derive/git.ts
5119
+ import { constants as bufferConstants } from "node:buffer";
5120
+ import { spawn as spawn2 } from "node:child_process";
5121
+ import { availableParallelism } from "node:os";
5122
+ import { dirname as dirname4, extname as extname2, isAbsolute as isAbsolute4, posix as posix4, relative as relative6, sep as sep5 } from "node:path";
5123
+ import { parse as parseYaml3 } from "yaml";
5124
+
5125
+ // src/shared/concurrency.ts
5126
+ async function mapConcurrent(items2, limit, fn) {
5127
+ const out = new Array(items2.length);
5128
+ let next = 0;
5129
+ const workers = Array.from(
5130
+ { length: Math.max(1, Math.min(limit, items2.length)) },
5131
+ async () => {
5132
+ for (; ; ) {
5133
+ const i = next++;
5134
+ const item = items2[i];
5135
+ if (i >= items2.length || item === void 0) return;
5136
+ out[i] = await fn(item);
5137
+ }
5138
+ }
5139
+ );
5140
+ await Promise.all(workers);
5141
+ return out;
5142
+ }
5143
+
5144
+ // src/meta/core/derive/git.ts
5145
+ var DEFAULT_BULK_THRESHOLD = 32;
5146
+ var NULL_SHA = "0".repeat(40);
5147
+ var MAX_GIT_OUTPUT_BYTES = Math.min(
5148
+ 512 * 1024 * 1024,
5149
+ bufferConstants.MAX_STRING_LENGTH
5150
+ );
5151
+ var HistoryTooLarge = class extends Error {
5152
+ };
5153
+ var BlobsUnreadable = class extends Error {
5154
+ constructor(detail) {
5155
+ super(detail);
5156
+ this.detail = detail;
5157
+ }
5158
+ detail;
5159
+ };
5160
+ var BlameUnreadable = class extends Error {
5161
+ constructor(rel, detail) {
5162
+ super(rel);
5163
+ this.rel = rel;
5164
+ this.detail = detail;
5165
+ }
5166
+ rel;
5167
+ detail;
5168
+ };
5169
+ function bodyOf(content, fenced) {
5170
+ if (fenced !== true) return content;
5171
+ const loc = locateFrontmatter(content);
5172
+ if (!loc) return content;
5173
+ return content.slice(0, loc.openStart) + content.slice(loc.closeEnd);
5174
+ }
5175
+ async function deriveFromGit(inputs, opts) {
5176
+ const records = /* @__PURE__ */ new Map();
5177
+ const byRoot = groupByRoot(inputs);
5178
+ if (byRoot.size === 0) {
5179
+ return {
5180
+ status: {
5181
+ available: false,
5182
+ reason: "no git repository contains the documents"
5183
+ },
5184
+ records
5185
+ };
5186
+ }
5187
+ const threshold = opts.bulkThreshold ?? DEFAULT_BULK_THRESHOLD;
5188
+ const machines = opts.machines ?? DEFAULT_MACHINES;
5189
+ const wantsProvenance = opts.fields?.includes(PROVENANCE_FIELD) ?? false;
5190
+ const maxBytes = opts.maxOutputBytes ?? MAX_GIT_OUTPUT_BYTES;
5191
+ const parse = yamlOnce();
5192
+ const reasons = [];
5193
+ for (const [root, bucket] of byRoot) {
5194
+ const run2 = (args, stdin) => runGit(args, root, stdin, maxBytes);
5195
+ const shallow = await run2(["rev-parse", "--is-shallow-repository"]);
5196
+ if (shallow.missing) {
5197
+ return {
5198
+ status: { available: false, reason: "git is not on PATH" },
5199
+ records: /* @__PURE__ */ new Map()
5200
+ };
5201
+ }
5202
+ if (shallow.code !== 0) {
5203
+ reasons.push(`git could not read the repository at ${root}`);
5204
+ continue;
5205
+ }
5206
+ if (text(shallow).trim() === "true") {
5207
+ reasons.push(
5208
+ `this checkout is shallow; use actions/checkout with fetch-depth: 0 (${root})`
5209
+ );
5210
+ continue;
5211
+ }
5212
+ try {
5213
+ const found = await histories(
5214
+ run2,
5215
+ bucket.map((e) => e.rel),
5216
+ threshold
5217
+ );
5218
+ if (found === null) {
5219
+ reasons.push(`git could not read the history at ${root}`);
5220
+ continue;
5221
+ }
5222
+ await readOtherParentBlobs(run2, found);
5223
+ const blobs = await fetchBlobs(run2, neededBlobs(found));
5224
+ if (!await followTakenBodies(run2, bucket, found, blobs, threshold)) {
5225
+ reasons.push(`git could not read the history at ${root}`);
5226
+ continue;
5227
+ }
5228
+ const manifestStamps = await stampsInManifests(run2, root, bucket, found, blobs, parse);
5229
+ const facts = bucket.map(
5230
+ (entry) => judge(
5231
+ entry.input,
5232
+ root,
5233
+ found.get(entry.rel) ?? [],
5234
+ blobs,
5235
+ opts.now,
5236
+ machines,
5237
+ manifestStamps.get(entry.input.label)
5238
+ )
5239
+ );
5240
+ if (wantsProvenance) {
5241
+ const pages = await mapConcurrent(bucket, GIT_CONCURRENCY, async (entry) => ({
5242
+ entry,
5243
+ blame: await blameOf(run2, entry.rel, entry.input.content),
5244
+ history: found.get(entry.rel) ?? []
5245
+ }));
5246
+ const evidence = await commitEvidence(run2, root, pages, blobs, parse);
5247
+ const provenanceRun = {
5248
+ machines,
5249
+ ...opts.generatedBy !== void 0 ? { generatedBy: opts.generatedBy } : {}
5250
+ };
5251
+ pages.forEach((page, i) => {
5252
+ const judged = facts[i];
5253
+ const commits = evidence[i];
5254
+ if (judged === void 0 || commits === void 0) return;
5255
+ const derived = provenanceFor(page.entry, page.blame, commits, provenanceRun);
5256
+ judged.provenance = derived.value;
5257
+ judged.provenanceDerivation = derived.derivation;
5258
+ });
5259
+ }
5260
+ bucket.forEach((entry, i) => {
5261
+ const judged = facts[i];
5262
+ if (judged !== void 0) records.set(entry.input.label, judged);
5263
+ });
5264
+ } catch (err) {
5265
+ if (err instanceof BlobsUnreadable) {
5266
+ reasons.push(`git cat-file could not read the history (${root}): ${err.detail}`);
5267
+ continue;
5268
+ }
5269
+ if (err instanceof BlameUnreadable) {
5270
+ const why = err.detail === "" ? "" : `: ${err.detail}`;
5271
+ reasons.push(`git blame could not read ${err.rel} (${root})${why}`);
5272
+ continue;
5273
+ }
5274
+ if (!(err instanceof HistoryTooLarge)) throw err;
5275
+ reasons.push(`git history is too large to read in one pass (${root})`);
5276
+ continue;
5277
+ }
5278
+ }
5279
+ if (reasons.length > 0) {
5280
+ return {
5281
+ status: { available: false, reason: reasons.join("; ") },
5282
+ records: /* @__PURE__ */ new Map()
5283
+ };
5284
+ }
5285
+ return { status: { available: true }, records };
5286
+ }
5287
+ function groupByRoot(inputs) {
5288
+ const rootOf = /* @__PURE__ */ new Map();
5289
+ const byRoot = /* @__PURE__ */ new Map();
5290
+ for (const input of inputs) {
5291
+ const dir = dirname4(input.absPath);
5292
+ let root = rootOf.get(dir);
5293
+ if (root === void 0) {
5294
+ root = findGitRoot(dir);
5295
+ rootOf.set(dir, root);
5296
+ }
5297
+ if (root === null) continue;
5298
+ const rel = relative6(root, input.absPath).split(sep5).join("/");
5299
+ const bucket = byRoot.get(root);
5300
+ if (bucket) bucket.push({ input, rel });
5301
+ else byRoot.set(root, [{ input, rel }]);
5302
+ }
5303
+ return byRoot;
5304
+ }
5305
+ var RECORD_FORMAT = "--format=%x1e%H%x00%aI%x00%an%x00%ae%x00%(trailers:key=Co-authored-by,valueonly,unfold,separator=%x1f)%x00%(trailers:key=Reviewed-by,valueonly,unfold,separator=%x1f)%x00%(trailers:key=Generated-by,valueonly,unfold,separator=%x1f)%x00%P";
5306
+ var LOG_ARGS = [
5307
+ "-c",
5308
+ "core.quotePath=false",
5309
+ "log",
5310
+ "--raw",
5311
+ "--diff-merges=first-parent",
5312
+ "-M",
5313
+ "--no-abbrev",
5314
+ RECORD_FORMAT
5315
+ ];
5316
+ function histories(run2, rels, threshold, rev = "HEAD") {
5317
+ return rels.length <= threshold ? perFileHistories(run2, rels, rev) : bulkHistories(run2, rels, rev);
5318
+ }
5319
+ async function perFileHistories(run2, rels, rev) {
5320
+ const found = /* @__PURE__ */ new Map();
5321
+ for (const rel of rels) {
5322
+ const out = await run2([...LOG_ARGS, rev, "--follow", "--", pathspec(rel)]);
5323
+ if (out.tooLarge) throw new HistoryTooLarge();
5324
+ if (out.code !== 0) {
5325
+ if (await isUnbornHead(run2)) return found;
5326
+ return null;
5327
+ }
5328
+ const attributed = attribute(parseLog(text(out)), [rel]);
5329
+ found.set(rel, attributed.get(rel) ?? []);
5330
+ }
5331
+ return found;
5332
+ }
5333
+ async function bulkHistories(run2, rels, rev) {
5334
+ const out = await run2([...LOG_ARGS, rev]);
5335
+ if (out.tooLarge) throw new HistoryTooLarge();
5336
+ if (out.code !== 0) {
5337
+ if (await isUnbornHead(run2)) return /* @__PURE__ */ new Map();
5338
+ return null;
5339
+ }
5340
+ return attribute(parseLog(text(out)), [...rels]);
5341
+ }
5342
+ async function followTakenBodies(run2, bucket, found, blobs, threshold) {
5343
+ for (; ; ) {
5344
+ const byParent = /* @__PURE__ */ new Map();
5345
+ for (const entry of bucket) {
5346
+ const history = found.get(entry.rel) ?? [];
5347
+ const taken = takenMerge(entry.input.extracted.fenced, history, blobs);
5348
+ if (taken === void 0) continue;
5349
+ const group = byParent.get(taken.parent) ?? [];
5350
+ group.push({ rel: entry.rel, keep: history.slice(0, taken.index), path: taken.path });
5351
+ byParent.set(taken.parent, group);
5352
+ }
5353
+ if (byParent.size === 0) return true;
5354
+ const walked = /* @__PURE__ */ new Map();
5355
+ for (const [parent, group] of byParent) {
5356
+ const paths = [...new Set(group.map((g) => g.path))];
5357
+ const side = await histories(run2, paths, threshold, parent);
5358
+ if (side === null) return false;
5359
+ for (const g of group) {
5360
+ const tail = side.get(g.path) ?? [];
5361
+ walked.set(g.rel, tail);
5362
+ found.set(g.rel, [...g.keep, ...tail]);
5363
+ }
5364
+ }
5365
+ await readOtherParentBlobs(run2, walked);
5366
+ const missing = new Set([...neededBlobs(walked)].filter((sha) => !blobs.has(sha)));
5367
+ for (const [sha, content] of await fetchBlobs(run2, missing)) blobs.set(sha, content);
5368
+ }
5369
+ }
5370
+ function takenMerge(fenced, history, blobs) {
5371
+ const blobBody = (sha) => sha === null ? null : bodyOf(blobs.get(sha) ?? "", fenced);
5372
+ const index = history.findIndex((h) => !sameBody(blobBody(h.newBlob), blobBody(h.oldBlob)));
5373
+ const newest = history[index];
5374
+ if (newest === void 0) return void 0;
5375
+ const body = blobBody(newest.newBlob);
5376
+ const k = newest.otherParentBlobs.findIndex((b) => b !== null && sameBody(body, blobBody(b)));
5377
+ const parent = newest.otherParents[k];
5378
+ if (k === -1 || parent === void 0) return void 0;
5379
+ return { index, parent, path: newest.pathAtCommit };
5380
+ }
5381
+ function pathspec(rel) {
5382
+ return /[*?[\]]/.test(rel) ? `:(literal)${rel}` : rel;
5383
+ }
5384
+ async function isUnbornHead(run2) {
5385
+ const out = await run2(["rev-parse", "--verify", "--quiet", "HEAD^{commit}"]);
5386
+ return !out.missing && out.code !== 0;
5387
+ }
5388
+ function parseLog(text2) {
5389
+ const records = [];
5390
+ for (const chunk of text2.split("")) {
5391
+ if (chunk === "") continue;
5392
+ const nl = chunk.indexOf("\n");
5393
+ const header = nl === -1 ? chunk : chunk.slice(0, nl);
5394
+ const fields = header.split("\0");
5395
+ const sha = fields[0];
5396
+ if (sha === void 0 || sha === "") continue;
5397
+ const raw = [];
5398
+ if (nl !== -1) {
5399
+ for (const line of chunk.slice(nl + 1).split("\n")) {
5400
+ const entry = parseRawLine(line);
5401
+ if (entry) raw.push(entry);
5402
+ }
5403
+ }
5404
+ records.push({
5405
+ sha,
5406
+ authorDate: fields[1] ?? "",
5407
+ authorName: fields[2] ?? "",
5408
+ authorEmail: fields[3] ?? "",
5409
+ coAuthors: splitTrailers(fields[4]),
5410
+ reviewedBy: splitTrailers(fields[5]),
5411
+ generatedBy: splitTrailers(fields[6]),
5412
+ parents: (fields[7] ?? "").trim().split(" ").filter((p) => p !== ""),
5413
+ raw
5414
+ });
5415
+ }
5416
+ return records;
5417
+ }
5418
+ function splitTrailers(field) {
5419
+ if (field === void 0 || field === "") return [];
5420
+ return field.split("").map((s) => s.trim()).filter((s) => s !== "");
5421
+ }
5422
+ function parseRawLine(line) {
5423
+ if (!line.startsWith(":")) return null;
5424
+ const tab = line.indexOf(" ");
5425
+ if (tab === -1) return null;
5426
+ const meta = line.slice(1, tab).split(" ");
5427
+ const oldBlob = meta[2];
5428
+ const newBlob = meta[3];
5429
+ const status = meta[4];
5430
+ if (oldBlob === void 0 || newBlob === void 0 || status === void 0) {
5431
+ return null;
5432
+ }
5433
+ const paths = line.slice(tab + 1).split(" ").map(unquotePath);
5434
+ const first = paths[0];
5435
+ if (first === void 0) return null;
5436
+ const second = paths[1];
5437
+ const renamed = status.startsWith("R") || status.startsWith("C");
5438
+ return {
5439
+ status,
5440
+ oldBlob,
5441
+ newBlob,
5442
+ oldPath: first,
5443
+ newPath: renamed && second !== void 0 ? second : first
5444
+ };
5445
+ }
5446
+ function unquotePath(p) {
5447
+ if (p.length < 2 || !p.startsWith('"') || !p.endsWith('"')) return p;
5448
+ const inner = p.slice(1, -1);
5449
+ const bytes = [];
5450
+ for (let i = 0; i < inner.length; i += 1) {
5451
+ const ch = inner[i];
5452
+ if (ch !== "\\") {
5453
+ bytes.push(...Buffer.from(ch ?? "", "utf8"));
5454
+ continue;
5455
+ }
5456
+ const next = inner[i + 1];
5457
+ if (next === void 0) break;
5458
+ const octal = /^[0-7]{3}/.exec(inner.slice(i + 1));
5459
+ if (octal) {
5460
+ bytes.push(parseInt(octal[0], 8));
5461
+ i += 3;
5462
+ continue;
5463
+ }
5464
+ const simple = {
5465
+ a: 7,
5466
+ b: 8,
5467
+ t: 9,
5468
+ n: 10,
5469
+ v: 11,
5470
+ f: 12,
5471
+ r: 13,
5472
+ '"': 34,
5473
+ "\\": 92
5474
+ };
5475
+ const code = simple[next];
5476
+ if (code === void 0) bytes.push(...Buffer.from(next, "utf8"));
5477
+ else bytes.push(code);
5478
+ i += 1;
5479
+ }
5480
+ return Buffer.from(bytes).toString("utf8");
5481
+ }
5482
+ function attribute(records, wanted) {
5483
+ const histories2 = /* @__PURE__ */ new Map();
5484
+ const tracked = /* @__PURE__ */ new Map();
5485
+ for (const rel of wanted) {
5486
+ histories2.set(rel, []);
5487
+ tracked.set(rel, rel);
5488
+ }
5489
+ for (const record of records) {
5490
+ if (record.raw.length === 0) continue;
5491
+ const byNewPath = /* @__PURE__ */ new Map();
5492
+ for (const entry of record.raw) byNewPath.set(entry.newPath, entry);
5493
+ for (const [rel, current] of tracked) {
5494
+ const entry = byNewPath.get(current);
5495
+ if (!entry) continue;
5496
+ histories2.get(rel)?.push({
5497
+ sha: record.sha,
5498
+ authorDate: record.authorDate,
5499
+ authorName: record.authorName,
5500
+ authorEmail: record.authorEmail,
5501
+ coAuthors: record.coAuthors,
5502
+ reviewedBy: record.reviewedBy,
5503
+ generatedBy: record.generatedBy,
5504
+ oldBlob: entry.oldBlob === NULL_SHA ? null : entry.oldBlob,
5505
+ newBlob: entry.newBlob === NULL_SHA ? null : entry.newBlob,
5506
+ pathAtCommit: entry.newPath,
5507
+ otherParents: record.parents.slice(1),
5508
+ otherParentBlobs: []
5509
+ });
5510
+ if (entry.oldPath !== entry.newPath) tracked.set(rel, entry.oldPath);
5511
+ }
5512
+ }
5513
+ return histories2;
5514
+ }
5515
+ function neededBlobs(histories2) {
5516
+ const shas = /* @__PURE__ */ new Set();
5517
+ for (const history of histories2.values()) {
5518
+ for (const h of history) {
5519
+ if (h.oldBlob !== null) shas.add(h.oldBlob);
5520
+ if (h.newBlob !== null) shas.add(h.newBlob);
5521
+ for (const b of h.otherParentBlobs) if (b !== null) shas.add(b);
5522
+ }
5523
+ }
5524
+ return shas;
5525
+ }
5526
+ async function readOtherParentBlobs(run2, histories2) {
5527
+ const merges = [];
5528
+ const specs = [];
5529
+ for (const history of histories2.values()) {
5530
+ for (const h of history) {
5531
+ if (h.otherParents.length === 0) continue;
5532
+ merges.push(h);
5533
+ for (const parent of h.otherParents) specs.push(`${parent}:${h.pathAtCommit}`);
5534
+ }
5535
+ }
5536
+ if (specs.length === 0) return;
5537
+ const out = await run2(["cat-file", "--batch-check"], `${specs.join("\n")}
5538
+ `);
5539
+ if (out.tooLarge) throw new HistoryTooLarge();
5540
+ if (out.code !== 0) throw new BlobsUnreadable(`exit ${String(out.code)}`);
5541
+ const lines = text(out).split("\n");
5542
+ let i = 0;
5543
+ for (const h of merges) {
5544
+ h.otherParentBlobs = h.otherParents.map(() => blobOfCheckLine(lines[i++]));
5545
+ }
5546
+ }
5547
+ function blobOfCheckLine(line) {
5548
+ const [sha, kind] = (line ?? "").split(" ");
5549
+ if (sha === void 0 || kind !== "blob" || !/^[0-9a-f]{40,64}$/.test(sha)) return null;
5550
+ return sha;
5551
+ }
5552
+ async function fetchBlobs(run2, shas) {
5553
+ const blobs = /* @__PURE__ */ new Map();
5554
+ if (shas.size === 0) return blobs;
5555
+ const out = await run2(["cat-file", "--batch"], `${[...shas].join("\n")}
5556
+ `);
5557
+ if (out.tooLarge) throw new HistoryTooLarge();
5558
+ if (out.code !== 0) throw new BlobsUnreadable(`exit ${String(out.code)}`);
5559
+ const buf = out.raw;
5560
+ let cursor = 0;
5561
+ while (cursor < buf.length) {
5562
+ const nl = buf.indexOf(10, cursor);
5563
+ if (nl === -1) break;
5564
+ const header = buf.toString("utf8", cursor, nl).split(" ");
5565
+ cursor = nl + 1;
5566
+ const sha = header[0];
5567
+ const kind = header[1];
5568
+ const size = Number(header[2]);
5569
+ if (sha === void 0 || kind !== "blob" || !Number.isFinite(size)) {
5570
+ continue;
5571
+ }
5572
+ blobs.set(sha, buf.toString("utf8", cursor, cursor + size));
5573
+ cursor += size + 1;
5574
+ }
5575
+ return blobs;
5576
+ }
5577
+ var git = (value, evidence) => ({
5578
+ value,
5579
+ source: "git",
5580
+ evidence
5581
+ });
5582
+ function factCommits(input, history, blobs) {
5583
+ const fenced = input.extracted.fenced;
5584
+ const blobBody = (sha) => sha === null ? null : bodyOf(blobs.get(sha) ?? "", fenced);
5585
+ const bodyChanging = [...history].reverse().filter((h) => {
5586
+ const body = blobBody(h.newBlob);
5587
+ if (sameBody(body, blobBody(h.oldBlob))) return false;
5588
+ return !h.otherParentBlobs.some((b) => b !== null && sameBody(body, blobBody(b)));
5589
+ });
5590
+ return {
5591
+ bodyChanging,
5592
+ oldest: history[history.length - 1],
5593
+ newestBody: bodyChanging[bodyChanging.length - 1]
5594
+ };
5595
+ }
5596
+ async function stampsInManifests(run2, root, bucket, histories2, blobs, parse) {
5597
+ const reads = [];
5598
+ for (const entry of bucket) {
5599
+ const refs = entry.input.stampManifests;
5600
+ if (refs === void 0) continue;
5601
+ const { oldest, newestBody } = factCommits(entry.input, histories2.get(entry.rel) ?? [], blobs);
5602
+ for (const field of STAMPED_DATE_FIELDS) {
5603
+ const ref = refs[field];
5604
+ const at2 = field === "created" ? oldest : newestBody;
5605
+ if (ref === void 0 || at2 === void 0) continue;
5606
+ const rel = insideRoot(root, ref.absPath);
5607
+ if (rel === void 0) continue;
5608
+ const key = ref.join === "path" ? entryAt(ref.entry, entry.rel, at2.pathAtCommit) : ref.entry;
5609
+ const revs = field === "created" ? [at2.sha] : [at2.sha, `${at2.sha}^`];
5610
+ for (const rev of revs) {
5611
+ reads.push({ label: entry.input.label, field, rev, spec: `${rev}:${rel}`, key, join: ref.join });
5612
+ }
5613
+ }
5614
+ }
5615
+ const out = /* @__PURE__ */ new Map();
5616
+ if (reads.length === 0) return out;
5617
+ const fetched = await fetchSpecs(run2, [...new Set(reads.map((r) => r.spec))]);
5618
+ const values = /* @__PURE__ */ new Map();
5619
+ const keyOf = (label, field, rev) => `${label}\0${field}\0${rev}`;
5620
+ for (const r of reads) {
5621
+ values.set(keyOf(r.label, r.field, r.rev), manifestValue(fetched.get(r.spec), r.key, r.join, r.field, parse));
5622
+ }
5623
+ for (const label of new Set(reads.map((r) => r.label))) {
5624
+ out.set(label, (field, rev) => values.get(keyOf(label, field, rev)));
5625
+ }
5626
+ return out;
5627
+ }
5628
+ function judge(input, root, history, blobs, now, machines, manifestStamp2) {
5629
+ const fenced = input.extracted.fenced;
5630
+ const extractor = extractorFor(input);
5631
+ const blobBody = (sha) => sha === null ? null : bodyOf(blobs.get(sha) ?? "", fenced);
5632
+ const stampsOf = (sha) => {
5633
+ if (sha === null) return {};
5634
+ const content = blobs.get(sha);
5635
+ return content === void 0 ? {} : extractStamps(extractor, content, input.absPath);
5636
+ };
5637
+ const stampAt = (field, h, parent) => {
5638
+ const kept = manifestStamp2?.(field, parent ? `${h.sha}^` : h.sha);
5639
+ if (kept !== void 0 && kept !== null) return kept;
5640
+ return stampsOf(parent ? h.oldBlob : h.newBlob)[field];
5641
+ };
5642
+ const { bodyChanging, oldest, newestBody } = factCommits(input, history, blobs);
5643
+ const head = history[0];
5644
+ const headBlob = head === void 0 ? null : head.newBlob;
5645
+ const created = oldest === void 0 ? null : createdOf(oldest, stampAt("created", oldest, false));
5646
+ const workingChanged = !sameBody(
5647
+ bodyOf(input.content, fenced),
5648
+ headBlob === null ? null : blobBody(headBlob)
5649
+ );
5650
+ let lastUpdated;
5651
+ let lastBodyCommit;
5652
+ if (workingChanged) {
5653
+ lastBodyCommit = null;
5654
+ const working = input.extracted.data["last-updated"];
5655
+ const committed = stampsOf(headBlob)["last-updated"];
5656
+ lastUpdated = working !== void 0 && working !== null && !sameValue(working, committed) ? git(working, "stamped in working tree") : git(utcDate2(now()), "uncommitted body change");
5657
+ } else if (newestBody === void 0) {
5658
+ lastBodyCommit = null;
5659
+ lastUpdated = null;
5660
+ } else {
5661
+ lastBodyCommit = newestBody.sha;
5662
+ lastUpdated = lastUpdatedOf(
5663
+ newestBody,
5664
+ stampAt("last-updated", newestBody, false),
5665
+ stampAt("last-updated", newestBody, true)
5666
+ );
5667
+ }
5668
+ const authors = bodyChanging.length === 0 ? null : authorsOf(bodyChanging, machines);
5669
+ let reviewedBy = null;
5670
+ let lastReviewed = null;
5671
+ if (!workingChanged && newestBody !== void 0 && newestBody.reviewedBy.length > 0) {
5672
+ const evidence = `Reviewed-by trailer in ${short2(newestBody.sha)}`;
5673
+ reviewedBy = git(newestBody.reviewedBy.map(nameOf), evidence);
5674
+ lastReviewed = git(datePart(newestBody.authorDate), evidence);
5675
+ }
5676
+ return {
5677
+ created,
5678
+ "last-updated": lastUpdated,
5679
+ authors,
5680
+ "reviewed-by": reviewedBy,
5681
+ "last-reviewed": lastReviewed,
5682
+ provenance: null,
5683
+ lastBodyCommit,
5684
+ root
5685
+ };
5686
+ }
5687
+ function createdOf(oldest, stamp) {
5688
+ if (stamp !== void 0 && stamp !== null) {
5689
+ return git(stamp, `stamped in ${short2(oldest.sha)}`);
5690
+ }
5691
+ const date = datePart(oldest.authorDate);
5692
+ return git(date, `added in ${short2(oldest.sha)} (${date})`);
5693
+ }
5694
+ function lastUpdatedOf(commit, stamp, before) {
5695
+ if (stamp !== void 0 && stamp !== null && !sameValue(stamp, before)) {
5696
+ return git(stamp, `stamped in ${short2(commit.sha)}`);
5697
+ }
5698
+ const date = datePart(commit.authorDate);
5699
+ return git(date, `body changed in ${short2(commit.sha)} (${date})`);
5700
+ }
5701
+ function authorsOf(bodyChanging, machines) {
5702
+ const seen = /* @__PURE__ */ new Set();
5703
+ const names = [];
5704
+ const add = (name, email) => {
5705
+ if (name === "") return;
5706
+ const identity = email === null || email === "" ? name : `${name} <${email}>`;
5707
+ if (machineIdentity(identity, machines) !== void 0) return;
5708
+ const key = email === null || email === "" ? `name:${name.toLowerCase()}` : email.toLowerCase();
5709
+ if (seen.has(key)) return;
5710
+ seen.add(key);
5711
+ names.push(name);
5712
+ };
5713
+ for (const h of bodyChanging) {
5714
+ add(h.authorName, h.authorEmail);
5715
+ for (const co of h.coAuthors) add(nameOf(co), emailOf(co));
5716
+ }
5717
+ const n = bodyChanging.length;
5718
+ return git(names, `${n} body-changing ${n === 1 ? "commit" : "commits"}`);
5719
+ }
5720
+ function provenanceFenced(extracted) {
5721
+ return extracted.fenced === true || extracted.format === "markdown" || extracted.format === "mdx";
5722
+ }
5723
+ var OUTSIDE_RANGE = "outside-the-attributed-range";
5724
+ var ATTRIBUTED = ":attributed";
5725
+ function provenanceFor(entry, blame, commits, opts) {
5726
+ const { input } = entry;
5727
+ const fenced = provenanceFenced(input.extracted);
5728
+ const base = { content: input.content, commits, machines: opts.machines, fenced };
5729
+ let derivation;
5730
+ const attribution = input.attribution;
5731
+ if (attribution !== void 0) {
5732
+ const page = readProvenancePage(input.content, { fenced });
5733
+ const { bodyLine } = page;
5734
+ const ranges = attribution.targets.flatMap((target) => {
5735
+ const entry2 = attributeRange({ ...base, blame, target, generatedBy: attribution.generatedBy, page });
5736
+ const span = entry2 === void 0 ? void 0 : parseLines(entry2.lines);
5737
+ return span === void 0 ? [] : [toFileLines(span, bodyLine)];
5738
+ });
5739
+ const inRange = (line) => ranges.some((range) => line.finalLine >= range.start && line.finalLine <= range.end);
5740
+ const attributed = new Map(commits);
5741
+ const trailers = { generatedBy: [attribution.generatedBy], coAuthoredBy: [] };
5742
+ attributed.set(OUTSIDE_RANGE, { sha: OUTSIDE_RANGE, trailers: { generatedBy: [], coAuthoredBy: [] } });
5743
+ const rekeyed = blame.map((line) => {
5744
+ if (inRange(line)) {
5745
+ if (line.uncommitted) return line;
5746
+ const sha = `${line.sha}${ATTRIBUTED}`;
5747
+ attributed.set(sha, { sha, trailers });
5748
+ return { ...line, sha };
5749
+ }
5750
+ return line.uncommitted ? { ...line, sha: OUTSIDE_RANGE, uncommitted: false } : line;
5751
+ });
5752
+ derivation = deriveProvenance({
5753
+ ...base,
5754
+ commits: attributed,
5755
+ generatedBy: attribution.generatedBy,
5756
+ blame: rekeyed
5757
+ });
5758
+ } else {
5759
+ derivation = deriveProvenance({
5760
+ ...base,
5761
+ blame,
5762
+ ...opts.generatedBy !== void 0 ? { generatedBy: opts.generatedBy } : {}
5763
+ });
5764
+ }
5765
+ const entries = derivation.derived.map((d) => d.entry);
5766
+ return {
5767
+ value: entries.length === 0 ? null : git(entries, provenanceEvidence(derivation)),
5768
+ derivation
5769
+ };
5770
+ }
5771
+ function provenanceEvidence(derivation) {
5772
+ const shas = [];
5773
+ let uncommitted = false;
5774
+ for (const range of derivation.derived) {
5775
+ for (let n = range.span.start; n <= range.span.end; n++) {
5776
+ const evidence = derivation.evidenceByLine.get(n);
5777
+ if (evidence?.machine === void 0) continue;
5778
+ if (evidence.rule === 1) uncommitted = true;
5779
+ else if (!shas.includes(evidence.sha)) shas.push(evidence.sha);
5780
+ }
5781
+ }
5782
+ const first = shas[0];
5783
+ if (first === void 0) return "uncommitted";
5784
+ const parts = [`blame ${short2(first)}`];
5785
+ if (shas.length > 1) parts.push(`${String(shas.length)} commits`);
5786
+ if (uncommitted) parts.push("uncommitted");
5787
+ return parts.join(", ");
5788
+ }
5789
+ async function blameOf(run2, rel, content) {
5790
+ const out = await run2([
5791
+ "-c",
5792
+ "core.quotePath=false",
5793
+ "blame",
5794
+ "--no-ignore-revs-file",
5795
+ "--line-porcelain",
5796
+ "--",
5797
+ rel
5798
+ ]);
5799
+ if (out.tooLarge) throw new HistoryTooLarge();
5800
+ if (out.code !== 0) {
5801
+ const inHead = await run2(["cat-file", "-e", `HEAD:${rel}`]);
5802
+ if (inHead.code !== 0) {
5803
+ return splitLines(content).map((line, i) => ({
5804
+ sha: ZERO_SHA,
5805
+ origLine: i + 1,
5806
+ finalLine: i + 1,
5807
+ author: "Not Committed Yet",
5808
+ authorMail: "not.committed.yet",
5809
+ filename: rel,
5810
+ boundary: false,
5811
+ uncommitted: true,
5812
+ content: line
5813
+ }));
5814
+ }
5815
+ throw new BlameUnreadable(rel, out.stderr.trim());
5816
+ }
5817
+ return parseLinePorcelain(text(out));
5818
+ }
5819
+ async function commitEvidence(run2, root, pages, blobs, parse) {
5820
+ const plans = pages.map(({ entry, blame, history }) => {
5821
+ const pathAt = /* @__PURE__ */ new Map();
5822
+ for (const line of blame) {
5823
+ if (!line.uncommitted && !pathAt.has(line.sha)) pathAt.set(line.sha, unquotePath(line.filename));
5824
+ }
5825
+ const trailers = /* @__PURE__ */ new Map();
5826
+ const pageBlob = /* @__PURE__ */ new Map();
5827
+ for (const h of history) {
5828
+ trailers.set(h.sha, { generatedBy: h.generatedBy, coAuthoredBy: h.coAuthors });
5829
+ const blob = h.newBlob === null ? void 0 : blobs.get(h.newBlob);
5830
+ if (blob !== void 0) pageBlob.set(h.sha, blob);
5831
+ }
5832
+ const manifest = entry.input.provenanceManifest;
5833
+ const manifestRel = manifest === void 0 ? void 0 : insideRoot(root, manifest.absPath);
5834
+ return { entry, pathAt, trailers, pageBlob, manifest, manifestRel };
5835
+ });
5836
+ const unlogged = /* @__PURE__ */ new Set();
5837
+ const specs = /* @__PURE__ */ new Set();
5838
+ for (const { pathAt, trailers, pageBlob, manifestRel } of plans) {
5839
+ for (const [sha, path] of pathAt) {
5840
+ if (!trailers.has(sha)) unlogged.add(sha);
5841
+ if (!pageBlob.has(sha)) specs.add(`${sha}:${path}`);
5842
+ if (manifestRel !== void 0) specs.add(`${sha}:${manifestRel}`);
5843
+ }
5844
+ }
5845
+ const logged = /* @__PURE__ */ new Map();
5846
+ if (unlogged.size > 0) {
5847
+ const out = await run2(["log", "--no-walk=unsorted", "--stdin", RECORD_FORMAT], `${[...unlogged].join("\n")}
5848
+ `);
5849
+ if (out.tooLarge) throw new HistoryTooLarge();
5850
+ if (out.code !== 0) throw new BlobsUnreadable(`git log exit ${String(out.code)}`);
5851
+ for (const record of parseLog(text(out))) {
5852
+ logged.set(record.sha, { generatedBy: record.generatedBy, coAuthoredBy: record.coAuthors });
5853
+ }
5854
+ }
5855
+ const fetched = await fetchSpecs(run2, [...specs]);
5856
+ return plans.map(({ entry, pathAt, trailers, pageBlob, manifest, manifestRel }) => {
5857
+ const commits = /* @__PURE__ */ new Map();
5858
+ for (const [sha, path] of pathAt) {
5859
+ const blob = pageBlob.get(sha) ?? fetched.get(`${sha}:${path}`);
5860
+ commits.set(sha, {
5861
+ sha,
5862
+ trailers: trailers.get(sha) ?? logged.get(sha) ?? { generatedBy: [], coAuthoredBy: [] },
5863
+ ...blob !== void 0 ? { blob } : {},
5864
+ ...manifest !== void 0 ? {
5865
+ stamp: manifestRel === void 0 ? [] : manifestStamp(
5866
+ fetched.get(`${sha}:${manifestRel}`),
5867
+ manifest.join === "path" ? entryAt(manifest.entry, entry.rel, path) : manifest.entry,
5868
+ manifest.join,
5869
+ parse
5870
+ )
5871
+ } : {}
5872
+ });
5873
+ }
5874
+ return commits;
5875
+ });
5876
+ }
5877
+ function insideRoot(root, abs) {
5878
+ const rel = relative6(root, abs);
5879
+ if (rel === "" || rel.startsWith("..") || isAbsolute4(rel)) return void 0;
5880
+ return rel.split(sep5).join("/");
5881
+ }
5882
+ function entryAt(entry, rel, relAtCommit) {
5883
+ if (relAtCommit === rel) return entry;
5884
+ const fromHere = posix4.relative(posix4.dirname(rel), relAtCommit);
5885
+ return posix4.join(posix4.dirname(entry), fromHere);
5886
+ }
5887
+ function manifestStamp(text2, key, join5, parse) {
5888
+ return provenanceEntries(manifestValue(text2, key, join5, PROVENANCE_FIELD, parse));
5889
+ }
5890
+ function yamlOnce() {
5891
+ const parsed = /* @__PURE__ */ new Map();
5892
+ return (text2) => {
5893
+ if (parsed.has(text2)) return parsed.get(text2);
5894
+ let data;
5895
+ try {
5896
+ data = parseYaml3(text2);
5897
+ } catch {
5898
+ data = void 0;
5899
+ }
5900
+ parsed.set(text2, data);
5901
+ return data;
5902
+ };
5903
+ }
5904
+ function manifestValue(text2, key, join5, field, parse) {
5905
+ if (text2 === void 0) return void 0;
5906
+ const data = parse(text2);
5907
+ if (!isRecord6(data)) return void 0;
5908
+ for (const [candidate, value] of Object.entries(data)) {
5909
+ const same = join5 === "path" ? posix4.normalize(candidate) === posix4.normalize(key) : candidate === key;
5910
+ if (same && isRecord6(value)) return value[field];
5911
+ }
5912
+ return void 0;
5913
+ }
5914
+ function isRecord6(x) {
5915
+ return typeof x === "object" && x !== null && !Array.isArray(x);
5916
+ }
5917
+ async function fetchSpecs(run2, specs) {
5918
+ const found = /* @__PURE__ */ new Map();
5919
+ if (specs.length === 0) return found;
5920
+ const out = await run2(["cat-file", "--batch"], `${specs.join("\n")}
5921
+ `);
5922
+ if (out.tooLarge && specs.length > 1) {
5923
+ const half = Math.ceil(specs.length / 2);
5924
+ const first = await fetchSpecs(run2, specs.slice(0, half));
5925
+ const rest = await fetchSpecs(run2, specs.slice(half));
5926
+ return new Map([...first, ...rest]);
5927
+ }
5928
+ if (out.tooLarge) throw new HistoryTooLarge();
5929
+ if (out.code !== 0) throw new BlobsUnreadable(`exit ${String(out.code)}`);
5930
+ for (const [spec, content] of parseSpecBatch(out.raw, specs)) found.set(spec, content);
5931
+ return found;
5932
+ }
5933
+ function parseSpecBatch(buf, specs) {
5934
+ const found = /* @__PURE__ */ new Map();
5935
+ let cursor = 0;
5936
+ for (const spec of specs) {
5937
+ const nl = buf.indexOf(10, cursor);
5938
+ if (nl === -1) throw new BlobsUnreadable(`the answer ends before ${spec}`);
5939
+ const header = buf.toString("utf8", cursor, nl);
5940
+ cursor = nl + 1;
5941
+ if (header.endsWith(" missing") || header.endsWith(" ambiguous")) continue;
5942
+ const parts = header.split(" ");
5943
+ const size = Number(parts[2]);
5944
+ if (parts.length !== 3 || !Number.isInteger(size) || size < 0) {
5945
+ throw new BlobsUnreadable(`unreadable header for ${spec}: ${header}`);
5946
+ }
5947
+ if (parts[1] === "blob") found.set(spec, buf.toString("utf8", cursor, cursor + size));
5948
+ cursor += size + 1;
5949
+ }
5950
+ return found;
5951
+ }
5952
+ function extractorFor(input) {
5953
+ return extractorByName(input.extracted.format) ?? extractorForExtension(extname2(input.absPath));
5954
+ }
5955
+ function extractStamps(extractor, content, filePath) {
5956
+ if (!extractor) return {};
5957
+ try {
5958
+ return extractor.extract(content, filePath).data;
5959
+ } catch {
5960
+ return {};
5961
+ }
5962
+ }
5963
+ function sameBody(a, b) {
5964
+ if (a === null || b === null) return a === b;
5965
+ return a.replace(/\r\n/g, "\n") === b.replace(/\r\n/g, "\n");
5966
+ }
5967
+ function sameValue(a, b) {
5968
+ return toJsonText(a) === toJsonText(b);
5969
+ }
5970
+ var short2 = (sha) => sha.slice(0, 7);
5971
+ var datePart = (iso) => {
5972
+ const d = new Date(iso);
5973
+ return Number.isNaN(d.getTime()) ? "" : utcDate2(d);
5974
+ };
5975
+ function utcDate2(d) {
5976
+ return d.toISOString().slice(0, 10);
5977
+ }
5978
+ function nameOf(trailer) {
5979
+ const lt = trailer.lastIndexOf("<");
5980
+ return (lt === -1 ? trailer : trailer.slice(0, lt)).trim();
5981
+ }
5982
+ function emailOf(trailer) {
5983
+ const m = /<([^>]*)>\s*$/.exec(trailer);
5984
+ return m?.[1]?.trim() ?? null;
5985
+ }
5986
+ var MAX_STDERR_BYTES = 16 * 1024;
5987
+ var GIT_CONCURRENCY = Math.min(8, availableParallelism());
5988
+ function text(out) {
5989
+ try {
5990
+ return out.raw.toString("utf8");
5991
+ } catch {
5992
+ throw new HistoryTooLarge();
5993
+ }
5994
+ }
5995
+ function runGit(args, cwd, stdin, maxBytes) {
5996
+ return new Promise((settle2, reject) => {
5997
+ let done = false;
5998
+ const finish = (out) => {
5999
+ if (done) return;
6000
+ done = true;
6001
+ settle2(out);
6002
+ };
6003
+ const fail = (err) => {
6004
+ if (done) return;
6005
+ done = true;
6006
+ reject(err instanceof Error ? err : new Error(String(err)));
6007
+ };
6008
+ const failed = (missing) => ({
6009
+ code: null,
6010
+ raw: Buffer.alloc(0),
6011
+ missing,
6012
+ tooLarge: false,
6013
+ stderr: ""
6014
+ });
6015
+ let child;
6016
+ try {
6017
+ child = spawn2("git", args, { cwd, windowsHide: true });
6018
+ } catch {
6019
+ finish(failed(true));
6020
+ return;
6021
+ }
6022
+ let chunks = [];
6023
+ let total = 0;
6024
+ let tooLarge = false;
6025
+ child.stdout.on("data", (chunk) => {
6026
+ if (tooLarge) return;
6027
+ total += chunk.length;
6028
+ if (total > maxBytes) {
6029
+ tooLarge = true;
6030
+ chunks = [];
6031
+ child.kill();
6032
+ return;
6033
+ }
6034
+ chunks.push(chunk);
6035
+ });
6036
+ const errChunks = [];
6037
+ let errTotal = 0;
6038
+ child.stderr.on("data", (chunk) => {
6039
+ if (errTotal >= MAX_STDERR_BYTES) return;
6040
+ const kept = chunk.subarray(0, MAX_STDERR_BYTES - errTotal);
6041
+ errTotal += kept.length;
6042
+ errChunks.push(kept);
6043
+ });
6044
+ child.on("error", (err) => {
6045
+ finish(failed(err.code === "ENOENT"));
6046
+ });
6047
+ child.stdin.on("error", () => {
6048
+ });
6049
+ child.on("close", (code) => {
6050
+ try {
6051
+ const raw = tooLarge ? Buffer.alloc(0) : Buffer.concat(chunks);
6052
+ const stderr = Buffer.concat(errChunks).toString("utf8");
6053
+ finish({ code, raw, missing: false, tooLarge, stderr });
6054
+ } catch (err) {
6055
+ fail(err);
6056
+ }
6057
+ });
6058
+ if (stdin === void 0) child.stdin.end();
6059
+ else child.stdin.end(stdin);
6060
+ });
6061
+ }
6062
+
6063
+ // src/meta/core/derive/index.ts
6064
+ var FIELD_SOURCES = {
6065
+ created: ["git"],
6066
+ "last-updated": ["git"],
6067
+ authors: ["git"],
6068
+ owner: ["codeowners"],
6069
+ "reviewed-by": ["github", "gitlab", "git"],
6070
+ "last-reviewed": ["github", "gitlab", "git"],
6071
+ provenance: ["git"]
6072
+ };
6073
+ var REVIEW_SOURCES = ["github", "gitlab"];
6074
+ var HOST_NAME = {
6075
+ github: "GitHub",
6076
+ gitlab: "GitLab"
6077
+ };
6078
+ function sourcesFor(field, commands) {
6079
+ if (isBuiltinField(field)) return FIELD_SOURCES[field];
6080
+ if (commands !== void 0 && Object.hasOwn(commands, field)) return ["command"];
6081
+ return [];
6082
+ }
6083
+ function consultedSources(fields, sources, commands) {
6084
+ const needed = /* @__PURE__ */ new Set();
6085
+ for (const field of fields) {
6086
+ for (const s of sourcesFor(field, commands)) needed.add(s);
6087
+ }
6088
+ return sources.filter((s) => needed.has(s));
6089
+ }
6090
+ function assertSourcesAvailable(sources, hint) {
6091
+ for (const name of Object.keys(sources)) {
6092
+ const status = sources[name];
6093
+ if (status === void 0 || status.available) continue;
6094
+ const reason = status.reason ?? "it could not answer";
6095
+ throw new DocmetaError(
6096
+ `${name} source unavailable: ${reason}${hint === void 0 ? "" : `; ${hint}`}`
6097
+ );
6098
+ }
6099
+ }
6100
+ async function deriveMetadata(inputs, ctx) {
6101
+ const consulted = new Set(consultedSources(ctx.fields, ctx.sources, ctx.commands));
6102
+ const sources = {};
6103
+ let git2 = /* @__PURE__ */ new Map();
6104
+ if (consulted.has("git")) {
6105
+ const result = await deriveFromGit(inputs, {
6106
+ cwd: ctx.cwd,
6107
+ now: ctx.now,
6108
+ fields: ctx.fields,
6109
+ ...ctx.generatedBy !== void 0 ? { generatedBy: ctx.generatedBy } : {},
6110
+ ...ctx.machines !== void 0 ? { machines: ctx.machines } : {}
6111
+ });
6112
+ sources.git = result.status;
6113
+ git2 = result.records;
6114
+ }
6115
+ const provenance = /* @__PURE__ */ new Map();
6116
+ for (const [label, facts] of git2) {
6117
+ if (facts.provenanceDerivation !== void 0) provenance.set(label, facts.provenanceDerivation);
6118
+ }
6119
+ const rootCache = /* @__PURE__ */ new Map();
6120
+ const rootOf = (absPath) => {
6121
+ const dir = dirname5(absPath);
6122
+ let root = rootCache.get(dir);
6123
+ if (root === void 0) {
6124
+ root = findGitRoot(dir);
6125
+ rootCache.set(dir, root);
6126
+ }
6127
+ return root;
6128
+ };
6129
+ const rootFor = (input) => git2.get(input.label)?.root ?? rootOf(input.absPath);
6130
+ let owners = /* @__PURE__ */ new Map();
6131
+ if (consulted.has("codeowners")) {
6132
+ const result = await deriveFromCodeowners(inputs, {
6133
+ explicit: ctx.codeowners,
6134
+ configDir: ctx.configDir,
6135
+ rootOf
6136
+ });
6137
+ sources.codeowners = result.status;
6138
+ owners = result.records;
6139
+ }
6140
+ const requested = REVIEW_SOURCES.filter((s) => consulted.has(s));
6141
+ let reviews = /* @__PURE__ */ new Map();
6142
+ if (requested.length > 0) {
6143
+ if (!consulted.has("git")) {
6144
+ for (const s of requested) {
6145
+ sources[s] = {
6146
+ available: false,
6147
+ reason: `the ${s} source needs the git source to find each document's commits; add git to sources`
6148
+ };
6149
+ }
6150
+ } else if (sources.git?.available === false) {
6151
+ for (const s of requested) {
6152
+ sources[s] = {
6153
+ available: false,
6154
+ reason: `the git source could not answer, so the ${s} source cannot locate any commit`
6155
+ };
6156
+ }
6157
+ } else {
6158
+ const result = await deriveReviewsByRoot(inputs, git2, rootFor, ctx, requested);
6159
+ Object.assign(sources, result.status);
6160
+ reviews = result.records;
6161
+ }
6162
+ }
6163
+ let commanded = /* @__PURE__ */ new Map();
6164
+ const doomed = Object.values(sources).some((s) => !s.available);
6165
+ if (consulted.has("command") && ctx.commands !== void 0 && !doomed) {
6166
+ const wanted = new Set(ctx.fields);
6167
+ const commands = Object.fromEntries(
6168
+ Object.entries(ctx.commands).filter(([field]) => wanted.has(field))
6169
+ );
6170
+ if (Object.keys(commands).length > 0) {
6171
+ const result = await deriveFromCommands(inputs, commands, { cwd: ctx.configDir ?? ctx.cwd });
6172
+ sources.command = result.status;
6173
+ commanded = result.records;
6174
+ }
6175
+ }
6176
+ const records = /* @__PURE__ */ new Map();
6177
+ for (const input of inputs) {
6178
+ const fields = {};
6179
+ const g = git2.get(input.label);
6180
+ const r = reviews.get(input.label);
6181
+ const c = commanded.get(input.label);
6182
+ for (const field of ctx.fields) {
6183
+ let value = null;
6184
+ for (const source of sourcesFor(field, ctx.commands)) {
6185
+ if (!consulted.has(source)) continue;
6186
+ const candidate = valueFrom(source, field, g, r, owners.get(input.label), c);
6187
+ if (candidate !== null) {
6188
+ value = candidate;
6189
+ break;
6190
+ }
6191
+ }
6192
+ fields[field] = value;
6193
+ }
6194
+ records.set(input.label, { file: input.label, fields });
6195
+ }
6196
+ return { records, sources, ...provenance.size > 0 ? { provenance } : {} };
6197
+ }
6198
+ function valueFrom(source, field, git2, reviews, owner, commanded) {
6199
+ switch (source) {
6200
+ case "git":
6201
+ if (git2 === void 0 || !isBuiltinField(field) || field === "owner") return null;
6202
+ return git2[field];
6203
+ case "command":
6204
+ return commanded?.[field] ?? null;
6205
+ case "codeowners":
6206
+ return field === "owner" ? owner ?? null : null;
6207
+ case "github":
6208
+ case "gitlab": {
6209
+ if (reviews === void 0) return null;
6210
+ if (field !== "reviewed-by" && field !== "last-reviewed") return null;
6211
+ const fact = reviews[field];
6212
+ return fact !== null && fact.source === source ? fact : null;
6213
+ }
6214
+ }
6215
+ }
6216
+ async function deriveReviewsByRoot(inputs, git2, rootFor, ctx, requested) {
6217
+ const byRoot = /* @__PURE__ */ new Map();
6218
+ for (const input of inputs) {
6219
+ const root = rootFor(input);
6220
+ if (root === null) continue;
6221
+ const bucket = byRoot.get(root) ?? [];
6222
+ bucket.push(input);
6223
+ byRoot.set(root, bucket);
6224
+ }
6225
+ const status = {};
6226
+ const records = /* @__PURE__ */ new Map();
6227
+ const unavailable2 = (which, reason) => {
6228
+ for (const s of which) status[s] = { available: false, reason };
6229
+ return { status, records: /* @__PURE__ */ new Map() };
6230
+ };
6231
+ if (byRoot.size === 0) {
6232
+ return unavailable2(requested, "no git repository contains the documents");
6233
+ }
6234
+ const cacheDir = resolve5(ctx.configDir ?? ctx.cwd, REVIEW_CACHE_DIR);
6235
+ for (const [root, bucket] of byRoot) {
6236
+ const client = ctx.reviews ?? await createReviewClient(root, { cwd: ctx.cwd, cache: ctx.cache, cacheDir, requested });
6237
+ const identity = await client.detect();
6238
+ if (identity === null) {
6239
+ const s = await client.status();
6240
+ return unavailable2(requested, s.reason ?? "no origin remote to tell GitHub from GitLab");
6241
+ }
6242
+ const source = identity.kind;
6243
+ if (!requested.includes(source)) {
6244
+ return unavailable2(
6245
+ requested,
6246
+ `the origin remote is ${identity.host}, which is ${HOST_NAME[source]}; add ${source} to sources, or drop reviewed-by and last-reviewed from the managed fields`
6247
+ );
6248
+ }
6249
+ const result = await deriveFromReviews(
6250
+ bucket.map((input) => ({
6251
+ label: input.label,
6252
+ sha: git2.get(input.label)?.lastBodyCommit ?? null
6253
+ })),
6254
+ client
6255
+ );
6256
+ if (!result.status.available) {
6257
+ status[source] = result.status;
6258
+ return { status, records: /* @__PURE__ */ new Map() };
6259
+ }
6260
+ status[source] = result.status;
6261
+ for (const [label, facts] of result.records) records.set(label, facts);
6262
+ }
6263
+ return { status, records };
6264
+ }
6265
+
6266
+ // src/meta/core/derive/table.ts
6267
+ var DERIVED_VIEW = "derived";
6268
+ var DERIVED_ROWS = "_derived_rows";
6269
+ var RESOLVED_VIEW = "resolved";
6270
+ var ORIGIN_COLUMN = "_origin";
6271
+ var SOURCES_COLUMN = "_sources";
6272
+ function derivedColumns(commands) {
6273
+ return ["_path", ...derivableFields(commands), SOURCES_COLUMN];
6274
+ }
6275
+ function mentionsDerived(sql) {
6276
+ return /\b(?:from|join|update|into|table|view)\s+(?:"derived"|derived\b)/i.test(sql);
6277
+ }
6278
+ function fieldsForSql(sql, fields) {
6279
+ const named = (field) => new RegExp(`(?<![\\w-])${escapeRegExp(field)}(?![\\w-])`, "i").test(sql);
6280
+ if (sql.includes("*") || /\b_(?:sources|origin)\b/i.test(sql)) {
6281
+ return fields.filter((field) => field !== PROVENANCE_FIELD || named(field));
6282
+ }
6283
+ return fields.filter(named);
6284
+ }
6285
+ function escapeRegExp(s) {
6286
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6287
+ }
6288
+ function createDerivedView(db, records, columns) {
6289
+ const quoted = columns.map(quoteIdent);
6290
+ db.exec(
6291
+ `CREATE TABLE ${DERIVED_ROWS} (${quoted.map((c, i) => i === 0 ? `${c} TEXT PRIMARY KEY` : `${c} TEXT`).join(", ")})`
6292
+ );
6293
+ const insert = db.prepare(
6294
+ `INSERT INTO ${DERIVED_ROWS} VALUES (${quoted.map(() => "?").join(", ")})`
6295
+ );
6296
+ const fields = columns.slice(1, -1);
6297
+ db.exec("BEGIN");
6298
+ let committed = false;
6299
+ try {
6300
+ for (const [label, record] of records) {
6301
+ const sources = {};
6302
+ const values = fields.map((field) => {
6303
+ const derived = record.fields[field];
6304
+ if (derived == null) return null;
6305
+ sources[field] = { source: derived.source, evidence: derived.evidence };
6306
+ return bindValue(derived.value);
6307
+ });
6308
+ insert.run(label, ...values, JSON.stringify(sources));
6309
+ }
6310
+ db.exec("COMMIT");
6311
+ committed = true;
6312
+ } finally {
6313
+ if (!committed) db.exec("ROLLBACK");
6314
+ }
6315
+ db.exec(`CREATE VIEW ${DERIVED_VIEW} AS SELECT * FROM ${DERIVED_ROWS}`);
6316
+ }
6317
+ function resolvedColumns(docsColumns, commands) {
6318
+ const columns = ["_path"];
6319
+ const seen = /* @__PURE__ */ new Set(["_path", ORIGIN_COLUMN, SOURCES_COLUMN]);
6320
+ for (const column of docsColumns) {
6321
+ if (seen.has(column) || RESERVED.has(column)) continue;
6322
+ seen.add(column);
6323
+ columns.push(column);
6324
+ }
6325
+ for (const field of derivableFields(commands)) {
6326
+ if (seen.has(field)) continue;
6327
+ seen.add(field);
6328
+ columns.push(field);
6329
+ }
6330
+ columns.push(ORIGIN_COLUMN, SOURCES_COLUMN);
6331
+ return columns;
6332
+ }
6333
+ function mentionsResolved(sql) {
6334
+ return /\b(?:from|join|update|into|table|view)\s+(?:"resolved"|resolved\b)/i.test(sql);
6335
+ }
6336
+ function jsonPath(field) {
6337
+ return `$.${JSON.stringify(field)}`;
6338
+ }
6339
+ function sqlString(value) {
6340
+ return `'${value.replaceAll("'", "''")}'`;
6341
+ }
6342
+ function createResolvedView(db, docsColumns, commands) {
6343
+ const columns = resolvedColumns(docsColumns, commands);
6344
+ const fields = columns.slice(1, -2);
6345
+ const inDocs = new Set(docsColumns);
6346
+ const inDerived = new Set(derivableFields(commands));
6347
+ const selects = [`d.${quoteIdent("_path")} AS ${quoteIdent("_path")}`];
6348
+ const origin = [];
6349
+ for (const field of fields) {
6350
+ const path = sqlString(jsonPath(field));
6351
+ const asserted = `json_type(d.${quoteIdent("_data")}, ${path}) IS NOT NULL`;
6352
+ const assertedValue = inDocs.has(field) ? `d.${quoteIdent(field)}` : `d.${quoteIdent("_data")} ->> ${path}`;
6353
+ if (inDerived.has(field)) {
6354
+ const derivedValue = `r.${quoteIdent(field)}`;
6355
+ selects.push(
6356
+ `CASE WHEN ${asserted} THEN ${assertedValue} ELSE ${derivedValue} END AS ${quoteIdent(field)}`
6357
+ );
6358
+ origin.push(
6359
+ `${sqlString(field)}, CASE WHEN ${asserted} THEN 'asserted' WHEN ${derivedValue} IS NOT NULL THEN 'derived' END`
6360
+ );
6361
+ } else {
6362
+ selects.push(`${assertedValue} AS ${quoteIdent(field)}`);
6363
+ origin.push(`${sqlString(field)}, CASE WHEN ${asserted} THEN 'asserted' END`);
6364
+ }
6365
+ }
6366
+ selects.push(
6367
+ `json_patch('{}', json_object(${origin.join(", ")})) AS ${quoteIdent(ORIGIN_COLUMN)}`
6368
+ );
6369
+ selects.push(`r.${quoteIdent(SOURCES_COLUMN)} AS ${quoteIdent(SOURCES_COLUMN)}`);
6370
+ db.exec(
6371
+ `CREATE VIEW ${RESOLVED_VIEW} AS SELECT ${selects.join(", ")} FROM docs d LEFT JOIN ${DERIVED_ROWS} r USING (${quoteIdent("_path")})`
6372
+ );
6373
+ }
6374
+ async function deriveForTable(inputs, ctx, hint, fields) {
6375
+ const derive = ctx.config?.derive;
6376
+ const commands = commandsOf(derive);
6377
+ const managesProvenance = derive?.fields?.includes(PROVENANCE_FIELD) === true && !fields.includes(PROVENANCE_FIELD);
6378
+ const result = await deriveMetadata(inputs, {
6379
+ cwd: ctx.cwd,
6380
+ base: ctx.base,
6381
+ ...ctx.configDir !== void 0 ? { configDir: ctx.configDir } : {},
6382
+ sources: derive?.sources ?? [...DERIVE_SOURCES],
6383
+ fields: managesProvenance ? [...fields, PROVENANCE_FIELD] : fields,
6384
+ ...derive?.codeowners !== void 0 ? { codeowners: derive.codeowners } : {},
6385
+ ...commands !== void 0 ? { commands } : {},
6386
+ cache: ctx.cache,
6387
+ now: ctx.now ?? (() => /* @__PURE__ */ new Date()),
6388
+ machines: machinesOf(derive)
6389
+ });
6390
+ assertSourcesAvailable(result.sources, hint);
6391
+ return result.records;
6392
+ }
6393
+
6394
+ // src/meta/core/checks.ts
6395
+ var CHECK_KEYWORD = "check";
6396
+ function checkSchemaRef(name) {
6397
+ if (!CHECK_NAME.test(name) || name.toLowerCase().endsWith(".json")) {
6398
+ throw new DocmetaError(
6399
+ `check name "${name}" must match [a-z0-9][a-z0-9._-]* and not end in ".json" \u2014 the name is part of each finding's identity (check:<name>).`
6400
+ );
6401
+ }
6402
+ return `check:${name}`;
6403
+ }
6404
+ var CONVENTION_COLUMNS = /* @__PURE__ */ new Set(["path", "line", "key", "message"]);
6405
+ function rowsToFindings(name, columns, rows, loaded) {
6406
+ if (!columns.includes("path")) {
6407
+ throw new DocmetaError(
6408
+ `check "${name}": the result has no \`path\` column. A check's SELECT names its findings through the column convention: \`path\` (required), and optionally \`line\`, \`key\`, and \`message\`; any other column is folded into the message.`
6409
+ );
6410
+ }
6411
+ const findings = /* @__PURE__ */ new Map();
6412
+ const extras = columns.filter((c) => !CONVENTION_COLUMNS.has(c));
6413
+ for (const row of rows) {
6414
+ const path = row.path;
6415
+ if (typeof path !== "string" || loaded !== void 0 && !loaded.has(path)) {
6416
+ throw new DocmetaError(
6417
+ `check "${name}": a result row names ${JSON.stringify(path)}, which this run did not load \u2014 a finding must attach to a file of the corpus. Fix the check's \`path\` column.`
6418
+ );
6419
+ }
6420
+ const key = typeof row.key === "string" && row.key !== "" ? row.key : void 0;
6421
+ const line = asLine(row.line);
6422
+ const message = typeof row.message === "string" && row.message !== "" ? row.message : synthesizeMessage(extras, row);
6423
+ const err = {
6424
+ schema: checkSchemaRef(name),
6425
+ keyword: CHECK_KEYWORD,
6426
+ // RFC 6901 escaping, or a key containing `/` or `~` would build a
6427
+ // pointer that parses as a different (nested) location.
6428
+ instancePath: key === void 0 ? "" : `/${escapePointerSegment(key)}`,
6429
+ message,
6430
+ ...line === void 0 ? {} : { line }
6431
+ };
6432
+ const bucket = findings.get(path);
6433
+ if (bucket) bucket.push(err);
6434
+ else findings.set(path, [err]);
6435
+ }
6436
+ return findings;
6437
+ }
6438
+ function asLine(value) {
6439
+ const n = typeof value === "bigint" ? Number(value) : typeof value === "number" ? value : void 0;
6440
+ return n !== void 0 && Number.isInteger(n) && n > 0 ? n : void 0;
6441
+ }
6442
+ function synthesizeMessage(extras, row) {
6443
+ const pairs = extras.flatMap((c) => {
6444
+ const value = row[c];
6445
+ if (value === null || value === void 0) return [];
6446
+ const text2 = typeof value === "string" ? value : typeof value === "number" || typeof value === "bigint" ? String(value) : JSON.stringify(value);
6447
+ return [`${c}=${text2}`];
6448
+ });
6449
+ return pairs.length > 0 ? pairs.join(", ") : "check matched";
6450
+ }
6451
+ async function runChecks(checks, entries, ctx = {}) {
6452
+ const merged = /* @__PURE__ */ new Map();
6453
+ if (checks.length === 0) return merged;
6454
+ const { DatabaseSync } = await loadSqlite();
6455
+ const db = new DatabaseSync(":memory:");
6456
+ try {
6457
+ const dataColumns = corpusDataColumns(entries);
6458
+ createDocsTable(db, entries, dataColumns);
6459
+ registerLineFor(db, entries);
6460
+ createCollectionViews(db, collectCollections(entries, ctx));
6461
+ const wantsDerived = checks.filter(
6462
+ (c) => mentionsDerived(c.query) || mentionsResolved(c.query)
6463
+ );
6464
+ if (wantsDerived.length > 0) {
6465
+ if (ctx.derive === void 0) {
6466
+ throw new DocmetaError(
6467
+ `check "${wantsDerived[0]?.name ?? ""}": the SQL names the derived or resolved table, but this run cannot derive. Run it through \`manni meta validate\`, or drop the reference.`
6468
+ );
6469
+ }
6470
+ const commands = ctx.commands;
6471
+ const readable = derivableFields(commands);
6472
+ const fields = /* @__PURE__ */ new Set();
6473
+ for (const c of wantsDerived) for (const f of fieldsForSql(c.query, readable)) fields.add(f);
6474
+ createDerivedView(db, await ctx.derive([...fields]), derivedColumns(commands));
6475
+ createResolvedView(db, dataColumns, commands);
6476
+ }
6477
+ db.exec("PRAGMA query_only = 1");
6478
+ const loaded = new Set(entries.map((e) => e.label));
6479
+ for (const check of checks) {
6480
+ const head = stripLeadingTrivia(check.query);
6481
+ if (/^(attach|vacuum)\b/i.test(head)) {
6482
+ throw new DocmetaError(
6483
+ `check "${check.name}": ${head.split(/\s/, 1)[0]?.toUpperCase() ?? "that statement"} is refused: it can write outside the docs table.`
6484
+ );
6485
+ }
6486
+ let tokens2;
6487
+ try {
6488
+ tokens2 = collectNamedParameters(check.query);
6489
+ } catch (err) {
6490
+ throw new DocmetaError(
6491
+ `check "${check.name}": ${errorMessage(err)}`
6492
+ );
6493
+ }
6494
+ if (tokens2.length > 0) {
6495
+ throw new DocmetaError(
6496
+ `check "${check.name}": the SQL references ${tokens2.join(", ")}, but checks bind no parameters \u2014 an unbound name binds NULL and matches nothing, so the gate would pass on a typo. Inline the value in the SQL.`
6497
+ );
6498
+ }
6499
+ let columns;
6500
+ let rows;
6501
+ try {
6502
+ assertSingleStatement(check.query);
6503
+ const stmt = db.prepare(check.query);
6504
+ columns = stmt.columns().map((c) => c.name);
6505
+ rows = stmt.all();
6506
+ } catch (err) {
6507
+ const message = errorMessage(err);
6508
+ throw new DocmetaError(
6509
+ message.includes("attempt to write a readonly database") ? `check "${check.name}": ${message} \u2014 checks are read-only by design; a check is a SELECT over the projection. Edit the corpus with \`manni meta query\` instead.` : `check "${check.name}": ${message}`
6510
+ );
6511
+ }
6512
+ for (const [path, errs] of rowsToFindings(
6513
+ check.name,
6514
+ columns,
6515
+ rows,
6516
+ loaded
6517
+ )) {
6518
+ const bucket = merged.get(path);
6519
+ if (bucket) bucket.push(...errs);
6520
+ else merged.set(path, errs);
6521
+ }
6522
+ }
6523
+ } finally {
6524
+ db.close();
6525
+ }
6526
+ return merged;
6527
+ }
6528
+
6529
+ // src/meta/core/derive/provenance-place.ts
6530
+ import { isAbsolute as isAbsolute5, relative as relative7, resolve as resolve6, sep as sep6 } from "node:path";
6531
+ var toPosix3 = (p) => p.split(sep6).join("/");
6532
+ function provenanceManifests(collections, configDir, base, field = PROVENANCE_FIELD) {
6533
+ const out = [];
6534
+ for (const collection of collections) {
6535
+ for (const manifest of collection.externalMetadata) {
6536
+ const implied = manifest.keys === void 0;
6537
+ if (!implied && manifest.keys?.includes(field) !== true) continue;
6538
+ const url = classifyRef(manifest.file).kind === "url";
6539
+ if (url && !implied) throw new DocmetaError(urlRefusal(collection.name, field));
6540
+ if (url) {
6541
+ out.push({
6542
+ collection: collection.name,
6543
+ absPath: manifest.file,
6544
+ file: manifest.file,
6545
+ join: externalMetadataJoin(manifest),
6546
+ perPage: false,
6547
+ written: manifest.file,
6548
+ implied: { collection, manifest, url }
6549
+ });
6550
+ continue;
6551
+ }
6552
+ const absPath = isAbsolute5(manifest.file) ? manifest.file : resolve6(configDir, manifest.file);
6553
+ const perPage = hasPagePlaceholder(manifest.file);
6554
+ out.push({
6555
+ collection: collection.name,
6556
+ absPath,
6557
+ file: perPage ? manifest.file : reportedPath(absPath, base),
6558
+ join: externalMetadataJoin(manifest),
6559
+ perPage,
6560
+ written: manifest.file,
6561
+ ...implied ? { implied: { collection, manifest, url } } : {}
6562
+ });
6563
+ }
6564
+ }
6565
+ return out;
6566
+ }
6567
+ function urlRefusal(collection, field) {
6568
+ return `collection ${collection}: ${field} cannot come from a URL manifest, because manni meta derive writes it.`;
6569
+ }
6570
+ function provenancePlace(label, data, manifests, collections, configDir, base, marked, field = PROVENANCE_FIELD) {
6571
+ if (manifests.length === 0) return void 0;
6572
+ const members = new Set(memberOf(collections, configDir, base, label));
6573
+ const owning = manifests.filter(
6574
+ (m) => members.has(m.collection) && (m.implied === void 0 || ownsKey(m.implied.collection, m.implied.manifest, field, marked))
6575
+ );
6576
+ const [declared, second] = owning;
6577
+ if (declared === void 0) return void 0;
6578
+ if (second !== void 0) {
6579
+ throw new DocmetaError(
6580
+ `${label} is in collections ${declared.collection} and ${second.collection}, and both keep ${field} in a manifest.`
6581
+ );
6582
+ }
6583
+ if (declared.implied?.url === true) throw new DocmetaError(urlRefusal(declared.collection, field));
6584
+ const manifest = declared.perPage ? ownManifest(declared, label, collections, configDir, base) : declared;
6585
+ if (manifest.join === PATH_JOIN) {
6586
+ const entry = toPosix3(relative7(configDir, resolve6(base, label)));
6587
+ return { manifest, absPath: manifest.absPath, entry, join: manifest.join };
6588
+ }
6589
+ const value = data[manifest.join];
6590
+ if (typeof value !== "string" && typeof value !== "number") {
6591
+ throw new DocmetaError(
6592
+ `${label} carries no ${manifest.join}, which ${manifest.file} joins on, so its ${field} has no entry there.`
6593
+ );
6594
+ }
6595
+ return { manifest, absPath: manifest.absPath, entry: String(value), join: manifest.join };
6596
+ }
6597
+ function ownManifest(manifest, label, collections, configDir, base) {
6598
+ const resolved = pageManifestPath(manifest.written, configDir, resolve6(base, label));
6599
+ if ("outside" in resolved) {
6600
+ const declared = collections.find((c) => c.name === manifest.collection)?.externalMetadata.find((m) => m.file === manifest.written);
6601
+ throw new DocmetaError(
6602
+ outsideRefusal(declared ?? { file: manifest.written }, manifest.collection, resolved.pageRel)
6603
+ );
6604
+ }
6605
+ return { ...manifest, absPath: resolved.abs, file: reportedPath(resolved.abs, base) };
6606
+ }
6607
+ function readerManifests(collections, configDir, base, field = PROVENANCE_FIELD) {
6608
+ const local = collections.map((c) => ({
6609
+ ...c,
6610
+ externalMetadata: c.externalMetadata.filter((m) => classifyRef(m.file).kind !== "url")
6611
+ }));
6612
+ return provenanceManifests(local, configDir, base, field);
6613
+ }
6614
+ function readerPlace(label, data, manifests, collections, configDir, base, marked, field = PROVENANCE_FIELD) {
6615
+ try {
6616
+ const place = provenancePlace(label, data, manifests, collections, configDir, base, marked, field);
6617
+ return place === void 0 ? void 0 : { absPath: place.absPath, entry: place.entry, join: place.join };
6618
+ } catch (err) {
6619
+ if (err instanceof DocmetaError) return void 0;
6620
+ throw err;
6621
+ }
6622
+ }
6623
+ function stampManifests(label, data, collections, configDir, base, marked, fields) {
6624
+ const out = {};
6625
+ for (const field of STAMPED_DATE_FIELDS) {
6626
+ if (!fields.includes(field)) continue;
6627
+ const manifests = readerManifests(collections, configDir, base, field);
6628
+ const place = readerPlace(label, data, manifests, collections, configDir, base, marked, field);
6629
+ if (place !== void 0) out[field] = place;
6630
+ }
6631
+ return out;
6632
+ }
6633
+
6634
+ // src/shared/cli-options.ts
6635
+ import { basename as basename4, relative as relative8 } from "node:path";
6636
+ function collect(value, prev) {
6637
+ return prev.concat([value]);
6638
+ }
6639
+ function splitList(value) {
6640
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
6641
+ }
6642
+ var STDIN_REQUIRES_AS = "Reading from stdin (`-`) requires --as <format> to choose an extractor.";
6643
+ async function readStdin() {
6644
+ const chunks = [];
6645
+ for await (const chunk of process.stdin) chunks.push(chunk);
6646
+ return Buffer.concat(chunks).toString("utf8");
6647
+ }
6648
+ function configOption(value) {
6649
+ if (value === false) return { noConfig: true };
6650
+ return typeof value === "string" ? { configPath: value } : {};
6651
+ }
6652
+ function explicitFalse(value) {
6653
+ return value === false ? false : void 0;
6654
+ }
6655
+ function reportConfig(toStdout, cwd) {
6656
+ return (info) => {
6657
+ const where = (relative8(cwd, info.dir) || ".").replace(/\\/g, "/");
6658
+ const line = `Using ${basename4(info.path)} (${where})
6659
+ `;
6660
+ (toStdout ? process.stdout : process.stderr).write(line);
6661
+ };
6662
+ }
6663
+
6664
+ // src/meta/commands/validate.ts
6665
+ function parseErrorResult(file, format, message, keyword) {
6666
+ const err = {
6667
+ schema: "(parse)",
6668
+ instancePath: "",
6669
+ message,
6670
+ keyword
6671
+ };
6672
+ return { file, format, ok: false, schemas: [], errors: [err] };
6673
+ }
6674
+ function once(fn) {
6675
+ let pending;
6676
+ return () => {
6677
+ pending ??= fn();
6678
+ return pending;
6679
+ };
6680
+ }
6681
+ async function runValidate(opts) {
6682
+ const cwd = opts.cwd ?? process.cwd();
6683
+ const run2 = await resolveRunConfig({
6684
+ cwd,
6685
+ configPath: opts.configPath,
6686
+ noConfig: opts.noConfig,
6687
+ inputs: opts.inputs,
6688
+ ...opts.collections !== void 0 ? { collections: opts.collections } : {},
6689
+ onConfigLoaded: opts.onConfigLoaded
6690
+ });
6691
+ const {
6692
+ config,
6693
+ inputs,
6694
+ base,
6695
+ configDir,
6696
+ collections,
6697
+ declaredCollections,
6698
+ fromCollections,
6699
+ configFile
6700
+ } = run2;
6701
+ const membersFor = (label) => memberOf(collections, configDir ?? cwd, base, label);
6702
+ const usingStdin = inputs.includes(STDIN_TOKEN);
6703
+ if (inputs.length === 0) {
6704
+ throw new DocmetaError(
6705
+ "No files to validate. Pass paths/globs, or declare a collection under `collections:` in manni.config.yaml."
6706
+ );
6707
+ }
6708
+ const forcedExtractor = opts.as ? extractorByName(opts.as) : void 0;
6709
+ if (opts.as && !forcedExtractor) {
6710
+ throw new DocmetaError(
6711
+ `Unknown format "${opts.as}". Supported extensions: ${supportedExtensions().join(", ")}.`
6712
+ );
6713
+ }
6714
+ const exts = opts.exts ?? (forcedExtractor ? forcedExtractor.extensions : void 0);
6715
+ const fileInputs = inputs.filter((i) => i !== STDIN_TOKEN);
6716
+ const allowEmpty = opts.allowEmpty ?? config?.allowEmpty;
6717
+ const exclude = opts.exclude ?? [];
6718
+ const { files: walked, gitignoreSkipped } = await resolveTargetSet({
6719
+ inputs: fileInputs,
6720
+ exts,
6721
+ exclude,
6722
+ cwd: base,
6723
+ allowEmpty,
6724
+ ...gitignoreOptions({
6725
+ flag: opts.respectGitignore,
6726
+ configured: config?.respectGitignore,
6727
+ onNotice: opts.onNotice
6728
+ })
6729
+ });
6730
+ const files = fromCollections ? retainMembers(walked, membersFor) : walked;
6731
+ assertNonEmpty({
6732
+ files,
6733
+ inputs: fileInputs,
6734
+ usingStdin,
6735
+ allowEmpty,
6736
+ exclude,
6737
+ exts,
6738
+ gitignoreSkipped,
6739
+ action: "validated"
6740
+ });
6741
+ const trustRoot = schemaTrustRoot(cwd, configDir);
6742
+ const externalMetadata = await loadExternalMetadata(collections, {
6743
+ configDir: configDir ?? cwd,
6744
+ base,
6745
+ offline: opts.offline ?? config?.offline ?? false,
6746
+ // A `{page}` manifest (0058) is read for exactly the pages this run reads.
6747
+ pages: files.map((file) => resolve7(base, file))
6748
+ });
6749
+ const validator = new Validator(
6750
+ schemaLoadOptions({
6751
+ // The config's directory when a config governs the run, so one project
6752
+ // keeps one cache no matter which directory the command was run from.
6753
+ root: configDir ?? cwd,
6754
+ // A relative file ref belongs to the run's directory, not the cache root.
6755
+ fileBase: cwd,
6756
+ ttlHours: config?.schemaCache?.ttlHours,
6757
+ offline: opts.offline ?? config?.offline,
6758
+ // Built from the rebased config, so a pinned local ref is keyed by the
6759
+ // same absolute spelling `resolveSchemaSet` will hand to `loadSchema`.
6760
+ pins: collectSchemaPins(config),
6761
+ registered: config?.registered
6762
+ })
6763
+ );
6764
+ const marks = pageMarks({
6765
+ validator,
6766
+ config,
6767
+ cwd,
6768
+ trustRoot,
6769
+ ...opts.cliSchemas !== void 0 ? { cliSchemas: opts.cliSchemas } : {}
6770
+ });
6771
+ let relocation;
6772
+ const relocationCtx = () => relocation ??= relocationContext(run2, {
6773
+ cwd,
6774
+ targets: fromCollections ? [] : opts.inputs,
6775
+ validator,
6776
+ ...opts.noConfig !== void 0 ? { noConfig: opts.noConfig } : {},
6777
+ ...opts.env !== void 0 ? { env: opts.env } : {}
6778
+ });
6779
+ const refFrame = { cwd, base: configDir ?? cwd, runBase: base };
6780
+ const locationFindings = async (label, own, data, refs, members, merged, lineFor) => {
6781
+ const preferences = await validator.locationPreferences(data, refs);
6782
+ if (preferences.size === 0) return [];
6783
+ const owned = new Set(merged.collisions.map((c) => c.key));
6784
+ const declaredMembers = declaredCollections === collections ? members : memberOf(declaredCollections, configDir ?? cwd, base, label);
6785
+ const joins = new Set(
6786
+ declaredCollections.filter((c) => declaredMembers.includes(c.name)).flatMap((c) => c.externalMetadata.map(externalMetadataJoin)).filter((join5) => join5 !== PATH_JOIN)
6787
+ );
6788
+ const findings = [];
6789
+ for (const key of Object.keys(data)) {
6790
+ const preference = preferences.get(key);
6791
+ if (preference === void 0) continue;
6792
+ const ref = canonicalSchemaRef(preference.schema, refFrame);
6793
+ const pointer = externalMetadataPointer(key);
6794
+ const common = {
6795
+ keyword: LOCATION_FINDING_KEYWORD,
6796
+ subject: key,
6797
+ instancePath: pointer,
6798
+ severity: "warning"
6799
+ };
6800
+ if (Object.hasOwn(own, key)) {
6801
+ if (preference.location !== "external" || owned.has(key) || joins.has(key)) continue;
6802
+ const line = lineFor(key);
6803
+ const none = await homeless(label, own, key);
6804
+ findings.push({
6805
+ schema: LOCATION_EXTERNAL_SCHEMA,
6806
+ ...common,
6807
+ // relocate refuses --no-config (U2), so it is no answer there.
6808
+ // Nor is it one for stdin, which relocate refuses: no advice there.
6809
+ message: opts.noConfig ? `"${key}" is stored in the page; ${ref} prefers external metadata, and --no-config leaves it no manifest.` : label === STDIN_LABEL ? `"${key}" is stored in the page; ${ref} prefers external metadata.` : none > 0 ? `"${key}" is stored in the page; ${ref} prefers external metadata, and this document is in none of the ${String(none)} collections.` : `"${key}" is stored in the page; ${ref} prefers external metadata. Run manni meta relocate.`,
6810
+ ...line != null ? { line } : {}
6811
+ });
6812
+ continue;
6813
+ }
6814
+ if (preference.location !== "page") continue;
6815
+ const at2 = merged.locate(pointer);
6816
+ if (at2 === void 0) continue;
6817
+ findings.push({
6818
+ schema: LOCATION_PAGE_SCHEMA,
6819
+ ...common,
6820
+ // A fetched manifest cannot give a value up, so relocate is no
6821
+ // answer; the value moves in the repository that serves it.
6822
+ message: /^https?:\/\//.test(at2.file) ? `"${key}" is stored in manifest ${at2.file}; ${ref} prefers the page, and a fetched manifest cannot be written. Move it in that repository.` : `"${key}" is stored in manifest ${at2.file}; ${ref} prefers the page. Run manni meta relocate.`,
6823
+ file: at2.file,
6824
+ ...at2.line != null ? { line: at2.line } : {}
6825
+ });
6826
+ }
6827
+ return findings;
6828
+ };
6829
+ const homeless = async (label, own, key) => {
6830
+ if (label === STDIN_LABEL) return 0;
6831
+ const home = await keyHome(relocationCtx(), label, own, key);
6832
+ return home.kind === "unowned" && home.home.kind === "none" && home.home.reason === "collections" && home.home.collections > 1 ? home.home.collections : 0;
6833
+ };
6834
+ const results = [];
6835
+ const skippedDefaultOnly = /* @__PURE__ */ new Set();
6836
+ const defaultOnly = (label, members) => {
6837
+ try {
6838
+ return resolveSchemaSetWithSource({
6839
+ filePath: label,
6840
+ fileSchema: void 0,
6841
+ cliSchemas: opts.cliSchemas,
6842
+ config,
6843
+ memberOf: members,
6844
+ fileBase: cwd,
6845
+ trustRoot
6846
+ }).source === "default";
6847
+ } catch {
6848
+ return false;
6849
+ }
6850
+ };
6851
+ const joinHits = /* @__PURE__ */ new Map();
6852
+ const scopedToCollections = (opts.collections?.length ?? 0) > 0;
6853
+ const scopedByFlags = opts.inputs.length > 0 || // redundant with inputs.length (stdin is an input) — kept as belt-and-suspenders
6854
+ usingStdin || opts.as !== void 0 || opts.exts !== void 0 || opts.exclude !== void 0 && opts.exclude.length > 0 || opts.respectGitignore !== void 0 || (opts.cliSchemas?.length ?? 0) > 0;
6855
+ const scoped = scopedByFlags || scopedToCollections;
6856
+ const configuredChecks = config?.checks ?? [];
6857
+ if (!scopedByFlags) {
6858
+ const covered = scopedToCollections ? collections.map((c) => c.name) : void 0;
6859
+ const orphans = orphanEntries(externalMetadata, files, base, covered);
6860
+ if (orphans.length > 0) throw orphanError(orphans);
6861
+ }
6862
+ const checksWillRun = configuredChecks.length > 0 && opts.checks !== false && !scoped;
6863
+ const checkEntries = [];
6864
+ const encryptionKey = lazyKey(configFile, opts.env);
6865
+ let unverified = 0;
6866
+ const deriveConfig = config?.derive;
6867
+ const configuredFields = managedFields(deriveConfig);
6868
+ const defaultedFields = deriveConfig !== void 0 && deriveConfig.fields === void 0;
6869
+ const deriveWillRun = deriveConfig !== void 0 && configuredFields.length > 0 && opts.derive !== false;
6870
+ const pageManaged = /* @__PURE__ */ new Map();
6871
+ const derivedChecks = checksWillRun ? configuredChecks.filter((c) => mentionsDerived(c.query) || mentionsResolved(c.query)) : [];
6872
+ const checksNeedDerived = derivedChecks.length > 0;
6873
+ const deriveInputs = [];
6874
+ const managedViews = /* @__PURE__ */ new Map();
6875
+ const keepDeriveInputs = deriveWillRun || checksNeedDerived;
6876
+ const deriveCommands = commandsOf(deriveConfig);
6877
+ const deriveFields = new Set(deriveWillRun && !defaultedFields ? configuredFields : []);
6878
+ const readable = derivableFields(deriveCommands);
6879
+ for (const c of derivedChecks) for (const f of fieldsForSql(c.query, readable)) deriveFields.add(f);
6880
+ const provenanceManifests2 = keepDeriveInputs && (deriveFields.has(PROVENANCE_FIELD) || deriveWillRun && configuredFields.includes(PROVENANCE_FIELD)) ? readerManifests(declaredCollections, configDir ?? cwd, base) : [];
6881
+ const derivedOnce = once(async () => {
6882
+ const derived = await deriveMetadata(deriveInputs, {
6883
+ cwd,
6884
+ base,
6885
+ ...configDir !== void 0 ? { configDir } : {},
6886
+ sources: deriveConfig?.sources ?? [...DERIVE_SOURCES],
6887
+ fields: [...deriveFields],
6888
+ ...deriveConfig?.codeowners !== void 0 ? { codeowners: deriveConfig.codeowners } : {},
6889
+ ...deriveCommands !== void 0 ? { commands: deriveCommands } : {},
6890
+ cache: opts.cache ?? true,
6891
+ now: opts.now ?? (() => /* @__PURE__ */ new Date()),
6892
+ machines: machinesOf(deriveConfig)
6893
+ });
6894
+ assertSourcesAvailable(
6895
+ derived.sources,
6896
+ deriveWillRun ? "pass --no-derive to skip the comparison, or narrow derive.sources" : "narrow derive.sources in manni.config.yaml"
6897
+ );
6898
+ for (const [name, status] of Object.entries(derived.sources)) {
6899
+ if (status.available && status.reason !== void 0) {
6900
+ opts.onNotice?.(`${name}: ${status.reason}`);
6901
+ }
6902
+ }
6903
+ return derived;
6904
+ });
6905
+ const processOne = async (label, content, extension) => {
6906
+ const extractor = forcedExtractor ?? extractorForExtension(extension);
6907
+ if (!extractor) {
6908
+ throw new DocmetaError(
6909
+ `Unsupported file type "${extension}" for "${label}". Supported: ${supportedExtensions().join(", ")}. Use --as to override.`
6910
+ );
6911
+ }
6912
+ const members = membersFor(label);
6913
+ let extracted;
6914
+ try {
6915
+ extracted = extractor.extract(content, label, {
6916
+ elements: resolveElements(label, config, members)
6917
+ });
6918
+ } catch (err) {
6919
+ if (err instanceof DocmetaError) throw err;
6920
+ if (opts.skipDefaultOnly === true && defaultOnly(label, members)) {
6921
+ skippedDefaultOnly.add(label);
6922
+ return;
6923
+ }
6924
+ results.push(
6925
+ parseErrorResult(label, extractor.name, errorMessage(err), "parse")
6926
+ );
6927
+ return;
6928
+ }
6929
+ const merged = await mergeWithMarks(label, extracted, externalMetadata, members, base, {
6930
+ encryptionKey,
6931
+ marks
6932
+ });
6933
+ if (keepDeriveInputs && label !== STDIN_LABEL && (checksNeedDerived || deriveCovers(deriveConfig, members))) {
6934
+ const place = provenanceManifests2.length === 0 ? void 0 : readerPlace(label, extracted.data, provenanceManifests2, declaredCollections, configDir ?? cwd, base, merged.marked);
6935
+ const owned = declaredCollections.length === 0 ? {} : stampManifests(label, extracted.data, declaredCollections, configDir ?? cwd, base, merged.marked, STAMPED_DATE_FIELDS);
6936
+ deriveInputs.push({
6937
+ label,
6938
+ absPath: resolve7(base, label),
6939
+ content,
6940
+ extracted,
6941
+ ...place !== void 0 ? { provenanceManifest: place } : {},
6942
+ ...Object.keys(owned).length > 0 ? { stampManifests: owned } : {}
6943
+ });
6944
+ }
6945
+ const own = extracted.data;
6946
+ extracted = merged.extracted;
6947
+ if (deriveWillRun && label !== STDIN_LABEL) {
6948
+ managedViews.set(label, {
6949
+ data: extracted.data,
6950
+ marked: [],
6951
+ hidden: [],
6952
+ refs: [],
6953
+ locate: merged.locate
6954
+ });
6955
+ }
6956
+ for (const j of merged.joins) {
6957
+ const byValue = joinHits.get(j.field) ?? /* @__PURE__ */ new Map();
6958
+ const line = extracted.lineFor(j.field);
6959
+ const hits = byValue.get(j.value) ?? [];
6960
+ hits.push({
6961
+ label,
6962
+ file: j.file,
6963
+ shown: j.pageValue ?? j.value,
6964
+ ...line != null ? { line } : {}
6965
+ });
6966
+ byValue.set(j.value, hits);
6967
+ joinHits.set(j.field, byValue);
6968
+ }
6969
+ if (checksWillRun) checkEntries.push({ label, extracted });
6970
+ let resolved;
6971
+ try {
6972
+ resolved = resolveSchemaSetWithSource({
6973
+ filePath: label,
6974
+ fileSchema: extracted.data[FILE_SCHEMA_KEY],
6975
+ cliSchemas: opts.cliSchemas,
6976
+ config,
6977
+ memberOf: members,
6978
+ // A document's own `$schema` is measured from the run's directory, the
6979
+ // same base `loadSchema` will read it from, and contained to the
6980
+ // repository the run is standing in.
6981
+ fileBase: cwd,
6982
+ trustRoot,
6983
+ onNotice: opts.onNotice
6984
+ });
6985
+ } catch (err) {
6986
+ results.push(
6987
+ parseErrorResult(label, extractor.name, errorMessage(err), "schema")
6988
+ );
6989
+ return;
6990
+ }
6991
+ if (opts.skipDefaultOnly === true && resolved.source === "default") {
6992
+ skippedDefaultOnly.add(label);
6993
+ return;
6994
+ }
6995
+ const schemaSet = resolved.schemas;
6996
+ if (deriveWillRun && defaultedFields && label !== STDIN_LABEL && deriveCovers(deriveConfig, members)) {
6997
+ let claimed = /* @__PURE__ */ new Set();
6998
+ try {
6999
+ claimed = await validator.claimedProperties(schemaSet);
7000
+ } catch (err) {
7001
+ if (!(err instanceof DocmetaError)) throw err;
7002
+ }
7003
+ const own2 = configuredFields.filter((f) => claimed.has(f));
7004
+ pageManaged.set(label, own2);
7005
+ for (const f of own2) deriveFields.add(f);
7006
+ }
7007
+ let errors;
7008
+ try {
7009
+ const view = await encryptionView({
7010
+ data: extracted.data,
7011
+ refs: schemaSet,
7012
+ validator,
7013
+ key: encryptionKey,
7014
+ locate: merged.locate
7015
+ });
7016
+ unverified += view.unverified.length;
7017
+ if (deriveWillRun && label !== STDIN_LABEL) {
7018
+ managedViews.set(label, {
7019
+ data: view.data,
7020
+ marked: view.marked,
7021
+ hidden: [...view.unreadable, ...view.unverified],
7022
+ refs: schemaSet,
7023
+ locate: merged.locate
7024
+ });
7025
+ }
7026
+ errors = [
7027
+ ...settleFindings(
7028
+ await validator.validate(
7029
+ view.data,
7030
+ schemaSet,
7031
+ extracted.lineFor,
7032
+ extracted.colFor,
7033
+ merged.locate
7034
+ ),
7035
+ view,
7036
+ extracted,
7037
+ merged.locate
7038
+ ),
7039
+ ...encryptionFindings(view, extracted, merged.locate),
7040
+ // Over the decrypted view, the object validation evaluated: a mark
7041
+ // counts where Ajv takes a branch, and a branch may read a value.
7042
+ ...await locationFindings(label, own, view.data, schemaSet, members, merged, extracted.lineFor)
7043
+ ];
7044
+ } catch (err) {
7045
+ if (!(err instanceof DocmetaError) || resolved.source !== "document") {
7046
+ throw err;
7047
+ }
7048
+ results.push(
7049
+ parseErrorResult(label, extractor.name, err.message, "schema")
7050
+ );
7051
+ return;
7052
+ }
7053
+ for (const { key, file, collection } of merged.collisions) {
7054
+ const line = extracted.lineFor(key);
7055
+ errors.push({
7056
+ schema: EXTERNAL_OWNED_SCHEMA,
7057
+ keyword: EXTERNAL_KEYWORD,
7058
+ subject: key,
7059
+ instancePath: externalMetadataPointer(key),
7060
+ message: `"${key}" is owned by manifest ${file} (collection ${collection}); remove it from the document`,
7061
+ ...line != null ? { line } : {}
7062
+ });
7063
+ }
7064
+ results.push({
7065
+ file: label,
7066
+ format: extractor.name,
7067
+ ok: !errors.some(isErrorSeverity),
7068
+ schemas: schemaSet,
7069
+ errors
7070
+ });
7071
+ };
7072
+ if (usingStdin) {
7073
+ const content = opts.stdinContent ?? "";
7074
+ if (!forcedExtractor) {
7075
+ throw new DocmetaError(
7076
+ STDIN_REQUIRES_AS
7077
+ );
7078
+ }
7079
+ await processOne(STDIN_LABEL, content, forcedExtractor.extensions[0] ?? "");
7080
+ }
7081
+ for (const file of files) {
7082
+ const content = await readFile6(resolve7(base, file), "utf8");
7083
+ await processOne(file, content, extname3(file));
7084
+ }
7085
+ if (unverified > 0) opts.onNotice?.(unverifiedWarning(unverified));
7086
+ if (externalMetadata) {
7087
+ const byLabel = new Map(results.map((r) => [r.file, r]));
7088
+ const matched = /* @__PURE__ */ new Map();
7089
+ for (const [field, byValue] of joinHits) {
7090
+ matched.set(field, new Set(byValue.keys()));
7091
+ for (const hits of byValue.values()) {
7092
+ if (hits.length < 2) continue;
7093
+ for (const hit of hits) {
7094
+ const others = hits.filter((h) => h.label !== hit.label).map((h) => h.label).join(", ");
7095
+ const result = byLabel.get(hit.label);
7096
+ if (!result) continue;
7097
+ result.errors.push({
7098
+ schema: EXTERNAL_DUPLICATE_SCHEMA,
7099
+ keyword: EXTERNAL_KEYWORD,
7100
+ subject: hit.shown,
7101
+ instancePath: externalMetadataPointer(field),
7102
+ message: `${hits.length} documents carry ${field} "${hit.shown}"; ${hit.file} cannot tell them apart (${others})`,
7103
+ ...hit.line != null ? { line: hit.line } : {}
7104
+ });
7105
+ result.ok = false;
7106
+ }
7107
+ }
7108
+ }
7109
+ if (!scopedByFlags) {
7110
+ const covered = scopedToCollections ? collections.map((c) => c.name) : void 0;
7111
+ const orphans = orphanJoins(externalMetadata, matched, covered);
7112
+ if (orphans.length > 0) throw orphanError(orphans);
7113
+ }
7114
+ }
7115
+ if (configuredChecks.length > 0 && opts.checks !== false) {
7116
+ if (scoped) {
7117
+ opts.onNotice?.(
7118
+ scopedToCollections ? `corpus checks skipped: run is scoped to collections ${collections.map((c) => c.name).join(", ")}` : "corpus checks skipped: run is scoped"
7119
+ );
7120
+ } else {
7121
+ const findings = await runChecks(configuredChecks, checkEntries, {
7122
+ // Every declared collection is a view (0041 rule 7), holding the
7123
+ // members this run loaded — decided by the same path arithmetic
7124
+ // `membersFor` uses, so a check and a `manni meta query` over the same
7125
+ // corpus see the same collections.
7126
+ collections: declaredCollections,
7127
+ ...configDir !== void 0 ? { configDir } : {},
7128
+ base,
7129
+ // A check that names the `derived` table (0040) gets the same view
7130
+ // `query` builds, from the run's one derivation — which already
7131
+ // covers every field the checks can read, so the list handed back
7132
+ // here is not needed. Built only when asked for, because building
7133
+ // it spawns git.
7134
+ derive: async () => (await derivedOnce()).records,
7135
+ // Its columns include the configured commands' keys (0042), which a
7136
+ // check's context cannot read from a config since 0041.
7137
+ ...deriveCommands !== void 0 ? { commands: deriveCommands } : {}
7138
+ });
7139
+ const byFile = new Map(results.map((r) => [r.file, r]));
7140
+ for (const [file, errs] of findings) {
7141
+ const result = byFile.get(file);
7142
+ if (!result && skippedDefaultOnly.has(file)) continue;
7143
+ if (!result) {
7144
+ throw new DocmetaError(
7145
+ `check findings for "${file}" have no validation result to attach to.`
7146
+ );
7147
+ }
7148
+ result.errors.push(...errs);
7149
+ result.ok = result.ok && !errs.some(isErrorSeverity);
7150
+ }
7151
+ }
7152
+ }
7153
+ if (deriveWillRun && deriveInputs.length > 0) {
7154
+ const derived = await derivedOnce();
7155
+ const byFile = new Map(results.map((r) => [r.file, r]));
7156
+ for (const input of deriveInputs) {
7157
+ const record = derived.records.get(input.label);
7158
+ const result = byFile.get(input.label);
7159
+ if (!record || !result) continue;
7160
+ const managed = managedViews.get(input.label);
7161
+ const managedHere = !deriveCovers(deriveConfig, membersFor(input.label)) ? [] : defaultedFields ? pageManaged.get(input.label) ?? [] : configuredFields;
7162
+ const fields = managedHere.filter(
7163
+ (field) => !(managed?.hidden.some((p) => isAtOrUnder(p, pointerOf(field))) ?? false)
7164
+ );
7165
+ let marked = [];
7166
+ if (managed !== void 0) {
7167
+ const stamped = { ...managed.data };
7168
+ for (const field of fields) {
7169
+ const d = record.fields[field];
7170
+ if (d != null) stamped[field] = d.value;
7171
+ }
7172
+ marked = [...managed.marked, ...await validator.markedPointers(stamped, managed.refs)];
7173
+ }
7174
+ const asserted = (field) => managed !== void 0 ? managed.data[field] : input.extracted.data[field];
7175
+ const findings = fields.flatMap((field) => {
7176
+ if (field === PROVENANCE_FIELD) {
7177
+ const derivation = derived.provenance?.get(input.label);
7178
+ if (derivation === void 0) return [];
7179
+ return provenanceFindings(
7180
+ compareProvenance(provenanceEntries(asserted(field)), derivation),
7181
+ derivation.page.bodyLine
7182
+ );
7183
+ }
7184
+ const compared = compareDerived(field, asserted(field), record.fields[field]);
7185
+ const at2 = pointerOf(field);
7186
+ return staleFindings(
7187
+ [marked.some((p) => isAtOrUnder(p, at2)) ? redactedDerived(compared) : compared],
7188
+ input.extracted.lineFor,
7189
+ managed?.locate
7190
+ );
7191
+ });
7192
+ if (findings.length === 0) continue;
7193
+ result.errors.push(...findings);
7194
+ result.ok = false;
7195
+ }
7196
+ }
7197
+ const frame = { cwd, base: configDir ?? cwd, runBase: base };
7198
+ const { results: reported3, baseline } = await settleBaseline(
7199
+ results,
7200
+ resolveBaselineRequest(opts, config?.baseline, configDir, cwd, {
7201
+ current: DEFAULT_BASELINE_PATH,
7202
+ legacy: LEGACY_BASELINE_PATH
7203
+ }),
7204
+ frame
7205
+ );
7206
+ const failed = reported3.filter((r) => !r.ok).length;
7207
+ const count2 = (keep) => reported3.reduce((n, r) => n + r.errors.filter(keep).length, 0);
7208
+ const warnings = count2((e) => e.severity === "warning");
7209
+ const notices = count2((e) => e.severity === "notice");
7210
+ const summary = {
7211
+ files: reported3.length,
7212
+ passed: reported3.length - failed,
7213
+ failed,
7214
+ errors: count2(isErrorSeverity),
7215
+ // Omitted at zero, like `gitignoreSkipped` below: only the location
7216
+ // findings (proposal 0047) are warnings, so a run with no
7217
+ // `x-manni-location` marks keeps the summary as it was.
7218
+ ...warnings > 0 ? { warnings } : {},
7219
+ ...notices > 0 ? { notices } : {},
7220
+ // Omitted when nothing was skipped: there is nothing to audit, and the
7221
+ // JSON summary stays as it was for every run in a clean repo.
7222
+ ...gitignoreSkipped > 0 ? { gitignoreSkipped } : {},
7223
+ ...baseline ? { baseline } : {}
7224
+ };
7225
+ return { results: reported3, summary, frame };
7226
+ }
7227
+
4270
7228
  export {
7229
+ collect,
7230
+ splitList,
7231
+ STDIN_REQUIRES_AS,
7232
+ readStdin,
7233
+ configOption,
7234
+ explicitFalse,
7235
+ reportConfig,
4271
7236
  writeTextAtomic,
4272
7237
  ENCRYPTION_KEY_ENV,
4273
7238
  resolveEncryptionKey,
@@ -4287,7 +7252,6 @@ export {
4287
7252
  META_CONTEXT,
4288
7253
  EncryptionRefusal,
4289
7254
  unreadableMessage,
4290
- unverifiedWarning,
4291
7255
  lazyKey,
4292
7256
  pointerOf,
4293
7257
  topLevelKeyOf,
@@ -4296,17 +7260,14 @@ export {
4296
7260
  withValueAt,
4297
7261
  encryptionView,
4298
7262
  settleFindings,
4299
- encryptionFindings,
4300
7263
  redactForModel,
4301
7264
  redactedDerived,
4302
7265
  EXTERNAL_OWNED_SCHEMA,
4303
7266
  EXTERNAL_DUPLICATE_SCHEMA,
4304
7267
  EXTERNAL_KEYWORD,
4305
7268
  PATH_JOIN,
4306
- reportedPath,
4307
7269
  externalMetadataJoin,
4308
7270
  loadExternalMetadata,
4309
- outsideRefusal,
4310
7271
  mergeExternalMetadata,
4311
7272
  mergeWithMarks,
4312
7273
  externalMetadataPointer,
@@ -4317,7 +7278,6 @@ export {
4317
7278
  LOCATION_KEYWORD,
4318
7279
  LOCATION_EXTERNAL_SCHEMA,
4319
7280
  LOCATION_PAGE_SCHEMA,
4320
- LOCATION_FINDING_KEYWORD,
4321
7281
  GRAPH_OUTPUT_KEYWORD,
4322
7282
  compileWithFormats,
4323
7283
  stringFormatNames,
@@ -4332,7 +7292,6 @@ export {
4332
7292
  familyMarks,
4333
7293
  SYSTEM_COLUMNS,
4334
7294
  RESERVED,
4335
- quoteIdent,
4336
7295
  bindValue,
4337
7296
  corpusDataColumns,
4338
7297
  createDocsTable,
@@ -4380,19 +7339,57 @@ export {
4380
7339
  toFileLines,
4381
7340
  toBodyLines,
4382
7341
  splitPageArgument,
4383
- ZERO_SHA,
4384
- DEFAULT_MACHINES,
4385
7342
  provenanceEntries,
4386
7343
  readProvenancePage,
4387
- parseLinePorcelain,
4388
- machineIdentity,
4389
- deriveProvenance,
4390
7344
  compareProvenance,
4391
7345
  planProvenanceWrite,
4392
7346
  rangeResults,
4393
7347
  provenanceFindings,
4394
7348
  renderProvenanceValue,
4395
7349
  parseProvenanceTarget,
4396
- attributeRange
7350
+ commandsOf,
7351
+ machinesOf,
7352
+ REVIEW_CACHE_DIR,
7353
+ REVIEW_CACHE_VERSION,
7354
+ ReviewCache,
7355
+ cachedClient,
7356
+ BinMissing,
7357
+ run,
7358
+ PATH_PLACEHOLDER,
7359
+ isPerFile,
7360
+ argvFor,
7361
+ valueOf,
7362
+ deriveFromCommands,
7363
+ parseOriginUrl,
7364
+ identityFromOrigin,
7365
+ GitHubClient,
7366
+ GitLabClient,
7367
+ createReviewClient,
7368
+ deriveFromReviews,
7369
+ mapConcurrent,
7370
+ provenanceFenced,
7371
+ FIELD_SOURCES,
7372
+ sourcesFor,
7373
+ consultedSources,
7374
+ assertSourcesAvailable,
7375
+ deriveMetadata,
7376
+ DERIVED_VIEW,
7377
+ DERIVED_ROWS,
7378
+ RESOLVED_VIEW,
7379
+ derivedColumns,
7380
+ mentionsDerived,
7381
+ fieldsForSql,
7382
+ createDerivedView,
7383
+ resolvedColumns,
7384
+ mentionsResolved,
7385
+ createResolvedView,
7386
+ deriveForTable,
7387
+ rowsToFindings,
7388
+ provenanceManifests,
7389
+ provenancePlace,
7390
+ readerManifests,
7391
+ readerPlace,
7392
+ stampManifests,
7393
+ runValidate
4397
7394
  };
4398
- //# sourceMappingURL=chunk-M3W7J6MU.js.map
7395
+ //# sourceMappingURL=chunk-TUAAR3MM.js.map