@fougere/core 0.5.0-alpha.1 → 0.7.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 (519) hide show
  1. package/README.md +1 -1
  2. package/dist/boot/Emissions.d.ts +14 -91
  3. package/dist/boot/Emissions.d.ts.map +1 -1
  4. package/dist/boot/Emissions.js +21 -118
  5. package/dist/boot/Emissions.js.map +1 -1
  6. package/dist/boot/ambient-port.d.ts +3 -24
  7. package/dist/boot/ambient-port.d.ts.map +1 -1
  8. package/dist/boot/ambient.als.d.ts.map +1 -1
  9. package/dist/boot/ambient.als.js +7 -12
  10. package/dist/boot/ambient.als.js.map +1 -1
  11. package/dist/boot/ambient.queue.d.ts +2 -5
  12. package/dist/boot/ambient.queue.d.ts.map +1 -1
  13. package/dist/boot/ambient.queue.js.map +1 -1
  14. package/dist/boot/apply.d.ts +1 -13
  15. package/dist/boot/apply.d.ts.map +1 -1
  16. package/dist/boot/apply.js +3 -15
  17. package/dist/boot/apply.js.map +1 -1
  18. package/dist/boot/auth.d.ts +6 -16
  19. package/dist/boot/auth.d.ts.map +1 -1
  20. package/dist/boot/boot.d.ts +5 -18
  21. package/dist/boot/boot.d.ts.map +1 -1
  22. package/dist/boot/boot.js +4 -11
  23. package/dist/boot/boot.js.map +1 -1
  24. package/dist/boot/bootstrap.d.ts.map +1 -1
  25. package/dist/boot/bootstrap.js +106 -163
  26. package/dist/boot/bootstrap.js.map +1 -1
  27. package/dist/boot/card.d.ts +5 -0
  28. package/dist/boot/card.d.ts.map +1 -0
  29. package/dist/boot/card.js +71 -0
  30. package/dist/boot/card.js.map +1 -0
  31. package/dist/boot/frame.d.ts +8 -32
  32. package/dist/boot/frame.d.ts.map +1 -1
  33. package/dist/boot/frame.js +23 -57
  34. package/dist/boot/frame.js.map +1 -1
  35. package/dist/boot/hosted.d.ts +3 -19
  36. package/dist/boot/hosted.d.ts.map +1 -1
  37. package/dist/boot/hosted.js +12 -17
  38. package/dist/boot/hosted.js.map +1 -1
  39. package/dist/boot/ownership.d.ts +5 -36
  40. package/dist/boot/ownership.d.ts.map +1 -1
  41. package/dist/boot/ownership.js +9 -49
  42. package/dist/boot/ownership.js.map +1 -1
  43. package/dist/boot/ports.d.ts +2 -15
  44. package/dist/boot/ports.d.ts.map +1 -1
  45. package/dist/boot/ports.js +1 -14
  46. package/dist/boot/ports.js.map +1 -1
  47. package/dist/boot/remote.d.ts +5 -38
  48. package/dist/boot/remote.d.ts.map +1 -1
  49. package/dist/boot/remote.js +8 -41
  50. package/dist/boot/remote.js.map +1 -1
  51. package/dist/boot/runner.d.ts +11 -0
  52. package/dist/boot/runner.d.ts.map +1 -0
  53. package/dist/boot/runner.js +13 -0
  54. package/dist/boot/runner.js.map +1 -0
  55. package/dist/boot/seed.d.ts +5 -32
  56. package/dist/boot/seed.d.ts.map +1 -1
  57. package/dist/boot/seed.js +10 -41
  58. package/dist/boot/seed.js.map +1 -1
  59. package/dist/boot/statement-drift.d.ts +4 -22
  60. package/dist/boot/statement-drift.d.ts.map +1 -1
  61. package/dist/boot/statement-drift.js +4 -9
  62. package/dist/boot/statement-drift.js.map +1 -1
  63. package/dist/boot/together.d.ts +8 -12
  64. package/dist/boot/together.d.ts.map +1 -1
  65. package/dist/boot/together.js +28 -65
  66. package/dist/boot/together.js.map +1 -1
  67. package/dist/boot/types.d.ts +36 -180
  68. package/dist/boot/types.d.ts.map +1 -1
  69. package/dist/builtin/config.d.ts +7 -0
  70. package/dist/builtin/config.d.ts.map +1 -0
  71. package/dist/builtin/config.js +15 -0
  72. package/dist/builtin/config.js.map +1 -0
  73. package/dist/{builtins → builtin}/logger.d.ts +4 -25
  74. package/dist/builtin/logger.d.ts.map +1 -0
  75. package/dist/{builtins → builtin}/logger.js +5 -32
  76. package/dist/builtin/logger.js.map +1 -0
  77. package/dist/config-loader.d.ts +9 -67
  78. package/dist/config-loader.d.ts.map +1 -1
  79. package/dist/config-loader.js +5 -18
  80. package/dist/config-loader.js.map +1 -1
  81. package/dist/contract.d.ts +13 -31
  82. package/dist/contract.d.ts.map +1 -1
  83. package/dist/contract.js +10 -26
  84. package/dist/contract.js.map +1 -1
  85. package/dist/crypto/encoding.d.ts +2 -15
  86. package/dist/crypto/encoding.d.ts.map +1 -1
  87. package/dist/crypto/encoding.js +2 -15
  88. package/dist/crypto/encoding.js.map +1 -1
  89. package/dist/crypto/node.d.ts.map +1 -1
  90. package/dist/crypto/node.js +1 -7
  91. package/dist/crypto/node.js.map +1 -1
  92. package/dist/crypto/port.d.ts +2 -18
  93. package/dist/crypto/port.d.ts.map +1 -1
  94. package/dist/crypto/port.js +1 -17
  95. package/dist/crypto/port.js.map +1 -1
  96. package/dist/crypto/webcrypto.d.ts.map +1 -1
  97. package/dist/crypto/webcrypto.js +1 -12
  98. package/dist/crypto/webcrypto.js.map +1 -1
  99. package/dist/declare.d.ts +41 -0
  100. package/dist/declare.d.ts.map +1 -0
  101. package/dist/{scan/declare.js → declare.js} +6 -31
  102. package/dist/declare.js.map +1 -0
  103. package/dist/define.d.ts +2 -3
  104. package/dist/define.d.ts.map +1 -1
  105. package/dist/define.js +2 -3
  106. package/dist/define.js.map +1 -1
  107. package/dist/descriptor/Fronds.d.ts +19 -0
  108. package/dist/descriptor/Fronds.d.ts.map +1 -0
  109. package/dist/descriptor/Fronds.js +40 -0
  110. package/dist/descriptor/Fronds.js.map +1 -0
  111. package/dist/descriptor/frond.d.ts +135 -0
  112. package/dist/descriptor/frond.d.ts.map +1 -0
  113. package/dist/{scan → descriptor}/frond.js.map +1 -1
  114. package/dist/dispatch/ArgumentResolver.d.ts +6 -3
  115. package/dist/dispatch/ArgumentResolver.d.ts.map +1 -1
  116. package/dist/dispatch/ArgumentResolver.js +48 -3
  117. package/dist/dispatch/ArgumentResolver.js.map +1 -1
  118. package/dist/dispatch/DispatchEvent.d.ts +2 -2
  119. package/dist/dispatch/DispatchEvent.d.ts.map +1 -1
  120. package/dist/dispatch/DispatchLifecycle.d.ts +1 -7
  121. package/dist/dispatch/DispatchLifecycle.d.ts.map +1 -1
  122. package/dist/dispatch/DispatchLifecycle.js +1 -7
  123. package/dist/dispatch/DispatchLifecycle.js.map +1 -1
  124. package/dist/dispatch/DispatchPort.d.ts +1 -1
  125. package/dist/dispatch/DispatchPort.d.ts.map +1 -1
  126. package/dist/dispatch/Dispatcher.d.ts +1 -1
  127. package/dist/dispatch/Dispatcher.d.ts.map +1 -1
  128. package/dist/dispatch/Dispatcher.js +2 -2
  129. package/dist/dispatch/Dispatcher.js.map +1 -1
  130. package/dist/{boot → dispatch}/HandlerFacade.d.ts +25 -38
  131. package/dist/dispatch/HandlerFacade.d.ts.map +1 -0
  132. package/dist/{boot → dispatch}/HandlerFacade.js +65 -74
  133. package/dist/dispatch/HandlerFacade.js.map +1 -0
  134. package/dist/dispatch/LocalRoutePolicy.d.ts +1 -1
  135. package/dist/dispatch/LocalRoutePolicy.d.ts.map +1 -1
  136. package/dist/dispatch/LocalRoutePolicy.js +4 -4
  137. package/dist/dispatch/LocalRoutePolicy.js.map +1 -1
  138. package/dist/dispatch/OperationRoute.d.ts +2 -2
  139. package/dist/dispatch/OperationRoute.d.ts.map +1 -1
  140. package/dist/dispatch/OutputView.d.ts +3 -2
  141. package/dist/dispatch/OutputView.d.ts.map +1 -1
  142. package/dist/dispatch/OutputView.js +19 -1
  143. package/dist/dispatch/OutputView.js.map +1 -1
  144. package/dist/dispatch/Route.d.ts +2 -3
  145. package/dist/dispatch/Route.d.ts.map +1 -1
  146. package/dist/dispatch/RoutePolicy.d.ts +1 -1
  147. package/dist/dispatch/RoutePolicy.d.ts.map +1 -1
  148. package/dist/dispatch/RouteRegistry.d.ts +1 -1
  149. package/dist/dispatch/RouteRegistry.d.ts.map +1 -1
  150. package/dist/dispatch/RouteRegistry.js +2 -2
  151. package/dist/dispatch/RouteRegistry.js.map +1 -1
  152. package/dist/dispatch/RouteResolver.d.ts +2 -4
  153. package/dist/dispatch/RouteResolver.d.ts.map +1 -1
  154. package/dist/dispatch/StorageGuard.d.ts +9 -8
  155. package/dist/dispatch/StorageGuard.d.ts.map +1 -1
  156. package/dist/dispatch/StorageGuard.js +66 -23
  157. package/dist/dispatch/StorageGuard.js.map +1 -1
  158. package/dist/dispatch/presenterArguments.d.ts +7 -0
  159. package/dist/dispatch/presenterArguments.d.ts.map +1 -0
  160. package/dist/dispatch/presenterArguments.js +12 -0
  161. package/dist/dispatch/presenterArguments.js.map +1 -0
  162. package/dist/dispatch/remoteRoutes.d.ts +7 -0
  163. package/dist/dispatch/remoteRoutes.d.ts.map +1 -0
  164. package/dist/dispatch/{RemoteRouteResolver.js → remoteRoutes.js} +6 -10
  165. package/dist/dispatch/remoteRoutes.js.map +1 -0
  166. package/dist/dispatch/routeNotFound.d.ts +9 -0
  167. package/dist/dispatch/routeNotFound.d.ts.map +1 -0
  168. package/dist/dispatch/routeNotFound.js +21 -0
  169. package/dist/dispatch/routeNotFound.js.map +1 -0
  170. package/dist/dispatch/validateInput.d.ts +5 -0
  171. package/dist/dispatch/validateInput.d.ts.map +1 -0
  172. package/dist/dispatch/validateInput.js +20 -0
  173. package/dist/dispatch/validateInput.js.map +1 -0
  174. package/dist/effective-operation.d.ts +6 -3
  175. package/dist/effective-operation.d.ts.map +1 -1
  176. package/dist/effective-operation.js +49 -27
  177. package/dist/effective-operation.js.map +1 -1
  178. package/dist/emit.d.ts +4 -42
  179. package/dist/emit.d.ts.map +1 -1
  180. package/dist/emit.js +3 -39
  181. package/dist/emit.js.map +1 -1
  182. package/dist/entry/facade.d.ts +8 -0
  183. package/dist/entry/facade.d.ts.map +1 -0
  184. package/dist/entry/facade.js +28 -0
  185. package/dist/entry/facade.js.map +1 -0
  186. package/dist/entry/transport.d.ts +5 -0
  187. package/dist/entry/transport.d.ts.map +1 -0
  188. package/dist/entry/transport.js +11 -0
  189. package/dist/entry/transport.js.map +1 -0
  190. package/dist/frond-config.d.ts +8 -71
  191. package/dist/frond-config.d.ts.map +1 -1
  192. package/dist/frond-config.js +1 -9
  193. package/dist/frond-config.js.map +1 -1
  194. package/dist/graph.d.ts +1 -1
  195. package/dist/graph.d.ts.map +1 -1
  196. package/dist/identity-keys.d.ts +2 -5
  197. package/dist/identity-keys.d.ts.map +1 -1
  198. package/dist/identity-keys.js +3 -16
  199. package/dist/identity-keys.js.map +1 -1
  200. package/dist/identity.d.ts +4 -30
  201. package/dist/identity.d.ts.map +1 -1
  202. package/dist/identity.js +11 -73
  203. package/dist/identity.js.map +1 -1
  204. package/dist/imports.d.ts +2 -8
  205. package/dist/imports.d.ts.map +1 -1
  206. package/dist/imports.js +1 -7
  207. package/dist/imports.js.map +1 -1
  208. package/dist/index.d.ts +28 -30
  209. package/dist/index.d.ts.map +1 -1
  210. package/dist/index.js +17 -22
  211. package/dist/index.js.map +1 -1
  212. package/dist/loader.d.ts +1 -8
  213. package/dist/loader.d.ts.map +1 -1
  214. package/dist/loader.js +1 -8
  215. package/dist/loader.js.map +1 -1
  216. package/dist/node.d.ts +2 -11
  217. package/dist/node.d.ts.map +1 -1
  218. package/dist/node.js +2 -11
  219. package/dist/node.js.map +1 -1
  220. package/dist/prefab/collector.d.ts +1 -22
  221. package/dist/prefab/collector.d.ts.map +1 -1
  222. package/dist/prefab/collector.js +1 -22
  223. package/dist/prefab/collector.js.map +1 -1
  224. package/dist/prefab/crud.d.ts +19 -35
  225. package/dist/prefab/crud.d.ts.map +1 -1
  226. package/dist/prefab/crud.js +39 -66
  227. package/dist/prefab/crud.js.map +1 -1
  228. package/dist/prefab/mirror.d.ts +5 -27
  229. package/dist/prefab/mirror.d.ts.map +1 -1
  230. package/dist/prefab/mirror.js +6 -24
  231. package/dist/prefab/mirror.js.map +1 -1
  232. package/dist/prefab/prefab.d.ts +1 -7
  233. package/dist/prefab/prefab.d.ts.map +1 -1
  234. package/dist/prefab/prefab.js +1 -7
  235. package/dist/prefab/prefab.js.map +1 -1
  236. package/dist/prefab/presenter.d.ts +2 -36
  237. package/dist/prefab/presenter.d.ts.map +1 -1
  238. package/dist/prefab/presenter.js +2 -36
  239. package/dist/prefab/presenter.js.map +1 -1
  240. package/dist/prefab/repository.d.ts +12 -80
  241. package/dist/prefab/repository.d.ts.map +1 -1
  242. package/dist/prefab/repository.js +22 -35
  243. package/dist/prefab/repository.js.map +1 -1
  244. package/dist/scan/adapters.d.ts +3 -0
  245. package/dist/scan/adapters.d.ts.map +1 -0
  246. package/dist/scan/adapters.js +25 -0
  247. package/dist/scan/adapters.js.map +1 -0
  248. package/dist/scan/bundling.d.ts +1 -12
  249. package/dist/scan/bundling.d.ts.map +1 -1
  250. package/dist/scan/bundling.js +1 -12
  251. package/dist/scan/bundling.js.map +1 -1
  252. package/dist/scan/conventions.d.ts +6 -40
  253. package/dist/scan/conventions.d.ts.map +1 -1
  254. package/dist/scan/conventions.js +5 -34
  255. package/dist/scan/conventions.js.map +1 -1
  256. package/dist/scan/emit.d.ts +2 -6
  257. package/dist/scan/emit.d.ts.map +1 -1
  258. package/dist/scan/emit.js +5 -39
  259. package/dist/scan/emit.js.map +1 -1
  260. package/dist/scan/handler-parser.d.ts +9 -76
  261. package/dist/scan/handler-parser.d.ts.map +1 -1
  262. package/dist/scan/handler-parser.js +28 -85
  263. package/dist/scan/handler-parser.js.map +1 -1
  264. package/dist/scan/result.d.ts +28 -0
  265. package/dist/scan/result.d.ts.map +1 -0
  266. package/dist/scan/result.js +2 -0
  267. package/dist/scan/result.js.map +1 -0
  268. package/dist/scan/scanner.d.ts +3 -28
  269. package/dist/scan/scanner.d.ts.map +1 -1
  270. package/dist/scan/scanner.js +36 -135
  271. package/dist/scan/scanner.js.map +1 -1
  272. package/dist/scan/statement.d.ts +1 -1
  273. package/dist/scan/statement.d.ts.map +1 -1
  274. package/dist/scan/statement.js +4 -30
  275. package/dist/scan/statement.js.map +1 -1
  276. package/dist/source.d.ts +41 -0
  277. package/dist/source.d.ts.map +1 -0
  278. package/dist/source.js +10 -0
  279. package/dist/source.js.map +1 -0
  280. package/dist/storage.d.ts +92 -0
  281. package/dist/storage.d.ts.map +1 -0
  282. package/dist/storage.js +50 -0
  283. package/dist/storage.js.map +1 -0
  284. package/dist/store.d.ts +18 -0
  285. package/dist/store.d.ts.map +1 -0
  286. package/dist/store.js +146 -0
  287. package/dist/store.js.map +1 -0
  288. package/dist/verify.d.ts +6 -39
  289. package/dist/verify.d.ts.map +1 -1
  290. package/dist/verify.js +19 -39
  291. package/dist/verify.js.map +1 -1
  292. package/dist/wire/CallLog.d.ts +32 -0
  293. package/dist/wire/CallLog.d.ts.map +1 -0
  294. package/dist/{contract → wire}/CallLog.js.map +1 -1
  295. package/dist/wire/Invocation.d.ts +32 -0
  296. package/dist/wire/Invocation.d.ts.map +1 -0
  297. package/dist/wire/Invocation.js +66 -0
  298. package/dist/wire/Invocation.js.map +1 -0
  299. package/dist/wire/RouteAddress.d.ts +24 -0
  300. package/dist/wire/RouteAddress.d.ts.map +1 -0
  301. package/dist/{contract → wire}/RouteAddress.js +7 -0
  302. package/dist/wire/RouteAddress.js.map +1 -0
  303. package/dist/wire/binding.d.ts +36 -0
  304. package/dist/wire/binding.d.ts.map +1 -0
  305. package/dist/{boot → wire}/binding.js +5 -62
  306. package/dist/wire/binding.js.map +1 -0
  307. package/dist/wire/call.d.ts +25 -172
  308. package/dist/wire/call.d.ts.map +1 -1
  309. package/dist/wire/call.js +15 -166
  310. package/dist/wire/call.js.map +1 -1
  311. package/dist/wire/drift.d.ts +3 -28
  312. package/dist/wire/drift.d.ts.map +1 -1
  313. package/dist/wire/drift.js +4 -23
  314. package/dist/wire/drift.js.map +1 -1
  315. package/dist/wire/errors.d.ts +4 -24
  316. package/dist/wire/errors.d.ts.map +1 -1
  317. package/dist/wire/errors.js +4 -24
  318. package/dist/wire/errors.js.map +1 -1
  319. package/dist/wire/http-error.d.ts +2 -13
  320. package/dist/wire/http-error.d.ts.map +1 -1
  321. package/dist/wire/http-error.js +5 -19
  322. package/dist/wire/http-error.js.map +1 -1
  323. package/dist/wire/loggerMiddleware.d.ts +2 -8
  324. package/dist/wire/loggerMiddleware.d.ts.map +1 -1
  325. package/dist/wire/loggerMiddleware.js +1 -7
  326. package/dist/wire/loggerMiddleware.js.map +1 -1
  327. package/dist/wire/middleware.d.ts +3 -7
  328. package/dist/wire/middleware.d.ts.map +1 -1
  329. package/dist/wire/middleware.js +1 -5
  330. package/dist/wire/middleware.js.map +1 -1
  331. package/dist/wire/operation.d.ts +12 -74
  332. package/dist/wire/operation.d.ts.map +1 -1
  333. package/dist/wire/operation.js +4 -66
  334. package/dist/wire/operation.js.map +1 -1
  335. package/dist/wire/signature.d.ts +37 -0
  336. package/dist/wire/signature.d.ts.map +1 -0
  337. package/dist/wire/signature.js +2 -0
  338. package/dist/wire/signature.js.map +1 -0
  339. package/package.json +3 -3
  340. package/src/boot/Emissions.ts +22 -119
  341. package/src/boot/ambient-port.ts +3 -24
  342. package/src/boot/ambient.als.ts +7 -12
  343. package/src/boot/ambient.queue.ts +2 -5
  344. package/src/boot/apply.ts +3 -15
  345. package/src/boot/auth.ts +6 -16
  346. package/src/boot/boot.ts +8 -23
  347. package/src/boot/bootstrap.ts +117 -173
  348. package/src/boot/card.ts +84 -0
  349. package/src/boot/frame.ts +31 -65
  350. package/src/boot/hosted.ts +15 -40
  351. package/src/boot/ownership.ts +14 -49
  352. package/src/boot/ports.ts +2 -15
  353. package/src/boot/remote.ts +11 -58
  354. package/src/boot/runner.ts +17 -0
  355. package/src/boot/seed.ts +11 -42
  356. package/src/boot/statement-drift.ts +10 -28
  357. package/src/boot/together.ts +38 -72
  358. package/src/boot/types.ts +36 -180
  359. package/src/{builtins → builtin}/config.ts +1 -7
  360. package/src/{builtins → builtin}/logger.ts +6 -39
  361. package/src/config-loader.ts +12 -71
  362. package/src/contract.ts +15 -39
  363. package/src/crypto/encoding.ts +2 -15
  364. package/src/crypto/node.ts +1 -7
  365. package/src/crypto/port.ts +2 -18
  366. package/src/crypto/webcrypto.ts +1 -12
  367. package/src/{scan/declare.ts → declare.ts} +11 -42
  368. package/src/define.ts +2 -3
  369. package/src/descriptor/Fronds.ts +47 -0
  370. package/src/descriptor/frond.ts +145 -0
  371. package/src/dispatch/ArgumentResolver.ts +54 -8
  372. package/src/dispatch/DispatchEvent.ts +2 -2
  373. package/src/dispatch/DispatchLifecycle.ts +1 -7
  374. package/src/dispatch/DispatchPort.ts +1 -1
  375. package/src/dispatch/Dispatcher.ts +3 -3
  376. package/src/dispatch/HandlerFacade.ts +231 -0
  377. package/src/dispatch/LocalRoutePolicy.ts +4 -4
  378. package/src/dispatch/OperationRoute.ts +2 -2
  379. package/src/dispatch/OutputView.ts +24 -2
  380. package/src/dispatch/Route.ts +2 -4
  381. package/src/dispatch/RoutePolicy.ts +1 -1
  382. package/src/dispatch/RouteRegistry.ts +2 -2
  383. package/src/dispatch/RouteResolver.ts +2 -4
  384. package/src/dispatch/StorageGuard.ts +75 -23
  385. package/src/dispatch/presenterArguments.ts +25 -0
  386. package/src/dispatch/{RemoteRouteResolver.ts → remoteRoutes.ts} +7 -9
  387. package/src/dispatch/routeNotFound.ts +33 -0
  388. package/src/dispatch/validateInput.ts +28 -0
  389. package/src/effective-operation.ts +60 -37
  390. package/src/emit.ts +5 -63
  391. package/src/entry/facade.ts +44 -0
  392. package/src/entry/transport.ts +16 -0
  393. package/src/frond-config.ts +8 -71
  394. package/src/graph.ts +1 -1
  395. package/src/identity-keys.ts +3 -16
  396. package/src/identity.ts +12 -80
  397. package/src/imports.ts +3 -32
  398. package/src/index.ts +28 -33
  399. package/src/loader.ts +2 -16
  400. package/src/node.ts +2 -11
  401. package/src/prefab/collector.ts +1 -22
  402. package/src/prefab/crud.ts +51 -90
  403. package/src/prefab/mirror.ts +9 -49
  404. package/src/prefab/prefab.ts +2 -21
  405. package/src/prefab/presenter.ts +2 -36
  406. package/src/prefab/repository.ts +32 -108
  407. package/src/scan/adapters.ts +32 -0
  408. package/src/scan/bundling.ts +1 -12
  409. package/src/scan/conventions.ts +6 -40
  410. package/src/scan/emit.ts +7 -43
  411. package/src/scan/handler-parser.ts +41 -163
  412. package/src/scan/result.ts +29 -0
  413. package/src/scan/scanner.ts +40 -138
  414. package/src/scan/statement.ts +6 -31
  415. package/src/source.ts +44 -0
  416. package/src/storage.ts +141 -0
  417. package/src/store.ts +152 -0
  418. package/src/verify.ts +19 -60
  419. package/src/wire/CallLog.ts +30 -0
  420. package/src/{contract → wire}/Invocation.ts +24 -17
  421. package/src/{contract → wire}/RouteAddress.ts +10 -0
  422. package/src/wire/binding.ts +102 -0
  423. package/src/wire/call.ts +30 -261
  424. package/src/wire/drift.ts +5 -30
  425. package/src/wire/errors.ts +4 -24
  426. package/src/wire/http-error.ts +5 -19
  427. package/src/wire/loggerMiddleware.ts +2 -8
  428. package/src/wire/middleware.ts +3 -7
  429. package/src/wire/operation.ts +13 -110
  430. package/src/wire/signature.ts +38 -0
  431. package/dist/boot/HandlerFacade.d.ts.map +0 -1
  432. package/dist/boot/HandlerFacade.js.map +0 -1
  433. package/dist/boot/binding.d.ts +0 -60
  434. package/dist/boot/binding.d.ts.map +0 -1
  435. package/dist/boot/binding.js.map +0 -1
  436. package/dist/builtins/config.d.ts +0 -13
  437. package/dist/builtins/config.d.ts.map +0 -1
  438. package/dist/builtins/config.js +0 -21
  439. package/dist/builtins/config.js.map +0 -1
  440. package/dist/builtins/logger.d.ts.map +0 -1
  441. package/dist/builtins/logger.js.map +0 -1
  442. package/dist/contract/Call.d.ts +0 -10
  443. package/dist/contract/Call.d.ts.map +0 -1
  444. package/dist/contract/Call.js +0 -15
  445. package/dist/contract/Call.js.map +0 -1
  446. package/dist/contract/CallLog.d.ts +0 -57
  447. package/dist/contract/CallLog.d.ts.map +0 -1
  448. package/dist/contract/Invocation.d.ts +0 -27
  449. package/dist/contract/Invocation.d.ts.map +0 -1
  450. package/dist/contract/Invocation.js +0 -59
  451. package/dist/contract/Invocation.js.map +0 -1
  452. package/dist/contract/RouteAddress.d.ts +0 -17
  453. package/dist/contract/RouteAddress.d.ts.map +0 -1
  454. package/dist/contract/RouteAddress.js.map +0 -1
  455. package/dist/dispatch/InputValidator.d.ts +0 -7
  456. package/dist/dispatch/InputValidator.d.ts.map +0 -1
  457. package/dist/dispatch/InputValidator.js +0 -22
  458. package/dist/dispatch/InputValidator.js.map +0 -1
  459. package/dist/dispatch/OperationExecutor.d.ts +0 -25
  460. package/dist/dispatch/OperationExecutor.d.ts.map +0 -1
  461. package/dist/dispatch/OperationExecutor.js +0 -32
  462. package/dist/dispatch/OperationExecutor.js.map +0 -1
  463. package/dist/dispatch/OutputProjector.d.ts +0 -12
  464. package/dist/dispatch/OutputProjector.d.ts.map +0 -1
  465. package/dist/dispatch/OutputProjector.js +0 -30
  466. package/dist/dispatch/OutputProjector.js.map +0 -1
  467. package/dist/dispatch/PresenterArgumentResolver.d.ts +0 -12
  468. package/dist/dispatch/PresenterArgumentResolver.d.ts.map +0 -1
  469. package/dist/dispatch/PresenterArgumentResolver.js +0 -20
  470. package/dist/dispatch/PresenterArgumentResolver.js.map +0 -1
  471. package/dist/dispatch/RemoteRouteResolver.d.ts +0 -13
  472. package/dist/dispatch/RemoteRouteResolver.d.ts.map +0 -1
  473. package/dist/dispatch/RemoteRouteResolver.js.map +0 -1
  474. package/dist/dispatch/RouteNotFoundError.d.ts +0 -10
  475. package/dist/dispatch/RouteNotFoundError.d.ts.map +0 -1
  476. package/dist/dispatch/RouteNotFoundError.js +0 -24
  477. package/dist/dispatch/RouteNotFoundError.js.map +0 -1
  478. package/dist/entry/DynamicFacade.d.ts +0 -10
  479. package/dist/entry/DynamicFacade.d.ts.map +0 -1
  480. package/dist/entry/DynamicFacade.js +0 -27
  481. package/dist/entry/DynamicFacade.js.map +0 -1
  482. package/dist/entry/FacadeEntry.d.ts +0 -11
  483. package/dist/entry/FacadeEntry.d.ts.map +0 -1
  484. package/dist/entry/FacadeEntry.js +0 -26
  485. package/dist/entry/FacadeEntry.js.map +0 -1
  486. package/dist/entry/TransportEntry.d.ts +0 -13
  487. package/dist/entry/TransportEntry.d.ts.map +0 -1
  488. package/dist/entry/TransportEntry.js +0 -24
  489. package/dist/entry/TransportEntry.js.map +0 -1
  490. package/dist/orm.d.ts +0 -207
  491. package/dist/orm.d.ts.map +0 -1
  492. package/dist/orm.js +0 -66
  493. package/dist/orm.js.map +0 -1
  494. package/dist/scan/Fronds.d.ts +0 -45
  495. package/dist/scan/Fronds.d.ts.map +0 -1
  496. package/dist/scan/Fronds.js +0 -66
  497. package/dist/scan/Fronds.js.map +0 -1
  498. package/dist/scan/declare.d.ts +0 -71
  499. package/dist/scan/declare.d.ts.map +0 -1
  500. package/dist/scan/declare.js.map +0 -1
  501. package/dist/scan/frond.d.ts +0 -224
  502. package/dist/scan/frond.d.ts.map +0 -1
  503. package/src/boot/HandlerFacade.ts +0 -242
  504. package/src/boot/binding.ts +0 -182
  505. package/src/contract/Call.ts +0 -18
  506. package/src/contract/CallLog.ts +0 -55
  507. package/src/dispatch/InputValidator.ts +0 -28
  508. package/src/dispatch/OperationExecutor.ts +0 -60
  509. package/src/dispatch/OutputProjector.ts +0 -32
  510. package/src/dispatch/PresenterArgumentResolver.ts +0 -25
  511. package/src/dispatch/RouteNotFoundError.ts +0 -29
  512. package/src/entry/DynamicFacade.ts +0 -28
  513. package/src/entry/FacadeEntry.ts +0 -32
  514. package/src/entry/TransportEntry.ts +0 -30
  515. package/src/orm.ts +0 -252
  516. package/src/scan/Fronds.ts +0 -73
  517. package/src/scan/frond.ts +0 -235
  518. /package/dist/{scan → descriptor}/frond.js +0 -0
  519. /package/dist/{contract → wire}/CallLog.js +0 -0
