@endora-commerce/cli 0.100.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 (356) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +37 -0
  3. package/dist/bin/endora.d.ts +28 -0
  4. package/dist/bin/endora.d.ts.map +1 -0
  5. package/dist/bin/endora.js +926 -0
  6. package/dist/bin/endora.js.map +1 -0
  7. package/dist/check/estate.d.ts +189 -0
  8. package/dist/check/estate.d.ts.map +1 -0
  9. package/dist/check/estate.js +1037 -0
  10. package/dist/check/estate.js.map +1 -0
  11. package/dist/check/hosts.d.ts +25 -0
  12. package/dist/check/hosts.d.ts.map +1 -0
  13. package/dist/check/hosts.js +1347 -0
  14. package/dist/check/hosts.js.map +1 -0
  15. package/dist/check/index.d.ts +60 -0
  16. package/dist/check/index.d.ts.map +1 -0
  17. package/dist/check/index.js +111 -0
  18. package/dist/check/index.js.map +1 -0
  19. package/dist/check/layout.d.ts +136 -0
  20. package/dist/check/layout.d.ts.map +1 -0
  21. package/dist/check/layout.js +262 -0
  22. package/dist/check/layout.js.map +1 -0
  23. package/dist/check/ledger.d.ts +98 -0
  24. package/dist/check/ledger.d.ts.map +1 -0
  25. package/dist/check/ledger.js +173 -0
  26. package/dist/check/ledger.js.map +1 -0
  27. package/dist/check/peer-owners.d.ts +116 -0
  28. package/dist/check/peer-owners.d.ts.map +1 -0
  29. package/dist/check/peer-owners.js +225 -0
  30. package/dist/check/peer-owners.js.map +1 -0
  31. package/dist/check/report.d.ts +33 -0
  32. package/dist/check/report.d.ts.map +1 -0
  33. package/dist/check/report.js +107 -0
  34. package/dist/check/report.js.map +1 -0
  35. package/dist/check/run.d.ts +147 -0
  36. package/dist/check/run.d.ts.map +1 -0
  37. package/dist/check/run.js +111 -0
  38. package/dist/check/run.js.map +1 -0
  39. package/dist/checks.d.ts +17 -0
  40. package/dist/checks.d.ts.map +1 -0
  41. package/dist/checks.js +17 -0
  42. package/dist/checks.js.map +1 -0
  43. package/dist/dev/index.d.ts +83 -0
  44. package/dist/dev/index.d.ts.map +1 -0
  45. package/dist/dev/index.js +298 -0
  46. package/dist/dev/index.js.map +1 -0
  47. package/dist/generate/divergence.d.ts +38 -0
  48. package/dist/generate/divergence.d.ts.map +1 -0
  49. package/dist/generate/divergence.js +237 -0
  50. package/dist/generate/divergence.js.map +1 -0
  51. package/dist/generate/index.d.ts +90 -0
  52. package/dist/generate/index.d.ts.map +1 -0
  53. package/dist/generate/index.js +369 -0
  54. package/dist/generate/index.js.map +1 -0
  55. package/dist/index.d.ts +23 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +35 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/inputs/declaration.d.ts +18 -0
  60. package/dist/inputs/declaration.d.ts.map +1 -0
  61. package/dist/inputs/declaration.js +64 -0
  62. package/dist/inputs/declaration.js.map +1 -0
  63. package/dist/inputs/env-file.d.ts +73 -0
  64. package/dist/inputs/env-file.d.ts.map +1 -0
  65. package/dist/inputs/env-file.js +134 -0
  66. package/dist/inputs/env-file.js.map +1 -0
  67. package/dist/inputs/prompt.d.ts +21 -0
  68. package/dist/inputs/prompt.d.ts.map +1 -0
  69. package/dist/inputs/prompt.js +59 -0
  70. package/dist/inputs/prompt.js.map +1 -0
  71. package/dist/inputs/resolve.d.ts +163 -0
  72. package/dist/inputs/resolve.d.ts.map +1 -0
  73. package/dist/inputs/resolve.js +290 -0
  74. package/dist/inputs/resolve.js.map +1 -0
  75. package/dist/install/host.d.ts +27 -0
  76. package/dist/install/host.d.ts.map +1 -0
  77. package/dist/install/host.js +90 -0
  78. package/dist/install/host.js.map +1 -0
  79. package/dist/install/index.d.ts +173 -0
  80. package/dist/install/index.d.ts.map +1 -0
  81. package/dist/install/index.js +793 -0
  82. package/dist/install/index.js.map +1 -0
  83. package/dist/install/wizard.d.ts +144 -0
  84. package/dist/install/wizard.d.ts.map +1 -0
  85. package/dist/install/wizard.js +362 -0
  86. package/dist/install/wizard.js.map +1 -0
  87. package/dist/lib/admin-artefacts.d.ts +70 -0
  88. package/dist/lib/admin-artefacts.d.ts.map +1 -0
  89. package/dist/lib/admin-artefacts.js +354 -0
  90. package/dist/lib/admin-artefacts.js.map +1 -0
  91. package/dist/lib/admin-surfaces.d.ts +298 -0
  92. package/dist/lib/admin-surfaces.d.ts.map +1 -0
  93. package/dist/lib/admin-surfaces.js +669 -0
  94. package/dist/lib/admin-surfaces.js.map +1 -0
  95. package/dist/lib/delegated-composer.d.ts +85 -0
  96. package/dist/lib/delegated-composer.d.ts.map +1 -0
  97. package/dist/lib/delegated-composer.js +241 -0
  98. package/dist/lib/delegated-composer.js.map +1 -0
  99. package/dist/lib/divergence-artefacts.d.ts +305 -0
  100. package/dist/lib/divergence-artefacts.d.ts.map +1 -0
  101. package/dist/lib/divergence-artefacts.js +828 -0
  102. package/dist/lib/divergence-artefacts.js.map +1 -0
  103. package/dist/lib/divergence.d.ts +337 -0
  104. package/dist/lib/divergence.d.ts.map +1 -0
  105. package/dist/lib/divergence.js +1005 -0
  106. package/dist/lib/divergence.js.map +1 -0
  107. package/dist/lib/docs-artefacts.d.ts +395 -0
  108. package/dist/lib/docs-artefacts.d.ts.map +1 -0
  109. package/dist/lib/docs-artefacts.js +781 -0
  110. package/dist/lib/docs-artefacts.js.map +1 -0
  111. package/dist/lib/emitted-exports.d.ts +21 -0
  112. package/dist/lib/emitted-exports.d.ts.map +1 -0
  113. package/dist/lib/emitted-exports.js +96 -0
  114. package/dist/lib/emitted-exports.js.map +1 -0
  115. package/dist/lib/emitted-freshness.d.ts +112 -0
  116. package/dist/lib/emitted-freshness.d.ts.map +1 -0
  117. package/dist/lib/emitted-freshness.js +288 -0
  118. package/dist/lib/emitted-freshness.js.map +1 -0
  119. package/dist/lib/entity-index-artefact.d.ts +95 -0
  120. package/dist/lib/entity-index-artefact.d.ts.map +1 -0
  121. package/dist/lib/entity-index-artefact.js +210 -0
  122. package/dist/lib/entity-index-artefact.js.map +1 -0
  123. package/dist/lib/instance-build-inputs.d.ts +108 -0
  124. package/dist/lib/instance-build-inputs.d.ts.map +1 -0
  125. package/dist/lib/instance-build-inputs.js +165 -0
  126. package/dist/lib/instance-build-inputs.js.map +1 -0
  127. package/dist/lib/module-docs.d.ts +473 -0
  128. package/dist/lib/module-docs.d.ts.map +1 -0
  129. package/dist/lib/module-docs.js +711 -0
  130. package/dist/lib/module-docs.js.map +1 -0
  131. package/dist/lib/module-package-subpaths.d.ts +56 -0
  132. package/dist/lib/module-package-subpaths.d.ts.map +1 -0
  133. package/dist/lib/module-package-subpaths.js +223 -0
  134. package/dist/lib/module-package-subpaths.js.map +1 -0
  135. package/dist/lib/module-packages.d.ts +200 -0
  136. package/dist/lib/module-packages.d.ts.map +1 -0
  137. package/dist/lib/module-packages.js +580 -0
  138. package/dist/lib/module-packages.js.map +1 -0
  139. package/dist/lib/module-population.d.ts +129 -0
  140. package/dist/lib/module-population.d.ts.map +1 -0
  141. package/dist/lib/module-population.js +172 -0
  142. package/dist/lib/module-population.js.map +1 -0
  143. package/dist/lib/module-roots.d.ts +337 -0
  144. package/dist/lib/module-roots.d.ts.map +1 -0
  145. package/dist/lib/module-roots.js +586 -0
  146. package/dist/lib/module-roots.js.map +1 -0
  147. package/dist/lib/nested-checkouts.d.ts +33 -0
  148. package/dist/lib/nested-checkouts.d.ts.map +1 -0
  149. package/dist/lib/nested-checkouts.js +160 -0
  150. package/dist/lib/nested-checkouts.js.map +1 -0
  151. package/dist/lib/platform-root.d.ts +43 -0
  152. package/dist/lib/platform-root.d.ts.map +1 -0
  153. package/dist/lib/platform-root.js +134 -0
  154. package/dist/lib/platform-root.js.map +1 -0
  155. package/dist/lib/platform-surface.d.ts +235 -0
  156. package/dist/lib/platform-surface.d.ts.map +1 -0
  157. package/dist/lib/platform-surface.js +393 -0
  158. package/dist/lib/platform-surface.js.map +1 -0
  159. package/dist/lib/port-registrations.d.ts +223 -0
  160. package/dist/lib/port-registrations.d.ts.map +1 -0
  161. package/dist/lib/port-registrations.js +532 -0
  162. package/dist/lib/port-registrations.js.map +1 -0
  163. package/dist/lib/read-size.d.ts +154 -0
  164. package/dist/lib/read-size.d.ts.map +1 -0
  165. package/dist/lib/read-size.js +182 -0
  166. package/dist/lib/read-size.js.map +1 -0
  167. package/dist/lib/registration-owners.d.ts +79 -0
  168. package/dist/lib/registration-owners.d.ts.map +1 -0
  169. package/dist/lib/registration-owners.js +77 -0
  170. package/dist/lib/registration-owners.js.map +1 -0
  171. package/dist/lib/release-index.d.ts +53 -0
  172. package/dist/lib/release-index.d.ts.map +1 -0
  173. package/dist/lib/release-index.js +162 -0
  174. package/dist/lib/release-index.js.map +1 -0
  175. package/dist/lib/repeating-timers.d.ts +79 -0
  176. package/dist/lib/repeating-timers.d.ts.map +1 -0
  177. package/dist/lib/repeating-timers.js +189 -0
  178. package/dist/lib/repeating-timers.js.map +1 -0
  179. package/dist/lib/source-text.d.ts +34 -0
  180. package/dist/lib/source-text.d.ts.map +1 -0
  181. package/dist/lib/source-text.js +80 -0
  182. package/dist/lib/source-text.js.map +1 -0
  183. package/dist/lib/specifiers.d.ts +20 -0
  184. package/dist/lib/specifiers.d.ts.map +1 -0
  185. package/dist/lib/specifiers.js +130 -0
  186. package/dist/lib/specifiers.js.map +1 -0
  187. package/dist/lib/sql-tables.d.ts +166 -0
  188. package/dist/lib/sql-tables.d.ts.map +1 -0
  189. package/dist/lib/sql-tables.js +464 -0
  190. package/dist/lib/sql-tables.js.map +1 -0
  191. package/dist/lib/switchable-modules.d.ts +54 -0
  192. package/dist/lib/switchable-modules.d.ts.map +1 -0
  193. package/dist/lib/switchable-modules.js +104 -0
  194. package/dist/lib/switchable-modules.js.map +1 -0
  195. package/dist/lib/tailwind-sources.d.ts +136 -0
  196. package/dist/lib/tailwind-sources.d.ts.map +1 -0
  197. package/dist/lib/tailwind-sources.js +307 -0
  198. package/dist/lib/tailwind-sources.js.map +1 -0
  199. package/dist/lib/ui-layer.d.ts +54 -0
  200. package/dist/lib/ui-layer.d.ts.map +1 -0
  201. package/dist/lib/ui-layer.js +57 -0
  202. package/dist/lib/ui-layer.js.map +1 -0
  203. package/dist/lib/workspace-packages.d.ts +186 -0
  204. package/dist/lib/workspace-packages.d.ts.map +1 -0
  205. package/dist/lib/workspace-packages.js +351 -0
  206. package/dist/lib/workspace-packages.js.map +1 -0
  207. package/dist/new-instance/deploy.d.ts +211 -0
  208. package/dist/new-instance/deploy.d.ts.map +1 -0
  209. package/dist/new-instance/deploy.js +1381 -0
  210. package/dist/new-instance/deploy.js.map +1 -0
  211. package/dist/new-instance/docs-toolchain.d.ts +66 -0
  212. package/dist/new-instance/docs-toolchain.d.ts.map +1 -0
  213. package/dist/new-instance/docs-toolchain.js +69 -0
  214. package/dist/new-instance/docs-toolchain.js.map +1 -0
  215. package/dist/new-instance/host.d.ts +124 -0
  216. package/dist/new-instance/host.d.ts.map +1 -0
  217. package/dist/new-instance/host.js +276 -0
  218. package/dist/new-instance/host.js.map +1 -0
  219. package/dist/new-instance/index.d.ts +118 -0
  220. package/dist/new-instance/index.d.ts.map +1 -0
  221. package/dist/new-instance/index.js +567 -0
  222. package/dist/new-instance/index.js.map +1 -0
  223. package/dist/new-instance/modules.d.ts +188 -0
  224. package/dist/new-instance/modules.d.ts.map +1 -0
  225. package/dist/new-instance/modules.js +392 -0
  226. package/dist/new-instance/modules.js.map +1 -0
  227. package/dist/new-instance/template.d.ts +505 -0
  228. package/dist/new-instance/template.d.ts.map +1 -0
  229. package/dist/new-instance/template.js +1886 -0
  230. package/dist/new-instance/template.js.map +1 -0
  231. package/dist/new-module/emit.d.ts +67 -0
  232. package/dist/new-module/emit.d.ts.map +1 -0
  233. package/dist/new-module/emit.js +1393 -0
  234. package/dist/new-module/emit.js.map +1 -0
  235. package/dist/new-module/host.d.ts +67 -0
  236. package/dist/new-module/host.d.ts.map +1 -0
  237. package/dist/new-module/host.js +224 -0
  238. package/dist/new-module/host.js.map +1 -0
  239. package/dist/new-module/index.d.ts +32 -0
  240. package/dist/new-module/index.d.ts.map +1 -0
  241. package/dist/new-module/index.js +193 -0
  242. package/dist/new-module/index.js.map +1 -0
  243. package/dist/new-module/spec.d.ts +188 -0
  244. package/dist/new-module/spec.d.ts.map +1 -0
  245. package/dist/new-module/spec.js +404 -0
  246. package/dist/new-module/spec.js.map +1 -0
  247. package/dist/new-module/text.d.ts +15 -0
  248. package/dist/new-module/text.d.ts.map +1 -0
  249. package/dist/new-module/text.js +22 -0
  250. package/dist/new-module/text.js.map +1 -0
  251. package/dist/new-storefront/dockerfile.d.ts +20 -0
  252. package/dist/new-storefront/dockerfile.d.ts.map +1 -0
  253. package/dist/new-storefront/dockerfile.js +131 -0
  254. package/dist/new-storefront/dockerfile.js.map +1 -0
  255. package/dist/new-storefront/gitignore.d.ts +23 -0
  256. package/dist/new-storefront/gitignore.d.ts.map +1 -0
  257. package/dist/new-storefront/gitignore.js +39 -0
  258. package/dist/new-storefront/gitignore.js.map +1 -0
  259. package/dist/new-storefront/index.d.ts +93 -0
  260. package/dist/new-storefront/index.d.ts.map +1 -0
  261. package/dist/new-storefront/index.js +329 -0
  262. package/dist/new-storefront/index.js.map +1 -0
  263. package/dist/new-storefront/npmrc.d.ts +98 -0
  264. package/dist/new-storefront/npmrc.d.ts.map +1 -0
  265. package/dist/new-storefront/npmrc.js +217 -0
  266. package/dist/new-storefront/npmrc.js.map +1 -0
  267. package/dist/new-storefront/reference.d.ts +189 -0
  268. package/dist/new-storefront/reference.d.ts.map +1 -0
  269. package/dist/new-storefront/reference.js +430 -0
  270. package/dist/new-storefront/reference.js.map +1 -0
  271. package/dist/new-storefront/rewrite.d.ts +171 -0
  272. package/dist/new-storefront/rewrite.d.ts.map +1 -0
  273. package/dist/new-storefront/rewrite.js +701 -0
  274. package/dist/new-storefront/rewrite.js.map +1 -0
  275. package/dist/release-index.json +284 -0
  276. package/dist/rules/action-route-permissions.d.ts +141 -0
  277. package/dist/rules/action-route-permissions.d.ts.map +1 -0
  278. package/dist/rules/action-route-permissions.js +556 -0
  279. package/dist/rules/action-route-permissions.js.map +1 -0
  280. package/dist/rules/bundle-pairing.d.ts +74 -0
  281. package/dist/rules/bundle-pairing.d.ts.map +1 -0
  282. package/dist/rules/bundle-pairing.js +281 -0
  283. package/dist/rules/bundle-pairing.js.map +1 -0
  284. package/dist/rules/channel-resolution.d.ts +17 -0
  285. package/dist/rules/channel-resolution.d.ts.map +1 -0
  286. package/dist/rules/channel-resolution.js +382 -0
  287. package/dist/rules/channel-resolution.js.map +1 -0
  288. package/dist/rules/command-coverage.d.ts +210 -0
  289. package/dist/rules/command-coverage.d.ts.map +1 -0
  290. package/dist/rules/command-coverage.js +714 -0
  291. package/dist/rules/command-coverage.js.map +1 -0
  292. package/dist/rules/container-imports.d.ts +60 -0
  293. package/dist/rules/container-imports.d.ts.map +1 -0
  294. package/dist/rules/container-imports.js +158 -0
  295. package/dist/rules/container-imports.js.map +1 -0
  296. package/dist/rules/default-language-prose.d.ts +212 -0
  297. package/dist/rules/default-language-prose.d.ts.map +1 -0
  298. package/dist/rules/default-language-prose.js +710 -0
  299. package/dist/rules/default-language-prose.js.map +1 -0
  300. package/dist/rules/diacritic-folds.d.ts +238 -0
  301. package/dist/rules/diacritic-folds.d.ts.map +1 -0
  302. package/dist/rules/diacritic-folds.js +681 -0
  303. package/dist/rules/diacritic-folds.js.map +1 -0
  304. package/dist/rules/entity-tenant-classification.d.ts +171 -0
  305. package/dist/rules/entity-tenant-classification.d.ts.map +1 -0
  306. package/dist/rules/entity-tenant-classification.js +323 -0
  307. package/dist/rules/entity-tenant-classification.js.map +1 -0
  308. package/dist/rules/entry-presence.d.ts +142 -0
  309. package/dist/rules/entry-presence.d.ts.map +1 -0
  310. package/dist/rules/entry-presence.js +339 -0
  311. package/dist/rules/entry-presence.js.map +1 -0
  312. package/dist/rules/entry-scope.d.ts +91 -0
  313. package/dist/rules/entry-scope.d.ts.map +1 -0
  314. package/dist/rules/entry-scope.js +404 -0
  315. package/dist/rules/entry-scope.js.map +1 -0
  316. package/dist/rules/env-inputs.d.ts +222 -0
  317. package/dist/rules/env-inputs.d.ts.map +1 -0
  318. package/dist/rules/env-inputs.js +951 -0
  319. package/dist/rules/env-inputs.js.map +1 -0
  320. package/dist/rules/kernel-boundary.d.ts +37 -0
  321. package/dist/rules/kernel-boundary.d.ts.map +1 -0
  322. package/dist/rules/kernel-boundary.js +195 -0
  323. package/dist/rules/kernel-boundary.js.map +1 -0
  324. package/dist/rules/nul-bytes.d.ts +233 -0
  325. package/dist/rules/nul-bytes.d.ts.map +1 -0
  326. package/dist/rules/nul-bytes.js +332 -0
  327. package/dist/rules/nul-bytes.js.map +1 -0
  328. package/dist/rules/platform-surface.d.ts +479 -0
  329. package/dist/rules/platform-surface.d.ts.map +1 -0
  330. package/dist/rules/platform-surface.js +749 -0
  331. package/dist/rules/platform-surface.js.map +1 -0
  332. package/dist/rules/port-catches.d.ts +225 -0
  333. package/dist/rules/port-catches.d.ts.map +1 -0
  334. package/dist/rules/port-catches.js +1374 -0
  335. package/dist/rules/port-catches.js.map +1 -0
  336. package/dist/rules/port-shape.d.ts +213 -0
  337. package/dist/rules/port-shape.d.ts.map +1 -0
  338. package/dist/rules/port-shape.js +670 -0
  339. package/dist/rules/port-shape.js.map +1 -0
  340. package/dist/rules/queue-names.d.ts +108 -0
  341. package/dist/rules/queue-names.d.ts.map +1 -0
  342. package/dist/rules/queue-names.js +395 -0
  343. package/dist/rules/queue-names.js.map +1 -0
  344. package/dist/rules/singleton-identity.d.ts +205 -0
  345. package/dist/rules/singleton-identity.d.ts.map +1 -0
  346. package/dist/rules/singleton-identity.js +830 -0
  347. package/dist/rules/singleton-identity.js.map +1 -0
  348. package/dist/rules/subscribe-seam.d.ts +121 -0
  349. package/dist/rules/subscribe-seam.d.ts.map +1 -0
  350. package/dist/rules/subscribe-seam.js +594 -0
  351. package/dist/rules/subscribe-seam.js.map +1 -0
  352. package/dist/rules/transaction-context.d.ts +40 -0
  353. package/dist/rules/transaction-context.d.ts.map +1 -0
  354. package/dist/rules/transaction-context.js +294 -0
  355. package/dist/rules/transaction-context.js.map +1 -0
  356. package/package.json +59 -0
