@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,27 +1,28 @@
1
1
  import { readdir, readFile } from 'node:fs/promises';
2
2
  import { existsSync, type Dirent } from 'node:fs';
3
3
  import { join, dirname, basename, resolve as resolvePath } from 'node:path';
4
- import type { FrondDescriptor, ProviderEntry, EntityEntry, HandlerEntry, PresenterEntry, CollectorEntry, SeedEntry, ScanResult, ScanDiagnostic } from './frond.js';
4
+ import type { FrondDescriptor, ProviderEntry, EntityEntry, HandlerEntry, PresenterEntry, CollectorEntry, SeedEntry } from '../descriptor/frond.js';
5
+ import type { ScanResult, ScanDiagnostic } from './result.js';
5
6
  import { ANONYMOUS_SCHEMA_NAME, type SchemaView } from '@fougere/schema';
6
7
  import type { OperationContract, OperationsMap } from '../wire/operation.js';
7
8
  import { cardinalityOf } from '../wire/operation.js';
8
- import { computeBindingPlan } from '../boot/binding.js';
9
+ import { computeBindingPlan } from '../wire/binding.js';
9
10
  import {
10
11
  parseAllHandlerMethods,
11
12
  parsePresenterMethods,
12
13
  parseConstructorParams,
13
14
  resetTypePrograms,
14
15
  seedTypeProgram,
15
- type ParsedType,
16
16
  } from './handler-parser.js';
17
+ import type { TypeRef } from '../wire/signature.js';
17
18
  import { loadFrondConfig } from '../frond-config.js';
18
19
  import { emitKeyOf } from '../emit.js';
19
20
  import { getPresenterFields } from '../prefab/presenter.js';
20
- import { ormKeyOf, togetherKeyOf } from '../orm.js';
21
+ import { storageKeyOf, togetherKeyOf } from '../storage.js';
21
22
  import { targetOf, viewsOf, outputOf } from '../prefab/prefab.js';
22
23
  import { ownedBy, repositoryKeyOf } from '../prefab/repository.js';
23
24
  import { lowerFirst } from '@fougere/schema';
24
- import { Fronds } from './Fronds.js';
25
+ import { Fronds } from '../descriptor/Fronds.js';
25
26
  import { getModuleLoader } from '../loader.js';