@@ -1,18 +1,18 @@
1
1
  import type { Container } from '@fougere/container';
2
2
  import { lowerFirst, type SchemaView } from '@fougere/schema';
3
- import type { EntityEntry, HandlerEntry, PresenterEntry } from '../scan/frond.js';
3
+ import type { EntityEntry, HandlerEntry, PresenterEntry } from '../descriptor/frond.js';
4
4
  import { hostedBy } from './hosted.js';
5
5
  import type { AuthRuntime } from './auth.js';
6
6
  import type { CreateAppOptions, App } from './types.js';
7
7
  import type { AppMiddleware } from '../wire/middleware.js';
8
- import { Logger } from '../builtins/logger.js';
9
- import { Config } from '../builtins/config.js';
8
+ import { Logger } from '../builtin/logger.js';
9
+ import { Config } from '../builtin/config.js';
10
10
  import { createRemoteRouter, createRemoteFacade } from './remote.js';
11
11
  import { registerFrames } from './together.js';
12
12
  import { Emissions } from './Emissions.js';
13
- import { HandlerFacade } from './HandlerFacade.js';
13
+ import { HandlerFacade } from '../dispatch/HandlerFacade.js';
14
14
  import { targetOf } from '../prefab/prefab.js';
15
- import { ownersOf, refuseOrmInUserCode, refuseCrudOnOwned } from './ownership.js';
15
+ import { ownersOf, refuseStorageInUserCode, refuseCrudOnOwned } from './ownership.js';
16
16
  import type { OperationContract, OperationsMap } from '../wire/operation.js';