@@ -0,0 +1,749 @@
1
+ /**
2
+ * CI check — a module reaches the platform only through what the platform
3
+ * publishes (feature 080, T042d; ruling **D-160.8**).
4
+ *
5
+ * ## Why it exists
6
+ *
7
+ * `specs/080-f4-real-scope/contracts/host-package.md` §1 classifies all 53
8
+ * platform files a module reaches as **P** (published API), **A** (accidental
9
+ * reach — host-internal, harness-only or orchestrator-only) or **O** (the
10
+ * module is reaching for a class where a sanctioned port exists). Three
11
+ * delivered rows now build on that classification, and **nothing in the
12
+ * repository enforced it**: `check:module-boundary` is module→module,
13
+ * `check:kernel-boundary` is ORM relations, and the `exports` map D-160.7 rules
14
+ * enforces nothing at all for the modules still in `backend/src` — which is all
15
+ * of them. A classification with no ratchet rots at the speed of the tree, and
16
+ * this one is load-bearing for the package split.
17
+ *
18
+ * ## The population, and what it misses
19
+ *
20
+ * Every import specifier written in a module's own sources — core under the
21
+ * application's module roots, packaged under `packages/modules/<id>`, overlay
22
+ * under `src/apps` — that names the platform. Two spellings, and they are the
23
+ * same reach:
24
+ *
25
+ * * a **relative** specifier that resolves to a file outside the module tree.
26
+ * "Outside the module tree" is the whole predicate: the host is everything
27
+ * that is not a module, so a reach into `src/seeds` or `src/composition.ts`
28
+ * is judged by the same rule as one into `src/kernel`, with no directory
29
+ * list to keep current.
30
+ * * a **bare** specifier into the host package — `<host>/kernel` — which is
31
+ * the only spelling a module outside the application tree has (feature 080,
32
+ * T060). It resolves through the host's own `exports` map to the same
33
+ * barrel, so `@endora-commerce/platform/http` and `../../http/index.js` are
34
+ * one answer. Neither the package name nor the subpath list is written
35
+ * here: both are read off the manifest of the member declaring
36
+ * `endora.type: "platform"`, so the D-161 scope rename and a sixth
37
+ * published directory arrive by being authored once.
38
+ *
39
+ * A declared subpath lands on a **barrel**, and reaching a barrel is reaching
40
+ * the published surface entire — so a bare reach is counted, attributed and
41
+ * cleared, and the symbol-level verdicts below are the relative spelling's. That
42
+ * is not a weaker rule for a packaged module: a name the barrel does not carry
43
+ * is a `tsc` error at the import, which is the same answer sooner. What the bare
44
+ * spelling *can* be wrong about is the **subpath**, and that is the finding it
45
+ * gets.
46
+ *
47
+ * Every specifier shape `scripts/lib/specifiers.ts` knows is read, `import type`
48
+ * and the one type-position `import('…')` included (§0b: a `from '…'`-only scan
49
+ * does not see it, and a module would move with an unrewritten specifier that
50
+ * `tsc` resolves and `node` never sees).
51
+ *
52
+ * **The second bullet is T060 and it is a repair, not a widening.** The
53
+ * population was relative specifiers alone, on the reasoning — written into
54
+ * this header — that "there is no host package yet, and when there is, its
55
+ * `exports` map refuses a deep path at resolution time and this check's
56
+ * population shrinks to nothing on its own". Both halves were wrong by the time
57
+ * the first module moved. The map refuses a *deep* path and licenses everything
58
+ * a widened map would license, which is precisely the reach D-160.8 exists to
59
+ * refuse; and a population that shrinks as the sweep proceeds is a check that
60
+ * reports green because it stopped looking. Measured: batch one took **104**
61
+ * reaches out of the walk and modules #1–#5 another 112, while the recorded
62
+ * read size absorbed one module's worth of the fall inside its own floor. That
63
+ * is why {@link hostDependentCoverage} exists beside the resolution — the
64
+ * repair without the floor is one edit away from happening again.
65
+ *
66
+ * **What that misses, stated rather than discovered later.** A specifier is not
67
+ * the only way to reach something, and `check:module-boundary` learned it the
68
+ * expensive way — it had to grow a SQL-table predicate for 121 reaches that name
69
+ * no specifier at all. The same three doors are open here and none of them is
70
+ * this check's:
71
+ *
72
+ * * **A container name.** `lazyPort('settingsReadPort')` reaches the platform
73
+ * through a string. That edge is `check:port-dependencies`' and
74
+ * `check:port-shape`'s, which between them own the port surface;
75
+ * `PLATFORM_OWNED_NAMES` is where a platform-owned name is declared.
76
+ * * **A platform-owned table.** `settings`, `audit_logs`,
77
+ * `module_registrations` and `sales_channels` are the host's, and SQL naming
78
+ * one names no specifier. That door is `check:module-boundary`'s and it is
79
+ * **open**: its owner map attributes those four to `kernel` off the
80
+ * platform-relative path of the file declaring them, so a module's SQL
81
+ * naming one is reported like any other cross-owner reach. `catalog`'s
82
+ * `sales_channel_products` join is ledgered under that attribution today,
83
+ * and `admin_actions`' `module_registrations` join was until feature 080
84
+ * retired it. This paragraph read *"a module→platform table reach is
85
+ * currently nobody's"* and was measured false while that second entry was
86
+ * being drained — it was written before the platform relocation taught the
87
+ * other check to attribute a `packages/platform/…` path to the kernel.
88
+ * What is still not this check's is the **verdict**: a table has no barrel,
89
+ * so there is no published-symbol question to ask about one.
90
+ * * **A module package's own layout.** Where inside a package a subpath
91
+ * leads is that package's `exports` map, and this check reads only the
92
+ * *host's*. A module that reached another module by bare specifier is
93
+ * `check:module-boundary`'s, which resolves module package names for
94
+ * exactly that reason.
95
+ *
96
+ * ## The granularity: per symbol, of a named file
97
+ *
98
+ * A finding is a **(module file, platform file, symbol)** triple, and the
99
+ * verdict is "does that platform file's barrel publish that name". Not per file,
100
+ * and the difference is not academic — !883 measured it: sixteen symbols of
101
+ * **P** files are reached by nobody and are deliberately unpublished, so
102
+ * "the file is P" licenses `SettingsCache`, `decryptSecretValue` and the LRU
103
+ * tuning constants along with the symbols the classification actually names.
104
+ *
105
+ * **What per-*file* granularity would catch that this will not.** A per-file
106
+ * check would refuse every reach into an **A** or **O** file outright, symbol or
107
+ * no symbol — so it would flag `SettingNotRegistered` imported from
108
+ * `settings.service.ts` (row 8, **O**), `parseHostMap` from
109
+ * `sales-channel-resolver.service.ts` (row 24, **O**) and
110
+ * `MembershipMutationResult` from `sales-channel-membership.service.ts` (row 15,
111
+ * **O**). Eight such symbols stand today and §8 rules every one of them
112
+ * *published* on purpose — a `@throws` a caller cannot name is a method a caller
113
+ * cannot call. So the per-file verdict would be eight false findings, and its
114
+ * one genuine catch is a different question: *is this symbol rightly on the
115
+ * barrel at all?* That question belongs to
116
+ * `test/unit/kernel/published-surface.test.ts`, which holds each barrel to
117
+ * §1.3's own symbol column in both directions. The two are deliberately not
118
+ * duplicates: that test asks whether the barrel is right, this check asks
119
+ * whether the tree obeys it, and both read the barrel through
120
+ * `scripts/lib/platform-surface.ts` so they cannot disagree about what it says.
121
+ *
122
+ * **That gap is closed** (T042f). It read, until then: `!883` prunes and
123
+ * ratchets three of the five barrels, and `tenancy/index.ts` and
124
+ * `commands/index.ts` are published surface by D-160.7 with no expected set at
125
+ * all — so for two of this check's five subpaths its authority was a barrel
126
+ * nothing held to §1.3. Both are now pruned to their **P** columns (§1.3 rows
127
+ * 2, 5, 12, 21, 28, 30, 33) and ratcheted two-way, and
128
+ * `published-surface.test.ts` derives its own population from
129
+ * {@link PUBLISHED_SUBPATHS} rather than a list, so a sixth published directory
130
+ * cannot arrive unratcheted the way these two did.
131
+ *
132
+ * Worth recording, because a green that moves nothing is the outcome most
133
+ * likely to be misread: closing it moved **no** ledger key and no finding. The
134
+ * 37 names the two barrels shed are reached by no module, and every name a
135
+ * module reaches stayed published — so nothing had been hiding behind the
136
+ * unratcheted barrels. The value bought is prospective: a 38th name added to
137
+ * either now has to move an expected set.
138
+ *
139
+ * ## Five findings
140
+ *
141
+ * * `unpublished-symbol` — the rule itself.
142
+ * * `whole-file-reach` — `import * as`, a side-effect import, `export *`, a
143
+ * `require()` or a dynamic `import()` with no named binding, at a file that
144
+ * is not a barrel. The symbol set is not knowable from the specifier, so the
145
+ * reach is the file's *whole* surface, internals included.
146
+ * * `unresolvable-reach` — a relative specifier that names no file the walk
147
+ * found. It is a finding and not a skip: !879 found #215 one layer in, where
148
+ * a walk of the right length had its *result* discarded downstream and
149
+ * reported clean behind a full-length `read:` line.
150
+ * * `unattributed-source` — a file under a module walk root that no module
151
+ * owns. Same reason: a file walked and not judged is worse than one not
152
+ * walked, because the `read:` line counts it.
153
+ * * `unpublished-subpath` — a bare specifier into the host package naming a
154
+ * subpath its `exports` map does not declare, the host's root among them
155
+ * (D-160.7 publishes no root export). Today `node` and `tsc` refuse it too,
156
+ * which is not a reason to leave it unjudged: what makes it a *finding* is
157
+ * that widening the map is the obvious repair, and the whole of D-160.8 is
158
+ * that widening the map is the thing an author must not do quietly.
159
+ * * `host-internal-subpath` — a bare specifier naming a subpath the `exports`
160
+ * map **does** declare and no barrel carries (D-160.14, feature 109). It is
161
+ * a third state and not a shade of the two above, and it is the only one of
162
+ * the six that `node` and `tsc` both accept: `./composition` resolves, so
163
+ * nothing but this check stands between a module and 27 composition symbols
164
+ * — `composeModules`, `createRootContainer`, `registerOrm` — that no module
165
+ * may name, production source or test. A module's server-bound test composes
166
+ * through the test kit's `composeTestServer`.
167
+ *
168
+ * It could not be a sixth {@link PUBLISHED_SUBPATHS} entry, measured:
169
+ * {@link PlatformSurface.published} is keyed by target file with no subpath
170
+ * dimension, so that entry would publish `composeModules` out of
171
+ * `kernel/compose.ts` for a module's *relative* reach as well — and, this
172
+ * check reporting `violations=0`, would change nothing it prints. The two
173
+ * lists answering differently is the mechanism, not a drift to reconcile:
174
+ * {@link HostPackage.declaredSubpaths} is the manifest's answer,
175
+ * {@link PUBLISHED_SUBPATHS} is the ruling's.
176
+ *
177
+ * ## One key space, and it is the repository's
178
+ *
179
+ * Every path here — a module source, a platform file, a barrel, a ledger key —
180
+ * is written relative to the **repository root**. That is more verbose than the
181
+ * `modules/blog/x.ts` shape the other ledgers use, and it is not a style
182
+ * choice: this check *resolves* specifiers, and a specifier resolves in exactly
183
+ * one namespace. `layout.keyOf` deliberately has two bases — an application
184
+ * file is keyed inside `backend/src`, a packaged module's file relative to the
185
+ * checkout, because there is no application prefix that would be true of the
186
+ * second — and a resolver straddling both is wrong for every reach that crosses
187
+ * between them. Measured on the split-tree fixture, where six modules live in
188
+ * `packages/modules/<id>/src`: 90 reaches resolved to nothing, and the whole
189
+ * point of that fixture is that a check keeps working while the layout moves.
190
+ *
191
+ * Plus the **refusals**, each over an input whose silent absence would narrow
192
+ * the answer rather than fail it:
193
+ *
194
+ * * a barrel this parse cannot read in full (an `export *`, a namespace
195
+ * re-export, an `export { … }` with no `from`) is exit 2, never a narrower
196
+ * published set. A short surface turns correct reaches into findings, and
197
+ * the obvious "repair" for one of those is to widen the barrel;
198
+ * * a workspace with no platform member, or a platform member publishing
199
+ * under no name — every bare reach would then be judged by nothing;
200
+ * * a module package whose `package.json` will not parse, which is what says
201
+ * whether it reaches the host at all;
202
+ * * and {@link hostDependentCoverage}'s shortfall: a module package that
203
+ * declares the host and contributed no host reach to this walk. That is
204
+ * issue #215's predicate over the population T060 restored, and it is the
205
+ * one derivation that would have caught T060's own defect — `manifest-index`
206
+ * counts modules that produced a *file*, which a packaged module does
207
+ * plentifully, and `platform-barrels` counts barrels, which a module move
208
+ * does not touch.
209
+ *
210
+ * ## The second consumer population (feature 115, D115-5)
211
+ *
212
+ * The rule above is *a reach into the host names a published subpath or a
213
+ * declared host-internal one, never a file inside the package by relative path*,
214
+ * and it was asked of **modules** only. The **application** writes the same
215
+ * reach — 84 of them, every one of which resolves in this checkout and in no
216
+ * instance built from published packages, which is the defect D-207 names — and
217
+ * was outside the population by construction: `moduleIdOf` answers `null` for
218
+ * every one of its files, so a green was honest about a population that did not
219
+ * contain them. {@link scanApplicationReaches} is that half, `relative-host-reach`
220
+ * is its finding, and {@link LedgeredHostReach} is its ledger's entry.
221
+ *
222
+ * It is one rule with two populations and not two rules: this analysis already
223
+ * derives the four inputs a second one would re-derive, and two derivations of
224
+ * one population are two answers waiting to disagree (D-100). Normative:
225
+ * `specs/115-lifecycle-container-move/contracts/host-reach-check.md`.
226
+ *
227
+ * Usage: `tsx scripts/check-platform-surface.ts [--list]`
228
+ * Exit 0 = every reach into the platform is published, declared or ledgered;
229
+ * exit 1 = at least one is not, or a ledger entry is stale;
230
+ * exit 2 = the walk, the index, a barrel or the application tree could not be read.
231
+ */
232
+ import { existsSync, readdirSync, statSync } from 'node:fs';
233
+ import { join, relative } from 'node:path';
234
+ import { moduleIdOf } from '../lib/module-population.js';
235
+ import { resolutionCandidates, resolveHostSpecifier, resolveRelative, } from '../lib/platform-surface.js';
236
+ import {} from '../lib/read-size.js';
237
+ import { namedSpecifiers } from '../lib/specifiers.js';
238
+ /** The symbol token recorded for a reach that names no symbol at all. */
239
+ export const WHOLE_FILE = '*';
240
+ /** The symbol token recorded for a finding that is about the file, not a name. */
241
+ export const NO_SYMBOL = '?';
242
+ /** The target token recorded for a source file no module owns. */
243
+ export const NO_MODULE = '(unattributed)';
244
+ /**
245
+ * The floor that follows the sweep: every module package whose manifest
246
+ * declares the host must have contributed a host reach to this walk.
247
+ *
248
+ * This is issue #215's predicate over the population T060 restored, and it is
249
+ * the one derivation that would have caught the defect. The other two cannot:
250
+ * `manifest-index` counts modules that produced a **file**, and a packaged
251
+ * module produces plenty; `platform-barrels` counts barrels, which the move
252
+ * does not touch. What fell was the *reaches*, and the number that recorded
253
+ * them was a snapshot in `test/helpers/check-read-sizes.ts` whose −10% floor
254
+ * absorbed one module's worth of the fall without a word.
255
+ *
256
+ * The declaration is a second author's, which is what makes it worth
257
+ * reconciling against: `manifests:generate` renders a module package's
258
+ * `peerDependencies` from the bare specifiers its sources import, and
259
+ * `manifests:check` fails on drift. So "the manifest says this package reaches
260
+ * the host" and "the walk read a reach from this package" are two derivations
261
+ * of one fact, and a walk that stopped reading bare specifiers makes them
262
+ * disagree in the same run.
263
+ *
264
+ * `null` rather than `expected: 0` for a tree with no module package that
265
+ * declares the host — every tree in this repository until !910, and every
266
+ * fixture workspace. An expectation of zero is itself a refusal in this
267
+ * grammar, and rightly: a floor that expects nothing is switched off.
268
+ */
269
+ export function hostDependentCoverage(packages, hostReachModules) {
270
+ const declaring = packages.filter((pkg) => pkg.dependsOnHost);
271
+ if (declaring.length === 0)
272
+ return null;
273
+ return {
274
+ source: 'host-dependents',
275
+ expected: declaring.length,
276
+ covered: declaring.filter((pkg) => hostReachModules.has(pkg.moduleId)).length,
277
+ };
278
+ }
279
+ /** `<module file>|<platform file>` — the ledger key and the identity of a reach. */
280
+ export function keyOf(finding) {
281
+ return `${finding.file}|${finding.target}`;
282
+ }
283
+ /** The file a specifier names, or `null` when the walk found no such file. */
284
+ /**
285
+ * A specifier's target file, or `null`. Exported because both hosts build the
286
+ * platform's published surface with it and a second resolver would be a second
287
+ * answer to "which file does this name".
288
+ */
289
+ export function resolveTarget(fromKey, specifier, files) {
290
+ const joined = resolveRelative(fromKey, specifier);
291
+ if (joined === null)
292
+ return null;
293
+ for (const candidate of resolutionCandidates(joined)) {
294
+ if (files.has(candidate))
295
+ return candidate;
296
+ }
297
+ return null;
298
+ }
299
+ /**
300
+ * Every module reach into the platform, judged against the published surface.
301
+ *
302
+ * Pure over source text, file keys and barrel-derived surface, so a fixture
303
+ * enters exactly where a run does — including the specifier extraction and the
304
+ * `.js` → `.ts` resolution, which is where a resolution bug would hide (issue
305
+ * #130).
306
+ */
307
+ export function scanPlatformSurface(input) {
308
+ const attribute = input.moduleIdOf ?? moduleIdOf;
309
+ const canonical = input.canonicalTargetOf ?? ((key) => key);
310
+ const host = input.host ?? null;
311
+ const platformSourceRoot = input.platformSourceRoot ?? null;
312
+ const withinPlatform = (key) => platformSourceRoot !== null &&
313
+ (key === platformSourceRoot || key.startsWith(`${platformSourceRoot}/`));
314
+ const findings = [];
315
+ const hostReachModules = new Set();
316
+ let reaches = 0;
317
+ for (const [file, text] of [...input.sources].sort(([a], [b]) => a.localeCompare(b))) {
318
+ const moduleId = attribute(file);
319
+ if (moduleId === null) {
320
+ // A file the walk opened and could not attribute. Skipping it would leave
321
+ // it counted in `read: files=` and judged by nothing, which is #215 one
322
+ // layer in (!879).
323
+ findings.push({
324
+ kind: 'unattributed-source',
325
+ file,
326
+ line: 1,
327
+ moduleId: null,
328
+ target: NO_MODULE,
329
+ symbol: NO_SYMBOL,
330
+ specifier: '',
331
+ });
332
+ continue;
333
+ }
334
+ // Attributed, and then exempt: a module inside the host package reaches the
335
+ // platform by relative path within one package — see `platformSourceRoot`.
336
+ if (withinPlatform(file))
337
+ continue;
338
+ for (const specifier of namedSpecifiers(text, file)) {
339
+ // A bare specifier into the host package is the same reach a module in
340
+ // the application tree writes relatively (feature 080, T060). Asked
341
+ // first, because a specifier that names the host is never a relative one
342
+ // and the two answers must not both be consulted.
343
+ const hostReach = resolveHostSpecifier(specifier.text, host);
344
+ if (hostReach !== null)
345
+ hostReachModules.add(moduleId);
346
+ if (hostReach?.kind === 'undeclared-subpath' || hostReach?.kind === 'host-internal-subpath') {
347
+ findings.push({
348
+ // The two are one branch and two verdicts on purpose: both are about
349
+ // the *subpath* rather than a symbol, and only one of them names a
350
+ // path the host's `exports` map resolves.
351
+ kind: hostReach.kind === 'undeclared-subpath'
352
+ ? 'unpublished-subpath'
353
+ : 'host-internal-subpath',
354
+ file,
355
+ line: specifier.line,
356
+ moduleId,
357
+ target: specifier.text,
358
+ symbol: NO_SYMBOL,
359
+ specifier: specifier.text,
360
+ });
361
+ continue;
362
+ }
363
+ // Someone else's package: `zod`, `@mikro-orm/core`, another module's.
364
+ if (hostReach === null && !specifier.text.startsWith('.'))
365
+ continue;
366
+ const resolved = hostReach === null ? resolveTarget(file, specifier.text, input.files) : hostReach.target;
367
+ const target = resolved === null ? null : canonical(resolved);
368
+ if (target === null) {
369
+ findings.push({
370
+ kind: 'unresolvable-reach',
371
+ file,
372
+ line: specifier.line,
373
+ moduleId,
374
+ target: specifier.text,
375
+ symbol: NO_SYMBOL,
376
+ specifier: specifier.text,
377
+ });
378
+ continue;
379
+ }
380
+ // Module → module is `check:module-boundary`'s rule, not this one's.
381
+ if (attribute(target) !== null)
382
+ continue;
383
+ const named = specifier.bindings.filter((binding) => !binding.startsWith('* as'));
384
+ const wholeFile = specifier.bindings.length === 0 || named.length < specifier.bindings.length;
385
+ if (wholeFile) {
386
+ reaches += 1;
387
+ // Reaching a barrel *is* reaching the published surface, whole or not.
388
+ if (!input.surface.barrels.has(target)) {
389
+ findings.push({
390
+ kind: 'whole-file-reach',
391
+ file,
392
+ line: specifier.line,
393
+ moduleId,
394
+ target,
395
+ symbol: WHOLE_FILE,
396
+ specifier: specifier.text,
397
+ });
398
+ }
399
+ }
400
+ const published = input.surface.published.get(target) ?? new Set();
401
+ for (const name of named) {
402
+ reaches += 1;
403
+ if (input.surface.barrels.has(target))
404
+ continue;
405
+ if (published.has(name))
406
+ continue;
407
+ findings.push({
408
+ kind: 'unpublished-symbol',
409
+ file,
410
+ line: specifier.line,
411
+ moduleId,
412
+ target,
413
+ symbol: name,
414
+ specifier: specifier.text,
415
+ });
416
+ }
417
+ }
418
+ }
419
+ findings.sort((a, b) => a.file === b.file
420
+ ? a.target === b.target
421
+ ? a.symbol.localeCompare(b.symbol)
422
+ : a.target.localeCompare(b.target)
423
+ : a.file.localeCompare(b.file));
424
+ return { reaches, findings, hostReachModules };
425
+ }
426
+ export function checkPlatformSurface(input, ledger) {
427
+ const scan = scanPlatformSurface(input);
428
+ const seen = new Map();
429
+ for (const finding of scan.findings) {
430
+ const symbols = seen.get(keyOf(finding)) ?? new Set();
431
+ symbols.add(finding.symbol);
432
+ seen.set(keyOf(finding), symbols);
433
+ }
434
+ const covers = (finding) => ledger[keyOf(finding)]?.symbols.includes(finding.symbol) === true;
435
+ const staleKeys = [];
436
+ const staleSymbols = [];
437
+ for (const [key, entry] of Object.entries(ledger)) {
438
+ const symbols = seen.get(key);
439
+ if (symbols === undefined) {
440
+ staleKeys.push(key);
441
+ continue;
442
+ }
443
+ for (const symbol of entry.symbols) {
444
+ if (!symbols.has(symbol))
445
+ staleSymbols.push(`${key}#${symbol}`);
446
+ }
447
+ }
448
+ return {
449
+ reaches: scan.reaches,
450
+ findings: scan.findings,
451
+ violations: scan.findings.filter((finding) => !covers(finding)),
452
+ ledgered: scan.findings.filter(covers),
453
+ staleKeys: staleKeys.sort(),
454
+ staleSymbols: staleSymbols.sort(),
455
+ unreadable: input.surface.unreadable,
456
+ hostReachModules: scan.hostReachModules,
457
+ };
458
+ }
459
+ /**
460
+ * The platform **source** file a specifier landing inside the member names, or
461
+ * `null`.
462
+ *
463
+ * The canonicalisation drops the member-relative path's **first segment** —
464
+ * whatever it is — and re-roots the remainder at the platform's source root. So
465
+ * `dist/lifecycle/manifest.js` and `src/lifecycle/manifest.ts` both canonicalise
466
+ * to `packages/platform/src/lifecycle/manifest.ts`, and the word `dist` appears
467
+ * nowhere: a build directory renamed in the package's own `tsconfig.build.json`
468
+ * arrives here by being renamed, not by anybody remembering this file (D-100).
469
+ *
470
+ * `null` for a specifier that lands inside the member and resolves to no source
471
+ * file. That is deliberate and is the contract's own ruling (§3): a relative
472
+ * specifier resolving to nothing inside the platform is not a host reach at all,
473
+ * and reporting it here would be this check answering `tsc`'s question. The
474
+ * risk it carries — a canonicalisation bug silently resolving everything to
475
+ * `null` — is not silent: the ledger is two-way, so a walk that stopped
476
+ * resolving reports every entry it holds as stale and exits 1.
477
+ */
478
+ export function canonicalPlatformFile(joined, input) {
479
+ const prefix = `${input.platformMemberRoot}/`;
480
+ if (!joined.startsWith(prefix))
481
+ return null;
482
+ const withinMember = joined.slice(prefix.length);
483
+ const cut = withinMember.indexOf('/');
484
+ if (cut <= 0)
485
+ return null;
486
+ const rest = withinMember.slice(cut + 1);
487
+ if (rest.length === 0)
488
+ return null;
489
+ for (const candidate of resolutionCandidates(`${input.platformSourceRoot}/${rest}`)) {
490
+ if (input.files.has(candidate))
491
+ return candidate;
492
+ }
493
+ return null;
494
+ }
495
+ /** The bare specifier carrying a platform file, or `null` when none does. */
496
+ function publishedSpecifierFor(target, input) {
497
+ const carriers = input.surface.publishedBy.get(target) ?? new Set();
498
+ for (const [subpath, barrel] of input.host.subpathTargets) {
499
+ // Reaching a barrel *is* reaching its subpath, whole or not.
500
+ if (barrel === target || carriers.has(barrel))
501
+ return `${input.host.name}/${subpath}`;
502
+ }
503
+ return null;
504
+ }
505
+ /**
506
+ * Every application reach into the platform written as a relative path.
507
+ *
508
+ * Pure over source text, file keys, barrel-derived surface and the two platform
509
+ * roots — so a fixture enters exactly where a run does, specifier extraction and
510
+ * `.js` → `.ts` resolution included (issue #130, and the `check-entry-scope`
511
+ * fixture that entered below its own classifier is the cautionary example).
512
+ *
513
+ * **A bare specifier into the host produces no finding here, and that is the
514
+ * discrimination the rule turns on.** `@endora-commerce/platform/kernel` and
515
+ * `@endora-commerce/platform/composition` are both correct from the application:
516
+ * the first is published and the second is the host-internal address the host is
517
+ * entitled to (D-160.14). The three-way answer for a bare specifier is a
518
+ * *module's* — `resolveHostSpecifier` in {@link scanPlatformSurface} — and asking
519
+ * it twice, with two populations and two verdicts, is two answers waiting to
520
+ * disagree.
521
+ */
522
+ export function scanApplicationReaches(input) {
523
+ const findings = [];
524
+ let reaches = 0;
525
+ for (const [file, text] of [...input.sources].sort(([a], [b]) => a.localeCompare(b))) {
526
+ for (const specifier of namedSpecifiers(text, file)) {
527
+ if (!specifier.text.startsWith('.'))
528
+ continue;
529
+ const joined = resolveRelative(file, specifier.text);
530
+ if (joined === null)
531
+ continue;
532
+ const target = canonicalPlatformFile(joined, input);
533
+ if (target === null)
534
+ continue;
535
+ reaches += 1;
536
+ findings.push({
537
+ kind: 'relative-host-reach',
538
+ file,
539
+ line: specifier.line,
540
+ moduleId: null,
541
+ target,
542
+ symbol: NO_SYMBOL,
543
+ specifier: specifier.text,
544
+ publishedAs: publishedSpecifierFor(target, input),
545
+ });
546
+ }
547
+ }
548
+ findings.sort((a, b) => a.file === b.file ? a.target.localeCompare(b.target) : a.file.localeCompare(b.file));
549
+ return { reaches, findings };
550
+ }
551
+ /**
552
+ * The application's reaches, judged against the ledger — both directions
553
+ * (host-reach-check.md §4 R4.3).
554
+ *
555
+ * One reach may appear twice under one key when a file names the same platform
556
+ * file from two specifiers; the ledger is keyed `(file, target)` and covers
557
+ * both, which is right: the entry's subject is the coupling, not the line.
558
+ */
559
+ export function checkApplicationReaches(input, ledger) {
560
+ const scan = scanApplicationReaches(input);
561
+ const seen = new Set(scan.findings.map(keyOf));
562
+ const covers = (finding) => ledger[keyOf(finding)] !== undefined;
563
+ return {
564
+ ...scan,
565
+ violations: scan.findings.filter((finding) => !covers(finding)),
566
+ ledgered: scan.findings.filter(covers),
567
+ staleKeys: Object.keys(ledger)
568
+ .filter((key) => !seen.has(key))
569
+ .sort(),
570
+ };
571
+ }
572
+ /**
573
+ * The coverage floor for the application half: the ledger's own still-on-disk
574
+ * file set, against what the walk opened of it.
575
+ *
576
+ * host-reach-check.md §5's fourth refusal, and it is `check:module-boundary`'s
577
+ * admin-host floor one package over — a floor derived from *the ledger* rather
578
+ * than from a count, so a walk that stopped reaching the files it is ledgered
579
+ * over is a refusal rather than a drained ledger. The ledger is a second
580
+ * author's answer to "which application files reach the platform": it was
581
+ * written by whoever measured the debt, and it goes stale loudly rather than
582
+ * quietly.
583
+ *
584
+ * `null` rather than `expected: 0` once the ledger empties, which is what this
585
+ * ledger is for: an expectation of zero is itself a refusal in this grammar
586
+ * (`readSizeRefusal`'s `no-expectation`), and rightly — a floor that expects
587
+ * nothing is switched off.
588
+ */
589
+ export function hostReachCoverage(ledger, onDisk, opened) {
590
+ const ledgeredFiles = new Set(Object.keys(ledger).map((key) => key.split('|')[0] ?? ''));
591
+ const expected = [...ledgeredFiles].filter((file) => onDisk(file));
592
+ if (expected.length === 0)
593
+ return null;
594
+ return {
595
+ source: 'host-reaches',
596
+ expected: expected.length,
597
+ covered: expected.filter((file) => opened.has(file)).length,
598
+ };
599
+ }
600
+ /**
601
+ * Why this run may not report on the application's reaches, or `null`.
602
+ *
603
+ * Two of host-reach-check.md §5's four refusals — the two that are facts about
604
+ * *this* half's inputs. The first ("no platform root") is already this check's
605
+ * own first refusal and stays there; the fourth is the ledger-derived coverage
606
+ * floor and belongs to `read-size.ts`, which is the one place in the estate that
607
+ * owns a short walk.
608
+ *
609
+ * Both fail in the direction that produces a **clean** result, which is why they
610
+ * are exit 2 and not a finding. An empty canonical-target map reports every
611
+ * relative reach as reaching nothing; an application walk that opened no file
612
+ * reports no reach at all — and this check's module half would go on printing
613
+ * `files=2069` beside it, because 1364 of `backend/src`'s files are a module's
614
+ * and a broken *application* walk leaves every one of those numbers healthy
615
+ * (issue #215's shape over this population).
616
+ */
617
+ export function applicationReachRefusal(input) {
618
+ if (input.canonicalTargets === 0) {
619
+ return ('the platform walk produced no source file — a relative reach into the platform is ' +
620
+ 'canonicalised against that walk, so every one of them would resolve to nothing and ' +
621
+ 'the application half would report clean over a population it never saw');
622
+ }
623
+ if (input.applicationFiles === 0) {
624
+ return ('the application walk opened no file — the module half of this check reads most of ' +
625
+ "`backend/src` and would keep printing a healthy `files=`, so a broken application " +
626
+ 'walk is a silence rather than a number that moved (issue #215)');
627
+ }
628
+ return null;
629
+ }
630
+ /**
631
+ * Why this run may not judge a reach against the surface it read, or `null`.
632
+ *
633
+ * Pure over the two things that can be silently missing — a barrel that is not
634
+ * there, and a re-export the parse cannot enumerate — because both fail in the
635
+ * same direction and it is the wrong one: a **short** published set turns
636
+ * correct reaches into findings, and the obvious repair for one of those is to
637
+ * widen the barrel. So it is exit 2, in the idiom of `readSizeRefusal`, and a
638
+ * proof enters where a run enters (issue #130).
639
+ */
640
+ export function platformSurfaceRefusal(input) {
641
+ if (input.missingBarrels.length > 0) {
642
+ return (`${input.missingBarrels.join(', ')} — a published subpath (D-160.7) whose barrel is ` +
643
+ 'not there. The published surface would come back short and every reach into that ' +
644
+ 'directory would read as a violation; refusing to report on it');
645
+ }
646
+ if (input.surface.unreadable.length > 0) {
647
+ const named = input.surface.unreadable
648
+ .map((entry) => `\n - ${entry.barrel}:${entry.line} ${entry.reason}`)
649
+ .join('');
650
+ return ('a published barrel holds a re-export this parse cannot enumerate, so the published ' +
651
+ 'surface would come back short — and the obvious "repair" for a reach it wrongly ' +
652
+ `refused is to widen the barrel:${named}`);
653
+ }
654
+ return null;
655
+ }
656
+ /**
657
+ * Every TypeScript source under `dir`, with the rule's own prunes applied.
658
+ *
659
+ * Exported because the population is part of the rule: two hosts computing
660
+ * "which files this check reads" two ways is the shape that lets one of them go
661
+ * half-blind.
662
+ */
663
+ export function collectPlatformSurfaceSources(dir, out = []) {
664
+ if (!existsSync(dir))
665
+ return out;
666
+ for (const name of readdirSync(dir)) {
667
+ const full = join(dir, name);
668
+ if (statSync(full).isDirectory()) {
669
+ if (name === 'node_modules' || name === 'dist')
670
+ continue;
671
+ collectPlatformSurfaceSources(full, out);
672
+ }
673
+ else if (
674
+ // `.tsx` since feature 091: a module package's `./admin` layer is React
675
+ // components, and a walk that cannot see them reports every relative
676
+ // reach into one as `unresolvable-reach` — a finding about the walk.
677
+ (name.endsWith('.ts') || name.endsWith('.tsx')) &&
678
+ !name.endsWith('.test.ts') &&
679
+ !name.endsWith('.test.tsx') &&
680
+ !name.endsWith('.d.ts')) {
681
+ out.push(full);
682
+ }
683
+ }
684
+ return out;
685
+ }
686
+ /**
687
+ * A module package's own tooling configuration, which is not module source.
688
+ *
689
+ * `vitest.config.ts` is the first `.ts` file a module package holds that its
690
+ * build does not compile (feature 089, Phase 1): `tsconfig.build.json` roots the
691
+ * emit at `src/`, and the manifest's `files` ships `dist` and the asset
692
+ * directories, so nothing a consumer installs contains it and no `exports`
693
+ * subpath can name it. The other configurations a package carries are `.json`
694
+ * and were therefore never in this walk at all.
695
+ *
696
+ * It has to leave the population rather than be made to resolve, because the
697
+ * file exists to `mergeConfig` the repository root's `vitest.config.base.ts` —
698
+ * which is where issue #255's foreign-workspace-link refusal lives, so the reach
699
+ * is mandatory — and that root is in no module walk root and no source root. A
700
+ * reach the walk cannot resolve is `unresolvable-reach`, deliberately fail-closed
701
+ * (#215 one layer in), and the honest answer here is that this file is not a
702
+ * module reach at all.
703
+ *
704
+ * Narrow on purpose: only a `*.config.ts` sitting **directly** at a module walk
705
+ * root, which is a package's own root or a module directory. A `config.ts` under
706
+ * `src/` stays module source, and a directory named `config/` is untouched.
707
+ */
708
+ export function isPackageToolingConfig(root, file) {
709
+ const within = relative(root, file).split('\\').join('/');
710
+ return !within.includes('/') && within.endsWith('.config.ts');
711
+ }
712
+ /** The remedy sentence a finding gets, by kind. */
713
+ /**
714
+ * The remedy paragraph for a finding.
715
+ *
716
+ * Exported because the remedy is part of the rule and not part of a host: an
717
+ * author outside this checkout reads the same sentence this repository's run
718
+ * prints.
719
+ */
720
+ export function remedyOf(finding) {
721
+ switch (finding.kind) {
722
+ case 'unpublished-symbol':
723
+ return `\`${finding.symbol}\` is not published out of ${finding.target}`;
724
+ case 'whole-file-reach':
725
+ return `reaches every export of ${finding.target}, internals included`;
726
+ case 'unresolvable-reach':
727
+ return `\`${finding.specifier}\` resolves to no file the walk found`;
728
+ case 'unattributed-source':
729
+ return 'the walk opened this file and no module owns it';
730
+ case 'unpublished-subpath':
731
+ return (`\`${finding.specifier}\` names no subpath the host publishes — its \`exports\` map ` +
732
+ 'refuses the path at resolution time');
733
+ case 'relative-host-reach':
734
+ return (`\`${finding.specifier}\` names ${finding.target} by relative path. ` +
735
+ (finding.publishedAs === null || finding.publishedAs === undefined
736
+ ? 'the platform declares no subpath carrying this file — the remedy is to declare ' +
737
+ 'one (host-internal unless a module needs it) or to stop reaching it'
738
+ : `the address is \`${finding.publishedAs}\``) +
739
+ '. A relative path into the package resolves in this checkout and in no instance, ' +
740
+ 'which is what stops a build made of published packages from working');
741
+ case 'host-internal-subpath':
742
+ return (`\`${finding.specifier}\` names a subpath the host declares for its own composition ` +
743
+ 'and publishes to nobody (host-package.md §2.7). It resolves, which is why this is ' +
744
+ 'the one finding here that neither `node` nor `tsc` would raise — and no module may ' +
745
+ 'name it, production source or test alike. A server-bound test composes through the ' +
746
+ 'test kit, never through `composeModules`');
747
+ }
748
+ }
749
+ //# sourceMappingURL=platform-surface.js.map