@fougere/core 0.2.0-alpha.2 → 0.4.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (500) hide show
  1. package/README.md +3 -2
  2. package/dist/boot/AppLifecycle.d.ts +18 -0
  3. package/dist/boot/AppLifecycle.d.ts.map +1 -0
  4. package/dist/boot/AppLifecycle.js +42 -0
  5. package/dist/boot/AppLifecycle.js.map +1 -0
  6. package/dist/boot/Emissions.d.ts +126 -0
  7. package/dist/boot/Emissions.d.ts.map +1 -0
  8. package/dist/boot/Emissions.js +233 -0
  9. package/dist/boot/Emissions.js.map +1 -0
  10. package/dist/boot/HandlerFacade.d.ts +72 -0
  11. package/dist/boot/HandlerFacade.d.ts.map +1 -0
  12. package/dist/boot/HandlerFacade.js +174 -0
  13. package/dist/boot/HandlerFacade.js.map +1 -0
  14. package/dist/boot/Lifecycle.d.ts +4 -0
  15. package/dist/boot/Lifecycle.d.ts.map +1 -0
  16. package/dist/boot/Lifecycle.js +3 -0
  17. package/dist/boot/Lifecycle.js.map +1 -0
  18. package/dist/boot/ambient-port.d.ts +38 -0
  19. package/dist/boot/ambient-port.d.ts.map +1 -0
  20. package/dist/boot/ambient-port.js +2 -0
  21. package/dist/boot/ambient-port.js.map +1 -0
  22. package/dist/boot/ambient.als.d.ts +3 -0
  23. package/dist/boot/ambient.als.d.ts.map +1 -0
  24. package/dist/boot/ambient.als.js +34 -0
  25. package/dist/boot/ambient.als.js.map +1 -0
  26. package/dist/boot/ambient.queue.d.ts +10 -0
  27. package/dist/boot/ambient.queue.d.ts.map +1 -0
  28. package/dist/boot/ambient.queue.js +60 -0
  29. package/dist/boot/ambient.queue.js.map +1 -0
  30. package/dist/boot/apply.d.ts +22 -0
  31. package/dist/boot/apply.d.ts.map +1 -0
  32. package/dist/boot/apply.js +38 -0
  33. package/dist/boot/apply.js.map +1 -0
  34. package/dist/boot/auth.d.ts +53 -0
  35. package/dist/boot/auth.d.ts.map +1 -0
  36. package/dist/boot/auth.js +2 -0
  37. package/dist/boot/auth.js.map +1 -0
  38. package/dist/{binding.d.ts → boot/binding.d.ts} +14 -17
  39. package/dist/boot/binding.d.ts.map +1 -0
  40. package/dist/{binding.js → boot/binding.js} +19 -10
  41. package/dist/boot/binding.js.map +1 -0
  42. package/dist/{boot.d.ts → boot/boot.d.ts} +25 -5
  43. package/dist/boot/boot.d.ts.map +1 -0
  44. package/dist/boot/boot.js +70 -0
  45. package/dist/boot/boot.js.map +1 -0
  46. package/dist/boot/bootstrap.d.ts.map +1 -0
  47. package/dist/boot/bootstrap.js +722 -0
  48. package/dist/boot/bootstrap.js.map +1 -0
  49. package/dist/boot/frame.d.ts +44 -0
  50. package/dist/boot/frame.d.ts.map +1 -0
  51. package/dist/boot/frame.js +172 -0
  52. package/dist/boot/frame.js.map +1 -0
  53. package/dist/boot/ownership.d.ts +42 -0
  54. package/dist/boot/ownership.d.ts.map +1 -0
  55. package/dist/boot/ownership.js +127 -0
  56. package/dist/boot/ownership.js.map +1 -0
  57. package/dist/boot/ports.d.ts +17 -0
  58. package/dist/boot/ports.d.ts.map +1 -0
  59. package/dist/boot/ports.js +57 -0
  60. package/dist/boot/ports.js.map +1 -0
  61. package/dist/{remote.d.ts → boot/remote.d.ts} +11 -3
  62. package/dist/boot/remote.d.ts.map +1 -0
  63. package/dist/{remote.js → boot/remote.js} +26 -28
  64. package/dist/boot/remote.js.map +1 -0
  65. package/dist/{seed.d.ts → boot/seed.d.ts} +13 -1
  66. package/dist/boot/seed.d.ts.map +1 -0
  67. package/dist/{seed.js → boot/seed.js} +24 -3
  68. package/dist/boot/seed.js.map +1 -0
  69. package/dist/boot/together.d.ts +26 -0
  70. package/dist/boot/together.d.ts.map +1 -0
  71. package/dist/boot/together.js +163 -0
  72. package/dist/boot/together.js.map +1 -0
  73. package/dist/boot/types.d.ts +260 -0
  74. package/dist/boot/types.d.ts.map +1 -0
  75. package/dist/boot/types.js.map +1 -0
  76. package/dist/builtins/config.d.ts +9 -0
  77. package/dist/builtins/config.d.ts.map +1 -1
  78. package/dist/builtins/config.js +16 -1
  79. package/dist/builtins/config.js.map +1 -1
  80. package/dist/builtins/logger.d.ts +37 -4
  81. package/dist/builtins/logger.d.ts.map +1 -1
  82. package/dist/builtins/logger.js +66 -7
  83. package/dist/builtins/logger.js.map +1 -1
  84. package/dist/config-loader.d.ts +39 -35
  85. package/dist/config-loader.d.ts.map +1 -1
  86. package/dist/config-loader.js +15 -54
  87. package/dist/config-loader.js.map +1 -1
  88. package/dist/contract/Call.d.ts +10 -0
  89. package/dist/contract/Call.d.ts.map +1 -0
  90. package/dist/contract/Call.js +15 -0
  91. package/dist/contract/Call.js.map +1 -0
  92. package/dist/contract/Invocation.d.ts +27 -0
  93. package/dist/contract/Invocation.d.ts.map +1 -0
  94. package/dist/contract/Invocation.js +59 -0
  95. package/dist/contract/Invocation.js.map +1 -0
  96. package/dist/contract/RouteAddress.d.ts +17 -0
  97. package/dist/contract/RouteAddress.d.ts.map +1 -0
  98. package/dist/contract/RouteAddress.js +38 -0
  99. package/dist/contract/RouteAddress.js.map +1 -0
  100. package/dist/contract.d.ts +25 -19
  101. package/dist/contract.d.ts.map +1 -1
  102. package/dist/contract.js +25 -10
  103. package/dist/contract.js.map +1 -1
  104. package/dist/crypto/encoding.d.ts +25 -0
  105. package/dist/crypto/encoding.d.ts.map +1 -0
  106. package/dist/crypto/encoding.js +58 -0
  107. package/dist/crypto/encoding.js.map +1 -0
  108. package/dist/crypto/node.d.ts +4 -0
  109. package/dist/crypto/node.d.ts.map +1 -0
  110. package/dist/crypto/node.js +25 -0
  111. package/dist/crypto/node.js.map +1 -0
  112. package/dist/crypto/port.d.ts +42 -0
  113. package/dist/crypto/port.d.ts.map +1 -0
  114. package/dist/crypto/port.js +19 -0
  115. package/dist/crypto/port.js.map +1 -0
  116. package/dist/crypto/webcrypto.d.ts +4 -0
  117. package/dist/crypto/webcrypto.d.ts.map +1 -0
  118. package/dist/crypto/webcrypto.js +34 -0
  119. package/dist/crypto/webcrypto.js.map +1 -0
  120. package/dist/dispatch/ArgumentResolver.d.ts +10 -0
  121. package/dist/dispatch/ArgumentResolver.d.ts.map +1 -0
  122. package/dist/dispatch/ArgumentResolver.js +12 -0
  123. package/dist/dispatch/ArgumentResolver.js.map +1 -0
  124. package/dist/dispatch/ArrayResult.d.ts +3 -0
  125. package/dist/dispatch/ArrayResult.d.ts.map +1 -0
  126. package/dist/dispatch/ArrayResult.js +11 -0
  127. package/dist/dispatch/ArrayResult.js.map +1 -0
  128. package/dist/dispatch/DispatchEvent.d.ts +18 -0
  129. package/dist/dispatch/DispatchEvent.d.ts.map +1 -0
  130. package/dist/dispatch/DispatchEvent.js +30 -0
  131. package/dist/dispatch/DispatchEvent.js.map +1 -0
  132. package/dist/dispatch/DispatchLifecycle.d.ts +9 -0
  133. package/dist/dispatch/DispatchLifecycle.d.ts.map +1 -0
  134. package/dist/dispatch/DispatchLifecycle.js +25 -0
  135. package/dist/dispatch/DispatchLifecycle.js.map +1 -0
  136. package/dist/dispatch/DispatchPort.d.ts +6 -0
  137. package/dist/dispatch/DispatchPort.d.ts.map +1 -0
  138. package/dist/dispatch/DispatchPort.js +2 -0
  139. package/dist/dispatch/DispatchPort.js.map +1 -0
  140. package/dist/dispatch/Dispatcher.d.ts +16 -0
  141. package/dist/dispatch/Dispatcher.d.ts.map +1 -0
  142. package/dist/dispatch/Dispatcher.js +44 -0
  143. package/dist/dispatch/Dispatcher.js.map +1 -0
  144. package/dist/dispatch/InFlight.d.ts +12 -0
  145. package/dist/dispatch/InFlight.d.ts.map +1 -0
  146. package/dist/dispatch/InFlight.js +46 -0
  147. package/dist/dispatch/InFlight.js.map +1 -0
  148. package/dist/dispatch/InputValidator.d.ts +7 -0
  149. package/dist/dispatch/InputValidator.d.ts.map +1 -0
  150. package/dist/dispatch/InputValidator.js +22 -0
  151. package/dist/dispatch/InputValidator.js.map +1 -0
  152. package/dist/dispatch/LocalRoutePolicy.d.ts +11 -0
  153. package/dist/dispatch/LocalRoutePolicy.d.ts.map +1 -0
  154. package/dist/dispatch/LocalRoutePolicy.js +32 -0
  155. package/dist/dispatch/LocalRoutePolicy.js.map +1 -0
  156. package/dist/dispatch/OperationExecutor.d.ts +25 -0
  157. package/dist/dispatch/OperationExecutor.d.ts.map +1 -0
  158. package/dist/dispatch/OperationExecutor.js +32 -0
  159. package/dist/dispatch/OperationExecutor.js.map +1 -0
  160. package/dist/dispatch/OperationRoute.d.ts +13 -0
  161. package/dist/dispatch/OperationRoute.d.ts.map +1 -0
  162. package/dist/dispatch/OperationRoute.js +15 -0
  163. package/dist/dispatch/OperationRoute.js.map +1 -0
  164. package/dist/dispatch/OutputProjector.d.ts +12 -0
  165. package/dist/dispatch/OutputProjector.d.ts.map +1 -0
  166. package/dist/dispatch/OutputProjector.js +30 -0
  167. package/dist/dispatch/OutputProjector.js.map +1 -0
  168. package/dist/dispatch/OutputView.d.ts +8 -0
  169. package/dist/dispatch/OutputView.d.ts.map +1 -0
  170. package/dist/dispatch/OutputView.js +10 -0
  171. package/dist/dispatch/OutputView.js.map +1 -0
  172. package/dist/dispatch/PresenterArgumentResolver.d.ts +12 -0
  173. package/dist/dispatch/PresenterArgumentResolver.d.ts.map +1 -0
  174. package/dist/dispatch/PresenterArgumentResolver.js +20 -0
  175. package/dist/dispatch/PresenterArgumentResolver.js.map +1 -0
  176. package/dist/dispatch/PresenterExecutor.d.ts +11 -0
  177. package/dist/dispatch/PresenterExecutor.d.ts.map +1 -0
  178. package/dist/dispatch/PresenterExecutor.js +56 -0
  179. package/dist/dispatch/PresenterExecutor.js.map +1 -0
  180. package/dist/dispatch/RemoteRouteResolver.d.ts +13 -0
  181. package/dist/dispatch/RemoteRouteResolver.d.ts.map +1 -0
  182. package/dist/dispatch/RemoteRouteResolver.js +18 -0
  183. package/dist/dispatch/RemoteRouteResolver.js.map +1 -0
  184. package/dist/dispatch/Route.d.ts +10 -0
  185. package/dist/dispatch/Route.d.ts.map +1 -0
  186. package/dist/dispatch/Route.js +2 -0
  187. package/dist/dispatch/Route.js.map +1 -0
  188. package/dist/dispatch/RouteNotFoundError.d.ts +10 -0
  189. package/dist/dispatch/RouteNotFoundError.d.ts.map +1 -0
  190. package/dist/dispatch/RouteNotFoundError.js +24 -0
  191. package/dist/dispatch/RouteNotFoundError.js.map +1 -0
  192. package/dist/dispatch/RoutePolicy.d.ts +8 -0
  193. package/dist/dispatch/RoutePolicy.d.ts.map +1 -0
  194. package/dist/dispatch/RoutePolicy.js +2 -0
  195. package/dist/dispatch/RoutePolicy.js.map +1 -0
  196. package/dist/dispatch/RouteRegistry.d.ts +18 -0
  197. package/dist/dispatch/RouteRegistry.d.ts.map +1 -0
  198. package/dist/dispatch/RouteRegistry.js +62 -0
  199. package/dist/dispatch/RouteRegistry.js.map +1 -0
  200. package/dist/dispatch/RouteResolver.d.ts +7 -0
  201. package/dist/dispatch/RouteResolver.d.ts.map +1 -0
  202. package/dist/dispatch/RouteResolver.js +2 -0
  203. package/dist/dispatch/RouteResolver.js.map +1 -0
  204. package/dist/dispatch/StorageGuard.d.ts +15 -0
  205. package/dist/dispatch/StorageGuard.d.ts.map +1 -0
  206. package/dist/dispatch/StorageGuard.js +63 -0
  207. package/dist/dispatch/StorageGuard.js.map +1 -0
  208. package/dist/effective-operation.d.ts +90 -0
  209. package/dist/effective-operation.d.ts.map +1 -0
  210. package/dist/effective-operation.js +449 -0
  211. package/dist/effective-operation.js.map +1 -0
  212. package/dist/emit.d.ts +2 -2
  213. package/dist/emit.d.ts.map +1 -1
  214. package/dist/emit.js +2 -2
  215. package/dist/emit.js.map +1 -1
  216. package/dist/entry/DynamicFacade.d.ts +10 -0
  217. package/dist/entry/DynamicFacade.d.ts.map +1 -0
  218. package/dist/entry/DynamicFacade.js +27 -0
  219. package/dist/entry/DynamicFacade.js.map +1 -0
  220. package/dist/entry/FacadeEntry.d.ts +11 -0
  221. package/dist/entry/FacadeEntry.d.ts.map +1 -0
  222. package/dist/entry/FacadeEntry.js +26 -0
  223. package/dist/entry/FacadeEntry.js.map +1 -0
  224. package/dist/entry/TransportEntry.d.ts +13 -0
  225. package/dist/entry/TransportEntry.d.ts.map +1 -0
  226. package/dist/entry/TransportEntry.js +24 -0
  227. package/dist/entry/TransportEntry.js.map +1 -0
  228. package/dist/frond-config.d.ts +24 -7
  229. package/dist/frond-config.d.ts.map +1 -1
  230. package/dist/frond-config.js.map +1 -1
  231. package/dist/graph.d.ts +1 -1
  232. package/dist/graph.d.ts.map +1 -1
  233. package/dist/graph.js +3 -2
  234. package/dist/graph.js.map +1 -1
  235. package/dist/identity-keys.d.ts +14 -0
  236. package/dist/identity-keys.d.ts.map +1 -0
  237. package/dist/identity-keys.js +37 -0
  238. package/dist/identity-keys.js.map +1 -0
  239. package/dist/identity.d.ts +58 -0
  240. package/dist/identity.d.ts.map +1 -0
  241. package/dist/identity.js +182 -0
  242. package/dist/identity.js.map +1 -0
  243. package/dist/imports.d.ts +2 -2
  244. package/dist/imports.d.ts.map +1 -1
  245. package/dist/imports.js +7 -3
  246. package/dist/imports.js.map +1 -1
  247. package/dist/index.d.ts +57 -38
  248. package/dist/index.d.ts.map +1 -1
  249. package/dist/index.js +38 -32
  250. package/dist/index.js.map +1 -1
  251. package/dist/loader.d.ts +16 -0
  252. package/dist/loader.d.ts.map +1 -0
  253. package/dist/loader.js +25 -0
  254. package/dist/loader.js.map +1 -0
  255. package/dist/node.d.ts +21 -0
  256. package/dist/node.d.ts.map +1 -0
  257. package/dist/node.js +24 -0
  258. package/dist/node.js.map +1 -0
  259. package/dist/orm.d.ts +69 -8
  260. package/dist/orm.d.ts.map +1 -1
  261. package/dist/orm.js +35 -2
  262. package/dist/orm.js.map +1 -1
  263. package/dist/prefab/collector.d.ts +29 -0
  264. package/dist/prefab/collector.d.ts.map +1 -0
  265. package/dist/prefab/collector.js +34 -0
  266. package/dist/prefab/collector.js.map +1 -0
  267. package/dist/{crud.d.ts → prefab/crud.d.ts} +7 -8
  268. package/dist/prefab/crud.d.ts.map +1 -0
  269. package/dist/{crud.js → prefab/crud.js} +39 -5
  270. package/dist/prefab/crud.js.map +1 -0
  271. package/dist/prefab/mirror.d.ts +52 -0
  272. package/dist/prefab/mirror.d.ts.map +1 -0
  273. package/dist/prefab/mirror.js +66 -0
  274. package/dist/prefab/mirror.js.map +1 -0
  275. package/dist/prefab/prefab.d.ts +15 -0
  276. package/dist/prefab/prefab.d.ts.map +1 -0
  277. package/dist/prefab/prefab.js +19 -0
  278. package/dist/prefab/prefab.js.map +1 -0
  279. package/dist/{presenter.d.ts → prefab/presenter.d.ts} +6 -17
  280. package/dist/prefab/presenter.d.ts.map +1 -0
  281. package/dist/{presenter.js → prefab/presenter.js} +5 -23
  282. package/dist/prefab/presenter.js.map +1 -0
  283. package/dist/prefab/repository.d.ts +104 -0
  284. package/dist/prefab/repository.d.ts.map +1 -0
  285. package/dist/prefab/repository.js +62 -0
  286. package/dist/prefab/repository.js.map +1 -0
  287. package/dist/scan/Fronds.d.ts +45 -0
  288. package/dist/scan/Fronds.d.ts.map +1 -0
  289. package/dist/scan/Fronds.js +66 -0
  290. package/dist/scan/Fronds.js.map +1 -0
  291. package/dist/scan/conventions.d.ts +74 -0
  292. package/dist/scan/conventions.d.ts.map +1 -0
  293. package/dist/scan/conventions.js +82 -0
  294. package/dist/scan/conventions.js.map +1 -0
  295. package/dist/scan/emit.d.ts +14 -0
  296. package/dist/scan/emit.d.ts.map +1 -0
  297. package/dist/scan/emit.js +212 -0
  298. package/dist/scan/emit.js.map +1 -0
  299. package/dist/scan/frond.d.ts +224 -0
  300. package/dist/scan/frond.d.ts.map +1 -0
  301. package/dist/scan/frond.js +2 -0
  302. package/dist/scan/frond.js.map +1 -0
  303. package/dist/{handler-parser.d.ts → scan/handler-parser.d.ts} +20 -9
  304. package/dist/scan/handler-parser.d.ts.map +1 -0
  305. package/dist/scan/handler-parser.js +665 -0
  306. package/dist/scan/handler-parser.js.map +1 -0
  307. package/dist/scan/scan-cache.d.ts +16 -0
  308. package/dist/scan/scan-cache.d.ts.map +1 -0
  309. package/dist/{scan-cache.js → scan/scan-cache.js} +27 -20
  310. package/dist/scan/scan-cache.js.map +1 -0
  311. package/dist/scan/scanner.d.ts +20 -0
  312. package/dist/scan/scanner.d.ts.map +1 -0
  313. package/dist/{scanner.js → scan/scanner.js} +212 -167
  314. package/dist/scan/scanner.js.map +1 -0
  315. package/dist/verify.d.ts +16 -3
  316. package/dist/verify.d.ts.map +1 -1
  317. package/dist/verify.js +21 -18
  318. package/dist/verify.js.map +1 -1
  319. package/dist/{call.d.ts → wire/call.d.ts} +76 -7
  320. package/dist/wire/call.d.ts.map +1 -0
  321. package/dist/{call.js → wire/call.js} +53 -80
  322. package/dist/wire/call.js.map +1 -0
  323. package/dist/{middleware.d.ts → wire/errors.d.ts} +18 -19
  324. package/dist/wire/errors.d.ts.map +1 -0
  325. package/dist/{middleware.js → wire/errors.js} +26 -14
  326. package/dist/wire/errors.js.map +1 -0
  327. package/dist/{errors.d.ts → wire/http-error.d.ts} +5 -6
  328. package/dist/wire/http-error.d.ts.map +1 -0
  329. package/dist/{errors.js → wire/http-error.js} +7 -6
  330. package/dist/wire/http-error.js.map +1 -0
  331. package/dist/wire/invocation.d.ts +4 -0
  332. package/dist/wire/invocation.d.ts.map +1 -0
  333. package/dist/wire/invocation.js +3 -0
  334. package/dist/wire/invocation.js.map +1 -0
  335. package/dist/{middleware/logger.d.ts → wire/loggerMiddleware.d.ts} +3 -3
  336. package/dist/wire/loggerMiddleware.d.ts.map +1 -0
  337. package/dist/{middleware/logger.js → wire/loggerMiddleware.js} +2 -2
  338. package/dist/wire/loggerMiddleware.js.map +1 -0
  339. package/dist/wire/middleware.d.ts +29 -0
  340. package/dist/wire/middleware.d.ts.map +1 -0
  341. package/dist/wire/middleware.js +20 -0
  342. package/dist/wire/middleware.js.map +1 -0
  343. package/dist/{operation.d.ts → wire/operation.d.ts} +39 -10
  344. package/dist/wire/operation.d.ts.map +1 -0
  345. package/dist/wire/operation.js +167 -0
  346. package/dist/wire/operation.js.map +1 -0
  347. package/package.json +27 -8
  348. package/src/boot/AppLifecycle.ts +50 -0
  349. package/src/boot/Emissions.ts +263 -0
  350. package/src/boot/HandlerFacade.ts +242 -0
  351. package/src/boot/Lifecycle.ts +3 -0
  352. package/src/boot/ambient-port.ts +41 -0
  353. package/src/boot/ambient.als.ts +43 -0
  354. package/src/boot/ambient.queue.ts +75 -0
  355. package/src/boot/apply.ts +47 -0
  356. package/src/boot/auth.ts +55 -0
  357. package/src/boot/binding.ts +182 -0
  358. package/src/boot/boot.ts +125 -0
  359. package/src/boot/bootstrap.ts +839 -0
  360. package/src/boot/frame.ts +210 -0
  361. package/src/boot/ownership.ts +140 -0
  362. package/src/boot/ports.ts +66 -0
  363. package/src/boot/remote.ts +179 -0
  364. package/src/boot/seed.ts +158 -0
  365. package/src/boot/together.ts +215 -0
  366. package/src/boot/types.ts +261 -0
  367. package/src/builtins/config.ts +19 -0
  368. package/src/builtins/logger.ts +175 -0
  369. package/src/config-loader.ts +146 -0
  370. package/src/contract/Call.ts +18 -0
  371. package/src/contract/Invocation.ts +72 -0
  372. package/src/contract/RouteAddress.ts +49 -0
  373. package/src/contract.ts +74 -0
  374. package/src/crypto/encoding.ts +59 -0
  375. package/src/crypto/node.ts +30 -0
  376. package/src/crypto/port.ts +45 -0
  377. package/src/crypto/webcrypto.ts +55 -0
  378. package/src/define.ts +10 -0
  379. package/src/dispatch/ArgumentResolver.ts +17 -0
  380. package/src/dispatch/ArrayResult.ts +10 -0
  381. package/src/dispatch/DispatchEvent.ts +38 -0
  382. package/src/dispatch/DispatchLifecycle.ts +23 -0
  383. package/src/dispatch/DispatchPort.ts +6 -0
  384. package/src/dispatch/Dispatcher.ts +48 -0
  385. package/src/dispatch/InFlight.ts +49 -0
  386. package/src/dispatch/InputValidator.ts +28 -0
  387. package/src/dispatch/LocalRoutePolicy.ts +34 -0
  388. package/src/dispatch/OperationExecutor.ts +60 -0
  389. package/src/dispatch/OperationRoute.ts +18 -0
  390. package/src/dispatch/OutputProjector.ts +32 -0
  391. package/src/dispatch/OutputView.ts +9 -0
  392. package/src/dispatch/PresenterArgumentResolver.ts +25 -0
  393. package/src/dispatch/PresenterExecutor.ts +55 -0
  394. package/src/dispatch/RemoteRouteResolver.ts +21 -0
  395. package/src/dispatch/Route.ts +11 -0
  396. package/src/dispatch/RouteNotFoundError.ts +29 -0
  397. package/src/dispatch/RoutePolicy.ts +8 -0
  398. package/src/dispatch/RouteRegistry.ts +73 -0
  399. package/src/dispatch/RouteResolver.ts +7 -0
  400. package/src/dispatch/StorageGuard.ts +71 -0
  401. package/src/effective-operation.ts +597 -0
  402. package/src/emit.ts +90 -0
  403. package/src/entry/DynamicFacade.ts +28 -0
  404. package/src/entry/FacadeEntry.ts +32 -0
  405. package/src/entry/TransportEntry.ts +30 -0
  406. package/src/frond-config.ts +138 -0
  407. package/src/graph.ts +180 -0
  408. package/src/identity-keys.ts +43 -0
  409. package/src/identity.ts +235 -0
  410. package/src/imports.ts +123 -0
  411. package/src/index.ts +106 -0
  412. package/src/loader.ts +41 -0
  413. package/src/node.ts +28 -0
  414. package/src/orm.ts +252 -0
  415. package/src/prefab/collector.ts +36 -0
  416. package/src/prefab/crud.ts +188 -0
  417. package/src/prefab/mirror.ts +122 -0
  418. package/src/prefab/prefab.ts +46 -0
  419. package/src/prefab/presenter.ts +63 -0
  420. package/src/prefab/repository.ts +170 -0
  421. package/src/scan/Fronds.ts +73 -0
  422. package/src/scan/conventions.ts +122 -0
  423. package/src/scan/emit.ts +231 -0
  424. package/src/scan/frond.ts +235 -0
  425. package/src/scan/handler-parser.ts +818 -0
  426. package/src/scan/scan-cache.ts +115 -0
  427. package/src/scan/scanner.ts +758 -0
  428. package/src/verify.ts +193 -0
  429. package/src/wire/call.ts +369 -0
  430. package/src/wire/errors.ts +124 -0
  431. package/src/wire/http-error.ts +77 -0
  432. package/src/wire/invocation.ts +10 -0
  433. package/src/wire/loggerMiddleware.ts +29 -0
  434. package/src/wire/middleware.ts +47 -0
  435. package/src/wire/operation.ts +258 -0
  436. package/dist/binding.d.ts.map +0 -1
  437. package/dist/binding.js.map +0 -1
  438. package/dist/boot.d.ts.map +0 -1
  439. package/dist/boot.js +0 -53
  440. package/dist/boot.js.map +0 -1
  441. package/dist/bootstrap.d.ts.map +0 -1
  442. package/dist/bootstrap.js +0 -879
  443. package/dist/bootstrap.js.map +0 -1
  444. package/dist/call.d.ts.map +0 -1
  445. package/dist/call.js.map +0 -1
  446. package/dist/collector.d.ts +0 -28
  447. package/dist/collector.d.ts.map +0 -1
  448. package/dist/collector.js +0 -32
  449. package/dist/collector.js.map +0 -1
  450. package/dist/crud.d.ts.map +0 -1
  451. package/dist/crud.js.map +0 -1
  452. package/dist/egress.d.ts +0 -52
  453. package/dist/egress.d.ts.map +0 -1
  454. package/dist/egress.js +0 -186
  455. package/dist/egress.js.map +0 -1
  456. package/dist/errors.d.ts.map +0 -1
  457. package/dist/errors.js.map +0 -1
  458. package/dist/handler-parser.d.ts.map +0 -1
  459. package/dist/handler-parser.js +0 -505
  460. package/dist/handler-parser.js.map +0 -1
  461. package/dist/invocation.d.ts +0 -20
  462. package/dist/invocation.d.ts.map +0 -1
  463. package/dist/invocation.js +0 -15
  464. package/dist/invocation.js.map +0 -1
  465. package/dist/middleware/error-handler.d.ts +0 -15
  466. package/dist/middleware/error-handler.d.ts.map +0 -1
  467. package/dist/middleware/error-handler.js +0 -29
  468. package/dist/middleware/error-handler.js.map +0 -1
  469. package/dist/middleware/logger.d.ts.map +0 -1
  470. package/dist/middleware/logger.js.map +0 -1
  471. package/dist/middleware.d.ts.map +0 -1
  472. package/dist/middleware.js.map +0 -1
  473. package/dist/mirror.d.ts +0 -93
  474. package/dist/mirror.d.ts.map +0 -1
  475. package/dist/mirror.js +0 -96
  476. package/dist/mirror.js.map +0 -1
  477. package/dist/operation.d.ts.map +0 -1
  478. package/dist/operation.js +0 -95
  479. package/dist/operation.js.map +0 -1
  480. package/dist/presenter.d.ts.map +0 -1
  481. package/dist/presenter.js.map +0 -1
  482. package/dist/remote.d.ts.map +0 -1
  483. package/dist/remote.js.map +0 -1
  484. package/dist/repository.d.ts +0 -55
  485. package/dist/repository.d.ts.map +0 -1
  486. package/dist/repository.js +0 -26
  487. package/dist/repository.js.map +0 -1
  488. package/dist/scan-cache.d.ts +0 -10
  489. package/dist/scan-cache.d.ts.map +0 -1
  490. package/dist/scan-cache.js.map +0 -1
  491. package/dist/scanner.d.ts +0 -34
  492. package/dist/scanner.d.ts.map +0 -1
  493. package/dist/scanner.js.map +0 -1
  494. package/dist/seed.d.ts.map +0 -1
  495. package/dist/seed.js.map +0 -1
  496. package/dist/types.d.ts +0 -421
  497. package/dist/types.d.ts.map +0 -1
  498. package/dist/types.js.map +0 -1
  499. /package/dist/{bootstrap.d.ts → boot/bootstrap.d.ts} +0 -0
  500. /package/dist/{types.js → boot/types.js} +0 -0
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Fougere Logger — structured, colored, multi-runtime.
3
+ *
4
+ * Works on Node, Bun, Deno, Cloudflare Workers (console-based, no native bindings).
5
+ */
6
+
7
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
8
+
9
+ /**
10
+ * One line, before it was formatted for a terminal.
11
+ *
12
+ * The console form is already cooked — ANSI codes, a badge, a timestamp — so anything
13
+ * that wants to FORWARD a log needs it before that. What travels is the structure, and
14
+ * nothing here knows where it goes.
15
+ */
16
+ export interface LogRecord {
17
+ level: Exclude<LogLevel, 'silent'>;
18
+ /** The logger's own name — 'boot:app', 'boot:app:catalog'. */
19
+ name: string;
20
+ message: string;
21
+ args: unknown[];
22
+ /** Epoch milliseconds. */
23
+ at: number;
24
+ }
25
+
26
+ export type LogSink = (record: LogRecord) => void;
27
+
28
+ /**
29
+ * Who else takes this process's log lines, beside the console. A list, like the span
30
+ * sinks: a line is one fact, and forwarding it twice is the caller's business.
31
+ *
32
+ * Nothing is built when nobody listens — the record is only shaped if a sink exists.
33
+ */
34
+ const sinks: LogSink[] = [];
35
+
36
+ /** Take every line this process logs. Returns the way to withdraw. */
37
+ export function onLog(next: LogSink): () => void {
38
+ sinks.push(next);
39
+ return () => {
40
+ const at = sinks.indexOf(next);
41
+ if (at >= 0) sinks.splice(at, 1);
42
+ };
43
+ }
44
+
45
+ const LEVELS: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3, silent: 4 };
46
+
47
+ /**
48
+ * The level, held ONCE for the process and CONSULTED at every emission.
49
+ *
50
+ * It used to be copied into each Logger by the constructor, and copied again by
51
+ * `child()` — so there was no place where "the level" was, and changing it meant
52
+ * rebuilding every logger AND everything that had been handed one. A value read at
53
+ * the moment it is used costs the same and can move.
54
+ */
55
+ let threshold: number = LEVELS[envLevel() ?? 'info'];
56
+
57
+ /**
58
+ * The level the PROCESS was started with. Read here rather than only at `applyConfig`,
59
+ * which runs after the boot has already said where its root is — an operator asking for
60
+ * `warn` got that line anyway. Guarded: this module runs on Workers, where there is no
61
+ * `process`.
62
+ */
63
+ export function envLevel(): LogLevel | undefined {
64
+ const raw = typeof process === 'undefined' ? undefined : process.env.FOUGERE_LOG_LEVEL;
65
+ return raw !== undefined && raw in LEVELS ? (raw as LogLevel) : undefined;
66
+ }
67
+
68
+ /**
69
+ * Set the level for every logger in this process, at once.
70
+ *
71
+ * Refuses a level it does not know rather than storing `undefined` as the threshold:
72
+ * every comparison against it is then `NaN`, so nothing is filtered and `logLevel()`
73
+ * reports its own fallback instead of what is in force.
74
+ */
75
+ export function setLogLevel(level: LogLevel): void {
76
+ if (!(level in LEVELS)) {
77
+ throw new Error(`Unknown log level: '${level}'. One of ${Object.keys(LEVELS).join(', ')}.`);
78
+ }
79
+ threshold = LEVELS[level];
80
+ }
81
+
82
+ /** What the level is now — the dual, so a reload can report what it changed. */
83
+ export function logLevel(): LogLevel {
84
+ return (Object.keys(LEVELS) as LogLevel[]).find((l) => LEVELS[l] === threshold) ?? 'info';
85
+ }
86
+
87
+ const COLORS: Record<string, string> = {
88
+ reset: '\x1b[0m',
89
+ dim: '\x1b[2m',
90
+ bold: '\x1b[1m',
91
+ green: '\x1b[32m',
92
+ yellow: '\x1b[33m',
93
+ red: '\x1b[31m',
94
+ cyan: '\x1b[36m',
95
+ magenta: '\x1b[35m',
96
+ gray: '\x1b[90m',
97
+ };
98
+
99
+ const LEVEL_STYLE: Record<string, { badge: string; color: string }> = {
100
+ debug: { badge: 'DBG', color: 'gray' },
101
+ info: { badge: 'INF', color: 'green' },
102
+ warn: { badge: 'WRN', color: 'yellow' },
103
+ error: { badge: 'ERR', color: 'red' },
104
+ };
105
+
106
+ function supportsColor(): boolean {
107
+ if (typeof process !== 'undefined') {
108
+ if (process.env.NO_COLOR) return false;
109
+ if (process.env.FORCE_COLOR === '1') return true;
110
+ return process.stdout?.isTTY === true;
111
+ }
112
+ return false;
113
+ }
114
+
115
+ function formatTime(): string {
116
+ const d = new Date();
117
+ const h = String(d.getHours()).padStart(2, '0');
118
+ const m = String(d.getMinutes()).padStart(2, '0');
119
+ const s = String(d.getSeconds()).padStart(2, '0');
120
+ const ms = String(d.getMilliseconds()).padStart(3, '0');
121
+ return `${h}:${m}:${s}.${ms}`;
122
+ }
123
+
124
+ export interface LoggerOptions {
125
+ /** Logger name / prefix. */
126
+ name?: string;
127
+ /** Force color on/off. Auto-detected by default. */
128
+ color?: boolean;
129
+ }
130
+
131
+ export class Logger {
132
+ private name: string;
133
+ private color: boolean;
134
+
135
+ constructor(prefix?: string, options?: Omit<LoggerOptions, 'name'>) {
136
+ this.name = prefix ?? 'app';
137
+ this.color = options?.color ?? supportsColor();
138
+ }
139
+
140
+ /** Create a child logger with a sub-name. It carries no level of its own either. */
141
+ child(name: string): Logger {
142
+ return new Logger(`${this.name}:${name}`, { color: this.color });
143
+ }
144
+
145
+ debug(msg: string, ...args: unknown[]) { this.log('debug', msg, args); }
146
+ info(msg: string, ...args: unknown[]) { this.log('info', msg, args); }
147
+ warn(msg: string, ...args: unknown[]) { this.log('warn', msg, args); }
148
+ error(msg: string, ...args: unknown[]) { this.log('error', msg, args); }
149
+
150
+ private log(level: string, msg: string, args: unknown[]) {
151
+ if (LEVELS[level as LogLevel] < threshold) return;
152
+
153
+ // Beside the console, never instead of it: a forwarded line is an addition, and a
154
+ // sink that throws must not cost the operator the line they were reading.
155
+ for (const take of sinks) {
156
+ try {
157
+ take({ level: level as LogRecord['level'], name: this.name, message: msg, args, at: Date.now() });
158
+ } catch { /* forwarding never breaks logging */ }
159
+ }
160
+
161
+ const style = LEVEL_STYLE[level];
162
+ const time = formatTime();
163
+ // One console method per level. `debug` and `info` both went to `console.log`, so
164
+ // nothing downstream — a terminal filter, a collector — could tell them apart.
165
+ const method = level as 'debug' | 'info' | 'warn' | 'error';
166
+
167
+ if (this.color) {
168
+ const c = COLORS[style.color];
169
+ const prefix = `${COLORS.dim}${time}${COLORS.reset} ${c}${COLORS.bold}${style.badge}${COLORS.reset} ${COLORS.magenta}${this.name}${COLORS.reset}`;
170
+ console[method](prefix, msg, ...args);
171
+ } else {
172
+ console[method](`${time} ${style.badge} [${this.name}]`, msg, ...args);
173
+ }
174
+ }
175
+ }
@@ -0,0 +1,146 @@
1
+ import { resolve } from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+ import type { AuthConfig } from './boot/auth.js';
4
+ import type { LogLevel } from './builtins/logger.js';
5
+ import type { ConventionsInput } from './scan/conventions.js';
6
+ import { getModuleLoader } from './loader.js';
7
+
8
+ // ── Types ────────────────────────────────────────
9
+
10
+ export interface FougereConfig {
11
+ /** Database configuration — the DEFAULT source, the one an entity lands in unnamed. */
12
+ db?: 'sqlite' | { dialect: 'sqlite'; path?: string } | false;
13
+ /**
14
+ * The other places rows live — a name, an engine, and the entities it holds.
15
+ *
16
+ * A fact about the APP, stated beside `remotes:` because it is the same kind of
17
+ * statement: `remotes:` says where a CALL goes, this says where a ROW is. Neither
18
+ * belongs on the entity, which describes itself and not its deployment.
19
+ *
20
+ * Only the exception is declared: everything unnamed stays in `db`. Membership is
21
+ * per ENTITY rather than per frond — two fronds may share a database, and one frond
22
+ * may hold entities in two — so the entity is the truth and a frond-wide shorthand
23
+ * waits for a case that needs it.
24
+ *
25
+ * A `ref()` across two sources gets no foreign key: two databases share no
26
+ * constraint. The DDL stops pretending rather than the `ref` being refused.
27
+ */
28
+ sources?: Record<string, { dialect?: 'sqlite'; path?: string; entities: string[] }>;
29
+ /**
30
+ * The names the scan reads instead of deriving them — the import scope, the fronds
31
+ * directory, the seven convention directories.
32
+ *
33
+ * Only what differs is stated. Read BEFORE the fronds are discovered, which is what
34
+ * lets it name the prefix they are found under: nothing in this file may import
35
+ * `@fronds/*`, or the name would be needed to read the file that declares it.
36
+ */
37
+ conventions?: ConventionsInput;
38
+ /**
39
+ * How much every logger says. The one key a running app CONSULTS rather than
40
+ * consumes, so re-reading this file changes it without rebuilding anything.
41
+ *
42
+ * `FOUGERE_LOG_LEVEL` still wins — it is how the CLI speaks, and the precedence is
43
+ * the usual one (CLI over config file).
44
+ */
45
+ logLevel?: LogLevel;
46
+ /** Remote fronds — frondName → base URL. */
47
+ remotes?: Record<string, string>;
48
+ /**
49
+ * Which realization answers which port — port class name → implementation class name.
50
+ *
51
+ * A fact about the APP, stated beside `remotes:` for the same reason `sources:` is:
52
+ * `remotes:` says where a CALL goes, `sources:` says where a ROW is, this says who
53
+ * performs an ACTION. None of the three belongs inside the frond, which describes
54
+ * itself and not its deployment — a `billing` frond naming Stripe would be the one
55
+ * thing the framework refuses.
56
+ *
57
+ * Only the exception is declared. One class extending a port answers it by
58
+ * convention and this key stays absent; two make the boot refuse until one is named.
59
+ */
60
+ ports?: Record<string, string>;
61
+ /** Auth declaration — picks a provider package and forwards options to it. */
62
+ auth?: AuthConfig;
63
+ /**
64
+ * Which protocol adapters this app serves.
65
+ *
66
+ * A fact about the APP, declared once beside `db`, `remotes` and `auth` — not a
67
+ * property of whichever host runs it. Before this existed the same decision was
68
+ * spelled five different ways: Nuxt mounted REST unconditionally, the Web hosts
69
+ * served it if you happened to create a route file, and Express if you happened to
70
+ * call a middleware. One decision, five dialects, no canonical place.
71
+ *
72
+ * Absent means not served. The call envelope is not listed here: it is the wire the
73
+ * client primitives use, not a projection an app chooses to publish.
74
+ */
75
+ adapters?: AdapterConfig;
76
+ }
77
+
78
+ /** Protocol adapters, by the name of the package that provides them. */
79
+ export interface AdapterConfig {
80
+ /** `@fougere/adapter-rest` — REST under `/api/{frond}/{plural}`. */
81
+ rest?: boolean;
82
+ /** `@fougere/adapter-graphql` — a GraphQL schema over the same operations. */
83
+ graphql?: boolean;
84
+ /** A surface of your own; the framework only records that you declared it. */
85
+ [adapter: string]: boolean | undefined;
86
+ }
87
+
88
+ // ── Loading ──────────────────────────────────────
89
+
90
+ const CONFIG_FILES = ['fougere.config.ts', 'fougere.config.js', 'fougere.config.mjs'];
91
+
92
+ async function loadConfigFrom(dir: string, fresh?: boolean): Promise<FougereConfig> {
93
+ const loader = getModuleLoader();
94
+ for (const file of CONFIG_FILES) {
95
+ const path = resolve(dir, file);
96
+ if (existsSync(path)) {
97
+ // A module is cached by its specifier, so a second load of an EDITED file hands
98
+ // back what was read the first time — measured, and it made re-reading a config
99
+ // return the config already in force. The loader owns its own cache, so it is the
100
+ // one told; the old module stays in memory, re-reading being for a change.
101
+ const mod = await loader(path, fresh ? { fresh } : undefined);
102
+ return ((mod as { default?: FougereConfig }).default ?? mod) as FougereConfig;
103
+ }
104
+ }
105
+ return {};
106
+ }
107
+
108
+ /**
109
+ * Load the root fougere.config.{ts,js,mjs} from the given directory.
110
+ *
111
+ * `fresh` re-reads a file that may have changed since the last load — what a host
112
+ * calls when it decides to reconsider its config, and the only way `applyConfig` has
113
+ * anything new to apply.
114
+ */
115
+ export async function loadConfig(root: string, options?: { fresh?: boolean }): Promise<FougereConfig> {
116
+ return loadConfigFrom(root, options?.fresh);
117
+ }
118
+
119
+ // ── Merging ──────────────────────────────────────
120
+
121
+ /**
122
+ * Override a config with another, the invariant of every cascade level:
123
+ * scalar keys replace, but `remotes` (the topology) MERGES — an override adds
124
+ * or redirects a frond without erasing the others. Used for workspace→app and
125
+ * for global→CLI alike.
126
+ */
127
+ function mergeGlobal(base: FougereConfig, override: Partial<FougereConfig>): FougereConfig {
128
+ const merged: FougereConfig = { ...base, ...override };
129
+ if (base.remotes || override.remotes) {
130
+ merged.remotes = { ...base.remotes, ...override.remotes };
131
+ }
132
+ return merged;
133
+ }
134
+
135
+ /**
136
+ * Load config along the workspace→app frontier. The workspace-root config is
137
+ * the base (canonical topology: `remotes`, shared `db`); the app-root config
138
+ * overrides. Same `root` boundary the fronds already cascade along. When both
139
+ * roots resolve to the same dir (single app, no workspace), this is the plain
140
+ * root config — idempotent, no behavior change.
141
+ */
142
+ export async function loadCascadedConfig(workspaceRoot: string, appRoot: string): Promise<FougereConfig> {
143
+ const base = await loadConfig(workspaceRoot);
144
+ if (resolve(workspaceRoot) === resolve(appRoot)) return base;
145
+ return mergeGlobal(base, await loadConfig(appRoot));
146
+ }
@@ -0,0 +1,18 @@
1
+ import { Invocation, type InvocationInput } from './Invocation.js';
2
+ import type { RouteAddress } from './RouteAddress.js';
3
+
4
+ /** One normalized request: where it goes and what the caller supplied. */
5
+ export class Call {
6
+ readonly address: RouteAddress;
7
+ readonly invocation: Invocation;
8
+
9
+ constructor(address: RouteAddress, invocation?: InvocationInput) {
10
+ this.address = address;
11
+ this.invocation = Invocation.from(invocation);
12
+ Object.freeze(this);
13
+ }
14
+
15
+ withInvocation(invocation: Invocation): Call {
16
+ return new Call(this.address, invocation);
17
+ }
18
+ }
@@ -0,0 +1,72 @@
1
+ export interface InvocationContext {
2
+ params: Record<string, unknown>;
3
+ query: Record<string, unknown>;
4
+ body: unknown;
5
+ state: Record<string, unknown>;
6
+ trace?: string;
7
+ identity?: string;
8
+ caller?: string;
9
+ }
10
+
11
+ export type InvocationInput = Partial<InvocationContext>;
12
+
13
+ function canonicalValue(value: unknown): unknown {
14
+ if (value === null || value === undefined || typeof value !== 'object') return value;
15
+ if (Array.isArray(value)) return Object.freeze(value.map(canonicalValue));
16
+
17
+ const prototype = Object.getPrototypeOf(value);
18
+ if (prototype !== Object.prototype && prototype !== null) return value;
19
+ return canonicalRecord(value);
20
+ }
21
+
22
+ function canonicalRecord(value: unknown): Record<string, unknown> {
23
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return Object.freeze({});
24
+
25
+ const result: Record<string, unknown> = {};
26
+ for (const [key, member] of Object.entries(value)) {
27
+ if (member !== undefined) result[key] = canonicalValue(member);
28
+ }
29
+ return Object.freeze(result);
30
+ }
31
+
32
+ /** Canonical invocation shared by every entry and transport. */
33
+ export class Invocation implements InvocationContext {
34
+ readonly params: Record<string, unknown>;
35
+ readonly query: Record<string, unknown>;
36
+ readonly body: unknown;
37
+ readonly state: Record<string, unknown>;
38
+ readonly trace?: string;
39
+ readonly identity?: string;
40
+ readonly caller?: string;
41
+
42
+ private constructor(input: InvocationInput) {
43
+ this.params = canonicalRecord(input.params);
44
+ this.query = canonicalRecord(input.query);
45
+ this.body = canonicalValue(input.body);
46
+ // State is host-owned and may still be enriched by existing middlewares. Moving it
47
+ // to immutable lifecycle context is a separate migration.
48
+ this.state = input.state ?? {};
49
+ if (input.trace !== undefined) this.trace = input.trace;
50
+ if (input.identity !== undefined) this.identity = input.identity;
51
+ if (input.caller !== undefined) this.caller = input.caller;
52
+ Object.freeze(this);
53
+ }
54
+
55
+ static from(input?: InvocationInput): Invocation {
56
+ return input instanceof Invocation ? input : new Invocation(input ?? {});
57
+ }
58
+
59
+ withBody(body: unknown): Invocation {
60
+ return new Invocation({ ...this, body });
61
+ }
62
+
63
+ withState(state: Record<string, unknown>): Invocation {
64
+ return new Invocation({ ...this, state });
65
+ }
66
+ }
67
+
68
+ export const EMPTY_INVOCATION = Invocation.from();
69
+
70
+ export function canonicalInvocation(input?: InvocationInput): Invocation {
71
+ return Invocation.from(input);
72
+ }
@@ -0,0 +1,49 @@
1
+ export interface RouteAddressInput {
2
+ entity: string;
3
+ operation: string;
4
+ surface?: string;
5
+ }
6
+
7
+ function required(value: string, name: string): string {
8
+ if (value.trim().length === 0) throw new TypeError(`Route ${name} cannot be empty`);
9
+ return value;
10
+ }
11
+
12
+ function optional(value: string | undefined, name: string): string | undefined {
13
+ return value === undefined ? undefined : required(value, name);
14
+ }
15
+
16
+ /** Canonical identity of one callable operation. */
17
+ export class RouteAddress {
18
+ readonly entity: string;
19
+ readonly operation: string;
20
+ readonly surface?: string;
21
+
22
+ constructor(input: RouteAddressInput) {
23
+ this.entity = required(input.entity, 'entity');
24
+ this.operation = required(input.operation, 'operation');
25
+ this.surface = optional(input.surface, 'surface');
26
+ Object.freeze(this);
27
+ }
28
+
29
+ key(): string {
30
+ return JSON.stringify([this.surface ?? null, this.entity, this.operation]);
31
+ }
32
+
33
+ equals(other: RouteAddress): boolean {
34
+ return this.key() === other.key();
35
+ }
36
+
37
+ toString(): string {
38
+ const audience = this.surface ? `${this.surface}/` : '';
39
+ return `${audience}${this.entity}.${this.operation}`;
40
+ }
41
+
42
+ toJSON(): RouteAddressInput {
43
+ return {
44
+ entity: this.entity,
45
+ operation: this.operation,
46
+ ...(this.surface !== undefined ? { surface: this.surface } : {}),
47
+ };
48
+ }
49
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Contract surface — what a sender needs to speak to a receiver, and
3
+ * nothing that assumes a runtime: the error vocabulary, the invocation
4
+ * shape, the call value, the naming convention.
5
+ *
6
+ * Browser-safe by design: no node builtin may enter this module's import
7
+ * graph. Published as the `@fougere/core/contract` subpath so client
8
+ * bundles never touch the full index (scanner, config loader). `@fougere/schema`
9
+ * is allowed in — measured, it imports no node builtin either.
10
+ */
11
+ import { lowerFirst } from '@fougere/schema';
12
+ import { canonicalInvocation, type InvocationContext } from './wire/invocation.js';
13
+ import type { FrondCall } from './wire/call.js';
14
+
15
+ export { FougereError, ErrorCode, validationErrorsOf } from './wire/errors.js';
16
+ // A receiver turns a refusal into what may cross a process boundary, and it is not
17
+ // core's boot — reaching it through the main entry dragged the scanner into a bundle.
18
+ export { toPublicError } from './wire/http-error.js';
19
+ export type { FougereErrorOptions } from './wire/errors.js';
20
+ export { canonicalInvocation, EMPTY_INVOCATION } from './wire/invocation.js';
21
+ export type { InvocationContext } from './wire/invocation.js';
22
+ export { Invocation } from './contract/Invocation.js';
23
+ export type { InvocationInput } from './contract/Invocation.js';
24
+ export { Call } from './contract/Call.js';
25
+ export { RouteAddress } from './contract/RouteAddress.js';
26
+ export type { RouteAddressInput } from './contract/RouteAddress.js';
27
+ export type { FrondCall, Transport, SignedCall } from './wire/call.js';
28
+
29
+ /**
30
+ * What `rpc.discover` answers. It belongs here and not to the runtime: the
31
+ * reserved op travels on the same wire as every other call, so a consumer that
32
+ * only sends — a browser bundle, a frond written elsewhere — needs its shape and
33
+ * nothing else. Type-only, so `call.js` never enters the runtime graph.
34
+ *
35
+ * Stated once, on purpose: two private copies of this interface have already gone
36
+ * stale (the CLI's, then the Rust demo's) the day an op stopped being a bare name.
37
+ */
38
+ export type { IdentityCard, CardOp, TopologyReport, FrondPlacement, Edge } from './wire/call.js';
39
+ export { assertIdentityCard } from './wire/call.js';
40
+
41
+ /**
42
+ * The key a class name is filed under — 'Post' → 'post'. Re-exported rather than
43
+ * respelled: a card writes it and a foreign key derives from it, so the convention
44
+ * belongs to the schema, and a second copy here is a second opinion. It travels
45
+ * through this subpath because a consumer of the wire (a browser bundle) may hold no
46
+ * schema dependency of its own — that audience only. Inside the package it is read from
47
+ * `@fougere/schema` directly: routing four modules through here bought nothing and put
48
+ * this file inside a cycle.
49
+ */
50
+ export { lowerFirst } from '@fougere/schema';
51
+
52
+ /** A call, fully fabricated: the designation and its completed invocation. */
53
+ export interface CallValue {
54
+ call: FrondCall;
55
+ invocation: InvocationContext;
56
+ }
57
+
58
+ /**
59
+ * Fabricate the call value — one designation, two spellings:
60
+ * `callValueOf(Post, 'list', { query })` (class + verb) or
61
+ * `callValueOf({ entity, op }, input)` (raw, for dynamic bridges).
62
+ * Missing invocation fields complete to the empty invocation.
63
+ */
64
+ export function callValueOf(
65
+ target: { name: string } | FrondCall,
66
+ opOrInput?: string | Partial<InvocationContext>,
67
+ input?: Partial<InvocationContext>,
68
+ ): CallValue {
69
+ const [call, given] =
70
+ typeof opOrInput === 'string'
71
+ ? [{ entity: lowerFirst((target as { name: string }).name), op: opOrInput }, input]
72
+ : [target as FrondCall, opOrInput];
73
+ return { call, invocation: canonicalInvocation(given) };
74
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * What both realizations spell identically — bytes in, bytes out, no platform.
3
+ *
4
+ * `Buffer` did all of this and does not exist on a Worker; `atob`/`btoa` exist on both.
5
+ * Nothing here is a policy decision, which is why it sits beside the port rather than
6
+ * inside either half: two copies of a base64url would be two chances to disagree on the
7
+ * padding, and the disagreement would show up as a bad signature.
8
+ */
9
+
10
+ const encoder = new TextEncoder();
11
+ const decoder = new TextDecoder();
12
+
13
+ export const bytesOf = (text: string): Uint8Array => encoder.encode(text);
14
+ export const textOf = (bytes: Uint8Array): string => decoder.decode(bytes);
15
+
16
+ /** Base64url, unpadded — what JWS puts between the dots. */
17
+ export function b64url(input: Uint8Array | string): string {
18
+ const bytes = typeof input === 'string' ? bytesOf(input) : input;
19
+ let binary = '';
20
+ // One character at a time: `String.fromCharCode(...bytes)` overflows the argument
21
+ // limit on a key, and a signature is small enough that the loop costs nothing.
22
+ for (const byte of bytes) binary += String.fromCharCode(byte);
23
+ return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
24
+ }
25
+
26
+ export function unb64url(input: string): Uint8Array {
27
+ const binary = atob(input.replace(/-/g, '+').replace(/_/g, '/'));
28
+ const bytes = new Uint8Array(binary.length);
29
+ for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
30
+ return bytes;
31
+ }
32
+
33
+ /** Plain base64, padded — what an environment variable carries a PEM as. */
34
+ export function b64(input: Uint8Array | string): string {
35
+ const bytes = typeof input === 'string' ? bytesOf(input) : input;
36
+ let binary = '';
37
+ for (const byte of bytes) binary += String.fromCharCode(byte);
38
+ return btoa(binary);
39
+ }
40
+
41
+ export const unb64 = (input: string): Uint8Array => {
42
+ const binary = atob(input);
43
+ const bytes = new Uint8Array(binary.length);
44
+ for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
45
+ return bytes;
46
+ };
47
+
48
+ /**
49
+ * The DER a PEM wraps. WebCrypto imports DER and knows nothing about the armour.
50
+ *
51
+ * The label is not checked: a caller asking for a private key and handed a public one
52
+ * fails at `importKey` with a message about the key, which is the truer error than one
53
+ * about a header line.
54
+ */
55
+ export function derOf(pem: string): Uint8Array {
56
+ const body = pem.replace(/-----(BEGIN|END)[^-]+-----/g, '').replace(/\s+/g, '');
57
+ if (body.length === 0) throw new Error('Not a PEM: no base64 body between the armour');
58
+ return unb64(body);
59
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Ed25519 through `node:crypto` — the default realization, and the fast one.
3
+ *
4
+ * `sign`/`verify` are synchronous here and stay synchronous: the promise is put around
5
+ * the answer, never around the work. That is the whole reason this file exists rather
6
+ * than everyone sharing the WebCrypto half.
7
+ */
8
+ import { createHash, createPrivateKey, createPublicKey, sign, verify } from 'node:crypto';
9
+ import type { CryptoPort, PublicJwk, Signer, Verifier } from './port.js';
10
+
11
+ export const crypto: CryptoPort = {
12
+ async sha256(data) {
13
+ return new Uint8Array(createHash('sha256').update(data).digest());
14
+ },
15
+
16
+ async signerOf(privateKeyPem): Promise<Signer> {
17
+ // Parsed here and captured, so a call signs against a KeyObject and never a string.
18
+ const key = createPrivateKey(privateKeyPem);
19
+ return { async sign(data) { return new Uint8Array(sign(null, data, key)); } };
20
+ },
21
+
22
+ async verifierOf(key): Promise<Verifier> {
23
+ const parsed = typeof key === 'string'
24
+ ? createPublicKey(key)
25
+ : createPublicKey({ key: key as never, format: 'jwk' });
26
+ return { async verify(data, signature) { return verify(null, data, parsed, signature); } };
27
+ },
28
+ };
29
+
30
+ export type { CryptoPort, PublicJwk, Signer, Verifier };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The two Ed25519 gestures a signed call needs, and the one hash that binds it.
3
+ *
4
+ * Node signs 59% faster than WebCrypto on the same curve (measured 2026-08-23: 14.30 µs
5
+ * against 22.72 µs per signature, 34.42 against 41.81 to verify), so this is a PORT with
6
+ * two realizations and not one implementation at the lowest common denominator. `#crypto`
7
+ * resolves to `crypto/node.ts` everywhere except under the `workerd` condition — the
8
+ * bundler picks at build time, and neither half ever appears in the other's output.
9
+ *
10
+ * The shape is async because WebCrypto is. On Node that costs one microtask, which the
11
+ * same measurement puts inside the noise of the signature itself (14.30 sync against
12
+ * 14.28 awaited) — awaiting is free, converting is not.
13
+ *
14
+ * A KEY IS PARSED ONCE. `crypto.subtle.importKey` is async and per-key, so an interface
15
+ * shaped `sign(key, data)` would import on every call; `signerOf(pem)` hands back
16
+ * something that holds the parsed key, and a boot builds it once.
17
+ */
18
+
19
+ /** A parsed private key. */
20
+ export interface Signer {
21
+ sign(data: Uint8Array): Promise<Uint8Array>;
22
+ }
23
+
24
+ /** A parsed public key. */
25
+ export interface Verifier {
26
+ /** False, never a throw: a bad signature is an answer, not a failure. */
27
+ verify(data: Uint8Array, signature: Uint8Array): Promise<boolean>;
28
+ }
29
+
30
+ /** A public key as a grant carries it — `{ kty: 'OKP', crv: 'Ed25519', x }`. */
31
+ export interface PublicJwk {
32
+ kty: string;
33
+ crv?: string;
34
+ x?: string;
35
+ [key: string]: unknown;
36
+ }
37
+
38
+ export interface CryptoPort {
39
+ /** SHA-256. What binds a body to an envelope without carrying the body. */
40
+ sha256(data: Uint8Array): Promise<Uint8Array>;
41
+ /** From a PKCS#8 PEM — what `fougere keys` writes. */
42
+ signerOf(privateKeyPem: string): Promise<Signer>;
43
+ /** From an SPKI PEM (the root's) or a JWK (the one a grant embeds). */
44
+ verifierOf(key: string | PublicJwk): Promise<Verifier>;
45
+ }