17
17
  import {
18
18
  resolveEffectiveOperations,
@@ -22,50 +22,31 @@ import { StorageGuard } from '../dispatch/StorageGuard.js';
22
22
  import { portBindings } from './ports.js';
23
23
  import { InFlight } from '../dispatch/InFlight.js';
24
24
  // The keys, each read from where its concept is declared — never respelled here.
25
- import { facadeKeyOf, contractsKeyOf, identityCardOf, type RpcAnswer } from '../wire/call.js';
25
+ import { facadeKeyOf, contractsKeyOf, type RpcAnswer } from '../wire/call.js';
26
+ import { identityCardOf } from './card.js';
26
27
  import { AppLifecycle } from './AppLifecycle.js';
28
+ import { inheritsCrud, subjectOf } from '../prefab/crud.js';
27
29
  import { repositoryKeyOf } from '../prefab/repository.js';
28
- import { ormKeyOf } from '../orm.js';
30
+ import { storageKeyOf } from '../storage.js';
29
31
  import { presenterKeyOf } from '../prefab/presenter.js';
30
32
  import { collectorKeyOf } from '../prefab/collector.js';
31
- import { RouteAddress } from '../contract/RouteAddress.js';
33
+ import { RouteAddress } from '../wire/RouteAddress.js';
32
34
  import { DispatchLifecycle } from '../dispatch/DispatchLifecycle.js';
33
35
  import { Dispatcher } from '../dispatch/Dispatcher.js';
34
36
  import { LocalRoutePolicy } from '../dispatch/LocalRoutePolicy.js';
35
37
  import { OperationRoute } from '../dispatch/OperationRoute.js';
36
- import { RemoteRouteResolver } from '../dispatch/RemoteRouteResolver.js';
38
+ import { remoteRoutes } from '../dispatch/remoteRoutes.js';
37
39
  import { RouteRegistry } from '../dispatch/RouteRegistry.js';
38
- import { FacadeEntry } from '../entry/FacadeEntry.js';
40
+ import { facadeOperations } from '../entry/facade.js';
39
41
 
40
42
 
41
- /**
42
- * The one wording for "nobody hosts this here", with both ways out. Said twice, and the
43
- * second copy (`schemaFor`) had lost the two remedies — the same dead end, strictly less
44
- * useful, for no reason.
45
- */
43
+ /** The one wording for "nobody hosts this here", with both ways out. */
46
44
  const notLoaded = (entity: string) =>
47
45
  `Frond for '${entity}' is not loaded.\n` +
48
46
  ` - Add '${entity}' to --fronds flag\n` +
49
47
  ` - Or declare a remote: remotes: { ${entity}: 'http://...' }`;
50
48
 
51
- /**
52
- * Two fronds cannot claim one name — said at boot, because nothing else says it.
53
- *
54
- * A door lands on the ROOT container under `facadeKeyOf(address)`, and that key carries
55
- * no frond; a presenter lands there too. `registerValue` is a `Map.set`, so the second
56
- * frond loaded simply replaced the first and every call meant for one went to the other.
57
- * Silent in-process, and worse than silent under a split: `createRemoteRouter` guards its
58
- * index with `if (!byEntity.has(...))`, so THERE the first frond discovered wins. The same
59
- * application answered differently depending on how it was deployed.
60
- *
61
- * Refusing is the honest answer while a key cannot say which frond owns it. It is not the
62
- * last word: qualify the key and this boot can accept both. ORMs and repositories are not
63
- * checked because they are registered in the frond's own scope, where two fronds do not meet.
64
- *
65
- * A frond declared remote registers nothing locally, so it cannot collide here. Two REMOTE
66
- * fronds publishing one entity still shadow each other inside the router — a hole this
67
- * check does not reach.
68
- */
49
+ /** Two fronds cannot claim one name — said at boot, because nothing else says it. */
69
50
  function assertOneOwnerPerKey(
70
51
  fronds: { name: string; handlers: HandlerEntry[]; presenters: PresenterEntry[] }[],
71
52
  remotes: Record<string, string> | undefined,
@@ -73,10 +54,10 @@ function assertOneOwnerPerKey(
73
54
  const owner = new Map<string, string>();
74
55
 
75
56
  const claim = (key: string, frond: string, what: string) => {
76
- const held = owner.get(key);
77
- if (held !== undefined && held !== frond) {
57
+ const first = owner.get(key);
58
+ if (first !== undefined && first !== frond) {
78
59
  throw new Error(
79
- `Two fronds claim the key '${key}': '${held}' and '${frond}'.\n`
60
+ `Two fronds claim the key '${key}': '${first}' and '${frond}'.\n`
80
61
  + ` A ${what} is registered under a key that names no frond, so one would silently replace the other.\n`
81
62
  + ` - Rename one of the two classes, or\n`
82
63
  + ` - keep one of the two fronds out of this process (--fronds), or declare it in remotes:`,
@@ -112,6 +93,16 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
112
93
  // no builtin and a Worker can run what it builds.
113
94
  const scanStart = performance.now();
114
95
  const { fronds, diagnostics } = await hostedBy(options);
96
+ // An app that states nothing AND scans nothing is a mistake — unless something else it
97
+ // declares brings its own entities, which an auth provider does. Refused here and not in
98
+ // `hostedBy`, which is handed the frond sources and cannot see the rest of the app. The
99
+ // condition is the KEYS, not the count: a scan that found nothing is an ordinary answer.
100
+ if (!options.fronds && !options.scan && !options.auth) {
101
+ throw new Error(
102
+ 'createApp needs `fronds:` (what this app states) or `scan:` (what a scanner found). '
103
+ + 'Neither was given, and nothing else declares entities of its own.',
104
+ );
105
+ }
115
106
  const operationModel = resolveEffectiveOperations(fronds, {
116
107
  diagnostics,
117
108
  remotes: options.remotes,
@@ -122,22 +113,11 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
122
113
  log.info(`read ${fronds.length} frond(s) in ${scanMs}ms`
123
114
  + (diagnostics.length ? ` — ${diagnostics.length} thing(s) the scan could not do` : ''));
124
115
 
125
- /**
126
- * Say what could not be read, at the one line everyone already watches.
127
- *
128
- * Not a refusal: an app whose `presenters/` is unreadable still serves its
129
- * entities, and stopping the boot would trade a partial app for none. But it is
130
- * an ERROR, not a debug line — the app now serves less than its source declares,
131
- * and nothing downstream can tell that from a source that declares less.
132
- */
116
+ /** Say what could not be read, at the one line everyone already watches. */
133
117
  for (const d of blocking) log.error(`[${d.code}] ${d.message}`, d.cause);
134
118
  for (const d of diagnostics) if (d.severity === 'warning') log.warn(`[${d.code}] ${d.message}`);
135
119
 
136
- /**
137
- * An ambiguous convention is not a partial scan. Every relevant declaration was read,
138
- * but more than one executable contract can be built from it. Refuse before auth,
139
- * storage, migrations or seeds make the boot observable.
140
- */
120
+ /** An ambiguous convention is not a partial scan. */
141
121
  const invalidOperations = operationModel.resolutionDiagnostics
142
122
  .filter((diagnostic) => diagnostic.severity === 'blocking');
143
123
  if (invalidOperations.length > 0) {
@@ -152,11 +132,11 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
152
132
 
153
133
  // Auth runtime — built once from the lazy AuthConfig produced by a provider factory
154
134
  // (e.g. betterAuth({...})) in fougere.config.ts. The provider receives our db +
155
- // ormFactory so all auth writes flow through EntityOrm.
135
+ // storageFactory so all auth writes flow through Storage.
156
136
  let authRuntime: AuthRuntime | undefined;
157
137
  if (options.auth) {
158
- if (!options.ormFactory) {
159
- throw new Error('createApp: `auth` is set but `ormFactory` is missing — auth providers need it to back their adapter.');
138
+ if (!options.storageFactory) {
139
+ throw new Error('createApp: `auth` is set but `storageFactory` is missing — auth providers need it to back their adapter.');
160
140
  }
161
141
  if (options.db === undefined) {
162
142
  throw new Error('createApp: `auth` is set but `db` is missing — pass the storage handle through CreateAppOptions.db.');
@@ -164,12 +144,12 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
164
144
  log.info('initializing auth runtime');
165
145
  authRuntime = await options.auth.create({
166
146
  db: options.db,
167
- ormFactory: options.ormFactory,
147
+ storageFactory: options.storageFactory,
168
148
  });
169
149
  log.info(`auth ready — mounted at ${authRuntime.basePath}`);
170
150
  }
171
151
 
172
- // Remote routing — judged at boot: declaring remotes without a transport is a config error.
152
+ // Remote routing — validated at boot: declaring remotes without a transport is a config error.
173
153
  // A remote declaration wins over local presence: `remotes: { blog: url }` IS
174
154
  // the topology statement — the frond's code may sit in fronds/**, it runs elsewhere.
175
155
  const declaredRemotes = Object.entries(options.remotes ?? {});
@@ -214,7 +194,7 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
214
194
 
215
195
  assertOneOwnerPerKey(fronds, options.remotes);
216
196
 
217
- // Every entity of every frond, by name — so a fact can be judged where it LANDS, and
197
+ // Every entity of every frond, by name — so a fact can be validated where it LANDS, and
218
198
  // so a `reads:` clause can name a neighbour's.
219
199
  const entityByName = fronds.schemas();
220
200
  // Which frond holds an entity — what turns "a member is remote" into a refusal that
@@ -254,14 +234,14 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
254
234
  // `reads:` is what makes a cross-source reader exist here, and the list IS its
255
235
  // environment — a source holding none of these is never opened. Registered under
256
236
  // the type's own name, which is the key `depKeyOf` already derives for a plain
257
- // parameter: `constructor(private sources: Sources)` and nothing else to say.
237
+ // parameter: `constructor(private reads: Reads)` and nothing else to say.
258
238
  // Declaring `reads:` with nothing to build the reader is a boot that ignores a
259
- // clause: the handler asking for `Sources` then dies at its first call, on a
239
+ // clause: the handler asking for `Reads` then dies at its first call, on a
260
240
  // container message that names neither the clause nor what is missing.
261
241
  if (frond.reads?.length && !options.sourcesFactory) {
262
242
  frondLog.warn(
263
243
  `[reads] ${frond.reads.join(', ')} — declared in frond.config.ts, but this boot passes no `
264
- + '`sourcesFactory`, so no reader is registered and a handler asking for `Sources` will fail '
244
+ + '`sourcesFactory`, so no reader is registered and a handler asking for `Reads` will fail '
265
245
  + 'at its first call. Pass one (`@fougere/adapter-duckdb`), or drop the clause.',
266
246
  );
267
247
  }
@@ -280,14 +260,14 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
280
260
  + 'so a query naming one would find no table. Check the spelling, or the entity file.',
281
261
  );
282
262
  }
283
- scope.registerValue('Sources', await options.sourcesFactory(named, frond.name));
263
+ scope.registerValue('Reads', await options.sourcesFactory(named, frond.name));
284
264
  frondLog.debug(`cross-source reader over ${named.length} entit(ies)`);
285
265
  }
286
266
 
287
267
  // Who owns what, and the rule that makes owning mean something. Before anything is
288
268
  // registered, so a bad line is named by this refusal rather than by the container's.
289
269
  const owners = ownersOf(frond.providers);
290
- refuseOrmInUserCode(frond, owners);
270
+ refuseStorageInUserCode(frond, owners, (entity) => entityByName.has(entity));
291
271
  refuseCrudOnOwned(frond, owners);
292
272
 
293
273
  for (const provider of frond.providers) {
@@ -306,28 +286,28 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
306
286
  frondLog.debug(`${frond.providers.length} provider(s): ${frond.providers.map((p) => p.ctor.name).join(', ')}`);
307
287
  }
308
288
 
309
- // Register EntityOrm for each entity — PascalCase type name (e.g. 'PostOrm')
310
- // When a handler declares Crud(Entity, Output), scope the ORM via .output(Output)
311
- if (options.ormFactory) {
289
+ // Register Storage for each entity — PascalCase type name (e.g. 'PostStorage')
290
+ // When a handler declares Crud(Entity, Output), scope the storage via .output(Output)
291
+ if (options.storageFactory) {
312
292
  for (const entity of frond.entities) {
313
- const ormName = ormKeyOf(entity.name);
314
- const baseOrm = options.ormFactory(entity.entityClass, entity.name);
293
+ const key = storageKeyOf(entity.name);
294
+ const baseStorage = options.storageFactory(entity.entityClass, entity.name);
315
295
 
316
296
  // Check if the default handler (no surface) declares an output override
317
297
  const defaultHandler = frond.handlers.find((h) => h.address === entity.name && !h.surface);
318
298
  const outputSchema = defaultHandler?.outputOverride ?? (defaultHandler?.ctor as any)?.__output;
319
299
  const scoped = outputSchema && outputSchema !== entity.entityClass
320
- ? baseOrm.output(outputSchema)
321
- : baseOrm;
300
+ ? baseStorage.output(outputSchema)
301
+ : baseStorage;
322
302
 
323
303
  // Storage is a way out like the client surface — see `StorageGuard`.
324
304
  const guarded = new StorageGuard(entity.entityClass.getFields(), entity.name).guard(scoped);
325
- scope.registerValue(ormName, guarded);
305
+ scope.registerValue(key, guarded);
326
306
 
327
307
  // The default repository IS the guarded port — it already answers every gesture a
328
308
  // declared one forwards, so the two forms have the same shape and a handler reads
329
- // `repo.list()` either way. The wrapper that used to sit here (`{ orm: guarded }`)
330
- // existed to make `repo.orm` true in both, back when `.orm` was the way in.
309
+ // `repo.list()` either way. The wrapper that used to sit here (`{ storage: guarded }`)
310
+ // existed to make `repo.storage` true in both, back when `.storage` was the way in.
331
311
  //
332
312
  // Not registered for an OWNED entity: an aggregate's members are reached through it
333
313
  // and nowhere else, and the default would be a second door under a name a handler
@@ -342,7 +322,7 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
342
322
  }
343
323
  }
344
324
  if (frond.entities.length > 0) {
345
- frondLog.debug(`${frond.entities.length} entity ORM(s): ${frond.entities.map((e) => e.name).join(', ')}`);
325
+ frondLog.debug(`${frond.entities.length} entity storage(s): ${frond.entities.map((e) => e.name).join(', ')}`);
346
326
  }
347
327
  }
348
328
 
@@ -357,8 +337,9 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
357
337
  entityByName,
358
338
  frondOf,
359
339
  hostedHere: (name) => !(options.remotes && name in options.remotes),
360
- ormFactory: options.ormFactory,
340
+ storageFactory: options.storageFactory,
361
341
  sourceOf: options.sourceOf,
342
+ transacts: options.transacts,
362
343
  transacted: options.transacted,
363
344
  log: frondLog,
364
345
  },
@@ -395,25 +376,25 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
395
376
  targetScope: Container,
396
377
  facadeKey: string,
397
378
  ) => {
398
- const facade = new HandlerFacade(
399
- {
400
- handler,
401
- handlers: frond.handlers,
402
- entity,
403
- scope: targetScope,
404
- key: facadeKey,
405
- operations: operationModel.forHandler(handler),
406
- },
407
- {
408
- frond: frond.name,
409
- frondScope: scope,
410
- log: frondLog,
411
- collectors: collectorTypeNames,
412
- presenters: presenterMap,
413
- middlewaresFor: getMiddlewares,
414
- emissions,
415
- },
416
- );
379
+ if (inheritsCrud(handler.ctor) && !entity) {
380
+ // An installed Crud subject may be absent from the local scan.
381
+ frondLog.debug(`${handler.ctor.name} extends Crud() and no scanned entity is named `
382
+ + `'${subjectOf(handler.ctor, handler.address)}' — installed entity, or a missing `
383
+ + `one: no storage will be injected`);
384
+ }
385
+
386
+ const facade = new HandlerFacade(handler, targetScope, {
387
+ key: facadeKey,
388
+ frond: frond.name,
389
+ handlers: frond.handlers,
390
+ operations: operationModel.forHandler(handler),
391
+ collectors: collectorTypeNames,
392
+ presenter: presenterMap.get(handler.address),
393
+ presenterScope: scope,
394
+ middlewares: () => getMiddlewares(handler.address),
395
+ });
396
+ // Emissions use the same contracts and execution path as direct calls.
397
+ emissions.note(facade.contracts, facadeKey);
417
398
  // The terms alongside the door, under the same audience — a surface that serves
418
399
  // fewer ops describes fewer ops.
419
400
  container.registerValue(contractsKeyOf(handler.address, handler.surface), facade.contracts);
@@ -440,18 +421,18 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
440
421
  routeRegistry.register(new OperationRoute(
441
422
  'local',
442
423
  address,
443
- (call) => facade.ops[operation](call.invocation),
424
+ (call) => facade.execute(operation, call.invocation),
444
425
  ));
445
426
  }
446
427
  }
447
428
 
448
- const entry = new FacadeEntry(
429
+ const operations = facadeOperations(
449
430
  handler.surface ? localDispatcher : dispatcher,
450
431
  handler.address,
451
432
  routeRegistry.operationNames(handler.address, handler.surface),
452
433
  handler.surface,
453
434
  );
454
- container.registerValue(facadeKey, entry.operations);
435
+ container.registerValue(facadeKey, operations);
455
436
  };
456
437
 
457
438
  // A presenter is about an entity — computed fields sit on a shape — so this walks
@@ -485,7 +466,7 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
485
466
  const facadeKey = facadeKeyOf(handler.address);
486
467
  buildFacade(entity, handler, scope, facadeKey);
487
468
  frondLog.debug(`${facadeKey} [${Object.keys(container.resolve(facadeKey) as any).join(', ')}]`
488
- + (entity ? '' : ' — no entity of that name: no ORM, no projection, no presenter'));
469
+ + (entity ? '' : ' — no entity of that name: no storage, no projection, no presenter'));
489
470
  }
490
471
 
491
472
  // The dual, and it stays: a shape that declares no operation answers nothing. Said
@@ -496,7 +477,7 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
496
477
  }
497
478
  }
498
479
 
499
- // Surface handlers — create sub-scope per surface handler with scoped ORM
480
+ // Surface handlers — create sub-scope per surface handler with scoped storage
500
481
  //
501
482
  // Pointing at nothing is legal HERE TOO. This loop used to `continue` when no entity
502
483
  // carried the handler's name, so `handlers/public/SearchHandler.ts` with no `Search`
@@ -506,25 +487,25 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
506
487
  const entity = frond.entities.find((e) => e.name === handler.address);
507
488
  const surfaceScope = scope.createScope();
508
489
 
509
- // Register scoped ORM if output override differs from entity — under the REPOSITORY
490
+ // Register scoped storage if output override differs from entity — under the REPOSITORY
510
491
  // key, which is what a Crud handler asks for, and under the port's own for a holder
511
492
  // that legitimately names it. Registering only the latter left a named surface with
512
493
  // no door at all once the façade stopped spelling the storage.
513
- if (entity && options.ormFactory) {
514
- const baseOrm = options.ormFactory(entity.entityClass, entity.name);
494
+ if (entity && options.storageFactory) {
495
+ const baseStorage = options.storageFactory(entity.entityClass, entity.name);
515
496
  const outputSchema = handler.outputOverride ?? (handler.ctor as any).__output;
516
497
  const scoped = outputSchema && outputSchema !== entity.entityClass
517
- ? baseOrm.output(outputSchema)
518
- : baseOrm;
498
+ ? baseStorage.output(outputSchema)
499
+ : baseStorage;
519
500
  const guarded = new StorageGuard(entity.entityClass.getFields(), entity.name).guard(scoped);
520
- surfaceScope.registerValue(ormKeyOf(entity.name), guarded);
501
+ surfaceScope.registerValue(storageKeyOf(entity.name), guarded);
521
502
  surfaceScope.registerValue(repositoryKeyOf(entity.name), guarded);
522
503
  }
523
504
 
524
505
  const facadeKey = facadeKeyOf(handler.address, handler.surface);
525
506
  buildFacade(entity, handler, surfaceScope, facadeKey);
526
507
  frondLog.debug(`${facadeKey} [${Object.keys(container.resolve(facadeKey) as any).join(', ')}]`
527
- + (entity ? '' : ' — no entity of that name: no ORM, no projection, no presenter'));
508
+ + (entity ? '' : ' — no entity of that name: no storage, no projection, no presenter'));
528
509
  }
529
510
 
530
511
  // A named surface is closed, so what it contains is a fact worth stating.
@@ -562,17 +543,7 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
562
543
  // Once every door exists: what is announced here and what is listened to are both known.
563
544
  emissions.register();
564
545
 
565
- /**
566
- * The last resort, held by the container so every resolution path shares it.
567
- *
568
- * It used to live on `app.resolve` alone, which meant the runner found a remote façade
569
- * and dependency injection did not — a handler asking for a neighbour that had moved
570
- * got `is not registered` while the very same call over the wire worked. One door now.
571
- *
572
- * Surface-scoped keys ('admin:productHandler') stay local: a named surface resolves in
573
- * this container only, so fabricating one for a remote frond would answer NOT_FOUND on
574
- * everything (see Known issues).
575
- */
546
+ /** The last resort, held by the container so every resolution path shares it. */
576
547
  container.setFallback?.((name) => {
577
548
  if (!remoteRouter) return undefined;
578
549
  if (!name.endsWith('Handler') || name.includes(':')) return undefined;
@@ -580,19 +551,15 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
580
551
  // `lowerFirst` because a DEPENDENCY names the type as written — `ProductHandler`,
581
552
  // PascalCase — while a card declares `product`, so the raw strip asked the router for
582
553
  // 'Product' and every by-type dependency on a remote handler answered NOT_FOUND.
583
- return new FacadeEntry(
554
+ return facadeOperations(
584
555
  dispatcher,
585
556
  lowerFirst(name.replace(/Handler$/, '')),
586
- ).operations;
557
+ );
587
558
  });
588
559
 
589
560
  /**
590
- * Everything this app holds, let go in reverse of how it was taken: what an extension
591
- * took on last, then the container's own, then whoever handed a resource in.
592
- *
593
- * It does NOT wait for running calls — `drain()` is that, and it is a separate gesture
594
- * because waiting and releasing do not have the same owner: a test releases at once, a
595
- * host turning the ring wants the work finished first.
561
+ * Everything this app holds, let go in reverse of how it was taken: what an extension took on
562
+ * last, then the container's own, then whoever handed a resource in.
596
563
  */
597
564
  const release = async (): Promise<void> => {
598
565
  // Every level is told to close even when one refuses, and the refusals leave together —
@@ -615,13 +582,7 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
615
582
  }
616
583
  };
