laymos 0.0.1 → 0.0.3

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 (491) hide show
  1. package/README.md +137 -710
  2. package/bin/laymos.mjs +2 -0
  3. package/dist/architecture-analysis-schema/architecture-analysis-schema.d.ts +109 -0
  4. package/dist/architecture-analysis-schema/architecture-analysis-schema.d.ts.map +1 -0
  5. package/dist/architecture-analysis-schema/architecture-analysis-schema.js +10 -0
  6. package/dist/architecture-analysis-schema/index.d.ts +7 -0
  7. package/dist/architecture-analysis-schema/index.d.ts.map +1 -0
  8. package/dist/architecture-analysis-schema/index.js +3 -0
  9. package/dist/architecture-analysis-schema/layer-analysis-schema.d.ts +27 -0
  10. package/dist/architecture-analysis-schema/layer-analysis-schema.d.ts.map +1 -0
  11. package/dist/architecture-analysis-schema/layer-analysis-schema.js +18 -0
  12. package/dist/architecture-analysis-schema/module-analysis-schema.d.ts +150 -0
  13. package/dist/architecture-analysis-schema/module-analysis-schema.d.ts.map +1 -0
  14. package/dist/architecture-analysis-schema/module-analysis-schema.js +96 -0
  15. package/dist/architecture-analysis-schema/module-source-schema.d.ts +16 -0
  16. package/dist/architecture-analysis-schema/module-source-schema.d.ts.map +1 -0
  17. package/dist/architecture-analysis-schema/module-source-schema.js +10 -0
  18. package/dist/architecture-analysis-schema/project-config-schema.d.ts +51 -0
  19. package/dist/architecture-analysis-schema/project-config-schema.d.ts.map +1 -0
  20. package/dist/architecture-analysis-schema/project-config-schema.js +110 -0
  21. package/dist/change-set-schema/change-set-schema.d.ts +55 -0
  22. package/dist/change-set-schema/change-set-schema.d.ts.map +1 -0
  23. package/dist/change-set-schema/change-set-schema.js +43 -0
  24. package/dist/change-set-schema/index.d.ts +3 -0
  25. package/dist/change-set-schema/index.d.ts.map +1 -0
  26. package/dist/change-set-schema/index.js +2 -0
  27. package/dist/cli/cli.js +6 -1
  28. package/dist/cli/errors.d.ts +2 -0
  29. package/dist/cli/errors.d.ts.map +1 -0
  30. package/dist/cli/errors.js +34 -0
  31. package/dist/cli/inspect/file/file.d.ts +8 -0
  32. package/dist/cli/inspect/file/file.d.ts.map +1 -0
  33. package/dist/cli/inspect/file/file.js +16 -0
  34. package/dist/cli/inspect/file/index.d.ts +2 -0
  35. package/dist/cli/inspect/file/index.d.ts.map +1 -0
  36. package/dist/cli/inspect/file/index.js +1 -0
  37. package/dist/cli/inspect/file/report.d.ts +3 -0
  38. package/dist/cli/inspect/file/report.d.ts.map +1 -0
  39. package/dist/cli/inspect/file/report.js +26 -0
  40. package/dist/cli/inspect/index.d.ts +2 -0
  41. package/dist/cli/inspect/index.d.ts.map +1 -0
  42. package/dist/cli/inspect/index.js +1 -0
  43. package/dist/cli/inspect/inspect.d.ts +4 -0
  44. package/dist/cli/inspect/inspect.d.ts.map +1 -0
  45. package/dist/cli/inspect/inspect.js +14 -0
  46. package/dist/cli/inspect/json.d.ts +4 -0
  47. package/dist/cli/inspect/json.d.ts.map +1 -0
  48. package/dist/cli/inspect/json.js +5 -0
  49. package/dist/cli/inspect/layer/index.d.ts +2 -0
  50. package/dist/cli/inspect/layer/index.d.ts.map +1 -0
  51. package/dist/cli/inspect/layer/index.js +1 -0
  52. package/dist/cli/inspect/layer/layer.d.ts +7 -0
  53. package/dist/cli/inspect/layer/layer.d.ts.map +1 -0
  54. package/dist/cli/inspect/layer/layer.js +13 -0
  55. package/dist/cli/inspect/layer/report.d.ts +3 -0
  56. package/dist/cli/inspect/layer/report.d.ts.map +1 -0
  57. package/dist/cli/inspect/layer/report.js +21 -0
  58. package/dist/cli/inspect/module/index.d.ts +2 -0
  59. package/dist/cli/inspect/module/index.d.ts.map +1 -0
  60. package/dist/cli/inspect/module/index.js +1 -0
  61. package/dist/cli/inspect/module/module.d.ts +7 -0
  62. package/dist/cli/inspect/module/module.d.ts.map +1 -0
  63. package/dist/cli/inspect/module/module.js +15 -0
  64. package/dist/cli/inspect/module/report.d.ts +3 -0
  65. package/dist/cli/inspect/module/report.d.ts.map +1 -0
  66. package/dist/cli/inspect/module/report.js +38 -0
  67. package/dist/cli/inspect/path-tree.d.ts +7 -0
  68. package/dist/cli/inspect/path-tree.d.ts.map +1 -0
  69. package/dist/cli/inspect/path-tree.js +62 -0
  70. package/dist/cli/inspect/path.d.ts +5 -0
  71. package/dist/cli/inspect/path.d.ts.map +1 -0
  72. package/dist/cli/inspect/path.js +10 -0
  73. package/dist/cli/inspect/project/index.d.ts +2 -0
  74. package/dist/cli/inspect/project/index.d.ts.map +1 -0
  75. package/dist/cli/inspect/project/index.js +1 -0
  76. package/dist/cli/inspect/project/project.d.ts +8 -0
  77. package/dist/cli/inspect/project/project.d.ts.map +1 -0
  78. package/dist/cli/inspect/project/project.js +19 -0
  79. package/dist/cli/inspect/project/report.d.ts +3 -0
  80. package/dist/cli/inspect/project/report.d.ts.map +1 -0
  81. package/dist/cli/inspect/project/report.js +16 -0
  82. package/dist/cli/lint/index.d.ts +2 -0
  83. package/dist/cli/lint/index.d.ts.map +1 -0
  84. package/dist/cli/lint/index.js +2 -0
  85. package/dist/cli/lint/layers/index.d.ts +3 -0
  86. package/dist/cli/lint/layers/index.d.ts.map +1 -0
  87. package/dist/cli/lint/layers/index.js +4 -0
  88. package/dist/cli/lint/layers/layers.d.ts +5 -0
  89. package/dist/cli/lint/layers/layers.d.ts.map +1 -0
  90. package/dist/cli/lint/layers/layers.js +18 -0
  91. package/dist/cli/lint/layers/report.d.ts +5 -0
  92. package/dist/cli/lint/layers/report.d.ts.map +1 -0
  93. package/dist/cli/lint/layers/report.js +60 -0
  94. package/dist/cli/lint/lint.d.ts +4 -0
  95. package/dist/cli/lint/lint.d.ts.map +1 -0
  96. package/dist/cli/lint/lint.js +26 -0
  97. package/dist/cli/lint/modules/index.d.ts +5 -0
  98. package/dist/cli/lint/modules/index.d.ts.map +1 -0
  99. package/dist/cli/lint/modules/index.js +4 -0
  100. package/dist/cli/lint/modules/modules.d.ts +5 -0
  101. package/dist/cli/lint/modules/modules.d.ts.map +1 -0
  102. package/dist/cli/lint/modules/modules.js +15 -0
  103. package/dist/cli/lint/modules/report.d.ts +5 -0
  104. package/dist/cli/lint/modules/report.d.ts.map +1 -0
  105. package/dist/cli/lint/modules/report.js +69 -0
  106. package/dist/cli/run.d.ts +1 -8
  107. package/dist/cli/run.d.ts.map +1 -1
  108. package/dist/cli/run.js +14 -242
  109. package/dist/cli/stories/index.d.ts +2 -0
  110. package/dist/cli/stories/index.d.ts.map +1 -0
  111. package/dist/cli/stories/index.js +1 -0
  112. package/dist/cli/stories/progress.d.ts +7 -0
  113. package/dist/cli/stories/progress.d.ts.map +1 -0
  114. package/dist/cli/stories/progress.js +45 -0
  115. package/dist/cli/stories/report.d.ts +9 -0
  116. package/dist/cli/stories/report.d.ts.map +1 -0
  117. package/dist/cli/stories/report.js +63 -0
  118. package/dist/cli/stories/stories.d.ts +6 -0
  119. package/dist/cli/stories/stories.d.ts.map +1 -0
  120. package/dist/cli/stories/stories.js +30 -0
  121. package/dist/domain/architecture-analysis/architecture-analysis.d.ts +4 -0
  122. package/dist/domain/architecture-analysis/architecture-analysis.d.ts.map +1 -0
  123. package/dist/domain/architecture-analysis/architecture-analysis.js +13 -0
  124. package/dist/domain/architecture-analysis/index.d.ts +3 -0
  125. package/dist/domain/architecture-analysis/index.d.ts.map +1 -0
  126. package/dist/domain/architecture-analysis/index.js +2 -0
  127. package/dist/domain/architecture-analysis/layer-rules.d.ts +3 -0
  128. package/dist/domain/architecture-analysis/layer-rules.d.ts.map +1 -0
  129. package/dist/domain/architecture-analysis/layer-rules.js +15 -0
  130. package/dist/domain/architecture-analysis/layers/allowed-dependencies.d.ts +3 -0
  131. package/dist/domain/architecture-analysis/layers/allowed-dependencies.d.ts.map +1 -0
  132. package/dist/domain/architecture-analysis/layers/allowed-dependencies.js +20 -0
  133. package/dist/domain/architecture-analysis/layers/coverage-violations.d.ts +2 -0
  134. package/dist/domain/architecture-analysis/layers/coverage-violations.d.ts.map +1 -0
  135. package/dist/domain/architecture-analysis/layers/coverage-violations.js +5 -0
  136. package/dist/domain/architecture-analysis/layers/dependency-violations.d.ts +5 -0
  137. package/dist/domain/architecture-analysis/layers/dependency-violations.d.ts.map +1 -0
  138. package/dist/domain/architecture-analysis/layers/dependency-violations.js +18 -0
  139. package/dist/domain/architecture-analysis/layers/index.d.ts +9 -0
  140. package/dist/domain/architecture-analysis/layers/index.d.ts.map +1 -0
  141. package/dist/domain/architecture-analysis/layers/index.js +4 -0
  142. package/dist/domain/architecture-analysis/layers/layer-membership.d.ts +3 -0
  143. package/dist/domain/architecture-analysis/layers/layer-membership.d.ts.map +1 -0
  144. package/dist/domain/architecture-analysis/layers/layer-membership.js +13 -0
  145. package/dist/domain/architecture-analysis/layers/layers.d.ts +11 -0
  146. package/dist/domain/architecture-analysis/layers/layers.d.ts.map +1 -0
  147. package/dist/domain/architecture-analysis/layers/layers.js +22 -0
  148. package/dist/domain/architecture-analysis/layers/module-coverage-violations.d.ts +4 -0
  149. package/dist/domain/architecture-analysis/layers/module-coverage-violations.d.ts.map +1 -0
  150. package/dist/domain/architecture-analysis/layers/module-coverage-violations.js +9 -0
  151. package/dist/domain/architecture-analysis/modules/dependency-graph.d.ts +2 -0
  152. package/dist/domain/architecture-analysis/modules/dependency-graph.d.ts.map +1 -0
  153. package/dist/domain/architecture-analysis/modules/dependency-graph.js +45 -0
  154. package/dist/domain/architecture-analysis/modules/dependency-permissions.d.ts +4 -0
  155. package/dist/domain/architecture-analysis/modules/dependency-permissions.d.ts.map +1 -0
  156. package/dist/domain/architecture-analysis/modules/dependency-permissions.js +35 -0
  157. package/dist/domain/architecture-analysis/modules/index.d.ts +12 -0
  158. package/dist/domain/architecture-analysis/modules/index.d.ts.map +1 -0
  159. package/dist/domain/architecture-analysis/modules/index.js +3 -0
  160. package/dist/domain/architecture-analysis/modules/module-descriptors.d.ts +5 -0
  161. package/dist/domain/architecture-analysis/modules/module-descriptors.d.ts.map +1 -0
  162. package/dist/domain/architecture-analysis/modules/module-descriptors.js +37 -0
  163. package/dist/domain/architecture-analysis/modules/module-membership.d.ts +3 -0
  164. package/dist/domain/architecture-analysis/modules/module-membership.d.ts.map +1 -0
  165. package/dist/domain/architecture-analysis/modules/module-membership.js +12 -0
  166. package/dist/domain/architecture-analysis/modules/modules.d.ts +16 -0
  167. package/dist/domain/architecture-analysis/modules/modules.d.ts.map +1 -0
  168. package/dist/domain/architecture-analysis/modules/modules.js +35 -0
  169. package/dist/domain/architecture-analysis/modules/public-entry-points.d.ts +3 -0
  170. package/dist/domain/architecture-analysis/modules/public-entry-points.d.ts.map +1 -0
  171. package/dist/domain/architecture-analysis/modules/public-entry-points.js +11 -0
  172. package/dist/domain/architecture-analysis/modules/violations/boundaries.d.ts +8 -0
  173. package/dist/domain/architecture-analysis/modules/violations/boundaries.d.ts.map +1 -0
  174. package/dist/domain/architecture-analysis/modules/violations/boundaries.js +47 -0
  175. package/dist/domain/architecture-analysis/modules/violations/coverage.d.ts +3 -0
  176. package/dist/domain/architecture-analysis/modules/violations/coverage.d.ts.map +1 -0
  177. package/dist/domain/architecture-analysis/modules/violations/coverage.js +7 -0
  178. package/dist/domain/architecture-analysis/modules/violations/cycles.d.ts +3 -0
  179. package/dist/domain/architecture-analysis/modules/violations/cycles.d.ts.map +1 -0
  180. package/dist/domain/architecture-analysis/modules/violations/cycles.js +7 -0
  181. package/dist/domain/architecture-analysis/modules/violations/dead-modules.d.ts +3 -0
  182. package/dist/domain/architecture-analysis/modules/violations/dead-modules.d.ts.map +1 -0
  183. package/dist/domain/architecture-analysis/modules/violations/dead-modules.js +23 -0
  184. package/dist/domain/architecture-analysis/modules/violations/dependencies.d.ts +4 -0
  185. package/dist/domain/architecture-analysis/modules/violations/dependencies.d.ts.map +1 -0
  186. package/dist/domain/architecture-analysis/modules/violations/dependencies.js +7 -0
  187. package/dist/domain/architecture-analysis/modules/violations/entry-points.d.ts +3 -0
  188. package/dist/domain/architecture-analysis/modules/violations/entry-points.d.ts.map +1 -0
  189. package/dist/domain/architecture-analysis/modules/violations/entry-points.js +17 -0
  190. package/dist/domain/architecture-analysis/modules/violations/graph-coverage.d.ts +3 -0
  191. package/dist/domain/architecture-analysis/modules/violations/graph-coverage.d.ts.map +1 -0
  192. package/dist/domain/architecture-analysis/modules/violations/graph-coverage.js +16 -0
  193. package/dist/domain/architecture-analysis/modules/violations/index.d.ts +3 -0
  194. package/dist/domain/architecture-analysis/modules/violations/index.d.ts.map +1 -0
  195. package/dist/domain/architecture-analysis/modules/violations/index.js +2 -0
  196. package/dist/domain/architecture-analysis/modules/violations/unused-shared.d.ts +3 -0
  197. package/dist/domain/architecture-analysis/modules/violations/unused-shared.d.ts.map +1 -0
  198. package/dist/domain/architecture-analysis/modules/violations/unused-shared.js +9 -0
  199. package/dist/domain/architecture-analysis/modules/violations/violations.d.ts +9 -0
  200. package/dist/domain/architecture-analysis/modules/violations/violations.d.ts.map +1 -0
  201. package/dist/domain/architecture-analysis/modules/violations/violations.js +22 -0
  202. package/dist/domain/file-graph/file-graph.d.ts +10 -0
  203. package/dist/domain/file-graph/file-graph.d.ts.map +1 -0
  204. package/dist/domain/file-graph/file-graph.js +37 -0
  205. package/dist/domain/file-graph/index.d.ts +4 -0
  206. package/dist/domain/file-graph/index.d.ts.map +1 -0
  207. package/dist/domain/file-graph/index.js +1 -0
  208. package/dist/domain/project-config/index.d.ts +10 -0
  209. package/dist/domain/project-config/index.d.ts.map +1 -0
  210. package/dist/domain/project-config/index.js +12 -0
  211. package/dist/domain/project-config/project-config.d.ts +52 -0
  212. package/dist/domain/project-config/project-config.d.ts.map +1 -0
  213. package/dist/domain/project-config/project-config.js +39 -0
  214. package/dist/domain/project-config/resolve.d.ts +8 -0
  215. package/dist/domain/project-config/resolve.d.ts.map +1 -0
  216. package/dist/domain/project-config/resolve.js +29 -0
  217. package/dist/domain/project-config/validation/containment.d.ts +4 -0
  218. package/dist/domain/project-config/validation/containment.d.ts.map +1 -0
  219. package/dist/domain/project-config/validation/containment.js +21 -0
  220. package/dist/domain/project-config/validation/index.d.ts +3 -0
  221. package/dist/domain/project-config/validation/index.d.ts.map +1 -0
  222. package/dist/domain/project-config/validation/index.js +2 -0
  223. package/dist/domain/project-config/validation/layers/cycles.d.ts +3 -0
  224. package/dist/domain/project-config/validation/layers/cycles.d.ts.map +1 -0
  225. package/dist/domain/project-config/validation/layers/cycles.js +64 -0
  226. package/dist/domain/project-config/validation/layers/index.d.ts +3 -0
  227. package/dist/domain/project-config/validation/layers/index.d.ts.map +1 -0
  228. package/dist/domain/project-config/validation/layers/index.js +2 -0
  229. package/dist/domain/project-config/validation/layers/layers.d.ts +3 -0
  230. package/dist/domain/project-config/validation/layers/layers.d.ts.map +1 -0
  231. package/dist/domain/project-config/validation/layers/layers.js +9 -0
  232. package/dist/domain/project-config/validation/layers/overlaps.d.ts +3 -0
  233. package/dist/domain/project-config/validation/layers/overlaps.d.ts.map +1 -0
  234. package/dist/domain/project-config/validation/layers/overlaps.js +26 -0
  235. package/dist/domain/project-config/validation/layers/references.d.ts +3 -0
  236. package/dist/domain/project-config/validation/layers/references.d.ts.map +1 -0
  237. package/dist/domain/project-config/validation/layers/references.js +21 -0
  238. package/dist/domain/project-config/validation/modules/graphs.d.ts +4 -0
  239. package/dist/domain/project-config/validation/modules/graphs.d.ts.map +1 -0
  240. package/dist/domain/project-config/validation/modules/graphs.js +96 -0
  241. package/dist/domain/project-config/validation/modules/index.d.ts +3 -0
  242. package/dist/domain/project-config/validation/modules/index.d.ts.map +1 -0
  243. package/dist/domain/project-config/validation/modules/index.js +2 -0
  244. package/dist/domain/project-config/validation/modules/modules.d.ts +3 -0
  245. package/dist/domain/project-config/validation/modules/modules.d.ts.map +1 -0
  246. package/dist/domain/project-config/validation/modules/modules.js +16 -0
  247. package/dist/domain/project-config/validation/modules/overlaps.d.ts +4 -0
  248. package/dist/domain/project-config/validation/modules/overlaps.d.ts.map +1 -0
  249. package/dist/domain/project-config/validation/modules/overlaps.js +43 -0
  250. package/dist/domain/project-config/validation/modules/paths.d.ts +3 -0
  251. package/dist/domain/project-config/validation/modules/paths.d.ts.map +1 -0
  252. package/dist/domain/project-config/validation/modules/paths.js +27 -0
  253. package/dist/domain/project-config/validation/modules/scopes.d.ts +4 -0
  254. package/dist/domain/project-config/validation/modules/scopes.d.ts.map +1 -0
  255. package/dist/domain/project-config/validation/modules/scopes.js +28 -0
  256. package/dist/domain/project-config/validation/paths.d.ts +3 -0
  257. package/dist/domain/project-config/validation/paths.d.ts.map +1 -0
  258. package/dist/domain/project-config/validation/paths.js +39 -0
  259. package/dist/domain/project-config/validation/validation.d.ts +3 -0
  260. package/dist/domain/project-config/validation/validation.d.ts.map +1 -0
  261. package/dist/domain/project-config/validation/validation.js +12 -0
  262. package/dist/index.d.ts +17 -1
  263. package/dist/index.d.ts.map +1 -1
  264. package/dist/index.js +22 -1
  265. package/dist/orchestrator/analyze-project/analyze-project.d.ts +119 -0
  266. package/dist/orchestrator/analyze-project/analyze-project.d.ts.map +1 -0
  267. package/dist/orchestrator/analyze-project/analyze-project.js +9 -0
  268. package/dist/orchestrator/analyze-project/index.d.ts +2 -0
  269. package/dist/orchestrator/analyze-project/index.d.ts.map +1 -0
  270. package/dist/orchestrator/analyze-project/index.js +2 -0
  271. package/dist/orchestrator/inspect/index.d.ts +4 -0
  272. package/dist/orchestrator/inspect/index.d.ts.map +1 -0
  273. package/dist/orchestrator/inspect/index.js +1 -0
  274. package/dist/orchestrator/inspect/inspect.d.ts +269 -0
  275. package/dist/orchestrator/inspect/inspect.d.ts.map +1 -0
  276. package/dist/orchestrator/inspect/inspect.js +147 -0
  277. package/dist/orchestrator/load-changes/index.d.ts +2 -0
  278. package/dist/orchestrator/load-changes/index.d.ts.map +1 -0
  279. package/dist/orchestrator/load-changes/index.js +2 -0
  280. package/dist/orchestrator/load-changes/load-changes.d.ts +6 -0
  281. package/dist/orchestrator/load-changes/load-changes.d.ts.map +1 -0
  282. package/dist/orchestrator/load-changes/load-changes.js +18 -0
  283. package/dist/orchestrator/load-module-source/errors.d.ts +17 -0
  284. package/dist/orchestrator/load-module-source/errors.d.ts.map +1 -0
  285. package/dist/orchestrator/load-module-source/errors.js +5 -0
  286. package/dist/orchestrator/load-module-source/index.d.ts +2 -0
  287. package/dist/orchestrator/load-module-source/index.d.ts.map +1 -0
  288. package/dist/orchestrator/load-module-source/index.js +1 -0
  289. package/dist/orchestrator/load-module-source/load-module-source.d.ts +12 -0
  290. package/dist/orchestrator/load-module-source/load-module-source.d.ts.map +1 -0
  291. package/dist/orchestrator/load-module-source/load-module-source.js +39 -0
  292. package/dist/orchestrator/load-project/index.d.ts +3 -0
  293. package/dist/orchestrator/load-project/index.d.ts.map +1 -0
  294. package/dist/orchestrator/load-project/index.js +2 -0
  295. package/dist/orchestrator/load-project/load-project.d.ts +54 -0
  296. package/dist/orchestrator/load-project/load-project.d.ts.map +1 -0
  297. package/dist/orchestrator/load-project/load-project.js +30 -0
  298. package/dist/orchestrator/run-stories/errors.d.ts +11 -0
  299. package/dist/orchestrator/run-stories/errors.d.ts.map +1 -0
  300. package/dist/orchestrator/run-stories/errors.js +3 -0
  301. package/dist/orchestrator/run-stories/extract-snippets.d.ts +12 -0
  302. package/dist/orchestrator/run-stories/extract-snippets.d.ts.map +1 -0
  303. package/dist/orchestrator/run-stories/extract-snippets.js +181 -0
  304. package/dist/orchestrator/run-stories/index.d.ts +3 -0
  305. package/dist/orchestrator/run-stories/index.d.ts.map +1 -0
  306. package/dist/orchestrator/run-stories/index.js +3 -0
  307. package/dist/orchestrator/run-stories/run-stories.d.ts +16 -0
  308. package/dist/orchestrator/run-stories/run-stories.d.ts.map +1 -0
  309. package/dist/orchestrator/run-stories/run-stories.js +290 -0
  310. package/dist/services/config/config.d.ts +12 -0
  311. package/dist/services/config/config.d.ts.map +1 -0
  312. package/dist/services/config/config.js +46 -0
  313. package/dist/services/config/errors.d.ts +13 -0
  314. package/dist/services/config/errors.d.ts.map +1 -0
  315. package/dist/services/config/errors.js +3 -0
  316. package/dist/services/config/index.d.ts +4 -0
  317. package/dist/services/config/index.d.ts.map +1 -0
  318. package/dist/services/config/index.js +7 -0
  319. package/dist/services/file-cruiser/errors.d.ts +10 -0
  320. package/dist/services/file-cruiser/errors.d.ts.map +1 -0
  321. package/dist/services/file-cruiser/errors.js +3 -0
  322. package/dist/services/file-cruiser/file-cruiser.d.ts +11 -0
  323. package/dist/services/file-cruiser/file-cruiser.d.ts.map +1 -0
  324. package/dist/services/file-cruiser/file-cruiser.js +7 -0
  325. package/dist/services/file-cruiser/index.d.ts +4 -0
  326. package/dist/services/file-cruiser/index.d.ts.map +1 -0
  327. package/dist/services/file-cruiser/index.js +6 -0
  328. package/dist/services/file-cruiser/oxc/file-scanner.d.ts +5 -0
  329. package/dist/services/file-cruiser/oxc/file-scanner.d.ts.map +1 -0
  330. package/dist/services/file-cruiser/oxc/file-scanner.js +54 -0
  331. package/dist/services/file-cruiser/oxc/import-resolver.d.ts +5 -0
  332. package/dist/services/file-cruiser/oxc/import-resolver.d.ts.map +1 -0
  333. package/dist/services/file-cruiser/oxc/import-resolver.js +44 -0
  334. package/dist/services/file-cruiser/oxc/index.d.ts +2 -0
  335. package/dist/services/file-cruiser/oxc/index.d.ts.map +1 -0
  336. package/dist/services/file-cruiser/oxc/index.js +2 -0
  337. package/dist/services/file-cruiser/oxc/oxc.d.ts +5 -0
  338. package/dist/services/file-cruiser/oxc/oxc.d.ts.map +1 -0
  339. package/dist/services/file-cruiser/oxc/oxc.js +29 -0
  340. package/dist/services/git/changed-paths.d.ts +4 -0
  341. package/dist/services/git/changed-paths.d.ts.map +1 -0
  342. package/dist/services/git/changed-paths.js +85 -0
  343. package/dist/services/git/errors.d.ts +11 -0
  344. package/dist/services/git/errors.d.ts.map +1 -0
  345. package/dist/services/git/errors.js +3 -0
  346. package/dist/services/git/git.d.ts +12 -0
  347. package/dist/services/git/git.d.ts.map +1 -0
  348. package/dist/services/git/git.js +23 -0
  349. package/dist/services/git/index.d.ts +4 -0
  350. package/dist/services/git/index.d.ts.map +1 -0
  351. package/dist/services/git/index.js +6 -0
  352. package/dist/services/git/parse-patch.d.ts +3 -0
  353. package/dist/services/git/parse-patch.d.ts.map +1 -0
  354. package/dist/services/git/parse-patch.js +53 -0
  355. package/dist/services/git/run-git.d.ts +9 -0
  356. package/dist/services/git/run-git.d.ts.map +1 -0
  357. package/dist/services/git/run-git.js +22 -0
  358. package/dist/story/index.d.ts +3 -0
  359. package/dist/story/index.d.ts.map +1 -0
  360. package/dist/story/index.js +2 -0
  361. package/dist/story/schema/index.d.ts +6 -0
  362. package/dist/story/schema/index.d.ts.map +1 -0
  363. package/dist/story/schema/index.js +4 -0
  364. package/dist/story/schema/slug.d.ts +2 -0
  365. package/dist/story/schema/slug.d.ts.map +1 -0
  366. package/dist/story/schema/slug.js +6 -0
  367. package/dist/story/schema/story-report-schema.d.ts +408 -0
  368. package/dist/story/schema/story-report-schema.d.ts.map +1 -0
  369. package/dist/story/schema/story-report-schema.js +94 -0
  370. package/dist/story/schema/story-tree-schema.d.ts +42 -0
  371. package/dist/story/schema/story-tree-schema.d.ts.map +1 -0
  372. package/dist/story/schema/story-tree-schema.js +40 -0
  373. package/dist/story/story.d.ts +38 -0
  374. package/dist/story/story.d.ts.map +1 -0
  375. package/dist/story/story.js +56 -0
  376. package/package.json +36 -24
  377. package/schema.json +212 -0
  378. package/dist/cli/index.d.ts +0 -2
  379. package/dist/cli/index.d.ts.map +0 -1
  380. package/dist/cli/index.js +0 -1
  381. package/dist/config/define-config.d.ts +0 -3
  382. package/dist/config/define-config.d.ts.map +0 -1
  383. package/dist/config/define-config.js +0 -228
  384. package/dist/config/index.d.ts +0 -5
  385. package/dist/config/index.d.ts.map +0 -1
  386. package/dist/config/index.js +0 -4
  387. package/dist/config/layer-graph.d.ts +0 -7
  388. package/dist/config/layer-graph.d.ts.map +0 -1
  389. package/dist/config/layer-graph.js +0 -53
  390. package/dist/config/layer.d.ts +0 -5
  391. package/dist/config/layer.d.ts.map +0 -1
  392. package/dist/config/layer.js +0 -21
  393. package/dist/config/module.d.ts +0 -9
  394. package/dist/config/module.d.ts.map +0 -1
  395. package/dist/config/module.js +0 -29
  396. package/dist/config/path.d.ts +0 -4
  397. package/dist/config/path.d.ts.map +0 -1
  398. package/dist/config/path.js +0 -32
  399. package/dist/config/types.d.ts +0 -36
  400. package/dist/config/types.d.ts.map +0 -1
  401. package/dist/config/types.js +0 -1
  402. package/dist/engine/1-extract/extract.d.ts +0 -12
  403. package/dist/engine/1-extract/extract.d.ts.map +0 -1
  404. package/dist/engine/1-extract/extract.js +0 -160
  405. package/dist/engine/1-extract/index.d.ts +0 -3
  406. package/dist/engine/1-extract/index.d.ts.map +0 -1
  407. package/dist/engine/1-extract/index.js +0 -1
  408. package/dist/engine/2-resolve/index.d.ts +0 -3
  409. package/dist/engine/2-resolve/index.d.ts.map +0 -1
  410. package/dist/engine/2-resolve/index.js +0 -1
  411. package/dist/engine/2-resolve/resolve.d.ts +0 -24
  412. package/dist/engine/2-resolve/resolve.d.ts.map +0 -1
  413. package/dist/engine/2-resolve/resolve.js +0 -74
  414. package/dist/engine/3-evaluate/evaluate.d.ts +0 -10
  415. package/dist/engine/3-evaluate/evaluate.d.ts.map +0 -1
  416. package/dist/engine/3-evaluate/evaluate.js +0 -80
  417. package/dist/engine/3-evaluate/index.d.ts +0 -3
  418. package/dist/engine/3-evaluate/index.d.ts.map +0 -1
  419. package/dist/engine/3-evaluate/index.js +0 -1
  420. package/dist/engine/4-emit/emit.d.ts +0 -7
  421. package/dist/engine/4-emit/emit.d.ts.map +0 -1
  422. package/dist/engine/4-emit/emit.js +0 -77
  423. package/dist/engine/4-emit/index.d.ts +0 -2
  424. package/dist/engine/4-emit/index.d.ts.map +0 -1
  425. package/dist/engine/4-emit/index.js +0 -1
  426. package/dist/engine/errors.d.ts +0 -19
  427. package/dist/engine/errors.d.ts.map +0 -1
  428. package/dist/engine/errors.js +0 -5
  429. package/dist/node.d.ts +0 -24
  430. package/dist/node.d.ts.map +0 -1
  431. package/dist/node.js +0 -139
  432. package/dist/report/architecture.d.ts +0 -104
  433. package/dist/report/architecture.d.ts.map +0 -1
  434. package/dist/report/architecture.js +0 -1
  435. package/dist/report/index.d.ts +0 -3
  436. package/dist/report/index.d.ts.map +0 -1
  437. package/dist/report/index.js +0 -2
  438. package/dist/report/stories.d.ts +0 -22
  439. package/dist/report/stories.d.ts.map +0 -1
  440. package/dist/report/stories.js +0 -1
  441. package/dist/story/artifact/artifact.d.ts +0 -57
  442. package/dist/story/artifact/artifact.d.ts.map +0 -1
  443. package/dist/story/artifact/artifact.js +0 -234
  444. package/dist/story/artifact/index.d.ts +0 -2
  445. package/dist/story/artifact/index.d.ts.map +0 -1
  446. package/dist/story/artifact/index.js +0 -1
  447. package/dist/story/artifact/storage.d.ts +0 -10
  448. package/dist/story/artifact/storage.d.ts.map +0 -1
  449. package/dist/story/artifact/storage.js +0 -162
  450. package/dist/story/artifact/types.d.ts +0 -74
  451. package/dist/story/artifact/types.d.ts.map +0 -1
  452. package/dist/story/artifact/types.js +0 -1
  453. package/dist/story/core/blocks.d.ts +0 -20
  454. package/dist/story/core/blocks.d.ts.map +0 -1
  455. package/dist/story/core/blocks.js +0 -104
  456. package/dist/story/core/declare.d.ts +0 -63
  457. package/dist/story/core/declare.d.ts.map +0 -1
  458. package/dist/story/core/declare.js +0 -62
  459. package/dist/story/core/index.d.ts +0 -4
  460. package/dist/story/core/index.d.ts.map +0 -1
  461. package/dist/story/core/index.js +0 -1
  462. package/dist/story/core/recorder.d.ts +0 -38
  463. package/dist/story/core/recorder.d.ts.map +0 -1
  464. package/dist/story/core/recorder.js +0 -28
  465. package/dist/story/core/runtime-context.d.ts +0 -14
  466. package/dist/story/core/runtime-context.d.ts.map +0 -1
  467. package/dist/story/core/runtime-context.js +0 -19
  468. package/dist/story/core/types.d.ts +0 -21
  469. package/dist/story/core/types.d.ts.map +0 -1
  470. package/dist/story/core/types.js +0 -1
  471. package/dist/story/effect/effect.d.ts +0 -52
  472. package/dist/story/effect/effect.d.ts.map +0 -1
  473. package/dist/story/effect/effect.js +0 -99
  474. package/dist/story/effect/index.d.ts +0 -3
  475. package/dist/story/effect/index.d.ts.map +0 -1
  476. package/dist/story/effect/index.js +0 -1
  477. package/dist/story/runner/index.d.ts +0 -3
  478. package/dist/story/runner/index.d.ts.map +0 -1
  479. package/dist/story/runner/index.js +0 -1
  480. package/dist/story/runner/runner.d.ts +0 -44
  481. package/dist/story/runner/runner.d.ts.map +0 -1
  482. package/dist/story/runner/runner.js +0 -476
  483. package/dist/story/story-runtime/index.d.ts +0 -2
  484. package/dist/story/story-runtime/index.d.ts.map +0 -1
  485. package/dist/story/story-runtime/index.js +0 -1
  486. package/dist/story/story-runtime/story-runtime.d.ts +0 -52
  487. package/dist/story/story-runtime/story-runtime.d.ts.map +0 -1
  488. package/dist/story/story-runtime/story-runtime.js +0 -194
  489. package/dist/story/vanilla/index.d.ts +0 -29
  490. package/dist/story/vanilla/index.d.ts.map +0 -1
  491. package/dist/story/vanilla/index.js +0 -78
