@maverickcer/env-cap 0.2.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 (195) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +279 -0
  3. package/dist/.dts/build/assemble-project.d.ts +47 -0
  4. package/dist/.dts/build/assemble-project.d.ts.map +1 -0
  5. package/dist/.dts/build/change-model.d.ts +62 -0
  6. package/dist/.dts/build/change-model.d.ts.map +1 -0
  7. package/dist/.dts/build/check-artifacts.d.ts +48 -0
  8. package/dist/.dts/build/check-artifacts.d.ts.map +1 -0
  9. package/dist/.dts/build/citation-verification.d.ts +62 -0
  10. package/dist/.dts/build/citation-verification.d.ts.map +1 -0
  11. package/dist/.dts/build/compatibility.d.ts +76 -0
  12. package/dist/.dts/build/compatibility.d.ts.map +1 -0
  13. package/dist/.dts/build/contract-model.d.ts +92 -0
  14. package/dist/.dts/build/contract-model.d.ts.map +1 -0
  15. package/dist/.dts/build/deep-equal.d.ts +9 -0
  16. package/dist/.dts/build/deep-equal.d.ts.map +1 -0
  17. package/dist/.dts/build/deep-freeze.d.ts +20 -0
  18. package/dist/.dts/build/deep-freeze.d.ts.map +1 -0
  19. package/dist/.dts/build/dependency-graph.d.ts +168 -0
  20. package/dist/.dts/build/dependency-graph.d.ts.map +1 -0
  21. package/dist/.dts/build/dependency-model.d.ts +79 -0
  22. package/dist/.dts/build/dependency-model.d.ts.map +1 -0
  23. package/dist/.dts/build/discover.d.ts +25 -0
  24. package/dist/.dts/build/discover.d.ts.map +1 -0
  25. package/dist/.dts/build/display-path.d.ts +24 -0
  26. package/dist/.dts/build/display-path.d.ts.map +1 -0
  27. package/dist/.dts/build/docs.d.ts +176 -0
  28. package/dist/.dts/build/docs.d.ts.map +1 -0
  29. package/dist/.dts/build/env-example.d.ts +85 -0
  30. package/dist/.dts/build/env-example.d.ts.map +1 -0
  31. package/dist/.dts/build/errors.d.ts +78 -0
  32. package/dist/.dts/build/errors.d.ts.map +1 -0
  33. package/dist/.dts/build/evidence-cache.d.ts +74 -0
  34. package/dist/.dts/build/evidence-cache.d.ts.map +1 -0
  35. package/dist/.dts/build/evidence-model.d.ts +50 -0
  36. package/dist/.dts/build/evidence-model.d.ts.map +1 -0
  37. package/dist/.dts/build/evidence-reference.d.ts +67 -0
  38. package/dist/.dts/build/evidence-reference.d.ts.map +1 -0
  39. package/dist/.dts/build/evidence-snapshot.d.ts +123 -0
  40. package/dist/.dts/build/evidence-snapshot.d.ts.map +1 -0
  41. package/dist/.dts/build/exclusive-group.d.ts +19 -0
  42. package/dist/.dts/build/exclusive-group.d.ts.map +1 -0
  43. package/dist/.dts/build/finding-model.d.ts +79 -0
  44. package/dist/.dts/build/finding-model.d.ts.map +1 -0
  45. package/dist/.dts/build/generate-documentation.d.ts +176 -0
  46. package/dist/.dts/build/generate-documentation.d.ts.map +1 -0
  47. package/dist/.dts/build/generate-env-artifacts.d.ts +176 -0
  48. package/dist/.dts/build/generate-env-artifacts.d.ts.map +1 -0
  49. package/dist/.dts/build/generate-evidence.d.ts +77 -0
  50. package/dist/.dts/build/generate-evidence.d.ts.map +1 -0
  51. package/dist/.dts/build/generate-manifest.d.ts +101 -0
  52. package/dist/.dts/build/generate-manifest.d.ts.map +1 -0
  53. package/dist/.dts/build/generate-usage.d.ts +95 -0
  54. package/dist/.dts/build/generate-usage.d.ts.map +1 -0
  55. package/dist/.dts/build/generated-banner.d.ts +52 -0
  56. package/dist/.dts/build/generated-banner.d.ts.map +1 -0
  57. package/dist/.dts/build/glob.d.ts +18 -0
  58. package/dist/.dts/build/glob.d.ts.map +1 -0
  59. package/dist/.dts/build/governance-fields.d.ts +34 -0
  60. package/dist/.dts/build/governance-fields.d.ts.map +1 -0
  61. package/dist/.dts/build/humanize-key.d.ts +14 -0
  62. package/dist/.dts/build/humanize-key.d.ts.map +1 -0
  63. package/dist/.dts/build/index.d.ts +107 -0
  64. package/dist/.dts/build/index.d.ts.map +1 -0
  65. package/dist/.dts/build/lifecycle-model.d.ts +64 -0
  66. package/dist/.dts/build/lifecycle-model.d.ts.map +1 -0
  67. package/dist/.dts/build/link.d.ts +215 -0
  68. package/dist/.dts/build/link.d.ts.map +1 -0
  69. package/dist/.dts/build/literal-eval.d.ts +23 -0
  70. package/dist/.dts/build/literal-eval.d.ts.map +1 -0
  71. package/dist/.dts/build/live-expirations.d.ts +47 -0
  72. package/dist/.dts/build/live-expirations.d.ts.map +1 -0
  73. package/dist/.dts/build/manifest-snapshot.d.ts +229 -0
  74. package/dist/.dts/build/manifest-snapshot.d.ts.map +1 -0
  75. package/dist/.dts/build/manifest.d.ts +31 -0
  76. package/dist/.dts/build/manifest.d.ts.map +1 -0
  77. package/dist/.dts/build/map-utils.d.ts +8 -0
  78. package/dist/.dts/build/map-utils.d.ts.map +1 -0
  79. package/dist/.dts/build/ownership-model.d.ts +57 -0
  80. package/dist/.dts/build/ownership-model.d.ts.map +1 -0
  81. package/dist/.dts/build/parse.d.ts +199 -0
  82. package/dist/.dts/build/parse.d.ts.map +1 -0
  83. package/dist/.dts/build/reference-projections.d.ts +156 -0
  84. package/dist/.dts/build/reference-projections.d.ts.map +1 -0
  85. package/dist/.dts/build/resolution/resolve-import.d.ts +53 -0
  86. package/dist/.dts/build/resolution/resolve-import.d.ts.map +1 -0
  87. package/dist/.dts/build/resolution/resolve-package-schema.d.ts +83 -0
  88. package/dist/.dts/build/resolution/resolve-package-schema.d.ts.map +1 -0
  89. package/dist/.dts/build/resolution/resolve-tsconfig-paths.d.ts +101 -0
  90. package/dist/.dts/build/resolution/resolve-tsconfig-paths.d.ts.map +1 -0
  91. package/dist/.dts/build/resolution/resolve-within-root.d.ts +35 -0
  92. package/dist/.dts/build/resolution/resolve-within-root.d.ts.map +1 -0
  93. package/dist/.dts/build/resolve-import.d.ts +50 -0
  94. package/dist/.dts/build/resolve-import.d.ts.map +1 -0
  95. package/dist/.dts/build/resolve-package-schema.d.ts +99 -0
  96. package/dist/.dts/build/resolve-package-schema.d.ts.map +1 -0
  97. package/dist/.dts/build/resolve-tsconfig-paths.d.ts +100 -0
  98. package/dist/.dts/build/resolve-tsconfig-paths.d.ts.map +1 -0
  99. package/dist/.dts/build/resolve-within-root.d.ts +35 -0
  100. package/dist/.dts/build/resolve-within-root.d.ts.map +1 -0
  101. package/dist/.dts/build/sarif.d.ts +66 -0
  102. package/dist/.dts/build/sarif.d.ts.map +1 -0
  103. package/dist/.dts/build/scan-dependencies.d.ts +47 -0
  104. package/dist/.dts/build/scan-dependencies.d.ts.map +1 -0
  105. package/dist/.dts/build/sort-by-identity.d.ts +14 -0
  106. package/dist/.dts/build/sort-by-identity.d.ts.map +1 -0
  107. package/dist/.dts/build/source-position.d.ts +69 -0
  108. package/dist/.dts/build/source-position.d.ts.map +1 -0
  109. package/dist/.dts/build/tool-version.d.ts +8 -0
  110. package/dist/.dts/build/tool-version.d.ts.map +1 -0
  111. package/dist/.dts/build/types.d.ts +53 -0
  112. package/dist/.dts/build/types.d.ts.map +1 -0
  113. package/dist/.dts/build/usage-report.d.ts +124 -0
  114. package/dist/.dts/build/usage-report.d.ts.map +1 -0
  115. package/dist/.dts/cli/filesystem.d.ts +3 -0
  116. package/dist/.dts/cli/filesystem.d.ts.map +1 -0
  117. package/dist/.dts/eslint-plugin/glob.d.ts +18 -0
  118. package/dist/.dts/eslint-plugin/glob.d.ts.map +1 -0
  119. package/dist/.dts/eslint-plugin/index.d.ts +27 -0
  120. package/dist/.dts/eslint-plugin/index.d.ts.map +1 -0
  121. package/dist/.dts/eslint-plugin/no-node-fs.d.ts +25 -0
  122. package/dist/.dts/eslint-plugin/no-node-fs.d.ts.map +1 -0
  123. package/dist/.dts/eslint-plugin/no-raw-process-env.d.ts +23 -0
  124. package/dist/.dts/eslint-plugin/no-raw-process-env.d.ts.map +1 -0
  125. package/dist/.dts/evidence/define-projection.d.ts +51 -0
  126. package/dist/.dts/evidence/define-projection.d.ts.map +1 -0
  127. package/dist/.dts/evidence/index.d.ts +23 -0
  128. package/dist/.dts/evidence/index.d.ts.map +1 -0
  129. package/dist/.dts/helpers/index.d.ts +135 -0
  130. package/dist/.dts/helpers/index.d.ts.map +1 -0
  131. package/dist/.dts/helpers/processors.d.ts +32 -0
  132. package/dist/.dts/helpers/processors.d.ts.map +1 -0
  133. package/dist/.dts/helpers/validators.d.ts +68 -0
  134. package/dist/.dts/helpers/validators.d.ts.map +1 -0
  135. package/dist/.dts/node/index.d.ts +2 -0
  136. package/dist/.dts/node/index.d.ts.map +1 -0
  137. package/dist/.dts/runtime/cache.d.ts +20 -0
  138. package/dist/.dts/runtime/cache.d.ts.map +1 -0
  139. package/dist/.dts/runtime/create.d.ts +20 -0
  140. package/dist/.dts/runtime/create.d.ts.map +1 -0
  141. package/dist/.dts/runtime/document.d.ts +181 -0
  142. package/dist/.dts/runtime/document.d.ts.map +1 -0
  143. package/dist/.dts/runtime/errors.d.ts +55 -0
  144. package/dist/.dts/runtime/errors.d.ts.map +1 -0
  145. package/dist/.dts/runtime/index.d.ts +24 -0
  146. package/dist/.dts/runtime/index.d.ts.map +1 -0
  147. package/dist/.dts/runtime/registry.d.ts +12 -0
  148. package/dist/.dts/runtime/registry.d.ts.map +1 -0
  149. package/dist/.dts/runtime/reset.d.ts +8 -0
  150. package/dist/.dts/runtime/reset.d.ts.map +1 -0
  151. package/dist/.dts/runtime/types.d.ts +175 -0
  152. package/dist/.dts/runtime/types.d.ts.map +1 -0
  153. package/dist/.dts/runtime/validate.d.ts +14 -0
  154. package/dist/.dts/runtime/validate.d.ts.map +1 -0
  155. package/dist/build.cjs +83 -0
  156. package/dist/build.cjs.map +1 -0
  157. package/dist/build.d.cts +1 -0
  158. package/dist/build.d.ts +1 -0
  159. package/dist/build.js +19 -0
  160. package/dist/build.js.map +1 -0
  161. package/dist/cli/index.js +134 -0
  162. package/dist/cli/index.js.map +1 -0
  163. package/dist/eslint-plugin/index.cjs +12 -0
  164. package/dist/eslint-plugin/index.cjs.map +1 -0
  165. package/dist/eslint-plugin/index.d.cts +1 -0
  166. package/dist/eslint-plugin/index.d.ts +1 -0
  167. package/dist/eslint-plugin/index.js +5 -0
  168. package/dist/eslint-plugin/index.js.map +1 -0
  169. package/dist/evidence.cjs +7 -0
  170. package/dist/evidence.cjs.map +1 -0
  171. package/dist/evidence.d.cts +1 -0
  172. package/dist/evidence.d.ts +1 -0
  173. package/dist/evidence.js +5 -0
  174. package/dist/evidence.js.map +1 -0
  175. package/dist/helpers.cjs +8 -0
  176. package/dist/helpers.cjs.map +1 -0
  177. package/dist/helpers.d.cts +1 -0
  178. package/dist/helpers.d.ts +1 -0
  179. package/dist/helpers.js +5 -0
  180. package/dist/helpers.js.map +1 -0
  181. package/dist/index.cjs +15 -0
  182. package/dist/index.cjs.map +1 -0
  183. package/dist/index.d.cts +1 -0
  184. package/dist/index.d.ts +1 -0
  185. package/dist/index.js +7 -0
  186. package/dist/index.js.map +1 -0
  187. package/dist/node.cjs +13 -0
  188. package/dist/node.cjs.map +1 -0
  189. package/dist/node.d.cts +1 -0
  190. package/dist/node.d.ts +1 -0
  191. package/dist/node.js +7 -0
  192. package/dist/node.js.map +1 -0
  193. package/package.json +183 -0
  194. package/schemas/contract-model.schema.json +321 -0
  195. package/schemas/env-cap-report.schema.json +2369 -0
