archprint 0.1.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 (276) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/LICENSE +21 -0
  3. package/README.md +268 -0
  4. package/dist/cli/codeframe.d.ts +6 -0
  5. package/dist/cli/codeframe.d.ts.map +1 -0
  6. package/dist/cli/codeframe.js +22 -0
  7. package/dist/cli/codeframe.js.map +1 -0
  8. package/dist/cli/generate.d.ts +37 -0
  9. package/dist/cli/generate.d.ts.map +1 -0
  10. package/dist/cli/generate.js +259 -0
  11. package/dist/cli/generate.js.map +1 -0
  12. package/dist/cli/init.d.ts +21 -0
  13. package/dist/cli/init.d.ts.map +1 -0
  14. package/dist/cli/init.js +19 -0
  15. package/dist/cli/init.js.map +1 -0
  16. package/dist/cli/outputs-manifest.d.ts +10 -0
  17. package/dist/cli/outputs-manifest.d.ts.map +1 -0
  18. package/dist/cli/outputs-manifest.js +53 -0
  19. package/dist/cli/outputs-manifest.js.map +1 -0
  20. package/dist/cli/program.d.ts +4 -0
  21. package/dist/cli/program.d.ts.map +1 -0
  22. package/dist/cli/program.js +316 -0
  23. package/dist/cli/program.js.map +1 -0
  24. package/dist/cli/recommend.d.ts +18 -0
  25. package/dist/cli/recommend.d.ts.map +1 -0
  26. package/dist/cli/recommend.js +193 -0
  27. package/dist/cli/recommend.js.map +1 -0
  28. package/dist/cli/report.d.ts +8 -0
  29. package/dist/cli/report.d.ts.map +1 -0
  30. package/dist/cli/report.js +482 -0
  31. package/dist/cli/report.js.map +1 -0
  32. package/dist/cli/rule-guidance.d.ts +6 -0
  33. package/dist/cli/rule-guidance.d.ts.map +1 -0
  34. package/dist/cli/rule-guidance.js +22 -0
  35. package/dist/cli/rule-guidance.js.map +1 -0
  36. package/dist/cli/scan.d.ts +54 -0
  37. package/dist/cli/scan.d.ts.map +1 -0
  38. package/dist/cli/scan.js +103 -0
  39. package/dist/cli/scan.js.map +1 -0
  40. package/dist/cli/summary.d.ts +18 -0
  41. package/dist/cli/summary.d.ts.map +1 -0
  42. package/dist/cli/summary.js +59 -0
  43. package/dist/cli/summary.js.map +1 -0
  44. package/dist/cli/wiring.d.ts +42 -0
  45. package/dist/cli/wiring.d.ts.map +1 -0
  46. package/dist/cli/wiring.js +246 -0
  47. package/dist/cli/wiring.js.map +1 -0
  48. package/dist/cli.d.ts +3 -0
  49. package/dist/cli.d.ts.map +1 -0
  50. package/dist/cli.js +13 -0
  51. package/dist/cli.js.map +1 -0
  52. package/dist/data/adoption-catalog.d.ts +16 -0
  53. package/dist/data/adoption-catalog.d.ts.map +1 -0
  54. package/dist/data/adoption-catalog.js +254 -0
  55. package/dist/data/adoption-catalog.js.map +1 -0
  56. package/dist/detector/app-isolation-detector.d.ts +22 -0
  57. package/dist/detector/app-isolation-detector.d.ts.map +1 -0
  58. package/dist/detector/app-isolation-detector.js +17 -0
  59. package/dist/detector/app-isolation-detector.js.map +1 -0
  60. package/dist/detector/confidence-gate.d.ts +33 -0
  61. package/dist/detector/confidence-gate.d.ts.map +1 -0
  62. package/dist/detector/confidence-gate.js +62 -0
  63. package/dist/detector/confidence-gate.js.map +1 -0
  64. package/dist/detector/console-isolation-detector.d.ts +17 -0
  65. package/dist/detector/console-isolation-detector.d.ts.map +1 -0
  66. package/dist/detector/console-isolation-detector.js +23 -0
  67. package/dist/detector/console-isolation-detector.js.map +1 -0
  68. package/dist/detector/cycle-detector.d.ts +18 -0
  69. package/dist/detector/cycle-detector.d.ts.map +1 -0
  70. package/dist/detector/cycle-detector.js +34 -0
  71. package/dist/detector/cycle-detector.js.map +1 -0
  72. package/dist/detector/deep-relative-detector.d.ts +17 -0
  73. package/dist/detector/deep-relative-detector.d.ts.map +1 -0
  74. package/dist/detector/deep-relative-detector.js +43 -0
  75. package/dist/detector/deep-relative-detector.js.map +1 -0
  76. package/dist/detector/dependency-internals-detector.d.ts +17 -0
  77. package/dist/detector/dependency-internals-detector.d.ts.map +1 -0
  78. package/dist/detector/dependency-internals-detector.js +65 -0
  79. package/dist/detector/dependency-internals-detector.js.map +1 -0
  80. package/dist/detector/entry-purity-detector.d.ts +19 -0
  81. package/dist/detector/entry-purity-detector.d.ts.map +1 -0
  82. package/dist/detector/entry-purity-detector.js +32 -0
  83. package/dist/detector/entry-purity-detector.js.map +1 -0
  84. package/dist/detector/env-access-detector.d.ts +17 -0
  85. package/dist/detector/env-access-detector.d.ts.map +1 -0
  86. package/dist/detector/env-access-detector.js +25 -0
  87. package/dist/detector/env-access-detector.js.map +1 -0
  88. package/dist/detector/family-maturity.d.ts +3 -0
  89. package/dist/detector/family-maturity.d.ts.map +1 -0
  90. package/dist/detector/family-maturity.js +12 -0
  91. package/dist/detector/family-maturity.js.map +1 -0
  92. package/dist/detector/feature-slice-detector.d.ts +22 -0
  93. package/dist/detector/feature-slice-detector.d.ts.map +1 -0
  94. package/dist/detector/feature-slice-detector.js +17 -0
  95. package/dist/detector/feature-slice-detector.js.map +1 -0
  96. package/dist/detector/layer-detector.d.ts +37 -0
  97. package/dist/detector/layer-detector.d.ts.map +1 -0
  98. package/dist/detector/layer-detector.js +165 -0
  99. package/dist/detector/layer-detector.js.map +1 -0
  100. package/dist/detector/marker-inference.d.ts +21 -0
  101. package/dist/detector/marker-inference.d.ts.map +1 -0
  102. package/dist/detector/marker-inference.js +236 -0
  103. package/dist/detector/marker-inference.js.map +1 -0
  104. package/dist/detector/orphan-detector.d.ts +12 -0
  105. package/dist/detector/orphan-detector.d.ts.map +1 -0
  106. package/dist/detector/orphan-detector.js +24 -0
  107. package/dist/detector/orphan-detector.js.map +1 -0
  108. package/dist/detector/pattern-detector.d.ts +48 -0
  109. package/dist/detector/pattern-detector.d.ts.map +1 -0
  110. package/dist/detector/pattern-detector.js +139 -0
  111. package/dist/detector/pattern-detector.js.map +1 -0
  112. package/dist/detector/phantom-dependency-detector.d.ts +18 -0
  113. package/dist/detector/phantom-dependency-detector.d.ts.map +1 -0
  114. package/dist/detector/phantom-dependency-detector.js +91 -0
  115. package/dist/detector/phantom-dependency-detector.js.map +1 -0
  116. package/dist/detector/public-api-detector.d.ts +23 -0
  117. package/dist/detector/public-api-detector.d.ts.map +1 -0
  118. package/dist/detector/public-api-detector.js +87 -0
  119. package/dist/detector/public-api-detector.js.map +1 -0
  120. package/dist/detector/reachability.d.ts +13 -0
  121. package/dist/detector/reachability.d.ts.map +1 -0
  122. package/dist/detector/reachability.js +64 -0
  123. package/dist/detector/reachability.js.map +1 -0
  124. package/dist/detector/role-layering-detector.d.ts +28 -0
  125. package/dist/detector/role-layering-detector.d.ts.map +1 -0
  126. package/dist/detector/role-layering-detector.js +75 -0
  127. package/dist/detector/role-layering-detector.js.map +1 -0
  128. package/dist/detector/self-consistency.d.ts +7 -0
  129. package/dist/detector/self-consistency.d.ts.map +1 -0
  130. package/dist/detector/self-consistency.js +65 -0
  131. package/dist/detector/self-consistency.js.map +1 -0
  132. package/dist/detector/server-client-detector.d.ts +19 -0
  133. package/dist/detector/server-client-detector.d.ts.map +1 -0
  134. package/dist/detector/server-client-detector.js +51 -0
  135. package/dist/detector/server-client-detector.js.map +1 -0
  136. package/dist/detector/sibling-isolation.d.ts +23 -0
  137. package/dist/detector/sibling-isolation.d.ts.map +1 -0
  138. package/dist/detector/sibling-isolation.js +76 -0
  139. package/dist/detector/sibling-isolation.js.map +1 -0
  140. package/dist/detector/stories-isolation-detector.d.ts +19 -0
  141. package/dist/detector/stories-isolation-detector.d.ts.map +1 -0
  142. package/dist/detector/stories-isolation-detector.js +31 -0
  143. package/dist/detector/stories-isolation-detector.js.map +1 -0
  144. package/dist/detector/test-isolation-detector.d.ts +18 -0
  145. package/dist/detector/test-isolation-detector.d.ts.map +1 -0
  146. package/dist/detector/test-isolation-detector.js +34 -0
  147. package/dist/detector/test-isolation-detector.js.map +1 -0
  148. package/dist/detector/ui-data-isolation-detector.d.ts +19 -0
  149. package/dist/detector/ui-data-isolation-detector.d.ts.map +1 -0
  150. package/dist/detector/ui-data-isolation-detector.js +41 -0
  151. package/dist/detector/ui-data-isolation-detector.js.map +1 -0
  152. package/dist/detector/workspace-package-detector.d.ts +19 -0
  153. package/dist/detector/workspace-package-detector.d.ts.map +1 -0
  154. package/dist/detector/workspace-package-detector.js +65 -0
  155. package/dist/detector/workspace-package-detector.js.map +1 -0
  156. package/dist/generator/app-isolation-emitters.d.ts +19 -0
  157. package/dist/generator/app-isolation-emitters.d.ts.map +1 -0
  158. package/dist/generator/app-isolation-emitters.js +19 -0
  159. package/dist/generator/app-isolation-emitters.js.map +1 -0
  160. package/dist/generator/console-isolation-emitters.d.ts +8 -0
  161. package/dist/generator/console-isolation-emitters.d.ts.map +1 -0
  162. package/dist/generator/console-isolation-emitters.js +10 -0
  163. package/dist/generator/console-isolation-emitters.js.map +1 -0
  164. package/dist/generator/deep-relative-emitters.d.ts +14 -0
  165. package/dist/generator/deep-relative-emitters.d.ts.map +1 -0
  166. package/dist/generator/deep-relative-emitters.js +20 -0
  167. package/dist/generator/deep-relative-emitters.js.map +1 -0
  168. package/dist/generator/dependency-internals-emitters.d.ts +17 -0
  169. package/dist/generator/dependency-internals-emitters.d.ts.map +1 -0
  170. package/dist/generator/dependency-internals-emitters.js +20 -0
  171. package/dist/generator/dependency-internals-emitters.js.map +1 -0
  172. package/dist/generator/entry-purity-emitters.d.ts +17 -0
  173. package/dist/generator/entry-purity-emitters.d.ts.map +1 -0
  174. package/dist/generator/entry-purity-emitters.js +24 -0
  175. package/dist/generator/entry-purity-emitters.js.map +1 -0
  176. package/dist/generator/env-access-emitters.d.ts +4 -0
  177. package/dist/generator/env-access-emitters.d.ts.map +1 -0
  178. package/dist/generator/env-access-emitters.js +19 -0
  179. package/dist/generator/env-access-emitters.js.map +1 -0
  180. package/dist/generator/eslint-plugin-emitter.d.ts +14 -0
  181. package/dist/generator/eslint-plugin-emitter.d.ts.map +1 -0
  182. package/dist/generator/eslint-plugin-emitter.js +57 -0
  183. package/dist/generator/eslint-plugin-emitter.js.map +1 -0
  184. package/dist/generator/feature-slice-emitters.d.ts +19 -0
  185. package/dist/generator/feature-slice-emitters.d.ts.map +1 -0
  186. package/dist/generator/feature-slice-emitters.js +19 -0
  187. package/dist/generator/feature-slice-emitters.js.map +1 -0
  188. package/dist/generator/graph-emitters.d.ts +11 -0
  189. package/dist/generator/graph-emitters.d.ts.map +1 -0
  190. package/dist/generator/graph-emitters.js +56 -0
  191. package/dist/generator/graph-emitters.js.map +1 -0
  192. package/dist/generator/layer-emitters.d.ts +39 -0
  193. package/dist/generator/layer-emitters.d.ts.map +1 -0
  194. package/dist/generator/layer-emitters.js +43 -0
  195. package/dist/generator/layer-emitters.js.map +1 -0
  196. package/dist/generator/phantom-dependency-emitters.d.ts +17 -0
  197. package/dist/generator/phantom-dependency-emitters.d.ts.map +1 -0
  198. package/dist/generator/phantom-dependency-emitters.js +19 -0
  199. package/dist/generator/phantom-dependency-emitters.js.map +1 -0
  200. package/dist/generator/public-api-emitters.d.ts +19 -0
  201. package/dist/generator/public-api-emitters.d.ts.map +1 -0
  202. package/dist/generator/public-api-emitters.js +19 -0
  203. package/dist/generator/public-api-emitters.js.map +1 -0
  204. package/dist/generator/role-layering-emitters.d.ts +18 -0
  205. package/dist/generator/role-layering-emitters.d.ts.map +1 -0
  206. package/dist/generator/role-layering-emitters.js +19 -0
  207. package/dist/generator/role-layering-emitters.js.map +1 -0
  208. package/dist/generator/rule-generator.d.ts +14 -0
  209. package/dist/generator/rule-generator.d.ts.map +1 -0
  210. package/dist/generator/rule-generator.js +174 -0
  211. package/dist/generator/rule-generator.js.map +1 -0
  212. package/dist/generator/server-client-emitters.d.ts +17 -0
  213. package/dist/generator/server-client-emitters.d.ts.map +1 -0
  214. package/dist/generator/server-client-emitters.js +18 -0
  215. package/dist/generator/server-client-emitters.js.map +1 -0
  216. package/dist/generator/stories-isolation-emitters.d.ts +17 -0
  217. package/dist/generator/stories-isolation-emitters.d.ts.map +1 -0
  218. package/dist/generator/stories-isolation-emitters.js +19 -0
  219. package/dist/generator/stories-isolation-emitters.js.map +1 -0
  220. package/dist/generator/test-isolation-emitters.d.ts +17 -0
  221. package/dist/generator/test-isolation-emitters.d.ts.map +1 -0
  222. package/dist/generator/test-isolation-emitters.js +19 -0
  223. package/dist/generator/test-isolation-emitters.js.map +1 -0
  224. package/dist/generator/ui-data-isolation-emitters.d.ts +17 -0
  225. package/dist/generator/ui-data-isolation-emitters.d.ts.map +1 -0
  226. package/dist/generator/ui-data-isolation-emitters.js +25 -0
  227. package/dist/generator/ui-data-isolation-emitters.js.map +1 -0
  228. package/dist/generator/workspace-package-emitters.d.ts +4 -0
  229. package/dist/generator/workspace-package-emitters.d.ts.map +1 -0
  230. package/dist/generator/workspace-package-emitters.js +24 -0
  231. package/dist/generator/workspace-package-emitters.js.map +1 -0
  232. package/dist/index.d.ts +52 -0
  233. package/dist/index.d.ts.map +1 -0
  234. package/dist/index.js +52 -0
  235. package/dist/index.js.map +1 -0
  236. package/dist/scanner/app-dirs.d.ts +2 -0
  237. package/dist/scanner/app-dirs.d.ts.map +1 -0
  238. package/dist/scanner/app-dirs.js +58 -0
  239. package/dist/scanner/app-dirs.js.map +1 -0
  240. package/dist/scanner/barrel-resolver.d.ts +4 -0
  241. package/dist/scanner/barrel-resolver.d.ts.map +1 -0
  242. package/dist/scanner/barrel-resolver.js +40 -0
  243. package/dist/scanner/barrel-resolver.js.map +1 -0
  244. package/dist/scanner/file-walker.d.ts +21 -0
  245. package/dist/scanner/file-walker.d.ts.map +1 -0
  246. package/dist/scanner/file-walker.js +279 -0
  247. package/dist/scanner/file-walker.js.map +1 -0
  248. package/dist/scanner/ignore-filter.d.ts +3 -0
  249. package/dist/scanner/ignore-filter.d.ts.map +1 -0
  250. package/dist/scanner/ignore-filter.js +26 -0
  251. package/dist/scanner/ignore-filter.js.map +1 -0
  252. package/dist/scanner/import-graph.d.ts +13 -0
  253. package/dist/scanner/import-graph.d.ts.map +1 -0
  254. package/dist/scanner/import-graph.js +103 -0
  255. package/dist/scanner/import-graph.js.map +1 -0
  256. package/dist/scanner/resolve-import.d.ts +6 -0
  257. package/dist/scanner/resolve-import.d.ts.map +1 -0
  258. package/dist/scanner/resolve-import.js +42 -0
  259. package/dist/scanner/resolve-import.js.map +1 -0
  260. package/dist/scanner/role-classifier.d.ts +12 -0
  261. package/dist/scanner/role-classifier.d.ts.map +1 -0
  262. package/dist/scanner/role-classifier.js +158 -0
  263. package/dist/scanner/role-classifier.js.map +1 -0
  264. package/dist/scanner/usage-scanner.d.ts +12 -0
  265. package/dist/scanner/usage-scanner.d.ts.map +1 -0
  266. package/dist/scanner/usage-scanner.js +31 -0
  267. package/dist/scanner/usage-scanner.js.map +1 -0
  268. package/dist/scanner/workspace-packages.d.ts +3 -0
  269. package/dist/scanner/workspace-packages.d.ts.map +1 -0
  270. package/dist/scanner/workspace-packages.js +96 -0
  271. package/dist/scanner/workspace-packages.js.map +1 -0
  272. package/dist/scanner/workspace-resolver.d.ts +2 -0
  273. package/dist/scanner/workspace-resolver.d.ts.map +1 -0
  274. package/dist/scanner/workspace-resolver.js +57 -0
  275. package/dist/scanner/workspace-resolver.js.map +1 -0
  276. package/package.json +100 -0