617
584
 
618
- /**
619
- * Stop taking calls, and resolve once the ones already running are done.
620
- *
621
- * Refuses on `timeoutMs` rather than resolving anyway: a drain that gives up quietly
622
- * reads exactly like a drain that succeeded, and the caller — who is about to release
623
- * a storage connection under whatever is left — is the one who must decide.
624
- */
585
+ /** Stop taking calls, and resolve once the ones already running are done. */
625
586
  const drain = async (timeoutMs?: number): Promise<void> => {
626
587
  inflight.close();
627
588
  if (timeoutMs === undefined) return inflight.whenIdle();
@@ -676,28 +637,7 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
676
637
  }
677
638
  };
678
639
 
679
- /**
680
- * THE membership rule, stated once — every projection reads this and nothing
681
- * else. It used to live twice, verbatim, in the REST and GraphQL adapters,
682
- * and two other readers invented their own answer.
683
- *
684
- * Naming an audience closes it. Two ways to name an entity into a surface,
685
- * in precedence order:
686
- * - `surfaces:` in frond.config.ts — when the list exists, it IS the list;
687
- * - a handler under `handlers/<surface>/` — which also restricts the façade.
688
- * `@expose` is a THIRD way and this rule does not read it: the scan sets a
689
- * boolean, read by the two adapters and by `exposedAdapters` — never here.
690
- * What neither of the two names is not served. It used to be the reverse: no
691
- * `public:categoryHandler` meant the FULL CategoryHandler rode the public
692
- * door, create/update/delete included (measured on demos/nuxt-blog).
693
- *
694
- * A named entity with no façade of its own falls back to the default one —
695
- * that is not the old widening, it is the author saying "this one, as it is".
696
- * The leak was exposure with no statement behind it.
697
- *
698
- * A surface key stays local: a doublure serves whatever the remote's own door
699
- * serves, which is the remote's business and not ours to re-audience.
700
- */
640
+ /** THE membership rule, stated once — every projection reads this and nothing else. */
701
641
  const facadeFor = (entity: string, surface?: string): Record<string, Function> | undefined => {
702
642
  if (!surface) return facadeAt(facadeKeyOf(entity), true);
703
643
 
@@ -709,18 +649,18 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
709
649
 
710
650
  const fallback = facadeAt(facadeKeyOf(entity), false);
711
651
  return fallback
712
- ? new FacadeEntry(
652
+ ? facadeOperations(
713
653
  localDispatcher,
714
654
  entity,
715
655
  routeRegistry.operationNames(entity, surface),
716
656
  surface,
717
- ).operations
657
+ )
718
658
  : undefined;
719
659
  };