package/README.md CHANGED
@@ -1,740 +1,167 @@
1
1
  # Laymos
2
2
 
3
- **Layers. Modules. Stories.**
4
-
5
- One config that declares your architecture. From it: enforcement rules, and the diagram — the same artifact. The picture you show in onboarding is provably true, because it's generated from the thing that's enforced.
6
-
7
- The pitch in one line: **"Declare your architecture once; get enforcement and the diagram for free."**
8
-
9
- Successor to `depcruise-viz`. Retired with it: stacks, features, barrels, feature closure.
10
-
11
- ---
12
-
13
- ## The Problem
14
-
15
- Every team draws an architecture diagram. Every team writes lint rules. They drift apart. The diagram in the wiki lies; the rules in the repo are unreadable. Laymos makes them one thing.
16
-
17
- There are three pillars, each answering a different question:
18
-
19
- - **Layers** — what are the big boundaries, and who may import whom?
20
- - **Modules** — within a boundary, what are the units, and how do they connect?
21
- - **Stories** — when code actually _runs_, what path does it take?
22
-
23
- Layers and modules are static. Stories are runtime. Together: intent, structure, behavior.
24
-
25
- ---
26
-
27
- ## Part 1: Layers
28
-
29
- ### What a layer is
30
-
31
- A layer is a **set of folders or files**.
32
-
33
- - Paths are **plain prefixes** — a path names a file or a directory subtree. No glob patterns; scattered folders are handled by listing multiple paths.
34
- - Nested paths: **longest matching prefix wins.** Disjointness follows by construction — the only invalid overlap is two layers declaring the identical path, a config error.
35
- - Layers are **explicit**. No implicit layers, no "open" layers that everything can import. If a layer is importable by all, every edge is drawn.
36
-
37
- _Why:_ explicitness is the product. Implicit permissions are exactly the ambiguity laymos exists to remove.
38
-
39
- ### The layer graph
40
-
41
- A layer graph is a **DAG** of layers.
42
-
43
- - An edge means **"may import."**
44
- - **No edge = forbidden.** Siblings without an edge cannot import each other.
45
- - Reachability is **transitive**: if A→B and B→C, then A may import C.
46
- - Cycles in a graph → config error, validated at config time.
47
-
48
- _Why transitive:_ it matches the general flow of software — a root can reach its descendants. We chose relaxed layering over strict layering to avoid noisy edge declarations.
49
-
50
- _Why DAG, not tree:_ a "domain" layer importable by many parents means multiple incoming edges. That's a DAG. A tree can't express it.
51
-
52
- ### Multiple graphs, one truth
53
-
54
- A project can define **multiple layer graphs** — e.g. one for frontend, one for backend.
55
-
56
- - A layer **may appear in more than one graph**. There is no special "shared layer" kind — just layers, sometimes used in multiple graphs.
57
- - The **union of all graphs must be acyclic.** A→B in one graph and B→A in another is a config error. So is any longer cycle formed across graphs.
58
- - Rules are generated from the **union**. Graphs are how you organize and communicate; the union is what's enforced.
59
- - Reachability follows that union, including paths whose edges are organized into different graphs. Each graph remains a focused view; together they declare the complete architecture.
60
-
61
- ### Scale
62
-
63
- Target: **under 10 layers per graph**, slightly above 10 in the extreme. Layers are the bird's-eye view. Hundreds of layers means you're doing modules' job with layers.
64
-
65
- ---
66
-
67
- ## Part 2: Modules
68
-
69
- ### What a module is
70
-
71
- A module is a **file or a folder**.
72
-
73
- - Modules are **strictly flat** — no module inside another module.
74
- - A module lives in exactly one layer: the **longest layer prefix containing its path**. Inferred from the path, never declared.
75
- - A module cannot straddle a layer boundary — no layer path may sit inside a module's path (validated structurally, at config time).
76
- - A layer itself can be a module.
77
-
78
- _Why flat:_ nesting modules recreates the hierarchy problem layers already solve. One level of granularity per pillar.
79
-
80
- ### Module rules are opt-in
81
-
82
- Defining a module surfaces **no errors by itself**. Modules default-allow; layers default-deny. This asymmetry is deliberate and worth stating loudly:
83
-
84
- - **Layers:** drawing the graph _is_ writing the rules. Absence of an edge forbids.
85
- - **Modules:** defining modules is organization. Rules are opted into per module.
86
-
87
- _Why:_ layers are few and architectural — full intent is cheap to declare. Modules are many — forcing rules on all of them would make adoption impossible.
88
-
89
- ### The two module rules
90
-
91
- A module may declare constraints on its edges, in either direction:
92
-
93
- - **`canImport: [...]`** — this module may only import the listed modules. Disciplines a _consumer_.
94
- - **`canImportedBy: [...]`** — only the listed modules may import this one. Protects a _provider_.
95
-
96
- We kept both because neither is expressible with only the other — dropping one means scattering inverted rules across every other module. It's one concept ("module constraints") with two fields, not two rule systems.
97
-
98
- Resolution law: **AND semantics, deny wins.** An import A→B is legal iff A's outgoing rule (if configured) allows B _and_ B's incoming rule (if configured) allows A. An unconfigured side has no opinion. Nothing can loosen; rules only tighten.
99
-
100
- Intra-module imports (files within the same module) always bypass module rules.
101
-
102
- Module rules may constrain modules in the same layer or across layers. A
103
- cross-layer import must satisfy both systems: the layer graph must permit it,
104
- and both modules' configured constraints must permit it. Module rules can
105
- tighten the layer graph but cannot grant permission that the graph denies.
106
-
107
- ### Layers and modules are decoupled
108
-
109
- - **Layer rules bind every file in a layer**, module'd or not.
110
- - **Module rules bind only module'd files.**
111
-
112
- _Why:_ an earlier draft made layer enforcement route through modules ("only module'd files play layer rules"). That created a blind spot — layer enforcement, the core feature, would depend on adoption of the opt-in feature. Decoupling kills the blind spot and is also simpler: both rule passes run over the same file-level edge graph, aggregated to different units.
113
-
114
- One import can violate both a layer rule and a module rule. Both are reported. No dedupe — they're different intents failing.
115
-
116
- ---
117
-
118
- ## Part 3: Coverage & Ignore
119
-
120
- Two independent coverage metrics, both **warnings, never errors**:
121
-
122
- 1. **Layer coverage** — of all files in the repo, how many belong to some layer? Uncovered files are flagged.
123
- 2. **Module coverage** — within a layer, how many files belong to some module?
124
-
125
- _Why warnings:_ a brownfield repo cannot reach 100% before the first useful run. Coverage is the ratchet, not the gate. Coverage is shown per layer in the viz, nudging teams toward full module definition.
126
-
127
- A layer with zero modules is valid config — just 0% module coverage.
128
-
129
- The source inventory comes only from configured `sourceRoots`. Each root is a
130
- project-relative file or directory; directories are traversed recursively.
131
- Laymos analyzes JavaScript and TypeScript source files (`js`, `jsx`, `mjs`,
132
- `cjs`, `ts`, `tsx`); declarations, minified files, and non-source assets are
133
- outside the inventory. Git tracking and `.gitignore` never affect analysis.
134
-
135
- **Ignore** is a single global set of folders/files, shared by layers and modules:
136
-
137
- - Ignored means **invisible, not permitted** — no rules generated, no coverage warnings, imports to/from them unchecked.
138
- - Layered code may freely import ignored files.
139
- - Ignore beats layer paths on conflict. It is the escape hatch (generated code, composition roots, anything).
140
- - Explicitly ignored files remain tagged as ignored in the report so the escape hatch is auditable, but their edges are removed and they do not count toward coverage.
141
-
142
- ---
143
-
144
- ## Part 4: Enforcement & Execution
145
-
146
- ### The check contract
147
-
148
- - **Config errors** (overlapping layers, union cycles, module straddling) → hard fail.
149
- - **Rule violations** (layer or module) → fail. Lint errors.
150
- - **Coverage** → warning. Never fails CI.
151
-
152
- `import type` **counts as a violation.** Type-only imports cross boundaries at the type level; intent is enforced regardless of runtime erasure. (Reservation on record: shared contract types across frontend/backend — `import type { AppRouter }` — will pressure this rule. Escape hatch deferred; `ignore` covers it for now.)
153
-
154
- ### How checking actually works
155
-
156
- [skott](https://github.com/antoine-coulon/skott) is used as an **extractor only** — run with type-only tracking always on, producing the full file-level import graph (it resolves ESM/CJS/`import type`/TS path aliases). Laymos's own engine owns all semantics:
157
-
158
- **extract (skott) → resolve (files → layers/modules) → evaluate (rules) → emit (violations + viz data)**
159
-
160
- _Why skott over dependency-cruiser_ (see ADR-0001): identical edge sets in a head-to-head POC, ~30% faster, far simpler graph API. _Why not transpile laymos config into an existing rule engine:_ our semantics (transitive reachability, sink layers, AND-gated module constraints) would compile into O(n²) regex pair-lists with foreign error messages and a transpiler to debug forever. Owning the engine means one evaluation produces both enforcement and visualization, with no drift between them.
161
-
162
- ### Violations are rich objects
163
-
164
- Verdicts are module/layer-level; **evidence is file-level.** (Line numbers and per-edge import kind were considered and dropped — no extractor provides them; both are additive later. ADR-0001.)
165
-
166
- ```ts
167
- { kind: "layer", from: { layer, file }, to: { layer, file } }
168
- { kind: "module", rule: "canImport" | "canImportedBy",
169
- from: { module, layer, file }, to: { module, layer, file } }
3
+ Laymos enforces architectural dependency rules. It also provides focused
4
+ dependency queries for exploring and understanding a codebase.
5
+
6
+ ## Architecture
7
+
8
+ Declare Layers, Modules, and LayerGraphs in a plain `laymos.config.json`.
9
+
10
+ A **Layer** is a named group of literal project-relative files and folders.
11
+ Layers partition the supported files beneath `sourceRoots`: every included
12
+ file belongs to exactly one Layer, and no declared Layer scopes may overlap.
13
+ Use `ignoredPaths` to remove files or folders from analysis explicitly.
14
+
15
+ A **LayerGraph** is a named set of **rules** representing one responsibility
16
+ (e.g. core architecture or test boundaries). It is an organizational grouping,
17
+ not an enforcement boundary. A project may have no LayerGraphs when no
18
+ cross-Layer imports are allowed.
19
+
20
+ Each rule maps a layer id to the layer ids it may directly depend on. Rules
21
+ are default-deny: any dependency between two layers with no declared path
22
+ between them — direct or transitive, across every LayerGraph combined — is a
23
+ violation. Permission is transitive, so only direct edges need declaring (if
24
+ `app` may depend on `domain` and `domain` may depend on `infra`, `app` may
25
+ depend on `infra` without declaring it explicitly). The combined graph must be
26
+ acyclic. A layer with no outgoing rule is a valid, intentional leaf.
27
+
28
+ A **Module** is a self-contained source boundary backed by a supported source
29
+ file or directory. A **Configured Module** is an explicit, disjoint boundary
30
+ within one Layer, and every included file belongs to one. Its `kind` is Normal
31
+ by default. Marking it `shared` lets peers in the same Layer import it; Layer
32
+ Rules still decide cross-Layer access. An Entry Module may depend on other
33
+ Modules but cannot be imported by one.
34
+
35
+ A Normal or Shared File Module is its own public entry point. A Normal or Shared
36
+ Directory Module requires a root `index.ts`. List an exact directory path in
37
+ `subpaths` to add another public `index.ts` for tree shaking. Entry Modules
38
+ follow their host's file convention and cannot declare Subpaths.
39
+
40
+ ```json
41
+ {
42
+ "$schema": "https://unpkg.com/laymos/schema.json",
43
+ "sourceRoots": ["src"],
44
+ "ignoredPaths": ["src/generated"],
45
+ "layers": {
46
+ "app": { "paths": ["src/app"], "description": "Application" },
47
+ "domain": { "paths": ["src/domain"], "description": "Domain" },
48
+ "infra": { "paths": ["src/infra"] }
49
+ },
50
+ "modules": {
51
+ "src/app": { "kind": "entry" },
52
+ "src/domain/orders": {},
53
+ "src/domain/catalog": { "kind": "shared", "subpaths": ["events"] },
54
+ "src/infra": {}
55
+ },
56
+ "layerGraphs": {
57
+ "architecture": {
58
+ "description": "Core layering",
59
+ "rules": {
60
+ "app": ["domain"],
61
+ "domain": ["infra"]
62
+ }
63
+ }
64
+ }
65
+ }
170
66
  ```
171
67
 
172
- Config speaks modules; violations point at files. That's not an inconsistency — config is intent, evidence is fact.
68
+ No package dependency is required to author or consume this file — the
69
+ `$schema` key gives editors autocomplete/validation directly, and any other
70
+ tool (including a separate devtools server rendering the project's
71
+ architecture) can read it as plain JSON. See
72
+ [ADR-0003](docs/adr/0003-json-config-over-typescript.md) for why.
173
73
 
174
- ### Config
175
-
176
- One file: **`laymos.config.ts`**, typed default export built with reference builders — cross-references are values, not strings, so they autocomplete and survive renames. Cost accepted: laymos executes user code to read config.
177
-
178
- `defineConfig` validates everything decidable from the config alone and throws with all issues at once. Source roots must be non-empty and non-overlapping;
179
- every layer, module, and ignored path must fall within one. The loader
180
- additionally warns — never errors — on declared paths that don't exist on disk:
181
- declaring structure before its folder exists is legitimate intent-first design.
182
-
183
- Every module used by a rule must also appear in `modules`, and each module may
184
- have at most one `rules(...)` entry. Rule references must reuse those declared
185
- module values rather than recreating the same path.
74
+ ## Library API
186
75
 
187
76
  ```ts
188
- import { defineConfig, edge, layer, layerGraph, module, rules } from 'laymos';
189
-
190
- const ui = layer('ui', ['src/ui']);
191
- const domain = layer('domain', ['src/domain']);
192
- const data = layer('data', ['src/data']);
193
-
194
- const billing = module('src/domain/billing');
195
- const checkout = module('src/domain/checkout');
77
+ import { Effect } from 'effect';
78
+ import { analyzeProject } from 'laymos';
196
79
 
197
- export default defineConfig({
198
- sourceRoots: ['src'],
199
- graphs: [layerGraph('app', [edge(ui, [domain, data]), edge(domain, data)])],
200
- modules: [billing, checkout],
201
- moduleRules: [rules(billing, { canImportedBy: [checkout] })],
202
- ignore: ['src/generated'],
203
- });
80
+ const analysis = await Effect.runPromise(
81
+ analyzeProject('/absolute/project/laymos.config.json'),
82
+ );
204
83
  ```
205
84
 
206
- Modules are declared and constrained in **two separate acts** (`module()` then `rules()`): mutually-referencing rules would otherwise hit the JS temporal dead zone between `const` bindings — and it mirrors the semantics, since declaring a module imposes nothing.
207
-
208
- Configured source roots and paths are project-root-relative plain paths. `src/x` and `./src/x`
209
- are equivalent; separators and redundant segments are normalized. Absolute
210
- paths, escaping `..`, and glob syntax are rejected.
211
-
212
- ---
213
-
214
- ## Part 5: Visualization (principles only — details deferred)
215
-
216
- Two things are always shown, distinctly:
217
-
218
- 1. **Declared** — the rules you configured.
219
- 2. **Actual** — the real state of the codebase, from the extracted graph.
220
-
221
- Per edge, three render states: **declared & used** (healthy), **declared & never used** (dead edge — candidate for tightening), **used & violating**. The allowed/used/violating triad is the product.
222
-
223
- The viz payload is self-contained: the labeled file tree plus layer-level rollups, correlated by root-relative file paths — no joins against other outputs needed.
224
-
225
- Graphs render side by side; a layer used across graphs spans them horizontally. Selecting a layer or module shows its actual edges — what it imports, what imports it — not just its rules. Rules are validations, never an exhaustive map; the actual state fills the rest.
226
-
227
- ---
85
+ `analyzeProject` returns `ArchitectureAnalysis`: the decoded Config plus Layer
86
+ and Module analysis. `ArchitectureAnalysisSchema` is its runtime and transport
87
+ contract; its Maps and Sets support Effect Schema's canonical JSON codec.
228
88
 
229
- ## Part 6: Stories
230
-
231
- ### The idea
232
-
233
- Layers and modules are static intent. Stories capture **runtime behavior at the intent level** — a flowchart of what actually happened, where "do this" might be a thousand lines but is one node.
234
-
235
- Not coverage tooling (istanbul knows _which lines_ ran, not _what they meant_). Not XState (we don't make the flowchart be the code — code stays code, the flowchart is derived; annotate what exists, no rewrite).
236
-
237
- ### The primitives — exactly three
238
-
239
- - **`functionBlock(name, meta, fn)`** — a named function-boundary block that may contribute to any number of Stories. Its metadata must describe what the boundary means in the narrative.
240
- - **`step(name, meta, fn)`** — a named inline block whose metadata must describe the action's narrative purpose.
241
- - **`decision(name, meta, value)`** — a statement-first, expression-capable condition over a string, number, or boolean literal union. Each chained `when(literal, armMeta, fn)` declares one Arm and eagerly executes the matching branch. Decision and Arm metadata must describe the choice and consequence.
89
+ Browser and RPC code should import the contract-only entrypoint:
242
90
 
243
91
  ```ts
244
- decision(
245
- 'fraud gate',
246
- { description: 'Reject high-risk orders' },
247
- score > 0.7 ? 'rejected' : 'approved',
248
- )
249
- .when(
250
- 'approved',
251
- { name: 'Accept order', description: 'Continue to payment' },
252
- () => processPayment(order),
253
- )
254
- .when(
255
- 'rejected',
256
- {
257
- name: 'Reject order',
258
- description: 'Stop checkout before any payment is captured',
259
- },
260
- () => rejectOrder(order),
261
- )
262
- .exhaustive();
92
+ import {
93
+ ArchitectureAnalysisSchema,
94
+ type ArchitectureAnalysis,
95
+ } from 'laymos/architecture-analysis-schema';
263
96
  ```
264
97
 
265
- Each `when` removes its literal from the remaining input union and narrows its
266
- callback to that literal. `exhaustive()` is optional when the chain's result is
267
- ignored; when present, it is callable only after the union is fully handled and
268
- returns the selected branch's result. `otherwise(armMeta, fn)` declares one
269
- fallback Arm, narrows its callback to the remaining union, and returns the
270
- selected result. A non-exhaustive chain with no matching Arm does nothing, like
271
- an `if` statement without an `else`. Literal Arms default their narrative name
272
- to the textual form of their literal; the fallback defaults to `Otherwise`.
273
-
274
- `laymos/story` provides `yield*`-able Effect builders. Arm callbacks return
275
- Effects; the matching Effect is selected eagerly and runs when the builder is
276
- yielded. `exhaustive()` remains an optional type-level proof (ADR-0002).
277
-
278
- Active execution context propagates across Effect fibers. Sequential visits
279
- append to the active execution path; visits that overlap while unfinished
280
- become parallel branches. Each concurrent branch must
281
- be spanned by a marked Block for laymos to reconstruct its nested sequence
282
- reliably. A visit the recorder cannot unambiguously place — it starts while a
283
- sibling visit is unfinished and no marked Block spans its branch — **fails the
284
- Scenario's recording** with an error naming both blocks and their source
285
- locations and the fix: wrap each concurrent branch in a block. Ambiguous
286
- structure is never recorded best-effort; an artifact that finalizes is correct,
287
- not approximate.
288
-
289
- Every block callback also establishes a parent scope. `functionBlock`, `step`, and
290
- the selected arm of `decision` may therefore contain visits recursively at any
291
- depth. A `step` can be either a leaf action or a meaningful narrative grouping;
292
- there is no separate grouping block.
293
-
294
- Each block carries a required, non-empty static **description** and optional
295
- per-invocation **attributes** — each scenario stamps its own attribute values
296
- into its block visits. Metadata is the mandatory second argument to every
297
- primitive, and every Decision Arm also requires metadata with a non-empty
298
- description. Story and Scenario descriptions follow the same rule.
299
-
300
- Dynamic attributes belong only to visits. They never participate in block
301
- identity or folding. Arguments, return values, and errors are never captured
302
- automatically.
303
- The public type is `Readonly<Record<string, unknown>>`. The `attributes` field
304
- accepts either a record or a resolver: function arguments are supplied to a
305
- `functionBlock` resolver, the selected literal to a `decision` resolver, and no
306
- arguments to a `step` resolver. Laymos JSON-serializes the supplied record and
307
- stores the resulting JSON data rather than retaining a live object reference.
308
- In recording mode, serialization failure throws a dedicated error containing
309
- the Block name and source location, causing the Scenario to fail. Values
310
- that serialize follow native `JSON.stringify` semantics. When
311
- no Scenario recorder is active, block wrappers do not evaluate or serialize
312
- attribute resolvers.
313
-
314
- _Why declared arms:_ runtime-only tracing can't know an un-run branch exists. Declaring arms gives the tracer the decision's known outcomes. **Accepted limitation:** blocks _inside_ a never-taken arm, or in never-loaded files, are invisible. The arm is shown as unobserved without implying that the story is incomplete. No static analysis for stories — stories are runtime, period.
98
+ This entrypoint contains data schemas and types only. Project analysis remains
99
+ available from the Node-oriented root entrypoint.
315
100
 
316
- The Decision Block definition owns every declared Arm. A literal Arm has its
317
- literal structural key; the Otherwise Arm has a reserved internal key. Both
318
- have a required narrative description and may have a distinct narrative name.
319
- Each Decision Visit stores
320
- its `selectedArm`, including when the selected Arm later fails, and its generic
321
- `children` execution path contains everything observed within that Arm. The
322
- Execution Path needs no Decision-specific item type. Every Decision Visit has
323
- a selected Arm; computing the input value happens before the Decision, just as
324
- an `if` condition is evaluated before its body.
325
-
326
- _Why explicit wrappers despite invasiveness:_ comment directives orphan on refactor; there's no free lunch, only choosing who pays. Bounded by guidance: **wrap functions, not if-statements.** `decision` is the only blessed intra-function construct. If a team finds that intolerable, stories aren't for them — opt-in per flow, like modules.
327
-
328
- Block identity is generated from the project-relative file, line, and column reported by `Error().stack`. Names and descriptions are narrative metadata, not identifiers. Laymos does not resolve source maps or preserve identity across generations: moving code and rerunning the suite intentionally produces a new story artifact.
329
-
330
- ### What a story is
331
-
332
- A Story is one executable implementation narrative for a feature or use case.
333
- Every Scenario prepares different conditions, invokes that same execution
334
- exactly once, and intentionally verifies its result. Their observations
335
- converge into one explanation of how the implementation logic works. A Story
336
- describes only explicitly marked Blocks and makes no completeness claim about
337
- the surrounding code or use case.
338
-
339
- - **One story per `<story-name>.story.ts` file** — hard convention, not suggestion. The kebab-case file is the discovery unit; the declared Story name remains independent human-facing metadata. The name deliberately does not contain `.test`, so no test runner ever picks a Story up: stories are not tests.
340
- - A discovered Story file that declares zero Stories or more than one Story is
341
- an invalid definition. It cannot produce a per-Story artifact and causes the
342
- execution API to reject rather than returning a test-failure result.
343
- - The Story ID is that file's project-relative path. A Story leaf name is
344
- unique among siblings, while the same leaf name may appear in different
345
- Story Groups. Moving the file intentionally creates a new execution identity
346
- on the next generation.
347
- - Each Scenario prepares one explicit value for the shared Story execution and
348
- verifies either its success value or typed error. Preparation, verification,
349
- and optional cleanup are operational phases outside the narrative.
350
- - Scenarios form one flat builder chain. Narrative nesting belongs to Block
351
- Visits, not to the Story/Scenario hierarchy.
352
- - Scenario names are narrative metadata and need not be unique. A Scenario has
353
- no generated identifier: it is identified by its declaration position within
354
- the Story, which the flat synchronous list makes total and deterministic.
355
- - Scenarios run sequentially in declaration order, each at most once per
356
- generation. There are no retries and no concurrency between Scenarios;
357
- parallelism inside a Scenario is recorded explicitly by the Execution Path.
358
- - A Scenario outcome is `succeeded`, `failed`, `interrupted`, or `skipped`, as
359
- determined by the Story runner. An expected typed execution error can produce
360
- a successful Scenario through `verifyError`; defects and interruptions never
361
- can. Phase failures remain distinguishable. Skipped Scenarios contain no
362
- visits.
363
- - Story, Scenario, Block, Decision, and Arm descriptions are required and must
364
- not be empty. Attributes and custom Arm names remain optional.
101
+ Load the current source snapshot for one Configured Module on demand:
365
102
 
366
103
  ```ts
367
- // checkout.story.ts
368
104
  import { Effect } from 'effect';
369
- import { storyGroup } from 'laymos/story';
370
-
371
- const commerce = storyGroup('Commerce', {
372
- description: 'Customer purchase behavior',
373
- });
374
- const checkout = commerce.group('Checkout', {
375
- description: 'Order placement and payment behavior',
376
- });
105
+ import { loadModuleSource } from 'laymos';
377
106
 
378
- checkout
379
- .story('Place an order', {
380
- description: 'Places an order after inventory and payment approval',
381
- })
382
- .provide(AppLive)
383
- .execute((prepared: CheckoutWorld) => checkout(prepared.orderId))
384
- .scenario(
385
- 'happy path',
386
- {
387
- description: 'Completes an eligible order',
388
- },
389
- (scenario) =>
390
- scenario
391
- .prepare(() => seedEligibleOrder())
392
- .verify((result, prepared) => verifyCompleted(result, prepared))
393
- .cleanup((prepared) => removeOrder(prepared.orderId)),
394
- )
395
- .scenario(
396
- 'fraud rejected',
397
- {
398
- description: 'Rejects a high-risk order before payment authorization',
399
- timeout: '2 minutes',
400
- },
401
- (scenario) =>
402
- scenario
403
- .prepare(() => seedRejectedOrder())
404
- .verifyError((error, prepared) => verifyRejection(error, prepared)),
405
- );
107
+ const snapshot = await Effect.runPromise(
108
+ loadModuleSource('/absolute/project/laymos.config.json', 'src/domain/orders'),
109
+ );
406
110
  ```
407
111
 
408
- Story Groups are reusable declaration values and normally live in a shared
409
- file. Root Groups use `storyGroup()`, nested Groups use `.group()`, and grouped
410
- Stories use `.story()`. Direct `story()` creates a Standalone Story. Group and
411
- Story names are non-empty path segments and cannot contain `/`; sibling Groups
412
- and Stories share one namespace. Every Group has a required description.
413
-
414
- `laymos/story` is the only authoring surface. One optional
415
- Story-level Layer provides the fixed environment used by all lifecycle phases;
416
- Scenarios vary explicit prepared values and service state, never the dependency
417
- graph. Scenario preparation, execution, verification, cleanup, blocks, and
418
- decision arms all return Effects.
419
-
420
- ### An owned Effect runner (ADR-0004)
421
-
422
- Stories run against real integrations — real databases, real APIs — and are
423
- not tests, so laymos owns their execution end-to-end. There is no test
424
- framework anywhere in the story path.
425
-
426
- - **Discovery and identity are the runner's.** `laymos stories` discovers
427
- `*.story.ts` files, loads them through jiti on Node, and the Story ID is the
428
- file the runner imported — no stack parsing for identity.
429
- - **Sequential, single-process.** Scenarios run in declaration order in one
430
- process. Module-level state is shared across Story files, exactly as it is
431
- in production; per-file worker isolation is deliberately absent.
432
- - **Explicit lifecycle, no retries or watch mode.** The builder separates
433
- preparation, shared execution, verification, and optional cleanup. Every
434
- runnable Scenario must intentionally verify either a success value or typed
435
- error. Cleanup runs whenever preparation produced a value.
436
- - **Timeouts protect liveness.** Every Scenario gets a generous default
437
- timeout (60 seconds), overridable per Scenario (`timeout: '10 minutes'` as
438
- an Effect `Duration`) and per run (`--timeout`). Timing out interrupts the
439
- Scenario fiber and is recorded as an `interrupted` outcome.
440
-
441
- ### Only execution is narrative
112
+ The snapshot contains only included supported source files assigned to that
113
+ Module by a fresh Architecture Analysis.
442
114
 
443
- Recording is active only while the runner invokes the shared Story execution.
444
- Preparation, verification, and cleanup may call marked production code, but
445
- those calls execute without producing Block Visits. Operational mechanics do
446
- not compete with the explanation of the feature logic.
447
-
448
- ### The artifact
449
-
450
- A generation run records all Scenarios of a Story into **one JSON artifact per Story**: shared Block definitions plus each Scenario's recursive Execution Path of Block Visits. Test result + artifact, from one run.
451
-
452
- The artifact preserves blocks separately from their visits. A block is the
453
- shared, source-identified narrative unit; a block visit is one occurrence in
454
- one scenario. The artifact knows two block kinds: `block` and `decision`.
455
- `functionBlock` and `step` both record as `block` — which primitive marked a
456
- block is recording mechanics, not narrative, exactly as the plain and Effect
457
- variants record identically. Only `decision` is distinct, because it owns
458
- declared Arms. A visit has no identifier of its own: its identity is its
459
- position in the Scenario's execution path, and its value contains facts only —
460
- Block ID, outcome, selected Arm when applicable, and optional attributes. It
461
- does not encode parentage, ordering, or next relationships.
462
-
463
- The Scenario owns one recursive execution path. The path is an array, so array
464
- order means sequence. A Visit item carries its facts and owns a nested child
465
- path; a Parallel item owns an array of branch paths.
115
+ Inspect an exact included source file or Configured Module without consuming
116
+ the complete Architecture Analysis:
466
117
 
467
118
  ```ts
468
- type StoryId = string;
469
- type BlockId = string;
470
-
471
- interface StoryArtifact {
472
- readonly schemaVersion: 3;
473
- readonly generatedAt: number;
474
- readonly name: string;
475
- readonly description: string;
476
- readonly blocks: Readonly<Record<BlockId, Block>>;
477
- readonly scenarios: readonly Scenario[];
478
- }
479
-
480
- type Arm =
481
- | {
482
- readonly kind: 'literal';
483
- readonly value: string | number | boolean;
484
- readonly name: string;
485
- readonly description: string;
486
- }
487
- | {
488
- readonly kind: 'otherwise';
489
- readonly name: string;
490
- readonly description: string;
491
- };
492
-
493
- type SelectedArm =
494
- | { readonly kind: 'literal'; readonly value: string | number | boolean }
495
- | { readonly kind: 'otherwise' };
496
-
497
- type Block =
498
- | {
499
- readonly kind: 'block';
500
- readonly name: string;
501
- readonly description: string;
502
- readonly location: StorySourceLocation;
503
- }
504
- | {
505
- readonly kind: 'decision';
506
- readonly name: string;
507
- readonly description: string;
508
- readonly location: StorySourceLocation;
509
- readonly arms: readonly Arm[];
510
- };
511
-
512
- interface Scenario {
513
- readonly name: string;
514
- readonly description: string;
515
- readonly location: StorySourceLocation;
516
- readonly outcome: ScenarioOutcome;
517
- readonly execution: ExecutionPath;
518
- readonly failures: readonly {
519
- readonly phase: 'preparation' | 'execution' | 'verification' | 'cleanup';
520
- readonly message: string;
521
- }[];
522
- }
523
-
524
- type ExecutionPath = readonly ExecutionItem[];
525
-
526
- type ExecutionItem =
527
- | {
528
- readonly blockId: BlockId;
529
- readonly outcome: BlockVisitOutcome;
530
- readonly selectedArm?: SelectedArm;
531
- readonly attributes?: Readonly<Record<string, unknown>>;
532
- readonly children: ExecutionPath;
533
- }
534
- | { readonly parallel: readonly ExecutionPath[] };
535
- ```
536
-
537
- Generated identities live in record keys and are not repeated in their values;
538
- visits carry no generated identity at all. Arrays are reserved for meaningful
539
- order. Records are serialized by sorted key for content hashing. The execution
540
- path is the sole source of sequence, parallelism, containment, and visit facts.
541
- There are no visit IDs, parent IDs, next IDs, flow-edge records, or
542
- parallel-group records — a dangling or orphaned visit reference is
543
- unrepresentable.
544
-
545
- Every Visit records when it began and how long it ran. Timing uses a hybrid
546
- clock: the Scenario stores its absolute start time and total duration, each
547
- Visit stores a monotonic `startOffsetMillis` relative to the Scenario start
548
- plus its own `durationMillis` — so trace nesting is consistent by
549
- construction and devtools can render a span waterfall directly from the
550
- Execution Path. Completion updates the Visit's outcome to `succeeded`,
551
- `failed`, or `interrupted`; return values and errors are not captured
552
- automatically.
553
-
554
- Synchronous throws, Promise rejections, and Effect failures or defects mark a
555
- Visit `failed`. An Effect interruption-only Cause marks it `interrupted`; a
556
- Cause containing both failure and interruption is `failed`. A Scenario timeout
557
- or run interruption marks every still-active Visit `interrupted`. A caught inner failure
558
- does not taint an enclosing Block that handles it and returns normally: the
559
- inner Visit is `failed` and the enclosing Visit is `succeeded`. Errors and
560
- Causes are not captured automatically.
561
-
562
- Every scenario preserves the visits observed before failure or interruption.
563
- Uncaught failure marks
564
- the failing visit and each enclosing visit through which it propagates as
565
- failed; cancellation or timeout marks visits still active at interruption as
566
- interrupted. When one visit must be referenced externally, its address is the
567
- Scenario's declaration position plus the visit's structural position in the
568
- execution path — derivable, never stored. The Story runner is the authority
569
- on the Scenario outcome.
570
-
571
- ### Ephemeral generation
572
-
573
- Story artifacts exist only as return values from an explicit generation
574
- request. Laymos writes no index, cache, staging directory, or artifact file.
575
- Callers may keep the returned values in memory for presentation, but a refresh
576
- always runs the requested Stories again and atomically replaces that caller-owned
577
- state.
578
-
579
- Generation orders Scenarios by declaration position and Blocks by generated
580
- identity. Sequential order and containment come from the execution path itself.
581
-
582
- Finalization also normalizes degenerate forms so one execution has one
583
- canonical representation: empty parallel branches are dropped, a Parallel item
584
- left with a single branch is inlined into its parent path, and one left with
585
- none is removed. A canonical artifact therefore guarantees every Parallel item
586
- has at least two non-empty branches. Consumers trust these invariants and the
587
- Arm rules above; consumers may trust these invariants.
588
-
589
- ### Node APIs
590
-
591
- `laymos/node` exposes discovery and fresh execution:
592
-
593
- ```ts
594
- discoverStories(baseDir): Effect<StoryCatalog, StoryDiscoveryError>
595
- runStory(baseDir, storyId): Effect<StoryRunResult, StoryRunnerError>
596
- runStoryGroup(baseDir, groupPath): Effect<StoriesRunResult, StoryDiscoveryError | StoryRunnerError>
597
- runStories(baseDir, storyIds): Effect<StoriesRunResult, StoryRunnerError>
598
- runAllStories(baseDir): Effect<AllStoriesRunResult, StoryRunnerError>
599
- ```
600
-
601
- Execution results carry fresh evidence and diagnostics:
602
-
603
- ```ts
604
- interface StoryRunResult {
605
- readonly status: 'passed' | 'failed';
606
- readonly artifact: StoryArtifact;
607
- readonly failures: readonly StoryFailure[];
608
- }
609
-
610
- interface StoriesRunResult {
611
- readonly status: 'passed' | 'failed';
612
- readonly report: LaymosStoriesReport;
613
- readonly failures: readonly StoryFailure[];
614
- }
119
+ import { Effect } from 'effect';
120
+ import { inspectFile, inspectModule } from 'laymos';
121
+
122
+ const file = await Effect.runPromise(
123
+ inspectFile('/absolute/project/laymos.config.json', 'src/domain/order.ts', {
124
+ recursive: true,
125
+ }),
126
+ );
127
+ const module = await Effect.runPromise(
128
+ inspectModule('/absolute/project/laymos.config.json', 'src/domain/orders'),
129
+ );
615
130
  ```
616
131
 
617
- `discoverStories` imports every Story module to collect names, descriptions,
618
- and Group ancestry, but it never prepares or executes a Scenario. Story files
619
- and their shared imports must therefore remain declaration-only at module
620
- scope. Discovery validates the complete catalog atomically and reports every
621
- invalid file, conflicting Group declaration, and sibling-name collision
622
- together. `runStoryGroup` performs fresh discovery and executes every Story in
623
- the selected subtree. `runStory` and `runStories` continue to use file-based
624
- Story IDs.
625
-
626
- The CLI mirrors group execution with
627
- `laymos stories --group "DynamoDB / Entities"`. A Group selection cannot be
628
- combined with explicit Story file arguments.
629
-
630
- The execution APIs run the owned Story runner with `baseDir` as the target
631
- project's root. The runner discovers `*.story.ts` files itself, loads them
632
- through jiti, and executes Scenarios sequentially in a single Node process.
633
- There is no test framework, configuration file, or worker pool in the story
634
- path.
635
-
636
- Interrupting an execution API interrupts its Effect. A Scenario timeout is
637
- returned as reportable partial evidence with an `interrupted` Scenario outcome;
638
- the runner then continues with the remaining Scenarios. Each artifact records
639
- `generatedAt` so a caller can label the evidence it currently holds.
640
-
641
- `runAllStories` runs every discovered Story to completion and returns
642
- `status: 'failed'` when any Scenario failed or was interrupted. Skipped
643
- Scenarios do not make the aggregate result fail.
644
-
645
- Discovering no Story files is a valid complete generation and succeeds with a
646
- passed empty report. `runStory` fails with `StoryRunnerError` when its Story ID
647
- does not resolve to an existing Story file.
132
+ ## CLI
648
133
 
649
- A failed or interrupted Scenario is reportable evidence, so a Scenario failure
650
- succeeds with `status: 'failed'` and a finalized result containing the Scenario's
651
- partial Block Visits. Module loading, invalid definitions, and recording
652
- failures fail with `StoryRunnerError` instead. The two result cases are not a
653
- discriminated union because they carry the same data; `status` is the aggregate
654
- test result and Scenario outcomes retain the detail.
655
-
656
- An invalid Story definition is also a rejecting failure; rejected requests do
657
- not expose any earlier in-memory results from that request.
658
-
659
- ### Outside a Scenario
660
-
661
- The production `laymos/story` entry has no recording capability. Its
662
- `functionBlock` returns the original function, `step` returns the original
663
- Effect, and `decision` performs only the declared branching. Attribute
664
- resolvers are not evaluated, and there is no recorder lookup, source-location
665
- capture, serialization, event emission, or runtime configuration.
666
-
667
- During Story execution, the runner uses Jiti to resolve `laymos/story` to a
668
- private `story-runtime` implementation throughout the loaded source graph.
669
- That runtime declares Stories and records Blocks while preserving the public
670
- surface's production semantics. `story-runtime` is deliberately absent from
671
- the package export map, so application code and TypeScript tooling cannot
672
- resolve it as a package subpath. Production logging and replay are outside v1.
673
-
674
- ---
675
-
676
- ## The Unifying Principle
677
-
678
- Every pillar has the same shape:
679
-
680
- > **Static truth + runtime evidence, merged. Declared intent + actual state, both visible.**
681
-
682
- - Layers/modules: config (intent) merged with the extracted import graph (reality) → violations + viz.
683
- - Stories: declared arms (shape) merged with recorded scenarios (reality) → observed flow graph.
684
- - Production, later: story graph (map) merged with logs (reality) → replay.
685
-
686
- One config. One engine. Enforcement and the diagram are the same artifact.
687
-
688
- ---
689
-
690
- ## Package Layout
691
-
692
- One package, three subpaths — consumers only pay for what they import:
693
-
694
- | Subpath | Contents | Runs where |
695
- | -------------- | ----------------------------------------------------- | ------------------------ |
696
- | `laymos` | Config DSL + types | Anywhere (browser-safe) |
697
- | `laymos/node` | analysis engine + Story discovery and fresh execution | Node, dev-time |
698
- | `laymos/story` | Effect Story builder and blocks | Production (Effect apps) |
699
-
700
- ```
701
- src/
702
- ├─ config/ builders: layer, edge, layerGraph, module, rules, defineConfig
703
- ├─ engine/ extract (skott) → resolve → evaluate → emit, tagged errors
704
- ├─ cli/ effect CLI: laymos lint, laymos stories
705
- ├─ story/
706
- │ ├─ core/ declaration model and recorder contract
707
- │ ├─ effect/ → "laymos/story"
708
- │ ├─ story-runtime/ private runner-only implementation
709
- │ ├─ runner/ owned Story runner: discovery, loading, execution
710
- │ └─ artifact/ Story recording and artifact data model
711
- ├─ index.ts → "laymos"
712
- └─ node.ts → "laymos/node"
134
+ ```sh
135
+ laymos [--config <path>] lint
136
+ laymos [--config <path>] lint layers
137
+ laymos [--config <path>] lint modules
138
+ laymos [--config <path>] inspect project [--json]
139
+ laymos [--config <path>] inspect layer <layer-name> [--json]
140
+ laymos [--config <path>] inspect file <file-path> [--recursive] [--json]
141
+ laymos [--config <path>] inspect module <module-path> [--json]
713
142
  ```
714
143
 
715
- ---
716
-
717
- ## Technology Choices
718
-
719
- | Choice | Why |
720
- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
721
- | **TypeScript projects** as the target | Import resolution, `import type` semantics, TS path aliases — the ecosystem laymos speaks. |
722
- | **skott** as extractor only, type-tracking always on | Identical edge sets to dependency-cruiser in a head-to-head POC, ~30% faster, simpler API (ADR-0001). All rule semantics stay in laymos's own engine. |
723
- | **Own rules engine** | Transitivity, sink layers, AND-gated module constraints don't survive translation to regex pair-lists. |
724
- | **Effect** for the engine, CLI, and Stories | One runtime and one failure/concurrency model across authoring and execution (ADR-0002). |
725
- | **`laymos.config.ts`** single typed file, reference builders | One source of truth; cross-references are values — autocomplete, rename-safe. Cost: config is executed code. |
726
- | **Owned Effect Story runner** (ADR-0004) | Stories run real integrations, not tests; mocking, retries, and parallel workers are anti-features. Sequential and single-process. |
727
- | **`Error().stack`** for generated block identity | Zero build-step; project-relative file, line, and column are sufficient within one generated artifact. |
728
- | **Structured JSON log lines** for prod mode | One-line runtime cost; correlation is a viewer problem. |
729
-
730
- ---
731
-
732
- ## Explicitly Out of Scope / Deferred
733
-
734
- - **Runtime coupling beyond imports** (events, pub/sub) — laymos governs static import structure; stories capture executed flows. Coupling without imports is not modeled.
735
- - **Cross-pillar assertions (v2):** a story asserting "this path never touches module X" — runtime evidence checked against layer/module rules. The pillars unify into one enforcement system here. Designed later, deliberately.
736
- - **Escape hatches** beyond `ignore` (per-edge exceptions, baselines) — deferred until real need.
737
- - **Viz specifics** — principles fixed (declared vs actual, triad of edge states, side-by-side graphs), rendering deferred.
738
- - **Line-level violation evidence** and per-edge import kind — dropped for v1 (ADR-0001); additive later.
739
- - **Production replay viewer** — format designed, feature deferred.
740
- - **Naming of graph-views** — "layer graph" for now; parked.
144
+ `lint` checks every architectural rule; `lint layers` checks Layer coverage,
145
+ configured Module presence, and dependencies, while `lint modules` checks
146
+ Module coverage, expected entry points, dependencies, public boundaries, and
147
+ cycles. Violations exit with status `1`, while invalid configuration or an
148
+ analysis failure exits with status `2`.
149
+
150
+ `inspect file` prints the file's Layer, Configured Module, public-boundary role,
151
+ and dependencies as a colored path tree. Direct dependencies are yellow; with
152
+ `--recursive`, transitive dependencies are gray. Only exact included supported
153
+ source files can be inspected. Included files with missing Layer or Module
154
+ membership remain inspectable and show a coverage warning.
155
+
156
+ `inspect project` summarizes the whole architecture. `inspect layer` accepts an
157
+ exact Layer name and reports its paths, allowed Layer links, Modules, Shared
158
+ count, and violations. `inspect module` accepts an exact configured Module path
159
+ and prints its configured kind, source shape, observed kind, public entry
160
+ points, and dependency tree. Add `--json` to any inspect command for stable tool
161
+ output. If the selected Module participates in a dependency cycle, inspection
162
+ stops and directs the user to `lint modules`.
163
+
164
+ The active inspection target is green in both trees. All commands use
165
+ `sourceRoots` and `ignoredPaths` from the config. Config paths default to
166
+ `./laymos.config.json`, and project-relative paths are resolved from the config
167
+ file's directory.