@fougere/core 0.2.0-alpha.2 → 0.4.0-alpha.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 (500) hide show
  1. package/README.md +3 -2
  2. package/dist/boot/AppLifecycle.d.ts +18 -0
  3. package/dist/boot/AppLifecycle.d.ts.map +1 -0
  4. package/dist/boot/AppLifecycle.js +42 -0
  5. package/dist/boot/AppLifecycle.js.map +1 -0
  6. package/dist/boot/Emissions.d.ts +126 -0
  7. package/dist/boot/Emissions.d.ts.map +1 -0
  8. package/dist/boot/Emissions.js +233 -0
  9. package/dist/boot/Emissions.js.map +1 -0
  10. package/dist/boot/HandlerFacade.d.ts +72 -0
  11. package/dist/boot/HandlerFacade.d.ts.map +1 -0
  12. package/dist/boot/HandlerFacade.js +174 -0
  13. package/dist/boot/HandlerFacade.js.map +1 -0
  14. package/dist/boot/Lifecycle.d.ts +4 -0
  15. package/dist/boot/Lifecycle.d.ts.map +1 -0
  16. package/dist/boot/Lifecycle.js +3 -0
  17. package/dist/boot/Lifecycle.js.map +1 -0
  18. package/dist/boot/ambient-port.d.ts +38 -0
  19. package/dist/boot/ambient-port.d.ts.map +1 -0
  20. package/dist/boot/ambient-port.js +2 -0
  21. package/dist/boot/ambient-port.js.map +1 -0
  22. package/dist/boot/ambient.als.d.ts +3 -0
  23. package/dist/boot/ambient.als.d.ts.map +1 -0
  24. package/dist/boot/ambient.als.js +34 -0
  25. package/dist/boot/ambient.als.js.map +1 -0
  26. package/dist/boot/ambient.queue.d.ts +10 -0
  27. package/dist/boot/ambient.queue.d.ts.map +1 -0
  28. package/dist/boot/ambient.queue.js +60 -0
  29. package/dist/boot/ambient.queue.js.map +1 -0
  30. package/dist/boot/apply.d.ts +22 -0
  31. package/dist/boot/apply.d.ts.map +1 -0
  32. package/dist/boot/apply.js +38 -0
  33. package/dist/boot/apply.js.map +1 -0
  34. package/dist/boot/auth.d.ts +53 -0
  35. package/dist/boot/auth.d.ts.map +1 -0
  36. package/dist/boot/auth.js +2 -0
  37. package/dist/boot/auth.js.map +1 -0
  38. package/dist/{binding.d.ts → boot/binding.d.ts} +14 -17
  39. package/dist/boot/binding.d.ts.map +1 -0
  40. package/dist/{binding.js → boot/binding.js} +19 -10
  41. package/dist/boot/binding.js.map +1 -0
  42. package/dist/{boot.d.ts → boot/boot.d.ts} +25 -5
  43. package/dist/boot/boot.d.ts.map +1 -0
  44. package/dist/boot/boot.js +70 -0
  45. package/dist/boot/boot.js.map +1 -0
  46. package/dist/boot/bootstrap.d.ts.map +1 -0
  47. package/dist/boot/bootstrap.js +722 -0
  48. package/dist/boot/bootstrap.js.map +1 -0
  49. package/dist/boot/frame.d.ts +44 -0
  50. package/dist/boot/frame.d.ts.map +1 -0
  51. package/dist/boot/frame.js +172 -0
  52. package/dist/boot/frame.js.map +1 -0
  53. package/dist/boot/ownership.d.ts +42 -0
  54. package/dist/boot/ownership.d.ts.map +1 -0
  55. package/dist/boot/ownership.js +127 -0
  56. package/dist/boot/ownership.js.map +1 -0
  57. package/dist/boot/ports.d.ts +17 -0
  58. package/dist/boot/ports.d.ts.map +1 -0
  59. package/dist/boot/ports.js +57 -0
  60. package/dist/boot/ports.js.map +1 -0
  61. package/dist/{remote.d.ts → boot/remote.d.ts} +11 -3
  62. package/dist/boot/remote.d.ts.map +1 -0
  63. package/dist/{remote.js → boot/remote.js} +26 -28
  64. package/dist/boot/remote.js.map +1 -0
  65. package/dist/{seed.d.ts → boot/seed.d.ts} +13 -1
  66. package/dist/boot/seed.d.ts.map +1 -0
  67. package/dist/{seed.js → boot/seed.js} +24 -3
  68. package/dist/boot/seed.js.map +1 -0
  69. package/dist/boot/together.d.ts +26 -0
  70. package/dist/boot/together.d.ts.map +1 -0
  71. package/dist/boot/together.js +163 -0
  72. package/dist/boot/together.js.map +1 -0
  73. package/dist/boot/types.d.ts +260 -0
  74. package/dist/boot/types.d.ts.map +1 -0
  75. package/dist/boot/types.js.map +1 -0
  76. package/dist/builtins/config.d.ts +9 -0
  77. package/dist/builtins/config.d.ts.map +1 -1
  78. package/dist/builtins/config.js +16 -1
  79. package/dist/builtins/config.js.map +1 -1
  80. package/dist/builtins/logger.d.ts +37 -4
  81. package/dist/builtins/logger.d.ts.map +1 -1
  82. package/dist/builtins/logger.js +66 -7
  83. package/dist/builtins/logger.js.map +1 -1
  84. package/dist/config-loader.d.ts +39 -35
  85. package/dist/config-loader.d.ts.map +1 -1
  86. package/dist/config-loader.js +15 -54
  87. package/dist/config-loader.js.map +1 -1
  88. package/dist/contract/Call.d.ts +10 -0
  89. package/dist/contract/Call.d.ts.map +1 -0
  90. package/dist/contract/Call.js +15 -0
  91. package/dist/contract/Call.js.map +1 -0
  92. package/dist/contract/Invocation.d.ts +27 -0
  93. package/dist/contract/Invocation.d.ts.map +1 -0
  94. package/dist/contract/Invocation.js +59 -0
  95. package/dist/contract/Invocation.js.map +1 -0
  96. package/dist/contract/RouteAddress.d.ts +17 -0
  97. package/dist/contract/RouteAddress.d.ts.map +1 -0
  98. package/dist/contract/RouteAddress.js +38 -0
  99. package/dist/contract/RouteAddress.js.map +1 -0
  100. package/dist/contract.d.ts +25 -19
  101. package/dist/contract.d.ts.map +1 -1
  102. package/dist/contract.js +25 -10
  103. package/dist/contract.js.map +1 -1
  104. package/dist/crypto/encoding.d.ts +25 -0
  105. package/dist/crypto/encoding.d.ts.map +1 -0
  106. package/dist/crypto/encoding.js +58 -0
  107. package/dist/crypto/encoding.js.map +1 -0
  108. package/dist/crypto/node.d.ts +4 -0
  109. package/dist/crypto/node.d.ts.map +1 -0
  110. package/dist/crypto/node.js +25 -0
  111. package/dist/crypto/node.js.map +1 -0
  112. package/dist/crypto/port.d.ts +42 -0
  113. package/dist/crypto/port.d.ts.map +1 -0
  114. package/dist/crypto/port.js +19 -0
  115. package/dist/crypto/port.js.map +1 -0
  116. package/dist/crypto/webcrypto.d.ts +4 -0
  117. package/dist/crypto/webcrypto.d.ts.map +1 -0
  118. package/dist/crypto/webcrypto.js +34 -0
  119. package/dist/crypto/webcrypto.js.map +1 -0
  120. package/dist/dispatch/ArgumentResolver.d.ts +10 -0
  121. package/dist/dispatch/ArgumentResolver.d.ts.map +1 -0
  122. package/dist/dispatch/ArgumentResolver.js +12 -0
  123. package/dist/dispatch/ArgumentResolver.js.map +1 -0
  124. package/dist/dispatch/ArrayResult.d.ts +3 -0
  125. package/dist/dispatch/ArrayResult.d.ts.map +1 -0
  126. package/dist/dispatch/ArrayResult.js +11 -0
  127. package/dist/dispatch/ArrayResult.js.map +1 -0
  128. package/dist/dispatch/DispatchEvent.d.ts +18 -0
  129. package/dist/dispatch/DispatchEvent.d.ts.map +1 -0
  130. package/dist/dispatch/DispatchEvent.js +30 -0
  131. package/dist/dispatch/DispatchEvent.js.map +1 -0
  132. package/dist/dispatch/DispatchLifecycle.d.ts +9 -0
  133. package/dist/dispatch/DispatchLifecycle.d.ts.map +1 -0
  134. package/dist/dispatch/DispatchLifecycle.js +25 -0
  135. package/dist/dispatch/DispatchLifecycle.js.map +1 -0
  136. package/dist/dispatch/DispatchPort.d.ts +6 -0
  137. package/dist/dispatch/DispatchPort.d.ts.map +1 -0
  138. package/dist/dispatch/DispatchPort.js +2 -0
  139. package/dist/dispatch/DispatchPort.js.map +1 -0
  140. package/dist/dispatch/Dispatcher.d.ts +16 -0
  141. package/dist/dispatch/Dispatcher.d.ts.map +1 -0
  142. package/dist/dispatch/Dispatcher.js +44 -0
  143. package/dist/dispatch/Dispatcher.js.map +1 -0
  144. package/dist/dispatch/InFlight.d.ts +12 -0
  145. package/dist/dispatch/InFlight.d.ts.map +1 -0
  146. package/dist/dispatch/InFlight.js +46 -0
  147. package/dist/dispatch/InFlight.js.map +1 -0
  148. package/dist/dispatch/InputValidator.d.ts +7 -0
  149. package/dist/dispatch/InputValidator.d.ts.map +1 -0
  150. package/dist/dispatch/InputValidator.js +22 -0
  151. package/dist/dispatch/InputValidator.js.map +1 -0
  152. package/dist/dispatch/LocalRoutePolicy.d.ts +11 -0
  153. package/dist/dispatch/LocalRoutePolicy.d.ts.map +1 -0
  154. package/dist/dispatch/LocalRoutePolicy.js +32 -0
  155. package/dist/dispatch/LocalRoutePolicy.js.map +1 -0
  156. package/dist/dispatch/OperationExecutor.d.ts +25 -0
  157. package/dist/dispatch/OperationExecutor.d.ts.map +1 -0
  158. package/dist/dispatch/OperationExecutor.js +32 -0
  159. package/dist/dispatch/OperationExecutor.js.map +1 -0
  160. package/dist/dispatch/OperationRoute.d.ts +13 -0
  161. package/dist/dispatch/OperationRoute.d.ts.map +1 -0
  162. package/dist/dispatch/OperationRoute.js +15 -0
  163. package/dist/dispatch/OperationRoute.js.map +1 -0
  164. package/dist/dispatch/OutputProjector.d.ts +12 -0
  165. package/dist/dispatch/OutputProjector.d.ts.map +1 -0
  166. package/dist/dispatch/OutputProjector.js +30 -0
  167. package/dist/dispatch/OutputProjector.js.map +1 -0
  168. package/dist/dispatch/OutputView.d.ts +8 -0
  169. package/dist/dispatch/OutputView.d.ts.map +1 -0
  170. package/dist/dispatch/OutputView.js +10 -0
  171. package/dist/dispatch/OutputView.js.map +1 -0
  172. package/dist/dispatch/PresenterArgumentResolver.d.ts +12 -0
  173. package/dist/dispatch/PresenterArgumentResolver.d.ts.map +1 -0
  174. package/dist/dispatch/PresenterArgumentResolver.js +20 -0
  175. package/dist/dispatch/PresenterArgumentResolver.js.map +1 -0
  176. package/dist/dispatch/PresenterExecutor.d.ts +11 -0
  177. package/dist/dispatch/PresenterExecutor.d.ts.map +1 -0
  178. package/dist/dispatch/PresenterExecutor.js +56 -0
  179. package/dist/dispatch/PresenterExecutor.js.map +1 -0
  180. package/dist/dispatch/RemoteRouteResolver.d.ts +13 -0
  181. package/dist/dispatch/RemoteRouteResolver.d.ts.map +1 -0
  182. package/dist/dispatch/RemoteRouteResolver.js +18 -0
  183. package/dist/dispatch/RemoteRouteResolver.js.map +1 -0
  184. package/dist/dispatch/Route.d.ts +10 -0
  185. package/dist/dispatch/Route.d.ts.map +1 -0
  186. package/dist/dispatch/Route.js +2 -0
  187. package/dist/dispatch/Route.js.map +1 -0
  188. package/dist/dispatch/RouteNotFoundError.d.ts +10 -0
  189. package/dist/dispatch/RouteNotFoundError.d.ts.map +1 -0
  190. package/dist/dispatch/RouteNotFoundError.js +24 -0
  191. package/dist/dispatch/RouteNotFoundError.js.map +1 -0
  192. package/dist/dispatch/RoutePolicy.d.ts +8 -0
  193. package/dist/dispatch/RoutePolicy.d.ts.map +1 -0
  194. package/dist/dispatch/RoutePolicy.js +2 -0
  195. package/dist/dispatch/RoutePolicy.js.map +1 -0
  196. package/dist/dispatch/RouteRegistry.d.ts +18 -0
  197. package/dist/dispatch/RouteRegistry.d.ts.map +1 -0
  198. package/dist/dispatch/RouteRegistry.js +62 -0
  199. package/dist/dispatch/RouteRegistry.js.map +1 -0
  200. package/dist/dispatch/RouteResolver.d.ts +7 -0
  201. package/dist/dispatch/RouteResolver.d.ts.map +1 -0
  202. package/dist/dispatch/RouteResolver.js +2 -0
  203. package/dist/dispatch/RouteResolver.js.map +1 -0
  204. package/dist/dispatch/StorageGuard.d.ts +15 -0
  205. package/dist/dispatch/StorageGuard.d.ts.map +1 -0
  206. package/dist/dispatch/StorageGuard.js +63 -0
  207. package/dist/dispatch/StorageGuard.js.map +1 -0
  208. package/dist/effective-operation.d.ts +90 -0
  209. package/dist/effective-operation.d.ts.map +1 -0
  210. package/dist/effective-operation.js +449 -0
  211. package/dist/effective-operation.js.map +1 -0
  212. package/dist/emit.d.ts +2 -2
  213. package/dist/emit.d.ts.map +1 -1
  214. package/dist/emit.js +2 -2
  215. package/dist/emit.js.map +1 -1
  216. package/dist/entry/DynamicFacade.d.ts +10 -0
  217. package/dist/entry/DynamicFacade.d.ts.map +1 -0
  218. package/dist/entry/DynamicFacade.js +27 -0
  219. package/dist/entry/DynamicFacade.js.map +1 -0
  220. package/dist/entry/FacadeEntry.d.ts +11 -0
  221. package/dist/entry/FacadeEntry.d.ts.map +1 -0
  222. package/dist/entry/FacadeEntry.js +26 -0
  223. package/dist/entry/FacadeEntry.js.map +1 -0
  224. package/dist/entry/TransportEntry.d.ts +13 -0
  225. package/dist/entry/TransportEntry.d.ts.map +1 -0
  226. package/dist/entry/TransportEntry.js +24 -0
  227. package/dist/entry/TransportEntry.js.map +1 -0
  228. package/dist/frond-config.d.ts +24 -7
  229. package/dist/frond-config.d.ts.map +1 -1
  230. package/dist/frond-config.js.map +1 -1
  231. package/dist/graph.d.ts +1 -1
  232. package/dist/graph.d.ts.map +1 -1
  233. package/dist/graph.js +3 -2
  234. package/dist/graph.js.map +1 -1
  235. package/dist/identity-keys.d.ts +14 -0
  236. package/dist/identity-keys.d.ts.map +1 -0
  237. package/dist/identity-keys.js +37 -0
  238. package/dist/identity-keys.js.map +1 -0
  239. package/dist/identity.d.ts +58 -0
  240. package/dist/identity.d.ts.map +1 -0
  241. package/dist/identity.js +182 -0
  242. package/dist/identity.js.map +1 -0
  243. package/dist/imports.d.ts +2 -2
  244. package/dist/imports.d.ts.map +1 -1
  245. package/dist/imports.js +7 -3
  246. package/dist/imports.js.map +1 -1
  247. package/dist/index.d.ts +57 -38
  248. package/dist/index.d.ts.map +1 -1
  249. package/dist/index.js +38 -32
  250. package/dist/index.js.map +1 -1
  251. package/dist/loader.d.ts +16 -0
  252. package/dist/loader.d.ts.map +1 -0
  253. package/dist/loader.js +25 -0
  254. package/dist/loader.js.map +1 -0
  255. package/dist/node.d.ts +21 -0
  256. package/dist/node.d.ts.map +1 -0
  257. package/dist/node.js +24 -0
  258. package/dist/node.js.map +1 -0
  259. package/dist/orm.d.ts +69 -8
  260. package/dist/orm.d.ts.map +1 -1
  261. package/dist/orm.js +35 -2
  262. package/dist/orm.js.map +1 -1
  263. package/dist/prefab/collector.d.ts +29 -0
  264. package/dist/prefab/collector.d.ts.map +1 -0
  265. package/dist/prefab/collector.js +34 -0
  266. package/dist/prefab/collector.js.map +1 -0
  267. package/dist/{crud.d.ts → prefab/crud.d.ts} +7 -8
  268. package/dist/prefab/crud.d.ts.map +1 -0
  269. package/dist/{crud.js → prefab/crud.js} +39 -5
  270. package/dist/prefab/crud.js.map +1 -0
  271. package/dist/prefab/mirror.d.ts +52 -0
  272. package/dist/prefab/mirror.d.ts.map +1 -0
  273. package/dist/prefab/mirror.js +66 -0
  274. package/dist/prefab/mirror.js.map +1 -0
  275. package/dist/prefab/prefab.d.ts +15 -0
  276. package/dist/prefab/prefab.d.ts.map +1 -0
  277. package/dist/prefab/prefab.js +19 -0
  278. package/dist/prefab/prefab.js.map +1 -0
  279. package/dist/{presenter.d.ts → prefab/presenter.d.ts} +6 -17
  280. package/dist/prefab/presenter.d.ts.map +1 -0
  281. package/dist/{presenter.js → prefab/presenter.js} +5 -23
  282. package/dist/prefab/presenter.js.map +1 -0
  283. package/dist/prefab/repository.d.ts +104 -0
  284. package/dist/prefab/repository.d.ts.map +1 -0
  285. package/dist/prefab/repository.js +62 -0
  286. package/dist/prefab/repository.js.map +1 -0
  287. package/dist/scan/Fronds.d.ts +45 -0
  288. package/dist/scan/Fronds.d.ts.map +1 -0
  289. package/dist/scan/Fronds.js +66 -0
  290. package/dist/scan/Fronds.js.map +1 -0
  291. package/dist/scan/conventions.d.ts +74 -0
  292. package/dist/scan/conventions.d.ts.map +1 -0
  293. package/dist/scan/conventions.js +82 -0
  294. package/dist/scan/conventions.js.map +1 -0
  295. package/dist/scan/emit.d.ts +14 -0
  296. package/dist/scan/emit.d.ts.map +1 -0
  297. package/dist/scan/emit.js +212 -0
  298. package/dist/scan/emit.js.map +1 -0
  299. package/dist/scan/frond.d.ts +224 -0
  300. package/dist/scan/frond.d.ts.map +1 -0
  301. package/dist/scan/frond.js +2 -0
  302. package/dist/scan/frond.js.map +1 -0
  303. package/dist/{handler-parser.d.ts → scan/handler-parser.d.ts} +20 -9
  304. package/dist/scan/handler-parser.d.ts.map +1 -0
  305. package/dist/scan/handler-parser.js +665 -0
  306. package/dist/scan/handler-parser.js.map +1 -0
  307. package/dist/scan/scan-cache.d.ts +16 -0
  308. package/dist/scan/scan-cache.d.ts.map +1 -0
  309. package/dist/{scan-cache.js → scan/scan-cache.js} +27 -20
  310. package/dist/scan/scan-cache.js.map +1 -0
  311. package/dist/scan/scanner.d.ts +20 -0
  312. package/dist/scan/scanner.d.ts.map +1 -0
  313. package/dist/{scanner.js → scan/scanner.js} +212 -167
  314. package/dist/scan/scanner.js.map +1 -0
  315. package/dist/verify.d.ts +16 -3
  316. package/dist/verify.d.ts.map +1 -1
  317. package/dist/verify.js +21 -18
  318. package/dist/verify.js.map +1 -1
  319. package/dist/{call.d.ts → wire/call.d.ts} +76 -7
  320. package/dist/wire/call.d.ts.map +1 -0
  321. package/dist/{call.js → wire/call.js} +53 -80
  322. package/dist/wire/call.js.map +1 -0
  323. package/dist/{middleware.d.ts → wire/errors.d.ts} +18 -19
  324. package/dist/wire/errors.d.ts.map +1 -0
  325. package/dist/{middleware.js → wire/errors.js} +26 -14
  326. package/dist/wire/errors.js.map +1 -0
  327. package/dist/{errors.d.ts → wire/http-error.d.ts} +5 -6
  328. package/dist/wire/http-error.d.ts.map +1 -0
  329. package/dist/{errors.js → wire/http-error.js} +7 -6
  330. package/dist/wire/http-error.js.map +1 -0
  331. package/dist/wire/invocation.d.ts +4 -0
  332. package/dist/wire/invocation.d.ts.map +1 -0
  333. package/dist/wire/invocation.js +3 -0
  334. package/dist/wire/invocation.js.map +1 -0
  335. package/dist/{middleware/logger.d.ts → wire/loggerMiddleware.d.ts} +3 -3
  336. package/dist/wire/loggerMiddleware.d.ts.map +1 -0
  337. package/dist/{middleware/logger.js → wire/loggerMiddleware.js} +2 -2
  338. package/dist/wire/loggerMiddleware.js.map +1 -0
  339. package/dist/wire/middleware.d.ts +29 -0
  340. package/dist/wire/middleware.d.ts.map +1 -0
  341. package/dist/wire/middleware.js +20 -0
  342. package/dist/wire/middleware.js.map +1 -0
  343. package/dist/{operation.d.ts → wire/operation.d.ts} +39 -10
  344. package/dist/wire/operation.d.ts.map +1 -0
  345. package/dist/wire/operation.js +167 -0
  346. package/dist/wire/operation.js.map +1 -0
  347. package/package.json +27 -8
  348. package/src/boot/AppLifecycle.ts +50 -0
  349. package/src/boot/Emissions.ts +263 -0
  350. package/src/boot/HandlerFacade.ts +242 -0
  351. package/src/boot/Lifecycle.ts +3 -0
  352. package/src/boot/ambient-port.ts +41 -0
  353. package/src/boot/ambient.als.ts +43 -0
  354. package/src/boot/ambient.queue.ts +75 -0
  355. package/src/boot/apply.ts +47 -0
  356. package/src/boot/auth.ts +55 -0
  357. package/src/boot/binding.ts +182 -0
  358. package/src/boot/boot.ts +125 -0
  359. package/src/boot/bootstrap.ts +839 -0
  360. package/src/boot/frame.ts +210 -0
  361. package/src/boot/ownership.ts +140 -0
  362. package/src/boot/ports.ts +66 -0
  363. package/src/boot/remote.ts +179 -0
  364. package/src/boot/seed.ts +158 -0
  365. package/src/boot/together.ts +215 -0
  366. package/src/boot/types.ts +261 -0
  367. package/src/builtins/config.ts +19 -0
  368. package/src/builtins/logger.ts +175 -0
  369. package/src/config-loader.ts +146 -0
  370. package/src/contract/Call.ts +18 -0
  371. package/src/contract/Invocation.ts +72 -0
  372. package/src/contract/RouteAddress.ts +49 -0
  373. package/src/contract.ts +74 -0
  374. package/src/crypto/encoding.ts +59 -0
  375. package/src/crypto/node.ts +30 -0
  376. package/src/crypto/port.ts +45 -0
  377. package/src/crypto/webcrypto.ts +55 -0
  378. package/src/define.ts +10 -0
  379. package/src/dispatch/ArgumentResolver.ts +17 -0
  380. package/src/dispatch/ArrayResult.ts +10 -0
  381. package/src/dispatch/DispatchEvent.ts +38 -0
  382. package/src/dispatch/DispatchLifecycle.ts +23 -0
  383. package/src/dispatch/DispatchPort.ts +6 -0
  384. package/src/dispatch/Dispatcher.ts +48 -0
  385. package/src/dispatch/InFlight.ts +49 -0
  386. package/src/dispatch/InputValidator.ts +28 -0
  387. package/src/dispatch/LocalRoutePolicy.ts +34 -0
  388. package/src/dispatch/OperationExecutor.ts +60 -0
  389. package/src/dispatch/OperationRoute.ts +18 -0
  390. package/src/dispatch/OutputProjector.ts +32 -0
  391. package/src/dispatch/OutputView.ts +9 -0
  392. package/src/dispatch/PresenterArgumentResolver.ts +25 -0
  393. package/src/dispatch/PresenterExecutor.ts +55 -0
  394. package/src/dispatch/RemoteRouteResolver.ts +21 -0
  395. package/src/dispatch/Route.ts +11 -0
  396. package/src/dispatch/RouteNotFoundError.ts +29 -0
  397. package/src/dispatch/RoutePolicy.ts +8 -0
  398. package/src/dispatch/RouteRegistry.ts +73 -0
  399. package/src/dispatch/RouteResolver.ts +7 -0
  400. package/src/dispatch/StorageGuard.ts +71 -0
  401. package/src/effective-operation.ts +597 -0
  402. package/src/emit.ts +90 -0
  403. package/src/entry/DynamicFacade.ts +28 -0
  404. package/src/entry/FacadeEntry.ts +32 -0
  405. package/src/entry/TransportEntry.ts +30 -0
  406. package/src/frond-config.ts +138 -0
  407. package/src/graph.ts +180 -0
  408. package/src/identity-keys.ts +43 -0
  409. package/src/identity.ts +235 -0
  410. package/src/imports.ts +123 -0
  411. package/src/index.ts +106 -0
  412. package/src/loader.ts +41 -0
  413. package/src/node.ts +28 -0
  414. package/src/orm.ts +252 -0
  415. package/src/prefab/collector.ts +36 -0
  416. package/src/prefab/crud.ts +188 -0
  417. package/src/prefab/mirror.ts +122 -0
  418. package/src/prefab/prefab.ts +46 -0
  419. package/src/prefab/presenter.ts +63 -0
  420. package/src/prefab/repository.ts +170 -0
  421. package/src/scan/Fronds.ts +73 -0
  422. package/src/scan/conventions.ts +122 -0
  423. package/src/scan/emit.ts +231 -0
  424. package/src/scan/frond.ts +235 -0
  425. package/src/scan/handler-parser.ts +818 -0
  426. package/src/scan/scan-cache.ts +115 -0
  427. package/src/scan/scanner.ts +758 -0
  428. package/src/verify.ts +193 -0
  429. package/src/wire/call.ts +369 -0
  430. package/src/wire/errors.ts +124 -0
  431. package/src/wire/http-error.ts +77 -0
  432. package/src/wire/invocation.ts +10 -0
  433. package/src/wire/loggerMiddleware.ts +29 -0
  434. package/src/wire/middleware.ts +47 -0
  435. package/src/wire/operation.ts +258 -0
  436. package/dist/binding.d.ts.map +0 -1
  437. package/dist/binding.js.map +0 -1
  438. package/dist/boot.d.ts.map +0 -1
  439. package/dist/boot.js +0 -53
  440. package/dist/boot.js.map +0 -1
  441. package/dist/bootstrap.d.ts.map +0 -1
  442. package/dist/bootstrap.js +0 -879
  443. package/dist/bootstrap.js.map +0 -1
  444. package/dist/call.d.ts.map +0 -1
  445. package/dist/call.js.map +0 -1
  446. package/dist/collector.d.ts +0 -28
  447. package/dist/collector.d.ts.map +0 -1
  448. package/dist/collector.js +0 -32
  449. package/dist/collector.js.map +0 -1
  450. package/dist/crud.d.ts.map +0 -1
  451. package/dist/crud.js.map +0 -1
  452. package/dist/egress.d.ts +0 -52
  453. package/dist/egress.d.ts.map +0 -1
  454. package/dist/egress.js +0 -186
  455. package/dist/egress.js.map +0 -1
  456. package/dist/errors.d.ts.map +0 -1
  457. package/dist/errors.js.map +0 -1
  458. package/dist/handler-parser.d.ts.map +0 -1
  459. package/dist/handler-parser.js +0 -505
  460. package/dist/handler-parser.js.map +0 -1
  461. package/dist/invocation.d.ts +0 -20
  462. package/dist/invocation.d.ts.map +0 -1
  463. package/dist/invocation.js +0 -15
  464. package/dist/invocation.js.map +0 -1
  465. package/dist/middleware/error-handler.d.ts +0 -15
  466. package/dist/middleware/error-handler.d.ts.map +0 -1
  467. package/dist/middleware/error-handler.js +0 -29
  468. package/dist/middleware/error-handler.js.map +0 -1
  469. package/dist/middleware/logger.d.ts.map +0 -1
  470. package/dist/middleware/logger.js.map +0 -1
  471. package/dist/middleware.d.ts.map +0 -1
  472. package/dist/middleware.js.map +0 -1
  473. package/dist/mirror.d.ts +0 -93
  474. package/dist/mirror.d.ts.map +0 -1
  475. package/dist/mirror.js +0 -96
  476. package/dist/mirror.js.map +0 -1
  477. package/dist/operation.d.ts.map +0 -1
  478. package/dist/operation.js +0 -95
  479. package/dist/operation.js.map +0 -1
  480. package/dist/presenter.d.ts.map +0 -1
  481. package/dist/presenter.js.map +0 -1
  482. package/dist/remote.d.ts.map +0 -1
  483. package/dist/remote.js.map +0 -1
  484. package/dist/repository.d.ts +0 -55
  485. package/dist/repository.d.ts.map +0 -1
  486. package/dist/repository.js +0 -26
  487. package/dist/repository.js.map +0 -1
  488. package/dist/scan-cache.d.ts +0 -10
  489. package/dist/scan-cache.d.ts.map +0 -1
  490. package/dist/scan-cache.js.map +0 -1
  491. package/dist/scanner.d.ts +0 -34
  492. package/dist/scanner.d.ts.map +0 -1
  493. package/dist/scanner.js.map +0 -1
  494. package/dist/seed.d.ts.map +0 -1
  495. package/dist/seed.js.map +0 -1
  496. package/dist/types.d.ts +0 -421
  497. package/dist/types.d.ts.map +0 -1
  498. package/dist/types.js.map +0 -1
  499. /package/dist/{bootstrap.d.ts → boot/bootstrap.d.ts} +0 -0
  500. /package/dist/{types.js → boot/types.js} +0 -0