package/dist/build.cjs ADDED
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+
3
+ var path6 = require('path');
4
+ var crypto = require('crypto');
5
+ var ts4 = require('typescript');
6
+ var module$1 = require('module');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var path6__default = /*#__PURE__*/_interopDefault(path6);
11
+ var crypto__default = /*#__PURE__*/_interopDefault(crypto);
12
+ var ts4__default = /*#__PURE__*/_interopDefault(ts4);
13
+
14
+ function displayPath(root,absolutePath){const relative=path6__default.default.relative(root,absolutePath);if(relative.startsWith("..")||path6__default.default.isAbsolute(relative))return absolutePath;return relative.split(path6__default.default.sep).join("/")}function positionOf(sourceFile,node){const{line,character}=sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));return {line:line+1,column:character+1}}function parsePositionCitation(citation){const match=/^(.+):(\d+):(\d+)$/.exec(citation);if(!match)return void 0;const[,file,lineText,columnText]=match;const line=Number(lineText);const column=Number(columnText);if(!file||line<1||column<1)return void 0;return {file,line,column}}function hashContent(content){return crypto__default.default.createHash("sha256").update(content).digest("hex")}function dynamicAccessVariableIdentity(file,exportName,key){return `${file}#${exportName}#${key}`}function previousContentHashes(previous){const baselines=new Map;for(const contract of previous?.dependency.contracts??[]){for(const variable of contract.variables){for(const assertion of variable.dynamicAccessAssertions){if(assertion.contentHash===void 0)continue;const identity=dynamicAccessVariableIdentity(contract.file,contract.exportName,variable.key);baselines.set(`${identity}#${assertion.file}:${assertion.line}:${assertion.column}`,assertion.contentHash);}}}return baselines}async function buildCitationSnapshots(activeContracts,previous,root,readFile){const previousBaselines=previousContentHashes(previous);const result=new Map;for(const contract of activeContracts){const file=displayPath(root,contract.file);for(const variable of contract.variables){const citations=variable.evidence?.dynamicAccess??[];if(citations.length===0)continue;const identity=dynamicAccessVariableIdentity(file,contract.exportName,variable.key);const assertions=[];for(const citation of citations){const position=parsePositionCitation(citation);if(!position)continue;let acknowledgment;let contentHash;try{const content=await readFile(path6__default.default.resolve(root,position.file));contentHash=hashContent(content);const baseline=previousBaselines.get(`${identity}#${position.file}:${position.line}:${position.column}`);acknowledgment=!baseline||baseline===contentHash?"fresh":"stale";}catch{acknowledgment="missing";contentHash=void 0;}assertions.push({...position,acknowledgment,contentHash});}result.set(identity,assertions);}}return result}function verifyDynamicAccessCitations(activeContracts,root,acknowledgments){const problems=[];for(const contract of activeContracts){const file=displayPath(root,contract.file);for(const variable of contract.variables){const identity=dynamicAccessVariableIdentity(file,contract.exportName,variable.key);for(const assertion of acknowledgments.get(identity)??[]){if(assertion.acknowledgment==="fresh")continue;problems.push({contractName:contract.contractName,file,exportName:contract.exportName,key:variable.key,position:{file:assertion.file,line:assertion.line,column:assertion.column},acknowledgment:assertion.acknowledgment});}}}return problems}function deepEqual(a,b){if(a===b)return true;if(typeof a!=="object"||typeof b!=="object"||a===null||b===null)return false;if(Array.isArray(a)||Array.isArray(b)){if(!Array.isArray(a)||!Array.isArray(b)||a.length!==b.length)return false;return a.every((v,i)=>deepEqual(v,b[i]))}const aKeys=Object.keys(a);const bKeys=Object.keys(b);if(aKeys.length!==bKeys.length)return false;return aKeys.every(key=>key in b&&deepEqual(a[key],b[key]))}var EVIDENCE_MODEL_SCHEMA_VERSION=1;function contractIdentity(contract){return `${contract.file}#${contract.exportName}`}function toContractRef(contract){return {file:contract.file,exportName:contract.exportName}}function toVariableRef(contract,variable){return {file:contract.file,exportName:contract.exportName,key:variable.key}}function toComparable(value){if(value===void 0)return void 0;if(typeof value==="string")return value;return JSON.stringify(value)}function genericFieldChanges(previous,current,skipKeys){const keys=new Set([...Object.keys(previous),...Object.keys(current)]);const changes=[];for(const key of [...keys].sort()){if(skipKeys.has(key))continue;const previousValue=previous[key];const currentValue=current[key];if(deepEqual(previousValue,currentValue))continue;if(key==="metadata"){changes.push(...recordFieldChanges(previousValue,currentValue));}else {changes.push({field:key,previous:toComparable(previousValue),current:toComparable(currentValue)});}}return changes}function recordFieldChanges(previous,current){const keys=new Set([...Object.keys(previous??{}),...Object.keys(current??{})]);const changes=[];for(const key of [...keys].sort()){const previousValue=previous?.[key];const currentValue=current?.[key];if(!deepEqual(previousValue,currentValue))changes.push({field:`metadata.${key}`,previous:toComparable(previousValue),current:toComparable(currentValue)});}return changes}function byIdentity(a,b){return a.file.localeCompare(b.file)||a.exportName.localeCompare(b.exportName)||(a.key??"").localeCompare(b.key??"")}function diffContracts(previous,current){const previousContracts=new Map((previous??[]).map(c=>[contractIdentity(c),c]));const currentContracts=new Map(current.map(c=>[contractIdentity(c),c]));const addedContracts=[];const removedContracts=[];const updatedContracts=[];const addedVariables=[];const removedVariables=[];const updatedVariables=[];for(const[identity,currentContract]of currentContracts){const previousContract=previousContracts.get(identity);if(!previousContract){addedContracts.push(toContractRef(currentContract));}else {const changes=genericFieldChanges(previousContract,currentContract,new Set(["file","exportName","variables"]));if(changes.length>0)updatedContracts.push({...toContractRef(currentContract),changes});}const previousVariables=new Map((previousContract?.variables??[]).map(v=>[v.key,v]));const currentVariables=new Map(currentContract.variables.map(v=>[v.key,v]));for(const[key,currentVariable]of currentVariables){const previousVariable=previousVariables.get(key);if(!previousVariable){addedVariables.push(toVariableRef(currentContract,currentVariable));continue}const changes=genericFieldChanges(previousVariable,currentVariable,new Set(["key"]));if(changes.length>0)updatedVariables.push({...toVariableRef(currentContract,currentVariable),changes});}if(previousContract){for(const[key,previousVariable]of previousVariables){if(!currentVariables.has(key))removedVariables.push(toVariableRef(previousContract,previousVariable));}}}for(const[identity,previousContract]of previousContracts){if(currentContracts.has(identity))continue;removedContracts.push(toContractRef(previousContract));for(const variable of previousContract.variables)removedVariables.push(toVariableRef(previousContract,variable));}return {addedContracts:addedContracts.sort(byIdentity),removedContracts:removedContracts.sort(byIdentity),addedVariables:addedVariables.sort(byIdentity),removedVariables:removedVariables.sort(byIdentity),updatedContracts:updatedContracts.sort(byIdentity),updatedVariables:updatedVariables.sort(byIdentity)}}async function readEvidenceSnapshot(snapshotPath,fs){let text;try{text=await fs.readFile(snapshotPath,"utf8");}catch{return {status:"missing"}}let parsed;try{parsed=JSON.parse(text);}catch(error){return {status:"invalid-json",detail:error instanceof Error?error.message:String(error)}}const foundVersion=parsed?.schemaVersion;if(foundVersion!==EVIDENCE_MODEL_SCHEMA_VERSION)return {status:"unsupported-version",foundVersion};return {status:"ok",snapshot:parsed}}async function writeEvidenceSnapshot(snapshotPath,snapshot,fs){await fs.mkdir(path6__default.default.dirname(snapshotPath),{recursive:true});await fs.writeFile(snapshotPath,`${JSON.stringify(snapshot,null,2)}
15
+ `,"utf8");}var EXPIRY_DAY_COUNT=/\b\d+ day\(s\)/g;function normalizeEvidenceSnapshotForComparison(snapshot){return {...snapshot,provenance:{...snapshot.provenance,generatedAt:""},change:{...snapshot.change,manifest:{addedContracts:[],removedContracts:[],addedVariables:[],removedVariables:[],updatedContracts:[],updatedVariables:[]},renamedVariables:[]},lifecycle:{...snapshot.lifecycle,expiring:snapshot.lifecycle.expiring.map(entry=>({...entry,daysRemaining:0}))},finding:{...snapshot.finding,findings:snapshot.finding.findings.map(f=>f.code==="EXPIRED"||f.code==="EXPIRING_SOON"?{...f,message:f.message.replace(EXPIRY_DAY_COUNT,"N day(s)")}:f)}}}async function computeEvidenceChanges(root,snapshotPath,activeContracts,currentContracts,readFile,fs){const read=await readEvidenceSnapshot(snapshotPath,fs);let previous;let readWarning;if(read.status==="invalid-json"){readWarning={file:snapshotPath,message:`Evidence snapshot could not be parsed as JSON (${read.detail}); treating this run as if no previous snapshot existed.`};}else if(read.status==="unsupported-version"){readWarning={file:snapshotPath,message:`Evidence snapshot has schemaVersion ${JSON.stringify(read.foundVersion)}, which this version of env-cap does not understand (expected ${EVIDENCE_MODEL_SCHEMA_VERSION}); treating this run as if no previous snapshot existed.`};}if(read.status==="ok"){previous=read.snapshot;}const dynamicAccessAcknowledgments=await buildCitationSnapshots(activeContracts,previous,root,readFile);return {report:diffContracts(previous?.contract.contracts,currentContracts),readWarning,dynamicAccessAcknowledgments,dynamicAccessCitationProblems:verifyDynamicAccessCitations(activeContracts,root,dynamicAccessAcknowledgments)}}function globToRegExp(pattern){let out="";for(let i=0;i<pattern.length;i++){const char=pattern[i];if(char==="*"&&pattern[i+1]==="*"){const precededBySlashOrStart=i===0||pattern[i-1]==="/";const followedBySlash=pattern[i+2]==="/";if(precededBySlashOrStart&&followedBySlash){out+="(?:.*/)?";i+=2;}else {out+=".*";i+=1;}}else if(char==="*"){out+="[^/]*";}else if(char==="?"){out+="[^/]";}else if(".+^${}()|[]\\".includes(char)){out+=`\\${char}`;}else {out+=char;}}return new RegExp(`^${out}$`)}function isAlwaysSkippedDirName(name){return name==="node_modules"||name===".git"}async function discoverSchemaFiles(options){const files=await walkDirectory(options.root,options.root,options.exclude,options.fs);const included=files.filter(file=>{const relativePath=toPosixRelative(options.root,file);return matchesAnyPattern(relativePath,options.include)&&!matchesAnyPattern(relativePath,options.exclude)});return included.map(file=>path6__default.default.normalize(file)).sort((a,b)=>a<b?-1:1)}async function walkDirectory(directory,root,exclude,fs){const entries=await fs.readdir(directory,{withFileTypes:true});const results=[];for(const entry of entries){const fullPath=path6__default.default.join(directory,entry.name);if(entry.isDirectory()){if(isAlwaysSkippedDirName(entry.name))continue;const relativeDir=`${toPosixRelative(root,fullPath)}/`;if(matchesAnyPattern(relativeDir,exclude))continue;results.push(...await walkDirectory(fullPath,root,exclude,fs));continue}if(entry.isFile()){results.push(fullPath);}}return results}function toPosixRelative(root,target){return path6__default.default.relative(root,target).split(path6__default.default.sep).join("/")}function matchesAnyPattern(filePath,patterns){return patterns.some(pattern=>globToRegExp(pattern).test(filePath))}function evaluateLiteral(node){if(ts4__default.default.isStringLiteralLike(node))return {ok:true,value:node.text};if(ts4__default.default.isNumericLiteral(node))return {ok:true,value:Number(node.text)};if(node.kind===ts4__default.default.SyntaxKind.TrueKeyword)return {ok:true,value:true};if(node.kind===ts4__default.default.SyntaxKind.FalseKeyword)return {ok:true,value:false};if(node.kind===ts4__default.default.SyntaxKind.NullKeyword)return {ok:true,value:null};if(ts4__default.default.isParenthesizedExpression(node))return evaluateLiteral(node.expression);if(ts4__default.default.isPrefixUnaryExpression(node)&&node.operator===ts4__default.default.SyntaxKind.MinusToken){const operand=evaluateLiteral(node.operand);return operand.ok&&typeof operand.value==="number"?{ok:true,value:-operand.value}:{ok:false}}if(ts4__default.default.isArrayLiteralExpression(node)){const values=[];for(const element of node.elements){const result=evaluateLiteral(element);if(!result.ok)return {ok:false};values.push(result.value);}return {ok:true,value:values}}if(ts4__default.default.isObjectLiteralExpression(node)){const record={};for(const prop of node.properties){if(!ts4__default.default.isPropertyAssignment(prop))return {ok:false};const key=getStaticPropertyName(prop.name);if(key===void 0)return {ok:false};const result=evaluateLiteral(prop.initializer);if(!result.ok)return {ok:false};record[key]=result.value;}return {ok:true,value:record}}return {ok:false}}function getStaticPropertyName(name){if(ts4__default.default.isIdentifier(name))return name.text;if(ts4__default.default.isStringLiteralLike(name))return name.text;if(ts4__default.default.isNumericLiteral(name))return name.text;return void 0}function parseSchemaFile(filePath,sourceText){const sourceFile=ts4__default.default.createSourceFile(filePath,sourceText,ts4__default.default.ScriptTarget.Latest,true,ts4__default.default.ScriptKind.TS);const warnings=[];const localConsts=new Map;const exportedConstNames=new Set;const imports=new Map;const createEnvCalls=[];const documentEnvCalls=[];for(const statement of sourceFile.statements){if(ts4__default.default.isImportDeclaration(statement)){collectImportBindings(statement,imports);continue}if(!ts4__default.default.isVariableStatement(statement))continue;const isExported=statement.modifiers?.some(m=>m.kind===ts4__default.default.SyntaxKind.ExportKeyword)??false;for(const decl of statement.declarationList.declarations){if(!ts4__default.default.isIdentifier(decl.name)||!decl.initializer)continue;const bindingName=decl.name.text;if(ts4__default.default.isObjectLiteralExpression(decl.initializer)){localConsts.set(bindingName,decl.initializer);if(isExported)exportedConstNames.add(bindingName);continue}if(ts4__default.default.isCallExpression(decl.initializer)){if(isCallToName(decl.initializer,"createEnv")){if(!isExported){warnings.push({file:filePath,message:`createEnv() call for "${bindingName}" is not exported; it can never be included in a generated manifest, so it's skipped entirely.`});continue}const[schemaArg,optionsArg]=decl.initializer.arguments;createEnvCalls.push({exportName:bindingName,schemaRef:resolveSchemaRef(schemaArg),optionsArg,node:decl.initializer});}else if(isCallToName(decl.initializer,"documentEnv")){const[schemaArg,docsArg]=decl.initializer.arguments;documentEnvCalls.push({schemaRef:resolveSchemaRef(schemaArg),docsArg,node:decl.initializer});}}}}for(const statement of sourceFile.statements){if(!ts4__default.default.isExpressionStatement(statement))continue;const expr=statement.expression;if(!ts4__default.default.isCallExpression(expr)||!isCallToName(expr,"documentEnv"))continue;const[schemaArg,docsArg]=expr.arguments;documentEnvCalls.push({schemaRef:resolveSchemaRef(schemaArg),docsArg,node:expr});}return {file:filePath,sourceFile,localConsts,exportedConstNames,imports,createEnvCalls,documentEnvCalls,warnings}}function resolveSchemaRef(node){if(!node)return {kind:"unresolvable"};if(ts4__default.default.isObjectLiteralExpression(node))return {kind:"literal",node};if(ts4__default.default.isIdentifier(node))return {kind:"identifier",name:node.text};return {kind:"unresolvable"}}function collectImportBindings(statement,imports){if(!ts4__default.default.isStringLiteralLike(statement.moduleSpecifier))return;const specifier=statement.moduleSpecifier.text;const namedBindings=statement.importClause?.namedBindings;if(!namedBindings||!ts4__default.default.isNamedImports(namedBindings))return;for(const element of namedBindings.elements){const localName=element.name.text;const importedName=element.propertyName?.text??localName;imports.set(localName,{specifier,importedName});}}function isCallToName(call,name){const expr=call.expression;if(ts4__default.default.isIdentifier(expr))return expr.text===name;if(ts4__default.default.isPropertyAccessExpression(expr))return expr.name.text===name;return false}function isValidEnvKey(key){return /^[A-Za-z_][A-Za-z0-9_]*$/.test(key)}function extractSchemaVariables(schemaLiteral,filePath,contextLabel,warnings,sourceFile){const variables=[];for(const prop of schemaLiteral.properties){if(!ts4__default.default.isPropertyAssignment(prop)){warnings.push({file:filePath,message:`Skipped a non-static schema entry in "${contextLabel}" (spread or computed key are not statically analyzable).`});continue}const key=getStaticPropertyName(prop.name);if(key===void 0)continue;if(!isValidEnvKey(key)){warnings.push({file:filePath,message:`Skipped "${key}" in "${contextLabel}": not a valid environment variable name (expected ${/^[A-Za-z_][A-Za-z0-9_]*$/.toString()}); it will never appear in generated docs or .env.example output.`});continue}if(!ts4__default.default.isObjectLiteralExpression(prop.initializer)){warnings.push({file:filePath,message:`Definition for "${key}" in "${contextLabel}" is not an inline object literal; skipping static analysis for this variable.`});continue}const declaration={file:filePath,...positionOf(sourceFile,prop)};variables.push(extractSchemaVariable(key,prop.initializer,filePath,contextLabel,warnings,declaration));}return variables}function extractSchemaVariable(key,definition,filePath,contextLabel,warnings,declaration){let hasDefault=false;let defaultValue;let hasProcessor=false;let processorSource;let processorReturnType;let hasValidator=false;let validatorSource;let context;for(const prop of definition.properties){if(!ts4__default.default.isPropertyAssignment(prop))continue;const name=getStaticPropertyName(prop.name);if(name==="default"){hasDefault=true;defaultValue=evaluateLiteral(prop.initializer);}else if(name==="processor"){hasProcessor=true;processorSource=normalizeSource(prop.initializer.getText());processorReturnType=extractReturnType(prop.initializer);}else if(name==="validator"){hasValidator=true;validatorSource=normalizeSource(prop.initializer.getText());}else if(name==="context"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&typeof evaluated.value==="string"&&evaluated.value.length>0){context=evaluated.value;}else {warnings.push({file:filePath,message:evaluated.ok&&evaluated.value===""?`"context" for "${key}" in "${contextLabel}" is an empty string; ignoring it -- validation contexts must be non-empty.`:`"context" for "${key}" in "${contextLabel}" is not a statically-resolvable string literal; ignoring it.`});}}}return {key,hasDefault,defaultValue,hasProcessor,processorSource,processorReturnType,hasValidator,validatorSource,context,declaration}}function extractReturnType(node){if((ts4__default.default.isArrowFunction(node)||ts4__default.default.isFunctionExpression(node))&&node.type){return node.type.getText().replace(/\s+/g,"")}return void 0}function normalizeSource(text){return text.replace(/\s+/g," ").trim()}function extractContractDocs(docsArg,filePath,contextLabel,warnings){let name;let category;let exclusiveGroup;let active=true;let owner;let sensitivity;let expiresAt;let deprecated;let deprecatedReason;let purpose;let legalBasis;let retention;let dataResidency;let auditRequired;let metadata;const variables=new Map;if(!docsArg||!ts4__default.default.isObjectLiteralExpression(docsArg)){if(docsArg){warnings.push({file:filePath,message:`documentEnv() call for "${contextLabel}" does not pass an inline object literal as its second argument; skipping it entirely.`});}return {name,category,exclusiveGroup,active,owner,sensitivity,expiresAt,deprecated,deprecatedReason,purpose,legalBasis,retention,dataResidency,auditRequired,metadata,variables}}for(const prop of docsArg.properties){if(!ts4__default.default.isPropertyAssignment(prop))continue;const propName=getStaticPropertyName(prop.name);if(propName==="name"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&typeof evaluated.value==="string")name=evaluated.value;}else if(propName==="category"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&typeof evaluated.value==="string"){category=evaluated.value;}else {warnings.push({file:filePath,message:`"category" for "${contextLabel}" is not a statically-resolvable string literal; ignoring it.`});}}else if(propName==="exclusiveGroup"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&typeof evaluated.value==="string"){exclusiveGroup=evaluated.value;}else {warnings.push({file:filePath,message:`"exclusiveGroup" for "${contextLabel}" is not a statically-resolvable string literal; ignoring it.`});}}else if(propName==="active"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&typeof evaluated.value==="boolean"){active=evaluated.value;}else {warnings.push({file:filePath,message:`"active" for "${contextLabel}" is not a statically-resolvable boolean literal; defaulting to active: true.`});}}else if(propName==="owner"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&typeof evaluated.value==="string")owner=evaluated.value;}else if(propName==="sensitivity"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&typeof evaluated.value==="string"){sensitivity=evaluated.value;}else {warnings.push({file:filePath,message:`"sensitivity" for "${contextLabel}" is not a statically-resolvable string literal; ignoring it.`});}}else if(propName==="expiresAt"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&typeof evaluated.value==="string"){expiresAt=evaluated.value;}else {warnings.push({file:filePath,message:`"expiresAt" for "${contextLabel}" is not a statically-resolvable string literal; ignoring it.`});}}else if(propName==="deprecated"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&typeof evaluated.value==="boolean"){deprecated=evaluated.value;}else {warnings.push({file:filePath,message:`"deprecated" for "${contextLabel}" is not a statically-resolvable boolean literal; ignoring it.`});}}else if(propName==="deprecatedReason"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&typeof evaluated.value==="string")deprecatedReason=evaluated.value;}else if(propName==="purpose"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&typeof evaluated.value==="string")purpose=evaluated.value;}else if(propName==="legalBasis"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&typeof evaluated.value==="string")legalBasis=evaluated.value;}else if(propName==="retention"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&typeof evaluated.value==="string")retention=evaluated.value;}else if(propName==="dataResidency"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&isStringOrStringArray(evaluated.value)){dataResidency=evaluated.value;}else {warnings.push({file:filePath,message:`"dataResidency" for "${contextLabel}" is not a statically-resolvable string or string array; ignoring it.`});}}else if(propName==="auditRequired"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&typeof evaluated.value==="boolean")auditRequired=evaluated.value;}else if(propName==="metadata"){const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&isRecord(evaluated.value)){metadata=evaluated.value;}else {warnings.push({file:filePath,message:`"metadata" for "${contextLabel}" is not a statically-resolvable object literal; ignoring it.`});}}else if(propName==="variables"){if(ts4__default.default.isObjectLiteralExpression(prop.initializer)){extractVariableDocsMap(prop.initializer,filePath,contextLabel,warnings,variables);}else {warnings.push({file:filePath,message:`"variables" for "${contextLabel}" is not an inline object literal; per-variable documentation is skipped entirely.`});}}}return {name,category,exclusiveGroup,active,owner,sensitivity,expiresAt,deprecated,deprecatedReason,purpose,legalBasis,retention,dataResidency,auditRequired,metadata,variables}}function extractVariableDocsMap(variablesLiteral,filePath,contextLabel,warnings,out){for(const prop of variablesLiteral.properties){if(!ts4__default.default.isPropertyAssignment(prop))continue;const key=getStaticPropertyName(prop.name);if(key===void 0)continue;if(!ts4__default.default.isObjectLiteralExpression(prop.initializer)){warnings.push({file:filePath,message:`documentEnv() variable docs for "${key}" in "${contextLabel}" is not an inline object literal; skipped.`});continue}let description;let owner;let sensitivity;let expiresAt;let refreshInstructions;let setupInstructions;let required;let deprecated;let deprecatedReason;let removeBy;let renamedFrom;let purpose;let legalBasis;let retention;let dataResidency;let auditRequired;let metadata;let evidence;for(const field of prop.initializer.properties){if(!ts4__default.default.isPropertyAssignment(field))continue;const fieldName=getStaticPropertyName(field.name);if(fieldName===void 0)continue;const evaluated=evaluateLiteral(field.initializer);if(!evaluated.ok)continue;if(fieldName==="description"&&typeof evaluated.value==="string")description=evaluated.value;else if(fieldName==="owner"&&typeof evaluated.value==="string")owner=evaluated.value;else if(fieldName==="sensitivity"&&typeof evaluated.value==="string")sensitivity=evaluated.value;else if(fieldName==="expiresAt"&&typeof evaluated.value==="string")expiresAt=evaluated.value;else if(fieldName==="refreshInstructions"&&typeof evaluated.value==="string")refreshInstructions=evaluated.value;else if(fieldName==="setupInstructions"&&typeof evaluated.value==="string")setupInstructions=evaluated.value;else if(fieldName==="required"&&typeof evaluated.value==="boolean")required=evaluated.value;else if(fieldName==="deprecated"&&typeof evaluated.value==="boolean")deprecated=evaluated.value;else if(fieldName==="deprecatedReason"&&typeof evaluated.value==="string")deprecatedReason=evaluated.value;else if(fieldName==="removeBy"&&typeof evaluated.value==="string")removeBy=evaluated.value;else if(fieldName==="renamedFrom"&&typeof evaluated.value==="string")renamedFrom=evaluated.value;else if(fieldName==="purpose"&&typeof evaluated.value==="string")purpose=evaluated.value;else if(fieldName==="legalBasis"&&typeof evaluated.value==="string")legalBasis=evaluated.value;else if(fieldName==="retention"&&typeof evaluated.value==="string")retention=evaluated.value;else if(fieldName==="dataResidency"&&isStringOrStringArray(evaluated.value))dataResidency=evaluated.value;else if(fieldName==="auditRequired"&&typeof evaluated.value==="boolean")auditRequired=evaluated.value;else if(fieldName==="metadata"&&isRecord(evaluated.value))metadata=evaluated.value;else if(fieldName==="evidence"&&isRecord(evaluated.value)){evidence={dynamicAccess:extractDynamicAccessCitations(evaluated.value.dynamicAccess,filePath,key,contextLabel,warnings)};}}out.set(key,{key,description,owner,sensitivity,expiresAt,refreshInstructions,setupInstructions,required,deprecated,deprecatedReason,removeBy,renamedFrom,purpose,legalBasis,retention,dataResidency,auditRequired,metadata,evidence});}}function extractDynamicAccessCitations(value,filePath,key,contextLabel,warnings){if(!Array.isArray(value))return void 0;const citations=[];for(const entry of value){if(typeof entry==="string"&&parsePositionCitation(entry)){citations.push(entry);}else {warnings.push({file:filePath,message:`documentEnv() "evidence.dynamicAccess" entry ${JSON.stringify(entry)} for "${key}" in "${contextLabel}" is not a well-formed "path:line:column" citation; ignoring it.`});}}return citations}function extractCreateEnvOptionsName(optionsArg){if(!optionsArg||!ts4__default.default.isObjectLiteralExpression(optionsArg))return void 0;for(const prop of optionsArg.properties){if(!ts4__default.default.isPropertyAssignment(prop))continue;if(getStaticPropertyName(prop.name)!=="name")continue;const evaluated=evaluateLiteral(prop.initializer);if(evaluated.ok&&typeof evaluated.value==="string")return evaluated.value}return void 0}function isRecord(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function isStringOrStringArray(value){if(typeof value==="string")return true;return Array.isArray(value)&&value.every(v=>typeof v==="string")}function mustGet(map,key){const value=map.get(key);if(value===void 0)throw new Error(`Internal error: expected key "${String(key)}" to be present in map.`);return value}function isWithinDirectory(baseDir,targetPath){const relative=path6__default.default.relative(baseDir,targetPath);return relative!==".."&&!relative.startsWith(`..${path6__default.default.sep}`)&&!path6__default.default.isAbsolute(relative)}function resolveWithinRoot(root,location,optionName,functionName){const resolved=path6__default.default.resolve(root,location);if(!isWithinDirectory(root,resolved)){return {ok:false,issue:{severity:"error",variable:optionName,files:[location],reason:`"${optionName}" ("${location}") resolves to "${resolved}", which is outside "root" ("${root}"). ${functionName}() refuses to write outside root -- use a path nested under root instead.`}}}return {ok:true,resolved}}var SCHEMA_FILE_EXTENSIONS=[".ts",".tsx"];var MAX_PACKAGE_SCHEMA_FILE_BYTES=1048576;var PACKAGE_JSON_ANCESTOR_SEARCH_LIMIT=8;function isRecord2(value){return typeof value==="object"&&value!==null}async function locatePackageManifest(packageName,root,fs){const req=module$1.createRequire(path6__default.default.join(root,"package.json"));try{const packageJsonPath=req.resolve(`${packageName}/package.json`);return {packageJsonPath,packageDir:path6__default.default.dirname(packageJsonPath)}}catch{}let mainFile;try{mainFile=req.resolve(packageName);}catch{return void 0}let dir=path6__default.default.dirname(mainFile);for(let i=0;i<PACKAGE_JSON_ANCESTOR_SEARCH_LIMIT;i++){const candidate=path6__default.default.join(dir,"package.json");try{const parsed=JSON.parse(await fs.readFile(candidate,"utf8"));if(isRecord2(parsed)&&parsed.name===packageName){return {packageJsonPath:candidate,packageDir:dir}}}catch{}const parent=path6__default.default.dirname(dir);if(parent===dir)break;dir=parent;}return void 0}async function resolveUncached(packageName,root,fs){const located=await locatePackageManifest(packageName,root,fs);if(!located){return {ok:false,code:"PACKAGE_NOT_FOUND",reason:`Package "${packageName}" listed in "packages" could not be resolved from "${root}" -- is it installed?`}}const{packageJsonPath,packageDir}=located;let manifest;try{manifest=JSON.parse(await fs.readFile(packageJsonPath,"utf8"));}catch{return {ok:false,code:"MALFORMED_PACKAGE_JSON",reason:`"${packageJsonPath}" is not valid JSON.`}}if(!isRecord2(manifest)){return {ok:false,code:"MALFORMED_PACKAGE_JSON",reason:`"${packageJsonPath}" does not contain a JSON object.`}}const envCapField=manifest.envCap;const declaredField=isRecord2(envCapField)&&typeof envCapField.schema==="string"?envCapField.schema:void 0;if(declaredField===void 0){return {ok:false,code:"FIELD_MISSING",reason:`"${packageName}"'s package.json has no "envCap.schema" field (or it is not a string).`}}const lexicallyResolved=path6__default.default.resolve(packageDir,declaredField);if(!isWithinDirectory(packageDir,lexicallyResolved)){return {ok:false,code:"OUTSIDE_PACKAGE",reason:`"${packageName}"'s "envCap.schema" ("${declaredField}") resolves outside its own package directory.`}}if(!SCHEMA_FILE_EXTENSIONS.some(ext=>lexicallyResolved.endsWith(ext))){return {ok:false,code:"INVALID_EXTENSION",reason:`"${packageName}"'s "envCap.schema" ("${declaredField}") must be a ${SCHEMA_FILE_EXTENSIONS.join("/")} file, not compiled/bundled output.`}}let realFile;let realPackageDir;try{;[realFile,realPackageDir]=await Promise.all([fs.realpath(lexicallyResolved),fs.realpath(packageDir)]);}catch{return {ok:false,code:"OUTSIDE_PACKAGE",reason:`"${packageName}"'s "envCap.schema" ("${declaredField}") does not resolve to a file that exists.`}}if(!isWithinDirectory(realPackageDir,realFile)){return {ok:false,code:"OUTSIDE_PACKAGE",reason:`"${packageName}"'s "envCap.schema" ("${declaredField}") resolves, after following symlinks, outside its own package directory.`}}let stats;try{stats=await fs.stat(realFile);}catch{return {ok:false,code:"OUTSIDE_PACKAGE",reason:`"${packageName}"'s "envCap.schema" ("${declaredField}") could not be read.`}}if(!stats.isFile()){return {ok:false,code:"OUTSIDE_PACKAGE",reason:`"${packageName}"'s "envCap.schema" ("${declaredField}") does not resolve to a regular file.`}}if(stats.size>MAX_PACKAGE_SCHEMA_FILE_BYTES){return {ok:false,code:"FILE_TOO_LARGE",reason:`"${packageName}"'s "envCap.schema" ("${declaredField}") is ${stats.size} bytes, exceeding the ${MAX_PACKAGE_SCHEMA_FILE_BYTES}-byte limit for a package-resolved schema file.`}}return {ok:true,origin:{packageName,declaredField,resolvedFile:realFile,packageDir:realPackageDir}}}function resolvePackageSchemaFile(packageName,root,cache,fs){let cached=cache.get(packageName);if(!cached){cached=resolveUncached(packageName,root,fs);cache.set(packageName,cached);}return cached}async function resolveAllowlistedPackages(packages,root,cache,fs){const uniqueNames=[...new Set(packages)];const resolved=await Promise.all(uniqueNames.map(async packageName=>({packageName,result:await resolvePackageSchemaFile(packageName,root,cache,fs)})));const files=[];const origins=new Map;const warnings=[];for(const{packageName,result}of resolved){if(result.ok){files.push({packageName,file:result.origin.resolvedFile});origins.set(result.origin.resolvedFile,result.origin);}else {warnings.push({file:`(package) ${packageName}`,message:result.reason});}}return {files,origins,warnings}}async function mergeLocalAndPackageFiles(localFiles,packageFiles,fs){const seenRealpaths=new Set;const merged=[];for(const file of localFiles){let real;try{real=await fs.realpath(file);}catch{real=file;}if(seenRealpaths.has(real))continue;seenRealpaths.add(real);merged.push(file);}for(const file of packageFiles){if(seenRealpaths.has(file))continue;seenRealpaths.add(file);merged.push(file);}return merged}async function resolvePackageImport(specifier,allowedPackages,root,cache,fs){const matched=allowedPackages.find(pkg=>specifier===pkg||specifier.startsWith(`${pkg}/`));if(!matched)return void 0;const result=await resolvePackageSchemaFile(matched,root,cache,fs);return result.ok?result.origin.resolvedFile:void 0}function createAliasResolutionCache(){return {resolutions:new Map}}async function fileExists(filePath,fs){try{return (await fs.stat(filePath)).isFile()}catch{}return false}async function loadTsconfigPaths(root,tsconfigOption,fs){if(tsconfigOption===false)return {resolution:void 0,warning:void 0};const isExplicit=tsconfigOption!==void 0;const configFile=path6__default.default.resolve(root,tsconfigOption??"tsconfig.json");if(!await fileExists(configFile,fs)){if(!isExplicit)return {resolution:void 0,warning:void 0};return {resolution:void 0,warning:{file:configFile,message:`"tsconfig" was set to "${tsconfigOption}", but no file exists at "${configFile}".`}}}const readResult=ts4__default.default.readConfigFile(configFile,p=>ts4__default.default.sys.readFile(p));if(readResult.error){const detail=ts4__default.default.flattenDiagnosticMessageText(readResult.error.messageText,"\n");return {resolution:void 0,warning:{file:configFile,message:`Could not parse "${configFile}": ${detail}`}}}const parsed=ts4__default.default.parseJsonConfigFileContent(readResult.config,ts4__default.default.sys,path6__default.default.dirname(configFile));const{paths,baseUrl}=parsed.options;const hasPaths=paths!==void 0&&Object.keys(paths).length>0;if(!hasPaths&&baseUrl===void 0){return {resolution:void 0,warning:void 0}}return {resolution:{compilerOptions:parsed.options,configFile},warning:void 0}}function resolveAliasImport(specifier,importingFile,resolution,cache){const cacheKey=`${importingFile}\0${specifier}`;if(cache.resolutions.has(cacheKey))return cache.resolutions.get(cacheKey);const result=ts4__default.default.resolveModuleName(specifier,importingFile,resolution.compilerOptions,ts4__default.default.sys);const resolvedFileName=result.resolvedModule?.resolvedFileName;let resolved;if(resolvedFileName){const normalized=path6__default.default.normalize(resolvedFileName);const isInNodeModules=normalized.split(path6__default.default.sep).includes("node_modules");const isTsSource=/\.tsx?$/.test(normalized);if(!isInNodeModules&&isTsSource)resolved=normalized;}cache.resolutions.set(cacheKey,resolved);return resolved}async function resolveRelativeImport(importingFile,specifier,fs){if(!specifier.startsWith("./")&&!specifier.startsWith("../"))return void 0;const withoutExt=specifier.replace(/\.(js|jsx|ts|tsx)$/,"");const base=path6__default.default.resolve(path6__default.default.dirname(importingFile),withoutExt);for(const candidate of [`${base}.ts`,`${base}.tsx`,path6__default.default.join(base,"index.ts"),path6__default.default.join(base,"index.tsx")]){if(await fileExists2(candidate,fs))return candidate}return void 0}async function fileExists2(filePath,fs){try{const stat=await fs.stat(filePath);return stat.isFile()}catch{return false}}async function resolveImportSpecifier(importingFile,specifier,context){const relative=await resolveRelativeImport(importingFile,specifier,context.fs);if(relative)return relative;if(context.tsconfigPaths){const aliased=resolveAliasImport(specifier,importingFile,context.tsconfigPaths,context.aliasCache);if(aliased)return aliased}return resolvePackageImport(specifier,context.packages,context.root,context.cache,context.fs)}async function linkFiles(discoveredFiles,readFile,context,packageOrigins=new Map){const analysisCache=new Map;async function getAnalysis(filePath){const cached=analysisCache.get(filePath);if(cached)return cached;let text;try{text=await readFile(filePath);}catch{return void 0}const parsed=parseSchemaFile(filePath,text);analysisCache.set(filePath,parsed);return parsed}for(const file of discoveredFiles){await getAnalysis(file);}const warnings=[];const unresolvedLinks=[];async function resolveSchema(ref,inFile,contextLabel){if(ref.kind==="unresolvable")return void 0;const inFileAnalysis=mustGet(analysisCache,inFile);if(ref.kind==="literal"){return {identity:`${inFile}#<inline:${ref.node.pos}>`,variables:extractSchemaVariables(ref.node,inFile,contextLabel,warnings,inFileAnalysis.sourceFile)}}const local=inFileAnalysis.localConsts.get(ref.name);if(local){return {identity:`${inFile}#${ref.name}`,variables:extractSchemaVariables(local,inFile,contextLabel,warnings,inFileAnalysis.sourceFile)}}const imported=inFileAnalysis.imports.get(ref.name);if(!imported)return void 0;const targetFile=await resolveImportSpecifier(inFile,imported.specifier,context);if(!targetFile)return void 0;const targetAnalysis=await getAnalysis(targetFile);if(!targetAnalysis)return void 0;const targetLocal=targetAnalysis.localConsts.get(imported.importedName);if(!targetLocal||!targetAnalysis.exportedConstNames.has(imported.importedName))return void 0;return {identity:`${targetFile}#${imported.importedName}`,variables:extractSchemaVariables(targetLocal,targetFile,contextLabel,warnings,targetAnalysis.sourceFile)}}const createEnvEntries=[];for(const file of discoveredFiles){const analysis=mustGet(analysisCache,file);for(const call of analysis.createEnvCalls){const resolved=await resolveSchema(call.schemaRef,file,call.exportName);if(!resolved){warnings.push({file,message:`createEnv() call for "${call.exportName}" does not pass an inline object literal or a statically-resolvable schema reference; skipping static analysis for this contract.`});continue}createEnvEntries.push({file,exportName:call.exportName,identity:resolved.identity,variables:resolved.variables,optionsName:extractCreateEnvOptionsName(call.optionsArg),declaration:{file,...positionOf(analysis.sourceFile,call.node)}});}}const documentEnvByIdentity=new Map;for(const file of discoveredFiles){const analysis=mustGet(analysisCache,file);for(const call of analysis.documentEnvCalls){const resolved=await resolveSchema(call.schemaRef,file,"documentEnv() call");if(!resolved){unresolvedLinks.push({file,reason:"documentEnv() call could not be statically linked to a schema (its first argument isn't an inline object literal or a resolvable local/imported reference)."});continue}if(documentEnvByIdentity.has(resolved.identity)){continue}const docs=extractContractDocs(call.docsArg,file,resolved.identity,warnings);const documentation={file,...positionOf(analysis.sourceFile,call.node)};documentEnvByIdentity.set(resolved.identity,{identity:resolved.identity,docs,documentation});}}const contracts=[];const undocumentedContracts=[];const undocumentedVariables=[];const staleDocEntries=[];for(const entry of createEnvEntries){const linked=documentEnvByIdentity.get(entry.identity);const docs=linked?.docs;if(!docs){undocumentedContracts.push({file:entry.file,exportName:entry.exportName});}const schemaKeys=new Set(entry.variables.map(v=>v.key));const docsVariables=docs?.variables??new Map;for(const key of schemaKeys){if(!docsVariables.has(key))undocumentedVariables.push({file:entry.file,exportName:entry.exportName,key});}for(const key of docsVariables.keys()){if(!schemaKeys.has(key))staleDocEntries.push({file:entry.file,exportName:entry.exportName,key});}const variables=entry.variables.map(v=>{const vd=docsVariables.get(v.key);return {...v,description:vd?.description,owner:vd?.owner,sensitivity:vd?.sensitivity,expiresAt:vd?.expiresAt,refreshInstructions:vd?.refreshInstructions,setupInstructions:vd?.setupInstructions,required:vd?.required,deprecated:vd?.deprecated,deprecatedReason:vd?.deprecatedReason,removeBy:vd?.removeBy,renamedFrom:vd?.renamedFrom,purpose:vd?.purpose,legalBasis:vd?.legalBasis,retention:vd?.retention,dataResidency:vd?.dataResidency,auditRequired:vd?.auditRequired,metadata:vd?.metadata,evidence:vd?.evidence,documented:vd!==void 0}});contracts.push({file:entry.file,exportName:entry.exportName,contractName:docs?.name??entry.optionsName??entry.exportName,active:docs?.active??true,category:docs?.category,exclusiveGroup:docs?.exclusiveGroup,owner:docs?.owner,sensitivity:docs?.sensitivity,expiresAt:docs?.expiresAt,deprecated:docs?.deprecated,deprecatedReason:docs?.deprecatedReason,purpose:docs?.purpose,legalBasis:docs?.legalBasis,retention:docs?.retention,dataResidency:docs?.dataResidency,auditRequired:docs?.auditRequired,metadata:docs?.metadata,packageOrigin:packageOrigins.get(entry.file),variables,documented:docs!==void 0,declaration:entry.declaration,documentation:linked?.documentation});}for(const analysis of analysisCache.values()){warnings.push(...analysis.warnings);}return {contracts,warnings,unresolvedLinks,undocumentedContracts,undocumentedVariables,staleDocEntries}}function summarizeContract(contract,root){return {file:path6__default.default.relative(root,contract.file),exportName:contract.exportName,contractName:contract.contractName,variableCount:contract.variables.length,active:contract.active,documented:contract.documented}}function effectiveOwner(contract,variable){return variable.owner??contract.owner}function effectiveSensitivity(contract,variable){return variable.sensitivity??contract.sensitivity}function effectivePurpose(contract,variable){return variable.purpose??contract.purpose}function effectiveLegalBasis(contract,variable){return variable.legalBasis??contract.legalBasis}function effectiveRetention(contract,variable){return variable.retention??contract.retention}function effectiveDataResidency(contract,variable){return variable.dataResidency??contract.dataResidency}function effectiveAuditRequired(contract,variable){return variable.auditRequired??contract.auditRequired}async function assembleProject(options){const{fs,root,include,exclude,packages,tsconfig}=options;const fileCache=new Map;const readFileCached=filePath=>{let cached=fileCache.get(filePath);if(!cached){cached=fs.readFile(filePath,"utf8");fileCache.set(filePath,cached);}return cached};const localSchemaFiles=await discoverSchemaFiles({fs,root,include,exclude});const packageCache=new Map;const{files:packageFiles,origins,warnings:packageWarnings}=await resolveAllowlistedPackages(packages,root,packageCache,fs);const schemaFiles=await mergeLocalAndPackageFiles(localSchemaFiles,packageFiles.map(f=>f.file),fs);const{resolution:tsconfigPaths,warning:tsconfigWarning}=await loadTsconfigPaths(root,tsconfig,fs);const tsconfigWarnings=tsconfigWarning?[tsconfigWarning]:[];const context={fs,root,packages,cache:packageCache,tsconfigPaths,aliasCache:createAliasResolutionCache()};const linkResult=await linkFiles(schemaFiles,readFileCached,context,origins);return {root,readFileCached,linkResult,context,origins,packageWarnings,tsconfigWarnings}}var CHANGE_MODEL_SCHEMA_VERSION=1;function contractIdentity2(file,exportName){return `${file}#${exportName}`}function byRenameIdentity(a,b){return a.contractIdentity.localeCompare(b.contractIdentity)||a.currentKey.localeCompare(b.currentKey)}function findByContractAndKey(refs,file,exportName,key){return refs.find(ref=>ref.file===file&&ref.exportName===exportName&&ref.key===key)}function buildChangeModel(manifest,currentContracts,root){const renamedVariables=[];for(const contract of currentContracts){const relativeFile=displayPath(root,contract.file);const cIdentity=contractIdentity2(relativeFile,contract.exportName);for(const variable of contract.variables){if(!variable.renamedFrom)continue;const addedRef=findByContractAndKey(manifest.addedVariables,relativeFile,contract.exportName,variable.key);const removedRef=findByContractAndKey(manifest.removedVariables,relativeFile,contract.exportName,variable.renamedFrom);if(!addedRef||!removedRef)continue;renamedVariables.push({contractIdentity:cIdentity,file:addedRef.file,exportName:addedRef.exportName,contractName:contract.contractName,previousKey:removedRef.key,currentKey:addedRef.key});}}renamedVariables.sort(byRenameIdentity);return {schemaVersion:CHANGE_MODEL_SCHEMA_VERSION,manifest,renamedVariables}}var DOCUMENTATION_COMPARISON_FIELDS=["description","owner","sensitivity","expiresAt","refreshInstructions","setupInstructions","required"];function documentationDivergences(a,b){const divergences=[];for(const field of DOCUMENTATION_COMPARISON_FIELDS){if(a[field]!==b[field])divergences.push(field);}const aMetadata=a.metadata??{};const bMetadata=b.metadata??{};for(const key of new Set([...Object.keys(aMetadata),...Object.keys(bMetadata)])){if(!deepEqual(aMetadata[key],bMetadata[key]))divergences.push(`metadata.${key}`);}return divergences}function detectCompatibilityIssues(contracts){const declarationsByKey=new Map;for(const contract of contracts){for(const variable of contract.variables){const list=declarationsByKey.get(variable.key)??[];list.push({file:contract.file,contractName:contract.contractName,variable});declarationsByKey.set(variable.key,list);}}const issues=[];for(const[key,declarations]of [...declarationsByKey.entries()].sort((a,b)=>a[0].localeCompare(b[0]))){for(let i=0;i<declarations.length;i++){for(let j=i+1;j<declarations.length;j++){const a=declarations[i];const b=declarations[j];if(a.variable.processorReturnType&&b.variable.processorReturnType&&a.variable.processorReturnType!==b.variable.processorReturnType){issues.push({severity:"error",variable:key,files:[a.file,b.file],code:"PROCESSOR_RETURN_TYPE_CONFLICT",reason:`Processor return types are declared incompatible: "${a.contractName}" produces ${a.variable.processorReturnType}, "${b.contractName}" produces ${b.variable.processorReturnType}.`});continue}if(a.variable.hasProcessor&&b.variable.hasProcessor&&a.variable.processorSource!==b.variable.processorSource){issues.push({severity:"warning",variable:key,files:[a.file,b.file],code:"PROCESSOR_SOURCE_CONFLICT",reason:`"${a.contractName}" and "${b.contractName}" both declare a processor for this variable with different implementations. Return types could not be statically verified -- add explicit return type annotations, or confirm they produce equivalent output.`});}if(a.variable.hasValidator&&b.variable.hasValidator&&a.variable.validatorSource!==b.variable.validatorSource){issues.push({severity:"warning",variable:key,files:[a.file,b.file],code:"VALIDATOR_SOURCE_CONFLICT",reason:`"${a.contractName}" and "${b.contractName}" both declare a validator for this variable with different implementations. Validator logic cannot be statically compared -- confirm they enforce compatible rules.`});}if(a.variable.documented&&b.variable.documented){const divergences=documentationDivergences(a.variable,b.variable);if(divergences.length>0){issues.push({severity:"warning",variable:key,files:[a.file,b.file],code:"DUPLICATE_VARIABLE_DOCUMENTATION",reason:`"${a.contractName}" and "${b.contractName}" both document this variable, but disagree on: ${divergences.join(", ")}. Confirm they're still meant to be the same variable and align the documentation, or document them separately if they're not.`});}}}}}return issues}function variableShape(variable){let valueType="unknown";if(variable.processorReturnType!==void 0){valueType=variable.processorReturnType;}else if(variable.hasDefault&&variable.defaultValue?.ok===true){valueType=typeof variable.defaultValue.value;}return {valueType,hasProcessor:variable.hasProcessor,hasValidator:variable.hasValidator}}function shapesMatch(a,b){if(a.valueType==="unknown"||b.valueType==="unknown")return false;return a.valueType===b.valueType&&a.hasProcessor===b.hasProcessor&&a.hasValidator===b.hasValidator}function detectDuplicateVariableShapes(contracts){const candidates=contracts.filter(c=>c.active&&!c.exclusiveGroup);const declarations=[];for(const contract of candidates){for(const variable of contract.variables){declarations.push({file:contract.file,contractName:contract.contractName,key:variable.key,shape:variableShape(variable)});}}declarations.sort((a,b)=>a.key.localeCompare(b.key)||a.contractName.localeCompare(b.contractName)||a.file.localeCompare(b.file));const issues=[];for(let i=0;i<declarations.length;i++){for(let j=i+1;j<declarations.length;j++){const a=declarations[i];const b=declarations[j];if(a.key===b.key)continue;if(a.file===b.file&&a.contractName===b.contractName)continue;if(!shapesMatch(a.shape,b.shape))continue;issues.push({severity:"info",variable:`${a.key} / ${b.key}`,files:[a.file,b.file],code:"DUPLICATE_VARIABLE_SHAPE_ACROSS_CONTRACTS",reason:`"${a.key}" (${a.contractName}) and "${b.key}" (${b.contractName}) declare an identical shape (type ${a.shape.valueType}, processor: ${a.shape.hasProcessor?"yes":"no"}, validator: ${a.shape.hasValidator?"yes":"no"}). Informational only -- they may be the same underlying value modelled twice, or two unrelated values that happen to look alike. Nothing is required.`});}}return issues}function byContractIdentity(a,b){return a.file.localeCompare(b.file)||a.exportName.localeCompare(b.exportName)}function governanceFieldsOf(source){return {owner:source.owner,sensitivity:source.sensitivity,expiresAt:source.expiresAt,purpose:source.purpose,legalBasis:source.legalBasis,retention:source.retention,dataResidency:source.dataResidency,auditRequired:source.auditRequired,metadata:source.metadata}}function relativizePosition(root,position){return {...position,file:displayPath(root,position.file)}}function relativizePackageOrigin(root,origin){if(!origin)return origin;return {...origin,resolvedFile:displayPath(root,origin.resolvedFile),packageDir:displayPath(root,origin.packageDir)}}var CONTRACT_MODEL_SCHEMA_VERSION=3;function buildContractModel(contracts,root){const projected=contracts.map(contract=>({file:displayPath(root,contract.file),exportName:contract.exportName,contractName:contract.contractName,active:contract.active,category:contract.category,exclusiveGroup:contract.exclusiveGroup,...governanceFieldsOf(contract),documented:contract.documented,packageOrigin:relativizePackageOrigin(root,contract.packageOrigin),declaration:relativizePosition(root,contract.declaration),documentation:contract.documentation&&relativizePosition(root,contract.documentation),variables:[...contract.variables].sort((a,b)=>a.key.localeCompare(b.key)).map(variable=>({key:variable.key,hasDefault:variable.hasDefault,defaultValue:variable.defaultValue,hasProcessor:variable.hasProcessor,processorSource:variable.processorSource,processorReturnType:variable.processorReturnType,hasValidator:variable.hasValidator,validatorSource:variable.validatorSource,context:variable.context,description:variable.description,owner:variable.owner,sensitivity:variable.sensitivity,expiresAt:variable.expiresAt,refreshInstructions:variable.refreshInstructions,setupInstructions:variable.setupInstructions,required:variable.required,purpose:variable.purpose,legalBasis:variable.legalBasis,retention:variable.retention,dataResidency:variable.dataResidency,auditRequired:variable.auditRequired,metadata:variable.metadata,documented:variable.documented,evidence:variable.evidence,declaration:relativizePosition(root,variable.declaration)}))}));projected.sort(byContractIdentity);return {schemaVersion:CONTRACT_MODEL_SCHEMA_VERSION,contracts:projected}}function deepFreeze(value){return deepFreezeInternal(value,new WeakSet)}function deepFreezeInternal(value,seen){if(value===null||typeof value!=="object")return value;if(seen.has(value))return value;seen.add(value);if(Array.isArray(value)){for(const item of value)deepFreezeInternal(item,seen);}else if(isPlainObject(value)){for(const key of Object.keys(value)){deepFreezeInternal(value[key],seen);}}return Object.freeze(value)}function isPlainObject(value){const proto=Object.getPrototypeOf(value);return proto===Object.prototype||proto===null}function unwrapParens(node){let current=node;while(ts4__default.default.isParenthesizedExpression(current))current=current.expression;return current}function staticElementKey(node){return ts4__default.default.isStringLiteralLike(node)?node.text:void 0}function forEachBindingName(name,record){if(ts4__default.default.isIdentifier(name)){record(name.text);return}for(const element of name.elements){if(ts4__default.default.isOmittedExpression(element))continue;forEachBindingName(element.name,record);}}function isAssignmentToken(token){return token>=ts4__default.default.SyntaxKind.FirstAssignment&&token<=ts4__default.default.SyntaxKind.LastAssignment}function recordIfIdentifierTarget(expression,assignedNames){const target=unwrapParens(expression);if(ts4__default.default.isIdentifier(target))assignedNames.add(target.text);}function recordAssignmentTargets(node,assignedNames){if(ts4__default.default.isBinaryExpression(node)&&isAssignmentToken(node.operatorToken.kind)){recordIfIdentifierTarget(node.left,assignedNames);return}if((ts4__default.default.isPrefixUnaryExpression(node)||ts4__default.default.isPostfixUnaryExpression(node))&&(node.operator===ts4__default.default.SyntaxKind.PlusPlusToken||node.operator===ts4__default.default.SyntaxKind.MinusMinusToken)){recordIfIdentifierTarget(node.operand,assignedNames);return}if((ts4__default.default.isForOfStatement(node)||ts4__default.default.isForInStatement(node))&&!ts4__default.default.isVariableDeclarationList(node.initializer)){recordIfIdentifierTarget(node.initializer,assignedNames);}}function recordDeclarationIntroduction(node,countDeclaration,constAliasCandidates){if(ts4__default.default.isVariableDeclaration(node)||ts4__default.default.isParameter(node)){forEachBindingName(node.name,countDeclaration);if(!ts4__default.default.isVariableDeclaration(node)||!ts4__default.default.isIdentifier(node.name))return;if(node.initializer===void 0)return;const initializer=unwrapParens(node.initializer);const isConst=(ts4__default.default.getCombinedNodeFlags(node)&ts4__default.default.NodeFlags.Const)!==0;if(isConst&&ts4__default.default.isIdentifier(initializer)){constAliasCandidates.push({aliasName:node.name.text,baseName:initializer.text,declaration:node});}return}if((ts4__default.default.isFunctionDeclaration(node)||ts4__default.default.isClassDeclaration(node))&&node.name){countDeclaration(node.name.text);}}function collectDeclarationFacts(sourceFile){const declarationCounts=new Map;const assignedNames=new Set;const constAliasCandidates=[];function countDeclaration(name){declarationCounts.set(name,(declarationCounts.get(name)??0)+1);}function walk(node){recordDeclarationIntroduction(node,countDeclaration,constAliasCandidates);recordAssignmentTargets(node,assignedNames);ts4__default.default.forEachChild(node,walk);}ts4__default.default.forEachChild(sourceFile,walk);return {declarationCounts,assignedNames,constAliasCandidates}}function resolveAliasTargets(candidates,declarationCounts,assignedNames,imports){const aliasBindings=new Map;const disqualified=[];const handledDeclarations=new Set;for(const candidate of candidates){const baseBinding=imports.get(candidate.baseName);if(baseBinding===void 0||imports.has(candidate.aliasName))continue;handledDeclarations.add(candidate.declaration);const isFileUnique=declarationCounts.get(candidate.aliasName)===1;const isNeverAssigned=!assignedNames.has(candidate.aliasName);if(isFileUnique&&isNeverAssigned){aliasBindings.set(candidate.aliasName,baseBinding);}else {disqualified.push(candidate);}}return {aliasBindings,disqualified,handledDeclarations}}function bindingElementKey(element){if(element.propertyName===void 0){return element.name.text}if(ts4__default.default.isIdentifier(element.propertyName)||ts4__default.default.isStringLiteral(element.propertyName)){return element.propertyName.text}if(ts4__default.default.isComputedPropertyName(element.propertyName)){const inner=element.propertyName.expression;return ts4__default.default.isStringLiteralLike(inner)?inner.text:inner}return element.propertyName}function scanFileForDependencies(filePath,sourceText){const scriptKind=filePath.endsWith(".tsx")?ts4__default.default.ScriptKind.TSX:ts4__default.default.ScriptKind.TS;const sourceFile=ts4__default.default.createSourceFile(filePath,sourceText,ts4__default.default.ScriptTarget.Latest,true,scriptKind);const imports=new Map;let hasWildcardReExport=false;for(const statement of sourceFile.statements){if(ts4__default.default.isImportDeclaration(statement)){collectImportBindings(statement,imports);}else if(ts4__default.default.isExportDeclaration(statement)&&!statement.exportClause&&statement.moduleSpecifier){hasWildcardReExport=true;}}const accesses=new Map;function record(localName,site){const sites=accesses.get(localName)??[];sites.push(site);accesses.set(localName,sites);}const{declarationCounts,assignedNames,constAliasCandidates}=collectDeclarationFacts(sourceFile);const{aliasBindings,disqualified,handledDeclarations}=resolveAliasTargets(constAliasCandidates,declarationCounts,assignedNames,imports);for(const[aliasName,binding]of aliasBindings){imports.set(aliasName,binding);}for(const candidate of disqualified){record(candidate.baseName,{kind:"escape",via:"reassignment",...positionOf(sourceFile,candidate.declaration)});}function recordObjectBindingAccesses(pattern,localName){for(const element of pattern.elements){if(element.dotDotDotToken){record(localName,{kind:"escape",via:"rest",...positionOf(sourceFile,element)});}else if(!ts4__default.default.isIdentifier(element.name)){record(localName,{kind:"escape",via:"nested-pattern",...positionOf(sourceFile,element)});}else {const key=bindingElementKey(element);if(typeof key==="string"){record(localName,{kind:"member",member:key,...positionOf(sourceFile,element)});}else {record(localName,{kind:"escape",via:"computed-key",...positionOf(sourceFile,element)});ts4__default.default.forEachChild(key,visit);}}if(element.initializer)ts4__default.default.forEachChild(element.initializer,visit);}}function tryRecordPropertyOrElementAccess(node){const isProperty=ts4__default.default.isPropertyAccessExpression(node);if(!isProperty&&!ts4__default.default.isElementAccessExpression(node))return false;const base=unwrapParens(node.expression);if(!ts4__default.default.isIdentifier(base)||!imports.has(base.text))return false;if(isProperty){record(base.text,{kind:"member",member:node.name.text,...positionOf(sourceFile,node)});return true}const key=staticElementKey(node.argumentExpression);record(base.text,key!==void 0?{kind:"member",member:key,...positionOf(sourceFile,node)}:{kind:"dynamic",...positionOf(sourceFile,node)});ts4__default.default.forEachChild(node.argumentExpression,visit);return true}function visit(node){if(ts4__default.default.isImportDeclaration(node)||ts4__default.default.isExportDeclaration(node)||ts4__default.default.isVariableDeclaration(node)&&handledDeclarations.has(node)){return}if(ts4__default.default.isVariableDeclaration(node)&&ts4__default.default.isObjectBindingPattern(node.name)&&node.initializer){const base=unwrapParens(node.initializer);if(ts4__default.default.isIdentifier(base)&&imports.has(base.text)){recordObjectBindingAccesses(node.name,base.text);return}}if(tryRecordPropertyOrElementAccess(node))return;if(ts4__default.default.isIdentifier(node)&&imports.has(node.text)){record(node.text,{kind:"reference",...positionOf(sourceFile,node)});return}ts4__default.default.forEachChild(node,visit);}ts4__default.default.forEachChild(sourceFile,visit);return {file:filePath,imports,accessesByLocalName:accesses,hasWildcardReExport}}function generatedFileMarker(){return "GENERATED FILE"}function generatedBanner(format="ts"){const text=`${generatedFileMarker()} -- do not edit by hand. Run \`npx env-cap\` to regenerate.`;return {ts:`// ${text}`,markdown:`<!-- ${text} -->`}[format]}function isGeneratedFile(content){const nonEmptyLines=content.split("\n").filter(line=>line.trim().length>0);return nonEmptyLines.slice(0,20).some(line=>line.includes(generatedFileMarker()))}function evidenceDisclaimer(){return "Machine-generated engineering artifact assembled from statically-provable code facts and author-declared documentation. It can support a security, privacy, or compliance review. It does not itself establish compliance with any standard."}function evidenceProjectionNote(evidencePath){const concept="Projected from env-cap's Evidence Model (ADR 0031/0038), the same source every other generated artifact draws from.";return evidencePath===void 0?concept:`${concept} This run also wrote it to \`${evidencePath}\`.`}async function scanAllFiles(scanFiles,readFile){const scanResults=[];for(const file of scanFiles){let text;try{text=await readFile(file);}catch{continue}if(isGeneratedFile(text))continue;scanResults.push(scanFileForDependencies(file,text));}return scanResults}function initBuildingContracts(contracts){const byIdentity2=new Map;for(const contract of contracts){const variables=new Map;for(const variable of contract.variables)variables.set(variable.key,{hasMemberAccess:false,positions:[]});byIdentity2.set(`${contract.file}#${contract.exportName}`,{file:contract.file,exportName:contract.exportName,contractName:contract.contractName,imported:false,variables,consumingFiles:new Set,ambiguousBarrelFiles:new Set,dynamicAccessSites:[],escapeSites:[]});}return byIdentity2}function attributeAccessSites(building,scanFile,sites=[]){for(const site of sites){if(site.kind==="dynamic"){building.dynamicAccessSites.push({file:scanFile,line:site.line,column:site.column});}else if(site.kind==="member"){const variable=building.variables.get(site.member);if(variable){variable.hasMemberAccess=true;variable.positions.push({file:scanFile,line:site.line,column:site.column});}}else {building.escapeSites.push({file:scanFile,line:site.line,column:site.column,via:site.kind==="escape"?site.via:"reference"});}}}function byPosition(a,b){return a.file.localeCompare(b.file)||a.line-b.line}function classifyVariable(info,hasDynamicAccess,hasEscape,dynamicAccessAssertions){if(info.hasMemberAccess){return {status:"used",evidence:"member-access",positions:[...info.positions].sort(byPosition),dynamicAccessAssertions}}const evidence=hasDynamicAccess?"dynamic-access":hasEscape?"escape":"no-access";return {status:hasDynamicAccess||hasEscape?"indeterminate":"unconsumed",evidence,positions:[],dynamicAccessAssertions}}function finalizeContract(building,root,dynamicAccessAcknowledgments){const hasDynamicAccess=building.dynamicAccessSites.length>0;const hasEscape=building.escapeSites.length>0;const relativeFile=displayPath(root,building.file);const variables=new Map;for(const[key,info]of building.variables){const dynamicAccessAssertions=dynamicAccessAcknowledgments?.get(dynamicAccessVariableIdentity(relativeFile,building.exportName,key))??[];variables.set(key,classifyVariable(info,hasDynamicAccess,hasEscape,dynamicAccessAssertions));}return {file:building.file,exportName:building.exportName,contractName:building.contractName,imported:building.imported,hasDynamicAccess,variables,consumingFiles:[...building.consumingFiles].sort(),ambiguousBarrelFiles:[...building.ambiguousBarrelFiles].sort(),dynamicAccessSites:[...building.dynamicAccessSites].sort(byPosition),escapeSites:[...building.escapeSites].sort(byPosition)}}async function buildDependencyGraph(contracts,scanFiles,readFile,context,scannedSurfaces=[{label:"application",root:"."}],dynamicAccessAcknowledgments){const warnings=[];const scanResults=await scanAllFiles(scanFiles,readFile);const scanByFile=new Map(scanResults.map(result=>[result.file,result]));const byIdentity2=initBuildingContracts(contracts);for(const scan of scanResults){for(const[localName,binding]of scan.imports){const resolvedFile=await resolveImportSpecifier(scan.file,binding.specifier,context);if(!resolvedFile)continue;const building=byIdentity2.get(`${resolvedFile}#${binding.importedName}`);if(!building)continue;building.imported=true;building.consumingFiles.add(scan.file);attributeAccessSites(building,scan.file,scan.accessesByLocalName.get(localName));}}for(const scan of scanResults){for(const[,binding]of scan.imports){const resolvedFile=await resolveImportSpecifier(scan.file,binding.specifier,context);if(!resolvedFile)continue;const targetScan=scanByFile.get(resolvedFile);if(!targetScan?.hasWildcardReExport)continue;for(const building of byIdentity2.values()){if(building.imported)continue;if(building.exportName!==binding.importedName)continue;building.ambiguousBarrelFiles.add(scan.file);}}}const finalContracts=[...byIdentity2.values()].map(building=>finalizeContract(building,context.root,dynamicAccessAcknowledgments));return {contracts:finalContracts,warnings,scannedSurfaces}}function formatSurfaces(surfaces){return surfaces.map(s=>s.label).join(", ")}function formatSites(sites){return sites.map(s=>`${s.file}:${s.line}:${s.column}`).join(", ")}function formatEscapeSites(sites){return sites.map(s=>`${s.file}:${s.line}:${s.column} (${s.via})`).join(", ")}function deriveOwnershipFindings(graph){const abandoned=[];const unresolvedConsumers=[];const unconsumedOwned=[];const indeterminate=[];const asserted=[];const surfaces=formatSurfaces(graph.scannedSurfaces);const hasFreshAssertion=info=>info.dynamicAccessAssertions.some(a=>a.acknowledgment==="fresh");for(const contract of graph.contracts){const identity={file:contract.file,exportName:contract.exportName,contractName:contract.contractName};if(!contract.imported){if(contract.ambiguousBarrelFiles.length>0){unresolvedConsumers.push({...identity,reason:`Not directly imported anywhere, but ${contract.ambiguousBarrelFiles.length} file(s) import "${contract.exportName}" through a specifier that resolves to a file containing an unresolved "export * from" re-export -- cannot determine whether this contract is forwarded by it. Files: ${contract.ambiguousBarrelFiles.join(", ")}.`});}else {abandoned.push(identity);}continue}for(const[key,info]of contract.variables){if(hasFreshAssertion(info)){if(info.status==="unconsumed"||info.status==="indeterminate"){asserted.push({...identity,key,wouldBeStatus:info.status,dynamicAccessAssertions:info.dynamicAccessAssertions});}continue}if(info.status==="unconsumed"){unconsumedOwned.push({...identity,key});}else if(info.status==="indeterminate"){const reason=info.evidence==="dynamic-access"?`Dynamic (computed) property access was observed on "${contract.exportName}" -- cannot determine whether "${key}" is read. Searched: ${surfaces}. Dynamic access observed at: ${formatSites(contract.dynamicAccessSites)}.`:`Contract "${contract.exportName}" is used in a way this scanner's local dataflow analysis cannot follow (a rest binding, nested destructuring, a computed key, an ambiguous alias, or the contract passed/referenced directly) -- cannot determine whether "${key}" is read. Searched: ${surfaces}. Escaped at: ${formatEscapeSites(contract.escapeSites)}.`;indeterminate.push({...identity,key,reason,dynamicAccessSites:contract.dynamicAccessSites,escapeSites:contract.escapeSites});}}}return {abandoned,unresolvedConsumers,unconsumedOwned,indeterminate,asserted}}var DEPENDENCY_MODEL_SCHEMA_VERSION=2;function relativizePosition2(root,position){return {...position,file:displayPath(root,position.file)}}async function buildDependencyModel(contracts,scanFiles,readFile,context,root,scannedSurfaces,dynamicAccessAcknowledgments){const graph=await buildDependencyGraph(contracts,scanFiles,readFile,context,scannedSurfaces,dynamicAccessAcknowledgments);const modelContracts=graph.contracts.map(contract=>({file:displayPath(root,contract.file),exportName:contract.exportName,contractName:contract.contractName,imported:contract.imported,hasDynamicAccess:contract.hasDynamicAccess,variables:[...contract.variables.entries()].map(([key,info])=>({key,status:info.status,positions:info.positions.map(p=>relativizePosition2(root,p)),dynamicAccessAssertions:info.dynamicAccessAssertions})).sort((a,b)=>a.key.localeCompare(b.key)),consumingFiles:[...contract.consumingFiles].map(f=>displayPath(root,f)).sort(),ambiguousBarrelFiles:[...contract.ambiguousBarrelFiles].map(f=>displayPath(root,f)).sort(),dynamicAccessSites:contract.dynamicAccessSites.map(p=>relativizePosition2(root,p))}));modelContracts.sort(byContractIdentity);const byFile=new Map;for(const contract of modelContracts){const ref={file:contract.file,exportName:contract.exportName};for(const consumingFile of contract.consumingFiles){const refs=byFile.get(consumingFile)??[];refs.push(ref);byFile.set(consumingFile,refs);}}const consumers=[...byFile.entries()].map(([file,refs])=>({file,contracts:refs})).sort((a,b)=>a.file.localeCompare(b.file));return {schemaVersion:DEPENDENCY_MODEL_SCHEMA_VERSION,contracts:modelContracts,consumers,warnings:graph.warnings,scannedSurfaces:graph.scannedSurfaces}}function formatIssues(header,issues){const blocks=issues.map(issue=>`${issue.variable}
16
+ ${"-".repeat(Math.max(issue.variable.length,3))}
17
+ [${issue.severity}] ${issue.reason}
18
+
19
+ Declared in:
20
+ ${issue.files.join("\n")}`);return [header,...blocks].join("\n\n")}function countedHeader(prefix,singularNoun,issues){const noun=issues.length===1?singularNoun:`${singularNoun}s`;return `${prefix}
21
+
22
+ ${issues.length} ${noun} found:`}var EnvManifestGenerationError=class _EnvManifestGenerationError extends Error{constructor(issues){super(formatIssues(countedHeader("Environment manifest generation failed.","incompatible declaration",issues),issues));this.code="ENV_MANIFEST_GENERATION_FAILED";this.name="EnvManifestGenerationError";this.issues=issues;Error.captureStackTrace?.(this,_EnvManifestGenerationError);}};var EnvDocumentationGenerationError=class _EnvDocumentationGenerationError extends Error{constructor(issues){super(formatIssues(countedHeader("Environment documentation generation failed.","documentation issue",issues),issues));this.code="ENV_DOCUMENTATION_GENERATION_FAILED";this.name="EnvDocumentationGenerationError";this.issues=issues;Error.captureStackTrace?.(this,_EnvDocumentationGenerationError);}};var EnvUsageAnalysisError=class _EnvUsageAnalysisError extends Error{constructor(issues){super(formatIssues(countedHeader("Dependency ownership report generation failed.","ownership issue",issues),issues));this.code="ENV_USAGE_ANALYSIS_FAILED";this.name="EnvUsageAnalysisError";this.issues=issues;Error.captureStackTrace?.(this,_EnvUsageAnalysisError);}};var EnvProjectGenerationError=class _EnvProjectGenerationError extends Error{constructor(issues){super(formatIssues(countedHeader("Project generation failed.","blocking issue",issues),issues));this.code="ENV_PROJECT_GENERATION_FAILED";this.name="EnvProjectGenerationError";this.issues=issues;Error.captureStackTrace?.(this,_EnvProjectGenerationError);}};function detectExclusiveGroupIssues(contracts){const byGroup=new Map;for(const contract of contracts){if(!contract.active||!contract.exclusiveGroup)continue;const members=byGroup.get(contract.exclusiveGroup)??[];members.push(contract);byGroup.set(contract.exclusiveGroup,members);}const issues=[];for(const[group,members]of [...byGroup.entries()].sort((a,b)=>a[0].localeCompare(b[0]))){for(let i=0;i<members.length;i++){for(let j=i+1;j<members.length;j++){const a=members[i];const b=members[j];issues.push({severity:"error",variable:`Exclusive group "${group}"`,files:[a.file,b.file],reason:`"${a.contractName}" and "${b.contractName}" are both active and both declare exclusiveGroup "${group}" -- only one active contract per exclusive group is allowed. Set active: false on whichever one isn't in use.`});}}}return issues}function discoverValidationContexts(contracts){const contexts=new Set;for(const contract of contracts){if(!contract.active)continue;for(const variable of contract.variables){if(variable.context!==void 0)contexts.add(variable.context);}}return [...contexts].sort()}function renderManifest(contracts,outputFile){const sorted=[...contracts].sort((a,b)=>a.file.localeCompare(b.file));const outputDir=path6__default.default.dirname(outputFile);const usedNames=new Set;const importLines=[];const referenceNames=[];for(const contract of sorted){let localName=contract.exportName;let suffix=1;while(usedNames.has(localName)){localName=`${contract.exportName}_${suffix}`;suffix+=1;}usedNames.add(localName);referenceNames.push(localName);let specifier;if(contract.packageOrigin){specifier=contract.packageOrigin.packageName;}else {const withoutExt=contract.file.replace(/\.tsx?$/,"");specifier=path6__default.default.relative(outputDir,withoutExt).split(path6__default.default.sep).join("/");if(!specifier.startsWith("."))specifier=`./${specifier}`;}const binding=localName===contract.exportName?contract.exportName:`${contract.exportName} as ${localName}`;importLines.push(`import { ${binding} } from "${specifier}";`);}const activeContexts=discoverValidationContexts(contracts);const activeContextsLines=activeContexts.length>0?["// all currently active validation contexts",`export const activeContexts = [${activeContexts.map(c=>JSON.stringify(c)).join(", ")}];`,""]:[];const lines=[generatedBanner("ts"),"",...importLines,"",...activeContextsLines,"export const manifest = [",...referenceNames.map(name=>` ${name},`),"];",""];return lines.join("\n")}function defaultInclude(){return ["**/env.schema.ts"]}function defaultExclude(){return ["**/node_modules/**","**/dist/**","**/.git/**"]}function computeManifest(root,linkResult,onIncompatibility){const{contracts}=linkResult;const activeContracts=contracts.filter(contract=>contract.active);const compatibilityIssues=[...detectCompatibilityIssues(activeContracts),...detectExclusiveGroupIssues(activeContracts),...detectDuplicateVariableShapes(activeContracts)];const compatibilityErrors=compatibilityIssues.filter(issue=>issue.severity==="error");const blocking=onIncompatibility==="throw"?compatibilityIssues.filter(issue=>issue.severity!=="info"):compatibilityErrors;return {activeContracts,contractSummaries:contracts.map(contract=>summarizeContract(contract,root)),warnings:compatibilityIssues.filter(issue=>issue.severity==="warning"),blocking}}async function writeManifest(outputPath,activeContracts,fs){const manifestSource=renderManifest(activeContracts,outputPath);await fs.mkdir(path6__default.default.dirname(outputPath),{recursive:true});await fs.writeFile(outputPath,manifestSource,"utf8");}async function generateEnvManifest(options){const root=path6__default.default.resolve(options.root??process.cwd());const include=options.include??defaultInclude();const exclude=options.exclude??defaultExclude();const packages=options.packages??[];const onIncompatibility=options.onIncompatibility??"warn";const locationResult=resolveWithinRoot(root,options.location,"location","generateEnvManifest");if(!locationResult.ok)throw new EnvManifestGenerationError([locationResult.issue]);const outputPath=locationResult.resolved;const{linkResult,packageWarnings,tsconfigWarnings}=await assembleProject({fs:options.fs,root,include,exclude,packages,tsconfig:options.tsconfig});const computed=computeManifest(root,linkResult,onIncompatibility);if(computed.blocking.length>0)throw new EnvManifestGenerationError(computed.blocking);await writeManifest(outputPath,computed.activeContracts,options.fs);return {outputPath,contracts:computed.contractSummaries,warnings:computed.warnings,parseWarnings:[...packageWarnings,...tsconfigWarnings,...linkResult.warnings]}}function sortedByContractName(entries){return [...entries].sort((a,b)=>a.contractName.localeCompare(b.contractName))}function renderDependencyOwnershipTable(entries){if(entries.length===0)return [];const lines=["## Dependency ownership","","Who owns each contract, who depends on it, and the blast radius if it changes.","","| Contract | Owner | Variables | Consumers | Blast radius |","|---|---|---|---|---|"];for(const entry of sortedByContractName(entries)){const consumers=entry.consumers.length>0?entry.consumers.join(", "):"--";lines.push(`| ${entry.contractName} (\`${entry.file}\`) | ${entry.owner??"--"} | ${entry.variableCount} | ${consumers} | ${entry.consumers.length} |`);}lines.push("");return lines}function renderAbandoned(findings){if(findings.length===0)return [];const lines=["## Abandoned ownership","","Contracts never imported anywhere in the scanned repository -- a feature's schema outliving the feature.","","| Contract | Owner | File |","|---|---|---|"];for(const f of sortedByContractName(findings))lines.push(`| ${f.contractName} | ${f.owner??"--"} | \`${f.file}\` |`);lines.push("");return lines}function renderUnresolvedConsumers(findings){if(findings.length===0)return [];const lines=["## Unresolved consumers (barrel re-exports)","","Contracts reachable only through an unresolved `export * from` barrel re-export -- cannot be proven abandoned or consumed. Advisory only; never fails a build.",""];for(const f of sortedByContractName(findings))lines.push(`- **${f.contractName}** (\`${f.file}\`): ${f.reason}`);lines.push("");return lines}function formatStaleOrMissingCitations(citations){if(citations.length===0)return "--";return citations.map(c=>`${c.position.file}:${c.position.line}:${c.position.column} (${c.acknowledgment})`).join(", ")}function renderUnconsumedOwned(findings,scannedSurfaces){if(findings.length===0)return [];const surfaces=scannedSurfaces.map(s=>s.label).join(", ");const lines=["## Unconsumed owned dependencies","","Variables no consumer reads within the scanned surfaces below. Not proof of dead code. Common reasons a real consumer wouldn't show up here: it's read by a separate, out-of-repo service or webhook handler; it's consumed by non-TypeScript code (a shell script, a Dockerfile, a Terraform/Kubernetes manifest); or it's read from an allow-listed package whose directory wasn't included in this project's own `packages` configuration (ADR 0014).","",`Searched: ${surfaces}.`,"",'A blank Stale/missing citations cell is the strongest "looks genuinely unused" signal -- no developer has ever claimed otherwise. A non-blank cell means someone specifically claimed dynamic access here via `dynamicAccess`, and that claim can no longer be verified (ADR 0037) -- check with them before deleting.',"","| Variable | Contract | Owner | Stale/missing citations |","|---|---|---|---|"];for(const f of sortedByContractName(findings))lines.push(`| \`${f.key}\` | ${f.contractName} | ${f.owner??"--"} | ${formatStaleOrMissingCitations(f.staleOrMissingCitations)} |`);lines.push("");return lines}function renderIndeterminate(findings){if(findings.length===0)return [];const lines=["## Indeterminate (dynamic access)","","Dynamic (computed) property access was observed -- usage cannot be determined statically. Never guessed at.","","| Variable | Contract | Reason | Stale/missing citations |","|---|---|---|---|"];for(const f of sortedByContractName(findings))lines.push(`| \`${f.key}\` | ${f.contractName} | ${f.reason} | ${formatStaleOrMissingCitations(f.staleOrMissingCitations)} |`);lines.push("");return lines}function formatAssertions(assertions){const sites=assertions.map(a=>`${a.file}:${a.line}:${a.column}`).join(", ");return `Per developers, this data point is dynamically accessed at ${sites}.`}function renderAsserted(findings){if(findings.length===0)return [];const lines=["## Asserted (developer-acknowledged dynamic access)","","Variables env-cap's own scan would otherwise flag as unconsumed or indeterminate, but a developer has cited exactly where the dynamic access happens via `dynamicAccess`. The raw static status is shown alongside the citation, never hidden behind it -- a citation is a re-acknowledgment, not proof.","","| Variable | Contract | Static status | Developer assertion |","|---|---|---|---|"];for(const f of sortedByContractName(findings))lines.push(`| \`${f.key}\` | ${f.contractName} | ${f.wouldBeStatus} | ${formatAssertions(f.dynamicAccessAssertions)} |`);lines.push("");return lines}function renderParseWarnings(warnings){if(warnings.length===0)return [];const lines=["## Parse warnings","","Includes any `packages` (ADR 0014) resolution failures, alongside ordinary schema-discovery warnings.",""];for(const w of warnings)lines.push(`- **${w.file}**: ${w.message}`);lines.push("");return lines}function renderUsageReport(computed){const lines=[generatedBanner("markdown"),"",`> ${evidenceDisclaimer()}`,"",`> ${evidenceProjectionNote()}`,"","# Dependency & Ownership Report","","_Produced by `env-cap --ownership`._","","Which feature owns each variable, which features consume that contract, and what the blast radius is if it changes.","",...renderDependencyOwnershipTable(computed.dependencyOwnership),...renderAbandoned(computed.abandonedContracts),...renderUnresolvedConsumers(computed.unresolvedConsumers),...renderUnconsumedOwned(computed.unconsumedOwnedVariables,computed.scannedSurfaces),...renderIndeterminate(computed.indeterminate),...renderAsserted(computed.asserted),...renderParseWarnings(computed.parseWarnings)];return lines.join("\n").replace(/\n{3,}/g,"\n\n")}async function computeScanSurface(root,exclude,origins,fs){const localScanFiles=await discoverSchemaFiles({fs,root,include:["**/*.ts","**/*.tsx"],exclude});const scannedSurfaces=[{label:"application",root:"."}];const scanFiles=[...localScanFiles];const seenPackageDirs=new Set;for(const origin of origins.values()){if(seenPackageDirs.has(origin.packageDir))continue;seenPackageDirs.add(origin.packageDir);const packageScanFiles=await discoverSchemaFiles({fs,root:origin.packageDir,include:["**/*.ts","**/*.tsx"],exclude:defaultExclude()});scanFiles.push(...packageScanFiles);scannedSurfaces.push({label:`package:${origin.packageName}`,root:displayPath(root,origin.packageDir)});}return {scanFiles,scannedSurfaces}}async function computeUsage(root,contracts,scanFiles,readFile,context,parseWarnings,scannedSurfaces,dynamicAccessAcknowledgments){const graph=await buildDependencyGraph(contracts,scanFiles,readFile,context,scannedSurfaces,dynamicAccessAcknowledgments);const findings=deriveOwnershipFindings(graph);const contractByIdentity=new Map;for(const contract of contracts)contractByIdentity.set(`${contract.file}#${contract.exportName}`,contract);const ownerFor=(file,exportName)=>contractByIdentity.get(`${file}#${exportName}`)?.owner;const effectiveOwnerFor=(file,exportName,key)=>{const contract=contractByIdentity.get(`${file}#${exportName}`);if(!contract)return void 0;const variable=contract.variables.find(v=>v.key===key);return variable?effectiveOwner(contract,variable):contract.owner};const dependencyOwnership=graph.contracts.map(c=>({contractName:c.contractName,file:path6__default.default.relative(root,c.file),owner:ownerFor(c.file,c.exportName),variableCount:c.variables.size,consumers:c.consumingFiles.map(f=>path6__default.default.relative(root,f))}));const abandonedContracts=findings.abandoned.map(f=>({contractName:f.contractName,file:path6__default.default.relative(root,f.file),owner:ownerFor(f.file,f.exportName)}));const unresolvedConsumers=findings.unresolvedConsumers.map(f=>({contractName:f.contractName,file:path6__default.default.relative(root,f.file),reason:f.reason}));const staleOrMissingCitationsFor=(contractName,file,exportName,key)=>{const relativeFile=displayPath(root,file);const identity=dynamicAccessVariableIdentity(relativeFile,exportName,key);const problems=[];for(const a of dynamicAccessAcknowledgments?.get(identity)??[]){if(a.acknowledgment==="fresh")continue;problems.push({contractName,file:relativeFile,exportName,key,position:{file:a.file,line:a.line,column:a.column},acknowledgment:a.acknowledgment});}return problems};const unconsumedOwnedVariables=findings.unconsumedOwned.map(f=>({contractName:f.contractName,owner:effectiveOwnerFor(f.file,f.exportName,f.key),key:f.key,staleOrMissingCitations:staleOrMissingCitationsFor(f.contractName,f.file,f.exportName,f.key)}));const indeterminate=findings.indeterminate.map(f=>({contractName:f.contractName,key:f.key,reason:f.reason,dynamicAccessSites:f.dynamicAccessSites.map(p=>({...p,file:displayPath(root,p.file)})),staleOrMissingCitations:staleOrMissingCitationsFor(f.contractName,f.file,f.exportName,f.key)}));const asserted=findings.asserted.map(f=>({contractName:f.contractName,key:f.key,wouldBeStatus:f.wouldBeStatus,dynamicAccessAssertions:f.dynamicAccessAssertions}));return {graph,result:{dependencyOwnership,abandonedContracts,unresolvedConsumers,unconsumedOwnedVariables,indeterminate,asserted,parseWarnings,scannedSurfaces:graph.scannedSurfaces}}}async function writeUsageReport(reportPath,computed,fs){const source=renderUsageReport(computed);await fs.mkdir(path6__default.default.dirname(reportPath),{recursive:true});await fs.writeFile(reportPath,source,"utf8");}async function generateUsageReport(options){const root=path6__default.default.resolve(options.root??process.cwd());const include=options.include??defaultInclude();const exclude=options.exclude??defaultExclude();const packages=options.packages??[];let reportPath;if(options.report){const result=resolveWithinRoot(root,options.report.location,"report.location","generateUsageReport");if(!result.ok)throw new EnvUsageAnalysisError([result.issue]);reportPath=result.resolved;}const{readFileCached,linkResult,context,origins,packageWarnings,tsconfigWarnings}=await assembleProject({fs:options.fs,root,include,exclude,packages,tsconfig:options.tsconfig});const{scanFiles,scannedSurfaces}=await computeScanSurface(root,exclude,origins,options.fs);const computed=await computeUsage(root,linkResult.contracts,scanFiles,readFileCached,context,[...packageWarnings,...tsconfigWarnings,...linkResult.warnings],scannedSurfaces);if(reportPath)await writeUsageReport(reportPath,computed.result,options.fs);return {reportPath,...computed.result}}var FINDING_MODEL_SCHEMA_VERSION=3;var NO_POSITION=void 0;function fromCompatibilityIssue(root,issue,code){return {severity:issue.severity,code,family:"compatibility",message:issue.reason,location:{model:"contract",file:displayPath(root,issue.files[0]),exportName:void 0,variable:issue.variable,position:NO_POSITION}}}function buildFindingModel(input){const{root}=input;const findings=[];for(const issue of input.compatibilityIssues??[]){findings.push(fromCompatibilityIssue(root,issue,issue.code??"DUPLICATE_VARIABLE_DOCUMENTATION"));}for(const issue of input.exclusiveGroupIssues??[]){findings.push(fromCompatibilityIssue(root,issue,"EXCLUSIVE_GROUP_VIOLATION"));}for(const check of input.artifactCheckFindings??[]){if(check.status==="ok")continue;findings.push({severity:"warning",code:check.status==="missing"?"ARTIFACT_MISSING":"ARTIFACT_STALE",family:"drift",message:check.detail??`${check.artifact} artifact is ${check.status}.`,location:{model:"change",path:check.path}});}const documentation=input.documentation;if(documentation){for(const c of documentation.undocumentedContracts){findings.push({severity:"warning",code:"UNDOCUMENTED_CONTRACT",family:"documentation",message:`"${c.exportName}" has no documentEnv() call linked to it.`,location:{model:"contract",file:displayPath(root,c.file),exportName:c.exportName,variable:void 0,position:NO_POSITION}});}for(const v of documentation.undocumentedVariables){findings.push({severity:"warning",code:"UNDOCUMENTED_VARIABLE",family:"documentation",message:`"${v.key}" (declared by "${v.exportName}") has no matching entry in a linked documentEnv()'s "variables".`,location:{model:"contract",file:displayPath(root,v.file),exportName:v.exportName,variable:v.key,position:NO_POSITION}});}for(const s of documentation.staleDocEntries){findings.push({severity:"warning",code:"STALE_DOC_ENTRY",family:"documentation",message:`"${s.key}" is documented under "${s.exportName}" but no longer exists in that contract's schema.`,location:{model:"contract",file:displayPath(root,s.file),exportName:s.exportName,variable:s.key,position:NO_POSITION}});}for(const e of documentation.expiringSoon){const expired=e.daysRemaining<0;findings.push({severity:"warning",code:expired?"EXPIRED":"EXPIRING_SOON",family:"documentation",message:expired?`"${e.key??e.exportName}" expired ${Math.abs(e.daysRemaining)} day(s) ago (expiresAt: ${e.expiresAt}).`:`"${e.key??e.exportName}" expires in ${e.daysRemaining} day(s) (expiresAt: ${e.expiresAt}).`,location:{model:"contract",file:displayPath(root,e.file),exportName:e.exportName,variable:e.key,position:NO_POSITION}});}for(const n of documentation.nonstandardSensitivityLevels){findings.push({severity:"info",code:"NONSTANDARD_SENSITIVITY_LEVEL",family:"documentation",message:`"${n.key??n.exportName}" declares sensitivity "${n.sensitivity}", which isn't one of the standard levels (secret/credential/pii/config) -- still honored verbatim, just flagged for vocabulary drift.`,location:{model:"contract",file:displayPath(root,n.file),exportName:n.exportName,variable:n.key,position:NO_POSITION}});}for(const u of documentation.unresolvedLinks){findings.push({severity:"warning",code:"UNRESOLVED_DOCUMENTENV_LINK",family:"documentation",message:u.reason,location:{model:"contract",file:displayPath(root,u.file),exportName:void 0,variable:void 0,position:NO_POSITION}});}}for(const a of input.abandonedContracts??[]){findings.push({severity:"warning",code:"ABANDONED_CONTRACT",family:"ownership",message:`"${a.contractName}" is never imported anywhere in the scanned repository.`,location:{model:"ownership",contractName:a.contractName,file:a.file,variable:void 0,position:NO_POSITION}});}for(const u of input.unresolvedConsumers??[]){findings.push({severity:"warning",code:"UNRESOLVED_CONSUMER",family:"ownership",message:u.reason,location:{model:"ownership",contractName:u.contractName,file:u.file,variable:void 0,position:NO_POSITION}});}for(const u of input.unconsumedOwnedVariables??[]){findings.push({severity:"warning",code:"UNCONSUMED_OWNED_VARIABLE",family:"ownership",message:`"${u.key}" (declared by "${u.contractName}") has no consumer found in the scanned repository.`,location:{model:"ownership",contractName:u.contractName,file:void 0,variable:u.key,position:NO_POSITION}});}for(const i of input.indeterminateOwnership??[]){findings.push({severity:"warning",code:"INDETERMINATE_OWNERSHIP",family:"ownership",message:i.reason,location:{model:"ownership",contractName:i.contractName,file:void 0,variable:i.key,position:NO_POSITION}});}for(const p of input.dynamicAccessCitationProblems??[]){const missing=p.acknowledgment==="missing";findings.push({severity:"warning",code:missing?"MISSING_DYNAMIC_ACCESS_CITATION":"STALE_DYNAMIC_ACCESS_CITATION",family:"ownership",message:missing?`"${p.key}" (declared by "${p.contractName}") cites dynamic access at ${p.position.file}:${p.position.line}:${p.position.column}, but that file no longer exists. Re-run generate:env once the citation is corrected.`:`"${p.key}" (declared by "${p.contractName}") cites dynamic access at ${p.position.file}:${p.position.line}:${p.position.column}, but that file's content has changed since it was last acknowledged. Re-run generate:env once you've re-confirmed the citation still applies.`,location:{model:"ownership",contractName:p.contractName,file:p.file,variable:p.key,position:p.position}});}return {schemaVersion:FINDING_MODEL_SCHEMA_VERSION,findings}}function humanizeKey(key){const spaced=key.replace(/([a-z0-9])([A-Z])/g,"$1 $2");return spaced.charAt(0).toUpperCase()+spaced.slice(1)}function renderMetadataValue(value){return typeof value==="string"?value:JSON.stringify(value)}function defineEvidenceProjection(schema){function project(evidence){const value={};const sources={};for(const key of Object.keys(schema)){const projector=schema[key];const{proxy,readPaths}=createTrackingProxy(evidence);value[key]=projector(proxy);sources[key]=readPaths();}return {value,sources}}function invoke(evidence){return project(evidence).value}return Object.assign(invoke,{project})}function readOnlyMembraneError(){return new TypeError("EvidenceModel is read-only inside a projector -- a projection must be a pure function of its evidence argument. See ADR 0032.")}function createTrackingProxy(evidence){const clone=structuredClone(evidence);const paths=new Set;const wrapped=new WeakMap;function wrap(value,path18){if(value===null||typeof value!=="object")return value;const target=value;const cached=wrapped.get(target);if(cached)return cached;const proxy=new Proxy(target,{get(currentTarget,prop,receiver){const result=Reflect.get(currentTarget,prop,receiver);if(typeof prop!=="string")return result;if(Array.isArray(currentTarget)&&prop==="length")return result;const nextPath=[...path18,prop];paths.add(nextPath.join("."));return wrap(result,nextPath)},set(){throw readOnlyMembraneError()},deleteProperty(){throw readOnlyMembraneError()},defineProperty(){throw readOnlyMembraneError()},setPrototypeOf(){throw readOnlyMembraneError()}});wrapped.set(target,proxy);return proxy}return {proxy:wrap(clone,[]),readPaths:()=>[...paths].sort()}}function byContractThenKey(a,b){return a.file.localeCompare(b.file)||a.exportName.localeCompare(b.exportName)||a.key.localeCompare(b.key)}function contractIndex(contracts){return new Map(contracts.map(c=>[`${c.file}#${c.exportName}`,c]))}function groupVariablesByOwner(contracts,ownerOf){const byOwner=new Map;for(const contract of contracts){for(const variable of contract.variables){const owner=ownerOf(contract,variable);if(owner===void 0)continue;const list=byOwner.get(owner)??[];list.push({contract,variable});byOwner.set(owner,list);}}return byOwner}var configurationReference=defineEvidenceProjection({disclaimer:()=>evidenceDisclaimer(),entries:evidence=>{const entries=[];for(const contract of evidence.contract.contracts){for(const variable of contract.variables){entries.push({file:contract.file,exportName:contract.exportName,contractName:contract.contractName,key:variable.key,description:variable.description,owner:variable.owner??contract.owner,sensitivity:variable.sensitivity??contract.sensitivity,required:variable.required,hasDefault:variable.hasDefault,hasProcessor:variable.hasProcessor,hasValidator:variable.hasValidator,expiresAt:variable.expiresAt,active:contract.active});}}return entries.sort(byContractThenKey)}});var ownershipSummary=defineEvidenceProjection({disclaimer:()=>evidenceDisclaimer(),owners:evidence=>{const contracts=contractIndex(evidence.contract.contracts);const nameOf=(file,exportName)=>contracts.get(`${file}#${exportName}`)?.contractName??exportName;const variablesByOwner=groupVariablesByOwner(evidence.ownership.contracts,(_contract,variable)=>variable.owner);const contractsByOwner=new Map;for(const contract of evidence.ownership.contracts){if(contract.owner===void 0)continue;const list=contractsByOwner.get(contract.owner)??[];list.push(nameOf(contract.file,contract.exportName));contractsByOwner.set(contract.owner,list);}const owners=new Set([...variablesByOwner.keys(),...contractsByOwner.keys()]);return [...owners].sort().map(owner=>({owner,variables:(variablesByOwner.get(owner)??[]).map(({contract,variable})=>`${nameOf(contract.file,contract.exportName)}.${variable.key}`).sort(),contracts:(contractsByOwner.get(owner)??[]).sort()}))},unowned:evidence=>{const contracts=contractIndex(evidence.contract.contracts);return evidence.ownership.unownedVariables.map(ref=>{const contractName=contracts.get(`${ref.file}#${ref.exportName}`)?.contractName??ref.exportName;return `${contractName}.${ref.key}`}).sort()}});var expiringSoonReport=defineEvidenceProjection({disclaimer:()=>evidenceDisclaimer(),entries:evidence=>{const contracts=contractIndex(evidence.contract.contracts);const ownerByVariable=new Map;for(const contract of evidence.ownership.contracts)for(const variable of contract.variables)ownerByVariable.set(`${contract.file}#${contract.exportName}#${variable.key}`,variable.owner);return evidence.lifecycle.expiring.map(entry=>{const contract=contracts.get(`${entry.file}#${entry.exportName}`);const variable=entry.key===void 0?void 0:contract?.variables.find(v=>v.key===entry.key);return {file:entry.file,exportName:entry.exportName,contractName:contract?.contractName??entry.exportName,key:entry.key,expiresAt:entry.expiresAt,daysRemaining:entry.daysRemaining,expired:entry.daysRemaining<0,refreshInstructions:variable?.refreshInstructions,owner:entry.key===void 0?contract?.owner:ownerByVariable.get(`${entry.file}#${entry.exportName}#${entry.key}`)??contract?.owner}})},expiredCount:evidence=>evidence.lifecycle.expiring.filter(e=>e.daysRemaining<0).length});function parseIsoDate(value){const date=new Date(value);return Number.isNaN(date.getTime())?void 0:date}var MS_PER_DAY=864e5;function daysRemainingFrom(date,now){return Math.ceil((date.getTime()-now.getTime())/MS_PER_DAY)}function computeExpiringEntries(contracts,expiringWithinDays,now){const entries=[];for(const contract of contracts){if(contract.expiresAt){const date=parseIsoDate(contract.expiresAt);if(date){const daysRemaining=daysRemainingFrom(date,now);if(daysRemaining<=expiringWithinDays){entries.push({file:contract.file,exportName:contract.exportName,key:void 0,expiresAt:contract.expiresAt,daysRemaining});}}}for(const variable of contract.variables){if(!variable.expiresAt)continue;const date=parseIsoDate(variable.expiresAt);if(!date)continue;const daysRemaining=daysRemainingFrom(date,now);if(daysRemaining<=expiringWithinDays){entries.push({file:contract.file,exportName:contract.exportName,key:variable.key,expiresAt:variable.expiresAt,daysRemaining});}}}return entries.sort((a,b)=>a.daysRemaining-b.daysRemaining)}function buildCatalog(contracts){return sortedContracts(contracts).map(contract=>{const variables={};for(const variable of contract.variables){variables[variable.key]={description:variable.description,owner:effectiveOwner(contract,variable),sensitivity:effectiveSensitivity(contract,variable),expiresAt:variable.expiresAt,refreshInstructions:variable.refreshInstructions,setupInstructions:variable.setupInstructions,required:variable.required,hasDefault:variable.hasDefault,hasProcessor:variable.hasProcessor,processorReturnType:variable.processorReturnType,hasValidator:variable.hasValidator,documented:variable.documented,context:variable.context,purpose:effectivePurpose(contract,variable),legalBasis:effectiveLegalBasis(contract,variable),retention:effectiveRetention(contract,variable),dataResidency:effectiveDataResidency(contract,variable),auditRequired:effectiveAuditRequired(contract,variable),metadata:variable.metadata,evidence:variable.evidence};}return {file:contract.file,exportName:contract.exportName,contractName:contract.contractName,active:contract.active,documented:contract.documented,category:contract.category,exclusiveGroup:contract.exclusiveGroup,...governanceFieldsOf(contract),variables}})}function extractPreviouslyDocumentedKeys(previousContent){const keys=new Set;for(const match of previousContent.matchAll(/^### `([A-Za-z_][A-Za-z0-9_]*)`$/gm)){if(match[1])keys.add(match[1]);}return keys}function extractPreviouslyActiveKeys(previousContent){const keys=new Set;let currentContractActive=false;for(const line of previousContent.split("\n")){const activeMatch=/^- Active: (yes|no)$/.exec(line);if(activeMatch){currentContractActive=activeMatch[1]==="yes";continue}const headingMatch=/^### `([A-Za-z_][A-Za-z0-9_]*)`$/.exec(line);if(headingMatch&&currentContractActive){keys.add(headingMatch[1]);}}return keys}function computeChangeSummary(contracts,previousContent){if(previousContent===void 0)return {added:[],removed:[],commented:[]};const previousKeys=extractPreviouslyDocumentedKeys(previousContent);const previousActiveKeys=extractPreviouslyActiveKeys(previousContent);const currentKeys=new Set;const currentActiveKeys=new Set;for(const contract of contracts){for(const variable of contract.variables){currentKeys.add(variable.key);if(contract.active)currentActiveKeys.add(variable.key);}}return {added:[...currentKeys].filter(k=>!previousKeys.has(k)).sort(),removed:[...previousKeys].filter(k=>!currentKeys.has(k)).sort(),commented:[...previousActiveKeys].filter(k=>currentKeys.has(k)&&!currentActiveKeys.has(k)).sort()}}function slugify(text){return text.toLowerCase().replace(/[^a-z0-9_]+/g,"-").replace(/^-+|-+$/g,"")||"section"}var AnchorRegistry=class{constructor(){this.used=new Map;this.assigned=new Map;}anchorFor(target,baseText){const existing=this.assigned.get(target);if(existing)return existing;const base=slugify(baseText);const count=this.used.get(base)??0;this.used.set(base,count+1);const anchor=count===0?base:`${base}-${count}`;this.assigned.set(target,anchor);return anchor}};function mdLink(text,anchor){return `[${text}](#${anchor})`}function sortedContracts(contracts){return [...contracts].sort((a,b)=>a.contractName.localeCompare(b.contractName))}function renderHeader(contracts,options,anchors,hasOwnership,hasDuplicates,hasLifecycle){const lines=[generatedBanner("markdown"),"",`> ${evidenceDisclaimer()}`,"",`> ${evidenceProjectionNote()}`,"","# Environment Variables","","_Produced by `env-cap --docs`._","",`_Generated ${options.generatedAt.toISOString()}_`,""];const summary=computeChangeSummary(contracts,options.previousContent);lines.push("## Changes since last report","");if(summary.added.length===0&&summary.removed.length===0&&summary.commented.length===0){lines.push("No changes.","");}else {if(summary.added.length>0)lines.push(`- **Added:** ${summary.added.map(k=>`\`${k}\``).join(", ")}`);if(summary.removed.length>0)lines.push(`- **Removed:** ${summary.removed.map(k=>`\`${k}\``).join(", ")}`);if(summary.commented.length>0)lines.push(`- **No longer required (inactive):** ${summary.commented.map(k=>`\`${k}\``).join(", ")}`);lines.push("");}lines.push("## Table of contents","",`- ${mdLink("Catalog","catalog")}`);for(const contract of sortedContracts(contracts)){lines.push(` - ${mdLink(contract.contractName,anchors.anchorFor(contract,`contract-${contract.contractName}`))}`);}if(hasOwnership)lines.push(`- ${mdLink("Ownership matrix","ownership-matrix")}`);if(hasDuplicates)lines.push(`- ${mdLink("Dependency graph","dependency-graph")}`);if(hasLifecycle)lines.push(`- ${mdLink("Lifecycle report","lifecycle-report")}`);lines.push(`- ${mdLink("Security review","security-review")}`,"");return lines}function renderCatalog(contracts,anchors,undocumented){const lines=["## Catalog","",'<a id="catalog"></a>',""];const undocumentedByIdentity=new Set(undocumented.map(u=>`${u.file}#${u.exportName}`));const usesValidationContexts=contracts.some(c=>c.variables.some(v=>v.context));if(usesValidationContexts){lines.push("> Validation contexts describe when validation participates. They do not restrict access to values, and they do not remove a variable's schema (or its `default` value) from wherever this manifest is imported.","");}for(const contract of sortedContracts(contracts)){lines.push(`<a id="${anchors.anchorFor(contract,`contract-${contract.contractName}`)}"></a>`);lines.push(`## ${contract.contractName}`,"",`Source: \`${contract.file}\``);if(undocumentedByIdentity.has(`${contract.file}#${contract.exportName}`)){lines.push("","> \u26A0\uFE0F **Undocumented.** No `documentEnv()` call is linked to this contract.");}lines.push(`- Active: ${contract.active?"yes":"no"}`);if(contract.category!==void 0)lines.push(`- Category: ${contract.category}`);if(contract.exclusiveGroup!==void 0)lines.push(`- Exclusive group: ${contract.exclusiveGroup}`);if(contract.owner!==void 0)lines.push(`- Owner: ${contract.owner}`);if(contract.sensitivity!==void 0)lines.push(`- Sensitivity: ${contract.sensitivity}`);if(contract.expiresAt!==void 0)lines.push(`- Expires: ${contract.expiresAt}`);if(contract.purpose!==void 0)lines.push(`- Purpose: ${contract.purpose}`);if(contract.legalBasis!==void 0)lines.push(`- Legal basis: ${contract.legalBasis}`);if(contract.retention!==void 0)lines.push(`- Retention: ${contract.retention}`);if(contract.dataResidency!==void 0)lines.push(`- Data residency: ${renderMetadataValue(contract.dataResidency)}`);if(contract.auditRequired!==void 0)lines.push(`- Audit required: ${contract.auditRequired?"yes":"no"}`);if(contract.metadata){for(const[metaKey,metaValue]of Object.entries(contract.metadata)){lines.push(`- ${humanizeKey(metaKey)}: ${renderMetadataValue(metaValue)}`);}}lines.push("");for(const variable of [...contract.variables].sort((a,b)=>a.key<b.key?-1:1)){lines.push(`<a id="${anchors.anchorFor(variable,`${contract.contractName}-${variable.key}`)}"></a>`);lines.push(`### \`${variable.key}\``,"");if(variable.description)lines.push(variable.description,"");if(!variable.documented)lines.push("> \u26A0\uFE0F **Undocumented.**","");lines.push(`- Default: ${variable.hasDefault?"yes":"no"}`,`- Processor: ${variable.hasProcessor?"yes":"no"}`,`- Validator: ${variable.hasValidator?"yes":"no"}`);if(variable.context!==void 0)lines.push(`- Validation context: ${variable.context}`);const owner=effectiveOwner(contract,variable);if(owner!==void 0)lines.push(`- Owner: ${owner}`);const sensitivity=effectiveSensitivity(contract,variable);if(sensitivity!==void 0)lines.push(`- Sensitivity: ${sensitivity}`);if(variable.expiresAt!==void 0)lines.push(`- Expires: ${variable.expiresAt}`);if(variable.setupInstructions!==void 0)lines.push(`- Setup instructions: ${variable.setupInstructions}`);if(variable.refreshInstructions!==void 0)lines.push(`- Refresh instructions: ${variable.refreshInstructions}`);if(variable.required!==void 0)lines.push(`- Required: ${variable.required?"yes":"no"}`);const purpose=effectivePurpose(contract,variable);if(purpose!==void 0)lines.push(`- Purpose: ${purpose}`);const legalBasis=effectiveLegalBasis(contract,variable);if(legalBasis!==void 0)lines.push(`- Legal basis: ${legalBasis}`);const retention=effectiveRetention(contract,variable);if(retention!==void 0)lines.push(`- Retention: ${retention}`);const dataResidency=effectiveDataResidency(contract,variable);if(dataResidency!==void 0)lines.push(`- Data residency: ${renderMetadataValue(dataResidency)}`);const auditRequired=effectiveAuditRequired(contract,variable);if(auditRequired!==void 0)lines.push(`- Audit required: ${auditRequired?"yes":"no"}`);const dynamicAccess=variable.evidence?.dynamicAccess;if(dynamicAccess&&dynamicAccess.length>0)lines.push(`- Dynamic access: ${dynamicAccess.join(", ")}`);for(const[metaKey,metaValue]of Object.entries(variable.metadata??{})){lines.push(`- ${humanizeKey(metaKey)}: ${renderMetadataValue(metaValue)}`);}lines.push("");}}return lines}function renderOwnershipMatrix(contracts,anchors){const grouped=groupVariablesByOwner(sortedContracts(contracts),effectiveOwner);const byOwner=new Map;for(const[owner,entries]of grouped){byOwner.set(owner,entries.map(({contract,variable})=>mdLink(`\`${variable.key}\` (${contract.contractName})`,anchors.anchorFor(variable,`${contract.contractName}-${variable.key}`))));}if(byOwner.size===0)return [];const lines=["## Ownership matrix","",'<a id="ownership-matrix"></a>',"","| Owner | Variables |","|---|---|"];for(const owner of [...byOwner.keys()].sort()){lines.push(`| ${owner} | ${mustGet(byOwner,owner).join(", ")} |`);}lines.push("");return lines}function renderDependencyGraph(contracts,anchors){const byKey=new Map;for(const contract of contracts){for(const variable of contract.variables){const list=byKey.get(variable.key)??[];list.push({contract,variable});byKey.set(variable.key,list);}}const hasDuplicates=[...byKey.values()].some(declarations=>declarations.length>1);const lines=["## Dependency graph","",'<a id="dependency-graph"></a>',"",`One row per unique variable name; more than one location means more than one feature declares it (see the package README's "Duplicate variables" section for how that's handled at runtime).`,"","| Variable | Declared in |","|---|---|"];for(const key of [...byKey.keys()].sort()){const declarations=mustGet(byKey,key);const locations=declarations.map(({contract,variable})=>mdLink(`${contract.contractName} (\`${contract.file}\`)`,anchors.anchorFor(variable,`${contract.contractName}-${variable.key}`))).join(", ");lines.push(`| \`${key}\` | ${locations} |`);}lines.push("");return {lines,hasDuplicates}}function renderLifecycleReport(contracts,anchors,expiringWithinDays,now){const rows=[];for(const contract of sortedContracts(contracts)){for(const variable of contract.variables){const owner=effectiveOwner(contract,variable);if(variable.expiresAt===void 0&&owner===void 0&&variable.refreshInstructions===void 0)continue;const expiresAt=variable.expiresAt;const expiry=expiresAt?parseIsoDate(expiresAt):void 0;let expiresCell=expiresAt??"--";if(expiry&&expiresAt){const daysRemaining=daysRemainingFrom(expiry,now);if(daysRemaining<0)expiresCell=`${expiresAt} (**expired ${Math.abs(daysRemaining)}d ago**)`;else if(daysRemaining<=expiringWithinDays)expiresCell=`${expiresAt} (**${daysRemaining}d remaining**)`;}rows.push(`| ${mdLink(`\`${variable.key}\``,anchors.anchorFor(variable,`${contract.contractName}-${variable.key}`))} | ${owner??"--"} | ${expiresCell} | ${variable.refreshInstructions??"--"} |`);}}if(rows.length===0)return {lines:[],hasLifecycle:false};const lines=["## Lifecycle report","",'<a id="lifecycle-report"></a>',"","| Variable | Owner | Expires | Refresh instructions |","|---|---|---|---|",...rows,""];return {lines,hasLifecycle:true}}function computeSecurityReviewCounters(contracts,expiringWithinDays,now,undocumentedContractCount,undocumentedVariableCount){let totalVariableDeclarations=0;let activeVariableDeclarations=0;const uniqueKeys=new Set;const keyContractCount=new Map;let expiresAtSetCount=0;let expiredCount=0;let expiringSoonCount=0;let requiredCount=0;let refreshInstructionsCount=0;let noOwnerCount=0;for(const contract of contracts){for(const variable of contract.variables){totalVariableDeclarations+=1;if(contract.active)activeVariableDeclarations+=1;uniqueKeys.add(variable.key);keyContractCount.set(variable.key,(keyContractCount.get(variable.key)??0)+1);if(variable.required)requiredCount+=1;if(variable.refreshInstructions)refreshInstructionsCount+=1;if(effectiveOwner(contract,variable)===void 0)noOwnerCount+=1;if(variable.expiresAt){expiresAtSetCount+=1;const date=parseIsoDate(variable.expiresAt);if(date){const daysRemaining=daysRemainingFrom(date,now);if(daysRemaining<0)expiredCount+=1;else if(daysRemaining<=expiringWithinDays)expiringSoonCount+=1;}}}}const duplicateVariableNameCount=[...keyContractCount.values()].filter(count=>count>1).length;return {totalContracts:contracts.length,totalVariableDeclarations,activeVariableDeclarations,uniqueVariableNames:uniqueKeys.size,expiresAtSetCount,expiredCount,expiringSoonCount,requiredCount,refreshInstructionsCount,noOwnerCount,duplicateVariableNameCount,undocumentedContractCount,undocumentedVariableCount}}function renderSecurityReview(contracts,options,now){const counters=computeSecurityReviewCounters(contracts,options.expiringWithinDays,now,options.undocumentedContracts.length,options.undocumentedVariables.length);return ["## Security review","",'<a id="security-review"></a>',"",`- Total contracts: ${counters.totalContracts}`,`- Total variable declarations: ${counters.totalVariableDeclarations} (${counters.activeVariableDeclarations} from active contracts)`,`- Unique variable names: ${counters.uniqueVariableNames}`,`- Variables with \`expiresAt\` set: ${counters.expiresAtSetCount}`,` - Already expired: ${counters.expiredCount}`,` - Expiring within ${options.expiringWithinDays} days: ${counters.expiringSoonCount}`,`- Variables marked \`required: true\`: ${counters.requiredCount}`,`- Variables with refresh instructions: ${counters.refreshInstructionsCount}`,`- Variables with no assigned owner: ${counters.noOwnerCount}`,`- Variable names declared by more than one contract: ${counters.duplicateVariableNameCount}`,`- Undocumented contracts: ${counters.undocumentedContractCount}`,`- Undocumented variables: ${counters.undocumentedVariableCount}`,""]}function renderDocs(contracts,options){const anchors=new AnchorRegistry;const sorted=sortedContracts(contracts);const ownership=renderOwnershipMatrix(sorted,anchors);const{lines:dependencyLines,hasDuplicates}=renderDependencyGraph(sorted,anchors);const{lines:lifecycleLines,hasLifecycle}=renderLifecycleReport(sorted,anchors,options.expiringWithinDays,options.generatedAt);const header=renderHeader(sorted,options,anchors,ownership.length>0,hasDuplicates,hasLifecycle);const catalog=renderCatalog(sorted,anchors,options.undocumentedContracts);const security=renderSecurityReview(sorted,options,options.generatedAt);return [...header,...catalog,...ownership,...dependencyLines,...lifecycleLines,...security].join("\n").replace(/\n{3,}/g,"\n\n")}function normalizeDocsForComparison(content){return content.replace(/^_Generated .+_$/m,"_Generated <normalized-for-comparison>_").replace(/\(\*\*\d+d remaining\*\*\)/g,"(**Nd remaining**)").replace(/\(\*\*expired \d+d ago\*\*\)/g,"(**expired Nd ago**)").replace(/^(- Already expired:) \d+$/gm,"$1 N").replace(/^(- Expiring within \d+ days:) \d+$/gm,"$1 N")}function isPrimitiveLiteral(value){return typeof value==="string"||typeof value==="number"||typeof value==="boolean"}function defaultLiteralFor(variable){return variable.defaultValue?.ok===true&&isPrimitiveLiteral(variable.defaultValue.value)?String(variable.defaultValue.value):void 0}function renderVariableLines(variable,options={}){const lines=[];if(options.note)lines.push(`# ${options.note}`);if(variable.description)lines.push(`# ${variable.description}`);if(variable.context)lines.push(`# Validation context: ${variable.context}`);if(variable.owner)lines.push(`# Owner: ${variable.owner}`);if(variable.setupInstructions)lines.push(`# Setup: ${variable.setupInstructions}`);if(variable.expiresAt)lines.push(`# Expires At: ${variable.expiresAt}`);if(variable.refreshInstructions)lines.push(`# Refresh Instructions: ${variable.refreshInstructions}`);if(variable.required)lines.push(`# Required: yes`);if(variable.purpose)lines.push(`# Purpose: ${variable.purpose}`);if(variable.legalBasis)lines.push(`# Legal Basis: ${variable.legalBasis}`);if(variable.retention)lines.push(`# Retention Policy: ${variable.retention}`);if(variable.dataResidency)lines.push(`# Data Residency: ${renderMetadataValue(variable.dataResidency)}`);if(variable.auditRequired)lines.push(`# Audit Required: yes`);for(const[metaKey,metaValue]of Object.entries(variable.metadata??{})){lines.push(`# ${humanizeKey(metaKey)}: ${renderMetadataValue(metaValue)}`);}const prefix=options.commented?"# ":"";lines.push(`${prefix}${variable.key}=${defaultLiteralFor(variable)??""}`,"");return lines}function groupByKey(contracts){const byKey=new Map;for(const contract of contracts){for(const variable of contract.variables){const list=byKey.get(variable.key)??[];list.push({contract,variable});byKey.set(variable.key,list);}}return byKey}function renderEnvExample(contracts,reconciliationHeader=[]){const sorted=[...contracts].sort((a,b)=>a.file.localeCompare(b.file));const active=sorted.filter(c=>c.active);const inactive=sorted.filter(c=>!c.active);const lines=["# AUTO-GENERATED EXAMPLE FILE.","# Copy to .env and fill in real values. Do not commit .env."];if(sorted.some(c=>c.variables.some(v=>v.context))){lines.push("# Validation context annotations describe when validation participates.","# They do not restrict access to values, and every variable below is","# still written to this file regardless of its context.");}if(reconciliationHeader.length>0)lines.push(...reconciliationHeader);lines.push("");const activeByKey=groupByKey(active);for(const key of [...activeByKey.keys()].sort()){const[first,...rest]=mustGet(activeByKey,key);lines.push(...renderVariableLines(first.variable));for(const dup of rest){lines.push(...renderVariableLines(dup.variable,{commented:true,note:`Also declared by "${dup.contract.contractName}" -- see "${first.contract.contractName}" above.`}));}}const inactiveByKey=groupByKey(inactive);for(const key of [...inactiveByKey.keys()].sort()){if(activeByKey.has(key))continue;const[first]=mustGet(inactiveByKey,key);lines.push(...renderVariableLines(first.variable,{commented:true,note:`Disabled -- feature "${first.contract.contractName}" is not active.`}));}return lines.join("\n").replace(/\n+$/,"\n")}function extractDeclaredVariables(source){const names=[];for(const line of source.split("\n")){const trimmed=line.trim();if(!trimmed||trimmed.startsWith("#"))continue;const match=/^([A-Za-z_][A-Za-z0-9_]*)=/.exec(trimmed);if(match?.[1])names.push(match[1]);}return names}function extractCommentedVariables(source){const names=[];for(const line of source.split("\n")){const trimmed=line.trim();if(!trimmed.startsWith("#"))continue;const withoutHash=trimmed.replace(/^#+\s*/,"");const match=/^([A-Za-z_][A-Za-z0-9_]*)=/.exec(withoutHash);if(match?.[1])names.push(match[1]);}return names}function computeReconciliation(contracts,existingContent){const activeKeys=new Set(contracts.filter(c=>c.active).flatMap(c=>c.variables.map(v=>v.key)));const knownKeys=new Set(contracts.flatMap(c=>c.variables.map(v=>v.key)));const oldLive=new Set(extractDeclaredVariables(existingContent));const oldKnown=new Set([...oldLive,...extractCommentedVariables(existingContent)]);const staleVariables=[...oldKnown].filter(key=>!knownKeys.has(key)).sort();const variablesToComment=[...oldLive].filter(key=>!activeKeys.has(key)&&knownKeys.has(key)).sort();const variablesToAdd=[...activeKeys].filter(key=>!oldLive.has(key)).sort();return {staleVariables,variablesToComment,variablesToAdd}}function renderReconciliationHeader(reconciliation){const lines=[];if(reconciliation.staleVariables.length>0){lines.push("#","# Remove the following variables (no longer declared by any feature):");for(const key of reconciliation.staleVariables)lines.push(`# - ${key}`);}if(reconciliation.variablesToComment.length>0){lines.push("#","# Comment the following variables (their feature is no longer active):");for(const key of reconciliation.variablesToComment)lines.push(`# - ${key}`);}if(reconciliation.variablesToAdd.length>0){lines.push("#","# Add the following variables (required by the current configuration):");for(const key of reconciliation.variablesToAdd)lines.push(`# - ${key}`);}return lines}async function writeEnvExample(contracts,location,fs,options={}){const onExisting=options.onExisting??"keep-sibling";let existingContent;try{existingContent=await fs.readFile(location,"utf8");}catch{}if(existingContent===void 0){const content2=renderEnvExample(contracts,[]);await fs.mkdir(path6__default.default.dirname(location),{recursive:true});await fs.writeFile(location,content2,"utf8");return {writtenPath:location,skippedExistingPath:void 0,staleVariables:[],variablesToComment:[],variablesToAdd:[]}}const reconciliation=computeReconciliation(contracts,existingContent);const{staleVariables,variablesToComment,variablesToAdd}=reconciliation;if(onExisting==="skip"){return {writtenPath:void 0,skippedExistingPath:location,staleVariables,variablesToComment,variablesToAdd}}const headerLines=onExisting==="keep-sibling"?renderReconciliationHeader(reconciliation):[];const content=renderEnvExample(contracts,headerLines);const writtenPath=onExisting==="overwrite"?location:`${location}.${Date.now()}`;const skippedExistingPath=onExisting==="overwrite"?void 0:location;await fs.mkdir(path6__default.default.dirname(writtenPath),{recursive:true});await fs.writeFile(writtenPath,content,"utf8");return {writtenPath,skippedExistingPath,staleVariables,variablesToComment,variablesToAdd}}function collectVariableNames(contracts){const names=new Set;for(const contract of contracts){for(const variable of contract.variables)names.add(variable.key);}return [...names]}function overrideVariable(variable,overrides){const override=overrides[variable.key];const date=override===void 0?void 0:parseIsoDate(override);return {...variable,expiresAt:date?override:variable.expiresAt,metadata:variable.metadata?{...variable.metadata}:variable.metadata}}function applyLiveExpirationOverrides(contracts,overrides){return contracts.map(contract=>({...contract,metadata:contract.metadata?{...contract.metadata}:contract.metadata,variables:contract.variables.map(variable=>overrideVariable(variable,overrides))}))}async function resolveLiveExpirationDates(contracts,liveExpirationDates){if(!liveExpirationDates)return contracts;const overrides=await liveExpirationDates(collectVariableNames(contracts));return deepFreeze(applyLiveExpirationOverrides(contracts,overrides))}var DEFAULT_EXPIRING_WITHIN_DAYS=30;var STANDARD_SENSITIVITY_LEVELS=new Set(["secret","credential","pii","config"]);function findNonstandardSensitivityLevels(contracts){const entries=[];for(const contract of contracts){if(contract.sensitivity!==void 0&&!STANDARD_SENSITIVITY_LEVELS.has(contract.sensitivity))entries.push({file:contract.file,exportName:contract.exportName,key:void 0,sensitivity:contract.sensitivity});for(const variable of [...contract.variables].sort((a,b)=>a.key.localeCompare(b.key))){if(variable.sensitivity===void 0)continue;if(STANDARD_SENSITIVITY_LEVELS.has(variable.sensitivity))continue;entries.push({file:contract.file,exportName:contract.exportName,key:variable.key,sensitivity:variable.sensitivity});}}return entries}function relativizeRef(root,ref){return {...ref,file:displayPath(root,ref.file)}}function computeDocumentation(root,linkResult,expiringWithinDays,generatedAt){const{contracts}=linkResult;return {contractSummaries:contracts.map(contract=>summarizeContract(contract,root)),catalog:buildCatalog(contracts),documentation:{undocumentedContracts:linkResult.undocumentedContracts,undocumentedVariables:linkResult.undocumentedVariables,staleDocEntries:linkResult.staleDocEntries,expiringSoon:computeExpiringEntries(contracts,expiringWithinDays,generatedAt),nonstandardSensitivityLevels:findNonstandardSensitivityLevels(contracts),unresolvedLinks:linkResult.unresolvedLinks},contractModelContracts:buildContractModel(contracts,root).contracts}}async function writeDocumentation(docsPath,envExamplePath,root,contracts,contractModelContracts,documentation,expiringWithinDays,generatedAt,fs,envExampleOnExisting){let previousContent;try{previousContent=await fs.readFile(docsPath,"utf8");}catch{}const docsSource=renderDocs(contractModelContracts,{expiringWithinDays,undocumentedContracts:documentation.undocumentedContracts.map(ref=>relativizeRef(root,ref)),undocumentedVariables:documentation.undocumentedVariables.map(ref=>relativizeRef(root,ref)),generatedAt,previousContent});await fs.mkdir(path6__default.default.dirname(docsPath),{recursive:true});await fs.writeFile(docsPath,docsSource,"utf8");const envExample=envExamplePath?await writeEnvExample(contracts,envExamplePath,fs,{onExisting:envExampleOnExisting}):void 0;return {envExample}}async function generateDocumentation(options){const root=path6__default.default.resolve(options.root??process.cwd());const include=options.include??defaultInclude();const exclude=options.exclude??defaultExclude();const packages=options.packages??[];const expiringWithinDays=options.expiringWithinDays??DEFAULT_EXPIRING_WITHIN_DAYS;const docsLocationResult=resolveWithinRoot(root,options.location,"location","generateDocumentation");if(!docsLocationResult.ok)throw new EnvDocumentationGenerationError([docsLocationResult.issue]);const docsPath=docsLocationResult.resolved;let envExamplePath;if(options.envExample){const envExampleResult=resolveWithinRoot(root,options.envExample.location,"envExample.location","generateDocumentation");if(!envExampleResult.ok)throw new EnvDocumentationGenerationError([envExampleResult.issue]);envExamplePath=envExampleResult.resolved;}const{linkResult,packageWarnings,tsconfigWarnings}=await assembleProject({fs:options.fs,root,include,exclude,packages,tsconfig:options.tsconfig});const docsContracts=await resolveLiveExpirationDates(linkResult.contracts,options.liveExpirationDates);const generatedAt=new Date;const computed=computeDocumentation(root,{...linkResult,contracts:docsContracts},expiringWithinDays,generatedAt);const{envExample}=await writeDocumentation(docsPath,envExamplePath,root,docsContracts,computed.contractModelContracts,computed.documentation,expiringWithinDays,generatedAt,options.fs,options.envExample?.onExisting);return {docsPath,envExample,contracts:computed.contractSummaries,catalog:computed.catalog,parseWarnings:[...packageWarnings,...tsconfigWarnings,...linkResult.warnings],documentation:computed.documentation}}var LIFECYCLE_MODEL_SCHEMA_VERSION=2;function hasLifecycleData(variable){return variable.expiresAt!==void 0||variable.refreshInstructions!==void 0||variable.deprecated!==void 0||variable.removeBy!==void 0||variable.renamedFrom!==void 0||variable.retention!==void 0}function buildLifecycleModel(contracts,expiringWithinDays,now,root){const modelContracts=[];for(const contract of contracts){const variables=[...contract.variables].filter(hasLifecycleData).sort((a,b)=>a.key.localeCompare(b.key)).map(variable=>({key:variable.key,expiresAt:variable.expiresAt,refreshInstructions:variable.refreshInstructions,deprecated:variable.deprecated,deprecatedReason:variable.deprecatedReason,removeBy:variable.removeBy,renamedFrom:variable.renamedFrom,retention:variable.retention}));const hasContractLevelData=contract.expiresAt!==void 0||contract.deprecated!==void 0||contract.deprecatedReason!==void 0||contract.retention!==void 0;if(!hasContractLevelData&&variables.length===0)continue;modelContracts.push({file:displayPath(root,contract.file),exportName:contract.exportName,contractName:contract.contractName,expiresAt:contract.expiresAt,deprecated:contract.deprecated,deprecatedReason:contract.deprecatedReason,retention:contract.retention,variables});}modelContracts.sort(byContractIdentity);return {schemaVersion:LIFECYCLE_MODEL_SCHEMA_VERSION,contracts:modelContracts,expiring:computeExpiringEntries(contracts,expiringWithinDays,now).map(entry=>({...entry,file:displayPath(root,entry.file)}))}}var OWNERSHIP_MODEL_SCHEMA_VERSION=1;function buildOwnershipModel(contracts,root){const modelContracts=contracts.map(contract=>({file:displayPath(root,contract.file),exportName:contract.exportName,contractName:contract.contractName,owner:contract.owner,variables:[...contract.variables].sort((a,b)=>a.key.localeCompare(b.key)).map(variable=>({key:variable.key,owner:effectiveOwner(contract,variable)}))}));modelContracts.sort(byContractIdentity);const unownedContracts=modelContracts.filter(c=>c.owner===void 0).map(({file,exportName})=>({file,exportName}));const unownedVariables=[];for(const contract of modelContracts){for(const variable of contract.variables){if(variable.owner===void 0){unownedVariables.push({file:contract.file,exportName:contract.exportName,key:variable.key});}}}return {schemaVersion:OWNERSHIP_MODEL_SCHEMA_VERSION,contracts:modelContracts,unownedContracts,unownedVariables}}function readToolVersion(){return "0.2.0"}async function generateEvidenceModel(options){const root=path6__default.default.resolve(options.root??process.cwd());const include=options.include??defaultInclude();const exclude=options.exclude??defaultExclude();const packages=options.packages??[];const expiringWithinDays=options.expiringWithinDays??DEFAULT_EXPIRING_WITHIN_DAYS;let snapshotPath;if(options.previousSnapshotLocation!==void 0){const result=resolveWithinRoot(root,options.previousSnapshotLocation,"previousSnapshotLocation","generateEvidenceModel");if(!result.ok)throw new EnvProjectGenerationError([result.issue]);snapshotPath=result.resolved;}const assembled=await assembleProject({fs:options.fs,root,include,exclude,packages,tsconfig:options.tsconfig});const{readFileCached,linkResult,context,origins,packageWarnings,tsconfigWarnings}=assembled;const generatedAt=new Date;const activeContracts=linkResult.contracts.filter(c=>c.active);const lifecycleContracts=options.liveExpirationDates?await resolveLiveExpirationDates(linkResult.contracts,options.liveExpirationDates):linkResult.contracts;const contract=buildContractModel(linkResult.contracts,root);const ownership=buildOwnershipModel(linkResult.contracts,root);const lifecycle=buildLifecycleModel(lifecycleContracts,expiringWithinDays,generatedAt,root);const evidenceChanges=snapshotPath?await computeEvidenceChanges(root,snapshotPath,activeContracts,contract.contracts,readFileCached,options.fs):void 0;const change=buildChangeModel(evidenceChanges?.report??{addedContracts:[],removedContracts:[],addedVariables:[],removedVariables:[],updatedContracts:[],updatedVariables:[]},linkResult.contracts,root);const{scanFiles,scannedSurfaces}=await computeScanSurface(root,exclude,origins,options.fs);const dependency=await buildDependencyModel(linkResult.contracts,scanFiles,readFileCached,context,root,scannedSurfaces,evidenceChanges?.dynamicAccessAcknowledgments);const documentationComputed=computeDocumentation(root,{...linkResult,contracts:lifecycleContracts},expiringWithinDays,generatedAt);const usageComputed=await computeUsage(root,linkResult.contracts,scanFiles,readFileCached,context,[...packageWarnings,...tsconfigWarnings,...linkResult.warnings],scannedSurfaces,evidenceChanges?.dynamicAccessAcknowledgments);const finding=buildFindingModel({root,compatibilityIssues:detectCompatibilityIssues(activeContracts),exclusiveGroupIssues:detectExclusiveGroupIssues(activeContracts),documentation:documentationComputed.documentation,abandonedContracts:usageComputed.result.abandonedContracts,unresolvedConsumers:usageComputed.result.unresolvedConsumers,unconsumedOwnedVariables:usageComputed.result.unconsumedOwnedVariables,indeterminateOwnership:usageComputed.result.indeterminate,dynamicAccessCitationProblems:evidenceChanges?.dynamicAccessCitationProblems??[]});const commit=options.commit?await options.commit():void 0;return deepFreeze({schemaVersion:EVIDENCE_MODEL_SCHEMA_VERSION,provenance:{generatedAt:generatedAt.toISOString(),toolVersion:readToolVersion(),commit},contract,dependency,ownership,lifecycle,finding,change})}async function computeSourceFingerprint(options){const{fs,root,include,exclude,packages}=options;const localSchemaFiles=await discoverSchemaFiles({fs,root,include,exclude});const packageCache=new Map;const{files:packageFiles,origins}=await resolveAllowlistedPackages(packages,root,packageCache,fs);const schemaFiles=await mergeLocalAndPackageFiles(localSchemaFiles,packageFiles.map(f=>f.file),fs);const{scanFiles}=await computeScanSurface(root,exclude,origins,fs);const allFiles=[...new Set([...schemaFiles,...scanFiles])].sort();const hash=crypto__default.default.createHash("sha256");hash.update(readToolVersion());for(const file of allFiles){hash.update(displayPath(root,file));try{hash.update(await fs.readFile(file,"utf8"));}catch{hash.update("(unreadable)");}}return hash.digest("hex")}function fingerprintPathFor(evidencePath){return `${evidencePath}.fingerprint`}async function writeEvidenceFingerprint(evidencePath,fingerprint,fs){await fs.writeFile(fingerprintPathFor(evidencePath),`${fingerprint}
23
+ `,"utf8");}async function getEvidenceModel(options){const root=path6__default.default.resolve(options.root??process.cwd());const include=options.include??defaultInclude();const exclude=options.exclude??defaultExclude();const packages=options.packages??[];const evidencePath=path6__default.default.resolve(root,options.location);const fingerprintPath=fingerprintPathFor(evidencePath);const recompute=async missReason=>({evidence:await generateEvidenceModel(options),source:"miss",missReason});let currentFingerprint;try{currentFingerprint=await computeSourceFingerprint({fs:options.fs,root,include,exclude,packages});}catch(error){return recompute(`could not compute a source fingerprint (${error instanceof Error?error.message:String(error)})`)}let storedFingerprint;try{storedFingerprint=(await options.fs.readFile(fingerprintPath,"utf8")).trim();}catch{return recompute(`no fingerprint sidecar found at ${fingerprintPath}`)}if(storedFingerprint!==currentFingerprint){return recompute("source fingerprint changed since the committed evidence artifact was last generated")}const read=await readEvidenceSnapshot(evidencePath,options.fs);if(read.status!=="ok"){return recompute(read.status==="missing"?`no evidence artifact found at ${evidencePath}`:read.status==="invalid-json"?`evidence artifact at ${evidencePath} could not be parsed as JSON (${read.detail})`:`evidence artifact at ${evidencePath} has an unrecognized schemaVersion (${JSON.stringify(read.foundVersion)})`)}return {evidence:read.snapshot,source:"hit",missReason:void 0}}function escalatedFindings(findings,family){return findings.filter(f=>f.family===family&&f.severity==="warning").map(f=>({severity:"error",variable:findingSubject(f),files:findingFiles(f),reason:`[${f.code}] ${f.message}`}))}function findingSubject(finding){const location=finding.location;if(location.model==="change")return `(artifact) ${location.path}`;if(location.variable!==void 0)return location.variable;const name=location.model==="ownership"?location.contractName:location.exportName;return `(contract) ${name??"unknown"}`}function findingFiles(finding){const location=finding.location;if(location.model==="change")return [location.path];return location.file===void 0?[]:[location.file]}async function computeArtifacts(options){const root=path6__default.default.resolve(options.root??process.cwd());const include=options.include??defaultInclude();const exclude=options.exclude??defaultExclude();const packages=options.packages??[];const manifestOptions=options.manifest===false?void 0:options.manifest;const docsOptions=options.docs===false?void 0:options.docs;const usageOptions=options.usage===false?void 0:options.usage;const evidenceOptions=options.evidence===false?void 0:options.evidence;const pathIssues=[];let manifestOutputPath;if(manifestOptions){const result=resolveWithinRoot(root,manifestOptions.location,"manifest.location","generateEnvArtifacts");if(result.ok)manifestOutputPath=result.resolved;else pathIssues.push(result.issue);}let docsPath;let envExamplePath;if(docsOptions){const result=resolveWithinRoot(root,docsOptions.location,"docs.location","generateEnvArtifacts");if(result.ok)docsPath=result.resolved;else pathIssues.push(result.issue);if(docsOptions.envExample){const exampleResult=resolveWithinRoot(root,docsOptions.envExample.location,"docs.envExample.location","generateEnvArtifacts");if(exampleResult.ok)envExamplePath=exampleResult.resolved;else pathIssues.push(exampleResult.issue);}}let usageReportPath;if(usageOptions?.report){const result=resolveWithinRoot(root,usageOptions.report.location,"usage.report.location","generateEnvArtifacts");if(result.ok)usageReportPath=result.resolved;else pathIssues.push(result.issue);}let evidencePath;if(evidenceOptions){const result=resolveWithinRoot(root,evidenceOptions.location,"evidence.location","generateEnvArtifacts");if(result.ok)evidencePath=result.resolved;else pathIssues.push(result.issue);}if(pathIssues.length>0)throw new EnvProjectGenerationError(pathIssues);const{readFileCached,linkResult,context,origins,packageWarnings,tsconfigWarnings}=await assembleProject({fs:options.fs,root,include,exclude,packages,tsconfig:options.tsconfig});const generatedAt=new Date;const activeContracts=linkResult.contracts.filter(c=>c.active);const liveExpirationContracts=options.liveExpirationDates?await resolveLiveExpirationDates(linkResult.contracts,options.liveExpirationDates):linkResult.contracts;const docsContracts=docsOptions?liveExpirationContracts:linkResult.contracts;const blocking=[];const manifestComputed=manifestOptions?computeManifest(root,linkResult,manifestOptions.onIncompatibility??"warn"):void 0;if(manifestComputed)blocking.push(...manifestComputed.blocking);const docsComputed=computeDocumentation(root,{...linkResult,contracts:liveExpirationContracts},docsOptions?.expiringWithinDays??DEFAULT_EXPIRING_WITHIN_DAYS,generatedAt);const{scanFiles,scannedSurfaces}=await computeScanSurface(root,exclude,origins,options.fs);const contract=buildContractModel(linkResult.contracts,root);const evidenceChanges=evidencePath?await computeEvidenceChanges(root,evidencePath,activeContracts,contract.contracts,readFileCached,options.fs):void 0;const usageComputed=await computeUsage(root,linkResult.contracts,scanFiles,readFileCached,context,[...packageWarnings,...tsconfigWarnings,...linkResult.warnings],scannedSurfaces,evidenceChanges?.dynamicAccessAcknowledgments);const ownership=buildOwnershipModel(linkResult.contracts,root);const lifecycle=buildLifecycleModel(liveExpirationContracts,docsOptions?.expiringWithinDays??DEFAULT_EXPIRING_WITHIN_DAYS,generatedAt,root);const change=buildChangeModel(evidenceChanges?.report??{addedContracts:[],removedContracts:[],addedVariables:[],removedVariables:[],updatedContracts:[],updatedVariables:[]},linkResult.contracts,root);const dependency=await buildDependencyModel(linkResult.contracts,scanFiles,readFileCached,context,root,scannedSurfaces,evidenceChanges?.dynamicAccessAcknowledgments);const finding=buildFindingModel({root,compatibilityIssues:[...detectCompatibilityIssues(activeContracts),...detectDuplicateVariableShapes(activeContracts)],exclusiveGroupIssues:detectExclusiveGroupIssues(activeContracts),documentation:docsComputed.documentation,abandonedContracts:usageComputed.result.abandonedContracts,unresolvedConsumers:usageComputed.result.unresolvedConsumers,unconsumedOwnedVariables:usageComputed.result.unconsumedOwnedVariables,indeterminateOwnership:usageComputed.result.indeterminate,dynamicAccessCitationProblems:evidenceChanges?.dynamicAccessCitationProblems??[]});if(options.onUndocumented==="throw")blocking.push(...escalatedFindings(finding.findings,"documentation"));if(options.onOwnershipIssue==="throw")blocking.push(...escalatedFindings(finding.findings,"ownership"));const evidence=deepFreeze({schemaVersion:EVIDENCE_MODEL_SCHEMA_VERSION,provenance:{generatedAt:generatedAt.toISOString(),toolVersion:readToolVersion(),commit:void 0},contract,dependency,ownership,lifecycle,finding,change});return {root,manifestOptions,manifestOutputPath,manifestComputed,docsOptions,docsPath,envExamplePath,docsComputed,docsContracts,usageOptions,usageReportPath,usageComputed,evidencePath,evidence,blocking,packageWarnings:[...packageWarnings,...tsconfigWarnings],linkWarnings:linkResult.warnings,generatedAt}}async function generateEnvArtifacts(options){const computed=await computeArtifacts(options);const{manifestOptions,manifestOutputPath,manifestComputed,docsOptions,docsPath,envExamplePath,docsComputed,docsContracts,usageOptions,usageReportPath,usageComputed,evidencePath,evidence,blocking,packageWarnings,linkWarnings,root,generatedAt}=computed;if(blocking.length>0)throw new EnvProjectGenerationError(blocking);let manifestResult;if(manifestOptions&&manifestComputed&&manifestOutputPath){await writeManifest(manifestOutputPath,manifestComputed.activeContracts,options.fs);manifestResult={outputPath:manifestOutputPath,contracts:manifestComputed.contractSummaries,warnings:manifestComputed.warnings,parseWarnings:[...packageWarnings,...linkWarnings]};}let docsResult;if(docsOptions&&docsPath){const{envExample}=await writeDocumentation(docsPath,envExamplePath,root,docsContracts,docsComputed.contractModelContracts,docsComputed.documentation,docsOptions.expiringWithinDays??DEFAULT_EXPIRING_WITHIN_DAYS,generatedAt,options.fs,docsOptions.envExample?.onExisting);docsResult={docsPath,envExample,contracts:docsComputed.contractSummaries,catalog:docsComputed.catalog,parseWarnings:[...packageWarnings,...linkWarnings],documentation:docsComputed.documentation};}let usageResult;if(usageOptions){if(usageReportPath)await writeUsageReport(usageReportPath,usageComputed.result,options.fs);usageResult={reportPath:usageReportPath,...usageComputed.result};}if(evidencePath){await writeEvidenceSnapshot(evidencePath,evidence,options.fs);const fingerprint=await computeSourceFingerprint({fs:options.fs,root,include:options.include??defaultInclude(),exclude:options.exclude??defaultExclude(),packages:options.packages??[]});await writeEvidenceFingerprint(evidencePath,fingerprint,options.fs);}return {manifest:manifestResult,docs:docsResult,usage:usageResult,evidence}}async function readIfExists(filePath,fs){try{return await fs.readFile(filePath,"utf8")}catch{return void 0}}function normalizeEvidenceJsonForComparison(text){try{const parsed=JSON.parse(text);return JSON.stringify(normalizeEvidenceSnapshotForComparison(parsed),null,2)}catch{return text}}async function compareTextArtifact(artifact,filePath,expected,fs,normalize=s=>s){const actual=await readIfExists(filePath,fs);if(actual===void 0)return {artifact,path:filePath,status:"missing",detail:"not yet generated"};if(normalize(actual)===normalize(expected))return {artifact,path:filePath,status:"ok"};return {artifact,path:filePath,status:"stale",detail:"generated content differs from what's committed"}}async function checkEnvArtifacts(options){const c=await computeArtifacts(options);if(c.blocking.length>0)throw new EnvProjectGenerationError(c.blocking);const findings=[];if(c.manifestOptions&&c.manifestComputed&&c.manifestOutputPath){const expected=renderManifest(c.manifestComputed.activeContracts,c.manifestOutputPath);findings.push(await compareTextArtifact("manifest",c.manifestOutputPath,expected,options.fs));}if(c.docsOptions&&c.docsPath){const previousContent=await readIfExists(c.docsPath,options.fs);const expected=renderDocs(c.docsComputed.contractModelContracts,{expiringWithinDays:c.docsOptions.expiringWithinDays??DEFAULT_EXPIRING_WITHIN_DAYS,undocumentedContracts:c.docsComputed.documentation.undocumentedContracts.map(ref=>relativizeRef(c.root,ref)),undocumentedVariables:c.docsComputed.documentation.undocumentedVariables.map(ref=>relativizeRef(c.root,ref)),generatedAt:c.generatedAt,previousContent});findings.push(await compareTextArtifact("docs",c.docsPath,expected,options.fs,normalizeDocsForComparison));if(c.docsOptions.envExample&&c.envExamplePath){const existing=await readIfExists(c.envExamplePath,options.fs);if(existing===void 0){findings.push({artifact:"envExample",path:c.envExamplePath,status:"missing",detail:"not yet generated"});}else {const{staleVariables,variablesToComment,variablesToAdd}=computeReconciliation(c.docsContracts,existing);const driftCount=staleVariables.length+variablesToComment.length+variablesToAdd.length;findings.push({artifact:"envExample",path:c.envExamplePath,status:driftCount>0?"stale":"ok",detail:driftCount>0?`${staleVariables.length} stale, ${variablesToComment.length} to comment, ${variablesToAdd.length} to add`:void 0});}}}if(c.usageOptions&&c.usageReportPath){const expected=renderUsageReport(c.usageComputed.result);findings.push(await compareTextArtifact("usage",c.usageReportPath,expected,options.fs));}if(c.evidencePath){const expected=JSON.stringify(c.evidence,null,2);findings.push(await compareTextArtifact("evidence",c.evidencePath,expected,options.fs,normalizeEvidenceJsonForComparison));}return {ok:findings.every(f=>f.status==="ok"),findings}}function sarifLevel(severity){return severity==="info"?"note":severity}function sarifLocations(finding){const location=finding.location;const uri=location.model==="change"?location.path:location.file;if(uri===void 0)return void 0;const position=location.model==="change"?void 0:location.position;return [{physicalLocation:{artifactLocation:{uri},...position===void 0?{}:{region:{startLine:position.line,startColumn:position.column}}}}]}function buildSarifLog(findingModel){const ruleIds=[...new Set(findingModel.findings.map(f=>f.code))].sort();return {$schema:"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/Schemata/sarif-schema-2.1.0.json",version:"2.1.0",runs:[{tool:{driver:{name:"env-cap",informationUri:"https://github.com/maverickcer/env-cap#readme",version:readToolVersion(),rules:ruleIds.map(id=>({id}))}},results:findingModel.findings.map(finding=>({ruleId:finding.code,level:sarifLevel(finding.severity),message:{text:finding.message},locations:sarifLocations(finding)}))}]}}
24
+
25
+ exports.CHANGE_MODEL_SCHEMA_VERSION = CHANGE_MODEL_SCHEMA_VERSION;
26
+ exports.CONTRACT_MODEL_SCHEMA_VERSION = CONTRACT_MODEL_SCHEMA_VERSION;
27
+ exports.DEPENDENCY_MODEL_SCHEMA_VERSION = DEPENDENCY_MODEL_SCHEMA_VERSION;
28
+ exports.EVIDENCE_MODEL_SCHEMA_VERSION = EVIDENCE_MODEL_SCHEMA_VERSION;
29
+ exports.EnvDocumentationGenerationError = EnvDocumentationGenerationError;
30
+ exports.EnvManifestGenerationError = EnvManifestGenerationError;
31
+ exports.EnvProjectGenerationError = EnvProjectGenerationError;
32
+ exports.EnvUsageAnalysisError = EnvUsageAnalysisError;
33
+ exports.FINDING_MODEL_SCHEMA_VERSION = FINDING_MODEL_SCHEMA_VERSION;
34
+ exports.LIFECYCLE_MODEL_SCHEMA_VERSION = LIFECYCLE_MODEL_SCHEMA_VERSION;
35
+ exports.OWNERSHIP_MODEL_SCHEMA_VERSION = OWNERSHIP_MODEL_SCHEMA_VERSION;
36
+ exports.applyLiveExpirationOverrides = applyLiveExpirationOverrides;
37
+ exports.buildChangeModel = buildChangeModel;
38
+ exports.buildContractModel = buildContractModel;
39
+ exports.buildDependencyModel = buildDependencyModel;
40
+ exports.buildFindingModel = buildFindingModel;
41
+ exports.buildLifecycleModel = buildLifecycleModel;
42
+ exports.buildOwnershipModel = buildOwnershipModel;
43
+ exports.buildSarifLog = buildSarifLog;
44
+ exports.checkEnvArtifacts = checkEnvArtifacts;
45
+ exports.collectVariableNames = collectVariableNames;
46
+ exports.computeExpiringEntries = computeExpiringEntries;
47
+ exports.computeReconciliation = computeReconciliation;
48
+ exports.computeSecurityReviewCounters = computeSecurityReviewCounters;
49
+ exports.computeSourceFingerprint = computeSourceFingerprint;
50
+ exports.configurationReference = configurationReference;
51
+ exports.deepFreeze = deepFreeze;
52
+ exports.detectCompatibilityIssues = detectCompatibilityIssues;
53
+ exports.detectDuplicateVariableShapes = detectDuplicateVariableShapes;
54
+ exports.detectExclusiveGroupIssues = detectExclusiveGroupIssues;
55
+ exports.discoverSchemaFiles = discoverSchemaFiles;
56
+ exports.displayPath = displayPath;
57
+ exports.effectiveOwner = effectiveOwner;
58
+ exports.expiringSoonReport = expiringSoonReport;
59
+ exports.extractCommentedVariables = extractCommentedVariables;
60
+ exports.extractContractDocs = extractContractDocs;
61
+ exports.extractDeclaredVariables = extractDeclaredVariables;
62
+ exports.extractPreviouslyDocumentedKeys = extractPreviouslyDocumentedKeys;
63
+ exports.extractSchemaVariables = extractSchemaVariables;
64
+ exports.generateDocumentation = generateDocumentation;
65
+ exports.generateEnvArtifacts = generateEnvArtifacts;
66
+ exports.generateEnvManifest = generateEnvManifest;
67
+ exports.generateEvidenceModel = generateEvidenceModel;
68
+ exports.generateUsageReport = generateUsageReport;
69
+ exports.getEvidenceModel = getEvidenceModel;
70
+ exports.groupVariablesByOwner = groupVariablesByOwner;
71
+ exports.linkFiles = linkFiles;
72
+ exports.ownershipSummary = ownershipSummary;
73
+ exports.parseSchemaFile = parseSchemaFile;
74
+ exports.readToolVersion = readToolVersion;
75
+ exports.renderDocs = renderDocs;
76
+ exports.renderEnvExample = renderEnvExample;
77
+ exports.renderManifest = renderManifest;
78
+ exports.renderUsageReport = renderUsageReport;
79
+ exports.resolveLiveExpirationDates = resolveLiveExpirationDates;
80
+ exports.resolveRelativeImport = resolveRelativeImport;
81
+ exports.writeEnvExample = writeEnvExample;
82
+ //# sourceMappingURL=build.cjs.map
83
+ //# sourceMappingURL=build.cjs.map