720
660
 
721
661
  if (remoteRouter) {
722
662
  const remoteFacades = new Map<string, Record<string, Function>>();
723
- routeRegistry.addResolver(new RemoteRouteResolver((entity) => {
663
+ routeRegistry.addResolver(remoteRoutes((entity) => {
724
664
  const known = remoteFacades.get(entity);
725
665
  if (known) return known;
726
666
  const facade = createRemoteFacade(entity, remoteRouter, getMiddlewares);
@@ -742,19 +682,14 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
742
682
  };
743
683
 
744
684
  /**
745
- * The storage an entity is backed by — the dual of `facadeFor`, which serves its
746
- * client-facing door. Both are ways in; an entity that opens none of them still has rows.
747
- *
748
- * It resolves through the owning frond's scope, because that is where entity ORMs live:
749
- * `resolve('UserOrm')` reads the ROOT container and never finds one, so callers outside
750
- * the frond concluded there was no storage. The seed loop did exactly that, and skipped
751
- * every entity with no façade — the one case its own fallback existed for.
685
+ * The storage an entity is backed by — the dual of `facadeFor`, which serves its client-facing
686
+ * door.
752
687
  */
753
- const ormFor = (entity: string): unknown | undefined => {
688
+ const storageFor = (entity: string): unknown | undefined => {
754
689
  const owner = fronds.owner(entity);
755
690
  if (!owner) return undefined;
756
691
 
757
- const key = ormKeyOf(entity);
692
+ const key = storageKeyOf(entity);
758
693
  try {
759
694
  return container.resolve<Container>(`frond:${owner.name}`).resolve(key);
760
695
  } catch {
@@ -762,14 +697,7 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
762
697
  }
763
698
  };
764
699
 
765
- /**
766
- * The presenter of an entity, resolved through its owning frond's scope.
767
- *
768
- * Same shape as `ormFor`, and it exists for the same reason: without it an adapter
769
- * has to spell the container key itself. `schema-graphql` did — a hand-written
770
- * `${Name}Presenter` — and a key respelled elsewhere finds nothing and says nothing
771
- * the day the convention moves, which is exactly the failure `verify.ts` refuses.
772
- */
700
+ /** The presenter of an entity, resolved through its owning frond's scope. */
773
701
  const presenterFor = (entity: string): unknown | undefined => {
774
702
  const owner = fronds.owner(entity);
775
703
  if (!owner) return undefined;
@@ -799,7 +727,7 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
799
727
  operationsFor,
800
728
  listensTo: () => emissions.listensTo(),
801
729
  deliver: (fact, payload) => emissions.deliver(fact, payload),
802
- ormFor,
730
+ storageFor,
803
731
  presenterFor,
804
732
  dispose: release,
805
733
  drain,
@@ -841,7 +769,23 @@ export async function createApp(options: CreateAppOptions): Promise<App> {
841
769
 
842
770
  // The last thing the boot does, and the first thing a release undoes. An extension may
843
771
  // await here — which is what a provider needing to OPEN something could never do.
844
- await appLifecycle.up(app);
772
+ //
773
+ // A refusal here has to release what the boot took, because the caller cannot: it handed
774
+ // `onDispose` over BEFORE this line and never receives the app that would carry it back.
775
+ // Whoever opened a connection to give it to us would otherwise leak it on every failed boot.
776
+ try {
777
+ await appLifecycle.up(app);
778
+ } catch (cause) {
779
+ try {
780
+ await release();
781
+ } catch (refused) {
782
+ throw new AggregateError(
783
+ [cause, ...(refused instanceof AggregateError ? refused.errors : [refused])],
784
+ `The boot failed and could not release everything it had taken: ${(cause as Error)?.message ?? String(cause)}`,
785
+ );
786
+ }
787
+ throw cause;
788
+ }
845
789
 
846
790
  return app;
847
791
  }
@@ -0,0 +1,84 @@
1
+ /** What an app says about ITSELF — the `rpc.discover` answer, built from the app. */
2
+ import { Card } from '@fougere/schema';
3
+ import type { App } from './types.js';
4
+ import { factsAnnouncedBy } from '../emit.js';
5
+ import type { InvocationContext } from '../wire/Invocation.js';
6
+ import { facadeKeyOf, contractsKeyOf, type CardOp, type IdentityCard } from '../wire/call.js';
7
+ import type { OperationContract } from '../wire/operation.js';
8
+
9
+ type AnyFacade = Record<string, (invocation?: InvocationContext) => Promise<unknown>>;
10
+
11
+ /** Serialize what the app hosts, for one audience. */
12
+ export function identityCardOf(app: App, surface?: string): IdentityCard {
13
+ const declared = app.fronds.schemas();
14
+
15
+ return {
16
+ fronds: app.fronds.map((frond) => {
17
+ // What the frond answers to, not what it stores. This walked `frond.entities`, so a
18
+ // handler carrying no entity — a health check, a search across shapes — was built,
19
+ // served, and absent from the card: `sync` could not generate its door and a remote
20
+ // consumer had no way to know it existed. The boot has said "pointing at nothing is
21
+ // legal" since handlers became the subject; the card had not caught up.
22
+ const byEntity = new Map(frond.entities.map((entity) => [entity.name, entity]));
23
+ const addresses = [...new Set([
24
+ ...frond.entities.map((entity) => entity.name),
25
+ ...frond.handlers.map((handler) => handler.address),
26
+ ])];
27
+
28
+ return {
29
+ name: frond.name,
30
+ doors: addresses.flatMap((address) => {
31
+ const ops = facadeOps(app, address, surface);
32
+ if (ops.length === 0) return [];
33
+ const entity = byEntity.get(address);
34
+ return [{
35
+ name: address,
36
+ ops,
37
+ // Absent when nothing of that name is stored. A door is still a door.
38
+ ...(entity ? { schema: Card.fromSchema(entity.entityClass, address).descriptor } : {}),
39
+ }];
40
+ }),
41
+ /** What leaves on its own — the same list on every surface, deliberately. */
42
+ facts: factsAnnouncedBy(frond.handlers).map((name) => {
43
+ const entityClass = declared.get(name);
44
+ return { name, ...(entityClass ? { schema: Card.fromSchema(entityClass, name).descriptor } : {}) };
45
+ }),
46
+ };
47
+ }),
48
+ };
49
+ }
50
+
51
+ function facadeOps(app: App, entityName: string, surface?: string): CardOp[] {
52
+ let facade: AnyFacade;
53
+ try {
54
+ facade = app.container.resolve<AnyFacade>(facadeKeyOf(entityName, surface));
55
+ } catch {
56
+ return [];
57
+ }
58
+
59
+ // The façade is the list of names; the model is the resolved terms.
60
+ const effective = app.operationsFor(entityName, surface);
61
+ if (!effective) {
62
+ throw new Error(
63
+ `Facade '${facadeKeyOf(entityName, surface)}' exists without an effective operation table.`,
64
+ );
65
+ }
66
+
67
+ return Object.keys(facade).map((name) => {
68
+ const contract = effective.get(name);
69
+ if (!contract) {
70
+ throw new Error(
71
+ `Facade '${facadeKeyOf(entityName, surface)}' serves '${name}' without an effective contract.`,
72
+ );
73
+ }
74
+
75
+ return {
76
+ name,
77
+ ...(contract?.description && { description: contract.description }),
78
+ ...(contract?.input && { input: Card.fromSchema(contract.input, name).descriptor }),
79
+ ...(contract?.output && { output: Card.fromSchema(contract.output, name).descriptor }),
80
+ ...(contract?.cardinality && { cardinality: contract.cardinality }),
81
+ kind: contract.kind,
82
+ };
83
+ });
84
+ }