@@ -0,0 +1,597 @@
1
+ /**
2
+ * The operation model after every declaration and convention has been resolved.
3
+ *
4
+ * The scanner produces evidence. This module turns that evidence into the one table
5
+ * boot, check, explain and protocol projections consume. It is deliberately pure: no
6
+ * container, filesystem, migration or handler construction is involved.
7
+ */
8
+ import { lowerFirst, type SchemaView } from '@fougere/schema';
9
+ import type { BindingPlan } from './boot/binding.js';
10
+ import { targetOf } from './prefab/prefab.js';
11
+ import type {
12
+ CollectorEntry,
13
+ FrondDescriptor,
14
+ HandlerEntry,
15
+ ScanDiagnostic,
16
+ } from './scan/frond.js';
17
+ import { verify } from './verify.js';
18
+ import {
19
+ inferOperationKind,
20
+ resolveContracts,
21
+ type OperationContract,
22
+ type OperationKind,
23
+ type ParsedType,
24
+ knownVerbs,
25
+ } from './wire/operation.js';
26
+
27
+ type Binding = BindingPlan[number];
28
+
29
+ export interface EffectiveParameter {
30
+ /** Position in the TypeScript signature. Never used to infer provenance. */
31
+ position: number;
32
+ name: string;
33
+ type: string | null;
34
+ optional: boolean;
35
+ nullable: boolean;
36
+ /** `?` and `| undefined` both mean canonical absence. */
37
+ undefinable: boolean;
38
+ /** The resolved provenance of this parameter. */
39
+ binding: Binding;
40
+ }
41
+
42
+ export interface EffectiveCollector {
43
+ parameter: string;
44
+ typeName: string;
45
+ className: string;
46
+ frond: string;
47
+ filePath: string;
48
+ }
49
+
50
+ export interface EffectiveOperation extends OperationContract {
51
+ /** Stable qualified identity: `blog/public/Post.publish`. */
52
+ id: string;
53
+ /** Human-facing identity independent of placement: `Post.publish`. */
54
+ operation: string;
55
+ name: string;
56
+ kind: OperationKind;
57
+ kindSource: 'explicit' | 'convention';
58
+ handler: {
59
+ className: string;
60
+ address: string;
61
+ filePath: string;
62
+ };
63
+ /** The class and method that execute after an optional operation override. */
64
+ implementation: {
65
+ className: string;
66
+ address: string;
67
+ method: string;
68
+ filePath: string;
69
+ };
70
+ /** Every valid operation has a plan, including the empty plan. */
71
+ binding: BindingPlan;
72
+ parameters: EffectiveParameter[];
73
+ collectors: EffectiveCollector[];
74
+ contexts: string[];
75
+ placement: {
76
+ frond: string;
77
+ runtime: 'local' | 'remote';
78
+ remote?: string;
79
+ };
80
+ exposure: {
81
+ surfaces: string[];
82
+ adapters: string[];
83
+ };
84
+ /** Whether output is an explicitly closed per-operation view. */
85
+ outputClosed: boolean;
86
+ semantics: typeof EFFECTIVE_OPERATION_SEMANTICS;
87
+ }
88
+
89
+ export type EffectiveOperationsMap = Map<string, EffectiveOperation>;
90
+
91
+ /** The absence rules every door normalises to before the handler is invoked. */
92
+ export const EFFECTIVE_OPERATION_SEMANTICS = Object.freeze({
93
+ optional: 'undefined' as const,
94
+ undefined: 'absence' as const,
95
+ null: 'explicit-value' as const,
96
+ jsonObjectUndefined: 'omitted' as const,
97
+ });
98
+
99
+ export interface EffectiveOperationOptions {
100
+ diagnostics?: readonly ScanDiagnostic[];
101
+ remotes?: Record<string, string>;
102
+ adapters?: Record<string, boolean | undefined>;
103
+ }
104
+
105
+ /**
106
+ * One resolved program. The handler map preserves object identity from the scan, so the
107
+ * boot never has to find a handler again by an order-dependent name lookup.
108
+ */
109
+ export class EffectiveOperationModel {
110
+ readonly operations: EffectiveOperation[];
111
+ readonly diagnostics: ScanDiagnostic[];
112
+ readonly resolutionDiagnostics: ScanDiagnostic[];
113
+
114
+ constructor(
115
+ operations: EffectiveOperation[],
116
+ diagnostics: ScanDiagnostic[],
117
+ resolutionDiagnostics: ScanDiagnostic[],
118
+ private readonly byHandler: Map<HandlerEntry, EffectiveOperationsMap>,
119
+ ) {
120
+ this.operations = operations;
121
+ this.diagnostics = diagnostics;
122
+ this.resolutionDiagnostics = resolutionDiagnostics;
123
+ }
124
+
125
+ forHandler(handler: HandlerEntry): EffectiveOperationsMap {
126
+ return this.byHandler.get(handler) ?? new Map();
127
+ }
128
+ }
129
+
130
+ /** Resolve the complete operation model without starting the application. */
131
+ export function resolveEffectiveOperations(
132
+ fronds: readonly FrondDescriptor[],
133
+ options: EffectiveOperationOptions = {},
134
+ ): EffectiveOperationModel {
135
+ const operations: EffectiveOperation[] = [];
136
+ const byHandler = new Map<HandlerEntry, EffectiveOperationsMap>();
137
+ const resolutionDiagnostics: ScanDiagnostic[] = [];
138
+ const scanDiagnostics = [...(options.diagnostics ?? [])];
139
+ const schemas = new Map(
140
+ fronds.flatMap((frond) => frond.entities.map((entity) => [entity.name, entity.entityClass] as const)),
141
+ );
142
+
143
+ // Input ambiguity is produced where schemas are available (the scanner), but it is a
144
+ // resolution failure. Lift it into the same refusal table as kind/binding/topology.
145
+ resolutionDiagnostics.push(...scanDiagnostics.filter((diagnostic) =>
146
+ diagnostic.code === 'input-contract-ambiguous'));
147
+
148
+ for (const frond of fronds) {
149
+ const collectorsByType = groupedCollectors(frond.collectors);
150
+ for (const [typeName, collectors] of collectorsByType) {
151
+ if (collectors.length < 2) continue;
152
+ resolutionDiagnostics.push({
153
+ severity: 'blocking',
154
+ code: 'collector-ambiguous',
155
+ filePath: collectors[0]!.filePath,
156
+ frond: frond.name,
157
+ subject: typeName,
158
+ message: `Frond '${frond.name}' declares ${collectors.length} collectors for '${typeName}' — `
159
+ + `${collectors.map((collector) => collector.ctor.name).sort().join(', ')}. `
160
+ + 'Exactly one collector must own a parameter type.',
161
+ });
162
+ }
163
+
164
+ const collectorNames = new Set(frond.collectors.map((collector) => collector.typeName));
165
+ for (const handler of frond.handlers) {
166
+ const effective = new Map<string, EffectiveOperation>();
167
+ byHandler.set(handler, effective);
168
+ const contracts = resolveContracts(handler, frond.operationsOverrides, collectorNames);
169
+
170
+ for (const [name, rawContract] of contracts) {
171
+ const subject = `${handler.ctor.name}.${name}`;
172
+ const contract = normalizeBinding(rawContract, handler, frond, name, resolutionDiagnostics);
173
+ if (!contract) continue;
174
+
175
+ const override = frond.operationsOverrides?.[name];
176
+ const inference = inferOperationKind(name);
177
+ const inferredKinds = new Set<OperationKind>();
178
+ if (inference.kind) inferredKinds.add(inference.kind);
179
+ if (inference.queryMatches.length > 0) inferredKinds.add('query');
180
+ if (inference.commandMatches.length > 0) inferredKinds.add('command');
181
+ const kind = override?.kind
182
+ ?? (inferredKinds.size === 1 ? [...inferredKinds][0] : undefined);
183
+ if (!kind) {
184
+ const ambiguous = inferredKinds.size > 1;
185
+ resolutionDiagnostics.push({
186
+ severity: 'blocking',
187
+ code: ambiguous ? 'operation-kind-ambiguous' : 'operation-kind-unknown',
188
+ filePath: handler.filePath,
189
+ frond: frond.name,
190
+ subject,
191
+ message: ambiguous
192
+ ? `Cannot resolve the kind of ${subject}: its name carries query evidence `
193
+ + `(${inference.queryMatches.join(', ')}) and command evidence `
194
+ + `(${inference.commandMatches.join(', ')}). `
195
+ + `Declare operations.${name}.kind.`
196
+ : `Cannot resolve the kind of ${subject}: '${name}' leads with no known verb. `
197
+ + `Rename it to lead with one, or declare operations.${name}.kind in `
198
+ + `frond.config.ts.\n query: ${knownVerbs().query.join(', ')}`
199
+ + `\n command: ${knownVerbs().command.join(', ')}`,
200
+ });
201
+ continue;
202
+ }
203
+
204
+ const implementation = implementationOf(frond, handler, name, resolutionDiagnostics);
205
+ if (!implementation) continue;
206
+
207
+ const params = contract.signature?.params ?? [];
208
+ const parameters = contract.binding.map((binding, position) => {
209
+ const param = params[position];
210
+ const undefinable = binding.optional
211
+ || param?.optional === true
212
+ || param?.type.undefined === true;
213
+ return {
214
+ position,
215
+ name: param?.name ?? binding.name,
216
+ type: param?.type.raw ?? null,
217
+ optional: undefinable,
218
+ nullable: param?.type.nullable === true,
219
+ undefinable,
220
+ binding,
221
+ } satisfies EffectiveParameter;
222
+ });
223
+
224
+ if (!validateProvenance(
225
+ fronds,
226
+ frond,
227
+ handler,
228
+ name,
229
+ contract,
230
+ parameters,
231
+ collectorsByType,
232
+ resolutionDiagnostics,
233
+ )) continue;
234
+
235
+ // A fact is judged by the entity it names. This used to be patched into the
236
+ // facade after resolution, leaving check/explain with a different input.
237
+ let input = contract.input;
238
+ if (!input) {
239
+ const fact = contract.binding.find((binding) => binding.source.kind === 'fact');
240
+ if (fact?.source.kind === 'fact') input = schemas.get(fact.source.factName);
241
+ }
242
+
243
+ const output = effectiveOutput(frond, handler, name, contract);
244
+ const className = handler.ctor.name.endsWith('Handler')
245
+ ? handler.ctor.name.slice(0, -'Handler'.length)
246
+ : handler.ctor.name;
247
+ const surface = handler.surface ? `/${handler.surface}` : '/default';
248
+ const remote = options.remotes?.[frond.name];
249
+ const op: EffectiveOperation = {
250
+ ...contract,
251
+ ...(input ? { input } : {}),
252
+ ...(output.schema ? { output: output.schema } : {}),
253
+ id: `${frond.name}${surface}/${className}.${name}`,
254
+ operation: `${className}.${name}`,
255
+ name,
256
+ kind,
257
+ kindSource: override?.kind
258
+ ? 'explicit'
259
+ : 'convention',
260
+ handler: {
261
+ className: handler.ctor.name,
262
+ address: handler.address,
263
+ filePath: handler.filePath,
264
+ },
265
+ implementation,
266
+ binding: contract.binding,
267
+ parameters,
268
+ collectors: parameters.flatMap((parameter) => {
269
+ if (parameter.binding.source.kind !== 'collector') return [];
270
+ const collector = collectorsByType.get(parameter.binding.source.typeName)?.[0];
271
+ return collector ? [{
272
+ parameter: parameter.name,
273
+ typeName: collector.typeName,
274
+ className: collector.ctor.name,
275
+ frond: frond.name,
276
+ filePath: collector.filePath,
277
+ }] : [];
278
+ }),
279
+ contexts: parameters
280
+ .filter((parameter) => parameter.binding.source.kind === 'context')
281
+ .map((parameter) => parameter.name),
282
+ placement: {
283
+ frond: frond.name,
284
+ runtime: remote ? 'remote' : 'local',
285
+ ...(remote ? { remote } : {}),
286
+ },
287
+ exposure: {
288
+ surfaces: surfacesOf(frond, handler),
289
+ adapters: exposedAdapters(handler, options.adapters),
290
+ },
291
+ outputClosed: output.closed,
292
+ semantics: EFFECTIVE_OPERATION_SEMANTICS,
293
+ };
294
+ effective.set(name, op);
295
+ operations.push(op);
296
+ }
297
+ }
298
+ }
299
+
300
+ // Topology and DI are part of the same effective program. A dependency targeting an
301
+ // actually remote frond is a refusal, not the warning appropriate to a future split.
302
+ for (const violation of verify({ fronds })) {
303
+ const remoteBoundary = options.remotes?.[violation.frond] !== undefined
304
+ || options.remotes?.[violation.dependsOn.frond] !== undefined;
305
+ resolutionDiagnostics.push({
306
+ severity: remoteBoundary && violation.rule === 'cross-frond-dependency'
307
+ ? 'blocking'
308
+ : violation.severity,
309
+ code: violation.rule,
310
+ filePath: violation.filePath,
311
+ frond: violation.frond,
312
+ subject: violation.subject,
313
+ message: violation.message,
314
+ });
315
+ }
316
+
317
+ const resolution = uniqueDiagnostics(resolutionDiagnostics);
318
+ return new EffectiveOperationModel(
319
+ operations,
320
+ uniqueDiagnostics([...scanDiagnostics, ...resolution]),
321
+ resolution,
322
+ byHandler,
323
+ );
324
+ }
325
+
326
+ function groupedCollectors(collectors: readonly CollectorEntry[]): Map<string, CollectorEntry[]> {
327
+ const grouped = new Map<string, CollectorEntry[]>();
328
+ for (const collector of collectors) {
329
+ const held = grouped.get(collector.typeName) ?? [];
330
+ held.push(collector);
331
+ grouped.set(collector.typeName, held);
332
+ }
333
+ return grouped;
334
+ }
335
+
336
+ function normalizeBinding(
337
+ contract: OperationContract,
338
+ handler: HandlerEntry,
339
+ frond: FrondDescriptor,
340
+ name: string,
341
+ diagnostics: ScanDiagnostic[],
342
+ ): (OperationContract & { binding: BindingPlan }) | undefined {
343
+ const params = contract.signature?.params ?? [];
344
+ if (!contract.binding) {
345
+ if (params.length === 0) return { ...contract, binding: [] };
346
+ diagnostics.push({
347
+ severity: 'blocking',
348
+ code: 'operation-unbound',
349
+ filePath: handler.filePath,
350
+ frond: frond.name,
351
+ subject: `${handler.ctor.name}.${name}`,
352
+ message: `${handler.ctor.name}.${name} declares ${params.length} parameter(s) and has no binding plan.`,
353
+ });
354
+ return undefined;
355
+ }
356
+ if (params.length === 0) return { ...contract, binding: contract.binding };
357
+
358
+ // Explicit plans are matched by parameter name and then placed in signature order.
359
+ // Array order is never allowed to become semantic authority.
360
+ const byName = new Map<string, Binding[]>();
361
+ for (const binding of contract.binding) {
362
+ const held = byName.get(binding.name) ?? [];
363
+ held.push(binding);
364
+ byName.set(binding.name, held);
365
+ }
366
+ const expected = new Set(params.map((param) => param.name));
367
+ const invalid = params.filter((param) => (byName.get(param.name)?.length ?? 0) !== 1);
368
+ const extras = [...byName.keys()].filter((binding) => !expected.has(binding));
369
+ if (invalid.length > 0 || extras.length > 0) {
370
+ diagnostics.push({
371
+ severity: 'blocking',
372
+ code: 'operation-binding-invalid',
373
+ filePath: handler.filePath,
374
+ frond: frond.name,
375
+ subject: `${handler.ctor.name}.${name}`,
376
+ message: `Binding plan for ${handler.ctor.name}.${name} must bind every parameter exactly once by name. `
377
+ + `${invalid.length ? `Invalid: ${invalid.map((param) => param.name).join(', ')}. ` : ''}`
378
+ + `${extras.length ? `Unknown: ${extras.join(', ')}.` : ''}`,
379
+ });
380
+ return undefined;
381
+ }
382
+ return {
383
+ ...contract,
384
+ binding: params.map((param) => byName.get(param.name)![0]!),
385
+ };
386
+ }
387
+
388
+ function validateProvenance(
389
+ fronds: readonly FrondDescriptor[],
390
+ frond: FrondDescriptor,
391
+ handler: HandlerEntry,
392
+ name: string,
393
+ contract: OperationContract & { binding: BindingPlan },
394
+ parameters: EffectiveParameter[],
395
+ localCollectors: Map<string, CollectorEntry[]>,
396
+ diagnostics: ScanDiagnostic[],
397
+ ): boolean {
398
+ let valid = true;
399
+ const declared = (handler.ctor as { __ops?: Record<string, OperationContract> }).__ops?.[name];
400
+ const explicitBinding = declared?.binding !== undefined
401
+ || handler.operations.get(name)?.binding !== undefined
402
+ || frond.operationsOverrides?.[name]?.binding !== undefined;
403
+ const ambiguousInput = diagnostics.some((diagnostic) =>
404
+ diagnostic.code === 'input-contract-ambiguous'
405
+ && diagnostic.subject === `${handler.ctor.name}.${name}`);
406
+ const body = parameters.filter((parameter) => parameter.binding.source.kind === 'body');
407
+ const inferredBody = new Set<EffectiveParameter>();
408
+
409
+ if (!explicitBinding && !ambiguousInput && contract.input) {
410
+ if (body.length === 1) {
411
+ inferredBody.add(body[0]!);
412
+ } else if (body.length > 1) {
413
+ const matches = body.filter((parameter) =>
414
+ schemaMatches(contract.input!, contract.signature?.params[parameter.position]?.type));
415
+ if (matches.length > 1) {
416
+ diagnostics.push({
417
+ severity: 'blocking',
418
+ code: 'parameter-binding-ambiguous',
419
+ filePath: handler.filePath,
420
+ frond: frond.name,
421
+ subject: `${handler.ctor.name}.${name}`,
422
+ message: `Cannot resolve which parameter of ${handler.ctor.name}.${name} receives the body: `
423
+ + `${matches.map((parameter) => `${parameter.name}: ${parameter.type ?? 'unknown'}`).join('; ')} `
424
+ + 'all match the resolved input contract. Declare an explicit binding plan.',
425
+ });
426
+ return false;
427
+ }
428
+ if (matches.length === 1) inferredBody.add(matches[0]!);
429
+ }
430
+ }
431
+
432
+ // A type written with an inline object literal names nothing a collector can register
433
+ // under, so the parameter IS the body. Only a NAMED type can be the wrong-frond
434
+ // substitution the refusal below exists for.
435
+ for (const parameter of body) {
436
+ if (structural(contract.signature?.params[parameter.position]?.type)) inferredBody.add(parameter);
437
+ }
438
+
439
+ for (const parameter of parameters) {
440
+ const source = parameter.binding.source;
441
+ if (source.kind === 'collector') {
442
+ const matches = localCollectors.get(source.typeName) ?? [];
443
+ if (matches.length !== 1) {
444
+ valid = false;
445
+ diagnostics.push({
446
+ severity: 'blocking',
447
+ code: matches.length === 0 ? 'collector-unavailable' : 'collector-ambiguous',
448
+ filePath: handler.filePath,
449
+ frond: frond.name,
450
+ subject: `${handler.ctor.name}.${name}(${parameter.name})`,
451
+ message: matches.length === 0
452
+ ? `No collector in frond '${frond.name}' resolves ${parameter.name}: ${parameter.type ?? source.typeName}.`
453
+ : `${matches.length} collectors in frond '${frond.name}' resolve '${source.typeName}'.`,
454
+ });
455
+ }
456
+ continue;
457
+ }
458
+ if (source.kind !== 'body' || explicitBinding || ambiguousInput) continue;
459
+
460
+ const typeName = lowerFirst(contract.signature?.params[parameter.position]?.type.name ?? '');
461
+ const elsewhere = fronds.flatMap((candidate) => candidate === frond
462
+ ? []
463
+ : candidate.collectors.filter((collector) => collector.typeName === typeName));
464
+ if (elsewhere.length > 0) {
465
+ // verify() supplies the more complete topology diagnostic; do not also call this
466
+ // an unknown body.
467
+ continue;
468
+ }
469
+
470
+ // An inferred body must be backed by exactly one resolved interpretation. With one
471
+ // body, the input itself is sufficient evidence; with several, exactly one type may
472
+ // match. Explicit plans are authoritative because they state every provenance.
473
+ const recognized = explicitBinding || inferredBody.has(parameter);
474
+ if (recognized) continue;
475
+ valid = false;
476
+ diagnostics.push({
477
+ severity: 'blocking',
478
+ code: 'parameter-binding-unknown',
479
+ filePath: handler.filePath,
480
+ frond: frond.name,
481
+ subject: `${handler.ctor.name}.${name}(${parameter.name})`,
482
+ message: `Cannot resolve a provenance for ${handler.ctor.name}.${name}(`
483
+ + `${parameter.name}: ${parameter.type ?? 'unknown'}). It is not a primitive, context, fact, `
484
+ + 'local collector or resolved input schema. Declare an explicit binding.',
485
+ });
486
+ }
487
+ return valid;
488
+ }
489
+
490
+ function structural(type: ParsedType | undefined): boolean {
491
+ return type !== undefined && type.raw.includes('{');
492
+ }
493
+
494
+ function schemaMatches(schema: SchemaView, type: ParsedType | undefined): boolean {
495
+ const names = new Set([schema.name, schema.derivation?.sourceName].filter(Boolean));
496
+ if (type?.name && names.has(type.name)) return true;
497
+ return type?.generics?.some((generic) => generic.name && names.has(generic.name)) ?? false;
498
+ }
499
+
500
+ function implementationOf(
501
+ frond: FrondDescriptor,
502
+ handler: HandlerEntry,
503
+ name: string,
504
+ diagnostics: ScanDiagnostic[],
505
+ ): EffectiveOperation['implementation'] | undefined {
506
+ const override = frond.operationsOverrides?.[name];
507
+ const candidates = override?.handlerName
508
+ ? frond.handlers.filter((candidate) => candidate.ctor.name === override.handlerName)
509
+ : [handler];
510
+ if (candidates.length !== 1) {
511
+ diagnostics.push({
512
+ severity: 'blocking',
513
+ code: candidates.length === 0 ? 'operation-handler-unresolved' : 'operation-handler-ambiguous',
514
+ filePath: handler.filePath,
515
+ frond: frond.name,
516
+ subject: `${handler.ctor.name}.${name}`,
517
+ message: `Operation ${handler.ctor.name}.${name} names handler '${override?.handlerName}', `
518
+ + `but ${candidates.length} matching handlers were found.`,
519
+ });
520
+ return undefined;
521
+ }
522
+ const implementation = candidates[0]!;
523
+ const method = override?.method ?? name;
524
+ const declared = implementation.operations.has(method)
525
+ || typeof (implementation.ctor as { prototype?: Record<string, unknown> }).prototype?.[method] === 'function'
526
+ || method in ((implementation.ctor as { __ops?: Record<string, unknown> }).__ops ?? {});
527
+ if (!declared) {
528
+ diagnostics.push({
529
+ severity: 'blocking',
530
+ code: 'operation-method-unresolved',
531
+ filePath: handler.filePath,
532
+ frond: frond.name,
533
+ subject: `${handler.ctor.name}.${name}`,
534
+ message: `Operation ${handler.ctor.name}.${name} resolves to `
535
+ + `${implementation.ctor.name}.${method}, but that method does not exist.`,
536
+ });
537
+ return undefined;
538
+ }
539
+ return {
540
+ className: implementation.ctor.name,
541
+ address: implementation.address,
542
+ method,
543
+ filePath: implementation.filePath,
544
+ };
545
+ }
546
+
547
+ function effectiveOutput(
548
+ frond: FrondDescriptor,
549
+ handler: HandlerEntry,
550
+ name: string,
551
+ contract: OperationContract,
552
+ ): { schema?: SchemaView; closed: boolean } {
553
+ const perOperation = (handler.ctor as { __opOutputs?: Record<string, SchemaView> }).__opOutputs?.[name];
554
+ if (perOperation) return { schema: perOperation, closed: true };
555
+ if (contract.output) return { schema: contract.output, closed: false };
556
+ const handlerWide = handler.outputOverride
557
+ ?? (handler.ctor as { __output?: SchemaView }).__output;
558
+ if (handlerWide) return { schema: handlerWide, closed: false };
559
+
560
+ const target = targetOf(handler.ctor) as { name?: string } | undefined;
561
+ const address = target?.name ? lowerFirst(target.name) : handler.address;
562
+ const entity = frond.entities.find((candidate) => candidate.name === address);
563
+ return { ...(entity ? { schema: entity.entityClass } : {}), closed: false };
564
+ }
565
+
566
+ function surfacesOf(frond: FrondDescriptor, handler: HandlerEntry): string[] {
567
+ if (handler.surface) return [handler.surface];
568
+ const surfaces = ['default'];
569
+ for (const [surface, addresses] of Object.entries(frond.surfaces ?? {})) {
570
+ const dedicated = frond.handlers.some((candidate) =>
571
+ candidate.address === handler.address && candidate.surface === surface);
572
+ if (!dedicated && addresses.some((address) =>
573
+ address.toLowerCase() === handler.address.toLowerCase())) surfaces.push(surface);
574
+ }
575
+ return [surfaces[0]!, ...surfaces.slice(1).sort()];
576
+ }
577
+
578
+ function exposedAdapters(
579
+ handler: HandlerEntry,
580
+ adapters: Record<string, boolean | undefined> | undefined,
581
+ ): string[] {
582
+ if (handler.exposed === false && !handler.surface) return [];
583
+ return Object.entries(adapters ?? {})
584
+ .filter(([, enabled]) => enabled === true)
585
+ .map(([adapter]) => adapter)
586
+ .sort();
587
+ }
588
+
589
+ function uniqueDiagnostics(diagnostics: readonly ScanDiagnostic[]): ScanDiagnostic[] {
590
+ const seen = new Set<string>();
591
+ return diagnostics.filter((diagnostic) => {
592
+ const key = [diagnostic.code, diagnostic.filePath, diagnostic.subject, diagnostic.message].join('\0');
593
+ if (seen.has(key)) return false;
594
+ seen.add(key);
595
+ return true;
596
+ });
597
+ }
package/src/emit.ts ADDED
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Announcing a fact — the half of the topology `remotes:` never covered.
3
+ *
4
+ * Every other call in Fougere names **one** recipient: `remotes` names one address per
5
+ * frond, `Facade<T>` resolves one door. `Emit<T>` names a **subject** instead, and the
6
+ * number of readers is not the emitter's business.
7
+ *
8
+ * ```ts
9
+ * constructor(private published: Emit<PostPublished>) {}
10
+ * await this.published({ id, title }); // I say it happened. I do not know who cares.
11
+ *
12
+ * async reindex(fact: Fact<PostPublished>) {} // any handler, any frond. Nothing to register.
13
+ * ```
14
+ *
15
+ * **It is a resolver, not a channel.** A bus moves messages — it has a queue, an envelope
16
+ * format, a delivery semantic. This has none of that: it answers *who*, then hands over to
17
+ * the call path that already exists. It is `resolve` returning N things instead of one.
18
+ * That is also why temporal decoupling stays out of reach: a resolver holds nothing. The
19
+ * day at-least-once is wanted, a real channel goes UNDER this, in the transport — the
20
+ * dispatch line becomes a publish and a consumer calls the same door on the far side.
21
+ */
22
+ import { lowerFirst } from '@fougere/schema';
23
+
24
+ /**
25
+ * What an emitter injects. Resolved by type, like `EntityOrm<Post>` and `Facade<T>`.
26
+ *
27
+ * The returned promise settles when the fact has been **dispatched**, never when it has
28
+ * been handled: a subscriber's failure is its own. The `EventBus` this replaces did
29
+ * `await Promise.all(handlers)` and handed their rejections back, which made a publication
30
+ * hostage to its own indexer.
31
+ */
32
+ export type Emit<T> = (fact: T) => Promise<void>;
33
+
34
+ /**
35
+ * What a subscriber accepts — and what it PROMISES about itself.
36
+ *
37
+ * Not merely "I subscribe". Push is the strict mode and pull is a special case of it: an
38
+ * op written for push is correct when called directly, the reverse is false. So this
39
+ * wrapper commits the operation to three things its author must honour, because no type
40
+ * can check them:
41
+ *
42
+ * 1. **replayable** — delivery is at-least-once the day a broker sits underneath;
43
+ * 2. **no reader for its return** — nobody receives what it hands back;
44
+ * 3. **owner of its failures** — they reach a log, never the emitter.
45
+ *
46
+ * It is the identity at runtime. The scan reads the written name, which is the whole
47
+ * mechanism: `binding.ts` sees `Fact<X>` and binds the parameter as a fact instead of
48
+ * letting it fall through to "everything else — body".
49
+ */
50
+ export type Fact<T> = T;
51
+
52
+ /**
53
+ * The container key of an emission — THE one place that spells the format.
54
+ *
55
+ * Its dual is right below, because the boot reads the fact back out of a handler's `deps`
56
+ * to know which emissions to register. A key and the way to undo it belong together: the
57
+ * pair that is split is the pair that drifts.
58
+ */
59
+ export function emitKeyOf(fact: string): string {
60
+ return `${lowerFirst(fact)}Emit`;
61
+ }
62
+
63
+ const SUFFIX = 'Emit';
64
+
65
+ /** The fact behind an emission key, or `undefined` when the key is not one. */
66
+ export function factOfEmitKey(key: string): string | undefined {
67
+ return key.length > SUFFIX.length && key.endsWith(SUFFIX)
68
+ ? key.slice(0, -SUFFIX.length)
69
+ : undefined;
70
+ }
71
+
72
+ /**
73
+ * What a set of handlers ANNOUNCES — `Emit<T>` read back out of their dependencies.
74
+ *
75
+ * Two readers, which is exactly why it is a function and not a line: the boot registers one
76
+ * emission value per name, and the identity card publishes the same set. Spelled twice they
77
+ * would drift the day an emission stops being a constructor dependency.
78
+ *
79
+ * The dual — what a handler ACCEPTS — is not here, because it is not read the same way: a
80
+ * `Fact<T>` parameter is a fact about one operation, and `computeBindingPlan` has already
81
+ * written it into the plan (`app.listensTo()`).
82
+ */
83
+ export function factsAnnouncedBy(handlers: readonly { deps: readonly string[] }[]): string[] {
84
+ return [...new Set(
85
+ handlers
86
+ .flatMap((handler) => handler.deps)
87
+ .map(factOfEmitKey)
88
+ .filter((fact): fact is string => fact !== undefined),
89
+ )];
90
+ }