@@ -0,0 +1,19 @@
1
+ import type { GenerationStatus } from '../detector/confidence-gate.js';
2
+ import type { FeatureSliceGroup } from '../detector/feature-slice-detector.js';
3
+ export interface CrossSliceRule {
4
+ name: string;
5
+ comment: string;
6
+ severity: 'error' | 'warn' | 'info';
7
+ from: {
8
+ path: string;
9
+ };
10
+ to: {
11
+ path: string;
12
+ pathNot: string;
13
+ };
14
+ }
15
+ export interface CrossSliceConfig {
16
+ forbidden: CrossSliceRule[];
17
+ }
18
+ export declare function toDependencyCruiserFeatureSlice(groups: readonly FeatureSliceGroup[], include?: readonly GenerationStatus[]): CrossSliceConfig;
19
+ //# sourceMappingURL=feature-slice-emitters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-slice-emitters.d.ts","sourceRoot":"","sources":["../../src/generator/feature-slice-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAU/E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACpC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACvB,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACvC;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,SAAS,iBAAiB,EAAE,EACpC,OAAO,GAAE,SAAS,gBAAgB,EAAa,GAC9C,gBAAgB,CAclB"}
@@ -0,0 +1,19 @@
1
+ const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
2
+ const slug = (container) => container.replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-|-$/g, '');
3
+ const confidencePct = (group) => `${(group.gate.conditions.confidence.value * 100).toFixed(0)}%`;
4
+ export function toDependencyCruiserFeatureSlice(groups, include = ['AUTO']) {
5
+ const forbidden = groups
6
+ .filter((group) => include.includes(group.gate.status))
7
+ .map((group) => {
8
+ const container = escapeRegExp(group.container);
9
+ return {
10
+ name: `no-cross-slice-${slug(group.container)}`,
11
+ comment: `Archprint inferred slice isolation: the ${group.sliceCount} slices under "${group.container}" do not import each other (${group.sliceFileCount - group.crossImporterCount}/${group.sliceFileCount} slice files conform); cross-slice imports are forbidden (confidence ${confidencePct(group)}).`,
12
+ severity: 'error',
13
+ from: { path: `^${container}/([^/]+)/` },
14
+ to: { path: `^${container}/([^/]+)/`, pathNot: `^${container}/$1/` },
15
+ };
16
+ });
17
+ return { forbidden };
18
+ }
19
+ //# sourceMappingURL=feature-slice-emitters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-slice-emitters.js","sourceRoot":"","sources":["../../src/generator/feature-slice-emitters.ts"],"names":[],"mappings":"AAGA,MAAM,YAAY,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAE7F,MAAM,IAAI,GAAG,CAAC,SAAiB,EAAU,EAAE,CACzC,SAAS,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAEjE,MAAM,aAAa,GAAG,CAAC,KAAwB,EAAU,EAAE,CACzD,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AAclE,MAAM,UAAU,+BAA+B,CAC7C,MAAoC,EACpC,UAAuC,CAAC,MAAM,CAAC;IAE/C,MAAM,SAAS,GAAG,MAAM;SACrB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;SACtD,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAChD,OAAO;YACL,IAAI,EAAE,kBAAkB,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE;YAC/C,OAAO,EAAE,2CAA2C,KAAK,CAAC,UAAU,kBAAkB,KAAK,CAAC,SAAS,+BAA+B,KAAK,CAAC,cAAc,GAAG,KAAK,CAAC,kBAAkB,IAAI,KAAK,CAAC,cAAc,wEAAwE,aAAa,CAAC,KAAK,CAAC,IAAI;YAC3S,QAAQ,EAAE,OAAgB;YAC1B,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,SAAS,WAAW,EAAE;YACxC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,SAAS,WAAW,EAAE,OAAO,EAAE,IAAI,SAAS,MAAM,EAAE;SACrE,CAAC;IACJ,CAAC,CAAC,CAAC;IACL,OAAO,EAAE,SAAS,EAAE,CAAC;AACvB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { LayerBoundary } from '../detector/layer-detector.js';
2
+ export interface LayerGraphEdge {
3
+ from: string;
4
+ to: string;
5
+ weight: number;
6
+ leak: boolean;
7
+ }
8
+ export declare function layerGraphEdges(boundaries: readonly LayerBoundary[]): LayerGraphEdge[];
9
+ export declare function toMermaid(boundaries: readonly LayerBoundary[]): string;
10
+ export declare function toGraphviz(boundaries: readonly LayerBoundary[]): string;
11
+ //# sourceMappingURL=graph-emitters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-emitters.d.ts","sourceRoot":"","sources":["../../src/generator/graph-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAEnE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,SAAS,aAAa,EAAE,GAAG,cAAc,EAAE,CAwBtF;AAKD,wBAAgB,SAAS,CAAC,UAAU,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CAiBtE;AAID,wBAAgB,UAAU,CAAC,UAAU,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CAcvE"}
@@ -0,0 +1,56 @@
1
+ export function layerGraphEdges(boundaries) {
2
+ const edges = [];
3
+ for (const boundary of boundaries) {
4
+ if (boundary.reverseFlow > 0) {
5
+ edges.push({
6
+ from: boundary.to,
7
+ to: boundary.from,
8
+ weight: boundary.reverseFlow,
9
+ leak: false,
10
+ });
11
+ }
12
+ if (boundary.stats.violatingFileCount > 0) {
13
+ edges.push({
14
+ from: boundary.from,
15
+ to: boundary.to,
16
+ weight: boundary.stats.violatingFileCount,
17
+ leak: true,
18
+ });
19
+ }
20
+ }
21
+ return edges.sort((a, b) => a.from.localeCompare(b.from) || a.to.localeCompare(b.to) || Number(a.leak) - Number(b.leak));
22
+ }
23
+ const graphNodes = (edges) => [...new Set(edges.flatMap((edge) => [edge.from, edge.to]))].sort();
24
+ export function toMermaid(boundaries) {
25
+ const edges = layerGraphEdges(boundaries);
26
+ const nodes = graphNodes(edges);
27
+ const id = new Map(nodes.map((node, index) => [node, `n${index}`]));
28
+ const lines = ['graph LR'];
29
+ for (const node of nodes)
30
+ lines.push(` ${id.get(node)}["${node}"]`);
31
+ edges.forEach((edge) => {
32
+ const from = id.get(edge.from);
33
+ const to = id.get(edge.to);
34
+ lines.push(edge.leak
35
+ ? ` ${from} -. "${edge.weight} ⚠" .-> ${to}`
36
+ : ` ${from} -->|${edge.weight}| ${to}`);
37
+ });
38
+ return lines.join('\n');
39
+ }
40
+ const dotId = (layer) => `"${layer.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
41
+ export function toGraphviz(boundaries) {
42
+ const edges = layerGraphEdges(boundaries);
43
+ const nodes = graphNodes(edges);
44
+ const lines = ['digraph archprint {', ' rankdir=LR;', ' node [shape=box];'];
45
+ for (const node of nodes)
46
+ lines.push(` ${dotId(node)};`);
47
+ for (const edge of edges) {
48
+ const attrs = edge.leak
49
+ ? `[label="${edge.weight}", style=dashed, color=red]`
50
+ : `[label="${edge.weight}"]`;
51
+ lines.push(` ${dotId(edge.from)} -> ${dotId(edge.to)} ${attrs};`);
52
+ }
53
+ lines.push('}');
54
+ return lines.join('\n');
55
+ }
56
+ //# sourceMappingURL=graph-emitters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-emitters.js","sourceRoot":"","sources":["../../src/generator/graph-emitters.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,eAAe,CAAC,UAAoC;IAClE,MAAM,KAAK,GAAqB,EAAE,CAAC;IACnC,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;QAClC,IAAI,QAAQ,CAAC,WAAW,GAAG,CAAC,EAAE,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,QAAQ,CAAC,EAAE;gBACjB,EAAE,EAAE,QAAQ,CAAC,IAAI;gBACjB,MAAM,EAAE,QAAQ,CAAC,WAAW;gBAC5B,IAAI,EAAE,KAAK;aACZ,CAAC,CAAC;QACL,CAAC;QACD,IAAI,QAAQ,CAAC,KAAK,CAAC,kBAAkB,GAAG,CAAC,EAAE,CAAC;YAC1C,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,EAAE,EAAE,QAAQ,CAAC,EAAE;gBACf,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,kBAAkB;gBACzC,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CACf,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAC9F,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,KAAgC,EAAY,EAAE,CAChE,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAErE,MAAM,UAAU,SAAS,CAAC,UAAoC;IAC5D,MAAM,KAAK,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAChC,MAAM,EAAE,GAAG,IAAI,GAAG,CAAiB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAEpF,MAAM,KAAK,GAAG,CAAC,UAAU,CAAC,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,IAAI,CAAE,KAAK,IAAI,IAAI,CAAC,CAAC;IACtE,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QACrB,MAAM,IAAI,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAE,CAAC;QAChC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CACR,IAAI,CAAC,IAAI;YACP,CAAC,CAAC,KAAK,IAAI,QAAQ,IAAI,CAAC,MAAM,WAAW,EAAE,EAAE;YAC7C,CAAC,CAAC,KAAK,IAAI,QAAQ,IAAI,CAAC,MAAM,KAAK,EAAE,EAAE,CAC1C,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,KAAK,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC;AAElG,MAAM,UAAU,UAAU,CAAC,UAAoC;IAC7D,MAAM,KAAK,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAEhC,MAAM,KAAK,GAAG,CAAC,qBAAqB,EAAE,eAAe,EAAE,qBAAqB,CAAC,CAAC;IAC9E,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI;YACrB,CAAC,CAAC,WAAW,IAAI,CAAC,MAAM,6BAA6B;YACrD,CAAC,CAAC,WAAW,IAAI,CAAC,MAAM,IAAI,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;IACrE,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,39 @@
1
+ import type { GenerationStatus } from '../detector/confidence-gate.js';
2
+ import type { LayerBoundary } from '../detector/layer-detector.js';
3
+ export interface DependencyCruiserRule {
4
+ name: string;
5
+ comment: string;
6
+ severity: 'error' | 'warn' | 'info';
7
+ from: {
8
+ path: string;
9
+ };
10
+ to: {
11
+ path: string;
12
+ };
13
+ }
14
+ export interface DependencyCruiserConfig {
15
+ forbidden: DependencyCruiserRule[];
16
+ }
17
+ export declare function toDependencyCruiser(boundaries: readonly LayerBoundary[], include?: readonly GenerationStatus[]): DependencyCruiserConfig;
18
+ export interface EslintBoundariesElement {
19
+ type: string;
20
+ pattern: string;
21
+ }
22
+ export interface EslintBoundariesRule {
23
+ from: string[];
24
+ disallow: string[];
25
+ message: string;
26
+ }
27
+ export interface EslintBoundariesConfig {
28
+ settings: {
29
+ 'boundaries/elements': EslintBoundariesElement[];
30
+ };
31
+ rules: {
32
+ 'boundaries/element-types': ['error', {
33
+ default: 'allow';
34
+ rules: EslintBoundariesRule[];
35
+ }];
36
+ };
37
+ }
38
+ export declare function toEslintBoundaries(boundaries: readonly LayerBoundary[], include?: readonly GenerationStatus[]): EslintBoundariesConfig;
39
+ //# sourceMappingURL=layer-emitters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layer-emitters.d.ts","sourceRoot":"","sources":["../../src/generator/layer-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAcnE,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACpC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACvB,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,qBAAqB,EAAE,CAAC;CACpC;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,SAAS,aAAa,EAAE,EACpC,OAAO,GAAE,SAAS,gBAAgB,EAAa,GAC9C,uBAAuB,CASzB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE;QAAE,qBAAqB,EAAE,uBAAuB,EAAE,CAAA;KAAE,CAAC;IAC/D,KAAK,EAAE;QACL,0BAA0B,EAAE,CAAC,OAAO,EAAE;YAAE,OAAO,EAAE,OAAO,CAAC;YAAC,KAAK,EAAE,oBAAoB,EAAE,CAAA;SAAE,CAAC,CAAC;KAC5F,CAAC;CACH;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,SAAS,aAAa,EAAE,EACpC,OAAO,GAAE,SAAS,gBAAgB,EAAa,GAC9C,sBAAsB,CA6BxB"}
@@ -0,0 +1,43 @@
1
+ const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
2
+ const layerPath = (layer) => `(^|/)${escapeRegExp(layer)}/`;
3
+ const confidencePct = (boundary) => `${(boundary.gate.conditions.confidence.value * 100).toFixed(0)}%`;
4
+ const selected = (boundaries, include) => boundaries.filter((boundary) => include.includes(boundary.gate.status));
5
+ export function toDependencyCruiser(boundaries, include = ['AUTO']) {
6
+ const forbidden = selected(boundaries, include).map((boundary) => ({
7
+ name: `no-${boundary.from}-to-${boundary.to}`,
8
+ comment: `Archprint inferred boundary: the "${boundary.to}" layer depends on "${boundary.from}" across ${boundary.reverseFlow} file(s); the reverse is forbidden (confidence ${confidencePct(boundary)}).`,
9
+ severity: 'error',
10
+ from: { path: layerPath(boundary.from) },
11
+ to: { path: layerPath(boundary.to) },
12
+ }));
13
+ return { forbidden };
14
+ }
15
+ export function toEslintBoundaries(boundaries, include = ['AUTO']) {
16
+ const chosen = selected(boundaries, include);
17
+ const layers = [...new Set(chosen.flatMap((boundary) => [boundary.from, boundary.to]))].sort();
18
+ const elements = layers.map((layer) => ({ type: layer, pattern: `**/${layer}/**` }));
19
+ const disallowByFrom = new Map();
20
+ for (const boundary of chosen) {
21
+ let targets = disallowByFrom.get(boundary.from);
22
+ if (targets === undefined) {
23
+ targets = new Set();
24
+ disallowByFrom.set(boundary.from, targets);
25
+ }
26
+ targets.add(boundary.to);
27
+ }
28
+ const rules = [...disallowByFrom.entries()]
29
+ .sort(([a], [b]) => a.localeCompare(b))
30
+ .map(([from, targets]) => {
31
+ const disallow = [...targets].sort();
32
+ return {
33
+ from: [from],
34
+ disallow,
35
+ message: `The "${from}" layer must not import: ${disallow.join(', ')} (inferred by Archprint from the observed import direction).`,
36
+ };
37
+ });
38
+ return {
39
+ settings: { 'boundaries/elements': elements },
40
+ rules: { 'boundaries/element-types': ['error', { default: 'allow', rules }] },
41
+ };
42
+ }
43
+ //# sourceMappingURL=layer-emitters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layer-emitters.js","sourceRoot":"","sources":["../../src/generator/layer-emitters.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { PhantomDependencyAnalysis } from '../detector/phantom-dependency-detector.js';
2
+ export interface PhantomDependencyRule {
3
+ name: string;
4
+ comment: string;
5
+ severity: 'error' | 'warn' | 'info';
6
+ from: {
7
+ pathNot: string;
8
+ };
9
+ to: {
10
+ dependencyTypes: string[];
11
+ };
12
+ }
13
+ export interface PhantomDependencyConfig {
14
+ forbidden: PhantomDependencyRule[];
15
+ }
16
+ export declare function toDependencyCruiserPhantomDependencies(analysis: PhantomDependencyAnalysis): PhantomDependencyConfig;
17
+ //# sourceMappingURL=phantom-dependency-emitters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phantom-dependency-emitters.d.ts","sourceRoot":"","sources":["../../src/generator/phantom-dependency-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4CAA4C,CAAC;AAE5F,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACpC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1B,EAAE,EAAE;QAAE,eAAe,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CACnC;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,qBAAqB,EAAE,CAAC;CACpC;AAED,wBAAgB,sCAAsC,CACpD,QAAQ,EAAE,yBAAyB,GAClC,uBAAuB,CAiBzB"}
@@ -0,0 +1,19 @@
1
+ export function toDependencyCruiserPhantomDependencies(analysis) {
2
+ if (analysis.externalImporterCount === 0 || analysis.gate.status !== 'AUTO') {
3
+ return { forbidden: [] };
4
+ }
5
+ const conform = analysis.externalImporterCount - analysis.offenderCount;
6
+ const floor = `${(analysis.gate.conditions.confidence.value * 100).toFixed(0)}%`;
7
+ return {
8
+ forbidden: [
9
+ {
10
+ name: 'no-phantom-dependencies',
11
+ comment: `Archprint inferred dependency declaration: ${conform}/${analysis.externalImporterCount} files import only packages declared in package.json; importing an undeclared (phantom/transitive) dependency is forbidden (confidence ${floor}).`,
12
+ severity: 'error',
13
+ from: { pathNot: 'node_modules' },
14
+ to: { dependencyTypes: ['npm-no-pkg', 'npm-unknown'] },
15
+ },
16
+ ],
17
+ };
18
+ }
19
+ //# sourceMappingURL=phantom-dependency-emitters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phantom-dependency-emitters.js","sourceRoot":"","sources":["../../src/generator/phantom-dependency-emitters.ts"],"names":[],"mappings":"AAcA,MAAM,UAAU,sCAAsC,CACpD,QAAmC;IAEnC,IAAI,QAAQ,CAAC,qBAAqB,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC5E,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC3B,CAAC;IACD,MAAM,OAAO,GAAG,QAAQ,CAAC,qBAAqB,GAAG,QAAQ,CAAC,aAAa,CAAC;IACxE,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACjF,OAAO;QACL,SAAS,EAAE;YACT;gBACE,IAAI,EAAE,yBAAyB;gBAC/B,OAAO,EAAE,8CAA8C,OAAO,IAAI,QAAQ,CAAC,qBAAqB,0IAA0I,KAAK,IAAI;gBACnP,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE;gBACjC,EAAE,EAAE,EAAE,eAAe,EAAE,CAAC,YAAY,EAAE,aAAa,CAAC,EAAE;aACvD;SACF;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { GenerationStatus } from '../detector/confidence-gate.js';
2
+ import type { PublicApiGroup } from '../detector/public-api-detector.js';
3
+ export interface DeepImportRule {
4
+ name: string;
5
+ comment: string;
6
+ severity: 'error' | 'warn' | 'info';
7
+ from: {
8
+ pathNot: string;
9
+ };
10
+ to: {
11
+ path: string;
12
+ pathNot: string;
13
+ };
14
+ }
15
+ export interface DeepImportConfig {
16
+ forbidden: DeepImportRule[];
17
+ }
18
+ export declare function toDependencyCruiserPublicApi(groups: readonly PublicApiGroup[], include?: readonly GenerationStatus[]): DeepImportConfig;
19
+ //# sourceMappingURL=public-api-emitters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-api-emitters.d.ts","sourceRoot":"","sources":["../../src/generator/public-api-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AASzE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACpC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1B,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACvC;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,SAAS,cAAc,EAAE,EACjC,OAAO,GAAE,SAAS,gBAAgB,EAAa,GAC9C,gBAAgB,CAclB"}
@@ -0,0 +1,19 @@
1
+ const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
2
+ const slug = (dir) => dir.replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-|-$/g, '');
3
+ const confidencePct = (group) => `${(group.gate.conditions.confidence.value * 100).toFixed(0)}%`;
4
+ export function toDependencyCruiserPublicApi(groups, include = ['AUTO']) {
5
+ const forbidden = groups
6
+ .filter((group) => include.includes(group.gate.status))
7
+ .map((group) => {
8
+ const dir = escapeRegExp(group.dir);
9
+ return {
10
+ name: `no-deep-import-${slug(group.dir)}`,
11
+ comment: `Archprint inferred public API: files outside "${group.dir}" import it through its barrel (${group.consumerCount - group.deepImporterCount}/${group.consumerCount} consumers); deep imports into its internals are forbidden (confidence ${confidencePct(group)}).`,
12
+ severity: 'error',
13
+ from: { pathNot: `^${dir}/` },
14
+ to: { path: `^${dir}/`, pathNot: `^${dir}/index\\.(ts|tsx)$` },
15
+ };
16
+ });
17
+ return { forbidden };
18
+ }
19
+ //# sourceMappingURL=public-api-emitters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-api-emitters.js","sourceRoot":"","sources":["../../src/generator/public-api-emitters.ts"],"names":[],"mappings":"AAGA,MAAM,YAAY,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAE7F,MAAM,IAAI,GAAG,CAAC,GAAW,EAAU,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAE/F,MAAM,aAAa,GAAG,CAAC,KAAqB,EAAU,EAAE,CACtD,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AAclE,MAAM,UAAU,4BAA4B,CAC1C,MAAiC,EACjC,UAAuC,CAAC,MAAM,CAAC;IAE/C,MAAM,SAAS,GAAG,MAAM;SACrB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;SACtD,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpC,OAAO;YACL,IAAI,EAAE,kBAAkB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;YACzC,OAAO,EAAE,iDAAiD,KAAK,CAAC,GAAG,mCAAmC,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC,iBAAiB,IAAI,KAAK,CAAC,aAAa,0EAA0E,aAAa,CAAC,KAAK,CAAC,IAAI;YAC5Q,QAAQ,EAAE,OAAgB;YAC1B,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,GAAG,GAAG,EAAE;YAC7B,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,GAAG,GAAG,EAAE,OAAO,EAAE,IAAI,GAAG,oBAAoB,EAAE;SAC/D,CAAC;IACJ,CAAC,CAAC,CAAC;IACL,OAAO,EAAE,SAAS,EAAE,CAAC;AACvB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { GenerationStatus } from '../detector/confidence-gate.js';
2
+ import type { RoleBoundary } from '../detector/role-layering-detector.js';
3
+ export interface RoleLayeringRule {
4
+ name: string;
5
+ comment: string;
6
+ severity: 'error' | 'warn' | 'info';
7
+ from: {
8
+ path: string;
9
+ };
10
+ to: {
11
+ path: string;
12
+ };
13
+ }
14
+ export interface RoleLayeringConfig {
15
+ forbidden: RoleLayeringRule[];
16
+ }
17
+ export declare function toDependencyCruiserRoleLayering(boundaries: readonly RoleBoundary[], include?: readonly GenerationStatus[]): RoleLayeringConfig;
18
+ //# sourceMappingURL=role-layering-emitters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role-layering-emitters.d.ts","sourceRoot":"","sources":["../../src/generator/role-layering-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAY1E,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACpC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACvB,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,gBAAgB,EAAE,CAAC;CAC/B;AAED,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,SAAS,YAAY,EAAE,EACnC,OAAO,GAAE,SAAS,gBAAgB,EAAa,GAC9C,kBAAkB,CAWpB"}
@@ -0,0 +1,19 @@
1
+ import { ROLE_PATTERNS } from '../scanner/role-classifier.js';
2
+ const rolePath = (role) => {
3
+ const sources = (ROLE_PATTERNS.get(role) ?? []).map((pattern) => pattern.source);
4
+ return sources.length === 1 ? sources[0] : `(${sources.join('|')})`;
5
+ };
6
+ const confidencePct = (boundary) => `${(boundary.gate.conditions.confidence.value * 100).toFixed(0)}%`;
7
+ export function toDependencyCruiserRoleLayering(boundaries, include = ['AUTO']) {
8
+ const forbidden = boundaries
9
+ .filter((boundary) => include.includes(boundary.gate.status))
10
+ .map((boundary) => ({
11
+ name: `no-${boundary.from.toLowerCase()}-to-${boundary.to.toLowerCase()}`,
12
+ comment: `Archprint inferred role layering: ${boundary.reverseFlow} ${boundary.to} file(s) depend on ${boundary.from}, not the reverse; a ${boundary.from} must not import a ${boundary.to} (confidence ${confidencePct(boundary)}).`,
13
+ severity: 'error',
14
+ from: { path: rolePath(boundary.from) },
15
+ to: { path: rolePath(boundary.to) },
16
+ }));
17
+ return { forbidden };
18
+ }
19
+ //# sourceMappingURL=role-layering-emitters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role-layering-emitters.js","sourceRoot":"","sources":["../../src/generator/role-layering-emitters.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAG9D,MAAM,QAAQ,GAAG,CAAC,IAAU,EAAU,EAAE;IACtC,MAAM,OAAO,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACjF,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AACvE,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,CAAC,QAAsB,EAAU,EAAE,CACvD,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AAcrE,MAAM,UAAU,+BAA+B,CAC7C,UAAmC,EACnC,UAAuC,CAAC,MAAM,CAAC;IAE/C,MAAM,SAAS,GAAG,UAAU;SACzB,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;SAC5D,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAClB,IAAI,EAAE,MAAM,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE;QACzE,OAAO,EAAE,qCAAqC,QAAQ,CAAC,WAAW,IAAI,QAAQ,CAAC,EAAE,sBAAsB,QAAQ,CAAC,IAAI,wBAAwB,QAAQ,CAAC,IAAI,sBAAsB,QAAQ,CAAC,EAAE,gBAAgB,aAAa,CAAC,QAAQ,CAAC,IAAI;QACrO,QAAQ,EAAE,OAAgB;QAC1B,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;QACvC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE;KACpC,CAAC,CAAC,CAAC;IACN,OAAO,EAAE,SAAS,EAAE,CAAC;AACvB,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { DetectedPattern } from '../detector/pattern-detector.js';
2
+ import type { PatternConfig } from '../detector/pattern-detector.js';
3
+ export interface RuleArtifacts {
4
+ ruleName: string;
5
+ files: {
6
+ rule: string;
7
+ card: string;
8
+ passingFixture: string;
9
+ failingFixture: string;
10
+ };
11
+ }
12
+ export declare function generateRuleArtifacts(config: PatternConfig, result: DetectedPattern, provenance?: string): RuleArtifacts;
13
+ export declare function emitRuleArtifacts(config: PatternConfig, result: DetectedPattern, outDir: string, provenance?: string): string;
14
+ //# sourceMappingURL=rule-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-generator.d.ts","sourceRoot":"","sources":["../../src/generator/rule-generator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAErE,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;CACvF;AAmKD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,eAAe,EACvB,UAAU,SAAK,GACd,aAAa,CAUf;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,MAAM,EACd,UAAU,SAAK,GACd,MAAM,CASR"}
@@ -0,0 +1,174 @@
1
+ import { mkdirSync, writeFileSync } from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { ROLE_PATTERNS } from '../scanner/role-classifier.js';
4
+ const asRegexArray = (sources) => `[${sources.map((source) => `/${source}/`).join(', ')}]`;
5
+ function renderRule(config, result) {
6
+ const entryPatterns = config.roles
7
+ .flatMap((role) => ROLE_PATTERNS.get(role) ?? [])
8
+ .map((pattern) => pattern.source);
9
+ const nonEntryPatterns = [...ROLE_PATTERNS]
10
+ .filter(([role]) => !config.roles.includes(role) && role !== 'COMPONENT')
11
+ .flatMap(([, patterns]) => patterns.map((pattern) => pattern.source));
12
+ const forbidden = config.forbidden.map((marker) => marker.source);
13
+ const includesServerAction = config.roles.includes('SERVER_ACTION');
14
+ const { ratio, roleFileCount, violatingFileCount } = result.stats;
15
+ const conform = (ratio * 100).toFixed(1);
16
+ return `/**
17
+ * @fileoverview ${config.id} ${config.name}: GENERATED BY ARCHPRINT. Do not edit by hand.
18
+ * Inferred from the real import graph: ${roleFileCount - violatingFileCount}/${roleFileCount} server-entry files (${conform}%) already follow this.
19
+ * Evidence and provenance: ./${config.name}.md
20
+ */
21
+ import type { Rule } from 'eslint';
22
+
23
+ const SERVER_ENTRY_PATTERNS: readonly RegExp[] = ${asRegexArray(entryPatterns)};
24
+ const NAMED_NON_ENTRY: readonly RegExp[] = ${asRegexArray(nonEntryPatterns)};
25
+ const FORBIDDEN: readonly RegExp[] = ${asRegexArray(forbidden)};
26
+
27
+ const normalize = (filePath: string): string => filePath.replace(/\\\\/g, '/');
28
+
29
+ function hasUseServerDirective(context: Rule.RuleContext): boolean {
30
+ for (const statement of context.sourceCode.ast.body) {
31
+ if (statement.type !== 'ExpressionStatement') break;
32
+ const expression = statement.expression;
33
+ if (expression.type !== 'Literal' || typeof expression.value !== 'string') break;
34
+ if (expression.value === 'use server') return true;
35
+ }
36
+ return false;
37
+ }
38
+
39
+ function isServerEntry(filename: string, context: Rule.RuleContext): boolean {
40
+ const file = normalize(filename);
41
+ if (SERVER_ENTRY_PATTERNS.some((pattern) => pattern.test(file))) return true;
42
+ ${includesServerAction
43
+ ? `if (!file.endsWith('.tsx') && !NAMED_NON_ENTRY.some((pattern) => pattern.test(file))) {
44
+ return hasUseServerDirective(context);
45
+ }`
46
+ : `void NAMED_NON_ENTRY;
47
+ void hasUseServerDirective;`}
48
+ return false;
49
+ }
50
+
51
+ const rule: Rule.RuleModule = {
52
+ meta: {
53
+ type: 'problem',
54
+ docs: {
55
+ description: ${JSON.stringify(config.description)},
56
+ recommended: false,
57
+ },
58
+ schema: [],
59
+ messages: {
60
+ forbidden:
61
+ '${config.id}: a server-entry file must not import the UI components/ layer (imported "{{specifier}}"). Move shared logic into a non-UI module.',
62
+ },
63
+ },
64
+ create(context) {
65
+ const filename = context.filename;
66
+ if (!isServerEntry(filename, context)) return {};
67
+ return {
68
+ ImportDeclaration(node) {
69
+ const specifier = String(node.source.value);
70
+ if (FORBIDDEN.some((pattern) => pattern.test(specifier))) {
71
+ context.report({ node: node.source, messageId: 'forbidden', data: { specifier } });
72
+ }
73
+ },
74
+ };
75
+ },
76
+ };
77
+
78
+ export default rule;
79
+ `;
80
+ }
81
+ function renderCard(config, result, provenance) {
82
+ const { roleFileCount, violatingFileCount, ratio, roleConfidence } = result.stats;
83
+ const example = result.violations[0];
84
+ const gate = result.gate.conditions;
85
+ const pass = (ok) => (ok ? 'PASS' : 'FAIL');
86
+ return `# ${config.id} \`${config.name}\`
87
+
88
+ > Generated by Archprint from the repository's real import graph. ${provenance}
89
+
90
+ ## Rule
91
+
92
+ ${config.description}
93
+
94
+ **Scope (server-entry roles):** ${config.roles.join(', ')}. Semantics: **direct import**. A violation
95
+ is a server-entry file that directly imports the \`components/\` layer. Transitive reach (importing a
96
+ module that itself imports a component) is out of scope.
97
+
98
+ ## Evidence (why Archprint generated this)
99
+
100
+ | gate condition | value | threshold | result |
101
+ |---|---|---|---|
102
+ | conformance (95% lower bound) | ${(gate.confidence.value * 100).toFixed(1)}% floor, ${(ratio * 100).toFixed(1)}% observed (${roleFileCount - violatingFileCount}/${roleFileCount}) | >= 90% | ${pass(gate.confidence.pass)} |
103
+ | exceptions | ${violatingFileCount} | <= 3 | ${pass(gate.exceptions.pass)} |
104
+ | role confidence | ${roleConfidence.toFixed(2)} | >= 0.80 | ${pass(gate.roleConfidence.pass)} |
105
+
106
+ Status: **${result.gate.status}** (all conditions must pass to auto-generate; the conformance floor is the
107
+ Wilson 95% lower bound over ${roleFileCount} observations, so a thin sample cannot auto-generate).
108
+ ${example
109
+ ? `\n## The one exception in the source repo\n\n\`${example.file}\` imports \`${example.specifier}\`. Review whether it is a genuine violation or a legitimate exception before enforcing.`
110
+ : ''}
111
+
112
+ ## Enforcement scope and limitations (honest boundaries)
113
+
114
+ This is a lightweight, self-contained ESLint rule. It enforces at the **import-specifier** level, which
115
+ is narrower than the graph-based inference that generated it:
116
+
117
+ - **Alias/barrel false negative:** an import whose specifier does not contain \`components\` but resolves
118
+ to the UI layer (e.g. \`@/lib/shared-ui\` aliased to a component) is **not** caught. The detector
119
+ resolves such imports to their leaf; the rule does not. A future alias-aware or dependency-cruiser
120
+ emission would close this.
121
+ - **External false positive:** a specifier literally containing \`components\` that resolves inside
122
+ \`node_modules\` would be flagged. Not relevant to this repo, but disclosed.
123
+ - **Server-entry scope:** mirrors the classifier. A file with a \`"use server"\` directive is treated as
124
+ server-entry only when it is otherwise unclassified; named roles (service, repository, worker, db /
125
+ data-access, shared, test) keep their role and are out of scope.
126
+
127
+ ## Fixtures
128
+
129
+ - \`fixtures/passing.ts\`: a server-entry file that does not import the UI layer (rule stays silent).
130
+ - \`fixtures/failing.ts\`: a server-entry file that imports the UI layer (rule reports).
131
+ `;
132
+ }
133
+ function renderPassingFixture() {
134
+ return `// app/api/example/route.ts (PASSING): a route handler with no UI-layer import.
135
+ import { NextResponse } from 'next/server';
136
+ import { getUser } from '@/utils/user';
137
+
138
+ export async function GET(): Promise<Response> {
139
+ return NextResponse.json(await getUser());
140
+ }
141
+ `;
142
+ }
143
+ function renderFailingFixture() {
144
+ return `// app/api/chat/route.ts (FAILING): a route handler importing the components/ (UI) layer.
145
+ import { NextResponse } from 'next/server';
146
+ import { convertToUIMessages } from '@/components/assistant-chat/helpers';
147
+
148
+ export async function POST(): Promise<Response> {
149
+ return NextResponse.json(convertToUIMessages([]));
150
+ }
151
+ `;
152
+ }
153
+ export function generateRuleArtifacts(config, result, provenance = '') {
154
+ return {
155
+ ruleName: config.name,
156
+ files: {
157
+ rule: renderRule(config, result),
158
+ card: renderCard(config, result, provenance),
159
+ passingFixture: renderPassingFixture(),
160
+ failingFixture: renderFailingFixture(),
161
+ },
162
+ };
163
+ }
164
+ export function emitRuleArtifacts(config, result, outDir, provenance = '') {
165
+ const artifacts = generateRuleArtifacts(config, result, provenance);
166
+ const ruleDir = path.join(outDir, artifacts.ruleName);
167
+ mkdirSync(path.join(ruleDir, 'fixtures'), { recursive: true });
168
+ writeFileSync(path.join(ruleDir, `${artifacts.ruleName}.ts`), artifacts.files.rule);
169
+ writeFileSync(path.join(ruleDir, `${artifacts.ruleName}.md`), artifacts.files.card);
170
+ writeFileSync(path.join(ruleDir, 'fixtures', 'passing.ts'), artifacts.files.passingFixture);
171
+ writeFileSync(path.join(ruleDir, 'fixtures', 'failing.ts'), artifacts.files.failingFixture);
172
+ return ruleDir;
173
+ }
174
+ //# sourceMappingURL=rule-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-generator.js","sourceRoot":"","sources":["../../src/generator/rule-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { ServerClientAnalysis } from '../detector/server-client-detector.js';
2
+ export interface ServerClientRule {
3
+ name: string;
4
+ comment: string;
5
+ severity: 'error' | 'warn' | 'info';
6
+ from: {
7
+ path: string;
8
+ };
9
+ to: {
10
+ path: string;
11
+ };
12
+ }
13
+ export interface ServerClientConfig {
14
+ forbidden: ServerClientRule[];
15
+ }
16
+ export declare function toDependencyCruiserServerClient(analysis: ServerClientAnalysis): ServerClientConfig;
17
+ //# sourceMappingURL=server-client-emitters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-client-emitters.d.ts","sourceRoot":"","sources":["../../src/generator/server-client-emitters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAElF,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACpC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACvB,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,gBAAgB,EAAE,CAAC;CAC/B;AAED,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,oBAAoB,GAC7B,kBAAkB,CAepB"}
@@ -0,0 +1,18 @@
1
+ export function toDependencyCruiserServerClient(analysis) {
2
+ if (analysis.clientCount === 0 || analysis.gate.status !== 'AUTO')
3
+ return { forbidden: [] };
4
+ const conform = analysis.clientCount - analysis.offenderCount;
5
+ const floor = `${(analysis.gate.conditions.confidence.value * 100).toFixed(0)}%`;
6
+ return {
7
+ forbidden: [
8
+ {
9
+ name: 'no-server-only-in-client',
10
+ comment: `Archprint inferred server/client boundary: ${conform}/${analysis.clientCount} "use client" modules import no server-only code; a client component importing a server-only module is forbidden (confidence ${floor}).`,
11
+ severity: 'error',
12
+ from: { path: '\\.(ts|tsx)$' },
13
+ to: { path: 'node_modules/server-only' },
14
+ },
15
+ ],
16
+ };
17
+ }
18
+ //# sourceMappingURL=server-client-emitters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-client-emitters.js","sourceRoot":"","sources":["../../src/generator/server-client-emitters.ts"],"names":[],"mappings":"AAcA,MAAM,UAAU,+BAA+B,CAC7C,QAA8B;IAE9B,IAAI,QAAQ,CAAC,WAAW,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC5F,MAAM,OAAO,GAAG,QAAQ,CAAC,WAAW,GAAG,QAAQ,CAAC,aAAa,CAAC;IAC9D,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACjF,OAAO;QACL,SAAS,EAAE;YACT;gBACE,IAAI,EAAE,0BAA0B;gBAChC,OAAO,EAAE,8CAA8C,OAAO,IAAI,QAAQ,CAAC,WAAW,gIAAgI,KAAK,IAAI;gBAC/N,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE;gBAC9B,EAAE,EAAE,EAAE,IAAI,EAAE,0BAA0B,EAAE;aACzC;SACF;KACF,CAAC;AACJ,CAAC"}