26
27
  import {
27
28
  type Conventions, type ConventionsInput,
@@ -30,13 +31,7 @@ import {
30
31
 
31
32
  // FS
32
33
 
33
- /**
34
- * What this scan run could not do. Reset by {@link scanProject}, which owns a run.
35
- *
36
- * Module-scoped like the loader and the cache root above: the scanner already has a
37
- * notion of "the current run". Three sites record — measured — so a per-run object
38
- * would carry twenty methods to spare one line of reset.
39
- */
34
+ /** What this scan run could not do. */
40
35
  let diagnostics: ScanDiagnostic[] = [];
41
36
 
42
37
  function record(d: ScanDiagnostic): void {
@@ -44,10 +39,8 @@ function record(d: ScanDiagnostic): void {
44
39
  }
45
40
 
46
41
  /**
47
- * An absent convention directory is the ordinary case — a frond without
48
- * `presenters/` is not a defect. Anything else (permissions, an I/O error, a path
49
- * that is not a directory) means the scan did not look, and answering `[]` says
50
- * it did. One `catch` used to conflate the two.
42
+ * An absent convention directory is the ordinary case — a frond without `presenters/` is not a
43
+ * defect.
51
44
  */
52
45
  async function readEntries(path: string): Promise<Dirent[]> {
53
46
  try {
@@ -114,13 +107,7 @@ function findWorkspaceRoot(from: string): string {
114
107
  return resolvePath(from); // fallback: use project root itself
115
108
  }
116
109
 
117
- /**
118
- * Strip the 'Handler' suffix → the name the handler answers to. 'PostHandler' → 'post'.
119
- *
120
- * Nothing here checks that an entity carries the result, and nothing should: a handler
121
- * about no stored row is ordinary. The old name of this function — `toEntityName` — is
122
- * what let "one façade per entity" be repeated until it read as a rule.
123
- */
110
+ /** Strip the 'Handler' suffix → the name the handler answers to. */
124
111
  function toAddress(className: string): string {
125
112
  const base = className.endsWith('Handler') ? className.slice(0, -7) : className;
126
113
  return lowerFirst(base);
@@ -129,20 +116,10 @@ function toAddress(className: string): string {
129
116
  // Scan
130
117
 
131
118
  /**
132
- * The container key a constructor parameter asks for — derived from its TYPE, not from
133
- * how the type was spelled.
134
- *
135
- * `deps` used to be `p.type.name`, so the key WAS the alias's name: `type ListOrm =
136
- * EntityOrm<List>` resolved only because someone had spelled it exactly like the
137
- * registration key (`ListOrm`), while `type ListRepo = EntityOrm<List>` — the same type —
138
- * typechecked and died at boot on `'ListRepo' is not registered`. And `EntityOrm<List>`
139
- * written out in full asked for `'EntityOrm'`, which nothing registers.
140
- *
141
- * `EntityOrm<X>` names X's storage, so that is the key. The generic argument was already
142
- * parsed (`ParsedType.generics`) and thrown away. Anything else keeps its own name: a
143
- * plain service IS designated by its class name.
119
+ * The container key a constructor parameter asks for — derived from its TYPE, not from how the
120
+ * type was spelled.
144
121
  */
145
- function depKeyOf(type: ParsedType): string {
122
+ function depKeyOf(type: TypeRef): string {
146
123
  // `Facade<PostHandler>` — the second port, read exactly like the first. The type names
147
124
  // what arrives: not the handler (its methods take positional arguments and it is never
148
125
  // injected), but the door built in front of it. Same key whether that door is the local
@@ -165,32 +142,25 @@ function depKeyOf(type: ParsedType): string {
165
142
  // `RepositoryOf<Post>` — what an author writes when no repository file exists. The dual
166
143
  // of the line below: one names the port, the other the holder, and both resolve to a key
167
144
  // rather than to a class the author would have to invent.
168
- const held = type.name === 'RepositoryOf' ? type.generics?.[0]?.name : undefined;
169
- if (held) return repositoryKeyOf(held);
145
+ const subject = type.name === 'RepositoryOf' ? type.generics?.[0]?.name : undefined;
146
+ if (subject) return repositoryKeyOf(subject);
170
147
 
171
- const target = type.name === 'EntityOrm' ? type.generics?.[0]?.name : undefined;
148
+ // The GENERIC names the subject, so a prefix does not change the key — `MeiliStorage<Card>`
149
+ // is `Card`'s storage. A class named `FileStorage` is untouched: it carries no generic.
150
+ const target = type.name.endsWith('Storage') && type.generics?.length === 1
151
+ ? type.generics[0]?.name
152
+ : undefined;
172
153
  if (!target) return type.name;
173
154
 
174
- return ormKeyOf(target);
155
+ return storageKeyOf(target);
175
156
  }
176
157
 
177
- /**
178
- * `'[Account, Ledger]'` → `['Account', 'Ledger']`.
179
- *
180
- * The tuple was chosen over the variadic form the parser reads more cleanly, because the
181
- * variadic one costs arities-with-defaults and a `never` filter on the TypeScript side
182
- * while the tuple maps to `[EntityOrm<Account>, EntityOrm<Ledger>]` in one line. The
183
- * parser does not get to decide alone; this split is what that choice costs.
184
- */
158
+ /** `'[Account, Ledger]'` → `['Account', 'Ledger']`. */
185
159
  function tupleMembers(raw: string): string[] {
186
160
  return raw.replace(/^\[|\]$/g, '').split(',').map((member) => member.trim()).filter(Boolean);
187
161
  }
188
162
 
189
- /**
190
- * These readings are semantic: an unchanged file can mean something different after an
191
- * imported alias changes. The parser's TypeScript Program is the cache for one scan; a
192
- * cache keyed only by this file's bytes would be unsound.
193
- */
163
+ /** These readings are semantic. */
194
164
  const ctorParamsOf = (filePath: string) =>
195
165
  parseConstructorParams(filePath);
196
166
 
@@ -216,9 +186,9 @@ async function toProvider(filePath: string): Promise<ProviderEntry> {
216
186
  const owned = ownedBy(ctor);
217
187
  const target = targetOf(ctor);
218
188
  if (owned.length > 1 && deps.length === 0) {
219
- deps.push(...owned.map((entity) => ormKeyOf(lowerFirst((entity as { name: string }).name))));
189
+ deps.push(...owned.map((entity) => storageKeyOf(lowerFirst((entity as { name: string }).name))));
220
190
  } else if (target && deps.length === 0) {
221
- deps.push(ormKeyOf(lowerFirst((target as { name: string }).name)));
191
+ deps.push(storageKeyOf(lowerFirst((target as { name: string }).name)));
222
192
  }
223
193
 
224
194
  // No `name` beside `ctor`: a provider registers under `ctor.name`, which is what
@@ -243,10 +213,10 @@ async function toEntityEntry(filePath: string): Promise<EntityEntry | null> {
243
213
  }
244
214
 
245
215
  /**
246
- * Resolve a ParsedType to a runtime schema if available in module exports.
216
+ * Resolve a TypeRef to a runtime schema if available in module exports.
247
217
  * Handles arrays, generics (uses base name), and simple references.
248
218
  */
249
- function resolveSchema(type: ParsedType, moduleExports: Record<string, unknown>): SchemaView | undefined {
219
+ function resolveSchema(type: TypeRef, moduleExports: Record<string, unknown>): SchemaView | undefined {
250
220
  // An array's element type IS `type.name` — the arity rides beside it, so nothing has
251
221
  // to be unwrapped here.
252
222
  // For generics like Pagination<Post>, also check inner types
@@ -271,12 +241,7 @@ function resolveSchema(type: ParsedType, moduleExports: Record<string, unknown>)
271
241
  return undefined;
272
242
  }
273
243
 
274
- /**
275
- * Parse ALL method signatures for unified binding.
276
- *
277
- * Resolves schemas for all params (not just the first) and stores
278
- * full signatures for the binding algorithm.
279
- */
244
+ /** Parse ALL method signatures for unified binding. */
280
245
  async function inferOperations(
281
246
  filePath: string,
282
247
  handlerName: string,
@@ -307,15 +272,8 @@ async function inferOperations(
307
272
  }
308
273
 
309
274
  /**
310
- * A base class the parse could not open — an installed package, typically, whose
311
- * source is not in the workspace. Its operations are missing from this façade, and
312
- * the scan cannot tell whether there were any.
313
- *
314
- * A warning, not a refusal: an installed base class with no operation is perfectly
315
- * ordinary, and the boot has no way to decide between the two. So it names the
316
- * clause and stops there. Stating the contract in `frond.config.ts` is the answer
317
- * — the third producer, which creates an op neither other producer found — and it
318
- * silences this by making the op exist.
275
+ * A base class the parse could not open — an installed package, typically, whose source is not
276
+ * in the workspace.
319
277
  */
320
278
  for (const base of parsed.unresolvedHeritage) {
321
279
  record({
@@ -341,10 +299,10 @@ async function inferOperations(
341
299
  // caller supplies through the body are candidates: a schema-typed collector, fact or
342
300
  // context parameter is not input merely because it names an entity. The old loop
343
301
  // ignored provenance and assigned the first schema it met, so swapping two parameters
344
- // silently changed the contract the façade used to judge the request body.
302
+ // silently changed the contract the façade used to validate the request body.
345
303
  const binding = computeBindingPlan(method.params, collectorTypeNames);
346
304
  const candidates = method.params.flatMap((param, index) => {
347
- if (binding[index]?.source.kind !== 'body') return [];
305
+ if (binding[index]?.source.kind !== 'input') return [];
348
306
  const schema = resolveSchema(param.type, moduleExports);
349
307
  return schema ? [{ param, schema }] : [];
350
308
  });
@@ -521,13 +479,7 @@ async function scanFrond(frondPath: string, name: string, source: FrondDescripto
521
479
  entities: entitiesDir, handlers: handlersDir,
522
480
  presenters: presentersDir, collectors: collectorsDir, seeds: seedsDir,
523
481
  } = conventions.dirs;
524
- /**
525
- * A convention directory, read by whoever knows the shape it holds.
526
- *
527
- * The same three lines were written six times — list, read in parallel, drop what the
528
- * reader refused — which is what made `handlers/` quietly special: it grew a second
529
- * pass for its surfaces and nothing else could.
530
- */
482
+ /** A convention directory, read by whoever knows the shape it holds. */
531
483
  const collect = async <T extends object>(
532
484
  dir: string,
533
485
  read: (filePath: string) => Promise<T | null>,
@@ -614,15 +566,7 @@ async function scanFrond(frondPath: string, name: string, source: FrondDescripto
614
566
  };
615
567
  }
616
568
 
617
- /**
618
- * The frond's name — the directory, unless its `package.json` renames it. One rule for a
619
- * frond under `fronds/` and for the root frond alike, so the root needs no second spelling.
620
- *
621
- * Carrying the convention is what makes a frond; the key never marked anything and
622
- * nothing read it. It earns its keep as the one thing the directory cannot say: that
623
- * `fronds/blog-v2/` serves the frond still called `blog` — so a rename on disk does not
624
- * rename the entity keys, the `@fronds/*` import or a `remotes:` entry.
625
- */
569
+ /** The frond's name — the directory, unless its `package.json` renames it. */
626
570
  async function frondNameOf(frondPath: string, dirName: string): Promise<string> {
627
571
  try {
628
572
  const pkg = JSON.parse(await readFile(join(frondPath, 'package.json'), 'utf8')) as {
@@ -636,15 +580,8 @@ async function frondNameOf(frondPath: string, dirName: string): Promise<string>
636
580
  }
637
581
 
638
582
  /**
639
- * A frond is a directory carrying the convention, and the project root is one such
640
- * directory — so a single-domain app writes `entities/` next to `app/` and never names
641
- * anything. `fronds/` is not the definition, it is where the OTHERS live: the root frond
642
- * stays put when a second domain appears, which is what makes flattening free instead of
643
- * a deferred move.
644
- *
645
- * `entities/` is the test, not "any convention directory". `services/` and `repositories/`
646
- * are ordinary top-level names in projects that never heard of Fougere, and a domain
647
- * without a single entity is not a domain.
583
+ * A frond is a directory carrying the convention, and the project root is one such directory — so
584
+ * a single-domain app writes `entities/` next to `app/` and never names anything.
648
585
  */
649
586
  async function rootFrondOf(root: string, workspaceRoot: string, conventions: Conventions): Promise<FrondDescriptor | null> {
650
587
  if ((await files(join(root, conventions.dirs.entities))).length === 0) return null;
@@ -652,21 +589,7 @@ async function rootFrondOf(root: string, workspaceRoot: string, conventions: Con
652
589
  return scanFrond(root, name, { path: root, package: frondPackage(name, conventions) }, conventions, workspaceRoot);
653
590
  }
654
591
 
655
- /**
656
- * `@fronds/<name>` → the directory it names, for every frond of a project.
657
- *
658
- * The framework states this convention — `FrondSource.package` has always spelled it, and
659
- * `fougere sync` writes it into a consumer's tsconfig — and until now its own reader could
660
- * not resolve it. The Nuxt module registered a Vite alias, so a `.vue` page could import
661
- * `@fronds/blog/entities/Post`, while the SCAN loaded sources through a bare jiti: a frond
662
- * naming its neighbour got `Cannot find module '@fronds/user/entities/User.js'`. So the one
663
- * form that survives a split was the one form that did not run.
664
- *
665
- * Hand it to the module loader — `createJiti(url, { alias: await frondAliases(root) })` —
666
- * and the named form resolves everywhere the framework reads. A directory listing and a
667
- * `package.json` read, no parsing: it is deliberately callable BEFORE the loader exists,
668
- * which is what makes the chicken-and-egg go away.
669
- */
592
+ /** `@fronds/<name>` → the directory it names, for every frond of a project. */
670
593
  export async function frondAliases(root: string, conventions: Conventions = DEFAULT_CONVENTIONS): Promise<Record<string, string>> {
671
594
  const frondsDir = join(root, conventions.fronds);
672
595
  const aliases: Record<string, string> = {};
@@ -680,17 +603,7 @@ export async function frondAliases(root: string, conventions: Conventions = DEFA
680
603
  aliases[frondPackage(await frondNameOf(path, dir), conventions)] = resolvePath(path);
681
604
  }
682
605
 
683
- /**
684
- * A SYNCED frond answers to the same name.
685
- *
686
- * `fougere sync` writes `.fougere/remotes/<name>/` and registers it in `remotes.json`,
687
- * which the Nuxt module already reads to alias `@fronds/<name>`. Doing it here too is
688
- * what makes the convention mean ONE thing: a consumer writes `@fronds/blog/entities/Post`
689
- * and never learns whether that frond is on this disk or was fetched from a card.
690
- *
691
- * A local frond wins a name collision — its source is the truth, a synced copy is a
692
- * mirror of somebody else's.
693
- */
606
+ /** A SYNCED frond answers to the same name. */
694
607
  try {
695
608
  const registry = JSON.parse(
696
609
  await readFile(join(root, '.fougere', 'remotes.json'), 'utf8'),
@@ -751,23 +664,12 @@ export async function scanProject(
751
664
 
752
665
  // The app's own domain first, then the ones it took in.
753
666
  const all = rootFrond ? [rootFrond, ...under] : under;
754
- const fronds = Fronds.scanned(filter ? all.filter((f) => filter.includes(f.name)) : all);
667
+ const fronds = Fronds.hosting(filter ? all.filter((f) => filter.includes(f.name)) : all);
755
668
 
756
669
  return { fronds, diagnostics };
757
670
  }
758
671
 
759
- /**
760
- * What changes when a frond's domain changes — the paths a dev loop watches.
761
- *
762
- * It lives beside the scan rather than beside the conventions it reads, because it turns
763
- * names into DISK paths: `join` is `node:path`, and `conventions.ts` is reached from
764
- * `index` through `frond()`, which an edge bundle imports. Measured — esbuild refused the
765
- * Worker with `Could not resolve "node:path"`.
766
- *
767
- * The root frond IS the scan root, so watching its path would match every write in the
768
- * project: `.nuxt/`, `node_modules/`, the build output. Its convention directories are
769
- * the frond, and they are what a scan re-reads.
770
- */
672
+ /** What changes when a frond's domain changes — the paths a dev loop watches. */
771
673
  export function watchPathsOf(
772
674
  frond: { source: { path: string } },
773
675
  scanRoot: string,
@@ -1,39 +1,14 @@
1
- /**
2
- * The scan, written as the STATEMENT an author would have written.
3
- *
4
- * A scan runs at build and the app runs elsewhere — two runtimes, no shared memory — so
5
- * something has to cross. What used to cross was the scan RESULT: every operation's
6
- * TypeScript signature re-serialized as JSON, beside the five CRUD ops `Crud.__ops`
7
- * declares at runtime anyway. A second writing of what the classes carry, and one that
8
- * drifts, since nothing compares the two.
9
- *
10
- * What crosses here is what `fronds.ts` holds: imports that bring the ORIGINAL classes
11
- * back, and `frond()` calls. Everything else `frond()` derives — `Post` is stored as
12
- * `post`, `PostHandler` answers at `post`, a computed field IS a method, and
13
- * `Presenter(Post)` keeps its subject. Only what TypeScript erases is named: a
14
- * constructor's parameter types (`deps`), and the surface a handler answers on, which the
15
- * scan read from its directory.
16
- *
17
- * Measured on demos/nuxt-blog: 102 lines of scan against 31 of statement, and the same app
18
- * — same rows, same computed fields, and no `typescript` loaded at boot.
19
- */
1
+ /** The scan, written as the STATEMENT an author would have written. */
20
2
  import { relative } from 'node:path';
21
- import type { FrondDescriptor, ScanResult } from './frond.js';
3
+ import type { FrondDescriptor } from '../descriptor/frond.js';
4
+ import type { ScanResult } from './result.js';
22
5
 
23
6
  type Live = { name?: string };
24
7
 
25
8
  /**
26
- * A file becomes the specifier the PROJECT already uses for it: `@fronds/blog/…`, the
27
- * import scope a handler names its neighbour by, and which the Nuxt module registers as an
28
- * alias for every frond it found.
29
- *
30
- * A relative path would name the same file by a second route, and two routes to one module
31
- * are two modules — so `Post` would not equal `Post` and every identity check would fail
32
- * quietly. It also puts the file outside what the alias resolves, which is what made Node,
33
- * not the bundler, read the frond's `.ts` and answer 500 on `../entities/Post.js`.
34
- *
35
- * `.js`, because the source is `.ts` and the project spells a TypeScript source that way
36
- * everywhere else — the alias resolves through the same rule as a hand-written import.
9
+ * A file becomes the specifier the PROJECT already uses for it: `@fronds/blog/…`, the import scope
10
+ * a handler names its neighbour by, and which the Nuxt module registers as an alias for every
11
+ * frond it found.
37
12
  */
38
13
  function specifierOf(filePath: string, frond: FrondDescriptor): string {
39
14
  const inside = relative(frond.source.path, filePath).replace(/\.tsx?$/, '.js');
package/src/source.ts ADDED
@@ -0,0 +1,44 @@
1
+ import { Registry, type SchemaView } from '@fougere/schema';
2
+ import type { StorageFactory } from './storage.js';
3
+
4
+ /** A place rows live, whatever realizes it. */
5
+ export interface Source {
6
+ storageFactory: StorageFactory;
7
+ /** Bring the shape of what lives here up to date. */
8
+ migrate?(view: SourceView): Promise<void>;
9
+ /** Run `fn` as ONE unit of work, with a factory bound to it. */
10
+ transacted?<R>(fn: (factory: StorageFactory) => Promise<R>): Promise<R>;
11
+ close?(): Promise<void>;
12
+ /** What distinguishes it when a query is reported. */
13
+ name?: string;
14
+ }
15
+
16
+ /** The app as ONE source sees it. */
17
+ export interface SourceView {
18
+ fronds: readonly { readonly name: string; readonly entities: readonly { readonly name: string }[] }[];
19
+ /** The auth provider's own entities, when they ride with this source. */
20
+ auth?: unknown;
21
+ elsewhere: readonly string[];
22
+ }
23
+
24
+ /** What a config file can carry about a source — values, never a live driver. */
25
+ export interface SourceConfig {
26
+ /** The adapter that realizes it. Absent means the conventional one. */
27
+ source?: string;
28
+ /** The entities whose rows live here. Absent on the default source: it holds the rest. */
29
+ entities?: string[];
30
+ [key: string]: unknown;
31
+ }
32
+
33
+ /** Which adapter answers a source name — one per process, no subject to hold. */
34
+ class SourceRegistry extends Registry<(conf: SourceConfig) => Source> {
35
+ /** The source this name stands for, built from the config entry that named it. */
36
+ open(name: string, conf: SourceConfig, path?: string): Source {
37
+ return this.resolve(name, path)(conf);
38
+ }
39
+ }
40
+
41
+ export const Sources = new SourceRegistry(
42
+ 'source',
43
+ 'import the adapter that answers it, or call Sources.register(name, build)',
44
+ );
package/src/storage.ts ADDED
@@ -0,0 +1,141 @@
1
+ import { upperFirst, lowerFirst, type SchemaView } from '@fougere/schema';
2
+
3
+ /** Options for list queries — pagination, sorting, counting. */
4
+ export interface ListOptions {
5
+ /** Number of records to return. */
6
+ limit?: number;
7
+
8
+ /** Offset-based: skip N records. */
9
+ offset?: number;
10
+
11
+ /** Page-based: 1-indexed page number (requires limit). */
12
+ page?: number;
13
+
14
+ /** Cursor-based: fetch records after this ID. */
15
+ after?: string;
16
+
17
+ /** Field name to order by. */
18
+ orderBy?: string;
19
+
20
+ /** Sort direction (default: 'asc'). */
21
+ order?: 'asc' | 'desc';
22
+
23
+ /** If true, also returns total count (for pagination UIs). */
24
+ count?: boolean;
25
+
26
+ /**
27
+ * Equality criteria, field by field — `{ orderId: '…' }`. Named rather than spread across the
28
+ * options so an unknown key stays ignored instead of silently becoming a filter.
29
+ */
30
+ where?: Record<string, unknown>;
31
+ }
32
+
33
+ /** Result of list() — extends Array so it's backward compatible. */
34
+ export interface ListResult<T> extends Array<T> {
35
+ /** Total number of matching records (only set when count: true). */
36
+ total?: number;
37
+ /** Cursor of the last item (for cursor-based pagination). */
38
+ endCursor?: string;
39
+ /** Whether more records exist after endCursor. */
40
+ hasMore?: boolean;
41
+ }
42
+
43
+ /** The keys `list()` answers to. */
44
+ const LIST_OPTION_KEYS = [
45
+ 'limit', 'offset', 'page', 'after', 'orderBy', 'order', 'count', 'where', 'select',
46
+ ] as const;
47
+
48
+ /** Refuse an option the port does not answer to, naming it and what was expected. */
49
+ export function assertListOptions(options: object | undefined, entity: string): void {
50
+ if (!options) return;
51
+ const legal = new Set<string>(LIST_OPTION_KEYS);
52
+ const strangers = Object.keys(options).filter((key) => !legal.has(key));
53
+ if (strangers.length === 0) return;
54
+ throw new Error(
55
+ `${entity}.list(): unknown option ${strangers.map((s) => `\`${s}\``).join(', ')}. ` +
56
+ `Known options are ${LIST_OPTION_KEYS.join(', ')} — to filter, pass \`where: { ${strangers[0]}: … }\`.`,
57
+ );
58
+ }
59
+
60
+ /** Select option — restrict returned fields to those of a SchemaView. */
61
+ export interface SelectOption {
62
+ select?: SchemaView;
63
+ }
64
+
65
+ /** Per-entity storage — scoped CRUD operations on a single entity type. */
66
+ export interface Storage<T = Record<string, unknown>> {
67
+ list(options?: ListOptions & SelectOption): Promise<ListResult<T>>;
68
+ findById(id: string, options?: SelectOption): Promise<T | undefined>;
69
+ /**
70
+ * Read by criteria — `findBy({ email })` for the one, `findAllBy({ orderId })` for the many,
71
+ * which is what a one-to-many relation *is*.
72
+ */
73
+ findBy(criteria: Partial<T> | Record<string, unknown>, options?: SelectOption): Promise<T | undefined>;
74
+ findAllBy(criteria: Partial<T> | Record<string, unknown>, options?: SelectOption): Promise<T[]>;
75
+ /**
76
+ * Read a SET of rows by their key, in one go — the gesture every other one was being bent into.
77
+ */
78
+ findByKeys(ids: readonly string[], options?: SelectOption): Promise<Map<string, T>>;
79
+ /** The dual of `findByKeys`: many rows per key, not one. */
80
+ findAllByKeys(field: string, keys: readonly string[], options?: SelectOption): Promise<Map<string, T[]>>;
81
+ create(input: Partial<T>, options?: SelectOption): Promise<T>;
82
+ /** Write the row, or make the existing one look like this — one statement. */
83
+ upsert(input: Partial<T>, options?: SelectOption): Promise<T>;
84
+ /** A whole page in one statement — what an import writes through. */
85
+ upsertAll(inputs: readonly Partial<T>[], options?: SelectOption): Promise<number>;
86
+ update(id: string, input: Partial<T>, options?: SelectOption): Promise<T>;
87
+ delete(id: string): Promise<boolean>;
88
+ /** Returns a scoped storage that restricts all read results to the fields of the given schema. */
89
+ output(schema: SchemaView): Storage<T>;
90
+ /** What this storage wraps — the Kysely instance for the SQL one, something else elsewhere. */
91
+ readonly client: unknown;
92
+ }
93
+
94
+ /**
95
+ * Factory that creates a Storage for a given entity.
96
+ * Called by bootstrap for every scanned entity.
97
+ */
98
+ export type StorageFactory = (entity: SchemaView, name: string) => Storage;
99
+
100
+ /** Container key of an entity's storage — 'reading' → 'ReadingStorage'. */
101
+ export function storageKeyOf(entity: string): string {
102
+ return `${upperFirst(entity)}${HELD}`;
103
+ }
104
+
105
+ /** The entity behind a storage key, or `undefined` when the key is not one. */
106
+ export function entityOfStorageKey(key: string, known: (entity: string) => boolean): string | undefined {
107
+ if (key.length <= HELD.length || !key.endsWith(HELD)) return undefined;
108
+ const entity = lowerFirst(key.slice(0, -HELD.length));
109
+
110
+ return storageKeyOf(entity) === key && known(entity) ? entity : undefined;
111
+ }
112
+
113
+ /** What a holder keeps, said in the key it is registered under. */
114
+ const HELD = 'Storage';
115
+
116
+ /** `Together<[Account, Ledger]>` — writes that stand or fall as one. */
117
+ export interface Together<E extends readonly unknown[], P extends readonly unknown[] = []> {
118
+ run<R>(fn: (entities: { [K in keyof E]: Storage<E[K]> }, providers: P) => Promise<R>): Promise<R>;
119
+ }
120
+
121
+ /**
122
+ * The container key of a frame — `[['Account', 'Ledger'], ['RateMirror']]` →
123
+ * 'Account+Ledger|RateMirrorTogether'.
124
+ */
125
+ export function togetherKeyOf(entities: readonly string[], providers: readonly string[] = []): string {
126
+ const named = entities.map(upperFirst).join(SEPARATOR);
127
+ return `${named}${providers.length ? KINDS + providers.map(upperFirst).join(SEPARATOR) : ''}${FRAME}`;
128
+ }
129
+
130
+ const FRAME = 'Together';
131
+ const SEPARATOR = '+';
132
+ /** Separates the two lists — what the unwind covers, and what is rebuilt to make it true. */
133
+ const KINDS = '|';
134
+
135
+ /** The members behind a frame key, or `undefined` when the key is not one. */
136
+ export function membersOfTogetherKey(key: string): { entities: string[]; providers: string[] } | undefined {
137
+ if (key.length <= FRAME.length || !key.endsWith(FRAME)) return undefined;
138
+ const [entities, providers = ''] = key.slice(0, -FRAME.length).split(KINDS);
139
+ const split = (list: string) => list.split(SEPARATOR).filter(Boolean);
140
+ return { entities: split(entities!), providers: split(providers) };
141
+ }